# Component Extraction Report — Attendance web-app Generated by `scripts/find-duplicates.mjs` (computed duplication scan) + 5 parallel code-reading agents (file-level evidence). Web app: React 19 + `@astryxdesign/core` design system + StyleX. Raw machine-readable data: `scripts/duplication-report.json`. ## Headline numbers | Metric | Value | | ------------------------------------------- | ---------------------------------------- | | Files / JSX elements scanned | 120 / 10,219 | | `VStack` uses | 1,425 (incl. 464× `gap=1`, 360× `gap=0`) | | `HStack` uses | 853 (incl. 204× `gap=2 vAlign=center`) | | Files importing `@astryxdesign/core/Layout` | 68 | Top file pairs by structural similarity (Jaccard): `GitProviders ↔ Integrations` **76%**, `NewIntegration ↔ NewIssueProvider` **58%**, `NewGitProvider ↔ NewIssueProvider` **57%**, `GitProviderDetails ↔ IntegrationDetails` **54%**, `NewGitProvider ↔ NewIntegration` **54%**. ## Tier 1 — byte-identical patterns, extract first | Candidate | Occurrences | Evidence | API sketch | | -------------------------------- | ------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------- | | **`StatCard` / `StatCardGrid`** | ~22 list + ~30 detail cards across 13 files | `GitProviders.tsx:176-185`, `LLMProviderDetails.tsx:274-283`, `AgentPlugins.tsx:240-249`, `Usage.tsx:612-675` (icon/trend variant) | `{ label, value, hint?, icon?, footnote? }` + `StatCardGrid({ cards, columns })` | | **`SectionHeader`** | 163 CLI hits / 25 files | `GitProviderDetails.tsx:205-210`, `IntegrationDetails.tsx:259-264`, `AgentDetails.tsx:423-428` | `{ title, description?, endAction?, level? }` | | **`WizardProgress`** | 3 byte-identical + 2 variants | `NewGitProvider.tsx:28-53`, `NewIssueProvider.tsx:47-72`, `NewIntegration.tsx:28-53` (only `STEP_LABELS` differs); sidebar variant `NewLLMProvider.tsx:134-158` | `{ steps: string[], current: number, variant? }` — slot into `` | | **`StatusCell`** | 7 list + 3 detail + dispatch | `GitProviders.tsx:95-105`, `Integrations.tsx:90-105`, `ChatSessions.tsx:114-122` | `{ variant, label, isPulsing?, detail? }` (wraps `StatusDot` + `Text`) | | **`LeadingCell` / `EntityRow`** | ~9 list + 7 feature sites | `GitProviders.tsx:66-76`, `PlaybookDiagram.tsx:102-112`, `ArtifactViewer.tsx:166-187` | `{ leading?, title, subtitle?, meta?/trailing? }` | | **`FormFooter` / `FormActions`** | 7 instances / 6 files | `GitProviderDetails.tsx:296-308`, `LLMProviderDetails.tsx:425-433`, `AgentDetails.tsx:802-815` (Reset/dirty variant) | `{ saved?, primaryLabel, onPrimary, secondaryLabel?, onSecondary?, isDirty? }` | | **`BackButton`** | 12+ sites | `NewGitProvider.tsx:104-109`, `GitProviderDetails.tsx:59-64`, `NewPlaybook.tsx:757-764` | `{ label, href, iconOnly? }` (ghost + `IconArrowLeft` + navigate) | ## Tier 2 — near-identical composites (bigger wins) ## Tier 2 — near-identical composites (bigger wins) > ⚠️ The codebase is being actively rewritten while this report was generated (e.g. `DetailHero` below landed mid-analysis). Line references are as-of-scan-time and may be slightly stale. | Candidate | Occurrences | Evidence | | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | **`DetailHero`** — ✅ **already exists** in `src/layouts/DetailHero.tsx`, already adopted by 8 detail pages (`AgentDetails`, `AgentPluginDetails`, `ConnectorDetails`, `GitProviderDetails`, `IntegrationDetails`, `IssueProviderDetails`, `LLMProviderDetails`, `PlaybookDetails`). **Remaining adopters:** `TaskDetails` (LayoutHeader variant), `DispatchIntegrations`, `Projects` (ProjectDetailsPage), `ArtifactViewer` | ~11 total | API `{ breadcrumbs?, leading?, status?, title, metadata?, description?, actions? }` — exactly the shape recommended by the infra analysis; replaces the hand-rolled hero block | | **`WizardPageShell`** | 3 full + 2 partial | `NewGitProvider.tsx:101-350`, `NewIssueProvider.tsx:139-393`, `NewIntegration.tsx:92-320` — back button + hero + step Card + footer | | **`ProviderPicker`** | 5 | `NewGitProvider.tsx:130-155`, `NewLLMProvider.tsx:173-191` — `SelectableCard`/`ClickableCard` grid over `Object.keys(definitions)` | | **`ReviewSummary` + `SummaryGrid`** | 3 | `NewGitProvider.tsx:272-319`, `NewIntegration.tsx:241-288` — muted Section + icon/name + label-value grid | | **`TableSection`** | 11 | `GitProviders.tsx:211-230`, `Usage.tsx:828-851` — section header + `Card padding={0}` + `Table` (fixed 7-prop set `density dividers verticalAlign textOverflow hasHover`) | | **`FilterToolbar`** | 6 | `Playbooks.tsx:196-221`, `Projects.tsx:415-450`, `ChatSessions.tsx:190-204` — hidden-label search + `SegmentedControl`/`Selector` + count | | **`DeleteResourceDialog` + `DangerZone`** | 3 detail + 4 org containers | `GitProviderDetails.tsx:314-347`, `OrganizationSettingsContainer.tsx:218-240` | | **`EmptyStateCard`** | 5 | `ProjectDispatch.tsx:79-92`, `OrganizationPeopleContainer.tsx:186-200` | | **`IconActionMenu`** | 4 | tasks + org tables + conversations (`DropdownMenu` ghost icon button) | | **`ValidatedField`** (auth) | ~15 fields / 5 forms | `LoginPage.tsx:116-171`, `SignupPage.tsx:164-355`, `ProfileSettingsPage.tsx:130-223`, `OrganizationForm`, `InviteMemberDialog` | ## Tier 3 — hooks & data/logic extraction | Candidate | Occurrences | Evidence | | ---------------------------------------------------------------- | ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | **`useRouteSection`** | 7 | `AgentDetails.tsx:820-830`, `GitProviderDetails.tsx:41-51`, `LLMProviderDetails.tsx:225-228` — normalizes `:section` param + `goToSection` (also fixes the `overview → ""` suffix inconsistency) | | **`useSeededList`** | 4 | `GitProviders.tsx:30-33`, `Integrations.tsx:28-33` — `location.state?.createdX` seeding | | **`useFilteredList`** | 6 | AgentPlugins, Playbooks, Projects, Agents, ChatSessions, Tasks | | **`useDetailEntity`** | 5 | `GitProviderDetails.tsx:46`, `IntegrationDetails.tsx:91` — state-entity-or-lookup + patch + didSave | | **`createKindRegistry` / `createStatusMap` / `createSeedStore`** | 8 data modules | `git-provider-data.ts:17-50`, `connector-data.ts:79-89`, `dispatch-data.ts:195-241` — collapse the per-feature `Record` + `getX(id)` + label/variant map trio; UI status maps (`TaskKanbanBoard.tsx:93-114`, `ArtifactViewer.tsx:64-69`) fold in | | **Shared contracts package (`packages/`)** | web-app ↔ worker | `auth-client.ts` vs `auth-options.ts`: user fields, password min 8, username 3-30 regex, auth base URL, `APP_NAME`, invitation roles duplicated across apps — `pnpm-workspace.yaml` currently only includes `apps/*` | ## Tier 4 — consistency fixes to fold into the rewrite - **`PageHero` bypasses** (inconsistent hero styling: `gap={1}` + plain `Heading level={1}` vs `PageHero`'s `gap={2}` + `display-2`): `Agents.tsx:582-595`, `LLMProviders.tsx:28-41`, `NewLLMProvider.tsx:111-121`, `OrganizationPeopleContainer.tsx:138-166`. - **`HeroTabs` bypass**: `Projects.tsx:1497-1532` re-implements it inline with raw `Toolbar`+`TabList` — drop-in swap. - **`FormLayout` unused**: the design system ships it, but every form hand-rolls field sets in `VStack`/`Grid` — worth centralizing with `ValidatedField`. - Feature-local duplicates (only extract within the feature): org `Skeleton` blocks, org confirm `AlertDialog` flows (`useConfirmAction`), playbooks `conditionLabels`/`edgeColors` maps (`PlaybookDiagram.tsx:74-86` vs `PlaybookFlow.tsx:10-22`), `WorkspaceToolsPanel`'s three terminal/browser/vnc panels. ## Deliberately NOT worth extracting - `LLMProviders` item-list (deliberate non-table presentation), `TasksView` (already shared), Usage charts (analytics-specific), `NewPlaybook` diagram inspector (single consumer), kanban DnD engine and dagre/ReactFlow layouts (single-use), per-feature info `Banner` copy, `goToSection` standalone (absorbed by hook), per-feature status label _maps_ (keep local; extract only the pill rendering). ## Suggested landing order (if the team proceeds) 1. Create `apps/web-app/src/components/` (currently none exists — everything lives in `features/`). 2. Land `TitleBlock`/`SectionHeader` + `EntityRow` first (together ~24 call sites across 8 features). 3. Land `StatCard`/`StatCardGrid`, `StatusCell`, `FormFooter`, `BackButton`. 4. Then composites: `useRouteSection` (routing hook for the 7 tabbed detail pages — `DetailHero` is done, the routing layer is the remaining shared piece), `WizardPageShell` + `WizardProgress` + `ProviderPicker`, `TableSection` + `FilterToolbar`. 5. Then data layer: kind-registry/status-map factories, `useSeededList`/`useFilteredList`, and the `packages/` contracts member for web-app ↔ worker auth constants. ## Rerunning the scanner ```sh cd apps/web-app node scripts/find-duplicates.mjs # terminal report node scripts/find-duplicates.mjs --json # machine-readable node scripts/find-duplicates.mjs --limit 20 --min-count 5 ```