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:
@@ -0,0 +1,162 @@
|
||||
<script setup lang="ts">
|
||||
import type { Task } from '~/modules/project-management/services/dto/task'
|
||||
import { useMailStore } from '~/modules/mail/stores/mail'
|
||||
|
||||
const { t } = useI18n()
|
||||
const router = useRouter()
|
||||
const route = useRoute()
|
||||
|
||||
useHead({ title: t('mail.title') })
|
||||
|
||||
// ─── Store ────────────────────────────────────────────────────────────────
|
||||
|
||||
const store = useMailStore()
|
||||
const {
|
||||
folderTree,
|
||||
selectedFolderPath,
|
||||
messages,
|
||||
messagesLoading,
|
||||
hasMoreMessages,
|
||||
selectedMessageId,
|
||||
selectedMessageDetail,
|
||||
detailLoading,
|
||||
} = storeToRefs(store)
|
||||
|
||||
// ─── Init : charge les dossiers + deep-link ───────────────────────────────
|
||||
|
||||
onMounted(async () => {
|
||||
if (folderTree.value.length === 0) {
|
||||
await store.fetchFolders()
|
||||
}
|
||||
|
||||
if (!selectedFolderPath.value && folderTree.value.length > 0) {
|
||||
const inbox = folderTree.value.find((f) => f.path.toUpperCase() === 'INBOX')
|
||||
const first = folderTree.value[0]
|
||||
const target = inbox?.path ?? first?.path
|
||||
if (target) {
|
||||
await store.selectFolder(target)
|
||||
}
|
||||
}
|
||||
|
||||
const messageIdParam = route.query.messageId
|
||||
if (messageIdParam) {
|
||||
const id = parseInt(String(messageIdParam), 10)
|
||||
if (!isNaN(id)) {
|
||||
await store.selectMessage(id)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// ─── Handlers ─────────────────────────────────────────────────────────────
|
||||
|
||||
async function handleFolderSelect(path: string): Promise<void> {
|
||||
await store.selectFolder(path)
|
||||
if (route.query.messageId) {
|
||||
const nextQuery = { ...route.query }
|
||||
delete nextQuery.messageId
|
||||
router.replace({ query: nextQuery })
|
||||
}
|
||||
}
|
||||
|
||||
async function handleMessageSelect(id: number): Promise<void> {
|
||||
await store.selectMessage(id)
|
||||
}
|
||||
|
||||
function handleLoadMore(): void {
|
||||
store.fetchMessages(true)
|
||||
}
|
||||
|
||||
// ─── Modals Phase 6 ────────────────────────────────────────────────────────
|
||||
|
||||
const showCreateTaskModal = ref(false)
|
||||
const showLinkTaskModal = ref(false)
|
||||
const activeMailIdForModal = ref<number | null>(null)
|
||||
|
||||
function handleCreateTask(mailId: number): void {
|
||||
activeMailIdForModal.value = mailId
|
||||
showCreateTaskModal.value = true
|
||||
}
|
||||
|
||||
function handleLinkTask(mailId: number): void {
|
||||
activeMailIdForModal.value = mailId
|
||||
showLinkTaskModal.value = true
|
||||
}
|
||||
|
||||
function handleTaskCreated(_task: Task): void {
|
||||
showCreateTaskModal.value = false
|
||||
// La tâche est créée et liée côté backend — toast géré par useMailService.createTaskFromMail
|
||||
}
|
||||
|
||||
function handleTaskLinked(_taskId: number): void {
|
||||
showLinkTaskModal.value = false
|
||||
// Toast géré par useMailService.linkTask
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex h-full flex-col overflow-hidden">
|
||||
<div class="flex flex-shrink-0 items-center justify-between border-b border-neutral-200 bg-white px-4 py-3">
|
||||
<h1 class="text-lg font-semibold text-neutral-900">
|
||||
{{ t('mail.title') }}
|
||||
</h1>
|
||||
<MailRefreshButton />
|
||||
</div>
|
||||
|
||||
<div class="flex flex-1 overflow-hidden">
|
||||
<aside class="w-[220px] flex-shrink-0 overflow-y-auto border-r border-neutral-200 bg-neutral-50 py-2">
|
||||
<p class="mb-1 px-3 text-xs font-semibold uppercase tracking-wide text-neutral-400">
|
||||
{{ t('mail.folders') }}
|
||||
</p>
|
||||
<MailFolderTree
|
||||
:folders="folderTree"
|
||||
:selected-path="selectedFolderPath"
|
||||
@select="handleFolderSelect"
|
||||
/>
|
||||
</aside>
|
||||
|
||||
<div class="flex w-[320px] flex-shrink-0 flex-col overflow-hidden border-r border-neutral-200 bg-white">
|
||||
<div class="flex flex-shrink-0 items-center justify-between border-b border-neutral-100 px-3 py-2">
|
||||
<p class="text-xs font-semibold uppercase tracking-wide text-neutral-400">
|
||||
{{ t('mail.messages') }}
|
||||
</p>
|
||||
</div>
|
||||
<div class="flex-1 overflow-hidden">
|
||||
<MailMessageList
|
||||
:messages="messages"
|
||||
:selected-id="selectedMessageId"
|
||||
:loading="messagesLoading"
|
||||
:has-more="hasMoreMessages"
|
||||
@select="handleMessageSelect"
|
||||
@load-more="handleLoadMore"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex-1 overflow-hidden bg-white">
|
||||
<MailMessageViewer
|
||||
:detail="selectedMessageDetail"
|
||||
:loading="detailLoading"
|
||||
@create-task="handleCreateTask"
|
||||
@link-task="handleLinkTask"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Modal créer tâche depuis mail -->
|
||||
<MailCreateTaskModal
|
||||
v-if="activeMailIdForModal !== null"
|
||||
v-model="showCreateTaskModal"
|
||||
:message-id="activeMailIdForModal"
|
||||
:message-detail="selectedMessageDetail"
|
||||
@created="handleTaskCreated"
|
||||
/>
|
||||
|
||||
<!-- Modal lier mail à tâche -->
|
||||
<MailLinkTaskModal
|
||||
v-if="activeMailIdForModal !== null"
|
||||
v-model="showLinkTaskModal"
|
||||
:message-id="activeMailIdForModal"
|
||||
@linked="handleTaskLinked"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
Reference in New Issue
Block a user