feat: drawers registry
This commit is contained in:
@ -13,7 +13,7 @@ type Props = {
|
||||
};
|
||||
|
||||
const Board: FC<Props> = ({ board }) => {
|
||||
const { selectedBoard, onUpdateBoard } = useBoardsContext();
|
||||
const { selectedBoard, onUpdateBoard, onDeleteBoard } = useBoardsContext();
|
||||
const isMobile = useIsMobile();
|
||||
const [isHovered, setIsHovered] = useState(false);
|
||||
|
||||
@ -49,6 +49,7 @@ const Board: FC<Props> = ({ board }) => {
|
||||
isHovered={
|
||||
selectedBoard?.id === board.id || isHovered
|
||||
}
|
||||
onDeleteBoard={onDeleteBoard}
|
||||
board={board}
|
||||
startEditing={startEditing}
|
||||
/>
|
||||
|
||||
@ -1,18 +1,21 @@
|
||||
import React, { FC } 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 = {
|
||||
board: BoardSchema;
|
||||
startEditing: () => void;
|
||||
onDeleteBoard: (board: BoardSchema) => void;
|
||||
isHovered?: boolean;
|
||||
};
|
||||
|
||||
const BoardMenu: FC<Props> = ({ board, startEditing, isHovered = true }) => {
|
||||
const { onDeleteBoard } = useBoardsContext();
|
||||
|
||||
const BoardMenu: FC<Props> = ({
|
||||
board,
|
||||
startEditing,
|
||||
onDeleteBoard,
|
||||
isHovered = true,
|
||||
}) => {
|
||||
return (
|
||||
<Menu>
|
||||
<Menu.Target>
|
||||
|
||||
@ -6,17 +6,16 @@ import Boards from "@/app/deals/components/shared/Boards/Boards";
|
||||
import { useBoardsContext } from "@/app/deals/contexts/BoardsContext";
|
||||
import { useProjectsContext } from "@/app/deals/contexts/ProjectsContext";
|
||||
import ProjectSelect from "@/components/selects/ProjectSelect/ProjectSelect";
|
||||
import { useDrawersContext } from "@/drawers/DrawersContext";
|
||||
import useIsMobile from "@/hooks/useIsMobile";
|
||||
|
||||
const Header = () => {
|
||||
const {
|
||||
projects,
|
||||
setSelectedProjectId,
|
||||
selectedProject,
|
||||
setIsEditorDrawerOpened: setIsProjectsDrawerOpened,
|
||||
} = useProjectsContext();
|
||||
const { setIsEditorDrawerOpened } = useBoardsContext();
|
||||
const { projects, setSelectedProjectId, refetchProjects, selectedProject } =
|
||||
useProjectsContext();
|
||||
const { refetchBoards } = useBoardsContext();
|
||||
const { openDrawer } = useDrawersContext();
|
||||
const isMobile = useIsMobile();
|
||||
|
||||
const getDesktopHeader = () => {
|
||||
return (
|
||||
<Flex
|
||||
@ -45,19 +44,44 @@ const Header = () => {
|
||||
);
|
||||
};
|
||||
|
||||
const selectProjectId = async (projectId: number | null) => {
|
||||
await refetchProjects();
|
||||
setSelectedProjectId(projectId);
|
||||
};
|
||||
|
||||
const openProjectsEditorDrawer = () => {
|
||||
openDrawer({
|
||||
key: "projectsEditorDrawer",
|
||||
props: {
|
||||
setSelectedProjectId: selectProjectId,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const openBoardsEditorDrawer = () => {
|
||||
if (!selectedProject) return;
|
||||
openDrawer({
|
||||
key: "projectBoardsEditorDrawer",
|
||||
props: {
|
||||
project: selectedProject,
|
||||
},
|
||||
onClose: refetchBoards,
|
||||
});
|
||||
};
|
||||
|
||||
const getMobileHeader = () => {
|
||||
return (
|
||||
<>
|
||||
<Group justify={"space-between"}>
|
||||
<Box
|
||||
p={"md"}
|
||||
onClick={() => setIsProjectsDrawerOpened(true)}>
|
||||
onClick={openProjectsEditorDrawer}>
|
||||
<IconChevronLeft />
|
||||
</Box>
|
||||
<Text>{selectedProject?.name}</Text>
|
||||
<Box
|
||||
p={"md"}
|
||||
onClick={() => setIsEditorDrawerOpened(true)}>
|
||||
onClick={openBoardsEditorDrawer}>
|
||||
<IconSettings />
|
||||
</Box>
|
||||
</Group>
|
||||
|
||||
@ -1,10 +1,11 @@
|
||||
import React, { FC } from "react";
|
||||
import { Group, Text } from "@mantine/core";
|
||||
import styles from "./StatusColumnHeader.module.css";
|
||||
import StatusMenu from "@/app/deals/components/shared/StatusMenu/StatusMenu";
|
||||
import { useBoardsContext } from "@/app/deals/contexts/BoardsContext";
|
||||
import { useStatusesContext } from "@/app/deals/contexts/StatusesContext";
|
||||
import InPlaceInput from "@/components/ui/InPlaceInput/InPlaceInput";
|
||||
import { StatusSchema } from "@/lib/client";
|
||||
import styles from "./StatusColumnHeader.module.css";
|
||||
|
||||
type Props = {
|
||||
status: StatusSchema;
|
||||
@ -12,7 +13,9 @@ type Props = {
|
||||
};
|
||||
|
||||
const StatusColumnHeader: FC<Props> = ({ status, isDragging }) => {
|
||||
const { onUpdateStatus } = useStatusesContext();
|
||||
const { onUpdateStatus, onDeleteStatus, refetchStatuses } =
|
||||
useStatusesContext();
|
||||
const { selectedBoard } = useBoardsContext();
|
||||
|
||||
const handleSave = (value: string) => {
|
||||
const newValue = value.trim();
|
||||
@ -48,8 +51,11 @@ const StatusColumnHeader: FC<Props> = ({ status, isDragging }) => {
|
||||
{status.name}
|
||||
</Text>
|
||||
<StatusMenu
|
||||
board={selectedBoard}
|
||||
status={status}
|
||||
handleEdit={startEditing}
|
||||
refetchStatuses={refetchStatuses}
|
||||
onDeleteStatus={onDeleteStatus}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
|
||||
@ -6,26 +6,35 @@ import {
|
||||
IconTrash,
|
||||
} from "@tabler/icons-react";
|
||||
import { Box, Group, Menu, Text } from "@mantine/core";
|
||||
import { useStatusesContext } from "@/app/deals/contexts/StatusesContext";
|
||||
import { useDrawersContext } from "@/drawers/DrawersContext";
|
||||
import useIsMobile from "@/hooks/useIsMobile";
|
||||
import { StatusSchema } from "@/lib/client";
|
||||
import { BoardSchema, StatusSchema } from "@/lib/client";
|
||||
|
||||
type Props = {
|
||||
status: StatusSchema;
|
||||
handleEdit: () => void;
|
||||
board: BoardSchema | null;
|
||||
onDeleteStatus: (status: StatusSchema) => void;
|
||||
refetchStatuses?: () => void;
|
||||
withChangeOrderButton?: boolean;
|
||||
};
|
||||
|
||||
const StatusMenu: FC<Props> = ({ status, handleEdit }) => {
|
||||
const StatusMenu: FC<Props> = ({
|
||||
status,
|
||||
handleEdit,
|
||||
board,
|
||||
onDeleteStatus,
|
||||
refetchStatuses,
|
||||
withChangeOrderButton = true,
|
||||
}) => {
|
||||
const isMobile = useIsMobile();
|
||||
const { onDeleteStatus, setIsEditorDrawerOpened } = useStatusesContext();
|
||||
const { openDrawer } = useDrawersContext();
|
||||
|
||||
return (
|
||||
<Menu>
|
||||
<Menu.Target>
|
||||
<Box
|
||||
style={{
|
||||
cursor: "pointer",
|
||||
}}
|
||||
style={{ cursor: "pointer" }}
|
||||
onClick={e => e.stopPropagation()}>
|
||||
<IconDotsVertical />
|
||||
</Box>
|
||||
@ -51,11 +60,18 @@ const StatusMenu: FC<Props> = ({ status, handleEdit }) => {
|
||||
<Text>Удалить</Text>
|
||||
</Group>
|
||||
</Menu.Item>
|
||||
{isMobile && (
|
||||
{isMobile && withChangeOrderButton && (
|
||||
<Menu.Item
|
||||
onClick={e => {
|
||||
e.stopPropagation();
|
||||
setIsEditorDrawerOpened(true);
|
||||
if (!board) return;
|
||||
openDrawer({
|
||||
key: "boardStatusesEditorDrawer",
|
||||
props: {
|
||||
board,
|
||||
},
|
||||
onClose: refetchStatuses,
|
||||
});
|
||||
}}>
|
||||
<Group wrap={"nowrap"}>
|
||||
<IconExchange />
|
||||
|
||||
@ -8,12 +8,11 @@ import { ProjectSchema, UpdateProjectSchema } from "@/lib/client";
|
||||
type ProjectsContextState = {
|
||||
selectedProject: ProjectSchema | null;
|
||||
setSelectedProjectId: React.Dispatch<React.SetStateAction<number | null>>;
|
||||
refetchProjects: () => Promise<void>;
|
||||
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>(
|
||||
@ -21,8 +20,6 @@ const ProjectsContext = createContext<ProjectsContextState | undefined>(
|
||||
);
|
||||
|
||||
const useProjectsContextState = () => {
|
||||
const [isEditorDrawerOpened, setIsEditorDrawerOpened] =
|
||||
useState<boolean>(false);
|
||||
const {
|
||||
projects,
|
||||
setProjects,
|
||||
@ -49,12 +46,11 @@ const useProjectsContextState = () => {
|
||||
return {
|
||||
projects,
|
||||
selectedProject,
|
||||
refetchProjects,
|
||||
setSelectedProjectId,
|
||||
onCreateProject,
|
||||
onUpdateProject,
|
||||
onDeleteProject,
|
||||
isEditorDrawerOpened,
|
||||
setIsEditorDrawerOpened,
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@ -1,36 +1,18 @@
|
||||
"use client";
|
||||
|
||||
import React, { createContext, FC, useContext, useState } from "react";
|
||||
import { useMutation, UseMutationResult } from "@tanstack/react-query";
|
||||
import { AxiosError } from "axios";
|
||||
import React, { createContext, FC, useContext } from "react";
|
||||
import { useBoardsContext } from "@/app/deals/contexts/BoardsContext";
|
||||
import useStatusesList from "@/hooks/useStatusesList";
|
||||
import { useStatusesOperations } from "@/hooks/useStatusesOperations";
|
||||
import {
|
||||
HttpValidationError,
|
||||
Options,
|
||||
StatusSchema,
|
||||
UpdateStatusData,
|
||||
UpdateStatusResponse,
|
||||
UpdateStatusSchema,
|
||||
} from "@/lib/client";
|
||||
import { updateStatusMutation } from "@/lib/client/@tanstack/react-query.gen";
|
||||
import { notifications } from "@/lib/notifications";
|
||||
import { StatusSchema, UpdateStatusSchema } from "@/lib/client";
|
||||
|
||||
type StatusesContextState = {
|
||||
statuses: StatusSchema[];
|
||||
setStatuses: React.Dispatch<React.SetStateAction<StatusSchema[]>>;
|
||||
updateStatus: UseMutationResult<
|
||||
UpdateStatusResponse,
|
||||
AxiosError<HttpValidationError>,
|
||||
Options<UpdateStatusData>
|
||||
>;
|
||||
refetchStatuses: () => void;
|
||||
onCreateStatus: (name: string) => void;
|
||||
onUpdateStatus: (statusId: number, status: UpdateStatusSchema) => void;
|
||||
onDeleteStatus: (status: StatusSchema) => void;
|
||||
isEditorDrawerOpened: boolean;
|
||||
setIsEditorDrawerOpened: React.Dispatch<React.SetStateAction<boolean>>;
|
||||
};
|
||||
|
||||
const StatusesContext = createContext<StatusesContextState | undefined>(
|
||||
@ -46,19 +28,6 @@ const useStatusesContextState = () => {
|
||||
} = useStatusesList({
|
||||
boardId: selectedBoard?.id,
|
||||
});
|
||||
const [isEditorDrawerOpened, setIsEditorDrawerOpened] =
|
||||
useState<boolean>(false);
|
||||
|
||||
const updateStatus = useMutation({
|
||||
...updateStatusMutation(),
|
||||
onError: error => {
|
||||
console.error(error);
|
||||
notifications.error({
|
||||
message: error.response?.data?.detail as string | undefined,
|
||||
});
|
||||
refetchStatuses();
|
||||
},
|
||||
});
|
||||
|
||||
const { onCreateStatus, onUpdateStatus, onDeleteStatus } =
|
||||
useStatusesOperations({
|
||||
@ -71,13 +40,10 @@ const useStatusesContextState = () => {
|
||||
return {
|
||||
statuses,
|
||||
setStatuses,
|
||||
updateStatus,
|
||||
refetchStatuses,
|
||||
onCreateStatus,
|
||||
onUpdateStatus,
|
||||
onDeleteStatus,
|
||||
isEditorDrawerOpened,
|
||||
setIsEditorDrawerOpened,
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@ -1,47 +1,21 @@
|
||||
"use client";
|
||||
|
||||
import React, { FC, ReactNode } from "react";
|
||||
import { IconChevronLeft, IconGripVertical } from "@tabler/icons-react";
|
||||
import { Box, Center, Divider, Drawer, Group, rem, Text } from "@mantine/core";
|
||||
import { useBoardsContext } from "@/app/deals/contexts/BoardsContext";
|
||||
import { useStatusesContext } from "@/app/deals/contexts/StatusesContext";
|
||||
import CreateStatusButton from "@/app/deals/drawers/BoardStatusesEditorDrawer/components/CreateStatusButton";
|
||||
import StatusMobile from "@/app/deals/drawers/BoardStatusesEditorDrawer/components/StatusMobile";
|
||||
import SortableDnd from "@/components/dnd/SortableDnd";
|
||||
import { StatusSchema } from "@/lib/client";
|
||||
import React, { FC } from "react";
|
||||
import { Drawer, rem } from "@mantine/core";
|
||||
import StatusesDrawerBody from "@/app/deals/drawers/BoardStatusesEditorDrawer/components/StatusesDrawerBody";
|
||||
import { BoardStatusesContextProvider } from "@/app/deals/drawers/BoardStatusesEditorDrawer/contexts/BoardStatusesContext";
|
||||
import { DrawerProps } from "@/drawers/types";
|
||||
import { BoardSchema } from "@/lib/client";
|
||||
|
||||
const BoardStatusesEditorDrawer: FC = () => {
|
||||
const {
|
||||
statuses,
|
||||
onUpdateStatus,
|
||||
isEditorDrawerOpened,
|
||||
setIsEditorDrawerOpened,
|
||||
} = useStatusesContext();
|
||||
const { selectedBoard } = useBoardsContext();
|
||||
const onClose = () => setIsEditorDrawerOpened(false);
|
||||
|
||||
const renderDraggable = () => (
|
||||
<Box p={"xs"}>
|
||||
<IconGripVertical />
|
||||
</Box>
|
||||
);
|
||||
|
||||
const renderStatus = (
|
||||
status: StatusSchema,
|
||||
renderDraggable?: (item: StatusSchema) => ReactNode
|
||||
) => {
|
||||
return (
|
||||
<Group wrap={"nowrap"}>
|
||||
{renderDraggable && renderDraggable(status)}
|
||||
<StatusMobile status={status} />
|
||||
</Group>
|
||||
);
|
||||
};
|
||||
|
||||
const onDragEnd = (itemId: number, newLexorank: string) => {
|
||||
onUpdateStatus(itemId, { lexorank: newLexorank });
|
||||
};
|
||||
type Props = {
|
||||
board: BoardSchema;
|
||||
};
|
||||
|
||||
const BoardStatusesEditorDrawer: FC<DrawerProps<Props>> = ({
|
||||
opened,
|
||||
onClose,
|
||||
props: { board },
|
||||
}) => {
|
||||
return (
|
||||
<Drawer
|
||||
size={"100%"}
|
||||
@ -49,7 +23,7 @@ const BoardStatusesEditorDrawer: FC = () => {
|
||||
onClose={onClose}
|
||||
removeScrollProps={{ allowPinchZoom: true }}
|
||||
withCloseButton={false}
|
||||
opened={isEditorDrawerOpened}
|
||||
opened={opened}
|
||||
trapFocus={false}
|
||||
styles={{
|
||||
body: {
|
||||
@ -59,27 +33,9 @@ const BoardStatusesEditorDrawer: FC = () => {
|
||||
gap: rem(10),
|
||||
},
|
||||
}}>
|
||||
<Group justify={"space-between"}>
|
||||
<Box
|
||||
onClick={onClose}
|
||||
p={"xs"}>
|
||||
<IconChevronLeft />
|
||||
</Box>
|
||||
<Center>
|
||||
<Text>{selectedBoard?.name}</Text>
|
||||
</Center>
|
||||
<Box p={"lg"} />
|
||||
</Group>
|
||||
<Divider />
|
||||
<SortableDnd
|
||||
initialItems={statuses}
|
||||
onDragEnd={onDragEnd}
|
||||
renderItem={renderStatus}
|
||||
renderDraggable={renderDraggable}
|
||||
dragHandleStyle={{ width: "auto" }}
|
||||
vertical
|
||||
/>
|
||||
<CreateStatusButton />
|
||||
<BoardStatusesContextProvider board={board}>
|
||||
<StatusesDrawerBody onClose={onClose} />
|
||||
</BoardStatusesContextProvider>
|
||||
</Drawer>
|
||||
);
|
||||
};
|
||||
|
||||
@ -1,10 +1,11 @@
|
||||
import { FC } from "react";
|
||||
import { IconPlus } from "@tabler/icons-react";
|
||||
import { Box, Group, Text } from "@mantine/core";
|
||||
import { modals } from "@mantine/modals";
|
||||
import { useStatusesContext } from "@/app/deals/contexts/StatusesContext";
|
||||
import { useBoardStatusesContext } from "@/app/deals/drawers/BoardStatusesEditorDrawer/contexts/BoardStatusesContext";
|
||||
|
||||
const CreateStatusButton = () => {
|
||||
const { onCreateStatus } = useStatusesContext();
|
||||
const CreateStatusButton: FC = () => {
|
||||
const { onCreateStatus } = useBoardStatusesContext();
|
||||
|
||||
const onStartCreating = () => {
|
||||
modals.openContextModal({
|
||||
|
||||
@ -0,0 +1,32 @@
|
||||
import React, { FC } from "react";
|
||||
import { Stack } 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";
|
||||
|
||||
type Props = {
|
||||
setSelectedProjectId: (projectId: number | null) => void;
|
||||
onClose: () => void;
|
||||
};
|
||||
|
||||
const ProjectsDrawerBody: FC<Props> = ({ setSelectedProjectId, onClose }) => {
|
||||
const { projects } = useProjectsContext();
|
||||
|
||||
return (
|
||||
<>
|
||||
<Stack gap={0}>
|
||||
{projects.map((project, index) => (
|
||||
<ProjectMobile
|
||||
key={index}
|
||||
project={project}
|
||||
setSelectedProjectId={setSelectedProjectId}
|
||||
closeDrawer={onClose}
|
||||
/>
|
||||
))}
|
||||
</Stack>
|
||||
<CreateProjectButton />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default ProjectsDrawerBody;
|
||||
@ -1,16 +1,17 @@
|
||||
import React, { FC } from "react";
|
||||
import { Box, Group, Text } from "@mantine/core";
|
||||
import { modals } from "@mantine/modals";
|
||||
import BoardMenu from "@/app/deals/components/shared/BoardMenu/BoardMenu";
|
||||
import { useStatusesContext } from "@/app/deals/contexts/StatusesContext";
|
||||
import { StatusSchema } from "@/lib/client";
|
||||
import StatusMenu from "@/app/deals/components/shared/StatusMenu/StatusMenu";
|
||||
import { useBoardStatusesContext } from "@/app/deals/drawers/BoardStatusesEditorDrawer/contexts/BoardStatusesContext";
|
||||
import { BoardSchema, StatusSchema } from "@/lib/client";
|
||||
|
||||
type Props = {
|
||||
status: StatusSchema;
|
||||
board: BoardSchema;
|
||||
};
|
||||
|
||||
const StatusMobile: FC<Props> = ({ status }) => {
|
||||
const { onUpdateStatus } = useStatusesContext();
|
||||
const StatusMobile: FC<Props> = ({ status, board }) => {
|
||||
const { onUpdateStatus, onDeleteStatus } = useBoardStatusesContext();
|
||||
|
||||
const startEditing = () => {
|
||||
modals.openContextModal({
|
||||
@ -34,9 +35,12 @@ const StatusMobile: FC<Props> = ({ status }) => {
|
||||
<Box>
|
||||
<Text>{status.name}</Text>
|
||||
</Box>
|
||||
<BoardMenu
|
||||
board={status}
|
||||
startEditing={startEditing}
|
||||
<StatusMenu
|
||||
status={status}
|
||||
board={board}
|
||||
onDeleteStatus={onDeleteStatus}
|
||||
handleEdit={startEditing}
|
||||
withChangeOrderButton={false}
|
||||
/>
|
||||
</Group>
|
||||
);
|
||||
|
||||
@ -0,0 +1,68 @@
|
||||
import React, { FC, ReactNode } from "react";
|
||||
import { IconChevronLeft, IconGripVertical } from "@tabler/icons-react";
|
||||
import { Box, Center, Divider, Group, Text } from "@mantine/core";
|
||||
import CreateStatusButton from "@/app/deals/drawers/BoardStatusesEditorDrawer/components/CreateStatusButton";
|
||||
import StatusMobile from "@/app/deals/drawers/BoardStatusesEditorDrawer/components/StatusMobile";
|
||||
import { useBoardStatusesContext } from "@/app/deals/drawers/BoardStatusesEditorDrawer/contexts/BoardStatusesContext";
|
||||
import SortableDnd from "@/components/dnd/SortableDnd";
|
||||
import { StatusSchema } from "@/lib/client";
|
||||
|
||||
type Props = {
|
||||
onClose: () => void;
|
||||
};
|
||||
|
||||
const StatusesDrawerBody: FC<Props> = ({ onClose }) => {
|
||||
const { onUpdateStatus, board, statuses } = useBoardStatusesContext();
|
||||
|
||||
const renderDraggable = () => (
|
||||
<Box p={"xs"}>
|
||||
<IconGripVertical />
|
||||
</Box>
|
||||
);
|
||||
|
||||
const renderStatus = (
|
||||
status: StatusSchema,
|
||||
renderDraggable?: (item: StatusSchema) => ReactNode
|
||||
) => {
|
||||
return (
|
||||
<Group wrap={"nowrap"}>
|
||||
{renderDraggable && renderDraggable(status)}
|
||||
<StatusMobile
|
||||
status={status}
|
||||
board={board}
|
||||
/>
|
||||
</Group>
|
||||
);
|
||||
};
|
||||
|
||||
const onDragEnd = (itemId: number, newLexorank: string) =>
|
||||
onUpdateStatus(itemId, { lexorank: newLexorank });
|
||||
|
||||
return (
|
||||
<>
|
||||
<Group justify={"space-between"}>
|
||||
<Box
|
||||
onClick={onClose}
|
||||
p={"xs"}>
|
||||
<IconChevronLeft />
|
||||
</Box>
|
||||
<Center>
|
||||
<Text>{board.name}</Text>
|
||||
</Center>
|
||||
<Box p={"lg"} />
|
||||
</Group>
|
||||
<Divider />
|
||||
<SortableDnd
|
||||
initialItems={statuses}
|
||||
onDragEnd={onDragEnd}
|
||||
renderItem={renderStatus}
|
||||
renderDraggable={renderDraggable}
|
||||
dragHandleStyle={{ width: "auto" }}
|
||||
vertical
|
||||
/>
|
||||
<CreateStatusButton />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default StatusesDrawerBody;
|
||||
@ -0,0 +1,78 @@
|
||||
"use client";
|
||||
|
||||
import React, { createContext, FC, useContext } from "react";
|
||||
import useStatusesList from "@/hooks/useStatusesList";
|
||||
import { useStatusesOperations } from "@/hooks/useStatusesOperations";
|
||||
import { BoardSchema, StatusSchema, UpdateStatusSchema } from "@/lib/client";
|
||||
|
||||
type BoardStatusesContextState = {
|
||||
board: BoardSchema;
|
||||
statuses: StatusSchema[];
|
||||
setStatuses: React.Dispatch<React.SetStateAction<StatusSchema[]>>;
|
||||
refetchStatuses: () => void;
|
||||
onCreateStatus: (name: string) => void;
|
||||
onUpdateStatus: (statusId: number, status: UpdateStatusSchema) => void;
|
||||
onDeleteStatus: (status: StatusSchema) => void;
|
||||
};
|
||||
|
||||
const BoardStatusesContext = createContext<
|
||||
BoardStatusesContextState | undefined
|
||||
>(undefined);
|
||||
|
||||
type Props = {
|
||||
board: BoardSchema;
|
||||
};
|
||||
|
||||
const useBoardStatusesContextState = ({ board }: Props) => {
|
||||
const {
|
||||
statuses,
|
||||
setStatuses,
|
||||
refetch: refetchStatuses,
|
||||
} = useStatusesList({
|
||||
boardId: board.id,
|
||||
});
|
||||
|
||||
const { onCreateStatus, onUpdateStatus, onDeleteStatus } =
|
||||
useStatusesOperations({
|
||||
statuses,
|
||||
setStatuses,
|
||||
refetchStatuses,
|
||||
boardId: board.id,
|
||||
});
|
||||
|
||||
return {
|
||||
board,
|
||||
statuses,
|
||||
setStatuses,
|
||||
refetchStatuses,
|
||||
onCreateStatus,
|
||||
onUpdateStatus,
|
||||
onDeleteStatus,
|
||||
};
|
||||
};
|
||||
|
||||
type BoardStatusesContextProviderProps = {
|
||||
children: React.ReactNode;
|
||||
} & Props;
|
||||
|
||||
export const BoardStatusesContextProvider: FC<
|
||||
BoardStatusesContextProviderProps
|
||||
> = ({ children, ...props }) => {
|
||||
const state = useBoardStatusesContextState(props);
|
||||
|
||||
return (
|
||||
<BoardStatusesContext.Provider value={state}>
|
||||
{children}
|
||||
</BoardStatusesContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
export const useBoardStatusesContext = () => {
|
||||
const context = useContext(BoardStatusesContext);
|
||||
if (!context) {
|
||||
throw new Error(
|
||||
"useBoardStatusesContext must be used within a BoardStatusesContextProvider"
|
||||
);
|
||||
}
|
||||
return context;
|
||||
};
|
||||
@ -1,47 +1,21 @@
|
||||
"use client";
|
||||
|
||||
import React, { FC, ReactNode } from "react";
|
||||
import { IconChevronLeft, IconGripVertical } from "@tabler/icons-react";
|
||||
import { Box, Center, Divider, Drawer, Group, rem, Text } from "@mantine/core";
|
||||
import { useBoardsContext } from "@/app/deals/contexts/BoardsContext";
|
||||
import { useProjectsContext } from "@/app/deals/contexts/ProjectsContext";
|
||||
import BoardMobile from "@/app/deals/drawers/ProjectBoardsEditorDrawer/components/BoardMobile";
|
||||
import CreateBoardButton from "@/app/deals/drawers/ProjectBoardsEditorDrawer/components/CreateBoardButton";
|
||||
import SortableDnd from "@/components/dnd/SortableDnd";
|
||||
import { BoardSchema } from "@/lib/client";
|
||||
import React, { FC } from "react";
|
||||
import { Drawer, rem } from "@mantine/core";
|
||||
import BoardsDrawerBody from "@/app/deals/drawers/ProjectBoardsEditorDrawer/components/BoardsDrawerBody";
|
||||
import { ProjectBoardsContextProvider } from "@/app/deals/drawers/ProjectBoardsEditorDrawer/contexts/ProjectBoardsContext";
|
||||
import { DrawerProps } from "@/drawers/types";
|
||||
import { ProjectSchema } from "@/lib/client";
|
||||
|
||||
const ProjectBoardsEditorDrawer: FC = () => {
|
||||
const {
|
||||
boards,
|
||||
onUpdateBoard,
|
||||
isEditorDrawerOpened,
|
||||
setIsEditorDrawerOpened,
|
||||
} = useBoardsContext();
|
||||
const { selectedProject } = useProjectsContext();
|
||||
const onClose = () => setIsEditorDrawerOpened(false);
|
||||
|
||||
const renderDraggable = () => (
|
||||
<Box p={"xs"}>
|
||||
<IconGripVertical />
|
||||
</Box>
|
||||
);
|
||||
|
||||
const renderBoard = (
|
||||
board: BoardSchema,
|
||||
renderDraggable?: (item: BoardSchema) => ReactNode
|
||||
) => {
|
||||
return (
|
||||
<Group wrap={"nowrap"}>
|
||||
{renderDraggable && renderDraggable(board)}
|
||||
<BoardMobile board={board} />
|
||||
</Group>
|
||||
);
|
||||
};
|
||||
|
||||
const onDragEnd = (itemId: number, newLexorank: string) => {
|
||||
onUpdateBoard(itemId, { lexorank: newLexorank });
|
||||
};
|
||||
type Props = {
|
||||
project: ProjectSchema;
|
||||
};
|
||||
|
||||
const ProjectBoardsEditorDrawer: FC<DrawerProps<Props>> = ({
|
||||
onClose,
|
||||
opened,
|
||||
props: { project },
|
||||
}) => {
|
||||
return (
|
||||
<Drawer
|
||||
size={"100%"}
|
||||
@ -49,7 +23,7 @@ const ProjectBoardsEditorDrawer: FC = () => {
|
||||
onClose={onClose}
|
||||
removeScrollProps={{ allowPinchZoom: true }}
|
||||
withCloseButton={false}
|
||||
opened={isEditorDrawerOpened}
|
||||
opened={opened}
|
||||
trapFocus={false}
|
||||
styles={{
|
||||
body: {
|
||||
@ -59,27 +33,9 @@ const ProjectBoardsEditorDrawer: FC = () => {
|
||||
gap: rem(10),
|
||||
},
|
||||
}}>
|
||||
<Group justify={"space-between"}>
|
||||
<Box
|
||||
onClick={onClose}
|
||||
p={"xs"}>
|
||||
<IconChevronLeft />
|
||||
</Box>
|
||||
<Center>
|
||||
<Text>{selectedProject?.name}</Text>
|
||||
</Center>
|
||||
<Box p={"lg"} />
|
||||
</Group>
|
||||
<Divider />
|
||||
<SortableDnd
|
||||
initialItems={boards}
|
||||
onDragEnd={onDragEnd}
|
||||
renderItem={renderBoard}
|
||||
renderDraggable={renderDraggable}
|
||||
dragHandleStyle={{ width: "auto" }}
|
||||
vertical
|
||||
/>
|
||||
<CreateBoardButton />
|
||||
<ProjectBoardsContextProvider project={project}>
|
||||
<BoardsDrawerBody onClose={onClose} />
|
||||
</ProjectBoardsContextProvider>
|
||||
</Drawer>
|
||||
);
|
||||
};
|
||||
|
||||
@ -2,16 +2,15 @@ import React, { FC } from "react";
|
||||
import { Box, Group, Text } from "@mantine/core";
|
||||
import { modals } from "@mantine/modals";
|
||||
import BoardMenu from "@/app/deals/components/shared/BoardMenu/BoardMenu";
|
||||
import { useBoardsContext } from "@/app/deals/contexts/BoardsContext";
|
||||
import { BoardSchema } from "@/lib/client";
|
||||
import { BoardSchema, UpdateBoardSchema } from "@/lib/client";
|
||||
|
||||
type Props = {
|
||||
board: BoardSchema;
|
||||
onUpdateBoard: (boardId: number, board: UpdateBoardSchema) => void;
|
||||
onDeleteBoard: (board: BoardSchema) => void;
|
||||
};
|
||||
|
||||
const BoardMobile: FC<Props> = ({ board }) => {
|
||||
const { onUpdateBoard } = useBoardsContext();
|
||||
|
||||
const BoardMobile: FC<Props> = ({ board, onUpdateBoard, onDeleteBoard }) => {
|
||||
const startEditing = () => {
|
||||
modals.openContextModal({
|
||||
modal: "enterNameModal",
|
||||
@ -37,6 +36,7 @@ const BoardMobile: FC<Props> = ({ board }) => {
|
||||
<BoardMenu
|
||||
board={board}
|
||||
startEditing={startEditing}
|
||||
onDeleteBoard={onDeleteBoard}
|
||||
/>
|
||||
</Group>
|
||||
);
|
||||
|
||||
@ -0,0 +1,71 @@
|
||||
import React, { FC, ReactNode } from "react";
|
||||
import { IconChevronLeft, IconGripVertical } from "@tabler/icons-react";
|
||||
import { Box, Center, Divider, Group, Text } from "@mantine/core";
|
||||
import BoardMobile from "@/app/deals/drawers/ProjectBoardsEditorDrawer/components/BoardMobile";
|
||||
import CreateBoardButton from "@/app/deals/drawers/ProjectBoardsEditorDrawer/components/CreateBoardButton";
|
||||
import { useProjectBoardsContext } from "@/app/deals/drawers/ProjectBoardsEditorDrawer/contexts/ProjectBoardsContext";
|
||||
import SortableDnd from "@/components/dnd/SortableDnd";
|
||||
import { BoardSchema } from "@/lib/client";
|
||||
|
||||
type Props = {
|
||||
onClose: () => void;
|
||||
};
|
||||
|
||||
const BoardsDrawerBody: FC<Props> = ({ onClose }) => {
|
||||
const { boards, onUpdateBoard, onDeleteBoard, project, onCreateBoard } =
|
||||
useProjectBoardsContext();
|
||||
|
||||
const renderDraggable = () => (
|
||||
<Box p={"xs"}>
|
||||
<IconGripVertical />
|
||||
</Box>
|
||||
);
|
||||
|
||||
const renderBoard = (
|
||||
board: BoardSchema,
|
||||
renderDraggable?: (item: BoardSchema) => ReactNode
|
||||
) => {
|
||||
return (
|
||||
<Group wrap={"nowrap"}>
|
||||
{renderDraggable && renderDraggable(board)}
|
||||
<BoardMobile
|
||||
board={board}
|
||||
onDeleteBoard={onDeleteBoard}
|
||||
onUpdateBoard={onUpdateBoard}
|
||||
/>
|
||||
</Group>
|
||||
);
|
||||
};
|
||||
|
||||
const onDragEnd = (itemId: number, newLexorank: string) => {
|
||||
onUpdateBoard(itemId, { lexorank: newLexorank });
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Group justify={"space-between"}>
|
||||
<Box
|
||||
onClick={onClose}
|
||||
p={"xs"}>
|
||||
<IconChevronLeft />
|
||||
</Box>
|
||||
<Center>
|
||||
<Text>{project.name}</Text>
|
||||
</Center>
|
||||
<Box p={"lg"} />
|
||||
</Group>
|
||||
<Divider />
|
||||
<SortableDnd
|
||||
initialItems={boards}
|
||||
onDragEnd={onDragEnd}
|
||||
renderItem={renderBoard}
|
||||
renderDraggable={renderDraggable}
|
||||
dragHandleStyle={{ width: "auto" }}
|
||||
vertical
|
||||
/>
|
||||
<CreateBoardButton onCreateBoard={onCreateBoard} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default BoardsDrawerBody;
|
||||
@ -1,11 +1,13 @@
|
||||
import { FC } from "react";
|
||||
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 CreateBoardButton = () => {
|
||||
const { onCreateBoard } = useBoardsContext();
|
||||
type Props = {
|
||||
onCreateBoard: (name: string) => void;
|
||||
};
|
||||
|
||||
const CreateBoardButton: FC<Props> = ({ onCreateBoard }) => {
|
||||
const onStartCreating = () => {
|
||||
modals.openContextModal({
|
||||
modal: "enterNameModal",
|
||||
@ -18,7 +20,9 @@ const CreateBoardButton = () => {
|
||||
};
|
||||
|
||||
return (
|
||||
<Group ml={"xs"} onClick={onStartCreating}>
|
||||
<Group
|
||||
ml={"xs"}
|
||||
onClick={onStartCreating}>
|
||||
<IconPlus />
|
||||
<Box mt={5}>
|
||||
<Text>Создать доску</Text>
|
||||
|
||||
@ -0,0 +1,77 @@
|
||||
"use client";
|
||||
|
||||
import React, { createContext, FC, useContext } from "react";
|
||||
import useBoardsList from "@/hooks/useBoardsList";
|
||||
import { useBoardsOperations } from "@/hooks/useBoardsOperations";
|
||||
import { BoardSchema, ProjectSchema, UpdateBoardSchema } from "@/lib/client";
|
||||
|
||||
type ProjectBoardsContextState = {
|
||||
boards: BoardSchema[];
|
||||
setBoards: React.Dispatch<React.SetStateAction<BoardSchema[]>>;
|
||||
project: ProjectSchema;
|
||||
refetchBoards: () => void;
|
||||
onCreateBoard: (name: string) => void;
|
||||
onUpdateBoard: (boardId: number, board: UpdateBoardSchema) => void;
|
||||
onDeleteBoard: (board: BoardSchema) => void;
|
||||
};
|
||||
|
||||
const ProjectBoardsContext = createContext<
|
||||
ProjectBoardsContextState | undefined
|
||||
>(undefined);
|
||||
|
||||
type Props = {
|
||||
project: ProjectSchema;
|
||||
};
|
||||
|
||||
const useProjectBoardsContextState = ({ project }: Props) => {
|
||||
const {
|
||||
boards,
|
||||
setBoards,
|
||||
refetch: refetchBoards,
|
||||
} = useBoardsList({ projectId: project?.id });
|
||||
|
||||
const { onCreateBoard, onUpdateBoard, onDeleteBoard } = useBoardsOperations(
|
||||
{
|
||||
boards,
|
||||
setBoards,
|
||||
refetchBoards,
|
||||
projectId: project?.id,
|
||||
}
|
||||
);
|
||||
|
||||
return {
|
||||
boards,
|
||||
setBoards,
|
||||
project,
|
||||
refetchBoards,
|
||||
onCreateBoard,
|
||||
onUpdateBoard,
|
||||
onDeleteBoard,
|
||||
};
|
||||
};
|
||||
|
||||
type ProjectBoardsContextProviderProps = {
|
||||
children: React.ReactNode;
|
||||
} & Props;
|
||||
|
||||
export const ProjectBoardsContextProvider: FC<
|
||||
ProjectBoardsContextProviderProps
|
||||
> = ({ children, ...props }) => {
|
||||
const state = useProjectBoardsContextState(props);
|
||||
|
||||
return (
|
||||
<ProjectBoardsContext.Provider value={state}>
|
||||
{children}
|
||||
</ProjectBoardsContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
export const useProjectBoardsContext = () => {
|
||||
const context = useContext(ProjectBoardsContext);
|
||||
if (!context) {
|
||||
throw new Error(
|
||||
"useProjectBoardsContext must be used within a ProjectBoardsContextProvider"
|
||||
);
|
||||
}
|
||||
return context;
|
||||
};
|
||||
@ -1,16 +1,20 @@
|
||||
"use client";
|
||||
|
||||
import React, { FC } from "react";
|
||||
import { Center, Divider, Drawer, 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";
|
||||
import { Center, Divider, Drawer, rem, Text } from "@mantine/core";
|
||||
import { ProjectsContextProvider } from "@/app/deals/contexts/ProjectsContext";
|
||||
import ProjectsDrawerBody from "@/app/deals/drawers/BoardStatusesEditorDrawer/components/ProjectsDrawerBody";
|
||||
import { DrawerProps } from "@/drawers/types";
|
||||
|
||||
const ProjectsEditorDrawer: FC = () => {
|
||||
const { projects, isEditorDrawerOpened, setIsEditorDrawerOpened } =
|
||||
useProjectsContext();
|
||||
const onClose = () => setIsEditorDrawerOpened(false);
|
||||
type Props = {
|
||||
setSelectedProjectId: (projectId: number | null) => void;
|
||||
};
|
||||
|
||||
const ProjectsEditorDrawer: FC<DrawerProps<Props>> = ({
|
||||
opened,
|
||||
onClose,
|
||||
props: { setSelectedProjectId },
|
||||
}) => {
|
||||
return (
|
||||
<Drawer
|
||||
size={"100%"}
|
||||
@ -18,7 +22,7 @@ const ProjectsEditorDrawer: FC = () => {
|
||||
onClose={onClose}
|
||||
removeScrollProps={{ allowPinchZoom: true }}
|
||||
withCloseButton={false}
|
||||
opened={isEditorDrawerOpened}
|
||||
opened={opened}
|
||||
trapFocus={false}
|
||||
styles={{
|
||||
body: {
|
||||
@ -32,15 +36,12 @@ const ProjectsEditorDrawer: FC = () => {
|
||||
<Text>Проекты</Text>
|
||||
</Center>
|
||||
<Divider />
|
||||
<Stack gap={0}>
|
||||
{projects.map((project, index) => (
|
||||
<ProjectMobile
|
||||
key={index}
|
||||
project={project}
|
||||
<ProjectsContextProvider>
|
||||
<ProjectsDrawerBody
|
||||
setSelectedProjectId={setSelectedProjectId}
|
||||
onClose={onClose}
|
||||
/>
|
||||
))}
|
||||
</Stack>
|
||||
<CreateProjectButton />
|
||||
</ProjectsContextProvider>
|
||||
</Drawer>
|
||||
);
|
||||
};
|
||||
|
||||
@ -1,9 +1,10 @@
|
||||
import { FC } from "react";
|
||||
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 CreateProjectButton: FC = () => {
|
||||
const { onCreateProject } = useProjectsContext();
|
||||
|
||||
const onStartCreating = () => {
|
||||
|
||||
@ -8,11 +8,16 @@ import styles from "./../ProjectsEditorDrawer.module.css";
|
||||
|
||||
type Props = {
|
||||
project: ProjectSchema;
|
||||
setSelectedProjectId: (projectId: number | null) => void;
|
||||
closeDrawer: () => void;
|
||||
};
|
||||
|
||||
const ProjectMobile: FC<Props> = ({ project }) => {
|
||||
const { onUpdateProject, setSelectedProjectId, setIsEditorDrawerOpened } =
|
||||
useProjectsContext();
|
||||
const ProjectMobile: FC<Props> = ({
|
||||
project,
|
||||
setSelectedProjectId,
|
||||
closeDrawer,
|
||||
}) => {
|
||||
const { onUpdateProject } = useProjectsContext();
|
||||
|
||||
const startEditing = () => {
|
||||
modals.openContextModal({
|
||||
@ -28,7 +33,7 @@ const ProjectMobile: FC<Props> = ({ project }) => {
|
||||
|
||||
const onClick = () => {
|
||||
setSelectedProjectId(project.id);
|
||||
setIsEditorDrawerOpened(false);
|
||||
closeDrawer();
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@ -24,7 +24,7 @@ const useDealsAndStatusesDnd = (): ReturnType => {
|
||||
const swiperRef = useRef<SwiperRef>(null);
|
||||
const [activeDeal, setActiveDeal] = useState<DealSchema | null>(null);
|
||||
const [activeStatus, setActiveStatus] = useState<StatusSchema | null>(null);
|
||||
const { statuses, setStatuses, updateStatus } = useStatusesContext();
|
||||
const { statuses, setStatuses, onUpdateStatus } = useStatusesContext();
|
||||
const { deals, setDeals, updateDeal } = useDealsContext();
|
||||
const sortedStatuses = useMemo(() => sortByLexorank(statuses), [statuses]);
|
||||
const isMobile = useIsMobile();
|
||||
@ -229,16 +229,7 @@ const useDealsAndStatusesDnd = (): ReturnType => {
|
||||
};
|
||||
|
||||
const onStatusDragEnd = (statusId: number, lexorank: string) => {
|
||||
updateStatus.mutate({
|
||||
path: {
|
||||
statusId,
|
||||
},
|
||||
body: {
|
||||
status: {
|
||||
lexorank,
|
||||
},
|
||||
},
|
||||
});
|
||||
onUpdateStatus(statusId, { lexorank });
|
||||
};
|
||||
|
||||
const handleDealDragEnd = (activeId: number | string, over: Over) => {
|
||||
|
||||
@ -1,34 +1,28 @@
|
||||
import { Space } from "@mantine/core";
|
||||
import Funnel from "@/app/deals/components/shared/Funnel/Funnel";
|
||||
import Header from "@/app/deals/components/shared/Header/Header";
|
||||
import { BoardsContextProvider } from "@/app/deals/contexts/BoardsContext";
|
||||
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";
|
||||
import { Space } from "@mantine/core";
|
||||
|
||||
export default function DealsPage() {
|
||||
return (
|
||||
<ProjectsContextProvider>
|
||||
<BoardsContextProvider>
|
||||
<StatusesContextProvider>
|
||||
<PageContainer>
|
||||
<PageBlock className={"mobile-margin-height"}>
|
||||
<Header />
|
||||
<Space h={"md"}/>
|
||||
<StatusesContextProvider>
|
||||
<Space h={"md"} />
|
||||
<DealsContextProvider>
|
||||
<Funnel />
|
||||
</DealsContextProvider>
|
||||
<BoardStatusesEditorDrawer />
|
||||
</StatusesContextProvider>
|
||||
<ProjectBoardsEditorDrawer />
|
||||
<ProjectsEditorDrawer />
|
||||
</PageBlock>
|
||||
</PageContainer>
|
||||
</StatusesContextProvider>
|
||||
</BoardsContextProvider>
|
||||
</ProjectsContextProvider>
|
||||
);
|
||||
|
||||
@ -19,6 +19,7 @@ import AppShellMainWrapper from "@/components/layout/AppShellWrappers/AppShellMa
|
||||
import AppShellNavbarWrapper from "@/components/layout/AppShellWrappers/AppShellNavbarWrapper";
|
||||
import Footer from "@/components/layout/Footer/Footer";
|
||||
import Navbar from "@/components/layout/Navbar/Navbar";
|
||||
import { DrawersContextProvider } from "@/drawers/DrawersContext";
|
||||
import { modals } from "@/modals/modals";
|
||||
import { ReactQueryProvider } from "@/providers/ReactQueryProvider";
|
||||
import ReduxProvider from "@/providers/ReduxProvider";
|
||||
@ -64,6 +65,7 @@ export default function RootLayout({ children }: Props) {
|
||||
<ModalsProvider
|
||||
labels={{ confirm: "Да", cancel: "Нет" }}
|
||||
modals={modals}>
|
||||
<DrawersContextProvider>
|
||||
<AppShell
|
||||
layout={"alt"}
|
||||
withBorder={false}
|
||||
@ -85,6 +87,7 @@ export default function RootLayout({ children }: Props) {
|
||||
<Footer />
|
||||
</AppShellFooterWrapper>
|
||||
</AppShell>
|
||||
</DrawersContextProvider>
|
||||
</ModalsProvider>
|
||||
</ReduxProvider>
|
||||
<Notifications position="bottom-right" />
|
||||
|
||||
101
src/drawers/DrawersContext.tsx
Normal file
101
src/drawers/DrawersContext.tsx
Normal file
@ -0,0 +1,101 @@
|
||||
"use client";
|
||||
|
||||
import React, { createContext, FC, useContext, useState } from "react";
|
||||
import drawerRegistry from "@/drawers/drawersRegistry";
|
||||
import {
|
||||
DrawerKey,
|
||||
DrawersStackState,
|
||||
OpenDrawerParams,
|
||||
} from "@/drawers/types";
|
||||
|
||||
type DrawersContextState = {
|
||||
stack: DrawersStackState[];
|
||||
openDrawer: <TKey extends DrawerKey>(
|
||||
params: OpenDrawerParams<TKey>
|
||||
) => void;
|
||||
closeDrawer: (key?: DrawerKey) => void;
|
||||
};
|
||||
|
||||
const DrawersContext = createContext<DrawersContextState | undefined>(
|
||||
undefined
|
||||
);
|
||||
|
||||
const useDrawersContextState = () => {
|
||||
const [stack, setStack] = useState<DrawersStackState[]>([]);
|
||||
|
||||
const openDrawer = <TKey extends DrawerKey>(
|
||||
params: OpenDrawerParams<TKey>
|
||||
) => {
|
||||
setStack(prev => {
|
||||
const filtered = prev.filter(d => d.key !== params.key);
|
||||
return [...filtered, { ...params, opened: false }];
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
setStack(prev =>
|
||||
prev.map(d =>
|
||||
d.key === params.key ? { ...d, opened: true } : d
|
||||
)
|
||||
);
|
||||
}, 10);
|
||||
};
|
||||
|
||||
const closeDrawer = (key?: DrawerKey) => {
|
||||
setStack(prev =>
|
||||
prev.map(d => (d.key === key ? { ...d, opened: false } : d))
|
||||
);
|
||||
|
||||
setTimeout(() => {
|
||||
setStack(prev =>
|
||||
key ? prev.filter(d => d.key !== key) : prev.slice(0, -1)
|
||||
);
|
||||
}, 1000);
|
||||
};
|
||||
|
||||
return {
|
||||
stack,
|
||||
openDrawer,
|
||||
closeDrawer,
|
||||
};
|
||||
};
|
||||
|
||||
type BoardsContextProviderProps = {
|
||||
children: React.ReactNode;
|
||||
};
|
||||
|
||||
export const DrawersContextProvider: FC<BoardsContextProviderProps> = ({
|
||||
children,
|
||||
}) => {
|
||||
const state = useDrawersContextState();
|
||||
|
||||
return (
|
||||
<DrawersContext.Provider value={state}>
|
||||
{children}
|
||||
|
||||
{state.stack.map(({ key, props, onClose, opened }) => {
|
||||
const Component = drawerRegistry[key];
|
||||
return (
|
||||
<Component
|
||||
key={key}
|
||||
opened={opened || false}
|
||||
onClose={() => {
|
||||
state.closeDrawer(key);
|
||||
onClose && onClose();
|
||||
}}
|
||||
props={props}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</DrawersContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
export const useDrawersContext = () => {
|
||||
const context = useContext(DrawersContext);
|
||||
if (!context) {
|
||||
throw new Error(
|
||||
"useDrawersContext must be used within a DrawersContextProvider"
|
||||
);
|
||||
}
|
||||
return context;
|
||||
};
|
||||
11
src/drawers/drawersRegistry.tsx
Normal file
11
src/drawers/drawersRegistry.tsx
Normal file
@ -0,0 +1,11 @@
|
||||
import BoardStatusesEditorDrawer from "@/app/deals/drawers/BoardStatusesEditorDrawer";
|
||||
import ProjectsEditorDrawer from "@/app/deals/drawers/ProjectsEditorDrawer";
|
||||
import ProjectBoardsEditorDrawer from "@/app/deals/drawers/ProjectBoardsEditorDrawer";
|
||||
|
||||
const drawerRegistry = {
|
||||
projectsEditorDrawer: ProjectsEditorDrawer,
|
||||
boardStatusesEditorDrawer: BoardStatusesEditorDrawer,
|
||||
projectBoardsEditorDrawer: ProjectBoardsEditorDrawer,
|
||||
};
|
||||
|
||||
export default drawerRegistry;
|
||||
24
src/drawers/types.ts
Normal file
24
src/drawers/types.ts
Normal file
@ -0,0 +1,24 @@
|
||||
import drawerRegistry from "./drawersRegistry";
|
||||
|
||||
export type DrawerRegistry = typeof drawerRegistry;
|
||||
|
||||
export type DrawerKey = keyof DrawerRegistry;
|
||||
|
||||
export type DrawerProps<T = unknown> = {
|
||||
opened: boolean;
|
||||
onClose: () => void;
|
||||
props: T;
|
||||
};
|
||||
|
||||
export type DrawersStackState = {
|
||||
key: DrawerKey;
|
||||
props?: any;
|
||||
onClose?: () => void;
|
||||
opened?: boolean;
|
||||
};
|
||||
|
||||
export type OpenDrawerParams<TKey extends DrawerKey> = {
|
||||
key: TKey;
|
||||
props?: Parameters<DrawerRegistry[TKey]>[0]["props"];
|
||||
onClose?: () => void;
|
||||
};
|
||||
@ -4,19 +4,24 @@ import { ProjectSchema } from "@/lib/client";
|
||||
import { getProjectsOptions } from "@/lib/client/@tanstack/react-query.gen";
|
||||
|
||||
const useProjectsList = () => {
|
||||
const [projects, setProjects] = useState<ProjectSchema[]>([]);
|
||||
|
||||
const { data, refetch, isLoading } = useQuery({
|
||||
const { refetch, data, isLoading } = useQuery({
|
||||
...getProjectsOptions(),
|
||||
});
|
||||
|
||||
const [projects, setProjects] = useState<ProjectSchema[]>([]);
|
||||
|
||||
const refetchProjects = async () => {
|
||||
const res = await refetch();
|
||||
setProjects(res.data?.projects ?? []);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (data?.projects) {
|
||||
setProjects(data.projects);
|
||||
}
|
||||
}, [data?.projects]);
|
||||
|
||||
return { projects, setProjects, refetch, isLoading };
|
||||
return { projects, setProjects, refetch: refetchProjects, isLoading };
|
||||
};
|
||||
|
||||
export default useProjectsList;
|
||||
|
||||
Reference in New Issue
Block a user