Compare commits
32 Commits
feature/IO
...
feature/IO
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ea54820bc0 | ||
|
|
7581b8634e | ||
|
|
9b485bfe45 | ||
|
|
91279c27fe | ||
|
|
3812a0650e | ||
|
|
b1c5bbb01f | ||
|
|
f440a2b022 | ||
|
|
6262b3ff83 | ||
|
|
307c77b30c | ||
|
|
f1d7a98fe8 | ||
|
|
be259317f9 | ||
|
|
046d104bfa | ||
|
|
9c693a2b74 | ||
|
|
2be0f3de09 | ||
|
|
de8c2cd5a2 | ||
|
|
b791f9846f | ||
|
|
daa7631056 | ||
|
|
14b8a2daef | ||
|
|
90b38d817d | ||
|
|
ace48e2890 | ||
|
|
fb810be5d5 | ||
|
|
50230e9f50 | ||
|
|
86e14967ca | ||
|
|
c45c3b4037 | ||
|
|
c4c11528b9 | ||
|
|
1f9c4e92f1 | ||
|
|
371e148e09 | ||
|
|
33af544ded | ||
|
|
6b8d0ec91c | ||
|
|
043c44ed51 | ||
|
|
6d5dbf3145 | ||
|
|
6d8463265c |
@@ -145,7 +145,7 @@
|
||||
|
||||
//Update row highlighting on production board.
|
||||
.ant-table-tbody > tr.ant-table-row:hover > td {
|
||||
background: #eaeaea !important;
|
||||
background: #e7f3ff !important;
|
||||
}
|
||||
|
||||
.job-line-manual {
|
||||
|
||||
@@ -1,13 +1,18 @@
|
||||
import { Badge, List, Tag } from "antd";
|
||||
import React, { useEffect } from "react";
|
||||
import React from "react";
|
||||
import { connect } from "react-redux";
|
||||
import {
|
||||
AutoSizer,
|
||||
CellMeasurer,
|
||||
CellMeasurerCache,
|
||||
List as VirtualizedList,
|
||||
} from "react-virtualized";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { setSelectedConversation } from "../../redux/messaging/messaging.actions";
|
||||
import { selectSelectedConversation } from "../../redux/messaging/messaging.selectors";
|
||||
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";
|
||||
|
||||
@@ -24,57 +29,66 @@ function ChatConversationListComponent({
|
||||
conversationList,
|
||||
selectedConversation,
|
||||
setSelectedConversation,
|
||||
subscribeToMoreConversations,
|
||||
loadMoreConversations,
|
||||
}) {
|
||||
useEffect(
|
||||
() => subscribeToMoreConversations(),
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
[]
|
||||
);
|
||||
const cache = new CellMeasurerCache({
|
||||
fixedWidth: true,
|
||||
defaultHeight: 60,
|
||||
});
|
||||
|
||||
const rowRenderer = ({ index, key, style }) => {
|
||||
const rowRenderer = ({ index, key, style, parent }) => {
|
||||
const item = conversationList[index];
|
||||
|
||||
return (
|
||||
<List.Item
|
||||
<CellMeasurer
|
||||
key={key}
|
||||
onClick={() => setSelectedConversation(item.id)}
|
||||
className={`chat-list-item ${
|
||||
item.id === selectedConversation
|
||||
? "chat-list-selected-conversation"
|
||||
: null
|
||||
}`}
|
||||
style={style}
|
||||
cache={cache}
|
||||
parent={parent}
|
||||
columnIndex={0}
|
||||
rowIndex={index}
|
||||
>
|
||||
<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}
|
||||
<List.Item
|
||||
onClick={() => setSelectedConversation(item.id)}
|
||||
className={`chat-list-item ${
|
||||
item.id === selectedConversation
|
||||
? "chat-list-selected-conversation"
|
||||
: null
|
||||
}`}
|
||||
style={style}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
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>
|
||||
<TimeAgoFormatter>{item.updated_at}</TimeAgoFormatter>
|
||||
</div>
|
||||
<Badge count={item.messages_aggregate.aggregate.count || 0} />
|
||||
</List.Item>
|
||||
<div style={{ 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>
|
||||
</CellMeasurer>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -86,7 +100,7 @@ function ChatConversationListComponent({
|
||||
height={height}
|
||||
width={width}
|
||||
rowCount={conversationList.length}
|
||||
rowHeight={60}
|
||||
rowHeight={cache.rowHeight}
|
||||
rowRenderer={rowRenderer}
|
||||
onScroll={({ scrollTop, scrollHeight, clientHeight }) => {
|
||||
if (scrollTop + clientHeight === scrollHeight) {
|
||||
|
||||
@@ -4,7 +4,7 @@ import {
|
||||
ShrinkOutlined,
|
||||
SyncOutlined,
|
||||
} from "@ant-design/icons";
|
||||
import { useLazyQuery, useSubscription } from "@apollo/client";
|
||||
import { useLazyQuery, useQuery } from "@apollo/client";
|
||||
import { Badge, Card, Col, Row, Space, Tag, Tooltip, Typography } from "antd";
|
||||
import React, { useCallback, useEffect, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
@@ -12,8 +12,7 @@ import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import {
|
||||
CONVERSATION_LIST_QUERY,
|
||||
CONVERSATION_LIST_SUBSCRIPTION,
|
||||
UNREAD_CONVERSATION_COUNT_SUBSCRIPTION,
|
||||
UNREAD_CONVERSATION_COUNT,
|
||||
} from "../../graphql/conversations.queries";
|
||||
import { toggleChatVisible } from "../../redux/messaging/messaging.actions";
|
||||
import {
|
||||
@@ -42,19 +41,20 @@ export function ChatPopupComponent({
|
||||
const { t } = useTranslation();
|
||||
const [pollInterval, setpollInterval] = useState(0);
|
||||
|
||||
const { data: unreadData } = useSubscription(
|
||||
UNREAD_CONVERSATION_COUNT_SUBSCRIPTION
|
||||
);
|
||||
|
||||
const [
|
||||
getConversations,
|
||||
{ loading, data, called, refetch, fetchMore, subscribeToMore },
|
||||
] = useLazyQuery(CONVERSATION_LIST_QUERY, {
|
||||
const { data: unreadData } = useQuery(UNREAD_CONVERSATION_COUNT, {
|
||||
fetchPolicy: "network-only",
|
||||
nextFetchPolicy: "network-only",
|
||||
skip: !chatVisible,
|
||||
...(pollInterval > 0 ? { pollInterval } : {}),
|
||||
});
|
||||
|
||||
const [getConversations, { loading, data, refetch, fetchMore }] =
|
||||
useLazyQuery(CONVERSATION_LIST_QUERY, {
|
||||
fetchPolicy: "network-only",
|
||||
nextFetchPolicy: "network-only",
|
||||
skip: !chatVisible,
|
||||
...(pollInterval > 0 ? { pollInterval } : {}),
|
||||
});
|
||||
|
||||
const fcmToken = sessionStorage.getItem("fcmtoken");
|
||||
|
||||
useEffect(() => {
|
||||
@@ -66,13 +66,13 @@ export function ChatPopupComponent({
|
||||
}, [fcmToken]);
|
||||
|
||||
useEffect(() => {
|
||||
if (called && chatVisible)
|
||||
if (chatVisible)
|
||||
getConversations({
|
||||
variables: {
|
||||
offset: 0,
|
||||
},
|
||||
});
|
||||
}, [chatVisible, called, getConversations]);
|
||||
}, [chatVisible, getConversations]);
|
||||
|
||||
const loadMoreConversations = useCallback(() => {
|
||||
if (data)
|
||||
@@ -119,43 +119,6 @@ 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>
|
||||
|
||||
@@ -0,0 +1,244 @@
|
||||
import {
|
||||
BranchesOutlined,
|
||||
ExclamationCircleFilled,
|
||||
PauseCircleOutlined,
|
||||
} from "@ant-design/icons";
|
||||
import { Card, Space, Table, Tooltip } from "antd";
|
||||
import moment from "moment";
|
||||
import React, { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Link } from "react-router-dom";
|
||||
import ChatOpenButton from "../../chat-open-button/chat-open-button.component";
|
||||
import OwnerNameDisplay from "../../owner-name-display/owner-name-display.component";
|
||||
import DashboardRefreshRequired from "../refresh-required.component";
|
||||
|
||||
export default function DashboardScheduledInToday({ data, ...cardProps }) {
|
||||
const { t } = useTranslation();
|
||||
const [state, setState] = useState({
|
||||
sortedInfo: {},
|
||||
});
|
||||
if (!data) return null;
|
||||
if (!data.scheduled_in_today)
|
||||
return <DashboardRefreshRequired {...cardProps} />;
|
||||
|
||||
const appt = []; // Flatten Data
|
||||
data.scheduled_in_today.forEach((item) => {
|
||||
if (item.job) {
|
||||
var i = {
|
||||
canceled: item.canceled,
|
||||
id: item.id,
|
||||
alt_transport: item.job.alt_transport,
|
||||
clm_no: item.job.clm_no,
|
||||
jobid: item.job.jobid,
|
||||
ins_co_nm: item.job.ins_co_nm,
|
||||
iouparent: item.job.iouparent,
|
||||
ownerid: item.job.ownerid,
|
||||
ownr_co_nm: item.job.ownr_co_nm,
|
||||
ownr_ea: item.job.ownr_ea,
|
||||
ownr_fn: item.job.ownr_fn,
|
||||
ownr_ln: item.job.ownr_ln,
|
||||
ownr_ph1: item.job.ownr_ph1,
|
||||
ownr_ph2: item.job.ownr_ph2,
|
||||
production_vars: item.job.production_vars,
|
||||
ro_number: item.job.ro_number,
|
||||
suspended: item.job.suspended,
|
||||
v_make_desc: item.job.v_make_desc,
|
||||
v_model_desc: item.job.v_model_desc,
|
||||
v_model_yr: item.job.v_model_yr,
|
||||
v_vin: item.job.v_vin,
|
||||
vehicleid: item.job.vehicleid,
|
||||
note: item.note,
|
||||
start: moment(item.start).format("hh:mm a"),
|
||||
title: item.title,
|
||||
};
|
||||
appt.push(i);
|
||||
}
|
||||
});
|
||||
appt.sort(function (a, b) {
|
||||
return new moment(a.start) - new moment(b.start);
|
||||
});
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: t("jobs.fields.ro_number"),
|
||||
dataIndex: "ro_number",
|
||||
key: "ro_number",
|
||||
render: (text, record) => (
|
||||
<Link
|
||||
to={"/manage/jobs/" + record.jobid}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
<Space>
|
||||
{record.ro_number || t("general.labels.na")}
|
||||
{record.production_vars && record.production_vars.alert ? (
|
||||
<ExclamationCircleFilled className="production-alert" />
|
||||
) : null}
|
||||
{record.suspended && (
|
||||
<PauseCircleOutlined style={{ color: "orangered" }} />
|
||||
)}
|
||||
{record.iouparent && (
|
||||
<Tooltip title={t("jobs.labels.iou")}>
|
||||
<BranchesOutlined style={{ color: "orangered" }} />
|
||||
</Tooltip>
|
||||
)}
|
||||
</Space>
|
||||
</Link>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: t("jobs.fields.owner"),
|
||||
dataIndex: "owner",
|
||||
key: "owner",
|
||||
ellipsis: true,
|
||||
responsive: ["md"],
|
||||
render: (text, record) => {
|
||||
return record.ownerid ? (
|
||||
<Link
|
||||
to={"/manage/owners/" + record.ownerid}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
<OwnerNameDisplay ownerObject={record} />
|
||||
</Link>
|
||||
) : (
|
||||
<span>
|
||||
<OwnerNameDisplay ownerObject={record} />
|
||||
</span>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: t("jobs.fields.ownr_ph1"),
|
||||
dataIndex: "ownr_ph1",
|
||||
key: "ownr_ph1",
|
||||
ellipsis: true,
|
||||
responsive: ["md"],
|
||||
render: (text, record) => (
|
||||
<ChatOpenButton phone={record.ownr_ph1} jobid={record.jobid} />
|
||||
),
|
||||
},
|
||||
{
|
||||
title: t("jobs.fields.ownr_ph2"),
|
||||
dataIndex: "ownr_ph2",
|
||||
key: "ownr_ph2",
|
||||
ellipsis: true,
|
||||
responsive: ["md"],
|
||||
render: (text, record) => (
|
||||
<ChatOpenButton phone={record.ownr_ph2} jobid={record.jobid} />
|
||||
),
|
||||
},
|
||||
{
|
||||
title: t("jobs.fields.ownr_ea"),
|
||||
dataIndex: "ownr_ea",
|
||||
key: "ownr_ea",
|
||||
ellipsis: true,
|
||||
responsive: ["md"],
|
||||
render: (text, record) => (
|
||||
<ChatOpenButton phone={record.ownr_ea} jobid={record.jobid} />
|
||||
),
|
||||
},
|
||||
{
|
||||
title: t("jobs.fields.vehicle"),
|
||||
dataIndex: "vehicle",
|
||||
key: "vehicle",
|
||||
ellipsis: true,
|
||||
render: (text, record) => {
|
||||
return record.vehicleid ? (
|
||||
<Link
|
||||
to={"/manage/vehicles/" + record.vehicleid}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
{`${record.v_model_yr || ""} ${record.v_make_desc || ""} ${
|
||||
record.v_model_desc || ""
|
||||
}`}
|
||||
</Link>
|
||||
) : (
|
||||
<span>{`${record.v_model_yr || ""} ${record.v_make_desc || ""} ${
|
||||
record.v_model_desc || ""
|
||||
}`}</span>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: t("jobs.fields.ins_co_nm"),
|
||||
dataIndex: "ins_co_nm",
|
||||
key: "ins_co_nm",
|
||||
ellipsis: true,
|
||||
responsive: ["md"],
|
||||
},
|
||||
{
|
||||
title: t("appointments.fields.time"),
|
||||
dataIndex: "start",
|
||||
key: "start",
|
||||
ellipsis: true,
|
||||
responsive: ["md"],
|
||||
},
|
||||
{
|
||||
title: t("appointments.fields.alt_transport"),
|
||||
dataIndex: "alt_transport",
|
||||
key: "alt_transport",
|
||||
ellipsis: true,
|
||||
responsive: ["md"],
|
||||
},
|
||||
];
|
||||
|
||||
const handleTableChange = (sorter) => {
|
||||
setState({ ...state, sortedInfo: sorter });
|
||||
};
|
||||
|
||||
return (
|
||||
<Card
|
||||
title={t("dashboard.titles.scheduledintoday", {
|
||||
date: moment().startOf("day").format("MM/DD/YYYY"),
|
||||
})}
|
||||
{...cardProps}
|
||||
>
|
||||
<div style={{ height: "100%" }}>
|
||||
<Table
|
||||
onChange={handleTableChange}
|
||||
pagination={{ position: "top", defaultPageSize: 50 }}
|
||||
columns={columns}
|
||||
scroll={{ x: true, y: "calc(100% - 2em)" }}
|
||||
rowKey="id"
|
||||
style={{ height: "85%" }}
|
||||
dataSource={appt}
|
||||
/>
|
||||
</div>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
export const DashboardScheduledInTodayGql = `
|
||||
scheduled_in_today: appointments(where: {start: {_gte: "${moment()
|
||||
.startOf("day")
|
||||
.toISOString()}", _lte: "${moment()
|
||||
.endOf("day")
|
||||
.toISOString()}"}, canceled: {_eq: false}, block: {_neq: true}}) {
|
||||
canceled
|
||||
id
|
||||
job {
|
||||
alt_transport
|
||||
clm_no
|
||||
jobid: id
|
||||
ins_co_nm
|
||||
iouparent
|
||||
ownerid
|
||||
ownr_co_nm
|
||||
ownr_ea
|
||||
ownr_fn
|
||||
ownr_ln
|
||||
ownr_ph1
|
||||
ownr_ph2
|
||||
production_vars
|
||||
ro_number
|
||||
suspended
|
||||
v_make_desc
|
||||
v_model_desc
|
||||
v_model_yr
|
||||
v_vin
|
||||
vehicleid
|
||||
}
|
||||
note
|
||||
start
|
||||
title
|
||||
}
|
||||
`;
|
||||
@@ -0,0 +1,210 @@
|
||||
import {
|
||||
BranchesOutlined,
|
||||
ExclamationCircleFilled,
|
||||
PauseCircleOutlined,
|
||||
} from "@ant-design/icons";
|
||||
import { Card, Space, Table, Tooltip } from "antd";
|
||||
import moment from "moment";
|
||||
import React, { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Link } from "react-router-dom";
|
||||
import ChatOpenButton from "../../chat-open-button/chat-open-button.component";
|
||||
import OwnerNameDisplay from "../../owner-name-display/owner-name-display.component";
|
||||
import DashboardRefreshRequired from "../refresh-required.component";
|
||||
|
||||
export default function DashboardScheduledOutToday({ data, ...cardProps }) {
|
||||
const { t } = useTranslation();
|
||||
const [state, setState] = useState({
|
||||
sortedInfo: {},
|
||||
});
|
||||
if (!data) return null;
|
||||
if (!data.scheduled_out_today)
|
||||
return <DashboardRefreshRequired {...cardProps} />;
|
||||
|
||||
data.scheduled_out_today.forEach((item) => {
|
||||
item.scheduled_completion= moment(item.scheduled_completion).format("hh:mm a")
|
||||
});
|
||||
data.scheduled_out_today.sort(function (a, b) {
|
||||
return new Date(a.scheduled_completion) - new Date(b.scheduled_completion);
|
||||
});
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: t("jobs.fields.ro_number"),
|
||||
dataIndex: "ro_number",
|
||||
key: "ro_number",
|
||||
render: (text, record) => (
|
||||
<Link
|
||||
to={"/manage/jobs/" + record.jobid}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
<Space>
|
||||
{record.ro_number || t("general.labels.na")}
|
||||
{record.production_vars && record.production_vars.alert ? (
|
||||
<ExclamationCircleFilled className="production-alert" />
|
||||
) : null}
|
||||
{record.suspended && (
|
||||
<PauseCircleOutlined style={{ color: "orangered" }} />
|
||||
)}
|
||||
{record.iouparent && (
|
||||
<Tooltip title={t("jobs.labels.iou")}>
|
||||
<BranchesOutlined style={{ color: "orangered" }} />
|
||||
</Tooltip>
|
||||
)}
|
||||
</Space>
|
||||
</Link>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: t("jobs.fields.owner"),
|
||||
dataIndex: "owner",
|
||||
key: "owner",
|
||||
ellipsis: true,
|
||||
responsive: ["md"],
|
||||
render: (text, record) => {
|
||||
return record.ownerid ? (
|
||||
<Link
|
||||
to={"/manage/owners/" + record.ownerid}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
<OwnerNameDisplay ownerObject={record} />
|
||||
</Link>
|
||||
) : (
|
||||
<span>
|
||||
<OwnerNameDisplay ownerObject={record} />
|
||||
</span>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: t("jobs.fields.ownr_ph1"),
|
||||
dataIndex: "ownr_ph1",
|
||||
key: "ownr_ph1",
|
||||
ellipsis: true,
|
||||
responsive: ["md"],
|
||||
render: (text, record) => (
|
||||
<ChatOpenButton phone={record.ownr_ph1} jobid={record.jobid} />
|
||||
),
|
||||
},
|
||||
{
|
||||
title: t("jobs.fields.ownr_ph2"),
|
||||
dataIndex: "ownr_ph2",
|
||||
key: "ownr_ph2",
|
||||
ellipsis: true,
|
||||
responsive: ["md"],
|
||||
render: (text, record) => (
|
||||
<ChatOpenButton phone={record.ownr_ph2} jobid={record.jobid} />
|
||||
),
|
||||
},
|
||||
{
|
||||
title: t("jobs.fields.ownr_ea"),
|
||||
dataIndex: "ownr_ea",
|
||||
key: "ownr_ea",
|
||||
ellipsis: true,
|
||||
responsive: ["md"],
|
||||
render: (text, record) => (
|
||||
<ChatOpenButton phone={record.ownr_ea} jobid={record.jobid} />
|
||||
),
|
||||
},
|
||||
{
|
||||
title: t("jobs.fields.vehicle"),
|
||||
dataIndex: "vehicle",
|
||||
key: "vehicle",
|
||||
ellipsis: true,
|
||||
render: (text, record) => {
|
||||
return record.vehicleid ? (
|
||||
<Link
|
||||
to={"/manage/vehicles/" + record.vehicleid}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
{`${record.v_model_yr || ""} ${record.v_make_desc || ""} ${
|
||||
record.v_model_desc || ""
|
||||
}`}
|
||||
</Link>
|
||||
) : (
|
||||
<span>{`${record.v_model_yr || ""} ${record.v_make_desc || ""} ${
|
||||
record.v_model_desc || ""
|
||||
}`}</span>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: t("jobs.fields.ins_co_nm"),
|
||||
dataIndex: "ins_co_nm",
|
||||
key: "ins_co_nm",
|
||||
ellipsis: true,
|
||||
responsive: ["md"],
|
||||
},
|
||||
{
|
||||
title: t("jobs.fields.scheduled_completion"),
|
||||
dataIndex: "scheduled_completion",
|
||||
key: "scheduled_completion",
|
||||
ellipsis: true,
|
||||
responsive: ["md"],
|
||||
},
|
||||
{
|
||||
title: t("appointments.fields.alt_transport"),
|
||||
dataIndex: "alt_transport",
|
||||
key: "alt_transport",
|
||||
ellipsis: true,
|
||||
responsive: ["md"],
|
||||
},
|
||||
];
|
||||
|
||||
const handleTableChange = (sorter) => {
|
||||
setState({ ...state, sortedInfo: sorter });
|
||||
};
|
||||
|
||||
return (
|
||||
<Card
|
||||
title={t("dashboard.titles.scheduledouttoday", {
|
||||
date: moment().startOf("day").format("MM/DD/YYYY"),
|
||||
})}
|
||||
{...cardProps}
|
||||
>
|
||||
<div style={{ height: "100%" }}>
|
||||
<Table
|
||||
onChange={handleTableChange}
|
||||
pagination={{ position: "top", defaultPageSize: 50 }}
|
||||
columns={columns}
|
||||
scroll={{ x: true, y: "calc(100% - 2em)" }}
|
||||
rowKey="id"
|
||||
style={{ height: "85%" }}
|
||||
dataSource={data.scheduled_out_today}
|
||||
/>
|
||||
</div>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
export const DashboardScheduledOutTodayGql = `
|
||||
scheduled_out_today: jobs(where: {
|
||||
date_invoiced: {_is_null: true},
|
||||
ro_number: {_is_null: false},
|
||||
voided: {_eq: false},
|
||||
scheduled_completion: {_gte: "${moment().startOf("day").toISOString()}",
|
||||
_lte: "${moment().endOf("day").toISOString()}"}}) {
|
||||
alt_transport
|
||||
clm_no
|
||||
jobid: id
|
||||
ins_co_nm
|
||||
iouparent
|
||||
ownerid
|
||||
ownr_co_nm
|
||||
ownr_ea
|
||||
ownr_fn
|
||||
ownr_ln
|
||||
ownr_ph1
|
||||
ownr_ph2
|
||||
production_vars
|
||||
ro_number
|
||||
scheduled_completion
|
||||
suspended
|
||||
v_make_desc
|
||||
v_model_desc
|
||||
v_model_yr
|
||||
v_vin
|
||||
vehicleid
|
||||
|
||||
}
|
||||
`;
|
||||
@@ -1,6 +1,6 @@
|
||||
import Icon, { SyncOutlined } from "@ant-design/icons";
|
||||
import { gql, useMutation, useQuery } from "@apollo/client";
|
||||
import { Button, Dropdown, Menu, notification, PageHeader, Space } from "antd";
|
||||
import { Button, Dropdown, Menu, PageHeader, Space, notification } from "antd";
|
||||
import i18next from "i18next";
|
||||
import _ from "lodash";
|
||||
import moment from "moment";
|
||||
@@ -37,6 +37,12 @@ import LoadingSkeleton from "../loading-skeleton/loading-skeleton.component";
|
||||
//Combination of the following:
|
||||
// /node_modules/react-grid-layout/css/styles.css
|
||||
// /node_modules/react-resizable/css/styles.css
|
||||
import DashboardScheduledInToday, {
|
||||
DashboardScheduledInTodayGql,
|
||||
} from "../dashboard-components/scheduled-in-today/scheduled-in-today.component";
|
||||
import DashboardScheduledOutToday, {
|
||||
DashboardScheduledOutTodayGql,
|
||||
} from "../dashboard-components/scheduled-out-today/scheduled-out-today.component";
|
||||
import "./dashboard-grid.styles.scss";
|
||||
import { GenerateDashboardData } from "./dashboard-grid.utils";
|
||||
|
||||
@@ -268,6 +274,28 @@ const componentList = {
|
||||
w: 2,
|
||||
h: 2,
|
||||
},
|
||||
ScheduleInToday: {
|
||||
label: i18next.t("dashboard.titles.scheduledintoday", {
|
||||
date: moment().startOf("day").format("MM/DD/YYYY"),
|
||||
}),
|
||||
component: DashboardScheduledInToday,
|
||||
gqlFragment: DashboardScheduledInTodayGql,
|
||||
minW: 10,
|
||||
minH: 2,
|
||||
w: 10,
|
||||
h: 2,
|
||||
},
|
||||
ScheduleOutToday: {
|
||||
label: i18next.t("dashboard.titles.scheduledouttoday", {
|
||||
date: moment().startOf("day").format("MM/DD/YYYY"),
|
||||
}),
|
||||
component: DashboardScheduledOutToday,
|
||||
gqlFragment: DashboardScheduledOutTodayGql,
|
||||
minW: 10,
|
||||
minH: 2,
|
||||
w: 10,
|
||||
h: 2,
|
||||
},
|
||||
};
|
||||
|
||||
const createDashboardQuery = (state) => {
|
||||
@@ -283,8 +311,12 @@ const createDashboardQuery = (state) => {
|
||||
monthly_sales: jobs(where: {_and: [
|
||||
{ voided: {_eq: false}},
|
||||
{date_invoiced: {_gte: "${moment()
|
||||
.startOf("month").startOf('day').toISOString()}"}}, {date_invoiced: {_lte: "${moment()
|
||||
.endOf("month").endOf('day').toISOString()}"}}]}) {
|
||||
.startOf("month")
|
||||
.startOf("day")
|
||||
.toISOString()}"}}, {date_invoiced: {_lte: "${moment()
|
||||
.endOf("month")
|
||||
.endOf("day")
|
||||
.toISOString()}"}}]}) {
|
||||
id
|
||||
ro_number
|
||||
date_invoiced
|
||||
|
||||
@@ -66,7 +66,7 @@ export function DmsAllocationsSummaryAp({ socket, bodyshop, billids, title }) {
|
||||
key: "status",
|
||||
},
|
||||
{
|
||||
title: t("jobs.fields.ro_number"),
|
||||
title: t("bills.fields.invoice_number"),
|
||||
dataIndex: ["Posting", "Reference"],
|
||||
key: "reference",
|
||||
},
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
import { Alert, Button, Col, Row, Space } from "antd";
|
||||
import i18n from "i18next";
|
||||
import React from "react";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { selectUpdateAvailable } from "../../redux/application/application.selectors";
|
||||
import { AlertOutlined } from "@ant-design/icons";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { setUpdateAvailable } from "../../redux/application/application.actions";
|
||||
import { store } from "../../redux/store";
|
||||
import * as serviceWorkerRegistration from "../../serviceWorkerRegistration";
|
||||
|
||||
let globalRegistration;
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
updateAvailable: selectUpdateAvailable,
|
||||
});
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
||||
});
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(UpdateAlert);
|
||||
|
||||
export function UpdateAlert({ updateAvailable }) {
|
||||
const { t } = useTranslation();
|
||||
if (!updateAvailable) return null;
|
||||
return (
|
||||
<Alert
|
||||
message={t("general.messages.newversiontitle")}
|
||||
showIcon
|
||||
icon={<AlertOutlined />}
|
||||
description={
|
||||
<Row gutter={[16, 16]}>
|
||||
<Col sm={24} md={16} lg={18}>
|
||||
{t("general.messages.newversionmessage")}
|
||||
</Col>
|
||||
<Col sm={24} md={8} lg={6}>
|
||||
<Space wrap>
|
||||
<Button
|
||||
onClick={async () => {
|
||||
window.open("https://imex-online.noticeable.news/", "_blank");
|
||||
}}
|
||||
>
|
||||
{i18n.t("general.actions.viewreleasenotes")}
|
||||
</Button>
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={async () => {
|
||||
if (globalRegistration && globalRegistration.waiting) {
|
||||
await globalRegistration.unregister();
|
||||
// Makes Workbox call skipWaiting()
|
||||
globalRegistration.waiting.postMessage({
|
||||
type: "SKIP_WAITING",
|
||||
});
|
||||
// Once the service worker is unregistered, we can reload the page to let
|
||||
// the browser download a fresh copy of our app (invalidating the cache)
|
||||
window.location.reload();
|
||||
}
|
||||
}}
|
||||
>
|
||||
{i18n.t("general.actions.refresh")}
|
||||
</Button>
|
||||
</Space>
|
||||
</Col>
|
||||
</Row>
|
||||
}
|
||||
closable={false}
|
||||
type="warning"
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
const onServiceWorkerUpdate = (registration) => {
|
||||
console.log("onServiceWorkerUpdate", registration);
|
||||
globalRegistration = registration;
|
||||
store.dispatch(setUpdateAvailable(true));
|
||||
};
|
||||
|
||||
serviceWorkerRegistration.register({ onUpdate: onServiceWorkerUpdate });
|
||||
@@ -1,37 +1,5 @@
|
||||
import { gql } from "@apollo/client";
|
||||
|
||||
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 {
|
||||
messages_aggregate(
|
||||
@@ -44,18 +12,6 @@ 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($offset: Int!) {
|
||||
conversations(
|
||||
|
||||
@@ -25,6 +25,7 @@ import {
|
||||
import * as Sentry from "@sentry/react";
|
||||
|
||||
import "./manage.page.styles.scss";
|
||||
import UpdateAlert from "../../components/update-alert/update-alert.component";
|
||||
|
||||
const ManageRootPage = lazy(() =>
|
||||
import("../manage-root/manage-root.page.container")
|
||||
@@ -394,6 +395,7 @@ export function Manage({ match, conflict, bodyshop }) {
|
||||
<>
|
||||
<ChatAffixContainer />
|
||||
<Layout className="layout-container">
|
||||
<UpdateAlert />
|
||||
<HeaderContainer />
|
||||
|
||||
<Content className="content-container">
|
||||
|
||||
@@ -6,7 +6,7 @@ import AlertComponent from "../../components/alert/alert.component";
|
||||
import LoadingSpinner from "../../components/loading-spinner/loading-spinner.component";
|
||||
import { QUERY_BODYSHOP } from "../../graphql/bodyshop.queries";
|
||||
import { setBodyshop } from "../../redux/user/user.actions";
|
||||
import "../../utils/RegisterSw";
|
||||
//import "../../utils/RegisterSw";
|
||||
import ManagePage from "./manage.page.component";
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
|
||||
@@ -12,6 +12,7 @@ import TechSider from "../../components/tech-sider/tech-sider.component";
|
||||
import { selectTechnician } from "../../redux/tech/tech.selectors";
|
||||
import FeatureWrapper from "../../components/feature-wrapper/feature-wrapper.component";
|
||||
import "./tech.page.styles.scss";
|
||||
import UpdateAlert from "../../components/update-alert/update-alert.component";
|
||||
const TimeTicketModalContainer = lazy(() =>
|
||||
import("../../components/time-ticket-modal/time-ticket-modal.container")
|
||||
);
|
||||
@@ -56,7 +57,9 @@ export function TechPage({ technician, match }) {
|
||||
<TechSider />
|
||||
<Layout>
|
||||
{technician ? null : <Redirect to={`${match.path}/login`} />}
|
||||
<UpdateAlert />
|
||||
<TechHeader />
|
||||
|
||||
<Content className="tech-content-container">
|
||||
<ErrorBoundary>
|
||||
<Suspense
|
||||
|
||||
@@ -9,7 +9,7 @@ import LoadingSpinner from "../../components/loading-spinner/loading-spinner.com
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import "../../utils/RegisterSw";
|
||||
//import "../../utils/RegisterSw";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
|
||||
@@ -62,3 +62,8 @@ export const setProblemJobs = (problemJobs) => ({
|
||||
type: ApplicationActionTypes.SET_PROBLEM_JOBS,
|
||||
payload: problemJobs,
|
||||
});
|
||||
|
||||
export const setUpdateAvailable = (isUpdateAvailable) => ({
|
||||
type: ApplicationActionTypes.SET_UPDATE_AVAILABLE,
|
||||
payload: isUpdateAvailable,
|
||||
});
|
||||
|
||||
@@ -3,6 +3,7 @@ import ApplicationActionTypes from "./application.types";
|
||||
const INITIAL_STATE = {
|
||||
loading: false,
|
||||
online: true,
|
||||
updateAvailable: false,
|
||||
breadcrumbs: [],
|
||||
recentItems: [],
|
||||
selectedHeader: "home",
|
||||
@@ -18,6 +19,11 @@ const INITIAL_STATE = {
|
||||
|
||||
const applicationReducer = (state = INITIAL_STATE, action) => {
|
||||
switch (action.type) {
|
||||
case ApplicationActionTypes.SET_UPDATE_AVAILABLE:
|
||||
return {
|
||||
...state,
|
||||
updateAvailable: action.payload,
|
||||
};
|
||||
case ApplicationActionTypes.SET_SELECTED_HEADER:
|
||||
return {
|
||||
...state,
|
||||
|
||||
@@ -48,3 +48,7 @@ export const selectProblemJobs = createSelector(
|
||||
[selectApplication],
|
||||
(application) => application.problemJobs
|
||||
);
|
||||
export const selectUpdateAvailable = createSelector(
|
||||
[selectApplication],
|
||||
(application) => application.updateAvailable
|
||||
);
|
||||
|
||||
@@ -12,5 +12,6 @@ const ApplicationActionTypes = {
|
||||
SET_ONLINE_STATUS: "SET_ONLINE_STATUS",
|
||||
INSERT_AUDIT_TRAIL: "INSERT_AUDIT_TRAIL",
|
||||
SET_PROBLEM_JOBS: "SET_PROBLEM_JOBS",
|
||||
SET_UPDATE_AVAILABLE: "SET_UPDATE_AVAILABLE"
|
||||
};
|
||||
export default ApplicationActionTypes;
|
||||
|
||||
@@ -858,7 +858,9 @@
|
||||
"prodhrssummary": "Production Hours Summary",
|
||||
"productiondollars": "Total dollars in Production",
|
||||
"productionhours": "Total hours in Production",
|
||||
"projectedmonthlysales": "Projected Monthly Sales"
|
||||
"projectedmonthlysales": "Projected Monthly Sales",
|
||||
"scheduledintoday": "Sheduled In Today: {{date}}",
|
||||
"scheduledouttoday": "Sheduled Out Today: {{date}}"
|
||||
}
|
||||
},
|
||||
"dms": {
|
||||
@@ -1118,7 +1120,7 @@
|
||||
},
|
||||
"messages": {
|
||||
"exception": "$t(titles.app) has encountered an error. Please try again. If the problem persists, please submit a support ticket or contact us.",
|
||||
"newversionmessage": "Click refresh below to update to the latest available version of ImEX Online. Please make sure all other tabs and windows are closed.",
|
||||
"newversionmessage": "Click refresh to update to the latest available version of ImEX Online. Please make sure all other tabs and windows are closed.",
|
||||
"newversiontitle": "New version of ImEX Online Available",
|
||||
"noacctfilepath": "There is no accounting file path set. You will not be able to export any items.",
|
||||
"nofeatureaccess": "You do not have access to this feature of ImEX Online. Please contact support to request a license for this feature.",
|
||||
|
||||
@@ -858,7 +858,9 @@
|
||||
"prodhrssummary": "",
|
||||
"productiondollars": "",
|
||||
"productionhours": "",
|
||||
"projectedmonthlysales": ""
|
||||
"projectedmonthlysales": "",
|
||||
"scheduledintoday": "",
|
||||
"scheduledouttoday": ""
|
||||
}
|
||||
},
|
||||
"dms": {
|
||||
|
||||
@@ -858,7 +858,9 @@
|
||||
"prodhrssummary": "",
|
||||
"productiondollars": "",
|
||||
"productionhours": "",
|
||||
"projectedmonthlysales": ""
|
||||
"projectedmonthlysales": "",
|
||||
"scheduledintoday": "",
|
||||
"scheduledouttoday": ""
|
||||
}
|
||||
},
|
||||
"dms": {
|
||||
|
||||
@@ -3,6 +3,7 @@ import { Button, notification, Space } from "antd";
|
||||
import i18n from "i18next";
|
||||
import React from "react";
|
||||
import * as serviceWorkerRegistration from "../serviceWorkerRegistration";
|
||||
import { store } from "../redux/store";
|
||||
|
||||
const onServiceWorkerUpdate = (registration) => {
|
||||
console.log("onServiceWorkerUpdate", registration);
|
||||
@@ -33,6 +34,9 @@ const onServiceWorkerUpdate = (registration) => {
|
||||
</Button>
|
||||
</Space>
|
||||
);
|
||||
|
||||
store.dispatch()
|
||||
|
||||
notification.open({
|
||||
icon: <AlertOutlined />,
|
||||
message: i18n.t("general.messages.newversiontitle"),
|
||||
|
||||
@@ -2690,6 +2690,13 @@
|
||||
table:
|
||||
name: inventory
|
||||
schema: public
|
||||
- name: parts_dispatch_lines
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: joblineid
|
||||
table:
|
||||
name: parts_dispatch_lines
|
||||
schema: public
|
||||
- name: parts_order_lines
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
@@ -3131,6 +3138,13 @@
|
||||
table:
|
||||
name: notes
|
||||
schema: public
|
||||
- name: parts_dispatches
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: jobid
|
||||
table:
|
||||
name: parts_dispatch
|
||||
schema: public
|
||||
- name: parts_orders
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
@@ -4556,6 +4570,165 @@
|
||||
template_engine: Kriti
|
||||
url: '{{$base_url}}/opensearch'
|
||||
version: 2
|
||||
- table:
|
||||
name: parts_dispatch
|
||||
schema: public
|
||||
object_relationships:
|
||||
- name: job
|
||||
using:
|
||||
foreign_key_constraint_on: jobid
|
||||
array_relationships:
|
||||
- name: parts_dispatch_lines
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: partsdispatchid
|
||||
table:
|
||||
name: parts_dispatch_lines
|
||||
schema: public
|
||||
insert_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
check:
|
||||
job:
|
||||
bodyshop:
|
||||
associations:
|
||||
_and:
|
||||
- user:
|
||||
authid:
|
||||
_eq: X-Hasura-User-Id
|
||||
- active:
|
||||
_eq: true
|
||||
columns:
|
||||
- id
|
||||
- created_at
|
||||
- updated_at
|
||||
- jobid
|
||||
- number
|
||||
- employeeid
|
||||
- dispatched_at
|
||||
- dispatched_by
|
||||
select_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
columns:
|
||||
- number
|
||||
- dispatched_by
|
||||
- created_at
|
||||
- dispatched_at
|
||||
- updated_at
|
||||
- employeeid
|
||||
- id
|
||||
- jobid
|
||||
filter:
|
||||
job:
|
||||
bodyshop:
|
||||
associations:
|
||||
_and:
|
||||
- user:
|
||||
authid:
|
||||
_eq: X-Hasura-User-Id
|
||||
- active:
|
||||
_eq: true
|
||||
update_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
columns:
|
||||
- number
|
||||
- dispatched_by
|
||||
- created_at
|
||||
- dispatched_at
|
||||
- updated_at
|
||||
- employeeid
|
||||
- id
|
||||
- jobid
|
||||
filter:
|
||||
job:
|
||||
bodyshop:
|
||||
associations:
|
||||
_and:
|
||||
- user:
|
||||
authid:
|
||||
_eq: X-Hasura-User-Id
|
||||
- active:
|
||||
_eq: true
|
||||
check: null
|
||||
- table:
|
||||
name: parts_dispatch_lines
|
||||
schema: public
|
||||
object_relationships:
|
||||
- name: jobline
|
||||
using:
|
||||
foreign_key_constraint_on: joblineid
|
||||
- name: parts_dispatch
|
||||
using:
|
||||
foreign_key_constraint_on: partsdispatchid
|
||||
insert_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
check:
|
||||
parts_dispatch:
|
||||
job:
|
||||
bodyshop:
|
||||
associations:
|
||||
_and:
|
||||
- user:
|
||||
authid:
|
||||
_eq: X-Hasura-User-Id
|
||||
- active:
|
||||
_eq: true
|
||||
columns:
|
||||
- id
|
||||
- created_at
|
||||
- updated_at
|
||||
- partsdispatchid
|
||||
- joblineid
|
||||
- quantity
|
||||
- accepted_at
|
||||
select_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
columns:
|
||||
- quantity
|
||||
- accepted_at
|
||||
- created_at
|
||||
- updated_at
|
||||
- id
|
||||
- joblineid
|
||||
- partsdispatchid
|
||||
filter:
|
||||
parts_dispatch:
|
||||
job:
|
||||
bodyshop:
|
||||
associations:
|
||||
_and:
|
||||
- user:
|
||||
authid:
|
||||
_eq: X-Hasura-User-Id
|
||||
- active:
|
||||
_eq: true
|
||||
update_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
columns:
|
||||
- id
|
||||
- created_at
|
||||
- updated_at
|
||||
- partsdispatchid
|
||||
- joblineid
|
||||
- quantity
|
||||
- accepted_at
|
||||
filter:
|
||||
parts_dispatch:
|
||||
job:
|
||||
bodyshop:
|
||||
associations:
|
||||
_and:
|
||||
- user:
|
||||
authid:
|
||||
_eq: X-Hasura-User-Id
|
||||
- active:
|
||||
_eq: true
|
||||
check: null
|
||||
- table:
|
||||
name: parts_order_lines
|
||||
schema: public
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
DROP TABLE "public"."parts_dispatch";
|
||||
@@ -0,0 +1,18 @@
|
||||
CREATE TABLE "public"."parts_dispatch" ("id" uuid NOT NULL DEFAULT gen_random_uuid(), "created_at" timestamptz NOT NULL DEFAULT now(), "updated_at" timestamptz NOT NULL DEFAULT now(), "jobid" uuid NOT NULL, "number" serial NOT NULL, "employeeid" uuid NOT NULL, "dispatched_at" timestamptz NOT NULL, "dispatched_by" text NOT NULL, PRIMARY KEY ("id") , FOREIGN KEY ("jobid") REFERENCES "public"."jobs"("id") ON UPDATE cascade ON DELETE cascade);
|
||||
CREATE OR REPLACE FUNCTION "public"."set_current_timestamp_updated_at"()
|
||||
RETURNS TRIGGER AS $$
|
||||
DECLARE
|
||||
_new record;
|
||||
BEGIN
|
||||
_new := NEW;
|
||||
_new."updated_at" = NOW();
|
||||
RETURN _new;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
CREATE TRIGGER "set_public_parts_dispatch_updated_at"
|
||||
BEFORE UPDATE ON "public"."parts_dispatch"
|
||||
FOR EACH ROW
|
||||
EXECUTE PROCEDURE "public"."set_current_timestamp_updated_at"();
|
||||
COMMENT ON TRIGGER "set_public_parts_dispatch_updated_at" ON "public"."parts_dispatch"
|
||||
IS 'trigger to set value of column "updated_at" to current timestamp on row update';
|
||||
CREATE EXTENSION IF NOT EXISTS pgcrypto;
|
||||
@@ -0,0 +1 @@
|
||||
DROP TABLE "public"."parts_dispatch_lines";
|
||||
@@ -0,0 +1,18 @@
|
||||
CREATE TABLE "public"."parts_dispatch_lines" ("id" uuid NOT NULL DEFAULT gen_random_uuid(), "created_at" timestamptz NOT NULL DEFAULT now(), "updated_at" timestamptz NOT NULL DEFAULT now(), "partsdispatchid" UUID NOT NULL, "joblineid" uuid NOT NULL, "quantity" numeric NOT NULL DEFAULT 1, "accepted_at" timestamptz NOT NULL, PRIMARY KEY ("id") , FOREIGN KEY ("joblineid") REFERENCES "public"."joblines"("id") ON UPDATE cascade ON DELETE cascade);
|
||||
CREATE OR REPLACE FUNCTION "public"."set_current_timestamp_updated_at"()
|
||||
RETURNS TRIGGER AS $$
|
||||
DECLARE
|
||||
_new record;
|
||||
BEGIN
|
||||
_new := NEW;
|
||||
_new."updated_at" = NOW();
|
||||
RETURN _new;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
CREATE TRIGGER "set_public_parts_dispatch_lines_updated_at"
|
||||
BEFORE UPDATE ON "public"."parts_dispatch_lines"
|
||||
FOR EACH ROW
|
||||
EXECUTE PROCEDURE "public"."set_current_timestamp_updated_at"();
|
||||
COMMENT ON TRIGGER "set_public_parts_dispatch_lines_updated_at" ON "public"."parts_dispatch_lines"
|
||||
IS 'trigger to set value of column "updated_at" to current timestamp on row update';
|
||||
CREATE EXTENSION IF NOT EXISTS pgcrypto;
|
||||
@@ -0,0 +1 @@
|
||||
alter table "public"."parts_dispatch_lines" drop constraint "parts_dispatch_lines_partsdispatchid_fkey";
|
||||
@@ -0,0 +1,5 @@
|
||||
alter table "public"."parts_dispatch_lines"
|
||||
add constraint "parts_dispatch_lines_partsdispatchid_fkey"
|
||||
foreign key ("partsdispatchid")
|
||||
references "public"."parts_dispatch"
|
||||
("id") on update cascade on delete cascade;
|
||||
@@ -0,0 +1 @@
|
||||
alter table "public"."parts_dispatch_lines" alter column "accepted_at" set not null;
|
||||
@@ -0,0 +1 @@
|
||||
alter table "public"."parts_dispatch_lines" alter column "accepted_at" drop not null;
|
||||
@@ -0,0 +1,5 @@
|
||||
alter table "public"."job_conversations" drop constraint "job_conversations_conversationid_fkey",
|
||||
add constraint "job_conversations_conversationid_fkey"
|
||||
foreign key ("conversationid")
|
||||
references "public"."conversations"
|
||||
("id") on update restrict on delete restrict;
|
||||
@@ -0,0 +1,5 @@
|
||||
alter table "public"."job_conversations" drop constraint "job_conversations_conversationid_fkey",
|
||||
add constraint "job_conversations_conversationid_fkey"
|
||||
foreign key ("conversationid")
|
||||
references "public"."conversations"
|
||||
("id") on update cascade on delete cascade;
|
||||
@@ -86,6 +86,7 @@ async function OpenSearchUpdateHandler(req, res) {
|
||||
"v_model_yr",
|
||||
"v_make_desc",
|
||||
"v_model_desc",
|
||||
"v_vin",
|
||||
]);
|
||||
document.bodyshopid = req.body.event.data.new.shopid;
|
||||
break;
|
||||
@@ -139,7 +140,7 @@ async function OpenSearchUpdateHandler(req, res) {
|
||||
"exported_at",
|
||||
"invoice_number",
|
||||
"is_credit_memo",
|
||||
"total"
|
||||
"total",
|
||||
]),
|
||||
...bill.bills_by_pk,
|
||||
bodyshopid: bill.bills_by_pk.job.shopid,
|
||||
@@ -244,17 +245,54 @@ async function OpensearchSearchHandler(req, res) {
|
||||
bool: {
|
||||
must: [
|
||||
{
|
||||
multi_match: {
|
||||
query: search,
|
||||
type: "phrase_prefix",
|
||||
//fields: ["*"],
|
||||
// fuzziness: "5",
|
||||
//prefix_length: 2,
|
||||
match: {
|
||||
bodyshopid: assocs.associations[0].shopid,
|
||||
},
|
||||
},
|
||||
{
|
||||
match: {
|
||||
bodyshopid: assocs.associations[0].shopid,
|
||||
bool: {
|
||||
should: [
|
||||
{
|
||||
multi_match: {
|
||||
query: search,
|
||||
type: "cross_fields",
|
||||
fields: ["*ownr_fn", "*ownr_ln"],
|
||||
},
|
||||
},
|
||||
{
|
||||
multi_match: {
|
||||
query: search,
|
||||
type: "most_fields",
|
||||
fields: [
|
||||
"*v_model_yr",
|
||||
"*v_make_desc^2",
|
||||
"*v_model_desc^3",
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
query_string: {
|
||||
query: `*${search}*`,
|
||||
|
||||
fields: [
|
||||
"*ro_number^20",
|
||||
"*clm_no^14",
|
||||
"*v_vin^12",
|
||||
"*plate_no^12",
|
||||
"*ownr_ln^10",
|
||||
"transactionid^10",
|
||||
"paymentnum^10",
|
||||
"invoice_number^10",
|
||||
"*ownr_fn^8",
|
||||
"*ownr_co_nm^8",
|
||||
"*ownr_ph1^8",
|
||||
"*ownr_ph2^8",
|
||||
"*",
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
minimum_should_match: 1,
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
Reference in New Issue
Block a user