Add collection map date filter and unassign spec
This commit is contained in:
parent
f15fb5b49f
commit
728bab1671
1 changed files with 90 additions and 0 deletions
|
|
@ -0,0 +1,90 @@
|
|||
# Collection Map — date filter bug + unassign-clears-date fix
|
||||
|
||||
**Goal:** Two related bugs make the Collection Map show almost no stops:
|
||||
|
||||
1. **Date-format bug in the PB filter.** `assigned_at` is stored as `2026-07-09T00:00:00.000Z` (ISO 8601 UTC, with `T` separator), but the filter bounds are `2026-07-09 00:00:00` (with a space, no `Z`). String comparison puts `T` (0x54) before the space (0x20), so the `>=` bound excludes every real row. The map then shows only items with empty `assigned_at` (the un-dated pool fallback) — which is why the user sees "one stop regardless of filter".
|
||||
|
||||
2. **Unassign doesn't clear `assigned_at`.** When the operator returns an item to the pool (no driver), the route passes `assigned_at: undefined` to `normalizeOrderItemStatusInput`, which silently skips the field. The item keeps its old date and gets re-filtered into the wrong day (or hidden by the date-format bug). The user expects: unassign resets the date so the item returns to the un-dated pool.
|
||||
|
||||
## Scope
|
||||
|
||||
### In Scope
|
||||
|
||||
- Fix the day-bound date format in `fetchRows` in the Collection Map page (`page.tsx`). Two filters use it; both fixed.
|
||||
- When the operator is unassigning (no `driverId`), the route clears `assigned_at: ""` so the item returns to the un-dated pool.
|
||||
- One new test covering the unassign-clears-date path.
|
||||
|
||||
### Out of Scope
|
||||
|
||||
- Changing how the assign route stores `assigned_at` (it already stores `T00:00:00.000Z` correctly).
|
||||
- The collection-pool unmark flow (`Mark Awaiting`) — it only flips `ready_for_collection`, doesn't change driver/date, so it's not affected.
|
||||
- Timezone changes. `assigned_at` is UTC; bounds stay UTC.
|
||||
|
||||
## Current State
|
||||
|
||||
`fetchRows` in `src/app/(app)/collection-map/page.tsx` (around line 340) builds:
|
||||
|
||||
```ts
|
||||
const dayStart = `${date} 00:00:00`;
|
||||
const dayEnd = `${date} 23:59:59`;
|
||||
const filter = `(item_status = "assigned" || item_status = "collection_pool") && ((assigned_at >= "${dayStart}" && assigned_at <= "${dayEnd}") || assigned_at = "" || assigned_at = null)`;
|
||||
```
|
||||
|
||||
The assign route stores `assigned_at: ${date}T00:00:00.000Z`. Filter bound is `${date} 00:00:00`. `localeCompare` / string sort puts `T` (0x54) before ` ` (0x20), so the bound `assigned_at >= "${date} 00:00:00"` fails for every real row.
|
||||
|
||||
`fetchRows` second call (the unmapped-rows query) duplicates the bad bounds.
|
||||
|
||||
`/api/v1/collection-map/items/[id]/assign/route.ts` passes `assigned_at: assignedAt` to `normalizeOrderItemStatusInput`. When the operator is unassigning (no `date` provided), `assignedAt` is `undefined`, the normaliser skips the field, and PB keeps the old `assigned_at`.
|
||||
|
||||
## User Confirmed Rule
|
||||
|
||||
- Unassign clears the date. Item returns to the un-dated pool. (Same behavior as a brand-new pool row that has never been assigned.)
|
||||
|
||||
## Behavior
|
||||
|
||||
### Server — unassign clears `assigned_at`
|
||||
|
||||
In the assign route, when `!isAssigning` (operator is returning to the pool), set `patch.assigned_at = ""` after the normaliser call. The normaliser already accepts `""` as a valid string and writes it; we just need to pass it explicitly.
|
||||
|
||||
```ts
|
||||
if (!isAssigning) {
|
||||
patch.assigned_at = "";
|
||||
}
|
||||
```
|
||||
|
||||
The `ready_for_collection` flag is left alone (the supplier-side gate is independent of driver assignment). `drv_id` is already cleared by the normaliser.
|
||||
|
||||
### Client — fixed date bounds
|
||||
|
||||
```ts
|
||||
const dayStart = `${date}T00:00:00.000Z`;
|
||||
const dayEnd = `${date}T23:59:59.999Z`;
|
||||
```
|
||||
|
||||
Used in both `getFullList` filters in `fetchRows`. `999` (rather than `000` of the next day) is fine for string comparison because the stored value is always at `T00:00:00.000Z` of the assigned day.
|
||||
|
||||
## Testing
|
||||
|
||||
### Route tests
|
||||
|
||||
- New test: `clears assigned_at when returning an item to the pool (no driverId)`. POST with `{ date: "" }` and an existing `assigned_at` on the row. The PATCH payload must contain `assigned_at: ""`.
|
||||
|
||||
Existing tests must still pass:
|
||||
|
||||
- `accepts the unmark path without a note (ready_for_collection: false)` — uses `date` unset, must still 200.
|
||||
- `writes trimmed notes to the item when ready_notes is provided` — uses a new assignment with a date, must still write `assigned_at`.
|
||||
|
||||
### Client tests
|
||||
|
||||
No automated test for the client filter. The fix is one string constant; manual smoke covers it.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- Picking any date in the Collection Map shows the assigned items for that date (and the un-dated pool items, as today).
|
||||
- Unassigning an item from the Collection Map or Collection Pool clears its `assigned_at`, so it shows up under the un-dated pool.
|
||||
- All targeted tests pass; full suite non-regressive.
|
||||
|
||||
## Risk / Rollback
|
||||
|
||||
- Low risk. Two one-line changes.
|
||||
- Rollback: revert the route change and the page constant. No schema change.
|
||||
Loading…
Reference in a new issue