Frontend Overview
hlmis-frontend (package name hlmis-ui) is a Next.js 16 App Router
app — React 19, TypeScript, pnpm. pnpm dev runs it on port 8111.
It talks to two backends:
- The HLMIS API — the main application backend. Every call goes through
one axios client (
src/core/api/axios.ts) whosebaseURLis resolved at runtime fromGET /api/config, not baked in at build. See State & Data Fetching. - TRVST — the optional track-and-trace service for batch verification.
All TRVST traffic is proxied through Next API routes under
src/app/api/trvst/; client logic lives insrc/adapters/trvst/.
Routing and access control
Route groups under src/app/:
| Group | What |
|---|---|
(auth)/login | The sign-in page (public) |
(public)/ | About, request-demo |
(dashboard)/ | Everything behind auth — one folder per feature area |
(dashboard)/view-mode/ | Read-only "browse as another store" mirror of the operational screens |
src/proxy.ts is the Next middleware. It reads the NextAuth JWT and
rewrites to /denied when the user's permissions array doesn't include
what a path needs (MANAGE_ORDERS for /requisition and /distributions,
MANAGE_ARRIVALS for /arrivals, the admin set for /users /
/maintenance / /analytics, and so on). The sidebar
(src/components/ui/app-sidebar.tsx) filters the same way, plus a few
structural conditions (isMainStore, organization.hasChildren), so a
user only ever sees links they can actually open.
Auth itself is NextAuth v4, JWT strategy, 1-hour sessions, credentials
provider — src/services/authOptions.ts. The authorize callback calls
the HLMIS API's /auth/login and stashes the returned bearer token on the
JWT; the axios client attaches it to every request.
Where code lives
The codebase is mid-migration from a flat layout to a layered src/core/:
| Path | Role |
|---|---|
src/core/api/axios.ts | The one axios client (singleton, lazy baseURL) |
src/core/api/services/* | One file per resource — thin functions that call the API and unwrap the { payload } envelope |
src/core/api/types/ | Hand-written types, increasingly aliased onto src/core/api/generated/schema.d.ts — see API Types & Versioning |
src/core/hooks/use*Service.ts | TanStack Query hooks wrapping the services — the layer components import |
src/core/lib/ | Framework-agnostic helpers: config.ts, deployment.ts, date-utils.ts, version-compat.ts |
src/services/, src/hooks/, src/types/ | Older equivalents, still in use. New work goes in src/core/. |
src/adapters/trvst/, src/adapters/superset/ | Integration-specific code, kept out of core |
src/components/ui/ | shadcn/Radix primitives |
src/components/attribute-fields/ | The custom-field renderer — see Attributes on the Frontend |
Three component systems
The app deliberately mixes shadcn/ui (Radix primitives, in
src/components/ui/), Mantine v7 (mantine-react-table for every data
grid), and MUI (icons, the tree view). All three are themed to the
HLMIS "Ledger Paper" identity — the orange scale in
src/app/context/providers.tsx (mantineTheme) and the CSS variables in
src/styles/globals.css keep Mantine and Radix from falling back to their
default blues.
Providers
src/app/context/providers.tsx wraps the tree, outermost first:
QueryClientProvider → MantineProvider → DeviceDetailsProvider
(captures the metadata TRVST headers need) → SessionProvider →
SocketProvider (a socket.io connection used for live scan events and
notifications).
i18n
next-intl, locales en / fr / ar / rw, message files at the repo
root under messages/. Locale is persisted in the NEXT_LOCALE cookie and
resolved server-side in src/i18n/request.ts; ar switches the layout to
RTL. Components read strings through useTranslations("Namespace").