Conversation
⚡ Lighthouse CI ResultsPerformance scores will be available in the artifacts. |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## dev #868 +/- ##
=======================================
Coverage 60.43% 60.43%
=======================================
Files 137 137
Lines 11997 11997
=======================================
Hits 7250 7250
Misses 4747 4747
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Pull request overview
Refactors the Back Office “Data management” area by extracting the previous monolithic page into dedicated components, centralizing “module completeness” logic in the yearConfig Pinia store, and using that completeness state to disable the “Open year for users” action when mandatory uploads are missing/failed.
Changes:
- Extracts module/submodule configuration UI and reduction objectives into separate components and shared constants.
- Moves completeness helpers into
yearConfigstore and wires them into the UI (incl. disabling “Open year for users” with tooltip). - Adds i18n strings for loading/disabled tooltip messaging.
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| frontend/src/stores/yearConfig.ts | Adds completeness helpers (isModuleIncomplete, anyModuleIncomplete, etc.) using latest_jobs + module config constants. |
| frontend/src/pages/back-office/DataManagementPage.vue | Replaces in-page logic with new components and disables “Open year for users” based on store completeness. |
| frontend/src/i18n/backoffice_data_management.ts | Adds translations for loading and disabled tooltip. |
| frontend/src/constant/backoffice-module-config.ts | Introduces shared module/submodule upload configuration constants. |
| frontend/src/components/organisms/data-management/ModuleConfig.vue | Implements module-level UI (enable toggle, uncertainty, common uploads, recalculation dialog) and provides callbacks to submodules. |
| frontend/src/components/organisms/data-management/SubmoduleConfig.vue | Implements per-submodule UI (enable toggle, threshold, uploads, computed factor sync). |
| frontend/src/components/organisms/data-management/ReductionObjectivesSection.vue | Extracts reduction objectives uploads + 3-slot goal editor into its own component. |
⚡ Lighthouse CI ResultsPerformance scores will be available in the artifacts. |
2 similar comments
⚡ Lighthouse CI ResultsPerformance scores will be available in the artifacts. |
⚡ Lighthouse CI ResultsPerformance scores will be available in the artifacts. |
9028b15 to
758de82
Compare
⚡ Lighthouse CI ResultsPerformance scores will be available in the artifacts. |
| function isModuleEnabled(module: string): boolean { | ||
| const subs = | ||
| MODULE_SUBMODULES[module as keyof typeof MODULE_SUBMODULES] ?? []; | ||
| const moduleTypeId = subs.length > 0 ? subs[0].moduleTypeId : 0; | ||
| if (!moduleTypeId) return true; | ||
| return ( | ||
| config.value?.config?.modules?.[String(moduleTypeId)]?.enabled ?? true | ||
| ); | ||
| } |
There was a problem hiding this comment.
isModuleEnabled derives moduleTypeId only from MODULE_SUBMODULES. If a module ever has only MODULE_COMMON_UPLOADS (or if a module is missing from MODULE_SUBMODULES due to config drift), moduleTypeId becomes 0 and the module is treated as enabled, which can cause anyModuleIncomplete to miss required checks. Consider resolving moduleTypeId from either (a) the first submodule, (b) the first common upload, or (c) a dedicated MODULE_TYPE_IDS mapping that is the single source of truth.
| <q-btn | ||
| icon="calendar_month" | ||
| color="accent" | ||
| :label="$t('open_year_for_users')" | ||
| class="text-weight-medium text-capitalize q-mt-md" | ||
| /> | ||
| :disable="yearConfigStore.anyModuleIncomplete" | ||
| > | ||
| <q-tooltip v-if="yearConfigStore.anyModuleIncomplete"> | ||
| {{ $t('data_management_open_year_disabled_tooltip') }} | ||
| </q-tooltip> | ||
| </q-btn> |
There was a problem hiding this comment.
The button is only disabled when anyModuleIncomplete is true, but anyModuleIncomplete is false when no config is loaded (config.value is falsy). This can leave “Open year for users” enabled while the year config is missing / still loading. Consider also disabling (or hiding) the button when !yearConfigStore.config (and possibly when yearConfigStore.loading), e.g., :disable="!yearConfigStore.config || yearConfigStore.anyModuleIncomplete".
| <q-card | ||
| v-if="getImportRow(common).hasData" | ||
| flat | ||
| class="col q-pa-lg" | ||
| :style="cardStyle(dataButtonColor(getImportRow(common)))" | ||
| > |
There was a problem hiding this comment.
getImportRow(common) (and the derived dataButtonColor/factorButtonColor) is invoked multiple times per render, and each call filters latestJobs. This can create avoidable work on each reactive update. Consider computing the row once per common item (e.g., via a small wrapper component, a computed map keyed by moduleTypeId/dataEntryTypeId, or assigning const row = getImportRow(common) in a setup()-level helper for the loop) and then use row throughout the template.
| function downloadLastCsv(row: ImportRow, targetType: TargetType) { | ||
| const job = | ||
| targetType === TargetType.DATA_ENTRIES | ||
| ? row.lastDataJob | ||
| : row.lastFactorJob; | ||
| if (!job?.meta) return; | ||
| const filePath = (job.meta as Record<string, unknown>) | ||
| .processed_file_path as string; | ||
| if (!filePath) return; | ||
| const a = document.createElement('a'); | ||
| a.href = `/api/v1/files/${filePath}`; | ||
| a.download = filePath.split('/').pop() || filePath; | ||
| document.body.appendChild(a); | ||
| a.click(); | ||
| document.body.removeChild(a); | ||
| } |
There was a problem hiding this comment.
a.href interpolates filePath directly into the URL. If filePath can contain characters needing escaping (spaces, #, ?) the download link can break; and if the backend expects path-safe encoding, it can also cause unexpected routing. Prefer building the URL with encoding (e.g., encodeURIComponent for each path segment or a URL object) before assigning to a.href.
| function safeFileName(meta: unknown): string | undefined { | ||
| const fp = (meta as Record<string, unknown>)?.file_path as string | undefined; | ||
| if (!fp) return undefined; | ||
| const parts = fp.split('/'); | ||
| return parts.length ? parts[parts.length - 1] : fp; | ||
| } |
There was a problem hiding this comment.
safeFileName only reads meta.file_path, but downloadLastCsv uses meta.processed_file_path. If the API only provides processed_file_path (or uses it for the relevant job type), the UI can display an empty filename next to the success checkmark. Consider falling back to processed_file_path when file_path is missing, and suppressing the checkmark display if no filename can be resolved.
| function findJob( | ||
| jobs: SyncJobSummary[], | ||
| moduleTypeId: number, | ||
| targetType: number | null, | ||
| dataEntryTypeId?: number, | ||
| ingestionMethod?: IngestionMethod, | ||
| ): SyncJobSummary | undefined { | ||
| const candidates = jobs.filter( | ||
| (j) => j.module_type_id === moduleTypeId && j.target_type === targetType, | ||
| ); |
There was a problem hiding this comment.
findJob/toSyncJobResponse/getImportRow/cardStyle/downloadLastCsv/safeFileName are duplicated between SubmoduleConfig.vue and ModuleConfig.vue. This duplication increases the risk of behavior drifting (e.g., filename meta key handling, ingestion-method selection, styling rules). Consider extracting these helpers into a shared composable/utility (e.g., useBackofficeImportRows() + formatJobMetaFilename()), then reusing it in both components.
What does this change?
Splits the monolithic
DataManagementPage.vue(~1500-line script) into focused, self-contained components:ModuleConfig.vue— module-level expansion item with activation toggle, uncertainty selector, common uploads, and submodule list; usesprovide/injectto share callbacks with childrenSubmoduleConfig.vue— per-submodule expansion item with enable toggle, threshold input, data/factor/reference upload cards, and computed-factor sync for Research FacilitiesReductionObjectivesSection.vue— standalone reduction-objectives card with file uploads and 3-slot goal editorbackoffice-module-config.ts— extractedMODULE_SUBMODULESandMODULE_COMMON_UPLOADSconstants into a dedicated fileCompleteness helpers (
isModuleEnabled,isSubmoduleEnabled,isModuleIncomplete,isSubmoduleIncomplete,anyModuleIncomplete) moved from the page into theyearConfigPinia store so they're reusable across components.The Open year for users button is now disabled with a tooltip when any enabled module has missing or failed mandatory uploads.
Two backend fixes are also included:
equipment_electric_consumption/schemas.py: removed fallback that readactive_hours/standby_hoursfrom factor values when the user didn't supply them — if these are missing the formula now correctly returnsNone.module_per_year.py: simplifiedfactors_maps_by_typeconstruction (removed incorrect key-prefix filtering that broke factor lookup); cleaned up unusedfactor_id_to_factorfrombase_csv_provider.py.Why is this needed?
DataManagementPage.vuehad grown to ~2500 lines of mixed concerns, making it hard to review, test, and extend. Extracting components and moving store logic into Pinia reduces coupling, enables independent testing, and fixes issue #630 where certain criteria were missing from the emission computation pipeline.Type of change
Related issues