// run.jsx — Le Run d'autofill (écran « moment fort ») // // CÂBLAGE (2026-08-07, passe « enlever le static ») : l'écran ne raconte que // le run RÉEL. L'ancienne narration (formulaire nommé d'avance, GES inventés, // « 47 champs ») était un scénario écrit d'avance — crédible, // donc dangereux : elle décrivait un travail qui n'avait pas lieu. Désormais // chaque ligne dérive de l'état du moteur, et chaque compteur est un compte. // Sans runId : on le dit, on ne simule rien. const { useState: useStateR, useEffect: useEffectR } = React; // statut du moteur → fraction de la barre, quand progress_pct n'est pas fourni const STATUS_P = { pending: 0.05, parsing: 0.2, inferring: 0.5, filling: 0.85, awaiting_user: 1, awaiting_review: 1, done: 1, failed: 1, }; // ordre des phases, pour savoir ce qui est « déjà passé » const PHASE_ORDER = ["pending", "parsing", "inferring", "filling", "awaiting_user", "awaiting_review", "done"]; const after = (status, phase) => PHASE_ORDER.indexOf(status) >= PHASE_ORDER.indexOf(phase); /** La narration, dérivée de l'état réel — rien d'écrit d'avance. */ function buildLog(run, t) { if (!run) return []; const st = run.status; const missing = (run.missingFields || []).length; const lines = [{ icon: "doc", txt: t("J'ouvre votre document…") }]; if (run.detectedFields) lines.push({ icon: "scan", txt: t("{n} champs à remplir détectés.", { n: run.detectedFields }) }); if (after(st, "inferring")) lines.push({ icon: "auto", txt: t("Je puise dans votre profil d'entreprise et votre fiche projet.") }); if (after(st, "filling")) lines.push({ icon: "auto", txt: t("J'inscris ce que je sais déjà, sans rien inventer.") }); if (st === "awaiting_user" && missing) lines.push({ icon: "flag", txt: t("{n} champs ont besoin de vous.", { n: missing }) }); if (st === "awaiting_review") lines.push({ icon: "check", txt: t("Tout est rempli. À vous de relire et de confirmer.") }); if (st === "done") lines.push({ icon: "check", txt: t("{formulaire} rempli et prêt.") }); if (st === "failed") lines.push({ icon: "flag", txt: (run.error || t("Une étape a échoué. Vos réponses sont conservées.")) }); return lines; } function RunScreen({ onNext, onBack, runId }) { const { t } = useLang(); const [run] = useRunPolling(runId); // Sans run : un écran honnête, pas une simulation. if (!runId) { return (

{t("Téléversez d'abord un {formulaire} à l'étape précédente : c'est lui qui lance le travail.")}

{t("Retour")}
); } const status = run ? run.status : "pending"; const failed = status === "failed"; // « Terminé » = le moteur rend la main à l'humain, quelle que soit la porte. const done = ["awaiting_user", "awaiting_review", "done", "failed"].includes(status); const p = run && typeof run.progressPct === "number" && run.progressPct > 0 ? Math.min(1, run.progressPct / 100) : (STATUS_P[status] ?? 0.1); const nDetected = (run && run.detectedFields) || 0; const nToConfirm = run && run.missingFields ? run.missingFields.length : 0; const nAuto = nDetected ? Math.max(0, nDetected - nToConfirm) : 0; // Les compteurs n'apparaissent que quand le moteur les CONNAÎT. const detectes = after(status, "parsing") && nDetected ? nDetected : 0; const auto = done && !failed ? nAuto : 0; const confirmer = done && !failed ? nToConfirm : 0; const filledRows = done && !failed ? 9 : Math.round(p * 9); const visibleLogs = buildLog(run, t); // Bornes visuelles des 4 phases affichées (parsing → awaiting_user) const PHASE_BOUNDS = [[0, 0.25], [0.25, 0.6], [0.6, 0.9], [0.9, 1]]; let phaseIdx = PHASE_BOUNDS.findIndex(([a, b]) => p >= a && p < b); if (done) phaseIdx = 3; // Ce que le bouton propose dépend de la porte par laquelle le moteur sort. const nextLabel = failed ? t("Reprendre") : status === "awaiting_review" ? t("Relire et confirmer les {n} champs", { n: nDetected }) : status === "done" ? t("Voir le résultat") : done ? t("Confirmer les {n} derniers champs", { n: nToConfirm }) : t("Remplissage en cours…"); return (
{t("Étape {n} sur 6", { n: 4 })}

{failed ? t("Le remplissage s'est arrêté") : done ? t("Le formulaire est rempli") : t("L'assistant remplit votre {formulaire}…")}

{failed ? (run && run.error) || t("Une étape a échoué. Vos réponses sont conservées.") : done ? t("On a complété tout ce qu'on savait. Quelques champs ont besoin de votre confirmation.") : t("Le moteur travaille. Suivez l'avancement en direct ci-dessous.")}

{/* Barre de progression par phase */}
{RUN_PHASES.map((ph, i) => { const [a, b] = PHASE_BOUNDS[i]; const local = Math.max(0, Math.min(1, (p - a) / (b - a))); const isDone = p >= b; const isActive = phaseIdx === i && !isDone; return (
{isDone ? : {i + 1}} {t(ph.label)}
{t(ph.desc)}
); })}
{/* Compteurs — le repère central */}
{detectes}
{t("champs détectés dans le {formulaire}")}
{auto}
{t("remplis automatiquement pour vous")}
{confirmer}
{t("à confirmer ensemble")}
{/* Narration : dérivée de l'état du moteur */}

{!done && } {t("Ce que fait l'assistant")}

{visibleLogs.map((l, i) => (
{l.txt}
))} {!done && (
)}
{/* Aperçu du document */}

{t("Aperçu du {formulaire}")}

{done && !failed ? <> {t("Tous les champs connus sont remplis")} : failed ? null : <> {t("Remplissage en cours…")}}
{t("Retour")} {nextLabel}
); } Object.assign(window, { RunScreen });