diff --git a/drizzle/0011_user_phone.sql b/drizzle/0011_user_phone.sql new file mode 100644 index 0000000..8f6d84c --- /dev/null +++ b/drizzle/0011_user_phone.sql @@ -0,0 +1,2 @@ +-- Add optional phone number to users (collected at onboarding / profile). +ALTER TABLE users ADD COLUMN IF NOT EXISTS phone text; diff --git a/drizzle/meta/_journal.json b/drizzle/meta/_journal.json index 6bf24a7..e679df7 100644 --- a/drizzle/meta/_journal.json +++ b/drizzle/meta/_journal.json @@ -78,6 +78,13 @@ "when": 1749139200000, "tag": "0010_error_events", "breakpoints": true + }, + { + "idx": 11, + "version": "7", + "when": 1780000000000, + "tag": "0011_user_phone", + "breakpoints": true } ] } \ No newline at end of file diff --git a/src/app/(app)/onboarding/page.tsx b/src/app/(app)/onboarding/page.tsx index 7a90f41..dda0ac5 100644 --- a/src/app/(app)/onboarding/page.tsx +++ b/src/app/(app)/onboarding/page.tsx @@ -55,6 +55,7 @@ export default function OnboardingPage() { const [form, setForm] = useState({ familyName: "", memberName: "", + phone: "", childName: "", birthDate: "", sex: "" as "male" | "female" | "other", @@ -167,6 +168,10 @@ export default function OnboardingPage() { setForm({ ...form, memberName: e.target.value })} placeholder="Mama" /> + setForm({ ...form, phone: e.target.value })} + placeholder="+91 98765 43210" /> +

For important reminders & updates about your baby

diff --git a/src/app/(app)/profile/page.tsx b/src/app/(app)/profile/page.tsx index 7e57238..07935f2 100644 --- a/src/app/(app)/profile/page.tsx +++ b/src/app/(app)/profile/page.tsx @@ -21,6 +21,7 @@ export default function ProfilePage() { const [name, setName] = useState(""); const [email, setEmail] = useState(""); + const [phone, setPhone] = useState(""); const [avatarUrl, setAvatarUrl] = useState(null); const [loading, setLoading] = useState(true); const [saving, setSaving] = useState(false); @@ -35,6 +36,7 @@ export default function ProfilePage() { if (data.user) { setName(data.user.name || ""); setEmail(data.user.email || ""); + setPhone(data.user.phone || ""); setAvatarUrl(data.user.avatarUrl || null); } setLoading(false); @@ -108,7 +110,7 @@ export default function ProfilePage() { const res = await fetch("/api/auth/profile", { method: "POST", headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ name }), + body: JSON.stringify({ name, phone }), }); const data = await res.json(); setSaveMsg(data.success ? "Saved!" : data.error || "Save failed"); @@ -192,6 +194,18 @@ export default function ProfilePage() { /> +
+ + setPhone(e.target.value)} + className="w-full px-3 py-2.5 bg-gray-50 dark:bg-gray-700 rounded-xl border border-gray-200 dark:border-gray-600 text-sm dark:text-white focus:outline-none focus:ring-2 focus:ring-rose-300" + placeholder="+91 98765 43210" + /> +

For important reminders & updates (optional)

+
+
15) { + return NextResponse.json({ error: "Enter a valid phone number" }, { status: 400 }); + } + normalizedPhone = cleaned; + } + } + const cookieStore = await cookies(); const sessionToken = cookieStore.get("tia_session")?.value; @@ -84,13 +102,20 @@ export async function POST(request: Request) { return NextResponse.json({ error: "Invalid session" }, { status: 401 }); } - // Update user name - await sql` - UPDATE users SET name = ${name}, updated_at = NOW() - WHERE id = ${session.user_id} - `; + // Update user name (+ phone only when the field was sent) + if (normalizedPhone !== undefined) { + await sql` + UPDATE users SET name = ${name}, phone = ${normalizedPhone}, updated_at = NOW() + WHERE id = ${session.user_id} + `; + } else { + await sql` + UPDATE users SET name = ${name}, updated_at = NOW() + WHERE id = ${session.user_id} + `; + } - return NextResponse.json({ success: true, name }); + return NextResponse.json({ success: true, name, phone: normalizedPhone ?? undefined }); } catch (error) { console.error("Profile update error:", error); return NextResponse.json({ error: String(error) }, { status: 500 }); diff --git a/src/app/api/debug-migration/route.ts b/src/app/api/debug-migration/route.ts index 36ace09..8c85908 100644 --- a/src/app/api/debug-migration/route.ts +++ b/src/app/api/debug-migration/route.ts @@ -101,6 +101,8 @@ export async function POST(req: Request) { `CREATE INDEX IF NOT EXISTS idx_error_events_created ON error_events (created_at DESC)`, `CREATE INDEX IF NOT EXISTS idx_error_events_source ON error_events (source)`, `CREATE INDEX IF NOT EXISTS idx_error_events_message ON error_events (message)`, + // 0011 — optional user phone number + `ALTER TABLE users ADD COLUMN IF NOT EXISTS phone text`, ]; const results: string[] = []; diff --git a/src/app/api/onboarding/route.ts b/src/app/api/onboarding/route.ts index 2bb8814..5bcc6bd 100644 --- a/src/app/api/onboarding/route.ts +++ b/src/app/api/onboarding/route.ts @@ -22,9 +22,23 @@ export async function POST(request: Request) { const userId = sessions[0].user_id; const body = await request.json(); - const { familyName, memberName, childName, birthDate, sex } = body; + const { familyName, memberName, phone, childName, birthDate, sex } = body; try { + // Save the parent's name + optional phone onto their user record. + // Phone: keep leading + then digits; store null when blank/invalid. + let normalizedPhone: string | null = null; + if (phone && typeof phone === "string") { + const cleaned = phone.trim().replace(/[^\d+]/g, "").replace(/(?!^)\+/g, ""); + const digits = cleaned.replace(/\D/g, ""); + if (digits.length >= 8 && digits.length <= 15) normalizedPhone = cleaned; + } + await sql` + UPDATE users + SET name = COALESCE(${memberName || null}, name), phone = ${normalizedPhone}, updated_at = NOW() + WHERE id = ${userId} + `; + // Create family const familyId = crypto.randomUUID(); await sql.unsafe( diff --git a/src/db/schema/auth.ts b/src/db/schema/auth.ts index 14f750e..74edccb 100644 --- a/src/db/schema/auth.ts +++ b/src/db/schema/auth.ts @@ -14,6 +14,7 @@ export const users = pgTable("users", { email: text("email").notNull().unique(), emailVerified: timestamp("email_verified"), image: text("image"), + phone: text("phone"), createdAt: timestamp("created_at").defaultNow().notNull(), updatedAt: timestamp("updated_at").defaultNow().notNull(), passwordHash: varchar("password_hash", { length: 255 }),