diff --git a/frontend/src/i18n/locales/de.json b/frontend/src/i18n/locales/de.json index 33b4344c..10342032 100644 --- a/frontend/src/i18n/locales/de.json +++ b/frontend/src/i18n/locales/de.json @@ -1870,7 +1870,44 @@ "photosBulkDeleted_one": "{{count}} Foto aus \"{{eventName}}\" gelöscht", "photosBulkDeleted_other": "{{count}} Fotos aus \"{{eventName}}\" gelöscht", "bulkDownload_one": "{{count}} Foto aus \"{{eventName}}\" heruntergeladen", - "bulkDownload_other": "{{count}} Fotos aus \"{{eventName}}\" heruntergeladen" + "bulkDownload_other": "{{count}} Fotos aus \"{{eventName}}\" heruntergeladen", + "customerLogin": "Kunde angemeldet: {{email}}", + "customerInvitationCreated": "Kunden-Einladung gesendet an {{email}}", + "customerInvitationAccepted": "Kunde hat Einladung angenommen: {{email}}", + "customerInvitationCancelled": "Kunden-Einladung zurückgezogen: {{email}}", + "customerPasswordResetRequested": "Passwort-Reset für Kunde angefordert: {{email}}", + "customerPasswordResetApplied": "Kunden-Passwort durch Admin zurückgesetzt: {{email}}", + "customerPasswordChange": "Kunde hat Passwort geändert: {{email}}", + "customerSelfProfileUpdate": "Kunde hat Profil aktualisiert: {{email}}", + "customerEventAccess": "{{email}} hat {{eventName}} geöffnet", + "customerUpdated": "Kunden-Konto aktualisiert: {{email}}", + "customerDeactivated": "Kunden-Konto deaktiviert: {{email}}", + "customerReactivated": "Kunden-Konto reaktiviert: {{email}}", + "customerErased": "Kunden-Konto gelöscht (DSGVO): {{email}}", + "adminInvitationCreated": "Admin-Einladung gesendet an {{email}}", + "adminInvitationAccepted": "Admin hat Einladung angenommen: {{email}}", + "adminInvitationCancelled": "Admin-Einladung zurückgezogen: {{email}}", + "adminUserUpdated": "Admin-Benutzer aktualisiert: {{username}}", + "adminUserDeactivated": "Admin-Benutzer deaktiviert: {{username}}", + "webhookCreated": "Webhook erstellt: {{name}}", + "webhookUpdated": "Webhook aktualisiert: {{name}}", + "webhookDeleted": "Webhook gelöscht: {{name}}", + "apiTokenCreated": "API-Token erstellt: {{name}}", + "apiTokenRevoked": "API-Token widerrufen: {{name}}", + "eventTypeCreated": "Event-Typ erstellt: {{name}}", + "eventTypeUpdated": "Event-Typ aktualisiert: {{name}}", + "eventTypeDeleted": "Event-Typ gelöscht: {{name}}", + "eventTypesReordered": "Event-Typen neu sortiert", + "eventPublished": "Event veröffentlicht: {{eventName}}", + "eventLogoUploaded": "Event-Logo hochgeladen für {{eventName}}", + "eventLogoRemoved": "Event-Logo entfernt für {{eventName}}", + "bulkDeleteCompleted": "Massen-Löschung abgeschlossen: {{count}} Events entfernt", + "photoReplaced": "Foto ersetzt in {{eventName}}", + "photoUploaded": "Foto hochgeladen in {{eventName}}", + "categoryHeroUpdated": "Kategorie-Titelbild aktualisiert", + "publicSiteResetToDefault": "Öffentliche Seite auf Standard zurückgesetzt", + "cmsPageLogoUploaded": "CMS-Seiten-Logo hochgeladen: {{slug}}", + "adminPasswordResetByAdmin": "Admin-Passwort durch anderen Admin zurückgesetzt: {{username}}" }, "notificationToasts": { "markedAllRead": "Alle Benachrichtigungen als gelesen markiert", @@ -2023,7 +2060,49 @@ "guest_feedback_rating": "Gast hat ein Foto bewertet in {{eventName}}", "guest_feedback_comment": "Gast hat ein Foto kommentiert in {{eventName}}", "system_activity": "Systemaktivität: {{type}}", - "unknown": "Unbekannte Aktivität" + "unknown": "Unbekannte Aktivität", + "feature_enabled": "{{label}} aktiviert", + "feature_disabled": "{{label}} deaktiviert", + "feature_flags_summary": "{{count}} Funktionen aktualisiert: {{summary}}", + "feature_flags_noop": "Funktions-Schalter geprüft (keine Änderungen)", + "customer_login": "Kunde angemeldet: {{email}}", + "customer_invitation_created": "Kunden-Einladung gesendet an {{email}}", + "customer_invitation_accepted": "Kunde hat Einladung angenommen: {{email}}", + "customer_invitation_cancelled": "Kunden-Einladung zurückgezogen: {{email}}", + "customer_password_reset_requested": "Passwort-Reset für Kunde angefordert: {{email}}", + "customer_password_reset_applied": "Kunden-Passwort durch Admin zurückgesetzt: {{email}}", + "customer_password_change": "Kunde hat Passwort geändert: {{email}}", + "customer_self_profile_update": "Kunde hat Profil aktualisiert: {{email}}", + "customer_event_access": "{{email}} hat {{eventName}} geöffnet", + "customer_updated": "Kunden-Konto aktualisiert: {{email}}", + "customer_deactivated": "Kunden-Konto deaktiviert: {{email}}", + "customer_reactivated": "Kunden-Konto reaktiviert: {{email}}", + "customer_erased": "Kunden-Konto gelöscht (DSGVO): {{email}}", + "admin_invitation_created": "Admin-Einladung gesendet an {{email}}", + "admin_invitation_accepted": "Admin hat Einladung angenommen: {{email}}", + "admin_invitation_cancelled": "Admin-Einladung zurückgezogen: {{email}}", + "admin_user_updated": "Admin-Benutzer aktualisiert: {{username}}", + "admin_user_deactivated": "Admin-Benutzer deaktiviert: {{username}}", + "admin_password_reset": "Admin-Passwort durch anderen Admin zurückgesetzt: {{username}}", + "admin_profile_updated": "Admin {{actorName}} hat Profil aktualisiert", + "webhook_created": "Webhook erstellt: {{name}}", + "webhook_updated": "Webhook aktualisiert: {{name}}", + "webhook_deleted": "Webhook gelöscht: {{name}}", + "api_token_created": "API-Token erstellt: {{name}}", + "api_token_revoked": "API-Token widerrufen: {{name}}", + "event_type_created": "Event-Typ erstellt: {{name}}", + "event_type_updated": "Event-Typ aktualisiert: {{name}}", + "event_type_deleted": "Event-Typ gelöscht: {{name}}", + "event_types_reordered": "Event-Typen neu sortiert", + "event_published": "Event veröffentlicht: {{eventName}}", + "event_logo_uploaded": "Event-Logo hochgeladen für {{eventName}}", + "event_logo_removed": "Event-Logo entfernt für {{eventName}}", + "bulk_delete_completed": "Massen-Löschung abgeschlossen: {{count}} Events entfernt", + "photo_replaced": "Foto ersetzt in {{eventName}}", + "photo_uploaded": "Foto hochgeladen in {{eventName}}", + "category_hero_updated": "Kategorie-Titelbild aktualisiert", + "public_site_reset_to_default": "Öffentliche Seite auf Standard zurückgesetzt", + "cms_page_logo_uploaded": "CMS-Seiten-Logo hochgeladen: {{slug}}" } }, "acceptInvitation": { diff --git a/frontend/src/i18n/locales/en.json b/frontend/src/i18n/locales/en.json index bded6c9d..075a555d 100644 --- a/frontend/src/i18n/locales/en.json +++ b/frontend/src/i18n/locales/en.json @@ -1538,7 +1538,44 @@ "photosBulkDeleted_one": "{{count}} photo deleted from \"{{eventName}}\"", "photosBulkDeleted_other": "{{count}} photos deleted from \"{{eventName}}\"", "bulkDownload_one": "{{count}} photo downloaded from \"{{eventName}}\"", - "bulkDownload_other": "{{count}} photos downloaded from \"{{eventName}}\"" + "bulkDownload_other": "{{count}} photos downloaded from \"{{eventName}}\"", + "customerLogin": "Customer logged in: {{email}}", + "customerInvitationCreated": "Customer invitation sent to {{email}}", + "customerInvitationAccepted": "Customer accepted invitation: {{email}}", + "customerInvitationCancelled": "Customer invitation cancelled: {{email}}", + "customerPasswordResetRequested": "Password reset requested for customer {{email}}", + "customerPasswordResetApplied": "Customer password reset by admin: {{email}}", + "customerPasswordChange": "Customer changed their password: {{email}}", + "customerSelfProfileUpdate": "Customer updated their profile: {{email}}", + "customerEventAccess": "{{email}} opened {{eventName}}", + "customerUpdated": "Customer account updated: {{email}}", + "customerDeactivated": "Customer account deactivated: {{email}}", + "customerReactivated": "Customer account reactivated: {{email}}", + "customerErased": "Customer account erased (GDPR): {{email}}", + "adminInvitationCreated": "Admin invitation sent to {{email}}", + "adminInvitationAccepted": "Admin accepted invitation: {{email}}", + "adminInvitationCancelled": "Admin invitation cancelled: {{email}}", + "adminUserUpdated": "Admin user updated: {{username}}", + "adminUserDeactivated": "Admin user deactivated: {{username}}", + "webhookCreated": "Webhook created: {{name}}", + "webhookUpdated": "Webhook updated: {{name}}", + "webhookDeleted": "Webhook deleted: {{name}}", + "apiTokenCreated": "API token created: {{name}}", + "apiTokenRevoked": "API token revoked: {{name}}", + "eventTypeCreated": "Event type created: {{name}}", + "eventTypeUpdated": "Event type updated: {{name}}", + "eventTypeDeleted": "Event type deleted: {{name}}", + "eventTypesReordered": "Event types reordered", + "eventPublished": "Event published: {{eventName}}", + "eventLogoUploaded": "Event logo uploaded for {{eventName}}", + "eventLogoRemoved": "Event logo removed for {{eventName}}", + "bulkDeleteCompleted": "Bulk delete completed: {{count}} events removed", + "photoReplaced": "Photo replaced in {{eventName}}", + "photoUploaded": "Photo uploaded to {{eventName}}", + "categoryHeroUpdated": "Category hero photo updated", + "publicSiteResetToDefault": "Public site reset to default", + "cmsPageLogoUploaded": "CMS page logo uploaded: {{slug}}", + "adminPasswordResetByAdmin": "Admin password reset by another admin: {{username}}" }, "notificationToasts": { "markedAllRead": "All notifications marked as read", @@ -1693,7 +1730,49 @@ "password_reset": "Password reset for: {{eventName}}", "admin_logout": "Admin {{actorName}} logged out", "system_activity": "System activity: {{type}}", - "unknown": "Unknown activity" + "unknown": "Unknown activity", + "feature_enabled": "{{label}} enabled", + "feature_disabled": "{{label}} disabled", + "feature_flags_summary": "{{count}} features updated: {{summary}}", + "feature_flags_noop": "Feature flags reviewed (no changes)", + "customer_login": "Customer logged in: {{email}}", + "customer_invitation_created": "Customer invitation sent to {{email}}", + "customer_invitation_accepted": "Customer accepted invitation: {{email}}", + "customer_invitation_cancelled": "Customer invitation cancelled: {{email}}", + "customer_password_reset_requested": "Password reset requested for customer {{email}}", + "customer_password_reset_applied": "Customer password reset by admin: {{email}}", + "customer_password_change": "Customer changed their password: {{email}}", + "customer_self_profile_update": "Customer updated their profile: {{email}}", + "customer_event_access": "{{email}} opened {{eventName}}", + "customer_updated": "Customer account updated: {{email}}", + "customer_deactivated": "Customer account deactivated: {{email}}", + "customer_reactivated": "Customer account reactivated: {{email}}", + "customer_erased": "Customer account erased (GDPR): {{email}}", + "admin_invitation_created": "Admin invitation sent to {{email}}", + "admin_invitation_accepted": "Admin accepted invitation: {{email}}", + "admin_invitation_cancelled": "Admin invitation cancelled: {{email}}", + "admin_user_updated": "Admin user updated: {{username}}", + "admin_user_deactivated": "Admin user deactivated: {{username}}", + "admin_password_reset": "Admin password reset by another admin: {{username}}", + "admin_profile_updated": "Admin {{actorName}} updated their profile", + "webhook_created": "Webhook created: {{name}}", + "webhook_updated": "Webhook updated: {{name}}", + "webhook_deleted": "Webhook deleted: {{name}}", + "api_token_created": "API token created: {{name}}", + "api_token_revoked": "API token revoked: {{name}}", + "event_type_created": "Event type created: {{name}}", + "event_type_updated": "Event type updated: {{name}}", + "event_type_deleted": "Event type deleted: {{name}}", + "event_types_reordered": "Event types reordered", + "event_published": "Event published: {{eventName}}", + "event_logo_uploaded": "Event logo uploaded for {{eventName}}", + "event_logo_removed": "Event logo removed for {{eventName}}", + "bulk_delete_completed": "Bulk delete completed: {{count}} events removed", + "photo_replaced": "Photo replaced in {{eventName}}", + "photo_uploaded": "Photo uploaded to {{eventName}}", + "category_hero_updated": "Category hero photo updated", + "public_site_reset_to_default": "Public site reset to default", + "cms_page_logo_uploaded": "CMS page logo uploaded: {{slug}}" } }, "acceptInvitation": { diff --git a/frontend/src/i18n/locales/fr.json b/frontend/src/i18n/locales/fr.json index bf6f5e68..ad71d41a 100644 --- a/frontend/src/i18n/locales/fr.json +++ b/frontend/src/i18n/locales/fr.json @@ -1479,7 +1479,44 @@ "photosBulkDeleted_other": "{{count}} photos supprimées de « {{eventName}} »", "bulkDownload_many": "{{count}} photos téléchargées depuis « {{eventName}} »", "bulkDownload_one": "{{count}} photo téléchargée depuis « {{eventName}} »", - "bulkDownload_other": "{{count}} photos téléchargées depuis « {{eventName}} »" + "bulkDownload_other": "{{count}} photos téléchargées depuis « {{eventName}} »", + "customerLogin": "Client connecté : {{email}}", + "customerInvitationCreated": "Invitation client envoyée à {{email}}", + "customerInvitationAccepted": "Client a accepté l’invitation : {{email}}", + "customerInvitationCancelled": "Invitation client annulée : {{email}}", + "customerPasswordResetRequested": "Réinitialisation du mot de passe demandée pour {{email}}", + "customerPasswordResetApplied": "Mot de passe client réinitialisé par admin : {{email}}", + "customerPasswordChange": "Client a changé son mot de passe : {{email}}", + "customerSelfProfileUpdate": "Client a mis à jour son profil : {{email}}", + "customerEventAccess": "{{email}} a ouvert {{eventName}}", + "customerUpdated": "Compte client mis à jour : {{email}}", + "customerDeactivated": "Compte client désactivé : {{email}}", + "customerReactivated": "Compte client réactivé : {{email}}", + "customerErased": "Compte client effacé (RGPD) : {{email}}", + "adminInvitationCreated": "Invitation admin envoyée à {{email}}", + "adminInvitationAccepted": "Admin a accepté l’invitation : {{email}}", + "adminInvitationCancelled": "Invitation admin annulée : {{email}}", + "adminUserUpdated": "Utilisateur admin mis à jour : {{username}}", + "adminUserDeactivated": "Utilisateur admin désactivé : {{username}}", + "webhookCreated": "Webhook créé : {{name}}", + "webhookUpdated": "Webhook mis à jour : {{name}}", + "webhookDeleted": "Webhook supprimé : {{name}}", + "apiTokenCreated": "Jeton API créé : {{name}}", + "apiTokenRevoked": "Jeton API révoqué : {{name}}", + "eventTypeCreated": "Type d’événement créé : {{name}}", + "eventTypeUpdated": "Type d’événement mis à jour : {{name}}", + "eventTypeDeleted": "Type d’événement supprimé : {{name}}", + "eventTypesReordered": "Types d’événements réorganisés", + "eventPublished": "Événement publié : {{eventName}}", + "eventLogoUploaded": "Logo d’événement téléversé pour {{eventName}}", + "eventLogoRemoved": "Logo d’événement retiré pour {{eventName}}", + "bulkDeleteCompleted": "Suppression en masse terminée : {{count}} événements supprimés", + "photoReplaced": "Photo remplacée dans {{eventName}}", + "photoUploaded": "Photo téléversée dans {{eventName}}", + "categoryHeroUpdated": "Photo principale de la catégorie mise à jour", + "publicSiteResetToDefault": "Site public réinitialisé par défaut", + "cmsPageLogoUploaded": "Logo de page CMS téléversé : {{slug}}", + "adminPasswordResetByAdmin": "Mot de passe admin réinitialisé par un autre admin : {{username}}" }, "notificationToasts": { "markedAllRead": "Toutes les notifications sont lues", @@ -1639,7 +1676,49 @@ "guest_feedback_rating": "Un invité a noté une photo dans {{eventName}}", "guest_feedback_comment": "Un invité a commenté une photo dans {{eventName}}", "system_activity": "Activité système : {{type}}", - "unknown": "Activité inconnue" + "unknown": "Activité inconnue", + "feature_enabled": "{{label}} activé", + "feature_disabled": "{{label}} désactivé", + "feature_flags_summary": "{{count}} fonctionnalités mises à jour : {{summary}}", + "feature_flags_noop": "Indicateurs revus (aucun changement)", + "customer_login": "Client connecté : {{email}}", + "customer_invitation_created": "Invitation client envoyée à {{email}}", + "customer_invitation_accepted": "Client a accepté l’invitation : {{email}}", + "customer_invitation_cancelled": "Invitation client annulée : {{email}}", + "customer_password_reset_requested": "Réinitialisation du mot de passe demandée pour {{email}}", + "customer_password_reset_applied": "Mot de passe client réinitialisé par admin : {{email}}", + "customer_password_change": "Client a changé son mot de passe : {{email}}", + "customer_self_profile_update": "Client a mis à jour son profil : {{email}}", + "customer_event_access": "{{email}} a ouvert {{eventName}}", + "customer_updated": "Compte client mis à jour : {{email}}", + "customer_deactivated": "Compte client désactivé : {{email}}", + "customer_reactivated": "Compte client réactivé : {{email}}", + "customer_erased": "Compte client effacé (RGPD) : {{email}}", + "admin_invitation_created": "Invitation admin envoyée à {{email}}", + "admin_invitation_accepted": "Admin a accepté l’invitation : {{email}}", + "admin_invitation_cancelled": "Invitation admin annulée : {{email}}", + "admin_user_updated": "Utilisateur admin mis à jour : {{username}}", + "admin_user_deactivated": "Utilisateur admin désactivé : {{username}}", + "admin_password_reset": "Mot de passe admin réinitialisé par un autre admin : {{username}}", + "admin_profile_updated": "Admin {{actorName}} a mis à jour son profil", + "webhook_created": "Webhook créé : {{name}}", + "webhook_updated": "Webhook mis à jour : {{name}}", + "webhook_deleted": "Webhook supprimé : {{name}}", + "api_token_created": "Jeton API créé : {{name}}", + "api_token_revoked": "Jeton API révoqué : {{name}}", + "event_type_created": "Type d’événement créé : {{name}}", + "event_type_updated": "Type d’événement mis à jour : {{name}}", + "event_type_deleted": "Type d’événement supprimé : {{name}}", + "event_types_reordered": "Types d’événements réorganisés", + "event_published": "Événement publié : {{eventName}}", + "event_logo_uploaded": "Logo d’événement téléversé pour {{eventName}}", + "event_logo_removed": "Logo d’événement retiré pour {{eventName}}", + "bulk_delete_completed": "Suppression en masse terminée : {{count}} événements supprimés", + "photo_replaced": "Photo remplacée dans {{eventName}}", + "photo_uploaded": "Photo téléversée dans {{eventName}}", + "category_hero_updated": "Photo principale de la catégorie mise à jour", + "public_site_reset_to_default": "Site public réinitialisé par défaut", + "cms_page_logo_uploaded": "Logo de page CMS téléversé : {{slug}}" } }, "acceptInvitation": { diff --git a/frontend/src/i18n/locales/nl.json b/frontend/src/i18n/locales/nl.json index 3efdea9b..9cf475c7 100644 --- a/frontend/src/i18n/locales/nl.json +++ b/frontend/src/i18n/locales/nl.json @@ -1538,7 +1538,44 @@ "photosBulkDeleted_one": "{{count}} foto verwijderd uit \"{{eventName}}\"", "photosBulkDeleted_other": "{{count}} foto's verwijderd uit \"{{eventName}}\"", "bulkDownload_one": "{{count}} foto gedownload uit \"{{eventName}}\"", - "bulkDownload_other": "{{count}} foto's gedownload uit \"{{eventName}}\"" + "bulkDownload_other": "{{count}} foto's gedownload uit \"{{eventName}}\"", + "customerLogin": "Klant ingelogd: {{email}}", + "customerInvitationCreated": "Klantuitnodiging verzonden naar {{email}}", + "customerInvitationAccepted": "Klant heeft uitnodiging geaccepteerd: {{email}}", + "customerInvitationCancelled": "Klantuitnodiging geannuleerd: {{email}}", + "customerPasswordResetRequested": "Wachtwoord-reset aangevraagd voor klant {{email}}", + "customerPasswordResetApplied": "Klantwachtwoord door admin gereset: {{email}}", + "customerPasswordChange": "Klant heeft wachtwoord gewijzigd: {{email}}", + "customerSelfProfileUpdate": "Klant heeft profiel bijgewerkt: {{email}}", + "customerEventAccess": "{{email}} heeft {{eventName}} geopend", + "customerUpdated": "Klantaccount bijgewerkt: {{email}}", + "customerDeactivated": "Klantaccount gedeactiveerd: {{email}}", + "customerReactivated": "Klantaccount geheractiveerd: {{email}}", + "customerErased": "Klantaccount gewist (AVG): {{email}}", + "adminInvitationCreated": "Beheerdersuitnodiging verzonden naar {{email}}", + "adminInvitationAccepted": "Beheerder heeft uitnodiging geaccepteerd: {{email}}", + "adminInvitationCancelled": "Beheerdersuitnodiging geannuleerd: {{email}}", + "adminUserUpdated": "Beheerdersaccount bijgewerkt: {{username}}", + "adminUserDeactivated": "Beheerdersaccount gedeactiveerd: {{username}}", + "webhookCreated": "Webhook aangemaakt: {{name}}", + "webhookUpdated": "Webhook bijgewerkt: {{name}}", + "webhookDeleted": "Webhook verwijderd: {{name}}", + "apiTokenCreated": "API-token aangemaakt: {{name}}", + "apiTokenRevoked": "API-token ingetrokken: {{name}}", + "eventTypeCreated": "Evenementtype aangemaakt: {{name}}", + "eventTypeUpdated": "Evenementtype bijgewerkt: {{name}}", + "eventTypeDeleted": "Evenementtype verwijderd: {{name}}", + "eventTypesReordered": "Evenementtypes gesorteerd", + "eventPublished": "Evenement gepubliceerd: {{eventName}}", + "eventLogoUploaded": "Evenementlogo geüpload voor {{eventName}}", + "eventLogoRemoved": "Evenementlogo verwijderd voor {{eventName}}", + "bulkDeleteCompleted": "Massa-verwijdering voltooid: {{count}} evenementen verwijderd", + "photoReplaced": "Foto vervangen in {{eventName}}", + "photoUploaded": "Foto geüpload naar {{eventName}}", + "categoryHeroUpdated": "Categorie hero-foto bijgewerkt", + "publicSiteResetToDefault": "Publieke site teruggezet naar standaard", + "cmsPageLogoUploaded": "CMS-pagina-logo geüpload: {{slug}}", + "adminPasswordResetByAdmin": "Beheerderswachtwoord door andere beheerder gereset: {{username}}" }, "notificationToasts": { "markedAllRead": "Alle meldingen als gelezen gemarkeerd", @@ -1693,7 +1730,49 @@ "guest_feedback_rating": "Gast heeft een foto beoordeeld in {{eventName}}", "guest_feedback_comment": "Gast heeft een opmerking geplaatst op een foto in {{eventName}}", "system_activity": "Systeemactiviteit: {{type}}", - "unknown": "Onbekende activiteit" + "unknown": "Onbekende activiteit", + "feature_enabled": "{{label}} ingeschakeld", + "feature_disabled": "{{label}} uitgeschakeld", + "feature_flags_summary": "{{count}} functies bijgewerkt: {{summary}}", + "feature_flags_noop": "Functie-schakelaars bekeken (geen wijzigingen)", + "customer_login": "Klant ingelogd: {{email}}", + "customer_invitation_created": "Klantuitnodiging verzonden naar {{email}}", + "customer_invitation_accepted": "Klant heeft uitnodiging geaccepteerd: {{email}}", + "customer_invitation_cancelled": "Klantuitnodiging geannuleerd: {{email}}", + "customer_password_reset_requested": "Wachtwoord-reset aangevraagd voor klant {{email}}", + "customer_password_reset_applied": "Klantwachtwoord door admin gereset: {{email}}", + "customer_password_change": "Klant heeft wachtwoord gewijzigd: {{email}}", + "customer_self_profile_update": "Klant heeft profiel bijgewerkt: {{email}}", + "customer_event_access": "{{email}} heeft {{eventName}} geopend", + "customer_updated": "Klantaccount bijgewerkt: {{email}}", + "customer_deactivated": "Klantaccount gedeactiveerd: {{email}}", + "customer_reactivated": "Klantaccount geheractiveerd: {{email}}", + "customer_erased": "Klantaccount gewist (AVG): {{email}}", + "admin_invitation_created": "Beheerdersuitnodiging verzonden naar {{email}}", + "admin_invitation_accepted": "Beheerder heeft uitnodiging geaccepteerd: {{email}}", + "admin_invitation_cancelled": "Beheerdersuitnodiging geannuleerd: {{email}}", + "admin_user_updated": "Beheerdersaccount bijgewerkt: {{username}}", + "admin_user_deactivated": "Beheerdersaccount gedeactiveerd: {{username}}", + "admin_password_reset": "Beheerderswachtwoord door andere beheerder gereset: {{username}}", + "admin_profile_updated": "Beheerder {{actorName}} heeft profiel bijgewerkt", + "webhook_created": "Webhook aangemaakt: {{name}}", + "webhook_updated": "Webhook bijgewerkt: {{name}}", + "webhook_deleted": "Webhook verwijderd: {{name}}", + "api_token_created": "API-token aangemaakt: {{name}}", + "api_token_revoked": "API-token ingetrokken: {{name}}", + "event_type_created": "Evenementtype aangemaakt: {{name}}", + "event_type_updated": "Evenementtype bijgewerkt: {{name}}", + "event_type_deleted": "Evenementtype verwijderd: {{name}}", + "event_types_reordered": "Evenementtypes gesorteerd", + "event_published": "Evenement gepubliceerd: {{eventName}}", + "event_logo_uploaded": "Evenementlogo geüpload voor {{eventName}}", + "event_logo_removed": "Evenementlogo verwijderd voor {{eventName}}", + "bulk_delete_completed": "Massa-verwijdering voltooid: {{count}} evenementen verwijderd", + "photo_replaced": "Foto vervangen in {{eventName}}", + "photo_uploaded": "Foto geüpload naar {{eventName}}", + "category_hero_updated": "Categorie hero-foto bijgewerkt", + "public_site_reset_to_default": "Publieke site teruggezet naar standaard", + "cms_page_logo_uploaded": "CMS-pagina-logo geüpload: {{slug}}" } }, "acceptInvitation": { diff --git a/frontend/src/i18n/locales/pt.json b/frontend/src/i18n/locales/pt.json index 22265029..1a1c6397 100644 --- a/frontend/src/i18n/locales/pt.json +++ b/frontend/src/i18n/locales/pt.json @@ -1558,7 +1558,44 @@ "photosBulkDeleted_other": "{{count}} fotos eliminadas de \"{{eventName}}\"", "bulkDownload_many": "{{count}} fotos descarregadas de \"{{eventName}}\"", "bulkDownload_one": "{{count}} foto descarregada de \"{{eventName}}\"", - "bulkDownload_other": "{{count}} fotos descarregadas de \"{{eventName}}\"" + "bulkDownload_other": "{{count}} fotos descarregadas de \"{{eventName}}\"", + "customerLogin": "Cliente entrou: {{email}}", + "customerInvitationCreated": "Convite de cliente enviado para {{email}}", + "customerInvitationAccepted": "Cliente aceitou o convite: {{email}}", + "customerInvitationCancelled": "Convite de cliente cancelado: {{email}}", + "customerPasswordResetRequested": "Redefinição de senha solicitada para o cliente {{email}}", + "customerPasswordResetApplied": "Senha do cliente redefinida pelo admin: {{email}}", + "customerPasswordChange": "Cliente alterou a senha: {{email}}", + "customerSelfProfileUpdate": "Cliente atualizou o perfil: {{email}}", + "customerEventAccess": "{{email}} abriu {{eventName}}", + "customerUpdated": "Conta de cliente atualizada: {{email}}", + "customerDeactivated": "Conta de cliente desativada: {{email}}", + "customerReactivated": "Conta de cliente reativada: {{email}}", + "customerErased": "Conta de cliente removida (LGPD): {{email}}", + "adminInvitationCreated": "Convite de admin enviado para {{email}}", + "adminInvitationAccepted": "Admin aceitou o convite: {{email}}", + "adminInvitationCancelled": "Convite de admin cancelado: {{email}}", + "adminUserUpdated": "Usuário admin atualizado: {{username}}", + "adminUserDeactivated": "Usuário admin desativado: {{username}}", + "webhookCreated": "Webhook criado: {{name}}", + "webhookUpdated": "Webhook atualizado: {{name}}", + "webhookDeleted": "Webhook excluído: {{name}}", + "apiTokenCreated": "Token de API criado: {{name}}", + "apiTokenRevoked": "Token de API revogado: {{name}}", + "eventTypeCreated": "Tipo de evento criado: {{name}}", + "eventTypeUpdated": "Tipo de evento atualizado: {{name}}", + "eventTypeDeleted": "Tipo de evento excluído: {{name}}", + "eventTypesReordered": "Tipos de evento reordenados", + "eventPublished": "Evento publicado: {{eventName}}", + "eventLogoUploaded": "Logotipo do evento carregado para {{eventName}}", + "eventLogoRemoved": "Logotipo do evento removido de {{eventName}}", + "bulkDeleteCompleted": "Exclusão em massa concluída: {{count}} eventos removidos", + "photoReplaced": "Foto substituída em {{eventName}}", + "photoUploaded": "Foto carregada em {{eventName}}", + "categoryHeroUpdated": "Foto principal da categoria atualizada", + "publicSiteResetToDefault": "Site público redefinido ao padrão", + "cmsPageLogoUploaded": "Logotipo da página CMS carregado: {{slug}}", + "adminPasswordResetByAdmin": "Senha de admin redefinida por outro admin: {{username}}" }, "notificationToasts": { "markedAllRead": "Todas as notificações marcadas como lidas", @@ -1718,7 +1755,49 @@ "guest_feedback_rating": "Convidado avaliou uma foto em {{eventName}}", "guest_feedback_comment": "Convidado comentou uma foto em {{eventName}}", "system_activity": "Atividade do sistema: {{type}}", - "unknown": "Atividade desconhecida" + "unknown": "Atividade desconhecida", + "feature_enabled": "{{label}} ativada", + "feature_disabled": "{{label}} desativada", + "feature_flags_summary": "{{count}} funcionalidades atualizadas: {{summary}}", + "feature_flags_noop": "Sinalizadores revisados (sem alterações)", + "customer_login": "Cliente entrou: {{email}}", + "customer_invitation_created": "Convite de cliente enviado para {{email}}", + "customer_invitation_accepted": "Cliente aceitou o convite: {{email}}", + "customer_invitation_cancelled": "Convite de cliente cancelado: {{email}}", + "customer_password_reset_requested": "Redefinição de senha solicitada para o cliente {{email}}", + "customer_password_reset_applied": "Senha do cliente redefinida pelo admin: {{email}}", + "customer_password_change": "Cliente alterou a senha: {{email}}", + "customer_self_profile_update": "Cliente atualizou o perfil: {{email}}", + "customer_event_access": "{{email}} abriu {{eventName}}", + "customer_updated": "Conta de cliente atualizada: {{email}}", + "customer_deactivated": "Conta de cliente desativada: {{email}}", + "customer_reactivated": "Conta de cliente reativada: {{email}}", + "customer_erased": "Conta de cliente removida (LGPD): {{email}}", + "admin_invitation_created": "Convite de admin enviado para {{email}}", + "admin_invitation_accepted": "Admin aceitou o convite: {{email}}", + "admin_invitation_cancelled": "Convite de admin cancelado: {{email}}", + "admin_user_updated": "Usuário admin atualizado: {{username}}", + "admin_user_deactivated": "Usuário admin desativado: {{username}}", + "admin_password_reset": "Senha de admin redefinida por outro admin: {{username}}", + "admin_profile_updated": "Admin {{actorName}} atualizou o perfil", + "webhook_created": "Webhook criado: {{name}}", + "webhook_updated": "Webhook atualizado: {{name}}", + "webhook_deleted": "Webhook excluído: {{name}}", + "api_token_created": "Token de API criado: {{name}}", + "api_token_revoked": "Token de API revogado: {{name}}", + "event_type_created": "Tipo de evento criado: {{name}}", + "event_type_updated": "Tipo de evento atualizado: {{name}}", + "event_type_deleted": "Tipo de evento excluído: {{name}}", + "event_types_reordered": "Tipos de evento reordenados", + "event_published": "Evento publicado: {{eventName}}", + "event_logo_uploaded": "Logotipo do evento carregado para {{eventName}}", + "event_logo_removed": "Logotipo do evento removido de {{eventName}}", + "bulk_delete_completed": "Exclusão em massa concluída: {{count}} eventos removidos", + "photo_replaced": "Foto substituída em {{eventName}}", + "photo_uploaded": "Foto carregada em {{eventName}}", + "category_hero_updated": "Foto principal da categoria atualizada", + "public_site_reset_to_default": "Site público redefinido ao padrão", + "cms_page_logo_uploaded": "Logotipo da página CMS carregado: {{slug}}" } }, "acceptInvitation": { diff --git a/frontend/src/i18n/locales/ru.json b/frontend/src/i18n/locales/ru.json index 2da2bb5d..c75e0ea4 100644 --- a/frontend/src/i18n/locales/ru.json +++ b/frontend/src/i18n/locales/ru.json @@ -1578,7 +1578,44 @@ "bulkDownload_few": "{{count}} фото скачано из «{{eventName}}»", "bulkDownload_many": "{{count}} фото скачано из «{{eventName}}»", "bulkDownload_one": "{{count}} фото скачано из «{{eventName}}»", - "bulkDownload_other": "{{count}} фото скачано из «{{eventName}}»" + "bulkDownload_other": "{{count}} фото скачано из «{{eventName}}»", + "customerLogin": "Клиент вошёл в систему: {{email}}", + "customerInvitationCreated": "Приглашение клиенту отправлено: {{email}}", + "customerInvitationAccepted": "Клиент принял приглашение: {{email}}", + "customerInvitationCancelled": "Приглашение клиента отозвано: {{email}}", + "customerPasswordResetRequested": "Запрошен сброс пароля клиента: {{email}}", + "customerPasswordResetApplied": "Пароль клиента сброшен администратором: {{email}}", + "customerPasswordChange": "Клиент сменил пароль: {{email}}", + "customerSelfProfileUpdate": "Клиент обновил профиль: {{email}}", + "customerEventAccess": "{{email}} открыл(а) {{eventName}}", + "customerUpdated": "Учётная запись клиента обновлена: {{email}}", + "customerDeactivated": "Учётная запись клиента деактивирована: {{email}}", + "customerReactivated": "Учётная запись клиента активирована: {{email}}", + "customerErased": "Учётная запись клиента удалена (GDPR): {{email}}", + "adminInvitationCreated": "Приглашение администратору отправлено: {{email}}", + "adminInvitationAccepted": "Администратор принял приглашение: {{email}}", + "adminInvitationCancelled": "Приглашение администратора отозвано: {{email}}", + "adminUserUpdated": "Администратор обновлён: {{username}}", + "adminUserDeactivated": "Администратор деактивирован: {{username}}", + "webhookCreated": "Вебхук создан: {{name}}", + "webhookUpdated": "Вебхук обновлён: {{name}}", + "webhookDeleted": "Вебхук удалён: {{name}}", + "apiTokenCreated": "API-токен создан: {{name}}", + "apiTokenRevoked": "API-токен отозван: {{name}}", + "eventTypeCreated": "Тип события создан: {{name}}", + "eventTypeUpdated": "Тип события обновлён: {{name}}", + "eventTypeDeleted": "Тип события удалён: {{name}}", + "eventTypesReordered": "Типы событий пересортированы", + "eventPublished": "Событие опубликовано: {{eventName}}", + "eventLogoUploaded": "Логотип события загружен для {{eventName}}", + "eventLogoRemoved": "Логотип события удалён в {{eventName}}", + "bulkDeleteCompleted": "Массовое удаление завершено: удалено событий — {{count}}", + "photoReplaced": "Фото заменено в {{eventName}}", + "photoUploaded": "Фото загружено в {{eventName}}", + "categoryHeroUpdated": "Главное фото категории обновлено", + "publicSiteResetToDefault": "Публичный сайт сброшен к значениям по умолчанию", + "cmsPageLogoUploaded": "Логотип CMS-страницы загружен: {{slug}}", + "adminPasswordResetByAdmin": "Пароль администратора сброшен другим администратором: {{username}}" }, "notificationToasts": { "markedAllRead": "Все уведомления отмечены как прочитанные", @@ -1743,7 +1780,49 @@ "guest_feedback_rating": "Гость оценил фото в {{eventName}}", "guest_feedback_comment": "Гость прокомментировал фото в {{eventName}}", "system_activity": "Системная активность: {{type}}", - "unknown": "Неизвестная активность" + "unknown": "Неизвестная активность", + "feature_enabled": "{{label}} включено", + "feature_disabled": "{{label}} отключено", + "feature_flags_summary": "Обновлено функций: {{count}} ({{summary}})", + "feature_flags_noop": "Флаги функций просмотрены (без изменений)", + "customer_login": "Клиент вошёл в систему: {{email}}", + "customer_invitation_created": "Приглашение клиенту отправлено: {{email}}", + "customer_invitation_accepted": "Клиент принял приглашение: {{email}}", + "customer_invitation_cancelled": "Приглашение клиента отозвано: {{email}}", + "customer_password_reset_requested": "Запрошен сброс пароля клиента: {{email}}", + "customer_password_reset_applied": "Пароль клиента сброшен администратором: {{email}}", + "customer_password_change": "Клиент сменил пароль: {{email}}", + "customer_self_profile_update": "Клиент обновил профиль: {{email}}", + "customer_event_access": "{{email}} открыл(а) {{eventName}}", + "customer_updated": "Учётная запись клиента обновлена: {{email}}", + "customer_deactivated": "Учётная запись клиента деактивирована: {{email}}", + "customer_reactivated": "Учётная запись клиента активирована: {{email}}", + "customer_erased": "Учётная запись клиента удалена (GDPR): {{email}}", + "admin_invitation_created": "Приглашение администратору отправлено: {{email}}", + "admin_invitation_accepted": "Администратор принял приглашение: {{email}}", + "admin_invitation_cancelled": "Приглашение администратора отозвано: {{email}}", + "admin_user_updated": "Администратор обновлён: {{username}}", + "admin_user_deactivated": "Администратор деактивирован: {{username}}", + "admin_password_reset": "Пароль администратора сброшен другим администратором: {{username}}", + "admin_profile_updated": "Администратор {{actorName}} обновил профиль", + "webhook_created": "Вебхук создан: {{name}}", + "webhook_updated": "Вебхук обновлён: {{name}}", + "webhook_deleted": "Вебхук удалён: {{name}}", + "api_token_created": "API-токен создан: {{name}}", + "api_token_revoked": "API-токен отозван: {{name}}", + "event_type_created": "Тип события создан: {{name}}", + "event_type_updated": "Тип события обновлён: {{name}}", + "event_type_deleted": "Тип события удалён: {{name}}", + "event_types_reordered": "Типы событий пересортированы", + "event_published": "Событие опубликовано: {{eventName}}", + "event_logo_uploaded": "Логотип события загружен для {{eventName}}", + "event_logo_removed": "Логотип события удалён в {{eventName}}", + "bulk_delete_completed": "Массовое удаление завершено: удалено событий — {{count}}", + "photo_replaced": "Фото заменено в {{eventName}}", + "photo_uploaded": "Фото загружено в {{eventName}}", + "category_hero_updated": "Главное фото категории обновлено", + "public_site_reset_to_default": "Публичный сайт сброшен к значениям по умолчанию", + "cms_page_logo_uploaded": "Логотип CMS-страницы загружен: {{slug}}" } }, "acceptInvitation": { diff --git a/frontend/src/services/admin.service.ts b/frontend/src/services/admin.service.ts index e3d59235..1d98b1cc 100644 --- a/frontend/src/services/admin.service.ts +++ b/frontend/src/services/admin.service.ts @@ -1,4 +1,61 @@ import { api } from '../config/api'; +import i18n from '../i18n/config'; + +/** + * Per-flag display labels for activity-log rendering. Values are + * i18n keys; if a key is missing in the active locale we fall back + * to a humanised version of the flag name. Kept in sync with + * `FeatureKey` in services/featureFlags.service.ts. + */ +const FEATURE_FLAG_LABEL_KEY: Record = { + galleries: 'settings.features.galleries.title', + reminderEmails: 'settings.features.reminderEmails.title', + calendar: 'settings.features.calendar.title', + calendarBooking: 'settings.features.calendarBooking.title', + quotes: 'settings.features.quotes.title', + bills: 'settings.features.bills.title', + messaging: 'settings.features.messaging.title', + analytics: 'settings.features.analytics.title', + userManagement: 'settings.features.userManagement.title', + clients: 'settings.features.clients.title', + customerPortal: 'settings.features.customerPortal.title', +}; + +/** + * Renders the `feature_flags_updated` activity row using the + * `metadata.changed = { [flagKey]: { from, to } }` payload the + * backend writes. One change → "Calendar enabled". Multiple → + * "3 features updated: Calendar enabled, Quotes disabled, …". + * + * Used by both the Dashboard recent-activity widget (via + * formatActivityMessage below) and the header notification + * dropdown (via notifications.service.ts). + */ +export function formatFeatureFlagsChanged( + changed: Record | undefined, +): string { + const t = i18n.t; + const entries = Object.entries(changed || {}); + if (entries.length === 0) { + return t('admin.activities.feature_flags_noop', 'Feature flags reviewed (no changes)'); + } + const pieces = entries.map(([key, change]) => { + const labelKey = FEATURE_FLAG_LABEL_KEY[key]; + // Humanise unknown keys (camelCase → "Camel Case") so a forward- + // compat flag added in a newer release still renders something + // readable on a frontend that doesn't know about it yet. + const fallback = key.replace(/([A-Z])/g, ' $1').replace(/^./, (c) => c.toUpperCase()); + const label = labelKey ? (t(labelKey, fallback) as string) : fallback; + return change.to + ? (t('admin.activities.feature_enabled', '{{label}} enabled', { label }) as string) + : (t('admin.activities.feature_disabled', '{{label}} disabled', { label }) as string); + }); + if (pieces.length === 1) return pieces[0]; + return t('admin.activities.feature_flags_summary', '{{count}} features updated: {{summary}}', { + count: pieces.length, + summary: pieces.join(', '), + }) as string; +} export interface DashboardStats { activeEvents: number; @@ -88,6 +145,51 @@ export type ActivityType = | "admin_logout" | "system_activity" | "unknown" + // Feature flag toggles emit one row per save with the full + // { changed: { key: { from, to } } } diff in metadata. + | "feature_flags_updated" + // Customer portal (#354). + | "customer_login" + | "customer_invitation_created" + | "customer_invitation_accepted" + | "customer_invitation_cancelled" + | "customer_password_reset_requested" + | "customer_password_reset_applied" + | "customer_password_change" + | "customer_self_profile_update" + | "customer_event_access" + | "customer_updated" + | "customer_deactivated" + | "customer_reactivated" + | "customer_erased" + // Admin user management (#350). + | "admin_invitation_created" + | "admin_invitation_accepted" + | "admin_invitation_cancelled" + | "admin_user_updated" + | "admin_user_deactivated" + | "admin_password_reset" + | "admin_profile_updated" + // Webhooks (#327) + API tokens (#322) + event types. + | "webhook_created" + | "webhook_updated" + | "webhook_deleted" + | "api_token_created" + | "api_token_revoked" + | "event_type_created" + | "event_type_updated" + | "event_type_deleted" + | "event_types_reordered" + // Other recent surfaces missing from the message map. + | "event_published" + | "event_logo_uploaded" + | "event_logo_removed" + | "bulk_delete_completed" + | "photo_replaced" + | "photo_uploaded" + | "category_hero_updated" + | "public_site_reset_to_default" + | "cms_page_logo_uploaded" export interface Activity { id: number; @@ -162,20 +264,68 @@ export const adminService = { // Format activity message formatActivityMessage(activity: Activity): string { + // Feature-flag toggles carry a `changed` diff in metadata. Render + // it inline so the activity row says what actually flipped, not + // just "feature_flags_updated". + if (activity.type === 'feature_flags_updated') { + return formatFeatureFlagsChanged(activity.metadata?.changed); + } + const md = activity.metadata || {}; const messages: Record = { 'event_created': `New event created: ${activity.eventName || 'Unknown'}`, - 'photos_uploaded': `${activity.metadata.count || 0} photos uploaded to ${activity.eventName || 'Unknown'}`, + 'photos_uploaded': `${md.count || 0} photos uploaded to ${activity.eventName || 'Unknown'}`, 'event_archived': `Event archived: ${activity.eventName || 'Unknown'}`, + 'event_published': `Event published: ${activity.eventName || md.event_name || 'Unknown'}`, + 'event_logo_uploaded': `Event logo uploaded for ${activity.eventName || 'Unknown'}`, + 'event_logo_removed': `Event logo removed for ${activity.eventName || 'Unknown'}`, 'archive_restored': `Archive restored: ${activity.eventName || 'Unknown'}`, - 'archive_deleted': `Archive deleted: ${activity.metadata.event_name || 'Unknown'}`, + 'archive_deleted': `Archive deleted: ${md.event_name || 'Unknown'}`, 'archive_downloaded': `Archive downloaded: ${activity.eventName || 'Unknown'}`, 'email_config_updated': 'Email configuration updated', - 'email_template_updated': `Email template updated: ${activity.metadata.template_key || ''}`, + 'email_template_updated': `Email template updated: ${md.template_key || ''}`, 'branding_updated': 'Branding settings updated', 'theme_updated': 'Theme settings updated', - 'bulk_download': `${activity.metadata.photo_count || 0} photos downloaded from ${activity.eventName || 'Unknown'}`, + 'bulk_download': `${md.photo_count || 0} photos downloaded from ${activity.eventName || 'Unknown'}`, + 'bulk_delete_completed': `${md.deleted || md.count || 0} events deleted`, 'gallery_password_entry': `Password entered for ${activity.eventName || 'Unknown'}`, - 'expiration_warning_viewed': `Expiration warning viewed for ${activity.eventName || 'Unknown'}` + 'expiration_warning_viewed': `Expiration warning viewed for ${activity.eventName || 'Unknown'}`, + 'photo_replaced': `Photo replaced in ${activity.eventName || 'Unknown'}`, + 'photo_uploaded': `Photo uploaded to ${activity.eventName || 'Unknown'}`, + 'category_hero_updated': `Category hero photo updated`, + 'public_site_reset_to_default': 'Public site reset to default', + 'cms_page_logo_uploaded': `CMS page logo uploaded: ${md.slug || ''}`, + // Customer portal (#354). + 'customer_login': `Customer logged in: ${md.email || activity.actorName || ''}`, + 'customer_invitation_created': `Customer invitation sent to ${md.email || ''}`, + 'customer_invitation_accepted': `Customer accepted invitation: ${md.email || ''}`, + 'customer_invitation_cancelled': `Customer invitation cancelled: ${md.email || ''}`, + 'customer_password_reset_requested': `Password reset requested for customer ${md.email || ''}`, + 'customer_password_reset_applied': `Customer password reset by admin: ${md.email || ''}`, + 'customer_password_change': `Customer changed their password`, + 'customer_self_profile_update': `Customer updated their profile`, + 'customer_event_access': `Customer opened ${activity.eventName || md.event_slug || 'a gallery'}`, + 'customer_updated': `Customer account updated: ${md.email || ''}`, + 'customer_deactivated': `Customer account deactivated: ${md.email || ''}`, + 'customer_reactivated': `Customer account reactivated: ${md.email || ''}`, + 'customer_erased': `Customer account erased (GDPR): ${md.email || ''}`, + // Admin user management (#350). + 'admin_invitation_created': `Admin invitation sent to ${md.email || ''}`, + 'admin_invitation_accepted': `Admin accepted invitation: ${md.email || md.username || ''}`, + 'admin_invitation_cancelled': `Admin invitation cancelled: ${md.email || ''}`, + 'admin_user_updated': `Admin user updated: ${md.username || md.email || ''}`, + 'admin_user_deactivated': `Admin user deactivated: ${md.username || md.email || ''}`, + 'admin_password_reset': `Admin password reset by another admin: ${md.username || md.email || ''}`, + 'admin_profile_updated': `Admin ${activity.actorName || ''} updated their profile`, + // Webhooks (#327) + API tokens (#322) + event types. + 'webhook_created': `Webhook created: ${md.name || ''}`, + 'webhook_updated': `Webhook updated: ${md.name || ''}`, + 'webhook_deleted': `Webhook deleted: ${md.name || ''}`, + 'api_token_created': `API token created: ${md.name || ''}`, + 'api_token_revoked': `API token revoked: ${md.name || ''}`, + 'event_type_created': `Event type created: ${md.name || ''}`, + 'event_type_updated': `Event type updated: ${md.name || ''}`, + 'event_type_deleted': `Event type deleted: ${md.name || ''}`, + 'event_types_reordered': 'Event types reordered', }; return messages[activity.type] || activity.type; diff --git a/frontend/src/services/notifications.service.ts b/frontend/src/services/notifications.service.ts index 0f352e0f..1c53cc7e 100644 --- a/frontend/src/services/notifications.service.ts +++ b/frontend/src/services/notifications.service.ts @@ -1,5 +1,6 @@ import { api } from '../config/api'; import i18n from '../i18n/config'; +import { formatFeatureFlagsChanged } from './admin.service'; export interface Notification { id: number; @@ -148,14 +149,150 @@ export const notificationsService = { eventName: notification.metadata.event_name }); case 'archive_restored': - return t('admin.notificationMessages.archiveRestored', { - eventName: notification.metadata.event_name + return t('admin.notificationMessages.archiveRestored', { + eventName: notification.metadata.event_name }); + + // ---- Feature flags -------------------------------------------------- + // Smart-formatted from metadata.changed so the row says what + // actually flipped (e.g. "Customer Portal enabled") instead of + // the raw activity type. Helper is shared with the Dashboard + // recent-activity widget. + case 'feature_flags_updated': + return formatFeatureFlagsChanged(notification.metadata?.changed); + + // ---- Customer portal (#354) ----------------------------------------- + case 'customer_login': + return t('admin.notificationMessages.customerLogin', { + email: notification.metadata.email || notification.actorName, + }); + case 'customer_invitation_created': + return t('admin.notificationMessages.customerInvitationCreated', { + email: notification.metadata.email, + }); + case 'customer_invitation_accepted': + return t('admin.notificationMessages.customerInvitationAccepted', { + email: notification.metadata.email, + }); + case 'customer_invitation_cancelled': + return t('admin.notificationMessages.customerInvitationCancelled', { + email: notification.metadata.email, + }); + case 'customer_password_reset_requested': + return t('admin.notificationMessages.customerPasswordResetRequested', { + email: notification.metadata.email, + }); + case 'customer_password_reset_applied': + return t('admin.notificationMessages.customerPasswordResetApplied', { + email: notification.metadata.email, + }); + case 'customer_password_change': + return t('admin.notificationMessages.customerPasswordChange', { + email: notification.metadata.email || notification.actorName, + }); + case 'customer_self_profile_update': + return t('admin.notificationMessages.customerSelfProfileUpdate', { + email: notification.metadata.email || notification.actorName, + }); + case 'customer_event_access': + return t('admin.notificationMessages.customerEventAccess', { + email: notification.metadata.email || notification.actorName, + eventName: notification.eventName || notification.metadata.event_slug || '', + }); + case 'customer_updated': + return t('admin.notificationMessages.customerUpdated', { + email: notification.metadata.email, + }); + case 'customer_deactivated': + return t('admin.notificationMessages.customerDeactivated', { + email: notification.metadata.email, + }); + case 'customer_reactivated': + return t('admin.notificationMessages.customerReactivated', { + email: notification.metadata.email, + }); + case 'customer_erased': + return t('admin.notificationMessages.customerErased', { + email: notification.metadata.email, + }); + + // ---- Admin user management (#350) ----------------------------------- + case 'admin_invitation_created': + return t('admin.notificationMessages.adminInvitationCreated', { + email: notification.metadata.email, + }); + case 'admin_invitation_accepted': + return t('admin.notificationMessages.adminInvitationAccepted', { + email: notification.metadata.email || notification.metadata.username, + }); + case 'admin_invitation_cancelled': + return t('admin.notificationMessages.adminInvitationCancelled', { + email: notification.metadata.email, + }); + case 'admin_user_updated': + return t('admin.notificationMessages.adminUserUpdated', { + username: notification.metadata.username || notification.metadata.email, + }); + case 'admin_user_deactivated': + return t('admin.notificationMessages.adminUserDeactivated', { + username: notification.metadata.username || notification.metadata.email, + }); + case 'admin_password_reset': + return t('admin.notificationMessages.adminPasswordResetByAdmin', { + username: notification.metadata.username || notification.metadata.email, + }); + + // ---- Webhooks (#327) + API tokens (#322) + event types -------------- + case 'webhook_created': + return t('admin.notificationMessages.webhookCreated', { name: notification.metadata.name }); + case 'webhook_updated': + return t('admin.notificationMessages.webhookUpdated', { name: notification.metadata.name }); + case 'webhook_deleted': + return t('admin.notificationMessages.webhookDeleted', { name: notification.metadata.name }); + case 'api_token_created': + return t('admin.notificationMessages.apiTokenCreated', { name: notification.metadata.name }); + case 'api_token_revoked': + return t('admin.notificationMessages.apiTokenRevoked', { name: notification.metadata.name }); + case 'event_type_created': + return t('admin.notificationMessages.eventTypeCreated', { name: notification.metadata.name }); + case 'event_type_updated': + return t('admin.notificationMessages.eventTypeUpdated', { name: notification.metadata.name }); + case 'event_type_deleted': + return t('admin.notificationMessages.eventTypeDeleted', { name: notification.metadata.name }); + case 'event_types_reordered': + return t('admin.notificationMessages.eventTypesReordered'); + + // ---- Other recent surfaces ------------------------------------------ + case 'event_published': + return t('admin.notificationMessages.eventPublished', { + eventName: notification.eventName || notification.metadata.event_name, + }); + case 'event_logo_uploaded': + return t('admin.notificationMessages.eventLogoUploaded', { eventName: notification.eventName }); + case 'event_logo_removed': + return t('admin.notificationMessages.eventLogoRemoved', { eventName: notification.eventName }); + case 'bulk_delete_completed': + return t('admin.notificationMessages.bulkDeleteCompleted', { + count: notification.metadata.deleted || notification.metadata.count || 0, + }); + case 'photo_replaced': + return t('admin.notificationMessages.photoReplaced', { eventName: notification.eventName }); + case 'photo_uploaded': + return t('admin.notificationMessages.photoUploaded', { eventName: notification.eventName }); + case 'category_hero_updated': + return t('admin.notificationMessages.categoryHeroUpdated'); + case 'public_site_reset_to_default': + return t('admin.notificationMessages.publicSiteResetToDefault'); + case 'cms_page_logo_uploaded': + return t('admin.notificationMessages.cmsPageLogoUploaded', { + slug: notification.metadata.slug, + }); + default: // Log unknown notification types for debugging console.warn('Unknown notification type:', notification.type, notification); - return notification.metadata.message || t('admin.notificationMessages.systemActivity', { - type: notification.type.replace(/_/g, ' ') + return notification.metadata.message || t('admin.notificationMessages.systemActivity', { + type: notification.type.replace(/_/g, ' ') }); } }, @@ -211,6 +348,68 @@ export const notificationsService = { case 'archive_deleted': case 'archive_restored': return { icon: 'Archive', color: 'text-blue-600' }; + + // Feature flag toggles + customer / admin / webhook surfaces. Icon + // choices favour the tone of the action (settings / users / network) + // over per-event-type cleverness. + case 'feature_flags_updated': + return { icon: 'ToggleRight', color: 'text-amber-600' }; + case 'customer_login': + case 'customer_event_access': + case 'customer_self_profile_update': + case 'customer_password_change': + return { icon: 'User', color: 'text-blue-600' }; + case 'customer_invitation_created': + case 'customer_invitation_accepted': + case 'customer_invitation_cancelled': + return { icon: 'Mail', color: 'text-teal-600' }; + case 'customer_password_reset_requested': + case 'customer_password_reset_applied': + return { icon: 'Lock', color: 'text-indigo-600' }; + case 'customer_updated': + case 'customer_deactivated': + case 'customer_reactivated': + return { icon: 'UserCog', color: 'text-gray-600' }; + case 'customer_erased': + return { icon: 'Trash2', color: 'text-red-600' }; + case 'admin_invitation_created': + case 'admin_invitation_accepted': + case 'admin_invitation_cancelled': + return { icon: 'Mail', color: 'text-teal-600' }; + case 'admin_user_updated': + case 'admin_user_deactivated': + return { icon: 'UserCog', color: 'text-gray-600' }; + case 'admin_password_reset': + return { icon: 'Lock', color: 'text-indigo-600' }; + case 'webhook_created': + case 'webhook_updated': + case 'webhook_deleted': + return { icon: 'Webhook', color: 'text-purple-600' }; + case 'api_token_created': + case 'api_token_revoked': + return { icon: 'Key', color: 'text-orange-600' }; + case 'event_type_created': + case 'event_type_updated': + case 'event_type_deleted': + case 'event_types_reordered': + return { icon: 'Tag', color: 'text-violet-600' }; + case 'event_published': + return { icon: 'CheckCircle', color: 'text-green-600' }; + case 'event_logo_uploaded': + case 'event_logo_removed': + return { icon: 'Image', color: 'text-pink-600' }; + case 'bulk_delete_completed': + return { icon: 'Trash2', color: 'text-red-600' }; + case 'photo_replaced': + case 'photo_uploaded': + return { icon: 'Image', color: 'text-purple-600' }; + case 'category_hero_updated': + return { icon: 'Folder', color: 'text-indigo-600' }; + case 'public_site_reset_to_default': + return { icon: 'Globe', color: 'text-gray-600' }; + case 'cms_page_logo_uploaded': + return { icon: 'FileText', color: 'text-green-600' }; + default: return { icon: 'Bell', color: 'text-gray-600' }; }