Skip to content

Frontend

Purpose

nuxt-frontend/ is the primary product UI. It serves operator dashboards, admin workflows, floor plans, emergency tooling, analytics, and a mobile-first layout. It also hosts the published docs output and doubles as the Capacitor app shell.

Key structure

PathRole
pages/Route-level screens — see pages reference below
components/Shared UI and domain components
components/mobile/views/Mobile-specific views layered over the same product domains
layouts/Desktop/mobile shells and navigation
middleware/Route guards — see middleware section below
plugins/Client-side plugins — see plugins section below
composables/Shared stateful behavior — see table below
server/Nuxt/Nitro server layer — see Nuxt Server
public/docs/Generated VitePress output
android/, ios/Capacitor native projects

Pages reference

All pages in nuxt-frontend/pages/:

PageRoutePurpose
index.vue/Root redirect
login.vue/loginEmail/password login form; OIDC redirects to /auth/login
dashboard.vue/dashboardOperator home — building/floor summary, gateway health, personnel counts
floorplan.vue/floorplanReal-time RTLS map with Leaflet — protected by lock file
personnel.vue/personnelPersonnel list, search, status overview
personnel/[id].vue/personnel/:idPersonnel detail and beacon assignment
assets.vue/assetsAsset list and status overview
assets/[id].vue/assets/:idAsset detail and history
emergency.vue/emergencyEmergency scenario trigger, muster tracking, notification status
gateways.vue/gatewaysGateway list, health, and BLE scan visualization
desktop-gateways.vue/desktop-gatewaysWindows gateway management and site config
ambient.vue/ambientAmbient BLE device overview
ambient/[id].vue/ambient/:idAmbient device detail
buildings.vue/buildingsBuilding/floor/zone management
analytics.vue/analyticsOccupancy and movement analytics
activity.vue/activityLive activity log stream
battery.vue/batteryBattery health dashboard and alerts
weather.vue/weatherWeather feed and alert configuration
ble-scanner.vue/ble-scannerVirtual and serial BLE scanner UI
admin.vue/adminAdmin settings root
admin/personnel.vue/admin/personnelBulk personnel and beacon management — protected by lock file
admin/api-docs.vue/admin/api-docsIn-app OpenAPI documentation viewer
profile.vue/profileUser profile and password change
change-password.vue/change-passwordForced password change (redirected when mustChangePassword flag is set)
phone-badge-onboarding.vue/phone-badge-onboardingOperator flow for issuing phone badges
phone-beacon-enroll.vue/phone-beacon-enrollPublic token-driven phone enrollment (native app required)
scan-qr.vue/scan-qrEmployee self-service: in-app QR scanner + manual token entry
accessibility.vue/accessibilityAccessibility statement
unauthorized.vue/unauthorizedRBAC access denied landing

Route middleware

Located in nuxt-frontend/middleware/:

FileTypeInvocationPurpose
auth.tsNamedmiddleware: ['auth'] in definePageMetaRedirects unauthenticated users to /login or OIDC /auth/login; enforces role check; redirects to /change-password when mustChangePassword is set
device.global.tsGlobalAutomatic on every navigationSwitches between default and mobile Nuxt layouts based on viewport width at navigation time
permissions.tsNamedmiddleware: ['auth', 'permissions']Fine-grained RBAC; use permissions: ['resource.action'] (all required) or permissionsAny: [...] (any one sufficient) in definePageMeta

Auth middleware behavior

  • OIDC-aware: when oidcProvider is 'okta' in runtime config, the SSO button redirects to /auth/oidc/login via nuxt-oidc-auth
  • Server-side: uses httpOnly auth_token cookie to determine auth state (authoritative)
  • Client-side: uses non-httpOnly auth_user cookie (set alongside auth_token on login)

Device middleware

Runs globally on every page navigation. Calls setPageLayout('mobile' | 'default') based on window.matchMedia(NARROW_BREAKPOINT).matches. Only activates on the client — server always renders with the default layout.

Client plugins

Located in nuxt-frontend/plugins/:

FileRunsPurpose
driver.client.tsClient onlyLoads driver.js CSS — enables the useOnboarding composable to run Driver.js step-by-step onboarding tours (used on the desktop gateway onboarding wizard)
errorHandler.client.tsClient onlyRegisters a global Vue error handler and window.onerror / window.onunhandledrejection listener; forwards all unhandled errors to useErrorLogging

Composables reference

Key composables in composables/. The floorplan subsystem is split across many focused files.

ComposablePurpose
useApi / useApiClientBase HTTP fetch helpers and typed API client
useAuthLogin state, JWT/session handling, permission checks
useRealtimeCentrifugo client — WebSocket connection and event subscription
useEmergencyEmergency trigger, muster status, scenario management
useCommunicationsEmergency notification channel state
useNotificationRulesAlert rule management
usePersonnel / usePersonnelAvatarPersonnel list, search, avatar loading
useBuildingsBuildings/floors/zones tree
useFloorplanTop-level floorplan orchestrator — composes all floorplan sub-composables
useFloorplanMapLeaflet map lifecycle
useFloorplanDataStoreShared reactive position/device data store for the floorplan
useFloorplanMarkersMarker rendering and clustering
useFloorplanClusteringMarker cluster plugin management
useFloorplanAmbientLayerAmbient device overlay
useFloorplanAssetLayerAsset overlay
useFloorplanGatewayLayerGateway status overlay
useFloorplanHeatmapRSSI / position heatmap layer
useFloorplanWebGLWebGL-accelerated rendering for large marker counts
useFloorplanZonesZone polygon rendering and click handling
useFloorplanControlsZoom, pan, and toolbar control state
useFloorplanAccuracyLayerPositioning accuracy visualisation
useFloorplanRSSIPer-receiver RSSI display
useFloorplanTooltipMarker tooltip content
useFloorplanDebugDebug overlay toggle
useFloorplanDragSelectDrag-to-select multiple markers
useFloorplanMeasureDistance measurement tool
useCharlottePort of reelyActive charlotte.js — Cytoscape.js Hyperlocal Context graph showing BLE device ↔ gateway RSSI relationships
useVirtualGatewayTurns a browser/phone into a BLE gateway using Web Bluetooth; posts scanned observations to the same ingest pipeline as hardware gateways
useNativeBleScannerBLE scanning via the Capacitor @capacitor-community/bluetooth-le plugin (native app only)
useSerialBleScannerBLE scanning via Web Serial API (Chrome desktop)
useRssiFilterRSSI sliding-window averaging shared by virtual and serial scanners
useDeviceIdentifierSniffypedia BLE device identification and RPA resolution
usePhoneBeaconEnrollmentPhone badge enrollment state machine
useOnboardingDesktop gateway onboarding wizard state
useActivityActivity log streaming
useAnalyticsAnalytics dashboard data
useBatteryBattery health data
useWeatherWeather feed
useAdminAdmin settings API
useErrorHandler / useErrorLoggingCentralised error capture and logging
useFormattersDate/time/unit formatting helpers
useResponsiveViewBreakpoint detection for desktop vs mobile layout switching
useMobileSheet / useCrudDialogShared sheet and dialog state patterns
useMapMarkerScaleScale-aware marker sizing on the floorplan
usePwaInstallPWA install prompt (present but intentionally disabled)

Current UX model

  • Desktop and mobile are both first-class in the same app.
  • layouts/mobile.vue provides a dedicated mobile header, drawer, bottom nav, and shared page-title mapping.
  • components/mobile/views/ mirrors the major product areas with mobile-specific composition.
  • Documentation is linked back into the application through /docs/.

Build and runtime model

  • Default local build target is node-server.
  • Azure deploys use Nitro azure preset.
  • Capacitor builds set CAPACITOR_BUILD=1, which disables SSR and produces a static SPA-style bundle.
  • PWA support is intentionally disabled because stale service workers previously broke deploys.

Authentication model

  • Local email/password auth is active.
  • Okta SSO is active via nuxt-oidc-auth (set NUXT_OKTA_DOMAIN + NUXT_OKTA_CLIENT_ID to enable). Auth0 has been removed.
  • The frontend consumes JWT/session data and permission lists returned by the backend auth routes.

Frontend coding conventions

Formatting

  • Prettier: no semicolons, single quotes, width 100, trailing commas es5, 2-space indentation
  • .vue files use the Vue parser override in Prettier

ESLint rules that matter

  • Component tags in templates must use PascalCase
  • prefer-const and no-var are enforced
  • console and debugger are warnings in dev, errors in production
  • any and non-null assertions are warnings in app code, relaxed in tests
  • MobileBottomSheet is intentionally restricted in favor of MobileDetailSheet

Practical style guidance

  • Follow existing Composition API patterns and typed script setup
  • Reuse composables before adding new page-local data flow
  • Preserve separate desktop/mobile rendering layers when a domain already has both
  • Do not re-enable PWA/service-worker behavior without a replacement cache strategy

Useful commands

bash
cd nuxt-frontend
npm run dev
npm run build
npm run typecheck
npm run lint
npm run test:unit
npm run test:e2e
npm run storybook

QC enforcement rules

The frontend QC pipeline enforces these rules on every changed file:

RuleToolEnforcement
TDD — test file required per source filecheck-test-quality.shError if missing; warns on mount-only or zero-assertion tests
Zod validation on all server mutation routescheck-zod-validation.shBlocks bare readBody() without schema validation
NISC design tokens only — no hardcoded hex or PrimeVue CSS varscheck-branding.shBlocks any hardcoded color outside the approved palette
SSR safety — browser APIs must be guardedcheck-ssr-safety.shBlocks window.*/document.* outside onMounted/import.meta.client
Heavy packages must use dynamic import()check-imports.shBlocks static imports of leaflet, chart.js, xlsx, jspdf, etc.
Env vars via useRuntimeConfig() onlycheck-env-vars.shBlocks hardcoded secrets and bare process.env in client code
Mobile-responsive patternscheck-mobile.shBlocks raw window.innerWidth; warns on DataTable without responsiveLayout
OpenAPI defineRouteMeta() on every routecheck-openapi-compliance.shBlocks server routes without metadata
No direct DB access from Nuxt routescheck-db-compliance.shBlocks pg imports and raw SQL in non-exempt server files
camelCase everywhereESLintBlocks snake_case variable/property names in frontend code

Run npm run qa (lint then test) before committing. The pipeline also runs npm run copilot:commit for Copilot-authored changes.

Frontend-specific handoff notes

  • nuxt.config.ts is a high-impact file: runtime config, Azure routing, auth activation, docs path, and caching behavior all live there.
  • pages/floorplan.vue is protected by lock files because it contains fragile ambient-rendering behavior.
  • The app currently ships both operational docs and product UI together, so docs changes can affect deploy output size and routing.

NISC Muster Tracking Documentation