added the api for the real data
This commit is contained in:
889
docs/Doormile-Hub-Console-API-Reference.md
Normal file
889
docs/Doormile-Hub-Console-API-Reference.md
Normal file
@@ -0,0 +1,889 @@
|
||||
# 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*
|
||||
Reference in New Issue
Block a user