-
Notifications
You must be signed in to change notification settings - Fork 2
Frontend Architecture Overview
The nr-forest-client frontend is a single-page application (SPA) built with Vue 3 and TypeScript that serves as the Ministry of Forests' client management system. It supports three distinct user types — IDIR staff, BCeID Business external users, and BC Services Card (BCSC) individual users — each routed to a purpose-built workflow on login.
The application enables external clients (companies and individuals) to apply for a forest client number, and empowers ministry staff to review submissions, manage client records, and search the client registry. It replaces legacy oracle-based workflows with a modern, accessible web interface backed by a Spring Boot REST API.
| Layer | Framework | Version | Purpose |
|---|---|---|---|
| UI Framework | Vue | 3.5.x | Reactive component model, Composition API |
| Language | TypeScript | ~6.0.0 | Static typing across all source files |
| Build Tool | Vite | 8.3.x | Dev server, bundling, plugin ecosystem |
| Router | Vue Router | 5.3.x | Client-side SPA routing with navigation guards |
| Authentication | AWS Amplify + Cognito | 6.x | Federated SSO via IDIR, BCeIDBusiness, BCSC |
| HTTP Client | Axios (via composables) | 1.20.x | REST API calls with auth headers |
| UI Components | Carbon Design System |
@carbon/web-components 2.x |
Accessible, consistent BC Gov styling |
| Utilities | VueUse | 15.x | Composable utilities (useEventBus, useLocalStorage, media queries) |
| HTML Sanitisation | vue-dompurify-html | 5.x | Safe rendering of server-provided HTML |
| Input Masking | vue-the-mask | 0.11.x | Phone, postal code, and custom input masks |
| Date Utilities | date-fns | 4.x | Date parsing, formatting, and validation |
| JSON Patching | fast-json-patch | 3.x | RFC 6902 patch generation for client edit operations |
graph TD
Start["Browser Request"] --> Guard["Router Navigation Guard\n(beforeEach)"]
Guard -->|"Not authenticated"| Landing["/landing — LandingPage"]
Guard -->|"Authenticated: resolve user"| Dashboard["/dashboard — UserLoadingPage"]
Landing -->|"BCeIDBusiness login"| BCeID["/new-client — FormBCeIDPage"]
Landing -->|"BCSC login"| BCSC["/new-client-bcsc — FormBCSCPage"]
Landing -->|"IDIR login"| Search["/search — SearchPage"]
BCeID -->|"Form submitted"| Confirmation["/form-submitted — FormSubmittedPage"]
BCSC -->|"Form submitted"| Confirmation
Search --> Submissions["/submissions — SubmissionListPage"]
Search --> ClientDetails["/clients/details/:id — ClientDetailsPage"]
Search --> StaffForm["/new-client-staff — FormStaffPage"]
Submissions --> Review["/submissions/:id — SubmissionReviewPage"]
Review -->|"Approved"| StaffProcessing["/client-submitted/:submissionId\nFormStaffProcessingPage"]
StaffForm -->|"Client created"| StaffConfirmation["/client-created — FormStaffConfirmationPage"]
Guard -->|"Feature flag off or no role"| Error["/error — ErrorPage"]
Guard -->|"Unknown path"| NotFound["/notfound — NotFoundPage"]
- 2025-05-05: Initial diagram published
The router enforces authentication via ForestClientUserSession.loadUser() in the
beforeEach guard. Each route carries a meta object declaring requireAuth,
visibleTo (provider list or role object), and redirectTo (provider-keyed fallback
routes). Staff-only routes additionally require one of the IDIR roles:
CLIENT_VIEWER, CLIENT_EDITOR, CLIENT_SUSPEND, or CLIENT_ADMIN.
Feature-flagged routes include a featureFlagged meta key — if the flag is absent or
falsy the guard redirects to the provider-appropriate fallback or the /error page.
graph TD
App["App.vue (Root)"]
App --> Header["MainHeaderComponent\n(navigation, help, profile, logout)"]
App --> RouterView["router-view (KeepAlive: SearchPage)"]
App --> GlobalModals["cds-modal (global delete/confirm)"]
App --> GlobalToast["cds-toast-notification (global)"]
App --> Overlay["LoadingOverlayComponent"]
RouterView --> PublicPages["Public / Auth Pages"]
RouterView --> ExternalPages["External User Pages"]
RouterView --> StaffPages["Staff Pages"]
PublicPages --> LandingPage
PublicPages --> UserLoadingPage
PublicPages --> ErrorPage
PublicPages --> NotFoundPage
PublicPages --> LogoutPage
ExternalPages --> FormBCeIDPage
ExternalPages --> FormBCSCPage
ExternalPages --> FormSubmittedPage
FormBCeIDPage --> BCeIDWizard["bceidform/ wizard steps\n(BusinessInformation, Address, Contact, Review)"]
FormBCSCPage --> BCeIDWizard
StaffPages --> FormStaffPage
StaffPages --> SearchPage
StaffPages --> SubmissionListPage
StaffPages --> SubmissionReviewPage
StaffPages --> ClientDetailsPage
StaffPages --> FormStaffConfirmationPage
StaffPages --> FormStaffProcessingPage
FormStaffPage --> StaffWizard["staffform/ wizard steps\n(BcRegistered, Combined, FirstNation,\nIndividual, Contacts, Locations, Review)"]
ClientDetailsPage --> ClientDetailViews["client-details/ views\n(Summary, Location, Contact, History,\nRelationships, RegistrationNumber)"]
SearchPage --> AdvancedSearch["search/AdvancedSearch.vue"]
- 2025-05-05: Initial diagram published
App.vue is the root component. It wires the global event buses
(modal-notification, toast-notification, overlay-event) and renders the
<router-view> with <keep-alive> caching for SearchPage. The MainHeaderComponent
is conditionally rendered based on $route.meta.hideHeader and only when a user session
is active.
sequenceDiagram
participant Component as Vue Component
participant Composable as useFetchTo / useFetch
participant Axios as Axios Instance
participant API as Backend REST API
participant EventBus as useEventBus (VueUse)
participant App as App.vue
Component->>Composable: useFetchTo(url, data, config)
Composable->>Axios: axios.request({ baseURL, Authorization: Bearer token })
Axios->>API: HTTP GET / POST / PATCH / DELETE
API-->>Axios: 200 OK (JSON payload)
Axios-->>Composable: result.data
Composable-->>Component: reactive data, loading=false
alt HTTP 4xx / 5xx
Axios-->>Composable: AxiosError
Composable->>EventBus: emit("error-notification", { fieldId, errorMsg })
EventBus-->>App: listener fires
App->>App: Open cds-toast-notification
end
- 2025-05-05: Initial diagram published
All HTTP communication flows through the useFetchTo composable in
src/composables/useFetch.ts. It injects the Bearer token from
ForestClientUserSession.token into every request and emits on the
error-notification event bus for ERR_BAD_RESPONSE, ERR_NETWORK, or
ERR_BAD_REQUEST error codes. The DataFetcher.vue component wraps useFetchTo
as a renderless component for declarative data fetching inside templates.
stateDiagram-v2
[*] --> Unauthenticated
Unauthenticated --> Redirecting : logIn(provider)
Redirecting --> Authenticated : Cognito callback → /dashboard
Authenticated --> SessionLoaded : loadUser() parses idToken
SessionLoaded --> ProviderRouted : beforeEach redirectTo[provider]
ProviderRouted --> [*] : User reaches their home route
SessionLoaded --> Unauthenticated : logOut()
Unauthenticated --> Landing : redirect to /landing
- 2025-05-05: Initial diagram published
ForestClientUserSession (singleton, attached to app.config.globalProperties.$session)
manages authentication state. signInWithRedirect triggers the Cognito-hosted UI for
the selected provider (IDIR, BCeIDBusiness, BCSC). On callback, the router sends
the user to /dashboard (UserLoadingPage), which calls loadUser() to parse the
idToken JWT claims and populate user, token, and authorities. The navigation
guard then redirects to the provider-appropriate home route.
| Type | Value |
|---|---|
| Base URL | Configured via VITE_FRONTEND_URL
|
| Entry Point | src/main.ts |
| Routes (Public) |
/landing, /dashboard, /error, /notfound, /logout
|
| Routes (BCeID/BCSC) |
/new-client, /new-client-bcsc, /form-submitted
|
| Routes (IDIR Staff) |
/search, /submissions, /submissions/:id, /new-client-staff, /client-created, /client-submitted/:submissionId, /clients/details/:id
|
| Feature Flags |
$features global property — populated from VITE_FEATURE_FLAGS env var |
| Cognito Redirect In | {VITE_FRONTEND_URL}/dashboard |
| Cognito Redirect Out | {VITE_FRONTEND_URL}/logout |
-
Backend REST API:
VITE_BACKEND_URL— all data requests proxied viauseFetchTo -
AWS Cognito:
VITE_AWS_COGNITO_*— federated authentication for three providers -
BC Sans Font:
@bcgov/bc-sans— BC Government typography -
nr-fsa-theme:
@bcgov-nr/nr-fsa-theme— shared BC Gov styles -
Carbon Web Components:
@carbon/web-components,@carbon/styles,@carbon/icons-vue— UI system
- No server-side rendering: The application is a pure SPA; all routing is client-side. SEO is not a goal.
-
Token stored in CookieStorage: AWS Amplify tokens are persisted in cookies (not
localStorage) to survive page refreshes, but this scopes the session to the cookie domain. -
SearchPage cache:
<keep-alive>is applied toSearchPageonly; all other pages re-mount on navigation, which means form state is lost on route change unless the page handles it explicitly. -
Feature flags are build-time environment variables: Flags are injected at container start via
VITE_FEATURE_FLAGSJSON; a redeploy or pod restart is required to toggle flags in OpenShift environments. -
No global state management library: Cross-component state relies on the
useEventBusevent bridge and Vue'sglobalProperties. Complex shared state between unrelated pages must use local storage or re-fetch patterns. -
Carbon Web Components use Shadow DOM: Custom directives (
v-masked,v-shadow) and scoped CSS cannot penetrate Shadow DOM boundaries without thev-shadow/::part()workaround implemented inCustomDirectives.ts.
When this page changes, also update:
- Frontend Structure
- Frontend Structure Guidelines
- Frontend Architecture Changelog
-
_Sidebar.mdif new top-level pages or sections are added
This wiki serves as the central documentation and knowledge base for the Forests Client Management System, maintained by the British Columbia Ministry of Forests.
Please Note:
- This wiki is a living document maintained by the development team and contributors.
- When making substantial architectural or code changes, remember to update the corresponding wiki documentation.
- Always verify critical environment configurations against team vaults and OpenShift secret managers.
Have questions, found a documentation discrepancy, or need clarification?
- Questions or Bug Reports: Open an issue in the GitHub repository
- Pull Requests: Submit code or doc improvements via Pull Requests
- Database Schema Reference: Explore our SchemaSpy ER Diagrams
Forest Client Wiki | GitHub Repository
- Architecture Overview
- Frontend Architecture
- Frontend Structure
- Backend Architecture
- Backend Structure
- Legacy Architecture
- Processor Architecture
- Data Model
- Development Overview
- Local Setup & Docker
- Frontend Setup
- Backend Setup
- Project Conventions
- Frontend Structure Guidelines