diff --git a/client/src/components/production-board-kanban-card/production-board-kanban-card-color-legend.component.jsx b/client/src/components/production-board-kanban/production-board-kanban-card-color-legend.component.jsx
similarity index 100%
rename from client/src/components/production-board-kanban-card/production-board-kanban-card-color-legend.component.jsx
rename to client/src/components/production-board-kanban/production-board-kanban-card-color-legend.component.jsx
diff --git a/client/src/components/production-board-kanban-card/production-board-kanban-card.component.jsx b/client/src/components/production-board-kanban/production-board-kanban-card.component.jsx
similarity index 96%
rename from client/src/components/production-board-kanban-card/production-board-kanban-card.component.jsx
rename to client/src/components/production-board-kanban/production-board-kanban-card.component.jsx
index 4febb86c0..e3fe39ff8 100644
--- a/client/src/components/production-board-kanban-card/production-board-kanban-card.component.jsx
+++ b/client/src/components/production-board-kanban/production-board-kanban-card.component.jsx
@@ -290,6 +290,22 @@ const PartsStatusComponent = ({ metadata, cardSettings }) =>
);
+const TasksToolTip = ({ metadata, cardSettings, t }) =>
+ cardSettings?.tasks && (
+
+
+ {metadata.tasks_aggregate?.aggregate?.count ? (
+ `T: ${metadata.tasks_aggregate.aggregate.count}`
+ ) : (
+ T: 0
+ )}
+
+
+ );
+
export default function ProductionBoardCard({ technician, card, bodyshop, cardSettings, clone }) {
const { t } = useTranslation();
const { metadata } = card;
@@ -336,7 +352,8 @@ export default function ProductionBoardCard({ technician, card, bodyshop, cardSe
cardSettings?.production_note ||
cardSettings?.partsstatus ||
cardSettings?.estimator ||
- cardSettings?.subtotal
+ cardSettings?.subtotal ||
+ cardSettings?.tasks
);
}, [cardSettings]);
@@ -393,6 +410,7 @@ export default function ProductionBoardCard({ technician, card, bodyshop, cardSe
employee_csr={employee_csr}
/>
+
diff --git a/client/src/components/production-board-kanban/production-board-kanban.component.jsx b/client/src/components/production-board-kanban/production-board-kanban.component.jsx
index afd0e94e1..62917e9bd 100644
--- a/client/src/components/production-board-kanban/production-board-kanban.component.jsx
+++ b/client/src/components/production-board-kanban/production-board-kanban.component.jsx
@@ -15,13 +15,13 @@ import AuditTrailMapping from "../../utils/AuditTrailMappings";
import IndefiniteLoading from "../indefinite-loading/indefinite-loading.component";
import ProductionBoardFilters from "../production-board-filters/production-board-filters.component";
import ProductionListDetailComponent from "../production-list-detail/production-list-detail.component";
-import CardColorLegend from "../production-board-kanban-card/production-board-kanban-card-color-legend.component";
+import CardColorLegend from "./production-board-kanban-card-color-legend.component.jsx";
import "./production-board-kanban.styles.scss";
import { createBoardData } from "./production-board-kanban.utils.js";
import ProductionBoardKanbanSettings from "./settings/production-board-kanban.settings.component.jsx";
import cloneDeep from "lodash/cloneDeep";
import isEqual from "lodash/isEqual";
-import { defaultKanbanSettings } from "./settings/defaultKanbanSettings.js";
+import { mergeWithDefaults } from "./settings/defaultKanbanSettings.js";
import NoteUpsertModal from "../../components/note-upsert-modal/note-upsert-modal.container";
const mapStateToProps = createStructuredSelector({
@@ -182,13 +182,10 @@ function ProductionBoardKanbanComponent({ data, bodyshop, refetch, insertAuditTr
[boardLanes, client, getCardByID, isMoving, t, insertAuditTrail]
);
- const cardSettings = useMemo(
- () =>
- associationSettings?.kanban_settings && Object.keys(associationSettings.kanban_settings).length > 0
- ? associationSettings.kanban_settings
- : defaultKanbanSettings,
- [associationSettings]
- );
+ const cardSettings = useMemo(() => {
+ const kanbanSettings = associationSettings?.kanban_settings;
+ return mergeWithDefaults(kanbanSettings);
+ }, [associationSettings]);
const handleSettingsChange = useCallback((newSettings) => {
setLoading(true);
diff --git a/client/src/components/production-board-kanban/production-board-kanban.statistics.jsx b/client/src/components/production-board-kanban/production-board-kanban.statistics.jsx
index 482ddb726..1af5ec59c 100644
--- a/client/src/components/production-board-kanban/production-board-kanban.statistics.jsx
+++ b/client/src/components/production-board-kanban/production-board-kanban.statistics.jsx
@@ -2,11 +2,13 @@ import React, { useMemo } from "react";
import { Card, Statistic } from "antd";
import { useTranslation } from "react-i18next";
import PropTypes from "prop-types";
-import { statisticsItems, defaultKanbanSettings } from "./settings/defaultKanbanSettings.js";
+import { defaultKanbanSettings, statisticsItems } from "./settings/defaultKanbanSettings.js";
+
export const StatisticType = {
HOURS: "hours",
AMOUNT: "amount",
- JOBS: "jobs"
+ JOBS: "jobs",
+ TASKS: "tasks"
};
const mergeStatistics = (items, values) => {
@@ -122,6 +124,20 @@ const ProductionStatistics = ({ data, cardSettings, reducerData }) => {
return parseFloat(total.toFixed(2));
}, [reducerData, cardSettings.totalAmountOnBoard]);
+ const tasksInProduction = useMemo(() => {
+ if (!data || !cardSettings.tasksInProduction) return null;
+ return data.reduce((acc, item) => acc + (item.tasks_aggregate?.aggregate?.count || 0), 0);
+ }, [data, cardSettings.tasksInProduction]);
+
+ const tasksOnBoard = useMemo(() => {
+ if (!reducerData || !cardSettings.tasksOnBoard) return null;
+ return reducerData.lanes.reduce((acc, lane) => {
+ return (
+ acc + lane.cards.reduce((laneAcc, card) => laneAcc + (card.metadata.tasks_aggregate?.aggregate?.count || 0), 0)
+ );
+ }, 0);
+ }, [reducerData, cardSettings.tasksOnBoard]);
+
const statistics = useMemo(
() =>
mergeStatistics(statisticsItems, [
@@ -134,7 +150,9 @@ const ProductionStatistics = ({ data, cardSettings, reducerData }) => {
{ id: 6, value: totalAmountOnBoard, type: StatisticType.AMOUNT },
{ id: 7, value: totalLABOnBoard, type: StatisticType.HOURS },
{ id: 8, value: totalLAROnBoard, type: StatisticType.HOURS },
- { id: 9, value: jobsOnBoard, type: StatisticType.JOBS }
+ { id: 9, value: jobsOnBoard, type: StatisticType.JOBS },
+ { id: 10, value: tasksOnBoard, type: StatisticType.TASKS },
+ { id: 11, value: tasksInProduction, type: StatisticType.TASKS }
]),
[
totalHrs,
@@ -146,7 +164,9 @@ const ProductionStatistics = ({ data, cardSettings, reducerData }) => {
totalAmountOnBoard,
totalLABOnBoard,
totalLAROnBoard,
- jobsOnBoard
+ jobsOnBoard,
+ tasksOnBoard,
+ tasksInProduction
]
);
@@ -187,37 +207,9 @@ const ProductionStatistics = ({ data, cardSettings, reducerData }) => {
};
ProductionStatistics.propTypes = {
- data: PropTypes.arrayOf(
- PropTypes.shape({
- labhrs: PropTypes.object,
- larhrs: PropTypes.object,
- job_totals: PropTypes.object
- })
- ).isRequired,
- cardSettings: PropTypes.shape({
- totalHrs: PropTypes.bool,
- totalLAB: PropTypes.bool,
- totalLAR: PropTypes.bool,
- jobsInProduction: PropTypes.bool,
- totalAmountInProduction: PropTypes.bool,
- totalHrsOnBoard: PropTypes.bool,
- totalLABOnBoard: PropTypes.bool,
- totalLAROnBoard: PropTypes.bool,
- jobsOnBoard: PropTypes.bool,
- totalAmountOnBoard: PropTypes.bool,
- statisticsOrder: PropTypes.arrayOf(PropTypes.number)
- }).isRequired,
- reducerData: PropTypes.shape({
- lanes: PropTypes.arrayOf(
- PropTypes.shape({
- cards: PropTypes.arrayOf(
- PropTypes.shape({
- metadata: PropTypes.object
- })
- ).isRequired
- })
- ).isRequired
- })
+ data: PropTypes.array.isRequired,
+ cardSettings: PropTypes.object.isRequired,
+ reducerData: PropTypes.object
};
export default ProductionStatistics;
diff --git a/client/src/components/production-board-kanban/settings/InformationSettings.jsx b/client/src/components/production-board-kanban/settings/InformationSettings.jsx
index c50f61697..3725d1ab8 100644
--- a/client/src/components/production-board-kanban/settings/InformationSettings.jsx
+++ b/client/src/components/production-board-kanban/settings/InformationSettings.jsx
@@ -18,7 +18,8 @@ const InformationSettings = ({ t }) => (
"sublets",
"partsstatus",
"estimator",
- "subtotal"
+ "subtotal",
+ "tasks"
].map((item) => (
diff --git a/client/src/components/production-board-kanban/settings/defaultKanbanSettings.js b/client/src/components/production-board-kanban/settings/defaultKanbanSettings.js
index ce0760637..0d10e4e39 100644
--- a/client/src/components/production-board-kanban/settings/defaultKanbanSettings.js
+++ b/client/src/components/production-board-kanban/settings/defaultKanbanSettings.js
@@ -8,7 +8,9 @@ const statisticsItems = [
{ id: 6, name: "totalAmountOnBoard", label: "total_amount_on_board" },
{ id: 7, name: "totalLABOnBoard", label: "total_lab_on_board" },
{ id: 8, name: "totalLAROnBoard", label: "total_lar_on_board" },
- { id: 9, name: "jobsOnBoard", label: "total_jobs_on_board" }
+ { id: 9, name: "jobsOnBoard", label: "total_jobs_on_board" },
+ { id: 10, name: "tasksOnBoard", label: "tasks_on_board" },
+ { id: 11, name: "tasksInProduction", label: "tasks_in_production" }
];
const defaultKanbanSettings = {
@@ -23,6 +25,7 @@ const defaultKanbanSettings = {
scheduled_completion: true,
cardcolor: false,
orientation: false,
+ tasks: false,
cardSize: "small",
model_info: true,
kiosk: false,
@@ -35,6 +38,8 @@ const defaultKanbanSettings = {
totalLABOnBoard: false,
totalLAROnBoard: false,
jobsOnBoard: false,
+ tasksOnBoard: false,
+ tasksInProduction: false,
totalAmountOnBoard: true,
estimator: false,
subtotal: false,
@@ -43,4 +48,20 @@ const defaultKanbanSettings = {
selectedEstimators: []
};
-export { defaultKanbanSettings, statisticsItems };
+const mergeWithDefaults = (settings) => {
+ // Create a new object that starts with the default settings
+ const mergedSettings = { ...defaultKanbanSettings };
+
+ // Override with the provided settings, if any
+ if (settings) {
+ for (const key in settings) {
+ if (settings.hasOwnProperty(key)) {
+ mergedSettings[key] = settings[key];
+ }
+ }
+ }
+
+ return mergedSettings;
+};
+
+export { defaultKanbanSettings, statisticsItems, mergeWithDefaults };
diff --git a/client/src/components/production-board-kanban/settings/production-board-kanban.settings.component.jsx b/client/src/components/production-board-kanban/settings/production-board-kanban.settings.component.jsx
index 38b0c9350..23da13412 100644
--- a/client/src/components/production-board-kanban/settings/production-board-kanban.settings.component.jsx
+++ b/client/src/components/production-board-kanban/settings/production-board-kanban.settings.component.jsx
@@ -3,13 +3,14 @@ import { Button, Card, Col, Form, notification, Popover, Row, Tabs } from "antd"
import React, { useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
import { UPDATE_KANBAN_SETTINGS } from "../../../graphql/user.queries.js";
-import { defaultKanbanSettings } from "./defaultKanbanSettings.js";
+import { defaultKanbanSettings, mergeWithDefaults } from "./defaultKanbanSettings.js";
import LayoutSettings from "./LayoutSettings.jsx";
import InformationSettings from "./InformationSettings.jsx";
import StatisticsSettings from "./StatisticsSettings.jsx";
import FilterSettings from "./FilterSettings.jsx";
+import PropTypes from "prop-types";
-export default function ProductionBoardKanbanSettings({ associationSettings, parentLoading, bodyshop, data }) {
+function ProductionBoardKanbanSettings({ associationSettings, parentLoading, bodyshop, data }) {
const [form] = Form.useForm();
const [open, setOpen] = useState(false);
const [loading, setLoading] = useState(false);
@@ -23,16 +24,11 @@ export default function ProductionBoardKanbanSettings({ associationSettings, par
useEffect(() => {
if (associationSettings?.kanban_settings) {
- form.setFieldsValue(associationSettings.kanban_settings);
- if (associationSettings.kanban_settings.statisticsOrder) {
- setStatisticsOrder(associationSettings.kanban_settings.statisticsOrder);
- }
- if (associationSettings.kanban_settings.selectedMdInsCos) {
- setSelectedMdInsCos(associationSettings.kanban_settings.selectedMdInsCos);
- }
- if (associationSettings.kanban_settings.selectedEstimators) {
- setSelectedEstimators(associationSettings.kanban_settings.selectedEstimators);
- }
+ const finalSettings = mergeWithDefaults(associationSettings.kanban_settings);
+ form.setFieldsValue(finalSettings);
+ setStatisticsOrder(finalSettings.statisticsOrder);
+ setSelectedMdInsCos(finalSettings.selectedMdInsCos);
+ setSelectedEstimators(finalSettings.selectedEstimators);
}
}, [form, associationSettings]);
@@ -155,3 +151,12 @@ export default function ProductionBoardKanbanSettings({ associationSettings, par
);
}
+
+ProductionBoardKanbanSettings.propTypes = {
+ associationSettings: PropTypes.object,
+ parentLoading: PropTypes.func.isRequired,
+ bodyshop: PropTypes.object.isRequired,
+ data: PropTypes.array
+};
+
+export default ProductionBoardKanbanSettings;
diff --git a/client/src/components/production-board-kanban/trello-board/controllers/Lane.jsx b/client/src/components/production-board-kanban/trello-board/controllers/Lane.jsx
index 493812840..cd30952fb 100644
--- a/client/src/components/production-board-kanban/trello-board/controllers/Lane.jsx
+++ b/client/src/components/production-board-kanban/trello-board/controllers/Lane.jsx
@@ -12,7 +12,7 @@ import { EyeInvisibleOutlined, EyeOutlined } from "@ant-design/icons";
import { createStructuredSelector } from "reselect";
import { selectBodyshop } from "../../../../redux/user/user.selectors.js";
import { selectTechnician } from "../../../../redux/tech/tech.selectors.js";
-import ProductionBoardCard from "../../../production-board-kanban-card/production-board-kanban-card.component.jsx";
+import ProductionBoardCard from "../../production-board-kanban-card.component.jsx";
import HeightMemoryWrapper from "../components/HeightMemoryWrapper.jsx";
import SizeMemoryWrapper from "../components/SizeMemoryWrapper.jsx";
import ListComponent from "../components/ListComponent.jsx";
diff --git a/client/src/graphql/jobs.queries.js b/client/src/graphql/jobs.queries.js
index 51ff6927c..569936b7c 100644
--- a/client/src/graphql/jobs.queries.js
+++ b/client/src/graphql/jobs.queries.js
@@ -2465,6 +2465,11 @@ export const SUBSCRIPTION_JOBS_IN_PRODUCTION = gql`
export const QUERY_JOBS_IN_PRODUCTION = gql`
query QUERY_JOBS_IN_PRODUCTION {
jobs(where: { inproduction: { _eq: true } }) {
+ tasks_aggregate(where: { completed: { _eq: false }, deleted: { _eq: false } }) {
+ aggregate {
+ count
+ }
+ }
id
updated_at
comment
diff --git a/client/src/translations/en_us/common.json b/client/src/translations/en_us/common.json
index f3a95722e..f35baa1d8 100644
--- a/client/src/translations/en_us/common.json
+++ b/client/src/translations/en_us/common.json
@@ -2757,7 +2757,9 @@
"total_lab_on_board": "Body Hours on Board",
"total_lar_on_board": "Refinish Hours on Board",
"total_amount_on_board": "Dollars on Board",
- "total_jobs_on_board": "Jobs on Board"
+ "total_jobs_on_board": "Jobs on Board",
+ "tasks_in_production": "Tasks in Production",
+ "tasks_on_board": "Tasks on Board"
}
},
"actions": {
@@ -2792,6 +2794,7 @@
"model_info": "Vehicle Info",
"actual_in": "Actual In",
"alert": "Alert",
+ "tasks": "Tasks",
"alertoff": "Remove alert from Job",
"alerton": "Add alert to Job",
"ats": "Alternative Transportation",
@@ -2845,6 +2848,9 @@
"total_lar_on_board": "Refinish Hours on Board",
"total_amount_on_board": "Dollars on Board",
"total_jobs_on_board": "Jobs on Board",
+ "tasks_in_production": "Tasks in Production",
+ "tasks_on_board": "Tasks on Board",
+ "tasks": "Tasks",
"hours": "Hours",
"currency_symbol": "$",
"jobs": "Jobs"
diff --git a/client/src/translations/es/common.json b/client/src/translations/es/common.json
index 30cac520b..1d89e15ea 100644
--- a/client/src/translations/es/common.json
+++ b/client/src/translations/es/common.json
@@ -2757,7 +2757,9 @@
"total_lab_on_board": "",
"total_lar_on_board": "",
"total_amount_on_board": "",
- "total_jobs_on_board": ""
+ "total_jobs_on_board": "",
+ "tasks_in_production": "",
+ "tasks_on_board": ""
}
},
"actions": {
@@ -2792,6 +2794,7 @@
"model_info": "",
"actual_in": "",
"alert": "",
+ "tasks": "",
"alertoff": "",
"alerton": "",
"ats": "",
@@ -2845,6 +2848,9 @@
"total_lar_on_board": "",
"total_amount_on_board": "",
"total_jobs_on_board": "",
+ "tasks_in_production": "",
+ "tasks_on_board": "",
+ "tasks": "",
"hours": "",
"currency_symbol": "",
"jobs": ""
diff --git a/client/src/translations/fr/common.json b/client/src/translations/fr/common.json
index 967aa9003..4806de18c 100644
--- a/client/src/translations/fr/common.json
+++ b/client/src/translations/fr/common.json
@@ -2757,7 +2757,9 @@
"total_lab_on_board": "",
"total_lar_on_board": "",
"total_amount_on_board": "",
- "total_jobs_on_board": ""
+ "total_jobs_on_board": "",
+ "tasks_in_production": "",
+ "tasks_on_board": ""
}
},
"actions": {
@@ -2792,6 +2794,7 @@
"model_info": "",
"actual_in": "",
"alert": "",
+ "tasks": "",
"alertoff": "",
"alerton": "",
"ats": "",
@@ -2845,6 +2848,9 @@
"total_lar_on_board": "",
"total_amount_on_board": "",
"total_jobs_on_board": "",
+ "tasks_in_production": "",
+ "tasks_on_board": "",
+ "tasks": "",
"hours": "",
"currency_symbol": "",
"jobs": ""