890 lines
20 KiB
Markdown
890 lines
20 KiB
Markdown
# Doormile Hub Console — Frontend Integration Guide
|
|
|
|
**Base URL:** `https://api.doormile.com`
|
|
**Auth:** Bearer token in `Authorization` header on every authenticated request
|
|
**Content-Type:** `application/json` on all POST / PATCH calls
|
|
|
|
## How the flow works
|
|
|
|
1. Staff opens the console → Login page
|
|
2. `POST /api/v1/hub/login` → receives JWT + hub context
|
|
3. Store token + hub context in localStorage
|
|
4. Every page reads hub name/city from stored context (no extra call)
|
|
5. Every API call sends `Authorization: Bearer <token>`
|
|
6. On 401 → clear localStorage → redirect to `/login`
|
|
|
|
---
|
|
|
|
## 1. Authentication
|
|
|
|
### POST /api/v1/hub/login
|
|
No auth header needed.
|
|
|
|
Request body:
|
|
|
|
```json
|
|
{
|
|
"email": "hub.coimbatore@doormile.in",
|
|
"password": "password123"
|
|
}
|
|
```
|
|
|
|
Response:
|
|
|
|
```json
|
|
{
|
|
"success": true,
|
|
"token": "eyJhbGci...",
|
|
"hub": {
|
|
"hubid": 1,
|
|
"hubname": "Coimbatore Jupiter Hub",
|
|
"hubtype": "sorting_center",
|
|
"city": "Coimbatore",
|
|
"capacity": 50
|
|
},
|
|
"staff": {
|
|
"displayname": "Coimbatore Hub Staff",
|
|
"email": "hub.coimbatore@doormile.in",
|
|
"role": 6
|
|
},
|
|
"is_doormile_staff": true
|
|
}
|
|
```
|
|
|
|
What to store in localStorage after login:
|
|
|
|
```js
|
|
localStorage.setItem('hub_token', data.token)
|
|
localStorage.setItem('hub_context', JSON.stringify(data.hub))
|
|
localStorage.setItem('hub_staff', JSON.stringify(data.staff))
|
|
localStorage.setItem('hub_is_doormile', String(data.is_doormile_staff))
|
|
```
|
|
|
|
Test accounts:
|
|
|
|
| Email | Password | Hub | Access |
|
|
|---|---|---|---|
|
|
| hub.coimbatore@doormile.in | password123 | Coimbatore Jupiter Hub | Full (Doormile staff) |
|
|
| hub.hyderabad@doormile.in | password123 | Hyderabad hub | Full (Doormile staff) |
|
|
| hub.bangalore@doormile.in | password123 | Bangalore hub | Full (Doormile staff) |
|
|
| hub.chennai@doormile.in | password123 | Chennai hub | Full (Doormile staff) |
|
|
| hub@kpmtravels.in | password123 | Hub 1 | Restricted (partner — no hub management) |
|
|
|
|
---
|
|
|
|
## 2. Dashboard
|
|
|
|
### GET /api/v1/hub/dashboard
|
|
Returns live KPI stats for the logged-in staff's hub.
|
|
|
|
Response:
|
|
|
|
```json
|
|
{
|
|
"success": true,
|
|
"data": {
|
|
"parcels_received_today": 142,
|
|
"milers_available": 6,
|
|
"milers_on_duty": 12,
|
|
"pending_pickups": 8,
|
|
"batches_sent_today": 3,
|
|
"exceptions": 2
|
|
}
|
|
}
|
|
```
|
|
|
|
Which UI element uses which field:
|
|
|
|
| Dashboard card | Field |
|
|
|---|---|
|
|
| Total Parcels Received | `parcels_received_today` |
|
|
| Available Milers | `milers_available` |
|
|
| Milers on Duty | `milers_on_duty` |
|
|
| Pending Pickups | `pending_pickups` |
|
|
| Batches Sent Today | `batches_sent_today` |
|
|
| Needs Checking | `exceptions` |
|
|
|
|
**Note:** Hub name shown in the header/title comes from localStorage (`hub_context.hubname`) — not from this endpoint. No extra call needed.
|
|
|
|
---
|
|
|
|
## 3. Inbound — Receive Parcels
|
|
|
|
### GET /api/v1/hub/inbound/today
|
|
Returns all parcels inbounded at this hub today. Now returns `sendername`, `originname`, `destinationname`, `temperature` (readable names instead of raw IDs and pincodes).
|
|
|
|
Response:
|
|
|
|
```json
|
|
{
|
|
"success": true,
|
|
"data": [
|
|
{
|
|
"bookingid": 15,
|
|
"trackingnumber": "DM-882201",
|
|
"sendername": "Acme Corp",
|
|
"originname": "Mumbai Hub",
|
|
"destinationname": "Dwarka Sec 12, Coimbatore",
|
|
"weight": "2.4 kg",
|
|
"condition": "Good",
|
|
"temperature": "N/A",
|
|
"shelf": "Zone A (Shelf 1)",
|
|
"inboundedat": "2026-07-04T09:15:00Z"
|
|
}
|
|
],
|
|
"total": 1
|
|
}
|
|
```
|
|
|
|
### POST /api/v1/hub/bookings/:id/inbound
|
|
Scan a parcel in. `:id` is the booking/consignment ID.
|
|
|
|
Request body:
|
|
|
|
```json
|
|
{
|
|
"tracking_id": "DM-882201",
|
|
"condition": "Good",
|
|
"temperature": "N/A",
|
|
"shelf": "Zone A (Shelf 1)",
|
|
"weight": "2.4 kg"
|
|
}
|
|
```
|
|
|
|
Condition values: `Good` · `Damaged Box` · `Wet / Crushed` · `Missing Label`
|
|
Temperature: Free text — "4.1°C" for cold chain items, "N/A" for dry goods
|
|
|
|
Response:
|
|
|
|
```json
|
|
{
|
|
"success": true,
|
|
"data": {
|
|
"bookingid": 15,
|
|
"trackingnumber": "DM-882201",
|
|
"recommended_shelf": "Zone A (Shelf 1)",
|
|
"status": "At_Hub"
|
|
}
|
|
}
|
|
```
|
|
|
|
Shelf recommendation logic (backend handles this automatically):
|
|
|
|
- Condition contains Damaged / Wet / Missing → Exception Area
|
|
- Temperature is not N/A → Zone C (Cold Room)
|
|
- Otherwise → Zone A or Zone B based on destination
|
|
|
|
Error — tracking ID not found:
|
|
|
|
```json
|
|
{
|
|
"success": false,
|
|
"message": "booking not found"
|
|
}
|
|
```
|
|
|
|
HTTP 404.
|
|
|
|
---
|
|
|
|
## 4. Order Assignment — Pickup Requests
|
|
|
|
### GET /api/v1/hub/bookings/unassigned
|
|
Returns all pending pickup requests for this hub's city.
|
|
|
|
Response:
|
|
|
|
```json
|
|
{
|
|
"success": true,
|
|
"data": [
|
|
{
|
|
"bookingid": 21,
|
|
"customerName": "Ramesh K.",
|
|
"pickupaddress": "Gandhipuram, Coimbatore",
|
|
"deliveryaddress": "Hitech City, Hyderabad",
|
|
"packagedescription": "Small Box, 2kg",
|
|
"createdat": "2026-07-04T08:45:00Z",
|
|
"status": "Pending_Pickup"
|
|
}
|
|
],
|
|
"total": 3
|
|
}
|
|
```
|
|
|
|
For assigning a miler to these bookings, see **Section 14 — Hub Assignment Override**.
|
|
|
|
---
|
|
|
|
## 5. Dispatch & Batches
|
|
|
|
### GET /api/v1/hub/batches
|
|
Returns all outgoing batches for this hub.
|
|
|
|
Response:
|
|
|
|
```json
|
|
{
|
|
"success": true,
|
|
"data": [
|
|
{
|
|
"tripsheetid": 1,
|
|
"batchlabel": "BATCH-9281",
|
|
"batchkind": "transfer",
|
|
"destinationlabel": "Mumbai Hub (BOM-02)",
|
|
"vehicle": "Truck DL-01-BZ-8055",
|
|
"itemcount": 154,
|
|
"status": "Draft",
|
|
"createdat": "2026-07-04T08:00:00Z",
|
|
"dispatchtime": null
|
|
}
|
|
],
|
|
"total": 1
|
|
}
|
|
```
|
|
|
|
Status values: `Draft` → `Ready` → `Dispatched`
|
|
Batchkind values: `local` · `transfer`
|
|
|
|
### POST /api/v1/hub/batches
|
|
Create a new outgoing batch.
|
|
|
|
Request body:
|
|
|
|
```json
|
|
{
|
|
"route": "Transfer to Mumbai Hub",
|
|
"destination": "Mumbai Hub (BOM-02)",
|
|
"vehicle": "Truck DL-01-BZ-8055",
|
|
"parcels_count": 154,
|
|
"kind": "transfer"
|
|
}
|
|
```
|
|
|
|
Response:
|
|
|
|
```json
|
|
{
|
|
"success": true,
|
|
"data": {
|
|
"tripsheetid": 2,
|
|
"batchlabel": "BATCH-9282",
|
|
"status": "Draft"
|
|
}
|
|
}
|
|
```
|
|
|
|
### PATCH /api/v1/hub/batches/:id/status
|
|
Move a batch through its status flow.
|
|
|
|
Request body — mark as ready:
|
|
|
|
```json
|
|
{ "status": "Ready" }
|
|
```
|
|
|
|
Request body — dispatch (send out):
|
|
|
|
```json
|
|
{ "status": "Dispatched" }
|
|
```
|
|
|
|
Response:
|
|
|
|
```json
|
|
{
|
|
"success": true,
|
|
"data": {
|
|
"tripsheetid": 1,
|
|
"status": "Ready",
|
|
"dispatchtime": null
|
|
}
|
|
}
|
|
```
|
|
|
|
Invalid status transition → 400:
|
|
|
|
```json
|
|
{
|
|
"success": false,
|
|
"message": "invalid status"
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 6. Milers
|
|
|
|
### GET /api/v1/hub/milers
|
|
Returns all milers assigned to this hub, now with operational stats per miler.
|
|
|
|
Response:
|
|
|
|
```json
|
|
{
|
|
"success": true,
|
|
"data": [
|
|
{
|
|
"userid": 4,
|
|
"displayname": "Rajesh Kumar",
|
|
"phone": "+91 98765 43210",
|
|
"vehicleid": 1,
|
|
"hubid": 1,
|
|
"availabilitystatus": "Available",
|
|
"rating": 4.8,
|
|
"completedorders": 45,
|
|
"cancelledorders": 2,
|
|
"currentlat": 11.0168,
|
|
"currentlon": 76.9558,
|
|
"device_token": "fcm_token_here",
|
|
"zones": ["641001", "641012"],
|
|
"assignedload": 2,
|
|
"capacity": 30,
|
|
"pickupspending": 1,
|
|
"codcollected": 4500.00,
|
|
"codpending": 1200.00,
|
|
"checkinat": "2026-07-04T08:12:00Z",
|
|
"hoursactive": 6.4,
|
|
"isverified": true
|
|
}
|
|
],
|
|
"total": 6
|
|
}
|
|
```
|
|
|
|
Availability status values: `Available` · `Assigned` · `On_Break` · `Offline`
|
|
|
|
The `zones`, `assignedload`, `capacity`, `pickupspending`, `codcollected`, `codpending`, `checkinat`, `hoursactive`, `isverified` fields were the empty fields the Milers page was trying to show. Now populated from real data.
|
|
|
|
### POST /api/v1/admin/milers
|
|
Create (onboard) a new miler. Hub JWT is accepted here.
|
|
|
|
Request body:
|
|
|
|
```json
|
|
{
|
|
"displayname": "Muthu Kumar",
|
|
"phone": "+91 90011 22334",
|
|
"hubid": 1,
|
|
"vehicleid": 2,
|
|
"availabilitystatus": "Available"
|
|
}
|
|
```
|
|
|
|
Response:
|
|
|
|
```json
|
|
{
|
|
"success": true,
|
|
"data": {
|
|
"userid": 10,
|
|
"displayname": "Muthu Kumar"
|
|
}
|
|
}
|
|
```
|
|
|
|
### PATCH /api/v1/admin/milers/:id
|
|
Update a miler's details or status.
|
|
|
|
Request body (any subset of fields):
|
|
|
|
```json
|
|
{
|
|
"availabilitystatus": "On_Break",
|
|
"hubid": 2
|
|
}
|
|
```
|
|
|
|
### DELETE /api/v1/admin/milers/:id
|
|
Remove a miler from the roster.
|
|
|
|
Response:
|
|
|
|
```json
|
|
{
|
|
"success": true,
|
|
"message": "miler removed"
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 7. Routing — Where Does It Go?
|
|
|
|
### GET /api/v1/hub/routing/:trackingno
|
|
Scan a parcel and get its sort destination. `:trackingno` is the tracking number scanned at the sort station.
|
|
|
|
Response:
|
|
|
|
```json
|
|
{
|
|
"success": true,
|
|
"data": {
|
|
"trackingno": "DM-TRK-0015",
|
|
"destination": "Peelamedu, 641004",
|
|
"recommendedshelf": "Zone B",
|
|
"nexthop": "Local delivery",
|
|
"condition": "Good",
|
|
"iscoldchain": false,
|
|
"weight": "2.4 kg",
|
|
"customername": "Ramesh K.",
|
|
"bookingid": 15
|
|
}
|
|
}
|
|
```
|
|
|
|
`nexthop` values: `Local delivery` · `Transfer to Mumbai Hub` (or whichever destination hub).
|
|
Use `recommendedshelf` to show the big shelf indicator UI.
|
|
|
|
Error — tracking number not found → 404. Show a "Parcel not found" error state.
|
|
|
|
```json
|
|
{
|
|
"success": false,
|
|
"message": "parcel not found"
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 8. Rider Routes
|
|
|
|
### GET /api/v1/hub/rider-routes
|
|
Returns all milers at this hub with their planned stops today.
|
|
|
|
### GET /api/v1/hub/milers/:id/route
|
|
Returns the planned stops for a single miler. `:id` is the miler user ID.
|
|
|
|
Response (same `data[]` shape for both; the single-miler endpoint returns one entry):
|
|
|
|
```json
|
|
{
|
|
"success": true,
|
|
"data": [
|
|
{
|
|
"mileruserid": 4,
|
|
"milername": "Rajesh Kumar",
|
|
"mode": "pickup",
|
|
"totalstops": 5,
|
|
"completedstops": 2,
|
|
"totaldistance_km": 12.4,
|
|
"stops": [
|
|
{ "seq": 1, "address": "Gandhipuram, Coimbatore", "lat": 11.0168, "lon": 76.9558, "items": 1, "bookingid": 20, "status": "completed", "eta_minutes": 0 },
|
|
{ "seq": 2, "address": "RS Puram, Coimbatore", "lat": 11.001, "lon": 76.951, "items": 2, "bookingid": 21, "status": "pending", "eta_minutes": 12 }
|
|
]
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
`mode` values: `pickup` · `delivery`
|
|
Stop status values: `pending` · `in_progress` · `completed`
|
|
|
|
Pass `stops[].lat/lon` to OSRM to draw the route line — the OSRM call stays client-side.
|
|
|
|
---
|
|
|
|
## 9. Live Trucks
|
|
|
|
### GET /api/v1/hub/tripsheets/in-transit
|
|
Returns transfer trucks currently in transit, with interpolated live positions.
|
|
Poll this every 5 seconds alongside miler locations.
|
|
|
|
Response:
|
|
|
|
```json
|
|
{
|
|
"success": true,
|
|
"data": [
|
|
{
|
|
"tripsheetid": 3,
|
|
"tripsheetno": "DM-TS-0003",
|
|
"label": "Chennai Jupiter Hub → Coimbatore Jupiter Hub",
|
|
"originlat": 13.0827, "originlon": 80.2707,
|
|
"destlat": 11.0168, "destlon": 76.9558,
|
|
"currentlat": 12.1, "currentlon": 78.9,
|
|
"status": "In_Transit",
|
|
"progresspct": 40,
|
|
"vehicleno": "TN-04-AX-8822",
|
|
"itemcount": 154,
|
|
"eta_minutes": 180
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
`currentlat/lon` gives the truck's position on the map. Replace the fake animated truck with these real coordinates.
|
|
|
|
---
|
|
|
|
## 10. Arriving Vehicles
|
|
|
|
### GET /api/v1/hub/inbound/vehicles
|
|
Returns trucks arriving at this hub (Dashboard → "Trucks Arriving" table).
|
|
|
|
Response:
|
|
|
|
```json
|
|
{
|
|
"success": true,
|
|
"data": [
|
|
{
|
|
"vehicleno": "TN-04-AX-8822",
|
|
"origin": "Chennai Jupiter Hub",
|
|
"tripsheetid": 3,
|
|
"status": "On the way",
|
|
"eta": "2 hrs 30 min",
|
|
"unloadedpct": 0,
|
|
"itemcount": 154
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
Status values: `On the way` · `Unloading`
|
|
(Dispatched maps to `On the way`, Arrived maps to `Unloading`.)
|
|
|
|
---
|
|
|
|
## 11. Activity Feed
|
|
|
|
### GET /api/v1/hub/activity?limit=10
|
|
Returns a real event feed for this hub (UNION across 4 sources: inbound, dispatch, exceptions, sorting).
|
|
|
|
Response:
|
|
|
|
```json
|
|
{
|
|
"success": true,
|
|
"data": [
|
|
{ "time": "2026-07-04T11:24:00Z", "type": "exception", "text": "Exception raised: Damaged on DM-TRK-999999" },
|
|
{ "time": "2026-07-04T11:22:00Z", "type": "inbound", "text": "Received parcel DM-TRK-999999 from Coimbatore Jupiter Hub" },
|
|
{ "time": "2026-07-04T11:20:00Z", "type": "dispatch", "text": "Dispatched batch DM-TS-0003 to Mumbai Hub" }
|
|
],
|
|
"total": 3
|
|
}
|
|
```
|
|
|
|
Type values: `inbound` · `dispatch` · `exception` · `sorting`
|
|
Use `type` to pick the icon color — same color scheme as the existing mock.
|
|
|
|
---
|
|
|
|
## 12. Zones
|
|
|
|
### GET /api/v1/hub/zones
|
|
Returns the delivery zone breakdown for this hub (Dashboard → "Delivery Areas").
|
|
|
|
Response:
|
|
|
|
```json
|
|
{
|
|
"success": true,
|
|
"data": [
|
|
{ "zone": "641001", "zonename": "Gandhipuram", "parcels": 12, "milers": 2, "status": "Active" },
|
|
{ "zone": "641004", "zonename": "Peelamedu", "parcels": 8, "milers": 1, "status": "Active" },
|
|
{ "zone": "641012", "zonename": "Jupiter Nagar","parcels": 3, "milers": 0, "status": "Need Milers" }
|
|
]
|
|
}
|
|
```
|
|
|
|
Status values: `Active` · `Need Milers` — use this to color the status chip.
|
|
|
|
---
|
|
|
|
## 13. Notifications
|
|
|
|
### GET /api/v1/hub/notifications
|
|
Returns notifications for the header bell icon, generated from real events.
|
|
|
|
Response:
|
|
|
|
```json
|
|
{
|
|
"success": true,
|
|
"data": [
|
|
{ "id": 1, "title": "Exception: Damaged", "type": "exception", "time": "3 min ago", "read": false },
|
|
{ "id": 2, "title": "Truck arriving from Chennai Hub", "type": "inbound", "time": "5 min ago", "read": false },
|
|
{ "id": 3, "title": "Pickup waiting 30+ min", "type": "alert", "time": "12 min ago", "read": false }
|
|
],
|
|
"total": 3
|
|
}
|
|
```
|
|
|
|
Type values: `exception` · `inbound` · `warning` · `alert`
|
|
|
|
### PATCH /api/v1/hub/notifications/:id/read
|
|
Mark a notification as read.
|
|
|
|
Response:
|
|
|
|
```json
|
|
{ "success": true }
|
|
```
|
|
|
|
On bell click → `GET /hub/notifications`. On item click → `PATCH /hub/notifications/:id/read`, then mark it read in local state.
|
|
|
|
---
|
|
|
|
## 14. Hub Assignment Override
|
|
|
|
The old admin assign endpoint (`POST /admin/bookings/:id/assign-miler`) returned **403** for hub staff. Two new hub-scoped endpoints replace it.
|
|
|
|
### POST /api/v1/hub/bookings/:id/assign-miler
|
|
Manual assign — hub staff picks the miler.
|
|
|
|
Request body:
|
|
|
|
```json
|
|
{ "mileruserid": 4 }
|
|
```
|
|
|
|
Response:
|
|
|
|
```json
|
|
{
|
|
"success": true,
|
|
"data": {
|
|
"bookingid": 21,
|
|
"mileruserid": 4,
|
|
"milername": "Rajesh Kumar",
|
|
"status": "Miler_Assigned"
|
|
}
|
|
}
|
|
```
|
|
|
|
Use this on the **Pickup Requests** page when staff manually selects a miler from the dialog.
|
|
|
|
### POST /api/v1/hub/bookings/:id/auto-assign
|
|
Auto-assign — AI engine picks the miler.
|
|
|
|
Request body:
|
|
|
|
```json
|
|
{}
|
|
```
|
|
|
|
Response (success):
|
|
|
|
```json
|
|
{
|
|
"success": true,
|
|
"data": {
|
|
"mileruserid": 4,
|
|
"milername": "Rajesh Kumar",
|
|
"distance_km": 0.4
|
|
}
|
|
}
|
|
```
|
|
|
|
Response (no miler) — HTTP 422:
|
|
|
|
```json
|
|
{
|
|
"success": false,
|
|
"message": "No eligible miler found in range"
|
|
}
|
|
```
|
|
|
|
Response (timeout) — HTTP 202:
|
|
|
|
```json
|
|
{
|
|
"success": true,
|
|
"message": "assignment in progress"
|
|
}
|
|
```
|
|
|
|
Use this for the **Auto-Assign** bulk button. Loop over selected booking IDs and call this for each one.
|
|
|
|
**Backend implementation notes:** shared `AssignMilerToBooking()` service (`booking_assignment_service.go`), `TryAssignOnce()` single-attempt wrapper on `crm_assignment`, `assignedbyuserid` is nullable for hub-initiated assignments, and `AdminAssignMiler` now sends FCM to the miler.
|
|
|
|
---
|
|
|
|
## 15. Hub Management (Doormile staff only)
|
|
|
|
These endpoints return 403 for partner accounts (`is_doormile_staff = false`).
|
|
Use the `is_doormile_staff` flag from localStorage to show/hide the UI for these.
|
|
|
|
### GET /api/v1/hub/hubs
|
|
List all hubs in the logged-in staff's city. Now includes `lat`/`lon` for map pins.
|
|
|
|
Response:
|
|
|
|
```json
|
|
{
|
|
"success": true,
|
|
"data": [
|
|
{
|
|
"hubid": 1,
|
|
"hubname": "Coimbatore Jupiter Hub",
|
|
"hubtype": "sorting_center",
|
|
"capacity": 50,
|
|
"status": "active",
|
|
"has_staff": true,
|
|
"lat": 11.0168,
|
|
"lon": 76.9558
|
|
},
|
|
{
|
|
"hubid": 2,
|
|
"hubname": "Coimbatore Spoke 1",
|
|
"hubtype": "spoke",
|
|
"capacity": 50,
|
|
"status": "active",
|
|
"has_staff": false,
|
|
"lat": 11.0210,
|
|
"lon": 76.9610
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
Use `lat/lon` to place hub pins on the Live Map.
|
|
|
|
### POST /api/v1/hub/hubs
|
|
Create a new hub in the staff's city.
|
|
|
|
Request body:
|
|
|
|
```json
|
|
{
|
|
"hubname": "Coimbatore RS Puram Hub",
|
|
"hubtype": "spoke",
|
|
"capacity": 30,
|
|
"contact": "+91 98765 00000",
|
|
"address": "RS Puram, Coimbatore",
|
|
"pincode": "641002"
|
|
}
|
|
```
|
|
|
|
**Note:** `applocationid` (city) is set automatically from the logged-in staff's hub — cannot be overridden.
|
|
|
|
### POST /api/v1/hub/staff
|
|
Create a hub staff login for any hub in the city.
|
|
|
|
Request body:
|
|
|
|
```json
|
|
{
|
|
"hubid": 2,
|
|
"email": "hub.rspuram@doormile.in",
|
|
"password": "securepassword",
|
|
"displayname": "RS Puram Hub Staff",
|
|
"tenantid": null
|
|
}
|
|
```
|
|
|
|
Set `tenantid` to the partner's tenant ID for partner accounts, `null` for Doormile staff.
|
|
|
|
Response:
|
|
|
|
```json
|
|
{
|
|
"success": true,
|
|
"data": {
|
|
"staffid": 6,
|
|
"email": "hub.rspuram@doormile.in",
|
|
"hubid": 2
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Error response shape
|
|
|
|
All errors follow the same shape:
|
|
|
|
```json
|
|
{
|
|
"success": false,
|
|
"message": "human readable error description"
|
|
}
|
|
```
|
|
|
|
| HTTP code | Meaning |
|
|
|---|---|
|
|
| 400 | Bad request — invalid body or status transition |
|
|
| 401 | Token expired or missing → redirect to /login |
|
|
| 202 | Accepted — async assignment in progress |
|
|
| 403 | Forbidden — partner account trying Doormile-only endpoint |
|
|
| 404 | Resource not found (booking ID, miler ID, tracking no etc.) |
|
|
| 422 | Unprocessable — no eligible miler found in range |
|
|
| 500 | Server error — show generic "something went wrong" toast |
|
|
|
|
---
|
|
|
|
## Page-to-endpoint map (quick reference)
|
|
|
|
| Page | Endpoints called |
|
|
|---|---|
|
|
| Login | POST /hub/login |
|
|
| Dashboard | GET /hub/dashboard · GET /hub/inbound/vehicles · GET /hub/activity · GET /hub/zones |
|
|
| Receive Parcels | GET /hub/inbound/today · POST /hub/bookings/:id/inbound |
|
|
| Pickup Requests | GET /hub/bookings/unassigned · GET /hub/milers · POST /hub/bookings/:id/assign-miler · POST /hub/bookings/:id/auto-assign |
|
|
| Where Does It Go? (Routing) | GET /hub/routing/:trackingno |
|
|
| Dispatch | GET /hub/batches · POST /hub/batches · PATCH /hub/batches/:id/status |
|
|
| Milers | GET /hub/milers · POST /admin/milers · PATCH /admin/milers/:id · DELETE /admin/milers/:id |
|
|
| Rider Routes | GET /hub/rider-routes · GET /hub/milers/:id/route |
|
|
| Live Map | GET /admin/milers/locations (poll 5s) · GET /hub/tripsheets/in-transit (poll 5s) · GET /hub/hubs |
|
|
| Header — Notifications | GET /hub/notifications · PATCH /hub/notifications/:id/read |
|
|
| Hub Settings | GET /hub/hubs · POST /hub/hubs · POST /hub/staff |
|
|
|
|
---
|
|
|
|
## Frontend implementation notes
|
|
|
|
**Token storage:**
|
|
|
|
```js
|
|
// On login success:
|
|
localStorage.setItem('hub_token', response.token)
|
|
localStorage.setItem('hub_context', JSON.stringify(response.hub))
|
|
localStorage.setItem('hub_staff', JSON.stringify(response.staff))
|
|
localStorage.setItem('hub_is_doormile', String(response.is_doormile_staff))
|
|
|
|
// On every API call:
|
|
headers: { 'Authorization': `Bearer ${localStorage.getItem('hub_token')}` }
|
|
|
|
// On logout or 401:
|
|
localStorage.clear()
|
|
navigate('/login')
|
|
```
|
|
|
|
**Show hub name dynamically (not hardcoded):**
|
|
|
|
```js
|
|
const hub = JSON.parse(localStorage.getItem('hub_context'))
|
|
// hub.hubname → "Coimbatore Jupiter Hub"
|
|
// hub.city → "Coimbatore"
|
|
// hub.hubid → 1
|
|
```
|
|
|
|
**Hide hub management UI for partners:**
|
|
|
|
```js
|
|
const isDoormile = localStorage.getItem('hub_is_doormile') === 'true'
|
|
// Show "Add New Hub" / "Add Staff" buttons only if isDoormile === true
|
|
// KPM partner account gets false → those buttons hidden
|
|
```
|
|
|
|
**On 401 — token expired, force re-login:**
|
|
|
|
```js
|
|
if (response.status === 401) {
|
|
localStorage.clear()
|
|
window.location.href = '/login'
|
|
}
|
|
```
|
|
|
|
**Polling intervals:**
|
|
|
|
- Miler locations → every 5 seconds
|
|
- Truck positions → every 5 seconds (same call cycle)
|
|
- Dashboard stats → every 30 seconds
|
|
- Notifications → every 60 seconds
|
|
|
|
---
|
|
|
|
*All endpoints live at `https://api.doormile.com` · Hub Console Backend v1.0 complete*
|