Merged in release/2024-01-19 (pull request #1184)
Release/2024 01 19 Approved-by: Dave Richer
This commit is contained in:
@@ -445,7 +445,7 @@ function Header({
|
|||||||
))}
|
))}
|
||||||
</Menu.SubMenu>
|
</Menu.SubMenu>
|
||||||
<Menu.Item style={{marginLeft: 'auto'}} key="profile">
|
<Menu.Item style={{marginLeft: 'auto'}} key="profile">
|
||||||
<Tooltip title="A faster more modern ImEX Online is ready for you to try! You can switch back at any time.">
|
<Tooltip title="A more modern ImEX Online is ready for you to try! You can switch back at any time.">
|
||||||
<InfoCircleOutlined/>
|
<InfoCircleOutlined/>
|
||||||
<span style={{marginRight: 8}}>Try the new ImEX Online</span>
|
<span style={{marginRight: 8}}>Try the new ImEX Online</span>
|
||||||
<Switch
|
<Switch
|
||||||
|
|||||||
@@ -7,21 +7,31 @@ import { connect } from "react-redux";
|
|||||||
import { useHistory } from "react-router";
|
import { useHistory } from "react-router";
|
||||||
import { createStructuredSelector } from "reselect";
|
import { createStructuredSelector } from "reselect";
|
||||||
import { UPDATE_JOB_LINES_IOU } from "../../graphql/jobs-lines.queries";
|
import { UPDATE_JOB_LINES_IOU } from "../../graphql/jobs-lines.queries";
|
||||||
|
import { selectTechnician } from "../../redux/tech/tech.selectors";
|
||||||
import {
|
import {
|
||||||
selectBodyshop,
|
selectBodyshop,
|
||||||
selectCurrentUser,
|
selectCurrentUser,
|
||||||
} from "../../redux/user/user.selectors";
|
} from "../../redux/user/user.selectors";
|
||||||
import { CreateIouForJob } from "../jobs-detail-header-actions/jobs-detail-header-actions.duplicate.util";
|
import { CreateIouForJob } from "../jobs-detail-header-actions/jobs-detail-header-actions.duplicate.util";
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
bodyshop: selectBodyshop,
|
bodyshop: selectBodyshop,
|
||||||
currentUser: selectCurrentUser,
|
currentUser: selectCurrentUser,
|
||||||
|
technician: selectTechnician,
|
||||||
});
|
});
|
||||||
|
|
||||||
const mapDispatchToProps = (dispatch) => ({
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
||||||
});
|
});
|
||||||
export default connect(mapStateToProps, mapDispatchToProps)(JobCreateIOU);
|
export default connect(mapStateToProps, mapDispatchToProps)(JobCreateIOU);
|
||||||
|
|
||||||
export function JobCreateIOU({ bodyshop, currentUser, job, selectedJobLines }) {
|
export function JobCreateIOU({
|
||||||
|
bodyshop,
|
||||||
|
currentUser,
|
||||||
|
job,
|
||||||
|
selectedJobLines,
|
||||||
|
technician,
|
||||||
|
}) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const client = useApolloClient();
|
const client = useApolloClient();
|
||||||
@@ -79,13 +89,19 @@ export function JobCreateIOU({ bodyshop, currentUser, job, selectedJobLines }) {
|
|||||||
title={t("jobs.labels.createiouwarning")}
|
title={t("jobs.labels.createiouwarning")}
|
||||||
onConfirm={handleCreateIou}
|
onConfirm={handleCreateIou}
|
||||||
disabled={
|
disabled={
|
||||||
!selectedJobLines || selectedJobLines.length === 0 || !job.converted
|
!selectedJobLines ||
|
||||||
|
selectedJobLines.length === 0 ||
|
||||||
|
!job.converted ||
|
||||||
|
technician
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<Button
|
<Button
|
||||||
loading={loading}
|
loading={loading}
|
||||||
disabled={
|
disabled={
|
||||||
!selectedJobLines || selectedJobLines.length === 0 || !job.converted
|
!selectedJobLines ||
|
||||||
|
selectedJobLines.length === 0 ||
|
||||||
|
!job.converted ||
|
||||||
|
technician
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{t("jobs.actions.createiou")}
|
{t("jobs.actions.createiou")}
|
||||||
|
|||||||
@@ -1,15 +1,37 @@
|
|||||||
import React from "react";
|
|
||||||
import { Card } from "antd";
|
import { Card } from "antd";
|
||||||
|
import React from "react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { connect } from "react-redux";
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
|
import { createStructuredSelector } from "reselect";
|
||||||
|
import { selectTechnician } from "../../redux/tech/tech.selectors";
|
||||||
|
|
||||||
export default function JobDetailCardTemplate({
|
const mapStateToProps = createStructuredSelector({
|
||||||
|
technician: selectTechnician,
|
||||||
|
});
|
||||||
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
|
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
||||||
|
});
|
||||||
|
|
||||||
|
export default connect(
|
||||||
|
mapStateToProps,
|
||||||
|
mapDispatchToProps
|
||||||
|
)(JobDetailCardTemplate);
|
||||||
|
|
||||||
|
export function JobDetailCardTemplate({
|
||||||
loading,
|
loading,
|
||||||
title,
|
title,
|
||||||
extraLink,
|
extraLink,
|
||||||
|
technician,
|
||||||
...otherProps
|
...otherProps
|
||||||
}) {
|
}) {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
let extra;
|
let extra;
|
||||||
if (extraLink) extra = { extra: <Link to={extraLink}>More</Link> };
|
if (extraLink && !technician)
|
||||||
|
extra = {
|
||||||
|
extra: <Link to={extraLink}>{t("jobs.labels.cards.more")}</Link>,
|
||||||
|
};
|
||||||
return (
|
return (
|
||||||
<Card
|
<Card
|
||||||
size="small"
|
size="small"
|
||||||
|
|||||||
@@ -19,10 +19,13 @@ export default function JobsCreateOwnerInfoNewComponent() {
|
|||||||
label={t("owners.fields.ownr_ln")}
|
label={t("owners.fields.ownr_ln")}
|
||||||
name={["owner", "data", "ownr_ln"]}
|
name={["owner", "data", "ownr_ln"]}
|
||||||
rules={[
|
rules={[
|
||||||
{
|
({ getFieldValue }) => ({
|
||||||
required: state.owner.new,
|
required:
|
||||||
|
state.owner.new &&
|
||||||
|
(!getFieldValue(["owner", "data", "ownr_co_nm"]) ||
|
||||||
|
getFieldValue(["owner", "data", "ownr_co_nm"]) === ""),
|
||||||
//message: t("general.validation.required"),
|
//message: t("general.validation.required"),
|
||||||
},
|
}),
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
<Input disabled={!state.owner.new} />
|
<Input disabled={!state.owner.new} />
|
||||||
@@ -31,10 +34,13 @@ export default function JobsCreateOwnerInfoNewComponent() {
|
|||||||
label={t("owners.fields.ownr_fn")}
|
label={t("owners.fields.ownr_fn")}
|
||||||
name={["owner", "data", "ownr_fn"]}
|
name={["owner", "data", "ownr_fn"]}
|
||||||
rules={[
|
rules={[
|
||||||
{
|
({ getFieldValue }) => ({
|
||||||
required: state.owner.new,
|
required:
|
||||||
|
state.owner.new &&
|
||||||
|
(!getFieldValue(["owner", "data", "ownr_co_nm"]) ||
|
||||||
|
getFieldValue(["owner", "data", "ownr_co_nm"]) === ""),
|
||||||
//message: t("general.validation.required"),
|
//message: t("general.validation.required"),
|
||||||
},
|
}),
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
<Input disabled={!state.owner.new} />
|
<Input disabled={!state.owner.new} />
|
||||||
@@ -51,6 +57,17 @@ export default function JobsCreateOwnerInfoNewComponent() {
|
|||||||
<Form.Item
|
<Form.Item
|
||||||
label={t("owners.fields.ownr_co_nm")}
|
label={t("owners.fields.ownr_co_nm")}
|
||||||
name={["owner", "data", "ownr_co_nm"]}
|
name={["owner", "data", "ownr_co_nm"]}
|
||||||
|
rules={[
|
||||||
|
({ getFieldValue }) => ({
|
||||||
|
required:
|
||||||
|
state.owner.new &&
|
||||||
|
(!getFieldValue(["owner", "data", "ownr_ln"]) ||
|
||||||
|
!getFieldValue(["owner", "data", "ownr_fn"]) ||
|
||||||
|
getFieldValue(["owner", "data", "ownr_ln"]) === "" ||
|
||||||
|
getFieldValue(["owner", "data", "ownr_fn"]) === ""),
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
}),
|
||||||
|
]}
|
||||||
>
|
>
|
||||||
<Input disabled={!state.owner.new} />
|
<Input disabled={!state.owner.new} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import { setModalContext } from "../../redux/modals/modals.actions";
|
|||||||
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 { DateTimeFormatter } from "../../utils/DateFormatter";
|
import { DateTimeFormatter } from "../../utils/DateFormatter";
|
||||||
|
import PhoneNumberFormatter from "../../utils/PhoneFormatter";
|
||||||
import ChatOpenButton from "../chat-open-button/chat-open-button.component";
|
import ChatOpenButton from "../chat-open-button/chat-open-button.component";
|
||||||
import DataLabel from "../data-label/data-label.component";
|
import DataLabel from "../data-label/data-label.component";
|
||||||
import JobAltTransportChange from "../job-at-change/job-at-change.component";
|
import JobAltTransportChange from "../job-at-change/job-at-change.component";
|
||||||
@@ -160,19 +161,35 @@ export function JobsDetailHeader({ job, bodyshop, disabled }) {
|
|||||||
<Card
|
<Card
|
||||||
style={{ height: "100%" }}
|
style={{ height: "100%" }}
|
||||||
title={
|
title={
|
||||||
<Link to={disabled ? "#" : `/manage/owners/${job.owner.id}`}>
|
disabled ? (
|
||||||
{ownerTitle.length > 0
|
<>
|
||||||
? ownerTitle
|
{ownerTitle.length > 0
|
||||||
: t("owner.labels.noownerinfo")}
|
? ownerTitle
|
||||||
</Link>
|
: t("owner.labels.noownerinfo")}
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<Link to={`/manage/owners/${job.owner.id}`}>
|
||||||
|
{ownerTitle.length > 0
|
||||||
|
? ownerTitle
|
||||||
|
: t("owner.labels.noownerinfo")}
|
||||||
|
</Link>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
<DataLabel key="2" label={t("jobs.fields.ownr_ph1")}>
|
<DataLabel key="2" label={t("jobs.fields.ownr_ph1")}>
|
||||||
<ChatOpenButton phone={job.ownr_ph1} jobid={job.id} />
|
{disabled ? (
|
||||||
|
<PhoneNumberFormatter>{job.ownr_ph1}</PhoneNumberFormatter>
|
||||||
|
) : (
|
||||||
|
<ChatOpenButton phone={job.ownr_ph1} jobid={job.id} />
|
||||||
|
)}
|
||||||
</DataLabel>
|
</DataLabel>
|
||||||
<DataLabel key="22" label={t("jobs.fields.ownr_ph2")}>
|
<DataLabel key="22" label={t("jobs.fields.ownr_ph2")}>
|
||||||
<ChatOpenButton phone={job.ownr_ph2} jobid={job.id} />
|
{disabled ? (
|
||||||
|
<PhoneNumberFormatter>{job.ownr_ph2}</PhoneNumberFormatter>
|
||||||
|
) : (
|
||||||
|
<ChatOpenButton phone={job.ownr_ph2} jobid={job.id} />
|
||||||
|
)}
|
||||||
</DataLabel>
|
</DataLabel>
|
||||||
<DataLabel key="3" label={t("owners.fields.address")}>
|
<DataLabel key="3" label={t("owners.fields.address")}>
|
||||||
{`${job.ownr_addr1 || ""} ${job.ownr_addr2 || ""} ${
|
{`${job.ownr_addr1 || ""} ${job.ownr_addr2 || ""} ${
|
||||||
@@ -180,7 +197,11 @@ export function JobsDetailHeader({ job, bodyshop, disabled }) {
|
|||||||
} ${job.ownr_st || ""} ${job.ownr_zip || ""}`}
|
} ${job.ownr_st || ""} ${job.ownr_zip || ""}`}
|
||||||
</DataLabel>
|
</DataLabel>
|
||||||
<DataLabel key="4" label={t("owners.fields.ownr_ea")}>
|
<DataLabel key="4" label={t("owners.fields.ownr_ea")}>
|
||||||
{job.ownr_ea || ""}
|
{disabled ? (
|
||||||
|
<>{job.ownr_ea || ""}</>
|
||||||
|
) : job.ownr_ea ? (
|
||||||
|
<a href={`mailto:${job.ownr_ea}`}>{job.ownr_ea}</a>
|
||||||
|
) : null}
|
||||||
</DataLabel>
|
</DataLabel>
|
||||||
{job.owner?.tax_number && (
|
{job.owner?.tax_number && (
|
||||||
<DataLabel key="5" label={t("owners.fields.tax_number")}>
|
<DataLabel key="5" label={t("owners.fields.tax_number")}>
|
||||||
@@ -195,17 +216,19 @@ export function JobsDetailHeader({ job, bodyshop, disabled }) {
|
|||||||
style={{ height: "100%" }}
|
style={{ height: "100%" }}
|
||||||
title={
|
title={
|
||||||
job.vehicle ? (
|
job.vehicle ? (
|
||||||
<Link
|
disabled ? (
|
||||||
to={
|
<>
|
||||||
disabled
|
{vehicleTitle.length > 0
|
||||||
? "#"
|
? vehicleTitle
|
||||||
: job.vehicle && `/manage/vehicles/${job.vehicle.id}`
|
: t("vehicles.labels.novehinfo")}{" "}
|
||||||
}
|
</>
|
||||||
>
|
) : (
|
||||||
{vehicleTitle.length > 0
|
<Link to={job.vehicle && `/manage/vehicles/${job.vehicle.id}`}>
|
||||||
? vehicleTitle
|
{vehicleTitle.length > 0
|
||||||
: t("vehicles.labels.novehinfo")}
|
? vehicleTitle
|
||||||
</Link>
|
: t("vehicles.labels.novehinfo")}
|
||||||
|
</Link>
|
||||||
|
)
|
||||||
) : (
|
) : (
|
||||||
<span></span>
|
<span></span>
|
||||||
)
|
)
|
||||||
@@ -223,7 +246,9 @@ export function JobsDetailHeader({ job, bodyshop, disabled }) {
|
|||||||
</VehicleVinDisplay>
|
</VehicleVinDisplay>
|
||||||
{bodyshop.pbs_serialnumber || bodyshop.cdk_dealerid ? (
|
{bodyshop.pbs_serialnumber || bodyshop.cdk_dealerid ? (
|
||||||
job.v_vin?.length !== 17 ? (
|
job.v_vin?.length !== 17 ? (
|
||||||
<WarningFilled style={{ color: "tomato", marginLeft: ".3rem" }} />
|
<WarningFilled
|
||||||
|
style={{ color: "tomato", marginLeft: ".3rem" }}
|
||||||
|
/>
|
||||||
) : null
|
) : null
|
||||||
) : null}
|
) : null}
|
||||||
</DataLabel>
|
</DataLabel>
|
||||||
@@ -231,7 +256,7 @@ export function JobsDetailHeader({ job, bodyshop, disabled }) {
|
|||||||
{job.regie_number || t("general.labels.na")}
|
{job.regie_number || t("general.labels.na")}
|
||||||
</DataLabel>
|
</DataLabel>
|
||||||
<DataLabel label={t("jobs.labels.relatedros")}>
|
<DataLabel label={t("jobs.labels.relatedros")}>
|
||||||
<JobsRelatedRos jobid={job.id} job={job} />
|
<JobsRelatedRos jobid={job.id} job={job} disabled={disabled} />
|
||||||
</DataLabel>
|
</DataLabel>
|
||||||
{job.vehicle && job.vehicle.notes && (
|
{job.vehicle && job.vehicle.notes && (
|
||||||
<DataLabel
|
<DataLabel
|
||||||
|
|||||||
@@ -10,9 +10,10 @@ import { Link, useHistory, useLocation } 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,
|
||||||
@@ -25,6 +26,7 @@ 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 [filter, setFilter] = useLocalStorage("filter_jobs_all", null);
|
||||||
const { page, sortcolumn, sortorder } = search;
|
const { page, sortcolumn, sortorder } = search;
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
|
|
||||||
@@ -93,6 +95,7 @@ export function JobsList({ bodyshop, refetch, loading, jobs, total }) {
|
|||||||
render: (text, record) => {
|
render: (text, record) => {
|
||||||
return record.status || t("general.labels.na");
|
return record.status || t("general.labels.na");
|
||||||
},
|
},
|
||||||
|
filteredValue: filter?.status || null,
|
||||||
filters: bodyshop.md_ro_statuses.statuses.map((s) => {
|
filters: bodyshop.md_ro_statuses.statuses.map((s) => {
|
||||||
return { text: s, value: [s] };
|
return { text: s, value: [s] };
|
||||||
}),
|
}),
|
||||||
@@ -189,6 +192,7 @@ export function JobsList({ bodyshop, refetch, loading, jobs, total }) {
|
|||||||
} else {
|
} else {
|
||||||
delete search.statusFilters;
|
delete search.statusFilters;
|
||||||
}
|
}
|
||||||
|
setFilter(filters);
|
||||||
history.push({ search: queryString.stringify(search) });
|
history.push({ search: queryString.stringify(search) });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import {
|
import {
|
||||||
SyncOutlined,
|
BranchesOutlined,
|
||||||
ExclamationCircleFilled,
|
ExclamationCircleFilled,
|
||||||
PauseCircleOutlined,
|
PauseCircleOutlined,
|
||||||
BranchesOutlined,
|
SyncOutlined,
|
||||||
} from "@ant-design/icons";
|
} from "@ant-design/icons";
|
||||||
import { useQuery } from "@apollo/client";
|
import { useQuery } from "@apollo/client";
|
||||||
import { Button, Card, Grid, Input, Space, Table, Tooltip } from "antd";
|
import { Button, Card, Grid, Input, Space, Table, Tooltip } from "antd";
|
||||||
@@ -14,382 +14,389 @@ import { Link, useHistory, useLocation } 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 CurrencyFormatter from "../../utils/CurrencyFormatter";
|
import CurrencyFormatter from "../../utils/CurrencyFormatter";
|
||||||
import { alphaSort } from "../../utils/sorters";
|
import { onlyUnique } from "../../utils/arrayHelper";
|
||||||
|
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";
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
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*"],
|
||||||
},
|
},
|
||||||
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_list", null);
|
||||||
filteredInfo: { text: "" },
|
|
||||||
});
|
|
||||||
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
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.push({
|
history.push({
|
||||||
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) =>
|
sorter: (a, b) =>
|
||||||
parseInt((a.ro_number || "0").replace(/\D/g, "")) -
|
parseInt((a.ro_number || "0").replace(/\D/g, "")) -
|
||||||
parseInt((b.ro_number || "0").replace(/\D/g, "")),
|
parseInt((b.ro_number || "0").replace(/\D/g, "")),
|
||||||
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.ownerid ? (
|
||||||
render: (text, record) => {
|
<Link
|
||||||
return record.ownerid ? (
|
to={"/manage/owners/" + record.ownerid}
|
||||||
<Link
|
onClick={(e) => e.stopPropagation()}
|
||||||
to={"/manage/owners/" + record.ownerid}
|
>
|
||||||
onClick={(e) => e.stopPropagation()}
|
<OwnerNameDisplay ownerObject={record} />
|
||||||
>
|
</Link>
|
||||||
<OwnerNameDisplay ownerObject={record} />
|
) : (
|
||||||
</Link>
|
<span>
|
||||||
) : (
|
|
||||||
<span>
|
|
||||||
<OwnerNameDisplay ownerObject={record} />
|
<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,
|
||||||
|
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"],
|
||||||
|
filterSearch: true,
|
||||||
|
filteredValue: filter?.estimator || null,
|
||||||
|
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-active")}
|
||||||
|
extra={
|
||||||
|
<Space wrap>
|
||||||
|
<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: 50 }}
|
||||||
|
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"),
|
/>
|
||||||
dataIndex: "ownr_ph1",
|
</Card>
|
||||||
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-active")}
|
|
||||||
extra={
|
|
||||||
<Space wrap>
|
|
||||||
<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: 50 }}
|
|
||||||
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)(JobsList);
|
export default connect(mapStateToProps, null)(JobsList);
|
||||||
|
|||||||
@@ -16,11 +16,12 @@ 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,
|
||||||
@@ -53,10 +54,8 @@ export function JobsReadyList({ bodyshop }) {
|
|||||||
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 = useHistory();
|
const history = useHistory();
|
||||||
@@ -105,7 +104,8 @@ export function JobsReadyList({ 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) => {
|
||||||
@@ -129,7 +129,6 @@ export function JobsReadyList({ bodyshop }) {
|
|||||||
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}
|
||||||
@@ -157,7 +156,6 @@ export function JobsReadyList({ bodyshop }) {
|
|||||||
dataIndex: "owner",
|
dataIndex: "owner",
|
||||||
key: "owner",
|
key: "owner",
|
||||||
ellipsis: true,
|
ellipsis: true,
|
||||||
|
|
||||||
responsive: ["md"],
|
responsive: ["md"],
|
||||||
sorter: (a, b) => alphaSort(a.ownr_ln, b.ownr_ln),
|
sorter: (a, b) => alphaSort(a.ownr_ln, b.ownr_ln),
|
||||||
sortOrder:
|
sortOrder:
|
||||||
@@ -197,16 +195,15 @@ export function JobsReadyList({ bodyshop }) {
|
|||||||
<ChatOpenButton phone={record.ownr_ph2} jobid={record.id} />
|
<ChatOpenButton 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: (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
|
||||||
@@ -217,11 +214,17 @@ export function JobsReadyList({ 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),
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
title: t("jobs.fields.vehicle"),
|
title: t("jobs.fields.vehicle"),
|
||||||
dataIndex: "vehicle",
|
dataIndex: "vehicle",
|
||||||
@@ -274,6 +277,7 @@ export function JobsReadyList({ 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
|
||||||
@@ -281,10 +285,11 @@ export function JobsReadyList({ 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"],
|
||||||
@@ -295,7 +300,6 @@ export function JobsReadyList({ bodyshop }) {
|
|||||||
key: "clm_total",
|
key: "clm_total",
|
||||||
responsive: ["md"],
|
responsive: ["md"],
|
||||||
ellipsis: true,
|
ellipsis: true,
|
||||||
|
|
||||||
sorter: (a, b) => a.clm_total - b.clm_total,
|
sorter: (a, b) => a.clm_total - b.clm_total,
|
||||||
sortOrder:
|
sortOrder:
|
||||||
state.sortedInfo.columnKey === "clm_total" && state.sortedInfo.order,
|
state.sortedInfo.columnKey === "clm_total" && state.sortedInfo.order,
|
||||||
@@ -306,9 +310,10 @@ export function JobsReadyList({ bodyshop }) {
|
|||||||
{
|
{
|
||||||
title: t("jobs.labels.estimator"),
|
title: t("jobs.labels.estimator"),
|
||||||
dataIndex: "jobs.labels.estimator",
|
dataIndex: "jobs.labels.estimator",
|
||||||
key: "jobs.labels.estimator",
|
key: "estimator",
|
||||||
ellipsis: true,
|
ellipsis: true,
|
||||||
responsive: ["xl"],
|
responsive: ["xl"],
|
||||||
|
filteredValue: filter?.estimator || null,
|
||||||
filterSearch: true,
|
filterSearch: true,
|
||||||
filters:
|
filters:
|
||||||
(jobs &&
|
(jobs &&
|
||||||
@@ -317,10 +322,11 @@ export function JobsReadyList({ 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(
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { Space, Tag } from "antd";
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
|
|
||||||
export default function JobsRelatedRos({ jobid, job }) {
|
export default function JobsRelatedRos({ jobid, job, disabled }) {
|
||||||
if (!(job && job.vehicle && job.vehicle.jobs)) return null;
|
if (!(job && job.vehicle && job.vehicle.jobs)) return null;
|
||||||
return (
|
return (
|
||||||
<Space wrap>
|
<Space wrap>
|
||||||
@@ -10,9 +10,15 @@ export default function JobsRelatedRos({ jobid, job }) {
|
|||||||
.filter((j) => j.id !== job.id)
|
.filter((j) => j.id !== job.id)
|
||||||
.map((j) => (
|
.map((j) => (
|
||||||
<Tag key={j.id}>
|
<Tag key={j.id}>
|
||||||
<Link to={`/manage/jobs/${j?.id}`}>{`${j.ro_number || "N/A"}${
|
{disabled ? (
|
||||||
j.clm_no ? ` | ${j.clm_no}` : ""
|
<>{`${j.ro_number || "N/A"}${j.clm_no ? ` | ${j.clm_no}` : ""}${
|
||||||
}${j.status ? ` | ${j.status}` : ""}`}</Link>
|
j.status ? ` | ${j.status}` : ""
|
||||||
|
}`}</>
|
||||||
|
) : (
|
||||||
|
<Link to={`/manage/jobs/${j?.id}`}>{`${j.ro_number || "N/A"}${
|
||||||
|
j.clm_no ? ` | ${j.clm_no}` : ""
|
||||||
|
}${j.status ? ` | ${j.status}` : ""}`}</Link>
|
||||||
|
)}
|
||||||
</Tag>
|
</Tag>
|
||||||
))}
|
))}
|
||||||
</Space>
|
</Space>
|
||||||
|
|||||||
@@ -76,7 +76,14 @@ const r = ({ technician, state, activeStatuses, data, bodyshop }) => {
|
|||||||
dataIndex: "ownr",
|
dataIndex: "ownr",
|
||||||
key: "ownr",
|
key: "ownr",
|
||||||
ellipsis: true,
|
ellipsis: true,
|
||||||
render: (text, record) => <OwnerNameDisplay ownerObject={record} />,
|
render: (text, record) =>
|
||||||
|
technician ? (
|
||||||
|
<OwnerNameDisplay ownerObject={record} />
|
||||||
|
) : (
|
||||||
|
<Link to={`/manage/owners/${record.ownerid}`}>
|
||||||
|
<OwnerNameDisplay ownerObject={record} />
|
||||||
|
</Link>
|
||||||
|
),
|
||||||
sorter: (a, b) => alphaSort(a.ownr_ln, b.ownr_ln),
|
sorter: (a, b) => alphaSort(a.ownr_ln, b.ownr_ln),
|
||||||
sortOrder:
|
sortOrder:
|
||||||
state.sortedInfo.columnKey === "ownr" && state.sortedInfo.order,
|
state.sortedInfo.columnKey === "ownr" && state.sortedInfo.order,
|
||||||
@@ -93,13 +100,18 @@ const r = ({ technician, state, activeStatuses, data, bodyshop }) => {
|
|||||||
),
|
),
|
||||||
sortOrder:
|
sortOrder:
|
||||||
state.sortedInfo.columnKey === "vehicle" && state.sortedInfo.order,
|
state.sortedInfo.columnKey === "vehicle" && state.sortedInfo.order,
|
||||||
render: (text, record) => (
|
render: (text, record) =>
|
||||||
<Link to={`/manage/vehicles/${record.vehicleid}`}>{`${
|
technician ? (
|
||||||
record.v_model_yr || ""
|
<>{`${record.v_model_yr || ""} ${record.v_make_desc || ""} ${
|
||||||
} ${record.v_make_desc || ""} ${record.v_model_desc || ""} ${
|
record.v_model_desc || ""
|
||||||
record.v_color || ""
|
} ${record.v_color || ""} ${record.plate_no || ""}`}</>
|
||||||
} ${record.plate_no || ""}`}</Link>
|
) : (
|
||||||
),
|
<Link to={`/manage/vehicles/${record.vehicleid}`}>{`${
|
||||||
|
record.v_model_yr || ""
|
||||||
|
} ${record.v_make_desc || ""} ${record.v_model_desc || ""} ${
|
||||||
|
record.v_color || ""
|
||||||
|
} ${record.plate_no || ""}`}</Link>
|
||||||
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: i18n.t("jobs.fields.actual_in"),
|
title: i18n.t("jobs.fields.actual_in"),
|
||||||
|
|||||||
@@ -13,12 +13,14 @@ import { selectTechnician } from "../../redux/tech/tech.selectors";
|
|||||||
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 { DateFormatter } from "../../utils/DateFormatter";
|
import { DateFormatter } from "../../utils/DateFormatter";
|
||||||
|
import PhoneNumberFormatter from "../../utils/PhoneFormatter";
|
||||||
import AlertComponent from "../alert/alert.component";
|
import AlertComponent from "../alert/alert.component";
|
||||||
import StartChatButton from "../chat-open-button/chat-open-button.component";
|
import StartChatButton from "../chat-open-button/chat-open-button.component";
|
||||||
import JobAtChange from "../job-at-change/job-at-change.component";
|
import JobAtChange from "../job-at-change/job-at-change.component";
|
||||||
import JobDetailCardsDocumentsComponent from "../job-detail-cards/job-detail-cards.documents.component";
|
import JobDetailCardsDocumentsComponent from "../job-detail-cards/job-detail-cards.documents.component";
|
||||||
import JobDetailCardsNotesComponent from "../job-detail-cards/job-detail-cards.notes.component";
|
import JobDetailCardsNotesComponent from "../job-detail-cards/job-detail-cards.notes.component";
|
||||||
import JobDetailCardsPartsComponent from "../job-detail-cards/job-detail-cards.parts.component";
|
import JobDetailCardsPartsComponent from "../job-detail-cards/job-detail-cards.parts.component";
|
||||||
|
import CardTemplate from "../job-detail-cards/job-detail-cards.template.component";
|
||||||
import JobEmployeeAssignments from "../job-employee-assignments/job-employee-assignments.container";
|
import JobEmployeeAssignments from "../job-employee-assignments/job-employee-assignments.container";
|
||||||
import ScoreboardAddButton from "../job-scoreboard-add-button/job-scoreboard-add-button.component";
|
import ScoreboardAddButton from "../job-scoreboard-add-button/job-scoreboard-add-button.component";
|
||||||
import LoadingSkeleton from "../loading-skeleton/loading-skeleton.component";
|
import LoadingSkeleton from "../loading-skeleton/loading-skeleton.component";
|
||||||
@@ -103,65 +105,85 @@ export function ProductionListDetail({
|
|||||||
{error && <AlertComponent error={JSON.stringify(error)} />}
|
{error && <AlertComponent error={JSON.stringify(error)} />}
|
||||||
{!loading && data && (
|
{!loading && data && (
|
||||||
<div>
|
<div>
|
||||||
<JobEmployeeAssignments job={data.jobs_by_pk} refetch={refetch} />
|
<Space direction="vertical">
|
||||||
<Descriptions bordered column={1}>
|
<CardTemplate
|
||||||
<Descriptions.Item label={t("jobs.fields.ro_number")}>
|
title={t("jobs.labels.employeeassignments")}
|
||||||
{theJob.ro_number || ""}
|
loading={loading}
|
||||||
</Descriptions.Item>
|
>
|
||||||
<Descriptions.Item label={t("jobs.fields.alt_transport")}>
|
<JobEmployeeAssignments job={data.jobs_by_pk} refetch={refetch} />
|
||||||
<Space>
|
</CardTemplate>
|
||||||
{data.jobs_by_pk.alt_transport || ""}
|
<Descriptions bordered column={1}>
|
||||||
<JobAtChange event={{ job: data.jobs_by_pk }} />
|
<Descriptions.Item label={t("jobs.fields.ro_number")}>
|
||||||
</Space>
|
{theJob.ro_number || ""}
|
||||||
</Descriptions.Item>
|
</Descriptions.Item>
|
||||||
<Descriptions.Item label={t("jobs.fields.clm_no")}>
|
<Descriptions.Item label={t("jobs.fields.alt_transport")}>
|
||||||
{theJob.clm_no || ""}
|
<Space>
|
||||||
</Descriptions.Item>
|
{data.jobs_by_pk.alt_transport || ""}
|
||||||
<Descriptions.Item label={t("jobs.fields.ins_co_nm")}>
|
<JobAtChange job={data.jobs_by_pk} />
|
||||||
{theJob.ins_co_nm || ""}
|
</Space>
|
||||||
</Descriptions.Item>
|
</Descriptions.Item>
|
||||||
<Descriptions.Item label={t("jobs.fields.owner")}>
|
<Descriptions.Item label={t("jobs.fields.clm_no")}>
|
||||||
<OwnerNameDisplay ownerObject={theJob} />
|
{theJob.clm_no || ""}
|
||||||
<StartChatButton
|
</Descriptions.Item>
|
||||||
phone={data.jobs_by_pk.ownr_ph1}
|
<Descriptions.Item label={t("jobs.fields.ins_co_nm")}>
|
||||||
jobid={data.jobs_by_pk.id}
|
{theJob.ins_co_nm || ""}
|
||||||
/>
|
</Descriptions.Item>
|
||||||
<StartChatButton
|
<Descriptions.Item label={t("jobs.fields.owner")}>
|
||||||
phone={data.jobs_by_pk.ownr_ph2}
|
<Space>
|
||||||
jobid={data.jobs_by_pk.id}
|
<OwnerNameDisplay ownerObject={theJob} />
|
||||||
/>
|
{!technician ? (
|
||||||
</Descriptions.Item>
|
<>
|
||||||
<Descriptions.Item label={t("jobs.fields.vehicle")}>
|
<StartChatButton
|
||||||
{`${theJob.v_model_yr || ""} ${theJob.v_color || ""} ${
|
phone={data.jobs_by_pk.ownr_ph1}
|
||||||
theJob.v_make_desc || ""
|
jobid={data.jobs_by_pk.id}
|
||||||
} ${theJob.v_model_desc || ""}`}
|
/>
|
||||||
</Descriptions.Item>
|
<StartChatButton
|
||||||
<Descriptions.Item label={t("jobs.fields.clm_total")}>
|
phone={data.jobs_by_pk.ownr_ph2}
|
||||||
<CurrencyFormatter>{theJob.clm_total}</CurrencyFormatter>
|
jobid={data.jobs_by_pk.id}
|
||||||
</Descriptions.Item>
|
/>
|
||||||
<Descriptions.Item label={t("jobs.fields.actual_in")}>
|
</>
|
||||||
<DateFormatter>{theJob.actual_in}</DateFormatter>
|
) : (
|
||||||
</Descriptions.Item>
|
<>
|
||||||
<Descriptions.Item label={t("jobs.fields.scheduled_completion")}>
|
<PhoneNumberFormatter>
|
||||||
<DateFormatter>{theJob.scheduled_completion}</DateFormatter>
|
{data.jobs_by_pk.ownr_ph1}
|
||||||
</Descriptions.Item>
|
</PhoneNumberFormatter>
|
||||||
</Descriptions>
|
<PhoneNumberFormatter>
|
||||||
|
{data.jobs_by_pk.ownr_ph2}
|
||||||
<JobDetailCardsPartsComponent
|
</PhoneNumberFormatter>
|
||||||
loading={loading}
|
</>
|
||||||
data={data ? data.jobs_by_pk : null}
|
)}
|
||||||
/>
|
</Space>
|
||||||
|
</Descriptions.Item>
|
||||||
<JobDetailCardsNotesComponent
|
<Descriptions.Item label={t("jobs.fields.vehicle")}>
|
||||||
loading={loading}
|
{`${theJob.v_model_yr || ""} ${theJob.v_color || ""} ${
|
||||||
data={data ? data.jobs_by_pk : null}
|
theJob.v_make_desc || ""
|
||||||
/>
|
} ${theJob.v_model_desc || ""}`}
|
||||||
{!bodyshop.uselocalmediaserver && (
|
</Descriptions.Item>
|
||||||
<JobDetailCardsDocumentsComponent
|
<Descriptions.Item label={t("jobs.fields.clm_total")}>
|
||||||
|
<CurrencyFormatter>{theJob.clm_total}</CurrencyFormatter>
|
||||||
|
</Descriptions.Item>
|
||||||
|
<Descriptions.Item label={t("jobs.fields.actual_in")}>
|
||||||
|
<DateFormatter>{theJob.actual_in}</DateFormatter>
|
||||||
|
</Descriptions.Item>
|
||||||
|
<Descriptions.Item label={t("jobs.fields.scheduled_completion")}>
|
||||||
|
<DateFormatter>{theJob.scheduled_completion}</DateFormatter>
|
||||||
|
</Descriptions.Item>
|
||||||
|
</Descriptions>
|
||||||
|
<JobDetailCardsPartsComponent
|
||||||
loading={loading}
|
loading={loading}
|
||||||
data={data ? data.jobs_by_pk : null}
|
data={data ? data.jobs_by_pk : null}
|
||||||
/>
|
/>
|
||||||
)}
|
<JobDetailCardsNotesComponent
|
||||||
|
loading={loading}
|
||||||
|
data={data ? data.jobs_by_pk : null}
|
||||||
|
/>
|
||||||
|
{!bodyshop.uselocalmediaserver && (
|
||||||
|
<JobDetailCardsDocumentsComponent
|
||||||
|
loading={loading}
|
||||||
|
data={data ? data.jobs_by_pk : null}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</Space>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</Drawer>
|
</Drawer>
|
||||||
|
|||||||
@@ -216,6 +216,7 @@ export function ScheduleJobModalContainer({
|
|||||||
okButtonProps={{
|
okButtonProps={{
|
||||||
loading: loading,
|
loading: loading,
|
||||||
}}
|
}}
|
||||||
|
closable={false}
|
||||||
>
|
>
|
||||||
<Form
|
<Form
|
||||||
form={form}
|
form={form}
|
||||||
|
|||||||
@@ -1,14 +1,20 @@
|
|||||||
import { Button, Table } from "antd";
|
import { Button, Table } from "antd";
|
||||||
import queryString from "query-string";
|
import queryString from "query-string";
|
||||||
import React from "react";
|
import React, { useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { useHistory, useLocation } from "react-router-dom";
|
import { useHistory, useLocation } from "react-router-dom";
|
||||||
|
import { alphaSort } from "../../utils/sorters";
|
||||||
|
|
||||||
export default function ShopEmployeesListComponent({ loading, employees }) {
|
export default function ShopEmployeesListComponent({ loading, employees }) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
const search = queryString.parse(useLocation().search);
|
const search = queryString.parse(useLocation().search);
|
||||||
|
|
||||||
|
const [state, setState] = useState({
|
||||||
|
sortedInfo: {},
|
||||||
|
filteredInfo: { text: "" },
|
||||||
|
});
|
||||||
|
|
||||||
const handleOnRowClick = (record) => {
|
const handleOnRowClick = (record) => {
|
||||||
if (record) {
|
if (record) {
|
||||||
search.employeeId = record.id;
|
search.employeeId = record.id;
|
||||||
@@ -18,32 +24,82 @@ export default function ShopEmployeesListComponent({ loading, employees }) {
|
|||||||
history.push({ search: queryString.stringify(search) });
|
history.push({ search: queryString.stringify(search) });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleTableChange = (pagination, filters, sorter) => {
|
||||||
|
setState({ ...state, filteredInfo: filters, sortedInfo: sorter });
|
||||||
|
};
|
||||||
|
|
||||||
const columns = [
|
const columns = [
|
||||||
{
|
{
|
||||||
title: t("employees.fields.employee_number"),
|
title: t("employees.fields.employee_number"),
|
||||||
dataIndex: "employee_number",
|
dataIndex: "employee_number",
|
||||||
key: "employee_number",
|
key: "employee_number",
|
||||||
|
sorter: (a, b) => alphaSort(a.employee_number, b.employee_number),
|
||||||
|
sortOrder:
|
||||||
|
state.sortedInfo.columnKey === "employee_number" &&
|
||||||
|
state.sortedInfo.order,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t("employees.fields.first_name"),
|
title: t("employees.labels.name"),
|
||||||
dataIndex: "first_name",
|
dataIndex: "employee_name",
|
||||||
key: "first_name",
|
key: "employee_name",
|
||||||
|
sorter: (a, b) =>
|
||||||
|
alphaSort(
|
||||||
|
`${a.first_name || ""} ${a.last_name || ""}`.trim(),
|
||||||
|
`${b.first_name || ""} ${b.last_name || ""}`.trim()
|
||||||
|
),
|
||||||
|
sortOrder:
|
||||||
|
state.sortedInfo.columnKey === "employee_name" &&
|
||||||
|
state.sortedInfo.order,
|
||||||
|
render: (text, record) =>
|
||||||
|
`${record.first_name || ""} ${record.last_name || ""}`.trim(),
|
||||||
},
|
},
|
||||||
{
|
|
||||||
title: t("employees.fields.last_name"),
|
|
||||||
dataIndex: "last_name",
|
|
||||||
key: "last_name",
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
{
|
||||||
title: t("employees.labels.rate_type"),
|
title: t("employees.labels.rate_type"),
|
||||||
dataIndex: "rate_type",
|
dataIndex: "rate_type",
|
||||||
key: "rate_type",
|
key: "rate_type",
|
||||||
|
sorter: (a, b) => Number(a.flat_rate) - Number(b.flat_rate),
|
||||||
|
sortOrder:
|
||||||
|
state.sortedInfo.columnKey === "rate_type" && state.sortedInfo.order,
|
||||||
|
filters: [
|
||||||
|
{
|
||||||
|
text: t("employees.labels.flat_rate"),
|
||||||
|
value: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: t("employees.labels.straight_time"),
|
||||||
|
value: false,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
onFilter: (value, record) => value === record.flate_rate,
|
||||||
render: (text, record) =>
|
render: (text, record) =>
|
||||||
record.flat_rate
|
record.flat_rate
|
||||||
? t("employees.labels.flat_rate")
|
? t("employees.labels.flat_rate")
|
||||||
: t("employees.labels.straight_time"),
|
: t("employees.labels.straight_time"),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: t("employees.labels.status"),
|
||||||
|
dataIndex: "active",
|
||||||
|
key: "active",
|
||||||
|
sorter: (a, b) => Number(a.active) - Number(b.active),
|
||||||
|
sortOrder:
|
||||||
|
state.sortedInfo.columnKey === "active" && state.sortedInfo.order,
|
||||||
|
filters: [
|
||||||
|
{
|
||||||
|
text: t("employees.labels.active"),
|
||||||
|
value: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: t("employees.labels.inactive"),
|
||||||
|
value: false,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
onFilter: (value, record) => value === record.active,
|
||||||
|
render: (text, record) =>
|
||||||
|
record.active
|
||||||
|
? t("employees.labels.active")
|
||||||
|
: t("employees.labels.inactive"),
|
||||||
|
},
|
||||||
];
|
];
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
@@ -74,6 +130,7 @@ export default function ShopEmployeesListComponent({ loading, employees }) {
|
|||||||
type: "radio",
|
type: "radio",
|
||||||
selectedRowKeys: [search.employeeId],
|
selectedRowKeys: [search.employeeId],
|
||||||
}}
|
}}
|
||||||
|
onChange={handleTableChange}
|
||||||
onRow={(record, rowIndex) => {
|
onRow={(record, rowIndex) => {
|
||||||
return {
|
return {
|
||||||
onClick: (event) => {
|
onClick: (event) => {
|
||||||
|
|||||||
@@ -166,9 +166,6 @@ export function TechClockOffButton({
|
|||||||
},
|
},
|
||||||
({ getFieldValue }) => ({
|
({ getFieldValue }) => ({
|
||||||
validator(rule, value) {
|
validator(rule, value) {
|
||||||
console.log(
|
|
||||||
bodyshop.tt_enforce_hours_for_tech_console
|
|
||||||
);
|
|
||||||
if (!bodyshop.tt_enforce_hours_for_tech_console) {
|
if (!bodyshop.tt_enforce_hours_for_tech_console) {
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
import { Button, Form, Input } from "antd";
|
import { Button, Form, Input } from "antd";
|
||||||
import React 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 { Redirect } from "react-router-dom";
|
||||||
import { createStructuredSelector } from "reselect";
|
import { createStructuredSelector } from "reselect";
|
||||||
import { techLoginStart } from "../../redux/tech/tech.actions";
|
import { techLoginStart } from "../../redux/tech/tech.actions";
|
||||||
import {
|
import {
|
||||||
@@ -11,7 +12,6 @@ import {
|
|||||||
} from "../../redux/tech/tech.selectors";
|
} from "../../redux/tech/tech.selectors";
|
||||||
import AlertComponent from "../alert/alert.component";
|
import AlertComponent from "../alert/alert.component";
|
||||||
import "./tech-login.styles.scss";
|
import "./tech-login.styles.scss";
|
||||||
import { Redirect } from "react-router-dom";
|
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
technician: selectTechnician,
|
technician: selectTechnician,
|
||||||
@@ -35,6 +35,10 @@ export function TechLogin({
|
|||||||
techLoginStart(values);
|
techLoginStart(values);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
document.title = t("titles.techconsole");
|
||||||
|
}, [t]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="tech-login-container">
|
<div className="tech-login-container">
|
||||||
{technician ? <Redirect to={`/tech/joblookup`} /> : null}
|
{technician ? <Redirect to={`/tech/joblookup`} /> : null}
|
||||||
|
|||||||
@@ -3,11 +3,12 @@ import { gql } from "@apollo/client";
|
|||||||
export const QUERY_EMPLOYEES = gql`
|
export const QUERY_EMPLOYEES = gql`
|
||||||
query QUERY_EMPLOYEES {
|
query QUERY_EMPLOYEES {
|
||||||
employees(order_by: { employee_number: asc }) {
|
employees(order_by: { employee_number: asc }) {
|
||||||
last_name
|
active
|
||||||
id
|
employee_number
|
||||||
first_name
|
first_name
|
||||||
flat_rate
|
flat_rate
|
||||||
employee_number
|
id
|
||||||
|
last_name
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ export const QUERY_ALL_ACTIVE_JOBS_PAGINATED = gql`
|
|||||||
$offset: Int
|
$offset: Int
|
||||||
$limit: Int
|
$limit: Int
|
||||||
$order: [jobs_order_by!]
|
$order: [jobs_order_by!]
|
||||||
$statuses: [String!]!,
|
$statuses: [String!]!
|
||||||
$isConverted: Boolean
|
$isConverted: Boolean
|
||||||
) {
|
) {
|
||||||
jobs(
|
jobs(
|
||||||
@@ -120,7 +120,9 @@ export const QUERY_PARTS_QUEUE = gql`
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
jobs(
|
jobs(
|
||||||
where: { _and: [{ status: { _in: $statuses }, converted: { _eq: true } }] }
|
where: {
|
||||||
|
_and: [{ status: { _in: $statuses }, converted: { _eq: true } }]
|
||||||
|
}
|
||||||
offset: $offset
|
offset: $offset
|
||||||
limit: $limit
|
limit: $limit
|
||||||
order_by: $order
|
order_by: $order
|
||||||
@@ -336,6 +338,7 @@ export const QUERY_JOBS_IN_PRODUCTION = gql`
|
|||||||
category
|
category
|
||||||
iouparent
|
iouparent
|
||||||
ro_number
|
ro_number
|
||||||
|
ownerid
|
||||||
ownr_fn
|
ownr_fn
|
||||||
ownr_ln
|
ownr_ln
|
||||||
ownr_co_nm
|
ownr_co_nm
|
||||||
|
|||||||
@@ -1,10 +1,17 @@
|
|||||||
import { Divider } from "antd";
|
import { Divider } from "antd";
|
||||||
import React from "react";
|
import React, { useEffect } from "react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
import TechClockInFormContainer from "../../components/tech-job-clock-in-form/tech-job-clock-in-form.container";
|
import TechClockInFormContainer from "../../components/tech-job-clock-in-form/tech-job-clock-in-form.container";
|
||||||
import TechClockedInList from "../../components/tech-job-clocked-in-list/tech-job-clocked-in-list.component";
|
import TechClockedInList from "../../components/tech-job-clocked-in-list/tech-job-clocked-in-list.component";
|
||||||
import TechJobStatistics from "../../components/tech-job-statistics/tech-job-statistics.component";
|
import TechJobStatistics from "../../components/tech-job-statistics/tech-job-statistics.component";
|
||||||
|
|
||||||
export default function TechClockComponent() {
|
export default function TechClockComponent() {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
document.title = t("titles.techjobclock");
|
||||||
|
}, [t]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<TechJobStatistics />
|
<TechJobStatistics />
|
||||||
|
|||||||
@@ -1,9 +1,16 @@
|
|||||||
import React from "react";
|
import React, { useEffect } from "react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
import RbacWrapperComponent from "../../components/rbac-wrapper/rbac-wrapper.component";
|
import RbacWrapperComponent from "../../components/rbac-wrapper/rbac-wrapper.component";
|
||||||
import TechLookupJobsDrawer from "../../components/tech-lookup-jobs-drawer/tech-lookup-jobs-drawer.component";
|
import TechLookupJobsDrawer from "../../components/tech-lookup-jobs-drawer/tech-lookup-jobs-drawer.component";
|
||||||
import TechLookupJobsList from "../../components/tech-lookup-jobs-list/tech-lookup-jobs-list.component";
|
import TechLookupJobsList from "../../components/tech-lookup-jobs-list/tech-lookup-jobs-list.component";
|
||||||
|
|
||||||
export default function TechLookupContainer() {
|
export default function TechLookupContainer() {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
document.title = t("titles.techjoblookup");
|
||||||
|
}, [t]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<RbacWrapperComponent action="jobs:list-active">
|
<RbacWrapperComponent action="jobs:list-active">
|
||||||
|
|||||||
@@ -1,7 +1,14 @@
|
|||||||
import React from "react";
|
import React, { useEffect } from "react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
import TimeTicketShift from "../../components/time-ticket-shift/time-ticket-shift.container";
|
import TimeTicketShift from "../../components/time-ticket-shift/time-ticket-shift.container";
|
||||||
|
|
||||||
export default function TechShiftClock() {
|
export default function TechShiftClock() {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
document.title = t("titles.techshiftclock");
|
||||||
|
}, [t]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<TimeTicketShift isTechConsole />
|
<TimeTicketShift isTechConsole />
|
||||||
|
|||||||
@@ -1001,10 +1001,13 @@
|
|||||||
},
|
},
|
||||||
"labels": {
|
"labels": {
|
||||||
"actions": "Actions",
|
"actions": "Actions",
|
||||||
|
"active": "Active",
|
||||||
"endmustbeafterstart": "End date must be after start date.",
|
"endmustbeafterstart": "End date must be after start date.",
|
||||||
"flat_rate": "Flat Rate",
|
"flat_rate": "Flat Rate",
|
||||||
|
"inactive": "Inactive",
|
||||||
"name": "Name",
|
"name": "Name",
|
||||||
"rate_type": "Rate Type",
|
"rate_type": "Rate Type",
|
||||||
|
"status": "Status",
|
||||||
"straight_time": "Straight Time"
|
"straight_time": "Straight Time"
|
||||||
},
|
},
|
||||||
"successes": {
|
"successes": {
|
||||||
@@ -1704,6 +1707,7 @@
|
|||||||
"estimator": "Estimator",
|
"estimator": "Estimator",
|
||||||
"filehandler": "File Handler",
|
"filehandler": "File Handler",
|
||||||
"insurance": "Insurance Details",
|
"insurance": "Insurance Details",
|
||||||
|
"more": "More",
|
||||||
"notes": "Notes",
|
"notes": "Notes",
|
||||||
"parts": "Parts",
|
"parts": "Parts",
|
||||||
"totals": "Totals",
|
"totals": "Totals",
|
||||||
@@ -2024,7 +2028,7 @@
|
|||||||
"joblookup": "Job Lookup",
|
"joblookup": "Job Lookup",
|
||||||
"login": "Login",
|
"login": "Login",
|
||||||
"logout": "Logout",
|
"logout": "Logout",
|
||||||
"productionboard": "Production Board - Visual",
|
"productionboard": "Production Visual",
|
||||||
"productionlist": "Production List",
|
"productionlist": "Production List",
|
||||||
"shiftclockin": "Shift Clock"
|
"shiftclockin": "Shift Clock"
|
||||||
}
|
}
|
||||||
@@ -2621,6 +2625,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",
|
||||||
@@ -2901,7 +2906,7 @@
|
|||||||
"parts-queue": "Parts Queue | $t(titles.app)",
|
"parts-queue": "Parts Queue | $t(titles.app)",
|
||||||
"payments-all": "Payments | $t(titles.app)",
|
"payments-all": "Payments | $t(titles.app)",
|
||||||
"phonebook": "Phonebook | $t(titles.app)",
|
"phonebook": "Phonebook | $t(titles.app)",
|
||||||
"productionboard": "Production - Board",
|
"productionboard": "Production Board - Visual | $t(titles.app)",
|
||||||
"productionlist": "Production Board - List | $t(titles.app)",
|
"productionlist": "Production Board - List | $t(titles.app)",
|
||||||
"profile": "My Profile | $t(titles.app)",
|
"profile": "My Profile | $t(titles.app)",
|
||||||
"readyjobs": "Ready Jobs | $t(titles.app)",
|
"readyjobs": "Ready Jobs | $t(titles.app)",
|
||||||
@@ -2913,6 +2918,10 @@
|
|||||||
"shop-csi": "CSI Responses | $t(titles.app)",
|
"shop-csi": "CSI Responses | $t(titles.app)",
|
||||||
"shop-templates": "Shop Templates | $t(titles.app)",
|
"shop-templates": "Shop Templates | $t(titles.app)",
|
||||||
"shop_vendors": "Vendors | $t(titles.app)",
|
"shop_vendors": "Vendors | $t(titles.app)",
|
||||||
|
"techconsole": "Technician Console | $t(titles.app)",
|
||||||
|
"techjoblookup": "Technician Job Lookup | $t(titles.app)",
|
||||||
|
"techjobclock": "Technician Job Clock | $t(titles.app)",
|
||||||
|
"techshiftclock": "Technician Shift Clock | $t(titles.app)",
|
||||||
"temporarydocs": "Temporary Documents | $t(titles.app)",
|
"temporarydocs": "Temporary Documents | $t(titles.app)",
|
||||||
"timetickets": "Time Tickets | $t(titles.app)",
|
"timetickets": "Time Tickets | $t(titles.app)",
|
||||||
"ttapprovals": "",
|
"ttapprovals": "",
|
||||||
|
|||||||
@@ -1001,10 +1001,13 @@
|
|||||||
},
|
},
|
||||||
"labels": {
|
"labels": {
|
||||||
"actions": "",
|
"actions": "",
|
||||||
|
"active": "",
|
||||||
"endmustbeafterstart": "",
|
"endmustbeafterstart": "",
|
||||||
"flat_rate": "",
|
"flat_rate": "",
|
||||||
|
"inactive": "",
|
||||||
"name": "",
|
"name": "",
|
||||||
"rate_type": "",
|
"rate_type": "",
|
||||||
|
"status": "",
|
||||||
"straight_time": ""
|
"straight_time": ""
|
||||||
},
|
},
|
||||||
"successes": {
|
"successes": {
|
||||||
@@ -1704,6 +1707,7 @@
|
|||||||
"estimator": "Estimador",
|
"estimator": "Estimador",
|
||||||
"filehandler": "File Handler",
|
"filehandler": "File Handler",
|
||||||
"insurance": "detalles del seguro",
|
"insurance": "detalles del seguro",
|
||||||
|
"more": "Más",
|
||||||
"notes": "Notas",
|
"notes": "Notas",
|
||||||
"parts": "Partes",
|
"parts": "Partes",
|
||||||
"totals": "Totales",
|
"totals": "Totales",
|
||||||
@@ -2621,6 +2625,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": "",
|
||||||
@@ -2894,7 +2899,7 @@
|
|||||||
"jobs-intake": "",
|
"jobs-intake": "",
|
||||||
"jobsavailable": "Empleos disponibles | $t(titles.app)",
|
"jobsavailable": "Empleos disponibles | $t(titles.app)",
|
||||||
"jobsdetail": "Trabajo {{ro_number}} | $t(titles.app)",
|
"jobsdetail": "Trabajo {{ro_number}} | $t(titles.app)",
|
||||||
"jobsdocuments": "Documentos de trabajo {{ro_number}} | $ t (títulos.app)",
|
"jobsdocuments": "Documentos de trabajo {{ro_number}} | $t(titles.app)",
|
||||||
"manageroot": "Casa | $t(titles.app)",
|
"manageroot": "Casa | $t(titles.app)",
|
||||||
"owners": "Todos los propietarios | $t(titles.app)",
|
"owners": "Todos los propietarios | $t(titles.app)",
|
||||||
"owners-detail": "",
|
"owners-detail": "",
|
||||||
@@ -2913,6 +2918,10 @@
|
|||||||
"shop-csi": "",
|
"shop-csi": "",
|
||||||
"shop-templates": "",
|
"shop-templates": "",
|
||||||
"shop_vendors": "Vendedores | $t(titles.app)",
|
"shop_vendors": "Vendedores | $t(titles.app)",
|
||||||
|
"techconsole": "$t(titles.app)",
|
||||||
|
"techjoblookup": "$t(titles.app)",
|
||||||
|
"techjobclock": "$t(titles.app)",
|
||||||
|
"techshiftclock": "$t(titles.app)",
|
||||||
"temporarydocs": "",
|
"temporarydocs": "",
|
||||||
"timetickets": "",
|
"timetickets": "",
|
||||||
"ttapprovals": "",
|
"ttapprovals": "",
|
||||||
|
|||||||
@@ -1001,10 +1001,13 @@
|
|||||||
},
|
},
|
||||||
"labels": {
|
"labels": {
|
||||||
"actions": "",
|
"actions": "",
|
||||||
|
"active": "",
|
||||||
"endmustbeafterstart": "",
|
"endmustbeafterstart": "",
|
||||||
"flat_rate": "",
|
"flat_rate": "",
|
||||||
|
"inactive": "",
|
||||||
"name": "",
|
"name": "",
|
||||||
"rate_type": "",
|
"rate_type": "",
|
||||||
|
"status": "",
|
||||||
"straight_time": ""
|
"straight_time": ""
|
||||||
},
|
},
|
||||||
"successes": {
|
"successes": {
|
||||||
@@ -1704,6 +1707,7 @@
|
|||||||
"estimator": "Estimateur",
|
"estimator": "Estimateur",
|
||||||
"filehandler": "Gestionnaire de fichiers",
|
"filehandler": "Gestionnaire de fichiers",
|
||||||
"insurance": "Détails de l'assurance",
|
"insurance": "Détails de l'assurance",
|
||||||
|
"more": "Plus",
|
||||||
"notes": "Remarques",
|
"notes": "Remarques",
|
||||||
"parts": "les pièces",
|
"parts": "les pièces",
|
||||||
"totals": "Totaux",
|
"totals": "Totaux",
|
||||||
@@ -2621,6 +2625,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": "",
|
||||||
@@ -2894,7 +2899,7 @@
|
|||||||
"jobs-intake": "",
|
"jobs-intake": "",
|
||||||
"jobsavailable": "Emplois disponibles | $t(titles.app)",
|
"jobsavailable": "Emplois disponibles | $t(titles.app)",
|
||||||
"jobsdetail": "Travail {{ro_number}} | $t(titles.app)",
|
"jobsdetail": "Travail {{ro_number}} | $t(titles.app)",
|
||||||
"jobsdocuments": "Documents de travail {{ro_number}} | $ t (titres.app)",
|
"jobsdocuments": "Documents de travail {{ro_number}} | $t(titles.app)",
|
||||||
"manageroot": "Accueil | $t(titles.app)",
|
"manageroot": "Accueil | $t(titles.app)",
|
||||||
"owners": "Tous les propriétaires | $t(titles.app)",
|
"owners": "Tous les propriétaires | $t(titles.app)",
|
||||||
"owners-detail": "",
|
"owners-detail": "",
|
||||||
@@ -2913,6 +2918,10 @@
|
|||||||
"shop-csi": "",
|
"shop-csi": "",
|
||||||
"shop-templates": "",
|
"shop-templates": "",
|
||||||
"shop_vendors": "Vendeurs | $t(titles.app)",
|
"shop_vendors": "Vendeurs | $t(titles.app)",
|
||||||
|
"techconsole": "$t(titles.app)",
|
||||||
|
"techjoblookup": "$t(titles.app)",
|
||||||
|
"techjobclock": "$t(titles.app)",
|
||||||
|
"techshiftclock": "$t(titles.app)",
|
||||||
"temporarydocs": "",
|
"temporarydocs": "",
|
||||||
"timetickets": "",
|
"timetickets": "",
|
||||||
"ttapprovals": "",
|
"ttapprovals": "",
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -2423,6 +2423,73 @@
|
|||||||
_eq: X-Hasura-User-Id
|
_eq: X-Hasura-User-Id
|
||||||
- active:
|
- active:
|
||||||
_eq: true
|
_eq: true
|
||||||
|
- table:
|
||||||
|
name: eula_acceptances
|
||||||
|
schema: public
|
||||||
|
object_relationships:
|
||||||
|
- name: eula
|
||||||
|
using:
|
||||||
|
foreign_key_constraint_on: eulaid
|
||||||
|
- name: user
|
||||||
|
using:
|
||||||
|
foreign_key_constraint_on: useremail
|
||||||
|
insert_permissions:
|
||||||
|
- role: user
|
||||||
|
permission:
|
||||||
|
check:
|
||||||
|
user:
|
||||||
|
authid:
|
||||||
|
_eq: X-Hasura-User-Id
|
||||||
|
columns:
|
||||||
|
- address
|
||||||
|
- buisness_name
|
||||||
|
- date_accepted
|
||||||
|
- eulaid
|
||||||
|
- first_name
|
||||||
|
- last_name
|
||||||
|
- phone_number
|
||||||
|
- useremail
|
||||||
|
select_permissions:
|
||||||
|
- role: user
|
||||||
|
permission:
|
||||||
|
columns:
|
||||||
|
- address
|
||||||
|
- buisness_name
|
||||||
|
- first_name
|
||||||
|
- last_name
|
||||||
|
- phone_number
|
||||||
|
- useremail
|
||||||
|
- created_at
|
||||||
|
- date_accepted
|
||||||
|
- updated_at
|
||||||
|
- eulaid
|
||||||
|
- id
|
||||||
|
filter:
|
||||||
|
user:
|
||||||
|
authid:
|
||||||
|
_eq: X-Hasura-User-Id
|
||||||
|
- table:
|
||||||
|
name: eulas
|
||||||
|
schema: public
|
||||||
|
array_relationships:
|
||||||
|
- name: eula_acceptances
|
||||||
|
using:
|
||||||
|
foreign_key_constraint_on:
|
||||||
|
column: eulaid
|
||||||
|
table:
|
||||||
|
name: eula_acceptances
|
||||||
|
schema: public
|
||||||
|
select_permissions:
|
||||||
|
- role: user
|
||||||
|
permission:
|
||||||
|
columns:
|
||||||
|
- id
|
||||||
|
- created_at
|
||||||
|
- updated_at
|
||||||
|
- effective_date
|
||||||
|
- end_date
|
||||||
|
- content
|
||||||
|
filter: {}
|
||||||
- table:
|
- table:
|
||||||
name: exportlog
|
name: exportlog
|
||||||
schema: public
|
schema: public
|
||||||
@@ -5888,6 +5955,13 @@
|
|||||||
table:
|
table:
|
||||||
name: email_audit_trail
|
name: email_audit_trail
|
||||||
schema: public
|
schema: public
|
||||||
|
- name: eula_acceptances
|
||||||
|
using:
|
||||||
|
foreign_key_constraint_on:
|
||||||
|
column: useremail
|
||||||
|
table:
|
||||||
|
name: eula_acceptances
|
||||||
|
schema: public
|
||||||
- name: exportlogs
|
- name: exportlogs
|
||||||
using:
|
using:
|
||||||
foreign_key_constraint_on:
|
foreign_key_constraint_on:
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
DROP TABLE "public"."eulas";
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
CREATE TABLE "public"."eulas" ("id" uuid NOT NULL DEFAULT gen_random_uuid(), "created_at" timestamptz NOT NULL DEFAULT now(), "updated_at" timestamptz NOT NULL DEFAULT now(), "effective_date" timestamptz NOT NULL, "end_date" timestamptz, "content" text NOT NULL, PRIMARY KEY ("id") );
|
||||||
|
CREATE OR REPLACE FUNCTION "public"."set_current_timestamp_updated_at"()
|
||||||
|
RETURNS TRIGGER AS $$
|
||||||
|
DECLARE
|
||||||
|
_new record;
|
||||||
|
BEGIN
|
||||||
|
_new := NEW;
|
||||||
|
_new."updated_at" = NOW();
|
||||||
|
RETURN _new;
|
||||||
|
END;
|
||||||
|
$$ LANGUAGE plpgsql;
|
||||||
|
CREATE TRIGGER "set_public_eulas_updated_at"
|
||||||
|
BEFORE UPDATE ON "public"."eulas"
|
||||||
|
FOR EACH ROW
|
||||||
|
EXECUTE PROCEDURE "public"."set_current_timestamp_updated_at"();
|
||||||
|
COMMENT ON TRIGGER "set_public_eulas_updated_at" ON "public"."eulas"
|
||||||
|
IS 'trigger to set value of column "updated_at" to current timestamp on row update';
|
||||||
|
CREATE EXTENSION IF NOT EXISTS pgcrypto;
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
DROP TABLE "public"."eula_acceptances";
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
CREATE TABLE "public"."eula_acceptances" ("id" uuid NOT NULL DEFAULT gen_random_uuid(), "created_at" timestamptz NOT NULL DEFAULT now(), "updated_at" timestamptz NOT NULL DEFAULT now(), "eulaid" uuid NOT NULL, "date_accepted" timestamptz NOT NULL, "first_name" text NOT NULL, "last_name" text NOT NULL, "address" text NOT NULL, "phone_number" Text NOT NULL, "buisness_name" Text NOT NULL, "useremail" text NOT NULL, PRIMARY KEY ("id") , FOREIGN KEY ("eulaid") REFERENCES "public"."eulas"("id") ON UPDATE restrict ON DELETE restrict, FOREIGN KEY ("useremail") REFERENCES "public"."users"("email") ON UPDATE restrict ON DELETE restrict);
|
||||||
|
CREATE OR REPLACE FUNCTION "public"."set_current_timestamp_updated_at"()
|
||||||
|
RETURNS TRIGGER AS $$
|
||||||
|
DECLARE
|
||||||
|
_new record;
|
||||||
|
BEGIN
|
||||||
|
_new := NEW;
|
||||||
|
_new."updated_at" = NOW();
|
||||||
|
RETURN _new;
|
||||||
|
END;
|
||||||
|
$$ LANGUAGE plpgsql;
|
||||||
|
CREATE TRIGGER "set_public_eula_acceptances_updated_at"
|
||||||
|
BEFORE UPDATE ON "public"."eula_acceptances"
|
||||||
|
FOR EACH ROW
|
||||||
|
EXECUTE PROCEDURE "public"."set_current_timestamp_updated_at"();
|
||||||
|
COMMENT ON TRIGGER "set_public_eula_acceptances_updated_at" ON "public"."eula_acceptances"
|
||||||
|
IS 'trigger to set value of column "updated_at" to current timestamp on row update';
|
||||||
|
CREATE EXTENSION IF NOT EXISTS pgcrypto;
|
||||||
Reference in New Issue
Block a user