@@ -104,7 +104,7 @@ export function App({
|
|||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Any route that is not assigned and matched will default to the Landing Page component
|
||||||
return (
|
return (
|
||||||
<Suspense fallback={<LoadingSpinner message="ImEX Online"/>}>
|
<Suspense fallback={<LoadingSpinner message="ImEX Online"/>}>
|
||||||
<Routes>
|
<Routes>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import Icon, {UploadOutlined} from "@ant-design/icons";
|
import Icon, {UploadOutlined} from "@ant-design/icons";
|
||||||
import {useApolloClient} from "@apollo/client";
|
import {useApolloClient} from "@apollo/client";
|
||||||
import {useTreatments} from "@splitsoftware/splitio-react";
|
import {useSplitTreatments} from "@splitsoftware/splitio-react";
|
||||||
import {Alert, Divider, Form, Input, Select, Space, Statistic, Switch, Upload,} from "antd";
|
import {Alert, Divider, Form, Input, Select, Space, Statistic, Switch, Upload,} from "antd";
|
||||||
import dayjs from "../../utils/day";
|
import dayjs from "../../utils/day";
|
||||||
import React, {useEffect, useState} from "react";
|
import React, {useEffect, useState} from "react";
|
||||||
@@ -27,34 +27,18 @@ const mapStateToProps = createStructuredSelector({
|
|||||||
});
|
});
|
||||||
const mapDispatchToProps = (dispatch) => ({});
|
const mapDispatchToProps = (dispatch) => ({});
|
||||||
|
|
||||||
export function BillFormComponent({
|
export function BillFormComponent({bodyshop, disabled, form, vendorAutoCompleteOptions, lineData, responsibilityCenters, loadLines, billEdit, disableInvNumber, job, loadOutstandingReturns, loadInventory, preferredMake}) {
|
||||||
bodyshop,
|
|
||||||
disabled,
|
|
||||||
form,
|
|
||||||
vendorAutoCompleteOptions,
|
|
||||||
lineData,
|
|
||||||
responsibilityCenters,
|
|
||||||
loadLines,
|
|
||||||
billEdit,
|
|
||||||
disableInvNumber,
|
|
||||||
job,
|
|
||||||
loadOutstandingReturns,
|
|
||||||
loadInventory,
|
|
||||||
preferredMake,
|
|
||||||
}) {
|
|
||||||
const {t} = useTranslation();
|
const {t} = useTranslation();
|
||||||
const client = useApolloClient();
|
const client = useApolloClient();
|
||||||
const [discount, setDiscount] = useState(0);
|
const [discount, setDiscount] = useState(0);
|
||||||
const {Extended_Bill_Posting} = useTreatments(
|
|
||||||
["Extended_Bill_Posting"],
|
const { treatments: {Extended_Bill_Posting, ClosingPeriod} } = useSplitTreatments({
|
||||||
{},
|
attributes: {},
|
||||||
bodyshop.imexshopid
|
names: ["Extended_Bill_Posting", "ClosingPeriod"],
|
||||||
);
|
splitKey: bodyshop.imexshopid,
|
||||||
const {ClosingPeriod} = useTreatments(
|
});
|
||||||
["ClosingPeriod"],
|
|
||||||
{},
|
|
||||||
bodyshop.imexshopid
|
|
||||||
);
|
|
||||||
|
|
||||||
const handleVendorSelect = (props, opt) => {
|
const handleVendorSelect = (props, opt) => {
|
||||||
setDiscount(opt.discount);
|
setDiscount(opt.discount);
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { useLazyQuery, useQuery } from "@apollo/client";
|
import { useLazyQuery, useQuery } from "@apollo/client";
|
||||||
import { useTreatments } from "@splitsoftware/splitio-react";
|
import {useSplitTreatments} from "@splitsoftware/splitio-react";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import { createStructuredSelector } from "reselect";
|
import { createStructuredSelector } from "reselect";
|
||||||
@@ -23,11 +23,11 @@ export function BillFormContainer({
|
|||||||
disabled,
|
disabled,
|
||||||
disableInvNumber,
|
disableInvNumber,
|
||||||
}) {
|
}) {
|
||||||
const { Simple_Inventory } = useTreatments(
|
const { treatments: {Simple_Inventory} } = useSplitTreatments({
|
||||||
["Simple_Inventory"],
|
attributes: {},
|
||||||
{},
|
names: ["Simple_Inventory"],
|
||||||
bodyshop && bodyshop.imexshopid
|
splitKey: bodyshop && bodyshop.imexshopid,
|
||||||
);
|
});
|
||||||
|
|
||||||
const { data: VendorAutoCompleteData } = useQuery(
|
const { data: VendorAutoCompleteData } = useQuery(
|
||||||
SEARCH_VENDOR_AUTOCOMPLETE,
|
SEARCH_VENDOR_AUTOCOMPLETE,
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { DeleteFilled, DollarCircleFilled } from "@ant-design/icons";
|
import { DeleteFilled, DollarCircleFilled } from "@ant-design/icons";
|
||||||
import { useTreatments } from "@splitsoftware/splitio-react";
|
import {useSplitTreatments} from "@splitsoftware/splitio-react";
|
||||||
import {
|
import {
|
||||||
Button, Form,
|
Button, Form,
|
||||||
Input,
|
Input,
|
||||||
@@ -40,11 +40,14 @@ export function BillEnterModalLinesComponent({
|
|||||||
}) {
|
}) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { setFieldsValue, getFieldsValue, getFieldValue } = form;
|
const { setFieldsValue, getFieldsValue, getFieldValue } = form;
|
||||||
const { Simple_Inventory } = useTreatments(
|
|
||||||
["Simple_Inventory"],
|
const { treatments: {Simple_Inventory} } = useSplitTreatments({
|
||||||
{},
|
attributes: {},
|
||||||
bodyshop && bodyshop.imexshopid
|
names: ["Simple_Inventory"],
|
||||||
);
|
splitKey: bodyshop && bodyshop.imexshopid,
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
const columns = (remove) => {
|
const columns = (remove) => {
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import { selectBodyshop } from "../../redux/user/user.selectors";
|
|||||||
import GlobalSearch from "../global-search/global-search.component";
|
import GlobalSearch from "../global-search/global-search.component";
|
||||||
import GlobalSearchOs from "../global-search/global-search-os.component";
|
import GlobalSearchOs from "../global-search/global-search-os.component";
|
||||||
import "./breadcrumbs.styles.scss";
|
import "./breadcrumbs.styles.scss";
|
||||||
import { useTreatments } from "@splitsoftware/splitio-react";
|
import { useSplitTreatments } from "@splitsoftware/splitio-react";
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
breadcrumbs: selectBreadcrumbs,
|
breadcrumbs: selectBreadcrumbs,
|
||||||
@@ -17,13 +17,14 @@ const mapStateToProps = createStructuredSelector({
|
|||||||
});
|
});
|
||||||
|
|
||||||
export function BreadCrumbs({ breadcrumbs, bodyshop }) {
|
export function BreadCrumbs({ breadcrumbs, bodyshop }) {
|
||||||
const { OpenSearch } = useTreatments(
|
|
||||||
["OpenSearch"],
|
|
||||||
{},
|
|
||||||
bodyshop && bodyshop.imexshopid
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
const { treatments: {OpenSearch} } = useSplitTreatments({
|
||||||
|
attributes: {},
|
||||||
|
names: ["OpenSearch"],
|
||||||
|
splitKey: bodyshop && bodyshop.imexshopid,
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
<Row className="breadcrumb-container">
|
<Row className="breadcrumb-container">
|
||||||
<Col xs={24} sm={24} md={16}>
|
<Col xs={24} sm={24} md={16}>
|
||||||
<Breadcrumb separator=">">
|
<Breadcrumb separator=">">
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ export function ContractConvertToRo({
|
|||||||
|
|
||||||
const contractLength = dayjs(contract.actualreturn).diff(
|
const contractLength = dayjs(contract.actualreturn).diff(
|
||||||
dayjs(contract.start),
|
dayjs(contract.start),
|
||||||
"days"
|
"day"
|
||||||
);
|
);
|
||||||
const billingLines = [];
|
const billingLines = [];
|
||||||
if (contractLength > 0)
|
if (contractLength > 0)
|
||||||
|
|||||||
@@ -153,7 +153,7 @@ export function ContractsList({
|
|||||||
(record.actualreturn &&
|
(record.actualreturn &&
|
||||||
record.start &&
|
record.start &&
|
||||||
`${dayjs(record.actualreturn)
|
`${dayjs(record.actualreturn)
|
||||||
.diff(dayjs(record.start), "days", true)
|
.diff(dayjs(record.start), "day", true)
|
||||||
.toFixed(1)} days`) ||
|
.toFixed(1)} days`) ||
|
||||||
"",
|
"",
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ import Icon, {
|
|||||||
UnorderedListOutlined,
|
UnorderedListOutlined,
|
||||||
UserOutlined,
|
UserOutlined,
|
||||||
} from "@ant-design/icons";
|
} from "@ant-design/icons";
|
||||||
import { useTreatments } from "@splitsoftware/splitio-react";
|
import {useSplitTreatments} from "@splitsoftware/splitio-react";
|
||||||
import { Layout, Menu } from "antd";
|
import { Layout, Menu } from "antd";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
@@ -87,21 +87,12 @@ function Header({
|
|||||||
recentItems,
|
recentItems,
|
||||||
setCardPaymentContext,
|
setCardPaymentContext,
|
||||||
}) {
|
}) {
|
||||||
const { Simple_Inventory } = useTreatments(
|
// TODO: Client Update - New Split Treatments usage example
|
||||||
["Simple_Inventory"],
|
const { treatments: {ImEXPay, DmsAp, Simple_Inventory} } = useSplitTreatments({
|
||||||
{},
|
attributes: {},
|
||||||
bodyshop && bodyshop.imexshopid
|
names: ["ImEXPay", "DmsAp", "Simple_Inventory"],
|
||||||
);
|
splitKey: bodyshop && bodyshop.imexshopid,
|
||||||
const { DmsAp } = useTreatments(
|
});
|
||||||
["DmsAp"],
|
|
||||||
{},
|
|
||||||
bodyshop && bodyshop.imexshopid
|
|
||||||
);
|
|
||||||
const { ImEXPay } = useTreatments(
|
|
||||||
["ImEXPay"],
|
|
||||||
{},
|
|
||||||
bodyshop && bodyshop.imexshopid
|
|
||||||
);
|
|
||||||
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
|||||||
@@ -89,7 +89,7 @@ export function JobChecklistForm({
|
|||||||
bodyshop.intakechecklist.next_contact_hours > 0 && {
|
bodyshop.intakechecklist.next_contact_hours > 0 && {
|
||||||
date_next_contact: dayjs().add(
|
date_next_contact: dayjs().add(
|
||||||
bodyshop.intakechecklist.next_contact_hours,
|
bodyshop.intakechecklist.next_contact_hours,
|
||||||
"hours"
|
"hour"
|
||||||
),
|
),
|
||||||
}),
|
}),
|
||||||
...(type === "deliver" && {
|
...(type === "deliver" && {
|
||||||
@@ -226,7 +226,7 @@ export function JobChecklistForm({
|
|||||||
(job.labhrs.aggregate.sum.mod_lb_hrs ||
|
(job.labhrs.aggregate.sum.mod_lb_hrs ||
|
||||||
0 + job.larhrs.aggregate.sum.mod_lb_hrs ||
|
0 + job.larhrs.aggregate.sum.mod_lb_hrs ||
|
||||||
0) / bodyshop.target_touchtime,
|
0) / bodyshop.target_touchtime,
|
||||||
"days"
|
"day"
|
||||||
)),
|
)),
|
||||||
scheduled_delivery:
|
scheduled_delivery:
|
||||||
job.scheduled_delivery && dayjs(job.scheduled_delivery),
|
job.scheduled_delivery && dayjs(job.scheduled_delivery),
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { useApolloClient } from "@apollo/client";
|
import { useApolloClient } from "@apollo/client";
|
||||||
import { useTreatments } from "@splitsoftware/splitio-react";
|
import { useSplitTreatments } from "@splitsoftware/splitio-react";
|
||||||
import { Button, notification, Popconfirm } from "antd";
|
import { Button, notification, Popconfirm } from "antd";
|
||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
@@ -27,11 +27,13 @@ export function JobCreateIOU({ bodyshop, currentUser, job, selectedJobLines }) {
|
|||||||
const client = useApolloClient();
|
const client = useApolloClient();
|
||||||
const history = useNavigate();
|
const history = useNavigate();
|
||||||
|
|
||||||
const { IOU_Tracking } = useTreatments(
|
|
||||||
["IOU_Tracking"],
|
const { treatments: {IOU_Tracking} } = useSplitTreatments({
|
||||||
{},
|
attributes: {},
|
||||||
bodyshop.imexshopid
|
names: ["IOU_Tracking"],
|
||||||
);
|
splitKey: bodyshop.imexshopid,
|
||||||
|
});
|
||||||
|
|
||||||
if (IOU_Tracking.treatment !== "on") return null;
|
if (IOU_Tracking.treatment !== "on") return null;
|
||||||
|
|
||||||
const handleCreateIou = async () => {
|
const handleCreateIou = async () => {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { useTranslation } from "react-i18next";
|
|||||||
import InputCurrency from "../form-items-formatted/currency-form-item.component";
|
import InputCurrency from "../form-items-formatted/currency-form-item.component";
|
||||||
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
|
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
|
||||||
import JoblinesPreset from "../job-lines-preset-button/job-lines-preset-button.component";
|
import JoblinesPreset from "../job-lines-preset-button/job-lines-preset-button.component";
|
||||||
import { useTreatments } from "@splitsoftware/splitio-react";
|
import { useSplitTreatments } from "@splitsoftware/splitio-react";
|
||||||
|
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import { createStructuredSelector } from "reselect";
|
import { createStructuredSelector } from "reselect";
|
||||||
@@ -35,16 +35,12 @@ export function JobLinesUpsertModalComponent({
|
|||||||
form.resetFields();
|
form.resetFields();
|
||||||
}, [visible, form]);
|
}, [visible, form]);
|
||||||
|
|
||||||
const { Allow_Negative_Jobline_Price } = useTreatments(
|
|
||||||
["Allow_Negative_Jobline_Price"],
|
const { treatments: {Allow_Negative_Jobline_Price, Autohouse_Detail_line} } = useSplitTreatments({
|
||||||
{},
|
attributes: {},
|
||||||
bodyshop.imexshopid
|
names: ["Allow_Negative_Jobline_Price", "Autohouse_Detail_line"],
|
||||||
);
|
splitKey: bodyshop.imexshopid,
|
||||||
const { Autohouse_Detail_line } = useTreatments(
|
});
|
||||||
["Autohouse_Detail_line"],
|
|
||||||
{},
|
|
||||||
bodyshop.imexshopid
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ import Axios from "axios";
|
|||||||
import Dinero from "dinero.js";
|
import Dinero from "dinero.js";
|
||||||
import CriticalPartsScan from "../../utils/criticalPartsScan";
|
import CriticalPartsScan from "../../utils/criticalPartsScan";
|
||||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||||
import { useTreatments } from "@splitsoftware/splitio-react";
|
import { useSplitTreatments } from "@splitsoftware/splitio-react";
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
jobLineEditModal: selectJobLineEditModal,
|
jobLineEditModal: selectJobLineEditModal,
|
||||||
bodyshop: selectBodyshop,
|
bodyshop: selectBodyshop,
|
||||||
@@ -30,11 +30,13 @@ function JobLinesUpsertModalContainer({
|
|||||||
toggleModalVisible,
|
toggleModalVisible,
|
||||||
bodyshop,
|
bodyshop,
|
||||||
}) {
|
}) {
|
||||||
const { CriticalPartsScanning } = useTreatments(
|
|
||||||
["CriticalPartsScanning"],
|
const { treatments: {CriticalPartsScanning} } = useSplitTreatments({
|
||||||
{},
|
attributes: {},
|
||||||
bodyshop.imexshopid
|
names: ['CriticalPartsScanning'],
|
||||||
);
|
splitKey: bodyshop.imexshopid,
|
||||||
|
});
|
||||||
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [insertJobLine] = useMutation(INSERT_NEW_JOB_LINE);
|
const [insertJobLine] = useMutation(INSERT_NEW_JOB_LINE);
|
||||||
const [updateJobLine] = useMutation(UPDATE_JOB_LINE);
|
const [updateJobLine] = useMutation(UPDATE_JOB_LINE);
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ import DataLabel from "../data-label/data-label.component";
|
|||||||
import PaymentExpandedRowComponent from "../payment-expanded-row/payment-expanded-row.component";
|
import PaymentExpandedRowComponent from "../payment-expanded-row/payment-expanded-row.component";
|
||||||
import PaymentsGenerateLink from "../payments-generate-link/payments-generate-link.component";
|
import PaymentsGenerateLink from "../payments-generate-link/payments-generate-link.component";
|
||||||
import PrintWrapperComponent from "../print-wrapper/print-wrapper.component";
|
import PrintWrapperComponent from "../print-wrapper/print-wrapper.component";
|
||||||
import { useTreatments } from "@splitsoftware/splitio-react";
|
import { useSplitTreatments } from "@splitsoftware/splitio-react";
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
bodyshop: selectBodyshop,
|
bodyshop: selectBodyshop,
|
||||||
@@ -36,21 +36,13 @@ const mapDispatchToProps = (dispatch) => ({
|
|||||||
setMessage: (text) => dispatch(setMessage(text)),
|
setMessage: (text) => dispatch(setMessage(text)),
|
||||||
});
|
});
|
||||||
|
|
||||||
export function JobPayments({
|
export function JobPayments({job, jobRO, bodyshop, setMessage, openChatByPhone, setPaymentContext, setCardPaymentContext, refetch}) {
|
||||||
job,
|
|
||||||
jobRO,
|
const { treatments: {ImEXPay} } = useSplitTreatments({
|
||||||
bodyshop,
|
attributes: {},
|
||||||
setMessage,
|
names: ["ImEXPay"],
|
||||||
openChatByPhone,
|
splitKey:bodyshop && bodyshop.imexshopid,
|
||||||
setPaymentContext,
|
});
|
||||||
setCardPaymentContext,
|
|
||||||
refetch,
|
|
||||||
}) {
|
|
||||||
const { ImEXPay } = useTreatments(
|
|
||||||
["ImEXPay"],
|
|
||||||
{},
|
|
||||||
bodyshop && bodyshop.imexshopid
|
|
||||||
);
|
|
||||||
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [state, setState] = useState({
|
const [state, setState] = useState({
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import {
|
|||||||
useMutation,
|
useMutation,
|
||||||
useQuery,
|
useQuery,
|
||||||
} from "@apollo/client";
|
} from "@apollo/client";
|
||||||
import { useTreatments } from "@splitsoftware/splitio-react";
|
import { useSplitTreatments } from "@splitsoftware/splitio-react";
|
||||||
import { Col, notification, Row } from "antd";
|
import { Col, notification, Row } from "antd";
|
||||||
import Axios from "axios";
|
import Axios from "axios";
|
||||||
import Dinero from "dinero.js";
|
import Dinero from "dinero.js";
|
||||||
@@ -50,16 +50,14 @@ const mapDispatchToProps = (dispatch) => ({
|
|||||||
insertAuditTrail: ({ jobid, operation }) =>
|
insertAuditTrail: ({ jobid, operation }) =>
|
||||||
dispatch(insertAuditTrail({ jobid, operation })),
|
dispatch(insertAuditTrail({ jobid, operation })),
|
||||||
});
|
});
|
||||||
export function JobsAvailableContainer({
|
export function JobsAvailableContainer({bodyshop, currentUser, insertAuditTrail,}) {
|
||||||
bodyshop,
|
|
||||||
currentUser,
|
const { treatments: {CriticalPartsScanning} } = useSplitTreatments({
|
||||||
insertAuditTrail,
|
attributes: {},
|
||||||
}) {
|
names: ["CriticalPartsScanning"],
|
||||||
const { CriticalPartsScanning } = useTreatments(
|
splitKey: bodyshop.imexshopid,
|
||||||
["CriticalPartsScanning"],
|
});
|
||||||
{},
|
|
||||||
bodyshop.imexshopid
|
|
||||||
);
|
|
||||||
const { loading, error, data, refetch } = useQuery(QUERY_AVAILABLE_JOBS, {
|
const { loading, error, data, refetch } = useQuery(QUERY_AVAILABLE_JOBS, {
|
||||||
fetchPolicy: "network-only",
|
fetchPolicy: "network-only",
|
||||||
nextFetchPolicy: "network-only",
|
nextFetchPolicy: "network-only",
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { logImEXEvent } from "../../firebase/firebase.utils";
|
|||||||
import cleanAxios from "../../utils/CleanAxios";
|
import cleanAxios from "../../utils/CleanAxios";
|
||||||
import formatBytes from "../../utils/formatbytes";
|
import formatBytes from "../../utils/formatbytes";
|
||||||
//import yauzl from "yauzl";
|
//import yauzl from "yauzl";
|
||||||
import { useTreatments } from "@splitsoftware/splitio-react";
|
import { useSplitTreatments } from "@splitsoftware/splitio-react";
|
||||||
|
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import { createStructuredSelector } from "reselect";
|
import { createStructuredSelector } from "reselect";
|
||||||
@@ -22,18 +22,17 @@ export default connect(
|
|||||||
mapDispatchToProps
|
mapDispatchToProps
|
||||||
)(JobsDocumentsDownloadButton);
|
)(JobsDocumentsDownloadButton);
|
||||||
|
|
||||||
export function JobsDocumentsDownloadButton({
|
export function JobsDocumentsDownloadButton({ bodyshop, galleryImages, identifier}) {
|
||||||
bodyshop,
|
|
||||||
galleryImages,
|
|
||||||
identifier,
|
|
||||||
}) {
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [download, setDownload] = useState(null);
|
const [download, setDownload] = useState(null);
|
||||||
const { Direct_Media_Download } = useTreatments(
|
|
||||||
["Direct_Media_Download"],
|
const { treatments: {Direct_Media_Download} } = useSplitTreatments({
|
||||||
{},
|
attributes: {},
|
||||||
bodyshop.imexshopid
|
names: ["Direct_Media_Download"],
|
||||||
);
|
splitKey: bodyshop.imexshopid,
|
||||||
|
});
|
||||||
|
|
||||||
const imagesToDownload = [
|
const imagesToDownload = [
|
||||||
...galleryImages.images.filter((image) => image.isSelected),
|
...galleryImages.images.filter((image) => image.isSelected),
|
||||||
...galleryImages.other.filter((image) => image.isSelected),
|
...galleryImages.other.filter((image) => image.isSelected),
|
||||||
|
|||||||
@@ -13,7 +13,8 @@ export default function LoadingSpinner({ loading = true, message, ...props }) {
|
|||||||
alignContent: "center"
|
alignContent: "center"
|
||||||
}}
|
}}
|
||||||
delay={200}
|
delay={200}
|
||||||
tip={message ? message : null}
|
// TODO: Client Update - tip only works when there are actually children, and this component is used in a lot of places where there are no children
|
||||||
|
// tip={message ? message : null}
|
||||||
>
|
>
|
||||||
{props.children}
|
{props.children}
|
||||||
</Spin>
|
</Spin>
|
||||||
|
|||||||
@@ -1,23 +1,11 @@
|
|||||||
import { DeleteFilled, WarningFilled, DownOutlined } from "@ant-design/icons";
|
import {DeleteFilled, DownOutlined, WarningFilled} from "@ant-design/icons";
|
||||||
import { useTreatments } from "@splitsoftware/splitio-react";
|
import {useSplitTreatments} from "@splitsoftware/splitio-react";
|
||||||
import {
|
import {Checkbox, Divider, Dropdown, Form, Input, InputNumber, Menu, Radio, Select, Space, Tag,} from "antd";
|
||||||
Divider,
|
|
||||||
Form,
|
|
||||||
Input,
|
|
||||||
InputNumber,
|
|
||||||
Radio,
|
|
||||||
Space,
|
|
||||||
Tag,
|
|
||||||
Select,
|
|
||||||
Menu,
|
|
||||||
Dropdown,
|
|
||||||
Checkbox,
|
|
||||||
} from "antd";
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import {useTranslation} from "react-i18next";
|
||||||
import { connect } from "react-redux";
|
import {connect} from "react-redux";
|
||||||
import { createStructuredSelector } from "reselect";
|
import {createStructuredSelector} from "reselect";
|
||||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
import {selectBodyshop} from "../../redux/user/user.selectors";
|
||||||
import FormDatePicker from "../form-date-picker/form-date-picker.component";
|
import FormDatePicker from "../form-date-picker/form-date-picker.component";
|
||||||
import CurrencyInput from "../form-items-formatted/currency-form-item.component";
|
import CurrencyInput from "../form-items-formatted/currency-form-item.component";
|
||||||
import FormListMoveArrows from "../form-list-move-arrows/form-list-move-arrows.component";
|
import FormListMoveArrows from "../form-list-move-arrows/form-list-move-arrows.component";
|
||||||
@@ -26,329 +14,319 @@ import VendorSearchSelect from "../vendor-search-select/vendor-search-select.com
|
|||||||
import PartsOrderModalPriceChange from "./parts-order-modal-price-change.component";
|
import PartsOrderModalPriceChange from "./parts-order-modal-price-change.component";
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
bodyshop: selectBodyshop,
|
bodyshop: selectBodyshop,
|
||||||
});
|
});
|
||||||
const mapDispatchToProps = (dispatch) => ({
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
||||||
});
|
});
|
||||||
export default connect(
|
export default connect(
|
||||||
mapStateToProps,
|
mapStateToProps,
|
||||||
mapDispatchToProps
|
mapDispatchToProps
|
||||||
)(PartsOrderModalComponent);
|
)(PartsOrderModalComponent);
|
||||||
|
|
||||||
export function PartsOrderModalComponent({
|
export function PartsOrderModalComponent({bodyshop, vendorList, sendTypeState, isReturn, preferredMake, job, form,}) {
|
||||||
bodyshop,
|
const [sendType, setSendType] = sendTypeState;
|
||||||
vendorList,
|
|
||||||
sendTypeState,
|
|
||||||
isReturn,
|
|
||||||
preferredMake,
|
|
||||||
job,
|
|
||||||
form,
|
|
||||||
}) {
|
|
||||||
const [sendType, setSendType] = sendTypeState;
|
|
||||||
const { OEConnection } = useTreatments(
|
|
||||||
["OEConnection"],
|
|
||||||
{},
|
|
||||||
bodyshop.imexshopid
|
|
||||||
);
|
|
||||||
const { OEConnection_PriceChange } = useTreatments(
|
|
||||||
["OEConnection_PriceChange"],
|
|
||||||
{},
|
|
||||||
bodyshop.imexshopid
|
|
||||||
);
|
|
||||||
const { t } = useTranslation();
|
|
||||||
const handleClick = ({ item, key, keyPath }) => {
|
|
||||||
form.setFieldsValue({ comments: item.props.value });
|
|
||||||
};
|
|
||||||
|
|
||||||
const menu = (
|
const {treatments: {OEConnection, OEConnection_PriceChange}} = useSplitTreatments({
|
||||||
<div>
|
attributes: {},
|
||||||
<Menu onClick={handleClick}>
|
names: ["OEConnection", "OEConnection_PriceChange"],
|
||||||
{bodyshop.md_parts_order_comment.map((comment, idx) => (
|
splitKey: bodyshop.imexshopid,
|
||||||
<Menu.Item value={comment.comment} key={idx}>
|
});
|
||||||
{comment.label}
|
|
||||||
</Menu.Item>
|
|
||||||
))}
|
|
||||||
</Menu>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<Form.Item name="returnfrombill" style={{ display: "none" }}>
|
|
||||||
<Input />
|
|
||||||
</Form.Item>
|
|
||||||
<LayoutFormRow grow noDivider>
|
|
||||||
<Form.Item
|
|
||||||
name="vendorid"
|
|
||||||
label={t("vendors.fields.name")}
|
|
||||||
rules={[
|
|
||||||
{
|
|
||||||
required: true,
|
|
||||||
//message: t("general.validation.required"),
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
<VendorSearchSelect
|
|
||||||
options={vendorList}
|
|
||||||
disabled={isReturn}
|
|
||||||
preferredMake={preferredMake}
|
|
||||||
showPhone
|
|
||||||
/>
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
|
||||||
name="deliver_by"
|
|
||||||
rules={[
|
|
||||||
{
|
|
||||||
required: true,
|
|
||||||
//message: t("general.validation.required"),
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
label={t("parts_orders.fields.deliver_by")}
|
|
||||||
>
|
|
||||||
<FormDatePicker onlyFuture />
|
|
||||||
</Form.Item>
|
|
||||||
{job && job.special_coverage_policy && (
|
|
||||||
<Tag color="tomato">
|
|
||||||
<Space>
|
|
||||||
<WarningFilled />
|
|
||||||
<span>{t("jobs.labels.specialcoveragepolicy")}</span>
|
|
||||||
</Space>
|
|
||||||
</Tag>
|
|
||||||
)}
|
|
||||||
{!isReturn && (
|
|
||||||
<Form.Item
|
|
||||||
name="removefrompartsqueue"
|
|
||||||
label={t("parts_orders.labels.removefrompartsqueue")}
|
|
||||||
valuePropName="checked"
|
|
||||||
>
|
|
||||||
<Checkbox />
|
|
||||||
</Form.Item>
|
|
||||||
)}
|
|
||||||
{OEConnection.treatment === "on" && !isReturn && (
|
|
||||||
<Form.Item
|
|
||||||
name="is_quote"
|
|
||||||
label={t("parts_orders.labels.is_quote")}
|
|
||||||
valuePropName="checked"
|
|
||||||
>
|
|
||||||
<Checkbox />
|
|
||||||
</Form.Item>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<Form.Item
|
const {t} = useTranslation();
|
||||||
name="order_type"
|
const handleClick = ({item, key, keyPath}) => {
|
||||||
initialValue="parts_order"
|
form.setFieldsValue({comments: item.props.value});
|
||||||
label={t("parts_orders.labels.order_type")}
|
};
|
||||||
>
|
|
||||||
<Radio.Group disabled={sendType === "oec"}>
|
const menu = (
|
||||||
<Radio value={"parts_order"}>
|
<div>
|
||||||
{t("parts_orders.labels.parts_order")}
|
<Menu onClick={handleClick}>
|
||||||
</Radio>
|
{bodyshop.md_parts_order_comment.map((comment, idx) => (
|
||||||
<Radio value={"sublet"}>
|
<Menu.Item value={comment.comment} key={idx}>
|
||||||
{t("parts_orders.labels.sublet_order")}
|
{comment.label}
|
||||||
</Radio>
|
</Menu.Item>
|
||||||
</Radio.Group>
|
))}
|
||||||
</Form.Item>
|
</Menu>
|
||||||
</LayoutFormRow>
|
</div>
|
||||||
<Divider orientation="left">
|
);
|
||||||
{t("parts_orders.labels.inthisorder")}
|
|
||||||
</Divider>
|
return (
|
||||||
<Form.List name={["parts_order_lines", "data"]}>
|
<div>
|
||||||
{(fields, { add, remove, move }) => {
|
<Form.Item name="returnfrombill" style={{display: "none"}}>
|
||||||
return (
|
<Input/>
|
||||||
<div>
|
</Form.Item>
|
||||||
{fields.map((field, index) => (
|
<LayoutFormRow grow noDivider>
|
||||||
<Form.Item required={false} key={field.key}>
|
<Form.Item
|
||||||
<div style={{ display: "flex" }}>
|
name="vendorid"
|
||||||
<LayoutFormRow grow noDivider style={{ flex: 1 }}>
|
label={t("vendors.fields.name")}
|
||||||
<Form.Item
|
rules={[
|
||||||
//span={8}
|
{
|
||||||
label={t("parts_orders.fields.line_desc")}
|
|
||||||
key={`${index}line_desc`}
|
|
||||||
name={[field.name, "line_desc"]}
|
|
||||||
rules={[
|
|
||||||
{
|
|
||||||
required: true,
|
required: true,
|
||||||
//message: t("general.validation.required"),
|
//message: t("general.validation.required"),
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
<Input />
|
<VendorSearchSelect
|
||||||
</Form.Item>
|
options={vendorList}
|
||||||
<Form.Item
|
disabled={isReturn}
|
||||||
label={t("parts_orders.fields.line_remarks")}
|
preferredMake={preferredMake}
|
||||||
key={`${index}line_remarks`}
|
showPhone
|
||||||
name={[field.name, "line_remarks"]}
|
/>
|
||||||
>
|
|
||||||
<Input />
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
|
||||||
label={t("parts_orders.fields.part_type")}
|
|
||||||
key={`${index}part_type`}
|
|
||||||
name={[field.name, "part_type"]}
|
|
||||||
>
|
|
||||||
<Select
|
|
||||||
disabled={
|
|
||||||
!(
|
|
||||||
sendType === "oec" &&
|
|
||||||
OEConnection_PriceChange.treatment === "on"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<Select.Option value="PAA">
|
|
||||||
{t("joblines.fields.part_types.PAA")}
|
|
||||||
</Select.Option>
|
|
||||||
<Select.Option value="PAC">
|
|
||||||
{t("joblines.fields.part_types.PAC")}
|
|
||||||
</Select.Option>
|
|
||||||
|
|
||||||
<Select.Option value="PAL">
|
|
||||||
{t("joblines.fields.part_types.PAL")}
|
|
||||||
</Select.Option>
|
|
||||||
<Select.Option value="PAG">
|
|
||||||
{t("joblines.fields.part_types.PAG")}
|
|
||||||
</Select.Option>
|
|
||||||
<Select.Option value="PAM">
|
|
||||||
{t("joblines.fields.part_types.PAM")}
|
|
||||||
</Select.Option>
|
|
||||||
<Select.Option value="PAP">
|
|
||||||
{t("joblines.fields.part_types.PAP")}
|
|
||||||
</Select.Option>
|
|
||||||
<Select.Option value="PAN">
|
|
||||||
{t("joblines.fields.part_types.PAN")}
|
|
||||||
</Select.Option>
|
|
||||||
<Select.Option value="PAO">
|
|
||||||
{t("joblines.fields.part_types.PAO")}
|
|
||||||
</Select.Option>
|
|
||||||
<Select.Option value="PAR">
|
|
||||||
{t("joblines.fields.part_types.PAR")}
|
|
||||||
</Select.Option>
|
|
||||||
<Select.Option value="PAS">
|
|
||||||
{t("joblines.fields.part_types.PAS")}
|
|
||||||
</Select.Option>
|
|
||||||
</Select>
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
|
||||||
label={t("parts_orders.fields.oem_partno")}
|
|
||||||
key={`${index}oem_partno`}
|
|
||||||
name={[field.name, "oem_partno"]}
|
|
||||||
>
|
|
||||||
<Input />
|
|
||||||
</Form.Item>
|
|
||||||
{
|
|
||||||
// <Form.Item
|
|
||||||
// label={t("parts_orders.fields.db_price")}
|
|
||||||
// key={`${index}db_price`}
|
|
||||||
// name={[field.name, "db_price"]}
|
|
||||||
// >
|
|
||||||
// <CurrencyInput />
|
|
||||||
// </Form.Item>
|
|
||||||
}
|
|
||||||
<Form.Item
|
|
||||||
label={t("parts_orders.fields.quantity")}
|
|
||||||
key={`${index}quantity`}
|
|
||||||
name={[field.name, "quantity"]}
|
|
||||||
rules={[
|
|
||||||
{
|
|
||||||
required: true,
|
|
||||||
//message: t("general.validation.required"),
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
<InputNumber />
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
|
||||||
label={t("parts_orders.fields.act_price")}
|
|
||||||
key={`${index}act_price`}
|
|
||||||
name={[field.name, "act_price"]}
|
|
||||||
>
|
|
||||||
<CurrencyInput
|
|
||||||
addonBefore={
|
|
||||||
<PartsOrderModalPriceChange
|
|
||||||
form={form}
|
|
||||||
field={field}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</Form.Item>
|
|
||||||
{isReturn && (
|
|
||||||
<Form.Item
|
|
||||||
label={t("parts_orders.fields.cost")}
|
|
||||||
key={`${index}cost`}
|
|
||||||
name={[field.name, "cost"]}
|
|
||||||
>
|
|
||||||
<CurrencyInput />
|
|
||||||
</Form.Item>
|
|
||||||
)}
|
|
||||||
</LayoutFormRow>
|
|
||||||
<Space wrap size="small" align="center">
|
|
||||||
<div>
|
|
||||||
<DeleteFilled
|
|
||||||
style={{ margin: "1rem" }}
|
|
||||||
onClick={() => {
|
|
||||||
remove(field.name);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<FormListMoveArrows
|
|
||||||
move={move}
|
|
||||||
index={index}
|
|
||||||
total={fields.length}
|
|
||||||
/>
|
|
||||||
</Space>
|
|
||||||
</div>
|
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
))}
|
<Form.Item
|
||||||
</div>
|
name="deliver_by"
|
||||||
);
|
rules={[
|
||||||
}}
|
{
|
||||||
</Form.List>
|
required: true,
|
||||||
<Form.Item
|
//message: t("general.validation.required"),
|
||||||
name="comments"
|
},
|
||||||
label={
|
]}
|
||||||
<Space>
|
label={t("parts_orders.fields.deliver_by")}
|
||||||
{t("parts_orders.fields.comments")}
|
>
|
||||||
<Dropdown overlay={menu}>
|
<FormDatePicker onlyFuture/>
|
||||||
<a
|
</Form.Item>
|
||||||
className="ant-dropdown-link"
|
{job && job.special_coverage_policy && (
|
||||||
href=" #"
|
<Tag color="tomato">
|
||||||
onClick={(e) => e.preventDefault()}
|
<Space>
|
||||||
>
|
<WarningFilled/>
|
||||||
<DownOutlined />
|
<span>{t("jobs.labels.specialcoveragepolicy")}</span>
|
||||||
</a>
|
</Space>
|
||||||
</Dropdown>
|
</Tag>
|
||||||
</Space>
|
)}
|
||||||
}
|
{!isReturn && (
|
||||||
>
|
<Form.Item
|
||||||
<Input.TextArea rows={3} />
|
name="removefrompartsqueue"
|
||||||
</Form.Item>
|
label={t("parts_orders.labels.removefrompartsqueue")}
|
||||||
|
valuePropName="checked"
|
||||||
|
>
|
||||||
|
<Checkbox/>
|
||||||
|
</Form.Item>
|
||||||
|
)}
|
||||||
|
{OEConnection.treatment === "on" && !isReturn && (
|
||||||
|
<Form.Item
|
||||||
|
name="is_quote"
|
||||||
|
label={t("parts_orders.labels.is_quote")}
|
||||||
|
valuePropName="checked"
|
||||||
|
>
|
||||||
|
<Checkbox/>
|
||||||
|
</Form.Item>
|
||||||
|
)}
|
||||||
|
|
||||||
<Form.Item noStyle shouldUpdate>
|
<Form.Item
|
||||||
{() => {
|
name="order_type"
|
||||||
const is_quote = form.getFieldValue("is_quote");
|
initialValue="parts_order"
|
||||||
if (is_quote) setSendType("oec");
|
label={t("parts_orders.labels.order_type")}
|
||||||
return (
|
>
|
||||||
<Radio.Group
|
<Radio.Group disabled={sendType === "oec"}>
|
||||||
defaultValue={sendType}
|
<Radio value={"parts_order"}>
|
||||||
value={sendType}
|
{t("parts_orders.labels.parts_order")}
|
||||||
onChange={(e) => setSendType(e.target.value)}
|
</Radio>
|
||||||
|
<Radio value={"sublet"}>
|
||||||
|
{t("parts_orders.labels.sublet_order")}
|
||||||
|
</Radio>
|
||||||
|
</Radio.Group>
|
||||||
|
</Form.Item>
|
||||||
|
</LayoutFormRow>
|
||||||
|
<Divider orientation="left">
|
||||||
|
{t("parts_orders.labels.inthisorder")}
|
||||||
|
</Divider>
|
||||||
|
<Form.List name={["parts_order_lines", "data"]}>
|
||||||
|
{(fields, {add, remove, move}) => {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{fields.map((field, index) => (
|
||||||
|
<Form.Item required={false} key={field.key}>
|
||||||
|
<div style={{display: "flex"}}>
|
||||||
|
<LayoutFormRow grow noDivider style={{flex: 1}}>
|
||||||
|
<Form.Item
|
||||||
|
//span={8}
|
||||||
|
label={t("parts_orders.fields.line_desc")}
|
||||||
|
key={`${index}line_desc`}
|
||||||
|
name={[field.name, "line_desc"]}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<Input/>
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("parts_orders.fields.line_remarks")}
|
||||||
|
key={`${index}line_remarks`}
|
||||||
|
name={[field.name, "line_remarks"]}
|
||||||
|
>
|
||||||
|
<Input/>
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("parts_orders.fields.part_type")}
|
||||||
|
key={`${index}part_type`}
|
||||||
|
name={[field.name, "part_type"]}
|
||||||
|
>
|
||||||
|
<Select
|
||||||
|
disabled={
|
||||||
|
!(
|
||||||
|
sendType === "oec" &&
|
||||||
|
OEConnection_PriceChange.treatment === "on"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Select.Option value="PAA">
|
||||||
|
{t("joblines.fields.part_types.PAA")}
|
||||||
|
</Select.Option>
|
||||||
|
<Select.Option value="PAC">
|
||||||
|
{t("joblines.fields.part_types.PAC")}
|
||||||
|
</Select.Option>
|
||||||
|
|
||||||
|
<Select.Option value="PAL">
|
||||||
|
{t("joblines.fields.part_types.PAL")}
|
||||||
|
</Select.Option>
|
||||||
|
<Select.Option value="PAG">
|
||||||
|
{t("joblines.fields.part_types.PAG")}
|
||||||
|
</Select.Option>
|
||||||
|
<Select.Option value="PAM">
|
||||||
|
{t("joblines.fields.part_types.PAM")}
|
||||||
|
</Select.Option>
|
||||||
|
<Select.Option value="PAP">
|
||||||
|
{t("joblines.fields.part_types.PAP")}
|
||||||
|
</Select.Option>
|
||||||
|
<Select.Option value="PAN">
|
||||||
|
{t("joblines.fields.part_types.PAN")}
|
||||||
|
</Select.Option>
|
||||||
|
<Select.Option value="PAO">
|
||||||
|
{t("joblines.fields.part_types.PAO")}
|
||||||
|
</Select.Option>
|
||||||
|
<Select.Option value="PAR">
|
||||||
|
{t("joblines.fields.part_types.PAR")}
|
||||||
|
</Select.Option>
|
||||||
|
<Select.Option value="PAS">
|
||||||
|
{t("joblines.fields.part_types.PAS")}
|
||||||
|
</Select.Option>
|
||||||
|
</Select>
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("parts_orders.fields.oem_partno")}
|
||||||
|
key={`${index}oem_partno`}
|
||||||
|
name={[field.name, "oem_partno"]}
|
||||||
|
>
|
||||||
|
<Input/>
|
||||||
|
</Form.Item>
|
||||||
|
{
|
||||||
|
// <Form.Item
|
||||||
|
// label={t("parts_orders.fields.db_price")}
|
||||||
|
// key={`${index}db_price`}
|
||||||
|
// name={[field.name, "db_price"]}
|
||||||
|
// >
|
||||||
|
// <CurrencyInput />
|
||||||
|
// </Form.Item>
|
||||||
|
}
|
||||||
|
<Form.Item
|
||||||
|
label={t("parts_orders.fields.quantity")}
|
||||||
|
key={`${index}quantity`}
|
||||||
|
name={[field.name, "quantity"]}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<InputNumber/>
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("parts_orders.fields.act_price")}
|
||||||
|
key={`${index}act_price`}
|
||||||
|
name={[field.name, "act_price"]}
|
||||||
|
>
|
||||||
|
<CurrencyInput
|
||||||
|
addonBefore={
|
||||||
|
<PartsOrderModalPriceChange
|
||||||
|
form={form}
|
||||||
|
field={field}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
{isReturn && (
|
||||||
|
<Form.Item
|
||||||
|
label={t("parts_orders.fields.cost")}
|
||||||
|
key={`${index}cost`}
|
||||||
|
name={[field.name, "cost"]}
|
||||||
|
>
|
||||||
|
<CurrencyInput/>
|
||||||
|
</Form.Item>
|
||||||
|
)}
|
||||||
|
</LayoutFormRow>
|
||||||
|
<Space wrap size="small" align="center">
|
||||||
|
<div>
|
||||||
|
<DeleteFilled
|
||||||
|
style={{margin: "1rem"}}
|
||||||
|
onClick={() => {
|
||||||
|
remove(field.name);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<FormListMoveArrows
|
||||||
|
move={move}
|
||||||
|
index={index}
|
||||||
|
total={fields.length}
|
||||||
|
/>
|
||||||
|
</Space>
|
||||||
|
</div>
|
||||||
|
</Form.Item>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
</Form.List>
|
||||||
|
<Form.Item
|
||||||
|
name="comments"
|
||||||
|
label={
|
||||||
|
<Space>
|
||||||
|
{t("parts_orders.fields.comments")}
|
||||||
|
<Dropdown overlay={menu}>
|
||||||
|
<a
|
||||||
|
className="ant-dropdown-link"
|
||||||
|
href=" #"
|
||||||
|
onClick={(e) => e.preventDefault()}
|
||||||
|
>
|
||||||
|
<DownOutlined/>
|
||||||
|
</a>
|
||||||
|
</Dropdown>
|
||||||
|
</Space>
|
||||||
|
}
|
||||||
>
|
>
|
||||||
<Radio disabled={is_quote} value={"none"}>
|
<Input.TextArea rows={3}/>
|
||||||
{t("general.labels.none")}
|
</Form.Item>
|
||||||
</Radio>
|
|
||||||
<Radio disabled={is_quote} value={"e"}>
|
<Form.Item noStyle shouldUpdate>
|
||||||
{t("parts_orders.labels.email")}
|
{() => {
|
||||||
</Radio>
|
const is_quote = form.getFieldValue("is_quote");
|
||||||
<Radio disabled={is_quote} value={"p"}>
|
if (is_quote) setSendType("oec");
|
||||||
{t("parts_orders.labels.print")}
|
return (
|
||||||
</Radio>
|
<Radio.Group
|
||||||
{OEConnection.treatment === "on" && !isReturn && (
|
defaultValue={sendType}
|
||||||
<Radio value={"oec"}>{t("parts_orders.labels.oec")}</Radio>
|
value={sendType}
|
||||||
)}
|
onChange={(e) => setSendType(e.target.value)}
|
||||||
</Radio.Group>
|
>
|
||||||
);
|
<Radio disabled={is_quote} value={"none"}>
|
||||||
}}
|
{t("general.labels.none")}
|
||||||
</Form.Item>
|
</Radio>
|
||||||
</div>
|
<Radio disabled={is_quote} value={"e"}>
|
||||||
);
|
{t("parts_orders.labels.email")}
|
||||||
|
</Radio>
|
||||||
|
<Radio disabled={is_quote} value={"p"}>
|
||||||
|
{t("parts_orders.labels.print")}
|
||||||
|
</Radio>
|
||||||
|
{OEConnection.treatment === "on" && !isReturn && (
|
||||||
|
<Radio value={"oec"}>{t("parts_orders.labels.oec")}</Radio>
|
||||||
|
)}
|
||||||
|
</Radio.Group>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
</Form.Item>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,395 +1,380 @@
|
|||||||
import { useMutation, useQuery, useApolloClient } from "@apollo/client";
|
import {useApolloClient, useMutation, useQuery} from "@apollo/client";
|
||||||
import { Form, Modal, notification } from "antd";
|
import {Form, Modal, notification} from "antd";
|
||||||
import dayjs from "../../utils/day";
|
import dayjs from "../../utils/day";
|
||||||
import React, { useEffect, useState } from "react";
|
import React, {useEffect, useState} from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import {useTranslation} from "react-i18next";
|
||||||
import { connect } from "react-redux";
|
import {connect} from "react-redux";
|
||||||
import { createStructuredSelector } from "reselect";
|
import {createStructuredSelector} from "reselect";
|
||||||
import { logImEXEvent, auth } from "../../firebase/firebase.utils";
|
import {auth, logImEXEvent} from "../../firebase/firebase.utils";
|
||||||
import { UPDATE_JOB_LINE_STATUS } from "../../graphql/jobs-lines.queries";
|
import {UPDATE_JOB_LINE_STATUS} from "../../graphql/jobs-lines.queries";
|
||||||
import {
|
import {INSERT_NEW_PARTS_ORDERS, QUERY_PARTS_ORDER_OEC,} from "../../graphql/parts-orders.queries";
|
||||||
INSERT_NEW_PARTS_ORDERS,
|
import {QUERY_ALL_VENDORS_FOR_ORDER} from "../../graphql/vendors.queries";
|
||||||
QUERY_PARTS_ORDER_OEC,
|
import {insertAuditTrail} from "../../redux/application/application.actions";
|
||||||
} from "../../graphql/parts-orders.queries";
|
import {setEmailOptions} from "../../redux/email/email.actions";
|
||||||
import { QUERY_ALL_VENDORS_FOR_ORDER } from "../../graphql/vendors.queries";
|
import {setModalContext, toggleModalVisible,} from "../../redux/modals/modals.actions";
|
||||||
import { insertAuditTrail } from "../../redux/application/application.actions";
|
import {selectPartsOrder} from "../../redux/modals/modals.selectors";
|
||||||
import { setEmailOptions } from "../../redux/email/email.actions";
|
import {selectBodyshop, selectCurrentUser,} from "../../redux/user/user.selectors";
|
||||||
import {
|
|
||||||
setModalContext,
|
|
||||||
toggleModalVisible,
|
|
||||||
} from "../../redux/modals/modals.actions";
|
|
||||||
import { selectPartsOrder } from "../../redux/modals/modals.selectors";
|
|
||||||
import {
|
|
||||||
selectBodyshop,
|
|
||||||
selectCurrentUser,
|
|
||||||
} from "../../redux/user/user.selectors";
|
|
||||||
import AuditTrailMapping from "../../utils/AuditTrailMappings";
|
import AuditTrailMapping from "../../utils/AuditTrailMappings";
|
||||||
import { GenerateDocument } from "../../utils/RenderTemplate";
|
import {GenerateDocument} from "../../utils/RenderTemplate";
|
||||||
import { TemplateList } from "../../utils/TemplateConstants";
|
import {TemplateList} from "../../utils/TemplateConstants";
|
||||||
import AlertComponent from "../alert/alert.component";
|
import AlertComponent from "../alert/alert.component";
|
||||||
import LoadingSpinner from "../loading-spinner/loading-spinner.component";
|
import LoadingSpinner from "../loading-spinner/loading-spinner.component";
|
||||||
import PartsOrderModalComponent from "./parts-order-modal.component";
|
import PartsOrderModalComponent from "./parts-order-modal.component";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import { useTreatments } from "@splitsoftware/splitio-react";
|
import {useSplitTreatments} from "@splitsoftware/splitio-react";
|
||||||
import _ from "lodash";
|
import _ from "lodash";
|
||||||
import { UPDATE_JOB } from "../../graphql/jobs.queries";
|
import {UPDATE_JOB} from "../../graphql/jobs.queries";
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
currentUser: selectCurrentUser,
|
currentUser: selectCurrentUser,
|
||||||
bodyshop: selectBodyshop,
|
bodyshop: selectBodyshop,
|
||||||
partsOrderModal: selectPartsOrder,
|
partsOrderModal: selectPartsOrder,
|
||||||
});
|
});
|
||||||
|
|
||||||
const mapDispatchToProps = (dispatch) => ({
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
setEmailOptions: (e) => dispatch(setEmailOptions(e)),
|
setEmailOptions: (e) => dispatch(setEmailOptions(e)),
|
||||||
toggleModalVisible: () => dispatch(toggleModalVisible("partsOrder")),
|
toggleModalVisible: () => dispatch(toggleModalVisible("partsOrder")),
|
||||||
setBillEnterContext: (context) =>
|
setBillEnterContext: (context) =>
|
||||||
dispatch(setModalContext({ context: context, modal: "billEnter" })),
|
dispatch(setModalContext({context: context, modal: "billEnter"})),
|
||||||
insertAuditTrail: ({ jobid, operation }) =>
|
insertAuditTrail: ({jobid, operation}) =>
|
||||||
dispatch(insertAuditTrail({ jobid, operation })),
|
dispatch(insertAuditTrail({jobid, operation})),
|
||||||
});
|
});
|
||||||
|
|
||||||
export function PartsOrderModalContainer({
|
export function PartsOrderModalContainer({partsOrderModal,toggleModalVisible,currentUser, bodyshop, setEmailOptions, setBillEnterContext, insertAuditTrail,}) {
|
||||||
partsOrderModal,
|
const {t} = useTranslation();
|
||||||
toggleModalVisible,
|
const client = useApolloClient();
|
||||||
currentUser,
|
|
||||||
bodyshop,
|
|
||||||
setEmailOptions,
|
|
||||||
setBillEnterContext,
|
|
||||||
insertAuditTrail,
|
|
||||||
}) {
|
|
||||||
const { t } = useTranslation();
|
|
||||||
const client = useApolloClient();
|
|
||||||
const { OEConnection_PriceChange } = useTreatments(
|
|
||||||
["OEConnection_PriceChange"],
|
|
||||||
{},
|
|
||||||
bodyshop.imexshopid
|
|
||||||
);
|
|
||||||
const { visible, context, actions } = partsOrderModal;
|
|
||||||
const {
|
|
||||||
jobId,
|
|
||||||
linesToOrder,
|
|
||||||
isReturn,
|
|
||||||
vendorId,
|
|
||||||
returnFromBill,
|
|
||||||
invoiceNumber,
|
|
||||||
job,
|
|
||||||
} = context;
|
|
||||||
|
|
||||||
const { refetch } = actions;
|
const {treatments: {OEConnection_PriceChange}} = useSplitTreatments({
|
||||||
const [form] = Form.useForm();
|
attributes: {},
|
||||||
const [saving, setSaving] = useState(false);
|
names: ["OEConnection_PriceChange"],
|
||||||
const sendTypeState = useState("e");
|
splitKey: bodyshop.imexshopid,
|
||||||
const sendType = sendTypeState[0];
|
|
||||||
|
|
||||||
const { loading, error, data } = useQuery(QUERY_ALL_VENDORS_FOR_ORDER, {
|
|
||||||
skip: !visible,
|
|
||||||
variables: { jobId: jobId },
|
|
||||||
fetchPolicy: "network-only",
|
|
||||||
nextFetchPolicy: "network-only",
|
|
||||||
});
|
|
||||||
|
|
||||||
const [insertPartOrder] = useMutation(INSERT_NEW_PARTS_ORDERS);
|
|
||||||
const [updateJobLines] = useMutation(UPDATE_JOB_LINE_STATUS);
|
|
||||||
const [updateJob] = useMutation(UPDATE_JOB);
|
|
||||||
|
|
||||||
const handleFinish = async ({
|
|
||||||
order_type,
|
|
||||||
removefrompartsqueue,
|
|
||||||
is_quote,
|
|
||||||
...values
|
|
||||||
}) => {
|
|
||||||
logImEXEvent("parts_order_insert");
|
|
||||||
setSaving(true);
|
|
||||||
let insertResult;
|
|
||||||
|
|
||||||
insertResult = await insertPartOrder({
|
|
||||||
variables: {
|
|
||||||
po: [
|
|
||||||
{
|
|
||||||
...values,
|
|
||||||
order_date: dayjs().format("YYYY-MM-DD"),
|
|
||||||
orderedby: currentUser.email,
|
|
||||||
jobid: jobId,
|
|
||||||
user_email: currentUser.email,
|
|
||||||
return: isReturn,
|
|
||||||
status: is_quote
|
|
||||||
? bodyshop.md_order_statuses.default_quote || "Quote"
|
|
||||||
: bodyshop.md_order_statuses.default_ordered || "Ordered*",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
refetchQueries: ["QUERY_PARTS_BILLS_BY_JOBID"],
|
|
||||||
});
|
|
||||||
if (!!insertResult.errors) {
|
|
||||||
notification["error"]({
|
|
||||||
message: t("parts_orders.errors.creating"),
|
|
||||||
description: JSON.stringify(insertResult.errors),
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
notification["success"]({
|
|
||||||
message: values.isReturn
|
|
||||||
? t("parts_orders.successes.return_created")
|
|
||||||
: t("parts_orders.successes.created"),
|
|
||||||
});
|
|
||||||
insertAuditTrail({
|
|
||||||
jobid: jobId,
|
|
||||||
operation: isReturn
|
|
||||||
? AuditTrailMapping.jobspartsreturn(
|
|
||||||
insertResult.data.insert_parts_orders.returning[0].order_number
|
|
||||||
)
|
|
||||||
: AuditTrailMapping.jobspartsorder(
|
|
||||||
insertResult.data.insert_parts_orders.returning[0].order_number
|
|
||||||
),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const jobLinesResult = await updateJobLines({
|
const {visible, context, actions} = partsOrderModal;
|
||||||
variables: {
|
const {
|
||||||
ids: values.parts_order_lines.data
|
jobId,
|
||||||
.filter((item) => item.job_line_id)
|
linesToOrder,
|
||||||
.map((item) => item.job_line_id),
|
isReturn,
|
||||||
status: isReturn
|
vendorId,
|
||||||
? bodyshop.md_order_statuses.default_returned || "Returned*"
|
returnFromBill,
|
||||||
: is_quote
|
invoiceNumber,
|
||||||
? bodyshop.md_order_statuses.default_quote || "Quote"
|
job,
|
||||||
: bodyshop.md_order_statuses.default_ordered || "Ordered*",
|
} = context;
|
||||||
},
|
|
||||||
|
const {refetch} = actions;
|
||||||
|
const [form] = Form.useForm();
|
||||||
|
const [saving, setSaving] = useState(false);
|
||||||
|
const sendTypeState = useState("e");
|
||||||
|
const sendType = sendTypeState[0];
|
||||||
|
|
||||||
|
const {loading, error, data} = useQuery(QUERY_ALL_VENDORS_FOR_ORDER, {
|
||||||
|
skip: !visible,
|
||||||
|
variables: {jobId: jobId},
|
||||||
|
fetchPolicy: "network-only",
|
||||||
|
nextFetchPolicy: "network-only",
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!isReturn && removefrompartsqueue) {
|
const [insertPartOrder] = useMutation(INSERT_NEW_PARTS_ORDERS);
|
||||||
await updateJob({
|
const [updateJobLines] = useMutation(UPDATE_JOB_LINE_STATUS);
|
||||||
variables: {
|
const [updateJob] = useMutation(UPDATE_JOB);
|
||||||
jobId: jobId,
|
|
||||||
job: {
|
|
||||||
queued_for_parts: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!!jobLinesResult.errors) {
|
const handleFinish = async ({
|
||||||
notification["error"]({
|
order_type,
|
||||||
message: t("parts_orders.errors.creating"),
|
removefrompartsqueue,
|
||||||
description: JSON.stringify(jobLinesResult.errors),
|
is_quote,
|
||||||
});
|
...values
|
||||||
}
|
}) => {
|
||||||
|
logImEXEvent("parts_order_insert");
|
||||||
|
setSaving(true);
|
||||||
|
let insertResult;
|
||||||
|
|
||||||
if (values.vendorid === bodyshop.inhousevendorid) {
|
insertResult = await insertPartOrder({
|
||||||
setBillEnterContext({
|
variables: {
|
||||||
actions: { refetch: refetch },
|
po: [
|
||||||
context: {
|
{
|
||||||
disableInvNumber: true,
|
...values,
|
||||||
job: { id: jobId },
|
order_date: dayjs().format("YYYY-MM-DD"),
|
||||||
bill: {
|
orderedby: currentUser.email,
|
||||||
vendorid: bodyshop.inhousevendorid,
|
jobid: jobId,
|
||||||
invoice_number: "ih",
|
user_email: currentUser.email,
|
||||||
isinhouse: true,
|
return: isReturn,
|
||||||
date: new dayjs(),
|
status: is_quote
|
||||||
total: 0,
|
? bodyshop.md_order_statuses.default_quote || "Quote"
|
||||||
billlines: values.parts_order_lines.data.map((p) => {
|
: bodyshop.md_order_statuses.default_ordered || "Ordered*",
|
||||||
return {
|
},
|
||||||
joblineid: p.job_line_id,
|
],
|
||||||
actual_price: p.act_price,
|
|
||||||
actual_cost: 0, //p.act_price,
|
|
||||||
line_desc: p.line_desc,
|
|
||||||
line_remarks: p.line_remarks,
|
|
||||||
part_type: p.part_type,
|
|
||||||
quantity: p.quantity || 1,
|
|
||||||
applicable_taxes: {
|
|
||||||
local: false,
|
|
||||||
state: false,
|
|
||||||
federal: false,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
toggleModalVisible();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (refetch) refetch();
|
|
||||||
|
|
||||||
const Templates = TemplateList("partsorder", context);
|
|
||||||
|
|
||||||
if (sendType === "e") {
|
|
||||||
const matchingVendor = data.vendors.filter(
|
|
||||||
(item) => item.id === values.vendorid
|
|
||||||
)[0];
|
|
||||||
|
|
||||||
let vendorEmails =
|
|
||||||
matchingVendor &&
|
|
||||||
matchingVendor.email &&
|
|
||||||
matchingVendor.email.split(RegExp("[;,]"));
|
|
||||||
|
|
||||||
GenerateDocument(
|
|
||||||
{
|
|
||||||
name: isReturn
|
|
||||||
? Templates.parts_return_slip.key
|
|
||||||
: order_type === "parts_order"
|
|
||||||
? Templates.parts_order.key
|
|
||||||
: Templates.sublet_order.key,
|
|
||||||
variables: {
|
|
||||||
id: insertResult.data.insert_parts_orders.returning[0].id,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
to: matchingVendor ? vendorEmails : null,
|
|
||||||
replyTo: bodyshop.email,
|
|
||||||
subject: isReturn
|
|
||||||
? Templates.parts_return_slip.subject
|
|
||||||
: order_type === "parts_order"
|
|
||||||
? Templates.parts_order.subject
|
|
||||||
: Templates.sublet_order.subject,
|
|
||||||
},
|
|
||||||
"e",
|
|
||||||
jobId
|
|
||||||
);
|
|
||||||
} else if (sendType === "p") {
|
|
||||||
GenerateDocument(
|
|
||||||
{
|
|
||||||
name: isReturn
|
|
||||||
? Templates.parts_return_slip.key
|
|
||||||
: order_type === "parts_order"
|
|
||||||
? Templates.parts_order.key
|
|
||||||
: Templates.sublet_order.key,
|
|
||||||
variables: {
|
|
||||||
id: insertResult.data.insert_parts_orders.returning[0].id,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{},
|
|
||||||
"p"
|
|
||||||
);
|
|
||||||
} else if (sendType === "oec") {
|
|
||||||
//Send to Partner OEC.
|
|
||||||
try {
|
|
||||||
const partsOrder = await client.query({
|
|
||||||
query: QUERY_PARTS_ORDER_OEC,
|
|
||||||
variables: {
|
|
||||||
id: insertResult.data.insert_parts_orders.returning[0].id,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
let po;
|
|
||||||
//Massage the data based on the split. Should they be able to overwrite OEC pricing?
|
|
||||||
if (OEConnection_PriceChange.treatment === "on") {
|
|
||||||
//Set the flag to include the override.
|
|
||||||
po = _.cloneDeep(partsOrder.data.parts_orders_by_pk);
|
|
||||||
po.parts_order_lines.forEach((pol) => {
|
|
||||||
pol.priceChange = true;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const oecResponse = await axios.post(
|
|
||||||
"http://localhost:1337/oec/",
|
|
||||||
|
|
||||||
po || partsOrder.data.parts_orders_by_pk,
|
|
||||||
{
|
|
||||||
headers: {
|
|
||||||
Authorization: `Bearer ${await auth.currentUser.getIdToken()}`,
|
|
||||||
},
|
},
|
||||||
}
|
refetchQueries: ["QUERY_PARTS_BILLS_BY_JOBID"],
|
||||||
);
|
|
||||||
|
|
||||||
if (oecResponse.data && oecResponse.data.success === false) {
|
|
||||||
notification.open({
|
|
||||||
type: "error",
|
|
||||||
message: t("parts_orders.errors.oec", {
|
|
||||||
error: oecResponse.data.error,
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.log("Error OEC.", error);
|
|
||||||
notification["error"]({
|
|
||||||
message: t("parts_orders.errors.oec", {
|
|
||||||
error: JSON.stringify(error.message),
|
|
||||||
}),
|
|
||||||
});
|
});
|
||||||
setSaving(false);
|
if (!!insertResult.errors) {
|
||||||
return;
|
notification["error"]({
|
||||||
}
|
message: t("parts_orders.errors.creating"),
|
||||||
}
|
description: JSON.stringify(insertResult.errors),
|
||||||
setSaving(false);
|
|
||||||
toggleModalVisible();
|
|
||||||
};
|
|
||||||
|
|
||||||
const initialValues = {
|
|
||||||
jobid: jobId,
|
|
||||||
return: isReturn,
|
|
||||||
deliver_by: isReturn ? dayjs(new Date()) : null,
|
|
||||||
vendorid: vendorId,
|
|
||||||
returnfrombill: returnFromBill,
|
|
||||||
|
|
||||||
parts_order_lines: {
|
|
||||||
data: linesToOrder
|
|
||||||
? linesToOrder.reduce((acc, value) => {
|
|
||||||
acc.push({
|
|
||||||
line_desc: value.line_desc,
|
|
||||||
oem_partno: value.oem_partno,
|
|
||||||
db_price: value.db_price,
|
|
||||||
act_price: value.act_price,
|
|
||||||
cost: value.cost,
|
|
||||||
quantity: value.part_qty,
|
|
||||||
job_line_id: isReturn ? value.joblineid : value.id,
|
|
||||||
part_type: value.part_type,
|
|
||||||
...(isReturn && { cm_received: false }),
|
|
||||||
});
|
});
|
||||||
return acc;
|
return;
|
||||||
}, [])
|
}
|
||||||
: [],
|
notification["success"]({
|
||||||
},
|
message: values.isReturn
|
||||||
};
|
? t("parts_orders.successes.return_created")
|
||||||
|
: t("parts_orders.successes.created"),
|
||||||
|
});
|
||||||
|
insertAuditTrail({
|
||||||
|
jobid: jobId,
|
||||||
|
operation: isReturn
|
||||||
|
? AuditTrailMapping.jobspartsreturn(
|
||||||
|
insertResult.data.insert_parts_orders.returning[0].order_number
|
||||||
|
)
|
||||||
|
: AuditTrailMapping.jobspartsorder(
|
||||||
|
insertResult.data.insert_parts_orders.returning[0].order_number
|
||||||
|
),
|
||||||
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
const jobLinesResult = await updateJobLines({
|
||||||
if (visible && !!linesToOrder) {
|
variables: {
|
||||||
form.resetFields();
|
ids: values.parts_order_lines.data
|
||||||
}
|
.filter((item) => item.job_line_id)
|
||||||
}, [visible, linesToOrder, form]);
|
.map((item) => item.job_line_id),
|
||||||
|
status: isReturn
|
||||||
|
? bodyshop.md_order_statuses.default_returned || "Returned*"
|
||||||
|
: is_quote
|
||||||
|
? bodyshop.md_order_statuses.default_quote || "Quote"
|
||||||
|
: bodyshop.md_order_statuses.default_ordered || "Ordered*",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
if (!isReturn && removefrompartsqueue) {
|
||||||
<Modal
|
await updateJob({
|
||||||
open={visible}
|
variables: {
|
||||||
title={
|
jobId: jobId,
|
||||||
isReturn
|
job: {
|
||||||
? `${t("parts_orders.labels.returnpartsorder")} ${invoiceNumber}`
|
queued_for_parts: false,
|
||||||
: t("parts_orders.labels.newpartsorder")
|
},
|
||||||
}
|
},
|
||||||
onCancel={() => toggleModalVisible()}
|
});
|
||||||
onOk={() => form.submit()}
|
}
|
||||||
okButtonProps={{ loading: saving }}
|
|
||||||
cancelButtonProps={{ loading: saving }}
|
if (!!jobLinesResult.errors) {
|
||||||
destroyOnClose
|
notification["error"]({
|
||||||
width="75%"
|
message: t("parts_orders.errors.creating"),
|
||||||
forceRender
|
description: JSON.stringify(jobLinesResult.errors),
|
||||||
>
|
});
|
||||||
{error ? <AlertComponent message={error.message} type="error" /> : null}
|
}
|
||||||
<Form
|
|
||||||
form={form}
|
if (values.vendorid === bodyshop.inhousevendorid) {
|
||||||
layout="vertical"
|
setBillEnterContext({
|
||||||
autoComplete="no"
|
actions: {refetch: refetch},
|
||||||
onFinish={handleFinish}
|
context: {
|
||||||
initialValues={initialValues}
|
disableInvNumber: true,
|
||||||
>
|
job: {id: jobId},
|
||||||
{loading ? (
|
bill: {
|
||||||
<LoadingSpinner />
|
vendorid: bodyshop.inhousevendorid,
|
||||||
) : (
|
invoice_number: "ih",
|
||||||
<PartsOrderModalComponent
|
isinhouse: true,
|
||||||
form={form}
|
date: new dayjs(),
|
||||||
vendorList={(data && data.vendors) || []}
|
total: 0,
|
||||||
sendTypeState={sendTypeState}
|
billlines: values.parts_order_lines.data.map((p) => {
|
||||||
isReturn={isReturn}
|
return {
|
||||||
preferredMake={data && data.jobs[0] && data.jobs[0].v_make_desc}
|
joblineid: p.job_line_id,
|
||||||
job={job}
|
actual_price: p.act_price,
|
||||||
/>
|
actual_cost: 0, //p.act_price,
|
||||||
)}
|
line_desc: p.line_desc,
|
||||||
</Form>
|
line_remarks: p.line_remarks,
|
||||||
</Modal>
|
part_type: p.part_type,
|
||||||
);
|
quantity: p.quantity || 1,
|
||||||
|
applicable_taxes: {
|
||||||
|
local: false,
|
||||||
|
state: false,
|
||||||
|
federal: false,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
toggleModalVisible();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (refetch) refetch();
|
||||||
|
|
||||||
|
const Templates = TemplateList("partsorder", context);
|
||||||
|
|
||||||
|
if (sendType === "e") {
|
||||||
|
const matchingVendor = data.vendors.filter(
|
||||||
|
(item) => item.id === values.vendorid
|
||||||
|
)[0];
|
||||||
|
|
||||||
|
let vendorEmails =
|
||||||
|
matchingVendor &&
|
||||||
|
matchingVendor.email &&
|
||||||
|
matchingVendor.email.split(RegExp("[;,]"));
|
||||||
|
|
||||||
|
GenerateDocument(
|
||||||
|
{
|
||||||
|
name: isReturn
|
||||||
|
? Templates.parts_return_slip.key
|
||||||
|
: order_type === "parts_order"
|
||||||
|
? Templates.parts_order.key
|
||||||
|
: Templates.sublet_order.key,
|
||||||
|
variables: {
|
||||||
|
id: insertResult.data.insert_parts_orders.returning[0].id,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
to: matchingVendor ? vendorEmails : null,
|
||||||
|
replyTo: bodyshop.email,
|
||||||
|
subject: isReturn
|
||||||
|
? Templates.parts_return_slip.subject
|
||||||
|
: order_type === "parts_order"
|
||||||
|
? Templates.parts_order.subject
|
||||||
|
: Templates.sublet_order.subject,
|
||||||
|
},
|
||||||
|
"e",
|
||||||
|
jobId
|
||||||
|
);
|
||||||
|
} else if (sendType === "p") {
|
||||||
|
GenerateDocument(
|
||||||
|
{
|
||||||
|
name: isReturn
|
||||||
|
? Templates.parts_return_slip.key
|
||||||
|
: order_type === "parts_order"
|
||||||
|
? Templates.parts_order.key
|
||||||
|
: Templates.sublet_order.key,
|
||||||
|
variables: {
|
||||||
|
id: insertResult.data.insert_parts_orders.returning[0].id,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{},
|
||||||
|
"p"
|
||||||
|
);
|
||||||
|
} else if (sendType === "oec") {
|
||||||
|
//Send to Partner OEC.
|
||||||
|
try {
|
||||||
|
const partsOrder = await client.query({
|
||||||
|
query: QUERY_PARTS_ORDER_OEC,
|
||||||
|
variables: {
|
||||||
|
id: insertResult.data.insert_parts_orders.returning[0].id,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
let po;
|
||||||
|
//Massage the data based on the split. Should they be able to overwrite OEC pricing?
|
||||||
|
if (OEConnection_PriceChange.treatment === "on") {
|
||||||
|
//Set the flag to include the override.
|
||||||
|
po = _.cloneDeep(partsOrder.data.parts_orders_by_pk);
|
||||||
|
po.parts_order_lines.forEach((pol) => {
|
||||||
|
pol.priceChange = true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const oecResponse = await axios.post(
|
||||||
|
"http://localhost:1337/oec/",
|
||||||
|
|
||||||
|
po || partsOrder.data.parts_orders_by_pk,
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
Authorization: `Bearer ${await auth.currentUser.getIdToken()}`,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
if (oecResponse.data && oecResponse.data.success === false) {
|
||||||
|
notification.open({
|
||||||
|
type: "error",
|
||||||
|
message: t("parts_orders.errors.oec", {
|
||||||
|
error: oecResponse.data.error,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.log("Error OEC.", error);
|
||||||
|
notification["error"]({
|
||||||
|
message: t("parts_orders.errors.oec", {
|
||||||
|
error: JSON.stringify(error.message),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
setSaving(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setSaving(false);
|
||||||
|
toggleModalVisible();
|
||||||
|
};
|
||||||
|
|
||||||
|
const initialValues = {
|
||||||
|
jobid: jobId,
|
||||||
|
return: isReturn,
|
||||||
|
deliver_by: isReturn ? dayjs(new Date()) : null,
|
||||||
|
vendorid: vendorId,
|
||||||
|
returnfrombill: returnFromBill,
|
||||||
|
|
||||||
|
parts_order_lines: {
|
||||||
|
data: linesToOrder
|
||||||
|
? linesToOrder.reduce((acc, value) => {
|
||||||
|
acc.push({
|
||||||
|
line_desc: value.line_desc,
|
||||||
|
oem_partno: value.oem_partno,
|
||||||
|
db_price: value.db_price,
|
||||||
|
act_price: value.act_price,
|
||||||
|
cost: value.cost,
|
||||||
|
quantity: value.part_qty,
|
||||||
|
job_line_id: isReturn ? value.joblineid : value.id,
|
||||||
|
part_type: value.part_type,
|
||||||
|
...(isReturn && {cm_received: false}),
|
||||||
|
});
|
||||||
|
return acc;
|
||||||
|
}, [])
|
||||||
|
: [],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (visible && !!linesToOrder) {
|
||||||
|
form.resetFields();
|
||||||
|
}
|
||||||
|
}, [visible, linesToOrder, form]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
open={visible}
|
||||||
|
title={
|
||||||
|
isReturn
|
||||||
|
? `${t("parts_orders.labels.returnpartsorder")} ${invoiceNumber}`
|
||||||
|
: t("parts_orders.labels.newpartsorder")
|
||||||
|
}
|
||||||
|
onCancel={() => toggleModalVisible()}
|
||||||
|
onOk={() => form.submit()}
|
||||||
|
okButtonProps={{loading: saving}}
|
||||||
|
cancelButtonProps={{loading: saving}}
|
||||||
|
destroyOnClose
|
||||||
|
width="75%"
|
||||||
|
forceRender
|
||||||
|
>
|
||||||
|
{error ? <AlertComponent message={error.message} type="error"/> : null}
|
||||||
|
<Form
|
||||||
|
form={form}
|
||||||
|
layout="vertical"
|
||||||
|
autoComplete="no"
|
||||||
|
onFinish={handleFinish}
|
||||||
|
initialValues={initialValues}
|
||||||
|
>
|
||||||
|
{loading ? (
|
||||||
|
<LoadingSpinner/>
|
||||||
|
) : (
|
||||||
|
<PartsOrderModalComponent
|
||||||
|
form={form}
|
||||||
|
vendorList={(data && data.vendors) || []}
|
||||||
|
sendTypeState={sendTypeState}
|
||||||
|
isReturn={isReturn}
|
||||||
|
preferredMake={data && data.jobs[0] && data.jobs[0].v_make_desc}
|
||||||
|
job={job}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</Form>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default connect(
|
export default connect(
|
||||||
mapStateToProps,
|
mapStateToProps,
|
||||||
mapDispatchToProps
|
mapDispatchToProps
|
||||||
)(PartsOrderModalContainer);
|
)(PartsOrderModalContainer);
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import { useTreatments } from "@splitsoftware/splitio-react";
|
import {useSplitTreatments} from "@splitsoftware/splitio-react";
|
||||||
import { Form, Input, Radio, Select } from "antd";
|
import {Form, Input, Radio, Select} from "antd";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import {useTranslation} from "react-i18next";
|
||||||
import { connect } from "react-redux";
|
import {connect} from "react-redux";
|
||||||
import { createStructuredSelector } from "reselect";
|
import {createStructuredSelector} from "reselect";
|
||||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
import {selectBodyshop} from "../../redux/user/user.selectors";
|
||||||
import DatePickerFormItem from "../form-date-picker/form-date-picker.component";
|
import DatePickerFormItem from "../form-date-picker/form-date-picker.component";
|
||||||
import CurrencyInput from "../form-items-formatted/currency-form-item.component";
|
import CurrencyInput from "../form-items-formatted/currency-form-item.component";
|
||||||
import JobSearchSelect from "../job-search-select/job-search-select.component";
|
import JobSearchSelect from "../job-search-select/job-search-select.component";
|
||||||
@@ -12,150 +12,149 @@ import LayoutFormRow from "../layout-form-row/layout-form-row.component";
|
|||||||
import PaymentFormTotalPayments from "./payment-form.totalpayments.component";
|
import PaymentFormTotalPayments from "./payment-form.totalpayments.component";
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
bodyshop: selectBodyshop,
|
bodyshop: selectBodyshop,
|
||||||
});
|
});
|
||||||
|
|
||||||
export function PaymentFormComponent({
|
export function PaymentFormComponent({form, bodyshop, disabled,}) {
|
||||||
form,
|
|
||||||
bodyshop,
|
|
||||||
disabled,
|
|
||||||
}) {
|
|
||||||
const { Qb_Multi_Ar } = useTreatments(
|
|
||||||
["Qb_Multi_Ar"],
|
|
||||||
{},
|
|
||||||
bodyshop && bodyshop.imexshopid
|
|
||||||
);
|
|
||||||
|
|
||||||
const { t } = useTranslation();
|
const {treatments: {Qb_Multi_Ar}} = useSplitTreatments({
|
||||||
|
attributes: {},
|
||||||
|
names: ["Qb_Multi_Ar"],
|
||||||
|
splitKey: bodyshop && bodyshop.imexshopid,
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<LayoutFormRow grow>
|
|
||||||
<Form.Item
|
|
||||||
name="jobid"
|
|
||||||
label={t("bills.fields.ro_number")}
|
|
||||||
rules={[
|
|
||||||
{
|
|
||||||
required: true,
|
|
||||||
//message: t("general.validation.required"),
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
<JobSearchSelect disabled={disabled} notExported={false} clm_no />
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
|
||||||
shouldUpdate={(prev, cur) => cur.jobid && prev.jobid !== cur.jobid}
|
|
||||||
>
|
|
||||||
{() => {
|
|
||||||
return (
|
|
||||||
<PaymentFormTotalPayments jobid={form.getFieldValue("jobid")} />
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
</Form.Item>
|
|
||||||
</LayoutFormRow>
|
|
||||||
|
|
||||||
<LayoutFormRow grow>
|
const {t} = useTranslation();
|
||||||
<Form.Item
|
|
||||||
label={t("payments.fields.amount")}
|
|
||||||
name="amount"
|
|
||||||
rules={[
|
|
||||||
{
|
|
||||||
required: true,
|
|
||||||
//message: t("general.validation.required"),
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
<CurrencyInput disabled={disabled} />
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
|
||||||
label={t("payments.fields.transactionid")}
|
|
||||||
name="transactionid"
|
|
||||||
>
|
|
||||||
<Input disabled={disabled} />
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item label={t("payments.fields.memo")} name="memo">
|
|
||||||
<Input disabled={disabled} />
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
|
||||||
label={t("payments.fields.date")}
|
|
||||||
name="date"
|
|
||||||
rules={[
|
|
||||||
{
|
|
||||||
required: true,
|
|
||||||
//message: t("general.validation.required"),
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
<DatePickerFormItem disabled={disabled} />
|
|
||||||
</Form.Item>
|
|
||||||
</LayoutFormRow>
|
|
||||||
|
|
||||||
<LayoutFormRow grow>
|
return (
|
||||||
<Form.Item
|
<div>
|
||||||
label={t("payments.fields.payer")}
|
<LayoutFormRow grow>
|
||||||
name="payer"
|
<Form.Item
|
||||||
rules={[
|
name="jobid"
|
||||||
{
|
label={t("bills.fields.ro_number")}
|
||||||
required: true,
|
rules={[
|
||||||
//message: t("general.validation.required"),
|
{
|
||||||
},
|
required: true,
|
||||||
]}
|
//message: t("general.validation.required"),
|
||||||
>
|
},
|
||||||
<Select disabled={disabled}>
|
]}
|
||||||
<Select.Option value={t("payments.labels.customer")}>
|
>
|
||||||
{t("payments.labels.customer")}
|
<JobSearchSelect disabled={disabled} notExported={false} clm_no/>
|
||||||
</Select.Option>
|
</Form.Item>
|
||||||
{Qb_Multi_Ar.treatment === "on" ? (
|
<Form.Item
|
||||||
<>
|
shouldUpdate={(prev, cur) => cur.jobid && prev.jobid !== cur.jobid}
|
||||||
<Select.OptGroup label={t("payments.labels.external")}>
|
>
|
||||||
{bodyshop.md_ins_cos.map((i, idx) => (
|
{() => {
|
||||||
<Select.Option key={idx} value={i.name}>
|
return (
|
||||||
{i.name}
|
<PaymentFormTotalPayments jobid={form.getFieldValue("jobid")}/>
|
||||||
</Select.Option>
|
);
|
||||||
))}
|
}}
|
||||||
</Select.OptGroup>
|
</Form.Item>
|
||||||
</>
|
</LayoutFormRow>
|
||||||
) : (
|
|
||||||
<Select.Option value={t("payments.labels.insurance")}>
|
|
||||||
{t("payments.labels.insurance")}
|
|
||||||
</Select.Option>
|
|
||||||
)}
|
|
||||||
</Select>
|
|
||||||
</Form.Item>
|
|
||||||
|
|
||||||
<Form.Item
|
<LayoutFormRow grow>
|
||||||
label={t("payments.fields.type")}
|
<Form.Item
|
||||||
name="type"
|
label={t("payments.fields.amount")}
|
||||||
rules={[
|
name="amount"
|
||||||
{
|
rules={[
|
||||||
required: true,
|
{
|
||||||
//message: t("general.validation.required"),
|
required: true,
|
||||||
},
|
//message: t("general.validation.required"),
|
||||||
]}
|
},
|
||||||
>
|
]}
|
||||||
<Select disabled={disabled}>
|
>
|
||||||
{bodyshop.md_payment_types.map((v, idx) => (
|
<CurrencyInput disabled={disabled}/>
|
||||||
<Select.Option key={idx} value={v}>
|
</Form.Item>
|
||||||
{v}
|
<Form.Item
|
||||||
</Select.Option>
|
label={t("payments.fields.transactionid")}
|
||||||
))}
|
name="transactionid"
|
||||||
</Select>
|
>
|
||||||
</Form.Item>
|
<Input disabled={disabled}/>
|
||||||
</LayoutFormRow>
|
</Form.Item>
|
||||||
<LayoutFormRow grow>
|
<Form.Item label={t("payments.fields.memo")} name="memo">
|
||||||
<Form.Item
|
<Input disabled={disabled}/>
|
||||||
label={t("general.labels.sendby")}
|
</Form.Item>
|
||||||
name="sendby"
|
<Form.Item
|
||||||
initialValue="none"
|
label={t("payments.fields.date")}
|
||||||
>
|
name="date"
|
||||||
<Radio.Group disabled={disabled}>
|
rules={[
|
||||||
<Radio value="none">{t("general.labels.none")}</Radio>
|
{
|
||||||
<Radio value="email">{t("general.labels.email")}</Radio>
|
required: true,
|
||||||
<Radio value="print">{t("general.labels.print")}</Radio>
|
//message: t("general.validation.required"),
|
||||||
</Radio.Group>
|
},
|
||||||
</Form.Item>
|
]}
|
||||||
</LayoutFormRow>
|
>
|
||||||
</div>
|
<DatePickerFormItem disabled={disabled}/>
|
||||||
);
|
</Form.Item>
|
||||||
|
</LayoutFormRow>
|
||||||
|
|
||||||
|
<LayoutFormRow grow>
|
||||||
|
<Form.Item
|
||||||
|
label={t("payments.fields.payer")}
|
||||||
|
name="payer"
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<Select disabled={disabled}>
|
||||||
|
<Select.Option value={t("payments.labels.customer")}>
|
||||||
|
{t("payments.labels.customer")}
|
||||||
|
</Select.Option>
|
||||||
|
{Qb_Multi_Ar.treatment === "on" ? (
|
||||||
|
<>
|
||||||
|
<Select.OptGroup label={t("payments.labels.external")}>
|
||||||
|
{bodyshop.md_ins_cos.map((i, idx) => (
|
||||||
|
<Select.Option key={idx} value={i.name}>
|
||||||
|
{i.name}
|
||||||
|
</Select.Option>
|
||||||
|
))}
|
||||||
|
</Select.OptGroup>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<Select.Option value={t("payments.labels.insurance")}>
|
||||||
|
{t("payments.labels.insurance")}
|
||||||
|
</Select.Option>
|
||||||
|
)}
|
||||||
|
</Select>
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
|
<Form.Item
|
||||||
|
label={t("payments.fields.type")}
|
||||||
|
name="type"
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<Select disabled={disabled}>
|
||||||
|
{bodyshop.md_payment_types.map((v, idx) => (
|
||||||
|
<Select.Option key={idx} value={v}>
|
||||||
|
{v}
|
||||||
|
</Select.Option>
|
||||||
|
))}
|
||||||
|
</Select>
|
||||||
|
</Form.Item>
|
||||||
|
</LayoutFormRow>
|
||||||
|
<LayoutFormRow grow>
|
||||||
|
<Form.Item
|
||||||
|
label={t("general.labels.sendby")}
|
||||||
|
name="sendby"
|
||||||
|
initialValue="none"
|
||||||
|
>
|
||||||
|
<Radio.Group disabled={disabled}>
|
||||||
|
<Radio value="none">{t("general.labels.none")}</Radio>
|
||||||
|
<Radio value="email">{t("general.labels.email")}</Radio>
|
||||||
|
<Radio value="print">{t("general.labels.print")}</Radio>
|
||||||
|
</Radio.Group>
|
||||||
|
</Form.Item>
|
||||||
|
</LayoutFormRow>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default connect(mapStateToProps, null)(PaymentFormComponent);
|
export default connect(mapStateToProps, null)(PaymentFormComponent);
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ export function ProductionlistColumnTouchTime({ bodyshop, job }) {
|
|||||||
|
|
||||||
const Difference_In_Days = dayjs().diff(
|
const Difference_In_Days = dayjs().diff(
|
||||||
dayjs(job.actual_in),
|
dayjs(job.actual_in),
|
||||||
"days",
|
"day",
|
||||||
true
|
true
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -1,319 +1,306 @@
|
|||||||
import { SyncOutlined } from "@ant-design/icons";
|
import {SyncOutlined} from "@ant-design/icons";
|
||||||
import { useTreatments } from "@splitsoftware/splitio-react";
|
import {useSplitTreatments} from "@splitsoftware/splitio-react";
|
||||||
import {
|
import {Button, Dropdown, Input, Menu, Space, Statistic, Table,} from "antd";
|
||||||
Button,
|
|
||||||
Dropdown,
|
|
||||||
Input,
|
|
||||||
Menu,
|
|
||||||
Space,
|
|
||||||
Statistic,
|
|
||||||
Table,
|
|
||||||
} from "antd";
|
|
||||||
import {PageHeader} from "@ant-design/pro-layout";
|
import {PageHeader} from "@ant-design/pro-layout";
|
||||||
import React, { useMemo, useState } from "react";
|
import React, {useMemo, useState} from "react";
|
||||||
import ReactDragListView from "react-drag-listview";
|
import ReactDragListView from "react-drag-listview";
|
||||||
import { useTranslation } from "react-i18next";
|
import {useTranslation} from "react-i18next";
|
||||||
import { connect } from "react-redux";
|
import {connect} from "react-redux";
|
||||||
import { createStructuredSelector } from "reselect";
|
import {createStructuredSelector} from "reselect";
|
||||||
import { selectTechnician } from "../../redux/tech/tech.selectors";
|
import {selectTechnician} from "../../redux/tech/tech.selectors";
|
||||||
import {
|
import {selectBodyshop, selectCurrentUser,} from "../../redux/user/user.selectors";
|
||||||
selectBodyshop,
|
|
||||||
selectCurrentUser,
|
|
||||||
} from "../../redux/user/user.selectors";
|
|
||||||
import ProductionListColumnsAdd from "../production-list-columns/production-list-columns.add.component";
|
import ProductionListColumnsAdd from "../production-list-columns/production-list-columns.add.component";
|
||||||
import ProductionListColumns from "../production-list-columns/production-list-columns.data";
|
import ProductionListColumns from "../production-list-columns/production-list-columns.data";
|
||||||
import ProductionListDetail from "../production-list-detail/production-list-detail.component";
|
import ProductionListDetail from "../production-list-detail/production-list-detail.component";
|
||||||
import ProductionListSaveConfigButton from "../production-list-save-config-button/production-list-save-config-button.component";
|
import ProductionListSaveConfigButton
|
||||||
|
from "../production-list-save-config-button/production-list-save-config-button.component";
|
||||||
import ProductionListPrint from "./production-list-print.component";
|
import ProductionListPrint from "./production-list-print.component";
|
||||||
import ProductionListTableViewSelect from "./production-list-table-view-select.component";
|
import ProductionListTableViewSelect from "./production-list-table-view-select.component";
|
||||||
import ResizeableTitle from "./production-list-table.resizeable.component";
|
import ResizeableTitle from "./production-list-table.resizeable.component";
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
bodyshop: selectBodyshop,
|
bodyshop: selectBodyshop,
|
||||||
technician: selectTechnician,
|
technician: selectTechnician,
|
||||||
currentUser: selectCurrentUser,
|
currentUser: selectCurrentUser,
|
||||||
});
|
});
|
||||||
|
|
||||||
export function ProductionListTable({
|
export function ProductionListTable({loading, data, refetch, bodyshop, technician, currentUser}) {
|
||||||
loading,
|
|
||||||
data,
|
|
||||||
refetch,
|
|
||||||
bodyshop,
|
|
||||||
technician,
|
|
||||||
currentUser,
|
|
||||||
}) {
|
|
||||||
const [searchText, setSearchText] = useState("");
|
|
||||||
const { Production_List_Status_Colors } = useTreatments(
|
|
||||||
["Production_List_Status_Colors"],
|
|
||||||
{},
|
|
||||||
bodyshop.imexshopid
|
|
||||||
);
|
|
||||||
const assoc = bodyshop.associations.find(
|
|
||||||
(a) => a.useremail === currentUser.email
|
|
||||||
);
|
|
||||||
|
|
||||||
const defaultView = assoc && assoc.default_prod_list_view;
|
const [searchText, setSearchText] = useState("");
|
||||||
|
|
||||||
const [state, setState] = useState(
|
const { treatments: {Production_List_Status_Colors} } = useSplitTreatments({
|
||||||
(bodyshop.production_config &&
|
attributes: {},
|
||||||
bodyshop.production_config.find((p) => p.name === defaultView)?.columns
|
names: ["Production_List_Status_Colors"],
|
||||||
.tableState) ||
|
splitKey: bodyshop.imexshopid,
|
||||||
bodyshop.production_config[0]?.columns.tableState || {
|
|
||||||
sortedInfo: {},
|
|
||||||
filteredInfo: { text: "" },
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
const { t } = useTranslation();
|
|
||||||
|
|
||||||
const matchingColumnConfig = useMemo(() => {
|
|
||||||
return bodyshop.production_config.find((p) => p.name === defaultView);
|
|
||||||
}, [bodyshop.production_config, defaultView]);
|
|
||||||
|
|
||||||
const [columns, setColumns] = useState(
|
|
||||||
(state &&
|
|
||||||
matchingColumnConfig &&
|
|
||||||
matchingColumnConfig.columns.columnKeys.map((k) => {
|
|
||||||
return {
|
|
||||||
...ProductionListColumns({
|
|
||||||
bodyshop,
|
|
||||||
technician,
|
|
||||||
state,
|
|
||||||
activeStatuses: bodyshop.md_ro_statuses.active_statuses,
|
|
||||||
}).find((e) => e.key === k.key),
|
|
||||||
width: k.width ?? 100,
|
|
||||||
};
|
|
||||||
})) ||
|
|
||||||
[]
|
|
||||||
);
|
|
||||||
|
|
||||||
const handleTableChange = (pagination, filters, sorter) => {
|
|
||||||
setState({
|
|
||||||
...state,
|
|
||||||
filteredInfo: filters,
|
|
||||||
sortedInfo: { columnKey: sorter.columnKey, order: sorter.order },
|
|
||||||
});
|
});
|
||||||
};
|
|
||||||
|
|
||||||
const onDragEnd = (fromIndex, toIndex) => {
|
|
||||||
const columnsCopy = columns.slice();
|
|
||||||
const item = columnsCopy.splice(fromIndex, 1)[0];
|
|
||||||
columnsCopy.splice(toIndex, 0, item);
|
|
||||||
setColumns(columnsCopy);
|
|
||||||
};
|
|
||||||
|
|
||||||
const removeColumn = (e) => {
|
const assoc = bodyshop.associations.find(
|
||||||
const { key } = e;
|
(a) => a.useremail === currentUser.email
|
||||||
setColumns(columns.filter((i) => i.key !== key));
|
);
|
||||||
};
|
|
||||||
|
|
||||||
const handleResize =
|
const defaultView = assoc && assoc.default_prod_list_view;
|
||||||
(index) =>
|
|
||||||
(e, { size }) => {
|
const [state, setState] = useState(
|
||||||
const nextColumns = [...columns];
|
(bodyshop.production_config &&
|
||||||
nextColumns[index] = {
|
bodyshop.production_config.find((p) => p.name === defaultView)?.columns
|
||||||
...nextColumns[index],
|
.tableState) ||
|
||||||
width: size.width,
|
bodyshop.production_config[0]?.columns.tableState || {
|
||||||
};
|
sortedInfo: {},
|
||||||
setColumns(nextColumns);
|
filteredInfo: {text: ""},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const {t} = useTranslation();
|
||||||
|
|
||||||
|
const matchingColumnConfig = useMemo(() => {
|
||||||
|
return bodyshop.production_config.find((p) => p.name === defaultView);
|
||||||
|
}, [bodyshop.production_config, defaultView]);
|
||||||
|
|
||||||
|
const [columns, setColumns] = useState(
|
||||||
|
(state &&
|
||||||
|
matchingColumnConfig &&
|
||||||
|
matchingColumnConfig.columns.columnKeys.map((k) => {
|
||||||
|
return {
|
||||||
|
...ProductionListColumns({
|
||||||
|
bodyshop,
|
||||||
|
technician,
|
||||||
|
state,
|
||||||
|
activeStatuses: bodyshop.md_ro_statuses.active_statuses,
|
||||||
|
}).find((e) => e.key === k.key),
|
||||||
|
width: k.width ?? 100,
|
||||||
|
};
|
||||||
|
})) ||
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleTableChange = (pagination, filters, sorter) => {
|
||||||
|
setState({
|
||||||
|
...state,
|
||||||
|
filteredInfo: filters,
|
||||||
|
sortedInfo: {columnKey: sorter.columnKey, order: sorter.order},
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const headerItem = (col) => (
|
const onDragEnd = (fromIndex, toIndex) => {
|
||||||
<Dropdown
|
const columnsCopy = columns.slice();
|
||||||
className="prod-header-dropdown"
|
const item = columnsCopy.splice(fromIndex, 1)[0];
|
||||||
overlay={
|
columnsCopy.splice(toIndex, 0, item);
|
||||||
<Menu onClick={removeColumn}>
|
setColumns(columnsCopy);
|
||||||
<Menu.Item key={col.key}>
|
};
|
||||||
{t("production.actions.removecolumn")}
|
|
||||||
</Menu.Item>
|
|
||||||
</Menu>
|
|
||||||
}
|
|
||||||
trigger={["contextMenu"]}
|
|
||||||
>
|
|
||||||
<span>{col.title}</span>
|
|
||||||
</Dropdown>
|
|
||||||
);
|
|
||||||
|
|
||||||
const dataSource =
|
const removeColumn = (e) => {
|
||||||
searchText === ""
|
const {key} = e;
|
||||||
? data
|
setColumns(columns.filter((i) => i.key !== key));
|
||||||
: data.filter(
|
};
|
||||||
(j) =>
|
|
||||||
(j.ro_number || "")
|
|
||||||
.toString()
|
|
||||||
.toLowerCase()
|
|
||||||
.includes(searchText.toLowerCase()) ||
|
|
||||||
(j.ownr_co_nm || "")
|
|
||||||
.toLowerCase()
|
|
||||||
.includes(searchText.toLowerCase()) ||
|
|
||||||
(j.ownr_fn || "")
|
|
||||||
.toLowerCase()
|
|
||||||
.includes(searchText.toLowerCase()) ||
|
|
||||||
(j.ownr_ln || "")
|
|
||||||
.toLowerCase()
|
|
||||||
.includes(searchText.toLowerCase()) ||
|
|
||||||
(j.status || "").toLowerCase().includes(searchText.toLowerCase()) ||
|
|
||||||
(j.ins_co_nm || "")
|
|
||||||
.toLowerCase()
|
|
||||||
.includes(searchText.toLowerCase()) ||
|
|
||||||
(j.clm_no || "").toLowerCase().includes(searchText.toLowerCase()) ||
|
|
||||||
(j.v_model_desc || "")
|
|
||||||
.toLowerCase()
|
|
||||||
.includes(searchText.toLowerCase()) ||
|
|
||||||
(j.v_make_desc || "")
|
|
||||||
.toLowerCase()
|
|
||||||
.includes(searchText.toLowerCase())
|
|
||||||
);
|
|
||||||
|
|
||||||
// const handleSelectRecord = (record) => {
|
const handleResize =
|
||||||
// if (selected !== record.id) {
|
(index) =>
|
||||||
// setSelected(record.id);
|
(e, {size}) => {
|
||||||
// } else {
|
const nextColumns = [...columns];
|
||||||
// setSelected(null);
|
nextColumns[index] = {
|
||||||
// }
|
...nextColumns[index],
|
||||||
// };
|
width: size.width,
|
||||||
|
};
|
||||||
if (!!!columns) return <div>No columns found.</div>;
|
setColumns(nextColumns);
|
||||||
|
|
||||||
const totalHrs = data
|
|
||||||
.reduce(
|
|
||||||
(acc, val) =>
|
|
||||||
acc +
|
|
||||||
(val.labhrs?.aggregate?.sum?.mod_lb_hrs || 0) +
|
|
||||||
(val.larhrs?.aggregate?.sum?.mod_lb_hrs || 0),
|
|
||||||
0
|
|
||||||
)
|
|
||||||
.toFixed(1);
|
|
||||||
const totalLAB = data
|
|
||||||
.reduce(
|
|
||||||
(acc, val) => acc + (val.labhrs?.aggregate?.sum?.mod_lb_hrs || 0),
|
|
||||||
0
|
|
||||||
)
|
|
||||||
.toFixed(1);
|
|
||||||
const totalLAR = data
|
|
||||||
.reduce(
|
|
||||||
(acc, val) => acc + (val.larhrs?.aggregate?.sum?.mod_lb_hrs || 0),
|
|
||||||
0
|
|
||||||
)
|
|
||||||
.toFixed(1);
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<PageHeader
|
|
||||||
title={
|
|
||||||
<Space>
|
|
||||||
<Statistic
|
|
||||||
title={t("dashboard.titles.productionhours")}
|
|
||||||
value={totalHrs}
|
|
||||||
/>
|
|
||||||
<Statistic
|
|
||||||
title={t("dashboard.titles.labhours")}
|
|
||||||
value={totalLAB}
|
|
||||||
/>
|
|
||||||
<Statistic
|
|
||||||
title={t("dashboard.titles.larhours")}
|
|
||||||
value={totalLAR}
|
|
||||||
/>
|
|
||||||
<Statistic
|
|
||||||
title={t("appointments.labels.inproduction")}
|
|
||||||
value={dataSource && dataSource.length}
|
|
||||||
/>
|
|
||||||
</Space>
|
|
||||||
}
|
|
||||||
extra={
|
|
||||||
<Space wrap>
|
|
||||||
<Button onClick={() => refetch && refetch()}>
|
|
||||||
<SyncOutlined />
|
|
||||||
</Button>
|
|
||||||
<ProductionListColumnsAdd
|
|
||||||
columnState={[columns, setColumns]}
|
|
||||||
tableState={state}
|
|
||||||
/>
|
|
||||||
<ProductionListSaveConfigButton
|
|
||||||
columns={columns}
|
|
||||||
tableState={state}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<ProductionListTableViewSelect
|
|
||||||
state={state}
|
|
||||||
setState={setState}
|
|
||||||
setColumns={setColumns}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Input
|
|
||||||
onChange={(e) => setSearchText(e.target.value)}
|
|
||||||
placeholder={t("general.labels.search")}
|
|
||||||
value={searchText}
|
|
||||||
/>
|
|
||||||
<ProductionListPrint />
|
|
||||||
</Space>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<ProductionListDetail jobs={dataSource} />
|
|
||||||
<ReactDragListView.DragColumn
|
|
||||||
onDragEnd={onDragEnd}
|
|
||||||
nodeSelector="th"
|
|
||||||
handleSelector=".prod-header-dropdown"
|
|
||||||
>
|
|
||||||
<Table
|
|
||||||
sticky
|
|
||||||
pagination={false}
|
|
||||||
size="small"
|
|
||||||
{...(Production_List_Status_Colors.treatment === "on" && {
|
|
||||||
onRow: (record, index) => {
|
|
||||||
if (!bodyshop.md_ro_statuses.production_colors) return null;
|
|
||||||
|
|
||||||
const color = bodyshop.md_ro_statuses.production_colors.find(
|
|
||||||
(x) => x.status === record.status
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!color) {
|
|
||||||
if (index % 2 === 0)
|
|
||||||
return {
|
|
||||||
style: {
|
|
||||||
backgroundColor: `rgb(236, 236, 236)`,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
className: "rowWithColor",
|
|
||||||
style: {
|
|
||||||
"--bgColor": `rgb(${color.color.r},${color.color.g},${color.color.b},${color.color.a})`,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
},
|
|
||||||
})}
|
|
||||||
components={{
|
|
||||||
header: {
|
|
||||||
cell: ResizeableTitle,
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
columns={columns.map((c, index) => {
|
|
||||||
return {
|
|
||||||
...c,
|
|
||||||
filteredValue: state.filteredInfo[c.key] || null,
|
|
||||||
sortOrder:
|
|
||||||
state.sortedInfo.columnKey === c.key && state.sortedInfo.order,
|
|
||||||
title: headerItem(c),
|
|
||||||
ellipsis: true,
|
|
||||||
width: c.width ?? 100,
|
|
||||||
onHeaderCell: (column) => ({
|
|
||||||
width: column.width,
|
|
||||||
onResize: handleResize(index),
|
|
||||||
}),
|
|
||||||
};
|
};
|
||||||
})}
|
|
||||||
rowKey="id"
|
const headerItem = (col) => (
|
||||||
loading={loading}
|
<Dropdown
|
||||||
dataSource={dataSource}
|
className="prod-header-dropdown"
|
||||||
scroll={{ x: 1000 }}
|
overlay={
|
||||||
onChange={handleTableChange}
|
<Menu onClick={removeColumn}>
|
||||||
/>
|
<Menu.Item key={col.key}>
|
||||||
</ReactDragListView.DragColumn>
|
{t("production.actions.removecolumn")}
|
||||||
</div>
|
</Menu.Item>
|
||||||
);
|
</Menu>
|
||||||
|
}
|
||||||
|
trigger={["contextMenu"]}
|
||||||
|
>
|
||||||
|
<span>{col.title}</span>
|
||||||
|
</Dropdown>
|
||||||
|
);
|
||||||
|
|
||||||
|
const dataSource =
|
||||||
|
searchText === ""
|
||||||
|
? data
|
||||||
|
: data.filter(
|
||||||
|
(j) =>
|
||||||
|
(j.ro_number || "")
|
||||||
|
.toString()
|
||||||
|
.toLowerCase()
|
||||||
|
.includes(searchText.toLowerCase()) ||
|
||||||
|
(j.ownr_co_nm || "")
|
||||||
|
.toLowerCase()
|
||||||
|
.includes(searchText.toLowerCase()) ||
|
||||||
|
(j.ownr_fn || "")
|
||||||
|
.toLowerCase()
|
||||||
|
.includes(searchText.toLowerCase()) ||
|
||||||
|
(j.ownr_ln || "")
|
||||||
|
.toLowerCase()
|
||||||
|
.includes(searchText.toLowerCase()) ||
|
||||||
|
(j.status || "").toLowerCase().includes(searchText.toLowerCase()) ||
|
||||||
|
(j.ins_co_nm || "")
|
||||||
|
.toLowerCase()
|
||||||
|
.includes(searchText.toLowerCase()) ||
|
||||||
|
(j.clm_no || "").toLowerCase().includes(searchText.toLowerCase()) ||
|
||||||
|
(j.v_model_desc || "")
|
||||||
|
.toLowerCase()
|
||||||
|
.includes(searchText.toLowerCase()) ||
|
||||||
|
(j.v_make_desc || "")
|
||||||
|
.toLowerCase()
|
||||||
|
.includes(searchText.toLowerCase())
|
||||||
|
);
|
||||||
|
|
||||||
|
// const handleSelectRecord = (record) => {
|
||||||
|
// if (selected !== record.id) {
|
||||||
|
// setSelected(record.id);
|
||||||
|
// } else {
|
||||||
|
// setSelected(null);
|
||||||
|
// }
|
||||||
|
// };
|
||||||
|
|
||||||
|
if (!!!columns) return <div>No columns found.</div>;
|
||||||
|
|
||||||
|
const totalHrs = data
|
||||||
|
.reduce(
|
||||||
|
(acc, val) =>
|
||||||
|
acc +
|
||||||
|
(val.labhrs?.aggregate?.sum?.mod_lb_hrs || 0) +
|
||||||
|
(val.larhrs?.aggregate?.sum?.mod_lb_hrs || 0),
|
||||||
|
0
|
||||||
|
)
|
||||||
|
.toFixed(1);
|
||||||
|
const totalLAB = data
|
||||||
|
.reduce(
|
||||||
|
(acc, val) => acc + (val.labhrs?.aggregate?.sum?.mod_lb_hrs || 0),
|
||||||
|
0
|
||||||
|
)
|
||||||
|
.toFixed(1);
|
||||||
|
const totalLAR = data
|
||||||
|
.reduce(
|
||||||
|
(acc, val) => acc + (val.larhrs?.aggregate?.sum?.mod_lb_hrs || 0),
|
||||||
|
0
|
||||||
|
)
|
||||||
|
.toFixed(1);
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<PageHeader
|
||||||
|
title={
|
||||||
|
<Space>
|
||||||
|
<Statistic
|
||||||
|
title={t("dashboard.titles.productionhours")}
|
||||||
|
value={totalHrs}
|
||||||
|
/>
|
||||||
|
<Statistic
|
||||||
|
title={t("dashboard.titles.labhours")}
|
||||||
|
value={totalLAB}
|
||||||
|
/>
|
||||||
|
<Statistic
|
||||||
|
title={t("dashboard.titles.larhours")}
|
||||||
|
value={totalLAR}
|
||||||
|
/>
|
||||||
|
<Statistic
|
||||||
|
title={t("appointments.labels.inproduction")}
|
||||||
|
value={dataSource && dataSource.length}
|
||||||
|
/>
|
||||||
|
</Space>
|
||||||
|
}
|
||||||
|
extra={
|
||||||
|
<Space wrap>
|
||||||
|
<Button onClick={() => refetch && refetch()}>
|
||||||
|
<SyncOutlined/>
|
||||||
|
</Button>
|
||||||
|
<ProductionListColumnsAdd
|
||||||
|
columnState={[columns, setColumns]}
|
||||||
|
tableState={state}
|
||||||
|
/>
|
||||||
|
<ProductionListSaveConfigButton
|
||||||
|
columns={columns}
|
||||||
|
tableState={state}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<ProductionListTableViewSelect
|
||||||
|
state={state}
|
||||||
|
setState={setState}
|
||||||
|
setColumns={setColumns}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Input
|
||||||
|
onChange={(e) => setSearchText(e.target.value)}
|
||||||
|
placeholder={t("general.labels.search")}
|
||||||
|
value={searchText}
|
||||||
|
/>
|
||||||
|
<ProductionListPrint/>
|
||||||
|
</Space>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<ProductionListDetail jobs={dataSource}/>
|
||||||
|
<ReactDragListView.DragColumn
|
||||||
|
onDragEnd={onDragEnd}
|
||||||
|
nodeSelector="th"
|
||||||
|
handleSelector=".prod-header-dropdown"
|
||||||
|
>
|
||||||
|
<Table
|
||||||
|
sticky
|
||||||
|
pagination={false}
|
||||||
|
size="small"
|
||||||
|
{...(Production_List_Status_Colors.treatment === "on" && {
|
||||||
|
onRow: (record, index) => {
|
||||||
|
if (!bodyshop.md_ro_statuses.production_colors) return null;
|
||||||
|
|
||||||
|
const color = bodyshop.md_ro_statuses.production_colors.find(
|
||||||
|
(x) => x.status === record.status
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!color) {
|
||||||
|
if (index % 2 === 0)
|
||||||
|
return {
|
||||||
|
style: {
|
||||||
|
backgroundColor: `rgb(236, 236, 236)`,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
className: "rowWithColor",
|
||||||
|
style: {
|
||||||
|
"--bgColor": `rgb(${color.color.r},${color.color.g},${color.color.b},${color.color.a})`,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
})}
|
||||||
|
components={{
|
||||||
|
header: {
|
||||||
|
cell: ResizeableTitle,
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
columns={columns.map((c, index) => {
|
||||||
|
return {
|
||||||
|
...c,
|
||||||
|
filteredValue: state.filteredInfo[c.key] || null,
|
||||||
|
sortOrder:
|
||||||
|
state.sortedInfo.columnKey === c.key && state.sortedInfo.order,
|
||||||
|
title: headerItem(c),
|
||||||
|
ellipsis: true,
|
||||||
|
width: c.width ?? 100,
|
||||||
|
onHeaderCell: (column) => ({
|
||||||
|
width: column.width,
|
||||||
|
onResize: handleResize(index),
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
})}
|
||||||
|
rowKey="id"
|
||||||
|
loading={loading}
|
||||||
|
dataSource={dataSource}
|
||||||
|
scroll={{x: 1000}}
|
||||||
|
onChange={handleTableChange}
|
||||||
|
/>
|
||||||
|
</ReactDragListView.DragColumn>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default connect(mapStateToProps, null)(ProductionListTable);
|
export default connect(mapStateToProps, null)(ProductionListTable);
|
||||||
|
|||||||
@@ -251,7 +251,7 @@ export function ReportCenterModalComponent({ reportCenterModal }) {
|
|||||||
>
|
>
|
||||||
<DatePicker.RangePicker
|
<DatePicker.RangePicker
|
||||||
format="MM/DD/YYYY"
|
format="MM/DD/YYYY"
|
||||||
ranges={DatePIckerRanges}
|
presets={DatePIckerRanges}
|
||||||
/>
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item style={{ margin: 0, padding: 0 }} dependencies={["key"]}>
|
<Form.Item style={{ margin: 0, padding: 0 }} dependencies={["key"]}>
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ export function ScheduleJobModalComponent({
|
|||||||
form.setFieldsValue({
|
form.setFieldsValue({
|
||||||
scheduled_completion: dayjs(values.start).businessAdd(
|
scheduled_completion: dayjs(values.start).businessAdd(
|
||||||
totalHours / bodyshop.target_touchtime,
|
totalHours / bodyshop.target_touchtime,
|
||||||
"days"
|
"day"
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -138,7 +138,7 @@ export function ScheduleJobModalComponent({
|
|||||||
if (ssDate.isBefore(dayjs())) {
|
if (ssDate.isBefore(dayjs())) {
|
||||||
form.setFieldsValue({ start: dayjs() });
|
form.setFieldsValue({ start: dayjs() });
|
||||||
} else {
|
} else {
|
||||||
form.setFieldsValue({ start: dayjs(d).add(8, "hours") });
|
form.setFieldsValue({ start: dayjs(d).add(8, "hour") });
|
||||||
}
|
}
|
||||||
handleDateBlur();
|
handleDateBlur();
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -61,9 +61,9 @@ export const ListOfDaysInCurrentMonth = () => {
|
|||||||
const days = [];
|
const days = [];
|
||||||
const dateStart = dayjs().startOf("month");
|
const dateStart = dayjs().startOf("month");
|
||||||
const dateEnd = dayjs().endOf("month");
|
const dateEnd = dayjs().endOf("month");
|
||||||
while (dateEnd.diff(dateStart, "days") > 0) {
|
while (dateEnd.diff(dateStart, "day") > 0) {
|
||||||
days.push(dateStart.format("YYYY-MM-DD"));
|
days.push(dateStart.format("YYYY-MM-DD"));
|
||||||
dateStart.add(1, "days");
|
dateStart.add(1, "day");
|
||||||
}
|
}
|
||||||
days.push(dateEnd.format("YYYY-MM-DD"));
|
days.push(dateEnd.format("YYYY-MM-DD"));
|
||||||
return days;
|
return days;
|
||||||
@@ -73,9 +73,9 @@ export const ListDaysBetween = ({ start, end }) => {
|
|||||||
const days = [];
|
const days = [];
|
||||||
const dateStart = dayjs(start);
|
const dateStart = dayjs(start);
|
||||||
const dateEnd = dayjs(end);
|
const dateEnd = dayjs(end);
|
||||||
while (dateEnd.diff(dateStart, "days") > 0) {
|
while (dateEnd.diff(dateStart, "day") > 0) {
|
||||||
days.push(dateStart.format("YYYY-MM-DD"));
|
days.push(dateStart.format("YYYY-MM-DD"));
|
||||||
dateStart.add(1, "days");
|
dateStart.add(1, "day");
|
||||||
}
|
}
|
||||||
days.push(dateEnd.format("YYYY-MM-DD"));
|
days.push(dateEnd.format("YYYY-MM-DD"));
|
||||||
return days;
|
return days;
|
||||||
|
|||||||
@@ -132,7 +132,7 @@ export function ShopEmployeesFormComponent({ bodyshop }) {
|
|||||||
dataIndex: "length",
|
dataIndex: "length",
|
||||||
key: "length",
|
key: "length",
|
||||||
render: (text, record) =>
|
render: (text, record) =>
|
||||||
dayjs(record.end).diff(dayjs(record.start), "days", true).toFixed(1),
|
dayjs(record.end).diff(dayjs(record.start), "day", true).toFixed(1),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t("general.labels.actions"),
|
title: t("general.labels.actions"),
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import { useTreatments } from "@splitsoftware/splitio-react";
|
import {useSplitTreatments} from "@splitsoftware/splitio-react";
|
||||||
import { Button, Card, Tabs } from "antd";
|
import {Button, Card, Tabs} from "antd";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import {useTranslation} from "react-i18next";
|
||||||
import { connect } from "react-redux";
|
import {connect} from "react-redux";
|
||||||
import { createStructuredSelector } from "reselect";
|
import {createStructuredSelector} from "reselect";
|
||||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
import {selectBodyshop} from "../../redux/user/user.selectors";
|
||||||
import ShopInfoGeneral from "./shop-info.general.component";
|
import ShopInfoGeneral from "./shop-info.general.component";
|
||||||
import ShopInfoIntakeChecklistComponent from "./shop-info.intake.component";
|
import ShopInfoIntakeChecklistComponent from "./shop-info.intake.component";
|
||||||
import ShopInfoLaborRates from "./shop-info.laborrates.component";
|
import ShopInfoLaborRates from "./shop-info.laborrates.component";
|
||||||
@@ -15,88 +15,90 @@ import ShopInfoResponsibilityCenterComponent from "./shop-info.responsibilitycen
|
|||||||
import ShopInfoROStatusComponent from "./shop-info.rostatus.component";
|
import ShopInfoROStatusComponent from "./shop-info.rostatus.component";
|
||||||
import ShopInfoSchedulingComponent from "./shop-info.scheduling.component";
|
import ShopInfoSchedulingComponent from "./shop-info.scheduling.component";
|
||||||
import ShopInfoSpeedPrint from "./shop-info.speedprint.component";
|
import ShopInfoSpeedPrint from "./shop-info.speedprint.component";
|
||||||
import { useNavigate, useLocation } from "react-router-dom";
|
import {useLocation, useNavigate} from "react-router-dom";
|
||||||
import queryString from "query-string";
|
import queryString from "query-string";
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
bodyshop: selectBodyshop,
|
bodyshop: selectBodyshop,
|
||||||
});
|
});
|
||||||
const mapDispatchToProps = (dispatch) => ({
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
||||||
});
|
});
|
||||||
export default connect(mapStateToProps, mapDispatchToProps)(ShopInfoComponent);
|
export default connect(mapStateToProps, mapDispatchToProps)(ShopInfoComponent);
|
||||||
|
|
||||||
export function ShopInfoComponent({ bodyshop, form, saveLoading }) {
|
export function ShopInfoComponent({bodyshop, form, saveLoading}) {
|
||||||
const { CriticalPartsScanning } = useTreatments(
|
|
||||||
["CriticalPartsScanning"],
|
|
||||||
{},
|
|
||||||
bodyshop.imexshopid
|
|
||||||
);
|
|
||||||
const { t } = useTranslation();
|
|
||||||
const history = useNavigate();
|
|
||||||
const location = useLocation();
|
|
||||||
const search = queryString.parse(location.search);
|
|
||||||
|
|
||||||
return (
|
const { treatments: {CriticalPartsScanning} } = useSplitTreatments({
|
||||||
<Card
|
attributes: {},
|
||||||
extra={
|
names: ["CriticalPartsScanning"],
|
||||||
<Button
|
splitKey: bodyshop.imexshopid,
|
||||||
type="primary"
|
});
|
||||||
loading={saveLoading}
|
|
||||||
onClick={() => form.submit()}
|
|
||||||
>
|
|
||||||
{t("general.actions.save")}
|
|
||||||
</Button>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<Tabs
|
|
||||||
defaultActiveKey={search.subtab}
|
|
||||||
onChange={(key) =>
|
|
||||||
history({
|
|
||||||
search: `?tab=${search.tab}&subtab=${key}`,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<Tabs.TabPane key="general" tab={t("bodyshop.labels.shopinfo")}>
|
|
||||||
<ShopInfoGeneral form={form} />
|
|
||||||
</Tabs.TabPane>
|
|
||||||
<Tabs.TabPane key="speedprint" tab={t("bodyshop.labels.speedprint")}>
|
|
||||||
<ShopInfoSpeedPrint form={form} />
|
|
||||||
</Tabs.TabPane>
|
|
||||||
<Tabs.TabPane key="rbac" tab={t("bodyshop.labels.rbac")}>
|
|
||||||
<ShopInfoRbacComponent form={form} />
|
|
||||||
</Tabs.TabPane>
|
|
||||||
<Tabs.TabPane key="roStatus" tab={t("bodyshop.labels.jobstatuses")}>
|
|
||||||
<ShopInfoROStatusComponent form={form} />
|
|
||||||
</Tabs.TabPane>
|
|
||||||
<Tabs.TabPane key="scheduling" tab={t("bodyshop.labels.scheduling")}>
|
|
||||||
<ShopInfoSchedulingComponent form={form} />
|
|
||||||
</Tabs.TabPane>
|
|
||||||
<Tabs.TabPane
|
|
||||||
key="orderStatus"
|
|
||||||
tab={t("bodyshop.labels.orderstatuses")}
|
|
||||||
>
|
|
||||||
<ShopInfoOrderStatusComponent form={form} />
|
|
||||||
</Tabs.TabPane>
|
|
||||||
<Tabs.TabPane
|
|
||||||
key="responsibilityCenters"
|
|
||||||
tab={t("bodyshop.labels.responsibilitycenters.title")}
|
|
||||||
>
|
|
||||||
<ShopInfoResponsibilityCenterComponent form={form} />
|
|
||||||
</Tabs.TabPane>
|
|
||||||
|
|
||||||
<Tabs.TabPane key="checklists" tab={t("bodyshop.labels.checklists")}>
|
const {t} = useTranslation();
|
||||||
<ShopInfoIntakeChecklistComponent form={form} />
|
const history = useNavigate();
|
||||||
</Tabs.TabPane>
|
const location = useLocation();
|
||||||
<Tabs.TabPane key="laborrates" tab={t("bodyshop.labels.laborrates")}>
|
const search = queryString.parse(location.search);
|
||||||
<ShopInfoLaborRates form={form} />
|
|
||||||
</Tabs.TabPane>
|
return (
|
||||||
{CriticalPartsScanning.treatment === "on" && (
|
<Card
|
||||||
<Tabs.TabPane key="partsscan" tab={t("bodyshop.labels.partsscan")}>
|
extra={
|
||||||
<ShopInfoPartsScan form={form} />
|
<Button
|
||||||
</Tabs.TabPane>
|
type="primary"
|
||||||
)}
|
loading={saveLoading}
|
||||||
</Tabs>
|
onClick={() => form.submit()}
|
||||||
</Card>
|
>
|
||||||
);
|
{t("general.actions.save")}
|
||||||
|
</Button>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Tabs
|
||||||
|
defaultActiveKey={search.subtab}
|
||||||
|
onChange={(key) =>
|
||||||
|
history({
|
||||||
|
search: `?tab=${search.tab}&subtab=${key}`,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Tabs.TabPane key="general" tab={t("bodyshop.labels.shopinfo")}>
|
||||||
|
<ShopInfoGeneral form={form}/>
|
||||||
|
</Tabs.TabPane>
|
||||||
|
<Tabs.TabPane key="speedprint" tab={t("bodyshop.labels.speedprint")}>
|
||||||
|
<ShopInfoSpeedPrint form={form}/>
|
||||||
|
</Tabs.TabPane>
|
||||||
|
<Tabs.TabPane key="rbac" tab={t("bodyshop.labels.rbac")}>
|
||||||
|
<ShopInfoRbacComponent form={form}/>
|
||||||
|
</Tabs.TabPane>
|
||||||
|
<Tabs.TabPane key="roStatus" tab={t("bodyshop.labels.jobstatuses")}>
|
||||||
|
<ShopInfoROStatusComponent form={form}/>
|
||||||
|
</Tabs.TabPane>
|
||||||
|
<Tabs.TabPane key="scheduling" tab={t("bodyshop.labels.scheduling")}>
|
||||||
|
<ShopInfoSchedulingComponent form={form}/>
|
||||||
|
</Tabs.TabPane>
|
||||||
|
<Tabs.TabPane
|
||||||
|
key="orderStatus"
|
||||||
|
tab={t("bodyshop.labels.orderstatuses")}
|
||||||
|
>
|
||||||
|
<ShopInfoOrderStatusComponent form={form}/>
|
||||||
|
</Tabs.TabPane>
|
||||||
|
<Tabs.TabPane
|
||||||
|
key="responsibilityCenters"
|
||||||
|
tab={t("bodyshop.labels.responsibilitycenters.title")}
|
||||||
|
>
|
||||||
|
<ShopInfoResponsibilityCenterComponent form={form}/>
|
||||||
|
</Tabs.TabPane>
|
||||||
|
|
||||||
|
<Tabs.TabPane key="checklists" tab={t("bodyshop.labels.checklists")}>
|
||||||
|
<ShopInfoIntakeChecklistComponent form={form}/>
|
||||||
|
</Tabs.TabPane>
|
||||||
|
<Tabs.TabPane key="laborrates" tab={t("bodyshop.labels.laborrates")}>
|
||||||
|
<ShopInfoLaborRates form={form}/>
|
||||||
|
</Tabs.TabPane>
|
||||||
|
{CriticalPartsScanning.treatment === "on" && (
|
||||||
|
<Tabs.TabPane key="partsscan" tab={t("bodyshop.labels.partsscan")}>
|
||||||
|
<ShopInfoPartsScan form={form}/>
|
||||||
|
</Tabs.TabPane>
|
||||||
|
)}
|
||||||
|
</Tabs>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { DeleteFilled } from "@ant-design/icons";
|
import { DeleteFilled } from "@ant-design/icons";
|
||||||
import { useTreatments } from "@splitsoftware/splitio-react";
|
import { useSplitTreatments } from "@splitsoftware/splitio-react";
|
||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
DatePicker,
|
DatePicker,
|
||||||
@@ -35,13 +35,14 @@ const mapDispatchToProps = (dispatch) => ({
|
|||||||
export default connect(mapStateToProps, mapDispatchToProps)(ShopInfoGeneral);
|
export default connect(mapStateToProps, mapDispatchToProps)(ShopInfoGeneral);
|
||||||
|
|
||||||
export function ShopInfoGeneral({ form, bodyshop }) {
|
export function ShopInfoGeneral({ form, bodyshop }) {
|
||||||
const { t } = useTranslation();
|
|
||||||
const { ClosingPeriod } = useTreatments(
|
|
||||||
["ClosingPeriod"],
|
|
||||||
{},
|
|
||||||
bodyshop && bodyshop.imexshopid
|
|
||||||
);
|
|
||||||
|
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
const { treatments: {ClosingPeriod} } = useSplitTreatments({
|
||||||
|
attributes: {},
|
||||||
|
names: ["ClosingPeriod"],
|
||||||
|
splitKey: bodyshop && bodyshop.imexshopid,
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
@@ -413,13 +414,12 @@ export function ShopInfoGeneral({ form, bodyshop }) {
|
|||||||
{ClosingPeriod.treatment === "on" && (
|
{ClosingPeriod.treatment === "on" && (
|
||||||
<>
|
<>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
allowClear
|
|
||||||
name={["accountingconfig", "ClosingPeriod"]}
|
name={["accountingconfig", "ClosingPeriod"]}
|
||||||
label={t("bodyshop.fields.closingperiod")} //{t("reportcenter.labels.dates")}
|
label={t("bodyshop.fields.closingperiod")} //{t("reportcenter.labels.dates")}
|
||||||
>
|
>
|
||||||
<DatePicker.RangePicker
|
<DatePicker.RangePicker
|
||||||
format="MM/DD/YYYY"
|
format="MM/DD/YYYY"
|
||||||
ranges={DatePickerRanges}
|
presets={DatePickerRanges}
|
||||||
/>
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -1,94 +1,97 @@
|
|||||||
import { Form, Input } from "antd";
|
import {Form, Input} from "antd";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import {useTranslation} from "react-i18next";
|
||||||
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
|
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
|
||||||
import { connect } from "react-redux";
|
import {connect} from "react-redux";
|
||||||
import { createStructuredSelector } from "reselect";
|
import {createStructuredSelector} from "reselect";
|
||||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
import {selectBodyshop} from "../../redux/user/user.selectors";
|
||||||
import { useTreatments } from "@splitsoftware/splitio-react";
|
import {useSplitTreatments} from "@splitsoftware/splitio-react";
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
bodyshop: selectBodyshop,
|
bodyshop: selectBodyshop,
|
||||||
});
|
});
|
||||||
const mapDispatchToProps = (dispatch) => ({
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
||||||
});
|
});
|
||||||
export default connect(
|
export default connect(
|
||||||
mapStateToProps,
|
mapStateToProps,
|
||||||
mapDispatchToProps
|
mapDispatchToProps
|
||||||
)(ShopInfoOrderStatusComponent);
|
)(ShopInfoOrderStatusComponent);
|
||||||
|
|
||||||
export function ShopInfoOrderStatusComponent({ bodyshop, form }) {
|
export function ShopInfoOrderStatusComponent({bodyshop, form}) {
|
||||||
const { t } = useTranslation();
|
const {t} = useTranslation();
|
||||||
const { OEConnection } = useTreatments(
|
|
||||||
["OEConnection"],
|
|
||||||
{},
|
|
||||||
bodyshop.imexshopid
|
|
||||||
);
|
|
||||||
return (
|
|
||||||
<LayoutFormRow header={t("bodyshop.labels.orderstatuses")}>
|
|
||||||
<Form.Item
|
|
||||||
label={t("bodyshop.fields.statuses.default_bo")}
|
|
||||||
rules={[
|
|
||||||
{
|
|
||||||
required: true,
|
|
||||||
//message: t("general.validation.required"),
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
name={["md_order_statuses", "default_bo"]}
|
|
||||||
>
|
|
||||||
<Input />
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
|
||||||
label={t("bodyshop.fields.statuses.default_ordered")}
|
|
||||||
rules={[
|
|
||||||
{
|
|
||||||
required: true,
|
|
||||||
//message: t("general.validation.required"),
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
name={["md_order_statuses", "default_ordered"]}
|
|
||||||
>
|
|
||||||
<Input />
|
|
||||||
</Form.Item>
|
|
||||||
|
|
||||||
<Form.Item
|
const {treatments: {OEConnection}} = useSplitTreatments({
|
||||||
label={t("bodyshop.fields.statuses.default_received")}
|
attributes: {},
|
||||||
rules={[
|
names: ["OEConnection"],
|
||||||
{
|
splitKey: bodyshop.imexshopid,
|
||||||
required: true,
|
});
|
||||||
//message: t("general.validation.required"),
|
|
||||||
},
|
return (
|
||||||
]}
|
<LayoutFormRow header={t("bodyshop.labels.orderstatuses")}>
|
||||||
name={["md_order_statuses", "default_received"]}
|
<Form.Item
|
||||||
>
|
label={t("bodyshop.fields.statuses.default_bo")}
|
||||||
<Input />
|
rules={[
|
||||||
</Form.Item>
|
{
|
||||||
<Form.Item
|
required: true,
|
||||||
label={t("bodyshop.fields.statuses.default_returned")}
|
//message: t("general.validation.required"),
|
||||||
rules={[
|
},
|
||||||
{
|
]}
|
||||||
required: true,
|
name={["md_order_statuses", "default_bo"]}
|
||||||
//message: t("general.validation.required"),
|
>
|
||||||
},
|
<Input/>
|
||||||
]}
|
</Form.Item>
|
||||||
name={["md_order_statuses", "default_returned"]}
|
<Form.Item
|
||||||
>
|
label={t("bodyshop.fields.statuses.default_ordered")}
|
||||||
<Input />
|
rules={[
|
||||||
</Form.Item>
|
{
|
||||||
{OEConnection.treatment === "on" && (
|
required: true,
|
||||||
<Form.Item
|
//message: t("general.validation.required"),
|
||||||
label={t("bodyshop.fields.statuses.default_quote")}
|
},
|
||||||
rules={[
|
]}
|
||||||
{
|
name={["md_order_statuses", "default_ordered"]}
|
||||||
required: true,
|
>
|
||||||
//message: t("general.validation.required"),
|
<Input/>
|
||||||
},
|
</Form.Item>
|
||||||
]}
|
|
||||||
name={["md_order_statuses", "default_quote"]}
|
<Form.Item
|
||||||
>
|
label={t("bodyshop.fields.statuses.default_received")}
|
||||||
<Input />
|
rules={[
|
||||||
</Form.Item>
|
{
|
||||||
)}
|
required: true,
|
||||||
</LayoutFormRow>
|
//message: t("general.validation.required"),
|
||||||
);
|
},
|
||||||
|
]}
|
||||||
|
name={["md_order_statuses", "default_received"]}
|
||||||
|
>
|
||||||
|
<Input/>
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("bodyshop.fields.statuses.default_returned")}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
name={["md_order_statuses", "default_returned"]}
|
||||||
|
>
|
||||||
|
<Input/>
|
||||||
|
</Form.Item>
|
||||||
|
{OEConnection.treatment === "on" && (
|
||||||
|
<Form.Item
|
||||||
|
label={t("bodyshop.fields.statuses.default_quote")}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
name={["md_order_statuses", "default_quote"]}
|
||||||
|
>
|
||||||
|
<Input/>
|
||||||
|
</Form.Item>
|
||||||
|
)}
|
||||||
|
</LayoutFormRow>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,411 +1,415 @@
|
|||||||
import { DeleteFilled } from "@ant-design/icons";
|
import {DeleteFilled} from "@ant-design/icons";
|
||||||
import { useTreatments } from "@splitsoftware/splitio-react";
|
import {Button, Form, Select, Space} from "antd";
|
||||||
import { Button, Form, Select, Space } from "antd";
|
import React, {useState} from "react";
|
||||||
import React, { useState } from "react";
|
import {ChromePicker} from "react-color";
|
||||||
import { ChromePicker } from "react-color";
|
import {useTranslation} from "react-i18next";
|
||||||
import { useTranslation } from "react-i18next";
|
|
||||||
import styled from "styled-components";
|
import styled from "styled-components";
|
||||||
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
|
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
|
||||||
|
|
||||||
import { connect } from "react-redux";
|
import {connect} from "react-redux";
|
||||||
import { createStructuredSelector } from "reselect";
|
import {createStructuredSelector} from "reselect";
|
||||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
import {selectBodyshop} from "../../redux/user/user.selectors";
|
||||||
|
import {useSplitTreatments} from "@splitsoftware/splitio-react";
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
bodyshop: selectBodyshop,
|
bodyshop: selectBodyshop,
|
||||||
});
|
});
|
||||||
|
|
||||||
const mapDispatchToProps = (dispatch) => ({
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
||||||
});
|
});
|
||||||
export default connect(
|
export default connect(
|
||||||
mapStateToProps,
|
mapStateToProps,
|
||||||
mapDispatchToProps
|
mapDispatchToProps
|
||||||
)(ShopInfoROStatusComponent);
|
)(ShopInfoROStatusComponent);
|
||||||
|
|
||||||
const SelectorDiv = styled.div`
|
const SelectorDiv = styled.div`
|
||||||
.ant-form-item .ant-select {
|
.ant-form-item .ant-select {
|
||||||
width: 200px;
|
width: 200px;
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
export function ShopInfoROStatusComponent({ bodyshop, form }) {
|
|
||||||
const { t } = useTranslation();
|
|
||||||
const { Production_List_Status_Colors } = useTreatments(
|
|
||||||
["Production_List_Status_Colors"],
|
|
||||||
{},
|
|
||||||
bodyshop.imexshopid
|
|
||||||
);
|
|
||||||
|
|
||||||
const [options, setOptions] = useState(
|
export function ShopInfoROStatusComponent({bodyshop, form}) {
|
||||||
form.getFieldValue(["md_ro_statuses", "statuses"]) || []
|
|
||||||
);
|
|
||||||
|
|
||||||
const handleBlur = () => {
|
const {t} = useTranslation();
|
||||||
setOptions(form.getFieldValue(["md_ro_statuses", "statuses"]));
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
const { treatments: {Production_List_Status_Colors} } = useSplitTreatments({
|
||||||
<SelectorDiv id="jobstatus">
|
attributes: {},
|
||||||
<Form.Item
|
names: ["Production_List_Status_Colors"],
|
||||||
name={["md_ro_statuses", "statuses"]}
|
splitKey: bodyshop.imexshopid,
|
||||||
label={t("bodyshop.labels.alljobstatuses")}
|
});
|
||||||
rules={[
|
|
||||||
{
|
const [options, setOptions] = useState(
|
||||||
required: true,
|
form.getFieldValue(["md_ro_statuses", "statuses"]) || []
|
||||||
//message: t("general.validation.required"),
|
);
|
||||||
type: "array",
|
|
||||||
},
|
const handleBlur = () => {
|
||||||
]}
|
setOptions(form.getFieldValue(["md_ro_statuses", "statuses"]));
|
||||||
>
|
};
|
||||||
<Select mode="tags" onBlur={handleBlur} />
|
|
||||||
</Form.Item>
|
return (
|
||||||
<Form.Item
|
<SelectorDiv id="jobstatus">
|
||||||
name={["md_ro_statuses", "active_statuses"]}
|
<Form.Item
|
||||||
label={t("bodyshop.fields.statuses.active_statuses")}
|
name={["md_ro_statuses", "statuses"]}
|
||||||
rules={[
|
label={t("bodyshop.labels.alljobstatuses")}
|
||||||
{
|
rules={[
|
||||||
required: true,
|
{
|
||||||
//message: t("general.validation.required"),
|
required: true,
|
||||||
type: "array",
|
//message: t("general.validation.required"),
|
||||||
},
|
type: "array",
|
||||||
]}
|
},
|
||||||
>
|
]}
|
||||||
<Select mode="multiple">
|
>
|
||||||
{options.map((item, idx) => (
|
<Select mode="tags" onBlur={handleBlur}/>
|
||||||
<Select.Option key={idx} value={item}>
|
</Form.Item>
|
||||||
{item}
|
<Form.Item
|
||||||
</Select.Option>
|
name={["md_ro_statuses", "active_statuses"]}
|
||||||
))}
|
label={t("bodyshop.fields.statuses.active_statuses")}
|
||||||
</Select>
|
rules={[
|
||||||
</Form.Item>
|
{
|
||||||
<Form.Item
|
required: true,
|
||||||
name={["md_ro_statuses", "pre_production_statuses"]}
|
//message: t("general.validation.required"),
|
||||||
label={t("bodyshop.fields.statuses.pre_production_statuses")}
|
type: "array",
|
||||||
rules={[
|
},
|
||||||
{
|
]}
|
||||||
required: true,
|
>
|
||||||
//message: t("general.validation.required"),
|
<Select mode="multiple">
|
||||||
type: "array",
|
{options.map((item, idx) => (
|
||||||
},
|
<Select.Option key={idx} value={item}>
|
||||||
]}
|
{item}
|
||||||
>
|
</Select.Option>
|
||||||
<Select mode="multiple">
|
|
||||||
{options.map((item, idx) => (
|
|
||||||
<Select.Option key={idx} value={item}>
|
|
||||||
{item}
|
|
||||||
</Select.Option>
|
|
||||||
))}
|
|
||||||
</Select>
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
|
||||||
name={["md_ro_statuses", "production_statuses"]}
|
|
||||||
label={t("bodyshop.fields.statuses.production_statuses")}
|
|
||||||
rules={[
|
|
||||||
{
|
|
||||||
required: true,
|
|
||||||
//message: t("general.validation.required"),
|
|
||||||
type: "array",
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
<Select mode="multiple">
|
|
||||||
{options.map((item, idx) => (
|
|
||||||
<Select.Option key={idx} value={item}>
|
|
||||||
{item}
|
|
||||||
</Select.Option>
|
|
||||||
))}
|
|
||||||
</Select>
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
|
||||||
name={["md_ro_statuses", "post_production_statuses"]}
|
|
||||||
label={t("bodyshop.fields.statuses.post_production_statuses")}
|
|
||||||
rules={[
|
|
||||||
{
|
|
||||||
required: true,
|
|
||||||
//message: t("general.validation.required"),
|
|
||||||
type: "array",
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
<Select mode="multiple">
|
|
||||||
{options.map((item, idx) => (
|
|
||||||
<Select.Option key={idx} value={item}>
|
|
||||||
{item}
|
|
||||||
</Select.Option>
|
|
||||||
))}
|
|
||||||
</Select>
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
|
||||||
name={["md_ro_statuses", "ready_statuses"]}
|
|
||||||
label={t("bodyshop.fields.statuses.ready_statuses")}
|
|
||||||
rules={[
|
|
||||||
{
|
|
||||||
//required: true,
|
|
||||||
//message: t("general.validation.required"),
|
|
||||||
type: "array",
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
<Select mode="multiple">
|
|
||||||
{options.map((item, idx) => (
|
|
||||||
<Select.Option key={idx} value={item}>
|
|
||||||
{item}
|
|
||||||
</Select.Option>
|
|
||||||
))}
|
|
||||||
</Select>
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
|
||||||
name={["md_ro_statuses", "additional_board_statuses"]}
|
|
||||||
label={t("bodyshop.fields.statuses.additional_board_statuses")}
|
|
||||||
rules={[
|
|
||||||
{
|
|
||||||
//required: true,
|
|
||||||
//message: t("general.validation.required"),
|
|
||||||
type: "array",
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
<Select mode="multiple">
|
|
||||||
{options.map((item, idx) => (
|
|
||||||
<Select.Option key={idx} value={item}>
|
|
||||||
{item}
|
|
||||||
</Select.Option>
|
|
||||||
))}
|
|
||||||
</Select>
|
|
||||||
</Form.Item>
|
|
||||||
<LayoutFormRow noDivider>
|
|
||||||
<Form.Item
|
|
||||||
label={t("bodyshop.fields.statuses.default_scheduled")}
|
|
||||||
rules={[
|
|
||||||
{
|
|
||||||
required: true,
|
|
||||||
//message: t("general.validation.required"),
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
name={["md_ro_statuses", "default_scheduled"]}
|
|
||||||
>
|
|
||||||
<Select>
|
|
||||||
{options.map((item, idx) => (
|
|
||||||
<Select.Option key={idx} value={item}>
|
|
||||||
{item}
|
|
||||||
</Select.Option>
|
|
||||||
))}
|
|
||||||
</Select>
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
|
||||||
label={t("bodyshop.fields.statuses.default_arrived")}
|
|
||||||
rules={[
|
|
||||||
{
|
|
||||||
required: true,
|
|
||||||
//message: t("general.validation.required"),
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
name={["md_ro_statuses", "default_arrived"]}
|
|
||||||
>
|
|
||||||
<Select>
|
|
||||||
{options.map((item, idx) => (
|
|
||||||
<Select.Option key={idx} value={item}>
|
|
||||||
{item}
|
|
||||||
</Select.Option>
|
|
||||||
))}
|
|
||||||
</Select>
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
|
||||||
label={t("bodyshop.fields.statuses.default_exported")}
|
|
||||||
rules={[
|
|
||||||
{
|
|
||||||
required: true,
|
|
||||||
//message: t("general.validation.required"),
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
name={["md_ro_statuses", "default_exported"]}
|
|
||||||
>
|
|
||||||
<Select>
|
|
||||||
{options.map((item, idx) => (
|
|
||||||
<Select.Option key={idx} value={item}>
|
|
||||||
{item}
|
|
||||||
</Select.Option>
|
|
||||||
))}
|
|
||||||
</Select>
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
|
||||||
label={t("bodyshop.fields.statuses.default_imported")}
|
|
||||||
rules={[
|
|
||||||
{
|
|
||||||
required: true,
|
|
||||||
//message: t("general.validation.required"),
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
name={["md_ro_statuses", "default_imported"]}
|
|
||||||
>
|
|
||||||
<Select>
|
|
||||||
{options.map((item, idx) => (
|
|
||||||
<Select.Option key={idx} value={item}>
|
|
||||||
{item}
|
|
||||||
</Select.Option>
|
|
||||||
))}
|
|
||||||
</Select>
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
|
||||||
label={t("bodyshop.fields.statuses.default_invoiced")}
|
|
||||||
rules={[
|
|
||||||
{
|
|
||||||
required: true,
|
|
||||||
//message: t("general.validation.required"),
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
name={["md_ro_statuses", "default_invoiced"]}
|
|
||||||
>
|
|
||||||
<Select>
|
|
||||||
{options.map((item, idx) => (
|
|
||||||
<Select.Option key={idx} value={item}>
|
|
||||||
{item}
|
|
||||||
</Select.Option>
|
|
||||||
))}
|
|
||||||
</Select>
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
|
||||||
label={t("bodyshop.fields.statuses.default_completed")}
|
|
||||||
rules={[
|
|
||||||
{
|
|
||||||
required: true,
|
|
||||||
//message: t("general.validation.required"),
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
name={["md_ro_statuses", "default_completed"]}
|
|
||||||
>
|
|
||||||
<Select>
|
|
||||||
{options.map((item, idx) => (
|
|
||||||
<Select.Option key={idx} value={item}>
|
|
||||||
{item}
|
|
||||||
</Select.Option>
|
|
||||||
))}
|
|
||||||
</Select>
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
|
||||||
label={t("bodyshop.fields.statuses.default_delivered")}
|
|
||||||
rules={[
|
|
||||||
{
|
|
||||||
required: true,
|
|
||||||
//message: t("general.validation.required"),
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
name={["md_ro_statuses", "default_delivered"]}
|
|
||||||
>
|
|
||||||
<Select>
|
|
||||||
{options.map((item, idx) => (
|
|
||||||
<Select.Option key={idx} value={item}>
|
|
||||||
{item}
|
|
||||||
</Select.Option>
|
|
||||||
))}
|
|
||||||
</Select>
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
|
||||||
label={t("bodyshop.fields.statuses.default_void")}
|
|
||||||
rules={[
|
|
||||||
{
|
|
||||||
required: true,
|
|
||||||
//message: t("general.validation.required"),
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
name={["md_ro_statuses", "default_void"]}
|
|
||||||
>
|
|
||||||
<Select>
|
|
||||||
{options.map((item, idx) => (
|
|
||||||
<Select.Option key={idx} value={item}>
|
|
||||||
{item}
|
|
||||||
</Select.Option>
|
|
||||||
))}
|
|
||||||
</Select>
|
|
||||||
</Form.Item>
|
|
||||||
</LayoutFormRow>
|
|
||||||
{Production_List_Status_Colors.treatment === "on" && (
|
|
||||||
<LayoutFormRow
|
|
||||||
grow
|
|
||||||
header={t("bodyshop.fields.statuses.production_colors")}
|
|
||||||
id="production_colors"
|
|
||||||
>
|
|
||||||
<Form.List name={["md_ro_statuses", "production_colors"]}>
|
|
||||||
{(fields, { add, remove, move }) => {
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<LayoutFormRow>
|
|
||||||
{fields.map((field, index) => (
|
|
||||||
<Form.Item key={field.key}>
|
|
||||||
<Space direction="vertical">
|
|
||||||
<div style={{ display: "flex" }}>
|
|
||||||
<Form.Item
|
|
||||||
style={{ flex: 1 }}
|
|
||||||
label={t("jobs.fields.status")}
|
|
||||||
key={`${index}status`}
|
|
||||||
name={[field.name, "status"]}
|
|
||||||
rules={[
|
|
||||||
{
|
|
||||||
required: true,
|
|
||||||
//message: t("general.validation.required"),
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
<Select>
|
|
||||||
{options.map((item, idx) => (
|
|
||||||
<Select.Option key={idx} value={item}>
|
|
||||||
{item}
|
|
||||||
</Select.Option>
|
|
||||||
))}
|
|
||||||
</Select>
|
|
||||||
</Form.Item>
|
|
||||||
<DeleteFilled
|
|
||||||
onClick={() => {
|
|
||||||
remove(field.name);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<Form.Item
|
|
||||||
label={t("bodyshop.fields.statuses.color")}
|
|
||||||
key={`${index}color`}
|
|
||||||
name={[field.name, "color"]}
|
|
||||||
rules={[
|
|
||||||
{
|
|
||||||
required: true,
|
|
||||||
//message: t("general.validation.required"),
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
<ColorPicker />
|
|
||||||
</Form.Item>
|
|
||||||
</Space>
|
|
||||||
</Form.Item>
|
|
||||||
))}
|
))}
|
||||||
</LayoutFormRow>
|
</Select>
|
||||||
<Form.Item>
|
</Form.Item>
|
||||||
<Button
|
<Form.Item
|
||||||
type="dashed"
|
name={["md_ro_statuses", "pre_production_statuses"]}
|
||||||
onClick={() => {
|
label={t("bodyshop.fields.statuses.pre_production_statuses")}
|
||||||
add();
|
rules={[
|
||||||
}}
|
{
|
||||||
style={{ width: "100%" }}
|
required: true,
|
||||||
>
|
//message: t("general.validation.required"),
|
||||||
{t("general.actions.add")}
|
type: "array",
|
||||||
</Button>
|
},
|
||||||
</Form.Item>
|
]}
|
||||||
</div>
|
>
|
||||||
);
|
<Select mode="multiple">
|
||||||
}}
|
{options.map((item, idx) => (
|
||||||
</Form.List>
|
<Select.Option key={idx} value={item}>
|
||||||
</LayoutFormRow>
|
{item}
|
||||||
)}
|
</Select.Option>
|
||||||
</SelectorDiv>
|
))}
|
||||||
);
|
</Select>
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
name={["md_ro_statuses", "production_statuses"]}
|
||||||
|
label={t("bodyshop.fields.statuses.production_statuses")}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
type: "array",
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<Select mode="multiple">
|
||||||
|
{options.map((item, idx) => (
|
||||||
|
<Select.Option key={idx} value={item}>
|
||||||
|
{item}
|
||||||
|
</Select.Option>
|
||||||
|
))}
|
||||||
|
</Select>
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
name={["md_ro_statuses", "post_production_statuses"]}
|
||||||
|
label={t("bodyshop.fields.statuses.post_production_statuses")}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
type: "array",
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<Select mode="multiple">
|
||||||
|
{options.map((item, idx) => (
|
||||||
|
<Select.Option key={idx} value={item}>
|
||||||
|
{item}
|
||||||
|
</Select.Option>
|
||||||
|
))}
|
||||||
|
</Select>
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
name={["md_ro_statuses", "ready_statuses"]}
|
||||||
|
label={t("bodyshop.fields.statuses.ready_statuses")}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
//required: true,
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
type: "array",
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<Select mode="multiple">
|
||||||
|
{options.map((item, idx) => (
|
||||||
|
<Select.Option key={idx} value={item}>
|
||||||
|
{item}
|
||||||
|
</Select.Option>
|
||||||
|
))}
|
||||||
|
</Select>
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
name={["md_ro_statuses", "additional_board_statuses"]}
|
||||||
|
label={t("bodyshop.fields.statuses.additional_board_statuses")}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
//required: true,
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
type: "array",
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<Select mode="multiple">
|
||||||
|
{options.map((item, idx) => (
|
||||||
|
<Select.Option key={idx} value={item}>
|
||||||
|
{item}
|
||||||
|
</Select.Option>
|
||||||
|
))}
|
||||||
|
</Select>
|
||||||
|
</Form.Item>
|
||||||
|
<LayoutFormRow noDivider>
|
||||||
|
<Form.Item
|
||||||
|
label={t("bodyshop.fields.statuses.default_scheduled")}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
name={["md_ro_statuses", "default_scheduled"]}
|
||||||
|
>
|
||||||
|
<Select>
|
||||||
|
{options.map((item, idx) => (
|
||||||
|
<Select.Option key={idx} value={item}>
|
||||||
|
{item}
|
||||||
|
</Select.Option>
|
||||||
|
))}
|
||||||
|
</Select>
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("bodyshop.fields.statuses.default_arrived")}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
name={["md_ro_statuses", "default_arrived"]}
|
||||||
|
>
|
||||||
|
<Select>
|
||||||
|
{options.map((item, idx) => (
|
||||||
|
<Select.Option key={idx} value={item}>
|
||||||
|
{item}
|
||||||
|
</Select.Option>
|
||||||
|
))}
|
||||||
|
</Select>
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("bodyshop.fields.statuses.default_exported")}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
name={["md_ro_statuses", "default_exported"]}
|
||||||
|
>
|
||||||
|
<Select>
|
||||||
|
{options.map((item, idx) => (
|
||||||
|
<Select.Option key={idx} value={item}>
|
||||||
|
{item}
|
||||||
|
</Select.Option>
|
||||||
|
))}
|
||||||
|
</Select>
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("bodyshop.fields.statuses.default_imported")}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
name={["md_ro_statuses", "default_imported"]}
|
||||||
|
>
|
||||||
|
<Select>
|
||||||
|
{options.map((item, idx) => (
|
||||||
|
<Select.Option key={idx} value={item}>
|
||||||
|
{item}
|
||||||
|
</Select.Option>
|
||||||
|
))}
|
||||||
|
</Select>
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("bodyshop.fields.statuses.default_invoiced")}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
name={["md_ro_statuses", "default_invoiced"]}
|
||||||
|
>
|
||||||
|
<Select>
|
||||||
|
{options.map((item, idx) => (
|
||||||
|
<Select.Option key={idx} value={item}>
|
||||||
|
{item}
|
||||||
|
</Select.Option>
|
||||||
|
))}
|
||||||
|
</Select>
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("bodyshop.fields.statuses.default_completed")}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
name={["md_ro_statuses", "default_completed"]}
|
||||||
|
>
|
||||||
|
<Select>
|
||||||
|
{options.map((item, idx) => (
|
||||||
|
<Select.Option key={idx} value={item}>
|
||||||
|
{item}
|
||||||
|
</Select.Option>
|
||||||
|
))}
|
||||||
|
</Select>
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("bodyshop.fields.statuses.default_delivered")}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
name={["md_ro_statuses", "default_delivered"]}
|
||||||
|
>
|
||||||
|
<Select>
|
||||||
|
{options.map((item, idx) => (
|
||||||
|
<Select.Option key={idx} value={item}>
|
||||||
|
{item}
|
||||||
|
</Select.Option>
|
||||||
|
))}
|
||||||
|
</Select>
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("bodyshop.fields.statuses.default_void")}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
name={["md_ro_statuses", "default_void"]}
|
||||||
|
>
|
||||||
|
<Select>
|
||||||
|
{options.map((item, idx) => (
|
||||||
|
<Select.Option key={idx} value={item}>
|
||||||
|
{item}
|
||||||
|
</Select.Option>
|
||||||
|
))}
|
||||||
|
</Select>
|
||||||
|
</Form.Item>
|
||||||
|
</LayoutFormRow>
|
||||||
|
{Production_List_Status_Colors.treatment === "on" && (
|
||||||
|
<LayoutFormRow
|
||||||
|
grow
|
||||||
|
header={t("bodyshop.fields.statuses.production_colors")}
|
||||||
|
id="production_colors"
|
||||||
|
>
|
||||||
|
<Form.List name={["md_ro_statuses", "production_colors"]}>
|
||||||
|
{(fields, {add, remove, move}) => {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<LayoutFormRow>
|
||||||
|
{fields.map((field, index) => (
|
||||||
|
<Form.Item key={field.key}>
|
||||||
|
<Space direction="vertical">
|
||||||
|
<div style={{display: "flex"}}>
|
||||||
|
<Form.Item
|
||||||
|
style={{flex: 1}}
|
||||||
|
label={t("jobs.fields.status")}
|
||||||
|
key={`${index}status`}
|
||||||
|
name={[field.name, "status"]}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<Select>
|
||||||
|
{options.map((item, idx) => (
|
||||||
|
<Select.Option key={idx} value={item}>
|
||||||
|
{item}
|
||||||
|
</Select.Option>
|
||||||
|
))}
|
||||||
|
</Select>
|
||||||
|
</Form.Item>
|
||||||
|
<DeleteFilled
|
||||||
|
onClick={() => {
|
||||||
|
remove(field.name);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<Form.Item
|
||||||
|
label={t("bodyshop.fields.statuses.color")}
|
||||||
|
key={`${index}color`}
|
||||||
|
name={[field.name, "color"]}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<ColorPicker/>
|
||||||
|
</Form.Item>
|
||||||
|
</Space>
|
||||||
|
</Form.Item>
|
||||||
|
))}
|
||||||
|
</LayoutFormRow>
|
||||||
|
<Form.Item>
|
||||||
|
<Button
|
||||||
|
type="dashed"
|
||||||
|
onClick={() => {
|
||||||
|
add();
|
||||||
|
}}
|
||||||
|
style={{width: "100%"}}
|
||||||
|
>
|
||||||
|
{t("general.actions.add")}
|
||||||
|
</Button>
|
||||||
|
</Form.Item>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
</Form.List>
|
||||||
|
</LayoutFormRow>
|
||||||
|
)}
|
||||||
|
</SelectorDiv>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ColorPicker = ({ value, onChange, style, ...restProps }) => {
|
export const ColorPicker = ({value, onChange, style, ...restProps}) => {
|
||||||
const handleChange = (color) => {
|
const handleChange = (color) => {
|
||||||
if (onChange) onChange(color.rgb);
|
if (onChange) onChange(color.rgb);
|
||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
<ChromePicker
|
<ChromePicker
|
||||||
{...restProps}
|
{...restProps}
|
||||||
color={value}
|
color={value}
|
||||||
onChangeComplete={handleChange}
|
onChangeComplete={handleChange}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -95,7 +95,7 @@ export function TechJobPrintTickets({ technician, event, attendacePrint }) {
|
|||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
<DatePicker.RangePicker
|
<DatePicker.RangePicker
|
||||||
ranges={DatePIckerRanges}
|
presets={DatePIckerRanges}
|
||||||
format={"MM/DD/YYYY"}
|
format={"MM/DD/YYYY"}
|
||||||
/>
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ export default function TimeTicketsDatesSelector() {
|
|||||||
end ? dayjs(end) : dayjs().endOf("week"),
|
end ? dayjs(end) : dayjs().endOf("week"),
|
||||||
]}
|
]}
|
||||||
format="MM/DD/YYYY"
|
format="MM/DD/YYYY"
|
||||||
ranges={DatePickerRanges}
|
presets={DatePickerRanges}
|
||||||
onCalendarChange={handleChange}
|
onCalendarChange={handleChange}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -187,7 +187,7 @@ export function TimeTicketList({
|
|||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{dayjs(record.clockoff)
|
{dayjs(record.clockoff)
|
||||||
.diff(dayjs(record.clockon), "hours", true)
|
.diff(dayjs(record.clockon), "hour", true)
|
||||||
.toFixed(2)}
|
.toFixed(2)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -124,7 +124,7 @@ const JobRelatedTicketsTable = ({
|
|||||||
if (!!val.clockoff && !!val.clockon)
|
if (!!val.clockoff && !!val.clockon)
|
||||||
return (
|
return (
|
||||||
acc +
|
acc +
|
||||||
dayjs(val.clockoff).diff(dayjs(val.clockon), "hours", true)
|
dayjs(val.clockoff).diff(dayjs(val.clockon), "hour", true)
|
||||||
);
|
);
|
||||||
return acc;
|
return acc;
|
||||||
}, 0);
|
}, 0);
|
||||||
@@ -251,7 +251,7 @@ const ShiftRelatedTicketsTable = ({
|
|||||||
const clockHrs = item.tickets.reduce((acc, val) => {
|
const clockHrs = item.tickets.reduce((acc, val) => {
|
||||||
if (!!val.clockoff && !!val.clockon)
|
if (!!val.clockoff && !!val.clockon)
|
||||||
return (
|
return (
|
||||||
acc + dayjs(val.clockoff).diff(dayjs(val.clockon), "hours", true)
|
acc + dayjs(val.clockoff).diff(dayjs(val.clockon), "hour", true)
|
||||||
);
|
);
|
||||||
return acc;
|
return acc;
|
||||||
}, 0);
|
}, 0);
|
||||||
|
|||||||
@@ -1,262 +1,249 @@
|
|||||||
import { DeleteFilled } from "@ant-design/icons";
|
import {DeleteFilled} from "@ant-design/icons";
|
||||||
import { useApolloClient } from "@apollo/client";
|
import {useApolloClient} from "@apollo/client";
|
||||||
import { useTreatments } from "@splitsoftware/splitio-react";
|
import {useSplitTreatments} from "@splitsoftware/splitio-react";
|
||||||
import {
|
import {Button, Divider, Form, Input, InputNumber, Space, Switch,} from "antd";
|
||||||
Button,
|
|
||||||
Divider,
|
|
||||||
Form,
|
|
||||||
Input,
|
|
||||||
InputNumber,
|
|
||||||
Space,
|
|
||||||
Switch,
|
|
||||||
} from "antd";
|
|
||||||
import {PageHeader} from "@ant-design/pro-layout";
|
import {PageHeader} from "@ant-design/pro-layout";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import {useTranslation} from "react-i18next";
|
||||||
import { CHECK_VENDOR_NAME } from "../../graphql/vendors.queries";
|
import {CHECK_VENDOR_NAME} from "../../graphql/vendors.queries";
|
||||||
import FormFieldsChanged from "../form-fields-changed-alert/form-fields-changed-alert.component";
|
import FormFieldsChanged from "../form-fields-changed-alert/form-fields-changed-alert.component";
|
||||||
import FormItemEmail from "../form-items-formatted/email-form-item.component";
|
import FormItemEmail from "../form-items-formatted/email-form-item.component";
|
||||||
import PhoneFormItem, {
|
import PhoneFormItem, {PhoneItemFormatterValidation,} from "../form-items-formatted/phone-form-item.component";
|
||||||
PhoneItemFormatterValidation,
|
|
||||||
} from "../form-items-formatted/phone-form-item.component";
|
|
||||||
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
|
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
|
||||||
import VendorsPhonebookAdd from "../vendors-phonebook-add/vendors-phonebook-add.component";
|
import VendorsPhonebookAdd from "../vendors-phonebook-add/vendors-phonebook-add.component";
|
||||||
|
|
||||||
import { connect } from "react-redux";
|
import {connect} from "react-redux";
|
||||||
import { createStructuredSelector } from "reselect";
|
import {createStructuredSelector} from "reselect";
|
||||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
import {selectBodyshop} from "../../redux/user/user.selectors";
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
bodyshop: selectBodyshop,
|
bodyshop: selectBodyshop,
|
||||||
});
|
});
|
||||||
const mapDispatchToProps = (dispatch) => ({
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
||||||
});
|
});
|
||||||
export default connect(
|
export default connect(
|
||||||
mapStateToProps,
|
mapStateToProps,
|
||||||
mapDispatchToProps
|
mapDispatchToProps
|
||||||
)(VendorsFormComponent);
|
)(VendorsFormComponent);
|
||||||
|
|
||||||
export function VendorsFormComponent({
|
export function VendorsFormComponent({bodyshop, form, formLoading, handleDelete, responsibilityCenters, selectedvendor}) {
|
||||||
bodyshop,
|
const {t} = useTranslation();
|
||||||
form,
|
const client = useApolloClient();
|
||||||
formLoading,
|
|
||||||
handleDelete,
|
|
||||||
responsibilityCenters,
|
|
||||||
selectedvendor,
|
|
||||||
}) {
|
|
||||||
const { t } = useTranslation();
|
|
||||||
const client = useApolloClient();
|
|
||||||
const { DmsAp } = useTreatments(
|
|
||||||
["DmsAp"],
|
|
||||||
{},
|
|
||||||
bodyshop && bodyshop.imexshopid
|
|
||||||
);
|
|
||||||
|
|
||||||
const { getFieldValue } = form;
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<PageHeader
|
|
||||||
title={
|
|
||||||
<Form.Item shouldUpdate>{() => form.getFieldValue("name")}</Form.Item>
|
|
||||||
}
|
|
||||||
extra={
|
|
||||||
<Space>
|
|
||||||
<Form.Item
|
|
||||||
label={t("vendors.fields.active")}
|
|
||||||
name="active"
|
|
||||||
initialValue={true}
|
|
||||||
valuePropName="checked"
|
|
||||||
>
|
|
||||||
<Switch />
|
|
||||||
</Form.Item>
|
|
||||||
<Button
|
|
||||||
onClick={() => form.submit()}
|
|
||||||
type="primary"
|
|
||||||
loading={formLoading}
|
|
||||||
>
|
|
||||||
{t("general.actions.save")}
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
type="danger"
|
|
||||||
disabled={selectedvendor === "new"}
|
|
||||||
onClick={handleDelete}
|
|
||||||
loading={formLoading}
|
|
||||||
>
|
|
||||||
{t("general.actions.delete")}
|
|
||||||
</Button>
|
|
||||||
|
|
||||||
<VendorsPhonebookAdd
|
const {treatments: {DmsAp}} = useSplitTreatments({
|
||||||
form={form}
|
attributes: {},
|
||||||
disabled={form.isFieldsTouched()}
|
names: ["DmsAp"],
|
||||||
/>
|
splitKey: bodyshop && bodyshop.imexshopid
|
||||||
</Space>
|
});
|
||||||
}
|
|
||||||
/>
|
|
||||||
<FormFieldsChanged form={form} />
|
|
||||||
<LayoutFormRow grow>
|
|
||||||
<Form.Item
|
|
||||||
label={t("vendors.fields.name")}
|
|
||||||
name="name"
|
|
||||||
validateTrigger="onBlur"
|
|
||||||
hasFeedback
|
|
||||||
rules={[
|
|
||||||
{
|
|
||||||
required: true,
|
|
||||||
//message: t("general.validation.required"),
|
|
||||||
},
|
|
||||||
({ getFieldValue }) => ({
|
|
||||||
async validator(rule, value) {
|
|
||||||
if (value) {
|
|
||||||
const response = await client.query({
|
|
||||||
query: CHECK_VENDOR_NAME,
|
|
||||||
variables: {
|
|
||||||
name: value,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
if (response.data.vendors_aggregate.aggregate.count === 0) {
|
|
||||||
return Promise.resolve();
|
const {getFieldValue} = form;
|
||||||
} else if (
|
return (
|
||||||
response.data.vendors_aggregate.nodes.length === 1 &&
|
<div>
|
||||||
response.data.vendors_aggregate.nodes[0].id ===
|
<PageHeader
|
||||||
form.getFieldValue("id")
|
title={
|
||||||
) {
|
<Form.Item shouldUpdate>{() => form.getFieldValue("name")}</Form.Item>
|
||||||
return Promise.resolve();
|
|
||||||
}
|
|
||||||
return Promise.reject(
|
|
||||||
t("vendors.validation.unique_vendor_name")
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
return Promise.resolve();
|
|
||||||
}
|
}
|
||||||
},
|
extra={
|
||||||
}),
|
<Space>
|
||||||
]}
|
<Form.Item
|
||||||
>
|
label={t("vendors.fields.active")}
|
||||||
<Input />
|
name="active"
|
||||||
</Form.Item>
|
initialValue={true}
|
||||||
|
valuePropName="checked"
|
||||||
|
>
|
||||||
|
<Switch/>
|
||||||
|
</Form.Item>
|
||||||
|
<Button
|
||||||
|
onClick={() => form.submit()}
|
||||||
|
type="primary"
|
||||||
|
loading={formLoading}
|
||||||
|
>
|
||||||
|
{t("general.actions.save")}
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
type="danger"
|
||||||
|
disabled={selectedvendor === "new"}
|
||||||
|
onClick={handleDelete}
|
||||||
|
loading={formLoading}
|
||||||
|
>
|
||||||
|
{t("general.actions.delete")}
|
||||||
|
</Button>
|
||||||
|
|
||||||
<Form.Item
|
<VendorsPhonebookAdd
|
||||||
label={t("vendors.fields.email")}
|
form={form}
|
||||||
rules={
|
disabled={form.isFieldsTouched()}
|
||||||
[
|
/>
|
||||||
// {
|
</Space>
|
||||||
// type: "email",
|
}
|
||||||
// message: t("general.validation.invalidemail"),
|
/>
|
||||||
// },
|
<FormFieldsChanged form={form}/>
|
||||||
]
|
<LayoutFormRow grow>
|
||||||
}
|
<Form.Item
|
||||||
name="email"
|
label={t("vendors.fields.name")}
|
||||||
>
|
name="name"
|
||||||
<FormItemEmail email={getFieldValue("email")} />
|
validateTrigger="onBlur"
|
||||||
</Form.Item>
|
hasFeedback
|
||||||
<Form.Item
|
rules={[
|
||||||
label={t("vendors.fields.phone")}
|
|
||||||
name="phone"
|
|
||||||
rules={[
|
|
||||||
({ getFieldValue }) =>
|
|
||||||
PhoneItemFormatterValidation(getFieldValue, "phone"),
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
<PhoneFormItem />
|
|
||||||
</Form.Item>
|
|
||||||
</LayoutFormRow>
|
|
||||||
<LayoutFormRow grow>
|
|
||||||
<Form.Item label={t("vendors.fields.street1")} name="street1">
|
|
||||||
<Input />
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item label={t("vendors.fields.street2")} name="street2">
|
|
||||||
<Input />
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item label={t("vendors.fields.city")} name="city">
|
|
||||||
<Input />
|
|
||||||
</Form.Item>
|
|
||||||
</LayoutFormRow>
|
|
||||||
<LayoutFormRow grow>
|
|
||||||
<Form.Item label={t("vendors.fields.state")} name="state">
|
|
||||||
<Input />
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item label={t("vendors.fields.zip")} name="zip">
|
|
||||||
<Input />
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item label={t("vendors.fields.country")} name="country">
|
|
||||||
<Input />
|
|
||||||
</Form.Item>
|
|
||||||
</LayoutFormRow>
|
|
||||||
<LayoutFormRow grow>
|
|
||||||
<Form.Item label={t("vendors.fields.discount")} name="discount">
|
|
||||||
<InputNumber min={0} max={1} precision={2} step={0.01} />
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item label={t("vendors.fields.due_date")} name="due_date">
|
|
||||||
<InputNumber min={0} />
|
|
||||||
</Form.Item>
|
|
||||||
{
|
|
||||||
// <Form.Item
|
|
||||||
// label={t("vendors.fields.cost_center")}
|
|
||||||
// rules={[
|
|
||||||
// { required: true, message: t("general.validation.required") },
|
|
||||||
// ]}
|
|
||||||
// name="cost_center"
|
|
||||||
// >
|
|
||||||
// <Select style={{ width: "150px" }}>
|
|
||||||
// {responsibilityCenters.costs.map((item) => (
|
|
||||||
// <Select.Option key={item.name}>{item.name}</Select.Option>
|
|
||||||
// ))}
|
|
||||||
// </Select>
|
|
||||||
// </Form.Item>
|
|
||||||
}
|
|
||||||
</LayoutFormRow>
|
|
||||||
|
|
||||||
{DmsAp.treatment === "on" && (
|
|
||||||
<Form.Item label={t("vendors.fields.dmsid")} name="dmsid">
|
|
||||||
<Input />
|
|
||||||
</Form.Item>
|
|
||||||
)}
|
|
||||||
<Divider align="left">{t("vendors.labels.preferredmakes")}</Divider>
|
|
||||||
<Form.List name="favorite">
|
|
||||||
{(fields, { add, remove }) => {
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
{fields.map((field, index) => (
|
|
||||||
<Form.Item key={field.key}>
|
|
||||||
<Space wrap>
|
|
||||||
<Form.Item
|
|
||||||
label={t("vendors.fields.make")}
|
|
||||||
key={`${index}make`}
|
|
||||||
name={[field.name]}
|
|
||||||
rules={[
|
|
||||||
{
|
{
|
||||||
required: true,
|
required: true,
|
||||||
//message: t("general.validation.required"),
|
//message: t("general.validation.required"),
|
||||||
},
|
},
|
||||||
]}
|
({getFieldValue}) => ({
|
||||||
>
|
async validator(rule, value) {
|
||||||
<Input />
|
if (value) {
|
||||||
</Form.Item>
|
const response = await client.query({
|
||||||
|
query: CHECK_VENDOR_NAME,
|
||||||
|
variables: {
|
||||||
|
name: value,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
<DeleteFilled
|
if (response.data.vendors_aggregate.aggregate.count === 0) {
|
||||||
onClick={() => {
|
return Promise.resolve();
|
||||||
remove(field.name);
|
} else if (
|
||||||
}}
|
response.data.vendors_aggregate.nodes.length === 1 &&
|
||||||
/>
|
response.data.vendors_aggregate.nodes[0].id ===
|
||||||
</Space>
|
form.getFieldValue("id")
|
||||||
</Form.Item>
|
) {
|
||||||
))}
|
return Promise.resolve();
|
||||||
<Form.Item>
|
}
|
||||||
<Button
|
return Promise.reject(
|
||||||
type="dashed"
|
t("vendors.validation.unique_vendor_name")
|
||||||
onClick={() => {
|
);
|
||||||
add();
|
} else {
|
||||||
}}
|
return Promise.resolve();
|
||||||
style={{ width: "100%" }}
|
}
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
]}
|
||||||
>
|
>
|
||||||
{t("vendors.actions.newpreferredmake")}
|
<Input/>
|
||||||
</Button>
|
</Form.Item>
|
||||||
</Form.Item>
|
|
||||||
</div>
|
<Form.Item
|
||||||
);
|
label={t("vendors.fields.email")}
|
||||||
}}
|
rules={
|
||||||
</Form.List>
|
[
|
||||||
</div>
|
// {
|
||||||
);
|
// type: "email",
|
||||||
|
// message: t("general.validation.invalidemail"),
|
||||||
|
// },
|
||||||
|
]
|
||||||
|
}
|
||||||
|
name="email"
|
||||||
|
>
|
||||||
|
<FormItemEmail email={getFieldValue("email")}/>
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("vendors.fields.phone")}
|
||||||
|
name="phone"
|
||||||
|
rules={[
|
||||||
|
({getFieldValue}) =>
|
||||||
|
PhoneItemFormatterValidation(getFieldValue, "phone"),
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<PhoneFormItem/>
|
||||||
|
</Form.Item>
|
||||||
|
</LayoutFormRow>
|
||||||
|
<LayoutFormRow grow>
|
||||||
|
<Form.Item label={t("vendors.fields.street1")} name="street1">
|
||||||
|
<Input/>
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item label={t("vendors.fields.street2")} name="street2">
|
||||||
|
<Input/>
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item label={t("vendors.fields.city")} name="city">
|
||||||
|
<Input/>
|
||||||
|
</Form.Item>
|
||||||
|
</LayoutFormRow>
|
||||||
|
<LayoutFormRow grow>
|
||||||
|
<Form.Item label={t("vendors.fields.state")} name="state">
|
||||||
|
<Input/>
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item label={t("vendors.fields.zip")} name="zip">
|
||||||
|
<Input/>
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item label={t("vendors.fields.country")} name="country">
|
||||||
|
<Input/>
|
||||||
|
</Form.Item>
|
||||||
|
</LayoutFormRow>
|
||||||
|
<LayoutFormRow grow>
|
||||||
|
<Form.Item label={t("vendors.fields.discount")} name="discount">
|
||||||
|
<InputNumber min={0} max={1} precision={2} step={0.01}/>
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item label={t("vendors.fields.due_date")} name="due_date">
|
||||||
|
<InputNumber min={0}/>
|
||||||
|
</Form.Item>
|
||||||
|
{
|
||||||
|
// <Form.Item
|
||||||
|
// label={t("vendors.fields.cost_center")}
|
||||||
|
// rules={[
|
||||||
|
// { required: true, message: t("general.validation.required") },
|
||||||
|
// ]}
|
||||||
|
// name="cost_center"
|
||||||
|
// >
|
||||||
|
// <Select style={{ width: "150px" }}>
|
||||||
|
// {responsibilityCenters.costs.map((item) => (
|
||||||
|
// <Select.Option key={item.name}>{item.name}</Select.Option>
|
||||||
|
// ))}
|
||||||
|
// </Select>
|
||||||
|
// </Form.Item>
|
||||||
|
}
|
||||||
|
</LayoutFormRow>
|
||||||
|
|
||||||
|
{DmsAp.treatment === "on" && (
|
||||||
|
<Form.Item label={t("vendors.fields.dmsid")} name="dmsid">
|
||||||
|
<Input/>
|
||||||
|
</Form.Item>
|
||||||
|
)}
|
||||||
|
<Divider align="left">{t("vendors.labels.preferredmakes")}</Divider>
|
||||||
|
<Form.List name="favorite">
|
||||||
|
{(fields, {add, remove}) => {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{fields.map((field, index) => (
|
||||||
|
<Form.Item key={field.key}>
|
||||||
|
<Space wrap>
|
||||||
|
<Form.Item
|
||||||
|
label={t("vendors.fields.make")}
|
||||||
|
key={`${index}make`}
|
||||||
|
name={[field.name]}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<Input/>
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
|
<DeleteFilled
|
||||||
|
onClick={() => {
|
||||||
|
remove(field.name);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Space>
|
||||||
|
</Form.Item>
|
||||||
|
))}
|
||||||
|
<Form.Item>
|
||||||
|
<Button
|
||||||
|
type="dashed"
|
||||||
|
onClick={() => {
|
||||||
|
add();
|
||||||
|
}}
|
||||||
|
style={{width: "100%"}}
|
||||||
|
>
|
||||||
|
{t("vendors.actions.newpreferredmake")}
|
||||||
|
</Button>
|
||||||
|
</Form.Item>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
</Form.List>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ import React, { useState } from "react";
|
|||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
// import { useNavigate } from 'react-router-dom';
|
// import { useNavigate } from 'react-router-dom';
|
||||||
import { useTreatments } from "@splitsoftware/splitio-react";
|
import { useSplitTreatments } from "@splitsoftware/splitio-react";
|
||||||
import Dinero from "dinero.js";
|
import Dinero from "dinero.js";
|
||||||
import dayjs from "../../utils/day";
|
import dayjs from "../../utils/day";
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
@@ -51,16 +51,12 @@ export function JobsCloseComponent({ job, bodyshop, jobRO }) {
|
|||||||
// const history = useHistory();
|
// const history = useHistory();
|
||||||
const [closeJob] = useMutation(UPDATE_JOB);
|
const [closeJob] = useMutation(UPDATE_JOB);
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const { Qb_Multi_Ar } = useTreatments(
|
|
||||||
["Qb_Multi_Ar"],
|
const { treatments: {Qb_Multi_Ar,ClosingPeriod} } = useSplitTreatments({
|
||||||
{},
|
attributes: {},
|
||||||
bodyshop && bodyshop.imexshopid
|
names: ["Qb_Multi_Ar", "ClosingPeriod"],
|
||||||
);
|
splitKey: bodyshop && bodyshop.imexshopid,
|
||||||
const { ClosingPeriod } = useTreatments(
|
});
|
||||||
["ClosingPeriod"],
|
|
||||||
{},
|
|
||||||
bodyshop && bodyshop.imexshopid
|
|
||||||
);
|
|
||||||
|
|
||||||
const handleFinish = async ({ removefromproduction, ...values }) => {
|
const handleFinish = async ({ removefromproduction, ...values }) => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import {BackTop, Layout} from "antd";
|
import {FloatButton, Layout} from "antd";
|
||||||
import preval from "preval.macro";
|
import preval from "preval.macro";
|
||||||
import React, {lazy, Suspense, useEffect} from "react";
|
import React, {lazy, Suspense, useEffect} from "react";
|
||||||
import {useTranslation} from "react-i18next";
|
import {useTranslation} from "react-i18next";
|
||||||
@@ -371,7 +371,7 @@ export function Manage({conflict, bodyshop}) {
|
|||||||
{PageContent}
|
{PageContent}
|
||||||
</Sentry.ErrorBoundary>
|
</Sentry.ErrorBoundary>
|
||||||
|
|
||||||
<BackTop/>
|
<FloatButton.BackTop/>
|
||||||
<Footer>
|
<Footer>
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { BackTop, Layout } from "antd";
|
import {FloatButton, Layout} from "antd";
|
||||||
import React, { Suspense, lazy, useEffect } from "react";
|
import React, { Suspense, lazy, useEffect } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
@@ -90,7 +90,7 @@ export function TechPage({ technician }) {
|
|||||||
</FeatureWrapper>
|
</FeatureWrapper>
|
||||||
</Suspense>
|
</Suspense>
|
||||||
</ErrorBoundary>
|
</ErrorBoundary>
|
||||||
<BackTop />
|
<FloatButton.BackTop />
|
||||||
</Content>
|
</Content>
|
||||||
</Layout>
|
</Layout>
|
||||||
</Layout>
|
</Layout>
|
||||||
|
|||||||
@@ -1,27 +1,62 @@
|
|||||||
import dayjs from "./day";
|
import dayjs from "./day";
|
||||||
const range = {
|
|
||||||
Today: [dayjs(), dayjs()],
|
const range = [
|
||||||
"Last 14 days": [dayjs().subtract(14, "day"), dayjs()],
|
{
|
||||||
"Last 7 days": [dayjs().subtract(7, "day"), dayjs()],
|
label: 'Today',
|
||||||
"Next 7 days": [dayjs(), dayjs().add(7, "day")],
|
value: [dayjs(), dayjs()]
|
||||||
"Next 14 days": [dayjs(), dayjs().add(14, "day")],
|
},
|
||||||
"Last Month": [
|
{
|
||||||
dayjs().startOf("month").subtract(1, "month"),
|
label: 'Last 14 days',
|
||||||
dayjs().startOf("month").subtract(1, "month").endOf("month"),
|
value: [dayjs().subtract(14, "day"), dayjs()]
|
||||||
],
|
},
|
||||||
"This Month": [dayjs().startOf("month"), dayjs().endOf("month")],
|
{
|
||||||
"Next Month": [
|
label: 'Last 7 days',
|
||||||
dayjs().startOf("month").add(1, "month"),
|
value: [dayjs().subtract(7, "day"), dayjs()]
|
||||||
dayjs().startOf("month").add(1, "month").endOf("month"),
|
},
|
||||||
],
|
{
|
||||||
"Last Quarter": [
|
label: 'Next 7 days',
|
||||||
dayjs().startOf("quarter").subtract(1, "quarter"),
|
value: [dayjs(), dayjs().add(7, "day")]
|
||||||
dayjs().startOf("quarter").subtract(1, "day"),
|
},
|
||||||
],
|
{
|
||||||
"This Quarter": [
|
label: 'Next 14 days',
|
||||||
dayjs().startOf("quarter"),
|
value: [dayjs(), dayjs().add(14, "day")],
|
||||||
dayjs().startOf("quarter").add(1, "quarter").subtract(1, "day"),
|
},
|
||||||
],
|
{
|
||||||
"Last 90 Days": [dayjs().add(-90, "day"), dayjs()],
|
label: 'Last Month',
|
||||||
};
|
value: [
|
||||||
|
dayjs().startOf("month").subtract(1, "month"),
|
||||||
|
dayjs().startOf("month").subtract(1, "month").endOf("month"),
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'This Month',
|
||||||
|
value: [dayjs().startOf("month"), dayjs().endOf("month")]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Next Month',
|
||||||
|
value: [
|
||||||
|
dayjs().startOf("month").add(1, "month"),
|
||||||
|
dayjs().startOf("month").add(1, "month").endOf("month"),
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Last Quarter',
|
||||||
|
value: [
|
||||||
|
dayjs().startOf("quarter").subtract(1, "quarter"),
|
||||||
|
dayjs().startOf("quarter").subtract(1, "day"),
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'This Quarter',
|
||||||
|
value: [
|
||||||
|
dayjs().startOf("quarter"),
|
||||||
|
dayjs().startOf("quarter").add(1, "quarter").subtract(1, "day"),
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Last 90 Days',
|
||||||
|
value: [dayjs().add(-90, "day"), dayjs()],
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
export default range;
|
export default range;
|
||||||
|
|||||||
@@ -54,27 +54,27 @@ function functionMapper(f, timezone) {
|
|||||||
case "date.yesterday":
|
case "date.yesterday":
|
||||||
return moment().tz(timezone).subtract(1, "day").format(isoFormat);
|
return moment().tz(timezone).subtract(1, "day").format(isoFormat);
|
||||||
case "date.3daysago":
|
case "date.3daysago":
|
||||||
return moment().tz(timezone).subtract(3, "days").format(isoFormat);
|
return moment().tz(timezone).subtract(3, "day").format(isoFormat);
|
||||||
case "date.7daysago":
|
case "date.7daysago":
|
||||||
return moment().tz(timezone).subtract(7, "days").format(isoFormat);
|
return moment().tz(timezone).subtract(7, "day").format(isoFormat);
|
||||||
case "date.tomorrow":
|
case "date.tomorrow":
|
||||||
return moment().tz(timezone).add(1, "day").format(isoFormat);
|
return moment().tz(timezone).add(1, "day").format(isoFormat);
|
||||||
case "date.3daysfromnow":
|
case "date.3daysfromnow":
|
||||||
return moment().tz(timezone).add(3, "days").format(isoFormat);
|
return moment().tz(timezone).add(3, "day").format(isoFormat);
|
||||||
case "date.7daysfromnow":
|
case "date.7daysfromnow":
|
||||||
return moment().tz(timezone).add(7, "days").format(isoFormat);
|
return moment().tz(timezone).add(7, "day").format(isoFormat);
|
||||||
case "date.yesterdaytz":
|
case "date.yesterdaytz":
|
||||||
return moment().tz(timezone).subtract(1, "day");
|
return moment().tz(timezone).subtract(1, "day");
|
||||||
case "date.3daysagotz":
|
case "date.3daysagotz":
|
||||||
return moment().tz(timezone).subtract(3, "days");
|
return moment().tz(timezone).subtract(3, "day");
|
||||||
case "date.7daysagotz":
|
case "date.7daysagotz":
|
||||||
return moment().tz(timezone).subtract(7, "days");
|
return moment().tz(timezone).subtract(7, "day");
|
||||||
case "date.tomorrowtz":
|
case "date.tomorrowtz":
|
||||||
return moment().tz(timezone).add(1, "day");
|
return moment().tz(timezone).add(1, "day");
|
||||||
case "date.3daysfromnowtz":
|
case "date.3daysfromnowtz":
|
||||||
return moment().tz(timezone).add(3, "days");
|
return moment().tz(timezone).add(3, "day");
|
||||||
case "date.7daysfromnowtz":
|
case "date.7daysfromnowtz":
|
||||||
return moment().tz(timezone).add(7, "days");
|
return moment().tz(timezone).add(7, "day");
|
||||||
|
|
||||||
case "date.now":
|
case "date.now":
|
||||||
return moment().tz(timezone);
|
return moment().tz(timezone);
|
||||||
|
|||||||
Reference in New Issue
Block a user