/** * Products list selection + export id helpers (node:test friendly). */ export function idsEqual(a: string | number, b: string | number): boolean { return String(a) === String(b); } export function selectionHas( selected: ReadonlyArray, id: string | number ): boolean { return selected.some((x) => idsEqual(x, id)); } /** Immutable add/remove for the products table checkbox selection. */ export function setProductSelected( selected: ReadonlyArray, id: string | number, selectedOn: boolean ): Array { if (selectedOn) { if (selectionHas(selected, id)) return [...selected]; return [...selected, id]; } return selected.filter((x) => !idsEqual(x, id)); } /** Toggle all visible (selectable) ids into / out of the selection. */ export function toggleSelectAllIds( selected: ReadonlyArray, visibleIds: ReadonlyArray ): Array { const allOn = visibleIds.length > 0 && visibleIds.every((id) => selectionHas(selected, id)); if (allOn) { return selected.filter((id) => !visibleIds.some((v) => idsEqual(v, id))); } const merged = [...selected]; for (const id of visibleIds) { if (!selectionHas(merged, id)) merged.push(id); } return merged; } export function selectableProductIds( products: ReadonlyArray<{ id: string | number; processing_status?: string | null }> ): Array { return products .filter((p) => p.processing_status !== "processing") .map((p) => p.id); } /** Raw product ids for processing jobs (raw tab uses row id; processed uses raw_product_id). */ export function selectedRawProductIds( products: ReadonlyArray<{ id: string | number; raw_product_id?: string | number | null; }>, selected: ReadonlyArray, kind: "processed" | "raw" ): string[] { const ids: string[] = []; for (const p of products) { if (!selectionHas(selected, p.id)) continue; if (kind === "raw") ids.push(String(p.id)); else if (p.raw_product_id) ids.push(String(p.raw_product_id)); } return ids; } export function toExportProductIds(selected: ReadonlyArray): string[] { return selected.map(String); } export function canOpenExportDialog(selectedCount: number): boolean { return selectedCount > 0; } /** Map processing_status to ProductStatusBadge branch key. */ export function normalizeStatusBadgeLabel(status?: string | null): string { return String(status ?? "").trim() || "unknown"; }