Compare commits

..

1 Commits

Author SHA1 Message Date
Dave Richer
ee8cbe33c4 Pagination Fix 2023-12-04 14:28:44 -05:00
43 changed files with 504 additions and 958 deletions

View File

@@ -10,7 +10,7 @@ import { createStructuredSelector } from "reselect";
import { import {
DELETE_BILL_LINE, DELETE_BILL_LINE,
INSERT_NEW_BILL_LINES, INSERT_NEW_BILL_LINES,
UPDATE_BILL_LINE, UPDATE_BILL_LINE
} from "../../graphql/bill-lines.queries"; } from "../../graphql/bill-lines.queries";
import { QUERY_BILL_BY_PK, UPDATE_BILL } from "../../graphql/bills.queries"; import { QUERY_BILL_BY_PK, UPDATE_BILL } from "../../graphql/bills.queries";
import { insertAuditTrail } from "../../redux/application/application.actions"; import { insertAuditTrail } from "../../redux/application/application.actions";
@@ -20,7 +20,6 @@ import AuditTrailMapping from "../../utils/AuditTrailMappings";
import AlertComponent from "../alert/alert.component"; import AlertComponent from "../alert/alert.component";
import BillFormContainer from "../bill-form/bill-form.container"; import BillFormContainer from "../bill-form/bill-form.container";
import BillMarkExportedButton from "../bill-mark-exported-button/bill-mark-exported-button.component"; import BillMarkExportedButton from "../bill-mark-exported-button/bill-mark-exported-button.component";
import BillPrintButton from "../bill-print-button/bill-print-button.component";
import BillReeportButtonComponent from "../bill-reexport-button/bill-reexport-button.component"; import BillReeportButtonComponent from "../bill-reexport-button/bill-reexport-button.component";
import JobDocumentsGallery from "../jobs-documents-gallery/jobs-documents-gallery.container"; import JobDocumentsGallery from "../jobs-documents-gallery/jobs-documents-gallery.container";
import JobsDocumentsLocalGallery from "../jobs-documents-local-gallery/jobs-documents-local-gallery.container"; import JobsDocumentsLocalGallery from "../jobs-documents-local-gallery/jobs-documents-local-gallery.container";
@@ -177,7 +176,7 @@ export function BillDetailEditcontainer({
extra={ extra={
<Space> <Space>
<BillDetailEditReturn data={data} /> <BillDetailEditReturn data={data} />
<BillPrintButton billid={search.billid} />
<Popconfirm <Popconfirm
visible={visible} visible={visible}
onConfirm={() => form.submit()} onConfirm={() => form.submit()}

View File

@@ -79,20 +79,6 @@ export function BillFormComponent({
}); });
}; };
const handleFederalTaxExemptSwitchToggle = (checked) => {
// Early gate
if (!checked) return;
const values = form.getFieldsValue("billlines");
// Gate bill lines
if (!values?.billlines?.length) return;
const billlines = values.billlines.map((b) => {
b.applicable_taxes.federal = false;
return b;
});
form.setFieldsValue({ billlines });
};
useEffect(() => { useEffect(() => {
if (job) form.validateFields(["is_credit_memo"]); if (job) form.validateFields(["is_credit_memo"]);
}, [job, form]); }, [job, form]);
@@ -401,16 +387,7 @@ export function BillFormComponent({
> >
<CurrencyInput min={0} /> <CurrencyInput min={0} />
</Form.Item> </Form.Item>
{bodyshop.pbs_serialnumber || bodyshop.cdk_dealerid ? ( <Form.Item shouldUpdate span={15}>
<Form.Item
span={2}
label={t("bills.labels.federal_tax_exempt")}
name="federal_tax_exempt"
>
<Switch onChange={handleFederalTaxExemptSwitchToggle} />
</Form.Item>
) : null}
<Form.Item shouldUpdate span={13}>
{() => { {() => {
const values = form.getFieldsValue([ const values = form.getFieldsValue([
"billlines", "billlines",
@@ -428,7 +405,7 @@ export function BillFormComponent({
totals = CalculateBillTotal(values); totals = CalculateBillTotal(values);
if (!!totals) if (!!totals)
return ( return (
<div align="right"> <div>
<Space wrap> <Space wrap>
<Statistic <Statistic
title={t("bills.labels.subtotal")} title={t("bills.labels.subtotal")}

View File

@@ -1,15 +1,14 @@
import { DeleteFilled, DollarCircleFilled } from "@ant-design/icons"; import { DeleteFilled, DollarCircleFilled } from "@ant-design/icons";
import { useTreatments } from "@splitsoftware/splitio-react"; import { useTreatments } from "@splitsoftware/splitio-react";
import { import {
Button, Button, Form,
Form,
Input, Input,
InputNumber, InputNumber,
Select, Select,
Space, Space,
Switch, Switch,
Table, Table,
Tooltip, Tooltip
} from "antd"; } from "antd";
import React from "react"; import React from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
@@ -467,8 +466,7 @@ export function BillEnterModalLinesComponent({
return { return {
key: `${field.index}fedtax`, key: `${field.index}fedtax`,
valuePropName: "checked", valuePropName: "checked",
initialValue: initialValue: true,
form.getFieldValue("federal_tax_exempt") === true ? false : true,
name: [field.name, "applicable_taxes", "federal"], name: [field.name, "applicable_taxes", "federal"],
}; };
}, },

View File

@@ -1,38 +0,0 @@
import { Button, Space } from "antd";
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
import { GenerateDocument } from "../../utils/RenderTemplate";
import { TemplateList } from "../../utils/TemplateConstants";
export default function BillPrintButton({ billid }) {
const { t } = useTranslation();
const [loading, setLoading] = useState(false);
const Templates = TemplateList("job_special");
const submitHandler = async () => {
setLoading(true);
try {
await GenerateDocument(
{
name: Templates.parts_invoice_label_single.key,
variables: {
id: billid,
},
},
{},
"p"
);
} catch (e) {
console.warn("Warning: Error generating a document.");
}
setLoading(false);
};
return (
<Space wrap>
<Button loading={loading} onClick={submitHandler}>
{t("bills.labels.printlabels")}
</Button>
</Space>
);
}

View File

@@ -4,7 +4,6 @@ import PhoneNumberFormatter from "../../utils/PhoneFormatter";
import ChatArchiveButton from "../chat-archive-button/chat-archive-button.component"; import ChatArchiveButton from "../chat-archive-button/chat-archive-button.component";
import ChatConversationTitleTags from "../chat-conversation-title-tags/chat-conversation-title-tags.component"; import ChatConversationTitleTags from "../chat-conversation-title-tags/chat-conversation-title-tags.component";
import ChatLabelComponent from "../chat-label/chat-label.component"; import ChatLabelComponent from "../chat-label/chat-label.component";
import ChatPrintButton from "../chat-print-button/chat-print-button.component";
import ChatTagRoContainer from "../chat-tag-ro/chat-tag-ro.container"; import ChatTagRoContainer from "../chat-tag-ro/chat-tag-ro.container";
export default function ChatConversationTitle({ conversation }) { export default function ChatConversationTitle({ conversation }) {
@@ -14,7 +13,6 @@ export default function ChatConversationTitle({ conversation }) {
{conversation && conversation.phone_num} {conversation && conversation.phone_num}
</PhoneNumberFormatter> </PhoneNumberFormatter>
<ChatLabelComponent conversation={conversation} /> <ChatLabelComponent conversation={conversation} />
<ChatPrintButton conversation={conversation} />
<ChatConversationTitleTags <ChatConversationTitleTags
jobConversations={ jobConversations={
(conversation && conversation.job_conversations) || [] (conversation && conversation.job_conversations) || []

View File

@@ -1,59 +0,0 @@
import { MailOutlined, PrinterOutlined } from "@ant-design/icons";
import { Space, Spin } from "antd";
import React, { useState } from "react";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { setEmailOptions } from "../../redux/email/email.actions";
import { GenerateDocument } from "../../utils/RenderTemplate";
import { TemplateList } from "../../utils/TemplateConstants";
const mapStateToProps = createStructuredSelector({});
const mapDispatchToProps = (dispatch) => ({
setEmailOptions: (e) => dispatch(setEmailOptions(e)),
});
export function ChatPrintButton({ conversation }) {
const [loading, setLoading] = useState(false);
return (
<Space wrap>
<PrinterOutlined
onClick={() => {
setLoading(true);
GenerateDocument(
{
name: TemplateList("messaging").conversation_list.key,
variables: { id: conversation.id },
},
{
subject: TemplateList("messaging").conversation_list.subject,
},
"p",
conversation.id
);
setLoading(false);
}}
/>
<MailOutlined
onClick={() => {
setLoading(true);
GenerateDocument(
{
name: TemplateList("messaging").conversation_list.key,
variables: { id: conversation.id },
},
{
subject: TemplateList("messaging").conversation_list.subject,
},
"e",
conversation.id
);
setLoading(false);
}}
/>
{loading && <Spin />}
</Space>
);
}
export default connect(mapStateToProps, mapDispatchToProps)(ChatPrintButton);

View File

@@ -35,15 +35,6 @@ export default function ContractsCarsComponent({
state.sortedInfo.columnKey === "status" && state.sortedInfo.order, state.sortedInfo.columnKey === "status" && state.sortedInfo.order,
render: (text, record) => <div>{t(record.status)}</div>, render: (text, record) => <div>{t(record.status)}</div>,
}, },
{
title: t("courtesycars.fields.readiness"),
dataIndex: "readiness",
key: "readiness",
sorter: (a, b) => alphaSort(a.readiness, b.readiness),
sortOrder:
state.sortedInfo.columnKey === "readiness" && state.sortedInfo.order,
render: (text, record) => t(record.readiness),
},
{ {
title: t("courtesycars.fields.year"), title: t("courtesycars.fields.year"),
dataIndex: "year", dataIndex: "year",

View File

@@ -7,7 +7,6 @@ import { useTranslation } from "react-i18next";
import { CHECK_CC_FLEET_NUMBER } from "../../graphql/courtesy-car.queries"; import { CHECK_CC_FLEET_NUMBER } from "../../graphql/courtesy-car.queries";
import { DateFormatter } from "../../utils/DateFormatter"; import { DateFormatter } from "../../utils/DateFormatter";
import CourtesyCarFuelSlider from "../courtesy-car-fuel-select/courtesy-car-fuel-select.component"; import CourtesyCarFuelSlider from "../courtesy-car-fuel-select/courtesy-car-fuel-select.component";
import CourtesyCarReadiness from "../courtesy-car-readiness-select/courtesy-car-readiness-select.component";
import CourtesyCarStatus from "../courtesy-car-status-select/courtesy-car-status-select.component"; import CourtesyCarStatus from "../courtesy-car-status-select/courtesy-car-status-select.component";
import FormDatePicker from "../form-date-picker/form-date-picker.component"; import FormDatePicker from "../form-date-picker/form-date-picker.component";
//import FormFieldsChanged from "../form-fields-changed-alert/form-fields-changed-alert.component"; //import FormFieldsChanged from "../form-fields-changed-alert/form-fields-changed-alert.component";
@@ -214,12 +213,6 @@ export default function CourtesyCarCreateFormComponent({ form, saveLoading }) {
> >
<CourtesyCarStatus /> <CourtesyCarStatus />
</Form.Item> </Form.Item>
<Form.Item
label={t("courtesycars.fields.readiness")}
name="readiness"
>
<CourtesyCarReadiness />
</Form.Item>
<div> <div>
<Form.Item <Form.Item
label={t("courtesycars.fields.nextservicekm")} label={t("courtesycars.fields.nextservicekm")}

View File

@@ -34,32 +34,6 @@ const CourtesyCarFuelComponent = (props, ref) => {
step={null} step={null}
style={{ marginLeft: "2rem", marginRight: "2rem" }} style={{ marginLeft: "2rem", marginRight: "2rem" }}
{...props} {...props}
tooltip={{
formatter: (value) => {
switch (value) {
case 0:
return t("courtesycars.labels.fuel.empty");
case 13:
return t("courtesycars.labels.fuel.18");
case 25:
return t("courtesycars.labels.fuel.14");
case 38:
return t("courtesycars.labels.fuel.38");
case 50:
return t("courtesycars.labels.fuel.12");
case 63:
return t("courtesycars.labels.fuel.58");
case 75:
return t("courtesycars.labels.fuel.34");
case 88:
return t("courtesycars.labels.fuel.78");
case 100:
return t("courtesycars.labels.fuel.full");
default:
return value;
}
},
}}
/> />
); );
}; };

View File

@@ -1,35 +0,0 @@
import { Select } from "antd";
import React, { forwardRef, useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
const { Option } = Select;
const CourtesyCarReadinessComponent = ({ value, onChange }, ref) => {
const [option, setOption] = useState(value);
const { t } = useTranslation();
useEffect(() => {
if (value !== option && onChange) {
onChange(option);
}
}, [value, option, onChange]);
return (
<Select
allowClear
ref={ref}
value={option}
style={{
width: 100,
}}
onChange={setOption}
>
<Option value="courtesycars.readiness.ready">
{t("courtesycars.readiness.ready")}
</Option>
<Option value="courtesycars.readiness.notready">
{t("courtesycars.readiness.notready")}
</Option>
</Select>
);
};
export default forwardRef(CourtesyCarReadinessComponent);

View File

@@ -91,26 +91,6 @@ export default function CourtesyCarsList({ loading, courtesycars, refetch }) {
); );
}, },
}, },
{
title: t("courtesycars.fields.readiness"),
dataIndex: "readiness",
key: "readiness",
sorter: (a, b) => alphaSort(a.readiness, b.readiness),
filters: [
{
text: t("courtesycars.readiness.ready"),
value: "courtesycars.readiness.ready",
},
{
text: t("courtesycars.readiness.notready"),
value: "courtesycars.readiness.notready",
},
],
onFilter: (value, record) => value.includes(record.readiness),
sortOrder:
state.sortedInfo.columnKey === "readiness" && state.sortedInfo.order,
render: (text, record) => t(record.readiness),
},
{ {
title: t("courtesycars.fields.year"), title: t("courtesycars.fields.year"),
dataIndex: "year", dataIndex: "year",
@@ -151,36 +131,6 @@ export default function CourtesyCarsList({ loading, courtesycars, refetch }) {
sortOrder: sortOrder:
state.sortedInfo.columnKey === "plate" && state.sortedInfo.order, state.sortedInfo.columnKey === "plate" && state.sortedInfo.order,
}, },
{
title: t("courtesycars.fields.fuel"),
dataIndex: "fuel",
key: "fuel",
sorter: (a, b) => alphaSort(a.fuel, b.fuel),
sortOrder:
state.sortedInfo.columnKey === "fuel" && state.sortedInfo.order,
render: (text, record) => {
switch (record.fuel) {
case 100:
return t("courtesycars.labels.fuel.full");
case 88:
return t("courtesycars.labels.fuel.78");
case 63:
return t("courtesycars.labels.fuel.58");
case 50:
return t("courtesycars.labels.fuel.12");
case 38:
return t("courtesycars.labels.fuel.34");
case 25:
return t("courtesycars.labels.fuel.14");
case 13:
return t("courtesycars.labels.fuel.18");
case 0:
return t("courtesycars.labels.fuel.empty");
default:
return record.fuel;
}
},
},
{ {
title: t("courtesycars.labels.outwith"), title: t("courtesycars.labels.outwith"),
dataIndex: "outwith", dataIndex: "outwith",

View File

@@ -108,14 +108,6 @@ export function JobsDetailHeaderActions({
}, },
}, },
}); });
insertAuditTrail({
jobid: job.id,
operation: AuditTrailMapping.alertToggle(
!!job.production_vars && !!job.production_vars.alert
? !job.production_vars.alert
: true
),
});
}; };
const handleSuspend = (e) => { const handleSuspend = (e) => {

View File

@@ -1,8 +1,8 @@
import { import {
BranchesOutlined,
ExclamationCircleFilled, ExclamationCircleFilled,
PauseCircleOutlined, PauseCircleOutlined,
WarningFilled, WarningFilled,
BranchesOutlined,
} from "@ant-design/icons"; } from "@ant-design/icons";
import { Card, Col, Row, Space, Tag, Tooltip } from "antd"; import { Card, Col, Row, Space, Tag, Tooltip } from "antd";
import React, { useState } from "react"; import React, { useState } from "react";
@@ -222,9 +222,6 @@ export function JobsDetailHeader({ job, bodyshop, disabled }) {
{`${job.v_vin || t("general.labels.na")}`} {`${job.v_vin || t("general.labels.na")}`}
</VehicleVinDisplay> </VehicleVinDisplay>
</DataLabel> </DataLabel>
<DataLabel label={t("jobs.fields.regie_number")}>
{job.regie_number || t("general.labels.na")}
</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} />
</DataLabel> </DataLabel>

View File

@@ -1,105 +1,117 @@
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, Typography} 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, {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, useHistory, useLocation } from "react-router-dom"; 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_PAGINATED} 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 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 {flattenDeep} from "lodash";
import { pageLimit } from '../../utils/config';
import axios from "axios";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop, bodyshop: selectBodyshop,
}); });
export function JobsList({ bodyshop }) { const mapDispatchToProps = () => ({
const searchParams = queryString.parse(useLocation().search); });
const { selected } = searchParams;
export function JobsList({bodyshop,}) {
const search = queryString.parse(useLocation().search);
const [openSearchResults, setOpenSearchResults] = useState([]);
const [searchLoading, setSearchLoading] = useState(false);
const {page, selected, sortorder, sortcolumn,statusFilters} = search;
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_PAGINATED, {
variables: { variables: {
statuses: bodyshop.md_ro_statuses.active_statuses || ["Open", "Open*"], offset: page ? (page - 1) * pageLimit : 0,
limit: pageLimit,
statusFilters: statusFilters ? JSON.parse(statusFilters) : bodyshop.md_ro_statuses.active_statuses || ["Open", "Open*"],
order: [
{
[sortcolumn || "ro_number"]:
sortorder && sortorder !== "false"
? (sortorder === "descend"
? "desc"
: "asc")
: "desc",
},
],
}, },
fetchPolicy: "network-only", fetchPolicy: "network-only",
nextFetchPolicy: "network-only", nextFetchPolicy: "network-only",
}); });
const total = data?.jobs_aggregate?.aggregate?.count || 0;
const [state, setState] = useState({ const [state, setState] = useState({
sortedInfo: {}, filteredInfo: {text: ""},
filteredInfo: { text: "" },
}); });
const { t } = useTranslation(); const {t} = useTranslation();
const history = useHistory(); const history = useHistory();
const [searchText, setSearchText] = useState("");
if (error) return <AlertComponent message={error.message} type="error" />; const jobs = data?.jobs || [];
const jobs = data
? searchText === ""
? data.jobs
: data.jobs.filter(
(j) =>
(j.ro_number || "")
.toString()
.toLowerCase()
.includes(searchText.toLowerCase()) ||
(j.ownr_co_nm || "")
.toLowerCase()
.includes(searchText.toLowerCase()) ||
(j.comments || "")
.toLowerCase()
.includes(searchText.toLowerCase()) ||
(j.ownr_fn || "")
.toLowerCase()
.includes(searchText.toLowerCase()) ||
(j.ownr_ln || "")
.toLowerCase()
.includes(searchText.toLowerCase()) ||
(j.clm_no || "").toLowerCase().includes(searchText.toLowerCase()) ||
(j.plate_no || "")
.toLowerCase()
.includes(searchText.toLowerCase()) ||
(j.v_model_desc || "")
.toLowerCase()
.includes(searchText.toLowerCase()) ||
(j.est_ct_fn || "")
.toLowerCase()
.includes(searchText.toLowerCase()) ||
(j.est_ct_ln || "")
.toLowerCase()
.includes(searchText.toLowerCase()) ||
(j.v_make_desc || "")
.toLowerCase()
.includes(searchText.toLowerCase())
)
: [];
const handleTableChange = (pagination, filters, sorter) => { const handleTableChange = (pagination, filters, sorter) => {
setState({ ...state, filteredInfo: filters, sortedInfo: sorter }); search.page = pagination.current;
search.sortcolumn = sorter.column && sorter.column.key;
search.sortorder = sorter.order;
if (filters.status) {
search.statusFilters = JSON.stringify(flattenDeep(filters.status));
} else {
delete search.statusFilters;
}
history.push({search: queryString.stringify(search)});
}; };
useEffect(() => {
if (search.search && search.search.trim() !== "") {
searchJobs().catch(e => {
console.error('Something went wrong searching for jobs in the job-list component', e);
});
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
if (error) return <AlertComponent message={error.message} type="error"/>;
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);
}
}
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, ...search,
selected: record.id, selected: record.id,
}), }),
}); });
@@ -112,11 +124,9 @@ export function JobsList({ bodyshop }) {
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: true,
parseInt((a.ro_number || "0").replace(/\D/g, "")) -
parseInt((b.ro_number || "0").replace(/\D/g, "")),
sortOrder: sortOrder:
state.sortedInfo.columnKey === "ro_number" && state.sortedInfo.order, sortcolumn === "ro_number" && sortorder,
render: (text, record) => ( render: (text, record) => (
<Link <Link
@@ -126,14 +136,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>
@@ -145,22 +155,20 @@ export function JobsList({ 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: false,
sortOrder: sortOrder: sortcolumn === "owner" && sortorder,
state.sortedInfo.columnKey === "owner" && state.sortedInfo.order,
render: (text, record) => { render: (text, record) => {
return record.ownerid ? ( return record.ownerid ? (
<Link <Link
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 +180,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 +190,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}/>
), ),
}, },
@@ -191,25 +199,17 @@ export function JobsList({ bodyshop }) {
dataIndex: "status", dataIndex: "status",
key: "status", key: "status",
ellipsis: true, ellipsis: true,
sorter: true,
sorter: (a, b) => alphaSort(a.status, b.status),
sortOrder: sortOrder:
state.sortedInfo.columnKey === "status" && state.sortedInfo.order, sortcolumn === "status" && sortorder,
filters: filters:bodyshop.md_ro_statuses.statuses.map((s) => {
(jobs && return { text: s, value: [s] };
jobs }),
.map((j) => j.status)
.filter(onlyUnique)
.map((s) => {
return {
text: s || "No Status*",
value: [s],
};
})) ||
[],
onFilter: (value, record) => value.includes(record.status), onFilter: (value, record) => value.includes(record.status),
render: (text, record) => {
return record.status || t("general.labels.na");
},
}, },
{ {
title: t("jobs.fields.vehicle"), title: t("jobs.fields.vehicle"),
dataIndex: "vehicle", dataIndex: "vehicle",
@@ -237,11 +237,10 @@ export function JobsList({ bodyshop }) {
dataIndex: "plate_no", dataIndex: "plate_no",
key: "plate_no", key: "plate_no",
ellipsis: true, ellipsis: true,
responsive: ["md"], responsive: ["md"],
sorter: (a, b) => alphaSort(a.plate_no, b.plate_no), sorter: true,
sortOrder: sortOrder:
state.sortedInfo.columnKey === "plate_no" && state.sortedInfo.order, sortcolumn === "plate_no" && sortorder,
}, },
{ {
title: t("jobs.fields.clm_no"), title: t("jobs.fields.clm_no"),
@@ -249,9 +248,9 @@ export function JobsList({ bodyshop }) {
key: "clm_no", key: "clm_no",
ellipsis: true, ellipsis: true,
responsive: ["md"], responsive: ["md"],
sorter: (a, b) => alphaSort(a.clm_no, b.clm_no), sorter: true,
sortOrder: sortOrder:
state.sortedInfo.columnKey === "clm_no" && state.sortedInfo.order, 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})` : ""
@@ -262,19 +261,20 @@ export function JobsList({ bodyshop }) {
dataIndex: "ins_co_nm", dataIndex: "ins_co_nm",
key: "ins_co_nm", key: "ins_co_nm",
ellipsis: true, ellipsis: true,
filters: // TODO: Restore Filters?
(jobs && // filters:
jobs // (jobs &&
.map((j) => j.ins_co_nm) // jobs
.filter(onlyUnique) // .map((j) => j.ins_co_nm)
.map((s) => { // .filter(onlyUnique)
return { // .map((s) => {
text: s, // return {
value: [s], // text: s,
}; // value: [s],
})) || // };
[], // })) ||
onFilter: (value, record) => value.includes(record.ins_co_nm), // [],
// onFilter: (value, record) => value.includes(record.ins_co_nm),
responsive: ["md"], responsive: ["md"],
}, },
{ {
@@ -283,10 +283,9 @@ export function JobsList({ bodyshop }) {
key: "clm_total", key: "clm_total",
responsive: ["md"], responsive: ["md"],
ellipsis: true, ellipsis: true,
sorter: true,
sorter: (a, b) => a.clm_total - b.clm_total,
sortOrder: sortOrder:
state.sortedInfo.columnKey === "clm_total" && state.sortedInfo.order, sortcolumn === "clm_total" && sortorder,
render: (text, record) => ( render: (text, record) => (
<CurrencyFormatter>{record.clm_total}</CurrencyFormatter> <CurrencyFormatter>{record.clm_total}</CurrencyFormatter>
), ),
@@ -297,23 +296,24 @@ export function JobsList({ bodyshop }) {
key: "jobs.labels.estimator", key: "jobs.labels.estimator",
ellipsis: true, ellipsis: true,
responsive: ["xl"], responsive: ["xl"],
filterSearch: true, // TODO Restore Filters?
filters: // filterSearch: true,
(jobs && // filters:
jobs // (jobs &&
.map((j) => `${j.est_ct_fn || ""} ${j.est_ct_ln || ""}`.trim()) // jobs
.filter(onlyUnique) // .map((j) => `${j.est_ct_fn || ""} ${j.est_ct_ln || ""}`.trim())
.map((s) => { // .filter(onlyUnique)
return { // .map((s) => {
text: s || "N/A", // return {
value: [s], // text: s || "N/A",
}; // value: [s],
})) || // };
[], // })) ||
onFilter: (value, record) => // [],
value.includes( // onFilter: (value, record) =>
`${record.est_ct_fn || ""} ${record.est_ct_ln || ""}`.trim() // value.includes(
), // `${record.est_ct_fn || ""} ${record.est_ct_ln || ""}`.trim()
// ),
render: (text, record) => render: (text, record) =>
`${record.est_ct_fn || ""} ${record.est_ct_ln || ""}`.trim(), `${record.est_ct_fn || ""} ${record.est_ct_ln || ""}`.trim(),
}, },
@@ -349,26 +349,57 @@ export function JobsList({ bodyshop }) {
title={t("titles.bc.jobs-active")} title={t("titles.bc.jobs-active")}
extra={ extra={
<Space wrap> <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.push({ search: queryString.stringify(search) });
}}
>
{t("general.actions.clear")}
</Button>
</>
)}
<Button onClick={() => refetch()}> <Button onClick={() => refetch()}>
<SyncOutlined /> <SyncOutlined />
</Button> </Button>
<Input.Search <Input.Search
placeholder={t("general.labels.search")} placeholder={search.search || t("general.labels.search")}
onChange={(e) => { onSearch={(value) => {
setSearchText(e.target.value); search.search = value;
history.push({ search: queryString.stringify(search) });
searchJobs(value);
}} }}
value={searchText} loading={loading || searchLoading}
enterButton enterButton
/> />
</Space> </Space>
} }
> >
<Table <Table
loading={loading} loading={loading || searchLoading }
pagination={{ defaultPageSize: 50 }} pagination={
search?.search
? {
pageSize: pageLimit,
showSizeChanger: false,
}
: {
pageSize: pageLimit,
current: parseInt(page || 1),
total: total,
showSizeChanger: false,
}
}
columns={columns} columns={columns}
rowKey="id" rowKey="id"
dataSource={jobs} dataSource={search?.search ? openSearchResults : jobs}
scroll={{ scroll={{
x: selectedBreakpoint ? scrollMapper[selectedBreakpoint[0]] : "100%", x: selectedBreakpoint ? scrollMapper[selectedBreakpoint[0]] : "100%",
}} }}
@@ -380,9 +411,9 @@ export function JobsList({ bodyshop }) {
type: "radio", type: "radio",
}} }}
onChange={handleTableChange} onChange={handleTableChange}
onRow={(record, rowIndex) => { onRow={(record) => {
return { return {
onClick: (event) => { onClick: () => {
handleOnRowClick(record); handleOnRowClick(record);
}, },
}; };
@@ -392,4 +423,4 @@ export function JobsList({ bodyshop }) {
); );
} }
export default connect(mapStateToProps, null)(JobsList); export default connect(mapStateToProps, mapDispatchToProps)(JobsList);

View File

@@ -1,23 +1,12 @@
import { ExclamationCircleFilled } from "@ant-design/icons"; import { ExclamationCircleFilled } from "@ant-design/icons";
import { useMutation } from "@apollo/client";
import { Dropdown, Menu } from "antd"; import { Dropdown, Menu } from "antd";
import React from "react"; import React from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { connect } from "react-redux"; import { useMutation } from "@apollo/client";
import { createStructuredSelector } from "reselect";
import { logImEXEvent } from "../../firebase/firebase.utils";
import { UPDATE_JOB } from "../../graphql/jobs.queries"; import { UPDATE_JOB } from "../../graphql/jobs.queries";
import { insertAuditTrail } from "../../redux/application/application.actions"; import { logImEXEvent } from "../../firebase/firebase.utils";
import AuditTrailMapping from "../../utils/AuditTrailMappings";
const mapStateToProps = createStructuredSelector({}); export default function ProductionListColumnAlert({ record }) {
const mapDispatchToProps = (dispatch) => ({
insertAuditTrail: ({ jobid, operation }) =>
dispatch(insertAuditTrail({ jobid, operation })),
});
export function ProductionListColumnAlert({ record, insertAuditTrail }) {
const { t } = useTranslation(); const { t } = useTranslation();
const [updateAlert] = useMutation(UPDATE_JOB); const [updateAlert] = useMutation(UPDATE_JOB);
@@ -38,14 +27,6 @@ export function ProductionListColumnAlert({ record, insertAuditTrail }) {
}, },
}, },
}, },
});
insertAuditTrail({
jobid: record.id,
operation: AuditTrailMapping.alertToggle(
!!record.production_vars && !!record.production_vars.alert
? !record.production_vars.alert
: true
),
}).then(() => { }).then(() => {
if (record.refetch) record.refetch(); if (record.refetch) record.refetch();
}); });
@@ -77,8 +58,3 @@ export function ProductionListColumnAlert({ record, insertAuditTrail }) {
</Dropdown> </Dropdown>
); );
} }
export default connect(
mapStateToProps,
mapDispatchToProps
)(ProductionListColumnAlert);

View File

@@ -55,11 +55,10 @@ const ret = {
"shiftclock:view": 2, "shiftclock:view": 2,
"shop:config": 4, "shop:config": 4,
"shop:dashboard": 3,
"shop:rbac": 5, "shop:rbac": 5,
"shop:reportcenter": 2,
"shop:templates": 4,
"shop:vendors": 2, "shop:vendors": 2,
"shop:dashboard": 3,
"shop:templates": 4,
"temporarydocs:view": 2, "temporarydocs:view": 2,

View File

@@ -5,7 +5,6 @@ import { connect } from "react-redux";
import { createStructuredSelector } from "reselect"; import { createStructuredSelector } from "reselect";
import { toggleModalVisible } from "../../redux/modals/modals.actions"; import { toggleModalVisible } from "../../redux/modals/modals.actions";
import { selectReportCenter } from "../../redux/modals/modals.selectors"; import { selectReportCenter } from "../../redux/modals/modals.selectors";
import RbacWrapperComponent from "../rbac-wrapper/rbac-wrapper.component";
import ReportCenterModalComponent from "./report-center-modal.component"; import ReportCenterModalComponent from "./report-center-modal.component";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
@@ -34,9 +33,7 @@ export function ReportCenterModalContainer({
destroyOnClose destroyOnClose
width="80%" width="80%"
> >
<RbacWrapperComponent action="shop:reportcenter"> <ReportCenterModalComponent />
<ReportCenterModalComponent />
</RbacWrapperComponent>
</Modal> </Modal>
); );
} }

View File

@@ -29,7 +29,7 @@ export default connect(
export function ScoreboardTimeTicketsStats({ bodyshop }) { export function ScoreboardTimeTicketsStats({ bodyshop }) {
const { t } = useTranslation(); const { t } = useTranslation();
const startDate = moment().startOf("month"); const startDate = moment().startOf("month")
const endDate = moment().endOf("month"); const endDate = moment().endOf("month");
const fixedPeriods = useMemo(() => { const fixedPeriods = useMemo(() => {
@@ -84,8 +84,6 @@ export function ScoreboardTimeTicketsStats({ bodyshop }) {
end: endDate.format("YYYY-MM-DD"), end: endDate.format("YYYY-MM-DD"),
fixedStart: fixedPeriods.start.format("YYYY-MM-DD"), fixedStart: fixedPeriods.start.format("YYYY-MM-DD"),
fixedEnd: fixedPeriods.end.format("YYYY-MM-DD"), fixedEnd: fixedPeriods.end.format("YYYY-MM-DD"),
jobStart: startDate,
jobEnd: endDate,
}, },
fetchPolicy: "network-only", fetchPolicy: "network-only",
nextFetchPolicy: "network-only", nextFetchPolicy: "network-only",
@@ -342,21 +340,11 @@ export function ScoreboardTimeTicketsStats({ bodyshop }) {
larData.push({ ...r, ...lar }); larData.push({ ...r, ...lar });
}); });
const jobData = {};
data.jobs.forEach((job) => {
job.tthrs = job.joblines.reduce((acc, val) => acc + val.mod_lb_hrs, 0);
});
jobData.tthrs = data.jobs
.reduce((acc, val) => acc + val.tthrs, 0)
.toFixed(1);
jobData.count = data.jobs.length.toFixed(0);
return { return {
fixed: ret, fixed: ret,
combinedData: combinedData, combinedData: combinedData,
labData: labData, labData: labData,
larData: larData, larData: larData,
jobData: jobData,
}; };
}, [fixedPeriods, data, bodyshop]); }, [fixedPeriods, data, bodyshop]);
@@ -368,10 +356,7 @@ export function ScoreboardTimeTicketsStats({ bodyshop }) {
<ScoreboardTimeticketsTargetsTable /> <ScoreboardTimeticketsTargetsTable />
</Col> </Col>
<Col span={24}> <Col span={24}>
<ScoreboardTicketsStats <ScoreboardTicketsStats data={calculatedData.fixed} />
data={calculatedData.fixed}
jobData={calculatedData.jobData}
/>
</Col> </Col>
<Col span={24}> <Col span={24}>
<ScoreboardTimeTicketsChart <ScoreboardTimeTicketsChart

View File

@@ -41,7 +41,7 @@ function useLocalStorage(key, initialValue) {
return [storedValue, setStoredValue]; return [storedValue, setStoredValue];
} }
export function ScoreboardTicketsStats({ data, jobData, bodyshop }) { export function ScoreboardTicketsStats({ data, bodyshop }) {
const { t } = useTranslation(); const { t } = useTranslation();
const [isLarge, setIsLarge] = useLocalStorage("isLargeStatistic", false); const [isLarge, setIsLarge] = useLocalStorage("isLargeStatistic", false);
@@ -408,7 +408,7 @@ export function ScoreboardTicketsStats({ data, jobData, bodyshop }) {
{/* Monthly Stats */} {/* Monthly Stats */}
<Row gutter={[16, 16]}> <Row gutter={[16, 16]}>
{/* This Month */} {/* This Month */}
<Col span={7} align="center"> <Col span={8} align="center">
<Card size="small" title={t("scoreboard.labels.thismonth")}> <Card size="small" title={t("scoreboard.labels.thismonth")}>
<Row gutter={[16, 16]}> <Row gutter={[16, 16]}>
<Col span={24}> <Col span={24}>
@@ -482,7 +482,7 @@ export function ScoreboardTicketsStats({ data, jobData, bodyshop }) {
</Card> </Card>
</Col> </Col>
{/* Last Month */} {/* Last Month */}
<Col span={7} align="center"> <Col span={8} align="center">
<Card size="small" title={t("scoreboard.labels.lastmonth")}> <Card size="small" title={t("scoreboard.labels.lastmonth")}>
<Row gutter={[16, 16]}> <Row gutter={[16, 16]}>
<Col span={24}> <Col span={24}>
@@ -556,7 +556,7 @@ export function ScoreboardTicketsStats({ data, jobData, bodyshop }) {
</Card> </Card>
</Col> </Col>
{/* Efficiency Over Period */} {/* Efficiency Over Period */}
<Col span={7} align="center"> <Col span={8} align="center">
<Card <Card
size="small" size="small"
title={t("scoreboard.labels.efficiencyoverperiod")} title={t("scoreboard.labels.efficiencyoverperiod")}
@@ -604,40 +604,6 @@ export function ScoreboardTicketsStats({ data, jobData, bodyshop }) {
</Row> </Row>
</Card> </Card>
</Col> </Col>
<Col span={3} align="center">
<Card
size="small"
title={t("scoreboard.labels.jobscompletednotinvoiced")}
>
<Row gutter={[16, 16]}>
<Col span={24}>
<Statistic
value={jobData.count}
valueStyle={{
fontSize: statisticSize,
fontWeight: statisticWeight,
}}
/>
</Col>
</Row>
<Row gutter={[16, 16]}>
<Col span={24}>
<Statistic
title={
<Typography.Text strong>
{t("scoreboard.labels.totalhrs")}
</Typography.Text>
}
value={jobData.tthrs}
valueStyle={{
fontSize: statisticSize,
fontWeight: statisticWeight,
}}
/>
</Col>
</Row>
</Card>
</Col>
</Row> </Row>
</Space> </Space>
{/* Disclaimer */} {/* Disclaimer */}

View File

@@ -65,8 +65,6 @@ export default function ScoreboardTimeTickets() {
end: endDate.format("YYYY-MM-DD"), end: endDate.format("YYYY-MM-DD"),
fixedStart: fixedPeriods.start.format("YYYY-MM-DD"), fixedStart: fixedPeriods.start.format("YYYY-MM-DD"),
fixedEnd: fixedPeriods.end.format("YYYY-MM-DD"), fixedEnd: fixedPeriods.end.format("YYYY-MM-DD"),
jobStart: startDate,
jobEnd: endDate,
}, },
fetchPolicy: "network-only", fetchPolicy: "network-only",
nextFetchPolicy: "network-only", nextFetchPolicy: "network-only",

View File

@@ -28,18 +28,6 @@ export function ShopInfoRbacComponent({ form, bodyshop }) {
return ( return (
<RbacWrapper action="shop:rbac"> <RbacWrapper action="shop:rbac">
<LayoutFormRow> <LayoutFormRow>
<Form.Item
label={t("bodyshop.fields.rbac.accounting.exportlog")}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
name={["md_rbac", "accounting:exportlog"]}
>
<InputNumber />
</Form.Item>
<Form.Item <Form.Item
label={t("bodyshop.fields.rbac.accounting.payables")} label={t("bodyshop.fields.rbac.accounting.payables")}
rules={[ rules={[
@@ -52,6 +40,18 @@ export function ShopInfoRbacComponent({ form, bodyshop }) {
> >
<InputNumber /> <InputNumber />
</Form.Item> </Form.Item>
<Form.Item
label={t("bodyshop.fields.rbac.accounting.exportlog")}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
name={["md_rbac", "accounting:exportlog"]}
>
<InputNumber />
</Form.Item>
<Form.Item <Form.Item
label={t("bodyshop.fields.rbac.accounting.payments")} label={t("bodyshop.fields.rbac.accounting.payments")}
rules={[ rules={[
@@ -77,62 +77,26 @@ export function ShopInfoRbacComponent({ form, bodyshop }) {
<InputNumber /> <InputNumber />
</Form.Item> </Form.Item>
<Form.Item <Form.Item
label={t("bodyshop.fields.rbac.bills.delete")} label={t("bodyshop.fields.rbac.csi.page")}
rules={[ rules={[
{ {
required: true, required: true,
//message: t("general.validation.required"), //message: t("general.validation.required"),
}, },
]} ]}
name={["md_rbac", "bills:delete"]} name={["md_rbac", "csi:page"]}
> >
<InputNumber /> <InputNumber />
</Form.Item> </Form.Item>
<Form.Item <Form.Item
label={t("bodyshop.fields.rbac.bills.enter")} label={t("bodyshop.fields.rbac.csi.export")}
rules={[ rules={[
{ {
required: true, required: true,
//message: t("general.validation.required"), //message: t("general.validation.required"),
}, },
]} ]}
name={["md_rbac", "bills:enter"]} name={["md_rbac", "csi:export"]}
>
<InputNumber />
</Form.Item>
<Form.Item
label={t("bodyshop.fields.rbac.bills.list")}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
name={["md_rbac", "bills:list"]}
>
<InputNumber />
</Form.Item>
<Form.Item
label={t("bodyshop.fields.rbac.bills.reexport")}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
name={["md_rbac", "bills:reexport"]}
>
<InputNumber />
</Form.Item>
<Form.Item
label={t("bodyshop.fields.rbac.bills.view")}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
name={["md_rbac", "bills:view"]}
> >
<InputNumber /> <InputNumber />
</Form.Item> </Form.Item>
@@ -209,38 +173,26 @@ export function ShopInfoRbacComponent({ form, bodyshop }) {
<InputNumber /> <InputNumber />
</Form.Item> </Form.Item>
<Form.Item <Form.Item
label={t("bodyshop.fields.rbac.csi.export")} label={t("bodyshop.fields.rbac.jobs.list-active")}
rules={[ rules={[
{ {
required: true, required: true,
//message: t("general.validation.required"), //message: t("general.validation.required"),
}, },
]} ]}
name={["md_rbac", "csi:export"]} name={["md_rbac", "jobs:list-active"]}
> >
<InputNumber /> <InputNumber />
</Form.Item> </Form.Item>
<Form.Item <Form.Item
label={t("bodyshop.fields.rbac.csi.page")} label={t("bodyshop.fields.rbac.jobs.list-ready")}
rules={[ rules={[
{ {
required: true, required: true,
//message: t("general.validation.required"), //message: t("general.validation.required"),
}, },
]} ]}
name={["md_rbac", "csi:page"]} name={["md_rbac", "jobs:list-ready"]}
>
<InputNumber />
</Form.Item>
<Form.Item
label={t("bodyshop.fields.rbac.employees.page")}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
name={["md_rbac", "employees:page"]}
> >
<InputNumber /> <InputNumber />
</Form.Item> </Form.Item>
@@ -256,6 +208,30 @@ export function ShopInfoRbacComponent({ form, bodyshop }) {
> >
<InputNumber /> <InputNumber />
</Form.Item> </Form.Item>
<Form.Item
label={t("bodyshop.fields.rbac.jobs.partsqueue")}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
name={["md_rbac", "jobs:partsqueue"]}
>
<InputNumber />
</Form.Item>
<Form.Item
label={t("bodyshop.fields.rbac.jobs.list-all")}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
name={["md_rbac", "jobs:list-all"]}
>
<InputNumber />
</Form.Item>
<Form.Item <Form.Item
label={t("bodyshop.fields.rbac.jobs.available-list")} label={t("bodyshop.fields.rbac.jobs.available-list")}
rules={[ rules={[
@@ -269,14 +245,26 @@ export function ShopInfoRbacComponent({ form, bodyshop }) {
<InputNumber /> <InputNumber />
</Form.Item> </Form.Item>
<Form.Item <Form.Item
label={t("bodyshop.fields.rbac.jobs.checklist-view")} label={t("bodyshop.fields.rbac.jobs.create")}
rules={[ rules={[
{ {
required: true, required: true,
//message: t("general.validation.required"), //message: t("general.validation.required"),
}, },
]} ]}
name={["md_rbac", "jobs:checklist-view"]} name={["md_rbac", "jobs:create"]}
>
<InputNumber />
</Form.Item>
<Form.Item
label={t("bodyshop.fields.rbac.jobs.intake")}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
name={["md_rbac", "jobs:intake"]}
> >
<InputNumber /> <InputNumber />
</Form.Item> </Form.Item>
@@ -292,18 +280,6 @@ export function ShopInfoRbacComponent({ form, bodyshop }) {
> >
<InputNumber /> <InputNumber />
</Form.Item> </Form.Item>
<Form.Item
label={t("bodyshop.fields.rbac.jobs.create")}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
name={["md_rbac", "jobs:create"]}
>
<InputNumber />
</Form.Item>
<Form.Item <Form.Item
label={t("bodyshop.fields.rbac.jobs.deliver")} label={t("bodyshop.fields.rbac.jobs.deliver")}
rules={[ rules={[
@@ -329,62 +305,14 @@ export function ShopInfoRbacComponent({ form, bodyshop }) {
<InputNumber /> <InputNumber />
</Form.Item> </Form.Item>
<Form.Item <Form.Item
label={t("bodyshop.fields.rbac.jobs.intake")} label={t("bodyshop.fields.rbac.jobs.checklist-view")}
rules={[ rules={[
{ {
required: true, required: true,
//message: t("general.validation.required"), //message: t("general.validation.required"),
}, },
]} ]}
name={["md_rbac", "jobs:intake"]} name={["md_rbac", "jobs:checklist-view"]}
>
<InputNumber />
</Form.Item>
<Form.Item
label={t("bodyshop.fields.rbac.jobs.list-active")}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
name={["md_rbac", "jobs:list-active"]}
>
<InputNumber />
</Form.Item>
<Form.Item
label={t("bodyshop.fields.rbac.jobs.list-all")}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
name={["md_rbac", "jobs:list-all"]}
>
<InputNumber />
</Form.Item>
<Form.Item
label={t("bodyshop.fields.rbac.jobs.list-ready")}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
name={["md_rbac", "jobs:list-ready"]}
>
<InputNumber />
</Form.Item>
<Form.Item
label={t("bodyshop.fields.rbac.jobs.partsqueue")}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
name={["md_rbac", "jobs:partsqueue"]}
> >
<InputNumber /> <InputNumber />
</Form.Item> </Form.Item>
@@ -401,14 +329,74 @@ export function ShopInfoRbacComponent({ form, bodyshop }) {
<InputNumber /> <InputNumber />
</Form.Item> </Form.Item>
<Form.Item <Form.Item
label={t("bodyshop.fields.rbac.owners.detail")} label={t("bodyshop.fields.rbac.bills.enter")}
rules={[ rules={[
{ {
required: true, required: true,
//message: t("general.validation.required"), //message: t("general.validation.required"),
}, },
]} ]}
name={["md_rbac", "owners:detail"]} name={["md_rbac", "bills:enter"]}
>
<InputNumber />
</Form.Item>
<Form.Item
label={t("bodyshop.fields.rbac.bills.delete")}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
name={["md_rbac", "bills:delete"]}
>
<InputNumber />
</Form.Item>
<Form.Item
label={t("bodyshop.fields.rbac.bills.reexport")}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
name={["md_rbac", "bills:reexport"]}
>
<InputNumber />
</Form.Item>
<Form.Item
label={t("bodyshop.fields.rbac.bills.view")}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
name={["md_rbac", "bills:view"]}
>
<InputNumber />
</Form.Item>
<Form.Item
label={t("bodyshop.fields.rbac.bills.list")}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
name={["md_rbac", "bills:list"]}
>
<InputNumber />
</Form.Item>
<Form.Item
label={t("bodyshop.fields.rbac.employees.page")}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
name={["md_rbac", "employees:page"]}
> >
<InputNumber /> <InputNumber />
</Form.Item> </Form.Item>
@@ -424,6 +412,18 @@ export function ShopInfoRbacComponent({ form, bodyshop }) {
> >
<InputNumber /> <InputNumber />
</Form.Item> </Form.Item>
<Form.Item
label={t("bodyshop.fields.rbac.owners.detail")}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
name={["md_rbac", "owners:detail"]}
>
<InputNumber />
</Form.Item>
<Form.Item <Form.Item
label={t("bodyshop.fields.rbac.payments.enter")} label={t("bodyshop.fields.rbac.payments.enter")}
rules={[ rules={[
@@ -448,30 +448,6 @@ export function ShopInfoRbacComponent({ form, bodyshop }) {
> >
<InputNumber /> <InputNumber />
</Form.Item> </Form.Item>
<Form.Item
label={t("bodyshop.fields.rbac.phonebook.edit")}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
name={["md_rbac", "phonebook:edit"]}
>
<InputNumber />
</Form.Item>
<Form.Item
label={t("bodyshop.fields.rbac.phonebook.view")}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
name={["md_rbac", "phonebook:view"]}
>
<InputNumber />
</Form.Item>
<Form.Item <Form.Item
label={t("bodyshop.fields.rbac.production.board")} label={t("bodyshop.fields.rbac.production.board")}
rules={[ rules={[
@@ -533,62 +509,26 @@ export function ShopInfoRbacComponent({ form, bodyshop }) {
<InputNumber /> <InputNumber />
</Form.Item> </Form.Item>
<Form.Item <Form.Item
label={t("bodyshop.fields.rbac.shop.config")} label={t("bodyshop.fields.rbac.timetickets.edit")}
rules={[ rules={[
{ {
required: true, required: true,
//message: t("general.validation.required"), //message: t("general.validation.required"),
}, },
]} ]}
name={["md_rbac", "shop:config"]} name={["md_rbac", "timetickets:edit"]}
> >
<InputNumber /> <InputNumber />
</Form.Item> </Form.Item>
<Form.Item <Form.Item
label={t("bodyshop.fields.rbac.shop.dashboard")} label={t("bodyshop.fields.rbac.timetickets.shiftedit")}
rules={[ rules={[
{ {
required: true, required: true,
//message: t("general.validation.required"), //message: t("general.validation.required"),
}, },
]} ]}
name={["md_rbac", "shop:dashboard"]} name={["md_rbac", "timetickets:shiftedit"]}
>
<InputNumber />
</Form.Item>
<Form.Item
label={t("bodyshop.fields.rbac.shop.rbac")}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
name={["md_rbac", "shop:rbac"]}
>
<InputNumber />
</Form.Item>
<Form.Item
label={t("bodyshop.fields.rbac.shop.reportcenter")}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
name={["md_rbac", "shop:reportcenter"]}
>
<InputNumber />
</Form.Item>
<Form.Item
label={t("bodyshop.fields.rbac.shop.templates")}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
name={["md_rbac", "shop:templates"]}
> >
<InputNumber /> <InputNumber />
</Form.Item> </Form.Item>
@@ -605,26 +545,50 @@ export function ShopInfoRbacComponent({ form, bodyshop }) {
<InputNumber /> <InputNumber />
</Form.Item> </Form.Item>
<Form.Item <Form.Item
label={t("bodyshop.fields.rbac.temporarydocs.view")} label={t("bodyshop.fields.rbac.shop.dashboard")}
rules={[ rules={[
{ {
required: true, required: true,
//message: t("general.validation.required"), //message: t("general.validation.required"),
}, },
]} ]}
name={["md_rbac", "temporarydocs:view"]} name={["md_rbac", "shop:dashboard"]}
> >
<InputNumber /> <InputNumber />
</Form.Item> </Form.Item>
<Form.Item <Form.Item
label={t("bodyshop.fields.rbac.timetickets.edit")} label={t("bodyshop.fields.rbac.shop.config")}
rules={[ rules={[
{ {
required: true, required: true,
//message: t("general.validation.required"), //message: t("general.validation.required"),
}, },
]} ]}
name={["md_rbac", "timetickets:edit"]} name={["md_rbac", "shop:config"]}
>
<InputNumber />
</Form.Item>
<Form.Item
label={t("bodyshop.fields.rbac.shop.rbac")}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
name={["md_rbac", "shop:rbac"]}
>
<InputNumber />
</Form.Item>
<Form.Item
label={t("bodyshop.fields.rbac.shop.templates")}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
name={["md_rbac", "shop:templates"]}
> >
<InputNumber /> <InputNumber />
</Form.Item> </Form.Item>
@@ -652,18 +616,6 @@ export function ShopInfoRbacComponent({ form, bodyshop }) {
> >
<InputNumber /> <InputNumber />
</Form.Item> </Form.Item>
<Form.Item
label={t("bodyshop.fields.rbac.timetickets.shiftedit")}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
name={["md_rbac", "timetickets:shiftedit"]}
>
<InputNumber />
</Form.Item>
<Form.Item <Form.Item
label={t("bodyshop.fields.rbac.users.editaccess")} label={t("bodyshop.fields.rbac.users.editaccess")}
rules={[ rules={[
@@ -676,6 +628,42 @@ export function ShopInfoRbacComponent({ form, bodyshop }) {
> >
<InputNumber /> <InputNumber />
</Form.Item> </Form.Item>
<Form.Item
label={t("bodyshop.fields.rbac.temporarydocs.view")}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
name={["md_rbac", "temporarydocs:view"]}
>
<InputNumber />
</Form.Item>
<Form.Item
label={t("bodyshop.fields.rbac.phonebook.view")}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
name={["md_rbac", "phonebook:view"]}
>
<InputNumber />
</Form.Item>
<Form.Item
label={t("bodyshop.fields.rbac.phonebook.edit")}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
name={["md_rbac", "phonebook:edit"]}
>
<InputNumber />
</Form.Item>
{Simple_Inventory.treatment === "on" && ( {Simple_Inventory.treatment === "on" && (
<> <>
<Form.Item <Form.Item

View File

@@ -30,15 +30,15 @@ export const QUERY_AVAILABLE_CC = gql`
fuel fuel
id id
make make
mileage
model model
plate
status
year
dailycost
mileage
notes notes
nextservicekm nextservicekm
nextservicedate nextservicedate
plate
readiness
status
year
} }
} }
`; `;
@@ -68,20 +68,19 @@ export const QUERY_ALL_CC = gql`
insuranceexpires insuranceexpires
leaseenddate leaseenddate
make make
mileage
model model
nextservicedate nextservicedate
nextservicekm nextservicekm
notes notes
plate plate
purchasedate purchasedate
readiness
registrationexpires registrationexpires
serviceenddate serviceenddate
servicestartdate servicestartdate
status status
vin vin
year year
mileage
cccontracts( cccontracts(
where: { status: { _eq: "contracts.status.out" } } where: { status: { _eq: "contracts.status.out" } }
order_by: { contract_date: desc } order_by: { contract_date: desc }
@@ -91,10 +90,10 @@ export const QUERY_ALL_CC = gql`
scheduledreturn scheduledreturn
job { job {
id id
ro_number
ownr_fn ownr_fn
ownr_ln ownr_ln
ownr_co_nm ownr_co_nm
ro_number
} }
} }
} }
@@ -120,20 +119,19 @@ export const QUERY_CC_BY_PK = gql`
insuranceexpires insuranceexpires
leaseenddate leaseenddate
make make
mileage
model model
nextservicedate nextservicedate
nextservicekm nextservicekm
notes notes
plate plate
purchasedate purchasedate
readiness
registrationexpires registrationexpires
serviceenddate serviceenddate
servicestartdate servicestartdate
status status
vin vin
year year
mileage
cccontracts_aggregate { cccontracts_aggregate {
aggregate { aggregate {
count(distinct: true) count(distinct: true)
@@ -141,20 +139,21 @@ export const QUERY_CC_BY_PK = gql`
} }
cccontracts(offset: $offset, limit: $limit, order_by: $order) { cccontracts(offset: $offset, limit: $limit, order_by: $order) {
agreementnumber agreementnumber
driver_fn
driver_ln
id id
status
start
scheduledreturn
kmstart kmstart
kmend kmend
scheduledreturn driver_ln
start driver_fn
status
job { job {
id ro_number
ownr_ln ownr_ln
ownr_fn ownr_fn
ownr_co_nm ownr_co_nm
ro_number id
} }
} }
} }

View File

@@ -5,13 +5,13 @@ 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!]!, $statusFilters: [String!]!,
$isConverted: Boolean $isConverted: Boolean
) { ) {
jobs( jobs(
offset: $offset offset: $offset
limit: $limit limit: $limit
where: { status: { _in: $statuses }, converted: { _eq: $isConverted } } where: { status: { _in: $statusFilters }, converted: { _eq: $isConverted } }
order_by: $order order_by: $order
) { ) {
iouparent iouparent
@@ -52,7 +52,7 @@ export const QUERY_ALL_ACTIVE_JOBS_PAGINATED = gql`
est_ct_fn est_ct_fn
est_ct_ln est_ct_ln
} }
jobs_aggregate(where: { status: { _in: $statuses } }) { jobs_aggregate(where: { status: { _in: $statusFilters } }) {
aggregate { aggregate {
count(distinct: true) count(distinct: true)
} }

View File

@@ -143,14 +143,9 @@ export const QUERY_TIME_TICKETS_IN_RANGE_SB = gql`
$end: date! $end: date!
$fixedStart: date! $fixedStart: date!
$fixedEnd: date! $fixedEnd: date!
$jobStart: timestamptz!
$jobEnd: timestamptz!
) { ) {
timetickets( timetickets(
where: { where: { date: { _gte: $start, _lte: $end }, cost_center: {_neq: "timetickets.labels.shift"} }
date: { _gte: $start, _lte: $end }
cost_center: { _neq: "timetickets.labels.shift" }
}
order_by: { date: desc_nulls_first } order_by: { date: desc_nulls_first }
) { ) {
actualhrs actualhrs
@@ -181,10 +176,7 @@ export const QUERY_TIME_TICKETS_IN_RANGE_SB = gql`
} }
} }
fixedperiod: timetickets( fixedperiod: timetickets(
where: { where: { date: { _gte: $fixedStart, _lte: $fixedEnd }, cost_center: {_neq: "timetickets.labels.shift"} }
date: { _gte: $fixedStart, _lte: $fixedEnd }
cost_center: { _neq: "timetickets.labels.shift" }
}
order_by: { date: desc_nulls_first } order_by: { date: desc_nulls_first }
) { ) {
actualhrs actualhrs
@@ -213,25 +205,6 @@ export const QUERY_TIME_TICKETS_IN_RANGE_SB = gql`
last_name last_name
} }
} }
jobs(
where: {
date_invoiced: { _is_null: true }
ro_number: { _is_null: false }
voided: { _eq: false }
_or: [
{ actual_completion: { _gte: $jobStart, _lte: $jobEnd } }
{ actual_delivery: { _gte: $jobStart, _lte: $jobEnd } }
]
}
) {
id
joblines(order_by: { line_no: asc }, where: { removed: { _eq: false } }) {
convertedtolbr
convertedtolbr_data
mod_lb_hrs
mod_lbr_ty
}
}
} }
`; `;

View File

@@ -1,14 +1,11 @@
import { useMutation, useQuery } from "@apollo/client"; import { useMutation, useQuery } from "@apollo/client";
import { Form, notification } from "antd"; import { Form, notification } from "antd";
import moment from "moment"; import moment from "moment";
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 { useLocation, useParams } from "react-router-dom"; import { useParams } from "react-router-dom";
import AlertComponent from "../../components/alert/alert.component"; import AlertComponent from "../../components/alert/alert.component";
import LoadingSpinner from "../../components/loading-spinner/loading-spinner.component";
import NotFound from "../../components/not-found/not-found.component";
import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component"; import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component";
import { QUERY_CC_BY_PK, UPDATE_CC } from "../../graphql/courtesy-car.queries"; import { QUERY_CC_BY_PK, UPDATE_CC } from "../../graphql/courtesy-car.queries";
import { import {
@@ -16,10 +13,13 @@ import {
setBreadcrumbs, setBreadcrumbs,
setSelectedHeader, setSelectedHeader,
} from "../../redux/application/application.actions"; } from "../../redux/application/application.actions";
import { pageLimit } from "../../utils/config";
import { CreateRecentItem } from "../../utils/create-recent-item"; import { CreateRecentItem } from "../../utils/create-recent-item";
import UndefinedToNull from "./../../utils/undefinedtonull";
import CourtesyCarDetailPageComponent from "./courtesy-car-detail.page.component"; import CourtesyCarDetailPageComponent from "./courtesy-car-detail.page.component";
import NotFound from "../../components/not-found/not-found.component";
import LoadingSpinner from "../../components/loading-spinner/loading-spinner.component";
import queryString from "query-string";
import { useLocation } from "react-router-dom";
import {pageLimit} from "../../utils/config";
const mapDispatchToProps = (dispatch) => ({ const mapDispatchToProps = (dispatch) => ({
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)), setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
@@ -112,10 +112,7 @@ export function CourtesyCarDetailPageContainer({
setSaveLoading(true); setSaveLoading(true);
const result = await updateCourtesyCar({ const result = await updateCourtesyCar({
variables: { variables: { cc: { ...values }, ccId: ccId },
cc: { ...UndefinedToNull(values, ["readiness"]) },
ccId: ccId,
},
refetchQueries: ["QUERY_CC_BY_PK"], refetchQueries: ["QUERY_CC_BY_PK"],
awaitRefetchQueries: true, awaitRefetchQueries: true,
}); });

View File

@@ -36,22 +36,14 @@ import JobsCloseLines from "../../components/jobs-close-lines/jobs-close-lines.c
import LayoutFormRow from "../../components/layout-form-row/layout-form-row.component"; import LayoutFormRow from "../../components/layout-form-row/layout-form-row.component";
import { generateJobLinesUpdatesForInvoicing } from "../../graphql/jobs-lines.queries"; import { generateJobLinesUpdatesForInvoicing } from "../../graphql/jobs-lines.queries";
import { UPDATE_JOB } from "../../graphql/jobs.queries"; import { UPDATE_JOB } from "../../graphql/jobs.queries";
import { insertAuditTrail } from "../../redux/application/application.actions";
import { selectJobReadOnly } from "../../redux/application/application.selectors"; import { selectJobReadOnly } from "../../redux/application/application.selectors";
import { selectBodyshop } from "../../redux/user/user.selectors"; import { selectBodyshop } from "../../redux/user/user.selectors";
import AuditTrailMapping from "../../utils/AuditTrailMappings";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop, bodyshop: selectBodyshop,
jobRO: selectJobReadOnly, jobRO: selectJobReadOnly,
}); });
const mapDispatchToProps = (dispatch) => ({ export function JobsCloseComponent({ job, bodyshop, jobRO }) {
insertAuditTrail: ({ jobid, operation }) =>
dispatch(insertAuditTrail({ jobid, operation })),
});
export function JobsCloseComponent({ job, bodyshop, jobRO, insertAuditTrail, }) {
const { t } = useTranslation(); const { t } = useTranslation();
const [form] = Form.useForm(); const [form] = Form.useForm();
const client = useApolloClient(); const client = useApolloClient();
@@ -118,10 +110,6 @@ export function JobsCloseComponent({ job, bodyshop, jobRO, insertAuditTrail, })
notification["success"]({ notification["success"]({
message: t("jobs.successes.closed"), message: t("jobs.successes.closed"),
}); });
insertAuditTrail({
jobid: job.id,
operation: AuditTrailMapping.jobinvoiced(),
});
// history.push(`/manage/jobs/${job.id}`); // history.push(`/manage/jobs/${job.id}`);
} else { } else {
setLoading(false); setLoading(false);
@@ -539,4 +527,4 @@ export function JobsCloseComponent({ job, bodyshop, jobRO, insertAuditTrail, })
</div> </div>
); );
} }
export default connect(mapStateToProps, mapDispatchToProps)(JobsCloseComponent); export default connect(mapStateToProps, null)(JobsCloseComponent);

View File

@@ -1,6 +1,6 @@
import _ from "lodash";
import { useLazyQuery, useMutation } from "@apollo/client"; import { useLazyQuery, useMutation } from "@apollo/client";
import { Form, notification } from "antd"; import { Form, notification } from "antd";
import _ from "lodash";
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";
@@ -90,7 +90,6 @@ function JobsCreateContainer({ bodyshop, setBreadcrumbs, setSelectedHeader }) {
{}, {},
values, values,
{ date_open: new Date() }, { date_open: new Date() },
{ date_estimated: new Date() },
{ {
vehicle: vehicle:
state.vehicle.selectedid || state.vehicle.none state.vehicle.selectedid || state.vehicle.none

View File

@@ -103,7 +103,6 @@
"admin_jobmarkforreexport": "ADMIN: Job marked for re-export.", "admin_jobmarkforreexport": "ADMIN: Job marked for re-export.",
"admin_jobuninvoice": "ADMIN: Job has been uninvoiced.", "admin_jobuninvoice": "ADMIN: Job has been uninvoiced.",
"admin_jobunvoid": "ADMIN: Job has been unvoided.", "admin_jobunvoid": "ADMIN: Job has been unvoided.",
"alerttoggle": "Alert Toggle set to {{status}}",
"appointmentcancel": "Appointment canceled. Lost Reason: {{lost_sale_reason}}.", "appointmentcancel": "Appointment canceled. Lost Reason: {{lost_sale_reason}}.",
"appointmentinsert": "Appointment created. Appointment Date: {{start}}.", "appointmentinsert": "Appointment created. Appointment Date: {{start}}.",
"billposted": "Bill with invoice number {{invoice_number}} posted.", "billposted": "Bill with invoice number {{invoice_number}} posted.",
@@ -112,7 +111,6 @@
"jobassignmentchange": "Employee {{name}} assigned to {{operation}}", "jobassignmentchange": "Employee {{name}} assigned to {{operation}}",
"jobassignmentremoved": "Employee assignment removed for {{operation}}", "jobassignmentremoved": "Employee assignment removed for {{operation}}",
"jobchecklist": "Checklist type \"{{type}}\" completed. In production set to {{inproduction}}. Status set to {{status}}.", "jobchecklist": "Checklist type \"{{type}}\" completed. In production set to {{inproduction}}. Status set to {{status}}.",
"jobinvoiced": "Job has been invoiced.",
"jobconverted": "Job converted and assigned number {{ro_number}}.", "jobconverted": "Job converted and assigned number {{ro_number}}.",
"jobfieldchanged": "Job field $t(jobs.fields.{{field}}) changed to {{value}}.", "jobfieldchanged": "Job field $t(jobs.fields.{{field}}) changed to {{value}}.",
"jobimported": "Job imported.", "jobimported": "Job imported.",
@@ -205,7 +203,6 @@
"entered_total": "Total of Entered Lines", "entered_total": "Total of Entered Lines",
"enteringcreditmemo": "You are entering a credit memo. Please ensure you are also entering positive values.", "enteringcreditmemo": "You are entering a credit memo. Please ensure you are also entering positive values.",
"federal_tax": "Federal Tax", "federal_tax": "Federal Tax",
"federal_tax_exempt": "Federal Tax Exempt?",
"generatepartslabel": "Generate Parts Labels after Saving?", "generatepartslabel": "Generate Parts Labels after Saving?",
"iouexists": "An IOU exists that is associated to this RO.", "iouexists": "An IOU exists that is associated to this RO.",
"local_tax": "Local Tax", "local_tax": "Local Tax",
@@ -214,7 +211,6 @@
"new": "New Bill", "new": "New Bill",
"noneselected": "No bill selected.", "noneselected": "No bill selected.",
"onlycmforinvoiced": "Only credit memos can be entered for any Job that has been invoiced, exported, or voided.", "onlycmforinvoiced": "Only credit memos can be entered for any Job that has been invoiced, exported, or voided.",
"printlabels": "Print Labels",
"retailtotal": "Bills Retail Total", "retailtotal": "Bills Retail Total",
"savewithdiscrepancy": "You are about to save this bill with a discrepancy. The system will continue to use the calculated amount using the bill lines. Press cancel to return to the bill.", "savewithdiscrepancy": "You are about to save this bill with a discrepancy. The system will continue to use the calculated amount using the bill lines. Press cancel to return to the bill.",
"state_tax": "Provincial/State Tax", "state_tax": "Provincial/State Tax",
@@ -451,7 +447,6 @@
"config": "Shop -> Config", "config": "Shop -> Config",
"dashboard": "Shop -> Dashboard", "dashboard": "Shop -> Dashboard",
"rbac": "Shop -> RBAC", "rbac": "Shop -> RBAC",
"reportcenter": "Shop -> Report Center",
"templates": "Shop -> Templates", "templates": "Shop -> Templates",
"vendors": "Shop -> Vendors" "vendors": "Shop -> Vendors"
}, },
@@ -786,7 +781,6 @@
"notes": "Notes", "notes": "Notes",
"plate": "Plate Number", "plate": "Plate Number",
"purchasedate": "Purchase Date", "purchasedate": "Purchase Date",
"readiness": "Readiness",
"registrationexpires": "Registration Expires On", "registrationexpires": "Registration Expires On",
"serviceenddate": "Usage End Date", "serviceenddate": "Usage End Date",
"servicestartdate": "Usage Start Date", "servicestartdate": "Usage Start Date",
@@ -823,10 +817,6 @@
}, },
"successes": { "successes": {
"saved": "Courtesy Car saved successfully." "saved": "Courtesy Car saved successfully."
},
"readiness": {
"notready": "Not Ready",
"ready": "Ready"
} }
}, },
"csi": { "csi": {
@@ -2054,9 +2044,6 @@
"sentby": "Sent by {{by}} at {{time}}", "sentby": "Sent by {{by}} at {{time}}",
"typeamessage": "Send a message...", "typeamessage": "Send a message...",
"unarchive": "Unarchive" "unarchive": "Unarchive"
},
"render": {
"conversation_list": "Conversation List"
} }
}, },
"notes": { "notes": {
@@ -2708,7 +2695,6 @@
"efficiencyoverperiod": "Efficiency over Selected Dates", "efficiencyoverperiod": "Efficiency over Selected Dates",
"entries": "Scoreboard Entries", "entries": "Scoreboard Entries",
"jobs": "Jobs", "jobs": "Jobs",
"jobscompletednotinvoiced": "Completed Not Invoiced",
"lastmonth": "Last Month", "lastmonth": "Last Month",
"lastweek": "Last Week", "lastweek": "Last Week",
"monthlytarget": "Monthly", "monthlytarget": "Monthly",
@@ -2723,7 +2709,6 @@
"timetickets": "Time Tickets", "timetickets": "Time Tickets",
"timeticketsemployee": "Time Tickets by Employee", "timeticketsemployee": "Time Tickets by Employee",
"todateactual": "Actual (MTD)", "todateactual": "Actual (MTD)",
"totalhrs": "Total Hours",
"totaloverperiod": "Total over Selected Dates", "totaloverperiod": "Total over Selected Dates",
"weeklyactual": "Actual (W)", "weeklyactual": "Actual (W)",
"weeklytarget": "Weekly", "weeklytarget": "Weekly",

View File

@@ -103,7 +103,6 @@
"admin_jobmarkforreexport": "", "admin_jobmarkforreexport": "",
"admin_jobuninvoice": "", "admin_jobuninvoice": "",
"admin_jobunvoid": "", "admin_jobunvoid": "",
"alerttoggle": "",
"appointmentcancel": "", "appointmentcancel": "",
"appointmentinsert": "", "appointmentinsert": "",
"billposted": "", "billposted": "",
@@ -112,7 +111,6 @@
"jobassignmentchange": "", "jobassignmentchange": "",
"jobassignmentremoved": "", "jobassignmentremoved": "",
"jobchecklist": "", "jobchecklist": "",
"jobinvoiced": "",
"jobconverted": "", "jobconverted": "",
"jobfieldchanged": "", "jobfieldchanged": "",
"jobimported": "", "jobimported": "",
@@ -205,7 +203,6 @@
"entered_total": "", "entered_total": "",
"enteringcreditmemo": "", "enteringcreditmemo": "",
"federal_tax": "", "federal_tax": "",
"federal_tax_exempt": "",
"generatepartslabel": "", "generatepartslabel": "",
"iouexists": "", "iouexists": "",
"local_tax": "", "local_tax": "",
@@ -214,7 +211,6 @@
"new": "", "new": "",
"noneselected": "", "noneselected": "",
"onlycmforinvoiced": "", "onlycmforinvoiced": "",
"printlabels": "",
"retailtotal": "", "retailtotal": "",
"savewithdiscrepancy": "", "savewithdiscrepancy": "",
"state_tax": "", "state_tax": "",
@@ -451,7 +447,6 @@
"config": "", "config": "",
"dashboard": "", "dashboard": "",
"rbac": "", "rbac": "",
"reportcenter": "",
"templates": "", "templates": "",
"vendors": "" "vendors": ""
}, },
@@ -786,7 +781,6 @@
"notes": "", "notes": "",
"plate": "", "plate": "",
"purchasedate": "", "purchasedate": "",
"readiness": "",
"registrationexpires": "", "registrationexpires": "",
"serviceenddate": "", "serviceenddate": "",
"servicestartdate": "", "servicestartdate": "",
@@ -823,10 +817,6 @@
}, },
"successes": { "successes": {
"saved": "" "saved": ""
},
"readiness": {
"notready": "",
"ready": ""
} }
}, },
"csi": { "csi": {
@@ -2054,9 +2044,6 @@
"sentby": "", "sentby": "",
"typeamessage": "Enviar un mensaje...", "typeamessage": "Enviar un mensaje...",
"unarchive": "" "unarchive": ""
},
"render": {
"conversation_list": ""
} }
}, },
"notes": { "notes": {
@@ -2708,7 +2695,6 @@
"efficiencyoverperiod": "", "efficiencyoverperiod": "",
"entries": "", "entries": "",
"jobs": "", "jobs": "",
"jobscompletednotinvoiced": "",
"lastmonth": "", "lastmonth": "",
"lastweek": "", "lastweek": "",
"monthlytarget": "", "monthlytarget": "",
@@ -2723,7 +2709,6 @@
"timetickets": "", "timetickets": "",
"timeticketsemployee": "", "timeticketsemployee": "",
"todateactual": "", "todateactual": "",
"totalhrs": "",
"totaloverperiod": "", "totaloverperiod": "",
"weeklyactual": "", "weeklyactual": "",
"weeklytarget": "", "weeklytarget": "",

View File

@@ -103,7 +103,6 @@
"admin_jobmarkforreexport": "", "admin_jobmarkforreexport": "",
"admin_jobuninvoice": "", "admin_jobuninvoice": "",
"admin_jobunvoid": "", "admin_jobunvoid": "",
"alerttoggle": "",
"appointmentcancel": "", "appointmentcancel": "",
"appointmentinsert": "", "appointmentinsert": "",
"billposted": "", "billposted": "",
@@ -112,7 +111,6 @@
"jobassignmentchange": "", "jobassignmentchange": "",
"jobassignmentremoved": "", "jobassignmentremoved": "",
"jobchecklist": "", "jobchecklist": "",
"jobinvoiced": "",
"jobconverted": "", "jobconverted": "",
"jobfieldchanged": "", "jobfieldchanged": "",
"jobimported": "", "jobimported": "",
@@ -205,7 +203,6 @@
"entered_total": "", "entered_total": "",
"enteringcreditmemo": "", "enteringcreditmemo": "",
"federal_tax": "", "federal_tax": "",
"federal_tax_exempt": "",
"generatepartslabel": "", "generatepartslabel": "",
"iouexists": "", "iouexists": "",
"local_tax": "", "local_tax": "",
@@ -214,7 +211,6 @@
"new": "", "new": "",
"noneselected": "", "noneselected": "",
"onlycmforinvoiced": "", "onlycmforinvoiced": "",
"printlabels": "",
"retailtotal": "", "retailtotal": "",
"savewithdiscrepancy": "", "savewithdiscrepancy": "",
"state_tax": "", "state_tax": "",
@@ -451,7 +447,6 @@
"config": "", "config": "",
"dashboard": "", "dashboard": "",
"rbac": "", "rbac": "",
"reportcenter": "",
"templates": "", "templates": "",
"vendors": "" "vendors": ""
}, },
@@ -786,7 +781,6 @@
"notes": "", "notes": "",
"plate": "", "plate": "",
"purchasedate": "", "purchasedate": "",
"readiness": "",
"registrationexpires": "", "registrationexpires": "",
"serviceenddate": "", "serviceenddate": "",
"servicestartdate": "", "servicestartdate": "",
@@ -823,10 +817,6 @@
}, },
"successes": { "successes": {
"saved": "" "saved": ""
},
"readiness": {
"notready": "",
"ready": ""
} }
}, },
"csi": { "csi": {
@@ -2054,9 +2044,6 @@
"sentby": "", "sentby": "",
"typeamessage": "Envoyer un message...", "typeamessage": "Envoyer un message...",
"unarchive": "" "unarchive": ""
},
"render": {
"conversation_list": ""
} }
}, },
"notes": { "notes": {
@@ -2708,7 +2695,6 @@
"efficiencyoverperiod": "", "efficiencyoverperiod": "",
"entries": "", "entries": "",
"jobs": "", "jobs": "",
"jobscompletednotinvoiced": "",
"lastmonth": "", "lastmonth": "",
"lastweek": "", "lastweek": "",
"monthlytarget": "", "monthlytarget": "",
@@ -2723,7 +2709,6 @@
"timetickets": "", "timetickets": "",
"timeticketsemployee": "", "timeticketsemployee": "",
"todateactual": "", "todateactual": "",
"totalhrs": "",
"totaloverperiod": "", "totaloverperiod": "",
"weeklyactual": "", "weeklyactual": "",
"weeklytarget": "", "weeklytarget": "",

View File

@@ -1,7 +1,6 @@
import i18n from "i18next"; import i18n from "i18next";
const AuditTrailMapping = { const AuditTrailMapping = {
alertToggle: (status) => i18n.t("audit_trail.messages.alerttoggle", { status }),
appointmentcancel: (lost_sale_reason) => appointmentcancel: (lost_sale_reason) =>
i18n.t("audit_trail.messages.appointmentcancel", { lost_sale_reason }), i18n.t("audit_trail.messages.appointmentcancel", { lost_sale_reason }),
appointmentinsert: (start) => appointmentinsert: (start) =>
@@ -12,8 +11,6 @@ const AuditTrailMapping = {
"ADMIN: " + i18n.t("audit_trail.messages.jobstatuschange", { status }), "ADMIN: " + i18n.t("audit_trail.messages.jobstatuschange", { status }),
jobsupplement: () => i18n.t("audit_trail.messages.jobsupplement"), jobsupplement: () => i18n.t("audit_trail.messages.jobsupplement"),
jobimported: () => i18n.t("audit_trail.messages.jobimported"), jobimported: () => i18n.t("audit_trail.messages.jobimported"),
jobinvoiced: () =>
i18n.t("audit_trail.messages.jobinvoiced"),
jobconverted: (ro_number) => jobconverted: (ro_number) =>
i18n.t("audit_trail.messages.jobconverted", { ro_number }), i18n.t("audit_trail.messages.jobconverted", { ro_number }),
jobfieldchange: (field, value) => jobfieldchange: (field, value) =>

View File

@@ -2102,17 +2102,6 @@ export const TemplateList = (type, context) => {
// }, // },
} }
: {}), : {}),
...(!type || type === "messaging"
? {
conversation_list: {
title: i18n.t("messaging.render.conversation_list"),
description: "",
subject: i18n.t("messaging.render.conversation_list"),
key: "conversation_list",
disabled: false,
},
}
: {}),
...(!type || type === "vendor" ...(!type || type === "vendor"
? { ? {
purchases_by_vendor_detailed: { purchases_by_vendor_detailed: {

View File

@@ -1,4 +1,4 @@
// Sometimes referred to as PageSize, this variable controls the amount of records // Sometimes referred to as PageSize, this variable controls the amount of records
// to show on one page during pagination. // to show on one page during pagination.
export const pageLimit = 50; export const pageLimit = 10;

View File

@@ -1388,62 +1388,60 @@
- active: - active:
_eq: true _eq: true
columns: columns:
- bodyshopid
- color
- created_at
- dailycost
- damage
- fleetnumber
- fuel
- id - id
- insuranceexpires - created_at
- leaseenddate
- make
- mileage
- model
- nextservicedate
- nextservicekm
- notes
- plate
- purchasedate
- readiness
- registrationexpires
- serviceenddate
- servicestartdate
- status
- updated_at - updated_at
- vin - bodyshopid
- make
- model
- year - year
- plate
- color
- vin
- fleetnumber
- purchasedate
- servicestartdate
- serviceenddate
- leaseenddate
- status
- nextservicekm
- nextservicedate
- damage
- notes
- fuel
- registrationexpires
- insuranceexpires
- dailycost
- mileage
select_permissions: select_permissions:
- role: user - role: user
permission: permission:
columns: columns:
- bodyshopid
- color
- created_at
- dailycost
- damage
- fleetnumber
- fuel
- id
- insuranceexpires - insuranceexpires
- leaseenddate - leaseenddate
- make
- mileage
- model
- nextservicedate - nextservicedate
- nextservicekm
- notes
- plate
- purchasedate - purchasedate
- readiness
- registrationexpires - registrationexpires
- serviceenddate - serviceenddate
- servicestartdate - servicestartdate
- dailycost
- fuel
- mileage
- nextservicekm
- color
- damage
- fleetnumber
- make
- model
- notes
- plate
- status - status
- updated_at
- vin - vin
- year - year
- created_at
- updated_at
- bodyshopid
- id
filter: filter:
bodyshop: bodyshop:
associations: associations:
@@ -1458,32 +1456,31 @@
- role: user - role: user
permission: permission:
columns: columns:
- bodyshopid
- color
- created_at
- dailycost
- damage
- fleetnumber
- fuel
- id
- insuranceexpires - insuranceexpires
- leaseenddate - leaseenddate
- make
- mileage
- model
- nextservicedate - nextservicedate
- nextservicekm
- notes
- plate
- purchasedate - purchasedate
- readiness
- registrationexpires - registrationexpires
- serviceenddate - serviceenddate
- servicestartdate - servicestartdate
- dailycost
- fuel
- mileage
- nextservicekm
- color
- damage
- fleetnumber
- make
- model
- notes
- plate
- status - status
- updated_at
- vin - vin
- year - year
- created_at
- updated_at
- bodyshopid
- id
filter: filter:
bodyshop: bodyshop:
associations: associations:
@@ -2023,24 +2020,24 @@
- active: - active:
_eq: true _eq: true
columns: columns:
- created_at
- employeeid
- id
- labor_rates - labor_rates
- percentage - percentage
- teamid - created_at
- updated_at - updated_at
- employeeid
- id
- teamid
select_permissions: select_permissions:
- role: user - role: user
permission: permission:
columns: columns:
- created_at
- employeeid
- id
- labor_rates - labor_rates
- percentage - percentage
- teamid - created_at
- updated_at - updated_at
- employeeid
- id
- teamid
filter: filter:
employee_team: employee_team:
bodyshop: bodyshop:
@@ -2055,13 +2052,13 @@
- role: user - role: user
permission: permission:
columns: columns:
- created_at
- employeeid
- id
- labor_rates - labor_rates
- percentage - percentage
- teamid - created_at
- updated_at - updated_at
- employeeid
- id
- teamid
filter: filter:
employee_team: employee_team:
bodyshop: bodyshop:
@@ -2123,23 +2120,21 @@
_eq: true _eq: true
columns: columns:
- active - active
- bodyshopid
- created_at
- id
- max_load
- name - name
- created_at
- updated_at - updated_at
- bodyshopid
- id
select_permissions: select_permissions:
- role: user - role: user
permission: permission:
columns: columns:
- active - active
- bodyshopid
- created_at
- id
- max_load
- name - name
- created_at
- updated_at - updated_at
- bodyshopid
- id
filter: filter:
bodyshop: bodyshop:
associations: associations:
@@ -2155,7 +2150,6 @@
columns: columns:
- active - active
- bodyshopid - bodyshopid
- max_load
- name - name
- updated_at - updated_at
filter: filter:

View File

@@ -1,4 +0,0 @@
-- Could not auto-generate a down migration.
-- Please write an appropriate down migration for the SQL below:
-- alter table "public"."courtesycars" add column "readiness" text
-- null;

View File

@@ -1,2 +0,0 @@
alter table "public"."courtesycars" add column "readiness" text
null;

View File

@@ -1,4 +0,0 @@
-- Could not auto-generate a down migration.
-- Please write an appropriate down migration for the SQL below:
-- alter table "public"."employee_team_members" add column "max_load" numeric
-- not null default '10000';

View File

@@ -1,2 +0,0 @@
alter table "public"."employee_team_members" add column "max_load" numeric
not null default '10000';

View File

@@ -1,3 +0,0 @@
alter table "public"."employee_team_members" alter column "max_load" set default '10000'::numeric;
alter table "public"."employee_team_members" alter column "max_load" drop not null;
alter table "public"."employee_team_members" add column "max_load" numeric;

View File

@@ -1 +0,0 @@
alter table "public"."employee_team_members" drop column "max_load" cascade;

View File

@@ -1,4 +0,0 @@
-- Could not auto-generate a down migration.
-- Please write an appropriate down migration for the SQL below:
-- alter table "public"."employee_teams" add column "max_load" numeric
-- not null default '10000';

View File

@@ -1,2 +0,0 @@
alter table "public"."employee_teams" add column "max_load" numeric
not null default '10000';

View File

@@ -50,7 +50,7 @@ exports.createUser = async (req, res) => {
`, `,
{ {
user: { user: {
email: email.toLowerCase(), email,
authid: userRecord.uid, authid: userRecord.uid,
associations: { associations: {
data: [{ shopid, authlevel, active: true }], data: [{ shopid, authlevel, active: true }],