shoc-frontend-new/src/domain/work-orders/mappers/work-order-uplift-mapper.ts
Arthur Bassi 32466c01d2 fix(work-orders): open uplifts from the slide-over and send evidence (SH-388)
Dispatchers need the design surface from the column and the detail,
and an optional evidence file is sent only after the scan passes.
2026-09-24 14:30:24 -03:00

207 lines
6 KiB
TypeScript

import type {
UpliftSummary,
WorkOrderUplift,
WorkOrderUpliftAttachment,
WorkOrderUpliftStatus,
} from "@/domain/work-orders/types/work-order-uplift";
import { WORK_ORDER_UPLIFT_STATUSES } from "@/domain/work-orders/types/work-order-uplift";
function asRecord(raw: unknown): Record<string, unknown> {
return typeof raw === "object" && raw !== null ? (raw as Record<string, unknown>) : {};
}
function readString(record: Record<string, unknown>, ...keys: string[]): string {
for (const key of keys) {
const value = record[key];
if (typeof value === "string") {
return value;
}
if (typeof value === "number") {
return String(value);
}
}
return "";
}
function readNumber(record: Record<string, unknown>, ...keys: string[]): number | null {
for (const key of keys) {
const value = record[key];
if (typeof value === "number") {
return value;
}
if (typeof value === "string" && value !== "") {
const parsed = Number(value);
if (!Number.isNaN(parsed)) {
return parsed;
}
}
}
return null;
}
function readBool(record: Record<string, unknown>, ...keys: string[]): boolean {
for (const key of keys) {
const value = record[key];
if (typeof value === "boolean") {
return value;
}
}
return false;
}
function readId(record: Record<string, unknown>): string | number {
const value = record.id ?? record.Id;
if (typeof value === "string" || typeof value === "number") {
return value;
}
return "";
}
function readOptionalId(
record: Record<string, unknown>,
...keys: string[]
): string | number | null {
for (const key of keys) {
const value = record[key];
if (typeof value === "string" || typeof value === "number") {
return value;
}
}
return null;
}
const STATUS_SET = new Set<string>(WORK_ORDER_UPLIFT_STATUSES);
function normalizeUpliftStatus(raw: string): WorkOrderUpliftStatus {
const normalized = raw.trim().toLowerCase().replace(/\s+/g, "_");
if (STATUS_SET.has(normalized)) {
return normalized as WorkOrderUpliftStatus;
}
if (normalized === "canceled") {
return "cancelled";
}
if (normalized === "autoapproved") {
return "auto_approved";
}
return "pending";
}
export function mapUpliftSummary(raw: unknown): UpliftSummary {
const item = asRecord(raw);
const nested = asRecord(item.upliftSummary ?? item.UpliftSummary);
const hasNested = Object.keys(nested).length > 0;
const source = hasNested ? nested : item;
const pendingCount =
readNumber(source, "pendingCount", "PendingCount") ??
readNumber(item, "pendingUpliftCount", "PendingUpliftCount") ??
0;
const hasUplift =
readBool(source, "hasUplift", "HasUplift") ||
readBool(item, "hasUplift", "HasUplift") ||
pendingCount > 0;
// Never treat the work-order lifecycle status/Status as uplift primaryStatus.
const primaryStatusRaw = hasNested
? readString(source, "primaryStatus", "PrimaryStatus", "status", "Status")
: readString(item, "primaryStatus", "PrimaryStatus");
const amount = hasNested
? readNumber(source, "amount", "Amount")
: readNumber(item, "amount", "Amount");
const summary: UpliftSummary = {
hasUplift,
pendingCount,
};
if (primaryStatusRaw) {
summary.primaryStatus = normalizeUpliftStatus(primaryStatusRaw);
}
if (amount != null) {
summary.amount = amount;
}
return summary;
}
function mapUpliftAttachment(raw: unknown, index: number): WorkOrderUpliftAttachment | null {
const record = asRecord(raw);
const name = readString(record, "name", "Name", "fileName", "FileName");
if (name === "") return null;
const idValue = record.id ?? record.Id;
const id =
typeof idValue === "string" || typeof idValue === "number"
? String(idValue)
: `${name}-${index}`;
return {
id,
name,
url: readString(record, "url", "Url", "fileUrl", "FileUrl"),
};
}
function mapUpliftAttachments(raw: unknown): WorkOrderUpliftAttachment[] {
if (!Array.isArray(raw)) return [];
return raw.flatMap((item, index) => {
const attachment = mapUpliftAttachment(item, index);
return attachment == null ? [] : [attachment];
});
}
function mapEvidenceAttachment(item: Record<string, unknown>): WorkOrderUpliftAttachment[] {
const listed = mapUpliftAttachments(
item.attachments ?? item.Attachments ?? item.documents ?? item.Documents,
);
if (listed.length > 0) return listed;
const evidenceId = readOptionalId(item, "evidenceDocumentId", "EvidenceDocumentId");
const name = readString(
item,
"evidenceFileName",
"EvidenceFileName",
"originalFileName",
"OriginalFileName",
);
if (evidenceId == null && name === "") return [];
return [
{
id: evidenceId == null ? "evidence" : String(evidenceId),
name: name === "" ? `Document #${evidenceId}` : name,
url: "",
},
];
}
export function mapWorkOrderUplift(raw: unknown): WorkOrderUplift {
const item = asRecord(raw);
const statusRaw = readString(item, "status", "Status");
return {
id: readId(item),
status: statusRaw ? normalizeUpliftStatus(statusRaw) : "pending",
amount: readNumber(item, "amount", "Amount", "requestedNTE", "RequestedNTE") ?? 0,
notes: readString(item, "notes", "Notes", "vendorReason", "VendorReason"),
requestedAt: readString(item, "requestedAt", "RequestedAt"),
requestedById: readOptionalId(
item,
"requestedById",
"RequestedById",
"requesterId",
"RequesterId",
),
requestedByName: readString(
item,
"requestedByName",
"RequestedByName",
"requestedByVendorName",
"RequestedByVendorName",
),
decidedAt: readString(item, "decidedAt", "DecidedAt"),
decidedByName: readString(item, "decidedByName", "DecidedByName"),
decisionNote: readString(item, "decisionNote", "DecisionNote", "revokeReason", "RevokeReason"),
attachments: mapEvidenceAttachment(item),
};
}
export function mapWorkOrderUplifts(rawItems: unknown[]): WorkOrderUplift[] {
return rawItems.map(mapWorkOrderUplift);
}