feat(dev): WP-33 — in-app dev switchers for scenario + role
CI / frontend (push) Successful in 1m47s
CI / storybook-a11y (push) Successful in 27m52s
CI / backend (push) Successful in 1m24s
CI / e2e (push) Successful in 27m24s
CI / semgrep (push) Failing after 30s
CI / api-client-drift (push) Successful in 2m7s

Surface the ?scenario= and ?role= dev stand-ins as dropdowns in the existing
debug-state devtool, so a demo can flip them with a click instead of editing the
URL. scenario.ts/role.ts gain set* setters + exported valid-value lists (reused
by the panel, no duplicated source of truth); scenario becomes tab-sticky like
role so it survives navigation. Applied via location.reload() since both are
read per-request in interceptors. Extends the debug-state eslint exemption to the
ui→infrastructure rule (same devtool precedent).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
eho
2026-07-22 16:53:26 +02:00
co-authored by Claude Opus 4.8
parent 610ff7c1cd
commit 1ed4850858
7 changed files with 161 additions and 8 deletions
@@ -4,6 +4,9 @@ import { SessionStore } from '@auth/application/session.store';
import { Session } from '@auth/domain/session';
import { BigProfileStore } from '@registratie/application/big-profile.store';
import { map } from '@shared/application/remote-data';
import { Role } from '@shared/domain/role';
import { ROLES, currentRole, setRole } from '@shared/infrastructure/role';
import { Scenario, SCENARIOS, currentScenario, setScenario } from '@shared/infrastructure/scenario';
import { maskBsn, redactProfile } from './mask';
// CIBG-GAP EXTENSION: n/a — devtool, no corresponding CIBG concept; deliberately
@@ -57,6 +60,27 @@ import { maskBsn, redactProfile } from './mask';
white-space: pre-wrap;
word-break: break-word;
}
.switchers {
display: flex;
gap: 0.75rem;
padding: 0.75rem;
border-bottom: var(--rhc-border-width-sm) solid var(--app-devpanel-border);
}
.switchers label {
display: flex;
flex-direction: column;
gap: 0.25rem;
font: 11px/1.3 monospace;
color: var(--app-devpanel-accent);
}
.switchers select {
font: 12px/1 monospace;
background: var(--app-devpanel-bg);
color: var(--app-devpanel-fg);
border: var(--rhc-border-width-sm) solid var(--app-devpanel-border);
border-radius: 3px;
padding: 0.25rem;
}
`,
],
template: `
@@ -66,6 +90,24 @@ import { maskBsn, redactProfile } from './mask';
</button>
@if (visible()) {
<div class="panel">
<div class="switchers">
<label
>role
<select [value]="role" (change)="switchRole($any($event.target).value)">
@for (r of roles; track r) {
<option [value]="r">{{ r }}</option>
}
</select>
</label>
<label
>scenario
<select [value]="scenario" (change)="switchScenario($any($event.target).value)">
@for (s of scenarios; track s) {
<option [value]="s">{{ s }}</option>
}
</select>
</label>
</div>
<pre>{{ snapshot() | json }}</pre>
</div>
}
@@ -92,6 +134,22 @@ export class DebugStateComponent {
pendingHerregistratie: this.profileStore?.pendingHerregistratie(),
}));
// Dev switchers (WP-33): flip role/scenario without hand-editing the URL. Both are
// read per-request in interceptors, so a reload re-runs them and re-fetches decisions.
protected readonly roles = ROLES;
protected readonly scenarios = SCENARIOS;
protected readonly role = currentRole();
protected readonly scenario = currentScenario();
switchRole(r: Role): void {
setRole(r);
location.reload();
}
switchScenario(s: Scenario): void {
setScenario(s);
location.reload();
}
toggle(): void {
if (!this.visible()) this.profileStore ??= this.injector.get(BigProfileStore);
this.visible.update((v) => !v);