The folder now equals the layer, as CLAUDE.md decision 2 requires. 33 directories move by git mv (25 flat, plus upload/'s 8 subfolders split across all three layers). 28 distinct @shared/ui/* specifiers rewrite across 73 files, longest-first. Five relative imports inside upload/ become @shared/ui aliases because their sibling now lives in a different layer; two stay relative because both ends stay in the same layer. Four .mdx docs get their seven broken story imports fixed; atomic-design.mdx's page-shell import is untouched, because layout/ does not move. No component, template, story title, or layer-tag comment changes. That is RD-28's job. Verified against the ticket's acceptance commands: the 26 flat directories become exactly 3 layer folders with the counts the ticket names, only three @shared/ui/* prefixes remain (atoms, molecules, organisms), the .mdx import count holds at 7, and the relative-import count inside ui/ drops from 7 to 2 as decision 4 requires. The @shared/ui/ occurrence count moves from 200 to 205: decision 4 mandates turning 5 of those 7 relative imports into @shared/ui/* aliases, which decision 3's "200 before, 200 after" check does not account for. The 5-occurrence gap is exactly the 5 conversions decision 4 names, not a lost or duplicated specifier. npm run ci --full passes: lint, typecheck, dep:check, format, tokens, seam, both apps' + both libraries' tests, both apps' localized build, audit, backend tests, all three generated-artifact drift checks, and both Storybook instances' build + axe-core a11y suite (67+45 suites, 198+112 tests, all green). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
98 lines
3.6 KiB
TypeScript
98 lines
3.6 KiB
TypeScript
import { Component, input, output } from '@angular/core';
|
||
import { FormsModule } from '@angular/forms';
|
||
import { FormFieldComponent } from '@shared/ui/molecules/form-field/form-field.component';
|
||
import { TextInputComponent } from '@shared/ui/atoms/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 }>();
|
||
}
|