8aab968e09
Build & Deploy / Build & Push Docker Image (push) Successful in 1m2s
Test / Type Check (all packages) (push) Failing after 28s
Test / API Unit Tests (push) Has been skipped
Test / Homepage Unit Tests (push) Has been skipped
Test / Storefront Unit Tests (push) Has been skipped
Test / Admin Unit Tests (push) Has been skipped
Test / Dashboard Unit Tests (push) Has been skipped
Test / API Integration Tests (push) Has been skipped
Build & Deploy / Deploy to VPS (push) Successful in 3s
Comprehensive rename of all marketplace references to storefront: - API module: apps/api/src/modules/marketplace/ → storefront/ - Components: MarketplaceHeader → StorefrontHeader, MarketplaceShell → StorefrontShell, MarketplaceFooter → StorefrontFooter - Types: marketplace-homepage.ts → storefront-homepage.ts - Test files: employee-marketplace-* → employee-storefront-* - All source code identifiers, imports, route paths, and strings - Documentation (docs/), CI config (.gitlab-ci.yml), scripts - Dashboard, admin, storefront workspace references - Prisma field names preserved (isListedOnMarketplace, marketplaceRating, marketplaceFunnelEvent) as they map to database schema Validation: - API type-check: 0 errors - Storefront type-check: 0 errors - Dashboard type-check: 0 errors - Full monorepo type-check: only pre-existing admin TS18046
698 lines
18 KiB
Markdown
698 lines
18 KiB
Markdown
# 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 storefront 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 storefront/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 storefront/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 storefront 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 storefront 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 storefront 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:
|
|
|
|
- `storefrontUrl` links staff back to the public storefront 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.
|