diff --git a/apps/mana/apps/web/src/lib/components/settings/searchIndex.ts b/apps/mana/apps/web/src/lib/components/settings/searchIndex.ts index 673f423ac..ba20cafd2 100644 --- a/apps/mana/apps/web/src/lib/components/settings/searchIndex.ts +++ b/apps/mana/apps/web/src/lib/components/settings/searchIndex.ts @@ -8,14 +8,14 @@ * `getSearchIndex()` so components stay reactive to locale changes. */ import type { Component } from 'svelte'; -import { Gear, Robot, ShieldCheck, Cloud, Tag, Megaphone } from '@mana/shared-icons'; +import { Gear, Robot, ShieldCheck, Cloud, Tag, HeartHalf } from '@mana/shared-icons'; export type CategoryId = | 'general' | 'ai' | 'security' | 'privacy' - | 'community' + | 'feedback' | 'data' | 'tag-presets'; @@ -46,7 +46,7 @@ const CATEGORY_DEFS: Record = { i18nKey: 'security', }, privacy: { icon: ShieldCheck, anchors: ['privacy'], i18nKey: 'privacy' }, - community: { icon: Megaphone, anchors: ['community-identity'], i18nKey: 'community' }, + feedback: { icon: HeartHalf, anchors: ['feedback-identity'], i18nKey: 'feedback' }, data: { icon: Cloud, anchors: [ @@ -118,9 +118,9 @@ const SEARCH_ENTRY_DEFS: SearchEntryDef[] = [ // Privacy { i18nKey: 'privacy_overview', category: 'privacy', anchor: 'privacy' }, { i18nKey: 'reset_all_private', category: 'privacy', anchor: 'privacy' }, - // Community - { i18nKey: 'community_show_real_name', category: 'community', anchor: 'community-identity' }, - { i18nKey: 'community_karma', category: 'community', anchor: 'community-identity' }, + // Feedback + { i18nKey: 'feedback_show_real_name', category: 'feedback', anchor: 'feedback-identity' }, + { i18nKey: 'feedback_karma', category: 'feedback', anchor: 'feedback-identity' }, // Data { i18nKey: 'cloud_sync', category: 'data', anchor: 'cloud-sync' }, { i18nKey: 'data_export', category: 'data', anchor: 'my-data' }, diff --git a/apps/mana/apps/web/src/lib/components/settings/sections/FeedbackIdentitySection.svelte b/apps/mana/apps/web/src/lib/components/settings/sections/FeedbackIdentitySection.svelte index 53c28d616..ef7bc8e29 100644 --- a/apps/mana/apps/web/src/lib/components/settings/sections/FeedbackIdentitySection.svelte +++ b/apps/mana/apps/web/src/lib/components/settings/sections/FeedbackIdentitySection.svelte @@ -1,9 +1,9 @@ @@ -13,7 +13,7 @@ import SettingsSectionHeader from '../SettingsSectionHeader.svelte'; import { authStore } from '$lib/stores/auth.svelte'; import { browser } from '$app/environment'; - import { Megaphone } from '@mana/shared-icons'; + import { HeartHalf } from '@mana/shared-icons'; function getAuthUrl(): string { if (browser && typeof window !== 'undefined') { @@ -27,8 +27,8 @@ type ProfileBlob = { displayHash?: string; displayName?: string; - communityShowRealName?: boolean; - communityKarma?: number; + feedbackShowRealName?: boolean; + feedbackKarma?: number; }; let profile = $state({}); @@ -42,7 +42,7 @@ try { const token = await authStore.getValidToken(); if (!token) throw new Error('not authenticated'); - // We don't have a "get my community profile" endpoint yet — but the + // We don't have a "get my feedback profile" endpoint yet — but the // my-feedback endpoint is enough to derive everything we need: // most-recent post carries the displayHash + displayName, the // auth.users karma comes back via /me/data. @@ -57,22 +57,22 @@ if (!profileRes.ok) throw new Error(`profile load ${profileRes.status}`); const data = (await profileRes.json()) as { auth?: { - communityShowRealName?: boolean; - communityKarma?: number; + feedbackShowRealName?: boolean; + feedbackKarma?: number; }; }; profile = { ...profile, - communityShowRealName: data.auth?.communityShowRealName ?? false, - communityKarma: data.auth?.communityKarma ?? 0, + feedbackShowRealName: data.auth?.feedbackShowRealName ?? false, + feedbackKarma: data.auth?.feedbackKarma ?? 0, }; if (dataRes && dataRes.ok) { const fields = (await dataRes.json()) as ProfileBlob; profile = { ...profile, ...fields }; } } catch (err) { - console.warn('[community-section] load failed:', err); - error = err instanceof Error ? err.message : 'Konnte die Community-Daten nicht laden'; + console.warn('[feedback-identity-section] load failed:', err); + error = err instanceof Error ? err.message : 'Konnte die Feedback-Identität nicht laden'; } finally { loading = false; } @@ -85,8 +85,8 @@ async function toggleRealName(next: boolean) { if (saving) return; saving = true; - const previous = profile.communityShowRealName; - profile = { ...profile, communityShowRealName: next }; // optimistic + const previous = profile.feedbackShowRealName; + profile = { ...profile, feedbackShowRealName: next }; // optimistic try { const token = await authStore.getValidToken(); const res = await fetch(`${getAuthUrl()}/api/v1/me/profile`, { @@ -95,28 +95,28 @@ 'Content-Type': 'application/json', Authorization: `Bearer ${token}`, }, - body: JSON.stringify({ communityShowRealName: next }), + body: JSON.stringify({ feedbackShowRealName: next }), }); if (!res.ok) throw new Error(`update ${res.status}`); } catch (err) { - console.warn('[community-section] toggle failed:', err); + console.warn('[feedback-identity-section] toggle failed:', err); error = 'Speichern fehlgeschlagen — versuch es nochmal.'; - profile = { ...profile, communityShowRealName: previous }; // rollback + profile = { ...profile, feedbackShowRealName: previous }; // rollback } finally { saving = false; } } - let karma = $derived(profile.communityKarma ?? 0); + let karma = $derived(profile.feedbackKarma ?? 0); let tier = $derived(tierFromKarma(karma)); let tierCfg = $derived(KARMA_TIER_CONFIG[tier]); - + {#if loading} @@ -132,7 +132,7 @@
{profile.displayName ?? 'Wachsame Eule (noch unbenutzt)'} - {#if authStore.user?.name && profile.communityShowRealName} + {#if authStore.user?.name && profile.feedbackShowRealName} · {authStore.user.name} {/if}
@@ -165,12 +165,12 @@ diff --git a/apps/mana/apps/web/src/lib/i18n/locales/settings/de.json b/apps/mana/apps/web/src/lib/i18n/locales/settings/de.json index 68fb564c1..c723ca595 100644 --- a/apps/mana/apps/web/src/lib/i18n/locales/settings/de.json +++ b/apps/mana/apps/web/src/lib/i18n/locales/settings/de.json @@ -1,7 +1,13 @@ { "categories": { - "general": { "label": "Allgemein", "description": "Theme, Sprache, Benachrichtigungen" }, - "ai": { "label": "KI", "description": "Compute-Backend & Modelle" }, + "general": { + "label": "Allgemein", + "description": "Theme, Sprache, Benachrichtigungen" + }, + "ai": { + "label": "KI", + "description": "Compute-Backend & Modelle" + }, "security": { "label": "Sicherheit", "description": "Passkeys, 2FA, Verschlüsselung & Sitzungen" @@ -10,12 +16,18 @@ "label": "Privatsphäre", "description": "Was ist gerade öffentlich oder per Link geteilt — mit Kill-Switch." }, - "community": { - "label": "Community", - "description": "Wie du in der Community auftauchst — Eulen-Pseudonym, Klarname, Karma." + "data": { + "label": "Daten & Sync", + "description": "Cloud-Sync, Export, Backup & DSGVO" }, - "data": { "label": "Daten & Sync", "description": "Cloud-Sync, Export, Backup & DSGVO" }, - "tag_presets": { "label": "Tag-Presets", "description": "Tag-Sets für neue Spaces" } + "tag_presets": { + "label": "Tag-Presets", + "description": "Tag-Sets für neue Spaces" + }, + "feedback": { + "label": "Feedback", + "description": "Wie du im Feedback-Feed auftauchst — Eulen-Pseudonym, Klarname, Karma." + } }, "sidebar": { "aria_categories": "Einstellungs-Kategorien", @@ -25,24 +37,54 @@ "no_results": "Keine Treffer für „{query}\"" }, "search": { - "theme": { "label": "Theme", "keywords": "dark, light, farbe, design" }, - "language": { "label": "Sprache", "keywords": "language, i18n, deutsch, english" }, - "notifications": { "label": "Benachrichtigungen", "keywords": "notification, sound" }, - "ai_options": { "label": "KI-Optionen", "keywords": "llm, ai, compute" }, + "theme": { + "label": "Theme", + "keywords": "dark, light, farbe, design" + }, + "language": { + "label": "Sprache", + "keywords": "language, i18n, deutsch, english" + }, + "notifications": { + "label": "Benachrichtigungen", + "keywords": "notification, sound" + }, + "ai_options": { + "label": "KI-Optionen", + "keywords": "llm, ai, compute" + }, "browser_model": { "label": "Browser-Modell (Gemma)", "keywords": "gemma, webgpu, lokal, offline" }, - "mana_server": { "label": "Mana-Server (KI)", "keywords": "server, self-hosted" }, - "cloud_ai": { "label": "Cloud-KI (Gemini)", "keywords": "google, cloud, gemini" }, - "passkeys": { "label": "Passkeys", "keywords": "webauthn, fido, biometrie" }, - "sessions": { "label": "Aktive Sessions", "keywords": "logout, gerät, device" }, - "two_factor": { "label": "Zwei-Faktor (2FA)", "keywords": "totp, 2fa, mfa" }, + "mana_server": { + "label": "Mana-Server (KI)", + "keywords": "server, self-hosted" + }, + "cloud_ai": { + "label": "Cloud-KI (Gemini)", + "keywords": "google, cloud, gemini" + }, + "passkeys": { + "label": "Passkeys", + "keywords": "webauthn, fido, biometrie" + }, + "sessions": { + "label": "Aktive Sessions", + "keywords": "logout, gerät, device" + }, + "two_factor": { + "label": "Zwei-Faktor (2FA)", + "keywords": "totp, 2fa, mfa" + }, "vault": { "label": "Verschlüsselung", "keywords": "vault, encryption, aes, schlüssel, zero-knowledge" }, - "security_log": { "label": "Sicherheits-Log", "keywords": "audit, history, verlauf" }, + "security_log": { + "label": "Sicherheits-Log", + "keywords": "audit, history, verlauf" + }, "privacy_overview": { "label": "Privatsphäre-Übersicht", "keywords": "public, öffentlich, unlisted, teilen, sharing, link" @@ -51,25 +93,46 @@ "label": "Alle auf privat zurücksetzen", "keywords": "kill-switch, kill switch, reset, privat, widerrufen" }, - "community_show_real_name": { - "label": "Klarname in der Community", + "feedback_show_real_name": { + "label": "Klarname im Feedback-Feed", "keywords": "name, eule, pseudonym, klarname, anonym" }, - "community_karma": { + "feedback_karma": { "label": "Karma & Tier", "keywords": "karma, tier, bronze, silver, gold, platin, eule" }, - "cloud_sync": { "label": "Cloud Sync", "keywords": "sync, geräte" }, - "data_export": { "label": "Daten exportieren", "keywords": "export, dsgvo, gdpr, json" }, - "auth_data": { "label": "Authentifizierung", "keywords": "sessions, 2fa, login" }, - "credits_data": { "label": "Credits & Transaktionen", "keywords": "guthaben, transaktionen" }, - "project_data": { "label": "Projektdaten", "keywords": "projekte, apps, statistik" }, - "retention": { "label": "Aufbewahrungsfristen", "keywords": "retention, dsgvo, fristen" }, + "cloud_sync": { + "label": "Cloud Sync", + "keywords": "sync, geräte" + }, + "data_export": { + "label": "Daten exportieren", + "keywords": "export, dsgvo, gdpr, json" + }, + "auth_data": { + "label": "Authentifizierung", + "keywords": "sessions, 2fa, login" + }, + "credits_data": { + "label": "Credits & Transaktionen", + "keywords": "guthaben, transaktionen" + }, + "project_data": { + "label": "Projektdaten", + "keywords": "projekte, apps, statistik" + }, + "retention": { + "label": "Aufbewahrungsfristen", + "keywords": "retention, dsgvo, fristen" + }, "backup": { "label": "Backup & Wiederherstellung", "keywords": "backup, restore, import, archiv, .mana" }, - "delete_account": { "label": "Konto löschen", "keywords": "delete, gdpr, dsgvo, gefahrenzone" } + "delete_account": { + "label": "Konto löschen", + "keywords": "delete, gdpr, dsgvo, gefahrenzone" + } }, "general": { "title": "Allgemein", diff --git a/apps/mana/apps/web/src/lib/i18n/locales/settings/en.json b/apps/mana/apps/web/src/lib/i18n/locales/settings/en.json index ac1ca1991..3b9edf40f 100644 --- a/apps/mana/apps/web/src/lib/i18n/locales/settings/en.json +++ b/apps/mana/apps/web/src/lib/i18n/locales/settings/en.json @@ -1,18 +1,33 @@ { "categories": { - "general": { "label": "General", "description": "Theme, language, notifications" }, - "ai": { "label": "AI", "description": "Compute backend & models" }, - "security": { "label": "Security", "description": "Passkeys, 2FA, encryption & sessions" }, + "general": { + "label": "General", + "description": "Theme, language, notifications" + }, + "ai": { + "label": "AI", + "description": "Compute backend & models" + }, + "security": { + "label": "Security", + "description": "Passkeys, 2FA, encryption & sessions" + }, "privacy": { "label": "Privacy", "description": "What is currently public or shared via link — with kill switch." }, - "community": { - "label": "Community", - "description": "How you appear in the community — owl pseudonym, real name, karma." + "data": { + "label": "Data & Sync", + "description": "Cloud sync, export, backup & GDPR" }, - "data": { "label": "Data & Sync", "description": "Cloud sync, export, backup & GDPR" }, - "tag_presets": { "label": "Tag presets", "description": "Tag sets for new spaces" } + "tag_presets": { + "label": "Tag presets", + "description": "Tag sets for new spaces" + }, + "feedback": { + "label": "Feedback", + "description": "How you appear in the feedback feed — owl pseudonym, real name, karma." + } }, "sidebar": { "aria_categories": "Settings categories", @@ -22,21 +37,54 @@ "no_results": "No matches for \"{query}\"" }, "search": { - "theme": { "label": "Theme", "keywords": "dark, light, color, design" }, - "language": { "label": "Language", "keywords": "language, i18n, german, english" }, - "notifications": { "label": "Notifications", "keywords": "notification, sound" }, - "ai_options": { "label": "AI options", "keywords": "llm, ai, compute" }, + "theme": { + "label": "Theme", + "keywords": "dark, light, color, design" + }, + "language": { + "label": "Language", + "keywords": "language, i18n, german, english" + }, + "notifications": { + "label": "Notifications", + "keywords": "notification, sound" + }, + "ai_options": { + "label": "AI options", + "keywords": "llm, ai, compute" + }, "browser_model": { "label": "Browser model (Gemma)", "keywords": "gemma, webgpu, local, offline" }, - "mana_server": { "label": "Mana server (AI)", "keywords": "server, self-hosted" }, - "cloud_ai": { "label": "Cloud AI (Gemini)", "keywords": "google, cloud, gemini" }, - "passkeys": { "label": "Passkeys", "keywords": "webauthn, fido, biometric" }, - "sessions": { "label": "Active sessions", "keywords": "logout, device" }, - "two_factor": { "label": "Two-factor (2FA)", "keywords": "totp, 2fa, mfa" }, - "vault": { "label": "Encryption", "keywords": "vault, encryption, aes, key, zero-knowledge" }, - "security_log": { "label": "Security log", "keywords": "audit, history" }, + "mana_server": { + "label": "Mana server (AI)", + "keywords": "server, self-hosted" + }, + "cloud_ai": { + "label": "Cloud AI (Gemini)", + "keywords": "google, cloud, gemini" + }, + "passkeys": { + "label": "Passkeys", + "keywords": "webauthn, fido, biometric" + }, + "sessions": { + "label": "Active sessions", + "keywords": "logout, device" + }, + "two_factor": { + "label": "Two-factor (2FA)", + "keywords": "totp, 2fa, mfa" + }, + "vault": { + "label": "Encryption", + "keywords": "vault, encryption, aes, key, zero-knowledge" + }, + "security_log": { + "label": "Security log", + "keywords": "audit, history" + }, "privacy_overview": { "label": "Privacy overview", "keywords": "public, unlisted, share, sharing, link" @@ -45,25 +93,46 @@ "label": "Reset all to private", "keywords": "kill-switch, kill switch, reset, private, revoke" }, - "community_show_real_name": { - "label": "Real name in community", + "feedback_show_real_name": { + "label": "Real name in feedback feed", "keywords": "name, owl, pseudonym, anonymous, identity" }, - "community_karma": { + "feedback_karma": { "label": "Karma & Tier", "keywords": "karma, tier, bronze, silver, gold, platinum, owl" }, - "cloud_sync": { "label": "Cloud Sync", "keywords": "sync, devices" }, - "data_export": { "label": "Export data", "keywords": "export, dsgvo, gdpr, json" }, - "auth_data": { "label": "Authentication", "keywords": "sessions, 2fa, login" }, - "credits_data": { "label": "Credits & transactions", "keywords": "balance, transactions" }, - "project_data": { "label": "Project data", "keywords": "projects, apps, statistics" }, - "retention": { "label": "Retention periods", "keywords": "retention, gdpr, periods" }, + "cloud_sync": { + "label": "Cloud Sync", + "keywords": "sync, devices" + }, + "data_export": { + "label": "Export data", + "keywords": "export, dsgvo, gdpr, json" + }, + "auth_data": { + "label": "Authentication", + "keywords": "sessions, 2fa, login" + }, + "credits_data": { + "label": "Credits & transactions", + "keywords": "balance, transactions" + }, + "project_data": { + "label": "Project data", + "keywords": "projects, apps, statistics" + }, + "retention": { + "label": "Retention periods", + "keywords": "retention, gdpr, periods" + }, "backup": { "label": "Backup & restore", "keywords": "backup, restore, import, archive, .mana" }, - "delete_account": { "label": "Delete account", "keywords": "delete, gdpr, danger zone" } + "delete_account": { + "label": "Delete account", + "keywords": "delete, gdpr, danger zone" + } }, "general": { "title": "General", diff --git a/apps/mana/apps/web/src/lib/i18n/locales/settings/es.json b/apps/mana/apps/web/src/lib/i18n/locales/settings/es.json index 15ace40f7..cff9905d9 100644 --- a/apps/mana/apps/web/src/lib/i18n/locales/settings/es.json +++ b/apps/mana/apps/web/src/lib/i18n/locales/settings/es.json @@ -1,16 +1,21 @@ { "categories": { - "general": { "label": "General", "description": "Tema, idioma, notificaciones" }, - "ai": { "label": "IA", "description": "Backend de cómputo y modelos" }, - "security": { "label": "Seguridad", "description": "Passkeys, 2FA, cifrado y sesiones" }, + "general": { + "label": "General", + "description": "Tema, idioma, notificaciones" + }, + "ai": { + "label": "IA", + "description": "Backend de cómputo y modelos" + }, + "security": { + "label": "Seguridad", + "description": "Passkeys, 2FA, cifrado y sesiones" + }, "privacy": { "label": "Privacidad", "description": "Qué es público o compartido por enlace ahora mismo — con interruptor de emergencia." }, - "community": { - "label": "Comunidad", - "description": "Cómo apareces en la comunidad — pseudónimo de búho, nombre real, karma." - }, "data": { "label": "Datos y sincronización", "description": "Cloud sync, exportación, copia de seguridad y RGPD" @@ -18,6 +23,10 @@ "tag_presets": { "label": "Plantillas de etiquetas", "description": "Conjuntos de etiquetas para nuevos espacios" + }, + "feedback": { + "label": "Feedback", + "description": "Cómo apareces en el feed de feedback — pseudónimo de búho, nombre real, karma." } }, "sidebar": { @@ -28,21 +37,54 @@ "no_results": "Sin resultados para «{query}»" }, "search": { - "theme": { "label": "Tema", "keywords": "oscuro, claro, color, diseño" }, - "language": { "label": "Idioma", "keywords": "language, i18n, alemán, inglés" }, - "notifications": { "label": "Notificaciones", "keywords": "notification, sonido" }, - "ai_options": { "label": "Opciones de IA", "keywords": "llm, ia, ai, compute" }, + "theme": { + "label": "Tema", + "keywords": "oscuro, claro, color, diseño" + }, + "language": { + "label": "Idioma", + "keywords": "language, i18n, alemán, inglés" + }, + "notifications": { + "label": "Notificaciones", + "keywords": "notification, sonido" + }, + "ai_options": { + "label": "Opciones de IA", + "keywords": "llm, ia, ai, compute" + }, "browser_model": { "label": "Modelo del navegador (Gemma)", "keywords": "gemma, webgpu, local, offline" }, - "mana_server": { "label": "Servidor Mana (IA)", "keywords": "servidor, self-hosted" }, - "cloud_ai": { "label": "IA en la nube (Gemini)", "keywords": "google, cloud, gemini" }, - "passkeys": { "label": "Passkeys", "keywords": "webauthn, fido, biometría" }, - "sessions": { "label": "Sesiones activas", "keywords": "logout, dispositivo" }, - "two_factor": { "label": "Doble factor (2FA)", "keywords": "totp, 2fa, mfa" }, - "vault": { "label": "Cifrado", "keywords": "vault, cifrado, aes, clave, zero-knowledge" }, - "security_log": { "label": "Registro de seguridad", "keywords": "audit, historial" }, + "mana_server": { + "label": "Servidor Mana (IA)", + "keywords": "servidor, self-hosted" + }, + "cloud_ai": { + "label": "IA en la nube (Gemini)", + "keywords": "google, cloud, gemini" + }, + "passkeys": { + "label": "Passkeys", + "keywords": "webauthn, fido, biometría" + }, + "sessions": { + "label": "Sesiones activas", + "keywords": "logout, dispositivo" + }, + "two_factor": { + "label": "Doble factor (2FA)", + "keywords": "totp, 2fa, mfa" + }, + "vault": { + "label": "Cifrado", + "keywords": "vault, cifrado, aes, clave, zero-knowledge" + }, + "security_log": { + "label": "Registro de seguridad", + "keywords": "audit, historial" + }, "privacy_overview": { "label": "Resumen de privacidad", "keywords": "público, unlisted, compartir, enlace" @@ -51,20 +93,38 @@ "label": "Restablecer todo a privado", "keywords": "kill-switch, reset, privado, revocar" }, - "community_show_real_name": { - "label": "Nombre real en la comunidad", + "feedback_show_real_name": { + "label": "Nombre real en el feed de feedback", "keywords": "nombre, búho, pseudónimo, anónimo, identidad" }, - "community_karma": { + "feedback_karma": { "label": "Karma y nivel", "keywords": "karma, nivel, bronce, plata, oro, platino, búho" }, - "cloud_sync": { "label": "Cloud Sync", "keywords": "sync, dispositivos" }, - "data_export": { "label": "Exportar datos", "keywords": "export, rgpd, gdpr, json" }, - "auth_data": { "label": "Autenticación", "keywords": "sesiones, 2fa, login" }, - "credits_data": { "label": "Créditos y transacciones", "keywords": "saldo, transacciones" }, - "project_data": { "label": "Datos de proyectos", "keywords": "proyectos, apps, estadística" }, - "retention": { "label": "Plazos de conservación", "keywords": "retention, rgpd, plazos" }, + "cloud_sync": { + "label": "Cloud Sync", + "keywords": "sync, dispositivos" + }, + "data_export": { + "label": "Exportar datos", + "keywords": "export, rgpd, gdpr, json" + }, + "auth_data": { + "label": "Autenticación", + "keywords": "sesiones, 2fa, login" + }, + "credits_data": { + "label": "Créditos y transacciones", + "keywords": "saldo, transacciones" + }, + "project_data": { + "label": "Datos de proyectos", + "keywords": "proyectos, apps, estadística" + }, + "retention": { + "label": "Plazos de conservación", + "keywords": "retention, rgpd, plazos" + }, "backup": { "label": "Copia y restauración", "keywords": "backup, restore, import, archivo, .mana" diff --git a/apps/mana/apps/web/src/lib/i18n/locales/settings/fr.json b/apps/mana/apps/web/src/lib/i18n/locales/settings/fr.json index 869a75122..ef3a48670 100644 --- a/apps/mana/apps/web/src/lib/i18n/locales/settings/fr.json +++ b/apps/mana/apps/web/src/lib/i18n/locales/settings/fr.json @@ -1,16 +1,21 @@ { "categories": { - "general": { "label": "Général", "description": "Thème, langue, notifications" }, - "ai": { "label": "IA", "description": "Backend de calcul et modèles" }, - "security": { "label": "Sécurité", "description": "Passkeys, 2FA, chiffrement et sessions" }, + "general": { + "label": "Général", + "description": "Thème, langue, notifications" + }, + "ai": { + "label": "IA", + "description": "Backend de calcul et modèles" + }, + "security": { + "label": "Sécurité", + "description": "Passkeys, 2FA, chiffrement et sessions" + }, "privacy": { "label": "Confidentialité", "description": "Ce qui est public ou partagé par lien actuellement — avec interrupteur d'urgence." }, - "community": { - "label": "Communauté", - "description": "Comment tu apparais dans la communauté — pseudonyme de chouette, vrai nom, karma." - }, "data": { "label": "Données et synchro", "description": "Cloud sync, export, sauvegarde et RGPD" @@ -18,6 +23,10 @@ "tag_presets": { "label": "Modèles d'étiquettes", "description": "Jeux d'étiquettes pour de nouveaux espaces" + }, + "feedback": { + "label": "Feedback", + "description": "Comment tu apparais dans le feed de feedback — pseudonyme de chouette, vrai nom, karma." } }, "sidebar": { @@ -28,21 +37,54 @@ "no_results": "Aucun résultat pour « {query} »" }, "search": { - "theme": { "label": "Thème", "keywords": "sombre, clair, couleur, design" }, - "language": { "label": "Langue", "keywords": "language, i18n, allemand, anglais" }, - "notifications": { "label": "Notifications", "keywords": "notification, son" }, - "ai_options": { "label": "Options d'IA", "keywords": "llm, ia, ai, compute" }, + "theme": { + "label": "Thème", + "keywords": "sombre, clair, couleur, design" + }, + "language": { + "label": "Langue", + "keywords": "language, i18n, allemand, anglais" + }, + "notifications": { + "label": "Notifications", + "keywords": "notification, son" + }, + "ai_options": { + "label": "Options d'IA", + "keywords": "llm, ia, ai, compute" + }, "browser_model": { "label": "Modèle navigateur (Gemma)", "keywords": "gemma, webgpu, local, hors ligne" }, - "mana_server": { "label": "Serveur Mana (IA)", "keywords": "serveur, self-hosted" }, - "cloud_ai": { "label": "IA cloud (Gemini)", "keywords": "google, cloud, gemini" }, - "passkeys": { "label": "Passkeys", "keywords": "webauthn, fido, biométrie" }, - "sessions": { "label": "Sessions actives", "keywords": "logout, appareil" }, - "two_factor": { "label": "Double facteur (2FA)", "keywords": "totp, 2fa, mfa" }, - "vault": { "label": "Chiffrement", "keywords": "vault, chiffrement, aes, clé, zero-knowledge" }, - "security_log": { "label": "Journal de sécurité", "keywords": "audit, historique" }, + "mana_server": { + "label": "Serveur Mana (IA)", + "keywords": "serveur, self-hosted" + }, + "cloud_ai": { + "label": "IA cloud (Gemini)", + "keywords": "google, cloud, gemini" + }, + "passkeys": { + "label": "Passkeys", + "keywords": "webauthn, fido, biométrie" + }, + "sessions": { + "label": "Sessions actives", + "keywords": "logout, appareil" + }, + "two_factor": { + "label": "Double facteur (2FA)", + "keywords": "totp, 2fa, mfa" + }, + "vault": { + "label": "Chiffrement", + "keywords": "vault, chiffrement, aes, clé, zero-knowledge" + }, + "security_log": { + "label": "Journal de sécurité", + "keywords": "audit, historique" + }, "privacy_overview": { "label": "Aperçu de la confidentialité", "keywords": "public, unlisted, partage, lien" @@ -51,20 +93,38 @@ "label": "Tout repasser en privé", "keywords": "kill-switch, reset, privé, révoquer" }, - "community_show_real_name": { - "label": "Vrai nom dans la communauté", + "feedback_show_real_name": { + "label": "Nom réel dans le feed de feedback", "keywords": "nom, chouette, pseudonyme, anonyme, identité" }, - "community_karma": { + "feedback_karma": { "label": "Karma & niveau", "keywords": "karma, niveau, bronze, argent, or, platine, chouette" }, - "cloud_sync": { "label": "Cloud Sync", "keywords": "sync, appareils" }, - "data_export": { "label": "Exporter les données", "keywords": "export, rgpd, gdpr, json" }, - "auth_data": { "label": "Authentification", "keywords": "sessions, 2fa, login" }, - "credits_data": { "label": "Crédits et transactions", "keywords": "solde, transactions" }, - "project_data": { "label": "Données projet", "keywords": "projets, apps, statistiques" }, - "retention": { "label": "Durées de conservation", "keywords": "retention, rgpd, durées" }, + "cloud_sync": { + "label": "Cloud Sync", + "keywords": "sync, appareils" + }, + "data_export": { + "label": "Exporter les données", + "keywords": "export, rgpd, gdpr, json" + }, + "auth_data": { + "label": "Authentification", + "keywords": "sessions, 2fa, login" + }, + "credits_data": { + "label": "Crédits et transactions", + "keywords": "solde, transactions" + }, + "project_data": { + "label": "Données projet", + "keywords": "projets, apps, statistiques" + }, + "retention": { + "label": "Durées de conservation", + "keywords": "retention, rgpd, durées" + }, "backup": { "label": "Sauvegarde et restauration", "keywords": "backup, restore, import, archive, .mana" diff --git a/apps/mana/apps/web/src/lib/i18n/locales/settings/it.json b/apps/mana/apps/web/src/lib/i18n/locales/settings/it.json index 51068e2f9..86d47fe3e 100644 --- a/apps/mana/apps/web/src/lib/i18n/locales/settings/it.json +++ b/apps/mana/apps/web/src/lib/i18n/locales/settings/it.json @@ -1,21 +1,33 @@ { "categories": { - "general": { "label": "Generale", "description": "Tema, lingua, notifiche" }, - "ai": { "label": "IA", "description": "Backend di calcolo e modelli" }, - "security": { "label": "Sicurezza", "description": "Passkey, 2FA, cifratura e sessioni" }, + "general": { + "label": "Generale", + "description": "Tema, lingua, notifiche" + }, + "ai": { + "label": "IA", + "description": "Backend di calcolo e modelli" + }, + "security": { + "label": "Sicurezza", + "description": "Passkey, 2FA, cifratura e sessioni" + }, "privacy": { "label": "Privacy", "description": "Cosa è pubblico o condiviso tramite link adesso — con interruttore di emergenza." }, - "community": { - "label": "Community", - "description": "Come appari nella community — pseudonimo di gufo, nome reale, karma." - }, "data": { "label": "Dati e sincronizzazione", "description": "Cloud sync, esportazione, backup e GDPR" }, - "tag_presets": { "label": "Preset di tag", "description": "Set di tag per nuovi spazi" } + "tag_presets": { + "label": "Preset di tag", + "description": "Set di tag per nuovi spazi" + }, + "feedback": { + "label": "Feedback", + "description": "Come appari nel feed di feedback — pseudonimo di gufo, nome reale, karma." + } }, "sidebar": { "aria_categories": "Categorie delle impostazioni", @@ -25,21 +37,54 @@ "no_results": "Nessun risultato per «{query}»" }, "search": { - "theme": { "label": "Tema", "keywords": "scuro, chiaro, colore, design" }, - "language": { "label": "Lingua", "keywords": "language, i18n, tedesco, inglese" }, - "notifications": { "label": "Notifiche", "keywords": "notification, suono" }, - "ai_options": { "label": "Opzioni IA", "keywords": "llm, ia, ai, compute" }, + "theme": { + "label": "Tema", + "keywords": "scuro, chiaro, colore, design" + }, + "language": { + "label": "Lingua", + "keywords": "language, i18n, tedesco, inglese" + }, + "notifications": { + "label": "Notifiche", + "keywords": "notification, suono" + }, + "ai_options": { + "label": "Opzioni IA", + "keywords": "llm, ia, ai, compute" + }, "browser_model": { "label": "Modello del browser (Gemma)", "keywords": "gemma, webgpu, locale, offline" }, - "mana_server": { "label": "Server Mana (IA)", "keywords": "server, self-hosted" }, - "cloud_ai": { "label": "IA cloud (Gemini)", "keywords": "google, cloud, gemini" }, - "passkeys": { "label": "Passkey", "keywords": "webauthn, fido, biometria" }, - "sessions": { "label": "Sessioni attive", "keywords": "logout, dispositivo" }, - "two_factor": { "label": "Due fattori (2FA)", "keywords": "totp, 2fa, mfa" }, - "vault": { "label": "Cifratura", "keywords": "vault, cifratura, aes, chiave, zero-knowledge" }, - "security_log": { "label": "Registro di sicurezza", "keywords": "audit, cronologia" }, + "mana_server": { + "label": "Server Mana (IA)", + "keywords": "server, self-hosted" + }, + "cloud_ai": { + "label": "IA cloud (Gemini)", + "keywords": "google, cloud, gemini" + }, + "passkeys": { + "label": "Passkey", + "keywords": "webauthn, fido, biometria" + }, + "sessions": { + "label": "Sessioni attive", + "keywords": "logout, dispositivo" + }, + "two_factor": { + "label": "Due fattori (2FA)", + "keywords": "totp, 2fa, mfa" + }, + "vault": { + "label": "Cifratura", + "keywords": "vault, cifratura, aes, chiave, zero-knowledge" + }, + "security_log": { + "label": "Registro di sicurezza", + "keywords": "audit, cronologia" + }, "privacy_overview": { "label": "Panoramica privacy", "keywords": "pubblico, unlisted, condividere, link" @@ -48,25 +93,46 @@ "label": "Riporta tutto a privato", "keywords": "kill-switch, reset, privato, revocare" }, - "community_show_real_name": { - "label": "Nome reale nella community", + "feedback_show_real_name": { + "label": "Nome reale nel feed di feedback", "keywords": "nome, gufo, pseudonimo, anonimo, identità" }, - "community_karma": { + "feedback_karma": { "label": "Karma e tier", "keywords": "karma, tier, bronzo, argento, oro, platino, gufo" }, - "cloud_sync": { "label": "Cloud Sync", "keywords": "sync, dispositivi" }, - "data_export": { "label": "Esporta i dati", "keywords": "export, gdpr, json" }, - "auth_data": { "label": "Autenticazione", "keywords": "sessioni, 2fa, login" }, - "credits_data": { "label": "Crediti e transazioni", "keywords": "saldo, transazioni" }, - "project_data": { "label": "Dati di progetto", "keywords": "progetti, app, statistiche" }, - "retention": { "label": "Periodi di conservazione", "keywords": "retention, gdpr, periodi" }, + "cloud_sync": { + "label": "Cloud Sync", + "keywords": "sync, dispositivi" + }, + "data_export": { + "label": "Esporta i dati", + "keywords": "export, gdpr, json" + }, + "auth_data": { + "label": "Autenticazione", + "keywords": "sessioni, 2fa, login" + }, + "credits_data": { + "label": "Crediti e transazioni", + "keywords": "saldo, transazioni" + }, + "project_data": { + "label": "Dati di progetto", + "keywords": "progetti, app, statistiche" + }, + "retention": { + "label": "Periodi di conservazione", + "keywords": "retention, gdpr, periodi" + }, "backup": { "label": "Backup e ripristino", "keywords": "backup, restore, import, archivio, .mana" }, - "delete_account": { "label": "Elimina account", "keywords": "delete, gdpr, zona di pericolo" } + "delete_account": { + "label": "Elimina account", + "keywords": "delete, gdpr, zona di pericolo" + } }, "general": { "title": "Generale", diff --git a/apps/mana/apps/web/src/lib/modules/settings/ListView.svelte b/apps/mana/apps/web/src/lib/modules/settings/ListView.svelte index e307f2a08..0fcaaac25 100644 --- a/apps/mana/apps/web/src/lib/modules/settings/ListView.svelte +++ b/apps/mana/apps/web/src/lib/modules/settings/ListView.svelte @@ -16,7 +16,7 @@ import AiSection from '$lib/components/settings/sections/AiSection.svelte'; import SecuritySection from '$lib/components/settings/sections/SecuritySection.svelte'; import PrivacySection from '$lib/components/settings/sections/PrivacySection.svelte'; - import CommunitySection from '$lib/components/settings/sections/CommunitySection.svelte'; + import FeedbackIdentitySection from '$lib/components/settings/sections/FeedbackIdentitySection.svelte'; import DataSection from '$lib/components/settings/sections/DataSection.svelte'; import TagPresetsSection from '$lib/components/settings/sections/TagPresetsSection.svelte'; @@ -80,8 +80,8 @@ {:else if activeCategory === 'privacy'} - {:else if activeCategory === 'community'} - + {:else if activeCategory === 'feedback'} + {:else if activeCategory === 'data'} {:else if activeCategory === 'tag-presets'} diff --git a/services/mana-analytics/src/db/schema/auth-users.ts b/services/mana-analytics/src/db/schema/auth-users.ts index 1be4ac984..b2418879e 100644 --- a/services/mana-analytics/src/db/schema/auth-users.ts +++ b/services/mana-analytics/src/db/schema/auth-users.ts @@ -1,5 +1,5 @@ /** - * Read-only cross-schema view of auth.users for the public-community + * Read-only cross-schema view of auth.users for the public-feedback * hub. mana-auth owns the table; we JOIN it from mana-analytics to * enrich feed responses with the post-author's real-name opt-in and * karma score. We never INSERT/UPDATE/DELETE here — that's @@ -17,6 +17,6 @@ const authSchema = pgSchema('auth'); export const authUsers = authSchema.table('users', { id: text('id').primaryKey(), name: text('name').notNull(), - communityShowRealName: boolean('community_show_real_name').default(false).notNull(), - communityKarma: integer('community_karma').default(0).notNull(), + feedbackShowRealName: boolean('feedback_show_real_name').default(false).notNull(), + feedbackKarma: integer('feedback_karma').default(0).notNull(), }); diff --git a/services/mana-analytics/src/services/feedback-karma.integration.test.ts b/services/mana-analytics/src/services/feedback-karma.integration.test.ts index a8e56f358..2b2ba7bcf 100644 --- a/services/mana-analytics/src/services/feedback-karma.integration.test.ts +++ b/services/mana-analytics/src/services/feedback-karma.integration.test.ts @@ -1,7 +1,7 @@ /** * Integration tests for the cross-schema karma flow. * - * Karma lives on auth.users.community_karma; mana-analytics increments + * Karma lives on auth.users.feedback_karma; mana-analytics increments * it inside toggleReaction. Tests verify the SQL path, the self-react * skip, and the floor-at-zero clamp. */ diff --git a/services/mana-analytics/src/services/feedback-redact.test.ts b/services/mana-analytics/src/services/feedback-redact.test.ts index 89f43373e..2629574ea 100644 --- a/services/mana-analytics/src/services/feedback-redact.test.ts +++ b/services/mana-analytics/src/services/feedback-redact.test.ts @@ -2,7 +2,7 @@ * Privacy-boundary tests für die `redact()`-Funktion. * * Kritisch: anonymous public endpoint darf NIE einen Klarnamen - * ausliefern, auch wenn der User-Account `communityShowRealName=true` + * ausliefern, auch wenn der User-Account `feedbackShowRealName=true` * gesetzt hat. Diese Tests sind das Sicherheitsnetz für die ›Public * bleibt anonym‹-Garantie der Community-Surface. */ @@ -35,14 +35,14 @@ const baseFeedback = { const optedInAuthor = { name: 'Till Schäfer', - communityShowRealName: true, - communityKarma: 47, + feedbackShowRealName: true, + feedbackKarma: 47, }; const optedOutAuthor = { name: 'Till Schäfer', - communityShowRealName: false, - communityKarma: 47, + feedbackShowRealName: false, + feedbackKarma: 47, }; describe('redact (privacy-boundary)', () => { diff --git a/services/mana-analytics/src/services/feedback.ts b/services/mana-analytics/src/services/feedback.ts index d474c7528..a7f8656dc 100644 --- a/services/mana-analytics/src/services/feedback.ts +++ b/services/mana-analytics/src/services/feedback.ts @@ -78,7 +78,7 @@ export type PublicFeedbackItem = { /** Author's community karma (public, drives tier-badge). */ karma: number; /** Real name, only present when: - * - the post-author opted in via communityShowRealName=true, AND + * - the post-author opted in via feedbackShowRealName=true, AND * - the response is going to an authenticated caller (the * anonymous /public endpoint always strips this). */ @@ -88,8 +88,8 @@ export type PublicFeedbackItem = { type FeedbackRow = typeof userFeedback.$inferSelect; type AuthUserRow = { name: string; - communityShowRealName: boolean; - communityKarma: number; + feedbackShowRealName: boolean; + feedbackKarma: number; } | null; export class FeedbackService { @@ -301,7 +301,7 @@ export class FeedbackService { const items = rows.map((r) => redact(r.feedback, r.author, { includeRealName: false })); const displayName = items[0]?.displayName ?? null; - const karma = rows[0]?.author?.communityKarma ?? 0; + const karma = rows[0]?.author?.feedbackKarma ?? 0; return { displayHash, displayName, karma, items }; } @@ -327,8 +327,8 @@ export class FeedbackService { private authorSelection() { return { name: authUsers.name, - communityShowRealName: authUsers.communityShowRealName, - communityKarma: authUsers.communityKarma, + feedbackShowRealName: authUsers.feedbackShowRealName, + feedbackKarma: authUsers.feedbackKarma, }; } @@ -442,7 +442,7 @@ export class FeedbackService { await this.db .update(authUsers) .set({ - communityKarma: sql`GREATEST(${authUsers.communityKarma} + ${delta}, 0)`, + feedbackKarma: sql`GREATEST(${authUsers.feedbackKarma} + ${delta}, 0)`, }) .where(eq(authUsers.id, item.authorId)); } @@ -767,9 +767,9 @@ function redact( adminResponse: row.adminResponse, createdAt: row.createdAt, updatedAt: row.updatedAt, - karma: author?.communityKarma ?? 0, + karma: author?.feedbackKarma ?? 0, }; - if (includeReal && author?.communityShowRealName && author.name) { + if (includeReal && author?.feedbackShowRealName && author.name) { item.realName = author.name; } return item; diff --git a/services/mana-analytics/src/test-helpers/db.ts b/services/mana-analytics/src/test-helpers/db.ts index e910ad343..4b1e4a5c4 100644 --- a/services/mana-analytics/src/test-helpers/db.ts +++ b/services/mana-analytics/src/test-helpers/db.ts @@ -47,7 +47,7 @@ let seededIds = new Set(); */ export async function seedUser( db: TestDb, - overrides: Partial<{ name: string; communityShowRealName: boolean; communityKarma: number }> = {} + overrides: Partial<{ name: string; feedbackShowRealName: boolean; feedbackKarma: number }> = {} ): Promise { const id = `test-${randomUUID()}`; const email = `${id}@test.local`; @@ -57,13 +57,13 @@ export async function seedUser( // model only declares the columns mana-analytics READS — auth.users // has additional NOT NULL columns (email, etc.) we'd otherwise miss. await db.execute(sql` - INSERT INTO auth.users (id, email, name, community_show_real_name, community_karma) + INSERT INTO auth.users (id, email, name, feedback_show_real_name, feedback_karma) VALUES ( ${id}, ${email}, ${name}, - ${overrides.communityShowRealName ?? false}, - ${overrides.communityKarma ?? 0} + ${overrides.feedbackShowRealName ?? false}, + ${overrides.feedbackKarma ?? 0} ) ON CONFLICT (id) DO NOTHING `); @@ -71,10 +71,10 @@ export async function seedUser( return { id, email, name }; } -/** Read auth.users.community_karma for a test user. */ +/** Read auth.users.feedback_karma for a test user. */ export async function getKarma(db: TestDb, userId: string): Promise { const [row] = await db - .select({ karma: authUsers.communityKarma }) + .select({ karma: authUsers.feedbackKarma }) .from(authUsers) .where(eq(authUsers.id, userId)) .limit(1); diff --git a/services/mana-auth/sql/009_rename_community_to_feedback.sql b/services/mana-auth/sql/009_rename_community_to_feedback.sql new file mode 100644 index 000000000..5896d175d --- /dev/null +++ b/services/mana-auth/sql/009_rename_community_to_feedback.sql @@ -0,0 +1,17 @@ +-- 009_rename_community_to_feedback.sql +-- Renames the two identity-opt-in columns on auth.users to match the +-- "feedback" brand the public hub now carries. Was originally added +-- in 008_community_identity.sql. +-- +-- Apply with: +-- psql "$DATABASE_URL" -f sql/009_rename_community_to_feedback.sql + +BEGIN; + +ALTER TABLE auth.users + RENAME COLUMN community_show_real_name TO feedback_show_real_name; + +ALTER TABLE auth.users + RENAME COLUMN community_karma TO feedback_karma; + +COMMIT; diff --git a/services/mana-auth/src/db/schema/auth.ts b/services/mana-auth/src/db/schema/auth.ts index ae97530c6..74e5423bb 100644 --- a/services/mana-auth/src/db/schema/auth.ts +++ b/services/mana-auth/src/db/schema/auth.ts @@ -53,14 +53,14 @@ export const users = authSchema.table('users', { // → Look → Templates). The flow is skippable, but even a skip sets // this timestamp so we don't re-prompt. See docs/plans/onboarding-flow.md. onboardingCompletedAt: timestamp('onboarding_completed_at', { withTimezone: true }), - // Community-Hub identity opt-ins (Phase 3.C of feedback-rewards-and-identity). + // Public-feedback identity opt-ins (Phase 3.C of feedback-rewards-and-identity). // Off by default — users stay anonymous as their tier-pseudonym ("Wachsame // Eule #4528"). Opt-in shows the real `name` next to the pseudonym in the - // auth-required community feed only; the public-mirror NEVER exposes it. - communityShowRealName: boolean('community_show_real_name').default(false).notNull(), + // auth-required feedback feed only; the public-mirror NEVER exposes it. + feedbackShowRealName: boolean('feedback_show_real_name').default(false).notNull(), // Karma += 1 per reaction received from another user, decremented on unreact. // Drives the public Bronze/Silver/Gold/Platinum-Eulen tier badge. - communityKarma: integer('community_karma').default(0).notNull(), + feedbackKarma: integer('feedback_karma').default(0).notNull(), }); // Sessions table (Better Auth schema) diff --git a/services/mana-auth/src/routes/me.ts b/services/mana-auth/src/routes/me.ts index 39c179b95..65d3c529a 100644 --- a/services/mana-auth/src/routes/me.ts +++ b/services/mana-auth/src/routes/me.ts @@ -72,13 +72,13 @@ export function createMeRoutes(userDataService: UserDataService, db: Database) { const body = (await c.req.json().catch(() => ({}))) as { name?: unknown; image?: unknown; - communityShowRealName?: unknown; + feedbackShowRealName?: unknown; }; const patch: { name?: string; image?: string; - communityShowRealName?: boolean; + feedbackShowRealName?: boolean; updatedAt: Date; } = { updatedAt: new Date(), @@ -93,11 +93,11 @@ export function createMeRoutes(userDataService: UserDataService, db: Database) { if (typeof body.image === 'string') { patch.image = body.image; } - if (typeof body.communityShowRealName === 'boolean') { - patch.communityShowRealName = body.communityShowRealName; + if (typeof body.feedbackShowRealName === 'boolean') { + patch.feedbackShowRealName = body.feedbackShowRealName; } - if (!('name' in patch) && !('image' in patch) && !('communityShowRealName' in patch)) { + if (!('name' in patch) && !('image' in patch) && !('feedbackShowRealName' in patch)) { return c.json({ error: 'no fields to update' }, 400); } @@ -109,14 +109,14 @@ export function createMeRoutes(userDataService: UserDataService, db: Database) { id: users.id, name: users.name, image: users.image, - communityShowRealName: users.communityShowRealName, + feedbackShowRealName: users.feedbackShowRealName, }); if (!updated) return c.json({ error: 'User not found' }, 404); return c.json({ name: updated.name, image: updated.image, - communityShowRealName: updated.communityShowRealName, + feedbackShowRealName: updated.feedbackShowRealName, }); }) );