@@ -10,20 +10,20 @@ import {
1010} from '@/components/shadcn/table' ;
1111import { cn } from '@/lib/utils' ;
1212import { Link } from '@inertiajs/react' ;
13- import { PlusIcon } from 'lucide-react' ;
13+ import { Loader2 , PlusIcon } from 'lucide-react' ;
1414import { getFieldValue , renderCellContent } from './datatable' ;
1515import { DatatableProvider } from './datatable-context' ;
1616import DatatablePagination from './partials/pagination' ;
1717import { DatatableOptions , DataTableProps } from './types' ;
1818import { useDatatable } from './use-datatable' ;
1919
20- export function DataTable < T extends object > ( {
20+ export function DataTable < TModel extends object > ( {
2121 url,
2222 columns,
2323 currentPage,
2424 createUrl,
2525 uniqueKey,
26- } : DataTableProps < T > ) {
26+ } : DataTableProps < TModel > ) {
2727 const options : DatatableOptions = {
2828 currentPage : currentPage || 1 ,
2929 pageSize : 10 ,
@@ -33,102 +33,123 @@ export function DataTable<T extends object>({
3333 hasPaginator : true ,
3434 } ;
3535
36- const { result, data, loading, refresh, nextPage, prevPage, goToPage } =
37- useDatatable < T > ( url , options ) ;
36+ const { result, data, refresh, loading, nextPage, prevPage, goToPage } =
37+ useDatatable < TModel > ( url , options ) ;
38+
39+ const renderEmptyState = ( ) => (
40+ < TableRow >
41+ < TableCell
42+ colSpan = { columns . length }
43+ className = "bg-background py-16 text-center text-muted-foreground"
44+ >
45+ < div className = "flex flex-col items-center gap-3" >
46+ < h3 className = "text-xl font-semibold" > No records found</ h3 >
47+ < p className = "text-sm text-muted-foreground" >
48+ Try adjusting your filters or add a new record.
49+ </ p >
50+ { createUrl && (
51+ < Link
52+ href = { createUrl }
53+ className = { cn (
54+ buttonVariants ( {
55+ variant : 'secondary' ,
56+ size : 'sm' ,
57+ } ) ,
58+ ) }
59+ >
60+ < PlusIcon className = "mr-1 size-4" />
61+ Add New
62+ </ Link >
63+ ) }
64+ </ div >
65+ </ TableCell >
66+ </ TableRow >
67+ ) ;
68+
69+ const renderLoadingState = ( ) => (
70+ < TableRow >
71+ < TableCell
72+ colSpan = { columns . length }
73+ className = "bg-background py-16 text-center text-muted-foreground"
74+ >
75+ < div className = "flex flex-col items-center gap-3" >
76+ < Loader2 className = "size-6 animate-spin text-primary" />
77+ < p > Fetching data...</ p >
78+ </ div >
79+ </ TableCell >
80+ </ TableRow >
81+ ) ;
3882
3983 return (
4084 < DatatableProvider value = { { refresh } } >
41- < Table >
42- < TableHeader >
43- < TableRow className = "bg-slate-100 dark:bg-slate-950" >
44- { columns . map ( ( column , index ) => (
45- < TableHead key = { `${ column . field } _${ index } ` } >
46- { column . header }
47- </ TableHead >
48- ) ) }
49- </ TableRow >
50- </ TableHeader >
51- { loading && data . length === 0 ? (
52- < TableBody >
53- < TableRow className = "text-center" >
54- < TableCell
55- colSpan = { columns . length }
56- className = "py-16 text-center"
57- >
58- Fetching data...
59- </ TableCell >
60- </ TableRow >
61- </ TableBody >
62- ) : data . length === 0 ? (
63- < TableBody >
64- < TableRow className = "text-center" >
65- < TableCell
66- colSpan = { columns . length }
67- className = "space-y-2 bg-background py-16 text-center hover:bg-background"
68- >
69- < h3 className = "text-2xl font-semibold" >
70- Oops!
71- </ h3 >
72- < p > There is no data</ p >
73- { createUrl && (
74- < Link
75- href = { createUrl }
76- className = { cn (
77- buttonVariants ( {
78- variant : 'secondary' ,
79- size : 'sm' ,
80- } ) ,
81- ) }
85+ < div className = "overflow-hidden rounded-lg border bg-card shadow-sm" >
86+ < div className = "overflow-x-auto" >
87+ < Table >
88+ < TableHeader >
89+ < TableRow className = "bg-muted/30" >
90+ { columns . map ( ( column , index ) => (
91+ < TableHead
92+ key = { `${ column . field } _${ index } ` }
93+ className = "px-4 py-3 text-left text-sm font-semibold whitespace-nowrap text-muted-foreground"
8294 >
83- < PlusIcon className = "size-4" />
84- < span > Add New</ span >
85- </ Link >
86- ) }
87- </ TableCell >
88- </ TableRow >
89- </ TableBody >
90- ) : (
91- < TableBody >
92- { data . map ( ( row ) => (
93- < TableRow
94- key = { getFieldValue ( row , uniqueKey ?? 'id' ) }
95- >
96- { columns . map ( ( column , index ) => {
97- return (
98- < TableCell
99- key = { `${ column . field } _${ index } ` }
100- className = { cn ( 'font-medium' ) }
101- >
102- { renderCellContent ( row , column ) }
103- </ TableCell >
104- ) ;
105- } ) }
95+ { column . header }
96+ </ TableHead >
97+ ) ) }
10698 </ TableRow >
107- ) ) }
108- </ TableBody >
109- ) }
99+ </ TableHeader >
100+
101+ < TableBody >
102+ { loading && data . length === 0
103+ ? renderLoadingState ( )
104+ : data . length === 0
105+ ? renderEmptyState ( )
106+ : data . map ( ( row , i ) => (
107+ < TableRow
108+ key = {
109+ getFieldValue (
110+ row ,
111+ uniqueKey ?? 'id' ,
112+ ) ?? i
113+ }
114+ className = "border-b transition-colors last:border-0 hover:bg-muted/50"
115+ >
116+ { columns . map ( ( column , index ) => (
117+ < TableCell
118+ key = { `${ column . field } _${ index } ` }
119+ className = "px-4 py-3 text-sm"
120+ >
121+ { renderCellContent (
122+ row ,
123+ column ,
124+ ) }
125+ </ TableCell >
126+ ) ) }
127+ </ TableRow >
128+ ) ) }
129+ </ TableBody >
110130
111- < TableFooter >
112- < TableRow className = "bg-background hover:bg-background" >
113- < TableCell colSpan = { columns . length } >
114- < div className = "flex items-center justify-between" >
115- < p className = "text-sm" >
116- Showing { result . meta . from } -{ ' ' }
117- { result . meta . to } of { result . meta . total }
118- </ p >
119- < div >
120- < DatatablePagination
121- meta = { result . meta }
122- onNext = { nextPage }
123- onGotoPage = { goToPage }
124- onPrev = { prevPage }
125- />
126- </ div >
127- </ div >
128- </ TableCell >
129- </ TableRow >
130- </ TableFooter >
131- </ Table >
131+ < TableFooter >
132+ < TableRow className = "bg-muted/20" >
133+ < TableCell colSpan = { columns . length } >
134+ < div className = "flex flex-col items-center justify-between gap-3 px-2 text-sm sm:flex-row" >
135+ < p className = "text-muted-foreground" >
136+ Showing { result . meta . from } –
137+ { result . meta . to } of{ ' ' }
138+ { result . meta . total } results
139+ </ p >
140+ < DatatablePagination
141+ meta = { result . meta }
142+ onNext = { nextPage }
143+ onGotoPage = { goToPage }
144+ onPrev = { prevPage }
145+ />
146+ </ div >
147+ </ TableCell >
148+ </ TableRow >
149+ </ TableFooter >
150+ </ Table >
151+ </ div >
152+ </ div >
132153 </ DatatableProvider >
133154 ) ;
134155}
0 commit comments