import { CLAIM_CAR_ANGLE_KEYS, hasClaimCarAngleCapture, hasDamagedPartCapture, } from "src/helpers/claim-car-angle-media"; import { catalogLikeKeyFromPart, normalizeDamageSelectedParts, } from "src/helpers/outer-damage-parts"; import type { ClaimVehicleTypeV2 } from "src/static/outer-car-parts-catalog"; /** Upload during CAPTURE_PART_DAMAGES (after parts + angles). */ export const CAPTURE_PHASE_DAMAGED_PARTY_DOC_KEYS = [ "damaged_chassis_number", "damaged_engine_photo", "damaged_metal_plate", ] as const; /** Metal-plate keys that are optional in the V4/V5 FileMaker flow. */ export const OPTIONAL_CAPTURE_PHASE_DOC_KEYS_V4V5 = [ "damaged_metal_plate", "guilty_metal_plate", ] as const; export type CapturePhaseSequence = | "parts" | "angles" | "capture_phase_documents" | "complete"; export interface ClaimCaptureProgress { partsCaptured: number; partsTotal: number; partsComplete: boolean; anglesCaptured: number; anglesTotal: number; anglesComplete: boolean; capturePhaseDocsComplete: boolean; capturePhaseDocsRemaining: number; sequencePhase: CapturePhaseSequence; } export function isCapturePhaseDamagedPartyDocKey(key: string): boolean { return (CAPTURE_PHASE_DAMAGED_PARTY_DOC_KEYS as readonly string[]).includes( key, ); } function isRequiredDocumentUploadedOnClaim( claimCase: any, key: string, ): boolean { const doc = (claimCase?.requiredDocuments as any)?.get?.(key) ?? claimCase?.requiredDocuments?.[key]; return !!doc?.uploaded; } export function getClaimCaptureProgress( claimCase: any, options?: { assumeCapturePhaseDocKey?: string; skipMetalPlate?: boolean }, ): ClaimCaptureProgress { const carType = claimCase?.vehicle?.carType as ClaimVehicleTypeV2 | undefined; const selectedNorm = normalizeDamageSelectedParts( claimCase?.damage?.selectedParts, carType, claimCase?.damage?.selectedOuterParts, ); const carAnglesData = claimCase?.media?.carAngles; const damagedPartsData = claimCase?.media?.damagedParts; const anglesCaptured = CLAIM_CAR_ANGLE_KEYS.filter((k) => hasClaimCarAngleCapture(carAnglesData, damagedPartsData, k), ).length; const partsCaptured = selectedNorm.filter((sp) => { const ck = sp.catalogKey ?? catalogLikeKeyFromPart(sp); return hasDamagedPartCapture(damagedPartsData, ck, selectedNorm); }).length; const partsTotal = selectedNorm.length; const completedSteps: string[] = claimCase?.workflow?.completedSteps ?? []; const outerPartsStepDone = completedSteps.includes("SELECT_OUTER_PARTS"); const partsComplete = partsTotal > 0 ? partsCaptured >= partsTotal : outerPartsStepDone; const anglesTotal = CLAIM_CAR_ANGLE_KEYS.length; const anglesComplete = anglesCaptured >= anglesTotal; const capturePhaseDocsRemaining = CAPTURE_PHASE_DAMAGED_PARTY_DOC_KEYS.filter( (k) => { if (options?.skipMetalPlate && OPTIONAL_CAPTURE_PHASE_DOC_KEYS_V4V5.includes(k as any)) return false; if (k === options?.assumeCapturePhaseDocKey) return false; return !isRequiredDocumentUploadedOnClaim(claimCase, k); }, ).length; const capturePhaseDocsComplete = capturePhaseDocsRemaining === 0; let sequencePhase: CapturePhaseSequence = "parts"; if (partsComplete && !anglesComplete) { sequencePhase = "angles"; } else if (partsComplete && anglesComplete && !capturePhaseDocsComplete) { sequencePhase = "capture_phase_documents"; } else if (partsComplete && anglesComplete && capturePhaseDocsComplete) { sequencePhase = "complete"; } return { partsCaptured, partsTotal, partsComplete, anglesCaptured, anglesTotal, anglesComplete, capturePhaseDocsComplete, capturePhaseDocsRemaining, sequencePhase, }; } export function isClaimCaptureStepComplete( claimCase: any, options?: { skipMetalPlate?: boolean }, ): boolean { const p = getClaimCaptureProgress(claimCase, options); return ( p.partsComplete && p.anglesComplete && p.capturePhaseDocsComplete ); } export function capturePhaseSequenceMessage(phase: CapturePhaseSequence): string { switch (phase) { case "parts": return "Upload photos for all selected damaged parts first."; case "angles": return "Capture all four car angles (front, back, left, right) next."; case "capture_phase_documents": return "Upload chassis number, engine photo, and damaged metal plate photos last."; default: return "Damage capture is complete."; } }