- Checkpoint, this thing is fast has heck.

Signed-off-by: Dave Richer <dave@imexsystems.ca>
This commit is contained in:
Dave Richer
2024-06-30 21:19:04 -04:00
parent 2f493c63f8
commit 4db2b73397
5 changed files with 167 additions and 116 deletions

View File

@@ -1,13 +1,12 @@
import { SyncOutlined, UnorderedListOutlined } from "@ant-design/icons"; import { SyncOutlined, UnorderedListOutlined } from "@ant-design/icons";
import { useApolloClient } from "@apollo/client"; import { useApolloClient } from "@apollo/client";
import Board from "../../components/trello-board/index"; 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 { PageHeader } from "@ant-design/pro-layout";
import React, { useEffect, useMemo, useState } from "react"; import React, { useEffect, useMemo, useState } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { connect } from "react-redux"; import { connect } from "react-redux";
import { createStructuredSelector } from "reselect"; import { createStructuredSelector } from "reselect";
import styled from "styled-components";
import { logImEXEvent } from "../../firebase/firebase.utils"; import { logImEXEvent } from "../../firebase/firebase.utils";
import { generate_UPDATE_JOB_KANBAN } from "../../graphql/jobs.queries"; import { generate_UPDATE_JOB_KANBAN } from "../../graphql/jobs.queries";
import { insertAuditTrail } from "../../redux/application/application.actions"; 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 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/production-board-kanban-card-color-legend.component";
import "./production-board-kanban.styles.scss"; 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"; import ProductionBoardKanbanSettings from "./production-board-kanban.settings.component.jsx";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
@@ -54,6 +53,7 @@ export function ProductionBoardKanbanComponent({
const [isMoving, setIsMoving] = useState(false); const [isMoving, setIsMoving] = useState(false);
const orientation = associationSettings?.kanban_settings?.orientation ? "vertical" : "horizontal"; const orientation = associationSettings?.kanban_settings?.orientation ? "vertical" : "horizontal";
const { t } = useTranslation(); const { t } = useTranslation();
useEffect(() => { useEffect(() => {
@@ -63,7 +63,7 @@ export function ProductionBoardKanbanComponent({
}, [associationSettings]); }, [associationSettings]);
useEffect(() => { useEffect(() => {
const boardData = createFakeBoardData( const boardData = createBoardData(
[...bodyshop.md_ro_statuses.production_statuses, ...(bodyshop.md_ro_statuses.additional_board_statuses || [])], [...bodyshop.md_ro_statuses.production_statuses, ...(bodyshop.md_ro_statuses.additional_board_statuses || [])],
data, data,
filter filter
@@ -113,89 +113,88 @@ export function ProductionBoardKanbanComponent({
}; };
// TODO, refine and use action // TODO, refine and use action
const onDragEnd = async (...args) => { const onDragEnd = async ({ type, source, destination, draggableId, ...args }) => {
console.dir(args);
// //cardId, sourceLaneId, targetLaneId, position, cardDetails // //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 const newChildCard = movedCardWillBeLast
// if (!type || type !== "lane" || !source || !destination) return; ? 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); let movedCardNewKanbanParent;
// if (movedCardWillBeFirst) {
// const sameColumnTransfer = source.droppableId === destination.droppableId; movedCardNewKanbanParent = "-1";
// const targetLane = boardLanes.lanes[Number.parseInt(destination.droppableId)]; } else if (movedCardWillBeLast) {
// const sourceLane = boardLanes.lanes[Number.parseInt(source.droppableId)]; movedCardNewKanbanParent = lastCardInTargetLane.id;
// const sourceCard = getCardByID(boardLanes, draggableId); } else if (!!newChildCard) {
// movedCardNewKanbanParent = newChildCard.metadata.kanbanparent;
// const movedCardWillBeFirst = destination.index === 0; } else {
// const movedCardWillBeLast = destination.index > targetLane.cards.length - 1; console.log("==> !!!!!!Couldn't find a parent.!!!! <==");
// const movedCardIsFirstNewCard = movedCardWillBeFirst && movedCardWillBeLast; }
// const newChildCardNewParent = newChildCard ? draggableId : null;
// const lastCardInTargetLane = targetLane.cards[targetLane.cards.length - 1]; try {
// const update = await client.mutate({
// const oldChildCard = sourceLane.cards[destination.index + 1]; mutation: generate_UPDATE_JOB_KANBAN(
// oldChildCard ? oldChildCard.id : null,
// // oldChildCardNewParent,
// const newChildCard = movedCardWillBeLast draggableId,
// ? null movedCardNewKanbanParent,
// : targetLane.cards[ targetLane.id,
// sameColumnTransfer newChildCard ? newChildCard.id : null,
// ? destination.index - destination.index > 0 newChildCardNewParent
// ? destination.index )
// : destination.index + 1 });
// : destination.index
// ]; insertAuditTrail({
// jobid: draggableId,
// const oldChildCardNewParent = oldChildCard ? sourceCard.metadata.kanbanparent : null; operation: AuditTrailMapping.jobstatuschange(targetLane.id),
// type: "jobstatuschange"
// let movedCardNewKanbanParent; });
// if (movedCardWillBeFirst) {
// movedCardNewKanbanParent = "-1"; if (update.errors) {
// } else if (movedCardWillBeLast) { notification["error"]({
// movedCardNewKanbanParent = lastCardInTargetLane.id; message: t("production.errors.boardupdate", {
// } else if (!!newChildCard) { message: JSON.stringify(update.errors)
// movedCardNewKanbanParent = newChildCard.metadata.kanbanparent; })
// } else { });
// console.log("==> !!!!!!Couldn't find a parent.!!!! <=="); }
// } } catch (error) {
// const newChildCardNewParent = newChildCard ? draggableId : null; notification["error"]({
// try { message: t("production.errors.boardupdate", {
// const update = await client.mutate({ message: error.message
// mutation: generate_UPDATE_JOB_KANBAN( })
// oldChildCard ? oldChildCard.id : null, });
// oldChildCardNewParent, } finally {
// draggableId, setIsMoving(false);
// 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( const totalHrs = useMemo(
() => () =>

View File

@@ -118,7 +118,6 @@
} }
.react-trello-column-header { .react-trello-column-header {
padding-bottom: 10px;
font-weight: bold; font-weight: bold;
} }

View File

@@ -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 (
<div
{...props}
className="height-preserving-container"
style={{
"--child-height": `${size}px`
}}
>
{children}
</div>
);
};
export default HeightPreservingItem;

View File

@@ -9,29 +9,7 @@ import { v1 } from "uuid";
import * as actions from "../../../redux/trello/trello.actions.js"; import * as actions from "../../../redux/trello/trello.actions.js";
import { Draggable, Droppable } from "../dnd/lib"; import { Draggable, Droppable } from "../dnd/lib";
import { Virtuoso, VirtuosoGrid } from "react-virtuoso"; import { Virtuoso, VirtuosoGrid } from "react-virtuoso";
import HeightPreservingItem from "../components/Lane/HeightPreservingItem.jsx";
function HeightPreservingItem({ children, ...props }) {
const [size, setSize] = useState(0);
const { "data-known-size": knownSize = 0 } = props;
useEffect(() => {
if (knownSize !== 0) {
setSize(knownSize);
}
}, [knownSize]);
return (
<div
{...props}
className="height-preserving-container"
style={{
"--child-height": `${size}px`
}}
>
{children}
</div>
);
}
function Lane({ function Lane({
actions, actions,
@@ -164,7 +142,7 @@ function Lane({
// const onDragStart = ({ payload }) => { // const onDragStart = ({ payload }) => {
// handleDragStart && handleDragStart(payload.id, payload.laneId); // handleDragStart && handleDragStart(payload.id, payload.laneId);
// }; // };
//
// const shouldAcceptDrop = (sourceContainerOptions) => { // const shouldAcceptDrop = (sourceContainerOptions) => {
// return droppable && sourceContainerOptions.groupName === groupName; // return droppable && sourceContainerOptions.groupName === groupName;
// }; // };
@@ -188,6 +166,10 @@ function Lane({
collapsibleLanes && setCollapsed(!collapsed); collapsibleLanes && setCollapsed(!collapsed);
}; };
/**
* Card component
* @type {React.NamedExoticComponent<{readonly item?: *, readonly provided?: *, readonly isDragging?: *}>}
*/
const Card = React.memo(({ provided, item: card, isDragging }) => { const Card = React.memo(({ provided, item: card, isDragging }) => {
const onDeleteCard = () => removeCard(card.id); const onDeleteCard = () => removeCard(card.id);
return ( return (
@@ -216,31 +198,56 @@ function Lane({
); );
}); });
/**
* Render the draggable component
* @param index
* @param item
* @returns {React.JSX.Element}
*/
const renderDraggable = (index, item) => ( const renderDraggable = (index, item) => (
<Draggable draggableId={item.id} index={index} key={item.id}> <Draggable draggableId={item.id} index={index} key={item.id}>
{(provided, snapshot) => <Card provided={provided} item={item} isDragging={snapshot.isDragging} />} {(provided, snapshot) => <Card provided={provided} item={item} isDragging={snapshot.isDragging} />}
</Draggable> </Draggable>
); );
/**
* Render the add card link
* @returns {false|React.JSX.Element}
*/
const renderAddCardLink = () => const renderAddCardLink = () =>
editable && !addCardMode && <components.AddCardLink onClick={showEditableCard} laneId={id} />; editable && !addCardMode && <components.AddCardLink onClick={showEditableCard} laneId={id} />;
/**
* Render the new card form
* @returns {false|React.JSX.Element}
*/
const renderNewCardForm = () => const renderNewCardForm = () =>
addCardMode && <components.NewCardForm onCancel={hideEditableCard} laneId={id} onAdd={addNewCard} />; addCardMode && <components.NewCardForm onCancel={hideEditableCard} laneId={id} onAdd={addNewCard} />;
/**
* Wrapper for the item in the grid layout
* @param children
* @param props
* @returns {React.JSX.Element}
* @constructor
*/
const ItemWrapper = ({ children, ...props }) => ( const ItemWrapper = ({ children, ...props }) => (
<div <div
{...props} {...props}
style={{ style={{
display: "flex", display: "flex",
flex: 1, flex: 1,
whiteSpace: "nowrap" whiteSpace: "wrap"
}} }}
> >
{children} {children}
</div> </div>
); );
/**
* Components for the grid layout
* @type {{Item: (function({children: *, [p: string]: *}): *), List: React.ForwardRefExoticComponent<React.PropsWithoutRef<{readonly children?: *, readonly style?: *}> & React.RefAttributes<unknown>>}}
*/
const gridComponents = { const gridComponents = {
List: forwardRef(({ style, children, ...props }, ref) => ( List: forwardRef(({ style, children, ...props }, ref) => (
<div <div
@@ -259,8 +266,6 @@ function Lane({
<div <div
{...props} {...props}
style={{ style={{
width: "10%",
display: "flex",
alignContent: "stretch", alignContent: "stretch",
boxSizing: "border-box" boxSizing: "border-box"
}} }}
@@ -270,8 +275,15 @@ function Lane({
) )
}; };
/**
* Render the droppable component with the provided cards and the provided props from react-beautiful-dnd
* @param provided
* @param renderedCards
* @returns {Element}
*/
const renderDroppable = (provided, renderedCards) => { const renderDroppable = (provided, renderedCards) => {
const Component = orientation === "vertical" ? VirtuosoGrid : Virtuoso; const Component = orientation === "vertical" ? VirtuosoGrid : Virtuoso;
const FinalComponent = collapsed ? "div" : Component;
const commonProps = { const commonProps = {
useWindowScroll: true, useWindowScroll: true,
@@ -298,7 +310,7 @@ function Lane({
itemContent: (index, item) => renderDraggable(index, item), itemContent: (index, item) => renderDraggable(index, item),
scrollerRef: provided.innerRef scrollerRef: provided.innerRef
}; };
const finalComponentProps = collapsed ? {} : componentProps;
return ( return (
<div <div
{...provided.droppableProps} {...provided.droppableProps}
@@ -306,7 +318,7 @@ function Lane({
className={allClassNames} className={allClassNames}
style={{ ...provided.droppableProps.style }} style={{ ...provided.droppableProps.style }}
> >
<Component {...componentProps} /> <FinalComponent {...finalComponentProps} />
{renderAddCardLink()} {renderAddCardLink()}
{renderNewCardForm()} {renderNewCardForm()}
{provided.placeholder} {provided.placeholder}
@@ -315,11 +327,12 @@ function Lane({
}; };
const renderDragContainer = () => { const renderDragContainer = () => {
if (collapsed) return <></>;
const renderedCards = sortCards(cards, laneSortFunction); const renderedCards = sortCards(cards, laneSortFunction);
return ( return (
<Droppable <Droppable
droppableId={id} droppableId={id}
index={index}
type="lane" type="lane"
direction={orientation === "horizontal" ? "vertical" : "grid"} direction={orientation === "horizontal" ? "vertical" : "grid"}
mode="virtual" mode="virtual"

View File

@@ -76,7 +76,7 @@ export const StyleHorizontal = styled.div`
// TODO: This will need to be changed // TODO: This will need to be changed
min-width: 250px; min-width: 250px;
min-height: 25px; min-height: 25px;
margin-bottom: 42px; margin-bottom: 10px;
} }
.react-trello-lane.lane-collapsed { .react-trello-lane.lane-collapsed {
@@ -89,6 +89,14 @@ export const StyleVertical = styled.div`
text-align: left; text-align: left;
} }
.react-trello-lane {
min-height: 5px;
}
.react-trello-lane.lane-collapsed {
min-height: 5px;
}
.react-trello-card { .react-trello-card {
flex: 0 1 auto; flex: 0 1 auto;
} }