TorbatYar Frontend — Shared Components Report
Date: 2026-07-26
Scope: components/ inventory, design system analysis, duplication mapping
1. Component Layer Overview
The frontend operates a three-tier component hierarchy:
Tier 1 — Platform DS components/ds/ (primary, ~90+ consumers)
Tier 2 — Marketing DS components/ui/ (legacy, ~10 consumers)
Tier 3 — Module DS components/{module}/design-system|ui/
Module pages components/{module}/pages/
Module shells components/{module}/*Shell.tsx
2. Primary Design System (components/ds/)
File inventory
| File |
Exports |
Role |
index.ts |
Barrel |
Public API |
Button.tsx |
Button |
CVA variants, ref forwarding |
Input.tsx |
Input |
Text input |
Card.tsx |
Card, subcomponents |
Surface container |
Badge.tsx |
Badge |
Semantic tones (success/warning/danger/info) |
Dialog.tsx |
Dialog, ConfirmDialog |
Modal dialogs |
Drawer.tsx |
Drawer |
Side panel |
DatePicker.tsx |
DatePicker |
Jalali calendar |
FormControls.tsx |
FormField, Select, Checkbox, MoneyInput, Tabs |
Form primitives |
NavChrome.tsx |
TableToolbar, Pagination, Breadcrumbs, Skeleton |
Navigation/table chrome |
Page.tsx |
PageHeader, EmptyState, LoadingState, ErrorState, DataTable, StatCard |
Page scaffolding |
DS export surface (from index.ts)
Button, Input, Card, Badge, Dialog, ConfirmDialog,
PageHeader, EmptyState, LoadingState, ErrorState,
DataTable, StatCard, DatePicker, Drawer, Select,
FormField, Checkbox, MoneyInput, Tabs,
TableToolbar, Breadcrumbs, Skeleton, Pagination
Adoption by module
| Consumer |
Approx. import sites |
| Accounting |
40+ |
| Beauty |
30+ |
| Healthcare |
25+ |
| Platform (admin, auth, settings) |
10+ |
Boundary compliance: ✅ components/ds/ imports nothing from domain modules.
Known DS gap
components/ds/Button.tsx does not expose a loading prop, but accounting routes use <Button loading={...}> — causing the current build failure. The legacy components/ui/Button.tsx does support loading.
3. Legacy Marketing UI (components/ui/)
File inventory
| File |
Exports |
Button.tsx |
Button (with loading prop) |
Badge.tsx |
Badge (variant-based) |
Container.tsx |
Container |
SectionTitle.tsx |
SectionTitle |
AppTile.tsx |
AppTile |
index.ts |
Barrel |
Consumers (~10 files)
app/page.tsx (platform home)
app/login/page.tsx, app/register/page.tsx, app/onboarding/page.tsx
app/dashboard/page.tsx
components/auth/AuthPanel.tsx
components/AppStoreGrid.tsx
components/TenantSitePage.tsx
Duplicate components: DS vs UI
| Component |
DS path |
UI path |
Key difference |
| Button |
ds/Button.tsx |
ui/Button.tsx |
UI has loading prop; DS uses CVA + ref |
| Badge |
ds/Badge.tsx |
ui/Badge.tsx |
DS uses tone; UI uses variant |
Recommendation: Deprecate components/ui/Button and Badge; add loading to DS Button; migrate 10 consumer files.
4. Platform Shared Components (root components/)
| Component |
Role |
Used by |
AuthGuard.tsx |
Redirect unauthenticated users |
All module layouts |
AdminAuthGuard.tsx |
Admin-specific auth |
Admin layout |
ThemeProvider.tsx |
White-label theme loader |
Root layout |
SiteHeader.tsx |
Global navigation bar |
Root layout (all routes) |
TenantSwitcher.tsx |
Workspace selector |
SiteHeader |
RouteProgress.tsx |
Navigation progress bar |
Accounting layout |
AppStoreGrid.tsx |
SuperApp module launcher |
Dashboard |
TenantSitePage.tsx |
Tenant subdomain landing |
Platform home |
HealthStatus.tsx |
Service health display |
UNUSED (dead code) |
5. Providers (components/providers/)
| Provider |
Context? |
Responsibility |
AppProviders.tsx |
No (QueryClient only) |
TanStack Query + Sonner toaster |
ColorModeProvider.tsx |
Yes |
Light/dark mode, localStorage persistence |
Provider stack in root layout:
ThemeProvider → ColorModeProvider → AppProviders → SiteHeader + main
No auth context — auth state accessed via useMe() hook hitting TanStack Query cache.
6. Module Shells
Side-by-side comparison
| Feature |
AccountingShell |
BeautyPortalShell |
HealthcarePortalShell |
| Sidebar nav |
✅ |
✅ |
✅ |
| Collapsible groups |
✅ |
✅ |
✅ |
| Mobile drawer |
✅ |
✅ |
✅ |
| Tenant display |
✅ |
✅ |
✅ |
| Nav source |
lib/accounting-nav.ts |
lib/beauty-portals.ts |
lib/healthcare-portals.ts |
| Portal ID param |
N/A |
✅ |
✅ |
| Lines of code |
~200 |
~220 |
~220 |
| Similarity |
— |
~90% identical structure |
~90% identical structure |
Portal layout factory
| Module |
File |
Portal IDs |
| Beauty |
components/beauty/createPortalLayout.tsx |
customer, owner, reception, staff, admin |
| Healthcare |
components/healthcare/createPortalLayout.tsx |
patient, doctor, reception, clinic, hospital, pharmacy, admin |
Both factories implement identical gate logic:
AuthGuard
useMe() loading/error/onboarding check
- Tenant ID validation
- Render domain portal shell
Duplication estimate: ~95% code overlap between the two factory files.
7. Module Design Systems
Beauty (components/beauty/design-system/)
| File |
Exports |
tokens.ts |
Status tone maps, accent constants |
BeautyStatusChip.tsx |
Domain status badges |
BeautyCards.tsx |
CardShell, SalonCard, ServiceCard, TimelineItem, StatWidget |
BeautyPrimitives.tsx |
StatWidget, SearchResultRow |
BeautyTablePage.tsx |
Searchable DataTable wrapper |
index.ts |
Barrel |
Healthcare (components/healthcare/ui/)
| File |
Exports |
tokens.ts |
Status tone maps (+ prescriptionStatusTone) |
MedicalStatusChip.tsx |
Domain status badges |
HealthcareCards.tsx |
CardShell, ClinicCard, DoctorCard, TimelineItem, StatWidget |
index.ts |
Barrel |
Naming inconsistency: Beauty uses design-system/ folder; healthcare uses ui/ folder for the same concept.
Duplicated module DS components
| Concept |
Beauty |
Healthcare |
Similarity |
| Status chip |
BeautyStatusChip |
MedicalStatusChip |
~85% |
| Card shell |
CardShell in BeautyCards |
CardShell in HealthcareCards |
~95% |
| Stat widget |
StatWidget in BeautyPrimitives |
StatWidget in HealthcareCards |
~98% |
| Timeline item |
TimelineItem in BeautyCards |
TimelineItem in HealthcareCards |
~90% |
| Search result row |
SearchResultRow in BeautyPrimitives |
(inline in public pages) |
~80% |
| Table page wrapper |
BeautyTablePage |
None |
— |
| Tokens |
design-system/tokens.ts |
ui/tokens.ts |
~90% |
8. Page Bundles
Large co-located page component files serving multiple routes:
| File |
Lines |
Routes served |
beauty/pages/public.tsx |
753 |
~20 site routes |
beauty/pages/owner.tsx |
~400+ |
~6 owner re-export routes |
beauty/pages/customer.tsx |
442 |
~20 customer routes |
beauty/pages/reception.tsx |
~350+ |
~10 reception routes |
healthcare/pages/patient.tsx |
552 |
~18 patient routes |
healthcare/pages/doctor.tsx |
537 |
~16 doctor routes |
healthcare/pages/public.tsx |
467 |
~15 site routes |
Shared page helpers
| File |
Exports |
beauty/pages/shared.tsx |
PageLoader, PageError, ServiceMetaBadges, MetricsGrid, audit helpers |
healthcare/pages/shared.tsx |
Same pattern — near copy-paste |
9. Accounting Screen Aggregators
Large reusable components (not route pages):
| File |
Lines |
Purpose |
BusinessDocsPage.tsx |
1,328 |
Document management screens |
WorkflowScreens.tsx |
1,132 |
Workflow UI (approvals, compliance) |
OperationalDocumentPage.tsx |
996 |
Voucher/document editing |
DomainScreens.tsx |
652 |
Domain-specific list screens |
SpecializedOpsPage.tsx |
~400+ |
Specialized operations |
EntityCombobox.tsx |
~200+ |
Entity picker |
CompletionScoreboard.tsx |
~150+ |
Setup progress |
These are shared within accounting only — correct boundary, but files are too large.
10. Public Site Layouts
| Component |
Module |
Unique elements |
PublicBeautyLayout.tsx |
Beauty |
--beauty-accent, beauty branding |
PublicHealthcareLayout.tsx |
Healthcare |
--health-accent, healthcare branding |
Both provide: hero header, footer, mobile nav, booking CTA. Structure ~90% identical.
11. Deprecated / Dead Components
| Component |
Status |
Action |
BeautyBusinessShell.tsx |
@deprecated in source |
Remove after confirming zero imports |
HealthStatus.tsx |
Zero imports |
Delete |
beauty/pages/mobile.tsx (MobilePortalFrame) |
Zero imports |
Delete |
12. Component Usage Matrix
| Component category |
Accounting |
Beauty |
Healthcare |
Platform |
ds/Button |
✅ |
✅ |
✅ |
❌ (uses ui) |
ds/DataTable |
✅ |
✅ |
✅ |
❌ |
ds/FormField |
✅ |
✅ |
❌ |
❌ |
ds/DatePicker |
✅ |
❌ |
❌ |
❌ |
ds/MoneyInput |
✅ |
❌ |
❌ |
❌ |
ui/Button |
❌ |
❌ |
❌ |
✅ |
| Module status chip |
❌ |
✅ |
✅ |
❌ |
| Module CardShell |
❌ |
✅ |
✅ |
❌ |
| Portal shell |
AccountingShell |
BeautyPortalShell |
HealthcarePortalShell |
AdminShell |
13. Consolidation Roadmap
Priority 1 — Unify Button/Badge (Phase 0)
Add loading prop to ds/Button; migrate ui/ consumers; deprecate ui/Button and ui/Badge.
components/portals/
├── PortalShell.tsx # Base sidebar shell (parametric nav)
├── createPortalLayout.tsx # Single factory
├── PublicSiteLayout.tsx # Parametric public layout
├── PageHelpers.tsx # Merged shared.tsx helpers
├── DomainCardShell.tsx # Parametric CardShell + StatWidget
└── DomainStatusChip.tsx # Parametric status chip
Priority 3 — Split large files (Phase 4)
- Accounting screen aggregators → one file per screen domain
- Page bundles → one file per route group (e.g.
customer/wallet.tsx)
14. Summary
| Metric |
Value |
| DS component files |
11 |
| UI (legacy) component files |
6 |
| Duplicate Button implementations |
2 |
| Duplicate Badge implementations |
2 |
| Duplicate portal factories |
2 |
| Duplicate portal shells |
3 (90% similar) |
| Duplicate shared page helpers |
2 (near copy-paste) |
| Dead components |
3 |
| Largest component file |
1,328 lines (BusinessDocsPage.tsx) |
The shared design system (components/ds/) is the correct foundation. The primary issue is parallel implementations at the module layer (beauty vs healthcare) and legacy UI coexistence at the platform layer.
Related Documents