Merged in release/2023-05-26 (pull request #815)

Release/2023 05 26
This commit is contained in:
Patrick Fic
2023-05-26 18:27:21 +00:00
30 changed files with 603 additions and 155 deletions

View File

@@ -51,6 +51,7 @@
"react-i18next": "^12.2.0", "react-i18next": "^12.2.0",
"react-icons": "^4.7.1", "react-icons": "^4.7.1",
"react-image-lightbox": "^5.1.4", "react-image-lightbox": "^5.1.4",
"react-intersection-observer": "^9.4.3",
"react-number-format": "^5.1.3", "react-number-format": "^5.1.3",
"react-redux": "^8.0.5", "react-redux": "^8.0.5",
"react-resizable": "^3.0.4", "react-resizable": "^3.0.4",

View File

@@ -1,5 +1,5 @@
import { Badge, List, Tag } from "antd"; import { Badge, List, Tag } from "antd";
import React from "react"; import React, { useEffect } from "react";
import { connect } from "react-redux"; import { connect } from "react-redux";
import { createStructuredSelector } from "reselect"; import { createStructuredSelector } from "reselect";
import { setSelectedConversation } from "../../redux/messaging/messaging.actions"; import { setSelectedConversation } from "../../redux/messaging/messaging.actions";
@@ -7,6 +7,8 @@ import { selectSelectedConversation } from "../../redux/messaging/messaging.sele
import { TimeAgoFormatter } from "../../utils/DateFormatter"; import { TimeAgoFormatter } from "../../utils/DateFormatter";
import PhoneFormatter from "../../utils/PhoneFormatter"; import PhoneFormatter from "../../utils/PhoneFormatter";
import OwnerNameDisplay from "../owner-name-display/owner-name-display.component"; import OwnerNameDisplay from "../owner-name-display/owner-name-display.component";
import { List as VirtualizedList, AutoSizer } from "react-virtualized";
import "./chat-conversation-list.styles.scss"; import "./chat-conversation-list.styles.scss";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
@@ -18,59 +20,86 @@ const mapDispatchToProps = (dispatch) => ({
dispatch(setSelectedConversation(conversationId)), dispatch(setSelectedConversation(conversationId)),
}); });
export function ChatConversationListComponent({ function ChatConversationListComponent({
conversationList, conversationList,
selectedConversation, selectedConversation,
setSelectedConversation, setSelectedConversation,
subscribeToMoreConversations,
loadMoreConversations,
}) { }) {
useEffect(
() => subscribeToMoreConversations(),
// eslint-disable-next-line react-hooks/exhaustive-deps
[]
);
const rowRenderer = ({ index, key, style }) => {
const item = conversationList[index];
return (
<List.Item
key={key}
onClick={() => setSelectedConversation(item.id)}
className={`chat-list-item ${
item.id === selectedConversation
? "chat-list-selected-conversation"
: null
}`}
style={style}
>
<div sryle={{ display: "inline-block" }}>
{item.label && <div className="chat-name">{item.label}</div>}
{item.job_conversations.length > 0 ? (
<div className="chat-name">
{item.job_conversations.map((j, idx) => (
<div key={idx}>
<OwnerNameDisplay ownerObject={j.job} />
</div>
))}
</div>
) : (
<PhoneFormatter>{item.phone_num}</PhoneFormatter>
)}
</div>
<div sryle={{ display: "inline-block" }}>
<div>
{item.job_conversations.length > 0
? item.job_conversations.map((j, idx) => (
<Tag key={idx} className="ro-number-tag">
{j.job.ro_number}
</Tag>
))
: null}
</div>
<TimeAgoFormatter>{item.updated_at}</TimeAgoFormatter>
</div>
<Badge count={item.messages_aggregate.aggregate.count || 0} />
</List.Item>
);
};
return ( return (
<div className="chat-list-container"> <div className="chat-list-container">
<List <AutoSizer>
bordered {({ height, width }) => (
dataSource={conversationList} <VirtualizedList
renderItem={(item) => ( height={height}
<List.Item width={width}
key={item.id} rowCount={conversationList.length}
onClick={() => setSelectedConversation(item.id)} rowHeight={60}
className={`chat-list-item ${ rowRenderer={rowRenderer}
item.id === selectedConversation onScroll={({ scrollTop, scrollHeight, clientHeight }) => {
? "chat-list-selected-conversation" if (scrollTop + clientHeight === scrollHeight) {
: null loadMoreConversations();
}`} }
> }}
<div sryle={{ display: "inline-block" }}> />
{item.label && <div className="chat-name">{item.label}</div>}
{item.job_conversations.length > 0 ? (
<div className="chat-name">
{item.job_conversations.map((j, idx) => (
<div key={idx}>
<OwnerNameDisplay ownerObject={j.job} />
</div>
))}
</div>
) : (
<PhoneFormatter>{item.phone_num}</PhoneFormatter>
)}
</div>
<div sryle={{ display: "inline-block" }}>
<div>
{item.job_conversations.length > 0
? item.job_conversations.map((j, idx) => (
<Tag key={idx} className="ro-number-tag">
{j.job.ro_number}
</Tag>
))
: null}
</div>
<TimeAgoFormatter>{item.updated_at}</TimeAgoFormatter>
</div>
<Badge count={item.messages_aggregate.aggregate.count || 0} />
</List.Item>
)} )}
/> </AutoSizer>
</div> </div>
); );
} }
export default connect( export default connect(
mapStateToProps, mapStateToProps,
mapDispatchToProps mapDispatchToProps

View File

@@ -3,8 +3,9 @@
} }
.chat-list-container { .chat-list-container {
flex: 1; flex: 1;
overflow: auto; overflow: hidden;
height: 100%; height: 100%;
border: 1px solid gainsboro;
} }
.chat-list-item { .chat-list-item {
@@ -21,4 +22,6 @@
.ro-number-tag { .ro-number-tag {
align-self: baseline; align-self: baseline;
} }
padding: 12px 24px;
border-bottom: 1px solid gainsboro;
} }

View File

@@ -4,15 +4,16 @@ import {
ShrinkOutlined, ShrinkOutlined,
SyncOutlined, SyncOutlined,
} from "@ant-design/icons"; } from "@ant-design/icons";
import { useQuery } from "@apollo/client"; import { useLazyQuery, useSubscription } from "@apollo/client";
import { Badge, Card, Col, Row, Space, Tag, Tooltip, Typography } from "antd"; import { Badge, Card, Col, Row, Space, Tag, Tooltip, Typography } from "antd";
import React, { useEffect, useState } from "react"; import React, { useCallback, useEffect, useState } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { connect } from "react-redux"; import { connect } from "react-redux";
import { createStructuredSelector } from "reselect"; import { createStructuredSelector } from "reselect";
import { import {
CONVERSATION_LIST_QUERY, CONVERSATION_LIST_QUERY,
UNREAD_CONVERSATION_COUNT, CONVERSATION_LIST_SUBSCRIPTION,
UNREAD_CONVERSATION_COUNT_SUBSCRIPTION,
} from "../../graphql/conversations.queries"; } from "../../graphql/conversations.queries";
import { toggleChatVisible } from "../../redux/messaging/messaging.actions"; import { toggleChatVisible } from "../../redux/messaging/messaging.actions";
import { import {
@@ -41,17 +42,17 @@ export function ChatPopupComponent({
const { t } = useTranslation(); const { t } = useTranslation();
const [pollInterval, setpollInterval] = useState(0); const [pollInterval, setpollInterval] = useState(0);
const { data: unreadData } = useQuery(UNREAD_CONVERSATION_COUNT, { const { data: unreadData } = useSubscription(
fetchPolicy: "network-only", UNREAD_CONVERSATION_COUNT_SUBSCRIPTION
nextFetchPolicy: "network-only", );
...(pollInterval > 0 ? { pollInterval } : {}),
});
const { loading, data, refetch, called } = useQuery(CONVERSATION_LIST_QUERY, { const [
getConversations,
{ loading, data, called, refetch, fetchMore, subscribeToMore },
] = useLazyQuery(CONVERSATION_LIST_QUERY, {
fetchPolicy: "network-only", fetchPolicy: "network-only",
nextFetchPolicy: "network-only", nextFetchPolicy: "network-only",
skip: !chatVisible, skip: !chatVisible,
...(pollInterval > 0 ? { pollInterval } : {}),
}); });
const fcmToken = sessionStorage.getItem("fcmtoken"); const fcmToken = sessionStorage.getItem("fcmtoken");
@@ -65,15 +66,22 @@ export function ChatPopupComponent({
}, [fcmToken]); }, [fcmToken]);
useEffect(() => { useEffect(() => {
if (called && chatVisible) refetch(); if (called && chatVisible)
}, [chatVisible, called, refetch]); getConversations({
variables: {
offset: 0,
},
});
}, [chatVisible, called, getConversations]);
// const unreadCount = data const loadMoreConversations = useCallback(() => {
// ? data.conversations.reduce( if (data)
// (acc, val) => val.messages_aggregate.aggregate.count + acc, fetchMore({
// 0 variables: {
// ) offset: data.conversations.length,
// : 0; },
});
}, [data, fetchMore]);
const unreadCount = unreadData?.messages_aggregate.aggregate.count || 0; const unreadCount = unreadData?.messages_aggregate.aggregate.count || 0;
@@ -110,6 +118,44 @@ export function ChatPopupComponent({
) : ( ) : (
<ChatConversationListComponent <ChatConversationListComponent
conversationList={data ? data.conversations : []} conversationList={data ? data.conversations : []}
loadMoreConversations={loadMoreConversations}
subscribeToMoreConversations={() =>
subscribeToMore({
document: CONVERSATION_LIST_SUBSCRIPTION,
variables: { offset: 0 },
updateQuery: (prev, { subscriptionData }) => {
if (
!subscriptionData.data ||
subscriptionData.data.conversations.length === 0
)
return prev;
let conversations = [...prev.conversations];
const newConversations =
subscriptionData.data.conversations;
for (const conversation of newConversations) {
const index = conversations.findIndex(
(prevConversation) =>
prevConversation.id === conversation.id
);
if (index !== -1) {
conversations.splice(index, 1);
conversations.unshift(conversation);
continue;
}
conversations.unshift(conversation);
}
return Object.assign({}, prev, {
conversations: conversations,
});
},
})
}
/> />
)} )}
</Col> </Col>

View File

@@ -99,7 +99,7 @@ export function JobPayments({
render: (text, record) => ( render: (text, record) => (
<Space wrap> <Space wrap>
<Button <Button
disabled={record.exportedat} // disabled={record.exportedat}
onClick={() => { onClick={() => {
setPaymentContext({ setPaymentContext({
actions: { refetch: refetch }, actions: { refetch: refetch },

View File

@@ -0,0 +1,100 @@
import React from "react";
import { Button, notification } from "antd";
import { useMutation } from "@apollo/client";
import { useTranslation } from "react-i18next";
import { INSERT_EXPORT_LOG } from "../../graphql/accounting.queries";
import { setModalContext } from "../../redux/modals/modals.actions";
import { connect } from "react-redux";
import { UPDATE_PAYMENT } from "../../graphql/payments.queries";
import { selectCurrentUser } from "../../redux/user/user.selectors";
import { createStructuredSelector } from "reselect";
const mapStateToProps = createStructuredSelector({
currentUser: selectCurrentUser,
});
const mapDispatchToProps = (dispatch) => ({
setPaymentContext: (context) =>
dispatch(setModalContext({ context: context, modal: "payment" })),
});
const PaymentMarkForExportButton = ({
bodyshop,
payment,
refetch,
setPaymentContext,
currentUser,
}) => {
const { t } = useTranslation();
const [insertExportLog, { loading: exportLogLoading }] =
useMutation(INSERT_EXPORT_LOG);
const [updatePayment, { loading: updatePaymentLoading }] =
useMutation(UPDATE_PAYMENT);
const handleClick = async () => {
const today = new Date();
await insertExportLog({
variables: {
logs: [
{
bodyshopid: bodyshop.id,
paymentid: payment.id,
successful: true,
useremail: currentUser.email,
},
],
},
});
const paymentUpdateResponse = await updatePayment({
variables: {
paymentId: payment.id,
payment: {
exportedat: today,
},
},
});
if (!!!paymentUpdateResponse.errors) {
notification.open({
type: "success",
key: "paymentsuccessmarkforexport",
message: t("payments.successes.markexported"),
});
if (refetch) refetch();
setPaymentContext({
actions: {
refetch,
},
context: {
...payment,
exportedat: today,
},
});
} else {
notification["error"]({
message: t("payments.errors.exporting", {
error: JSON.stringify(paymentUpdateResponse.error),
}),
});
}
};
return (
<Button
onClick={handleClick}
loading={exportLogLoading || updatePaymentLoading}
disabled={!!payment.exportedat}
>
{t("payments.labels.markexported")}
</Button>
);
};
export default connect(
mapStateToProps,
mapDispatchToProps
)(PaymentMarkForExportButton);

View File

@@ -1,6 +1,6 @@
import { useMutation } from "@apollo/client"; import { useMutation } from "@apollo/client";
import { Button, Form, Modal, notification } from "antd"; import { Button, Form, Modal, notification, Space } 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 { connect } from "react-redux"; import { connect } from "react-redux";
@@ -19,6 +19,8 @@ import {
import { GenerateDocument } from "../../utils/RenderTemplate"; import { GenerateDocument } from "../../utils/RenderTemplate";
import { TemplateList } from "../../utils/TemplateConstants"; import { TemplateList } from "../../utils/TemplateConstants";
import PaymentForm from "../payment-form/payment-form.component"; import PaymentForm from "../payment-form/payment-form.component";
import PaymentReexportButton from "../payment-reexport-button/payment-reexport-button.component";
import PaymentMarkForExportButton from "../payment-mark-export-button/payment-mark-export-button-component";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
paymentModal: selectPayment, paymentModal: selectPayment,
@@ -176,12 +178,21 @@ function PaymentModalContainer({
</span> </span>
} }
> >
<Space>
<PaymentReexportButton payment={context} refetch={actions.refetch} />
<PaymentMarkForExportButton
bodyshop={bodyshop}
payment={context}
refetch={actions.refetch}
/>
</Space>
<Form <Form
onFinish={handleFinish} onFinish={handleFinish}
autoComplete={"off"} autoComplete={"off"}
form={form} form={form}
layout="vertical" layout="vertical"
initialValues={context || {}} initialValues={context || {}}
disabled={context.exportedat}
> >
<PaymentForm form={form} /> <PaymentForm form={form} />
</Form> </Form>

View File

@@ -0,0 +1,66 @@
import React from "react";
import { Button, notification } from "antd";
import { useTranslation } from "react-i18next";
import { UPDATE_PAYMENT } from "../../graphql/payments.queries";
import { useMutation } from "@apollo/client";
import { setModalContext } from "../../redux/modals/modals.actions";
import { connect } from "react-redux";
const mapDispatchToProps = (dispatch) => ({
setPaymentContext: (context) =>
dispatch(setModalContext({ context: context, modal: "payment" })),
});
const PaymentReexportButton = ({ payment, refetch, setPaymentContext }) => {
const { t } = useTranslation();
const [updatePayment, { loading }] = useMutation(UPDATE_PAYMENT);
const handleClick = async () => {
const paymentUpdateResponse = await updatePayment({
variables: {
paymentId: payment.id,
payment: {
exportedat: null,
},
},
});
if (!!!paymentUpdateResponse.errors) {
notification.open({
type: "success",
key: "paymentsuccessexport",
message: t("payments.successes.markreexported"),
});
if (refetch) refetch();
setPaymentContext({
actions: {
refetch,
},
context: {
...payment,
exportedat: null,
},
});
} else {
notification["error"]({
message: t("payments.errors.exporting", {
error: JSON.stringify(paymentUpdateResponse.error),
}),
});
}
};
return (
<Button
onClick={handleClick}
loading={loading}
disabled={!payment.exportedat}
>
{t("payments.labels.markforreexport")}
</Button>
);
};
export default connect(null, mapDispatchToProps)(PaymentReexportButton);

View File

@@ -156,7 +156,7 @@ export function PaymentsListPaginated({
render: (text, record) => ( render: (text, record) => (
<Space> <Space>
<Button <Button
disabled={record.exportedat} // disabled={record.exportedat}
onClick={async () => { onClick={async () => {
let apolloResults; let apolloResults;
if (search.search) { if (search.search) {

View File

@@ -25,6 +25,7 @@ export default function ProductionListDate({
// } // }
//e.stopPropagation(); //e.stopPropagation();
updateAlert({ updateAlert({
variables: { variables: {
jobId: record.id, jobId: record.id,
@@ -32,6 +33,11 @@ export default function ProductionListDate({
[field]: date, [field]: date,
}, },
}, },
optimisticResponse: {
update_jobs: {
[field]: date,
},
},
}).then(() => { }).then(() => {
if (record.refetch) record.refetch(); if (record.refetch) record.refetch();
if (!time) { if (!time) {
@@ -49,9 +55,11 @@ export default function ProductionListDate({
(moment().add(1, "day").isSame(moment(record[field]), "day") && (moment().add(1, "day").isSame(moment(record[field]), "day") &&
"production-completion-soon")); "production-completion-soon"));
} }
return ( return (
<Dropdown <Dropdown
//trigger={["click"]} trigger={["click"]}
onVisibleChange={(v) => setVisible(v)}
visible={visible} visible={visible}
style={{ style={{
height: "19px", height: "19px",

View File

@@ -39,9 +39,31 @@ export function ScheduleCalendarComponent({ data, refetch, bodyshop }) {
employeevacation: true, employeevacation: true,
ins_co_nm: null, ins_co_nm: null,
}); });
const [estimatorsFilter, setEstimatiorsFilter] = useLocalStorage(
"estimators",
[]
);
const estimators = useMemo(
() =>
data
.filter((d) => d.__typename === "appointments")
.map((app) => `${app.job.est_ct_fn} ${app.job.est_ct_ln}`),
[data]
);
const filteredData = useMemo(() => { const filteredData = useMemo(() => {
return data.filter( return data.filter((d) => {
(d) => const estFilter =
d.__typename === "appointments"
? estimatorsFilter.length === 0
? true
: !!estimatorsFilter.find(
(e) => e === `${d.job.est_ct_fn} ${d.job.est_ct_ln}`
)
: true;
return (
(d.block || (d.block ||
(filter.intake && d.isintake) || (filter.intake && d.isintake) ||
(filter.manual && !d.isintake && d.block === false) || (filter.manual && !d.isintake && d.block === false) ||
@@ -50,9 +72,11 @@ export function ScheduleCalendarComponent({ data, refetch, bodyshop }) {
!!d.employee)) && !!d.employee)) &&
(filter.ins_co_nm && filter.ins_co_nm.length > 0 (filter.ins_co_nm && filter.ins_co_nm.length > 0
? filter.ins_co_nm.includes(d.job?.ins_co_nm) ? filter.ins_co_nm.includes(d.job?.ins_co_nm)
: true) : true) &&
); estFilter
}, [data, filter]); );
});
}, [data, filter, estimatorsFilter]);
return ( return (
<Row gutter={[16, 16]}> <Row gutter={[16, 16]}>
@@ -63,6 +87,21 @@ export function ScheduleCalendarComponent({ data, refetch, bodyshop }) {
extra={ extra={
<Space wrap> <Space wrap>
<ScheduleAtsSummary appointments={filteredData} /> <ScheduleAtsSummary appointments={filteredData} />
<Select
style={{ minWidth: "15rem" }}
mode="multiple"
placeholder={t("schedule.labels.estimators")}
allowClear
onClear={() => setEstimatiorsFilter([])}
value={[...estimatorsFilter]}
onChange={(e) => {
setEstimatiorsFilter(e);
}}
options={estimators.map((e) => ({
label: e,
value: e,
}))}
/>
<Select <Select
style={{ minWidth: "15rem" }} style={{ minWidth: "15rem" }}
mode="multiple" mode="multiple"

View File

@@ -15,6 +15,8 @@ import ShopInfoResponsibilityCenterComponent from "./shop-info.responsibilitycen
import ShopInfoROStatusComponent from "./shop-info.rostatus.component"; import ShopInfoROStatusComponent from "./shop-info.rostatus.component";
import ShopInfoSchedulingComponent from "./shop-info.scheduling.component"; import ShopInfoSchedulingComponent from "./shop-info.scheduling.component";
import ShopInfoSpeedPrint from "./shop-info.speedprint.component"; import ShopInfoSpeedPrint from "./shop-info.speedprint.component";
import { useHistory, useLocation } from "react-router-dom";
import queryString from "query-string";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop, bodyshop: selectBodyshop,
@@ -31,6 +33,10 @@ export function ShopInfoComponent({ bodyshop, form, saveLoading }) {
bodyshop.imexshopid bodyshop.imexshopid
); );
const { t } = useTranslation(); const { t } = useTranslation();
const history = useHistory();
const location = useLocation();
const search = queryString.parse(location.search);
return ( return (
<Card <Card
extra={ extra={
@@ -43,7 +49,12 @@ export function ShopInfoComponent({ bodyshop, form, saveLoading }) {
</Button> </Button>
} }
> >
<Tabs> <Tabs
defaultActiveKey={search.subtab}
onChange={(key) =>
history.push({ search: `?tab=${search.tab}&subtab=${key}` })
}
>
<Tabs.TabPane key="general" tab={t("bodyshop.labels.shopinfo")}> <Tabs.TabPane key="general" tab={t("bodyshop.labels.shopinfo")}>
<ShopInfoGeneral form={form} /> <ShopInfoGeneral form={form} />
</Tabs.TabPane> </Tabs.TabPane>

View File

@@ -24,9 +24,13 @@ const timeZonesList = momentTZ.tz.names();
export default function ShopInfoGeneral({ form }) { export default function ShopInfoGeneral({ form }) {
const { t } = useTranslation(); const { t } = useTranslation();
return ( return (
<div> <div>
<LayoutFormRow header={t("bodyshop.labels.businessinformation")}> <LayoutFormRow
header={t("bodyshop.labels.businessinformation")}
id="businessinformation"
>
<Form.Item <Form.Item
label={t("bodyshop.fields.shopname")} label={t("bodyshop.fields.shopname")}
name="shopname" name="shopname"
@@ -155,7 +159,10 @@ export default function ShopInfoGeneral({ form }) {
<InputNumber min={0} /> <InputNumber min={0} />
</Form.Item> </Form.Item>
</LayoutFormRow> </LayoutFormRow>
<LayoutFormRow header={t("bodyshop.labels.accountingsetup")}> <LayoutFormRow
header={t("bodyshop.labels.accountingsetup")}
id="accountingsetup"
>
<Form.Item <Form.Item
label={t("bodyshop.labels.qbo")} label={t("bodyshop.labels.qbo")}
valuePropName="checked" valuePropName="checked"
@@ -386,7 +393,10 @@ export default function ShopInfoGeneral({ form }) {
<Select mode="tags" /> <Select mode="tags" />
</Form.Item> </Form.Item>
</LayoutFormRow> </LayoutFormRow>
<LayoutFormRow header={t("bodyshop.labels.scoreboardsetup")}> <LayoutFormRow
header={t("bodyshop.labels.scoreboardsetup")}
id="scoreboardsetup"
>
<Form.Item <Form.Item
label={t("bodyshop.fields.dailypainttarget")} label={t("bodyshop.fields.dailypainttarget")}
name={["scoreboard_target", "dailyPaintTarget"]} name={["scoreboard_target", "dailyPaintTarget"]}
@@ -445,7 +455,10 @@ export default function ShopInfoGeneral({ form }) {
<InputNumber min={1} precision={1} /> <InputNumber min={1} precision={1} />
</Form.Item> </Form.Item>
</LayoutFormRow> </LayoutFormRow>
<LayoutFormRow header={t("bodyshop.labels.systemsettings")}> <LayoutFormRow
header={t("bodyshop.labels.systemsettings")}
id="systemsettings"
>
<Form.Item <Form.Item
name={["md_referral_sources"]} name={["md_referral_sources"]}
label={t("bodyshop.fields.md_referral_sources")} label={t("bodyshop.fields.md_referral_sources")}
@@ -655,7 +668,11 @@ export default function ShopInfoGeneral({ form }) {
<Input /> <Input />
</Form.Item> </Form.Item>
</LayoutFormRow> </LayoutFormRow>
<LayoutFormRow grow header={t("bodyshop.labels.messagingpresets")}> <LayoutFormRow
grow
header={t("bodyshop.labels.messagingpresets")}
id="messagingpresets"
>
<Form.List name={["md_messaging_presets"]}> <Form.List name={["md_messaging_presets"]}>
{(fields, { add, remove, move }) => { {(fields, { add, remove, move }) => {
return ( return (
@@ -720,7 +737,11 @@ export default function ShopInfoGeneral({ form }) {
}} }}
</Form.List> </Form.List>
</LayoutFormRow> </LayoutFormRow>
<LayoutFormRow grow header={t("bodyshop.labels.notespresets")}> <LayoutFormRow
grow
header={t("bodyshop.labels.notespresets")}
id="notespresets"
>
<Form.List name={["md_notes_presets"]}> <Form.List name={["md_notes_presets"]}>
{(fields, { add, remove, move }) => { {(fields, { add, remove, move }) => {
return ( return (
@@ -785,7 +806,11 @@ export default function ShopInfoGeneral({ form }) {
}} }}
</Form.List> </Form.List>
</LayoutFormRow> </LayoutFormRow>
<LayoutFormRow grow header={t("bodyshop.labels.partslocations")}> <LayoutFormRow
grow
header={t("bodyshop.labels.partslocations")}
id="partslocations"
>
<Form.List name={["md_parts_locations"]}> <Form.List name={["md_parts_locations"]}>
{(fields, { add, remove, move }) => { {(fields, { add, remove, move }) => {
return ( return (
@@ -839,7 +864,11 @@ export default function ShopInfoGeneral({ form }) {
}} }}
</Form.List> </Form.List>
</LayoutFormRow> </LayoutFormRow>
<LayoutFormRow grow header={t("bodyshop.labels.insurancecos")}> <LayoutFormRow
grow
header={t("bodyshop.labels.insurancecos")}
id="insurancecos"
>
<Form.List name={["md_ins_cos"]}> <Form.List name={["md_ins_cos"]}>
{(fields, { add, remove, move }) => { {(fields, { add, remove, move }) => {
return ( return (
@@ -935,7 +964,11 @@ export default function ShopInfoGeneral({ form }) {
}} }}
</Form.List> </Form.List>
</LayoutFormRow> </LayoutFormRow>
<LayoutFormRow grow header={t("bodyshop.labels.estimators")}> <LayoutFormRow
grow
header={t("bodyshop.labels.estimators")}
id="estimators"
>
<Form.List name={["md_estimators"]}> <Form.List name={["md_estimators"]}>
{(fields, { add, remove, move }) => { {(fields, { add, remove, move }) => {
return ( return (
@@ -1024,7 +1057,11 @@ export default function ShopInfoGeneral({ form }) {
}} }}
</Form.List> </Form.List>
</LayoutFormRow> </LayoutFormRow>
<LayoutFormRow grow header={t("bodyshop.labels.filehandlers")}> <LayoutFormRow
grow
header={t("bodyshop.labels.filehandlers")}
id="filehandlers"
>
<Form.List name={["md_filehandlers"]}> <Form.List name={["md_filehandlers"]}>
{(fields, { add, remove, move }) => { {(fields, { add, remove, move }) => {
return ( return (
@@ -1106,7 +1143,11 @@ export default function ShopInfoGeneral({ form }) {
}} }}
</Form.List> </Form.List>
</LayoutFormRow> </LayoutFormRow>
<LayoutFormRow grow header={t("bodyshop.fields.md_ccc_rates")}> <LayoutFormRow
grow
header={t("bodyshop.fields.md_ccc_rates")}
id="md_ccc_rates"
>
<Form.List name={["md_ccc_rates"]}> <Form.List name={["md_ccc_rates"]}>
{(fields, { add, remove, move }) => { {(fields, { add, remove, move }) => {
return ( return (
@@ -1223,7 +1264,11 @@ export default function ShopInfoGeneral({ form }) {
}} }}
</Form.List> </Form.List>
</LayoutFormRow> </LayoutFormRow>
<LayoutFormRow grow header={t("bodyshop.fields.md_jobline_presets")}> <LayoutFormRow
grow
header={t("bodyshop.fields.md_jobline_presets")}
id="md_jobline_presets"
>
<Form.List name={["md_jobline_presets"]}> <Form.List name={["md_jobline_presets"]}>
{(fields, { add, remove, move }) => { {(fields, { add, remove, move }) => {
return ( return (
@@ -1404,7 +1449,11 @@ export default function ShopInfoGeneral({ form }) {
}} }}
</Form.List> </Form.List>
</LayoutFormRow> </LayoutFormRow>
<LayoutFormRow grow header={t("bodyshop.fields.md_parts_order_comment")}> <LayoutFormRow
grow
header={t("bodyshop.fields.md_parts_order_comment")}
id="md_parts_order_comment"
>
<Form.List name={["md_parts_order_comment"]}> <Form.List name={["md_parts_order_comment"]}>
{(fields, { add, remove, move }) => { {(fields, { add, remove, move }) => {
return ( return (
@@ -1470,7 +1519,11 @@ export default function ShopInfoGeneral({ form }) {
}} }}
</Form.List> </Form.List>
</LayoutFormRow> </LayoutFormRow>
<LayoutFormRow grow header={t("bodyshop.labels.md_to_emails")}> <LayoutFormRow
grow
header={t("bodyshop.labels.md_to_emails")}
id="md_to_emails"
>
<Form.List name={["md_to_emails"]}> <Form.List name={["md_to_emails"]}>
{(fields, { add, remove, move }) => { {(fields, { add, remove, move }) => {
return ( return (

View File

@@ -20,7 +20,10 @@ export default function ShopInfoIntakeChecklistComponent({ form }) {
const TemplateListGenerated = TemplateList(); const TemplateListGenerated = TemplateList();
return ( return (
<div> <div>
<LayoutFormRow header={t("bodyshop.labels.intakechecklist")}> <LayoutFormRow
header={t("bodyshop.labels.intakechecklist")}
id="intakechecklist"
>
<Form.List name={["intakechecklist", "form"]}> <Form.List name={["intakechecklist", "form"]}>
{(fields, { add, remove, move }) => { {(fields, { add, remove, move }) => {
return ( return (
@@ -188,7 +191,10 @@ export default function ShopInfoIntakeChecklistComponent({ form }) {
</Form.Item> </Form.Item>
</SelectorDiv> </SelectorDiv>
<LayoutFormRow header={t("bodyshop.labels.deliverchecklist")}> <LayoutFormRow
header={t("bodyshop.labels.deliverchecklist")}
id="deliverchecklist"
>
<Form.List name={["deliverchecklist", "form"]}> <Form.List name={["deliverchecklist", "form"]}>
{(fields, { add, remove, move }) => { {(fields, { add, remove, move }) => {
return ( return (

View File

@@ -95,7 +95,6 @@ export function ShopInfoResponsibilityCenterComponent({ bodyshop, form }) {
{form.getFieldValue("pbs_serialnumber")} {form.getFieldValue("pbs_serialnumber")}
</DataLabel> </DataLabel>
)} )}
<LayoutFormRow> <LayoutFormRow>
<Form.Item <Form.Item
label={t("bodyshop.fields.dms.default_journal")} label={t("bodyshop.fields.dms.default_journal")}
@@ -315,7 +314,10 @@ export function ShopInfoResponsibilityCenterComponent({ bodyshop, form }) {
</DataLabel> </DataLabel>
</> </>
)} )}
<LayoutFormRow header={t("bodyshop.labels.responsibilitycenters.costs")}> <LayoutFormRow
header={t("bodyshop.labels.responsibilitycenters.costs")}
id="costs"
>
<Form.List name={["md_responsibility_centers", "costs"]}> <Form.List name={["md_responsibility_centers", "costs"]}>
{(fields, { add, remove }) => { {(fields, { add, remove }) => {
return ( return (
@@ -462,6 +464,7 @@ export function ShopInfoResponsibilityCenterComponent({ bodyshop, form }) {
<LayoutFormRow <LayoutFormRow
header={t("bodyshop.labels.responsibilitycenters.profits")} header={t("bodyshop.labels.responsibilitycenters.profits")}
id="profits"
> >
<Form.List name={["md_responsibility_centers", "profits"]}> <Form.List name={["md_responsibility_centers", "profits"]}>
{(fields, { add, remove }) => { {(fields, { add, remove }) => {
@@ -601,7 +604,7 @@ export function ShopInfoResponsibilityCenterComponent({ bodyshop, form }) {
{fields.map((field, index) => ( {fields.map((field, index) => (
<Form.Item key={field.key}> <Form.Item key={field.key}>
<div> <div>
<LayoutFormRow> <LayoutFormRow id="mappingname">
<Form.Item <Form.Item
label={t("bodyshop.fields.dms.mappingname")} label={t("bodyshop.fields.dms.mappingname")}
key={`${index}name`} key={`${index}name`}
@@ -631,6 +634,7 @@ export function ShopInfoResponsibilityCenterComponent({ bodyshop, form }) {
</LayoutFormRow> </LayoutFormRow>
<LayoutFormRow <LayoutFormRow
header={t("bodyshop.labels.defaultcostsmapping")} header={t("bodyshop.labels.defaultcostsmapping")}
id="defaultcostsmapping"
> >
<Form.Item <Form.Item
label={t( label={t(
@@ -4088,6 +4092,7 @@ export function ShopInfoResponsibilityCenterComponent({ bodyshop, form }) {
<LayoutFormRow <LayoutFormRow
header={t("bodyshop.labels.responsibilitycenters.tax_accounts")} header={t("bodyshop.labels.responsibilitycenters.tax_accounts")}
id="tax_accounts"
> >
<Form.Item <Form.Item
label={t("bodyshop.fields.responsibilitycenters.federal_tax")} label={t("bodyshop.fields.responsibilitycenters.federal_tax")}
@@ -4202,7 +4207,7 @@ export function ShopInfoResponsibilityCenterComponent({ bodyshop, form }) {
</Form.Item> </Form.Item>
</LayoutFormRow> </LayoutFormRow>
{DmsAp.treatment === "on" && ( {DmsAp.treatment === "on" && (
<LayoutFormRow> <LayoutFormRow id="federal_tax_itc">
<Form.Item <Form.Item
label={t("bodyshop.fields.responsibilitycenters.federal_tax_itc")} label={t("bodyshop.fields.responsibilitycenters.federal_tax_itc")}
rules={[ rules={[
@@ -4316,7 +4321,7 @@ export function ShopInfoResponsibilityCenterComponent({ bodyshop, form }) {
</Form.Item> </Form.Item>
</LayoutFormRow> </LayoutFormRow>
)} )}
<LayoutFormRow> <LayoutFormRow id="state_tax">
<Form.Item <Form.Item
label={t("bodyshop.fields.responsibilitycenters.state_tax")} label={t("bodyshop.fields.responsibilitycenters.state_tax")}
rules={[ rules={[
@@ -4414,7 +4419,7 @@ export function ShopInfoResponsibilityCenterComponent({ bodyshop, form }) {
<InputNumber precision={2} /> <InputNumber precision={2} />
</Form.Item> </Form.Item>
</LayoutFormRow> </LayoutFormRow>
<LayoutFormRow> <LayoutFormRow id="local_tax">
<Form.Item <Form.Item
label={t("bodyshop.fields.responsibilitycenters.local_tax")} label={t("bodyshop.fields.responsibilitycenters.local_tax")}
rules={[ rules={[
@@ -4512,7 +4517,7 @@ export function ShopInfoResponsibilityCenterComponent({ bodyshop, form }) {
<InputNumber precision={2} /> <InputNumber precision={2} />
</Form.Item> </Form.Item>
</LayoutFormRow> </LayoutFormRow>
<LayoutFormRow header={<div>AR</div>}> <LayoutFormRow header={<div>AR</div>} id="AR">
{/* <Form.Item {/* <Form.Item
label={t("bodyshop.fields.responsibilitycenters.ar")} label={t("bodyshop.fields.responsibilitycenters.ar")}
rules={[ rules={[
@@ -4576,7 +4581,10 @@ export function ShopInfoResponsibilityCenterComponent({ bodyshop, form }) {
</LayoutFormRow> </LayoutFormRow>
{DmsAp.treatment === "on" && ( {DmsAp.treatment === "on" && (
<LayoutFormRow header={t("bodyshop.fields.responsibilitycenters.ap")}> <LayoutFormRow
header={t("bodyshop.fields.responsibilitycenters.ap")}
id="ap"
>
{/* <Form.Item {/* <Form.Item
label={t("bodyshop.fields.responsibilitycenters.ap")} label={t("bodyshop.fields.responsibilitycenters.ap")}
rules={[ rules={[
@@ -4639,7 +4647,7 @@ export function ShopInfoResponsibilityCenterComponent({ bodyshop, form }) {
</Form.Item> </Form.Item>
</LayoutFormRow> </LayoutFormRow>
)} )}
<LayoutFormRow header={<div>Refund</div>}> <LayoutFormRow header={<div>Refund</div>} id="refund">
{/* <Form.Item {/* <Form.Item
label={t("bodyshop.fields.responsibilitycenters.refund")} label={t("bodyshop.fields.responsibilitycenters.refund")}
rules={[ rules={[
@@ -4702,7 +4710,10 @@ export function ShopInfoResponsibilityCenterComponent({ bodyshop, form }) {
</Form.Item> </Form.Item>
</LayoutFormRow> </LayoutFormRow>
{Qb_Multi_Ar.treatment === "on" && ( {Qb_Multi_Ar.treatment === "on" && (
<LayoutFormRow header={<div>Multiple Payers Item</div>}> <LayoutFormRow
header={<div>Multiple Payers Item</div>}
id="accountitem"
>
<Form.Item <Form.Item
label={t("bodyshop.fields.responsibilitycenter_accountitem")} label={t("bodyshop.fields.responsibilitycenter_accountitem")}
rules={[ rules={[
@@ -4730,7 +4741,7 @@ export function ShopInfoResponsibilityCenterComponent({ bodyshop, form }) {
<div> <div>
{fields.map((field, index) => ( {fields.map((field, index) => (
<Form.Item key={field.key}> <Form.Item key={field.key}>
<LayoutFormRow> <LayoutFormRow id="sales_tax_codes">
<Form.Item <Form.Item
label={t( label={t(
"bodyshop.fields.responsibilitycenters.sales_tax_codes.description" "bodyshop.fields.responsibilitycenters.sales_tax_codes.description"

View File

@@ -44,7 +44,7 @@ export function ShopInfoROStatusComponent({ bodyshop, form }) {
}; };
return ( return (
<SelectorDiv> <SelectorDiv id="jobstatus">
<Form.Item <Form.Item
name={["md_ro_statuses", "statuses"]} name={["md_ro_statuses", "statuses"]}
label={t("bodyshop.labels.alljobstatuses")} label={t("bodyshop.labels.alljobstatuses")}
@@ -322,6 +322,7 @@ export function ShopInfoROStatusComponent({ bodyshop, form }) {
<LayoutFormRow <LayoutFormRow
grow grow
header={t("bodyshop.fields.statuses.production_colors")} header={t("bodyshop.fields.statuses.production_colors")}
id="production_colors"
> >
<Form.List name={["md_ro_statuses", "production_colors"]}> <Form.List name={["md_ro_statuses", "production_colors"]}>
{(fields, { add, remove, move }) => { {(fields, { add, remove, move }) => {

View File

@@ -92,7 +92,7 @@ export default function ShopInfoSchedulingComponent({ form }) {
</Form.Item> </Form.Item>
</LayoutFormRow> </LayoutFormRow>
<Divider orientation="left">{t("bodyshop.labels.workingdays")}</Divider> <Divider orientation="left">{t("bodyshop.labels.workingdays")}</Divider>
<Space wrap size="large"> <Space wrap size="large" id="workingdays">
<Form.Item <Form.Item
label={t("general.labels.sunday")} label={t("general.labels.sunday")}
name={["workingdays", "sunday"]} name={["workingdays", "sunday"]}
@@ -143,7 +143,7 @@ export default function ShopInfoSchedulingComponent({ form }) {
<Switch /> <Switch />
</Form.Item> </Form.Item>
</Space> </Space>
<LayoutFormRow header={t("bodyshop.labels.apptcolors")}> <LayoutFormRow header={t("bodyshop.labels.apptcolors")} id="apptcolors">
<Form.List name={["appt_colors"]}> <Form.List name={["appt_colors"]}>
{(fields, { add, remove, move }) => { {(fields, { add, remove, move }) => {
return ( return (
@@ -208,7 +208,7 @@ export default function ShopInfoSchedulingComponent({ form }) {
}} }}
</Form.List> </Form.List>
</LayoutFormRow> </LayoutFormRow>
<LayoutFormRow header={t("bodyshop.labels.ssbuckets")}> <LayoutFormRow header={t("bodyshop.labels.ssbuckets")} id="ssbuckets">
<Form.List name={["ssbuckets"]}> <Form.List name={["ssbuckets"]}>
{(fields, { add, remove, move }) => { {(fields, { add, remove, move }) => {
return ( return (

View File

@@ -96,8 +96,9 @@ export function TimeTicketModalComponent({
]} ]}
> >
<JobSearchSelect <JobSearchSelect
convertedOnly={!bodyshop.tt_allow_post_to_invoiced} convertedOnly={true}
notExported={!bodyshop.tt_allow_post_to_invoiced} notExported={!bodyshop.tt_allow_post_to_invoiced}
notInvoiced={!bodyshop.tt_allow_post_to_invoiced}
/> />
</Form.Item> </Form.Item>
)} )}

View File

@@ -57,6 +57,8 @@ export const QUERY_ALL_ACTIVE_APPOINTMENTS = gql`
v_model_yr v_model_yr
v_make_desc v_make_desc
v_model_desc v_model_desc
est_ct_fn
est_ct_ln
labhrs: joblines_aggregate( labhrs: joblines_aggregate(
where: { mod_lbr_ty: { _neq: "LAR" }, removed: { _eq: false } } where: { mod_lbr_ty: { _neq: "LAR" }, removed: { _eq: false } }
) { ) {

View File

@@ -1,35 +1,36 @@
import { gql } from "@apollo/client"; import { gql } from "@apollo/client";
// export const CONVERSATION_LIST_SUBSCRIPTION = gql` export const CONVERSATION_LIST_SUBSCRIPTION = gql`
// subscription CONVERSATION_LIST_SUBSCRIPTION { subscription CONVERSATION_LIST_SUBSCRIPTION($offset: Int!) {
// conversations( conversations(
// order_by: { updated_at: desc } order_by: { updated_at: desc }
// limit: 50 limit: 1
// where: { archived: { _eq: false } } offset: $offset
// ) { where: { archived: { _eq: false } }
// phone_num ) {
// id phone_num
// updated_at id
// unreadcnt updated_at
// messages_aggregate( unreadcnt
// where: { read: { _eq: false }, isoutbound: { _eq: false } } messages_aggregate(
// ) { where: { read: { _eq: false }, isoutbound: { _eq: false } }
// aggregate { ) {
// count aggregate {
// } count
// } }
// job_conversations { }
// job { job_conversations {
// id job {
// ro_number id
// ownr_fn ro_number
// ownr_ln ownr_fn
// ownr_co_nm ownr_ln
// } ownr_co_nm
// } }
// } }
// } }
// `; }
`;
export const UNREAD_CONVERSATION_COUNT = gql` export const UNREAD_CONVERSATION_COUNT = gql`
query UNREAD_CONVERSATION_COUNT { query UNREAD_CONVERSATION_COUNT {
@@ -43,11 +44,24 @@ export const UNREAD_CONVERSATION_COUNT = gql`
} }
`; `;
export const UNREAD_CONVERSATION_COUNT_SUBSCRIPTION = gql`
subscription UNREAD_CONVERSATION_COUNT_SUBSCRIPTION {
messages_aggregate(
where: { read: { _eq: false }, isoutbound: { _eq: false } }
) {
aggregate {
count
}
}
}
`;
export const CONVERSATION_LIST_QUERY = gql` export const CONVERSATION_LIST_QUERY = gql`
query CONVERSATION_LIST_QUERY { query CONVERSATION_LIST_QUERY($offset: Int!) {
conversations( conversations(
order_by: { updated_at: desc } order_by: { updated_at: desc }
limit: 50 limit: 50
offset: $offset
where: { archived: { _eq: false } } where: { archived: { _eq: false } }
) { ) {
phone_num phone_num

View File

@@ -1075,6 +1075,9 @@ export const UPDATE_JOB = gql`
lbr_adjustments lbr_adjustments
suspended suspended
queued_for_parts queued_for_parts
scheduled_completion
actual_in
date_repairstarted
} }
} }
} }

View File

@@ -1,5 +1,7 @@
import { Tabs } from "antd"; import { Tabs } from "antd";
import React, { useEffect } from "react"; import React, { useEffect } from "react";
import { useHistory, useLocation } from "react-router-dom";
import queryString from "query-string";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import ShopEmployeesContainer from "../../components/shop-employees/shop-employees.container"; import ShopEmployeesContainer from "../../components/shop-employees/shop-employees.container";
import ShopInfoContainer from "../../components/shop-info/shop-info.container"; import ShopInfoContainer from "../../components/shop-info/shop-info.container";
@@ -24,6 +26,9 @@ const mapDispatchToProps = (dispatch) => ({
export function ShopPage({ bodyshop, setSelectedHeader, setBreadcrumbs }) { export function ShopPage({ bodyshop, setSelectedHeader, setBreadcrumbs }) {
const { t } = useTranslation(); const { t } = useTranslation();
const history = useHistory();
const search = queryString.parse(useLocation().search);
useEffect(() => { useEffect(() => {
document.title = t("titles.shop"); document.title = t("titles.shop");
setSelectedHeader("shop"); setSelectedHeader("shop");
@@ -37,7 +42,10 @@ export function ShopPage({ bodyshop, setSelectedHeader, setBreadcrumbs }) {
return ( return (
<RbacWrapper action="shop:config"> <RbacWrapper action="shop:config">
<Tabs> <Tabs
defaultActiveKey={search.tab}
onChange={(key) => history.push({ search: `?tab=${key}` })}
>
<Tabs.TabPane tab={t("bodyshop.labels.shopinfo")} key="info"> <Tabs.TabPane tab={t("bodyshop.labels.shopinfo")} key="info">
<ShopInfoContainer /> <ShopInfoContainer />
</Tabs.TabPane> </Tabs.TabPane>

View File

@@ -2217,10 +2217,13 @@
"new": "New Payment", "new": "New Payment",
"signup": "Please contact support to sign up for electronic payments.", "signup": "Please contact support to sign up for electronic payments.",
"title": "Payments", "title": "Payments",
"totalpayments": "Total Payments" "totalpayments": "Total Payments",
"markexported": "Mark Exported",
"markforreexport": "Mark for Re-export"
}, },
"successes": { "successes": {
"exported": "Payment(s) exported successfully.", "exported": "Payment(s) exported successfully.",
"markreexported": "Payment marked for re-export successfully",
"markexported": "Payment(s) marked exported.", "markexported": "Payment(s) marked exported.",
"payment": "Payment created successfully. ", "payment": "Payment created successfully. ",
"stripe": "Credit card transaction charged successfully." "stripe": "Credit card transaction charged successfully."
@@ -2316,7 +2319,7 @@
"folder_label_multiple": "Folder Label - Multi", "folder_label_multiple": "Folder Label - Multi",
"glass_express_checklist": "Glass Express Checklist", "glass_express_checklist": "Glass Express Checklist",
"guarantee": "Repair Guarantee", "guarantee": "Repair Guarantee",
"individual_job_note": "Job Note RO # {{ro_number}}", "individual_job_note": "RO Job Note",
"invoice_customer_payable": "Invoice (Customer Payable)", "invoice_customer_payable": "Invoice (Customer Payable)",
"invoice_total_payable": "Invoice (Total Payable)", "invoice_total_payable": "Invoice (Total Payable)",
"iou_form": "IOU Form", "iou_form": "IOU Form",
@@ -2332,7 +2335,8 @@
"mechanical_authorization": "Mechanical Authorization", "mechanical_authorization": "Mechanical Authorization",
"mpi_animal_checklist": "MPI - Animal Checklist", "mpi_animal_checklist": "MPI - Animal Checklist",
"mpi_eglass_auth": "MPI - eGlass Auth", "mpi_eglass_auth": "MPI - eGlass Auth",
"mpi_final_acct_sheet": "MPI - Final Accounting Sheet", "mpi_final_acct_sheet": "MPI - Final Accounting Sheet (Direct Repair)",
"mpi_final_repair_acct_sheet": "MPI - Final Accounting Sheet",
"paint_grid": "Paint Grid", "paint_grid": "Paint Grid",
"parts_invoice_label_single": "Parts Label Single", "parts_invoice_label_single": "Parts Label Single",
"parts_label_multiple": "Parts Label - Multi", "parts_label_multiple": "Parts Label - Multi",
@@ -2394,6 +2398,7 @@
}, },
"subjects": { "subjects": {
"jobs": { "jobs": {
"individual_job_note": "Job Note RO: {{ro_number}}",
"parts_order": "Parts Order PO: {{ro_number}} - {{name}}", "parts_order": "Parts Order PO: {{ro_number}} - {{name}}",
"sublet_order": "Sublet Order PO: {{ro_number}} - {{name}}" "sublet_order": "Sublet Order PO: {{ro_number}} - {{name}}"
} }
@@ -2619,6 +2624,7 @@
"atssummary": "ATS Summary", "atssummary": "ATS Summary",
"employeevacation": "Employee Vacations", "employeevacation": "Employee Vacations",
"ins_co_nm_filter": "Filter by Insurance Company", "ins_co_nm_filter": "Filter by Insurance Company",
"estimators": "Filter by Writer/Customer Rep.",
"intake": "Intake Events", "intake": "Intake Events",
"manual": "Manual Events", "manual": "Manual Events",
"manualevent": "Add Manual Event" "manualevent": "Add Manual Event"

View File

@@ -2333,6 +2333,7 @@
"mpi_animal_checklist": "", "mpi_animal_checklist": "",
"mpi_eglass_auth": "", "mpi_eglass_auth": "",
"mpi_final_acct_sheet": "", "mpi_final_acct_sheet": "",
"mpi_final_repair_acct_sheet": "",
"paint_grid": "", "paint_grid": "",
"parts_invoice_label_single": "", "parts_invoice_label_single": "",
"parts_label_multiple": "", "parts_label_multiple": "",
@@ -2394,6 +2395,7 @@
}, },
"subjects": { "subjects": {
"jobs": { "jobs": {
"individual_job_note": "",
"parts_order": "", "parts_order": "",
"sublet_order": "" "sublet_order": ""
} }

View File

@@ -2333,6 +2333,7 @@
"mpi_animal_checklist": "", "mpi_animal_checklist": "",
"mpi_eglass_auth": "", "mpi_eglass_auth": "",
"mpi_final_acct_sheet": "", "mpi_final_acct_sheet": "",
"mpi_final_repair_acct_sheet": "",
"paint_grid": "", "paint_grid": "",
"parts_invoice_label_single": "", "parts_invoice_label_single": "",
"parts_label_multiple": "", "parts_label_multiple": "",
@@ -2394,6 +2395,7 @@
}, },
"subjects": { "subjects": {
"jobs": { "jobs": {
"individual_job_note": "",
"parts_order": "", "parts_order": "",
"sublet_order": "" "sublet_order": ""
} }

View File

@@ -3,7 +3,10 @@ import { setContext } from "@apollo/client/link/context";
import { HttpLink } from "@apollo/client/link/http"; //"apollo-link-http"; import { HttpLink } from "@apollo/client/link/http"; //"apollo-link-http";
import { RetryLink } from "@apollo/client/link/retry"; import { RetryLink } from "@apollo/client/link/retry";
import { WebSocketLink } from "@apollo/client/link/ws"; import { WebSocketLink } from "@apollo/client/link/ws";
import { getMainDefinition } from "@apollo/client/utilities"; import {
getMainDefinition,
offsetLimitPagination,
} from "@apollo/client/utilities";
//import { split } from "apollo-link"; //import { split } from "apollo-link";
import apolloLogger from "apollo-link-logger"; import apolloLogger from "apollo-link-logger";
//import axios from "axios"; //import axios from "axios";
@@ -140,7 +143,15 @@ middlewares.push(
) )
); );
const cache = new InMemoryCache({}); const cache = new InMemoryCache({
typePolicies: {
Query: {
fields: {
conversations: offsetLimitPagination(),
},
},
},
});
const client = new ApolloClient({ const client = new ApolloClient({
link: ApolloLink.from(middlewares), link: ApolloLink.from(middlewares),

View File

@@ -421,6 +421,17 @@ export const TemplateList = (type, context) => {
CA_MB: true, CA_MB: true,
}, },
}, },
mpi_final_repair_acct_sheet: {
title: i18n.t("printcenter.jobs.mpi_final_repair_acct_sheet"),
description: "",
key: "mpi_final_repair_acct_sheet",
subject: i18n.t("printcenter.jobs.mpi_final_repair_acct_sheet"),
disabled: false,
group: "post",
regions: {
CA_MB: true,
},
},
mpi_eglass_auth: { mpi_eglass_auth: {
title: i18n.t("printcenter.jobs.mpi_eglass_auth"), title: i18n.t("printcenter.jobs.mpi_eglass_auth"),
description: "", description: "",
@@ -542,7 +553,7 @@ export const TemplateList = (type, context) => {
title: i18n.t("printcenter.jobs.individual_job_note"), title: i18n.t("printcenter.jobs.individual_job_note"),
description: "", description: "",
key: "individual_job_note", key: "individual_job_note",
subject: i18n.t("printcenter.jobs.individual_job_note", { subject: i18n.t("printcenter.subjects.jobs.individual_job_note", {
ro_number: (context && context.ro_number) || "", ro_number: (context && context.ro_number) || "",
}), }),
disabled: false, disabled: false,

View File

@@ -11168,6 +11168,11 @@ react-image-lightbox@^5.1.4:
prop-types "^15.7.2" prop-types "^15.7.2"
react-modal "^3.11.1" react-modal "^3.11.1"
react-intersection-observer@^9.4.3:
version "9.4.3"
resolved "https://registry.yarnpkg.com/react-intersection-observer/-/react-intersection-observer-9.4.3.tgz#ec84ce0c25cad548075130ea045ac5c7adf908f3"
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:
version "16.13.1" version "16.13.1"
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"

View File

@@ -432,7 +432,7 @@ async function QueryDmsCustomerById(socket, JobData, CustomerId) {
async function QueryDmsCustomerByName(socket, JobData) { async function QueryDmsCustomerByName(socket, JobData) {
const ownerName = ( const ownerName = (
JobData.ownr_co_nm && JobData.ownr_co_nm !== "" JobData.ownr_co_nm && JobData.ownr_co_nm.trim() !== ""
? JobData.ownr_co_nm ? JobData.ownr_co_nm
: `${JobData.ownr_ln},${JobData.ownr_fn}` : `${JobData.ownr_ln},${JobData.ownr_fn}`
).replace(replaceSpecialRegex, ""); ).replace(replaceSpecialRegex, "");
@@ -642,7 +642,8 @@ async function InsertDmsCustomer(socket, newCustomerNumber) {
.toUpperCase(), .toUpperCase(),
middleName: null, middleName: null,
nameType: nameType:
socket.JobData.ownr_co_nm && socket.JobData.ownr_co_nm socket.JobData.ownr_co_nm &&
String(socket.JobData.ownr_co_nm).trim() !== ""
? "Business" ? "Business"
: "Person", : "Person",
suffix: null, suffix: null,
@@ -728,7 +729,9 @@ async function InsertDmsVehicle(socket) {
deliveryDate: moment() deliveryDate: moment()
// .tz(socket.JobData.bodyshop.timezone) // .tz(socket.JobData.bodyshop.timezone)
.format("YYYYMMDD"), .format("YYYYMMDD"),
licensePlateNo: socket.JobData.plate_no, licensePlateNo: String(socket.JobData.plate_no)
.replace(/([^\w]|_)/g, "")
.toUpperCase(),
make: socket.txEnvelope.dms_make, make: socket.txEnvelope.dms_make,
modelAbrev: socket.txEnvelope.dms_model, modelAbrev: socket.txEnvelope.dms_model,
modelYear: socket.JobData.v_model_yr, modelYear: socket.JobData.v_model_yr,

View File

@@ -4312,14 +4312,9 @@ tslib@^1.11.1:
integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==
tslib@^2.0.1, tslib@^2.1.0: tslib@^2.0.1, tslib@^2.1.0:
version "2.5.2"
tslib@^2.3.1, tslib@^2.5.0: resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.5.2.tgz#1b6f07185c881557b0ffa84b111a0106989e8338"
version "2.5.0" integrity sha512-5svOrSA2w3iGFDs1HibEVBGbDrAY82bFQ3HZ3ixB+88nsbsWQoKqDRb5UBYAUPEzbBn6dAp5gRNXglySbx1MlA==
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.5.0.tgz#42bfed86f5787aeb41d031866c8f402429e0fddf"
integrity sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==
version "2.5.0"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.5.0.tgz#42bfed86f5787aeb41d031866c8f402429e0fddf"
integrity sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==
tslib@^2.3.1, tslib@^2.5.0: tslib@^2.3.1, tslib@^2.5.0:
version "2.5.0" version "2.5.0"