Files
atomic-design-poc/src/app/shared/ui/upload/document-upload/document-upload.component.ts
Edwin van den Houdt 9d58f597ea feat(fp): WP-13 — CIBG-gap register + hygiene + MDX
Mark every hand-rolled shared/ui surface with a `// CIBG-GAP EXTENSION:`
comment + `cibgGap` story parameter (skeleton, spinner, rich-text-editor,
wizard-shell's error summary, application-link's non-navigating row,
debug-state, status-badge, card, placeholder-chip) so deviations from the
CIBG design system are auditable. Add the register MDX
(Foundations/CIBG Gap Register), cross-linked from ADR-0003. Delete the
near-identity upload-status-banner wrapper; its one consumer now uses
<app-alert> directly (a story added to keep the info-banner state covered).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-03 22:42:13 +02:00

65 lines
2.6 KiB
TypeScript

import { Component, input, output } from '@angular/core';
import type { DeliveryChannel, UploadState } from '@shared/upload/upload.machine';
import { AlertComponent } from '@shared/ui/alert/alert.component';
import { DocumentCategoryComponent } from '../document-category/document-category.component';
/** Organism: the full document-upload step — the category list, or a load-error
banner. Pure UI: re-exposes the category events, tagging each with its category
where the parent needs it. The container wires these to the upload reducer. */
@Component({
selector: 'app-document-upload',
imports: [DocumentCategoryComponent, AlertComponent],
styles: [
`
:host {
display: flex;
flex-direction: column;
gap: var(--rhc-space-max-xl);
}
`,
],
template: `
@if (state().categoriesError) {
<app-alert type="error">{{ state().categoriesError }}</app-alert>
} @else {
@if (state().backgroundSyncAvailable === false && state().categories.length > 0) {
<app-alert type="info">{{ foregroundOnlyMessage }}</app-alert>
}
@for (c of state().categories; track c.categoryId) {
<app-document-category
[category]="c"
[uploads]="uploadsFor(c.categoryId)"
[channel]="channelFor(c.categoryId)"
[rejection]="state().rejections[c.categoryId]"
[previewUrlFor]="previewUrlFor()"
(fileSelected)="fileSelected.emit({ categoryId: c.categoryId, files: $event })"
(removeUpload)="removeUpload.emit($event)"
(retryUpload)="retryUpload.emit($event)"
(deleteUpload)="deleteUpload.emit($event)"
(channelChange)="channelChange.emit({ categoryId: c.categoryId, channel: $event })"
/>
}
}
`,
})
export class DocumentUploadComponent {
state = input.required<UploadState>();
/** Optional: builds a preview/download URL for a completed upload's documentId. */
previewUrlFor = input<(documentId: string) => string | undefined>();
fileSelected = output<{ categoryId: string; files: File[] }>();
removeUpload = output<string>();
retryUpload = output<string>();
deleteUpload = output<{ localId: string; documentId: string }>();
channelChange = output<{ categoryId: string; channel: DeliveryChannel }>();
protected readonly foregroundOnlyMessage = $localize`:@@upload.foregroundOnly:Uploads gaan alleen door zolang deze pagina open blijft.`;
protected uploadsFor(categoryId: string) {
return this.state().uploads.filter((u) => u.categoryId === categoryId);
}
protected channelFor(categoryId: string): DeliveryChannel {
return this.state().deliveryChannel[categoryId] ?? 'digital';
}
}