Skip to content

Business Table

BusinessTable is a generic, filterable, paginated table component used broadly across modules (system admin, BusinessManager, PartnersManager, SocialMediaManager, TaskTable, and others).

It is built on shared list/filter primitives:

  • useListEngine
  • useListFilterRendering
  • Filter<T> contracts from BusinessTable/filters.tsx

From src/components/BusinessTable/index.tsx:

  • default export: BusinessTable<T extends Record<string, any>>(props)
  • type Column<T>
  • type FilterGroup (alias of ListFilterGroup)
export type Column<T> = {
key: keyof T;
label: string;
render?: (row: T) => React.ReactNode;
};
export type FilterGroup = ListFilterGroup;

Filters come from src/components/BusinessTable/filters.tsx.

Supported type values:

  • "text"
  • "select"
  • "range"
  • "dateRange"

Each filter requires:

  • key
  • label
  • placement ("header" | "panel")

Optional metadata:

  • group
  • helpText
  • dynamic
interface BusinessTableProps<T> {
title: string;
data: T[];
columns: Column<T>[];
rowKey: keyof T;
onSelect?: (id: string) => void;
filters?: readonly Filter<T>[];
filterGroups?: readonly FilterGroup[];
actions?: React.ReactNode;
loading?: boolean;
pageSize?: number;
initialFilters?: Record<string, string>;
}
  • rowKey: used for row React key and onSelect id extraction.
  • actions: rendered in the right-side kebab menu (DropdownButton).
  • pageSize:
    • > 0 enables pagination footer
    • 0/undefined shows all filtered rows
  • loading: adds top-row loader in table header.
  • initialFilters: initial text/select filter state map.
  • Header filters render inline.
  • Panel filters render inside SidePanelButton (“Filters”).
  • Grouped panel rendering is enabled when filterGroups or filter group values are provided.

If onSelect is provided, row click calls:

onSelect(String(row[rowKey]))

When filtered data is empty, component renders “No records found”.

Footer appears only when both conditions are true:

  • pageSize > 0
  • totalPages > 1

Footer includes:

  • first/previous buttons
  • page indicator
  • next/last buttons
  • filtered item count
import BusinessTable, { type Column, type FilterGroup } from "@/components/BusinessTable";
import type { Filter } from "@/components/BusinessTable/filters";
type Partner = {
partnerId: number;
name: string;
status: string;
createdAt: string;
};
const columns: Column<Partner>[] = [
{ key: "name", label: "Partner Name" },
{ key: "status", label: "Status" },
{
key: "createdAt",
label: "Created",
render: (row) => new Date(row.createdAt).toLocaleDateString(),
},
];
const filters: Filter<Partner>[] = [
{
type: "text",
label: "Search",
key: "name",
placement: "header",
placeholder: "Search partner",
},
{
type: "select",
label: "Status",
key: "status",
placement: "panel",
options: [
{ label: "Active", value: "Active" },
{ label: "Inactive", value: "Inactive" },
],
group: "status",
},
{
type: "dateRange",
label: "Created Date",
key: "createdAt",
placement: "panel",
group: "dates",
},
];
const filterGroups: FilterGroup[] = [
{ id: "status", label: "Status Filters", order: 1 },
{ id: "dates", label: "Date Filters", order: 2 },
];
<BusinessTable<Partner>
title="Partners"
data={partners}
columns={columns}
rowKey="partnerId"
filters={filters}
filterGroups={filterGroups}
pageSize={20}
loading={isLoading}
onSelect={(id) => openPartner(id)}
actions={<button onClick={openCreate}>Create Partner</button>}
/>;

Use BusinessTable when you need:

  • tabular display with reusable filter controls,
  • optional grouped side-panel filters,
  • built-in pagination and row click handling.

Use DynamicTable for simpler table rendering with lightweight row-selection behavior. Use ListEngine when rendering non-table item layouts.

  • src/components/BusinessTable/index.tsx
  • src/components/BusinessTable/filters.tsx
  • src/components/ListEngine/useListEngine.ts
  • src/components/ListEngine/useListFilterRendering.tsx