"use client"; import { useMemo, useState } from "react"; import { useRouter } from "next/navigation"; import type { BoundaryQuestionSummary } from "@pipeline/shared"; import { ApiError, generateBoundaryQuestions, submitBoundaryQuestions, updateBoundaryQuestion } from "@/shared/pipeline-api"; import { buildBoundaryQuestionRows, validateRequiredAnswers, type BoundaryQuestionErrors, type BoundaryQuestionFormRow, } from "./model"; type BoundaryQuestionsEditorProps = { articleId: string; initialQuestions: readonly BoundaryQuestionSummary[]; }; export function BoundaryQuestionsEditor({ articleId, initialQuestions, }: BoundaryQuestionsEditorProps) { const router = useRouter(); const [rows, setRows] = useState(() => buildBoundaryQuestionRows(initialQuestions), ); const [errors, setErrors] = useState({}); const [formMessage, setFormMessage] = useState(""); const [isBusy, setBusy] = useState(false); const requiredProgress = useMemo(() => { const requiredRows = rows.filter((row) => row.isRequired); const answeredRows = requiredRows.filter((row) => row.answer.trim()); return `${answeredRows.length}/${requiredRows.length}`; }, [rows]); function updateRow(questionId: string, answer: string) { setRows((current) => current.map((row) => (row.id === questionId ? { ...row, answer } : row)), ); } async function handleGenerate() { setBusy(true); setFormMessage(""); try { const response = await generateBoundaryQuestions(articleId); setRows(buildBoundaryQuestionRows(response.questions)); setErrors({}); router.refresh(); } catch (error) { setFormMessage(error instanceof ApiError ? error.message : "Unable to generate questions"); } finally { setBusy(false); } } async function handleSave(row: BoundaryQuestionFormRow) { setBusy(true); setFormMessage(""); try { const response = await updateBoundaryQuestion(articleId, row.id, { answer: row.answer.trim() || null, }); setRows((current) => current.map((candidate) => candidate.id === row.id ? buildBoundaryQuestionRows([response.question])[0] : candidate, ), ); setErrors((current) => { const next = { ...current }; delete next[row.id]; return next; }); } catch (error) { setFormMessage(error instanceof ApiError ? error.message : "Unable to save answer"); } finally { setBusy(false); } } async function handleSubmit() { const nextErrors = validateRequiredAnswers(rows); if (Object.keys(nextErrors).length > 0) { setErrors(nextErrors); return; } setBusy(true); setFormMessage(""); try { await submitBoundaryQuestions(articleId); router.push(`/articles/${articleId}`); router.refresh(); } catch (error) { setFormMessage(error instanceof ApiError ? error.message : "Unable to submit answers"); } finally { setBusy(false); } } return (

Boundary questions

Required answers: {requiredProgress}

{formMessage ?

Error: {formMessage}

: null} {rows.length === 0 ? (

No boundary questions generated yet.

) : (
{rows.map((row) => (
{row.category.replaceAll("_", " ")}

{row.question}

{row.stateLabel}