Skip to content

Frontend Structure

Paulo Gomes da Cruz Junior edited this page Oct 23, 2026 · 10 revisions

Code Structure Tree — Waste Plus Frontend

Directory Layout

frontend/
├── index.html                         # HTML entry point (preloads fonts, gov logos, runtime config)
├── vite.config.ts                     # Vite + Vitest + PWA plugin configuration
├── eslint.config.js                   # Flat ESLint config (TS, React Hooks, TanStack Query, Prettier)
├── playwright.config.ts               # Playwright E2E + a11y multi-browser config
├── tsconfig.json                      # Project references root
├── tsconfig.app.json                  # App source (strict, ES2022, react-jsx)
├── tsconfig.node.json                 # Node/build scripts (ES2023)
├── tsconfig.test.json                 # Test-specific overrides
├── package.json                       # Dependencies, scripts, overrides, engines
├── Caddyfile                          # Caddy web server config (production)
├── Dockerfile                         # Container build
├── openshift.deploy.yml               # OpenShift deployment manifest
│
├── public/                            # Static assets served as-is
│   ├── robots.txt
│   ├── data/
│   │   ├── config.js                  # Runtime environment injection (window.config)
│   │   └── params.js                  # Runtime parameters injection
│   ├── fonts/                         # BCSans font files (woff2)
│   ├── icons/                         # Favicons, app icons, site.webmanifest
│   ├── img/                           # BC gov logos, landing images
│   └── screenshots/                   # PWA install screenshots
│
├── src/                               # Application source
│   ├── main.tsx                       # React root — providers, Amplify init, SW registration
│   ├── App.tsx                        # Root component — routes + devtools
│   ├── env.ts                         # Merged Vite + runtime env accessor
│   ├── registerServiceWorker.ts       # PWA service worker registration
│   ├── service-worker.ts             # Workbox SW with precaching + background sync
│   ├── vite-env.d.ts                  # Vite client type declarations
│   │
│   ├── assets/                        # Static imports (images, SVG, etc.)
│   │
│   ├── styles/                        # Global SCSS
│   │   └── _fonts.scss                # Font-face declarations
│   │
│   ├── router.tsx                      # createAppRouter factory + router singleton + single Register
│   ├── routerContext.ts                # AppRouterContext type ({ queryClient })
│   ├── routeTree.gen.ts                # Generated route tree (committed; tsc runs before vite build)
│   │
│   ├── routes/                         # File-based routes — one file per route, folder = URL
│   │   ├── __root.tsx                  # Root route: title sync, auth-aware not-found, error component
│   │   ├── index.tsx                   # / — public-only landing
│   │   ├── dashboard.tsx               # /dashboard — post-login redirect resolver
│   │   ├── no-role.tsx                 # /no-role — status page (outside the shell)
│   │   ├── unauthorized.tsx            # /unauthorized — status page (outside the shell)
│   │   └── _authenticated/             # Auth-gated subtree (pathless layout in _authenticated.tsx)
│   │       ├── _admin.tsx              # ADMIN role-tier layout
│   │       ├── _admin/                 # /configuration subtree (flag-gated in configuration.tsx)
│   │       ├── _client-access.tsx       # VIEWER/SUBMITTER role-tier layout
│   │       ├── _client-access/clients.tsx  # /clients
│   │       ├── search.tsx              # /search (owns filter state via validateSearch)
│   │       ├── reporting-units/         # /reporting-units/* (flag-gated routes + loader)
│   │       └── $.tsx                    # Catch-all 404 inside the shell
│   │
│   ├── routing/                        # Shared routing helpers (route files only in routes/)
│   │   ├── auth.ts                     # computeAuthRedirect, hasAnyRole (pure logic)
│   │   ├── redirectStorage.ts          # Single-key session storage for post-login redirect URL
│   │   ├── safeRedirect.ts              # Open-redirect protection + OAuth callback detection
│   │   ├── searchParams.ts             # Zod /search schema + router-level searchParamCodec
│   │   ├── staticData.ts               # StaticDataRouteOption augmentation + NavTarget union
│   │   └── nav/                        # Side-nav projection from route staticData
│   │       ├── useNavEntries.ts         # Filters/sorts nav entries (roles, flags, connectivity)
│   │       └── routeMetadata.ts        # collectRouteGates (gate inheritance), resolveRouteTitle
│   │
│   ├── context/                       # React Context providers
│   │   ├── auth/                      # Authentication context
│   │   │   ├── AuthContext.tsx         # Context definition (user, isLoggedIn, actions)
│   │   │   ├── AuthProvider.tsx        # Amplify session, token refresh, mock support
│   │   │   ├── authUtils.ts           # JWT parsing, role/privilege extraction
│   │   │   ├── userAccessValidation.ts # Role validation rules engine
│   │   │   ├── useAuth.ts             # Consumer hook
│   │   │   └── types.ts               # Role, FamLoginUser, IdpProvider types
│   │   │
│   │   ├── layout/                    # Layout state context
│   │   │   ├── LayoutContext.tsx       # Side nav, header panel toggle state
│   │   │   ├── LayoutProvider.tsx      # Responsive breakpoint-aware provider
│   │   │   └── useLayout.ts           # Consumer hook
│   │   │
│   │   ├── theme/                     # Carbon theme context
│   │   │   ├── ThemeContext.tsx        # Theme state (g10/g100 toggle)
│   │   │   ├── ThemeProvider.tsx       # Syncs with preference context
│   │   │   └── useTheme.ts            # Consumer hook
│   │   │
│   │   ├── preference/                # User preferences context
│   │   │   ├── PreferenceContext.tsx   # Preference state + loading
│   │   │   ├── PreferenceProvider.tsx  # API load/save lifecycle
│   │   │   ├── usePreference.ts       # Consumer hook
│   │   │   ├── utils.ts               # Backend preference read/write
│   │   │   └── types.ts               # CarbonTheme, UserPreference
│   │   │
│   │   ├── notification/              # Toast notification context
│   │   │   ├── NotificationContext.tsx # Toast display + notification event contract
│   │   │   ├── NotificationProvider.tsx # Carbon toast rendering, queueing, and listener registry
│   │   │   ├── eventBridge.ts          # Outside-React adapter for notification publishers
│   │   │   └── useNotification.ts     # Consumer hook
│   │   │
│   │   └── pageTitle/                 # Page title context
│   │       ├── PageTitleContext.tsx    # Hierarchical title management
│   │       ├── PageTitleProvider.tsx   # Document title sync
│   │       └── usePageTitle.ts        # Consumer hook
│   │
│   ├── hooks/                         # Custom React hooks
│   │   ├── useBreakpoint/             # Carbon breakpoint tracking
│   │   │   ├── index.tsx              # Resize listener, breakpoint detection
│   │   │   └── types.ts              # Breakpoint type definitions
│   │   │
│   │   ├── useDebounce/               # Generic debounce
│   │   │   └── index.tsx
│   │   │
│   │   ├── useOfflineMode/            # Online/offline detection
│   │   │   ├── index.tsx              # Hook exposing online status
│   │   │   └── onlineStatusStore.ts   # Singleton store with feature flag
│   │   │
│   │   ├── useOutsideClick/           # Click-outside detection
│   │   │   └── index.ts
│   │   │
│   │   ├── useNotificationEvents/     # Notification event publishing and scoped listeners
│   │   │   ├── index.tsx              # Hook: sendEvent, subscribe, unsubscribe, clearEvents
│   │   │   ├── eventHandler.ts        # Notification event helpers for internal and external publishers
│   │   │   ├── useScopedNotification.ts # Page-scoped inline notification hook
│   │   │   ├── types.ts               # Notification event types and payload shape
│   │   │   └── useNotificationEvents.unit.test.tsx # Hook contract tests
│   │   │
│   │   └── useSyncPreferencesToFilters/  # Preference → filter sync
│   │       └── index.tsx
│   │
│   ├── config/                        # Infrastructure configuration
│   │   ├── api/                       # HTTP client layer
│   │   │   ├── types.ts               # HttpClient, APIConfig, ApiError, ProblemDetails
│   │   │   ├── request.ts             # Axios request pipeline
│   │   │   ├── CancelablePromise.ts   # Cancellable async wrapper
│   │   │   ├── problemDetailsMiddleware.ts # RFC 7807 error normalization
│   │   │   └── failureNotificationMiddleware.ts # Global toast emission for unscoped API failures
│   │   │
│   │   ├── fam/                       # AWS Amplify / Cognito config
│   │   │   └── config.ts             # Amplify auth config grouped by environment, redirect, and logout concerns
│   │   │
│   │   ├── pwa/                       # Progressive Web App config
│   │   │   ├── middleware.ts          # Offline data + mutation queue middleware
│   │   │   ├── syncHandlers.ts        # Background sync placeholders
│   │   │   ├── types.ts              # PwaDB, CachedValue, IdbMiddlewareOptions
│   │   │   ├── utils.ts              # Periodic/background sync registration
│   │   │   └── idb/
│   │   │       └── config.ts          # IndexedDB setup and CRUD utilities
│   │   │
│   │   ├── react-query/               # TanStack Query config
│   │   │   ├── config.ts             # QueryClientConfig (3h stale, no retry)
│   │   │   ├── hooks.ts              # Shared query hooks, batched filter queries, scoped error notifications
│   │   │   ├── queryKeys.ts          # Typed query-key factory with metadata-aware cache keys
│   │   │   ├── retry.ts              # noRetry stub
│   │   │   └── TimeUnits.ts          # Time constants
│   │   │
│   │   └── tests/                     # Test infrastructure
│   │       ├── setup-env.ts           # Vitest global mocks (Amplify, APIs, DOM)
│   │       ├── custom-matchers.ts     # toContainText, toBeEmptyDOMElement
│   │       ├── auth.helper.ts         # E2E auth mocking / real login
│   │       ├── auth.setup.ts          # Playwright auth setup fixture
│   │       ├── e2e.helper.ts          # API route mocking utilities
│   │       ├── a11y.helper.ts         # Axe accessibility helpers
│   │       ├── coverage.setup.ts      # Coverage instrumentation setup
│   │       ├── browser.setup.ts       # Browser test setup
│   │       ├── browser.teardown.ts    # Browser test teardown
│   │       └── MockPromise.ts         # Controllable promise for testing
│   │
│   ├── services/                      # Backend service clients
│   │   ├── APIs.ts                    # Service registry (user, search, codes, forestclient)
│   │   ├── search.service.ts          # Reporting unit search API
│   │   ├── search.types.ts            # Search DTOs
│   │   ├── search.utils.ts            # View-model ↔ DTO converters
│   │   ├── codes.service.ts           # Reference data API
│   │   ├── users.service.ts           # User preferences API
│   │   ├── forestclient.service.ts    # Forest client lookup API
│   │   ├── forestclient.types.ts      # Forest client DTOs
│   │   ├── pagination.types.ts        # PageableRequest, sorting utilities
│   │   ├── types.ts                   # Re-exported shared service types
│   │   ├── utils.ts                   # removeEmpty, B3 headers, DTO converters
│   │   └── utils.types.ts             # Override, DefinedValue, ArrayKey
│   │
│   ├── components/                    # Reusable UI components
│   │   ├── Layout/                    # Application shell
│   │   │   ├── index.tsx              # Shell wrapper (header + content + sidenav)
│   │   │   ├── LayoutHeader/          # Global header bar
│   │   │   │   ├── index.tsx
│   │   │   │   ├── LayoutHeaderGlobalBar.tsx
│   │   │   │   └── utils.ts
│   │   │   ├── LayoutHeaderPanel/     # Right-side header panel
│   │   │   │   └── index.tsx
│   │   │   ├── LayoutSideNav/         # Side navigation
│   │   │   │   └── index.tsx
│   │   │   ├── HeaderPanelProfile/    # User profile in header panel
│   │   │   │   └── index.tsx
│   │   │   ├── HeaderDistrictDisplay/ # District/client display in header
│   │   │   │   ├── index.tsx
│   │   │   │   ├── ClientDisplay/
│   │   │   │   └── DistrictDisplay/
│   │   │   ├── AvatarImage/           # User avatar component
│   │   │   │   └── index.tsx
│   │   │   └── ThemeToggle/           # Dark/light theme switch
│   │   │       └── index.tsx
│   │   │
│   │   ├── core/                      # Domain-agnostic reusable components
│   │   │   ├── PageTitle/             # Page header with breadcrumbs
│   │   │   │   ├── index.tsx
│   │   │   │   └── types.ts
│   │   │   ├── EmptySection/          # Empty state display
│   │   │   │   └── index.tsx
│   │   │   ├── Subtitle/             # Section subtitle
│   │   │   │   └── index.tsx
│   │   │   ├── DistrictSelection/     # District picker panel
│   │   │   │   ├── index.tsx
│   │   │   │   ├── DistrictListing/
│   │   │   │   ├── DistrictItem/
│   │   │   │   ├── ClientListing/
│   │   │   │   ├── constants.ts
│   │   │   │   ├── types.ts
│   │   │   │   └── utils.ts
│   │   │   └── Tags/                  # Utility tag components
│   │   │       ├── ColorTag/
│   │   │       ├── DateTag/
│   │   │       ├── EmptyValueTag/
│   │   │       ├── UnderConstructionTag/
│   │   │       └── YesNoTag/
│   │   │
│   │   ├── Form/                      # Form and data display components
│   │   │   ├── FormulaInput/          # Monaco-powered formula editor with BigNumber evaluation
│   │   │   │   ├── index.tsx          # Root component + FormulaInputProps interface
│   │   │   │   ├── index.scss         # BEM styles + Carbon token bindings
│   │   │   │   ├── index.unit.test.tsx # 61-test suite (Vitest + Testing Library)
│   │   │   │   ├── useFormulaEngine.ts # Evaluation, variable resolution, precision drift check
│   │   │   │   ├── useMonacoFormula.ts # Monaco language registration, completions, hover, markers
│   │   │   │   ├── DependencyGraph.tsx # Variable dependency visualisation (Carbon Tags)
│   │   │   │   ├── VariablePanel.tsx  # Available-variable list (fixed / dynamic)
│   │   │   │   ├── types.ts           # FormulaError, EvaluationResult, PositionedToken, DriftCheckResult
│   │   │   │   └── utils.ts           # Pure helpers: parseFormula, evaluateFormula, tokenize, checkDrift
│   │   │   ├── TableResource/         # Generic paginated data table
│   │   │   │   ├── index.tsx
│   │   │   │   ├── types.ts
│   │   │   │   ├── useTableToolbar.ts
│   │   │   │   ├── ColumnCustomizationMenu/
│   │   │   │   ├── TableResourceActions/
│   │   │   │   ├── TableResourceExpandRow/
│   │   │   │   └── TableResourceRow/
│   │   │   ├── ActiveMultiSelect/     # Multi-select with active state
│   │   │   │   └── index.tsx
│   │   │   ├── AutoCompleteInput/     # Autocomplete text input
│   │   │   │   └── index.tsx
│   │   │   ├── ReadonlyInput/         # Read-only display field
│   │   │   │   ├── index.tsx
│   │   │   │   ├── constants.ts
│   │   │   │   └── utils.ts
│   │   │   └── SearchInput/           # Search text input
│   │   │       └── index.tsx
│   │   │
│   │   └── waste/                     # Domain-specific components
│   │       ├── WasteSearch/           # Waste search feature
│   │       │   ├── index.tsx
│   │       │   ├── WasteSearchTable/
│   │       │   │   ├── index.tsx
│   │       │   │   ├── rowActions.tsx
│   │       │   │   ├── index.unit.test.tsx
│   │       │   │   ├── rowActions.unit.test.tsx
│   │       │   │   └── constants.tsx
│   │       │   ├── WasteSearchFilters/
│   │       │   │   ├── index.tsx
│   │       │   │   ├── useWasteSearchFilters.ts
│   │       │   │   ├── useWasteSearchFilterOptions.ts
│   │       │   │   └── utils.ts
│   │       │   ├── WasteSearchFiltersActive/
│   │       │   │   ├── index.tsx
│   │       │   │   └── utils.ts
│   │       │   ├── WasteSearchFiltersAdvanced/
│   │       │   │   ├── index.tsx
│   │       │   │   ├── AdvancedFilterClientInput.tsx
│   │       │   │   ├── AdvancedFilterDateRange.tsx
│   │       │   │   ├── useAdvancedFilterHandlers.ts
│   │       │   │   ├── useClientLookup.ts
│   │       │   │   └── utils.ts
│   │       │   └── WasteSearchTableExpandContent/
│   │       │       └── index.tsx
│   │       ├── MyClientListing/       # Client list table
│   │       │   ├── index.tsx
│   │       │   └── constants.tsx
│   │       ├── CodeDescriptionTag/    # Code + description badge
│   │       │   └── index.tsx
│   │       ├── RedirectLinkTag/       # Clickable redirect link
│   │       │   └── index.tsx
│   │       ├── RoleBasedRedirectLinkTag/ # Role-gated link
│   │       │   └── index.tsx
│   │       ├── TooltipRedirectLinkTag/   # Link with tooltip
│   │       │   └── index.tsx
│   │       └── TooltipRoleBasedRedirectLinkTag/ # Role-gated link + tooltip
│   │           └── index.tsx
│   │
│   └── pages/                         # Route-level page components
│       ├── Landing/                   # Public landing page
│       │   └── index.tsx
│       ├── MyClientList/              # My clients page
│       │   └── index.tsx
│       ├── WasteSearch/               # Waste search page
│       │   └── index.tsx
│       ├── GlobalError/               # Route error boundary fallback
│       │   └── index.tsx
│       ├── NoRole/                    # No FAM roles assigned
│       │   └── index.tsx
│       ├── NotFound/                  # 404 page
│       │   └── index.tsx
│       └── RoleError/                 # Unauthorized / role violation
│           └── index.tsx
│
├── stub/                              # WireMock stubs for local dev
│   ├── __files/                       # Response body files
│   └── mappings/                      # Request/response mappings
│
├── build/                             # Production build output
├── coverage/                          # Test coverage reports
├── test-reports/                      # JUnit test reports
└── test-results/                      # Playwright test artifacts

Folder Taxonomy Commentary

src/components/

Three-tier organization:

Tier Folder Purpose
Shell Layout/ Application frame: header, sidenav, panels
Reusable core/ Domain-agnostic components: PageTitle, Tags, EmptySection
Domain waste/ Feature-specific components: WasteSearch, MyClientListing
Forms Form/ Generic form controls: TableResource, inputs, selects

src/context/

Each context follows a consistent pattern:

  • <Name>Context.tsx — createContext() with typed state
  • <Name>Provider.tsx — side-effects, API calls, lifecycle
  • use<Name>.ts — consumer hook with null-check
  • types.ts — type definitions (when needed)

src/hooks/

Cross-cutting utilities organized by concern. Each hook gets its own directory with:

  • index.tsx — the hook implementation
  • types.ts — type definitions (optional)
  • *.unit.test.tsx — co-located test

src/services/

Service-per-domain pattern where each service:

  • Extends HttpClient base class
  • Has its own *.types.ts for DTOs
  • Returns CancelablePromise<T>
  • Applies problemDetailsMiddleware() per-request

src/config/

Infrastructure concerns separated by tool:

  • api/ — HTTP client infrastructure
  • fam/ — AWS Amplify/Cognito config
  • pwa/ — Offline and service worker config
  • react-query/ — TanStack Query client config
  • tests/ — Test setup and helpers

Proposed Improved Structure

Current Gaps

  1. No src/queries/ directory — Resolved. A centralized query hook layer now lives in src/config/react-query/hooks.ts with a typed key factory in src/config/react-query/queryKeys.ts. All production useQuery calls are routed through these shared hooks.
  2. No src/types/ directory — Resolved. Shared types and Zod 4 wire schemas now live in src/types/, one *.types.ts module per domain, with an index.ts barrel re-exporting them as @/types (branch chore/tech-debt-types, commits 2da3794d..0c3f4fcc). src/services/types.ts remains as a back-compat re-export barrel. See the Frontend Type Boundary pattern.
  3. No src/constants/ directory — Constants are co-located per component. A shared constants directory would help with app-wide values.
  4. Test helpers in config/tests/ — E2E and unit helpers cohabit. Separating would clarify ownership.

Recommended Future Layout

src/
├── queries/                    # NEW: TanStack Query hooks
│   ├── useSearchQuery.ts       #   Wraps SearchService.searchReportingUnit
│   ├── useCodesQuery.ts        #   Wraps CodesService methods
│   └── useForestClientQuery.ts #   Wraps ForestClientService methods
│
├── types/                      # DONE: Shared domain types + Zod 4 wire schemas
│   ├── index.ts                #   Barrel: export type * from each *.types.ts
│   ├── pagination.types.ts     #   PageType, PageableResponse (extracted from services/)
│   ├── search.types.ts         #   Search DTOs + codeDescriptionSchema
│   └── …                       #   audit, forestclient, speciesComposition, tableResource, utils
│
├── constants/                  # NEW: App-wide constants
│   └── roles.ts                #   Role enums (extracted from auth/types)
│
├── config/tests/               # SPLIT into:
│   ├── unit/                   #   Vitest setup, mocks, matchers
│   └── e2e/                    #   Playwright helpers, auth, stubs

Future-Proofing Recommendations

  1. Feature folders — As the app grows beyond waste search, consider grouping by feature (src/features/waste-search/, src/features/client-management/) to co-locate routes, components, queries, and types.
  2. Barrel exports — Partially resolved. src/types/index.ts barrels all shared types as @/types, and src/services/types.ts re-exports them for back-compat. Barrels for context/ and hooks/ remain open ideas.
  3. Query key factory — Resolved. src/config/react-query/queryKeys.ts centralizes all query keys in a typed queryKeys factory organized by domain (codes, search, forestClient, preference, autocomplete).
  4. Error boundary components — Add component-level error boundaries for resilient feature isolation.

Waste Plus Wiki

Home

Architecture

Codes

Decisions

Knowledge Base

Development

Changes

Clone this wiki locally