import type { WorkOrderUplift, UpliftSummary, WorkOrderUpliftStatus, } from "@/domain/work-orders/types/work-order-uplift"; import type { WorkOrderTableRow } from "@/domain/work-orders/types/work-order-table-row"; export function formatUpliftMoney(value: number | null | undefined): string { if (value == null || Number.isNaN(value)) { return "—"; } return new Intl.NumberFormat("en-US", { style: "currency", currency: "USD", maximumFractionDigits: 0, }).format(value); } const PILL_LABELS: Record = { pending: "Pending approval", approved: "Approved", auto_approved: "Auto-approved", rejected: "Rejected", cancelled: "Cancelled", revoked: "Revoked", }; const CELL_STATUS_WORD: Record = { pending: "Pending", approved: "Approved", auto_approved: "Approved", rejected: "Rejected", cancelled: "Cancelled", revoked: "Revoked", }; export function formatUpliftStatusLabel(status: WorkOrderUpliftStatus): string { return PILL_LABELS[status]; } export function getUpliftCellLabel(summary: UpliftSummary): string | null { if (!summary.hasUplift && summary.pendingCount <= 0) { return null; } if (summary.pendingCount > 0) { if (summary.amount != null) { return `${formatUpliftMoney(summary.amount)} Pending`; } return summary.pendingCount === 1 ? "1 Pending" : `${summary.pendingCount} Pending`; } if (summary.primaryStatus) { const label = CELL_STATUS_WORD[summary.primaryStatus]; if (summary.amount != null) { return `${formatUpliftMoney(summary.amount)} ${label}`; } return label; } if (summary.hasUplift) { return "Uplift"; } return null; } export function getUpliftStatusPillStyle(status: WorkOrderUpliftStatus): { background: string; color: string; } { switch (status) { case "pending": return { background: "var(--status-pending-bg)", color: "var(--status-pending-text)" }; case "approved": case "auto_approved": return { background: "var(--status-completed-bg)", color: "var(--status-completed-text)" }; case "rejected": return { background: "var(--status-pastdue-bg)", color: "var(--status-pastdue-text)" }; case "cancelled": case "revoked": return { background: "var(--status-canceled-bg)", color: "var(--status-canceled-text)" }; default: return { background: "var(--color-bg-muted)", color: "var(--color-text-muted)" }; } } export function isWorkOrderUpliftsReadOnly(status: WorkOrderTableRow["status"]): boolean { return status === "Completed" || status === "Canceled"; } export type UpliftAllowanceHelperTone = "muted" | "success" | "warning"; export type UpliftAllowanceHelperIcon = "check" | "alert" | null; export type UpliftAllowanceHelper = { text: string; tone: UpliftAllowanceHelperTone; tint: boolean; icon: UpliftAllowanceHelperIcon; }; export function upliftAllowanceHelper( amount: number, remainingAllowance: number, ): UpliftAllowanceHelper { const typed = Number.isFinite(amount) && amount > 0; if (!typed) { if (remainingAllowance > 0) { return { text: `${formatUpliftMoney(remainingAllowance)} left to auto-approve`, tone: "muted", tint: false, icon: null, }; } return { text: "Limit used — new uplifts need admin approval", tone: "warning", tint: false, icon: "alert", }; } if (amount <= remainingAllowance) { return { text: `Approves instantly · ${formatUpliftMoney(remainingAllowance - amount)} left after this`, tone: "success", tint: false, icon: "check", }; } return { text: `Needs admin approval · ${formatUpliftMoney(remainingAllowance)} limit`, tone: "warning", tint: true, icon: "alert", }; } export function canRevokeWorkOrderUplift( uplift: Pick, currentUserId: string | number | null | undefined, currentUserIsAdmin: boolean, ): boolean { if (uplift.status !== "auto_approved") { // why: SH-214/SH-212 keep admin-approved revoke on Uplift Approvals, not this work order panel. return false; } if (currentUserIsAdmin) { return false; } return ( currentUserId != null && currentUserId !== "" && uplift.requestedById != null && String(uplift.requestedById) === String(currentUserId) ); } export function upliftRevokeRequiresReason(status: WorkOrderUpliftStatus): boolean { return status === "approved"; } export const UPLIFT_STANDARD_ALLOWANCE_USD = 500; export const UPLIFT_EMERGENCY_ALLOWANCE_USD = 5000; export function getUpliftAllowanceCap(workOrderType: string): number { return workOrderType === "Emergency" ? UPLIFT_EMERGENCY_ALLOWANCE_USD : UPLIFT_STANDARD_ALLOWANCE_USD; } export function hasOpenWorkOrderUplift( uplifts: ReadonlyArray>, ): boolean { return uplifts.some((uplift) => uplift.status === "pending"); } export function getRemainingUpliftAllowance( workOrderType: string, uplifts: ReadonlyArray>, ): number { const consumed = uplifts .filter((uplift) => uplift.status === "auto_approved") .reduce((sum, uplift) => sum + uplift.amount, 0); return Math.max(0, getUpliftAllowanceCap(workOrderType) - consumed); } function displayActor(name: string): string { return name.trim() === "" ? "—" : name; } export function formatUpliftAbsoluteDate(iso: string): string { if (iso.trim() === "") return "—"; const date = new Date(iso); if (Number.isNaN(date.getTime())) return "—"; return date.toLocaleDateString("en-US", { month: "short", day: "numeric", year: "numeric" }); } export function formatUpliftRelativeTime(iso: string, nowMs: number): string { if (iso.trim() === "") return "—"; const timestamp = new Date(iso).getTime(); if (Number.isNaN(timestamp)) return "—"; const minutes = Math.floor((nowMs - timestamp) / 60000); if (minutes < 1) return "just now"; if (minutes < 60) return `${minutes}m ago`; const hours = Math.floor(minutes / 60); if (hours < 24) return `${hours}h ago`; return `${Math.floor(hours / 24)}d ago`; } type UpliftMetaSource = Pick< WorkOrderUplift, "status" | "requestedAt" | "requestedByName" | "decidedAt" | "decidedByName" >; export function formatUpliftCardMeta(uplift: UpliftMetaSource, nowMs: number): string { const requester = displayActor(uplift.requestedByName); const decider = displayActor(uplift.decidedByName); if (uplift.status === "pending") { return `${requester} · ${formatUpliftRelativeTime(uplift.requestedAt, nowMs)}`; } if (uplift.status === "auto_approved") { return `Within allowance · ${requester} · ${formatUpliftAbsoluteDate(uplift.requestedAt)}`; } if (uplift.status === "approved") { return `Approved by ${decider} · ${formatUpliftAbsoluteDate(uplift.decidedAt)}`; } if (uplift.status === "rejected") { return `Rejected by ${decider} · ${formatUpliftAbsoluteDate(uplift.decidedAt)}`; } if (uplift.status === "revoked") { return `Revoked by ${decider} · ${formatUpliftAbsoluteDate(uplift.decidedAt)}`; } const when = uplift.decidedAt.trim() === "" ? uplift.requestedAt : uplift.decidedAt; return `Cancelled by ${requester} · ${formatUpliftAbsoluteDate(when)}`; } export function isVoidUpliftStatus(status: WorkOrderUpliftStatus): boolean { return status === "rejected" || status === "cancelled" || status === "revoked"; } export type UpliftCellVisual = | { kind: "pending"; amount: string | null; statusLabel: "Pending" } | { kind: "approved"; amount: string | null; statusLabel: "Approved" } | { kind: "rejected"; amount: string | null; statusLabel: "Rejected" } | { kind: "plain"; label: string }; export function getUpliftCellVisual(summary: UpliftSummary): UpliftCellVisual | null { const label = getUpliftCellLabel(summary); if (label == null) return null; if (summary.pendingCount > 0) { return { kind: "pending", amount: summary.amount != null ? formatUpliftMoney(summary.amount) : null, statusLabel: "Pending", }; } if (summary.primaryStatus === "approved" || summary.primaryStatus === "auto_approved") { return { kind: "approved", amount: summary.amount != null ? formatUpliftMoney(summary.amount) : null, statusLabel: "Approved", }; } if (summary.primaryStatus === "rejected") { return { kind: "rejected", amount: summary.amount != null ? formatUpliftMoney(summary.amount) : null, statusLabel: "Rejected", }; } return { kind: "plain", label }; }