From b3919c785b8968279ce1159437f40e05abc2a42a Mon Sep 17 00:00:00 2001 From: AlexSserb Date: Fri, 20 Dec 2024 00:27:39 +0400 Subject: [PATCH] feat: assignment of employees to deals --- src/client/index.ts | 4 + src/client/models/AssignmentSchema.ts | 10 + src/client/models/DealSchema.ts | 2 + .../GetAvailableEmployeesToAssignResponse.ts | 9 + src/client/models/ManageEmployeeRequest.ts | 10 + src/client/models/ManageEmployeeResponse.ts | 9 + src/client/services/DealService.ts | 44 +++ src/modals/modals.ts | 2 + .../LeadsPage/contexts/DealPageContext.tsx | 17 +- .../drawers/DealEditDrawer/DealEditDrawer.tsx | 357 ++---------------- .../tabs/EmployeesTab/EmployeesTab.tsx | 14 + .../components/AvailableEmployeesSelect.tsx | 27 ++ .../EmployeesTab/components/EmployeeInput.tsx | 29 ++ .../components/EmployeesTable.tsx | 42 +++ .../hooks/useAvailableEmployeesList.tsx | 16 + .../EmployeesTab/hooks/useEmployeesTab.tsx | 73 ++++ .../hooks/useEmployeesTableColumns.tsx | 36 ++ .../EmployeesTab/modals/AssignUserModal.tsx | 58 +++ .../types/AssignUserModalForm.tsx | 7 + 19 files changed, 437 insertions(+), 329 deletions(-) create mode 100644 src/client/models/AssignmentSchema.ts create mode 100644 src/client/models/GetAvailableEmployeesToAssignResponse.ts create mode 100644 src/client/models/ManageEmployeeRequest.ts create mode 100644 src/client/models/ManageEmployeeResponse.ts create mode 100644 src/pages/LeadsPage/tabs/EmployeesTab/EmployeesTab.tsx create mode 100644 src/pages/LeadsPage/tabs/EmployeesTab/components/AvailableEmployeesSelect.tsx create mode 100644 src/pages/LeadsPage/tabs/EmployeesTab/components/EmployeeInput.tsx create mode 100644 src/pages/LeadsPage/tabs/EmployeesTab/components/EmployeesTable.tsx create mode 100644 src/pages/LeadsPage/tabs/EmployeesTab/hooks/useAvailableEmployeesList.tsx create mode 100644 src/pages/LeadsPage/tabs/EmployeesTab/hooks/useEmployeesTab.tsx create mode 100644 src/pages/LeadsPage/tabs/EmployeesTab/hooks/useEmployeesTableColumns.tsx create mode 100644 src/pages/LeadsPage/tabs/EmployeesTab/modals/AssignUserModal.tsx create mode 100644 src/pages/LeadsPage/tabs/EmployeesTab/types/AssignUserModalForm.tsx diff --git a/src/client/index.ts b/src/client/index.ts index 6bca2a8..8210506 100644 --- a/src/client/index.ts +++ b/src/client/index.ts @@ -9,6 +9,7 @@ export type { OpenAPIConfig } from './core/OpenAPI'; export type { AddUserRequest } from './models/AddUserRequest'; export type { AddUserResponse } from './models/AddUserResponse'; +export type { AssignmentSchema } from './models/AssignmentSchema'; export type { AuthLoginRequest } from './models/AuthLoginRequest'; export type { AuthLoginResponse } from './models/AuthLoginResponse'; export type { BarcodeAttributeSchema } from './models/BarcodeAttributeSchema'; @@ -189,6 +190,7 @@ export type { GetAllTransactionsRequest } from './models/GetAllTransactionsReque export type { GetAllTransactionsResponse } from './models/GetAllTransactionsResponse'; export type { GetAllTransactionTagsResponse } from './models/GetAllTransactionTagsResponse'; export type { GetAllUsersResponse } from './models/GetAllUsersResponse'; +export type { GetAvailableEmployeesToAssignResponse } from './models/GetAvailableEmployeesToAssignResponse'; export type { GetAvailableUsersForDepartmentSectionResponse } from './models/GetAvailableUsersForDepartmentSectionResponse'; export type { GetBarcodeTemplateByIdRequest } from './models/GetBarcodeTemplateByIdRequest'; export type { GetBarcodeTemplateByIdResponse } from './models/GetBarcodeTemplateByIdResponse'; @@ -215,6 +217,8 @@ export type { GetTransactionTagsResponse } from './models/GetTransactionTagsResp export type { GetWorkShiftsResponse } from './models/GetWorkShiftsResponse'; export type { GroupBillRequestSchema } from './models/GroupBillRequestSchema'; export type { HTTPValidationError } from './models/HTTPValidationError'; +export type { ManageEmployeeRequest } from './models/ManageEmployeeRequest'; +export type { ManageEmployeeResponse } from './models/ManageEmployeeResponse'; export type { MarketplaceCreateSchema } from './models/MarketplaceCreateSchema'; export type { MarketplaceSchema } from './models/MarketplaceSchema'; export type { NotificationChannel } from './models/NotificationChannel'; diff --git a/src/client/models/AssignmentSchema.ts b/src/client/models/AssignmentSchema.ts new file mode 100644 index 0000000..7d1c13d --- /dev/null +++ b/src/client/models/AssignmentSchema.ts @@ -0,0 +1,10 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +import type { UserSchema } from './UserSchema'; +export type AssignmentSchema = { + user: UserSchema; + createdAt: string; +}; + diff --git a/src/client/models/DealSchema.ts b/src/client/models/DealSchema.ts index cadc5ac..f16299d 100644 --- a/src/client/models/DealSchema.ts +++ b/src/client/models/DealSchema.ts @@ -2,6 +2,7 @@ /* istanbul ignore file */ /* tslint:disable */ /* eslint-disable */ +import type { AssignmentSchema } from './AssignmentSchema'; import type { BoxSchema } from './BoxSchema'; import type { ClientSchema } from './ClientSchema'; import type { DealBillRequestSchema } from './DealBillRequestSchema'; @@ -34,6 +35,7 @@ export type DealSchema = { manager?: (UserSchema | null); pallets?: Array; boxes?: Array; + assignments?: Array; deliveryDate?: (string | null); receivingSlotDate?: (string | null); }; diff --git a/src/client/models/GetAvailableEmployeesToAssignResponse.ts b/src/client/models/GetAvailableEmployeesToAssignResponse.ts new file mode 100644 index 0000000..9d46ad4 --- /dev/null +++ b/src/client/models/GetAvailableEmployeesToAssignResponse.ts @@ -0,0 +1,9 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +import type { UserSchema } from './UserSchema'; +export type GetAvailableEmployeesToAssignResponse = { + employees: Array; +}; + diff --git a/src/client/models/ManageEmployeeRequest.ts b/src/client/models/ManageEmployeeRequest.ts new file mode 100644 index 0000000..0f1a05d --- /dev/null +++ b/src/client/models/ManageEmployeeRequest.ts @@ -0,0 +1,10 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +export type ManageEmployeeRequest = { + dealId: number; + userId: number; + isAssign: boolean; +}; + diff --git a/src/client/models/ManageEmployeeResponse.ts b/src/client/models/ManageEmployeeResponse.ts new file mode 100644 index 0000000..aea7bcf --- /dev/null +++ b/src/client/models/ManageEmployeeResponse.ts @@ -0,0 +1,9 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +export type ManageEmployeeResponse = { + ok: boolean; + message: string; +}; + diff --git a/src/client/services/DealService.ts b/src/client/services/DealService.ts index 9940c55..c7adbfc 100644 --- a/src/client/services/DealService.ts +++ b/src/client/services/DealService.ts @@ -61,8 +61,11 @@ import type { DealUpdateServiceQuantityRequest } from '../models/DealUpdateServi import type { DealUpdateServiceQuantityResponse } from '../models/DealUpdateServiceQuantityResponse'; import type { DealUpdateServiceRequest } from '../models/DealUpdateServiceRequest'; import type { DealUpdateServiceResponse } from '../models/DealUpdateServiceResponse'; +import type { GetAvailableEmployeesToAssignResponse } from '../models/GetAvailableEmployeesToAssignResponse'; import type { GetDealProductsBarcodesPdfRequest } from '../models/GetDealProductsBarcodesPdfRequest'; import type { GetDealProductsBarcodesPdfResponse } from '../models/GetDealProductsBarcodesPdfResponse'; +import type { ManageEmployeeRequest } from '../models/ManageEmployeeRequest'; +import type { ManageEmployeeResponse } from '../models/ManageEmployeeResponse'; import type { CancelablePromise } from '../core/CancelablePromise'; import { OpenAPI } from '../core/OpenAPI'; import { request as __request } from '../core/request'; @@ -382,6 +385,47 @@ export class DealService { }, }); } + /** + * Manage Employee + * @returns ManageEmployeeResponse Successful Response + * @throws ApiError + */ + public static manageEmployee({ + requestBody, + }: { + requestBody: ManageEmployeeRequest, + }): CancelablePromise { + return __request(OpenAPI, { + method: 'POST', + url: '/deal/employee', + body: requestBody, + mediaType: 'application/json', + errors: { + 422: `Validation Error`, + }, + }); + } + /** + * Get Available Employees To Assign + * @returns GetAvailableEmployeesToAssignResponse Successful Response + * @throws ApiError + */ + public static getAvailableEmployeesToAssign({ + dealId, + }: { + dealId: number, + }): CancelablePromise { + return __request(OpenAPI, { + method: 'GET', + url: '/deal/employee/available/{deal_id}', + path: { + 'deal_id': dealId, + }, + errors: { + 422: `Validation Error`, + }, + }); + } /** * Services Add * @returns DealAddServicesResponse Successful Response diff --git a/src/modals/modals.ts b/src/modals/modals.ts index 7995414..dd68f51 100644 --- a/src/modals/modals.ts +++ b/src/modals/modals.ts @@ -28,6 +28,7 @@ import ShippingProductModal from "../pages/LeadsPage/tabs/ShippingTab/modals/Shi import DepartmentModal from "../pages/AdminPage/tabs/OrganizationalStructureTab/modals/DepartmentModal.tsx"; import AddUserToDepartmentModal from "../pages/AdminPage/tabs/OrganizationalStructureTab/modals/AddUserToDepartmentModal.tsx"; +import AssignUserModal from "../pages/LeadsPage/tabs/EmployeesTab/modals/AssignUserModal.tsx"; export const modals = { enterDeadline: EnterDeadlineModal, @@ -59,4 +60,5 @@ export const modals = { shippingProductModal: ShippingProductModal, departmentModal: DepartmentModal, addUserToDepartmentModal: AddUserToDepartmentModal, + assignUserModal: AssignUserModal, }; diff --git a/src/pages/LeadsPage/contexts/DealPageContext.tsx b/src/pages/LeadsPage/contexts/DealPageContext.tsx index 13cd2c9..42751e7 100644 --- a/src/pages/LeadsPage/contexts/DealPageContext.tsx +++ b/src/pages/LeadsPage/contexts/DealPageContext.tsx @@ -5,6 +5,7 @@ type DealPageContextState = { selectedDeal?: DealSchema; setSelectedDeal: (deal: DealSchema | undefined) => void; refetchDeals: () => Promise; + refetchDeal: () => void; }; const DealPageContext = createContext( @@ -22,18 +23,24 @@ const useDealPageContextState = (props: DealPageContextStateProps) => { undefined, ); - useEffect(() => { - if (!defaultDealId) - return; - DealService.getDealById({ dealId: defaultDealId }).then(deal => { - setSelectedDeal(deal); + const refetchDeal = () => { + const dealId = selectedDeal?.id ?? defaultDealId; + if (!dealId) return; + DealService.getDealById({ dealId }).then(deal => { + setSelectedDeal(deal); }); + }; + + useEffect(() => { + refetchDeal(); }, []); + return { selectedDeal, setSelectedDeal, refetchDeals, + refetchDeal, }; }; diff --git a/src/pages/LeadsPage/drawers/DealEditDrawer/DealEditDrawer.tsx b/src/pages/LeadsPage/drawers/DealEditDrawer/DealEditDrawer.tsx index d1005ff..9277ad1 100644 --- a/src/pages/LeadsPage/drawers/DealEditDrawer/DealEditDrawer.tsx +++ b/src/pages/LeadsPage/drawers/DealEditDrawer/DealEditDrawer.tsx @@ -1,282 +1,14 @@ import { Box, Drawer, rem, Tabs } from "@mantine/core"; -import { FC, useEffect } from "react"; +import { FC, ReactNode, useEffect } from "react"; import { useDealPageContext } from "../../contexts/DealPageContext.tsx"; -import { IconBox, IconCalendarUser, IconCubeSend, IconSettings, IconUser } from "@tabler/icons-react"; +import { IconBox, IconCalendarUser, IconCubeSend, IconSettings, IconUser, IconUsersGroup } from "@tabler/icons-react"; import DealStatusChangeTable from "../../components/DealStatusChangeTable/DealStatusChangeTable.tsx"; import DealEditDrawerGeneralTab from "./tabs/DealEditDrawerGeneralTab.tsx"; import { useQueryClient } from "@tanstack/react-query"; import ProductAndServiceTab from "../../tabs/ProductAndServiceTab/ProductAndServiceTab.tsx"; import { motion } from "framer-motion"; -import ClientTab from "./tabs/ClientTab.tsx"; import ShippingTab from "../../tabs/ShippingTab/ShippingTab.tsx"; -// import styles from './DealEditDrawer.module.css'; - -// const useDealServicesTableState = () => { -// const { selectedDeal, setSelectedDeal } = useDealPageContext(); -// const tableRef = useRef>(null); -// -// const onServiceUpdate = (service: DealServiceSchema) => { -// if (!selectedDeal) return; -// DealService.updateDealService({ -// requestBody: { -// dealId: selectedDeal.id, -// service, -// }, -// }).then(async ({ ok, message }) => { -// if (!ok) { -// notifications.guess(ok, { message }); -// return; -// } -// await DealService.getDealById({ dealId: selectedDeal.id }).then( -// setSelectedDeal, -// ); -// }); -// }; -// const onServiceDelete = (service: DealServiceSchema) => { -// if (!selectedDeal) return; -// modals.openConfirmModal({ -// title: "Удаление услуги", -// children: ( -// <> -// Вы уверены, что хотите удалить услугу: -// {service.service.name}? -// -// ), -// onConfirm: () => { -// DealService.deleteDealService({ -// requestBody: { -// dealId: selectedDeal.id, -// serviceId: service.service.id, -// }, -// }).then(async ({ ok, message }) => { -// if (!ok) { -// notifications.guess(ok, { message }); -// return; -// } -// await DealService.getDealById({ -// dealId: selectedDeal.id, -// }).then(setSelectedDeal); -// }); -// }, -// labels: { -// cancel: "Отмена", -// confirm: "Удалить", -// }, -// }); -// }; -// const onServiceCreate = (service: DealServiceSchema) => { -// if (!selectedDeal) return; -// DealService.addDealService({ -// requestBody: { -// dealId: selectedDeal.id, -// serviceId: service.service.id, -// quantity: service.quantity, -// price: service.price, -// }, -// }).then(async ({ ok, message }) => { -// if (!ok) { -// notifications.guess(ok, { message }); -// return; -// } -// await DealService.getDealById({ dealId: selectedDeal.id }).then( -// setSelectedDeal, -// ); -// }); -// }; -// const onsServiceMultipleDelete = (items: DealServiceSchema[]) => { -// if (!selectedDeal) return; -// modals.openConfirmModal({ -// title: "Удаление услуг", -// children: ( -// <> -// -// Вы уверены, что хотите удалить выбранные услуги? -// -// -// ), -// onConfirm: () => { -// DealService.deleteMultipleDealServices({ -// requestBody: { -// dealId: selectedDeal.id, -// serviceIds: items.map(item => item.service.id), -// }, -// }).then(async ({ ok, message }) => { -// if (!ok) { -// notifications.guess(ok, { message }); -// return; -// } -// await DealService.getDealById({ -// dealId: selectedDeal.id, -// }).then(setSelectedDeal); -// }); -// }, -// labels: { -// cancel: "Отмена", -// confirm: "Удалить", -// }, -// }); -// }; -// -// return { -// onServiceUpdate, -// onServiceDelete, -// onServiceCreate, -// onsServiceMultipleDelete, -// tableRef, -// services: selectedDeal?.services || [], -// }; -// }; -// const DealEditDrawerServicesTable = () => { -// const { -// services, -// tableRef, -// onServiceCreate, -// onServiceUpdate, -// onServiceDelete, -// onsServiceMultipleDelete, -// } = useDealServicesTableState(); -// -// return ( -// -// ); -// }; - -// const useDealProductTableState = () => { -// const { selectedDeal, setSelectedDeal } = useDealPageContext(); -// -// const onProductUpdate = (product: DealProductSchema) => { -// if (!selectedDeal) return; -// DealService.updateDealProduct({ -// requestBody: { -// dealId: selectedDeal.id, -// product: product, -// }, -// }).then(async ({ ok, message }) => { -// notifications.guess(ok, { message }); -// if (!ok) return; -// await DealService.getDealById({ dealId: selectedDeal.id }).then( -// setSelectedDeal, -// ); -// }); -// }; -// const onProductDelete = (product: DealProductSchema) => { -// if (!selectedDeal) return; -// modals.openConfirmModal({ -// title: "Удаление товара", -// children: ( -// <> -// Вы уверены, что хотите удалить товар: -// {product.product.name}? -// -// ), -// onConfirm: () => { -// DealService.deleteDealProduct({ -// requestBody: { -// dealId: selectedDeal.id, -// productId: product.product.id, -// }, -// }).then(async ({ ok, message }) => { -// if (!ok) { -// notifications.guess(ok, { message }); -// return; -// } -// await DealService.getDealById({ -// dealId: selectedDeal.id, -// }).then(setSelectedDeal); -// }); -// }, -// labels: { -// cancel: "Отмена", -// confirm: "Удалить", -// }, -// }); -// }; -// const onProductCreate = (product: DealProductSchema) => { -// if (!selectedDeal) return; -// DealService.addDealProduct({ -// requestBody: { -// dealId: selectedDeal.id, -// product: product, -// }, -// }).then(async ({ ok, message }) => { -// if (!ok) { -// notifications.guess(ok, { message }); -// return; -// } -// await DealService.getDealById({ dealId: selectedDeal.id }).then( -// setSelectedDeal, -// ); -// }); -// }; -// const onProductMultipleDelete = (items: DealProductSchema[]) => { -// if (!selectedDeal) return; -// modals.openConfirmModal({ -// title: "Удаление товаров", -// children: ( -// <> -// -// Вы уверены, что хотите удалить выбранные товары? -// -// -// ), -// onConfirm: () => { -// DealService.deleteMultipleDealProducts({ -// requestBody: { -// dealId: selectedDeal.id, -// productIds: items.map(item => item.product.id), -// }, -// }).then(async ({ ok, message }) => { -// if (!ok) { -// notifications.guess(ok, { message }); -// return; -// } -// await DealService.getDealById({ -// dealId: selectedDeal.id, -// }).then(setSelectedDeal); -// }); -// }, -// labels: { -// cancel: "Отмена", -// confirm: "Удалить", -// }, -// }); -// }; -// return { -// clientId: selectedDeal?.clientId || -1, -// products: selectedDeal?.products || [], -// onProductUpdate, -// onProductDelete, -// onProductCreate, -// onProductMultipleDelete, -// }; -// }; -// const DealEditDrawerProductsTable = () => { -// const { -// products, -// clientId, -// onProductUpdate, -// onProductDelete, -// onProductCreate, -// onProductMultipleDelete, -// } = useDealProductTableState(); -// return ( -// -// ); -// }; +import EmployeesTab from "../../tabs/EmployeesTab/EmployeesTab.tsx"; const useDealStatusChangeState = () => { const { selectedDeal } = useDealPageContext(); @@ -306,6 +38,25 @@ const DealEditDrawer: FC = () => { if (isVisible) return; queryClient.invalidateQueries({ queryKey: ["getDealSummaries"] }); }, [isVisible]); + + const getTabPanel = (value: string, component: ReactNode) => { + return ( + + + + {component} + + + + ); + }; + return ( { leftSection={}> Отгрузка + }> + Исполнители + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + {getTabPanel("general", )} + {getTabPanel("client", )} + {getTabPanel("history", )} + {getTabPanel("servicesAndProducts", )} + {getTabPanel("shipment", )} + {getTabPanel("employees", )} ); diff --git a/src/pages/LeadsPage/tabs/EmployeesTab/EmployeesTab.tsx b/src/pages/LeadsPage/tabs/EmployeesTab/EmployeesTab.tsx new file mode 100644 index 0000000..e6e394a --- /dev/null +++ b/src/pages/LeadsPage/tabs/EmployeesTab/EmployeesTab.tsx @@ -0,0 +1,14 @@ +import { Stack } from "@mantine/core"; +import EmployeeInput from "./components/EmployeeInput.tsx"; +import EmployeesTable from "./components/EmployeesTable.tsx"; + +const EmployeesTab = () => { + return ( + + + + + ); +}; + +export default EmployeesTab; diff --git a/src/pages/LeadsPage/tabs/EmployeesTab/components/AvailableEmployeesSelect.tsx b/src/pages/LeadsPage/tabs/EmployeesTab/components/AvailableEmployeesSelect.tsx new file mode 100644 index 0000000..24e0ecb --- /dev/null +++ b/src/pages/LeadsPage/tabs/EmployeesTab/components/AvailableEmployeesSelect.tsx @@ -0,0 +1,27 @@ +import { FC } from "react"; +import ObjectSelect, { ObjectSelectProps } from "../../../../../components/ObjectSelect/ObjectSelect.tsx"; +import { UserSchema } from "../../../../../client"; +import useAvailableEmployeesList from "../hooks/useAvailableEmployeesList.tsx"; + +type DealData = { + dealId: number; +} + +type Props = DealData & Omit< + ObjectSelectProps, + "data" | "getValueFn" | "getLabelFn" +>; + +const UserForDepartmentSelect: FC = ({ dealId, ...selectProps }) => { + const { objects: employees } = useAvailableEmployeesList({ dealId }); + + return ( + `${user.firstName} ${user.secondName}`} + getValueFn={(user: UserSchema) => user.id.toString()} + {...selectProps} + /> + ); +}; +export default UserForDepartmentSelect; diff --git a/src/pages/LeadsPage/tabs/EmployeesTab/components/EmployeeInput.tsx b/src/pages/LeadsPage/tabs/EmployeesTab/components/EmployeeInput.tsx new file mode 100644 index 0000000..8098b74 --- /dev/null +++ b/src/pages/LeadsPage/tabs/EmployeesTab/components/EmployeeInput.tsx @@ -0,0 +1,29 @@ +import { Button, Group } from "@mantine/core"; +import { IconQrcode, IconTablePlus } from "@tabler/icons-react"; +import useEmployeesTab from "../hooks/useEmployeesTab.tsx"; + +const EmployeeInput = () => { + const { + onAssignEmployeeByQrClick, + onAssignEmployeeManuallyClick, + } = useEmployeesTab(); + + return ( + + + + + ); +}; + +export default EmployeeInput; diff --git a/src/pages/LeadsPage/tabs/EmployeesTab/components/EmployeesTable.tsx b/src/pages/LeadsPage/tabs/EmployeesTab/components/EmployeesTable.tsx new file mode 100644 index 0000000..9927ca0 --- /dev/null +++ b/src/pages/LeadsPage/tabs/EmployeesTab/components/EmployeesTable.tsx @@ -0,0 +1,42 @@ +import { useDealPageContext } from "../../../contexts/DealPageContext.tsx"; +import { BaseTable } from "../../../../../components/BaseTable/BaseTable.tsx"; +import useEmployeeTableColumns from "../hooks/useEmployeesTableColumns.tsx"; +import { ActionIcon, Flex, Tooltip } from "@mantine/core"; +import { IconTrash } from "@tabler/icons-react"; +import { MRT_TableOptions } from "mantine-react-table"; +import { AssignmentSchema } from "../../../../../client"; +import useEmployeesTab from "../hooks/useEmployeesTab.tsx"; + + +const EmployeesTable = () => { + const { selectedDeal: deal } = useDealPageContext(); + const columns = useEmployeeTableColumns(); + const { onUnassignEmployeeClick } = useEmployeesTab(); + + return ( + ( + + + onUnassignEmployeeClick(row.original)} + variant={"default"}> + + + + + ), + } as MRT_TableOptions + } + /> + ); +}; + +export default EmployeesTable; diff --git a/src/pages/LeadsPage/tabs/EmployeesTab/hooks/useAvailableEmployeesList.tsx b/src/pages/LeadsPage/tabs/EmployeesTab/hooks/useAvailableEmployeesList.tsx new file mode 100644 index 0000000..add1f86 --- /dev/null +++ b/src/pages/LeadsPage/tabs/EmployeesTab/hooks/useAvailableEmployeesList.tsx @@ -0,0 +1,16 @@ +import { DealService } from "../../../../../client"; +import ObjectList from "../../../../../hooks/objectList.tsx"; + + +type Props = { + dealId: number; +} + +const useAvailableEmployeesList = ({ dealId }: Props) => + ObjectList({ + queryFn: () => DealService.getAvailableEmployeesToAssign({ dealId }), + getObjectsFn: response => response.employees, + queryKey: "getAvailableEmployeesToAssign", + }); + +export default useAvailableEmployeesList; diff --git a/src/pages/LeadsPage/tabs/EmployeesTab/hooks/useEmployeesTab.tsx b/src/pages/LeadsPage/tabs/EmployeesTab/hooks/useEmployeesTab.tsx new file mode 100644 index 0000000..7809ae7 --- /dev/null +++ b/src/pages/LeadsPage/tabs/EmployeesTab/hooks/useEmployeesTab.tsx @@ -0,0 +1,73 @@ +import { notifications } from "../../../../../shared/lib/notifications.ts"; +import { modals } from "@mantine/modals"; +import { useDealPageContext } from "../../../contexts/DealPageContext.tsx"; +import { AssignmentSchema, DealService } from "../../../../../client"; + +const useEmployeesTab = () => { + const { selectedDeal: deal, refetchDeal } = useDealPageContext(); + + const manageEmployee = (dealId: number, userId: number, isAssign: boolean) => { + DealService.manageEmployee({ + requestBody: { + dealId, + userId, + isAssign, + }, + }) + .then(({ ok, message }) => { + notifications.guess(ok, { message }); + refetchDeal(); + }) + .catch((err) => console.log(err)); + }; + + const onInputFinish = (userIdInput: string) => { + const userId = parseInt(userIdInput); + if (isNaN(userId)) { + notifications.error({ message: "Ошибка, некорректные данные в QR-коде" }); + return; + } + + if (!deal) return; + manageEmployee(deal.id, userId, true); + }; + + const onAssignEmployeeByQrClick = () => { + modals.openContextModal({ + modal: "scanningModal", + innerProps: { + label: "Отсканируйте QR-код", + onScan: onInputFinish, + closeOnScan: true, + }, + withCloseButton: false, + }); + }; + + const onUnassignEmployeeClick = (assignment: AssignmentSchema) => { + if (!deal) return; + manageEmployee(deal.id, assignment.user.id, false); + }; + + const onAssignEmployeeManuallyClick = () => { + if (!deal) return; + + modals.openContextModal({ + modal: "assignUserModal", + title: `Назначение исполнителя`, + withCloseButton: false, + innerProps: { + deal, + manageEmployee, + }, + }); + }; + + return { + onAssignEmployeeByQrClick, + onAssignEmployeeManuallyClick, + onUnassignEmployeeClick, + }; +}; + +export default useEmployeesTab; diff --git a/src/pages/LeadsPage/tabs/EmployeesTab/hooks/useEmployeesTableColumns.tsx b/src/pages/LeadsPage/tabs/EmployeesTab/hooks/useEmployeesTableColumns.tsx new file mode 100644 index 0000000..4ee62f3 --- /dev/null +++ b/src/pages/LeadsPage/tabs/EmployeesTab/hooks/useEmployeesTableColumns.tsx @@ -0,0 +1,36 @@ +import { useMemo } from "react"; +import { MRT_ColumnDef } from "mantine-react-table"; +import { AssignmentSchema } from "../../../../../client"; + + +const useEmployeeTableColumns = () => { + return useMemo[]>( + () => [ + { + accessorKey: "createdAt", + header: "Дата назначения", + Cell: ({ cell }) => new Date(cell.getValue() as string).toLocaleString("ru"), + }, + { + header: "ФИО", + Cell: ({ row }) => + `${row.original.user.secondName} ${row.original.user.firstName} ${row.original.user.patronymic}`, + }, + { + accessorKey: "user.role.name", + header: "Роль", + }, + { + accessorKey: "user.position.name", + header: "Должность", + }, + { + accessorKey: "user.comment", + header: "Дополнительная информация", + }, + ], + [], + ); +}; + +export default useEmployeeTableColumns; diff --git a/src/pages/LeadsPage/tabs/EmployeesTab/modals/AssignUserModal.tsx b/src/pages/LeadsPage/tabs/EmployeesTab/modals/AssignUserModal.tsx new file mode 100644 index 0000000..536902b --- /dev/null +++ b/src/pages/LeadsPage/tabs/EmployeesTab/modals/AssignUserModal.tsx @@ -0,0 +1,58 @@ +import { useForm } from "@mantine/form"; +import { ContextModalProps } from "@mantine/modals"; +import { Button, Flex, rem } from "@mantine/core"; +import { DealSchema } from "../../../../../client"; +import AssignUserModalForm from "../types/AssignUserModalForm.tsx"; +import AvailableEmployeesSelect from "../components/AvailableEmployeesSelect.tsx"; + +type Props = { + deal: DealSchema; + manageEmployee: (dealId: number, userId: number, isAssign: boolean) => void; +} + +const AssignEmployeeModal = ({ + context, + id, + innerProps, + }: ContextModalProps) => { + const { + deal, + manageEmployee, + } = innerProps; + + const form = useForm>({ + validate: { + employee: employee => !employee && "Необходимо выбрать работника", + }, + }); + + const onSubmit = () => { + if (!form.values.employee) return; + manageEmployee(deal.id, form.values.employee.id, true); + context.closeContextModal(id); + }; + + return ( +
onSubmit())}> + + + + +
+ ); +}; + +export default AssignEmployeeModal; diff --git a/src/pages/LeadsPage/tabs/EmployeesTab/types/AssignUserModalForm.tsx b/src/pages/LeadsPage/tabs/EmployeesTab/types/AssignUserModalForm.tsx new file mode 100644 index 0000000..07a7a84 --- /dev/null +++ b/src/pages/LeadsPage/tabs/EmployeesTab/types/AssignUserModalForm.tsx @@ -0,0 +1,7 @@ +import { UserSchema } from "../../../../../client"; + +type AssignUserModalForm = { + employee: UserSchema; +} + +export default AssignUserModalForm;