# 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`)

```typescript
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:
1. `AuthGuard`
2. `useMe()` loading/error/onboarding check
3. Tenant ID validation
4. 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`.

### Priority 2 — Extract portal infrastructure (Phase 2)

```
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

- [frontend-architecture-audit.md](./frontend-architecture-audit.md)
- [frontend-migration-plan.md](./frontend-migration-plan.md)
- [dependency-report.md](./dependency-report.md)
