refactoring

This commit is contained in:
2025-03-10 23:35:05 +04:00
parent ea80e92c18
commit f8ae7c62a2
17 changed files with 280 additions and 266 deletions

View File

@@ -18,8 +18,8 @@
border-bottom: none; border-bottom: none;
} }
.board-during-dnd { .no-transition {
background-color: var(--color-gray-10); transition: none !important;
} }
.board::after { .board::after {

View File

@@ -5,31 +5,30 @@ import { IconEdit, IconPlus, IconTrash } from "@tabler/icons-react";
import classNames from "classnames"; import classNames from "classnames";
import styles from "./Board.module.scss"; import styles from "./Board.module.scss";
import useStatus from "../../Statuses/Status/hooks/useStatus.tsx"; import useStatus from "../../Statuses/Status/hooks/useStatus.tsx";
import { useDndContext } from "../../../../pages/CardsPage/contexts/DndContext.tsx";
import DragState from "../../../../pages/CardsPage/enums/DragState.ts";
import { useBoardsContext } from "../../../../contexts/BoardsContext.tsx";
type Props = { type Props = {
board: BoardSchema; board: BoardSchema;
selectedBoard: BoardSchema | null;
setSelectedBoard: (board: BoardSchema) => void;
onEditBoardClick: (board: BoardSchema) => void;
onDeleteBoardClick: (board: BoardSchema) => void;
isBoardDragEnded: boolean;
refetch: () => void;
} }
const Board = ({ const Board = ({ board }: Props) => {
board, const {
selectedBoard, selectedBoard,
setSelectedBoard, setSelectedBoard,
onEditBoardClick, onEditBoardClick,
onDeleteBoardClick, onDeleteBoardClick,
isBoardDragEnded, } = useBoardsContext();
refetch,
}: Props) => { const { dragState } = useDndContext();
const { showContextMenu } = useContextMenu(); const { showContextMenu } = useContextMenu();
const { onCreateStatusClick } = useStatus({ refetch }); const { onCreateStatusClick } = useStatus();
const isDropDisabled = dragState !== DragState.DRAG_BOARD;
const contextMenu = (board: BoardSchema) => showContextMenu([ const contextMenu = (board: BoardSchema) => showContextMenu([
{ {
@@ -54,16 +53,22 @@ const Board = ({
return ( return (
<Droppable <Droppable
droppableId={board.id.toString()} droppableId={"board-" + board.id.toString()}
direction={"horizontal"} direction={"horizontal"}
isDropDisabled={isDropDisabled}
> >
{provided => ( {provided => (
<div <div
{...provided.droppableProps} {...provided.droppableProps}
ref={provided.innerRef} ref={provided.innerRef}
onMouseEnter={() => {
if (dragState === DragState.DRAG_CARD) {
setSelectedBoard(board);
}
}}
> >
<Draggable <Draggable
draggableId={board.id.toString()} draggableId={"board-" + board.id.toString()}
index={board.ordinalNumber} index={board.ordinalNumber}
> >
{(provided) => ( {(provided) => (
@@ -77,7 +82,6 @@ const Board = ({
board.id === selectedBoard?.id ? board.id === selectedBoard?.id ?
styles["selected-board"] : styles["selected-board"] :
styles["board"], styles["board"],
!isBoardDragEnded && styles["board-during-dnd"],
)} )}
onClick={() => setSelectedBoard(board)} onClick={() => setSelectedBoard(board)}

View File

@@ -1,88 +1,66 @@
import { Box, Center, Group, Stack } from "@mantine/core"; import { Box, Center, Group, Stack } from "@mantine/core";
import { DragDropContext } from "@hello-pangea/dnd"; import { DragDropContext } from "@hello-pangea/dnd";
import { BoardSchema, CardSummary } from "../../../../client"; import { BoardSchema } from "../../../../client";
import { IconPlus } from "@tabler/icons-react"; import { IconPlus } from "@tabler/icons-react";
import useBoards from "./hooks/useBoards.tsx";
import Statuses from "../../Statuses/Statuses/Statuses.tsx"; import Statuses from "../../Statuses/Statuses/Statuses.tsx";
import Board from "../Board/Board.tsx"; import Board from "../Board/Board.tsx";
import useBoardsDnd from "./hooks/useBoardsDnd.tsx";
import PrefillCardsWithExcelDrawer import PrefillCardsWithExcelDrawer
from "../../../../pages/CardsPage/drawers/PrefillCardWithExcelDrawer/PrefillCardsWithExcelDrawer.tsx"; from "../../../../pages/CardsPage/drawers/PrefillCardWithExcelDrawer/PrefillCardsWithExcelDrawer.tsx";
import { useDndContext } from "../../../../pages/CardsPage/contexts/DndContext.tsx";
import { useBoardsContext } from "../../../../contexts/BoardsContext.tsx";
type Props = { const Boards = () => {
summariesRaw: CardSummary[];
refetchSummaries: () => void;
boards: BoardSchema[];
refetchBoards: () => void;
}
const Boards = (props: Props) => {
const { const {
boards,
selectedBoard, selectedBoard,
setSelectedBoard,
onCreateBoardClick, onCreateBoardClick,
onEditBoardClick, } = useBoardsContext();
onDeleteBoardClick,
} = useBoards(props);
const { const {
onBoardDragEnd, onDragEnd,
isBoardDragEnded, onDragStart,
setIsBoardDragEnded, } = useDndContext();
} = useBoardsDnd(props);
const getBoardsTabs = () => { const getBoardsTabs = () => {
return ( return (
<DragDropContext <Group
onDragStart={() => { mx={"3%"}
setIsBoardDragEnded(false); mb={"md"}
}} gap={0}
onDragEnd={onBoardDragEnd} align={"end"}
wrap={"nowrap"}
> >
<Group {boards.map((board: BoardSchema) => (
mx={"3%"} <Board
mb={"md"} key={board.id}
gap={0} board={board}
align={"end"} />
wrap={"nowrap"} ))}
<Center
px={"md"}
py={"xs"}
style={{ cursor: "pointer", borderBottom: "solid gray 1px" }}
onClick={onCreateBoardClick}
> >
{props.boards.map((board: BoardSchema) => ( <IconPlus />
<Board </Center>
key={board.id} <Box w={"100%"} style={{ borderBottom: "solid gray 1px" }}></Box>
</Group>
board={board}
selectedBoard={selectedBoard}
setSelectedBoard={setSelectedBoard}
onEditBoardClick={onEditBoardClick}
onDeleteBoardClick={onDeleteBoardClick}
isBoardDragEnded={isBoardDragEnded}
refetch={props.refetchBoards}
/>
))}
<Center
px={"md"}
py={"xs"}
style={{ cursor: "pointer", borderBottom: "solid gray 1px" }}
onClick={onCreateBoardClick}
>
<IconPlus />
</Center>
<Box w={"100%"} style={{ borderBottom: "solid gray 1px" }}></Box>
</Group>
</DragDropContext>
); );
}; };
return ( return (
<Stack> <DragDropContext
{getBoardsTabs()} onDragStart={onDragStart}
<Statuses onDragEnd={onDragEnd}
selectedBoard={selectedBoard} >
{...props} <Stack>
/> {getBoardsTabs()}
<PrefillCardsWithExcelDrawer board={selectedBoard} /> <Statuses />
</Stack> <PrefillCardsWithExcelDrawer board={selectedBoard} />
</Stack>
</DragDropContext>
); );
}; };

View File

@@ -1,16 +1,14 @@
import { DropResult } from "@hello-pangea/dnd"; import { DropResult } from "@hello-pangea/dnd";
import { BoardSchema, BoardService } from "../../../../../client"; import { BoardSchema, BoardService } from "../../../../../client";
import { notifications } from "../../../../../shared/lib/notifications.ts"; import { notifications } from "../../../../../shared/lib/notifications.ts";
import { useState } from "react"; import { useBoardsContext } from "../../../../../contexts/BoardsContext.tsx";
type Props = { const useBoardsDnd = () => {
boards: BoardSchema[]; const {
refetchBoards: () => void; boards,
} refetchBoards,
} = useBoardsContext();
const useBoardsDnd = ({ boards, refetchBoards }: Props) => {
const [isBoardDragEnded, setIsBoardDragEnded] = useState(true);
const updateBoardOrder = (board: BoardSchema, newOrdinalNumber: number) => { const updateBoardOrder = (board: BoardSchema, newOrdinalNumber: number) => {
BoardService.updateBoardOrder({ BoardService.updateBoardOrder({
@@ -18,22 +16,21 @@ const useBoardsDnd = ({ boards, refetchBoards }: Props) => {
projectId: board.projectId, projectId: board.projectId,
boardId: board.id, boardId: board.id,
newOrdinalNumber, newOrdinalNumber,
} },
}) })
.then(({ ok, message }) => { .then(({ ok, message }) => {
if (!ok) notifications.error({ message }); if (!ok) notifications.error({ message });
refetchBoards(); refetchBoards();
}) });
}; };
const onBoardDragEnd = async (result: DropResult) => { const onBoardDragEnd = async (result: DropResult) => {
setIsBoardDragEnded(true);
// If there is no changes // If there is no changes
if (!result.destination || result.destination == result.source) return; if (!result.destination || result.destination == result.source) return;
// Checking for valid cardId // Checking for valid cardId
const boardId = parseInt(result.draggableId); const boardIdStr = result.draggableId.replace("board-", "");
const boardId = parseInt(boardIdStr);
if (isNaN(boardId)) return; if (isNaN(boardId)) return;
// Checking for valid card // Checking for valid card
@@ -45,8 +42,6 @@ const useBoardsDnd = ({ boards, refetchBoards }: Props) => {
return { return {
onBoardDragEnd, onBoardDragEnd,
isBoardDragEnded,
setIsBoardDragEnded,
}; };
}; };

View File

@@ -29,7 +29,7 @@ export const CardsDndColumn: FC<Props> = ({
}) => { }) => {
const { selectedProject } = useProjectsContext(); const { selectedProject } = useProjectsContext();
const isCreatingDealFromFileEnabled = isModuleInProject(Modules.SERVICES_AND_PRODUCTS, selectedProject); const isCreatingDealFromFileEnabled = isModuleInProject(Modules.SERVICES_AND_PRODUCTS, selectedProject);
const isDropDisabled = dragState === DragState.DRAG_STATUS; const isDropDisabled = dragState !== DragState.DRAG_CARD;
const droppableId = status.id.toString(); const droppableId = status.id.toString();
const isGroup = (obj: GroupWithCards | CardSummary): obj is GroupWithCards => { const isGroup = (obj: GroupWithCards | CardSummary): obj is GroupWithCards => {

View File

@@ -9,7 +9,7 @@ type Props = {
} }
const CardsDndFooter = ({ dragState }: Props) => { const CardsDndFooter = ({ dragState }: Props) => {
const isDealDragEnded = dragState === DragState.DRAG_ENDED; const isDealDragEnded = dragState !== DragState.DRAG_CARD;
return ( return (
<Flex <Flex

View File

@@ -1,7 +1,7 @@
import styles from "../../../../pages/CardsPage/ui/CardsPage.module.css"; import styles from "../../../../pages/CardsPage/ui/CardsPage.module.css";
import { Divider, Stack, Text, Title } from "@mantine/core"; import { Divider, Stack, Text, Title } from "@mantine/core";
import getColumnColor from "../../Cards/CardsDndColumn/utils/getColumnColor.ts"; import getColumnColor from "../../Cards/CardsDndColumn/utils/getColumnColor.ts";
import { BoardSchema, CardSummary, StatusSchema } from "../../../../client"; import { CardSummary, StatusSchema } from "../../../../client";
import { getPluralForm } from "../../../../shared/lib/utils.ts"; import { getPluralForm } from "../../../../shared/lib/utils.ts";
import { sum } from "lodash"; import { sum } from "lodash";
import { Draggable, Droppable } from "@hello-pangea/dnd"; import { Draggable, Droppable } from "@hello-pangea/dnd";
@@ -11,20 +11,23 @@ import { IconEdit, IconTrash } from "@tabler/icons-react";
import useStatus from "./hooks/useStatus.tsx"; import useStatus from "./hooks/useStatus.tsx";
import isModuleInProject, { Modules } from "../../../../modules/utils/isModuleInProject.ts"; import isModuleInProject, { Modules } from "../../../../modules/utils/isModuleInProject.ts";
import { useEqualHeightsContext } from "./contexts/EqualHeightContext.tsx"; import { useEqualHeightsContext } from "./contexts/EqualHeightContext.tsx";
import { useBoardsContext } from "../../../../contexts/BoardsContext.tsx";
type Props = { type Props = {
status: StatusSchema; status: StatusSchema;
board: BoardSchema | null;
index: number; index: number;
summaries: CardSummary[]; summaries: CardSummary[];
dragState: DragState; dragState: DragState;
refetch: () => void;
} }
const Status = ({ summaries, status, board, dragState, index, refetch }: Props) => { const Status = ({ summaries, status, dragState, index }: Props) => {
const isDropDisabled = dragState === DragState.DRAG_CARD; const {
const isServicesAndProductsIncluded = isModuleInProject(Modules.SERVICES_AND_PRODUCTS, board?.project); selectedBoard,
} = useBoardsContext();
const isDropDisabled = dragState !== DragState.DRAG_STATUS;
const isServicesAndProductsIncluded = isModuleInProject(Modules.SERVICES_AND_PRODUCTS, selectedBoard?.project);
const { const {
divRefs, divRefs,
@@ -33,7 +36,7 @@ const Status = ({ summaries, status, board, dragState, index, refetch }: Props)
const { const {
onEditStatusClick, onEditStatusClick,
onDeleteStatusClick, onDeleteStatusClick,
} = useStatus({ refetch }); } = useStatus();
const getDealsText = () => { const getDealsText = () => {
const pluralForm = getPluralForm( const pluralForm = getPluralForm(

View File

@@ -1,13 +1,14 @@
import { BoardSchema, StatusSchema, StatusService } from "../../../../../client"; import { BoardSchema, StatusSchema, StatusService } from "../../../../../client";
import { modals } from "@mantine/modals"; import { modals } from "@mantine/modals";
import { notifications } from "../../../../../shared/lib/notifications.ts"; import { notifications } from "../../../../../shared/lib/notifications.ts";
import { useBoardsContext } from "../../../../../contexts/BoardsContext.tsx";
type Props = { const useStatus = () => {
refetch: () => void; const {
} refetchBoards: refetch,
} = useBoardsContext();
const useStatus = ({ refetch }: Props) => {
const onEditStatusClick = (status: StatusSchema) => { const onEditStatusClick = (status: StatusSchema) => {
modals.openContextModal({ modals.openContextModal({
modal: "statusModal", modal: "statusModal",

View File

@@ -1,38 +1,21 @@
import { BoardSchema, CardSummary, StatusSchema } from "../../../../client"; import { StatusSchema } from "../../../../client";
import CardsDndColumn from "../../Cards/CardsDndColumn/CardsDndColumn.tsx"; import CardsDndColumn from "../../Cards/CardsDndColumn/CardsDndColumn.tsx";
import styles from "../../../../pages/CardsPage/ui/CardsPage.module.css"; import styles from "../../../../pages/CardsPage/ui/CardsPage.module.css";
import CardsDndFooter from "../../Cards/CardsDndFooter/CardsDndFooter.tsx"; import CardsDndFooter from "../../Cards/CardsDndFooter/CardsDndFooter.tsx";
import { Flex, rem, Stack } from "@mantine/core"; import { Flex, rem, Stack } from "@mantine/core";
import { DragDropContext } from "@hello-pangea/dnd";
import useDnd from "../../../../pages/CardsPage/hooks/useDnd.tsx";
import Status from "../Status/Status.tsx"; import Status from "../Status/Status.tsx";
import { EqualHeightsContextProvider } from "../Status/contexts/EqualHeightContext.tsx"; import { EqualHeightsContextProvider } from "../Status/contexts/EqualHeightContext.tsx";
import { useBoardsContext } from "../../../../contexts/BoardsContext.tsx";
import { useDndContext } from "../../../../pages/CardsPage/contexts/DndContext.tsx";
type Props = { const Statuses = () => {
selectedBoard: BoardSchema | null; const { selectedBoard } = useBoardsContext();
summariesRaw: CardSummary[];
refetchSummaries: () => void;
refetchBoards: () => void;
}
const Statuses = ({
selectedBoard,
summariesRaw,
refetchSummaries,
refetchBoards,
}: Props) => {
const { const {
summaries, summaries,
dragState, dragState,
onDragStart, } = useDndContext();
onDragEnd,
} = useDnd({
selectedBoard,
summariesRaw,
refetchSummaries,
refetchBoards,
});
const statusDndColumn = (status: StatusSchema, index: number) => { const statusDndColumn = (status: StatusSchema, index: number) => {
const filteredSummaries = summaries.filter( const filteredSummaries = summaries.filter(
@@ -45,9 +28,7 @@ const Statuses = ({
summaries={filteredSummaries} summaries={filteredSummaries}
index={index} index={index}
status={status} status={status}
board={selectedBoard}
dragState={dragState} dragState={dragState}
refetch={refetchBoards}
/> />
<CardsDndColumn <CardsDndColumn
withCreateButton={index === 0} withCreateButton={index === 0}
@@ -64,26 +45,21 @@ const Statuses = ({
.sort((a, b) => a.ordinalNumber - b.ordinalNumber) ?? []; .sort((a, b) => a.ordinalNumber - b.ordinalNumber) ?? [];
return ( return (
<DragDropContext <EqualHeightsContextProvider statuses={statuses}>
onDragStart={onDragStart} <Flex
onDragEnd={onDragEnd} justify={"space-between"}
> direction={"column"}
<EqualHeightsContextProvider statuses={statuses}> >
<Flex <Flex className={styles["statuses"]}>
justify={"space-between"} {selectedBoard &&
direction={"column"} statuses.map(((status: StatusSchema, index: number) => {
> return statusDndColumn(status, index);
<Flex className={styles["statuses"]}> }))
{selectedBoard && }
statuses.map(((status: StatusSchema, index: number) => {
return statusDndColumn(status, index);
}))
}
</Flex>
<CardsDndFooter dragState={dragState} />
</Flex> </Flex>
</EqualHeightsContextProvider> <CardsDndFooter dragState={dragState} />
</DragDropContext> </Flex>
</EqualHeightsContextProvider>
); );
}; };

View File

@@ -1,13 +1,15 @@
import { DropResult } from "@hello-pangea/dnd"; import { DropResult } from "@hello-pangea/dnd";
import { BoardSchema, StatusSchema, StatusService } from "../../../../../client"; import { StatusSchema, StatusService } from "../../../../../client";
import { notifications } from "../../../../../shared/lib/notifications.ts"; import { notifications } from "../../../../../shared/lib/notifications.ts";
import { useBoardsContext } from "../../../../../contexts/BoardsContext.tsx";
type Props = {
board: BoardSchema | null;
refetch: () => void;
}
const useStatusesDnd = ({ refetch, board }: Props) => { const useStatusesDnd = () => {
const {
refetchBoards,
selectedBoard: board,
} = useBoardsContext();
const updateStatusOrder = (status: StatusSchema, newOrdinalNumber: number) => { const updateStatusOrder = (status: StatusSchema, newOrdinalNumber: number) => {
if (!board) return; if (!board) return;
@@ -20,7 +22,7 @@ const useStatusesDnd = ({ refetch, board }: Props) => {
}) })
.then(({ ok, message }) => { .then(({ ok, message }) => {
if (!ok) notifications.error({ message }); if (!ok) notifications.error({ message });
refetch(); refetchBoards();
}); });
}; };

View File

@@ -1,15 +1,24 @@
import { useEffect, useState } from "react"; import React, { createContext, FC, useContext, useEffect, useState } from "react";
import { BoardSchema, BoardService } from "../../../../../client"; import { BoardSchema, BoardService } from "../client";
import { useProjectsContext } from "./ProjectsContext.tsx";
import { modals } from "@mantine/modals"; import { modals } from "@mantine/modals";
import { notifications } from "../../../../../shared/lib/notifications.ts"; import { notifications } from "../shared/lib/notifications.ts";
import { useProjectsContext } from "../../../../../contexts/ProjectsContext.tsx"; import useBoards from "../pages/CardsPage/hooks/useBoards.tsx";
type Props = { type BoardsContextState = {
boards: BoardSchema[]; boards: BoardSchema[],
refetchBoards: () => void; refetchBoards: () => void,
} selectedBoard: BoardSchema | null,
setSelectedBoard: React.Dispatch<React.SetStateAction<BoardSchema | null>>,
onCreateBoardClick: () => void,
onEditBoardClick: (board: BoardSchema) => void,
onDeleteBoardClick: (board: BoardSchema) => void,
};
const useBoards = ({ boards, refetchBoards }: Props) => { const BoardsContext = createContext<BoardsContextState | undefined>(undefined);
const useBoardsContextState = () => {
const { boards, refetchBoards } = useBoards();
const [selectedBoard, setSelectedBoard] = useState<BoardSchema | null>(null); const [selectedBoard, setSelectedBoard] = useState<BoardSchema | null>(null);
const { selectedProject: project } = useProjectsContext(); const { selectedProject: project } = useProjectsContext();
@@ -20,7 +29,6 @@ const useBoards = ({ boards, refetchBoards }: Props) => {
} }
if (selectedBoard) { if (selectedBoard) {
// Update selected board after changing all boards
let newBoard = boards.find(board => board.id === selectedBoard.id); let newBoard = boards.find(board => board.id === selectedBoard.id);
if (!newBoard && boards.length > 0) { if (!newBoard && boards.length > 0) {
@@ -73,6 +81,8 @@ const useBoards = ({ boards, refetchBoards }: Props) => {
}; };
return { return {
boards,
refetchBoards,
selectedBoard, selectedBoard,
setSelectedBoard, setSelectedBoard,
onCreateBoardClick, onCreateBoardClick,
@@ -81,4 +91,26 @@ const useBoards = ({ boards, refetchBoards }: Props) => {
}; };
}; };
export default useBoards; type BoardsContextProviderProps = {
children: React.ReactNode;
};
export const BoardsContextProvider: FC<BoardsContextProviderProps> = ({ children }) => {
const state = useBoardsContextState();
return (
<BoardsContext.Provider value={state}>
{children}
</BoardsContext.Provider>
);
};
export const useBoardsContext = () => {
const context = useContext(BoardsContext);
if (!context) {
throw new Error(
"useBoardsContext must be used within a BoardsContextProvider",
);
}
return context;
};

View File

@@ -25,6 +25,7 @@ import TasksProvider from "./providers/TasksProvider/TasksProvider.tsx";
import { ContextMenuProvider } from "mantine-contextmenu"; import { ContextMenuProvider } from "mantine-contextmenu";
import { ProjectsContextProvider } from "./contexts/ProjectsContext.tsx"; import { ProjectsContextProvider } from "./contexts/ProjectsContext.tsx";
import { ModulesContextProvider } from "./modules/context/ModulesContext.tsx"; import { ModulesContextProvider } from "./modules/context/ModulesContext.tsx";
import { BoardsContextProvider } from "./contexts/BoardsContext.tsx";
// Configuring router // Configuring router
const router = createRouter({ routeTree }); const router = createRouter({ routeTree });
@@ -57,10 +58,12 @@ ReactDOM.createRoot(document.getElementById("root")!).render(
<DatesProvider settings={{ locale: "ru" }}> <DatesProvider settings={{ locale: "ru" }}>
<TasksProvider> <TasksProvider>
<ProjectsContextProvider> <ProjectsContextProvider>
<ModulesContextProvider> <BoardsContextProvider>
<RouterProvider router={router} /> <ModulesContextProvider>
<Notifications /> <RouterProvider router={router} />
</ModulesContextProvider> <Notifications />
</ModulesContextProvider>
</BoardsContextProvider>
</ProjectsContextProvider> </ProjectsContextProvider>
</TasksProvider> </TasksProvider>
</DatesProvider> </DatesProvider>

View File

@@ -19,9 +19,7 @@ type CardPageContextStateProps = {
const useCardPageContextState = (props: CardPageContextStateProps) => { const useCardPageContextState = (props: CardPageContextStateProps) => {
const { refetchCards, defaultCardId } = props; const { refetchCards, defaultCardId } = props;
const [selectedCard, setSelectedCard] = useState<CardSchema | undefined>( const [selectedCard, setSelectedCard] = useState<CardSchema | undefined>(undefined);
undefined,
);
const refetchCard = () => { const refetchCard = () => {
const cardId = selectedCard?.id ?? defaultCardId; const cardId = selectedCard?.id ?? defaultCardId;

View File

@@ -0,0 +1,91 @@
import React, { createContext, FC, useContext, useState } from "react";
import { CardSummary } from "../../../client";
import DragState from "../enums/DragState.ts";
import useCardsDnd from "../../../components/Dnd/Cards/CardsDndColumn/hooks/useCardsDnd.tsx";
import useStatusesDnd from "../../../components/Dnd/Statuses/Statuses/hooks/useStatusesDnd.tsx";
import { DragStart, DropResult } from "@hello-pangea/dnd";
import useBoardsDnd from "../../../components/Dnd/Boards/Boards/hooks/useBoardsDnd.tsx";
type DndContextState = {
summaries: CardSummary[],
dragState: DragState,
onDragStart: (start: DragStart) => void,
onDragEnd: (result: DropResult) => Promise<void>,
};
const DndContext = createContext<DndContextState | undefined>(undefined);
type DndContextProps = {
summariesRaw: CardSummary[];
refetchSummaries: () => void;
}
const useDndContextState = ({
summariesRaw,
refetchSummaries,
}: DndContextProps) => {
const [dragState, setDragState] = useState<DragState>(DragState.DRAG_ENDED);
const {
summaries,
onCardDragEnd,
} = useCardsDnd({
summariesRaw,
refetchSummaries,
});
const { onStatusDragEnd } = useStatusesDnd();
const { onBoardDragEnd } = useBoardsDnd();
const onDragEnd = async (result: DropResult) => {
setDragState(DragState.DRAG_ENDED);
if (result.draggableId.includes("status")) {
return onStatusDragEnd(result);
}
if (result.draggableId.includes("board")) {
return onBoardDragEnd(result);
}
return onCardDragEnd(result);
};
const onDragStart = (start: DragStart) => {
if (start.source.droppableId.includes("status")) {
setDragState(DragState.DRAG_STATUS);
} else if (start.source.droppableId.includes("board")) {
setDragState(DragState.DRAG_BOARD);
} else {
setDragState(DragState.DRAG_CARD);
}
};
return {
summaries,
dragState,
onDragStart,
onDragEnd,
};
};
type DndContextProviderProps = {
children: React.ReactNode;
} & DndContextProps;
export const DndContextProvider: FC<DndContextProviderProps> = ({ children, ...props }) => {
const state = useDndContextState(props);
return (
<DndContext.Provider value={state}>
{children}
</DndContext.Provider>
);
};
export const useDndContext = () => {
const context = useContext(DndContext);
if (!context) {
throw new Error(
"useDndContext must be used within a DndContextProvider",
);
}
return context;
};

View File

@@ -2,6 +2,7 @@ enum DragState {
DRAG_ENDED, DRAG_ENDED,
DRAG_CARD, DRAG_CARD,
DRAG_STATUS, DRAG_STATUS,
DRAG_BOARD,
} }
export default DragState; export default DragState;

View File

@@ -1,63 +0,0 @@
import { BoardSchema, CardSummary } from "../../../client";
import { DragStart, DropResult } from "@hello-pangea/dnd";
import { useState } from "react";
import DragState from "../enums/DragState.ts";
import useCardsDnd from "../../../components/Dnd/Cards/CardsDndColumn/hooks/useCardsDnd.tsx";
import useStatusesDnd from "../../../components/Dnd/Statuses/Statuses/hooks/useStatusesDnd.tsx";
type Props = {
selectedBoard: BoardSchema | null;
summariesRaw: CardSummary[];
refetchSummaries: () => void;
refetchBoards: () => void;
}
const useDnd = ({
selectedBoard,
summariesRaw,
refetchSummaries,
refetchBoards,
}: Props) => {
const [dragState, setDragState] = useState<DragState>(DragState.DRAG_ENDED);
const {
summaries,
onCardDragEnd,
} = useCardsDnd({
summariesRaw,
refetchSummaries,
})
const {
onStatusDragEnd,
} = useStatusesDnd({
board: selectedBoard,
refetch: refetchBoards,
});
const onDragEnd = async (result: DropResult) => {
setDragState(DragState.DRAG_ENDED);
if (result.draggableId.includes("status")) {
return onStatusDragEnd(result);
}
return onCardDragEnd(result);
}
const onDragStart = (start: DragStart) => {
if (start.source.droppableId.includes("status")) {
setDragState(DragState.DRAG_STATUS);
} else {
setDragState(DragState.DRAG_CARD);
}
}
return {
summaries,
dragState,
onDragStart,
onDragEnd,
};
};
export default useDnd;

View File

@@ -13,37 +13,30 @@ import { useParams } from "@tanstack/react-router";
import { PrefillCardsWithExcelContextProvider } from "../contexts/PrefillDealsWithExcelContext.tsx"; import { PrefillCardsWithExcelContextProvider } from "../contexts/PrefillDealsWithExcelContext.tsx";
import DisplayMode from "../enums/DisplayMode.ts"; import DisplayMode from "../enums/DisplayMode.ts";
import CardsPageHeader from "../components/CardsPageHeader/CardsPageHeader.tsx"; import CardsPageHeader from "../components/CardsPageHeader/CardsPageHeader.tsx";
import Boards from "../../../components/Dnd/Boards/Boards/Boards.tsx";
import useBoards from "../hooks/useBoards.tsx";
import { ProjectsEditorContextProvider } from "../contexts/ProjectsEditorContext.tsx"; import { ProjectsEditorContextProvider } from "../contexts/ProjectsEditorContext.tsx";
import ProjectEditDrawer from "../drawers/ProjectEditDrawer/ProjectEditDrawer.tsx"; import ProjectEditDrawer from "../drawers/ProjectEditDrawer/ProjectEditDrawer.tsx";
import Boards from "../../../components/Dnd/Boards/Boards/Boards.tsx";
import { DndContextProvider } from "../contexts/DndContext.tsx";
export const CardsPage: FC = () => { export const CardsPage: FC = () => {
const { data, form } = useCardsPageState(); const { data, form } = useCardsPageState();
const { boards, refetchBoards } = useBoards();
const { dealId } = useParams({ strict: false }); const { dealId } = useParams({ strict: false });
const { summariesRaw, refetch: refetchSummaries } = useCardSummaries(); const { summariesRaw, refetch: refetchSummaries } = useCardSummaries();
const [displayMode, setDisplayMode] = useState<DisplayMode>( const [displayMode, setDisplayMode] = useState<DisplayMode>(DisplayMode.BOARD);
DisplayMode.BOARD,
const tableBody = (
<CardsTable items={data} />
); );
const getTableBody = () => { const boardsBody = (
return ( <DndContextProvider
<CardsTable items={data} /> summariesRaw={summariesRaw}
); refetchSummaries={refetchSummaries}
}; >
<Boards />
const getBoardsBody = () => { </DndContextProvider>
return ( );
<Boards
summariesRaw={summariesRaw}
refetchSummaries={refetchSummaries}
boards={boards}
refetchBoards={refetchBoards}
/>
);
};
const getBody = () => { const getBody = () => {
return ( return (
@@ -53,8 +46,8 @@ export const CardsPage: FC = () => {
animate={{ opacity: 1 }} animate={{ opacity: 1 }}
transition={{ duration: 0.2 }}> transition={{ duration: 0.2 }}>
{displayMode === DisplayMode.TABLE {displayMode === DisplayMode.TABLE
? getTableBody() ? tableBody
: getBoardsBody() : boardsBody
} }
</motion.div> </motion.div>
); );