Docs
Frontend layout - Tenantx kernel (React + TypeScript)

Frontend layout - Tenantx kernel (React + TypeScript)

The SPA is organized so **tenant** UI, **platform admin**, and **shared infrastructure** are obvious to extend.

Frontend layout - Tenantx kernel (React + TypeScript)

The SPA is organized so tenant UI, platform admin, and shared infrastructure are obvious to extend.

Top-level (frontend/src/)

AreaPathRole
App shell & routesApp.tsxBrowserRouter, lazy pages, protected layout
Lazy page mapcomponents/LazyComponents.tsxReact.lazy imports for every page
Permission gatecomponents/PermissionRoute.tsxSpatie permission name -> route guard
Navigationcomponents/navigation/SmartSidebar.tsxtitleKey + useHasPermission
Hookshooks/Domain subfolders (auth/, core/, ui/, subscription/, permissions/, …); entry points like @/hooks/auth/useAuth, @/hooks/core/useLanguage
API clientlib/api/ApiClient in client.ts; feature modules (authApi.ts, organizationsApi.ts, etc.) re-exported from client.ts
i18nlib/translations/en.ts / ar.ts, merged page splits, types.ts
Tenant pagespages/Kernel-only routes (dashboard, settings, org-admin, etc.)
Platform adminplatform//platform shell, platformApi, platform hooks

Import alias

Vite/TS resolve @/ -> src/ (see frontend/tsconfig*.json and frontend/vite.config.ts).

Convention: Prefer @/components/..., @/lib/api/client, @/hooks/... instead of deep relatives.

Inventory

To list files that use the @/ alias (sample of lines):

php scripts/inventory-kernel-structure.php

The JSON output includes frontend_src_ts_counts_by_top_level (.ts/.tsx counts per first segment under frontend/src/) and frontend_alias_imports_sample (large; capped in script). Use ripgrep for exact work:

rg "from ['\"]@/" frontend/src

Physical reorganization (optional)

hooks/ is grouped by domain (auth, core, ui, subscription, …). The map used for the initial hooks split is archived at scripts/frontend-reorg-map.hooks-applied.json; the active scripts/frontend-reorg-map.json defaults to [] so dry-run/apply do not expect old paths. pages/ and components/ stay mostly as before; use the map script to move more files when needed. To move additional files into subfolders (same idea as scripts/reorganize-kernel-models.php on the backend), use a JSON map and the reorganize script:

  1. Copy scripts/frontend-reorg-map.example.json to scripts/frontend-reorg-map.json (or pass --map=...).
  2. Edit the map: each entry is { "from": "hooks/useAuth.tsx", "to": "hooks/auth/useAuth.tsx" } — paths are relative to frontend/src/ (no src/ prefix). Extension may be omitted (.tsx / .ts resolved).
  3. Dry run — wrapper scripts (repo root) or PHP directly:
    • Windows: powershell -File scripts/frontend-reorg-dry-run.ps1 or pwsh -File scripts/frontend-reorg-dry-run.ps1 (optional: -Map scripts/my-map.json)
    • Bash: ./scripts/frontend-reorg-dry-run.sh (optional: MAP=scripts/my-map.json ./scripts/frontend-reorg-dry-run.sh)
    • PHP: php scripts/reorganize-kernel-frontend.php --dry-run (optional: --map=scripts/frontend-reorg-map.json)
  4. Apply — same pattern: powershell -File scripts/frontend-reorg-apply.ps1, scripts/frontend-reorg-apply.sh, or php scripts/reorganize-kernel-frontend.php --apply
  5. Verify: cd frontend && npx tsc --noEmit.

Not handled automatically: import('./foo') dynamic imports, re-exports in index.ts barrels, or relative imports that are not plain from './x' (fix manually if needed). Prefer a worktree or branch for large moves.

Suggested groupings (taxonomy only — add to the map as you prefer):

AreaPossible subfolders under src/
hooks/Done: auth/, core/, ui/, subscription/, organization/, permissions/, help-center/, users/, activity/, system/, workspaces/, notifications/, search/, utils/
pages/Already has settings/, org-admin/, subscription/; top-level route files could move under tenant/, auth/, etc.
components/Already split (settings/, layout/, ui/); further group by feature

Update components/LazyComponents.tsx and any relative imports in App.tsx (./pages/...) when you move pages — the script rewrites from './…' when resolution matches a map entry; adjust LazyComponents @/ strings if you rename modules.

Backend coupling

HTTP paths and permission names are documented in docs/PERMISSIONS.md and AGENTS.md. API types follow snake_case from Laravel; map in lib/api modules per nazim-api-domain-types skill.

Related

  • AGENTS.md - canonical architecture and API-module guidance
  • docs/I18N.md - en / ar only for SPA copy