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 }),