feat(settings): move growth CSV export from growth header to settings page

- Remove 📥 export button from growth page header (less clutter)
- Add "Export Growth Data" row in settings with child name and CSV download
- Fetches growth records on demand, shows loading state while exporting

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Manohar Gupta 2026-05-18 23:24:28 +05:30
parent acd2adde7e
commit 5fe24b8c59
2 changed files with 46 additions and 8 deletions

View file

@ -345,13 +345,6 @@ export default function GrowthPage() {
<h1 className="text-sm font-semibold dark:text-white">Growth 📈</h1> <h1 className="text-sm font-semibold dark:text-white">Growth 📈</h1>
</div> </div>
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<button
onClick={exportCSV}
title="Export CSV"
className="p-2 rounded-lg text-gray-400 hover:text-gray-600 hover:bg-gray-100 dark:hover:bg-gray-700 transition-colors text-sm"
>
📥
</button>
<button <button
title="Set goals" title="Set goals"
onClick={() => { setShowGoals(g => !g); setShowAdd(false); setEditingId(null); }} onClick={() => { setShowGoals(g => !g); setShowAdd(false); setEditingId(null); }}

View file

@ -27,7 +27,8 @@ interface Invite {
export default function SettingsPage() { export default function SettingsPage() {
const router = useRouter(); const router = useRouter();
const { theme, mode, setMode } = useTheme(); const { theme, mode, setMode } = useTheme();
const { tier, memberCount, familyId, children, familyName: providerFamilyName } = useFamily(); const { tier, memberCount, familyId, children, familyName: providerFamilyName, childId, child } = useFamily();
const [exporting, setExporting] = useState(false);
const [themeOpen, setThemeOpen] = useState(false); const [themeOpen, setThemeOpen] = useState(false);
const [inviteOpen, setInviteOpen] = useState(false); const [inviteOpen, setInviteOpen] = useState(false);
const [familyOpen, setFamilyOpen] = useState(true); const [familyOpen, setFamilyOpen] = useState(true);
@ -62,6 +63,34 @@ export default function SettingsPage() {
} }
}, [familyId]); }, [familyId]);
const exportGrowthCSV = async () => {
if (!childId) return;
setExporting(true);
try {
const res = await fetch(`/api/growth?childId=${childId}`);
const data = await res.json();
const records = data.growth || [];
if (records.length === 0) { alert("No growth records to export."); return; }
const headers = ["Date", "Weight (kg)", "Height (cm)", "Head (cm)", "Notes"];
const rows = records.map((r: { measured_at: string; weight_kg: number | null; height_cm: number | null; head_circumference_cm: number | null; notes: string | null }) => [
new Date(r.measured_at).toLocaleDateString(),
r.weight_kg ?? "",
r.height_cm ?? "",
r.head_circumference_cm ?? "",
r.notes ?? "",
]);
const csv = [headers, ...rows].map(row => row.join(",")).join("\n");
const blob = new Blob([csv], { type: "text/csv" });
const url = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = url;
a.download = `${child?.name || "child"}_growth_${new Date().toISOString().split("T")[0]}.csv`;
a.click();
URL.revokeObjectURL(url);
} catch { alert("Export failed. Please try again."); }
setExporting(false);
};
const savePedPhone = async () => { const savePedPhone = async () => {
setPedSaving(true); setPedSaving(true);
await fetch("/api/family", { await fetch("/api/family", {
@ -315,6 +344,22 @@ export default function SettingsPage() {
</Link> </Link>
</div> </div>
{/* Export Data */}
<button
onClick={exportGrowthCSV}
disabled={exporting || !childId}
className="w-full flex items-center justify-between p-4 bg-white dark:bg-gray-800 rounded-xl hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors disabled:opacity-50"
>
<div className="flex items-center gap-3">
<span className="text-xl">📥</span>
<div>
<div className="font-medium text-left">Export Growth Data</div>
<div className="text-xs text-gray-400 text-left">Download {child?.name ? `${child.name}'s` : "growth"} records as CSV</div>
</div>
</div>
<span className="text-gray-400 text-sm">{exporting ? "…" : "→"}</span>
</button>
{/* App Version */} {/* App Version */}
<div className="p-4 bg-white dark:bg-gray-800 rounded-xl mt-4"> <div className="p-4 bg-white dark:bg-gray-800 rounded-xl mt-4">
<div className="font-medium">App Version</div> <div className="font-medium">App Version</div>