2026-08-10 17:12:10 -03:00
|
|
|
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 STATUS_LABELS: Record<WorkOrderUpliftStatus, string> = {
|
|
|
|
|
pending: "Pending",
|
|
|
|
|
approved: "Approved",
|
|
|
|
|
auto_approved: "Auto",
|
|
|
|
|
rejected: "Rejected",
|
|
|
|
|
cancelled: "Cancelled",
|
|
|
|
|
revoked: "Revoked",
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export function formatUpliftStatusLabel(status: WorkOrderUpliftStatus): string {
|
|
|
|
|
return STATUS_LABELS[status];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function getUpliftCellLabel(summary: UpliftSummary): string | null {
|
2026-08-10 19:09:25 -03:00
|
|
|
if (!summary.hasUplift && summary.pendingCount <= 0) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
2026-08-10 17:12:10 -03:00
|
|
|
if (summary.pendingCount > 0) {
|
|
|
|
|
return summary.pendingCount === 1 ? "1 Pending" : `${summary.pendingCount} Pending`;
|
|
|
|
|
}
|
|
|
|
|
if (summary.primaryStatus) {
|
|
|
|
|
const label = formatUpliftStatusLabel(summary.primaryStatus);
|
|
|
|
|
if (summary.amount != null) {
|
|
|
|
|
return `${label} ${formatUpliftMoney(summary.amount)}`;
|
|
|
|
|
}
|
|
|
|
|
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":
|
|
|
|
|
return { background: "var(--status-success-bg, #DCFCE7)", color: "var(--color-success)" };
|
|
|
|
|
case "auto_approved":
|
|
|
|
|
return { background: "var(--color-primary-tint)", color: "var(--color-primary)" };
|
|
|
|
|
case "rejected":
|
|
|
|
|
return { background: "#FEE2E2", color: "#B91C1C" };
|
|
|
|
|
case "cancelled":
|
|
|
|
|
case "revoked":
|
|
|
|
|
return { background: "var(--color-bg-muted)", color: "var(--color-text-muted)" };
|
|
|
|
|
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 function canOpenUpliftsDialog(
|
|
|
|
|
summary: UpliftSummary,
|
|
|
|
|
status: WorkOrderTableRow["status"],
|
|
|
|
|
): boolean {
|
|
|
|
|
if (summary.hasUplift || summary.pendingCount > 0) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return !isWorkOrderUpliftsReadOnly(status);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function canRevokeWorkOrderUplift(
|
|
|
|
|
uplift: Pick<WorkOrderUplift, "status" | "requestedById">,
|
|
|
|
|
currentUserId: string | number | null | undefined,
|
|
|
|
|
isAdmin: boolean,
|
|
|
|
|
): boolean {
|
|
|
|
|
if (uplift.status === "auto_approved") {
|
|
|
|
|
// SH-196: auto-approved revoke is dispatcher-own only (no admin bypass).
|
|
|
|
|
return (
|
|
|
|
|
currentUserId != null &&
|
|
|
|
|
currentUserId !== "" &&
|
|
|
|
|
uplift.requestedById != null &&
|
|
|
|
|
String(uplift.requestedById) === String(currentUserId)
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
return isAdmin && uplift.status === "approved";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function upliftRevokeRequiresReason(status: WorkOrderUpliftStatus): boolean {
|
|
|
|
|
return status === "approved";
|
|
|
|
|
}
|