feat(collection-pool): add buildSupplierGroups helper
This commit is contained in:
parent
cdaa2e2b25
commit
4662347ca0
2 changed files with 104 additions and 0 deletions
|
|
@ -2,7 +2,9 @@ import { describe, expect, it } from "vitest";
|
|||
|
||||
import {
|
||||
buildProximityMarkerMap,
|
||||
buildSupplierGroups,
|
||||
buildVisibleRows,
|
||||
type CollectionPoolGroup,
|
||||
type CollectionPoolSortMode,
|
||||
getAssignmentBadge,
|
||||
getAssignmentState,
|
||||
|
|
@ -263,3 +265,52 @@ describe("buildVisibleRows sort modes (nearby pinning)", () => {
|
|||
expect(out.map((r) => r.id)).toEqual(["a", "b", "no-coords-1", "no-coords-2"]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("buildSupplierGroups", () => {
|
||||
it("returns an empty array for empty input", () => {
|
||||
expect(buildSupplierGroups([], new Map())).toEqual([]);
|
||||
});
|
||||
|
||||
it("folds rows by supplierId in input order", () => {
|
||||
const rows = [
|
||||
makeRow({ id: "a", supplierId: "sup-1", supplierName: "Acme", poNumber: "PO-1" }),
|
||||
makeRow({ id: "b", supplierId: "sup-2", supplierName: "Beta", poNumber: "PO-2" }),
|
||||
makeRow({ id: "c", supplierId: "sup-1", supplierName: "Acme", poNumber: "PO-3" }),
|
||||
];
|
||||
const groups = buildSupplierGroups(rows, new Map());
|
||||
expect(groups.map((g) => g.supplierId)).toEqual(["sup-1", "sup-2"]);
|
||||
expect(groups[0].rows.map((r) => r.id)).toEqual(["a", "c"]);
|
||||
expect(groups[1].rows.map((r) => r.id)).toEqual(["b"]);
|
||||
});
|
||||
|
||||
it("captures the top two PO numbers and reports the remaining count", () => {
|
||||
const rows = [
|
||||
makeRow({ supplierId: "sup-1", poNumber: "PO-1" }),
|
||||
makeRow({ supplierId: "sup-1", poNumber: "PO-2" }),
|
||||
makeRow({ supplierId: "sup-1", poNumber: "PO-3" }),
|
||||
makeRow({ supplierId: "sup-1", poNumber: "PO-4" }),
|
||||
makeRow({ supplierId: "sup-1", poNumber: "PO-4" }), // duplicate PO
|
||||
];
|
||||
const [group] = buildSupplierGroups(rows, new Map());
|
||||
expect(group.topPoNumbers).toEqual(["PO-1", "PO-2"]);
|
||||
expect(group.remainingPoCount).toBe(2);
|
||||
});
|
||||
|
||||
it("uses marker token from supplierMarkerMap", () => {
|
||||
const map = new Map([["sup-1", "marker-blue"]]);
|
||||
const [group] = buildSupplierGroups(
|
||||
[makeRow({ supplierId: "sup-1" })],
|
||||
map,
|
||||
);
|
||||
expect(group.markerToken).toBe("marker-blue");
|
||||
});
|
||||
|
||||
it("returns empty eyebrow when no rows have a PO number", () => {
|
||||
const [group] = buildSupplierGroups(
|
||||
[makeRow({ supplierId: "sup-1", poNumber: "" })],
|
||||
new Map(),
|
||||
);
|
||||
expect(group.topPoNumbers).toEqual([]);
|
||||
expect(group.remainingPoCount).toBe(0);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -124,6 +124,59 @@ export function buildVisibleRows(
|
|||
});
|
||||
}
|
||||
|
||||
export type CollectionPoolGroup = {
|
||||
supplierId: string;
|
||||
supplierName: string;
|
||||
/** Cluster colour token from the proximity map (may be undefined). */
|
||||
markerToken?: string;
|
||||
/** First two distinct PO numbers for the header eyebrow. */
|
||||
topPoNumbers: string[];
|
||||
/** Number of distinct POs beyond the top two. */
|
||||
remainingPoCount: number;
|
||||
/** Rows belonging to this group, in render order. */
|
||||
rows: CollectionPoolViewRow[];
|
||||
};
|
||||
|
||||
/**
|
||||
* Folds already-sorted rows into per-supplier groups, preserving input
|
||||
* order. The caller is responsible for sorting rows by `supplierName`
|
||||
* (use `buildVisibleRows` first); this helper does not re-sort.
|
||||
*/
|
||||
export function buildSupplierGroups(
|
||||
rows: CollectionPoolViewRow[],
|
||||
supplierMarkerMap: Map<string, string>,
|
||||
): CollectionPoolGroup[] {
|
||||
const groups: CollectionPoolGroup[] = [];
|
||||
const bySupplier = new Map<string, CollectionPoolGroup>();
|
||||
|
||||
for (const row of rows) {
|
||||
let group = bySupplier.get(row.supplierId);
|
||||
if (!group) {
|
||||
group = {
|
||||
supplierId: row.supplierId,
|
||||
supplierName: row.supplierName ?? "",
|
||||
markerToken: supplierMarkerMap.get(row.supplierId),
|
||||
topPoNumbers: [],
|
||||
remainingPoCount: 0,
|
||||
rows: [],
|
||||
};
|
||||
bySupplier.set(row.supplierId, group);
|
||||
groups.push(group);
|
||||
}
|
||||
group.rows.push(row);
|
||||
}
|
||||
|
||||
for (const group of groups) {
|
||||
const uniquePos = Array.from(
|
||||
new Set(group.rows.map((r) => r.poNumber ?? "").filter(Boolean)),
|
||||
);
|
||||
group.topPoNumbers = uniquePos.slice(0, 2);
|
||||
group.remainingPoCount = Math.max(0, uniquePos.length - 2);
|
||||
}
|
||||
|
||||
return groups;
|
||||
}
|
||||
|
||||
export function buildProximityMarkerMap(
|
||||
suppliers: Array<{ supplierId: string; lat: number; lng: number }>,
|
||||
radiusKm: number,
|
||||
|
|
|
|||
Loading…
Reference in a new issue