diff --git a/.gitignore b/.gitignore index 9b2ed79f9..9a2f7ecc1 100644 --- a/.gitignore +++ b/.gitignore @@ -128,3 +128,5 @@ vitest-coverage/ *.vitest.log test-output.txt server/job/test/fixtures + +.github diff --git a/client/src/components/job-at-change/schedule-event.component.jsx b/client/src/components/job-at-change/schedule-event.component.jsx index 4facb2a19..123bc9879 100644 --- a/client/src/components/job-at-change/schedule-event.component.jsx +++ b/client/src/components/job-at-change/schedule-event.component.jsx @@ -1,5 +1,5 @@ import { AlertFilled } from "@ant-design/icons"; -import { useMutation } from "@apollo/client"; +import { useLazyQuery, useMutation } from "@apollo/client"; import { Button, Divider, Dropdown, Form, Input, Popover, Select, Space } from "antd"; import parsePhoneNumber from "libphonenumber-js"; import queryString from "query-string"; @@ -8,24 +8,30 @@ import { useTranslation } from "react-i18next"; import { connect } from "react-redux"; import { Link, useLocation, useNavigate } from "react-router-dom"; import { createStructuredSelector } from "reselect"; +import { useNotification } from "../../contexts/Notifications/notificationContext.jsx"; import { useSocket } from "../../contexts/SocketIO/useSocket.js"; import { UPDATE_APPOINTMENT } from "../../graphql/appointments.queries"; +import { GET_JOB_BY_PK_QUICK_INTAKE, JOB_PRODUCTION_TOGGLE } from "../../graphql/jobs.queries"; +import { insertAuditTrail } from "../../redux/application/application.actions"; import { openChatByPhone, setMessage } from "../../redux/messaging/messaging.actions"; import { setModalContext } from "../../redux/modals/modals.actions"; import { selectBodyshop } from "../../redux/user/user.selectors"; +import AuditTrailMapping from "../../utils/AuditTrailMappings"; import CurrencyFormatter from "../../utils/CurrencyFormatter"; +import { DateTimeFormatterFunction } from "../../utils/DateFormatter"; import dayjs from "../../utils/day"; import { GenerateDocument } from "../../utils/RenderTemplate"; import { TemplateList } from "../../utils/TemplateConstants"; import ChatOpenButton from "../chat-open-button/chat-open-button.component"; import DataLabel from "../data-label/data-label.component"; +import FormDateTimePickerComponent from "../form-date-time-picker/form-date-time-picker.component"; import OwnerNameDisplay from "../owner-name-display/owner-name-display.component"; import ProductionListColumnComment from "../production-list-columns/production-list-columns.comment.component"; import ScheduleManualEvent from "../schedule-manual-event/schedule-manual-event.component"; +import { HasFeatureAccess } from "./../feature-wrapper/feature-wrapper.component"; import ScheduleAtChange from "./job-at-change.component"; import ScheduleEventColor from "./schedule-event.color.component"; import ScheduleEventNote from "./schedule-event.note.component"; -import { useNotification } from "../../contexts/Notifications/notificationContext.jsx"; const mapStateToProps = createStructuredSelector({ bodyshop: selectBodyshop @@ -33,7 +39,8 @@ const mapStateToProps = createStructuredSelector({ const mapDispatchToProps = (dispatch) => ({ setScheduleContext: (context) => dispatch(setModalContext({ context: context, modal: "schedule" })), openChatByPhone: (phone) => dispatch(openChatByPhone(phone)), - setMessage: (text) => dispatch(setMessage(text)) + setMessage: (text) => dispatch(setMessage(text)), + insertAuditTrail: ({ jobid, operation }) => dispatch(insertAuditTrail({ jobid, operation })) }); export function ScheduleEventComponent({ @@ -43,16 +50,36 @@ export function ScheduleEventComponent({ event, refetch, handleCancel, - setScheduleContext + setScheduleContext, + insertAuditTrail }) { const { t } = useTranslation(); const [open, setOpen] = useState(false); const history = useNavigate(); const searchParams = queryString.parse(useLocation().search); const [updateAppointment] = useMutation(UPDATE_APPOINTMENT); + const [mutationUpdateJob] = useMutation(JOB_PRODUCTION_TOGGLE); const [title, setTitle] = useState(event.title); const { socket } = useSocket(); const notification = useNotification(); + const [form] = Form.useForm(); + const [popOverVisible, setPopOverVisible] = useState(false); + + const [getJobDetails] = useLazyQuery(GET_JOB_BY_PK_QUICK_INTAKE, { + variables: { id: event.job.id }, + onCompleted: (data) => { + if (data?.jobs_by_pk) { + form.setFieldsValue({ + actual_in: data.jobs_by_pk.actual_in ? data.jobs_by_pk.actual_in : dayjs(), + scheduled_completion: data.jobs_by_pk.scheduled_completion, + actual_completion: data.jobs_by_pk.actual_completion, + scheduled_delivery: data.jobs_by_pk.scheduled_delivery, + actual_delivery: data.jobs_by_pk.actual_delivery + }); + } + }, + fetchPolicy: "network-only" + }); const blockContent = ( @@ -89,6 +116,74 @@ export function ScheduleEventComponent({ ); + const handleConvert = async (values) => { + const res = await mutationUpdateJob({ + variables: { + jobId: event.job.id, + job: { + ...values, + status: bodyshop.md_ro_statuses.default_arrived, + inproduction: true + } + } + }); + + if (!res.errors) { + notification["success"]({ + message: t("jobs.successes.converted") + }); + insertAuditTrail({ + jobid: event.job.id, + operation: AuditTrailMapping.jobintake( + res.data.update_jobs.returning[0].status, + DateTimeFormatterFunction(values.scheduled_completion) + ) + }); + setPopOverVisible(false); + refetch(); + } + }; + + const popMenu = ( +
e.stopPropagation()}> +
+ + + + + + + + + + + + + +
+
+ ); + const popoverContent = (
{!event.isintake ? ( @@ -294,7 +389,7 @@ export function ScheduleEventComponent({ ) : ( )} - {event.isintake ? ( + {event.isintake && HasFeatureAccess({ featureName: "checklist", bodyshop }) ? ( - ) : null} + ) : ( + { + getJobDetails(); + e.stopPropagation(); + }} + getPopupContainer={(trigger) => trigger.parentNode} + trigger="click" + > + + + )}
); diff --git a/client/src/utils/AuditTrailMappings.js b/client/src/utils/AuditTrailMappings.js index f2680d9d8..ea1494fec 100644 --- a/client/src/utils/AuditTrailMappings.js +++ b/client/src/utils/AuditTrailMappings.js @@ -15,8 +15,8 @@ const AuditTrailMapping = { jobchecklist: (type, inproduction, status) => i18n.t("audit_trail.messages.jobchecklist", { type, inproduction, status }), jobconverted: (ro_number) => i18n.t("audit_trail.messages.jobconverted", { ro_number }), - jobintake: (status, email, scheduled_completion) => - i18n.t("audit_trail.messages.jobintake", { status, email, scheduled_completion }), + jobintake: (status, scheduled_completion) => + i18n.t("audit_trail.messages.jobintake", { status, scheduled_completion }), jobdelivery: (status, email, actual_completion) => i18n.t("audit_trail.messages.jobdelivery", { status, email, actual_completion }), jobexported: () => i18n.t("audit_trail.messages.jobexported"),