-
- {item.job_conversations.length > 0
- ? item.job_conversations.map((j, idx) => (
-
- {j.job.ro_number}
-
- ))
- : null}
+
setSelectedConversation(item.id)}
+ className={`chat-list-item ${
+ item.id === selectedConversation
+ ? "chat-list-selected-conversation"
+ : null
+ }`}
+ style={style}
+ >
+
+ {item.label &&
{item.label}
}
+ {item.job_conversations.length > 0 ? (
+
+ {item.job_conversations.map((j, idx) => (
+
+
+
+ ))}
+
+ ) : (
+
{item.phone_num}
+ )}
- {item.updated_at}
-
-
-
+
+
+ {item.job_conversations.length > 0
+ ? item.job_conversations.map((j, idx) => (
+
+ {j.job.ro_number}
+
+ ))
+ : null}
+
+
{item.updated_at}
+
+
+
+
);
};
@@ -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) {
diff --git a/client/src/components/chat-popup/chat-popup.component.jsx b/client/src/components/chat-popup/chat-popup.component.jsx
index 1c80d7c4c..6d582bdab 100644
--- a/client/src/components/chat-popup/chat-popup.component.jsx
+++ b/client/src/components/chat-popup/chat-popup.component.jsx
@@ -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({
- 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,
- });
- },
- })
- }
/>
)}
diff --git a/client/src/components/contract-cars/contract-cars.component.jsx b/client/src/components/contract-cars/contract-cars.component.jsx
index c66ff9faa..541d6d7c3 100644
--- a/client/src/components/contract-cars/contract-cars.component.jsx
+++ b/client/src/components/contract-cars/contract-cars.component.jsx
@@ -59,6 +59,14 @@ export default function ContractsCarsComponent({
sortOrder:
state.sortedInfo.columnKey === "model" && state.sortedInfo.order,
},
+ {
+ title: t("courtesycars.fields.color"),
+ dataIndex: "color",
+ key: "color",
+ sorter: (a, b) => alphaSort(a.color, b.color),
+ sortOrder:
+ state.sortedInfo.columnKey === "color" && state.sortedInfo.order,
+ },
{
title: t("courtesycars.fields.plate"),
dataIndex: "plate",
@@ -93,6 +101,9 @@ export default function ContractsCarsComponent({
(cc.model || "")
.toLowerCase()
.includes(state.search.toLowerCase()) ||
+ (cc.color || "")
+ .toLowerCase()
+ .includes(state.search.toLowerCase()) ||
(cc.plate || "").toLowerCase().includes(state.search.toLowerCase())
);
diff --git a/client/src/components/dashboard-components/scheduled-in-today/scheduled-in-today.component.jsx b/client/src/components/dashboard-components/scheduled-in-today/scheduled-in-today.component.jsx
new file mode 100644
index 000000000..87d1980a1
--- /dev/null
+++ b/client/src/components/dashboard-components/scheduled-in-today/scheduled-in-today.component.jsx
@@ -0,0 +1,235 @@
+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 ;
+
+ const appt = []; // Flatten Data
+ data.scheduled_in_today.forEach((item) => {
+ 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 Date(a.start) - new Date(b.start); });
+
+ const columns = [
+ {
+ title: t("jobs.fields.ro_number"),
+ dataIndex: "ro_number",
+ key: "ro_number",
+ render: (text, record) => (
+ e.stopPropagation()}
+ >
+
+ {record.ro_number || t("general.labels.na")}
+ {record.production_vars && record.production_vars.alert ? (
+
+ ) : null}
+ {record.suspended && (
+
+ )}
+ {record.iouparent && (
+
+
+
+ )}
+
+
+ ),
+ },
+ {
+ title: t("jobs.fields.owner"),
+ dataIndex: "owner",
+ key: "owner",
+ ellipsis: true,
+ responsive: ["md"],
+ render: (text, record) => {
+ return record.ownerid ? (
+ e.stopPropagation()}
+ >
+
+
+ ) : (
+
+
+
+ );
+ },
+ },
+ {
+ title: t("jobs.fields.ownr_ph1"),
+ dataIndex: "ownr_ph1",
+ key: "ownr_ph1",
+ ellipsis: true,
+ responsive: ["md"],
+ render: (text, record) => (
+
+ ),
+ },
+ {
+ title: t("jobs.fields.ownr_ph2"),
+ dataIndex: "ownr_ph2",
+ key: "ownr_ph2",
+ ellipsis: true,
+ responsive: ["md"],
+ render: (text, record) => (
+
+ ),
+ },
+ {
+ title: t("jobs.fields.ownr_ea"),
+ dataIndex: "ownr_ea",
+ key: "ownr_ea",
+ ellipsis: true,
+ responsive: ["md"],
+ render: (text, record) => (
+
+ ),
+ },
+ {
+ title: t("jobs.fields.vehicle"),
+ dataIndex: "vehicle",
+ key: "vehicle",
+ ellipsis: true,
+ render: (text, record) => {
+ return record.vehicleid ? (
+ e.stopPropagation()}
+ >
+ {`${record.v_model_yr || ""} ${record.v_make_desc || ""} ${
+ record.v_model_desc || ""
+ }`}
+
+ ) : (
+ {`${record.v_model_yr || ""} ${record.v_make_desc || ""} ${
+ record.v_model_desc || ""
+ }`}
+ );
+ },
+ },
+ {
+ 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 (
+
+
+
+ );
+}
+
+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
+ }
+`;
diff --git a/client/src/components/dashboard-components/scheduled-out-today/scheduled-out-today.component.jsx b/client/src/components/dashboard-components/scheduled-out-today/scheduled-out-today.component.jsx
new file mode 100644
index 000000000..586e719ab
--- /dev/null
+++ b/client/src/components/dashboard-components/scheduled-out-today/scheduled-out-today.component.jsx
@@ -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 ;
+
+ 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) => (
+ e.stopPropagation()}
+ >
+
+ {record.ro_number || t("general.labels.na")}
+ {record.production_vars && record.production_vars.alert ? (
+
+ ) : null}
+ {record.suspended && (
+
+ )}
+ {record.iouparent && (
+
+
+
+ )}
+
+
+ ),
+ },
+ {
+ title: t("jobs.fields.owner"),
+ dataIndex: "owner",
+ key: "owner",
+ ellipsis: true,
+ responsive: ["md"],
+ render: (text, record) => {
+ return record.ownerid ? (
+ e.stopPropagation()}
+ >
+
+
+ ) : (
+
+
+
+ );
+ },
+ },
+ {
+ title: t("jobs.fields.ownr_ph1"),
+ dataIndex: "ownr_ph1",
+ key: "ownr_ph1",
+ ellipsis: true,
+ responsive: ["md"],
+ render: (text, record) => (
+
+ ),
+ },
+ {
+ title: t("jobs.fields.ownr_ph2"),
+ dataIndex: "ownr_ph2",
+ key: "ownr_ph2",
+ ellipsis: true,
+ responsive: ["md"],
+ render: (text, record) => (
+
+ ),
+ },
+ {
+ title: t("jobs.fields.ownr_ea"),
+ dataIndex: "ownr_ea",
+ key: "ownr_ea",
+ ellipsis: true,
+ responsive: ["md"],
+ render: (text, record) => (
+
+ ),
+ },
+ {
+ title: t("jobs.fields.vehicle"),
+ dataIndex: "vehicle",
+ key: "vehicle",
+ ellipsis: true,
+ render: (text, record) => {
+ return record.vehicleid ? (
+ e.stopPropagation()}
+ >
+ {`${record.v_model_yr || ""} ${record.v_make_desc || ""} ${
+ record.v_model_desc || ""
+ }`}
+
+ ) : (
+ {`${record.v_model_yr || ""} ${record.v_make_desc || ""} ${
+ record.v_model_desc || ""
+ }`}
+ );
+ },
+ },
+ {
+ 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 (
+
+
+
+ );
+}
+
+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
+
+ }
+`;
diff --git a/client/src/components/dashboard-grid/dashboard-grid.component.jsx b/client/src/components/dashboard-grid/dashboard-grid.component.jsx
index 39a5432c2..d24dd5641 100644
--- a/client/src/components/dashboard-grid/dashboard-grid.component.jsx
+++ b/client/src/components/dashboard-grid/dashboard-grid.component.jsx
@@ -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
diff --git a/client/src/components/dms-allocations-summary-ap/dms-allocations-summary-ap.component.jsx b/client/src/components/dms-allocations-summary-ap/dms-allocations-summary-ap.component.jsx
index 1da53e3ba..54651544d 100644
--- a/client/src/components/dms-allocations-summary-ap/dms-allocations-summary-ap.component.jsx
+++ b/client/src/components/dms-allocations-summary-ap/dms-allocations-summary-ap.component.jsx
@@ -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",
},
diff --git a/client/src/components/dms-post-form/dms-post-form.component.jsx b/client/src/components/dms-post-form/dms-post-form.component.jsx
index c9043d050..e1358e989 100644
--- a/client/src/components/dms-post-form/dms-post-form.component.jsx
+++ b/client/src/components/dms-post-form/dms-post-form.component.jsx
@@ -11,6 +11,7 @@ import {
Select,
Space,
Statistic,
+ Switch,
Typography,
} from "antd";
import Dinero from "dinero.js";
@@ -183,6 +184,13 @@ export function DmsPostForm({ bodyshop, socket, job, logsRef }) {
+
+
+
)}
diff --git a/client/src/components/header/header.component.jsx b/client/src/components/header/header.component.jsx
index 1ed3f526d..f0edf82d2 100644
--- a/client/src/components/header/header.component.jsx
+++ b/client/src/components/header/header.component.jsx
@@ -311,7 +311,9 @@ function Header({
icon={