Fixed up scheduling logic using UTC dates instead of local dates. Updated scheduling header. Fixed month view not showing. BOD-179

This commit is contained in:
Patrick Fic
2020-08-10 11:59:12 -07:00
parent 3862f7f11f
commit 0df61a2701
12 changed files with 75 additions and 73 deletions

17
.vscode/launch.json vendored
View File

@@ -1,19 +1,20 @@
{ {
"version": "0.2.0", "version": "0.2.0",
"configurations": [ "configurations": [
{
"name": "Attach to Chrome",
"port": 9222,
"request": "attach",
"type": "pwa-chrome",
"webRoot": "${workspaceFolder}/client/src"
},
{ {
"name": "Chrome", "name": "Chrome",
"type": "chrome", "type": "chrome",
"request": "launch", "request": "launch",
"url": "http://localhost:3000", "url": "http://localhost:3000",
"webRoot": "${workspaceRoot}/src" "webRoot": "${workspaceRoot}/client/src"
},
{
"name": "Yarn Dev Server",
"type": "node",
"request": "launch",
"runtimeExecutable": "yarn",
"runtimeArgs": ["dev"]
} }
] ]
} }

View File

@@ -1,6 +1,6 @@
import Icon from "@ant-design/icons"; import Icon from "@ant-design/icons";
import React, { useEffect, useRef } from "react"; import React, { useEffect, useRef } from "react";
import { FaCheck, FaCheckDouble } from "react-icons/fa"; import { MdDone, MdDoneAll } from "react-icons/md";
import { import {
AutoSizer, AutoSizer,
CellMeasurer, CellMeasurer,
@@ -38,8 +38,9 @@ export default function ChatMessageListComponent({ messages }) {
style={style} style={style}
className={`${ className={`${
messages[index].isoutbound ? "mine messages" : "yours messages" messages[index].isoutbound ? "mine messages" : "yours messages"
}`}> }`}
<div className='message msgmargin'> >
<div className="message msgmargin">
{MessageRender(messages[index])} {MessageRender(messages[index])}
{StatusRender(messages[index].status)} {StatusRender(messages[index].status)}
</div> </div>
@@ -50,7 +51,7 @@ export default function ChatMessageListComponent({ messages }) {
}; };
return ( return (
<div className='chat'> <div className="chat">
<AutoSizer> <AutoSizer>
{({ height, width }) => ( {({ height, width }) => (
<List <List
@@ -73,12 +74,8 @@ export default function ChatMessageListComponent({ messages }) {
const MessageRender = (message) => { const MessageRender = (message) => {
if (message.image) { if (message.image) {
return ( return (
<a href={message.image_path} target='__blank'> <a href={message.image_path} target="__blank">
<img <img alt="Received" className="message-img" src={message.image_path} />
alt='Received'
className='message-img'
src={message.image_path}
/>
</a> </a>
); );
} else { } else {
@@ -89,9 +86,9 @@ const MessageRender = (message) => {
const StatusRender = (status) => { const StatusRender = (status) => {
switch (status) { switch (status) {
case "sent": case "sent":
return <Icon component={FaCheck} className='message-icon' />; return <Icon component={MdDone} className="message-icon" />;
case "delivered": case "delivered":
return <Icon component={FaCheckDouble} className='message-icon' />; return <Icon component={MdDoneAll} className="message-icon" />;
default: default:
return null; return null;
} }

View File

@@ -36,7 +36,7 @@ export default function ProductionBoardCard(card) {
<div className="imex-flex-row imex-flex-row__flex-space-around"> <div className="imex-flex-row imex-flex-row__flex-space-around">
<div className="mex-flex-row__margin"> <div className="mex-flex-row__margin">
<div>{`B: ${card.labhrs.aggregate.sum.mod_lb_hrs || "?"}`}</div> <div>{`B: ${card.labhrs.aggregate.sum.mod_lb_hrs || "?"}`}</div>
<div>{`R: ${card.labhrs.aggregate.sum.mod_lb_hrs || "?"}`}</div> <div>{`R: ${card.larhrs.aggregate.sum.mod_lb_hrs || "?"}`}</div>
</div> </div>
<div className="mex-flex-row__margin"> <div className="mex-flex-row__margin">
<div>{`B: ${ <div>{`B: ${

View File

@@ -12,9 +12,9 @@ const mapStateToProps = createStructuredSelector({
export function ProductionBoardKanbanContainer({ bodyshop }) { export function ProductionBoardKanbanContainer({ bodyshop }) {
const { loading, data } = useSubscription(SUBSCRIPTION_JOBS_IN_PRODUCTION, { const { loading, data } = useSubscription(SUBSCRIPTION_JOBS_IN_PRODUCTION, {
variables: { // variables: {
statusList: bodyshop.md_ro_statuses.production_statuses || [], // statusList: bodyshop.md_ro_statuses.production_statuses || [],
}, // },
}); });
return ( return (

View File

@@ -15,9 +15,9 @@ export default connect(mapStateToProps, null)(ProductionListTableContainer);
export function ProductionListTableContainer({ bodyshop }) { export function ProductionListTableContainer({ bodyshop }) {
const { loading, data } = useSubscription(SUBSCRIPTION_JOBS_IN_PRODUCTION, { const { loading, data } = useSubscription(SUBSCRIPTION_JOBS_IN_PRODUCTION, {
variables: { // variables: {
statusList: bodyshop.md_ro_statuses.production_statuses || [], // statusList: bodyshop.md_ro_statuses.production_statuses || [],
}, // },
}); });
const columnState = useState( const columnState = useState(

View File

@@ -24,10 +24,7 @@ export function ScheduleBlockDay({ date, children, refetch, bodyshop }) {
const handleMenu = async (e) => { const handleMenu = async (e) => {
e.domEvent.stopPropagation(); e.domEvent.stopPropagation();
if (e.key === "block") { if (e.key === "block") {
const blockAppt = { const blockAppt = {
title: t("appointments.labels.blocked"), title: t("appointments.labels.blocked"),
block: true, block: true,
@@ -38,7 +35,6 @@ export function ScheduleBlockDay({ date, children, refetch, bodyshop }) {
}; };
logImEXEvent("dashboard_change_layout"); logImEXEvent("dashboard_change_layout");
const result = await insertBlock({ const result = await insertBlock({
variables: { app: [blockAppt] }, variables: { app: [blockAppt] },
}); });
@@ -57,9 +53,7 @@ export function ScheduleBlockDay({ date, children, refetch, bodyshop }) {
const menu = ( const menu = (
<Menu onClick={handleMenu}> <Menu onClick={handleMenu}>
<Menu.Item key='block'>{t("appointments.actions.block")}</Menu.Item> <Menu.Item key="block">{t("appointments.actions.block")}</Menu.Item>
<Menu.Item key='2'>2nd menu item</Menu.Item>
<Menu.Item key='3'>3rd menu item</Menu.Item>
</Menu> </Menu>
); );

View File

@@ -6,8 +6,13 @@ import {
selectScheduleLoadCalculating, selectScheduleLoadCalculating,
} from "../../redux/application/application.selectors"; } from "../../redux/application/application.selectors";
import LoadingSkeleton from "../loading-skeleton/loading-skeleton.component"; import LoadingSkeleton from "../loading-skeleton/loading-skeleton.component";
import { Progress } from "antd"; import { Progress, Statistic } from "antd";
import { MdCallReceived, MdCallMissedOutgoing } from "react-icons/md"; import {
MdCallReceived,
MdCallMissedOutgoing,
MdFileDownload,
MdFileUpload,
} from "react-icons/md";
import Icon from "@ant-design/icons"; import Icon from "@ant-design/icons";
import ScheduleBlockDay from "../schedule-block-day/schedule-block-day.component"; import ScheduleBlockDay from "../schedule-block-day/schedule-block-day.component";
@@ -34,18 +39,24 @@ export function ScheduleCalendarHeaderComponent({
const loadData = load[date.toISOString().substr(0, 10)]; const loadData = load[date.toISOString().substr(0, 10)];
const LoadComponent = loadData ? ( const LoadComponent = loadData ? (
<div style={{ display: "flex", flexDirection: "column" }}> <div className="imex-flex-row imex-flex-row__flex-space-around">
<Progress <Icon component={MdFileDownload} style={{ color: "green" }} />
style={{ display: "flex", alignItems: "center" }} {(loadData.hoursIn || 0) && loadData.hoursIn.toFixed(2)}
percent={((loadData.expectedLoad || 0) / ShopTargetHrs) * 100} <Icon component={MdFileUpload} style={{ color: "red" }} />
{(loadData.hoursOut || 0) && loadData.hoursOut.toFixed(2)}
<Statistic
value={((loadData.expectedLoad || 0) / ShopTargetHrs) * 100}
suffix={"%"}
precision={0}
valueStyle={{
color:
Math.abs(
100 - ((loadData.expectedLoad || 0) / ShopTargetHrs) * 100
) <= 10
? "green"
: "red",
}}
/> />
<div className="imex-flex-row imex-flex-row__flex-space-around">
<Icon component={MdCallReceived} />
{(loadData.hoursIn || 0) && loadData.hoursIn.toFixed(2)}
<Icon component={MdCallMissedOutgoing} />
{(loadData.hoursOut || 0) && loadData.hoursOut.toFixed(2)}
</div>
</div> </div>
) : null; ) : null;
@@ -53,11 +64,7 @@ export function ScheduleCalendarHeaderComponent({
<ScheduleBlockDay date={date} refetch={refetch}> <ScheduleBlockDay date={date} refetch={refetch}>
<div> <div>
{label} {label}
{calculating || JSON.stringify(load) === "{}" ? ( {calculating ? <LoadingSkeleton /> : LoadComponent}
<LoadingSkeleton />
) : (
LoadComponent
)}
</div> </div>
</ScheduleBlockDay> </ScheduleBlockDay>
); );

View File

@@ -12,3 +12,11 @@
.imex-event-block { .imex-event-block {
background-color: rgba(212, 2, 2, 0.6); background-color: rgba(212, 2, 2, 0.6);
} }
.rbc-month-view {
height: 125rem;
}
.rbc-event.rbc-selected {
background-color: slategrey;
}

View File

@@ -19,9 +19,7 @@ export function ScoreboardLastDays({ bodyshop, sbEntriesByDate }) {
const ArrayOfDate = []; const ArrayOfDate = [];
for (var i = lastNumberWorkingDays - 1; i >= 0; i--) { for (var i = lastNumberWorkingDays - 1; i >= 0; i--) {
ArrayOfDate.push( ArrayOfDate.push(moment().businessSubtract(i, "day").format("yyyy-MM-DD"));
moment().businessSubtract(i, "day").toISOString().substr(0, 10)
);
} }
return ( return (

View File

@@ -52,7 +52,7 @@ export const QUERY_ALL_ACTIVE_JOBS = gql`
export const SUBSCRIPTION_JOBS_IN_PRODUCTION = gql` export const SUBSCRIPTION_JOBS_IN_PRODUCTION = gql`
subscription SUBSCRIPTION_JOBS_IN_PRODUCTION($statusList: [String!]!) { subscription SUBSCRIPTION_JOBS_IN_PRODUCTION($statusList: [String!]!) {
jobs(where: { status: { _in: $statusList } }) { jobs(where: { inproduction: { _eq: true } }) {
id id
status status
ro_number ro_number

View File

@@ -35,7 +35,7 @@ export function* calculateScheduleLoad({ payload: end }) {
const { arrJobs, compJobs } = result.data; const { arrJobs, compJobs } = result.data;
arrJobs.forEach((item) => { arrJobs.forEach((item) => {
const itemDate = moment(item.scheduled_in).toISOString().substr(0, 10); const itemDate = moment(item.scheduled_in).format("yyyy-MM-DD");
if (!!load[itemDate]) { if (!!load[itemDate]) {
load[itemDate].hoursIn = load[itemDate].hoursIn =
(load[itemDate].hoursIn || 0) + (load[itemDate].hoursIn || 0) +
@@ -44,7 +44,7 @@ export function* calculateScheduleLoad({ payload: end }) {
load[itemDate].jobsIn.push(item); load[itemDate].jobsIn.push(item);
} else { } else {
load[itemDate] = { load[itemDate] = {
jobsIn: [], jobsIn: [item],
jobsOut: [], jobsOut: [],
hoursIn: hoursIn:
item.labhrs.aggregate.sum.mod_lb_hrs + item.labhrs.aggregate.sum.mod_lb_hrs +
@@ -54,9 +54,7 @@ export function* calculateScheduleLoad({ payload: end }) {
}); });
compJobs.forEach((item) => { compJobs.forEach((item) => {
const itemDate = moment(item.scheduled_completion) const itemDate = moment(item.scheduled_completion).format("yyyy-MM-DD");
.toISOString()
.substr(0, 10);
if (!!load[itemDate]) { if (!!load[itemDate]) {
load[itemDate].hoursOut = load[itemDate].hoursOut =
(load[itemDate].hoursOut || 0) + (load[itemDate].hoursOut || 0) +
@@ -65,6 +63,7 @@ export function* calculateScheduleLoad({ payload: end }) {
load[itemDate].jobsOut.push(item); load[itemDate].jobsOut.push(item);
} else { } else {
load[itemDate] = { load[itemDate] = {
jobsOut: [item],
hoursOut: hoursOut:
item.labhrs.aggregate.sum.mod_lb_hrs + item.labhrs.aggregate.sum.mod_lb_hrs +
item.larhrs.aggregate.sum.mod_lb_hrs, item.larhrs.aggregate.sum.mod_lb_hrs,
@@ -75,20 +74,19 @@ export function* calculateScheduleLoad({ payload: end }) {
//Propagate the expected load to each day. //Propagate the expected load to each day.
const range = Math.round(moment.duration(end.diff(today)).asDays()); const range = Math.round(moment.duration(end.diff(today)).asDays());
for (var day = 0; day < range; day++) { for (var day = 0; day < range; day++) {
const current = moment(today) const current = moment(today).add(day, "days").format("yyyy-MM-DD");
.add(day, "days")
.toISOString()
.substr(0, 10);
const prev = moment(today) const prev = moment(today)
.add(day - 1, "days") .add(day - 1, "days")
.toISOString() .format("yyyy-MM-DD");
.substr(0, 10);
if (!!!load[current]) { if (!!!load[current]) {
load[current] = {}; load[current] = {};
} }
if (day === 0) { if (day === 0) {
//Starting on day 1. The load is current. //Starting on day 1. The load is current.
load[current].expectedLoad = load.productionHoursTotal; load[current].expectedLoad =
load.productionHoursTotal +
(load[current].hoursIn || 0) -
(load[current].hoursOut || 0);
} else { } else {
load[current].expectedLoad = load[current].expectedLoad =
load[prev].expectedLoad + load[prev].expectedLoad +
@@ -96,7 +94,6 @@ export function* calculateScheduleLoad({ payload: end }) {
(load[current].hoursOut || 0); (load[current].hoursOut || 0);
} }
} }
yield put(scheduleLoadSuccess(load)); yield put(scheduleLoadSuccess(load));
} catch (error) { } catch (error) {
//console.log("Error in sendEmailFailure saga.", error.message); //console.log("Error in sendEmailFailure saga.", error.message);

View File

@@ -53,7 +53,7 @@ exports.job = async (req, res) => {
//Initialize the bucket matrix //Initialize the bucket matrix
for (i = 0; i < totalMatrixDays; i++) { for (i = 0; i < totalMatrixDays; i++) {
const theDate = moment().add(i, "days").toISOString().substr(0, 10); const theDate = moment().add(i, "days").format("yyyy-MM-DD");
//Only need to create a matrix for jobs of the same bucket. //Only need to create a matrix for jobs of the same bucket.
bucketMatrix[theDate] = { in: 0, out: 0 }; bucketMatrix[theDate] = { in: 0, out: 0 };
@@ -76,7 +76,7 @@ exports.job = async (req, res) => {
)[0]; )[0];
if (appointmentBucket.id === JobBucket.id) { if (appointmentBucket.id === JobBucket.id) {
//Theyre the same classification. Add it to the matrix. //Theyre the same classification. Add it to the matrix.
const appDate = moment(appointment.start).toISOString().substr(0, 10); const appDate = moment(appointment.start).format("yyyy-MM-DD");
bucketMatrix[appDate] = { bucketMatrix[appDate] = {
...bucketMatrix[appDate], ...bucketMatrix[appDate],
in: bucketMatrix[appDate].in + 1, in: bucketMatrix[appDate].in + 1,
@@ -85,7 +85,7 @@ exports.job = async (req, res) => {
}); });
//Populate the jobs that are leaving today. //Populate the jobs that are leaving today.
const todayIsoString = moment().toISOString().substr(0, 10); const todayIsoString = moment().format("yyyy-MM-DD");
productionview.forEach((pjob) => { productionview.forEach((pjob) => {
const jobHrs = pjob.larhrs + pjob.labhrs; const jobHrs = pjob.larhrs + pjob.labhrs;
//Is the job in the same bucket? //Is the job in the same bucket?
@@ -100,7 +100,7 @@ exports.job = async (req, res) => {
let dateToUse; let dateToUse;
dateToUse = compDate.isValid() dateToUse = compDate.isValid()
? moment().diff(compDate, "days") <= 0 ? moment().diff(compDate, "days") <= 0
? compDate.toISOString().substr(0, 10) ? compDate.format("yyyy-MM-DD")
: todayIsoString : todayIsoString
: todayIsoString; : todayIsoString;