A production-grade frontend for a B2B healthcare SaaS, built for the Ragas AI frontend assignment. Demonstrates auth, multi-page architecture, a patient-management module with grid/list views, an analytics dashboard, and service-worker-driven notifications.
Live demo: https://careplane-ragas-demo.netlify.app
Repo: https://github.com/Developement-Practice/ragas-ai-assignment
PR: #1
The login screen offers three paths:
| Method | Status | How to use |
|---|---|---|
| Continue as demo user | ✅ Always works (no Firebase round-trip) | One click on the login screen |
| Email / password | ✅ Live | Demo credentials shared in the PR description / submission form |
| Continue with Google | ✅ Live | Any Google account |
All three are wired against Firebase project redux-http-7f91b. The demo user is held in localStorage, so signing out fully clears state.
Demo email/password credentials are intentionally kept out of the README and live only in the PR description / submission form to avoid them being indexed by code search.
| Layer | Choice | Why |
|---|---|---|
| Framework | Next.js 16 (App Router) + React 19 | File-based routing, RSC, dynamic imports for chart code-splitting |
| Language | TypeScript (strict) | End-to-end type safety from RTK Query → views |
| State | Redux Toolkit + RTK Query | Scalable for multi-module SaaS; built-in caching, dedup, refetch |
| Styling | Tailwind CSS v4 + shadcn/ui (Base UI primitives) | Accessible primitives, consistent design tokens, dark mode built in |
| Forms | React Hook Form + Zod | Type-inferred validation, minimal re-renders |
| Charts | Recharts | Composable, themeable, dynamically imported on the analytics route |
| Auth | Firebase Authentication | Email/password + Google OAuth |
| Mock API | MSW (Mock Service Worker) | Realistic REST simulation without standing up a backend |
| Notifications | Service Worker + Notifications API | Local push notifications, no FCM dependency |
| Toasts | sonner | Lightweight, accessible |
| Deploy | Netlify (@netlify/plugin-nextjs) |
Auto-deploy on push, preview deploys per PR |
- Authentication — Firebase Email/Password + Google sign-in, session persistence (IndexedDB), validated form, friendly error mapping, demo-mode fallback so reviewers don't need Firebase to evaluate.
- Pages —
/login,/dashboard,/analytics,/patients,/patients/[id],/settings/notifications. - Patient module — searchable, filterable patient roster with grid ↔ list toggle (persisted to
localStoragevia Redux), pagination, click-through detail page with Overview / Visits / Labs / Notes tabs. Skeleton loaders, empty states, error states. - Service-worker notifications — own SW at
/sw.js(scoped narrowly to coexist with MSW's worker), three demo triggers: appointment reminder · critical-patient alert · lab results ready. Permission-gated, persisted preferences, in-app history dropdown synced with system notifications. - State management — Redux Toolkit with three slices (
auth,patientsView,notifications) and two RTK Query APIs (patientsApi,analyticsApi).
- Reusable component design + clean folder structure — feature-based foldering, shared UI primitives, shared
KpiCard,ChartCard,EmptyState,PageHeader,StatusPill. - Dark mode —
next-themes, system-aware, toggle in topbar, theme-aware chart palettes. - Accessibility baseline — semantic landmarks, skip-to-content link,
aria-labels on icon buttons,aria-currenton active nav,aria-liveon pagination, full keyboard navigation, focus-visible rings. - Performance — dynamic
import()of Recharts on analytics route, RTK Query response caching, debounced search input,next/fontself-hosted Inter, server-rendered shell with client-side data fetching.
src/
app/ # Next.js App Router
(auth)/login/ # Public login route
(app)/ # Auth-gated app shell
dashboard/
patients/ [id]/
analytics/
settings/notifications/
layout.tsx, providers.tsx # Root + Redux/Theme/MSW/Auth bootstrap
features/
auth/ # Firebase init, slice, listener, AuthGate
patients/ # RTK Query API, view slice, components
analytics/ # RTK Query API, chart components
notifications/ # Slice, SW registration, scheduler
components/
ui/ # shadcn/ui primitives (Base UI under the hood)
shared/ # AppShell, Sidebar, TopBar, KpiCard, …
store/ # configureStore + typed hooks
mocks/ # MSW handlers + 56 seeded patients
lib/ # cn, date/format, status helpers
types/ # Domain types
public/
sw.js # CarePlane notification worker (scope: /sw-scope/)
mockServiceWorker.js # MSW worker (scope: /)
- Node 20+ (tested on 22)
- npm 10+
git clone <repo-url>
cd ragas-ai-assignment
npm install
cp .env.example .env.local # then fill in Firebase keys (see FIREBASE_SETUP.md)
npm run dev # http://localhost:3000The app boots in demo mode automatically if Firebase env vars are missing — click "Continue as demo user" on the login page.
npm run dev # next dev (Turbopack)
npm run build # next build
npm run start # next start (after build)
npm run lint # eslint
npm run typecheck # tsc --noEmit
npm run format # prettier --writeSee FIREBASE_SETUP.md for a 5-minute walkthrough (project creation → enabling providers → wiring env vars → authorized domains for the live deploy).
- Push this repo to GitHub.
- Netlify → Add new site → Import from Git → select repo. Defaults from
netlify.tomlare honoured (build:npm run build, publish:.next, plugin:@netlify/plugin-nextjs). - Site settings → Environment variables: add the six
NEXT_PUBLIC_FIREBASE_*keys plusNEXT_PUBLIC_ENABLE_MSW=true. Trigger a redeploy. - Firebase console → Authentication → Settings → Authorized domains: add your
*.netlify.appdomain so Google OAuth works.
The assignment asks for a UI demonstration, not a backend. MSW intercepts real fetch calls inside the browser, so the codebase looks and behaves exactly like a real API client — RTK Query, error handling, loading states, pagination, all real. Swapping to a live backend is a one-line baseUrl change.
MSW's mockServiceWorker.js has to register at root scope / to intercept all requests. Our notification SW is registered at scope /sw-scope/ (a never-navigated path) so it doesn't override MSW. Notification APIs (showNotification, notificationclick) are scope-independent, so this works cleanly.
Three separate concerns — auth state, view preferences, notification history — each with their own update patterns and consumers. Redux DevTools time-travel debugging matters for healthcare workflows. RTK Query gives caching, dedup, and refetch semantics for free, which would be reinvented in Zustand.
Newer shadcn (base-nova preset) ships Base UI primitives — same ergonomics as Radix but with better TypeScript inference and smaller bundle. The pattern-difference for consumers is render={<Component />} instead of asChild.
Each domain (auth, patients, analytics, notifications) keeps its slice, API, and components colocated. This scales: a new module slots in beside the existing ones without surgery on shared files. components/shared/ holds primitives that are genuinely cross-cutting.
-
npm install && npm run dev→ loads atlocalhost:3000, redirects to/login - Click "Continue as demo user" → lands on
/dashboard - Sign in with email/password (after Firebase setup) and Google
-
/patients→ toggle grid ↔ list, search, filter by status / department, click into a patient -
/patients/[id]→ tabs (Overview / Visits / Labs / Notes) all render -
/analytics→ 4 charts render and adapt to dark mode -
/settings/notifications→ enable permission, trigger each of the three demos, verify OS notification appears - Click a system notification → tab focuses and navigates
- Notification bell shows history with unread count
- Theme toggle: Light / Dark / System
- Mobile: hamburger menu opens, all pages responsive
- Keyboard-only nav from login through every page
-
npm run build && npm run lint && npm run typecheckall clean
MIT © 2026 Kaustav Ghosh