From 7a7ad71ac0c659ee63965aa0291fe0acd95e0d4e Mon Sep 17 00:00:00 2001 From: SJCoetz Date: Fri, 10 Jul 2026 00:34:50 +0200 Subject: [PATCH] Add collection pool nearby sort implementation plan --- .../2026-07-09-collection-pool-nearby-sort.md | 244 ++++++++++++++++++ 1 file changed, 244 insertions(+) create mode 100644 docs/superpowers/plans/2026-07-09-collection-pool-nearby-sort.md diff --git a/docs/superpowers/plans/2026-07-09-collection-pool-nearby-sort.md b/docs/superpowers/plans/2026-07-09-collection-pool-nearby-sort.md new file mode 100644 index 0000000..f4b51ae --- /dev/null +++ b/docs/superpowers/plans/2026-07-09-collection-pool-nearby-sort.md @@ -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 { + 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). \ No newline at end of file