feat: enhance DataTable with custom empty state support and update GrnClient to utilize it

This commit is contained in:
Kalana B. Thilakarathna
2026-10-19 23:23:18 +05:30
parent 992cd292fa
commit 21b38b5276
3 changed files with 28 additions and 7 deletions
@@ -3,7 +3,7 @@
import { useState, useEffect, useCallback, useMemo } from 'react' import { useState, useEffect, useCallback, useMemo } from 'react'
import { useRouter } from 'next/navigation' import { useRouter } from 'next/navigation'
import { HugeiconsIcon } from '@hugeicons/react' import { HugeiconsIcon } from '@hugeicons/react'
import { Add01Icon } from '@hugeicons/core-free-icons' import { Add01Icon, PackageDeliveredIcon } from '@hugeicons/core-free-icons'
import { DataTable } from '@/components/shared/data-table' import { DataTable } from '@/components/shared/data-table'
import type { TableConfig } from '@/components/shared/data-table' import type { TableConfig } from '@/components/shared/data-table'
import { Button } from '@/components/ui/button' import { Button } from '@/components/ui/button'
@@ -11,6 +11,7 @@ import { toast } from '@/components/ui/toast'
import { grn as grnApi, getApiError, getPaginatedItems } from '@/lib/api/index' import { grn as grnApi, getApiError, getPaginatedItems } from '@/lib/api/index'
import type { GrnResponse } from '@/types/api' import type { GrnResponse } from '@/types/api'
import { GrnViewSheet } from './grn-view-sheet' import { GrnViewSheet } from './grn-view-sheet'
import { EmptyState } from '@/components/shared/empty-state'
const fmtDate = (v: string) => const fmtDate = (v: string) =>
new Date(v).toLocaleDateString('en-GB', { day: '2-digit', month: 'short', year: 'numeric' }) new Date(v).toLocaleDateString('en-GB', { day: '2-digit', month: 'short', year: 'numeric' })
@@ -53,7 +54,19 @@ export function GrnClient() {
rowKey: 'id', rowKey: 'id',
serverSide: true, serverSide: true,
stickyHeader: true, stickyHeader: true,
emptyMessage: 'No goods receipts found.', emptyState: (
<EmptyState
icon={PackageDeliveredIcon}
title="No goods receipts yet"
description="GRNs appear here once goods are received against a purchase order or booked as a direct receipt."
action={
<Button size="sm" onClick={() => router.push('/procurement/grn/new')}>
<HugeiconsIcon icon={Add01Icon} size={14} />
New GRN
</Button>
}
/>
),
pagination: { enabled: true, pageSize, pageSizeOptions: [10, 20, 50] }, pagination: { enabled: true, pageSize, pageSizeOptions: [10, 20, 50] },
columns: [ columns: [
{ {
@@ -620,9 +620,11 @@ export function DataTable<T extends object>({
</div> </div>
))} ))}
{!loading && displayData.length === 0 && ( {!loading && displayData.length === 0 && (
<div className="h-32 flex items-center justify-center text-muted-foreground text-sm"> config.emptyState ?? (
{config.emptyMessage ?? 'No records found.'} <div className="h-32 flex items-center justify-center text-muted-foreground text-sm">
</div> {config.emptyMessage ?? 'No records found.'}
</div>
)
)} )}
{!loading && displayData.map(row => ( {!loading && displayData.map(row => (
<MobileCard <MobileCard
@@ -710,9 +712,13 @@ export function DataTable<T extends object>({
<TableRow> <TableRow>
<TableCell <TableCell
colSpan={visibleColumns.length + (config.selectable ? 1 : 0) + (config.expandable ? 1 : 0)} colSpan={visibleColumns.length + (config.selectable ? 1 : 0) + (config.expandable ? 1 : 0)}
className="h-32 text-center text-muted-foreground" className="p-0"
> >
{config.emptyMessage ?? 'No records found.'} {config.emptyState ?? (
<div className="h-32 flex items-center justify-center text-muted-foreground text-sm">
{config.emptyMessage ?? 'No records found.'}
</div>
)}
</TableCell> </TableCell>
</TableRow> </TableRow>
)} )}
@@ -119,6 +119,8 @@ export interface TableConfig<T = object> {
exportable?: boolean | ExportConfig; exportable?: boolean | ExportConfig;
density?: 'compact' | 'normal' | 'comfortable'; density?: 'compact' | 'normal' | 'comfortable';
emptyMessage?: string; emptyMessage?: string;
/** Custom rendered empty state. Overrides emptyMessage when both are provided. */
emptyState?: React.ReactNode;
rowClass?: (row: T) => string; rowClass?: (row: T) => string;
isRowSelectable?: (row: T) => boolean; isRowSelectable?: (row: T) => boolean;
expandRender?: (row: T) => React.ReactNode; expandRender?: (row: T) => React.ReactNode;