// Form modal: create / edit / delete tasks and events. function Field({ label, children, hint }) { return (
{label}
{children} {hint &&
{hint}
}
); } function inputStyle() { return { width: "100%", padding: "12px 14px", background: "var(--surface)", border: "1px solid var(--line-2)", borderRadius: 12, fontSize: 16, color: "var(--ink)", outline: "none", }; } function Segmented({ options, value, onChange, color = "var(--terracotta)" }) { return (
{options.map(opt => { const active = opt.value === value; return ( ); })}
); } function CustomDateChips({ days, onChange }) { const [input, setInput] = React.useState(ymd(addDays(TODAY, 1))); const sorted = [...(days || [])].sort(); return (
{sorted.length === 0 && Aucune date — ajoutez-en ci-dessous.} {sorted.map(d => ( {fmtShort(fromYmd(d))} ))}
setInput(e.target.value)} style={{ ...inputStyle(), width: "auto" }} />
); } function TaskForm({ initial, onSave, onDelete, onClose }) { const blank = { id: "", kind: "task", title: "", date: ymd(TODAY), priority: "mid", status: "todo", recurrence: { kind: "none" }, notify: false, notes: "", overrides: {}, assignee: null, }; const [draft, setDraft] = React.useState({ ...blank, ...initial }); const isNew = !initial?.id; const set = (k, v) => setDraft(d => ({ ...d, [k]: v })); const submit = (e) => { e?.preventDefault?.(); if (!draft.title.trim()) return; const id = draft.id || `i_${Date.now().toString(36)}_${Math.random().toString(36).slice(2,6)}`; onSave({ ...draft, id }); onClose(); }; return (
{isNew ? "Création" : "Modification"}
{isNew ? "Nouvelle entrée" : draft.title || "Sans titre"}
set("kind", v)} options={[ { value: "task", label: "Tâche", icon: "check", color: KINDS.task.color }, { value: "appointment", label: "Rendez-vous", icon: "stethoscope", color: KINDS.appointment.color }, { value: "birthday", label: "Anniversaire", icon: "cake", color: KINDS.birthday.color }, { value: "family", label: "Famille", icon: "users", color: KINDS.family.color }, { value: "event", label: "Événement", icon: "party", color: KINDS.event.color }, ]} /> set("title", e.target.value)} style={inputStyle()} />
set("date", e.target.value)} style={inputStyle()} />
set("priority", v)} options={[ { value: "low", label: "Faible", icon: "flag", color: PRIORITIES.low.color }, { value: "mid", label: "Moyenne", icon: "flag", color: PRIORITIES.mid.color }, { value: "high", label: "Élevée", icon: "flag", color: PRIORITIES.high.color }, ]} />
{draft.kind === "task" && (draft.recurrence?.kind || "none") === "none" && !isNew && ( set("status", v)} options={Object.entries(STATES).map(([k, s]) => ({ value: k, label: s.label, color: s.color }))} /> )} set("recurrence", { ...(draft.recurrence || {}), kind: v, days: v === "custom" ? (draft.recurrence?.days || []) : undefined })} options={[ { value: "none", label: "Une fois", icon: "dot" }, { value: "daily", label: "Quotidien", icon: "sun" }, { value: "weekly", label: "Hebdo", icon: "calendar"}, { value: "monthly", label: "Mensuel", icon: "moon" }, { value: "yearly", label: "Annuel", icon: "star" }, { value: "custom", label: "Sur mesure", icon: "sparkle"}, ]} /> {draft.recurrence?.kind === "custom" && ( set("recurrence", { ...draft.recurrence, days })} /> )} set("assignee", v === "both" ? null : v)} options={[ { value: "both", label: "Les deux", icon: "users" }, { value: "baptiste", label: "Baptiste", icon: "dot" }, { value: "imelda", label: "Imelda", icon: "dot" }, ]} />