Component Tree¶
The frontend follows a loose Atomic Design split (atoms/molecules/organisms) under frontend/src/components/, plus feature folders (audit/, charts/, layout/). Pages live in frontend/src/pages/ grouped by audience: app/ (end users), back-office/ (admins), system/ (superadmin).
Top-Level Tree¶
graph TD
App[App.vue] --> Layout[layouts/MainLayout.vue]
Layout --> Router{router-view}
Router --> AppPages[pages/app/]
Router --> BackOffice[pages/back-office/]
Router --> System[pages/system/]
Router --> Errors[ErrorNotFound / ErrorUnauthorized]
AppPages --> LoginPage
AppPages --> WorkspaceSetupPage
AppPages --> WorkspacePage --> HomePage
WorkspacePage --> ModulePage
WorkspacePage --> ModuleResultsPage
WorkspacePage --> ResultsPage
WorkspacePage --> SimulationsPage
WorkspacePage --> AddSimulationPage
WorkspacePage --> EditSimulationPage
WorkspacePage --> DocumentationPage
BackOffice --> UserManagementPage
BackOffice --> DataManagementPage
BackOffice --> ReportingPage
BackOffice --> UITextsEditingPage
BackOffice --> DocumentationEditingPage
System --> LogsPage WorkspacePage is the workspace shell: it owns the unit/year context and renders the module-scoped children.
Component Layers¶
graph LR
Pages[pages/*] --> Organisms
Pages --> Layout[components/layout]
Organisms[components/organisms] --> Molecules
Organisms --> Audit[components/audit]
Organisms --> Charts[components/charts]
Molecules[components/molecules] --> Atoms
Atoms[components/atoms] --> Quasar[Quasar primitives]
Charts --> ECharts[vue-echarts]
Pages -.uses.-> Stores[(Pinia stores)]
Organisms -.uses.-> Stores
Stores -.calls.-> Api[api/ ky http client] - atoms/ —
CO2Container,CO2DestinationInput,Co2LanguageSelector,ModuleIcon. Single-purpose, story-covered. - molecules/ —
BigNumber,ChartContainer,Co2TimelineItem,HeadCountBarChart,NoteDialog. - organisms/ — feature subfolders:
backoffice/,data-management/,layout/,login/,module/,workspace-selector/, plus large composed views likeAdditionalCategoriesSection.vueandItFocusSection.vue. - audit/ — back-office audit log UI:
AuditDetailDrawer,AuditFilterBar,AuditPagination,AuditSearchBar,AuditStatCards,AuditTable.
Pinia Stores¶
State lives in frontend/src/stores/. The active stores are:
| Store | Purpose |
|---|---|
auth | Current user, roles_raw, permissions, login/out |
workspace | Selected unit + year; drives most of the app shell |
modules | Module config and per-module activity data |
factors | Emission factors loaded from the backend |
unitFilters | Unit picker filtering and search |
yearConfig | Year-scoped configuration (open/closed years, etc.) |
building_rooms | Room metadata for equipment module |
files | Upload state for CSV imports |
backoffice | Back-office shell state |
backofficeDataManagement | Factor / unit / activity admin |
colorblind | Accessibility palette toggle |
Routing & Guards¶
Defined in frontend/src/router/routes.ts. All authenticated routes nest under /:language(en|fr)/; the workspace routes nest further under /:unit/:year/. Guards from router/guards/:
permissionGuard— one guard for all gated routes: back-office pages (meta.requiredPermission, any-scope) and module data-entry pages (meta.moduleEdit, workspace-scoped view+edit).validateUnitGuard,redirectToWorkspaceIfSelectedGuard— workspace.
API Layer¶
frontend/src/api/ wraps ky with endpoint constants and an auth-aware fetch wrapper. Components do not call fetch directly: they go through stores, which call the api layer. Errors bubble back to UI as Quasar Notify toasts.
Charts and Visualization¶
components/charts/ plus vue-echarts (declarative ECharts). Use ChartContainer.vue for sizing and reactive resize. See HeadCountBarChart.vue for a worked example.