feat: deals filters indicator and refactoring
This commit is contained in:
@ -6,7 +6,7 @@ import { useDealsContext } from "@/app/deals/contexts/DealsContext";
|
|||||||
import BaseTable from "@/components/ui/BaseTable/BaseTable";
|
import BaseTable from "@/components/ui/BaseTable/BaseTable";
|
||||||
|
|
||||||
const DealsTable: FC = () => {
|
const DealsTable: FC = () => {
|
||||||
const { deals, paginationInfo, page, setPage, dealsFilters } =
|
const { deals, paginationInfo, page, setPage, dealsFiltersForm } =
|
||||||
useDealsContext();
|
useDealsContext();
|
||||||
const columns = useDealsTableColumns();
|
const columns = useDealsTableColumns();
|
||||||
|
|
||||||
@ -18,12 +18,18 @@ const DealsTable: FC = () => {
|
|||||||
records={[...deals]}
|
records={[...deals]}
|
||||||
columns={columns}
|
columns={columns}
|
||||||
sortStatus={{
|
sortStatus={{
|
||||||
columnAccessor: dealsFilters.sortingField,
|
columnAccessor: dealsFiltersForm.values.sortingField ?? "",
|
||||||
direction: dealsFilters.sortingDirection,
|
direction: dealsFiltersForm.values.sortingDirection,
|
||||||
}}
|
}}
|
||||||
onSortStatusChange={sorting => {
|
onSortStatusChange={sorting => {
|
||||||
dealsFilters.setSortingField(sorting.columnAccessor);
|
dealsFiltersForm.setFieldValue(
|
||||||
dealsFilters.setSortingDirection(sorting.direction);
|
"sortingField",
|
||||||
|
sorting.columnAccessor
|
||||||
|
);
|
||||||
|
dealsFiltersForm.setFieldValue(
|
||||||
|
"sortingDirection",
|
||||||
|
sorting.direction
|
||||||
|
);
|
||||||
}}
|
}}
|
||||||
emptyState={
|
emptyState={
|
||||||
<Group
|
<Group
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { IconEdit, IconFilter, IconPlus } from "@tabler/icons-react";
|
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 { modals } from "@mantine/modals";
|
||||||
import ToolPanelAction from "@/app/deals/components/desktop/ToolPanelAction/ToolPanelAction";
|
import ToolPanelAction from "@/app/deals/components/desktop/ToolPanelAction/ToolPanelAction";
|
||||||
import ViewSelector from "@/app/deals/components/desktop/ViewSelector/ViewSelector";
|
import ViewSelector from "@/app/deals/components/desktop/ViewSelector/ViewSelector";
|
||||||
@ -14,7 +14,7 @@ import { useDrawersContext } from "@/drawers/DrawersContext";
|
|||||||
import useIsMobile from "@/hooks/utils/useIsMobile";
|
import useIsMobile from "@/hooks/utils/useIsMobile";
|
||||||
|
|
||||||
const TopToolPanel = () => {
|
const TopToolPanel = () => {
|
||||||
const { dealsFilters } = useDealsContext();
|
const { dealsFiltersForm, isChangedFilters } = useDealsContext();
|
||||||
const { view } = useViewContext();
|
const { view } = useViewContext();
|
||||||
const { projects, setSelectedProjectId, selectedProject, projectsCrud } =
|
const { projects, setSelectedProjectId, selectedProject, projectsCrud } =
|
||||||
useProjectsContext();
|
useProjectsContext();
|
||||||
@ -52,11 +52,16 @@ const TopToolPanel = () => {
|
|||||||
gap={"sm"}>
|
gap={"sm"}>
|
||||||
<DealsTableFiltersModal
|
<DealsTableFiltersModal
|
||||||
getOpener={onFiltersClick => (
|
getOpener={onFiltersClick => (
|
||||||
|
<Indicator
|
||||||
|
disabled={!isChangedFilters}
|
||||||
|
offset={3}
|
||||||
|
size={8}>
|
||||||
<ToolPanelAction onClick={onFiltersClick}>
|
<ToolPanelAction onClick={onFiltersClick}>
|
||||||
<IconFilter />
|
<IconFilter />
|
||||||
</ToolPanelAction>
|
</ToolPanelAction>
|
||||||
|
</Indicator>
|
||||||
)}
|
)}
|
||||||
filters={dealsFilters}
|
filtersForm={dealsFiltersForm}
|
||||||
selectedProject={selectedProject}
|
selectedProject={selectedProject}
|
||||||
boardAndStatusEnabled={view === "table"}
|
boardAndStatusEnabled={view === "table"}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@ -1,8 +1,9 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
import { UseFormReturnType } from "@mantine/form";
|
||||||
import { useStatusesContext } from "@/app/deals/contexts/StatusesContext";
|
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 { DealsCrud, useDealsCrud } from "@/hooks/cruds/useDealsCrud";
|
||||||
import useDealsList from "@/hooks/lists/useDealsList";
|
import useDealsList from "@/hooks/lists/useDealsList";
|
||||||
import { DealSchema, PaginationInfoSchema } from "@/lib/client";
|
import { DealSchema, PaginationInfoSchema } from "@/lib/client";
|
||||||
@ -16,7 +17,8 @@ type DealsContextState = {
|
|||||||
paginationInfo?: PaginationInfoSchema;
|
paginationInfo?: PaginationInfoSchema;
|
||||||
page: number;
|
page: number;
|
||||||
setPage: React.Dispatch<React.SetStateAction<number>>;
|
setPage: React.Dispatch<React.SetStateAction<number>>;
|
||||||
dealsFilters: DealsFilters;
|
dealsFiltersForm: UseFormReturnType<DealsFiltersForm>;
|
||||||
|
isChangedFilters: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
|
|||||||
@ -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 { useDebouncedValue } from "@mantine/hooks";
|
||||||
import { BoardSchema, SortDir, StatusSchema } from "@/lib/client";
|
import { BoardSchema, SortDir, StatusSchema } from "@/lib/client";
|
||||||
|
|
||||||
export type DealsFilters = {
|
export type DealsFiltersForm = {
|
||||||
id?: number;
|
id?: number;
|
||||||
debouncedId?: number;
|
name: string;
|
||||||
setId: Dispatch<SetStateAction<number | undefined>>;
|
|
||||||
name?: string;
|
|
||||||
debouncedName?: string;
|
|
||||||
setName: Dispatch<SetStateAction<string | undefined>>;
|
|
||||||
board: BoardSchema | null;
|
board: BoardSchema | null;
|
||||||
setBoard: Dispatch<SetStateAction<BoardSchema | null>>;
|
|
||||||
status: StatusSchema | null;
|
status: StatusSchema | null;
|
||||||
setStatus: Dispatch<SetStateAction<StatusSchema | null>>;
|
sortingField?: string;
|
||||||
sortingField: string;
|
|
||||||
setSortingField: Dispatch<SetStateAction<string>>;
|
|
||||||
sortingDirection: SortDir;
|
sortingDirection: SortDir;
|
||||||
setSortingDirection: Dispatch<SetStateAction<SortDir>>;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const useDealsFilters = (): DealsFilters => {
|
type ReturnType = {
|
||||||
const [id, setId] = useState<number>();
|
debouncedId: number | undefined;
|
||||||
const [debouncedId] = useDebouncedValue(id, 300);
|
debouncedName: string;
|
||||||
|
form: UseFormReturnType<DealsFiltersForm>;
|
||||||
|
isChangedFilters: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
const [name, setName] = useState<string>();
|
const useDealsFilters = (): ReturnType => {
|
||||||
const [debouncedName] = useDebouncedValue(name, 300);
|
const initialValues = {
|
||||||
|
id: undefined,
|
||||||
|
board: null,
|
||||||
|
status: null,
|
||||||
|
name: "",
|
||||||
|
sortingField: "createdAt",
|
||||||
|
sortingDirection: "asc" as SortDir,
|
||||||
|
};
|
||||||
|
|
||||||
const [board, setBoard] = useState<BoardSchema | null>(null);
|
const form = useForm<DealsFiltersForm>({
|
||||||
const [status, setStatus] = useState<StatusSchema | null>(null);
|
initialValues,
|
||||||
|
});
|
||||||
|
|
||||||
const [sortingField, setSortingField] = useState("createdAt");
|
const isChangedFilters = !isEqual(form.values, initialValues);
|
||||||
const [sortingDirection, setSortingDirection] = useState<SortDir>("asc");
|
|
||||||
|
const [debouncedId] = useDebouncedValue(form.values.id, 300);
|
||||||
|
const [debouncedName] = useDebouncedValue(form.values.name, 300);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id,
|
|
||||||
setId,
|
|
||||||
debouncedId,
|
debouncedId,
|
||||||
name,
|
|
||||||
setName,
|
|
||||||
debouncedName,
|
debouncedName,
|
||||||
board,
|
form,
|
||||||
setBoard,
|
isChangedFilters,
|
||||||
status,
|
|
||||||
setStatus,
|
|
||||||
sortingField,
|
|
||||||
setSortingField,
|
|
||||||
sortingDirection,
|
|
||||||
setSortingDirection,
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -2,21 +2,22 @@
|
|||||||
|
|
||||||
import { ReactNode } from "react";
|
import { ReactNode } from "react";
|
||||||
import { Flex, Modal, NumberInput, rem, TextInput } from "@mantine/core";
|
import { Flex, Modal, NumberInput, rem, TextInput } from "@mantine/core";
|
||||||
|
import { UseFormReturnType } from "@mantine/form";
|
||||||
import { useDisclosure } from "@mantine/hooks";
|
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 BoardSelect from "@/components/selects/BoardSelect/BoardSelect";
|
||||||
import StatusSelect from "@/components/selects/StatusSelect/StatusSelect";
|
import StatusSelect from "@/components/selects/StatusSelect/StatusSelect";
|
||||||
import { ProjectSchema } from "@/lib/client";
|
import { ProjectSchema } from "@/lib/client";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
filters: DealsFilters;
|
filtersForm: UseFormReturnType<DealsFiltersForm>;
|
||||||
selectedProject: ProjectSchema | null;
|
selectedProject: ProjectSchema | null;
|
||||||
boardAndStatusEnabled: boolean;
|
boardAndStatusEnabled: boolean;
|
||||||
getOpener: (open: () => void) => ReactNode;
|
getOpener: (open: () => void) => ReactNode;
|
||||||
};
|
};
|
||||||
|
|
||||||
const DealsTableFiltersModal = ({
|
const DealsTableFiltersModal = ({
|
||||||
filters,
|
filtersForm,
|
||||||
selectedProject,
|
selectedProject,
|
||||||
boardAndStatusEnabled,
|
boardAndStatusEnabled,
|
||||||
getOpener,
|
getOpener,
|
||||||
@ -36,34 +37,32 @@ const DealsTableFiltersModal = ({
|
|||||||
<NumberInput
|
<NumberInput
|
||||||
label={"ID"}
|
label={"ID"}
|
||||||
placeholder={"Введите ID"}
|
placeholder={"Введите ID"}
|
||||||
value={filters.id}
|
{...filtersForm.getInputProps("id")}
|
||||||
|
value={filtersForm.values.id}
|
||||||
onChange={value =>
|
onChange={value =>
|
||||||
typeof value === "number"
|
typeof value === "number"
|
||||||
? filters.setId(Number(value))
|
? filtersForm.setFieldValue("id", Number(value))
|
||||||
: filters.setId(undefined)
|
: filtersForm.setFieldValue("id", undefined)
|
||||||
}
|
}
|
||||||
min={1}
|
min={1}
|
||||||
/>
|
/>
|
||||||
<TextInput
|
<TextInput
|
||||||
label={"Название"}
|
label={"Название"}
|
||||||
placeholder={"Введите название"}
|
placeholder={"Введите название"}
|
||||||
defaultValue={filters.name}
|
{...filtersForm.getInputProps("name")}
|
||||||
onChange={event => filters.setName(event.target.value)}
|
|
||||||
/>
|
/>
|
||||||
{boardAndStatusEnabled && (
|
{boardAndStatusEnabled && (
|
||||||
<>
|
<>
|
||||||
<BoardSelect
|
<BoardSelect
|
||||||
label={"Доска"}
|
label={"Доска"}
|
||||||
value={filters.board}
|
{...filtersForm.getInputProps("board")}
|
||||||
onChange={filters.setBoard}
|
|
||||||
projectId={selectedProject?.id}
|
projectId={selectedProject?.id}
|
||||||
clearable
|
clearable
|
||||||
/>
|
/>
|
||||||
<StatusSelect
|
<StatusSelect
|
||||||
label={"Статус"}
|
label={"Статус"}
|
||||||
value={filters.status}
|
{...filtersForm.getInputProps("status")}
|
||||||
onChange={filters.setStatus}
|
boardId={filtersForm.values.board?.id}
|
||||||
boardId={filters.board?.id}
|
|
||||||
clearable
|
clearable
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
|
|||||||
@ -1,7 +1,8 @@
|
|||||||
import { Dispatch, SetStateAction, useState } from "react";
|
import { Dispatch, SetStateAction, useState } from "react";
|
||||||
import { useQuery, useQueryClient } from "@tanstack/react-query";
|
import { useQuery, useQueryClient } from "@tanstack/react-query";
|
||||||
|
import { UseFormReturnType } from "@mantine/form";
|
||||||
import useDealsFilters, {
|
import useDealsFilters, {
|
||||||
DealsFilters,
|
DealsFiltersForm,
|
||||||
} from "@/app/deals/hooks/useDealsFilters";
|
} from "@/app/deals/hooks/useDealsFilters";
|
||||||
import { DealSchema, GetDealsData, PaginationInfoSchema } from "@/lib/client";
|
import { DealSchema, GetDealsData, PaginationInfoSchema } from "@/lib/client";
|
||||||
import {
|
import {
|
||||||
@ -18,7 +19,8 @@ type Props = {
|
|||||||
type ReturnType = {
|
type ReturnType = {
|
||||||
deals: DealSchema[];
|
deals: DealSchema[];
|
||||||
setDeals: (deals: DealSchema[]) => void;
|
setDeals: (deals: DealSchema[]) => void;
|
||||||
dealsFilters: DealsFilters;
|
dealsFiltersForm: UseFormReturnType<DealsFiltersForm>;
|
||||||
|
isChangedFilters: boolean;
|
||||||
refetchDeals: () => void;
|
refetchDeals: () => void;
|
||||||
page: number;
|
page: number;
|
||||||
setPage: Dispatch<SetStateAction<number>>;
|
setPage: Dispatch<SetStateAction<number>>;
|
||||||
@ -34,21 +36,22 @@ const useDealsList = ({
|
|||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
const [page, setPage] = useState(1);
|
const [page, setPage] = useState(1);
|
||||||
const itemsPerPage = 10;
|
const itemsPerPage = 10;
|
||||||
const dealsFilters = useDealsFilters();
|
const { form, debouncedName, debouncedId, isChangedFilters } =
|
||||||
|
useDealsFilters();
|
||||||
|
|
||||||
const options: Omit<GetDealsData, "url"> = {
|
const options: Omit<GetDealsData, "url"> = {
|
||||||
query: {
|
query: {
|
||||||
page: withPagination ? page : null,
|
page: withPagination ? page : null,
|
||||||
itemsPerPage: withPagination ? itemsPerPage : null,
|
itemsPerPage: withPagination ? itemsPerPage : null,
|
||||||
sortingField: withPagination ? dealsFilters.sortingField : null,
|
sortingField: withPagination ? form.values.sortingField : null,
|
||||||
sortingDirection: withPagination
|
sortingDirection: withPagination
|
||||||
? dealsFilters.sortingDirection
|
? form.values.sortingDirection
|
||||||
: null,
|
: null,
|
||||||
projectId: withPagination ? projectId : null,
|
projectId: withPagination ? projectId : null,
|
||||||
boardId: withPagination ? dealsFilters.board?.id : boardId,
|
boardId: withPagination ? form.values.board?.id : boardId,
|
||||||
statusId: withPagination ? dealsFilters.status?.id : null,
|
statusId: withPagination ? form.values.status?.id : null,
|
||||||
name: dealsFilters.debouncedName,
|
name: debouncedName,
|
||||||
id: dealsFilters.debouncedId,
|
id: debouncedId,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -68,7 +71,8 @@ const useDealsList = ({
|
|||||||
return {
|
return {
|
||||||
deals: data?.items ?? [],
|
deals: data?.items ?? [],
|
||||||
setDeals,
|
setDeals,
|
||||||
dealsFilters,
|
dealsFiltersForm: form,
|
||||||
|
isChangedFilters,
|
||||||
refetchDeals: refetch,
|
refetchDeals: refetch,
|
||||||
page,
|
page,
|
||||||
setPage,
|
setPage,
|
||||||
|
|||||||
Reference in New Issue
Block a user