Add candidateTeam field to evaluations; update related components and API endpoints for consistency

This commit is contained in:
Julien Froidefond
2026-02-20 09:22:12 +01:00
parent f0c5d768db
commit 9fcceb2649
11 changed files with 79 additions and 38 deletions

View File

@@ -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() {
<div className="space-y-6">
<div className="flex flex-wrap items-center justify-between gap-4">
<h1 className="font-mono text-base font-medium text-zinc-800 dark:text-zinc-100">
{evaluation.candidateName} <span className="text-zinc-500">/</span> {evaluation.candidateRole}
{evaluation.candidateName}
{evaluation.candidateTeam && (
<span className="text-zinc-500"> ({evaluation.candidateTeam})</span>
)}
<span className="text-zinc-500"> / </span> {evaluation.candidateRole}
</h1>
<div className="flex gap-2">
<button
onClick={handleSave}
onClick={() => handleSave()}
disabled={saving}
className="rounded border border-zinc-300 dark:border-zinc-600 bg-zinc-100 dark:bg-zinc-700 px-3 py-1.5 font-mono text-xs text-zinc-700 dark:text-zinc-300 hover:bg-zinc-200 dark:hover:bg-zinc-700 disabled:opacity-50"
>
@@ -233,6 +247,7 @@ export default function EvaluationDetailPage() {
<CandidateForm
candidateName={evaluation.candidateName}
candidateRole={evaluation.candidateRole}
candidateTeam={evaluation.candidateTeam ?? ""}
evaluatorName={evaluation.evaluatorName}
evaluationDate={evaluation.evaluationDate.split("T")[0]}
templateId={evaluation.templateId}