- reapply proper prettier formatting.

Signed-off-by: Dave Richer <dave@imexsystems.ca>
This commit is contained in:
Dave Richer
2024-04-03 14:09:09 -04:00
parent e51f72ff98
commit ab031c01de
26 changed files with 10734 additions and 10641 deletions

View File

@@ -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) || []

View File

@@ -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>
)
});

View File

@@ -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>
);

View File

@@ -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 {

View File

@@ -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,
});

View File

@@ -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}/>
</>
);
}

View File

@@ -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"

View File

@@ -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}

View File

@@ -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)}

View File

@@ -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 {

View File

@@ -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/>}

View File

@@ -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 || ''}`);

View File

@@ -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}/>

View File

@@ -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!]!) {

View File

@@ -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`

View File

@@ -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
}
}
}
`

View File

@@ -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);

View File

@@ -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>
))}

View File

@@ -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}/>
);
}

View File

@@ -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}/>
);
}

View File

@@ -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

View File

@@ -15,9 +15,9 @@ const moment = require("moment");
// TODO use InstanceDynamicManager on the footer default to prevent the word IMEX from being hardcoded
const generateEmailTemplate = (strings) => {
const generateEmailTemplate = (strings) => {
let now = () =>moment().format('MM/DD/YYYY @ hh:mm a');
let now = () => moment().format('MM/DD/YYYY @ hh:mm a');
return `
<html xmlns="http://www.w3.org/1999/xhtml" lang="en-US">

View File

@@ -5,13 +5,13 @@ require("dotenv").config({
const axios = require("axios");
let nodemailer = require("nodemailer");
let aws = require("@aws-sdk/client-ses");
let { defaultProvider } = require("@aws-sdk/credential-provider-node");
let {defaultProvider} = require("@aws-sdk/credential-provider-node");
const InstanceManager = require("../utils/instanceMgr").default;
const logger = require("../utils/logger");
const client = require("../graphql-client/graphql-client").client;
const queries = require("../graphql-client/queries");
const {isObject} = require("lodash");
const generateEmailTemplate = require('./generateTemplate');
const generateEmailTemplate = require('./generateTemplate');
const ses = new aws.SES({
// The key apiVersion is no longer supported in v3, and can be removed.
@@ -25,10 +25,10 @@ const ses = new aws.SES({
});
let transporter = nodemailer.createTransport({
SES: { ses, aws }
SES: {ses, aws}
});
exports.sendServerEmail = async function ({ subject, text }) {
exports.sendServerEmail = async function ({subject, text}) {
if (process.env.NODE_ENV === undefined) return;
try {
transporter.sendMail(
@@ -60,7 +60,7 @@ exports.sendServerEmail = async function ({ subject, text }) {
logger.log("server-email-failure", "error", null, null, error);
}
};
exports.sendTaskEmail = async function ({ to, subject, text, attachments }) {
exports.sendTaskEmail = async function ({to, subject, text, attachments}) {
try {
transporter.sendMail(
{
@@ -125,12 +125,12 @@ exports.sendEmail = async (req, res) => {
attachments:
[
...((req.body.attachments &&
req.body.attachments.map((a) => {
return {
filename: a.filename,
path: a.path
};
})) ||
req.body.attachments.map((a) => {
return {
filename: a.filename,
path: a.path
};
})) ||
[]),
...downloadedMedia.map((a) => {
return {
@@ -157,7 +157,7 @@ exports.sendEmail = async (req, res) => {
replyTo: req.body.ReplyTo.Email,
to: req.body.to,
cc: req.body.cc,
subject: req.body.subject,
subject: req.body.subject,
templateStrings: req.body.templateStrings
// info,
});
@@ -186,14 +186,14 @@ exports.sendEmail = async (req, res) => {
subject: req.body.subject,
bodyshopid: req.body.bodyshopid
});
res.status(500).json({ success: false, error: err });
res.status(500).json({success: false, error: err});
}
}
);
};
async function getImage(imageUrl) {
let image = await axios.get(imageUrl, { responseType: "arraybuffer" });
let image = await axios.get(imageUrl, {responseType: "arraybuffer"});
let raw = Buffer.from(image.data).toString("base64");
return "data:" + image.headers["content-type"] + ";base64," + raw;
}
@@ -281,10 +281,10 @@ exports.emailBounce = async function (req, res) {
})} has tried to deliver an email with the subject: ${subject} to the intended recipients but encountered an error.
${body.bounce?.bouncedRecipients.map(
(r) =>
`Recipient: ${r.emailAddress} | Status: ${r.action} | Code: ${r.diagnosticCode}
(r) =>
`Recipient: ${r.emailAddress} | Status: ${r.action} | Code: ${r.diagnosticCode}
`
)}
)}
`
},
(err, info) => {