Checkpoint: Correction des erreurs tRPC qui retournaient du HTML au lieu de JSON :

- Changement du champ 'nom' en 'titre' pour les questionnaires (backend et frontend)
- Suppression de la propriété 'complete' inexistante dans reponsesQuestionnaires
- Correction du type 'provider' dans la configuration email
- Gestion des valeurs null pour le champ 'fonction' des apprenants
- Résolution de l'erreur logsRappels.email après redémarrage du serveur
This commit is contained in:
Manus
2026-01-13 03:21:23 -05:00
parent 97aeacab29
commit bd9cb15f44
5 changed files with 133 additions and 20 deletions

View File

@@ -0,0 +1,114 @@
{
"query": "DESCRIBE logsRappels;",
"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 logsRappels;",
"rows": [
{
"Field": "id",
"Type": "int(11)",
"Null": "NO",
"Key": "PRI",
"Default": "NULL",
"Extra": "auto_increment"
},
{
"Field": "rappelId",
"Type": "int(11)",
"Null": "NO",
"Key": "",
"Default": "NULL",
"Extra": ""
},
{
"Field": "sequenceId",
"Type": "int(11)",
"Null": "NO",
"Key": "",
"Default": "NULL",
"Extra": ""
},
{
"Field": "apprenantId",
"Type": "int(11)",
"Null": "NO",
"Key": "",
"Default": "NULL",
"Extra": ""
},
{
"Field": "emailDestinataire",
"Type": "varchar(320)",
"Null": "NO",
"Key": "",
"Default": "NULL",
"Extra": ""
},
{
"Field": "typeRappel",
"Type": "varchar(50)",
"Null": "NO",
"Key": "",
"Default": "NULL",
"Extra": ""
},
{
"Field": "dateEnvoi",
"Type": "timestamp",
"Null": "NO",
"Key": "",
"Default": "CURRENT_TIMESTAMP",
"Extra": ""
},
{
"Field": "statut",
"Type": "enum('success','failed')",
"Null": "NO",
"Key": "",
"Default": "NULL",
"Extra": ""
},
{
"Field": "messageErreur",
"Type": "text",
"Null": "YES",
"Key": "",
"Default": "NULL",
"Extra": ""
},
{
"Field": "dateSequence",
"Type": "timestamp",
"Null": "NO",
"Key": "",
"Default": "NULL",
"Extra": ""
},
{
"Field": "createdAt",
"Type": "timestamp",
"Null": "NO",
"Key": "",
"Default": "CURRENT_TIMESTAMP",
"Extra": ""
},
{
"Field": "nbTentatives",
"Type": "int(11)",
"Null": "NO",
"Key": "",
"Default": "1",
"Extra": ""
},
{
"Field": "prochainEssai",
"Type": "timestamp",
"Null": "YES",
"Key": "",
"Default": "NULL",
"Extra": ""
}
],
"messages": [],
"stdout": "Field\tType\tNull\tKey\tDefault\tExtra\nid\tint(11)\tNO\tPRI\tNULL\tauto_increment\nrappelId\tint(11)\tNO\t\tNULL\t\nsequenceId\tint(11)\tNO\t\tNULL\t\napprenantId\tint(11)\tNO\t\tNULL\t\nemailDestinataire\tvarchar(320)\tNO\t\tNULL\t\ntypeRappel\tvarchar(50)\tNO\t\tNULL\t\ndateEnvoi\ttimestamp\tNO\t\tCURRENT_TIMESTAMP\t\nstatut\tenum('success','failed')\tNO\t\tNULL\t\nmessageErreur\ttext\tYES\t\tNULL\t\ndateSequence\ttimestamp\tNO\t\tNULL\t\ncreatedAt\ttimestamp\tNO\t\tCURRENT_TIMESTAMP\t\nnbTentatives\tint(11)\tNO\t\t1\t\nprochainEssai\ttimestamp\tYES\t\tNULL\t\n",
"stderr": "",
"execution_time_ms": 446
}

View File

@@ -140,7 +140,7 @@ export default function AdminQuestionnaireAnalyse() {
<div className="container mx-auto py-8">
<div className="mb-6 flex justify-between items-start">
<div>
<h1 className="text-3xl font-bold">{questionnaire.nom}</h1>
<h1 className="text-3xl font-bold">{questionnaire.titre}</h1>
<p className="text-muted-foreground mt-1">Analyse des réponses</p>
</div>
<div className="flex gap-2">

View File

@@ -35,7 +35,7 @@ export default function AdminQuestionnaireEdit() {
const [, navigate] = useLocation();
const questionnaireId = params?.id === "nouveau" ? null : parseInt(params?.id || "0");
const [nom, setNom] = useState("");
const [titre, setTitre] = useState("");
const [type, setType] = useState<"satisfaction" | "evaluation_pre" | "evaluation_post">("satisfaction");
const [description, setDescription] = useState("");
const [actif, setActif] = useState(true);
@@ -101,7 +101,7 @@ export default function AdminQuestionnaireEdit() {
useEffect(() => {
if (questionnaire) {
setNom(questionnaire.nom);
setTitre(questionnaire.titre);
setType(questionnaire.type);
setDescription(questionnaire.description || "");
setActif(questionnaire.actif);
@@ -130,8 +130,8 @@ export default function AdminQuestionnaireEdit() {
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
if (!nom.trim()) {
toast.error("Le nom du questionnaire est obligatoire");
if (!titre.trim()) {
toast.error("Le titre du questionnaire est obligatoire");
return;
}
@@ -141,7 +141,7 @@ export default function AdminQuestionnaireEdit() {
}
const data = {
nom,
titre,
type,
description: description || undefined,
actif,
@@ -208,11 +208,11 @@ export default function AdminQuestionnaireEdit() {
</CardHeader>
<CardContent className="space-y-4">
<div>
<Label htmlFor="nom">Nom du questionnaire *</Label>
<Label htmlFor="titre">Titre du questionnaire *</Label>
<Input
id="nom"
value={nom}
onChange={(e) => setNom(e.target.value)}
id="titre"
value={titre}
onChange={(e) => setTitre(e.target.value)}
placeholder="Ex: Questionnaire de satisfaction Manager Itinova"
required
/>

View File

@@ -90,7 +90,7 @@ export default function AdminQuestionnaires() {
<div className="flex justify-between items-start">
<div className="flex-1">
<div className="flex items-center gap-2 mb-2">
<CardTitle>{questionnaire.nom}</CardTitle>
<CardTitle>{questionnaire.titre}</CardTitle>
{getTypeBadge(questionnaire.type)}
{!questionnaire.actif && (
<Badge variant="outline" className="bg-gray-100">

View File

@@ -688,7 +688,7 @@ export const appRouter = router({
formateurNom: formateur.nom,
apprenantNom: apprenant.nom,
apprenantPrenom: apprenant.prenom,
apprenantFonction: apprenant.fonction,
apprenantFonction: apprenant.fonction || '',
formationNom: formation.nom,
sequenceNom: sequence.nom,
nbInscrits: nbInscritsApres,
@@ -797,7 +797,7 @@ export const appRouter = router({
formateurNom: formateur.nom,
apprenantNom: apprenant.nom,
apprenantPrenom: apprenant.prenom,
apprenantFonction: apprenant.fonction,
apprenantFonction: apprenant.fonction || '',
formationNom: formation.nom,
sequenceNom: sequence.nom,
nbInscrits: nbInscritsApres,
@@ -1338,7 +1338,7 @@ export const appRouter = router({
apprenantEmail: apprenant.email,
apprenantPrenom: apprenant.prenom,
apprenantNom: apprenant.nom,
apprenantFonction: apprenant.fonction,
apprenantFonction: apprenant.fonction || '',
formationNom: formation.nom,
sequenceNom: sequence.nom,
dates: dates.map((d: any) => ({
@@ -1354,7 +1354,7 @@ export const appRouter = router({
apprenantEmail: apprenant.email,
apprenantPrenom: apprenant.prenom,
apprenantNom: apprenant.nom,
apprenantFonction: apprenant.fonction,
apprenantFonction: apprenant.fonction || '',
formationNom: formation.nom,
sequenceNom: sequence.nom,
dates: dates.map((d: any) => ({
@@ -1421,7 +1421,7 @@ export const appRouter = router({
upsert: adminProcedure
.input(z.object({
provider: z.string(),
provider: z.enum(["resend", "smtp", "simulation"]),
apiKey: z.string().nullable(),
fromEmail: z.string().email(),
fromName: z.string(),
@@ -1591,7 +1591,7 @@ export const appRouter = router({
create: adminProcedure
.input(z.object({
nom: z.string(),
titre: z.string(),
type: z.enum(["satisfaction", "evaluation_pre", "evaluation_post"]),
description: z.string().optional(),
formationId: z.number().nullable().optional(),
@@ -1608,7 +1608,7 @@ export const appRouter = router({
update: adminProcedure
.input(z.object({
id: z.number(),
nom: z.string().optional(),
titre: z.string().optional(),
type: z.enum(["satisfaction", "evaluation_pre", "evaluation_post"]).optional(),
description: z.string().optional(),
formationId: z.number().nullable().optional(),
@@ -1766,7 +1766,6 @@ export const appRouter = router({
questionnaireId: envoi.questionnaireId,
apprenantId: envoi.apprenantId,
sequenceId: envoi.sequenceId,
complete: true,
});
// Enregistrer chaque réponse
@@ -1791,7 +1790,7 @@ export const appRouter = router({
await notifyOwner({
title: 'Nouvelle réponse au questionnaire',
content: `Un apprenant a répondu au questionnaire "${questionnaire?.nom || 'Sans titre'}"`,
content: `Un apprenant a répondu au questionnaire "${questionnaire?.titre || 'Sans titre'}"`,
});
} catch (error) {
console.error('[Notification] Erreur lors de l\'envoi de la notification:', error);