Skip to content

UX Prototype Architectural Review

Per-component audit of all canary components in @arda-cards/design-system (ux-prototype repository) against the architectural framework established by:

Each component is evaluated against these criteria:

CriterionCompliantNeeds Change
No TanStack importComponent has no useQuery/useMutationComponent fetches data directly
Typed data contractData comes via typed props or typed provider hooksData comes via untyped callbacks or unknown[] keys
Edit lifecycleUses initialValue → draft → onConfirm/onCancelMutates parent state directly or lacks lifecycle callbacks
Intrinsic validationValidates own fields, exposes ValidationResultNo validation or validation is ad-hoc
Contextual errorsAccepts contextErrors from parentNo mechanism for parent-injected errors
Standardized typesUses EditableComponentProps<T>, ValidationResult, etc.Custom one-off interfaces

Components rated:

  • Compliant — no changes needed for the target architecture
  • Minor — small additions (add contextErrors prop, export validation function)
  • Moderate — interface restructuring (replace callbacks with typed providers, add lifecycle)
  • Major — significant rework (data fetching moved out, state model changed)

Three diagrams showing how components compose. Leaf components are primitives or atoms; branching nodes are molecules or organisms that contain children. Annotations show the data contract at each boundary.

PlantUML diagram

PlantUML diagram

PlantUML diagram

PlantUML diagram

PlantUML diagram


Primitives are vendored ShadCN/Radix components. They are stateless UI building blocks with no data-fetching, no business logic, and no edit lifecycle. All compliant — no changes needed.

ComponentStatusNotes
alert-dialogCompliantRadix primitive wrapper
aspect-ratioCompliantRadix primitive wrapper
buttonCompliantCVA variants, loading state
checkboxCompliantRadix primitive wrapper
collapsibleCompliantRadix primitive wrapper
dropdown-menuCompliantRadix primitive wrapper
inputCompliantStyled HTML input
labelCompliantRadix label wrapper
popoverCompliantRadix primitive wrapper
progressCompliantRadix primitive wrapper
separatorCompliantRadix primitive wrapper
sheetCompliantRadix primitive wrapper
sidebarCompliantRadix sidebar primitive with context
skeletonCompliantCSS-only loading placeholder
sliderCompliantRadix primitive wrapper
tableCompliantStyled HTML table
tabsCompliantRadix primitive wrapper
textareaCompliantStyled HTML textarea
toggleCompliantRadix primitive wrapper
tooltipCompliantRadix primitive wrapper

Simple presentational components with no data-fetching or complex state.

ComponentStatusNotes
avatarCompliantRenders initials/image; no state
badgeCompliantCVA-styled label
badge-baseCompliantBase badge component
brand-logoCompliantStatic SVG/image
buttonCompliantStyled button with loading
cardCompliantContainer with styling
dialogCompliantRadix dialog wrapper
drawerCompliantRadix drawer wrapper
icon-buttonCompliantIcon-only button variant
icon-labelCompliantIcon + text pair
input-groupCompliantInput with addon/button
read-only-fieldCompliantLabel + value display
search-inputCompliantDebounced search input
copyright-acknowledgmentCompliantControlled checkbox — stateless
ComponentStatusChange needed
confirm-dialogMinorCurrently has a custom interface (ArdaConfirmDialogStaticConfig + ArdaConfirmDialogRuntimeConfig). Should adopt standardized edit lifecycle types when available. Add contextErrors capability for forms that use it inside validation flows.

All cell display components are pure renderers receiving value and data from AG Grid’s row model. No data fetching, no state.

ComponentStatusNotes
boolean-cell-displayCompliantRenders checkbox icon from value
color-cell-displayCompliantRenders color swatch from value
date-cell-displayCompliantFormats date from value
image-cell-displayCompliantRenders ImageDisplay from value
memo-cell-displayCompliantRenders truncated text from value
number-cell-displayCompliantFormats number from value
select-cell-displayCompliantRenders label from value + options map
text-cell-displayCompliantRenders text from value

Cell editors manage local state and interact with AG Grid’s edit lifecycle. Most are value-only editors (Category 1 from section 6.2 of tanstack-component-binding-analysis.md) and need minor changes. The image cell editor is a mutation editor (Category 3) needing moderate changes.

ComponentStatusChange needed
boolean-cell-editorMinorValue-only editor. Currently self-contained. Add standardized ValidationResult for intrinsic validation (boolean is always valid, but the pattern should be consistent).
color-cell-editorMinorValue-only editor with static options. No data-fetching. Standardize lifecycle types.
date-cell-editorMinorValue-only editor. Should add intrinsic validation (reject invalid dates like Feb 30). Currently no validation — date parsing delegated to browser’s date input.
image-cell-editorModerateMutation editor. Currently receives config via factory createImageCellEditor(config). Under FD-01, factory should also accept typed provider hooks (useImageUpload, useCheckReachability). The existing callback interface (onUpload, onCheckReachability on ImageUploadDialog) is already close to FD-01; the factory just needs to forward them.
memo-cell-editorMinorValue-only editor. Rich text editing. Standardize lifecycle.
memo-button-cellMinorButton that opens memo editor. No data-fetching.
create-memo-button-cell-editorMinorSimilar to memo-button-cell. Standardize lifecycle.
number-cell-editorMinorValue-only editor. Should add intrinsic validation (numeric range, integer vs. decimal). Currently accepts any input.
select-cell-editorMinorValue-only editor with static options array. No data-fetching. Standardize lifecycle.
text-cell-editorMinorValue-only editor. Simplest case. Standardize lifecycle.
action-cell-rendererCompliantNot an editor — renders action buttons. Callbacks come from column def.

3.1 Presentational Molecules — Compliant

Section titled “3.1 Presentational Molecules — Compliant”
ComponentStatusNotes
field-listCompliantRenders array of FieldDef; no state, no fetching
grid-actionCompliantAction button wrapper for grid cells
image-comparison-layoutCompliantSide-by-side layout; pure rendering
image-hover-previewCompliantPopover on hover; manages delay timer only
item-details-card-previewCompliantStatic card rendering
item-details-headerCompliantHeader with title, image, and actions
drag-headerCompliantAG Grid custom header; no data-fetching
ComponentStatusNotes
sidebar-headerCompliantStatic rendering
sidebar-navCompliantStatic rendering
sidebar-nav-groupCompliantStatic rendering
sidebar-nav-itemCompliantStatic rendering
sidebar-user-menuCompliantStatic rendering with callbacks
sidebar-story-decoratorCompliantStorybook-only decorator
ComponentStatusNotes
action-toolbarCompliantRenders action buttons from config; manages overflow
overflow-toolbarCompliantResponsive toolbar with overflow menu
ComponentStatusChange needed
data-gridMinorAG Grid wrapper (DataGrid). Accepts rowData as prop — compliant with FD-01 (data provided externally). Manages column persistence, sort state, and selection internally. Needs no data-fetching changes. Should add typed onCellValueChanged callback with ValidationResult when cell editing evolves.
sort-menu-headerCompliantCustom sort header; no data-fetching
ComponentStatusChange needed
image-displayMinorRenders image with loading/error states. Currently manages loadState internally via onLoad/onError on <img>. Under hierarchical edit lifecycle, should accept onImageChange as a typed callback (already does). Needs contextErrors prop for form contexts.
image-drop-zoneMinorUnified input surface. Accepts onInput: (input: ImageInput) => void callback — already FD-01 compliant. Performs intrinsic validation (MIME type, HTTPS URL). Should export a validateImageInput() function for parent composition. Does not fetch data (HEIC conversion is local).
image-form-fieldMinorForm field wrapper. Accepts config, imageUrl, onChange. Already FD-01 compliant (data via props). **Should adopt `EditableComponentProps<string
image-inspector-overlayCompliantModal image viewer; pure rendering with optional onEdit callback.
image-preview-editorCompliantCrop/zoom/rotate editor; manages crop state locally, pushes via onCropChange. Already follows the draft-push pattern.
ComponentStatusChange needed
item-grid-columnsModerateColumn definition factory (createItemGridColumnDefs). Currently accepts ItemGridLookups — an interface with supplier? and classificationType? lookup functions typed as (search: string) => Promise<TypeaheadOption[]>. This is already a typed provider pattern (FD-01 compliant for lookups). Changes needed: (1) Expand ItemGridLookups to include all lookup fields (currently only 2 of 9+). (2) Add ImageCellEditor factory wiring for useImageUpload/useCheckReachability hooks. (3) Generalize to a broader GridEditorProviders interface that covers all editor types.
ComponentStatusChange needed
typeahead-cell-editorModerateCurrently accepts lookup: (search: string) => Promise<TypeaheadOption[]> — this is a typed async callback, which is the correct FD-01 pattern for data that must be fetched during editing. The callback is already TanStack-agnostic. Changes needed: (1) The lookup prop should be renamed to a hook-style interface for consistency: useLookup: (query: string) => { data, isLoading } — this lets the component react to loading state from the hook rather than managing its own loading/error state from the promise. (2) Add contextErrors support. (3) Export intrinsic validation function. (4) The manual debounce + AbortController logic (~30 lines) would be replaced by TanStack Query’s built-in deduplication and cancellation when the hook is TanStack-backed — but the component shouldn’t know that. Alternative: keep the current lookup promise interface — it’s simpler than a hook and the component already handles loading/error well. The debounce is a UI concern (don’t hammer the server) that belongs in the component regardless of the data source. This is a design decision.

ComponentStatusNotes
app-headerCompliantStatic rendering with callbacks
sidebarCompliantRenders nav from config
sidebar-contextCompliantContext provider for sidebar state
ComponentStatusChange needed
image-upload-dialogModerateState machine orchestrator. Currently accepts onUpload: (file: Blob) => Promise<string> and onCheckReachability: (url: string) => Promise<boolean> — these are already typed callback props (FD-01 compliant). Changes needed: (1) The upload progress is simulated internally (50ms timer). Under the hierarchical edit lifecycle, the onUpload callback should support progress reporting — either via an onProgress callback parameter or by returning a richer result. This is the Open Design Question from upload-component-backend-analysis.md. (2) Add UploadError state handling for production retry flows. (3) The onConfirm callback returns ImageUploadResult — should adopt EditLifecycleCallbacks<ImageUploadResult> for consistency. (4) Add contextErrors support.
ComponentStatusChange needed
item-card-editorModerateWYSIWYG card editor. Currently accepts fields: ItemCardFields + onChange + onImageConfirmed. This is a draft-management component that already follows the hierarchical edit pattern partially (local state → push via onChange). Changes needed: (1) Adopt EditableComponentProps<ItemCardFields> — rename fields to initialValue, add onConfirm/onCancel. Currently only has onChange (every keystroke pushes up) without a confirm/cancel lifecycle. (2) Add intrinsic validation (required title). (3) Add contextErrors support. (4) The internal ImageUploadDialog needs typed provider hooks wired through (same as image-cell-editor — factory or context injection).
ComponentStatusChange needed
item-detailsMinorDrawer panel showing item fields. Accepts all data via props (title, fields, cardPreview, actions). FD-01 compliant — no data-fetching. The component is a pure renderer. Needs contextErrors support if it becomes editable in the future. Currently read-only.
ComponentStatusChange needed
item-gridMinorWraps createEntityDataGrid factory. Accepts items (data) and lookups (typed provider callbacks) via props — already FD-01 compliant for data sourcing. The lookups: ItemGridLookups interface is a typed provider. Changes needed: expand ItemGridLookups to cover all lookup fields and image editor hooks. The onPublishRow callback is a mutation trigger — under the hierarchical lifecycle, this becomes the entity-level onConfirm.
ComponentStatusChange needed
create-entity-data-gridMinorFactory that creates typed grid components. Accepts data and onRowPublish via model props — FD-01 compliant. The onRowPublish: (rowId, changes, entity?) => Promise<void> is effectively an untyped mutation callback. Under the standardized types, this should adopt EditLifecycleCallbacks for row-level edit operations. The factory’s EntityDataGridConfig already follows the Static/Init separation pattern.
create-entity-data-grid-shimCompliantBackward-compatible shim; no data-fetching.

FileStatusChange needed
image-field-config.tsCompliantTypes only; no data-fetching
image-upload-handlers.tsCompliantDefault stubs for Storybook; production implementations provided by app
get-cropped-image.tsCompliantCanvas utility; no data-fetching
maybe-convert-heic.tsCompliantFile conversion utility; no data-fetching
__mocks__/image-story-data.tsCompliantTest/story fixtures

New types needed (from hierarchical-edit-lifecycle-exploration.md):

TypePurpose
ValidationResultStandardized validation output
FieldErrorPer-field error descriptor
EditLifecycleCallbacks<T>onChange, onConfirm, onCancel
EditableComponentProps<T>Base props for any editable component
useDraft<T> hookReusable draft management

TierTotalCompliantMinorModerateMajor
Primitives2020000
Atoms (UI)1414000
Atoms (grid cell displays)88000
Atoms (grid cell editors)101810
Molecules (presentational)1515000
Molecules (data/image)72320
Organisms83230
Total82631360

These are the components that need interface restructuring:

  1. image-cell-editor — Factory needs typed provider hooks for upload/reachability
  2. typeahead-cell-editorlookup callback may evolve to hook interface; design decision pending
  3. item-grid-columns — Expand ItemGridLookups to all fields, add image editor hook wiring
  4. image-upload-dialog — Upload progress interface, UploadError state, standardized lifecycle types
  5. item-card-editor — Adopt EditableComponentProps, add confirm/cancel lifecycle, wire image providers
  6. item-grid — Expand lookups interface to match column needs

Pattern: add contextErrors prop, export validation function, adopt standardized types. These are additive — no breaking interface changes.

The canary components are already well-aligned with FD-01. The key patterns — typed callback props for data and mutations, data via props not fetching, factory functions for AG Grid editors — are already in place. The changes are evolutionary (standardize types, add validation, expand provider interfaces) rather than revolutionary.

The 6 moderate-change components are exactly the image-related and grid components that the image upload project touches. The standardized types (ValidationResult, EditableComponentProps<T>, useDraft<T>) should be introduced as part of this project and applied to the image components first. Other canary components can adopt the types incrementally in subsequent projects.


Copyright: (c) Arda Systems 2025-2026, All rights reserved