From 431bc35d8be1cf344bedb21f73bbe3f6a02a5be7 Mon Sep 17 00:00:00 2001 From: AlexSserb Date: Wed, 27 Nov 2024 15:57:56 +0400 Subject: [PATCH] feat: tags for expenses, filters by tags in statistics --- src/client/index.ts | 7 + src/client/models/BaseExpenseTagSchema.ts | 8 ++ src/client/models/CreateExpenseTagRequest.ts | 9 ++ src/client/models/DeleteExpenseTagResponse.ts | 9 ++ src/client/models/ExpenseSchemaBase.ts | 2 + src/client/models/ExpenseTagSchema.ts | 9 ++ .../models/GetAllExpenseTagsResponse.ts | 9 ++ src/client/models/PaginationInfoSchema.ts | 4 +- src/client/models/UpdateExpenseSchema.ts | 1 + src/client/models/UpdateExpenseTagRequest.ts | 9 ++ src/client/models/UpdateExpenseTagResponse.ts | 9 ++ src/client/services/ExpenseService.ts | 77 +++++++++++ src/hooks/useCRUD.tsx | 8 +- src/modals/modals.ts | 2 + .../AdminPage/hooks/useExpenseTagsList.tsx | 10 ++ .../AdminPage/tabs/Expenses/ExpensesTab.tsx | 31 ++++- .../components/CreateExpenseTagForm.tsx | 39 ++++++ .../Expenses/components/ExpenseTagsInput.tsx | 19 +++ .../Expenses/components/ExpenseTagsTable.tsx | 84 ++++++++++++ .../hooks/useExpenseTagsTableColumns.tsx | 35 +++++ .../hooks/useExpensesTableColumns.tsx | 5 + .../tabs/Expenses/modals/ExpenseFormModal.tsx | 12 +- .../tabs/Expenses/modals/ExpenseTagsModal.tsx | 65 +++++++++ .../AdminPage/tabs/Expenses/types/Expense.tsx | 11 ++ .../ExpenseTagSelect/ExpenseTagSelect.tsx | 23 ++++ .../tabs/ProfitTab/ProfitTab.tsx | 11 +- .../ProfitTab/components/Filters/Filters.tsx | 31 ++++- .../components/ProfitChart/ProfitChart.tsx | 17 +-- .../ProfitChart/hooks/useProfitChart.tsx | 63 --------- .../components/ProfitTable/ProfitTable.tsx | 4 +- .../hooks/useProfitMantineTable.tsx | 36 ----- .../ProfitTable/hooks/useProfitTable.tsx | 83 ++++-------- .../ProfitTab/contexts/ProfitTabContext.tsx | 125 ++++++++++++++++++ .../modals/ProfitChartFiltersModal.tsx | 47 ------- ...iltersModal.tsx => ProfitFiltersModal.tsx} | 12 +- .../{TableFormFilters.ts => FormFilters.ts} | 5 +- src/types/CRUDTable.tsx | 4 +- 37 files changed, 687 insertions(+), 248 deletions(-) create mode 100644 src/client/models/BaseExpenseTagSchema.ts create mode 100644 src/client/models/CreateExpenseTagRequest.ts create mode 100644 src/client/models/DeleteExpenseTagResponse.ts create mode 100644 src/client/models/ExpenseTagSchema.ts create mode 100644 src/client/models/GetAllExpenseTagsResponse.ts create mode 100644 src/client/models/UpdateExpenseTagRequest.ts create mode 100644 src/client/models/UpdateExpenseTagResponse.ts create mode 100644 src/pages/AdminPage/hooks/useExpenseTagsList.tsx create mode 100644 src/pages/AdminPage/tabs/Expenses/components/CreateExpenseTagForm.tsx create mode 100644 src/pages/AdminPage/tabs/Expenses/components/ExpenseTagsInput.tsx create mode 100644 src/pages/AdminPage/tabs/Expenses/components/ExpenseTagsTable.tsx create mode 100644 src/pages/AdminPage/tabs/Expenses/hooks/useExpenseTagsTableColumns.tsx create mode 100644 src/pages/AdminPage/tabs/Expenses/modals/ExpenseTagsModal.tsx create mode 100644 src/pages/AdminPage/tabs/Expenses/types/Expense.tsx create mode 100644 src/pages/StatisticsPage/components/ExpenseTagSelect/ExpenseTagSelect.tsx delete mode 100644 src/pages/StatisticsPage/tabs/ProfitTab/components/ProfitChart/hooks/useProfitChart.tsx delete mode 100644 src/pages/StatisticsPage/tabs/ProfitTab/components/ProfitTable/hooks/useProfitMantineTable.tsx create mode 100644 src/pages/StatisticsPage/tabs/ProfitTab/contexts/ProfitTabContext.tsx delete mode 100644 src/pages/StatisticsPage/tabs/ProfitTab/modals/ProfitChartFiltersModal.tsx rename src/pages/StatisticsPage/tabs/ProfitTab/modals/{ProfitTableFiltersModal.tsx => ProfitFiltersModal.tsx} (82%) rename src/pages/StatisticsPage/types/{TableFormFilters.ts => FormFilters.ts} (72%) diff --git a/src/client/index.ts b/src/client/index.ts index 551c19e..d5bdf04 100644 --- a/src/client/index.ts +++ b/src/client/index.ts @@ -25,6 +25,7 @@ export type { BarcodeTemplateUpdateRequest } from './models/BarcodeTemplateUpdat export type { BarcodeTemplateUpdateResponse } from './models/BarcodeTemplateUpdateResponse'; export type { BaseEnumListSchema } from './models/BaseEnumListSchema'; export type { BaseEnumSchema } from './models/BaseEnumSchema'; +export type { BaseExpenseTagSchema } from './models/BaseExpenseTagSchema'; export type { BaseMarketplaceSchema } from './models/BaseMarketplaceSchema'; export type { BaseShippingWarehouseSchema } from './models/BaseShippingWarehouseSchema'; export type { BillPaymentStatus } from './models/BillPaymentStatus'; @@ -47,6 +48,7 @@ export type { CreateBarcodeTemplateAttributeRequest } from './models/CreateBarco export type { CreateBarcodeTemplateAttributeResponse } from './models/CreateBarcodeTemplateAttributeResponse'; export type { CreateDealBillRequest } from './models/CreateDealBillRequest'; export type { CreateDealBillResponse } from './models/CreateDealBillResponse'; +export type { CreateExpenseTagRequest } from './models/CreateExpenseTagRequest'; export type { CreateMarketplaceRequest } from './models/CreateMarketplaceRequest'; export type { CreateMarketplaceResponse } from './models/CreateMarketplaceResponse'; export type { CreatePaymentRecordRequest } from './models/CreatePaymentRecordRequest'; @@ -133,6 +135,7 @@ export type { DealUpdateServiceQuantityResponse } from './models/DealUpdateServi export type { DealUpdateServiceRequest } from './models/DealUpdateServiceRequest'; export type { DealUpdateServiceResponse } from './models/DealUpdateServiceResponse'; export type { DeleteExpenseResponse } from './models/DeleteExpenseResponse'; +export type { DeleteExpenseTagResponse } from './models/DeleteExpenseTagResponse'; export type { DeleteMarketplaceRequest } from './models/DeleteMarketplaceRequest'; export type { DeleteMarketplaceResponse } from './models/DeleteMarketplaceResponse'; export type { DeletePaymentRecordRequest } from './models/DeletePaymentRecordRequest'; @@ -147,6 +150,7 @@ export type { DeleteShiftResponse } from './models/DeleteShiftResponse'; export type { DeleteShippingWarehouseRequest } from './models/DeleteShippingWarehouseRequest'; export type { DeleteShippingWarehouseResponse } from './models/DeleteShippingWarehouseResponse'; export type { ExpenseSchemaBase } from './models/ExpenseSchemaBase'; +export type { ExpenseTagSchema } from './models/ExpenseTagSchema'; export type { FinishShiftByIdResponse } from './models/FinishShiftByIdResponse'; export type { FinishShiftResponse } from './models/FinishShiftResponse'; export type { GetAllBarcodeTemplateAttributesResponse } from './models/GetAllBarcodeTemplateAttributesResponse'; @@ -154,6 +158,7 @@ export type { GetAllBarcodeTemplateSizesResponse } from './models/GetAllBarcodeT export type { GetAllBarcodeTemplatesResponse } from './models/GetAllBarcodeTemplatesResponse'; export type { GetAllBaseMarketplacesResponse } from './models/GetAllBaseMarketplacesResponse'; export type { GetAllExpensesResponse } from './models/GetAllExpensesResponse'; +export type { GetAllExpenseTagsResponse } from './models/GetAllExpenseTagsResponse'; export type { GetAllPayRatesResponse } from './models/GetAllPayRatesResponse'; export type { GetAllPayrollSchemeResponse } from './models/GetAllPayrollSchemeResponse'; export type { GetAllPositionsResponse } from './models/GetAllPositionsResponse'; @@ -249,6 +254,8 @@ export type { TimeTrackingRecord } from './models/TimeTrackingRecord'; export type { UpdateExpenseRequest } from './models/UpdateExpenseRequest'; export type { UpdateExpenseResponse } from './models/UpdateExpenseResponse'; export type { UpdateExpenseSchema } from './models/UpdateExpenseSchema'; +export type { UpdateExpenseTagRequest } from './models/UpdateExpenseTagRequest'; +export type { UpdateExpenseTagResponse } from './models/UpdateExpenseTagResponse'; export type { UpdateMarketplaceRequest } from './models/UpdateMarketplaceRequest'; export type { UpdateMarketplaceResponse } from './models/UpdateMarketplaceResponse'; export type { UpdatePayRateRequest } from './models/UpdatePayRateRequest'; diff --git a/src/client/models/BaseExpenseTagSchema.ts b/src/client/models/BaseExpenseTagSchema.ts new file mode 100644 index 0000000..74d9b3a --- /dev/null +++ b/src/client/models/BaseExpenseTagSchema.ts @@ -0,0 +1,8 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +export type BaseExpenseTagSchema = { + name: string; +}; + diff --git a/src/client/models/CreateExpenseTagRequest.ts b/src/client/models/CreateExpenseTagRequest.ts new file mode 100644 index 0000000..355d6f9 --- /dev/null +++ b/src/client/models/CreateExpenseTagRequest.ts @@ -0,0 +1,9 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +import type { BaseExpenseTagSchema } from './BaseExpenseTagSchema'; +export type CreateExpenseTagRequest = { + tag: BaseExpenseTagSchema; +}; + diff --git a/src/client/models/DeleteExpenseTagResponse.ts b/src/client/models/DeleteExpenseTagResponse.ts new file mode 100644 index 0000000..2901793 --- /dev/null +++ b/src/client/models/DeleteExpenseTagResponse.ts @@ -0,0 +1,9 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +export type DeleteExpenseTagResponse = { + ok: boolean; + message: string; +}; + diff --git a/src/client/models/ExpenseSchemaBase.ts b/src/client/models/ExpenseSchemaBase.ts index dce0bb2..7cebe98 100644 --- a/src/client/models/ExpenseSchemaBase.ts +++ b/src/client/models/ExpenseSchemaBase.ts @@ -2,6 +2,7 @@ /* istanbul ignore file */ /* tslint:disable */ /* eslint-disable */ +import type { ExpenseTagSchema } from './ExpenseTagSchema'; import type { UserSchema } from './UserSchema'; export type ExpenseSchemaBase = { id: number; @@ -10,5 +11,6 @@ export type ExpenseSchemaBase = { amount: number; createdByUser: UserSchema; spentDate: string; + tags: Array; }; diff --git a/src/client/models/ExpenseTagSchema.ts b/src/client/models/ExpenseTagSchema.ts new file mode 100644 index 0000000..276e9cf --- /dev/null +++ b/src/client/models/ExpenseTagSchema.ts @@ -0,0 +1,9 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +export type ExpenseTagSchema = { + name: string; + id: number; +}; + diff --git a/src/client/models/GetAllExpenseTagsResponse.ts b/src/client/models/GetAllExpenseTagsResponse.ts new file mode 100644 index 0000000..c4b657c --- /dev/null +++ b/src/client/models/GetAllExpenseTagsResponse.ts @@ -0,0 +1,9 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +import type { ExpenseTagSchema } from './ExpenseTagSchema'; +export type GetAllExpenseTagsResponse = { + tags: Array; +}; + diff --git a/src/client/models/PaginationInfoSchema.ts b/src/client/models/PaginationInfoSchema.ts index 9fb7e01..fe40eb4 100644 --- a/src/client/models/PaginationInfoSchema.ts +++ b/src/client/models/PaginationInfoSchema.ts @@ -3,7 +3,7 @@ /* tslint:disable */ /* eslint-disable */ export type PaginationInfoSchema = { - totalPages: number; - totalItems: number; + totalPages?: number; + totalItems?: number; }; diff --git a/src/client/models/UpdateExpenseSchema.ts b/src/client/models/UpdateExpenseSchema.ts index fc1eff9..bf3defc 100644 --- a/src/client/models/UpdateExpenseSchema.ts +++ b/src/client/models/UpdateExpenseSchema.ts @@ -8,5 +8,6 @@ export type UpdateExpenseSchema = { comment?: (string | null); amount: number; spentDate: string; + tags?: Array; }; diff --git a/src/client/models/UpdateExpenseTagRequest.ts b/src/client/models/UpdateExpenseTagRequest.ts new file mode 100644 index 0000000..a114632 --- /dev/null +++ b/src/client/models/UpdateExpenseTagRequest.ts @@ -0,0 +1,9 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +import type { ExpenseTagSchema } from './ExpenseTagSchema'; +export type UpdateExpenseTagRequest = { + tag: ExpenseTagSchema; +}; + diff --git a/src/client/models/UpdateExpenseTagResponse.ts b/src/client/models/UpdateExpenseTagResponse.ts new file mode 100644 index 0000000..4b7f023 --- /dev/null +++ b/src/client/models/UpdateExpenseTagResponse.ts @@ -0,0 +1,9 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +export type UpdateExpenseTagResponse = { + ok: boolean; + message: string; +}; + diff --git a/src/client/services/ExpenseService.ts b/src/client/services/ExpenseService.ts index 9aafed3..bb3b085 100644 --- a/src/client/services/ExpenseService.ts +++ b/src/client/services/ExpenseService.ts @@ -2,10 +2,15 @@ /* istanbul ignore file */ /* tslint:disable */ /* eslint-disable */ +import type { CreateExpenseTagRequest } from '../models/CreateExpenseTagRequest'; import type { DeleteExpenseResponse } from '../models/DeleteExpenseResponse'; +import type { DeleteExpenseTagResponse } from '../models/DeleteExpenseTagResponse'; import type { GetAllExpensesResponse } from '../models/GetAllExpensesResponse'; +import type { GetAllExpenseTagsResponse } from '../models/GetAllExpenseTagsResponse'; import type { UpdateExpenseRequest } from '../models/UpdateExpenseRequest'; import type { UpdateExpenseResponse } from '../models/UpdateExpenseResponse'; +import type { UpdateExpenseTagRequest } from '../models/UpdateExpenseTagRequest'; +import type { UpdateExpenseTagResponse } from '../models/UpdateExpenseTagResponse'; import type { CancelablePromise } from '../core/CancelablePromise'; import { OpenAPI } from '../core/OpenAPI'; import { request as __request } from '../core/request'; @@ -75,4 +80,76 @@ export class ExpenseService { }, }); } + /** + * Get All + * @returns GetAllExpenseTagsResponse Successful Response + * @throws ApiError + */ + public static getAllExpenseTags(): CancelablePromise { + return __request(OpenAPI, { + method: 'GET', + url: '/expense/get-all-tags', + }); + } + /** + * Update Expense + * @returns UpdateExpenseTagResponse Successful Response + * @throws ApiError + */ + public static createExpenseTag({ + requestBody, + }: { + requestBody: CreateExpenseTagRequest, + }): CancelablePromise { + return __request(OpenAPI, { + method: 'POST', + url: '/expense/create-tag', + body: requestBody, + mediaType: 'application/json', + errors: { + 422: `Validation Error`, + }, + }); + } + /** + * Update Expense + * @returns UpdateExpenseTagResponse Successful Response + * @throws ApiError + */ + public static updateExpenseTag({ + requestBody, + }: { + requestBody: UpdateExpenseTagRequest, + }): CancelablePromise { + return __request(OpenAPI, { + method: 'POST', + url: '/expense/update-tag', + body: requestBody, + mediaType: 'application/json', + errors: { + 422: `Validation Error`, + }, + }); + } + /** + * Update Expense + * @returns DeleteExpenseTagResponse Successful Response + * @throws ApiError + */ + public static deleteExpenseTag({ + tagId, + }: { + tagId: number, + }): CancelablePromise { + return __request(OpenAPI, { + method: 'DELETE', + url: '/expense/delete-tag/{tag_id}', + path: { + 'tag_id': tagId, + }, + errors: { + 422: `Validation Error`, + }, + }); + } } diff --git a/src/hooks/useCRUD.tsx b/src/hooks/useCRUD.tsx index a2d0f94..715a475 100644 --- a/src/hooks/useCRUD.tsx +++ b/src/hooks/useCRUD.tsx @@ -1,11 +1,11 @@ -type Props = { - onCreate: (element: T) => void; +type Props = { + onCreate: (element: T | C) => void; onChange: (element: T) => void; onDelete: (element: T) => void; }; -export function useCRUD(props: Props) { - const onCreate = (element: T) => { +export function useCRUD(props: Props) { + const onCreate = (element: T | C) => { props.onCreate(element); }; const onChange = (element: T) => { diff --git a/src/modals/modals.ts b/src/modals/modals.ts index d704743..c84c907 100644 --- a/src/modals/modals.ts +++ b/src/modals/modals.ts @@ -23,6 +23,7 @@ import MarketplaceFormModal from "../pages/MarketplacesPage/modals/MarketplaceFo import ServicePriceCategoryForm from "../pages/ServicesPage/modals/ServicePriceCategoryForm.tsx"; import ScanningModal from "./ScanningModal/ScanningModal.tsx"; import ExpenseFormModal from "../pages/AdminPage/tabs/Expenses/modals/ExpenseFormModal.tsx"; +import ExpenseTagsModal from "../pages/AdminPage/tabs/Expenses/modals/ExpenseTagsModal.tsx"; export const modals = { enterDeadline: EnterDeadlineModal, @@ -50,4 +51,5 @@ export const modals = { servicePriceCategoryForm: ServicePriceCategoryForm, scanningModal: ScanningModal, expenseFormModal: ExpenseFormModal, + expenseTagsModal: ExpenseTagsModal, }; diff --git a/src/pages/AdminPage/hooks/useExpenseTagsList.tsx b/src/pages/AdminPage/hooks/useExpenseTagsList.tsx new file mode 100644 index 0000000..5f4213f --- /dev/null +++ b/src/pages/AdminPage/hooks/useExpenseTagsList.tsx @@ -0,0 +1,10 @@ +import ObjectList from "../../../hooks/objectList.tsx"; +import { ExpenseService } from "../../../client"; + +const useExpenseTagsList = () => + ObjectList({ + queryFn: ExpenseService.getAllExpenseTags, + getObjectsFn: response => response.tags, + queryKey: "getAllExpenseTags", + }); +export default useExpenseTagsList; \ No newline at end of file diff --git a/src/pages/AdminPage/tabs/Expenses/ExpensesTab.tsx b/src/pages/AdminPage/tabs/Expenses/ExpensesTab.tsx index 043eb3d..693ec1f 100644 --- a/src/pages/AdminPage/tabs/Expenses/ExpensesTab.tsx +++ b/src/pages/AdminPage/tabs/Expenses/ExpensesTab.tsx @@ -9,6 +9,7 @@ import { Pagination, Flex, rem, Button, Tooltip, ActionIcon } from "@mantine/cor import { useEffect, useState } from "react"; import { IconEdit, IconTrash } from "@tabler/icons-react"; import { MRT_TableOptions } from "mantine-react-table"; +import { Expense } from "./types/Expense.tsx"; export const ExpensesTab = () => { @@ -22,11 +23,12 @@ export const ExpensesTab = () => { const columns = useExpensesTableColumns(); useEffect(() => { - if (!paginationInfo) return; - setTotalPages(paginationInfo.totalPages); + if (!paginationInfo ) return; + if (!paginationInfo.totalPages) setTotalPages(0); + else setTotalPages(paginationInfo.totalPages); }, [paginationInfo]); - const onUpdate = (expense: ExpenseSchemaBase) => { + const onUpdate = (expense: Expense) => { ExpenseService.updateExpense({ requestBody: { expense: { @@ -54,13 +56,14 @@ export const ExpensesTab = () => { }; const onEditClick = (expense: ExpenseSchemaBase) => { + const expenseToEdit = { ...expense, tags: expense.tags.map(tag => tag.name) }; modals.openContextModal({ modal: "expenseFormModal", title: "Редактирование записи о расходах", withCloseButton: false, innerProps: { onChange: event => onUpdate(event), - element: expense, + element: expenseToEdit, }, }); }; @@ -76,6 +79,19 @@ export const ExpensesTab = () => { .catch(err => console.log(err)); }; + const onTagChange = async () => { + await refetch(); + } + + const onTagsEditClick = () => { + modals.openContextModal({ + modal: "expenseTagsModal", + title: "Редактирование тегов", + withCloseButton: false, + innerProps: { onChange: onTagChange }, + }); + } + return ( { enableSorting: false, enableColumnActions: false, renderTopToolbar: ( - + + ), renderRowActions: ({ row }) => ( diff --git a/src/pages/AdminPage/tabs/Expenses/components/CreateExpenseTagForm.tsx b/src/pages/AdminPage/tabs/Expenses/components/CreateExpenseTagForm.tsx new file mode 100644 index 0000000..9331cf6 --- /dev/null +++ b/src/pages/AdminPage/tabs/Expenses/components/CreateExpenseTagForm.tsx @@ -0,0 +1,39 @@ +import { BaseExpenseTagSchema } from "../../../../../client"; +import { Button, Stack, TextInput } from "@mantine/core"; +import { useState } from "react"; +import { notifications } from "../../../../../shared/lib/notifications.ts"; + + +type Props = { + onCreate: (tag: BaseExpenseTagSchema) => void; +} + +const CreateExpenseTagForm = ({ onCreate }: Props) => { + const [expenseTag, setExpenseTag] = useState(""); + + const onCreateClick = () => { + if (expenseTag.length === 0) { + notifications.error({ message: "Нельзя добавить тег без названия" }); + return; + } + onCreate({ name: expenseTag }); + setExpenseTag(""); + }; + + return ( + + setExpenseTag(event.target.value)} + /> + + + ); +}; + +export default CreateExpenseTagForm; \ No newline at end of file diff --git a/src/pages/AdminPage/tabs/Expenses/components/ExpenseTagsInput.tsx b/src/pages/AdminPage/tabs/Expenses/components/ExpenseTagsInput.tsx new file mode 100644 index 0000000..684174d --- /dev/null +++ b/src/pages/AdminPage/tabs/Expenses/components/ExpenseTagsInput.tsx @@ -0,0 +1,19 @@ +import { TagsInput, TagsInputProps } from "@mantine/core"; +import useExpenseTagsList from "../../../hooks/useExpenseTagsList.tsx"; + + +type Props = Omit; + +const ExpenseTagsInput = (props: Props) => { + const { objects } = useExpenseTagsList(); + return ( + object.name)} + {...props} + label={"Теги"} + placeholder={"Выберите теги"} + /> + ); +}; + +export default ExpenseTagsInput; \ No newline at end of file diff --git a/src/pages/AdminPage/tabs/Expenses/components/ExpenseTagsTable.tsx b/src/pages/AdminPage/tabs/Expenses/components/ExpenseTagsTable.tsx new file mode 100644 index 0000000..e92bd38 --- /dev/null +++ b/src/pages/AdminPage/tabs/Expenses/components/ExpenseTagsTable.tsx @@ -0,0 +1,84 @@ +import { CRUDTableProps } from "../../../../../types/CRUDTable.tsx"; +import { BaseExpenseTagSchema, ExpenseTagSchema } from "../../../../../client"; +import { FC, useState } from "react"; +import { ActionIcon, Flex, Stack, Tooltip } from "@mantine/core"; +import { IconCheck, IconEdit, IconTrash } from "@tabler/icons-react"; +import { MRT_TableOptions } from "mantine-react-table"; +import { BaseTable } from "../../../../../components/BaseTable/BaseTable.tsx"; +import { useExpenseTagsTableColumns } from "../hooks/useExpenseTagsTableColumns.tsx"; +import CreateExpenseTagForm from "./CreateExpenseTagForm.tsx"; + + +type Props = CRUDTableProps; + +const ExpenseTagsTable: FC = ({ items, onCreate, onDelete, onChange }) => { + const [editingTagId, setEditingTagId] = useState(-1); + const [tagName, setTagName] = useState(""); + + const columns = useExpenseTagsTableColumns({ editingTagId, setTagName }); + + const onStartEditing = (tag: ExpenseTagSchema) => { + setEditingTagId(tag.id); + setTagName(tag.name); + }; + + const onFinishEditing = (tag: ExpenseTagSchema) => { + if (!onChange) return; + if (tag.name !== tagName) { + onChange({ + id: editingTagId, + name: tagName, + }); + } + + setEditingTagId(-1); + setTagName(""); + }; + + return ( + + onCreate && onCreate(item)} /> + ( + + + { + editingTagId === row.original.id ? ( + onFinishEditing(row.original)} + variant={"default"}> + + + ) : ( + onStartEditing(row.original)} + variant={"default"}> + + + ) + } + + + onDelete && onDelete(row.original)} + variant={"default"}> + + + + + ), + } as MRT_TableOptions + } + /> + + ); +}; + +export default ExpenseTagsTable; \ No newline at end of file diff --git a/src/pages/AdminPage/tabs/Expenses/hooks/useExpenseTagsTableColumns.tsx b/src/pages/AdminPage/tabs/Expenses/hooks/useExpenseTagsTableColumns.tsx new file mode 100644 index 0000000..e8a0f87 --- /dev/null +++ b/src/pages/AdminPage/tabs/Expenses/hooks/useExpenseTagsTableColumns.tsx @@ -0,0 +1,35 @@ +import { useMemo } from "react"; +import { MRT_ColumnDef } from "mantine-react-table"; +import { ExpenseTagSchema } from "../../../../../client"; +import { TextInput } from "@mantine/core"; + + +type Props = { + editingTagId: number; + setTagName: (tagName: string) => void; +} + +export const useExpenseTagsTableColumns = ({ + editingTagId, setTagName, + }: Props) => { + return useMemo[]>( + () => [ + { + accessorKey: "name", + header: "Наименование", + Cell: ({ row }) => { + if (row.original.id === editingTagId) { + return ( + setTagName(event.target.value)} + /> + ); + } + return row.original.name; + }, + }, + ], + [editingTagId], + ); +}; \ No newline at end of file diff --git a/src/pages/AdminPage/tabs/Expenses/hooks/useExpensesTableColumns.tsx b/src/pages/AdminPage/tabs/Expenses/hooks/useExpensesTableColumns.tsx index f1ddae6..48d344f 100644 --- a/src/pages/AdminPage/tabs/Expenses/hooks/useExpensesTableColumns.tsx +++ b/src/pages/AdminPage/tabs/Expenses/hooks/useExpensesTableColumns.tsx @@ -24,6 +24,11 @@ export const useExpensesTableColumns = () => { accessorKey: "amount", header: "Сумма", }, + { + accessorKey: "tags", + header: "Теги", + Cell: ({ row }) => row.original.tags.map(tag => tag.name).join(", "), + }, { accessorKey: "createdByUser", header: "Создал запись", diff --git a/src/pages/AdminPage/tabs/Expenses/modals/ExpenseFormModal.tsx b/src/pages/AdminPage/tabs/Expenses/modals/ExpenseFormModal.tsx index 338473f..f08f3dc 100644 --- a/src/pages/AdminPage/tabs/Expenses/modals/ExpenseFormModal.tsx +++ b/src/pages/AdminPage/tabs/Expenses/modals/ExpenseFormModal.tsx @@ -2,10 +2,11 @@ import { ContextModalProps } from "@mantine/modals"; import { useForm } from "@mantine/form"; import { Flex, NumberInput, rem, TextInput } from "@mantine/core"; import BaseFormModal, { CreateEditFormProps } from "../../../../ClientsPage/modals/BaseFormModal/BaseFormModal.tsx"; -import { ExpenseSchemaBase } from "../../../../../client"; import { DatePickerInput } from "@mantine/dates"; +import ExpenseTagsInput from "../components/ExpenseTagsInput.tsx"; +import { Expense } from "../types/Expense.tsx"; -type Props = CreateEditFormProps; +type Props = CreateEditFormProps; const ExpenseFormModal = ({ context, @@ -13,11 +14,11 @@ const ExpenseFormModal = ({ innerProps, }: ContextModalProps) => { const isEditing = "element" in innerProps; - const initialValue: Partial = isEditing + const initialValue: Partial = isEditing ? innerProps.element : {}; - const form = useForm>({ + const form = useForm>({ initialValues: initialValue, validate: { name: name => !name && "Необходимо указать наименование", @@ -58,6 +59,9 @@ const ExpenseFormModal = ({ placeholder={"Укажите дату расхода"} valueFormat={"DD.MM.YYYY"} /> + diff --git a/src/pages/AdminPage/tabs/Expenses/modals/ExpenseTagsModal.tsx b/src/pages/AdminPage/tabs/Expenses/modals/ExpenseTagsModal.tsx new file mode 100644 index 0000000..c0220e3 --- /dev/null +++ b/src/pages/AdminPage/tabs/Expenses/modals/ExpenseTagsModal.tsx @@ -0,0 +1,65 @@ +import { ContextModalProps } from "@mantine/modals"; +import { BaseExpenseTagSchema, ExpenseService, ExpenseTagSchema } from "../../../../../client"; +import useExpenseTagsList from "../../../hooks/useExpenseTagsList.tsx"; +import ExpenseTagsTable from "../components/ExpenseTagsTable.tsx"; +import { useCRUD } from "../../../../../hooks/useCRUD.tsx"; +import { notifications } from "../../../../../shared/lib/notifications.ts"; + +type Props = { + onChange: () => Promise; +}; + +const ExpenseTagsModal = ({ + innerProps, + }: ContextModalProps) => { + const { onChange: onTagChange } = innerProps; + const { objects: expenseTags, refetch } = useExpenseTagsList(); + + const crud = useCRUD({ + onChange: tag => { + ExpenseService.updateExpenseTag({ + requestBody: { + tag, + }, + }) + .then(async ({ ok, message }) => { + notifications.guess(ok, { message }); + if (!ok) return; + await refetch(); + await onTagChange(); + }) + .catch(err => console.log(err)); + }, + onCreate: tag => { + ExpenseService.createExpenseTag({ + requestBody: { + tag, + }, + }) + .then(async ({ ok, message }) => { + notifications.guess(ok, { message }); + if (!ok) return; + await refetch(); + await onTagChange(); + }) + .catch(err => console.log(err)); + }, + onDelete: (tag) => { + ExpenseService.deleteExpenseTag({ + tagId: tag.id, + }) + .then(async ({ ok, message }) => { + notifications.guess(ok, { message }); + if (!ok) return; + await refetch(); + }) + .catch(err => console.log(err)); + }, + }); + + return ( + + ); +}; + +export default ExpenseTagsModal; diff --git a/src/pages/AdminPage/tabs/Expenses/types/Expense.tsx b/src/pages/AdminPage/tabs/Expenses/types/Expense.tsx new file mode 100644 index 0000000..91a37dc --- /dev/null +++ b/src/pages/AdminPage/tabs/Expenses/types/Expense.tsx @@ -0,0 +1,11 @@ +import type { UserSchema } from "../../../../../client"; + +export type Expense = { + id: number; + name: string; + comment: string; + amount: number; + createdByUser: UserSchema; + spentDate: string; + tags: Array; +}; diff --git a/src/pages/StatisticsPage/components/ExpenseTagSelect/ExpenseTagSelect.tsx b/src/pages/StatisticsPage/components/ExpenseTagSelect/ExpenseTagSelect.tsx new file mode 100644 index 0000000..94d131a --- /dev/null +++ b/src/pages/StatisticsPage/components/ExpenseTagSelect/ExpenseTagSelect.tsx @@ -0,0 +1,23 @@ +import { FC } from "react"; +import useExpenseTagsList from "../../../AdminPage/hooks/useExpenseTagsList.tsx"; +import { ExpenseTagSchema } from "../../../../client"; +import ObjectSelect, { ObjectSelectProps } from "../../../../components/ObjectSelect/ObjectSelect.tsx"; + +type Props = Omit< + ObjectSelectProps, + "data" | "getValueFn" | "getLabelFn" +>; +const ExpenseTagSelect: FC = props => { + const { objects: tags } = useExpenseTagsList(); + return ( + tag.name} + getValueFn={(tag: ExpenseTagSchema) => tag.id.toString()} + clearable + {...props} + onClear={() => props.onChange(null)} + /> + ); +}; +export default ExpenseTagSelect; diff --git a/src/pages/StatisticsPage/tabs/ProfitTab/ProfitTab.tsx b/src/pages/StatisticsPage/tabs/ProfitTab/ProfitTab.tsx index 286a583..22fbb17 100644 --- a/src/pages/StatisticsPage/tabs/ProfitTab/ProfitTab.tsx +++ b/src/pages/StatisticsPage/tabs/ProfitTab/ProfitTab.tsx @@ -1,12 +1,15 @@ import { ProfitChart } from "./components/ProfitChart/ProfitChart.tsx"; import styles from "../../ui/StatisticsPage.module.css"; import { ProfitTable } from "./components/ProfitTable/ProfitTable.tsx"; +import { ProfitTabContextProvider } from "./contexts/ProfitTabContext.tsx"; export const ProfitTab = () => { return ( -
- - -
+ +
+ + +
+
); }; \ No newline at end of file diff --git a/src/pages/StatisticsPage/tabs/ProfitTab/components/Filters/Filters.tsx b/src/pages/StatisticsPage/tabs/ProfitTab/components/Filters/Filters.tsx index c7aa772..54ead0a 100644 --- a/src/pages/StatisticsPage/tabs/ProfitTab/components/Filters/Filters.tsx +++ b/src/pages/StatisticsPage/tabs/ProfitTab/components/Filters/Filters.tsx @@ -1,7 +1,7 @@ import { DatePickerInput, DatePickerInputProps } from "@mantine/dates"; -import { Stack, Text } from "@mantine/core"; +import { Divider, Stack, Text } from "@mantine/core"; import ClientSelectNew from "../../../../../../components/Selects/ClientSelectNew/ClientSelectNew.tsx"; -import { BaseMarketplaceSchema, ClientSchema, UserSchema } from "../../../../../../client"; +import { BaseMarketplaceSchema, ClientSchema, ExpenseTagSchema, UserSchema } from "../../../../../../client"; import { ObjectSelectProps } from "../../../../../../components/ObjectSelect/ObjectSelect.tsx"; import BaseMarketplaceSelect from "../../../../../../components/Selects/BaseMarketplaceSelect/BaseMarketplaceSelect.tsx"; @@ -9,6 +9,7 @@ import DealStatusSelect from "../../../../../DealsPage/components/DealStatusSele import { DealStatusType } from "../../../../../../shared/enums/DealStatus.ts"; import { ProfitTableSegmentedControl } from "../ProfitTableSegmentedControl/ProfitTableSegmentedControl.tsx"; import ManagerSelect from "../../../../../../components/ManagerSelect/ManagerSelect.tsx"; +import ExpenseTagSelect from "../../../../components/ExpenseTagSelect/ExpenseTagSelect.tsx"; type FiltersProps = { @@ -32,6 +33,12 @@ type FiltersProps = { >; onManagerClear?: () => void; + tagSelectProps?: Omit< + ObjectSelectProps, + "data" | "getValueFn" | "getLabelFn" + >; + onTagClear?: () => void; + groupTableByProps?: { value: string, onChange: (value: string) => void, @@ -49,11 +56,17 @@ export const Filters = (props: FiltersProps) => { onDealStatusClear, managerSelectProps, onManagerClear, + tagSelectProps, + onTagClear, groupTableByProps, } = props; return ( + + + Фильтры для выручки и прибыли: + {datePickerProps && { placeholder={"Выберите менеджера"} /> } + {tagSelectProps && + <> + + Фильтры для расходов: + + + } {groupTableByProps && <> - Группировать: + + Группировка таблицы: { const { - profitData, - form, - isLoading, - } = useProfitChart(); + profitChartData: profitData, + isChartLoading: isLoading, + } = useProfitTabContext(); const formattedProfitData = profitData.map( value => { @@ -35,17 +33,14 @@ export const ProfitChart = () => { const units = ["₽", "шт"]; - const chartSizes = ["42vh", "28vh"]; + const chartSizes = ["47vh", "28vh"]; return (
- - + {getChartsSeries.map((series, idx) => { return ( { - const form = useForm({ - mode: "controlled", - initialValues: { - dateRange: getDefaultDates(), - client: null, - marketplace: null, - dealStatus: defaultDealStatus, - manager: null, - }, - }); - const [profitData, setProfitData] = useState([]); - const [isLoading, setIsLoading] = useState(false); - - const getFilters = () => { - const dateRange = form.values.dateRange; - - return { - dateRange: [ - dateToString(dateRange[0]), - dateToString(dateRange[1]), - ], - clientId: form.values.client?.id ?? -1, - baseMarketplaceKey: form.values.marketplace?.key ?? "all", - dealStatusId: form.values.dealStatus?.id ?? -1, - managerId: form.values.manager?.id ?? -1, - }; - }; - - const fetchProfitData = () => { - setIsLoading(true); - StatisticsService.getProfitChartData({ - requestBody: getFilters(), - }) - .then(res => { - setProfitData(res.data); - }) - .catch(err => console.log(err)) - .finally(() => setIsLoading(false)); - }; - - useEffect(() => { - if (form.values.dateRange.length < 2 || form.values.dateRange[1] === null) { - return; - } - fetchProfitData(); - }, [form.values]); - - return { - profitData, - form, - isLoading, - }; -}; \ No newline at end of file diff --git a/src/pages/StatisticsPage/tabs/ProfitTab/components/ProfitTable/ProfitTable.tsx b/src/pages/StatisticsPage/tabs/ProfitTab/components/ProfitTable/ProfitTable.tsx index 61a1f1f..6d61a34 100644 --- a/src/pages/StatisticsPage/tabs/ProfitTab/components/ProfitTable/ProfitTable.tsx +++ b/src/pages/StatisticsPage/tabs/ProfitTab/components/ProfitTable/ProfitTable.tsx @@ -2,7 +2,7 @@ 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"; +import { ProfitFiltersModal } from "../../modals/ProfitFiltersModal.tsx"; export const ProfitTable = () => { @@ -10,7 +10,7 @@ export const ProfitTable = () => { return ( - + diff --git a/src/pages/StatisticsPage/tabs/ProfitTab/components/ProfitTable/hooks/useProfitMantineTable.tsx b/src/pages/StatisticsPage/tabs/ProfitTab/components/ProfitTable/hooks/useProfitMantineTable.tsx deleted file mode 100644 index 2abed81..0000000 --- a/src/pages/StatisticsPage/tabs/ProfitTab/components/ProfitTable/hooks/useProfitMantineTable.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import { GroupStatisticsTable } from "../../ProfitTableSegmentedControl/ProfitTableSegmentedControl.tsx"; -import { useProfitTableColumns } from "./columns.tsx"; -import { useMantineReactTable } from "mantine-react-table"; -import { ProfitTableDataItem } from "../../../../../../../client"; -import { MRT_Localization_RU } from "mantine-react-table/locales/ru/index.cjs"; - - -type Props = { - groupTableBy: GroupStatisticsTable; - profitData: ProfitTableDataItem[]; -} - -export const useProfitMantineTable = ({ groupTableBy, profitData }: Props) => { - const columns = useProfitTableColumns({ - groupTableBy, - }); - - const defaultSorting = [{ id: "groupedValue", desc: true }]; - - const table = useMantineReactTable({ - enablePagination: false, - data: profitData, - columns, - enableTopToolbar: false, - enableBottomToolbar: false, - enableSorting: true, - initialState: { - sorting: defaultSorting, - }, - localization: MRT_Localization_RU, - enableRowVirtualization: true, - mantineTableContainerProps: { style: { maxHeight: "86vh" } }, - }); - - return { table }; -}; \ No newline at end of file diff --git a/src/pages/StatisticsPage/tabs/ProfitTab/components/ProfitTable/hooks/useProfitTable.tsx b/src/pages/StatisticsPage/tabs/ProfitTab/components/ProfitTable/hooks/useProfitTable.tsx index 50c1fab..70696b4 100644 --- a/src/pages/StatisticsPage/tabs/ProfitTab/components/ProfitTable/hooks/useProfitTable.tsx +++ b/src/pages/StatisticsPage/tabs/ProfitTab/components/ProfitTable/hooks/useProfitTable.tsx @@ -1,69 +1,36 @@ -import { useForm } from "@mantine/form"; -import { TableFormFilters } from "../../../../../types/TableFormFilters.ts"; -import { 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"; -import { defaultDealStatus } from "../../../../../utils/defaultFilterValues.ts"; -import { dateToString } from "../../../../../../../types/utils.ts"; +import { useProfitTabContext } from "../../../contexts/ProfitTabContext.tsx"; +import { useProfitTableColumns } from "./columns.tsx"; +import { useMantineReactTable } from "mantine-react-table"; +import { MRT_Localization_RU } from "mantine-react-table/locales/ru/index.cjs"; export const useProfitTable = () => { - const form = useForm({ - mode: "controlled", - initialValues: { - dateRange: getDefaultDates(), - groupTableBy: GroupStatisticsTable.BY_DATES, - client: null, - marketplace: null, - dealStatus: defaultDealStatus, - manager: null, - }, - }); - const [isLoading, setIsLoading] = useState(false); + const { + form, + isTableLoading: isLoading, + profitTableData + } = useProfitTabContext(); - const [profitData, setProfitData] = useState([]); - - const { table } = useProfitMantineTable({ + const columns = useProfitTableColumns({ groupTableBy: form.values.groupTableBy, - profitData, }); - const getFilters = () => { - const dateRange = form.values.dateRange; + const defaultSorting = [{ id: "groupedValue", desc: true }]; - return { - dateRange: [ - dateToString(dateRange[0]), - dateToString(dateRange[1]), - ], - groupTableBy: form.values.groupTableBy, - clientId: form.values.client?.id ?? -1, - baseMarketplaceKey: form.values.marketplace?.key ?? "all", - dealStatusId: form.values.dealStatus?.id ?? -1, - managerId: form.values.manager?.id ?? -1, - }; - }; - - 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]); + const table = useMantineReactTable({ + enablePagination: false, + data: profitTableData, + columns, + enableTopToolbar: false, + enableBottomToolbar: false, + enableSorting: true, + initialState: { + sorting: defaultSorting, + }, + localization: MRT_Localization_RU, + enableRowVirtualization: true, + mantineTableContainerProps: { style: { maxHeight: "86vh" } }, + }); return { table, diff --git a/src/pages/StatisticsPage/tabs/ProfitTab/contexts/ProfitTabContext.tsx b/src/pages/StatisticsPage/tabs/ProfitTab/contexts/ProfitTabContext.tsx new file mode 100644 index 0000000..0db662d --- /dev/null +++ b/src/pages/StatisticsPage/tabs/ProfitTab/contexts/ProfitTabContext.tsx @@ -0,0 +1,125 @@ +import { createContext, FC, useContext, useEffect, useState } from "react"; +import { ProfitChartDataItem, ProfitTableDataItem, StatisticsService } from "../../../../../client"; +import { useForm, UseFormReturnType } from "@mantine/form"; +import { FormFilters } from "../../../types/FormFilters.ts"; +import { getDefaultDates } from "../../../utils/dates.ts"; +import { GroupStatisticsTable } from "../components/ProfitTableSegmentedControl/ProfitTableSegmentedControl.tsx"; +import { defaultDealStatus } from "../../../utils/defaultFilterValues.ts"; +import { dateToString } from "../../../../../types/utils.ts"; + +type ProfitTabContextState = { + form: UseFormReturnType FormFilters>; + isChartLoading: boolean; + isTableLoading: boolean; + profitChartData: ProfitChartDataItem[]; + profitTableData: ProfitTableDataItem[]; +}; + +const ProfitTabContext = createContext( + undefined, +); + +const useProfitTabContextState = () => { + const form = useForm({ + mode: "controlled", + initialValues: { + dateRange: getDefaultDates(), + groupTableBy: GroupStatisticsTable.BY_DATES, + client: null, + marketplace: null, + dealStatus: defaultDealStatus, + manager: null, + tag: null, + }, + }); + const [isChartLoading, setIsChartLoading] = useState(false); + const [isTableLoading, setIsTableLoading] = useState(false); + const [profitChartData, setProfitChartData] = useState([]); + const [profitTableData, setProfitTableData] = useState([]); + + const getChartFilters = () => { + 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, + managerId: form.values.manager?.id ?? -1, + tagId: form.values.tag?.id ?? -1, + }; + }; + + const fetchChartProfitData = () => { + setIsChartLoading(true); + StatisticsService.getProfitChartData({ + requestBody: getChartFilters(), + }) + .then(res => { + setProfitChartData(res.data); + }) + .catch(err => console.log(err)) + .finally(() => setIsChartLoading(false)); + }; + + const getTableFilters = () => { + return { + ...getChartFilters(), + groupTableBy: form.values.groupTableBy, + }; + }; + + const fetchTableProfitData = () => { + setIsTableLoading(true); + StatisticsService.getProfitTableData({ + requestBody: getTableFilters(), + }) + .then(res => { + setProfitTableData(res.data); + }) + .catch(err => console.log(err)) + .finally(() => setIsTableLoading(false)); + }; + + useEffect(() => { + if (form.values.dateRange.length < 2 || form.values.dateRange[1] === null) { + return; + } + fetchChartProfitData(); + fetchTableProfitData(); + }, [form.values]); + + return { + form, + isChartLoading, + isTableLoading, + profitChartData, + profitTableData, + }; +}; + +type ProfitTabContextProviderProps = { + children: React.ReactNode; +}; + +export const ProfitTabContextProvider: FC = ({ children }) => { + const state = useProfitTabContextState(); + return ( + + {children} + + ); +}; + +export const useProfitTabContext = () => { + const context = useContext(ProfitTabContext); + if (!context) { + throw new Error( + "useProfitTabContext must be used within a ProfitTabContextProvider", + ); + } + return context; +}; diff --git a/src/pages/StatisticsPage/tabs/ProfitTab/modals/ProfitChartFiltersModal.tsx b/src/pages/StatisticsPage/tabs/ProfitTab/modals/ProfitChartFiltersModal.tsx deleted file mode 100644 index 46983c0..0000000 --- a/src/pages/StatisticsPage/tabs/ProfitTab/modals/ProfitChartFiltersModal.tsx +++ /dev/null @@ -1,47 +0,0 @@ -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; -} - -export const ProfitChartFiltersModal = ({ form }: Props) => { - const [opened, { open, close }] = useDisclosure(); - - return ( - <> - - - form.setFieldValue("client", null)} - baseMarketplaceSelectProps={form.getInputProps("marketplace")} - onBaseMarketplaceClear={() => form.setFieldValue("marketplace", null)} - dealStatusSelectProps={form.getInputProps("dealStatus")} - onDealStatusClear={() => form.setFieldValue("dealStatus", null)} - managerSelectProps={form.getInputProps("manager")} - onManagerClear={() => form.setFieldValue("manager", null)} - /> - - - ); -}; \ No newline at end of file diff --git a/src/pages/StatisticsPage/tabs/ProfitTab/modals/ProfitTableFiltersModal.tsx b/src/pages/StatisticsPage/tabs/ProfitTab/modals/ProfitFiltersModal.tsx similarity index 82% rename from src/pages/StatisticsPage/tabs/ProfitTab/modals/ProfitTableFiltersModal.tsx rename to src/pages/StatisticsPage/tabs/ProfitTab/modals/ProfitFiltersModal.tsx index 89532b8..d1e9afa 100644 --- a/src/pages/StatisticsPage/tabs/ProfitTab/modals/ProfitTableFiltersModal.tsx +++ b/src/pages/StatisticsPage/tabs/ProfitTab/modals/ProfitFiltersModal.tsx @@ -1,16 +1,16 @@ 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 { FormFilters } from "../../../types/FormFilters.ts"; import { UseFormReturnType } from "@mantine/form"; import { useDisclosure } from "@mantine/hooks"; type Props = { - form: UseFormReturnType; + form: UseFormReturnType; } -export const ProfitTableFiltersModal = ({ form }: Props) => { +export const ProfitFiltersModal = ({ form }: Props) => { const [opened, { open, close }] = useDisclosure(); return ( @@ -22,13 +22,13 @@ export const ProfitTableFiltersModal = ({ form }: Props) => { > - Фильтры таблицы + Фильтры { onDealStatusClear={() => form.setFieldValue("dealStatus", null)} managerSelectProps={form.getInputProps("manager")} onManagerClear={() => form.setFieldValue("manager", null)} + tagSelectProps={form.getInputProps("tag")} + onTagClear={() => form.setFieldValue("tag", null)} /> diff --git a/src/pages/StatisticsPage/types/TableFormFilters.ts b/src/pages/StatisticsPage/types/FormFilters.ts similarity index 72% rename from src/pages/StatisticsPage/types/TableFormFilters.ts rename to src/pages/StatisticsPage/types/FormFilters.ts index 9cd4553..4fb7ac6 100644 --- a/src/pages/StatisticsPage/types/TableFormFilters.ts +++ b/src/pages/StatisticsPage/types/FormFilters.ts @@ -1,14 +1,15 @@ import { GroupStatisticsTable, } from "../tabs/ProfitTab/components/ProfitTableSegmentedControl/ProfitTableSegmentedControl.tsx"; -import { BaseMarketplaceSchema, ClientSchema, UserSchema } from "../../../client"; +import { BaseMarketplaceSchema, ClientSchema, ExpenseTagSchema, UserSchema } from "../../../client"; import { DealStatusType } from "../../../shared/enums/DealStatus.ts"; -export interface TableFormFilters { +export interface FormFilters { dateRange: [Date | null, Date | null]; groupTableBy: GroupStatisticsTable; client: ClientSchema | null; marketplace: BaseMarketplaceSchema | null; dealStatus: DealStatusType | null; manager: UserSchema | null; + tag: ExpenseTagSchema | null; } \ No newline at end of file diff --git a/src/types/CRUDTable.tsx b/src/types/CRUDTable.tsx index f9d7745..65ce634 100644 --- a/src/types/CRUDTable.tsx +++ b/src/types/CRUDTable.tsx @@ -2,9 +2,9 @@ import { RefObject } from "react"; import { BaseTableRef } from "../components/BaseTable/BaseTable.tsx"; import { MRT_RowData } from "mantine-react-table"; -export interface CRUDTableProps { +export interface CRUDTableProps { items: T[]; - onCreate?: (item: T) => void; + onCreate?: (item: C | T) => void; onDelete?: (item: T) => void; onChange?: (item: T) => void; onSelectionChange?: (selectedItems: T[]) => void;