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.
This commit is contained in:
+6
-1
@@ -10,8 +10,11 @@ declare(strict_types=1);
|
|||||||
* - `permission` : section ou item masqué si la permission effective absente (RBAC fin —
|
* - `permission` : section ou item masqué si la permission effective absente (RBAC fin —
|
||||||
* `User::getEffectivePermissions()` ; ROLE_ADMIN bypasse via le voter, mais la
|
* `User::getEffectivePermissions()` ; ROLE_ADMIN bypasse via le voter, mais la
|
||||||
* sidebar évalue les permissions effectives réelles — combiner avec `roles` au besoin).
|
* 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.
|
* (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.<domaine>.<item>).
|
* Les labels sont des clés i18n (sidebar.<domaine>.<item>).
|
||||||
*/
|
*/
|
||||||
return [
|
return [
|
||||||
@@ -23,6 +26,8 @@ return [
|
|||||||
['label' => 'sidebar.general.myTasks', 'to' => '/my-tasks', 'icon' => 'mdi:clipboard-check-outline', 'module' => 'project-management'],
|
['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.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'],
|
['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'],
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
|
|||||||
@@ -139,11 +139,16 @@ const route = useRoute()
|
|||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
const { sections } = useSidebar()
|
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(() =>
|
const translatedSections = computed(() =>
|
||||||
sections.value.map((section) => ({
|
sections.value.map((section) => ({
|
||||||
label: t(section.label),
|
label: t(section.label),
|
||||||
icon: section.icon,
|
icon: section.icon,
|
||||||
items: section.items.map((item) => ({
|
items: section.items
|
||||||
|
.filter((item) => item.to !== '/mail')
|
||||||
|
.map((item) => ({
|
||||||
label: t(item.label),
|
label: t(item.label),
|
||||||
to: item.to,
|
to: item.to,
|
||||||
icon: item.icon,
|
icon: item.icon,
|
||||||
|
|||||||
@@ -140,7 +140,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useMailService } from '~/services/mail'
|
import { useMailService } from '~/modules/mail/services/mail'
|
||||||
|
|
||||||
const { getConfiguration, updateConfiguration, testConfiguration } = useMailService()
|
const { getConfiguration, updateConfiguration, testConfiguration } = useMailService()
|
||||||
|
|
||||||
|
|||||||
@@ -353,7 +353,8 @@
|
|||||||
"dashboard": "Tableau de bord",
|
"dashboard": "Tableau de bord",
|
||||||
"myTasks": "Mes tâches",
|
"myTasks": "Mes tâches",
|
||||||
"projects": "Projets",
|
"projects": "Projets",
|
||||||
"timeTracking": "Suivi de temps"
|
"timeTracking": "Suivi de temps",
|
||||||
|
"mail": "Messagerie"
|
||||||
},
|
},
|
||||||
"admin": {
|
"admin": {
|
||||||
"section": "Administration",
|
"section": "Administration",
|
||||||
|
|||||||
+2
-2
@@ -1,10 +1,10 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { MailMessageDetailDto } from '~/services/dto/mail'
|
import type { MailMessageDetailDto } from '~/modules/mail/services/dto/mail'
|
||||||
import type { Task } from '~/modules/project-management/services/dto/task'
|
import type { Task } from '~/modules/project-management/services/dto/task'
|
||||||
import type { Project } from '~/modules/project-management/services/dto/project'
|
import type { Project } from '~/modules/project-management/services/dto/project'
|
||||||
import type { TaskGroup } from '~/modules/project-management/services/dto/task-group'
|
import type { TaskGroup } from '~/modules/project-management/services/dto/task-group'
|
||||||
import type { UserData } from '~/services/dto/user-data'
|
import type { UserData } from '~/services/dto/user-data'
|
||||||
import { useMailService } from '~/services/mail'
|
import { useMailService } from '~/modules/mail/services/mail'
|
||||||
import { useProjectService } from '~/modules/project-management/services/projects'
|
import { useProjectService } from '~/modules/project-management/services/projects'
|
||||||
import { useTaskGroupService } from '~/modules/project-management/services/task-groups'
|
import { useTaskGroupService } from '~/modules/project-management/services/task-groups'
|
||||||
import { useUserService } from '~/services/users'
|
import { useUserService } from '~/services/users'
|
||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { MailFolderDto } from '~/services/dto/mail'
|
import type { MailFolderDto } from '~/modules/mail/services/dto/mail'
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
/** Arbre de dossiers (getter folderTree du store) */
|
/** Arbre de dossiers (getter folderTree du store) */
|
||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { Task } from '~/modules/project-management/services/dto/task'
|
import type { Task } from '~/modules/project-management/services/dto/task'
|
||||||
import type { Project } from '~/modules/project-management/services/dto/project'
|
import type { Project } from '~/modules/project-management/services/dto/project'
|
||||||
import { useMailService } from '~/services/mail'
|
import { useMailService } from '~/modules/mail/services/mail'
|
||||||
import { useTaskService } from '~/modules/project-management/services/tasks'
|
import { useTaskService } from '~/modules/project-management/services/tasks'
|
||||||
import { useProjectService } from '~/modules/project-management/services/projects'
|
import { useProjectService } from '~/modules/project-management/services/projects'
|
||||||
|
|
||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { MailMessageHeaderDto } from '~/services/dto/mail'
|
import type { MailMessageHeaderDto } from '~/modules/mail/services/dto/mail'
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
messages: readonly MailMessageHeaderDto[]
|
messages: readonly MailMessageHeaderDto[]
|
||||||
+2
-2
@@ -1,7 +1,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { MailMessageDetailDto, MailAddressDto, MailAttachmentDto } from '~/services/dto/mail'
|
import type { MailMessageDetailDto, MailAddressDto, MailAttachmentDto } from '~/modules/mail/services/dto/mail'
|
||||||
import { sanitizeMailHtml } from '~/utils/sanitizeMailHtml'
|
import { sanitizeMailHtml } from '~/utils/sanitizeMailHtml'
|
||||||
import { useMailService } from '~/services/mail'
|
import { useMailService } from '~/modules/mail/services/mail'
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
/** Détail complet du message. null = aucun message sélectionné. */
|
/** Détail complet du message. null = aucun message sélectionné. */
|
||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useMailStore } from '~/stores/mail'
|
import { useMailStore } from '~/modules/mail/stores/mail'
|
||||||
|
|
||||||
const store = useMailStore()
|
const store = useMailStore()
|
||||||
const { syncing } = storeToRefs(store)
|
const { syncing } = storeToRefs(store)
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export default defineNuxtConfig({})
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { Task } from '~/modules/project-management/services/dto/task'
|
import type { Task } from '~/modules/project-management/services/dto/task'
|
||||||
import { useMailStore } from '~/stores/mail'
|
import { useMailStore } from '~/modules/mail/stores/mail'
|
||||||
|
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
@@ -11,7 +11,7 @@ import type {
|
|||||||
MailCreateTaskInput,
|
MailCreateTaskInput,
|
||||||
MailLinkTaskInput,
|
MailLinkTaskInput,
|
||||||
MailSyncResultDto,
|
MailSyncResultDto,
|
||||||
} from './dto/mail'
|
} from '~/modules/mail/services/dto/mail'
|
||||||
import type { Task } from '~/modules/project-management/services/dto/task'
|
import type { Task } from '~/modules/project-management/services/dto/task'
|
||||||
|
|
||||||
type BackendMailMessage = {
|
type BackendMailMessage = {
|
||||||
@@ -3,8 +3,8 @@ import type {
|
|||||||
MailFolderDto,
|
MailFolderDto,
|
||||||
MailMessageHeaderDto,
|
MailMessageHeaderDto,
|
||||||
MailMessageDetailDto,
|
MailMessageDetailDto,
|
||||||
} from '~/services/dto/mail'
|
} from '~/modules/mail/services/dto/mail'
|
||||||
import { useMailService } from '~/services/mail'
|
import { useMailService } from '~/modules/mail/services/mail'
|
||||||
|
|
||||||
const POLL_INTERVAL_MS = 30 * 1000 // 30 secondes
|
const POLL_INTERVAL_MS = 30 * 1000 // 30 secondes
|
||||||
|
|
||||||
@@ -551,8 +551,8 @@ import { useTaskService } from '~/modules/project-management/services/tasks'
|
|||||||
import { useTaskRecurrenceService } from '~/modules/project-management/services/task-recurrences'
|
import { useTaskRecurrenceService } from '~/modules/project-management/services/task-recurrences'
|
||||||
|
|
||||||
import type { Project } from '~/modules/project-management/services/dto/project'
|
import type { Project } from '~/modules/project-management/services/dto/project'
|
||||||
import { useMailService } from '~/services/mail'
|
import { useMailService } from '~/modules/mail/services/mail'
|
||||||
import type { MailMessageHeaderDto } from '~/services/dto/mail'
|
import type { MailMessageHeaderDto } from '~/modules/mail/services/dto/mail'
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
modelValue: boolean
|
modelValue: boolean
|
||||||
|
|||||||
Reference in New Issue
Block a user