Add collection map date filter and unassign implementation plan
This commit is contained in:
parent
72b2ec09e2
commit
f15fb5b49f
1 changed files with 216 additions and 0 deletions
|
|
@ -0,0 +1,216 @@
|
|||
# Collection Map — date filter bug + unassign-clears-date fix
|
||||
|
||||
**Goal:** Fix two related bugs that make the collection map show almost no stops, and make sure the unassign path correctly clears `assigned_at` so the item returns to the un-dated pool.
|
||||
|
||||
**Architecture:** Two one-line server changes + one client date-format fix + four new tests.
|
||||
|
||||
**Tech Stack:** Next.js client + server routes, PocketBase admin SDK, Vitest.
|
||||
|
||||
**Spec:** [docs/superpowers/specs/2026-07-09-collection-map-date-filter-and-unassign-design.md](file:///Volumes/EXT/DEVELOPMENT/WFDASH_NEXTJS/docs/superpowers/specs/2026-07-09-collection-map-date-filter-and-unassign-design.md)
|
||||
|
||||
---
|
||||
|
||||
## File Structure
|
||||
|
||||
| File | Responsibility | Touched? |
|
||||
| --- | --- | --- |
|
||||
| `src/app/api/v1/collection-map/items/[id]/assign/route.ts` | Clear `assigned_at` on unassign | Modify |
|
||||
| `src/app/api/v1/collection-map/items/[id]/assign/route.test.ts` | Cover the unassign-clears-date path | Modify |
|
||||
| `src/app/(app)/collection-map/page.tsx` | Fix the day-bound date format in the PB filter | Modify |
|
||||
|
||||
---
|
||||
|
||||
## Task 1: Unassign clears `assigned_at`
|
||||
|
||||
**Files:**
|
||||
- Modify: `src/app/api/v1/collection-map/items/[id]/assign/route.ts`
|
||||
|
||||
- [ ] **Step 1: Find the patch construction**
|
||||
|
||||
In the route, find the `const patch: Record<string, unknown> = normalizeOrderItemStatusInput({...})` block (around line 74–79).
|
||||
|
||||
- [ ] **Step 2: Clear `assigned_at` after the normaliser call**
|
||||
|
||||
Right after the existing `if (typeof body.ready_for_collection === "boolean")` block, add the clear. The intent: when the operator is returning an item to the pool (no driver being assigned), `assigned_at` should be empty so the item reappears in the un-dated pool catchall in the collection-map filter.
|
||||
|
||||
```ts
|
||||
// Returning an item to the pool (no driver being assigned) also
|
||||
// clears the date the operator originally assigned. Without this,
|
||||
// the item keeps its old `assigned_at` and the collection map's
|
||||
// date filter (which matches by string compare on `assigned_at`)
|
||||
// may still surface it under the wrong day, or hide it entirely
|
||||
// on the un-dated fallback.
|
||||
if (!isAssigning) {
|
||||
patch.assigned_at = "";
|
||||
}
|
||||
```
|
||||
|
||||
Place this after the `if (typeof body.ready_for_collection === "boolean")` block, before the `const pb = await getAdminPocketBase();` line.
|
||||
|
||||
- [ ] **Step 3: Lint**
|
||||
|
||||
```bash
|
||||
npx eslint 'src/app/api/v1/collection-map/items/[id]/assign/route.ts'
|
||||
```
|
||||
|
||||
Expected: exit 0.
|
||||
|
||||
- [ ] **Step 4: Commit**
|
||||
|
||||
```bash
|
||||
git add 'src/app/api/v1/collection-map/items/[id]/assign/route.ts'
|
||||
git commit -m "fix(collection-map): clear assigned_at on unassign so item returns to pool"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Task 2: Cover the unassign-clears-date path
|
||||
|
||||
**Files:**
|
||||
- Modify: `src/app/api/v1/collection-map/items/[id]/assign/route.test.ts`
|
||||
|
||||
- [ ] **Step 1: Find the existing tests**
|
||||
|
||||
The test file already exists with 5 tests from the prior "mark ready notes" task. Find the `it("accepts the unmark path without a note")` test. That's the test that exercises the unmark path (with `ready_for_collection: false`).
|
||||
|
||||
- [ ] **Step 2: Add a new test for the driver-unassign case**
|
||||
|
||||
Add a new test below the unmark-without-note test:
|
||||
|
||||
```ts
|
||||
it("clears assigned_at when returning an item to the pool (no driverId)", async () => {
|
||||
getOneMock.mockResolvedValueOnce({
|
||||
id: "item-1",
|
||||
assigned_at: "2026-07-09T00:00:00.000Z",
|
||||
});
|
||||
updateMock.mockResolvedValueOnce({ id: "item-1" });
|
||||
|
||||
const res = await PATCH(
|
||||
makeReq({ date: "" }),
|
||||
{ params: Promise.resolve({ id: "item-1" }) },
|
||||
);
|
||||
|
||||
expect(res.status).toBe(200);
|
||||
const payload = updateMock.mock.calls[0][1] as Record<string, unknown>;
|
||||
expect(payload.assigned_at).toBe("");
|
||||
expect(payload.item_status).toBe("collection_pool");
|
||||
expect(payload.drv_status).toBe("unassigned");
|
||||
});
|
||||
```
|
||||
|
||||
- [ ] **Step 3: Run the test file**
|
||||
|
||||
```bash
|
||||
npm run test:run -- 'src/app/api/v1/collection-map/items/[id]/assign/route.test.ts'
|
||||
```
|
||||
|
||||
Expected: 6/6 pass.
|
||||
|
||||
- [ ] **Step 4: Lint**
|
||||
|
||||
```bash
|
||||
npx eslint 'src/app/api/v1/collection-map/items/[id]/assign/route.test.ts'
|
||||
```
|
||||
|
||||
Expected: exit 0.
|
||||
|
||||
- [ ] **Step 5: Commit**
|
||||
|
||||
```bash
|
||||
git add 'src/app/api/v1/collection-map/items/[id]/assign/route.test.ts'
|
||||
git commit -m "test(collection-map): cover assigned_at clear on unassign"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Task 3: Fix the day-bound date format in the map filter
|
||||
|
||||
**Files:**
|
||||
- Modify: `src/app/(app)/collection-map/page.tsx`
|
||||
|
||||
- [ ] **Step 1: Find the day-bound construction**
|
||||
|
||||
In `fetchRows` (the function that loads `order_items`), find the `const dayStart = ...` and `const dayEnd = ...` lines. The current code is:
|
||||
|
||||
```ts
|
||||
const dayStart = `${date} 00:00:00`;
|
||||
const dayEnd = `${date} 23:59:59`;
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Replace with ISO 8601 UTC bounds**
|
||||
|
||||
`assigned_at` is stored as a UTC ISO string (e.g. `2026-07-09T00:00:00.000Z`). The filter must use the same format so the string comparison actually orders correctly.
|
||||
|
||||
```ts
|
||||
// Build a UTC ISO 8601 day window. `assigned_at` is stored at
|
||||
// "YYYY-MM-DDT00:00:00.000Z" (see /api/v1/collection-map/items/[id]/assign),
|
||||
// so the bounds need the same "T" and trailing "Z" or string
|
||||
// comparison puts `T` (0x54) before the space (0x20) and the bound
|
||||
// excludes every real row.
|
||||
const dayStart = `${date}T00:00:00.000Z`;
|
||||
const dayEnd = `${date}T23:59:59.999Z`;
|
||||
```
|
||||
|
||||
- [ ] **Step 3: Update the second `getFullList` call (the unmapped-rows one)**
|
||||
|
||||
The same `assigned_at` filter is used in the second `getFullList` call for unmapped rows. The bounds there are duplicated. Update the same way:
|
||||
|
||||
```ts
|
||||
const dayStart = `${date}T00:00:00.000Z`;
|
||||
const dayEnd = `${date}T23:59:59.999Z`;
|
||||
```
|
||||
|
||||
If the bounds already exist in a local variable, reuse them; otherwise duplicate the fix. The current code (lines 396–401) has the bounds inlined:
|
||||
|
||||
```ts
|
||||
filter: `(item_status = "assigned" || item_status = "collection_pool") && ((assigned_at >= "${date} 00:00:00" && assigned_at <= "${date} 23:59:59") || assigned_at = "" || assigned_at = null)`,
|
||||
```
|
||||
|
||||
Replace with:
|
||||
|
||||
```ts
|
||||
filter: `(item_status = "assigned" || item_status = "collection_pool") && ((assigned_at >= "${date}T00:00:00.000Z" && assigned_at <= "${date}T23:59:59.999Z") || assigned_at = "" || assigned_at = null)`,
|
||||
```
|
||||
|
||||
- [ ] **Step 4: Lint**
|
||||
|
||||
```bash
|
||||
npx eslint 'src/app/(app)/collection-map/page.tsx'
|
||||
```
|
||||
|
||||
Expected: exit 0.
|
||||
|
||||
- [ ] **Step 5: Commit**
|
||||
|
||||
```bash
|
||||
git add 'src/app/(app)/collection-map/page.tsx'
|
||||
git commit -m "fix(collection-map): use ISO 8601 UTC bounds for assigned_at day filter"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Task 4: Final verification
|
||||
|
||||
- [ ] **Step 1: Targeted tests**
|
||||
|
||||
```bash
|
||||
npm run test:run -- 'src/app/api/v1/collection-map/items/[id]/assign'
|
||||
```
|
||||
|
||||
Expected: 6/6 pass.
|
||||
|
||||
- [ ] **Step 2: Full suite**
|
||||
|
||||
```bash
|
||||
npm run test:run
|
||||
```
|
||||
|
||||
Expected: same passing set as before plus the new test. The 6 pre-existing failures are acceptable.
|
||||
|
||||
- [ ] **Step 3: Manual smoke**
|
||||
|
||||
1. Open the Collection Map page.
|
||||
2. Pick today's date. All assigned + pool items for today should appear.
|
||||
3. Pick a past date — items with that `assigned_at` should appear, others shouldn't.
|
||||
4. From the Collection Pool, click `Mark Awaiting` on a row (unmark). The row should disappear from any "today" / "tomorrow" map view and reappear under the un-dated pool.
|
||||
5. From the Collection Map, click a pin → `Reassign` modal → clear the date (or remove the driver). The item should return to the un-dated pool.
|
||||
Loading…
Reference in a new issue