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:
114
.manus/db/db-query-1768292182483.json
Normal file
114
.manus/db/db-query-1768292182483.json
Normal 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
|
||||
}
|
||||
@@ -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">
|
||||
|
||||
@@ -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
|
||||
/>
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user