"use client"; import { useState, useEffect, useCallback } from "react"; import Link from "next/link"; import { useRouter } from "next/navigation"; import { updateEvaluation, deleteEvaluation, fetchEvaluation } from "@/actions/evaluations"; import { CandidateForm } from "@/components/CandidateForm"; import { DimensionCard } from "@/components/DimensionCard"; import { RadarChart } from "@/components/RadarChart"; import { ExportModal } from "@/components/ExportModal"; import { ShareModal } from "@/components/ShareModal"; import { ConfirmModal } from "@/components/ConfirmModal"; import { generateFindings, generateRecommendations, computeAverageScore } from "@/lib/export-utils"; interface Dimension { id: string; slug: string; title: string; rubric: string; suggestedQuestions?: string | null; } interface DimensionScore { id: string; dimensionId: string; score: number | null; justification: string | null; examplesObserved: string | null; confidence: string | null; candidateNotes: string | null; dimension: Dimension; } interface Evaluation { id: string; candidateName: string; candidateRole: string; candidateTeam?: string | null; evaluatorName: string; evaluatorId?: string | null; evaluationDate: string; templateId: string; template: { id: string; name: string; dimensions: Dimension[] }; status: string; findings: string | null; recommendations: string | null; dimensionScores: DimensionScore[]; sharedWith?: { id: string; user: { id: string; email: string; name: string | null } }[]; isPublic?: boolean; } interface EvaluationEditorProps { id: string; initialEvaluation: Evaluation; templates: { id: string; name: string; dimensions?: { id: string; suggestedQuestions?: string | null }[] }[]; users: { id: string; email: string; name: string | null }[]; } export function EvaluationEditor({ id, initialEvaluation, templates, users }: EvaluationEditorProps) { const router = useRouter(); const [evaluation, setEvaluation] = useState(initialEvaluation); const [saving, setSaving] = useState(false); const [exportOpen, setExportOpen] = useState(false); const [shareOpen, setShareOpen] = useState(false); const [deleteConfirmOpen, setDeleteConfirmOpen] = useState(false); const [collapseAllTrigger, setCollapseAllTrigger] = useState(0); const fetchEval = useCallback(async () => { const result = await fetchEvaluation(id); if (result.success && result.data) { const d = result.data; setEvaluation({ ...d, dimensionScores: d.dimensionScores ?? [] } as Evaluation); } }, [id]); // Draft backup to localStorage (debounced) useEffect(() => { if (!evaluation || !id) return; const t = setTimeout(() => { try { localStorage.setItem( `eval-draft-${id}`, JSON.stringify({ ...evaluation, evaluationDate: evaluation.evaluationDate }) ); } catch { /* ignore */ } }, 2000); return () => clearTimeout(t); }, [evaluation, id]); const handleFormChange = (field: string, value: string) => { setEvaluation((e) => (e ? { ...e, [field]: value } : null!)); }; const handleScoreChange = (dimensionId: string, data: Partial) => { setEvaluation((e) => { if (!e) return null!; const existing = e.dimensionScores.find((ds) => ds.dimensionId === dimensionId); const dim = e.template?.dimensions?.find((d) => d.id === dimensionId); const scores = existing ? e.dimensionScores.map((ds) => ds.dimensionId === dimensionId ? { ...ds, ...data } : ds ) : [ ...e.dimensionScores, { id: `temp-${dimensionId}`, dimensionId, score: (data as { score?: number }).score ?? null, justification: (data as { justification?: string }).justification ?? null, examplesObserved: (data as { examplesObserved?: string }).examplesObserved ?? null, confidence: (data as { confidence?: string }).confidence ?? null, candidateNotes: (data as { candidateNotes?: string }).candidateNotes ?? null, dimension: dim ?? { id: dimensionId, slug: "", title: "", rubric: "" }, }, ]; const next = { ...e, dimensionScores: scores }; if (data.score !== undefined) { setTimeout(() => handleSave(next, { skipRefresh: true }), 0); } return next; }); }; const handleSave = async (evalOverride?: Evaluation | null, options?: { skipRefresh?: boolean }) => { const toSave = evalOverride ?? evaluation; if (!toSave) return; setSaving(true); try { const result = await updateEvaluation(id, { 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, isPublic: toSave.isPublic ?? false, dimensionScores: (toSave.dimensionScores ?? []).map((ds) => ({ dimensionId: ds.dimensionId, evaluationId: id, score: ds.score, justification: ds.justification, examplesObserved: ds.examplesObserved, confidence: ds.confidence, candidateNotes: ds.candidateNotes, })), }); if (result.success) { if (!options?.skipRefresh) fetchEval(); } else { alert(result.error); } } catch (err) { console.error("Save error:", err); alert("Erreur lors de la sauvegarde"); } finally { setSaving(false); } }; const handleGenerateFindings = () => { const findings = generateFindings(evaluation.dimensionScores ?? []); const recommendations = generateRecommendations(evaluation.dimensionScores ?? []); setEvaluation((e) => (e ? { ...e, findings, recommendations } : null!)); }; const allFives = evaluation?.dimensionScores?.every( (ds) => ds.score === 5 && (!ds.justification || ds.justification.trim() === "") ); const showAllFivesWarning = allFives && evaluation?.status === "submitted"; const dimensions = evaluation.template?.dimensions ?? []; const dimensionScores = evaluation.dimensionScores ?? []; const scoreMap = new Map(dimensionScores.map((ds) => [ds.dimensionId, ds])); const radarData = dimensions .filter((dim) => !(dim.title ?? "").startsWith("[Optionnel]")) .map((dim) => { const ds = scoreMap.get(dim.id); const score = ds?.score; if (score == null) return null; const title = dim.title ?? ""; const s = Number(score); if (Number.isNaN(s) || s < 0 || s > 5) return null; return { dimension: title.length > 12 ? title.slice(0, 12) + "…" : title, score: s, fullMark: 5, }; }) .filter((d): d is { dimension: string; score: number; fullMark: number } => d != null); const avgScore = computeAverageScore(dimensionScores); const templatesForForm = templates.map((t) => ({ id: t.id, name: t.name })); return (

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

{showAllFivesWarning && (
⚠ Tous les scores = 5 sans justification
)}

Session

Dimensions

{dimensions.map((dim, i) => (
))}

Synthèse

Moyenne {avgScore.toFixed(1)}/5