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 aebc697a4..4f5b3910c 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
@@ -6,7 +6,6 @@ import { PageHeader } from "@ant-design/pro-layout";
import React, { useEffect, useMemo, useState } from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
-import { Sticky, StickyContainer } from "react-sticky";
import { createStructuredSelector } from "reselect";
import styled from "styled-components";
import { logImEXEvent } from "../../firebase/firebase.utils";
@@ -64,7 +63,7 @@ export function ProductionBoardKanbanComponent({
}, [associationSettings]);
useEffect(() => {
- const boardData = createBoardData(
+ const boardData = createFakeBoardData(
[...bodyshop.md_ro_statuses.production_statuses, ...(bodyshop.md_ro_statuses.additional_board_statuses || [])],
data,
filter
@@ -114,89 +113,90 @@ export function ProductionBoardKanbanComponent({
};
// TODO, refine and use action
- const onDragEnd = async ({ draggableId, type, source, reason, mode, destination, combine }) => {
- //cardId, sourceLaneId, targetLaneId, position, cardDetails
- logImEXEvent("kanban_drag_end");
-
- // Early Gate
- if (!type || type !== "lane" || !source || !destination) return;
-
- setIsMoving(true);
-
- const sameColumnTransfer = source.droppableId === destination.droppableId;
- const targetLane = boardLanes.lanes[Number.parseInt(destination.droppableId)];
- const sourceLane = boardLanes.lanes[Number.parseInt(source.droppableId)];
- const sourceCard = getCardByID(boardLanes, draggableId);
-
- const movedCardWillBeFirst = destination.index === 0;
- const movedCardWillBeLast = destination.index > targetLane.cards.length - 1;
- const movedCardIsFirstNewCard = movedCardWillBeFirst && movedCardWillBeLast;
-
- const lastCardInTargetLane = targetLane.cards[targetLane.cards.length - 1];
-
- const oldChildCard = sourceLane.cards[destination.index + 1];
-
+ const onDragEnd = async (...args) => {
+ console.dir(args);
+ // //cardId, sourceLaneId, targetLaneId, position, cardDetails
+ // logImEXEvent("kanban_drag_end");
//
- const newChildCard = movedCardWillBeLast
- ? null
- : targetLane.cards[
- sameColumnTransfer
- ? destination.index - destination.index > 0
- ? destination.index
- : destination.index + 1
- : destination.index
- ];
-
- const oldChildCardNewParent = oldChildCard ? sourceCard.metadata.kanbanparent : null;
-
- let movedCardNewKanbanParent;
- if (movedCardWillBeFirst) {
- movedCardNewKanbanParent = "-1";
- } else if (movedCardWillBeLast) {
- movedCardNewKanbanParent = lastCardInTargetLane.id;
- } else if (!!newChildCard) {
- movedCardNewKanbanParent = newChildCard.metadata.kanbanparent;
- } else {
- console.log("==> !!!!!!Couldn't find a parent.!!!! <==");
- }
- const newChildCardNewParent = newChildCard ? draggableId : null;
- try {
- const update = await client.mutate({
- mutation: generate_UPDATE_JOB_KANBAN(
- oldChildCard ? oldChildCard.id : null,
- oldChildCardNewParent,
- draggableId,
- movedCardNewKanbanParent,
- targetLane.id,
- newChildCard ? newChildCard.id : null,
- newChildCardNewParent
- )
- });
-
- insertAuditTrail({
- jobid: draggableId,
- operation: AuditTrailMapping.jobstatuschange(targetLane.id),
- type: "jobstatuschange"
- });
-
- if (update.errors) {
- notification["error"]({
- message: t("production.errors.boardupdate", {
- message: JSON.stringify(update.errors)
- })
- });
- }
- } catch (error) {
- notification["error"]({
- message: t("production.errors.boardupdate", {
- message: error.message
- })
- });
- } finally {
- setIsMoving(false);
- }
+ // // Early Gate
+ // if (!type || type !== "lane" || !source || !destination) return;
+ //
+ // setIsMoving(true);
+ //
+ // const sameColumnTransfer = source.droppableId === destination.droppableId;
+ // const targetLane = boardLanes.lanes[Number.parseInt(destination.droppableId)];
+ // const sourceLane = boardLanes.lanes[Number.parseInt(source.droppableId)];
+ // const sourceCard = getCardByID(boardLanes, draggableId);
+ //
+ // const movedCardWillBeFirst = destination.index === 0;
+ // const movedCardWillBeLast = destination.index > targetLane.cards.length - 1;
+ // const movedCardIsFirstNewCard = movedCardWillBeFirst && movedCardWillBeLast;
+ //
+ // const lastCardInTargetLane = targetLane.cards[targetLane.cards.length - 1];
+ //
+ // const oldChildCard = sourceLane.cards[destination.index + 1];
+ //
+ // //
+ // const newChildCard = movedCardWillBeLast
+ // ? null
+ // : targetLane.cards[
+ // sameColumnTransfer
+ // ? destination.index - destination.index > 0
+ // ? destination.index
+ // : destination.index + 1
+ // : destination.index
+ // ];
+ //
+ // const oldChildCardNewParent = oldChildCard ? sourceCard.metadata.kanbanparent : null;
+ //
+ // let movedCardNewKanbanParent;
+ // if (movedCardWillBeFirst) {
+ // movedCardNewKanbanParent = "-1";
+ // } else if (movedCardWillBeLast) {
+ // movedCardNewKanbanParent = lastCardInTargetLane.id;
+ // } else if (!!newChildCard) {
+ // movedCardNewKanbanParent = newChildCard.metadata.kanbanparent;
+ // } else {
+ // console.log("==> !!!!!!Couldn't find a parent.!!!! <==");
+ // }
+ // const newChildCardNewParent = newChildCard ? draggableId : null;
+ // try {
+ // const update = await client.mutate({
+ // mutation: generate_UPDATE_JOB_KANBAN(
+ // oldChildCard ? oldChildCard.id : null,
+ // oldChildCardNewParent,
+ // draggableId,
+ // movedCardNewKanbanParent,
+ // targetLane.id,
+ // newChildCard ? newChildCard.id : null,
+ // newChildCardNewParent
+ // )
+ // });
+ //
+ // insertAuditTrail({
+ // jobid: draggableId,
+ // operation: AuditTrailMapping.jobstatuschange(targetLane.id),
+ // type: "jobstatuschange"
+ // });
+ //
+ // if (update.errors) {
+ // notification["error"]({
+ // message: t("production.errors.boardupdate", {
+ // message: JSON.stringify(update.errors)
+ // })
+ // });
+ // }
+ // } catch (error) {
+ // notification["error"]({
+ // message: t("production.errors.boardupdate", {
+ // message: error.message
+ // })
+ // });
+ // } finally {
+ // setIsMoving(false);
+ // }
+ // };
};
-
const totalHrs = useMemo(
() =>
data
@@ -219,45 +219,7 @@ export function ProductionBoardKanbanComponent({
[data]
);
- const selectedBreakpoint = Object.entries(Grid.useBreakpoint())
- .filter((screen) => !!screen[1])
- .slice(-1)[0];
-
- const standardSizes = {
- xs: "250",
- sm: "250",
- md: "250",
- lg: "250",
- xl: "250",
- xxl: "250"
- };
-
- const compactSizes = {
- xs: "150",
- sm: "150",
- md: "150",
- lg: "150",
- xl: "155",
- xxl: "155"
- };
-
- const width = selectedBreakpoint
- ? associationSettings && associationSettings.kanban_settings && associationSettings.kanban_settings.compact
- ? compactSizes[selectedBreakpoint[0]]
- : standardSizes[selectedBreakpoint[0]]
- : "250";
-
- const StickyHeader = ({ title }) => (
-
- {({ style }) => (
-
- {title}
-
- )}
-
- );
-
- const NormalHeader = ({ title }) => (
+ const Header = ({ title }) => (
{title}
@@ -285,7 +247,7 @@ export function ProductionBoardKanbanComponent({
const components = {
Card: (cardProps) => ProductionBoardCard({ card: cardProps, technician, bodyshop, cardSettings }),
- LaneHeader: cardSettings.stickyheader && orientation === "horizontal" ? StickyHeader : NormalHeader
+ LaneHeader: Header
};
if (loading) {
@@ -293,7 +255,7 @@ export function ProductionBoardKanbanComponent({
}
return (
-
+
{cardSettings.cardcolor &&
}
- {cardSettings.stickyheader ? (
-
-
-
- ) : (
-
-
-
- )}
-
+
+
);
}
export default connect(mapStateToProps, mapDispatchToProps)(ProductionBoardKanbanComponent);
-
-const Container = styled.div`
- .react-trello-card-skeleton,
- .react-trello-card,
- .react-trello-card-adder-form {
- box-sizing: border-box;
- max-width: ${(props) => props.width}px;
- min-width: ${(props) => props.width}px;
- }
-`;
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 92666e1a5..1657d398e 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
@@ -2,12 +2,12 @@
padding: 5px;
}
-.react-trello-card {
- border-radius: 3px;
- background-color: #fff;
- padding: 4px;
- margin-bottom: 7px;
-}
+//.react-trello-card {
+// border-radius: 3px;
+// background-color: #fff;
+// padding: 4px;
+// margin-bottom: 7px;
+//}
// .react-trello-card-skeleton,
// .react-trello-card,
@@ -33,12 +33,12 @@
justify-content: space-between;
}
-.react-trello-column {
- padding: 10px;
- border-radius: 2px;
- background-color: #eee;
- margin: 5px;
-}
+//.react-trello-column {
+// padding: 10px;
+// border-radius: 2px;
+// background-color: #eee;
+// margin: 5px;
+//}
.react-trello-column input:focus {
outline: none;
@@ -84,6 +84,10 @@
width: 100%;
padding: 0px;
}
+.height-preserving-container:empty {
+ min-height: calc(var(--child-height));
+ box-sizing: border-box;
+}
.react-trello-card-adder-form__title:focus {
outline: none;
diff --git a/client/src/components/trello-board/controllers/Lane.jsx b/client/src/components/trello-board/controllers/Lane.jsx
index c97175488..f87ebfb50 100644
--- a/client/src/components/trello-board/controllers/Lane.jsx
+++ b/client/src/components/trello-board/controllers/Lane.jsx
@@ -1,4 +1,4 @@
-import React, { useCallback, useEffect, useMemo, useRef, useState } from "react";
+import React, { forwardRef, useCallback, useEffect, useRef, useState } from "react";
import classNames from "classnames";
import PropTypes from "prop-types";
import { bindActionCreators } from "redux";
@@ -7,55 +7,32 @@ import isEqual from "lodash/isEqual";
import { v1 } from "uuid";
import * as actions from "../../../redux/trello/trello.actions.js";
-import { Droppable, Draggable } from "../dnd/lib/index.js";
+import { Draggable, Droppable } from "../dnd/lib";
+import { Virtuoso, VirtuosoGrid } from "react-virtuoso";
+
+function HeightPreservingItem({ children, ...props }) {
+ const [size, setSize] = useState(0);
+ const { "data-known-size": knownSize = 0 } = props;
+
+ useEffect(() => {
+ if (knownSize !== 0) {
+ setSize(knownSize);
+ }
+ }, [knownSize]);
+
+ return (
+
+ {children}
+
+ );
+}
-/**
- * Lane is a React component that represents a lane in a Trello-like board.
- * It uses Redux for state management and provides a variety of props to customize its behavior.
- *
- * @component
- * @param {Object} props - Component props
- * @param {Object} props.actions - Redux actions
- * @param {string} props.id - The unique identifier for the lane
- * @param {string} props.boardId - The unique identifier for the board
- * @param {string} props.title - The title of the lane
- * @param {number} props.index - The index of the lane
- * @param {Function} props.laneSortFunction - Function to sort the cards in the lane
- * @param {Object} props.style - The CSS styles to apply to the lane
- * @param {Object} props.cardStyle - The CSS styles to apply to the cards
- * @param {Object} props.tagStyle - The CSS styles to apply to the tags
- * @param {Object} props.titleStyle - The CSS styles to apply to the title
- * @param {Object} props.labelStyle - The CSS styles to apply to the label
- * @param {Array} props.cards - The cards in the lane
- * @param {string} props.label - The label of the lane
- * @param {boolean} props.draggable - Whether the lane is draggable
- * @param {boolean} props.collapsibleLanes - Whether the lanes are collapsible
- * @param {boolean} props.droppable - Whether the lane is droppable
- * @param {Function} props.onCardClick - Callback function when a card is clicked
- * @param {Function} props.onBeforeCardDelete - Callback function before a card is deleted
- * @param {Function} props.onCardDelete - Callback function when a card is deleted
- * @param {Function} props.onCardAdd - Callback function when a card is added
- * @param {Function} props.onCardUpdate - Callback function when a card is updated
- * @param {Function} props.onLaneDelete - Callback function when a lane is deleted
- * @param {Function} props.onLaneUpdate - Callback function when a lane is updated
- * @param {Function} props.onLaneClick - Callback function when a lane is clicked
- * @param {Function} props.onLaneScroll - Callback function when a lane is scrolled
- * @param {boolean} props.editable - Whether the lane is editable
- * @param {boolean} props.cardDraggable - Whether the cards are draggable
- * @param {string} props.cardDragClass - The CSS class to apply when a card is being dragged
- * @param {string} props.cardDropClass - The CSS class to apply when a card is dropped
- * @param {boolean} props.canAddLanes - Whether lanes can be added to the board
- * @param {boolean} props.hideCardDeleteIcon - Whether to hide the card delete icon
- * @param {Object} props.components - Custom components to use in the lane
- * @param {Function} props.getCardDetails - Function to get the details of a card
- * @param {Function} props.handleDragStart - Callback function when a drag starts
- * @param {Function} props.handleDragEnd - Callback function when a drag ends
- * @param {string} props.orientation - The orientation of the lane ("horizontal" or "vertical")
- * @param {string} props.className - The CSS class to apply to the lane
- * @param {number} props.currentPage - The current page of the lane
- * @param {Object} props.otherProps - Any other props to pass to the lane
- * @returns {JSX.Element} A lane in a Trello-like board
- */
function Lane({
actions,
id,
@@ -101,20 +78,9 @@ function Lane({
const [currentPageFinal, setCurrentPageFinal] = useState(currentPage);
const [addCardMode, setAddCardMode] = useState(false);
const [collapsed, setCollapsed] = useState(false);
- // const [isDraggingOver, setIsDraggingOver] = useState(false);
const laneRef = useRef(null);
- const flexStyle = useMemo(() => {
- return orientation === "vertical"
- ? {
- display: "flex",
- flexWrap: "wrap",
- minHeight: "10px"
- }
- : {};
- }, [orientation]);
-
useEffect(() => {
if (!isEqual(cards, currentPageFinal)) {
setCurrentPageFinal(currentPage);
@@ -222,17 +188,19 @@ function Lane({
collapsibleLanes && setCollapsed(!collapsed);
};
- // const groupName = `TrelloBoard${boardId}Lane`;
-
- const renderDragContainer = (isDraggingOver) => {
- const stableCards = collapsed ? [] : cards;
-
- const cardList = sortCards(stableCards, laneSortFunction).map((card, idx) => {
- const onDeleteCard = () => removeCard(card.id);
- const cardToRender = (
+ const Card = React.memo(({ provided, item: card, isDragging }) => {
+ const onDeleteCard = () => removeCard(card.id);
+ return (
+
- );
+
+ );
+ });
- return cardDraggable && (!card.hasOwnProperty("draggable") || card.draggable) ? (
-
- {(provided, snapshot) => {
- return (
-
- {cardToRender}
-
- );
- }}
-
- ) : (
- {cardToRender}
- );
- });
+ const renderDraggable = (index, item) => (
+
+ {(provided, snapshot) => }
+
+ );
+
+ const renderAddCardLink = () =>
+ editable && !addCardMode && ;
+
+ const renderNewCardForm = () =>
+ addCardMode && ;
+
+ const ItemWrapper = ({ children, ...props }) => (
+
+ {children}
+
+ );
+
+ const gridComponents = {
+ List: forwardRef(({ style, children, ...props }, ref) => (
+
+ {children}
+
+ )),
+ Item: ({ children, ...props }) => (
+
+ {children}
+
+ )
+ };
+
+ const renderDroppable = (provided, renderedCards) => {
+ const Component = orientation === "vertical" ? VirtuosoGrid : Virtuoso;
+
+ const commonProps = {
+ useWindowScroll: true,
+ data: renderedCards
+ };
+
+ const componentProps =
+ orientation === "vertical"
+ ? {
+ ...commonProps,
+ scrollerRef: provided.innerRef,
+ listClassName: "grid-container",
+ itemClassName: "grid-item",
+ components: gridComponents,
+ itemContent: (index, item) => {renderDraggable(index, item)}
+ }
+ : {
+ ...commonProps,
+ overscan: {
+ main: 22,
+ reverse: 22
+ },
+ components: { Item: HeightPreservingItem },
+ itemContent: (index, item) => renderDraggable(index, item),
+ scrollerRef: provided.innerRef
+ };
return (
-
- {(provided, snapshot) => (
-
- {cardList}
- {editable && !addCardMode && }
- {addCardMode && }
- {provided.placeholder}
-
+
+
+ {renderAddCardLink()}
+ {renderNewCardForm()}
+ {provided.placeholder}
+
+ );
+ };
+
+ const renderDragContainer = () => {
+ if (collapsed) return <>>;
+ const renderedCards = sortCards(cards, laneSortFunction);
+ return (
+ (
+
)}
+ >
+ {(provided) => renderDroppable(provided, renderedCards)}
);
};
diff --git a/client/src/components/trello-board/helpers/LaneHelper.js b/client/src/components/trello-board/helpers/LaneHelper.js
index 071ba2ad3..d84d1c16c 100644
--- a/client/src/components/trello-board/helpers/LaneHelper.js
+++ b/client/src/components/trello-board/helpers/LaneHelper.js
@@ -89,42 +89,47 @@ const LaneHelper = {
},
// TODO: This has been updated to new DND Lib, verified.
- moveCardAcrossLanes: (state, { fromLaneId, toLaneId, cardId, index }) => {
- // Clone the state to avoid mutation
- const newLanes = cloneDeep(state.lanes);
-
- // Find the source and destination lanes using the lane indices
- const fromLane = newLanes[fromLaneId];
- const toLane = newLanes[toLaneId];
-
- // Find the card in the source lane
- const cardIndex = fromLane.cards.findIndex((card) => card.id === cardId);
- if (cardIndex === -1) {
- throw new Error("Card not found in the source lane");
- }
-
- // Remove the card from the source lane
- const [card] = fromLane.cards.splice(cardIndex, 1);
-
- // Insert the card into the destination lane at the specified index
- toLane.cards.splice(index, 0, card);
-
- let idx = 0;
-
- // Update the lane and card indexes for all lanes
- newLanes.forEach((lane, laneIndex) => {
- lane.cards.forEach((card, cardIndex) => {
- card.idx = idx;
- card.laneIndex = laneIndex;
- card.cardIndex = cardIndex;
- card.laneId = lane.id;
- idx++;
- });
- });
-
- return update(state, {
- lanes: { $set: newLanes }
- });
+ moveCardAcrossLanes: (state, ...args) => {
+ return state;
+ // console.dir({
+ // state,
+ // args: { fromLaneId, toLaneId, cardId, index }
+ // });
+ // // Clone the state to avoid mutation
+ // const newLanes = cloneDeep(state.lanes);
+ //
+ // // Find the source and destination lanes using the lane indices
+ // const fromLane = newLanes[fromLaneId];
+ // const toLane = newLanes[toLaneId];
+ //
+ // // Find the card in the source lane
+ // const cardIndex = fromLane.cards.findIndex((card) => card.id === cardId);
+ // if (cardIndex === -1) {
+ // throw new Error("Card not found in the source lane");
+ // }
+ //
+ // // Remove the card from the source lane
+ // const [card] = fromLane.cards.splice(cardIndex, 1);
+ //
+ // // Insert the card into the destination lane at the specified index
+ // toLane.cards.splice(index, 0, card);
+ //
+ // let idx = 0;
+ //
+ // // Update the lane and card indexes for all lanes
+ // newLanes.forEach((lane, laneIndex) => {
+ // lane.cards.forEach((card, cardIndex) => {
+ // card.idx = idx;
+ // card.laneIndex = laneIndex;
+ // card.cardIndex = cardIndex;
+ // card.laneId = lane.id;
+ // idx++;
+ // });
+ // });
+ //
+ // return update(state, {
+ // lanes: { $set: newLanes }
+ // });
},
updateCardsForLane: (state, { laneId, cards }) => {
diff --git a/client/src/components/trello-board/styles/Base.js b/client/src/components/trello-board/styles/Base.js
index bee24c08c..b9f3f791a 100644
--- a/client/src/components/trello-board/styles/Base.js
+++ b/client/src/components/trello-board/styles/Base.js
@@ -9,10 +9,7 @@ const getBoardWrapperStyles = (props) => {
// TODO: The white-space: nowrap; would be a good place to offer further customization
// This will be put in the lane settings and marked as 'Horizontal Wrapping'
return `
- display: flex;
- flex-direction: row;
- overflow-x: auto;
- white-space: nowrap;
+ white-space: nowrap;
`;
}
return "";
@@ -22,14 +19,13 @@ const getSectionStyles = (props) => {
if (props.orientation === "horizontal") {
return `
display: inline-flex;
- position: relative;
flex-direction: column;
+ white-space: nowrap;
+ overflow-y: none;
`;
}
return `
margin-bottom: 10px;
- position: relative;
- flex-direction: column;
`;
};
@@ -80,6 +76,7 @@ export const StyleHorizontal = styled.div`
// TODO: This will need to be changed
min-width: 250px;
min-height: 25px;
+ margin-bottom: 42px;
}
.react-trello-lane.lane-collapsed {
@@ -99,6 +96,12 @@ export const StyleVertical = styled.div`
.react-trello-board {
display: flex;
}
+
+ .grid-container {
+ }
+
+ .grid-item {
+ }
`;
export const CustomPopoverContainer = styled(PopoverContainer)`
@@ -148,10 +151,9 @@ export const CustomPopoverContent = styled(PopoverContent)`
`;
export const BoardWrapper = styled.div`
- background-color: #ffffff;
- overflow-y: scroll;
- padding: 5px;
color: #393939;
+ overflow-x: auto;
+ overflow-y: hidden;
${getBoardWrapperStyles};
`;
@@ -166,7 +168,7 @@ export const Section = styled.section`
background-color: #e3e3e3;
border-radius: 3px;
margin: 2px 2px;
- padding: 5px;
+ padding: 2px;
${getSectionStyles};
`;
@@ -195,7 +197,6 @@ export const LaneFooter = styled.div`
export const ScrollableLane = styled.div`
flex: 1;
- overflow-y: auto;
min-width: 250px;
overflow-x: hidden;
align-self: center;