Merge branch 'master' into feature/america
This commit is contained in:
@@ -51,6 +51,7 @@
|
||||
"react-i18next": "^12.2.0",
|
||||
"react-icons": "^4.7.1",
|
||||
"react-image-lightbox": "^5.1.4",
|
||||
"react-intersection-observer": "^9.4.3",
|
||||
"react-number-format": "^5.1.3",
|
||||
"react-redux": "^8.0.5",
|
||||
"react-resizable": "^3.0.4",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Badge, List, Tag } from "antd";
|
||||
import React from "react";
|
||||
import React, { useEffect } from "react";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { setSelectedConversation } from "../../redux/messaging/messaging.actions";
|
||||
@@ -7,6 +7,8 @@ import { selectSelectedConversation } from "../../redux/messaging/messaging.sele
|
||||
import { TimeAgoFormatter } from "../../utils/DateFormatter";
|
||||
import PhoneFormatter from "../../utils/PhoneFormatter";
|
||||
import OwnerNameDisplay from "../owner-name-display/owner-name-display.component";
|
||||
import { List as VirtualizedList, AutoSizer } from "react-virtualized";
|
||||
|
||||
import "./chat-conversation-list.styles.scss";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
@@ -18,59 +20,86 @@ const mapDispatchToProps = (dispatch) => ({
|
||||
dispatch(setSelectedConversation(conversationId)),
|
||||
});
|
||||
|
||||
export function ChatConversationListComponent({
|
||||
function ChatConversationListComponent({
|
||||
conversationList,
|
||||
selectedConversation,
|
||||
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 (
|
||||
<div className="chat-list-container">
|
||||
<List
|
||||
bordered
|
||||
dataSource={conversationList}
|
||||
renderItem={(item) => (
|
||||
<List.Item
|
||||
key={item.id}
|
||||
onClick={() => setSelectedConversation(item.id)}
|
||||
className={`chat-list-item ${
|
||||
item.id === selectedConversation
|
||||
? "chat-list-selected-conversation"
|
||||
: null
|
||||
}`}
|
||||
>
|
||||
<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>
|
||||
{({ height, width }) => (
|
||||
<VirtualizedList
|
||||
height={height}
|
||||
width={width}
|
||||
rowCount={conversationList.length}
|
||||
rowHeight={60}
|
||||
rowRenderer={rowRenderer}
|
||||
onScroll={({ scrollTop, scrollHeight, clientHeight }) => {
|
||||
if (scrollTop + clientHeight === scrollHeight) {
|
||||
loadMoreConversations();
|
||||
}
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</AutoSizer>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
|
||||
@@ -3,8 +3,9 @@
|
||||
}
|
||||
.chat-list-container {
|
||||
flex: 1;
|
||||
overflow: auto;
|
||||
overflow: hidden;
|
||||
height: 100%;
|
||||
border: 1px solid gainsboro;
|
||||
}
|
||||
|
||||
.chat-list-item {
|
||||
@@ -21,4 +22,6 @@
|
||||
.ro-number-tag {
|
||||
align-self: baseline;
|
||||
}
|
||||
padding: 12px 24px;
|
||||
border-bottom: 1px solid gainsboro;
|
||||
}
|
||||
|
||||
@@ -4,15 +4,16 @@ import {
|
||||
ShrinkOutlined,
|
||||
SyncOutlined,
|
||||
} 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 React, { useEffect, useState } from "react";
|
||||
import React, { useCallback, useEffect, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import {
|
||||
CONVERSATION_LIST_QUERY,
|
||||
UNREAD_CONVERSATION_COUNT,
|
||||
CONVERSATION_LIST_SUBSCRIPTION,
|
||||
UNREAD_CONVERSATION_COUNT_SUBSCRIPTION,
|
||||
} from "../../graphql/conversations.queries";
|
||||
import { toggleChatVisible } from "../../redux/messaging/messaging.actions";
|
||||
import {
|
||||
@@ -41,17 +42,17 @@ export function ChatPopupComponent({
|
||||
const { t } = useTranslation();
|
||||
const [pollInterval, setpollInterval] = useState(0);
|
||||
|
||||
const { data: unreadData } = useQuery(UNREAD_CONVERSATION_COUNT, {
|
||||
fetchPolicy: "network-only",
|
||||
nextFetchPolicy: "network-only",
|
||||
...(pollInterval > 0 ? { pollInterval } : {}),
|
||||
});
|
||||
const { data: unreadData } = useSubscription(
|
||||
UNREAD_CONVERSATION_COUNT_SUBSCRIPTION
|
||||
);
|
||||
|
||||
const { loading, data, refetch, called } = useQuery(CONVERSATION_LIST_QUERY, {
|
||||
const [
|
||||
getConversations,
|
||||
{ loading, data, called, refetch, fetchMore, subscribeToMore },
|
||||
] = useLazyQuery(CONVERSATION_LIST_QUERY, {
|
||||
fetchPolicy: "network-only",
|
||||
nextFetchPolicy: "network-only",
|
||||
skip: !chatVisible,
|
||||
...(pollInterval > 0 ? { pollInterval } : {}),
|
||||
});
|
||||
|
||||
const fcmToken = sessionStorage.getItem("fcmtoken");
|
||||
@@ -65,15 +66,22 @@ export function ChatPopupComponent({
|
||||
}, [fcmToken]);
|
||||
|
||||
useEffect(() => {
|
||||
if (called && chatVisible) refetch();
|
||||
}, [chatVisible, called, refetch]);
|
||||
if (called && chatVisible)
|
||||
getConversations({
|
||||
variables: {
|
||||
offset: 0,
|
||||
},
|
||||
});
|
||||
}, [chatVisible, called, getConversations]);
|
||||
|
||||
// const unreadCount = data
|
||||
// ? data.conversations.reduce(
|
||||
// (acc, val) => val.messages_aggregate.aggregate.count + acc,
|
||||
// 0
|
||||
// )
|
||||
// : 0;
|
||||
const loadMoreConversations = useCallback(() => {
|
||||
if (data)
|
||||
fetchMore({
|
||||
variables: {
|
||||
offset: data.conversations.length,
|
||||
},
|
||||
});
|
||||
}, [data, fetchMore]);
|
||||
|
||||
const unreadCount = unreadData?.messages_aggregate.aggregate.count || 0;
|
||||
|
||||
@@ -110,6 +118,44 @@ export function ChatPopupComponent({
|
||||
) : (
|
||||
<ChatConversationListComponent
|
||||
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>
|
||||
|
||||
@@ -115,7 +115,7 @@ export function JobPayments({
|
||||
render: (text, record) => (
|
||||
<Space wrap>
|
||||
<Button
|
||||
disabled={record.exportedat}
|
||||
// disabled={record.exportedat}
|
||||
onClick={() => {
|
||||
setPaymentContext({
|
||||
actions: { refetch: refetch },
|
||||
|
||||
@@ -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);
|
||||
@@ -1,6 +1,6 @@
|
||||
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 { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
@@ -19,6 +19,8 @@ import {
|
||||
import { GenerateDocument } from "../../utils/RenderTemplate";
|
||||
import { TemplateList } from "../../utils/TemplateConstants";
|
||||
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({
|
||||
paymentModal: selectPayment,
|
||||
@@ -176,12 +178,24 @@ function PaymentModalContainer({
|
||||
</span>
|
||||
}
|
||||
>
|
||||
{!context || (context && !context.id) ? null : (
|
||||
<Space>
|
||||
<PaymentReexportButton payment={context} refetch={actions.refetch} />
|
||||
<PaymentMarkForExportButton
|
||||
bodyshop={bodyshop}
|
||||
payment={context}
|
||||
refetch={actions.refetch}
|
||||
/>
|
||||
</Space>
|
||||
)}
|
||||
|
||||
<Form
|
||||
onFinish={handleFinish}
|
||||
autoComplete={"off"}
|
||||
form={form}
|
||||
layout="vertical"
|
||||
initialValues={context || {}}
|
||||
disabled={context?.exportedat}
|
||||
>
|
||||
<PaymentForm form={form} />
|
||||
</Form>
|
||||
|
||||
@@ -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);
|
||||
@@ -156,7 +156,7 @@ export function PaymentsListPaginated({
|
||||
render: (text, record) => (
|
||||
<Space>
|
||||
<Button
|
||||
disabled={record.exportedat}
|
||||
// disabled={record.exportedat}
|
||||
onClick={async () => {
|
||||
let apolloResults;
|
||||
if (search.search) {
|
||||
|
||||
@@ -25,6 +25,7 @@ export default function ProductionListDate({
|
||||
// }
|
||||
|
||||
//e.stopPropagation();
|
||||
|
||||
updateAlert({
|
||||
variables: {
|
||||
jobId: record.id,
|
||||
@@ -32,6 +33,11 @@ export default function ProductionListDate({
|
||||
[field]: date,
|
||||
},
|
||||
},
|
||||
optimisticResponse: {
|
||||
update_jobs: {
|
||||
[field]: date,
|
||||
},
|
||||
},
|
||||
}).then(() => {
|
||||
if (record.refetch) record.refetch();
|
||||
if (!time) {
|
||||
@@ -49,9 +55,11 @@ export default function ProductionListDate({
|
||||
(moment().add(1, "day").isSame(moment(record[field]), "day") &&
|
||||
"production-completion-soon"));
|
||||
}
|
||||
|
||||
return (
|
||||
<Dropdown
|
||||
//trigger={["click"]}
|
||||
trigger={["click"]}
|
||||
onVisibleChange={(v) => setVisible(v)}
|
||||
visible={visible}
|
||||
style={{
|
||||
height: "19px",
|
||||
|
||||
@@ -21,6 +21,7 @@ import ScheduleVerifyIntegrity from "../schedule-verify-integrity/schedule-verif
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import _ from "lodash";
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
});
|
||||
@@ -39,9 +40,39 @@ export function ScheduleCalendarComponent({ data, refetch, bodyshop }) {
|
||||
employeevacation: true,
|
||||
ins_co_nm: null,
|
||||
});
|
||||
const [estimatorsFilter, setEstimatiorsFilter] = useLocalStorage(
|
||||
"estimators",
|
||||
[]
|
||||
);
|
||||
|
||||
const estimators = useMemo(() => {
|
||||
return _.uniq([
|
||||
...data
|
||||
.filter((d) => d.__typename === "appointments")
|
||||
.map((app) =>
|
||||
`${app.job?.est_ct_fn || ""} ${app.job?.est_ct_ln || ""}`.trim()
|
||||
)
|
||||
.filter((e) => e.length > 0),
|
||||
...bodyshop.md_estimators.map((e) =>
|
||||
`${e.est_ct_fn || ""} ${e.est_ct_ln || ""}`.trim()
|
||||
),
|
||||
]);
|
||||
}, [data, bodyshop.md_estimators]);
|
||||
|
||||
const filteredData = useMemo(() => {
|
||||
return data.filter(
|
||||
(d) =>
|
||||
return data.filter((d) => {
|
||||
const estFilter =
|
||||
d.__typename === "appointments"
|
||||
? estimatorsFilter.length === 0
|
||||
? true
|
||||
: !!estimatorsFilter.find(
|
||||
(e) =>
|
||||
e ===
|
||||
`${d.job?.est_ct_fn || ""} ${d.job?.est_ct_ln || ""}`.trim()
|
||||
)
|
||||
: true;
|
||||
|
||||
return (
|
||||
(d.block ||
|
||||
(filter.intake && d.isintake) ||
|
||||
(filter.manual && !d.isintake && d.block === false) ||
|
||||
@@ -50,9 +81,11 @@ export function ScheduleCalendarComponent({ data, refetch, bodyshop }) {
|
||||
!!d.employee)) &&
|
||||
(filter.ins_co_nm && filter.ins_co_nm.length > 0
|
||||
? filter.ins_co_nm.includes(d.job?.ins_co_nm)
|
||||
: true)
|
||||
);
|
||||
}, [data, filter]);
|
||||
: true) &&
|
||||
estFilter
|
||||
);
|
||||
});
|
||||
}, [data, filter, estimatorsFilter]);
|
||||
|
||||
return (
|
||||
<Row gutter={[16, 16]}>
|
||||
@@ -63,6 +96,21 @@ export function ScheduleCalendarComponent({ data, refetch, bodyshop }) {
|
||||
extra={
|
||||
<Space wrap>
|
||||
<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
|
||||
style={{ minWidth: "15rem" }}
|
||||
mode="multiple"
|
||||
|
||||
@@ -16,6 +16,8 @@ import ShopInfoROStatusComponent from "./shop-info.rostatus.component";
|
||||
import ShopInfoSchedulingComponent from "./shop-info.scheduling.component";
|
||||
import ShopInfoSpeedPrint from "./shop-info.speedprint.component";
|
||||
import ShopInfoTaskPresets from "./shop-info.task-presets.component";
|
||||
import { useHistory, useLocation } from "react-router-dom";
|
||||
import queryString from "query-string";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
@@ -32,6 +34,10 @@ export function ShopInfoComponent({ bodyshop, form, saveLoading }) {
|
||||
bodyshop.imexshopid
|
||||
);
|
||||
const { t } = useTranslation();
|
||||
const history = useHistory();
|
||||
const location = useLocation();
|
||||
const search = queryString.parse(location.search);
|
||||
|
||||
return (
|
||||
<Card
|
||||
extra={
|
||||
@@ -44,7 +50,12 @@ export function ShopInfoComponent({ bodyshop, form, saveLoading }) {
|
||||
</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")}>
|
||||
<ShopInfoGeneral form={form} />
|
||||
</Tabs.TabPane>
|
||||
|
||||
@@ -24,9 +24,13 @@ const timeZonesList = momentTZ.tz.names();
|
||||
|
||||
export default function ShopInfoGeneral({ form }) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<div>
|
||||
<LayoutFormRow header={t("bodyshop.labels.businessinformation")}>
|
||||
<LayoutFormRow
|
||||
header={t("bodyshop.labels.businessinformation")}
|
||||
id="businessinformation"
|
||||
>
|
||||
<Form.Item
|
||||
label={t("bodyshop.fields.shopname")}
|
||||
name="shopname"
|
||||
@@ -155,7 +159,10 @@ export default function ShopInfoGeneral({ form }) {
|
||||
<InputNumber min={0} />
|
||||
</Form.Item>
|
||||
</LayoutFormRow>
|
||||
<LayoutFormRow header={t("bodyshop.labels.accountingsetup")}>
|
||||
<LayoutFormRow
|
||||
header={t("bodyshop.labels.accountingsetup")}
|
||||
id="accountingsetup"
|
||||
>
|
||||
<Form.Item
|
||||
label={t("bodyshop.labels.qbo")}
|
||||
valuePropName="checked"
|
||||
@@ -386,7 +393,10 @@ export default function ShopInfoGeneral({ form }) {
|
||||
<Select mode="tags" />
|
||||
</Form.Item>
|
||||
</LayoutFormRow>
|
||||
<LayoutFormRow header={t("bodyshop.labels.scoreboardsetup")}>
|
||||
<LayoutFormRow
|
||||
header={t("bodyshop.labels.scoreboardsetup")}
|
||||
id="scoreboardsetup"
|
||||
>
|
||||
<Form.Item
|
||||
label={t("bodyshop.fields.dailypainttarget")}
|
||||
name={["scoreboard_target", "dailyPaintTarget"]}
|
||||
@@ -445,7 +455,10 @@ export default function ShopInfoGeneral({ form }) {
|
||||
<InputNumber min={1} precision={1} />
|
||||
</Form.Item>
|
||||
</LayoutFormRow>
|
||||
<LayoutFormRow header={t("bodyshop.labels.systemsettings")}>
|
||||
<LayoutFormRow
|
||||
header={t("bodyshop.labels.systemsettings")}
|
||||
id="systemsettings"
|
||||
>
|
||||
<Form.Item
|
||||
name={["md_referral_sources"]}
|
||||
label={t("bodyshop.fields.md_referral_sources")}
|
||||
@@ -655,7 +668,11 @@ export default function ShopInfoGeneral({ form }) {
|
||||
<Input />
|
||||
</Form.Item>
|
||||
</LayoutFormRow>
|
||||
<LayoutFormRow grow header={t("bodyshop.labels.messagingpresets")}>
|
||||
<LayoutFormRow
|
||||
grow
|
||||
header={t("bodyshop.labels.messagingpresets")}
|
||||
id="messagingpresets"
|
||||
>
|
||||
<Form.List name={["md_messaging_presets"]}>
|
||||
{(fields, { add, remove, move }) => {
|
||||
return (
|
||||
@@ -720,7 +737,11 @@ export default function ShopInfoGeneral({ form }) {
|
||||
}}
|
||||
</Form.List>
|
||||
</LayoutFormRow>
|
||||
<LayoutFormRow grow header={t("bodyshop.labels.notespresets")}>
|
||||
<LayoutFormRow
|
||||
grow
|
||||
header={t("bodyshop.labels.notespresets")}
|
||||
id="notespresets"
|
||||
>
|
||||
<Form.List name={["md_notes_presets"]}>
|
||||
{(fields, { add, remove, move }) => {
|
||||
return (
|
||||
@@ -785,7 +806,11 @@ export default function ShopInfoGeneral({ form }) {
|
||||
}}
|
||||
</Form.List>
|
||||
</LayoutFormRow>
|
||||
<LayoutFormRow grow header={t("bodyshop.labels.partslocations")}>
|
||||
<LayoutFormRow
|
||||
grow
|
||||
header={t("bodyshop.labels.partslocations")}
|
||||
id="partslocations"
|
||||
>
|
||||
<Form.List name={["md_parts_locations"]}>
|
||||
{(fields, { add, remove, move }) => {
|
||||
return (
|
||||
@@ -839,7 +864,11 @@ export default function ShopInfoGeneral({ form }) {
|
||||
}}
|
||||
</Form.List>
|
||||
</LayoutFormRow>
|
||||
<LayoutFormRow grow header={t("bodyshop.labels.insurancecos")}>
|
||||
<LayoutFormRow
|
||||
grow
|
||||
header={t("bodyshop.labels.insurancecos")}
|
||||
id="insurancecos"
|
||||
>
|
||||
<Form.List name={["md_ins_cos"]}>
|
||||
{(fields, { add, remove, move }) => {
|
||||
return (
|
||||
@@ -935,7 +964,11 @@ export default function ShopInfoGeneral({ form }) {
|
||||
}}
|
||||
</Form.List>
|
||||
</LayoutFormRow>
|
||||
<LayoutFormRow grow header={t("bodyshop.labels.estimators")}>
|
||||
<LayoutFormRow
|
||||
grow
|
||||
header={t("bodyshop.labels.estimators")}
|
||||
id="estimators"
|
||||
>
|
||||
<Form.List name={["md_estimators"]}>
|
||||
{(fields, { add, remove, move }) => {
|
||||
return (
|
||||
@@ -1024,7 +1057,11 @@ export default function ShopInfoGeneral({ form }) {
|
||||
}}
|
||||
</Form.List>
|
||||
</LayoutFormRow>
|
||||
<LayoutFormRow grow header={t("bodyshop.labels.filehandlers")}>
|
||||
<LayoutFormRow
|
||||
grow
|
||||
header={t("bodyshop.labels.filehandlers")}
|
||||
id="filehandlers"
|
||||
>
|
||||
<Form.List name={["md_filehandlers"]}>
|
||||
{(fields, { add, remove, move }) => {
|
||||
return (
|
||||
@@ -1106,7 +1143,11 @@ export default function ShopInfoGeneral({ form }) {
|
||||
}}
|
||||
</Form.List>
|
||||
</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"]}>
|
||||
{(fields, { add, remove, move }) => {
|
||||
return (
|
||||
@@ -1223,7 +1264,11 @@ export default function ShopInfoGeneral({ form }) {
|
||||
}}
|
||||
</Form.List>
|
||||
</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"]}>
|
||||
{(fields, { add, remove, move }) => {
|
||||
return (
|
||||
@@ -1404,7 +1449,11 @@ export default function ShopInfoGeneral({ form }) {
|
||||
}}
|
||||
</Form.List>
|
||||
</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"]}>
|
||||
{(fields, { add, remove, move }) => {
|
||||
return (
|
||||
@@ -1470,7 +1519,11 @@ export default function ShopInfoGeneral({ form }) {
|
||||
}}
|
||||
</Form.List>
|
||||
</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"]}>
|
||||
{(fields, { add, remove, move }) => {
|
||||
return (
|
||||
|
||||
@@ -20,7 +20,10 @@ export default function ShopInfoIntakeChecklistComponent({ form }) {
|
||||
const TemplateListGenerated = TemplateList();
|
||||
return (
|
||||
<div>
|
||||
<LayoutFormRow header={t("bodyshop.labels.intakechecklist")}>
|
||||
<LayoutFormRow
|
||||
header={t("bodyshop.labels.intakechecklist")}
|
||||
id="intakechecklist"
|
||||
>
|
||||
<Form.List name={["intakechecklist", "form"]}>
|
||||
{(fields, { add, remove, move }) => {
|
||||
return (
|
||||
@@ -188,7 +191,10 @@ export default function ShopInfoIntakeChecklistComponent({ form }) {
|
||||
</Form.Item>
|
||||
</SelectorDiv>
|
||||
|
||||
<LayoutFormRow header={t("bodyshop.labels.deliverchecklist")}>
|
||||
<LayoutFormRow
|
||||
header={t("bodyshop.labels.deliverchecklist")}
|
||||
id="deliverchecklist"
|
||||
>
|
||||
<Form.List name={["deliverchecklist", "form"]}>
|
||||
{(fields, { add, remove, move }) => {
|
||||
return (
|
||||
|
||||
@@ -95,7 +95,6 @@ export function ShopInfoResponsibilityCenterComponent({ bodyshop, form }) {
|
||||
{form.getFieldValue("pbs_serialnumber")}
|
||||
</DataLabel>
|
||||
)}
|
||||
|
||||
<LayoutFormRow>
|
||||
<Form.Item
|
||||
label={t("bodyshop.fields.dms.default_journal")}
|
||||
@@ -315,7 +314,10 @@ export function ShopInfoResponsibilityCenterComponent({ bodyshop, form }) {
|
||||
</DataLabel>
|
||||
</>
|
||||
)}
|
||||
<LayoutFormRow header={t("bodyshop.labels.responsibilitycenters.costs")}>
|
||||
<LayoutFormRow
|
||||
header={t("bodyshop.labels.responsibilitycenters.costs")}
|
||||
id="costs"
|
||||
>
|
||||
<Form.List name={["md_responsibility_centers", "costs"]}>
|
||||
{(fields, { add, remove }) => {
|
||||
return (
|
||||
@@ -462,6 +464,7 @@ export function ShopInfoResponsibilityCenterComponent({ bodyshop, form }) {
|
||||
|
||||
<LayoutFormRow
|
||||
header={t("bodyshop.labels.responsibilitycenters.profits")}
|
||||
id="profits"
|
||||
>
|
||||
<Form.List name={["md_responsibility_centers", "profits"]}>
|
||||
{(fields, { add, remove }) => {
|
||||
@@ -601,7 +604,7 @@ export function ShopInfoResponsibilityCenterComponent({ bodyshop, form }) {
|
||||
{fields.map((field, index) => (
|
||||
<Form.Item key={field.key}>
|
||||
<div>
|
||||
<LayoutFormRow>
|
||||
<LayoutFormRow id="mappingname">
|
||||
<Form.Item
|
||||
label={t("bodyshop.fields.dms.mappingname")}
|
||||
key={`${index}name`}
|
||||
@@ -631,6 +634,7 @@ export function ShopInfoResponsibilityCenterComponent({ bodyshop, form }) {
|
||||
</LayoutFormRow>
|
||||
<LayoutFormRow
|
||||
header={t("bodyshop.labels.defaultcostsmapping")}
|
||||
id="defaultcostsmapping"
|
||||
>
|
||||
<Form.Item
|
||||
label={t(
|
||||
@@ -4088,6 +4092,7 @@ export function ShopInfoResponsibilityCenterComponent({ bodyshop, form }) {
|
||||
|
||||
<LayoutFormRow
|
||||
header={t("bodyshop.labels.responsibilitycenters.tax_accounts")}
|
||||
id="tax_accounts"
|
||||
>
|
||||
<Form.Item
|
||||
label={t("bodyshop.fields.responsibilitycenters.federal_tax")}
|
||||
@@ -4202,7 +4207,7 @@ export function ShopInfoResponsibilityCenterComponent({ bodyshop, form }) {
|
||||
</Form.Item>
|
||||
</LayoutFormRow>
|
||||
{DmsAp.treatment === "on" && (
|
||||
<LayoutFormRow>
|
||||
<LayoutFormRow id="federal_tax_itc">
|
||||
<Form.Item
|
||||
label={t("bodyshop.fields.responsibilitycenters.federal_tax_itc")}
|
||||
rules={[
|
||||
@@ -4316,7 +4321,7 @@ export function ShopInfoResponsibilityCenterComponent({ bodyshop, form }) {
|
||||
</Form.Item>
|
||||
</LayoutFormRow>
|
||||
)}
|
||||
<LayoutFormRow>
|
||||
<LayoutFormRow id="state_tax">
|
||||
<Form.Item
|
||||
label={t("bodyshop.fields.responsibilitycenters.state_tax")}
|
||||
rules={[
|
||||
@@ -4438,7 +4443,8 @@ export function ShopInfoResponsibilityCenterComponent({ bodyshop, form }) {
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<LayoutFormRow>
|
||||
|
||||
<LayoutFormRow id="local_tax">
|
||||
<Form.Item
|
||||
label={t("bodyshop.fields.responsibilitycenters.local_tax")}
|
||||
rules={[
|
||||
@@ -4536,7 +4542,7 @@ export function ShopInfoResponsibilityCenterComponent({ bodyshop, form }) {
|
||||
<InputNumber precision={2} />
|
||||
</Form.Item>
|
||||
</LayoutFormRow>
|
||||
<LayoutFormRow header={<div>AR</div>}>
|
||||
<LayoutFormRow header={<div>AR</div>} id="AR">
|
||||
{/* <Form.Item
|
||||
label={t("bodyshop.fields.responsibilitycenters.ar")}
|
||||
rules={[
|
||||
@@ -4600,7 +4606,10 @@ export function ShopInfoResponsibilityCenterComponent({ bodyshop, form }) {
|
||||
</LayoutFormRow>
|
||||
|
||||
{DmsAp.treatment === "on" && (
|
||||
<LayoutFormRow header={t("bodyshop.fields.responsibilitycenters.ap")}>
|
||||
<LayoutFormRow
|
||||
header={t("bodyshop.fields.responsibilitycenters.ap")}
|
||||
id="ap"
|
||||
>
|
||||
{/* <Form.Item
|
||||
label={t("bodyshop.fields.responsibilitycenters.ap")}
|
||||
rules={[
|
||||
@@ -4663,7 +4672,7 @@ export function ShopInfoResponsibilityCenterComponent({ bodyshop, form }) {
|
||||
</Form.Item>
|
||||
</LayoutFormRow>
|
||||
)}
|
||||
<LayoutFormRow header={<div>Refund</div>}>
|
||||
<LayoutFormRow header={<div>Refund</div>} id="refund">
|
||||
{/* <Form.Item
|
||||
label={t("bodyshop.fields.responsibilitycenters.refund")}
|
||||
rules={[
|
||||
@@ -4726,7 +4735,10 @@ export function ShopInfoResponsibilityCenterComponent({ bodyshop, form }) {
|
||||
</Form.Item>
|
||||
</LayoutFormRow>
|
||||
{Qb_Multi_Ar.treatment === "on" && (
|
||||
<LayoutFormRow header={<div>Multiple Payers Item</div>}>
|
||||
<LayoutFormRow
|
||||
header={<div>Multiple Payers Item</div>}
|
||||
id="accountitem"
|
||||
>
|
||||
<Form.Item
|
||||
label={t("bodyshop.fields.responsibilitycenter_accountitem")}
|
||||
rules={[
|
||||
@@ -4754,7 +4766,7 @@ export function ShopInfoResponsibilityCenterComponent({ bodyshop, form }) {
|
||||
<div>
|
||||
{fields.map((field, index) => (
|
||||
<Form.Item key={field.key}>
|
||||
<LayoutFormRow>
|
||||
<LayoutFormRow id="sales_tax_codes">
|
||||
<Form.Item
|
||||
label={t(
|
||||
"bodyshop.fields.responsibilitycenters.sales_tax_codes.description"
|
||||
|
||||
@@ -44,7 +44,7 @@ export function ShopInfoROStatusComponent({ bodyshop, form }) {
|
||||
};
|
||||
|
||||
return (
|
||||
<SelectorDiv>
|
||||
<SelectorDiv id="jobstatus">
|
||||
<Form.Item
|
||||
name={["md_ro_statuses", "statuses"]}
|
||||
label={t("bodyshop.labels.alljobstatuses")}
|
||||
@@ -322,6 +322,7 @@ export function ShopInfoROStatusComponent({ bodyshop, form }) {
|
||||
<LayoutFormRow
|
||||
grow
|
||||
header={t("bodyshop.fields.statuses.production_colors")}
|
||||
id="production_colors"
|
||||
>
|
||||
<Form.List name={["md_ro_statuses", "production_colors"]}>
|
||||
{(fields, { add, remove, move }) => {
|
||||
|
||||
@@ -92,7 +92,7 @@ export default function ShopInfoSchedulingComponent({ form }) {
|
||||
</Form.Item>
|
||||
</LayoutFormRow>
|
||||
<Divider orientation="left">{t("bodyshop.labels.workingdays")}</Divider>
|
||||
<Space wrap size="large">
|
||||
<Space wrap size="large" id="workingdays">
|
||||
<Form.Item
|
||||
label={t("general.labels.sunday")}
|
||||
name={["workingdays", "sunday"]}
|
||||
@@ -143,7 +143,7 @@ export default function ShopInfoSchedulingComponent({ form }) {
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
</Space>
|
||||
<LayoutFormRow header={t("bodyshop.labels.apptcolors")}>
|
||||
<LayoutFormRow header={t("bodyshop.labels.apptcolors")} id="apptcolors">
|
||||
<Form.List name={["appt_colors"]}>
|
||||
{(fields, { add, remove, move }) => {
|
||||
return (
|
||||
@@ -208,7 +208,7 @@ export default function ShopInfoSchedulingComponent({ form }) {
|
||||
}}
|
||||
</Form.List>
|
||||
</LayoutFormRow>
|
||||
<LayoutFormRow header={t("bodyshop.labels.ssbuckets")}>
|
||||
<LayoutFormRow header={t("bodyshop.labels.ssbuckets")} id="ssbuckets">
|
||||
<Form.List name={["ssbuckets"]}>
|
||||
{(fields, { add, remove, move }) => {
|
||||
return (
|
||||
|
||||
@@ -97,8 +97,9 @@ export function TimeTicketModalComponent({
|
||||
]}
|
||||
>
|
||||
<JobSearchSelect
|
||||
convertedOnly={!bodyshop.tt_allow_post_to_invoiced}
|
||||
convertedOnly={true}
|
||||
notExported={!bodyshop.tt_allow_post_to_invoiced}
|
||||
notInvoiced={!bodyshop.tt_allow_post_to_invoiced}
|
||||
/>
|
||||
</Form.Item>
|
||||
)}
|
||||
|
||||
@@ -57,6 +57,8 @@ export const QUERY_ALL_ACTIVE_APPOINTMENTS = gql`
|
||||
v_model_yr
|
||||
v_make_desc
|
||||
v_model_desc
|
||||
est_ct_fn
|
||||
est_ct_ln
|
||||
labhrs: joblines_aggregate(
|
||||
where: { mod_lbr_ty: { _neq: "LAR" }, removed: { _eq: false } }
|
||||
) {
|
||||
|
||||
@@ -1,35 +1,36 @@
|
||||
import { gql } from "@apollo/client";
|
||||
|
||||
// export const CONVERSATION_LIST_SUBSCRIPTION = gql`
|
||||
// subscription CONVERSATION_LIST_SUBSCRIPTION {
|
||||
// conversations(
|
||||
// order_by: { updated_at: desc }
|
||||
// limit: 50
|
||||
// where: { archived: { _eq: false } }
|
||||
// ) {
|
||||
// phone_num
|
||||
// id
|
||||
// updated_at
|
||||
// unreadcnt
|
||||
// messages_aggregate(
|
||||
// where: { read: { _eq: false }, isoutbound: { _eq: false } }
|
||||
// ) {
|
||||
// aggregate {
|
||||
// count
|
||||
// }
|
||||
// }
|
||||
// job_conversations {
|
||||
// job {
|
||||
// id
|
||||
// ro_number
|
||||
// ownr_fn
|
||||
// ownr_ln
|
||||
// ownr_co_nm
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// `;
|
||||
export const CONVERSATION_LIST_SUBSCRIPTION = gql`
|
||||
subscription CONVERSATION_LIST_SUBSCRIPTION($offset: Int!) {
|
||||
conversations(
|
||||
order_by: { updated_at: desc }
|
||||
limit: 1
|
||||
offset: $offset
|
||||
where: { archived: { _eq: false } }
|
||||
) {
|
||||
phone_num
|
||||
id
|
||||
updated_at
|
||||
unreadcnt
|
||||
messages_aggregate(
|
||||
where: { read: { _eq: false }, isoutbound: { _eq: false } }
|
||||
) {
|
||||
aggregate {
|
||||
count
|
||||
}
|
||||
}
|
||||
job_conversations {
|
||||
job {
|
||||
id
|
||||
ro_number
|
||||
ownr_fn
|
||||
ownr_ln
|
||||
ownr_co_nm
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const UNREAD_CONVERSATION_COUNT = gql`
|
||||
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`
|
||||
query CONVERSATION_LIST_QUERY {
|
||||
query CONVERSATION_LIST_QUERY($offset: Int!) {
|
||||
conversations(
|
||||
order_by: { updated_at: desc }
|
||||
limit: 50
|
||||
offset: $offset
|
||||
where: { archived: { _eq: false } }
|
||||
) {
|
||||
phone_num
|
||||
|
||||
@@ -1076,6 +1076,9 @@ export const UPDATE_JOB = gql`
|
||||
lbr_adjustments
|
||||
suspended
|
||||
queued_for_parts
|
||||
scheduled_completion
|
||||
actual_in
|
||||
date_repairstarted
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import { Tabs } from "antd";
|
||||
import React, { useEffect } from "react";
|
||||
import { useHistory, useLocation } from "react-router-dom";
|
||||
import queryString from "query-string";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import ShopEmployeesContainer from "../../components/shop-employees/shop-employees.container";
|
||||
import ShopInfoContainer from "../../components/shop-info/shop-info.container";
|
||||
@@ -26,6 +28,9 @@ const mapDispatchToProps = (dispatch) => ({
|
||||
|
||||
export function ShopPage({ bodyshop, setSelectedHeader, setBreadcrumbs }) {
|
||||
const { t } = useTranslation();
|
||||
const history = useHistory();
|
||||
const search = queryString.parse(useLocation().search);
|
||||
|
||||
useEffect(() => {
|
||||
document.title = t("titles.shop");
|
||||
setSelectedHeader("shop");
|
||||
@@ -39,7 +44,10 @@ export function ShopPage({ bodyshop, setSelectedHeader, setBreadcrumbs }) {
|
||||
|
||||
return (
|
||||
<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">
|
||||
<ShopInfoContainer />
|
||||
</Tabs.TabPane>
|
||||
|
||||
@@ -2217,10 +2217,13 @@
|
||||
"new": "New Payment",
|
||||
"signup": "Please contact support to sign up for electronic payments.",
|
||||
"title": "Payments",
|
||||
"totalpayments": "Total Payments"
|
||||
"totalpayments": "Total Payments",
|
||||
"markexported": "Mark Exported",
|
||||
"markforreexport": "Mark for Re-export"
|
||||
},
|
||||
"successes": {
|
||||
"exported": "Payment(s) exported successfully.",
|
||||
"markreexported": "Payment marked for re-export successfully",
|
||||
"markexported": "Payment(s) marked exported.",
|
||||
"payment": "Payment created successfully. ",
|
||||
"stripe": "Credit card transaction charged successfully."
|
||||
@@ -2316,7 +2319,7 @@
|
||||
"folder_label_multiple": "Folder Label - Multi",
|
||||
"glass_express_checklist": "Glass Express Checklist",
|
||||
"guarantee": "Repair Guarantee",
|
||||
"individual_job_note": "Job Note RO # {{ro_number}}",
|
||||
"individual_job_note": "RO Job Note",
|
||||
"invoice_customer_payable": "Invoice (Customer Payable)",
|
||||
"invoice_total_payable": "Invoice (Total Payable)",
|
||||
"iou_form": "IOU Form",
|
||||
@@ -2332,7 +2335,8 @@
|
||||
"mechanical_authorization": "Mechanical Authorization",
|
||||
"mpi_animal_checklist": "MPI - Animal Checklist",
|
||||
"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",
|
||||
"parts_invoice_label_single": "Parts Label Single",
|
||||
"parts_label_multiple": "Parts Label - Multi",
|
||||
@@ -2394,6 +2398,7 @@
|
||||
},
|
||||
"subjects": {
|
||||
"jobs": {
|
||||
"individual_job_note": "Job Note RO: {{ro_number}}",
|
||||
"parts_order": "Parts Order PO: {{ro_number}} - {{name}}",
|
||||
"sublet_order": "Sublet Order PO: {{ro_number}} - {{name}}"
|
||||
}
|
||||
@@ -2619,6 +2624,7 @@
|
||||
"atssummary": "ATS Summary",
|
||||
"employeevacation": "Employee Vacations",
|
||||
"ins_co_nm_filter": "Filter by Insurance Company",
|
||||
"estimators": "Filter by Writer/Customer Rep.",
|
||||
"intake": "Intake Events",
|
||||
"manual": "Manual Events",
|
||||
"manualevent": "Add Manual Event"
|
||||
|
||||
@@ -2333,6 +2333,7 @@
|
||||
"mpi_animal_checklist": "",
|
||||
"mpi_eglass_auth": "",
|
||||
"mpi_final_acct_sheet": "",
|
||||
"mpi_final_repair_acct_sheet": "",
|
||||
"paint_grid": "",
|
||||
"parts_invoice_label_single": "",
|
||||
"parts_label_multiple": "",
|
||||
@@ -2394,6 +2395,7 @@
|
||||
},
|
||||
"subjects": {
|
||||
"jobs": {
|
||||
"individual_job_note": "",
|
||||
"parts_order": "",
|
||||
"sublet_order": ""
|
||||
}
|
||||
|
||||
@@ -2333,6 +2333,7 @@
|
||||
"mpi_animal_checklist": "",
|
||||
"mpi_eglass_auth": "",
|
||||
"mpi_final_acct_sheet": "",
|
||||
"mpi_final_repair_acct_sheet": "",
|
||||
"paint_grid": "",
|
||||
"parts_invoice_label_single": "",
|
||||
"parts_label_multiple": "",
|
||||
@@ -2394,6 +2395,7 @@
|
||||
},
|
||||
"subjects": {
|
||||
"jobs": {
|
||||
"individual_job_note": "",
|
||||
"parts_order": "",
|
||||
"sublet_order": ""
|
||||
}
|
||||
|
||||
@@ -3,7 +3,10 @@ import { setContext } from "@apollo/client/link/context";
|
||||
import { HttpLink } from "@apollo/client/link/http"; //"apollo-link-http";
|
||||
import { RetryLink } from "@apollo/client/link/retry";
|
||||
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 apolloLogger from "apollo-link-logger";
|
||||
//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({
|
||||
link: ApolloLink.from(middlewares),
|
||||
|
||||
@@ -421,6 +421,17 @@ export const TemplateList = (type, context) => {
|
||||
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: {
|
||||
title: i18n.t("printcenter.jobs.mpi_eglass_auth"),
|
||||
description: "",
|
||||
@@ -542,7 +553,7 @@ export const TemplateList = (type, context) => {
|
||||
title: i18n.t("printcenter.jobs.individual_job_note"),
|
||||
description: "",
|
||||
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) || "",
|
||||
}),
|
||||
disabled: false,
|
||||
|
||||
@@ -11168,6 +11168,11 @@ react-image-lightbox@^5.1.4:
|
||||
prop-types "^15.7.2"
|
||||
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:
|
||||
version "16.13.1"
|
||||
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
|
||||
|
||||
@@ -224,7 +224,7 @@ async function CdkSelectedCustomer(socket, selectedCustomerId) {
|
||||
} finally {
|
||||
//Ensure we always insert logEvents
|
||||
//GQL to insert logevents.
|
||||
|
||||
|
||||
CdkBase.createLogEvent(
|
||||
socket,
|
||||
"DEBUG",
|
||||
@@ -432,7 +432,7 @@ async function QueryDmsCustomerById(socket, JobData, CustomerId) {
|
||||
|
||||
async function QueryDmsCustomerByName(socket, JobData) {
|
||||
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_ln},${JobData.ownr_fn}`
|
||||
).replace(replaceSpecialRegex, "");
|
||||
@@ -642,7 +642,8 @@ async function InsertDmsCustomer(socket, newCustomerNumber) {
|
||||
.toUpperCase(),
|
||||
middleName: null,
|
||||
nameType:
|
||||
socket.JobData.ownr_co_nm && socket.JobData.ownr_co_nm
|
||||
socket.JobData.ownr_co_nm &&
|
||||
String(socket.JobData.ownr_co_nm).trim() !== ""
|
||||
? "Business"
|
||||
: "Person",
|
||||
suffix: null,
|
||||
@@ -728,7 +729,9 @@ async function InsertDmsVehicle(socket) {
|
||||
deliveryDate: moment()
|
||||
// .tz(socket.JobData.bodyshop.timezone)
|
||||
.format("YYYYMMDD"),
|
||||
licensePlateNo: socket.JobData.plate_no,
|
||||
licensePlateNo: String(socket.JobData.plate_no)
|
||||
.replace(/([^\w]|_)/g, "")
|
||||
.toUpperCase(),
|
||||
make: socket.txEnvelope.dms_make,
|
||||
modelAbrev: socket.txEnvelope.dms_model,
|
||||
modelYear: socket.JobData.v_model_yr,
|
||||
|
||||
@@ -792,10 +792,11 @@ const CreateCosts = (job) => {
|
||||
job.bodyshop.md_responsibility_centers.defaults.costs.MAPA
|
||||
].add(
|
||||
Dinero({
|
||||
amount:
|
||||
amount: Math.round(
|
||||
(job.bodyshop.jc_hourly_rates &&
|
||||
job.bodyshop.jc_hourly_rates.mapa * 100) ||
|
||||
0,
|
||||
0
|
||||
),
|
||||
}).multiply(job.job_totals.rates.mapa.hours)
|
||||
);
|
||||
}
|
||||
@@ -806,10 +807,11 @@ const CreateCosts = (job) => {
|
||||
job.bodyshop.md_responsibility_centers.defaults.costs.MAPA
|
||||
].add(
|
||||
Dinero({
|
||||
amount:
|
||||
amount: Math.round(
|
||||
(job.bodyshop.jc_hourly_rates &&
|
||||
job.bodyshop.jc_hourly_rates.mapa * 100) ||
|
||||
0,
|
||||
0
|
||||
),
|
||||
}).multiply(job.job_totals.rates.mapa.hours)
|
||||
);
|
||||
}
|
||||
@@ -829,10 +831,11 @@ const CreateCosts = (job) => {
|
||||
job.bodyshop.md_responsibility_centers.defaults.costs.MASH
|
||||
].add(
|
||||
Dinero({
|
||||
amount:
|
||||
amount: Math.round(
|
||||
(job.bodyshop.jc_hourly_rates &&
|
||||
job.bodyshop.jc_hourly_rates.mash * 100) ||
|
||||
0,
|
||||
0
|
||||
),
|
||||
}).multiply(job.job_totals.rates.mash.hours)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -642,10 +642,11 @@ function GenerateCostingData(job) {
|
||||
job.bodyshop.md_responsibility_centers.defaults.costs.MAPA
|
||||
].add(
|
||||
Dinero({
|
||||
amount:
|
||||
amount: Math.round(
|
||||
(job.bodyshop.jc_hourly_rates &&
|
||||
job.bodyshop.jc_hourly_rates.mapa * 100) ||
|
||||
0,
|
||||
0
|
||||
),
|
||||
}).multiply(materialsHours.mapaHrs)
|
||||
);
|
||||
}
|
||||
@@ -656,10 +657,11 @@ function GenerateCostingData(job) {
|
||||
job.bodyshop.md_responsibility_centers.defaults.costs.MAPA
|
||||
].add(
|
||||
Dinero({
|
||||
amount:
|
||||
amount: Math.round(
|
||||
(job.bodyshop.jc_hourly_rates &&
|
||||
job.bodyshop.jc_hourly_rates.mapa * 100) ||
|
||||
0,
|
||||
0
|
||||
),
|
||||
}).multiply(materialsHours.mapaHrs)
|
||||
);
|
||||
}
|
||||
@@ -680,10 +682,11 @@ function GenerateCostingData(job) {
|
||||
job.bodyshop.md_responsibility_centers.defaults.costs.MASH
|
||||
].add(
|
||||
Dinero({
|
||||
amount:
|
||||
amount: Math.round(
|
||||
(job.bodyshop.jc_hourly_rates &&
|
||||
job.bodyshop.jc_hourly_rates.mash * 100) ||
|
||||
0,
|
||||
0
|
||||
),
|
||||
}).multiply(materialsHours.mashHrs)
|
||||
);
|
||||
}
|
||||
|
||||
11
yarn.lock
11
yarn.lock
@@ -4312,14 +4312,9 @@ tslib@^1.11.1:
|
||||
integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==
|
||||
|
||||
tslib@^2.0.1, tslib@^2.1.0:
|
||||
|
||||
tslib@^2.3.1, tslib@^2.5.0:
|
||||
version "2.5.0"
|
||||
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==
|
||||
version "2.5.2"
|
||||
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.5.2.tgz#1b6f07185c881557b0ffa84b111a0106989e8338"
|
||||
integrity sha512-5svOrSA2w3iGFDs1HibEVBGbDrAY82bFQ3HZ3ixB+88nsbsWQoKqDRb5UBYAUPEzbBn6dAp5gRNXglySbx1MlA==
|
||||
|
||||
tslib@^2.3.1, tslib@^2.5.0:
|
||||
version "2.5.0"
|
||||
|
||||
Reference in New Issue
Block a user