refactoring
This commit is contained in:
@@ -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 {
|
||||||
|
|||||||
@@ -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)}
|
||||||
|
|||||||
@@ -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>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -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,
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -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 => {
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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(
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
@@ -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>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -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();
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
|
};
|
||||||
11
src/main.tsx
11
src/main.tsx
@@ -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>
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
91
src/pages/CardsPage/contexts/DndContext.tsx
Normal file
91
src/pages/CardsPage/contexts/DndContext.tsx
Normal 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;
|
||||||
|
};
|
||||||
@@ -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;
|
||||||
|
|||||||
@@ -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;
|
|
||||||
@@ -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>
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user