From bb7d7e79532d207aba911a1e281fddc38eb3bcdc Mon Sep 17 00:00:00 2001 From: Matthieu Date: Sat, 20 Jun 2026 19:52:13 +0200 Subject: [PATCH] feat(mail) : extract Mail front into Nuxt module layer LST-67 (2.5) front. Completes the Mail module. - New frontend/modules/mail/ layer (auto-detected): /mail page (3 columns), 7 components, mail service + DTO, mail store (folders/messages/unread polling). - sanitizeMailHtml util and useSystemFolderLabel composable stay global; AdminMailTab stays in /admin (service import repointed). - Consumers repointed: AdminMailTab and PM TaskModal -> ~/modules/mail/...; the store is auto-imported (Pinia storesDirs) so the layout badge/polling is unchanged. - /mail gated by the mail module: sidebar.php item with module=mail (so SidebarFilter disables /mail when the module is off); the layout filters /mail from the API sections to avoid a visual duplicate. ROLE_CLIENT exclusion kept. - i18n key sidebar.general.mail added. nuxt build passes; /mail and all other routes preserved. --- config/sidebar.php | 7 ++++++- frontend/app/layouts/default.vue | 15 ++++++++++----- frontend/components/admin/AdminMailTab.vue | 2 +- frontend/i18n/locales/fr.json | 3 ++- .../mail/components}/MailAttachmentPreview.vue | 0 .../mail/components}/MailCreateTaskModal.vue | 4 ++-- .../mail/components}/MailFolderTree.vue | 2 +- .../mail/components}/MailLinkTaskModal.vue | 2 +- .../mail/components}/MailMessageList.vue | 2 +- .../mail/components}/MailMessageViewer.vue | 4 ++-- .../mail/components}/MailRefreshButton.vue | 2 +- frontend/modules/mail/nuxt.config.ts | 1 + frontend/{ => modules/mail}/pages/mail.vue | 2 +- frontend/{ => modules/mail}/services/dto/mail.ts | 0 frontend/{ => modules/mail}/services/mail.ts | 2 +- frontend/{ => modules/mail}/stores/mail.ts | 4 ++-- .../project-management/components/TaskModal.vue | 4 ++-- 17 files changed, 34 insertions(+), 22 deletions(-) rename frontend/{components/mail => modules/mail/components}/MailAttachmentPreview.vue (100%) rename frontend/{components/mail => modules/mail/components}/MailCreateTaskModal.vue (97%) rename frontend/{components/mail => modules/mail/components}/MailFolderTree.vue (98%) rename frontend/{components/mail => modules/mail/components}/MailLinkTaskModal.vue (99%) rename frontend/{components/mail => modules/mail/components}/MailMessageList.vue (98%) rename frontend/{components/mail => modules/mail/components}/MailMessageViewer.vue (98%) rename frontend/{components/mail => modules/mail/components}/MailRefreshButton.vue (89%) create mode 100644 frontend/modules/mail/nuxt.config.ts rename frontend/{ => modules/mail}/pages/mail.vue (99%) rename frontend/{ => modules/mail}/services/dto/mail.ts (100%) rename frontend/{ => modules/mail}/services/mail.ts (99%) rename frontend/{ => modules/mail}/stores/mail.ts (99%) diff --git a/config/sidebar.php b/config/sidebar.php index 12d9a50..835e132 100644 --- a/config/sidebar.php +++ b/config/sidebar.php @@ -10,8 +10,11 @@ declare(strict_types=1); * - `permission` : section ou item masqué si la permission effective absente (RBAC fin — * `User::getEffectivePermissions()` ; ROLE_ADMIN bypasse via le voter, mais la * sidebar évalue les permissions effectives réelles — combiner avec `roles` au besoin). - * Les items contextuels (Kanban/Groupes/Archives), feature-flag (Documents, Mail) et user-flag + * Les items contextuels (Kanban/Groupes/Archives), feature-flag (Documents) et user-flag * (Mes absences) restent rendus côté layout, hors de cet endpoint. + * Mail est déclaré ici UNIQUEMENT pour le gating module (disabledRoutes si module inactif) ; + * son rendu visuel + badge non-lus reste géré côté layout, qui filtre `/mail` de translatedSections + * pour éviter le doublon. * Les labels sont des clés i18n (sidebar..). */ return [ @@ -23,6 +26,8 @@ return [ ['label' => 'sidebar.general.myTasks', 'to' => '/my-tasks', 'icon' => 'mdi:clipboard-check-outline', 'module' => 'project-management'], ['label' => 'sidebar.general.projects', 'to' => '/projects', 'icon' => 'mdi:folder-outline', 'module' => 'project-management'], ['label' => 'sidebar.general.timeTracking', 'to' => '/time-tracking', 'icon' => 'mdi:calendar-edit-outline', 'module' => 'time-tracking'], + // Gating module uniquement (cf. en-tête) : rendu visuel + badge gérés côté layout. + ['label' => 'sidebar.general.mail', 'to' => '/mail', 'icon' => 'mdi:email-outline', 'module' => 'mail'], ], ], [ diff --git a/frontend/app/layouts/default.vue b/frontend/app/layouts/default.vue index 7ae9da6..a7c1729 100644 --- a/frontend/app/layouts/default.vue +++ b/frontend/app/layouts/default.vue @@ -139,15 +139,20 @@ const route = useRoute() const { t } = useI18n() const { sections } = useSidebar() +// `/mail` est déclaré dans config/sidebar.php pour le gating module (disabledRoutes), +// mais son rendu visuel + badge non-lus est géré manuellement ci-dessous (feature-flag Mail). +// On le filtre des sections dynamiques pour éviter un doublon dans la nav. const translatedSections = computed(() => sections.value.map((section) => ({ label: t(section.label), icon: section.icon, - items: section.items.map((item) => ({ - label: t(item.label), - to: item.to, - icon: item.icon, - })), + items: section.items + .filter((item) => item.to !== '/mail') + .map((item) => ({ + label: t(item.label), + to: item.to, + icon: item.icon, + })), })), ) diff --git a/frontend/components/admin/AdminMailTab.vue b/frontend/components/admin/AdminMailTab.vue index 0d54a64..a6cecd5 100644 --- a/frontend/components/admin/AdminMailTab.vue +++ b/frontend/components/admin/AdminMailTab.vue @@ -140,7 +140,7 @@