17 KiB
17 KiB
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.
DetailHerobelow 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
PageHerobypasses (inconsistent hero styling:gap={1}+ plainHeading level={1}vsPageHero'sgap={2}+display-2):Agents.tsx:582-595,LLMProviders.tsx:28-41,NewLLMProvider.tsx:111-121,OrganizationPeopleContainer.tsx:138-166.HeroTabsbypass:Projects.tsx:1497-1532re-implements it inline with rawToolbar+TabList— drop-in swap.FormLayoutunused: the design system ships it, but every form hand-rolls field sets inVStack/Grid— worth centralizing withValidatedField.- Feature-local duplicates (only extract within the feature): org
Skeletonblocks, org confirmAlertDialogflows (useConfirmAction), playbooksconditionLabels/edgeColorsmaps (PlaybookDiagram.tsx:74-86vsPlaybookFlow.tsx:10-22),WorkspaceToolsPanel's three terminal/browser/vnc panels.
Deliberately NOT worth extracting
LLMProvidersitem-list (deliberate non-table presentation),TasksView(already shared), Usage charts (analytics-specific),NewPlaybookdiagram inspector (single consumer), kanban DnD engine and dagre/ReactFlow layouts (single-use), per-feature infoBannercopy,goToSectionstandalone (absorbed by hook), per-feature status label maps (keep local; extract only the pill rendering).
Suggested landing order (if the team proceeds)
- Create
apps/web-app/src/components/(currently none exists — everything lives infeatures/). - Land
TitleBlock/SectionHeader+EntityRowfirst (together ~24 call sites across 8 features). - Land
StatCard/StatCardGrid,StatusCell,FormFooter,BackButton. - Then composites:
useRouteSection(routing hook for the 7 tabbed detail pages —DetailHerois done, the routing layer is the remaining shared piece),WizardPageShell+WizardProgress+ProviderPicker,TableSection+FilterToolbar. - Then data layer: kind-registry/status-map factories,
useSeededList/useFilteredList, and thepackages/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