mirror of
https://github.com/Sea-Haven-Industries/shoc-frontend-new.git
synced 2026-10-01 03:53:11 +00:00
SH-214/SH-212 own that action on Uplift Approvals; the WO surface only revokes the requester's auto-approved uplift.
130 lines
4 KiB
TypeScript
130 lines
4 KiB
TypeScript
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 {
|
|
if (!summary.hasUplift && summary.pendingCount <= 0) {
|
|
return null;
|
|
}
|
|
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,
|
|
): boolean {
|
|
if (uplift.status !== "auto_approved") {
|
|
// SH-214/SH-212: admin-approved revoke lives on Uplift Approvals, not the WO dialog.
|
|
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<Pick<WorkOrderUplift, "status">>,
|
|
): boolean {
|
|
return uplifts.some((uplift) => uplift.status === "pending");
|
|
}
|
|
|
|
export function getRemainingUpliftAllowance(
|
|
workOrderType: string,
|
|
uplifts: ReadonlyArray<Pick<WorkOrderUplift, "status" | "amount">>,
|
|
): number {
|
|
const consumed = uplifts
|
|
.filter((uplift) => uplift.status === "auto_approved")
|
|
.reduce((sum, uplift) => sum + uplift.amount, 0);
|
|
return Math.max(0, getUpliftAllowanceCap(workOrderType) - consumed);
|
|
}
|