import { ReloadOutlined, RollbackOutlined } from "@ant-design/icons"; import { Button, Card, Col, Divider, Form, Input, InputNumber, Row, Select, Space, Statistic, Tooltip, Typography } from "antd"; import Dinero from "dinero.js"; import { useTranslation } from "react-i18next"; import { useEffect, useMemo, useState } from "react"; import dayjs from "../../utils/day"; /** * RR DMS Post Form component * Submit: "rr-export-job" * @param bodyshop * @param socket * @param job * @param logsRef * @param allocationsSummary * @param opCodeParts // { prefix, base, suffix } from container * @param onChangeOpCodeParts // (partsWithFlags) => void * @returns {JSX.Element} * @constructor */ export default function RRPostForm({ bodyshop, socket, job, logsRef, allocationsSummary, opCodeParts, onChangeOpCodeParts }) { const [form] = Form.useForm(); const { t } = useTranslation(); // Capture the baseline/default OpCode parts ONCE per mount (tied to resetKey in container) const [baselineOpCodeParts] = useState(() => ({ prefix: opCodeParts?.prefix ?? "", base: opCodeParts?.base ?? "", suffix: opCodeParts?.suffix ?? "" })); // Advisors const [advisors, setAdvisors] = useState([]); const [advLoading, setAdvLoading] = useState(false); const getAdvisorNumber = (a) => a?.advisorId; const getAdvisorLabel = (a) => `${a?.firstName || ""} ${a?.lastName || ""}`.trim(); const fetchRrAdvisors = (refresh = false) => { if (!socket) return; setAdvLoading(true); const onResult = (payload) => { try { const list = payload?.result ?? payload ?? []; setAdvisors(Array.isArray(list) ? list : []); } finally { setAdvLoading(false); socket.off("rr-get-advisors:result", onResult); } }; socket.once("rr-get-advisors:result", onResult); socket.emit("rr-get-advisors", { departmentType: "B", refresh }, (ack) => { if (ack?.ok) { const list = ack.result ?? []; setAdvisors(Array.isArray(list) ? list : []); } else if (ack) { console.error("Something went wrong fetching DMS Advisors"); } setAdvLoading(false); socket.off("rr-get-advisors:result", onResult); }); }; useEffect(() => { fetchRrAdvisors(false); }, [bodyshop?.id, socket]); const initialValues = useMemo( () => ({ story: `${t("jobs.labels.dms.defaultstory", { ro_number: job.ro_number, ownr_nm: `${job.ownr_fn || ""} ${job.ownr_ln || ""} ${job.ownr_co_nm || ""}`.trim(), ins_co_nm: job.ins_co_nm || "N/A", clm_po: `${job.clm_no ? `${job.clm_no} ` : ""}${job.po_number || ""}` }).trim()}.${ job.area_of_damage?.impact1 ? " " + t("jobs.labels.dms.damageto", { area_of_damage: (job.area_of_damage && job.area_of_damage.impact1.padStart(2, "0")) || "UNKNOWN" }) : "" }`.slice(0, 239), inservicedate: dayjs( `${ (job.v_model_yr && (job.v_model_yr < 100 ? job.v_model_yr >= (dayjs().year() + 1) % 100 ? 1900 + parseInt(job.v_model_yr, 10) : 2000 + parseInt(job.v_model_yr, 10) : job.v_model_yr)) || 2019 }-01-01` ), opPrefix: opCodeParts?.prefix ?? "", opBase: opCodeParts?.base ?? "", opSuffix: opCodeParts?.suffix ?? "" }), [job, t, opCodeParts] ); // Keep the RR OpCode parts in sync with DmsContainer state const opPrefixWatch = Form.useWatch("opPrefix", form); const opBaseWatch = Form.useWatch("opBase", form); const opSuffixWatch = Form.useWatch("opSuffix", form); // Detect if current form values differ from baseline defaults const isCustomOpCode = useMemo(() => { const current = { prefix: opPrefixWatch !== undefined ? opPrefixWatch : (baselineOpCodeParts.prefix ?? ""), base: opBaseWatch !== undefined ? opBaseWatch : (baselineOpCodeParts.base ?? ""), suffix: opSuffixWatch !== undefined ? opSuffixWatch : (baselineOpCodeParts.suffix ?? "") }; return ( current.prefix !== (baselineOpCodeParts.prefix ?? "") || current.base !== (baselineOpCodeParts.base ?? "") || current.suffix !== (baselineOpCodeParts.suffix ?? "") ); }, [opPrefixWatch, opBaseWatch, opSuffixWatch, baselineOpCodeParts]); // Push changes up to container with some metadata useEffect(() => { if (!onChangeOpCodeParts) return; const parts = { prefix: opPrefixWatch || "", base: opBaseWatch || "", suffix: opSuffixWatch || "", isCustom: isCustomOpCode }; onChangeOpCodeParts(parts); }, [opPrefixWatch, opBaseWatch, opSuffixWatch, isCustomOpCode, onChangeOpCodeParts]); const handleFinish = (values) => { if (!socket) return; const { opPrefix, opBase, opSuffix, ...rest } = values; const combinedOpCode = `${opPrefix || ""}${opBase || ""}${opSuffix || ""}`.trim(); const txEnvelope = { ...rest, opPrefix, opBase, opSuffix }; if (combinedOpCode) { txEnvelope.opCode = combinedOpCode; } socket.emit("rr-export-job", { bodyshopId: bodyshop?.id, jobId: job.id, job, txEnvelope }); logsRef?.current?.scrollIntoView({ behavior: "smooth" }); }; // Discrepancy is ignored for RR; we still show totals for operator context. // Use the lifted allocationsSummary from the container instead of reading from the socket. const totals = useMemo(() => { if (!allocationsSummary || allocationsSummary.length === 0) { return { totalSale: Dinero(), totalCost: Dinero() }; } return allocationsSummary.reduce( (acc, val) => ({ totalSale: acc.totalSale.add(Dinero(val.sale)), totalCost: acc.totalCost.add(Dinero(val.cost)) }), { totalSale: Dinero(), totalCost: Dinero() } ); }, [allocationsSummary]); const handleResetOpCode = () => { form.setFieldsValue({ opPrefix: baselineOpCodeParts.prefix, opBase: baselineOpCodeParts.base, opSuffix: baselineOpCodeParts.suffix }); }; // Check if early RO was created (job has all early RO fields) const hasEarlyRO = !!(job?.dms_id && job?.dms_customer_id && job?.dms_advisor_id); return ( {hasEarlyRO && ( ✅ {t("jobs.labels.dms.earlyro.created")} {job.dms_id}
{t("jobs.labels.dms.earlyro.willupdate")}
)}
{/* Advisor + inline Refresh - Only show if no early RO */} {!hasEarlyRO && ( )} {/* Validation */} {() => { // When early RO exists, advisor is already set, so we don't need to validate it const advisorOk = hasEarlyRO ? true : !!form.getFieldValue("advisorNo"); return ( = ); }}
); }