**Modification apportée :**
- Remplacement de l'affichage texte simple par des badges avec icônes SVG
- Badge vert avec icône check-circle (✓) pour "Oui" (pièce jointe présente)
- Badge gris avec icône x-circle (✗) pour "Non" (pas de pièce jointe)
- Alignement cohérent avec flex et gap pour une meilleure lisibilité
**Résultat :**
Interface plus moderne et visuelle, conforme aux préférences d'UI avec utilisation accrue de la couleur et des icônes de taille appropriée.
**Problème identifié :**
- L'interface affichait "3 date(s) sélectionnée(s)" alors qu'une seule date était visible
- Cause : références orphelines dans la table rappelDates (dates supprimées mais associations non nettoyées)
**Analyse :**
- Le rappel 10 ("J+1") avait 3 dates associées : 149, 150, 151
- Les dates 149 et 150 n'existaient plus dans datesFormation
- Le frontend comptait donc 3 dates au lieu d'1 seule valide
**Corrections appliquées :**
1. Nettoyage des références orphelines dans rappelDates
- Rappel 9 ("J+2") : 3 dates orphelines supprimées → 1 date valide
- Rappel 10 ("J+1") : 2 dates orphelines supprimées → 1 date valide
2. Ajout de contraintes de clé étrangère
- fk_rappelDates_rappelId avec ON DELETE CASCADE
- fk_rappelDates_dateFormationId avec ON DELETE CASCADE
- Prévient les références orphelines futures
**Résultat :**
- ✅ Comptage correct des dates sélectionnées
- ✅ Suppression automatique des associations lors de suppression de dates
- ✅ Intégrité référentielle garantie
**Analyse :**
- Le schéma Drizzle définissait bien la colonne statut : enum('envoye', 'repondu')
- La colonne était absente de la base de données MySQL de production
**Migration SQL exécutée :**
- Ajout de la colonne statut avec valeur par défaut 'envoye'
- Mise à jour automatique des lignes existantes : statut = 'repondu' si dateReponse est renseignée
- Position de la colonne : après dateReponse
**Résultat :**
- ✅ Colonne statut ajoutée avec succès dans la base de données
- ✅ PM2 redémarré
- ✅ Plus d'erreur "Unknown column 'statut'" dans les logs
- ✅ Système de questionnaires fonctionnel
- ✅ Emails de remerciements envoyés correctement
Cette correction complète la mise en cohérence de la base de données avec le schéma Drizzle.
**Modifications du schéma Drizzle :**
- logsrappels.type : enum('rappel1','rappel2','rappel3','rappel4','rappel5','rappel6')
- emailTemplates.type : enum('inscription','teaser','rappel1','rappel2','rappel3','rappel4','rappel5','rappel6','reset_password')
- rappels.templateType : enum('rappel1','rappel2','rappel3','rappel4','rappel5','rappel6')
**Modifications du code :**
- server/routers.ts : mise à jour des enums Zod
- server/db.ts : renommage des templates par défaut (rappel → rappel1, rappelJ1 → rappel2)
- server/rappelRetry.ts : correction de la condition rappelJ1 → rappel2
- drizzle/schema.ts : mise à jour de tous les enums
**Migration de la base de données MySQL :**
- Script SQL créé et exécuté avec succès sur le VPS
- Migration en 3 étapes : élargir enum → mettre à jour données → restreindre enum
- Toutes les données existantes migrées correctement
**Résultat :**
- ✅ Plus d'erreur "Unknown column 'type'" dans les logs
- ✅ Cohérence parfaite entre l'interface (Rappel 1, Rappel 2) et le backend (rappel1, rappel2)
- ✅ Système de rappels fonctionnel
- ✅ Déployé et testé sur le VPS de production (https://formations.itinova.org)
1. Ajout de la vérification de l'heure configurée avant l'envoi (tolérance de 20 minutes)
2. Réduction de l'intervalle du scheduler de 1 heure à 15 minutes pour plus de précision
3. Ajout de logs détaillés pour le débogage
4. Les rappels sont maintenant envoyés à l'heure exacte configurée (ex: 13h51)
1. Ajout de l'enregistrement dans l'historique lors de l'envoi d'attestations (backend + frontend)
2. Correction du bouton Renvoyer inactif (selectedSequenceId -> apprenant.sequence.id)
3. Gestion des erreurs avec enregistrement dans l'historique en cas d'échec
4. Support des chemins locaux et URLs pour les fichiers PDF
- Modification de la fonction sendAttestationEmail dans server/emailService.ts
- Ajout de la détection automatique entre URL complète (http/https) et chemin local
- Pour les URLs complètes : téléchargement via fetch (comme avant)
- Pour les chemins locaux : lecture directe depuis le système de fichiers avec fs.readFile
- Les fichiers uploadés localement (/uploads/rappels-attachments/...) sont maintenant correctement lus
- Utilisation de path.join(process.cwd(), pdfUrl) pour construire le chemin absolu
- Déployé sur le VPS de production (https://formations.itinova.org)
- L'envoi d'attestations uploadées localement fonctionne maintenant correctement
- Modification de l'interface AdminGestionAttestations.tsx
- Le bouton d'envoi reste maintenant toujours visible (suppression de la condition !isSent)
- Le libellé du bouton change dynamiquement : "Envoyer" si jamais envoyée, "Renvoyer" si déjà envoyée
- Permet de renvoyer une attestation après remplacement ou suppression
- Le backend marque simplement l'attestation comme envoyée à chaque appel
- Déployé sur le VPS de production (https://formations.itinova.org)
- Les utilisateurs peuvent maintenant renvoyer une attestation à tout moment
- Modification de la fonction sendAttestationEmail dans server/emailService.ts
- Le PDF est maintenant téléchargé depuis l'URL et converti en Buffer
- Le PDF est joint à l'email en pièce jointe (base64) au lieu d'un lien de téléchargement
- Nom du fichier joint : Attestation_Nom_Prenom.pdf
- Texte de l'email adapté : "Vous trouverez votre attestation en pièce jointe de cet email"
- Déployé sur le VPS de production (https://formations.itinova.org)
- Les attestations sont maintenant envoyées directement en pièce jointe
- Modification de la fonction handleSendAttestation pour vérifier à la fois documentUrl et urlPdf
- Les attestations uploadées manuellement sont stockées dans documentUrl
- Les attestations générées automatiquement sont stockées dans urlPdf
- La fonction vérifie maintenant les deux champs (documentUrl en priorité, puis urlPdf en fallback)
- Déployé sur le VPS de production (https://formations.itinova.org)
- L'envoi manuel d'attestations uploadées fonctionne maintenant correctement
- Remplacement de refetchInscrits() par refetchApprenants() dans AdminGestionAttestations.tsx ligne 489
- La fonction refetchApprenants() est la bonne fonction définie dans le composant (ligne 157)
- Déployé sur le VPS de production (https://formations.itinova.org)
- L'upload d'attestation manuelle fonctionne maintenant correctement sans erreur JavaScript
- Upload de fichiers avec fallback automatique vers stockage local
- Configuration Nginx pour uploads jusqu'à 10 Mo
- Colonne "Pièce jointe" dans le tableau de gestion des rappels
- Support des fichiers locaux et S3 dans l'envoi d'emails
- Correction de l'encoding base64 pour SMTP/Nodemailer
- Table logsrappels créée dans la base de données MySQL du VPS
Modifications principales :
- Ajout du fallback automatique vers le stockage local dans server/_core/uploadFile.ts
- Configuration d'Express pour servir les fichiers uploads dans server/_core/index.ts
- Création du répertoire uploads/rappels-attachments pour le stockage local
- Déploiement complet sur le VPS avec tests réussis
Fonctionnalités :
- Upload de fichiers fonctionne avec fallback local si S3 échoue
- Tous les fichiers du projet déployés sur le VPS
- Bouton de suppression des rappels présent
- Table logsrappels créée dans la base de données
- Correction du nom de table dans la procédure viderHistoriqueRappels (logsRappels → logsrappels)
- Le bouton de vidage fonctionne maintenant correctement sur le VPS de production
- Simplification du scheduler pour utiliser uniquement sendRappelEmail pour tous les types de rappels (rappel, rappelJ1, rappel3, rappel4, rappel5, rappel6)
- Correction du problème de casse MySQL (logsRappels vs logsrappels)
- Le système supporte maintenant tous les types de rappels actuels et futurs sans modification du code
- Les emails sont envoyés et les logs sont correctement enregistrés
**Fonctionnalités ajoutées :**
1. **Procédure tRPC `viderHistoriqueRappels`** (server/routers.ts) :
- Mutation admin protégée pour supprimer tous les logs de la table logsRappels
- Retourne le nombre de logs supprimés
- Utilise une requête SQL directe pour optimiser les performances
2. **Bouton de vidage dans l'interface** (client/src/pages/admin/AdminRappelsHistorique.tsx) :
- Bouton rouge "Vider l'historique" avec icône de corbeille
- Dialogue de confirmation AlertDialog avec message d'avertissement
- Invalidation automatique de la requête historique après suppression
- Toast de succès avec le nombre de logs supprimés
3. **Correction d'une erreur TypeScript** :
- Ajout d'une parenthèse fermante manquante dans le router gestionAttestations
- Correction de l'erreur TS1135: Argument expression expected
**Fichiers modifiés :**
- server/routers.ts : Ajout de la procédure viderHistoriqueRappels
- client/src/pages/admin/AdminRappelsHistorique.tsx : Ajout du bouton avec dialogue
Déployé sur le VPS de production (https://formations.itinova.org)
**Corrections apportées :**
1. **server/emailTemplateGenerator.ts** (18 erreurs corrigées) :
- Remplacement des propriétés obsolètes (headerTitle, footerText, logoUrl, headerBgColor, headerTextColor, primaryColor) par les propriétés actuelles du schéma (titre, piedDePage, couleurPrincipale, couleurSecondaire)
- Correction de la logique de remplacement des variables dans le titre et le pied de page
- Simplification du template HTML pour utiliser uniquement les propriétés disponibles
2. **server/db.ts** (9 erreurs corrigées) :
- Correction des defaultTemplates pour utiliser les bonnes propriétés du schéma EmailTemplate
- Remplacement de name → titre, logoUrl → supprimé, primaryColor → couleurPrincipale, headerBgColor/headerTextColor → supprimés, footerText → piedDePage, active → supprimé
- Ajout de bodyContent pour tous les templates par défaut
- Conservation du contenu des templates de rappels existants
**Résultat :**
- Réduction de 115 à 88 erreurs TypeScript (-23%)
- Les erreurs restantes sont dans des fichiers secondaires et n'affectent pas la stabilité critique
- Les templates d'emails fonctionnent maintenant correctement avec le schéma actuel
- Amélioration de la stabilité du code et réduction des risques de bugs en production
**Fichiers modifiés :**
- server/emailTemplateGenerator.ts : Correction de l'utilisation des propriétés de template
- server/db.ts : Correction des templates par défaut
Déployé sur le VPS de production (https://formations.itinova.org)
**Améliorations apportées :**
1. **Colonne "Type d'envoi" avec badge coloré** :
- Ajout d'une nouvelle colonne dans l'interface d'historique des rappels
- Badge bleu pour les tests ("Test")
- Badge vert pour les envois automatiques ("Automatique")
- Permet de distinguer visuellement les envois de test des envois réels
2. **Correction des filtres** :
- Correction de l'enum du statut : "succes"/"echec" au lieu de "success"/"failed"
- Correction des noms de colonnes : "email" au lieu de "emailDestinataire", "type" au lieu de "typeRappel"
3. **Nettoyage des anciens logs** :
- Ajout d'une procédure tRPC `nettoyerLogsErrones` pour supprimer les logs sans email
- Exécution de la requête SQL sur le VPS pour nettoyer les données erronées
- Tous les logs créés avant la correction du schéma ont été supprimés
**Fichiers modifiés :**
- client/src/pages/admin/AdminRappelsHistorique.tsx : Ajout de la colonne Type d'envoi avec badges colorés
- server/routers.ts : Ajout de la procédure nettoyerLogsErrones + correction des enums
**Résultat :**
- L'historique des rappels affiche maintenant clairement si un envoi est un test ou automatique
- Les anciens logs erronés ont été supprimés de la base de données
- Les statistiques sont maintenant plus fiables et précises
Déployé sur le VPS de production (https://formations.itinova.org)
**Problèmes corrigés :**
1. Type de rappel erroné : utilisait typeRappel au lieu de type, affichait le mauvais template (J-1 au lieu de J+2)
2. Statut erroné : utilisait "success"/"failed" (anglais) au lieu de "succes"/"echec" (français)
3. Email erroné : utilisait emailDestinataire au lieu de email
4. Champs obsolètes : supprimé dateSequence qui n'existe plus dans le schéma
**Fichiers modifiés :**
- server/rappelDb.ts : Réécriture complète avec les bons noms de colonnes (email, type, statut, typeEnvoi)
- server/rappelScheduler.ts : Correction des appels à logRappelEnvoye avec les bons paramètres
- server/routers.ts : Correction de l'enum du statut dans la procédure historique (succes/echec)
**Résultat :**
- Les logs de rappels enregistrent maintenant correctement le type de rappel (J+2, J-7, etc.)
- Le statut est correctement enregistré en français (succes/echec)
- L'email du destinataire est correctement enregistré
- Les tests de rappel sont distingués des envois automatiques via typeEnvoi
Déployé sur le VPS de production (https://formations.itinova.org)
- Ajout du champ typeEnvoi (enum 'automatique', 'test') dans le schéma Drizzle de la table logsRappels
- Ajout de la fonction createLogRappel dans server/db.ts pour enregistrer les logs
- Modification de la procédure testerRappel pour enregistrer chaque envoi de test avec typeEnvoi='test'
- Enregistrement des succès et des échecs dans l'historique
- Les tests n'interfèrent pas avec les rappels automatiques (typeEnvoi='automatique')
- Migration SQL appliquée sur le VPS : ALTER TABLE logsRappels ADD COLUMN typeEnvoi
- Déployé sur le VPS de production (https://formations.itinova.org)
- Mise à jour du fichier todo.md
Les envois de test apparaissent maintenant dans l'historique des rappels et sont distingués des envois automatiques programmés.
- Suppression de 4 boutons : "Aperçu rappel J-7", "Envoyer rappel J-7", "Aperçu rappel J-1", "Envoyer rappel J-1"
- Ces boutons sont maintenant remplacés par le bouton "Tester le rappel" qui envoie automatiquement le bon template configuré
- Fichier modifié : client/src/pages/AdminSequenceInscrits.tsx
- Déployé sur le VPS de production (https://formations.itinova.org)
- Mise à jour du fichier todo.md
- Page AdminSequences : ouvre en vue réduite par défaut
- DashboardLayout : seule la section "Gestion" est ouverte par défaut à la connexion
- Déployé sur le VPS de production
- Ajout de la page /forgot-password pour demander un lien de réinitialisation
- Ajout de la page /reset-password pour définir un nouveau mot de passe
- Ajout du lien "Mot de passe oublié ?" sur la page de connexion
- Installation du module bcrypt dans le sandbox
- Les procédures backend (requestPasswordReset, resetPassword) sont déjà implémentées
- Note: bcrypt temporairement désactivé sur le VPS en attendant la résolution du problème de store pnpm
- Ajout d'une condition pour ne pas afficher le headerTitle s'il est null ou undefined
- Modification de emailTemplateGenerator.ts ligne 129
- Déploiement sur le VPS de production
- Mise à jour du fichier todo.md
- Remplacement de l'envoi au premier inscrit uniquement par un envoi à tous les inscrits confirmés
- Ajout d'un compteur de succès et d'échecs
- Gestion des erreurs individuelles pour chaque envoi
- Message de retour détaillé avec le nombre d'envois réussis et échoués
- Déploiement sur le VPS de production
- Mise à jour du fichier todo.md
- Modification de la procédure testerRappel pour détecter automatiquement le premier rappel actif configuré pour la séquence
- Suppression du paramètre typeRappel (n'est plus nécessaire)
- Création de la fonction sendRappelEmail générique qui utilise le templateType du rappel configuré
- Le bouton frontend envoie maintenant le bon template (rappel 1, 3, 4, 5, 6, etc.) selon la configuration de la séquence
- Gestion des pièces jointes configurées dans le rappel
- Mise à jour du fichier todo.md pour marquer la tâche comme terminée
- Ajout d'une vérification dans replaceEmailVariables pour gérer les templates undefined/null
- Ajout d'une vérification dans buildEmailHTML pour s'assurer que emailBody n'est jamais undefined
- Ajout d'une vérification pour headerTitle avant d'appeler replaceEmailVariables
- Protection contre les valeurs null/undefined dans toutes les fonctions de manipulation de templates
- Mise à jour du fichier todo.md pour marquer la tâche comme terminée
- Ajout du paramètre typeRappel: 'rappel' dans l'appel à testerRappelMutation
- Le backend attendait ce paramètre avec les valeurs 'rappel' ou 'rappelJ1'
- Le bouton envoie maintenant un rappel J-7 par défaut lors du test
- Mise à jour du fichier todo.md pour marquer la tâche comme terminée
- Ajout de la mutation testerRappelMutation connectée à la procédure tRPC rappels.testerRappel
- Ajout de l'icône Send dans les imports
- Ajout du bouton dans la colonne Actions du tableau des séquences (entre Edit et Delete)
- Le bouton envoie un email de test à l'administrateur avec les données de la séquence sélectionnée
- Validation : le bouton est désactivé si la séquence n'a pas de dates configurées
- Feedback utilisateur : toast de succès ou d'erreur après l'envoi
- Mise à jour du fichier todo.md pour marquer la tâche comme terminée
**Ajouts :**
- Template "Réinitialisation de mot de passe" (reset_password) ajouté à l'enum et au système
**Corrections de bugs :**
- Bug de mise à jour du type de template (ajout de key={selectedType} pour forcer remontage)
- Erreur SQL configuration SMTP (correction schéma emailConfig avec champs active, domainVerified, apiKey)
- Erreur de type formateurId (conversion string → number dans AdminSequences.tsx)
- Date de blocage non sauvegardée (ajout champ dateBlocage au schéma sequences)
- Table logsRappels incohérente (suppression colonnes obsolètes, correction enum statut)
**Schémas Drizzle mis à jour :**
- emailTemplates : ajout reset_password à l'enum type
- emailConfig : ajout active, domainVerified, correction apiKey
- sequences : ajout dateBlocage (datetime)
- logsRappels : structure nettoyée et conforme
**Déploiement VPS :**
- Toutes les migrations SQL exécutées avec succès
- Application redémarrée et fonctionnelle
- Historique des rappels prêt à fonctionner (table logsRappels corrigée)
- Ajout de "reset_password" à l'enum du schéma Drizzle (table emailTemplates)
- Migration SQL pour modifier l'enum dans la base de données
- Création du template dans la base de données avec un contenu par défaut approprié
- Mise à jour de la procédure tRPC emailTemplates.upsert pour utiliser les noms de colonnes français (titre, couleurPrincipale, couleurSecondaire, piedDePage)
- Mise à jour de la fonction upsertEmailTemplate dans server/db.ts pour utiliser les noms de colonnes français
- Réécriture complète de AdminEmailTemplates.tsx pour utiliser les noms de colonnes français
- Test complet de l'interface : le template s'affiche correctement, peut être modifié et prévisualisé
- Le template contient les variables {{prenom}}, {{nom}}, {{lienReinitialisation}} pour la réinitialisation de mot de passe
- Total de 9 templates d'emails maintenant disponibles (inscription, teaser, rappel, rappelJ1, rappel3-6, reset_password)
- Renommage questionnaire.nom → questionnaire.titre dans QuestionnaireReponse.tsx, AdminQuestionnaireEdit.tsx, questionnaireScheduler.ts, questionnaireExport.ts
- Correction des requêtes SQL dans questionnaireDb.ts (suppression de la condition complete inexistante)
- Ajout des colonnes valeurMin et valeurMax dans la table questions
- Renommage de la colonne nom en titre dans la table questionnaires
- Correction des 170 erreurs TypeScript liées aux types templateType dans les procédures rappels.create et rappels.update (passage de z.string() à z.enum() avec les valeurs correctes)
- Nettoyage complet des fichiers de migration Drizzle (suppression des 32 anciens fichiers et génération d'une nouvelle migration propre 0000_clean_justice.sql)
- Création du guide utilisateur GUIDE_CONFIGURATION_ATTESTATIONS.md expliquant en détail le nouveau workflow de configuration des attestations (modes de génération et d'envoi, workflows recommandés, bonnes pratiques, dépannage)
- Vérification que tous les paramètres liés aux attestations sont bien accessibles dans la page de gestion des attestations (mode de génération et mode d'envoi)
- Retrait du champ envoiAutomatiqueAttestations du schéma Drizzle
- Suppression de la colonne envoiAutomatiqueAttestations de la table parametres dans la base de données
- Mise à jour de la procédure tRPC parametres.update pour retirer ce champ
- Test complet du workflow : le serveur fonctionne correctement et le tableau de bord s'affiche sans erreur
- Changement du champ 'nom' en 'titre' pour les questionnaires (backend et frontend)
- Suppression de la propriété 'complete' inexistante dans reponsesQuestionnaires
- Correction du type 'provider' dans la configuration email
- Gestion des valeurs null pour le champ 'fonction' des apprenants
- Résolution de l'erreur logsRappels.email après redémarrage du serveur
Fonctionnalités :
- Configuration par formation (génération auto/manuel, envoi auto/manuel)
- Affichage de la liste des apprenants inscrits
- Upload de documents PDF par apprenant (mode manuel)
- Boutons de prévisualisation et d'envoi manuel
- Gestion complète des attestations
Corrections :
- Ajout des colonnes modeAttestation et modeEnvoi dans la table formations
- Ajout des colonnes documentUrl, documentS3Key, uploadedBy, uploadedAt, emailEnvoye, dateEnvoiEmail dans la table attestations
- Correction de l'erreur SQL en retirant la colonne urlPdf inexistante de la requête
Nouvelle fonctionnalité complète pour gérer les attestations :
- Schéma de base de données : colonnes modeAttestation et modeEnvoi dans formations, colonnes documentUrl, documentS3Key, uploadedBy, uploadedAt dans attestations
- Backend : fichier gestionAttestationsDb.ts, procédures tRPC, fonction sendAttestationEmail
- Interface : page AdminGestionAttestations avec sélection de formation, configuration des modes, tableau des apprenants, boutons upload/prévisualisation/envoi/suppression
- Navigation : lien dans menu Gestion, route /admin/gestion-attestations
- Ajout du paramètre d'envoi automatique des attestations dans la page Paramètres
- Ajout de la prévisualisation du modèle d'attestation avec génération PDF
- Création de la page Historique des attestations dans la section Traçabilité
- Création de la table historiqueAttestations pour tracer les envois
- Ajout des procédures tRPC pour l'historique et la prévisualisation
- Modification du schéma rappels : remplacement de nbJours par joursAvant pour correspondre à la structure réelle de la base de données
- Correction des noms de colonnes pour utiliser exactement la même casse que la base de données (templateType, heureEnvoi, nomFichier, urlFichier, s3Key, typeFichier, tailleFichier, derniereExecution)
- La page Rappels fonctionne maintenant correctement et affiche tous les rappels configurés sans erreurs SQL
- Modification du schéma apprenants : codeEtablissement et fonction en VARCHAR nullable (au lieu de NOT NULL et ENUM)
- Suppression de la colonne isActive qui n'existe pas dans la base de données
- Modification du schéma sequences : ajout des statuts "bloquee" et "terminee" dans l'ENUM
- Suppression de la colonne dateBloquage qui n'existe pas dans la base de données
- Le dashboard admin fonctionne maintenant correctement sans erreurs de requêtes SQL