feat(WP-67): merge behandelportal into this repo as a monorepo
Restructures into apps/ssp + apps/behandelportal (two Angular projects) plus libs/shared + libs/beheer (cross-app libraries), replacing WP-61's separate sibling repo. That split had already produced real drift: a hand-vendored copy of the backend's OpenAPI doc, a shared/ui+layout tree forked and silently diverging (7 files), and beheer + the styles.scss token bridge duplicated byte-for-byte across both repos. - git mv the SSP's src/app/* into apps/ssp/; fold shared/, beheer/, environments/, the Storybook docs/*.mdx, and styles.scss into libs/shared + libs/beheer (all confirmed identical between the two repos before merging). auth stays deliberately duplicated per ADR-0002 (actor-specific, expected to diverge) - amended there. - One generated API client (libs/shared), no more vendored swagger.json. - .dependency-cruiser split into a base factory + one config per app, and Storybook into .storybook-ssp/.storybook-behandelportal - both forced by the @auth/* alias resolving to different directories per app. - SiteHeaderComponent/ShellComponent gained HEADER_NAV_ITEMS/ HEADER_ADMIN_LINKS/DEBUG_PANEL injection tokens so each app supplies its own nav/admin-links/dev-panel instead of one being hardcoded. - CLAUDE.md, ARCHITECTURE.md, dependencies.md, and ADR-0002 updated; WP-67 backlog entry documents the full decision trail. npm run ci green (lint, dep:check x2, 360 tests across ssp/ behandelportal/shared/beheer, both localized builds, backend tests, snippet + api-client drift); both dev servers, both Storybook instances, and docker compose verified working. The old sibling repo (/home/eho/repos/behandelportal) is left untouched, not deleted. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,97 @@
|
||||
import { Component, input, output } from '@angular/core';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { FormFieldComponent } from '@shared/ui/form-field/form-field.component';
|
||||
import { TextInputComponent } from '@shared/ui/text-input/text-input.component';
|
||||
|
||||
export interface AdresValue {
|
||||
straat: string;
|
||||
postcode: string;
|
||||
woonplaats: string;
|
||||
}
|
||||
export type AdresErrors = Partial<Record<keyof AdresValue, string>>;
|
||||
|
||||
/** Organism: the editable address block (straat / postcode / woonplaats), grouped
|
||||
in a fieldset/legend. Pure & presentational — values in via `value`, errors in
|
||||
via `errors`, every keystroke out via `fieldChange`. No store, no services, no
|
||||
internal state; the container owns the Model and decides what a change means
|
||||
(registratie-wizard dispatches SetField; change-request-form sets its props).
|
||||
Composes the form-field molecule (×3) so labels/error wiring stay consistent. */
|
||||
@Component({
|
||||
selector: 'app-address-fields',
|
||||
imports: [FormsModule, FormFieldComponent, TextInputComponent],
|
||||
// No scoped `fieldset` reset here: the fieldset must keep CIBG's
|
||||
// `.form-horizontal fieldset` grey-box padding/margin. A local `fieldset{padding:0}`
|
||||
// would tie on specificity and (injected later) win, flattening the padding.
|
||||
styles: [
|
||||
`
|
||||
legend {
|
||||
padding: 0;
|
||||
font-weight: var(--rhc-text-font-weight-semi-bold);
|
||||
margin-block-end: var(--rhc-space-max-md);
|
||||
}
|
||||
`,
|
||||
],
|
||||
template: `
|
||||
<fieldset>
|
||||
<legend>{{ legend() }}</legend>
|
||||
<app-form-field
|
||||
i18n-label="@@address.straat"
|
||||
label="Straat en huisnummer"
|
||||
[fieldId]="idPrefix() + '-straat'"
|
||||
required
|
||||
[error]="errors().straat"
|
||||
>
|
||||
<app-text-input
|
||||
[inputId]="idPrefix() + '-straat'"
|
||||
[invalid]="!!errors().straat"
|
||||
[ngModel]="value().straat"
|
||||
(ngModelChange)="fieldChange.emit({ key: 'straat', value: $event })"
|
||||
name="straat"
|
||||
[ngModelOptions]="{ standalone: true }"
|
||||
/>
|
||||
</app-form-field>
|
||||
<app-form-field
|
||||
i18n-label="@@address.postcode"
|
||||
label="Postcode"
|
||||
[fieldId]="idPrefix() + '-postcode'"
|
||||
required
|
||||
[error]="errors().postcode"
|
||||
>
|
||||
<app-text-input
|
||||
[inputId]="idPrefix() + '-postcode'"
|
||||
[invalid]="!!errors().postcode"
|
||||
[ngModel]="value().postcode"
|
||||
(ngModelChange)="fieldChange.emit({ key: 'postcode', value: $event })"
|
||||
name="postcode"
|
||||
i18n-placeholder="@@address.postcodePlaceholder"
|
||||
placeholder="1234 AB"
|
||||
[ngModelOptions]="{ standalone: true }"
|
||||
/>
|
||||
</app-form-field>
|
||||
<app-form-field
|
||||
i18n-label="@@address.woonplaats"
|
||||
label="Woonplaats"
|
||||
[fieldId]="idPrefix() + '-woonplaats'"
|
||||
required
|
||||
[error]="errors().woonplaats"
|
||||
>
|
||||
<app-text-input
|
||||
[inputId]="idPrefix() + '-woonplaats'"
|
||||
[invalid]="!!errors().woonplaats"
|
||||
[ngModel]="value().woonplaats"
|
||||
(ngModelChange)="fieldChange.emit({ key: 'woonplaats', value: $event })"
|
||||
name="woonplaats"
|
||||
[ngModelOptions]="{ standalone: true }"
|
||||
/>
|
||||
</app-form-field>
|
||||
</fieldset>
|
||||
`,
|
||||
})
|
||||
export class AddressFieldsComponent {
|
||||
value = input.required<AdresValue>();
|
||||
errors = input<AdresErrors>({});
|
||||
/** Prefix for field ids/labels — keeps them unique if two blocks ever co-exist. */
|
||||
idPrefix = input('adres');
|
||||
legend = input($localize`:@@address.legend:Adres`);
|
||||
fieldChange = output<{ key: keyof AdresValue; value: string }>();
|
||||
}
|
||||
Reference in New Issue
Block a user