import { compactWeekLabel } from "@/domain/work-orders/utils/list-week-utils"; import { formatRescheduleCountLabel } from "@/domain/work-orders/utils/reschedule-indicator"; import { deriveDayFields } from "@/domain/work-orders/utils/table-day-utils"; import { addDaysIso, mondayOf } from "@/domain/work-orders/utils/wizard-date-utils"; const ISO_DATE = /^\d{4}-\d{2}-\d{2}$/; const WEEKDAY_COUNT = 7; const FRIDAY_OFFSET = 4; export type CarryOverRow = { scheduledOn: string; status?: string; carriedOver?: number; }; export function isTerminalCarryOverStatus(status?: string): boolean { return status === "Completed" || status === "Canceled"; } export function shouldShowCarriedOverPill(count: number, status?: string): boolean { return count >= 2 && !isTerminalCarryOverStatus(status); } export function toIsoDate(value?: string): string { if (!value) { return ""; } const iso = value.slice(0, 10); return ISO_DATE.test(iso) ? iso : ""; } export function resolveCarryOverSourceWeek(input: { originalWeek?: string; originalDate?: string; scheduledOn?: string; }): string { const originalWeek = toIsoDate(input.originalWeek); if (originalWeek) { return mondayOf(originalWeek); } const originalDate = toIsoDate(input.originalDate); if (originalDate) { return mondayOf(originalDate); } const scheduledOn = toIsoDate(input.scheduledOn); return scheduledOn ? mondayOf(scheduledOn) : ""; } export function formatCarriedOverHover(count: number, sourceWeekIso?: string): string { const times = formatRescheduleCountLabel(count); const base = `Carried over ${times} — this WO was not completed and moved to the next period`; const sourceLabel = sourceWeekIso ? compactWeekLabel(sourceWeekIso) : ""; if (!sourceLabel) { return base; } return `${base}. Source week: ${sourceLabel}.`; } /** Non-terminal WOs scheduled before the viewed week are display carry-overs. */ export function isCarryOverDisplayRow(row: CarryOverRow, weekMonday: string): boolean { if (isTerminalCarryOverStatus(row.status)) { return false; } const scheduled = toIsoDate(row.scheduledOn); return Boolean(scheduled) && scheduled < weekMonday; } export function mapCarryOverDisplayDay(scheduledOn: string, weekMonday: string): string { const scheduled = toIsoDate(scheduledOn); if (!scheduled || !ISO_DATE.test(weekMonday)) { return ""; } const sourceMonday = mondayOf(scheduled); const offsetMs = new Date(`${scheduled}T12:00:00`).getTime() - new Date(`${sourceMonday}T12:00:00`).getTime(); const offset = Math.round(offsetMs / 86_400_000); if (offset < 0 || offset >= WEEKDAY_COUNT) { return weekMonday; } if (offset > FRIDAY_OFFSET) { return weekMonday; } return addDaysIso(weekMonday, offset); } export function rowMatchesCarryOverDay( row: CarryOverRow, selectedDayIso: string, weekMonday: string, ): boolean { if (!isCarryOverDisplayRow(row, weekMonday)) { return false; } return mapCarryOverDisplayDay(row.scheduledOn, weekMonday) === selectedDayIso; } /** Aligns UI day keys with the mapped weekday so reorder stays on the displayed tab. */ export function applyCarryOverDisplayDayFields< T extends CarryOverRow & { dayGroup: string; dayLabel: string }, >(row: T, weekMonday: string): T { if (!isCarryOverDisplayRow(row, weekMonday)) { return row; } const displayDay = mapCarryOverDisplayDay(row.scheduledOn, weekMonday); if (!displayDay) { return row; } return { ...row, ...deriveDayFields(displayDay) }; }