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 0232e9067..53447dd6e 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 @@ -3,7 +3,7 @@ import { useApolloClient } from "@apollo/client"; import Board from "../../components/trello-board/index"; import { Button, Grid, notification, Skeleton, Space, Statistic } from "antd"; import { PageHeader } from "@ant-design/pro-layout"; -import React, { useEffect, useState } from "react"; +import React, { useEffect, useMemo, useState } from "react"; import { useTranslation } from "react-i18next"; import { connect } from "react-redux"; import { Sticky, StickyContainer } from "react-sticky"; @@ -71,14 +71,11 @@ export function ProductionBoardKanbanComponent({ ); let idx = 0; - const newCardIndexMappings = {}; // Build Board Lanes Data boardData.lanes = boardData.lanes.map((lane, laneIndex) => { const cardsWithIndex = lane.cards.map((card, cardIndex) => { const cardWithIndex = { ...card, idx, lane: lane.id, cardIndex, laneIndex }; - newCardIndexMappings[idx] = { laneIndex, cardIndex }; - idx++; return cardWithIndex; }); @@ -130,21 +127,10 @@ export function ProductionBoardKanbanComponent({ const sourceLane = boardLanes.lanes[Number.parseInt(source.droppableId)]; const sourceCard = getCardByID(boardLanes, draggableId); - console.dir({ - sameColumnTransfer, - targetLane, - sourceLane, - sourceCard, - destination - }); - const movedCardWillBeFirst = destination.index === 0; const movedCardWillBeLast = destination.index > targetLane.cards.length - 1; const movedCardIsFirstNewCard = movedCardWillBeFirst && movedCardWillBeLast; - console.log("movedCardWillBeFirst, movedCardWillBeLast"); - console.dir({ movedCardWillBeFirst, movedCardWillBeLast, movedCardIsFirstNewCard }); - const lastCardInTargetLane = targetLane.cards[targetLane.cards.length - 1]; const oldChildCard = sourceLane.cards[destination.index + 1]; @@ -162,13 +148,6 @@ export function ProductionBoardKanbanComponent({ const oldChildCardNewParent = oldChildCard ? sourceCard.metadata.kanbanparent : null; - console.dir({ - lastCardInTargetLane, - oldChildCard, - newChildCard, - oldChildCardNewParent - }); - let movedCardNewKanbanParent; if (movedCardWillBeFirst) { movedCardNewKanbanParent = "-1"; @@ -217,14 +196,28 @@ export function ProductionBoardKanbanComponent({ } }; - const totalHrs = data - .reduce( - (acc, val) => acc + (val.labhrs?.aggregate?.sum?.mod_lb_hrs || 0) + (val.larhrs?.aggregate?.sum?.mod_lb_hrs || 0), - 0 - ) - .toFixed(1); - const totalLAB = data.reduce((acc, val) => acc + (val.labhrs?.aggregate?.sum?.mod_lb_hrs || 0), 0).toFixed(1); - const totalLAR = data.reduce((acc, val) => acc + (val.larhrs?.aggregate?.sum?.mod_lb_hrs || 0), 0).toFixed(1); + const totalHrs = useMemo( + () => + data + .reduce( + (acc, val) => + acc + (val.labhrs?.aggregate?.sum?.mod_lb_hrs || 0) + (val.larhrs?.aggregate?.sum?.mod_lb_hrs || 0), + 0 + ) + .toFixed(1), + [data] + ); + + const totalLAB = useMemo( + () => data.reduce((acc, val) => acc + (val.labhrs?.aggregate?.sum?.mod_lb_hrs || 0), 0).toFixed(1), + [data] + ); + + const totalLAR = useMemo( + () => data.reduce((acc, val) => acc + (val.larhrs?.aggregate?.sum?.mod_lb_hrs || 0), 0).toFixed(1), + [data] + ); + const selectedBreakpoint = Object.entries(Grid.useBreakpoint()) .filter((screen) => !!screen[1]) .slice(-1)[0]; @@ -353,7 +346,7 @@ export default connect(mapStateToProps, mapDispatchToProps)(ProductionBoardKanba const Container = styled.div` .react-trello-card-skeleton, - .react-trel lo-card, + .react-trello-card, .react-trello-card-adder-form { box-sizing: border-box; max-width: ${(props) => props.width}px; diff --git a/client/src/components/trello-board/controllers/BoardContainer.jsx b/client/src/components/trello-board/controllers/BoardContainer.jsx index b91336d32..a6a0c7806 100644 --- a/client/src/components/trello-board/controllers/BoardContainer.jsx +++ b/client/src/components/trello-board/controllers/BoardContainer.jsx @@ -8,6 +8,7 @@ import isEqual from "lodash/isEqual"; import Lane from "./Lane"; import { PopoverWrapper } from "react-popopo"; import * as actions from "../../../redux/trello/trello.actions.js"; +import { isFunction } from "lodash"; /** * BoardContainer is a React component that represents a Trello-like board. @@ -234,11 +235,34 @@ const BoardContainer = ({ ] ); + const onLaneDrag = ({ draggableId, type, source, reason, mode, destination, combine }) => { + if (!type || type !== "lane" || !source || !destination) return; + + dispatch( + actions.moveCardAcrossLanes({ + fromLaneId: Number.parseInt(source.droppableId), + toLaneId: Number.parseInt(destination.droppableId), + cardId: draggableId, + index: destination.index + }) + ); + // onCardMoveAcrossLanes(payload.laneId, laneId, payload.id, addedIndex); + }; + + const combinedDragEnd = (...params) => { + // Early Gate + if (!params || !params[0]) return; + onLaneDrag(params[0]); + if (isFunction(onDragEnd)) { + onDragEnd(params[0]); + } + }; + return ( {}, onCardClick = () => {}, onBeforeCardDelete = () => {}, onCardDelete = () => {}, @@ -206,29 +204,6 @@ function Lane({ return droppable && sourceContainerOptions.groupName === groupName; }; - const onDragEnd = (laneId, result) => { - const { addedIndex, payload } = result; - - if (isDraggingOver) { - setIsDraggingOver(false); - } - - if (addedIndex != null) { - const newCard = { ...cloneDeep(payload), laneId }; - const response = handleDragEnd ? handleDragEnd(payload.id, payload.laneId, laneId, addedIndex, newCard) : true; - if (response === undefined || !!response) { - actions.moveCardAcrossLanes({ - fromLaneId: payload.laneId, - toLaneId: laneId, - cardId: payload.id, - index: addedIndex - }); - onCardMoveAcrossLanes(payload.laneId, laneId, payload.id, addedIndex); - } - return response; - } - }; - const updateCard = (updatedCard) => { actions.updateCard({ laneId: id, card: updatedCard }); onCardUpdate(id, updatedCard); @@ -396,7 +371,6 @@ Lane.propTypes = { draggable: PropTypes.bool, collapsibleLanes: PropTypes.bool, droppable: PropTypes.bool, - onCardMoveAcrossLanes: PropTypes.func, onCardClick: PropTypes.func, onBeforeCardDelete: PropTypes.func, onCardDelete: PropTypes.func, diff --git a/client/src/components/trello-board/helpers/LaneHelper.js b/client/src/components/trello-board/helpers/LaneHelper.js index bb1eaefa9..071ba2ad3 100644 --- a/client/src/components/trello-board/helpers/LaneHelper.js +++ b/client/src/components/trello-board/helpers/LaneHelper.js @@ -1,7 +1,7 @@ import update from "immutability-helper"; +import cloneDeep from "lodash/cloneDeep"; const updateLanes = (state, lanes) => update(state, { lanes: { $set: lanes } }); - const updateLaneCards = (lane, cards) => update(lane, { cards: { $set: cards } }); const LaneHelper = { @@ -53,42 +53,78 @@ const LaneHelper = { return updateLanes(state, newLanes); }, + // TODO: Unverified, needs to be hoisted. removeCardFromLane: (state, { laneId, cardId }) => { - const lanes = state.lanes.map((lane) => { - if (lane.id === laneId) { - const newCards = lane.cards.filter((card) => card.id !== cardId); - return updateLaneCards(lane, newCards); - } else { - return lane; - } + // Clone the state to avoid mutation + const newLanes = cloneDeep(state.lanes); + + // Find the lane from which the card will be removed + const lane = newLanes.find((lane) => lane.id === laneId); + + // Find the card in the lane + const cardIndex = lane.cards.findIndex((card) => card.id === cardId); + if (cardIndex === -1) { + throw new Error("Card not found in the lane"); + } + + // Remove the card from the lane + lane.cards.splice(cardIndex, 1); + + 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 } }); - return updateLanes(state, lanes); }, - moveCardAcrossLanes: (state, ...params) => { - console.log("state"); - console.dir(state); - console.log("params"); - console.dir(params); - // let cardToMove = null; - // const interimLanes = state.lanes.map((lane) => { - // if (lane.id === fromLaneId) { - // cardToMove = lane.cards.find((card) => card.id === cardId); - // const newCards = lane.cards.filter((card) => card.id !== cardId); - // return updateLaneCards(lane, newCards); - // } else { - // return lane; - // } - // }); - // return LaneHelper.appendCardToLane( - // { ...state, lanes: interimLanes }, - // { - // laneId: toLaneId, - // card: cardToMove, - // index: index - // } - // ); - return state; + // 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 } + }); }, 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 e79e3c391..bee24c08c 100644 --- a/client/src/components/trello-board/styles/Base.js +++ b/client/src/components/trello-board/styles/Base.js @@ -7,6 +7,7 @@ const getBoardWrapperStyles = (props) => { } if (props.orientation === "horizontal") { // 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;