90 lines
17 KiB
Markdown
90 lines
17 KiB
Markdown
# 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 `<PageHero actions={…}>` |
|
||
| **`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<Kind, Definition>` + `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
|
||
```
|