Add collection pool nearby sort implementation plan

This commit is contained in:
SJCoetz 2026-07-10 00:34:50 +02:00
parent 8d742e5e87
commit 7a7ad71ac0

View file

@ -0,0 +1,244 @@
# Collection Pool: Nearby sort works on Ready/Pending tabs — Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** The `Nearby suppliers` sort produces a visibly different order from `Operational` on every filter tab. Rows without supplier coordinates go to the end of the list under `Nearby`.
**Architecture:** One-line change in the sort comparator in `view-model.ts` (use `"\uffff"` instead of `"zzz"` so missing keys sink to the end). Add three unit tests pinning the behaviour.
**Tech Stack:** TypeScript, Vitest, existing buildVisibleRows pattern.
**Spec:** [docs/superpowers/specs/2026-07-09-collection-pool-nearby-sort-design.md](file:///Volumes/EXT/DEVELOPMENT/WFDASH_NEXTJS/docs/superpowers/specs/2026-07-09-collection-pool-nearby-sort-design.md)
---
## File Structure
| File | Responsibility | Touched? |
| --- | --- | --- |
| `src/app/(app)/collection-pool/view-model.ts` | Sort comparator — sink missing coords to the end | Modify |
| `src/app/(app)/collection-pool/view-model.test.ts` | Three new sort tests | Create if missing, modify if present |
---
## Task 1: Check whether the test file already exists
- [ ] **Step 1: Check for the test file**
Run:
```bash
ls -1 'src/app/(app)/collection-pool/view-model.test.ts' 2>/dev/null && echo "EXISTS" || echo "MISSING"
```
If the file exists, Task 2 just appends to it. If it doesn't, Task 2 creates it.
---
## Task 2: Add the new sort tests
**Files:**
- Modify or create: `src/app/(app)/collection-pool/view-model.test.ts`
- [ ] **Step 1: Read the existing test file (if any) to match its import + helper conventions**
Look at the top of the file. If it exists, mirror its existing `beforeEach` / mock patterns. The tests below are written to work standalone if you have to create the file from scratch.
- [ ] **Step 2: Append (or create) the three tests**
```ts
import { describe, expect, it } from "vitest";
import { buildVisibleRows, type CollectionPoolViewRow } from "./view-model";
function makeRow(overrides: Partial<CollectionPoolViewRow>): CollectionPoolViewRow {
return {
id: overrides.id ?? "row-1",
description: overrides.description ?? "",
poNumber: overrides.poNumber ?? "PO-1",
orderNumber: overrides.orderNumber ?? "O-1",
supplierName: overrides.supplierName ?? "Supplier",
companyName: overrides.companyName ?? "Company",
customerName: overrides.customerName ?? "Customer",
ready_for_collection: overrides.ready_for_collection ?? false,
clusterSortKey: overrides.clusterSortKey,
assignmentState: overrides.assignmentState ?? "unassigned",
updated: overrides.updated ?? "2026-07-01T00:00:00.000Z",
} as CollectionPoolViewRow;
}
describe("buildVisibleRows — sort modes", () => {
const todayYmd = "2026-07-09";
it("operational sort is stable on the same input regardless of readyFilter", () => {
const rows: CollectionPoolViewRow[] = [
makeRow({ id: "a", poNumber: "PO-A", updated: "2026-07-02T00:00:00.000Z" }),
makeRow({ id: "b", poNumber: "PO-B", updated: "2026-07-01T00:00:00.000Z" }),
];
const all = buildVisibleRows(rows, {
todayYmd,
searchText: "",
readyFilter: "all",
sortMode: "operational",
});
const ready = buildVisibleRows(rows, {
todayYmd,
searchText: "",
readyFilter: "ready",
sortMode: "operational",
});
const pending = buildVisibleRows(rows, {
todayYmd,
searchText: "",
readyFilter: "unready",
sortMode: "operational",
});
expect(all.map((r) => r.id)).toEqual(["a", "b"]);
expect(ready.map((r) => r.id)).toEqual(["a", "b"]);
expect(pending.map((r) => r.id)).toEqual(["a", "b"]);
});
it("nearby sort groups rows by clusterSortKey ascending", () => {
const rows: CollectionPoolViewRow[] = [
makeRow({ id: "c", clusterSortKey: "cluster-c" }),
makeRow({ id: "a", clusterSortKey: "cluster-a" }),
makeRow({ id: "b", clusterSortKey: "cluster-b" }),
];
const out = buildVisibleRows(rows, {
todayYmd,
searchText: "",
readyFilter: "all",
sortMode: "nearby",
});
expect(out.map((r) => r.id)).toEqual(["a", "b", "c"]);
});
it("nearby sort sinks rows with missing clusterSortKey to the end", () => {
const rows: CollectionPoolViewRow[] = [
makeRow({ id: "no-coords-1", clusterSortKey: undefined }),
makeRow({ id: "a", clusterSortKey: "cluster-a" }),
makeRow({ id: "no-coords-2", clusterSortKey: undefined }),
makeRow({ id: "b", clusterSortKey: "cluster-b" }),
];
const out = buildVisibleRows(rows, {
todayYmd,
searchText: "",
readyFilter: "all",
sortMode: "nearby",
});
// Rows WITH coords come first, ordered by cluster key.
// Rows WITHOUT coords come last (sink to the end).
expect(out.map((r) => r.id)).toEqual(["a", "b", "no-coords-1", "no-coords-2"]);
});
});
```
- [ ] **Step 3: Run the new tests**
```bash
npm run test:run -- 'src/app/(app)/collection-pool/view-model'
```
Expected: 3 failed (the new tests fail because the production code still uses `"zzz"`, which sorts missing-coord rows to the *start* of the list, not the end).
- [ ] **Step 4: Commit (test only)**
```bash
git add 'src/app/(app)/collection-pool/view-model.test.ts'
git commit -m "test(collection-pool): pin nearby-sort behaviour for missing coords"
```
---
## Task 3: Fix the sort comparator
**Files:**
- Modify: `src/app/(app)/collection-pool/view-model.ts`
- [ ] **Step 1: Find the `nearby` block in the sort**
Open `src/app/(app)/collection-pool/view-model.ts` and locate the sort block. The current code is:
```ts
if (sortMode === "nearby") {
const clusterDiff = (a.clusterSortKey ?? "zzz").localeCompare(b.clusterSortKey ?? "zzz");
if (clusterDiff !== 0) return clusterDiff;
}
```
- [ ] **Step 2: Replace the sentinel with `"\uffff"`**
```ts
if (sortMode === "nearby") {
// Rows without a cluster key (no supplier coords) sink to the
// end of the list. We use "\uffff" instead of "zzz" so the
// missing-coords group sorts after every real cluster key,
// and within that group the existing tie-breakers apply.
const keyA = a.clusterSortKey ?? "\uffff";
const keyB = b.clusterSortKey ?? "\uffff";
const clusterDiff = keyA.localeCompare(keyB);
if (clusterDiff !== 0) return clusterDiff;
}
```
- [ ] **Step 3: Run the new tests**
```bash
npm run test:run -- 'src/app/(app)/collection-pool/view-model'
```
Expected: 3/3 pass.
- [ ] **Step 4: Lint**
```bash
npx eslint 'src/app/(app)/collection-pool/view-model.ts'
```
Expected: exit 0. Pre-existing warnings on unrelated lines are fine.
- [ ] **Step 5: Commit**
```bash
git add 'src/app/(app)/collection-pool/view-model.ts'
git commit -m "fix(collection-pool): sink missing-coord rows to the end of Nearby sort"
```
---
## Task 4: Final verification
- [ ] **Step 1: Targeted tests**
```bash
npm run test:run -- 'src/app/(app)/collection-pool/view-model'
```
Expected: green.
- [ ] **Step 2: Full collection-pool suite**
```bash
npm run test:run -- 'src/app/(app)/collection-pool'
```
Expected: same passing set as before plus the 3 new sort tests. The 1 pre-existing failure on `page.test.tsx:175` is acceptable.
- [ ] **Step 3: Full suite**
```bash
npm run test:run
```
Expected: same passing set as before plus the 3 new tests. Pre-existing failures (the 6 we documented across the repo) are acceptable.
- [ ] **Step 4: Manual smoke**
1. Open the Collection Pool page on `All`. Click `Nearby suppliers`. Rows with coordinates should group at the top; rows without coordinates should sink to the bottom.
2. Switch to `Ready to collect`. The sort should still produce a visibly different order from `Operational`.
3. Switch to `Pending`. Same as above.
4. Switch back to `Operational`. Order should match the previous behavior (assignment state + updated).