A healthcare management dashboard for monitoring patients, tracking vitals, and visualising clinical analytics — built with Firebase Auth, real-time-ready state management, and in-browser push notifications via a custom Service Worker.

MediFlow Dashboard is a front-end healthcare management platform designed to give clinicians and administrators a single, fast interface for managing patient records, tracking status changes, and reviewing operational analytics. It was built as a production-quality reference application demonstrating how a modern React stack can be applied to a sensitive, data-heavy domain.
The application supports a full authentication flow — email/password and Google sign-in via Firebase — and gates all clinical screens behind a protected route layer. Patient records include vitals, admission status, and history. The analytics section surfaces aggregated trends through interactive charts, giving administrators visibility into patient throughput, status distributions, and department-level metrics over time.
Healthcare staff need to monitor patient status across a ward at a glance, get notified when a patient's condition changes, and quickly drill into individual records without switching between systems. Most internal tools in this space are slow, fragmented, or require a backend service running just to render a list. The goal was to build a fully working clinical dashboard — with auth, notifications, and analytics — that runs entirely as a static front-end, with no custom backend dependency.
The application is built as a layered client-side architecture with strict separation between auth, state, UI components, and the notification system:
useAuthInit hook subscribes
to Firebase's auth state stream and syncs it into Zustand on every page load.cva and @radix-ui/react-slot provides a consistent,
fully-typed design system without pulling in a pre-built UI kit.sw.js) handles push notifications
for patient status changes. A thin notificationService bridge decouples the React
tree from the SW lifecycle — components call the service after a state mutation, and
the service posts a structured message to the SW, which fires the notification. This
means alerts reach staff even when the tab is backgrounded.| Technology | Role |
|---|---|
| React 18 | UI component tree — pages, layout, atoms, molecules |
| TypeScript | Strict typing across all layers — components, store, services, data |
| Vite | Fast dev server and lean production bundler |
| Zustand + persist | Global state for auth and patient records with localStorage persistence |
| Firebase Auth | Email/password and Google OAuth — no custom credential store |
| Tailwind CSS | Utility-first styling; atoms use cva for variant composition |
| Recharts | Declarative, composable charts for the analytics page |
| Service Worker | In-browser push notifications for patient status changes |
Zustand was chosen over Redux for its minimal boilerplate and first-class TypeScript
support — defining a store takes a single typed create call. Firebase Auth was
chosen to eliminate the most security-sensitive part of the system from the scope of
the project entirely. The Service Worker was kept as a plain JS file outside the Vite
build to avoid bundler interference with the SW registration lifecycle.
Bridging Zustand state to a Service Worker
The Service Worker lives entirely outside the React tree and has no access to Zustand's
store. Patient status updates flow through Zustand, but the SW needs to receive them to
fire notifications. Rather than coupling the component layer to the SW, a notificationService
module acts as a one-way bridge: it is called by store actions after a mutation and
uses navigator.serviceWorker.controller.postMessage to forward the event. The result
is that neither the component layer nor the store is aware of the SW at all.
Hand-rolling an atom library without shadcn CLI
Using cva + @radix-ui/react-slot to build atoms manually — rather than running the
shadcn generator — required careful design of the variant API and prop-forwarding
pattern upfront. The benefit is full control over the generated markup and class
output, and a significantly smaller bundle since only the primitives actually used are
included.
tsc --noEmit and vite build both pass with zero errors