Skip to content

Instantly share code, notes, and snippets.

@saulin18
Forked from leonardof02/ExampleTable.tsx
Created August 31, 2025 01:21
Show Gist options
  • Select an option

  • Save saulin18/18137b3f3b5472f36bdfdb1e898147b3 to your computer and use it in GitHub Desktop.

Select an option

Save saulin18/18137b3f3b5472f36bdfdb1e898147b3 to your computer and use it in GitHub Desktop.
Generic Table used in Merco Sistema at NOX Creation
// This is an example table using the GenericTableComponent.tsx
// mockData is generated randomly
import { Image, Text, Checkbox, Badge, Box, Flex } from "@chakra-ui/react";
import { ColumnDef } from "@tanstack/react-table";
import React from "react";
import GenericTable from "@/frontend/core/components/GenericTable";
import TransitActionsButtonGroup from "./TransitTableActions";
import { formatDate } from "@/frontend/core/utils/formatDate";
import { ChevronDownIcon, ChevronRightIcon } from "@chakra-ui/icons";
import TransitTableActions from "./TransitTableActions";
const mockData: MerchancyItem[] = [
{
id: "001",
category: "Electrónica",
product: "Laptop Dell XPS 13",
deliveryDate: new Date("2024-07-15"),
paymentDate: new Date("2024-08-01"),
quantity: 1,
amount: "USD",
amortized: "Pagado",
type: "Compra",
},
{
id: "002",
category: "Libros",
product: "El Principito",
deliveryDate: new Date("2024-06-20"),
quantity: 3,
amount: "USD",
amortized: "Pendiente",
type: "Venta",
subRows: [
{
id: "002a",
category: "Libros",
product: "El Principito - Edición Especial",
deliveryDate: new Date("2024-06-22"),
quantity: 1,
amount: "USD",
amortized: "Pendiente",
type: "Venta",
},
],
},
{
id: "003",
category: "Ropa",
product: "Camiseta Manga Larga",
deliveryDate: new Date("2024-07-10"),
paymentDate: new Date("2024-07-30"),
quantity: 5,
amount: "USD",
amortized: "Pagado",
type: "Compra",
},
{
id: "004",
category: "Herramientas",
product: "Martillo",
deliveryDate: new Date("2024-05-25"),
quantity: 2,
amount: "USD",
amortized: "Pendiente",
type: "Compra",
},
{
id: "005",
category: "Jardín",
product: "Regadera",
deliveryDate: new Date("2024-06-01"),
paymentDate: new Date("2024-06-15"),
quantity: 1,
amount: "USD",
amortized: "Pagado",
type: "Venta",
subRows: [
{
id: "005a",
category: "Jardín",
product: "Manguera",
deliveryDate: new Date("2024-06-03"),
quantity: 1,
amount: "USD",
amortized: "Pagado",
type: "Venta",
},
],
},
];
type MerchancyItem = {
id: string;
category: string;
product: string;
deliveryDate: Date;
paymentDate?: Date;
quantity: number;
amount: string;
amortized: "Pagado" | "Pendiente";
type: string;
subRows?: MerchancyItem[];
};
interface Props {}
export default function TransitTable({}: Props) {
const page = 1;
const pageSize = 10;
const columns: ColumnDef<MerchancyItem>[] = React.useMemo(
() => [
{
header: ({ table }) => (
<Checkbox
size={"sm"}
colorScheme="cyan"
isChecked={table.getIsAllRowsSelected()}
isIndeterminate={table.getIsSomeRowsSelected()}
onChange={(event) => {
table.toggleAllRowsSelected(event.target.checked);
}}
>
Id
</Checkbox>
),
accessorKey: "code",
cell: ({ row, getValue }) => (
<Flex alignItems={"center"} gap={"10px"}>
<Checkbox
size={"sm"}
colorScheme="cyan"
type="checkbox"
isChecked={row.getIsSelected()}
onChange={(event) => row.toggleSelected(event.target.checked)}
>
{getValue<string>()}
</Checkbox>
{row.getCanExpand() && (
<Box onClick={() => row.toggleExpanded()} cursor={"pointer"}>
{row.getIsExpanded() ? (
<ChevronDownIcon />
) : (
<ChevronRightIcon />
)}
</Box>
)}
</Flex>
),
},
{
header: "Categoria",
accessorKey: "category",
},
{
header: "Producto",
accessorKey: "product",
},
{
header: "Fecha de entrega",
accessorKey: "deliveryDate",
cell: (date) => formatDate(date.getValue<Date>()),
},
{
header: "Fecha Pago",
accessorKey: "paymentDate",
cell: (date) => formatDate(date.getValue<Date>()),
},
{
header: "Cantidad",
accessorKey: "quantity",
},
{
header: "Importe",
accessorKey: "amount",
cell: (amount) => <Badge>{amount.getValue<string>()}</Badge>,
},
{
header: "Amortizado",
accessorKey: "amortized",
cell: (amortized) => (
<Badge
boxShadow={"none"}
backgroundColor={
amortized.getValue<string>() === "Pagado"
? "green.500"
: "red.500"
}
color={"white"}
>
{amortized.getValue<string>()}
</Badge>
),
},
{
header: "Tipo",
accessorKey: "type",
},
{
id: "actions",
cell: (props) => <TransitTableActions />,
},
],
[]
);
return (
<GenericTable
onFind={() => {}}
columns={columns}
data={mockData}
title="Mercancías en Tránsito"
pagination={{
count: 10,
page,
pageSize,
}}
/>
);
}
// Generic Table using Tantack Table lib
// WARNING: Pagination support is managed by other developer
// Tanstack offers support for pagination too, next time that I use this feature
// find in tanstack docs for pagination support instead of implement my own like this
import {
ColumnDef,
flexRender,
getCoreRowModel,
getExpandedRowModel,
getSortedRowModel,
useReactTable,
} from "@tanstack/react-table";
import {
Box,
Flex,
Text,
Table,
TableContainer,
Tbody,
Td,
Th,
Thead,
Tr,
Stack,
} from "@chakra-ui/react";
import React, { useEffect } from "react";
import { SearchIcon } from "@chakra-ui/icons";
import OrderByIcon from "../icons/OrderByIcon";
import ExportableTableContainer from "./ExportableTableContainer";
import SearchIconButton from "./SearchIconButton";
import { Pagination } from "./Pagination";
import TableFilterCount from "./TableFilterCount";
interface Props<T> {
title: string;
noExportable?: boolean;
columns: ColumnDef<T>[];
data: T[];
pagination: {
count: number;
page: number;
pageSize: number;
onChange?: (e: number) => void;
};
onChangeFilterCount?: (e: number) => void;
onSelectItems?: (items: Array<any>) => void;
onFind?: (column: string, value: string) => void;
onDownloadExcel?: () => void;
}
// IMPORTANTE: La columna de acciones debe tener como id: "actions"
// para que no tenga header
interface RowWithSubItems {
subRows?: [];
}
export default function GenericTable<T>({
data,
title,
columns,
pagination,
onChangeFilterCount,
onSelectItems,
onFind,
onDownloadExcel,
noExportable = false,
}: Props<T>) {
const { getRowModel, getHeaderGroups, getSelectedRowModel, toggleAllRowsSelected } = useReactTable({
data: data,
columns: columns,
getCoreRowModel: getCoreRowModel(),
getSortedRowModel: getSortedRowModel(),
getExpandedRowModel: getExpandedRowModel(),
getSubRows: (row) => (row as RowWithSubItems).subRows,
enableExpanding: true,
enableRowSelection: true,
});
useEffect(()=>{
toggleAllRowsSelected(false)
}, [data])
useEffect(() => {
if (onSelectItems)
onSelectItems(getSelectedRowModel().rows.map((t) => t.original));
}, [getSelectedRowModel()]);
const tableContent = (
<TableContainer>
<Table fontSize={"13px"} minH={"100px"} variant={"striped"}>
<Thead position="sticky" top={0} bg={"white"}>
{getHeaderGroups().map((headerGroup) => (
<Tr key={headerGroup.id}>
{headerGroup.headers.map((header, i) => (
<Th
key={header.id}
justifyContent={"space-between"}
alignItems={"center"}
cursor={"pointer"}
>
{header.id !== "actions" && (
<Flex width={"full"} justifyContent={"space-between"}>
<Text lineHeight={2}>
{flexRender(
header.column.columnDef.header,
header.getContext()
)}
</Text>
{columns[i].id && (
<Flex alignItems={"center"} gap={"2px"}>
<Box onClick={() => header.column.toggleSorting()}>
<OrderByIcon />
</Box>
<Box>
<SearchIconButton
ButtonIcon={<SearchIcon />}
onFind={onFind}
column_name={columns[i].id as string}
/>
</Box>
</Flex>
)}
</Flex>
)}
</Th>
))}
</Tr>
))}
</Thead>
<Tbody>
{getRowModel().rows.map((row) => (
<React.Fragment key={row.id}>
<Tr
key={row.id}
borderColor={"red.400 !important"}
borderY={"none"} /* row.getIsSelected() ? "2px solid" : */
>
{row.getVisibleCells().map((cell) => (
<Td key={cell.id}>
{flexRender(cell.column.columnDef.cell, cell.getContext())}
</Td>
))}
</Tr>
{row.subRows.map(
(subRow) =>
row.getIsExpanded() && (
<Tr
key={subRow.id}
borderColor={"red.400!important"}
borderY={"none"}
>
{subRow.getVisibleCells().map((cell) => (
<Td key={cell.id}>
{flexRender(
cell.column.columnDef.cell,
cell.getContext()
)}
</Td>
))}
</Tr>
)
)}
</React.Fragment>
))}
</Tbody>
</Table>
</TableContainer>
);
const paginationElement = (
<React.Fragment>
{data.length > 0 && (
<Pagination
count={pagination.count}
pageSize={pagination.pageSize}
/* siblingCount={2} */
page={pagination.page}
onChange={(e) => {
if (pagination.onChange) {
pagination.onChange(e.page);
window.scrollTo({
top: 0,
left: 0,
behavior: "smooth",
});
}
}}
/>
)}
</React.Fragment>
);
if (!noExportable)
return (
<ExportableTableContainer
title={title}
onChangeFilterCount={onChangeFilterCount}
onDownloadExcel={onDownloadExcel}
>
{tableContent}
{paginationElement}
</ExportableTableContainer>
);
if (noExportable)
return (
<Stack spacing={1}>
<TableFilterCount onChangeFilterCount={onChangeFilterCount} />
{tableContent}
{paginationElement}
</Stack>
);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment