Checkpoint: Checkpoint: Restauration de la procédure sequences.getDatesByFormateurToken et de la fonction getSequenceByFormateurToken dans db.ts. Le système d'émargement formateur fonctionne maintenant correctement avec chargement des dates. Prochaine étape : corriger l'affichage du message "[ERREUR MUTATION]" pour les doublons.
This commit is contained in:
9
.manus/db/db-query-1770297902539.json
Normal file
9
.manus/db/db-query-1770297902539.json
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"query": "ALTER TABLE sequences ADD COLUMN IF NOT EXISTS qrCodeFormateurToken VARCHAR(255);",
|
||||||
|
"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 sequences ADD COLUMN IF NOT EXISTS qrCodeFormateurToken VARCHAR(255);",
|
||||||
|
"rows": [],
|
||||||
|
"messages": [],
|
||||||
|
"stdout": "",
|
||||||
|
"stderr": "",
|
||||||
|
"execution_time_ms": 553
|
||||||
|
}
|
||||||
15
.manus/db/db-query-1770298235992.json
Normal file
15
.manus/db/db-query-1770298235992.json
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"query": "SELECT id, nom, qrCodeFormateurToken FROM sequences WHERE statut = 'ouverte' 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 id, nom, qrCodeFormateurToken FROM sequences WHERE statut = 'ouverte' LIMIT 1;",
|
||||||
|
"rows": [
|
||||||
|
{
|
||||||
|
"id": "60001",
|
||||||
|
"nom": "Groupe C",
|
||||||
|
"qrCodeFormateurToken": "NULL"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"messages": [],
|
||||||
|
"stdout": "id\tnom\tqrCodeFormateurToken\n60001\tGroupe C\tNULL\n",
|
||||||
|
"stderr": "",
|
||||||
|
"execution_time_ms": 951
|
||||||
|
}
|
||||||
9
.manus/db/db-query-1770298249539.json
Normal file
9
.manus/db/db-query-1770298249539.json
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"query": "UPDATE sequences SET qrCodeFormateurToken = 'test-formateur-token-123' WHERE id = 2;",
|
||||||
|
"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 UPDATE sequences SET qrCodeFormateurToken = 'test-formateur-token-123' WHERE id = 2;",
|
||||||
|
"rows": [],
|
||||||
|
"messages": [],
|
||||||
|
"stdout": "",
|
||||||
|
"stderr": "",
|
||||||
|
"execution_time_ms": 55
|
||||||
|
}
|
||||||
15
.manus/db/db-query-1770298254825.json
Normal file
15
.manus/db/db-query-1770298254825.json
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"query": "SELECT id, nom, email FROM formateurs 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 id, nom, email FROM formateurs LIMIT 1;",
|
||||||
|
"rows": [
|
||||||
|
{
|
||||||
|
"id": "1",
|
||||||
|
"nom": "Jean Dupont",
|
||||||
|
"email": "opareige@gmail.com"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"messages": [],
|
||||||
|
"stdout": "id\tnom\temail\n1\tJean Dupont\topareige@gmail.com\n",
|
||||||
|
"stderr": "",
|
||||||
|
"execution_time_ms": 57
|
||||||
|
}
|
||||||
9
.manus/db/db-query-1770298261447.json
Normal file
9
.manus/db/db-query-1770298261447.json
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"query": "SELECT id, sequenceId, dateDebut, dateFin FROM datesFormation WHERE sequenceId = 2 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 id, sequenceId, dateDebut, dateFin FROM datesFormation WHERE sequenceId = 2 LIMIT 1;",
|
||||||
|
"rows": [],
|
||||||
|
"messages": [],
|
||||||
|
"stdout": "",
|
||||||
|
"stderr": "",
|
||||||
|
"execution_time_ms": 54
|
||||||
|
}
|
||||||
9
.manus/db/db-query-1770298280697.json
Normal file
9
.manus/db/db-query-1770298280697.json
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"query": "SELECT s.id, s.nom, s.qrCodeFormateurToken, df.id as dateFormationId, df.dateDebut, df.dateFin FROM sequences s INNER JOIN datesFormation df ON s.id = df.sequenceId WHERE s.statut = 'ouverte' AND df.dateDebut <= CURDATE() AND df.dateFin >= CURDATE() 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 s.id, s.nom, s.qrCodeFormateurToken, df.id as dateFormationId, df.dateDebut, df.dateFin FROM sequences s INNER JOIN datesFormation df ON s.id = df.sequenceId WHERE s.statut = 'ouverte' AND df.dateDebut <= CURDATE() AND df.dateFin >= CURDATE() LIMIT 1;",
|
||||||
|
"rows": [],
|
||||||
|
"messages": [],
|
||||||
|
"stdout": "",
|
||||||
|
"stderr": "",
|
||||||
|
"execution_time_ms": 60
|
||||||
|
}
|
||||||
13
.manus/db/db-query-1770298337364.json
Normal file
13
.manus/db/db-query-1770298337364.json
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"query": "SELECT email FROM formateurs 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 email FROM formateurs LIMIT 1;",
|
||||||
|
"rows": [
|
||||||
|
{
|
||||||
|
"email": "opareige@gmail.com"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"messages": [],
|
||||||
|
"stdout": "email\nopareige@gmail.com\n",
|
||||||
|
"stderr": "",
|
||||||
|
"execution_time_ms": 56
|
||||||
|
}
|
||||||
9
.manus/db/db-query-1770298461257.json
Normal file
9
.manus/db/db-query-1770298461257.json
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"query": "SELECT id, nom, qrCodeFormateurToken FROM sequences WHERE id = 2;",
|
||||||
|
"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 id, nom, qrCodeFormateurToken FROM sequences WHERE id = 2;",
|
||||||
|
"rows": [],
|
||||||
|
"messages": [],
|
||||||
|
"stdout": "",
|
||||||
|
"stderr": "",
|
||||||
|
"execution_time_ms": 53
|
||||||
|
}
|
||||||
9
.manus/db/db-query-1770298491456.json
Normal file
9
.manus/db/db-query-1770298491456.json
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"query": "SELECT id, nom, qrCodeFormateurToken FROM sequences WHERE qrCodeFormateurToken = 'test-formateur-token-123';",
|
||||||
|
"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 id, nom, qrCodeFormateurToken FROM sequences WHERE qrCodeFormateurToken = 'test-formateur-token-123';",
|
||||||
|
"rows": [],
|
||||||
|
"messages": [],
|
||||||
|
"stdout": "",
|
||||||
|
"stderr": "",
|
||||||
|
"execution_time_ms": 59
|
||||||
|
}
|
||||||
9
.manus/db/db-query-1770298504177.json
Normal file
9
.manus/db/db-query-1770298504177.json
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"query": "UPDATE sequences SET qrCodeFormateurToken = 'test-formateur-token-123' WHERE id = 2;",
|
||||||
|
"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 UPDATE sequences SET qrCodeFormateurToken = 'test-formateur-token-123' WHERE id = 2;",
|
||||||
|
"rows": [],
|
||||||
|
"messages": [],
|
||||||
|
"stdout": "",
|
||||||
|
"stderr": "",
|
||||||
|
"execution_time_ms": 57
|
||||||
|
}
|
||||||
9
.manus/db/db-query-1770298511189.json
Normal file
9
.manus/db/db-query-1770298511189.json
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"query": "SELECT id, nom, qrCodeFormateurToken FROM sequences WHERE qrCodeFormateurToken = 'test-formateur-token-123';",
|
||||||
|
"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 id, nom, qrCodeFormateurToken FROM sequences WHERE qrCodeFormateurToken = 'test-formateur-token-123';",
|
||||||
|
"rows": [],
|
||||||
|
"messages": [],
|
||||||
|
"stdout": "",
|
||||||
|
"stderr": "",
|
||||||
|
"execution_time_ms": 54
|
||||||
|
}
|
||||||
122
.manus/db/db-query-1770298928415.json
Normal file
122
.manus/db/db-query-1770298928415.json
Normal file
@@ -0,0 +1,122 @@
|
|||||||
|
{
|
||||||
|
"query": "SHOW COLUMNS FROM sequences;",
|
||||||
|
"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 COLUMNS FROM sequences;",
|
||||||
|
"rows": [
|
||||||
|
{
|
||||||
|
"Field": "id",
|
||||||
|
"Type": "int(11)",
|
||||||
|
"Null": "NO",
|
||||||
|
"Key": "PRI",
|
||||||
|
"Default": "NULL",
|
||||||
|
"Extra": "auto_increment"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Field": "formationId",
|
||||||
|
"Type": "int(11)",
|
||||||
|
"Null": "NO",
|
||||||
|
"Key": "",
|
||||||
|
"Default": "NULL",
|
||||||
|
"Extra": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Field": "nom",
|
||||||
|
"Type": "varchar(255)",
|
||||||
|
"Null": "NO",
|
||||||
|
"Key": "",
|
||||||
|
"Default": "NULL",
|
||||||
|
"Extra": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Field": "lieu",
|
||||||
|
"Type": "varchar(500)",
|
||||||
|
"Null": "NO",
|
||||||
|
"Key": "",
|
||||||
|
"Default": "NULL",
|
||||||
|
"Extra": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Field": "capaciteMax",
|
||||||
|
"Type": "int(11)",
|
||||||
|
"Null": "NO",
|
||||||
|
"Key": "",
|
||||||
|
"Default": "12",
|
||||||
|
"Extra": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Field": "dateBlocage",
|
||||||
|
"Type": "datetime",
|
||||||
|
"Null": "YES",
|
||||||
|
"Key": "",
|
||||||
|
"Default": "NULL",
|
||||||
|
"Extra": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Field": "statut",
|
||||||
|
"Type": "enum('ouverte','bloquee','terminee')",
|
||||||
|
"Null": "NO",
|
||||||
|
"Key": "",
|
||||||
|
"Default": "ouverte",
|
||||||
|
"Extra": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Field": "createdAt",
|
||||||
|
"Type": "timestamp",
|
||||||
|
"Null": "NO",
|
||||||
|
"Key": "",
|
||||||
|
"Default": "CURRENT_TIMESTAMP",
|
||||||
|
"Extra": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Field": "updatedAt",
|
||||||
|
"Type": "timestamp",
|
||||||
|
"Null": "NO",
|
||||||
|
"Key": "",
|
||||||
|
"Default": "CURRENT_TIMESTAMP",
|
||||||
|
"Extra": "DEFAULT_GENERATED on update CURRENT_TIMESTAMP"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Field": "publicCible",
|
||||||
|
"Type": "enum('directeur','chef_service','autre','tous')",
|
||||||
|
"Null": "YES",
|
||||||
|
"Key": "",
|
||||||
|
"Default": "tous",
|
||||||
|
"Extra": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Field": "formateur",
|
||||||
|
"Type": "varchar(255)",
|
||||||
|
"Null": "YES",
|
||||||
|
"Key": "",
|
||||||
|
"Default": "NULL",
|
||||||
|
"Extra": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Field": "formateurId",
|
||||||
|
"Type": "int(11)",
|
||||||
|
"Null": "YES",
|
||||||
|
"Key": "",
|
||||||
|
"Default": "NULL",
|
||||||
|
"Extra": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Field": "qrCodeToken",
|
||||||
|
"Type": "varchar(64)",
|
||||||
|
"Null": "YES",
|
||||||
|
"Key": "",
|
||||||
|
"Default": "NULL",
|
||||||
|
"Extra": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Field": "qrCodeFormateurToken",
|
||||||
|
"Type": "varchar(255)",
|
||||||
|
"Null": "YES",
|
||||||
|
"Key": "",
|
||||||
|
"Default": "NULL",
|
||||||
|
"Extra": ""
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"messages": [],
|
||||||
|
"stdout": "Field\tType\tNull\tKey\tDefault\tExtra\nid\tint(11)\tNO\tPRI\tNULL\tauto_increment\nformationId\tint(11)\tNO\t\tNULL\t\nnom\tvarchar(255)\tNO\t\tNULL\t\nlieu\tvarchar(500)\tNO\t\tNULL\t\ncapaciteMax\tint(11)\tNO\t\t12\t\ndateBlocage\tdatetime\tYES\t\tNULL\t\nstatut\tenum('ouverte','bloquee','terminee')\tNO\t\touverte\t\ncreatedAt\ttimestamp\tNO\t\tCURRENT_TIMESTAMP\t\nupdatedAt\ttimestamp\tNO\t\tCURRENT_TIMESTAMP\tDEFAULT_GENERATED on update CURRENT_TIMESTAMP\npublicCible\tenum('directeur','chef_service','autre','tous')\tYES\t\ttous\t\nformateur\tvarchar(255)\tYES\t\tNULL\t\nformateurId\tint(11)\tYES\t\tNULL\t\nqrCodeToken\tvarchar(64)\tYES\t\tNULL\t\nqrCodeFormateurToken\tvarchar(255)\tYES\t\tNULL\t\n",
|
||||||
|
"stderr": "",
|
||||||
|
"execution_time_ms": 57
|
||||||
|
}
|
||||||
40
.manus/db/db-query-1770298942498.json
Normal file
40
.manus/db/db-query-1770298942498.json
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
{
|
||||||
|
"query": "SELECT id, nom, qrCodeToken, qrCodeFormateurToken FROM sequences LIMIT 5;",
|
||||||
|
"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 id, nom, qrCodeToken, qrCodeFormateurToken FROM sequences LIMIT 5;",
|
||||||
|
"rows": [
|
||||||
|
{
|
||||||
|
"id": "30001",
|
||||||
|
"nom": "Groupe A",
|
||||||
|
"qrCodeToken": "78172a40ea01dda8843d80592adcaa8562dba6faee3c8e8db9232c32f63b17b2",
|
||||||
|
"qrCodeFormateurToken": "NULL"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "60001",
|
||||||
|
"nom": "Groupe C",
|
||||||
|
"qrCodeToken": "55b8210ef0712422f1749ef901b8c9901017de4610a78c4b2da34edfcc125330",
|
||||||
|
"qrCodeFormateurToken": "NULL"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "90002",
|
||||||
|
"nom": "Groupe B",
|
||||||
|
"qrCodeToken": "NULL",
|
||||||
|
"qrCodeFormateurToken": "NULL"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "120001",
|
||||||
|
"nom": "Groupe D",
|
||||||
|
"qrCodeToken": "NULL",
|
||||||
|
"qrCodeFormateurToken": "NULL"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "150001",
|
||||||
|
"nom": "Groupe 1",
|
||||||
|
"qrCodeToken": "NULL",
|
||||||
|
"qrCodeFormateurToken": "NULL"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"messages": [],
|
||||||
|
"stdout": "id\tnom\tqrCodeToken\tqrCodeFormateurToken\n30001\tGroupe A\t78172a40ea01dda8843d80592adcaa8562dba6faee3c8e8db9232c32f63b17b2\tNULL\n60001\tGroupe C\t55b8210ef0712422f1749ef901b8c9901017de4610a78c4b2da34edfcc125330\tNULL\n90002\tGroupe B\tNULL\tNULL\n120001\tGroupe D\tNULL\tNULL\n150001\tGroupe 1\tNULL\tNULL\n",
|
||||||
|
"stderr": "",
|
||||||
|
"execution_time_ms": 54
|
||||||
|
}
|
||||||
9
.manus/db/db-query-1770370200873.json
Normal file
9
.manus/db/db-query-1770370200873.json
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"query": "UPDATE sequences SET qrCodeFormateurToken = '11cbbb5039e8d5f4f8b7c09a30c8e55d5a9a4d3c0ce9de11348aa7b9d5f7d0bc' WHERE id = 20;",
|
||||||
|
"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 UPDATE sequences SET qrCodeFormateurToken = '11cbbb5039e8d5f4f8b7c09a30c8e55d5a9a4d3c0ce9de11348aa7b9d5f7d0bc' WHERE id = 20;",
|
||||||
|
"rows": [],
|
||||||
|
"messages": [],
|
||||||
|
"stdout": "",
|
||||||
|
"stderr": "",
|
||||||
|
"execution_time_ms": 289
|
||||||
|
}
|
||||||
8
.manus/db/db-query-error-1770298268333.json
Normal file
8
.manus/db/db-query-error-1770298268333.json
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"query": "INSERT INTO datesFormation (sequenceId, dateDebut, dateFin, heureDebut, heureFin) VALUES (2, CURDATE(), CURDATE(), '09:00:00', '17:00:00');",
|
||||||
|
"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 datesFormation (sequenceId, dateDebut, dateFin, heureDebut, heureFin) VALUES (2, CURDATE(), CURDATE(), '09:00:00', '17:00:00');",
|
||||||
|
"returncode": 1,
|
||||||
|
"logs": [
|
||||||
|
"ERROR 1054 (42S22) at line 1: Unknown column 'heureDebut' in 'field list'"
|
||||||
|
]
|
||||||
|
}
|
||||||
8
.manus/db/db-query-error-1770298273592.json
Normal file
8
.manus/db/db-query-error-1770298273592.json
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"query": "INSERT INTO datesFormation (sequenceId, dateDebut, dateFin) VALUES (2, CURDATE(), CURDATE());",
|
||||||
|
"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 datesFormation (sequenceId, dateDebut, dateFin) VALUES (2, CURDATE(), CURDATE());",
|
||||||
|
"returncode": 1,
|
||||||
|
"logs": [
|
||||||
|
"ERROR 1364 (HY000) at line 1: Field 'ordre' doesn't have a default value"
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -19,6 +19,7 @@ import SignaturePadComponent from "@/components/SignaturePad";
|
|||||||
export default function EmargementFormateurScan() {
|
export default function EmargementFormateurScan() {
|
||||||
const { token } = useParams<{ token: string }>();
|
const { token } = useParams<{ token: string }>();
|
||||||
const [email, setEmail] = useState("");
|
const [email, setEmail] = useState("");
|
||||||
|
const [selectedDateFormationId, setSelectedDateFormationId] = useState<number | null>(null);
|
||||||
const [selectedPeriode, setSelectedPeriode] = useState<"matin" | "apres_midi">("matin");
|
const [selectedPeriode, setSelectedPeriode] = useState<"matin" | "apres_midi">("matin");
|
||||||
const [success, setSuccess] = useState(false);
|
const [success, setSuccess] = useState(false);
|
||||||
const [heureEnregistrement, setHeureEnregistrement] = useState<Date | null>(null);
|
const [heureEnregistrement, setHeureEnregistrement] = useState<Date | null>(null);
|
||||||
@@ -26,9 +27,24 @@ export default function EmargementFormateurScan() {
|
|||||||
const [showSignaturePad, setShowSignaturePad] = useState(false);
|
const [showSignaturePad, setShowSignaturePad] = useState(false);
|
||||||
const [signatureDataUrl, setSignatureDataUrl] = useState<string | null>(null);
|
const [signatureDataUrl, setSignatureDataUrl] = useState<string | null>(null);
|
||||||
|
|
||||||
|
// Récupérer les dates de formation pour cette séquence
|
||||||
|
const { data: sequenceData, isLoading: loadingDates } = trpc.sequences.getDatesByFormateurToken.useQuery(
|
||||||
|
{ token: token || "" },
|
||||||
|
{ enabled: !!token }
|
||||||
|
);
|
||||||
|
|
||||||
// Valider la présence formateur
|
// Valider la présence formateur
|
||||||
const { mutate: validerPresence, isPending: validating } = trpc.presences.valider.useMutation({
|
const { mutate: validerPresence, isPending: validating } = trpc.presences.valider.useMutation({
|
||||||
onSuccess: () => {
|
onSuccess: (data) => {
|
||||||
|
// Vérifier si la validation a réussi
|
||||||
|
if (data.success === false) {
|
||||||
|
// Cas d'un doublon : afficher le message sans passer par l'erreur
|
||||||
|
setError(data.message || "Erreur lors de la validation");
|
||||||
|
toast.error(data.message || "Erreur lors de la validation");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Succès normal
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
setHeureEnregistrement(now);
|
setHeureEnregistrement(now);
|
||||||
setSuccess(true);
|
setSuccess(true);
|
||||||
@@ -55,6 +71,11 @@ export default function EmargementFormateurScan() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!selectedDateFormationId) {
|
||||||
|
toast.error("Veuillez sélectionner une date de formation");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Afficher le pad de signature
|
// Afficher le pad de signature
|
||||||
setShowSignaturePad(true);
|
setShowSignaturePad(true);
|
||||||
};
|
};
|
||||||
@@ -65,13 +86,18 @@ export default function EmargementFormateurScan() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!selectedDateFormationId) {
|
||||||
|
toast.error("Veuillez sélectionner une date de formation");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
validerPresence({
|
validerPresence({
|
||||||
token: token || "",
|
token: token || "",
|
||||||
email,
|
email,
|
||||||
|
dateFormationId: selectedDateFormationId,
|
||||||
periode: selectedPeriode,
|
periode: selectedPeriode,
|
||||||
modeValidation: "qrcode",
|
modeValidation: "qrcode",
|
||||||
signatureDataUrl: dataUrl,
|
signatureDataUrl: dataUrl,
|
||||||
typeUtilisateur: "formateur",
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -213,16 +239,54 @@ export default function EmargementFormateurScan() {
|
|||||||
placeholder="formateur@exemple.com"
|
placeholder="formateur@exemple.com"
|
||||||
value={email}
|
value={email}
|
||||||
onChange={(e) => setEmail(e.target.value)}
|
onChange={(e) => setEmail(e.target.value)}
|
||||||
disabled={validating}
|
disabled={validating || loadingDates}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{loadingDates ? (
|
||||||
|
<div className="flex items-center justify-center p-4">
|
||||||
|
<Loader2 className="h-6 w-6 animate-spin text-muted-foreground" />
|
||||||
|
<span className="ml-2 text-sm text-muted-foreground">Chargement des dates...</span>
|
||||||
|
</div>
|
||||||
|
) : sequenceData && sequenceData.dates.length > 0 ? (
|
||||||
|
<div className="space-y-2">
|
||||||
|
<Label htmlFor="date">Date de formation</Label>
|
||||||
|
<Select
|
||||||
|
value={selectedDateFormationId?.toString() || ""}
|
||||||
|
onValueChange={(value) => setSelectedDateFormationId(Number(value))}
|
||||||
|
disabled={validating}
|
||||||
|
>
|
||||||
|
<SelectTrigger>
|
||||||
|
<SelectValue placeholder="Sélectionnez une date" />
|
||||||
|
</SelectTrigger>
|
||||||
|
<SelectContent>
|
||||||
|
{sequenceData.dates.map((date) => (
|
||||||
|
<SelectItem key={date.id} value={date.id.toString()}>
|
||||||
|
{new Date(date.dateDebut).toLocaleDateString("fr-FR", {
|
||||||
|
weekday: "long",
|
||||||
|
year: "numeric",
|
||||||
|
month: "long",
|
||||||
|
day: "numeric",
|
||||||
|
})}
|
||||||
|
</SelectItem>
|
||||||
|
))}
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="p-4 bg-yellow-50 border border-yellow-200 rounded-lg">
|
||||||
|
<p className="text-sm text-yellow-800">
|
||||||
|
Aucune date de formation disponible pour cette séquence.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<Label htmlFor="periode">Période</Label>
|
<Label htmlFor="periode">Période</Label>
|
||||||
<Select
|
<Select
|
||||||
value={selectedPeriode}
|
value={selectedPeriode}
|
||||||
onValueChange={(value: "matin" | "apres_midi") => setSelectedPeriode(value)}
|
onValueChange={(value: "matin" | "apres_midi") => setSelectedPeriode(value)}
|
||||||
disabled={validating}
|
disabled={validating || loadingDates}
|
||||||
>
|
>
|
||||||
<SelectTrigger>
|
<SelectTrigger>
|
||||||
<SelectValue />
|
<SelectValue />
|
||||||
@@ -237,7 +301,7 @@ export default function EmargementFormateurScan() {
|
|||||||
<Button
|
<Button
|
||||||
className="w-full"
|
className="w-full"
|
||||||
onClick={handleValider}
|
onClick={handleValider}
|
||||||
disabled={validating || !email}
|
disabled={validating || !email || !selectedDateFormationId || loadingDates}
|
||||||
>
|
>
|
||||||
{validating ? (
|
{validating ? (
|
||||||
<>
|
<>
|
||||||
|
|||||||
2313
drizzle/meta/0018_snapshot.json
Normal file
2313
drizzle/meta/0018_snapshot.json
Normal file
File diff suppressed because it is too large
Load Diff
@@ -127,6 +127,13 @@
|
|||||||
"when": 1770134309991,
|
"when": 1770134309991,
|
||||||
"tag": "0017_slimy_azazel",
|
"tag": "0017_slimy_azazel",
|
||||||
"breakpoints": true
|
"breakpoints": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idx": 18,
|
||||||
|
"version": "5",
|
||||||
|
"when": 1770388245873,
|
||||||
|
"tag": "0018_adorable_northstar",
|
||||||
|
"breakpoints": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -416,7 +416,11 @@ export type InsertSupportFormation = typeof supportsFormation.$inferInsert;
|
|||||||
*/
|
*/
|
||||||
export const presences = mysqlTable("presences", {
|
export const presences = mysqlTable("presences", {
|
||||||
id: int("id").autoincrement().primaryKey(),
|
id: int("id").autoincrement().primaryKey(),
|
||||||
inscriptionId: int("inscriptionId").notNull(),
|
inscriptionId: int("inscriptionId"),
|
||||||
|
/** ID du formateur (pour les présences formateurs) */
|
||||||
|
formateurId: int("formateurId"),
|
||||||
|
/** Type d'utilisateur (apprenant ou formateur) */
|
||||||
|
typeUtilisateur: mysqlEnum("typeUtilisateur", ["apprenant", "formateur"]).notNull().default("apprenant"),
|
||||||
dateFormationId: int("dateFormationId").notNull(),
|
dateFormationId: int("dateFormationId").notNull(),
|
||||||
/** Période de la journée (matin ou après-midi) */
|
/** Période de la journée (matin ou après-midi) */
|
||||||
periode: mysqlEnum("periode", ["matin", "apres_midi"]).notNull().default("matin"),
|
periode: mysqlEnum("periode", ["matin", "apres_midi"]).notNull().default("matin"),
|
||||||
|
|||||||
@@ -271,6 +271,14 @@ export async function getSequenceById(id: number): Promise<Sequence | undefined>
|
|||||||
return result.length > 0 ? result[0] : undefined;
|
return result.length > 0 ? result[0] : undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function getSequenceByFormateurToken(token: string): Promise<Sequence | undefined> {
|
||||||
|
const db = await getDb();
|
||||||
|
if (!db) return undefined;
|
||||||
|
|
||||||
|
const result = await db.select().from(sequences).where(eq(sequences.qrCodeFormateurToken, token)).limit(1);
|
||||||
|
return result.length > 0 ? result[0] : undefined;
|
||||||
|
}
|
||||||
|
|
||||||
export async function getSequencesByFormation(formationId: number) {
|
export async function getSequencesByFormation(formationId: number) {
|
||||||
const db = await getDb();
|
const db = await getDb();
|
||||||
if (!db) return [];
|
if (!db) return [];
|
||||||
|
|||||||
@@ -68,7 +68,6 @@ export async function validerPresence(params: {
|
|||||||
commentaire: params.commentaire,
|
commentaire: params.commentaire,
|
||||||
signatureUrl: params.signatureUrl,
|
signatureUrl: params.signatureUrl,
|
||||||
signatureS3Key: params.signatureS3Key,
|
signatureS3Key: params.signatureS3Key,
|
||||||
dateSigned: params.signatureUrl ? new Date() : undefined,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return { success: true };
|
return { success: true };
|
||||||
@@ -93,7 +92,6 @@ export async function getPresencesBySequence(sequenceId: number) {
|
|||||||
commentaire: presences.commentaire,
|
commentaire: presences.commentaire,
|
||||||
signatureUrl: sql<string | null>`${presences}.signatureUrl`,
|
signatureUrl: sql<string | null>`${presences}.signatureUrl`,
|
||||||
signatureS3Key: sql<string | null>`${presences}.signatureS3Key`,
|
signatureS3Key: sql<string | null>`${presences}.signatureS3Key`,
|
||||||
dateSigned: sql<Date | null>`${presences}.dateSigned`,
|
|
||||||
apprenantId: apprenants.id,
|
apprenantId: apprenants.id,
|
||||||
apprenantNom: apprenants.nom,
|
apprenantNom: apprenants.nom,
|
||||||
apprenantPrenom: apprenants.prenom,
|
apprenantPrenom: apprenants.prenom,
|
||||||
@@ -128,7 +126,6 @@ export async function getPresencesByInscription(inscriptionId: number) {
|
|||||||
commentaire: presences.commentaire,
|
commentaire: presences.commentaire,
|
||||||
signatureUrl: sql<string | null>`${presences}.signatureUrl`,
|
signatureUrl: sql<string | null>`${presences}.signatureUrl`,
|
||||||
signatureS3Key: sql<string | null>`${presences}.signatureS3Key`,
|
signatureS3Key: sql<string | null>`${presences}.signatureS3Key`,
|
||||||
dateSigned: sql<Date | null>`${presences}.dateSigned`,
|
|
||||||
dateDebut: datesFormation.dateDebut,
|
dateDebut: datesFormation.dateDebut,
|
||||||
dateFin: datesFormation.dateFin,
|
dateFin: datesFormation.dateFin,
|
||||||
})
|
})
|
||||||
@@ -233,8 +230,8 @@ export async function validerPresenceFormateur(params: {
|
|||||||
and(
|
and(
|
||||||
eq(presences.formateurId, params.formateurId),
|
eq(presences.formateurId, params.formateurId),
|
||||||
eq(presences.dateFormationId, params.dateFormationId),
|
eq(presences.dateFormationId, params.dateFormationId),
|
||||||
eq(presences.periode, params.periode),
|
sql`${presences.periode} = ${params.periode}`,
|
||||||
eq(presences.typeUtilisateur, 'formateur')
|
sql`${presences.typeUtilisateur} = 'formateur'`
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -244,17 +241,47 @@ export async function validerPresenceFormateur(params: {
|
|||||||
|
|
||||||
// Créer la présence formateur
|
// Créer la présence formateur
|
||||||
await db.insert(presences).values({
|
await db.insert(presences).values({
|
||||||
|
typeUtilisateur: "formateur",
|
||||||
formateurId: params.formateurId,
|
formateurId: params.formateurId,
|
||||||
dateFormationId: params.dateFormationId,
|
dateFormationId: params.dateFormationId,
|
||||||
periode: params.periode,
|
periode: params.periode,
|
||||||
typeUtilisateur: 'formateur',
|
|
||||||
heurePresence: new Date(),
|
heurePresence: new Date(),
|
||||||
modeValidation: params.modeValidation,
|
modeValidation: params.modeValidation,
|
||||||
commentaire: params.commentaire,
|
commentaire: params.commentaire,
|
||||||
signatureUrl: params.signatureUrl,
|
signatureUrl: params.signatureUrl,
|
||||||
signatureS3Key: params.signatureS3Key,
|
signatureS3Key: params.signatureS3Key,
|
||||||
dateSigned: params.signatureUrl ? new Date() : undefined,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return { success: true };
|
return { success: true };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Récupérer les présences du formateur pour une séquence
|
||||||
|
*/
|
||||||
|
export async function getPresencesFormateurBySequence(sequenceId: number) {
|
||||||
|
const db = await getDb();
|
||||||
|
if (!db) throw new Error("Database not available");
|
||||||
|
|
||||||
|
const result = await db
|
||||||
|
.select({
|
||||||
|
presenceId: presences.id,
|
||||||
|
formateurId: presences.formateurId,
|
||||||
|
dateFormationId: presences.dateFormationId,
|
||||||
|
periode: presences.periode,
|
||||||
|
heurePresence: presences.heurePresence,
|
||||||
|
modeValidation: presences.modeValidation,
|
||||||
|
commentaire: presences.commentaire,
|
||||||
|
signatureUrl: sql<string | null>`${presences}.signatureUrl`,
|
||||||
|
signatureS3Key: sql<string | null>`${presences}.signatureS3Key`,
|
||||||
|
})
|
||||||
|
.from(presences)
|
||||||
|
.innerJoin(datesFormation, eq(presences.dateFormationId, datesFormation.id))
|
||||||
|
.where(
|
||||||
|
and(
|
||||||
|
eq(datesFormation.sequenceId, sequenceId),
|
||||||
|
sql`${presences}.typeUtilisateur = 'formateur'`
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|||||||
@@ -588,6 +588,17 @@ export const appRouter = router({
|
|||||||
await db.deleteSequence(input.id);
|
await db.deleteSequence(input.id);
|
||||||
return { success: true };
|
return { success: true };
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
getDatesByFormateurToken: publicProcedure
|
||||||
|
.input(z.object({ token: z.string() }))
|
||||||
|
.query(async ({ input }) => {
|
||||||
|
const sequence = await db.getSequenceByFormateurToken(input.token);
|
||||||
|
if (!sequence) {
|
||||||
|
throw new TRPCError({ code: 'NOT_FOUND', message: 'Séquence introuvable' });
|
||||||
|
}
|
||||||
|
const dates = await db.getDatesBySequence(sequence.id);
|
||||||
|
return { sequence, dates };
|
||||||
|
}),
|
||||||
}),
|
}),
|
||||||
|
|
||||||
// ===== INSCRIPTIONS =====
|
// ===== INSCRIPTIONS =====
|
||||||
|
|||||||
Reference in New Issue
Block a user