refactor: move libs/shared/src/ui/ into atoms/molecules/organisms (RD-27)

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>
This commit is contained in:
eho
2026-09-05 08:14:10 +02:00
co-authored by Claude Sonnet 5
parent 7a8eab917b
commit 43dc3210cd
135 changed files with 438 additions and 213 deletions
@@ -1,5 +1,5 @@
import { Component, output } from '@angular/core';
import { ButtonComponent } from '@shared/ui/button/button.component';
import { ButtonComponent } from '@shared/ui/atoms/button/button.component';
/**
* Organism: employee-SSO-style mock login (ADR-C-004). No real auth — and,
@@ -2,12 +2,12 @@ import { Component, computed, inject } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { successOf } from '@shared/application/remote-data';
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 { SkeletonComponent } from '@shared/ui/skeleton/skeleton.component';
import { DataBlockComponent } from '@shared/ui/data-block/data-block.component';
import { DataRowComponent } from '@shared/ui/data-row/data-row.component';
import { ASYNC } from '@shared/ui/async/async.component';
import { AlertComponent } from '@shared/ui/atoms/alert/alert.component';
import { ButtonComponent } from '@shared/ui/atoms/button/button.component';
import { SkeletonComponent } from '@shared/ui/atoms/skeleton/skeleton.component';
import { DataBlockComponent } from '@shared/ui/molecules/data-block/data-block.component';
import { DataRowComponent } from '@shared/ui/molecules/data-row/data-row.component';
import { ASYNC } from '@shared/ui/molecules/async/async.component';
import { BeoordelingStore } from '@behandeling/application/beoordeling.store';
import { detailRows } from '@behandeling/domain/beoordeling-view';
import { BeoordelingDocumentenComponent } from '@behandeling/ui/beoordeling-documenten/beoordeling-documenten.component';
@@ -1,13 +1,16 @@
import { Component, computed, input, output } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { ButtonComponent } from '@shared/ui/button/button.component';
import { HeadingComponent } from '@shared/ui/heading/heading.component';
import { AlertComponent } from '@shared/ui/alert/alert.component';
import { FormFieldComponent } from '@shared/ui/form-field/form-field.component';
import { TextInputComponent } from '@shared/ui/text-input/text-input.component';
import { RadioGroupComponent, RadioOption } from '@shared/ui/radio-group/radio-group.component';
import { DataBlockComponent } from '@shared/ui/data-block/data-block.component';
import { DataRowComponent } from '@shared/ui/data-row/data-row.component';
import { ButtonComponent } from '@shared/ui/atoms/button/button.component';
import { HeadingComponent } from '@shared/ui/atoms/heading/heading.component';
import { AlertComponent } from '@shared/ui/atoms/alert/alert.component';
import { FormFieldComponent } from '@shared/ui/molecules/form-field/form-field.component';
import { TextInputComponent } from '@shared/ui/atoms/text-input/text-input.component';
import {
RadioGroupComponent,
RadioOption,
} from '@shared/ui/atoms/radio-group/radio-group.component';
import { DataBlockComponent } from '@shared/ui/molecules/data-block/data-block.component';
import { DataRowComponent } from '@shared/ui/molecules/data-row/data-row.component';
import { createStore } from '@shared/application/store';
import { whenTag } from '@shared/kernel/fp';
import { BesluitState, BesluitMsg, initial, reduce } from '@behandeling/domain/besluit.machine';
@@ -1,6 +1,6 @@
import { Component, input } from '@angular/core';
import { ApplicationListComponent } from '@shared/ui/application-list/application-list.component';
import { ApplicationLinkComponent } from '@shared/ui/application-link/application-link.component';
import { ApplicationListComponent } from '@shared/ui/molecules/application-list/application-list.component';
import { ApplicationLinkComponent } from '@shared/ui/molecules/application-link/application-link.component';
import { WerkvoorraadItem } from '@behandeling/domain/werkvoorraad-item';
import { werkvoorraadRow } from '@behandeling/domain/werkvoorraad-item-view';
@@ -1,9 +1,9 @@
import { Component, computed, effect, inject } from '@angular/core';
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 { SkeletonComponent } from '@shared/ui/skeleton/skeleton.component';
import { ASYNC } from '@shared/ui/async/async.component';
import { AlertComponent } from '@shared/ui/atoms/alert/alert.component';
import { ButtonComponent } from '@shared/ui/atoms/button/button.component';
import { SkeletonComponent } from '@shared/ui/atoms/skeleton/skeleton.component';
import { ASYNC } from '@shared/ui/molecules/async/async.component';
import { AccessStore } from '@shared/application/access.store';
import { successOr } from '@shared/application/remote-data';
import { WerkvoorraadStore } from '@behandeling/application/werkvoorraad.store';
@@ -1,8 +1,8 @@
import { Component, 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';
import { ButtonComponent } from '@shared/ui/button/button.component';
import { FormFieldComponent } from '@shared/ui/molecules/form-field/form-field.component';
import { TextInputComponent } from '@shared/ui/atoms/text-input/text-input.component';
import { ButtonComponent } from '@shared/ui/atoms/button/button.component';
/** Organism: DigiD-style mock login. No real auth — just composes atoms/molecules. */
@Component({
+1 -1
View File
@@ -1,7 +1,7 @@
import { Component, inject, signal } from '@angular/core';
import { Router } from '@angular/router';
import { PageShellComponent } from '@shared/layout/page-shell/page-shell.component';
import { AlertComponent } from '@shared/ui/alert/alert.component';
import { AlertComponent } from '@shared/ui/atoms/alert/alert.component';
import { LoginFormComponent } from '@auth/ui/login-form/login-form.component';
import { SessionStore } from '@auth/application/session.store';
@@ -1,10 +1,10 @@
import { Component, ElementRef, computed, input, output, viewChild } from '@angular/core';
import { PlaceholderOption } from '@shared/ui/rich-text-editor/rich-text-editor.component';
import { ButtonComponent } from '@shared/ui/button/button.component';
import { HeadingComponent } from '@shared/ui/heading/heading.component';
import { MaskedValueComponent } from '@shared/ui/masked-value/masked-value.component';
import { AlertComponent } from '@shared/ui/alert/alert.component';
import { StepperComponent } from '@shared/ui/stepper/stepper.component';
import { PlaceholderOption } from '@shared/ui/molecules/rich-text-editor/rich-text-editor.component';
import { ButtonComponent } from '@shared/ui/atoms/button/button.component';
import { HeadingComponent } from '@shared/ui/atoms/heading/heading.component';
import { MaskedValueComponent } from '@shared/ui/atoms/masked-value/masked-value.component';
import { AlertComponent } from '@shared/ui/atoms/alert/alert.component';
import { StepperComponent } from '@shared/ui/molecules/stepper/stepper.component';
import { Besluit, Brief, CaseContext, LibraryPassage } from '@brief/domain/brief';
import { besluitGuidance, inferSelection } from '@brief/domain/besluit';
import { Diagnostic } from '@brief/domain/placeholders';
@@ -1,8 +1,11 @@
import { Component, computed, input, linkedSignal, output } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { CheckboxComponent } from '@shared/ui/checkbox/checkbox.component';
import { RadioGroupComponent, RadioOption } from '@shared/ui/radio-group/radio-group.component';
import { HeadingComponent } from '@shared/ui/heading/heading.component';
import { CheckboxComponent } from '@shared/ui/atoms/checkbox/checkbox.component';
import {
RadioGroupComponent,
RadioOption,
} from '@shared/ui/atoms/radio-group/radio-group.component';
import { HeadingComponent } from '@shared/ui/atoms/heading/heading.component';
import { Besluit, LibraryPassage } from '@brief/domain/brief';
import { redenenFor } from '@brief/domain/besluit';
+3 -3
View File
@@ -1,8 +1,8 @@
import { Component, computed, inject } from '@angular/core';
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 { AlertComponent } from '@shared/ui/atoms/alert/alert.component';
import { ButtonComponent } from '@shared/ui/atoms/button/button.component';
import { ASYNC } from '@shared/ui/molecules/async/async.component';
import { BriefStore } from '@brief/application/brief.store';
import { LetterComposerComponent } from '@brief/ui/letter-composer/letter-composer.component';
import { BehandelSchermComponent } from '@brief/ui/behandel-scherm/behandel-scherm.component';
@@ -1,5 +1,5 @@
import { Component, computed, input, output } from '@angular/core';
import { AlertComponent } from '@shared/ui/alert/alert.component';
import { AlertComponent } from '@shared/ui/atoms/alert/alert.component';
import { Diagnostic } from '@brief/domain/placeholders';
/** Molecule: lists all letter diagnostics grouped by severity. Errors block
@@ -3,8 +3,8 @@ import { RichTextBlock } from '@shared/kernel/rich-text';
import {
RichTextEditorComponent,
PlaceholderOption,
} from '@shared/ui/rich-text-editor/rich-text-editor.component';
import { ButtonComponent } from '@shared/ui/button/button.component';
} from '@shared/ui/molecules/rich-text-editor/rich-text-editor.component';
import { ButtonComponent } from '@shared/ui/atoms/button/button.component';
import { LetterBlock } from '@brief/domain/brief';
/** Molecule: one block in a section — its editor plus provenance + block controls.
@@ -14,7 +14,7 @@ import {
signal,
viewChild,
} from '@angular/core';
import { ButtonComponent } from '@shared/ui/button/button.component';
import { ButtonComponent } from '@shared/ui/atoms/button/button.component';
import { formatDatumNl } from '@shared/kernel/datum';
import { Paragraph } from '@shared/kernel/rich-text';
import { Brief, LetterBlock } from '@brief/domain/brief';
@@ -1,5 +1,5 @@
import { Component, computed, input } from '@angular/core';
import { PlaceholderChipComponent } from '@shared/ui/placeholder-chip/placeholder-chip.component';
import { PlaceholderChipComponent } from '@shared/ui/atoms/placeholder-chip/placeholder-chip.component';
import { RichTextNode } from '@shared/kernel/rich-text';
import { Diagnostic, PlaceholderDef } from '@brief/domain/placeholders';
@@ -1,8 +1,8 @@
import { Component, computed, input, output, signal } from '@angular/core';
import { HeadingComponent } from '@shared/ui/heading/heading.component';
import { StatusBadgeComponent } from '@shared/ui/status-badge/status-badge.component';
import { ButtonComponent } from '@shared/ui/button/button.component';
import { AlertComponent } from '@shared/ui/alert/alert.component';
import { HeadingComponent } from '@shared/ui/atoms/heading/heading.component';
import { StatusBadgeComponent } from '@shared/ui/atoms/status-badge/status-badge.component';
import { ButtonComponent } from '@shared/ui/atoms/button/button.component';
import { AlertComponent } from '@shared/ui/atoms/alert/alert.component';
import { Brief } from '@brief/domain/brief';
import { OrgTemplate } from '@brief/domain/org-template';
import { Diagnostic } from '@brief/domain/placeholders';
@@ -1,5 +1,5 @@
import { Component, computed, input, output } from '@angular/core';
import { PlaceholderOption } from '@shared/ui/rich-text-editor/rich-text-editor.component';
import { PlaceholderOption } from '@shared/ui/molecules/rich-text-editor/rich-text-editor.component';
import { Brief } from '@brief/domain/brief';
import { BriefMsg } from '@brief/domain/brief.machine';
import { LetterSectionComponent } from '@brief/ui/letter-section/letter-section.component';
@@ -1,8 +1,8 @@
import { Component, input, output } from '@angular/core';
import { RichTextBlock } from '@shared/kernel/rich-text';
import { PlaceholderOption } from '@shared/ui/rich-text-editor/rich-text-editor.component';
import { ButtonComponent } from '@shared/ui/button/button.component';
import { HeadingComponent } from '@shared/ui/heading/heading.component';
import { PlaceholderOption } from '@shared/ui/molecules/rich-text-editor/rich-text-editor.component';
import { ButtonComponent } from '@shared/ui/atoms/button/button.component';
import { HeadingComponent } from '@shared/ui/atoms/heading/heading.component';
import { LetterSection } from '@brief/domain/brief';
import { BriefMsg } from '@brief/domain/brief.machine';
import { LetterBlockComponent } from '@brief/ui/letter-block/letter-block.component';
@@ -1,8 +1,8 @@
import { Component, computed, input, output } from '@angular/core';
import { HeadingComponent } from '@shared/ui/heading/heading.component';
import { AlertComponent } from '@shared/ui/alert/alert.component';
import { FileInputComponent } from '@shared/ui/upload/file-input/file-input.component';
import { SingleUploadComponent } from '@shared/ui/upload/single-upload/single-upload.component';
import { HeadingComponent } from '@shared/ui/atoms/heading/heading.component';
import { AlertComponent } from '@shared/ui/atoms/alert/alert.component';
import { FileInputComponent } from '@shared/ui/atoms/upload/file-input/file-input.component';
import { SingleUploadComponent } from '@shared/ui/molecules/upload/single-upload/single-upload.component';
import { UploadState } from '@shared/domain/upload.machine';
const LOGO_CATEGORY = 'org-logo';
@@ -1,6 +1,6 @@
import { Component, computed, input, output } from '@angular/core';
import { ButtonComponent } from '@shared/ui/button/button.component';
import { AlertComponent } from '@shared/ui/alert/alert.component';
import { ButtonComponent } from '@shared/ui/atoms/button/button.component';
import { AlertComponent } from '@shared/ui/atoms/alert/alert.component';
import { UploadState } from '@shared/domain/upload.machine';
import { Brief } from '@brief/domain/brief';
import { SAMPLE_LETTER_BRIEF } from '@brief/domain/sample-letter';
@@ -1,7 +1,7 @@
import { Component, input, output } from '@angular/core';
import { DatePipe } from '@angular/common';
import { HeadingComponent } from '@shared/ui/heading/heading.component';
import { ButtonComponent } from '@shared/ui/button/button.component';
import { HeadingComponent } from '@shared/ui/atoms/heading/heading.component';
import { ButtonComponent } from '@shared/ui/atoms/button/button.component';
import { OrgTemplateVersion } from '@brief/domain/org-template';
/**
@@ -1,8 +1,8 @@
import { Component, computed, effect, inject } from '@angular/core';
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 { AlertComponent } from '@shared/ui/atoms/alert/alert.component';
import { ButtonComponent } from '@shared/ui/atoms/button/button.component';
import { ASYNC } from '@shared/ui/molecules/async/async.component';
import { AccessStore } from '@shared/application/access.store';
import { OrgTemplateStore } from '@brief/application/org-template.store';
import { OrgTemplateEditorComponent } from '@brief/ui/org-template-editor/org-template-editor.component';
@@ -1,8 +1,8 @@
import { Component, computed, input, output, signal } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { CheckboxComponent } from '@shared/ui/checkbox/checkbox.component';
import { ButtonComponent } from '@shared/ui/button/button.component';
import { TextInputComponent } from '@shared/ui/text-input/text-input.component';
import { CheckboxComponent } from '@shared/ui/atoms/checkbox/checkbox.component';
import { ButtonComponent } from '@shared/ui/atoms/button/button.component';
import { TextInputComponent } from '@shared/ui/atoms/text-input/text-input.component';
import { textOf } from '@shared/kernel/rich-text';
import { LibraryPassage } from '@brief/domain/brief';
@@ -1,7 +1,7 @@
import { Component, input, output, signal } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { AlertComponent } from '@shared/ui/alert/alert.component';
import { ButtonComponent } from '@shared/ui/button/button.component';
import { AlertComponent } from '@shared/ui/atoms/alert/alert.component';
import { ButtonComponent } from '@shared/ui/atoms/button/button.component';
/** Molecule: shows the rejection comments (drafter view) or collects them from the
approver. The approver rejects WITH comments; they never edit the letter. */
@@ -1,8 +1,8 @@
import { Component, computed, inject, input } 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';
import { AlertComponent } from '@shared/ui/alert/alert.component';
import { FormFieldComponent } from '@shared/ui/molecules/form-field/form-field.component';
import { TextInputComponent } from '@shared/ui/atoms/text-input/text-input.component';
import { AlertComponent } from '@shared/ui/atoms/alert/alert.component';
import {
WizardShellComponent,
WizardError,
@@ -10,7 +10,7 @@ import {
naarStapLabel,
} from '@shared/layout/wizard-shell/wizard-shell.component';
import { toWizardErrors } from '@shared/layout/wizard-shell/wizard-errors';
import { ConfirmationComponent } from '@shared/ui/confirmation/confirmation.component';
import { ConfirmationComponent } from '@shared/ui/molecules/confirmation/confirmation.component';
import { createStore } from '@shared/application/store';
import { whenTag } from '@shared/kernel/fp';
import { BigProfileStore } from '@registratie/application/big-profile.store';
@@ -23,7 +23,7 @@ import {
hasProgress,
} from '@herregistratie/domain/herregistratie.machine';
import { createDraftSync } from '@registratie/application/draft-sync';
import { DocumentUploadComponent } from '@shared/ui/upload/document-upload/document-upload.component';
import { DocumentUploadComponent } from '@shared/ui/organisms/upload/document-upload/document-upload.component';
import { createUploadController } from '@shared/application/upload-controller';
import { UploadState, initialUpload, deliveryRefs } from '@shared/domain/upload.machine';
@@ -1,7 +1,7 @@
import { Component, computed, inject } from '@angular/core';
import { PageShellComponent } from '@shared/layout/page-shell/page-shell.component';
import { AlertComponent } from '@shared/ui/alert/alert.component';
import { ASYNC } from '@shared/ui/async/async.component';
import { AlertComponent } from '@shared/ui/atoms/alert/alert.component';
import { ASYNC } from '@shared/ui/molecules/async/async.component';
import { map } from '@shared/application/remote-data';
import { BigProfileStore } from '@registratie/application/big-profile.store';
import { HerregistratieWizardComponent } from '@herregistratie/ui/herregistratie-wizard/herregistratie-wizard.component';
@@ -1,8 +1,8 @@
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';
import { RadioGroupComponent, JA_NEE } from '@shared/ui/radio-group/radio-group.component';
import { FormFieldComponent } from '@shared/ui/molecules/form-field/form-field.component';
import { TextInputComponent } from '@shared/ui/atoms/text-input/text-input.component';
import { RadioGroupComponent, JA_NEE } from '@shared/ui/atoms/radio-group/radio-group.component';
import { Answers, Errors } from '@herregistratie/domain/intake.machine';
/** Step: the intake wizard's first screen (foreign work in the last 5 years).
@@ -1,6 +1,6 @@
import { Component, computed, effect, inject, input, untracked } from '@angular/core';
import { ButtonComponent } from '@shared/ui/button/button.component';
import { ConfirmationComponent } from '@shared/ui/confirmation/confirmation.component';
import { ButtonComponent } from '@shared/ui/atoms/button/button.component';
import { ConfirmationComponent } from '@shared/ui/molecules/confirmation/confirmation.component';
import {
WizardShellComponent,
WizardError,
@@ -1,7 +1,7 @@
import { Component, computed, input, output } from '@angular/core';
import { AlertComponent } from '@shared/ui/alert/alert.component';
import { DataRowComponent } from '@shared/ui/data-row/data-row.component';
import { ReviewSectionComponent } from '@shared/ui/review-section/review-section.component';
import { AlertComponent } from '@shared/ui/atoms/alert/alert.component';
import { DataRowComponent } from '@shared/ui/molecules/data-row/data-row.component';
import { ReviewSectionComponent } from '@shared/ui/molecules/review-section/review-section.component';
import { Answers, lageUren } from '@herregistratie/domain/intake.machine';
/** Step: the intake wizard's review screen. Pure & presentational values in via
@@ -1,8 +1,8 @@
import { Component, computed, 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';
import { RadioGroupComponent, JA_NEE } from '@shared/ui/radio-group/radio-group.component';
import { FormFieldComponent } from '@shared/ui/molecules/form-field/form-field.component';
import { TextInputComponent } from '@shared/ui/atoms/text-input/text-input.component';
import { RadioGroupComponent, JA_NEE } from '@shared/ui/atoms/radio-group/radio-group.component';
import { Answers, Errors, lageUren } from '@herregistratie/domain/intake.machine';
/** Step: the intake wizard's second screen (work experience in the Netherlands,
@@ -1,6 +1,6 @@
import { Component } from '@angular/core';
import { PageShellComponent } from '@shared/layout/page-shell/page-shell.component';
import { AlertComponent } from '@shared/ui/alert/alert.component';
import { AlertComponent } from '@shared/ui/atoms/alert/alert.component';
import { IntakeWizardComponent } from '@herregistratie/ui/intake-wizard/intake-wizard.component';
/** Page: the branching intake questionnaire. Built entirely from existing
@@ -1,7 +1,7 @@
import { Component, computed, inject } from '@angular/core';
import { HeadingComponent } from '@shared/ui/heading/heading.component';
import { ApplicationListComponent } from '@shared/ui/application-list/application-list.component';
import { ApplicationLinkComponent } from '@shared/ui/application-link/application-link.component';
import { HeadingComponent } from '@shared/ui/atoms/heading/heading.component';
import { ApplicationListComponent } from '@shared/ui/molecules/application-list/application-list.component';
import { ApplicationLinkComponent } from '@shared/ui/molecules/application-link/application-link.component';
import { AccessStore } from '@shared/application/access.store';
import { HEADER_ADMIN_LINKS } from '@shared/layout/site-header/nav-config';
@@ -1,7 +1,7 @@
import { Component, computed, inject } from '@angular/core';
import { HeadingComponent } from '@shared/ui/heading/heading.component';
import { ApplicationListComponent } from '@shared/ui/application-list/application-list.component';
import { ApplicationLinkComponent } from '@shared/ui/application-link/application-link.component';
import { HeadingComponent } from '@shared/ui/atoms/heading/heading.component';
import { ApplicationListComponent } from '@shared/ui/molecules/application-list/application-list.component';
import { ApplicationLinkComponent } from '@shared/ui/molecules/application-link/application-link.component';
import { FeatureFlagStore } from '@shared/application/feature-flags.store';
import { FLAG_INSCHRIJVING_OPEN } from '@shared/domain/feature-flag';
@@ -1,7 +1,7 @@
import { Component, computed, input, output } from '@angular/core';
import { formatDatumNl } from '@shared/kernel/datum';
import { ButtonComponent } from '@shared/ui/button/button.component';
import { AlertComponent } from '@shared/ui/alert/alert.component';
import { ButtonComponent } from '@shared/ui/atoms/button/button.component';
import { AlertComponent } from '@shared/ui/atoms/alert/alert.component';
import { Aanvraag } from '@registratie/domain/aanvraag';
import { TYPE_LABELS } from '@registratie/domain/aanvraag-view';
import { blockActions } from '@registratie/domain/block-actions';
@@ -2,11 +2,11 @@ import { Component, computed, inject } from '@angular/core';
import { successOf } from '@shared/application/remote-data';
import { ActivatedRoute } from '@angular/router';
import { PageShellComponent } from '@shared/layout/page-shell/page-shell.component';
import { SkeletonComponent } from '@shared/ui/skeleton/skeleton.component';
import { AlertComponent } from '@shared/ui/alert/alert.component';
import { DataBlockComponent } from '@shared/ui/data-block/data-block.component';
import { DataRowComponent } from '@shared/ui/data-row/data-row.component';
import { ASYNC } from '@shared/ui/async/async.component';
import { SkeletonComponent } from '@shared/ui/atoms/skeleton/skeleton.component';
import { AlertComponent } from '@shared/ui/atoms/alert/alert.component';
import { DataBlockComponent } from '@shared/ui/molecules/data-block/data-block.component';
import { DataRowComponent } from '@shared/ui/molecules/data-row/data-row.component';
import { ASYNC } from '@shared/ui/molecules/async/async.component';
import { AanvragenStore } from '@registratie/application/aanvragen.store';
import { Aanvraag } from '@registratie/domain/aanvraag';
import { detailRows } from '@registratie/domain/aanvraag-view';
@@ -1,7 +1,7 @@
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';
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;
@@ -1,10 +1,10 @@
import { Component, computed, effect, inject } from '@angular/core';
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 { DataBlockComponent } from '@shared/ui/data-block/data-block.component';
import { DataRowComponent } from '@shared/ui/data-row/data-row.component';
import { ASYNC } from '@shared/ui/async/async.component';
import { AlertComponent } from '@shared/ui/atoms/alert/alert.component';
import { ButtonComponent } from '@shared/ui/atoms/button/button.component';
import { DataBlockComponent } from '@shared/ui/molecules/data-block/data-block.component';
import { DataRowComponent } from '@shared/ui/molecules/data-row/data-row.component';
import { ASYNC } from '@shared/ui/molecules/async/async.component';
import { AccessStore } from '@shared/application/access.store';
import { successOr } from '@shared/application/remote-data';
import { formatDatumNl } from '@shared/kernel/datum';
@@ -1,12 +1,12 @@
import { Component, computed, input } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { ButtonComponent } from '@shared/ui/button/button.component';
import { HeadingComponent } from '@shared/ui/heading/heading.component';
import { AlertComponent } from '@shared/ui/alert/alert.component';
import { FormFieldComponent } from '@shared/ui/form-field/form-field.component';
import { TextInputComponent } from '@shared/ui/text-input/text-input.component';
import { DataBlockComponent } from '@shared/ui/data-block/data-block.component';
import { DataRowComponent } from '@shared/ui/data-row/data-row.component';
import { ButtonComponent } from '@shared/ui/atoms/button/button.component';
import { HeadingComponent } from '@shared/ui/atoms/heading/heading.component';
import { AlertComponent } from '@shared/ui/atoms/alert/alert.component';
import { FormFieldComponent } from '@shared/ui/molecules/form-field/form-field.component';
import { TextInputComponent } from '@shared/ui/atoms/text-input/text-input.component';
import { DataBlockComponent } from '@shared/ui/molecules/data-block/data-block.component';
import { DataRowComponent } from '@shared/ui/molecules/data-row/data-row.component';
import { Adres } from '@registratie/domain/person';
import { createStore } from '@shared/application/store';
import { whenTag } from '@shared/kernel/fp';
@@ -1,11 +1,11 @@
import { Component, computed, inject } from '@angular/core';
import { Router } from '@angular/router';
import { AlertComponent } from '@shared/ui/alert/alert.component';
import { SkeletonComponent } from '@shared/ui/skeleton/skeleton.component';
import { HeadingComponent } from '@shared/ui/heading/heading.component';
import { ApplicationListComponent } from '@shared/ui/application-list/application-list.component';
import { ApplicationLinkComponent } from '@shared/ui/application-link/application-link.component';
import { ASYNC } from '@shared/ui/async/async.component';
import { AlertComponent } from '@shared/ui/atoms/alert/alert.component';
import { SkeletonComponent } from '@shared/ui/atoms/skeleton/skeleton.component';
import { HeadingComponent } from '@shared/ui/atoms/heading/heading.component';
import { ApplicationListComponent } from '@shared/ui/molecules/application-list/application-list.component';
import { ApplicationLinkComponent } from '@shared/ui/molecules/application-link/application-link.component';
import { ASYNC } from '@shared/ui/molecules/async/async.component';
import { successOr } from '@shared/application/remote-data';
import { AanvragenStore } from '@registratie/application/aanvragen.store';
import { Aanvraag, AanvraagType } from '@registratie/domain/aanvraag';
@@ -1,10 +1,10 @@
import { Component, inject } from '@angular/core';
import { successOf } from '@shared/application/remote-data';
import { HeadingComponent } from '@shared/ui/heading/heading.component';
import { SkeletonComponent } from '@shared/ui/skeleton/skeleton.component';
import { DataBlockComponent } from '@shared/ui/data-block/data-block.component';
import { DataRowComponent } from '@shared/ui/data-row/data-row.component';
import { ASYNC } from '@shared/ui/async/async.component';
import { HeadingComponent } from '@shared/ui/atoms/heading/heading.component';
import { SkeletonComponent } from '@shared/ui/atoms/skeleton/skeleton.component';
import { DataBlockComponent } from '@shared/ui/molecules/data-block/data-block.component';
import { DataRowComponent } from '@shared/ui/molecules/data-row/data-row.component';
import { ASYNC } from '@shared/ui/molecules/async/async.component';
import { BigProfileStore } from '@registratie/application/big-profile.store';
import { RegistrationSummaryComponent } from '@registratie/ui/registration-summary/registration-summary.component';
@@ -1,8 +1,8 @@
import { Component, inject } from '@angular/core';
import { successOf } from '@shared/application/remote-data';
import { HeadingComponent } from '@shared/ui/heading/heading.component';
import { SkeletonComponent } from '@shared/ui/skeleton/skeleton.component';
import { ASYNC } from '@shared/ui/async/async.component';
import { HeadingComponent } from '@shared/ui/atoms/heading/heading.component';
import { SkeletonComponent } from '@shared/ui/atoms/skeleton/skeleton.component';
import { ASYNC } from '@shared/ui/molecules/async/async.component';
import { BigProfileStore } from '@registratie/application/big-profile.store';
import { RegistrationTableComponent } from '@registratie/ui/registration-table/registration-table.component';
@@ -1,10 +1,10 @@
import { Component, computed, inject } from '@angular/core';
import { successOf } from '@shared/application/remote-data';
import { HeadingComponent } from '@shared/ui/heading/heading.component';
import { AlertComponent } from '@shared/ui/alert/alert.component';
import { SkeletonComponent } from '@shared/ui/skeleton/skeleton.component';
import { TaskListComponent } from '@shared/ui/task-list/task-list.component';
import { ASYNC } from '@shared/ui/async/async.component';
import { HeadingComponent } from '@shared/ui/atoms/heading/heading.component';
import { AlertComponent } from '@shared/ui/atoms/alert/alert.component';
import { SkeletonComponent } from '@shared/ui/atoms/skeleton/skeleton.component';
import { TaskListComponent } from '@shared/ui/molecules/task-list/task-list.component';
import { ASYNC } from '@shared/ui/molecules/async/async.component';
import { BigProfileStore } from '@registratie/application/big-profile.store';
import { tasksFromProfile } from '@registratie/domain/tasks';
@@ -1,10 +1,10 @@
import { Component, inject, 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';
import { RadioGroupComponent } from '@shared/ui/radio-group/radio-group.component';
import { AlertComponent } from '@shared/ui/alert/alert.component';
import { SkeletonComponent } from '@shared/ui/skeleton/skeleton.component';
import { FormFieldComponent } from '@shared/ui/molecules/form-field/form-field.component';
import { TextInputComponent } from '@shared/ui/atoms/text-input/text-input.component';
import { RadioGroupComponent } from '@shared/ui/atoms/radio-group/radio-group.component';
import { AlertComponent } from '@shared/ui/atoms/alert/alert.component';
import { SkeletonComponent } from '@shared/ui/atoms/skeleton/skeleton.component';
import { AddressFieldsComponent } from '@registratie/ui/address-fields/address-fields.component';
import { RegistratieLookupStore } from '@registratie/application/registratie-lookup.store';
import { Draft, DraftField, Errors } from '@registratie/domain/registratie-wizard.machine';
@@ -1,14 +1,14 @@
import { Component, computed, inject, 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';
import { RadioGroupComponent, JA_NEE } from '@shared/ui/radio-group/radio-group.component';
import { AlertComponent } from '@shared/ui/alert/alert.component';
import { SkeletonComponent } from '@shared/ui/skeleton/skeleton.component';
import { DataRowComponent } from '@shared/ui/data-row/data-row.component';
import { DataBlockComponent } from '@shared/ui/data-block/data-block.component';
import { ASYNC } from '@shared/ui/async/async.component';
import { DocumentUploadComponent } from '@shared/ui/upload/document-upload/document-upload.component';
import { FormFieldComponent } from '@shared/ui/molecules/form-field/form-field.component';
import { TextInputComponent } from '@shared/ui/atoms/text-input/text-input.component';
import { RadioGroupComponent, JA_NEE } from '@shared/ui/atoms/radio-group/radio-group.component';
import { AlertComponent } from '@shared/ui/atoms/alert/alert.component';
import { SkeletonComponent } from '@shared/ui/atoms/skeleton/skeleton.component';
import { DataRowComponent } from '@shared/ui/molecules/data-row/data-row.component';
import { DataBlockComponent } from '@shared/ui/molecules/data-block/data-block.component';
import { ASYNC } from '@shared/ui/molecules/async/async.component';
import { DocumentUploadComponent } from '@shared/ui/organisms/upload/document-upload/document-upload.component';
import { createUploadController } from '@shared/application/upload-controller';
import { UploadMsg, UploadState } from '@shared/domain/upload.machine';
import { RemoteData, successOr } from '@shared/application/remote-data';
@@ -1,7 +1,7 @@
import { Component, computed, inject, input, output } from '@angular/core';
import { AlertComponent } from '@shared/ui/alert/alert.component';
import { DataRowComponent } from '@shared/ui/data-row/data-row.component';
import { ReviewSectionComponent } from '@shared/ui/review-section/review-section.component';
import { AlertComponent } from '@shared/ui/atoms/alert/alert.component';
import { DataRowComponent } from '@shared/ui/molecules/data-row/data-row.component';
import { ReviewSectionComponent } from '@shared/ui/molecules/review-section/review-section.component';
import { successOr } from '@shared/application/remote-data';
import { RegistratieLookupStore } from '@registratie/application/registratie-lookup.store';
import { DuoLookupDto } from '@registratie/contracts/duo-diplomas.dto';
@@ -1,6 +1,6 @@
import { Component, computed, effect, inject, input, untracked } from '@angular/core';
import { ButtonComponent } from '@shared/ui/button/button.component';
import { ConfirmationComponent } from '@shared/ui/confirmation/confirmation.component';
import { ButtonComponent } from '@shared/ui/atoms/button/button.component';
import { ConfirmationComponent } from '@shared/ui/molecules/confirmation/confirmation.component';
import {
WizardShellComponent,
WizardError,
@@ -1,6 +1,6 @@
import { Component } from '@angular/core';
import { PageShellComponent } from '@shared/layout/page-shell/page-shell.component';
import { AlertComponent } from '@shared/ui/alert/alert.component';
import { AlertComponent } from '@shared/ui/atoms/alert/alert.component';
import { RegistratieWizardComponent } from '@registratie/ui/registratie-wizard/registratie-wizard.component';
/** Page: register in the BIG-register. Built entirely from existing building
@@ -1,8 +1,8 @@
import { Component, computed, inject } from '@angular/core';
import { successOf } from '@shared/application/remote-data';
import { PageShellComponent } from '@shared/layout/page-shell/page-shell.component';
import { SkeletonComponent } from '@shared/ui/skeleton/skeleton.component';
import { ASYNC } from '@shared/ui/async/async.component';
import { SkeletonComponent } from '@shared/ui/atoms/skeleton/skeleton.component';
import { ASYNC } from '@shared/ui/molecules/async/async.component';
import { RegistrationSummaryComponent } from '@registratie/ui/registration-summary/registration-summary.component';
import { ChangeRequestFormComponent } from '@registratie/ui/change-request-form/change-request-form.component';
import { BigProfileStore } from '@registratie/application/big-profile.store';
@@ -2,9 +2,9 @@ import { Component, input } from '@angular/core';
import { DatePipe } from '@angular/common';
import { Registration } from '@registratie/domain/registration';
import { statusColor, statusLabel } from '@registratie/domain/registration.policy';
import { DataRowComponent } from '@shared/ui/data-row/data-row.component';
import { StatusBadgeComponent } from '@shared/ui/status-badge/status-badge.component';
import { DataBlockComponent } from '@shared/ui/data-block/data-block.component';
import { DataRowComponent } from '@shared/ui/molecules/data-row/data-row.component';
import { StatusBadgeComponent } from '@shared/ui/atoms/status-badge/status-badge.component';
import { DataBlockComponent } from '@shared/ui/molecules/data-block/data-block.component';
/** Organism: registration summary in a CIBG Datablock. Composes data-row rows +
status-badge atom (no card wrapper the datablock carries its own surface). */
@@ -1,5 +1,5 @@
import { Component } from '@angular/core';
import { HeadingComponent } from '@shared/ui/heading/heading.component';
import { HeadingComponent } from '@shared/ui/atoms/heading/heading.component';
import { HerregistratieWizardComponent } from '@herregistratie/ui/herregistratie-wizard/herregistratie-wizard.component';
import { ConceptCardComponent } from './concept-card.component';
import { SNIPPETS } from './snippets.generated';
+2 -2
View File
@@ -1,7 +1,7 @@
import { Component, computed, signal } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { HeadingComponent } from '@shared/ui/heading/heading.component';
import { TextInputComponent } from '@shared/ui/text-input/text-input.component';
import { HeadingComponent } from '@shared/ui/atoms/heading/heading.component';
import { TextInputComponent } from '@shared/ui/atoms/text-input/text-input.component';
import { parsePostcode } from '@registratie/domain/value-objects/postcode';
import { ConceptCardComponent } from './concept-card.component';
import { SNIPPETS } from './snippets.generated';
+3 -3
View File
@@ -1,8 +1,8 @@
import { Component, computed, signal } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { HeadingComponent } from '@shared/ui/heading/heading.component';
import { TextInputComponent } from '@shared/ui/text-input/text-input.component';
import { MaskedValueComponent } from '@shared/ui/masked-value/masked-value.component';
import { HeadingComponent } from '@shared/ui/atoms/heading/heading.component';
import { TextInputComponent } from '@shared/ui/atoms/text-input/text-input.component';
import { MaskedValueComponent } from '@shared/ui/atoms/masked-value/masked-value.component';
import { parseBsn } from '@shared/kernel/bsn';
import { maskBsn } from '@shared/kernel/pii';
import { ConceptCardComponent } from './concept-card.component';
@@ -1,8 +1,8 @@
import { Component } from '@angular/core';
import type { Resource } from '@angular/core';
import { HeadingComponent } from '@shared/ui/heading/heading.component';
import { ASYNC } from '@shared/ui/async/async.component';
import { SkeletonComponent } from '@shared/ui/skeleton/skeleton.component';
import { HeadingComponent } from '@shared/ui/atoms/heading/heading.component';
import { ASYNC } from '@shared/ui/molecules/async/async.component';
import { SkeletonComponent } from '@shared/ui/atoms/skeleton/skeleton.component';
import { ConceptCardComponent } from './concept-card.component';
import { SNIPPETS } from './snippets.generated';
import { highlightTs } from './highlight-ts';
+1 -1
View File
@@ -1,5 +1,5 @@
import { Component } from '@angular/core';
import { HeadingComponent } from '@shared/ui/heading/heading.component';
import { HeadingComponent } from '@shared/ui/atoms/heading/heading.component';
import { RegistrationSummaryComponent } from '@registratie/ui/registration-summary/registration-summary.component';
import { Registration } from '@registratie/domain/registration';
import { ConceptCardComponent } from './concept-card.component';
@@ -1,5 +1,5 @@
import { Component } from '@angular/core';
import { HeadingComponent } from '@shared/ui/heading/heading.component';
import { HeadingComponent } from '@shared/ui/atoms/heading/heading.component';
import { IntakeWizardComponent } from '@herregistratie/ui/intake-wizard/intake-wizard.component';
import { ConceptCardComponent } from './concept-card.component';
import { SNIPPETS } from './snippets.generated';
@@ -0,0 +1,219 @@
# RD-27 — Make the folder equal the layer in `libs/shared/src/ui/`
Status: done
Source: PLAN.md 4a, Phase 4
## Why
CLAUDE.md decision 2 says "folder = layer": `libs/shared/ui` atoms → molecules → organisms.
Today `libs/shared/src/ui/` is 26 flat directories, and the only record of a component's layer
is its story title and a header comment. Nothing stops an atom importing an organism.
This ticket makes the structure say what the rule says. It is a **pure move**: no component
changes, no story title changes, no behaviour. RD-29 then adds the dependency-cruiser rules
that the folders make expressible.
**This is the highest-risk ticket in the arc**, because a broken `.mdx` story import compiles
fine and only fails when Storybook builds. The README says it must not be pushed without
`npm run ci --full`.
## Read first
- `libs/shared/src/ui/` — 25 flat component directories plus `upload/` with 8 of its own.
- `libs/shared/docs/atomic-design.mdx:2-5` — three of the seven `.mdx` story imports that break.
- PLAN.md 4a — the design record, including why `layout/` does not move.
- `.storybook-ssp/main.ts:11-14` — the globs, which are recursive and need no edit.
## Decisions (pre-made, don't relitigate)
1. **Three layer folders, and the full 33-directory mapping. This table is the ticket.**
`libs/shared/src/ui/atoms/` — 12 flat:
```
alert button checkbox heading link masked-value
placeholder-chip radio-group skeleton spinner status-badge text-input
```
`libs/shared/src/ui/molecules/` — 13 flat:
```
application-link application-list async choice-link choice-list confirmation
data-block data-row form-field review-section rich-text-editor stepper task-list
```
`upload/` **keeps its feature subfolder inside each layer** — 8 directories:
| New location | Directories |
| ---------------------- | ----------------------------------------------------------------------------------------------------- |
| `ui/atoms/upload/` | `delivery-channel-toggle`, `document-chip`, `file-input`, `upload-progress-bar`, `upload-status-icon` |
| `ui/molecules/upload/` | `single-upload` |
| `ui/organisms/upload/` | `document-category`, `document-upload` |
`ui/organisms/` holds nothing but `upload/`. That is correct and worth seeing.
The mapping is derived from each component's own story title, not invented — every one of
the 33 already declares its layer as `Design System/<Layer>/…`.
2. **Use `git mv` per directory, so the diff reads as renames.** `git diff --stat -M` must show
renames plus one-line import edits, nothing else.
3. **Rewrite the 28 distinct specifiers, longest key first.** All 28 are listed by the mapping
in decision 1; 25 are flat and 3 are the upload components imported from outside
(`upload/document-upload``organisms/upload/document-upload`, `upload/file-input`
`atoms/upload/file-input`, `upload/single-upload``molecules/upload/single-upload`).
Verified: **no specifier is a prefix of another**, so ordering cannot corrupt a rewrite here.
Do it longest-first anyway — it costs nothing and the property is not guaranteed to hold if
this is ever repeated.
There are 200 occurrences across 73 files. The count of occurrences must not change.
4. **Five of the seven relative imports inside `upload/` become aliases; two stay relative.**
A `../sibling/` import only breaks when the sibling lands in a different layer:
| File (new location) | Import | Becomes |
| ------------------------------------ | ---------------------------- | ------------------------------------------------- |
| `organisms/upload/document-category` | `../delivery-channel-toggle` | `@shared/ui/atoms/upload/delivery-channel-toggle` |
| `organisms/upload/document-category` | `../file-input` | `@shared/ui/atoms/upload/file-input` |
| `organisms/upload/document-category` | `../single-upload` | `@shared/ui/molecules/upload/single-upload` |
| `molecules/upload/single-upload` | `../document-chip` | `@shared/ui/atoms/upload/document-chip` |
| `molecules/upload/single-upload` | `../upload-progress-bar` | `@shared/ui/atoms/upload/upload-progress-bar` |
Unchanged, because both ends stay in the same layer: `atoms/upload/document-chip`
`../upload-status-icon`, and `organisms/upload/document-upload``../document-category`.
**Use the alias, not `../../../`.** Cross-directory imports inside `libs/shared` already use
`@shared/…` (see `wizard-shell.component.ts`), and a three-level relative path is exactly the
thing a later move breaks silently.
5. **Seven `.mdx` story imports break. All seven are real imports, not prose.** The Order table
says eight; measured, it is seven:
```
a11y.mdx:2 ../src/ui/alert/alert.stories -> ui/atoms/alert/…
a11y.mdx:3 ../src/ui/form-field/form-field.stories -> ui/molecules/form-field/…
atomic-design.mdx:2 ../src/ui/button/button.stories -> ui/atoms/button/…
atomic-design.mdx:3 ../src/ui/form-field/form-field.stories -> ui/molecules/form-field/…
atomic-design.mdx:5 ../src/ui/upload/document-upload/… -> ui/organisms/upload/document-upload/…
fp-in-ui.mdx:2 ../src/ui/async/async.stories -> ui/molecules/async/…
remote-data.mdx:2 ../src/ui/async/async.stories -> ui/molecules/async/…
```
`atomic-design.mdx:4` imports `../src/layout/page-shell/…`. **Leave it alone**`layout/`
does not move.
6. **`layout/` does not move, and neither does `libs/beheer/src/ui/`.** CLAUDE.md §5 explicitly
sanctions `libs/shared/layout` holding several layers, and `libs/beheer` is a bounded context
that lives in `libs/` only because two apps share it. Both are settled; do not revisit them.
7. **Change no story title, no layer-tag comment, and no component code.** The titles already
say the right thing, which is what made decision 1's mapping derivable. The two mislabelled
tags (`async` has `/** Convenience: */`, `breadcrumb` says `/** Chrome: */`) and the two
missing ones (`masked-value`, `rich-text-editor`) are **RD-28's** job, not this ticket's.
8. **No barrel file.** The repository has none and does not need one. A barrel would also hide
exactly the layer boundary this ticket exists to expose.
## Files
- 33 directories moved under `libs/shared/src/ui/`
- ~73 files with a one-line specifier edit
- 4 `.mdx` files (7 import lines)
No changes to `angular.json`, `eslint.config.mjs`, `plopfile.mjs`, any `tsconfig.json`,
`.dependency-cruiser.*`, `scripts/check-tokens.sh`, or `e2e/`. Verified: `@shared/*` maps to
`libs/shared/src/*`, so a deeper path resolves unchanged; both Storybook globs are recursive;
dependency-cruiser's `ui-not-infrastructure` pattern is `(/ui/|/layout/)`, which still matches a
nested path; and `check-tokens.sh`'s CIBG-GAP check keys on the **directory basename**, which a
parent-folder move preserves.
## Steps
1. `mkdir` the three layer folders, then `git mv` all 33 directories per decision 1.
2. Rewrite the 28 specifiers across `apps/` and `libs/` (decision 3).
3. Fix the five relative imports inside `upload/` (decision 4).
4. Fix the seven `.mdx` imports (decision 5).
5. `npm run typecheck` — four tsconfigs, and the fastest way to catch a missed specifier.
6. `git add -A`, then run the acceptance commands.
7. Update this ticket's `Status:` to `done` and the README's RD-27 row to `done`.
8. Commit all of it together.
## Acceptance criteria
Measured against the tree before handover. Run after `git add -A`.
The structure is three folders, and every directory landed:
```bash
ls -d libs/shared/src/ui/*/ | wc -l # is 26 -> MUST be 3
ls -d libs/shared/src/ui/atoms/*/ | wc -l # MUST be 13 (12 + upload)
ls -d libs/shared/src/ui/atoms/upload/*/ | wc -l # MUST be 5
ls -d libs/shared/src/ui/molecules/*/ | wc -l # MUST be 14 (13 + upload)
ls -d libs/shared/src/ui/molecules/upload/*/ | wc -l # MUST be 1
ls -d libs/shared/src/ui/organisms/*/ | wc -l # MUST be 1 (upload)
ls -d libs/shared/src/ui/organisms/upload/*/ | wc -l # MUST be 2
```
**The single strongest check in this ticket** — every specifier now names a layer, so only three
distinct values may remain:
```bash
git grep -ho "@shared/ui/[a-z0-9-]*" -- apps libs | sort -u # is 26 values -> MUST be exactly these 3:
# @shared/ui/atoms
# @shared/ui/molecules
# @shared/ui/organisms
```
Nothing was lost or duplicated in the rewrite:
```bash
git grep -ho "@shared/ui/" -- apps libs | wc -l # is 200 -> MUST still be 200
git grep -c "src/ui/" -- '*.mdx' | awk -F: '{s+=$NF} END {print s+0}' # is 7 -> MUST still be 7
git grep -c "from '\.\./" -- libs/shared/src/ui/ | awk -F: '{s+=$NF} END {print s+0}' # is 7 -> MUST be 2
```
The move reads as a move (decision 2):
```bash
git diff --cached --stat -M | tail -1 # inspect: renames + one-line edits, no rewritten files
```
```bash
npm run ci --full # exits 0 — REQUIRED, see Verification
```
## Verification
**`npm run ci --full` is mandatory and non-negotiable for this ticket.** Plain `npm run ci` does
not build Storybook, and a `.mdx` importing a moved story path is invisible to the type-checker,
the linter and every unit test. It fails only when `build-storybook` runs. The README calls out
RD-27 by name for this reason.
Run `npm run typecheck` first anyway (step 5): it covers four tsconfigs and catches a missed
`@shared/ui/*` specifier in seconds rather than at the end of a full gate.
`libs/shared/docs/layers.mdx` deep-links two story ids
(`design-system-molecules-application-link--navigatie`,
`domein-registratie-aanvraag-block--concept`). Story ids derive from **titles**, and decision 7
changes no title, so both links survive. Do not "fix" them.
## Out of scope
- The dependency-cruiser ladder rules. RD-29 adds them, and it depends on this ticket.
- Layer-tag comments and the `libs/beheer` title rule — RD-28.
- `layout/` (decision 6).
- Any component's code, template, styles or story title.
## Risks
- **A broken `.mdx` import is invisible until Storybook builds.** This is the whole reason the
ticket carries `--full`. Decision 5 lists all seven; check each one after the move.
- **`git mv`, not `mv` + `git add`.** Both produce the same tree, but only the first keeps the
diff readable as renames — and this diff is 33 directories wide.
- **Do not flatten `upload/`** (decision 1). Its subfolder survives inside each layer.
- **Do not reach for `../../../`** (decision 4). Use the alias.
- **The occurrence count is the tripwire for a bad `sed`.** 200 before, 200 after. A rewrite
that accidentally matches twice, or drops a line, moves that number.
- **`atomic-design.mdx` has four story imports and only three of them move.** The fourth is
`page-shell`, in `layout/`.
+1 -1
View File
@@ -121,7 +121,7 @@ two. Note that RD-15 exists because 22 abandoned agent worktrees are still on di
| RD-24 | `concepts.page` to 6 sections + `concept-card` + globals + code tokens | 02 | yes | done |
| RD-25 | `org-template-editor` to `sample-letter.ts` + labels + 2 children | 02 | yes | done |
| RD-26 | `letter-canvas`: inline the labels + `letter-line`; keep one disable | 02 | yes | done |
| RD-27 | **The layer move:** 33 `git mv` + 28 specifiers + 8 MDX imports | 21 | yes | todo |
| RD-27 | **The layer move:** 33 `git mv` + 28 specifiers + 8 MDX imports | 21 | yes | done |
| RD-28 | Layer-tag fixes + the `libs/beheer` title rule | 27 | | todo |
| RD-29 | The 3 atomic-ladder rules in dependency-cruiser | 27 | | todo |
| RD-30 | Archive the finished backlogs (16,300 lines) + an archive README | 01 | | todo |
+3 -3
View File
@@ -1,9 +1,9 @@
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 { AlertComponent } from '@shared/ui/atoms/alert/alert.component';
import { ButtonComponent } from '@shared/ui/atoms/button/button.component';
import { ASYNC } from '@shared/ui/molecules/async/async.component';
import { AccessStore } from '@shared/application/access.store';
import { successOr } from '@shared/application/remote-data';
import { AuditStore } from '@beheer/application/audit.store';
+3 -3
View File
@@ -1,8 +1,8 @@
import { Component, computed, inject, signal } from '@angular/core';
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 { AlertComponent } from '@shared/ui/atoms/alert/alert.component';
import { ButtonComponent } from '@shared/ui/atoms/button/button.component';
import { ASYNC } from '@shared/ui/molecules/async/async.component';
import { AccessStore } from '@shared/application/access.store';
import { FeatureFlagStore } from '@shared/application/feature-flags.store';
@@ -1,5 +1,5 @@
import { Component, computed, input, output } from '@angular/core';
import { ButtonComponent } from '@shared/ui/button/button.component';
import { ButtonComponent } from '@shared/ui/atoms/button/button.component';
import { ChangeCounts, StamColumn, StamRow, StamTable, activeOn } from '@beheer/domain/stamdata';
interface DisplayRow {
+3 -3
View File
@@ -1,8 +1,8 @@
import { Component, computed, effect, inject } from '@angular/core';
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 { AlertComponent } from '@shared/ui/atoms/alert/alert.component';
import { ButtonComponent } from '@shared/ui/atoms/button/button.component';
import { ASYNC } from '@shared/ui/molecules/async/async.component';
import { AccessStore } from '@shared/application/access.store';
import { StamdataStore } from '@beheer/application/stamdata.store';
import { StamdataTableEditorComponent } from '@beheer/ui/stamdata-table-editor/stamdata-table-editor.component';
+2 -2
View File
@@ -1,6 +1,6 @@
import { Meta, Canvas } from '@storybook/addon-docs/blocks';
import * as AlertStories from '../src/ui/alert/alert.stories';
import * as FormFieldStories from '../src/ui/form-field/form-field.stories';
import * as AlertStories from '../src/ui/atoms/alert/alert.stories';
import * as FormFieldStories from '../src/ui/molecules/form-field/form-field.stories';
<Meta title="Foundations/Accessibility" />
+3 -3
View File
@@ -1,8 +1,8 @@
import { Meta, Canvas } from '@storybook/addon-docs/blocks';
import * as ButtonStories from '../src/ui/button/button.stories';
import * as FormFieldStories from '../src/ui/form-field/form-field.stories';
import * as ButtonStories from '../src/ui/atoms/button/button.stories';
import * as FormFieldStories from '../src/ui/molecules/form-field/form-field.stories';
import * as PageShellStories from '../src/layout/page-shell/page-shell.stories';
import * as DocumentUploadStories from '../src/ui/upload/document-upload/document-upload.stories';
import * as DocumentUploadStories from '../src/ui/organisms/upload/document-upload/document-upload.stories';
<Meta title="Foundations/Atomic Design" />
+1 -1
View File
@@ -1,5 +1,5 @@
import { Meta, Canvas } from '@storybook/addon-docs/blocks';
import * as AsyncStories from '../src/ui/async/async.stories';
import * as AsyncStories from '../src/ui/molecules/async/async.stories';
<Meta title="Foundations/FP in the UI" />
+1 -1
View File
@@ -1,5 +1,5 @@
import { Meta, Canvas } from '@storybook/addon-docs/blocks';
import * as AsyncStories from '../src/ui/async/async.stories';
import * as AsyncStories from '../src/ui/molecules/async/async.stories';
<Meta title="Foundations/RemoteData & Async" />
@@ -1,6 +1,6 @@
import { Component, input } from '@angular/core';
import { HeadingComponent } from '@shared/ui/heading/heading.component';
import { LinkComponent } from '@shared/ui/link/link.component';
import { HeadingComponent } from '@shared/ui/atoms/heading/heading.component';
import { LinkComponent } from '@shared/ui/atoms/link/link.component';
/** Template: standard page body optional back-link, a heading, optional intro,
and projected content. The breadcrumb lives in the site header (blue bar), so
@@ -2,7 +2,7 @@ import type { Meta, StoryObj } from '@storybook/angular';
import { applicationConfig, moduleMetadata } from '@storybook/angular';
import { provideRouter } from '@angular/router';
import { PageShellComponent } from './page-shell.component';
import { ButtonComponent } from '@shared/ui/button/button.component';
import { ButtonComponent } from '@shared/ui/atoms/button/button.component';
const meta: Meta<PageShellComponent> = {
title: 'Design System/Templates/PageShell',
@@ -9,10 +9,10 @@ import {
viewChild,
} from '@angular/core';
import { FormsModule } from '@angular/forms';
import { ButtonComponent } from '@shared/ui/button/button.component';
import { AlertComponent } from '@shared/ui/alert/alert.component';
import { SpinnerComponent } from '@shared/ui/spinner/spinner.component';
import { StepperComponent } from '@shared/ui/stepper/stepper.component';
import { ButtonComponent } from '@shared/ui/atoms/button/button.component';
import { AlertComponent } from '@shared/ui/atoms/alert/alert.component';
import { SpinnerComponent } from '@shared/ui/atoms/spinner/spinner.component';
import { StepperComponent } from '@shared/ui/molecules/stepper/stepper.component';
import { whenTag } from '@shared/kernel/fp';
/** CIBG procesnavigatie primary-button copy for a non-final step: "Naar stap 2 - Werk".
@@ -1,5 +1,5 @@
import { Component, computed, input, output } from '@angular/core';
import { ButtonComponent } from '@shared/ui/button/button.component';
import { ButtonComponent } from '@shared/ui/atoms/button/button.component';
/**
* Atom: a possibly-masked sensitive value (BSN, BIG-nummer, ) with an optional, audited

Some files were not shown because too many files have changed in this diff Show More