IO-3255 Clean up front end components for pm.
This commit is contained in:
@@ -0,0 +1,73 @@
|
|||||||
|
import { AlertOutlined, BulbOutlined } from "@ant-design/icons";
|
||||||
|
import { Button, Layout, Space } from "antd";
|
||||||
|
import { useEffect } from "react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { connect } from "react-redux";
|
||||||
|
import { Link } from "react-router-dom";
|
||||||
|
import { createStructuredSelector } from "reselect";
|
||||||
|
import WssStatusDisplayComponent from "../../components/wss-status-display/wss-status-display.component.jsx";
|
||||||
|
import { addAlerts } from "../../redux/application/application.actions.js";
|
||||||
|
import { selectAlerts } from "../../redux/application/application.selectors.js";
|
||||||
|
import { selectBodyshop, selectInstanceConflict } from "../../redux/user/user.selectors";
|
||||||
|
import InstanceRenderManager from "../../utils/instanceRenderMgr.js";
|
||||||
|
const { Footer } = Layout;
|
||||||
|
|
||||||
|
const mapStateToProps = createStructuredSelector({
|
||||||
|
conflict: selectInstanceConflict,
|
||||||
|
bodyshop: selectBodyshop,
|
||||||
|
alerts: selectAlerts
|
||||||
|
});
|
||||||
|
|
||||||
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
|
setAlerts: (alerts) => dispatch(addAlerts(alerts))
|
||||||
|
});
|
||||||
|
|
||||||
|
export function GlobalFooter() {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
window.Canny("initChangelog", {
|
||||||
|
appID: "680bd2c7ee501290377f6686",
|
||||||
|
position: "top",
|
||||||
|
align: "left",
|
||||||
|
theme: "light" // options: light [default], dark, auto
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Footer>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
justifyContent: "center",
|
||||||
|
alignItems: "center",
|
||||||
|
margin: "1rem 0rem"
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Link to="/manage/feature-request">
|
||||||
|
<Button icon={<BulbOutlined />} type="text">
|
||||||
|
{t("general.labels.feature-request")}
|
||||||
|
</Button>
|
||||||
|
</Link>
|
||||||
|
<Space>
|
||||||
|
<WssStatusDisplayComponent />
|
||||||
|
<div>
|
||||||
|
{`${InstanceRenderManager({
|
||||||
|
imex: t("titles.imexonline"),
|
||||||
|
rome: t("titles.romeonline")
|
||||||
|
})} - ${import.meta.env.VITE_APP_GIT_SHA_DATE}`}
|
||||||
|
</div>
|
||||||
|
<Button icon={<AlertOutlined />} data-canny-changelog type="text">
|
||||||
|
{t("general.labels.changelog")}
|
||||||
|
</Button>
|
||||||
|
</Space>
|
||||||
|
<Link to="/disclaimer" target="_blank" style={{ color: "#ccc" }}>
|
||||||
|
Disclaimer & Notices
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</Footer>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default connect(mapStateToProps, mapDispatchToProps)(GlobalFooter);
|
||||||
@@ -12,7 +12,7 @@ import { PageHeader } from "@ant-design/pro-layout";
|
|||||||
import { useMutation } from "@apollo/client";
|
import { useMutation } from "@apollo/client";
|
||||||
import { Button, Dropdown, Input, Space, Table, Tag } from "antd";
|
import { Button, Dropdown, Input, Space, Table, Tag } from "antd";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import React, { useState } from "react";
|
import { useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import { createStructuredSelector } from "reselect";
|
import { createStructuredSelector } from "reselect";
|
||||||
@@ -32,7 +32,7 @@ import JobLinesBillRefernece from "../job-lines-bill-reference/job-lines-bill-re
|
|||||||
import { useSplitTreatments } from "@splitsoftware/splitio-react";
|
import { useSplitTreatments } from "@splitsoftware/splitio-react";
|
||||||
import _ from "lodash";
|
import _ from "lodash";
|
||||||
import { FaTasks } from "react-icons/fa";
|
import { FaTasks } from "react-icons/fa";
|
||||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
import { selectBodyshop, selectPartsManagementOnly } from "../../redux/user/user.selectors";
|
||||||
import dayjs from "../../utils/day";
|
import dayjs from "../../utils/day";
|
||||||
import InstanceRenderManager from "../../utils/instanceRenderMgr";
|
import InstanceRenderManager from "../../utils/instanceRenderMgr";
|
||||||
import { HasFeatureAccess } from "../feature-wrapper/feature-wrapper.component";
|
import { HasFeatureAccess } from "../feature-wrapper/feature-wrapper.component";
|
||||||
@@ -45,11 +45,13 @@ import PartsOrderDrawer from "../parts-order-list-table/parts-order-list-table-d
|
|||||||
import PartsOrderModalContainer from "../parts-order-modal/parts-order-modal.container";
|
import PartsOrderModalContainer from "../parts-order-modal/parts-order-modal.container";
|
||||||
import JobLinesExpander from "./job-lines-expander.component";
|
import JobLinesExpander from "./job-lines-expander.component";
|
||||||
import JobLinesPartPriceChange from "./job-lines-part-price-change.component";
|
import JobLinesPartPriceChange from "./job-lines-part-price-change.component";
|
||||||
|
import JobLinesExpanderSimple from "./jobs-lines-expander-simple.component";
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
bodyshop: selectBodyshop,
|
bodyshop: selectBodyshop,
|
||||||
jobRO: selectJobReadOnly,
|
jobRO: selectJobReadOnly,
|
||||||
technician: selectTechnician
|
technician: selectTechnician,
|
||||||
|
partsManagementOnly: selectPartsManagementOnly
|
||||||
});
|
});
|
||||||
|
|
||||||
const mapDispatchToProps = (dispatch) => ({
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
@@ -79,7 +81,7 @@ export function JobLinesComponent({
|
|||||||
handleBillOnRowClick,
|
handleBillOnRowClick,
|
||||||
handlePartsOrderOnRowClick,
|
handlePartsOrderOnRowClick,
|
||||||
handlePartsDispatchOnRowClick,
|
handlePartsDispatchOnRowClick,
|
||||||
simple
|
partsManagementOnly
|
||||||
}) {
|
}) {
|
||||||
const [deleteJobLine] = useMutation(DELETE_JOB_LINE_BY_PK);
|
const [deleteJobLine] = useMutation(DELETE_JOB_LINE_BY_PK);
|
||||||
const {
|
const {
|
||||||
@@ -93,7 +95,13 @@ export function JobLinesComponent({
|
|||||||
const [selectedLines, setSelectedLines] = useState([]);
|
const [selectedLines, setSelectedLines] = useState([]);
|
||||||
const [state, setState] = useState({
|
const [state, setState] = useState({
|
||||||
sortedInfo: {},
|
sortedInfo: {},
|
||||||
filteredInfo: {}
|
filteredInfo: {
|
||||||
|
...(partsManagementOnly
|
||||||
|
? {
|
||||||
|
part_type: ["PAN", "PAC", "PAR", "PAL", "PAA", "PAM", "PAP", "PAS", "PASL", "PAG"]
|
||||||
|
}
|
||||||
|
: {})
|
||||||
|
}
|
||||||
});
|
});
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const jobIsPrivate = bodyshop.md_ins_cos.find((c) => c.name === job.ins_co_nm)?.private;
|
const jobIsPrivate = bodyshop.md_ins_cos.find((c) => c.name === job.ins_co_nm)?.private;
|
||||||
@@ -221,7 +229,7 @@ export function JobLinesComponent({
|
|||||||
sorter: (a, b) => a.part_qty - b.part_qty,
|
sorter: (a, b) => a.part_qty - b.part_qty,
|
||||||
sortOrder: state.sortedInfo.columnKey === "part_qty" && state.sortedInfo.order
|
sortOrder: state.sortedInfo.columnKey === "part_qty" && state.sortedInfo.order
|
||||||
},
|
},
|
||||||
...(!simple
|
...(!partsManagementOnly
|
||||||
? [
|
? [
|
||||||
{
|
{
|
||||||
title: t("joblines.fields.mod_lbr_ty"),
|
title: t("joblines.fields.mod_lbr_ty"),
|
||||||
@@ -273,7 +281,7 @@ export function JobLinesComponent({
|
|||||||
key: "location",
|
key: "location",
|
||||||
render: (text, record) => <JobLineLocationPopup jobline={record} disabled={jobRO} />
|
render: (text, record) => <JobLineLocationPopup jobline={record} disabled={jobRO} />
|
||||||
},
|
},
|
||||||
...(!simple && HasFeatureAccess({ featureName: "bills", bodyshop })
|
...(!partsManagementOnly && HasFeatureAccess({ featureName: "bills", bodyshop })
|
||||||
? [
|
? [
|
||||||
{
|
{
|
||||||
title: t("joblines.labels.billref"),
|
title: t("joblines.labels.billref"),
|
||||||
@@ -307,70 +315,74 @@ export function JobLinesComponent({
|
|||||||
onFilter: (value, record) => value.includes(record.status),
|
onFilter: (value, record) => value.includes(record.status),
|
||||||
render: (text, record) => <JobLineStatusPopup jobline={record} disabled={jobRO} />
|
render: (text, record) => <JobLineStatusPopup jobline={record} disabled={jobRO} />
|
||||||
},
|
},
|
||||||
{
|
...!partsManagementOnly
|
||||||
title: t("general.labels.actions"),
|
? [
|
||||||
dataIndex: "actions",
|
{
|
||||||
key: "actions",
|
title: t("general.labels.actions"),
|
||||||
render: (text, record) => (
|
dataIndex: "actions",
|
||||||
<Space>
|
key: "actions",
|
||||||
{(record.manual_line || jobIsPrivate) && !technician && (
|
render: (text, record) => (
|
||||||
<>
|
<Space>
|
||||||
<Button
|
{(record.manual_line || jobIsPrivate) && !technician && (
|
||||||
disabled={jobRO}
|
<>
|
||||||
onClick={() => {
|
<Button
|
||||||
setJobLineEditContext({
|
disabled={jobRO}
|
||||||
actions: { refetch: refetch, submit: form && form.submit },
|
onClick={() => {
|
||||||
context: { ...record, jobid: job.id }
|
setJobLineEditContext({
|
||||||
});
|
actions: { refetch: refetch, submit: form && form.submit },
|
||||||
}}
|
context: { ...record, jobid: job.id }
|
||||||
>
|
});
|
||||||
<EditFilled />
|
}}
|
||||||
</Button>
|
>
|
||||||
</>
|
<EditFilled />
|
||||||
)}
|
</Button>
|
||||||
<Button
|
</>
|
||||||
title={t("tasks.buttons.create")}
|
)}
|
||||||
onClick={() => {
|
<Button
|
||||||
setTaskUpsertContext({
|
title={t("tasks.buttons.create")}
|
||||||
context: {
|
onClick={() => {
|
||||||
jobid: job.id,
|
setTaskUpsertContext({
|
||||||
joblineid: record.id
|
context: {
|
||||||
}
|
jobid: job.id,
|
||||||
});
|
joblineid: record.id
|
||||||
}}
|
}
|
||||||
>
|
});
|
||||||
<FaTasks />
|
}}
|
||||||
</Button>
|
>
|
||||||
{(record.manual_line || jobIsPrivate) && !technician && (
|
<FaTasks />
|
||||||
<>
|
</Button>
|
||||||
<Button
|
{(record.manual_line || jobIsPrivate) && !technician && (
|
||||||
disabled={jobRO}
|
<>
|
||||||
onClick={async () => {
|
<Button
|
||||||
await deleteJobLine({
|
disabled={jobRO}
|
||||||
variables: { joblineId: record.id },
|
onClick={async () => {
|
||||||
update(cache) {
|
await deleteJobLine({
|
||||||
cache.modify({
|
variables: { joblineId: record.id },
|
||||||
fields: {
|
update(cache) {
|
||||||
joblines(existingJobLines, { readField }) {
|
cache.modify({
|
||||||
return existingJobLines.filter((jlRef) => record.id !== readField("id", jlRef));
|
fields: {
|
||||||
|
joblines(existingJobLines, { readField }) {
|
||||||
|
return existingJobLines.filter((jlRef) => record.id !== readField("id", jlRef));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
});
|
await axios.post("/job/totalsssu", {
|
||||||
}
|
id: job.id
|
||||||
});
|
});
|
||||||
await axios.post("/job/totalsssu", {
|
refetch && refetch();
|
||||||
id: job.id
|
}}
|
||||||
});
|
>
|
||||||
refetch && refetch();
|
<DeleteFilled />
|
||||||
}}
|
</Button>
|
||||||
>
|
</>
|
||||||
<DeleteFilled />
|
)}
|
||||||
</Button>
|
</Space>
|
||||||
</>
|
)
|
||||||
)}
|
}
|
||||||
</Space>
|
]
|
||||||
)
|
: []
|
||||||
}
|
|
||||||
];
|
];
|
||||||
|
|
||||||
const handleTableChange = (pagination, filters, sorter) => {
|
const handleTableChange = (pagination, filters, sorter) => {
|
||||||
@@ -543,17 +555,19 @@ export function JobLinesComponent({
|
|||||||
<Dropdown menu={markMenu} trigger={["click"]}>
|
<Dropdown menu={markMenu} trigger={["click"]}>
|
||||||
<Button id="repair-data-mark-button">{t("jobs.actions.mark")}</Button>
|
<Button id="repair-data-mark-button">{t("jobs.actions.mark")}</Button>
|
||||||
</Dropdown>
|
</Dropdown>
|
||||||
<Button
|
{!partsManagementOnly && (
|
||||||
disabled={jobRO || technician}
|
<Button
|
||||||
onClick={() => {
|
disabled={jobRO || technician}
|
||||||
setJobLineEditContext({
|
onClick={() => {
|
||||||
actions: { refetch: refetch },
|
setJobLineEditContext({
|
||||||
context: { jobid: job.id }
|
actions: { refetch: refetch },
|
||||||
});
|
context: { jobid: job.id }
|
||||||
}}
|
});
|
||||||
>
|
}}
|
||||||
{t("joblines.actions.new")}
|
>
|
||||||
</Button>
|
{t("joblines.actions.new")}
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
{InstanceRenderManager({ rome: <JobSendPartPriceChangeComponent job={job} disabled={technician} /> })}
|
{InstanceRenderManager({ rome: <JobSendPartPriceChangeComponent job={job} disabled={technician} /> })}
|
||||||
<JobCreateIOU job={job} selectedJobLines={selectedLines} />
|
<JobCreateIOU job={job} selectedJobLines={selectedLines} />
|
||||||
<Input.Search
|
<Input.Search
|
||||||
@@ -577,7 +591,12 @@ export function JobLinesComponent({
|
|||||||
x: true
|
x: true
|
||||||
}}
|
}}
|
||||||
expandable={{
|
expandable={{
|
||||||
expandedRowRender: (record) => <JobLinesExpander jobline={record} jobid={job.id} />,
|
expandedRowRender: (record) =>
|
||||||
|
partsManagementOnly ? (
|
||||||
|
<JobLinesExpanderSimple jobline={record} jobid={job.id} />
|
||||||
|
) : (
|
||||||
|
<JobLinesExpander jobline={record} jobid={job.id} />
|
||||||
|
),
|
||||||
rowExpandable: (record) => true,
|
rowExpandable: (record) => true,
|
||||||
//expandRowByClick: true,
|
//expandRowByClick: true,
|
||||||
expandIcon: ({ expanded, onExpand, record }) =>
|
expandIcon: ({ expanded, onExpand, record }) =>
|
||||||
|
|||||||
@@ -0,0 +1,83 @@
|
|||||||
|
import { useQuery } from "@apollo/client";
|
||||||
|
import { Col, Row, Skeleton, Timeline, Typography } from "antd";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { connect } from "react-redux";
|
||||||
|
import { Link } from "react-router-dom";
|
||||||
|
import { createStructuredSelector } from "reselect";
|
||||||
|
import { GET_JOB_LINE_ORDERS } from "../../graphql/jobs.queries";
|
||||||
|
import { selectTechnician } from "../../redux/tech/tech.selectors.js";
|
||||||
|
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||||
|
import { DateFormatter } from "../../utils/DateFormatter";
|
||||||
|
import AlertComponent from "../alert/alert.component";
|
||||||
|
|
||||||
|
const mapStateToProps = createStructuredSelector({
|
||||||
|
bodyshop: selectBodyshop,
|
||||||
|
technician: selectTechnician
|
||||||
|
});
|
||||||
|
|
||||||
|
const mapDispatchToProps = (dispatch) => ({});
|
||||||
|
|
||||||
|
export default connect(mapStateToProps, mapDispatchToProps)(JobLinesExpanderSimple);
|
||||||
|
|
||||||
|
export function JobLinesExpanderSimple({ jobline, jobid, bodyshop, technician }) {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const { loading, error, data } = useQuery(GET_JOB_LINE_ORDERS, {
|
||||||
|
fetchPolicy: "network-only",
|
||||||
|
nextFetchPolicy: "network-only",
|
||||||
|
variables: {
|
||||||
|
joblineid: jobline.id
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (loading) return <Skeleton />;
|
||||||
|
if (error) return <AlertComponent message={error.message} type="error" />;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Row>
|
||||||
|
<Col span={24}>
|
||||||
|
<Typography.Title level={4}>{t("parts_orders.labels.parts_orders")}</Typography.Title>
|
||||||
|
<Timeline
|
||||||
|
items={
|
||||||
|
data.parts_order_lines.length > 0
|
||||||
|
? data.parts_order_lines.map((line) => ({
|
||||||
|
key: line.id,
|
||||||
|
children: (
|
||||||
|
<Row wrap>
|
||||||
|
<Col span={4}>
|
||||||
|
{!technician ? (
|
||||||
|
<>
|
||||||
|
<Link to={`/manage/jobs/${jobid}?partsorderid=${line.parts_order.id}`}>
|
||||||
|
{line.parts_order.order_number}
|
||||||
|
</Link>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
`${line.parts_order.order_number}`
|
||||||
|
)}
|
||||||
|
</Col>
|
||||||
|
<Col span={4}>
|
||||||
|
<DateFormatter>{line.parts_order.order_date}</DateFormatter>
|
||||||
|
</Col>
|
||||||
|
<Col span={4}>{line.parts_order.vendor.name}</Col>
|
||||||
|
{line.backordered_eta ? (
|
||||||
|
<Col span={4}>
|
||||||
|
<span>
|
||||||
|
{`${t("parts_orders.fields.backordered_eta")}: `}
|
||||||
|
<DateFormatter>{line.backordered_eta}</DateFormatter>
|
||||||
|
</span>
|
||||||
|
</Col>
|
||||||
|
) : null}
|
||||||
|
</Row>
|
||||||
|
)
|
||||||
|
}))
|
||||||
|
: [
|
||||||
|
{
|
||||||
|
key: "no-orders",
|
||||||
|
children: t("parts_orders.labels.notyetordered")
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -11,7 +11,7 @@ import { UPDATE_JOB } from "../../graphql/jobs.queries";
|
|||||||
import { insertAuditTrail } from "../../redux/application/application.actions.js";
|
import { insertAuditTrail } from "../../redux/application/application.actions.js";
|
||||||
import { selectJobReadOnly } from "../../redux/application/application.selectors";
|
import { selectJobReadOnly } from "../../redux/application/application.selectors";
|
||||||
import { setModalContext } from "../../redux/modals/modals.actions";
|
import { setModalContext } from "../../redux/modals/modals.actions";
|
||||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
import { selectBodyshop, selectPartsManagementOnly } from "../../redux/user/user.selectors";
|
||||||
import AuditTrailMapping from "../../utils/AuditTrailMappings.js";
|
import AuditTrailMapping from "../../utils/AuditTrailMappings.js";
|
||||||
import CurrencyFormatter from "../../utils/CurrencyFormatter";
|
import CurrencyFormatter from "../../utils/CurrencyFormatter";
|
||||||
import { DateTimeFormatter, DateTimeFormatterFunction } from "../../utils/DateFormatter";
|
import { DateTimeFormatter, DateTimeFormatterFunction } from "../../utils/DateFormatter";
|
||||||
@@ -30,7 +30,8 @@ import "./jobs-detail-header.styles.scss";
|
|||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
jobRO: selectJobReadOnly,
|
jobRO: selectJobReadOnly,
|
||||||
bodyshop: selectBodyshop
|
bodyshop: selectBodyshop,
|
||||||
|
partsManagementOnly: selectPartsManagementOnly
|
||||||
});
|
});
|
||||||
|
|
||||||
const mapDispatchToProps = (dispatch) => ({
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
@@ -51,19 +52,20 @@ const mapDispatchToProps = (dispatch) => ({
|
|||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
|
||||||
const colSpan = {
|
export function JobsDetailHeader({ job, bodyshop, disabled, insertAuditTrail, partsManagementOnly }) {
|
||||||
xs: { span: 24 },
|
|
||||||
sm: { span: 24 },
|
|
||||||
md: { span: 12 },
|
|
||||||
lg: { span: 6 },
|
|
||||||
xl: { span: 6 }
|
|
||||||
};
|
|
||||||
|
|
||||||
export function JobsDetailHeader({ job, bodyshop, disabled, insertAuditTrail }) {
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { notification } = useNotification();
|
const { notification } = useNotification();
|
||||||
const [notesClamped, setNotesClamped] = useState(true);
|
const [notesClamped, setNotesClamped] = useState(true);
|
||||||
const [updateJob] = useMutation(UPDATE_JOB);
|
const [updateJob] = useMutation(UPDATE_JOB);
|
||||||
|
|
||||||
|
const colSpan = {
|
||||||
|
xs: { span: 24 },
|
||||||
|
sm: { span: 24 },
|
||||||
|
md: { span: partsManagementOnly ? 8 : 12 },
|
||||||
|
lg: { span: partsManagementOnly ? 8 : 6 },
|
||||||
|
xl: { span: partsManagementOnly ? 8 : 6 }
|
||||||
|
};
|
||||||
|
|
||||||
const vehicleTitle =
|
const vehicleTitle =
|
||||||
`${job.v_model_yr || ""} ${job.v_color || ""} ${job.v_make_desc || ""} ${job.v_model_desc || ""}`.trim();
|
`${job.v_model_yr || ""} ${job.v_color || ""} ${job.v_make_desc || ""} ${job.v_model_desc || ""}`.trim();
|
||||||
const bodyHrs = job.joblines.filter((j) => j.mod_lbr_ty !== "LAR").reduce((acc, val) => acc + val.mod_lb_hrs, 0);
|
const bodyHrs = job.joblines.filter((j) => j.mod_lbr_ty !== "LAR").reduce((acc, val) => acc + val.mod_lb_hrs, 0);
|
||||||
@@ -143,81 +145,86 @@ export function JobsDetailHeader({ job, bodyshop, disabled, insertAuditTrail })
|
|||||||
<span style={{ margin: "0rem .5rem" }}>/</span>
|
<span style={{ margin: "0rem .5rem" }}>/</span>
|
||||||
<CurrencyFormatter>{job.owner_owing}</CurrencyFormatter>
|
<CurrencyFormatter>{job.owner_owing}</CurrencyFormatter>
|
||||||
</DataLabel>
|
</DataLabel>
|
||||||
<DataLabel label={t("jobs.fields.alt_transport")}>
|
|
||||||
{job.alt_transport}
|
{!partsManagementOnly && (
|
||||||
<JobAltTransportChange job={job} />
|
<>
|
||||||
</DataLabel>
|
<DataLabel label={t("jobs.fields.alt_transport")}>
|
||||||
{job?.cccontracts?.length > 0 && (
|
{job.alt_transport}
|
||||||
<DataLabel label={t("jobs.labels.contracts")}>
|
<JobAltTransportChange job={job} />
|
||||||
{job.cccontracts.map((c, index) => (
|
</DataLabel>
|
||||||
<Space key={c.id} wrap>
|
{job?.cccontracts?.length > 0 && (
|
||||||
<Link to={`/manage/courtesycars/contracts/${c.id}`}>
|
<DataLabel label={t("jobs.labels.contracts")}>
|
||||||
{`${c.agreementnumber} - ${c.courtesycar.fleetnumber} ${c.courtesycar.year} ${c.courtesycar.make} ${c.courtesycar.model}`}
|
{job.cccontracts.map((c, index) => (
|
||||||
{index !== job.cccontracts.length - 1 ? "," : null}
|
<Space key={c.id} wrap>
|
||||||
</Link>
|
<Link to={`/manage/courtesycars/contracts/${c.id}`}>
|
||||||
|
{`${c.agreementnumber} - ${c.courtesycar.fleetnumber} ${c.courtesycar.year} ${c.courtesycar.make} ${c.courtesycar.model}`}
|
||||||
|
{index !== job.cccontracts.length - 1 ? "," : null}
|
||||||
|
</Link>
|
||||||
|
</Space>
|
||||||
|
))}
|
||||||
|
</DataLabel>
|
||||||
|
)}
|
||||||
|
<DataLabel label={t("jobs.fields.production_vars.note")}>
|
||||||
|
<ProductionListColumnProductionNote record={job} />
|
||||||
|
</DataLabel>
|
||||||
|
<DataLabel label={t("jobs.fields.estimate_sent_approval")}>
|
||||||
|
<Space>
|
||||||
|
<Checkbox
|
||||||
|
checked={!!job.estimate_sent_approval}
|
||||||
|
onChange={(e) => handleCheckboxChange("estimate_sent_approval", e.target.checked)}
|
||||||
|
disabled={disabled}
|
||||||
|
>
|
||||||
|
{job.estimate_sent_approval && (
|
||||||
|
<span style={{ color: "#888" }}>
|
||||||
|
<DateTimeFormatter>{job.estimate_sent_approval}</DateTimeFormatter>
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</Checkbox>
|
||||||
</Space>
|
</Space>
|
||||||
))}
|
</DataLabel>
|
||||||
</DataLabel>
|
<DataLabel label={t("jobs.fields.estimate_approved")}>
|
||||||
|
<Space>
|
||||||
|
<Checkbox
|
||||||
|
checked={!!job.estimate_approved}
|
||||||
|
onChange={(e) => handleCheckboxChange("estimate_approved", e.target.checked)}
|
||||||
|
disabled={disabled}
|
||||||
|
>
|
||||||
|
{job.estimate_approved && (
|
||||||
|
<span style={{ color: "#888" }}>
|
||||||
|
<DateTimeFormatter>{job.estimate_approved}</DateTimeFormatter>
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</Checkbox>
|
||||||
|
</Space>
|
||||||
|
</DataLabel>
|
||||||
|
<Space wrap>
|
||||||
|
{job.special_coverage_policy && (
|
||||||
|
<Tag color="tomato">
|
||||||
|
<Space>
|
||||||
|
<WarningFilled />
|
||||||
|
<span>{t("jobs.labels.specialcoveragepolicy")}</span>
|
||||||
|
</Space>
|
||||||
|
</Tag>
|
||||||
|
)}
|
||||||
|
{job.ca_gst_registrant && (
|
||||||
|
<Tag color="geekblue">
|
||||||
|
<Space>
|
||||||
|
<WarningFilled />
|
||||||
|
<span>{t("jobs.fields.ca_gst_registrant")}</span>
|
||||||
|
</Space>
|
||||||
|
</Tag>
|
||||||
|
)}
|
||||||
|
{job.hit_and_run && (
|
||||||
|
<Tag color="green">
|
||||||
|
<Space>
|
||||||
|
<WarningFilled />
|
||||||
|
<span>{t("jobs.fields.hit_and_run")}</span>
|
||||||
|
</Space>
|
||||||
|
</Tag>
|
||||||
|
)}
|
||||||
|
</Space>
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
<DataLabel label={t("jobs.fields.production_vars.note")}>
|
|
||||||
<ProductionListColumnProductionNote record={job} />
|
|
||||||
</DataLabel>
|
|
||||||
<DataLabel label={t("jobs.fields.estimate_sent_approval")}>
|
|
||||||
<Space>
|
|
||||||
<Checkbox
|
|
||||||
checked={!!job.estimate_sent_approval}
|
|
||||||
onChange={(e) => handleCheckboxChange("estimate_sent_approval", e.target.checked)}
|
|
||||||
disabled={disabled}
|
|
||||||
>
|
|
||||||
{job.estimate_sent_approval && (
|
|
||||||
<span style={{ color: "#888" }}>
|
|
||||||
<DateTimeFormatter>{job.estimate_sent_approval}</DateTimeFormatter>
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
</Checkbox>
|
|
||||||
</Space>
|
|
||||||
</DataLabel>
|
|
||||||
<DataLabel label={t("jobs.fields.estimate_approved")}>
|
|
||||||
<Space>
|
|
||||||
<Checkbox
|
|
||||||
checked={!!job.estimate_approved}
|
|
||||||
onChange={(e) => handleCheckboxChange("estimate_approved", e.target.checked)}
|
|
||||||
disabled={disabled}
|
|
||||||
>
|
|
||||||
{job.estimate_approved && (
|
|
||||||
<span style={{ color: "#888" }}>
|
|
||||||
<DateTimeFormatter>{job.estimate_approved}</DateTimeFormatter>
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
</Checkbox>
|
|
||||||
</Space>
|
|
||||||
</DataLabel>
|
|
||||||
<Space wrap>
|
|
||||||
{job.special_coverage_policy && (
|
|
||||||
<Tag color="tomato">
|
|
||||||
<Space>
|
|
||||||
<WarningFilled />
|
|
||||||
<span>{t("jobs.labels.specialcoveragepolicy")}</span>
|
|
||||||
</Space>
|
|
||||||
</Tag>
|
|
||||||
)}
|
|
||||||
{job.ca_gst_registrant && (
|
|
||||||
<Tag color="geekblue">
|
|
||||||
<Space>
|
|
||||||
<WarningFilled />
|
|
||||||
<span>{t("jobs.fields.ca_gst_registrant")}</span>
|
|
||||||
</Space>
|
|
||||||
</Tag>
|
|
||||||
)}
|
|
||||||
{job.hit_and_run && (
|
|
||||||
<Tag color="green">
|
|
||||||
<Space>
|
|
||||||
<WarningFilled />
|
|
||||||
<span>{t("jobs.fields.hit_and_run")}</span>
|
|
||||||
</Space>
|
|
||||||
</Tag>
|
|
||||||
)}
|
|
||||||
</Space>
|
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
</Col>
|
</Col>
|
||||||
@@ -334,17 +341,19 @@ export function JobsDetailHeader({ job, bodyshop, disabled, insertAuditTrail })
|
|||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
</Col>
|
</Col>
|
||||||
<Col {...colSpan}>
|
{!partsManagementOnly && (
|
||||||
<Card style={{ height: "100%" }} title={t("jobs.labels.employeeassignments")}>
|
<Col {...colSpan}>
|
||||||
<div>
|
<Card style={{ height: "100%" }} title={t("jobs.labels.employeeassignments")}>
|
||||||
<JobEmployeeAssignments job={job} />
|
<div>
|
||||||
<Divider style={{ margin: ".5rem" }} />
|
<JobEmployeeAssignments job={job} />
|
||||||
<DataLabel label={t("jobs.labels.labor_hrs")}>
|
<Divider style={{ margin: ".5rem" }} />
|
||||||
{bodyHrs.toFixed(1)} / {refinishHrs.toFixed(1)} / {(bodyHrs + refinishHrs).toFixed(1)}
|
<DataLabel label={t("jobs.labels.labor_hrs")}>
|
||||||
</DataLabel>
|
{bodyHrs.toFixed(1)} / {refinishHrs.toFixed(1)} / {(bodyHrs + refinishHrs).toFixed(1)}
|
||||||
</div>
|
</DataLabel>
|
||||||
</Card>
|
</div>
|
||||||
</Col>
|
</Card>
|
||||||
|
</Col>
|
||||||
|
)}
|
||||||
</Row>
|
</Row>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,27 +8,27 @@ import PartsDispatchTable from "../parts-dispatch-table/parts-dispatch-table.com
|
|||||||
import PartsOrderListTableComponent from "../parts-order-list-table/parts-order-list-table.component";
|
import PartsOrderListTableComponent from "../parts-order-list-table/parts-order-list-table.component";
|
||||||
import PartsOrderModal from "../parts-order-modal/parts-order-modal.container";
|
import PartsOrderModal from "../parts-order-modal/parts-order-modal.container";
|
||||||
|
|
||||||
export default function JobsDetailPliComponent({
|
import { connect } from "react-redux";
|
||||||
|
import { createStructuredSelector } from "reselect";
|
||||||
|
import { selectPartsManagementOnly } from "../../redux/user/user.selectors";
|
||||||
|
const mapStateToProps = createStructuredSelector({
|
||||||
|
//currentUser: selectCurrentUser
|
||||||
|
partsManagementOnly: selectPartsManagementOnly
|
||||||
|
});
|
||||||
|
const mapDispatchToProps = (dispatch) => ({});
|
||||||
|
export default connect(mapStateToProps, mapDispatchToProps)(JobsDetailPliComponent);
|
||||||
|
|
||||||
|
export function JobsDetailPliComponent({
|
||||||
job,
|
job,
|
||||||
billsQuery,
|
billsQuery,
|
||||||
handleBillOnRowClick,
|
handleBillOnRowClick,
|
||||||
handlePartsOrderOnRowClick,
|
handlePartsOrderOnRowClick,
|
||||||
handlePartsDispatchOnRowClick
|
handlePartsDispatchOnRowClick,
|
||||||
|
partsManagementOnly
|
||||||
}) {
|
}) {
|
||||||
return (
|
if (partsManagementOnly) {
|
||||||
<div>
|
return (
|
||||||
<PartsOrderModal />
|
|
||||||
{billsQuery.error ? <AlertComponent message={billsQuery.error.message} type="error" /> : null}
|
|
||||||
<BillDetailEditcontainer />
|
|
||||||
<Row gutter={[16, 16]}>
|
<Row gutter={[16, 16]}>
|
||||||
<Col span={24}>
|
|
||||||
<JobBillsTotal
|
|
||||||
bills={billsQuery.data ? billsQuery.data.bills : []}
|
|
||||||
partsOrders={billsQuery.data ? billsQuery.data.parts_orders : []}
|
|
||||||
loading={billsQuery.loading}
|
|
||||||
jobTotals={job.job_totals}
|
|
||||||
/>
|
|
||||||
</Col>
|
|
||||||
<Col span={24}>
|
<Col span={24}>
|
||||||
<PartsOrderListTableComponent
|
<PartsOrderListTableComponent
|
||||||
job={job}
|
job={job}
|
||||||
@@ -36,13 +36,38 @@ export default function JobsDetailPliComponent({
|
|||||||
billsQuery={billsQuery}
|
billsQuery={billsQuery}
|
||||||
/>
|
/>
|
||||||
</Col>
|
</Col>
|
||||||
<Col span={24}>
|
|
||||||
<BillsListTable job={job} handleOnRowClick={handleBillOnRowClick} billsQuery={billsQuery} />
|
|
||||||
</Col>
|
|
||||||
<Col span={24}>
|
|
||||||
<PartsDispatchTable job={job} handleOnRowClick={handlePartsDispatchOnRowClick} billsQuery={billsQuery} />
|
|
||||||
</Col>
|
|
||||||
</Row>
|
</Row>
|
||||||
</div>
|
);
|
||||||
);
|
} else {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<PartsOrderModal />
|
||||||
|
{billsQuery.error ? <AlertComponent message={billsQuery.error.message} type="error" /> : null}
|
||||||
|
<BillDetailEditcontainer />
|
||||||
|
<Row gutter={[16, 16]}>
|
||||||
|
<Col span={24}>
|
||||||
|
<JobBillsTotal
|
||||||
|
bills={billsQuery.data ? billsQuery.data.bills : []}
|
||||||
|
partsOrders={billsQuery.data ? billsQuery.data.parts_orders : []}
|
||||||
|
loading={billsQuery.loading}
|
||||||
|
jobTotals={job.job_totals}
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
|
<Col span={24}>
|
||||||
|
<PartsOrderListTableComponent
|
||||||
|
job={job}
|
||||||
|
handleOnRowClick={handlePartsOrderOnRowClick}
|
||||||
|
billsQuery={billsQuery}
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
|
<Col span={24}>
|
||||||
|
<BillsListTable job={job} handleOnRowClick={handleBillOnRowClick} billsQuery={billsQuery} />
|
||||||
|
</Col>
|
||||||
|
<Col span={24}>
|
||||||
|
<PartsDispatchTable job={job} handleOnRowClick={handlePartsDispatchOnRowClick} billsQuery={billsQuery} />
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import { QUERY_BILL_BY_PK } from "../../graphql/bills.queries";
|
|||||||
import { DELETE_PARTS_ORDER } from "../../graphql/parts-orders.queries";
|
import { DELETE_PARTS_ORDER } from "../../graphql/parts-orders.queries";
|
||||||
import { selectJobReadOnly } from "../../redux/application/application.selectors";
|
import { selectJobReadOnly } from "../../redux/application/application.selectors";
|
||||||
import { setModalContext } from "../../redux/modals/modals.actions";
|
import { setModalContext } from "../../redux/modals/modals.actions";
|
||||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
import { selectBodyshop, selectPartsManagementOnly } from "../../redux/user/user.selectors";
|
||||||
import CurrencyFormatter from "../../utils/CurrencyFormatter";
|
import CurrencyFormatter from "../../utils/CurrencyFormatter";
|
||||||
import { DateFormatter } from "../../utils/DateFormatter";
|
import { DateFormatter } from "../../utils/DateFormatter";
|
||||||
import { TemplateList } from "../../utils/TemplateConstants";
|
import { TemplateList } from "../../utils/TemplateConstants";
|
||||||
@@ -31,7 +31,8 @@ import PrintWrapper from "../print-wrapper/print-wrapper.component";
|
|||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
jobRO: selectJobReadOnly,
|
jobRO: selectJobReadOnly,
|
||||||
bodyshop: selectBodyshop
|
bodyshop: selectBodyshop,
|
||||||
|
partsManagementOnly: selectPartsManagementOnly
|
||||||
});
|
});
|
||||||
|
|
||||||
const mapDispatchToProps = (dispatch) => ({
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
@@ -60,7 +61,8 @@ export function PartsOrderListTableDrawerComponent({
|
|||||||
billsQuery,
|
billsQuery,
|
||||||
handleOnRowClick,
|
handleOnRowClick,
|
||||||
setPartsReceiveContext,
|
setPartsReceiveContext,
|
||||||
setTaskUpsertContext
|
setTaskUpsertContext,
|
||||||
|
partsManagementOnly
|
||||||
}) {
|
}) {
|
||||||
const selectedBreakpoint = Object.entries(Grid.useBreakpoint())
|
const selectedBreakpoint = Object.entries(Grid.useBreakpoint())
|
||||||
.filter((screen) => !!screen[1])
|
.filter((screen) => !!screen[1])
|
||||||
@@ -134,19 +136,21 @@ export function PartsOrderListTableDrawerComponent({
|
|||||||
>
|
>
|
||||||
{t("parts_orders.actions.receive")}
|
{t("parts_orders.actions.receive")}
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
{!partsManagementOnly && (
|
||||||
title={t("tasks.buttons.create")}
|
<Button
|
||||||
onClick={() => {
|
title={t("tasks.buttons.create")}
|
||||||
setTaskUpsertContext({
|
onClick={() => {
|
||||||
context: {
|
setTaskUpsertContext({
|
||||||
jobid: job.id,
|
context: {
|
||||||
partsorderid: record.id
|
jobid: job.id,
|
||||||
}
|
partsorderid: record.id
|
||||||
});
|
}
|
||||||
}}
|
});
|
||||||
>
|
}}
|
||||||
<FaTasks />
|
>
|
||||||
</Button>
|
<FaTasks />
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
<Popconfirm
|
<Popconfirm
|
||||||
title={t("parts_orders.labels.confirmdelete")}
|
title={t("parts_orders.labels.confirmdelete")}
|
||||||
disabled={jobRO}
|
disabled={jobRO}
|
||||||
@@ -170,43 +174,44 @@ export function PartsOrderListTableDrawerComponent({
|
|||||||
<DeleteFilled />
|
<DeleteFilled />
|
||||||
</Button>
|
</Button>
|
||||||
</Popconfirm>
|
</Popconfirm>
|
||||||
|
{!partsManagementOnly && (
|
||||||
<Button
|
<Button
|
||||||
disabled={
|
disabled={
|
||||||
(jobRO ? !record.return : jobRO) ||
|
(jobRO ? !record.return : jobRO) ||
|
||||||
record.vendor.id === bodyshop.inhousevendorid ||
|
record.vendor.id === bodyshop.inhousevendorid ||
|
||||||
!HasFeatureAccess({ bodyshop, featureName: "bills" })
|
!HasFeatureAccess({ bodyshop, featureName: "bills" })
|
||||||
}
|
}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
logImEXEvent("parts_order_receive_bill");
|
logImEXEvent("parts_order_receive_bill");
|
||||||
setBillEnterContext({
|
setBillEnterContext({
|
||||||
actions: { refetch: refetch },
|
actions: { refetch: refetch },
|
||||||
context: {
|
context: {
|
||||||
job: job,
|
job: job,
|
||||||
bill: {
|
bill: {
|
||||||
vendorid: record.vendor.id,
|
vendorid: record.vendor.id,
|
||||||
is_credit_memo: record.return,
|
is_credit_memo: record.return,
|
||||||
billlines: record.parts_order_lines.map((pol) => ({
|
billlines: record.parts_order_lines.map((pol) => ({
|
||||||
joblineid: pol.job_line_id || "noline",
|
joblineid: pol.job_line_id || "noline",
|
||||||
line_desc: pol.line_desc,
|
line_desc: pol.line_desc,
|
||||||
quantity: pol.quantity,
|
quantity: pol.quantity,
|
||||||
actual_price: pol.act_price,
|
actual_price: pol.act_price,
|
||||||
cost_center: pol.jobline?.part_type
|
cost_center: pol.jobline?.part_type
|
||||||
? bodyshop.pbs_serialnumber || bodyshop.cdk_dealerid
|
? bodyshop.pbs_serialnumber || bodyshop.cdk_dealerid
|
||||||
? pol.jobline.part_type !== "PAE"
|
? pol.jobline.part_type !== "PAE"
|
||||||
? pol.jobline.part_type
|
? pol.jobline.part_type
|
||||||
: null
|
: null
|
||||||
: responsibilityCenters.defaults &&
|
: responsibilityCenters.defaults &&
|
||||||
(responsibilityCenters.defaults.costs[pol.jobline.part_type] || null)
|
(responsibilityCenters.defaults.costs[pol.jobline.part_type] || null)
|
||||||
: null
|
: null
|
||||||
}))
|
}))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
});
|
}}
|
||||||
}}
|
>
|
||||||
>
|
<LockWrapperComponent featureName="bills">{t("parts_orders.actions.receivebill")}</LockWrapperComponent>
|
||||||
<LockWrapperComponent featureName="bills">{t("parts_orders.actions.receivebill")}</LockWrapperComponent>
|
</Button>
|
||||||
</Button>
|
)}
|
||||||
|
|
||||||
<PrintWrapper
|
<PrintWrapper
|
||||||
templateObject={{
|
templateObject={{
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import { logImEXEvent } from "../../firebase/firebase.utils";
|
|||||||
import { DELETE_PARTS_ORDER } from "../../graphql/parts-orders.queries";
|
import { DELETE_PARTS_ORDER } from "../../graphql/parts-orders.queries";
|
||||||
import { selectJobReadOnly } from "../../redux/application/application.selectors";
|
import { selectJobReadOnly } from "../../redux/application/application.selectors";
|
||||||
import { setModalContext } from "../../redux/modals/modals.actions";
|
import { setModalContext } from "../../redux/modals/modals.actions";
|
||||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
import { selectBodyshop, selectPartsManagementOnly } from "../../redux/user/user.selectors";
|
||||||
import { DateFormatter } from "../../utils/DateFormatter";
|
import { DateFormatter } from "../../utils/DateFormatter";
|
||||||
import { TemplateList } from "../../utils/TemplateConstants";
|
import { TemplateList } from "../../utils/TemplateConstants";
|
||||||
import { alphaSort } from "../../utils/sorters";
|
import { alphaSort } from "../../utils/sorters";
|
||||||
@@ -23,7 +23,8 @@ import ShareToTeamsButton from "../share-to-teams/share-to-teams.component.jsx";
|
|||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
jobRO: selectJobReadOnly,
|
jobRO: selectJobReadOnly,
|
||||||
bodyshop: selectBodyshop
|
bodyshop: selectBodyshop,
|
||||||
|
partsManagementOnly: selectPartsManagementOnly
|
||||||
});
|
});
|
||||||
|
|
||||||
const mapDispatchToProps = (dispatch) => ({
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
@@ -52,7 +53,8 @@ export function PartsOrderListTableComponent({
|
|||||||
billsQuery,
|
billsQuery,
|
||||||
handleOnRowClick,
|
handleOnRowClick,
|
||||||
setPartsReceiveContext,
|
setPartsReceiveContext,
|
||||||
setTaskUpsertContext
|
setTaskUpsertContext,
|
||||||
|
partsManagementOnly
|
||||||
}) {
|
}) {
|
||||||
const responsibilityCenters = bodyshop.md_responsibility_centers;
|
const responsibilityCenters = bodyshop.md_responsibility_centers;
|
||||||
const Templates = TemplateList("partsorder", { job });
|
const Templates = TemplateList("partsorder", { job });
|
||||||
@@ -106,18 +108,23 @@ export function PartsOrderListTableComponent({
|
|||||||
>
|
>
|
||||||
{t("parts_orders.actions.receive")}
|
{t("parts_orders.actions.receive")}
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
{!partsManagementOnly && (
|
||||||
title={t("tasks.buttons.create")}
|
<>
|
||||||
icon={<FaTasks />}
|
<Button
|
||||||
onClick={() => {
|
title={t("tasks.buttons.create")}
|
||||||
setTaskUpsertContext({
|
icon={<FaTasks />}
|
||||||
context: {
|
onClick={() => {
|
||||||
jobid: job.id,
|
setTaskUpsertContext({
|
||||||
partsorderid: record.id
|
context: {
|
||||||
}
|
jobid: job.id,
|
||||||
});
|
partsorderid: record.id
|
||||||
}}
|
}
|
||||||
/>
|
});
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
<Popconfirm
|
<Popconfirm
|
||||||
title={t("parts_orders.labels.confirmdelete")}
|
title={t("parts_orders.labels.confirmdelete")}
|
||||||
disabled={jobRO}
|
disabled={jobRO}
|
||||||
@@ -141,47 +148,49 @@ export function PartsOrderListTableComponent({
|
|||||||
<Button disabled={jobRO} icon={<DeleteFilled />} />
|
<Button disabled={jobRO} icon={<DeleteFilled />} />
|
||||||
</Popconfirm>
|
</Popconfirm>
|
||||||
|
|
||||||
<Button
|
{!partsManagementOnly && (
|
||||||
disabled={
|
<Button
|
||||||
(jobRO ? !record.return : jobRO) ||
|
disabled={
|
||||||
record.vendor.id === bodyshop.inhousevendorid ||
|
(jobRO ? !record.return : jobRO) ||
|
||||||
!HasFeatureAccess({ bodyshop, featureName: "bills" })
|
record.vendor.id === bodyshop.inhousevendorid ||
|
||||||
}
|
!HasFeatureAccess({ bodyshop, featureName: "bills" })
|
||||||
onClick={() => {
|
}
|
||||||
logImEXEvent("parts_order_receive_bill");
|
onClick={() => {
|
||||||
|
logImEXEvent("parts_order_receive_bill");
|
||||||
|
|
||||||
setBillEnterContext({
|
setBillEnterContext({
|
||||||
actions: { refetch: refetch },
|
actions: { refetch: refetch },
|
||||||
context: {
|
context: {
|
||||||
job: job,
|
job: job,
|
||||||
bill: {
|
bill: {
|
||||||
vendorid: record.vendor.id,
|
vendorid: record.vendor.id,
|
||||||
is_credit_memo: record.return,
|
is_credit_memo: record.return,
|
||||||
billlines: record.parts_order_lines.map((pol) => {
|
billlines: record.parts_order_lines.map((pol) => {
|
||||||
return {
|
return {
|
||||||
joblineid: pol.job_line_id || "noline",
|
joblineid: pol.job_line_id || "noline",
|
||||||
line_desc: pol.line_desc,
|
line_desc: pol.line_desc,
|
||||||
quantity: pol.quantity,
|
quantity: pol.quantity,
|
||||||
|
|
||||||
actual_price: pol.act_price,
|
actual_price: pol.act_price,
|
||||||
|
|
||||||
cost_center: pol.jobline?.part_type
|
cost_center: pol.jobline?.part_type
|
||||||
? bodyshop.pbs_serialnumber || bodyshop.cdk_dealerid
|
? bodyshop.pbs_serialnumber || bodyshop.cdk_dealerid
|
||||||
? pol.jobline.part_type !== "PAE"
|
? pol.jobline.part_type !== "PAE"
|
||||||
? pol.jobline.part_type
|
? pol.jobline.part_type
|
||||||
: null
|
: null
|
||||||
: responsibilityCenters.defaults &&
|
: responsibilityCenters.defaults &&
|
||||||
(responsibilityCenters.defaults.costs[pol.jobline.part_type] || null)
|
(responsibilityCenters.defaults.costs[pol.jobline.part_type] || null)
|
||||||
: null
|
: null
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
});
|
}}
|
||||||
}}
|
>
|
||||||
>
|
<LockWrapperComponent featureName="bills">{t("parts_orders.actions.receivebill")}</LockWrapperComponent>
|
||||||
<LockWrapperComponent featureName="bills">{t("parts_orders.actions.receivebill")}</LockWrapperComponent>
|
</Button>
|
||||||
</Button>
|
)}
|
||||||
|
|
||||||
<PrintWrapper
|
<PrintWrapper
|
||||||
templateObject={{
|
templateObject={{
|
||||||
|
|||||||
@@ -1865,6 +1865,7 @@ export const QUERY_SIMPLIFIED_PARTS_PAGINATED_STATUS_FILTERED = gql`
|
|||||||
owner_owing
|
owner_owing
|
||||||
ro_number
|
ro_number
|
||||||
po_number
|
po_number
|
||||||
|
converted
|
||||||
status
|
status
|
||||||
updated_at
|
updated_at
|
||||||
ded_amt
|
ded_amt
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
import { Button, FloatButton, Layout, Space, Spin } from "antd";
|
import { FloatButton, Layout, Spin } from "antd";
|
||||||
import { AlertOutlined, BulbOutlined } from "@ant-design/icons";
|
|
||||||
|
|
||||||
// import preval from "preval.macro";
|
// import preval from "preval.macro";
|
||||||
import React, { lazy, Suspense, useEffect, useState } from "react";
|
import { lazy, Suspense, useEffect, useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import { Link, Route, Routes } from "react-router-dom";
|
import { Route, Routes } from "react-router-dom";
|
||||||
import { createStructuredSelector } from "reselect";
|
import { createStructuredSelector } from "reselect";
|
||||||
import BreadCrumbs from "../../components/breadcrumbs/breadcrumbs.component";
|
import BreadCrumbs from "../../components/breadcrumbs/breadcrumbs.component";
|
||||||
import ChatAffixContainer from "../../components/chat-affix/chat-affix.container";
|
import ChatAffixContainer from "../../components/chat-affix/chat-affix.container";
|
||||||
@@ -15,19 +14,19 @@ import ErrorBoundary from "../../components/error-boundary/error-boundary.compon
|
|||||||
//Component Imports
|
//Component Imports
|
||||||
import * as Sentry from "@sentry/react";
|
import * as Sentry from "@sentry/react";
|
||||||
import TestComponent from "../../components/_test/test.page";
|
import TestComponent from "../../components/_test/test.page";
|
||||||
|
import GlobalFooter from "../../components/global-footer/global-footer.component.jsx";
|
||||||
import HeaderContainer from "../../components/header/header.container";
|
import HeaderContainer from "../../components/header/header.container";
|
||||||
import LoadingSpinner from "../../components/loading-spinner/loading-spinner.component";
|
import LoadingSpinner from "../../components/loading-spinner/loading-spinner.component";
|
||||||
import PartnerPingComponent from "../../components/partner-ping/partner-ping.component";
|
import PartnerPingComponent from "../../components/partner-ping/partner-ping.component";
|
||||||
import PrintCenterModalContainer from "../../components/print-center-modal/print-center-modal.container";
|
import PrintCenterModalContainer from "../../components/print-center-modal/print-center-modal.container";
|
||||||
import ShopSubStatusComponent from "../../components/shop-sub-status/shop-sub-status.component";
|
import ShopSubStatusComponent from "../../components/shop-sub-status/shop-sub-status.component";
|
||||||
import { selectBodyshop, selectInstanceConflict } from "../../redux/user/user.selectors";
|
|
||||||
import UpdateAlert from "../../components/update-alert/update-alert.component";
|
import UpdateAlert from "../../components/update-alert/update-alert.component";
|
||||||
import InstanceRenderManager from "../../utils/instanceRenderMgr.js";
|
|
||||||
import WssStatusDisplayComponent from "../../components/wss-status-display/wss-status-display.component.jsx";
|
|
||||||
import { selectAlerts } from "../../redux/application/application.selectors.js";
|
|
||||||
import { addAlerts } from "../../redux/application/application.actions.js";
|
|
||||||
import { useNotification } from "../../contexts/Notifications/notificationContext.jsx";
|
import { useNotification } from "../../contexts/Notifications/notificationContext.jsx";
|
||||||
import { useSocket } from "../../contexts/SocketIO/useSocket.js";
|
import { useSocket } from "../../contexts/SocketIO/useSocket.js";
|
||||||
|
import { addAlerts } from "../../redux/application/application.actions.js";
|
||||||
|
import { selectAlerts } from "../../redux/application/application.selectors.js";
|
||||||
|
import { selectBodyshop, selectInstanceConflict } from "../../redux/user/user.selectors";
|
||||||
|
import InstanceRenderManager from "../../utils/instanceRenderMgr.js";
|
||||||
|
|
||||||
const JobsPage = lazy(() => import("../jobs/jobs.page"));
|
const JobsPage = lazy(() => import("../jobs/jobs.page"));
|
||||||
|
|
||||||
@@ -658,38 +657,7 @@ export function Manage({ conflict, bodyshop, alerts, setAlerts }) {
|
|||||||
|
|
||||||
<FloatButton.BackTop style={{ right: 100, bottom: 25 }} />
|
<FloatButton.BackTop style={{ right: 100, bottom: 25 }} />
|
||||||
</Content>
|
</Content>
|
||||||
<Footer>
|
<GlobalFooter />
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
display: "flex",
|
|
||||||
flexDirection: "column",
|
|
||||||
justifyContent: "center",
|
|
||||||
alignItems: "center",
|
|
||||||
margin: "1rem 0rem"
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Link to="/manage/feature-request">
|
|
||||||
<Button icon={<BulbOutlined />} type="text">
|
|
||||||
{t("general.labels.feature-request")}
|
|
||||||
</Button>
|
|
||||||
</Link>
|
|
||||||
<Space>
|
|
||||||
<WssStatusDisplayComponent />
|
|
||||||
<div onClick={broadcastMessage}>
|
|
||||||
{`${InstanceRenderManager({
|
|
||||||
imex: t("titles.imexonline"),
|
|
||||||
rome: t("titles.romeonline")
|
|
||||||
})} - ${import.meta.env.VITE_APP_GIT_SHA_DATE}`}
|
|
||||||
</div>
|
|
||||||
<Button icon={<AlertOutlined />} data-canny-changelog type="text">
|
|
||||||
{t("general.labels.changelog")}
|
|
||||||
</Button>
|
|
||||||
</Space>
|
|
||||||
<Link to="/disclaimer" target="_blank" style={{ color: "#ccc" }}>
|
|
||||||
Disclaimer & Notices
|
|
||||||
</Link>
|
|
||||||
</div>
|
|
||||||
</Footer>
|
|
||||||
</Layout>
|
</Layout>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -22,11 +22,12 @@ import { QUERY_PARTS_BILLS_BY_JOBID } from "../../graphql/bills.queries.js";
|
|||||||
import { insertAuditTrail } from "../../redux/application/application.actions.js";
|
import { insertAuditTrail } from "../../redux/application/application.actions.js";
|
||||||
import { selectJobReadOnly } from "../../redux/application/application.selectors.js";
|
import { selectJobReadOnly } from "../../redux/application/application.selectors.js";
|
||||||
import { setModalContext } from "../../redux/modals/modals.actions.js";
|
import { setModalContext } from "../../redux/modals/modals.actions.js";
|
||||||
import { selectBodyshop } from "../../redux/user/user.selectors.js";
|
import { selectBodyshop, selectPartsManagementOnly } from "../../redux/user/user.selectors.js";
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
bodyshop: selectBodyshop,
|
bodyshop: selectBodyshop,
|
||||||
jobRO: selectJobReadOnly
|
jobRO: selectJobReadOnly,
|
||||||
|
partsManagementOnly: selectPartsManagementOnly
|
||||||
});
|
});
|
||||||
const mapDispatchToProps = (dispatch) => ({
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
setPrintCenterContext: (context) =>
|
setPrintCenterContext: (context) =>
|
||||||
@@ -53,7 +54,8 @@ export function SimplifiedPartsJobDetailComponent({
|
|||||||
job,
|
job,
|
||||||
mutationUpdateJob,
|
mutationUpdateJob,
|
||||||
insertAuditTrail,
|
insertAuditTrail,
|
||||||
refetch
|
refetch,
|
||||||
|
partsManagementOnly
|
||||||
}) {
|
}) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [form] = Form.useForm();
|
const [form] = Form.useForm();
|
||||||
@@ -141,10 +143,14 @@ export function SimplifiedPartsJobDetailComponent({
|
|||||||
{t("jobs.actions.printCenter")}
|
{t("jobs.actions.printCenter")}
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<JobsDetailHeaderActions key="actions" job={job} refetch={refetch} />
|
{!partsManagementOnly && (
|
||||||
<Button type="primary" loading={loading} disabled={jobRO} onClick={() => form.submit()}>
|
<>
|
||||||
{t("general.actions.save")}
|
<JobsDetailHeaderActions key="actions" job={job} refetch={refetch} />
|
||||||
</Button>
|
<Button type="primary" loading={loading} disabled={jobRO} onClick={() => form.submit()}>
|
||||||
|
{t("general.actions.save")}
|
||||||
|
</Button>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</Space>
|
</Space>
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -153,7 +159,7 @@ export function SimplifiedPartsJobDetailComponent({
|
|||||||
<JobLineUpsertModalContainer />
|
<JobLineUpsertModalContainer />
|
||||||
|
|
||||||
<PageHeader title={<Space>{job.ro_number || t("general.labels.na")}</Space>} extra={menuExtra} />
|
<PageHeader title={<Space>{job.ro_number || t("general.labels.na")}</Space>} extra={menuExtra} />
|
||||||
<JobsDetailHeader job={job} />
|
<JobsDetailHeader job={job} disabled={true} />
|
||||||
<Divider type="horizontal" />
|
<Divider type="horizontal" />
|
||||||
<JobProfileDataWarning job={job} />
|
<JobProfileDataWarning job={job} />
|
||||||
<FormFieldsChanged form={form} />
|
<FormFieldsChanged form={form} />
|
||||||
@@ -178,7 +184,6 @@ export function SimplifiedPartsJobDetailComponent({
|
|||||||
handlePartsDispatchOnRowClick={handlePartsDispatchOnRowClick}
|
handlePartsDispatchOnRowClick={handlePartsDispatchOnRowClick}
|
||||||
refetch={refetch}
|
refetch={refetch}
|
||||||
form={form}
|
form={form}
|
||||||
simple
|
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ function SimplifiedPartsJobsDetailContainer({ setBreadcrumbs, addRecentItem, set
|
|||||||
ro_number: (data.jobs_by_pk && data.jobs_by_pk.ro_number) || t("general.labels.na")
|
ro_number: (data.jobs_by_pk && data.jobs_by_pk.ro_number) || t("general.labels.na")
|
||||||
});
|
});
|
||||||
setBreadcrumbs([
|
setBreadcrumbs([
|
||||||
{ link: "/parts/jobs", label: t("titles.bc.jobs") },
|
{ link: "/parts/", label: t("titles.bc.jobs") },
|
||||||
{
|
{
|
||||||
link: `/parts/jobs/${jobId}`,
|
link: `/parts/jobs/${jobId}`,
|
||||||
label: t("titles.bc.jobs-detail", {
|
label: t("titles.bc.jobs-detail", {
|
||||||
@@ -81,7 +81,7 @@ function SimplifiedPartsJobsDetailContainer({ setBreadcrumbs, addRecentItem, set
|
|||||||
"job",
|
"job",
|
||||||
|
|
||||||
`${data.jobs_by_pk.ro_number || t("general.labels.na")} | ${OwnerNameDisplayFunction(data.jobs_by_pk)}`,
|
`${data.jobs_by_pk.ro_number || t("general.labels.na")} | ${OwnerNameDisplayFunction(data.jobs_by_pk)}`,
|
||||||
`/manage/jobs/${jobId}`
|
`/parts/jobs/${jobId}`
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,26 +1,24 @@
|
|||||||
import { AlertOutlined, BulbOutlined } from "@ant-design/icons";
|
|
||||||
import * as Sentry from "@sentry/react";
|
import * as Sentry from "@sentry/react";
|
||||||
import { Button, FloatButton, Layout, Space, Spin } from "antd";
|
import { FloatButton, Layout, Spin } from "antd";
|
||||||
import { lazy, Suspense, useEffect, useState } from "react";
|
import { lazy, Suspense, useEffect, useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import { Link, Route, Routes } from "react-router-dom";
|
import { Route, Routes } from "react-router-dom";
|
||||||
import { createStructuredSelector } from "reselect";
|
import { createStructuredSelector } from "reselect";
|
||||||
import BreadCrumbs from "../../components/breadcrumbs/breadcrumbs.component.jsx";
|
import BreadCrumbs from "../../components/breadcrumbs/breadcrumbs.component.jsx";
|
||||||
import ConflictComponent from "../../components/conflict/conflict.component.jsx";
|
import ConflictComponent from "../../components/conflict/conflict.component.jsx";
|
||||||
import ErrorBoundary from "../../components/error-boundary/error-boundary.component.jsx";
|
import ErrorBoundary from "../../components/error-boundary/error-boundary.component.jsx";
|
||||||
|
import GlobalFooter from "../../components/global-footer/global-footer.component.jsx";
|
||||||
import LoadingSpinner from "../../components/loading-spinner/loading-spinner.component.jsx";
|
import LoadingSpinner from "../../components/loading-spinner/loading-spinner.component.jsx";
|
||||||
import PrintCenterModalContainer from "../../components/print-center-modal/print-center-modal.container.jsx";
|
import PrintCenterModalContainer from "../../components/print-center-modal/print-center-modal.container.jsx";
|
||||||
import ShopSubStatusComponent from "../../components/shop-sub-status/shop-sub-status.component.jsx";
|
import ShopSubStatusComponent from "../../components/shop-sub-status/shop-sub-status.component.jsx";
|
||||||
import UpdateAlert from "../../components/update-alert/update-alert.component.jsx";
|
import UpdateAlert from "../../components/update-alert/update-alert.component.jsx";
|
||||||
import WssStatusDisplayComponent from "../../components/wss-status-display/wss-status-display.component.jsx";
|
|
||||||
import { useNotification } from "../../contexts/Notifications/notificationContext.jsx";
|
import { useNotification } from "../../contexts/Notifications/notificationContext.jsx";
|
||||||
import { useSocket } from "../../contexts/SocketIO/useSocket.js";
|
import { useSocket } from "../../contexts/SocketIO/useSocket.js";
|
||||||
import { addAlerts } from "../../redux/application/application.actions.js";
|
import { addAlerts } from "../../redux/application/application.actions.js";
|
||||||
import { selectAlerts } from "../../redux/application/application.selectors.js";
|
import { selectAlerts } from "../../redux/application/application.selectors.js";
|
||||||
import { selectBodyshop, selectInstanceConflict } from "../../redux/user/user.selectors.js";
|
import { selectBodyshop, selectInstanceConflict } from "../../redux/user/user.selectors.js";
|
||||||
import InstanceRenderManager from "../../utils/instanceRenderMgr.js";
|
import InstanceRenderManager from "../../utils/instanceRenderMgr.js";
|
||||||
|
|
||||||
const SimplifiedPartsJobsPage = lazy(() => import("../simplified-parts-jobs/simplified-parts-jobs.page.jsx"));
|
const SimplifiedPartsJobsPage = lazy(() => import("../simplified-parts-jobs/simplified-parts-jobs.page.jsx"));
|
||||||
const SimplifiedPartsJobsDetailPage = lazy(
|
const SimplifiedPartsJobsDetailPage = lazy(
|
||||||
() => import("../simplified-parts-jobs-detail/simplified-parts-jobs-detail.container.jsx")
|
() => import("../simplified-parts-jobs-detail/simplified-parts-jobs-detail.container.jsx")
|
||||||
@@ -29,12 +27,10 @@ const ShopPage = lazy(() => import("../shop/shop.page.component.jsx"));
|
|||||||
const ShopVendorPageContainer = lazy(() => import("../shop-vendor/shop-vendor.page.container.jsx"));
|
const ShopVendorPageContainer = lazy(() => import("../shop-vendor/shop-vendor.page.container.jsx"));
|
||||||
const EmailOverlayContainer = lazy(() => import("../../components/email-overlay/email-overlay.container.jsx"));
|
const EmailOverlayContainer = lazy(() => import("../../components/email-overlay/email-overlay.container.jsx"));
|
||||||
const FeatureRequestPage = lazy(() => import("../feature-request/feature-request.page.jsx"));
|
const FeatureRequestPage = lazy(() => import("../feature-request/feature-request.page.jsx"));
|
||||||
const JobCostingModal = lazy(() => import("../../components/job-costing-modal/job-costing-modal.container.jsx"));
|
|
||||||
const ReportCenterModal = lazy(() => import("../../components/report-center-modal/report-center-modal.container.jsx"));
|
const ReportCenterModal = lazy(() => import("../../components/report-center-modal/report-center-modal.container.jsx"));
|
||||||
const BillEnterModalContainer = lazy(() => import("../../components/bill-enter-modal/bill-enter-modal.container.jsx"));
|
|
||||||
const Help = lazy(() => import("../help/help.page.jsx"));
|
const Help = lazy(() => import("../help/help.page.jsx"));
|
||||||
|
|
||||||
const { Content, Footer } = Layout;
|
const { Content } = Layout;
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
conflict: selectInstanceConflict,
|
conflict: selectInstanceConflict,
|
||||||
@@ -141,8 +137,6 @@ export function SimplifiedPartsPage({ conflict, bodyshop, alerts, setAlerts }) {
|
|||||||
}
|
}
|
||||||
>
|
>
|
||||||
<BreadCrumbs />
|
<BreadCrumbs />
|
||||||
<BillEnterModalContainer />
|
|
||||||
<JobCostingModal />
|
|
||||||
<ReportCenterModal />
|
<ReportCenterModal />
|
||||||
<EmailOverlayContainer />
|
<EmailOverlayContainer />
|
||||||
<PrintCenterModalContainer />
|
<PrintCenterModalContainer />
|
||||||
@@ -199,13 +193,6 @@ export function SimplifiedPartsPage({ conflict, bodyshop, alerts, setAlerts }) {
|
|||||||
else if (bodyshop && bodyshop.sub_status !== "active") PageContent = <ShopSubStatusComponent />;
|
else if (bodyshop && bodyshop.sub_status !== "active") PageContent = <ShopSubStatusComponent />;
|
||||||
else PageContent = AppRouteTable;
|
else PageContent = AppRouteTable;
|
||||||
|
|
||||||
const broadcastMessage = () => {
|
|
||||||
if (socket && bodyshop && bodyshop.id) {
|
|
||||||
console.log(`Broadcasting message to bodyshop ${bodyshop.id}:`);
|
|
||||||
socket.emit("broadcast-to-bodyshop", bodyshop.id, `Hello from ${clientId}`);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Layout style={{ minHeight: "100vh" }} className="layout-container">
|
<Layout style={{ minHeight: "100vh" }} className="layout-container">
|
||||||
<UpdateAlert />
|
<UpdateAlert />
|
||||||
@@ -216,38 +203,7 @@ export function SimplifiedPartsPage({ conflict, bodyshop, alerts, setAlerts }) {
|
|||||||
</Sentry.ErrorBoundary>
|
</Sentry.ErrorBoundary>
|
||||||
<FloatButton.BackTop style={{ right: 100, bottom: 25 }} />
|
<FloatButton.BackTop style={{ right: 100, bottom: 25 }} />
|
||||||
</Content>
|
</Content>
|
||||||
<Footer>
|
<GlobalFooter />
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
display: "flex",
|
|
||||||
flexDirection: "column",
|
|
||||||
justifyContent: "center",
|
|
||||||
alignItems: "center",
|
|
||||||
margin: "1rem 0rem"
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Link to="/manage/feature-request">
|
|
||||||
<Button icon={<BulbOutlined />} type="text">
|
|
||||||
{t("general.labels.feature-request")}
|
|
||||||
</Button>
|
|
||||||
</Link>
|
|
||||||
<Space>
|
|
||||||
<WssStatusDisplayComponent />
|
|
||||||
<div onClick={broadcastMessage}>
|
|
||||||
{`${InstanceRenderManager({
|
|
||||||
imex: t("titles.imexonline"),
|
|
||||||
rome: t("titles.romeonline")
|
|
||||||
})} - ${import.meta.env.VITE_APP_GIT_SHA_DATE}`}
|
|
||||||
</div>
|
|
||||||
<Button icon={<AlertOutlined />} data-canny-changelog type="text">
|
|
||||||
{t("general.labels.changelog")}
|
|
||||||
</Button>
|
|
||||||
</Space>
|
|
||||||
<Link to="/disclaimer" target="_blank" style={{ color: "#ccc" }}>
|
|
||||||
Disclaimer & Notices
|
|
||||||
</Link>
|
|
||||||
</div>
|
|
||||||
</Footer>
|
|
||||||
</Layout>
|
</Layout>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -123,3 +123,8 @@ export const setImexShopId = (imexshopid) => ({
|
|||||||
type: UserActionTypes.SET_IMEX_SHOP_ID,
|
type: UserActionTypes.SET_IMEX_SHOP_ID,
|
||||||
payload: imexshopid
|
payload: imexshopid
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const setPartsManagementOnly = (partsManagementOnly) => ({
|
||||||
|
type: UserActionTypes.SET_PARTS_MANAGEMENT_ONLY,
|
||||||
|
payload: partsManagementOnly
|
||||||
|
});
|
||||||
@@ -7,6 +7,7 @@ const INITIAL_STATE = {
|
|||||||
//language: "en-US"
|
//language: "en-US"
|
||||||
},
|
},
|
||||||
bodyshop: null,
|
bodyshop: null,
|
||||||
|
partsManagementOnly: null,
|
||||||
loginLoading: false,
|
loginLoading: false,
|
||||||
fingerprint: null,
|
fingerprint: null,
|
||||||
error: null,
|
error: null,
|
||||||
@@ -125,7 +126,11 @@ const userReducer = (state = INITIAL_STATE, action) => {
|
|||||||
...state,
|
...state,
|
||||||
imexshopid: action.payload
|
imexshopid: action.payload
|
||||||
};
|
};
|
||||||
|
case UserActionTypes.SET_PARTS_MANAGEMENT_ONLY:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
partsManagementOnly: action.payload
|
||||||
|
};
|
||||||
default:
|
default:
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ import {
|
|||||||
setInstanceConflict,
|
setInstanceConflict,
|
||||||
setInstanceId,
|
setInstanceId,
|
||||||
setLocalFingerprint,
|
setLocalFingerprint,
|
||||||
|
setPartsManagementOnly,
|
||||||
signInFailure,
|
signInFailure,
|
||||||
signInSuccess,
|
signInSuccess,
|
||||||
signOutFailure,
|
signOutFailure,
|
||||||
@@ -344,13 +345,13 @@ export function* SetAuthLevelFromShopDetails({ payload }) {
|
|||||||
payload.features?.allAccess === true
|
payload.features?.allAccess === true
|
||||||
? window.$crisp.push(["set", "session:segments", [["allAccess"]]])
|
? window.$crisp.push(["set", "session:segments", [["allAccess"]]])
|
||||||
: (() => {
|
: (() => {
|
||||||
const featureKeys = Object.keys(payload.features).filter(
|
const featureKeys = Object.keys(payload.features).filter(
|
||||||
(key) =>
|
(key) =>
|
||||||
payload.features[key] === true ||
|
payload.features[key] === true ||
|
||||||
(typeof payload.features[key] === "string" && !isNaN(Date.parse(payload.features[key])))
|
(typeof payload.features[key] === "string" && !isNaN(Date.parse(payload.features[key])))
|
||||||
);
|
);
|
||||||
window.$crisp.push(["set", "session:segments", [["basic", ...featureKeys]]]);
|
window.$crisp.push(["set", "session:segments", [["basic", ...featureKeys]]]);
|
||||||
})();
|
})();
|
||||||
|
|
||||||
InstanceRenderManager({
|
InstanceRenderManager({
|
||||||
executeFunction: true,
|
executeFunction: true,
|
||||||
@@ -359,6 +360,10 @@ export function* SetAuthLevelFromShopDetails({ payload }) {
|
|||||||
window.$zoho.salesiq.visitor.info({ "Shop Name": payload.shopname });
|
window.$zoho.salesiq.visitor.info({ "Shop Name": payload.shopname });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
//Set whether it is for parts management only.
|
||||||
|
|
||||||
|
yield put(setPartsManagementOnly(true || payload.features.partsManagementOnly));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.warn("Couldnt find $crisp.", error.message);
|
console.warn("Couldnt find $crisp.", error.message);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { create } from "lodash";
|
||||||
import { createSelector } from "reselect";
|
import { createSelector } from "reselect";
|
||||||
|
|
||||||
const selectUser = (state) => state.user;
|
const selectUser = (state) => state.user;
|
||||||
@@ -17,3 +18,7 @@ export const selectAuthLevel = createSelector([selectUser], (user) => user.authL
|
|||||||
export const selectLoginLoading = createSelector([selectUser], (user) => user.loginLoading);
|
export const selectLoginLoading = createSelector([selectUser], (user) => user.loginLoading);
|
||||||
|
|
||||||
export const selectCurrentEula = createSelector([selectUser], (user) => user.currentEula);
|
export const selectCurrentEula = createSelector([selectUser], (user) => user.currentEula);
|
||||||
|
export const selectPartsManagementOnly = createSelector(
|
||||||
|
[selectUser],
|
||||||
|
(user) => user.partsManagementOnly
|
||||||
|
);
|
||||||
@@ -33,6 +33,7 @@ const UserActionTypes = {
|
|||||||
CHECK_ACTION_CODE_FAILURE: "CHECK_ACTION_CODE_FAILURE",
|
CHECK_ACTION_CODE_FAILURE: "CHECK_ACTION_CODE_FAILURE",
|
||||||
SET_CURRENT_EULA: "SET_CURRENT_EULA",
|
SET_CURRENT_EULA: "SET_CURRENT_EULA",
|
||||||
EULA_ACCEPTED: "EULA_ACCEPTED",
|
EULA_ACCEPTED: "EULA_ACCEPTED",
|
||||||
SET_IMEX_SHOP_ID: "SET_IMEX_SHOP_ID"
|
SET_IMEX_SHOP_ID: "SET_IMEX_SHOP_ID",
|
||||||
|
SET_PARTS_MANAGEMENT_ONLY: "SET_PARTS_MANAGEMENT_ONLY",
|
||||||
};
|
};
|
||||||
export default UserActionTypes;
|
export default UserActionTypes;
|
||||||
|
|||||||
Reference in New Issue
Block a user