From 79d341482427c041b621fe87e5fff8335cf4087c Mon Sep 17 00:00:00 2001 From: matthieu Date: Wed, 20 May 2026 07:46:21 +0200 Subject: [PATCH] =?UTF-8?q?fix(mail)=20:=20aligne=20le=20contrat=20front/b?= =?UTF-8?q?ack=20pour=20le=20listing=20et=20le=20d=C3=A9tail=20des=20messa?= =?UTF-8?q?ges?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Le service appelait GET /mail/messages?folder=X (404) au lieu de la vraie route /mail/folders/{path}/messages. Ajoute aussi une couche de mapping backend→DTO (messages→items, fromAddress→fromEmail, toAddresses→toRecipients, détail plat→imbriqué) pour réconcilier la dérive de contrat entre Phase 3 (API) et Phase 4 (DTOs front). Co-Authored-By: Claude Opus 4.7 (1M context) --- frontend/services/mail.ts | 68 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 65 insertions(+), 3 deletions(-) diff --git a/frontend/services/mail.ts b/frontend/services/mail.ts index 496a750..2f44422 100644 --- a/frontend/services/mail.ts +++ b/frontend/services/mail.ts @@ -14,6 +14,47 @@ import type { } from './dto/mail' import type { Task } from './dto/task' +type BackendMailMessage = { + id: number + messageId: string + uid: number + folderPath?: string + subject: string | null + fromAddress: string | null + fromName: string | null + toAddresses: string[] | null + ccAddresses: string[] | null + sentAt: string | null + isRead: boolean + isFlagged: boolean + hasAttachments: boolean + snippet?: string | null + linkedTaskIds?: number[] +} + +function toAddressList(values: string[] | null | undefined): { email: string; name: string | null }[] { + return (values ?? []).map((email) => ({ email, name: null })) +} + +function mapHeader(m: BackendMailMessage, fallbackFolderPath = ''): MailMessageHeaderDto { + return { + id: m.id, + messageId: m.messageId, + folderPath: m.folderPath ?? fallbackFolderPath, + subject: m.subject, + fromName: m.fromName, + fromEmail: m.fromAddress, + toRecipients: toAddressList(m.toAddresses), + ccRecipients: toAddressList(m.ccAddresses), + sentAt: m.sentAt, + receivedAt: m.sentAt ?? '', + isRead: m.isRead, + isFlagged: m.isFlagged, + hasAttachments: m.hasAttachments, + linkedTaskIds: m.linkedTaskIds ?? [], + } +} + export function useMailService() { const api = useApi() @@ -74,10 +115,19 @@ export function useMailService() { cursor?: string, limit?: number, ): Promise { - const query: Record = { folder: folderPath } + const query: Record = {} if (cursor) query.cursor = cursor if (limit) query.limit = limit - return api.get('/mail/messages', query) + const path = `/mail/folders/${encodeURIComponent(folderPath)}/messages` + const response = await api.get<{ messages: BackendMailMessage[]; nextCursor: string | null }>( + path, + query, + ) + return { + items: response.messages.map((m) => mapHeader(m, folderPath)), + nextCursor: response.nextCursor, + total: response.messages.length, + } } /** @@ -85,7 +135,19 @@ export function useMailService() { * @param id - ID BDD du message (MailMessage.id) */ async function getMessage(id: number): Promise { - return api.get(`/mail/messages/${id}`) + const response = await api.get< + BackendMailMessage & { + bodyHtml: string | null + bodyText: string | null + attachments: MailMessageDetailDto['attachments'] + } + >(`/mail/messages/${id}`) + return { + header: mapHeader(response), + bodyHtml: response.bodyHtml, + bodyText: response.bodyText, + attachments: response.attachments, + } } // ─── Actions sur les messages ─────────────────────────────────────────────