diff --git a/client/src/components/jobs-detail-header-actions/jobs-detail-header-actions.addevent.jsx b/client/src/components/jobs-detail-header-actions/jobs-detail-header-actions.addevent.jsx deleted file mode 100644 index 28c79e80e..000000000 --- a/client/src/components/jobs-detail-header-actions/jobs-detail-header-actions.addevent.jsx +++ /dev/null @@ -1,164 +0,0 @@ -import { useMutation } from "@apollo/client"; -import { - Button, - Card, - Form, - Input, - Menu, - notification, - Popover, - Select, - Space, -} from "antd"; -import dayjs from "../../utils/day"; -import React, { useState } from "react"; -import { useTranslation } from "react-i18next"; -import { connect } from "react-redux"; -import { createStructuredSelector } from "reselect"; -import { logImEXEvent } from "../../firebase/firebase.utils"; -import { INSERT_MANUAL_APPT } from "../../graphql/appointments.queries"; -import { selectBodyshop } from "../../redux/user/user.selectors"; -import FormDateTimePickerComponent from "../form-date-time-picker/form-date-time-picker.component"; - -const mapStateToProps = createStructuredSelector({ - bodyshop: selectBodyshop, -}); -const mapDispatchToProps = (dispatch) => ({ - //setUserLanguage: language => dispatch(setUserLanguage(language)) -}); -export default connect( - mapStateToProps, - mapDispatchToProps -)(JobsDetailHeaderAddEvent); - -export function JobsDetailHeaderAddEvent({ bodyshop, jobid, ...props }) { - const { t } = useTranslation(); - const [insertAppointment] = useMutation(INSERT_MANUAL_APPT); - - const [loading, setLoading] = useState(false); - const [form] = Form.useForm(); - const [visibility, setVisibility] = useState(false); - - const handleFinish = async (values) => { - logImEXEvent("schedule_manual_event"); - - setLoading(true); - try { - insertAppointment({ - variables: { - apt: { ...values, isintake: false, jobid, bodyshopid: bodyshop.id }, - }, - refetchQueries: ["QUERY_ALL_ACTIVE_APPOINTMENTS"], - }); - notification.open({ - type: "success", - message: t("appointments.successes.created"), - }); - } catch (error) { - console.log(error); - } finally { - setLoading(false); - setVisibility(false); - } - }; - - const overlay = ( - -
-
- - - - - - - - { - const start = form.getFieldValue("start"); - form.setFieldsValue({ end: start.add(30, "minutes") }); - }} - /> - - ({ - async validator(rule, value) { - if (value) { - const { start } = form.getFieldsValue(); - if (dayjs(start).isAfter(dayjs(value))) { - return Promise.reject( - t("employees.labels.endmustbeafterstart") - ); - } else { - return Promise.resolve(); - } - } else { - return Promise.resolve(); - } - }, - }), - ]} - > - - - - - - - - - - -
-
-
- ); - - const handleClick = (e) => { - setVisibility(true); - }; - - // TODO - Client Update - Why is this a menu item? - return ( - - - {t("appointments.labels.manualevent")} - - - ); -} diff --git a/client/src/components/jobs-detail-header-actions/jobs-detail-header-actions.component.jsx b/client/src/components/jobs-detail-header-actions/jobs-detail-header-actions.component.jsx index 286703536..f602d3d98 100644 --- a/client/src/components/jobs-detail-header-actions/jobs-detail-header-actions.component.jsx +++ b/client/src/components/jobs-detail-header-actions/jobs-detail-header-actions.component.jsx @@ -1,575 +1,977 @@ -import { DownCircleFilled } from "@ant-design/icons"; -import { useApolloClient, useMutation } from "@apollo/client"; -import { - Button, - Dropdown, - Form, - Menu, - Popconfirm, - Popover, - Select, - notification, -} from "antd"; -import React, { useMemo } from "react"; -import { useTranslation } from "react-i18next"; -import { connect } from "react-redux"; -import { Link, useNavigate } from "react-router-dom"; -import { createStructuredSelector } from "reselect"; -import { logImEXEvent } from "../../firebase/firebase.utils"; -import { CANCEL_APPOINTMENTS_BY_JOB_ID } from "../../graphql/appointments.queries"; -import { DELETE_JOB, UPDATE_JOB, VOID_JOB } from "../../graphql/jobs.queries"; -import { insertAuditTrail } from "../../redux/application/application.actions"; -import { selectJobReadOnly } from "../../redux/application/application.selectors"; -import { setModalContext } from "../../redux/modals/modals.actions"; -import { - selectBodyshop, - selectCurrentUser, -} from "../../redux/user/user.selectors"; +import {DownCircleFilled} from "@ant-design/icons"; +import {useApolloClient, useMutation} from "@apollo/client"; +import {Button, Card, Dropdown, Form, Input, notification, Popconfirm, Popover, Select, Space,} from "antd"; +import React, {useMemo, useState} from "react"; +import {useTranslation} from "react-i18next"; +import {connect} from "react-redux"; +import {Link, useNavigate} from "react-router-dom"; +import {createStructuredSelector} from "reselect"; +import {auth, logImEXEvent} from "../../firebase/firebase.utils"; +import {CANCEL_APPOINTMENTS_BY_JOB_ID, INSERT_MANUAL_APPT} from "../../graphql/appointments.queries"; +import {DELETE_JOB, UPDATE_JOB, VOID_JOB} from "../../graphql/jobs.queries"; +import {insertAuditTrail} from "../../redux/application/application.actions"; +import {selectJobReadOnly} from "../../redux/application/application.selectors"; +import {setModalContext} from "../../redux/modals/modals.actions"; +import {selectBodyshop, selectCurrentUser,} from "../../redux/user/user.selectors"; import AuditTrailMapping from "../../utils/AuditTrailMappings"; import RbacWrapper from "../rbac-wrapper/rbac-wrapper.component"; -import JobsDetailHeaderActionsAddevent from "./jobs-detail-header-actions.addevent"; import AddToProduction from "./jobs-detail-header-actions.addtoproduction.util"; -import JobsDetaiLheaderCsi from "./jobs-detail-header-actions.csi.component"; import DuplicateJob from "./jobs-detail-header-actions.duplicate.util"; -import JobsDetailHeaderActionsExportcustdataComponent from "./jobs-detail-header-actions.exportcustdata.component"; +import axios from "axios"; +import {setEmailOptions} from "../../redux/email/email.actions"; +import {openChatByPhone, setMessage} from "../../redux/messaging/messaging.actions"; +import {GET_CURRENT_QUESTIONSET_ID, INSERT_CSI} from "../../graphql/csi.queries"; +import {TemplateList} from "../../utils/TemplateConstants"; +import parsePhoneNumber from "libphonenumber-js"; +import {HasFeatureAccess} from "../feature-wrapper/feature-wrapper.component"; +import {DateTimeFormatter} from "../../utils/DateFormatter"; +import FormDateTimePickerComponent from "../form-date-time-picker/form-date-time-picker.component"; +import dayjs from "../../utils/day"; const mapStateToProps = createStructuredSelector({ - bodyshop: selectBodyshop, - jobRO: selectJobReadOnly, - currentUser: selectCurrentUser, + bodyshop: selectBodyshop, + jobRO: selectJobReadOnly, + currentUser: selectCurrentUser, }); const mapDispatchToProps = (dispatch) => ({ - setScheduleContext: (context) => - dispatch(setModalContext({ context: context, modal: "schedule" })), - setBillEnterContext: (context) => - dispatch(setModalContext({ context: context, modal: "billEnter" })), - setPaymentContext: (context) => - dispatch(setModalContext({ context: context, modal: "payment" })), - setJobCostingContext: (context) => - dispatch(setModalContext({ context: context, modal: "jobCosting" })), - setTimeTicketContext: (context) => - dispatch(setModalContext({ context: context, modal: "timeTicket" })), - setCardPaymentContext: (context) => - dispatch(setModalContext({ context: context, modal: "cardPayment" })), - insertAuditTrail: ({ jobid, operation }) => - dispatch(insertAuditTrail({ jobid, operation })), + setScheduleContext: (context) => + dispatch(setModalContext({context: context, modal: "schedule"})), + setBillEnterContext: (context) => + dispatch(setModalContext({context: context, modal: "billEnter"})), + setPaymentContext: (context) => + dispatch(setModalContext({context: context, modal: "payment"})), + setJobCostingContext: (context) => + dispatch(setModalContext({context: context, modal: "jobCosting"})), + setTimeTicketContext: (context) => + dispatch(setModalContext({context: context, modal: "timeTicket"})), + setCardPaymentContext: (context) => + dispatch(setModalContext({context: context, modal: "cardPayment"})), + insertAuditTrail: ({jobid, operation}) => + dispatch(insertAuditTrail({jobid, operation})), + setEmailOptions: (e) => dispatch(setEmailOptions(e)), + openChatByPhone: (phone) => dispatch(openChatByPhone(phone)), + setMessage: (text) => dispatch(setMessage(text)), }); -export function JobsDetailHeaderActions({ - job, - bodyshop, - currentUser, - refetch, - setScheduleContext, - setBillEnterContext, - setPaymentContext, - setJobCostingContext, - jobRO, - setTimeTicketContext, - setCardPaymentContext, - insertAuditTrail, -}) { - const { t } = useTranslation(); - const client = useApolloClient(); - const history = useNavigate(); - const [deleteJob] = useMutation(DELETE_JOB); - const [updateJob] = useMutation(UPDATE_JOB); - const [voidJob] = useMutation(VOID_JOB); - const [cancelAllAppointments] = useMutation(CANCEL_APPOINTMENTS_BY_JOB_ID); - const jobInProduction = useMemo(() => { - return bodyshop.md_ro_statuses.production_statuses.includes(job.status); - }, [job, bodyshop.md_ro_statuses.production_statuses]); +export function JobsDetailHeaderActions({job, bodyshop, currentUser, refetch, setScheduleContext, setBillEnterContext, setPaymentContext, setJobCostingContext, jobRO, setTimeTicketContext, setCardPaymentContext, insertAuditTrail, setEmailOptions, openChatByPhone, setMessage }) { + const {t} = useTranslation(); + const client = useApolloClient(); + const history = useNavigate(); + const [form] = Form.useForm(); + const [loading, setLoading] = useState(false); + const [insertAppointment] = useMutation(INSERT_MANUAL_APPT); + const [deleteJob] = useMutation(DELETE_JOB); + const [insertCsi] = useMutation(INSERT_CSI); + const [updateJob] = useMutation(UPDATE_JOB); + const [voidJob] = useMutation(VOID_JOB); + const [cancelAllAppointments] = useMutation(CANCEL_APPOINTMENTS_BY_JOB_ID); + const jobInProduction = useMemo(() => { + return bodyshop.md_ro_statuses.production_statuses.includes(job.status); + }, [job, bodyshop.md_ro_statuses.production_statuses]); + const [visibility, setVisibility] = useState(false); - const jobInPreProduction = useMemo(() => { - return bodyshop.md_ro_statuses.pre_production_statuses.includes(job.status); - }, [job.status, bodyshop.md_ro_statuses.pre_production_statuses]); + const jobInPreProduction = useMemo(() => { + return bodyshop.md_ro_statuses.pre_production_statuses.includes(job.status); + }, [job.status, bodyshop.md_ro_statuses.pre_production_statuses]); - const jobInPostProduction = useMemo(() => { - return bodyshop.md_ro_statuses.post_production_statuses.includes( - job.status - ); - }, [job.status, bodyshop.md_ro_statuses.post_production_statuses]); + const jobInPostProduction = useMemo(() => { + return bodyshop.md_ro_statuses.post_production_statuses.includes( + job.status + ); + }, [job.status, bodyshop.md_ro_statuses.post_production_statuses]); - const handleAlertToggle = (e) => { - logImEXEvent("production_toggle_alert"); - //e.stopPropagation(); - updateJob({ - variables: { - jobId: job.id, - job: { - production_vars: { - ...job.production_vars, - alert: - !!job.production_vars && !!job.production_vars.alert - ? !job.production_vars.alert - : true, - }, - }, - }, - }); - insertAuditTrail({ - jobid: job.id, - operation: AuditTrailMapping.alertToggle( - !!job.production_vars && !!job.production_vars.alert - ? !job.production_vars.alert - : true - ), - }); - }; + const handleFinish = async (values) => { + logImEXEvent("schedule_manual_event"); - const handleSuspend = (e) => { - logImEXEvent("production_toggle_alert"); - //e.stopPropagation(); - updateJob({ - variables: { - jobId: job.id, - job: { - suspended: !job.suspended, - }, - }, - }); - }; - - const statusmenu = ( - - { - logImEXEvent("job_header_schedule"); - - setScheduleContext({ - actions: { refetch: refetch }, - context: { - jobId: job.id, - job: job, - alt_transport: job.alt_transport, - }, - }); - }} - > - {t("jobs.actions.schedule")} - - - {job.status !== bodyshop.md_ro_statuses.default_scheduled ? ( - t("menus.jobsactions.cancelallappointments") - ) : ( - { - const jobUpdate = await cancelAllAppointments({ - variables: { - jobid: job.id, - job: { - date_scheduled: null, - scheduled_in: null, - scheduled_completion: null, - lost_sale_reason, - date_lost_sale: new Date(), - status: bodyshop.md_ro_statuses.default_imported, - }, - }, - }); - if (!jobUpdate.errors) { - notification["success"]({ - message: t("appointments.successes.canceled"), - }); - insertAuditTrail({ - jobid: job.id, - operation: - AuditTrailMapping.appointmentcancel(lost_sale_reason), - }); - return; - } - }} - > - - + + + + + + { + const start = form.getFieldValue("start"); + form.setFieldsValue({ end: start.add(30, "minutes") }); + }} + /> + + ({ + async validator(rule, value) { + if (value) { + const { start } = form.getFieldsValue(); + if (dayjs(start).isAfter(dayjs(value))) { + return Promise.reject( + t("employees.labels.endmustbeafterstart") + ); + } else { + return Promise.resolve(); + } + } else { + return Promise.resolve(); + } + }, + }), + ]} + > + + + + + + + + + + + + + + ); + + const menuItems = [ + { + disabled: !jobInPreProduction || !job.converted || jobRO, + label: t("jobs.actions.schedule"), + onClick: () => { + logImEXEvent("job_header_schedule"); + setScheduleContext({ + actions: {refetch: refetch}, + context: { + jobId: job.id, + job: job, + alt_transport: job.alt_transport, + }, + }); + }, + }, + { + disabled: job.status !== bodyshop.md_ro_statuses.default_scheduled, + label: job.status !== bodyshop.md_ro_statuses.default_scheduled ? ( + t("menus.jobsactions.cancelallappointments") + ) : ( + { + const jobUpdate = await cancelAllAppointments({ + variables: { + jobid: job.id, + job: { + date_scheduled: null, + scheduled_in: null, + scheduled_completion: null, + lost_sale_reason, + date_lost_sale: new Date(), + status: bodyshop.md_ro_statuses.default_imported, + }, + }, + }); + if (!jobUpdate.errors) { + notification["success"]({ + message: t("appointments.successes.canceled"), + }); + insertAuditTrail({ + jobid: job.id, + operation: + AuditTrailMapping.appointmentcancel(lost_sale_reason), + }); + + } + }} + > + +