From 608a0633693cebb4cac526e708f5fe8fc8e2dbc7 Mon Sep 17 00:00:00 2001 From: AlexSserb Date: Fri, 15 Nov 2024 15:19:14 +0400 Subject: [PATCH 1/4] feat: profit chart in statistics --- package.json | 4 +- src/client/index.ts | 4 + src/client/models/GetProfitDataRequest.ts | 11 +++ src/client/models/GetProfitDataResponse.ts | 9 ++ src/client/models/ProfitDataItem.ts | 11 +++ src/client/services/StatisticsService.ts | 31 +++++++ src/components/Navbar/Navbar.tsx | 7 +- .../components/Filters/Filters.tsx | 90 +++++++++++++++++++ .../components/ProfitTable/ProfitTable.tsx | 52 +++++++++++ .../components/ProfitTable/columns.tsx | 31 +++++++ .../StatisticsTabSegmentControl.tsx | 28 ++++++ .../StatisticsTableSegmentControl.tsx | 43 +++++++++ src/pages/StatisticsPage/index.ts | 1 + .../StatisticsPage/tabs/ProfitTab/Chart.tsx | 31 +++++++ .../tabs/ProfitTab/ProfitTab.tsx | 72 +++++++++++++++ .../StatisticsPage/types/ChartFormFilters.ts | 9 ++ .../StatisticsPage/types/ProfitTableRow.ts | 6 ++ .../StatisticsPage/types/TableFormFilters.ts | 6 ++ .../ui/StatisticsPage.module.css | 12 +++ .../StatisticsPage/ui/StatisticsPage.tsx | 38 ++++++++ src/pages/StatisticsPage/utils/dates.ts | 15 ++++ src/routes/statistics.lazy.tsx | 6 ++ 22 files changed, 515 insertions(+), 2 deletions(-) create mode 100644 src/client/models/GetProfitDataRequest.ts create mode 100644 src/client/models/GetProfitDataResponse.ts create mode 100644 src/client/models/ProfitDataItem.ts create mode 100644 src/client/services/StatisticsService.ts create mode 100644 src/pages/StatisticsPage/components/Filters/Filters.tsx create mode 100644 src/pages/StatisticsPage/components/ProfitTable/ProfitTable.tsx create mode 100644 src/pages/StatisticsPage/components/ProfitTable/columns.tsx create mode 100644 src/pages/StatisticsPage/components/StatisticsTabSegmentControl/StatisticsTabSegmentControl.tsx create mode 100644 src/pages/StatisticsPage/components/StatisticsTableSegmentControl/StatisticsTableSegmentControl.tsx create mode 100644 src/pages/StatisticsPage/index.ts create mode 100644 src/pages/StatisticsPage/tabs/ProfitTab/Chart.tsx create mode 100644 src/pages/StatisticsPage/tabs/ProfitTab/ProfitTab.tsx create mode 100644 src/pages/StatisticsPage/types/ChartFormFilters.ts create mode 100644 src/pages/StatisticsPage/types/ProfitTableRow.ts create mode 100644 src/pages/StatisticsPage/types/TableFormFilters.ts create mode 100644 src/pages/StatisticsPage/ui/StatisticsPage.module.css create mode 100644 src/pages/StatisticsPage/ui/StatisticsPage.tsx create mode 100644 src/pages/StatisticsPage/utils/dates.ts create mode 100644 src/routes/statistics.lazy.tsx diff --git a/package.json b/package.json index beef21f..6e3b53b 100644 --- a/package.json +++ b/package.json @@ -16,8 +16,9 @@ "@fortawesome/free-solid-svg-icons": "^6.6.0", "@fortawesome/react-fontawesome": "^0.2.2", "@hello-pangea/dnd": "^17.0.0", + "@mantine/charts": "^7.13.5", "@mantine/core": "^7.11.2", - "@mantine/dates": "^7.11.2", + "@mantine/dates": "^7.13.5", "@mantine/dropzone": "^7.11.2", "@mantine/form": "^7.11.2", "@mantine/hooks": "^7.11.2", @@ -51,6 +52,7 @@ "react-redux": "^9.1.2", "react-to-print": "^2.15.1", "reactflow": "^11.11.4", + "recharts": "^2.13.3", "zod": "^3.23.8" }, "devDependencies": { diff --git a/src/client/index.ts b/src/client/index.ts index 16fcf4f..24b729c 100644 --- a/src/client/index.ts +++ b/src/client/index.ts @@ -166,6 +166,8 @@ export type { GetProductBarcodePdfRequest } from './models/GetProductBarcodePdfR export type { GetProductBarcodePdfResponse } from './models/GetProductBarcodePdfResponse'; export type { GetProductBarcodeRequest } from './models/GetProductBarcodeRequest'; export type { GetProductBarcodeResponse } from './models/GetProductBarcodeResponse'; +export type { GetProfitDataRequest } from './models/GetProfitDataRequest'; +export type { GetProfitDataResponse } from './models/GetProfitDataResponse'; export type { GetServiceKitSchema } from './models/GetServiceKitSchema'; export type { GetTimeTrackingRecordsRequest } from './models/GetTimeTrackingRecordsRequest'; export type { GetTimeTrackingRecordsResponse } from './models/GetTimeTrackingRecordsResponse'; @@ -199,6 +201,7 @@ export type { ProductUpdateRequest } from './models/ProductUpdateRequest'; export type { ProductUpdateResponse } from './models/ProductUpdateResponse'; export type { ProductUploadBarcodeImageResponse } from './models/ProductUploadBarcodeImageResponse'; export type { ProductUploadImageResponse } from './models/ProductUploadImageResponse'; +export type { ProfitDataItem } from './models/ProfitDataItem'; export type { RoleSchema } from './models/RoleSchema'; export type { ServiceCategoryPriceSchema } from './models/ServiceCategoryPriceSchema'; export type { ServiceCategoryReorderRequest } from './models/ServiceCategoryReorderRequest'; @@ -260,6 +263,7 @@ export { ProductService } from './services/ProductService'; export { RoleService } from './services/RoleService'; export { ServiceService } from './services/ServiceService'; export { ShippingWarehouseService } from './services/ShippingWarehouseService'; +export { StatisticsService } from './services/StatisticsService'; export { TaskService } from './services/TaskService'; export { TimeTrackingService } from './services/TimeTrackingService'; export { UserService } from './services/UserService'; diff --git a/src/client/models/GetProfitDataRequest.ts b/src/client/models/GetProfitDataRequest.ts new file mode 100644 index 0000000..47fb791 --- /dev/null +++ b/src/client/models/GetProfitDataRequest.ts @@ -0,0 +1,11 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +export type GetProfitDataRequest = { + dateRange: any[]; + clientId: number; + baseMarketplaceKey: string; + dealStatusId: number; +}; + diff --git a/src/client/models/GetProfitDataResponse.ts b/src/client/models/GetProfitDataResponse.ts new file mode 100644 index 0000000..2f7eb91 --- /dev/null +++ b/src/client/models/GetProfitDataResponse.ts @@ -0,0 +1,9 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +import type { ProfitDataItem } from './ProfitDataItem'; +export type GetProfitDataResponse = { + data: Array; +}; + diff --git a/src/client/models/ProfitDataItem.ts b/src/client/models/ProfitDataItem.ts new file mode 100644 index 0000000..d2fdee8 --- /dev/null +++ b/src/client/models/ProfitDataItem.ts @@ -0,0 +1,11 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +export type ProfitDataItem = { + date: string; + revenue: number; + profit: number; + dealsCount: number; +}; + diff --git a/src/client/services/StatisticsService.ts b/src/client/services/StatisticsService.ts new file mode 100644 index 0000000..b729913 --- /dev/null +++ b/src/client/services/StatisticsService.ts @@ -0,0 +1,31 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +import type { GetProfitDataRequest } from '../models/GetProfitDataRequest'; +import type { GetProfitDataResponse } from '../models/GetProfitDataResponse'; +import type { CancelablePromise } from '../core/CancelablePromise'; +import { OpenAPI } from '../core/OpenAPI'; +import { request as __request } from '../core/request'; +export class StatisticsService { + /** + * Get Profit Data + * @returns GetProfitDataResponse Successful Response + * @throws ApiError + */ + public static getProfitData({ + requestBody, + }: { + requestBody: GetProfitDataRequest, + }): CancelablePromise { + return __request(OpenAPI, { + method: 'POST', + url: '/statistics/get-profit-data', + body: requestBody, + mediaType: 'application/json', + errors: { + 422: `Validation Error`, + }, + }); + } +} diff --git a/src/components/Navbar/Navbar.tsx b/src/components/Navbar/Navbar.tsx index dc4ecbf..fac1aaf 100644 --- a/src/components/Navbar/Navbar.tsx +++ b/src/components/Navbar/Navbar.tsx @@ -4,7 +4,7 @@ import { IconBarcode, IconBox, IconBuildingWarehouse, - IconCash, + IconCash, IconChartDots, IconDashboard, IconFileBarcode, IconHome2, @@ -93,6 +93,11 @@ const mockdata = [ label: "Маркетплейсы", href: "/marketplaces", }, + { + icon: IconChartDots, + label: "Статистика", + href: "/statistics", + } ]; export function Navbar() { diff --git a/src/pages/StatisticsPage/components/Filters/Filters.tsx b/src/pages/StatisticsPage/components/Filters/Filters.tsx new file mode 100644 index 0000000..9db79f9 --- /dev/null +++ b/src/pages/StatisticsPage/components/Filters/Filters.tsx @@ -0,0 +1,90 @@ +import PageBlock from "../../../../components/PageBlock/PageBlock.tsx"; +import { DatePickerInput, DatePickerInputProps } from "@mantine/dates"; +import { Group, Text } from "@mantine/core"; +import ClientSelectNew from "../../../../components/Selects/ClientSelectNew/ClientSelectNew.tsx"; +import { BaseMarketplaceSchema, ClientSchema } from "../../../../client"; +import { ObjectSelectProps } from "../../../../components/ObjectSelect/ObjectSelect.tsx"; +import BaseMarketplaceSelect from "../../../../components/Selects/BaseMarketplaceSelect/BaseMarketplaceSelect.tsx"; +import DealStatusSelect from "../../../DealsPage/components/DealStatusSelect/DealStatusSelect.tsx"; +import { DealStatusType } from "../../../../shared/enums/DealStatus.ts"; +import { StatisticsTableSegmentControl } from "../StatisticsTableSegmentControl/StatisticsTableSegmentControl.tsx"; + + +type FiltersProps = { + datePickerProps?: DatePickerInputProps<"range">; + + clientSelectProps?: Omit, "data">; + onClientClear?: () => void; + + baseMarketplaceSelectProps?: Omit< + ObjectSelectProps, + "data" | "getValueFn" | "getLabelFn" + >; + onBaseMarketplaceClear?: () => void; + + dealStatusSelectProps?: Omit, "data">; + onDealStatusClear?: () => void; +} + +export const Filters = (props: FiltersProps) => { + const { + datePickerProps, + clientSelectProps, + onClientClear, + baseMarketplaceSelectProps, + onBaseMarketplaceClear, + dealStatusSelectProps, + onDealStatusClear, + } = props; + + return ( + + + {datePickerProps && + + } + {dealStatusSelectProps && + + } + {clientSelectProps && + + } + {baseMarketplaceSelectProps && + + } + { + <> + + Группировать: + + setGroupTableBy(parseInt(event))} + /> + + } + + + ); +}; \ No newline at end of file diff --git a/src/pages/StatisticsPage/components/ProfitTable/ProfitTable.tsx b/src/pages/StatisticsPage/components/ProfitTable/ProfitTable.tsx new file mode 100644 index 0000000..4be9973 --- /dev/null +++ b/src/pages/StatisticsPage/components/ProfitTable/ProfitTable.tsx @@ -0,0 +1,52 @@ +import { useProfitTableColumns } from "./columns.tsx"; +import { ProfitDataItem } from "../../../../client"; +import PageBlock from "../../../../components/PageBlock/PageBlock.tsx"; +import { GroupStatisticsTable } from "../StatisticsTableSegmentControl/StatisticsTableSegmentControl.tsx"; +import styles from "../../ui/StatisticsPage.module.css"; +import { MantineReactTable, useMantineReactTable } from "mantine-react-table"; +import { MRT_Localization_RU } from "mantine-react-table/locales/ru/index.cjs"; +import { Filters } from "../Filters/Filters.tsx"; +import { useForm } from "@mantine/form"; +import { getDefaultDates } from "../../utils/dates.ts"; +import { TableFormFilters } from "../../types/TableFormFilters.ts"; + +type Props = { + data: ProfitDataItem[]; +} + +export const ProfitTable = ({ data }: Props) => { + const form = useForm({ + mode: "controlled", + initialValues: { + dateRange: getDefaultDates(), + groupTableBy: GroupStatisticsTable.BY_DATES, + }, + }); + + const columns = useProfitTableColumns(); + const defaultSorting = [{ id: "date", desc: true }]; + + const table = useMantineReactTable({ + localization: MRT_Localization_RU, + enablePagination: true, + data, + columns, + enableTopToolbar: false, + enableBottomToolbar: true, + enableSorting: true, + initialState: { + sorting: defaultSorting, + }, + }); + + return ( +
+ + + + + + +
+ ); +}; \ No newline at end of file diff --git a/src/pages/StatisticsPage/components/ProfitTable/columns.tsx b/src/pages/StatisticsPage/components/ProfitTable/columns.tsx new file mode 100644 index 0000000..4708d87 --- /dev/null +++ b/src/pages/StatisticsPage/components/ProfitTable/columns.tsx @@ -0,0 +1,31 @@ +import { useMemo } from "react"; +import { MRT_ColumnDef } from "mantine-react-table"; +import { ProfitTableRow } from "../../types/ProfitTableRow.ts"; + +export const useProfitTableColumns = () => { + return useMemo[]>( + () => [ + { + accessorKey: "date", + header: "Дата", + }, + { + accessorKey: "dealsCount", + header: "Кол-во сделок" + }, + { + accessorKey: "profit", + header: "Прибыль", + Cell: ({ row }) => + row.original.profit.toLocaleString("ru-RU") + "₽", + }, + { + accessorKey: "revenue", + header: "Выручка", + Cell: ({ row }) => + row.original.revenue.toLocaleString("ru-RU") + "₽", + }, + ], + [] + ); +}; diff --git a/src/pages/StatisticsPage/components/StatisticsTabSegmentControl/StatisticsTabSegmentControl.tsx b/src/pages/StatisticsPage/components/StatisticsTabSegmentControl/StatisticsTabSegmentControl.tsx new file mode 100644 index 0000000..14100a7 --- /dev/null +++ b/src/pages/StatisticsPage/components/StatisticsTabSegmentControl/StatisticsTabSegmentControl.tsx @@ -0,0 +1,28 @@ +import { SegmentedControl, SegmentedControlProps } from "@mantine/core"; +import { FC } from "react"; + +export enum StatisticsTab { + PROFIT, + SALARIES, +} + +type Props = Omit; +const data = [ + { + label: "Выручка по сделкам", + value: StatisticsTab.PROFIT.toString(), + }, + { + label: "Зарплаты", + value: StatisticsTab.SALARIES.toString(), + }, +]; + +export const StatisticsTabSegmentControl: FC = props => { + return ( + + ); +}; \ No newline at end of file diff --git a/src/pages/StatisticsPage/components/StatisticsTableSegmentControl/StatisticsTableSegmentControl.tsx b/src/pages/StatisticsPage/components/StatisticsTableSegmentControl/StatisticsTableSegmentControl.tsx new file mode 100644 index 0000000..b901735 --- /dev/null +++ b/src/pages/StatisticsPage/components/StatisticsTableSegmentControl/StatisticsTableSegmentControl.tsx @@ -0,0 +1,43 @@ +import { SegmentedControl, SegmentedControlProps } from "@mantine/core"; +import { FC } from "react"; + +export enum GroupStatisticsTable { + BY_DATES, + BY_CLIENTS, + BY_STATUSES, + BY_WAREHOUSES, + BY_MARKETPLACES, +} + +type Props = Omit; +const data = [ + { + label: "По датам", + value: GroupStatisticsTable.BY_DATES.toString(), + }, + { + label: "По клиентам", + value: GroupStatisticsTable.BY_CLIENTS.toString(), + }, + { + label: "По статусам", + value: GroupStatisticsTable.BY_STATUSES.toString(), + }, + { + label: "По складам отгрузки", + value: GroupStatisticsTable.BY_WAREHOUSES.toString(), + }, + { + label: "По маркетплейсам", + value: GroupStatisticsTable.BY_MARKETPLACES.toString(), + }, +]; + +export const StatisticsTableSegmentControl: FC = props => { + return ( + + ); +}; \ No newline at end of file diff --git a/src/pages/StatisticsPage/index.ts b/src/pages/StatisticsPage/index.ts new file mode 100644 index 0000000..568eecd --- /dev/null +++ b/src/pages/StatisticsPage/index.ts @@ -0,0 +1 @@ +export { StatisticsPage } from "./ui/StatisticsPage.tsx"; diff --git a/src/pages/StatisticsPage/tabs/ProfitTab/Chart.tsx b/src/pages/StatisticsPage/tabs/ProfitTab/Chart.tsx new file mode 100644 index 0000000..2019612 --- /dev/null +++ b/src/pages/StatisticsPage/tabs/ProfitTab/Chart.tsx @@ -0,0 +1,31 @@ +import { AreaChart } from "@mantine/charts"; +import "@mantine/charts/styles.css"; +import PageBlock from "../../../../components/PageBlock/PageBlock.tsx"; +import { ProfitDataItem } from "../../../../client"; + +type Props = { + data: ProfitDataItem[]; +} + +export const Chart = ({ data }: Props) => { + return ( + + new Intl.NumberFormat("ru-RU").format(value)} + series={[ + { name: "profit", label: "Прибыль", color: "indigo.6" }, + { name: "revenue", label: "Выручка", color: "teal.6" }, + ]} + fillOpacity={0.5} + /> + + ); +}; \ No newline at end of file diff --git a/src/pages/StatisticsPage/tabs/ProfitTab/ProfitTab.tsx b/src/pages/StatisticsPage/tabs/ProfitTab/ProfitTab.tsx new file mode 100644 index 0000000..a9e7309 --- /dev/null +++ b/src/pages/StatisticsPage/tabs/ProfitTab/ProfitTab.tsx @@ -0,0 +1,72 @@ +import { useForm } from "@mantine/form"; +import { Chart } from "./Chart.tsx"; +import { Filters } from "../../components/Filters/Filters.tsx"; +import styles from "../../ui/StatisticsPage.module.css"; +import { ChartFormFilters } from "../../types/ChartFormFilters.ts"; +import { useEffect, useState } from "react"; +import { ProfitDataItem, StatisticsService } from "../../../../client"; +import { dateToString, getDefaultDates } from "../../utils/dates.ts"; +import { ProfitTable } from "../../components/ProfitTable/ProfitTable.tsx"; + +export const ProfitTab = () => { + const form = useForm({ + mode: "controlled", + initialValues: { + dateRange: getDefaultDates(), + client: null, + marketplace: null, + dealStatus: null, + }, + }); + const [profitData, setProfitData] = useState([]); + + const getFilters = () => { + const dateRange = form.values.dateRange; + + return { + dateRange: [ + dateToString(dateRange[0]), + dateToString(dateRange[1]), + ], + clientId: form.values.client?.id ?? -1, + baseMarketplaceKey: form.values.marketplace?.key ?? "all", + dealStatusId: form.values.dealStatus?.id ?? -1, + }; + }; + + const fetchProfitData = () => { + StatisticsService.getProfitData({ + requestBody: getFilters(), + }) + .then(res => { + setProfitData(res.data); + }) + .catch(err => console.log(err)); + }; + + useEffect(() => { + if (form.values.dateRange.length < 2 || form.values.dateRange[1] === null) { + return; + } + + console.log(form.values); + + fetchProfitData(); + }, [form.values]); + + return ( +
+ form.setFieldValue("client", null)} + baseMarketplaceSelectProps={form.getInputProps("marketplace")} + onBaseMarketplaceClear={() => form.setFieldValue("marketplace", null)} + dealStatusSelectProps={form.getInputProps("dealStatus")} + onDealStatusClear={() => form.setFieldValue("dealStatus", null)} + /> + + +
+ ); +}; \ No newline at end of file diff --git a/src/pages/StatisticsPage/types/ChartFormFilters.ts b/src/pages/StatisticsPage/types/ChartFormFilters.ts new file mode 100644 index 0000000..3815461 --- /dev/null +++ b/src/pages/StatisticsPage/types/ChartFormFilters.ts @@ -0,0 +1,9 @@ +import { BaseMarketplaceSchema, ClientSchema } from "../../../client"; +import { DealStatusType } from "../../../shared/enums/DealStatus.ts"; + +export interface ChartFormFilters { + dateRange: [Date | null, Date | null]; + client: ClientSchema | null; + marketplace: BaseMarketplaceSchema | null; + dealStatus: DealStatusType | null; +} \ No newline at end of file diff --git a/src/pages/StatisticsPage/types/ProfitTableRow.ts b/src/pages/StatisticsPage/types/ProfitTableRow.ts new file mode 100644 index 0000000..f775a01 --- /dev/null +++ b/src/pages/StatisticsPage/types/ProfitTableRow.ts @@ -0,0 +1,6 @@ +export type ProfitTableRow = { + date: string; + dealsCount: number; + profit: number; + revenue: number; +} \ No newline at end of file diff --git a/src/pages/StatisticsPage/types/TableFormFilters.ts b/src/pages/StatisticsPage/types/TableFormFilters.ts new file mode 100644 index 0000000..eb9b802 --- /dev/null +++ b/src/pages/StatisticsPage/types/TableFormFilters.ts @@ -0,0 +1,6 @@ +import { GroupStatisticsTable } from "../components/StatisticsTableSegmentControl/StatisticsTableSegmentControl.tsx"; + +export interface TableFormFilters { + dateRange: [Date | null, Date | null]; + groupTableBy: GroupStatisticsTable; +} \ No newline at end of file diff --git a/src/pages/StatisticsPage/ui/StatisticsPage.module.css b/src/pages/StatisticsPage/ui/StatisticsPage.module.css new file mode 100644 index 0000000..61ac571 --- /dev/null +++ b/src/pages/StatisticsPage/ui/StatisticsPage.module.css @@ -0,0 +1,12 @@ +.container { + display: flex; + flex-direction: column; + flex: 1; + gap: rem(10); +} + +.top-panel { + padding: rem(5); + gap: rem(10); + display: flex; +} \ No newline at end of file diff --git a/src/pages/StatisticsPage/ui/StatisticsPage.tsx b/src/pages/StatisticsPage/ui/StatisticsPage.tsx new file mode 100644 index 0000000..3436bf2 --- /dev/null +++ b/src/pages/StatisticsPage/ui/StatisticsPage.tsx @@ -0,0 +1,38 @@ +import { useState } from "react"; +import { + StatisticsTab, + StatisticsTabSegmentControl, +} from "../components/StatisticsTabSegmentControl/StatisticsTabSegmentControl.tsx"; +import styles from "./StatisticsPage.module.css"; +import PageBlock from "../../../components/PageBlock/PageBlock.tsx"; +import { ProfitTab } from "../tabs/ProfitTab/ProfitTab.tsx"; + +export const StatisticsPage = () => { + const [serviceType, setServiceType] = useState(StatisticsTab.PROFIT); + + const getBody = () => { + switch (serviceType) { + case StatisticsTab.PROFIT: + return ( + + ); + case StatisticsTab.SALARIES: + return ( + <>Статистика по ЗП + ); + } + }; + + return ( +
+ + setServiceType(parseInt(event))} + /> + + {getBody()} +
+ ); +}; \ No newline at end of file diff --git a/src/pages/StatisticsPage/utils/dates.ts b/src/pages/StatisticsPage/utils/dates.ts new file mode 100644 index 0000000..f25830d --- /dev/null +++ b/src/pages/StatisticsPage/utils/dates.ts @@ -0,0 +1,15 @@ +export const getDefaultDates = (): [Date, Date] => { + const dateTo = new Date(); + const dateFrom = new Date(); + dateFrom.setDate(dateTo.getDate() - 28); + return [dateFrom, dateTo]; +}; + +export const dateToString = (date: Date | null) => { + if (date === null) return null; + const month = date.getMonth() + 1; + const day = date.getDate(); + const monthStr = month < 10 ? `0${month}` : month; + const dayStr = day < 10 ? `0${day}` : day; + return `${date.getFullYear()}-${monthStr}-${dayStr}`; +}; diff --git a/src/routes/statistics.lazy.tsx b/src/routes/statistics.lazy.tsx new file mode 100644 index 0000000..f5fef96 --- /dev/null +++ b/src/routes/statistics.lazy.tsx @@ -0,0 +1,6 @@ +import { createLazyFileRoute } from "@tanstack/react-router"; +import { StatisticsPage } from "../pages/StatisticsPage"; + +export const Route = createLazyFileRoute("/statistics")({ + component: StatisticsPage, +}); From 3b8c75d3d356725656c75e61d02c6a57f566623d Mon Sep 17 00:00:00 2001 From: AlexSserb Date: Sun, 17 Nov 2024 01:23:04 +0400 Subject: [PATCH 2/4] feat: profit table and division of charts in statistics --- src/client/index.ts | 10 +- ...equest.ts => GetProfitChartDataRequest.ts} | 2 +- .../models/GetProfitChartDataResponse.ts | 9 ++ .../models/GetProfitTableDataRequest.ts | 10 ++ .../models/GetProfitTableDataResponse.ts | 9 ++ ...ofitDataItem.ts => ProfitChartDataItem.ts} | 2 +- src/client/models/ProfitTableDataItem.ts | 11 +++ ...tDataResponse.ts => ProfitTableGroupBy.ts} | 6 +- src/client/services/StatisticsService.ts | 38 ++++++-- .../components/Filters/Filters.tsx | 90 ------------------ .../components/ProfitTable/ProfitTable.tsx | 52 ---------- .../components/ProfitTable/columns.tsx | 31 ------ .../StatisticsTabSegmentedControl.tsx} | 2 +- .../StatisticsPage/tabs/ProfitTab/Chart.tsx | 31 ------ .../tabs/ProfitTab/ProfitTab.tsx | 70 +------------- .../ProfitTab/components/Filters/Filters.tsx | 94 +++++++++++++++++++ .../components/ProfitChart/ProfitChart.tsx | 55 +++++++++++ .../ProfitChart/hooks/useProfitChart.tsx | 59 ++++++++++++ .../components/ProfitTable/ProfitTable.tsx | 19 ++++ .../components/ProfitTable/hooks/columns.tsx | 53 +++++++++++ .../hooks/useProfitMantineTable.tsx | 36 +++++++ .../ProfitTable/hooks/useProfitTable.tsx | 63 +++++++++++++ .../ProfitTableSegmentedControl.tsx} | 2 +- .../modals/ProfitChartFiltersModal.tsx | 45 +++++++++ .../modals/ProfitTableFiltersModal.tsx | 43 +++++++++ .../StatisticsPage/types/ProfitTableRow.ts | 6 -- .../StatisticsPage/types/TableFormFilters.ts | 2 +- .../ui/StatisticsPage.module.css | 8 ++ .../StatisticsPage/ui/StatisticsPage.tsx | 18 ++-- 29 files changed, 571 insertions(+), 305 deletions(-) rename src/client/models/{GetProfitDataRequest.ts => GetProfitChartDataRequest.ts} (85%) create mode 100644 src/client/models/GetProfitChartDataResponse.ts create mode 100644 src/client/models/GetProfitTableDataRequest.ts create mode 100644 src/client/models/GetProfitTableDataResponse.ts rename src/client/models/{ProfitDataItem.ts => ProfitChartDataItem.ts} (85%) create mode 100644 src/client/models/ProfitTableDataItem.ts rename src/client/models/{GetProfitDataResponse.ts => ProfitTableGroupBy.ts} (50%) delete mode 100644 src/pages/StatisticsPage/components/Filters/Filters.tsx delete mode 100644 src/pages/StatisticsPage/components/ProfitTable/ProfitTable.tsx delete mode 100644 src/pages/StatisticsPage/components/ProfitTable/columns.tsx rename src/pages/StatisticsPage/components/{StatisticsTabSegmentControl/StatisticsTabSegmentControl.tsx => StatisticsTabSegmentedControl/StatisticsTabSegmentedControl.tsx} (89%) delete mode 100644 src/pages/StatisticsPage/tabs/ProfitTab/Chart.tsx create mode 100644 src/pages/StatisticsPage/tabs/ProfitTab/components/Filters/Filters.tsx create mode 100644 src/pages/StatisticsPage/tabs/ProfitTab/components/ProfitChart/ProfitChart.tsx create mode 100644 src/pages/StatisticsPage/tabs/ProfitTab/components/ProfitChart/hooks/useProfitChart.tsx create mode 100644 src/pages/StatisticsPage/tabs/ProfitTab/components/ProfitTable/ProfitTable.tsx create mode 100644 src/pages/StatisticsPage/tabs/ProfitTab/components/ProfitTable/hooks/columns.tsx create mode 100644 src/pages/StatisticsPage/tabs/ProfitTab/components/ProfitTable/hooks/useProfitMantineTable.tsx create mode 100644 src/pages/StatisticsPage/tabs/ProfitTab/components/ProfitTable/hooks/useProfitTable.tsx rename src/pages/StatisticsPage/{components/StatisticsTableSegmentControl/StatisticsTableSegmentControl.tsx => tabs/ProfitTab/components/ProfitTableSegmentedControl/ProfitTableSegmentedControl.tsx} (93%) create mode 100644 src/pages/StatisticsPage/tabs/ProfitTab/modals/ProfitChartFiltersModal.tsx create mode 100644 src/pages/StatisticsPage/tabs/ProfitTab/modals/ProfitTableFiltersModal.tsx delete mode 100644 src/pages/StatisticsPage/types/ProfitTableRow.ts diff --git a/src/client/index.ts b/src/client/index.ts index 24b729c..d97ce91 100644 --- a/src/client/index.ts +++ b/src/client/index.ts @@ -166,8 +166,10 @@ export type { GetProductBarcodePdfRequest } from './models/GetProductBarcodePdfR export type { GetProductBarcodePdfResponse } from './models/GetProductBarcodePdfResponse'; export type { GetProductBarcodeRequest } from './models/GetProductBarcodeRequest'; export type { GetProductBarcodeResponse } from './models/GetProductBarcodeResponse'; -export type { GetProfitDataRequest } from './models/GetProfitDataRequest'; -export type { GetProfitDataResponse } from './models/GetProfitDataResponse'; +export type { GetProfitChartDataRequest } from './models/GetProfitChartDataRequest'; +export type { GetProfitChartDataResponse } from './models/GetProfitChartDataResponse'; +export type { GetProfitTableDataRequest } from './models/GetProfitTableDataRequest'; +export type { GetProfitTableDataResponse } from './models/GetProfitTableDataResponse'; export type { GetServiceKitSchema } from './models/GetServiceKitSchema'; export type { GetTimeTrackingRecordsRequest } from './models/GetTimeTrackingRecordsRequest'; export type { GetTimeTrackingRecordsResponse } from './models/GetTimeTrackingRecordsResponse'; @@ -201,7 +203,9 @@ export type { ProductUpdateRequest } from './models/ProductUpdateRequest'; export type { ProductUpdateResponse } from './models/ProductUpdateResponse'; export type { ProductUploadBarcodeImageResponse } from './models/ProductUploadBarcodeImageResponse'; export type { ProductUploadImageResponse } from './models/ProductUploadImageResponse'; -export type { ProfitDataItem } from './models/ProfitDataItem'; +export type { ProfitChartDataItem } from './models/ProfitChartDataItem'; +export type { ProfitTableDataItem } from './models/ProfitTableDataItem'; +export type { ProfitTableGroupBy } from './models/ProfitTableGroupBy'; export type { RoleSchema } from './models/RoleSchema'; export type { ServiceCategoryPriceSchema } from './models/ServiceCategoryPriceSchema'; export type { ServiceCategoryReorderRequest } from './models/ServiceCategoryReorderRequest'; diff --git a/src/client/models/GetProfitDataRequest.ts b/src/client/models/GetProfitChartDataRequest.ts similarity index 85% rename from src/client/models/GetProfitDataRequest.ts rename to src/client/models/GetProfitChartDataRequest.ts index 47fb791..0d586c4 100644 --- a/src/client/models/GetProfitDataRequest.ts +++ b/src/client/models/GetProfitChartDataRequest.ts @@ -2,7 +2,7 @@ /* istanbul ignore file */ /* tslint:disable */ /* eslint-disable */ -export type GetProfitDataRequest = { +export type GetProfitChartDataRequest = { dateRange: any[]; clientId: number; baseMarketplaceKey: string; diff --git a/src/client/models/GetProfitChartDataResponse.ts b/src/client/models/GetProfitChartDataResponse.ts new file mode 100644 index 0000000..99ff762 --- /dev/null +++ b/src/client/models/GetProfitChartDataResponse.ts @@ -0,0 +1,9 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +import type { ProfitChartDataItem } from './ProfitChartDataItem'; +export type GetProfitChartDataResponse = { + data: Array; +}; + diff --git a/src/client/models/GetProfitTableDataRequest.ts b/src/client/models/GetProfitTableDataRequest.ts new file mode 100644 index 0000000..898bde3 --- /dev/null +++ b/src/client/models/GetProfitTableDataRequest.ts @@ -0,0 +1,10 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +import type { ProfitTableGroupBy } from './ProfitTableGroupBy'; +export type GetProfitTableDataRequest = { + dateRange: any[]; + groupTableBy: ProfitTableGroupBy; +}; + diff --git a/src/client/models/GetProfitTableDataResponse.ts b/src/client/models/GetProfitTableDataResponse.ts new file mode 100644 index 0000000..e03f713 --- /dev/null +++ b/src/client/models/GetProfitTableDataResponse.ts @@ -0,0 +1,9 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +import type { ProfitTableDataItem } from './ProfitTableDataItem'; +export type GetProfitTableDataResponse = { + data: Array; +}; + diff --git a/src/client/models/ProfitDataItem.ts b/src/client/models/ProfitChartDataItem.ts similarity index 85% rename from src/client/models/ProfitDataItem.ts rename to src/client/models/ProfitChartDataItem.ts index d2fdee8..bb63817 100644 --- a/src/client/models/ProfitDataItem.ts +++ b/src/client/models/ProfitChartDataItem.ts @@ -2,7 +2,7 @@ /* istanbul ignore file */ /* tslint:disable */ /* eslint-disable */ -export type ProfitDataItem = { +export type ProfitChartDataItem = { date: string; revenue: number; profit: number; diff --git a/src/client/models/ProfitTableDataItem.ts b/src/client/models/ProfitTableDataItem.ts new file mode 100644 index 0000000..b755b96 --- /dev/null +++ b/src/client/models/ProfitTableDataItem.ts @@ -0,0 +1,11 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +export type ProfitTableDataItem = { + groupedValue: (string | number); + revenue: number; + profit: number; + dealsCount: number; +}; + diff --git a/src/client/models/GetProfitDataResponse.ts b/src/client/models/ProfitTableGroupBy.ts similarity index 50% rename from src/client/models/GetProfitDataResponse.ts rename to src/client/models/ProfitTableGroupBy.ts index 2f7eb91..c9a9c1d 100644 --- a/src/client/models/GetProfitDataResponse.ts +++ b/src/client/models/ProfitTableGroupBy.ts @@ -2,8 +2,4 @@ /* istanbul ignore file */ /* tslint:disable */ /* eslint-disable */ -import type { ProfitDataItem } from './ProfitDataItem'; -export type GetProfitDataResponse = { - data: Array; -}; - +export type ProfitTableGroupBy = 0 | 1 | 2 | 3 | 4; diff --git a/src/client/services/StatisticsService.ts b/src/client/services/StatisticsService.ts index b729913..6a9c77e 100644 --- a/src/client/services/StatisticsService.ts +++ b/src/client/services/StatisticsService.ts @@ -2,25 +2,47 @@ /* istanbul ignore file */ /* tslint:disable */ /* eslint-disable */ -import type { GetProfitDataRequest } from '../models/GetProfitDataRequest'; -import type { GetProfitDataResponse } from '../models/GetProfitDataResponse'; +import type { GetProfitChartDataRequest } from '../models/GetProfitChartDataRequest'; +import type { GetProfitChartDataResponse } from '../models/GetProfitChartDataResponse'; +import type { GetProfitTableDataRequest } from '../models/GetProfitTableDataRequest'; +import type { GetProfitTableDataResponse } from '../models/GetProfitTableDataResponse'; import type { CancelablePromise } from '../core/CancelablePromise'; import { OpenAPI } from '../core/OpenAPI'; import { request as __request } from '../core/request'; export class StatisticsService { /** - * Get Profit Data - * @returns GetProfitDataResponse Successful Response + * Get Profit Chart Data + * @returns GetProfitChartDataResponse Successful Response * @throws ApiError */ - public static getProfitData({ + public static getProfitChartData({ requestBody, }: { - requestBody: GetProfitDataRequest, - }): CancelablePromise { + requestBody: GetProfitChartDataRequest, + }): CancelablePromise { return __request(OpenAPI, { method: 'POST', - url: '/statistics/get-profit-data', + url: '/statistics/get-profit-chart-data', + body: requestBody, + mediaType: 'application/json', + errors: { + 422: `Validation Error`, + }, + }); + } + /** + * Get Profit Table Data + * @returns GetProfitTableDataResponse Successful Response + * @throws ApiError + */ + public static getProfitTableData({ + requestBody, + }: { + requestBody: GetProfitTableDataRequest, + }): CancelablePromise { + return __request(OpenAPI, { + method: 'POST', + url: '/statistics/get-profit-table-data', body: requestBody, mediaType: 'application/json', errors: { diff --git a/src/pages/StatisticsPage/components/Filters/Filters.tsx b/src/pages/StatisticsPage/components/Filters/Filters.tsx deleted file mode 100644 index 9db79f9..0000000 --- a/src/pages/StatisticsPage/components/Filters/Filters.tsx +++ /dev/null @@ -1,90 +0,0 @@ -import PageBlock from "../../../../components/PageBlock/PageBlock.tsx"; -import { DatePickerInput, DatePickerInputProps } from "@mantine/dates"; -import { Group, Text } from "@mantine/core"; -import ClientSelectNew from "../../../../components/Selects/ClientSelectNew/ClientSelectNew.tsx"; -import { BaseMarketplaceSchema, ClientSchema } from "../../../../client"; -import { ObjectSelectProps } from "../../../../components/ObjectSelect/ObjectSelect.tsx"; -import BaseMarketplaceSelect from "../../../../components/Selects/BaseMarketplaceSelect/BaseMarketplaceSelect.tsx"; -import DealStatusSelect from "../../../DealsPage/components/DealStatusSelect/DealStatusSelect.tsx"; -import { DealStatusType } from "../../../../shared/enums/DealStatus.ts"; -import { StatisticsTableSegmentControl } from "../StatisticsTableSegmentControl/StatisticsTableSegmentControl.tsx"; - - -type FiltersProps = { - datePickerProps?: DatePickerInputProps<"range">; - - clientSelectProps?: Omit, "data">; - onClientClear?: () => void; - - baseMarketplaceSelectProps?: Omit< - ObjectSelectProps, - "data" | "getValueFn" | "getLabelFn" - >; - onBaseMarketplaceClear?: () => void; - - dealStatusSelectProps?: Omit, "data">; - onDealStatusClear?: () => void; -} - -export const Filters = (props: FiltersProps) => { - const { - datePickerProps, - clientSelectProps, - onClientClear, - baseMarketplaceSelectProps, - onBaseMarketplaceClear, - dealStatusSelectProps, - onDealStatusClear, - } = props; - - return ( - - - {datePickerProps && - - } - {dealStatusSelectProps && - - } - {clientSelectProps && - - } - {baseMarketplaceSelectProps && - - } - { - <> - - Группировать: - - setGroupTableBy(parseInt(event))} - /> - - } - - - ); -}; \ No newline at end of file diff --git a/src/pages/StatisticsPage/components/ProfitTable/ProfitTable.tsx b/src/pages/StatisticsPage/components/ProfitTable/ProfitTable.tsx deleted file mode 100644 index 4be9973..0000000 --- a/src/pages/StatisticsPage/components/ProfitTable/ProfitTable.tsx +++ /dev/null @@ -1,52 +0,0 @@ -import { useProfitTableColumns } from "./columns.tsx"; -import { ProfitDataItem } from "../../../../client"; -import PageBlock from "../../../../components/PageBlock/PageBlock.tsx"; -import { GroupStatisticsTable } from "../StatisticsTableSegmentControl/StatisticsTableSegmentControl.tsx"; -import styles from "../../ui/StatisticsPage.module.css"; -import { MantineReactTable, useMantineReactTable } from "mantine-react-table"; -import { MRT_Localization_RU } from "mantine-react-table/locales/ru/index.cjs"; -import { Filters } from "../Filters/Filters.tsx"; -import { useForm } from "@mantine/form"; -import { getDefaultDates } from "../../utils/dates.ts"; -import { TableFormFilters } from "../../types/TableFormFilters.ts"; - -type Props = { - data: ProfitDataItem[]; -} - -export const ProfitTable = ({ data }: Props) => { - const form = useForm({ - mode: "controlled", - initialValues: { - dateRange: getDefaultDates(), - groupTableBy: GroupStatisticsTable.BY_DATES, - }, - }); - - const columns = useProfitTableColumns(); - const defaultSorting = [{ id: "date", desc: true }]; - - const table = useMantineReactTable({ - localization: MRT_Localization_RU, - enablePagination: true, - data, - columns, - enableTopToolbar: false, - enableBottomToolbar: true, - enableSorting: true, - initialState: { - sorting: defaultSorting, - }, - }); - - return ( -
- - - - - - -
- ); -}; \ No newline at end of file diff --git a/src/pages/StatisticsPage/components/ProfitTable/columns.tsx b/src/pages/StatisticsPage/components/ProfitTable/columns.tsx deleted file mode 100644 index 4708d87..0000000 --- a/src/pages/StatisticsPage/components/ProfitTable/columns.tsx +++ /dev/null @@ -1,31 +0,0 @@ -import { useMemo } from "react"; -import { MRT_ColumnDef } from "mantine-react-table"; -import { ProfitTableRow } from "../../types/ProfitTableRow.ts"; - -export const useProfitTableColumns = () => { - return useMemo[]>( - () => [ - { - accessorKey: "date", - header: "Дата", - }, - { - accessorKey: "dealsCount", - header: "Кол-во сделок" - }, - { - accessorKey: "profit", - header: "Прибыль", - Cell: ({ row }) => - row.original.profit.toLocaleString("ru-RU") + "₽", - }, - { - accessorKey: "revenue", - header: "Выручка", - Cell: ({ row }) => - row.original.revenue.toLocaleString("ru-RU") + "₽", - }, - ], - [] - ); -}; diff --git a/src/pages/StatisticsPage/components/StatisticsTabSegmentControl/StatisticsTabSegmentControl.tsx b/src/pages/StatisticsPage/components/StatisticsTabSegmentedControl/StatisticsTabSegmentedControl.tsx similarity index 89% rename from src/pages/StatisticsPage/components/StatisticsTabSegmentControl/StatisticsTabSegmentControl.tsx rename to src/pages/StatisticsPage/components/StatisticsTabSegmentedControl/StatisticsTabSegmentedControl.tsx index 14100a7..10e25e7 100644 --- a/src/pages/StatisticsPage/components/StatisticsTabSegmentControl/StatisticsTabSegmentControl.tsx +++ b/src/pages/StatisticsPage/components/StatisticsTabSegmentedControl/StatisticsTabSegmentedControl.tsx @@ -18,7 +18,7 @@ const data = [ }, ]; -export const StatisticsTabSegmentControl: FC = props => { +export const StatisticsTabSegmentedControl: FC = props => { return ( { - return ( - - new Intl.NumberFormat("ru-RU").format(value)} - series={[ - { name: "profit", label: "Прибыль", color: "indigo.6" }, - { name: "revenue", label: "Выручка", color: "teal.6" }, - ]} - fillOpacity={0.5} - /> - - ); -}; \ No newline at end of file diff --git a/src/pages/StatisticsPage/tabs/ProfitTab/ProfitTab.tsx b/src/pages/StatisticsPage/tabs/ProfitTab/ProfitTab.tsx index a9e7309..286a583 100644 --- a/src/pages/StatisticsPage/tabs/ProfitTab/ProfitTab.tsx +++ b/src/pages/StatisticsPage/tabs/ProfitTab/ProfitTab.tsx @@ -1,72 +1,12 @@ -import { useForm } from "@mantine/form"; -import { Chart } from "./Chart.tsx"; -import { Filters } from "../../components/Filters/Filters.tsx"; +import { ProfitChart } from "./components/ProfitChart/ProfitChart.tsx"; import styles from "../../ui/StatisticsPage.module.css"; -import { ChartFormFilters } from "../../types/ChartFormFilters.ts"; -import { useEffect, useState } from "react"; -import { ProfitDataItem, StatisticsService } from "../../../../client"; -import { dateToString, getDefaultDates } from "../../utils/dates.ts"; -import { ProfitTable } from "../../components/ProfitTable/ProfitTable.tsx"; +import { ProfitTable } from "./components/ProfitTable/ProfitTable.tsx"; export const ProfitTab = () => { - const form = useForm({ - mode: "controlled", - initialValues: { - dateRange: getDefaultDates(), - client: null, - marketplace: null, - dealStatus: null, - }, - }); - const [profitData, setProfitData] = useState([]); - - const getFilters = () => { - const dateRange = form.values.dateRange; - - return { - dateRange: [ - dateToString(dateRange[0]), - dateToString(dateRange[1]), - ], - clientId: form.values.client?.id ?? -1, - baseMarketplaceKey: form.values.marketplace?.key ?? "all", - dealStatusId: form.values.dealStatus?.id ?? -1, - }; - }; - - const fetchProfitData = () => { - StatisticsService.getProfitData({ - requestBody: getFilters(), - }) - .then(res => { - setProfitData(res.data); - }) - .catch(err => console.log(err)); - }; - - useEffect(() => { - if (form.values.dateRange.length < 2 || form.values.dateRange[1] === null) { - return; - } - - console.log(form.values); - - fetchProfitData(); - }, [form.values]); - return ( -
- form.setFieldValue("client", null)} - baseMarketplaceSelectProps={form.getInputProps("marketplace")} - onBaseMarketplaceClear={() => form.setFieldValue("marketplace", null)} - dealStatusSelectProps={form.getInputProps("dealStatus")} - onDealStatusClear={() => form.setFieldValue("dealStatus", null)} - /> - - +
+ +
); }; \ No newline at end of file diff --git a/src/pages/StatisticsPage/tabs/ProfitTab/components/Filters/Filters.tsx b/src/pages/StatisticsPage/tabs/ProfitTab/components/Filters/Filters.tsx new file mode 100644 index 0000000..c7b4e35 --- /dev/null +++ b/src/pages/StatisticsPage/tabs/ProfitTab/components/Filters/Filters.tsx @@ -0,0 +1,94 @@ +import { DatePickerInput, DatePickerInputProps } from "@mantine/dates"; +import { Stack, Text } from "@mantine/core"; +import ClientSelectNew from "../../../../../../components/Selects/ClientSelectNew/ClientSelectNew.tsx"; +import { BaseMarketplaceSchema, ClientSchema } from "../../../../../../client"; +import { ObjectSelectProps } from "../../../../../../components/ObjectSelect/ObjectSelect.tsx"; +import BaseMarketplaceSelect + from "../../../../../../components/Selects/BaseMarketplaceSelect/BaseMarketplaceSelect.tsx"; +import DealStatusSelect from "../../../../../DealsPage/components/DealStatusSelect/DealStatusSelect.tsx"; +import { DealStatusType } from "../../../../../../shared/enums/DealStatus.ts"; +import { ProfitTableSegmentedControl } from "../ProfitTableSegmentedControl/ProfitTableSegmentedControl.tsx"; + + +type FiltersProps = { + datePickerProps?: DatePickerInputProps<"range">; + + clientSelectProps?: Omit, "data">; + onClientClear?: () => void; + + baseMarketplaceSelectProps?: Omit< + ObjectSelectProps, + "data" | "getValueFn" | "getLabelFn" + >; + onBaseMarketplaceClear?: () => void; + + dealStatusSelectProps?: Omit, "data">; + onDealStatusClear?: () => void; + + groupTableByProps?: { + value: string, + onChange: (value: string) => void, + }; +} + +export const Filters = (props: FiltersProps) => { + const { + datePickerProps, + clientSelectProps, + onClientClear, + baseMarketplaceSelectProps, + onBaseMarketplaceClear, + dealStatusSelectProps, + onDealStatusClear, + groupTableByProps, + } = props; + + return ( + + {datePickerProps && + + } + {dealStatusSelectProps && + + } + {clientSelectProps && + + } + {baseMarketplaceSelectProps && + + } + {groupTableByProps && + <> + Группировать: + + + } + + ); +}; \ No newline at end of file diff --git a/src/pages/StatisticsPage/tabs/ProfitTab/components/ProfitChart/ProfitChart.tsx b/src/pages/StatisticsPage/tabs/ProfitTab/components/ProfitChart/ProfitChart.tsx new file mode 100644 index 0000000..86b5a88 --- /dev/null +++ b/src/pages/StatisticsPage/tabs/ProfitTab/components/ProfitChart/ProfitChart.tsx @@ -0,0 +1,55 @@ +import { AreaChart } from "@mantine/charts"; +import "@mantine/charts/styles.css"; +import PageBlock from "../../../../../../components/PageBlock/PageBlock.tsx"; +import { useProfitChart } from "./hooks/useProfitChart.tsx"; +import { Skeleton, Stack } from "@mantine/core"; +import { ProfitChartFiltersModal } from "../../modals/ProfitChartFiltersModal.tsx"; + + +export const ProfitChart = () => { + const { + profitData, + form, + isLoading, + } = useProfitChart(); + + const getChartsSeries = [ + [ + { name: "profit", label: "Прибыль", color: "indigo.6" }, + { name: "revenue", label: "Выручка", color: "teal.6" }, + ], + [ + { name: "dealsCount", label: "Количество сделок", color: "indigo.6" }, + ], + ]; + + const units = ["₽", "шт"]; + + return ( + + + + + {getChartsSeries.map((series, idx) => { + return ( + new Intl.NumberFormat("ru-RU").format(value)} + series={series} + fillOpacity={0.5} + /> + ); + })} + + + + ); +}; \ No newline at end of file diff --git a/src/pages/StatisticsPage/tabs/ProfitTab/components/ProfitChart/hooks/useProfitChart.tsx b/src/pages/StatisticsPage/tabs/ProfitTab/components/ProfitChart/hooks/useProfitChart.tsx new file mode 100644 index 0000000..00a41cf --- /dev/null +++ b/src/pages/StatisticsPage/tabs/ProfitTab/components/ProfitChart/hooks/useProfitChart.tsx @@ -0,0 +1,59 @@ +import { ChartFormFilters } from "../../../../../types/ChartFormFilters.ts"; +import { useForm } from "@mantine/form"; +import { dateToString, getDefaultDates } from "../../../../../utils/dates.ts"; +import { useEffect, useState } from "react"; +import { ProfitChartDataItem, StatisticsService } from "../../../../../../../client"; + + +export const useProfitChart = () => { + const form = useForm({ + mode: "controlled", + initialValues: { + dateRange: getDefaultDates(), + client: null, + marketplace: null, + dealStatus: null, + }, + }); + const [profitData, setProfitData] = useState([]); + const [isLoading, setIsLoading] = useState(false); + + const getFilters = () => { + const dateRange = form.values.dateRange; + + return { + dateRange: [ + dateToString(dateRange[0]), + dateToString(dateRange[1]), + ], + clientId: form.values.client?.id ?? -1, + baseMarketplaceKey: form.values.marketplace?.key ?? "all", + dealStatusId: form.values.dealStatus?.id ?? -1, + }; + }; + + const fetchProfitData = () => { + setIsLoading(true); + StatisticsService.getProfitChartData({ + requestBody: getFilters(), + }) + .then(res => { + setProfitData(res.data); + }) + .catch(err => console.log(err)) + .finally(() => setIsLoading(false)); + }; + + useEffect(() => { + if (form.values.dateRange.length < 2 || form.values.dateRange[1] === null) { + return; + } + fetchProfitData(); + }, [form.values]); + + return { + profitData, + form, + isLoading, + }; +}; \ No newline at end of file diff --git a/src/pages/StatisticsPage/tabs/ProfitTab/components/ProfitTable/ProfitTable.tsx b/src/pages/StatisticsPage/tabs/ProfitTab/components/ProfitTable/ProfitTable.tsx new file mode 100644 index 0000000..6d70697 --- /dev/null +++ b/src/pages/StatisticsPage/tabs/ProfitTab/components/ProfitTable/ProfitTable.tsx @@ -0,0 +1,19 @@ +import PageBlock from "../../../../../../components/PageBlock/PageBlock.tsx"; +import { MantineReactTable } from "mantine-react-table"; +import { useProfitTable } from "./hooks/useProfitTable.tsx"; +import { Skeleton } from "@mantine/core"; +import { ProfitTableFiltersModal } from "../../modals/ProfitTableFiltersModal.tsx"; + + +export const ProfitTable = () => { + const { table, form, isLoading } = useProfitTable(); + + return ( + + + + + + + ); +}; \ No newline at end of file diff --git a/src/pages/StatisticsPage/tabs/ProfitTab/components/ProfitTable/hooks/columns.tsx b/src/pages/StatisticsPage/tabs/ProfitTab/components/ProfitTable/hooks/columns.tsx new file mode 100644 index 0000000..e5e742b --- /dev/null +++ b/src/pages/StatisticsPage/tabs/ProfitTab/components/ProfitTable/hooks/columns.tsx @@ -0,0 +1,53 @@ +import { useMemo } from "react"; +import { MRT_ColumnDef } from "mantine-react-table"; +import { ProfitTableDataItem } from "../../../../../../../client"; +import { GroupStatisticsTable } from "../../ProfitTableSegmentedControl/ProfitTableSegmentedControl.tsx"; +import { DealStatus, DealStatusDictionary } from "../../../../../../../shared/enums/DealStatus.ts"; + +type Props = { + groupTableBy: GroupStatisticsTable; +} + +export const useProfitTableColumns = ({ groupTableBy }: Props) => { + const groupedValueHeader = { + [GroupStatisticsTable.BY_DATES]: "Дата", + [GroupStatisticsTable.BY_CLIENTS]: "Клиент", + [GroupStatisticsTable.BY_STATUSES]: "Статус", + [GroupStatisticsTable.BY_MARKETPLACES]: "Маркетплейс", + [GroupStatisticsTable.BY_WAREHOUSES]: "Склад отгрузки", + }; + + return useMemo[]>( + () => [ + { + accessorKey: "groupedValue", + header: groupedValueHeader[groupTableBy], + enableSorting: groupTableBy === GroupStatisticsTable.BY_DATES, + Cell: ({ row }) => { + if (groupTableBy === GroupStatisticsTable.BY_STATUSES) { + const statusIndex = row.original.groupedValue as DealStatus; + return DealStatusDictionary[statusIndex]; + } + return row.original.groupedValue; + }, + }, + { + accessorKey: "dealsCount", + header: "Кол-во", + }, + { + accessorKey: "profit", + header: "Прибыль", + Cell: ({ row }) => + row.original.profit.toLocaleString("ru-RU") + "₽", + }, + { + accessorKey: "revenue", + header: "Выручка", + Cell: ({ row }) => + row.original.revenue.toLocaleString("ru-RU") + "₽", + }, + ], + [groupTableBy], + ); +}; diff --git a/src/pages/StatisticsPage/tabs/ProfitTab/components/ProfitTable/hooks/useProfitMantineTable.tsx b/src/pages/StatisticsPage/tabs/ProfitTab/components/ProfitTable/hooks/useProfitMantineTable.tsx new file mode 100644 index 0000000..2abed81 --- /dev/null +++ b/src/pages/StatisticsPage/tabs/ProfitTab/components/ProfitTable/hooks/useProfitMantineTable.tsx @@ -0,0 +1,36 @@ +import { GroupStatisticsTable } from "../../ProfitTableSegmentedControl/ProfitTableSegmentedControl.tsx"; +import { useProfitTableColumns } from "./columns.tsx"; +import { useMantineReactTable } from "mantine-react-table"; +import { ProfitTableDataItem } from "../../../../../../../client"; +import { MRT_Localization_RU } from "mantine-react-table/locales/ru/index.cjs"; + + +type Props = { + groupTableBy: GroupStatisticsTable; + profitData: ProfitTableDataItem[]; +} + +export const useProfitMantineTable = ({ groupTableBy, profitData }: Props) => { + const columns = useProfitTableColumns({ + groupTableBy, + }); + + const defaultSorting = [{ id: "groupedValue", desc: true }]; + + const table = useMantineReactTable({ + enablePagination: false, + data: profitData, + columns, + enableTopToolbar: false, + enableBottomToolbar: false, + enableSorting: true, + initialState: { + sorting: defaultSorting, + }, + localization: MRT_Localization_RU, + enableRowVirtualization: true, + mantineTableContainerProps: { style: { maxHeight: "86vh" } }, + }); + + return { table }; +}; \ No newline at end of file diff --git a/src/pages/StatisticsPage/tabs/ProfitTab/components/ProfitTable/hooks/useProfitTable.tsx b/src/pages/StatisticsPage/tabs/ProfitTab/components/ProfitTable/hooks/useProfitTable.tsx new file mode 100644 index 0000000..3717aeb --- /dev/null +++ b/src/pages/StatisticsPage/tabs/ProfitTab/components/ProfitTable/hooks/useProfitTable.tsx @@ -0,0 +1,63 @@ +import { useForm } from "@mantine/form"; +import { TableFormFilters } from "../../../../../types/TableFormFilters.ts"; +import { dateToString, getDefaultDates } from "../../../../../utils/dates.ts"; +import { GroupStatisticsTable } from "../../ProfitTableSegmentedControl/ProfitTableSegmentedControl.tsx"; +import { useEffect, useState } from "react"; +import { ProfitTableDataItem, StatisticsService } from "../../../../../../../client"; +import { useProfitMantineTable } from "./useProfitMantineTable.tsx"; + + +export const useProfitTable = () => { + const form = useForm({ + mode: "controlled", + initialValues: { + dateRange: getDefaultDates(), + groupTableBy: GroupStatisticsTable.BY_DATES, + }, + }); + const [isLoading, setIsLoading] = useState(false); + + const [profitData, setProfitData] = useState([]); + + const { table } = useProfitMantineTable({ + groupTableBy: form.values.groupTableBy, + profitData, + }); + + const getFilters = () => { + const dateRange = form.values.dateRange; + + return { + dateRange: [ + dateToString(dateRange[0]), + dateToString(dateRange[1]), + ], + groupTableBy: form.values.groupTableBy, + }; + }; + + const fetchProfitData = () => { + setIsLoading(true); + StatisticsService.getProfitTableData({ + requestBody: getFilters(), + }) + .then(res => { + setProfitData(res.data); + }) + .catch(err => console.log(err)) + .finally(() => setIsLoading(false)); + }; + + useEffect(() => { + if (form.values.dateRange.length < 2 || form.values.dateRange[1] === null) { + return; + } + fetchProfitData(); + }, [form.values]); + + return { + table, + form, + isLoading, + }; +}; \ No newline at end of file diff --git a/src/pages/StatisticsPage/components/StatisticsTableSegmentControl/StatisticsTableSegmentControl.tsx b/src/pages/StatisticsPage/tabs/ProfitTab/components/ProfitTableSegmentedControl/ProfitTableSegmentedControl.tsx similarity index 93% rename from src/pages/StatisticsPage/components/StatisticsTableSegmentControl/StatisticsTableSegmentControl.tsx rename to src/pages/StatisticsPage/tabs/ProfitTab/components/ProfitTableSegmentedControl/ProfitTableSegmentedControl.tsx index b901735..5f1aee1 100644 --- a/src/pages/StatisticsPage/components/StatisticsTableSegmentControl/StatisticsTableSegmentControl.tsx +++ b/src/pages/StatisticsPage/tabs/ProfitTab/components/ProfitTableSegmentedControl/ProfitTableSegmentedControl.tsx @@ -33,7 +33,7 @@ const data = [ }, ]; -export const StatisticsTableSegmentControl: FC = props => { +export const ProfitTableSegmentedControl: FC = props => { return ( ; +} + +export const ProfitChartFiltersModal = ({ form }: Props) => { + const [opened, { open, close }] = useDisclosure(); + + return ( + <> + + + form.setFieldValue("client", null)} + baseMarketplaceSelectProps={form.getInputProps("marketplace")} + onBaseMarketplaceClear={() => form.setFieldValue("marketplace", null)} + dealStatusSelectProps={form.getInputProps("dealStatus")} + onDealStatusClear={() => form.setFieldValue("dealStatus", null)} + /> + + + ); +}; \ No newline at end of file diff --git a/src/pages/StatisticsPage/tabs/ProfitTab/modals/ProfitTableFiltersModal.tsx b/src/pages/StatisticsPage/tabs/ProfitTab/modals/ProfitTableFiltersModal.tsx new file mode 100644 index 0000000..b95d3ee --- /dev/null +++ b/src/pages/StatisticsPage/tabs/ProfitTab/modals/ProfitTableFiltersModal.tsx @@ -0,0 +1,43 @@ +import { Button, Group, Modal } from "@mantine/core"; +import { IconFilter } from "@tabler/icons-react"; +import { Filters } from "../components/Filters/Filters.tsx"; +import { TableFormFilters } from "../../../types/TableFormFilters.ts"; +import { UseFormReturnType } from "@mantine/form"; +import { useDisclosure } from "@mantine/hooks"; + + +type Props = { + form: UseFormReturnType; +} + +export const ProfitTableFiltersModal = ({ form }: Props) => { + const [opened, { open, close }] = useDisclosure(); + + return ( + <> + + + form.setFieldValue("groupTableBy", parseInt(value)), + }} + /> + + + ); +}; \ No newline at end of file diff --git a/src/pages/StatisticsPage/types/ProfitTableRow.ts b/src/pages/StatisticsPage/types/ProfitTableRow.ts deleted file mode 100644 index f775a01..0000000 --- a/src/pages/StatisticsPage/types/ProfitTableRow.ts +++ /dev/null @@ -1,6 +0,0 @@ -export type ProfitTableRow = { - date: string; - dealsCount: number; - profit: number; - revenue: number; -} \ No newline at end of file diff --git a/src/pages/StatisticsPage/types/TableFormFilters.ts b/src/pages/StatisticsPage/types/TableFormFilters.ts index eb9b802..689493e 100644 --- a/src/pages/StatisticsPage/types/TableFormFilters.ts +++ b/src/pages/StatisticsPage/types/TableFormFilters.ts @@ -1,4 +1,4 @@ -import { GroupStatisticsTable } from "../components/StatisticsTableSegmentControl/StatisticsTableSegmentControl.tsx"; +import { GroupStatisticsTable } from "../tabs/ProfitTab/components/ProfitTableSegmentedControl/ProfitTableSegmentedControl.tsx"; export interface TableFormFilters { dateRange: [Date | null, Date | null]; diff --git a/src/pages/StatisticsPage/ui/StatisticsPage.module.css b/src/pages/StatisticsPage/ui/StatisticsPage.module.css index 61ac571..2407bc3 100644 --- a/src/pages/StatisticsPage/ui/StatisticsPage.module.css +++ b/src/pages/StatisticsPage/ui/StatisticsPage.module.css @@ -5,6 +5,14 @@ gap: rem(10); } +.page-container { + display: flex; + flex-direction: row; + gap: rem(10); + height: 96vh; + flex-wrap: wrap; +} + .top-panel { padding: rem(5); gap: rem(10); diff --git a/src/pages/StatisticsPage/ui/StatisticsPage.tsx b/src/pages/StatisticsPage/ui/StatisticsPage.tsx index 3436bf2..491d599 100644 --- a/src/pages/StatisticsPage/ui/StatisticsPage.tsx +++ b/src/pages/StatisticsPage/ui/StatisticsPage.tsx @@ -1,8 +1,8 @@ import { useState } from "react"; import { StatisticsTab, - StatisticsTabSegmentControl, -} from "../components/StatisticsTabSegmentControl/StatisticsTabSegmentControl.tsx"; + StatisticsTabSegmentedControl, +} from "../components/StatisticsTabSegmentedControl/StatisticsTabSegmentedControl.tsx"; import styles from "./StatisticsPage.module.css"; import PageBlock from "../../../components/PageBlock/PageBlock.tsx"; import { ProfitTab } from "../tabs/ProfitTab/ProfitTab.tsx"; @@ -25,13 +25,13 @@ export const StatisticsPage = () => { return (
- - setServiceType(parseInt(event))} - /> - + {/**/} + {/* setServiceType(parseInt(event))}*/} + {/* />*/} + {/**/} {getBody()}
); From 873ab7e2bb8c27925e9814bbafe7899003c2ac7d Mon Sep 17 00:00:00 2001 From: AlexSserb Date: Sun, 17 Nov 2024 01:33:11 +0400 Subject: [PATCH 3/4] fix: fixed wrapping on statistics page --- .../tabs/ProfitTab/components/ProfitChart/ProfitChart.tsx | 2 +- .../tabs/ProfitTab/components/ProfitTable/ProfitTable.tsx | 2 +- .../tabs/ProfitTab/components/ProfitTable/hooks/columns.tsx | 4 ++++ 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/pages/StatisticsPage/tabs/ProfitTab/components/ProfitChart/ProfitChart.tsx b/src/pages/StatisticsPage/tabs/ProfitTab/components/ProfitChart/ProfitChart.tsx index 86b5a88..dc9a16c 100644 --- a/src/pages/StatisticsPage/tabs/ProfitTab/components/ProfitChart/ProfitChart.tsx +++ b/src/pages/StatisticsPage/tabs/ProfitTab/components/ProfitChart/ProfitChart.tsx @@ -26,7 +26,7 @@ export const ProfitChart = () => { const units = ["₽", "шт"]; return ( - + diff --git a/src/pages/StatisticsPage/tabs/ProfitTab/components/ProfitTable/ProfitTable.tsx b/src/pages/StatisticsPage/tabs/ProfitTab/components/ProfitTable/ProfitTable.tsx index 6d70697..61a1f1f 100644 --- a/src/pages/StatisticsPage/tabs/ProfitTab/components/ProfitTable/ProfitTable.tsx +++ b/src/pages/StatisticsPage/tabs/ProfitTab/components/ProfitTable/ProfitTable.tsx @@ -9,7 +9,7 @@ export const ProfitTable = () => { const { table, form, isLoading } = useProfitTable(); return ( - + diff --git a/src/pages/StatisticsPage/tabs/ProfitTab/components/ProfitTable/hooks/columns.tsx b/src/pages/StatisticsPage/tabs/ProfitTab/components/ProfitTable/hooks/columns.tsx index e5e742b..408fce1 100644 --- a/src/pages/StatisticsPage/tabs/ProfitTab/components/ProfitTable/hooks/columns.tsx +++ b/src/pages/StatisticsPage/tabs/ProfitTab/components/ProfitTable/hooks/columns.tsx @@ -30,22 +30,26 @@ export const useProfitTableColumns = ({ groupTableBy }: Props) => { } return row.original.groupedValue; }, + size: 60, }, { accessorKey: "dealsCount", header: "Кол-во", + size: 40, }, { accessorKey: "profit", header: "Прибыль", Cell: ({ row }) => row.original.profit.toLocaleString("ru-RU") + "₽", + size: 50, }, { accessorKey: "revenue", header: "Выручка", Cell: ({ row }) => row.original.revenue.toLocaleString("ru-RU") + "₽", + size: 50, }, ], [groupTableBy], From 7bebd40f8152279f8a1e4adfd408e8e4f0104a2c Mon Sep 17 00:00:00 2001 From: AlexSserb Date: Sun, 17 Nov 2024 13:55:58 +0400 Subject: [PATCH 4/4] feat: setting manager for a deal --- src/client/index.ts | 1 + src/client/models/DealGeneralInfoSchema.ts | 2 ++ src/client/models/DealSchema.ts | 2 ++ src/client/services/UserService.ts | 12 ++++++++++ .../ManagerSelect/ManagerSelect.tsx | 23 +++++++++++++++++++ .../tabs/DealEditDrawerGeneralTab.tsx | 6 +++++ src/pages/LeadsPage/hooks/useManagersList.tsx | 11 +++++++++ 7 files changed, 57 insertions(+) create mode 100644 src/components/ManagerSelect/ManagerSelect.tsx create mode 100644 src/pages/LeadsPage/hooks/useManagersList.tsx diff --git a/src/client/index.ts b/src/client/index.ts index d97ce91..d76b9ea 100644 --- a/src/client/index.ts +++ b/src/client/index.ts @@ -161,6 +161,7 @@ export type { GetClientMarketplacesResponse } from './models/GetClientMarketplac export type { GetDealBillById } from './models/GetDealBillById'; export type { GetDealProductsBarcodesPdfRequest } from './models/GetDealProductsBarcodesPdfRequest'; export type { GetDealProductsBarcodesPdfResponse } from './models/GetDealProductsBarcodesPdfResponse'; +export type { GetManagersResponse } from './models/GetManagersResponse'; export type { GetPaymentRecordsResponse } from './models/GetPaymentRecordsResponse'; export type { GetProductBarcodePdfRequest } from './models/GetProductBarcodePdfRequest'; export type { GetProductBarcodePdfResponse } from './models/GetProductBarcodePdfResponse'; diff --git a/src/client/models/DealGeneralInfoSchema.ts b/src/client/models/DealGeneralInfoSchema.ts index 210cbb2..4d5906c 100644 --- a/src/client/models/DealGeneralInfoSchema.ts +++ b/src/client/models/DealGeneralInfoSchema.ts @@ -2,6 +2,7 @@ /* istanbul ignore file */ /* tslint:disable */ /* eslint-disable */ +import type { UserSchema } from './UserSchema'; export type DealGeneralInfoSchema = { name: string; isDeleted: boolean; @@ -10,5 +11,6 @@ export type DealGeneralInfoSchema = { shippingWarehouse?: (string | null); deliveryDate?: (string | null); receivingSlotDate?: (string | null); + manager?: (UserSchema | null); }; diff --git a/src/client/models/DealSchema.ts b/src/client/models/DealSchema.ts index da00c3c..6856353 100644 --- a/src/client/models/DealSchema.ts +++ b/src/client/models/DealSchema.ts @@ -9,6 +9,7 @@ import type { DealServiceSchema } from './DealServiceSchema'; import type { DealStatusHistorySchema } from './DealStatusHistorySchema'; import type { ServicePriceCategorySchema } from './ServicePriceCategorySchema'; import type { ShippingWarehouseSchema } from './ShippingWarehouseSchema'; +import type { UserSchema } from './UserSchema'; export type DealSchema = { id: number; name: string; @@ -26,6 +27,7 @@ export type DealSchema = { shippingWarehouse?: (ShippingWarehouseSchema | string | null); billRequest?: (DealBillRequestSchema | null); category?: (ServicePriceCategorySchema | null); + manager?: (UserSchema | null); deliveryDate?: (string | null); receivingSlotDate?: (string | null); }; diff --git a/src/client/services/UserService.ts b/src/client/services/UserService.ts index b1037fa..3339e4d 100644 --- a/src/client/services/UserService.ts +++ b/src/client/services/UserService.ts @@ -5,6 +5,7 @@ import type { CreateUserRequest } from '../models/CreateUserRequest'; import type { CreateUserResponse } from '../models/CreateUserResponse'; import type { GetAllUsersResponse } from '../models/GetAllUsersResponse'; +import type { GetManagersResponse } from '../models/GetManagersResponse'; import type { UpdateUserRequest } from '../models/UpdateUserRequest'; import type { UpdateUserResponse } from '../models/UpdateUserResponse'; import type { CancelablePromise } from '../core/CancelablePromise'; @@ -62,4 +63,15 @@ export class UserService { }, }); } + /** + * Get Managers + * @returns GetManagersResponse Successful Response + * @throws ApiError + */ + public static getManagers(): CancelablePromise { + return __request(OpenAPI, { + method: 'GET', + url: '/user/get-managers', + }); + } } diff --git a/src/components/ManagerSelect/ManagerSelect.tsx b/src/components/ManagerSelect/ManagerSelect.tsx new file mode 100644 index 0000000..6cdb7d9 --- /dev/null +++ b/src/components/ManagerSelect/ManagerSelect.tsx @@ -0,0 +1,23 @@ +import { FC } from "react"; +import ObjectSelect, { ObjectSelectProps } from "../ObjectSelect/ObjectSelect.tsx"; +import { UserSchema } from "../../client"; +import useManagersList from "../../pages/LeadsPage/hooks/useManagersList.tsx"; + +type Props = Omit< + ObjectSelectProps, + "data" | "getValueFn" | "getLabelFn" +>; +const UserSelect: FC = props => { + const { objects: managers } = useManagersList(); + return ( + `${manager.firstName} ${manager.secondName}`} + getValueFn={(manager: UserSchema) => manager.id.toString()} + clearable + {...props} + onClear={() => props.onChange(null)} + /> + ); +}; +export default UserSelect; diff --git a/src/pages/LeadsPage/drawers/DealEditDrawer/tabs/DealEditDrawerGeneralTab.tsx b/src/pages/LeadsPage/drawers/DealEditDrawer/tabs/DealEditDrawerGeneralTab.tsx index 70d253e..2a63e8f 100644 --- a/src/pages/LeadsPage/drawers/DealEditDrawer/tabs/DealEditDrawerGeneralTab.tsx +++ b/src/pages/LeadsPage/drawers/DealEditDrawer/tabs/DealEditDrawerGeneralTab.tsx @@ -30,6 +30,7 @@ import { IconBarcode, IconPrinter } from "@tabler/icons-react"; import styles from "../../../ui/LeadsPage.module.css"; import { base64ToBlob } from "../../../../../shared/lib/utils.ts"; import { DatePickerInput } from "@mantine/dates"; +import ManagerSelect from "../../../../../components/ManagerSelect/ManagerSelect.tsx"; type Props = { deal: DealSchema; @@ -209,6 +210,11 @@ const Content: FC = ({ deal }) => { {...form.getInputProps("receivingSlotDate")} /> + + ObjectList({ + queryFn: UserService.getManagers, + getObjectsFn: response => response.managers, + queryKey: "getManagers", + }); + +export default useManagersList;