Merge branch 'rome/test-beta' into master-AIO. Initia
This commit is contained in:
@@ -182,7 +182,7 @@ jobs:
|
|||||||
imex-test-app-build:
|
imex-test-app-build:
|
||||||
docker:
|
docker:
|
||||||
- image: cimg/node:16.15.0
|
- image: cimg/node:16.15.0
|
||||||
|
resource_class: large
|
||||||
working_directory: ~/repo/client
|
working_directory: ~/repo/client
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
|
|||||||
@@ -32669,6 +32669,27 @@
|
|||||||
</translation>
|
</translation>
|
||||||
</translations>
|
</translations>
|
||||||
</concept_node>
|
</concept_node>
|
||||||
|
<concept_node>
|
||||||
|
<name>parts_lines</name>
|
||||||
|
<definition_loaded>false</definition_loaded>
|
||||||
|
<description></description>
|
||||||
|
<comment></comment>
|
||||||
|
<default_text></default_text>
|
||||||
|
<translations>
|
||||||
|
<translation>
|
||||||
|
<language>en-US</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
<translation>
|
||||||
|
<language>es-MX</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
<translation>
|
||||||
|
<language>fr-CA</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
</translations>
|
||||||
|
</concept_node>
|
||||||
<concept_node>
|
<concept_node>
|
||||||
<name>parts_received</name>
|
<name>parts_received</name>
|
||||||
<definition_loaded>false</definition_loaded>
|
<definition_loaded>false</definition_loaded>
|
||||||
|
|||||||
@@ -101,6 +101,7 @@ function BillEnterModalContainer({
|
|||||||
location,
|
location,
|
||||||
outstanding_returns,
|
outstanding_returns,
|
||||||
inventory,
|
inventory,
|
||||||
|
federal_tax_exempt,
|
||||||
...remainingValues
|
...remainingValues
|
||||||
} = values;
|
} = values;
|
||||||
|
|
||||||
|
|||||||
@@ -280,24 +280,22 @@ const CardPaymentModalComponent = ({
|
|||||||
<Input
|
<Input
|
||||||
className="ipayfield"
|
className="ipayfield"
|
||||||
data-ipayname="account"
|
data-ipayname="account"
|
||||||
//type="hidden"
|
type="hidden"
|
||||||
value={
|
value={
|
||||||
payments && data && data.jobs.length > 0
|
payments && data && data.jobs.length > 0
|
||||||
? data.jobs.map((j) => j.ro_number).join(", ")
|
? data.jobs.map((j) => j.ro_number).join(", ")
|
||||||
: null
|
: null
|
||||||
}
|
}
|
||||||
hidden
|
|
||||||
/>
|
/>
|
||||||
<Input
|
<Input
|
||||||
className="ipayfield"
|
className="ipayfield"
|
||||||
data-ipayname="email"
|
data-ipayname="email"
|
||||||
// type="hidden"
|
type="hidden"
|
||||||
value={
|
value={
|
||||||
payments && data && data.jobs.length > 0
|
payments && data && data.jobs.length > 0
|
||||||
? data.jobs.filter((j) => j.ownr_ea)[0]?.ownr_ea
|
? data.jobs.filter((j) => j.ownr_ea)[0]?.ownr_ea
|
||||||
: null
|
: null
|
||||||
}
|
}
|
||||||
hidden
|
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
@@ -325,9 +323,8 @@ const CardPaymentModalComponent = ({
|
|||||||
<Input
|
<Input
|
||||||
className="ipayfield"
|
className="ipayfield"
|
||||||
data-ipayname="amount"
|
data-ipayname="amount"
|
||||||
//type="hidden"
|
type="hidden"
|
||||||
value={totalAmountToCharge?.toFixed(2)}
|
value={totalAmountToCharge?.toFixed(2)}
|
||||||
hidden
|
|
||||||
/>
|
/>
|
||||||
<Button
|
<Button
|
||||||
type="primary"
|
type="primary"
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import React, {useEffect} from "react";
|
|||||||
import {useTranslation} from "react-i18next";
|
import {useTranslation} from "react-i18next";
|
||||||
import {useLocation} from "react-router-dom";
|
import {useLocation} from "react-router-dom";
|
||||||
import {QUERY_CSI_RESPONSE_BY_PK} from "../../graphql/csi.queries";
|
import {QUERY_CSI_RESPONSE_BY_PK} from "../../graphql/csi.queries";
|
||||||
|
import {DateFormatter} from "../../utils/DateFormatter";
|
||||||
import AlertComponent from "../alert/alert.component";
|
import AlertComponent from "../alert/alert.component";
|
||||||
import ConfigFormComponents from "../config-form-components/config-form-components.component";
|
import ConfigFormComponents from "../config-form-components/config-form-components.component";
|
||||||
import LoadingSpinner from "../loading-spinner/loading-spinner.component";
|
import LoadingSpinner from "../loading-spinner/loading-spinner.component";
|
||||||
@@ -44,7 +45,13 @@ export default function CsiResponseFormContainer() {
|
|||||||
readOnly
|
readOnly
|
||||||
componentList={data.csi_by_pk.csiquestion.config}
|
componentList={data.csi_by_pk.csiquestion.config}
|
||||||
/>
|
/>
|
||||||
</Form>
|
{data.csi_by_pk.validuntil ? (
|
||||||
|
<>
|
||||||
|
{t("csi.fields.validuntil")}
|
||||||
|
{": "}
|
||||||
|
<DateFormatter>{data.csi_by_pk.validuntil}</DateFormatter>
|
||||||
|
</>
|
||||||
|
) : null}</Form>
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,9 +5,9 @@ import React, {useState} from "react";
|
|||||||
import {useTranslation} from "react-i18next";
|
import {useTranslation} from "react-i18next";
|
||||||
import {Link, useLocation, useNavigate} from "react-router-dom";
|
import {Link, useLocation, useNavigate} from "react-router-dom";
|
||||||
import {DateFormatter} from "../../utils/DateFormatter";
|
import {DateFormatter} from "../../utils/DateFormatter";
|
||||||
import {alphaSort} from "../../utils/sorters";
|
|
||||||
import OwnerNameDisplay from "../owner-name-display/owner-name-display.component";
|
|
||||||
import {pageLimit} from "../../utils/config";
|
import {pageLimit} from "../../utils/config";
|
||||||
|
import {alphaSort, dateSort} from "../../utils/sorters";
|
||||||
|
import OwnerNameDisplay, {OwnerNameDisplayFunction,} from "../owner-name-display/owner-name-display.component";
|
||||||
|
|
||||||
export default function CsiResponseListPaginated({
|
export default function CsiResponseListPaginated({
|
||||||
refetch,
|
refetch,
|
||||||
@@ -16,23 +16,25 @@ export default function CsiResponseListPaginated({
|
|||||||
total,
|
total,
|
||||||
}) {
|
}) {
|
||||||
const search = queryString.parse(useLocation().search);
|
const search = queryString.parse(useLocation().search);
|
||||||
const {responseid, page, sortcolumn, sortorder} = search;
|
const {responseid} = search;
|
||||||
const history = useNavigate();
|
const history = useNavigate();
|
||||||
|
const {t} = useTranslation();
|
||||||
const [state, setState] = useState({
|
const [state, setState] = useState({
|
||||||
sortedInfo: {},
|
sortedInfo: {},
|
||||||
filteredInfo: {text: ""},
|
filteredInfo: {text: ""},
|
||||||
|
page: "",
|
||||||
});
|
});
|
||||||
|
|
||||||
const {t} = useTranslation();
|
|
||||||
const columns = [
|
const columns = [
|
||||||
{
|
{
|
||||||
title: t("jobs.fields.ro_number"),
|
title: t("jobs.fields.ro_number"),
|
||||||
dataIndex: "ro_number",
|
dataIndex: "ro_number",
|
||||||
key: "ro_number",
|
key: "ro_number",
|
||||||
width: "8%",
|
|
||||||
sorter: (a, b) => alphaSort(a.job.ro_number, b.job.ro_number),
|
|
||||||
sortOrder: sortcolumn === "ro_number" && sortorder,
|
|
||||||
|
|
||||||
|
sorter: (a, b) => alphaSort(a.job.ro_number, b.job.ro_number),
|
||||||
|
sortOrder:
|
||||||
|
state.sortedInfo.columnKey === "ro_number" && state.sortedInfo.order,
|
||||||
render: (text, record) => (
|
render: (text, record) => (
|
||||||
<Link to={"/manage/jobs/" + record.job.id}>
|
<Link to={"/manage/jobs/" + record.job.id}>
|
||||||
{record.job.ro_number || t("general.labels.na")}
|
{record.job.ro_number || t("general.labels.na")}
|
||||||
@@ -41,15 +43,15 @@ export default function CsiResponseListPaginated({
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t("jobs.fields.owner"),
|
title: t("jobs.fields.owner"),
|
||||||
dataIndex: "owner",
|
dataIndex: "owner_name",
|
||||||
key: "owner",
|
key: "owner_name",
|
||||||
ellipsis: true,
|
sorter: (a, b) => alphaSort(OwnerNameDisplayFunction(a.job),
|
||||||
sorter: (a, b) => alphaSort(a.job.ownr_ln, b.job.ownr_ln),
|
OwnerNameDisplayFunction(b.job)
|
||||||
width: "25%",
|
),
|
||||||
sortOrder: sortcolumn === "owner" && sortorder,
|
sortOrder: state.sortedInfo.columnKey === "owner_name" && state.sortedInfo.order,
|
||||||
render: (text, record) => {
|
render: (text, record) => {
|
||||||
return record.job.owner ? (
|
return record.job.ownerid ? (
|
||||||
<Link to={"/manage/owners/" + record.job.owner.id}>
|
<Link to={"/manage/owners/" + record.job.ownerid}>
|
||||||
<OwnerNameDisplay ownerObject={record.job}/>
|
<OwnerNameDisplay ownerObject={record.job}/>
|
||||||
</Link>
|
</Link>
|
||||||
) : (
|
) : (
|
||||||
@@ -64,9 +66,8 @@ export default function CsiResponseListPaginated({
|
|||||||
dataIndex: "completedon",
|
dataIndex: "completedon",
|
||||||
key: "completedon",
|
key: "completedon",
|
||||||
ellipsis: true,
|
ellipsis: true,
|
||||||
sorter: (a, b) => a.completedon - b.completedon,
|
sorter: (a, b) => dateSort(a.completedon, b.completedon),
|
||||||
width: "25%",
|
sortOrder: state.sortedInfo.columnKey === "completedon" && state.sortedInfo.order,
|
||||||
sortOrder: sortcolumn === "completedon" && sortorder,
|
|
||||||
render: (text, record) => {
|
render: (text, record) => {
|
||||||
return record.completedon ? (
|
return record.completedon ? (
|
||||||
<DateFormatter>{record.completedon}</DateFormatter>
|
<DateFormatter>{record.completedon}</DateFormatter>
|
||||||
@@ -76,11 +77,12 @@ export default function CsiResponseListPaginated({
|
|||||||
];
|
];
|
||||||
|
|
||||||
const handleTableChange = (pagination, filters, sorter) => {
|
const handleTableChange = (pagination, filters, sorter) => {
|
||||||
setState({...state, filteredInfo: filters, sortedInfo: sorter});
|
setState({
|
||||||
search.page = pagination.current;
|
...state,
|
||||||
search.sortcolumn = sorter.columnKey;
|
filteredInfo: filters,
|
||||||
search.sortorder = sorter.order;
|
sortedInfo: sorter,
|
||||||
history({search: queryString.stringify(search)});
|
page: pagination.current,
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleOnRowClick = (record) => {
|
const handleOnRowClick = (record) => {
|
||||||
@@ -108,7 +110,7 @@ export default function CsiResponseListPaginated({
|
|||||||
pagination={{
|
pagination={{
|
||||||
position: "top",
|
position: "top",
|
||||||
pageSize: pageLimit,
|
pageSize: pageLimit,
|
||||||
current: parseInt(page || 1),
|
current: parseInt(state.page || 1),
|
||||||
total: total,
|
total: total,
|
||||||
}}
|
}}
|
||||||
columns={columns}
|
columns={columns}
|
||||||
@@ -121,13 +123,7 @@ export default function CsiResponseListPaginated({
|
|||||||
},
|
},
|
||||||
selectedRowKeys: [responseid],
|
selectedRowKeys: [responseid],
|
||||||
type: "radio",
|
type: "radio",
|
||||||
}}
|
|
||||||
onRow={(record, rowIndex) => {
|
|
||||||
return {
|
|
||||||
onClick: (event) => {
|
|
||||||
handleOnRowClick(record);
|
|
||||||
}, // click row
|
|
||||||
};
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Card>
|
</Card>
|
||||||
|
|||||||
@@ -0,0 +1,77 @@
|
|||||||
|
import {useQuery} from "@apollo/client";
|
||||||
|
import {Card, Divider, Drawer, Grid} from "antd";
|
||||||
|
import queryString from "query-string";
|
||||||
|
import React from "react";
|
||||||
|
import {useTranslation} from "react-i18next";
|
||||||
|
import {Link, useNavigate, useLocation} from "react-router-dom";
|
||||||
|
import {QUERY_PARTS_QUEUE_CARD_DETAILS} from "../../graphql/jobs.queries";
|
||||||
|
import AlertComponent from "../alert/alert.component";
|
||||||
|
import JobsDetailHeader from "../jobs-detail-header/jobs-detail-header.component";
|
||||||
|
import LoadingSpinner from "../loading-spinner/loading-spinner.component";
|
||||||
|
import PartsQueueJobLinesComponent from "./parts-queue-job-lines.component";
|
||||||
|
|
||||||
|
export default function PartsQueueDetailCard() {
|
||||||
|
const selectedBreakpoint = Object.entries(Grid.useBreakpoint())
|
||||||
|
.filter((screen) => !!screen[1])
|
||||||
|
.slice(-1)[0];
|
||||||
|
|
||||||
|
const bpoints = {
|
||||||
|
xs: "100%",
|
||||||
|
sm: "100%",
|
||||||
|
md: "100%",
|
||||||
|
lg: "75%",
|
||||||
|
xl: "75%",
|
||||||
|
xxl: "60%",
|
||||||
|
};
|
||||||
|
const drawerPercentage = selectedBreakpoint
|
||||||
|
? bpoints[selectedBreakpoint[0]]
|
||||||
|
: "100%";
|
||||||
|
|
||||||
|
const searchParams = queryString.parse(useLocation().search);
|
||||||
|
const {selected} = searchParams;
|
||||||
|
const history = useNavigate();
|
||||||
|
const {loading, error, data} = useQuery(QUERY_PARTS_QUEUE_CARD_DETAILS, {
|
||||||
|
variables: {id: selected},
|
||||||
|
skip: !selected,
|
||||||
|
fetchPolicy: "network-only",
|
||||||
|
nextFetchPolicy: "network-only",
|
||||||
|
});
|
||||||
|
|
||||||
|
const {t} = useTranslation();
|
||||||
|
const handleDrawerClose = () => {
|
||||||
|
delete searchParams.selected;
|
||||||
|
history({
|
||||||
|
search: queryString.stringify({
|
||||||
|
...searchParams,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Drawer
|
||||||
|
open={!!selected}
|
||||||
|
destroyOnClose
|
||||||
|
width={drawerPercentage}
|
||||||
|
placement="right"
|
||||||
|
onClose={handleDrawerClose}
|
||||||
|
>
|
||||||
|
{loading ? <LoadingSpinner/> : null}
|
||||||
|
{error ? <AlertComponent message={error.message} type="error"/> : null}
|
||||||
|
{data ? (
|
||||||
|
<Card
|
||||||
|
title={
|
||||||
|
<Link to={`/manage/jobs/${data.jobs_by_pk.id}`}>
|
||||||
|
{data.jobs_by_pk.ro_number || t("general.labels.na")}
|
||||||
|
</Link>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<JobsDetailHeader job={data ? data.jobs_by_pk : null}/>
|
||||||
|
<Divider type="horizontal"/>
|
||||||
|
<PartsQueueJobLinesComponent
|
||||||
|
jobLines={data.jobs_by_pk ? data.jobs_by_pk.joblines : null}
|
||||||
|
/>
|
||||||
|
</Card>
|
||||||
|
) : null}
|
||||||
|
</Drawer>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,209 @@
|
|||||||
|
import {Card, Table} from "antd";
|
||||||
|
import React, {useState} from "react";
|
||||||
|
import {useTranslation} from "react-i18next";
|
||||||
|
import {connect} from "react-redux";
|
||||||
|
import {createStructuredSelector} from "reselect";
|
||||||
|
import {selectJobReadOnly} from "../../redux/application/application.selectors";
|
||||||
|
import {selectBodyshop} from "../../redux/user/user.selectors";
|
||||||
|
import CurrencyFormatter from "../../utils/CurrencyFormatter";
|
||||||
|
import {onlyUnique} from "../../utils/arrayHelper";
|
||||||
|
import {alphaSort} from "../../utils/sorters";
|
||||||
|
|
||||||
|
const mapStateToProps = createStructuredSelector({
|
||||||
|
bodyshop: selectBodyshop,
|
||||||
|
jobRO: selectJobReadOnly,
|
||||||
|
});
|
||||||
|
|
||||||
|
const mapDispatchToProps = (dispatch) => ({});
|
||||||
|
|
||||||
|
export function PartsQueueJobLinesComponent({jobRO, loading, jobLines}) {
|
||||||
|
const [state, setState] = useState({
|
||||||
|
sortedInfo: {},
|
||||||
|
filteredInfo: {},
|
||||||
|
});
|
||||||
|
const {t} = useTranslation();
|
||||||
|
|
||||||
|
const columns = [
|
||||||
|
{
|
||||||
|
title: "#",
|
||||||
|
dataIndex: "line_no",
|
||||||
|
key: "line_no",
|
||||||
|
sorter: (a, b) => a.line_no - b.line_no,
|
||||||
|
sortOrder:
|
||||||
|
state.sortedInfo.columnKey === "line_no" && state.sortedInfo.order,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t("joblines.fields.line_desc"),
|
||||||
|
dataIndex: "line_desc",
|
||||||
|
key: "line_desc",
|
||||||
|
sorter: (a, b) => alphaSort(a.line_desc, b.line_desc),
|
||||||
|
onCell: (record) => ({
|
||||||
|
className: record.manual_line && "job-line-manual",
|
||||||
|
style: {
|
||||||
|
...(record.critical ? {boxShadow: " -.5em 0 0 #FFC107"} : {}),
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
sortOrder:
|
||||||
|
state.sortedInfo.columnKey === "line_desc" && state.sortedInfo.order,
|
||||||
|
ellipsis: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t("joblines.fields.oem_partno"),
|
||||||
|
dataIndex: "oem_partno",
|
||||||
|
key: "oem_partno",
|
||||||
|
sorter: (a, b) => alphaSort(a.oem_partno, b.oem_partno),
|
||||||
|
sortOrder:
|
||||||
|
state.sortedInfo.columnKey === "oem_partno" && state.sortedInfo.order,
|
||||||
|
ellipsis: true,
|
||||||
|
render: (text, record) =>
|
||||||
|
`${record.oem_partno || ""} ${
|
||||||
|
record.alt_partno ? `(${record.alt_partno})` : ""
|
||||||
|
}`.trim(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t("joblines.fields.part_type"),
|
||||||
|
dataIndex: "part_type",
|
||||||
|
key: "part_type",
|
||||||
|
filteredValue: state.filteredInfo.part_type || null,
|
||||||
|
sorter: (a, b) => alphaSort(a.part_type, b.part_type),
|
||||||
|
sortOrder:
|
||||||
|
state.sortedInfo.columnKey === "part_type" && state.sortedInfo.order,
|
||||||
|
filters: [
|
||||||
|
{
|
||||||
|
text: t("jobs.labels.partsfilter"),
|
||||||
|
value: [
|
||||||
|
"PAN",
|
||||||
|
"PAC",
|
||||||
|
"PAR",
|
||||||
|
"PAL",
|
||||||
|
"PAA",
|
||||||
|
"PAM",
|
||||||
|
"PAP",
|
||||||
|
"PAS",
|
||||||
|
"PASL",
|
||||||
|
"PAG",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: t("joblines.fields.part_types.PAN"),
|
||||||
|
value: ["PAN"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: t("joblines.fields.part_types.PAP"),
|
||||||
|
value: ["PAP"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: t("joblines.fields.part_types.PAL"),
|
||||||
|
value: ["PAL"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: t("joblines.fields.part_types.PAA"),
|
||||||
|
value: ["PAA"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: t("joblines.fields.part_types.PAG"),
|
||||||
|
value: ["PAG"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: t("joblines.fields.part_types.PAS"),
|
||||||
|
value: ["PAS"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: t("joblines.fields.part_types.PASL"),
|
||||||
|
value: ["PASL"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: t("joblines.fields.part_types.PAC"),
|
||||||
|
value: ["PAC"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: t("joblines.fields.part_types.PAR"),
|
||||||
|
value: ["PAR"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: t("joblines.fields.part_types.PAM"),
|
||||||
|
value: ["PAM"],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
onFilter: (value, record) => value.includes(record.part_type),
|
||||||
|
render: (text, record) =>
|
||||||
|
record.part_type
|
||||||
|
? t(`joblines.fields.part_types.${record.part_type}`)
|
||||||
|
: null,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t("joblines.fields.part_qty"),
|
||||||
|
dataIndex: "part_qty",
|
||||||
|
key: "part_qty",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t("joblines.fields.act_price"),
|
||||||
|
dataIndex: "act_price",
|
||||||
|
key: "act_price",
|
||||||
|
sorter: (a, b) => a.act_price - b.act_price,
|
||||||
|
sortOrder:
|
||||||
|
state.sortedInfo.columnKey === "act_price" && state.sortedInfo.order,
|
||||||
|
ellipsis: true,
|
||||||
|
render: (text, record) => (
|
||||||
|
<CurrencyFormatter>
|
||||||
|
{record.db_ref === "900510" || record.db_ref === "900511"
|
||||||
|
? record.prt_dsmk_m
|
||||||
|
: record.act_price}
|
||||||
|
</CurrencyFormatter>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t("joblines.fields.location"),
|
||||||
|
dataIndex: "location",
|
||||||
|
key: "location",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t("joblines.fields.status"),
|
||||||
|
dataIndex: "status",
|
||||||
|
key: "status",
|
||||||
|
sorter: (a, b) => alphaSort(a.status, b.status),
|
||||||
|
sortOrder:
|
||||||
|
state.sortedInfo.columnKey === "status" && state.sortedInfo.order,
|
||||||
|
filteredValue: state.filteredInfo.status || null,
|
||||||
|
filters:
|
||||||
|
(jobLines &&
|
||||||
|
jobLines
|
||||||
|
.map((l) => l.status)
|
||||||
|
.filter(onlyUnique)
|
||||||
|
.map((s) => {
|
||||||
|
return {
|
||||||
|
text: s || "No Status*",
|
||||||
|
value: [s],
|
||||||
|
};
|
||||||
|
})) ||
|
||||||
|
[],
|
||||||
|
onFilter: (value, record) => value.includes(record.status),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const handleTableChange = (pagination, filters, sorter) => {
|
||||||
|
setState((state) => ({
|
||||||
|
...state,
|
||||||
|
filteredInfo: filters,
|
||||||
|
sortedInfo: sorter,
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card title={t("jobs.labels.parts_lines")}>
|
||||||
|
<Table
|
||||||
|
columns={columns}
|
||||||
|
rowKey="id"
|
||||||
|
loading={loading}
|
||||||
|
pagination={false}
|
||||||
|
dataSource={jobLines}
|
||||||
|
onChange={handleTableChange}
|
||||||
|
/>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default connect(
|
||||||
|
mapStateToProps,
|
||||||
|
mapDispatchToProps
|
||||||
|
)(PartsQueueJobLinesComponent);
|
||||||
@@ -8,31 +8,30 @@ import {useTranslation} from "react-i18next";
|
|||||||
import {connect} from "react-redux";
|
import {connect} from "react-redux";
|
||||||
import {Link, useLocation, useNavigate} from "react-router-dom";
|
import {Link, useLocation, useNavigate} from "react-router-dom";
|
||||||
import {createStructuredSelector} from "reselect";
|
import {createStructuredSelector} from "reselect";
|
||||||
import AlertComponent from "../../components/alert/alert.component";
|
|
||||||
import JobPartsQueueCount from "../../components/job-parts-queue-count/job-parts-queue-count.component";
|
|
||||||
import JobRemoveFromPartsQueue
|
|
||||||
from "../../components/job-remove-from-parst-queue/job-remove-from-parts-queue.component";
|
|
||||||
import OwnerNameDisplay from "../../components/owner-name-display/owner-name-display.component";
|
|
||||||
import ProductionListColumnComment
|
|
||||||
from "../../components/production-list-columns/production-list-columns.comment.component";
|
|
||||||
import {QUERY_PARTS_QUEUE} from "../../graphql/jobs.queries";
|
import {QUERY_PARTS_QUEUE} from "../../graphql/jobs.queries";
|
||||||
import {selectBodyshop} from "../../redux/user/user.selectors";
|
import {selectBodyshop} from "../../redux/user/user.selectors";
|
||||||
import {DateTimeFormatter, TimeAgoFormatter} from "../../utils/DateFormatter";
|
import {DateTimeFormatter, TimeAgoFormatter} from "../../utils/DateFormatter";
|
||||||
|
import {onlyUnique} from "../../utils/arrayHelper";
|
||||||
|
import {pageLimit} from "../../utils/config";
|
||||||
import {alphaSort, dateSort} from "../../utils/sorters";
|
import {alphaSort, dateSort} from "../../utils/sorters";
|
||||||
import useLocalStorage from "../../utils/useLocalStorage";
|
import useLocalStorage from "../../utils/useLocalStorage";
|
||||||
import {pageLimit} from "../../utils/config";
|
import AlertComponent from "../alert/alert.component";
|
||||||
|
import JobPartsQueueCount from "../job-parts-queue-count/job-parts-queue-count.component";
|
||||||
|
import JobRemoveFromPartsQueue from "../job-remove-from-parst-queue/job-remove-from-parts-queue.component";
|
||||||
|
import OwnerNameDisplay, {OwnerNameDisplayFunction,} from "../owner-name-display/owner-name-display.component";
|
||||||
|
import ProductionListColumnComment from "../production-list-columns/production-list-columns.comment.component";
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
bodyshop: selectBodyshop,
|
bodyshop: selectBodyshop,
|
||||||
});
|
});
|
||||||
|
|
||||||
export function PartsQueuePageComponent({bodyshop}) {
|
export function PartsQueueListComponent({bodyshop}) {
|
||||||
const searchParams = queryString.parse(useLocation().search);
|
const searchParams = queryString.parse(useLocation().search);
|
||||||
const {
|
const {
|
||||||
//page,
|
selected,
|
||||||
sortcolumn,
|
sortcolumn,
|
||||||
sortorder,
|
sortorder,
|
||||||
statusFilters,
|
statusFilters
|
||||||
} = searchParams;
|
} = searchParams;
|
||||||
const history = useNavigate();
|
const history = useNavigate();
|
||||||
const [filter, setFilter] = useLocalStorage("filter_parts_queue", null);
|
const [filter, setFilter] = useLocalStorage("filter_parts_queue", null);
|
||||||
@@ -41,19 +40,10 @@ export function PartsQueuePageComponent({bodyshop}) {
|
|||||||
fetchPolicy: "network-only",
|
fetchPolicy: "network-only",
|
||||||
nextFetchPolicy: "network-only",
|
nextFetchPolicy: "network-only",
|
||||||
variables: {
|
variables: {
|
||||||
// offset: page ? (page - 1) * 25 : 0,
|
|
||||||
// limit: 25,
|
|
||||||
statuses: (statusFilters && JSON.parse(statusFilters)) ||
|
statuses: (statusFilters && JSON.parse(statusFilters)) ||
|
||||||
bodyshop.md_ro_statuses.active_statuses || ["Open", "Open*"],
|
bodyshop.md_ro_statuses.active_statuses || ["Open", "Open*"],
|
||||||
order: [
|
|
||||||
{
|
|
||||||
[sortcolumn || "ro_number"]: sortorder
|
|
||||||
? sortorder === "descend"
|
|
||||||
? "desc"
|
|
||||||
: "asc"
|
|
||||||
: "desc",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -109,6 +99,18 @@ export function PartsQueuePageComponent({bodyshop}) {
|
|||||||
history({search: queryString.stringify(searchParams)});
|
history({search: queryString.stringify(searchParams)});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleOnRowClick = (record) => {
|
||||||
|
if (record) {
|
||||||
|
if (record.id) {
|
||||||
|
history({
|
||||||
|
search: queryString.stringify({
|
||||||
|
...searchParams,
|
||||||
|
selected: record.id,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
const columns = [
|
const columns = [
|
||||||
{
|
{
|
||||||
title: t("jobs.fields.ro_number"),
|
title: t("jobs.fields.ro_number"),
|
||||||
@@ -127,7 +129,8 @@ export function PartsQueuePageComponent({bodyshop}) {
|
|||||||
title: t("jobs.fields.owner"),
|
title: t("jobs.fields.owner"),
|
||||||
dataIndex: "ownr_ln",
|
dataIndex: "ownr_ln",
|
||||||
key: "ownr_ln",
|
key: "ownr_ln",
|
||||||
sorter: (a, b) => alphaSort(a.ownr_ln, b.ownr_ln),
|
sorter: (a, b) =>
|
||||||
|
alphaSort(OwnerNameDisplayFunction(a), OwnerNameDisplayFunction(b)),
|
||||||
sortOrder: sortcolumn === "ownr_ln" && sortorder,
|
sortOrder: sortcolumn === "ownr_ln" && sortorder,
|
||||||
render: (text, record) => {
|
render: (text, record) => {
|
||||||
return record.ownerid ? (
|
return record.ownerid ? (
|
||||||
@@ -141,6 +144,56 @@ export function PartsQueuePageComponent({bodyshop}) {
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: t("jobs.fields.vehicle"),
|
||||||
|
dataIndex: "vehicle",
|
||||||
|
key: "vehicle",
|
||||||
|
ellipsis: true,
|
||||||
|
sorter: (a, b) =>
|
||||||
|
alphaSort(
|
||||||
|
`${a.v_model_yr || ""} ${a.v_make_desc || ""} ${
|
||||||
|
a.v_model_desc || ""
|
||||||
|
}`,
|
||||||
|
`${b.v_model_yr || ""} ${b.v_make_desc || ""} ${b.v_model_desc || ""}`
|
||||||
|
),
|
||||||
|
sortOrder: sortcolumn === "vehicle" && sortorder,
|
||||||
|
render: (text, record) => {
|
||||||
|
return record.vehicleid ? (
|
||||||
|
<Link to={"/manage/vehicles/" + record.vehicleid}>
|
||||||
|
{`${record.v_model_yr || ""} ${record.v_make_desc || ""} ${
|
||||||
|
record.v_model_desc || ""
|
||||||
|
}`}
|
||||||
|
</Link>
|
||||||
|
) : (
|
||||||
|
<span>{`${record.v_model_yr || ""} ${record.v_make_desc || ""} ${
|
||||||
|
record.v_model_desc || ""
|
||||||
|
}`}</span>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t("jobs.fields.ins_co_nm_short"),
|
||||||
|
dataIndex: "ins_co_nm",
|
||||||
|
key: "ins_co_nm",
|
||||||
|
ellipsis: true,
|
||||||
|
sorter: (a, b) => alphaSort(a.ins_co_nm, b.ins_co_nm),
|
||||||
|
sortOrder: sortcolumn === "ins_co_nm" && sortorder,
|
||||||
|
filteredValue: filter?.ins_co_nm || null,
|
||||||
|
filters:
|
||||||
|
(jobs &&
|
||||||
|
jobs
|
||||||
|
.map((j) => j.ins_co_nm)
|
||||||
|
.filter(onlyUnique)
|
||||||
|
.map((s) => {
|
||||||
|
return {
|
||||||
|
text: s || "No Ins. Co.*",
|
||||||
|
value: [s],
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.sort((a, b) => alphaSort(a.text, b.text))) ||
|
||||||
|
[],
|
||||||
|
onFilter: (value, record) => value.includes(record.ins_co_nm),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: t("jobs.fields.status"),
|
title: t("jobs.fields.status"),
|
||||||
dataIndex: "status",
|
dataIndex: "status",
|
||||||
@@ -172,23 +225,16 @@ export function PartsQueuePageComponent({bodyshop}) {
|
|||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t("jobs.fields.vehicle"),
|
title: t("jobs.fields.scheduled_completion"),
|
||||||
dataIndex: "vehicle",
|
dataIndex: "scheduled_completion",
|
||||||
key: "vehicle",
|
key: "scheduled_completion",
|
||||||
ellipsis: true,
|
ellipsis: true,
|
||||||
render: (text, record) => {
|
sorter: (a, b) =>
|
||||||
return record.vehicleid ? (
|
dateSort(a.scheduled_completion, b.scheduled_completion),
|
||||||
<Link to={"/manage/vehicles/" + record.vehicleid}>
|
sortOrder: sortcolumn === "scheduled_completion" && sortorder,
|
||||||
{`${record.v_model_yr || ""} ${record.v_make_desc || ""} ${
|
render: (text, record) => (
|
||||||
record.v_model_desc || ""
|
<DateTimeFormatter>{record.scheduled_completion}</DateTimeFormatter>
|
||||||
}`}
|
),
|
||||||
</Link>
|
|
||||||
) : (
|
|
||||||
<span>{`${record.v_model_yr || ""} ${record.v_make_desc || ""} ${
|
|
||||||
record.v_model_desc || ""
|
|
||||||
}`}</span>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
// {
|
// {
|
||||||
// title: t("vehicles.fields.plate_no"),
|
// title: t("vehicles.fields.plate_no"),
|
||||||
@@ -199,15 +245,8 @@ export function PartsQueuePageComponent({bodyshop}) {
|
|||||||
// render: (text, record) => {
|
// render: (text, record) => {
|
||||||
// return record.plate_no ? record.plate_no : "";
|
// return record.plate_no ? record.plate_no : "";
|
||||||
// },
|
// },
|
||||||
// },
|
|
||||||
{
|
//},
|
||||||
title: t("jobs.fields.clm_no"),
|
|
||||||
dataIndex: "clm_no",
|
|
||||||
key: "clm_no",
|
|
||||||
ellipsis: true,
|
|
||||||
sorter: (a, b) => alphaSort(a.clm_no, b.clm_no),
|
|
||||||
sortOrder: sortcolumn === "clm_no" && sortorder,
|
|
||||||
},
|
|
||||||
// {
|
// {
|
||||||
// title: t("jobs.fields.clm_total"),
|
// title: t("jobs.fields.clm_total"),
|
||||||
// dataIndex: "clm_total",
|
// dataIndex: "clm_total",
|
||||||
@@ -309,9 +348,15 @@ export function PartsQueuePageComponent({bodyshop}) {
|
|||||||
style={{height: "100%"}}
|
style={{height: "100%"}}
|
||||||
scroll={{x: true}}
|
scroll={{x: true}}
|
||||||
onChange={handleTableChange}
|
onChange={handleTableChange}
|
||||||
/>
|
rowSelection={{
|
||||||
|
onSelect: (record) => {
|
||||||
|
handleOnRowClick(record);
|
||||||
|
},
|
||||||
|
selectedRowKeys: [selected],
|
||||||
|
type: "radio",
|
||||||
|
}}/>
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default connect(mapStateToProps, null)(PartsQueuePageComponent);
|
export default connect(mapStateToProps, null)(PartsQueueListComponent);
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import React from "react";
|
|
||||||
import {Form} from "antd";
|
import {Form} from "antd";
|
||||||
|
import React from "react";
|
||||||
import ConfigFormComponents from "../config-form-components/config-form-components.component";
|
import ConfigFormComponents from "../config-form-components/config-form-components.component";
|
||||||
|
|
||||||
export default function ShopCsiConfigForm({selectedCsi}) {
|
export default function ShopCsiConfigForm({selectedCsi}) {
|
||||||
@@ -10,7 +10,7 @@ export default function ShopCsiConfigForm({selectedCsi}) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
The Config Form {readOnly}
|
{readOnly}
|
||||||
{selectedCsi && (
|
{selectedCsi && (
|
||||||
<Form form={form} onFinish={handleFinish}>
|
<Form form={form} onFinish={handleFinish}>
|
||||||
<ConfigFormComponents
|
<ConfigFormComponents
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
import {CheckCircleFilled} from "@ant-design/icons";
|
import {CheckCircleFilled} from "@ant-design/icons";
|
||||||
|
import {useQuery} from "@apollo/client";
|
||||||
import {Button, Col, List, Row} from "antd";
|
import {Button, Col, List, Row} from "antd";
|
||||||
import React, {useState} from "react";
|
import React, {useState} from "react";
|
||||||
import {useQuery} from "@apollo/client";
|
|
||||||
import {useTranslation} from "react-i18next";
|
import {useTranslation} from "react-i18next";
|
||||||
import {GET_ALL_QUESTION_SETS} from "../../graphql/csi.queries";
|
import {GET_ALL_QUESTION_SETS} from "../../graphql/csi.queries";
|
||||||
import {DateFormatter} from "../../utils/DateFormatter";
|
import {DateFormatter} from "../../utils/DateFormatter";
|
||||||
@@ -21,7 +22,7 @@ export default function ShopCsiConfig() {
|
|||||||
if (error) return <AlertComponent message={error.message} type="error"/>;
|
if (error) return <AlertComponent message={error.message} type="error"/>;
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
The Config Form
|
|
||||||
<Row>
|
<Row>
|
||||||
<Col span={3}>
|
<Col span={3}>
|
||||||
<List
|
<List
|
||||||
@@ -42,7 +43,8 @@ export default function ShopCsiConfig() {
|
|||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</Col>
|
</Col>
|
||||||
<Col span={21}>
|
<Col span={1}/>
|
||||||
|
<Col span={20}>
|
||||||
<ShopCsiConfigForm selectedCsi={selectedCsi}/>
|
<ShopCsiConfigForm selectedCsi={selectedCsi}/>
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
|
|||||||
@@ -57,19 +57,16 @@ export const INSERT_CSI = gql`
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
export const QUERY_CSI_RESPONSE_PAGINATED = gql`
|
export const QUERY_CSI_RESPONSE_PAGINATED = gql`
|
||||||
query QUERY_CSI_RESPONSE_PAGINATED(
|
query QUERY_CSI_RESPONSE_PAGINATED{
|
||||||
$offset: Int
|
|
||||||
$limit: Int
|
csi(order_by: { completedon: desc_nulls_last }) {
|
||||||
$order: [csi_order_by!]!
|
|
||||||
) {
|
|
||||||
csi(offset: $offset, limit: $limit, order_by: $order) {
|
|
||||||
id
|
id
|
||||||
completedon
|
completedon
|
||||||
job {
|
job {
|
||||||
ownr_fn
|
ownr_fn
|
||||||
ownr_ln
|
ownr_ln
|
||||||
|
ownerid
|
||||||
ro_number
|
ro_number
|
||||||
|
|
||||||
id
|
id
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -83,6 +80,7 @@ export const QUERY_CSI_RESPONSE_PAGINATED = gql`
|
|||||||
export const QUERY_CSI_RESPONSE_BY_PK = gql`
|
export const QUERY_CSI_RESPONSE_BY_PK = gql`
|
||||||
query QUERY_CSI_RESPONSE_BY_PK($id: uuid!) {
|
query QUERY_CSI_RESPONSE_BY_PK($id: uuid!) {
|
||||||
csi_by_pk(id: $id) {
|
csi_by_pk(id: $id) {
|
||||||
|
completedon
|
||||||
relateddata
|
relateddata
|
||||||
valid
|
valid
|
||||||
validuntil
|
validuntil
|
||||||
|
|||||||
@@ -110,10 +110,9 @@ export const QUERY_ALL_ACTIVE_JOBS = gql`
|
|||||||
export const QUERY_PARTS_QUEUE = gql`
|
export const QUERY_PARTS_QUEUE = gql`
|
||||||
query QUERY_PARTS_QUEUE(
|
query QUERY_PARTS_QUEUE(
|
||||||
$statuses: [String!]!
|
$statuses: [String!]!
|
||||||
$offset: Int
|
, $offset: Int
|
||||||
$limit: Int
|
, $limit: Int) {
|
||||||
$order: [jobs_order_by!]
|
|
||||||
) {
|
|
||||||
jobs_aggregate(where: { _and: [{ status: { _in: $statuses } }] }) {
|
jobs_aggregate(where: { _and: [{ status: { _in: $statuses } }] }) {
|
||||||
aggregate {
|
aggregate {
|
||||||
count(distinct: true)
|
count(distinct: true)
|
||||||
@@ -125,7 +124,7 @@ export const QUERY_PARTS_QUEUE = gql`
|
|||||||
}
|
}
|
||||||
offset: $offset
|
offset: $offset
|
||||||
limit: $limit
|
limit: $limit
|
||||||
order_by: $order
|
order_by: { ro_number: desc }
|
||||||
) {
|
) {
|
||||||
ownr_fn
|
ownr_fn
|
||||||
ownr_ln
|
ownr_ln
|
||||||
@@ -142,7 +141,9 @@ export const QUERY_PARTS_QUEUE = gql`
|
|||||||
v_color
|
v_color
|
||||||
vehicleid
|
vehicleid
|
||||||
scheduled_in
|
scheduled_in
|
||||||
|
scheduled_completion
|
||||||
id
|
id
|
||||||
|
ins_co_nm
|
||||||
clm_no
|
clm_no
|
||||||
ro_number
|
ro_number
|
||||||
status
|
status
|
||||||
@@ -2440,3 +2441,163 @@ export const QUERY_JOBS_TECH_ASIGNED_TO_BY_TEAM = gql`
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
export const QUERY_PARTS_QUEUE_CARD_DETAILS = gql`
|
||||||
|
query QUERY_JOB_CARD_DETAILS($id: uuid!) {
|
||||||
|
jobs_by_pk(id: $id) {
|
||||||
|
actual_completion
|
||||||
|
actual_delivery
|
||||||
|
actual_in
|
||||||
|
alt_transport
|
||||||
|
available_jobs {
|
||||||
|
id
|
||||||
|
}
|
||||||
|
area_of_damage
|
||||||
|
ca_gst_registrant
|
||||||
|
cccontracts {
|
||||||
|
agreementnumber
|
||||||
|
courtesycar {
|
||||||
|
id
|
||||||
|
make
|
||||||
|
model
|
||||||
|
year
|
||||||
|
plate
|
||||||
|
fleetnumber
|
||||||
|
}
|
||||||
|
id
|
||||||
|
scheduledreturn
|
||||||
|
start
|
||||||
|
status
|
||||||
|
}
|
||||||
|
clm_no
|
||||||
|
clm_total
|
||||||
|
comment
|
||||||
|
date_estimated
|
||||||
|
date_exported
|
||||||
|
date_invoiced
|
||||||
|
date_last_contacted
|
||||||
|
date_next_contact
|
||||||
|
date_open
|
||||||
|
date_repairstarted
|
||||||
|
date_scheduled
|
||||||
|
ded_amt
|
||||||
|
employee_body
|
||||||
|
employee_body_rel {
|
||||||
|
id
|
||||||
|
first_name
|
||||||
|
last_name
|
||||||
|
}
|
||||||
|
employee_csr
|
||||||
|
employee_csr_rel {
|
||||||
|
id
|
||||||
|
first_name
|
||||||
|
last_name
|
||||||
|
}
|
||||||
|
employee_prep
|
||||||
|
employee_prep_rel {
|
||||||
|
id
|
||||||
|
first_name
|
||||||
|
last_name
|
||||||
|
}
|
||||||
|
employee_refinish
|
||||||
|
employee_refinish_rel {
|
||||||
|
id
|
||||||
|
first_name
|
||||||
|
last_name
|
||||||
|
}
|
||||||
|
est_co_nm
|
||||||
|
est_ct_fn
|
||||||
|
est_ct_ln
|
||||||
|
est_ea
|
||||||
|
est_ph1
|
||||||
|
id
|
||||||
|
ins_co_nm
|
||||||
|
ins_ct_fn
|
||||||
|
ins_ct_ln
|
||||||
|
ins_ea
|
||||||
|
ins_ph1
|
||||||
|
inproduction
|
||||||
|
job_totals
|
||||||
|
joblines(
|
||||||
|
order_by: { line_no: asc }
|
||||||
|
where: {
|
||||||
|
part_type: {
|
||||||
|
_in: [
|
||||||
|
"PAN"
|
||||||
|
"PAC"
|
||||||
|
"PAR"
|
||||||
|
"PAL"
|
||||||
|
"PAA"
|
||||||
|
"PAM"
|
||||||
|
"PAP"
|
||||||
|
"PAG"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
removed: { _eq: false }
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
act_price
|
||||||
|
alt_partno
|
||||||
|
db_ref
|
||||||
|
id
|
||||||
|
line_desc
|
||||||
|
line_no
|
||||||
|
location
|
||||||
|
mod_lbr_ty
|
||||||
|
mod_lb_hrs
|
||||||
|
oem_partno
|
||||||
|
part_qty
|
||||||
|
part_type
|
||||||
|
prt_dsmk_m
|
||||||
|
status
|
||||||
|
}
|
||||||
|
lbr_adjustments
|
||||||
|
ownr_co_nm
|
||||||
|
ownr_ea
|
||||||
|
ownr_fn
|
||||||
|
ownr_ln
|
||||||
|
ownr_ph1
|
||||||
|
ownr_ph2
|
||||||
|
owner {
|
||||||
|
id
|
||||||
|
allow_text_message
|
||||||
|
preferred_contact
|
||||||
|
tax_number
|
||||||
|
}
|
||||||
|
owner_owing
|
||||||
|
plate_no
|
||||||
|
plate_st
|
||||||
|
po_number
|
||||||
|
production_vars
|
||||||
|
ro_number
|
||||||
|
scheduled_completion
|
||||||
|
scheduled_delivery
|
||||||
|
scheduled_in
|
||||||
|
special_coverage_policy
|
||||||
|
status
|
||||||
|
suspended
|
||||||
|
updated_at
|
||||||
|
vehicle {
|
||||||
|
id
|
||||||
|
jobs {
|
||||||
|
id
|
||||||
|
clm_no
|
||||||
|
ro_number
|
||||||
|
}
|
||||||
|
notes
|
||||||
|
plate_no
|
||||||
|
v_color
|
||||||
|
v_make_desc
|
||||||
|
v_model_desc
|
||||||
|
v_model_yr
|
||||||
|
}
|
||||||
|
vehicleid
|
||||||
|
v_color
|
||||||
|
v_make_desc
|
||||||
|
v_model_desc
|
||||||
|
v_model_yr
|
||||||
|
v_vin
|
||||||
|
voided
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|||||||
@@ -1,88 +1,75 @@
|
|||||||
import {useMutation, useQuery} from "@apollo/client";
|
//import {useMutation, useQuery } from "@apollo/client";
|
||||||
import {Button, Form, Layout, Result, Typography} from "antd";
|
import {Button, Form, Layout, Result, Typography} from "antd";
|
||||||
import React, {useState} from "react";
|
import axios from "axios";
|
||||||
|
import React, {useCallback, useEffect, useState} from "react";
|
||||||
import {useTranslation} from "react-i18next";
|
import {useTranslation} from "react-i18next";
|
||||||
|
import {connect} from "react-redux";
|
||||||
import {useParams} from "react-router-dom";
|
import {useParams} from "react-router-dom";
|
||||||
|
import {createStructuredSelector} from "reselect";
|
||||||
import AlertComponent from "../../components/alert/alert.component";
|
import AlertComponent from "../../components/alert/alert.component";
|
||||||
import ConfigFormComponents from "../../components/config-form-components/config-form-components.component";
|
import ConfigFormComponents from "../../components/config-form-components/config-form-components.component";
|
||||||
import LoadingSpinner from "../../components/loading-spinner/loading-spinner.component";
|
import LoadingSpinner from "../../components/loading-spinner/loading-spinner.component";
|
||||||
import {COMPLETE_SURVEY, QUERY_SURVEY} from "../../graphql/csi.queries";
|
|
||||||
import {connect} from "react-redux";
|
|
||||||
import {createStructuredSelector} from "reselect";
|
|
||||||
import {selectCurrentUser} from "../../redux/user/user.selectors";
|
import {selectCurrentUser} from "../../redux/user/user.selectors";
|
||||||
|
import {DateTimeFormat} from "./../../utils/DateFormatter";
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
currentUser: selectCurrentUser,
|
currentUser: selectCurrentUser,
|
||||||
});
|
});
|
||||||
const mapDispatchToProps = (dispatch) => ({
|
const mapDispatchToProps = (dispatch) => ({});
|
||||||
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
|
||||||
});
|
|
||||||
export default connect(mapStateToProps, mapDispatchToProps)(CsiContainerPage);
|
export default connect(mapStateToProps, mapDispatchToProps)(CsiContainerPage);
|
||||||
|
|
||||||
export function CsiContainerPage({currentUser}) {
|
export function CsiContainerPage({currentUser}) {
|
||||||
const {surveyId} = useParams();
|
const {surveyId} = useParams();
|
||||||
const [form] = Form.useForm();
|
const [form] = Form.useForm();
|
||||||
|
const [axiosResponse, setAxiosResponse] = useState(null);
|
||||||
const [submitting, setSubmitting] = useState({
|
const [submitting, setSubmitting] = useState({
|
||||||
loading: false,
|
loading: false,
|
||||||
submitted: false,
|
submitted: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
const {loading, error, data} = useQuery(QUERY_SURVEY, {
|
|
||||||
variables: {surveyId},
|
|
||||||
fetchPolicy: "network-only",
|
|
||||||
nextFetchPolicy: "network-only",
|
|
||||||
});
|
|
||||||
|
|
||||||
const {t} = useTranslation();
|
const {t} = useTranslation();
|
||||||
const [completeSurvey] = useMutation(COMPLETE_SURVEY);
|
|
||||||
if (loading) return <LoadingSpinner/>;
|
|
||||||
|
|
||||||
if (error || !!!data.csi_by_pk)
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<Result
|
|
||||||
status="error"
|
|
||||||
title={t("csi.errors.notfoundtitle")}
|
|
||||||
subTitle={t("csi.errors.notfoundsubtitle")}
|
|
||||||
>
|
|
||||||
{error ? (
|
|
||||||
<div>ERROR: {error.graphQLErrors.map((e) => e.message)}</div>
|
|
||||||
) : null}
|
|
||||||
</Result>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
|
|
||||||
const handleFinish = async (values) => {
|
const getAxiosData = useCallback(async () => {
|
||||||
setSubmitting({...submitting, loading: true});
|
try {
|
||||||
|
try {
|
||||||
|
window.$crisp.push(["do", "chat:hide"]);
|
||||||
|
} catch {
|
||||||
|
console.log("Unable to attach to crisp instance. ");
|
||||||
|
}
|
||||||
|
setSubmitting((prevSubmitting) => ({...prevSubmitting, loading: true}));
|
||||||
|
|
||||||
const result = await completeSurvey({
|
const response = await axios.post("/csi/lookup", {
|
||||||
variables: {
|
surveyId
|
||||||
surveyId,
|
|
||||||
survey: {
|
|
||||||
response: values,
|
|
||||||
valid: false,
|
|
||||||
completedon: new Date(),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
setSubmitting((prevSubmitting) => ({
|
||||||
if (!!!result.errors) {
|
...prevSubmitting,
|
||||||
setSubmitting({...submitting, loading: false, submitted: true});
|
|
||||||
} else {
|
|
||||||
setSubmitting({
|
|
||||||
...submitting,
|
|
||||||
loading: false,
|
loading: false,
|
||||||
error: JSON.stringify(result.errors),
|
}));
|
||||||
|
setAxiosResponse(response.data);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Something went wrong...: ${error.message}`);
|
||||||
|
console.dir({
|
||||||
|
stack: error?.stack,
|
||||||
|
message:
|
||||||
|
error?.message,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
}, [setAxiosResponse, surveyId]);
|
||||||
|
|
||||||
const {
|
useEffect(() => {
|
||||||
relateddata: {bodyshop, job},
|
getAxiosData().catch((err) =>
|
||||||
csiquestion: {config: csiquestions},
|
console.error(
|
||||||
} = data.csi_by_pk;
|
`Something went wrong fetching axios data: ${err.message || ""}`
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}, [getAxiosData]);
|
||||||
|
|
||||||
if (currentUser && currentUser.authorized)
|
// Return if authorized
|
||||||
|
if (currentUser && currentUser.authorized) {
|
||||||
return (
|
return (
|
||||||
<Layout
|
<Layout
|
||||||
style={{height: "100vh", display: "flex", flexDirection: "column"}}
|
style={{height: "100vh", display: "flex", flexDirection: "column"}}
|
||||||
@@ -94,11 +81,68 @@ export function CsiContainerPage({currentUser}) {
|
|||||||
/>
|
/>
|
||||||
</Layout>
|
</Layout>
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (submitting.loading) return <LoadingSpinner/>;
|
||||||
|
|
||||||
|
const handleFinish = async (values) => {
|
||||||
|
try {
|
||||||
|
setSubmitting({...submitting, loading: true, submitting: true});
|
||||||
|
const result = await axios.post("/csi/submit", {surveyId, values});
|
||||||
|
console.log("result", result);
|
||||||
|
if (!!!result.errors && result.data.update_csi.affected_rows > 0) {
|
||||||
|
setSubmitting({...submitting, loading: false, submitted: true});
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Something went wrong...: ${error.message}`);
|
||||||
|
console.dir({
|
||||||
|
stack: error?.stack,
|
||||||
|
message: error?.message,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!axiosResponse || axiosResponse.csi_by_pk === null) {
|
||||||
|
// Do something here , this is where you would return a loading box or something
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Layout style={{display: "flex", flexDirection: "column"}}>
|
||||||
|
<Layout.Content
|
||||||
|
style={{
|
||||||
|
backgroundColor: "#fff",
|
||||||
|
margin: "2em 4em",
|
||||||
|
padding: "2em",
|
||||||
|
overflowY: "auto",
|
||||||
|
textAlign: "center",
|
||||||
|
display: "flex",
|
||||||
|
justifyContent: "center",
|
||||||
|
alignItems: "center",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Form>
|
||||||
|
<Result
|
||||||
|
status="error"
|
||||||
|
title={t("csi.errors.notfoundtitle")}
|
||||||
|
subTitle={t("csi.errors.notfoundsubtitle")}
|
||||||
|
/>
|
||||||
|
</Form>
|
||||||
|
</Layout.Content>
|
||||||
|
<Layout.Footer>
|
||||||
|
{t("csi.labels.copyright")}{" "}
|
||||||
|
{t("csi.fields.surveyid", {surveyId: surveyId})}
|
||||||
|
</Layout.Footer>
|
||||||
|
</Layout>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
const {
|
||||||
|
relateddata: {bodyshop, job},
|
||||||
|
csiquestion: {config: csiquestions},
|
||||||
|
} = axiosResponse.csi_by_pk;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Layout
|
<Layout style={{display: "flex", flexDirection: "column"}}>
|
||||||
style={{height: "100vh", display: "flex", flexDirection: "column"}}
|
|
||||||
>
|
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
display: "flex",
|
display: "flex",
|
||||||
@@ -108,24 +152,35 @@ export function CsiContainerPage({currentUser}) {
|
|||||||
>
|
>
|
||||||
<div style={{display: "flex", alignItems: "center", margin: "2em"}}>
|
<div style={{display: "flex", alignItems: "center", margin: "2em"}}>
|
||||||
{bodyshop.logo_img_path && bodyshop.logo_img_path.src ? (
|
{bodyshop.logo_img_path && bodyshop.logo_img_path.src ? (
|
||||||
<img src={bodyshop.logo_img_path.src} alt="Logo"/>
|
<img src={bodyshop.logo_img_path.src} alt={bodyshop.shopname.concat("Logo")}
|
||||||
) : null}
|
height={bodyshop.logo_img_path.height}
|
||||||
<div style={{margin: "2em"}}>
|
width={bodyshop.logo_img_path.width}
|
||||||
<strong>{bodyshop.shopname || ""}</strong>
|
/>) : null}
|
||||||
<div>{`${bodyshop.address1 || ""}`}</div>
|
<div style={{margin: "2em", verticalAlign: "middle"}}>
|
||||||
<div>{`${bodyshop.address2 || ""}`}</div>
|
<Typography.Title level={4} style={{margin: 0}}>
|
||||||
<div>{`${bodyshop.city || ""} ${bodyshop.state || ""} ${
|
{bodyshop.shopname || ""}
|
||||||
bodyshop.zip_post || ""
|
</Typography.Title>
|
||||||
}`}</div>
|
<Typography.Paragraph style={{margin: 0}}>
|
||||||
|
{`${bodyshop.address1 || ""}${bodyshop.address2 ? ", " : ""}${
|
||||||
|
bodyshop.address2 || ""
|
||||||
|
}`.trim()}
|
||||||
|
</Typography.Paragraph>
|
||||||
|
<Typography.Paragraph style={{margin: 0}}>
|
||||||
|
{`${bodyshop.city || ""}${
|
||||||
|
bodyshop.city && bodyshop.state ? ", " : ""
|
||||||
|
}${bodyshop.state || ""} ${bodyshop.zip_post || ""}`.trim()}
|
||||||
|
</Typography.Paragraph>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<Typography.Title>{t("csi.labels.title")}</Typography.Title>
|
<Typography.Title>{t("csi.labels.title")}</Typography.Title>
|
||||||
<strong>{`Hi ${job.ownr_co_nm || job.ownr_fn || ""}!`}</strong>
|
<strong>{t("csi.labels.greeting", {
|
||||||
|
name: job.ownr_co_nm || job.ownr_fn || "",
|
||||||
|
})}</strong>
|
||||||
<Typography.Paragraph>
|
<Typography.Paragraph>
|
||||||
{`At ${
|
{t("csi.labels.intro", {
|
||||||
|
shopname:
|
||||||
bodyshop.shopname || ""
|
bodyshop.shopname || ""
|
||||||
}, we value your feedback. We would love to
|
})}
|
||||||
hear what you have to say. Please fill out the form below.`}
|
|
||||||
</Typography.Paragraph>
|
</Typography.Paragraph>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -158,21 +213,42 @@ export function CsiContainerPage({currentUser}) {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Form form={form} onFinish={handleFinish}>
|
<Form form={form} onFinish={handleFinish}>
|
||||||
<ConfigFormComponents componentList={csiquestions}/>
|
{axiosResponse.csi_by_pk.valid ? (
|
||||||
|
<><ConfigFormComponents componentList={csiquestions}/>
|
||||||
<Button
|
<Button
|
||||||
loading={submitting.loading}
|
loading={submitting.loading}
|
||||||
type="primary"
|
type="primary"
|
||||||
htmlType="submit"
|
htmlType="submit" style={{float: "right"}}
|
||||||
>
|
>
|
||||||
{t("general.actions.submit")}
|
{t("general.actions.submit")}
|
||||||
</Button>
|
</Button>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<Result
|
||||||
|
title={t("csi.errors.surveycompletetitle")}
|
||||||
|
status="warning"
|
||||||
|
subTitle={t("csi.errors.surveycompletesubtitle", {
|
||||||
|
date: DateTimeFormat(axiosResponse.csi_by_pk.completedon),
|
||||||
|
})}
|
||||||
|
/>
|
||||||
|
<Typography.Paragraph
|
||||||
|
type="secondary"
|
||||||
|
style={{textAlign: "center"}}
|
||||||
|
>
|
||||||
|
{t("csi.successes.submittedsub")}
|
||||||
|
</Typography.Paragraph>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</Form>
|
</Form>
|
||||||
</Layout.Content>
|
</Layout.Content>
|
||||||
)}
|
|
||||||
|
|
||||||
|
)}
|
||||||
<Layout.Footer>
|
<Layout.Footer>
|
||||||
{`Survey ID: ${surveyId}`}
|
{t("csi.labels.copyright")}{" "}
|
||||||
|
{t("csi.fields.surveyid", {surveyId: surveyId})}
|
||||||
</Layout.Footer>
|
</Layout.Footer>
|
||||||
</Layout>
|
</Layout>
|
||||||
);
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
import React, {useEffect} from "react";
|
import React, {useEffect} from "react";
|
||||||
import {useTranslation} from "react-i18next";
|
import {useTranslation} from "react-i18next";
|
||||||
import {connect} from "react-redux";
|
import {connect} from "react-redux";
|
||||||
|
import PartsQueueDetailCard from "../../components/parts-queue-card/parts-queue-card.component";
|
||||||
|
import PartsQueueList from "../../components/parts-queue-list/parts-queue.list.component";
|
||||||
import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component";
|
import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component";
|
||||||
import {setBreadcrumbs, setSelectedHeader,} from "../../redux/application/application.actions";
|
import {setBreadcrumbs, setSelectedHeader,} from "../../redux/application/application.actions";
|
||||||
import PartsQueuePage from "./parts-queue.page.component";
|
|
||||||
|
|
||||||
const mapDispatchToProps = (dispatch) => ({
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
|
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
|
||||||
@@ -23,7 +24,8 @@ export function PartsQueuePageContainer({setBreadcrumbs, setSelectedHeader}) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<RbacWrapper action="jobs:partsqueue">
|
<RbacWrapper action="jobs:partsqueue">
|
||||||
<PartsQueuePage/>
|
<PartsQueueList/>
|
||||||
|
<PartsQueueDetailCard/>
|
||||||
</RbacWrapper>
|
</RbacWrapper>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,20 +1,17 @@
|
|||||||
import {Col, Row} from "antd";
|
|
||||||
import {useQuery} from "@apollo/client";
|
import {useQuery} from "@apollo/client";
|
||||||
|
import {Col, Row} from "antd";
|
||||||
import React, {useEffect} from "react";
|
import React, {useEffect} from "react";
|
||||||
import {useTranslation} from "react-i18next";
|
import {useTranslation} from "react-i18next";
|
||||||
import {connect} from "react-redux";
|
import {connect} from "react-redux";
|
||||||
import {useLocation} from "react-router-dom";
|
|
||||||
import {createStructuredSelector} from "reselect";
|
import {createStructuredSelector} from "reselect";
|
||||||
import AlertComponent from "../../components/alert/alert.component";
|
import AlertComponent from "../../components/alert/alert.component";
|
||||||
import CsiResponseFormContainer from "../../components/csi-response-form/csi-response-form.container";
|
import CsiResponseFormContainer from "../../components/csi-response-form/csi-response-form.container";
|
||||||
import CsiResponseListPaginated
|
import CsiResponseListPaginated
|
||||||
from "../../components/csi-response-list-paginated/csi-response-list-paginated.component";
|
from "../../components/csi-response-list-paginated/csi-response-list-paginated.component";
|
||||||
import {QUERY_CSI_RESPONSE_PAGINATED} from "../../graphql/csi.queries";
|
|
||||||
import {setBreadcrumbs, setSelectedHeader} from "../../redux/application/application.actions";
|
|
||||||
import {selectBodyshop} from "../../redux/user/user.selectors";
|
|
||||||
import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component";
|
import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component";
|
||||||
import {pageLimit} from "../../utils/config";
|
import {QUERY_CSI_RESPONSE_PAGINATED} from "../../graphql/csi.queries";
|
||||||
import queryString from "query-string";
|
import {setBreadcrumbs, setSelectedHeader,} from "../../redux/application/application.actions";
|
||||||
|
import {selectBodyshop} from "../../redux/user/user.selectors";
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
bodyshop: selectBodyshop,
|
bodyshop: selectBodyshop,
|
||||||
@@ -25,29 +22,18 @@ const mapDispatchToProps = (dispatch) => ({
|
|||||||
setSelectedHeader: (key) => dispatch(setSelectedHeader(key)),
|
setSelectedHeader: (key) => dispatch(setSelectedHeader(key)),
|
||||||
});
|
});
|
||||||
|
|
||||||
export function ShopCsiContainer({bodyshop, setBreadcrumbs, setSelectedHeader}) {
|
export function ShopCsiContainer({
|
||||||
|
bodyshop,
|
||||||
|
setBreadcrumbs,
|
||||||
|
setSelectedHeader,
|
||||||
|
}) {
|
||||||
const {t} = useTranslation();
|
const {t} = useTranslation();
|
||||||
const searchParams = queryString.parse(useLocation().search);
|
|
||||||
const {page, sortcolumn, sortorder} = searchParams;
|
|
||||||
|
|
||||||
const {loading, error, data, refetch} = useQuery(
|
const {loading, error, data, refetch} = useQuery(
|
||||||
QUERY_CSI_RESPONSE_PAGINATED,
|
QUERY_CSI_RESPONSE_PAGINATED,
|
||||||
{
|
{
|
||||||
fetchPolicy: "network-only",
|
fetchPolicy: "network-only",
|
||||||
nextFetchPolicy: "network-only",
|
nextFetchPolicy: "network-only",
|
||||||
variables: {
|
|
||||||
offset: page ? (page - 1) * pageLimit : 0,
|
|
||||||
limit: pageLimit,
|
|
||||||
order: [
|
|
||||||
{
|
|
||||||
[sortcolumn || "completedon"]: sortorder
|
|
||||||
? sortorder === "descend"
|
|
||||||
? "desc_nulls_last"
|
|
||||||
: "asc"
|
|
||||||
: "desc_nulls_last",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -1923,6 +1923,7 @@
|
|||||||
"override_header": "Override estimate header on import?",
|
"override_header": "Override estimate header on import?",
|
||||||
"ownerassociation": "Owner Association",
|
"ownerassociation": "Owner Association",
|
||||||
"parts": "Parts",
|
"parts": "Parts",
|
||||||
|
"parts_lines": "Parts Lines",
|
||||||
"parts_received": "Parts Rec.",
|
"parts_received": "Parts Rec.",
|
||||||
"parts_tax_rates": "Parts Tax rates",
|
"parts_tax_rates": "Parts Tax rates",
|
||||||
"partsfilter": "Parts Only",
|
"partsfilter": "Parts Only",
|
||||||
|
|||||||
@@ -1923,6 +1923,7 @@
|
|||||||
"override_header": "¿Anular encabezado estimado al importar?",
|
"override_header": "¿Anular encabezado estimado al importar?",
|
||||||
"ownerassociation": "",
|
"ownerassociation": "",
|
||||||
"parts": "Partes",
|
"parts": "Partes",
|
||||||
|
"parts_lines": "",
|
||||||
"parts_received": "",
|
"parts_received": "",
|
||||||
"parts_tax_rates": "",
|
"parts_tax_rates": "",
|
||||||
"partsfilter": "",
|
"partsfilter": "",
|
||||||
|
|||||||
@@ -1923,6 +1923,7 @@
|
|||||||
"override_header": "Remplacer l'en-tête d'estimation à l'importation?",
|
"override_header": "Remplacer l'en-tête d'estimation à l'importation?",
|
||||||
"ownerassociation": "",
|
"ownerassociation": "",
|
||||||
"parts": "les pièces",
|
"parts": "les pièces",
|
||||||
|
"parts_lines": "",
|
||||||
"parts_received": "",
|
"parts_received": "",
|
||||||
"parts_tax_rates": "",
|
"parts_tax_rates": "",
|
||||||
"partsfilter": "",
|
"partsfilter": "",
|
||||||
|
|||||||
@@ -84,6 +84,7 @@ app.use('/adm', require("./server/routes/adminRoutes"));
|
|||||||
app.use('/tech', require("./server/routes/techRoutes"));
|
app.use('/tech', require("./server/routes/techRoutes"));
|
||||||
app.use('/intellipay', require("./server/routes/intellipayRoutes"));
|
app.use('/intellipay', require("./server/routes/intellipayRoutes"));
|
||||||
app.use('/cdk', require("./server/routes/cdkRoutes"));
|
app.use('/cdk', require("./server/routes/cdkRoutes"));
|
||||||
|
app.use('/csi', require("./server/routes/csiRoutes"));
|
||||||
app.use('/payroll', require("./server/routes/payrollRoutes"));
|
app.use('/payroll', require("./server/routes/payrollRoutes"));
|
||||||
|
|
||||||
// Default route for forbidden access
|
// Default route for forbidden access
|
||||||
|
|||||||
2
server/csi/csi.js
Normal file
2
server/csi/csi.js
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
exports.lookup = require("./lookup").default;
|
||||||
|
exports.submit = require("./submit").default;
|
||||||
24
server/csi/lookup.js
Normal file
24
server/csi/lookup.js
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
const path = require("path");
|
||||||
|
const queries = require("../graphql-client/queries");
|
||||||
|
const logger = require("../utils/logger");
|
||||||
|
require("dotenv").config({
|
||||||
|
path: path.resolve(
|
||||||
|
process.cwd(),
|
||||||
|
`.env.${process.env.NODE_ENV || "development"}`
|
||||||
|
),
|
||||||
|
});
|
||||||
|
|
||||||
|
const client = require("../graphql-client/graphql-client").client;
|
||||||
|
|
||||||
|
exports.default = async (req, res) => {
|
||||||
|
try {
|
||||||
|
logger.log("csi-surveyID-lookup", "DEBUG", "csi", req.body.surveyId, null);
|
||||||
|
const gql_response = await client.request(queries.QUERY_SURVEY, {
|
||||||
|
surveyId: req.body.surveyId,
|
||||||
|
});
|
||||||
|
res.status(200).json(gql_response);
|
||||||
|
} catch (error) {
|
||||||
|
logger.log("csi-surveyID-lookup", "ERROR", "csi", req.body.surveyId, error);
|
||||||
|
res.status(400).json(error);
|
||||||
|
}
|
||||||
|
};
|
||||||
29
server/csi/submit.js
Normal file
29
server/csi/submit.js
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
const path = require("path");
|
||||||
|
const queries = require("../graphql-client/queries");
|
||||||
|
const logger = require("../utils/logger");
|
||||||
|
require("dotenv").config({
|
||||||
|
path: path.resolve(
|
||||||
|
process.cwd(),
|
||||||
|
`.env.${process.env.NODE_ENV || "development"}`
|
||||||
|
),
|
||||||
|
});
|
||||||
|
|
||||||
|
const client = require("../graphql-client/graphql-client").client;
|
||||||
|
|
||||||
|
exports.default = async (req, res) => {
|
||||||
|
try {
|
||||||
|
logger.log("csi-surveyID-submit", "DEBUG", "csi", req.body.surveyId, null);
|
||||||
|
const gql_response = await client.request(queries.COMPLETE_SURVEY, {
|
||||||
|
surveyId: req.body.surveyId,
|
||||||
|
survey: {
|
||||||
|
response: req.body.values,
|
||||||
|
valid: false,
|
||||||
|
completedon: new Date(),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
res.status(200).json(gql_response);
|
||||||
|
} catch (error) {
|
||||||
|
logger.log("csi-surveyID-submit", "ERROR", "csi", req.body.surveyId, error);
|
||||||
|
res.status(400).json(error);
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -2272,3 +2272,124 @@ exports.INSERT_TIME_TICKETS = `mutation INSERT_TIMETICKETS($timetickets: [timeti
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports.QUERY_JOB_PAYROLL_DATA = `query QUERY_JOB_PAYROLL_DATA($id: uuid!) {
|
||||||
|
jobs_by_pk(id: $id) {
|
||||||
|
bodyshop{
|
||||||
|
id
|
||||||
|
md_responsibility_centers
|
||||||
|
md_tasks_presets
|
||||||
|
employee_teams{
|
||||||
|
id
|
||||||
|
name
|
||||||
|
employee_team_members{
|
||||||
|
id
|
||||||
|
employee{
|
||||||
|
id
|
||||||
|
first_name
|
||||||
|
last_name
|
||||||
|
}
|
||||||
|
percentage
|
||||||
|
labor_rates
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
timetickets{
|
||||||
|
id
|
||||||
|
employeeid
|
||||||
|
rate
|
||||||
|
productivehrs
|
||||||
|
actualhrs
|
||||||
|
ciecacode
|
||||||
|
}
|
||||||
|
lbr_adjustments
|
||||||
|
ro_number
|
||||||
|
id
|
||||||
|
job_totals
|
||||||
|
rate_la1
|
||||||
|
rate_la2
|
||||||
|
rate_la3
|
||||||
|
rate_la4
|
||||||
|
rate_laa
|
||||||
|
rate_lab
|
||||||
|
rate_lad
|
||||||
|
rate_lae
|
||||||
|
rate_laf
|
||||||
|
rate_lag
|
||||||
|
rate_lam
|
||||||
|
rate_lar
|
||||||
|
rate_las
|
||||||
|
rate_lau
|
||||||
|
rate_ma2s
|
||||||
|
rate_ma2t
|
||||||
|
rate_ma3s
|
||||||
|
rate_mabl
|
||||||
|
rate_macs
|
||||||
|
rate_mahw
|
||||||
|
rate_mapa
|
||||||
|
rate_mash
|
||||||
|
rate_matd
|
||||||
|
status
|
||||||
|
materials
|
||||||
|
completed_tasks
|
||||||
|
joblines(where: { removed: { _eq: false } }){
|
||||||
|
id
|
||||||
|
line_no
|
||||||
|
unq_seq
|
||||||
|
line_ind
|
||||||
|
line_desc
|
||||||
|
part_type
|
||||||
|
line_ref
|
||||||
|
oem_partno
|
||||||
|
db_price
|
||||||
|
act_price
|
||||||
|
part_qty
|
||||||
|
mod_lbr_ty
|
||||||
|
db_hrs
|
||||||
|
mod_lb_hrs
|
||||||
|
lbr_op
|
||||||
|
lbr_amt
|
||||||
|
op_code_desc
|
||||||
|
status
|
||||||
|
notes
|
||||||
|
location
|
||||||
|
tax_part
|
||||||
|
db_ref
|
||||||
|
manual_line
|
||||||
|
prt_dsmk_p
|
||||||
|
prt_dsmk_m
|
||||||
|
misc_amt
|
||||||
|
misc_tax
|
||||||
|
assigned_team
|
||||||
|
convertedtolbr
|
||||||
|
convertedtolbr_data
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}`;
|
||||||
|
|
||||||
|
exports.INSERT_TIME_TICKETS = `mutation INSERT_TIMETICKETS($timetickets: [timetickets_insert_input!]!) {
|
||||||
|
insert_timetickets(objects: $timetickets) {
|
||||||
|
affected_rows
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports.QUERY_SURVEY = `query QUERY_SURVEY($surveyId: uuid!) {
|
||||||
|
csi_by_pk(id: $surveyId) {
|
||||||
|
completedon
|
||||||
|
csiquestion {
|
||||||
|
id
|
||||||
|
config
|
||||||
|
}
|
||||||
|
id
|
||||||
|
relateddata
|
||||||
|
valid
|
||||||
|
validuntil
|
||||||
|
}
|
||||||
|
}`;
|
||||||
|
|
||||||
|
exports.COMPLETE_SURVEY = `mutation COMPLETE_SURVEY($surveyId: uuid!, $survey: csi_set_input) {
|
||||||
|
update_csi(where: { id: { _eq: $surveyId } }, _set: $survey) {
|
||||||
|
affected_rows
|
||||||
|
}
|
||||||
|
}`;
|
||||||
|
|||||||
8
server/routes/csiRoutes.js
Normal file
8
server/routes/csiRoutes.js
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
const express = require("express");
|
||||||
|
const router = express.Router();
|
||||||
|
const {lookup, submit} = require("../csi/csi");
|
||||||
|
|
||||||
|
router.post("/lookup", lookup);
|
||||||
|
router.post("/submit", submit);
|
||||||
|
|
||||||
|
module.exports = router;
|
||||||
Reference in New Issue
Block a user