feat: profit table and division of charts in statistics
This commit is contained in:
@@ -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