From db5e37a62e563aba16a5d7bcbf1f7f2f25ec02e2 Mon Sep 17 00:00:00 2001 From: Mohammad Umar Date: Fri, 25 Sep 2026 12:21:53 +0000 Subject: [PATCH] feat: landing page at /, dashboard overview moves to /dashboard Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- src/app/(dashboard)/{ => dashboard}/page.tsx | 0 src/app/globals.css | 14 + src/app/not-found.tsx | 2 +- src/app/page.tsx | 404 +++++++++++++++++++ src/app/setup/page.tsx | 2 +- src/components/layout/sidebar.tsx | 4 +- src/components/tour/tour-provider.tsx | 2 +- src/components/tour/tour-steps.tsx | 26 +- src/lib/supabase/middleware.ts | 6 +- 9 files changed, 439 insertions(+), 21 deletions(-) rename src/app/(dashboard)/{ => dashboard}/page.tsx (100%) create mode 100644 src/app/page.tsx diff --git a/src/app/(dashboard)/page.tsx b/src/app/(dashboard)/dashboard/page.tsx similarity index 100% rename from src/app/(dashboard)/page.tsx rename to src/app/(dashboard)/dashboard/page.tsx diff --git a/src/app/globals.css b/src/app/globals.css index cd06856..0cc5e43 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -76,6 +76,20 @@ --animate-tour-card-in: tour-card-in 0.3s cubic-bezier(0.2, 0.8, 0.2, 1); --animate-tour-nudge: tour-nudge 0.36s ease-in-out; --animate-tour-rise: tour-rise 0.5s cubic-bezier(0.2, 0.8, 0.2, 1) both; + + /* Landing page (app/page.tsx) */ + --animate-landing-float: landing-float 6s ease-in-out infinite; + --animate-landing-rise: tour-rise 0.7s cubic-bezier(0.2, 0.8, 0.2, 1) both; + --animate-landing-glow: landing-glow 8s ease-in-out infinite; +} + +@keyframes landing-float { + 0%, 100% { transform: translateY(0); } + 50% { transform: translateY(-10px); } +} +@keyframes landing-glow { + 0%, 100% { opacity: 0.55; transform: scale(1); } + 50% { opacity: 0.8; transform: scale(1.08); } } /* The ring breathing around what to click next */ diff --git a/src/app/not-found.tsx b/src/app/not-found.tsx index a399d77..5641518 100644 --- a/src/app/not-found.tsx +++ b/src/app/not-found.tsx @@ -16,7 +16,7 @@ export default function NotFound() {

- + diff --git a/src/app/page.tsx b/src/app/page.tsx new file mode 100644 index 0000000..9e2497b --- /dev/null +++ b/src/app/page.tsx @@ -0,0 +1,404 @@ +import Link from 'next/link' +import { + ArrowRight, BellRing, CalendarCheck, CheckCheck, FileText, HeartPulse, Languages, MessageCircle, + Moon, ShieldCheck, Sparkles, Stethoscope, UploadCloud, Activity, Smartphone, Lock, +} from 'lucide-react' +import { siteConfig } from '@/config/site' +import { ThemeToggle } from '@/components/theme/theme-toggle' + +export const metadata = { + title: { absolute: `${siteConfig.name} — Post-discharge care on WhatsApp` }, + description: + 'CareLoop keeps hospitals connected to patients after discharge: care plans, nightly check-ins and instant nurse escalation, all on WhatsApp, in the patient’s own language.', +} + +const STEPS = [ + { + icon: UploadCloud, + title: 'Drop the discharge letter', + body: 'The nurse uploads the PDF. AI reads medicines, follow-ups and warning signs into a form — nothing is sent yet.', + }, + { + icon: ShieldCheck, + title: 'Review and approve', + body: 'A clinician checks every detail and approves once. Nothing reaches the patient without a human sign-off.', + }, + { + icon: MessageCircle, + title: 'Care continues on WhatsApp', + body: 'The patient gets their care plan in their language, a check-in every night, and answers drawn only from their own instructions.', + }, +] + +const FEATURES = [ + { + icon: FileText, + title: 'Document-first intake', + body: 'Discharge PDFs become structured care plans in seconds — demographics, medications, appointments and red flags.', + }, + { + icon: Languages, + title: 'Speaks their language', + body: 'Arabic, English, Hindi, Tamil and Tagalog. Instructions patients actually understand, not just receive.', + }, + { + icon: Moon, + title: 'Nightly check-ins', + body: 'One gentle message each evening: medicines taken? How are you feeling? Adherence tracked automatically.', + }, + { + icon: BellRing, + title: 'Instant escalation', + body: 'Chest pain, breathlessness, a fever — warning signs raise a nurse alert the moment they are reported.', + }, + { + icon: CalendarCheck, + title: 'Follow-ups that happen', + body: 'Appointments are booked, confirmed and rescheduled right in the chat, so fewer patients slip through.', + }, + { + icon: Lock, + title: 'Built for hospitals', + body: 'Multi-tenant with row-level security, role-based access and a full audit trail of every conversation.', + }, +] + +const STATS = [ + { value: '0', label: 'apps for patients to install' }, + { value: '5', label: 'languages out of the box' }, + { value: '24/7', label: 'warning-sign monitoring' }, + { value: '1', label: 'click to approve and send' }, +] + +const LANGUAGES = [ + { word: 'مرحبا', name: 'Arabic' }, + { word: 'Hello', name: 'English' }, + { word: 'नमस्ते', name: 'Hindi' }, + { word: 'வணக்கம்', name: 'Tamil' }, + { word: 'Kumusta', name: 'Tagalog' }, +] + +function Logo() { + return ( + + + + + {siteConfig.name} + + ) +} + +function Bubble({ from, children, time }: { from: 'clinic' | 'patient'; children: React.ReactNode; time: string }) { + const patient = from === 'patient' + return ( +
+
+ {children} + + {time} + {patient && } + +
+
+ ) +} + +function PhoneMockup() { + return ( +
+
+
+
+ + + +
+

City Hospital Care

+

online

+
+
+
+ + Good evening Fatima. Did you take your evening medicines today? + + Yes, all + Not yet + + + Yes, all of them + Wonderful. How are you feeling tonight? + A bit short of breath when I lie down + + Thank you for telling us. A nurse has been notified and will call you shortly. + +
+
+
+ +
+
+ + + + + New high-risk alert +
+

Fatima Al Hashimi reported breathlessness — heart failure care plan.

+
+ +
+

Check-ins answered

+

92%

+
+
+
+
+
+ ) +} + +export default function LandingPage() { + return ( +
+ {/* Ambient glow */} +
+
+
+
+
+ + {/* Nav */} +
+ +
+ +
+ {/* Hero */} +
+
+ + AI-assisted post-discharge care · Built for the UAE + +

+ Care doesn’t end at{' '} + discharge. +

+

+ CareLoop turns a discharge letter into a living care plan on WhatsApp — in the patient’s own language. + Nightly check-ins, answers from their own instructions, and a nurse alerted the moment something is wrong. +

+
+ + Try the live demo + + + + See how it works + +
+
    + {['No app for patients', 'Nurse approves every plan', 'Hospital-grade security'].map((t) => ( +
  • + + + + {t} +
  • + ))} +
+
+
+ +
+
+ + {/* Stats */} +
+
+ {STATS.map((s) => ( +
+

+ {s.value} +

+

{s.label}

+
+ ))} +
+
+ + {/* Problem */} +
+

The gap

+

+ Hospitals lose sight of patients the moment they walk out the door. +

+

+ Instructions are forgotten, medicines are skipped, follow-ups are missed and warning signs go unreported — + until the patient is back in the emergency room. CareLoop closes that loop, without adding hours of phone calls. +

+
+ + {/* How it works */} +
+
+ {STEPS.map((s, i) => ( +
+ + {i + 1} + + + + +

{s.title}

+

{s.body}

+
+ ))} +
+
+ + {/* Features */} +
+
+

Everything in one loop

+

+ A clinical dashboard for nurses. Just WhatsApp for patients. +

+
+
+ {FEATURES.map((f) => ( +
+ + + +

{f.title}

+

{f.body}

+
+ ))} +
+
+ + {/* Split: nurse vs patient */} +
+
+
+ + For care teams + +

See who needs you, right now.

+
    + {[ + 'Live alerts ranked by severity, with the patient’s own words', + 'Medication adherence and check-in trends per patient', + 'Appointment funnel from booked to attended', + 'Every WhatsApp conversation in one transcript', + ].map((t) => ( +
  • + {t} +
  • + ))} +
+
+
+ + For patients + +

Nothing new to learn.

+
    + {[ + 'No downloads, accounts or passwords — just WhatsApp', + 'Care plan in plain language they can read', + 'Ask questions any time, answered from their instructions', + 'Send a voice note — it is understood too', + ].map((t) => ( +
  • + {t} +
  • + ))} +
+
+
+
+ + {/* Languages */} +
+

Multilingual by design

+

+ Every patient hears from their hospital in their own language. +

+
+ {LANGUAGES.map((l) => ( +
+

{l.word}

+

{l.name}

+
+ ))} +
+
+ + {/* CTA */} +
+
+
+
+

+ Close the loop on every discharge. +

+

+ Add a sample patient from a discharge letter and watch the care plan arrive on WhatsApp — in under a minute. +

+ + Open the dashboard + + +
+
+
+ +
+
+ +

Post-discharge patient follow-up over WhatsApp · © {new Date().getFullYear()} {siteConfig.name}

+
+
+
+ ) +} diff --git a/src/app/setup/page.tsx b/src/app/setup/page.tsx index 7e3ea81..f8d3027 100644 --- a/src/app/setup/page.tsx +++ b/src/app/setup/page.tsx @@ -69,7 +69,7 @@ export default function SetupPage() { )} - Try again + Try again
) diff --git a/src/components/layout/sidebar.tsx b/src/components/layout/sidebar.tsx index 7122d00..58dae2d 100644 --- a/src/components/layout/sidebar.tsx +++ b/src/components/layout/sidebar.tsx @@ -22,7 +22,7 @@ export interface NavItem { /** The everyday screens, in the order a nurse reaches for them. Settings sits apart, at the bottom. */ export const NAV_ITEMS: NavItem[] = [ - { label: 'Overview', href: '/', icon: LayoutDashboard }, + { label: 'Overview', href: '/dashboard', icon: LayoutDashboard }, { label: 'Patients', href: '/patients', icon: Users, alsoActiveOn: ['/episodes'] }, { label: 'Alerts', href: '/alerts', icon: Bell }, { label: 'Appointments', href: '/appointments', icon: Calendar }, @@ -32,7 +32,7 @@ export const NAV_ITEMS: NavItem[] = [ export const SETTINGS_ITEM: NavItem = { label: 'Settings', href: '/settings', icon: Settings } export function isNavActive(pathname: string, item: NavItem) { - if (item.href === '/') return pathname === '/' + if (item.href === '/dashboard') return pathname === '/dashboard' return [item.href, ...(item.alsoActiveOn ?? [])].some((p) => pathname === p || pathname.startsWith(`${p}/`)) } diff --git a/src/components/tour/tour-provider.tsx b/src/components/tour/tour-provider.tsx index 772a158..1d9ddea 100644 --- a/src/components/tour/tour-provider.tsx +++ b/src/components/tour/tour-provider.tsx @@ -134,7 +134,7 @@ export function TourProvider({ enabled, children }: { enabled: boolean; children }, start: () => { tour.start() - if (window.location.pathname !== '/') router.push('/') + if (window.location.pathname !== '/dashboard') router.push('/dashboard') }, resume: () => { tour.resume() diff --git a/src/components/tour/tour-steps.tsx b/src/components/tour/tour-steps.tsx index b2f653b..05d6385 100644 --- a/src/components/tour/tour-steps.tsx +++ b/src/components/tour/tour-steps.tsx @@ -103,8 +103,8 @@ export const TOUR_STEPS: TourStep[] = [ { id: 'overview-stats', chapter: 'overview', - route: /^\/$/, - href: () => '/', + route: /^\/dashboard$/, + href: () => '/dashboard', target: '[data-tour="overview-stats"]', placement: ['bottom', 'top'], title: 'The whole ward at a glance', @@ -114,8 +114,8 @@ export const TOUR_STEPS: TourStep[] = [ { id: 'overview-attention', chapter: 'overview', - route: /^\/$/, - href: () => '/', + route: /^\/dashboard$/, + href: () => '/dashboard', target: '[data-tour="overview-attention"]', placement: ['right', 'left', 'top', 'bottom'], title: 'Who needs a nurse, right now', @@ -125,8 +125,8 @@ export const TOUR_STEPS: TourStep[] = [ { id: 'overview-whatsapp', chapter: 'overview', - route: /^\/$/, - href: () => '/', + route: /^\/dashboard$/, + href: () => '/dashboard', target: '[data-tour="whatsapp-line"]', placement: ['right', 'top'], optional: true, @@ -137,8 +137,8 @@ export const TOUR_STEPS: TourStep[] = [ { id: 'overview-phone', chapter: 'overview', - route: /^\/$/, - href: () => '/', + route: /^\/dashboard$/, + href: () => '/dashboard', wide: true, title: 'Try it on your own phone', body: ( @@ -159,8 +159,8 @@ export const TOUR_STEPS: TourStep[] = [ { id: 'overview-add', chapter: 'overview', - route: /^\/$/, - href: () => '/', + route: /^\/dashboard$/, + href: () => '/dashboard', target: '[data-tour="add-patient"]', placement: ['bottom', 'left'], title: 'Let’s send a patient home', @@ -380,7 +380,7 @@ export const TOUR_STEPS: TourStep[] = [ id: 'explore-alerts', chapter: 'explore', route: null, - href: () => '/', + href: () => '/dashboard', target: '[data-tour="nav-alerts"]', placement: ['right', 'bottom'], optional: true, @@ -392,7 +392,7 @@ export const TOUR_STEPS: TourStep[] = [ id: 'explore-analytics', chapter: 'explore', route: null, - href: () => '/', + href: () => '/dashboard', target: '[data-tour="nav-analytics"]', placement: ['right', 'bottom'], optional: true, @@ -404,7 +404,7 @@ export const TOUR_STEPS: TourStep[] = [ id: 'explore-finish', chapter: 'explore', route: null, - href: () => '/', + href: () => '/dashboard', target: '[data-tour="tour-button"]', placement: ['bottom', 'left'], title: 'That’s CareLoop', diff --git a/src/lib/supabase/middleware.ts b/src/lib/supabase/middleware.ts index 9701289..ed2f178 100644 --- a/src/lib/supabase/middleware.ts +++ b/src/lib/supabase/middleware.ts @@ -35,15 +35,15 @@ export async function updateSession(request: NextRequest) { const { pathname } = request.nextUrl - // Public routes that don't require a user session. + // Public routes that don't require a user session. / is the landing page. // /api/webhooks is verified by Twilio signature; /api/cron by CRON_SECRET bearer token. const publicPaths = ['/setup', '/invite', '/api/webhooks', '/api/cron', '/api/v1/auth'] - const isPublic = publicPaths.some((p) => pathname.startsWith(p)) + const isPublic = pathname === '/' || publicPaths.some((p) => pathname.startsWith(p)) // There is no login screen. The old /login address goes to the dashboard. if (pathname === '/login') { const url = request.nextUrl.clone() - url.pathname = '/' + url.pathname = '/dashboard' return NextResponse.redirect(url) }