1216 — BackOffice upload card: API success status + FACTORS-first order¶
1. Problem¶
Two small bugs reported on the BackOffice Configuration page (issue #1216):
- Box order. Screenshot from Equipments showed the DATA card rendered before the FACTORS card. Specification is FACTORS first, DATA second — same as every other module.
- API connection success. When the operator uploads via the API path (e.g. plane / cloud connectors) and the API job succeeds, the DATA card stays grey ("accent"). The operator can't tell "API connected, rows imported" from "nothing uploaded yet".
2. Investigation¶
Box order¶
Bug already fixed on dev by commit 69c5a3d8 on 2026-05-19 (chore(frontend): invert factors and data for common) following the per-submodule fix 780773a8 (fix(310): reorder factors before data on data-management page). Both render paths now declare FACTORS first:
frontend/src/components/molecules/data-management/ModuleUploadsSection.vue:98-121(common uploads — Equipments / Purchases)frontend/src/components/molecules/data-management/SubmoduleItem.vue:253-285(per-submodule uploads)
No code change needed for bug 1; the fix is to pin the order with a regression test so a future refactor can't silently revert it.
API success → green¶
frontend/src/composables/useUploadCard.ts:30-36 reads only row.lastDataJob:
function dataButtonColor(row: ImportRow): string {
if (row.isDisabled) return "grey-4";
if (!row.lastDataJob) return "accent"; // ← API-only row hits this
if (row.lastDataJob.result === IngestionResult.ERROR) return "negative";
if (row.lastDataJob.result === IngestionResult.WARNING) return "warning";
return "positive";
}
ImportRow already exposes lastApiDataJob (populated from subConfig.latest_api_data_job in useModuleConfig.getImportRow), so the data is on the row — dataButtonColor just doesn't look at it.
3. Fix¶
Bug 2: extend dataButtonColor to honour lastApiDataJob.result === SUCCESS¶
Precedence is preserved: disabled > CSV-error > CSV-warning > CSV-success > API-success > accent. CSV result always wins over API result so an errored CSV after a prior API success stays red. Only explicit IngestionResult.SUCCESS triggers green — an API job with an unrecognised result falls through to accent (no silent green-by-default).
function dataButtonColor(row: ImportRow): string {
if (row.isDisabled) return "grey-4";
if (row.lastDataJob?.result === IngestionResult.ERROR) return "negative";
if (row.lastDataJob?.result === IngestionResult.WARNING) return "warning";
if (row.lastDataJob?.result === IngestionResult.SUCCESS) return "positive";
if (row.lastApiDataJob?.result === IngestionResult.SUCCESS) return "positive";
return "accent";
}
The inline "API success: N rows imported" caption in UploadCard.vue (lines 340-354) is gated on apiJob && !hasApiErrorOrWarn, independent of buttonColor — it keeps rendering alongside the new green border.
Bug 1: regression test only¶
No code change. Test pins FACTORS-first in the upload row.
4. Files changed¶
| File | Change |
|---|---|
frontend/src/composables/useUploadCard.ts | Extend dataButtonColor to surface API success. |
frontend/tests/integration/data-management.spec.ts | Add tests 1216a (API-only → green button) and 1216b (FACTORS before DATA in render order). |
5. Before / after¶
| State | Before | After |
|---|---|---|
Only lastApiDataJob.result === SUCCESS | Data button = accent (grey), card border grey. Inline "API ingestion: 42 rows imported" text below. | Data button = positive (green), card border green. Inline text unchanged. |
lastDataJob.result === SUCCESS (CSV) | green | green (unchanged) |
lastDataJob.result === ERROR, lastApiDataJob.result === SUCCESS | red (negative) | red (CSV error wins, unchanged) |
| No jobs at all | grey (accent) | grey (unchanged) |
| Equipments / Purchases upload cards | FACTORS before DATA (already fixed in 69c5a3d8) | FACTORS before DATA, now pinned by a regression test. |
6. Regression tests¶
frontend/tests/integration/data-management.spec.tstest1216a — successful API ingestion (no CSV) turns the data card button green: mounts a year-configuration where the member submodule has onlylatest_api_data_job: SUCCESS(no CSVlatest_data_job), expands Headcount → member, asserts the "Add Data" button carries Quasar'sbg-positiveclass.frontend/tests/integration/data-management.spec.tstest1216b — FACTORS card renders before DATA card in the upload row (regression): asserts the FACTORS button's bounding-boxxis less than the DATA button'sxin the per-submodule render path (SubmoduleItem.vue). Same FACTORS-first rule applies to the common path (ModuleUploadsSection.vue) — both share the rule via template ordering, so a single regression test pins both.
7. Verification¶
make type-check— passes (vue-tsc clean).make lint— passes (prettier + eslint clean).playwright test tests/integration/data-management.spec.ts— 19/19 pass, including the two new tests.