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>
378 lines
12 KiB
Markdown
378 lines
12 KiB
Markdown
# TorbatYar Frontend — Import Report
|
|
|
|
**Date:** 2026-07-26
|
|
**Scope:** Import patterns, alias usage, violations, and conventions
|
|
|
|
---
|
|
|
|
## 1. Summary Statistics
|
|
|
|
| Metric | Value |
|
|
|--------|------:|
|
|
| Total source files (`.ts`/`.tsx`) | ~481 |
|
|
| Total import statements | ~811 |
|
|
| `@/` alias imports | 756 (93%) |
|
|
| Relative imports (`./`, `../`) | 55 (7%) |
|
|
| Files using `@/` alias | 445 / 481 (92%) |
|
|
| Files using relative imports | 30 / 481 (6%) |
|
|
| Files using both patterns | 25 |
|
|
| Cross-domain imports | 0 |
|
|
| Circular import chains | 0 |
|
|
|
|
**Verdict:** Import conventions are strong and consistently applied.
|
|
|
|
---
|
|
|
|
## 2. Path Alias Configuration
|
|
|
|
### tsconfig.json
|
|
|
|
```json
|
|
{
|
|
"compilerOptions": {
|
|
"paths": { "@/*": ["./*"] }
|
|
}
|
|
}
|
|
```
|
|
|
|
Single alias `@/` maps to `frontend/` root. No scoped sub-aliases exist.
|
|
|
|
### Implications
|
|
|
|
| Aspect | Effect |
|
|
|--------|--------|
|
|
| Readability | ✅ Clean absolute-style paths |
|
|
| Refactoring | ⚠️ No enforced module scoping via alias |
|
|
| Tree-shaking | Neutral — bundler resolves same as relative |
|
|
| IDE navigation | ✅ Works well |
|
|
| Boundary enforcement | ❌ Cannot restrict by alias alone — needs ESLint |
|
|
|
|
---
|
|
|
|
## 3. Import Pattern Analysis
|
|
|
|
### Pattern A — Absolute `@/` (dominant, 93%)
|
|
|
|
Used for all cross-folder imports:
|
|
|
|
```typescript
|
|
// App route → component
|
|
export { CustomerWallet as default } from "@/components/beauty/pages/customer";
|
|
|
|
// Component → design system
|
|
import { Button, DataTable, PageHeader } from "@/components/ds";
|
|
|
|
// Component → API client
|
|
import { accountingApi } from "@/lib/accounting-api";
|
|
|
|
// Component → hook
|
|
import { useTenantId } from "@/hooks/useTenantId";
|
|
|
|
// Layout → guard
|
|
import { AuthGuard } from "@/components/AuthGuard";
|
|
```
|
|
|
|
### Pattern B — Relative `./` (local composition, 7%)
|
|
|
|
Used within co-located modules:
|
|
|
|
```typescript
|
|
// Page bundle → shared helpers
|
|
import { BeautyPageLoader, BeautyPageError } from "./shared";
|
|
|
|
// DS internal
|
|
import { Button } from "./Button";
|
|
|
|
// Design system barrel
|
|
export { BeautyStatusChip } from "./BeautyStatusChip";
|
|
export { CardShell } from "./BeautyCards";
|
|
|
|
// Portal layout → shell (same module)
|
|
import { BeautyPortalShell } from "@/components/beauty/BeautyPortalShell";
|
|
// Note: even same-module sometimes uses @/ — inconsistent but harmless
|
|
```
|
|
|
|
### Pattern C — Re-export routes (beauty/healthcare)
|
|
|
|
~175 routes use single-line re-exports:
|
|
|
|
```typescript
|
|
export { CustomerWallet as default } from "@/components/beauty/pages/customer";
|
|
```
|
|
|
|
This is the **canonical thin-route pattern** for the codebase.
|
|
|
|
---
|
|
|
|
## 4. Import Frequency by Target
|
|
|
|
### Most imported modules
|
|
|
|
| Target | Approx. imports | Category |
|
|
|--------|----------------:|----------|
|
|
| `@/components/ds` | 90+ | Design system |
|
|
| `@/hooks/useTenantId` | 67 | Shared hook |
|
|
| `@/lib/utils` | 47 | Utilities |
|
|
| `@/lib/accounting-api` | 30 | Domain API |
|
|
| `@/lib/beauty-business-api` | 28 | Domain API |
|
|
| `@/lib/auth` | 15 | Platform auth |
|
|
| `@/lib/healthcare-api` | 14 | Domain API |
|
|
| `@/components/AuthGuard` | 10+ | Platform guard |
|
|
| `@/hooks/useMe` | 7 | Shared hook |
|
|
| `@tanstack/react-query` | 70+ | External |
|
|
|
|
### Most imported DS exports
|
|
|
|
| Export | Typical consumers |
|
|
|--------|-------------------|
|
|
| `Button` | All modules |
|
|
| `PageHeader` | All modules |
|
|
| `DataTable` | All modules |
|
|
| `LoadingState`, `ErrorState` | Portal layouts, pages |
|
|
| `Dialog`, `ConfirmDialog` | CRUD pages |
|
|
| `FormField`, `MoneyInput` | Accounting, beauty owner |
|
|
| `DatePicker` | Accounting |
|
|
|
|
---
|
|
|
|
## 5. Domain Import Isolation
|
|
|
|
### Accounting imports
|
|
|
|
```
|
|
✅ @/components/ds
|
|
✅ @/components/accounting/*
|
|
✅ @/lib/accounting-api
|
|
✅ @/lib/accounting-nav
|
|
✅ @/hooks/useTenantId, useMe
|
|
✅ @/lib/auth, @/lib/utils
|
|
❌ @/components/beauty/* — NOT FOUND
|
|
❌ @/components/healthcare/* — NOT FOUND
|
|
❌ @/lib/beauty-business-api — NOT FOUND
|
|
❌ @/lib/healthcare-api — NOT FOUND
|
|
```
|
|
|
|
### Beauty imports
|
|
|
|
```
|
|
✅ @/components/ds
|
|
✅ @/components/beauty/*
|
|
✅ @/lib/beauty-business-api
|
|
✅ @/lib/beauty-portals
|
|
✅ @/hooks/useTenantId, useMe, useBeautyLookups
|
|
❌ @/components/accounting/* — NOT FOUND
|
|
❌ @/components/healthcare/* — NOT FOUND
|
|
```
|
|
|
|
### Healthcare imports
|
|
|
|
```
|
|
✅ @/components/ds
|
|
✅ @/components/healthcare/*
|
|
✅ @/lib/healthcare-api
|
|
✅ @/lib/healthcare-portals
|
|
✅ @/hooks/useTenantId, useMe, useHealthcareLookups, useHealthcarePatientContext
|
|
❌ @/components/accounting/* — NOT FOUND
|
|
❌ @/components/beauty/* — NOT FOUND
|
|
```
|
|
|
|
### Platform imports
|
|
|
|
```
|
|
✅ @/components/ui — Marketing surfaces
|
|
✅ @/components/ds — Some admin/settings
|
|
✅ @/lib/api — Core backend
|
|
✅ @/lib/auth, @/lib/identity
|
|
❌ Domain module components — NOT FOUND
|
|
```
|
|
|
|
**Compliance: 100%** — no cross-domain import violations detected.
|
|
|
|
---
|
|
|
|
## 6. Shared Layer Import Compliance
|
|
|
|
### `components/ds/` imports
|
|
|
|
The design system imports only:
|
|
- `@/lib/utils` (cn helper)
|
|
- Internal relative `./` imports
|
|
- External packages (cva, lucide-react, react)
|
|
|
|
**Does NOT import:** any domain module ✅
|
|
|
|
### `hooks/` imports
|
|
|
|
| Hook | Allowed imports | Violations |
|
|
|------|-----------------|------------|
|
|
| `useMe` | `@/lib/api` | None |
|
|
| `useTenantId` | `@/hooks/useMe` | None |
|
|
| `useBeautyLookups` | `@/lib/beauty-business-api` | None (domain hook) |
|
|
| `useHealthcareLookups` | `@/lib/healthcare-api` | None (domain hook) |
|
|
| `useAuth` | `@/lib/auth` | None |
|
|
|
|
Shared hooks (useMe, useTenantId, useAuth) do not import domain APIs. ✅
|
|
|
|
---
|
|
|
|
## 7. Improper Import Patterns
|
|
|
|
### 7.1 Legacy UI in new module code
|
|
|
|
| Issue | Severity | Details |
|
|
|-------|----------|---------|
|
|
| Module code importing `components/ui/` | 🟡 Low | Not found in beauty/healthcare/accounting — compliant |
|
|
| Platform code importing `components/ui/` | ✅ Expected | Login, dashboard, homepage |
|
|
|
|
### 7.2 Deep imports bypassing barrels
|
|
|
|
Some files import directly from DS sub-files instead of barrel:
|
|
|
|
```typescript
|
|
// Bypasses barrel — found occasionally
|
|
import { Button } from "@/components/ds/Button";
|
|
// Preferred
|
|
import { Button } from "@/components/ds";
|
|
```
|
|
|
|
**Impact:** Low — does not break boundaries, reduces barrel effectiveness.
|
|
|
|
### 7.3 Same-module `@/` vs relative inconsistency
|
|
|
|
Beauty and healthcare components sometimes use `@/components/beauty/...` for same-module imports where `./` would suffice.
|
|
|
|
**Impact:** None functional; stylistic inconsistency only.
|
|
|
|
### 7.4 Server/client boundary
|
|
|
|
Most app routes are `"use client"` — no widespread server component data fetching pattern. API clients check `typeof window` for BFF vs direct URL.
|
|
|
|
**Not a violation** but limits RSC benefits.
|
|
|
|
---
|
|
|
|
## 8. External Package Imports
|
|
|
|
| Package | Import count | Modules using |
|
|
|---------|-------------|---------------|
|
|
| `react` | All files | Universal |
|
|
| `next/navigation` | ~50+ | Client routes (useRouter, redirect) |
|
|
| `next/link` | ~30+ | Navigation |
|
|
| `next/image` | ~10 | Public sites |
|
|
| `@tanstack/react-query` | ~70+ | All modules |
|
|
| `react-hook-form` | ~29 | Accounting, beauty owner |
|
|
| `@hookform/resolvers/zod` | ~29 | With react-hook-form |
|
|
| `zod` | ~35+ | Form schemas |
|
|
| `lucide-react` | ~100+ | Icons everywhere |
|
|
| `sonner` | ~40+ | Toast via import or AppProviders |
|
|
| `class-variance-authority` | 2 | ds/Button, ds/Badge |
|
|
| `dayjs` | ~5 | Date formatting |
|
|
| `jalaali-js` | 1 | ds/DatePicker |
|
|
| `framer-motion` | ~3 | Limited animation |
|
|
| `@tanstack/react-table` | 0 | **Unused** |
|
|
| `recharts` | 0 | **Unused** |
|
|
|
|
---
|
|
|
|
## 9. Import Graph Layers
|
|
|
|
```
|
|
┌─────────────────────────────────────────────────────────┐
|
|
│ app/**/page.tsx │
|
|
│ (routes — import components, occasionally inline) │
|
|
└────────────────────────┬────────────────────────────────┘
|
|
│
|
|
┌────────────────────────▼────────────────────────────────┐
|
|
│ components/{module}/pages/*.tsx │
|
|
│ components/{module}/*Shell.tsx │
|
|
│ components/accounting/*.tsx │
|
|
└────────────────────────┬────────────────────────────────┘
|
|
│
|
|
┌────────────────┼────────────────┐
|
|
│ │ │
|
|
┌───────▼──────┐ ┌───────▼──────┐ ┌───────▼──────┐
|
|
│ components/ds│ │ hooks/ │ │ lib/*-api.ts │
|
|
└───────┬──────┘ └───────┬──────┘ └───────┬──────┘
|
|
│ │ │
|
|
└────────────────┼────────────────┘
|
|
│
|
|
┌────────▼────────┐
|
|
│ lib/auth.ts │
|
|
│ lib/utils.ts │
|
|
│ lib/api.ts │
|
|
└─────────────────┘
|
|
```
|
|
|
|
No back-edges detected. Graph is a DAG.
|
|
|
|
---
|
|
|
|
## 10. False-Positive Cross-Module Names
|
|
|
|
These filenames suggest cross-module coupling but are contained within their domain:
|
|
|
|
| Route/file | Looks like | Actually imports |
|
|
|------------|------------|------------------|
|
|
| `beauty/owner/accounting-integration/page.tsx` | Accounting | `@/components/beauty/pages/owner` |
|
|
| `beauty/owner/crm-integration/page.tsx` | CRM | `@/components/beauty/pages/owner` |
|
|
| `healthcare/clinic/accounting-integration/page.tsx` | Accounting | `@/components/healthcare/pages/clinic` |
|
|
|
|
Integration **pages** refer to external systems; they do not import other frontend modules.
|
|
|
|
---
|
|
|
|
## 11. Recommended Import Conventions
|
|
|
|
### For new code
|
|
|
|
```typescript
|
|
// 1. Design system — always from barrel
|
|
import { Button, DataTable } from "@/components/ds";
|
|
|
|
// 2. Domain code — absolute @/ within module
|
|
import { BeautyPortalShell } from "@/components/beauty/BeautyPortalShell";
|
|
|
|
// 3. Co-located helpers — relative
|
|
import { PageLoader } from "./shared";
|
|
|
|
// 4. Platform utilities
|
|
import { cn, formatMoney } from "@/lib/utils";
|
|
import { useTenantId } from "@/hooks/useTenantId";
|
|
|
|
// 5. Domain API — only in own module
|
|
import { beautyBusinessApi } from "@/lib/beauty-business-api";
|
|
|
|
// 6. NEVER cross-module
|
|
// import { AccountingShell } from "@/components/accounting/..."; // in beauty
|
|
```
|
|
|
|
### Route files
|
|
|
|
```typescript
|
|
// Preferred — thin re-export
|
|
export { MyPage as default } from "@/components/{module}/pages/{portal}";
|
|
|
|
// Acceptable — dynamic params wrapper
|
|
export { default } from "@/components/{module}/pages/public";
|
|
```
|
|
|
|
---
|
|
|
|
## 12. Enforcement Plan
|
|
|
|
| Mechanism | Purpose | Phase |
|
|
|-----------|---------|-------|
|
|
| ESLint `no-restricted-imports` | Block cross-domain paths | Phase 1 |
|
|
| ESLint `no-restricted-imports` | Block `components/ui/` in domain code | Phase 1 |
|
|
| Custom script: import graph | CI validation of DAG | Phase 1 |
|
|
| Path aliases `@accounting/*` | Make boundaries explicit | Phase 2 (optional) |
|
|
| `eslint-plugin-import` ordering | Consistent import sort | Phase 2 (optional) |
|
|
|
|
---
|
|
|
|
## Related Documents
|
|
|
|
- [module-boundaries.md](./module-boundaries.md)
|
|
- [dependency-report.md](./dependency-report.md)
|
|
- [frontend-architecture-audit.md](./frontend-architecture-audit.md)
|