feat: profit chart in statistics
This commit is contained in:
@@ -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": {
|
||||
|
||||
@@ -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';
|
||||
|
||||
11
src/client/models/GetProfitDataRequest.ts
Normal file
11
src/client/models/GetProfitDataRequest.ts
Normal file
@@ -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;
|
||||
};
|
||||
|
||||
9
src/client/models/GetProfitDataResponse.ts
Normal file
9
src/client/models/GetProfitDataResponse.ts
Normal file
@@ -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<ProfitDataItem>;
|
||||
};
|
||||
|
||||
11
src/client/models/ProfitDataItem.ts
Normal file
11
src/client/models/ProfitDataItem.ts
Normal file
@@ -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;
|
||||
};
|
||||
|
||||
31
src/client/services/StatisticsService.ts
Normal file
31
src/client/services/StatisticsService.ts
Normal file
@@ -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<GetProfitDataResponse> {
|
||||
return __request(OpenAPI, {
|
||||
method: 'POST',
|
||||
url: '/statistics/get-profit-data',
|
||||
body: requestBody,
|
||||
mediaType: 'application/json',
|
||||
errors: {
|
||||
422: `Validation Error`,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -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() {
|
||||
|
||||
90
src/pages/StatisticsPage/components/Filters/Filters.tsx
Normal file
90
src/pages/StatisticsPage/components/Filters/Filters.tsx
Normal file
@@ -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<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>
|
||||
);
|
||||
};
|
||||
@@ -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<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>
|
||||
);
|
||||
};
|
||||
31
src/pages/StatisticsPage/components/ProfitTable/columns.tsx
Normal file
31
src/pages/StatisticsPage/components/ProfitTable/columns.tsx
Normal file
@@ -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<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") + "₽",
|
||||
},
|
||||
],
|
||||
[]
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,28 @@
|
||||
import { SegmentedControl, SegmentedControlProps } from "@mantine/core";
|
||||
import { FC } from "react";
|
||||
|
||||
export enum StatisticsTab {
|
||||
PROFIT,
|
||||
SALARIES,
|
||||
}
|
||||
|
||||
type Props = Omit<SegmentedControlProps, "data">;
|
||||
const data = [
|
||||
{
|
||||
label: "Выручка по сделкам",
|
||||
value: StatisticsTab.PROFIT.toString(),
|
||||
},
|
||||
{
|
||||
label: "Зарплаты",
|
||||
value: StatisticsTab.SALARIES.toString(),
|
||||
},
|
||||
];
|
||||
|
||||
export const StatisticsTabSegmentControl: FC<Props> = props => {
|
||||
return (
|
||||
<SegmentedControl
|
||||
data={data}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -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<SegmentedControlProps, "data">;
|
||||
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> = props => {
|
||||
return (
|
||||
<SegmentedControl
|
||||
data={data}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
};
|
||||
1
src/pages/StatisticsPage/index.ts
Normal file
1
src/pages/StatisticsPage/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export { StatisticsPage } from "./ui/StatisticsPage.tsx";
|
||||
31
src/pages/StatisticsPage/tabs/ProfitTab/Chart.tsx
Normal file
31
src/pages/StatisticsPage/tabs/ProfitTab/Chart.tsx
Normal file
@@ -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 (
|
||||
<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>
|
||||
);
|
||||
};
|
||||
72
src/pages/StatisticsPage/tabs/ProfitTab/ProfitTab.tsx
Normal file
72
src/pages/StatisticsPage/tabs/ProfitTab/ProfitTab.tsx
Normal file
@@ -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<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>
|
||||
);
|
||||
};
|
||||
9
src/pages/StatisticsPage/types/ChartFormFilters.ts
Normal file
9
src/pages/StatisticsPage/types/ChartFormFilters.ts
Normal file
@@ -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;
|
||||
}
|
||||
6
src/pages/StatisticsPage/types/ProfitTableRow.ts
Normal file
6
src/pages/StatisticsPage/types/ProfitTableRow.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
export type ProfitTableRow = {
|
||||
date: string;
|
||||
dealsCount: number;
|
||||
profit: number;
|
||||
revenue: number;
|
||||
}
|
||||
6
src/pages/StatisticsPage/types/TableFormFilters.ts
Normal file
6
src/pages/StatisticsPage/types/TableFormFilters.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import { GroupStatisticsTable } from "../components/StatisticsTableSegmentControl/StatisticsTableSegmentControl.tsx";
|
||||
|
||||
export interface TableFormFilters {
|
||||
dateRange: [Date | null, Date | null];
|
||||
groupTableBy: GroupStatisticsTable;
|
||||
}
|
||||
12
src/pages/StatisticsPage/ui/StatisticsPage.module.css
Normal file
12
src/pages/StatisticsPage/ui/StatisticsPage.module.css
Normal file
@@ -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;
|
||||
}
|
||||
38
src/pages/StatisticsPage/ui/StatisticsPage.tsx
Normal file
38
src/pages/StatisticsPage/ui/StatisticsPage.tsx
Normal file
@@ -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 (
|
||||
<ProfitTab />
|
||||
);
|
||||
case StatisticsTab.SALARIES:
|
||||
return (
|
||||
<>Статистика по ЗП</>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={styles["container"]}>
|
||||
<PageBlock>
|
||||
<StatisticsTabSegmentControl
|
||||
size={"md"}
|
||||
value={serviceType.toString()}
|
||||
onChange={event => setServiceType(parseInt(event))}
|
||||
/>
|
||||
</PageBlock>
|
||||
{getBody()}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
15
src/pages/StatisticsPage/utils/dates.ts
Normal file
15
src/pages/StatisticsPage/utils/dates.ts
Normal file
@@ -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}`;
|
||||
};
|
||||
6
src/routes/statistics.lazy.tsx
Normal file
6
src/routes/statistics.lazy.tsx
Normal file
@@ -0,0 +1,6 @@
|
||||
import { createLazyFileRoute } from "@tanstack/react-router";
|
||||
import { StatisticsPage } from "../pages/StatisticsPage";
|
||||
|
||||
export const Route = createLazyFileRoute("/statistics")({
|
||||
component: StatisticsPage,
|
||||
});
|
||||
Reference in New Issue
Block a user