/** * Génère un récapitulatif complet d'une demande traitée pour diffusion interne. */ const requestTypeLabels: Record = { subvention_fonctionnement: "Subvention de fonctionnement", subvention_projet: "Subvention de projet", agrement_jeunesse_education: "Agrément Jeunesse et Éducation Populaire", agrement_sport: "Agrément Sport", autorisation_occupation: "Autorisation d'occupation", demande_salle: "Demande de salle / local", demande_materiel_evenementiel: "Demande de matériel événementiel", autre: "Autre demande", }; const statusLabels: Record = { validee: "Validée", refusee: "Refusée", en_cours_traitement: "En cours de traitement", information_complementaire: "Information complémentaire demandée", }; const statusBadgeStyles: Record = { validee: { label: "VALIDÉE", bg: "#ecfdf3", border: "#a7f3d0", text: "#166534" }, refusee: { label: "REFUSÉE", bg: "#fef2f2", border: "#fecaca", text: "#b91c1c" }, en_cours_traitement: { label: "EN COURS", bg: "#eff6ff", border: "#bfdbfe", text: "#1d4ed8" }, information_complementaire: { label: "INFO REQUISE", bg: "#fff7ed", border: "#fed7aa", text: "#c2410c" }, }; interface RecapData { request: { id: number; titre: string; type: string; status: string; description: string | null; montantDemande: number | null; montantAccorde: number | null; commentaireAdmin: string | null; formData: string | null; dateSubmission: Date | null; dateTraitement: Date | null; createdAt: Date | null; }; association: { nomAssociation: string; siret: string | null; adresse: string | null; codePostal: string | null; ville: string | null; telephone: string | null; emailContact: string | null; nomRepresentant: string | null; } | null; traitePar: string; documents?: { name: string; type: string }[]; serviceLabel?: string | null; } function formatDateTimeFr(date: Date | string | null | undefined) { if (!date) return "Non renseignée"; return new Date(date).toLocaleString("fr-FR", { day: "2-digit", month: "2-digit", year: "numeric", hour: "2-digit", minute: "2-digit", }); } function formatDateFr(date: string | null | undefined) { if (!date) return "Non renseignée"; return new Date(date).toLocaleDateString("fr-FR", { day: "2-digit", month: "2-digit", year: "numeric", }); } function formatAmount(amount: number | null | undefined) { if (amount == null) return "Non renseigné"; return `${(amount / 100).toLocaleString("fr-FR")} €`; } function escapeHtml(value: string) { return value .replace(/&/g, "&") .replace(//g, ">") .replace(/"/g, """) .replace(/'/g, "'"); } function parseRequestHighlights(requestType: string, formData: any): Array<{ label: string; value: string }> { if (!formData || typeof formData !== "object") return []; if (requestType === "demande_salle") { const salles = Array.isArray(formData.sallesNoms) ? formData.sallesNoms.join(", ") : Array.isArray(formData.sallesSelectionnees) ? formData.sallesSelectionnees.join(", ") : ""; const highlights = [ salles ? { label: "Salle(s)", value: salles } : null, formData.dateReservation ? { label: "Date de début", value: formatDateFr(formData.dateReservation) } : null, formData.dateFinReservation ? { label: "Date de fin", value: formatDateFr(formData.dateFinReservation) } : null, formData.heureDebut || formData.heureFin ? { label: "Créneau", value: `${formData.heureDebut || "?"} - ${formData.heureFin || "?"}` } : null, formData.nombreParticipants ? { label: "Participants", value: String(formData.nombreParticipants) } : null, formData.motifReservation ? { label: "Motif", value: String(formData.motifReservation) } : null, ]; return highlights.filter(Boolean) as Array<{ label: string; value: string }>; } if (requestType === "demande_materiel_evenementiel") { const highlights = [ formData.commune ? { label: "Commune", value: String(formData.commune) } : null, formData.dateDebutManifestation || formData.dateManifestation ? { label: "Début manifestation", value: formatDateFr(formData.dateDebutManifestation || formData.dateManifestation) } : null, formData.dateFinManifestation ? { label: "Fin manifestation", value: formatDateFr(formData.dateFinManifestation) } : null, formData.dateRestitution ? { label: "Restitution", value: formatDateFr(formData.dateRestitution) } : null, formData.motifDemande ? { label: "Motif", value: String(formData.motifDemande) } : null, ]; return highlights.filter(Boolean) as Array<{ label: string; value: string }>; } return [ formData.dateDebut ? { label: "Date de début", value: formatDateFr(formData.dateDebut) } : null, formData.dateFin ? { label: "Date de fin", value: formatDateFr(formData.dateFin) } : null, formData.objetProjet ? { label: "Objet", value: String(formData.objetProjet) } : null, ].filter(Boolean) as Array<{ label: string; value: string }>; } export function generateRequestRecapEmail(data: RecapData): { title: string; content: string; html: string } { const { request, association, traitePar, documents, serviceLabel } = data; const typeLabel = requestTypeLabels[request.type] || request.type; const statusLabel = statusLabels[request.status] || request.status; const badge = statusBadgeStyles[request.status] || { label: statusLabel.toUpperCase(), bg: "#f3f4f6", border: "#d1d5db", text: "#374151", }; let formData: any = {}; try { formData = request.formData ? JSON.parse(request.formData) : {}; } catch { formData = {}; } const highlights = parseRequestHighlights(request.type, formData); const subject = `Demande traitée #${request.id} - ${statusLabel} - ${request.titre}`; const textSections: string[] = [ "Récapitulatif de demande traitée", "", `Demande n°${request.id} - ${statusLabel}`, `Type : ${typeLabel}`, `Association : ${association?.nomAssociation || "Non renseignée"}`, `Traitée par : ${traitePar}`, `Date de traitement : ${formatDateTimeFr(request.dateTraitement)}`, serviceLabel ? `Service concerné : ${serviceLabel}` : "", request.commentaireAdmin ? `Commentaire : ${request.commentaireAdmin}` : "", request.montantAccorde != null ? `Montant accordé : ${formatAmount(request.montantAccorde)}` : "", "", "Informations de l'association", `- Nom : ${association?.nomAssociation || "Non renseigné"}`, association?.siret ? `- SIRET : ${association.siret}` : "", association?.adresse ? `- Adresse : ${association.adresse}${association.codePostal ? `, ${association.codePostal}` : ""}${association.ville ? ` ${association.ville}` : ""}` : "", association?.nomRepresentant ? `- Représentant : ${association.nomRepresentant}` : "", association?.telephone ? `- Téléphone : ${association.telephone}` : "", association?.emailContact ? `- Email : ${association.emailContact}` : "", "", "Points clés du dossier", ...highlights.map((item) => `- ${item.label} : ${item.value}`), request.description ? `- Description : ${request.description}` : "", request.montantDemande != null ? `- Montant demandé : ${formatAmount(request.montantDemande)}` : "", ]; if (documents?.length) { textSections.push("", "Documents joints au dossier"); textSections.push(...documents.map((doc, index) => `${index + 1}. ${doc.name} (${doc.type})`)); } textSections.push( "", "Le PDF récapitulatif est joint à cet email lorsqu'il est disponible pour ce type de demande.", "", "Ce message a été généré automatiquement par le Portail Associations." ); const htmlHighlights = highlights .map( (item) => ` ${escapeHtml(item.label)} ${escapeHtml(item.value)} ` ) .join(""); const htmlDocuments = documents?.length ? `

Documents du dossier

    ${documents.map((doc) => `
  • ${escapeHtml(doc.name)} (${escapeHtml(doc.type)})
  • `).join("")}
` : ""; const html = `
Portail Associations

Transmission d'une demande traitée

Ce message reprend les informations essentielles du dossier et le PDF récapitulatif est joint lorsqu'il est disponible.

${escapeHtml(badge.label)}

${escapeHtml(request.titre)}

Demande #${request.id} • ${escapeHtml(typeLabel)}

Décision et transmission

${serviceLabel ? ` ` : ""} ${request.montantAccorde != null ? ` ` : ""}
Traitée par ${escapeHtml(traitePar)}
Date de traitement ${escapeHtml(formatDateTimeFr(request.dateTraitement))}
Service concerné ${escapeHtml(serviceLabel)}
Montant accordé ${escapeHtml(formatAmount(request.montantAccorde))}
${request.commentaireAdmin ? `
Commentaire administratif
${escapeHtml(request.commentaireAdmin).replace(/\n/g, "
")}
` : ""}

Association

${association?.siret ? ` ` : ""} ${(association?.adresse || association?.ville || association?.codePostal) ? ` ` : ""} ${association?.nomRepresentant ? ` ` : ""} ${association?.telephone ? ` ` : ""} ${association?.emailContact ? ` ` : ""}
Nom ${escapeHtml(association?.nomAssociation || "Non renseignée")}
SIRET ${escapeHtml(association.siret)}
Adresse ${escapeHtml( [association?.adresse, association?.codePostal, association?.ville].filter(Boolean).join(" ") )}
Représentant ${escapeHtml(association.nomRepresentant)}
Téléphone ${escapeHtml(association.telephone)}
Email ${escapeHtml(association.emailContact)}

Points clés du dossier

${request.montantDemande != null ? ` ` : ""} ${htmlHighlights} ${request.description ? ` ` : ""}
Date de soumission ${escapeHtml(formatDateTimeFr(request.dateSubmission))}
Montant demandé ${escapeHtml(formatAmount(request.montantDemande))}
Description ${escapeHtml(request.description).replace(/\n/g, "
")}
${htmlDocuments}

Le PDF récapitulatif est joint à cet email lorsqu’il est disponible pour ce type de demande. Tu peux l’utiliser pour la transmission interne, l’archivage ou l’impression.

`; return { title: subject, content: textSections.filter(Boolean).join("\n"), html, }; }