Merged in release/2022-01-28 (pull request #362)
release/2022-01-28 Approved-by: Patrick Fic
This commit is contained in:
@@ -10,7 +10,7 @@ npx hasura migrate apply --endpoint https://db.imex.online/ --admin-secret 'Prod
|
|||||||
npx hasura migrate apply --endpoint https://db.test.bodyshop.app/ --admin-secret 'Test-ImEXOnlineBySnaptSoftware!'
|
npx hasura migrate apply --endpoint https://db.test.bodyshop.app/ --admin-secret 'Test-ImEXOnlineBySnaptSoftware!'
|
||||||
|
|
||||||
NGROK TEsting:
|
NGROK TEsting:
|
||||||
./ngrok.exe http http://localhost:5000 -host-header="localhost:5000"
|
./ngrok.exe http http://localhost:4000 -host-header="localhost:4000"
|
||||||
|
|
||||||
Finding deadfiles - run from client directory
|
Finding deadfiles - run from client directory
|
||||||
npx deadfile ./src/index.js --exclude build templates
|
npx deadfile ./src/index.js --exclude build templates
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<babeledit_project be_version="2.7.1" version="1.2">
|
<babeledit_project version="1.2" be_version="2.7.1">
|
||||||
<!--
|
<!--
|
||||||
|
|
||||||
BabelEdit project file
|
BabelEdit project file
|
||||||
@@ -25448,6 +25448,27 @@
|
|||||||
<folder_node>
|
<folder_node>
|
||||||
<name>dms</name>
|
<name>dms</name>
|
||||||
<children>
|
<children>
|
||||||
|
<concept_node>
|
||||||
|
<name>damageto</name>
|
||||||
|
<definition_loaded>false</definition_loaded>
|
||||||
|
<description></description>
|
||||||
|
<comment></comment>
|
||||||
|
<default_text></default_text>
|
||||||
|
<translations>
|
||||||
|
<translation>
|
||||||
|
<language>en-US</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
<translation>
|
||||||
|
<language>es-MX</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
<translation>
|
||||||
|
<language>fr-CA</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
</translations>
|
||||||
|
</concept_node>
|
||||||
<concept_node>
|
<concept_node>
|
||||||
<name>defaultstory</name>
|
<name>defaultstory</name>
|
||||||
<definition_loaded>false</definition_loaded>
|
<definition_loaded>false</definition_loaded>
|
||||||
@@ -37167,6 +37188,27 @@
|
|||||||
</translation>
|
</translation>
|
||||||
</translations>
|
</translations>
|
||||||
</concept_node>
|
</concept_node>
|
||||||
|
<concept_node>
|
||||||
|
<name>estimates_written_converted</name>
|
||||||
|
<definition_loaded>false</definition_loaded>
|
||||||
|
<description></description>
|
||||||
|
<comment></comment>
|
||||||
|
<default_text></default_text>
|
||||||
|
<translations>
|
||||||
|
<translation>
|
||||||
|
<language>en-US</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
<translation>
|
||||||
|
<language>es-MX</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
<translation>
|
||||||
|
<language>fr-CA</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
</translations>
|
||||||
|
</concept_node>
|
||||||
<concept_node>
|
<concept_node>
|
||||||
<name>estimator_detail</name>
|
<name>estimator_detail</name>
|
||||||
<definition_loaded>false</definition_loaded>
|
<definition_loaded>false</definition_loaded>
|
||||||
@@ -38049,6 +38091,27 @@
|
|||||||
</translation>
|
</translation>
|
||||||
</translations>
|
</translations>
|
||||||
</concept_node>
|
</concept_node>
|
||||||
|
<concept_node>
|
||||||
|
<name>production_by_category_one</name>
|
||||||
|
<definition_loaded>false</definition_loaded>
|
||||||
|
<description></description>
|
||||||
|
<comment></comment>
|
||||||
|
<default_text></default_text>
|
||||||
|
<translations>
|
||||||
|
<translation>
|
||||||
|
<language>en-US</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
<translation>
|
||||||
|
<language>es-MX</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
<translation>
|
||||||
|
<language>fr-CA</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
</translations>
|
||||||
|
</concept_node>
|
||||||
<concept_node>
|
<concept_node>
|
||||||
<name>production_by_csr</name>
|
<name>production_by_csr</name>
|
||||||
<definition_loaded>false</definition_loaded>
|
<definition_loaded>false</definition_loaded>
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import { ApolloProvider } from "@apollo/client";
|
|||||||
import { SplitFactory, SplitSdk } from "@splitsoftware/splitio-react";
|
import { SplitFactory, SplitSdk } from "@splitsoftware/splitio-react";
|
||||||
import { ConfigProvider } from "antd";
|
import { ConfigProvider } from "antd";
|
||||||
import enLocale from "antd/es/locale/en_US";
|
import enLocale from "antd/es/locale/en_US";
|
||||||
import LogRocket from "logrocket";
|
|
||||||
import moment from "moment";
|
import moment from "moment";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
@@ -10,13 +9,8 @@ import GlobalLoadingBar from "../components/global-loading-bar/global-loading-ba
|
|||||||
import client from "../utils/GraphQLClient";
|
import client from "../utils/GraphQLClient";
|
||||||
import App from "./App";
|
import App from "./App";
|
||||||
|
|
||||||
|
|
||||||
moment.locale("en-US");
|
moment.locale("en-US");
|
||||||
|
|
||||||
|
|
||||||
//tracker.start();
|
|
||||||
if (process.env.NODE_ENV === "production") LogRocket.init("gvfvfw/bodyshopapp");
|
|
||||||
|
|
||||||
export const factory = SplitSdk({
|
export const factory = SplitSdk({
|
||||||
core: {
|
core: {
|
||||||
authorizationKey: process.env.REACT_APP_SPLIT_API,
|
authorizationKey: process.env.REACT_APP_SPLIT_API,
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
|
import { useTreatments } from "@splitsoftware/splitio-react";
|
||||||
import { Button, Result } from "antd";
|
import { Button, Result } from "antd";
|
||||||
|
import LogRocket from "logrocket";
|
||||||
import React, { lazy, Suspense, useEffect } from "react";
|
import React, { lazy, Suspense, useEffect } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
@@ -9,15 +11,18 @@ import ErrorBoundary from "../components/error-boundary/error-boundary.component
|
|||||||
//Component Imports
|
//Component Imports
|
||||||
import LoadingSpinner from "../components/loading-spinner/loading-spinner.component";
|
import LoadingSpinner from "../components/loading-spinner/loading-spinner.component";
|
||||||
import DisclaimerPage from "../pages/disclaimer/disclaimer.page";
|
import DisclaimerPage from "../pages/disclaimer/disclaimer.page";
|
||||||
|
import LandingPage from "../pages/landing/landing.page";
|
||||||
import TechPageContainer from "../pages/tech/tech.page.container";
|
import TechPageContainer from "../pages/tech/tech.page.container";
|
||||||
import { setOnline } from "../redux/application/application.actions";
|
import { setOnline } from "../redux/application/application.actions";
|
||||||
import { selectOnline } from "../redux/application/application.selectors";
|
import { selectOnline } from "../redux/application/application.selectors";
|
||||||
import { checkUserSession } from "../redux/user/user.actions";
|
import { checkUserSession } from "../redux/user/user.actions";
|
||||||
import { selectCurrentUser } from "../redux/user/user.selectors";
|
import {
|
||||||
|
selectBodyshop,
|
||||||
|
selectCurrentUser,
|
||||||
|
} from "../redux/user/user.selectors";
|
||||||
import PrivateRoute from "../utils/private-route";
|
import PrivateRoute from "../utils/private-route";
|
||||||
import "./App.styles.scss";
|
import "./App.styles.scss";
|
||||||
|
|
||||||
import LandingPage from "../pages/landing/landing.page";
|
|
||||||
const ResetPassword = lazy(() =>
|
const ResetPassword = lazy(() =>
|
||||||
import("../pages/reset-password/reset-password.component")
|
import("../pages/reset-password/reset-password.component")
|
||||||
);
|
);
|
||||||
@@ -32,13 +37,26 @@ const MobilePaymentContainer = lazy(() =>
|
|||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
currentUser: selectCurrentUser,
|
currentUser: selectCurrentUser,
|
||||||
online: selectOnline,
|
online: selectOnline,
|
||||||
|
bodyshop: selectBodyshop,
|
||||||
});
|
});
|
||||||
const mapDispatchToProps = (dispatch) => ({
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
checkUserSession: () => dispatch(checkUserSession()),
|
checkUserSession: () => dispatch(checkUserSession()),
|
||||||
setOnline: (isOnline) => dispatch(setOnline(isOnline)),
|
setOnline: (isOnline) => dispatch(setOnline(isOnline)),
|
||||||
});
|
});
|
||||||
|
|
||||||
export function App({ checkUserSession, currentUser, online, setOnline }) {
|
export function App({
|
||||||
|
bodyshop,
|
||||||
|
checkUserSession,
|
||||||
|
currentUser,
|
||||||
|
online,
|
||||||
|
setOnline,
|
||||||
|
}) {
|
||||||
|
const { LogRocket_Tracking } = useTreatments(
|
||||||
|
["LogRocket_Tracking"],
|
||||||
|
{},
|
||||||
|
bodyshop && bodyshop.imexshopid
|
||||||
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!navigator.onLine) {
|
if (!navigator.onLine) {
|
||||||
setOnline(false);
|
setOnline(false);
|
||||||
@@ -59,6 +77,16 @@ export function App({ checkUserSession, currentUser, online, setOnline }) {
|
|||||||
window.addEventListener("online", function (e) {
|
window.addEventListener("online", function (e) {
|
||||||
setOnline(true);
|
setOnline(true);
|
||||||
});
|
});
|
||||||
|
useEffect(() => {
|
||||||
|
if (currentUser.authorized) {
|
||||||
|
if (
|
||||||
|
process.env.NODE_ENV === "production" &&
|
||||||
|
LogRocket_Tracking.treatment === "on"
|
||||||
|
) {
|
||||||
|
LogRocket.init("gvfvfw/bodyshopapp");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [currentUser.authorized, LogRocket_Tracking.treatment]);
|
||||||
|
|
||||||
if (currentUser.authorized === null) {
|
if (currentUser.authorized === null) {
|
||||||
return <LoadingSpinner message={t("general.labels.loggingin")} />;
|
return <LoadingSpinner message={t("general.labels.loggingin")} />;
|
||||||
|
|||||||
@@ -245,7 +245,7 @@ function BillEnterModalContainer({
|
|||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
title={t("bills.labels.new")}
|
title={t("bills.labels.new")}
|
||||||
width={"90%"}
|
width={"98%"}
|
||||||
visible={billEnterModal.visible}
|
visible={billEnterModal.visible}
|
||||||
okText={t("general.actions.save")}
|
okText={t("general.actions.save")}
|
||||||
keyboard="false"
|
keyboard="false"
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { DeleteFilled, WarningOutlined } from "@ant-design/icons";
|
import { DeleteFilled, DollarCircleFilled } from "@ant-design/icons";
|
||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
Form,
|
Form,
|
||||||
@@ -8,6 +8,7 @@ import {
|
|||||||
Space,
|
Space,
|
||||||
Switch,
|
Switch,
|
||||||
Table,
|
Table,
|
||||||
|
Tooltip,
|
||||||
} from "antd";
|
} from "antd";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
@@ -43,7 +44,7 @@ export function BillEnterModalLinesComponent({
|
|||||||
title: t("billlines.fields.jobline"),
|
title: t("billlines.fields.jobline"),
|
||||||
dataIndex: "joblineid",
|
dataIndex: "joblineid",
|
||||||
editable: true,
|
editable: true,
|
||||||
|
width: "20rem",
|
||||||
formItemProps: (field) => {
|
formItemProps: (field) => {
|
||||||
return {
|
return {
|
||||||
key: `${field.index}joblinename`,
|
key: `${field.index}joblinename`,
|
||||||
@@ -57,11 +58,24 @@ export function BillEnterModalLinesComponent({
|
|||||||
],
|
],
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
wrapper: (props) => (
|
||||||
|
<Form.Item
|
||||||
|
noStyle
|
||||||
|
shouldUpdate={(prev, cur) =>
|
||||||
|
prev.is_credit_memo !== cur.is_credit_memo
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{() => {
|
||||||
|
return props.children;
|
||||||
|
}}
|
||||||
|
</Form.Item>
|
||||||
|
),
|
||||||
formInput: (record, index) => (
|
formInput: (record, index) => (
|
||||||
<BillLineSearchSelect
|
<BillLineSearchSelect
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
options={lineData}
|
options={lineData}
|
||||||
style={{ width: "100%", minWidth: "10rem" }}
|
style={{ width: "100%", minWidth: "10rem" }}
|
||||||
|
allowRemoved={form.getFieldValue("is_credit_memo") || false}
|
||||||
onSelect={(value, opt) => {
|
onSelect={(value, opt) => {
|
||||||
setFieldsValue({
|
setFieldsValue({
|
||||||
billlines: getFieldsValue(["billlines"]).billlines.map(
|
billlines: getFieldsValue(["billlines"]).billlines.map(
|
||||||
@@ -201,23 +215,58 @@ export function BillEnterModalLinesComponent({
|
|||||||
};
|
};
|
||||||
},
|
},
|
||||||
formInput: (record, index) => (
|
formInput: (record, index) => (
|
||||||
<CurrencyInput min={0} disabled={disabled} />
|
<CurrencyInput
|
||||||
|
min={0}
|
||||||
|
disabled={disabled}
|
||||||
|
controls={false}
|
||||||
|
addonAfter={
|
||||||
|
<Form.Item shouldUpdate noStyle>
|
||||||
|
{() => {
|
||||||
|
const line = getFieldsValue(["billlines"]).billlines[index];
|
||||||
|
if (!!!line) return null;
|
||||||
|
let lineDiscount = 1 - line.actual_cost / line.actual_price;
|
||||||
|
if (isNaN(lineDiscount)) lineDiscount = 0;
|
||||||
|
return (
|
||||||
|
<Tooltip title={`${(lineDiscount * 100).toFixed(2) || 0}%`}>
|
||||||
|
<DollarCircleFilled
|
||||||
|
style={{
|
||||||
|
color:
|
||||||
|
Math.abs(lineDiscount - discount) > 0.005
|
||||||
|
? lineDiscount > discount
|
||||||
|
? "orange"
|
||||||
|
: "red"
|
||||||
|
: "green",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
</Form.Item>
|
||||||
|
}
|
||||||
|
/>
|
||||||
),
|
),
|
||||||
additional: (record, index) => (
|
// additional: (record, index) => (
|
||||||
<Form.Item shouldUpdate>
|
// <Form.Item shouldUpdate>
|
||||||
{() => {
|
// {() => {
|
||||||
const line = getFieldsValue(["billlines"]).billlines[index];
|
// const line = getFieldsValue(["billlines"]).billlines[index];
|
||||||
if (!!!line) return null;
|
// if (!!!line) return null;
|
||||||
const lineDiscount = (
|
// const lineDiscount = (
|
||||||
1 -
|
// 1 -
|
||||||
Math.round((line.actual_cost / line.actual_price) * 100) / 100
|
// Math.round((line.actual_cost / line.actual_price) * 100) / 100
|
||||||
).toPrecision(2);
|
// ).toPrecision(2);
|
||||||
|
|
||||||
if (lineDiscount - discount === 0) return <div />;
|
// return (
|
||||||
return <WarningOutlined style={{ color: "red" }} />;
|
// <Tooltip title={`${(lineDiscount * 100).toFixed(0) || 0}%`}>
|
||||||
}}
|
// <DollarCircleFilled
|
||||||
</Form.Item>
|
// style={{
|
||||||
),
|
// color: lineDiscount - discount !== 0 ? "red" : "green",
|
||||||
|
// }}
|
||||||
|
// />
|
||||||
|
// </Tooltip>
|
||||||
|
// );
|
||||||
|
// }}
|
||||||
|
// </Form.Item>
|
||||||
|
// ),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t("billlines.fields.cost_center"),
|
title: t("billlines.fields.cost_center"),
|
||||||
@@ -488,6 +537,7 @@ const EditableCell = ({
|
|||||||
formInput,
|
formInput,
|
||||||
formItemProps,
|
formItemProps,
|
||||||
additional,
|
additional,
|
||||||
|
wrapper,
|
||||||
...restProps
|
...restProps
|
||||||
}) => {
|
}) => {
|
||||||
if (additional)
|
if (additional)
|
||||||
@@ -505,7 +555,20 @@ const EditableCell = ({
|
|||||||
</Space>
|
</Space>
|
||||||
</td>
|
</td>
|
||||||
);
|
);
|
||||||
|
if (wrapper)
|
||||||
|
return (
|
||||||
|
<wrapper>
|
||||||
|
<td {...restProps}>
|
||||||
|
<Form.Item
|
||||||
|
labelCol={{ span: 0 }}
|
||||||
|
name={dataIndex}
|
||||||
|
{...(formItemProps && formItemProps(record))}
|
||||||
|
>
|
||||||
|
{(formInput && formInput(record, record.name)) || children}
|
||||||
|
</Form.Item>
|
||||||
|
</td>
|
||||||
|
</wrapper>
|
||||||
|
);
|
||||||
return (
|
return (
|
||||||
<td {...restProps}>
|
<td {...restProps}>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
|
|||||||
@@ -4,7 +4,10 @@ import { useTranslation } from "react-i18next";
|
|||||||
|
|
||||||
//To be used as a form element only.
|
//To be used as a form element only.
|
||||||
const { Option } = Select;
|
const { Option } = Select;
|
||||||
const BillLineSearchSelect = ({ options, disabled, ...restProps }, ref) => {
|
const BillLineSearchSelect = (
|
||||||
|
{ options, disabled, allowRemoved, ...restProps },
|
||||||
|
ref
|
||||||
|
) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -12,6 +15,7 @@ const BillLineSearchSelect = ({ options, disabled, ...restProps }, ref) => {
|
|||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
ref={ref}
|
ref={ref}
|
||||||
showSearch
|
showSearch
|
||||||
|
dropdownMatchSelectWidth={false}
|
||||||
// optionFilterProp="line_desc"
|
// optionFilterProp="line_desc"
|
||||||
filterOption={(inputValue, option) => {
|
filterOption={(inputValue, option) => {
|
||||||
return (
|
return (
|
||||||
@@ -36,7 +40,7 @@ const BillLineSearchSelect = ({ options, disabled, ...restProps }, ref) => {
|
|||||||
{options
|
{options
|
||||||
? options.map((item) => (
|
? options.map((item) => (
|
||||||
<Option
|
<Option
|
||||||
disabled={item.removed}
|
disabled={allowRemoved ? false : item.removed}
|
||||||
key={item.id}
|
key={item.id}
|
||||||
value={item.id}
|
value={item.id}
|
||||||
cost={item.act_price ? item.act_price : 0}
|
cost={item.act_price ? item.act_price : 0}
|
||||||
@@ -49,9 +53,14 @@ const BillLineSearchSelect = ({ options, disabled, ...restProps }, ref) => {
|
|||||||
...(item.removed ? { textDecoration: "line-through" } : {}),
|
...(item.removed ? { textDecoration: "line-through" } : {}),
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{`${item.removed ? `(REMOVED) ` : ""}${item.line_desc}${
|
<span>{`${item.removed ? `(REMOVED) ` : ""}${item.line_desc}${
|
||||||
item.oem_partno ? ` - ${item.oem_partno}` : ""
|
item.oem_partno ? ` - ${item.oem_partno}` : ""
|
||||||
}${item.act_price ? ` - $${item.act_price}` : ``}`}
|
}`}</span>
|
||||||
|
<span style={{ float: "right", paddingleft: "1rem" }}>
|
||||||
|
{item.act_price
|
||||||
|
? `$${item.act_price && item.act_price.toFixed(2)}`
|
||||||
|
: ``}
|
||||||
|
</span>
|
||||||
</Option>
|
</Option>
|
||||||
))
|
))
|
||||||
: null}
|
: null}
|
||||||
|
|||||||
@@ -58,7 +58,8 @@ export function BillsListTableComponent({
|
|||||||
disabled={
|
disabled={
|
||||||
record.is_credit_memo || record.vendorid === bodyshop.inhousevendorid
|
record.is_credit_memo || record.vendorid === bodyshop.inhousevendorid
|
||||||
}
|
}
|
||||||
onClick={() =>
|
onClick={() => {
|
||||||
|
console.log(record);
|
||||||
setPartsOrderContext({
|
setPartsOrderContext({
|
||||||
actions: {},
|
actions: {},
|
||||||
context: {
|
context: {
|
||||||
@@ -74,12 +75,13 @@ export function BillsListTableComponent({
|
|||||||
cost: i.actual_cost,
|
cost: i.actual_cost,
|
||||||
quantity: i.quantity,
|
quantity: i.quantity,
|
||||||
joblineid: i.joblineid,
|
joblineid: i.joblineid,
|
||||||
|
oem_partno: i.jobline && i.jobline.oem_partno,
|
||||||
};
|
};
|
||||||
}),
|
}),
|
||||||
isReturn: true,
|
isReturn: true,
|
||||||
},
|
},
|
||||||
})
|
});
|
||||||
}
|
}}
|
||||||
>
|
>
|
||||||
{t("bills.actions.return")}
|
{t("bills.actions.return")}
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -45,13 +45,14 @@ function ChatSendMessageComponent({
|
|||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const handleEnter = () => {
|
const handleEnter = () => {
|
||||||
if (message === "" || !message) return;
|
|
||||||
logImEXEvent("messaging_send_message");
|
|
||||||
const selectedImages = selectedMedia.filter((i) => i.isSelected);
|
const selectedImages = selectedMedia.filter((i) => i.isSelected);
|
||||||
if (selectedImages < 11) {
|
if ((message === "" || !message) && selectedImages.length === 0) return;
|
||||||
|
logImEXEvent("messaging_send_message");
|
||||||
|
|
||||||
|
if (selectedImages.length < 11) {
|
||||||
sendMessage({
|
sendMessage({
|
||||||
to: conversation.phone_num,
|
to: conversation.phone_num,
|
||||||
body: message,
|
body: message || "",
|
||||||
messagingServiceSid: bodyshop.messagingservicesid,
|
messagingServiceSid: bodyshop.messagingservicesid,
|
||||||
conversationid: conversation.id,
|
conversationid: conversation.id,
|
||||||
selectedMedia: selectedImages,
|
selectedMedia: selectedImages,
|
||||||
@@ -92,7 +93,7 @@ function ChatSendMessageComponent({
|
|||||||
</span>
|
</span>
|
||||||
<SendOutlined
|
<SendOutlined
|
||||||
className="imex-flex-row__margin"
|
className="imex-flex-row__margin"
|
||||||
disabled={message === "" || !message}
|
// disabled={message === "" || !message}
|
||||||
onClick={handleEnter}
|
onClick={handleEnter}
|
||||||
/>
|
/>
|
||||||
<Spin
|
<Spin
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import { DeleteFilled, DownOutlined } from "@ant-design/icons";
|
|||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
Card,
|
Card,
|
||||||
DatePicker,
|
|
||||||
Divider,
|
Divider,
|
||||||
Dropdown,
|
Dropdown,
|
||||||
Form,
|
Form,
|
||||||
@@ -15,6 +14,7 @@ import {
|
|||||||
Typography,
|
Typography,
|
||||||
} from "antd";
|
} from "antd";
|
||||||
import Dinero from "dinero.js";
|
import Dinero from "dinero.js";
|
||||||
|
import moment from "moment";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
@@ -23,9 +23,9 @@ import { determineDmsType } from "../../pages/dms/dms.container";
|
|||||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||||
import DmsCdkMakes from "../dms-cdk-makes/dms-cdk-makes.component";
|
import DmsCdkMakes from "../dms-cdk-makes/dms-cdk-makes.component";
|
||||||
import DmsCdkMakesRefetch from "../dms-cdk-makes/dms-cdk-makes.refetch.component";
|
import DmsCdkMakesRefetch from "../dms-cdk-makes/dms-cdk-makes.refetch.component";
|
||||||
|
import FormDatePicker from "../form-date-picker/form-date-picker.component";
|
||||||
import CurrencyInput from "../form-items-formatted/currency-form-item.component";
|
import CurrencyInput from "../form-items-formatted/currency-form-item.component";
|
||||||
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
|
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
|
||||||
import moment from "moment";
|
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
bodyshop: selectBodyshop,
|
bodyshop: selectBodyshop,
|
||||||
@@ -80,11 +80,25 @@ export function DmsPostForm({ bodyshop, socket, job, logsRef }) {
|
|||||||
layout="vertical"
|
layout="vertical"
|
||||||
onFinish={handleFinish}
|
onFinish={handleFinish}
|
||||||
initialValues={{
|
initialValues={{
|
||||||
story: t("jobs.labels.dms.defaultstory", {
|
story: `${t("jobs.labels.dms.defaultstory", {
|
||||||
ro_number: job.ro_number,
|
ro_number: job.ro_number,
|
||||||
area_of_damage:
|
ownr_nm: `${job.ownr_fn || ""} ${job.ownr_ln || ""} ${
|
||||||
(job.area_of_damage && job.area_of_damage.impact1) || "UNKNOWN",
|
job.ownr_co_nm || ""
|
||||||
}).substr(0, 239),
|
}`.trim(),
|
||||||
|
ins_co_nm: job.ins_co_nm || "N/A",
|
||||||
|
clm_po: `${job.clm_no ? `${job.clm_no} ` : ""}${
|
||||||
|
job.po_number || ""
|
||||||
|
}`,
|
||||||
|
}).trim()}.${
|
||||||
|
job.area_of_damage && job.area_of_damage.impact1
|
||||||
|
? " " +
|
||||||
|
t("jobs.labels.dms.damageto", {
|
||||||
|
area_of_damage:
|
||||||
|
(job.area_of_damage && job.area_of_damage.impact1) ||
|
||||||
|
"UNKNOWN",
|
||||||
|
})
|
||||||
|
: ""
|
||||||
|
}`.substr(0, 239),
|
||||||
inservicedate: moment("2019-01-01"),
|
inservicedate: moment("2019-01-01"),
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@@ -162,7 +176,7 @@ export function DmsPostForm({ bodyshop, socket, job, logsRef }) {
|
|||||||
name="inservicedate"
|
name="inservicedate"
|
||||||
label={t("jobs.fields.dms.inservicedate")}
|
label={t("jobs.fields.dms.inservicedate")}
|
||||||
>
|
>
|
||||||
<DatePicker format="MM/DD/YYYY" />
|
<FormDatePicker />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</LayoutFormRow>
|
</LayoutFormRow>
|
||||||
<Space>
|
<Space>
|
||||||
|
|||||||
@@ -1,14 +1,19 @@
|
|||||||
import { DatePicker } from "antd";
|
import { DatePicker } from "antd";
|
||||||
import moment from "moment";
|
import moment from "moment";
|
||||||
import React, { forwardRef } from "react";
|
import React, { useRef } from "react";
|
||||||
//To be used as a form element only.
|
//To be used as a form element only.
|
||||||
|
|
||||||
const dateFormat = "MM/DD/YYYY";
|
const dateFormat = "MM/DD/YYYY";
|
||||||
|
|
||||||
const FormDatePicker = (
|
export default function FormDatePicker({
|
||||||
{ value, onChange, onBlur, onlyFuture, ...restProps },
|
value,
|
||||||
ref
|
onChange,
|
||||||
) => {
|
onBlur,
|
||||||
|
onlyFuture,
|
||||||
|
...restProps
|
||||||
|
}) {
|
||||||
|
const ref = useRef();
|
||||||
|
|
||||||
const handleChange = (newDate) => {
|
const handleChange = (newDate) => {
|
||||||
if (value !== newDate && onChange) {
|
if (value !== newDate && onChange) {
|
||||||
onChange(newDate);
|
onChange(newDate);
|
||||||
@@ -19,17 +24,44 @@ const FormDatePicker = (
|
|||||||
if (e.key.toLowerCase() === "t") {
|
if (e.key.toLowerCase() === "t") {
|
||||||
if (onChange) {
|
if (onChange) {
|
||||||
onChange(new moment());
|
onChange(new moment());
|
||||||
|
// if (ref.current && ref.current.blur) ref.current.blur();
|
||||||
}
|
}
|
||||||
|
} else if (e.key.toLowerCase() === "enter") {
|
||||||
|
if (ref.current && ref.current.blur) ref.current.blur();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleBlur = (e) => {
|
||||||
|
const v = e.target.value;
|
||||||
|
if (!v) return;
|
||||||
|
|
||||||
|
const _a = moment(
|
||||||
|
v,
|
||||||
|
["MMDDYY", "MMDDYYYY", "MMDD", "MM/DD/YY"],
|
||||||
|
"en",
|
||||||
|
false
|
||||||
|
);
|
||||||
|
|
||||||
|
if (_a.isValid() && value && value.isValid && value.isValid()) {
|
||||||
|
_a.set({
|
||||||
|
hours: value.hours(),
|
||||||
|
minutes: value.minutes(),
|
||||||
|
seconds: value.seconds(),
|
||||||
|
milliseconds: value.milliseconds(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_a.isValid() && onChange) onChange(_a);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div onKeyDown={handleKeyDown}>
|
<div onKeyDown={handleKeyDown}>
|
||||||
<DatePicker
|
<DatePicker
|
||||||
|
ref={ref}
|
||||||
value={value ? moment(value) : null}
|
value={value ? moment(value) : null}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
format={dateFormat}
|
format={dateFormat}
|
||||||
onBlur={onBlur}
|
onBlur={onBlur || handleBlur}
|
||||||
disabledTime
|
disabledTime
|
||||||
{...(onlyFuture && {
|
{...(onlyFuture && {
|
||||||
disabledDate: (d) => moment().subtract(1, "day").isAfter(d),
|
disabledDate: (d) => moment().subtract(1, "day").isAfter(d),
|
||||||
@@ -38,6 +70,4 @@ const FormDatePicker = (
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
}
|
||||||
|
|
||||||
export default forwardRef(FormDatePicker);
|
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ export default function GlobalSearch() {
|
|||||||
<Link to={`/manage/jobs/${job.id}`}>
|
<Link to={`/manage/jobs/${job.id}`}>
|
||||||
<Space size="small" split={<Divider type="vertical" />}>
|
<Space size="small" split={<Divider type="vertical" />}>
|
||||||
<strong>{job.ro_number || t("general.labels.na")}</strong>
|
<strong>{job.ro_number || t("general.labels.na")}</strong>
|
||||||
|
<span>{`${job.status || ""}`}</span>
|
||||||
<span>{`${job.ownr_fn || ""} ${job.ownr_ln || ""} ${
|
<span>{`${job.ownr_fn || ""} ${job.ownr_ln || ""} ${
|
||||||
job.ownr_co_nm || ""
|
job.ownr_co_nm || ""
|
||||||
}`}</span>
|
}`}</span>
|
||||||
|
|||||||
@@ -105,6 +105,10 @@ export function JobChecklistForm({
|
|||||||
completed_at: new Date(),
|
completed_at: new Date(),
|
||||||
},
|
},
|
||||||
|
|
||||||
|
...(type === "intake" &&
|
||||||
|
values.scheduled_delivery && {
|
||||||
|
scheduled_delivery: values.scheduled_delivery,
|
||||||
|
}),
|
||||||
...(type === "deliver" && {
|
...(type === "deliver" && {
|
||||||
scheduled_delivery: values.scheduled_delivery,
|
scheduled_delivery: values.scheduled_delivery,
|
||||||
actual_delivery: values.actual_delivery,
|
actual_delivery: values.actual_delivery,
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
import { useMutation } from "@apollo/client";
|
import { useMutation } from "@apollo/client";
|
||||||
import { Button, Form, notification, DatePicker } from "antd";
|
import { Button, Form, notification } from "antd";
|
||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { UPDATE_JOB } from "../../graphql/jobs.queries";
|
import { UPDATE_JOB } from "../../graphql/jobs.queries";
|
||||||
import DateTimePicker from "../form-date-time-picker/form-date-time-picker.component";
|
import DateTimePicker from "../form-date-time-picker/form-date-time-picker.component";
|
||||||
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
|
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
|
||||||
import moment from "moment";
|
import moment from "moment";
|
||||||
|
import FormDatePicker from "../form-date-picker/form-date-picker.component";
|
||||||
|
|
||||||
export default function JobsAdminDatesChange({ job }) {
|
export default function JobsAdminDatesChange({ job }) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
@@ -54,7 +56,7 @@ export default function JobsAdminDatesChange({ job }) {
|
|||||||
label={t("jobs.fields.date_estimated")}
|
label={t("jobs.fields.date_estimated")}
|
||||||
name="date_estimated"
|
name="date_estimated"
|
||||||
>
|
>
|
||||||
<DatePicker format="MM/DD/YYYY" />
|
<FormDatePicker format="MM/DD/YYYY" />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item label={t("jobs.fields.date_open")} name="date_open">
|
<Form.Item label={t("jobs.fields.date_open")} name="date_open">
|
||||||
<DateTimePicker />
|
<DateTimePicker />
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { DatePicker, Form, Statistic, Tooltip } from "antd";
|
import { Form, Statistic, Tooltip } from "antd";
|
||||||
import React, { useMemo } from "react";
|
import React, { useMemo } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
@@ -34,7 +34,7 @@ export function JobsDetailDatesComponent({ jobRO, job, bodyshop }) {
|
|||||||
label={t("jobs.fields.date_estimated")}
|
label={t("jobs.fields.date_estimated")}
|
||||||
name="date_estimated"
|
name="date_estimated"
|
||||||
>
|
>
|
||||||
<DatePicker disabled={jobRO} format="MM/DD/YYYY" />
|
<FormDatePicker disabled={jobRO} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item label={t("jobs.fields.date_open")} name="date_open">
|
<Form.Item label={t("jobs.fields.date_open")} name="date_open">
|
||||||
<DateTimePicker disabled={jobRO} />
|
<DateTimePicker disabled={jobRO} />
|
||||||
|
|||||||
@@ -91,68 +91,76 @@ export function PartsOrderModalComponent({
|
|||||||
<div>
|
<div>
|
||||||
{fields.map((field, index) => (
|
{fields.map((field, index) => (
|
||||||
<Form.Item required={false} key={field.key}>
|
<Form.Item required={false} key={field.key}>
|
||||||
<LayoutFormRow grow noDivider>
|
<div style={{ display: "flex" }}>
|
||||||
<Form.Item
|
<LayoutFormRow grow noDivider style={{ flex: 1 }}>
|
||||||
//span={8}
|
|
||||||
label={t("parts_orders.fields.line_desc")}
|
|
||||||
key={`${index}line_desc`}
|
|
||||||
name={[field.name, "line_desc"]}
|
|
||||||
rules={[
|
|
||||||
{
|
|
||||||
required: true,
|
|
||||||
//message: t("general.validation.required"),
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
<Input />
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
|
||||||
label={t("parts_orders.fields.line_remarks")}
|
|
||||||
key={`${index}line_remarks`}
|
|
||||||
name={[field.name, "line_remarks"]}
|
|
||||||
>
|
|
||||||
<Input />
|
|
||||||
</Form.Item>
|
|
||||||
{
|
|
||||||
// <Form.Item
|
|
||||||
// label={t("parts_orders.fields.db_price")}
|
|
||||||
// key={`${index}db_price`}
|
|
||||||
// name={[field.name, "db_price"]}
|
|
||||||
// >
|
|
||||||
// <CurrencyInput />
|
|
||||||
// </Form.Item>
|
|
||||||
}
|
|
||||||
<Form.Item
|
|
||||||
label={t("parts_orders.fields.quantity")}
|
|
||||||
key={`${index}quantity`}
|
|
||||||
name={[field.name, "quantity"]}
|
|
||||||
rules={[
|
|
||||||
{
|
|
||||||
required: true,
|
|
||||||
//message: t("general.validation.required"),
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
<InputNumber />
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
|
||||||
label={t("parts_orders.fields.act_price")}
|
|
||||||
key={`${index}act_price`}
|
|
||||||
name={[field.name, "act_price"]}
|
|
||||||
>
|
|
||||||
<CurrencyInput />
|
|
||||||
</Form.Item>
|
|
||||||
{isReturn && (
|
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t("parts_orders.fields.cost")}
|
//span={8}
|
||||||
key={`${index}cost`}
|
label={t("parts_orders.fields.line_desc")}
|
||||||
name={[field.name, "cost"]}
|
key={`${index}line_desc`}
|
||||||
|
name={[field.name, "line_desc"]}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<Input />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("parts_orders.fields.line_remarks")}
|
||||||
|
key={`${index}line_remarks`}
|
||||||
|
name={[field.name, "line_remarks"]}
|
||||||
|
>
|
||||||
|
<Input />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("parts_orders.fields.oem_partno")}
|
||||||
|
key={`${index}oem_partno`}
|
||||||
|
name={[field.name, "oem_partno"]}
|
||||||
|
>
|
||||||
|
<Input />
|
||||||
|
</Form.Item>
|
||||||
|
{
|
||||||
|
// <Form.Item
|
||||||
|
// label={t("parts_orders.fields.db_price")}
|
||||||
|
// key={`${index}db_price`}
|
||||||
|
// name={[field.name, "db_price"]}
|
||||||
|
// >
|
||||||
|
// <CurrencyInput />
|
||||||
|
// </Form.Item>
|
||||||
|
}
|
||||||
|
<Form.Item
|
||||||
|
label={t("parts_orders.fields.quantity")}
|
||||||
|
key={`${index}quantity`}
|
||||||
|
name={[field.name, "quantity"]}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<InputNumber />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("parts_orders.fields.act_price")}
|
||||||
|
key={`${index}act_price`}
|
||||||
|
name={[field.name, "act_price"]}
|
||||||
>
|
>
|
||||||
<CurrencyInput />
|
<CurrencyInput />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
)}
|
{isReturn && (
|
||||||
|
<Form.Item
|
||||||
<Space wrap align="center">
|
label={t("parts_orders.fields.cost")}
|
||||||
|
key={`${index}cost`}
|
||||||
|
name={[field.name, "cost"]}
|
||||||
|
>
|
||||||
|
<CurrencyInput />
|
||||||
|
</Form.Item>
|
||||||
|
)}
|
||||||
|
</LayoutFormRow>
|
||||||
|
<Space wrap size="small" align="center">
|
||||||
<div>
|
<div>
|
||||||
<DeleteFilled
|
<DeleteFilled
|
||||||
style={{ margin: "1rem" }}
|
style={{ margin: "1rem" }}
|
||||||
@@ -167,7 +175,7 @@ export function PartsOrderModalComponent({
|
|||||||
total={fields.length}
|
total={fields.length}
|
||||||
/>
|
/>
|
||||||
</Space>
|
</Space>
|
||||||
</LayoutFormRow>
|
</div>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { useApolloClient } from "@apollo/client";
|
|||||||
import Board, { moveCard } from "@asseinfo/react-kanban";
|
import Board, { moveCard } from "@asseinfo/react-kanban";
|
||||||
//import "@asseinfo/react-kanban/dist/styles.css";
|
//import "@asseinfo/react-kanban/dist/styles.css";
|
||||||
import "./production-board-kanban.styles.scss";
|
import "./production-board-kanban.styles.scss";
|
||||||
import { SyncOutlined } from '@ant-design/icons'
|
import { SyncOutlined } from "@ant-design/icons";
|
||||||
import { Grid, notification, Button, PageHeader, Space, Statistic } from "antd";
|
import { Grid, notification, Button, PageHeader, Space, Statistic } from "antd";
|
||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
@@ -49,9 +49,16 @@ export function ProductionBoardKanbanComponent({
|
|||||||
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setBoardLanes(
|
const boardData = createBoardData(
|
||||||
createBoardData(bodyshop.md_ro_statuses.production_statuses, data, filter)
|
bodyshop.md_ro_statuses.production_statuses,
|
||||||
|
data,
|
||||||
|
filter
|
||||||
);
|
);
|
||||||
|
|
||||||
|
boardData.columns = boardData.columns.map((d) => {
|
||||||
|
return { ...d, title: `${d.title} (${d.cards.length})` };
|
||||||
|
});
|
||||||
|
setBoardLanes(boardData);
|
||||||
setIsMoving(false);
|
setIsMoving(false);
|
||||||
}, [
|
}, [
|
||||||
data,
|
data,
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
import { useMutation } from "@apollo/client";
|
import { useMutation } from "@apollo/client";
|
||||||
import { DatePicker, Dropdown, TimePicker, Button, Card } from "antd";
|
import { Button, Card, Dropdown, TimePicker } from "antd";
|
||||||
import moment from "moment";
|
import moment from "moment";
|
||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
import { logImEXEvent } from "../../firebase/firebase.utils";
|
import { logImEXEvent } from "../../firebase/firebase.utils";
|
||||||
import { UPDATE_JOB } from "../../graphql/jobs.queries";
|
import { UPDATE_JOB } from "../../graphql/jobs.queries";
|
||||||
import { DateFormatter } from "../../utils/DateFormatter";
|
import { DateFormatter } from "../../utils/DateFormatter";
|
||||||
|
import FormDatePicker from "../form-date-picker/form-date-picker.component";
|
||||||
import { useTranslation } from "react-i18next";
|
|
||||||
|
|
||||||
export default function ProductionListDate({
|
export default function ProductionListDate({
|
||||||
record,
|
record,
|
||||||
@@ -17,8 +17,12 @@ export default function ProductionListDate({
|
|||||||
const [updateAlert] = useMutation(UPDATE_JOB);
|
const [updateAlert] = useMutation(UPDATE_JOB);
|
||||||
const [visible, setVisible] = useState(false);
|
const [visible, setVisible] = useState(false);
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const handleChange = (date) => {
|
const handleChange = (date) => {
|
||||||
logImEXEvent("product_toggle_date", { field });
|
logImEXEvent("product_toggle_date", { field });
|
||||||
|
if (date.isSame(record[field] && moment(record[field]))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
//e.stopPropagation();
|
//e.stopPropagation();
|
||||||
updateAlert({
|
updateAlert({
|
||||||
@@ -58,7 +62,7 @@ export default function ProductionListDate({
|
|||||||
style={{ padding: "1rem" }}
|
style={{ padding: "1rem" }}
|
||||||
onClick={(e) => e.stopPropagation()}
|
onClick={(e) => e.stopPropagation()}
|
||||||
>
|
>
|
||||||
<DatePicker
|
<FormDatePicker
|
||||||
onClick={(e) => e.stopPropagation()}
|
onClick={(e) => e.stopPropagation()}
|
||||||
value={(record[field] && moment(record[field])) || null}
|
value={(record[field] && moment(record[field])) || null}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
|
|||||||
@@ -8,7 +8,8 @@ import { connect } from "react-redux";
|
|||||||
import { createStructuredSelector } from "reselect";
|
import { createStructuredSelector } from "reselect";
|
||||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||||
const ProdTemplates = TemplateList("production");
|
const ProdTemplates = TemplateList("production");
|
||||||
const ProductionByTech = TemplateList("special").production_by_technician_one;
|
const { production_by_technician_one, production_by_category_one } =
|
||||||
|
TemplateList("special");
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
bodyshop: selectBodyshop,
|
bodyshop: selectBodyshop,
|
||||||
@@ -58,7 +59,7 @@ export function ProductionListPrint({ bodyshop }) {
|
|||||||
setLoading(true);
|
setLoading(true);
|
||||||
await GenerateDocument(
|
await GenerateDocument(
|
||||||
{
|
{
|
||||||
name: ProductionByTech.key,
|
name: production_by_technician_one.key,
|
||||||
variables: { id: e.id },
|
variables: { id: e.id },
|
||||||
},
|
},
|
||||||
{},
|
{},
|
||||||
@@ -71,6 +72,29 @@ export function ProductionListPrint({ bodyshop }) {
|
|||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
))}
|
))}
|
||||||
</Menu.SubMenu>
|
</Menu.SubMenu>
|
||||||
|
<Menu.SubMenu
|
||||||
|
title={t("reportcenter.templates.production_by_category_one")}
|
||||||
|
>
|
||||||
|
{bodyshop.md_categories.map((e) => (
|
||||||
|
<Menu.Item
|
||||||
|
key={e}
|
||||||
|
onClick={async () => {
|
||||||
|
setLoading(true);
|
||||||
|
await GenerateDocument(
|
||||||
|
{
|
||||||
|
name: production_by_category_one.key,
|
||||||
|
variables: { category: e },
|
||||||
|
},
|
||||||
|
{},
|
||||||
|
"p"
|
||||||
|
);
|
||||||
|
setLoading(false);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{e}
|
||||||
|
</Menu.Item>
|
||||||
|
))}
|
||||||
|
</Menu.SubMenu>
|
||||||
</Menu>
|
</Menu>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -32,7 +32,9 @@ export default function VendorsFormComponent({
|
|||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<PageHeader
|
<PageHeader
|
||||||
title={form.getFieldValue("name")}
|
title={
|
||||||
|
<Form.Item shouldUpdate>{() => form.getFieldValue("name")}</Form.Item>
|
||||||
|
}
|
||||||
extra={
|
extra={
|
||||||
<Space>
|
<Space>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
|
|||||||
@@ -181,7 +181,10 @@ export const UPDATE_JOB_LINE = gql`
|
|||||||
|
|
||||||
export const GET_JOB_LINES_TO_ENTER_BILL = gql`
|
export const GET_JOB_LINES_TO_ENTER_BILL = gql`
|
||||||
query GET_JOB_LINES_TO_ENTER_BILL($id: uuid!) {
|
query GET_JOB_LINES_TO_ENTER_BILL($id: uuid!) {
|
||||||
joblines(where: { jobid: { _eq: $id } }) {
|
joblines(
|
||||||
|
where: { jobid: { _eq: $id } }
|
||||||
|
order_by: { act_price: desc_nulls_last }
|
||||||
|
) {
|
||||||
removed
|
removed
|
||||||
id
|
id
|
||||||
line_desc
|
line_desc
|
||||||
|
|||||||
@@ -5,14 +5,14 @@ export const GLOBAL_SEARCH_QUERY = gql`
|
|||||||
search_jobs(args: { search: $search }) {
|
search_jobs(args: { search: $search }) {
|
||||||
id
|
id
|
||||||
ro_number
|
ro_number
|
||||||
|
status
|
||||||
|
|
||||||
clm_total
|
|
||||||
clm_no
|
clm_no
|
||||||
v_model_yr
|
v_model_yr
|
||||||
v_model_desc
|
v_model_desc
|
||||||
v_make_desc
|
v_make_desc
|
||||||
v_color
|
v_color
|
||||||
plate_no
|
|
||||||
ownr_fn
|
ownr_fn
|
||||||
ownr_ln
|
ownr_ln
|
||||||
ownr_co_nm
|
ownr_co_nm
|
||||||
|
|||||||
@@ -1506,7 +1506,8 @@
|
|||||||
"difference": "Difference",
|
"difference": "Difference",
|
||||||
"diskscan": "Scan Disk for Estimates",
|
"diskscan": "Scan Disk for Estimates",
|
||||||
"dms": {
|
"dms": {
|
||||||
"defaultstory": "Bodyshop RO {{ro_number}}. Damage to $t(jobs.fields.area_of_damage_impact.{{area_of_damage}}).",
|
"damageto": "Damage to $t(jobs.fields.area_of_damage_impact.{{area_of_damage}}).",
|
||||||
|
"defaultstory": "B/S RO: {{ro_number}}. Owner: {{ownr_nm}}. Insurance Co: {{ins_co_nm}}. Claim/PO #: {{clm_po}}",
|
||||||
"invoicedatefuture": "Invoice date must be today or in the future for CDK posting.",
|
"invoicedatefuture": "Invoice date must be today or in the future for CDK posting.",
|
||||||
"kmoutnotgreaterthankmin": "Mileage out must be greater than mileage in.",
|
"kmoutnotgreaterthankmin": "Mileage out must be greater than mileage in.",
|
||||||
"logs": "Logs",
|
"logs": "Logs",
|
||||||
@@ -2220,6 +2221,7 @@
|
|||||||
"attendance_summary": "Attendance Summary (All Employees)",
|
"attendance_summary": "Attendance Summary (All Employees)",
|
||||||
"credits_not_received_date": "Credits not Received by Date",
|
"credits_not_received_date": "Credits not Received by Date",
|
||||||
"csi": "CSI Responses",
|
"csi": "CSI Responses",
|
||||||
|
"estimates_written_converted": "Estimates Written/Converted",
|
||||||
"estimator_detail": "Jobs by Estimator (Detail)",
|
"estimator_detail": "Jobs by Estimator (Detail)",
|
||||||
"estimator_summary": "Jobs by Estimator (Summary)",
|
"estimator_summary": "Jobs by Estimator (Summary)",
|
||||||
"export_payables": "Export Log - Payables",
|
"export_payables": "Export Log - Payables",
|
||||||
@@ -2262,6 +2264,7 @@
|
|||||||
"payments_by_date": "Payments by Date",
|
"payments_by_date": "Payments by Date",
|
||||||
"payments_by_date_type": "Payments by Date and Type",
|
"payments_by_date_type": "Payments by Date and Type",
|
||||||
"production_by_category": "Production by Category",
|
"production_by_category": "Production by Category",
|
||||||
|
"production_by_category_one": "Production filtered by Category",
|
||||||
"production_by_csr": "Production by CSR",
|
"production_by_csr": "Production by CSR",
|
||||||
"production_by_last_name": "Production by Last Name",
|
"production_by_last_name": "Production by Last Name",
|
||||||
"production_by_repair_status": "Production by Status",
|
"production_by_repair_status": "Production by Status",
|
||||||
|
|||||||
@@ -1506,6 +1506,7 @@
|
|||||||
"difference": "",
|
"difference": "",
|
||||||
"diskscan": "",
|
"diskscan": "",
|
||||||
"dms": {
|
"dms": {
|
||||||
|
"damageto": "",
|
||||||
"defaultstory": "",
|
"defaultstory": "",
|
||||||
"invoicedatefuture": "",
|
"invoicedatefuture": "",
|
||||||
"kmoutnotgreaterthankmin": "",
|
"kmoutnotgreaterthankmin": "",
|
||||||
@@ -2220,6 +2221,7 @@
|
|||||||
"attendance_summary": "",
|
"attendance_summary": "",
|
||||||
"credits_not_received_date": "",
|
"credits_not_received_date": "",
|
||||||
"csi": "",
|
"csi": "",
|
||||||
|
"estimates_written_converted": "",
|
||||||
"estimator_detail": "",
|
"estimator_detail": "",
|
||||||
"estimator_summary": "",
|
"estimator_summary": "",
|
||||||
"export_payables": "",
|
"export_payables": "",
|
||||||
@@ -2262,6 +2264,7 @@
|
|||||||
"payments_by_date": "",
|
"payments_by_date": "",
|
||||||
"payments_by_date_type": "",
|
"payments_by_date_type": "",
|
||||||
"production_by_category": "",
|
"production_by_category": "",
|
||||||
|
"production_by_category_one": "",
|
||||||
"production_by_csr": "",
|
"production_by_csr": "",
|
||||||
"production_by_last_name": "",
|
"production_by_last_name": "",
|
||||||
"production_by_repair_status": "",
|
"production_by_repair_status": "",
|
||||||
|
|||||||
@@ -1506,6 +1506,7 @@
|
|||||||
"difference": "",
|
"difference": "",
|
||||||
"diskscan": "",
|
"diskscan": "",
|
||||||
"dms": {
|
"dms": {
|
||||||
|
"damageto": "",
|
||||||
"defaultstory": "",
|
"defaultstory": "",
|
||||||
"invoicedatefuture": "",
|
"invoicedatefuture": "",
|
||||||
"kmoutnotgreaterthankmin": "",
|
"kmoutnotgreaterthankmin": "",
|
||||||
@@ -2220,6 +2221,7 @@
|
|||||||
"attendance_summary": "",
|
"attendance_summary": "",
|
||||||
"credits_not_received_date": "",
|
"credits_not_received_date": "",
|
||||||
"csi": "",
|
"csi": "",
|
||||||
|
"estimates_written_converted": "",
|
||||||
"estimator_detail": "",
|
"estimator_detail": "",
|
||||||
"estimator_summary": "",
|
"estimator_summary": "",
|
||||||
"export_payables": "",
|
"export_payables": "",
|
||||||
@@ -2262,6 +2264,7 @@
|
|||||||
"payments_by_date": "",
|
"payments_by_date": "",
|
||||||
"payments_by_date_type": "",
|
"payments_by_date_type": "",
|
||||||
"production_by_category": "",
|
"production_by_category": "",
|
||||||
|
"production_by_category_one": "",
|
||||||
"production_by_csr": "",
|
"production_by_csr": "",
|
||||||
"production_by_last_name": "",
|
"production_by_last_name": "",
|
||||||
"production_by_repair_status": "",
|
"production_by_repair_status": "",
|
||||||
|
|||||||
@@ -1466,6 +1466,24 @@ export const TemplateList = (type, context) => {
|
|||||||
},
|
},
|
||||||
group: "customers",
|
group: "customers",
|
||||||
},
|
},
|
||||||
|
estimates_written_converted: {
|
||||||
|
title: i18n.t("reportcenter.templates.estimates_written_converted"),
|
||||||
|
description: "",
|
||||||
|
subject: i18n.t(
|
||||||
|
"reportcenter.templates.estimates_written_converted"
|
||||||
|
),
|
||||||
|
key: "estimates_written_converted",
|
||||||
|
//idtype: "vendor",
|
||||||
|
disabled: false,
|
||||||
|
rangeFilter: {
|
||||||
|
object: i18n.t("reportcenter.labels.objects.jobs"),
|
||||||
|
field:
|
||||||
|
i18n.t("jobs.fields.date_open") +
|
||||||
|
"/" +
|
||||||
|
i18n.t("jobs.fields.date_invoiced"), // Also date invoice.
|
||||||
|
},
|
||||||
|
group: "sales",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
: {}),
|
: {}),
|
||||||
...(!type || type === "courtesycarcontract"
|
...(!type || type === "courtesycarcontract"
|
||||||
@@ -1629,6 +1647,16 @@ export const TemplateList = (type, context) => {
|
|||||||
//idtype: "vendor",
|
//idtype: "vendor",
|
||||||
disabled: false,
|
disabled: false,
|
||||||
},
|
},
|
||||||
|
production_by_category_one: {
|
||||||
|
title: i18n.t("reportcenter.templates.production_by_category_one"),
|
||||||
|
description: "",
|
||||||
|
subject: i18n.t(
|
||||||
|
"reportcenter.templates.production_by_category_one"
|
||||||
|
),
|
||||||
|
key: "production_by_category_one",
|
||||||
|
//idtype: "vendor",
|
||||||
|
disabled: false,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
: {}),
|
: {}),
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -76,9 +76,7 @@ const generateBill = (bill) => {
|
|||||||
DueDate:
|
DueDate:
|
||||||
bill.due_date && moment(bill.due_date).format("YYYY-MM-DD"),
|
bill.due_date && moment(bill.due_date).format("YYYY-MM-DD"),
|
||||||
RefNumber: bill.invoice_number,
|
RefNumber: bill.invoice_number,
|
||||||
Memo: `RO ${bill.job.ro_number || ""} OWNER ${
|
Memo: `RO ${bill.job.ro_number || ""}`,
|
||||||
bill.job.ownr_fn || ""
|
|
||||||
} ${bill.job.ownr_ln || ""} ${bill.job.ownr_co_nm || ""}`,
|
|
||||||
ExpenseLineAdd: bill.billlines.map((il) =>
|
ExpenseLineAdd: bill.billlines.map((il) =>
|
||||||
generateBillLine(
|
generateBillLine(
|
||||||
il,
|
il,
|
||||||
|
|||||||
@@ -13,13 +13,20 @@ const logger = require("../utils/logger");
|
|||||||
const client = twilio(
|
const client = twilio(
|
||||||
process.env.TWILIO_AUTH_TOKEN,
|
process.env.TWILIO_AUTH_TOKEN,
|
||||||
process.env.TWILIO_AUTH_KEY
|
process.env.TWILIO_AUTH_KEY
|
||||||
);const { admin } = require("../firebase/firebase-handler");
|
);
|
||||||
|
const { admin } = require("../firebase/firebase-handler");
|
||||||
|
|
||||||
const gqlClient = require("../graphql-client/graphql-client").client;
|
const gqlClient = require("../graphql-client/graphql-client").client;
|
||||||
|
|
||||||
exports.send = (req, res) => {
|
exports.send = (req, res) => {
|
||||||
const { to, messagingServiceSid, body, conversationid, selectedMedia, imexshopid } =
|
const {
|
||||||
req.body;
|
to,
|
||||||
|
messagingServiceSid,
|
||||||
|
body,
|
||||||
|
conversationid,
|
||||||
|
selectedMedia,
|
||||||
|
imexshopid,
|
||||||
|
} = req.body;
|
||||||
|
|
||||||
logger.log("sms-outbound", "DEBUG", req.user.email, null, {
|
logger.log("sms-outbound", "DEBUG", req.user.email, null, {
|
||||||
messagingServiceSid: messagingServiceSid,
|
messagingServiceSid: messagingServiceSid,
|
||||||
@@ -34,7 +41,12 @@ exports.send = (req, res) => {
|
|||||||
req.body.selectedMedia.length > 0 ? selectedMedia.map((i) => i.src) : [],
|
req.body.selectedMedia.length > 0 ? selectedMedia.map((i) => i.src) : [],
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!!to && !!messagingServiceSid && !!body && !!conversationid) {
|
if (
|
||||||
|
!!to &&
|
||||||
|
!!messagingServiceSid &&
|
||||||
|
(!!body || !!selectedMedia.length > 0) &&
|
||||||
|
!!conversationid
|
||||||
|
) {
|
||||||
client.messages
|
client.messages
|
||||||
.create({
|
.create({
|
||||||
body: body,
|
body: body,
|
||||||
@@ -67,10 +79,9 @@ exports.send = (req, res) => {
|
|||||||
const data = {
|
const data = {
|
||||||
type: "messaging-outbound",
|
type: "messaging-outbound",
|
||||||
conversationid: newMessage.conversationid || "",
|
conversationid: newMessage.conversationid || "",
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
admin.messaging().send({
|
admin.messaging().send({
|
||||||
topic: `${imexshopid}-messaging`,
|
topic: `${imexshopid}-messaging`,
|
||||||
data,
|
data,
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user