Files

17 KiB
Raw Permalink Blame History

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

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