From 4db2b73397c67160969c6f015d41bf3884ef5601 Mon Sep 17 00:00:00 2001 From: Dave Richer Date: Sun, 30 Jun 2024 21:19:04 -0400 Subject: [PATCH] - Checkpoint, this thing is fast has heck. Signed-off-by: Dave Richer --- .../production-board-kanban.component.jsx | 167 +++++++++--------- .../production-board-kanban.styles.scss | 1 - .../components/Lane/HeightPreservingItem.jsx | 32 ++++ .../trello-board/controllers/Lane.jsx | 73 ++++---- .../components/trello-board/styles/Base.js | 10 +- 5 files changed, 167 insertions(+), 116 deletions(-) create mode 100644 client/src/components/trello-board/components/Lane/HeightPreservingItem.jsx 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 4f5b3910c..871fe7c8d 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 @@ -1,13 +1,12 @@ import { SyncOutlined, UnorderedListOutlined } from "@ant-design/icons"; import { useApolloClient } from "@apollo/client"; import Board from "../../components/trello-board/index"; -import { Button, Grid, notification, Skeleton, Space, Statistic } from "antd"; +import { Button, notification, Skeleton, Space, Statistic } from "antd"; 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 { createStructuredSelector } from "reselect"; -import styled from "styled-components"; import { logImEXEvent } from "../../firebase/firebase.utils"; import { generate_UPDATE_JOB_KANBAN } from "../../graphql/jobs.queries"; import { insertAuditTrail } from "../../redux/application/application.actions"; @@ -20,7 +19,7 @@ import ProductionBoardCard from "../production-board-kanban-card/production-boar 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, createFakeBoardData } from "./production-board-kanban.utils.js"; +import { createFakeBoardData, createBoardData } from "./production-board-kanban.utils.js"; import ProductionBoardKanbanSettings from "./production-board-kanban.settings.component.jsx"; const mapStateToProps = createStructuredSelector({ @@ -54,6 +53,7 @@ export function ProductionBoardKanbanComponent({ const [isMoving, setIsMoving] = useState(false); const orientation = associationSettings?.kanban_settings?.orientation ? "vertical" : "horizontal"; + const { t } = useTranslation(); useEffect(() => { @@ -63,7 +63,7 @@ export function ProductionBoardKanbanComponent({ }, [associationSettings]); useEffect(() => { - const boardData = createFakeBoardData( + const boardData = createBoardData( [...bodyshop.md_ro_statuses.production_statuses, ...(bodyshop.md_ro_statuses.additional_board_statuses || [])], data, filter @@ -113,89 +113,88 @@ export function ProductionBoardKanbanComponent({ }; // TODO, refine and use action - const onDragEnd = async (...args) => { - console.dir(args); + const onDragEnd = async ({ type, source, destination, draggableId, ...args }) => { // //cardId, sourceLaneId, targetLaneId, position, cardDetails - // logImEXEvent("kanban_drag_end"); + logImEXEvent("kanban_drag_end"); + + // Early Gate + if (!type || type !== "lane" || !source || !destination) return; + + setIsMoving(true); + + const targetLane = boardLanes.lanes.find((lane) => lane.id === destination.droppableId); + const sourceLane = boardLanes.lanes.find((lane) => lane.id === source.droppableId); + + const sameColumnTransfer = source.droppableId === destination.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]; + // - // // Early Gate - // if (!type || type !== "lane" || !source || !destination) return; + 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; // - // 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); - // } - // }; + 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( () => 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 1657d398e..683cb2920 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 @@ -118,7 +118,6 @@ } .react-trello-column-header { - padding-bottom: 10px; font-weight: bold; } diff --git a/client/src/components/trello-board/components/Lane/HeightPreservingItem.jsx b/client/src/components/trello-board/components/Lane/HeightPreservingItem.jsx new file mode 100644 index 000000000..503f1e4ac --- /dev/null +++ b/client/src/components/trello-board/components/Lane/HeightPreservingItem.jsx @@ -0,0 +1,32 @@ +import React, { useEffect, useState } from "react"; + +const HeightPreservingItem = ({ children, ...props }) => { + const [size, setSize] = useState(0); + const { "data-known-size": knownSize = 0 } = props; + + useEffect(() => { + if (knownSize !== 0) { + setSize(knownSize); + } + }, [knownSize]); + // + // useEffect(() => { + // if (knownSize !== 0 && knownSize !== size) { + // setSize(knownSize); + // } + // }, [knownSize, size]); + + return ( +
+ {children} +
+ ); +}; + +export default HeightPreservingItem; diff --git a/client/src/components/trello-board/controllers/Lane.jsx b/client/src/components/trello-board/controllers/Lane.jsx index f87ebfb50..4c637378d 100644 --- a/client/src/components/trello-board/controllers/Lane.jsx +++ b/client/src/components/trello-board/controllers/Lane.jsx @@ -9,29 +9,7 @@ import { v1 } from "uuid"; import * as actions from "../../../redux/trello/trello.actions.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} -
- ); -} +import HeightPreservingItem from "../components/Lane/HeightPreservingItem.jsx"; function Lane({ actions, @@ -164,7 +142,7 @@ function Lane({ // const onDragStart = ({ payload }) => { // handleDragStart && handleDragStart(payload.id, payload.laneId); // }; - // + // const shouldAcceptDrop = (sourceContainerOptions) => { // return droppable && sourceContainerOptions.groupName === groupName; // }; @@ -188,6 +166,10 @@ function Lane({ collapsibleLanes && setCollapsed(!collapsed); }; + /** + * Card component + * @type {React.NamedExoticComponent<{readonly item?: *, readonly provided?: *, readonly isDragging?: *}>} + */ const Card = React.memo(({ provided, item: card, isDragging }) => { const onDeleteCard = () => removeCard(card.id); return ( @@ -216,31 +198,56 @@ function Lane({ ); }); + /** + * Render the draggable component + * @param index + * @param item + * @returns {React.JSX.Element} + */ const renderDraggable = (index, item) => ( {(provided, snapshot) => } ); + /** + * Render the add card link + * @returns {false|React.JSX.Element} + */ const renderAddCardLink = () => editable && !addCardMode && ; + /** + * Render the new card form + * @returns {false|React.JSX.Element} + */ const renderNewCardForm = () => addCardMode && ; + /** + * Wrapper for the item in the grid layout + * @param children + * @param props + * @returns {React.JSX.Element} + * @constructor + */ const ItemWrapper = ({ children, ...props }) => (
{children}
); + /** + * Components for the grid layout + * @type {{Item: (function({children: *, [p: string]: *}): *), List: React.ForwardRefExoticComponent & React.RefAttributes>}} + */ const gridComponents = { List: forwardRef(({ style, children, ...props }, ref) => (
{ const Component = orientation === "vertical" ? VirtuosoGrid : Virtuoso; + const FinalComponent = collapsed ? "div" : Component; const commonProps = { useWindowScroll: true, @@ -298,7 +310,7 @@ function Lane({ itemContent: (index, item) => renderDraggable(index, item), scrollerRef: provided.innerRef }; - + const finalComponentProps = collapsed ? {} : componentProps; return (
- + {renderAddCardLink()} {renderNewCardForm()} {provided.placeholder} @@ -315,11 +327,12 @@ function Lane({ }; const renderDragContainer = () => { - if (collapsed) return <>; const renderedCards = sortCards(cards, laneSortFunction); + return (