How to convert the column filter state into the format that the backend is excepting? #4650
Unanswered
its-monotype
asked this question in
Q&A
Replies: 2 comments
-
Interested in this also. Ive been struggling also in connecting the front end filter state to my backend |
Beta Was this translation helpful? Give feedback.
0 replies
-
I just tested this today seems ok. The backend if graphql endpoint. Basically just store the table in a ref so i can access all methods of the table inside the useMemo function so i can access the column definition including the metadata where i specify the filterType for a specifc column that then convert accordingly to the operator. import { Filter } from "@/components/tables/products2.table/filter";
import { defaultColumns } from "@/components/tables/products2.table/table-config";
import { TData } from "@/components/tables/products2.table/types";
import { useCrmq_GetProductsQuery } from "@/graphql/generated/client-operations/queries.generated";
import { Jb_Product_Bool_Exp, Order_By } from "@/graphql/generated/types";
import { logger } from "@/utils/helpers";
import { chakra, Flex, Icon, IconButton, Input, Select, Table, Tbody, Td, Th, Thead, Tr } from "@chakra-ui/react";
import {
ColumnFiltersState,
flexRender,
getCoreRowModel,
getFilteredRowModel,
getSortedRowModel,
PaginationState,
SortingState,
useReactTable,
type Table as TableType,
} from "@tanstack/react-table";
import { useEffect, useMemo, useRef, useState } from "react";
import { AiOutlineDoubleLeft, AiOutlineDoubleRight, AiOutlineLeft, AiOutlineRight } from "react-icons/ai";
const Products2Table = () => {
const [{ pageIndex, pageSize }, setPagination] = useState<PaginationState>({
pageIndex: 0,
pageSize: 10,
});
const tableRef = useRef<TableType<TData> | null>(null);
const pagination = useMemo(
() => ({
pageIndex,
pageSize,
}),
[pageIndex, pageSize]
);
const [columnFilters, setColumnFilters] = useState<ColumnFiltersState>([]);
const where = useMemo(() => {
let where: { _and: Jb_Product_Bool_Exp[] } = { _and: [] };
columnFilters.forEach((filt) => {
const operator = tableRef.current?.getColumn?.(filt.id)?.columnDef.meta?.filterType === "array" ? "_in" : "_eq";
if (typeof filt.value === "string" || typeof filt.value === "number") {
// @ts-ignore
where._and.push({ [filt.id]: { [operator]: operator === "_eq" ? filt.value : filt.value.split(",") } });
}
if (Array.isArray(filt.value)) {
if (filt.value?.[0]) where._and.push({ [filt.id]: { _gte: parseInt(filt.value[0]) } });
if (filt.value?.[1]) where._and.push({ [filt.id]: { _lte: parseInt(filt.value[1]) } });
}
});
logger.log("file: index.tsx:48 ~ columnFilters.forEach ~ columnFilters", columnFilters);
logger.log("file: index.tsx:54 ~ filters ~ where", where);
return where;
}, [columnFilters]);
// SORTING
const [sorting, setSorting] = useState<SortingState>([]);
const memoSorting = useMemo(() => {
if (!sorting?.length) return { order_by: { created_at: Order_By.Desc } };
return {
order_by: { [sorting?.[0]?.id]: sorting[0].desc ? Order_By.Desc : Order_By.Asc },
};
}, [sorting]);
const { data } = useCrmq_GetProductsQuery(
{
limit: pagination.pageSize,
offset: pagination.pageIndex * pagination.pageSize,
where,
...memoSorting,
},
{
select(data) {
return {
jb_products: data.jb_products.map((p) => ({ ...p, id: p.id.toString() })),
jb_product_aggregate: data.jb_product_aggregate,
};
},
}
);
const tablePages = useMemo(
() => Math.floor((data?.jb_product_aggregate?.aggregate?.count ?? 0) / pagination.pageSize),
[data, pagination.pageSize]
);
const table = useReactTable({
data: (data?.jb_products ?? []) as TData[],
columns: defaultColumns,
columnResizeMode: "onChange",
enableFilters: true,
enableColumnFilters: true,
state: {
pagination,
columnFilters,
sorting,
},
getFilteredRowModel: getFilteredRowModel(),
onColumnFiltersChange: setColumnFilters,
onSortingChange: setSorting,
onPaginationChange: setPagination,
getSortedRowModel: getSortedRowModel(),
enableColumnResizing: true,
enableHiding: true,
manualSorting: true,
manualPagination: true,
getCoreRowModel: getCoreRowModel(),
});
useEffect(() => {
tableRef.current = table;
}, [table]);
return (
<>
<Flex flexDir={"column"} w="full" overflow={"auto"}>
<Table
overflow={"auto"}
h="200px"
{...{
style: {
width: table.getCenterTotalSize(),
},
}}
>
<Thead>
{table.getHeaderGroups().map((headerGroup) => (
<Tr key={headerGroup.id}>
{headerGroup.headers.map((header) => (
<Th
key={header.id}
{...{
colSpan: header.colSpan,
style: {
width: header.getSize(),
},
}}
>
<div
style={{
display: "flex",
flexDirection: "column",
alignContent: "stretch",
height: "100%",
justifyContent: "start",
justifyItems: "start",
}}
>
<span
{...{
className: `${header.column.getCanSort() ? "cursor-pointer select-none" : ""}`,
onClick: header.column.getToggleSortingHandler(),
}}
>
{header.isPlaceholder ? null : flexRender(header.column.columnDef.header, header.getContext())}
{" "}{" "}
{{
asc: " 🔼",
desc: " 🔽",
}[header.column.getIsSorted() as string] ?? null}
</span>
{header.column.getCanFilter() ? (
<div style={{ paddingTop: 2 }}>
<Filter column={header.column} table={table} />
</div>
) : null}
<div
{...{
onMouseDown: header.getResizeHandler(),
onTouchStart: header.getResizeHandler(),
className: `resizer ${header.column.getIsResizing() ? "isResizing" : ""}`,
}}
></div>
</div>
</Th>
))}
</Tr>
))}
</Thead>
<Tbody overflow={"auto"} h="200px">
{table?.getRowModel?.().rows.map((row) => (
<Tr key={row.id}>
{row.getVisibleCells().map((cell) => (
<Td
{...{
key: cell.id,
style: {
width: cell.column.getSize(),
},
}}
key={cell.id}
>
{flexRender(cell.column.columnDef.cell, cell.getContext())}
</Td>
))}
</Tr>
))}
</Tbody>
</Table>
</Flex>
<Flex gap={1} align="center" pt={2}>
<IconButton
size="sm"
icon={<Icon as={AiOutlineDoubleLeft} />}
onClick={() => table.setPageIndex(0)}
disabled={!table.getCanPreviousPage()}
aria-label={""}
/>
<IconButton
size="sm"
icon={<Icon as={AiOutlineLeft} />}
onClick={() => table.previousPage()}
disabled={!table.getCanPreviousPage()}
aria-label={""}
/>
<IconButton
size="sm"
icon={<Icon as={AiOutlineRight} />}
onClick={() => table.nextPage()}
disabled={!table.getCanNextPage()}
aria-label={""}
/>
<IconButton
size="sm"
icon={<Icon as={AiOutlineDoubleRight} />}
onClick={() => table.setPageIndex(table.getPageCount() - 1)}
disabled={!table.getCanNextPage()}
aria-label={""}
/>
<chakra.span display="flex">
<div>Page</div>
<strong>
{table.getState().pagination.pageIndex + 1} of {tablePages}
</strong>
</chakra.span>
<Flex flex="1 0" w="full" justify={"end"}>
<span className="flex items-center gap-1">
Go to page:
<Input
size="sm"
rounded={"md"}
w="4rem"
type="number"
defaultValue={table.getState().pagination.pageIndex + 1}
onChange={(e) => {
const page = e.target.value ? Number(e.target.value) - 1 : 0;
table.setPageIndex(page);
}}
/>
</span>
<Select
size="sm"
rounded={"md"}
w="10rem"
value={table.getState().pagination.pageSize}
onChange={(e) => {
table.setPageSize(Number(e.target.value));
}}
>
{[10, 20, 30, 40, 50].map((pageSize) => (
<option key={pageSize} value={pageSize}>
Show {pageSize}
</option>
))}
</Select>
</Flex>
</Flex>
</>
);
};
export default Products2Table; |
Beta Was this translation helpful? Give feedback.
0 replies
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
I'm trying to implement server-side column filtering for a table in my React app, but I'm having trouble converting the "ColumnFiltersState" object that I'm using on the frontend into the format that the backend (which uses the "nestjs-paginate" library) is expecting. The "ColumnFiltersState" object contains a "value" field that can be of type "unknown" and I'm not sure how to handle it. I have a couple of possible solutions in mind:
Can anyone provide some guidance on how to correctly map the column filters for the backend, and which solution would be the best approach? I would really appreciate any feedback or advice on the best approach, and even better if there is an example code to help me understand the solution better.
Beta Was this translation helpful? Give feedback.
All reactions