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="container mx-auto py-8">
|
||||||
<div className="mb-6 flex justify-between items-start">
|
<div className="mb-6 flex justify-between items-start">
|
||||||
<div>
|
<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>
|
<p className="text-muted-foreground mt-1">Analyse des réponses</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex gap-2">
|
<div className="flex gap-2">
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ export default function AdminQuestionnaireEdit() {
|
|||||||
const [, navigate] = useLocation();
|
const [, navigate] = useLocation();
|
||||||
const questionnaireId = params?.id === "nouveau" ? null : parseInt(params?.id || "0");
|
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 [type, setType] = useState<"satisfaction" | "evaluation_pre" | "evaluation_post">("satisfaction");
|
||||||
const [description, setDescription] = useState("");
|
const [description, setDescription] = useState("");
|
||||||
const [actif, setActif] = useState(true);
|
const [actif, setActif] = useState(true);
|
||||||
@@ -101,7 +101,7 @@ export default function AdminQuestionnaireEdit() {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (questionnaire) {
|
if (questionnaire) {
|
||||||
setNom(questionnaire.nom);
|
setTitre(questionnaire.titre);
|
||||||
setType(questionnaire.type);
|
setType(questionnaire.type);
|
||||||
setDescription(questionnaire.description || "");
|
setDescription(questionnaire.description || "");
|
||||||
setActif(questionnaire.actif);
|
setActif(questionnaire.actif);
|
||||||
@@ -130,8 +130,8 @@ export default function AdminQuestionnaireEdit() {
|
|||||||
const handleSubmit = (e: React.FormEvent) => {
|
const handleSubmit = (e: React.FormEvent) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
if (!nom.trim()) {
|
if (!titre.trim()) {
|
||||||
toast.error("Le nom du questionnaire est obligatoire");
|
toast.error("Le titre du questionnaire est obligatoire");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -141,7 +141,7 @@ export default function AdminQuestionnaireEdit() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const data = {
|
const data = {
|
||||||
nom,
|
titre,
|
||||||
type,
|
type,
|
||||||
description: description || undefined,
|
description: description || undefined,
|
||||||
actif,
|
actif,
|
||||||
@@ -208,11 +208,11 @@ export default function AdminQuestionnaireEdit() {
|
|||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent className="space-y-4">
|
<CardContent className="space-y-4">
|
||||||
<div>
|
<div>
|
||||||
<Label htmlFor="nom">Nom du questionnaire *</Label>
|
<Label htmlFor="titre">Titre du questionnaire *</Label>
|
||||||
<Input
|
<Input
|
||||||
id="nom"
|
id="titre"
|
||||||
value={nom}
|
value={titre}
|
||||||
onChange={(e) => setNom(e.target.value)}
|
onChange={(e) => setTitre(e.target.value)}
|
||||||
placeholder="Ex: Questionnaire de satisfaction Manager Itinova"
|
placeholder="Ex: Questionnaire de satisfaction Manager Itinova"
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -90,7 +90,7 @@ export default function AdminQuestionnaires() {
|
|||||||
<div className="flex justify-between items-start">
|
<div className="flex justify-between items-start">
|
||||||
<div className="flex-1">
|
<div className="flex-1">
|
||||||
<div className="flex items-center gap-2 mb-2">
|
<div className="flex items-center gap-2 mb-2">
|
||||||
<CardTitle>{questionnaire.nom}</CardTitle>
|
<CardTitle>{questionnaire.titre}</CardTitle>
|
||||||
{getTypeBadge(questionnaire.type)}
|
{getTypeBadge(questionnaire.type)}
|
||||||
{!questionnaire.actif && (
|
{!questionnaire.actif && (
|
||||||
<Badge variant="outline" className="bg-gray-100">
|
<Badge variant="outline" className="bg-gray-100">
|
||||||
|
|||||||
@@ -688,7 +688,7 @@ export const appRouter = router({
|
|||||||
formateurNom: formateur.nom,
|
formateurNom: formateur.nom,
|
||||||
apprenantNom: apprenant.nom,
|
apprenantNom: apprenant.nom,
|
||||||
apprenantPrenom: apprenant.prenom,
|
apprenantPrenom: apprenant.prenom,
|
||||||
apprenantFonction: apprenant.fonction,
|
apprenantFonction: apprenant.fonction || '',
|
||||||
formationNom: formation.nom,
|
formationNom: formation.nom,
|
||||||
sequenceNom: sequence.nom,
|
sequenceNom: sequence.nom,
|
||||||
nbInscrits: nbInscritsApres,
|
nbInscrits: nbInscritsApres,
|
||||||
@@ -797,7 +797,7 @@ export const appRouter = router({
|
|||||||
formateurNom: formateur.nom,
|
formateurNom: formateur.nom,
|
||||||
apprenantNom: apprenant.nom,
|
apprenantNom: apprenant.nom,
|
||||||
apprenantPrenom: apprenant.prenom,
|
apprenantPrenom: apprenant.prenom,
|
||||||
apprenantFonction: apprenant.fonction,
|
apprenantFonction: apprenant.fonction || '',
|
||||||
formationNom: formation.nom,
|
formationNom: formation.nom,
|
||||||
sequenceNom: sequence.nom,
|
sequenceNom: sequence.nom,
|
||||||
nbInscrits: nbInscritsApres,
|
nbInscrits: nbInscritsApres,
|
||||||
@@ -1338,7 +1338,7 @@ export const appRouter = router({
|
|||||||
apprenantEmail: apprenant.email,
|
apprenantEmail: apprenant.email,
|
||||||
apprenantPrenom: apprenant.prenom,
|
apprenantPrenom: apprenant.prenom,
|
||||||
apprenantNom: apprenant.nom,
|
apprenantNom: apprenant.nom,
|
||||||
apprenantFonction: apprenant.fonction,
|
apprenantFonction: apprenant.fonction || '',
|
||||||
formationNom: formation.nom,
|
formationNom: formation.nom,
|
||||||
sequenceNom: sequence.nom,
|
sequenceNom: sequence.nom,
|
||||||
dates: dates.map((d: any) => ({
|
dates: dates.map((d: any) => ({
|
||||||
@@ -1354,7 +1354,7 @@ export const appRouter = router({
|
|||||||
apprenantEmail: apprenant.email,
|
apprenantEmail: apprenant.email,
|
||||||
apprenantPrenom: apprenant.prenom,
|
apprenantPrenom: apprenant.prenom,
|
||||||
apprenantNom: apprenant.nom,
|
apprenantNom: apprenant.nom,
|
||||||
apprenantFonction: apprenant.fonction,
|
apprenantFonction: apprenant.fonction || '',
|
||||||
formationNom: formation.nom,
|
formationNom: formation.nom,
|
||||||
sequenceNom: sequence.nom,
|
sequenceNom: sequence.nom,
|
||||||
dates: dates.map((d: any) => ({
|
dates: dates.map((d: any) => ({
|
||||||
@@ -1421,7 +1421,7 @@ export const appRouter = router({
|
|||||||
|
|
||||||
upsert: adminProcedure
|
upsert: adminProcedure
|
||||||
.input(z.object({
|
.input(z.object({
|
||||||
provider: z.string(),
|
provider: z.enum(["resend", "smtp", "simulation"]),
|
||||||
apiKey: z.string().nullable(),
|
apiKey: z.string().nullable(),
|
||||||
fromEmail: z.string().email(),
|
fromEmail: z.string().email(),
|
||||||
fromName: z.string(),
|
fromName: z.string(),
|
||||||
@@ -1591,7 +1591,7 @@ export const appRouter = router({
|
|||||||
|
|
||||||
create: adminProcedure
|
create: adminProcedure
|
||||||
.input(z.object({
|
.input(z.object({
|
||||||
nom: z.string(),
|
titre: z.string(),
|
||||||
type: z.enum(["satisfaction", "evaluation_pre", "evaluation_post"]),
|
type: z.enum(["satisfaction", "evaluation_pre", "evaluation_post"]),
|
||||||
description: z.string().optional(),
|
description: z.string().optional(),
|
||||||
formationId: z.number().nullable().optional(),
|
formationId: z.number().nullable().optional(),
|
||||||
@@ -1608,7 +1608,7 @@ export const appRouter = router({
|
|||||||
update: adminProcedure
|
update: adminProcedure
|
||||||
.input(z.object({
|
.input(z.object({
|
||||||
id: z.number(),
|
id: z.number(),
|
||||||
nom: z.string().optional(),
|
titre: z.string().optional(),
|
||||||
type: z.enum(["satisfaction", "evaluation_pre", "evaluation_post"]).optional(),
|
type: z.enum(["satisfaction", "evaluation_pre", "evaluation_post"]).optional(),
|
||||||
description: z.string().optional(),
|
description: z.string().optional(),
|
||||||
formationId: z.number().nullable().optional(),
|
formationId: z.number().nullable().optional(),
|
||||||
@@ -1766,7 +1766,6 @@ export const appRouter = router({
|
|||||||
questionnaireId: envoi.questionnaireId,
|
questionnaireId: envoi.questionnaireId,
|
||||||
apprenantId: envoi.apprenantId,
|
apprenantId: envoi.apprenantId,
|
||||||
sequenceId: envoi.sequenceId,
|
sequenceId: envoi.sequenceId,
|
||||||
complete: true,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Enregistrer chaque réponse
|
// Enregistrer chaque réponse
|
||||||
@@ -1791,7 +1790,7 @@ export const appRouter = router({
|
|||||||
|
|
||||||
await notifyOwner({
|
await notifyOwner({
|
||||||
title: 'Nouvelle réponse au questionnaire',
|
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) {
|
} catch (error) {
|
||||||
console.error('[Notification] Erreur lors de l\'envoi de la notification:', error);
|
console.error('[Notification] Erreur lors de l\'envoi de la notification:', error);
|
||||||
|
|||||||
Reference in New Issue
Block a user