"use client"; import { useState } from "react"; import type { RegisterInput } from "@/lib/foreshadow/board"; interface RegisterFormProps { projectId: string; busy: boolean; onRegister: (input: RegisterInput) => Promise; } const EMPTY = { code: "", title: "", plantedAt: "", content: "", expectedCloseFrom: "", expectedCloseTo: "", importance: "", }; // 登记新伏笔(UX §6.8)。受控表单 → 组装 RegisterInput;成功后清空。 export function RegisterForm({ projectId, busy, onRegister, }: RegisterFormProps) { const [open, setOpen] = useState(false); const [form, setForm] = useState({ ...EMPTY }); const set = (key: keyof typeof EMPTY, value: string): void => setForm((prev) => ({ ...prev, [key]: value })); const submit = async (): Promise => { const ok = await onRegister({ projectId, code: form.code, title: form.title, plantedAt: toInt(form.plantedAt), content: form.content || null, expectedCloseFrom: toInt(form.expectedCloseFrom), expectedCloseTo: toInt(form.expectedCloseTo), importance: form.importance || null, }); if (ok) { setForm({ ...EMPTY }); setOpen(false); } }; if (!open) { return ( ); } const canSubmit = form.code.trim().length > 0 && form.title.trim().length > 0 && !busy; return (
{ e.preventDefault(); void submit(); }} className="rounded border border-line bg-panel p-4" >
set("code", e.target.value)} className={inputCls} /> set("title", e.target.value)} className={inputCls} /> set("plantedAt", e.target.value)} className={inputCls} /> set("expectedCloseFrom", e.target.value)} className={inputCls} /> set("expectedCloseTo", e.target.value)} className={inputCls} /> set("importance", e.target.value)} placeholder="主线/支线" className={inputCls} /> set("content", e.target.value)} className={inputCls} />
); } const inputCls = "w-full rounded border border-line bg-bg px-2 py-1 text-sm text-ink focus:border-cinnabar focus:outline-none"; interface FieldProps { label: string; id: string; span2?: boolean; span3?: boolean; children: React.ReactNode; } function Field({ label, id, span2, span3, children }: FieldProps) { const span = span3 ? "col-span-2 sm:col-span-3" : span2 ? "col-span-2" : ""; return (
{children}
); } function toInt(raw: string): number | null { const trimmed = raw.trim(); if (trimmed.length === 0) return null; const n = Number.parseInt(trimmed, 10); return Number.isInteger(n) ? n : null; }