archetecture security fix
This commit is contained in:
@@ -0,0 +1,697 @@
|
||||
# Dashboard App
|
||||
|
||||
This document explains the purpose, structure, runtime behavior, and main feature flows of the dashboard application in `apps/dashboard`.
|
||||
|
||||
Source of truth:
|
||||
|
||||
- `apps/dashboard/src/app/*`
|
||||
- `apps/dashboard/src/components/*`
|
||||
- `apps/dashboard/src/lib/*`
|
||||
- `apps/dashboard/src/hooks/*`
|
||||
- `apps/dashboard/src/middleware.ts`
|
||||
- `apps/dashboard/next.config.js`
|
||||
|
||||
## Purpose
|
||||
|
||||
The dashboard is the internal company workspace for RentalDriveGo. It is the private B2B interface used by rental-company staff to run day-to-day operations.
|
||||
|
||||
The app covers:
|
||||
|
||||
- employee sign-in
|
||||
- onboarding after company creation
|
||||
- fleet management
|
||||
- reservations and rental lifecycle operations
|
||||
- online reservation intake
|
||||
- customer CRM
|
||||
- contracts and billing
|
||||
- subscription management
|
||||
- reviews, complaints, notifications, and settings
|
||||
|
||||
It is distinct from:
|
||||
|
||||
- the marketplace app, which is public and cross-company
|
||||
- the company site app, which is public and company-branded
|
||||
- the admin app, which is platform-internal rather than tenant-scoped
|
||||
|
||||
## Tech and Runtime
|
||||
|
||||
- framework: Next.js 14 App Router
|
||||
- ui: React 18 + Tailwind CSS
|
||||
- charts: Recharts
|
||||
- realtime notifications: `socket.io-client`
|
||||
- shared types: `@rentaldrivego/types`
|
||||
- default dev port: `3001`
|
||||
- base path: `/dashboard`
|
||||
|
||||
Defined in `package.json`:
|
||||
|
||||
- `npm run dev --workspace @rentaldrivego/dashboard`
|
||||
- `npm run build --workspace @rentaldrivego/dashboard`
|
||||
- `npm run start --workspace @rentaldrivego/dashboard`
|
||||
|
||||
## URL and Deployment Model
|
||||
|
||||
The app is designed to run under `/dashboard`, not at the domain root.
|
||||
|
||||
Key behavior from `next.config.js`:
|
||||
|
||||
- `basePath` is `/dashboard`
|
||||
- browser API calls are proxied through `/dashboard/api/*`
|
||||
- those calls are rewritten to the API origin configured by `API_INTERNAL_URL` or `API_URL`
|
||||
- image loading allows Cloudinary plus `/storage/*` assets from the API host
|
||||
- `DASHBOARD_ASSET_PREFIX` can force absolute JS/CSS asset URLs when the dashboard is served behind another frontend proxy
|
||||
|
||||
This lets the dashboard run:
|
||||
|
||||
- directly in dev on port `3001`
|
||||
- behind a reverse proxy
|
||||
- embedded under a shared marketplace/admin hostname while still loading its own chunks correctly
|
||||
|
||||
## Top-Level Structure
|
||||
|
||||
Main folders:
|
||||
|
||||
```text
|
||||
apps/dashboard/
|
||||
src/app/
|
||||
(dashboard)/ private employee workspace
|
||||
(public)/ public shell pages
|
||||
onboarding/ post-signup setup
|
||||
sign-in/ employee sign-in
|
||||
sign-up/ company signup
|
||||
forgot-password/
|
||||
reset-password/
|
||||
src/components/
|
||||
layout/ sidebar, topbar, public shell
|
||||
reservations/ inspections, photo flows, condition sheet
|
||||
team/ invite/edit modals and permissions matrix
|
||||
ui/ shared view components
|
||||
src/hooks/
|
||||
useTeam.ts team management data/actions
|
||||
src/lib/
|
||||
api.ts cookie-aware API fetch wrapper
|
||||
preferences.ts language/theme persistence scoped per employee
|
||||
dashboardPaths.ts normalizes basePath-aware routes
|
||||
urls.ts resolves marketplace/admin app links
|
||||
src/middleware.ts auth gate and redirect logic
|
||||
```
|
||||
|
||||
## Rendering Strategy
|
||||
|
||||
Most dashboard screens are client components.
|
||||
|
||||
That is a deliberate fit for the app because the dashboard relies heavily on:
|
||||
|
||||
- HttpOnly session-cookie authentication
|
||||
- cookie and embedded-host redirects
|
||||
- optimistic UI updates
|
||||
- direct REST fetches after mount
|
||||
- websocket notification updates
|
||||
- large interactive forms and modal-heavy workflows
|
||||
|
||||
The root `src/app/layout.tsx` is still server-rendered enough to:
|
||||
|
||||
- read the shared language cookie
|
||||
- set `lang` and `dir`
|
||||
- inject a theme bootstrapping script before hydration
|
||||
|
||||
## Auth and Session Model
|
||||
|
||||
The dashboard uses API-issued employee sessions stored in an HttpOnly cookie.
|
||||
|
||||
### Login flow
|
||||
|
||||
`src/app/sign-in/[[...sign-in]]/SignInPageClient.tsx`:
|
||||
|
||||
1. submits credentials to `POST /auth/employee/login`
|
||||
2. receives an HttpOnly `employee_session` cookie from the API
|
||||
3. stores only non-sensitive employee profile/preferences client-side
|
||||
4. redirects to the requested dashboard path
|
||||
|
||||
The sign-in page also supports:
|
||||
|
||||
- language and theme query params
|
||||
- embedded usage through `postMessage`
|
||||
- redirect handoff to the admin app after the API sets the admin HttpOnly session cookie
|
||||
|
||||
### Protected-route middleware
|
||||
|
||||
`src/middleware.ts` protects the app.
|
||||
|
||||
Important behavior:
|
||||
|
||||
- public dashboard routes are limited to sign-in and forgot-password flows
|
||||
- all other `/dashboard/*` routes require the `employee_session` cookie
|
||||
- unauthenticated users are redirected either to the marketplace root or to `/dashboard/sign-in`, depending on host context
|
||||
- duplicate `/dashboard/dashboard` paths are normalized back to `/dashboard`
|
||||
|
||||
This means route protection is enforced before React renders the protected pages.
|
||||
|
||||
### Role gating
|
||||
|
||||
The dashboard does not depend only on route middleware. It also performs UI-level role gating.
|
||||
|
||||
Examples:
|
||||
|
||||
- the sidebar filters navigation items by employee role
|
||||
- subscription access is restricted to `OWNER`
|
||||
- manager/owner-only actions are hidden or disabled in feature pages
|
||||
|
||||
Role rank used in the shell:
|
||||
|
||||
- `OWNER`
|
||||
- `MANAGER`
|
||||
- `AGENT`
|
||||
|
||||
## Shell and Navigation
|
||||
|
||||
The private shell is defined by:
|
||||
|
||||
- `src/app/(dashboard)/layout.tsx`
|
||||
- `src/components/layout/Sidebar.tsx`
|
||||
- `src/components/layout/TopBar.tsx`
|
||||
|
||||
### Sidebar
|
||||
|
||||
Responsibilities:
|
||||
|
||||
- tenant branding fetch from `/companies/me/brand`
|
||||
- current employee profile fetch from `/auth/employee/me`
|
||||
- role-based navigation filtering
|
||||
- language and theme controls
|
||||
- logout handling
|
||||
- communication with a parent frame when embedded
|
||||
|
||||
Main sections exposed in navigation:
|
||||
|
||||
- dashboard home
|
||||
- fleet
|
||||
- reservations
|
||||
- online reservations
|
||||
- customers
|
||||
- offers
|
||||
- team
|
||||
- reports
|
||||
- subscription
|
||||
- billing
|
||||
- contracts
|
||||
- reviews
|
||||
- complaints
|
||||
- notifications
|
||||
- settings
|
||||
|
||||
### Top bar
|
||||
|
||||
Responsibilities:
|
||||
|
||||
- current page title resolution based on normalized dashboard path
|
||||
- unread notification count
|
||||
- compact inbox dropdown
|
||||
- websocket subscription to realtime notification events
|
||||
|
||||
Notification reads are synchronized through the API and local UI events.
|
||||
|
||||
## Shared State: Language and Theme
|
||||
|
||||
The dashboard uses `DashboardI18nProvider` in `src/components/I18nProvider.tsx`.
|
||||
|
||||
It handles:
|
||||
|
||||
- current language: `en`, `fr`, `ar`
|
||||
- current theme: `light`, `dark`
|
||||
- full in-app copy dictionaries
|
||||
- persistence to cookies and `localStorage`
|
||||
- per-employee scoped preferences using non-sensitive cached profile metadata
|
||||
|
||||
`src/lib/preferences.ts` is the key utility here. It stores both:
|
||||
|
||||
- shared values like `rentaldrivego-language`
|
||||
- employee-scoped variants so one staff member does not override another on the same device
|
||||
|
||||
Arabic also flips layout direction through the root layout by setting `dir="rtl"`.
|
||||
|
||||
## Data Fetching Conventions
|
||||
|
||||
The dashboard centralizes API access in `src/lib/api.ts`.
|
||||
|
||||
### `apiFetch`
|
||||
|
||||
Browser fetch behavior:
|
||||
|
||||
- never reads authentication tokens from `localStorage`
|
||||
- sends authenticated requests with the HttpOnly session cookie
|
||||
- defaults JSON requests to `Content-Type: application/json`
|
||||
- preserves `FormData` for file uploads
|
||||
- includes credentials for cookie-aware flows
|
||||
- unwraps `{ data }` responses automatically
|
||||
|
||||
Browser base URL:
|
||||
|
||||
- `NEXT_PUBLIC_API_URL`
|
||||
- fallback `/dashboard/api/v1`
|
||||
|
||||
### `apiFetchServer`
|
||||
|
||||
Server-side helper for routes/components that explicitly receive a trusted server-side token and need `cache: 'no-store'`. Browser code should use HttpOnly cookies instead.
|
||||
|
||||
### Pattern used across pages
|
||||
|
||||
Most pages follow the same client-side pattern:
|
||||
|
||||
1. mount
|
||||
2. call one or more `apiFetch(...)` requests
|
||||
3. store result in local component state
|
||||
4. show loading and empty states
|
||||
5. mutate through API actions
|
||||
6. refetch or patch local state
|
||||
|
||||
There is intentionally very little abstraction beyond this. The code favors explicit page-level orchestration over a large client data framework.
|
||||
|
||||
## Feature Areas
|
||||
|
||||
### 1. Company signup and onboarding
|
||||
|
||||
Relevant routes:
|
||||
|
||||
- `src/app/sign-up/[[...sign-up]]/page.tsx`
|
||||
- `src/app/onboarding/page.tsx`
|
||||
|
||||
The signup screen creates the company through:
|
||||
|
||||
- `POST /auth/company/signup`
|
||||
|
||||
The onboarding flow then completes the initial tenant setup in three steps:
|
||||
|
||||
1. basic company info via `PATCH /companies/me`
|
||||
2. brand/public profile via `PATCH /companies/me/brand`
|
||||
3. payment and marketplace settings via `PATCH /companies/me/brand`
|
||||
|
||||
This makes onboarding a dashboard-owned continuation of the API signup flow.
|
||||
|
||||
### 2. Dashboard home
|
||||
|
||||
Route:
|
||||
|
||||
- `src/app/(dashboard)/page.tsx`
|
||||
|
||||
Backed by:
|
||||
|
||||
- `GET /analytics/dashboard`
|
||||
|
||||
Primary contents:
|
||||
|
||||
- KPI cards
|
||||
- source breakdown chart
|
||||
- subscription-status banner
|
||||
- recent reservations list
|
||||
|
||||
Revenue visibility is reduced for lower-privilege roles.
|
||||
|
||||
### 3. Fleet
|
||||
|
||||
Routes:
|
||||
|
||||
- `src/app/(dashboard)/fleet/page.tsx`
|
||||
- `src/app/(dashboard)/fleet/[id]/page.tsx`
|
||||
- `src/components/VehicleCalendar.tsx`
|
||||
|
||||
Backed by:
|
||||
|
||||
- `GET /vehicles`
|
||||
- `POST /vehicles`
|
||||
- `GET /vehicles/:id`
|
||||
- `PATCH /vehicles/:id`
|
||||
- `PATCH /vehicles/:id/status`
|
||||
- `POST /vehicles/:id/photos`
|
||||
- `DELETE /vehicles/:id/photos/:idx`
|
||||
- `GET /vehicles/:id/maintenance`
|
||||
- `POST /vehicles/:id/maintenance`
|
||||
- `GET /vehicles/:id/calendar`
|
||||
- `POST /vehicles/:id/calendar/blocks`
|
||||
- `DELETE /vehicles/:id/calendar/blocks/:blockId`
|
||||
|
||||
Key responsibilities:
|
||||
|
||||
- create and edit vehicles
|
||||
- upload vehicle images
|
||||
- publish/unpublish vehicles
|
||||
- change operational status
|
||||
- record maintenance
|
||||
- visualize reservations and manual blocks on a calendar
|
||||
- configure pickup and dropoff location rules
|
||||
|
||||
### 4. Reservations
|
||||
|
||||
Routes:
|
||||
|
||||
- `src/app/(dashboard)/reservations/page.tsx`
|
||||
- `src/app/(dashboard)/reservations/new/page.tsx`
|
||||
- `src/app/(dashboard)/reservations/[id]/page.tsx`
|
||||
|
||||
Backed by:
|
||||
|
||||
- `GET /reservations`
|
||||
- `POST /reservations`
|
||||
- `GET /reservations/:id`
|
||||
- `PATCH /reservations/:id`
|
||||
- `POST /reservations/:id/confirm`
|
||||
- `POST /reservations/:id/checkin`
|
||||
- `POST /reservations/:id/checkout`
|
||||
- `POST /reservations/:id/close`
|
||||
- `POST /reservations/:id/extend`
|
||||
- `PUT /reservations/:id/inspections/:type`
|
||||
- `PATCH /reservations/:id/additional-drivers/:driverId/approval`
|
||||
- `GET|POST|DELETE /reservations/:id/photos*`
|
||||
|
||||
This is the heaviest operational area of the dashboard.
|
||||
|
||||
Major workflows:
|
||||
|
||||
- create draft bookings
|
||||
- choose existing or new customers
|
||||
- upload and manage customer license images
|
||||
- confirm/check-in/check-out/close rentals
|
||||
- edit booking vs return data based on reservation workflow state
|
||||
- complete check-in/check-out inspections
|
||||
- upload pickup and dropoff photos
|
||||
- extend rentals
|
||||
- approve additional drivers
|
||||
|
||||
Supporting reservation UI components:
|
||||
|
||||
- `DamageInspectionCard.tsx`
|
||||
- `ReservationPhotoSection.tsx`
|
||||
- `VehicleConditionSheet.tsx`
|
||||
|
||||
### 5. Online reservations
|
||||
|
||||
Route:
|
||||
|
||||
- `src/app/(dashboard)/online-reservations/page.tsx`
|
||||
|
||||
Backed by:
|
||||
|
||||
- `GET /reservations?source=MARKETPLACE&status=DRAFT`
|
||||
- `POST /reservations/:id/confirm`
|
||||
- `POST /reservations/:id/cancel`
|
||||
|
||||
This page is the triage surface for new public booking requests before staff converts them into active operational reservations.
|
||||
|
||||
### 6. Customers
|
||||
|
||||
Route:
|
||||
|
||||
- `src/app/(dashboard)/customers/page.tsx`
|
||||
|
||||
Backed mainly by:
|
||||
|
||||
- `GET /customers`
|
||||
|
||||
The reservations-create flow also uses:
|
||||
|
||||
- `POST /customers`
|
||||
- `PATCH /customers/:id`
|
||||
- `POST /customers/:id/license-image`
|
||||
|
||||
The customer surface acts as a company-local CRM rather than a global renter account manager.
|
||||
|
||||
### 7. Offers
|
||||
|
||||
Route:
|
||||
|
||||
- `src/app/(dashboard)/offers/page.tsx`
|
||||
|
||||
Backed by:
|
||||
|
||||
- `GET /offers`
|
||||
- `GET /offers/:id`
|
||||
- `POST /offers`
|
||||
- `PATCH /offers/:id`
|
||||
- `DELETE /offers/:id`
|
||||
- `GET /vehicles`
|
||||
|
||||
Purpose:
|
||||
|
||||
- create and edit discounts
|
||||
- target specific vehicles
|
||||
- manage promo details and active/public state
|
||||
|
||||
### 8. Team
|
||||
|
||||
Route:
|
||||
|
||||
- `src/app/(dashboard)/team/page.tsx`
|
||||
|
||||
Supporting hook and components:
|
||||
|
||||
- `src/hooks/useTeam.ts`
|
||||
- `components/team/InviteModal.tsx`
|
||||
- `components/team/EditMemberModal.tsx`
|
||||
- `components/team/PermissionsMatrix.tsx`
|
||||
|
||||
Backed by:
|
||||
|
||||
- `GET /team`
|
||||
- `GET /team/stats`
|
||||
- `POST /team/invite`
|
||||
- `PATCH /team/:id/role`
|
||||
- `POST /team/:id/deactivate`
|
||||
- `POST /team/:id/reactivate`
|
||||
- `DELETE /team/:id`
|
||||
|
||||
This area centralizes employee lifecycle management.
|
||||
|
||||
### 9. Reports
|
||||
|
||||
Route:
|
||||
|
||||
- `src/app/(dashboard)/reports/page.tsx`
|
||||
|
||||
Backed by:
|
||||
|
||||
- `GET /analytics/report?period=...`
|
||||
|
||||
Purpose:
|
||||
|
||||
- revenue and operational analytics
|
||||
- summarized reporting views
|
||||
|
||||
### 10. Subscription
|
||||
|
||||
Route:
|
||||
|
||||
- `src/app/(dashboard)/subscription/page.tsx`
|
||||
|
||||
Backed by:
|
||||
|
||||
- `GET /subscriptions/me`
|
||||
- `GET /subscriptions/invoices`
|
||||
- `GET /subscriptions/providers`
|
||||
- `GET /subscriptions/plans`
|
||||
- `GET /subscriptions/features`
|
||||
- `POST /subscriptions/checkout`
|
||||
- `POST /subscriptions/cancel`
|
||||
- `POST /subscriptions/resume`
|
||||
|
||||
This is owner-only and manages the SaaS relationship between the company and RentalDriveGo.
|
||||
|
||||
Key functions:
|
||||
|
||||
- current plan visibility
|
||||
- plan comparison
|
||||
- provider availability checks
|
||||
- checkout redirection
|
||||
- invoice history
|
||||
- cancel/resume lifecycle
|
||||
|
||||
### 11. Billing
|
||||
|
||||
Route:
|
||||
|
||||
- `src/app/(dashboard)/billing/page.tsx`
|
||||
|
||||
Backed by:
|
||||
|
||||
- `GET /reservations`
|
||||
- `GET /payments/company`
|
||||
- `POST /payments/reservations/:id/manual`
|
||||
|
||||
This page handles customer-side rental billing, not SaaS subscription billing.
|
||||
|
||||
It focuses on:
|
||||
|
||||
- reservation invoice state
|
||||
- collected vs outstanding balances
|
||||
- payment history
|
||||
- manual payment recording
|
||||
- quick jumps to reservation and contract pages
|
||||
|
||||
### 12. Contracts
|
||||
|
||||
Routes:
|
||||
|
||||
- `src/app/(dashboard)/contracts/page.tsx`
|
||||
- `src/app/(dashboard)/contracts/[id]/page.tsx`
|
||||
|
||||
Backed by:
|
||||
|
||||
- `GET /reservations`
|
||||
- `GET /reservations/:id/contract`
|
||||
|
||||
Purpose:
|
||||
|
||||
- list reservations with contract/invoice state
|
||||
- generate and reopen rental-contract views
|
||||
- present printable contract payloads for staff operations
|
||||
|
||||
### 13. Reviews and complaints
|
||||
|
||||
Routes:
|
||||
|
||||
- `src/app/(dashboard)/reviews/page.tsx`
|
||||
- `src/app/(dashboard)/complaints/page.tsx`
|
||||
|
||||
Backed by:
|
||||
|
||||
- `GET /reviews`
|
||||
- `GET /reviews/stats`
|
||||
- `PATCH /reviews/:id/reply`
|
||||
- `GET /complaints`
|
||||
- `POST /complaints`
|
||||
- `PATCH /complaints/:id`
|
||||
|
||||
These pages give staff post-rental quality and dispute management tools.
|
||||
|
||||
### 14. Notifications
|
||||
|
||||
Route:
|
||||
|
||||
- `src/app/(dashboard)/notifications/page.tsx`
|
||||
|
||||
Backed by:
|
||||
|
||||
- `GET /notifications/company`
|
||||
- `GET /notifications/history`
|
||||
- `GET /notifications/company/preferences`
|
||||
- `POST /notifications/company/:id/read`
|
||||
- `POST /notifications/company/read-all`
|
||||
- `PATCH /notifications/company/preferences`
|
||||
|
||||
This page complements the top-bar notification dropdown by adding:
|
||||
|
||||
- full inbox view
|
||||
- delivery history audit
|
||||
- per-event, per-channel preference management
|
||||
|
||||
### 15. Settings
|
||||
|
||||
Route:
|
||||
|
||||
- `src/app/(dashboard)/settings/page.tsx`
|
||||
|
||||
Backed by:
|
||||
|
||||
- `GET|PATCH /companies/me/brand`
|
||||
- `POST /companies/me/brand/logo`
|
||||
- `POST /companies/me/brand/hero`
|
||||
- `POST|DELETE /companies/me/brand/custom-domain`
|
||||
- `GET|PATCH /companies/me/contract-settings`
|
||||
- `GET|POST|PATCH /companies/me/insurance-policies`
|
||||
- `GET|POST|PATCH /companies/me/pricing-rules`
|
||||
- `GET|PATCH /companies/me/accounting-settings`
|
||||
|
||||
This is the broad configuration surface for the tenant.
|
||||
|
||||
It includes:
|
||||
|
||||
- public brand and marketplace profile
|
||||
- payment account identifiers
|
||||
- custom domain setup
|
||||
- rental policies
|
||||
- insurance products
|
||||
- pricing rules
|
||||
- accounting defaults
|
||||
|
||||
## Public Shell Pages
|
||||
|
||||
Public-facing dashboard pages live outside the private shell.
|
||||
|
||||
Notable routes:
|
||||
|
||||
- `/dashboard/sign-in`
|
||||
- `/dashboard/sign-up`
|
||||
- `/dashboard/forgot-password`
|
||||
- `/dashboard/reset-password`
|
||||
- `/dashboard/onboarding`
|
||||
|
||||
Layout:
|
||||
|
||||
- `src/app/(public)/layout.tsx`
|
||||
- `src/components/layout/PublicShell.tsx`
|
||||
- `src/components/layout/PublicHeader.tsx`
|
||||
- `src/components/layout/PublicFooter.tsx`
|
||||
|
||||
These pages share the branding system and language/theme controls but do not render the private sidebar/topbar shell.
|
||||
|
||||
## Embedded and Cross-App Behavior
|
||||
|
||||
The dashboard supports embedded scenarios.
|
||||
|
||||
Examples in the code:
|
||||
|
||||
- sign-in posts messages to the parent frame
|
||||
- logout posts an employee-logout event
|
||||
- current embedded path is reported upward
|
||||
|
||||
It also coordinates with sibling apps:
|
||||
|
||||
- `marketplaceUrl` links staff back to the public marketplace domain
|
||||
- `adminUrl` supports cookie-based handoff into the admin interface
|
||||
- host-aware URL rewriting allows the same app to function under external domains, internal Docker hostnames, and proxied environments
|
||||
|
||||
## API Dependency Summary
|
||||
|
||||
The dashboard is a thin client over the company API. Its main backend dependencies are:
|
||||
|
||||
- auth: employee login and profile
|
||||
- companies: brand, contract settings, pricing rules, accounting, domains
|
||||
- vehicles: fleet CRUD, photos, maintenance, availability calendar
|
||||
- reservations: booking lifecycle and inspections
|
||||
- customers: CRM and license documents
|
||||
- offers: promotions
|
||||
- team: employee management
|
||||
- analytics: dashboard and reports
|
||||
- notifications: inbox, history, preferences
|
||||
- subscriptions: SaaS plan management
|
||||
- payments: rental payment recording
|
||||
- reviews and complaints: post-rental follow-up
|
||||
|
||||
The frontend does not try to reproduce business rules locally. It delegates workflow authority to the API and reflects the resulting state.
|
||||
|
||||
## Design Characteristics
|
||||
|
||||
The current dashboard code favors:
|
||||
|
||||
- explicit page-local state over a global data layer
|
||||
- direct REST integration over generated clients
|
||||
- strong tenant scoping through backend auth rather than frontend assumptions
|
||||
- rich operational forms over minimal CRUD scaffolds
|
||||
- multilingual support directly in the app shell
|
||||
|
||||
Tradeoffs of this approach:
|
||||
|
||||
- easy to reason about per page
|
||||
- low indirection when debugging API interactions
|
||||
- more repeated fetch/state patterns than a centralized query layer would have
|
||||
- large single-file page components in the heaviest screens
|
||||
|
||||
## Recommended Maintenance Rules
|
||||
|
||||
When changing the dashboard, keep these rules in mind:
|
||||
|
||||
1. Preserve the `/dashboard` base path and `toDashboardAppPath` normalization behavior.
|
||||
2. Keep route protection aligned with `src/middleware.ts`.
|
||||
3. If a new page depends on employee role, gate it both in navigation and in-page behavior.
|
||||
4. Reuse `apiFetch` for auth and error handling consistency.
|
||||
5. Keep language/theme strings inside the dashboard i18n system rather than scattering literals.
|
||||
6. Update this README when a new major page, workflow, or integration pattern is added.
|
||||
Reference in New Issue
Block a user