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 04fba02b8..74fc1d3b0 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
@@ -17,7 +17,7 @@ import ProductionBoardFilters from "../production-board-filters/production-board
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 "./production-board-kanban.styles.scss";
-import { createBoardData } from "./production-board-kanban.utils.js";
+import { createBoardData, createFakeBoardData } from "./production-board-kanban.utils.js";
import ProductionBoardKanbanSettings from "./production-board-kanban.settings.component.jsx";
import cloneDeep from "lodash/cloneDeep";
import isEqual from "lodash/isEqual";
diff --git a/client/src/components/production-board-kanban/production-board-kanban.container.jsx b/client/src/components/production-board-kanban/production-board-kanban.container.jsx
index a93f176e2..236cf9099 100644
--- a/client/src/components/production-board-kanban/production-board-kanban.container.jsx
+++ b/client/src/components/production-board-kanban/production-board-kanban.container.jsx
@@ -1,6 +1,6 @@
import { useApolloClient, useQuery, useSubscription } from "@apollo/client";
import _ from "lodash";
-import React, { useEffect, useState } from "react";
+import React, { useEffect, useState, useCallback, useMemo } from "react";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import {
@@ -19,22 +19,44 @@ const mapStateToProps = createStructuredSelector({
});
export function ProductionBoardKanbanContainer({ bodyshop, currentUser }) {
+ const client = useApolloClient();
+ const [joblist, setJoblist] = useState([]);
+
const { refetch, loading, data } = useQuery(QUERY_JOBS_IN_PRODUCTION, {
pollInterval: 3600000,
fetchPolicy: "network-only",
nextFetchPolicy: "network-only"
});
- const client = useApolloClient();
- const [joblist, setJoblist] = useState([]);
+
const { data: updatedJobs } = useSubscription(SUBSCRIPTION_JOBS_IN_PRODUCTION);
+ const { loading: associationSettingsLoading, data: associationSettings } = useQuery(QUERY_KANBAN_SETTINGS, {
+ variables: { email: currentUser.email }
+ });
+
+ const getUpdatedJobData = useCallback(
+ async (jobId) => {
+ await client.query({
+ query: QUERY_EXACT_JOB_IN_PRODUCTION,
+ variables: { id: jobId }
+ });
+ },
+ [client]
+ );
+
+ const getUpdatedJobsData = useCallback(
+ async (jobIds) => {
+ await client.query({
+ query: QUERY_EXACT_JOBS_IN_PRODUCTION,
+ variables: { ids: jobIds }
+ });
+ },
+ [client]
+ );
+
useEffect(() => {
- if (!(data && data.jobs)) return;
- setJoblist(
- data.jobs.map((j) => {
- return { id: j.id, updated_at: j.updated_at };
- })
- );
+ if (!data?.jobs) return;
+ setJoblist(data.jobs.map((j) => ({ id: j.id, updated_at: j.updated_at })));
}, [data]);
useEffect(() => {
@@ -46,46 +68,25 @@ export function ProductionBoardKanbanContainer({ bodyshop, currentUser }) {
(a, b) => a.id === b.id && a.updated_at === b.updated_at
);
- jobDiff.forEach((job) => {
- getUpdatedJobData(job.id);
- });
if (jobDiff.length > 1) {
getUpdatedJobsData(jobDiff.map((j) => j.id));
} else if (jobDiff.length === 1) {
- jobDiff.forEach((job) => {
- getUpdatedJobData(job.id);
- });
+ getUpdatedJobData(jobDiff[0].id);
}
setJoblist(updatedJobs.jobs);
- // eslint-disable-next-line react-hooks/exhaustive-deps
- }, [updatedJobs]);
+ }, [updatedJobs, joblist, getUpdatedJobData, getUpdatedJobsData]);
- const getUpdatedJobData = async (jobId) => {
- client.query({
- query: QUERY_EXACT_JOB_IN_PRODUCTION,
- variables: { id: jobId }
- });
- };
- const getUpdatedJobsData = async (jobIds) => {
- client.query({
- query: QUERY_EXACT_JOBS_IN_PRODUCTION,
- variables: { ids: jobIds }
- });
- };
-
- const { loading: associationSettingsLoading, data: associationSettings } = useQuery(QUERY_KANBAN_SETTINGS, {
- variables: { email: currentUser.email }
- });
+ const filteredAssociationSettings = useMemo(() => {
+ return associationSettings?.associations[0] || null;
+ }, [associationSettings]);
return (
);
}
diff --git a/client/src/components/production-board-kanban/production-board-kanban.styles.scss b/client/src/components/production-board-kanban/production-board-kanban.styles.scss
index fcc02a2cf..a1b8f9fd1 100644
--- a/client/src/components/production-board-kanban/production-board-kanban.styles.scss
+++ b/client/src/components/production-board-kanban/production-board-kanban.styles.scss
@@ -1,7 +1,6 @@
.react-trello-board {
padding: 5px;
}
-
.item .is-dragging {
box-shadow: 2px 2px grey;
rotate: 5deg;
diff --git a/client/src/components/production-list-columns/production-list-columns.alert.component.jsx b/client/src/components/production-list-columns/production-list-columns.alert.component.jsx
index c24294c87..e9a4e2d62 100644
--- a/client/src/components/production-list-columns/production-list-columns.alert.component.jsx
+++ b/client/src/components/production-list-columns/production-list-columns.alert.component.jsx
@@ -2,7 +2,6 @@ import { ExclamationCircleFilled } from "@ant-design/icons";
import { useMutation } from "@apollo/client";
import { Button } from "antd";
import React, { useCallback } from "react";
-import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { logImEXEvent } from "../../firebase/firebase.utils";
@@ -13,11 +12,17 @@ import AuditTrailMapping from "../../utils/AuditTrailMappings";
const mapStateToProps = createStructuredSelector({});
const mapDispatchToProps = (dispatch) => ({
- insertAuditTrail: ({ jobid, operation, type }) => dispatch(insertAuditTrail({ jobid, operation, type }))
+ insertAuditTrail: ({ jobid, operation, type }) =>
+ dispatch(
+ insertAuditTrail({
+ jobid,
+ operation,
+ type
+ })
+ )
});
const ProductionListColumnAlert = ({ record, insertAuditTrail }) => {
- const { t } = useTranslation();
const [updateAlert] = useMutation(UPDATE_JOB);
const handleAlertToggle = useCallback(() => {
diff --git a/client/src/components/trello-board/components/Lane/HeightMemoryWrapper.jsx b/client/src/components/trello-board/components/Lane/HeightMemoryWrapper.jsx
new file mode 100644
index 000000000..e9ea79b79
--- /dev/null
+++ b/client/src/components/trello-board/components/Lane/HeightMemoryWrapper.jsx
@@ -0,0 +1,49 @@
+import React, { useEffect, useRef } from "react";
+import PropTypes from "prop-types";
+
+/**
+ * Height Memory Wrapper
+ * @param children
+ * @param maxHeight
+ * @param setMaxHeight
+ * @returns {Element}
+ * @constructor
+ */
+const HeightMemoryWrapper = ({ children, maxHeight, setMaxHeight }) => {
+ const ref = useRef(null);
+
+ useEffect(() => {
+ const currentRef = ref.current; // Step 1: Capture the current ref value
+ const updateHeight = () => {
+ const currentHeight = currentRef?.firstChild?.clientHeight || 0;
+ setMaxHeight((prevHeight) => Math.max(prevHeight, currentHeight));
+ };
+
+ const resizeObserver = new ResizeObserver(updateHeight);
+ if (currentRef?.firstChild) {
+ // Step 2: Use the captured ref for observing
+ resizeObserver.observe(currentRef.firstChild);
+ }
+
+ return () => {
+ if (currentRef?.firstChild) {
+ // Step 2: Use the captured ref for observing
+ resizeObserver.unobserve(currentRef.firstChild);
+ }
+ };
+ }, [setMaxHeight]);
+
+ return (
+
+ {children}
+
+ );
+};
+
+HeightMemoryWrapper.propTypes = {
+ children: PropTypes.node.isRequired,
+ maxHeight: PropTypes.number.isRequired,
+ setMaxHeight: PropTypes.func.isRequired
+};
+
+export default HeightMemoryWrapper;
diff --git a/client/src/components/trello-board/controllers/BoardContainer.jsx b/client/src/components/trello-board/controllers/BoardContainer.jsx
index 8ee046fbb..c11941ee8 100644
--- a/client/src/components/trello-board/controllers/BoardContainer.jsx
+++ b/client/src/components/trello-board/controllers/BoardContainer.jsx
@@ -15,11 +15,13 @@ import { BoardWrapper } from "../styles/Base.js";
* @component
* @param {Object} props - Component props
* @param {Object} props.data - The initial data for the board
+ * @param {Function} props.onDataChange - Callback function when the data changes
* @param {Function} props.onDragEnd - Callback function when a drag ends
* @param {Function} props.laneSortFunction - Callback function when a drag ends
* @param {string} props.orientation - The orientation of the board ("horizontal" or "vertical")
* @param {Function} props.eventBusHandle - Function to handle events from the event bus
* @param {Object} props.reducerData - The initial data for the Redux reducer
+ *
* @returns {JSX.Element} A Trello-like board
*/
const BoardContainer = ({
@@ -34,6 +36,7 @@ const BoardContainer = ({
}) => {
const [isDragging, setIsDragging] = useState(false);
const [isProcessing, setIsProcessing] = useState(false);
+ const [maxLaneHeight, setMaxLaneHeight] = useState(0);
const dispatch = useDispatch();
const currentReducerData = useSelector((state) => (state.trello.lanes ? state.trello : {}));
@@ -146,6 +149,8 @@ const BoardContainer = ({
isDragging={isDragging}
isProcessing={isProcessing}
cardSettings={cardSettings}
+ maxLaneHeight={maxLaneHeight}
+ setMaxLaneHeight={setMaxLaneHeight}
/>
);
})}
@@ -157,7 +162,6 @@ const BoardContainer = ({
BoardContainer.propTypes = {
id: PropTypes.string,
- actions: PropTypes.object,
data: PropTypes.object.isRequired,
reducerData: PropTypes.object,
onDataChange: PropTypes.func,
diff --git a/client/src/components/trello-board/controllers/Lane.jsx b/client/src/components/trello-board/controllers/Lane.jsx
index e100a00a2..0af0093c7 100644
--- a/client/src/components/trello-board/controllers/Lane.jsx
+++ b/client/src/components/trello-board/controllers/Lane.jsx
@@ -13,7 +13,25 @@ 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 HeightMemoryWrapper from "../components/Lane/HeightMemoryWrapper.jsx";
+/**
+ * Lane is a React component that represents a lane in a Trello-like board.
+ * @param id
+ * @param title
+ * @param index
+ * @param isProcessing
+ * @param laneSortFunction
+ * @param cards
+ * @param cardSettings
+ * @param orientation
+ * @param maxLaneHeight
+ * @param setMaxLaneHeight
+ * @param technician -- connected to redux
+ * @param bodyshop -- connected to redux
+ * @returns {Element}
+ * @constructor
+ */
const Lane = ({
id,
title,
@@ -23,6 +41,8 @@ const Lane = ({
cards,
cardSettings = {},
orientation = "vertical",
+ maxLaneHeight,
+ setMaxLaneHeight,
technician,
bodyshop
}) => {
@@ -92,7 +112,6 @@ const Lane = ({
(provided, renderedCards) => {
const Component = orientation === "vertical" ? VirtuosoGrid : Virtuoso;
const FinalComponent = collapsed ? "div" : Component;
-
const commonProps = {
useWindowScroll: true,
data: renderedCards
@@ -119,14 +138,25 @@ const Lane = ({
components: { Item: HeightPreservingItem },
overscan: { main: 3, reverse: 3 },
itemContent: (index, item) => renderDraggable(index, item),
- scrollerRef: provided.innerRef
+ scrollerRef: provided.innerRef,
+ style: {
+ minHeight: maxLaneHeight
+ }
};
const componentProps = orientation === "vertical" ? verticalProps : horizontalProps;
- const finalComponentProps = collapsed ? {} : componentProps;
+ const finalComponentProps = collapsed
+ ? orientation === "horizontal"
+ ? {
+ style: {
+ height: orientation === "vertical" ? 10 : maxLaneHeight
+ }
+ }
+ : {}
+ : componentProps;
- return (
-
+ return orientation === "horizontal" ? (
+
{isVisible && }
- {(orientation === "horizontal" || renderedCards.length === 0 || collapsed) && provided.placeholder}
+
+ ) : (
+
+ {isVisible && }
+ {provided.placeholder}
);
},
- [orientation, collapsed, isVisible, renderDraggable]
+ [orientation, collapsed, isVisible, renderDraggable, maxLaneHeight, setMaxLaneHeight]
);
const renderDragContainer = useCallback(
@@ -194,15 +233,15 @@ const Lane = ({
Lane.propTypes = {
id: PropTypes.string.isRequired,
- title: PropTypes.node,
- index: PropTypes.number,
+ title: PropTypes.node.isRequired,
+ index: PropTypes.number.isRequired,
laneSortFunction: PropTypes.func,
- cards: PropTypes.array,
- orientation: PropTypes.string,
- isProcessing: PropTypes.bool,
- cardSettings: PropTypes.object,
- technician: PropTypes.object,
- bodyshop: PropTypes.object
+ cards: PropTypes.array.isRequired,
+ orientation: PropTypes.string.isRequired,
+ isProcessing: PropTypes.bool.isRequired,
+ cardSettings: PropTypes.object.isRequired,
+ maxLaneHeight: PropTypes.number.isRequired,
+ setMaxLaneHeight: PropTypes.func.isRequired
};
const mapDispatchToProps = (dispatch) => ({
diff --git a/client/src/components/trello-board/styles/Base.js b/client/src/components/trello-board/styles/Base.js
index 376d51729..b17cdb8e8 100644
--- a/client/src/components/trello-board/styles/Base.js
+++ b/client/src/components/trello-board/styles/Base.js
@@ -1,5 +1,4 @@
-import { PopoverContainer, PopoverContent } from "react-popopo";
-import styled, { css } from "styled-components";
+import styled from "styled-components";
const getBoardWrapperStyles = (props) => {
if (props.orientation === "vertical") {
@@ -64,6 +63,7 @@ export const StyleHorizontal = styled.div`
white-space: nowrap;
min-width: 4%;
}
+
.react-trello-column-header {
border-radius: 5px;
min-height: 15px;
@@ -72,6 +72,7 @@ export const StyleHorizontal = styled.div`
overflow: hidden;
text-overflow: ellipsis;
}
+
.react-trello-card {
height: auto;
margin: 2px;
@@ -88,6 +89,7 @@ export const StyleVertical = styled.div`
text-overflow: ellipsis;
text-align: left;
}
+
.react-trello-lane {
min-height: 5px;
height: 100%;