From 26a16327cfb37406101a39cc981ba79796e5e77f Mon Sep 17 00:00:00 2001 From: Manus Date: Fri, 30 Jan 2026 04:04:16 -0500 Subject: [PATCH] =?UTF-8?q?Checkpoint:=20Correction=20et=20finalisation=20?= =?UTF-8?q?du=20template=20email=20attestation=20:=20ajout=20du=20type=20'?= =?UTF-8?q?attestation'=20dans=20le=20frontend,=20cr=C3=A9ation=20automati?= =?UTF-8?q?que=20du=20template=20au=20d=C3=A9marrage=20de=20l'application,?= =?UTF-8?q?=20d=C3=A9ploiement=20r=C3=A9ussi=20sur=20le=20VPS.=20Le=20temp?= =?UTF-8?q?late=20est=20maintenant=20visible=20et=20modifiable=20dans=20l'?= =?UTF-8?q?interface=20de=20gestion=20des=20templates.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/pages/AdminEmailTemplates.tsx | 7 ++-- server/_core/index.ts | 4 ++ server/initTemplates.ts | 51 ++++++++++++++++++++++++ 3 files changed, 59 insertions(+), 3 deletions(-) create mode 100644 server/initTemplates.ts diff --git a/client/src/pages/AdminEmailTemplates.tsx b/client/src/pages/AdminEmailTemplates.tsx index 81daf24..28982e3 100644 --- a/client/src/pages/AdminEmailTemplates.tsx +++ b/client/src/pages/AdminEmailTemplates.tsx @@ -38,7 +38,7 @@ export default function AdminEmailTemplates() { const [selectedType, setSelectedType] = useState("inscription"); const [showPreviewModal, setShowPreviewModal] = useState(false); const [formData, setFormData] = useState({ - type: "inscription" as "inscription" | "teaser" | "rappel1" | "rappel2" | "rappel3" | "rappel4" | "rappel5" | "rappel6" | "reset_password", + type: "inscription" as "inscription" | "teaser" | "rappel1" | "rappel2" | "rappel3" | "rappel4" | "rappel5" | "rappel6" | "reset_password" | "attestation", titre: "", bodyContent: "", couleurPrincipale: "#283581", @@ -92,7 +92,7 @@ export default function AdminEmailTemplates() { useEffect(() => { if (currentTemplate) { const newData = { - type: currentTemplate.type as "inscription" | "teaser" | "rappel1" | "rappel2" | "rappel3" | "rappel4" | "rappel5" | "rappel6" | "reset_password", + type: currentTemplate.type as "inscription" | "teaser" | "rappel1" | "rappel2" | "rappel3" | "rappel4" | "rappel5" | "rappel6" | "reset_password" | "attestation", titre: currentTemplate.titre, bodyContent: currentTemplate.bodyContent || "", couleurPrincipale: currentTemplate.couleurPrincipale, @@ -128,7 +128,7 @@ export default function AdminEmailTemplates() { const handleReset = () => { if (currentTemplate) { setFormData({ - type: currentTemplate.type as "inscription" | "teaser" | "rappel1" | "rappel2" | "rappel3" | "rappel4" | "rappel5" | "rappel6" | "reset_password", + type: currentTemplate.type as "inscription" | "teaser" | "rappel1" | "rappel2" | "rappel3" | "rappel4" | "rappel5" | "rappel6" | "reset_password" | "attestation", titre: currentTemplate.titre, bodyContent: currentTemplate.bodyContent || "", couleurPrincipale: currentTemplate.couleurPrincipale, @@ -216,6 +216,7 @@ export default function AdminEmailTemplates() { { value: "rappel5", label: "Rappel 5" }, { value: "rappel6", label: "Rappel 6" }, { value: "reset_password", label: "Réinitialisation de mot de passe" }, + { value: "attestation", label: "Attestation de formation" }, ]; return ( diff --git a/server/_core/index.ts b/server/_core/index.ts index 8db7490..2f4cca0 100644 --- a/server/_core/index.ts +++ b/server/_core/index.ts @@ -14,6 +14,7 @@ import { serveStatic, setupVite } from "./vite"; import { initRappelScheduler } from "../rappelScheduler"; import { initRappelRetryScheduler } from "../rappelRetry"; import { initFail2BanAlertSystem } from "../fail2banAlertService"; +import { initAttestationTemplate } from "../initTemplates"; function isPortAvailable(port: number): Promise { return new Promise(resolve => { @@ -75,6 +76,9 @@ async function startServer() { server.listen(port, () => { console.log(`Server running on http://localhost:${port}/`); + // Initialiser les templates par défaut + initAttestationTemplate(); + // Initialiser le scheduler de rappels automatiques initRappelScheduler(); diff --git a/server/initTemplates.ts b/server/initTemplates.ts new file mode 100644 index 0000000..7122959 --- /dev/null +++ b/server/initTemplates.ts @@ -0,0 +1,51 @@ +import { getDb } from './db'; +import { emailTemplates } from '../drizzle/schema'; +import { eq } from 'drizzle-orm'; + +/** + * Initialise le template d'email pour les attestations s'il n'existe pas déjà + */ +export async function initAttestationTemplate() { + const db = await getDb(); + if (!db) { + console.warn('[initTemplates] Database not available, skipping template initialization'); + return; + } + + try { + // Vérifier si le template attestation existe déjà + const existing = await db + .select() + .from(emailTemplates) + .where(eq(emailTemplates.type, 'attestation')) + .limit(1); + + if (existing.length > 0) { + console.log('[initTemplates] Template attestation already exists'); + return; + } + + // Créer le template par défaut + await db.insert(emailTemplates).values({ + type: 'attestation', + titre: 'Attestation de formation', + bodyContent: `

Bonjour {{prenomApprenant}} {{nomApprenant}},

+ +

Nous vous remercions d'avoir participé à la formation {{nomFormation}}.

+ +

Vous trouverez ci-joint votre attestation de formation.

+ +

Nous espérons que cette formation vous a été bénéfique et nous vous souhaitons beaucoup de succès dans vos projets futurs.

+ +

Cordialement,
+L'équipe de formation

`, + couleurPrincipale: '#283581', + couleurSecondaire: '#0578BE', + piedDePage: 'Cet email a été envoyé automatiquement, merci de ne pas y répondre.', + }); + + console.log('[initTemplates] Template attestation created successfully'); + } catch (error) { + console.error('[initTemplates] Error creating attestation template:', error); + } +}