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
Vehicle Table
Table with server-side filters including custom autocomplete and select filters
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.
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.
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
| Prop | Type | Default | Description |
|---|---|---|---|
collection | Collection<T> | null | - | Required. SGERP collection to display |
columns | ColumnDef<T>[] | - | Required. Column definitions |
filters | FilterDef<T>[] | [] | Optional filter definitions |
defaultFilters | Record<string, any> | {} | Pre-set filters applied to all requests |
initialOrderBy | string | - | Initial sorting (e.g., 'id' or '-id' for desc) |
onlyFields | string[] | - | Limit fields returned from API (e.g., ['id', 'name']) |
pageSize | number | 20 | Number of items to load per page |
emptyMessage | string | "No data available" | Message when no data |
loadingMessage | string | "Loading..." | Message while loading |
onRowClick | (row: T) => void | - | Callback when a row is clicked |
className | string | "" | Additional CSS classes |
ColumnDef
| Property | Type | Description |
|---|---|---|
key | string | Required. Property key from the model |
label | string | Required. Column header label |
width | string | Optional. CSS width value (e.g., "120px") |
sortable | boolean | Enable sorting for this column |
hidden | boolean | Hide column by default |
render | (value: any, row: T) => ReactNode | Custom cell renderer |
FilterDef
| Property | Type | Description |
|---|---|---|
key | string | Required. Property key to filter |
label | string | Required. 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 |
serverParam | string | API parameter name for server filters |
clientFilter | (row: T, value: any) => boolean | Custom client filter function |
renderFilter | (value: any, onChange: (value: any) => void) => ReactNode | Custom 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;
},
},
];
Related
- SGERP Autocomplete - Dropdown for selecting from collections
- Collections - Learn about SGERP collections
- Project API - API reference for projects
- Passenger API - API reference for passengers