feat: profit table and division of charts in statistics
This commit is contained in:
		@@ -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';
 | 
			
		||||
 
 | 
			
		||||
@@ -2,7 +2,7 @@
 | 
			
		||||
/* istanbul ignore file */
 | 
			
		||||
/* tslint:disable */
 | 
			
		||||
/* eslint-disable */
 | 
			
		||||
export type GetProfitDataRequest = {
 | 
			
		||||
export type GetProfitChartDataRequest = {
 | 
			
		||||
    dateRange: any[];
 | 
			
		||||
    clientId: number;
 | 
			
		||||
    baseMarketplaceKey: string;
 | 
			
		||||
							
								
								
									
										9
									
								
								src/client/models/GetProfitChartDataResponse.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								src/client/models/GetProfitChartDataResponse.ts
									
									
									
									
									
										Normal file
									
								
							@@ -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<ProfitChartDataItem>;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										10
									
								
								src/client/models/GetProfitTableDataRequest.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								src/client/models/GetProfitTableDataRequest.ts
									
									
									
									
									
										Normal file
									
								
							@@ -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;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										9
									
								
								src/client/models/GetProfitTableDataResponse.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								src/client/models/GetProfitTableDataResponse.ts
									
									
									
									
									
										Normal file
									
								
							@@ -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<ProfitTableDataItem>;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@@ -2,7 +2,7 @@
 | 
			
		||||
/* istanbul ignore file */
 | 
			
		||||
/* tslint:disable */
 | 
			
		||||
/* eslint-disable */
 | 
			
		||||
export type ProfitDataItem = {
 | 
			
		||||
export type ProfitChartDataItem = {
 | 
			
		||||
    date: string;
 | 
			
		||||
    revenue: number;
 | 
			
		||||
    profit: number;
 | 
			
		||||
							
								
								
									
										11
									
								
								src/client/models/ProfitTableDataItem.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								src/client/models/ProfitTableDataItem.ts
									
									
									
									
									
										Normal file
									
								
							@@ -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;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@@ -2,8 +2,4 @@
 | 
			
		||||
/* istanbul ignore file */
 | 
			
		||||
/* tslint:disable */
 | 
			
		||||
/* eslint-disable */
 | 
			
		||||
import type { ProfitDataItem } from './ProfitDataItem';
 | 
			
		||||
export type GetProfitDataResponse = {
 | 
			
		||||
    data: Array<ProfitDataItem>;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export type ProfitTableGroupBy = 0 | 1 | 2 | 3 | 4;
 | 
			
		||||
@@ -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<GetProfitDataResponse> {
 | 
			
		||||
        requestBody: GetProfitChartDataRequest,
 | 
			
		||||
    }): CancelablePromise<GetProfitChartDataResponse> {
 | 
			
		||||
        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<GetProfitTableDataResponse> {
 | 
			
		||||
        return __request(OpenAPI, {
 | 
			
		||||
            method: 'POST',
 | 
			
		||||
            url: '/statistics/get-profit-table-data',
 | 
			
		||||
            body: requestBody,
 | 
			
		||||
            mediaType: 'application/json',
 | 
			
		||||
            errors: {
 | 
			
		||||
 
 | 
			
		||||
@@ -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<ObjectSelectProps<ClientSchema>, "data">;
 | 
			
		||||
    onClientClear?: () => void;
 | 
			
		||||
 | 
			
		||||
    baseMarketplaceSelectProps?: Omit<
 | 
			
		||||
        ObjectSelectProps<BaseMarketplaceSchema>,
 | 
			
		||||
        "data" | "getValueFn" | "getLabelFn"
 | 
			
		||||
    >;
 | 
			
		||||
    onBaseMarketplaceClear?: () => void;
 | 
			
		||||
 | 
			
		||||
    dealStatusSelectProps?: Omit<ObjectSelectProps<DealStatusType>, "data">;
 | 
			
		||||
    onDealStatusClear?: () => void;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const Filters = (props: FiltersProps) => {
 | 
			
		||||
    const {
 | 
			
		||||
        datePickerProps,
 | 
			
		||||
        clientSelectProps,
 | 
			
		||||
        onClientClear,
 | 
			
		||||
        baseMarketplaceSelectProps,
 | 
			
		||||
        onBaseMarketplaceClear,
 | 
			
		||||
        dealStatusSelectProps,
 | 
			
		||||
        onDealStatusClear,
 | 
			
		||||
    } = props;
 | 
			
		||||
 | 
			
		||||
    return (
 | 
			
		||||
        <PageBlock>
 | 
			
		||||
            <Group>
 | 
			
		||||
                {datePickerProps &&
 | 
			
		||||
                    <DatePickerInput
 | 
			
		||||
                        {...datePickerProps}
 | 
			
		||||
                        type="range"
 | 
			
		||||
                        placeholder="Выберите даты"
 | 
			
		||||
                        maxDate={new Date()}
 | 
			
		||||
                    />
 | 
			
		||||
                }
 | 
			
		||||
                {dealStatusSelectProps &&
 | 
			
		||||
                    <DealStatusSelect
 | 
			
		||||
                        {...dealStatusSelectProps}
 | 
			
		||||
                        onClear={onDealStatusClear}
 | 
			
		||||
                        clearable
 | 
			
		||||
                        placeholder={"Выберите статус"}
 | 
			
		||||
                    />
 | 
			
		||||
                }
 | 
			
		||||
                {clientSelectProps &&
 | 
			
		||||
                    <ClientSelectNew
 | 
			
		||||
                        {...clientSelectProps}
 | 
			
		||||
                        onClear={onClientClear}
 | 
			
		||||
                        clearable
 | 
			
		||||
                        searchable
 | 
			
		||||
                        placeholder={"Выберите клиента"}
 | 
			
		||||
                    />
 | 
			
		||||
                }
 | 
			
		||||
                {baseMarketplaceSelectProps &&
 | 
			
		||||
                    <BaseMarketplaceSelect
 | 
			
		||||
                        {...baseMarketplaceSelectProps}
 | 
			
		||||
                        onClear={onBaseMarketplaceClear}
 | 
			
		||||
                        clearable
 | 
			
		||||
                        placeholder={"Выберите маркетплейс"}
 | 
			
		||||
                    />
 | 
			
		||||
                }
 | 
			
		||||
                {
 | 
			
		||||
                    <>
 | 
			
		||||
                        <Text>
 | 
			
		||||
                            Группировать:
 | 
			
		||||
                        </Text>
 | 
			
		||||
                        <StatisticsTableSegmentControl
 | 
			
		||||
                            value={groupTableBy.toString()}
 | 
			
		||||
                            onChange={event => setGroupTableBy(parseInt(event))}
 | 
			
		||||
                        />
 | 
			
		||||
                    </>
 | 
			
		||||
                }
 | 
			
		||||
            </Group>
 | 
			
		||||
        </PageBlock>
 | 
			
		||||
    );
 | 
			
		||||
};
 | 
			
		||||
@@ -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<TableFormFilters>({
 | 
			
		||||
        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 (
 | 
			
		||||
        <div className={styles["container"]}>
 | 
			
		||||
            <PageBlock>
 | 
			
		||||
                <Filters />
 | 
			
		||||
            </PageBlock>
 | 
			
		||||
            <PageBlock>
 | 
			
		||||
                <MantineReactTable table={table} />
 | 
			
		||||
            </PageBlock>
 | 
			
		||||
        </div>
 | 
			
		||||
    );
 | 
			
		||||
};
 | 
			
		||||
@@ -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<MRT_ColumnDef<ProfitTableRow>[]>(
 | 
			
		||||
        () => [
 | 
			
		||||
            {
 | 
			
		||||
                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") + "₽",
 | 
			
		||||
            },
 | 
			
		||||
        ],
 | 
			
		||||
        []
 | 
			
		||||
    );
 | 
			
		||||
};
 | 
			
		||||
@@ -18,7 +18,7 @@ const data = [
 | 
			
		||||
    },
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
export const StatisticsTabSegmentControl: FC<Props> = props => {
 | 
			
		||||
export const StatisticsTabSegmentedControl: FC<Props> = props => {
 | 
			
		||||
    return (
 | 
			
		||||
        <SegmentedControl
 | 
			
		||||
            data={data}
 | 
			
		||||
@@ -1,31 +0,0 @@
 | 
			
		||||
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 (
 | 
			
		||||
        <PageBlock>
 | 
			
		||||
            <AreaChart
 | 
			
		||||
                mx={"lg"}
 | 
			
		||||
                my={"sm"}
 | 
			
		||||
                w={"98%"}
 | 
			
		||||
                h={"50vh"}
 | 
			
		||||
                data={data}
 | 
			
		||||
                dataKey="date"
 | 
			
		||||
                unit="₽"
 | 
			
		||||
                tooltipAnimationDuration={200}
 | 
			
		||||
                valueFormatter={(value) => new Intl.NumberFormat("ru-RU").format(value)}
 | 
			
		||||
                series={[
 | 
			
		||||
                    { name: "profit", label: "Прибыль", color: "indigo.6" },
 | 
			
		||||
                    { name: "revenue", label: "Выручка", color: "teal.6" },
 | 
			
		||||
                ]}
 | 
			
		||||
                fillOpacity={0.5}
 | 
			
		||||
            />
 | 
			
		||||
        </PageBlock>
 | 
			
		||||
    );
 | 
			
		||||
};
 | 
			
		||||
@@ -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<ChartFormFilters>({
 | 
			
		||||
        mode: "controlled",
 | 
			
		||||
        initialValues: {
 | 
			
		||||
            dateRange: getDefaultDates(),
 | 
			
		||||
            client: null,
 | 
			
		||||
            marketplace: null,
 | 
			
		||||
            dealStatus: null,
 | 
			
		||||
        },
 | 
			
		||||
    });
 | 
			
		||||
    const [profitData, setProfitData] = useState<ProfitDataItem[]>([]);
 | 
			
		||||
 | 
			
		||||
    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 (
 | 
			
		||||
        <div className={styles["container"]}>
 | 
			
		||||
            <Filters
 | 
			
		||||
                datePickerProps={form.getInputProps("dateRange")}
 | 
			
		||||
                clientSelectProps={form.getInputProps("client")}
 | 
			
		||||
                onClientClear={() => form.setFieldValue("client", null)}
 | 
			
		||||
                baseMarketplaceSelectProps={form.getInputProps("marketplace")}
 | 
			
		||||
                onBaseMarketplaceClear={() => form.setFieldValue("marketplace", null)}
 | 
			
		||||
                dealStatusSelectProps={form.getInputProps("dealStatus")}
 | 
			
		||||
                onDealStatusClear={() => form.setFieldValue("dealStatus", null)}
 | 
			
		||||
            />
 | 
			
		||||
            <Chart data={profitData} />
 | 
			
		||||
            <ProfitTable data={profitData} />
 | 
			
		||||
        <div className={styles["page-container"]}>
 | 
			
		||||
            <ProfitChart />
 | 
			
		||||
            <ProfitTable />
 | 
			
		||||
        </div>
 | 
			
		||||
    );
 | 
			
		||||
};
 | 
			
		||||
@@ -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<ObjectSelectProps<ClientSchema>, "data">;
 | 
			
		||||
    onClientClear?: () => void;
 | 
			
		||||
 | 
			
		||||
    baseMarketplaceSelectProps?: Omit<
 | 
			
		||||
        ObjectSelectProps<BaseMarketplaceSchema>,
 | 
			
		||||
        "data" | "getValueFn" | "getLabelFn"
 | 
			
		||||
    >;
 | 
			
		||||
    onBaseMarketplaceClear?: () => void;
 | 
			
		||||
 | 
			
		||||
    dealStatusSelectProps?: Omit<ObjectSelectProps<DealStatusType>, "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 (
 | 
			
		||||
        <Stack mb={"lg"}>
 | 
			
		||||
            {datePickerProps &&
 | 
			
		||||
                <DatePickerInput
 | 
			
		||||
                    {...datePickerProps}
 | 
			
		||||
                    type="range"
 | 
			
		||||
                    placeholder="Выберите даты"
 | 
			
		||||
                    maxDate={new Date()}
 | 
			
		||||
                />
 | 
			
		||||
            }
 | 
			
		||||
            {dealStatusSelectProps &&
 | 
			
		||||
                <DealStatusSelect
 | 
			
		||||
                    {...dealStatusSelectProps}
 | 
			
		||||
                    onClear={onDealStatusClear}
 | 
			
		||||
                    clearable
 | 
			
		||||
                    placeholder={"Выберите статус"}
 | 
			
		||||
                />
 | 
			
		||||
            }
 | 
			
		||||
            {clientSelectProps &&
 | 
			
		||||
                <ClientSelectNew
 | 
			
		||||
                    {...clientSelectProps}
 | 
			
		||||
                    onClear={onClientClear}
 | 
			
		||||
                    clearable
 | 
			
		||||
                    searchable
 | 
			
		||||
                    placeholder={"Выберите клиента"}
 | 
			
		||||
                />
 | 
			
		||||
            }
 | 
			
		||||
            {baseMarketplaceSelectProps &&
 | 
			
		||||
                <BaseMarketplaceSelect
 | 
			
		||||
                    {...baseMarketplaceSelectProps}
 | 
			
		||||
                    onClear={onBaseMarketplaceClear}
 | 
			
		||||
                    clearable
 | 
			
		||||
                    placeholder={"Выберите маркетплейс"}
 | 
			
		||||
                />
 | 
			
		||||
            }
 | 
			
		||||
            {groupTableByProps &&
 | 
			
		||||
                <>
 | 
			
		||||
                    <Text>Группировать:</Text>
 | 
			
		||||
                    <ProfitTableSegmentedControl
 | 
			
		||||
                        {...groupTableByProps}
 | 
			
		||||
                        orientation={"vertical"}
 | 
			
		||||
                        size={"md"}
 | 
			
		||||
                        w={"100%"}
 | 
			
		||||
                    />
 | 
			
		||||
                </>
 | 
			
		||||
            }
 | 
			
		||||
        </Stack>
 | 
			
		||||
    );
 | 
			
		||||
};
 | 
			
		||||
@@ -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 (
 | 
			
		||||
        <PageBlock style={{ flex: 3, minWidth: "500px", padding: "25px" }}>
 | 
			
		||||
            <ProfitChartFiltersModal
 | 
			
		||||
                form={form}
 | 
			
		||||
            />
 | 
			
		||||
            <Skeleton visible={isLoading}>
 | 
			
		||||
                <Stack gap={"xl"}>
 | 
			
		||||
                    {getChartsSeries.map((series, idx) => {
 | 
			
		||||
                        return (
 | 
			
		||||
                            <AreaChart
 | 
			
		||||
                                my={"sm"}
 | 
			
		||||
                                w={"98%"}
 | 
			
		||||
                                h={"39vh"}
 | 
			
		||||
                                data={profitData}
 | 
			
		||||
                                dataKey="date"
 | 
			
		||||
                                unit={units[idx]}
 | 
			
		||||
                                tooltipAnimationDuration={200}
 | 
			
		||||
                                valueFormatter={(value) => new Intl.NumberFormat("ru-RU").format(value)}
 | 
			
		||||
                                series={series}
 | 
			
		||||
                                fillOpacity={0.5}
 | 
			
		||||
                            />
 | 
			
		||||
                        );
 | 
			
		||||
                    })}
 | 
			
		||||
                </Stack>
 | 
			
		||||
            </Skeleton>
 | 
			
		||||
        </PageBlock>
 | 
			
		||||
    );
 | 
			
		||||
};
 | 
			
		||||
@@ -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<ChartFormFilters>({
 | 
			
		||||
        mode: "controlled",
 | 
			
		||||
        initialValues: {
 | 
			
		||||
            dateRange: getDefaultDates(),
 | 
			
		||||
            client: null,
 | 
			
		||||
            marketplace: null,
 | 
			
		||||
            dealStatus: null,
 | 
			
		||||
        },
 | 
			
		||||
    });
 | 
			
		||||
    const [profitData, setProfitData] = useState<ProfitChartDataItem[]>([]);
 | 
			
		||||
    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,
 | 
			
		||||
    };
 | 
			
		||||
};
 | 
			
		||||
@@ -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 (
 | 
			
		||||
        <PageBlock style={{ flex: 1, padding: "25px" }}>
 | 
			
		||||
            <ProfitTableFiltersModal form={form} />
 | 
			
		||||
            <Skeleton visible={isLoading}>
 | 
			
		||||
                <MantineReactTable table={table} />
 | 
			
		||||
            </Skeleton>
 | 
			
		||||
        </PageBlock>
 | 
			
		||||
    );
 | 
			
		||||
};
 | 
			
		||||
@@ -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<MRT_ColumnDef<ProfitTableDataItem>[]>(
 | 
			
		||||
        () => [
 | 
			
		||||
            {
 | 
			
		||||
                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],
 | 
			
		||||
    );
 | 
			
		||||
};
 | 
			
		||||
@@ -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 };
 | 
			
		||||
};
 | 
			
		||||
@@ -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<TableFormFilters>({
 | 
			
		||||
        mode: "controlled",
 | 
			
		||||
        initialValues: {
 | 
			
		||||
            dateRange: getDefaultDates(),
 | 
			
		||||
            groupTableBy: GroupStatisticsTable.BY_DATES,
 | 
			
		||||
        },
 | 
			
		||||
    });
 | 
			
		||||
    const [isLoading, setIsLoading] = useState(false);
 | 
			
		||||
 | 
			
		||||
    const [profitData, setProfitData] = useState<ProfitTableDataItem[]>([]);
 | 
			
		||||
 | 
			
		||||
    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,
 | 
			
		||||
    };
 | 
			
		||||
};
 | 
			
		||||
@@ -33,7 +33,7 @@ const data = [
 | 
			
		||||
    },
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
export const StatisticsTableSegmentControl: FC<Props> = props => {
 | 
			
		||||
export const ProfitTableSegmentedControl: FC<Props> = props => {
 | 
			
		||||
    return (
 | 
			
		||||
        <SegmentedControl
 | 
			
		||||
            data={data}
 | 
			
		||||
@@ -0,0 +1,45 @@
 | 
			
		||||
import { Button, Group, Modal } from "@mantine/core";
 | 
			
		||||
import { IconFilter } from "@tabler/icons-react";
 | 
			
		||||
import { Filters } from "../components/Filters/Filters.tsx";
 | 
			
		||||
import { UseFormReturnType } from "@mantine/form";
 | 
			
		||||
import { useDisclosure } from "@mantine/hooks";
 | 
			
		||||
import { ChartFormFilters } from "../../../types/ChartFormFilters.ts";
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
type Props = {
 | 
			
		||||
    form: UseFormReturnType<ChartFormFilters>;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const ProfitChartFiltersModal = ({ form }: Props) => {
 | 
			
		||||
    const [opened, { open, close }] = useDisclosure();
 | 
			
		||||
 | 
			
		||||
    return (
 | 
			
		||||
        <>
 | 
			
		||||
            <Button
 | 
			
		||||
                variant={"default"}
 | 
			
		||||
                onClick={open}
 | 
			
		||||
                mb={"lg"}
 | 
			
		||||
            >
 | 
			
		||||
                <Group gap={"xs"}>
 | 
			
		||||
                    <IconFilter />
 | 
			
		||||
                    Фильтры графиков
 | 
			
		||||
                </Group>
 | 
			
		||||
            </Button>
 | 
			
		||||
            <Modal
 | 
			
		||||
                opened={opened}
 | 
			
		||||
                onClose={close}
 | 
			
		||||
                title={"Фильтры графиков"}
 | 
			
		||||
            >
 | 
			
		||||
                <Filters
 | 
			
		||||
                    datePickerProps={form.getInputProps("dateRange")}
 | 
			
		||||
                    clientSelectProps={form.getInputProps("client")}
 | 
			
		||||
                    onClientClear={() => form.setFieldValue("client", null)}
 | 
			
		||||
                    baseMarketplaceSelectProps={form.getInputProps("marketplace")}
 | 
			
		||||
                    onBaseMarketplaceClear={() => form.setFieldValue("marketplace", null)}
 | 
			
		||||
                    dealStatusSelectProps={form.getInputProps("dealStatus")}
 | 
			
		||||
                    onDealStatusClear={() => form.setFieldValue("dealStatus", null)}
 | 
			
		||||
                />
 | 
			
		||||
            </Modal>
 | 
			
		||||
        </>
 | 
			
		||||
    );
 | 
			
		||||
};
 | 
			
		||||
@@ -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<TableFormFilters>;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const ProfitTableFiltersModal = ({ form }: Props) => {
 | 
			
		||||
    const [opened, { open, close }] = useDisclosure();
 | 
			
		||||
 | 
			
		||||
    return (
 | 
			
		||||
        <>
 | 
			
		||||
            <Button
 | 
			
		||||
                variant={"default"}
 | 
			
		||||
                onClick={open}
 | 
			
		||||
                mb={"lg"}
 | 
			
		||||
            >
 | 
			
		||||
                <Group gap={"xs"}>
 | 
			
		||||
                    <IconFilter />
 | 
			
		||||
                    Фильтры таблицы
 | 
			
		||||
                </Group>
 | 
			
		||||
            </Button>
 | 
			
		||||
            <Modal
 | 
			
		||||
                opened={opened}
 | 
			
		||||
                onClose={close}
 | 
			
		||||
                title={"Фильтры таблицы"}
 | 
			
		||||
            >
 | 
			
		||||
                <Filters
 | 
			
		||||
                    datePickerProps={form.getInputProps("dateRange")}
 | 
			
		||||
                    groupTableByProps={{
 | 
			
		||||
                        value: form.values.groupTableBy.toString(),
 | 
			
		||||
                        onChange: (value: string) => form.setFieldValue("groupTableBy", parseInt(value)),
 | 
			
		||||
                    }}
 | 
			
		||||
                />
 | 
			
		||||
            </Modal>
 | 
			
		||||
        </>
 | 
			
		||||
    );
 | 
			
		||||
};
 | 
			
		||||
@@ -1,6 +0,0 @@
 | 
			
		||||
export type ProfitTableRow = {
 | 
			
		||||
    date: string;
 | 
			
		||||
    dealsCount: number;
 | 
			
		||||
    profit: number;
 | 
			
		||||
    revenue: number;
 | 
			
		||||
}
 | 
			
		||||
@@ -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];
 | 
			
		||||
 
 | 
			
		||||
@@ -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);
 | 
			
		||||
 
 | 
			
		||||
@@ -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 (
 | 
			
		||||
        <div className={styles["container"]}>
 | 
			
		||||
            <PageBlock>
 | 
			
		||||
                <StatisticsTabSegmentControl
 | 
			
		||||
                    size={"md"}
 | 
			
		||||
                    value={serviceType.toString()}
 | 
			
		||||
                    onChange={event => setServiceType(parseInt(event))}
 | 
			
		||||
                />
 | 
			
		||||
            </PageBlock>
 | 
			
		||||
            {/*<PageBlock>*/}
 | 
			
		||||
            {/*    <StatisticsTabSegmentedControl*/}
 | 
			
		||||
            {/*        size={"md"}*/}
 | 
			
		||||
            {/*        value={serviceType.toString()}*/}
 | 
			
		||||
            {/*        onChange={event => setServiceType(parseInt(event))}*/}
 | 
			
		||||
            {/*    />*/}
 | 
			
		||||
            {/*</PageBlock>*/}
 | 
			
		||||
            {getBody()}
 | 
			
		||||
        </div>
 | 
			
		||||
    );
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user