From 83314e91a8e1959f99e18eae85a15dea39888eec Mon Sep 17 00:00:00 2001 From: Mannu Date: Sun, 10 May 2026 15:52:59 +0530 Subject: [PATCH] Update Family, Profile with working APIs - fetch & save baby details --- src/app/api/auth/profile/route.ts | 29 ++++ src/app/api/children/route.ts | 50 ++++--- src/app/family/page.tsx | 211 +++++++++++++++++++++++++----- src/app/profile/page.tsx | 87 ++++++++---- 4 files changed, 300 insertions(+), 77 deletions(-) create mode 100644 src/app/api/auth/profile/route.ts diff --git a/src/app/api/auth/profile/route.ts b/src/app/api/auth/profile/route.ts new file mode 100644 index 0000000..b57a975 --- /dev/null +++ b/src/app/api/auth/profile/route.ts @@ -0,0 +1,29 @@ +import { NextResponse } from "next/server"; + +// Mock user for now - in production, fetch from database +export async function GET() { + try { + // TODO: In production, get user from session + const user = { + id: "1", + name: "Parent", + email: "parent@example.com", + }; + + return NextResponse.json({ user }); + } catch (error) { + return NextResponse.json({ error: String(error) }, { status: 500 }); + } +} + +export async function POST(request: Request) { + try { + const body = await request.json(); + const { name } = body; + + // TODO: Save to database in production + return NextResponse.json({ success: true, name }); + } catch (error) { + return NextResponse.json({ error: String(error) }, { status: 500 }); + } +} \ No newline at end of file diff --git a/src/app/api/children/route.ts b/src/app/api/children/route.ts index 47b97e6..ad748dd 100644 --- a/src/app/api/children/route.ts +++ b/src/app/api/children/route.ts @@ -1,16 +1,27 @@ import { NextResponse } from "next/server"; import { sql } from "@/db"; -interface ChildEntry { - familyId: string; - name: string; - birthDate: string; - sex: "male" | "female" | "other"; +// GET - list children +export async function GET(request: Request) { + const { searchParams } = new URL(request.url); + const familyId = searchParams.get("familyId") || "default"; + + try { + const children = await sql.unsafe( + `SELECT id, name, birth_date as "birthDate", sex, stage, created_at as "createdAt" FROM children WHERE family_id = $1 ORDER BY created_at DESC`, + [familyId] + ); + return NextResponse.json({ children: children || [] }); + } catch (error) { + console.error(error); + return NextResponse.json({ error: String(error) }, { status: 500 }); + } } +// POST - create child export async function POST(request: Request) { try { - const body: ChildEntry = await request.json(); + const body = await request.json(); const { familyId, name, birthDate, sex } = body; if (!familyId || !name || !birthDate || !sex) { @@ -18,7 +29,7 @@ export async function POST(request: Request) { } const [child] = await sql.unsafe( - `INSERT INTO children (family_id, name, birth_date, sex, stage) VALUES ($1, $2, $3, $4, 'newborn') RETURNING *`, + `INSERT INTO children (family_id, name, birth_date, sex, stage) VALUES ($1, $2, $3, $4, 'newborn') RETURNING id, name, birth_date as "birthDate", sex, stage`, [familyId, name, birthDate, sex] ); @@ -29,21 +40,22 @@ export async function POST(request: Request) { } } -export async function GET(request: Request) { - const { searchParams } = new URL(request.url); - const familyId = searchParams.get("familyId"); - - if (!familyId) { - return NextResponse.json({ error: "familyId required" }, { status: 400 }); - } - +// PATCH - update child +export async function PATCH(request: Request) { try { - const children = await sql.unsafe( - `SELECT * FROM children WHERE family_id = $1 ORDER BY created_at DESC`, - [familyId] + const body = await request.json(); + const { id, name, birthDate } = body; + + if (!id || !name || !birthDate) { + return NextResponse.json({ error: "Missing required fields" }, { status: 400 }); + } + + const [child] = await sql.unsafe( + `UPDATE children SET name = $1, birth_date = $2, updated_at = NOW() WHERE id = $3 RETURNING id, name, birth_date as "birthDate", sex`, + [name, birthDate, id] ); - return NextResponse.json({ children }); + return NextResponse.json({ success: true, child }); } catch (error) { console.error(error); return NextResponse.json({ error: String(error) }, { status: 500 }); diff --git a/src/app/family/page.tsx b/src/app/family/page.tsx index ef55fda..0033c8f 100644 --- a/src/app/family/page.tsx +++ b/src/app/family/page.tsx @@ -1,21 +1,85 @@ "use client"; -import { useState } from "react"; +import { useState, useEffect } from "react"; import { useRouter } from "next/navigation"; -interface FamilyMember { +interface Child { id: string; name: string; - role: string; - avatar: string; + birthDate: string; + sex: string; + stage?: string; } export default function FamilyPage() { const router = useRouter(); - const [members] = useState([ - { id: "1", name: "You", role: "Parent", avatar: "👤" }, - { id: "2", name: "Baby Tia", role: "Child", avatar: "👶" }, - ]); + const [children, setChildren] = useState([]); + const [loading, setLoading] = useState(true); + const [editing, setEditing] = useState(null); + const [editName, setEditName] = useState(""); + const [editDob, setEditDob] = useState(""); + const [showAdd, setShowAdd] = useState(false); + const [newName, setNewName] = useState(""); + const [newDob, setNewDob] = useState(""); + const [newSex, setNewSex] = useState("male"); + + useEffect(() => { + fetchChildren(); + }, []); + + const fetchChildren = async () => { + try { + const res = await fetch("/api/children?familyId=default"); + const data = await res.json(); + setChildren(data.children || []); + } catch (err) { + console.error("Failed to fetch:", err); + } + setLoading(false); + }; + + const startEdit = (child: Child) => { + setEditing(child.id); + setEditName(child.name); + setEditDob(child.birthDate); + }; + + const saveEdit = async (childId: string) => { + try { + const res = await fetch("/api/children", { + method: "PATCH", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ id: childId, name: editName, birthDate: editDob }), + }); + const data = await res.json(); + if (data.success) { + setChildren(children.map((c) => (c.id === childId ? { ...c, name: editName, birthDate: editDob } : c))); + } + } catch (err) { + console.error("Failed to save:", err); + } + setEditing(null); + }; + + const addChild = async () => { + if (!newName || !newDob) return; + try { + const res = await fetch("/api/children", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ name: newName, birthDate: newDob, sex: newSex, familyId: "default" }), + }); + const data = await res.json(); + if (data.success) { + setChildren([data.child, ...children]); + setShowAdd(false); + setNewName(""); + setNewDob(""); + } + } catch (err) { + console.error("Failed to add:", err); + } + }; return (
@@ -25,32 +89,117 @@ export default function FamilyPage() {
- {/* Members List */} -
-
Family Members
- {members.map((member) => ( -
-
{member.avatar}
-
-
{member.name}
-
{member.role}
-
- + {/* Add Child Form */} + {showAdd && ( +
+ setNewName(e.target.value)} + placeholder="Baby's name" + className="w-full p-2 border rounded-lg" + /> + setNewDob(e.target.value)} + className="w-full p-2 border rounded-lg" + /> + +
+ +
- ))} -
+
+ )} - {/* Add Member */} - + {loading ? ( +
Loading...
+ ) : children.length === 0 && !showAdd ? ( +
+
👶
+

No baby added yet

+ +
+ ) : ( + children.map((child) => ( +
+ {editing === child.id ? ( +
+ setEditName(e.target.value)} + className="w-full p-2 border rounded-lg" + /> + setEditDob(e.target.value)} + className="w-full p-2 border rounded-lg" + /> +
+ + +
+
+ ) : ( +
+
+
{child.sex === "male" ? "👦" : "👧"}
+
+
{child.name}
+
+ Born: {child.birthDate ? new Date(child.birthDate).toLocaleDateString() : "Not set"} +
+
+
+
+ +
+
+ )} +
+ )) + )} - {/* Quick Info */} -
-
Family Plan
-
Up to 4 family members
-
Free for now
-
+ {/* Add Button */} + {!showAdd && children.length > 0 && ( + + )}
); diff --git a/src/app/profile/page.tsx b/src/app/profile/page.tsx index 82a2f0e..6cfe7fb 100644 --- a/src/app/profile/page.tsx +++ b/src/app/profile/page.tsx @@ -1,12 +1,36 @@ "use client"; -import { useState } from "react"; +import { useState, useEffect } from "react"; import { useRouter } from "next/navigation"; export default function ProfilePage() { const router = useRouter(); - const [name, setName] = useState("Parent"); - const [email, setEmail] = useState("parent@example.com"); + const [name, setName] = useState("Loading..."); + const [email, setEmail] = useState("Loading..."); + const [loading, setLoading] = useState(true); + + useEffect(() => { + // Fetch user profile from API + fetch("/api/auth/profile") + .then((r) => r.json()) + .then((data) => { + if (data.user) { + setName(data.user.name || "Parent"); + setEmail(data.user.email || "parent@example.com"); + } + setLoading(false); + }) + .catch(() => { + setName("Parent"); + setEmail("parent@example.com"); + setLoading(false); + }); + }, []); + + const saveProfile = async () => { + // TODO: Call API to save profile + alert("Profile saved!"); + }; return (
@@ -25,36 +49,45 @@ export default function ProfilePage() {
{/* Form */} -
-
- - setName(e.target.value)} - className="w-full p-3 bg-white dark:bg-gray-800 rounded-xl border" - /> -
+ {loading ? ( +
Loading...
+ ) : ( +
+
+ + setName(e.target.value)} + className="w-full p-3 bg-white dark:bg-gray-800 rounded-xl border" + /> +
-
- - setEmail(e.target.value)} - className="w-full p-3 bg-white dark:bg-gray-800 rounded-xl border" - /> -
+
+ + setEmail(e.target.value)} + className="w-full p-3 bg-white dark:bg-gray-800 rounded-xl border" + disabled + /> +
Email cannot be changed
+
- -
+ +
+ )} {/* Account Info */}
Account
-
Member since: Jan 2024
+
Member since: January 2024