);
}
function TrashBtn({ onClick, title = 'Remove' }) {
const [h, setH] = uSS(false);
return (
);
}
function AddRowBtn({ onClick, children }) {
return ;
}
function Settings() {
const S = useHaven();
const d = S.doc;
const [confirming, setConfirming] = uSS(false);
const [restorePreview, setRestorePreview] = uSS(null); // { doc } or { error: true }
const pickPlanDoc = async (e) => {
const fl = e.target.files && e.target.files[0];
e.target.value = '';
if (!fl) return;
try { const r = await Store.uploadFile(fl); S.setPlanDoc({ name: r.name, url: r.url }); }
catch (err) { alert("Couldn't store the file — is the NAS reachable?"); }
};
const pickBackup = (e) => {
const file = e.target.files && e.target.files[0];
e.target.value = '';
if (!file) return;
const r = new FileReader();
r.onload = () => {
try {
const doc = JSON.parse(r.result);
if (!doc || !doc.version || !doc.budget || !doc.child) throw new Error('not haven');
setRestorePreview({ doc });
} catch (err) { setRestorePreview({ error: true }); }
};
r.readAsText(file);
};
return (
{/* ── child & plan ── */}
The plan
S.setChild({ name: v })} />
S.setChild({ ndis: v })} />
S.setChild({ parent: v })} />
v && S.setPlanPeriod({ start: v })} />
v && S.setPlanPeriod({ end: v })} />
The plan dates drive the "on pace" marker, the days-remaining countdown and the review reminder — worth getting exactly right.
{/* ── budget ── */}
The amounts from the plan, per support area. "Spent so far" is for money already used before you started Haven — it keeps your remaining figures honest.>}>Budget
{d.budget.map((c) => (
{c.name}
{fmtMoney(c.budget)} total
Support areaYearly budgetSpent so far
{c.subs.map((s, i) => (
S.subPatch(c.key, i, { name: v })} />
S.subPatch(c.key, i, { budget: num(v) })} />
S.subPatch(c.key, i, { spent: num(v) })} />
S.subRemove(c.key, i)} />
{d.providers.length === 0 && No providers yet — add your therapists, programs and suppliers so logging a claim is one click.}
{d.providers.map((p) => (
S.listPatch('goals', g.id, { supports: v })} />
S.listPatch('goals', g.id, { note: v })} />
))}
{/* ── backup & restore ── */}
Backup & restore
The backup file holds every record — budgets, claims, invoices, workers, goals and notes. Your uploaded invoice files live separately in web/haven/api/files on the NAS; include that folder in your Hyper Backup.
{/* ── start fresh ── */}
Start fresh
Clears every sample claim, invoice, timesheet, provider, worker and goal, and zeroes the budgets — leaving a clean Haven ready for the real plan. {d.child.name ? `${d.child.name}'s` : 'The'} name and plan dates stay.
setConfirming(false)} width={480} title="Start fresh?" sub="A clean slate for real records.">
This removes all records currently in Haven on every computer. Your NAS keeps a daily snapshot in api/store/backups, so yesterday's data is still recoverable if you change your mind.
{/* ── restore preview ── */}
setRestorePreview(null)} width={480}
title={restorePreview && restorePreview.error ? "That's not a Haven backup" : 'Restore this backup?'}
sub={restorePreview && restorePreview.error ? null : 'Check it\u2019s the right one before anything changes.'}>
{restorePreview && restorePreview.error ? (
That file couldn't be read as a Haven backup. Look for a file named like haven-backup-2026-06-10.json.