feat: departments and department sections
This commit is contained in:
@@ -2,14 +2,13 @@ import styles from "./AdminPage.module.css";
|
||||
import { Tabs } from "@mantine/core";
|
||||
import PageBlock from "../../components/PageBlock/PageBlock.tsx";
|
||||
import {
|
||||
IconBriefcase,
|
||||
IconTopologyStar3,
|
||||
IconCalendarUser,
|
||||
IconCoins,
|
||||
IconCurrencyDollar,
|
||||
IconQrcode,
|
||||
IconUser,
|
||||
} from "@tabler/icons-react";
|
||||
import RolesAndPositionsTab from "./tabs/RolesAndPositions/RolesAndPositionsTab.tsx";
|
||||
import UsersTab from "./tabs/Users/UsersTab.tsx";
|
||||
import { motion } from "framer-motion";
|
||||
import FinancesTab from "./tabs/Finances/FinancesTab.tsx";
|
||||
@@ -18,6 +17,7 @@ import { WorkShiftsTab } from "./tabs/WorkShifts/WorkShiftsTab.tsx";
|
||||
import { TransactionsTab } from "./tabs/Transactions/TransactionsTab.tsx";
|
||||
import { useSelector } from "react-redux";
|
||||
import { RootState } from "../../redux/store.ts";
|
||||
import OrganizationalStructureTab from "./tabs/OrganizationalStructureTab/OrganizationalStructureTab.tsx";
|
||||
|
||||
const AdminPage = () => {
|
||||
const userRole = useSelector((state: RootState) => state.auth.role);
|
||||
@@ -45,8 +45,8 @@ const AdminPage = () => {
|
||||
)}
|
||||
<Tabs.Tab
|
||||
value={"rolesAndPositions"}
|
||||
leftSection={<IconBriefcase />}>
|
||||
Должности
|
||||
leftSection={<IconTopologyStar3 />}>
|
||||
Организационная структура
|
||||
</Tabs.Tab>
|
||||
{isAdmin && (
|
||||
<Tabs.Tab
|
||||
@@ -81,7 +81,7 @@ const AdminPage = () => {
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
transition={{ duration: 0.2 }}>
|
||||
<RolesAndPositionsTab />
|
||||
<OrganizationalStructureTab />
|
||||
</motion.div>
|
||||
</Tabs.Panel>
|
||||
<Tabs.Panel value={"finances"}>
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
import { Tabs } from "@mantine/core";
|
||||
import { IconBriefcase, IconSitemap } from "@tabler/icons-react";
|
||||
import { motion } from "framer-motion";
|
||||
import RolesAndPositions from "./components/RolesAndPositions.tsx";
|
||||
import Departments from "./components/Departments.tsx";
|
||||
|
||||
|
||||
const OrganizationalStructureTab = () => {
|
||||
return (
|
||||
<Tabs
|
||||
keepMounted={false}
|
||||
defaultValue={"departments"}
|
||||
color={"gray.6"}>
|
||||
<Tabs.List
|
||||
justify={"center"}
|
||||
grow>
|
||||
<Tabs.Tab
|
||||
value={"departments"}
|
||||
leftSection={<IconSitemap />}>
|
||||
Департаменты и отделы
|
||||
</Tabs.Tab>
|
||||
<Tabs.Tab
|
||||
value={"positions"}
|
||||
leftSection={<IconBriefcase />}>
|
||||
Должности
|
||||
</Tabs.Tab>
|
||||
</Tabs.List>
|
||||
<Tabs.Panel value={"departments"}>
|
||||
<motion.div
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
transition={{ duration: 0.2 }}>
|
||||
<Departments />
|
||||
</motion.div>
|
||||
</Tabs.Panel>
|
||||
<Tabs.Panel value={"positions"}>
|
||||
<motion.div
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
transition={{ duration: 0.2 }}>
|
||||
<RolesAndPositions />
|
||||
</motion.div>
|
||||
</Tabs.Panel>
|
||||
</Tabs>
|
||||
);
|
||||
};
|
||||
|
||||
export default OrganizationalStructureTab;
|
||||
@@ -0,0 +1,35 @@
|
||||
import { Button, Group, rem } from "@mantine/core";
|
||||
import { IconChevronsDown, IconChevronsUp, IconPlus } from "@tabler/icons-react";
|
||||
import { useDepartmentContext } from "../contexts/DepartmentContext.tsx";
|
||||
|
||||
|
||||
const DepartmentButtons = () => {
|
||||
const {
|
||||
departmentIds,
|
||||
toggleAllDepartmentIds,
|
||||
onCreateDepartmentClick,
|
||||
} = useDepartmentContext();
|
||||
|
||||
|
||||
return (
|
||||
<Group gap={"md"}>
|
||||
<Button
|
||||
variant={"default"}
|
||||
onClick={toggleAllDepartmentIds}
|
||||
>
|
||||
{departmentIds.length > 0 ? <IconChevronsUp /> : <IconChevronsDown />}
|
||||
</Button>
|
||||
<Button
|
||||
variant={"default"}
|
||||
onClick={() => onCreateDepartmentClick(false)}
|
||||
>
|
||||
<Group gap={rem(10)}>
|
||||
<IconPlus />
|
||||
Добавить департамент
|
||||
</Group>
|
||||
</Button>
|
||||
</Group>
|
||||
);
|
||||
};
|
||||
|
||||
export default DepartmentButtons;
|
||||
@@ -0,0 +1,17 @@
|
||||
import { Stack } from "@mantine/core";
|
||||
import DepartmentsTree from "./DepartmentsTree.tsx";
|
||||
import DepartmentButtons from "./DepartmentButtons.tsx";
|
||||
import { DepartmentContextProvider } from "../contexts/DepartmentContext.tsx";
|
||||
|
||||
const Departments = () => {
|
||||
return (
|
||||
<DepartmentContextProvider>
|
||||
<Stack mt={"md"}>
|
||||
<DepartmentButtons />
|
||||
<DepartmentsTree />
|
||||
</Stack>
|
||||
</DepartmentContextProvider>
|
||||
);
|
||||
};
|
||||
|
||||
export default Departments;
|
||||
@@ -0,0 +1,139 @@
|
||||
import { Accordion, ActionIcon, Center, Flex, Text, Title, Tooltip } from "@mantine/core";
|
||||
import { IconEdit, IconPlaylistAdd, IconTrash, IconUserPlus } from "@tabler/icons-react";
|
||||
import { DepartmentSchema, DepartmentSectionSchema } from "../../../../../client";
|
||||
import { useDepartmentContext } from "../contexts/DepartmentContext.tsx";
|
||||
import UsersTable from "./UsersTable.tsx";
|
||||
import { ReactNode } from "react";
|
||||
|
||||
|
||||
const DepartmentsTree = () => {
|
||||
const {
|
||||
departments,
|
||||
departmentIds,
|
||||
setDepartmentIds,
|
||||
onCreateDepartmentClick,
|
||||
onUpdateDepartmentClick,
|
||||
onDeleteDepartmentClick,
|
||||
onAddUserClick,
|
||||
} = useDepartmentContext();
|
||||
|
||||
const getAction = (label: string, func: () => void, icon: ReactNode) => {
|
||||
return (
|
||||
<Tooltip label={label} key={label}>
|
||||
<ActionIcon
|
||||
variant={"default"}
|
||||
onClick={func}
|
||||
>
|
||||
{icon}
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
);
|
||||
};
|
||||
|
||||
const getActions = (
|
||||
element: DepartmentSectionSchema | DepartmentSchema,
|
||||
isSection: boolean,
|
||||
) => {
|
||||
const actions = [
|
||||
isSection ? (
|
||||
getAction(
|
||||
"Добавить пользователя в отдел",
|
||||
() => onAddUserClick(element as DepartmentSectionSchema),
|
||||
<IconUserPlus />,
|
||||
)
|
||||
) : (
|
||||
getAction(
|
||||
"Добавить отдел",
|
||||
() => onCreateDepartmentClick(true, { departmentId: element.id, name: "" }),
|
||||
<IconPlaylistAdd />,
|
||||
)
|
||||
),
|
||||
getAction(
|
||||
"Редактировать",
|
||||
() => onUpdateDepartmentClick(element, isSection),
|
||||
<IconEdit />,
|
||||
),
|
||||
getAction(
|
||||
"Удалить",
|
||||
() => onDeleteDepartmentClick(element, isSection),
|
||||
<IconTrash />,
|
||||
),
|
||||
];
|
||||
|
||||
return (
|
||||
<Flex gap={"md"} mx={"md"} direction={"row"}>
|
||||
{...actions}
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
const getUsersTable = (section: DepartmentSectionSchema) => {
|
||||
if (!section.users || section.users.length === 0) {
|
||||
return <Text>Сотрудники не добавлены</Text>;
|
||||
}
|
||||
return <UsersTable users={section.users} sectionId={section.id}/>;
|
||||
};
|
||||
|
||||
const getDepartmentSections = (department: DepartmentSchema) => {
|
||||
if (!department.sections || department.sections.length === 0) return;
|
||||
|
||||
const accordionIds: string[] = [];
|
||||
const sortedSections: DepartmentSectionSchema[] = department.sections?.sort((a, b) => a.id - b.id) ?? [];
|
||||
const items = sortedSections.map(section => {
|
||||
const value = `section ${section.id}`;
|
||||
accordionIds.push(value);
|
||||
return (
|
||||
<Accordion.Item key={section.id} value={value}>
|
||||
<Center>
|
||||
<Accordion.Control>
|
||||
<Title order={4}>Отдел - {section.name}</Title>
|
||||
</Accordion.Control>
|
||||
{getActions(section, true)}
|
||||
</Center>
|
||||
<Accordion.Panel>
|
||||
{getUsersTable(section)}
|
||||
</Accordion.Panel>
|
||||
</Accordion.Item>
|
||||
);
|
||||
});
|
||||
|
||||
return (
|
||||
<Accordion
|
||||
multiple={true}
|
||||
defaultValue={accordionIds}
|
||||
bd={"solid 1px gray"}
|
||||
>
|
||||
{items}
|
||||
</Accordion>
|
||||
);
|
||||
};
|
||||
|
||||
const getDepartments = departments.map((department) => {
|
||||
return (
|
||||
<Accordion.Item key={department.id} value={department.id.toString()}>
|
||||
<Center>
|
||||
<Accordion.Control>
|
||||
<Title order={4}>Департамент - {department.name}</Title>
|
||||
</Accordion.Control>
|
||||
{getActions(department, false)}
|
||||
</Center>
|
||||
<Accordion.Panel>
|
||||
{getDepartmentSections(department)}
|
||||
</Accordion.Panel>
|
||||
</Accordion.Item>
|
||||
);
|
||||
});
|
||||
|
||||
return (
|
||||
<Accordion
|
||||
multiple={true}
|
||||
value={departmentIds}
|
||||
onChange={value => setDepartmentIds(value)}
|
||||
bd={"solid 1px gray"}
|
||||
>
|
||||
{getDepartments}
|
||||
</Accordion>
|
||||
);
|
||||
};
|
||||
|
||||
export default DepartmentsTree;
|
||||
@@ -1,9 +1,9 @@
|
||||
import PositionsTable from "../../components/PositionsTable/PositionsTable.tsx";
|
||||
import usePositionsList from "../../hooks/usePositionsList.tsx";
|
||||
import { PositionSchema, PositionService } from "../../../../client";
|
||||
import { notifications } from "../../../../shared/lib/notifications.ts";
|
||||
import PositionsTable from "../../../components/PositionsTable/PositionsTable.tsx";
|
||||
import usePositionsList from "../../../hooks/usePositionsList.tsx";
|
||||
import { PositionSchema, PositionService } from "../../../../../client";
|
||||
import { notifications } from "../../../../../shared/lib/notifications.ts";
|
||||
|
||||
const RolesAndPositionsTab = () => {
|
||||
const RolesAndPositions = () => {
|
||||
const { objects: positions, refetch } = usePositionsList();
|
||||
const onCreate = (position: PositionSchema) => {
|
||||
PositionService.createPosition({
|
||||
@@ -36,4 +36,4 @@ const RolesAndPositionsTab = () => {
|
||||
/>
|
||||
);
|
||||
};
|
||||
export default RolesAndPositionsTab;
|
||||
export default RolesAndPositions;
|
||||
@@ -0,0 +1,27 @@
|
||||
import { FC } from "react";
|
||||
import ObjectSelect, { ObjectSelectProps } from "../../../../../components/ObjectSelect/ObjectSelect.tsx";
|
||||
import { UserSchema } from "../../../../../client";
|
||||
import useAvailableUsersList from "../hooks/useAvailableUsersList.tsx";
|
||||
|
||||
type SectionData = {
|
||||
sectionId: number;
|
||||
}
|
||||
|
||||
type Props = SectionData & Omit<
|
||||
ObjectSelectProps<UserSchema | null>,
|
||||
"data" | "getValueFn" | "getLabelFn"
|
||||
>;
|
||||
|
||||
const UserForDepartmentSelect: FC<Props> = props => {
|
||||
const { objects: users } = useAvailableUsersList({ sectionId: props.sectionId });
|
||||
|
||||
return (
|
||||
<ObjectSelect
|
||||
data={users}
|
||||
getLabelFn={(user: UserSchema) => `${user.firstName} ${user.secondName}`}
|
||||
getValueFn={(user: UserSchema) => user.id.toString()}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
};
|
||||
export default UserForDepartmentSelect;
|
||||
@@ -0,0 +1,63 @@
|
||||
import { BaseTable } from "../../../../../components/BaseTable/BaseTable.tsx";
|
||||
import useUsersTableColumns from "../hooks/useUsersTableColumns.tsx";
|
||||
import { DepartmentService, UserSchema } from "../../../../../client";
|
||||
import { ActionIcon, Flex, Tooltip } from "@mantine/core";
|
||||
import { IconTrash } from "@tabler/icons-react";
|
||||
import { MRT_TableOptions } from "mantine-react-table";
|
||||
import { notifications } from "../../../../../shared/lib/notifications.ts";
|
||||
import { useDepartmentContext } from "../contexts/DepartmentContext.tsx";
|
||||
|
||||
|
||||
type Props = {
|
||||
users: UserSchema[];
|
||||
sectionId: number;
|
||||
}
|
||||
|
||||
const UsersTable = ({ users, sectionId }: Props) => {
|
||||
const columns = useUsersTableColumns();
|
||||
const { fetchDepartments } = useDepartmentContext();
|
||||
|
||||
const onDeleteUserClick = (userId: number) => {
|
||||
DepartmentService.deleteUser({
|
||||
requestBody: {
|
||||
sectionId,
|
||||
userId,
|
||||
}
|
||||
})
|
||||
.then(({ok, message}) => {
|
||||
fetchDepartments();
|
||||
if (ok) return;
|
||||
notifications.error({ message });
|
||||
})
|
||||
.catch(err => console.log(err));
|
||||
}
|
||||
|
||||
return (
|
||||
<BaseTable
|
||||
data={users}
|
||||
columns={columns}
|
||||
|
||||
restProps={
|
||||
{
|
||||
enableSorting: false,
|
||||
enableColumnActions: false,
|
||||
enableRowActions: true,
|
||||
positionActionsColumn: "last",
|
||||
renderRowActions: ({ row }) => (
|
||||
<Flex gap="md">
|
||||
<Tooltip label="Удалить">
|
||||
<ActionIcon
|
||||
onClick={() => onDeleteUserClick(row.original.id)}
|
||||
variant={"default"}>
|
||||
<IconTrash />
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
</Flex>
|
||||
),
|
||||
} as MRT_TableOptions<UserSchema>
|
||||
}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export default UsersTable;
|
||||
@@ -0,0 +1,153 @@
|
||||
import React, { createContext, FC, useContext, useEffect, useState } from "react";
|
||||
import {
|
||||
DepartmentSchema,
|
||||
DepartmentSectionBaseSchema,
|
||||
DepartmentSectionSchema,
|
||||
DepartmentService,
|
||||
} from "../../../../../client";
|
||||
import { modals } from "@mantine/modals";
|
||||
import { Text } from "@mantine/core";
|
||||
import useDepartmentCrud from "../hooks/useDepartmentCrud.tsx";
|
||||
import useDepartmentSectionCrud from "../hooks/useDepartmentSectionCrud.tsx";
|
||||
|
||||
type DepartmentContextState = {
|
||||
departments: DepartmentSchema[],
|
||||
departmentIds: string[],
|
||||
setDepartmentIds: React.Dispatch<React.SetStateAction<string[]>>,
|
||||
fetchDepartments: () => void,
|
||||
onCreateDepartmentClick: (isSection: boolean, element?: DepartmentSectionBaseSchema) => void,
|
||||
onUpdateDepartmentClick: (element: DepartmentSchema | DepartmentSectionSchema, isSection: boolean) => void,
|
||||
onDeleteDepartmentClick: (element: DepartmentSchema, isSection: boolean) => void,
|
||||
onAddUserClick: (departmentSection: DepartmentSectionSchema) => void,
|
||||
toggleAllDepartmentIds: () => void,
|
||||
};
|
||||
|
||||
const DepartmentContext = createContext<DepartmentContextState | undefined>(
|
||||
undefined,
|
||||
);
|
||||
|
||||
const useDepartmentContextState = () => {
|
||||
const [departments, setDepartments] = useState<DepartmentSchema[]>([]);
|
||||
const [departmentIds, setDepartmentIds] = useState<string[]>([]);
|
||||
|
||||
const fetchDepartments = () => {
|
||||
DepartmentService.getDepartments()
|
||||
.then(res => {
|
||||
setDepartments(res.departments);
|
||||
if (departmentIds.length === 0) {
|
||||
setDepartmentIds(res.departments.map(dep => dep.id.toString()));
|
||||
}
|
||||
})
|
||||
.catch(e => console.log(e));
|
||||
};
|
||||
|
||||
const departmentsCrud = useDepartmentCrud({ fetchDepartments });
|
||||
const departmentSectionsCrud = useDepartmentSectionCrud({ fetchDepartments });
|
||||
|
||||
useEffect(() => {
|
||||
fetchDepartments();
|
||||
}, []);
|
||||
|
||||
const openDepartmentModal = (
|
||||
title: string,
|
||||
isDepartmentSection: boolean,
|
||||
element?: DepartmentSchema | DepartmentSectionSchema | DepartmentSectionBaseSchema,
|
||||
) => {
|
||||
modals.openContextModal({
|
||||
modal: "departmentModal",
|
||||
title,
|
||||
withCloseButton: false,
|
||||
innerProps: {
|
||||
departmentsCrud,
|
||||
departmentSectionsCrud,
|
||||
isDepartmentSection,
|
||||
element,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const onCreateDepartmentClick = (isSection: boolean, element?: DepartmentSectionBaseSchema) => {
|
||||
const title: string = isSection ? "Создание отдела" : "Создание департамента";
|
||||
openDepartmentModal(title, isSection, element);
|
||||
};
|
||||
|
||||
const onUpdateDepartmentClick = (element: DepartmentSchema | DepartmentSectionSchema, isSection: boolean) => {
|
||||
const title: string = isSection ? "Редактирование отдела" : "Редактирование департамента";
|
||||
openDepartmentModal(title, isSection, element);
|
||||
};
|
||||
|
||||
const onDeleteDepartmentClick = (element: DepartmentSchema | DepartmentSectionSchema, isSection: boolean) => {
|
||||
modals.openConfirmModal({
|
||||
title: "Удаление " + (isSection ? "отдела" : "департамента"),
|
||||
children: (
|
||||
<Text size="sm">
|
||||
Вы уверены что хотите удалить {isSection ? "отдел" : "департамент"} {element.name}?
|
||||
</Text>
|
||||
),
|
||||
labels: { confirm: "Да", cancel: "Нет" },
|
||||
confirmProps: { color: "red" },
|
||||
onConfirm: () => {
|
||||
if (isSection) {
|
||||
departmentSectionsCrud.onDelete(element as DepartmentSectionSchema);
|
||||
} else {
|
||||
departmentsCrud.onDelete(element);
|
||||
}
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const onAddUserClick = (departmentSection: DepartmentSectionSchema) => {
|
||||
modals.openContextModal({
|
||||
modal: "addUserToDepartmentModal",
|
||||
title: `Добавление пользователя в отдел ${departmentSection.name}`,
|
||||
withCloseButton: false,
|
||||
innerProps: {
|
||||
departmentSection,
|
||||
fetchDepartments,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
const toggleAllDepartmentIds = () => {
|
||||
if (departmentIds.length > 0) {
|
||||
setDepartmentIds([]);
|
||||
return;
|
||||
}
|
||||
setDepartmentIds(departments.map(department => department.id.toString()));
|
||||
}
|
||||
|
||||
return {
|
||||
departments,
|
||||
departmentIds,
|
||||
setDepartmentIds,
|
||||
fetchDepartments,
|
||||
onCreateDepartmentClick,
|
||||
onUpdateDepartmentClick,
|
||||
onDeleteDepartmentClick,
|
||||
onAddUserClick,
|
||||
toggleAllDepartmentIds,
|
||||
};
|
||||
};
|
||||
|
||||
type DepartmentContextProviderProps = {
|
||||
children: React.ReactNode;
|
||||
};
|
||||
|
||||
export const DepartmentContextProvider: FC<DepartmentContextProviderProps> = ({ children }) => {
|
||||
const state = useDepartmentContextState();
|
||||
return (
|
||||
<DepartmentContext.Provider value={state}>
|
||||
{children}
|
||||
</DepartmentContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
export const useDepartmentContext = () => {
|
||||
const context = useContext(DepartmentContext);
|
||||
if (!context) {
|
||||
throw new Error(
|
||||
"useDepartmentContext must be used within a DepartmentContextProvider",
|
||||
);
|
||||
}
|
||||
return context;
|
||||
};
|
||||
@@ -0,0 +1,16 @@
|
||||
import { DepartmentService } from "../../../../../client";
|
||||
import ObjectList from "../../../../../hooks/objectList.tsx";
|
||||
|
||||
|
||||
type Props = {
|
||||
sectionId: number;
|
||||
}
|
||||
|
||||
const useAvailableUsersList = ({ sectionId }: Props) =>
|
||||
ObjectList({
|
||||
queryFn: () => DepartmentService.getAvailableUsersForSection({ sectionId }),
|
||||
getObjectsFn: response => response.users,
|
||||
queryKey: "getAvailableUsersForDepartmentSection",
|
||||
});
|
||||
|
||||
export default useAvailableUsersList;
|
||||
@@ -0,0 +1,49 @@
|
||||
import { useCRUD } from "../../../../../hooks/useCRUD.tsx";
|
||||
import { DepartmentBaseSchema, DepartmentSchema, DepartmentService } from "../../../../../client";
|
||||
import { notifications } from "../../../../../shared/lib/notifications.ts";
|
||||
|
||||
|
||||
export type DepartmentCrud = {
|
||||
onCreate: (element: DepartmentBaseSchema) => void,
|
||||
onDelete: (element: DepartmentSchema) => void,
|
||||
onChange: (element: DepartmentSchema) => void
|
||||
}
|
||||
|
||||
type Props = {
|
||||
fetchDepartments: () => void;
|
||||
}
|
||||
|
||||
const useDepartmentCrud = ({ fetchDepartments }: Props): DepartmentCrud => {
|
||||
return useCRUD<DepartmentSchema, DepartmentBaseSchema>({
|
||||
onChange: department => {
|
||||
DepartmentService.updateDepartment({
|
||||
requestBody: { department },
|
||||
})
|
||||
.then(({ ok, message }) => {
|
||||
notifications.guess(ok, { message });
|
||||
fetchDepartments();
|
||||
})
|
||||
.catch(err => console.log(err));
|
||||
},
|
||||
onCreate: department => {
|
||||
DepartmentService.createDepartment({
|
||||
requestBody: { department },
|
||||
})
|
||||
.then(({ ok, message }) => {
|
||||
notifications.guess(ok, { message });
|
||||
fetchDepartments();
|
||||
})
|
||||
.catch(err => console.log(err));
|
||||
},
|
||||
onDelete: department => {
|
||||
DepartmentService.deleteDepartment({ departmentId: department.id })
|
||||
.then(({ ok, message }) => {
|
||||
notifications.guess(ok, { message });
|
||||
fetchDepartments();
|
||||
})
|
||||
.catch(err => console.log(err));
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export default useDepartmentCrud;
|
||||
@@ -0,0 +1,49 @@
|
||||
import { useCRUD } from "../../../../../hooks/useCRUD.tsx";
|
||||
import { DepartmentSectionBaseSchema, DepartmentSectionSchema, DepartmentService } from "../../../../../client";
|
||||
import { notifications } from "../../../../../shared/lib/notifications.ts";
|
||||
|
||||
|
||||
export type DepartmentSectionCrud = {
|
||||
onCreate: (element: DepartmentSectionBaseSchema) => void,
|
||||
onDelete: (element: DepartmentSectionSchema) => void,
|
||||
onChange: (element: DepartmentSectionSchema) => void
|
||||
}
|
||||
|
||||
type Props = {
|
||||
fetchDepartments: () => void;
|
||||
}
|
||||
|
||||
const useDepartmentSectionCrud = ({ fetchDepartments }: Props): DepartmentSectionCrud => {
|
||||
return useCRUD<DepartmentSectionSchema, DepartmentSectionBaseSchema>({
|
||||
onChange: section => {
|
||||
DepartmentService.updateSection({
|
||||
requestBody: { section },
|
||||
})
|
||||
.then(({ ok, message }) => {
|
||||
notifications.guess(ok, { message });
|
||||
fetchDepartments();
|
||||
})
|
||||
.catch(err => console.log(err));
|
||||
},
|
||||
onCreate: section => {
|
||||
DepartmentService.createSection({
|
||||
requestBody: { section },
|
||||
})
|
||||
.then(({ ok, message }) => {
|
||||
notifications.guess(ok, { message });
|
||||
fetchDepartments();
|
||||
})
|
||||
.catch(err => console.log(err));
|
||||
},
|
||||
onDelete: section => {
|
||||
DepartmentService.deleteSection({ sectionId: section.id })
|
||||
.then(({ ok, message }) => {
|
||||
notifications.guess(ok, { message });
|
||||
fetchDepartments();
|
||||
})
|
||||
.catch(err => console.log(err));
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export default useDepartmentSectionCrud;
|
||||
@@ -0,0 +1,31 @@
|
||||
import { useMemo } from "react";
|
||||
import { MRT_ColumnDef } from "mantine-react-table";
|
||||
import { UserSchema } from "../../../../../client";
|
||||
|
||||
|
||||
const useUsersTableColumns = () => {
|
||||
return useMemo<MRT_ColumnDef<UserSchema>[]>(
|
||||
() => [
|
||||
{
|
||||
header: "ФИО",
|
||||
Cell: ({ row }) =>
|
||||
`${row.original.secondName} ${row.original.firstName} ${row.original.patronymic}`,
|
||||
},
|
||||
{
|
||||
accessorKey: "role.name",
|
||||
header: "Роль",
|
||||
},
|
||||
{
|
||||
accessorKey: "position.name",
|
||||
header: "Должность",
|
||||
},
|
||||
{
|
||||
accessorKey: "comment",
|
||||
header: "Доп. информация",
|
||||
}
|
||||
],
|
||||
[],
|
||||
);
|
||||
};
|
||||
|
||||
export default useUsersTableColumns;
|
||||
@@ -0,0 +1,64 @@
|
||||
import { useForm } from "@mantine/form";
|
||||
import { ContextModalProps } from "@mantine/modals";
|
||||
import { Button, Flex, rem } from "@mantine/core";
|
||||
import { DepartmentSectionSchema, DepartmentService } from "../../../../../client";
|
||||
import AddUserToDepartmentModalForm from "../types/AddUserToDepartmentModalForm.tsx";
|
||||
import { notifications } from "../../../../../shared/lib/notifications.ts";
|
||||
import UserForDepartmentSelect from "../components/UserForDepartmentSelect.tsx";
|
||||
|
||||
type Props = {
|
||||
departmentSection: DepartmentSectionSchema;
|
||||
fetchDepartments: () => void;
|
||||
}
|
||||
|
||||
const AddUserToDepartmentModal = ({
|
||||
context,
|
||||
id,
|
||||
innerProps,
|
||||
}: ContextModalProps<Props>) => {
|
||||
const form = useForm<Partial<AddUserToDepartmentModalForm>>({
|
||||
validate: {
|
||||
user: user => !user && "Необходимо выбрать пользователя",
|
||||
},
|
||||
});
|
||||
|
||||
const onSubmit = () => {
|
||||
if (!form.values.user) return;
|
||||
DepartmentService.addUser({
|
||||
requestBody: {
|
||||
userId: form.values.user.id,
|
||||
sectionId: innerProps.departmentSection.id,
|
||||
}
|
||||
})
|
||||
.then(({ ok, message }) => {
|
||||
notifications.guess(ok, { message });
|
||||
innerProps.fetchDepartments();
|
||||
})
|
||||
.catch(err => console.log(err));
|
||||
|
||||
context.closeContextModal(id);
|
||||
};
|
||||
|
||||
return (
|
||||
<form onSubmit={form.onSubmit(() => onSubmit())}>
|
||||
<Flex
|
||||
direction={"column"}
|
||||
gap={rem(10)}
|
||||
>
|
||||
<UserForDepartmentSelect
|
||||
label={"Пользователь"}
|
||||
{...form.getInputProps("user")}
|
||||
sectionId={innerProps.departmentSection.id}
|
||||
/>
|
||||
<Button
|
||||
variant={"default"}
|
||||
type={"submit"}
|
||||
>
|
||||
Сохранить
|
||||
</Button>
|
||||
</Flex>
|
||||
</form>
|
||||
);
|
||||
};
|
||||
|
||||
export default AddUserToDepartmentModal;
|
||||
@@ -0,0 +1,79 @@
|
||||
import { useForm } from "@mantine/form";
|
||||
import { ContextModalProps } from "@mantine/modals";
|
||||
import { Button, Flex, rem, TextInput } from "@mantine/core";
|
||||
import { DepartmentModalForm } from "../types/DepartmentModalForm.tsx";
|
||||
import { DepartmentSchema, DepartmentSectionBaseSchema, DepartmentSectionSchema } from "../../../../../client";
|
||||
import { DepartmentCrud } from "../hooks/useDepartmentCrud.tsx";
|
||||
import { DepartmentSectionCrud } from "../hooks/useDepartmentSectionCrud.tsx";
|
||||
|
||||
type Props = {
|
||||
departmentsCrud: DepartmentCrud;
|
||||
departmentSectionsCrud: DepartmentSectionCrud;
|
||||
element?: DepartmentSchema | DepartmentSectionSchema | DepartmentSectionBaseSchema;
|
||||
isDepartmentSection: boolean;
|
||||
}
|
||||
|
||||
const DepartmentModal = ({
|
||||
context,
|
||||
id,
|
||||
innerProps,
|
||||
}: ContextModalProps<Props>) => {
|
||||
const {
|
||||
departmentsCrud,
|
||||
departmentSectionsCrud,
|
||||
element,
|
||||
isDepartmentSection,
|
||||
} = innerProps;
|
||||
|
||||
const initialValues: DepartmentModalForm = {
|
||||
name: element?.name ?? "",
|
||||
};
|
||||
const form = useForm<DepartmentModalForm>({
|
||||
initialValues,
|
||||
validate: {
|
||||
name: name => !name && "Необходимо указать название",
|
||||
},
|
||||
});
|
||||
|
||||
const onSubmit = () => {
|
||||
if (isDepartmentSection) {
|
||||
const sectionData = { ...element as DepartmentSectionSchema, ...form.values };
|
||||
if (sectionData.id) {
|
||||
departmentSectionsCrud.onChange(sectionData);
|
||||
} else {
|
||||
departmentSectionsCrud.onCreate(sectionData);
|
||||
}
|
||||
} else {
|
||||
const departmentData = element as DepartmentSchema;
|
||||
if (element) {
|
||||
departmentsCrud.onChange({ ...form.values, id: departmentData.id });
|
||||
} else {
|
||||
departmentsCrud.onCreate(form.values);
|
||||
}
|
||||
}
|
||||
context.closeContextModal(id);
|
||||
};
|
||||
|
||||
return (
|
||||
<form onSubmit={form.onSubmit(() => onSubmit())}>
|
||||
<Flex
|
||||
direction={"column"}
|
||||
gap={rem(10)}
|
||||
>
|
||||
<TextInput
|
||||
label={"Название"}
|
||||
{...form.getInputProps("name")}
|
||||
/>
|
||||
|
||||
<Button
|
||||
variant={"default"}
|
||||
type={"submit"}
|
||||
>
|
||||
Сохранить
|
||||
</Button>
|
||||
</Flex>
|
||||
</form>
|
||||
);
|
||||
};
|
||||
|
||||
export default DepartmentModal;
|
||||
@@ -0,0 +1,7 @@
|
||||
import { UserSchema } from "../../../../../client";
|
||||
|
||||
type AddUserToDepartmentModalForm = {
|
||||
user: UserSchema;
|
||||
}
|
||||
|
||||
export default AddUserToDepartmentModalForm;
|
||||
@@ -0,0 +1,3 @@
|
||||
export type DepartmentModalForm = {
|
||||
name: string;
|
||||
}
|
||||
Reference in New Issue
Block a user