shoc-frontend-new/src/domain/work-orders/utils/carry-over-display.ts

109 lines
3.5 KiB
TypeScript

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) };
}