# CLAUDE.md — `src/pages/api/`

Rules for editing `api.js` and `doormileApi.js` — the **central API layer**, every page calls into one of these two. The root `CLAUDE.md` covers project-wide conventions; this file is the scoped rule sheet for the API layer specifically.

**The old jupiter.nearle.app backend (`REACT_APP_URL` / `REACT_APP_URL2` / `REACT_APP_URL3`) has been fully retired from this project.** Every server call now goes through `utils/doormileAxios.js` → `api.doormile.com/api/v1`, either directly from `doormileApi.js` or via the thin wrappers in `api.js`. Do not reintroduce `process.env.REACT_APP_URL*` or raw `axios` calls to `jupiter.nearle.app` — the one sanctioned exception is the AI dispatch optimiser (`routes.workolik.com` / `routemate.workolik.com`) and its final delivery commit, which are a separate solver service with no equivalent in the new API and are explicitly left untouched (see `createOptimisationDeliveries`, `reconcileSteps`, `fetchBatchEfficiency`, `createAutomationDeliveries`, `finalCreatedeliveries` in `api.js`).

---

## 1. Two files, two roles

- **`doormileApi.js`** — one named export per `api.doormile.com/admin/*` endpoint (see `express-console-api.md` at the repo root for the full reference). Thin: build the URL, call `doormileAxios`, unwrap the envelope, return.
- **`api.js`** — the page-facing layer. Existing function names/signatures were kept stable during the migration (so page files didn't all need parallel edits) and now delegate to `doormileApi.js` internally. New pages should import directly from `doormileApi.js` unless matching an existing `queryKey`-shaped call site in `api.js`.

## 2. Function signature patterns

### TanStack `useQuery` / `useInfiniteQuery` consumers
Destructure from `queryKey` in the order the call site declared it. The leading `_` is the query name and is intentionally discarded.

```js
export const fetchorderscount = async ({ queryKey }) => {
  const [, , startdate, enddate] = queryKey;
  return (await getReports(startdate, enddate)) || {};
};

export const fetchOrders = async ({ pageParam = 1, queryKey }) => {
  const [, , , , , , rowsPerPage] = queryKey;
  const rows = (await getBookings(pageParam, rowsPerPage)) || [];
  return {
    rows,
    nextPage: rows.length === Number(rowsPerPage) ? pageParam + 1 : undefined
  };
};
```

**Hard rules:**
- The query-key array order at the call site MUST match the destructure order here. Re-ordering one without the other silently breaks every caller.
- Infinite queries return `{ rows, nextPage }`. `nextPage` is `undefined` when the page size wasn't filled (signals end-of-stream to `getNextPageParam`).
- Many `queryKey` positions that used to carry `applocationid`/`tenantid`/`locationid`/`status` filters are now unused padding — the new API doesn't accept those as query params on most resources (see §3). Don't delete the positions; call sites still destructure by index.

---

## 3. Base client and what the new API can't do

| Use | When |
|---|---|
| `doormileAxios` (via `doormileApi.js` functions) | Every `/admin/*` endpoint. Bearer token attached automatically from `localStorage.doormileToken`. |
| Hardcoded `https://routes.workolik.com` | Bike solver + reconcile-steps + batch efficiency — untouched, separate service. |
| Hardcoded `https://routemate.workolik.com` | Auto / multi-trip solver — untouched, separate service. |
| Hardcoded `https://jupiter.nearle.app` | Final `/deliveries/createdeliveries` commit only — untouched, part of the same solver pipeline. |

The new API has **no equivalent** for a number of things the old jupiter backend served. Functions covering these return safe empty defaults (`null`/`[]`/`{}`) rather than throwing — match this pattern for anything new that hits the same gap:

- Zones/`applocationid` as a filterable resource — derive a picker list from `GET /admin/hubs` instead (`fetchAppLocations` in `api.js`).
- Live rider GPS/battery/periodic logs, per-delivery GPS trail/polyline logs.
- Payment-mode types, item subcategories, rider shifts, vehicle/account types.
- Tenant tab-count summaries, per-rider/per-location report breakdowns, rider login/checkout audit logs.
- The `/substitutions` rider-absence feature (riders.js) — no backend at all.
- Invoices and expense-request approvals — no backend at all (the `requests.js` page/route is disabled, not deleted).

Before assuming a new page's data need is covered, check `express-console-api.md` — roughly half its routes are documented as "written but unproven" against the real backend, so field names on GET responses are a best-effort guess in several places (flagged inline with comments where that's true).

---

## 4. Error handling pattern

```js
export const someFetch = async () => {
  try {
    return (await someDoormileApiFn()) || [];
  } catch (err) {
    const message = err.response?.data?.message || err.message || 'Something went wrong';
    OpenToast(message);
    return []; // or null / {} — match what the caller expects
  }
};
```

- Toast on failure via `OpenToast` from `components/third-party/OpenToast`. Don't `throw` — TanStack Query's `onError` is rarely wired by callers.
- Return a sensible empty default so the call site's destructuring doesn't crash.

---

## 5. When NOT to add to `api.js`

- **Adding a new shared GET against `/admin/*`** → put it in `doormileApi.js`.
- **Adding a one-off mutation used in only one page** → tolerated inline in that page (importing straight from `doormileApi.js`); doesn't need a new export in `api.js`.
- **Adding a polling endpoint used by multiple pages** → put it in `api.js` as a named export so the query cache keys are coherent.

---

## 6. Response shape

`doormileApi.js` functions already unwrap the envelope: GET/list functions return `response.data.data` directly; mutations return the full `response.data` (`{success, data|message}`) so callers can check `res.success`. Don't assume `.details` / `.status` — those were the old jupiter envelope and no longer apply anywhere in this layer.

---

## 7. Things that look broken but are intentional

- Several `api.js` functions ignore most of their destructured `queryKey`/arguments — the new endpoint doesn't accept those filters. This is deliberate degradation, not a bug; see §3.
- A handful of pages (`createorder1.js`, `multipleOrders.js`) synthesize fallback data client-side (e.g. a fixed 09:00–21:00 delivery-slot window) because the endpoint that used to supply it no longer exists. Look for the comment explaining why before "fixing" it.
- `notifyRider` / `notifyMiler` now take a **miler profile ID**, not an FCM token — the new API's `/admin/milers/:id/notify` looks the device token up server-side. Don't pass a token here.
