Files
Attendance/apps/web-app/scripts/extraction-report.md
T

90 lines
17 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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
```