From e3137de46d95f1aa34c4178dc3dd8bceaf1fa029 Mon Sep 17 00:00:00 2001 From: AlexSserb Date: Sat, 9 Aug 2025 10:13:25 +0400 Subject: [PATCH] feat: boards dnd editor for mobile --- src/app/deals/components/Boards/Boards.tsx | 2 +- .../CreateBoardButton/CreateBoardButton.tsx | 4 +- src/app/deals/components/Header/Header.tsx | 38 ++++++++-- src/app/deals/contexts/BoardsContext.tsx | 6 ++ .../ProjectBoardsEditorDrawer.tsx | 69 +++++++++++++++++++ .../components/CreateBoardButtonMobile.tsx | 30 ++++++++ .../ProjectBoardsEditorDrawer/index.ts | 3 + src/app/deals/page.tsx | 2 + .../dnd/SortableDnd/SortableDnd.tsx | 21 ++++-- .../layout/PageBlock/PageBlock.module.css | 6 +- src/modals/EnterNameModal/EnterNameModal.tsx | 2 +- 11 files changed, 166 insertions(+), 17 deletions(-) create mode 100644 src/app/deals/drawers/ProjectBoardsEditorDrawer/ProjectBoardsEditorDrawer.tsx create mode 100644 src/app/deals/drawers/ProjectBoardsEditorDrawer/components/CreateBoardButtonMobile.tsx create mode 100644 src/app/deals/drawers/ProjectBoardsEditorDrawer/index.ts diff --git a/src/app/deals/components/Boards/Boards.tsx b/src/app/deals/components/Boards/Boards.tsx index 09a3bd3..2aba06a 100644 --- a/src/app/deals/components/Boards/Boards.tsx +++ b/src/app/deals/components/Boards/Boards.tsx @@ -36,7 +36,7 @@ const Boards = () => { renderItem={renderBoard} onDragEnd={onDragEnd} onItemClick={selectBoard} - rowStyle={{ flexWrap: "nowrap" }} + containerStyle={{ flexWrap: "nowrap" }} disabled={isMobile} /> diff --git a/src/app/deals/components/CreateBoardButton/CreateBoardButton.tsx b/src/app/deals/components/CreateBoardButton/CreateBoardButton.tsx index 114bd17..8de7717 100644 --- a/src/app/deals/components/CreateBoardButton/CreateBoardButton.tsx +++ b/src/app/deals/components/CreateBoardButton/CreateBoardButton.tsx @@ -7,7 +7,9 @@ const CreateBoardButton = () => { const { onCreateBoard } = useBoardsContext(); return ( - + { const { projects, setSelectedProject, selectedProject } = useProjectsContext(); + const { setIsEditorDrawerOpened } = useBoardsContext(); - return ( - + const getDesktopHeader = () => { + return ( value && setSelectedProject(value)} /> + ); + }; + + const getMobileHeader = () => { + return ( + + + + + {selectedProject?.name} + setIsEditorDrawerOpened(true)}> + + + + ); + }; + + return ( + + {isMobile ? getMobileHeader() : getDesktopHeader()} ); }; diff --git a/src/app/deals/contexts/BoardsContext.tsx b/src/app/deals/contexts/BoardsContext.tsx index 38dc146..052148b 100644 --- a/src/app/deals/contexts/BoardsContext.tsx +++ b/src/app/deals/contexts/BoardsContext.tsx @@ -21,6 +21,8 @@ type BoardsContextState = { onCreateBoard: (name: string) => void; onUpdateBoard: (boardId: number, board: UpdateBoardSchema) => void; onDeleteBoard: (board: BoardSchema) => void; + isEditorDrawerOpened: boolean; + setIsEditorDrawerOpened: React.Dispatch>; }; const BoardsContext = createContext(undefined); @@ -35,6 +37,8 @@ const useBoardsContextState = () => { const [selectedBoard, setSelectedBoard] = useState( null ); + const [isEditorDrawerOpened, setIsEditorDrawerOpened] = + useState(false); useEffect(() => { if (boards.length > 0 && selectedBoard === null) { @@ -70,6 +74,8 @@ const useBoardsContextState = () => { onCreateBoard, onUpdateBoard, onDeleteBoard, + isEditorDrawerOpened, + setIsEditorDrawerOpened, }; }; diff --git a/src/app/deals/drawers/ProjectBoardsEditorDrawer/ProjectBoardsEditorDrawer.tsx b/src/app/deals/drawers/ProjectBoardsEditorDrawer/ProjectBoardsEditorDrawer.tsx new file mode 100644 index 0000000..f673846 --- /dev/null +++ b/src/app/deals/drawers/ProjectBoardsEditorDrawer/ProjectBoardsEditorDrawer.tsx @@ -0,0 +1,69 @@ +"use client"; + +import React, { FC } from "react"; +import { IconChevronLeft } from "@tabler/icons-react"; +import { Box, Center, Drawer, Group, rem, Text } from "@mantine/core"; +import Board from "@/app/deals/components/Board/Board"; +import { useBoardsContext } from "@/app/deals/contexts/BoardsContext"; +import { useProjectsContext } from "@/app/deals/contexts/ProjectsContext"; +import CreateBoardButtonMobile from "@/app/deals/drawers/ProjectBoardsEditorDrawer/components/CreateBoardButtonMobile"; +import SortableDnd from "@/components/dnd/SortableDnd"; +import { BoardSchema } from "@/lib/client"; + +const ProjectBoardsEditorDrawer: FC = () => { + const { + boards, + onUpdateBoard, + isEditorDrawerOpened, + setIsEditorDrawerOpened, + } = useBoardsContext(); + const { selectedProject } = useProjectsContext(); + const onClose = () => setIsEditorDrawerOpened(false); + + const renderBoard = (board: BoardSchema) => { + return ; + }; + + const onDragEnd = (itemId: number, newLexorank: string) => { + onUpdateBoard(itemId, { lexorank: newLexorank }); + }; + + return ( + + + + + +
+ {selectedProject?.name} +
+ +
+ + +
+ ); +}; + +export default ProjectBoardsEditorDrawer; diff --git a/src/app/deals/drawers/ProjectBoardsEditorDrawer/components/CreateBoardButtonMobile.tsx b/src/app/deals/drawers/ProjectBoardsEditorDrawer/components/CreateBoardButtonMobile.tsx new file mode 100644 index 0000000..f80dd96 --- /dev/null +++ b/src/app/deals/drawers/ProjectBoardsEditorDrawer/components/CreateBoardButtonMobile.tsx @@ -0,0 +1,30 @@ +import { IconPlus } from "@tabler/icons-react"; +import { Box, Group, Text } from "@mantine/core"; +import { modals } from "@mantine/modals"; +import { useBoardsContext } from "@/app/deals/contexts/BoardsContext"; + +const CreateBoardButtonMobile = () => { + const { onCreateBoard } = useBoardsContext(); + + const onStartCreating = () => { + modals.openContextModal({ + modal: "enterNameModal", + title: "Создание доски", + withCloseButton: true, + innerProps: { + onComplete: onCreateBoard, + }, + }); + }; + + return ( + + + + Создать доску + + + ); +}; + +export default CreateBoardButtonMobile; diff --git a/src/app/deals/drawers/ProjectBoardsEditorDrawer/index.ts b/src/app/deals/drawers/ProjectBoardsEditorDrawer/index.ts new file mode 100644 index 0000000..705c80f --- /dev/null +++ b/src/app/deals/drawers/ProjectBoardsEditorDrawer/index.ts @@ -0,0 +1,3 @@ +import ProjectBoardsEditorDrawer from "@/app/deals/drawers/ProjectBoardsEditorDrawer/ProjectBoardsEditorDrawer"; + +export default ProjectBoardsEditorDrawer; diff --git a/src/app/deals/page.tsx b/src/app/deals/page.tsx index 823bcb0..da082e4 100644 --- a/src/app/deals/page.tsx +++ b/src/app/deals/page.tsx @@ -5,6 +5,7 @@ import Header from "@/app/deals/components/Header/Header"; import { BoardsContextProvider } from "@/app/deals/contexts/BoardsContext"; import { ProjectsContextProvider } from "@/app/deals/contexts/ProjectsContext"; import { StatusesContextProvider } from "@/app/deals/contexts/StatusesContext"; +import ProjectBoardsEditorDrawer from "@/app/deals/drawers/ProjectBoardsEditorDrawer"; import PageBlock from "@/components/layout/PageBlock/PageBlock"; import PageContainer from "@/components/layout/PageContainer/PageContainer"; import { DealsContextProvider } from "./contexts/DealsContext"; @@ -23,6 +24,7 @@ export default function DealsPage() { + diff --git a/src/components/dnd/SortableDnd/SortableDnd.tsx b/src/components/dnd/SortableDnd/SortableDnd.tsx index 116d5a8..240111a 100644 --- a/src/components/dnd/SortableDnd/SortableDnd.tsx +++ b/src/components/dnd/SortableDnd/SortableDnd.tsx @@ -10,7 +10,7 @@ import React, { import { Active, DndContext, DragEndEvent } from "@dnd-kit/core"; import { SortableContext } from "@dnd-kit/sortable"; import { LexoRank } from "lexorank"; -import { Box, Group } from "@mantine/core"; +import { Box, Flex } from "@mantine/core"; import useDndSensors from "@/app/deals/hooks/useSensors"; import { SortableOverlay } from "@/components/dnd/SortableDnd/SortableOverlay"; import SortableItem from "@/components/dnd/SortableItem"; @@ -25,9 +25,10 @@ type Props = { initialItems: T[]; renderItem: (item: T) => ReactNode; onDragEnd: (itemId: number, newLexorank: string) => void; - onItemClick: (item: T) => void; - rowStyle?: CSSProperties; + onItemClick?: (item: T) => void; + containerStyle?: CSSProperties; itemStyle?: CSSProperties; + vertical?: boolean; disabled?: boolean; }; @@ -36,8 +37,9 @@ const SortableDnd = ({ renderItem, onDragEnd, onItemClick, - rowStyle, + containerStyle, itemStyle, + vertical, disabled = false, }: Props) => { const [active, setActive] = useState(null); @@ -100,14 +102,19 @@ const SortableDnd = ({ - {items.map((item, index) => ( { + if (!onItemClick) return; e.preventDefault(); e.stopPropagation(); onItemClick(item); @@ -121,7 +128,7 @@ const SortableDnd = ({ ))} - + {activeItem ? renderItem(activeItem) : null} diff --git a/src/components/layout/PageBlock/PageBlock.module.css b/src/components/layout/PageBlock/PageBlock.module.css index 56cf0b8..7a0bdf2 100644 --- a/src/components/layout/PageBlock/PageBlock.module.css +++ b/src/components/layout/PageBlock/PageBlock.module.css @@ -1,5 +1,4 @@ .container { - border-radius: rem(40); background-color: white; @mixin dark { background-color: var(--mantine-color-dark-8); @@ -8,7 +7,10 @@ @mixin light { box-shadow: 5px 5px 24px rgba(0, 0, 0, 0.16); } - padding: rem(35); + @media (min-width: 48em) { + padding: rem(35); + border-radius: rem(40); + } } .container-full-height { diff --git a/src/modals/EnterNameModal/EnterNameModal.tsx b/src/modals/EnterNameModal/EnterNameModal.tsx index e9fa3af..e92a346 100644 --- a/src/modals/EnterNameModal/EnterNameModal.tsx +++ b/src/modals/EnterNameModal/EnterNameModal.tsx @@ -20,7 +20,7 @@ const EnterNameModal = ({ }: ContextModalProps) => { const form = useForm({ initialValues: { - name: innerProps.defaultValue, + name: innerProps.defaultValue ?? "", }, validate: { name: name => !name && "Введите название",