Ir al contenido

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:

FacetValue (example)
Obsidian tag#kdx-coverage-bar
Angular selector<kdx-coverage-bar>
Directory + fileshared/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.
  • Inputsname = optional, name* = input.required, name⇄ = two-way model(), ↑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.

TagClassWhat it doesInputsSpec
#kdx-avatarKdxAvatarComponentAvatar with initials + deterministic color from a seed, or image/icon, with optional status badge; configurable size/shape/variant ([[adr-014-user-entityADR-014]])image icon label colorSeed size shape variant borderWidth badge
#kdx-employee-cardEmployeeCardComponentEmployee as card or compact row: avatar, tags, hours, occupancy bar; dual-modeemployee* mode subtitle positionName hoursData tags ↑cardClick
#kdx-employee-listEmployeeListComponentLays out employee cards as grid or list per viewModeitems* viewMode ↑cardClick
#kdx-worker-rowKdxWorkerRowComponentConfigurable employee row — mode='worker': coverage bar + reinforcement ([[adr-024-employee-bar-semantics-and-contract-matchingADR-024]]); mode='assignment': status border + status tag + FSM ↑suspend/↑resume/↑end + [actions] slotmode initials* firstName* lastName* positionTitle* assignedHours* requiredHours* coveragePct* effectiveHours statusLabel statusSeverity isReinforcement ↑rowClick
#kdx-coverage-rowKdxCoverageRowComponentOrg-unit row: avatar stack, hours, coverage % barunitName* assignedHours* requiredHours* coveragePct* workers vacantCount ↑rowClick
#kdx-detail-profileKdxDetailProfileComponentProfile header: XL avatar + name + subtitle + badgename* image subtitle badge colorSeed
TagClassWhat it doesInputsSpec
#kdx-coverage-barCoverageBarComponentSegmented coverage bar (green/yellow/blue/red) or single-color legacy bar ([[adr-001-c-hour-color-vocabularyADR-001-c]])value breakdown
#kdx-statKdxStatComponentSingle 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-stripKpiStripComponentHorizontal strip of KPI metrics (composes #kdx-stat inline)items
#kdx-stat-cardsStatCardsComponentGrid of stat cards (composes #kdx-stat compact)cards
TagClassWhat it doesInputsSpec
#kdx-org-nodeOrgNodeComponentOrg-unit/position card: avatars, title, metrics, coverage bar, vacancy indicatordata* selected ↑nodeClick
#kdx-position-rowKdxPositionRowComponentPosition row with variant styling (vacant/contract/temp/reinforcement) + metricstitle* requiredHours* assignedHours coveragePct variant expanded ↑rowClick
TagClassWhat it doesInputsSpec
#kdx-tagKdxTagComponentUnified 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
TagClassWhat it doesInputsSpec
#kdx-list-headerListHeaderComponentPage header: title + subtitle + search + view-mode togglestitle* subtitle searchPlaceholder searchTerm⇄ viewOptions viewMode⇄
#kdx-search-barSearchBarComponentSearch input + icon + optional view-mode buttonsplaceholder searchTerm⇄ viewOptions viewMode⇄
#kdx-filter-barFilterBarComponentComposite: filter pills + tag drawerpills* selected⇄ tags selectedTags⇄
#kdx-filter-pillsFilterPillsComponentIcon+count pill buttons, single-selectpills* selected⇄
#kdx-tag-filter-pillsTagFilterPillsComponentSection-based multi-select tag pills + popoversections* selected⇄
#kdx-tag-filter-drawerTagFilterDrawerComponentDrawer of tag filters grouped by category + apply/cleartags* selected⇄
TagClassWhat it doesInputsSpec
#kdx-detail-drawerKdxDetailDrawerComponentRight-side drawer with header slot; visible model + widthvisible⇄ header width
#kdx-detail-sectionKdxDetailSectionComponentSection container: icon + title + projected bodyicon title*
#kdx-detail-fieldKdxDetailFieldComponentLabel + content pair (uppercase label)label*
#kdx-detail-actionsKdxDetailActionsComponentFooter container for action buttons (divider + gap)(projection only)
TagClassWhat it doesInputsSpec
#kdx-resource-stateResourceStateComponentSmart 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-stateLoadingStateComponentCentered spinner + sr-only message (role=status)message
#kdx-error-stateErrorStateComponentCentered error message + retry buttonmessage retryLabel ↑retry
#kdx-empty-stateEmptyStateComponentCentered icon + heading + message; CTA projectionicon heading message
TagClassWhat it doesInputsSpec
#kdx-transition-timelineKdxTransitionTimelineEmployee FSM status-transition timeline: actor avatar, verb, dates, reasonsevents
TagClassWhat it doesInputsSpec
#kdx-theme-setterKdxThemeSetterComponentControl panel: dark-mode toggle, preset, primary + surface swatches (drives LayoutService)(service-driven)
#kdx-help-tooltipHelpTooltipComponentIcon button → dialog rendering markdown help loaded from public/*.mdsrc title

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.

TagClassWhat it doesInputsSpec
#app-page-containerPageContainerMax-width card wrapper: heading + optional badge + projected contentheading badge badgeSeverity
#app-cookie-consentCookieConsentComponentFixed-bottom consent banner (accept/decline → cookie); SSR-safe(none)

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.

TagClassWhat it doesInputsSpec
#kdx-beta-badgeKdxBetaBadgeComponentBETA/ALPHA chiplabel size
#kdx-employee-capacity-barKdxEmployeeCapacityBarComponentCapacity bar by hours-source (cargo/prod/prestación) + soft/hard capcapacity*
#kdx-employee-chipKdxEmployeeChipComponentDraggable candidate card: avatar, regime, capacity barrow* dimmed ↑select
#kdx-assigned-worker-cardKdxAssignedWorkerCardComponentAssigned-worker micro-card: hours, share %, recurrence, actionsworker* requiredHours ↑onDragStart ↑changeRequested ↑viewProfileRequested ↑endRequested
#kdx-position-cardKdxPositionCardComponentPosition card: hours, source-split bar, required tags, worker stack; drop targetdata* ↑workerClick ↑employeeDropped ↑assignedDropped ↑endRequested ↑changeAssignment ↑viewProfile ↑hovered
#kdx-position-day-gridKdxPositionDayGridComponentPositions grouped Mon–Sun + “sin día”positions* (forwards position-card outputs)
#kdx-employee-sidebarKdxEmployeeSidebarComponentEligible-candidate rail with search + cargo filters (decoupled — pure inputs)rows loading error highlightedPositionId ↑select
#kdx-assign-drop-popupKdxAssignDropPopupComponentConfirm popup after drop: hours stepper, vehicle, recurrencevisible ctx ↑visibleChange ↑confirm ↑cancel

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.

TagClassWhat it doesInputsSpec
#kdx-focus-headerFocusHeaderComponentMonth org-unit header: back breadcrumb, ancestor path, focus/staffing toggles, PDF, publishorgUnitName* path focusMode staffingOpen published publishing pdfDisabled publishDisabled ↑back ↑navigateUnit ↑toggleFocus ↑toggleStaffing ↑downloadPdf ↑publish
#kdx-coverage-stripCoverageStripComponentAggregate month coverage: hours balance + delta, per-ISO-week cards (tone + gaps), hours-source legendcoverage*
#kdx-scheduling-toolbarSchedulingToolbarComponentGrid view controls: layout/zoom/cell-rep segmented groups, search, gap-jump chiplayout⇄ zoom⇄ cellRep⇄ search⇄ gapCount ↑jumpToGap

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.)

TagClassWhat it doesInputsSpec
#kdx-card-sectionKdxCardSectionComponentp-card + icon/title header + count badge + projected bodytitle* icon count cardClass
#kdx-tag-gridKdxTagGridComponentCategory-grouped tag-chip columns (contracts/quals/certs)columns* removable ↑remove
#kdx-hours-balance-cardKdxHoursBalanceCardComponentHours balance: base + adjustments → effective, consumption bar, state chipbalance*
#kdx-module-gridKdxModuleGridComponentNav tiles (icon badge + label + desc + chevron)links*
#kdx-alert-listKdxAlertListComponentSeverity-iconed alert list + empty statealerts max emptyMessage
#kdx-data-tableKdxDataTableComponentp-table wrapper baking ADR-013 a11y (aria-label, scope=col), striped, empty/loading, sortable, projected #bodyvalue ariaLabel* columns* loading rows paginator dataKey

Wave-4 design notes:

  • #kdx-assignment-row was implemented as a configurable #kdx-worker-row variant (mode='assignment' → status border + FSM ↑suspend/↑resume/↑end + [actions] slot; worker-mode untouched), honoring “configurable over many.”
  • #kdx-org-tree-table was droppedp-treeTable + p-treeTableToggler resolve 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 to structure instead.

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 custom kdx-* 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 Libraryoverview.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).


Tracked deviations from the one-standard goal, fixed during this program:

  1. #cv-tag → signals + consolidation.Done (Wave 1)cv-tag + emp-tag consolidated into the single configurable #kdx-tag (shared/components/tag/); both legacy components deleted, all consumers updated, spec added.
  2. 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.
  3. State leaves — confirm #kdx-resource-state is the single configurable wrapper and the three leaf states stay pure presentational.
  4. app-* carve-out#app-page-container / #app-cookie-consent stay app-* 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 live staffing() computed from coverage-summary employee_breakdown; the alerts card shows an empty state (no endpoint yet), examples moved to shared/mocks/dashboard-mocks.ts; the no-mock-imports guard now also flags inline // mock|fake|hardcoded|dummy markers (verified both ways, 34/34).
  • Raw Tailwind colorsshared/components/stat-cards/stat-cards.component.ts L16–21 (gradient utilities from-cyan-700 to-cyan-900, …); roster/components/assignment-dialog.component.ts L84,88,93 (text-red-500, border-red-200). Replace with @theme tokens / var(--p-*) (ADR-012-c). A catalog component must not break the rule the program enforces.
  • ::ng-deep without justificationtransition-timeline.component.ts L70–82 (×5) and help-tooltip.component.ts L52: add a one-line ADR-012 justification comment, or move PrimeNG token overrides into KdxPreset.
  • Smart component in the cataloghelp-tooltip.component.ts injects HttpClient; re-classify it as help-infrastructure (not a kdx-* primitive) or invert the dependency (content via input()).
  • a11y debt to bake into primitiveshours.component.ts table (≈L146–164) lacks scope="col" + aria-label; structure.component.ts p-treeTable (≈L189) lacks aria-label. #kdx-data-table / #kdx-org-tree-table must bake these in, with hours/structure as the proving migration targets.
  • Residual hardcoded #ffftransition-timeline.component.ts template L99 (system-actor p-avatar [style]) and TS L176 (avatarStyle()) use literal white → var(--p-surface-0). Minor; sweep in a later wave or at the gate.

[!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 tagPurpose
#kdx-tagConfigurable tag merging cv-tag + emp-tag (variant/mandatory/tooltip/removable)
#kdx-statSingle 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-sectionp-card + icon/title header + optional count badge. Highest-frequency unextracted pattern (6× in roster-detail alone)
#kdx-data-tablep-table wrapper enforcing ADR-013 a11y (aria-label, scope=col), striped, empty/loading/sort slots. Proving targets: hours, structure
#kdx-icon-action-barConfigurable cluster of [rounded][outlined] size=small icon buttons + tooltips for row actions (edit/delete/suspend/…); centralizes the danger/disabled convention
#kdx-drawer-footerSticky cancel/confirm footer for drawers
#kdx-confirm-popupConsistent danger/confirm wrapper for destructive actions
#kdx-sectionSemantic <section> + heading using the typography scale ([[adr-012-b-typography
#kdx-page-headerDropped — 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 tagPurposeCoveris-e source
#kdx-beta-badgeBETA/ALPHA chipshared/components/beta-badge/
#kdx-employee-capacity-barEmployee capacity/utilization barshared/components/staffing/employee-capacity-bar.component.ts
#kdx-employee-chipDraggable compact employee card (avatar + name + capacity)staffing/employee-chip.component.ts
#kdx-assigned-worker-cardCard for a worker already assigned to a slotstaffing/assigned-worker-card.component.ts
#kdx-position-cardPosition summary card (requirements + coverage)staffing/position-card.component.ts
#kdx-employee-sidebarSidebar list of eligible employeesstaffing/employee-sidebar.component.ts
#kdx-position-day-gridPer-day grid of position slotsstaffing/position-day-grid.component.ts
#kdx-assign-drop-popupDrag-drop assignment confirmation popupstaffing/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 tagPurposeBuild note
#kdx-tag-gridGrouped tag display (contracts/quals/certs)from roster-detail ≈L340–406 (3 copy-paste columns); logic-bearing → spec
#kdx-hours-balance-cardContract + hours-balance cardfrom roster-detail ≈L447–499; logic-bearing → spec
#kdx-module-gridTile grid of module/nav entriesconfirmed 2× duplicate: dashboard ≈L256–281 + control-panel ≈L47–75
#kdx-alert-listSeverity-tagged alert listdashboard ≈L241–251 (move its mock into a showcase fixture)
#kdx-assignment-rowAssignment row + action buttonsvariant of #kdx-worker-row (mode/actions), not a 4th row sibling — roster-detail ≈L276–321
#kdx-employee-headerProfile headerreuse/extend existing #kdx-detail-profile — do not build a 3rd profile header
(info grid)label+value pairsreuse existing #kdx-detail-fieldroster-detail ≈L412–445, 578–616
#kdx-hours-table-rowOne row of the hours ledger tablefrom hours.component.ts; pair with #kdx-data-table + a11y
#kdx-org-tree-tableTree-table wrapper for org structurefrom org-chart-detail / structure; bake aria-label
#kdx-kpi-carddashboard hero cardscomposes #kdx-stat (not a sibling to stat-cards) — dashboard ≈L63–168

  1. Adding a component — create shared/components/<name>/, build per the kdx-angular-component skill (standalone, OnPush, signal I/O, inject(), @if/@for, PrimeNG-first, no raw Tailwind colors), add a showcase page, add a *.spec.ts where logic-bearing, and add a row here. Tag = selector = directory.
  2. One configurable component over many — before adding, check whether an existing component can absorb the case via an input.
  3. 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.