Files
carmanagement/apps/dashboard/README.md
T
root 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
refactor: rename marketplace to storefront across the entire monorepo
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
2026-06-28 01:14:46 -04:00

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.