feat(pin-model): add aggregateDriverStatus helper (TDD)

This commit is contained in:
SJCoetz 2026-07-12 12:12:41 +02:00
parent 8c21347ecc
commit 56626c8270
2 changed files with 116 additions and 0 deletions

View file

@ -48,3 +48,41 @@ export function splitSupplierBucket<
}
return { assignedByDriver, poolItems };
}
export interface DriverStatus {
total: number;
collected: number;
ready: number;
waiting: number;
}
/**
* Counts a driver's stops into the four buckets the driver card surfaces:
* - collected : item_status === "collected" (the green anchor)
* - ready : ready_for_collection === true and not collected
* - waiting : ready_for_collection !== true and not collected
* - total : items.length
*
* Items with `ready_for_collection` undefined or null are treated as
* not ready (waiting). Pure — no DB / side effects.
*/
export function aggregateDriverStatus<
T extends {
item_status: string;
ready_for_collection?: boolean | null;
}
>(items: T[]): DriverStatus {
let collected = 0;
let ready = 0;
let waiting = 0;
for (const it of items) {
if (it.item_status === "collected") {
collected++;
} else if (it.ready_for_collection) {
ready++;
} else {
waiting++;
}
}
return { total: items.length, collected, ready, waiting };
}

78
src/lib/pin-model.test.ts Normal file
View file

@ -0,0 +1,78 @@
import { describe, expect, it } from "vitest";
import { aggregateDriverStatus } from "@/app/(app)/collection-map/pin-model";
describe("aggregateDriverStatus", () => {
it("returns zeros for an empty array", () => {
expect(aggregateDriverStatus([])).toEqual({
total: 0,
collected: 0,
ready: 0,
waiting: 0,
});
});
it("counts every item as collected when every item is collected", () => {
const items = [
{ item_status: "collected" as const, ready_for_collection: true },
{ item_status: "collected" as const, ready_for_collection: true },
{ item_status: "collected" as const, ready_for_collection: false },
];
expect(aggregateDriverStatus(items)).toEqual({
total: 3,
collected: 3,
ready: 0,
waiting: 0,
});
});
it("counts every item as waiting when nothing is ready or collected", () => {
const items = [
{ item_status: "collection_pool" as const, ready_for_collection: false },
{ item_status: "assigned" as const, ready_for_collection: false },
];
expect(aggregateDriverStatus(items)).toEqual({
total: 2,
collected: 0,
ready: 0,
waiting: 2,
});
});
it("rolls up mixed states (collected + ready + waiting)", () => {
const items = [
{ item_status: "collected" as const, ready_for_collection: true },
{ item_status: "collected" as const, ready_for_collection: true },
{ item_status: "assigned" as const, ready_for_collection: true },
{ item_status: "assigned" as const, ready_for_collection: true },
{ item_status: "assigned" as const, ready_for_collection: true },
{ item_status: "collection_pool" as const, ready_for_collection: false },
];
expect(aggregateDriverStatus(items)).toEqual({
total: 6,
collected: 2,
ready: 3,
waiting: 1,
});
});
it("treats undefined ready_for_collection as waiting", () => {
const items = [
{ item_status: "assigned" as const },
{ item_status: "collection_pool" as const },
];
expect(aggregateDriverStatus(items).waiting).toBe(2);
expect(aggregateDriverStatus(items).ready).toBe(0);
expect(aggregateDriverStatus(items).collected).toBe(0);
expect(aggregateDriverStatus(items).total).toBe(2);
});
it("treats null ready_for_collection as waiting", () => {
const items = [
{ item_status: "assigned" as const, ready_for_collection: null },
{ item_status: "collection_pool" as const, ready_for_collection: null },
];
expect(aggregateDriverStatus(items).waiting).toBe(2);
expect(aggregateDriverStatus(items).ready).toBe(0);
expect(aggregateDriverStatus(items).collected).toBe(0);
});
});