diff --git a/prisma/dev.db b/prisma/dev.db index e69bfe3..81efa10 100644 Binary files a/prisma/dev.db and b/prisma/dev.db differ diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 96558e2..2c7d121 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -46,6 +46,7 @@ model Evaluation { id String @id @default(cuid()) candidateName String candidateRole String + candidateTeam String? // équipe du candidat evaluatorName String evaluationDate DateTime templateId String diff --git a/prisma/seed.ts b/prisma/seed.ts index 58319cb..96032c6 100644 --- a/prisma/seed.ts +++ b/prisma/seed.ts @@ -175,9 +175,9 @@ async function main() { }); const candidates = [ - { name: "Alice Chen", role: "Senior ML Engineer", evaluator: "Jean Dupont" }, - { name: "Bob Martin", role: "Data Scientist", evaluator: "Marie Curie" }, - { name: "Carol White", role: "AI Product Manager", evaluator: "Jean Dupont" }, + { name: "Alice Chen", role: "Senior ML Engineer", team: "Cars Front", evaluator: "Jean Dupont" }, + { name: "Bob Martin", role: "Data Scientist", team: "Cars Front", evaluator: "Marie Curie" }, + { name: "Carol White", role: "AI Product Manager", team: "Cars Data", evaluator: "Jean Dupont" }, ]; for (let i = 0; i < candidates.length; i++) { @@ -186,6 +186,7 @@ async function main() { data: { candidateName: c.name, candidateRole: c.role, + candidateTeam: c.team, evaluatorName: c.evaluator, evaluationDate: new Date(2025, 1, 15 + i), templateId: template.id, diff --git a/src/app/api/evaluations/[id]/route.ts b/src/app/api/evaluations/[id]/route.ts index 45dbe49..d752799 100644 --- a/src/app/api/evaluations/[id]/route.ts +++ b/src/app/api/evaluations/[id]/route.ts @@ -80,7 +80,7 @@ export async function PUT( const { id } = await params; const body = await req.json(); - const { candidateName, candidateRole, evaluatorName, evaluationDate, status, findings, recommendations, dimensionScores } = body; + const { candidateName, candidateRole, candidateTeam, evaluatorName, evaluationDate, status, findings, recommendations, dimensionScores } = body; const existing = await prisma.evaluation.findUnique({ where: { id } }); if (!existing) { @@ -90,8 +90,12 @@ export async function PUT( const updateData: Record = {}; if (candidateName != null) updateData.candidateName = candidateName; if (candidateRole != null) updateData.candidateRole = candidateRole; + if (candidateTeam !== undefined) updateData.candidateTeam = candidateTeam || null; if (evaluatorName != null) updateData.evaluatorName = evaluatorName; - if (evaluationDate != null) updateData.evaluationDate = new Date(evaluationDate); + if (evaluationDate != null) { + const d = new Date(evaluationDate); + if (!isNaN(d.getTime())) updateData.evaluationDate = d; + } if (status != null) updateData.status = status; if (findings != null) updateData.findings = findings; if (recommendations != null) updateData.recommendations = recommendations; @@ -106,14 +110,12 @@ export async function PUT( }); } - const evaluation = await prisma.evaluation.update({ - where: { id }, - data: updateData, - include: { - template: { include: { dimensions: { orderBy: { orderIndex: "asc" } } } }, - dimensionScores: { include: { dimension: true } }, - }, - }); + if (Object.keys(updateData).length > 0) { + await prisma.evaluation.update({ + where: { id }, + data: updateData as Record, + }); + } if (dimensionScores && Array.isArray(dimensionScores)) { for (const ds of dimensionScores) { @@ -157,7 +159,8 @@ export async function PUT( return NextResponse.json(updated); } catch (e) { console.error(e); - return NextResponse.json({ error: "Failed to update evaluation" }, { status: 500 }); + const msg = e instanceof Error ? e.message : "Failed to update evaluation"; + return NextResponse.json({ error: msg }, { status: 500 }); } } diff --git a/src/app/api/evaluations/route.ts b/src/app/api/evaluations/route.ts index f9eb385..c0d81a1 100644 --- a/src/app/api/evaluations/route.ts +++ b/src/app/api/evaluations/route.ts @@ -29,7 +29,7 @@ export async function GET(req: NextRequest) { export async function POST(req: NextRequest) { try { const body = await req.json(); - const { candidateName, candidateRole, evaluatorName, evaluationDate, templateId } = body; + const { candidateName, candidateRole, candidateTeam, evaluatorName, evaluationDate, templateId } = body; if (!candidateName || !candidateRole || !evaluatorName || !evaluationDate || !templateId) { return NextResponse.json( @@ -50,6 +50,7 @@ export async function POST(req: NextRequest) { data: { candidateName, candidateRole, + candidateTeam: candidateTeam || null, evaluatorName, evaluationDate: new Date(evaluationDate), templateId, diff --git a/src/app/api/export/pdf/route.ts b/src/app/api/export/pdf/route.ts index 5dbc7e2..b85b209 100644 --- a/src/app/api/export/pdf/route.ts +++ b/src/app/api/export/pdf/route.ts @@ -28,7 +28,8 @@ export async function GET(req: NextRequest) { doc.setFontSize(18); doc.text("Évaluation Maturité IA Gen", 14, 20); doc.setFontSize(10); - doc.text(`Candidat : ${evaluation.candidateName} | Rôle : ${evaluation.candidateRole}`, 14, 28); + const teamStr = evaluation.candidateTeam ? ` | Équipe : ${evaluation.candidateTeam}` : ""; + doc.text(`Candidat : ${evaluation.candidateName} | Rôle : ${evaluation.candidateRole}${teamStr}`, 14, 28); doc.text(`Évaluateur : ${evaluation.evaluatorName} | Date : ${format(evaluation.evaluationDate, "yyyy-MM-dd")}`, 14, 34); doc.text(`Modèle : ${evaluation.template?.name ?? ""} | Statut : ${evaluation.status === "submitted" ? "Soumise" : "Brouillon"}`, 14, 40); diff --git a/src/app/evaluations/[id]/page.tsx b/src/app/evaluations/[id]/page.tsx index a07080d..776bcf6 100644 --- a/src/app/evaluations/[id]/page.tsx +++ b/src/app/evaluations/[id]/page.tsx @@ -32,6 +32,7 @@ interface Evaluation { id: string; candidateName: string; candidateRole: string; + candidateTeam?: string | null; evaluatorName: string; evaluationDate: string; templateId: string; @@ -70,10 +71,10 @@ export default function EvaluationDetailPage() { const tmpl = templatesData.find((t: { id: string }) => t.id === evalData.templateId); if (tmpl?.dimensions?.length) { const dimMap = new Map(tmpl.dimensions.map((d: { id: string }) => [d.id, d])); - evalData.template.dimensions = evalData.template.dimensions.map((d: { id: string }) => ({ - ...d, - suggestedQuestions: d.suggestedQuestions ?? dimMap.get(d.id)?.suggestedQuestions, - })); + evalData.template.dimensions = evalData.template.dimensions.map((d: { id: string; suggestedQuestions?: string | null }) => ({ + ...d, + suggestedQuestions: d.suggestedQuestions ?? dimMap.get(d.id)?.suggestedQuestions, + })); } } } catch { @@ -117,26 +118,32 @@ export default function EvaluationDetailPage() { const scores = e.dimensionScores.map((ds) => ds.dimensionId === dimensionId ? { ...ds, ...data } : ds ); - return { ...e, dimensionScores: scores }; + const next = { ...e, dimensionScores: scores }; + if (data.score !== undefined) { + setTimeout(() => handleSave(next, { skipRefresh: true }), 0); + } + return next; }); }; - const handleSave = async () => { - if (!evaluation) return; + const handleSave = async (evalOverride?: Evaluation | null, options?: { skipRefresh?: boolean }) => { + const toSave = evalOverride ?? evaluation; + if (!toSave) return; setSaving(true); try { const res = await fetch(`/api/evaluations/${id}`, { method: "PUT", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ - candidateName: evaluation.candidateName, - candidateRole: evaluation.candidateRole, - evaluatorName: evaluation.evaluatorName, - evaluationDate: evaluation.evaluationDate, - status: evaluation.status, - findings: evaluation.findings, - recommendations: evaluation.recommendations, - dimensionScores: (evaluation.dimensionScores ?? []).map((ds) => ({ + candidateName: toSave.candidateName, + candidateRole: toSave.candidateRole, + candidateTeam: toSave.candidateTeam ?? null, + evaluatorName: toSave.evaluatorName, + evaluationDate: typeof toSave.evaluationDate === "string" ? toSave.evaluationDate : new Date(toSave.evaluationDate).toISOString(), + status: toSave.status, + findings: toSave.findings, + recommendations: toSave.recommendations, + dimensionScores: (toSave.dimensionScores ?? []).map((ds) => ({ dimensionId: ds.dimensionId, evaluationId: id, score: ds.score, @@ -148,11 +155,14 @@ export default function EvaluationDetailPage() { }), }); if (res.ok) { - fetchEval(); + if (!options?.skipRefresh) fetchEval(); } else { - const data = await res.json(); - alert(data.error ?? "Save failed"); + const data = await res.json().catch(() => ({})); + alert(data.error ?? `Save failed (${res.status})`); } + } catch (err) { + console.error("Save error:", err); + alert("Erreur lors de la sauvegarde"); } finally { setSaving(false); } @@ -203,11 +213,15 @@ export default function EvaluationDetailPage() {

- {evaluation.candidateName} / {evaluation.candidateRole} + {evaluation.candidateName} + {evaluation.candidateTeam && ( + ({evaluation.candidateTeam}) + )} + / {evaluation.candidateRole}

+
+ + onChange("candidateTeam", e.target.value)} + className={inputClass} + disabled={disabled} + placeholder="Cars Front" + /> +