feat: projects create, update, delete

This commit is contained in:
2025-08-13 15:03:09 +04:00
parent f2bba7e469
commit 90582b329e
27 changed files with 2310 additions and 1219 deletions

View File

@ -11,8 +11,12 @@ import { ColorSchemeToggle } from "@/components/ui/ColorSchemeToggle/ColorScheme
import useIsMobile from "@/hooks/useIsMobile";
const Header = () => {
const { projects, setSelectedProject, selectedProject } =
useProjectsContext();
const {
projects,
setSelectedProject,
selectedProject,
setIsEditorDrawerOpened: setIsProjectsDrawerOpened,
} = useProjectsContext();
const { setIsEditorDrawerOpened } = useBoardsContext();
const isMobile = useIsMobile();
@ -38,7 +42,9 @@ const Header = () => {
return (
<Stack gap={0}>
<Group justify={"space-between"}>
<Box p={"md"}>
<Box
p={"md"}
onClick={() => setIsProjectsDrawerOpened(true)}>
<IconChevronLeft />
</Box>
<Text>{selectedProject?.name}</Text>

View File

@ -7,8 +7,9 @@ import React, {
useEffect,
useState,
} from "react";
import { ProjectSchema } from "@/lib/client";
import useProjectsList from "@/hooks/useProjectsList";
import { useProjectsOperations } from "@/hooks/useProjectsOperations";
import { ProjectSchema, UpdateProjectSchema } from "@/lib/client";
type ProjectsContextState = {
selectedProject: ProjectSchema | null;
@ -16,6 +17,11 @@ type ProjectsContextState = {
React.SetStateAction<ProjectSchema | null>
>;
projects: ProjectSchema[];
onCreateProject: (name: string) => void;
onUpdateProject: (projectId: number, project: UpdateProjectSchema) => void;
onDeleteProject: (project: ProjectSchema) => void;
isEditorDrawerOpened: boolean;
setIsEditorDrawerOpened: React.Dispatch<React.SetStateAction<boolean>>;
};
const ProjectsContext = createContext<ProjectsContextState | undefined>(
@ -23,7 +29,13 @@ const ProjectsContext = createContext<ProjectsContextState | undefined>(
);
const useProjectsContextState = () => {
const { projects } = useProjectsList();
const [isEditorDrawerOpened, setIsEditorDrawerOpened] =
useState<boolean>(false);
const {
projects,
setProjects,
refetch: refetchProjects,
} = useProjectsList();
const [selectedProject, setSelectedProject] =
useState<ProjectSchema | null>(null);
@ -43,10 +55,22 @@ const useProjectsContextState = () => {
setSelectedProject(null);
}, [projects]);
const { onCreateProject, onUpdateProject, onDeleteProject } =
useProjectsOperations({
projects,
setProjects,
refetchProjects,
});
return {
projects,
selectedProject,
setSelectedProject,
onCreateProject,
onUpdateProject,
onDeleteProject,
isEditorDrawerOpened,
setIsEditorDrawerOpened,
};
};

View File

@ -0,0 +1,15 @@
.project:hover {
background-color: whitesmoke;
@mixin dark {
background-color: var(--mantine-color-dark-6);
}
}
.menu-button {
cursor: pointer;
}
.project:hover:has(.menu-button:hover) {
background: none; /* or revert to normal */
}

View File

@ -0,0 +1,66 @@
"use client";
import React, { FC } from "react";
import { IconChevronLeft } from "@tabler/icons-react";
import {
Box,
Center,
Divider,
Drawer,
Group,
rem,
Stack,
Text,
} from "@mantine/core";
import { useProjectsContext } from "@/app/deals/contexts/ProjectsContext";
import CreateProjectButton from "@/app/deals/drawers/ProjectsEditorDrawer/components/CreateProjectButton";
import ProjectMobile from "@/app/deals/drawers/ProjectsEditorDrawer/components/ProjectMobile";
const ProjectsEditorDrawer: FC = () => {
const { projects, isEditorDrawerOpened, setIsEditorDrawerOpened } =
useProjectsContext();
const onClose = () => setIsEditorDrawerOpened(false);
return (
<Drawer
size={"100%"}
position={"left"}
onClose={onClose}
removeScrollProps={{ allowPinchZoom: true }}
withCloseButton={false}
opened={isEditorDrawerOpened}
trapFocus={false}
styles={{
body: {
height: "100%",
display: "flex",
flexDirection: "column",
gap: rem(10),
},
}}>
<Group justify={"space-between"}>
<Box
onClick={onClose}
p={"xs"}>
<IconChevronLeft />
</Box>
<Center>
<Text>Проекты</Text>
</Center>
<Box p={"lg"} />
</Group>
<Divider />
<Stack gap={0}>
{projects.map((project, index) => (
<ProjectMobile
key={index}
project={project}
/>
))}
</Stack>
<CreateProjectButton />
</Drawer>
);
};
export default ProjectsEditorDrawer;

View File

@ -0,0 +1,32 @@
import { IconPlus } from "@tabler/icons-react";
import { Box, Group, Text } from "@mantine/core";
import { modals } from "@mantine/modals";
import { useProjectsContext } from "@/app/deals/contexts/ProjectsContext";
const CreateProjectButton = () => {
const { onCreateProject } = useProjectsContext();
const onStartCreating = () => {
modals.openContextModal({
modal: "enterNameModal",
title: "Создание проекта",
withCloseButton: true,
innerProps: {
onComplete: onCreateProject,
},
});
};
return (
<Group
ml={"xs"}
onClick={onStartCreating}>
<IconPlus />
<Box mt={5}>
<Text>Создать проект</Text>
</Box>
</Group>
);
};
export default CreateProjectButton;

View File

@ -0,0 +1,56 @@
import React, { FC } from "react";
import { IconDotsVertical, IconEdit, IconTrash } from "@tabler/icons-react";
import { Box, Group, Menu, Text } from "@mantine/core";
import { useProjectsContext } from "@/app/deals/contexts/ProjectsContext";
import { ProjectSchema } from "@/lib/client";
import styles from "./../ProjectsEditorDrawer.module.css";
type Props = {
project: ProjectSchema;
startEditing: () => void;
menuIconSize: number;
};
const ProjectMenu: FC<Props> = ({
project,
startEditing,
menuIconSize = 16,
}) => {
const { onDeleteProject } = useProjectsContext();
return (
<Menu>
<Menu.Target>
<Box
className={styles["menu-button"]}
onClick={e => e.stopPropagation()}>
<IconDotsVertical size={menuIconSize} />
</Box>
</Menu.Target>
<Menu.Dropdown>
<Menu.Item
onClick={e => {
e.stopPropagation();
startEditing();
}}>
<Group wrap={"nowrap"}>
<IconEdit />
<Text>Переименовать</Text>
</Group>
</Menu.Item>
<Menu.Item
onClick={e => {
e.stopPropagation();
onDeleteProject(project);
}}>
<Group wrap={"nowrap"}>
<IconTrash />
<Text>Удалить</Text>
</Group>
</Menu.Item>
</Menu.Dropdown>
</Menu>
);
};
export default ProjectMenu;

View File

@ -0,0 +1,55 @@
import React, { FC } from "react";
import { Box, Group, Text } from "@mantine/core";
import { modals } from "@mantine/modals";
import { useProjectsContext } from "@/app/deals/contexts/ProjectsContext";
import ProjectMenu from "@/app/deals/drawers/ProjectsEditorDrawer/components/ProjectMenu";
import { ProjectSchema } from "@/lib/client";
import styles from "./../ProjectsEditorDrawer.module.css";
type Props = {
project: ProjectSchema;
};
const ProjectMobile: FC<Props> = ({ project }) => {
const { onUpdateProject, setSelectedProject, setIsEditorDrawerOpened } =
useProjectsContext();
const startEditing = () => {
modals.openContextModal({
modal: "enterNameModal",
title: "Редактирование проекта",
withCloseButton: true,
innerProps: {
onComplete: name => onUpdateProject(project.id, { name }),
defaultValue: project.name,
},
});
};
const onClick = () => {
setSelectedProject(project);
setIsEditorDrawerOpened(false);
};
return (
<Group
w={"100%"}
pl={"xs"}
py={"xs"}
justify={"space-between"}
wrap={"nowrap"}
className={styles.project}
onClick={onClick}>
<Box>
<Text>{project.name}</Text>
</Box>
<ProjectMenu
project={project}
startEditing={startEditing}
menuIconSize={22}
/>
</Group>
);
};
export default ProjectMobile;

View File

@ -0,0 +1,3 @@
import ProjectsEditorDrawer from "@/app/deals/drawers/ProjectsEditorDrawer/ProjectsEditorDrawer";
export default ProjectsEditorDrawer;

View File

@ -5,6 +5,7 @@ import { ProjectsContextProvider } from "@/app/deals/contexts/ProjectsContext";
import { StatusesContextProvider } from "@/app/deals/contexts/StatusesContext";
import BoardStatusesEditorDrawer from "@/app/deals/drawers/BoardStatusesEditorDrawer";
import ProjectBoardsEditorDrawer from "@/app/deals/drawers/ProjectBoardsEditorDrawer";
import ProjectsEditorDrawer from "@/app/deals/drawers/ProjectsEditorDrawer";
import PageBlock from "@/components/layout/PageBlock/PageBlock";
import PageContainer from "@/components/layout/PageContainer/PageContainer";
import { DealsContextProvider } from "./contexts/DealsContext";
@ -27,6 +28,7 @@ export default function DealsPage() {
<BoardStatusesEditorDrawer />
</StatusesContextProvider>
<ProjectBoardsEditorDrawer />
<ProjectsEditorDrawer />
</PageBlock>
</PageContainer>
</BoardsContextProvider>