diff --git a/client/src/components/jobs-documents-imgproxy-gallery/jobs-document-imgproxy-gallery.download.component.jsx b/client/src/components/jobs-documents-imgproxy-gallery/jobs-document-imgproxy-gallery.download.component.jsx
index 65140d6b0..35b4a785c 100644
--- a/client/src/components/jobs-documents-imgproxy-gallery/jobs-document-imgproxy-gallery.download.component.jsx
+++ b/client/src/components/jobs-documents-imgproxy-gallery/jobs-document-imgproxy-gallery.download.component.jsx
@@ -3,7 +3,6 @@ import axios from "axios";
import { useState } from "react";
import { useTranslation } from "react-i18next";
import { logImEXEvent } from "../../firebase/firebase.utils";
-import cleanAxios from "../../utils/CleanAxios";
import formatBytes from "../../utils/formatbytes";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
@@ -12,7 +11,7 @@ import { selectBodyshop } from "../../redux/user/user.selectors";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop
});
-const mapDispatchToProps = (dispatch) => ({
+const mapDispatchToProps = () => ({
//setUserLanguage: language => dispatch(setUserLanguage(language))
});
@@ -26,7 +25,7 @@ const mapDispatchToProps = (dispatch) => ({
export default connect(mapStateToProps, mapDispatchToProps)(JobsDocumentsImgproxyDownloadButton);
-export function JobsDocumentsImgproxyDownloadButton({ bodyshop, galleryImages, identifier, jobId }) {
+export function JobsDocumentsImgproxyDownloadButton({ galleryImages, identifier, jobId }) {
const { t } = useTranslation();
const [download, setDownload] = useState(null);
const [loading, setLoading] = useState(false);
diff --git a/client/src/components/schedule-calendar-wrapper/schedule-calendar-header.component.jsx b/client/src/components/schedule-calendar-wrapper/schedule-calendar-header.component.jsx
index b00ff0c36..faea7a23d 100644
--- a/client/src/components/schedule-calendar-wrapper/schedule-calendar-header.component.jsx
+++ b/client/src/components/schedule-calendar-wrapper/schedule-calendar-header.component.jsx
@@ -1,9 +1,9 @@
import Icon from "@ant-design/icons";
import { Card, Popover, Space } from "antd";
-import _ from "lodash";
+import { groupBy } from "lodash";
import dayjs from "../../utils/day";
-import React, { useMemo } from "react";
+import { useMemo } from "react";
import { useTranslation } from "react-i18next";
import { MdFileDownload, MdFileUpload } from "react-icons/md";
import { connect } from "react-redux";
@@ -26,21 +26,12 @@ const mapStateToProps = createStructuredSelector({
calculating: selectScheduleLoadCalculating
});
-const mapDispatchToProps = (dispatch) => ({});
+const mapDispatchToProps = () => ({});
-export function ScheduleCalendarHeaderComponent({
- bodyshop,
- label,
- refetch,
- date,
- load,
- calculating,
- events,
- ...otherProps
-}) {
+export function ScheduleCalendarHeaderComponent({ bodyshop, label, refetch, date, load, calculating, events }) {
const ATSToday = useMemo(() => {
if (!events) return [];
- return _.groupBy(
+ return groupBy(
events.filter((e) => !e.vacation && e.isintake && dayjs(date).isSame(dayjs(e.start), "day")),
"job.alt_transport"
);
@@ -155,7 +146,11 @@ export function ScheduleCalendarHeaderComponent({
- {`${(loadData.allHoursInBody || 0) && loadData.allHoursInBody.toFixed(1)}/${(loadData.allHoursInRefinish || 0) && loadData.allHoursInRefinish.toFixed(1)}/${(loadData.allHoursIn || 0) && loadData.allHoursIn.toFixed(1)}`}
+
+ {`${(loadData.allHoursInBody || 0) && loadData.allHoursInBody.toFixed(1)}/${
+ (loadData.allHoursInRefinish || 0) && loadData.allHoursInRefinish.toFixed(1)
+ }/${(loadData.allHoursIn || 0) && loadData.allHoursIn.toFixed(1)}`}
+
diff --git a/client/src/components/scoreboard-chart/scoreboard-chart.component.jsx b/client/src/components/scoreboard-chart/scoreboard-chart.component.jsx
index 7b613aaed..2ab5ba7a3 100644
--- a/client/src/components/scoreboard-chart/scoreboard-chart.component.jsx
+++ b/client/src/components/scoreboard-chart/scoreboard-chart.component.jsx
@@ -1,8 +1,7 @@
import { Card } from "antd";
import Dinero from "dinero.js";
-import _ from "lodash";
+import { round } from "lodash";
import dayjs from "../../utils/day";
-import React from "react";
import { connect } from "react-redux";
import {
Area,
@@ -29,7 +28,7 @@ const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop
});
-const mapDispatchToProps = (dispatch) => ({
+const mapDispatchToProps = () => ({
//setUserLanguage: language => dispatch(setUserLanguage(language))
});
export default connect(mapStateToProps, mapDispatchToProps)(ScoreboardChart);
@@ -40,7 +39,7 @@ export function ScoreboardChart({ sbEntriesByDate, bodyshop }) {
const data = listOfBusDays.reduce((acc, val) => {
//Sum up the current day.
let dayhrs;
- if (!!sbEntriesByDate[val]) {
+ if (sbEntriesByDate[val]) {
dayhrs = sbEntriesByDate[val].reduce(
(dayAcc, dayVal) => {
return {
@@ -61,9 +60,9 @@ export function ScoreboardChart({ sbEntriesByDate, bodyshop }) {
const theValue = {
date: dayjs(val).format("D ddd"),
- paintHrs: _.round(dayhrs.painthrs, 1),
- bodyHrs: _.round(dayhrs.bodyhrs, 1),
- accTargetHrs: _.round(
+ paintHrs: round(dayhrs.painthrs, 1),
+ bodyHrs: round(dayhrs.bodyhrs, 1),
+ accTargetHrs: round(
Utils.AsOfDateTargetHours(
bodyshop.scoreboard_target.dailyBodyTarget + bodyshop.scoreboard_target.dailyPaintTarget,
val
@@ -72,14 +71,14 @@ export function ScoreboardChart({ sbEntriesByDate, bodyshop }) {
bodyshop.scoreboard_target.dailyPaintTarget,
1
),
- accHrs: _.round(
+ accHrs: round(
acc.length > 0
? acc[acc.length - 1].accHrs + dayhrs.painthrs + dayhrs.bodyhrs
: dayhrs.painthrs + dayhrs.bodyhrs,
1
),
- sales: _.round(dayhrs.sales, 2),
- accSales: _.round(acc.length > 0 ? acc[acc.length - 1].accSales + dayhrs.sales : dayhrs.sales, 2)
+ sales: round(dayhrs.sales, 2),
+ accSales: round(acc.length > 0 ? acc[acc.length - 1].accSales + dayhrs.sales : dayhrs.sales, 2)
};
return [...acc, theValue];
diff --git a/client/src/components/scoreboard-display/scoreboard-display.component.jsx b/client/src/components/scoreboard-display/scoreboard-display.component.jsx
index 3d8a2f98a..053aef778 100644
--- a/client/src/components/scoreboard-display/scoreboard-display.component.jsx
+++ b/client/src/components/scoreboard-display/scoreboard-display.component.jsx
@@ -1,23 +1,25 @@
-import { Col, Row } from "antd";
-import { useEffect } from "react";
+import { Col, Row, Spin } from "antd";
+import { useEffect, useState } from "react";
import ScoreboardChart from "../scoreboard-chart/scoreboard-chart.component";
import ScoreboardLastDays from "../scoreboard-last-days/scoreboard-last-days.component";
import ScoreboardTargetsTable from "../scoreboard-targets-table/scoreboard-targets-table.component";
-
import { useApolloClient, useQuery } from "@apollo/client";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { GET_BLOCKED_DAYS, QUERY_SCOREBOARD } from "../../graphql/scoreboard.queries";
import { selectBodyshop } from "../../redux/user/user.selectors";
import dayjs from "../../utils/day";
+import {
+ clearHolidays,
+ clearWorkingWeekdays,
+ setHolidays,
+ setWorkingWeekdays
+} from "../scoreboard-targets-table/scoreboard-targets-table.util";
const mapStateToProps = createStructuredSelector({
- //currentUser: selectCurrentUser
bodyshop: selectBodyshop
});
-const mapDispatchToProps = (dispatch) => ({
- //setUserLanguage: language => dispatch(setUserLanguage(language))
-});
+const mapDispatchToProps = () => ({});
export default connect(mapStateToProps, mapDispatchToProps)(ScoreboardDisplayComponent);
export function ScoreboardDisplayComponent({ bodyshop }) {
@@ -32,7 +34,6 @@ export function ScoreboardDisplayComponent({ bodyshop }) {
const { data } = scoreboardSubscription;
const client = useApolloClient();
const scoreBoardlist = data?.scoreboard || [];
-
const sbEntriesByDate = {};
scoreBoardlist.forEach((i) => {
@@ -43,35 +44,52 @@ export function ScoreboardDisplayComponent({ bodyshop }) {
sbEntriesByDate[entryDate].push(i);
});
- useEffect(() => {
- //Update the locals.
- async function setDayJSSettings() {
- let appointments;
+ const [loading, setLoading] = useState(true); // Loading state
- if (!bodyshop.scoreboard_target.ignoreblockeddays) {
- const { data } = await client.query({
- query: GET_BLOCKED_DAYS,
- variables: {
- start: dayjs().startOf("month"),
- end: dayjs().endOf("month")
- }
- });
- appointments = data.appointments;
- }
- dayjs.updateLocale(dayjs.locale(), {
- workingWeekdays: translateSettingsToWorkingDays(bodyshop.workingdays),
- ...(appointments?.length
- ? {
- holidays: appointments.map((h) => dayjs(h.start).format("MM-DD-YYYY"))
+ useEffect(() => {
+ async function setDayJSSettings() {
+ try {
+ let appointments;
+
+ if (!bodyshop.scoreboard_target.ignoreblockeddays) {
+ const { data } = await client.query({
+ query: GET_BLOCKED_DAYS,
+ variables: {
+ start: dayjs().startOf("month"),
+ end: dayjs().endOf("month")
}
- : {}),
- holidayFormat: "MM-DD-YYYY"
- });
+ });
+ appointments = data.appointments;
+ }
+
+ const holidays = appointments ? appointments.map((h) => dayjs(h.start).format("MM-DD-YYYY")) : [];
+ const workingWeekdays = translateSettingsToWorkingDays(bodyshop.workingdays);
+
+ // Set holidays and working weekdays
+ setHolidays(holidays);
+ setWorkingWeekdays(workingWeekdays);
+ } finally {
+ setLoading(false); // Set loading to false after processing
+ }
}
setDayJSSettings();
+
+ // Cleanup on unmount
+ return () => {
+ clearHolidays();
+ clearWorkingWeekdays();
+ };
}, [client, bodyshop]);
+ if (loading) {
+ return (
+
+
+
+ );
+ }
+
return (
@@ -89,27 +107,12 @@ export function ScoreboardDisplayComponent({ bodyshop }) {
function translateSettingsToWorkingDays(workingdays) {
const days = [];
-
- if (workingdays.monday) {
- days.push(1);
- }
- if (workingdays.tuesday) {
- days.push(2);
- }
- if (workingdays.wednesday) {
- days.push(3);
- }
- if (workingdays.thursday) {
- days.push(4);
- }
- if (workingdays.friday) {
- days.push(5);
- }
- if (workingdays.saturday) {
- days.push(6);
- }
- if (workingdays.sunday) {
- days.push(0);
- }
+ if (workingdays.monday) days.push(1);
+ if (workingdays.tuesday) days.push(2);
+ if (workingdays.wednesday) days.push(3);
+ if (workingdays.thursday) days.push(4);
+ if (workingdays.friday) days.push(5);
+ if (workingdays.saturday) days.push(6);
+ if (workingdays.sunday) days.push(0);
return days;
}
diff --git a/client/src/components/scoreboard-last-days/scoreboard-last-days.component.jsx b/client/src/components/scoreboard-last-days/scoreboard-last-days.component.jsx
index d511bc96e..f0140d8e7 100644
--- a/client/src/components/scoreboard-last-days/scoreboard-last-days.component.jsx
+++ b/client/src/components/scoreboard-last-days/scoreboard-last-days.component.jsx
@@ -1,4 +1,3 @@
-import React from "react";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { selectBodyshop } from "../../redux/user/user.selectors";
@@ -10,7 +9,7 @@ import LoadingSkeleton from "../loading-skeleton/loading-skeleton.component";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop
});
-const mapDispatchToProps = (dispatch) => ({
+const mapDispatchToProps = () => ({
//setUserLanguage: language => dispatch(setUserLanguage(language))
});
@@ -26,7 +25,7 @@ export function ScoreboardLastDays({ bodyshop, sbEntriesByDate }) {
{ArrayOfDate.map((a) => (
- {!!sbEntriesByDate ? : }
+ {sbEntriesByDate ? : }
))}
diff --git a/client/src/components/scoreboard-targets-table/scoreboard-targets-table.component.jsx b/client/src/components/scoreboard-targets-table/scoreboard-targets-table.component.jsx
index c017b0267..ebc3b52b9 100644
--- a/client/src/components/scoreboard-targets-table/scoreboard-targets-table.component.jsx
+++ b/client/src/components/scoreboard-targets-table/scoreboard-targets-table.component.jsx
@@ -1,8 +1,8 @@
import { CalendarOutlined } from "@ant-design/icons";
import { Card, Col, Divider, Row, Statistic } from "antd";
-import _ from "lodash";
+import { groupBy } from "lodash";
import dayjs from "../../utils/day";
-import React, { useMemo } from "react";
+import { useMemo } from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
@@ -13,7 +13,7 @@ import * as Util from "./scoreboard-targets-table.util";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop
});
-const mapDispatchToProps = (dispatch) => ({
+const mapDispatchToProps = () => ({
//setUserLanguage: language => dispatch(setUserLanguage(language))
});
@@ -24,7 +24,7 @@ export function ScoreboardTargetsTable({ bodyshop, scoreBoardlist }) {
const { t } = useTranslation();
const values = useMemo(() => {
- const dateHash = _.groupBy(scoreBoardlist, "date");
+ const dateHash = groupBy(scoreBoardlist, "date");
let ret = {
todayBody: 0,
@@ -213,4 +213,5 @@ export function ScoreboardTargetsTable({ bodyshop, scoreBoardlist }) {
);
}
+
export default connect(mapStateToProps, mapDispatchToProps)(ScoreboardTargetsTable);
diff --git a/client/src/components/scoreboard-targets-table/scoreboard-targets-table.util.js b/client/src/components/scoreboard-targets-table/scoreboard-targets-table.util.js
index 32703881c..af6ace810 100644
--- a/client/src/components/scoreboard-targets-table/scoreboard-targets-table.util.js
+++ b/client/src/components/scoreboard-targets-table/scoreboard-targets-table.util.js
@@ -1,29 +1,172 @@
import dayjs from "../../utils/day";
-export const CalculateWorkingDaysThisMonth = () => dayjs().endOf("month").businessDaysInMonth().length;
+const DEFAULT_WORKING_DAYS = [1, 2, 3, 4, 5]; // Default to Monday-Friday
-export const CalculateWorkingDaysInPeriod = (start, end) => dayjs(end).businessDiff(dayjs(start));
+// Module-level state for holidays and working weekdays
+let holidays = [];
+let workingWeekdays = DEFAULT_WORKING_DAYS;
-export const CalculateWorkingDaysAsOfToday = () => dayjs().endOf("day").businessDiff(dayjs().startOf("month"));
+/**
+ * Sets the holidays for the business logic.
+ * @param newHolidays
+ */
+export const setHolidays = (newHolidays = []) => {
+ holidays = newHolidays;
+};
-export const CalculateWorkingDaysLastMonth = () =>
- dayjs().subtract(1, "month").endOf("month").businessDaysInMonth().length;
+/**
+ * Clears the holidays.
+ */
+export const clearHolidays = () => {
+ holidays = [];
+};
+/**
+ * Sets the working weekdays for the business logic.
+ * @param newWorkingWeekdays
+ */
+export const setWorkingWeekdays = (newWorkingWeekdays = DEFAULT_WORKING_DAYS) => {
+ workingWeekdays = newWorkingWeekdays;
+};
+
+/**
+ * Clears the working weekdays, resetting to default (Monday-Friday).
+ */
+export const clearWorkingWeekdays = () => {
+ workingWeekdays = DEFAULT_WORKING_DAYS; // Reset to default
+};
+
+/**
+ * Translates the bodyshop working days settings to an array of weekdays.
+ * @returns {*[]}
+ */
+export const getHolidays = () => {
+ return holidays;
+};
+
+/**
+ * Translates the working days settings from the bodyshop to an array of weekdays.
+ * @returns {number[]}
+ */
+export const getWorkingWeekdays = () => {
+ return workingWeekdays;
+};
+
+/**
+ * Calculates the number of working days in the current month, excluding holidays.
+ * @returns {number}
+ * @constructor
+ */
+export const CalculateWorkingDaysThisMonth = () => {
+ const businessDays = dayjs().businessDaysInMonth();
+ return businessDays.filter((day) => !holidays.includes(dayjs(day).format("MM-DD-YYYY"))).length;
+};
+
+/**
+ * Calculates the number of working days in a given period, excluding holidays.
+ * @param start
+ * @param end
+ * @returns {number}
+ * @constructor
+ */
+export const CalculateWorkingDaysInPeriod = (start, end) => {
+ let businessDays = dayjs(end).businessDiff(dayjs(start));
+ if (dayjs(end).isBusinessDay() && !holidays.includes(dayjs(end).format("MM-DD-YYYY"))) {
+ businessDays += 1;
+ }
+ return businessDays;
+};
+
+/**
+ * Calculates the number of working days as of today, excluding holidays.
+ * @returns {number}
+ * @constructor
+ */
+export const CalculateWorkingDaysAsOfToday = () => {
+ const today = dayjs().startOf("day");
+ let businessDays = today.businessDiff(dayjs().startOf("month"));
+ if (today.isBusinessDay() && !holidays.includes(today.format("MM-DD-YYYY"))) {
+ businessDays += 1;
+ }
+ return businessDays;
+};
+
+/**
+ * Calculates the number of working days in the last month, excluding holidays.
+ * @returns {number}
+ * @constructor
+ */
+export const CalculateWorkingDaysLastMonth = () => {
+ const businessDays = dayjs().subtract(1, "month").businessDaysInMonth();
+ return businessDays.filter((day) => !holidays.includes(dayjs(day).format("MM-DD-YYYY"))).length;
+};
+
+/**
+ * Calculates the weekly target hours based on daily target hours and the number of working days in the current week.
+ * @param dailyTargetHrs
+ * @returns {number}
+ * @constructor
+ */
export const WeeklyTargetHrs = (dailyTargetHrs) =>
dailyTargetHrs * CalculateWorkingDaysInPeriod(dayjs().startOf("week"), dayjs().endOf("week"));
+/**
+ * Calculates the weekly target hours for a specific period.
+ * @param dailyTargetHrs
+ * @param start
+ * @param end
+ * @returns {number}
+ * @constructor
+ */
export const WeeklyTargetHrsInPeriod = (dailyTargetHrs, start, end) =>
dailyTargetHrs * CalculateWorkingDaysInPeriod(start, end);
+/**
+ * Calculates the monthly target hours based on daily target hours and the number of working days in the current month.
+ * @param dailyTargetHrs
+ * @returns {number}
+ * @constructor
+ */
export const MonthlyTargetHrs = (dailyTargetHrs) => dailyTargetHrs * CalculateWorkingDaysThisMonth();
+/**
+ * Calculates the monthly target hours for the last month based on daily target hours and the number of working days
+ * in the last month.
+ * @param dailyTargetHrs
+ * @returns {number}
+ * @constructor
+ */
export const LastMonthTargetHrs = (dailyTargetHrs) => dailyTargetHrs * CalculateWorkingDaysLastMonth();
+/**
+ * Calculates the target hours as of today based on daily target hours and the number of working days as of today.
+ * @param dailyTargetHrs
+ * @returns {number}
+ * @constructor
+ */
export const AsOfTodayTargetHrs = (dailyTargetHrs) => dailyTargetHrs * CalculateWorkingDaysAsOfToday();
-export const AsOfDateTargetHours = (dailyTargetHours, date) =>
- dailyTargetHours * dayjs(date).businessDiff(dayjs().startOf("month"));
+/**
+ * Calculates the target hours as of a specific date based on daily target hours and the number of business days up to
+ * that date.
+ * @param dailyTargetHours
+ * @param date
+ * @returns {number}
+ * @constructor
+ */
+export const AsOfDateTargetHours = (dailyTargetHours, date) => {
+ let businessDays = dayjs(date).businessDiff(dayjs().startOf("month"));
+ if (dayjs(date).isBusinessDay() && !holidays.includes(dayjs(date).format("MM-DD-YYYY"))) {
+ businessDays += 1;
+ }
+ return dailyTargetHours * businessDays;
+};
+/**
+ * Generates a list of all days in the current month.
+ * @returns {*[]}
+ * @constructor
+ */
export const ListOfDaysInCurrentMonth = () => {
const days = [];
let dateStart = dayjs().startOf("month");
@@ -36,6 +179,13 @@ export const ListOfDaysInCurrentMonth = () => {
return days;
};
+/**
+ * Generates a list of all days between two dates.
+ * @param start
+ * @param end
+ * @returns {*[]}
+ * @constructor
+ */
export const ListDaysBetween = ({ start, end }) => {
const days = [];
let dateStart = dayjs(start);