- Fix the page layout, the footer was in the content section causing it not to remain at the bottom and just reside 'at the bottom' of the content section. Also added a 100% on the outer container height (100vh) so the grey background fills the page

Signed-off-by: Dave Richer <dave@imexsystems.ca>
This commit is contained in:
Dave Richer
2024-01-16 15:32:35 -05:00
9 changed files with 718 additions and 681 deletions

View File

@@ -1,276 +1,282 @@
import { SyncOutlined } from "@ant-design/icons"; import {SyncOutlined} from "@ant-design/icons";
import { Button, Card, Input, Space, Table, Typography } from "antd"; import {Button, Card, Input, Space, Table, Typography} from "antd";
import axios from "axios"; import axios from "axios";
import _ from "lodash"; import _ from "lodash";
import queryString from "query-string"; import queryString from "query-string";
import React, { useEffect, useState } from "react"; import React, {useEffect, useState} from "react";
import { useTranslation } from "react-i18next"; import {useTranslation} from "react-i18next";
import { connect } from "react-redux"; import {connect} from "react-redux";
import { Link, useNavigate, useLocation } from "react-router-dom"; import {Link, useLocation, useNavigate} from "react-router-dom";
import { createStructuredSelector } from "reselect"; import {createStructuredSelector} from "reselect";
import { selectBodyshop } from "../../redux/user/user.selectors"; import {selectBodyshop} from "../../redux/user/user.selectors";
import CurrencyFormatter from "../../utils/CurrencyFormatter"; import CurrencyFormatter from "../../utils/CurrencyFormatter";
import {pageLimit} from "../../utils/config";
import useLocalStorage from "../../utils/useLocalStorage";
import StartChatButton from "../chat-open-button/chat-open-button.component"; import StartChatButton from "../chat-open-button/chat-open-button.component";
import OwnerNameDisplay from "../owner-name-display/owner-name-display.component"; import OwnerNameDisplay from "../owner-name-display/owner-name-display.component";
import {pageLimit} from "../../utils/config";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
//currentUser: selectCurrentUser //currentUser: selectCurrentUser
bodyshop: selectBodyshop, bodyshop: selectBodyshop,
}); });
const mapDispatchToProps = (dispatch) => ({ const mapDispatchToProps = (dispatch) => ({
//setUserLanguage: language => dispatch(setUserLanguage(language)) //setUserLanguage: language => dispatch(setUserLanguage(language))
}); });
export function JobsList({ bodyshop, refetch, loading, jobs, total }) { export function JobsList({bodyshop, refetch, loading, jobs, total}) {
const search = queryString.parse(useLocation().search); const search = queryString.parse(useLocation().search);
const [openSearchResults, setOpenSearchResults] = useState([]); const [openSearchResults, setOpenSearchResults] = useState([]);
const [searchLoading, setSearchLoading] = useState(false); const [searchLoading, setSearchLoading] = useState(false);
const { page, sortcolumn, sortorder } = search; const [filter, setFilter] = useLocalStorage("filter_jobs_all", null);
const history = useNavigate(); const {page, sortcolumn, sortorder} = search;
const history = useNavigate();
const { t } = useTranslation(); 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",
sorter: true, //(a, b) => alphaSort(a.ro_number, b.ro_number), sorter: true, //(a, b) => alphaSort(a.ro_number, b.ro_number),
sortOrder: sortcolumn === "ro_number" && sortorder, sortOrder: sortcolumn === "ro_number" && sortorder,
render: (text, record) => ( render: (text, record) => (
<Link to={"/manage/jobs/" + record.id}> <Link to={"/manage/jobs/" + record.id}>
{record.ro_number || t("general.labels.na")} {record.ro_number || t("general.labels.na")}
</Link> </Link>
), ),
}, },
{ {
title: t("jobs.fields.owner"), title: t("jobs.fields.owner"),
dataIndex: "ownr_ln", dataIndex: "ownr_ln",
key: "ownr_ln", key: "ownr_ln",
ellipsis: true, ellipsis: true,
//sorter: true, // (a, b) => alphaSort(a.ownr_ln, b.ownr_ln), //sorter: true, // (a, b) => alphaSort(a.ownr_ln, b.ownr_ln),
//sortOrder: sortcolumn === "ownr_ln" && sortorder, //sortOrder: sortcolumn === "ownr_ln" && sortorder,
render: (text, record) => { render: (text, record) => {
return record.ownerid ? ( return record.ownerid ? (
<Link to={"/manage/owners/" + record.ownerid}> <Link to={"/manage/owners/" + record.ownerid}>
<OwnerNameDisplay ownerObject={record} /> <OwnerNameDisplay ownerObject={record}/>
</Link> </Link>
) : ( ) : (
<span> <span>
<OwnerNameDisplay ownerObject={record} /> <OwnerNameDisplay ownerObject={record}/>
</span> </span>
); );
}, },
}, },
{ {
title: t("jobs.fields.ownr_ph1"), title: t("jobs.fields.ownr_ph1"),
dataIndex: "ownr_ph1", dataIndex: "ownr_ph1",
key: "ownr_ph1", key: "ownr_ph1",
ellipsis: true, ellipsis: true,
render: (text, record) => ( render: (text, record) => (
<StartChatButton phone={record.ownr_ph1} jobid={record.id} /> <StartChatButton phone={record.ownr_ph1} jobid={record.id}/>
), ),
}, },
{ {
title: t("jobs.fields.ownr_ph2"), title: t("jobs.fields.ownr_ph2"),
dataIndex: "ownr_ph2", dataIndex: "ownr_ph2",
key: "ownr_ph2", key: "ownr_ph2",
ellipsis: true, ellipsis: true,
render: (text, record) => ( render: (text, record) => (
<StartChatButton phone={record.ownr_ph2} jobid={record.id} /> <StartChatButton phone={record.ownr_ph2} jobid={record.id}/>
), ),
}, },
{ {
title: t("jobs.fields.status"), title: t("jobs.fields.status"),
dataIndex: "status", dataIndex: "status",
key: "status", key: "status",
ellipsis: true, ellipsis: true,
sorter: true, // (a, b) => alphaSort(a.status, b.status), sorter: true, // (a, b) => alphaSort(a.status, b.status),
sortOrder: sortcolumn === "status" && sortorder, sortOrder: sortcolumn === "status" && sortorder,
render: (text, record) => { render: (text, record) => {
return record.status || t("general.labels.na"); return record.status || t("general.labels.na");
}, },
filters: bodyshop.md_ro_statuses.statuses.map((s) => { filteredValue: filter?.status || null,
return { text: s, value: [s] }; filters: bodyshop.md_ro_statuses.statuses.map((s) => {
}), return {text: s, value: [s]};
onFilter: (value, record) => value.includes(record.status), }),
}, onFilter: (value, record) => value.includes(record.status),
},
{ {
title: t("jobs.fields.vehicle"), title: t("jobs.fields.vehicle"),
dataIndex: "vehicle", dataIndex: "vehicle",
key: "vehicle", key: "vehicle",
ellipsis: true, ellipsis: true,
render: (text, record) => { render: (text, record) => {
return record.vehicleid ? ( return record.vehicleid ? (
<Link to={"/manage/vehicles/" + record.vehicleid}> <Link to={"/manage/vehicles/" + record.vehicleid}>
{`${record.v_model_yr || ""} ${record.v_make_desc || ""} ${ {`${record.v_model_yr || ""} ${record.v_make_desc || ""} ${
record.v_model_desc || "" record.v_model_desc || ""
}`} }`}
</Link> </Link>
) : ( ) : (
<span>{`${record.v_model_yr || ""} ${record.v_make_desc || ""} ${ <span>{`${record.v_model_yr || ""} ${record.v_make_desc || ""} ${
record.v_model_desc || "" record.v_model_desc || ""
}`}</span> }`}</span>
); );
}, },
}, },
{ {
title: t("vehicles.fields.plate_no"), title: t("vehicles.fields.plate_no"),
dataIndex: "plate_no", dataIndex: "plate_no",
key: "plate_no", key: "plate_no",
ellipsis: true, ellipsis: true,
sorter: true, //(a, b) => alphaSort(a.plate_no, b.plate_no), sorter: true, //(a, b) => alphaSort(a.plate_no, b.plate_no),
sortOrder: sortcolumn === "plate_no" && sortorder, sortOrder: sortcolumn === "plate_no" && sortorder,
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"), title: t("jobs.fields.clm_no"),
dataIndex: "clm_no", dataIndex: "clm_no",
key: "clm_no", key: "clm_no",
ellipsis: true, ellipsis: true,
sorter: true, //(a, b) => alphaSort(a.clm_no, b.clm_no), sorter: true, //(a, b) => alphaSort(a.clm_no, b.clm_no),
sortOrder: sortcolumn === "clm_no" && sortorder, sortOrder: sortcolumn === "clm_no" && sortorder,
render: (text, record) => render: (text, record) =>
`${record.clm_no || ""}${ `${record.clm_no || ""}${
record.po_number ? ` (PO: ${record.po_number})` : "" record.po_number ? ` (PO: ${record.po_number})` : ""
}`, }`,
}, },
{ {
title: t("jobs.fields.ins_co_nm"), title: t("jobs.fields.ins_co_nm"),
dataIndex: "ins_co_nm", dataIndex: "ins_co_nm",
key: "ins_co_nm", key: "ins_co_nm",
ellipsis: true, ellipsis: true,
}, },
{ {
title: t("jobs.fields.clm_total"), title: t("jobs.fields.clm_total"),
dataIndex: "clm_total", dataIndex: "clm_total",
key: "clm_total", key: "clm_total",
sorter: true, //(a, b) => a.clm_total - b.clm_total, sorter: true, //(a, b) => a.clm_total - b.clm_total,
sortOrder: sortcolumn === "clm_total" && sortorder, sortOrder: sortcolumn === "clm_total" && sortorder,
render: (text, record) => { render: (text, record) => {
return record.clm_total ? ( return record.clm_total ? (
<CurrencyFormatter>{record.clm_total}</CurrencyFormatter> <CurrencyFormatter>{record.clm_total}</CurrencyFormatter>
) : ( ) : (
t("general.labels.unknown") t("general.labels.unknown")
); );
}, },
}, },
{ {
title: t("jobs.fields.owner_owing"), title: t("jobs.fields.owner_owing"),
dataIndex: "owner_owing", dataIndex: "owner_owing",
key: "owner_owing", key: "owner_owing",
render: (text, record) => ( render: (text, record) => (
<CurrencyFormatter>{record.owner_owing}</CurrencyFormatter> <CurrencyFormatter>{record.owner_owing}</CurrencyFormatter>
), ),
}, },
{ {
title: t("jobs.fields.comment"), title: t("jobs.fields.comment"),
dataIndex: "comment", dataIndex: "comment",
key: "comment", key: "comment",
ellipsis: true, ellipsis: true,
}, },
]; ];
const handleTableChange = (pagination, filters, sorter) => { const handleTableChange = (pagination, filters, sorter) => {
search.page = pagination.current; search.page = pagination.current;
search.sortcolumn = sorter.column && sorter.column.key; search.sortcolumn = sorter.column && sorter.column.key;
search.sortorder = sorter.order; search.sortorder = sorter.order;
if (filters.status) { if (filters.status) {
search.statusFilters = JSON.stringify(_.flattenDeep(filters.status)); search.statusFilters = JSON.stringify(_.flattenDeep(filters.status));
} else { } else {
delete search.statusFilters; delete search.statusFilters;
}
history({ search: queryString.stringify(search) });
};
useEffect(() => {
if (search.search && search.search.trim() !== "") {
searchJobs();
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
async function searchJobs(value) {
try {
setSearchLoading(true);
const searchData = await axios.post("/search", {
search: value || search.search,
index: "jobs",
});
setOpenSearchResults(searchData.data.hits.hits.map((s) => s._source));
} catch (error) {
console.log("Error while fetching search results", error);
} finally {
setSearchLoading(false);
}
}
return (
<Card
extra={
<Space wrap>
{search.search && (
<>
<Typography.Title level={4}>
{t("general.labels.searchresults", { search: search.search })}
</Typography.Title>
<Button
onClick={() => {
delete search.search;
delete search.page;
history({ search: queryString.stringify(search) });
}}
>
{t("general.actions.clear")}
</Button>
</>
)}
<Button onClick={() => refetch()}>
<SyncOutlined />
</Button>
<Input.Search
placeholder={search.search || t("general.labels.search")}
onSearch={(value) => {
search.search = value;
history({ search: queryString.stringify(search) });
searchJobs(value);
}}
loading={loading || searchLoading}
enterButton
/>
</Space>
}
>
<Table
loading={loading || searchLoading}
pagination={
search?.search
? {
pageSize: pageLimit,
showSizeChanger: false,
}
: {
pageSize: pageLimit,
current: parseInt(page || 1),
total: total,
showSizeChanger: false,
}
} }
columns={columns} setFilter(filters);
rowKey="id" history({search: queryString.stringify(search)});
dataSource={search?.search ? openSearchResults : jobs} };
onChange={handleTableChange}
/> useEffect(() => {
</Card> if (search.search && search.search.trim() !== "") {
); searchJobs();
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
async function searchJobs(value) {
try {
setSearchLoading(true);
const searchData = await axios.post("/search", {
search: value || search.search,
index: "jobs",
});
setOpenSearchResults(searchData.data.hits.hits.map((s) => s._source));
} catch (error) {
console.log("Error while fetching search results", error);
} finally {
setSearchLoading(false);
}
}
return (
<Card
extra={
<Space wrap>
{search.search && (
<>
<Typography.Title level={4}>
{t("general.labels.searchresults", {search: search.search})}
</Typography.Title>
<Button
onClick={() => {
delete search.search;
delete search.page;
history({search: queryString.stringify(search)});
}}
>
{t("general.actions.clear")}
</Button>
</>
)}
<Button onClick={() => refetch()}>
<SyncOutlined/>
</Button>
<Input.Search
placeholder={search.search || t("general.labels.search")}
onSearch={(value) => {
search.search = value;
history({search: queryString.stringify(search)});
searchJobs(value);
}}
loading={loading || searchLoading}
enterButton
/>
</Space>
}
>
<Table
loading={loading || searchLoading}
pagination={
search?.search
? {
pageSize: pageLimit,
showSizeChanger: false,
}
: {
pageSize: pageLimit,
current: parseInt(page || 1),
total: total,
showSizeChanger: false,
}
}
columns={columns}
rowKey="id"
dataSource={search?.search ? openSearchResults : jobs}
onChange={handleTableChange}
/>
</Card>
);
} }
export default connect(mapStateToProps, mapDispatchToProps)(JobsList); export default connect(mapStateToProps, mapDispatchToProps)(JobsList);

View File

@@ -1,22 +1,18 @@
import { import {BranchesOutlined, ExclamationCircleFilled, PauseCircleOutlined, SyncOutlined,} from "@ant-design/icons";
SyncOutlined, import {useQuery} from "@apollo/client";
ExclamationCircleFilled, import {Button, Card, Grid, Input, Space, Table, Tooltip} from "antd";
PauseCircleOutlined,
BranchesOutlined,
} from "@ant-design/icons";
import { useQuery } from "@apollo/client";
import { Button, Card, Grid, Input, Space, Table, Tooltip } from "antd";
import queryString from "query-string"; import queryString from "query-string";
import React, { useState } from "react"; import React, {useState} from "react";
import { useTranslation } from "react-i18next"; import {useTranslation} from "react-i18next";
import { connect } from "react-redux"; import {connect} from "react-redux";
import { Link, useNavigate, useLocation } from "react-router-dom"; import {Link, useLocation, useNavigate} from "react-router-dom";
import { createStructuredSelector } from "reselect"; import {createStructuredSelector} from "reselect";
import { QUERY_ALL_ACTIVE_JOBS } from "../../graphql/jobs.queries"; import {QUERY_ALL_ACTIVE_JOBS} from "../../graphql/jobs.queries";
import { selectBodyshop } from "../../redux/user/user.selectors"; import {selectBodyshop} from "../../redux/user/user.selectors";
import { onlyUnique } from "../../utils/arrayHelper"; import {onlyUnique} from "../../utils/arrayHelper";
import CurrencyFormatter from "../../utils/CurrencyFormatter"; import CurrencyFormatter from "../../utils/CurrencyFormatter";
import { alphaSort } from "../../utils/sorters"; import {alphaSort, statusSort} from "../../utils/sorters";
import useLocalStorage from "../../utils/useLocalStorage";
import AlertComponent from "../alert/alert.component"; import AlertComponent from "../alert/alert.component";
import ChatOpenButton from "../chat-open-button/chat-open-button.component"; import ChatOpenButton from "../chat-open-button/chat-open-button.component";
import OwnerNameDisplay from "../owner-name-display/owner-name-display.component"; import OwnerNameDisplay from "../owner-name-display/owner-name-display.component";
@@ -25,13 +21,13 @@ const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop, bodyshop: selectBodyshop,
}); });
export function JobsList({ bodyshop }) { export function JobsList({bodyshop}) {
const searchParams = queryString.parse(useLocation().search); const searchParams = queryString.parse(useLocation().search);
const { selected } = searchParams; const {selected} = searchParams;
const selectedBreakpoint = Object.entries(Grid.useBreakpoint()) const selectedBreakpoint = Object.entries(Grid.useBreakpoint())
.filter((screen) => !!screen[1]) .filter((screen) => !!screen[1])
.slice(-1)[0]; .slice(-1)[0];
const { loading, error, data, refetch } = useQuery(QUERY_ALL_ACTIVE_JOBS, { const {loading, error, data, refetch} = useQuery(QUERY_ALL_ACTIVE_JOBS, {
variables: { variables: {
statuses: bodyshop.md_ro_statuses.active_statuses || ["Open", "Open*"], statuses: bodyshop.md_ro_statuses.active_statuses || ["Open", "Open*"],
}, },
@@ -39,16 +35,14 @@ export function JobsList({ bodyshop }) {
nextFetchPolicy: "network-only", nextFetchPolicy: "network-only",
}); });
const [state, setState] = useState({ const [state, setState] = useState({sortedInfo: {}});
sortedInfo: {}, const [filter, setFilter] = useLocalStorage("filter_jobs_list", null);
filteredInfo: { text: "" },
});
const { t } = useTranslation(); const {t} = useTranslation();
const history = useNavigate(); const history = useNavigate();
const [searchText, setSearchText] = useState(""); const [searchText, setSearchText] = useState("");
if (error) return <AlertComponent message={error.message} type="error" />; if (error) return <AlertComponent message={error.message} type="error"/>;
const jobs = data const jobs = data
? searchText === "" ? searchText === ""
@@ -91,7 +85,8 @@ export function JobsList({ bodyshop }) {
: []; : [];
const handleTableChange = (pagination, filters, sorter) => { const handleTableChange = (pagination, filters, sorter) => {
setState({ ...state, filteredInfo: filters, sortedInfo: sorter }); setState({...state, sortedInfo: sorter});
setFilter(filters);
}; };
const handleOnRowClick = (record) => { const handleOnRowClick = (record) => {
@@ -126,14 +121,14 @@ export function JobsList({ bodyshop }) {
<Space> <Space>
{record.ro_number || t("general.labels.na")} {record.ro_number || t("general.labels.na")}
{record.production_vars && record.production_vars.alert ? ( {record.production_vars && record.production_vars.alert ? (
<ExclamationCircleFilled className="production-alert" /> <ExclamationCircleFilled className="production-alert"/>
) : null} ) : null}
{record.suspended && ( {record.suspended && (
<PauseCircleOutlined style={{ color: "orangered" }} /> <PauseCircleOutlined style={{color: "orangered"}}/>
)} )}
{record.iouparent && ( {record.iouparent && (
<Tooltip title={t("jobs.labels.iou")}> <Tooltip title={t("jobs.labels.iou")}>
<BranchesOutlined style={{ color: "orangered" }} /> <BranchesOutlined style={{color: "orangered"}}/>
</Tooltip> </Tooltip>
)} )}
</Space> </Space>
@@ -156,11 +151,11 @@ export function JobsList({ bodyshop }) {
to={"/manage/owners/" + record.ownerid} to={"/manage/owners/" + record.ownerid}
onClick={(e) => e.stopPropagation()} onClick={(e) => e.stopPropagation()}
> >
<OwnerNameDisplay ownerObject={record} /> <OwnerNameDisplay ownerObject={record}/>
</Link> </Link>
) : ( ) : (
<span> <span>
<OwnerNameDisplay ownerObject={record} /> <OwnerNameDisplay ownerObject={record}/>
</span> </span>
); );
}, },
@@ -172,7 +167,7 @@ export function JobsList({ bodyshop }) {
ellipsis: true, ellipsis: true,
responsive: ["md"], responsive: ["md"],
render: (text, record) => ( render: (text, record) => (
<ChatOpenButton phone={record.ownr_ph1} jobid={record.id} /> <ChatOpenButton phone={record.ownr_ph1} jobid={record.id}/>
), ),
}, },
{ {
@@ -182,7 +177,7 @@ export function JobsList({ bodyshop }) {
ellipsis: true, ellipsis: true,
responsive: ["md"], responsive: ["md"],
render: (text, record) => ( render: (text, record) => (
<ChatOpenButton phone={record.ownr_ph2} jobid={record.id} /> <ChatOpenButton phone={record.ownr_ph2} jobid={record.id}/>
), ),
}, },
@@ -195,6 +190,7 @@ export function JobsList({ bodyshop }) {
sorter: (a, b) => alphaSort(a.status, b.status), sorter: (a, b) => alphaSort(a.status, b.status),
sortOrder: sortOrder:
state.sortedInfo.columnKey === "status" && state.sortedInfo.order, state.sortedInfo.columnKey === "status" && state.sortedInfo.order,
filteredValue: filter?.status || null,
filters: filters:
(jobs && (jobs &&
jobs jobs
@@ -205,7 +201,14 @@ export function JobsList({ bodyshop }) {
text: s || "No Status*", text: s || "No Status*",
value: [s], value: [s],
}; };
})) || })
.sort((a, b) =>
statusSort(
a.text,
b.text,
bodyshop.md_ro_statuses.active_statuses
)
)) ||
[], [],
onFilter: (value, record) => value.includes(record.status), onFilter: (value, record) => value.includes(record.status),
}, },
@@ -262,6 +265,7 @@ export function JobsList({ bodyshop }) {
dataIndex: "ins_co_nm", dataIndex: "ins_co_nm",
key: "ins_co_nm", key: "ins_co_nm",
ellipsis: true, ellipsis: true,
filteredValue: filter?.ins_co_nm || null,
filters: filters:
(jobs && (jobs &&
jobs jobs
@@ -269,10 +273,11 @@ export function JobsList({ bodyshop }) {
.filter(onlyUnique) .filter(onlyUnique)
.map((s) => { .map((s) => {
return { return {
text: s, text: s || "No Ins. Co.*",
value: [s], value: [s],
}; };
})) || })
.sort((a, b) => alphaSort(a.text, b.text))) ||
[], [],
onFilter: (value, record) => value.includes(record.ins_co_nm), onFilter: (value, record) => value.includes(record.ins_co_nm),
responsive: ["md"], responsive: ["md"],
@@ -298,6 +303,7 @@ export function JobsList({ bodyshop }) {
ellipsis: true, ellipsis: true,
responsive: ["xl"], responsive: ["xl"],
filterSearch: true, filterSearch: true,
filteredValue: filter?.estimator || null,
filters: filters:
(jobs && (jobs &&
jobs jobs
@@ -305,10 +311,11 @@ export function JobsList({ bodyshop }) {
.filter(onlyUnique) .filter(onlyUnique)
.map((s) => { .map((s) => {
return { return {
text: s || "N/A", text: s || "No Estimator*",
value: [s], value: [s],
}; };
})) || })
.sort((a, b) => alphaSort(a.text, b.text))) ||
[], [],
onFilter: (value, record) => onFilter: (value, record) =>
value.includes( value.includes(
@@ -350,7 +357,7 @@ export function JobsList({ bodyshop }) {
extra={ extra={
<Space wrap> <Space wrap>
<Button onClick={() => refetch()}> <Button onClick={() => refetch()}>
<SyncOutlined /> <SyncOutlined/>
</Button> </Button>
<Input.Search <Input.Search
placeholder={t("general.labels.search")} placeholder={t("general.labels.search")}
@@ -365,7 +372,7 @@ export function JobsList({ bodyshop }) {
> >
<Table <Table
loading={loading} loading={loading}
pagination={{ defaultPageSize: 50 }} pagination={{defaultPageSize: 50}}
columns={columns} columns={columns}
rowKey="id" rowKey="id"
dataSource={jobs} dataSource={jobs}

View File

@@ -1,408 +1,409 @@
import { import {BranchesOutlined, ExclamationCircleFilled, PauseCircleOutlined, SyncOutlined,} from "@ant-design/icons";
BranchesOutlined, import {useQuery} from "@apollo/client";
ExclamationCircleFilled, import {Button, Card, Grid, Input, Space, Table, Tooltip} from "antd";
PauseCircleOutlined,
SyncOutlined,
} from "@ant-design/icons";
import { useQuery } from "@apollo/client";
import { Button, Card, Grid, Input, Space, Table, Tooltip } from "antd";
import queryString from "query-string"; import queryString from "query-string";
import React, { useMemo, useState } from "react"; import React, {useMemo, 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, useNavigate, useLocation } from "react-router-dom"; import {Link, useLocation, useNavigate} from "react-router-dom";
import { createStructuredSelector } from "reselect"; import {createStructuredSelector} from "reselect";
import { QUERY_ALL_ACTIVE_JOBS } from "../../graphql/jobs.queries"; import {QUERY_ALL_ACTIVE_JOBS} from "../../graphql/jobs.queries";
import { selectBodyshop } from "../../redux/user/user.selectors"; import {selectBodyshop} from "../../redux/user/user.selectors";
import CurrencyFormatter from "../../utils/CurrencyFormatter"; import CurrencyFormatter from "../../utils/CurrencyFormatter";
import { onlyUnique } from "../../utils/arrayHelper"; import {onlyUnique} from "../../utils/arrayHelper";
import { alphaSort } from "../../utils/sorters"; import {pageLimit} from "../../utils/config";
import {alphaSort, statusSort} from "../../utils/sorters";
import useLocalStorage from "../../utils/useLocalStorage";
import AlertComponent from "../alert/alert.component"; import AlertComponent from "../alert/alert.component";
import ChatOpenButton from "../chat-open-button/chat-open-button.component"; import ChatOpenButton from "../chat-open-button/chat-open-button.component";
import OwnerNameDisplay from "../owner-name-display/owner-name-display.component"; import OwnerNameDisplay from "../owner-name-display/owner-name-display.component";
import {pageLimit} from "../../utils/config";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop, bodyshop: selectBodyshop,
}); });
export function JobsReadyList({ bodyshop }) { export function JobsReadyList({bodyshop}) {
const searchParams =queryString.parse(useLocation().search); const searchParams = queryString.parse(useLocation().search);
const { selected } = searchParams; const {selected} = searchParams;
const selectedBreakpoint = Object.entries(Grid.useBreakpoint()) const selectedBreakpoint = Object.entries(Grid.useBreakpoint())
.filter((screen) => !!screen[1]) .filter((screen) => !!screen[1])
.slice(-1)[0]; .slice(-1)[0];
const readyStatuses = useMemo(() => { const readyStatuses = useMemo(() => {
if (bodyshop.md_ro_statuses.ready_statuses) if (bodyshop.md_ro_statuses.ready_statuses)
return bodyshop.md_ro_statuses.ready_statuses; return bodyshop.md_ro_statuses.ready_statuses;
return bodyshop.md_ro_statuses.post_production_statuses.filter( return bodyshop.md_ro_statuses.post_production_statuses.filter(
(s) => (s) =>
s !== bodyshop.md_ro_statuses.default_invoiced && s !== bodyshop.md_ro_statuses.default_invoiced &&
s !== bodyshop.md_ro_statuses.default_exported s !== bodyshop.md_ro_statuses.default_exported
); );
}, [bodyshop.md_ro_statuses]); }, [bodyshop.md_ro_statuses]);
const { loading, error, data, refetch } = useQuery(QUERY_ALL_ACTIVE_JOBS, { const {loading, error, data, refetch} = useQuery(QUERY_ALL_ACTIVE_JOBS, {
variables: { variables: {
statuses: readyStatuses, statuses: readyStatuses,
isConverted: true, isConverted: true,
}, },
fetchPolicy: "network-only", fetchPolicy: "network-only",
nextFetchPolicy: "network-only", nextFetchPolicy: "network-only",
}); });
const [state, setState] = useState({ const [state, setState] = useState({sortedInfo: {}});
sortedInfo: {}, const [filter, setFilter] = useLocalStorage("filter_jobs_ready", null);
filteredInfo: { text: "" },
});
const { t } = useTranslation(); const {t} = useTranslation();
const history = useNavigate(); const history = useNavigate();
const [searchText, setSearchText] = useState(""); const [searchText, setSearchText] = useState("");
if (error) return <AlertComponent message={error.message} type="error" />; if (error) return <AlertComponent message={error.message} type="error"/>;
const jobs = data const jobs = data
? searchText === "" ? searchText === ""
? data.jobs ? data.jobs
: data.jobs.filter( : data.jobs.filter(
(j) => (j) =>
(j.ro_number || "") (j.ro_number || "")
.toString() .toString()
.toLowerCase() .toLowerCase()
.includes(searchText.toLowerCase()) || .includes(searchText.toLowerCase()) ||
(j.ownr_co_nm || "") (j.ownr_co_nm || "")
.toLowerCase() .toLowerCase()
.includes(searchText.toLowerCase()) || .includes(searchText.toLowerCase()) ||
(j.comments || "") (j.comments || "")
.toLowerCase() .toLowerCase()
.includes(searchText.toLowerCase()) || .includes(searchText.toLowerCase()) ||
(j.ownr_fn || "") (j.ownr_fn || "")
.toLowerCase() .toLowerCase()
.includes(searchText.toLowerCase()) || .includes(searchText.toLowerCase()) ||
(j.ownr_ln || "") (j.ownr_ln || "")
.toLowerCase() .toLowerCase()
.includes(searchText.toLowerCase()) || .includes(searchText.toLowerCase()) ||
(j.clm_no || "").toLowerCase().includes(searchText.toLowerCase()) || (j.clm_no || "").toLowerCase().includes(searchText.toLowerCase()) ||
(j.plate_no || "") (j.plate_no || "")
.toLowerCase() .toLowerCase()
.includes(searchText.toLowerCase()) || .includes(searchText.toLowerCase()) ||
(j.v_model_desc || "") (j.v_model_desc || "")
.toLowerCase() .toLowerCase()
.includes(searchText.toLowerCase()) || .includes(searchText.toLowerCase()) ||
(j.est_ct_fn || "") (j.est_ct_fn || "")
.toLowerCase() .toLowerCase()
.includes(searchText.toLowerCase()) || .includes(searchText.toLowerCase()) ||
(j.est_ct_ln || "") (j.est_ct_ln || "")
.toLowerCase() .toLowerCase()
.includes(searchText.toLowerCase()) || .includes(searchText.toLowerCase()) ||
(j.v_make_desc || "") (j.v_make_desc || "")
.toLowerCase() .toLowerCase()
.includes(searchText.toLowerCase()) .includes(searchText.toLowerCase())
) )
: []; : [];
const handleTableChange = (pagination, filters, sorter) => { const handleTableChange = (pagination, filters, sorter) => {
setState({ ...state, filteredInfo: filters, sortedInfo: sorter }); setState({...state, sortedInfo: sorter});
}; setFilter(filters);
};
const handleOnRowClick = (record) => { const handleOnRowClick = (record) => {
if (record) { if (record) {
if (record.id) { if (record.id) {
history({ history({
search: queryString.stringify({ search: queryString.stringify({
...searchParams, ...searchParams,
selected: record.id, selected: record.id,
}), }),
}); });
} }
} }
}; };
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",
sorter: (a, b) => alphaSort(a.ro_number, b.ro_number), sorter: (a, b) => alphaSort(a.ro_number, b.ro_number),
sortOrder: sortOrder:
state.sortedInfo.columnKey === "ro_number" && state.sortedInfo.order, state.sortedInfo.columnKey === "ro_number" && state.sortedInfo.order,
render: (text, record) => (
render: (text, record) => ( <Link
<Link to={"/manage/jobs/" + record.id}
to={"/manage/jobs/" + record.id} onClick={(e) => e.stopPropagation()}
onClick={(e) => e.stopPropagation()} >
> <Space>
<Space> {record.ro_number || t("general.labels.na")}
{record.ro_number || t("general.labels.na")} {record.production_vars && record.production_vars.alert ? (
{record.production_vars && record.production_vars.alert ? ( <ExclamationCircleFilled className="production-alert"/>
<ExclamationCircleFilled className="production-alert" /> ) : null}
) : null} {record.suspended && (
{record.suspended && ( <PauseCircleOutlined style={{color: "orangered"}}/>
<PauseCircleOutlined style={{ color: "orangered" }} /> )}
)} {record.iouparent && (
{record.iouparent && ( <Tooltip title={t("jobs.labels.iou")}>
<Tooltip title={t("jobs.labels.iou")}> <BranchesOutlined style={{color: "orangered"}}/>
<BranchesOutlined style={{ color: "orangered" }} /> </Tooltip>
</Tooltip> )}
)} </Space>
</Space> </Link>
</Link> ),
), },
}, {
{ title: t("jobs.fields.owner"),
title: t("jobs.fields.owner"), dataIndex: "owner",
dataIndex: "owner", key: "owner",
key: "owner", ellipsis: true,
ellipsis: true, responsive: ["md"],
sorter: (a, b) => alphaSort(a.ownr_ln, b.ownr_ln),
responsive: ["md"], sortOrder:
sorter: (a, b) => alphaSort(a.ownr_ln, b.ownr_ln), state.sortedInfo.columnKey === "owner" && state.sortedInfo.order,
sortOrder: render: (text, record) => {
state.sortedInfo.columnKey === "owner" && state.sortedInfo.order, return record.owner ? (
render: (text, record) => { <Link
return record.owner ? ( to={"/manage/owners/" + record.owner.id}
<Link onClick={(e) => e.stopPropagation()}
to={"/manage/owners/" + record.owner.id} >
onClick={(e) => e.stopPropagation()} <OwnerNameDisplay ownerObject={record}/>
> </Link>
<OwnerNameDisplay ownerObject={record} /> ) : (
</Link> <span>
) : ( <OwnerNameDisplay ownerObject={record}/>
<span>
<OwnerNameDisplay ownerObject={record} />
</span> </span>
); );
},
},
{
title: t("jobs.fields.ownr_ph1"),
dataIndex: "ownr_ph1",
key: "ownr_ph1",
ellipsis: true,
responsive: ["md"],
render: (text, record) => (
<ChatOpenButton phone={record.ownr_ph1} jobid={record.id} />
),
},
{
title: t("jobs.fields.ownr_ph2"),
dataIndex: "ownr_ph2",
key: "ownr_ph2",
ellipsis: true,
responsive: ["md"],
render: (text, record) => (
<ChatOpenButton phone={record.ownr_ph2} jobid={record.id} />
),
},
{
title: t("jobs.fields.status"),
dataIndex: "status",
key: "status",
ellipsis: true,
sorter: (a, b) => alphaSort(a.status, b.status),
sortOrder:
state.sortedInfo.columnKey === "status" && state.sortedInfo.order,
filters:
(jobs &&
jobs
.map((j) => j.status)
.filter(onlyUnique)
.map((s) => {
return {
text: s || "No Status*",
value: [s],
};
})) ||
[],
onFilter: (value, record) => value.includes(record.status),
},
{
title: t("jobs.fields.vehicle"),
dataIndex: "vehicle",
key: "vehicle",
ellipsis: true,
render: (text, record) => {
return record.vehicleid ? (
<Link
to={"/manage/vehicles/" + record.vehicleid}
onClick={(e) => e.stopPropagation()}
>
{`${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("vehicles.fields.plate_no"),
dataIndex: "plate_no",
key: "plate_no",
ellipsis: true,
responsive: ["md"],
sorter: (a, b) => alphaSort(a.plate_no, b.plate_no),
sortOrder:
state.sortedInfo.columnKey === "plate_no" && state.sortedInfo.order,
},
{
title: t("jobs.fields.clm_no"),
dataIndex: "clm_no",
key: "clm_no",
ellipsis: true,
responsive: ["md"],
sorter: (a, b) => alphaSort(a.clm_no, b.clm_no),
sortOrder:
state.sortedInfo.columnKey === "clm_no" && state.sortedInfo.order,
render: (text, record) =>
`${record.clm_no || ""}${
record.po_number ? ` (PO: ${record.po_number})` : ""
}`,
},
{
title: t("jobs.fields.ins_co_nm"),
dataIndex: "ins_co_nm",
key: "ins_co_nm",
ellipsis: true,
filters:
(jobs &&
jobs
.map((j) => j.ins_co_nm)
.filter(onlyUnique)
.map((s) => {
return {
text: s,
value: [s],
};
})) ||
[],
onFilter: (value, record) => value.includes(record.ins_co_nm),
responsive: ["md"],
},
{
title: t("jobs.fields.clm_total"),
dataIndex: "clm_total",
key: "clm_total",
responsive: ["md"],
ellipsis: true,
sorter: (a, b) => a.clm_total - b.clm_total,
sortOrder:
state.sortedInfo.columnKey === "clm_total" && state.sortedInfo.order,
render: (text, record) => (
<CurrencyFormatter>{record.clm_total}</CurrencyFormatter>
),
},
{
title: t("jobs.labels.estimator"),
dataIndex: "jobs.labels.estimator",
key: "jobs.labels.estimator",
ellipsis: true,
responsive: ["xl"],
filterSearch: true,
filters:
(jobs &&
jobs
.map((j) => `${j.est_ct_fn || ""} ${j.est_ct_ln || ""}`.trim())
.filter(onlyUnique)
.map((s) => {
return {
text: s || "N/A",
value: [s],
};
})) ||
[],
onFilter: (value, record) =>
value.includes(
`${record.est_ct_fn || ""} ${record.est_ct_ln || ""}`.trim()
),
render: (text, record) =>
`${record.est_ct_fn || ""} ${record.est_ct_ln || ""}`.trim(),
},
{
title: t("jobs.fields.comment"),
dataIndex: "comment",
key: "comment",
ellipsis: true,
responsive: ["md"],
},
// {
// title: t("jobs.fields.owner_owing"),
// dataIndex: "owner_owing",
// key: "owner_owing",
// responsive: ["md"],
// render: (text, record) => (
// <CurrencyFormatter>{record.owner_owing}</CurrencyFormatter>
// ),
// },
];
const scrollMapper = {
xs: true,
sm: true,
md: true,
lg: "100%",
xl: "100%",
xxl: "100%",
};
return (
<Card
title={t("titles.bc.jobs-ready")}
extra={
<Space wrap>
<span>({readyStatuses && readyStatuses.join(", ")})</span>
<Button onClick={() => refetch()}>
<SyncOutlined />
</Button>
<Input.Search
placeholder={t("general.labels.search")}
onChange={(e) => {
setSearchText(e.target.value);
}}
value={searchText}
enterButton
/>
</Space>
}
>
<Table
loading={loading}
pagination={{ defaultPageSize: pageLimit }}
columns={columns}
rowKey="id"
dataSource={jobs}
scroll={{
x: selectedBreakpoint ? scrollMapper[selectedBreakpoint[0]] : "100%",
}}
rowSelection={{
onSelect: (record) => {
handleOnRowClick(record);
},
selectedRowKeys: [selected],
type: "radio",
}}
onChange={handleTableChange}
onRow={(record, rowIndex) => {
return {
onClick: (event) => {
handleOnRowClick(record);
}, },
}; },
}} {
/> title: t("jobs.fields.ownr_ph1"),
</Card> dataIndex: "ownr_ph1",
); key: "ownr_ph1",
ellipsis: true,
responsive: ["md"],
render: (text, record) => (
<ChatOpenButton phone={record.ownr_ph1} jobid={record.id}/>
),
},
{
title: t("jobs.fields.ownr_ph2"),
dataIndex: "ownr_ph2",
key: "ownr_ph2",
ellipsis: true,
responsive: ["md"],
render: (text, record) => (
<ChatOpenButton phone={record.ownr_ph2} jobid={record.id}/>
),
},
{
title: t("jobs.fields.status"),
dataIndex: "status",
key: "status",
ellipsis: true,
sorter: (a, b) => alphaSort(a.status, b.status),
sortOrder:
state.sortedInfo.columnKey === "status" && state.sortedInfo.order,
filteredValue: filter?.status || null,
filters:
(jobs &&
jobs
.map((j) => j.status)
.filter(onlyUnique)
.map((s) => {
return {
text: s || "No Status*",
value: [s],
};
})
.sort((a, b) =>
statusSort(
a.text,
b.text,
bodyshop.md_ro_statuses.active_statuses
)
)) ||
[],
onFilter: (value, record) => value.includes(record.status),
},
{
title: t("jobs.fields.vehicle"),
dataIndex: "vehicle",
key: "vehicle",
ellipsis: true,
render: (text, record) => {
return record.vehicleid ? (
<Link
to={"/manage/vehicles/" + record.vehicleid}
onClick={(e) => e.stopPropagation()}
>
{`${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("vehicles.fields.plate_no"),
dataIndex: "plate_no",
key: "plate_no",
ellipsis: true,
responsive: ["md"],
sorter: (a, b) => alphaSort(a.plate_no, b.plate_no),
sortOrder:
state.sortedInfo.columnKey === "plate_no" && state.sortedInfo.order,
},
{
title: t("jobs.fields.clm_no"),
dataIndex: "clm_no",
key: "clm_no",
ellipsis: true,
responsive: ["md"],
sorter: (a, b) => alphaSort(a.clm_no, b.clm_no),
sortOrder:
state.sortedInfo.columnKey === "clm_no" && state.sortedInfo.order,
render: (text, record) =>
`${record.clm_no || ""}${
record.po_number ? ` (PO: ${record.po_number})` : ""
}`,
},
{
title: t("jobs.fields.ins_co_nm"),
dataIndex: "ins_co_nm",
key: "ins_co_nm",
ellipsis: true,
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),
responsive: ["md"],
},
{
title: t("jobs.fields.clm_total"),
dataIndex: "clm_total",
key: "clm_total",
responsive: ["md"],
ellipsis: true,
sorter: (a, b) => a.clm_total - b.clm_total,
sortOrder:
state.sortedInfo.columnKey === "clm_total" && state.sortedInfo.order,
render: (text, record) => (
<CurrencyFormatter>{record.clm_total}</CurrencyFormatter>
),
},
{
title: t("jobs.labels.estimator"),
dataIndex: "jobs.labels.estimator",
key: "estimator",
ellipsis: true,
responsive: ["xl"],
filteredValue: filter?.estimator || null,
filterSearch: true,
filters:
(jobs &&
jobs
.map((j) => `${j.est_ct_fn || ""} ${j.est_ct_ln || ""}`.trim())
.filter(onlyUnique)
.map((s) => {
return {
text: s || "No Estimator*",
value: [s],
};
})
.sort((a, b) => alphaSort(a.text, b.text))) ||
[],
onFilter: (value, record) =>
value.includes(
`${record.est_ct_fn || ""} ${record.est_ct_ln || ""}`.trim()
),
render: (text, record) =>
`${record.est_ct_fn || ""} ${record.est_ct_ln || ""}`.trim(),
},
{
title: t("jobs.fields.comment"),
dataIndex: "comment",
key: "comment",
ellipsis: true,
responsive: ["md"],
},
// {
// title: t("jobs.fields.owner_owing"),
// dataIndex: "owner_owing",
// key: "owner_owing",
// responsive: ["md"],
// render: (text, record) => (
// <CurrencyFormatter>{record.owner_owing}</CurrencyFormatter>
// ),
// },
];
const scrollMapper = {
xs: true,
sm: true,
md: true,
lg: "100%",
xl: "100%",
xxl: "100%",
};
return (
<Card
title={t("titles.bc.jobs-ready")}
extra={
<Space wrap>
<span>({readyStatuses && readyStatuses.join(", ")})</span>
<Button onClick={() => refetch()}>
<SyncOutlined/>
</Button>
<Input.Search
placeholder={t("general.labels.search")}
onChange={(e) => {
setSearchText(e.target.value);
}}
value={searchText}
enterButton
/>
</Space>
}
>
<Table
loading={loading}
pagination={{defaultPageSize: pageLimit}}
columns={columns}
rowKey="id"
dataSource={jobs}
scroll={{
x: selectedBreakpoint ? scrollMapper[selectedBreakpoint[0]] : "100%",
}}
rowSelection={{
onSelect: (record) => {
handleOnRowClick(record);
},
selectedRowKeys: [selected],
type: "radio",
}}
onChange={handleTableChange}
onRow={(record, rowIndex) => {
return {
onClick: (event) => {
handleOnRowClick(record);
},
};
}}
/>
</Card>
);
} }
export default connect(mapStateToProps, null)(JobsReadyList); export default connect(mapStateToProps, null)(JobsReadyList);

View File

@@ -2621,6 +2621,7 @@
"open_orders": "Open Orders by Date", "open_orders": "Open Orders by Date",
"open_orders_csr": "Open Orders by CSR", "open_orders_csr": "Open Orders by CSR",
"open_orders_estimator": "Open Orders by Estimator", "open_orders_estimator": "Open Orders by Estimator",
"open_orders_excel": "Open Orders - Excel",
"open_orders_ins_co": "Open Orders by Insurance Company", "open_orders_ins_co": "Open Orders by Insurance Company",
"open_orders_referral": "Open Orders by Referral Source", "open_orders_referral": "Open Orders by Referral Source",
"open_orders_specific_csr": "Open Orders filtered by CSR", "open_orders_specific_csr": "Open Orders filtered by CSR",

View File

@@ -2621,6 +2621,7 @@
"open_orders": "", "open_orders": "",
"open_orders_csr": "", "open_orders_csr": "",
"open_orders_estimator": "", "open_orders_estimator": "",
"open_orders_excel": "",
"open_orders_ins_co": "", "open_orders_ins_co": "",
"open_orders_referral": "", "open_orders_referral": "",
"open_orders_specific_csr": "", "open_orders_specific_csr": "",

View File

@@ -2621,6 +2621,7 @@
"open_orders": "", "open_orders": "",
"open_orders_csr": "", "open_orders_csr": "",
"open_orders_estimator": "", "open_orders_estimator": "",
"open_orders_excel": "",
"open_orders_ins_co": "", "open_orders_ins_co": "",
"open_orders_referral": "", "open_orders_referral": "",
"open_orders_specific_csr": "", "open_orders_specific_csr": "",

View File

@@ -2026,6 +2026,19 @@ export const TemplateList = (type, context) => {
}, },
group: "customers", group: "customers",
}, },
open_orders_excel: {
title: i18n.t("reportcenter.templates.open_orders_excel"),
subject: i18n.t("reportcenter.templates.open_orders_excel"),
key: "open_orders_excel",
//idtype: "vendor",
reporttype: "excel",
disabled: false,
rangeFilter: {
object: i18n.t("reportcenter.labels.objects.jobs"),
field: i18n.t("jobs.fields.date_open"),
},
group: "jobs",
},
} }
: {}), : {}),
...(!type || type === "courtesycarcontract" ...(!type || type === "courtesycarcontract"

View File

@@ -29,17 +29,24 @@ const ftpSetup = {
password: process.env.KAIZEN_PASSWORD, password: process.env.KAIZEN_PASSWORD,
debug: (message, ...data) => logger.log(message, "DEBUG", "api", null, data), debug: (message, ...data) => logger.log(message, "DEBUG", "api", null, data),
algorithms: { algorithms: {
serverHostKey: ["ssh-rsa", "ssh-dss", "rsa-sha2-256", "rsa-sha2-512", "ecdsa-sha2-nistp256", "ecdsa-sha2-nistp384"], serverHostKey: [
"ssh-rsa",
"ssh-dss",
"rsa-sha2-256",
"rsa-sha2-512",
"ecdsa-sha2-nistp256",
"ecdsa-sha2-nistp384",
],
}, },
}; };
exports.default = async (req, res) => { exports.default = async (req, res) => {
//Query for the List of Bodyshop Clients. //Query for the List of Bodyshop Clients.
logger.log("kaizen-start", "DEBUG", "api", null, null); logger.log("kaizen-start", "DEBUG", "api", null, null);
const kaizenShopsNames = ["SUMMIT", "STRATHMORE", "SUNRIDGE"]; const kaizenShopsIDs = ["SUMMIT", "STRATHMORE", "SUNRIDGE"];
const { bodyshops } = await client.request(queries.GET_KAIZEN_SHOPS, { const { bodyshops } = await client.request(queries.GET_KAIZEN_SHOPS, {
shopname: kaizenShopsNames, imexshopid: kaizenShopsIDs,
}); });
const specificShopIds = req.body.bodyshopIds; // ['uuid] const specificShopIds = req.body.bodyshopIds; // ['uuid]

View File

@@ -1739,8 +1739,8 @@ exports.GET_ENTEGRAL_SHOPS = `query GET_ENTEGRAL_SHOPS {
} }
}`; }`;
exports.GET_KAIZEN_SHOPS = `query GET_KAIZEN_SHOPS($shopname: [String]) { exports.GET_KAIZEN_SHOPS = `query GET_KAIZEN_SHOPS($imexshopid: [String]) {
bodyshops(where: {shopname: {_in: $shopname}}){ bodyshops(where: {imexshopid: {_in: $imexshopid}}){
id id
shopname shopname
address1 address1