Component Catalog — kdx-* Library
Component Catalog — kdx-* Library
Sección titulada «Component Catalog — kdx-* Library»Single source of truth for every reusable presentational component in the frontend. This is the documentation counterpart of the live showcase (/showcase) and the design-system reference in [[design-system|stack/design-system]]. Naming rules come from [[adr-012-a-custom-component-naming|ADR-012-a]]; the build standard from the kdx-angular-component skill; testing from [[adr-009-a-frontend-testing-strategy|ADR-009-a]].
[!info] Goal This project aims to be 100% components: feature templates only compose components + layout utilities, never inline markup. Prefer one configurable component over many near-duplicates. Every component is pinned here with a unique Obsidian tag.
Identity rule — tag = selector = directory
Sección titulada «Identity rule — tag = selector = directory»Each component has one unique identity used three ways, always the same string:
| Facet | Value (example) |
|---|---|
| Obsidian tag | #kdx-coverage-bar |
| Angular selector | <kdx-coverage-bar> |
| Directory + file | shared/components/coverage-bar/coverage-bar.component.ts |
The Obsidian tag is the selector with a leading #. The directory is the selector minus the kdx-/app- prefix. Filter the Obsidian tag pane (or search tag:#kdx-coverage-bar) to locate any component, what it does, and where it lives. When adding a component, the tag, selector, and directory must agree — this is what makes the identity unambiguous across code, docs, and graph.
- Tag — the unique Obsidian identifier (= selector).
- Class — exported TypeScript class.
- Inputs —
name= optional,name*=input.required,name⇄= two-waymodel(),↑name=output(). Legacy@Input()flagged inline. - Spec — ✅ has
*.spec.ts, ⬜ missing (tracked for backfill, [[adr-009-a-frontend-testing-strategy|ADR-009-a]]). - All 32 components are
standalone: true+ChangeDetectionStrategy.OnPush. Only deviations are noted.
People & Roster
Sección titulada «People & Roster»| Tag | Class | What it does | Inputs | Spec |
|---|---|---|---|---|
#kdx-avatar | KdxAvatarComponent | Avatar with initials + deterministic color from a seed, or image/icon, with optional status badge; configurable size/shape/variant ([[adr-014-user-entity | ADR-014]]) | image icon label colorSeed size shape variant borderWidth badge |
#kdx-employee-card | EmployeeCardComponent | Employee as card or compact row: avatar, tags, hours, occupancy bar; dual-mode | employee* mode subtitle positionName hoursData tags ↑cardClick | ✅ |
#kdx-employee-list | EmployeeListComponent | Lays out employee cards as grid or list per viewMode | items* viewMode ↑cardClick | ⬜ |
#kdx-worker-row | KdxWorkerRowComponent | Configurable employee row — mode='worker': coverage bar + reinforcement ([[adr-024-employee-bar-semantics-and-contract-matching | ADR-024]]); mode='assignment': status border + status tag + FSM ↑suspend/↑resume/↑end + [actions] slot | mode initials* firstName* lastName* positionTitle* assignedHours* requiredHours* coveragePct* effectiveHours statusLabel statusSeverity isReinforcement ↑rowClick |
#kdx-coverage-row | KdxCoverageRowComponent | Org-unit row: avatar stack, hours, coverage % bar | unitName* assignedHours* requiredHours* coveragePct* workers vacantCount ↑rowClick | ⬜ |
#kdx-detail-profile | KdxDetailProfileComponent | Profile header: XL avatar + name + subtitle + badge | name* image subtitle badge colorSeed | ✅ |
Coverage, Hours & Metrics
Sección titulada «Coverage, Hours & Metrics»| Tag | Class | What it does | Inputs | Spec |
|---|---|---|---|---|
#kdx-coverage-bar | CoverageBarComponent | Segmented coverage bar (green/yellow/blue/red) or single-color legacy bar ([[adr-001-c-hour-color-vocabulary | ADR-001-c]]) | value breakdown |
#kdx-stat | KdxStatComponent | Single metric atom (variant hero/compact/inline): label + value + unit + icon + delta + tone; composed by stat-cards, kpi-strip and the dashboard hero (no separate kpi-card) | variant label* value* unit icon delta tone footnote … | ✅ |
#kdx-kpi-strip | KpiStripComponent | Horizontal strip of KPI metrics (composes #kdx-stat inline) | items | ✅ |
#kdx-stat-cards | StatCardsComponent | Grid of stat cards (composes #kdx-stat compact) | cards | ✅ |
Org Structure
Sección titulada «Org Structure»| Tag | Class | What it does | Inputs | Spec |
|---|---|---|---|---|
#kdx-org-node | OrgNodeComponent | Org-unit/position card: avatars, title, metrics, coverage bar, vacancy indicator | data* selected ↑nodeClick | ✅ |
#kdx-position-row | KdxPositionRowComponent | Position row with variant styling (vacant/contract/temp/reinforcement) + metrics | title* requiredHours* assignedHours coveragePct variant expanded ↑rowClick | ⬜ |
| Tag | Class | What it does | Inputs | Spec |
|---|---|---|---|---|
#kdx-tag | KdxTagComponent | Unified configurable tag: variant='category' → category-colored chip; variant='requirement' → position-requirement pill (dims when optional). Replaces the former cv-tag + emp-tag (both deleted) | variant label value category mandatory tooltip removable ↑remove | ✅ |
Search, Filters & Headers
Sección titulada «Search, Filters & Headers»| Tag | Class | What it does | Inputs | Spec |
|---|---|---|---|---|
#kdx-list-header | ListHeaderComponent | Page header: title + subtitle + search + view-mode toggles | title* subtitle searchPlaceholder searchTerm⇄ viewOptions viewMode⇄ | ⬜ |
#kdx-search-bar | SearchBarComponent | Search input + icon + optional view-mode buttons | placeholder searchTerm⇄ viewOptions viewMode⇄ | ⬜ |
#kdx-filter-bar | FilterBarComponent | Composite: filter pills + tag drawer | pills* selected⇄ tags selectedTags⇄ | ⬜ |
#kdx-filter-pills | FilterPillsComponent | Icon+count pill buttons, single-select | pills* selected⇄ | ⬜ |
#kdx-tag-filter-pills | TagFilterPillsComponent | Section-based multi-select tag pills + popover | sections* selected⇄ | ⬜ |
#kdx-tag-filter-drawer | TagFilterDrawerComponent | Drawer of tag filters grouped by category + apply/clear | tags* selected⇄ | ⬜ |
Detail Panel
Sección titulada «Detail Panel»| Tag | Class | What it does | Inputs | Spec |
|---|---|---|---|---|
#kdx-detail-drawer | KdxDetailDrawerComponent | Right-side drawer with header slot; visible model + width | visible⇄ header width | ✅ |
#kdx-detail-section | KdxDetailSectionComponent | Section container: icon + title + projected body | icon title* | ✅ |
#kdx-detail-field | KdxDetailFieldComponent | Label + content pair (uppercase label) | label* | ✅ |
#kdx-detail-actions | KdxDetailActionsComponent | Footer container for action buttons (divider + gap) | (projection only) | ✅ |
Resource & Feedback States
Sección titulada «Resource & Feedback States»| Tag | Class | What it does | Inputs | Spec |
|---|---|---|---|---|
#kdx-resource-state | ResourceStateComponent | Smart wrapper: switches loading/error/empty/content from a status signal (composes the three leaf states) | status hasValue errorMessage loadingMessage emptyIcon emptyHeading emptyMessage ↑retry | ✅ |
#kdx-loading-state | LoadingStateComponent | Centered spinner + sr-only message (role=status) | message | ✅ |
#kdx-error-state | ErrorStateComponent | Centered error message + retry button | message retryLabel ↑retry | ✅ |
#kdx-empty-state | EmptyStateComponent | Centered icon + heading + message; CTA projection | icon heading message | ⬜ |
Audit & Timeline
Sección titulada «Audit & Timeline»| Tag | Class | What it does | Inputs | Spec |
|---|---|---|---|---|
#kdx-transition-timeline | KdxTransitionTimeline | Employee FSM status-transition timeline: actor avatar, verb, dates, reasons | events | ⬜ |
Theming & Help
Sección titulada «Theming & Help»| Tag | Class | What it does | Inputs | Spec |
|---|---|---|---|---|
#kdx-theme-setter | KdxThemeSetterComponent | Control panel: dark-mode toggle, preset, primary + surface swatches (drives LayoutService) | (service-driven) | ✅ |
#kdx-help-tooltip | HelpTooltipComponent | Icon button → dialog rendering markdown help loaded from public/*.md | src title | ✅ |
Layout & Shell (app-*)
Sección titulada «Layout & Shell (app-*)»These are shell/layout components, not design-system primitives, so per [[adr-012-a-custom-component-naming|ADR-012-a]] they intentionally keep the app-* prefix.
| Tag | Class | What it does | Inputs | Spec |
|---|---|---|---|---|
#app-page-container | PageContainer | Max-width card wrapper: heading + optional badge + projected content | heading badge badgeSeverity | ✅ |
#app-cookie-consent | CookieConsentComponent | Fixed-bottom consent banner (accept/decline → cookie); SSR-safe | (none) | ✅ |
Staffing (Coveris-e import)
Sección titulada «Staffing (Coveris-e import)»Imported from Coveris-e and adapted to the -main standard, decoupled from Coveris-e models via shared/components/staffing/staffing.types.ts. Showcased at /showcase/staffing. Built ahead of migration so the pieces are ready when scheduling lands — visible in the library even though no feature consumes them yet.
| Tag | Class | What it does | Inputs | Spec |
|---|---|---|---|---|
#kdx-beta-badge | KdxBetaBadgeComponent | BETA/ALPHA chip | label size | ⬜ |
#kdx-employee-capacity-bar | KdxEmployeeCapacityBarComponent | Capacity bar by hours-source (cargo/prod/prestación) + soft/hard cap | capacity* | ✅ |
#kdx-employee-chip | KdxEmployeeChipComponent | Draggable candidate card: avatar, regime, capacity bar | row* dimmed ↑select | ⬜ |
#kdx-assigned-worker-card | KdxAssignedWorkerCardComponent | Assigned-worker micro-card: hours, share %, recurrence, actions | worker* requiredHours ↑onDragStart ↑changeRequested ↑viewProfileRequested ↑endRequested | ⬜ |
#kdx-position-card | KdxPositionCardComponent | Position card: hours, source-split bar, required tags, worker stack; drop target | data* ↑workerClick ↑employeeDropped ↑assignedDropped ↑endRequested ↑changeAssignment ↑viewProfile ↑hovered | ✅ |
#kdx-position-day-grid | KdxPositionDayGridComponent | Positions grouped Mon–Sun + “sin día” | positions* (forwards position-card outputs) | ⬜ |
#kdx-employee-sidebar | KdxEmployeeSidebarComponent | Eligible-candidate rail with search + cargo filters (decoupled — pure inputs) | rows loading error highlightedPositionId ↑select | ⬜ |
#kdx-assign-drop-popup | KdxAssignDropPopupComponent | Confirm popup after drop: hours stepper, vehicle, recurrence | visible ctx ↑visibleChange ↑confirm ↑cancel | ✅ |
Scheduling (Coveris-e extract)
Sección titulada «Scheduling (Coveris-e extract)»Extracted fresh from Coveris-e’s 2007-line scheduling-month monolith and rebuilt to the -main standard, decoupled via shared/components/scheduling/scheduling.types.ts (which also holds the lifted pure logic computeCoverageStrip + isoWeek). Showcased at /showcase/scheduling.
| Tag | Class | What it does | Inputs | Spec |
|---|---|---|---|---|
#kdx-focus-header | FocusHeaderComponent | Month org-unit header: back breadcrumb, ancestor path, focus/staffing toggles, PDF, publish | orgUnitName* path focusMode staffingOpen published publishing pdfDisabled publishDisabled ↑back ↑navigateUnit ↑toggleFocus ↑toggleStaffing ↑downloadPdf ↑publish | ⬜ |
#kdx-coverage-strip | CoverageStripComponent | Aggregate month coverage: hours balance + delta, per-ISO-week cards (tone + gaps), hours-source legend | coverage* | ✅ |
#kdx-scheduling-toolbar | SchedulingToolbarComponent | Grid view controls: layout/zoom/cell-rep segmented groups, search, gap-jump chip | layout⇄ zoom⇄ cellRep⇄ search⇄ gapCount ↑jumpToGap | ✅ |
Page Pieces (Wave 4 — native extraction)
Sección titulada «Page Pieces (Wave 4 — native extraction)»Extracted from -main’s own feature pages (roster-detail, dashboard, hours) so those templates hold components + layout only. Showcased at /showcase/page-pieces. (The metric atom #kdx-stat lives under Coverage, Hours & Metrics above.)
| Tag | Class | What it does | Inputs | Spec |
|---|---|---|---|---|
#kdx-card-section | KdxCardSectionComponent | p-card + icon/title header + count badge + projected body | title* icon count cardClass | ⬜ |
#kdx-tag-grid | KdxTagGridComponent | Category-grouped tag-chip columns (contracts/quals/certs) | columns* removable ↑remove | ✅ |
#kdx-hours-balance-card | KdxHoursBalanceCardComponent | Hours balance: base + adjustments → effective, consumption bar, state chip | balance* | ✅ |
#kdx-module-grid | KdxModuleGridComponent | Nav tiles (icon badge + label + desc + chevron) | links* | ⬜ |
#kdx-alert-list | KdxAlertListComponent | Severity-iconed alert list + empty state | alerts max emptyMessage | ✅ |
#kdx-data-table | KdxDataTableComponent | p-table wrapper baking ADR-013 a11y (aria-label, scope=col), striped, empty/loading, sortable, projected #body | value ariaLabel* columns* loading rows paginator dataKey | ✅ |
Wave-4 design notes:
#kdx-assignment-rowwas implemented as a configurable#kdx-worker-rowvariant (mode='assignment'→ status border + FSM↑suspend/↑resume/↑end+[actions]slot; worker-mode untouched), honoring “configurable over many.”was dropped —#kdx-org-tree-tablep-treeTable+p-treeTableTogglerresolve via Angular DI and cannot be cleanly content-projected into a wrapper (NG0201). The ADR-013 a11y wins (aria-label+scope=col) were applied inline tostructureinstead.
Showcase / Component Library
Sección titulada «Showcase / Component Library»Every kdx-* component is validated in the showcase before use in a feature (showcase-before-use, [[adr-012-a-custom-component-naming|ADR-012-a]]). Routes are defined in frontend/src/app/showcase/showcase.routes.ts and mounted at /showcase, inside the authenticated AppLayout shell. Index pages group the catalog:
/showcase/custom— all customkdx-*primitives/showcase/group— composite/grouped contexts (roster cards, position rows, org node, detail panel)/showcase/primeng— PrimeNG reference gallery (colors, typography, buttons, forms, data, feedback, layout, overlays, menus, badges, tags)
Status: the showcase is now a first-class, web-visible Component Library — overview.component.ts is data-driven with per-category count badges and the Control Panel carries a prominent “Component Library” tile → /showcase. Category indexes: /showcase/staffing (8, live), /showcase/scheduling (3, live), /showcase/page-pieces (placeholder, filled by Wave 4).
Standardization backlog (-main)
Sección titulada «Standardization backlog (-main)»Tracked deviations from the one-standard goal, fixed during this program:
✅ Done (Wave 1) —#cv-tag→ signals + consolidation.cv-tag+emp-tagconsolidated into the single configurable#kdx-tag(shared/components/tag/); both legacy components deleted, all consumers updated, spec added.13 missing specs.✅ Done (Wave 2) — all 13 backfilled (logic-bearing with behavior assertions; baseline also repaired — 8 specs that blocked compilation). Full suite 64 files / 763 passing / 0 failing. The per-row Spec ✅/⬜ columns above are reconciled in the final gate pass.- State leaves — confirm
#kdx-resource-stateis the single configurable wrapper and the three leaf states stay pure presentational. app-*carve-out —#app-page-container/#app-cookie-consentstayapp-*by ADR-012-a; documented here so it is not re-litigated.
Known defects (found by audit — fix during the program)
Sección titulada «Known defects (found by audit — fix during the program)»ADR-022 violation — inline mock in a live page.✅ Fixed (#9, Wave 4) — the dashboard KPI is now a livestaffing()computed from coverage-summaryemployee_breakdown; the alerts card shows an empty state (no endpoint yet), examples moved toshared/mocks/dashboard-mocks.ts; theno-mock-importsguard now also flags inline// mock|fake|hardcoded|dummymarkers (verified both ways, 34/34).- Raw Tailwind colors —
shared/components/stat-cards/stat-cards.component.tsL16–21 (gradient utilitiesfrom-cyan-700 to-cyan-900, …);roster/components/assignment-dialog.component.tsL84,88,93 (text-red-500,border-red-200). Replace with@themetokens /var(--p-*)(ADR-012-c). A catalog component must not break the rule the program enforces. ::ng-deepwithout justification —transition-timeline.component.tsL70–82 (×5) andhelp-tooltip.component.tsL52: add a one-line ADR-012 justification comment, or move PrimeNG token overrides intoKdxPreset.- Smart component in the catalog —
help-tooltip.component.tsinjectsHttpClient; re-classify it as help-infrastructure (not akdx-*primitive) or invert the dependency (content viainput()). - a11y debt to bake into primitives —
hours.component.tstable (≈L146–164) lacksscope="col"+aria-label;structure.component.tsp-treeTable(≈L189) lacksaria-label.#kdx-data-table/#kdx-org-tree-tablemust bake these in, withhours/structureas the proving migration targets. - Residual hardcoded
#fff—transition-timeline.component.tstemplate L99 (system-actorp-avatar[style]) and TS L176 (avatarStyle()) use literal white →var(--p-surface-0). Minor; sweep in a later wave or at the gate.
Roadmap — planned components
Sección titulada «Roadmap — planned components»[!warning] Planned, not yet built The entries below are scheduled for this program and are not yet in the codebase. They appear here so the catalog is the complete forward index. Each becomes a real row (with Spec status) once built and showcased.
Ordering (corrected by audit): the page-piece primitives in §D are not Coveris-e-only — the same inline markup already lives in
-main(roster-detail,dashboard). Build them early, from-main, where they pay off immediately and de-risk the later Coveris-e imports. Only the genuinely Coveris-e-specific shapes (scheduling chrome §C, drag-drop) come last.
A · Proposed generic primitives (reduce inline markup in both projects)
Sección titulada «A · Proposed generic primitives (reduce inline markup in both projects)»| Planned tag | Purpose |
|---|---|
#kdx-tag | Configurable tag merging cv-tag + emp-tag (variant/mandatory/tooltip/removable) |
#kdx-stat | Single metrics atom (label + value + delta + tone + icon). stat-cards, kpi-strip, and the dashboard hero cards all compose it — do not ship a separate kdx-kpi-card sibling (that recreates the cv-tag/emp-tag split) |
#kdx-card-section | p-card + icon/title header + optional count badge. Highest-frequency unextracted pattern (6× in roster-detail alone) |
#kdx-data-table | p-table wrapper enforcing ADR-013 a11y (aria-label, scope=col), striped, empty/loading/sort slots. Proving targets: hours, structure |
#kdx-icon-action-bar | Configurable cluster of [rounded][outlined] size=small icon buttons + tooltips for row actions (edit/delete/suspend/…); centralizes the danger/disabled convention |
#kdx-drawer-footer | Sticky cancel/confirm footer for drawers |
#kdx-confirm-popup | Consistent danger/confirm wrapper for destructive actions |
#kdx-section | Semantic <section> + heading using the typography scale ([[adr-012-b-typography |
#kdx-page-header | Dropped — the 4 candidate sites differ structurally; migrate hours to the existing #kdx-list-header instead of building a new header |
B · Coveris-e imports — clearly new components (import & adapt) ✅ DONE (Wave 2)
Sección titulada «B · Coveris-e imports — clearly new components (import & adapt) ✅ DONE (Wave 2)»All 8 imported, decoupled (staffing/staffing.types.ts), and showcased at /showcase/staffing — see the Staffing category above for the live rows. Source: /Dev/Coveris/cotton-coveris-mvp (Coveris-e).
| Planned tag | Purpose | Coveris-e source |
|---|---|---|
#kdx-beta-badge | BETA/ALPHA chip | shared/components/beta-badge/ |
#kdx-employee-capacity-bar | Employee capacity/utilization bar | shared/components/staffing/employee-capacity-bar.component.ts |
#kdx-employee-chip | Draggable compact employee card (avatar + name + capacity) | staffing/employee-chip.component.ts |
#kdx-assigned-worker-card | Card for a worker already assigned to a slot | staffing/assigned-worker-card.component.ts |
#kdx-position-card | Position summary card (requirements + coverage) | staffing/position-card.component.ts |
#kdx-employee-sidebar | Sidebar list of eligible employees | staffing/employee-sidebar.component.ts |
#kdx-position-day-grid | Per-day grid of position slots | staffing/position-day-grid.component.ts |
#kdx-assign-drop-popup | Drag-drop assignment confirmation popup | staffing/assign-drop-popup.component.ts |
C · Coveris-e scheduling chrome (extract & build) ✅ DONE (Wave 3)
Sección titulada «C · Coveris-e scheduling chrome (extract & build) ✅ DONE (Wave 3)»All 3 extracted from scheduling-month.component.ts, decoupled (scheduling/scheduling.types.ts + lifted computeCoverageStrip/isoWeek), and showcased at /showcase/scheduling — see the Scheduling category above for the live rows.
D · Page-piece components (extract & build — build EARLY from -main)
Sección titulada «D · Page-piece components (extract & build — build EARLY from -main)»Extracted from inline-heavy pages; each carries a header comment citing its source file + line range. These already exist in -main today (roster-detail, dashboard, hours) — build them from -main first, then reconcile any Coveris-e-specific variant during the import wave. Reuse existing components where noted instead of building new.
| Planned tag | Purpose | Build note |
|---|---|---|
#kdx-tag-grid | Grouped tag display (contracts/quals/certs) | from roster-detail ≈L340–406 (3 copy-paste columns); logic-bearing → spec |
#kdx-hours-balance-card | Contract + hours-balance card | from roster-detail ≈L447–499; logic-bearing → spec |
#kdx-module-grid | Tile grid of module/nav entries | confirmed 2× duplicate: dashboard ≈L256–281 + control-panel ≈L47–75 |
#kdx-alert-list | Severity-tagged alert list | dashboard ≈L241–251 (move its mock into a showcase fixture) |
#kdx-assignment-row | Assignment row + action buttons | variant of #kdx-worker-row (mode/actions), not a 4th row sibling — roster-detail ≈L276–321 |
#kdx-employee-header | Profile header | reuse/extend existing #kdx-detail-profile — do not build a 3rd profile header |
| (info grid) | label+value pairs | reuse existing #kdx-detail-field — roster-detail ≈L412–445, 578–616 |
#kdx-hours-table-row | One row of the hours ledger table | from hours.component.ts; pair with #kdx-data-table + a11y |
#kdx-org-tree-table | Tree-table wrapper for org structure | from org-chart-detail / structure; bake aria-label |
#kdx-kpi-card | dashboard hero cards | composes #kdx-stat (not a sibling to stat-cards) — dashboard ≈L63–168 |
Maintenance rules
Sección titulada «Maintenance rules»- Adding a component — create
shared/components/<name>/, build per thekdx-angular-componentskill (standalone, OnPush, signal I/O,inject(),@if/@for, PrimeNG-first, no raw Tailwind colors), add a showcase page, add a*.spec.tswhere logic-bearing, and add a row here. Tag = selector = directory. - One configurable component over many — before adding, check whether an existing component can absorb the case via an input.
- The catalog is the SSOT — a
kdx-*/app-*component that is not listed here is a gap; a row whose tag, selector, or directory disagree is a bug.