Normalize dashboard API base URLs so bare API origins still call /api/v1 endpoints. Reuse the shared dashboard API base on email verification. Update production storefront URL examples to use the root domain and route footer, policy, and workspace storefront paths through Traefik.
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.tsapps/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/dashboardnpm run build --workspace @rentaldrivego/dashboardnpm 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:
basePathis/dashboard- browser API calls are proxied through
/dashboard/api/* - those calls are rewritten to the API origin configured by
API_INTERNAL_URLorAPI_URL - image loading allows Cloudinary plus
/storage/*assets from the API host DASHBOARD_ASSET_PREFIXcan 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:
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
langanddir - 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:
- submits credentials to
POST /auth/employee/login - receives an HttpOnly
employee_sessioncookie from the API - stores only non-sensitive employee profile/preferences client-side
- 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 theemployee_sessioncookie - unauthenticated users are redirected either to the storefront root or to
/dashboard/sign-in, depending on host context - duplicate
/dashboard/dashboardpaths 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:
OWNERMANAGERAGENT
Shell and Navigation
The private shell is defined by:
src/app/(dashboard)/layout.tsxsrc/components/layout/Sidebar.tsxsrc/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
FormDatafor 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:
- mount
- call one or more
apiFetch(...)requests - store result in local component state
- show loading and empty states
- mutate through API actions
- 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.tsxsrc/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:
- basic company info via
PATCH /companies/me - brand/public profile via
PATCH /companies/me/brand - 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.tsxsrc/app/(dashboard)/fleet/[id]/page.tsxsrc/components/VehicleCalendar.tsx
Backed by:
GET /vehiclesPOST /vehiclesGET /vehicles/:idPATCH /vehicles/:idPATCH /vehicles/:id/statusPOST /vehicles/:id/photosDELETE /vehicles/:id/photos/:idxGET /vehicles/:id/maintenancePOST /vehicles/:id/maintenanceGET /vehicles/:id/calendarPOST /vehicles/:id/calendar/blocksDELETE /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.tsxsrc/app/(dashboard)/reservations/new/page.tsxsrc/app/(dashboard)/reservations/[id]/page.tsx
Backed by:
GET /reservationsPOST /reservationsGET /reservations/:idPATCH /reservations/:idPOST /reservations/:id/confirmPOST /reservations/:id/checkinPOST /reservations/:id/checkoutPOST /reservations/:id/closePOST /reservations/:id/extendPUT /reservations/:id/inspections/:typePATCH /reservations/:id/additional-drivers/:driverId/approvalGET|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.tsxReservationPhotoSection.tsxVehicleConditionSheet.tsx
5. Online reservations
Route:
src/app/(dashboard)/online-reservations/page.tsx
Backed by:
GET /reservations?source=MARKETPLACE&status=DRAFTPOST /reservations/:id/confirmPOST /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 /customersPATCH /customers/:idPOST /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 /offersGET /offers/:idPOST /offersPATCH /offers/:idDELETE /offers/:idGET /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.tscomponents/team/InviteModal.tsxcomponents/team/EditMemberModal.tsxcomponents/team/PermissionsMatrix.tsx
Backed by:
GET /teamGET /team/statsPOST /team/invitePATCH /team/:id/rolePOST /team/:id/deactivatePOST /team/:id/reactivateDELETE /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/meGET /subscriptions/invoicesGET /subscriptions/providersGET /subscriptions/plansGET /subscriptions/featuresPOST /subscriptions/checkoutPOST /subscriptions/cancelPOST /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 /reservationsGET /payments/companyPOST /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.tsxsrc/app/(dashboard)/contracts/[id]/page.tsx
Backed by:
GET /reservationsGET /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.tsxsrc/app/(dashboard)/complaints/page.tsx
Backed by:
GET /reviewsGET /reviews/statsPATCH /reviews/:id/replyGET /complaintsPOST /complaintsPATCH /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/companyGET /notifications/historyGET /notifications/company/preferencesPOST /notifications/company/:id/readPOST /notifications/company/read-allPATCH /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/brandPOST /companies/me/brand/logoPOST /companies/me/brand/heroPOST|DELETE /companies/me/brand/custom-domainGET|PATCH /companies/me/contract-settingsGET|POST|PATCH /companies/me/insurance-policiesGET|POST|PATCH /companies/me/pricing-rulesGET|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.tsxsrc/components/layout/PublicShell.tsxsrc/components/layout/PublicHeader.tsxsrc/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:
storefrontUrllinks staff back to the public storefront domainadminUrlsupports 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:
- Preserve the
/dashboardbase path andtoDashboardAppPathnormalization behavior. - Keep route protection aligned with
src/middleware.ts. - If a new page depends on employee role, gate it both in navigation and in-page behavior.
- Reuse
apiFetchfor auth and error handling consistency. - Keep language/theme strings inside the dashboard i18n system rather than scattering literals.
- Update this README when a new major page, workflow, or integration pattern is added.