fix(growth): fix add button and form submission

- Fix + Add button to properly open form (was closing immediately)
- Add error handling and saving state to form
- Add visual feedback (Saving... text, error messages)
- Clear form state properly when opening

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Manohar Gupta 2026-05-17 13:58:04 +05:30
parent 318b277e44
commit abbaaf8874

View file

@ -64,6 +64,8 @@ export default function GrowthPage() {
// Chart state
const [chartMetric, setChartMetric] = useState<"weight" | "height" | "head">("weight");
const [saving, setSaving] = useState(false);
const [saveError, setSaveError] = useState<string | null>(null);
// Goals state
const [goal, setGoal] = useState<Goal>({});
@ -101,37 +103,66 @@ export default function GrowthPage() {
};
const handleAdd = async () => {
if (!childId || (!weight && !height && !headCircumference)) return;
await fetch("/api/growth", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
childId,
measuredAt: new Date(measuredAt).toISOString(),
weightKg: weight ? parseFloat(weight) : null,
heightCm: height ? parseFloat(height) : null,
headCircumferenceCm: headCircumference ? parseFloat(headCircumference) : null,
}),
});
resetForm();
fetchGrowthData();
if (!childId || (!weight && !height && !headCircumference)) {
setSaveError("Please enter at least one measurement");
return;
}
setSaving(true);
setSaveError(null);
try {
const res = await fetch("/api/growth", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
childId,
measuredAt: new Date(measuredAt).toISOString(),
weightKg: weight ? parseFloat(weight) : null,
heightCm: height ? parseFloat(height) : null,
headCircumferenceCm: headCircumference ? parseFloat(headCircumference) : null,
}),
});
if (!res.ok) {
const err = await res.json();
setSaveError(err.error || "Failed to save");
return;
}
resetForm();
fetchGrowthData();
} catch (e: any) {
setSaveError(e.message || "Failed to save");
} finally {
setSaving(false);
}
};
const handleEdit = async (id: string) => {
await fetch("/api/growth", {
method: "PUT",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
id,
measuredAt: new Date(measuredAt).toISOString(),
weightKg: weight ? parseFloat(weight) : null,
heightCm: height ? parseFloat(height) : null,
headCircumferenceCm: headCircumference ? parseFloat(headCircumference) : null,
}),
});
setEditingId(null);
resetForm();
fetchGrowthData();
setSaving(true);
setSaveError(null);
try {
const res = await fetch("/api/growth", {
method: "PUT",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
id,
measuredAt: new Date(measuredAt).toISOString(),
weightKg: weight ? parseFloat(weight) : null,
heightCm: height ? parseFloat(height) : null,
headCircumferenceCm: headCircumference ? parseFloat(headCircumference) : null,
}),
});
if (!res.ok) {
const err = await res.json();
setSaveError(err.error || "Failed to update");
return;
}
setEditingId(null);
resetForm();
fetchGrowthData();
} catch (e: any) {
setSaveError(e.message || "Failed to update");
} finally {
setSaving(false);
}
};
const handleDelete = async (id: string) => {
@ -330,7 +361,7 @@ export default function GrowthPage() {
<button onClick={() => setShowGoals(!showGoals)} className="p-2 text-sm bg-gray-200 dark:bg-gray-700 rounded-lg" title="Set goals">
🎯
</button>
<button onClick={() => { setShowAdd(!showAdd); setEditingId(null); resetForm(); }} className="p-2 bg-rose-400 text-white rounded-lg">
<button onClick={() => { setShowAdd(!showAdd); setEditingId(null); setWeight(""); setHeight(""); setHeadCircumference(""); setMeasuredAt(new Date().toISOString().split("T")[0]); }} className="p-2 bg-rose-400 text-white rounded-lg">
+ Add
</button>
</div>
@ -547,6 +578,9 @@ export default function GrowthPage() {
{showAdd && (
<div className="mx-4 mb-4 p-4 bg-white dark:bg-gray-800 rounded-xl space-y-3">
<h3 className="font-semibold">{editingId ? "Edit Record" : "Add New Measurement"}</h3>
{saveError && (
<div className="p-2 bg-red-100 dark:bg-red-900 text-red-600 rounded-lg text-sm">{saveError}</div>
)}
<input
type="date"
value={measuredAt}
@ -580,16 +614,16 @@ export default function GrowthPage() {
<div className="flex gap-2">
{editingId ? (
<>
<button onClick={() => handleEdit(editingId)} className="flex-1 p-3 bg-rose-400 text-white rounded-xl">
Update
<button onClick={() => handleEdit(editingId)} disabled={saving} className="flex-1 p-3 bg-rose-400 text-white rounded-xl disabled:opacity-50">
{saving ? "Saving..." : "Update"}
</button>
<button onClick={resetForm} className="flex-1 p-3 bg-gray-200 dark:bg-gray-700 rounded-xl">
Cancel
</button>
</>
) : (
<button onClick={handleAdd} className="w-full p-3 bg-rose-400 text-white rounded-xl">
Save
<button onClick={() => handleAdd()} disabled={saving} className="w-full p-3 bg-rose-400 text-white rounded-xl disabled:opacity-50">
{saving ? "Saving..." : "Save"}
</button>
)}
</div>