feat(notifications): alert oversight review queues

This commit is contained in:
JB
2026-07-26 01:53:53 -04:00
parent d4ddfe3b80
commit 551a5c29a1
12 changed files with 328 additions and 3 deletions
@@ -3,6 +3,8 @@ import { getSessionUser } from '$lib/server/auth/session';
import { getDatabase } from '$lib/server/database';
import { getPagination } from '$lib/server/pagination';
import { NOTIFICATION_CATEGORIES } from '$lib/server/notifications';
import { getPermissions } from '$lib/server/auth/authorization';
import { OVERSIGHT_NOTIFICATION_CATEGORIES, OVERSIGHT_NOTIFICATION_FREQUENCIES } from '$lib/server/oversight-notifications';
import type { Actions, PageServerLoad } from './$types';
export const load: PageServerLoad = async ({ cookies, url }) => {
@@ -34,6 +36,17 @@ export const load: PageServerLoad = async ({ cookies, url }) => {
LEFT JOIN notification_preferences p ON p.user_id=n.user_id AND p.category=n.category
${where} ORDER BY n.created_at DESC LIMIT ? OFFSET ?`)).all(...args, pagination.perPage, pagination.offset);
const preferenceRows = await (await database.prepare('SELECT * FROM notification_preferences WHERE user_id=?')).all(user.id);
const permissions = await getPermissions(user);
const oversightCategories = OVERSIGHT_NOTIFICATION_CATEGORIES.filter(
(category) => permissions.includes('*') || permissions.includes(category.permission)
);
const oversightRows = oversightCategories.length
? await (await database.prepare('SELECT * FROM oversight_notification_preferences WHERE user_id=?')).all(user.id)
: [];
const oversightPreferences = oversightCategories.map((category) => ({
...category,
frequency: String(oversightRows.find((row) => row.oversight_category === category.id)?.frequency ?? 'immediate')
}));
const preferences = NOTIFICATION_CATEGORIES.map((name) => {
const stored = preferenceRows.find((row) => row.category === name);
return {
@@ -46,7 +59,7 @@ export const load: PageServerLoad = async ({ cookies, url }) => {
const unread = Number((await (await database.prepare(`SELECT COUNT(*) AS count FROM notifications n
LEFT JOIN notification_preferences p ON p.user_id=n.user_id AND p.category=n.category
WHERE n.user_id=? AND n.read_at IS NULL AND (n.mandatory=1 OR COALESCE(p.in_app_enabled,1)=1)`)).get(user.id))?.count ?? 0);
return { notifications, preferences, pagination, filters: { categories, states }, unread, hasVerifiedEmail: Boolean(await (await database.prepare('SELECT 1 FROM user_emails WHERE user_id=?')).get(user.id)) };
return { notifications, preferences, oversightPreferences, pagination, filters: { categories, states }, unread, hasVerifiedEmail: Boolean(await (await database.prepare('SELECT 1 FROM user_emails WHERE user_id=?')).get(user.id)) };
};
export const actions: Actions = {
@@ -85,5 +98,30 @@ export const actions: Actions = {
notificationPreferencesSaved: true,
notificationPreferenceSaveToken: crypto.randomUUID()
};
},
saveOversightPreferences: async ({ cookies, request }) => {
const user = await getSessionUser(cookies);
if (!user) redirect(303, '/sign-in');
const permissions = await getPermissions(user);
const available = OVERSIGHT_NOTIFICATION_CATEGORIES.filter(
(category) => permissions.includes('*') || permissions.includes(category.permission)
);
if (!available.length) return fail(403, { oversightPreferenceError: 'Oversight permission is required.' });
const form = await request.formData();
const database = await getDatabase();
const now = new Date().toISOString();
for (const category of available) {
const frequency = String(form.get(`oversight:${category.id}`) ?? 'immediate');
if (!OVERSIGHT_NOTIFICATION_FREQUENCIES.includes(frequency as any)) {
return fail(400, { oversightPreferenceError: 'Choose a valid Oversight notification frequency.' });
}
await (await database.prepare(`INSERT INTO oversight_notification_preferences(user_id,oversight_category,frequency,updated_at)
VALUES(?,?,?,?) ON CONFLICT(user_id,oversight_category) DO UPDATE SET
frequency=excluded.frequency,updated_at=excluded.updated_at`)).run(user.id, category.id, frequency, now);
}
return {
oversightPreferencesSaved: true,
notificationPreferenceSaveToken: crypto.randomUUID()
};
}
};