fix: projects editor to selected project editor, moved attributes editor
This commit is contained in:
@@ -308,6 +308,7 @@ export type { ProductUploadImageResponse } from './models/ProductUploadImageResp
|
|||||||
export type { ProfitChartDataItem } from './models/ProfitChartDataItem';
|
export type { ProfitChartDataItem } from './models/ProfitChartDataItem';
|
||||||
export type { ProfitTableDataItem } from './models/ProfitTableDataItem';
|
export type { ProfitTableDataItem } from './models/ProfitTableDataItem';
|
||||||
export type { ProfitTableGroupBy } from './models/ProfitTableGroupBy';
|
export type { ProfitTableGroupBy } from './models/ProfitTableGroupBy';
|
||||||
|
export type { ProjectGeneralInfoSchema } from './models/ProjectGeneralInfoSchema';
|
||||||
export type { ProjectSchema } from './models/ProjectSchema';
|
export type { ProjectSchema } from './models/ProjectSchema';
|
||||||
export type { ReceiptBoxSchema } from './models/ReceiptBoxSchema';
|
export type { ReceiptBoxSchema } from './models/ReceiptBoxSchema';
|
||||||
export type { ReceiptPalletSchema } from './models/ReceiptPalletSchema';
|
export type { ReceiptPalletSchema } from './models/ReceiptPalletSchema';
|
||||||
|
|||||||
9
src/client/models/ProjectGeneralInfoSchema.ts
Normal file
9
src/client/models/ProjectGeneralInfoSchema.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
/* generated using openapi-typescript-codegen -- do not edit */
|
||||||
|
/* istanbul ignore file */
|
||||||
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
export type ProjectGeneralInfoSchema = {
|
||||||
|
name: string;
|
||||||
|
id: number;
|
||||||
|
};
|
||||||
|
|
||||||
@@ -2,8 +2,8 @@
|
|||||||
/* istanbul ignore file */
|
/* istanbul ignore file */
|
||||||
/* tslint:disable */
|
/* tslint:disable */
|
||||||
/* eslint-disable */
|
/* eslint-disable */
|
||||||
import type { ProjectSchema } from './ProjectSchema';
|
import type { ProjectGeneralInfoSchema } from './ProjectGeneralInfoSchema';
|
||||||
export type UpdateProjectRequest = {
|
export type UpdateProjectRequest = {
|
||||||
project: ProjectSchema;
|
project: ProjectGeneralInfoSchema;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { useEffect, useState } from "react";
|
|||||||
import { BoardSchema, BoardService } from "../../../../../client";
|
import { BoardSchema, BoardService } 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 { useCardPageContext } from "../../../../../pages/CardsPage/contexts/CardPageContext.tsx";
|
import { useProjectsContext } from "../../../../../contexts/ProjectsContext.tsx";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
boards: BoardSchema[];
|
boards: BoardSchema[];
|
||||||
@@ -11,7 +11,7 @@ type Props = {
|
|||||||
|
|
||||||
const useBoards = ({ boards, refetchBoards }: Props) => {
|
const useBoards = ({ boards, refetchBoards }: Props) => {
|
||||||
const [selectedBoard, setSelectedBoard] = useState<BoardSchema | null>(null);
|
const [selectedBoard, setSelectedBoard] = useState<BoardSchema | null>(null);
|
||||||
const { selectedProject: project } = useCardPageContext();
|
const { selectedProject: project } = useProjectsContext();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (boards.length > 0 && selectedBoard === null) {
|
if (boards.length > 0 && selectedBoard === null) {
|
||||||
@@ -24,7 +24,7 @@ const useBoards = ({ boards, refetchBoards }: Props) => {
|
|||||||
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) {
|
||||||
newBoard = boards[0]
|
newBoard = boards[0];
|
||||||
}
|
}
|
||||||
setSelectedBoard(newBoard ?? null);
|
setSelectedBoard(newBoard ?? null);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import { IconCheck, IconLayoutGridRemove, IconTrash } from "@tabler/icons-react"
|
|||||||
import { useContextMenu } from "mantine-contextmenu";
|
import { useContextMenu } from "mantine-contextmenu";
|
||||||
import useCardSummaryState from "./useCardSummaryState.tsx";
|
import useCardSummaryState from "./useCardSummaryState.tsx";
|
||||||
import isModuleInProject, { Modules } from "../../../../pages/CardsPage/utils/isModuleInProject.ts";
|
import isModuleInProject, { Modules } from "../../../../pages/CardsPage/utils/isModuleInProject.ts";
|
||||||
|
import { useProjectsContext } from "../../../../contexts/ProjectsContext.tsx";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
cardSummary: CardSummary;
|
cardSummary: CardSummary;
|
||||||
@@ -18,7 +19,8 @@ type Props = {
|
|||||||
|
|
||||||
const CardSummaryItem: FC<Props> = ({ cardSummary, color }) => {
|
const CardSummaryItem: FC<Props> = ({ cardSummary, color }) => {
|
||||||
const { showContextMenu } = useContextMenu();
|
const { showContextMenu } = useContextMenu();
|
||||||
const { selectedProject, setSelectedCard } = useCardPageContext();
|
const { selectedProject } = useProjectsContext();
|
||||||
|
const { setSelectedCard } = useCardPageContext();
|
||||||
const { onDelete, onComplete, onDeleteFromGroup } = useCardSummaryState();
|
const { onDelete, onComplete, onDeleteFromGroup } = useCardSummaryState();
|
||||||
|
|
||||||
const isServicesAndProductsIncluded = isModuleInProject(Modules.SERVICES_AND_PRODUCTS, selectedProject);
|
const isServicesAndProductsIncluded = isModuleInProject(Modules.SERVICES_AND_PRODUCTS, selectedProject);
|
||||||
|
|||||||
@@ -9,8 +9,8 @@ import { groupBy, has, uniq } from "lodash";
|
|||||||
import { CardGroupView } from "../CardGroupView/CardGroupView.tsx";
|
import { CardGroupView } from "../CardGroupView/CardGroupView.tsx";
|
||||||
import CreateDealsFromFileButton from "../CreateCardsFromFileButton/CreateDealsFromFileButton.tsx";
|
import CreateDealsFromFileButton from "../CreateCardsFromFileButton/CreateDealsFromFileButton.tsx";
|
||||||
import DragState from "../../../../pages/CardsPage/enums/DragState.ts";
|
import DragState from "../../../../pages/CardsPage/enums/DragState.ts";
|
||||||
import { useCardPageContext } from "../../../../pages/CardsPage/contexts/CardPageContext.tsx";
|
|
||||||
import isModuleInProject, { Modules } from "../../../../pages/CardsPage/utils/isModuleInProject.ts";
|
import isModuleInProject, { Modules } from "../../../../pages/CardsPage/utils/isModuleInProject.ts";
|
||||||
|
import { useProjectsContext } from "../../../../contexts/ProjectsContext.tsx";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
status: StatusSchema;
|
status: StatusSchema;
|
||||||
@@ -27,7 +27,7 @@ export const CardsDndColumn: FC<Props> = ({
|
|||||||
dragState,
|
dragState,
|
||||||
withCreateButton = false,
|
withCreateButton = false,
|
||||||
}) => {
|
}) => {
|
||||||
const { selectedProject } = useCardPageContext();
|
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_STATUS;
|
||||||
const droppableId = status.id.toString();
|
const droppableId = status.id.toString();
|
||||||
|
|||||||
@@ -7,8 +7,8 @@ import { CardService, StatusSchema } from "../../../../client";
|
|||||||
import { useQueryClient } from "@tanstack/react-query";
|
import { useQueryClient } from "@tanstack/react-query";
|
||||||
import { dateWithoutTimezone } from "../../../../shared/lib/date.ts";
|
import { dateWithoutTimezone } from "../../../../shared/lib/date.ts";
|
||||||
import { usePrefillCardContext } from "../../../../pages/CardsPage/contexts/PrefillCardContext.tsx";
|
import { usePrefillCardContext } from "../../../../pages/CardsPage/contexts/PrefillCardContext.tsx";
|
||||||
import { useCardPageContext } from "../../../../pages/CardsPage/contexts/CardPageContext.tsx";
|
|
||||||
import isModuleInProject, { Modules } from "../../../../pages/CardsPage/utils/isModuleInProject.ts";
|
import isModuleInProject, { Modules } from "../../../../pages/CardsPage/utils/isModuleInProject.ts";
|
||||||
|
import { useProjectsContext } from "../../../../contexts/ProjectsContext.tsx";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
status: StatusSchema;
|
status: StatusSchema;
|
||||||
@@ -20,7 +20,7 @@ const CreateCardButton = ({ status }: Props) => {
|
|||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
const { prefillCard, setPrefillCard } = usePrefillCardContext();
|
const { prefillCard, setPrefillCard } = usePrefillCardContext();
|
||||||
|
|
||||||
const { selectedProject } = useCardPageContext();
|
const { selectedProject } = useProjectsContext();
|
||||||
const isPrefillingDealEnabled = isModuleInProject(Modules.SERVICES_AND_PRODUCTS, selectedProject);
|
const isPrefillingDealEnabled = isModuleInProject(Modules.SERVICES_AND_PRODUCTS, selectedProject);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -9,8 +9,8 @@ import ShippingWarehouseAutocomplete
|
|||||||
from "../../../Selects/ShippingWarehouseAutocomplete/ShippingWarehouseAutocomplete.tsx";
|
from "../../../Selects/ShippingWarehouseAutocomplete/ShippingWarehouseAutocomplete.tsx";
|
||||||
import BaseMarketplaceSelect from "../../../Selects/BaseMarketplaceSelect/BaseMarketplaceSelect.tsx";
|
import BaseMarketplaceSelect from "../../../Selects/BaseMarketplaceSelect/BaseMarketplaceSelect.tsx";
|
||||||
import { usePrefillCardContext } from "../../../../pages/CardsPage/contexts/PrefillCardContext.tsx";
|
import { usePrefillCardContext } from "../../../../pages/CardsPage/contexts/PrefillCardContext.tsx";
|
||||||
import { useCardPageContext } from "../../../../pages/CardsPage/contexts/CardPageContext.tsx";
|
|
||||||
import isModuleInProject, { Modules } from "../../../../pages/CardsPage/utils/isModuleInProject.ts";
|
import isModuleInProject, { Modules } from "../../../../pages/CardsPage/utils/isModuleInProject.ts";
|
||||||
|
import { useProjectsContext } from "../../../../contexts/ProjectsContext.tsx";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
onSubmit: (quickDeal: QuickCard) => void;
|
onSubmit: (quickDeal: QuickCard) => void;
|
||||||
@@ -18,7 +18,7 @@ type Props = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const CreateCardForm: FC<Props> = ({ onSubmit, onCancel }) => {
|
const CreateCardForm: FC<Props> = ({ onSubmit, onCancel }) => {
|
||||||
const { selectedProject } = useCardPageContext();
|
const { selectedProject } = useProjectsContext();
|
||||||
const isPrefillingEnabled = isModuleInProject(Modules.SERVICES_AND_PRODUCTS, selectedProject);
|
const isPrefillingEnabled = isModuleInProject(Modules.SERVICES_AND_PRODUCTS, selectedProject);
|
||||||
const { prefillOnOpen, prefillCard } = usePrefillCardContext();
|
const { prefillOnOpen, prefillCard } = usePrefillCardContext();
|
||||||
|
|
||||||
|
|||||||
68
src/contexts/ProjectsContext.tsx
Normal file
68
src/contexts/ProjectsContext.tsx
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
import React, { createContext, FC, useContext, useEffect, useState } from "react";
|
||||||
|
import { ProjectSchema } from "../client";
|
||||||
|
import useProjects from "../hooks/useProjects.tsx";
|
||||||
|
|
||||||
|
type ProjectsContextState = {
|
||||||
|
selectedProject: ProjectSchema | null;
|
||||||
|
setSelectedProject: React.Dispatch<React.SetStateAction<ProjectSchema | null>>;
|
||||||
|
refetchProjects: () => void;
|
||||||
|
projects: ProjectSchema[];
|
||||||
|
};
|
||||||
|
|
||||||
|
const ProjectsContext = createContext<ProjectsContextState | undefined>(
|
||||||
|
undefined,
|
||||||
|
);
|
||||||
|
|
||||||
|
const useProjectsContextState = () => {
|
||||||
|
const { projects, refetchProjects } = useProjects();
|
||||||
|
const [selectedProject, setSelectedProject] = useState<ProjectSchema | null>(null);
|
||||||
|
|
||||||
|
const refetch = () => {
|
||||||
|
refetchProjects();
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (projects.length > 0) {
|
||||||
|
if (selectedProject) {
|
||||||
|
setSelectedProject(
|
||||||
|
projects.find(project => project.id === selectedProject.id) ?? null,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
setSelectedProject(projects[0]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
setSelectedProject(null);
|
||||||
|
}
|
||||||
|
}, [projects]);
|
||||||
|
|
||||||
|
return {
|
||||||
|
projects,
|
||||||
|
refetchProjects: refetch,
|
||||||
|
selectedProject,
|
||||||
|
setSelectedProject,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
type ProjectsContextProviderProps = {
|
||||||
|
children: React.ReactNode;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const ProjectsContextProvider: FC<ProjectsContextProviderProps> = ({ children }) => {
|
||||||
|
const state = useProjectsContextState();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ProjectsContext.Provider value={state}>
|
||||||
|
{children}
|
||||||
|
</ProjectsContext.Provider>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useProjectsContext = () => {
|
||||||
|
const context = useContext(ProjectsContext);
|
||||||
|
if (!context) {
|
||||||
|
throw new Error(
|
||||||
|
"useProjectsContext must be used within a ProjectsContextProvider",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return context;
|
||||||
|
};
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { type FullProjectSchema, ProjectService } from "../../../client";
|
import { type FullProjectSchema, ProjectService } from "../client";
|
||||||
|
|
||||||
|
|
||||||
const useProjects = () => {
|
const useProjects = () => {
|
||||||
12
src/main.tsx
12
src/main.tsx
@@ -23,6 +23,7 @@ import { DatesProvider } from "@mantine/dates";
|
|||||||
import { modals } from "./modals/modals.ts";
|
import { modals } from "./modals/modals.ts";
|
||||||
import TasksProvider from "./providers/TasksProvider/TasksProvider.tsx";
|
import TasksProvider from "./providers/TasksProvider/TasksProvider.tsx";
|
||||||
import { ContextMenuProvider } from "mantine-contextmenu";
|
import { ContextMenuProvider } from "mantine-contextmenu";
|
||||||
|
import { ProjectsContextProvider } from "./contexts/ProjectsContext.tsx";
|
||||||
|
|
||||||
// Configuring router
|
// Configuring router
|
||||||
const router = createRouter({ routeTree });
|
const router = createRouter({ routeTree });
|
||||||
@@ -43,22 +44,21 @@ const queryClient = new QueryClient();
|
|||||||
|
|
||||||
// Configuring OpenAPI
|
// Configuring OpenAPI
|
||||||
OpenAPI.BASE = import.meta.env.VITE_API_URL;
|
OpenAPI.BASE = import.meta.env.VITE_API_URL;
|
||||||
OpenAPI.TOKEN = JSON.parse(localStorage.getItem("authState") || "{}")[
|
OpenAPI.TOKEN = JSON.parse(localStorage.getItem("authState") || "{}")["accessToken"];
|
||||||
"accessToken"
|
|
||||||
];
|
|
||||||
ReactDOM.createRoot(document.getElementById("root")!).render(
|
ReactDOM.createRoot(document.getElementById("root")!).render(
|
||||||
<Provider store={store}>
|
<Provider store={store}>
|
||||||
<QueryClientProvider client={queryClient}>
|
<QueryClientProvider client={queryClient}>
|
||||||
<MantineProvider defaultColorScheme={"dark"}>
|
<MantineProvider defaultColorScheme={"dark"}>
|
||||||
<ContextMenuProvider>
|
<ContextMenuProvider>
|
||||||
|
|
||||||
<ModalsProvider
|
<ModalsProvider
|
||||||
labels={{ confirm: "Да", cancel: "Нет" }}
|
labels={{ confirm: "Да", cancel: "Нет" }}
|
||||||
modals={modals}>
|
modals={modals}>
|
||||||
<DatesProvider settings={{ locale: "ru" }}>
|
<DatesProvider settings={{ locale: "ru" }}>
|
||||||
<TasksProvider>
|
<TasksProvider>
|
||||||
<RouterProvider router={router} />
|
<ProjectsContextProvider>
|
||||||
<Notifications />
|
<RouterProvider router={router} />
|
||||||
|
<Notifications />
|
||||||
|
</ProjectsContextProvider>
|
||||||
</TasksProvider>
|
</TasksProvider>
|
||||||
</DatesProvider>
|
</DatesProvider>
|
||||||
</ModalsProvider>
|
</ModalsProvider>
|
||||||
|
|||||||
@@ -35,9 +35,9 @@ import ReceiptModal from "../pages/ReceiptPage/components/ReceiptEditing/modals/
|
|||||||
import SelectScannedProductModal from "../pages/ReceiptPage/modals/SelectScannedProductModal.tsx";
|
import SelectScannedProductModal from "../pages/ReceiptPage/modals/SelectScannedProductModal.tsx";
|
||||||
import BoardModal from "../pages/CardsPage/modals/BoardModal/BoardModal.tsx";
|
import BoardModal from "../pages/CardsPage/modals/BoardModal/BoardModal.tsx";
|
||||||
import StatusModal from "../pages/CardsPage/modals/StatusModal/StatusModal.tsx";
|
import StatusModal from "../pages/CardsPage/modals/StatusModal/StatusModal.tsx";
|
||||||
import AttributeModal from "../pages/CardsPage/drawers/ProjectsEditorDrawer/tabs/AttributesTab/modals/AttributeModal.tsx";
|
import AttributeModal from "../pages/AdminPage/tabs/Attributes/modals/AttributeModal.tsx";
|
||||||
import CreateProjectModal
|
import CreateProjectModal
|
||||||
from "../pages/CardsPage/drawers/ProjectsEditorDrawer/tabs/ProjectsTab/modals/CreateProjectModal.tsx";
|
from "../pages/CardsPage/drawers/ProjectEditDrawer/tabs/General/modals/CreateProjectModal.tsx";
|
||||||
|
|
||||||
export const modals = {
|
export const modals = {
|
||||||
enterDeadline: EnterDeadlineModal,
|
enterDeadline: EnterDeadlineModal,
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import {
|
|||||||
IconCalendarUser,
|
IconCalendarUser,
|
||||||
IconCoins,
|
IconCoins,
|
||||||
IconCurrencyDollar,
|
IconCurrencyDollar,
|
||||||
IconQrcode,
|
IconQrcode, IconSubtask,
|
||||||
IconTopologyStar3,
|
IconTopologyStar3,
|
||||||
IconUser,
|
IconUser,
|
||||||
} from "@tabler/icons-react";
|
} from "@tabler/icons-react";
|
||||||
@@ -21,6 +21,7 @@ import { RootState } from "../../redux/store.ts";
|
|||||||
import OrganizationalStructureTab from "./tabs/OrganizationalStructureTab/OrganizationalStructureTab.tsx";
|
import OrganizationalStructureTab from "./tabs/OrganizationalStructureTab/OrganizationalStructureTab.tsx";
|
||||||
import { ReactNode } from "react";
|
import { ReactNode } from "react";
|
||||||
import WorkShiftsPlanning from "./tabs/WorkShiftsPlanning/WorkShiftsPlanning.tsx";
|
import WorkShiftsPlanning from "./tabs/WorkShiftsPlanning/WorkShiftsPlanning.tsx";
|
||||||
|
import Attributes from "./tabs/Attributes/Attributes.tsx";
|
||||||
|
|
||||||
const AdminPage = () => {
|
const AdminPage = () => {
|
||||||
const userRole = useSelector((state: RootState) => state.auth.role);
|
const userRole = useSelector((state: RootState) => state.auth.role);
|
||||||
@@ -88,6 +89,13 @@ const AdminPage = () => {
|
|||||||
Доходы и расходы
|
Доходы и расходы
|
||||||
</Tabs.Tab>
|
</Tabs.Tab>
|
||||||
)}
|
)}
|
||||||
|
{isAdmin && (
|
||||||
|
<Tabs.Tab
|
||||||
|
value={"attributes"}
|
||||||
|
leftSection={<IconSubtask />}>
|
||||||
|
Атрибуты карточек
|
||||||
|
</Tabs.Tab>
|
||||||
|
)}
|
||||||
</Tabs.List>
|
</Tabs.List>
|
||||||
{getTabPanel("users", <UsersTab />)}
|
{getTabPanel("users", <UsersTab />)}
|
||||||
{getTabPanel("rolesAndPositions", <OrganizationalStructureTab />)}
|
{getTabPanel("rolesAndPositions", <OrganizationalStructureTab />)}
|
||||||
@@ -96,6 +104,7 @@ const AdminPage = () => {
|
|||||||
{getTabPanel("workShiftsPlanning", <WorkShiftsPlanning />)}
|
{getTabPanel("workShiftsPlanning", <WorkShiftsPlanning />)}
|
||||||
{getTabPanel("workShifts", <WorkShiftsTab />)}
|
{getTabPanel("workShifts", <WorkShiftsTab />)}
|
||||||
{getTabPanel("transactions", <TransactionsTab />)}
|
{getTabPanel("transactions", <TransactionsTab />)}
|
||||||
|
{getTabPanel("attributes", <Attributes />)}
|
||||||
</Tabs>
|
</Tabs>
|
||||||
</PageBlock>
|
</PageBlock>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
import { BaseTable } from "../../../../../../components/BaseTable/BaseTable.tsx";
|
import { BaseTable } from "../../../../components/BaseTable/BaseTable.tsx";
|
||||||
import useAttributesTableColumns from "./hooks/attributesTableColumns.tsx";
|
import useAttributesTableColumns from "./hooks/attributesTableColumns.tsx";
|
||||||
import useAttributesList from "../../../../../../hooks/useAttributesList.tsx";
|
import useAttributesList from "../../../../hooks/useAttributesList.tsx";
|
||||||
import { ActionIcon, Flex, Group, Stack, Text, Tooltip } from "@mantine/core";
|
import { ActionIcon, Flex, Group, Stack, Text, Tooltip } from "@mantine/core";
|
||||||
import InlineButton from "../../../../../../components/InlineButton/InlineButton.tsx";
|
import InlineButton from "../../../../components/InlineButton/InlineButton.tsx";
|
||||||
import { IconEdit, IconPlus, IconTrash } from "@tabler/icons-react";
|
import { IconEdit, IconPlus, IconTrash } from "@tabler/icons-react";
|
||||||
import { modals } from "@mantine/modals";
|
import { modals } from "@mantine/modals";
|
||||||
import { AttributeSchema, AttributeService } from "../../../../../../client";
|
import { AttributeSchema, AttributeService } from "../../../../client";
|
||||||
import { notifications } from "../../../../../../shared/lib/notifications.ts";
|
import { notifications } from "../../../../shared/lib/notifications.ts";
|
||||||
import { MRT_TableOptions } from "mantine-react-table";
|
import { MRT_TableOptions } from "mantine-react-table";
|
||||||
|
|
||||||
const AttributesTab = () => {
|
const Attributes = () => {
|
||||||
const columns = useAttributesTableColumns();
|
const columns = useAttributesTableColumns();
|
||||||
const { objects: attributes, refetch: refetchAttributes } = useAttributesList();
|
const { objects: attributes, refetch: refetchAttributes } = useAttributesList();
|
||||||
|
|
||||||
@@ -66,7 +66,7 @@ const AttributesTab = () => {
|
|||||||
return (
|
return (
|
||||||
<Stack>
|
<Stack>
|
||||||
<Group>
|
<Group>
|
||||||
<InlineButton onClick={onCreateAttributeClick}>
|
<InlineButton onClick={onCreateAttributeClick} mt={"md"}>
|
||||||
<IconPlus />
|
<IconPlus />
|
||||||
Добавить атрибут
|
Добавить атрибут
|
||||||
</InlineButton>
|
</InlineButton>
|
||||||
@@ -109,4 +109,4 @@ const AttributesTab = () => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default AttributesTab;
|
export default Attributes;
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import ObjectSelect, { ObjectSelectProps } from "../../../../../../../components/ObjectSelect/ObjectSelect.tsx";
|
import ObjectSelect, { ObjectSelectProps } from "../../../../../components/ObjectSelect/ObjectSelect.tsx";
|
||||||
import { AttributeTypeSchema } from "../../../../../../../client";
|
import { AttributeTypeSchema } from "../../../../../client";
|
||||||
import useAttributeTypesList from "../hooks/useAttributeTypesList.tsx";
|
import useAttributeTypesList from "../hooks/useAttributeTypesList.tsx";
|
||||||
|
|
||||||
type Props = Omit<ObjectSelectProps<AttributeTypeSchema>, "data">;
|
type Props = Omit<ObjectSelectProps<AttributeTypeSchema>, "data">;
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import { Checkbox, NumberInput, TextInput } from "@mantine/core";
|
import { Checkbox, NumberInput, TextInput } from "@mantine/core";
|
||||||
import { UseFormReturnType } from "@mantine/form";
|
import { UseFormReturnType } from "@mantine/form";
|
||||||
import { DatePickerInput, DateTimePicker } from "@mantine/dates";
|
import { DatePickerInput, DateTimePicker } from "@mantine/dates";
|
||||||
import { AttributeSchema } from "../../../../../../../client";
|
import { AttributeSchema } from "../../../../../client";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
form: UseFormReturnType<Partial<AttributeSchema>>;
|
form: UseFormReturnType<Partial<AttributeSchema>>;
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
import { useMemo } from "react";
|
import { useMemo } from "react";
|
||||||
import { MRT_ColumnDef } from "mantine-react-table";
|
import { MRT_ColumnDef } from "mantine-react-table";
|
||||||
import { AttributeSchema } from "../../../../../../../client";
|
import { AttributeSchema } from "../../../../../client";
|
||||||
import { IconCheck, IconX } from "@tabler/icons-react";
|
import { IconCheck, IconX } from "@tabler/icons-react";
|
||||||
import { formatDate, formatDateTime } from "../../../../../../../types/utils.ts";
|
import { formatDate, formatDateTime } from "../../../../../types/utils.ts";
|
||||||
|
|
||||||
|
|
||||||
const useAttributesTableColumns = () => {
|
const useAttributesTableColumns = () => {
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import { AttributeService } from "../../../../../../../client";
|
import { AttributeService } from "../../../../../client";
|
||||||
import ObjectList from "../../../../../../../hooks/objectList.tsx";
|
import ObjectList from "../../../../../hooks/objectList.tsx";
|
||||||
|
|
||||||
const useAttributeTypesList = () =>
|
const useAttributeTypesList = () =>
|
||||||
ObjectList({
|
ObjectList({
|
||||||
@@ -2,11 +2,11 @@ import { ContextModalProps } from "@mantine/modals";
|
|||||||
import { Button, Checkbox, Stack, Textarea, TextInput } from "@mantine/core";
|
import { Button, Checkbox, Stack, Textarea, TextInput } from "@mantine/core";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import AttributeTypeSelect from "../components/AttributeTypeSelect.tsx";
|
import AttributeTypeSelect from "../components/AttributeTypeSelect.tsx";
|
||||||
import { AttributeSchema, AttributeService } from "../../../../../../../client";
|
import { AttributeSchema, AttributeService } from "../../../../../client";
|
||||||
import { convertRussianToSnakeCase } from "../../../utils/stringConverting.ts";
|
import { convertRussianToSnakeCase } from "../../../../CardsPage/drawers/ProjectEditDrawer/utils/stringConverting.ts";
|
||||||
import { useForm } from "@mantine/form";
|
import { useForm } from "@mantine/form";
|
||||||
import DefaultAttributeValueInput from "../components/DefaultAttributeValueInput.tsx";
|
import DefaultAttributeValueInput from "../components/DefaultAttributeValueInput.tsx";
|
||||||
import { notifications } from "../../../../../../../shared/lib/notifications.ts";
|
import { notifications } from "../../../../../shared/lib/notifications.ts";
|
||||||
|
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import { ActionIcon, Flex, rem, Text } from "@mantine/core";
|
import { ActionIcon, Flex, rem, Text } from "@mantine/core";
|
||||||
import { IconEdit, IconMenu2, IconMenuDeep } from "@tabler/icons-react";
|
import { IconEdit, IconMenu2, IconMenuDeep, IconPlus } from "@tabler/icons-react";
|
||||||
import { motion } from "framer-motion";
|
import { motion } from "framer-motion";
|
||||||
import styles from "../../ui/CardsPage.module.css";
|
import styles from "../../ui/CardsPage.module.css";
|
||||||
import PageBlock from "../../../../components/PageBlock/PageBlock.tsx";
|
import PageBlock from "../../../../components/PageBlock/PageBlock.tsx";
|
||||||
@@ -7,30 +7,41 @@ import DisplayMode from "../../enums/DisplayMode.ts";
|
|||||||
import { UseFormReturnType } from "@mantine/form";
|
import { UseFormReturnType } from "@mantine/form";
|
||||||
import { CardsPageState } from "../../hooks/useCardsPageState.tsx";
|
import { CardsPageState } from "../../hooks/useCardsPageState.tsx";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { ProjectSchema } from "../../../../client";
|
|
||||||
import ObjectSelect from "../../../../components/ObjectSelect/ObjectSelect.tsx";
|
import ObjectSelect from "../../../../components/ObjectSelect/ObjectSelect.tsx";
|
||||||
import CardsTableFiltersModal from "../../modals/CardsTableFiltersModal.tsx";
|
import CardsTableFiltersModal from "../../modals/CardsTableFiltersModal.tsx";
|
||||||
import { useProjectsEditorContext } from "../../contexts/ProjectsEditorContext.tsx";
|
import { useProjectsEditorContext } from "../../contexts/ProjectsEditorContext.tsx";
|
||||||
import { useSelector } from "react-redux";
|
import { useSelector } from "react-redux";
|
||||||
import { RootState } from "../../../../redux/store.ts";
|
import { RootState } from "../../../../redux/store.ts";
|
||||||
|
import { useProjectsContext } from "../../../../contexts/ProjectsContext.tsx";
|
||||||
|
import { modals } from "@mantine/modals";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
displayMode: DisplayMode;
|
displayMode: DisplayMode;
|
||||||
setDisplayMode: React.Dispatch<React.SetStateAction<DisplayMode>>;
|
setDisplayMode: React.Dispatch<React.SetStateAction<DisplayMode>>;
|
||||||
form: UseFormReturnType<CardsPageState>;
|
form: UseFormReturnType<CardsPageState>;
|
||||||
projects: ProjectSchema[];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const CardsPageHeader = ({
|
const CardsPageHeader = ({
|
||||||
displayMode,
|
displayMode,
|
||||||
setDisplayMode,
|
setDisplayMode,
|
||||||
form,
|
form,
|
||||||
projects,
|
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
const { openProjectsEditor } = useProjectsEditorContext();
|
const { openProjectsEditor } = useProjectsEditorContext();
|
||||||
|
const { selectedProject, setSelectedProject, projects, refetchProjects } = useProjectsContext();
|
||||||
const userRole = useSelector((state: RootState) => state.auth.role);
|
const userRole = useSelector((state: RootState) => state.auth.role);
|
||||||
const isAdmin = userRole === "admin";
|
const isAdmin = userRole === "admin";
|
||||||
|
|
||||||
|
const handleCreateClick = () => {
|
||||||
|
modals.openContextModal({
|
||||||
|
modal: "createProjectModal",
|
||||||
|
title: "Создание проекта",
|
||||||
|
withCloseButton: false,
|
||||||
|
innerProps: {
|
||||||
|
refetchProjects,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
const getHeaderInputsBoard = () => {
|
const getHeaderInputsBoard = () => {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
@@ -40,17 +51,26 @@ const CardsPageHeader = ({
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{isAdmin && (
|
{isAdmin && (
|
||||||
<ActionIcon
|
<>
|
||||||
size={"lg"}
|
<ActionIcon
|
||||||
onClick={openProjectsEditor}
|
size={"lg"}
|
||||||
variant={"default"}>
|
onClick={handleCreateClick}
|
||||||
<IconEdit />
|
variant={"default"}>
|
||||||
</ActionIcon>
|
<IconPlus />
|
||||||
|
</ActionIcon>
|
||||||
|
<ActionIcon
|
||||||
|
size={"lg"}
|
||||||
|
onClick={openProjectsEditor}
|
||||||
|
variant={"default"}>
|
||||||
|
<IconEdit />
|
||||||
|
</ActionIcon>
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
<ObjectSelect
|
<ObjectSelect
|
||||||
placeholder={"Выберите проект"}
|
placeholder={"Выберите проект"}
|
||||||
data={projects}
|
data={projects}
|
||||||
{...form.getInputProps("project")}
|
value={selectedProject}
|
||||||
|
onChange={setSelectedProject}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,12 +1,11 @@
|
|||||||
import React, { createContext, FC, useContext, useEffect, useState } from "react";
|
import React, { createContext, FC, useContext, useEffect, useState } from "react";
|
||||||
import { CardSchema, CardService, ProjectSchema } from "../../../client";
|
import { CardSchema, CardService } from "../../../client";
|
||||||
|
|
||||||
type CardPageContextState = {
|
type CardPageContextState = {
|
||||||
selectedCard?: CardSchema;
|
selectedCard?: CardSchema;
|
||||||
setSelectedCard: (card: CardSchema | undefined) => void;
|
setSelectedCard: (card: CardSchema | undefined) => void;
|
||||||
refetchCards: () => Promise<void>;
|
refetchCards: () => Promise<void>;
|
||||||
refetchCard: () => void;
|
refetchCard: () => void;
|
||||||
selectedProject?: ProjectSchema | null;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const CardPageContext = createContext<CardPageContextState | undefined>(
|
const CardPageContext = createContext<CardPageContextState | undefined>(
|
||||||
@@ -16,7 +15,6 @@ const CardPageContext = createContext<CardPageContextState | undefined>(
|
|||||||
type CardPageContextStateProps = {
|
type CardPageContextStateProps = {
|
||||||
refetchCards: () => Promise<void>;
|
refetchCards: () => Promise<void>;
|
||||||
defaultCardId?: number;
|
defaultCardId?: number;
|
||||||
selectedProject?: ProjectSchema | null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const useCardPageContextState = (props: CardPageContextStateProps) => {
|
const useCardPageContextState = (props: CardPageContextStateProps) => {
|
||||||
@@ -41,7 +39,6 @@ const useCardPageContextState = (props: CardPageContextStateProps) => {
|
|||||||
return {
|
return {
|
||||||
selectedCard,
|
selectedCard,
|
||||||
setSelectedCard,
|
setSelectedCard,
|
||||||
selectedProject: props.selectedProject,
|
|
||||||
refetchCards,
|
refetchCards,
|
||||||
refetchCard,
|
refetchCard,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -5,37 +5,28 @@ type ProjectsEditorContextState = {
|
|||||||
openedProjectsEditor: boolean;
|
openedProjectsEditor: boolean;
|
||||||
openProjectsEditor: () => void;
|
openProjectsEditor: () => void;
|
||||||
closeProjectsEditor: () => void;
|
closeProjectsEditor: () => void;
|
||||||
onUpdate: () => void;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const ProjectsEditorContext = createContext<ProjectsEditorContextState | undefined>(
|
const ProjectsEditorContext = createContext<ProjectsEditorContextState | undefined>(
|
||||||
undefined,
|
undefined,
|
||||||
);
|
);
|
||||||
|
|
||||||
type ProjectsEditorContextStateProps = {
|
const useProjectsEditorContextState = () => {
|
||||||
onUpdate: () => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
const useProjectsEditorContextState = ({ onUpdate }: ProjectsEditorContextStateProps) => {
|
|
||||||
const [opened, { open, close }] = useDisclosure(false);
|
const [opened, { open, close }] = useDisclosure(false);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
openedProjectsEditor: opened,
|
openedProjectsEditor: opened,
|
||||||
openProjectsEditor: open,
|
openProjectsEditor: open,
|
||||||
closeProjectsEditor: close,
|
closeProjectsEditor: close,
|
||||||
onUpdate,
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
type ProjectsEditorContextProviderProps = {
|
type ProjectsEditorContextProviderProps = {
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
} & ProjectsEditorContextStateProps;
|
};
|
||||||
|
|
||||||
export const ProjectsEditorContextProvider: FC<ProjectsEditorContextProviderProps> = ({
|
export const ProjectsEditorContextProvider: FC<ProjectsEditorContextProviderProps> = ({ children }) => {
|
||||||
children,
|
const state = useProjectsEditorContextState();
|
||||||
...props
|
|
||||||
}) => {
|
|
||||||
const state = useProjectsEditorContextState(props);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ProjectsEditorContext.Provider value={state}>
|
<ProjectsEditorContext.Provider value={state}>
|
||||||
@@ -48,7 +39,7 @@ export const useProjectsEditorContext = () => {
|
|||||||
const context = useContext(ProjectsEditorContext);
|
const context = useContext(ProjectsEditorContext);
|
||||||
if (!context) {
|
if (!context) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
"useProjectsEditorContext must be used within a ProjectsEditorContextProvider",
|
"useProjectEditorContext must be used within a ProjectEditorContextProvider",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return context;
|
return context;
|
||||||
|
|||||||
@@ -1,13 +1,14 @@
|
|||||||
import { Box, Drawer, rem, Tabs } from "@mantine/core";
|
import { Box, Drawer, rem, Tabs } from "@mantine/core";
|
||||||
import { IconSettings, IconSubtask } from "@tabler/icons-react";
|
import { IconHexagons, IconSettings, IconSubtask } from "@tabler/icons-react";
|
||||||
import { ReactNode } from "react";
|
import { ReactNode } from "react";
|
||||||
import { motion } from "framer-motion";
|
import { motion } from "framer-motion";
|
||||||
import { useProjectsEditorContext } from "../../contexts/ProjectsEditorContext.tsx";
|
import { useProjectsEditorContext } from "../../contexts/ProjectsEditorContext.tsx";
|
||||||
import ProjectsTab from "./tabs/ProjectsTab/ProjectsTab.tsx";
|
import General from "./tabs/General/General.tsx";
|
||||||
import AttributesTab from "./tabs/AttributesTab/AttributesTab.tsx";
|
import Attributes from "./tabs/Attributes/Attributes.tsx";
|
||||||
|
import Modules from "./tabs/Modules/Modules.tsx";
|
||||||
|
|
||||||
|
|
||||||
const ProjectsEditorDrawer = () => {
|
const ProjectEditDrawer = () => {
|
||||||
const { closeProjectsEditor, openedProjectsEditor } = useProjectsEditorContext();
|
const { closeProjectsEditor, openedProjectsEditor } = useProjectsEditorContext();
|
||||||
|
|
||||||
const getTabPanel = (value: string, component: ReactNode) => {
|
const getTabPanel = (value: string, component: ReactNode) => {
|
||||||
@@ -45,16 +46,21 @@ const ProjectsEditorDrawer = () => {
|
|||||||
},
|
},
|
||||||
}}>
|
}}>
|
||||||
<Tabs
|
<Tabs
|
||||||
defaultValue={"projects"}
|
defaultValue={"general"}
|
||||||
flex={1}
|
flex={1}
|
||||||
variant={"outline"}
|
variant={"outline"}
|
||||||
orientation={"vertical"}
|
orientation={"vertical"}
|
||||||
keepMounted={false}>
|
keepMounted={false}>
|
||||||
<Tabs.List>
|
<Tabs.List>
|
||||||
<Tabs.Tab
|
<Tabs.Tab
|
||||||
value={"projects"}
|
value={"general"}
|
||||||
leftSection={<IconSettings />}>
|
leftSection={<IconSettings />}>
|
||||||
Проекты
|
Общее
|
||||||
|
</Tabs.Tab>
|
||||||
|
<Tabs.Tab
|
||||||
|
value={"modules"}
|
||||||
|
leftSection={<IconHexagons />}>
|
||||||
|
Модули
|
||||||
</Tabs.Tab>
|
</Tabs.Tab>
|
||||||
<Tabs.Tab
|
<Tabs.Tab
|
||||||
value={"attributes"}
|
value={"attributes"}
|
||||||
@@ -63,11 +69,12 @@ const ProjectsEditorDrawer = () => {
|
|||||||
</Tabs.Tab>
|
</Tabs.Tab>
|
||||||
</Tabs.List>
|
</Tabs.List>
|
||||||
|
|
||||||
{getTabPanel("projects", <ProjectsTab />)}
|
{getTabPanel("general", <General/>)}
|
||||||
{getTabPanel("attributes", <AttributesTab />)}
|
{getTabPanel("attributes", <Attributes/>)}
|
||||||
|
{getTabPanel("modules", <Modules />)}
|
||||||
</Tabs>
|
</Tabs>
|
||||||
</Drawer>
|
</Drawer>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default ProjectsEditorDrawer;
|
export default ProjectEditDrawer;
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
import { ProjectService } from "../../../../../../client";
|
||||||
|
import useAttributesList from "../../../../../../hooks/useAttributesList.tsx";
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import { useSet } from "@mantine/hooks";
|
||||||
|
import useAttributesTableColumns from "./hooks/attributesTableColumns.tsx";
|
||||||
|
import { notifications } from "../../../../../../shared/lib/notifications.ts";
|
||||||
|
import { rem, Stack } from "@mantine/core";
|
||||||
|
import { BaseTable } from "../../../../../../components/BaseTable/BaseTable.tsx";
|
||||||
|
import eqSet from "../../utils/eqSet.ts";
|
||||||
|
import InlineButton from "../../../../../../components/InlineButton/InlineButton.tsx";
|
||||||
|
import { IconCheck } from "@tabler/icons-react";
|
||||||
|
import { useProjectsContext } from "../../../../../../contexts/ProjectsContext.tsx";
|
||||||
|
|
||||||
|
|
||||||
|
const Attributes = () => {
|
||||||
|
const { selectedProject: project, refetchProjects } = useProjectsContext();
|
||||||
|
|
||||||
|
const { objects: attributes } = useAttributesList();
|
||||||
|
const [defaultSelectedAttributes, setDefaultSelectedAttributes] = useState(new Set<number>(project?.attributes.map(m => m.id)));
|
||||||
|
const selectedAttributes = useSet<number>(project?.attributes.map(a => a.id));
|
||||||
|
const columns = useAttributesTableColumns({ selectedAttributes });
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
selectedAttributes.clear();
|
||||||
|
project?.attributes.forEach(attribute => {
|
||||||
|
selectedAttributes.add(attribute.id);
|
||||||
|
});
|
||||||
|
setDefaultSelectedAttributes(new Set([...selectedAttributes]));
|
||||||
|
}, [project]);
|
||||||
|
|
||||||
|
const onUpdateAttributesClick = () => {
|
||||||
|
if (!project) return;
|
||||||
|
ProjectService.updateProjectAttributes({
|
||||||
|
requestBody: {
|
||||||
|
projectId: project.id,
|
||||||
|
attributeIds: selectedAttributes.values().toArray(),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.then(({ ok, message }) => {
|
||||||
|
if (!ok) {
|
||||||
|
notifications.error({ message });
|
||||||
|
}
|
||||||
|
refetchProjects();
|
||||||
|
})
|
||||||
|
.catch(err => console.log(err));
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Stack gap={rem(10)}>
|
||||||
|
<BaseTable
|
||||||
|
data={attributes}
|
||||||
|
columns={columns}
|
||||||
|
|
||||||
|
restProps={{
|
||||||
|
enableSorting: false,
|
||||||
|
enableColumnActions: false,
|
||||||
|
enableRowVirtualization: true,
|
||||||
|
mantineTableContainerProps: { style: { maxHeight: "88vh" } },
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
{!eqSet(selectedAttributes, defaultSelectedAttributes) && (
|
||||||
|
<InlineButton onClick={onUpdateAttributesClick}>
|
||||||
|
<IconCheck />
|
||||||
|
Сохранить
|
||||||
|
</InlineButton>
|
||||||
|
)}
|
||||||
|
</Stack>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Attributes;
|
||||||
@@ -0,0 +1,114 @@
|
|||||||
|
import { Button, Fieldset, Flex, rem, Stack, Text, TextInput } from "@mantine/core";
|
||||||
|
import { ProjectService } from "../../../../../../client";
|
||||||
|
import { useForm } from "@mantine/form";
|
||||||
|
import { notifications } from "../../../../../../shared/lib/notifications.ts";
|
||||||
|
import { isEqual } from "lodash";
|
||||||
|
import { useProjectsContext } from "../../../../../../contexts/ProjectsContext.tsx";
|
||||||
|
import { modals } from "@mantine/modals";
|
||||||
|
import { useProjectsEditorContext } from "../../../../contexts/ProjectsEditorContext.tsx";
|
||||||
|
|
||||||
|
|
||||||
|
type ProjectForm = {
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const General = () => {
|
||||||
|
const { selectedProject: project, refetchProjects } = useProjectsContext();
|
||||||
|
const { closeProjectsEditor } = useProjectsEditorContext();
|
||||||
|
if (!project) return;
|
||||||
|
|
||||||
|
const form = useForm<ProjectForm>({
|
||||||
|
initialValues: project,
|
||||||
|
validate: {
|
||||||
|
name: name => !name && "Название проекта не введено",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const onProjectDelete = () => {
|
||||||
|
ProjectService.deleteProject({
|
||||||
|
projectId: project.id,
|
||||||
|
})
|
||||||
|
.then(({ ok, message }) => {
|
||||||
|
if (!ok) {
|
||||||
|
notifications.error({ message });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
closeProjectsEditor();
|
||||||
|
refetchProjects();
|
||||||
|
})
|
||||||
|
.catch(err => console.log(err));
|
||||||
|
};
|
||||||
|
|
||||||
|
const onDeleteProjectClick = () => {
|
||||||
|
modals.openConfirmModal({
|
||||||
|
title: "Удаление проекта",
|
||||||
|
children: (
|
||||||
|
<Text size="sm">
|
||||||
|
Вы уверены что хотите удалить проект "{project.name}"?
|
||||||
|
</Text>
|
||||||
|
),
|
||||||
|
labels: { confirm: "Да", cancel: "Нет" },
|
||||||
|
confirmProps: { color: "red" },
|
||||||
|
onConfirm: () => onProjectDelete(),
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const onSubmit = (values: ProjectForm) => {
|
||||||
|
ProjectService.updateProject({
|
||||||
|
requestBody: {
|
||||||
|
project: {
|
||||||
|
id: project.id,
|
||||||
|
name: values.name,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.then(({ ok, message }) => {
|
||||||
|
if (!ok) {
|
||||||
|
notifications.error({ message });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
refetchProjects();
|
||||||
|
})
|
||||||
|
.catch(err => console.log(err));
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<form onSubmit={form.onSubmit(values => onSubmit(values))}>
|
||||||
|
<Stack>
|
||||||
|
<Fieldset legend={"Общие параметры"}>
|
||||||
|
<Stack>
|
||||||
|
<TextInput
|
||||||
|
label={"Название"}
|
||||||
|
{...form.getInputProps("name")}
|
||||||
|
/>
|
||||||
|
</Stack>
|
||||||
|
</Fieldset>
|
||||||
|
<Flex direction={"row-reverse"} gap={rem(10)}>
|
||||||
|
<Button
|
||||||
|
variant={"default"}
|
||||||
|
type={"submit"}
|
||||||
|
disabled={isEqual(project, form.values)}
|
||||||
|
>
|
||||||
|
Сохранить изменения
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
type={"reset"}
|
||||||
|
variant={"default"}
|
||||||
|
disabled={isEqual(project, form.values)}
|
||||||
|
onClick={() => form.reset()}
|
||||||
|
>
|
||||||
|
Отменить изменения
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
variant={"default"}
|
||||||
|
onClick={() => onDeleteProjectClick()}
|
||||||
|
>
|
||||||
|
Удалить
|
||||||
|
</Button>
|
||||||
|
</Flex>
|
||||||
|
</Stack>
|
||||||
|
</form>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default General;
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
import useModulesList from "./hooks/useModulesList.tsx";
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import { useSet } from "@mantine/hooks";
|
||||||
|
import useModulesTableColumns from "./hooks/modulesTableColumns.tsx";
|
||||||
|
import { ProjectService } from "../../../../../../client";
|
||||||
|
import { notifications } from "../../../../../../shared/lib/notifications.ts";
|
||||||
|
import { rem, Stack } from "@mantine/core";
|
||||||
|
import { BaseTable } from "../../../../../../components/BaseTable/BaseTable.tsx";
|
||||||
|
import eqSet from "../../utils/eqSet.ts";
|
||||||
|
import InlineButton from "../../../../../../components/InlineButton/InlineButton.tsx";
|
||||||
|
import { IconCheck } from "@tabler/icons-react";
|
||||||
|
import { useProjectsContext } from "../../../../../../contexts/ProjectsContext.tsx";
|
||||||
|
|
||||||
|
const Modules = () => {
|
||||||
|
const { selectedProject: project, refetchProjects } = useProjectsContext();
|
||||||
|
|
||||||
|
const { objects: modules } = useModulesList();
|
||||||
|
const [defaultSelectedModules, setDefaultSelectedModules] = useState(
|
||||||
|
new Set<number>(project?.modules.map(m => m.id)),
|
||||||
|
);
|
||||||
|
const selectedModules = useSet<number>();
|
||||||
|
const columns = useModulesTableColumns({ selectedModules });
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
selectedModules.clear();
|
||||||
|
project?.modules.forEach(module => {
|
||||||
|
selectedModules.add(module.id);
|
||||||
|
});
|
||||||
|
setDefaultSelectedModules(new Set([...selectedModules]));
|
||||||
|
}, [project]);
|
||||||
|
|
||||||
|
const updateProjectModules = () => {
|
||||||
|
if (!project) return;
|
||||||
|
ProjectService.updateProjectModules({
|
||||||
|
requestBody: {
|
||||||
|
projectId: project.id,
|
||||||
|
moduleIds: selectedModules.values().toArray(),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.then(({ ok, message }) => {
|
||||||
|
if (!ok) {
|
||||||
|
notifications.error({ message });
|
||||||
|
}
|
||||||
|
refetchProjects();
|
||||||
|
})
|
||||||
|
.catch(err => console.log(err));
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Stack gap={rem(10)}>
|
||||||
|
<BaseTable
|
||||||
|
data={modules}
|
||||||
|
columns={columns}
|
||||||
|
|
||||||
|
restProps={{
|
||||||
|
enableSorting: false,
|
||||||
|
enableColumnActions: false,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
{!eqSet(selectedModules, defaultSelectedModules) && (
|
||||||
|
<InlineButton onClick={updateProjectModules}>
|
||||||
|
<IconCheck />
|
||||||
|
Сохранить
|
||||||
|
</InlineButton>
|
||||||
|
)}
|
||||||
|
</Stack>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Modules;
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
import { Group } from "@mantine/core";
|
|
||||||
import { ProjectSchema } from "../../../../../../client";
|
|
||||||
import ModulesPicker from "./components/ModulesPicker.tsx";
|
|
||||||
import { useEffect, useState } from "react";
|
|
||||||
import AttributesPicker from "./components/AttributesPicker.tsx";
|
|
||||||
import useProjects from "../../../../hooks/useProjects.tsx";
|
|
||||||
import ProjectsEditor from "./components/ProjectsEditor.tsx";
|
|
||||||
|
|
||||||
const ProjectsTab = () => {
|
|
||||||
const { projects, refetchProjects } = useProjects();
|
|
||||||
const [selectedProject, setSelectedProject] = useState<ProjectSchema | null>(null);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (projects.length > 0) {
|
|
||||||
if (!selectedProject) {
|
|
||||||
setSelectedProject(projects[0]);
|
|
||||||
} else {
|
|
||||||
setSelectedProject(projects.find((project) => project.id === selectedProject.id) ?? null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, [projects]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Group align={"flex-start"}>
|
|
||||||
<ProjectsEditor
|
|
||||||
projects={projects}
|
|
||||||
refetchProjects={refetchProjects}
|
|
||||||
selectedProject={selectedProject}
|
|
||||||
setSelectedProject={setSelectedProject}
|
|
||||||
/>
|
|
||||||
<ModulesPicker
|
|
||||||
project={selectedProject}
|
|
||||||
refetchProjects={refetchProjects}
|
|
||||||
/>
|
|
||||||
<AttributesPicker
|
|
||||||
project={selectedProject}
|
|
||||||
refetchProjects={refetchProjects}
|
|
||||||
/>
|
|
||||||
</Group>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default ProjectsTab;
|
|
||||||
@@ -1,79 +0,0 @@
|
|||||||
import { ProjectSchema, ProjectService } from "../../../../../../../client";
|
|
||||||
import styles from "../../../ProjectsEditorDrawer.module.css";
|
|
||||||
import { Center, rem, Stack, Title } from "@mantine/core";
|
|
||||||
import { BaseTable } from "../../../../../../../components/BaseTable/BaseTable.tsx";
|
|
||||||
import useAttributesList from "../../../../../../../hooks/useAttributesList.tsx";
|
|
||||||
import useAttributesTableColumns from "../hooks/attributesTableColumns.tsx";
|
|
||||||
import { useSet } from "@mantine/hooks";
|
|
||||||
import { useEffect, useState } from "react";
|
|
||||||
import { notifications } from "../../../../../../../shared/lib/notifications.ts";
|
|
||||||
import InlineButton from "../../../../../../../components/InlineButton/InlineButton.tsx";
|
|
||||||
import { IconCheck } from "@tabler/icons-react";
|
|
||||||
import eqSet from "../../../utils/eqSet.ts";
|
|
||||||
|
|
||||||
|
|
||||||
type Props = {
|
|
||||||
project: ProjectSchema | null;
|
|
||||||
refetchProjects: () => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
const AttributesPicker = ({ project, refetchProjects }: Props) => {
|
|
||||||
const { objects: attributes } = useAttributesList();
|
|
||||||
const [defaultSelectedAttributes, setDefaultSelectedAttributes] = useState(new Set<number>(project?.attributes.map(m => m.id)));
|
|
||||||
const selectedAttributes = useSet<number>(project?.attributes.map(a => a.id));
|
|
||||||
const columns = useAttributesTableColumns({ selectedAttributes });
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
selectedAttributes.clear();
|
|
||||||
project?.attributes.forEach(attribute => {
|
|
||||||
selectedAttributes.add(attribute.id);
|
|
||||||
});
|
|
||||||
setDefaultSelectedAttributes(new Set([...selectedAttributes]));
|
|
||||||
}, [project]);
|
|
||||||
|
|
||||||
const onUpdateAttributesClick = () => {
|
|
||||||
if (!project) return;
|
|
||||||
ProjectService.updateProjectAttributes({
|
|
||||||
requestBody: {
|
|
||||||
projectId: project.id,
|
|
||||||
attributeIds: selectedAttributes.values().toArray(),
|
|
||||||
},
|
|
||||||
})
|
|
||||||
.then(({ ok, message }) => {
|
|
||||||
if (!ok) {
|
|
||||||
notifications.error({ message });
|
|
||||||
}
|
|
||||||
refetchProjects();
|
|
||||||
})
|
|
||||||
.catch(err => console.log(err));
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className={styles["container-wrapper"]} style={{ flex: 2 }}>
|
|
||||||
<Stack gap={rem(10)}>
|
|
||||||
<Center>
|
|
||||||
<Title order={4}>Атрибуты проекта</Title>
|
|
||||||
</Center>
|
|
||||||
<BaseTable
|
|
||||||
data={attributes}
|
|
||||||
columns={columns}
|
|
||||||
|
|
||||||
restProps={{
|
|
||||||
enableSorting: false,
|
|
||||||
enableColumnActions: false,
|
|
||||||
enableRowVirtualization: true,
|
|
||||||
mantineTableContainerProps: { style: { maxHeight: "88vh" } },
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
{!eqSet(selectedAttributes, defaultSelectedAttributes) && (
|
|
||||||
<InlineButton onClick={onUpdateAttributesClick}>
|
|
||||||
<IconCheck />
|
|
||||||
Сохранить
|
|
||||||
</InlineButton>
|
|
||||||
)}
|
|
||||||
</Stack>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default AttributesPicker;
|
|
||||||
@@ -1,78 +0,0 @@
|
|||||||
import { ProjectSchema, ProjectService } from "../../../../../../../client";
|
|
||||||
import useModulesList from "../hooks/useModulesList.tsx";
|
|
||||||
import styles from "../../../ProjectsEditorDrawer.module.css";
|
|
||||||
import { useSet } from "@mantine/hooks";
|
|
||||||
import useModulesTableColumns from "../hooks/modulesTableColumns.tsx";
|
|
||||||
import { BaseTable } from "../../../../../../../components/BaseTable/BaseTable.tsx";
|
|
||||||
import { Center, rem, Stack, Title } from "@mantine/core";
|
|
||||||
import { useEffect, useState } from "react";
|
|
||||||
import InlineButton from "../../../../../../../components/InlineButton/InlineButton.tsx";
|
|
||||||
import { IconCheck } from "@tabler/icons-react";
|
|
||||||
import { notifications } from "../../../../../../../shared/lib/notifications.ts";
|
|
||||||
import eqSet from "../../../utils/eqSet.ts";
|
|
||||||
|
|
||||||
type Props = {
|
|
||||||
project: ProjectSchema | null;
|
|
||||||
refetchProjects: () => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
const ModulesPicker = ({ project, refetchProjects }: Props) => {
|
|
||||||
const { objects: modules } = useModulesList();
|
|
||||||
const [defaultSelectedModules, setDefaultSelectedModules] = useState(
|
|
||||||
new Set<number>(project?.modules.map(m => m.id)),
|
|
||||||
);
|
|
||||||
const selectedModules = useSet<number>();
|
|
||||||
const columns = useModulesTableColumns({ selectedModules });
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
selectedModules.clear();
|
|
||||||
project?.modules.forEach(module => {
|
|
||||||
selectedModules.add(module.id);
|
|
||||||
});
|
|
||||||
setDefaultSelectedModules(new Set([...selectedModules]));
|
|
||||||
}, [project]);
|
|
||||||
|
|
||||||
const updateProjectModules = () => {
|
|
||||||
if (!project) return;
|
|
||||||
ProjectService.updateProjectModules({
|
|
||||||
requestBody: {
|
|
||||||
projectId: project.id,
|
|
||||||
moduleIds: selectedModules.values().toArray(),
|
|
||||||
},
|
|
||||||
})
|
|
||||||
.then(({ ok, message }) => {
|
|
||||||
if (!ok) {
|
|
||||||
notifications.error({ message });
|
|
||||||
}
|
|
||||||
refetchProjects();
|
|
||||||
})
|
|
||||||
.catch(err => console.log(err));
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className={styles["container-wrapper"]}>
|
|
||||||
<Stack gap={rem(10)}>
|
|
||||||
<Center>
|
|
||||||
<Title order={4}>Модули проекта</Title>
|
|
||||||
</Center>
|
|
||||||
<BaseTable
|
|
||||||
data={modules}
|
|
||||||
columns={columns}
|
|
||||||
|
|
||||||
restProps={{
|
|
||||||
enableSorting: false,
|
|
||||||
enableColumnActions: false,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
{!eqSet(selectedModules, defaultSelectedModules) && (
|
|
||||||
<InlineButton onClick={updateProjectModules}>
|
|
||||||
<IconCheck />
|
|
||||||
Сохранить
|
|
||||||
</InlineButton>
|
|
||||||
)}
|
|
||||||
</Stack>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default ModulesPicker;
|
|
||||||
@@ -1,95 +0,0 @@
|
|||||||
import styles from "../../../ProjectsEditorDrawer.module.css";
|
|
||||||
import { ActionIcon, Center, Flex, rem, Stack, Title, Tooltip } from "@mantine/core";
|
|
||||||
import InlineButton from "../../../../../../../components/InlineButton/InlineButton.tsx";
|
|
||||||
import { IconCheck, IconEdit, IconPlus, IconTrash } from "@tabler/icons-react";
|
|
||||||
import { BaseTable } from "../../../../../../../components/BaseTable/BaseTable.tsx";
|
|
||||||
import { MRT_TableOptions } from "mantine-react-table";
|
|
||||||
import { FullProjectSchema, ProjectSchema } from "../../../../../../../client";
|
|
||||||
import useProjectsTableColumns from "../hooks/projectsTableColumns.tsx";
|
|
||||||
import useProjectsTab from "../hooks/useProjectsTab.tsx";
|
|
||||||
import React from "react";
|
|
||||||
|
|
||||||
type Props = {
|
|
||||||
projects: ProjectSchema[];
|
|
||||||
refetchProjects: () => void;
|
|
||||||
selectedProject: ProjectSchema | null;
|
|
||||||
setSelectedProject: React.Dispatch<React.SetStateAction<ProjectSchema | null>>;
|
|
||||||
}
|
|
||||||
|
|
||||||
const ProjectsEditor = ({
|
|
||||||
projects,
|
|
||||||
refetchProjects,
|
|
||||||
selectedProject,
|
|
||||||
setSelectedProject,
|
|
||||||
}: Props) => {
|
|
||||||
const {
|
|
||||||
editingProjects,
|
|
||||||
handleEditClick,
|
|
||||||
handleDeleteClick,
|
|
||||||
handleCreateClick,
|
|
||||||
} = useProjectsTab({
|
|
||||||
refetchProjects,
|
|
||||||
});
|
|
||||||
|
|
||||||
const columns = useProjectsTableColumns({ editingProjects, selectedProject, setSelectedProject });
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className={styles["container-wrapper"]} style={{ flex: 1.5 }}>
|
|
||||||
<Stack gap={rem(10)}>
|
|
||||||
<Center>
|
|
||||||
<Title order={4}>Проекты</Title>
|
|
||||||
</Center>
|
|
||||||
|
|
||||||
<BaseTable
|
|
||||||
data={projects}
|
|
||||||
columns={columns}
|
|
||||||
|
|
||||||
restProps={
|
|
||||||
{
|
|
||||||
enableSorting: false,
|
|
||||||
enableColumnActions: false,
|
|
||||||
enableRowActions: true,
|
|
||||||
positionActionsColumn: "last",
|
|
||||||
|
|
||||||
renderRowActions: ({ row }) => (
|
|
||||||
<Flex gap="md">
|
|
||||||
<Tooltip label="Редактировать">
|
|
||||||
<ActionIcon
|
|
||||||
onClick={() => handleEditClick(row.original)}
|
|
||||||
variant={"default"}>
|
|
||||||
{
|
|
||||||
editingProjects.has(row.original.id) ? (
|
|
||||||
<IconCheck />
|
|
||||||
) : (
|
|
||||||
<IconEdit />
|
|
||||||
)
|
|
||||||
}
|
|
||||||
</ActionIcon>
|
|
||||||
</Tooltip>
|
|
||||||
<Tooltip label={"Удалить"}>
|
|
||||||
<ActionIcon
|
|
||||||
onClick={() => handleDeleteClick(row.original)}
|
|
||||||
disabled={row.original.boardsCount > 0}
|
|
||||||
variant={"default"}>
|
|
||||||
<IconTrash />
|
|
||||||
</ActionIcon>
|
|
||||||
</Tooltip>
|
|
||||||
</Flex>
|
|
||||||
),
|
|
||||||
} as MRT_TableOptions<FullProjectSchema>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<InlineButton
|
|
||||||
variant={"default"}
|
|
||||||
onClick={handleCreateClick}
|
|
||||||
style={{ border: "dashed var(--item-border-size) var(--mantine-color-default-border)" }}
|
|
||||||
>
|
|
||||||
<IconPlus />
|
|
||||||
Добавить
|
|
||||||
</InlineButton>
|
|
||||||
</Stack>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default ProjectsEditor;
|
|
||||||
@@ -1,83 +0,0 @@
|
|||||||
import { ProjectSchema, ProjectService } from "../../../../../../../client";
|
|
||||||
import { notifications } from "../../../../../../../shared/lib/notifications.ts";
|
|
||||||
import { useProjectsEditorContext } from "../../../../../contexts/ProjectsEditorContext.tsx";
|
|
||||||
import { useMap } from "@mantine/hooks";
|
|
||||||
import { modals } from "@mantine/modals";
|
|
||||||
|
|
||||||
type Props = {
|
|
||||||
refetchProjects: () => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
const useProjectsTab = ({ refetchProjects }: Props) => {
|
|
||||||
const { onUpdate } = useProjectsEditorContext();
|
|
||||||
const editingProjects = useMap<number, ProjectSchema>();
|
|
||||||
|
|
||||||
const updateProject = (project: ProjectSchema) => {
|
|
||||||
ProjectService.updateProject({
|
|
||||||
requestBody: {
|
|
||||||
project,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
.then(({ ok, message }) => {
|
|
||||||
if (!ok) {
|
|
||||||
notifications.error({ message });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
editingProjects.delete(project.id);
|
|
||||||
refetchProjects();
|
|
||||||
onUpdate();
|
|
||||||
})
|
|
||||||
.catch(err => console.log(err));
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleEditClick = (project: ProjectSchema) => {
|
|
||||||
const editedProject = editingProjects.get(project.id);
|
|
||||||
|
|
||||||
if (!editedProject) {
|
|
||||||
editingProjects.set(project.id, project);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (editedProject.name.length === 0) {
|
|
||||||
notifications.error({ message: "Имя проекта не может быть пустым" });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
updateProject(project);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleDeleteClick = (project: ProjectSchema) => {
|
|
||||||
ProjectService.deleteProject({
|
|
||||||
projectId: project.id,
|
|
||||||
})
|
|
||||||
.then(({ ok, message }) => {
|
|
||||||
if (!ok) {
|
|
||||||
notifications.error({ message });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
refetchProjects();
|
|
||||||
onUpdate();
|
|
||||||
})
|
|
||||||
.catch(err => console.log(err));
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleCreateClick = () => {
|
|
||||||
modals.openContextModal({
|
|
||||||
modal: "createProjectModal",
|
|
||||||
title: "Создание проекта",
|
|
||||||
withCloseButton: false,
|
|
||||||
innerProps: {
|
|
||||||
refetchProjects,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
return {
|
|
||||||
editingProjects,
|
|
||||||
handleEditClick,
|
|
||||||
handleDeleteClick,
|
|
||||||
handleCreateClick,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export default useProjectsTab;
|
|
||||||
@@ -1,18 +1,17 @@
|
|||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { BoardSchema, BoardService } from "../../../client";
|
import { BoardSchema, BoardService } from "../../../client";
|
||||||
|
import { useProjectsContext } from "../../../contexts/ProjectsContext.tsx";
|
||||||
|
|
||||||
type Props = {
|
|
||||||
projectId?: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
const useBoards = ({ projectId }: Props) => {
|
const useBoards = () => {
|
||||||
|
const { selectedProject } = useProjectsContext();
|
||||||
const [boards, setBoards] = useState<BoardSchema[]>([]);
|
const [boards, setBoards] = useState<BoardSchema[]>([]);
|
||||||
|
|
||||||
const refetchBoards = () => {
|
const refetchBoards = () => {
|
||||||
if (!projectId) return;
|
if (!selectedProject) return;
|
||||||
|
|
||||||
BoardService.getBoards({
|
BoardService.getBoards({
|
||||||
projectId,
|
projectId: selectedProject.id,
|
||||||
})
|
})
|
||||||
.then(data => {
|
.then(data => {
|
||||||
setBoards(data.boards);
|
setBoards(data.boards);
|
||||||
@@ -22,7 +21,7 @@ const useBoards = ({ projectId }: Props) => {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
refetchBoards();
|
refetchBoards();
|
||||||
}, [projectId]);
|
}, [selectedProject]);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
boards,
|
boards,
|
||||||
|
|||||||
@@ -3,28 +3,21 @@ import { useForm } from "@mantine/form";
|
|||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { BaseMarketplaceSchema, BoardSchema, ClientSchema, ProjectSchema, StatusSchema } from "../../../client";
|
import { BaseMarketplaceSchema, BoardSchema, ClientSchema, ProjectSchema, StatusSchema } from "../../../client";
|
||||||
|
|
||||||
|
|
||||||
type Props = {
|
|
||||||
projects: ProjectSchema[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export type CardsPageState = {
|
export type CardsPageState = {
|
||||||
id: number | null;
|
id: number | null;
|
||||||
marketplace: BaseMarketplaceSchema | null;
|
marketplace: BaseMarketplaceSchema | null;
|
||||||
client: ClientSchema | null;
|
client: ClientSchema | null;
|
||||||
project: ProjectSchema | null;
|
|
||||||
|
|
||||||
projectForTable: ProjectSchema | null;
|
projectForTable: ProjectSchema | null;
|
||||||
board: BoardSchema | null;
|
board: BoardSchema | null;
|
||||||
status: StatusSchema | null;
|
status: StatusSchema | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
const useCardsPageState = ({ projects }: Props) => {
|
const useCardsPageState = () => {
|
||||||
const { objects } = useCardSummariesFull();
|
const { objects } = useCardSummariesFull();
|
||||||
|
|
||||||
const form = useForm<CardsPageState>({
|
const form = useForm<CardsPageState>({
|
||||||
initialValues: {
|
initialValues: {
|
||||||
project: null,
|
|
||||||
id: null,
|
id: null,
|
||||||
marketplace: null,
|
marketplace: null,
|
||||||
client: null,
|
client: null,
|
||||||
@@ -51,7 +44,7 @@ const useCardsPageState = ({ projects }: Props) => {
|
|||||||
}
|
}
|
||||||
if (form.values.projectForTable) {
|
if (form.values.projectForTable) {
|
||||||
result = result.filter(
|
result = result.filter(
|
||||||
obj => obj.board.projectId === form.values.project?.id,
|
obj => obj.board.projectId === form.values.projectForTable?.id,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (form.values.board) {
|
if (form.values.board) {
|
||||||
@@ -78,12 +71,6 @@ const useCardsPageState = ({ projects }: Props) => {
|
|||||||
applyFilters();
|
applyFilters();
|
||||||
}, [form.values, objects]);
|
}, [form.values, objects]);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (projects.length > 0 && form.values.project === null) {
|
|
||||||
form.setFieldValue("project", projects[0]);
|
|
||||||
}
|
|
||||||
}, [projects]);
|
|
||||||
|
|
||||||
return { data, form };
|
return { data, form };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -13,16 +13,14 @@ 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 useProjects from "../hooks/useProjects.tsx";
|
|
||||||
import Boards from "../../../components/Dnd/Boards/Boards/Boards.tsx";
|
import Boards from "../../../components/Dnd/Boards/Boards/Boards.tsx";
|
||||||
import useBoards from "../hooks/useBoards.tsx";
|
import useBoards from "../hooks/useBoards.tsx";
|
||||||
import { ProjectsEditorContextProvider } from "../contexts/ProjectsEditorContext.tsx";
|
import { ProjectsEditorContextProvider } from "../contexts/ProjectsEditorContext.tsx";
|
||||||
import ProjectsEditorDrawer from "../drawers/ProjectsEditorDrawer/ProjectsEditorDrawer.tsx";
|
import ProjectEditDrawer from "../drawers/ProjectEditDrawer/ProjectEditDrawer.tsx";
|
||||||
|
|
||||||
export const CardsPage: FC = () => {
|
export const CardsPage: FC = () => {
|
||||||
const { projects, refetchProjects } = useProjects();
|
const { data, form } = useCardsPageState();
|
||||||
const { data, form } = useCardsPageState({ projects });
|
const { boards, refetchBoards } = useBoards();
|
||||||
const { boards, refetchBoards } = useBoards({ projectId: form.values.project?.id });
|
|
||||||
const { dealId } = useParams({ strict: false });
|
const { dealId } = useParams({ strict: false });
|
||||||
const { summariesRaw, refetch: refetchSummaries } = useCardSummaries();
|
const { summariesRaw, refetch: refetchSummaries } = useCardSummaries();
|
||||||
|
|
||||||
@@ -74,39 +72,37 @@ export const CardsPage: FC = () => {
|
|||||||
padding: 0,
|
padding: 0,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<CardPageContextProvider
|
<ProjectsEditorContextProvider>
|
||||||
defaultCardId={(dealId && parseInt(dealId)) || undefined}
|
<CardPageContextProvider
|
||||||
refetchCards={async () => {
|
defaultCardId={(dealId && parseInt(dealId)) || undefined}
|
||||||
await refetchSummaries();
|
refetchCards={async () => {
|
||||||
}}
|
await refetchSummaries();
|
||||||
selectedProject={form.values.project}
|
}}
|
||||||
>
|
>
|
||||||
<PrefillCardContextProvider>
|
<PrefillCardContextProvider>
|
||||||
<PrefillCardsWithExcelContextProvider>
|
<PrefillCardsWithExcelContextProvider>
|
||||||
<ProjectsEditorContextProvider onUpdate={refetchProjects}>
|
|
||||||
<CardsPageHeader
|
<CardsPageHeader
|
||||||
form={form}
|
form={form}
|
||||||
displayMode={displayMode}
|
displayMode={displayMode}
|
||||||
setDisplayMode={setDisplayMode}
|
setDisplayMode={setDisplayMode}
|
||||||
projects={projects}
|
|
||||||
/>
|
/>
|
||||||
<ProjectsEditorDrawer />
|
<PageBlock
|
||||||
</ProjectsEditorContextProvider>
|
style={{
|
||||||
<PageBlock
|
display: "flex",
|
||||||
style={{
|
flexDirection: "column",
|
||||||
display: "flex",
|
flex: 1,
|
||||||
flexDirection: "column",
|
height: "100%",
|
||||||
flex: 1,
|
}}
|
||||||
height: "100%",
|
>
|
||||||
}}
|
{getBody()}
|
||||||
>
|
</PageBlock>
|
||||||
{getBody()}
|
<CardEditDrawer />
|
||||||
</PageBlock>
|
<ProjectEditDrawer />
|
||||||
<CardEditDrawer />
|
<CardPrefillDrawer />
|
||||||
<CardPrefillDrawer />
|
</PrefillCardsWithExcelContextProvider>
|
||||||
</PrefillCardsWithExcelContextProvider>
|
</PrefillCardContextProvider>
|
||||||
</PrefillCardContextProvider>
|
</CardPageContextProvider>
|
||||||
</CardPageContextProvider>
|
</ProjectsEditorContextProvider>
|
||||||
</PageBlock>
|
</PageBlock>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ export enum Modules {
|
|||||||
EMPLOYEES = "employees",
|
EMPLOYEES = "employees",
|
||||||
CLIENTS = "clients",
|
CLIENTS = "clients",
|
||||||
MANAGERS = "managers",
|
MANAGERS = "managers",
|
||||||
|
MEGA_MODULE = "hui",
|
||||||
}
|
}
|
||||||
|
|
||||||
const isModuleInProject = (module: Modules, project?: ProjectSchema | null) => {
|
const isModuleInProject = (module: Modules, project?: ProjectSchema | null) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user