import type { WorkOrderBoardCreatePayload, WorkOrderTypeApi, } from "@/domain/work-orders/types/work-order-board"; import { WORK_ORDER_TYPE_API } from "@/domain/work-orders/types/work-order-board"; import type { WOType, WorkOrderWizardDraft } from "@/domain/work-orders/types/work-order-wizard"; import { fmtDateRange, formatApptTimeValue, mondayOf, } from "@/domain/work-orders/utils/wizard-date-utils"; const WO_TYPE_TO_API: Record = { PM: WORK_ORDER_TYPE_API.PM, Emergency: WORK_ORDER_TYPE_API.Emergency, Reactive: WORK_ORDER_TYPE_API.Reactive, "Add-On": WORK_ORDER_TYPE_API.AddOn, Overdue: WORK_ORDER_TYPE_API.Other, }; /** Leading token of a location label, e.g. `BK5` from `BK5 · Dallas` or `CHI-1042` from `CHI-1042 — Chicago`. */ export function parseSiteCodeFromLocationName(name: string): string { const trimmed = name.trim(); if (!trimmed) { return ""; } const match = trimmed.match(/^([A-Za-z0-9-]+)/); return match?.[1] ?? ""; } export function resolveSiteCode(option: { siteCode?: string; name?: string; }): string { if (option.siteCode?.trim()) { return option.siteCode.trim(); } return parseSiteCodeFromLocationName(option.name ?? ""); } export function mapWizardTypeToApi(type: WOType | ""): WorkOrderTypeApi | null { if (!type) { return null; } return WO_TYPE_TO_API[type]; } /** * Legacy/general description only. * Service notes, extras, POC, and vendor notes are sent as structured fields. */ export function buildBoardDescription(draft: WorkOrderWizardDraft): string { const parts: string[] = []; if (draft.schedMode === "range" && draft.scheduledOn && draft.scheduleEnd) { parts.push(`Schedule range: ${fmtDateRange(draft.scheduledOn, draft.scheduleEnd)}`); } return parts.join("\n"); } function trimOrUndefined(value: string | undefined): string | undefined { const trimmed = value?.trim(); return trimmed ? trimmed : undefined; } function normalizeExtraServices( primary: string | undefined, extras: string[], ): string[] | undefined { if (extras.length === 0) { return undefined; } const seen = new Set(); const primaryKey = primary?.toLowerCase() ?? ""; const out: string[] = []; for (const raw of extras) { const trimmed = raw.trim(); if (!trimmed) { continue; } const key = trimmed.toLowerCase(); if (key === primaryKey || seen.has(key)) { continue; } seen.add(key); out.push(trimmed); } return out.length > 0 ? out : undefined; } export function mapWizardDraftToBoardCreatePayload( draft: WorkOrderWizardDraft, ): WorkOrderBoardCreatePayload { const workOrderType = mapWizardTypeToApi(draft.type); const siteCode = draft.siteCode.trim() || resolveSiteCode({ name: draft.locationName }); if (workOrderType === null) { throw new Error("Work order type is required"); } if (!siteCode) { throw new Error("Site code is required"); } const payload: WorkOrderBoardCreatePayload = { workOrderType, siteCode, }; if (draft.woNumber.trim()) { payload.woNumber = draft.woNumber.trim(); } if (draft.assigneeId.trim()) { payload.assignTo = draft.assigneeId.trim(); } if (draft.dueDate) { payload.dueDate = draft.dueDate; } const isRange = draft.schedMode === "range"; if (!isRange && draft.scheduleWeekOnly) { payload.scheduleWeekOnly = true; const week = draft.targetWeek?.trim() || (draft.scheduledOn ? mondayOf(draft.scheduledOn) : ""); if (week) { payload.targetWeek = week; } } else if (!isRange && draft.scheduledOn) { payload.scheduledDate = draft.scheduledOn; } const vendorId = draft.vendorId.trim() ? Number(draft.vendorId) : NaN; const hasVendor = Number.isFinite(vendorId); if (hasVendor) { payload.vendorId = vendorId; } const apptTime = formatApptTimeValue(draft.apptStart, draft.apptEnd); if (hasVendor && (draft.scheduledOn || apptTime) && !draft.scheduleWeekOnly && !isRange) { if (draft.scheduledOn) { payload.apptDate = draft.scheduledOn; } if (apptTime) { payload.apptTime = apptTime; } } const primaryService = trimOrUndefined(draft.pm); if (primaryService) { payload.primaryService = primaryService; } const extraServices = normalizeExtraServices(primaryService, draft.extraServices); if (extraServices) { payload.extraServices = extraServices; } const serviceNotes = trimOrUndefined(draft.pmNote); if (serviceNotes) { payload.serviceNotes = serviceNotes; } const pocName = trimOrUndefined(draft.pocName); if (pocName) { payload.pocName = pocName; } const pocPhone = trimOrUndefined(draft.pocPhone); if (pocPhone) { payload.pocPhone = pocPhone; } const pocNotes = trimOrUndefined(draft.pocNotes); if (pocNotes) { payload.pocNotes = pocNotes; } const techPhone = trimOrUndefined(draft.techPhone); if (techPhone) { payload.techPhone = techPhone; } const vendorNotes = trimOrUndefined(draft.vendorNotes); if (vendorNotes) { payload.vendorNotes = vendorNotes; } const description = buildBoardDescription(draft); if (description) { payload.description = description; } const locationId = draft.locationId.trim() ? Number(draft.locationId) : NaN; if (Number.isFinite(locationId)) { payload.locationId = locationId; } return payload; }