Compare commits

..

2 Commits

Author SHA1 Message Date
Allan Carr
1e7f43fe3d IO-2501 Correct for missing query variables 2023-12-21 18:32:37 -08:00
Allan Carr
b97de32a44 IO-2501 Add Jobs Complete Not Invoiced Section to Stats 2023-12-12 15:41:36 -08:00
10 changed files with 92 additions and 89 deletions

View File

@@ -4,7 +4,6 @@ import PhoneNumberFormatter from "../../utils/PhoneFormatter";
import ChatArchiveButton from "../chat-archive-button/chat-archive-button.component";
import ChatConversationTitleTags from "../chat-conversation-title-tags/chat-conversation-title-tags.component";
import ChatLabelComponent from "../chat-label/chat-label.component";
import ChatPrintButton from "../chat-print-button/chat-print-button.component";
import ChatTagRoContainer from "../chat-tag-ro/chat-tag-ro.container";
export default function ChatConversationTitle({ conversation }) {
@@ -14,7 +13,6 @@ export default function ChatConversationTitle({ conversation }) {
{conversation && conversation.phone_num}
</PhoneNumberFormatter>
<ChatLabelComponent conversation={conversation} />
<ChatPrintButton conversation={conversation} />
<ChatConversationTitleTags
jobConversations={
(conversation && conversation.job_conversations) || []

View File

@@ -1,59 +0,0 @@
import { MailOutlined, PrinterOutlined } from "@ant-design/icons";
import { Space, Spin } from "antd";
import React, { useState } from "react";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { setEmailOptions } from "../../redux/email/email.actions";
import { GenerateDocument } from "../../utils/RenderTemplate";
import { TemplateList } from "../../utils/TemplateConstants";
const mapStateToProps = createStructuredSelector({});
const mapDispatchToProps = (dispatch) => ({
setEmailOptions: (e) => dispatch(setEmailOptions(e)),
});
export function ChatPrintButton({ conversation }) {
const [loading, setLoading] = useState(false);
return (
<Space wrap>
<PrinterOutlined
onClick={() => {
setLoading(true);
GenerateDocument(
{
name: TemplateList("messaging").conversation_list.key,
variables: { id: conversation.id },
},
{
subject: TemplateList("messaging").conversation_list.subject,
},
"p",
conversation.id
);
setLoading(false);
}}
/>
<MailOutlined
onClick={() => {
setLoading(true);
GenerateDocument(
{
name: TemplateList("messaging").conversation_list.key,
variables: { id: conversation.id },
},
{
subject: TemplateList("messaging").conversation_list.subject,
},
"e",
conversation.id
);
setLoading(false);
}}
/>
{loading && <Spin />}
</Space>
);
}
export default connect(mapStateToProps, mapDispatchToProps)(ChatPrintButton);

View File

@@ -29,7 +29,7 @@ export default connect(
export function ScoreboardTimeTicketsStats({ bodyshop }) {
const { t } = useTranslation();
const startDate = moment().startOf("month")
const startDate = moment().startOf("month");
const endDate = moment().endOf("month");
const fixedPeriods = useMemo(() => {
@@ -84,6 +84,8 @@ export function ScoreboardTimeTicketsStats({ bodyshop }) {
end: endDate.format("YYYY-MM-DD"),
fixedStart: fixedPeriods.start.format("YYYY-MM-DD"),
fixedEnd: fixedPeriods.end.format("YYYY-MM-DD"),
jobStart: startDate,
jobEnd: endDate,
},
fetchPolicy: "network-only",
nextFetchPolicy: "network-only",
@@ -340,11 +342,21 @@ export function ScoreboardTimeTicketsStats({ bodyshop }) {
larData.push({ ...r, ...lar });
});
const jobData = {};
data.jobs.forEach((job) => {
job.tthrs = job.joblines.reduce((acc, val) => acc + val.mod_lb_hrs, 0);
});
jobData.tthrs = data.jobs
.reduce((acc, val) => acc + val.tthrs, 0)
.toFixed(1);
jobData.count = data.jobs.length.toFixed(0);
return {
fixed: ret,
combinedData: combinedData,
labData: labData,
larData: larData,
jobData: jobData,
};
}, [fixedPeriods, data, bodyshop]);
@@ -356,7 +368,10 @@ export function ScoreboardTimeTicketsStats({ bodyshop }) {
<ScoreboardTimeticketsTargetsTable />
</Col>
<Col span={24}>
<ScoreboardTicketsStats data={calculatedData.fixed} />
<ScoreboardTicketsStats
data={calculatedData.fixed}
jobData={calculatedData.jobData}
/>
</Col>
<Col span={24}>
<ScoreboardTimeTicketsChart

View File

@@ -41,7 +41,7 @@ function useLocalStorage(key, initialValue) {
return [storedValue, setStoredValue];
}
export function ScoreboardTicketsStats({ data, bodyshop }) {
export function ScoreboardTicketsStats({ data, jobData, bodyshop }) {
const { t } = useTranslation();
const [isLarge, setIsLarge] = useLocalStorage("isLargeStatistic", false);
@@ -408,7 +408,7 @@ export function ScoreboardTicketsStats({ data, bodyshop }) {
{/* Monthly Stats */}
<Row gutter={[16, 16]}>
{/* This Month */}
<Col span={8} align="center">
<Col span={7} align="center">
<Card size="small" title={t("scoreboard.labels.thismonth")}>
<Row gutter={[16, 16]}>
<Col span={24}>
@@ -482,7 +482,7 @@ export function ScoreboardTicketsStats({ data, bodyshop }) {
</Card>
</Col>
{/* Last Month */}
<Col span={8} align="center">
<Col span={7} align="center">
<Card size="small" title={t("scoreboard.labels.lastmonth")}>
<Row gutter={[16, 16]}>
<Col span={24}>
@@ -556,7 +556,7 @@ export function ScoreboardTicketsStats({ data, bodyshop }) {
</Card>
</Col>
{/* Efficiency Over Period */}
<Col span={8} align="center">
<Col span={7} align="center">
<Card
size="small"
title={t("scoreboard.labels.efficiencyoverperiod")}
@@ -604,6 +604,40 @@ export function ScoreboardTicketsStats({ data, bodyshop }) {
</Row>
</Card>
</Col>
<Col span={3} align="center">
<Card
size="small"
title={t("scoreboard.labels.jobscompletednotinvoiced")}
>
<Row gutter={[16, 16]}>
<Col span={24}>
<Statistic
value={jobData.count}
valueStyle={{
fontSize: statisticSize,
fontWeight: statisticWeight,
}}
/>
</Col>
</Row>
<Row gutter={[16, 16]}>
<Col span={24}>
<Statistic
title={
<Typography.Text strong>
{t("scoreboard.labels.totalhrs")}
</Typography.Text>
}
value={jobData.tthrs}
valueStyle={{
fontSize: statisticSize,
fontWeight: statisticWeight,
}}
/>
</Col>
</Row>
</Card>
</Col>
</Row>
</Space>
{/* Disclaimer */}

View File

@@ -65,6 +65,8 @@ export default function ScoreboardTimeTickets() {
end: endDate.format("YYYY-MM-DD"),
fixedStart: fixedPeriods.start.format("YYYY-MM-DD"),
fixedEnd: fixedPeriods.end.format("YYYY-MM-DD"),
jobStart: startDate,
jobEnd: endDate,
},
fetchPolicy: "network-only",
nextFetchPolicy: "network-only",

View File

@@ -143,9 +143,14 @@ export const QUERY_TIME_TICKETS_IN_RANGE_SB = gql`
$end: date!
$fixedStart: date!
$fixedEnd: date!
$jobStart: timestamptz!
$jobEnd: timestamptz!
) {
timetickets(
where: { date: { _gte: $start, _lte: $end }, cost_center: {_neq: "timetickets.labels.shift"} }
where: {
date: { _gte: $start, _lte: $end }
cost_center: { _neq: "timetickets.labels.shift" }
}
order_by: { date: desc_nulls_first }
) {
actualhrs
@@ -176,7 +181,10 @@ export const QUERY_TIME_TICKETS_IN_RANGE_SB = gql`
}
}
fixedperiod: timetickets(
where: { date: { _gte: $fixedStart, _lte: $fixedEnd }, cost_center: {_neq: "timetickets.labels.shift"} }
where: {
date: { _gte: $fixedStart, _lte: $fixedEnd }
cost_center: { _neq: "timetickets.labels.shift" }
}
order_by: { date: desc_nulls_first }
) {
actualhrs
@@ -205,6 +213,25 @@ export const QUERY_TIME_TICKETS_IN_RANGE_SB = gql`
last_name
}
}
jobs(
where: {
date_invoiced: { _is_null: true }
ro_number: { _is_null: false }
voided: { _eq: false }
_or: [
{ actual_completion: { _gte: $jobStart, _lte: $jobEnd } }
{ actual_delivery: { _gte: $jobStart, _lte: $jobEnd } }
]
}
) {
id
joblines(order_by: { line_no: asc }, where: { removed: { _eq: false } }) {
convertedtolbr
convertedtolbr_data
mod_lb_hrs
mod_lbr_ty
}
}
}
`;

View File

@@ -2044,9 +2044,6 @@
"sentby": "Sent by {{by}} at {{time}}",
"typeamessage": "Send a message...",
"unarchive": "Unarchive"
},
"render": {
"conversation_list": "Conversation List"
}
},
"notes": {
@@ -2698,6 +2695,7 @@
"efficiencyoverperiod": "Efficiency over Selected Dates",
"entries": "Scoreboard Entries",
"jobs": "Jobs",
"jobscompletednotinvoiced": "Completed Not Invoiced",
"lastmonth": "Last Month",
"lastweek": "Last Week",
"monthlytarget": "Monthly",
@@ -2712,6 +2710,7 @@
"timetickets": "Time Tickets",
"timeticketsemployee": "Time Tickets by Employee",
"todateactual": "Actual (MTD)",
"totalhrs": "Total Hours",
"totaloverperiod": "Total over Selected Dates",
"weeklyactual": "Actual (W)",
"weeklytarget": "Weekly",

View File

@@ -2044,9 +2044,6 @@
"sentby": "",
"typeamessage": "Enviar un mensaje...",
"unarchive": ""
},
"render": {
"conversation_list": ""
}
},
"notes": {
@@ -2698,6 +2695,7 @@
"efficiencyoverperiod": "",
"entries": "",
"jobs": "",
"jobscompletednotinvoiced": "",
"lastmonth": "",
"lastweek": "",
"monthlytarget": "",
@@ -2712,6 +2710,7 @@
"timetickets": "",
"timeticketsemployee": "",
"todateactual": "",
"totalhrs": "",
"totaloverperiod": "",
"weeklyactual": "",
"weeklytarget": "",

View File

@@ -2044,9 +2044,6 @@
"sentby": "",
"typeamessage": "Envoyer un message...",
"unarchive": ""
},
"render": {
"conversation_list": ""
}
},
"notes": {
@@ -2698,6 +2695,7 @@
"efficiencyoverperiod": "",
"entries": "",
"jobs": "",
"jobscompletednotinvoiced": "",
"lastmonth": "",
"lastweek": "",
"monthlytarget": "",
@@ -2712,6 +2710,7 @@
"timetickets": "",
"timeticketsemployee": "",
"todateactual": "",
"totalhrs": "",
"totaloverperiod": "",
"weeklyactual": "",
"weeklytarget": "",

View File

@@ -2102,17 +2102,6 @@ export const TemplateList = (type, context) => {
// },
}
: {}),
...(!type || type === "messaging"
? {
conversation_list: {
title: i18n.t("messaging.render.conversation_list"),
description: "",
subject: i18n.t("messaging.render.conversation_list"),
key: "conversation_list",
disabled: false,
},
}
: {}),
...(!type || type === "vendor"
? {
purchases_by_vendor_detailed: {