Move business logic out of App Router into module packages, add boundary validation scripts, and keep all routes as thin re-exports without changing URLs or API behavior. Co-authored-by: Cursor <cursoragent@cursor.com>
328 lines
12 KiB
Markdown
328 lines
12 KiB
Markdown
# 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)
|