From 7189fc766c76e49e7f37f77132c07aab3da9ca31 Mon Sep 17 00:00:00 2001 From: Mannu Date: Mon, 18 May 2026 10:24:43 +0530 Subject: [PATCH] refactor(ui): apply design system components across all pages Replace raw / + ); -} \ No newline at end of file +} diff --git a/src/app/admin/children/page.tsx b/src/app/admin/children/page.tsx index b403c3f..e727eb9 100644 --- a/src/app/admin/children/page.tsx +++ b/src/app/admin/children/page.tsx @@ -1,6 +1,7 @@ "use client"; import { useEffect, useState } from "react"; +import { Button, Input } from "@/components/ui"; interface Child { id: string; @@ -59,17 +60,14 @@ export default function AdminChildren() {

Children

{children.length} total children

- + - setSearch(e.target.value)} - className="w-full bg-gray-800 border border-gray-700 rounded-lg px-4 py-2 text-white" />
diff --git a/src/app/admin/families/page.tsx b/src/app/admin/families/page.tsx index f03146c..2eeca15 100644 --- a/src/app/admin/families/page.tsx +++ b/src/app/admin/families/page.tsx @@ -1,6 +1,7 @@ "use client"; import { useEffect, useState } from "react"; +import { Button, Input, Select, Badge } from "@/components/ui"; interface Member { id: string; @@ -135,39 +136,28 @@ export default function AdminFamilies() {

{families.length} total families

- - + +
{/* Filters */}
- setSearch(e.target.value)} - className="flex-1 bg-gray-800 border border-gray-700 rounded-lg px-4 py-2 text-white" + className="flex-1" /> - +
{/* Table */} @@ -192,19 +182,12 @@ export default function AdminFamilies() {
{family.id}
- - {family.tier} - + {family.tier} - + {family.childCount} @@ -219,25 +202,24 @@ export default function AdminFamilies() {
- setAddMember({familyId: family.id, email: e.target.value, role: "caregiver", name: ""})} - className="flex-1 bg-gray-700 border border-gray-600 rounded px-3 py-2 text-white" + className="flex-1" /> - - + +
{(family.members || []).map((m) => (
{m.email} ({m.role}) - +
))}
@@ -254,4 +236,4 @@ export default function AdminFamilies() { ); -} \ No newline at end of file +} diff --git a/src/app/admin/page.tsx b/src/app/admin/page.tsx index c07827b..4912345 100644 --- a/src/app/admin/page.tsx +++ b/src/app/admin/page.tsx @@ -1,6 +1,7 @@ "use client"; import { useEffect, useState } from "react"; +import { Select } from "@/components/ui"; interface Stats { overview: { @@ -59,15 +60,11 @@ export default function AdminDashboard() {

Dashboard

Platform overview and analytics

- setPeriod(e.target.value)}> - + {/* Overview Cards */} diff --git a/src/app/admin/settings/page.tsx b/src/app/admin/settings/page.tsx index 7109b19..5e1c042 100644 --- a/src/app/admin/settings/page.tsx +++ b/src/app/admin/settings/page.tsx @@ -1,6 +1,7 @@ "use client"; import { useState } from "react"; +import { Button, Input } from "@/components/ui"; interface Settings { proPrice: number; @@ -40,12 +41,12 @@ export default function AdminSettings() {
$ - setSettings({ ...settings, proPrice: Number(e.target.value) })} - className="flex-1 bg-gray-700 border border-gray-600 rounded-lg px-3 py-2" + className="flex-1" />
@@ -58,20 +59,18 @@ export default function AdminSettings() {
- setSettings({ ...settings, freeMaxChildren: Number(e.target.value) })} - className="w-full bg-gray-700 border border-gray-600 rounded-lg px-3 py-2" />
- setSettings({ ...settings, freeMaxMembers: Number(e.target.value) })} - className="w-full bg-gray-700 border border-gray-600 rounded-lg px-3 py-2" />
@@ -83,32 +82,27 @@ export default function AdminSettings() {
- setSettings({ ...settings, aiModel: e.target.value })} - className="w-full bg-gray-700 border border-gray-600 rounded-lg px-3 py-2" />
- setSettings({ ...settings, aiBaseUrl: e.target.value })} - className="w-full bg-gray-700 border border-gray-600 rounded-lg px-3 py-2" />
{/* Save */} - + ); -} \ No newline at end of file +} diff --git a/src/app/admin/support/page.tsx b/src/app/admin/support/page.tsx index 5f49522..19b9cac 100644 --- a/src/app/admin/support/page.tsx +++ b/src/app/admin/support/page.tsx @@ -1,6 +1,7 @@ "use client"; import { useEffect, useState } from "react"; +import { Button, Badge } from "@/components/ui"; interface Ticket { id: string; @@ -18,7 +19,6 @@ export default function AdminSupport() { const [loading, setLoading] = useState(true); const [statusFilter, setStatusFilter] = useState("all"); const [selectedTicket, setSelectedTicket] = useState(null); - const [replyMessage, setReplyMessage] = useState(""); useEffect(() => { fetchTickets(); @@ -49,6 +49,12 @@ export default function AdminSupport() { } }; + const priorityVariant = (p: string): "rose" | "warning" | "default" => + p === "urgent" ? "rose" : p === "high" ? "warning" : "default"; + + const statusVariant = (s: string): "rose" | "warning" | "default" => + s === "open" ? "rose" : s === "in_progress" ? "warning" : "default"; + if (loading) { return
Loading...
; } @@ -65,15 +71,14 @@ export default function AdminSupport() { {/* Filters */}
{["all", "open", "in_progress", "resolved", "closed"].map((status) => ( - + ))}
@@ -89,20 +94,8 @@ export default function AdminSupport() { }`} >
- - {ticket.priority} - - - {ticket.status.replace("_", " ")} - + {ticket.priority} + {ticket.status.replace("_", " ")}
{ticket.subject}
{ticket.email}
@@ -132,28 +125,19 @@ export default function AdminSupport() {
{selectedTicket.status === "open" && ( - + )} {selectedTicket.status === "in_progress" && ( - + )} {selectedTicket.status === "resolved" && ( - + )}
@@ -166,4 +150,4 @@ export default function AdminSupport() { ); -} \ No newline at end of file +} diff --git a/src/app/admin/users/page.tsx b/src/app/admin/users/page.tsx index aadf24c..237c6f9 100644 --- a/src/app/admin/users/page.tsx +++ b/src/app/admin/users/page.tsx @@ -1,6 +1,7 @@ "use client"; import { useEffect, useState } from "react"; +import { Button, Input, Select, Modal } from "@/components/ui"; interface User { id: string; @@ -25,6 +26,7 @@ export default function AdminUsers() { const [search, setSearch] = useState(""); const [showAdd, setShowAdd] = useState(false); const [showPassword, setShowPassword] = useState(null); + const [passwordValue, setPasswordValue] = useState(""); const [newUser, setNewUser] = useState({ email: "", name: "", familyId: "", role: "caregiver" }); useEffect(() => { @@ -98,6 +100,7 @@ export default function AdminUsers() { if (res.ok) { fetchUsers(); setShowPassword(null); + setPasswordValue(""); } } catch (err) { console.error("Failed to set password:", err); @@ -132,47 +135,45 @@ export default function AdminUsers() {

Users

{users.length} total users

- +
+ + +
{showAdd && (
- setNewUser({ ...newUser, email: e.target.value })} - className="flex-1 bg-gray-700 border border-gray-600 rounded px-3 py-2 text-white min-w-[200px]" + className="flex-1 min-w-[200px]" /> - setNewUser({ ...newUser, name: e.target.value })} - className="bg-gray-700 border border-gray-600 rounded px-3 py-2 text-white" /> - - + +
)} - setSearch(e.target.value)} - className="w-full bg-gray-800 border border-gray-700 rounded-lg px-4 py-2 text-white" />
@@ -195,32 +196,20 @@ export default function AdminUsers() { {user.familyName || "-"} - {user.hasPassword ? ( - - ) : ( - - )} + {user.createdAt?.slice(0, 10)} - + ))} @@ -231,37 +220,35 @@ export default function AdminUsers() { )}
- {/* Password Modal */} - {showPassword && ( -
-
-

Set Password

- -
- - -
+ { setShowPassword(null); setPasswordValue(""); }} + title="Set Password" + > +
+ setPasswordValue(e.target.value)} + /> +
+ +
- )} +
); -} \ No newline at end of file +} diff --git a/src/app/ai/page.tsx b/src/app/ai/page.tsx index cfb3239..788809a 100644 --- a/src/app/ai/page.tsx +++ b/src/app/ai/page.tsx @@ -2,6 +2,7 @@ import { useState, useEffect } from "react"; import { useFamily } from "../FamilyProvider"; +import { Button, Input, ConfirmDialog } from "@/components/ui"; interface AIChat { id: string; @@ -182,7 +183,7 @@ export default function AIChatPage() { ←

Chats

- +
{sessions.length === 0 ? ( @@ -237,9 +238,7 @@ export default function AIChatPage() {
🀱

Ask anything about your baby

Tap ☰ to see past chats, or just type below to start

- +
) : currentSession.messages.length === 0 ? (
@@ -278,38 +277,30 @@ export default function AIChatPage() { {/* Input */}
- setInput(e.target.value)} onKeyDown={e => e.key === "Enter" && !e.shiftKey && handleSend()} placeholder="Ask about your baby..." - className="flex-1 p-3 border dark:border-gray-600 rounded-xl dark:bg-gray-700 dark:text-white dark:placeholder-gray-400 text-sm focus:outline-none focus:ring-2 focus:ring-rose-300" disabled={loading} + className="flex-1" /> - +
- {/* Delete confirm modal */} - {deleteConfirm && ( -
-
-

Delete this conversation?

-

This can't be undone.

-
- - -
-
-
- )} + setDeleteConfirm(null)} + onConfirm={() => deleteConfirm && deleteSession(deleteConfirm)} + title="Delete this conversation?" + description="This can't be undone." + confirmLabel="Delete" + variant="danger" + /> ); } diff --git a/src/app/growth/page.tsx b/src/app/growth/page.tsx index 1e6df3b..bbf2c60 100644 --- a/src/app/growth/page.tsx +++ b/src/app/growth/page.tsx @@ -2,6 +2,7 @@ import { useState, useEffect, useRef } from "react"; import { useFamily } from "../FamilyProvider"; +import { Button, Card, Input, ConfirmDialog } from "@/components/ui"; import { WHO_BOY_WEIGHT, WHO_GIRL_WEIGHT, getAgeInMonthsFromBirth, getPercentile, type GrowthStandard } from "@/lib/growth-standards"; import { Chart as ChartJS, @@ -83,6 +84,7 @@ export default function GrowthPage() { const [chartMetric, setChartMetric] = useState<"weight" | "height" | "head">("weight"); const [saving, setSaving] = useState(false); const [saveError, setSaveError] = useState(null); + const [confirmDeleteId, setConfirmDeleteId] = useState(null); const [showWhoStandards, setShowWhoStandards] = useState(true); const [showChart, setShowChart] = useState(true); const [showHistory, setShowHistory] = useState(true); @@ -186,8 +188,8 @@ export default function GrowthPage() { }; const handleDelete = async (id: string) => { - if (!confirm("Delete this record?")) return; await fetch(`/api/growth?id=${id}`, { method: "DELETE" }); + setConfirmDeleteId(null); fetchGrowthData(); }; @@ -375,55 +377,33 @@ export default function GrowthPage() {

Growth πŸ“ˆ

- - - + + +
{/* Goals Card */} {showGoals && ( -
+

Set Growth Goals

-
- - setGoal({ ...goal, weightKg: parseFloat(e.target.value) || undefined })} - className="w-full p-2 border rounded-lg dark:bg-gray-700" - /> -
-
- - setGoal({ ...goal, heightCm: parseFloat(e.target.value) || undefined })} - className="w-full p-2 border rounded-lg dark:bg-gray-700" - /> -
+ setGoal({ ...goal, weightKg: parseFloat(e.target.value) || undefined })} + /> + setGoal({ ...goal, heightCm: parseFloat(e.target.value) || undefined })} + />
- - + +
-
+ )} {/* Latest Reading Card */} @@ -621,54 +601,20 @@ export default function GrowthPage() { {saveError && (
{saveError}
)} - setMeasuredAt(e.target.value)} - className="w-full p-3 border rounded-xl dark:bg-gray-700" - /> - setWeight(e.target.value)} - className="w-full p-3 border rounded-xl dark:bg-gray-700" - /> - setHeight(e.target.value)} - className="w-full p-3 border rounded-xl dark:bg-gray-700" - /> - setHeadCircumference(e.target.value)} - className="w-full p-3 border rounded-xl dark:bg-gray-700" - /> + setMeasuredAt(e.target.value)} /> + setWeight(e.target.value)} /> + setHeight(e.target.value)} /> + setHeadCircumference(e.target.value)} />
{editingId ? ( <> - - + + ) : ( <> - - + + )}
@@ -681,16 +627,16 @@ export default function GrowthPage() {

Loading...

) : growthData.length === 0 ? ( -
-
πŸ“
-

Track {child?.name}'s Growth

-

- Start logging weight, height, and head measurements to see how {child?.name} is growing compared to WHO standards. -

- -
+ +
+
πŸ“
+

Track {child?.name}'s Growth

+

+ Start logging weight, height, and head measurements to see how {child?.name} is growing compared to WHO standards. +

+ +
+
) : ( /* History List - Collapsible */
@@ -730,7 +676,7 @@ export default function GrowthPage() { ✏️
)} + + setConfirmDeleteId(null)} + onConfirm={() => confirmDeleteId && handleDelete(confirmDeleteId)} + title="Delete this record?" + description="This measurement will be permanently removed." + confirmLabel="Delete" + variant="danger" + /> ); } \ No newline at end of file diff --git a/src/app/login/page.tsx b/src/app/login/page.tsx index e0f7550..59ae5b7 100644 --- a/src/app/login/page.tsx +++ b/src/app/login/page.tsx @@ -2,6 +2,7 @@ import { useState } from "react"; import { useRouter } from "next/navigation"; +import { Button, Input, Card } from "@/components/ui"; export default function LoginPage() { const router = useRouter(); @@ -32,7 +33,6 @@ export default function LoginPage() { return; } - // Redirect based on response if (data.isNewUser) { router.push("/onboarding"); } else if (data.familyId) { @@ -40,7 +40,7 @@ export default function LoginPage() { } else { router.push("/onboarding"); } - } catch (err) { + } catch { setError("Something went wrong"); } finally { setLoading(false); @@ -49,46 +49,33 @@ export default function LoginPage() { return (
-
+

{mode === "login" ? "Welcome Back" : "Create Account"}

-
- - setEmail(e.target.value)} - className="w-full p-3 border rounded-xl dark:bg-gray-700 dark:text-white" - required - /> -
+ setEmail(e.target.value)} + required + /> -
- - setPassword(e.target.value)} - className="w-full p-3 border rounded-xl dark:bg-gray-700 dark:text-white" - required - minLength={4} - /> -
+ setPassword(e.target.value)} + required + minLength={4} + error={error || undefined} + /> - {error && ( -

{error}

- )} - - +

@@ -101,7 +88,7 @@ export default function LoginPage() { {mode === "login" ? "Sign Up" : "Sign In"}

-
+
); -} \ No newline at end of file +} diff --git a/src/app/medical/page.tsx b/src/app/medical/page.tsx index d069923..e759496 100644 --- a/src/app/medical/page.tsx +++ b/src/app/medical/page.tsx @@ -2,6 +2,7 @@ import { useState, useEffect } from "react"; import { useFamily } from "../FamilyProvider"; +import { Button, Card, Modal, Input, Select, Textarea, Badge } from "@/components/ui"; interface Medicine { id: string; @@ -992,83 +993,38 @@ const SUPPLEMENTS = [ {/* Log Dose Modal */} - {logDoseMedId && ( -
-
-

Log Dose

- setDoseAmount(e.target.value)} - placeholder="Amount given (e.g. 5ml)" - className="w-full p-2 border dark:border-gray-600 rounded-lg text-sm dark:bg-gray-700 dark:text-white" - /> - setDoseTime(e.target.value)} - className="w-full p-2 border dark:border-gray-600 rounded-lg text-sm dark:bg-gray-700 dark:text-white" - /> - setDoseNotes(e.target.value)} - placeholder="Notes (optional)" - className="w-full p-2 border dark:border-gray-600 rounded-lg text-sm dark:bg-gray-700 dark:text-white" - /> -
- - -
+ setLogDoseMedId(null)} title="Log Dose" maxWidth="sm"> +
+ setDoseAmount(e.target.value)} /> + setDoseTime(e.target.value)} /> + setDoseNotes(e.target.value)} /> +
+ +
- )} +
{/* Correction Modal */} - {correctDose && ( -
-
-

Edit Dose (Correction)

-

Original is kept. A correction record is added.

+ setCorrectDose(null)} title="Edit Dose (Correction)" maxWidth="sm"> +
+

Original is kept. A correction record is added.

+ {correctDose && (
Original: {correctDose.dose.amount_given || "β€”"} Β· {new Date(correctDose.dose.administered_at).toLocaleString()}
- setCorrectAmount(e.target.value)} - placeholder="Corrected amount" - className="w-full p-2 border dark:border-gray-600 rounded-lg text-sm dark:bg-gray-700 dark:text-white" - /> - setCorrectNotes(e.target.value)} - placeholder="Corrected notes" - className="w-full p-2 border dark:border-gray-600 rounded-lg text-sm dark:bg-gray-700 dark:text-white" - /> - setCorrectReason(e.target.value)} - placeholder="Reason for correction (optional)" - className="w-full p-2 border dark:border-gray-600 rounded-lg text-sm dark:bg-gray-700 dark:text-white" - /> -
- - -
+ )} + setCorrectAmount(e.target.value)} /> + setCorrectNotes(e.target.value)} /> + setCorrectReason(e.target.value)} /> +
+ +
- )} +
); } \ No newline at end of file diff --git a/src/app/onboarding/page.tsx b/src/app/onboarding/page.tsx index 0c22ad1..cfe43ca 100644 --- a/src/app/onboarding/page.tsx +++ b/src/app/onboarding/page.tsx @@ -2,6 +2,7 @@ import { useState, useEffect } from "react"; import { useRouter } from "next/navigation"; +import { Button, Card, Input, Select } from "@/components/ui"; const IAP_SCHEDULE = [ { name: "BCG", weeks: 0, milestone: "At Birth" }, @@ -155,38 +156,20 @@ export default function OnboardingPage() {
)} -
+ {/* Step 1 β€” Family info */} {step === 1 && ( <>

Your family

- - - + )} @@ -194,49 +177,35 @@ export default function OnboardingPage() { {step === 2 && ( <>

Your baby

- - -
{s.given && ( - setVaccStates(prev => ({ ...prev, [v.name]: { ...s, date: e.target.value } }))} - className="mt-2 w-full p-1.5 border rounded-lg text-sm" /> )}
@@ -302,23 +268,12 @@ export default function OnboardingPage() { )}
- - + +
)} -
+ ); diff --git a/src/app/page.tsx b/src/app/page.tsx index a3705a5..9fee7b1 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -5,6 +5,7 @@ import Link from "next/link"; import { useTheme } from "./ThemeProvider"; import { useFamily } from "./FamilyProvider"; import { useStageCheck, type BabyStage } from "@/hooks/useStageCheck"; +import { Button, Modal, Select, Input } from "@/components/ui"; const OFFLINE_QUEUE_KEY = "tia_offline_queue"; @@ -109,17 +110,41 @@ function LogModal({ type, childId, onClose }: { type: "feed" | "diaper" | "sleep setLoading(false); }; + const title = type === "feed" ? "Log Feed" : type === "diaper" ? "Log Diaper" : "Log Sleep"; return ( -
-
-

{type === "feed" && "Log Feed"}{type === "diaper" && "Log Diaper"}{type === "sleep" && "Log Sleep"}

- {type === "feed" && (<> setAmountMl(e.target.value)} className="w-full p-3 border dark:border-gray-600 rounded-xl mb-3 bg-white dark:bg-gray-700 dark:text-white dark:placeholder-gray-400" />)} - {type === "diaper" && ()} - {type === "sleep" && ()} - setNotes(e.target.value)} className="w-full p-3 border dark:border-gray-600 rounded-xl mb-4 bg-white dark:bg-gray-700 dark:text-white dark:placeholder-gray-400" /> -
+ +
+ {type === "feed" && ( + <> + + setAmountMl(e.target.value)} /> + + )} + {type === "diaper" && ( + + )} + {type === "sleep" && ( + + )} + setNotes(e.target.value)} /> +
+ + +
-
+ ); } diff --git a/src/app/settings/page.tsx b/src/app/settings/page.tsx index 283ae7d..bacd771 100644 --- a/src/app/settings/page.tsx +++ b/src/app/settings/page.tsx @@ -5,6 +5,7 @@ import Link from "next/link"; import { useRouter } from "next/navigation"; import { useTheme } from "../ThemeProvider"; import { useFamily } from "../FamilyProvider"; +import { Button, Card, Input, Select, Badge } from "@/components/ui"; interface Member { id: string; @@ -180,7 +181,7 @@ export default function SettingsPage() {
{tier === "free" && ( - + )} @@ -195,9 +196,7 @@ export default function SettingsPage() {
{member.name || member.email}
{member.email}
- - {member.role} - + {member.role} ))} @@ -256,28 +255,14 @@ export default function SettingsPage() { {/* Add invite form */} {canInvite && (
- setInviteEmail(e.target.value)} - placeholder="Email address" - className="w-full p-2 border rounded-lg text-sm" - /> - setInviteEmail(e.target.value)} placeholder="Email address" /> + - + +
)} @@ -301,15 +286,13 @@ export default function SettingsPage() {
{themeOptions.map((opt) => ( - + ))}
@@ -324,20 +307,8 @@ export default function SettingsPage() {

Shown on the emergency guide and in AI medical redirects.

- setPedPhone(e.target.value)} - placeholder="+91 98765 43210" - className="flex-1 p-2 border dark:border-gray-600 rounded-lg text-sm dark:bg-gray-700 dark:text-white" - /> - + setPedPhone(e.target.value)} placeholder="+91 98765 43210" className="flex-1" /> +
View Emergency Guide β†’