import { readDocStatusFromRecord } from "@/domain/work-orders/mappers/doc-status-mapper"; import { parseSiteCodeFromLocationName } from "@/domain/work-orders/mappers/work-order-board-mapper"; import type { EmployeeOption, LocationOption, TaskListTemplate, WorkOrderAuditEntry, WorkOrderComment, WorkOrderDetail, WorkOrderDispatchSummary, WorkOrderFormValues, WorkOrderListItem, } from "@/domain/work-orders/types/work-order"; import type { UpliftRequest } from "@/domain/uplifts/types/uplift"; import type { WorkOrderStatus } from "@/domain/work-orders/types/work-order-common"; import { WORK_ORDER_STATUS_OPTIONS } from "@/domain/work-orders/types/work-order-common"; import { toDateInputValue } from "@/lib/time-utils"; export { mapDocStatusToApi } from "@/domain/work-orders/mappers/doc-status-mapper"; function asRecord(raw: unknown): Record { return typeof raw === "object" && raw !== null ? (raw as Record) : {}; } function toWorkOrderStatus(value: string): WorkOrderStatus { if (WORK_ORDER_STATUS_OPTIONS.includes(value as WorkOrderStatus)) { return value as WorkOrderStatus; } const fromEditEnum: Record = { Open: "Open", InProgress: "In Progress", OnHold: "On Hold", Completed: "Done", Cancelled: "Cancelled", Canceled: "Cancelled", }; return fromEditEnum[value] ?? "Open"; } function readString(record: Record, ...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 readId(record: Record): string | number { const value = record.id ?? record.Id; if (typeof value === "string" || typeof value === "number") { return value; } return ""; } function readNumber(record: Record, ...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 readIdList(raw: unknown): number[] | undefined { if (!Array.isArray(raw)) { return undefined; } const ids: number[] = []; for (const entry of raw) { if (typeof entry === "number" && !Number.isNaN(entry)) { ids.push(entry); continue; } if (typeof entry === "string" && entry !== "") { const parsed = Number(entry); if (!Number.isNaN(parsed)) { ids.push(parsed); } continue; } if (typeof entry === "object" && entry !== null) { const id = readNumber( asRecord(entry), "id", "Id", "contactId", "ContactId", "categoryId", "CategoryId", ); if (id != null) { ids.push(id); } } } return ids; } function readOptionalIdList( record: Record, ...keys: string[] ): number[] | undefined { for (const key of keys) { if (!(key in record)) { continue; } const list = readIdList(record[key]); if (list !== undefined) { return list; } } return undefined; } const FORM_STATUS_TO_EDIT_ENUM: Record = { Open: "Open", "In Progress": "InProgress", InProgress: "InProgress", "On Hold": "OnHold", OnHold: "OnHold", Done: "Completed", Completed: "Completed", Cancelled: "Cancelled", Canceled: "Cancelled", }; const FORM_PRIORITY_TO_EDIT_ENUM: Record = { "Sev 1": "Critical", "Sev 2": "High", "Sev 3": "Medium", "Sev 4": "Low", "Sev 5": "Low", Low: "Low", Medium: "Medium", High: "High", Critical: "Critical", }; export function mapFormStatusToEditEnum(status: string): string { return FORM_STATUS_TO_EDIT_ENUM[status] ?? status; } export function mapFormPriorityToEditEnum(priority: string): string { return FORM_PRIORITY_TO_EDIT_ENUM[priority] ?? priority; } function appendIfPresent( formData: FormData, key: string, value: string | number | null | undefined, ): void { if (value === null || value === undefined) { return; } const asString = String(value).trim(); if (asString === "") { return; } formData.append(key, asString); } function appendIdList(formData: FormData, key: string, ids: number[] | undefined): void { if (ids === undefined) { return; } for (const id of ids) { formData.append(key, String(id)); } } function readBool(record: Record, ...keys: string[]): boolean { for (const key of keys) { const value = record[key]; if (typeof value === "boolean") { return value; } } return false; } export function mapWorkOrderListItem(raw: unknown): WorkOrderListItem { const item = asRecord(raw); return { id: readId(item), number: readString(item, "internalWONumber", "number", "workOrderNumber"), title: readString(item, "title", "Title"), location: readString(item, "location", "Location", "locationName", "LocationName"), priority: readString(item, "priority", "Priority"), status: readString(item, "status", "Status"), createdDate: readString(item, "createdDate", "CreatedDate"), dueDate: readString(item, "dueDate", "DueDate"), assignedTo: readString(item, "assignedTo", "AssignedTo", "employeeName", "EmployeeName"), lastUpdated: readString(item, "lastUpdated", "LastUpdated"), }; } export function mapWorkOrderListItems(rawItems: unknown[]): WorkOrderListItem[] { return rawItems.map(mapWorkOrderListItem); } export { mapWorkOrderTableRow, mapWorkOrderTableRows } from "./work-order-table-row-mapper"; export function mapWorkOrderComment(raw: unknown): WorkOrderComment { const item = asRecord(raw); const authorId = readString(item, "authorId", "AuthorId") || null; return { id: readId(item), createdDate: readString(item, "createdDate", "CreatedDate"), commenttext: readString(item, "commenttext", "Commenttext", "text", "Text"), commentType: readString(item, "commentType", "CommentType"), userName: readString(item, "userName", "UserName", "authorName", "AuthorName"), authorId, commenter: readString(item, "commenter", "Commenter") || null, recordType: readString(item, "recordType", "RecordType") || null, }; } export function mapWorkOrderAuditEntry(raw: unknown): WorkOrderAuditEntry { const item = asRecord(raw); const typeRaw = readString(item, "type", "Type").toLowerCase(); const type = typeRaw === "system" ? ("system" as const) : typeRaw === "manual" ? ("manual" as const) : undefined; return { id: readId(item), fieldName: readString(item, "fieldName", "FieldName"), oldValue: readString(item, "oldValue", "OldValue"), newValue: readString(item, "newValue", "NewValue"), action: readString(item, "action", "Action"), createdAt: readString(item, "createdAt", "CreatedAt", "time", "Time"), userName: readString(item, "userName", "UserName"), type, dispatcherId: readString(item, "dispatcherId", "DispatcherId") || undefined, }; } export function mapWorkOrderDispatchSummary(raw: unknown): WorkOrderDispatchSummary { const item = asRecord(raw); return { id: readId(item), vendorId: readId({ id: item.vendorId ?? item.VendorId }), dispatchNumber: readString(item, "dispatchNumber", "DispatchNumber"), poNumber: readString(item, "poNumber", "PoNumber", "PONumber"), vendorName: readString(item, "vendorName", "VendorName"), vendorEmail: readString(item, "vendorEmail", "VendorEmail"), nteAmount: readNumber(item, "nteAmount", "NTEAmount"), status: readString(item, "status", "Status"), dispatchedAt: readString(item, "dispatchedAt", "DispatchedAt"), estimatedArrivalAt: readString(item, "estimatedArrivalAt", "EstimatedArrivalAt"), etaManualOverride: readBool(item, "etaManualOverride", "EtaManualOverride"), }; } export function mapWorkOrderDetail(raw: unknown): WorkOrderDetail { const wo = asRecord(raw); const locationId = wo.locationId ?? wo.LocationId; const contactIds = readOptionalIdList(wo, "contactIds", "ContactIds", "contacts", "Contacts"); const categoryIds = readOptionalIdList( wo, "categoryIds", "CategoryIds", "categories", "Categories", ); return { id: readId(wo), internalWONumber: readString(wo, "internalWONumber", "InternalWONumber"), workOrderNumber: readString(wo, "workerOrderNumber", "workOrderNumber", "WorkerOrderNumber"), title: readString(wo, "title", "workerOrderTitle", "WorkerOrderTitle"), location: readString(wo, "location", "Location"), locationId: locationId != null ? String(locationId) : "", locationZip: readString(wo, "locationZip", "LocationZip"), assignedTo: readString(wo, "assignee", "assignedTo", "Assignee", "AssignedTo"), dueDate: toDateInputValue(readString(wo, "dueDate", "DueDate")), createdDate: toDateInputValue(readString(wo, "createdDate", "CreatedDate")), createdDateFull: readString(wo, "createdDate", "CreatedDate"), description: readString(wo, "description", "Description"), priority: readString(wo, "priority", "Priority"), status: readString(wo, "status", "Status") || "Open", po: readString(wo, "po", "PO"), tt: readString(wo, "tt", "TT"), problem: readString(wo, "problem", "Problem"), trade: readString(wo, "trade", "Trade"), subTrade: readString(wo, "subTrade", "SubTrade"), vendorNTE: wo.vendorNTE != null ? String(wo.vendorNTE) : "", scheduledDate: toDateInputValue(readString(wo, "scheduledDate", "ScheduledDate")), completedDate: toDateInputValue(readString(wo, "completedDate", "CompletedDate")), // DocStatus is a board field — persist via PATCH …/board, not EditWorkorder. docStatus: readDocStatusFromRecord(wo), source: readString(wo, "source", "Source"), ...(contactIds !== undefined ? { contactIds } : {}), ...(categoryIds !== undefined ? { categoryIds } : {}), comments: Array.isArray(wo.comments) ? wo.comments.map(mapWorkOrderComment) : Array.isArray(wo.Comments) ? wo.Comments.map(mapWorkOrderComment) : [], auditLog: Array.isArray(wo.auditLog) ? wo.auditLog.map(mapWorkOrderAuditEntry) : Array.isArray(wo.AuditLog) ? wo.AuditLog.map(mapWorkOrderAuditEntry) : [], dispatches: Array.isArray(wo.dispatches) ? wo.dispatches.map(mapWorkOrderDispatchSummary) : Array.isArray(wo.Dispatches) ? wo.Dispatches.map(mapWorkOrderDispatchSummary) : [], }; } export function mapWorkOrderDetailToFormValues(detail: WorkOrderDetail): WorkOrderFormValues { return { internalWONumber: detail.internalWONumber, workOrderNumber: detail.workOrderNumber, title: detail.title, locationId: detail.locationId, priority: detail.priority, status: toWorkOrderStatus(detail.status), createdDate: detail.createdDate, dueDate: detail.dueDate, scheduledDate: detail.scheduledDate, completedDate: detail.completedDate, docStatus: detail.docStatus, assignedTo: detail.assignedTo, description: detail.description, po: detail.po, tt: detail.tt, problem: detail.problem, trade: detail.trade, subTrade: detail.subTrade, vendorNTE: detail.vendorNTE, source: detail.source, ...(detail.contactIds !== undefined ? { contactIds: detail.contactIds } : {}), ...(detail.categoryIds !== undefined ? { categoryIds: detail.categoryIds } : {}), }; } export function mapWorkOrderFormToCreatePayload( values: WorkOrderFormValues, ): Record { return { WorkerOrderNumber: values.workOrderNumber, WorkerOrderTitle: values.title, LocationId: values.locationId ? parseInt(values.locationId, 10) : null, Priority: values.priority || null, Status: values.status, CreatedDate: values.createdDate || null, DueDate: values.dueDate || null, ScheduledDate: values.scheduledDate || null, CompletedDate: values.completedDate || null, AssignTo: values.assignedTo || null, Description: values.description, PO: values.po || null, TT: values.tt || null, Problem: values.problem || null, Trade: values.trade || null, SubTrade: values.subTrade || null, VendorNTE: values.vendorNTE ? parseFloat(values.vendorNTE) : null, Source: values.source || null, }; } export function mapWorkOrderFormToUpdateFormData( id: string | number, values: WorkOrderFormValues, ): FormData { const formData = new FormData(); formData.append("Id", String(id)); appendIfPresent(formData, "WorkerOrderTitle", values.title); appendIfPresent(formData, "Description", values.description); appendIfPresent(formData, "Status", mapFormStatusToEditEnum(values.status)); appendIfPresent(formData, "Priority", mapFormPriorityToEditEnum(values.priority)); const locationId = values.locationId ? parseInt(values.locationId, 10) : NaN; appendIfPresent(formData, "LocationId", Number.isNaN(locationId) ? undefined : locationId); appendIfPresent(formData, "AssignTo", values.assignedTo); appendIfPresent(formData, "DueDate", values.dueDate); appendIfPresent(formData, "CompletedDate", values.completedDate); appendIdList(formData, "ContactIds", values.contactIds); appendIdList(formData, "CategoryIds", values.categoryIds); return formData; } export function mapLocationOption(raw: unknown): LocationOption { const item = asRecord(raw); const name = readString(item, "name", "Name", "locationName", "LocationName"); const siteCode = readString(item, "siteCode", "SiteCode", "code", "Code") || parseSiteCodeFromLocationName(name); return { id: readId(item), name, ...(siteCode ? { siteCode } : {}), }; } export function mapLocationOptions(rawItems: unknown[]): LocationOption[] { return rawItems.map(mapLocationOption); } export function mapEmployeeOption(raw: unknown): EmployeeOption { const item = asRecord(raw); return { id: readId(item), name: readString(item, "name", "Name", "fullName", "FullName", "employeeName", "EmployeeName") || readString(item, "userName", "UserName"), }; } export function mapEmployeeOptions(rawItems: unknown[]): EmployeeOption[] { return rawItems.map(mapEmployeeOption); } export { mapVendorDropdownItem, mapVendorDropdownItems } from "./work-order-vendor-mapper"; export function mapTaskListTemplate(raw: unknown): TaskListTemplate { const item = asRecord(raw); const items = item.items ?? item.Items; return { id: readId(item), name: readString(item, "name", "Name"), items: Array.isArray(items) ? items : [], }; } export function mapTaskListTemplates(rawItems: unknown[]): TaskListTemplate[] { return rawItems.map(mapTaskListTemplate); } export { mapDispatchChecklistItem, mapDispatchComment, mapDispatchDetail, mapDispatchSignoff, mapDispatchToVendorPayload, mapDispatchWorkOrderRef, mapUpdateDispatchPayload, } from "./work-order-dispatch-mapper"; export function mapUpliftRequest(raw: unknown): UpliftRequest { const item = asRecord(raw); return { id: readId(item), status: readString(item, "status", "Status"), currentNTE: readNumber(item, "currentNTE", "CurrentNTE"), requestedNTE: readNumber(item, "requestedNTE", "RequestedNTE") ?? 0, delta: readNumber(item, "delta", "Delta") ?? 0, vendorReason: readString(item, "vendorReason", "VendorReason"), requestedAt: readString(item, "requestedAt", "RequestedAt"), requestedByVendorName: readString(item, "requestedByVendorName", "RequestedByVendorName"), decidedAt: readString(item, "decidedAt", "DecidedAt"), decidedByName: readString(item, "decidedByName", "DecidedByName"), decisionNote: readString(item, "decisionNote", "DecisionNote"), requiredTier: readNumber(item, "requiredTier", "RequiredTier") ?? 1, canDecide: readBool(item, "canDecide", "CanDecide"), }; } export function mapUpliftRequests(rawItems: unknown[]): UpliftRequest[] { return rawItems.map(mapUpliftRequest); }