feat(beheer): admin audit view at /beheer/audit (finishes WP-42)
The WP-41 GET /admin/audit trail now has an FE view: a beheer audit page (domain AuditEntry + adapter/parse + store) rendering the data-minimised trail as a read-only table, capability-gated on cases:manage. Added to ADMIN_LINKS (header nav + dashboard Beheer section) and to the role.interceptor ROLE_AWARE list so the admin-gated call carries X-Role. Closes WP-42's audit half. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -44,7 +44,7 @@ Gates land before the work they cover; each lint rule lands in the same WP as th
|
|||||||
for its existing violations, so every WP ends green.
|
for its existing violations, so every WP ends green.
|
||||||
|
|
||||||
| WP | Title | Phase | Status |
|
| WP | Title | Phase | Status |
|
||||||
| ---------------------------------------------- | --------------------------------------------------------------------------------- | --------------------------- | ------- |
|
| ---------------------------------------------- | --------------------------------------------------------------------------------- | --------------------------- | ------ |
|
||||||
| [WP-01](WP-01-axe-ci-gate.md) | Axe-on-every-story CI gate | 0 · gates | done |
|
| [WP-01](WP-01-axe-ci-gate.md) | Axe-on-every-story CI gate | 0 · gates | done |
|
||||||
| [WP-02](WP-02-check-tokens.md) | Harden `check:tokens` + fix what it catches | 0 · gates | done |
|
| [WP-02](WP-02-check-tokens.md) | Harden `check:tokens` + fix what it catches | 0 · gates | done |
|
||||||
| [WP-03](WP-03-contracts-purity.md) | Boundaries I: contracts purity + ApiClient confinement | 0 · gates | done |
|
| [WP-03](WP-03-contracts-purity.md) | Boundaries I: contracts purity + ApiClient confinement | 0 · gates | done |
|
||||||
@@ -86,7 +86,7 @@ for its existing violations, so every WP ends green.
|
|||||||
| [WP-39](WP-39-showcase-snippets-animations.md) | Showcase: linked code snippets + teaching animations | 8 · platform/DX/showcase | done |
|
| [WP-39](WP-39-showcase-snippets-animations.md) | Showcase: linked code snippets + teaching animations | 8 · platform/DX/showcase | done |
|
||||||
| [WP-40](WP-40-pii-kernel.md) | PII kernel: branded `Bsn` VO (elfproef) + masked-value atom | 8 · platform/DX/showcase | done |
|
| [WP-40](WP-40-pii-kernel.md) | PII kernel: branded `Bsn` VO (elfproef) + masked-value atom | 8 · platform/DX/showcase | done |
|
||||||
| [WP-41](WP-41-persisted-authz-audit.md) | Persisted, queryable authz/PII-reveal audit (no PII) | 8 · platform/DX/showcase | done |
|
| [WP-41](WP-41-persisted-authz-audit.md) | Persisted, queryable authz/PII-reveal audit (no PII) | 8 · platform/DX/showcase | done |
|
||||||
| [WP-42](WP-42-privacy-security-showcase.md) | Privacy & security showcase page (mask + no-PII log) | 8 · platform/DX/showcase | partial |
|
| [WP-42](WP-42-privacy-security-showcase.md) | Privacy & security showcase page (mask + no-PII log) | 8 · platform/DX/showcase | done |
|
||||||
| [WP-43](WP-43-scaffold-generators.md) | Runnable generators: value-object / form-machine / bff-endpoint / ui-component | 8 · platform/DX/showcase | todo |
|
| [WP-43](WP-43-scaffold-generators.md) | Runnable generators: value-object / form-machine / bff-endpoint / ui-component | 8 · platform/DX/showcase | todo |
|
||||||
| [WP-44](WP-44-context-generator.md) | Runnable generator: `gen:context` | 8 · platform/DX/showcase | todo |
|
| [WP-44](WP-44-context-generator.md) | Runnable generator: `gen:context` | 8 · platform/DX/showcase | todo |
|
||||||
| [WP-45](WP-45-create-ssp-generator.md) | `create-ssp` bootstrap generator (mechanise new-ssp) | 8 · platform/DX/showcase | todo |
|
| [WP-45](WP-45-create-ssp-generator.md) | `create-ssp` bootstrap generator (mechanise new-ssp) | 8 · platform/DX/showcase | todo |
|
||||||
|
|||||||
@@ -1,6 +1,15 @@
|
|||||||
# WP-42 — Privacy & security showcase page
|
# WP-42 — Privacy & security showcase page
|
||||||
|
|
||||||
Status: partial — mask/parse showcase done; audit half pending WP-41
|
Status: done (optional Foundations MDX writeup left as a nice-to-have)
|
||||||
|
|
||||||
|
## Audit view (added after WP-41)
|
||||||
|
|
||||||
|
`/beheer/audit` — an admin page (`beheer/ui/audit.page.ts`) reading the WP-41 `GET /admin/audit`
|
||||||
|
trail through a `beheer` adapter/store (domain `AuditEntry` + trust-boundary parse), rendered as a
|
||||||
|
read-only table (time/action/resource/decision/role/correlation-id), capability-gated on
|
||||||
|
`cases:manage`. Added to `ADMIN_LINKS` (so it shows in the header nav + dashboard "Beheer" section)
|
||||||
|
and to the `role.interceptor` ROLE_AWARE list (else it silently 403s). This closes the audit half.
|
||||||
|
|
||||||
Phase: 8 — platform/DX/showcase
|
Phase: 8 — platform/DX/showcase
|
||||||
Priority: P2
|
Priority: P2
|
||||||
Depends on: WP-40, WP-41
|
Depends on: WP-40, WP-41
|
||||||
|
|||||||
@@ -84,6 +84,13 @@ export const routes: Routes = [
|
|||||||
loadComponent: () =>
|
loadComponent: () =>
|
||||||
import('@registratie/ui/admin-cases.page').then((m) => m.AdminCasesPage),
|
import('@registratie/ui/admin-cases.page').then((m) => m.AdminCasesPage),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: 'beheer/audit',
|
||||||
|
// Admin-only authz/PII-reveal audit trail (WP-41/42). capabilityGuard denies-by-default
|
||||||
|
// unless GET /me resolved `cases:manage` (reused for audit read). Backend re-enforces.
|
||||||
|
canActivate: [capabilityGuard('cases:manage')],
|
||||||
|
loadComponent: () => import('@beheer/ui/audit.page').then((m) => m.AuditPage),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: 'concepts',
|
path: 'concepts',
|
||||||
loadComponent: () => import('./showcase/concepts.page').then((m) => m.ConceptsPage),
|
loadComponent: () => import('./showcase/concepts.page').then((m) => m.ConceptsPage),
|
||||||
|
|||||||
@@ -0,0 +1,32 @@
|
|||||||
|
import { Injectable, inject, signal } from '@angular/core';
|
||||||
|
import { RemoteData } from '@shared/application/remote-data';
|
||||||
|
import { AuditEntry } from '@beheer/domain/audit-entry';
|
||||||
|
import { AuditAdapter, parseAuditEntries } from '@beheer/infrastructure/audit.adapter';
|
||||||
|
|
||||||
|
type Err = Error | undefined;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Admin view of the persisted authz/PII-reveal audit trail (WP-41/42). One root singleton
|
||||||
|
* owning the list as a RemoteData signal, parsed at the trust boundary. Read-only.
|
||||||
|
*/
|
||||||
|
@Injectable({ providedIn: 'root' })
|
||||||
|
export class AuditStore {
|
||||||
|
private adapter = inject(AuditAdapter);
|
||||||
|
|
||||||
|
private state = signal<RemoteData<Err, AuditEntry[]>>({ tag: 'Loading' });
|
||||||
|
readonly entries = this.state.asReadonly();
|
||||||
|
|
||||||
|
async load() {
|
||||||
|
if (this.state().tag !== 'Success') this.state.set({ tag: 'Loading' });
|
||||||
|
try {
|
||||||
|
const parsed = parseAuditEntries(await this.adapter.list());
|
||||||
|
this.state.set(
|
||||||
|
parsed.ok
|
||||||
|
? { tag: 'Success', value: parsed.value }
|
||||||
|
: { tag: 'Failure', error: new Error(parsed.error) },
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
this.state.set({ tag: 'Failure', error: e as Error });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
/** One authz/PII-reveal audit row as the FE sees it (WP-41 backend → WP-42 view). Pure
|
||||||
|
type; data-minimised (no PII) by construction on the server. */
|
||||||
|
export interface AuditEntry {
|
||||||
|
at: string; // ISO timestamp
|
||||||
|
action: string;
|
||||||
|
resource: string;
|
||||||
|
decision: 'allow' | 'deny';
|
||||||
|
role: string;
|
||||||
|
correlationId: string;
|
||||||
|
}
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
import { Injectable, inject } from '@angular/core';
|
||||||
|
import { Result, ok, err } from '@shared/kernel/fp';
|
||||||
|
import { ApiClient } from '@shared/infrastructure/api-client';
|
||||||
|
import type { AuthzAuditDto } from '@shared/infrastructure/api-client';
|
||||||
|
import { AuditEntry } from '@beheer/domain/audit-entry';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Infrastructure adapter for the admin authz/PII-reveal audit trail (`GET /admin/audit`,
|
||||||
|
* WP-41). The single place the ApiClient lives for audit; the store parses at the boundary.
|
||||||
|
*/
|
||||||
|
@Injectable({ providedIn: 'root' })
|
||||||
|
export class AuditAdapter {
|
||||||
|
private client = inject(ApiClient);
|
||||||
|
|
||||||
|
list(): Promise<AuthzAuditDto[]> {
|
||||||
|
return this.client.audit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Trust-boundary parse of the audit rows. */
|
||||||
|
export function parseAuditEntries(json: unknown): Result<string, AuditEntry[]> {
|
||||||
|
if (!Array.isArray(json)) return err('audit: not an array');
|
||||||
|
const out: AuditEntry[] = [];
|
||||||
|
for (const item of json) {
|
||||||
|
if (typeof item !== 'object' || item === null) return err('audit: row not an object');
|
||||||
|
const d = item as AuthzAuditDto;
|
||||||
|
if (
|
||||||
|
typeof d.at !== 'string' ||
|
||||||
|
typeof d.action !== 'string' ||
|
||||||
|
typeof d.resource !== 'string' ||
|
||||||
|
typeof d.role !== 'string' ||
|
||||||
|
typeof d.correlationId !== 'string'
|
||||||
|
)
|
||||||
|
return err('audit: missing fields');
|
||||||
|
out.push({
|
||||||
|
at: d.at,
|
||||||
|
action: d.action,
|
||||||
|
resource: d.resource,
|
||||||
|
decision: d.decision === 'allow' ? 'allow' : 'deny',
|
||||||
|
role: d.role,
|
||||||
|
correlationId: d.correlationId,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return ok(out);
|
||||||
|
}
|
||||||
@@ -0,0 +1,128 @@
|
|||||||
|
import { Component, computed, effect, inject } from '@angular/core';
|
||||||
|
import { DatePipe } from '@angular/common';
|
||||||
|
import { PageShellComponent } from '@shared/layout/page-shell/page-shell.component';
|
||||||
|
import { AlertComponent } from '@shared/ui/alert/alert.component';
|
||||||
|
import { ButtonComponent } from '@shared/ui/button/button.component';
|
||||||
|
import { ASYNC } from '@shared/ui/async/async.component';
|
||||||
|
import { AccessStore } from '@shared/application/access.store';
|
||||||
|
import { AuditStore } from '@beheer/application/audit.store';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Admin page: the persisted authz/PII-reveal audit trail (WP-41/42) — data-minimised, no PII.
|
||||||
|
* Deny-by-default capability gate (`cases:manage`, reused for admin audit read). Read-only table.
|
||||||
|
*/
|
||||||
|
@Component({
|
||||||
|
selector: 'app-audit-page',
|
||||||
|
imports: [PageShellComponent, AlertComponent, ButtonComponent, DatePipe, ...ASYNC],
|
||||||
|
styles: [
|
||||||
|
`
|
||||||
|
.scroll {
|
||||||
|
overflow-x: auto;
|
||||||
|
}
|
||||||
|
table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
font-size: var(--rhc-text-font-size-sm);
|
||||||
|
}
|
||||||
|
th,
|
||||||
|
td {
|
||||||
|
text-align: left;
|
||||||
|
padding: var(--rhc-space-max-sm) var(--rhc-space-max-md);
|
||||||
|
border-block-end: var(--rhc-border-width-sm) solid var(--rhc-color-cool-grey-200);
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
th {
|
||||||
|
font-weight: var(--rhc-text-font-weight-semi-bold);
|
||||||
|
}
|
||||||
|
.deny {
|
||||||
|
color: var(--rhc-color-rood-600, #a30000);
|
||||||
|
font-weight: var(--rhc-text-font-weight-semi-bold);
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
],
|
||||||
|
template: `
|
||||||
|
<app-page-shell [heading]="heading" [intro]="intro" backLink="/dashboard">
|
||||||
|
@if (!access.ready()) {
|
||||||
|
<!-- wait for /me before deciding — avoids flashing the denial to an admin -->
|
||||||
|
} @else if (!canRead()) {
|
||||||
|
<app-alert type="error">{{ deniedText }}</app-alert>
|
||||||
|
} @else {
|
||||||
|
<app-async [data]="store.entries()">
|
||||||
|
<ng-template appAsyncError>
|
||||||
|
<app-alert type="error">{{ failedText }}</app-alert>
|
||||||
|
<app-button variant="secondary" (click)="reload()">{{ retryText }}</app-button>
|
||||||
|
</ng-template>
|
||||||
|
<ng-template appAsyncLoaded>
|
||||||
|
@if (entries().length === 0) {
|
||||||
|
<app-alert type="info">{{ emptyText }}</app-alert>
|
||||||
|
} @else {
|
||||||
|
<div class="scroll">
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>{{ colTijd }}</th>
|
||||||
|
<th>{{ colActie }}</th>
|
||||||
|
<th>{{ colResource }}</th>
|
||||||
|
<th>{{ colBesluit }}</th>
|
||||||
|
<th>{{ colRol }}</th>
|
||||||
|
<th>{{ colCid }}</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
@for (e of entries(); track e.at + e.action + e.correlationId) {
|
||||||
|
<tr>
|
||||||
|
<td>{{ e.at | date: 'short' }}</td>
|
||||||
|
<td>{{ e.action }}</td>
|
||||||
|
<td>{{ e.resource }}</td>
|
||||||
|
<td [class.deny]="e.decision === 'deny'">{{ e.decision }}</td>
|
||||||
|
<td>{{ e.role }}</td>
|
||||||
|
<td>{{ e.correlationId }}</td>
|
||||||
|
</tr>
|
||||||
|
}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</ng-template>
|
||||||
|
</app-async>
|
||||||
|
}
|
||||||
|
</app-page-shell>
|
||||||
|
`,
|
||||||
|
})
|
||||||
|
export class AuditPage {
|
||||||
|
protected store = inject(AuditStore);
|
||||||
|
protected access = inject(AccessStore);
|
||||||
|
|
||||||
|
protected canRead = computed(() => this.access.can('cases:manage'));
|
||||||
|
protected entries = computed(() => {
|
||||||
|
const rd = this.store.entries();
|
||||||
|
return rd.tag === 'Success' ? rd.value : [];
|
||||||
|
});
|
||||||
|
|
||||||
|
protected heading = $localize`:@@audit.heading:Auditlog`;
|
||||||
|
protected intro = $localize`:@@audit.intro:Toegangs- en inzagebeslissingen (autorisatie en het tonen van afgeschermde gegevens). Vastgelegd zonder persoonsgegevens.`;
|
||||||
|
protected deniedText = $localize`:@@audit.denied:U hebt geen rechten om de auditlog te bekijken.`;
|
||||||
|
protected failedText = $localize`:@@audit.failed:De auditlog kon niet worden geladen.`;
|
||||||
|
protected emptyText = $localize`:@@audit.empty:Nog geen auditregels.`;
|
||||||
|
protected retryText = $localize`:@@audit.retry:Opnieuw proberen`;
|
||||||
|
protected colTijd = $localize`:@@audit.col.tijd:Tijd`;
|
||||||
|
protected colActie = $localize`:@@audit.col.actie:Actie`;
|
||||||
|
protected colResource = $localize`:@@audit.col.resource:Resource`;
|
||||||
|
protected colBesluit = $localize`:@@audit.col.besluit:Besluit`;
|
||||||
|
protected colRol = $localize`:@@audit.col.rol:Rol`;
|
||||||
|
protected colCid = $localize`:@@audit.col.cid:Correlatie-id`;
|
||||||
|
|
||||||
|
private loadRequested = false;
|
||||||
|
constructor() {
|
||||||
|
effect(() => {
|
||||||
|
if (this.canRead() && !this.loadRequested) {
|
||||||
|
this.loadRequested = true;
|
||||||
|
void this.store.load();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
protected reload() {
|
||||||
|
void this.store.load();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -13,6 +13,7 @@ const ROLE_AWARE = [
|
|||||||
'/api/v1/brief',
|
'/api/v1/brief',
|
||||||
'/api/v1/admin/org-template',
|
'/api/v1/admin/org-template',
|
||||||
'/api/v1/admin/cases',
|
'/api/v1/admin/cases',
|
||||||
|
'/api/v1/admin/audit',
|
||||||
'/api/v1/stamdata',
|
'/api/v1/stamdata',
|
||||||
'/api/v1/me',
|
'/api/v1/me',
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -31,4 +31,10 @@ export const ADMIN_LINKS: readonly AdminLink[] = [
|
|||||||
to: '/beheer/zaken',
|
to: '/beheer/zaken',
|
||||||
cap: 'cases:manage',
|
cap: 'cases:manage',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
label: $localize`:@@header.nav.audit:Auditlog`,
|
||||||
|
description: $localize`:@@admin.link.audit.desc:Toegangs- en inzagebeslissingen bekijken`,
|
||||||
|
to: '/beheer/audit',
|
||||||
|
cap: 'cases:manage',
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -3658,6 +3658,62 @@
|
|||||||
<source>Aanvragen</source>
|
<source>Aanvragen</source>
|
||||||
<target datatype="html">Cases</target>
|
<target datatype="html">Cases</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="header.nav.audit" datatype="html">
|
||||||
|
<source>Auditlog</source>
|
||||||
|
<target datatype="html">Audit log</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="admin.link.audit.desc" datatype="html">
|
||||||
|
<source>Toegangs- en inzagebeslissingen bekijken</source>
|
||||||
|
<target datatype="html">View access and disclosure decisions</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="audit.heading" datatype="html">
|
||||||
|
<source>Auditlog</source>
|
||||||
|
<target datatype="html">Audit log</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="audit.intro" datatype="html">
|
||||||
|
<source>Toegangs- en inzagebeslissingen (autorisatie en het tonen van afgeschermde gegevens). Vastgelegd zonder persoonsgegevens.</source>
|
||||||
|
<target datatype="html">Access and disclosure decisions (authorization and revealing masked data). Recorded without personal data.</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="audit.denied" datatype="html">
|
||||||
|
<source>U hebt geen rechten om de auditlog te bekijken.</source>
|
||||||
|
<target datatype="html">You do not have permission to view the audit log.</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="audit.failed" datatype="html">
|
||||||
|
<source>De auditlog kon niet worden geladen.</source>
|
||||||
|
<target datatype="html">The audit log could not be loaded.</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="audit.empty" datatype="html">
|
||||||
|
<source>Nog geen auditregels.</source>
|
||||||
|
<target datatype="html">No audit entries yet.</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="audit.retry" datatype="html">
|
||||||
|
<source>Opnieuw proberen</source>
|
||||||
|
<target datatype="html">Try again</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="audit.col.tijd" datatype="html">
|
||||||
|
<source>Tijd</source>
|
||||||
|
<target datatype="html">Time</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="audit.col.actie" datatype="html">
|
||||||
|
<source>Actie</source>
|
||||||
|
<target datatype="html">Action</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="audit.col.resource" datatype="html">
|
||||||
|
<source>Resource</source>
|
||||||
|
<target datatype="html">Resource</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="audit.col.besluit" datatype="html">
|
||||||
|
<source>Besluit</source>
|
||||||
|
<target datatype="html">Decision</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="audit.col.rol" datatype="html">
|
||||||
|
<source>Rol</source>
|
||||||
|
<target datatype="html">Role</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="audit.col.cid" datatype="html">
|
||||||
|
<source>Correlatie-id</source>
|
||||||
|
<target datatype="html">Correlation id</target>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="dashboard.beheer" datatype="html">
|
<trans-unit id="dashboard.beheer" datatype="html">
|
||||||
<source>Beheer</source>
|
<source>Beheer</source>
|
||||||
<target datatype="html">Administration</target>
|
<target datatype="html">Administration</target>
|
||||||
|
|||||||
+100
-2
@@ -94,6 +94,90 @@
|
|||||||
<context context-type="linenumber">13</context>
|
<context context-type="linenumber">13</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="audit.heading" datatype="html">
|
||||||
|
<source>Auditlog</source>
|
||||||
|
<context-group purpose="location">
|
||||||
|
<context context-type="sourcefile">src/app/beheer/ui/audit.page.ts</context>
|
||||||
|
<context context-type="linenumber">102</context>
|
||||||
|
</context-group>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="audit.intro" datatype="html">
|
||||||
|
<source>Toegangs- en inzagebeslissingen (autorisatie en het tonen van afgeschermde gegevens). Vastgelegd zonder persoonsgegevens.</source>
|
||||||
|
<context-group purpose="location">
|
||||||
|
<context context-type="sourcefile">src/app/beheer/ui/audit.page.ts</context>
|
||||||
|
<context context-type="linenumber">103</context>
|
||||||
|
</context-group>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="audit.denied" datatype="html">
|
||||||
|
<source>U hebt geen rechten om de auditlog te bekijken.</source>
|
||||||
|
<context-group purpose="location">
|
||||||
|
<context context-type="sourcefile">src/app/beheer/ui/audit.page.ts</context>
|
||||||
|
<context context-type="linenumber">104</context>
|
||||||
|
</context-group>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="audit.failed" datatype="html">
|
||||||
|
<source>De auditlog kon niet worden geladen.</source>
|
||||||
|
<context-group purpose="location">
|
||||||
|
<context context-type="sourcefile">src/app/beheer/ui/audit.page.ts</context>
|
||||||
|
<context context-type="linenumber">105</context>
|
||||||
|
</context-group>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="audit.empty" datatype="html">
|
||||||
|
<source>Nog geen auditregels.</source>
|
||||||
|
<context-group purpose="location">
|
||||||
|
<context context-type="sourcefile">src/app/beheer/ui/audit.page.ts</context>
|
||||||
|
<context context-type="linenumber">106</context>
|
||||||
|
</context-group>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="audit.retry" datatype="html">
|
||||||
|
<source>Opnieuw proberen</source>
|
||||||
|
<context-group purpose="location">
|
||||||
|
<context context-type="sourcefile">src/app/beheer/ui/audit.page.ts</context>
|
||||||
|
<context context-type="linenumber">107</context>
|
||||||
|
</context-group>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="audit.col.tijd" datatype="html">
|
||||||
|
<source>Tijd</source>
|
||||||
|
<context-group purpose="location">
|
||||||
|
<context context-type="sourcefile">src/app/beheer/ui/audit.page.ts</context>
|
||||||
|
<context context-type="linenumber">108</context>
|
||||||
|
</context-group>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="audit.col.actie" datatype="html">
|
||||||
|
<source>Actie</source>
|
||||||
|
<context-group purpose="location">
|
||||||
|
<context context-type="sourcefile">src/app/beheer/ui/audit.page.ts</context>
|
||||||
|
<context context-type="linenumber">109</context>
|
||||||
|
</context-group>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="audit.col.resource" datatype="html">
|
||||||
|
<source>Resource</source>
|
||||||
|
<context-group purpose="location">
|
||||||
|
<context context-type="sourcefile">src/app/beheer/ui/audit.page.ts</context>
|
||||||
|
<context context-type="linenumber">110</context>
|
||||||
|
</context-group>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="audit.col.besluit" datatype="html">
|
||||||
|
<source>Besluit</source>
|
||||||
|
<context-group purpose="location">
|
||||||
|
<context context-type="sourcefile">src/app/beheer/ui/audit.page.ts</context>
|
||||||
|
<context context-type="linenumber">111</context>
|
||||||
|
</context-group>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="audit.col.rol" datatype="html">
|
||||||
|
<source>Rol</source>
|
||||||
|
<context-group purpose="location">
|
||||||
|
<context context-type="sourcefile">src/app/beheer/ui/audit.page.ts</context>
|
||||||
|
<context context-type="linenumber">112</context>
|
||||||
|
</context-group>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="audit.col.cid" datatype="html">
|
||||||
|
<source>Correlatie-id</source>
|
||||||
|
<context-group purpose="location">
|
||||||
|
<context context-type="sourcefile">src/app/beheer/ui/audit.page.ts</context>
|
||||||
|
<context context-type="linenumber">113</context>
|
||||||
|
</context-group>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="beheer.added" datatype="html">
|
<trans-unit id="beheer.added" datatype="html">
|
||||||
<source>toegevoegd</source>
|
<source>toegevoegd</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
@@ -2611,14 +2695,14 @@
|
|||||||
<source>Voer een geldig BSN van 9 cijfers in.</source>
|
<source>Voer een geldig BSN van 9 cijfers in.</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/shared/kernel/bsn.ts</context>
|
<context context-type="sourcefile">src/app/shared/kernel/bsn.ts</context>
|
||||||
<context context-type="linenumber">17</context>
|
<context context-type="linenumber">18</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="validation.bsnElfproef" datatype="html">
|
<trans-unit id="validation.bsnElfproef" datatype="html">
|
||||||
<source>Dit is geen geldig BSN (klopt niet met de elfproef).</source>
|
<source>Dit is geen geldig BSN (klopt niet met de elfproef).</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/shared/kernel/bsn.ts</context>
|
<context context-type="sourcefile">src/app/shared/kernel/bsn.ts</context>
|
||||||
<context context-type="linenumber">21</context>
|
<context context-type="linenumber">23</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="header.nav.huisstijl" datatype="html">
|
<trans-unit id="header.nav.huisstijl" datatype="html">
|
||||||
@@ -2663,6 +2747,20 @@
|
|||||||
<context context-type="linenumber">30</context>
|
<context context-type="linenumber">30</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="header.nav.audit" datatype="html">
|
||||||
|
<source>Auditlog</source>
|
||||||
|
<context-group purpose="location">
|
||||||
|
<context context-type="sourcefile">src/app/shared/layout/admin-links.ts</context>
|
||||||
|
<context context-type="linenumber">35</context>
|
||||||
|
</context-group>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="admin.link.audit.desc" datatype="html">
|
||||||
|
<source>Toegangs- en inzagebeslissingen bekijken</source>
|
||||||
|
<context-group purpose="location">
|
||||||
|
<context context-type="sourcefile">src/app/shared/layout/admin-links.ts</context>
|
||||||
|
<context context-type="linenumber">36</context>
|
||||||
|
</context-group>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="crumb.dashboard" datatype="html">
|
<trans-unit id="crumb.dashboard" datatype="html">
|
||||||
<source>Mijn overzicht</source>
|
<source>Mijn overzicht</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
|
|||||||
Reference in New Issue
Block a user