feat: board creation and actions dropdown

This commit is contained in:
2025-08-07 09:19:30 +04:00
parent 4b843d8e5d
commit 335fbfe81c
12 changed files with 341 additions and 61 deletions

View File

@ -1,5 +1,7 @@
import React, { FC } from "react";
import { Box } from "@mantine/core";
import React, { FC, useState } from "react";
import { IconDotsVertical, IconEdit, IconTrash } from "@tabler/icons-react";
import { Box, Group, Menu, Text } from "@mantine/core";
import { useBoardsContext } from "@/app/deals/contexts/BoardsContext";
import { BoardSchema } from "@/lib/client";
type Props = {
@ -7,7 +9,54 @@ type Props = {
};
const Board: FC<Props> = ({ board }) => {
return <Box miw={100} style={{ borderWidth: 1, margin: 0 }}>{board.name}</Box>;
const [isHovered, setIsHovered] = useState(false);
const { selectedBoard } = useBoardsContext();
return (
<Group
miw={130}
px={"md"}
py={"xs"}
justify={"space-between"}
wrap={"nowrap"}
style={{ borderWidth: 1 }}
onMouseEnter={() => setIsHovered(true)}
onMouseLeave={() => setIsHovered(false)}>
<Text>{board.name}</Text>
<Menu>
<Menu.Target>
<Box
style={{
opacity:
isHovered || selectedBoard?.id === board.id
? 1
: 0,
cursor: "pointer",
}}
onClick={e => {
e.preventDefault();
e.stopPropagation();
}}>
<IconDotsVertical size={16} />
</Box>
</Menu.Target>
<Menu.Dropdown>
<Menu.Item>
<Group wrap={"nowrap"}>
<IconEdit />
<Text>Переименовать</Text>
</Group>
</Menu.Item>
<Menu.Item>
<Group wrap={"nowrap"}>
<IconTrash />
<Text>Удалить</Text>
</Group>
</Menu.Item>
</Menu.Dropdown>
</Menu>
</Group>
);
};
export default Board;

View File

@ -2,12 +2,13 @@
import React from "react";
import { useMutation } from "@tanstack/react-query";
import { ScrollArea } from "@mantine/core";
import { Group, ScrollArea } from "@mantine/core";
import Board from "@/app/deals/components/Board/Board";
import CreateBoardButton from "@/app/deals/components/CreateBoardButton/CreateBoardButton";
import { useBoardsContext } from "@/app/deals/contexts/BoardsContext";
import SortableDnd from "@/components/dnd/SortableDnd";
import { BoardSchema } from "@/lib/client";
import { updateBoardMutation } from "@/lib/client/@tanstack/react-query.gen";
import SortableDnd from "@/components/dnd/SortableDnd";
import { notifications } from "@/lib/notifications";
const Boards = () => {
@ -51,13 +52,16 @@ const Boards = () => {
scrollbars={"x"}
scrollbarSize={0}
w={"100%"}>
<SortableDnd
initialItems={boards}
renderItem={renderBoard}
onDragEnd={onDragEnd}
onItemClick={selectBoard}
rowStyle={{ flexWrap: "nowrap" }}
/>
<Group wrap={"nowrap"}>
<SortableDnd
initialItems={boards}
renderItem={renderBoard}
onDragEnd={onDragEnd}
onItemClick={selectBoard}
rowStyle={{ flexWrap: "nowrap" }}
/>
<CreateBoardButton />
</Group>
</ScrollArea>
);
};

View File

@ -0,0 +1,54 @@
import { useState } from "react";
import { IconCheck, IconPlus, IconX } from "@tabler/icons-react";
import { Box, Group, TextInput } from "@mantine/core";
import { useBoardsContext } from "@/app/deals/contexts/BoardsContext";
const CreateBoardButton = () => {
const { onCreateBoardClick } = useBoardsContext();
const [isWriting, setIsWriting] = useState<boolean>(false);
const [name, setName] = useState<string>("");
const onStartCreating = () => {
setName("");
setIsWriting(true);
};
const onCancelCreating = () => setIsWriting(false);
const onCompleteCreating = () => {
if (name) {
onCreateBoardClick(name);
}
setIsWriting(false);
};
return (
<Box style={{ cursor: "pointer" }}>
{isWriting ? (
<Group>
<TextInput
placeholder={"Название доски"}
variant={"unstyled"}
value={name}
onChange={e => setName(e.target.value)}
onKeyDown={e =>
e.key === "Enter" && onCompleteCreating()
}
/>
<Box onClick={onCompleteCreating}>
<IconCheck />
</Box>
<Box onClick={onCancelCreating}>
<IconX />
</Box>
</Group>
) : (
<Box onClick={onStartCreating}>
<IconPlus />
</Box>
)}
</Box>
);
};
export default CreateBoardButton;

View File

@ -6,7 +6,6 @@ type Props = {
};
const DealCard = ({ deal }: Props) => {
console.log("deal");
return <Card>{deal.name}</Card>;
};

View File

@ -1,37 +0,0 @@
import React from "react";
import { useSortable } from "@dnd-kit/sortable";
import { CSS } from "@dnd-kit/utilities";
type Props = {
children: React.ReactNode;
id: string;
};
const SortableItem = ({ children, id }: Props) => {
const {
attributes,
listeners,
setNodeRef,
transform,
transition,
isDragging,
} = useSortable({ id });
const style = {
transform: CSS.Transform.toString(transform),
transition,
opacity: isDragging ? 0 : 1,
};
return (
<div
ref={setNodeRef}
style={style}
{...attributes}
{...listeners}>
{children}
</div>
);
};
export default SortableItem;

View File

@ -8,11 +8,16 @@ type Props = {
children: ReactNode;
};
const StatusColumnWrapper = ({ status, children, isDragging = false }: Props) => {
const StatusColumnWrapper = ({
status,
children,
isDragging = false,
}: Props) => {
return (
<Box
style={{
backgroundColor: "#eee",
borderWidth: 1,
borderColor: "gray",
padding: 2,
width: "15vw",
minWidth: 150,