diff --git a/frontend/messages/de.json b/frontend/messages/de.json index 16718738..a7c1abdd 100644 --- a/frontend/messages/de.json +++ b/frontend/messages/de.json @@ -294,5 +294,16 @@ "enrich_done_body": "Alle Dokumente wurden bearbeitet.", "enrich_back_to_list": "Zurück zur Liste", "comment_empty_hint": "Noch keine Kommentare – starte die Diskussion!", - "comment_start_discussion": "Diskussion starten →" + "comment_start_discussion": "Diskussion starten →", + "notification_bell_label": "Benachrichtigungen", + "notification_bell_unread_label": "{count} ungelesene Benachrichtigungen", + "notification_mark_all_read": "Alle gelesen", + "notification_empty": "Keine neuen Benachrichtigungen", + "notification_type_reply": "{actor} hat auf deinen Kommentar geantwortet", + "notification_type_mention": "{actor} hat dich in einem Kommentar erwähnt", + "notification_prefs_heading": "Benachrichtigungen", + "notification_pref_reply": "E-Mail, wenn jemand auf meinen Kommentar antwortet", + "notification_pref_mention": "E-Mail, wenn jemand mich in einem Kommentar erwähnt", + "mention_btn_label": "Person erwähnen", + "mention_popup_empty": "Keine Nutzer gefunden" } diff --git a/frontend/messages/en.json b/frontend/messages/en.json index 462dea3b..7f5d0893 100644 --- a/frontend/messages/en.json +++ b/frontend/messages/en.json @@ -294,5 +294,16 @@ "enrich_done_body": "All documents have been processed.", "enrich_back_to_list": "Back to list", "comment_empty_hint": "No comments yet – start the discussion!", - "comment_start_discussion": "Start discussion →" + "comment_start_discussion": "Start discussion →", + "notification_bell_label": "Notifications", + "notification_bell_unread_label": "{count} unread notifications", + "notification_mark_all_read": "Mark all read", + "notification_empty": "No new notifications", + "notification_type_reply": "{actor} replied to your comment", + "notification_type_mention": "{actor} mentioned you in a comment", + "notification_prefs_heading": "Notifications", + "notification_pref_reply": "Email when someone replies to my comment", + "notification_pref_mention": "Email when someone mentions me in a comment", + "mention_btn_label": "Mention person", + "mention_popup_empty": "No users found" } diff --git a/frontend/messages/es.json b/frontend/messages/es.json index 0ae0b91a..d145b9e9 100644 --- a/frontend/messages/es.json +++ b/frontend/messages/es.json @@ -294,5 +294,16 @@ "enrich_done_body": "Todos los documentos han sido procesados.", "enrich_back_to_list": "Volver a la lista", "comment_empty_hint": "Aún no hay comentarios – ¡inicia la discusión!", - "comment_start_discussion": "Iniciar discusión →" + "comment_start_discussion": "Iniciar discusión →", + "notification_bell_label": "Notificaciones", + "notification_bell_unread_label": "{count} notificaciones sin leer", + "notification_mark_all_read": "Marcar todo como leído", + "notification_empty": "No hay notificaciones nuevas", + "notification_type_reply": "{actor} respondió a tu comentario", + "notification_type_mention": "{actor} te mencionó en un comentario", + "notification_prefs_heading": "Notificaciones", + "notification_pref_reply": "Correo cuando alguien responde a mi comentario", + "notification_pref_mention": "Correo cuando alguien me menciona en un comentario", + "mention_btn_label": "Mencionar persona", + "mention_popup_empty": "No se encontraron usuarios" } diff --git a/frontend/src/lib/components/NotificationBell.svelte b/frontend/src/lib/components/NotificationBell.svelte new file mode 100644 index 00000000..533ce5b0 --- /dev/null +++ b/frontend/src/lib/components/NotificationBell.svelte @@ -0,0 +1,304 @@ + + + + +
+ + + + + {#if open} + + {/if} +
diff --git a/frontend/src/routes/+layout.svelte b/frontend/src/routes/+layout.svelte index 3dac8238..4ae6c1d1 100644 --- a/frontend/src/routes/+layout.svelte +++ b/frontend/src/routes/+layout.svelte @@ -4,6 +4,7 @@ import { page } from '$app/state'; import { onMount } from 'svelte'; import LanguageSwitcher from '$lib/components/LanguageSwitcher.svelte'; import ThemeToggle from '$lib/components/ThemeToggle.svelte'; +import NotificationBell from '$lib/components/NotificationBell.svelte'; import AppNav from './AppNav.svelte'; import UserMenu from './UserMenu.svelte'; @@ -52,6 +53,11 @@ const userInitials = $derived.by(() => { + + {#if data?.user} + + {/if} + diff --git a/frontend/src/routes/profile/+page.server.ts b/frontend/src/routes/profile/+page.server.ts index 7a14518a..6a39a472 100644 --- a/frontend/src/routes/profile/+page.server.ts +++ b/frontend/src/routes/profile/+page.server.ts @@ -1,10 +1,15 @@ import { fail } from '@sveltejs/kit'; +import { env } from '$env/dynamic/private'; import type { PageServerLoad, Actions } from './$types'; import { createApiClient } from '$lib/api.server'; import { getErrorMessage } from '$lib/errors'; -export const load: PageServerLoad = async ({ locals }) => { - return { user: locals.user }; +const apiBase = () => env.API_INTERNAL_URL || 'http://localhost:8080'; + +export const load: PageServerLoad = async ({ locals, fetch }) => { + const res = await fetch(`${apiBase()}/api/users/me/notification-preferences`); + const notificationPrefs = res.ok ? await res.json() : null; + return { user: locals.user, notificationPrefs }; }; export const actions: Actions = { @@ -50,5 +55,26 @@ export const actions: Actions = { } return { passwordSuccess: true }; + }, + + updateNotificationPrefs: async ({ request, fetch }) => { + const formData = await request.formData(); + const body = { + notifyOnReply: formData.get('notifyOnReply') === 'true', + notifyOnMention: formData.get('notifyOnMention') === 'true' + }; + + const res = await fetch(`${apiBase()}/api/users/me/notification-preferences`, { + method: 'PUT', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(body) + }); + + if (!res.ok) { + const data = await res.json().catch(() => ({})); + return fail(res.status, { prefsError: getErrorMessage(data?.code) }); + } + + return { prefsSuccess: true }; } }; diff --git a/frontend/src/routes/profile/+page.svelte b/frontend/src/routes/profile/+page.svelte index 3447118e..2d0432dd 100644 --- a/frontend/src/routes/profile/+page.svelte +++ b/frontend/src/routes/profile/+page.svelte @@ -1,9 +1,14 @@
@@ -30,4 +35,54 @@ let { data, form } = $props();
+ + +
+

+ {m.notification_prefs_heading()} +

+ + {#if form?.prefsSuccess} +
+ {m.profile_saved()} +
+ {/if} + {#if form?.prefsError} +
+ {form.prefsError} +
+ {/if} + +
+ + + +
+ + + +
+ + +
+