feat(compliance): Rapport view with per-template answer columns (#1167) - #1180
Merged
Conversation
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
16 tasks
There was a problem hiding this comment.
🟡 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); | ||
| }), |
14 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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 onf{fieldId}; the view reads it through one accessor guarded withObject.prototype.hasOwnProperty.call, and a missing key renders the shared en-dash placeholder in place. There is no index, no zip and noObject.valuesanywhere 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 assignsreplyRequest.siteId = workerId, andBackendConfigurationCompliancesService.UpdatewritesfoundCase.SiteId = model.SiteIdunconditionally (every guarded variant is commented out at:249-260). The report DTO carries worker names and no site id, so the modal would writeSiteId = 0onto the SDK case. The case page's own save path never setssiteId.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 allebutton.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
RedigerusesmatTooltiprather than thetitleattribute the criterion quotes — house pattern, literal deviation.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 byparseJSON(...)→Invalid Date. The new hardening renders that as the en dash, i.e. indistinguishable from "unanswered", where before it readInvalid Dateand was visibly wrong. Narrow, pre-existing and app-wide; the durable fix is interceptor-side.esES'Anualmente el {{día}} {{mes}}') where an earlier run oftranslateTsFiles.pytranslated the placeholder names. Untouched here — fixing them means editing existing lines. Worth its own issue.translates.tsimports only 14, sobgBG, csCZ, elGR, etET, hrHR, huHU, isIS, ltLT, lvLV, roRO, skSK, slSLnever reach the UI regardless of content.Tests
23 jest cases on the section helpers and 11 new TestBed cases over
buildGridColumnsand the row ceilings — theanswer_prefixing, the duplicate-key dedupe (CDK throws on a duplicatedisplayedColumns) and the per-section column-array identity (mtx-grid's_countPinnedPositionmutates 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
setModepreservesreportVisible) — 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