import { useMutation, useQuery } from "@apollo/client/react"; import { Button, Card, Form, InputNumber, Popover, Select, Space } from "antd"; import axios from "axios"; import { useState } from "react"; import { useTranslation } from "react-i18next"; import { connect } from "react-redux"; import { createStructuredSelector } from "reselect"; import { logImEXEvent } from "../../firebase/firebase.utils"; import { GET_LINE_TICKET_BY_PK } from "../../graphql/jobs-lines.queries"; import { UPDATE_JOB_STATUS } from "../../graphql/jobs.queries"; import { UPDATE_TIME_TICKET } from "../../graphql/timetickets.queries"; import { selectTechnician } from "../../redux/tech/tech.selectors"; import { selectBodyshop } from "../../redux/user/user.selectors"; import { CalculateAllocationsTotals } from "../labor-allocations-table/labor-allocations-table.utility"; import TechJobClockoutDelete from "../tech-job-clock-out-delete/tech-job-clock-out-delete.component"; import { LaborAllocationContainer } from "../time-ticket-modal/time-ticket-modal.component"; import { useTreatmentsWithConfig } from "../../feature-flags/splitio-react-replacement"; import { useNotification } from "../../contexts/Notifications/notificationContext.jsx"; import { bodyshopHasDmsKey } from "../../utils/dmsUtils.js"; const mapStateToProps = createStructuredSelector({ bodyshop: selectBodyshop, technician: selectTechnician }); export function TechClockOffButton({ bodyshop, technician, jobId, timeTicketId, completedCallback, isShiftTicket, otherBtnProps }) { const [loading, setLoading] = useState(false); const [updateTimeticket] = useMutation(UPDATE_TIME_TICKET); const [updateJobStatus] = useMutation(UPDATE_JOB_STATUS); const notification = useNotification(); const [form] = Form.useForm(); const { treatments: { Enhanced_Payroll } } = useTreatmentsWithConfig({ attributes: {}, names: ["Enhanced_Payroll"], splitKey: bodyshop.imexshopid }); const id = jobId ?? null; const { loading: queryLoading, data: lineTicketData } = useQuery(GET_LINE_TICKET_BY_PK, { variables: { id }, skip: !id, fetchPolicy: "network-only", nextFetchPolicy: "network-only" }); const { t } = useTranslation(); const emps = bodyshop.employees.filter((e) => e.id === technician?.id)[0]; const hasDmsKey = bodyshopHasDmsKey(bodyshop); const handleFinish = async (values) => { logImEXEvent("tech_clock_out_job"); const status = values.status; delete values.status; setLoading(true); const result = await updateTimeticket({ variables: { timeticketId: timeTicketId, timeticket: { clockoff: (await axios.post("/utils/time")).data, ...values, rate: emps && emps.rates.filter((r) => r.cost_center === values.cost_center)[0]?.rate, flat_rate: emps && emps.flat_rate, ciecacode: bodyshop.cdk_dealerid || bodyshop.pbs_serialnumber || bodyshop.rr_dealerid || Enhanced_Payroll.treatment === "on" ? values.cost_center : Object.keys(bodyshop.md_responsibility_centers.defaults.costs).find((key) => { return bodyshop.md_responsibility_centers.defaults.costs[key] === values.cost_center; }) } } }); if (result.errors) { notification.error({ title: t("timetickets.errors.clockingout", { message: JSON.stringify(result.errors) }) }); } else { notification.success({ title: t("timetickets.successes.clockedout") }); } if (!isShiftTicket) { const job_update_result = await updateJobStatus({ variables: { jobId: id, status: status } }); if (job_update_result.errors) { notification.error({ title: t("jobs.errors.updating", { message: JSON.stringify(result.errors) }) }); } else { notification.success({ title: t("jobs.successes.updated") }); } } setLoading(false); if (completedCallback) completedCallback(); }; const overlay = (
{!isShiftTicket ? (
({ validator(rule, value) { if (!bodyshop.tt_enforce_hours_for_tech_console) { return Promise.resolve(); } if (!value || getFieldValue("cost_center") === null || !lineTicketData) return Promise.resolve(); //Check the cost center, const totals = CalculateAllocationsTotals( bodyshop, lineTicketData.joblines, lineTicketData.timetickets, lineTicketData.jobs_by_pk.lbr_adjustments ); const fieldTypeToCheck = hasDmsKey ? "mod_lbr_ty" : "cost_center"; const costCenterDiff = Math.round( totals.find((total) => total[fieldTypeToCheck] === getFieldValue("cost_center")) ?.difference * 10 ) / 10; if (value > costCenterDiff) return Promise.reject(t("timetickets.validation.hoursenteredmorethanavailable")); return Promise.resolve(); } }) ]} >
) : null} ({ value: item, label: item }))} /> )} {!isShiftTicket && ( )}
); return ( {overlay}} trigger="click" getPopupContainer={() => document.querySelector("#time-ticket-modal")} > ); } export default connect(mapStateToProps, null)(TechClockOffButton);