import { buildPaginatedResult, DEFAULT_PAGE_SIZE, err, ok, type PaginationInput, type ReadonlyDB, } from "@tailor-platform/erp-kit/core"; import type { DB } from "../generated/kysely-tailordb"; import { InvalidPaginationError } from "../lib/errors.generated"; type ShiftOrderByField = "date" | "plannedStartAt" | "createdAt"; export interface ListShiftsInput extends PaginationInput {} /** * Function: listShifts * Description: Returns an unfiltered, paginated list of shifts across all * statuses and shiftTypes — the general-purpose listing used by * administrative and planning views. */ export async function run(db: ReadonlyDB, input: ListShiftsInput) { const limit = input.limit ?? DEFAULT_PAGE_SIZE; const offset = input.offset ?? 0; if (!Number.isInteger(limit) || limit < 1) { return err(new InvalidPaginationError(`limit=${input.limit}`)); } if (!Number.isInteger(offset) || offset < 0) { return err(new InvalidPaginationError(`offset=${input.offset}`)); } // A calendar listing reads by date, so `date` stays the default rather than `id`. The `id` // tiebreaker is what makes the order total: without it, rows sharing a date come back in // whatever order the engine picks and can repeat or vanish across pages. const shifts = await db .selectFrom("Shift") .selectAll() .orderBy(input.orderBy ?? "date", input.orderDirection ?? "asc") .orderBy("id", "asc") .limit(limit + 1) .offset(offset) .execute(); return ok(buildPaginatedResult(shifts, limit)); }