SGERP Table

Advanced data table with filtering, sorting, column visibility, and infinite scroll

Live Example

Project Table

An interactive table with filtering, sorting, column visibility, and infinite scroll

Loading...

Vehicle Table

Table with server-side filters including custom autocomplete and select filters

Loading...

Filter-less Table Examples

Active Vehicles Table

Table showing only active vehicles with default filters, no filter UI shown.

This table shows only active vehicles (is_invalidated=false), applied via defaultFilters. The onlyFields prop limits API response to only necessary fields for better performance.

Loading...

Simulation Vehicles Table

Table showing vehicles for a specific simulation (simulation_id=211384)

This table shows vehicles for simulation ID 211384. Perfect for embedding in a simulation details page.

Loading...

Prerequisites

This component requires sgerp-frontend-lib to be installed in your project. See the Installation guide for setup instructions.

Dependencies

This component will automatically install:

  • shadcn/ui components: dropdown-menu, button, input, select

Features

  • Infinite Scroll: Automatically loads more data as you scroll
  • Client & Server Filtering: Choose between client-side or server-side filtering per filter
  • Column Visibility: Show/hide columns with a dropdown menu
  • Sorting: Click column headers to sort (ascending/descending)
  • Custom Rendering: Define custom cell renderers for any column
  • Type-safe: Full TypeScript support with generics
  • Responsive: Works on all screen sizes

Usage

import { useSGERP } from 'sgerp-frontend-lib';
import { SGERPTable } from '@/components/ui/sgerp-table';
import type { ColumnDef, FilterDef } from '@/components/ui/sgerp-table';

function MyComponent() {
  const api = useSGERP();

  const columns: ColumnDef<Project>[] = [
    {
      key: 'id',
      label: 'ID',
      width: '80px',
      sortable: true,
    },
    {
      key: 'name',
      label: 'Project Name',
      sortable: true,
    },
    {
      key: 'is_invalidated',
      label: 'Status',
      render: (value) => value ? 'Inactive' : 'Active',
    },
  ];

  const filters: FilterDef<Project>[] = [
    {
      key: 'name',
      label: 'Search',
      type: 'text',
      mode: 'client',
      clientFilter: (row, value) =>
        row.name.toLowerCase().includes(value.toLowerCase()),
    },
  ];

  return (
    <SGERPTable
      collection={api?.collections.project ?? null}
      columns={columns}
      filters={filters}
      pageSize={20}
    />
  );
}

Props

SGERPTableProps

PropTypeDefaultDescription
collectionCollection<T> | null-Required. SGERP collection to display
columnsColumnDef<T>[]-Required. Column definitions
filtersFilterDef<T>[][]Optional filter definitions
defaultFiltersRecord<string, any>{}Pre-set filters applied to all requests
initialOrderBystring-Initial sorting (e.g., 'id' or '-id' for desc)
onlyFieldsstring[]-Limit fields returned from API (e.g., ['id', 'name'])
pageSizenumber20Number of items to load per page
emptyMessagestring"No data available"Message when no data
loadingMessagestring"Loading..."Message while loading
onRowClick(row: T) => void-Callback when a row is clicked
classNamestring""Additional CSS classes

ColumnDef

PropertyTypeDescription
keystringRequired. Property key from the model
labelstringRequired. Column header label
widthstringOptional. CSS width value (e.g., "120px")
sortablebooleanEnable sorting for this column
hiddenbooleanHide column by default
render(value: any, row: T) => ReactNodeCustom cell renderer

FilterDef

PropertyTypeDescription
keystringRequired. Property key to filter
labelstringRequired. Filter label/placeholder
type'text' | 'select' | 'number' | 'date' | 'custom'Required. Input type
mode'client' | 'server'Required. Client or server-side filtering
options{ label: string; value: string | number }[]Options for select filters
serverParamstringAPI parameter name for server filters
clientFilter(row: T, value: any) => booleanCustom client filter function
renderFilter(value: any, onChange: (value: any) => void) => ReactNodeCustom filter component (for type='custom')

Examples

Basic Table

Simple table with sortable columns:

import { SGERPTable } from '@/components/ui/sgerp-table';

const columns = [
  { key: 'id', label: 'ID', sortable: true },
  { key: 'name', label: 'Name', sortable: true },
  { key: 'email', label: 'Email' },
];

<SGERPTable
  collection={api?.collections.user ?? null}
  columns={columns}
/>

Custom Cell Rendering

Render custom content in cells:

const columns: ColumnDef<Project>[] = [
  {
    key: 'name',
    label: 'Project',
    render: (value) => (
      <div className="font-medium text-primary">{value}</div>
    ),
  },
  {
    key: 'is_invalidated',
    label: 'Status',
    render: (value) => (
      <span className={`badge ${value ? 'inactive' : 'active'}`}>
        {value ? 'Inactive' : 'Active'}
      </span>
    ),
  },
  {
    key: 'created_at',
    label: 'Created',
    render: (value) => new Date(value).toLocaleDateString(),
  },
];

Client-Side Filtering

Filter data on the client without re-fetching:

const filters: FilterDef<Passenger>[] = [
  {
    key: 'name',
    label: 'Search by name',
    type: 'text',
    mode: 'client',
    clientFilter: (row, value) =>
      row.name.toLowerCase().includes(value.toLowerCase()),
  },
  {
    key: 'passenger_type_name',
    label: 'Type',
    type: 'select',
    mode: 'client',
    options: [
      { label: 'Standard', value: 'standard' },
      { label: 'VIP', value: 'vip' },
    ],
  },
];

Server-Side Filtering

Send filters to the API to fetch filtered results:

const filters: FilterDef<Passenger>[] = [
  {
    key: 'project_id',
    label: 'Project ID',
    type: 'number',
    mode: 'server',
    serverParam: 'project_id', // API parameter name
  },
  {
    key: 'organization_id',
    label: 'Organization',
    type: 'number',
    mode: 'server',
    serverParam: 'organization_id',
  },
];

When a server filter changes, the table will re-fetch data from the API with the filter parameters.

Mixed Filtering

Combine client and server filters:

const filters: FilterDef<Passenger>[] = [
  {
    key: 'project_id',
    label: 'Project',
    type: 'number',
    mode: 'server', // Filters on API
  },
  {
    key: 'name',
    label: 'Search name',
    type: 'text',
    mode: 'client', // Filters in browser
    clientFilter: (row, value) =>
      row.name.toLowerCase().includes(value.toLowerCase()),
  },
];

Column Visibility

Users can show/hide columns using the "Columns" dropdown in the toolbar.

To hide columns by default:

const columns: ColumnDef<Project>[] = [
  { key: 'id', label: 'ID', sortable: true },
  { key: 'name', label: 'Name', sortable: true },
  {
    key: 'created_at',
    label: 'Created',
    hidden: true, // Hidden by default
  },
  {
    key: 'modified_at',
    label: 'Modified',
    hidden: true, // Hidden by default
  },
];

Custom Filter Components

Use custom components for filters (e.g., autocomplete):

import { SGERPAutocomplete } from '@/components/ui/sgerp-autocomplete';

const filters: FilterDef<Passenger>[] = [
  {
    key: 'project_id',
    label: 'Project',
    type: 'custom',
    mode: 'server',
    serverParam: 'project_id',
    renderFilter: (value, onChange) => (
      <SGERPAutocomplete
        collectionName="project"
        value={value || null}
        onSelectionChange={(id) => onChange(id || '')}
        placeholder="Filter by project"
      />
    ),
  },
];

Filter-less Table with Default Filters

Create a table without filter UI by using defaultFilters instead of filters:

// Shows vehicles for a specific simulation without showing filter controls
<SGERPTable
  collection={api?.collections.vehicle ?? null}
  columns={columns}
  defaultFilters={{ simulation_id: 123, is_invalidated: false }}
  initialOrderBy="-id"
  onlyFields={['id', 'service_number', 'status', 'speed']}
  pageSize={20}
/>

The defaultFilters prop applies filters to all API requests without showing filter inputs. The onlyFields prop optimizes the API request by only fetching the specified fields, reducing payload size. This is useful when embedding filtered tables in specific contexts (e.g., showing vehicles for a specific simulation on a simulation details page).

Row Click Handler

Handle clicks on table rows:

<SGERPTable
  collection={api?.collections.project ?? null}
  columns={columns}
  onRowClick={(row) => {
    console.log('Clicked project:', row);
    router.push(`/projects/${row.id}`);
  }}
/>

Infinite Scroll

The table automatically loads more data as you scroll to the bottom. It uses an IntersectionObserver to detect when you're near the end and calls collection.fetchNext().

You can control the page size:

<SGERPTable
  collection={api?.collections.project ?? null}
  columns={columns}
  pageSize={50} // Load 50 items per page
/>

Styling

The table uses Tailwind CSS classes and can be customized:

<SGERPTable
  collection={api?.collections.project ?? null}
  columns={columns}
  className="shadow-lg rounded-xl"
/>

TypeScript

The component is fully type-safe:

import type { Project } from 'sgerp-frontend-lib';
import type { ColumnDef, FilterDef } from '@/components/ui/sgerp-table';

// Type-safe column definitions
const columns: ColumnDef<Project>[] = [
  {
    key: 'name', // Autocomplete for Project properties
    label: 'Project Name',
    render: (value, row) => {
      // value and row are properly typed
      return <div>{value}</div>;
    },
  },
];

// Type-safe filters
const filters: FilterDef<Project>[] = [
  {
    key: 'timezone', // Autocomplete for Project properties
    label: 'Timezone',
    type: 'select',
    mode: 'client',
    clientFilter: (row, value) => {
      // row is typed as Project
      return row.timezone === value;
    },
  },
];