- reapply proper prettier formatting.
Signed-off-by: Dave Richer <dave@imexsystems.ca>
This commit is contained in:
@@ -6,7 +6,7 @@ import {connect} from "react-redux";
|
||||
import {createStructuredSelector} from "reselect";
|
||||
import {selectJobReadOnly} from "../../redux/application/application.selectors";
|
||||
import {setModalContext} from "../../redux/modals/modals.actions";
|
||||
import {selectBodyshop, selectCurrentUser} from "../../redux/user/user.selectors";
|
||||
import {selectBodyshop} from "../../redux/user/user.selectors";
|
||||
import CurrencyFormatter from "../../utils/CurrencyFormatter";
|
||||
import {DateFormatter} from "../../utils/DateFormatter";
|
||||
import {alphaSort, dateSort} from "../../utils/sorters";
|
||||
@@ -22,7 +22,10 @@ const mapStateToProps = createStructuredSelector({
|
||||
});
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
setBillEnterContext: (context) => dispatch(setModalContext({ context: context, modal: "billEnter" })),
|
||||
setBillEnterContext: (context) => dispatch(setModalContext({
|
||||
context: context,
|
||||
modal: "billEnter"
|
||||
})),
|
||||
setReconciliationContext: (context) => dispatch(setModalContext({
|
||||
context: context,
|
||||
modal: "reconciliation"
|
||||
@@ -40,7 +43,7 @@ export function BillsListTableComponent({
|
||||
setReconciliationContext,
|
||||
setTaskUpsertContext,
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
const {t} = useTranslation();
|
||||
|
||||
const [state, setState] = useState({
|
||||
sortedInfo: {}
|
||||
@@ -51,14 +54,14 @@ export function BillsListTableComponent({
|
||||
|
||||
const Templates = TemplateList("bill");
|
||||
const bills = billsQuery.data ? billsQuery.data.bills : [];
|
||||
const { refetch } = billsQuery;
|
||||
const {refetch} = billsQuery;
|
||||
|
||||
|
||||
const recordActions = (record, showView = false) => (
|
||||
<Space wrap>
|
||||
{showView && (
|
||||
<Button onClick={() => handleOnRowClick(record)}>
|
||||
<EditFilled />
|
||||
<EditFilled/>
|
||||
</Button>
|
||||
)}
|
||||
<Button title={t('tasks.buttons.create')} onClick={() => {
|
||||
@@ -71,9 +74,9 @@ export function BillsListTableComponent({
|
||||
}}>
|
||||
<FaTasks/>
|
||||
</Button>
|
||||
<BillDeleteButton bill={record} jobid={job.id} />
|
||||
<BillDeleteButton bill={record} jobid={job.id}/>
|
||||
<BillDetailEditReturnComponent
|
||||
data={{ bills_by_pk: { ...record, jobid: job.id } }}
|
||||
data={{bills_by_pk: {...record, jobid: job.id}}}
|
||||
disabled={record.is_credit_memo || record.vendorid === bodyshop.inhousevendorid || jobRO}
|
||||
/>
|
||||
|
||||
@@ -81,9 +84,9 @@ export function BillsListTableComponent({
|
||||
<PrintWrapperComponent
|
||||
templateObject={{
|
||||
name: Templates.inhouse_invoice.key,
|
||||
variables: { id: record.id }
|
||||
variables: {id: record.id}
|
||||
}}
|
||||
messageObject={{ subject: Templates.inhouse_invoice.subject }}
|
||||
messageObject={{subject: Templates.inhouse_invoice.subject}}
|
||||
/>
|
||||
)}
|
||||
</Space>
|
||||
@@ -126,7 +129,7 @@ export function BillsListTableComponent({
|
||||
key: "is_credit_memo",
|
||||
sorter: (a, b) => a.is_credit_memo - b.is_credit_memo,
|
||||
sortOrder: state.sortedInfo.columnKey === "is_credit_memo" && state.sortedInfo.order,
|
||||
render: (text, record) => <Checkbox checked={record.is_credit_memo} />
|
||||
render: (text, record) => <Checkbox checked={record.is_credit_memo}/>
|
||||
},
|
||||
{
|
||||
title: t("bills.fields.exported"),
|
||||
@@ -134,7 +137,7 @@ export function BillsListTableComponent({
|
||||
key: "exported",
|
||||
sorter: (a, b) => a.exported - b.exported,
|
||||
sortOrder: state.sortedInfo.columnKey === "exported" && state.sortedInfo.order,
|
||||
render: (text, record) => <Checkbox checked={record.exported} />
|
||||
render: (text, record) => <Checkbox checked={record.exported}/>
|
||||
},
|
||||
{
|
||||
title: t("general.labels.actions"),
|
||||
@@ -145,18 +148,18 @@ export function BillsListTableComponent({
|
||||
];
|
||||
|
||||
const handleTableChange = (pagination, filters, sorter) => {
|
||||
setState({ ...state, filteredInfo: filters, sortedInfo: sorter });
|
||||
setState({...state, filteredInfo: filters, sortedInfo: sorter});
|
||||
};
|
||||
|
||||
const filteredBills = bills
|
||||
? searchText === ""
|
||||
? bills
|
||||
: bills.filter(
|
||||
(b) =>
|
||||
(b.invoice_number || "").toLowerCase().includes(searchText.toLowerCase()) ||
|
||||
(b.vendor.name || "").toLowerCase().includes(searchText.toLowerCase()) ||
|
||||
(b.total || "").toString().toLowerCase().includes(searchText.toLowerCase())
|
||||
)
|
||||
(b) =>
|
||||
(b.invoice_number || "").toLowerCase().includes(searchText.toLowerCase()) ||
|
||||
(b.vendor.name || "").toLowerCase().includes(searchText.toLowerCase()) ||
|
||||
(b.total || "").toString().toLowerCase().includes(searchText.toLowerCase())
|
||||
)
|
||||
: [];
|
||||
|
||||
return (
|
||||
@@ -165,14 +168,14 @@ export function BillsListTableComponent({
|
||||
extra={
|
||||
<Space wrap>
|
||||
<Button onClick={() => refetch()}>
|
||||
<SyncOutlined />
|
||||
<SyncOutlined/>
|
||||
</Button>
|
||||
{job && job.converted ? (
|
||||
<>
|
||||
<Button
|
||||
onClick={() => {
|
||||
setBillEnterContext({
|
||||
actions: { refetch: billsQuery.refetch },
|
||||
actions: {refetch: billsQuery.refetch},
|
||||
context: {
|
||||
job
|
||||
}
|
||||
@@ -184,7 +187,7 @@ export function BillsListTableComponent({
|
||||
<Button
|
||||
onClick={() => {
|
||||
setReconciliationContext({
|
||||
actions: { refetch: billsQuery.refetch },
|
||||
actions: {refetch: billsQuery.refetch},
|
||||
context: {
|
||||
job,
|
||||
bills: (billsQuery.data && billsQuery.data.bills) || []
|
||||
|
||||
@@ -26,26 +26,35 @@ import Icon, {
|
||||
UnorderedListOutlined,
|
||||
UserOutlined
|
||||
} from "@ant-design/icons";
|
||||
import { useSplitTreatments } from "@splitsoftware/splitio-react";
|
||||
import { Layout, Menu, Switch, Tooltip } from "antd";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { BsKanban } from "react-icons/bs";
|
||||
import { FaCalendarAlt, FaCarCrash, FaCreditCard, FaFileInvoiceDollar, FaTasks } from "react-icons/fa";
|
||||
import { GiPayMoney, GiPlayerTime, GiSettingsKnobs } from "react-icons/gi";
|
||||
import { IoBusinessOutline } from "react-icons/io5";
|
||||
import { RiSurveyLine } from "react-icons/ri";
|
||||
import { connect } from "react-redux";
|
||||
import { Link } from "react-router-dom";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { selectRecentItems, selectSelectedHeader } from "../../redux/application/application.selectors";
|
||||
import { setModalContext } from "../../redux/modals/modals.actions";
|
||||
import { signOutStart } from "../../redux/user/user.actions";
|
||||
import { selectBodyshop, selectCurrentUser } from "../../redux/user/user.selectors";
|
||||
import { FiLogOut } from "react-icons/fi";
|
||||
import { checkBeta, handleBeta, setBeta } from "../../utils/betaHandler";
|
||||
import {useSplitTreatments} from "@splitsoftware/splitio-react";
|
||||
import {Layout, Menu, Switch, Tooltip} from "antd";
|
||||
import React, {useEffect, useState} from "react";
|
||||
import {useTranslation} from "react-i18next";
|
||||
import {BsKanban} from "react-icons/bs";
|
||||
import {
|
||||
FaCalendarAlt,
|
||||
FaCarCrash,
|
||||
FaCreditCard,
|
||||
FaFileInvoiceDollar,
|
||||
FaTasks
|
||||
} from "react-icons/fa";
|
||||
import {GiPayMoney, GiPlayerTime, GiSettingsKnobs} from "react-icons/gi";
|
||||
import {IoBusinessOutline} from "react-icons/io5";
|
||||
import {RiSurveyLine} from "react-icons/ri";
|
||||
import {connect} from "react-redux";
|
||||
import {Link} from "react-router-dom";
|
||||
import {createStructuredSelector} from "reselect";
|
||||
import {
|
||||
selectRecentItems,
|
||||
selectSelectedHeader
|
||||
} from "../../redux/application/application.selectors";
|
||||
import {setModalContext} from "../../redux/modals/modals.actions";
|
||||
import {signOutStart} from "../../redux/user/user.actions";
|
||||
import {selectBodyshop, selectCurrentUser} from "../../redux/user/user.selectors";
|
||||
import {FiLogOut} from "react-icons/fi";
|
||||
import {checkBeta, handleBeta, setBeta} from "../../utils/betaHandler";
|
||||
import InstanceRenderManager from "../../utils/instanceRenderMgr";
|
||||
import { HasFeatureAccess } from "../feature-wrapper/feature-wrapper.component";
|
||||
import {HasFeatureAccess} from "../feature-wrapper/feature-wrapper.component";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
currentUser: selectCurrentUser,
|
||||
@@ -55,38 +64,53 @@ const mapStateToProps = createStructuredSelector({
|
||||
});
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
setBillEnterContext: (context) => dispatch(setModalContext({ context: context, modal: "billEnter" })),
|
||||
setTimeTicketContext: (context) => dispatch(setModalContext({ context: context, modal: "timeTicket" })),
|
||||
setPaymentContext: (context) => dispatch(setModalContext({ context: context, modal: "payment" })),
|
||||
setReportCenterContext: (context) => dispatch(setModalContext({ context: context, modal: "reportCenter" })),
|
||||
setBillEnterContext: (context) => dispatch(setModalContext({
|
||||
context: context,
|
||||
modal: "billEnter"
|
||||
})),
|
||||
setTimeTicketContext: (context) => dispatch(setModalContext({
|
||||
context: context,
|
||||
modal: "timeTicket"
|
||||
})),
|
||||
setPaymentContext: (context) => dispatch(setModalContext({context: context, modal: "payment"})),
|
||||
setReportCenterContext: (context) => dispatch(setModalContext({
|
||||
context: context,
|
||||
modal: "reportCenter"
|
||||
})),
|
||||
signOutStart: () => dispatch(signOutStart()),
|
||||
setCardPaymentContext: (context) => dispatch(setModalContext({ context: context, modal: "cardPayment" })),
|
||||
setTaskUpsertContext: (context) => dispatch(setModalContext({context: context, modal: 'taskUpsert'})),
|
||||
setCardPaymentContext: (context) => dispatch(setModalContext({
|
||||
context: context,
|
||||
modal: "cardPayment"
|
||||
})),
|
||||
setTaskUpsertContext: (context) => dispatch(setModalContext({
|
||||
context: context,
|
||||
modal: 'taskUpsert'
|
||||
})),
|
||||
});
|
||||
|
||||
function Header({
|
||||
handleMenuClick,
|
||||
currentUser,
|
||||
bodyshop,
|
||||
selectedHeader,
|
||||
signOutStart,
|
||||
setBillEnterContext,
|
||||
setTimeTicketContext,
|
||||
setPaymentContext,
|
||||
setReportCenterContext,
|
||||
recentItems,
|
||||
setCardPaymentContext,
|
||||
setTaskUpsertContext,
|
||||
}) {
|
||||
handleMenuClick,
|
||||
currentUser,
|
||||
bodyshop,
|
||||
selectedHeader,
|
||||
signOutStart,
|
||||
setBillEnterContext,
|
||||
setTimeTicketContext,
|
||||
setPaymentContext,
|
||||
setReportCenterContext,
|
||||
recentItems,
|
||||
setCardPaymentContext,
|
||||
setTaskUpsertContext,
|
||||
}) {
|
||||
const {
|
||||
treatments: { ImEXPay, DmsAp, Simple_Inventory }
|
||||
treatments: {ImEXPay, DmsAp, Simple_Inventory}
|
||||
} = useSplitTreatments({
|
||||
attributes: {},
|
||||
names: ["ImEXPay", "DmsAp", "Simple_Inventory"],
|
||||
splitKey: bodyshop && bodyshop.imexshopid
|
||||
});
|
||||
const [betaSwitch, setBetaSwitch] = useState(false);
|
||||
const { t } = useTranslation();
|
||||
const {t} = useTranslation();
|
||||
|
||||
useEffect(() => {
|
||||
const isBeta = checkBeta();
|
||||
@@ -105,18 +129,18 @@ function Header({
|
||||
InstanceRenderManager({
|
||||
imex: true,
|
||||
rome: true,
|
||||
promanager: HasFeatureAccess({ featureName: "bills", bodyshop })
|
||||
promanager: HasFeatureAccess({featureName: "bills", bodyshop})
|
||||
})
|
||||
) {
|
||||
accountingChildren.push(
|
||||
{
|
||||
key: "bills",
|
||||
icon: <Icon component={FaFileInvoiceDollar} />,
|
||||
icon: <Icon component={FaFileInvoiceDollar}/>,
|
||||
label: <Link to="/manage/bills">{t("menus.header.bills")}</Link>
|
||||
},
|
||||
{
|
||||
key: "enterbills",
|
||||
icon: <Icon component={GiPayMoney} />,
|
||||
icon: <Icon component={GiPayMoney}/>,
|
||||
label: t("menus.header.enterbills"),
|
||||
onClick: () => {
|
||||
setBillEnterContext({
|
||||
@@ -135,7 +159,7 @@ function Header({
|
||||
},
|
||||
{
|
||||
key: "inventory",
|
||||
icon: <Icon component={FaFileInvoiceDollar} />,
|
||||
icon: <Icon component={FaFileInvoiceDollar}/>,
|
||||
label: <Link to="/manage/inventory">{t("menus.header.inventory")}</Link>
|
||||
}
|
||||
);
|
||||
@@ -144,7 +168,7 @@ function Header({
|
||||
InstanceRenderManager({
|
||||
imex: true,
|
||||
rome: true,
|
||||
promanager: HasFeatureAccess({ featureName: "payments", bodyshop })
|
||||
promanager: HasFeatureAccess({featureName: "payments", bodyshop})
|
||||
})
|
||||
) {
|
||||
accountingChildren.push(
|
||||
@@ -153,12 +177,12 @@ function Header({
|
||||
},
|
||||
{
|
||||
key: "allpayments",
|
||||
icon: <BankFilled />,
|
||||
icon: <BankFilled/>,
|
||||
label: <Link to="/manage/payments">{t("menus.header.allpayments")}</Link>
|
||||
},
|
||||
{
|
||||
key: "enterpayments",
|
||||
icon: <Icon component={FaCreditCard} />,
|
||||
icon: <Icon component={FaCreditCard}/>,
|
||||
label: t("menus.header.enterpayment"),
|
||||
onClick: () => {
|
||||
setPaymentContext({
|
||||
@@ -173,7 +197,7 @@ function Header({
|
||||
if (ImEXPay.treatment === "on") {
|
||||
accountingChildren.push({
|
||||
key: "entercardpayments",
|
||||
icon: <Icon component={FaCreditCard} />,
|
||||
icon: <Icon component={FaCreditCard}/>,
|
||||
label: t("menus.header.entercardpayment"),
|
||||
onClick: () => {
|
||||
setCardPaymentContext({
|
||||
@@ -188,7 +212,7 @@ function Header({
|
||||
InstanceRenderManager({
|
||||
imex: true,
|
||||
rome: true,
|
||||
promanager: HasFeatureAccess({ featureName: "timetickets", bodyshop })
|
||||
promanager: HasFeatureAccess({featureName: "timetickets", bodyshop})
|
||||
})
|
||||
) {
|
||||
accountingChildren.push(
|
||||
@@ -197,7 +221,7 @@ function Header({
|
||||
},
|
||||
{
|
||||
key: "timetickets",
|
||||
icon: <FieldTimeOutlined />,
|
||||
icon: <FieldTimeOutlined/>,
|
||||
label: <Link to="/manage/timetickets">{t("menus.header.timetickets")}</Link>
|
||||
}
|
||||
);
|
||||
@@ -205,14 +229,14 @@ function Header({
|
||||
if (bodyshop?.md_tasks_presets?.use_approvals) {
|
||||
accountingChildren.push({
|
||||
key: "ttapprovals",
|
||||
icon: <FieldTimeOutlined />,
|
||||
icon: <FieldTimeOutlined/>,
|
||||
label: <Link to="/manage/ttapprovals">{t("menus.header.ttapprovals")}</Link>
|
||||
});
|
||||
}
|
||||
accountingChildren.push(
|
||||
{
|
||||
key: "entertimetickets",
|
||||
icon: <Icon component={GiPlayerTime} />,
|
||||
icon: <Icon component={GiPlayerTime}/>,
|
||||
label: t("menus.header.entertimeticket"),
|
||||
onClick: () => {
|
||||
setTimeTicketContext({
|
||||
@@ -234,7 +258,8 @@ function Header({
|
||||
const accountingExportChildren = [
|
||||
{
|
||||
key: "receivables",
|
||||
label: <Link to="/manage/accounting/receivables">{t("menus.header.accounting-receivables")}</Link>
|
||||
label: <Link
|
||||
to="/manage/accounting/receivables">{t("menus.header.accounting-receivables")}</Link>
|
||||
}
|
||||
];
|
||||
|
||||
@@ -266,12 +291,12 @@ function Header({
|
||||
InstanceRenderManager({
|
||||
imex: true,
|
||||
rome: true,
|
||||
promanager: HasFeatureAccess({ featureName: "export", bodyshop })
|
||||
promanager: HasFeatureAccess({featureName: "export", bodyshop})
|
||||
})
|
||||
) {
|
||||
accountingChildren.push({
|
||||
key: "accountingexport",
|
||||
icon: <ExportOutlined />,
|
||||
icon: <ExportOutlined/>,
|
||||
label: t("menus.header.export"),
|
||||
children: accountingExportChildren
|
||||
});
|
||||
@@ -280,44 +305,44 @@ function Header({
|
||||
const menuItems = [
|
||||
{
|
||||
key: "home",
|
||||
icon: <HomeFilled />,
|
||||
icon: <HomeFilled/>,
|
||||
label: <Link to="/manage/">{t("menus.header.home")}</Link>
|
||||
},
|
||||
{
|
||||
key: "schedule",
|
||||
icon: <Icon component={FaCalendarAlt} />,
|
||||
icon: <Icon component={FaCalendarAlt}/>,
|
||||
label: <Link to="/manage/schedule">{t("menus.header.schedule")}</Link>
|
||||
},
|
||||
{
|
||||
key: "jobssubmenu",
|
||||
id: "header-jobs",
|
||||
icon: <Icon component={FaCarCrash} />,
|
||||
icon: <Icon component={FaCarCrash}/>,
|
||||
label: t("menus.header.jobs"),
|
||||
children: [
|
||||
{
|
||||
key: "activejobs",
|
||||
icon: <FileFilled />,
|
||||
icon: <FileFilled/>,
|
||||
label: <Link to="/manage/jobs">{t("menus.header.activejobs")}</Link>
|
||||
},
|
||||
{
|
||||
key: "readyjobs",
|
||||
icon: <CheckCircleOutlined />,
|
||||
icon: <CheckCircleOutlined/>,
|
||||
label: <Link to="/manage/jobs/ready">{t("menus.header.readyjobs")}</Link>
|
||||
},
|
||||
{
|
||||
key: "parts-queue",
|
||||
icon: <ToolFilled />,
|
||||
icon: <ToolFilled/>,
|
||||
label: <Link to="/manage/partsqueue">{t("menus.header.parts-queue")}</Link>
|
||||
},
|
||||
{
|
||||
key: "availablejobs",
|
||||
id: "header-jobs-available",
|
||||
icon: <ImportOutlined />,
|
||||
icon: <ImportOutlined/>,
|
||||
label: <Link to="/manage/available">{t("menus.header.availablejobs")}</Link>
|
||||
},
|
||||
{
|
||||
key: "newjob",
|
||||
icon: <FileAddOutlined />,
|
||||
icon: <FileAddOutlined/>,
|
||||
label: <Link to="/manage/jobs/new">{t("menus.header.newjob")}</Link>
|
||||
},
|
||||
{
|
||||
@@ -325,7 +350,7 @@ function Header({
|
||||
},
|
||||
{
|
||||
key: "alljobs",
|
||||
icon: <UnorderedListOutlined />,
|
||||
icon: <UnorderedListOutlined/>,
|
||||
label: <Link to="/manage/jobs/all">{t("menus.header.alljobs")}</Link>
|
||||
},
|
||||
{
|
||||
@@ -333,54 +358,54 @@ function Header({
|
||||
},
|
||||
{
|
||||
key: "productionlist",
|
||||
icon: <ScheduleOutlined />,
|
||||
icon: <ScheduleOutlined/>,
|
||||
label: <Link to="/manage/production/list">{t("menus.header.productionlist")}</Link>
|
||||
},
|
||||
...(InstanceRenderManager({
|
||||
imex: true,
|
||||
rome: true,
|
||||
promanager: HasFeatureAccess({ featureName: "visualboard", bodyshop })
|
||||
promanager: HasFeatureAccess({featureName: "visualboard", bodyshop})
|
||||
})
|
||||
? [
|
||||
{
|
||||
key: "productionboard",
|
||||
icon: <Icon component={BsKanban} />,
|
||||
label: <Link to="/manage/production/board">{t("menus.header.productionboard")}</Link>
|
||||
}
|
||||
]
|
||||
{
|
||||
key: "productionboard",
|
||||
icon: <Icon component={BsKanban}/>,
|
||||
label: <Link to="/manage/production/board">{t("menus.header.productionboard")}</Link>
|
||||
}
|
||||
]
|
||||
: []),
|
||||
|
||||
...(InstanceRenderManager({
|
||||
imex: true,
|
||||
rome: true,
|
||||
promanager: HasFeatureAccess({ featureName: "scoreboard", bodyshop })
|
||||
promanager: HasFeatureAccess({featureName: "scoreboard", bodyshop})
|
||||
})
|
||||
? [
|
||||
{
|
||||
type: "divider"
|
||||
},
|
||||
{
|
||||
key: "scoreboard",
|
||||
icon: <LineChartOutlined />,
|
||||
label: <Link to="/manage/scoreboard">{t("menus.header.scoreboard")}</Link>
|
||||
}
|
||||
]
|
||||
{
|
||||
type: "divider"
|
||||
},
|
||||
{
|
||||
key: "scoreboard",
|
||||
icon: <LineChartOutlined/>,
|
||||
label: <Link to="/manage/scoreboard">{t("menus.header.scoreboard")}</Link>
|
||||
}
|
||||
]
|
||||
: [])
|
||||
]
|
||||
},
|
||||
{
|
||||
key: "customers",
|
||||
icon: <UserOutlined />,
|
||||
icon: <UserOutlined/>,
|
||||
label: t("menus.header.customers"),
|
||||
children: [
|
||||
{
|
||||
key: "owners",
|
||||
icon: <TeamOutlined />,
|
||||
icon: <TeamOutlined/>,
|
||||
label: <Link to="/manage/owners">{t("menus.header.owners")}</Link>
|
||||
},
|
||||
{
|
||||
key: "vehicles",
|
||||
icon: <CarFilled />,
|
||||
icon: <CarFilled/>,
|
||||
label: <Link to="/manage/vehicles">{t("menus.header.vehicles")}</Link>
|
||||
}
|
||||
]
|
||||
@@ -391,63 +416,65 @@ function Header({
|
||||
promanager: false // HasFeatureAccess({ featureName: 'courtesycars', bodyshop }),
|
||||
})
|
||||
? [
|
||||
{
|
||||
key: "ccs",
|
||||
icon: <CarFilled />,
|
||||
label: t("menus.header.courtesycars"),
|
||||
children: [
|
||||
{
|
||||
key: "courtesycarsall",
|
||||
icon: <CarFilled />,
|
||||
label: <Link to="/manage/courtesycars">{t("menus.header.courtesycars-all")}</Link>
|
||||
},
|
||||
{
|
||||
key: "contracts",
|
||||
icon: <FileFilled />,
|
||||
label: <Link to="/manage/courtesycars/contracts">{t("menus.header.courtesycars-contracts")}</Link>
|
||||
},
|
||||
{
|
||||
key: "newcontract",
|
||||
icon: <FileAddFilled />,
|
||||
label: <Link to="/manage/courtesycars/contracts/new">{t("menus.header.courtesycars-newcontract")}</Link>
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
{
|
||||
key: "ccs",
|
||||
icon: <CarFilled/>,
|
||||
label: t("menus.header.courtesycars"),
|
||||
children: [
|
||||
{
|
||||
key: "courtesycarsall",
|
||||
icon: <CarFilled/>,
|
||||
label: <Link to="/manage/courtesycars">{t("menus.header.courtesycars-all")}</Link>
|
||||
},
|
||||
{
|
||||
key: "contracts",
|
||||
icon: <FileFilled/>,
|
||||
label: <Link
|
||||
to="/manage/courtesycars/contracts">{t("menus.header.courtesycars-contracts")}</Link>
|
||||
},
|
||||
{
|
||||
key: "newcontract",
|
||||
icon: <FileAddFilled/>,
|
||||
label: <Link
|
||||
to="/manage/courtesycars/contracts/new">{t("menus.header.courtesycars-newcontract")}</Link>
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
: []),
|
||||
|
||||
...(accountingChildren.length > 0
|
||||
? [
|
||||
{
|
||||
key: "accounting",
|
||||
icon: <DollarCircleFilled />,
|
||||
label: t("menus.header.accounting"),
|
||||
children: accountingChildren
|
||||
}
|
||||
]
|
||||
{
|
||||
key: "accounting",
|
||||
icon: <DollarCircleFilled/>,
|
||||
label: t("menus.header.accounting"),
|
||||
children: accountingChildren
|
||||
}
|
||||
]
|
||||
: []),
|
||||
{
|
||||
key: "phonebook",
|
||||
icon: <PhoneOutlined />,
|
||||
icon: <PhoneOutlined/>,
|
||||
label: <Link to="/manage/phonebook">{t("menus.header.phonebook")}</Link>
|
||||
},
|
||||
...(InstanceRenderManager({
|
||||
imex: true,
|
||||
rome: true,
|
||||
promanager: HasFeatureAccess({ featureName: "media", bodyshop })
|
||||
promanager: HasFeatureAccess({featureName: "media", bodyshop})
|
||||
})
|
||||
? [
|
||||
{
|
||||
key: "temporarydocs",
|
||||
icon: <PaperClipOutlined />,
|
||||
label: <Link to="/manage/temporarydocs">{t("menus.header.temporarydocs")}</Link>
|
||||
}
|
||||
]
|
||||
{
|
||||
key: "temporarydocs",
|
||||
icon: <PaperClipOutlined/>,
|
||||
label: <Link to="/manage/temporarydocs">{t("menus.header.temporarydocs")}</Link>
|
||||
}
|
||||
]
|
||||
: []),
|
||||
{
|
||||
key: 'tasks',
|
||||
id: 'tasks',
|
||||
icon: <FaTasks />,
|
||||
icon: <FaTasks/>,
|
||||
label: t('menus.header.tasks'),
|
||||
children: [
|
||||
{
|
||||
@@ -475,22 +502,22 @@ function Header({
|
||||
},
|
||||
{
|
||||
key: 'shopsubmenu',
|
||||
icon: <SettingOutlined />,
|
||||
icon: <SettingOutlined/>,
|
||||
label: t("menus.header.shop"),
|
||||
children: [
|
||||
{
|
||||
key: "shop",
|
||||
icon: <Icon component={GiSettingsKnobs} />,
|
||||
icon: <Icon component={GiSettingsKnobs}/>,
|
||||
label: <Link to="/manage/shop?tab=info">{t("menus.header.shop_config")}</Link>
|
||||
},
|
||||
{
|
||||
key: "dashboard",
|
||||
icon: <DashboardFilled />,
|
||||
icon: <DashboardFilled/>,
|
||||
label: <Link to="/manage/dashboard">{t("menus.header.dashboard")}</Link>
|
||||
},
|
||||
{
|
||||
key: "reportcenter",
|
||||
icon: <BarChartOutlined />,
|
||||
icon: <BarChartOutlined/>,
|
||||
label: t("menus.header.reportcenter"),
|
||||
onClick: () => {
|
||||
setReportCenterContext({
|
||||
@@ -501,21 +528,21 @@ function Header({
|
||||
},
|
||||
{
|
||||
key: "shop-vendors",
|
||||
icon: <Icon component={IoBusinessOutline} />,
|
||||
icon: <Icon component={IoBusinessOutline}/>,
|
||||
label: <Link to="/manage/shop/vendors">{t("menus.header.shop_vendors")}</Link>
|
||||
},
|
||||
...(InstanceRenderManager({
|
||||
imex: true,
|
||||
rome: true,
|
||||
promanager: HasFeatureAccess({ featureName: "csi", bodyshop })
|
||||
promanager: HasFeatureAccess({featureName: "csi", bodyshop})
|
||||
})
|
||||
? [
|
||||
{
|
||||
key: "shop-csi",
|
||||
icon: <Icon component={RiSurveyLine} />,
|
||||
label: <Link to="/manage/shop/csi">{t("menus.header.shop_csi")}</Link>
|
||||
}
|
||||
]
|
||||
{
|
||||
key: "shop-csi",
|
||||
icon: <Icon component={RiSurveyLine}/>,
|
||||
label: <Link to="/manage/shop/csi">{t("menus.header.shop_csi")}</Link>
|
||||
}
|
||||
]
|
||||
: [])
|
||||
]
|
||||
},
|
||||
@@ -525,14 +552,14 @@ function Header({
|
||||
children: [
|
||||
{
|
||||
key: "signout",
|
||||
icon: <Icon component={FiLogOut} />,
|
||||
icon: <Icon component={FiLogOut}/>,
|
||||
danger: true,
|
||||
label: t("user.actions.signout"),
|
||||
onClick: () => signOutStart()
|
||||
},
|
||||
{
|
||||
key: "help",
|
||||
icon: <Icon component={QuestionCircleFilled} />,
|
||||
icon: <Icon component={QuestionCircleFilled}/>,
|
||||
label: t("menus.header.help"),
|
||||
onClick: () => {
|
||||
window.open(
|
||||
@@ -558,19 +585,19 @@ function Header({
|
||||
...(InstanceRenderManager({
|
||||
imex: true,
|
||||
rome: true,
|
||||
promanager: HasFeatureAccess({ featureName: "timetickets", bodyshop })
|
||||
promanager: HasFeatureAccess({featureName: "timetickets", bodyshop})
|
||||
})
|
||||
? [
|
||||
{
|
||||
key: "shiftclock",
|
||||
icon: <Icon component={GiPlayerTime} />,
|
||||
label: <Link to="/manage/shiftclock">{t("menus.header.shiftclock")}</Link>
|
||||
}
|
||||
]
|
||||
{
|
||||
key: "shiftclock",
|
||||
icon: <Icon component={GiPlayerTime}/>,
|
||||
label: <Link to="/manage/shiftclock">{t("menus.header.shiftclock")}</Link>
|
||||
}
|
||||
]
|
||||
: []),
|
||||
{
|
||||
key: "profile",
|
||||
icon: <UserOutlined />,
|
||||
icon: <UserOutlined/>,
|
||||
label: <Link to="/manage/profile">{t("menus.currentuser.profile")}</Link>
|
||||
}
|
||||
// {
|
||||
@@ -604,7 +631,7 @@ function Header({
|
||||
},
|
||||
{
|
||||
key: "recent",
|
||||
icon: <ClockCircleFilled />,
|
||||
icon: <ClockCircleFilled/>,
|
||||
children: recentItems.map((i, idx) => ({
|
||||
key: idx,
|
||||
label: <Link to={i.url}>{i.label}</Link>
|
||||
@@ -618,7 +645,7 @@ function Header({
|
||||
imex: () => {
|
||||
menuItems.push({
|
||||
key: "beta-switch",
|
||||
style: { marginLeft: "auto" },
|
||||
style: {marginLeft: "auto"},
|
||||
label: (
|
||||
<Tooltip
|
||||
title={`A more modern ${InstanceRenderManager({
|
||||
@@ -627,9 +654,9 @@ function Header({
|
||||
promanager: t("titles.promanager")
|
||||
})} is ready for you to try! You can switch back at any time.`}
|
||||
>
|
||||
<InfoCircleOutlined />
|
||||
<span style={{ marginRight: 8 }}>Try the new app</span>
|
||||
<Switch checked={betaSwitch} onChange={betaSwitchChange} />
|
||||
<InfoCircleOutlined/>
|
||||
<span style={{marginRight: 8}}>Try the new app</span>
|
||||
<Switch checked={betaSwitch} onChange={betaSwitchChange}/>
|
||||
</Tooltip>
|
||||
)
|
||||
});
|
||||
|
||||
@@ -1,20 +1,17 @@
|
||||
import { useQuery } from "@apollo/client";
|
||||
import { Col, Divider, Row, Skeleton, Space, Timeline, Typography } from "antd";
|
||||
import {useQuery} from "@apollo/client";
|
||||
import {Col, Divider, Row, Skeleton, Space, Timeline, Typography} from "antd";
|
||||
import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Link } from "react-router-dom";
|
||||
import { GET_JOB_LINE_ORDERS } from "../../graphql/jobs.queries";
|
||||
import {useTranslation} from "react-i18next";
|
||||
import {Link} from "react-router-dom";
|
||||
import {GET_JOB_LINE_ORDERS} from "../../graphql/jobs.queries";
|
||||
import CurrencyFormatter from "../../utils/CurrencyFormatter";
|
||||
import { DateFormatter } from "../../utils/DateFormatter";
|
||||
import {DateFormatter} from "../../utils/DateFormatter";
|
||||
import AlertComponent from "../alert/alert.component";
|
||||
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import {connect} from "react-redux";
|
||||
import {createStructuredSelector} from "reselect";
|
||||
import {selectBodyshop, selectCurrentUser} from "../../redux/user/user.selectors";
|
||||
import {
|
||||
QUERY_JOB_TASKS_PAGINATED,
|
||||
QUERY_JOBLINE_TASKS_PAGINATED
|
||||
} from "../../graphql/tasks.queries.js";
|
||||
import {QUERY_JOBLINE_TASKS_PAGINATED} from "../../graphql/tasks.queries.js";
|
||||
import TaskListContainer from "../task-list/task-list.container.jsx";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
@@ -26,9 +23,9 @@ const mapDispatchToProps = (dispatch) => ({
|
||||
});
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(JobLinesExpander);
|
||||
|
||||
export function JobLinesExpander({ jobline, jobid, bodyshop, currentUser }) {
|
||||
const { t } = useTranslation();
|
||||
const { loading, error, data } = useQuery(GET_JOB_LINE_ORDERS, {
|
||||
export function JobLinesExpander({jobline, jobid, bodyshop, currentUser}) {
|
||||
const {t} = useTranslation();
|
||||
const {loading, error, data} = useQuery(GET_JOB_LINE_ORDERS, {
|
||||
fetchPolicy: "network-only",
|
||||
nextFetchPolicy: "network-only",
|
||||
variables: {
|
||||
@@ -36,8 +33,8 @@ export function JobLinesExpander({ jobline, jobid, bodyshop, currentUser }) {
|
||||
}
|
||||
});
|
||||
|
||||
if (loading) return <Skeleton />;
|
||||
if (error) return <AlertComponent message={error.message} type="error" />;
|
||||
if (loading) return <Skeleton/>;
|
||||
if (error) return <AlertComponent message={error.message} type="error"/>;
|
||||
|
||||
return (
|
||||
<Row>
|
||||
@@ -47,23 +44,23 @@ export function JobLinesExpander({ jobline, jobid, bodyshop, currentUser }) {
|
||||
items={
|
||||
data.parts_order_lines.length > 0
|
||||
? data.parts_order_lines.map((line) => ({
|
||||
key: line.id,
|
||||
children: (
|
||||
<Space split={<Divider type="vertical" />} wrap>
|
||||
<Link to={`/manage/jobs/${jobid}?partsorderid=${line.parts_order.id}`}>
|
||||
{line.parts_order.order_number}
|
||||
</Link>
|
||||
<DateFormatter>{line.parts_order.order_date}</DateFormatter>
|
||||
{line.parts_order.vendor.name}
|
||||
</Space>
|
||||
)
|
||||
}))
|
||||
key: line.id,
|
||||
children: (
|
||||
<Space split={<Divider type="vertical"/>} wrap>
|
||||
<Link to={`/manage/jobs/${jobid}?partsorderid=${line.parts_order.id}`}>
|
||||
{line.parts_order.order_number}
|
||||
</Link>
|
||||
<DateFormatter>{line.parts_order.order_date}</DateFormatter>
|
||||
{line.parts_order.vendor.name}
|
||||
</Space>
|
||||
)
|
||||
}))
|
||||
: [
|
||||
{
|
||||
key: "no-orders",
|
||||
children: t("parts_orders.labels.notyetordered")
|
||||
}
|
||||
]
|
||||
{
|
||||
key: "no-orders",
|
||||
children: t("parts_orders.labels.notyetordered")
|
||||
}
|
||||
]
|
||||
}
|
||||
/>{" "}
|
||||
</Col>
|
||||
@@ -73,39 +70,39 @@ export function JobLinesExpander({ jobline, jobid, bodyshop, currentUser }) {
|
||||
items={
|
||||
data.billlines.length > 0
|
||||
? data.billlines.map((line) => ({
|
||||
key: line.id,
|
||||
children: (
|
||||
<Row wrap>
|
||||
<Col span={4}>
|
||||
<Link to={`/manage/jobs/${jobid}?tab=partssublet&billid=${line.bill.id}`}>
|
||||
{line.bill.invoice_number}
|
||||
</Link>
|
||||
</Col>
|
||||
<Col span={4}>
|
||||
key: line.id,
|
||||
children: (
|
||||
<Row wrap>
|
||||
<Col span={4}>
|
||||
<Link to={`/manage/jobs/${jobid}?tab=partssublet&billid=${line.bill.id}`}>
|
||||
{line.bill.invoice_number}
|
||||
</Link>
|
||||
</Col>
|
||||
<Col span={4}>
|
||||
<span>
|
||||
{`${t("billlines.fields.actual_price")}: `}
|
||||
<CurrencyFormatter>{line.actual_price}</CurrencyFormatter>
|
||||
</span>
|
||||
</Col>
|
||||
<Col span={4}>
|
||||
</Col>
|
||||
<Col span={4}>
|
||||
<span>
|
||||
{`${t("billlines.fields.actual_cost")}: `}
|
||||
<CurrencyFormatter>{line.actual_cost}</CurrencyFormatter>
|
||||
</span>
|
||||
</Col>
|
||||
<Col span={4}>
|
||||
<DateFormatter>{line.bill.date}</DateFormatter>
|
||||
</Col>
|
||||
<Col span={4}> {line.bill.vendor.name}</Col>
|
||||
</Row>
|
||||
)
|
||||
}))
|
||||
</Col>
|
||||
<Col span={4}>
|
||||
<DateFormatter>{line.bill.date}</DateFormatter>
|
||||
</Col>
|
||||
<Col span={4}> {line.bill.vendor.name}</Col>
|
||||
</Row>
|
||||
)
|
||||
}))
|
||||
: [
|
||||
{
|
||||
key: "no-orders",
|
||||
children: t("parts_orders.labels.notyetordered")
|
||||
}
|
||||
]
|
||||
{
|
||||
key: "no-orders",
|
||||
children: t("parts_orders.labels.notyetordered")
|
||||
}
|
||||
]
|
||||
}
|
||||
/>
|
||||
</Col>
|
||||
@@ -115,27 +112,31 @@ export function JobLinesExpander({ jobline, jobid, bodyshop, currentUser }) {
|
||||
items={
|
||||
data.parts_dispatch_lines.length > 0
|
||||
? data.parts_dispatch_lines.map((line) => ({
|
||||
key: line.id,
|
||||
children: (
|
||||
<Space split={<Divider type="vertical" />} wrap>
|
||||
<Link to={`/manage/jobs/${jobid}?partsorderid=${line.id}`}>{line.parts_dispatch.number}</Link>
|
||||
{bodyshop.employees.find((e) => e.id === line.parts_dispatch.employeeid)?.first_name}
|
||||
<Space>
|
||||
{t("parts_dispatch_lines.fields.accepted_at")}
|
||||
<DateFormatter>{line.accepted_at}</DateFormatter>
|
||||
</Space>
|
||||
key: line.id,
|
||||
children: (
|
||||
<Space split={<Divider type="vertical"/>} wrap>
|
||||
<Link
|
||||
to={`/manage/jobs/${jobid}?partsorderid=${line.id}`}>{line.parts_dispatch.number}</Link>
|
||||
{bodyshop.employees.find((e) => e.id === line.parts_dispatch.employeeid)?.first_name}
|
||||
<Space>
|
||||
{t("parts_dispatch_lines.fields.accepted_at")}
|
||||
<DateFormatter>{line.accepted_at}</DateFormatter>
|
||||
</Space>
|
||||
)
|
||||
}))
|
||||
</Space>
|
||||
)
|
||||
}))
|
||||
: {
|
||||
key: "dispatch-lines",
|
||||
children: t("parts_orders.labels.notyetordered")
|
||||
}
|
||||
key: "dispatch-lines",
|
||||
children: t("parts_orders.labels.notyetordered")
|
||||
}
|
||||
}
|
||||
/>
|
||||
</Col>
|
||||
<Col md={24} lg={24}>
|
||||
<TaskListContainer currentUser={currentUser} bodyshop={bodyshop} parentJobId={jobid} relationshipType={'joblineid'} relationshipId={jobline.id} query={QUERY_JOBLINE_TASKS_PAGINATED} titleTranslation='tasks.titles.job_tasks'/>
|
||||
<TaskListContainer currentUser={currentUser} bodyshop={bodyshop} parentJobId={jobid}
|
||||
relationshipType={'joblineid'} relationshipId={jobline.id}
|
||||
query={QUERY_JOBLINE_TASKS_PAGINATED}
|
||||
titleTranslation='tasks.titles.job_tasks'/>
|
||||
</Col>
|
||||
</Row>
|
||||
);
|
||||
|
||||
@@ -457,7 +457,7 @@ export function JobLinesComponent({
|
||||
disabled={(job && !job.converted) || (selectedLines.length > 0 ? false : true) || jobRO || technician}
|
||||
onClick={() => {
|
||||
setBillEnterContext({
|
||||
actions: { refetch: refetch },
|
||||
actions: {refetch: refetch },
|
||||
context: {
|
||||
disableInvNumber: true,
|
||||
job: { id: job.id },
|
||||
@@ -465,7 +465,7 @@ export function JobLinesComponent({
|
||||
vendorid: bodyshop.inhousevendorid,
|
||||
invoice_number: "ih",
|
||||
isinhouse: true,
|
||||
date: new dayjs(),
|
||||
date: dayjs(),
|
||||
total: 0,
|
||||
billlines: selectedLines.map((p) => {
|
||||
return {
|
||||
|
||||
@@ -154,7 +154,7 @@ export function JobLineConvertToLabor({ children, jobline, job, insertAuditTrail
|
||||
setLoading(true);
|
||||
|
||||
form.setFieldsValue({
|
||||
// date: new dayjs(),
|
||||
// date: dayjs(),
|
||||
// bodyhrs: Math.round(v.bodyhrs * 10) / 10,
|
||||
// painthrs: Math.round(v.painthrs * 10) / 10,
|
||||
});
|
||||
|
||||
@@ -1,35 +1,51 @@
|
||||
import { DownCircleFilled } from "@ant-design/icons";
|
||||
import { useApolloClient, useMutation } from "@apollo/client";
|
||||
import { Button, Card, Dropdown, Form, Input, Modal, notification, Popconfirm, Popover, Select, Space } from "antd";
|
||||
import React, { useMemo, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { Link, useNavigate } from "react-router-dom";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { auth, logImEXEvent } from "../../firebase/firebase.utils";
|
||||
import { CANCEL_APPOINTMENTS_BY_JOB_ID, INSERT_MANUAL_APPT } from "../../graphql/appointments.queries";
|
||||
import { DELETE_JOB, UPDATE_JOB, VOID_JOB } from "../../graphql/jobs.queries";
|
||||
import { insertAuditTrail } from "../../redux/application/application.actions";
|
||||
import { selectJobReadOnly } from "../../redux/application/application.selectors";
|
||||
import { setModalContext } from "../../redux/modals/modals.actions";
|
||||
import { selectBodyshop, selectCurrentUser } from "../../redux/user/user.selectors";
|
||||
import {DownCircleFilled} from "@ant-design/icons";
|
||||
import {useApolloClient, useMutation} from "@apollo/client";
|
||||
import {
|
||||
Button,
|
||||
Card,
|
||||
Dropdown,
|
||||
Form,
|
||||
Input,
|
||||
Modal,
|
||||
notification,
|
||||
Popconfirm,
|
||||
Popover,
|
||||
Select,
|
||||
Space
|
||||
} from "antd";
|
||||
import React, {useMemo, useState} from "react";
|
||||
import {useTranslation} from "react-i18next";
|
||||
import {connect} from "react-redux";
|
||||
import {Link, useNavigate} from "react-router-dom";
|
||||
import {createStructuredSelector} from "reselect";
|
||||
import {auth, logImEXEvent} from "../../firebase/firebase.utils";
|
||||
import {
|
||||
CANCEL_APPOINTMENTS_BY_JOB_ID,
|
||||
INSERT_MANUAL_APPT
|
||||
} from "../../graphql/appointments.queries";
|
||||
import {DELETE_JOB, UPDATE_JOB, VOID_JOB} from "../../graphql/jobs.queries";
|
||||
import {insertAuditTrail} from "../../redux/application/application.actions";
|
||||
import {selectJobReadOnly} from "../../redux/application/application.selectors";
|
||||
import {setModalContext} from "../../redux/modals/modals.actions";
|
||||
import {selectBodyshop, selectCurrentUser} from "../../redux/user/user.selectors";
|
||||
import AuditTrailMapping from "../../utils/AuditTrailMappings";
|
||||
import RbacWrapper from "../rbac-wrapper/rbac-wrapper.component";
|
||||
import AddToProduction from "./jobs-detail-header-actions.addtoproduction.util";
|
||||
import DuplicateJob from "./jobs-detail-header-actions.duplicate.util";
|
||||
import axios from "axios";
|
||||
import { setEmailOptions } from "../../redux/email/email.actions";
|
||||
import { openChatByPhone, setMessage } from "../../redux/messaging/messaging.actions";
|
||||
import { GET_CURRENT_QUESTIONSET_ID, INSERT_CSI } from "../../graphql/csi.queries";
|
||||
import { TemplateList } from "../../utils/TemplateConstants";
|
||||
import {setEmailOptions} from "../../redux/email/email.actions";
|
||||
import {openChatByPhone, setMessage} from "../../redux/messaging/messaging.actions";
|
||||
import {GET_CURRENT_QUESTIONSET_ID, INSERT_CSI} from "../../graphql/csi.queries";
|
||||
import {TemplateList} from "../../utils/TemplateConstants";
|
||||
import parsePhoneNumber from "libphonenumber-js";
|
||||
import { HasFeatureAccess } from "../feature-wrapper/feature-wrapper.component";
|
||||
import { DateTimeFormatter } from "../../utils/DateFormatter";
|
||||
import {HasFeatureAccess} from "../feature-wrapper/feature-wrapper.component";
|
||||
import {DateTimeFormatter} from "../../utils/DateFormatter";
|
||||
import FormDateTimePickerComponent from "../form-date-time-picker/form-date-time-picker.component";
|
||||
import dayjs from "../../utils/day";
|
||||
import { useSplitTreatments } from "@splitsoftware/splitio-react";
|
||||
import {useSplitTreatments} from "@splitsoftware/splitio-react";
|
||||
import InstanceRenderManager from "../../utils/instanceRenderMgr";
|
||||
import JobsDetailHeaderActionsToggleProduction from "./jobs-detail-header-actions.toggle-production";
|
||||
import JobsDetailHeaderActionsToggleProduction
|
||||
from "./jobs-detail-header-actions.toggle-production";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
@@ -38,38 +54,57 @@ const mapStateToProps = createStructuredSelector({
|
||||
});
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
setScheduleContext: (context) => dispatch(setModalContext({ context: context, modal: "schedule" })),
|
||||
setBillEnterContext: (context) => dispatch(setModalContext({ context: context, modal: "billEnter" })),
|
||||
setPaymentContext: (context) => dispatch(setModalContext({ context: context, modal: "payment" })),
|
||||
setJobCostingContext: (context) => dispatch(setModalContext({ context: context, modal: "jobCosting" })),
|
||||
setTimeTicketContext: (context) => dispatch(setModalContext({ context: context, modal: "timeTicket" })),
|
||||
setCardPaymentContext: (context) => dispatch(setModalContext({ context: context, modal: "cardPayment" })),
|
||||
insertAuditTrail: ({ jobid, operation, type }) => dispatch(insertAuditTrail({ jobid, operation, type })),
|
||||
setTimeTicketTaskContext: (context) => dispatch(setModalContext({ context: context, modal: "timeTicketTask" })),
|
||||
setScheduleContext: (context) => dispatch(setModalContext({context: context, modal: "schedule"})),
|
||||
setBillEnterContext: (context) => dispatch(setModalContext({
|
||||
context: context,
|
||||
modal: "billEnter"
|
||||
})),
|
||||
setPaymentContext: (context) => dispatch(setModalContext({context: context, modal: "payment"})),
|
||||
setJobCostingContext: (context) => dispatch(setModalContext({
|
||||
context: context,
|
||||
modal: "jobCosting"
|
||||
})),
|
||||
setTimeTicketContext: (context) => dispatch(setModalContext({
|
||||
context: context,
|
||||
modal: "timeTicket"
|
||||
})),
|
||||
setCardPaymentContext: (context) => dispatch(setModalContext({
|
||||
context: context,
|
||||
modal: "cardPayment"
|
||||
})),
|
||||
insertAuditTrail: ({jobid, operation, type}) => dispatch(insertAuditTrail({
|
||||
jobid,
|
||||
operation,
|
||||
type
|
||||
})),
|
||||
setTimeTicketTaskContext: (context) => dispatch(setModalContext({
|
||||
context: context,
|
||||
modal: "timeTicketTask"
|
||||
})),
|
||||
setEmailOptions: (e) => dispatch(setEmailOptions(e)),
|
||||
openChatByPhone: (phone) => dispatch(openChatByPhone(phone)),
|
||||
setMessage: (text) => dispatch(setMessage(text))
|
||||
});
|
||||
|
||||
export function JobsDetailHeaderActions({
|
||||
job,
|
||||
bodyshop,
|
||||
currentUser,
|
||||
refetch,
|
||||
setScheduleContext,
|
||||
setBillEnterContext,
|
||||
setPaymentContext,
|
||||
setJobCostingContext,
|
||||
jobRO,
|
||||
setTimeTicketContext,
|
||||
setCardPaymentContext,
|
||||
insertAuditTrail,
|
||||
setEmailOptions,
|
||||
openChatByPhone,
|
||||
setMessage,
|
||||
setTimeTicketTaskContext
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
job,
|
||||
bodyshop,
|
||||
currentUser,
|
||||
refetch,
|
||||
setScheduleContext,
|
||||
setBillEnterContext,
|
||||
setPaymentContext,
|
||||
setJobCostingContext,
|
||||
jobRO,
|
||||
setTimeTicketContext,
|
||||
setCardPaymentContext,
|
||||
insertAuditTrail,
|
||||
setEmailOptions,
|
||||
openChatByPhone,
|
||||
setMessage,
|
||||
setTimeTicketTaskContext
|
||||
}) {
|
||||
const {t} = useTranslation();
|
||||
const client = useApolloClient();
|
||||
const history = useNavigate();
|
||||
const [form] = Form.useForm();
|
||||
@@ -83,7 +118,7 @@ export function JobsDetailHeaderActions({
|
||||
const [cancelAllAppointments] = useMutation(CANCEL_APPOINTMENTS_BY_JOB_ID);
|
||||
|
||||
const {
|
||||
treatments: { ImEXPay }
|
||||
treatments: {ImEXPay}
|
||||
} = useSplitTreatments({
|
||||
attributes: {},
|
||||
names: ["ImEXPay"],
|
||||
@@ -117,7 +152,7 @@ export function JobsDetailHeaderActions({
|
||||
DuplicateJob(
|
||||
client,
|
||||
job.id,
|
||||
{ defaultOpenStatus: bodyshop.md_ro_statuses.default_imported },
|
||||
{defaultOpenStatus: bodyshop.md_ro_statuses.default_imported},
|
||||
(newJobId) => {
|
||||
history(`/manage/jobs/${newJobId}`);
|
||||
notification["success"]({
|
||||
@@ -128,7 +163,7 @@ export function JobsDetailHeaderActions({
|
||||
);
|
||||
|
||||
const handleDuplicateConfirm = () =>
|
||||
DuplicateJob(client, job.id, { defaultOpenStatus: bodyshop.md_ro_statuses.default_imported }, (newJobId) => {
|
||||
DuplicateJob(client, job.id, {defaultOpenStatus: bodyshop.md_ro_statuses.default_imported}, (newJobId) => {
|
||||
history(`/manage/jobs/${newJobId}`);
|
||||
notification["success"]({
|
||||
message: t("jobs.successes.duplicated")
|
||||
@@ -142,7 +177,7 @@ export function JobsDetailHeaderActions({
|
||||
try {
|
||||
insertAppointment({
|
||||
variables: {
|
||||
apt: { ...values, isintake: false, jobid: job.id, bodyshopid: bodyshop.id }
|
||||
apt: {...values, isintake: false, jobid: job.id, bodyshopid: bodyshop.id}
|
||||
},
|
||||
refetchQueries: ["QUERY_ALL_ACTIVE_APPOINTMENTS"]
|
||||
});
|
||||
@@ -159,7 +194,7 @@ export function JobsDetailHeaderActions({
|
||||
|
||||
const handleDeleteJob = async () => {
|
||||
//delete the job.
|
||||
const result = await deleteJob({ variables: { id: job.id } });
|
||||
const result = await deleteJob({variables: {id: job.id}});
|
||||
|
||||
if (!!!result.errors) {
|
||||
notification["success"]({
|
||||
@@ -220,7 +255,7 @@ export function JobsDetailHeaderActions({
|
||||
});
|
||||
|
||||
if (!!!result.errors) {
|
||||
notification["success"]({ message: t("csi.successes.created") });
|
||||
notification["success"]({message: t("csi.successes.created")});
|
||||
} else {
|
||||
notification["error"]({
|
||||
message: t("csi.errors.creating", {
|
||||
@@ -375,7 +410,7 @@ export function JobsDetailHeaderActions({
|
||||
try {
|
||||
QbXmlResponse = await axios.post(
|
||||
"/accounting/qbxml/receivables",
|
||||
{ jobIds: [job.id], custDataOnly: true },
|
||||
{jobIds: [job.id], custDataOnly: true},
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${await auth.currentUser.getIdToken()}`
|
||||
@@ -484,7 +519,7 @@ export function JobsDetailHeaderActions({
|
||||
setIsCancelScheduleModalVisible(false);
|
||||
};
|
||||
|
||||
const handleLostSaleFinish = async ({ lost_sale_reason }) => {
|
||||
const handleLostSaleFinish = async ({lost_sale_reason}) => {
|
||||
const jobUpdate = await cancelAllAppointments({
|
||||
variables: {
|
||||
jobid: job.id,
|
||||
@@ -524,10 +559,10 @@ export function JobsDetailHeaderActions({
|
||||
}
|
||||
]}
|
||||
>
|
||||
<Input />
|
||||
<Input/>
|
||||
</Form.Item>
|
||||
<Form.Item label={t("appointments.fields.note")} name="note">
|
||||
<Input />
|
||||
<Input/>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("appointments.fields.start")}
|
||||
@@ -542,7 +577,7 @@ export function JobsDetailHeaderActions({
|
||||
<FormDateTimePickerComponent
|
||||
onBlur={() => {
|
||||
const start = form.getFieldValue("start");
|
||||
form.setFieldsValue({ end: start.add(30, "minutes") });
|
||||
form.setFieldsValue({end: start.add(30, "minutes")});
|
||||
}}
|
||||
/>
|
||||
</Form.Item>
|
||||
@@ -554,10 +589,10 @@ export function JobsDetailHeaderActions({
|
||||
required: true
|
||||
//message: t("general.validation.required"),
|
||||
},
|
||||
({ getFieldValue }) => ({
|
||||
({getFieldValue}) => ({
|
||||
async validator(rule, value) {
|
||||
if (value) {
|
||||
const { start } = form.getFieldsValue();
|
||||
const {start} = form.getFieldsValue();
|
||||
if (dayjs(start).isAfter(dayjs(value))) {
|
||||
return Promise.reject(t("employees.labels.endmustbeafterstart"));
|
||||
} else {
|
||||
@@ -570,7 +605,7 @@ export function JobsDetailHeaderActions({
|
||||
})
|
||||
]}
|
||||
>
|
||||
<FormDateTimePickerComponent />
|
||||
<FormDateTimePickerComponent/>
|
||||
</Form.Item>
|
||||
<Form.Item label={t("appointments.fields.color")} name="color">
|
||||
<Select>
|
||||
@@ -601,7 +636,7 @@ export function JobsDetailHeaderActions({
|
||||
onClick: () => {
|
||||
logImEXEvent("job_header_schedule");
|
||||
setScheduleContext({
|
||||
actions: { refetch: refetch },
|
||||
actions: {refetch: refetch},
|
||||
context: {
|
||||
jobId: job.id,
|
||||
job: job,
|
||||
@@ -645,7 +680,8 @@ export function JobsDetailHeaderActions({
|
||||
{
|
||||
key: "checklist",
|
||||
disabled: !job.converted,
|
||||
label: <Link to={`/manage/jobs/${job.id}/checklist`}>{t("jobs.actions.viewchecklist")}</Link>
|
||||
label: <Link
|
||||
to={`/manage/jobs/${job.id}/checklist`}>{t("jobs.actions.viewchecklist")}</Link>
|
||||
}
|
||||
],
|
||||
rome: "USE_IMEX",
|
||||
@@ -653,35 +689,35 @@ export function JobsDetailHeaderActions({
|
||||
{
|
||||
key: "toggleproduction",
|
||||
disabled: !job.converted || jobRO,
|
||||
label: <JobsDetailHeaderActionsToggleProduction job={job} refetch={refetch} />
|
||||
label: <JobsDetailHeaderActionsToggleProduction job={job} refetch={refetch}/>
|
||||
}
|
||||
]
|
||||
}),
|
||||
...(InstanceRenderManager({
|
||||
imex: true,
|
||||
rome: "USE_IMEX",
|
||||
promanager: HasFeatureAccess({ featureName: "timetickets", bodyshop })
|
||||
promanager: HasFeatureAccess({featureName: "timetickets", bodyshop})
|
||||
})
|
||||
? [
|
||||
{
|
||||
key: "entertimetickets",
|
||||
disabled: !job.converted || (!bodyshop.tt_allow_post_to_invoiced && job.date_invoiced),
|
||||
label: t("timetickets.actions.enter"),
|
||||
onClick: () => {
|
||||
logImEXEvent("job_header_enter_time_ticekts");
|
||||
{
|
||||
key: "entertimetickets",
|
||||
disabled: !job.converted || (!bodyshop.tt_allow_post_to_invoiced && job.date_invoiced),
|
||||
label: t("timetickets.actions.enter"),
|
||||
onClick: () => {
|
||||
logImEXEvent("job_header_enter_time_ticekts");
|
||||
|
||||
setTimeTicketContext({
|
||||
actions: {},
|
||||
context: {
|
||||
jobId: job.id,
|
||||
created_by: currentUser.displayName
|
||||
? currentUser.email.concat(" | ", currentUser.displayName)
|
||||
: currentUser.email
|
||||
}
|
||||
});
|
||||
}
|
||||
setTimeTicketContext({
|
||||
actions: {},
|
||||
context: {
|
||||
jobId: job.id,
|
||||
created_by: currentUser.displayName
|
||||
? currentUser.email.concat(" | ", currentUser.displayName)
|
||||
: currentUser.email
|
||||
}
|
||||
});
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
: [])
|
||||
];
|
||||
|
||||
@@ -692,7 +728,7 @@ export function JobsDetailHeaderActions({
|
||||
onClick: () => {
|
||||
setTimeTicketTaskContext({
|
||||
actions: {},
|
||||
context: { jobid: job.id }
|
||||
context: {jobid: job.id}
|
||||
});
|
||||
},
|
||||
label: t("timetickets.actions.claimtasks")
|
||||
@@ -708,7 +744,7 @@ export function JobsDetailHeaderActions({
|
||||
|
||||
setPaymentContext({
|
||||
actions: {},
|
||||
context: { jobid: job.id }
|
||||
context: {jobid: job.id}
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -723,18 +759,18 @@ export function JobsDetailHeaderActions({
|
||||
|
||||
setCardPaymentContext({
|
||||
actions: {},
|
||||
context: { jobid: job.id }
|
||||
context: {jobid: job.id}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (HasFeatureAccess({ featureName: "courtesycars" })) {
|
||||
if (HasFeatureAccess({featureName: "courtesycars"})) {
|
||||
menuItems.push({
|
||||
key: "cccontract",
|
||||
disabled: jobRO || !job.converted,
|
||||
label: (
|
||||
<Link state={{ jobId: job.id }} to="/manage/courtesycars/contracts/new">
|
||||
<Link state={{jobId: job.id}} to="/manage/courtesycars/contracts/new">
|
||||
{t("menus.jobsactions.newcccontract")}
|
||||
</Link>
|
||||
)
|
||||
@@ -744,17 +780,17 @@ export function JobsDetailHeaderActions({
|
||||
menuItems.push(
|
||||
job.inproduction
|
||||
? {
|
||||
key: "removefromproduction",
|
||||
disabled: !job.converted,
|
||||
label: t("jobs.actions.removefromproduction"),
|
||||
onClick: () => AddToProduction(client, job.id, refetch, true)
|
||||
}
|
||||
key: "removefromproduction",
|
||||
disabled: !job.converted,
|
||||
label: t("jobs.actions.removefromproduction"),
|
||||
onClick: () => AddToProduction(client, job.id, refetch, true)
|
||||
}
|
||||
: {
|
||||
key: "addtoproduction",
|
||||
disabled: !job.converted,
|
||||
label: t("jobs.actions.addtoproduction"),
|
||||
onClick: () => AddToProduction(client, job.id, refetch)
|
||||
}
|
||||
key: "addtoproduction",
|
||||
disabled: !job.converted,
|
||||
label: t("jobs.actions.addtoproduction"),
|
||||
onClick: () => AddToProduction(client, job.id, refetch)
|
||||
}
|
||||
);
|
||||
|
||||
menuItems.push(
|
||||
@@ -810,25 +846,25 @@ export function JobsDetailHeaderActions({
|
||||
...(InstanceRenderManager({
|
||||
imex: true,
|
||||
rome: true,
|
||||
promanager: HasFeatureAccess({ featureName: "bills", bodyshop })
|
||||
promanager: HasFeatureAccess({featureName: "bills", bodyshop})
|
||||
})
|
||||
? [
|
||||
{
|
||||
key: "postbills",
|
||||
disabled: !job.converted,
|
||||
label: t("jobs.actions.postbills"),
|
||||
onClick: () => {
|
||||
logImEXEvent("job_header_enter_bills");
|
||||
{
|
||||
key: "postbills",
|
||||
disabled: !job.converted,
|
||||
label: t("jobs.actions.postbills"),
|
||||
onClick: () => {
|
||||
logImEXEvent("job_header_enter_bills");
|
||||
|
||||
setBillEnterContext({
|
||||
actions: { refetch: refetch },
|
||||
context: {
|
||||
job: job
|
||||
}
|
||||
});
|
||||
}
|
||||
setBillEnterContext({
|
||||
actions: {refetch: refetch},
|
||||
context: {
|
||||
job: job
|
||||
}
|
||||
});
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
: []),
|
||||
|
||||
{
|
||||
@@ -839,7 +875,7 @@ export function JobsDetailHeaderActions({
|
||||
const result = await updateJob({
|
||||
variables: {
|
||||
jobId: job.id,
|
||||
job: { queued_for_parts: true }
|
||||
job: {queued_for_parts: true}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -889,7 +925,7 @@ export function JobsDetailHeaderActions({
|
||||
InstanceRenderManager({
|
||||
imex: true,
|
||||
rome: true,
|
||||
promanager: HasFeatureAccess({ featureName: "export", bodyshop })
|
||||
promanager: HasFeatureAccess({featureName: "export", bodyshop})
|
||||
})
|
||||
) {
|
||||
menuItems.push({
|
||||
@@ -900,7 +936,7 @@ export function JobsDetailHeaderActions({
|
||||
});
|
||||
}
|
||||
|
||||
if (HasFeatureAccess({ featureName: "csi", bodyshop })) {
|
||||
if (HasFeatureAccess({featureName: "csi", bodyshop})) {
|
||||
const children = [
|
||||
{
|
||||
key: "email",
|
||||
@@ -930,20 +966,20 @@ export function JobsDetailHeaderActions({
|
||||
...job.csiinvites.map((item, idx) => {
|
||||
return item.completedon
|
||||
? {
|
||||
key: idx,
|
||||
label: (
|
||||
<Link to={`/manage/shop/csi?responseid=${item.id}`}>
|
||||
<DateTimeFormatter>{item.completedon}</DateTimeFormatter>
|
||||
</Link>
|
||||
)
|
||||
}
|
||||
key: idx,
|
||||
label: (
|
||||
<Link to={`/manage/shop/csi?responseid=${item.id}`}>
|
||||
<DateTimeFormatter>{item.completedon}</DateTimeFormatter>
|
||||
</Link>
|
||||
)
|
||||
}
|
||||
: {
|
||||
key: idx,
|
||||
onClick: () => {
|
||||
navigator.clipboard.writeText(`${window.location.protocol}//${window.location.host}/csi/${item.id}`);
|
||||
},
|
||||
label: t("general.actions.copylink")
|
||||
};
|
||||
key: idx,
|
||||
onClick: () => {
|
||||
navigator.clipboard.writeText(`${window.location.protocol}//${window.location.host}/csi/${item.id}`);
|
||||
},
|
||||
label: t("general.actions.copylink")
|
||||
};
|
||||
})
|
||||
);
|
||||
}
|
||||
@@ -963,7 +999,7 @@ export function JobsDetailHeaderActions({
|
||||
logImEXEvent("job_header_job_costing");
|
||||
|
||||
setJobCostingContext({
|
||||
actions: { refetch: refetch },
|
||||
actions: {refetch: refetch},
|
||||
context: {
|
||||
jobId: job.id
|
||||
}
|
||||
@@ -1073,10 +1109,10 @@ export function JobsDetailHeaderActions({
|
||||
<Dropdown menu={menu} trigger={["click"]} key="changestatus">
|
||||
<Button>
|
||||
<span>{t("general.labels.actions")}</span>
|
||||
<DownCircleFilled />
|
||||
<DownCircleFilled/>
|
||||
</Button>
|
||||
</Dropdown>
|
||||
<Popover content={popOverContent} open={visibility} />
|
||||
<Popover content={popOverContent} open={visibility}/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
import { SyncOutlined } from "@ant-design/icons";
|
||||
import { Button, Card, Input, Space, Table, Typography } from "antd";
|
||||
import {SyncOutlined} from "@ant-design/icons";
|
||||
import {Button, Card, Input, Space, Table, Typography} from "antd";
|
||||
import axios from "axios";
|
||||
import _ from "lodash";
|
||||
import queryString from "query-string";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { Link, useLocation, useNavigate } from "react-router-dom";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import React, {useEffect, useState} from "react";
|
||||
import {useTranslation} from "react-i18next";
|
||||
import {connect} from "react-redux";
|
||||
import {Link, useLocation, useNavigate} from "react-router-dom";
|
||||
import {createStructuredSelector} from "reselect";
|
||||
import {selectBodyshop} from "../../redux/user/user.selectors";
|
||||
import CurrencyFormatter from "../../utils/CurrencyFormatter";
|
||||
import { pageLimit } from "../../utils/config";
|
||||
import {pageLimit} from "../../utils/config";
|
||||
import useLocalStorage from "../../utils/useLocalStorage";
|
||||
import StartChatButton from "../chat-open-button/chat-open-button.component";
|
||||
import OwnerNameDisplay from "../owner-name-display/owner-name-display.component";
|
||||
@@ -23,15 +23,15 @@ const mapDispatchToProps = (dispatch) => ({
|
||||
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
||||
});
|
||||
|
||||
export function JobsList({ bodyshop, refetch, loading, jobs, total }) {
|
||||
export function JobsList({bodyshop, refetch, loading, jobs, total}) {
|
||||
const search = queryString.parse(useLocation().search);
|
||||
const [openSearchResults, setOpenSearchResults] = useState([]);
|
||||
const [searchLoading, setSearchLoading] = useState(false);
|
||||
const [filter, setFilter] = useLocalStorage("filter_jobs_all", null);
|
||||
const { page, sortcolumn, sortorder } = search;
|
||||
const {page, sortcolumn, sortorder} = search;
|
||||
const history = useNavigate();
|
||||
|
||||
const { t } = useTranslation();
|
||||
const {t} = useTranslation();
|
||||
const columns = [
|
||||
{
|
||||
title: t("jobs.fields.ro_number"),
|
||||
@@ -54,11 +54,11 @@ export function JobsList({ bodyshop, refetch, loading, jobs, total }) {
|
||||
render: (text, record) => {
|
||||
return record.ownerid ? (
|
||||
<Link to={"/manage/owners/" + record.ownerid}>
|
||||
<OwnerNameDisplay ownerObject={record} />
|
||||
<OwnerNameDisplay ownerObject={record}/>
|
||||
</Link>
|
||||
) : (
|
||||
<span>
|
||||
<OwnerNameDisplay ownerObject={record} />
|
||||
<OwnerNameDisplay ownerObject={record}/>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
@@ -69,7 +69,7 @@ export function JobsList({ bodyshop, refetch, loading, jobs, total }) {
|
||||
key: "ownr_ph1",
|
||||
|
||||
ellipsis: true,
|
||||
render: (text, record) => <StartChatButton phone={record.ownr_ph1} jobid={record.id} />
|
||||
render: (text, record) => <StartChatButton phone={record.ownr_ph1} jobid={record.id}/>
|
||||
},
|
||||
{
|
||||
title: t("jobs.fields.ownr_ph2"),
|
||||
@@ -77,7 +77,7 @@ export function JobsList({ bodyshop, refetch, loading, jobs, total }) {
|
||||
key: "ownr_ph2",
|
||||
|
||||
ellipsis: true,
|
||||
render: (text, record) => <StartChatButton phone={record.ownr_ph2} jobid={record.id} />
|
||||
render: (text, record) => <StartChatButton phone={record.ownr_ph2} jobid={record.id}/>
|
||||
},
|
||||
{
|
||||
title: t("jobs.fields.status"),
|
||||
@@ -92,7 +92,7 @@ export function JobsList({ bodyshop, refetch, loading, jobs, total }) {
|
||||
},
|
||||
filteredValue: filter?.status || null,
|
||||
filters: bodyshop.md_ro_statuses.statuses.map((s) => {
|
||||
return { text: s, value: [s] };
|
||||
return {text: s, value: [s]};
|
||||
}),
|
||||
onFilter: (value, record) => value.includes(record.status)
|
||||
},
|
||||
@@ -178,7 +178,7 @@ export function JobsList({ bodyshop, refetch, loading, jobs, total }) {
|
||||
delete search.statusFilters;
|
||||
}
|
||||
setFilter(filters);
|
||||
history({ search: queryString.stringify(search) });
|
||||
history({search: queryString.stringify(search)});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
@@ -210,13 +210,13 @@ export function JobsList({ bodyshop, refetch, loading, jobs, total }) {
|
||||
{search.search && (
|
||||
<>
|
||||
<Typography.Title level={4}>
|
||||
{t("general.labels.searchresults", { search: search.search })}
|
||||
{t("general.labels.searchresults", {search: search.search})}
|
||||
</Typography.Title>
|
||||
<Button
|
||||
onClick={() => {
|
||||
delete search.search;
|
||||
delete search.page;
|
||||
history({ search: queryString.stringify(search) });
|
||||
history({search: queryString.stringify(search)});
|
||||
}}
|
||||
>
|
||||
{t("general.actions.clear")}
|
||||
@@ -224,13 +224,13 @@ export function JobsList({ bodyshop, refetch, loading, jobs, total }) {
|
||||
</>
|
||||
)}
|
||||
<Button onClick={() => refetch()}>
|
||||
<SyncOutlined />
|
||||
<SyncOutlined/>
|
||||
</Button>
|
||||
<Input.Search
|
||||
placeholder={search.search || t("general.labels.search")}
|
||||
onSearch={(value) => {
|
||||
search.search = value;
|
||||
history({ search: queryString.stringify(search) });
|
||||
history({search: queryString.stringify(search)});
|
||||
searchJobs(value);
|
||||
}}
|
||||
loading={loading || searchLoading}
|
||||
@@ -244,15 +244,15 @@ export function JobsList({ bodyshop, refetch, loading, jobs, total }) {
|
||||
pagination={
|
||||
search?.search
|
||||
? {
|
||||
pageSize: pageLimit,
|
||||
showSizeChanger: false
|
||||
}
|
||||
pageSize: pageLimit,
|
||||
showSizeChanger: false
|
||||
}
|
||||
: {
|
||||
pageSize: pageLimit,
|
||||
current: parseInt(page || 1),
|
||||
total: total,
|
||||
showSizeChanger: false
|
||||
}
|
||||
pageSize: pageLimit,
|
||||
current: parseInt(page || 1),
|
||||
total: total,
|
||||
showSizeChanger: false
|
||||
}
|
||||
}
|
||||
columns={columns}
|
||||
rowKey="id"
|
||||
|
||||
@@ -1,23 +1,28 @@
|
||||
import { BranchesOutlined, ExclamationCircleFilled, PauseCircleOutlined, SyncOutlined } from "@ant-design/icons";
|
||||
import { useQuery } from "@apollo/client";
|
||||
import { Button, Card, Grid, Input, Space, Table, Tooltip } from "antd";
|
||||
import {
|
||||
BranchesOutlined,
|
||||
ExclamationCircleFilled,
|
||||
PauseCircleOutlined,
|
||||
SyncOutlined
|
||||
} from "@ant-design/icons";
|
||||
import {useQuery} from "@apollo/client";
|
||||
import {Button, Card, Grid, Input, Space, Table, Tooltip} from "antd";
|
||||
import queryString from "query-string";
|
||||
import React, { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { Link, useLocation, useNavigate } from "react-router-dom";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { QUERY_ALL_ACTIVE_JOBS } from "../../graphql/jobs.queries";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import { onlyUnique } from "../../utils/arrayHelper";
|
||||
import React, {useState} from "react";
|
||||
import {useTranslation} from "react-i18next";
|
||||
import {connect} from "react-redux";
|
||||
import {Link, useLocation, useNavigate} from "react-router-dom";
|
||||
import {createStructuredSelector} from "reselect";
|
||||
import {QUERY_ALL_ACTIVE_JOBS} from "../../graphql/jobs.queries";
|
||||
import {selectBodyshop} from "../../redux/user/user.selectors";
|
||||
import {onlyUnique} from "../../utils/arrayHelper";
|
||||
import CurrencyFormatter from "../../utils/CurrencyFormatter";
|
||||
import { alphaSort, statusSort } from "../../utils/sorters";
|
||||
import {alphaSort, statusSort} from "../../utils/sorters";
|
||||
import useLocalStorage from "../../utils/useLocalStorage";
|
||||
import AlertComponent from "../alert/alert.component";
|
||||
import ChatOpenButton from "../chat-open-button/chat-open-button.component";
|
||||
import OwnerNameDisplay from "../owner-name-display/owner-name-display.component";
|
||||
import { setJoyRideSteps } from "../../redux/application/application.actions";
|
||||
import { OwnerNameDisplayFunction } from "./../owner-name-display/owner-name-display.component";
|
||||
import {setJoyRideSteps} from "../../redux/application/application.actions";
|
||||
import {OwnerNameDisplayFunction} from "./../owner-name-display/owner-name-display.component";
|
||||
import InstanceRenderManager from "../../utils/instanceRenderMgr";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
@@ -28,13 +33,13 @@ const mapDispatchToProps = (dispatch) => ({
|
||||
setJoyRideSteps: (steps) => dispatch(setJoyRideSteps(steps))
|
||||
});
|
||||
|
||||
export function JobsList({ bodyshop, setJoyRideSteps }) {
|
||||
export function JobsList({bodyshop, setJoyRideSteps}) {
|
||||
const searchParams = queryString.parse(useLocation().search);
|
||||
const { selected } = searchParams;
|
||||
const {selected} = searchParams;
|
||||
const selectedBreakpoint = Object.entries(Grid.useBreakpoint())
|
||||
.filter((screen) => !!screen[1])
|
||||
.slice(-1)[0];
|
||||
const { loading, error, data, refetch } = useQuery(QUERY_ALL_ACTIVE_JOBS, {
|
||||
const {loading, error, data, refetch} = useQuery(QUERY_ALL_ACTIVE_JOBS, {
|
||||
variables: {
|
||||
statuses: bodyshop.md_ro_statuses.active_statuses || ["Open", "Open*"]
|
||||
},
|
||||
@@ -42,36 +47,36 @@ export function JobsList({ bodyshop, setJoyRideSteps }) {
|
||||
nextFetchPolicy: "network-only"
|
||||
});
|
||||
|
||||
const [state, setState] = useState({ sortedInfo: {} });
|
||||
const [state, setState] = useState({sortedInfo: {}});
|
||||
const [filter, setFilter] = useLocalStorage("filter_jobs_list", null);
|
||||
|
||||
const { t } = useTranslation();
|
||||
const {t} = useTranslation();
|
||||
const history = useNavigate();
|
||||
const [searchText, setSearchText] = useState("");
|
||||
|
||||
if (error) return <AlertComponent message={error.message} type="error" />;
|
||||
if (error) return <AlertComponent message={error.message} type="error"/>;
|
||||
|
||||
const jobs = data
|
||||
? searchText === ""
|
||||
? data.jobs
|
||||
: data.jobs.filter(
|
||||
(j) =>
|
||||
(j.ro_number || "").toString().toLowerCase().includes(searchText.toLowerCase()) ||
|
||||
(j.ownr_co_nm || "").toLowerCase().includes(searchText.toLowerCase()) ||
|
||||
(j.comments || "").toLowerCase().includes(searchText.toLowerCase()) ||
|
||||
(j.ownr_fn || "").toLowerCase().includes(searchText.toLowerCase()) ||
|
||||
(j.ownr_ln || "").toLowerCase().includes(searchText.toLowerCase()) ||
|
||||
(j.clm_no || "").toLowerCase().includes(searchText.toLowerCase()) ||
|
||||
(j.plate_no || "").toLowerCase().includes(searchText.toLowerCase()) ||
|
||||
(j.v_model_desc || "").toLowerCase().includes(searchText.toLowerCase()) ||
|
||||
(j.est_ct_fn || "").toLowerCase().includes(searchText.toLowerCase()) ||
|
||||
(j.est_ct_ln || "").toLowerCase().includes(searchText.toLowerCase()) ||
|
||||
(j.v_make_desc || "").toLowerCase().includes(searchText.toLowerCase())
|
||||
)
|
||||
(j) =>
|
||||
(j.ro_number || "").toString().toLowerCase().includes(searchText.toLowerCase()) ||
|
||||
(j.ownr_co_nm || "").toLowerCase().includes(searchText.toLowerCase()) ||
|
||||
(j.comments || "").toLowerCase().includes(searchText.toLowerCase()) ||
|
||||
(j.ownr_fn || "").toLowerCase().includes(searchText.toLowerCase()) ||
|
||||
(j.ownr_ln || "").toLowerCase().includes(searchText.toLowerCase()) ||
|
||||
(j.clm_no || "").toLowerCase().includes(searchText.toLowerCase()) ||
|
||||
(j.plate_no || "").toLowerCase().includes(searchText.toLowerCase()) ||
|
||||
(j.v_model_desc || "").toLowerCase().includes(searchText.toLowerCase()) ||
|
||||
(j.est_ct_fn || "").toLowerCase().includes(searchText.toLowerCase()) ||
|
||||
(j.est_ct_ln || "").toLowerCase().includes(searchText.toLowerCase()) ||
|
||||
(j.v_make_desc || "").toLowerCase().includes(searchText.toLowerCase())
|
||||
)
|
||||
: [];
|
||||
|
||||
const handleTableChange = (pagination, filters, sorter) => {
|
||||
setState({ ...state, sortedInfo: sorter });
|
||||
setState({...state, sortedInfo: sorter});
|
||||
setFilter(filters);
|
||||
};
|
||||
|
||||
@@ -102,12 +107,12 @@ export function JobsList({ bodyshop, setJoyRideSteps }) {
|
||||
<Space>
|
||||
{record.ro_number || t("general.labels.na")}
|
||||
{record.production_vars && record.production_vars.alert ? (
|
||||
<ExclamationCircleFilled className="production-alert" />
|
||||
<ExclamationCircleFilled className="production-alert"/>
|
||||
) : null}
|
||||
{record.suspended && <PauseCircleOutlined style={{ color: "orangered" }} />}
|
||||
{record.suspended && <PauseCircleOutlined style={{color: "orangered"}}/>}
|
||||
{record.iouparent && (
|
||||
<Tooltip title={t("jobs.labels.iou")}>
|
||||
<BranchesOutlined style={{ color: "orangered" }} />
|
||||
<BranchesOutlined style={{color: "orangered"}}/>
|
||||
</Tooltip>
|
||||
)}
|
||||
</Space>
|
||||
@@ -126,11 +131,11 @@ export function JobsList({ bodyshop, setJoyRideSteps }) {
|
||||
render: (text, record) => {
|
||||
return record.ownerid ? (
|
||||
<Link to={"/manage/owners/" + record.ownerid} onClick={(e) => e.stopPropagation()}>
|
||||
<OwnerNameDisplay ownerObject={record} />
|
||||
<OwnerNameDisplay ownerObject={record}/>
|
||||
</Link>
|
||||
) : (
|
||||
<span>
|
||||
<OwnerNameDisplay ownerObject={record} />
|
||||
<OwnerNameDisplay ownerObject={record}/>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
@@ -141,7 +146,7 @@ export function JobsList({ bodyshop, setJoyRideSteps }) {
|
||||
key: "ownr_ph1",
|
||||
ellipsis: true,
|
||||
responsive: ["md"],
|
||||
render: (text, record) => <ChatOpenButton phone={record.ownr_ph1} jobid={record.id} />
|
||||
render: (text, record) => <ChatOpenButton phone={record.ownr_ph1} jobid={record.id}/>
|
||||
},
|
||||
{
|
||||
title: t("jobs.fields.ownr_ph2"),
|
||||
@@ -149,7 +154,7 @@ export function JobsList({ bodyshop, setJoyRideSteps }) {
|
||||
key: "ownr_ph2",
|
||||
ellipsis: true,
|
||||
responsive: ["md"],
|
||||
render: (text, record) => <ChatOpenButton phone={record.ownr_ph2} jobid={record.id} />
|
||||
render: (text, record) => <ChatOpenButton phone={record.ownr_ph2} jobid={record.id}/>
|
||||
},
|
||||
|
||||
{
|
||||
@@ -344,7 +349,7 @@ export function JobsList({ bodyshop, setJoyRideSteps }) {
|
||||
})}
|
||||
|
||||
<Button onClick={() => refetch()}>
|
||||
<SyncOutlined />
|
||||
<SyncOutlined/>
|
||||
</Button>
|
||||
<Input.Search
|
||||
placeholder={t("general.labels.search")}
|
||||
@@ -359,7 +364,7 @@ export function JobsList({ bodyshop, setJoyRideSteps }) {
|
||||
>
|
||||
<Table
|
||||
loading={loading}
|
||||
pagination={{ defaultPageSize: 50 }}
|
||||
pagination={{defaultPageSize: 50}}
|
||||
columns={columns}
|
||||
rowKey="id"
|
||||
dataSource={jobs}
|
||||
|
||||
@@ -13,7 +13,7 @@ import {logImEXEvent} from "../../firebase/firebase.utils";
|
||||
import {DELETE_PARTS_ORDER} from "../../graphql/parts-orders.queries";
|
||||
import {selectJobReadOnly} from "../../redux/application/application.selectors";
|
||||
import {setModalContext} from "../../redux/modals/modals.actions";
|
||||
import {selectBodyshop, selectCurrentUser} from "../../redux/user/user.selectors";
|
||||
import {selectBodyshop} from "../../redux/user/user.selectors";
|
||||
import CurrencyFormatter from "../../utils/CurrencyFormatter";
|
||||
import {DateFormatter} from "../../utils/DateFormatter";
|
||||
import {alphaSort} from "../../utils/sorters";
|
||||
@@ -33,11 +33,13 @@ import {FaTasks} from "react-icons/fa";
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
jobRO: selectJobReadOnly,
|
||||
bodyshop: selectBodyshop,
|
||||
currentUser: selectCurrentUser
|
||||
});
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
setBillEnterContext: (context) => dispatch(setModalContext({ context: context, modal: "billEnter" })),
|
||||
setBillEnterContext: (context) => dispatch(setModalContext({
|
||||
context: context,
|
||||
modal: "billEnter"
|
||||
})),
|
||||
setPartsReceiveContext: (context) => dispatch(setModalContext({
|
||||
context: context,
|
||||
modal: "partsReceive"
|
||||
@@ -54,8 +56,7 @@ export function PartsOrderListTableComponent({
|
||||
handleOnRowClick,
|
||||
setPartsReceiveContext,
|
||||
setTaskUpsertContext,
|
||||
currentUser
|
||||
}) {
|
||||
}) {
|
||||
const selectedBreakpoint = Object.entries(Grid.useBreakpoint())
|
||||
.filter((screen) => !!screen[1])
|
||||
.slice(-1)[0];
|
||||
@@ -70,9 +71,9 @@ export function PartsOrderListTableComponent({
|
||||
};
|
||||
const drawerPercentage = selectedBreakpoint ? bpoints[selectedBreakpoint[0]] : "100%";
|
||||
const responsibilityCenters = bodyshop.md_responsibility_centers;
|
||||
const Templates = TemplateList("partsorder", { job });
|
||||
const Templates = TemplateList("partsorder", {job});
|
||||
|
||||
const { t } = useTranslation();
|
||||
const {t} = useTranslation();
|
||||
const [state, setState] = useState({
|
||||
sortedInfo: {}
|
||||
});
|
||||
@@ -83,13 +84,13 @@ export function PartsOrderListTableComponent({
|
||||
const [deletePartsOrder] = useMutation(DELETE_PARTS_ORDER);
|
||||
|
||||
const parts_orders = billsQuery.data ? billsQuery.data.parts_orders : [];
|
||||
const { refetch } = billsQuery;
|
||||
const {refetch} = billsQuery;
|
||||
|
||||
const recordActions = (record, showView = false) => (
|
||||
<Space direction='horizontal' wrap>
|
||||
{showView && (
|
||||
<Button onClick={() => handleOnRowClick(record)}>
|
||||
<EyeFilled />
|
||||
<EyeFilled/>
|
||||
</Button>
|
||||
)}
|
||||
|
||||
@@ -98,7 +99,7 @@ export function PartsOrderListTableComponent({
|
||||
onClick={() => {
|
||||
logImEXEvent("parts_order_receive_bill");
|
||||
setPartsReceiveContext({
|
||||
actions: { refetch: refetch },
|
||||
actions: {refetch: refetch},
|
||||
context: {
|
||||
jobId: job.id,
|
||||
job: job,
|
||||
@@ -135,11 +136,11 @@ export function PartsOrderListTableComponent({
|
||||
//Delete the parts return.!
|
||||
|
||||
await deletePartsOrder({
|
||||
variables: { partsOrderId: record.id },
|
||||
variables: {partsOrderId: record.id},
|
||||
update(cache) {
|
||||
cache.modify({
|
||||
fields: {
|
||||
parts_orders(existingPartsOrders, { readField }) {
|
||||
parts_orders(existingPartsOrders, {readField}) {
|
||||
return existingPartsOrders.filter((billref) => record.id !== readField("id", billref));
|
||||
}
|
||||
}
|
||||
@@ -149,7 +150,7 @@ export function PartsOrderListTableComponent({
|
||||
}}
|
||||
>
|
||||
<Button disabled={jobRO}>
|
||||
<DeleteFilled />
|
||||
<DeleteFilled/>
|
||||
</Button>
|
||||
</Popconfirm>
|
||||
<FeatureWrapperComponent featureName="bills" noauth={() => null}>
|
||||
@@ -159,7 +160,7 @@ export function PartsOrderListTableComponent({
|
||||
logImEXEvent("parts_order_receive_bill");
|
||||
|
||||
setBillEnterContext({
|
||||
actions: { refetch: refetch },
|
||||
actions: {refetch: refetch},
|
||||
context: {
|
||||
job: job,
|
||||
bill: {
|
||||
@@ -179,7 +180,7 @@ export function PartsOrderListTableComponent({
|
||||
? pol.jobline.part_type
|
||||
: null
|
||||
: responsibilityCenters.defaults &&
|
||||
(responsibilityCenters.defaults.costs[pol.jobline.part_type] || null)
|
||||
(responsibilityCenters.defaults.costs[pol.jobline.part_type] || null)
|
||||
: null
|
||||
};
|
||||
})
|
||||
@@ -194,7 +195,7 @@ export function PartsOrderListTableComponent({
|
||||
<PrintWrapper
|
||||
templateObject={{
|
||||
name: record.return ? Templates.parts_return_slip.key : Templates.parts_order.key,
|
||||
variables: { id: record.id }
|
||||
variables: {id: record.id}
|
||||
}}
|
||||
messageObject={{
|
||||
subject: record.return ? Templates.parts_return_slip.subject : Templates.parts_order.subject,
|
||||
@@ -235,7 +236,7 @@ export function PartsOrderListTableComponent({
|
||||
key: "return",
|
||||
sorter: (a, b) => a.return - b.return,
|
||||
sortOrder: state.sortedInfo.columnKey === "return" && state.sortedInfo.order,
|
||||
render: (text, record) => <Checkbox checked={record.return} />
|
||||
render: (text, record) => <Checkbox checked={record.return}/>
|
||||
},
|
||||
{
|
||||
title: t("parts_orders.fields.deliver_by"),
|
||||
@@ -259,7 +260,7 @@ export function PartsOrderListTableComponent({
|
||||
];
|
||||
|
||||
const handleTableChange = (pagination, filters, sorter) => {
|
||||
setState({ ...state, filteredInfo: filters, sortedInfo: sorter });
|
||||
setState({...state, filteredInfo: filters, sortedInfo: sorter});
|
||||
};
|
||||
|
||||
const selectedPartsOrderRecord = parts_orders.find((r) => r.id === selectedpartsorder);
|
||||
@@ -290,15 +291,15 @@ export function PartsOrderListTableComponent({
|
||||
},
|
||||
...(selectedPartsOrderRecord && selectedPartsOrderRecord.return
|
||||
? [
|
||||
{
|
||||
title: t("parts_orders.fields.cost"),
|
||||
dataIndex: "cost",
|
||||
key: "cost",
|
||||
sorter: (a, b) => a.cost - b.cost,
|
||||
sortOrder: state.sortedInfo.columnKey === "cost" && state.sortedInfo.order,
|
||||
render: (text, record) => <CurrencyFormatter>{record.cost}</CurrencyFormatter>
|
||||
}
|
||||
]
|
||||
{
|
||||
title: t("parts_orders.fields.cost"),
|
||||
dataIndex: "cost",
|
||||
key: "cost",
|
||||
sorter: (a, b) => a.cost - b.cost,
|
||||
sortOrder: state.sortedInfo.columnKey === "cost" && state.sortedInfo.order,
|
||||
render: (text, record) => <CurrencyFormatter>{record.cost}</CurrencyFormatter>
|
||||
}
|
||||
]
|
||||
: []),
|
||||
{
|
||||
title: t("parts_orders.fields.part_type"),
|
||||
@@ -326,19 +327,19 @@ export function PartsOrderListTableComponent({
|
||||
|
||||
...(selectedPartsOrderRecord && selectedPartsOrderRecord.return
|
||||
? [
|
||||
{
|
||||
title: t("parts_orders.fields.cm_received"),
|
||||
dataIndex: "cm_received",
|
||||
key: "cm_received",
|
||||
render: (text, record) => (
|
||||
<PartsOrderCmReceived
|
||||
orderLineId={record.id}
|
||||
checked={record.cm_received}
|
||||
partsorderid={selectedPartsOrderRecord.id}
|
||||
/>
|
||||
)
|
||||
}
|
||||
]
|
||||
{
|
||||
title: t("parts_orders.fields.cm_received"),
|
||||
dataIndex: "cm_received",
|
||||
key: "cm_received",
|
||||
render: (text, record) => (
|
||||
<PartsOrderCmReceived
|
||||
orderLineId={record.id}
|
||||
checked={record.cm_received}
|
||||
partsorderid={selectedPartsOrderRecord.id}
|
||||
/>
|
||||
)
|
||||
}
|
||||
]
|
||||
: []),
|
||||
{
|
||||
title: t("parts_orders.fields.backordered_on"),
|
||||
@@ -387,7 +388,8 @@ export function PartsOrderListTableComponent({
|
||||
|
||||
return (
|
||||
<div>
|
||||
<PageHeader title={record && `${record.vendor.name} - ${record.order_number}`} extra={recordActions(record)} />
|
||||
<PageHeader title={record && `${record.vendor.name} - ${record.order_number}`}
|
||||
extra={recordActions(record)}/>
|
||||
<Table
|
||||
scroll={{
|
||||
x: true //y: "50rem"
|
||||
@@ -397,7 +399,7 @@ export function PartsOrderListTableComponent({
|
||||
dataSource={record.parts_order_lines}
|
||||
/>
|
||||
<DataLabel label={t("parts_orders.fields.comments")}>
|
||||
<div style={{ whiteSpace: "pre" }}>{record.comments}</div>
|
||||
<div style={{whiteSpace: "pre"}}>{record.comments}</div>
|
||||
</DataLabel>
|
||||
</div>
|
||||
);
|
||||
@@ -407,10 +409,10 @@ export function PartsOrderListTableComponent({
|
||||
? searchText === ""
|
||||
? parts_orders
|
||||
: parts_orders.filter(
|
||||
(b) =>
|
||||
(b.order_number || "").toString().toLowerCase().includes(searchText.toLowerCase()) ||
|
||||
(b.vendor.name || "").toLowerCase().includes(searchText.toLowerCase())
|
||||
)
|
||||
(b) =>
|
||||
(b.order_number || "").toString().toLowerCase().includes(searchText.toLowerCase()) ||
|
||||
(b.vendor.name || "").toLowerCase().includes(searchText.toLowerCase())
|
||||
)
|
||||
: [];
|
||||
|
||||
return (
|
||||
@@ -419,7 +421,7 @@ export function PartsOrderListTableComponent({
|
||||
extra={
|
||||
<Space wrap>
|
||||
<Button onClick={() => refetch()}>
|
||||
<SyncOutlined />
|
||||
<SyncOutlined/>
|
||||
</Button>
|
||||
<Input.Search
|
||||
placeholder={t("general.labels.search")}
|
||||
@@ -432,7 +434,7 @@ export function PartsOrderListTableComponent({
|
||||
</Space>
|
||||
}
|
||||
>
|
||||
<PartsReceiveModalContainer />
|
||||
<PartsReceiveModalContainer/>
|
||||
<Drawer
|
||||
placement="right"
|
||||
onClose={() => handleOnRowClick(null)}
|
||||
|
||||
@@ -158,7 +158,7 @@ export function PartsOrderModalContainer({
|
||||
vendorid: bodyshop.inhousevendorid,
|
||||
invoice_number: "ih",
|
||||
isinhouse: true,
|
||||
date: new dayjs(),
|
||||
date: dayjs(),
|
||||
total: 0,
|
||||
billlines: values.parts_order_lines.data.map((p) => {
|
||||
return {
|
||||
|
||||
@@ -46,7 +46,7 @@ const DueDateRecord = ({dueDate}) => {
|
||||
* @constructor
|
||||
*/
|
||||
const PriorityLabel = ({priority}) => {
|
||||
switch(priority) {
|
||||
switch (priority) {
|
||||
case 1:
|
||||
return <div>
|
||||
High <ExclamationCircleFilled style={{marginLeft: '5px', color: 'red'}}/>
|
||||
@@ -89,7 +89,7 @@ function TaskListComponent({
|
||||
toggleDeletedStatus,
|
||||
relationshipType,
|
||||
relationshipId,
|
||||
onlyMine ,
|
||||
onlyMine,
|
||||
parentJobId
|
||||
}) {
|
||||
const {t} = useTranslation();
|
||||
@@ -108,7 +108,7 @@ function TaskListComponent({
|
||||
|
||||
const history = useNavigate();
|
||||
const columns = [];
|
||||
|
||||
|
||||
if (!onlyMine) {
|
||||
columns.push(
|
||||
{
|
||||
@@ -118,14 +118,14 @@ function TaskListComponent({
|
||||
width: '8%',
|
||||
sorter: true,
|
||||
sortOrder: sortcolumn === "assigned_to" && sortorder,
|
||||
render: (text, record) => {
|
||||
const employee = bodyshop?.employees?.find(e => e.user_email === record.assigned_to);
|
||||
return employee ? `${ employee.first_name} ${ employee.last_name}` : t("general.labels.na");
|
||||
render: (text, record) => {
|
||||
const employee = bodyshop?.employees?.find(e => e.user_email === record.assigned_to);
|
||||
return employee ? `${employee.first_name} ${employee.last_name}` : t("general.labels.na");
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
columns.push(
|
||||
{
|
||||
title: t("tasks.fields.job.ro_number"),
|
||||
@@ -134,7 +134,8 @@ function TaskListComponent({
|
||||
width: '8%',
|
||||
render: (text, record) =>
|
||||
record.job
|
||||
? <Link to={`/manage/jobs/${record.job.id}`}>{record.job.ro_number || t("general.labels.na")}</Link>
|
||||
? <Link
|
||||
to={`/manage/jobs/${record.job.id}`}>{record.job.ro_number || t("general.labels.na")}</Link>
|
||||
: t("general.labels.na")
|
||||
},
|
||||
{
|
||||
@@ -151,7 +152,8 @@ function TaskListComponent({
|
||||
width: '8%',
|
||||
render: (text, record) =>
|
||||
record.parts_order
|
||||
? <Link to={`/manage/jobs/${record.job.id}?partsorderid=${record.parts_order.id}&tab=partssublet`}>
|
||||
? <Link
|
||||
to={`/manage/jobs/${record.job.id}?partsorderid=${record.parts_order.id}&tab=partssublet`}>
|
||||
{record.parts_order.order_number && record.parts_order.vendor && record.parts_order.vendor.name
|
||||
? `${record.parts_order.order_number} - ${record.parts_order.vendor.name}`
|
||||
: t("general.labels.na")}
|
||||
@@ -186,7 +188,7 @@ function TaskListComponent({
|
||||
sorter: true,
|
||||
sortOrder: sortcolumn === "due_date" && sortorder,
|
||||
width: '8%',
|
||||
render: (text, record) => <DueDateRecord dueDate={record.due_date} />,
|
||||
render: (text, record) => <DueDateRecord dueDate={record.due_date}/>,
|
||||
},
|
||||
{
|
||||
title: t("tasks.fields.remind_at"),
|
||||
@@ -195,7 +197,7 @@ function TaskListComponent({
|
||||
sorter: true,
|
||||
sortOrder: sortcolumn === "remind_at" && sortorder,
|
||||
width: '8%',
|
||||
render: (text, record) => <DueDateRecord dueDate={record.remind_at} />,
|
||||
render: (text, record) => <DueDateRecord dueDate={record.remind_at}/>,
|
||||
},
|
||||
{
|
||||
title: t("tasks.fields.priority"),
|
||||
@@ -267,9 +269,9 @@ function TaskListComponent({
|
||||
}
|
||||
history({search: queryString.stringify(search)});
|
||||
};
|
||||
|
||||
|
||||
const expandableRow = (record) => {
|
||||
return <Card title={t('tasks.fields.description')} size='small'>
|
||||
return <Card title={t('tasks.fields.description')} size='small'>
|
||||
{record.description}
|
||||
</Card>
|
||||
};
|
||||
@@ -289,7 +291,7 @@ function TaskListComponent({
|
||||
checked={mine === "true"}
|
||||
onChange={(value) => handleSwitchChange('mine', value)}
|
||||
/>
|
||||
)}
|
||||
)}
|
||||
<Switch
|
||||
checkedChildren={<CheckCircleFilled/>}
|
||||
unCheckedChildren={<CheckCircleOutlined/>}
|
||||
|
||||
@@ -12,7 +12,16 @@ import TaskListComponent from "./task-list.component.jsx";
|
||||
import {notification} from "antd";
|
||||
import {useTranslation} from "react-i18next";
|
||||
|
||||
export default function TaskListContainer({bodyshop, titleTranslation ,query, relationshipType, relationshipId, currentUser, onlyMine, parentJobId}) {
|
||||
export default function TaskListContainer({
|
||||
bodyshop,
|
||||
titleTranslation,
|
||||
query,
|
||||
relationshipType,
|
||||
relationshipId,
|
||||
currentUser,
|
||||
onlyMine,
|
||||
parentJobId
|
||||
}) {
|
||||
const {t} = useTranslation();
|
||||
const searchParams = queryString.parse(useLocation().search);
|
||||
const {page, sortcolumn, sortorder, deleted, completed, mine} = searchParams;
|
||||
@@ -26,7 +35,7 @@ export default function TaskListContainer({bodyshop, titleTranslation ,query, r
|
||||
[relationshipType]: relationshipId,
|
||||
deleted: deleted === 'true',
|
||||
completed: completed === "true",
|
||||
assigned_to: mine === "true" ? currentUser.email: undefined, // replace currentUserID with the actual ID of the current user
|
||||
assigned_to: mine === "true" ? currentUser.email : undefined, // replace currentUserID with the actual ID of the current user
|
||||
offset: page ? (page - 1) * pageLimit : 0,
|
||||
limit: pageLimit,
|
||||
order: [
|
||||
@@ -56,7 +65,7 @@ export default function TaskListContainer({bodyshop, titleTranslation ,query, r
|
||||
window.removeEventListener('taskUpdated', handleTaskUpdated);
|
||||
};
|
||||
}, [refetch]);
|
||||
|
||||
|
||||
/**
|
||||
* Toggle task completed mutation
|
||||
*/
|
||||
@@ -81,8 +90,8 @@ export default function TaskListContainer({bodyshop, titleTranslation ,query, r
|
||||
// refetch().catch((e) => {
|
||||
// console.error(`Something went wrong fetching tasks: ${e.message || ''}`);
|
||||
// });
|
||||
window.dispatchEvent( new CustomEvent('taskUpdated', {
|
||||
detail: { message: 'A task has been completed.' },
|
||||
window.dispatchEvent(new CustomEvent('taskUpdated', {
|
||||
detail: {message: 'A task has been completed.'},
|
||||
}));
|
||||
notification["success"]({
|
||||
message: t("tasks.successes.completed"),
|
||||
@@ -115,8 +124,8 @@ export default function TaskListContainer({bodyshop, titleTranslation ,query, r
|
||||
deleted_at: deleted_at
|
||||
}
|
||||
});
|
||||
window.dispatchEvent( new CustomEvent('taskUpdated', {
|
||||
detail: { message: 'A task has been deleted.' },
|
||||
window.dispatchEvent(new CustomEvent('taskUpdated', {
|
||||
detail: {message: 'A task has been deleted.'},
|
||||
}));
|
||||
// refetch().catch((e) => {
|
||||
// console.error(`Something went wrong fetching tasks: ${e.message || ''}`);
|
||||
|
||||
@@ -56,8 +56,8 @@ export function TaskUpsertModalContainer({
|
||||
});
|
||||
|
||||
const {
|
||||
loading: taskLoading,
|
||||
error: taskError,
|
||||
loading: taskLoading,
|
||||
error: taskError,
|
||||
data: taskData
|
||||
} = useQuery(QUERY_GET_TASK_BY_ID, {
|
||||
variables: {id: taskId},
|
||||
@@ -139,7 +139,7 @@ export function TaskUpsertModalContainer({
|
||||
ReplyTo: {
|
||||
Email: 'noreply@imex.online'
|
||||
},
|
||||
to: values.assigned_to,
|
||||
to: values.assigned_to,
|
||||
subject: `A Task has been re-assigned to you on ${bodyshop.shopname} - ${values.title}`,
|
||||
templateStrings: {
|
||||
header: values.title,
|
||||
@@ -189,7 +189,7 @@ export function TaskUpsertModalContainer({
|
||||
// });
|
||||
// },
|
||||
})).data.insert_tasks.returning[0].id;
|
||||
|
||||
|
||||
if (refetch) await refetch();
|
||||
|
||||
form.resetFields();
|
||||
@@ -255,7 +255,8 @@ export function TaskUpsertModalContainer({
|
||||
destroyOnClose
|
||||
>
|
||||
<Form form={form} onFinish={handleFinish} layout="vertical">
|
||||
<TaskUpsertModalComponent form={form} loading={loading || (taskId && taskLoading)} error={error} data={data}
|
||||
<TaskUpsertModalComponent form={form} loading={loading || (taskId && taskLoading)}
|
||||
error={error} data={data}
|
||||
selectedJobId={selectedJobId}
|
||||
setSelectedJobId={setSelectedJobId}
|
||||
selectedJobDetails={selectedJobDetails}/>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { gql } from "@apollo/client";
|
||||
import {gql} from "@apollo/client";
|
||||
|
||||
export const INSERT_NEW_BILL = gql`
|
||||
mutation INSERT_NEW_BILL($bill: [bills_insert_input!]!) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { gql } from "@apollo/client";
|
||||
import {gql} from "@apollo/client";
|
||||
|
||||
export const QUERY_ALL_ACTIVE_JOBS_PAGINATED = gql`
|
||||
query QUERY_ALL_JOBS_PAGINATED_STATUS_FILTERED(
|
||||
@@ -2049,12 +2049,12 @@ export const generate_UPDATE_JOB_KANBAN = (
|
||||
}`;
|
||||
|
||||
return gql`
|
||||
mutation UPDATE_JOB_KANBAN {
|
||||
${oldChildId ? oldChildQuery : ""}
|
||||
${movedId ? movedQuery : ""}
|
||||
${newChildId ? newChildQuery : ""}
|
||||
}
|
||||
`;
|
||||
mutation UPDATE_JOB_KANBAN {
|
||||
${oldChildId ? oldChildQuery : ""}
|
||||
${movedId ? movedQuery : ""}
|
||||
${newChildId ? newChildQuery : ""}
|
||||
}
|
||||
`;
|
||||
};
|
||||
|
||||
export const QUERY_JOB_LBR_ADJUSTMENTS = gql`
|
||||
|
||||
@@ -386,28 +386,28 @@ export const MUTATION_INSERT_NEW_TASK = gql`
|
||||
* @type {DocumentNode}
|
||||
*/
|
||||
export const MUTATION_UPDATE_TASK = gql`
|
||||
mutation MUTATION_UPDATE_TASK($taskId: uuid!, $task: tasks_set_input!) {
|
||||
update_tasks(where: { id: { _eq: $taskId } }, _set: $task) {
|
||||
returning {
|
||||
id
|
||||
created_at
|
||||
updated_at
|
||||
title
|
||||
description
|
||||
deleted
|
||||
deleted_at
|
||||
due_date
|
||||
created_by
|
||||
assigned_to
|
||||
completed
|
||||
completed_at
|
||||
remind_at
|
||||
priority
|
||||
jobid
|
||||
joblineid
|
||||
partsorderid
|
||||
billid
|
||||
}
|
||||
}
|
||||
mutation MUTATION_UPDATE_TASK($taskId: uuid!, $task: tasks_set_input!) {
|
||||
update_tasks(where: { id: { _eq: $taskId } }, _set: $task) {
|
||||
returning {
|
||||
id
|
||||
created_at
|
||||
updated_at
|
||||
title
|
||||
description
|
||||
deleted
|
||||
deleted_at
|
||||
due_date
|
||||
created_by
|
||||
assigned_to
|
||||
completed
|
||||
completed_at
|
||||
remind_at
|
||||
priority
|
||||
jobid
|
||||
joblineid
|
||||
partsorderid
|
||||
billid
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
@@ -91,7 +91,7 @@ export function JobsDetailPage({
|
||||
job,
|
||||
mutationUpdateJob,
|
||||
handleSubmit,
|
||||
currentUser,
|
||||
currentUser,
|
||||
insertAuditTrail,
|
||||
refetch
|
||||
}) {
|
||||
@@ -120,7 +120,7 @@ export function JobsDetailPage({
|
||||
window.removeEventListener('taskUpdated', handleTaskUpdated);
|
||||
};
|
||||
}, [refetch]);
|
||||
|
||||
|
||||
//useKeyboardSaveShortcut(form.submit);
|
||||
|
||||
const handleFinish = async (values) => {
|
||||
@@ -406,15 +406,19 @@ export function JobsDetailPage({
|
||||
key: 'tasks',
|
||||
icon: <FaTasks/>,
|
||||
label: <Space direction='horizontal'>
|
||||
{t("jobs.labels.tasks")}{job.tasks_aggregate.aggregate.count > 0 && <Badge count={job.tasks_aggregate.aggregate.count} />}
|
||||
{t("jobs.labels.tasks")}{job.tasks_aggregate.aggregate.count > 0 &&
|
||||
<Badge count={job.tasks_aggregate.aggregate.count}/>}
|
||||
</Space>,
|
||||
children: <TaskListContainer currentUser={currentUser} bodyshop={bodyshop} relationshipType={'jobid'} relationshipId={job.id} query={QUERY_JOB_TASKS_PAGINATED} titleTranslation='tasks.titles.job_tasks'/>
|
||||
children: <TaskListContainer currentUser={currentUser} bodyshop={bodyshop}
|
||||
relationshipType={'jobid'} relationshipId={job.id}
|
||||
query={QUERY_JOB_TASKS_PAGINATED}
|
||||
titleTranslation='tasks.titles.job_tasks'/>
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</Form>
|
||||
</div>
|
||||
);
|
||||
/>
|
||||
</Form>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(JobsDetailPage);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { FloatButton, Layout, Spin, Collapse, Button, Space, Tag } from "antd";
|
||||
import {Button, Collapse, FloatButton, Layout, Space, Spin, Tag} from "antd";
|
||||
// import preval from "preval.macro";
|
||||
import React, {lazy, Suspense, useEffect, useState} from "react";
|
||||
import {useTranslation} from "react-i18next";
|
||||
@@ -20,8 +20,8 @@ import PartnerPingComponent from "../../components/partner-ping/partner-ping.com
|
||||
import PrintCenterModalContainer
|
||||
from "../../components/print-center-modal/print-center-modal.container";
|
||||
import ShopSubStatusComponent from "../../components/shop-sub-status/shop-sub-status.component";
|
||||
import { requestForToken } from "../../firebase/firebase.utils";
|
||||
import { selectBodyshop, selectInstanceConflict } from "../../redux/user/user.selectors";
|
||||
import {requestForToken} from "../../firebase/firebase.utils";
|
||||
import {selectBodyshop, selectInstanceConflict} from "../../redux/user/user.selectors";
|
||||
|
||||
import UpdateAlert from "../../components/update-alert/update-alert.component";
|
||||
import {setJoyRideFinished} from "../../redux/application/application.actions.js";
|
||||
@@ -106,7 +106,7 @@ const MyTasksPage = lazy(() => import("../tasks/myTasksPageContainer.jsx"));
|
||||
const AllTasksPage = lazy(() => import("../tasks/allTasksPageContainer.jsx"));
|
||||
|
||||
const TaskUpsertModalContainer = lazy(() => import("../../components/task-upsert-modal/task-upsert-modal.container"));
|
||||
const { Content, Footer } = Layout;
|
||||
const {Content, Footer} = Layout;
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
conflict: selectInstanceConflict,
|
||||
@@ -119,8 +119,8 @@ const mapDispatchToProps = (dispatch) => ({
|
||||
setJoyRideFinished: (steps) => dispatch(setJoyRideFinished(steps))
|
||||
});
|
||||
|
||||
export function Manage({ conflict, bodyshop, enableJoyRide, joyRideSteps, setJoyRideFinished }) {
|
||||
const { t } = useTranslation();
|
||||
export function Manage({conflict, bodyshop, enableJoyRide, joyRideSteps, setJoyRideFinished}) {
|
||||
const {t} = useTranslation();
|
||||
const [chatVisible] = useState(false);
|
||||
const [tours, setTours] = useState([]);
|
||||
|
||||
@@ -156,115 +156,115 @@ export function Manage({ conflict, bodyshop, enableJoyRide, joyRideSteps, setJoy
|
||||
/>
|
||||
}
|
||||
>
|
||||
<PaymentModalContainer />
|
||||
<PaymentModalContainer/>
|
||||
|
||||
<CardPaymentModalContainer />
|
||||
<CardPaymentModalContainer/>
|
||||
<TaskUpsertModalContainer/>
|
||||
<BreadCrumbs />
|
||||
<BillEnterModalContainer />
|
||||
<JobCostingModal />
|
||||
<ReportCenterModal />
|
||||
<EmailOverlayContainer />
|
||||
<TimeTicketModalContainer />
|
||||
<TimeTicketModalTask />
|
||||
<PrintCenterModalContainer />
|
||||
<BreadCrumbs/>
|
||||
<BillEnterModalContainer/>
|
||||
<JobCostingModal/>
|
||||
<ReportCenterModal/>
|
||||
<EmailOverlayContainer/>
|
||||
<TimeTicketModalContainer/>
|
||||
<TimeTicketModalTask/>
|
||||
<PrintCenterModalContainer/>
|
||||
<Routes>
|
||||
<Route path="/_test" element={<TestComponent />} />
|
||||
<Route path="/" element={<ManageRootPage />} />
|
||||
<Route path="/_test" element={<TestComponent/>}/>
|
||||
<Route path="/" element={<ManageRootPage/>}/>
|
||||
<Route
|
||||
path="/jobs"
|
||||
element={
|
||||
<Suspense fallback={<Spin />}>
|
||||
<JobsPage />
|
||||
<Suspense fallback={<Spin/>}>
|
||||
<JobsPage/>
|
||||
</Suspense>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/jobs/:jobId/intake"
|
||||
element={
|
||||
<Suspense fallback={<Spin />}>
|
||||
<JobIntake />
|
||||
<Suspense fallback={<Spin/>}>
|
||||
<JobIntake/>
|
||||
</Suspense>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/jobs/:jobId/deliver"
|
||||
element={
|
||||
<Suspense fallback={<Spin />}>
|
||||
<JobDeliver />
|
||||
<Suspense fallback={<Spin/>}>
|
||||
<JobDeliver/>
|
||||
</Suspense>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/jobs/:jobId/checklist"
|
||||
element={
|
||||
<Suspense fallback={<Spin />}>
|
||||
<JobChecklistView />
|
||||
<Suspense fallback={<Spin/>}>
|
||||
<JobChecklistView/>
|
||||
</Suspense>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/jobs/:jobId/close"
|
||||
element={
|
||||
<Suspense fallback={<Spin />}>
|
||||
<JobsClose />
|
||||
<Suspense fallback={<Spin/>}>
|
||||
<JobsClose/>
|
||||
</Suspense>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/jobs/:jobId/admin"
|
||||
element={
|
||||
<Suspense fallback={<Spin />}>
|
||||
<JobsAdmin />
|
||||
<Suspense fallback={<Spin/>}>
|
||||
<JobsAdmin/>
|
||||
</Suspense>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/jobs/all"
|
||||
element={
|
||||
<Suspense fallback={<Spin />}>
|
||||
<AllJobs />
|
||||
<Suspense fallback={<Spin/>}>
|
||||
<AllJobs/>
|
||||
</Suspense>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/jobs/ready"
|
||||
element={
|
||||
<Suspense fallback={<Spin />}>
|
||||
<ReadyJobs />
|
||||
<Suspense fallback={<Spin/>}>
|
||||
<ReadyJobs/>
|
||||
</Suspense>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/jobs/new"
|
||||
element={
|
||||
<Suspense fallback={<Spin />}>
|
||||
<JobsCreateContainerPage />
|
||||
<Suspense fallback={<Spin/>}>
|
||||
<JobsCreateContainerPage/>
|
||||
</Suspense>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/jobs/:jobId"
|
||||
element={
|
||||
<Suspense fallback={<Spin />}>
|
||||
<JobsDetailPage />
|
||||
<Suspense fallback={<Spin/>}>
|
||||
<JobsDetailPage/>
|
||||
</Suspense>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/temporarydocs/"
|
||||
element={
|
||||
<Suspense fallback={<Spin />}>
|
||||
<TempDocs />
|
||||
<Suspense fallback={<Spin/>}>
|
||||
<TempDocs/>
|
||||
</Suspense>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path='/tasks/mytasks'
|
||||
element={
|
||||
<Suspense fallback={<Spin/>}>
|
||||
<MyTasksPage/>
|
||||
</Suspense>}
|
||||
element={
|
||||
<Suspense fallback={<Spin/>}>
|
||||
<MyTasksPage/>
|
||||
</Suspense>}
|
||||
/>
|
||||
<Route
|
||||
path='/tasks/alltasks'
|
||||
@@ -276,144 +276,144 @@ export function Manage({ conflict, bodyshop, enableJoyRide, joyRideSteps, setJoy
|
||||
<Route
|
||||
path="/inventory/"
|
||||
element={
|
||||
<Suspense fallback={<Spin />}>
|
||||
<InventoryListPage />
|
||||
<Suspense fallback={<Spin/>}>
|
||||
<InventoryListPage/>
|
||||
</Suspense>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/courtesycars/"
|
||||
element={
|
||||
<Suspense fallback={<Spin />}>
|
||||
<CourtesyCarsPage />
|
||||
<Suspense fallback={<Spin/>}>
|
||||
<CourtesyCarsPage/>
|
||||
</Suspense>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/courtesycars/new"
|
||||
element={
|
||||
<Suspense fallback={<Spin />}>
|
||||
<CourtesyCarCreateContainer />
|
||||
<Suspense fallback={<Spin/>}>
|
||||
<CourtesyCarCreateContainer/>
|
||||
</Suspense>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/courtesycars/contracts"
|
||||
element={
|
||||
<Suspense fallback={<Spin />}>
|
||||
<ContractsList />
|
||||
<Suspense fallback={<Spin/>}>
|
||||
<ContractsList/>
|
||||
</Suspense>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/courtesycars/contracts/new"
|
||||
element={
|
||||
<Suspense fallback={<Spin />}>
|
||||
<ContractCreatePage />
|
||||
<Suspense fallback={<Spin/>}>
|
||||
<ContractCreatePage/>
|
||||
</Suspense>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/courtesycars/contracts/:contractId"
|
||||
element={
|
||||
<Suspense fallback={<Spin />}>
|
||||
<ContractDetailPage />
|
||||
<Suspense fallback={<Spin/>}>
|
||||
<ContractDetailPage/>
|
||||
</Suspense>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/courtesycars/:ccId"
|
||||
element={
|
||||
<Suspense fallback={<Spin />}>
|
||||
<CourtesyCarDetailContainer />
|
||||
<Suspense fallback={<Spin/>}>
|
||||
<CourtesyCarDetailContainer/>
|
||||
</Suspense>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/profile"
|
||||
element={
|
||||
<Suspense fallback={<Spin />}>
|
||||
<ProfilePage />
|
||||
<Suspense fallback={<Spin/>}>
|
||||
<ProfilePage/>
|
||||
</Suspense>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/vehicles"
|
||||
element={
|
||||
<Suspense fallback={<Spin />}>
|
||||
<VehiclesContainer />
|
||||
<Suspense fallback={<Spin/>}>
|
||||
<VehiclesContainer/>
|
||||
</Suspense>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/production/list"
|
||||
element={
|
||||
<Suspense fallback={<Spin />}>
|
||||
<ProductionListPage />
|
||||
<Suspense fallback={<Spin/>}>
|
||||
<ProductionListPage/>
|
||||
</Suspense>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/production/board"
|
||||
element={
|
||||
<Suspense fallback={<Spin />}>
|
||||
<ProductionBoardPage />
|
||||
<Suspense fallback={<Spin/>}>
|
||||
<ProductionBoardPage/>
|
||||
</Suspense>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/vehicles/:vehId"
|
||||
element={
|
||||
<Suspense fallback={<Spin />}>
|
||||
<VehiclesDetailContainer />
|
||||
<Suspense fallback={<Spin/>}>
|
||||
<VehiclesDetailContainer/>
|
||||
</Suspense>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/bills"
|
||||
element={
|
||||
<Suspense fallback={<Spin />}>
|
||||
<BillsListPage />
|
||||
<Suspense fallback={<Spin/>}>
|
||||
<BillsListPage/>
|
||||
</Suspense>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/owners"
|
||||
element={
|
||||
<Suspense fallback={<Spin />}>
|
||||
<OwnersContainer />
|
||||
<Suspense fallback={<Spin/>}>
|
||||
<OwnersContainer/>
|
||||
</Suspense>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/owners/:ownerId"
|
||||
element={
|
||||
<Suspense fallback={<Spin />}>
|
||||
<OwnersDetailContainer />
|
||||
<Suspense fallback={<Spin/>}>
|
||||
<OwnersDetailContainer/>
|
||||
</Suspense>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/schedule"
|
||||
element={
|
||||
<Suspense fallback={<Spin />}>
|
||||
<ScheduleContainer />
|
||||
<Suspense fallback={<Spin/>}>
|
||||
<ScheduleContainer/>
|
||||
</Suspense>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/available"
|
||||
element={
|
||||
<Suspense fallback={<Spin />}>
|
||||
<JobsAvailablePage />
|
||||
<Suspense fallback={<Spin/>}>
|
||||
<JobsAvailablePage/>
|
||||
</Suspense>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/shop"
|
||||
element={
|
||||
<Suspense fallback={<Spin />}>
|
||||
<ShopPage />
|
||||
<Suspense fallback={<Spin/>}>
|
||||
<ShopPage/>
|
||||
</Suspense>
|
||||
}
|
||||
/>
|
||||
@@ -426,16 +426,16 @@ export function Manage({ conflict, bodyshop, enableJoyRide, joyRideSteps, setJoy
|
||||
<Route
|
||||
path="/shop/vendors"
|
||||
element={
|
||||
<Suspense fallback={<Spin />}>
|
||||
<ShopVendorPageContainer />
|
||||
<Suspense fallback={<Spin/>}>
|
||||
<ShopVendorPageContainer/>
|
||||
</Suspense>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/shop/csi"
|
||||
element={
|
||||
<Suspense fallback={<Spin />}>
|
||||
<ShopCsiPageContainer />
|
||||
<Suspense fallback={<Spin/>}>
|
||||
<ShopCsiPageContainer/>
|
||||
</Suspense>
|
||||
}
|
||||
/>
|
||||
@@ -443,8 +443,8 @@ export function Manage({ conflict, bodyshop, enableJoyRide, joyRideSteps, setJoy
|
||||
<Route
|
||||
path="/accounting/qbo"
|
||||
element={
|
||||
<Suspense fallback={<Spin />}>
|
||||
<AccountingQboCallback />
|
||||
<Suspense fallback={<Spin/>}>
|
||||
<AccountingQboCallback/>
|
||||
</Suspense>
|
||||
}
|
||||
/>
|
||||
@@ -452,56 +452,56 @@ export function Manage({ conflict, bodyshop, enableJoyRide, joyRideSteps, setJoy
|
||||
<Route
|
||||
path="/accounting/receivables"
|
||||
element={
|
||||
<Suspense fallback={<Spin />}>
|
||||
<AccountingReceivables />
|
||||
<Suspense fallback={<Spin/>}>
|
||||
<AccountingReceivables/>
|
||||
</Suspense>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/accounting/payables"
|
||||
element={
|
||||
<Suspense fallback={<Spin />}>
|
||||
<AccountingPayables />
|
||||
<Suspense fallback={<Spin/>}>
|
||||
<AccountingPayables/>
|
||||
</Suspense>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/accounting/payments"
|
||||
element={
|
||||
<Suspense fallback={<Spin />}>
|
||||
<AccountingPayments />
|
||||
<Suspense fallback={<Spin/>}>
|
||||
<AccountingPayments/>
|
||||
</Suspense>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/accounting/exportlogs"
|
||||
element={
|
||||
<Suspense fallback={<Spin />}>
|
||||
<ExportLogs />
|
||||
<Suspense fallback={<Spin/>}>
|
||||
<ExportLogs/>
|
||||
</Suspense>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/ttapprovals"
|
||||
element={
|
||||
<Suspense fallback={<Spin />}>
|
||||
<TtApprovals />
|
||||
<Suspense fallback={<Spin/>}>
|
||||
<TtApprovals/>
|
||||
</Suspense>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/partsqueue"
|
||||
element={
|
||||
<Suspense fallback={<Spin />}>
|
||||
<PartsQueue />
|
||||
<Suspense fallback={<Spin/>}>
|
||||
<PartsQueue/>
|
||||
</Suspense>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/phonebook"
|
||||
element={
|
||||
<Suspense fallback={<Spin />}>
|
||||
<Phonebook />
|
||||
<Suspense fallback={<Spin/>}>
|
||||
<Phonebook/>
|
||||
</Suspense>
|
||||
}
|
||||
/>
|
||||
@@ -509,65 +509,65 @@ export function Manage({ conflict, bodyshop, enableJoyRide, joyRideSteps, setJoy
|
||||
<Route
|
||||
path="/payments"
|
||||
element={
|
||||
<Suspense fallback={<Spin />}>
|
||||
<PaymentsAll />
|
||||
<Suspense fallback={<Spin/>}>
|
||||
<PaymentsAll/>
|
||||
</Suspense>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/shiftclock"
|
||||
element={
|
||||
<Suspense fallback={<Spin />}>
|
||||
<ShiftClock />
|
||||
<Suspense fallback={<Spin/>}>
|
||||
<ShiftClock/>
|
||||
</Suspense>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/scoreboard"
|
||||
element={
|
||||
<Suspense fallback={<Spin />}>
|
||||
<Scoreboard />
|
||||
<Suspense fallback={<Spin/>}>
|
||||
<Scoreboard/>
|
||||
</Suspense>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/timetickets"
|
||||
element={
|
||||
<Suspense fallback={<Spin />}>
|
||||
<TimeTicketsAll />
|
||||
<Suspense fallback={<Spin/>}>
|
||||
<TimeTicketsAll/>
|
||||
</Suspense>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/help"
|
||||
element={
|
||||
<Suspense fallback={<Spin />}>
|
||||
<Help />
|
||||
<Suspense fallback={<Spin/>}>
|
||||
<Help/>
|
||||
</Suspense>
|
||||
}
|
||||
/>
|
||||
<Route path="/emailtest" element={<EmailTest />} />
|
||||
<Route path="/emailtest" element={<EmailTest/>}/>
|
||||
<Route
|
||||
path="/dashboard"
|
||||
element={
|
||||
<Suspense fallback={<Spin />}>
|
||||
<Dashboard />
|
||||
<Suspense fallback={<Spin/>}>
|
||||
<Dashboard/>
|
||||
</Suspense>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/dms"
|
||||
element={
|
||||
<Suspense fallback={<Spin />}>
|
||||
<Dms />
|
||||
<Suspense fallback={<Spin/>}>
|
||||
<Dms/>
|
||||
</Suspense>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/dmsap"
|
||||
element={
|
||||
<Suspense fallback={<Spin />}>
|
||||
<DmsPayables />
|
||||
<Suspense fallback={<Spin/>}>
|
||||
<DmsPayables/>
|
||||
</Suspense>
|
||||
}
|
||||
/>
|
||||
@@ -577,16 +577,16 @@ export function Manage({ conflict, bodyshop, enableJoyRide, joyRideSteps, setJoy
|
||||
|
||||
let PageContent;
|
||||
|
||||
if (conflict) PageContent = <ConflictComponent />;
|
||||
else if (bodyshop && bodyshop.sub_status !== "active") PageContent = <ShopSubStatusComponent />;
|
||||
if (conflict) PageContent = <ConflictComponent/>;
|
||||
else if (bodyshop && bodyshop.sub_status !== "active") PageContent = <ShopSubStatusComponent/>;
|
||||
else PageContent = AppRouteTable;
|
||||
|
||||
return (
|
||||
<>
|
||||
<ChatAffixContainer bodyshop={bodyshop} chatVisible={chatVisible} />
|
||||
<Layout style={{ minHeight: "100vh" }} className="layout-container">
|
||||
<UpdateAlert />
|
||||
<HeaderContainer />
|
||||
<ChatAffixContainer bodyshop={bodyshop} chatVisible={chatVisible}/>
|
||||
<Layout style={{minHeight: "100vh"}} className="layout-container">
|
||||
<UpdateAlert/>
|
||||
<HeaderContainer/>
|
||||
<Content className="content-container">
|
||||
<Joyride
|
||||
debug
|
||||
@@ -603,12 +603,12 @@ export function Manage({ conflict, bodyshop, enableJoyRide, joyRideSteps, setJoy
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<PartnerPingComponent />
|
||||
<Sentry.ErrorBoundary fallback={<ErrorBoundary />} showDialog>
|
||||
<PartnerPingComponent/>
|
||||
<Sentry.ErrorBoundary fallback={<ErrorBoundary/>} showDialog>
|
||||
{PageContent}
|
||||
</Sentry.ErrorBoundary>
|
||||
|
||||
<FloatButton.BackTop style={{ right: 100, bottom: 25 }} />
|
||||
<FloatButton.BackTop style={{right: 100, bottom: 25}}/>
|
||||
</Content>
|
||||
<Footer>
|
||||
<div
|
||||
@@ -620,7 +620,7 @@ export function Manage({ conflict, bodyshop, enableJoyRide, joyRideSteps, setJoy
|
||||
margin: "1rem 0rem"
|
||||
}}
|
||||
>
|
||||
<div style={{ display: "flex" }}>
|
||||
<div style={{display: "flex"}}>
|
||||
<div>
|
||||
{`${InstanceRenderManager({
|
||||
imex: t("titles.imexonline"),
|
||||
@@ -628,9 +628,9 @@ export function Manage({ conflict, bodyshop, enableJoyRide, joyRideSteps, setJoy
|
||||
promanager: t("titles.promanager")
|
||||
})} - ${import.meta.env.VITE_APP_GIT_SHA_DATE}`}
|
||||
</div>
|
||||
<div id="noticeable-widget" style={{ marginLeft: "1rem" }} />
|
||||
<div id="noticeable-widget" style={{marginLeft: "1rem"}}/>
|
||||
</div>
|
||||
<Link to="/disclaimer" target="_blank" style={{ color: "#ccc" }}>
|
||||
<Link to="/disclaimer" target="_blank" style={{color: "#ccc"}}>
|
||||
Disclaimer & Notices
|
||||
</Link>
|
||||
</div>
|
||||
@@ -647,7 +647,8 @@ export function Manage({ conflict, bodyshop, enableJoyRide, joyRideSteps, setJoy
|
||||
Get Tours
|
||||
</Button>
|
||||
{tours.map((tour) => (
|
||||
<Tag key={tour.id} onClick={() => window.productFruits.api.tours.tryStartTour(tour.id)}>
|
||||
<Tag key={tour.id}
|
||||
onClick={() => window.productFruits.api.tours.tryStartTour(tour.id)}>
|
||||
{tour.name}
|
||||
</Tag>
|
||||
))}
|
||||
|
||||
@@ -64,7 +64,8 @@ export function MyTasksPageContainer({
|
||||
}, [setTaskUpsertContext]);
|
||||
|
||||
return (
|
||||
<TasksPageComponent type={TaskPageTypes.ALL_TASKS} currentUser={currentUser} bodyshop={bodyshop}/>
|
||||
<TasksPageComponent type={TaskPageTypes.ALL_TASKS} currentUser={currentUser}
|
||||
bodyshop={bodyshop}/>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -38,7 +38,8 @@ export function MyTasksPageContainer({bodyshop, currentUser, setBreadcrumbs, set
|
||||
}, [t, setBreadcrumbs, setSelectedHeader]);
|
||||
|
||||
return (
|
||||
<TasksPageComponent type={TaskPageTypes.MY_TASKS} currentUser={currentUser} bodyshop={bodyshop}/>
|
||||
<TasksPageComponent type={TaskPageTypes.MY_TASKS} currentUser={currentUser}
|
||||
bodyshop={bodyshop}/>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
export const TaskPageTypes = {
|
||||
MY_TASKS: 'myTasks',
|
||||
ALL_TASKS: 'allTasks',
|
||||
MY_TASKS: 'myTasks',
|
||||
ALL_TASKS: 'allTasks',
|
||||
};
|
||||
|
||||
export default TaskPageTypes;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user