You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Last-mile delivery is the most expensive segment of the logistics chain. Small and mid-sized courier companies lack real-time coordination tools, leading to double-assignments, idle drivers, zero customer visibility, and wasted fuel. dispatchCore solves this by providing a centralized, real-time dispatch control platform.
Stream GPS during active delivery via geolocation hook
✅ Implemented
FR-ID-06
Update delivery status once a bid is accepted
✅ Implemented
FR-ID-07
View own delivery history with earnings
✅ Implemented
FR-ID-08
In-app messaging with dispatcher and recipient
✅ Implemented
FR-ID-09
View earnings dashboard (today, weekly, chart) computed from history
✅ Implemented
FR-ID-10
View and manage own bids
✅ Implemented
Customer
ID
Requirement
Status
FR-C-01
Track delivery live via public link (no login required)
✅ Implemented
FR-C-02
View order status timeline
✅ Implemented
FR-C-03
See a live map pin of the driver's current location
✅ Implemented
1.3 Non-Functional Requirements
Category
Requirement
Target
Latency
GPS location updates
< 500ms end-to-end
Latency
Bid notifications to dispatcher
< 300ms
Availability
System uptime
99.9%
Concurrency
Simultaneous order assignments
Zero double-assignments (ACID guarantee)
Scalability
Concurrent WebSocket connections
10,000+ per server
Scalability
GPS pings per second
1,000+ writes/sec
Security
Data isolation
Strict tenant boundary (company_id scoping)
Security
Authentication
JWT (HttpOnly cookie primary) with bearer-token fallback for strict browser environments
1.4 MoSCoW Prioritization
Priority
Features
Must Have
Multi-tenant isolation, Order CRUD, Direct assignment with locking, Live GPS tracking, Real-time events (Firebase), Marketplace listing, Bidding, Customer tracking page, Delivery history (role-scoped), In-app messaging
graph TB
subgraph Clients["Client Layer (Browser)"]
SA["SuperAdmin"]
DD["Dispatcher"]
ED["Employed Driver"]
ID["Independent Driver"]
CT["Customer"]
end
subgraph Frontend["Frontend (React 19 + Vite 7)"]
RC["React Components (TSX)"]
RR["React Router DOM v7"]
FB["Firebase SDK"]
TW["Tailwind CSS v4"]
FM["Framer Motion"]
end
subgraph Backend["Backend (Node.js + Express)"]
REST["REST API (11 route modules)"]
FA["Firebase Admin SDK"]
MW["Middleware Layer"]
end
subgraph Services["Service Layer"]
AS["Assignment Service"]
MS["Marketplace Service"]
LS["Location Service"]
RMS["Route Matching Service"]
HS["History Service"]
end
subgraph Data["Data Layer"]
ORM["Sequelize ORM v6"]
DB[("MySQL")]
RTDB[("Firebase RTDB")]
end
Clients --> Frontend
RC --> REST
FB --> RTDB
REST --> MW --> Services
FA --> RTDB
Services --> ORM --> DB
Services --> FA --> RTDB
Loading
2.2 Data Flow Diagram (Level 0 - Context)
graph LR
D["Dispatcher"] -->|"Creates orders, assigns drivers"| DC["dispatchCore"]
ED["Employed Driver"] -->|"Streams GPS, updates status"| DC
ID["Independent Driver"] -->|"Bids, registers routes, streams GPS"| DC
C["Customer"] -->|"Requests tracking"| DC
DC -->|"Live map, bid updates, messages"| D
DC -->|"Assignment notifications, messages"| ED
DC -->|"Bid results, marketplace, messages"| ID
DC -->|"Live tracking pin"| C
Loading
2.3 Component Diagram
graph TB
subgraph Presentation["Presentation Layer"]
UI["React Views (6 dashboards + extras)"]
MAP["MapLibre GL Map (react-map-gl)"]
THEME["Tailwind CSS v4 + CSS Custom Properties"]
end
subgraph Communication["Communication Layer"]
HTTP["Fetch API (custom wrapper)"]
FBC["Firebase Client SDK"]
end
subgraph API["API Layer"]
ROUTER["Express Router (11 modules)"]
FBA["Firebase Admin SDK"]
end
subgraph Business["Business Logic Layer"]
ASSIGN["Assignment Service"]
MARKET["Marketplace Service"]
LOCATE["Location Service"]
ROUTE["Route Matching Service"]
HIST["History Service"]
end
subgraph Middleware["Middleware Layer"]
TENANT["Tenant Resolver"]
VALID["Input Validator"]
ERR["Error Handler"]
RATE["Rate Limiter"]
end
subgraph Persistence["Persistence Layer"]
SEQ["Sequelize Models (13)"]
MYSQL[("MySQL")]
end
UI --> Communication --> API
API --> Middleware --> Business --> Persistence
Business --> WSS
Loading
3. Database Design
3.1 Entity Relationship Diagram
erDiagram
COMPANY ||--o{ DRIVER : "has employed"
COMPANY ||--o{ HUB : owns
COMPANY ||--o{ ORDER : receives
COMPANY ||--o{ VEHICLE : owns
DRIVER ||--o{ DRIVER_ROUTE : "pre-registers"
DRIVER ||--o{ BID : submits
DRIVER ||--o{ ASSIGNMENT : "assigned to"
DRIVER ||--o{ DRIVER_LOCATION_LOG : "streams GPS"
ORDER ||--o{ BID : "receives bids"
ORDER ||--o| ASSIGNMENT : "fulfilled by"
ORDER ||--o{ MESSAGE : "has messages"
ASSIGNMENT ||--o{ ROUTE_STOP : contains
ASSIGNMENT ||--o{ DELIVERY_EVENT : logs
COMPANY {
int id PK
string name
string email
string phone
string location
string address
string plan_type
datetime created_at
}
DRIVER {
int id PK
int company_id FK "null for independent"
string name
string email
string phone
string password_hash
enum type "EMPLOYED | INDEPENDENT"
enum status "AVAILABLE | BUSY | OFFLINE"
enum verification_status "PENDING | VERIFIED | REJECTED"
string license_number
datetime created_at
}
DRIVER_ROUTE {
int id PK
int driver_id FK
float start_lat
float start_lng
string start_address
float end_lat
float end_lng
string end_address
datetime departure_time
boolean is_active
datetime created_at
}
VEHICLE {
int id PK
int company_id FK
int driver_id FK
string plate_number
enum type "BIKE | VAN | TRUCK"
float capacity_kg
enum status "ACTIVE | MAINTENANCE | RETIRED"
}
HUB {
int id PK
int company_id FK
string name
string address
float lat
float lng
}
ORDER {
int id PK
int company_id FK
string tracking_code
enum status "UNASSIGNED | LISTED | ASSIGNED | PICKED_UP | EN_ROUTE | DELIVERED | CANCELLED"
float listed_price
float weight_kg
float pickup_lat
float pickup_lng
string pickup_address
float delivery_lat
float delivery_lng
string delivery_address
enum priority "LOW | NORMAL | HIGH | URGENT"
string recipient_name
string recipient_phone
string recipient_email
string notes
datetime created_at
}
BID {
int id PK
int order_id FK
int driver_id FK
float offered_price
enum status "PENDING | ACCEPTED | REJECTED | EXPIRED"
string message
datetime created_at
}
ASSIGNMENT {
int id PK
int order_id FK
int driver_id FK
int vehicle_id FK
int assigned_by_company_id FK
enum source "DIRECT | BID"
datetime estimated_arrival
datetime created_at
}
ROUTE_STOP {
int id PK
int assignment_id FK
int order_id FK
int sequence_number
float lat
float lng
enum status "PENDING | ARRIVED | COMPLETED | SKIPPED"
}
DRIVER_LOCATION_LOG {
int id PK
int driver_id FK
float lat
float lng
float speed
float heading
datetime recorded_at
}
DELIVERY_EVENT {
int id PK
int assignment_id FK
enum event_type "ASSIGNED | PICKED_UP | EN_ROUTE | DELIVERED | FAILED | RETURNED"
datetime timestamp
string notes
string photo_url
}
MESSAGE {
int id PK
int order_id FK
string channel "dispatcher-driver | dispatcher-recipient | driver-recipient"
string sender_type "dispatcher | driver | recipient"
int sender_id "company.id | driver.id | null"
string sender_name
text text
boolean is_read
datetime created_at
}
SUPERADMIN_SETTING {
int id PK
string admin_name
string admin_email
string theme "light | dark | system (default)"
boolean email_reports
boolean auto_approve_drivers
datetime created_at
}
sequenceDiagram
actor D as Dispatcher
participant API as REST API
participant MW as Tenant Middleware
participant AS as Assignment Service
participant DB as MySQL
participant FB as Firebase RTDB
D->>API: POST /api/orders/:id/assign {driverId, vehicleId}
API->>MW: Validate tenant scope
MW->>AS: assignOrder(orderId, driverId, vehicleId, companyId)
AS->>DB: BEGIN TRANSACTION (SERIALIZABLE)
AS->>DB: SELECT * FROM orders WHERE id=:id FOR UPDATE
DB-->>AS: Order (status: UNASSIGNED)
AS->>DB: SELECT * FROM drivers WHERE id=:driverId FOR UPDATE
DB-->>AS: Driver (status: AVAILABLE)
AS->>DB: UPDATE orders SET status='ASSIGNED'
AS->>DB: UPDATE drivers SET status='BUSY'
AS->>DB: INSERT INTO assignments
AS->>DB: INSERT INTO delivery_events (ASSIGNED)
AS->>DB: COMMIT
AS->>FB: Write assignment event to Firebase
FB-->>D: Assignment confirmed (live update)
AS-->>API: Assignment object
API-->>D: 201 Created
Loading
5.3 Sequence Diagram — Marketplace Bidding
sequenceDiagram
actor Disp as Dispatcher
actor ID1 as Independent Driver A
actor ID2 as Independent Driver B
participant API as REST API
participant MS as Marketplace Service
participant DB as MySQL
participant FB as Firebase RTDB
Disp->>API: PUT /api/orders/55/list {price: 15}
API->>MS: listOrder(55, 15)
MS->>DB: UPDATE orders SET status='LISTED', listed_price=15
MS->>FB: Write order listed event to Firebase
FB-->>ID1: New listing available
FB-->>ID2: New listing available
ID1->>API: POST /api/bids {orderId: 55, price: 12}
API->>MS: placeBid(55, driverA, 12)
MS->>DB: INSERT INTO bids
MS->>FB: Write bid event to Firebase
FB-->>Disp: New bid from Driver A
Disp->>API: PUT /api/bids/1/accept
API->>MS: acceptBid(1)
MS->>DB: BEGIN TRANSACTION
MS->>DB: UPDATE bid SET status='ACCEPTED'
MS->>DB: UPDATE remaining bids SET status='REJECTED'
MS->>DB: UPDATE order SET status='ASSIGNED'
MS->>DB: INSERT INTO assignments (source: BID)
MS->>DB: COMMIT
MS->>FB: Write bid accepted + assignment events to Firebase
FB-->>ID1: Bid accepted
FB-->>ID2: Bid rejected
FB-->>Disp: Assignment confirmed
Loading
5.4 Sequence Diagram — GPS Location Broadcasting
sequenceDiagram
actor DR as Driver
participant FBC as Firebase Client SDK
participant FBA as Firebase Admin SDK
participant LS as Location Service
participant DB as MySQL
loop Every 3 seconds (via useGeolocationPing hook)
DR->>FBC: Browser Geolocation API
FBC->>FBA: Write location to /drivers/{driverId}/location
FBA->>LS: recordPing(driverId, lat, lng, speed, heading)
LS->>DB: INSERT INTO driver_location_logs
LS->>LS: shouldBroadcast(driver)?
alt Employed Driver (always broadcast)
LS->>FBA: Write to /companies/{id}/drivers/{driverId}/location
else Independent Driver with active assignment
LS->>FBA: Write to /companies/{id}/drivers/{driverId}/location
LS->>FBA: Write to /orders/{id}/tracking/driverLocation
else Independent Driver without assignment
Note right of LS: Do NOT broadcast
end
end
Loading
5.5 Activity Diagram — Order Lifecycle
flowchart TD
A["Order Created"] --> B{"Dispatcher Decision"}
B -->|"Assign Directly"| C["Select Employed Driver"]
B -->|"List on Marketplace"| D["Set Listed Price"]
C --> E{"Driver Available?"}
E -->|"Yes"| F["Lock Order (SERIALIZABLE)"]
E -->|"No"| C
F --> G{"Lock Acquired?"}
G -->|"Yes"| H["Create Assignment"]
G -->|"No (Race Condition)"| I["Return 409 Conflict"]
D --> J["Order Status: LISTED"]
J --> K["Independent Drivers Browse"]
K --> L["Driver Places Bid"]
L --> M["Dispatcher Reviews Bids"]
M -->|"Accept"| N["Convert Bid to Assignment"]
M -->|"Reject"| L
H --> O["Order Status: ASSIGNED"]
N --> O
O --> P["Driver: PICKED_UP"]
P --> Q["Driver: EN_ROUTE"]
Q --> R["Driver: DELIVERED"]
R --> S["Log Delivery Event"]
S --> T["End"]
B -->|"Cancel"| U["Order Status: CANCELLED"]
U --> T
# Run all testscd frontend && npm run test:unit &&cd ../backend && npm run test# Frontend onlycd frontend && npm run test:unit # Run oncecd frontend && npm run test:unit -- --watch # Watch mode# Backend onlycd backend && npm run test# With coveragecd backend && npm run test -- --watch # Watch modecd backend && npm run test:unit # Unit testscd backend && npm run test:integration # Integration tests