Add recurring events, push notifications, config sync — and mobile layout fixes
Recurring event editing (rrule) with this/all occurrence scope, web push subscriptions (VAPID) with a cached config layer for notification settings, and email delivery via nodemailer. Also fixes the calendar view never actually stacking on mobile: the Calendars filter column used flex:1 with minWidth:0 on its sibling, so flex-wrap never triggered regardless of viewport width, squeezing the grid and view switcher into a sliver next to a fixed 220px sidebar. Adds a proper mobile breakpoint that stacks the layout, scrolls the week grid horizontally instead of compressing it, and enlarges touch targets.
This commit is contained in:
parent
dad3cc472c
commit
ca0dc9b070
28 changed files with 1517 additions and 155 deletions
28
backend/src/routes/config.js
Normal file
28
backend/src/routes/config.js
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
import { requireAuth, requireCap } from '../auth.js'
|
||||
import { pool } from '../db.js'
|
||||
|
||||
export async function configRoutes(app) {
|
||||
app.addHook('preHandler', requireAuth)
|
||||
|
||||
// GET /api/config — all config keys as a flat object
|
||||
app.get('/api/config', { preHandler: requireCap('view') }, async () => {
|
||||
const { rows } = await pool.query('SELECT key, value FROM config ORDER BY key')
|
||||
return Object.fromEntries(rows.map(r => [r.key, r.value]))
|
||||
})
|
||||
|
||||
// PATCH /api/config/:key — update a single config key. Gated on 'admin'
|
||||
// (same cap that already gates the activity log) rather than a new
|
||||
// capability — nothing about notification settings warrants a separate one.
|
||||
app.patch('/api/config/:key', { preHandler: requireCap('admin') }, async (req, reply) => {
|
||||
const { key } = req.params
|
||||
const { value } = req.body || {}
|
||||
if (value === undefined) return reply.status(400).send({ error: 'value required' })
|
||||
|
||||
await pool.query(
|
||||
`INSERT INTO config (key, value, updated_at) VALUES ($1, $2, NOW())
|
||||
ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value, updated_at = NOW()`,
|
||||
[key, JSON.stringify(value)]
|
||||
)
|
||||
return { ok: true }
|
||||
})
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue