Compare commits

..

1 Commits

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

View File

@@ -22110,6 +22110,19 @@
"is-typedarray": "^1.0.0"
}
},
"node_modules/typescript": {
"version": "4.9.5",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz",
"integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==",
"peer": true,
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
},
"engines": {
"node": ">=4.2.0"
}
},
"node_modules/typescript-compare": {
"version": "0.0.2",
"resolved": "https://registry.npmjs.org/typescript-compare/-/typescript-compare-0.0.2.tgz",

View File

@@ -22,7 +22,6 @@ import {
} from "../redux/user/user.selectors";
import PrivateRoute from "../utils/private-route";
import "./App.styles.scss";
import handleBeta from "../utils/handleBeta";
const ResetPassword = lazy(() =>
import("../pages/reset-password/reset-password.component")
@@ -58,6 +57,7 @@ export function App({
if (!navigator.onLine) {
setOnline(false);
}
checkUserSession();
}, [checkUserSession, setOnline]);
@@ -73,7 +73,6 @@ export function App({
window.addEventListener("online", function (e) {
setOnline(true);
});
useEffect(() => {
if (currentUser.authorized && bodyshop) {
client.setAttribute("imexshopid", bodyshop.imexshopid);
@@ -108,8 +107,6 @@ export function App({
/>
);
handleBeta();
return (
<Switch>
<Suspense fallback={<LoadingSpinner message="ImEX Online" />}>

View File

@@ -10,7 +10,7 @@ import { createStructuredSelector } from "reselect";
import {
DELETE_BILL_LINE,
INSERT_NEW_BILL_LINES,
UPDATE_BILL_LINE,
UPDATE_BILL_LINE
} from "../../graphql/bill-lines.queries";
import { QUERY_BILL_BY_PK, UPDATE_BILL } from "../../graphql/bills.queries";
import { insertAuditTrail } from "../../redux/application/application.actions";
@@ -20,7 +20,6 @@ import AuditTrailMapping from "../../utils/AuditTrailMappings";
import AlertComponent from "../alert/alert.component";
import BillFormContainer from "../bill-form/bill-form.container";
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 JobDocumentsGallery from "../jobs-documents-gallery/jobs-documents-gallery.container";
import JobsDocumentsLocalGallery from "../jobs-documents-local-gallery/jobs-documents-local-gallery.container";
@@ -177,7 +176,7 @@ export function BillDetailEditcontainer({
extra={
<Space>
<BillDetailEditReturn data={data} />
<BillPrintButton billid={search.billid} />
<Popconfirm
visible={visible}
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(() => {
if (job) form.validateFields(["is_credit_memo"]);
}, [job, form]);
@@ -401,16 +387,7 @@ export function BillFormComponent({
>
<CurrencyInput min={0} />
</Form.Item>
{bodyshop.pbs_serialnumber || bodyshop.cdk_dealerid ? (
<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}>
<Form.Item shouldUpdate span={15}>
{() => {
const values = form.getFieldsValue([
"billlines",
@@ -428,7 +405,7 @@ export function BillFormComponent({
totals = CalculateBillTotal(values);
if (!!totals)
return (
<div align="right">
<div>
<Space wrap>
<Statistic
title={t("bills.labels.subtotal")}

View File

@@ -1,15 +1,14 @@
import { DeleteFilled, DollarCircleFilled } from "@ant-design/icons";
import { useTreatments } from "@splitsoftware/splitio-react";
import {
Button,
Form,
Button, Form,
Input,
InputNumber,
Select,
Space,
Switch,
Table,
Tooltip,
Tooltip
} from "antd";
import React from "react";
import { useTranslation } from "react-i18next";
@@ -467,8 +466,7 @@ export function BillEnterModalLinesComponent({
return {
key: `${field.index}fedtax`,
valuePropName: "checked",
initialValue:
form.getFieldValue("federal_tax_exempt") === true ? false : true,
initialValue: true,
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 ChatConversationTitleTags from "../chat-conversation-title-tags/chat-conversation-title-tags.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";
export default function ChatConversationTitle({ conversation }) {
@@ -14,7 +13,6 @@ export default function ChatConversationTitle({ conversation }) {
{conversation && conversation.phone_num}
</PhoneNumberFormatter>
<ChatLabelComponent conversation={conversation} />
<ChatPrintButton conversation={conversation} />
<ChatConversationTitleTags
jobConversations={
(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,
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"),
dataIndex: "year",

View File

@@ -7,7 +7,6 @@ import { useTranslation } from "react-i18next";
import { CHECK_CC_FLEET_NUMBER } from "../../graphql/courtesy-car.queries";
import { DateFormatter } from "../../utils/DateFormatter";
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 FormDatePicker from "../form-date-picker/form-date-picker.component";
//import FormFieldsChanged from "../form-fields-changed-alert/form-fields-changed-alert.component";
@@ -214,9 +213,6 @@ export default function CourtesyCarCreateFormComponent({ form, saveLoading }) {
>
<CourtesyCarStatus />
</Form.Item>
<Form.Item label={t("courtesycars.fields.readiness")} name="readiness">
<CourtesyCarReadiness />
</Form.Item>
<div>
<Form.Item
label={t("courtesycars.fields.nextservicekm")}
@@ -231,9 +227,8 @@ export default function CourtesyCarCreateFormComponent({ form, saveLoading }) {
>
{() => {
const nextservicekm = form.getFieldValue("nextservicekm");
const mileageOver = nextservicekm
? nextservicekm <= form.getFieldValue("mileage")
: false;
const mileageOver =
nextservicekm && nextservicekm <= form.getFieldValue("mileage");
if (mileageOver)
return (
<Space direction="vertical" style={{ color: "tomato" }}>

View File

@@ -34,32 +34,6 @@ const CourtesyCarFuelComponent = (props, ref) => {
step={null}
style={{ marginLeft: "2rem", marginRight: "2rem" }}
{...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

@@ -74,11 +74,10 @@ export default function CourtesyCarsList({ loading, courtesycars, refetch }) {
render: (text, record) => {
const { nextservicedate, nextservicekm, mileage } = record;
const mileageOver = nextservicekm ? nextservicekm <= mileage : false;
const mileageOver = nextservicekm <= mileage;
const dueForService =
nextservicedate &&
moment(nextservicedate).endOf("day").isSameOrBefore(moment());
nextservicedate && moment(nextservicedate).isBefore(moment());
return (
<Space>
@@ -92,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"),
dataIndex: "year",
@@ -152,36 +131,6 @@ export default function CourtesyCarsList({ loading, courtesycars, refetch }) {
sortOrder:
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"),
dataIndex: "outwith",

View File

@@ -13,7 +13,7 @@ import Icon, {
FileFilled,
//GlobalOutlined,
HomeFilled,
ImportOutlined, InfoCircleOutlined,
ImportOutlined,
LineChartOutlined,
PaperClipOutlined,
PhoneOutlined,
@@ -26,8 +26,8 @@ import Icon, {
UserOutlined,
} from "@ant-design/icons";
import { useTreatments } from "@splitsoftware/splitio-react";
import {Layout, Menu, Switch, Tooltip} from "antd";
import React, {useEffect, useState} from "react";
import { Layout, Menu } from "antd";
import React from "react";
import { useTranslation } from "react-i18next";
import { BsKanban } from "react-icons/bs";
import {
@@ -52,7 +52,6 @@ import {
selectBodyshop,
selectCurrentUser,
} from "../../redux/user/user.selectors";
import {handleBeta, setBeta, checkBeta} from "../../utils/handleBeta";
const mapStateToProps = createStructuredSelector({
currentUser: selectCurrentUser,
@@ -103,21 +102,9 @@ function Header({
{},
bodyshop && bodyshop.imexshopid
);
const [betaSwitch, setBetaSwitch] = useState(false);
const { t } = useTranslation();
useEffect(() => {
const isBeta = checkBeta();
setBetaSwitch(isBeta);
}, []);
const betaSwitchChange = (checked) => {
setBeta(checked);
setBetaSwitch(checked);
handleBeta();
}
return (
<Layout.Header>
<Menu
@@ -444,17 +431,6 @@ function Header({
</Menu.Item>
))}
</Menu.SubMenu>
<Menu.Item style={{marginLeft: 'auto'}} key="profile">
<Tooltip title="A more modern ImEX Online is ready for you to try! You can switch back at any time.">
<InfoCircleOutlined/>
<span style={{marginRight: 8}}>Try the new ImEX Online</span>
<Switch
checked={betaSwitch}
onChange={betaSwitchChange}
/>
</Tooltip>
</Menu.Item>
</Menu>
</Layout.Header>
);

View File

@@ -7,31 +7,21 @@ import { connect } from "react-redux";
import { useHistory } from "react-router";
import { createStructuredSelector } from "reselect";
import { UPDATE_JOB_LINES_IOU } from "../../graphql/jobs-lines.queries";
import { selectTechnician } from "../../redux/tech/tech.selectors";
import {
selectBodyshop,
selectCurrentUser,
} from "../../redux/user/user.selectors";
import { CreateIouForJob } from "../jobs-detail-header-actions/jobs-detail-header-actions.duplicate.util";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
currentUser: selectCurrentUser,
technician: selectTechnician,
});
const mapDispatchToProps = (dispatch) => ({
//setUserLanguage: language => dispatch(setUserLanguage(language))
});
export default connect(mapStateToProps, mapDispatchToProps)(JobCreateIOU);
export function JobCreateIOU({
bodyshop,
currentUser,
job,
selectedJobLines,
technician,
}) {
export function JobCreateIOU({ bodyshop, currentUser, job, selectedJobLines }) {
const { t } = useTranslation();
const [loading, setLoading] = useState(false);
const client = useApolloClient();
@@ -89,19 +79,13 @@ export function JobCreateIOU({
title={t("jobs.labels.createiouwarning")}
onConfirm={handleCreateIou}
disabled={
!selectedJobLines ||
selectedJobLines.length === 0 ||
!job.converted ||
technician
!selectedJobLines || selectedJobLines.length === 0 || !job.converted
}
>
<Button
loading={loading}
disabled={
!selectedJobLines ||
selectedJobLines.length === 0 ||
!job.converted ||
technician
!selectedJobLines || selectedJobLines.length === 0 || !job.converted
}
>
{t("jobs.actions.createiou")}

View File

@@ -1,37 +1,15 @@
import { Card } from "antd";
import React from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { Card } from "antd";
import { Link } from "react-router-dom";
import { createStructuredSelector } from "reselect";
import { selectTechnician } from "../../redux/tech/tech.selectors";
const mapStateToProps = createStructuredSelector({
technician: selectTechnician,
});
const mapDispatchToProps = (dispatch) => ({
//setUserLanguage: language => dispatch(setUserLanguage(language))
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(JobDetailCardTemplate);
export function JobDetailCardTemplate({
export default function JobDetailCardTemplate({
loading,
title,
extraLink,
technician,
...otherProps
}) {
const { t } = useTranslation();
let extra;
if (extraLink && !technician)
extra = {
extra: <Link to={extraLink}>{t("jobs.labels.cards.more")}</Link>,
};
if (extraLink) extra = { extra: <Link to={extraLink}>More</Link> };
return (
<Card
size="small"

View File

@@ -19,13 +19,10 @@ export default function JobsCreateOwnerInfoNewComponent() {
label={t("owners.fields.ownr_ln")}
name={["owner", "data", "ownr_ln"]}
rules={[
({ getFieldValue }) => ({
required:
state.owner.new &&
(!getFieldValue(["owner", "data", "ownr_co_nm"]) ||
getFieldValue(["owner", "data", "ownr_co_nm"]) === ""),
{
required: state.owner.new,
//message: t("general.validation.required"),
}),
},
]}
>
<Input disabled={!state.owner.new} />
@@ -34,13 +31,10 @@ export default function JobsCreateOwnerInfoNewComponent() {
label={t("owners.fields.ownr_fn")}
name={["owner", "data", "ownr_fn"]}
rules={[
({ getFieldValue }) => ({
required:
state.owner.new &&
(!getFieldValue(["owner", "data", "ownr_co_nm"]) ||
getFieldValue(["owner", "data", "ownr_co_nm"]) === ""),
{
required: state.owner.new,
//message: t("general.validation.required"),
}),
},
]}
>
<Input disabled={!state.owner.new} />
@@ -57,17 +51,6 @@ export default function JobsCreateOwnerInfoNewComponent() {
<Form.Item
label={t("owners.fields.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} />
</Form.Item>

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) => {

View File

@@ -1,8 +1,8 @@
import {
BranchesOutlined,
ExclamationCircleFilled,
PauseCircleOutlined,
WarningFilled,
BranchesOutlined,
} from "@ant-design/icons";
import { Card, Col, Row, Space, Tag, Tooltip } from "antd";
import React, { useState } from "react";
@@ -15,7 +15,6 @@ import { setModalContext } from "../../redux/modals/modals.actions";
import { selectBodyshop } from "../../redux/user/user.selectors";
import CurrencyFormatter from "../../utils/CurrencyFormatter";
import { DateTimeFormatter } from "../../utils/DateFormatter";
import PhoneNumberFormatter from "../../utils/PhoneFormatter";
import ChatOpenButton from "../chat-open-button/chat-open-button.component";
import DataLabel from "../data-label/data-label.component";
import JobAltTransportChange from "../job-at-change/job-at-change.component";
@@ -161,35 +160,19 @@ export function JobsDetailHeader({ job, bodyshop, disabled }) {
<Card
style={{ height: "100%" }}
title={
disabled ? (
<>
{ownerTitle.length > 0
? ownerTitle
: t("owner.labels.noownerinfo")}
</>
) : (
<Link to={`/manage/owners/${job.owner.id}`}>
{ownerTitle.length > 0
? ownerTitle
: t("owner.labels.noownerinfo")}
</Link>
)
<Link to={disabled ? "#" : `/manage/owners/${job.owner.id}`}>
{ownerTitle.length > 0
? ownerTitle
: t("owner.labels.noownerinfo")}
</Link>
}
>
<div>
<DataLabel key="2" label={t("jobs.fields.ownr_ph1")}>
{disabled ? (
<PhoneNumberFormatter>{job.ownr_ph1}</PhoneNumberFormatter>
) : (
<ChatOpenButton phone={job.ownr_ph1} jobid={job.id} />
)}
<ChatOpenButton phone={job.ownr_ph1} jobid={job.id} />
</DataLabel>
<DataLabel key="22" label={t("jobs.fields.ownr_ph2")}>
{disabled ? (
<PhoneNumberFormatter>{job.ownr_ph2}</PhoneNumberFormatter>
) : (
<ChatOpenButton phone={job.ownr_ph2} jobid={job.id} />
)}
<ChatOpenButton phone={job.ownr_ph2} jobid={job.id} />
</DataLabel>
<DataLabel key="3" label={t("owners.fields.address")}>
{`${job.ownr_addr1 || ""} ${job.ownr_addr2 || ""} ${
@@ -197,11 +180,7 @@ export function JobsDetailHeader({ job, bodyshop, disabled }) {
} ${job.ownr_st || ""} ${job.ownr_zip || ""}`}
</DataLabel>
<DataLabel key="4" label={t("owners.fields.ownr_ea")}>
{disabled ? (
<>{job.ownr_ea || ""}</>
) : job.ownr_ea ? (
<a href={`mailto:${job.ownr_ea}`}>{job.ownr_ea}</a>
) : null}
{job.ownr_ea || ""}
</DataLabel>
{job.owner?.tax_number && (
<DataLabel key="5" label={t("owners.fields.tax_number")}>
@@ -216,19 +195,17 @@ export function JobsDetailHeader({ job, bodyshop, disabled }) {
style={{ height: "100%" }}
title={
job.vehicle ? (
disabled ? (
<>
{vehicleTitle.length > 0
? vehicleTitle
: t("vehicles.labels.novehinfo")}{" "}
</>
) : (
<Link to={job.vehicle && `/manage/vehicles/${job.vehicle.id}`}>
{vehicleTitle.length > 0
? vehicleTitle
: t("vehicles.labels.novehinfo")}
</Link>
)
<Link
to={
disabled
? "#"
: job.vehicle && `/manage/vehicles/${job.vehicle.id}`
}
>
{vehicleTitle.length > 0
? vehicleTitle
: t("vehicles.labels.novehinfo")}
</Link>
) : (
<span></span>
)
@@ -244,19 +221,9 @@ export function JobsDetailHeader({ job, bodyshop, disabled }) {
<VehicleVinDisplay>
{`${job.v_vin || t("general.labels.na")}`}
</VehicleVinDisplay>
{bodyshop.pbs_serialnumber || bodyshop.cdk_dealerid ? (
job.v_vin?.length !== 17 ? (
<WarningFilled
style={{ color: "tomato", marginLeft: ".3rem" }}
/>
) : null
) : null}
</DataLabel>
<DataLabel label={t("jobs.fields.regie_number")}>
{job.regie_number || t("general.labels.na")}
</DataLabel>
<DataLabel label={t("jobs.labels.relatedros")}>
<JobsRelatedRos jobid={job.id} job={job} disabled={disabled} />
<JobsRelatedRos jobid={job.id} job={job} />
</DataLabel>
{job.vehicle && job.vehicle.notes && (
<DataLabel

View File

@@ -10,10 +10,9 @@ import { Link, useHistory, useLocation } from "react-router-dom";
import { createStructuredSelector } from "reselect";
import { selectBodyshop } from "../../redux/user/user.selectors";
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 OwnerNameDisplay from "../owner-name-display/owner-name-display.component";
import {pageLimit} from "../../utils/config";
const mapStateToProps = createStructuredSelector({
//currentUser: selectCurrentUser
bodyshop: selectBodyshop,
@@ -26,7 +25,6 @@ export function JobsList({ bodyshop, refetch, loading, jobs, total }) {
const search = queryString.parse(useLocation().search);
const [openSearchResults, setOpenSearchResults] = useState([]);
const [searchLoading, setSearchLoading] = useState(false);
const [filter, setFilter] = useLocalStorage("filter_jobs_all", null);
const { page, sortcolumn, sortorder } = search;
const history = useHistory();
@@ -95,7 +93,6 @@ export function JobsList({ bodyshop, refetch, loading, jobs, total }) {
render: (text, record) => {
return record.status || t("general.labels.na");
},
filteredValue: filter?.status || null,
filters: bodyshop.md_ro_statuses.statuses.map((s) => {
return { text: s, value: [s] };
}),
@@ -192,7 +189,6 @@ export function JobsList({ bodyshop, refetch, loading, jobs, total }) {
} else {
delete search.statusFilters;
}
setFilter(filters);
history.push({ search: queryString.stringify(search) });
};

View File

@@ -1,402 +1,426 @@
import {
BranchesOutlined,
ExclamationCircleFilled,
PauseCircleOutlined,
SyncOutlined,
} from "@ant-design/icons";
import { useQuery } from "@apollo/client";
import { Button, Card, Grid, Input, Space, Table, Tooltip } from "antd";
import {BranchesOutlined, ExclamationCircleFilled, PauseCircleOutlined, SyncOutlined,} from "@ant-design/icons";
import {useQuery} from "@apollo/client";
import {Button, Card, Grid, Input, Space, Table, Tooltip, Typography} from "antd";
import queryString from "query-string";
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { Link, useHistory, useLocation } from "react-router-dom";
import { createStructuredSelector } from "reselect";
import { QUERY_ALL_ACTIVE_JOBS } from "../../graphql/jobs.queries";
import { selectBodyshop } from "../../redux/user/user.selectors";
import React, {useEffect, useState} from "react";
import {useTranslation} from "react-i18next";
import {connect} from "react-redux";
import {Link, useHistory, useLocation} from "react-router-dom";
import {createStructuredSelector} from "reselect";
import {QUERY_ALL_ACTIVE_JOBS_PAGINATED} from "../../graphql/jobs.queries";
import {selectBodyshop} from "../../redux/user/user.selectors";
import {onlyUnique} from "../../utils/arrayHelper";
import CurrencyFormatter from "../../utils/CurrencyFormatter";
import { onlyUnique } from "../../utils/arrayHelper";
import { alphaSort, statusSort } from "../../utils/sorters";
import useLocalStorage from "../../utils/useLocalStorage";
import AlertComponent from "../alert/alert.component";
import ChatOpenButton from "../chat-open-button/chat-open-button.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({
bodyshop: selectBodyshop,
bodyshop: selectBodyshop,
});
export function JobsList({ bodyshop }) {
const searchParams = queryString.parse(useLocation().search);
const { selected } = searchParams;
const selectedBreakpoint = Object.entries(Grid.useBreakpoint())
.filter((screen) => !!screen[1])
.slice(-1)[0];
const { loading, error, data, refetch } = useQuery(QUERY_ALL_ACTIVE_JOBS, {
variables: {
statuses: bodyshop.md_ro_statuses.active_statuses || ["Open", "Open*"],
},
fetchPolicy: "network-only",
nextFetchPolicy: "network-only",
});
const mapDispatchToProps = () => ({
});
const [state, setState] = useState({ sortedInfo: {} });
const [filter, setFilter] = useLocalStorage("filter_jobs_list", null);
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 { t } = useTranslation();
const history = useHistory();
const [searchText, setSearchText] = useState("");
const selectedBreakpoint = Object.entries(Grid.useBreakpoint())
.filter((screen) => !!screen[1])
.slice(-1)[0];
if (error) return <AlertComponent message={error.message} type="error" />;
const {loading, error, data, refetch} = useQuery(QUERY_ALL_ACTIVE_JOBS_PAGINATED, {
variables: {
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",
nextFetchPolicy: "network-only",
});
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 total = data?.jobs_aggregate?.aggregate?.count || 0;
const handleTableChange = (pagination, filters, sorter) => {
setState({ ...state, sortedInfo: sorter });
setFilter(filters);
};
const [state, setState] = useState({
filteredInfo: {text: ""},
});
const handleOnRowClick = (record) => {
if (record) {
if (record.id) {
history.push({
search: queryString.stringify({
...searchParams,
selected: record.id,
}),
});
}
const {t} = useTranslation();
const history = useHistory();
const jobs = data?.jobs || [];
const handleTableChange = (pagination, filters, 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 columns = [
{
title: t("jobs.fields.ro_number"),
dataIndex: "ro_number",
key: "ro_number",
sorter: (a, b) =>
parseInt((a.ro_number || "0").replace(/\D/g, "")) -
parseInt((b.ro_number || "0").replace(/\D/g, "")),
sortOrder:
state.sortedInfo.columnKey === "ro_number" && state.sortedInfo.order,
render: (text, record) => (
<Link
to={"/manage/jobs/" + record.id}
onClick={(e) => e.stopPropagation()}
>
<Space>
{record.ro_number || t("general.labels.na")}
{record.production_vars && record.production_vars.alert ? (
<ExclamationCircleFilled className="production-alert" />
) : null}
{record.suspended && (
<PauseCircleOutlined style={{ color: "orangered" }} />
)}
{record.iouparent && (
<Tooltip title={t("jobs.labels.iou")}>
<BranchesOutlined style={{ color: "orangered" }} />
</Tooltip>
)}
</Space>
</Link>
),
},
{
title: t("jobs.fields.owner"),
dataIndex: "owner",
key: "owner",
ellipsis: true,
responsive: ["md"],
sorter: (a, b) => alphaSort(a.ownr_ln, b.ownr_ln),
sortOrder:
state.sortedInfo.columnKey === "owner" && state.sortedInfo.order,
render: (text, record) => {
return record.ownerid ? (
<Link
to={"/manage/owners/" + record.ownerid}
onClick={(e) => e.stopPropagation()}
>
<OwnerNameDisplay ownerObject={record} />
</Link>
) : (
<span>
<OwnerNameDisplay ownerObject={record} />
const handleOnRowClick = (record) => {
if (record) {
if (record.id) {
history.push({
search: queryString.stringify({
...search,
selected: record.id,
}),
});
}
}
};
const columns = [
{
title: t("jobs.fields.ro_number"),
dataIndex: "ro_number",
key: "ro_number",
sorter: true,
sortOrder:
sortcolumn === "ro_number" && sortorder,
render: (text, record) => (
<Link
to={"/manage/jobs/" + record.id}
onClick={(e) => e.stopPropagation()}
>
<Space>
{record.ro_number || t("general.labels.na")}
{record.production_vars && record.production_vars.alert ? (
<ExclamationCircleFilled className="production-alert"/>
) : null}
{record.suspended && (
<PauseCircleOutlined style={{color: "orangered"}}/>
)}
{record.iouparent && (
<Tooltip title={t("jobs.labels.iou")}>
<BranchesOutlined style={{color: "orangered"}}/>
</Tooltip>
)}
</Space>
</Link>
),
},
{
title: t("jobs.fields.owner"),
dataIndex: "owner",
key: "owner",
ellipsis: true,
responsive: ["md"],
sorter: false,
sortOrder: sortcolumn === "owner" && sortorder,
render: (text, record) => {
return record.ownerid ? (
<Link
to={"/manage/owners/" + record.ownerid}
onClick={(e) => e.stopPropagation()}
>
<OwnerNameDisplay ownerObject={record}/>
</Link>
) : (
<span>
<OwnerNameDisplay ownerObject={record}/>
</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);
);
},
};
}}
/>
</Card>
);
},
{
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: true,
sortOrder:
sortcolumn === "status" && sortorder,
filters:bodyshop.md_ro_statuses.statuses.map((s) => {
return { text: s, value: [s] };
}),
onFilter: (value, record) => value.includes(record.status),
render: (text, record) => {
return record.status || t("general.labels.na");
},
},
{
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: true,
sortOrder:
sortcolumn === "plate_no" && sortorder,
},
{
title: t("jobs.fields.clm_no"),
dataIndex: "clm_no",
key: "clm_no",
ellipsis: true,
responsive: ["md"],
sorter: true,
sortOrder:
sortcolumn === "clm_no" && sortorder,
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,
// TODO: Restore Filters?
// 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: true,
sortOrder:
sortcolumn === "clm_total" && sortorder,
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"],
// TODO Restore Filters?
// 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>
{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()}>
<SyncOutlined />
</Button>
<Input.Search
placeholder={search.search || t("general.labels.search")}
onSearch={(value) => {
search.search = value;
history.push({ search: queryString.stringify(search) });
searchJobs(value);
}}
loading={loading || searchLoading}
enterButton
/>
</Space>
}
>
<Table
loading={loading || searchLoading }
pagination={
search?.search
? {
pageSize: pageLimit,
showSizeChanger: false,
}
: {
pageSize: pageLimit,
current: parseInt(page || 1),
total: total,
showSizeChanger: false,
}
}
columns={columns}
rowKey="id"
dataSource={search?.search ? openSearchResults : jobs}
scroll={{
x: selectedBreakpoint ? scrollMapper[selectedBreakpoint[0]] : "100%",
}}
rowSelection={{
onSelect: (record) => {
handleOnRowClick(record);
},
selectedRowKeys: [selected],
type: "radio",
}}
onChange={handleTableChange}
onRow={(record) => {
return {
onClick: () => {
handleOnRowClick(record);
},
};
}}
/>
</Card>
);
}
export default connect(mapStateToProps, null)(JobsList);
export default connect(mapStateToProps, mapDispatchToProps)(JobsList);

View File

@@ -16,12 +16,11 @@ import { QUERY_ALL_ACTIVE_JOBS } from "../../graphql/jobs.queries";
import { selectBodyshop } from "../../redux/user/user.selectors";
import CurrencyFormatter from "../../utils/CurrencyFormatter";
import { onlyUnique } from "../../utils/arrayHelper";
import { pageLimit } from "../../utils/config";
import { alphaSort, statusSort } from "../../utils/sorters";
import useLocalStorage from "../../utils/useLocalStorage";
import { alphaSort } from "../../utils/sorters";
import AlertComponent from "../alert/alert.component";
import ChatOpenButton from "../chat-open-button/chat-open-button.component";
import OwnerNameDisplay from "../owner-name-display/owner-name-display.component";
import {pageLimit} from "../../utils/config";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
@@ -54,8 +53,10 @@ export function JobsReadyList({ bodyshop }) {
nextFetchPolicy: "network-only",
});
const [state, setState] = useState({ sortedInfo: {} });
const [filter, setFilter] = useLocalStorage("filter_jobs_ready", null);
const [state, setState] = useState({
sortedInfo: {},
filteredInfo: { text: "" },
});
const { t } = useTranslation();
const history = useHistory();
@@ -104,8 +105,7 @@ export function JobsReadyList({ bodyshop }) {
: [];
const handleTableChange = (pagination, filters, sorter) => {
setState({ ...state, sortedInfo: sorter });
setFilter(filters);
setState({ ...state, filteredInfo: filters, sortedInfo: sorter });
};
const handleOnRowClick = (record) => {
@@ -129,6 +129,7 @@ export function JobsReadyList({ bodyshop }) {
sorter: (a, b) => alphaSort(a.ro_number, b.ro_number),
sortOrder:
state.sortedInfo.columnKey === "ro_number" && state.sortedInfo.order,
render: (text, record) => (
<Link
to={"/manage/jobs/" + record.id}
@@ -156,6 +157,7 @@ export function JobsReadyList({ bodyshop }) {
dataIndex: "owner",
key: "owner",
ellipsis: true,
responsive: ["md"],
sorter: (a, b) => alphaSort(a.ownr_ln, b.ownr_ln),
sortOrder:
@@ -195,15 +197,16 @@ export function JobsReadyList({ bodyshop }) {
<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
@@ -214,17 +217,11 @@ export function JobsReadyList({ bodyshop }) {
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",
@@ -277,7 +274,6 @@ export function JobsReadyList({ bodyshop }) {
dataIndex: "ins_co_nm",
key: "ins_co_nm",
ellipsis: true,
filteredValue: filter?.ins_co_nm || null,
filters:
(jobs &&
jobs
@@ -285,11 +281,10 @@ export function JobsReadyList({ bodyshop }) {
.filter(onlyUnique)
.map((s) => {
return {
text: s || "No Ins Co.*",
text: s,
value: [s],
};
})
.sort((a, b) => alphaSort(a.text, b.text))) ||
})) ||
[],
onFilter: (value, record) => value.includes(record.ins_co_nm),
responsive: ["md"],
@@ -300,6 +295,7 @@ export function JobsReadyList({ bodyshop }) {
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,
@@ -310,10 +306,9 @@ export function JobsReadyList({ bodyshop }) {
{
title: t("jobs.labels.estimator"),
dataIndex: "jobs.labels.estimator",
key: "estimator",
key: "jobs.labels.estimator",
ellipsis: true,
responsive: ["xl"],
filteredValue: filter?.estimator || null,
filterSearch: true,
filters:
(jobs &&
@@ -322,11 +317,10 @@ export function JobsReadyList({ bodyshop }) {
.filter(onlyUnique)
.map((s) => {
return {
text: s || "No Estimator*",
text: s || "N/A",
value: [s],
};
})
.sort((a, b) => alphaSort(a.text, b.text))) ||
})) ||
[],
onFilter: (value, record) =>
value.includes(

View File

@@ -2,7 +2,7 @@ import { Space, Tag } from "antd";
import React from "react";
import { Link } from "react-router-dom";
export default function JobsRelatedRos({ jobid, job, disabled }) {
export default function JobsRelatedRos({ jobid, job }) {
if (!(job && job.vehicle && job.vehicle.jobs)) return null;
return (
<Space wrap>
@@ -10,15 +10,9 @@ export default function JobsRelatedRos({ jobid, job, disabled }) {
.filter((j) => j.id !== job.id)
.map((j) => (
<Tag key={j.id}>
{disabled ? (
<>{`${j.ro_number || "N/A"}${j.clm_no ? ` | ${j.clm_no}` : ""}${
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>
)}
<Link to={`/manage/jobs/${j?.id}`}>{`${j.ro_number || "N/A"}${
j.clm_no ? ` | ${j.clm_no}` : ""
}${j.status ? ` | ${j.status}` : ""}`}</Link>
</Tag>
))}
</Space>

View File

@@ -1,7 +1,7 @@
import { Button, Dropdown, Menu } from "antd";
import React from "react";
import { useTranslation } from "react-i18next";
import { Button, Dropdown, Menu } from "antd";
import dataSource from "./production-list-columns.data";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
@@ -24,7 +24,6 @@ export function ProductionColumnsComponent({
columnState,
technician,
bodyshop,
data,
tableState,
}) {
const [columns, setColumns] = columnState;
@@ -37,7 +36,6 @@ export function ProductionColumnsComponent({
bodyshop,
technician,
state: tableState,
data: data,
activeStatuses: bodyshop.md_ro_statuses.active_statuses,
}).filter((i) => i.key === e.key),
]);
@@ -48,7 +46,6 @@ export function ProductionColumnsComponent({
technician,
state: tableState,
activeStatuses: bodyshop.md_ro_statuses.active_statuses,
data: data,
});
const menu = (
<Menu

View File

@@ -1,23 +1,12 @@
import { ExclamationCircleFilled } from "@ant-design/icons";
import { useMutation } from "@apollo/client";
import { Dropdown, Menu } from "antd";
import React from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { logImEXEvent } from "../../firebase/firebase.utils";
import { useMutation } from "@apollo/client";
import { UPDATE_JOB } from "../../graphql/jobs.queries";
import { insertAuditTrail } from "../../redux/application/application.actions";
import AuditTrailMapping from "../../utils/AuditTrailMappings";
import { logImEXEvent } from "../../firebase/firebase.utils";
const mapStateToProps = createStructuredSelector({});
const mapDispatchToProps = (dispatch) => ({
insertAuditTrail: ({ jobid, operation }) =>
dispatch(insertAuditTrail({ jobid, operation })),
});
export function ProductionListColumnAlert({ record, insertAuditTrail }) {
export default function ProductionListColumnAlert({ record }) {
const { t } = useTranslation();
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(() => {
if (record.refetch) record.refetch();
});
@@ -77,8 +58,3 @@ export function ProductionListColumnAlert({ record, insertAuditTrail }) {
</Dropdown>
);
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(ProductionListColumnAlert);

View File

@@ -1,4 +1,4 @@
import { BranchesOutlined, PauseCircleOutlined } from "@ant-design/icons";
import { PauseCircleOutlined, BranchesOutlined } from "@ant-design/icons";
import { Space, Tooltip } from "antd";
import i18n from "i18next";
import moment from "moment";
@@ -6,7 +6,6 @@ import { Link } from "react-router-dom";
import CurrencyFormatter from "../../utils/CurrencyFormatter";
import { TimeFormatter } from "../../utils/DateFormatter";
import PhoneFormatter from "../../utils/PhoneFormatter";
import { onlyUnique } from "../../utils/arrayHelper";
import { alphaSort, dateSort, statusSort } from "../../utils/sorters";
import JobAltTransportChange from "../job-at-change/job-at-change.component";
import JobPartsQueueCount from "../job-parts-queue-count/job-parts-queue-count.component";
@@ -26,7 +25,7 @@ import ProductionListColumnCategory from "./production-list-columns.status.categ
import ProductionListColumnStatus from "./production-list-columns.status.component";
import ProductionlistColumnTouchTime from "./prodution-list-columns.touchtime.component";
const r = ({ technician, state, activeStatuses, data, bodyshop }) => {
const r = ({ technician, state, activeStatuses, bodyshop }) => {
return [
{
title: i18n.t("jobs.actions.viewdetail"),
@@ -76,14 +75,7 @@ const r = ({ technician, state, activeStatuses, data, bodyshop }) => {
dataIndex: "ownr",
key: "ownr",
ellipsis: true,
render: (text, record) =>
technician ? (
<OwnerNameDisplay ownerObject={record} />
) : (
<Link to={`/manage/owners/${record.ownerid}`}>
<OwnerNameDisplay ownerObject={record} />
</Link>
),
render: (text, record) => <OwnerNameDisplay ownerObject={record} />,
sorter: (a, b) => alphaSort(a.ownr_ln, b.ownr_ln),
sortOrder:
state.sortedInfo.columnKey === "ownr" && state.sortedInfo.order,
@@ -100,18 +92,13 @@ const r = ({ technician, state, activeStatuses, data, bodyshop }) => {
),
sortOrder:
state.sortedInfo.columnKey === "vehicle" && state.sortedInfo.order,
render: (text, record) =>
technician ? (
<>{`${record.v_model_yr || ""} ${record.v_make_desc || ""} ${
record.v_model_desc || ""
} ${record.v_color || ""} ${record.plate_no || ""}`}</>
) : (
<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>
),
render: (text, record) => (
<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"),
@@ -549,36 +536,6 @@ const r = ({ technician, state, activeStatuses, data, bodyshop }) => {
<JobPartsQueueCount parts={record.joblines_status} record={record} />
),
},
{
title: i18n.t("jobs.labels.estimator"),
dataIndex: "estimator",
key: "estimator",
sorter: (a, b) =>
alphaSort(
`${a.est_ct_fn || ""} ${a.est_ct_ln || ""}`.trim(),
`${b.est_ct_fn || ""} ${b.est_ct_ln || ""}`.trim()
),
sortOrder:
state.sortedInfo.columnKey === "estimator" && state.sortedInfo.order,
filters:
(data &&
data
.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(),
},
//Added as a place holder for St Claude. Not implemented as it requires another join for a field used by only 1 client.
// {

View File

@@ -13,14 +13,12 @@ import { selectTechnician } from "../../redux/tech/tech.selectors";
import { selectBodyshop } from "../../redux/user/user.selectors";
import CurrencyFormatter from "../../utils/CurrencyFormatter";
import { DateFormatter } from "../../utils/DateFormatter";
import PhoneNumberFormatter from "../../utils/PhoneFormatter";
import AlertComponent from "../alert/alert.component";
import StartChatButton from "../chat-open-button/chat-open-button.component";
import JobAtChange from "../job-at-change/job-at-change.component";
import JobDetailCardsDocumentsComponent from "../job-detail-cards/job-detail-cards.documents.component";
import JobDetailCardsNotesComponent from "../job-detail-cards/job-detail-cards.notes.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 ScoreboardAddButton from "../job-scoreboard-add-button/job-scoreboard-add-button.component";
import LoadingSkeleton from "../loading-skeleton/loading-skeleton.component";
@@ -105,13 +103,7 @@ export function ProductionListDetail({
{error && <AlertComponent error={JSON.stringify(error)} />}
{!loading && data && (
<div>
<CardTemplate
title={t("jobs.labels.employeeassignments")}
loading={loading}
>
<JobEmployeeAssignments job={data.jobs_by_pk} refetch={refetch} />
</CardTemplate>
<div style={{ height: "8px" }} />
<JobEmployeeAssignments job={data.jobs_by_pk} refetch={refetch} />
<Descriptions bordered column={1}>
<Descriptions.Item label={t("jobs.fields.ro_number")}>
{theJob.ro_number || ""}
@@ -119,7 +111,7 @@ export function ProductionListDetail({
<Descriptions.Item label={t("jobs.fields.alt_transport")}>
<Space>
{data.jobs_by_pk.alt_transport || ""}
<JobAtChange job={data.jobs_by_pk} />
<JobAtChange event={{ job: data.jobs_by_pk }} />
</Space>
</Descriptions.Item>
<Descriptions.Item label={t("jobs.fields.clm_no")}>
@@ -129,30 +121,15 @@ export function ProductionListDetail({
{theJob.ins_co_nm || ""}
</Descriptions.Item>
<Descriptions.Item label={t("jobs.fields.owner")}>
<Space>
<OwnerNameDisplay ownerObject={theJob} />
{!technician ? (
<>
<StartChatButton
phone={data.jobs_by_pk.ownr_ph1}
jobid={data.jobs_by_pk.id}
/>
<StartChatButton
phone={data.jobs_by_pk.ownr_ph2}
jobid={data.jobs_by_pk.id}
/>
</>
) : (
<>
<PhoneNumberFormatter>
{data.jobs_by_pk.ownr_ph1}
</PhoneNumberFormatter>
<PhoneNumberFormatter>
{data.jobs_by_pk.ownr_ph2}
</PhoneNumberFormatter>
</>
)}
</Space>
<OwnerNameDisplay ownerObject={theJob} />
<StartChatButton
phone={data.jobs_by_pk.ownr_ph1}
jobid={data.jobs_by_pk.id}
/>
<StartChatButton
phone={data.jobs_by_pk.ownr_ph2}
jobid={data.jobs_by_pk.id}
/>
</Descriptions.Item>
<Descriptions.Item label={t("jobs.fields.vehicle")}>
{`${theJob.v_model_yr || ""} ${theJob.v_color || ""} ${
@@ -169,24 +146,21 @@ export function ProductionListDetail({
<DateFormatter>{theJob.scheduled_completion}</DateFormatter>
</Descriptions.Item>
</Descriptions>
<div style={{ height: "8px" }} />
<JobDetailCardsPartsComponent
loading={loading}
data={data ? data.jobs_by_pk : null}
/>
<div style={{ height: "8px" }} />
<JobDetailCardsNotesComponent
loading={loading}
data={data ? data.jobs_by_pk : null}
/>
{!bodyshop.uselocalmediaserver && (
<>
<div style={{ height: "8px" }} />
<JobDetailCardsDocumentsComponent
loading={loading}
data={data ? data.jobs_by_pk : null}
/>
</>
<JobDetailCardsDocumentsComponent
loading={loading}
data={data ? data.jobs_by_pk : null}
/>
)}
</div>
)}

View File

@@ -24,7 +24,6 @@ export function ProductionListTable({
technician,
currentUser,
state,
data,
setColumns,
setState,
}) {
@@ -42,7 +41,6 @@ export function ProductionListTable({
bodyshop,
technician,
state,
data: data,
activeStatuses: bodyshop.md_ro_statuses.active_statuses,
}).find((e) => e.key === k.key),
width: k.width,
@@ -97,7 +95,6 @@ export function ProductionListTable({
...ProductionListColumns({
technician,
state,
data: data,
activeStatuses: bodyshop.md_ro_statuses.active_statuses,
}).find((e) => e.key === k.key),
width: k.width,

View File

@@ -10,7 +10,7 @@ import {
Statistic,
Table,
} from "antd";
import React, { useEffect, useMemo, useState } from "react";
import React, { useMemo, useState } from "react";
import ReactDragListView from "react-drag-listview";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
@@ -79,7 +79,6 @@ export function ProductionListTable({
bodyshop,
technician,
state,
data: data,
activeStatuses: bodyshop.md_ro_statuses.active_statuses,
}).find((e) => e.key === k.key),
width: k.width ?? 100,
@@ -88,33 +87,6 @@ export function ProductionListTable({
[]
);
useEffect(() => {
const newColumns =
(state &&
matchingColumnConfig &&
matchingColumnConfig.columns.columnKeys.map((k) => {
return {
...ProductionListColumns({
bodyshop,
technician,
state,
data: data,
activeStatuses: bodyshop.md_ro_statuses.active_statuses,
}).find((e) => e.key === k.key),
width: k.width ?? 100,
};
})) ||
[];
setColumns(newColumns);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [
//state,
matchingColumnConfig,
bodyshop,
technician,
data,
]); //State removed from dependency array as it causes race condition when removing columns from table view and is not needed.
const handleTableChange = (pagination, filters, sorter) => {
setState({
...state,
@@ -132,8 +104,7 @@ export function ProductionListTable({
const removeColumn = (e) => {
const { key } = e;
const newColumns = columns.filter((i) => i.key !== key);
setColumns(newColumns);
setColumns(columns.filter((i) => i.key !== key));
};
const handleResize =
@@ -256,7 +227,6 @@ export function ProductionListTable({
<ProductionListColumnsAdd
columnState={[columns, setColumns]}
tableState={state}
data={data}
/>
<ProductionListSaveConfigButton
columns={columns}
@@ -267,7 +237,6 @@ export function ProductionListTable({
state={state}
setState={setState}
setColumns={setColumns}
data={data}
/>
<Input

View File

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

View File

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

View File

@@ -216,7 +216,6 @@ export function ScheduleJobModalContainer({
okButtonProps={{
loading: loading,
}}
closable={false}
>
<Form
form={form}

View File

@@ -29,7 +29,7 @@ export default connect(
export function ScoreboardTimeTicketsStats({ bodyshop }) {
const { t } = useTranslation();
const startDate = moment().startOf("month");
const startDate = moment().startOf("month")
const endDate = moment().endOf("month");
const fixedPeriods = useMemo(() => {
@@ -84,8 +84,6 @@ export function ScoreboardTimeTicketsStats({ bodyshop }) {
end: endDate.format("YYYY-MM-DD"),
fixedStart: fixedPeriods.start.format("YYYY-MM-DD"),
fixedEnd: fixedPeriods.end.format("YYYY-MM-DD"),
jobStart: startDate,
jobEnd: endDate,
},
fetchPolicy: "network-only",
nextFetchPolicy: "network-only",
@@ -342,21 +340,11 @@ export function ScoreboardTimeTicketsStats({ bodyshop }) {
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 {
fixed: ret,
combinedData: combinedData,
labData: labData,
larData: larData,
jobData: jobData,
};
}, [fixedPeriods, data, bodyshop]);
@@ -368,10 +356,7 @@ export function ScoreboardTimeTicketsStats({ bodyshop }) {
<ScoreboardTimeticketsTargetsTable />
</Col>
<Col span={24}>
<ScoreboardTicketsStats
data={calculatedData.fixed}
jobData={calculatedData.jobData}
/>
<ScoreboardTicketsStats data={calculatedData.fixed} />
</Col>
<Col span={24}>
<ScoreboardTimeTicketsChart

View File

@@ -41,7 +41,7 @@ function useLocalStorage(key, initialValue) {
return [storedValue, setStoredValue];
}
export function ScoreboardTicketsStats({ data, jobData, bodyshop }) {
export function ScoreboardTicketsStats({ data, bodyshop }) {
const { t } = useTranslation();
const [isLarge, setIsLarge] = useLocalStorage("isLargeStatistic", false);
@@ -408,7 +408,7 @@ export function ScoreboardTicketsStats({ data, jobData, bodyshop }) {
{/* Monthly Stats */}
<Row gutter={[16, 16]}>
{/* This Month */}
<Col span={7} align="center">
<Col span={8} align="center">
<Card size="small" title={t("scoreboard.labels.thismonth")}>
<Row gutter={[16, 16]}>
<Col span={24}>
@@ -482,7 +482,7 @@ export function ScoreboardTicketsStats({ data, jobData, bodyshop }) {
</Card>
</Col>
{/* Last Month */}
<Col span={7} align="center">
<Col span={8} align="center">
<Card size="small" title={t("scoreboard.labels.lastmonth")}>
<Row gutter={[16, 16]}>
<Col span={24}>
@@ -556,7 +556,7 @@ export function ScoreboardTicketsStats({ data, jobData, bodyshop }) {
</Card>
</Col>
{/* Efficiency Over Period */}
<Col span={7} align="center">
<Col span={8} align="center">
<Card
size="small"
title={t("scoreboard.labels.efficiencyoverperiod")}
@@ -604,40 +604,6 @@ export function ScoreboardTicketsStats({ data, jobData, bodyshop }) {
</Row>
</Card>
</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>
</Space>
{/* Disclaimer */}

View File

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

View File

@@ -1,20 +1,14 @@
import { Button, Table } from "antd";
import queryString from "query-string";
import React, { useState } from "react";
import React from "react";
import { useTranslation } from "react-i18next";
import { useHistory, useLocation } from "react-router-dom";
import { alphaSort } from "../../utils/sorters";
export default function ShopEmployeesListComponent({ loading, employees }) {
const { t } = useTranslation();
const history = useHistory();
const search = queryString.parse(useLocation().search);
const [state, setState] = useState({
sortedInfo: {},
filteredInfo: { text: "" },
});
const handleOnRowClick = (record) => {
if (record) {
search.employeeId = record.id;
@@ -24,82 +18,32 @@ export default function ShopEmployeesListComponent({ loading, employees }) {
history.push({ search: queryString.stringify(search) });
}
};
const handleTableChange = (pagination, filters, sorter) => {
setState({ ...state, filteredInfo: filters, sortedInfo: sorter });
};
const columns = [
{
title: t("employees.fields.employee_number"),
dataIndex: "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.labels.name"),
dataIndex: "employee_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.first_name"),
dataIndex: "first_name",
key: "first_name",
},
{
title: t("employees.fields.last_name"),
dataIndex: "last_name",
key: "last_name",
},
{
title: t("employees.labels.rate_type"),
dataIndex: "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) =>
record.flat_rate
? t("employees.labels.flat_rate")
: 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 (
<div>
@@ -130,7 +74,6 @@ export default function ShopEmployeesListComponent({ loading, employees }) {
type: "radio",
selectedRowKeys: [search.employeeId],
}}
onChange={handleTableChange}
onRow={(record, rowIndex) => {
return {
onClick: (event) => {

View File

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

View File

@@ -166,6 +166,9 @@ export function TechClockOffButton({
},
({ getFieldValue }) => ({
validator(rule, value) {
console.log(
bodyshop.tt_enforce_hours_for_tech_console
);
if (!bodyshop.tt_enforce_hours_for_tech_console) {
return Promise.resolve();
}

View File

@@ -1,8 +1,7 @@
import { Button, Form, Input } from "antd";
import React, { useEffect } from "react";
import React from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { Redirect } from "react-router-dom";
import { createStructuredSelector } from "reselect";
import { techLoginStart } from "../../redux/tech/tech.actions";
import {
@@ -12,6 +11,7 @@ import {
} from "../../redux/tech/tech.selectors";
import AlertComponent from "../alert/alert.component";
import "./tech-login.styles.scss";
import { Redirect } from "react-router-dom";
const mapStateToProps = createStructuredSelector({
technician: selectTechnician,
@@ -35,10 +35,6 @@ export function TechLogin({
techLoginStart(values);
};
useEffect(() => {
document.title = t("titles.techconsole");
}, [t]);
return (
<div className="tech-login-container">
{technician ? <Redirect to={`/tech/joblookup`} /> : null}

View File

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

View File

@@ -3,12 +3,11 @@ import { gql } from "@apollo/client";
export const QUERY_EMPLOYEES = gql`
query QUERY_EMPLOYEES {
employees(order_by: { employee_number: asc }) {
active
employee_number
last_name
id
first_name
flat_rate
id
last_name
employee_number
}
}
`;

View File

@@ -5,13 +5,13 @@ export const QUERY_ALL_ACTIVE_JOBS_PAGINATED = gql`
$offset: Int
$limit: Int
$order: [jobs_order_by!]
$statuses: [String!]!
$statusFilters: [String!]!,
$isConverted: Boolean
) {
jobs(
offset: $offset
limit: $limit
where: { status: { _in: $statuses }, converted: { _eq: $isConverted } }
where: { status: { _in: $statusFilters }, converted: { _eq: $isConverted } }
order_by: $order
) {
iouparent
@@ -52,7 +52,7 @@ export const QUERY_ALL_ACTIVE_JOBS_PAGINATED = gql`
est_ct_fn
est_ct_ln
}
jobs_aggregate(where: { status: { _in: $statuses } }) {
jobs_aggregate(where: { status: { _in: $statusFilters } }) {
aggregate {
count(distinct: true)
}
@@ -120,9 +120,7 @@ export const QUERY_PARTS_QUEUE = gql`
}
}
jobs(
where: {
_and: [{ status: { _in: $statuses }, converted: { _eq: true } }]
}
where: { _and: [{ status: { _in: $statuses }, converted: { _eq: true } }] }
offset: $offset
limit: $limit
order_by: $order
@@ -338,7 +336,6 @@ export const QUERY_JOBS_IN_PRODUCTION = gql`
category
iouparent
ro_number
ownerid
ownr_fn
ownr_ln
ownr_co_nm
@@ -367,8 +364,6 @@ export const QUERY_JOBS_IN_PRODUCTION = gql`
employee_refinish
employee_prep
employee_csr
est_ct_fn
est_ct_ln
suspended
date_repairstarted
joblines_status {

View File

@@ -143,14 +143,9 @@ export const QUERY_TIME_TICKETS_IN_RANGE_SB = gql`
$end: date!
$fixedStart: date!
$fixedEnd: date!
$jobStart: timestamptz!
$jobEnd: timestamptz!
) {
timetickets(
where: {
date: { _gte: $start, _lte: $end }
cost_center: { _neq: "timetickets.labels.shift" }
}
where: { date: { _gte: $start, _lte: $end }, cost_center: {_neq: "timetickets.labels.shift"} }
order_by: { date: desc_nulls_first }
) {
actualhrs
@@ -181,10 +176,7 @@ export const QUERY_TIME_TICKETS_IN_RANGE_SB = gql`
}
}
fixedperiod: timetickets(
where: {
date: { _gte: $fixedStart, _lte: $fixedEnd }
cost_center: { _neq: "timetickets.labels.shift" }
}
where: { date: { _gte: $fixedStart, _lte: $fixedEnd }, cost_center: {_neq: "timetickets.labels.shift"} }
order_by: { date: desc_nulls_first }
) {
actualhrs
@@ -213,25 +205,6 @@ export const QUERY_TIME_TICKETS_IN_RANGE_SB = gql`
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 { Form, notification } from "antd";
import moment from "moment";
import queryString from "query-string";
import React, { useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
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 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 { QUERY_CC_BY_PK, UPDATE_CC } from "../../graphql/courtesy-car.queries";
import {
@@ -16,10 +13,13 @@ import {
setBreadcrumbs,
setSelectedHeader,
} from "../../redux/application/application.actions";
import { pageLimit } from "../../utils/config";
import { CreateRecentItem } from "../../utils/create-recent-item";
import UndefinedToNull from "./../../utils/undefinedtonull";
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) => ({
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
@@ -112,10 +112,7 @@ export function CourtesyCarDetailPageContainer({
setSaveLoading(true);
const result = await updateCourtesyCar({
variables: {
cc: { ...UndefinedToNull(values, ["readiness"]) },
ccId: ccId,
},
variables: { cc: { ...values }, ccId: ccId },
refetchQueries: ["QUERY_CC_BY_PK"],
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 { generateJobLinesUpdatesForInvoicing } from "../../graphql/jobs-lines.queries";
import { UPDATE_JOB } from "../../graphql/jobs.queries";
import { insertAuditTrail } from "../../redux/application/application.actions";
import { selectJobReadOnly } from "../../redux/application/application.selectors";
import { selectBodyshop } from "../../redux/user/user.selectors";
import AuditTrailMapping from "../../utils/AuditTrailMappings";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
jobRO: selectJobReadOnly,
});
const mapDispatchToProps = (dispatch) => ({
insertAuditTrail: ({ jobid, operation }) =>
dispatch(insertAuditTrail({ jobid, operation })),
});
export function JobsCloseComponent({ job, bodyshop, jobRO, insertAuditTrail, }) {
export function JobsCloseComponent({ job, bodyshop, jobRO }) {
const { t } = useTranslation();
const [form] = Form.useForm();
const client = useApolloClient();
@@ -118,10 +110,6 @@ export function JobsCloseComponent({ job, bodyshop, jobRO, insertAuditTrail, })
notification["success"]({
message: t("jobs.successes.closed"),
});
insertAuditTrail({
jobid: job.id,
operation: AuditTrailMapping.jobinvoiced(),
});
// history.push(`/manage/jobs/${job.id}`);
} else {
setLoading(false);
@@ -539,4 +527,4 @@ export function JobsCloseComponent({ job, bodyshop, jobRO, insertAuditTrail, })
</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 { Form, notification } from "antd";
import _ from "lodash";
import React, { useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
@@ -90,7 +90,6 @@ function JobsCreateContainer({ bodyshop, setBreadcrumbs, setSelectedHeader }) {
{},
values,
{ date_open: new Date() },
{ date_estimated: new Date() },
{
vehicle:
state.vehicle.selectedid || state.vehicle.none

View File

@@ -1,17 +1,10 @@
import { Divider } from "antd";
import React, { useEffect } from "react";
import { useTranslation } from "react-i18next";
import React from "react";
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 TechJobStatistics from "../../components/tech-job-statistics/tech-job-statistics.component";
export default function TechClockComponent() {
const { t } = useTranslation();
useEffect(() => {
document.title = t("titles.techjobclock");
}, [t]);
return (
<div>
<TechJobStatistics />

View File

@@ -1,16 +1,9 @@
import React, { useEffect } from "react";
import { useTranslation } from "react-i18next";
import React from "react";
import RbacWrapperComponent from "../../components/rbac-wrapper/rbac-wrapper.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";
export default function TechLookupContainer() {
const { t } = useTranslation();
useEffect(() => {
document.title = t("titles.techjoblookup");
}, [t]);
return (
<div>
<RbacWrapperComponent action="jobs:list-active">

View File

@@ -1,14 +1,7 @@
import React, { useEffect } from "react";
import { useTranslation } from "react-i18next";
import React from "react";
import TimeTicketShift from "../../components/time-ticket-shift/time-ticket-shift.container";
export default function TechShiftClock() {
const { t } = useTranslation();
useEffect(() => {
document.title = t("titles.techshiftclock");
}, [t]);
return (
<div>
<TimeTicketShift isTechConsole />

View File

@@ -103,7 +103,6 @@
"admin_jobmarkforreexport": "ADMIN: Job marked for re-export.",
"admin_jobuninvoice": "ADMIN: Job has been uninvoiced.",
"admin_jobunvoid": "ADMIN: Job has been unvoided.",
"alerttoggle": "Alert Toggle set to {{status}}",
"appointmentcancel": "Appointment canceled. Lost Reason: {{lost_sale_reason}}.",
"appointmentinsert": "Appointment created. Appointment Date: {{start}}.",
"billposted": "Bill with invoice number {{invoice_number}} posted.",
@@ -112,7 +111,6 @@
"jobassignmentchange": "Employee {{name}} assigned to {{operation}}",
"jobassignmentremoved": "Employee assignment removed for {{operation}}",
"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}}.",
"jobfieldchanged": "Job field $t(jobs.fields.{{field}}) changed to {{value}}.",
"jobimported": "Job imported.",
@@ -205,7 +203,6 @@
"entered_total": "Total of Entered Lines",
"enteringcreditmemo": "You are entering a credit memo. Please ensure you are also entering positive values.",
"federal_tax": "Federal Tax",
"federal_tax_exempt": "Federal Tax Exempt?",
"generatepartslabel": "Generate Parts Labels after Saving?",
"iouexists": "An IOU exists that is associated to this RO.",
"local_tax": "Local Tax",
@@ -214,7 +211,6 @@
"new": "New Bill",
"noneselected": "No bill selected.",
"onlycmforinvoiced": "Only credit memos can be entered for any Job that has been invoiced, exported, or voided.",
"printlabels": "Print Labels",
"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.",
"state_tax": "Provincial/State Tax",
@@ -451,7 +447,6 @@
"config": "Shop -> Config",
"dashboard": "Shop -> Dashboard",
"rbac": "Shop -> RBAC",
"reportcenter": "Shop -> Report Center",
"templates": "Shop -> Templates",
"vendors": "Shop -> Vendors"
},
@@ -786,7 +781,6 @@
"notes": "Notes",
"plate": "Plate Number",
"purchasedate": "Purchase Date",
"readiness": "Readiness",
"registrationexpires": "Registration Expires On",
"serviceenddate": "Usage End Date",
"servicestartdate": "Usage Start Date",
@@ -823,10 +817,6 @@
},
"successes": {
"saved": "Courtesy Car saved successfully."
},
"readiness": {
"notready": "Not Ready",
"ready": "Ready"
}
},
"csi": {
@@ -1001,13 +991,10 @@
},
"labels": {
"actions": "Actions",
"active": "Active",
"endmustbeafterstart": "End date must be after start date.",
"flat_rate": "Flat Rate",
"inactive": "Inactive",
"name": "Name",
"rate_type": "Rate Type",
"status": "Status",
"straight_time": "Straight Time"
},
"successes": {
@@ -1707,7 +1694,6 @@
"estimator": "Estimator",
"filehandler": "File Handler",
"insurance": "Insurance Details",
"more": "More",
"notes": "Notes",
"parts": "Parts",
"totals": "Totals",
@@ -2028,7 +2014,7 @@
"joblookup": "Job Lookup",
"login": "Login",
"logout": "Logout",
"productionboard": "Production Visual",
"productionboard": "Production Board - Visual",
"productionlist": "Production List",
"shiftclockin": "Shift Clock"
}
@@ -2058,9 +2044,6 @@
"sentby": "Sent by {{by}} at {{time}}",
"typeamessage": "Send a message...",
"unarchive": "Unarchive"
},
"render": {
"conversation_list": "Conversation List"
}
},
"notes": {
@@ -2625,7 +2608,6 @@
"open_orders": "Open Orders by Date",
"open_orders_csr": "Open Orders by CSR",
"open_orders_estimator": "Open Orders by Estimator",
"open_orders_excel": "Open Orders - Excel",
"open_orders_ins_co": "Open Orders by Insurance Company",
"open_orders_referral": "Open Orders by Referral Source",
"open_orders_specific_csr": "Open Orders filtered by CSR",
@@ -2713,7 +2695,6 @@
"efficiencyoverperiod": "Efficiency over Selected Dates",
"entries": "Scoreboard Entries",
"jobs": "Jobs",
"jobscompletednotinvoiced": "Completed Not Invoiced",
"lastmonth": "Last Month",
"lastweek": "Last Week",
"monthlytarget": "Monthly",
@@ -2728,7 +2709,6 @@
"timetickets": "Time Tickets",
"timeticketsemployee": "Time Tickets by Employee",
"todateactual": "Actual (MTD)",
"totalhrs": "Total Hours",
"totaloverperiod": "Total over Selected Dates",
"weeklyactual": "Actual (W)",
"weeklytarget": "Weekly",
@@ -2906,7 +2886,7 @@
"parts-queue": "Parts Queue | $t(titles.app)",
"payments-all": "Payments | $t(titles.app)",
"phonebook": "Phonebook | $t(titles.app)",
"productionboard": "Production Board - Visual | $t(titles.app)",
"productionboard": "Production - Board",
"productionlist": "Production Board - List | $t(titles.app)",
"profile": "My Profile | $t(titles.app)",
"readyjobs": "Ready Jobs | $t(titles.app)",
@@ -2918,10 +2898,6 @@
"shop-csi": "CSI Responses | $t(titles.app)",
"shop-templates": "Shop Templates | $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)",
"timetickets": "Time Tickets | $t(titles.app)",
"ttapprovals": "",

View File

@@ -103,7 +103,6 @@
"admin_jobmarkforreexport": "",
"admin_jobuninvoice": "",
"admin_jobunvoid": "",
"alerttoggle": "",
"appointmentcancel": "",
"appointmentinsert": "",
"billposted": "",
@@ -112,7 +111,6 @@
"jobassignmentchange": "",
"jobassignmentremoved": "",
"jobchecklist": "",
"jobinvoiced": "",
"jobconverted": "",
"jobfieldchanged": "",
"jobimported": "",
@@ -205,7 +203,6 @@
"entered_total": "",
"enteringcreditmemo": "",
"federal_tax": "",
"federal_tax_exempt": "",
"generatepartslabel": "",
"iouexists": "",
"local_tax": "",
@@ -214,7 +211,6 @@
"new": "",
"noneselected": "",
"onlycmforinvoiced": "",
"printlabels": "",
"retailtotal": "",
"savewithdiscrepancy": "",
"state_tax": "",
@@ -451,7 +447,6 @@
"config": "",
"dashboard": "",
"rbac": "",
"reportcenter": "",
"templates": "",
"vendors": ""
},
@@ -786,7 +781,6 @@
"notes": "",
"plate": "",
"purchasedate": "",
"readiness": "",
"registrationexpires": "",
"serviceenddate": "",
"servicestartdate": "",
@@ -823,10 +817,6 @@
},
"successes": {
"saved": ""
},
"readiness": {
"notready": "",
"ready": ""
}
},
"csi": {
@@ -1001,13 +991,10 @@
},
"labels": {
"actions": "",
"active": "",
"endmustbeafterstart": "",
"flat_rate": "",
"inactive": "",
"name": "",
"rate_type": "",
"status": "",
"straight_time": ""
},
"successes": {
@@ -1707,7 +1694,6 @@
"estimator": "Estimador",
"filehandler": "File Handler",
"insurance": "detalles del seguro",
"more": "Más",
"notes": "Notas",
"parts": "Partes",
"totals": "Totales",
@@ -2058,9 +2044,6 @@
"sentby": "",
"typeamessage": "Enviar un mensaje...",
"unarchive": ""
},
"render": {
"conversation_list": ""
}
},
"notes": {
@@ -2625,7 +2608,6 @@
"open_orders": "",
"open_orders_csr": "",
"open_orders_estimator": "",
"open_orders_excel": "",
"open_orders_ins_co": "",
"open_orders_referral": "",
"open_orders_specific_csr": "",
@@ -2713,7 +2695,6 @@
"efficiencyoverperiod": "",
"entries": "",
"jobs": "",
"jobscompletednotinvoiced": "",
"lastmonth": "",
"lastweek": "",
"monthlytarget": "",
@@ -2728,7 +2709,6 @@
"timetickets": "",
"timeticketsemployee": "",
"todateactual": "",
"totalhrs": "",
"totaloverperiod": "",
"weeklyactual": "",
"weeklytarget": "",
@@ -2899,7 +2879,7 @@
"jobs-intake": "",
"jobsavailable": "Empleos disponibles | $t(titles.app)",
"jobsdetail": "Trabajo {{ro_number}} | $t(titles.app)",
"jobsdocuments": "Documentos de trabajo {{ro_number}} | $t(titles.app)",
"jobsdocuments": "Documentos de trabajo {{ro_number}} | $ t (títulos.app)",
"manageroot": "Casa | $t(titles.app)",
"owners": "Todos los propietarios | $t(titles.app)",
"owners-detail": "",
@@ -2918,10 +2898,6 @@
"shop-csi": "",
"shop-templates": "",
"shop_vendors": "Vendedores | $t(titles.app)",
"techconsole": "$t(titles.app)",
"techjoblookup": "$t(titles.app)",
"techjobclock": "$t(titles.app)",
"techshiftclock": "$t(titles.app)",
"temporarydocs": "",
"timetickets": "",
"ttapprovals": "",

View File

@@ -103,7 +103,6 @@
"admin_jobmarkforreexport": "",
"admin_jobuninvoice": "",
"admin_jobunvoid": "",
"alerttoggle": "",
"appointmentcancel": "",
"appointmentinsert": "",
"billposted": "",
@@ -112,7 +111,6 @@
"jobassignmentchange": "",
"jobassignmentremoved": "",
"jobchecklist": "",
"jobinvoiced": "",
"jobconverted": "",
"jobfieldchanged": "",
"jobimported": "",
@@ -205,7 +203,6 @@
"entered_total": "",
"enteringcreditmemo": "",
"federal_tax": "",
"federal_tax_exempt": "",
"generatepartslabel": "",
"iouexists": "",
"local_tax": "",
@@ -214,7 +211,6 @@
"new": "",
"noneselected": "",
"onlycmforinvoiced": "",
"printlabels": "",
"retailtotal": "",
"savewithdiscrepancy": "",
"state_tax": "",
@@ -451,7 +447,6 @@
"config": "",
"dashboard": "",
"rbac": "",
"reportcenter": "",
"templates": "",
"vendors": ""
},
@@ -786,7 +781,6 @@
"notes": "",
"plate": "",
"purchasedate": "",
"readiness": "",
"registrationexpires": "",
"serviceenddate": "",
"servicestartdate": "",
@@ -823,10 +817,6 @@
},
"successes": {
"saved": ""
},
"readiness": {
"notready": "",
"ready": ""
}
},
"csi": {
@@ -1001,13 +991,10 @@
},
"labels": {
"actions": "",
"active": "",
"endmustbeafterstart": "",
"flat_rate": "",
"inactive": "",
"name": "",
"rate_type": "",
"status": "",
"straight_time": ""
},
"successes": {
@@ -1707,7 +1694,6 @@
"estimator": "Estimateur",
"filehandler": "Gestionnaire de fichiers",
"insurance": "Détails de l'assurance",
"more": "Plus",
"notes": "Remarques",
"parts": "les pièces",
"totals": "Totaux",
@@ -2058,9 +2044,6 @@
"sentby": "",
"typeamessage": "Envoyer un message...",
"unarchive": ""
},
"render": {
"conversation_list": ""
}
},
"notes": {
@@ -2625,7 +2608,6 @@
"open_orders": "",
"open_orders_csr": "",
"open_orders_estimator": "",
"open_orders_excel": "",
"open_orders_ins_co": "",
"open_orders_referral": "",
"open_orders_specific_csr": "",
@@ -2713,7 +2695,6 @@
"efficiencyoverperiod": "",
"entries": "",
"jobs": "",
"jobscompletednotinvoiced": "",
"lastmonth": "",
"lastweek": "",
"monthlytarget": "",
@@ -2728,7 +2709,6 @@
"timetickets": "",
"timeticketsemployee": "",
"todateactual": "",
"totalhrs": "",
"totaloverperiod": "",
"weeklyactual": "",
"weeklytarget": "",
@@ -2899,7 +2879,7 @@
"jobs-intake": "",
"jobsavailable": "Emplois disponibles | $t(titles.app)",
"jobsdetail": "Travail {{ro_number}} | $t(titles.app)",
"jobsdocuments": "Documents de travail {{ro_number}} | $t(titles.app)",
"jobsdocuments": "Documents de travail {{ro_number}} | $ t (titres.app)",
"manageroot": "Accueil | $t(titles.app)",
"owners": "Tous les propriétaires | $t(titles.app)",
"owners-detail": "",
@@ -2918,10 +2898,6 @@
"shop-csi": "",
"shop-templates": "",
"shop_vendors": "Vendeurs | $t(titles.app)",
"techconsole": "$t(titles.app)",
"techjoblookup": "$t(titles.app)",
"techjobclock": "$t(titles.app)",
"techshiftclock": "$t(titles.app)",
"temporarydocs": "",
"timetickets": "",
"ttapprovals": "",

View File

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

View File

@@ -2026,19 +2026,6 @@ export const TemplateList = (type, context) => {
},
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"
@@ -2115,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"
? {
purchases_by_vendor_detailed: {

View File

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

View File

@@ -1,37 +0,0 @@
export const BETA_KEY = 'betaSwitchImex';
export const checkBeta = () => {
const cookie = document.cookie.split('; ').find(row => row.startsWith(BETA_KEY));
return cookie ? cookie.split('=')[1] === 'true' : false;
}
export const setBeta = (value) => {
const domain = window.location.hostname.split('.').slice(-2).join('.');
document.cookie = `${BETA_KEY}=${value}; path=/; domain=.${domain}`;
}
export const handleBeta = () => {
// If the current host name does not start with beta or test, then we don't need to do anything.
if (window.location.hostname.startsWith('localhost')) {
console.log('Not on beta or test, so no need to handle beta.');
return;
}
const isBeta = checkBeta();
const currentHostName = window.location.hostname;
// Beta is enabled, but the current host name does start with beta.
if (isBeta && !currentHostName.startsWith('beta')) {
const href= `${window.location.protocol}//beta.${currentHostName}${window.location.pathname}${window.location.search}${window.location.hash}`;
window.location.replace(href);
}
// Beta is not enabled, but the current host name does start with beta.
else if (!isBeta && currentHostName.startsWith('beta')) {
const href = `${window.location.protocol}//${currentHostName.replace('beta.', '')}${window.location.pathname}${window.location.search}${window.location.hash}`;
window.location.replace(href);
}
}
export default handleBeta;

View File

@@ -53,7 +53,7 @@
jsonpointer "^5.0.0"
leven "^3.1.0"
"@apollo/client@^3.7.9":
"@apollo/client@^3.0.0", "@apollo/client@^3.7.9":
version "3.7.9"
resolved "https://registry.npmjs.org/@apollo/client/-/client-3.7.9.tgz"
integrity sha512-YnJvrJOVWrp4y/zdNvUaM8q4GuSHCEIecsRDTJhK/veT33P/B7lfqGJ24NeLdKMj8tDEuXYF7V0t+th4+rgC+Q==
@@ -96,7 +96,7 @@
resolved "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.21.0.tgz"
integrity sha512-gMuZsmsgxk/ENC3O/fRw5QY8A9/uxQbbCEypnLIiYYc/qVJtEV7ouxC3EllIIwNzMqAQee5tanFabWsUOutS7g==
"@babel/core@^7.1.0", "@babel/core@^7.12.3", "@babel/core@^7.16.0":
"@babel/core@^7.0.0", "@babel/core@^7.0.0-0", "@babel/core@^7.1.0", "@babel/core@^7.12.0", "@babel/core@^7.12.3", "@babel/core@^7.13.0", "@babel/core@^7.16.0", "@babel/core@^7.4.0-0", "@babel/core@^7.8.0", "@babel/core@>=7.11.0":
version "7.18.9"
resolved "https://registry.npmjs.org/@babel/core/-/core-7.18.9.tgz"
integrity sha512-1LIb1eL8APMy91/IMW+31ckrfBM4yCoLaVzoDhZUKSM4cu1L1nIidyxkCgzPAgrC5WEz36IPEr/eSeSF9pIn+g==
@@ -159,27 +159,6 @@
json5 "^2.2.2"
semver "^6.3.0"
"@babel/core@^7.8.0":
version "7.21.0"
resolved "https://registry.npmjs.org/@babel/core/-/core-7.21.0.tgz"
integrity sha512-PuxUbxcW6ZYe656yL3EAhpy7qXKq0DmYsrJLpbB8XrsCP9Nm+XCg9XFMb5vIDliPD7+U/+M+QJlH17XOcB7eXA==
dependencies:
"@ampproject/remapping" "^2.2.0"
"@babel/code-frame" "^7.18.6"
"@babel/generator" "^7.21.0"
"@babel/helper-compilation-targets" "^7.20.7"
"@babel/helper-module-transforms" "^7.21.0"
"@babel/helpers" "^7.21.0"
"@babel/parser" "^7.21.0"
"@babel/template" "^7.20.7"
"@babel/traverse" "^7.21.0"
"@babel/types" "^7.21.0"
convert-source-map "^1.7.0"
debug "^4.1.0"
gensync "^1.0.0-beta.2"
json5 "^2.2.2"
semver "^6.3.0"
"@babel/eslint-parser@^7.16.3":
version "7.19.1"
resolved "https://registry.npmjs.org/@babel/eslint-parser/-/eslint-parser-7.19.1.tgz"
@@ -750,7 +729,7 @@
dependencies:
"@babel/helper-plugin-utils" "^7.8.3"
"@babel/plugin-syntax-flow@^7.18.6":
"@babel/plugin-syntax-flow@^7.14.5", "@babel/plugin-syntax-flow@^7.18.6":
version "7.18.6"
resolved "https://registry.npmjs.org/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.18.6.tgz"
integrity sha512-LUbR+KNTBWCUAqRG9ex5Gnzu2IOkt8jRJbHHXFT9q+L9zm7M/QQbEqXyw1n1pohYvOyWC8CjeyjrSaIwiYjK7A==
@@ -1148,7 +1127,7 @@
dependencies:
"@babel/plugin-transform-react-jsx" "^7.18.6"
"@babel/plugin-transform-react-jsx@^7.18.6":
"@babel/plugin-transform-react-jsx@^7.14.9", "@babel/plugin-transform-react-jsx@^7.18.6":
version "7.18.6"
resolved "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.18.6.tgz"
integrity sha512-Mz7xMPxoy9kPS/JScj6fJs03TZ/fZ1dJPlMjRAgTaxaS0fUBk8FV/A2rRgfPsVCZqALNwMexD+0Uaf5zlcKPpw==
@@ -1589,7 +1568,7 @@
resolved "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz"
integrity sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==
"@craco/craco@^7.0.0":
"@craco/craco@^6.0.0", "@craco/craco@^7.0.0":
version "7.0.0"
resolved "https://registry.npmjs.org/@craco/craco/-/craco-7.0.0.tgz"
integrity sha512-OyjL9zpURB6Ha1HO62Hlt27Xd7UYJ8DRiBNuE4DBB8Ue0iQ9q/xsv3ze7ROm6gCZqV6I2Gxjnq0EHCCye+4xDQ==
@@ -1865,7 +1844,7 @@
"@firebase/util" "1.9.2"
tslib "^2.1.0"
"@firebase/app-compat@0.2.3":
"@firebase/app-compat@0.2.3", "@firebase/app-compat@0.x":
version "0.2.3"
resolved "https://registry.npmjs.org/@firebase/app-compat/-/app-compat-0.2.3.tgz"
integrity sha512-sX6rD1KFX6K2CuCnQvc9jZLOgAFZ+sv2jKKahIl4SbTM561D682B8n4Jtx/SgDrvcTVTdb05g4NhZOws9hxYxA==
@@ -1876,12 +1855,12 @@
"@firebase/util" "1.9.2"
tslib "^2.1.0"
"@firebase/app-types@0.9.0":
"@firebase/app-types@0.9.0", "@firebase/app-types@0.x":
version "0.9.0"
resolved "https://registry.npmjs.org/@firebase/app-types/-/app-types-0.9.0.tgz"
integrity sha512-AeweANOIo0Mb8GiYm3xhTEBVCmPwTYAu9Hcd2qSkLuga/6+j9b1Jskl5bpiSQWy9eJ/j5pavxj6eYogmnuzm+Q==
"@firebase/app@0.9.3":
"@firebase/app@0.9.3", "@firebase/app@0.x":
version "0.9.3"
resolved "https://registry.npmjs.org/@firebase/app/-/app-0.9.3.tgz"
integrity sha512-G79JUceVDaHRZ4WkA11GyVldVXhdyRJRwWVQFFvAAVfQJLvy2TA6lQjeUn28F6FmeUWxDGwPC30bxCRWq7Op8Q==
@@ -2166,7 +2145,7 @@
node-fetch "2.6.7"
tslib "^2.1.0"
"@firebase/util@1.9.2":
"@firebase/util@1.9.2", "@firebase/util@1.x":
version "1.9.2"
resolved "https://registry.npmjs.org/@firebase/util/-/util-1.9.2.tgz"
integrity sha512-9l0uMGPGw3GsoD5khjMmYCCcMq/OR/OOSViiWMN+s2Q0pxM+fYzrii1H+r8qC/uoMjSVXomjLZt0vZIyryCqtQ==
@@ -3071,7 +3050,7 @@
resolved "https://registry.npmjs.org/@types/aria-query/-/aria-query-4.2.2.tgz"
integrity sha512-HnYpAE1Y6kRyKM/XkEuiRQhTHvkzMBurTHnpFLYLBGPIylZNPs9jJcuOOYWxPLJCSEtmZT0Y8rHDokKN7rRTig==
"@types/babel__core@^7.0.0", "@types/babel__core@^7.1.12":
"@types/babel__core@^7.0.0", "@types/babel__core@^7.1.12", "@types/babel__core@^7.1.9":
version "7.1.19"
resolved "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.19.tgz"
integrity sha512-WEOTgRsbYkvA/KCsDwVEGkd7WAr1e3g31VHQ8zy5gul/V1qKullU/BU5I68X5v7V3GnB9eotmom4v5a5gjxorw==
@@ -3379,7 +3358,7 @@
hoist-non-react-statics "^3.3.0"
redux "^4.0.0"
"@types/react@*", "@types/react@>=16.9.11":
"@types/react@*", "@types/react@^16.8 || ^17.0 || ^18.0", "@types/react@>=16.9.11":
version "18.0.15"
resolved "https://registry.npmjs.org/@types/react/-/react-18.0.15.tgz"
integrity sha512-iz3BtLuIYH1uWdsv6wXYdhozhqj20oD4/Hk2DNXIn1kFsmp9x8d9QB6FnPhfkbhd2PgEONt9Q1x/ebkwjfFLow==
@@ -3495,7 +3474,7 @@
dependencies:
"@types/node" "*"
"@typescript-eslint/eslint-plugin@^5.5.0":
"@typescript-eslint/eslint-plugin@^4.0.0 || ^5.0.0", "@typescript-eslint/eslint-plugin@^5.5.0":
version "5.54.0"
resolved "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.54.0.tgz"
integrity sha512-+hSN9BdSr629RF02d7mMtXhAJvDTyCbprNYJKrXETlul/Aml6YZwd90XioVbjejQeHbb3R8Dg0CkRgoJDxo8aw==
@@ -3518,7 +3497,7 @@
dependencies:
"@typescript-eslint/utils" "5.54.0"
"@typescript-eslint/parser@^5.5.0":
"@typescript-eslint/parser@^5.0.0", "@typescript-eslint/parser@^5.5.0":
version "5.54.0"
resolved "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.54.0.tgz"
integrity sha512-aAVL3Mu2qTi+h/r04WI/5PfNWvO6pdhpeMRWk9R7rEV4mwJNzoWf5CCU5vDKBsPIFQFjEq1xg7XBI2rjiMXQbQ==
@@ -3800,6 +3779,11 @@ acorn-walk@^8.1.1:
resolved "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz"
integrity sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==
"acorn@^6.0.0 || ^7.0.0 || ^8.0.0", acorn@^8, acorn@^8.2.4, acorn@^8.4.1, acorn@^8.5.0, acorn@^8.7.1, acorn@^8.8.0:
version "8.8.2"
resolved "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz"
integrity sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==
acorn@^7.0.0:
version "7.4.1"
resolved "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz"
@@ -3810,11 +3794,6 @@ acorn@^7.1.1:
resolved "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz"
integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==
acorn@^8.2.4, acorn@^8.4.1, acorn@^8.5.0, acorn@^8.7.1, acorn@^8.8.0:
version "8.8.2"
resolved "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz"
integrity sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==
address@^1.0.1, address@^1.1.2:
version "1.2.2"
resolved "https://registry.npmjs.org/address/-/address-1.2.2.tgz"
@@ -3862,7 +3841,7 @@ ajv-keywords@^5.0.0:
dependencies:
fast-deep-equal "^3.1.3"
ajv@^6.10.0, ajv@^6.12.2, ajv@^6.12.4, ajv@^6.12.5, ajv@^6.12.6:
ajv@^6.10.0, ajv@^6.12.2, ajv@^6.12.4, ajv@^6.12.5, ajv@^6.12.6, ajv@^6.9.1:
version "6.12.6"
resolved "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz"
integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==
@@ -3872,7 +3851,7 @@ ajv@^6.10.0, ajv@^6.12.2, ajv@^6.12.4, ajv@^6.12.5, ajv@^6.12.6:
json-schema-traverse "^0.4.1"
uri-js "^4.2.2"
ajv@^8.0.0, ajv@^8.8.0:
ajv@^8.0.0, ajv@^8.8.0, ajv@^8.8.2:
version "8.12.0"
resolved "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz"
integrity sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==
@@ -3882,7 +3861,7 @@ ajv@^8.0.0, ajv@^8.8.0:
require-from-string "^2.0.2"
uri-js "^4.2.2"
ajv@^8.6.0:
ajv@^8.6.0, ajv@>=8:
version "8.12.0"
resolved "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz"
integrity sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==
@@ -4582,7 +4561,7 @@ browser-process-hrtime@^1.0.0:
resolved "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz"
integrity sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==
browserslist@^4.0.0, browserslist@^4.14.5, browserslist@^4.18.1, browserslist@^4.20.2, browserslist@^4.21.2, browserslist@^4.21.3, browserslist@^4.21.4, browserslist@^4.21.5:
browserslist@^4.0.0, browserslist@^4.14.5, browserslist@^4.18.1, browserslist@^4.20.2, browserslist@^4.21.2, browserslist@^4.21.3, browserslist@^4.21.4, browserslist@^4.21.5, "browserslist@>= 4", "browserslist@>= 4.21.0", browserslist@>=4:
version "4.21.5"
resolved "https://registry.npmjs.org/browserslist/-/browserslist-4.21.5.tgz"
integrity sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w==
@@ -5384,7 +5363,7 @@ cuint@^0.2.2:
resolved "https://registry.npmjs.org/cuint/-/cuint-0.2.2.tgz"
integrity sha512-d4ZVpCW31eWwCMe1YT3ur7mUDnTXbgwyzaL320DrcRT45rfjYxkt5QWLrmOJ+/UEAI2+fQgKe/fCjR8l4TpRgw==
cypress@^10.3.1:
cypress@^10.3.1, "cypress@^2.1.0 || ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0":
version "10.11.0"
resolved "https://registry.npmjs.org/cypress/-/cypress-10.11.0.tgz"
integrity sha512-lsaE7dprw5DoXM00skni6W5ElVVLGAdRUUdZjX2dYsGjbY/QnpzWZ95Zom1mkGg0hAaO/QVTZoFVS7Jgr/GUPA==
@@ -6041,7 +6020,7 @@ enquire.js@^2.1.6:
resolved "https://registry.npmjs.org/enquire.js/-/enquire.js-2.1.6.tgz"
integrity sha512-/KujNpO+PT63F7Hlpu4h3pE3TokKRHN26JYmQpPyjkRD/N57R7bPDNojMXdi7uveAKjYB7yQnartCxZnFWr0Xw==
enquirer@^2.3.6:
enquirer@^2.3.6, "enquirer@>= 2.3.0 < 3":
version "2.3.6"
resolved "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz"
integrity sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==
@@ -6391,7 +6370,7 @@ eslint-webpack-plugin@^3.1.1:
normalize-path "^3.0.0"
schema-utils "^4.0.0"
eslint@^8.3.0:
eslint@*, "eslint@^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8", "eslint@^3 || ^4 || ^5 || ^6 || ^7 || ^8", "eslint@^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0", "eslint@^6.0.0 || ^7.0.0 || ^8.0.0", "eslint@^7.0.0 || ^8.0.0", "eslint@^7.5.0 || ^8.0.0", eslint@^8.0.0, eslint@^8.1.0, eslint@^8.3.0, "eslint@>= 3.2.1", "eslint@>= 6", eslint@>=5:
version "8.35.0"
resolved "https://registry.npmjs.org/eslint/-/eslint-8.35.0.tgz"
integrity sha512-BxAf1fVL7w+JLRQhWl2pzGeSiGqbWumV4WNvc9Rhp6tiCtm4oHnyPBSEtMGZwrQgudFQ+otqzWoPB7x+hxoWsw==
@@ -7171,7 +7150,7 @@ graphql-tag@^2.12.6:
dependencies:
tslib "^2.1.0"
graphql@^16.6.0:
"graphql@^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0", "graphql@^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0", "graphql@^14.0.0 || ^15.0.0 || ^16.0.0", "graphql@^15.7.2 || ^16.0.0", graphql@^16.6.0:
version "16.6.0"
resolved "https://registry.npmjs.org/graphql/-/graphql-16.6.0.tgz"
integrity sha512-KPIBPDlW7NxrbT/eh4qPXz5FiFdL5UbaA0XUNz2Rp3Z3hqBSkbj0GVjwFDztsWVauZUWsbKHgMg++sk8UX0bkw==
@@ -7435,7 +7414,7 @@ i18next-browser-languagedetector@^7.0.1:
dependencies:
"@babel/runtime" "^7.19.4"
i18next@^22.4.10:
i18next@^22.4.10, "i18next@>= 19.0.0":
version "22.4.10"
resolved "https://registry.npmjs.org/i18next/-/i18next-22.4.10.tgz"
integrity sha512-3EqgGK6fAJRjnGgfkNSStl4mYLCjUoJID338yVyLMj5APT67HUtWoqSayZewiiC5elzMUB1VEUwcmSCoeQcNEA==
@@ -8268,7 +8247,7 @@ jest-resolve-dependencies@^27.5.1:
jest-regex-util "^27.5.1"
jest-snapshot "^27.5.1"
jest-resolve@^27.4.2, jest-resolve@^27.5.1:
jest-resolve@*, jest-resolve@^27.4.2, jest-resolve@^27.5.1:
version "27.5.1"
resolved "https://registry.npmjs.org/jest-resolve/-/jest-resolve-27.5.1.tgz"
integrity sha512-FFDy8/9E6CV83IMbDpcjOhumAQPDyETnU2KZ1O98DwTnz8AOBsW/Xv3GySr1mOZdItLR+zDZ7I/UdTFbgSOVCw==
@@ -8478,7 +8457,7 @@ jest-worker@^28.0.2:
merge-stream "^2.0.0"
supports-color "^8.0.0"
jest@^27.4.3:
"jest@^27.0.0 || ^28.0.0", jest@^27.4.3:
version "27.5.1"
resolved "https://registry.npmjs.org/jest/-/jest-27.5.1.tgz"
integrity sha512-Yn0mADZB89zTtjkPJEXwrac3LHudkQMR+Paqa8uxJHCBr9agxztUifWCyiYrjhMPBoUVBjyny0I7XH6ozDr7QQ==
@@ -8745,7 +8724,7 @@ less-loader@^7.3.0:
loader-utils "^2.0.0"
schema-utils "^3.0.0"
less@^4.1.1:
"less@^3.5.0 || ^4.0.0", less@^4.1.1:
version "4.1.3"
resolved "https://registry.npmjs.org/less/-/less-4.1.3.tgz"
integrity sha512-w16Xk/Ta9Hhyei0Gpz9m7VS8F28nieJaL/VyShID7cYvP6IL5oHeL6p4TXSDJqZE/lNv0oJ2pGVjJsRkfwm5FA==
@@ -9207,7 +9186,7 @@ moment-timezone@^0.5.40, moment-timezone@^0.5.41:
dependencies:
moment "^2.29.4"
moment@^2.24.0, moment@^2.29.2, moment@^2.29.4:
moment@^2.24.0, moment@^2.29.2, moment@^2.29.4, moment@2.x.x:
version "2.29.4"
resolved "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz"
integrity sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==
@@ -10319,15 +10298,7 @@ postcss-value-parser@^4.0.0, postcss-value-parser@^4.0.2, postcss-value-parser@^
resolved "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz"
integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==
postcss@^7.0.35:
version "7.0.39"
resolved "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz"
integrity sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==
dependencies:
picocolors "^0.2.1"
source-map "^0.6.1"
postcss@^8.0.9, postcss@^8.3.5, postcss@^8.4.19, postcss@^8.4.4:
"postcss@^7.0.0 || ^8.0.1", postcss@^8, postcss@^8.0.0, postcss@^8.0.3, postcss@^8.0.9, postcss@^8.1.0, postcss@^8.1.4, postcss@^8.2, postcss@^8.2.14, postcss@^8.2.15, postcss@^8.2.2, postcss@^8.3, postcss@^8.3.5, postcss@^8.4, postcss@^8.4.19, postcss@^8.4.21, postcss@^8.4.4, postcss@^8.4.6, "postcss@>= 8", postcss@>=8, postcss@>=8.0.9:
version "8.4.21"
resolved "https://registry.npmjs.org/postcss/-/postcss-8.4.21.tgz"
integrity sha512-tP7u/Sn/dVxK2NnruI4H9BG+x+Wxz6oeZ1cJ8P6G/PZY0IKk4k/63TDsQf2kQq3+qoJeLm2kIBUNlZe3zgb4Zg==
@@ -10336,6 +10307,14 @@ postcss@^8.0.9, postcss@^8.3.5, postcss@^8.4.19, postcss@^8.4.4:
picocolors "^1.0.0"
source-map-js "^1.0.2"
postcss@^7.0.35:
version "7.0.39"
resolved "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz"
integrity sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==
dependencies:
picocolors "^0.2.1"
source-map "^0.6.1"
prelude-ls@^1.2.1:
version "1.2.1"
resolved "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz"
@@ -10410,7 +10389,7 @@ prompts@^2.0.1, prompts@^2.4.2:
kleur "^3.0.3"
sisteransi "^1.0.5"
prop-types@^15.5.10, prop-types@^15.5.8, prop-types@^15.6.0, prop-types@^15.6.1, prop-types@^15.6.2, prop-types@^15.7.2, prop-types@^15.8.1, prop-types@15.x:
prop-types@^15.0.0, prop-types@^15.5.10, prop-types@^15.5.8, prop-types@^15.6.0, prop-types@^15.6.1, prop-types@^15.6.2, prop-types@^15.7.2, prop-types@^15.8.1, prop-types@15.x:
version "15.8.1"
resolved "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz"
integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==
@@ -11140,7 +11119,7 @@ react-dev-utils@^12.0.1:
strip-ansi "^6.0.1"
text-table "^0.2.0"
react-dom@^17.0.2:
react-dom@*, "react-dom@^0.14 || ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^18.0.0", "react-dom@^0.14.0 || ^15.0.0 || ^16 || ^17 || ^18", "react-dom@^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0", "react-dom@^15.3.0 || ^16.0.0-alpha", "react-dom@^16.0.0 || ^17.0.0 || ^18.0.0", "react-dom@^16.14.0 || ^17 || ^18", "react-dom@^16.8 || ^17.0 || ^18.0", "react-dom@^16.8.0 || ^17.0.0", "react-dom@^16.8.0 || ^17.0.0 || ^18.0.0", "react-dom@^16.8.5 || ^17.0.0", react-dom@^17.0.2, "react-dom@>= 16.3.0", "react-dom@>= 16.8.0", react-dom@>=15, react-dom@>=15.0.0, react-dom@>=16.0.0, react-dom@>=16.11.0, react-dom@>=16.3.0, react-dom@>=16.9.0, "react-dom@16.x || 17.x":
version "17.0.2"
resolved "https://registry.npmjs.org/react-dom/-/react-dom-17.0.2.tgz"
integrity sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA==
@@ -11212,7 +11191,7 @@ react-intersection-observer@^9.4.3:
resolved "https://registry.npmjs.org/react-intersection-observer/-/react-intersection-observer-9.4.3.tgz"
integrity sha512-WNRqMQvKpupr6MzecAQI0Pj0+JQong307knLP4g/nBex7kYfIaZsPpXaIhKHR+oV8z+goUbH9e10j6lGRnTzlQ==
react-is@^16.10.2, react-is@^16.12.0, react-is@^16.13.1, react-is@^16.6.0, react-is@^16.7.0:
react-is@^16.10.2, react-is@^16.12.0, react-is@^16.13.1, react-is@^16.6.0, react-is@^16.7.0, "react-is@>= 16.8.0":
version "16.13.1"
resolved "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz"
integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
@@ -11292,7 +11271,7 @@ react-redux@^8.0.5:
react-is "^18.0.0"
use-sync-external-store "^1.0.0"
react-refresh@^0.11.0:
react-refresh@^0.11.0, "react-refresh@>=0.10.0 <1.0.0":
version "0.11.0"
resolved "https://registry.npmjs.org/react-refresh/-/react-refresh-0.11.0.tgz"
integrity sha512-F27qZr8uUqwhWZboondsPx8tnC3Ct3SxZA3V5WyEvujRyyNv0VYPhoBg1gZ8/MV5tubQp76Trw8lTv9hzRBa+A==
@@ -11341,7 +11320,7 @@ react-router@5.3.3:
tiny-invariant "^1.0.2"
tiny-warning "^1.0.0"
react-scripts@^5.0.1:
react-scripts@^5.0.0, react-scripts@^5.0.1:
version "5.0.1"
resolved "https://registry.npmjs.org/react-scripts/-/react-scripts-5.0.1.tgz"
integrity sha512-8VAmEm/ZAwQzJ+GOMLbBsTdDKOpuZh7RPs0UymvBR2vRk4iZWCskjbFnxqjrzoIvlNNRZ3QJFx6/qDSi6zSnaQ==
@@ -11442,7 +11421,7 @@ react-virtualized@^9.22.3:
prop-types "^15.7.2"
react-lifecycles-compat "^3.0.4"
react@^17.0.2:
react@*, "react@^0.14 || ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^18.0.0", "react@^0.14.0 || ^15.0.0 || ^16 || ^17 || ^18", "react@^0.14.0 || ^15.0.0 || ^16.0.0 || ^17.0.0", "react@^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0", "react@^15.3.0 || ^16.0.0-alpha", "react@^16.0.0 || ^17.0.0 || ^18.0.0", "react@^16.14.0 || ^17 || ^18", "react@^16.8 || ^17.0 || ^18.0", "react@^16.8.0 || ^17.0.0", "react@^16.8.0 || ^17.0.0 || ^18.0.0", "react@^16.8.3 || ^17 || ^18", "react@^16.8.5 || ^17.0.0", react@^17.0.2, "react@>= 16", "react@>= 16.3", "react@>= 16.3.0", "react@>= 16.8.0", react@>=15, react@>=15.0.0, react@>=16.0.0, react@>=16.11.0, react@>=16.14.0, react@>=16.3.0, react@>=16.8.0, react@>=16.9.0, "react@15.x || 16.x || 17.x || 18.x", "react@16.x || 17.x", react@17.0.2:
version "17.0.2"
resolved "https://registry.npmjs.org/react/-/react-17.0.2.tgz"
integrity sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA==
@@ -11573,7 +11552,7 @@ redux-state-sync@^3.1.4:
dependencies:
broadcast-channel "^3.1.0"
redux@^4.0.0, redux@^4.0.4, redux@^4.2.1:
redux@^4, redux@^4.0.0, redux@^4.0.4, redux@^4.2.1, redux@>4.0.0:
version "4.2.1"
resolved "https://registry.npmjs.org/redux/-/redux-4.2.1.tgz"
integrity sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==
@@ -11839,7 +11818,7 @@ rollup-plugin-terser@^7.0.0:
serialize-javascript "^4.0.0"
terser "^5.0.0"
rollup@^2.43.1:
"rollup@^1.20.0 || ^2.0.0", rollup@^1.20.0||^2.0.0, rollup@^2.0.0, rollup@^2.43.1:
version "2.79.1"
resolved "https://registry.npmjs.org/rollup/-/rollup-2.79.1.tgz"
integrity sha512-uKxbd0IhMZOhjAiD5oAFp7BqvkA4Dv47qpOCtaNvng4HBwdbWtdOh8f5nZNuk2rp51PMGk3bzfWu5oayNEuYnw==
@@ -11907,7 +11886,7 @@ sass-loader@^12.3.0:
klona "^2.0.4"
neo-async "^2.6.2"
sass@^1.58.3:
sass@^1.3.0, sass@^1.58.3:
version "1.58.3"
resolved "https://registry.npmjs.org/sass/-/sass-1.58.3.tgz"
integrity sha512-Q7RaEtYf6BflYrQ+buPudKR26/lH+10EmO9bBqbmPh/KeLqv8bjpTNqxe71ocONqXq+jYiCbpPUmQMS+JJPk4A==
@@ -12538,7 +12517,7 @@ style-utils@~0.2.0:
resolved "https://registry.npmjs.org/style-utils/-/style-utils-0.2.1.tgz"
integrity sha512-eKRIfWnUSdBqe2ko+qisUwBSlfWpHru89geRqzmScpDhkPW1ksmE04d//nDcXeF+TVK5cnBG90mMmHgxyxXleQ==
styled-components@^5.3.6:
styled-components@^5.3.6, "styled-components@>= 2":
version "5.3.6"
resolved "https://registry.npmjs.org/styled-components/-/styled-components-5.3.6.tgz"
integrity sha512-hGTZquGAaTqhGWldX7hhfzjnIYBZ0IXQXkCYdvF1Sq3DsUaLx6+NTHC5Jj1ooM2F68sBiVz3lvhfwQs/S3l6qg==
@@ -12562,7 +12541,7 @@ stylehacks@^5.1.1:
browserslist "^4.21.4"
postcss-selector-parser "^6.0.4"
subscriptions-transport-ws@^0.11.0:
subscriptions-transport-ws@^0.11.0, "subscriptions-transport-ws@^0.9.0 || ^0.11.0":
version "0.11.0"
resolved "https://registry.npmjs.org/subscriptions-transport-ws/-/subscriptions-transport-ws-0.11.0.tgz"
integrity sha512-8D4C6DIH5tGiAIpp5I0wD/xRlNiZAPGHygzCe7VzyzUoxHtawzjNAY9SUTXU05/EY2NMY9/9GF0ycizkXr1CWQ==
@@ -12917,7 +12896,7 @@ ts-invariant@^0.10.3:
dependencies:
tslib "^2.1.0"
ts-node@^10.7.0:
ts-node@^10.7.0, ts-node@>=9.0.0:
version "10.9.1"
resolved "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz"
integrity sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==
@@ -13028,7 +13007,7 @@ type-fest@^0.16.0:
resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.16.0.tgz"
integrity sha512-eaBzG6MxNzEn9kiwvtre90cXaNLkmadMWa1zQMs3XORCXNbsH/OewwbxC5ia9dCxIxnTAsSxXJaa/p5y8DlvJg==
type-fest@^0.20.2:
type-fest@^0.20.2, "type-fest@>=0.17.0 <4.0.0":
version "0.20.2"
resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz"
integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==
@@ -13081,6 +13060,11 @@ typescript-tuple@^2.2.1:
dependencies:
typescript-compare "^0.0.2"
"typescript@^3.2.1 || ^4", "typescript@>= 2.7", typescript@>=2.7, "typescript@>=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta", typescript@>=3:
version "4.9.5"
resolved "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz"
integrity sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==
unbox-primitive@^1.0.2:
version "1.0.2"
resolved "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz"
@@ -13386,7 +13370,7 @@ webpack-dev-middleware@^5.3.1:
range-parser "^1.2.1"
schema-utils "^4.0.0"
webpack-dev-server@^4.6.0:
webpack-dev-server@^4.6.0, "webpack-dev-server@3.x || 4.x":
version "4.11.1"
resolved "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.11.1.tgz"
integrity sha512-lILVz9tAUy1zGFwieuaQtYiadImb5M3d+H+L1zDYalYoDl0cksAB1UNyuE5MMWJrG6zR1tXkCP2fitl7yoUJiw==
@@ -13458,7 +13442,7 @@ webpack-sources@^2.2.0:
source-list-map "^2.0.1"
source-map "^0.6.1"
webpack@^5.64.4:
"webpack@^4.0.0 || ^5.0.0", "webpack@^4.37.0 || ^5.0.0", "webpack@^4.4.0 || ^5.9.0", "webpack@^4.44.2 || ^5.47.0", webpack@^5.0.0, webpack@^5.1.0, webpack@^5.20.0, webpack@^5.64.4, "webpack@>= 4", webpack@>=2, "webpack@>=4.43.0 <6.0.0":
version "5.75.0"
resolved "https://registry.npmjs.org/webpack/-/webpack-5.75.0.tgz"
integrity sha512-piaIaoVJlqMsPtX/+3KTTO6jfvrSYgauFVdt8cr9LTHKmcq/AMd4mhzsiP7ZF/PGRNPGA8336jldh9l2Kt2ogQ==

View File

@@ -1,6 +1,3 @@
- function:
name: jobs_ar_summary
schema: public
- function:
name: search_bills
schema: public

View File

@@ -1388,62 +1388,60 @@
- active:
_eq: true
columns:
- bodyshopid
- color
- created_at
- dailycost
- damage
- fleetnumber
- fuel
- id
- insuranceexpires
- leaseenddate
- make
- mileage
- model
- nextservicedate
- nextservicekm
- notes
- plate
- purchasedate
- readiness
- registrationexpires
- serviceenddate
- servicestartdate
- status
- created_at
- updated_at
- vin
- bodyshopid
- make
- model
- year
- plate
- color
- vin
- fleetnumber
- purchasedate
- servicestartdate
- serviceenddate
- leaseenddate
- status
- nextservicekm
- nextservicedate
- damage
- notes
- fuel
- registrationexpires
- insuranceexpires
- dailycost
- mileage
select_permissions:
- role: user
permission:
columns:
- bodyshopid
- color
- created_at
- dailycost
- damage
- fleetnumber
- fuel
- id
- insuranceexpires
- leaseenddate
- make
- mileage
- model
- nextservicedate
- nextservicekm
- notes
- plate
- purchasedate
- readiness
- registrationexpires
- serviceenddate
- servicestartdate
- dailycost
- fuel
- mileage
- nextservicekm
- color
- damage
- fleetnumber
- make
- model
- notes
- plate
- status
- updated_at
- vin
- year
- created_at
- updated_at
- bodyshopid
- id
filter:
bodyshop:
associations:
@@ -1458,32 +1456,31 @@
- role: user
permission:
columns:
- bodyshopid
- color
- created_at
- dailycost
- damage
- fleetnumber
- fuel
- id
- insuranceexpires
- leaseenddate
- make
- mileage
- model
- nextservicedate
- nextservicekm
- notes
- plate
- purchasedate
- readiness
- registrationexpires
- serviceenddate
- servicestartdate
- dailycost
- fuel
- mileage
- nextservicekm
- color
- damage
- fleetnumber
- make
- model
- notes
- plate
- status
- updated_at
- vin
- year
- created_at
- updated_at
- bodyshopid
- id
filter:
bodyshop:
associations:
@@ -2023,24 +2020,24 @@
- active:
_eq: true
columns:
- created_at
- employeeid
- id
- labor_rates
- percentage
- teamid
- created_at
- updated_at
- employeeid
- id
- teamid
select_permissions:
- role: user
permission:
columns:
- created_at
- employeeid
- id
- labor_rates
- percentage
- teamid
- created_at
- updated_at
- employeeid
- id
- teamid
filter:
employee_team:
bodyshop:
@@ -2055,13 +2052,13 @@
- role: user
permission:
columns:
- created_at
- employeeid
- id
- labor_rates
- percentage
- teamid
- created_at
- updated_at
- employeeid
- id
- teamid
filter:
employee_team:
bodyshop:
@@ -2123,23 +2120,21 @@
_eq: true
columns:
- active
- bodyshopid
- created_at
- id
- max_load
- name
- created_at
- updated_at
- bodyshopid
- id
select_permissions:
- role: user
permission:
columns:
- active
- bodyshopid
- created_at
- id
- max_load
- name
- created_at
- updated_at
- bodyshopid
- id
filter:
bodyshop:
associations:
@@ -2155,7 +2150,6 @@
columns:
- active
- bodyshopid
- max_load
- name
- updated_at
filter:
@@ -2423,73 +2417,6 @@
_eq: X-Hasura-User-Id
- active:
_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:
name: exportlog
schema: public
@@ -2673,9 +2600,6 @@
- table:
name: ioevents
schema: public
- table:
name: job_ar_schema
schema: public
- table:
name: job_conversations
schema: public
@@ -3815,7 +3739,6 @@
- referral_source
- referral_source_extra
- regie_number
- remove_from_ar
- ro_number
- scheduled_completion
- scheduled_delivery
@@ -4097,7 +4020,6 @@
- referral_source
- referral_source_extra
- regie_number
- remove_from_ar
- ro_number
- scheduled_completion
- scheduled_delivery
@@ -5960,13 +5882,6 @@
table:
name: email_audit_trail
schema: public
- name: eula_acceptances
using:
foreign_key_constraint_on:
column: useremail
table:
name: eula_acceptances
schema: public
- name: exportlogs
using:
foreign_key_constraint_on:

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

@@ -1 +0,0 @@
DROP TABLE "public"."eulas";

View File

@@ -1,18 +0,0 @@
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;

View File

@@ -1 +0,0 @@
DROP TABLE "public"."eula_acceptances";

View File

@@ -1,18 +0,0 @@
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;

View File

@@ -1,33 +0,0 @@
-- Could not auto-generate a down migration.
-- Please write an appropriate down migration for the SQL below:
-- CREATE OR REPLACE FUNCTION public.jobs_ar_summary ()
-- RETURNS SETOF jobs
-- LANGUAGE plpgsql
-- STABLE
-- AS $function$
-- BEGIN
--
-- RETURN query
-- select
-- j.id,
-- j.shopid,
-- j.ro_number,
-- j.clm_total,
-- p.total_payments,
-- j.clm_total - p.total_payments as balance
-- from
-- jobs j
-- left join (
-- select
-- p.jobid,
-- sum(p.amount) as total_payments
-- from
-- payments p
-- group by
-- p.jobid
-- ) p on
-- j.id = p.jobid ;
--
--
-- END
-- $function$;

View File

@@ -1,31 +0,0 @@
CREATE OR REPLACE FUNCTION public.jobs_ar_summary ()
RETURNS SETOF jobs
LANGUAGE plpgsql
STABLE
AS $function$
BEGIN
RETURN query
select
j.id,
j.shopid,
j.ro_number,
j.clm_total,
p.total_payments,
j.clm_total - p.total_payments as balance
from
jobs j
left join (
select
p.jobid,
sum(p.amount) as total_payments
from
payments p
group by
p.jobid
) p on
j.id = p.jobid ;
END
$function$;

View File

@@ -1 +0,0 @@
DROP TABLE "public"."job_ar_schema";

View File

@@ -1 +0,0 @@
CREATE TABLE "public"."job_ar_schema" ("id" uuid NOT NULL, "ro_number" text, "clm_total" numeric NOT NULL, "total_payments" numeric NOT NULL DEFAULT 0, "balance" numeric NOT NULL DEFAULT 0, PRIMARY KEY ("id") );

View File

@@ -1,34 +0,0 @@
-- Could not auto-generate a down migration.
-- Please write an appropriate down migration for the SQL below:
-- DROP FUNCTION public.jobs_ar_summary;
--
-- CREATE OR REPLACE FUNCTION public.jobs_ar_summary ()
-- RETURNS SETOF job_ar_schema
-- LANGUAGE plpgsql
-- STABLE
-- AS $function$
-- BEGIN
--
-- RETURN query
-- select
-- j.id,
-- j.ro_number,
-- j.clm_total,
-- p.total_payments,
-- j.clm_total - p.total_payments as balance
-- from
-- jobs j
-- left join (
-- select
-- p.jobid,
-- sum(p.amount) as total_payments
-- from
-- payments p
-- group by
-- p.jobid
-- ) p on
-- j.id = p.jobid ;
--
--
-- END
-- $function$;

View File

@@ -1,32 +0,0 @@
DROP FUNCTION public.jobs_ar_summary;
CREATE OR REPLACE FUNCTION public.jobs_ar_summary ()
RETURNS SETOF job_ar_schema
LANGUAGE plpgsql
STABLE
AS $function$
BEGIN
RETURN query
select
j.id,
j.ro_number,
j.clm_total,
p.total_payments,
j.clm_total - p.total_payments as balance
from
jobs j
left join (
select
p.jobid,
sum(p.amount) as total_payments
from
payments p
group by
p.jobid
) p on
j.id = p.jobid ;
END
$function$;

View File

@@ -1,32 +0,0 @@
-- Could not auto-generate a down migration.
-- Please write an appropriate down migration for the SQL below:
-- CREATE OR REPLACE FUNCTION public.jobs_ar_summary ()
-- RETURNS SETOF job_ar_schema
-- LANGUAGE plpgsql
-- STABLE
-- AS $function$
-- BEGIN
--
-- RETURN query
-- select
-- j.id,
-- j.ro_number,
-- j.clm_total,
-- p.total_payments,
-- j.clm_total - p.total_payments as balance
-- from
-- jobs j
-- left join (
-- select
-- p.jobid,
-- coalesce (sum(p.amount),0) as total_payments
-- from
-- payments p
-- group by
-- p.jobid
-- ) p on
-- j.id = p.jobid ;
--
--
-- END
-- $function$;

View File

@@ -1,30 +0,0 @@
CREATE OR REPLACE FUNCTION public.jobs_ar_summary ()
RETURNS SETOF job_ar_schema
LANGUAGE plpgsql
STABLE
AS $function$
BEGIN
RETURN query
select
j.id,
j.ro_number,
j.clm_total,
p.total_payments,
j.clm_total - p.total_payments as balance
from
jobs j
left join (
select
p.jobid,
coalesce (sum(p.amount),0) as total_payments
from
payments p
group by
p.jobid
) p on
j.id = p.jobid ;
END
$function$;

View File

@@ -1,32 +0,0 @@
-- Could not auto-generate a down migration.
-- Please write an appropriate down migration for the SQL below:
-- CREATE OR REPLACE FUNCTION public.jobs_ar_summary ()
-- RETURNS SETOF job_ar_schema
-- LANGUAGE plpgsql
-- STABLE
-- AS $function$
-- BEGIN
--
-- RETURN query
-- select
-- j.id,
-- j.ro_number,
-- j.clm_total,
-- p.total_payments,
-- j.clm_total - p.total_payments as balance
-- from
-- jobs j
-- left join (
-- select
-- p.jobid,
-- coalesce (sum(p.amount),0) as total_payments
-- from
-- payments p
-- group by
-- p.jobid
-- ) p on
-- j.id = p.jobid ;
--
--
-- END
-- $function$;

View File

@@ -1,30 +0,0 @@
CREATE OR REPLACE FUNCTION public.jobs_ar_summary ()
RETURNS SETOF job_ar_schema
LANGUAGE plpgsql
STABLE
AS $function$
BEGIN
RETURN query
select
j.id,
j.ro_number,
j.clm_total,
p.total_payments,
j.clm_total - p.total_payments as balance
from
jobs j
left join (
select
p.jobid,
coalesce (sum(p.amount),0) as total_payments
from
payments p
group by
p.jobid
) p on
j.id = p.jobid ;
END
$function$;

View File

@@ -1,32 +0,0 @@
-- Could not auto-generate a down migration.
-- Please write an appropriate down migration for the SQL below:
-- CREATE OR REPLACE FUNCTION public.jobs_ar_summary ()
-- RETURNS SETOF job_ar_schema
-- LANGUAGE plpgsql
-- STABLE
-- AS $function$
-- BEGIN
--
-- RETURN query
-- select
-- j.id,
-- j.ro_number,
-- j.clm_total,
-- coalesce (p.total_payments,0) as total_payments,
-- j.clm_total - coalesce (p.total_payments,0) as balance
-- from
-- jobs j
-- left join (
-- select
-- p.jobid,
-- coalesce (sum(p.amount),0) as total_payments
-- from
-- payments p
-- group by
-- p.jobid
-- ) p on
-- j.id = p.jobid ;
--
--
-- END
-- $function$;

View File

@@ -1,30 +0,0 @@
CREATE OR REPLACE FUNCTION public.jobs_ar_summary ()
RETURNS SETOF job_ar_schema
LANGUAGE plpgsql
STABLE
AS $function$
BEGIN
RETURN query
select
j.id,
j.ro_number,
j.clm_total,
coalesce (p.total_payments,0) as total_payments,
j.clm_total - coalesce (p.total_payments,0) as balance
from
jobs j
left join (
select
p.jobid,
coalesce (sum(p.amount),0) as total_payments
from
payments p
group by
p.jobid
) p on
j.id = p.jobid ;
END
$function$;

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"."jobs" add column "remove_from_ar" boolean
-- not null default 'false';

View File

@@ -1,2 +0,0 @@
alter table "public"."jobs" add column "remove_from_ar" boolean
not null default 'false';

View File

@@ -1,33 +0,0 @@
-- Could not auto-generate a down migration.
-- Please write an appropriate down migration for the SQL below:
-- CREATE OR REPLACE FUNCTION public.jobs_ar_summary ()
-- RETURNS SETOF job_ar_schema
-- LANGUAGE plpgsql
-- STABLE
-- AS $function$
-- BEGIN
--
-- RETURN query
-- select
-- j.id,
-- j.ro_number,
-- j.clm_total,
-- coalesce (p.total_payments,0) as total_payments,
-- j.clm_total - coalesce (p.total_payments,0) as balance
-- from
-- jobs j
-- left join (
-- select
-- p.jobid,
-- coalesce (sum(p.amount),0) as total_payments
-- from
-- payments p
-- group by
-- p.jobid
-- ) p on
-- j.id = p.jobid
-- where j.remove_from_ar = false;
--
--
-- END
-- $function$;

View File

@@ -1,31 +0,0 @@
CREATE OR REPLACE FUNCTION public.jobs_ar_summary ()
RETURNS SETOF job_ar_schema
LANGUAGE plpgsql
STABLE
AS $function$
BEGIN
RETURN query
select
j.id,
j.ro_number,
j.clm_total,
coalesce (p.total_payments,0) as total_payments,
j.clm_total - coalesce (p.total_payments,0) as balance
from
jobs j
left join (
select
p.jobid,
coalesce (sum(p.amount),0) as total_payments
from
payments p
group by
p.jobid
) p on
j.id = p.jobid
where j.remove_from_ar = false;
END
$function$;

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"."job_ar_schema" add column "date_invoiced" timestamptz
-- null;

View File

@@ -1,2 +0,0 @@
alter table "public"."job_ar_schema" add column "date_invoiced" timestamptz
null;

View File

@@ -1,34 +0,0 @@
-- Could not auto-generate a down migration.
-- Please write an appropriate down migration for the SQL below:
-- CREATE OR REPLACE FUNCTION public.jobs_ar_summary ()
-- RETURNS SETOF job_ar_schema
-- LANGUAGE plpgsql
-- STABLE
-- AS $function$
-- BEGIN
--
-- RETURN query
-- select
-- j.id,
-- j.ro_number,
-- j.clm_total,
-- j.date_invoiced,
-- coalesce (p.total_payments,0) as total_payments,
-- j.clm_total - coalesce (p.total_payments,0) as balance
-- from
-- jobs j
-- left join (
-- select
-- p.jobid,
-- coalesce (sum(p.amount),0) as total_payments
-- from
-- payments p
-- group by
-- p.jobid
-- ) p on
-- j.id = p.jobid
-- where j.remove_from_ar = false and j.date_invoiced is not null;
--
--
-- END
-- $function$;

View File

@@ -1,32 +0,0 @@
CREATE OR REPLACE FUNCTION public.jobs_ar_summary ()
RETURNS SETOF job_ar_schema
LANGUAGE plpgsql
STABLE
AS $function$
BEGIN
RETURN query
select
j.id,
j.ro_number,
j.clm_total,
j.date_invoiced,
coalesce (p.total_payments,0) as total_payments,
j.clm_total - coalesce (p.total_payments,0) as balance
from
jobs j
left join (
select
p.jobid,
coalesce (sum(p.amount),0) as total_payments
from
payments p
group by
p.jobid
) p on
j.id = p.jobid
where j.remove_from_ar = false and j.date_invoiced is not null;
END
$function$;

View File

@@ -1,34 +0,0 @@
-- Could not auto-generate a down migration.
-- Please write an appropriate down migration for the SQL below:
-- CREATE OR REPLACE FUNCTION public.jobs_ar_summary ()
-- RETURNS SETOF job_ar_schema
-- LANGUAGE plpgsql
-- STABLE
-- AS $function$
-- BEGIN
--
-- RETURN query
-- select
-- j.id,
-- j.ro_number,
-- j.clm_total,
-- coalesce (p.total_payments,0) as total_payments,
-- j.clm_total - coalesce (p.total_payments,0) as balance,
-- j.date_invoiced
-- from
-- jobs j
-- left join (
-- select
-- p.jobid,
-- coalesce (sum(p.amount),0) as total_payments
-- from
-- payments p
-- group by
-- p.jobid
-- ) p on
-- j.id = p.jobid
-- where j.remove_from_ar = false and j.date_invoiced is not null;
--
--
-- END
-- $function$;

View File

@@ -1,32 +0,0 @@
CREATE OR REPLACE FUNCTION public.jobs_ar_summary ()
RETURNS SETOF job_ar_schema
LANGUAGE plpgsql
STABLE
AS $function$
BEGIN
RETURN query
select
j.id,
j.ro_number,
j.clm_total,
coalesce (p.total_payments,0) as total_payments,
j.clm_total - coalesce (p.total_payments,0) as balance,
j.date_invoiced
from
jobs j
left join (
select
p.jobid,
coalesce (sum(p.amount),0) as total_payments
from
payments p
group by
p.jobid
) p on
j.id = p.jobid
where j.remove_from_ar = false and j.date_invoiced is not null;
END
$function$;

View File

@@ -1,34 +0,0 @@
-- Could not auto-generate a down migration.
-- Please write an appropriate down migration for the SQL below:
-- CREATE OR REPLACE FUNCTION public.jobs_ar_summary ()
-- RETURNS SETOF job_ar_schema
-- LANGUAGE plpgsql
-- STABLE
-- AS $function$
-- BEGIN
--
-- RETURN query
-- select
-- j.id,
-- j.ro_number,
-- j.clm_total,
-- coalesce (p.total_payments,0) as total_payments,
-- j.clm_total - coalesce (p.total_payments,0) as balance,
-- j.date_invoiced
-- from
-- jobs j
-- left join (
-- select
-- p.jobid,
-- coalesce (sum(p.amount),0) as total_payments
-- from
-- payments p
-- group by
-- p.jobid
-- ) p on
-- j.id = p.jobid
-- where j.remove_from_ar = false and j.date_invoiced is not null and balance > 0;
--
--
-- END
-- $function$;

View File

@@ -1,32 +0,0 @@
CREATE OR REPLACE FUNCTION public.jobs_ar_summary ()
RETURNS SETOF job_ar_schema
LANGUAGE plpgsql
STABLE
AS $function$
BEGIN
RETURN query
select
j.id,
j.ro_number,
j.clm_total,
coalesce (p.total_payments,0) as total_payments,
j.clm_total - coalesce (p.total_payments,0) as balance,
j.date_invoiced
from
jobs j
left join (
select
p.jobid,
coalesce (sum(p.amount),0) as total_payments
from
payments p
group by
p.jobid
) p on
j.id = p.jobid
where j.remove_from_ar = false and j.date_invoiced is not null and balance > 0;
END
$function$;

View File

@@ -1,34 +0,0 @@
-- Could not auto-generate a down migration.
-- Please write an appropriate down migration for the SQL below:
-- CREATE OR REPLACE FUNCTION public.jobs_ar_summary ()
-- RETURNS SETOF job_ar_schema
-- LANGUAGE plpgsql
-- STABLE
-- AS $function$
-- BEGIN
--
-- RETURN query
-- select
-- j.id,
-- j.ro_number,
-- j.clm_total,
-- coalesce (p.total_payments,0) as total_payments,
-- j.clm_total - coalesce (p.total_payments,0) as balance,
-- j.date_invoiced
-- from
-- jobs j
-- left join (
-- select
-- p.jobid,
-- coalesce (sum(p.amount),0) as total_payments
-- from
-- payments p
-- group by
-- p.jobid
-- ) p on
-- j.id = p.jobid
-- where j.remove_from_ar = false and j.date_invoiced is not null and j.clm_total - coalesce (p.total_payments,0) > 0;
--
--
-- END
-- $function$;

View File

@@ -1,32 +0,0 @@
CREATE OR REPLACE FUNCTION public.jobs_ar_summary ()
RETURNS SETOF job_ar_schema
LANGUAGE plpgsql
STABLE
AS $function$
BEGIN
RETURN query
select
j.id,
j.ro_number,
j.clm_total,
coalesce (p.total_payments,0) as total_payments,
j.clm_total - coalesce (p.total_payments,0) as balance,
j.date_invoiced
from
jobs j
left join (
select
p.jobid,
coalesce (sum(p.amount),0) as total_payments
from
payments p
group by
p.jobid
) p on
j.id = p.jobid
where j.remove_from_ar = false and j.date_invoiced is not null and j.clm_total - coalesce (p.total_payments,0) > 0;
END
$function$;

2
package-lock.json generated
View File

@@ -55,7 +55,7 @@
"source-map-explorer": "^2.5.2"
},
"engines": {
"node": ">=18.0.0",
"node": ">=16.0.0",
"npm": ">=8.0.0"
}
},

View File

@@ -34,10 +34,6 @@ const io = new Server(server, {
"http://localhost:3000",
"https://imex.online",
"https://www.imex.online",
"https://beta.test.imex.online",
"https://www.beta.test.imex.online",
"https://beta.imex.online",
"https://www.beta.imex.online",
],
methods: ["GET", "POST"],
credentials: true,
@@ -228,7 +224,6 @@ app.post("/qbo/payments", fb.validateFirebaseIdToken, qbo.payments);
var data = require("./server/data/data");
app.post("/data/ah", data.autohouse);
app.post("/data/cc", data.claimscorp);
app.post("/data/kaizen", data.kaizen);
app.post("/record-handler/arms", data.arms);
var taskHandler = require("./server/tasks/tasks");

View File

@@ -507,7 +507,7 @@ const CreateRepairOrderTag = (job, errorCallback) => {
Body: repairCosts.BodyLaborTotalCost.toFormat(CCDineroFormat),
Paint: repairCosts.RefinishLaborTotalCost.toFormat(CCDineroFormat),
Prep: Dinero().toFormat(CCDineroFormat),
Frame: repairCosts.FrameLaborTotalCost.toFormat(CCDineroFormat),
Frame: Dinero(job.job_totals.rates.laf.total).toFormat(CCDineroFormat),
Mech: repairCosts.MechanicalLaborTotalCost.toFormat(CCDineroFormat),
Glass: repairCosts.GlassLaborTotalCost.toFormat(CCDineroFormat),
Elec: repairCosts.ElectricalLaborTotalCost.toFormat(CCDineroFormat),

Some files were not shown because too many files have changed in this diff Show More