diff --git a/.manus/db/db-query-1768924734065.json b/.manus/db/db-query-1768924734065.json
new file mode 100644
index 0000000..9761483
--- /dev/null
+++ b/.manus/db/db-query-1768924734065.json
@@ -0,0 +1,9 @@
+{
+ "query": "CREATE TABLE IF NOT EXISTS `passwordResetTokens` (\n `id` int AUTO_INCREMENT PRIMARY KEY,\n `email` varchar(320) NOT NULL,\n `token` varchar(255) NOT NULL UNIQUE,\n `expiresAt` timestamp NOT NULL,\n `used` boolean NOT NULL DEFAULT false,\n `createdAt` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP\n);",
+ "command": "mysql --batch --raw --column-names --default-character-set=utf8mb4 --host gateway02.us-east-1.prod.aws.tidbcloud.com --port 4000 --user 4CrrYuB5tme73Qo.root --database 7PAT67UmWoxv8vwp8Bbcv6 --execute CREATE TABLE IF NOT EXISTS `passwordResetTokens` (\n `id` int AUTO_INCREMENT PRIMARY KEY,\n `email` varchar(320) NOT NULL,\n `token` varchar(255) NOT NULL UNIQUE,\n `expiresAt` timestamp NOT NULL,\n `used` boolean NOT NULL DEFAULT false,\n `createdAt` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP\n);",
+ "rows": [],
+ "messages": [],
+ "stdout": "",
+ "stderr": "",
+ "execution_time_ms": 101
+}
\ No newline at end of file
diff --git a/.manus/db/db-query-1768925512631.json b/.manus/db/db-query-1768925512631.json
new file mode 100644
index 0000000..1806f6d
--- /dev/null
+++ b/.manus/db/db-query-1768925512631.json
@@ -0,0 +1,13 @@
+{
+ "query": "SHOW TABLES LIKE 'passwordResetTokens';",
+ "command": "mysql --batch --raw --column-names --default-character-set=utf8mb4 --host gateway02.us-east-1.prod.aws.tidbcloud.com --port 4000 --user 4CrrYuB5tme73Qo.root --database 7PAT67UmWoxv8vwp8Bbcv6 --execute SHOW TABLES LIKE 'passwordResetTokens';",
+ "rows": [
+ {
+ "Tables_in_7PAT67UmWoxv8vwp8Bbcv6 (passwordResetTokens)": "passwordResetTokens"
+ }
+ ],
+ "messages": [],
+ "stdout": "Tables_in_7PAT67UmWoxv8vwp8Bbcv6 (passwordResetTokens)\npasswordResetTokens\n",
+ "stderr": "",
+ "execution_time_ms": 56
+}
\ No newline at end of file
diff --git a/.manus/db/db-query-1768925517071.json b/.manus/db/db-query-1768925517071.json
new file mode 100644
index 0000000..f2959d0
--- /dev/null
+++ b/.manus/db/db-query-1768925517071.json
@@ -0,0 +1,58 @@
+{
+ "query": "DESCRIBE passwordResetTokens;",
+ "command": "mysql --batch --raw --column-names --default-character-set=utf8mb4 --host gateway02.us-east-1.prod.aws.tidbcloud.com --port 4000 --user 4CrrYuB5tme73Qo.root --database 7PAT67UmWoxv8vwp8Bbcv6 --execute DESCRIBE passwordResetTokens;",
+ "rows": [
+ {
+ "Field": "id",
+ "Type": "int(11)",
+ "Null": "NO",
+ "Key": "PRI",
+ "Default": "NULL",
+ "Extra": "auto_increment"
+ },
+ {
+ "Field": "userId",
+ "Type": "int(11)",
+ "Null": "NO",
+ "Key": "",
+ "Default": "NULL",
+ "Extra": ""
+ },
+ {
+ "Field": "token",
+ "Type": "varchar(255)",
+ "Null": "NO",
+ "Key": "UNI",
+ "Default": "NULL",
+ "Extra": ""
+ },
+ {
+ "Field": "expiresAt",
+ "Type": "timestamp",
+ "Null": "NO",
+ "Key": "",
+ "Default": "NULL",
+ "Extra": ""
+ },
+ {
+ "Field": "used",
+ "Type": "tinyint(1)",
+ "Null": "NO",
+ "Key": "",
+ "Default": "0",
+ "Extra": ""
+ },
+ {
+ "Field": "createdAt",
+ "Type": "timestamp",
+ "Null": "NO",
+ "Key": "",
+ "Default": "CURRENT_TIMESTAMP",
+ "Extra": ""
+ }
+ ],
+ "messages": [],
+ "stdout": "Field\tType\tNull\tKey\tDefault\tExtra\nid\tint(11)\tNO\tPRI\tNULL\tauto_increment\nuserId\tint(11)\tNO\t\tNULL\t\ntoken\tvarchar(255)\tNO\tUNI\tNULL\t\nexpiresAt\ttimestamp\tNO\t\tNULL\t\nused\ttinyint(1)\tNO\t\t0\t\ncreatedAt\ttimestamp\tNO\t\tCURRENT_TIMESTAMP\t\n",
+ "stderr": "",
+ "execution_time_ms": 53
+}
\ No newline at end of file
diff --git a/.manus/db/db-query-1768925534190.json b/.manus/db/db-query-1768925534190.json
new file mode 100644
index 0000000..3e3dc2e
--- /dev/null
+++ b/.manus/db/db-query-1768925534190.json
@@ -0,0 +1,18 @@
+{
+ "query": "SELECT * FROM passwordResetTokens LIMIT 1;",
+ "command": "mysql --batch --raw --column-names --default-character-set=utf8mb4 --host gateway02.us-east-1.prod.aws.tidbcloud.com --port 4000 --user 4CrrYuB5tme73Qo.root --database 7PAT67UmWoxv8vwp8Bbcv6 --execute SELECT * FROM passwordResetTokens LIMIT 1;",
+ "rows": [
+ {
+ "id": "1",
+ "userId": "1",
+ "token": "e6626bd6ba7eabccd094178e79e5218050337ae35ae415ed7af4a6e6a83a2cba",
+ "expiresAt": "2025-11-18 21:19:50",
+ "used": "0",
+ "createdAt": "2025-11-17 21:19:50"
+ }
+ ],
+ "messages": [],
+ "stdout": "id\tuserId\ttoken\texpiresAt\tused\tcreatedAt\n1\t1\te6626bd6ba7eabccd094178e79e5218050337ae35ae415ed7af4a6e6a83a2cba\t2025-11-18 21:19:50\t0\t2025-11-17 21:19:50\n",
+ "stderr": "",
+ "execution_time_ms": 57
+}
\ No newline at end of file
diff --git a/.manus/db/db-query-1768925802156.json b/.manus/db/db-query-1768925802156.json
new file mode 100644
index 0000000..b10bef0
--- /dev/null
+++ b/.manus/db/db-query-1768925802156.json
@@ -0,0 +1,24 @@
+{
+ "query": "SHOW CREATE TABLE passwordResetTokens;",
+ "command": "mysql --batch --raw --column-names --default-character-set=utf8mb4 --host gateway02.us-east-1.prod.aws.tidbcloud.com --port 4000 --user 4CrrYuB5tme73Qo.root --database 7PAT67UmWoxv8vwp8Bbcv6 --execute SHOW CREATE TABLE passwordResetTokens;",
+ "rows": [
+ {
+ "Table": "passwordResetTokens",
+ "Create Table": "CREATE TABLE `passwordResetTokens` ("
+ }
+ ],
+ "messages": [
+ " `id` int(11) NOT NULL AUTO_INCREMENT,",
+ " `userId` int(11) NOT NULL,",
+ " `token` varchar(255) NOT NULL,",
+ " `expiresAt` timestamp NOT NULL,",
+ " `used` tinyint(1) NOT NULL DEFAULT '0',",
+ " `createdAt` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,",
+ " PRIMARY KEY (`id`) /*T![clustered_index] CLUSTERED */,",
+ " UNIQUE KEY `passwordResetTokens_token_unique` (`token`)",
+ ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin AUTO_INCREMENT=90001"
+ ],
+ "stdout": "Table\tCreate Table\npasswordResetTokens\tCREATE TABLE `passwordResetTokens` (\n `id` int(11) NOT NULL AUTO_INCREMENT,\n `userId` int(11) NOT NULL,\n `token` varchar(255) NOT NULL,\n `expiresAt` timestamp NOT NULL,\n `used` tinyint(1) NOT NULL DEFAULT '0',\n `createdAt` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n PRIMARY KEY (`id`) /*T![clustered_index] CLUSTERED */,\n UNIQUE KEY `passwordResetTokens_token_unique` (`token`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin AUTO_INCREMENT=90001\n",
+ "stderr": "",
+ "execution_time_ms": 94
+}
\ No newline at end of file
diff --git a/.manus/db/db-query-1768925813992.json b/.manus/db/db-query-1768925813992.json
new file mode 100644
index 0000000..d11d22a
--- /dev/null
+++ b/.manus/db/db-query-1768925813992.json
@@ -0,0 +1,9 @@
+{
+ "query": "ALTER TABLE passwordResetTokens ADD COLUMN email varchar(320) NOT NULL AFTER id;",
+ "command": "mysql --batch --raw --column-names --default-character-set=utf8mb4 --host gateway02.us-east-1.prod.aws.tidbcloud.com --port 4000 --user 4CrrYuB5tme73Qo.root --database 7PAT67UmWoxv8vwp8Bbcv6 --execute ALTER TABLE passwordResetTokens ADD COLUMN email varchar(320) NOT NULL AFTER id;",
+ "rows": [],
+ "messages": [],
+ "stdout": "",
+ "stderr": "",
+ "execution_time_ms": 441
+}
\ No newline at end of file
diff --git a/.manus/db/db-query-1768925878549.json b/.manus/db/db-query-1768925878549.json
new file mode 100644
index 0000000..5a1605e
--- /dev/null
+++ b/.manus/db/db-query-1768925878549.json
@@ -0,0 +1,9 @@
+{
+ "query": "ALTER TABLE passwordResetTokens MODIFY COLUMN userId int(11) NULL;",
+ "command": "mysql --batch --raw --column-names --default-character-set=utf8mb4 --host gateway02.us-east-1.prod.aws.tidbcloud.com --port 4000 --user 4CrrYuB5tme73Qo.root --database 7PAT67UmWoxv8vwp8Bbcv6 --execute ALTER TABLE passwordResetTokens MODIFY COLUMN userId int(11) NULL;",
+ "rows": [],
+ "messages": [],
+ "stdout": "",
+ "stderr": "",
+ "execution_time_ms": 205
+}
\ No newline at end of file
diff --git a/.manus/db/db-query-error-1768925502688.json b/.manus/db/db-query-error-1768925502688.json
new file mode 100644
index 0000000..333ef5a
--- /dev/null
+++ b/.manus/db/db-query-error-1768925502688.json
@@ -0,0 +1,8 @@
+{
+ "query": "INSERT INTO passwordResetTokens (email, token, expiresAt) VALUES ('test@example.com', 'test123456', DATE_ADD(NOW(), INTERVAL 24 HOUR));",
+ "command": "mysql --batch --raw --column-names --default-character-set=utf8mb4 --host gateway02.us-east-1.prod.aws.tidbcloud.com --port 4000 --user 4CrrYuB5tme73Qo.root --database 7PAT67UmWoxv8vwp8Bbcv6 --execute INSERT INTO passwordResetTokens (email, token, expiresAt) VALUES ('test@example.com', 'test123456', DATE_ADD(NOW(), INTERVAL 24 HOUR));",
+ "returncode": 1,
+ "logs": [
+ "ERROR 1054 (42S22) at line 1: Unknown column 'email' in 'field list'"
+ ]
+}
\ No newline at end of file
diff --git a/client/src/index.css b/client/src/index.css
index e720375..e1dd526 100644
--- a/client/src/index.css
+++ b/client/src/index.css
@@ -134,6 +134,12 @@
}
@layer components {
+ /* Classe pour les titres avec charte graphique bleue */
+ .page-title {
+ @apply text-3xl font-bold;
+ color: #6B9FE8;
+ text-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
+ }
/**
* Custom container utility that centers content and adds responsive padding.
*
diff --git a/client/src/pages/AdminAnalytics.tsx b/client/src/pages/AdminAnalytics.tsx
index 5c6e187..62decee 100644
--- a/client/src/pages/AdminAnalytics.tsx
+++ b/client/src/pages/AdminAnalytics.tsx
@@ -148,7 +148,7 @@ export default function AdminAnalytics() {
-
Tableau de bord analytique
+
Tableau de bord analytique
Statistiques et indicateurs de performance des formations
diff --git a/client/src/pages/AdminApprenantDetail.tsx b/client/src/pages/AdminApprenantDetail.tsx
index 1a42d35..0759783 100644
--- a/client/src/pages/AdminApprenantDetail.tsx
+++ b/client/src/pages/AdminApprenantDetail.tsx
@@ -89,7 +89,7 @@ export default function AdminApprenantDetail() {
Retour aux apprenants
-
Détail de l'apprenant
+
Détail de l'apprenant
Consultez toutes les inscriptions de cet apprenant
diff --git a/client/src/pages/AdminApprenants.tsx b/client/src/pages/AdminApprenants.tsx
index 57cf889..d680efb 100644
--- a/client/src/pages/AdminApprenants.tsx
+++ b/client/src/pages/AdminApprenants.tsx
@@ -118,7 +118,7 @@ export default function AdminApprenants() {
-
Apprenants
+
Apprenants
Gérez les apprenants et leurs informations
diff --git a/client/src/pages/AdminCalendrier.tsx b/client/src/pages/AdminCalendrier.tsx
index 58cd1bb..a08e834 100644
--- a/client/src/pages/AdminCalendrier.tsx
+++ b/client/src/pages/AdminCalendrier.tsx
@@ -173,7 +173,7 @@ export default function AdminCalendrier() {
-
Calendrier des séquences
+
Calendrier des séquences
Vue mensuelle de toutes les séquences planifiées
diff --git a/client/src/pages/AdminEmailConfig.tsx b/client/src/pages/AdminEmailConfig.tsx
index 683349c..fefd19d 100644
--- a/client/src/pages/AdminEmailConfig.tsx
+++ b/client/src/pages/AdminEmailConfig.tsx
@@ -106,7 +106,7 @@ export default function AdminEmailConfig() {
-
Configuration des emails
+
Configuration des emails
Configurez l'envoi d'emails via Resend et basculez entre mode simulation et production
diff --git a/client/src/pages/AdminEmailTemplates.tsx b/client/src/pages/AdminEmailTemplates.tsx
index 905a885..81daf24 100644
--- a/client/src/pages/AdminEmailTemplates.tsx
+++ b/client/src/pages/AdminEmailTemplates.tsx
@@ -221,7 +221,7 @@ export default function AdminEmailTemplates() {
return (
-
Templates d'emails
+
Templates d'emails
Personnalisez l'apparence de vos emails automatiques
diff --git a/client/src/pages/AdminEtablissements.tsx b/client/src/pages/AdminEtablissements.tsx
index d778d17..d9a5623 100644
--- a/client/src/pages/AdminEtablissements.tsx
+++ b/client/src/pages/AdminEtablissements.tsx
@@ -16,7 +16,7 @@ export default function AdminEtablissements() {
-
Établissements
+
Établissements
Vue d'ensemble des établissements et de leur participation
diff --git a/client/src/pages/AdminFormations.tsx b/client/src/pages/AdminFormations.tsx
index 858c698..6611efb 100644
--- a/client/src/pages/AdminFormations.tsx
+++ b/client/src/pages/AdminFormations.tsx
@@ -121,7 +121,7 @@ export default function AdminFormations() {
-
Formations
+
Formations
Gérez les formations et leurs liens d'inscription
diff --git a/client/src/pages/AdminGestionAttestations.tsx b/client/src/pages/AdminGestionAttestations.tsx
index b2f617c..67685a3 100644
--- a/client/src/pages/AdminGestionAttestations.tsx
+++ b/client/src/pages/AdminGestionAttestations.tsx
@@ -292,7 +292,7 @@ export default function AdminGestionAttestations() {
-
Gestion des attestations de formation
+
Gestion des attestations de formation
Configurez le mode de génération et d'envoi des attestations pour chaque formation
diff --git a/client/src/pages/AdminImportExcel.tsx b/client/src/pages/AdminImportExcel.tsx
index b486be2..a7049b2 100644
--- a/client/src/pages/AdminImportExcel.tsx
+++ b/client/src/pages/AdminImportExcel.tsx
@@ -111,7 +111,7 @@ export default function AdminImportExcel() {
-
Import Excel - Formations et Séquences
+
Import Excel - Formations et Séquences
Importez plusieurs formations et séquences en une seule fois à partir d'un fichier Excel
diff --git a/client/src/pages/AdminParametres.tsx b/client/src/pages/AdminParametres.tsx
index 6e74993..3a091a7 100644
--- a/client/src/pages/AdminParametres.tsx
+++ b/client/src/pages/AdminParametres.tsx
@@ -104,7 +104,7 @@ export default function AdminParametres() {
-
Paramètres de l'application
+
Paramètres de l'application
Configuration globale de l'application
diff --git a/client/src/pages/AdminQuestionnaireAnalyse.tsx b/client/src/pages/AdminQuestionnaireAnalyse.tsx
index 5b8c285..e423e15 100644
--- a/client/src/pages/AdminQuestionnaireAnalyse.tsx
+++ b/client/src/pages/AdminQuestionnaireAnalyse.tsx
@@ -140,7 +140,7 @@ export default function AdminQuestionnaireAnalyse() {
-
{questionnaire.titre}
+
{questionnaire.titre}
Analyse des réponses
diff --git a/client/src/pages/AdminQuestionnaireEdit.tsx b/client/src/pages/AdminQuestionnaireEdit.tsx
index 4cdcafe..23d1b69 100644
--- a/client/src/pages/AdminQuestionnaireEdit.tsx
+++ b/client/src/pages/AdminQuestionnaireEdit.tsx
@@ -196,7 +196,7 @@ export default function AdminQuestionnaireEdit() {
-
+
{questionnaireId ? "Modifier le questionnaire" : "Nouveau questionnaire"}
diff --git a/client/src/pages/AdminQuestionnaireSuivi.tsx b/client/src/pages/AdminQuestionnaireSuivi.tsx
index 02107b7..e0658e9 100644
--- a/client/src/pages/AdminQuestionnaireSuivi.tsx
+++ b/client/src/pages/AdminQuestionnaireSuivi.tsx
@@ -29,7 +29,7 @@ export default function AdminQuestionnaireSuivi() {
-
Suivi des Questionnaires
+
Suivi des Questionnaires
Tableau de bord de suivi des questionnaires avec taux de réponse et statistiques
diff --git a/client/src/pages/AdminQuestionnaires.tsx b/client/src/pages/AdminQuestionnaires.tsx
index 504b40b..935f944 100644
--- a/client/src/pages/AdminQuestionnaires.tsx
+++ b/client/src/pages/AdminQuestionnaires.tsx
@@ -57,7 +57,7 @@ export default function AdminQuestionnaires() {
-
Questionnaires
+
Questionnaires
Gérez les questionnaires de satisfaction et d'évaluation
diff --git a/client/src/pages/AdminRappels.tsx b/client/src/pages/AdminRappels.tsx
index 988c627..fe4d716 100644
--- a/client/src/pages/AdminRappels.tsx
+++ b/client/src/pages/AdminRappels.tsx
@@ -318,7 +318,7 @@ export default function AdminRappels() {
-
Rappels automatiques
+
Rappels automatiques
Configurez les rappels envoyés automatiquement avant les séquences
diff --git a/client/src/pages/AdminRapportPublicCible.tsx b/client/src/pages/AdminRapportPublicCible.tsx
index 8bdae16..7cbf2d9 100644
--- a/client/src/pages/AdminRapportPublicCible.tsx
+++ b/client/src/pages/AdminRapportPublicCible.tsx
@@ -20,9 +20,15 @@ export default function AdminRapportPublicCible() {
const totalInscrits = sequenceInscriptions.length;
const mismatchCount = sequenceInscriptions.filter((insc: any) => {
- const apprenantFonction = insc.apprenant.fonction;
+ const apprenantFonction = insc.apprenant?.fonction;
const sequencePublicCible = sequence.publicCible;
+ // Si la fonction de l'apprenant est null ou undefined, on considère qu'il n'y a pas de correspondance
+ if (!apprenantFonction) return true;
+
+ // Si le public cible est "Tous", il y a toujours correspondance
+ if (sequencePublicCible === "tous") return false;
+
// Mapper les fonctions aux publics cibles
const fonctionToPublicCible: Record
= {
directeur: "directeur",
@@ -106,7 +112,7 @@ export default function AdminRapportPublicCible() {
-
Rapport d'analyse Public Cible
+
Rapport d'analyse Public Cible
Comparaison entre le public cible des séquences et la fonction des apprenants inscrits
diff --git a/client/src/pages/AdminSequenceInscrits.tsx b/client/src/pages/AdminSequenceInscrits.tsx
index 5203ba0..082f51f 100644
--- a/client/src/pages/AdminSequenceInscrits.tsx
+++ b/client/src/pages/AdminSequenceInscrits.tsx
@@ -309,7 +309,7 @@ export default function AdminSequenceInscrits() {
Retour aux séquences
-
Inscrits à la séquence
+
Inscrits à la séquence
{getFormationName()} - {sequence.nom}
diff --git a/client/src/pages/AdminSequences.tsx b/client/src/pages/AdminSequences.tsx
index 757a3b3..79d5d36 100644
--- a/client/src/pages/AdminSequences.tsx
+++ b/client/src/pages/AdminSequences.tsx
@@ -373,7 +373,7 @@ export default function AdminSequences() {
-
Gestion des Séquences
+
Gestion des Séquences
Créez et gérez les séquences de formation avec leurs dates
diff --git a/client/src/pages/AdminUsers.tsx b/client/src/pages/AdminUsers.tsx
index d6c57ec..3e7c214 100644
--- a/client/src/pages/AdminUsers.tsx
+++ b/client/src/pages/AdminUsers.tsx
@@ -239,7 +239,7 @@ export default function AdminUsers() {
-
Gestion des Utilisateurs
+
Gestion des Utilisateurs
Gérez les comptes utilisateurs de l'application
diff --git a/client/src/pages/ComponentShowcase.tsx b/client/src/pages/ComponentShowcase.tsx
index 603659d..a362d0e 100644
--- a/client/src/pages/ComponentShowcase.tsx
+++ b/client/src/pages/ComponentShowcase.tsx
@@ -230,7 +230,7 @@ export default function ComponentsShowcase() {
-
+
Shadcn/ui Component Library
diff --git a/client/src/pages/FormateurDashboard.tsx b/client/src/pages/FormateurDashboard.tsx
index 5c802d1..69a2cf2 100644
--- a/client/src/pages/FormateurDashboard.tsx
+++ b/client/src/pages/FormateurDashboard.tsx
@@ -36,7 +36,7 @@ export default function FormateurDashboard() {
{/* En-tête */}
-
Tableau de bord formateur
+
Tableau de bord formateur
Vue d'ensemble de vos formations et prochaines interventions
diff --git a/client/src/pages/FormateurEmargement.tsx b/client/src/pages/FormateurEmargement.tsx
index 3dc01f4..0ac5772 100644
--- a/client/src/pages/FormateurEmargement.tsx
+++ b/client/src/pages/FormateurEmargement.tsx
@@ -119,7 +119,7 @@ export default function FormateurEmargement() {
-
Émargement numérique
+
Émargement numérique
Gérez les présences de vos apprenants avec le QR code
diff --git a/client/src/pages/HistoriqueAttestations.tsx b/client/src/pages/HistoriqueAttestations.tsx
index a784489..e10870b 100644
--- a/client/src/pages/HistoriqueAttestations.tsx
+++ b/client/src/pages/HistoriqueAttestations.tsx
@@ -28,7 +28,7 @@ export default function HistoriqueAttestations() {
-
Historique des attestations
+
Historique des attestations
Suivi de tous les envois d'attestations de formation
diff --git a/client/src/pages/Home.tsx b/client/src/pages/Home.tsx
index 63b9b58..cf1e339 100644
--- a/client/src/pages/Home.tsx
+++ b/client/src/pages/Home.tsx
@@ -113,7 +113,7 @@ export default function Home() {
{/* Features Grid */}
-
Fonctionnalités principales
+
Fonctionnalités principales
{features.map((feature, index) => (
@@ -135,7 +135,7 @@ export default function Home() {
{/* How it works */}
-
Comment ça fonctionne ?
+
Comment ça fonctionne ?
diff --git a/client/src/pages/Inscription.tsx b/client/src/pages/Inscription.tsx
index bb0ded0..bbb2db9 100644
--- a/client/src/pages/Inscription.tsx
+++ b/client/src/pages/Inscription.tsx
@@ -229,7 +229,6 @@ export default function Inscription() {
Vous recevrez un email de confirmation avec tous les détails
Une invitation Outlook sera envoyée pour bloquer votre agenda
- Un email de rappel vous sera envoyé 7 jours avant la formation
diff --git a/client/src/pages/LoginChoice.tsx b/client/src/pages/LoginChoice.tsx
index 0221c92..4b46028 100644
--- a/client/src/pages/LoginChoice.tsx
+++ b/client/src/pages/LoginChoice.tsx
@@ -47,7 +47,7 @@ export default function LoginChoice() {
-
{APP_TITLE}
+
{APP_TITLE}
Choisissez votre méthode de connexion
diff --git a/client/src/pages/QuestionnaireReponse.tsx b/client/src/pages/QuestionnaireReponse.tsx
index c182f71..1a1b8d7 100644
--- a/client/src/pages/QuestionnaireReponse.tsx
+++ b/client/src/pages/QuestionnaireReponse.tsx
@@ -113,7 +113,7 @@ export default function QuestionnaireReponse() {
{APP_LOGO && (
)}
-
{data.questionnaire?.titre || 'Questionnaire'}
+
{data.questionnaire?.titre || 'Questionnaire'}
{data.questionnaire?.description && (
{data.questionnaire.description}
)}
diff --git a/client/src/pages/admin/AdminAttestations.tsx b/client/src/pages/admin/AdminAttestations.tsx
index 1d17fd0..5258231 100644
--- a/client/src/pages/admin/AdminAttestations.tsx
+++ b/client/src/pages/admin/AdminAttestations.tsx
@@ -70,7 +70,7 @@ export default function AdminAttestations() {
-
Configuration des attestations
+
Configuration des attestations
Personnalisez le contenu et la signature des attestations de formation
diff --git a/client/src/pages/admin/AdminFail2Ban.tsx b/client/src/pages/admin/AdminFail2Ban.tsx
index b3ee10f..6b326bf 100644
--- a/client/src/pages/admin/AdminFail2Ban.tsx
+++ b/client/src/pages/admin/AdminFail2Ban.tsx
@@ -65,7 +65,7 @@ export default function AdminFail2Ban() {
-
Monitoring Fail2Ban
+
Monitoring Fail2Ban
Surveillance des tentatives d'intrusion SSH
diff --git a/client/src/pages/admin/AdminRappelsHistorique.tsx b/client/src/pages/admin/AdminRappelsHistorique.tsx
index 4997a93..6a3b025 100644
--- a/client/src/pages/admin/AdminRappelsHistorique.tsx
+++ b/client/src/pages/admin/AdminRappelsHistorique.tsx
@@ -87,7 +87,7 @@ export default function AdminRappelsHistorique() {
-
Historique des Rappels
+
Historique des Rappels
Consultez tous les rappels envoyés avec filtres avancés
diff --git a/client/src/pages/admin/AdminRappelsStats.tsx b/client/src/pages/admin/AdminRappelsStats.tsx
index d1f3424..c42bea4 100644
--- a/client/src/pages/admin/AdminRappelsStats.tsx
+++ b/client/src/pages/admin/AdminRappelsStats.tsx
@@ -29,7 +29,7 @@ export default function AdminRappelsStats() {
-
Statistiques des Rappels
+
Statistiques des Rappels
Analysez les performances du système de rappels
diff --git a/client/src/pages/formateur/FormateurDashboard.tsx b/client/src/pages/formateur/FormateurDashboard.tsx
index 8f3d245..9e640de 100644
--- a/client/src/pages/formateur/FormateurDashboard.tsx
+++ b/client/src/pages/formateur/FormateurDashboard.tsx
@@ -56,7 +56,7 @@ export default function FormateurDashboard() {
-
Mon Calendrier
+
Mon Calendrier
Vos interventions à venir
diff --git a/client/src/pages/formateur/FormateurHistorique.tsx b/client/src/pages/formateur/FormateurHistorique.tsx
index ba958f1..79497ce 100644
--- a/client/src/pages/formateur/FormateurHistorique.tsx
+++ b/client/src/pages/formateur/FormateurHistorique.tsx
@@ -40,7 +40,7 @@ export default function FormateurHistorique() {
-
Historique de mes formations
+
Historique de mes formations
Toutes vos formations passées
diff --git a/client/src/pages/formateur/FormateurSequence.tsx b/client/src/pages/formateur/FormateurSequence.tsx
index 159efa1..ba7ff20 100644
--- a/client/src/pages/formateur/FormateurSequence.tsx
+++ b/client/src/pages/formateur/FormateurSequence.tsx
@@ -141,7 +141,7 @@ export default function FormateurSequence() {
{/* En-tête */}
-
{sequence.nom}
+
{sequence.nom}
{sequence.formationNom}
diff --git a/docs/screenshot-titres-bleus.md b/docs/screenshot-titres-bleus.md
new file mode 100644
index 0000000..64760ef
--- /dev/null
+++ b/docs/screenshot-titres-bleus.md
@@ -0,0 +1,43 @@
+# Harmonisation des titres - Charte graphique bleue
+
+Tous les titres des pages ont été harmonisés avec la charte graphique du "Tableau de bord" :
+- **Couleur** : Bleu clair (#6B9FE8)
+- **Ombre** : Ombre portée subtile (0 2px 4px rgba(0, 0, 0, 0.1))
+- **Police** : 3xl, bold
+
+## Pages modifiées (32 au total)
+
+- Tableau de bord
+- Formations
+- Séquences
+- Apprenants
+- Utilisateurs
+- Formateurs
+- Calendrier
+- Émargement
+- Gestion des attestations
+- Configuration des emails
+- Templates d'emails
+- Établissements
+- Import Excel
+- Paramètres
+- Questionnaires
+- Suivi des questionnaires
+- Rappels automatiques
+- Rapport Public Cible
+- Analytics
+- Et toutes les autres pages...
+
+## Implémentation
+
+Une classe CSS réutilisable `.page-title` a été créée dans `client/src/index.css` :
+
+```css
+.page-title {
+ @apply text-3xl font-bold;
+ color: #6B9FE8;
+ text-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
+}
+```
+
+Tous les titres `
` et `` ont été mis à jour pour utiliser cette classe.
diff --git a/drizzle/meta/0003_snapshot.json b/drizzle/meta/0003_snapshot.json
new file mode 100644
index 0000000..de20256
--- /dev/null
+++ b/drizzle/meta/0003_snapshot.json
@@ -0,0 +1,2163 @@
+{
+ "version": "5",
+ "dialect": "mysql",
+ "id": "88480634-882a-482c-92e4-aba1d95c1155",
+ "prevId": "a451f0fc-75be-459b-9bfe-acfb5226ed56",
+ "tables": {
+ "apprenants": {
+ "name": "apprenants",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "int",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": true
+ },
+ "nom": {
+ "name": "nom",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "prenom": {
+ "name": "prenom",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "email": {
+ "name": "email",
+ "type": "varchar(320)",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "codeEtablissement": {
+ "name": "codeEtablissement",
+ "type": "varchar(50)",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "fonction": {
+ "name": "fonction",
+ "type": "varchar(100)",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "createdAt": {
+ "name": "createdAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "(now())"
+ },
+ "updatedAt": {
+ "name": "updatedAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "onUpdate": true,
+ "default": "(now())"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {
+ "apprenants_id": {
+ "name": "apprenants_id",
+ "columns": [
+ "id"
+ ]
+ }
+ },
+ "uniqueConstraints": {
+ "apprenants_email_unique": {
+ "name": "apprenants_email_unique",
+ "columns": [
+ "email"
+ ]
+ }
+ },
+ "checkConstraint": {}
+ },
+ "attestations": {
+ "name": "attestations",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "int",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": true
+ },
+ "apprenantId": {
+ "name": "apprenantId",
+ "type": "int",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "sequenceId": {
+ "name": "sequenceId",
+ "type": "int",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "inscriptionId": {
+ "name": "inscriptionId",
+ "type": "int",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "urlPdf": {
+ "name": "urlPdf",
+ "type": "varchar(500)",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "s3Key": {
+ "name": "s3Key",
+ "type": "varchar(500)",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "documentUrl": {
+ "name": "documentUrl",
+ "type": "varchar(500)",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "documentS3Key": {
+ "name": "documentS3Key",
+ "type": "varchar(500)",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "uploadedBy": {
+ "name": "uploadedBy",
+ "type": "int",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "uploadedAt": {
+ "name": "uploadedAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "emailEnvoye": {
+ "name": "emailEnvoye",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": false
+ },
+ "dateEnvoiEmail": {
+ "name": "dateEnvoiEmail",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "dateGeneration": {
+ "name": "dateGeneration",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "(now())"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {
+ "attestations_id": {
+ "name": "attestations_id",
+ "columns": [
+ "id"
+ ]
+ }
+ },
+ "uniqueConstraints": {},
+ "checkConstraint": {}
+ },
+ "configAttestation": {
+ "name": "configAttestation",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "int",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": true
+ },
+ "texteAttestation": {
+ "name": "texteAttestation",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "nomSignataire": {
+ "name": "nomSignataire",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "fonctionSignataire": {
+ "name": "fonctionSignataire",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "logoUrl": {
+ "name": "logoUrl",
+ "type": "varchar(500)",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "logoS3Key": {
+ "name": "logoS3Key",
+ "type": "varchar(500)",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "signatureUrl": {
+ "name": "signatureUrl",
+ "type": "varchar(500)",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "signatureS3Key": {
+ "name": "signatureS3Key",
+ "type": "varchar(500)",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "updatedAt": {
+ "name": "updatedAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "onUpdate": true,
+ "default": "(now())"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {
+ "configAttestation_id": {
+ "name": "configAttestation_id",
+ "columns": [
+ "id"
+ ]
+ }
+ },
+ "uniqueConstraints": {},
+ "checkConstraint": {}
+ },
+ "datesFormation": {
+ "name": "datesFormation",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "int",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": true
+ },
+ "sequenceId": {
+ "name": "sequenceId",
+ "type": "int",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "ordre": {
+ "name": "ordre",
+ "type": "int",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "dateDebut": {
+ "name": "dateDebut",
+ "type": "datetime",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "dateFin": {
+ "name": "dateFin",
+ "type": "datetime",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "createdAt": {
+ "name": "createdAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "(now())"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {
+ "datesFormation_id": {
+ "name": "datesFormation_id",
+ "columns": [
+ "id"
+ ]
+ }
+ },
+ "uniqueConstraints": {},
+ "checkConstraint": {}
+ },
+ "emailConfig": {
+ "name": "emailConfig",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "int",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": true
+ },
+ "provider": {
+ "name": "provider",
+ "type": "enum('resend','smtp','simulation')",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "'simulation'"
+ },
+ "apiKey": {
+ "name": "apiKey",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "smtpHost": {
+ "name": "smtpHost",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "smtpPort": {
+ "name": "smtpPort",
+ "type": "int",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "smtpSecure": {
+ "name": "smtpSecure",
+ "type": "enum('none','tls','ssl')",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "smtpUser": {
+ "name": "smtpUser",
+ "type": "varchar(320)",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "smtpPassword": {
+ "name": "smtpPassword",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "fromEmail": {
+ "name": "fromEmail",
+ "type": "varchar(320)",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "fromName": {
+ "name": "fromName",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "'Formation Manager Itinova'"
+ },
+ "mode": {
+ "name": "mode",
+ "type": "enum('simulation','production')",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "'simulation'"
+ },
+ "domainVerified": {
+ "name": "domainVerified",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": false
+ },
+ "active": {
+ "name": "active",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": true
+ },
+ "createdAt": {
+ "name": "createdAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "(now())"
+ },
+ "updatedAt": {
+ "name": "updatedAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "onUpdate": true,
+ "default": "(now())"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {
+ "emailConfig_id": {
+ "name": "emailConfig_id",
+ "columns": [
+ "id"
+ ]
+ }
+ },
+ "uniqueConstraints": {},
+ "checkConstraint": {}
+ },
+ "emailTemplates": {
+ "name": "emailTemplates",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "int",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": true
+ },
+ "type": {
+ "name": "type",
+ "type": "enum('inscription','teaser','rappel1','rappel2','rappel3','rappel4','rappel5','rappel6','reset_password')",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "titre": {
+ "name": "titre",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "bodyContent": {
+ "name": "bodyContent",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "couleurPrincipale": {
+ "name": "couleurPrincipale",
+ "type": "varchar(7)",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "'#283581'"
+ },
+ "couleurSecondaire": {
+ "name": "couleurSecondaire",
+ "type": "varchar(7)",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "'#0578BE'"
+ },
+ "piedDePage": {
+ "name": "piedDePage",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "createdAt": {
+ "name": "createdAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "(now())"
+ },
+ "updatedAt": {
+ "name": "updatedAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "onUpdate": true,
+ "default": "(now())"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {
+ "emailTemplates_id": {
+ "name": "emailTemplates_id",
+ "columns": [
+ "id"
+ ]
+ }
+ },
+ "uniqueConstraints": {
+ "emailTemplates_type_unique": {
+ "name": "emailTemplates_type_unique",
+ "columns": [
+ "type"
+ ]
+ }
+ },
+ "checkConstraint": {}
+ },
+ "envoisQuestionnaires": {
+ "name": "envoisQuestionnaires",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "int",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": true
+ },
+ "questionnaireId": {
+ "name": "questionnaireId",
+ "type": "int",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "sequenceId": {
+ "name": "sequenceId",
+ "type": "int",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "apprenantId": {
+ "name": "apprenantId",
+ "type": "int",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "token": {
+ "name": "token",
+ "type": "varchar(100)",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "dateEnvoi": {
+ "name": "dateEnvoi",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "(now())"
+ },
+ "dateReponse": {
+ "name": "dateReponse",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "statut": {
+ "name": "statut",
+ "type": "enum('envoye','repondu')",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "'envoye'"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {
+ "envoisQuestionnaires_id": {
+ "name": "envoisQuestionnaires_id",
+ "columns": [
+ "id"
+ ]
+ }
+ },
+ "uniqueConstraints": {
+ "envoisQuestionnaires_token_unique": {
+ "name": "envoisQuestionnaires_token_unique",
+ "columns": [
+ "token"
+ ]
+ }
+ },
+ "checkConstraint": {}
+ },
+ "formateurs": {
+ "name": "formateurs",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "int",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": true
+ },
+ "nom": {
+ "name": "nom",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "email": {
+ "name": "email",
+ "type": "varchar(320)",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "createdAt": {
+ "name": "createdAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "(now())"
+ },
+ "updatedAt": {
+ "name": "updatedAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "onUpdate": true,
+ "default": "(now())"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {
+ "formateurs_id": {
+ "name": "formateurs_id",
+ "columns": [
+ "id"
+ ]
+ }
+ },
+ "uniqueConstraints": {},
+ "checkConstraint": {}
+ },
+ "formations": {
+ "name": "formations",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "int",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": true
+ },
+ "nom": {
+ "name": "nom",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "lienUnique": {
+ "name": "lienUnique",
+ "type": "varchar(100)",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "actif": {
+ "name": "actif",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": true
+ },
+ "modeAttestation": {
+ "name": "modeAttestation",
+ "type": "enum('auto','manuel')",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "'auto'"
+ },
+ "modeEnvoi": {
+ "name": "modeEnvoi",
+ "type": "enum('auto','manuel')",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "'manuel'"
+ },
+ "createdAt": {
+ "name": "createdAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "(now())"
+ },
+ "updatedAt": {
+ "name": "updatedAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "onUpdate": true,
+ "default": "(now())"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {
+ "formations_id": {
+ "name": "formations_id",
+ "columns": [
+ "id"
+ ]
+ }
+ },
+ "uniqueConstraints": {
+ "formations_lienUnique_unique": {
+ "name": "formations_lienUnique_unique",
+ "columns": [
+ "lienUnique"
+ ]
+ }
+ },
+ "checkConstraint": {}
+ },
+ "historiqueAttestations": {
+ "name": "historiqueAttestations",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "int",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": true
+ },
+ "sequenceId": {
+ "name": "sequenceId",
+ "type": "int",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "apprenantId": {
+ "name": "apprenantId",
+ "type": "int",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "dateEnvoi": {
+ "name": "dateEnvoi",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "(now())"
+ },
+ "statut": {
+ "name": "statut",
+ "type": "enum('envoye','erreur')",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "messageErreur": {
+ "name": "messageErreur",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "urlAttestation": {
+ "name": "urlAttestation",
+ "type": "varchar(500)",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "createdAt": {
+ "name": "createdAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "(now())"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {
+ "historiqueAttestations_id": {
+ "name": "historiqueAttestations_id",
+ "columns": [
+ "id"
+ ]
+ }
+ },
+ "uniqueConstraints": {},
+ "checkConstraint": {}
+ },
+ "historiqueParametres": {
+ "name": "historiqueParametres",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "int",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": true
+ },
+ "userId": {
+ "name": "userId",
+ "type": "int",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "userName": {
+ "name": "userName",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "champModifie": {
+ "name": "champModifie",
+ "type": "varchar(100)",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "ancienneValeur": {
+ "name": "ancienneValeur",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "nouvelleValeur": {
+ "name": "nouvelleValeur",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "dateModification": {
+ "name": "dateModification",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "(now())"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {
+ "historiqueParametres_id": {
+ "name": "historiqueParametres_id",
+ "columns": [
+ "id"
+ ]
+ }
+ },
+ "uniqueConstraints": {},
+ "checkConstraint": {}
+ },
+ "inscriptions": {
+ "name": "inscriptions",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "int",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": true
+ },
+ "sequenceId": {
+ "name": "sequenceId",
+ "type": "int",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "apprenantId": {
+ "name": "apprenantId",
+ "type": "int",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "statut": {
+ "name": "statut",
+ "type": "enum('confirmee','en_attente','annulee')",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "'confirmee'"
+ },
+ "dateInscription": {
+ "name": "dateInscription",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "(now())"
+ },
+ "updatedAt": {
+ "name": "updatedAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "onUpdate": true,
+ "default": "(now())"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {
+ "inscriptions_id": {
+ "name": "inscriptions_id",
+ "columns": [
+ "id"
+ ]
+ }
+ },
+ "uniqueConstraints": {},
+ "checkConstraint": {}
+ },
+ "logsNotifications": {
+ "name": "logsNotifications",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "int",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": true
+ },
+ "type": {
+ "name": "type",
+ "type": "enum('inscription','annulation','capacite_atteinte','place_disponible','remerciement','formateur_inscription','formateur_annulation')",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "destinataire": {
+ "name": "destinataire",
+ "type": "varchar(320)",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "sequenceId": {
+ "name": "sequenceId",
+ "type": "int",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "apprenantId": {
+ "name": "apprenantId",
+ "type": "int",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "statut": {
+ "name": "statut",
+ "type": "enum('succes','echec')",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "messageErreur": {
+ "name": "messageErreur",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "dateEnvoi": {
+ "name": "dateEnvoi",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "(now())"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {
+ "logsNotifications_id": {
+ "name": "logsNotifications_id",
+ "columns": [
+ "id"
+ ]
+ }
+ },
+ "uniqueConstraints": {},
+ "checkConstraint": {}
+ },
+ "logsrappels": {
+ "name": "logsrappels",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "int",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": true
+ },
+ "rappelId": {
+ "name": "rappelId",
+ "type": "int",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "sequenceId": {
+ "name": "sequenceId",
+ "type": "int",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "apprenantId": {
+ "name": "apprenantId",
+ "type": "int",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "email": {
+ "name": "email",
+ "type": "varchar(320)",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "type": {
+ "name": "type",
+ "type": "enum('rappel1','rappel2','rappel3','rappel4','rappel5','rappel6')",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "typeEnvoi": {
+ "name": "typeEnvoi",
+ "type": "enum('automatique','test')",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "'automatique'"
+ },
+ "statut": {
+ "name": "statut",
+ "type": "enum('succes','echec')",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "messageErreur": {
+ "name": "messageErreur",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "nbTentatives": {
+ "name": "nbTentatives",
+ "type": "int",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": 1
+ },
+ "prochainEssai": {
+ "name": "prochainEssai",
+ "type": "datetime",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "dateEnvoi": {
+ "name": "dateEnvoi",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "(now())"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {
+ "logsrappels_id": {
+ "name": "logsrappels_id",
+ "columns": [
+ "id"
+ ]
+ }
+ },
+ "uniqueConstraints": {},
+ "checkConstraint": {}
+ },
+ "parametres": {
+ "name": "parametres",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "int",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": true
+ },
+ "urlPublique": {
+ "name": "urlPublique",
+ "type": "varchar(500)",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "'https://formations.itinova.org'"
+ },
+ "delaiExpirationQR": {
+ "name": "delaiExpirationQR",
+ "type": "int",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": 30
+ },
+ "dureeValiditeToken": {
+ "name": "dureeValiditeToken",
+ "type": "int",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": 24
+ },
+ "notificationsActives": {
+ "name": "notificationsActives",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": true
+ },
+ "updatedAt": {
+ "name": "updatedAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "onUpdate": true,
+ "default": "(now())"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {
+ "parametres_id": {
+ "name": "parametres_id",
+ "columns": [
+ "id"
+ ]
+ }
+ },
+ "uniqueConstraints": {},
+ "checkConstraint": {}
+ },
+ "passwordResetTokens": {
+ "name": "passwordResetTokens",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "int",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": true
+ },
+ "email": {
+ "name": "email",
+ "type": "varchar(320)",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "token": {
+ "name": "token",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "expiresAt": {
+ "name": "expiresAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "used": {
+ "name": "used",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": false
+ },
+ "createdAt": {
+ "name": "createdAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "(now())"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {
+ "passwordResetTokens_id": {
+ "name": "passwordResetTokens_id",
+ "columns": [
+ "id"
+ ]
+ }
+ },
+ "uniqueConstraints": {
+ "passwordResetTokens_token_unique": {
+ "name": "passwordResetTokens_token_unique",
+ "columns": [
+ "token"
+ ]
+ }
+ },
+ "checkConstraint": {}
+ },
+ "presences": {
+ "name": "presences",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "int",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": true
+ },
+ "inscriptionId": {
+ "name": "inscriptionId",
+ "type": "int",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "dateFormationId": {
+ "name": "dateFormationId",
+ "type": "int",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "heurePresence": {
+ "name": "heurePresence",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "(now())"
+ },
+ "modeValidation": {
+ "name": "modeValidation",
+ "type": "enum('qrcode','manuel')",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "validateurId": {
+ "name": "validateurId",
+ "type": "int",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "createdAt": {
+ "name": "createdAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "(now())"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {
+ "presences_id": {
+ "name": "presences_id",
+ "columns": [
+ "id"
+ ]
+ }
+ },
+ "uniqueConstraints": {},
+ "checkConstraint": {}
+ },
+ "questionnaires": {
+ "name": "questionnaires",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "int",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": true
+ },
+ "titre": {
+ "name": "titre",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "type": {
+ "name": "type",
+ "type": "enum('satisfaction','evaluation_pre','evaluation_post')",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "actif": {
+ "name": "actif",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": true
+ },
+ "createdAt": {
+ "name": "createdAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "(now())"
+ },
+ "updatedAt": {
+ "name": "updatedAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "onUpdate": true,
+ "default": "(now())"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {
+ "questionnaires_id": {
+ "name": "questionnaires_id",
+ "columns": [
+ "id"
+ ]
+ }
+ },
+ "uniqueConstraints": {},
+ "checkConstraint": {}
+ },
+ "questions": {
+ "name": "questions",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "int",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": true
+ },
+ "questionnaireId": {
+ "name": "questionnaireId",
+ "type": "int",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "texte": {
+ "name": "texte",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "typeQuestion": {
+ "name": "typeQuestion",
+ "type": "enum('choix_multiple','echelle','texte_libre','oui_non')",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "options": {
+ "name": "options",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "valeurMin": {
+ "name": "valeurMin",
+ "type": "int",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "valeurMax": {
+ "name": "valeurMax",
+ "type": "int",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "obligatoire": {
+ "name": "obligatoire",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": false
+ },
+ "ordre": {
+ "name": "ordre",
+ "type": "int",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "createdAt": {
+ "name": "createdAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "(now())"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {
+ "questions_id": {
+ "name": "questions_id",
+ "columns": [
+ "id"
+ ]
+ }
+ },
+ "uniqueConstraints": {},
+ "checkConstraint": {}
+ },
+ "rappelDates": {
+ "name": "rappelDates",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "int",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": true
+ },
+ "rappelId": {
+ "name": "rappelId",
+ "type": "int",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "dateFormationId": {
+ "name": "dateFormationId",
+ "type": "int",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "createdAt": {
+ "name": "createdAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "(now())"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {
+ "rappelDates_id": {
+ "name": "rappelDates_id",
+ "columns": [
+ "id"
+ ]
+ }
+ },
+ "uniqueConstraints": {},
+ "checkConstraint": {}
+ },
+ "rappels": {
+ "name": "rappels",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "int",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": true
+ },
+ "nom": {
+ "name": "nom",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "templateType": {
+ "name": "templateType",
+ "type": "enum('rappel1','rappel2','rappel3','rappel4','rappel5','rappel6')",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "joursAvant": {
+ "name": "joursAvant",
+ "type": "int",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "heureEnvoi": {
+ "name": "heureEnvoi",
+ "type": "varchar(5)",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "'09:00'"
+ },
+ "timing": {
+ "name": "timing",
+ "type": "enum('pre_formation','post_formation')",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "'pre_formation'"
+ },
+ "nomFichier": {
+ "name": "nomFichier",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "urlFichier": {
+ "name": "urlFichier",
+ "type": "varchar(500)",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "s3Key": {
+ "name": "s3Key",
+ "type": "varchar(500)",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "typeFichier": {
+ "name": "typeFichier",
+ "type": "varchar(100)",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "tailleFichier": {
+ "name": "tailleFichier",
+ "type": "int",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "actif": {
+ "name": "actif",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": true
+ },
+ "derniereExecution": {
+ "name": "derniereExecution",
+ "type": "datetime",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "createdAt": {
+ "name": "createdAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "(now())"
+ },
+ "updatedAt": {
+ "name": "updatedAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "onUpdate": true,
+ "default": "(now())"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {
+ "rappels_id": {
+ "name": "rappels_id",
+ "columns": [
+ "id"
+ ]
+ }
+ },
+ "uniqueConstraints": {},
+ "checkConstraint": {}
+ },
+ "reponsesQuestionnaires": {
+ "name": "reponsesQuestionnaires",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "int",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": true
+ },
+ "questionnaireId": {
+ "name": "questionnaireId",
+ "type": "int",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "sequenceId": {
+ "name": "sequenceId",
+ "type": "int",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "apprenantId": {
+ "name": "apprenantId",
+ "type": "int",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "dateReponse": {
+ "name": "dateReponse",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "(now())"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {
+ "reponsesQuestionnaires_id": {
+ "name": "reponsesQuestionnaires_id",
+ "columns": [
+ "id"
+ ]
+ }
+ },
+ "uniqueConstraints": {},
+ "checkConstraint": {}
+ },
+ "reponsesQuestions": {
+ "name": "reponsesQuestions",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "int",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": true
+ },
+ "reponseQuestionnaireId": {
+ "name": "reponseQuestionnaireId",
+ "type": "int",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "questionId": {
+ "name": "questionId",
+ "type": "int",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "reponseTexte": {
+ "name": "reponseTexte",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "reponseNumerique": {
+ "name": "reponseNumerique",
+ "type": "int",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "createdAt": {
+ "name": "createdAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "(now())"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {
+ "reponsesQuestions_id": {
+ "name": "reponsesQuestions_id",
+ "columns": [
+ "id"
+ ]
+ }
+ },
+ "uniqueConstraints": {},
+ "checkConstraint": {}
+ },
+ "sequences": {
+ "name": "sequences",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "int",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": true
+ },
+ "formationId": {
+ "name": "formationId",
+ "type": "int",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "nom": {
+ "name": "nom",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "lieu": {
+ "name": "lieu",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "capaciteMax": {
+ "name": "capaciteMax",
+ "type": "int",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "statut": {
+ "name": "statut",
+ "type": "enum('ouverte','bloquee','terminee','fermee','annulee')",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "'ouverte'"
+ },
+ "publicCible": {
+ "name": "publicCible",
+ "type": "enum('directeur','chef_service','autre','tous')",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "'tous'"
+ },
+ "formateurId": {
+ "name": "formateurId",
+ "type": "int",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "dateBlocage": {
+ "name": "dateBlocage",
+ "type": "datetime",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "qrCodeToken": {
+ "name": "qrCodeToken",
+ "type": "varchar(100)",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "createdAt": {
+ "name": "createdAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "(now())"
+ },
+ "updatedAt": {
+ "name": "updatedAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "onUpdate": true,
+ "default": "(now())"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {
+ "sequences_id": {
+ "name": "sequences_id",
+ "columns": [
+ "id"
+ ]
+ }
+ },
+ "uniqueConstraints": {
+ "sequences_qrCodeToken_unique": {
+ "name": "sequences_qrCodeToken_unique",
+ "columns": [
+ "qrCodeToken"
+ ]
+ }
+ },
+ "checkConstraint": {}
+ },
+ "supportsFormation": {
+ "name": "supportsFormation",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "int",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": true
+ },
+ "sequenceId": {
+ "name": "sequenceId",
+ "type": "int",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "formateurId": {
+ "name": "formateurId",
+ "type": "int",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "nomFichier": {
+ "name": "nomFichier",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "typeFichier": {
+ "name": "typeFichier",
+ "type": "varchar(100)",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "tailleFichier": {
+ "name": "tailleFichier",
+ "type": "int",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "urlFichier": {
+ "name": "urlFichier",
+ "type": "varchar(500)",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "s3Key": {
+ "name": "s3Key",
+ "type": "varchar(500)",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "createdAt": {
+ "name": "createdAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "(now())"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {
+ "supportsFormation_id": {
+ "name": "supportsFormation_id",
+ "columns": [
+ "id"
+ ]
+ }
+ },
+ "uniqueConstraints": {},
+ "checkConstraint": {}
+ },
+ "users": {
+ "name": "users",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "int",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": true
+ },
+ "openId": {
+ "name": "openId",
+ "type": "varchar(64)",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "email": {
+ "name": "email",
+ "type": "varchar(320)",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "username": {
+ "name": "username",
+ "type": "varchar(100)",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "password": {
+ "name": "password",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "loginMethod": {
+ "name": "loginMethod",
+ "type": "varchar(64)",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "role": {
+ "name": "role",
+ "type": "enum('user','admin','formateur')",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "'user'"
+ },
+ "isActive": {
+ "name": "isActive",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": true
+ },
+ "formateurId": {
+ "name": "formateurId",
+ "type": "int",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "createdAt": {
+ "name": "createdAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "(now())"
+ },
+ "updatedAt": {
+ "name": "updatedAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "onUpdate": true,
+ "default": "(now())"
+ },
+ "lastSignedIn": {
+ "name": "lastSignedIn",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "(now())"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {
+ "users_id": {
+ "name": "users_id",
+ "columns": [
+ "id"
+ ]
+ }
+ },
+ "uniqueConstraints": {
+ "users_openId_unique": {
+ "name": "users_openId_unique",
+ "columns": [
+ "openId"
+ ]
+ },
+ "users_username_unique": {
+ "name": "users_username_unique",
+ "columns": [
+ "username"
+ ]
+ }
+ },
+ "checkConstraint": {}
+ }
+ },
+ "views": {},
+ "_meta": {
+ "schemas": {},
+ "tables": {},
+ "columns": {}
+ },
+ "internal": {
+ "tables": {},
+ "indexes": {}
+ }
+}
\ No newline at end of file
diff --git a/drizzle/meta/_journal.json b/drizzle/meta/_journal.json
index 8561228..25c7846 100644
--- a/drizzle/meta/_journal.json
+++ b/drizzle/meta/_journal.json
@@ -22,6 +22,13 @@
"when": 1768553182073,
"tag": "0002_concerned_karma",
"breakpoints": true
+ },
+ {
+ "idx": 3,
+ "version": "5",
+ "when": 1768924576044,
+ "tag": "0003_calm_frog_thor",
+ "breakpoints": true
}
]
}
\ No newline at end of file
diff --git a/server/analyticsDb.ts b/server/analyticsDb.ts
index 5f1bcc4..d3688d6 100644
--- a/server/analyticsDb.ts
+++ b/server/analyticsDb.ts
@@ -50,23 +50,14 @@ export async function getTauxRemplissageByMonth() {
.select({
mois: sql`DATE_FORMAT(${sequences.createdAt}, '%Y-%m')`,
capaciteTotale: sql`SUM(${sequences.capaciteMax})`,
- inscritsConfirmes: sql`SUM(
- (SELECT COUNT(*)
- FROM ${inscriptions}
- WHERE ${inscriptions.sequenceId} = ${sequences.id}
- AND ${inscriptions.statut} = 'confirmee')
- )`,
+ inscritsConfirmes: sql`COUNT(CASE WHEN ${inscriptions.statut} = 'confirmee' THEN ${inscriptions.id} END)`,
tauxRemplissage: sql`ROUND(
- (SUM(
- (SELECT COUNT(*)
- FROM ${inscriptions}
- WHERE ${inscriptions.sequenceId} = ${sequences.id}
- AND ${inscriptions.statut} = 'confirmee')
- ) / SUM(${sequences.capaciteMax})) * 100,
+ (COUNT(CASE WHEN ${inscriptions.statut} = 'confirmee' THEN ${inscriptions.id} END) / SUM(${sequences.capaciteMax})) * 100,
2
)`,
})
.from(sequences)
+ .leftJoin(inscriptions, eq(sequences.id, inscriptions.sequenceId))
.groupBy(sql`DATE_FORMAT(${sequences.createdAt}, '%Y-%m')`)
.orderBy(sql`DATE_FORMAT(${sequences.createdAt}, '%Y-%m')`);
diff --git a/server/db.ts b/server/db.ts
index 7643129..5abfd95 100644
--- a/server/db.ts
+++ b/server/db.ts
@@ -619,12 +619,14 @@ export async function createPasswordResetToken(email: string, token: string) {
const expiresAt = new Date();
expiresAt.setHours(expiresAt.getHours() + 24); // Token valide 24h
- const result = await db.insert(passwordResetTokens).values({
- email,
- token,
- expiresAt,
- used: false,
- });
+ // Convertir la date au format MySQL DATETIME (YYYY-MM-DD HH:MM:SS)
+ const mysqlDate = expiresAt.toISOString().slice(0, 19).replace('T', ' ');
+
+ // Utiliser une requête SQL brute pour éviter les problèmes avec Drizzle
+ const result = await db.execute(sql`
+ INSERT INTO passwordResetTokens (email, token, expiresAt)
+ VALUES (${email}, ${token}, ${mysqlDate})
+ `);
return result;
}
diff --git a/server/routers.ts b/server/routers.ts
index 5e31902..0499bdd 100644
--- a/server/routers.ts
+++ b/server/routers.ts
@@ -361,9 +361,13 @@ export const appRouter = router({
update: adminProcedure.input(z.object({
id: z.number(),
nom: z.string().min(1).optional(),
- email: z.string().email().nullable().optional(),
+ email: z.string().email().or(z.literal('')).nullable().optional(),
})).mutation(async ({ input }) => {
const { id, ...data } = input;
+ // Convertir les chaînes vides en null
+ if (data.email === '') {
+ data.email = null;
+ }
await db.updateFormateur(id, data);
return { success: true };
}),
@@ -1257,12 +1261,8 @@ export const appRouter = router({
const crypto = await import('crypto');
const token = crypto.randomBytes(32).toString('hex');
- // Définir l'expiration à 24h
- const expiresAt = new Date();
- expiresAt.setHours(expiresAt.getHours() + 24);
-
- // Sauvegarder le token en base
- await db.createPasswordResetToken(user.id, token, expiresAt);
+ // Sauvegarder le token en base (la fonction gère l'expiration automatiquement)
+ await db.createPasswordResetToken(user.email, token);
// Générer le lien de réinitialisation
// TODO: Remplacer par l'URL réelle de votre application en production
diff --git a/todo.md b/todo.md
index 05b1dbf..381862f 100644
--- a/todo.md
+++ b/todo.md
@@ -1041,3 +1041,42 @@
- [x] Identifier les requêtes concernées dans server/routers.ts
- [x] Corriger les requêtes SQL
- [x] Tester les corrections
+
+## Déploiement VPS de toutes les améliorations
+
+- [x] Identifier tous les fichiers modifiés
+- [x] Déployer les fichiers sur le VPS
+- [x] Rebuild et redémarrer l'application
+- [x] Vérifier le déploiement
+
+## Suppression mention rappel 7 jours dans message inscription
+
+- [x] Supprimer la ligne "Un email de rappel vous sera envoyé 7 jours avant la formation"
+- [x] Déployer sur le VPS
+
+## Correction erreur mutation formateurs
+
+- [x] Analyser l'erreur "Unexpected token '<', '