Skip to content

feat(compliance): Rapport view with per-template answer columns (#1167) - #1180

Merged
renemadsen merged 1 commit into
stablefrom
feat/1167-compliance-rapport-view
Sep 4, 2026
Merged

feat(compliance): Rapport view with per-template answer columns (#1167)#1180
renemadsen merged 1 commit into
stablefrom
feat/1167-compliance-rapport-view

Conversation

@renemadsen

Copy link
Copy Markdown
Member

Closes #1167. Part of the compliance-report epic #1160.

The third view: the filtered set grouped tag group → template group, each sub-report rendered whole with that template's own answer columns beside six pinned metadata columns.

Highlights

Cells are addressed by key, never by position. The server sends an ordered schema plus a Dictionary<string,string> keyed on f{fieldId}; the view reads it through one accessor guarded with Object.prototype.hasOwnProperty.call, and a missing key renders the shared en-dash placeholder in place. There is no index, no zip and no Object.values anywhere in the view — the column-desync bug class from #1160 is not expressible here.

Routes to the case page instead of reusing ComplianceCaseModalComponent. That modal assigns replyRequest.siteId = workerId, and BackendConfigurationCompliancesService.Update writes foundCase.SiteId = model.SiteId unconditionally (every guarded variant is commented out at :249-260). The report DTO carries worker names and no site id, so the modal would write SiteId = 0 onto the SDK case. The case page's own save path never sets siteId.

Rows are bounded twice — 100 per sub-report, and a 500-row page budget spent in server order. Sections are (tag × template) pairs, so dozens of small ones would never trip a per-section cap and the whole 5000-row server allowance would land in one DOM. A collapsed section keeps its heading, true row count and Vis alle button.

i18n backlog cleared. This wave's six keys plus #1164's four now exist in all 26 locales. Previously 24 files were ~116 keys behind, so those languages rendered the literal string {{count}} images — braces included, because ngx-translate returns an unresolved key uninterpolated.

Deliberately not shipped

Known issues this PR does not fix

  • DateInterceptor's regex is unanchored (common/interceptors/date.interceptor.ts:31), so a free-text answer that merely contains an ISO timestamp is replaced wholesale by parseJSON(...)Invalid Date. The new hardening renders that as the en dash, i.e. indistinguishable from "unanswered", where before it read Invalid Date and was visibly wrong. Narrow, pre-existing and app-wide; the durable fix is interceptor-side.
  • 28 pre-existing broken interpolations in older locale files (e.g. esES 'Anualmente el {{día}} {{mes}}') where an earlier run of translateTsFiles.py translated the placeholder names. Untouched here — fixing them means editing existing lines. Worth its own issue.
  • 12 locale files are dead: translates.ts imports only 14, so bgBG, csCZ, elGR, etET, hrHR, huHU, isIS, ltLT, lvLV, roRO, skSK, slSL never reach the UI regardless of content.

Tests

23 jest cases on the section helpers and 11 new TestBed cases over buildGridColumns and the row ceilings — the answer_ prefixing, the duplicate-key dedupe (CDK throws on a duplicate displayedColumns) and the per-section column-array identity (mtx-grid's _countPinnedPosition mutates the column objects, so two grids must never share an array). 3 Playwright tests.

Not covered: the new template layer — the budget footer, the grid guard, the images cell's two states. The TestBed spec asserts column models only, and Playwright cannot reach a populated sub-report on an unseeded shard.

Review

Two independent review gates. Gate 1 caught a Playwright test asserting a pre-fetch state the page never reaches (the shell auto-fetches on load and setMode preserves reportVisible) — a deterministic red shard against correct product behaviour. Gate 2 caught the i18n gap. Three reviewer findings were checked and found wrong, and were rejected rather than applied.

🤖 Generated with Claude Code

https://claude.ai/code/session_01LcMeVFxqWqQzvqHjAa3Xkc

Adds the third compliance view: the filtered set grouped tag group -> template
group, each sub-report rendered whole with the template's own answer columns
beside six pinned metadata columns.

Cells are addressed BY KEY, never by position. The server sends an ordered
column schema plus a Dictionary<string,string> per case keyed on f{fieldId},
and the view reads it through a single accessor guarded with
Object.prototype.hasOwnProperty.call. A missing key renders the shared en-dash
placeholder IN PLACE, so the column-desync bug class the epic exists to kill is
not expressible here -- there is no index, no zip and no Object.values anywhere
in the view.

Deliberately routes to the case page rather than reusing
ComplianceCaseModalComponent: that modal assigns replyRequest.siteId =
workerId, and BackendConfigurationCompliancesService.Update writes
foundCase.SiteId = model.SiteId unconditionally (every guarded variant is
commented out). The report DTO carries worker NAMES and no site id, so the
modal would write SiteId = 0 onto the SDK case. The case page's own save path
never sets siteId. Cost: leaving the page drops the fetched result, so the user
presses Opdater tabel again on return.

Rows are bounded twice. Each sub-report reveals at most 100 rows, and the page
spends a 500-row budget across sub-reports in server order -- sections are
(tag x template) pairs, so dozens of small ones would otherwise never trip a
per-section cap and the whole 5000-row server allowance would land in one DOM.
A collapsed section keeps its heading, its true row count and its Vis alle
button, so nothing becomes unreachable.

Answer cells are hardened against the global DateInterceptor, which recurses
into every object in every response body and rewrites ISO-timestamp strings
into Date objects in place -- including the untyped cell bag. A Date-valued
cell is now formatted in UTC to agree with the Udfoert dato column, while
locally-constructed period bounds keep local-time formatting.

i18n: this wave's six keys plus #1164's four are propagated to all 26 locales,
which also clears a backlog that had left 24 files ~116 keys behind. Without
it, 24 languages rendered the literal string {{count}} images, braces and all,
because ngx-translate returns an unresolved key uninterpolated.

Not shipped here: the Billeder cell is a static count. Opening the gallery
needs an authenticated blob fetch and belongs to #1168, so that acceptance
criterion of #1167 is NOT met by this commit.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LcMeVFxqWqQzvqHjAa3Xkc
Copilot AI lite review requested due to automatic review settings September 4, 2026 10:31

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

It introduces at least one real UI/UX bug risk (meta-line labels not updating with filter changes) and adds an unconditional Playwright sleep that should be replaced with a deterministic wait.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

Implements the Compliance standalone page’s Rapport view: rendering the filtered result grouped tag group → template group, where each sub-report shows pinned metadata columns plus per-template keyed answer columns, with row caps to bound initial DOM size.

Changes:

  • Added a new client service method for the Rapport projection endpoint (/compliance-report/eform-columns) and corresponding typed DTO models for tag/template grouping and keyed cells.
  • Introduced pure helper functions (and Jest coverage) for section flattening, stable labeling, keyed cell lookup, and date formatting conventions used by the Rapport view.
  • Added the Rapport view component (mtx-grid pinned columns, per-section + page-wide row ceilings), Playwright coverage for empty-install invariants, and backfilled i18n keys across locales.
File summaries
File Description
eform-client/src/app/plugins/modules/backend-configuration-pn/services/backend-configuration-pn-compliance-report.service.ts Adds eformColumns() API call for the Rapport projection endpoint.
eform-client/src/app/plugins/modules/backend-configuration-pn/modules/compliance-report/helpers/index.ts Re-exports the new Rapport section helper module.
eform-client/src/app/plugins/modules/backend-configuration-pn/modules/compliance-report/helpers/compliance-report-sections.ts Adds pure helper logic for section building, keyed answer lookup, worker name formatting, and dd.MM.yyyy formatting.
eform-client/src/app/plugins/modules/backend-configuration-pn/modules/compliance-report/helpers/compliance-report-sections.spec.ts Jest coverage for the helper rules (keyed lookup, labels, section flattening, date formatting).
eform-client/src/app/plugins/modules/backend-configuration-pn/modules/compliance-report/components/index.ts Exports the new Rapport view component.
eform-client/src/app/plugins/modules/backend-configuration-pn/modules/compliance-report/components/compliance-report-view/compliance-report-view.component.ts Implements the Rapport view rendering, grid column building, row ceilings, meta line, and actions.
eform-client/src/app/plugins/modules/backend-configuration-pn/modules/compliance-report/components/compliance-report-view/compliance-report-view.component.spec.ts TestBed coverage for grid-column rules and row-ceiling behavior.
eform-client/src/app/plugins/modules/backend-configuration-pn/modules/compliance-report/components/compliance-report-view/compliance-report-view.component.scss Adds theme-token-only styling and pinned-column background fixes for workspace theme.
eform-client/src/app/plugins/modules/backend-configuration-pn/modules/compliance-report/components/compliance-report-view/compliance-report-view.component.html Adds the Rapport view template: meta line, sections, grids, and action templates.
eform-client/src/app/plugins/modules/backend-configuration-pn/modules/compliance-report/components/compliance-report-page/compliance-report-page.component.html Wires the new Rapport view into the page’s mode switch.
eform-client/src/app/plugins/modules/backend-configuration-pn/modules/compliance-report/compliance-report.module.ts Imports MtxGridModule and declares the Rapport view component.
eform-client/src/app/plugins/modules/backend-configuration-pn/models/compliance-report/compliance-report.model.ts Adds Rapport DTOs: column schema, keyed cells, images, and tag/template grouping models.
eform-client/src/app/plugins/modules/backend-configuration-pn/i18n/enUS.ts Adds new Rapport-related keys (e.g., Completion date, Without tag, Columns unavailable, image count, edit tooltip).
eform-client/src/app/plugins/modules/backend-configuration-pn/i18n/da.ts Adds Danish translations for the new Rapport-related keys.
eform-client/src/app/plugins/modules/backend-configuration-pn/i18n/bgBG.ts Backfills missing keys for Bulgarian locale.
eform-client/src/app/plugins/modules/backend-configuration-pn/i18n/csCZ.ts Backfills missing keys for Czech locale.
eform-client/src/app/plugins/modules/backend-configuration-pn/i18n/deDE.ts Backfills missing keys for German locale.
eform-client/src/app/plugins/modules/backend-configuration-pn/i18n/elGR.ts Backfills missing keys for Greek locale.
eform-client/src/app/plugins/modules/backend-configuration-pn/i18n/esES.ts Backfills missing keys for Spanish locale.
eform-client/src/app/plugins/modules/backend-configuration-pn/i18n/etET.ts Backfills missing keys for Estonian locale.
eform-client/src/app/plugins/modules/backend-configuration-pn/i18n/fiFI.ts Backfills missing keys for Finnish locale.
eform-client/src/app/plugins/modules/backend-configuration-pn/i18n/frFR.ts Backfills missing keys for French locale.
eform-client/src/app/plugins/modules/backend-configuration-pn/i18n/hrHR.ts Backfills missing keys for Croatian locale.
eform-client/src/app/plugins/modules/backend-configuration-pn/i18n/huHU.ts Backfills missing keys for Hungarian locale.
eform-client/src/app/plugins/modules/backend-configuration-pn/i18n/isIS.ts Backfills missing keys for Icelandic locale.
eform-client/src/app/plugins/modules/backend-configuration-pn/i18n/itIT.ts Backfills missing keys for Italian locale.
eform-client/src/app/plugins/modules/backend-configuration-pn/i18n/ltLT.ts Backfills missing keys for Lithuanian locale.
eform-client/src/app/plugins/modules/backend-configuration-pn/i18n/lvLV.ts Backfills missing keys for Latvian locale.
eform-client/src/app/plugins/modules/backend-configuration-pn/i18n/nlNL.ts Backfills missing keys for Dutch locale.
eform-client/src/app/plugins/modules/backend-configuration-pn/i18n/noNO.ts Backfills missing keys for Norwegian locale.
eform-client/src/app/plugins/modules/backend-configuration-pn/i18n/plPL.ts Backfills missing keys for Polish locale.
eform-client/src/app/plugins/modules/backend-configuration-pn/i18n/ptBR.ts Backfills missing keys for Portuguese (Brazil) locale.
eform-client/src/app/plugins/modules/backend-configuration-pn/i18n/ptPT.ts Backfills missing keys for Portuguese (Portugal) locale.
eform-client/src/app/plugins/modules/backend-configuration-pn/i18n/roRO.ts Backfills missing keys for Romanian locale.
eform-client/src/app/plugins/modules/backend-configuration-pn/i18n/skSK.ts Backfills missing keys for Slovak locale.
eform-client/src/app/plugins/modules/backend-configuration-pn/i18n/slSL.ts Backfills missing keys for Slovenian locale.
eform-client/src/app/plugins/modules/backend-configuration-pn/i18n/svSE.ts Backfills missing keys for Swedish locale.
eform-client/src/app/plugins/modules/backend-configuration-pn/i18n/ukUA.ts Backfills missing keys for Ukrainian locale.
eform-client/playwright/e2e/plugins/backend-configuration-pn/s/compliance-report-view.spec.ts Adds Playwright assertions for Rapport view invariants on an empty installation.
Review details
  • Files reviewed: 39/39 changed files
  • Comments generated: 2
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +39 to +47
async function goToRapport(page: Page): Promise<void> {
await page.goto(BASE_URL);
await new LoginPage(page).login();
await page.waitForTimeout(2000);
await page.goto(PAGE_URL);
await page.locator('#complianceFilterProperty').waitFor({ state: 'visible', timeout: 60000 });
await page.locator('#complianceMode-report').click();
await expect(page.locator('#complianceMode-report')).toHaveAttribute('aria-pressed', 'true');
}
Comment on lines +164 to +171
tap(() => {
// A re-render detaches the row the confirm dialog was opened from.
this.closeDeleteDialog();
// Cleared on every attempt: while the spinner is up the previous
// failure is no longer the current state of the view.
this.loadFailed = false;
this.state.setLoading(true);
}),
@renemadsen
renemadsen merged commit 46700c2 into stable Sep 4, 2026
60 of 62 checks passed
@renemadsen
renemadsen deleted the feat/1167-compliance-rapport-view branch September 4, 2026 11:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants