"use client" import { create } from "zustand" import { persist } from "zustand/middleware" import { debouncedPersistJSONStorage } from "@/lib/stores/debounced-json-storage" import type { Email } from "@/lib/email-data" export type ScheduleSendPayload = { sendAtIso: string to: { name: string; email: string }[] subject: string previewText: string bodyHtml: string } type ScheduledStoreState = { scheduledEmails: Email[] snoozedEmails: Email[] sentPlaceholderEmails: Email[] } function rowToSchedulePayload(row: Email): ScheduleSendPayload { const email = row.senderEmail?.trim() ?? "" const name = row.scheduledToName ?? row.sender return { sendAtIso: row.scheduledSendAt ?? new Date().toISOString(), to: email ? [{ name, email }] : [], subject: row.subject, previewText: row.preview, bodyHtml: row.body ?? `

`, } } type ScheduledStoreActions = { createScheduledSend: (payload: ScheduleSendPayload) => { id: string } deleteScheduledSend: (id: string) => void archiveScheduledSend: (id: string) => void snoozeScheduledSend: (id: string) => void rescheduleScheduledSend: (id: string, sendAtIso: string) => void markScheduledReadState: (id: string, read: boolean) => void getScheduledEditPayload: (id: string) => ScheduleSendPayload | null updateScheduledSend: (id: string, payload: ScheduleSendPayload) => void sendScheduledNow: (id: string) => void removeScheduledLocal: (id: string) => void /** Mettre en attente depuis la boîte (clone id `snz-…` dans En attente ; l’appelant masque l’id source). */ snoozeMailboxEmail: (row: Email) => void /** Quitter « En attente » : réaffiche dans la Boîte (snz-) ou parmi Planifiés (ex-envoi différé snoozé). */ restoreSnoozedToInbox: (row: Email) => void } export const useScheduledStore = create()( persist( (set, get) => ({ scheduledEmails: [], snoozedEmails: [], sentPlaceholderEmails: [], createScheduledSend: (payload) => { const id = `sched-${Date.now()}-${Math.random().toString(36).slice(2, 7)}` const first = payload.to[0] const toName = first?.name?.trim() || first?.email || "Destinataire" const row: Email = { id, sender: toName, senderEmail: first?.email, subject: payload.subject.trim() || "(Sans objet)", preview: payload.previewText.slice(0, 200), body: payload.bodyHtml, date: payload.sendAtIso, read: true, starred: false, important: false, labels: ["scheduled"], scheduledSendAt: payload.sendAtIso, scheduledToName: toName, } set((s) => ({ scheduledEmails: [row, ...s.scheduledEmails.filter((e) => e.id !== id)], })) return { id } }, deleteScheduledSend: (id) => set((s) => ({ scheduledEmails: s.scheduledEmails.filter((e) => e.id !== id), })), archiveScheduledSend: (id) => set((s) => ({ scheduledEmails: s.scheduledEmails.filter((e) => e.id !== id), })), snoozeScheduledSend: (id) => set((s) => { const row = s.scheduledEmails.find((e) => e.id === id) if (!row) return s const wake = new Date(Date.now() + 24 * 60 * 60 * 1000) return { scheduledEmails: s.scheduledEmails.filter((e) => e.id !== id), snoozedEmails: [ { ...row, labels: ["snoozed"], scheduledSendAt: undefined, scheduledToName: undefined, snoozeWakeAt: wake.toISOString(), sender: row.scheduledToName ?? row.sender, read: true, }, ...s.snoozedEmails, ], } }), rescheduleScheduledSend: (id, sendAtIso) => set((s) => ({ scheduledEmails: s.scheduledEmails.map((e) => e.id === id ? { ...e, scheduledSendAt: sendAtIso } : e ), })), markScheduledReadState: (id, read) => set((s) => ({ scheduledEmails: s.scheduledEmails.map((e) => e.id === id ? { ...e, read } : e ), })), getScheduledEditPayload: (id) => { const row = get().scheduledEmails.find((e) => e.id === id) if (!row) return null return rowToSchedulePayload(row) }, updateScheduledSend: (id, payload) => set((s) => { const first = payload.to[0] const toName = first?.name?.trim() || first?.email || "Destinataire" return { scheduledEmails: s.scheduledEmails.map((e) => e.id === id ? { ...e, sender: toName, senderEmail: first?.email, subject: payload.subject.trim() || "(Sans objet)", preview: payload.previewText.slice(0, 200), body: payload.bodyHtml, scheduledSendAt: payload.sendAtIso, scheduledToName: toName, } : e ), } }), sendScheduledNow: (id) => set((s) => { const row = s.scheduledEmails.find((e) => e.id === id) if (!row) return s const now = new Date() return { scheduledEmails: s.scheduledEmails.filter((e) => e.id !== id), sentPlaceholderEmails: [ { id: `sent-now-${Date.now()}-${Math.random().toString(36).slice(2, 5)}`, sender: row.scheduledToName ?? row.sender, senderEmail: row.senderEmail, subject: row.subject, preview: row.preview, body: row.body, date: now.toISOString(), read: true, starred: false, important: false, labels: ["sent"], }, ...s.sentPlaceholderEmails, ], } }), removeScheduledLocal: (id) => set((s) => ({ scheduledEmails: s.scheduledEmails.filter((e) => e.id !== id), })), snoozeMailboxEmail: (row) => set((s) => { const persistedId = row.id.startsWith("snz-") ? row.id : `snz-${row.id}` const wake = new Date(Date.now() + 24 * 60 * 60 * 1000) const wakeIso = wake.toISOString() const copy: Email = { ...row, id: persistedId, labels: ["snoozed"], snoozeWakeAt: wakeIso, scheduledSendAt: undefined, scheduledToName: undefined, read: true, } return { snoozedEmails: [ copy, ...s.snoozedEmails.filter((e) => e.id !== persistedId), ], } }), restoreSnoozedToInbox: (row) => set((s) => { const nextSnoozed = s.snoozedEmails.filter((e) => e.id !== row.id) if (row.id.startsWith("snz-")) { return { snoozedEmails: nextSnoozed } } const resumeAt = row.snoozeWakeAt ?? new Date(Date.now() + 24 * 60 * 60 * 1000).toISOString() const back: Email = { ...row, labels: ["scheduled"], scheduledSendAt: resumeAt, scheduledToName: row.sender, snoozeWakeAt: undefined, date: "", read: true, } return { snoozedEmails: nextSnoozed, scheduledEmails: [ back, ...s.scheduledEmails.filter((e) => e.id !== row.id), ], } }), }), { name: "ultimail-scheduled-state", storage: debouncedPersistJSONStorage, version: 1, } ) )