322 lines
13 KiB
TypeScript
322 lines
13 KiB
TypeScript
"use client";
|
||
|
||
import { useState, useEffect, useRef, useCallback } from "react";
|
||
|
||
const SCREENS = [
|
||
{ label: "🏡 Home", key: "home" },
|
||
{ label: "💉 Vaccinations", key: "vaccines" },
|
||
{ label: "📸 Memories", key: "memories" },
|
||
] as const;
|
||
|
||
// ── Screen 0: Home / Quick Log ──────────────────────────────────
|
||
function HomeScreen() {
|
||
return (
|
||
<div className="w-full h-full bg-gradient-to-br from-rose-50 to-amber-50 flex flex-col overflow-hidden text-[10px]">
|
||
{/* Status bar */}
|
||
<div className="flex items-center justify-between px-4 pt-3 pb-1">
|
||
<span className="font-semibold text-gray-600 text-[9px]">9:41</span>
|
||
<div className="flex items-center gap-1 text-gray-500 text-[9px]">
|
||
<span>●●●</span>
|
||
<span>WiFi</span>
|
||
<span>🔋</span>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Top nav */}
|
||
<div className="flex items-center justify-between px-3 pb-1">
|
||
<div className="w-6 h-5 flex flex-col justify-between py-0.5">
|
||
{[0,1,2].map(i => <div key={i} className="h-0.5 w-4 bg-gray-500 rounded" />)}
|
||
</div>
|
||
<div className="flex items-center gap-2 text-[11px]">
|
||
<span>☀️</span>
|
||
<span className="text-red-500 font-bold text-[10px]">🆘</span>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Greeting */}
|
||
<div className="px-3 pb-2">
|
||
<p className="font-bold text-[13px] text-gray-900">Good morning 👋</p>
|
||
<p className="text-gray-500 text-[9px]">How is Arjun doing today?</p>
|
||
</div>
|
||
|
||
{/* Baby card */}
|
||
<div className="mx-3 mb-2 bg-white rounded-2xl shadow-sm px-3 py-2 flex items-center gap-3">
|
||
<div className="w-10 h-10 bg-rose-100 rounded-full flex items-center justify-center text-lg flex-shrink-0">
|
||
👶
|
||
</div>
|
||
<div className="flex-1">
|
||
<p className="font-semibold text-gray-900 text-[11px]">Arjun</p>
|
||
<p className="text-gray-500 text-[9px]">4 months</p>
|
||
</div>
|
||
<span className="text-gray-400 text-[11px]">→</span>
|
||
</div>
|
||
|
||
{/* Today summary */}
|
||
<div className="mx-3 mb-2 bg-white rounded-2xl shadow-sm">
|
||
<div className="grid grid-cols-3 divide-x divide-gray-100">
|
||
{[
|
||
{ icon: "🍼", label: "Feeds", count: 4, last: "2m ago" },
|
||
{ icon: "🚼", label: "Diapers", count: 3, last: "1h ago" },
|
||
{ icon: "😴", label: "Sleep", count: 1, last: "3h ago" },
|
||
].map(item => (
|
||
<div key={item.label} className="flex flex-col items-center py-2 px-1">
|
||
<span className="text-sm mb-0.5">{item.icon}</span>
|
||
<span className="text-[13px] font-bold text-gray-800 leading-tight">{item.count}</span>
|
||
<span className="text-[8px] text-gray-400">{item.label}</span>
|
||
<span className="text-[8px] text-rose-400 mt-0.5">{item.last}</span>
|
||
</div>
|
||
))}
|
||
</div>
|
||
</div>
|
||
|
||
{/* Quick Log */}
|
||
<div className="px-3 mb-2">
|
||
<p className="font-semibold text-gray-800 text-[10px] mb-1.5">Quick Log</p>
|
||
<div className="flex gap-2">
|
||
{[["🍼","Feed"],["😴","Sleep"],["🚼","Diaper"]].map(([icon, lbl]) => (
|
||
<div key={lbl} className="flex flex-col items-center bg-white rounded-xl shadow-sm py-2 px-3 gap-0.5">
|
||
<span className="text-lg">{icon}</span>
|
||
<span className="text-[9px] text-gray-600">{lbl}</span>
|
||
</div>
|
||
))}
|
||
</div>
|
||
</div>
|
||
|
||
{/* Recent Activity */}
|
||
<div className="px-3 flex-1 overflow-hidden">
|
||
<div className="flex justify-between items-center mb-1.5">
|
||
<p className="font-semibold text-gray-800 text-[10px]">Recent Activity</p>
|
||
<span className="text-rose-500 text-[9px]">See all →</span>
|
||
</div>
|
||
<div className="space-y-1.5">
|
||
{[
|
||
{ icon: "🍼", label: "Feed", detail: "90 ml", time: "10:42 AM" },
|
||
{ icon: "😴", label: "Sleep", detail: "2h", time: "8:30 AM" },
|
||
{ icon: "🚼", label: "Diaper",detail: "", time: "7:00 AM" },
|
||
].map(row => (
|
||
<div key={row.time} className="flex items-center justify-between bg-white rounded-xl px-2.5 py-1.5">
|
||
<div className="flex items-center gap-2">
|
||
<span className="text-sm">{row.icon}</span>
|
||
<div>
|
||
<p className="font-medium text-gray-800 text-[10px]">{row.label}</p>
|
||
<p className="text-gray-400 text-[8px]">{row.time}</p>
|
||
</div>
|
||
</div>
|
||
{row.detail && <span className="text-gray-400 text-[9px]">{row.detail}</span>}
|
||
</div>
|
||
))}
|
||
</div>
|
||
</div>
|
||
|
||
{/* Bottom tab bar strip */}
|
||
<div className="mt-auto border-t border-gray-100 bg-white flex justify-around py-1.5 px-2">
|
||
{[["🏡","Home"],["📋","Activity"],["✨","AI"],["☰","Menu"]].map(([icon, lbl]) => (
|
||
<div key={lbl} className="flex flex-col items-center gap-0.5">
|
||
<span className="text-sm">{icon}</span>
|
||
<span className={`text-[7px] ${lbl === "Home" ? "text-rose-500 font-semibold" : "text-gray-400"}`}>{lbl}</span>
|
||
</div>
|
||
))}
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
// ── Screen 1: Vaccinations ──────────────────────────────────────
|
||
function VaccinationsScreen() {
|
||
return (
|
||
<div className="w-full h-full bg-gradient-to-br from-rose-50 to-amber-50 flex flex-col overflow-hidden text-[10px]">
|
||
{/* Header */}
|
||
<div className="flex items-center gap-2 px-3 pt-8 pb-3">
|
||
<span className="text-gray-500 text-sm">←</span>
|
||
<p className="font-bold text-gray-900 text-[13px]">Medical</p>
|
||
</div>
|
||
|
||
{/* Tab bar */}
|
||
<div className="px-3 mb-2">
|
||
<div className="flex gap-1 bg-white rounded-xl p-1 shadow-sm">
|
||
{["Vaccines","Medicine","Allergies"].map((t, i) => (
|
||
<div key={t} className={`flex-1 text-center py-1 rounded-lg text-[8px] font-medium ${i === 0 ? "bg-rose-500 text-white" : "text-gray-500"}`}>
|
||
{t}
|
||
</div>
|
||
))}
|
||
</div>
|
||
</div>
|
||
|
||
<p className="px-3 font-semibold text-gray-800 text-[10px] mb-2">IAP Schedule</p>
|
||
|
||
{/* Status tabs */}
|
||
<div className="px-3 mb-2">
|
||
<div className="flex gap-1">
|
||
{[["Upcoming (3)","rose"],["Completed (4)","gray"],["Overdue (0)","gray"]].map(([lbl, col]) => (
|
||
<div key={lbl} className={`px-2 py-1 rounded-full text-[8px] font-medium border ${col === "rose" ? "bg-rose-100 text-rose-700 border-rose-200" : "bg-white text-gray-500 border-gray-200"}`}>
|
||
{lbl}
|
||
</div>
|
||
))}
|
||
</div>
|
||
</div>
|
||
|
||
{/* Vaccine rows */}
|
||
<div className="px-3 space-y-1.5 flex-1 overflow-hidden">
|
||
{[
|
||
{ name: "OPV-1", due: "15 Jul 2025", done: false },
|
||
{ name: "Pentavalent-1", due: "15 Jul 2025", done: false },
|
||
{ name: "PCV-1", due: "10 Jul 2025", done: true },
|
||
{ name: "Rota-1", due: "15 Jul 2025", done: false },
|
||
].map(v => (
|
||
<div key={v.name} className="bg-white rounded-xl px-2.5 py-2 flex items-center justify-between shadow-sm">
|
||
<div>
|
||
<p className="font-medium text-gray-900 text-[10px]">{v.name}</p>
|
||
<p className="text-gray-400 text-[8px]">Due: {v.due}</p>
|
||
</div>
|
||
{v.done
|
||
? <span className="text-green-500 font-bold text-sm">✓</span>
|
||
: <div className="px-2 py-1 bg-rose-400 text-white rounded-lg text-[8px] font-medium">Mark Given</div>
|
||
}
|
||
</div>
|
||
))}
|
||
</div>
|
||
|
||
{/* Telegram chip */}
|
||
<div className="mx-3 mt-2 mb-3 bg-sky-50 border border-sky-200 rounded-xl px-3 py-2 flex items-center gap-2">
|
||
<span className="text-sm">📲</span>
|
||
<p className="text-sky-700 text-[9px] font-medium">Telegram reminder in 3 days</p>
|
||
</div>
|
||
|
||
{/* Bottom tab strip */}
|
||
<div className="border-t border-gray-100 bg-white flex justify-around py-1.5 px-2">
|
||
{[["🏡","Home"],["📋","Activity"],["✨","AI"],["☰","Menu"]].map(([icon, lbl]) => (
|
||
<div key={lbl} className="flex flex-col items-center gap-0.5">
|
||
<span className="text-sm">{icon}</span>
|
||
<span className="text-[7px] text-gray-400">{lbl}</span>
|
||
</div>
|
||
))}
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
// ── Screen 2: Memories ──────────────────────────────────────────
|
||
function MemoriesScreen() {
|
||
return (
|
||
<div className="w-full h-full bg-gradient-to-br from-rose-50 to-amber-50 flex flex-col overflow-hidden text-[10px]">
|
||
{/* Header */}
|
||
<div className="flex items-center gap-2 px-3 pt-8 pb-3">
|
||
<span className="text-gray-500 text-sm">←</span>
|
||
<p className="font-bold text-gray-900 text-[13px]">Memories</p>
|
||
</div>
|
||
|
||
{/* Folder tabs */}
|
||
<div className="px-3 mb-3">
|
||
<div className="flex gap-1.5 overflow-hidden">
|
||
{[["🌟","All"],["👣","First Steps"],["🛁","Bath Time"],["🍼","Feeding"]].map(([emoji, lbl], i) => (
|
||
<div key={lbl} className={`flex-shrink-0 flex items-center gap-1 px-2 py-1 rounded-full text-[8px] font-medium border ${i === 0 ? "bg-rose-100 text-rose-700 border-rose-200" : "bg-white text-gray-500 border-gray-200"}`}>
|
||
<span>{emoji}</span><span>{lbl}</span>
|
||
</div>
|
||
))}
|
||
</div>
|
||
</div>
|
||
|
||
{/* Photo grid */}
|
||
<div className="px-3 grid grid-cols-2 gap-2 flex-1 overflow-hidden">
|
||
{[
|
||
{ bg: "bg-rose-200", emoji: "😊", caption: "First smile" },
|
||
{ bg: "bg-amber-200", emoji: "🛁", caption: "Bath time" },
|
||
{ bg: "bg-violet-200", emoji: "🏆", caption: "4 months!" },
|
||
{ bg: "bg-green-200", emoji: "👨👩👧", caption: "With Nani" },
|
||
].map(photo => (
|
||
<div key={photo.caption} className={`${photo.bg} rounded-xl flex flex-col items-center justify-center aspect-square gap-1`}>
|
||
<span className="text-2xl">{photo.emoji}</span>
|
||
<span className="text-[8px] text-gray-600 font-medium">{photo.caption}</span>
|
||
</div>
|
||
))}
|
||
</div>
|
||
|
||
{/* Upload row */}
|
||
<div className="mx-3 mt-2 mb-2">
|
||
<div className="border-2 border-dashed border-rose-200 rounded-xl flex items-center justify-center py-2 gap-2">
|
||
<span className="text-sm">📷</span>
|
||
<span className="text-rose-400 text-[9px] font-medium">Add memory</span>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Bottom tab strip */}
|
||
<div className="border-t border-gray-100 bg-white flex justify-around py-1.5 px-2">
|
||
{[["🏡","Home"],["📋","Activity"],["✨","AI"],["☰","Menu"]].map(([icon, lbl]) => (
|
||
<div key={lbl} className="flex flex-col items-center gap-0.5">
|
||
<span className="text-sm">{icon}</span>
|
||
<span className="text-[7px] text-gray-400">{lbl}</span>
|
||
</div>
|
||
))}
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
// ── Main component ──────────────────────────────────────────────
|
||
export function PhoneMockup() {
|
||
const [active, setActive] = useState(0);
|
||
const intervalRef = useRef<ReturnType<typeof setInterval> | null>(null);
|
||
|
||
const start = useCallback(() => {
|
||
if (intervalRef.current) clearInterval(intervalRef.current);
|
||
intervalRef.current = setInterval(() => {
|
||
setActive(prev => (prev + 1) % SCREENS.length);
|
||
}, 2500);
|
||
}, []);
|
||
|
||
const stop = useCallback(() => {
|
||
if (intervalRef.current) {
|
||
clearInterval(intervalRef.current);
|
||
intervalRef.current = null;
|
||
}
|
||
}, []);
|
||
|
||
useEffect(() => {
|
||
start();
|
||
return stop;
|
||
}, [start, stop]);
|
||
|
||
const screenComponents = [<HomeScreen key="home" />, <VaccinationsScreen key="vax" />, <MemoriesScreen key="mem" />];
|
||
|
||
return (
|
||
<div className="flex flex-col items-center gap-3">
|
||
{/* Phone */}
|
||
<div
|
||
className="relative"
|
||
style={{ width: 280 }}
|
||
onMouseEnter={stop}
|
||
onMouseLeave={start}
|
||
>
|
||
{/* Device shell */}
|
||
<div className="absolute inset-0 -m-3 rounded-[42px] bg-gray-900 shadow-2xl" />
|
||
|
||
{/* Screen */}
|
||
<div className="relative rounded-[34px] overflow-hidden bg-white" style={{ height: 580 }}>
|
||
{screenComponents.map((screen, i) => (
|
||
<div
|
||
key={i}
|
||
className={`absolute inset-0 transition-opacity duration-500 ${active === i ? "opacity-100" : "opacity-0 pointer-events-none"}`}
|
||
>
|
||
{screen}
|
||
</div>
|
||
))}
|
||
|
||
{/* Dot indicators */}
|
||
<div className="absolute bottom-3 left-0 right-0 flex justify-center gap-1.5 z-10">
|
||
{SCREENS.map((_, i) => (
|
||
<button
|
||
key={i}
|
||
onClick={() => { stop(); setActive(i); }}
|
||
className={`w-2 h-2 rounded-full transition-all duration-300 ${active === i ? "bg-rose-500 w-4" : "bg-rose-200"}`}
|
||
aria-label={`Show screen ${i + 1}`}
|
||
/>
|
||
))}
|
||
</div>
|
||
</div>
|
||
|
||
{/* Notch */}
|
||
<div className="absolute top-0 left-1/2 -translate-x-1/2 w-24 h-6 bg-gray-900 rounded-b-2xl z-10" />
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|