Skip to content

Frontend Overview

Ankit Upadhyay edited this page Jul 24, 2026 · 3 revisions

Frontend / PIMS Web Overview

apps/frontend is the web PIMS (clinic-facing) and the public marketing/auth site. Next.js 15 (App Router), React 19, TypeScript, Tailwind 4, Zustand, with a custom design system. The code under apps/frontend/src is canonical.

Companion pages: Auth & Session · PMS UI · Inventory & Forms · Finance, Companions, Tasks · Public & Marketing Pages · Testing.

PIMS web app dashboard
The PIMS web app (apps/frontend), shown here running inside the desktop shell.

App Router layout (src/app)

Two route groups under src/app/(routes):

  • (app) — authenticated PMS surfaces (appointments, dashboard, finance, inventory, forms, tasks, chat, companions, organization, settings, developers/(portal), …). The group layout is an async Server Component that calls await connection() (forces dynamic rendering) and wraps children in SessionInitializer.
  • (public) — marketing, auth, and legal (signin, signup, pricing, about, contact, pet-parents, pet-businesses, pms, trust-center, privacy-policy, accessibility, …).

The root layout (src/app/layout.tsx) mounts global providers: SkipLink, Cookies, PostHogBootstrap/PostHogUserSync, RouteAnnouncer, loader overlays, ToastProvider.

Feature-based source organization

Business logic lives under src/app/features/<domain>/ with a consistent shape — pages/, components/, services/, hooks/, types/, lib/, constants/, utils/, and a barrel index.ts. Route page.tsx files are thin wrappers that import a feature page component (e.g. (public)/signin/page.tsxfeatures/auth/pages/SignIn/SignInPage). ~30 feature domains exist.

State: Zustand

One store per domain under src/app/stores/ (26 stores: authStore, orgStore, appointmentStore, appointmentWorkspaceStore, taskStore, inventoryStore, invoiceStore, …), created with create<T>() and named useXxxStore. The normalized convention is xById: Record<string,T> + idsByOrgId + status + error + lastFetchedAt, with selectors like getTasksByOrgId. orgStore persists via persist/createJSONStorage. Store-facing hooks live in src/app/hooks/ (useAppointments, useTask, useInvoices, usePermissions, …).

API layer

src/app/services/axios.ts (the configured api instance + getData/postData/patchData) and src/app/services/http/ (a typed http wrapper that unwraps {data,status} and normalizes errors via HttpError). Per-domain services call the API and write into Zustand stores.

Design system (src/app/ui)

A barrel (src/app/ui/index.ts) re-exports primitives with typed props — Button (variant-delegating to Primary/Secondary/Delete), Text, Stack, Card, Badge, Input — plus namespaced groups Inputs, Filters, Cards, Tables, Overlays, Layout, Primitives. Each primitive ships *.stories.tsx (Storybook 9 + a11y addon). Tokens are CSS-variable-driven in src/app/globals.css (custom Satoshi Variable font; token reference in ui/tokens.md). See Design System and Tokens.

Security: nonce CSP

src/middleware.ts applies a per-request nonce CSP to (app) routes (STRICT_CSP_PATH_PREFIXES) with no 'unsafe-inline'; all other paths (public/marketing) get allowInlineScripts: true. This is why the (app) layout forces dynamic rendering — a per-request nonce can't be baked into a static page. src/securityHeaders.ts builds the CSP (allowlisting Stripe, Cal.com, PostHog, Stream, S3/CloudFront, Documenso, IDEXX/Merck iframes) and other headers. See Frontend: Public and Marketing Pages.

"Realtime" on the web

There is no Socket.IO event bus or useRealtimeSync hook. Live updates come from three mechanisms: the Stream Chat websocket (useChatNotifications), cross-tab sync via custom window events + useSyncExternalStore (e.g. useAppointmentLockWindow), and bulk store hydration on org change (SessionInitializer). See Realtime Setup.

Related

Product & Domain
Architecture
Applications
Design & Accessibility
Engineering Handbook
Decisions (ADRs)
Design Docs & Plans
Roadmap
Operations
Meta

Canonical code & docs: main repo · Auto-generated companion: DeepWiki

Clone this wiki locally