feat: deals filters indicator and refactoring

This commit is contained in:
2025-09-01 18:50:29 +04:00
parent 48d539154c
commit a6d8948e9d
6 changed files with 81 additions and 69 deletions

View File

@ -6,7 +6,7 @@ import { useDealsContext } from "@/app/deals/contexts/DealsContext";
import BaseTable from "@/components/ui/BaseTable/BaseTable";
const DealsTable: FC = () => {
const { deals, paginationInfo, page, setPage, dealsFilters } =
const { deals, paginationInfo, page, setPage, dealsFiltersForm } =
useDealsContext();
const columns = useDealsTableColumns();
@ -18,12 +18,18 @@ const DealsTable: FC = () => {
records={[...deals]}
columns={columns}
sortStatus={{
columnAccessor: dealsFilters.sortingField,
direction: dealsFilters.sortingDirection,
columnAccessor: dealsFiltersForm.values.sortingField ?? "",
direction: dealsFiltersForm.values.sortingDirection,
}}
onSortStatusChange={sorting => {
dealsFilters.setSortingField(sorting.columnAccessor);
dealsFilters.setSortingDirection(sorting.direction);
dealsFiltersForm.setFieldValue(
"sortingField",
sorting.columnAccessor
);
dealsFiltersForm.setFieldValue(
"sortingDirection",
sorting.direction
);
}}
emptyState={
<Group

View File

@ -1,7 +1,7 @@
"use client";
import { IconEdit, IconFilter, IconPlus } from "@tabler/icons-react";
import { Flex, Group } from "@mantine/core";
import { Flex, Group, Indicator } from "@mantine/core";
import { modals } from "@mantine/modals";
import ToolPanelAction from "@/app/deals/components/desktop/ToolPanelAction/ToolPanelAction";
import ViewSelector from "@/app/deals/components/desktop/ViewSelector/ViewSelector";
@ -14,7 +14,7 @@ import { useDrawersContext } from "@/drawers/DrawersContext";
import useIsMobile from "@/hooks/utils/useIsMobile";
const TopToolPanel = () => {
const { dealsFilters } = useDealsContext();
const { dealsFiltersForm, isChangedFilters } = useDealsContext();
const { view } = useViewContext();
const { projects, setSelectedProjectId, selectedProject, projectsCrud } =
useProjectsContext();
@ -52,11 +52,16 @@ const TopToolPanel = () => {
gap={"sm"}>
<DealsTableFiltersModal
getOpener={onFiltersClick => (
<Indicator
disabled={!isChangedFilters}
offset={3}
size={8}>
<ToolPanelAction onClick={onFiltersClick}>
<IconFilter />
</ToolPanelAction>
</Indicator>
)}
filters={dealsFilters}
filtersForm={dealsFiltersForm}
selectedProject={selectedProject}
boardAndStatusEnabled={view === "table"}
/>

View File

@ -1,8 +1,9 @@
"use client";
import React from "react";
import { UseFormReturnType } from "@mantine/form";
import { useStatusesContext } from "@/app/deals/contexts/StatusesContext";
import { DealsFilters } from "@/app/deals/hooks/useDealsFilters";
import { DealsFiltersForm } from "@/app/deals/hooks/useDealsFilters";
import { DealsCrud, useDealsCrud } from "@/hooks/cruds/useDealsCrud";
import useDealsList from "@/hooks/lists/useDealsList";
import { DealSchema, PaginationInfoSchema } from "@/lib/client";
@ -16,7 +17,8 @@ type DealsContextState = {
paginationInfo?: PaginationInfoSchema;
page: number;
setPage: React.Dispatch<React.SetStateAction<number>>;
dealsFilters: DealsFilters;
dealsFiltersForm: UseFormReturnType<DealsFiltersForm>;
isChangedFilters: boolean;
};
type Props = {

View File

@ -1,52 +1,48 @@
import { Dispatch, SetStateAction, useState } from "react";
import { isEqual } from "lodash";
import { useForm, UseFormReturnType } from "@mantine/form";
import { useDebouncedValue } from "@mantine/hooks";
import { BoardSchema, SortDir, StatusSchema } from "@/lib/client";
export type DealsFilters = {
export type DealsFiltersForm = {
id?: number;
debouncedId?: number;
setId: Dispatch<SetStateAction<number | undefined>>;
name?: string;
debouncedName?: string;
setName: Dispatch<SetStateAction<string | undefined>>;
name: string;
board: BoardSchema | null;
setBoard: Dispatch<SetStateAction<BoardSchema | null>>;
status: StatusSchema | null;
setStatus: Dispatch<SetStateAction<StatusSchema | null>>;
sortingField: string;
setSortingField: Dispatch<SetStateAction<string>>;
sortingField?: string;
sortingDirection: SortDir;
setSortingDirection: Dispatch<SetStateAction<SortDir>>;
};
const useDealsFilters = (): DealsFilters => {
const [id, setId] = useState<number>();
const [debouncedId] = useDebouncedValue(id, 300);
type ReturnType = {
debouncedId: number | undefined;
debouncedName: string;
form: UseFormReturnType<DealsFiltersForm>;
isChangedFilters: boolean;
};
const [name, setName] = useState<string>();
const [debouncedName] = useDebouncedValue(name, 300);
const useDealsFilters = (): ReturnType => {
const initialValues = {
id: undefined,
board: null,
status: null,
name: "",
sortingField: "createdAt",
sortingDirection: "asc" as SortDir,
};
const [board, setBoard] = useState<BoardSchema | null>(null);
const [status, setStatus] = useState<StatusSchema | null>(null);
const form = useForm<DealsFiltersForm>({
initialValues,
});
const [sortingField, setSortingField] = useState("createdAt");
const [sortingDirection, setSortingDirection] = useState<SortDir>("asc");
const isChangedFilters = !isEqual(form.values, initialValues);
const [debouncedId] = useDebouncedValue(form.values.id, 300);
const [debouncedName] = useDebouncedValue(form.values.name, 300);
return {
id,
setId,
debouncedId,
name,
setName,
debouncedName,
board,
setBoard,
status,
setStatus,
sortingField,
setSortingField,
sortingDirection,
setSortingDirection,
form,
isChangedFilters,
};
};

View File

@ -2,21 +2,22 @@
import { ReactNode } from "react";
import { Flex, Modal, NumberInput, rem, TextInput } from "@mantine/core";
import { UseFormReturnType } from "@mantine/form";
import { useDisclosure } from "@mantine/hooks";
import { DealsFilters } from "@/app/deals/hooks/useDealsFilters";
import { DealsFiltersForm } from "@/app/deals/hooks/useDealsFilters";
import BoardSelect from "@/components/selects/BoardSelect/BoardSelect";
import StatusSelect from "@/components/selects/StatusSelect/StatusSelect";
import { ProjectSchema } from "@/lib/client";
type Props = {
filters: DealsFilters;
filtersForm: UseFormReturnType<DealsFiltersForm>;
selectedProject: ProjectSchema | null;
boardAndStatusEnabled: boolean;
getOpener: (open: () => void) => ReactNode;
};
const DealsTableFiltersModal = ({
filters,
filtersForm,
selectedProject,
boardAndStatusEnabled,
getOpener,
@ -36,34 +37,32 @@ const DealsTableFiltersModal = ({
<NumberInput
label={"ID"}
placeholder={"Введите ID"}
value={filters.id}
{...filtersForm.getInputProps("id")}
value={filtersForm.values.id}
onChange={value =>
typeof value === "number"
? filters.setId(Number(value))
: filters.setId(undefined)
? filtersForm.setFieldValue("id", Number(value))
: filtersForm.setFieldValue("id", undefined)
}
min={1}
/>
<TextInput
label={"Название"}
placeholder={"Введите название"}
defaultValue={filters.name}
onChange={event => filters.setName(event.target.value)}
{...filtersForm.getInputProps("name")}
/>
{boardAndStatusEnabled && (
<>
<BoardSelect
label={"Доска"}
value={filters.board}
onChange={filters.setBoard}
{...filtersForm.getInputProps("board")}
projectId={selectedProject?.id}
clearable
/>
<StatusSelect
label={"Статус"}
value={filters.status}
onChange={filters.setStatus}
boardId={filters.board?.id}
{...filtersForm.getInputProps("status")}
boardId={filtersForm.values.board?.id}
clearable
/>
</>

View File

@ -1,7 +1,8 @@
import { Dispatch, SetStateAction, useState } from "react";
import { useQuery, useQueryClient } from "@tanstack/react-query";
import { UseFormReturnType } from "@mantine/form";
import useDealsFilters, {
DealsFilters,
DealsFiltersForm,
} from "@/app/deals/hooks/useDealsFilters";
import { DealSchema, GetDealsData, PaginationInfoSchema } from "@/lib/client";
import {
@ -18,7 +19,8 @@ type Props = {
type ReturnType = {
deals: DealSchema[];
setDeals: (deals: DealSchema[]) => void;
dealsFilters: DealsFilters;
dealsFiltersForm: UseFormReturnType<DealsFiltersForm>;
isChangedFilters: boolean;
refetchDeals: () => void;
page: number;
setPage: Dispatch<SetStateAction<number>>;
@ -34,21 +36,22 @@ const useDealsList = ({
const queryClient = useQueryClient();
const [page, setPage] = useState(1);
const itemsPerPage = 10;
const dealsFilters = useDealsFilters();
const { form, debouncedName, debouncedId, isChangedFilters } =
useDealsFilters();
const options: Omit<GetDealsData, "url"> = {
query: {
page: withPagination ? page : null,
itemsPerPage: withPagination ? itemsPerPage : null,
sortingField: withPagination ? dealsFilters.sortingField : null,
sortingField: withPagination ? form.values.sortingField : null,
sortingDirection: withPagination
? dealsFilters.sortingDirection
? form.values.sortingDirection
: null,
projectId: withPagination ? projectId : null,
boardId: withPagination ? dealsFilters.board?.id : boardId,
statusId: withPagination ? dealsFilters.status?.id : null,
name: dealsFilters.debouncedName,
id: dealsFilters.debouncedId,
boardId: withPagination ? form.values.board?.id : boardId,
statusId: withPagination ? form.values.status?.id : null,
name: debouncedName,
id: debouncedId,
},
};
@ -68,7 +71,8 @@ const useDealsList = ({
return {
deals: data?.items ?? [],
setDeals,
dealsFilters,
dealsFiltersForm: form,
isChangedFilters,
refetchDeals: refetch,
page,
setPage,