From d73db2820cf18a7ff8a3f72f2f93276ee5bc298c Mon Sep 17 00:00:00 2001 From: Manus Sandbox Date: Sun, 30 Nov 2025 13:33:32 -0500 Subject: [PATCH] =?UTF-8?q?Checkpoint:=20Impl=C3=A9mentation=20de=20trois?= =?UTF-8?q?=20fonctionnalit=C3=A9s=20majeures=20:=201.=20Personnalisation?= =?UTF-8?q?=20des=20templates=20d'emails=20avec=20variables=20{{lieu}}=20e?= =?UTF-8?q?t=20{{formateur}}=20(d=C3=A9j=C3=A0=20disponible)=202.=20Aper?= =?UTF-8?q?=C3=A7u=20avant=20envoi=20avec=20donn=C3=A9es=20r=C3=A9elles=20?= =?UTF-8?q?d'un=20apprenant=20(modal=20avec=20iframe)=203.=20Email=20de=20?= =?UTF-8?q?rappel=20J-1=20avec=20bouton=20d'envoi=20et=20d'aper=C3=A7u?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Les templates personnalisés supportent maintenant toutes les variables disponibles. L'aperçu permet de vérifier le rendu final avant l'envoi groupé. Le rappel J-1 complète le système de communication avec les apprenants. --- client/src/pages/AdminSequenceInscrits.tsx | 104 +++++++++++- server/emailPreview.ts | 176 +++++++++++++++++++++ server/emailService.ts | 92 ++++++++++- server/routers.ts | 16 +- todo.md | 22 +++ 5 files changed, 401 insertions(+), 9 deletions(-) create mode 100644 server/emailPreview.ts diff --git a/client/src/pages/AdminSequenceInscrits.tsx b/client/src/pages/AdminSequenceInscrits.tsx index 411db6e..e5de46b 100644 --- a/client/src/pages/AdminSequenceInscrits.tsx +++ b/client/src/pages/AdminSequenceInscrits.tsx @@ -4,13 +4,20 @@ import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/com import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; import { trpc } from "@/lib/trpc"; -import { ArrowLeft, Download, Mail, Search } from "lucide-react"; +import { ArrowLeft, Download, Mail, Search, Eye } from "lucide-react"; import { useLocation, useRoute } from "wouter"; import { useState, useMemo } from "react"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { Skeleton } from "@/components/ui/skeleton"; import { toast } from "sonner"; +import { + Dialog, + DialogContent, + DialogDescription, + DialogHeader, + DialogTitle, +} from "@/components/ui/dialog"; export default function AdminSequenceInscrits() { const [, setLocation] = useLocation(); @@ -41,11 +48,17 @@ export default function AdminSequenceInscrits() { const [filterEtablissement, setFilterEtablissement] = useState("all"); const [filterFonction, setFilterFonction] = useState("all"); const [sortBy, setSortBy] = useState<"date" | "nom">("date"); + const [showPreviewModal, setShowPreviewModal] = useState(false); + const [previewType, setPreviewType] = useState<"teaser" | "rappel">("teaser"); const utils = trpc.useUtils(); const { data: sequence, isLoading: loadingSequence } = trpc.sequences.getById.useQuery({ id: sequenceId }); const { data: inscriptions, isLoading: loadingInscriptions } = trpc.inscriptions.listBySequence.useQuery({ sequenceId }); const { data: formations } = trpc.formations.list.useQuery(); + const { data: emailPreview, isLoading: loadingPreview } = trpc.inscriptions.emailPreview.useQuery( + { sequenceId, type: previewType }, + { enabled: showPreviewModal } + ); const updateStatutMutation = trpc.inscriptions.updateStatut.useMutation({ onSuccess: () => { @@ -143,8 +156,9 @@ export default function AdminSequenceInscrits() { exportFeuilleMutation.mutate({ sequenceId }); }; - const handleSendEmails = (type: "teaser" | "rappel") => { - if (confirm(`Êtes-vous sûr de vouloir envoyer les emails ${type === "teaser" ? "teaser" : "de rappel J-7"} à tous les inscrits confirmés ?`)) { + const handleSendEmails = (type: "teaser" | "rappel" | "rappel_j1") => { + const typeLabel = type === "teaser" ? "teaser" : type === "rappel" ? "de rappel J-7" : "de rappel J-1"; + if (confirm(`Êtes-vous sûr de vouloir envoyer les emails ${typeLabel} à tous les inscrits confirmés ?`)) { sendEmailsMutation.mutate({ sequenceId, type }); } }; @@ -354,6 +368,17 @@ export default function AdminSequenceInscrits() {
+ + + +
+ + {/* Modal d'aperçu d'email */} + + + + + Aperçu de l'email {previewType === "teaser" ? "teaser" : previewType === "rappel" ? "de rappel J-7" : "de rappel J-1"} + + + {loadingPreview ? ( + "Chargement de l'aperçu..." + ) : emailPreview ? ( + `Destinataire : ${emailPreview.recipient.prenom} ${emailPreview.recipient.nom} (${emailPreview.recipient.email})` + ) : ( + "Erreur lors du chargement de l'aperçu" + )} + + + {loadingPreview ? ( +
+
+
+ ) : emailPreview ? ( +
+
+

Sujet :

+

{emailPreview.subject}

+
+
+