import { sortRows, type SortState } from "./columnSort"; // The playlist detail's VIEW derivation: what the reader sees, as opposed to the playlist's stored // order. Kept structural (no `Column`, no component imports) like `columnSort.ts`, so it stays pure // and testable — the "save this order" button writes whatever `applyView` returned, which is reason // enough for this to be covered rather than eyeballed. interface Grouped { channel_title?: string | null; } interface Sortable { key: string; sortValue?: (row: T) => string | number; } /** Filters are applied by the caller; this is sort, then optional channel grouping. Groups are * ordered by channel name in the sort's direction, and the sort applies WITHIN each group (with no * sort, each group keeps its stored order). */ export function applyView( rows: T[], columns: Sortable[], sort: SortState, group: boolean, ): T[] { const sorted = sortRows(rows, columns, sort); if (!group) return sorted; const groups = new Map(); for (const v of sorted) { const k = v.channel_title ?? ""; const g = groups.get(k); if (g) g.push(v); else groups.set(k, [v]); } const sign = sort?.dir === "desc" ? -1 : 1; const keys = [...groups.keys()].sort((a, b) => sign * a.localeCompare(b)); return keys.flatMap((k) => groups.get(k)!); } /** Same items in the same positions. Identity is the id, so a refetch that changed only a title * doesn't read as a reorder — and a shorter list (a filtered view) is never "the same order". */ export const sameOrder = (a: { id: string }[], b: { id: string }[]) => a.length === b.length && a.every((v, i) => v.id === b[i]!.id);