@@ -9,6 +9,22 @@ import { PaginatedData } from '@/types';
99import { Input } from './ui/input' ;
1010import { useEffect , useState } from 'react' ;
1111
12+ function SortIndicator ( { sortKey } : { sortKey : string } ) {
13+ if ( typeof window === 'undefined' ) {
14+ return null ;
15+ }
16+
17+ const params = new URLSearchParams ( window . location . search ) ;
18+ const current = params . get ( 'sort_by' ) ;
19+ const dir = params . get ( 'sort_dir' ) || 'desc' ;
20+
21+ if ( current !== sortKey ) {
22+ return < span className = "text-muted-foreground" > ↕</ span > ;
23+ }
24+
25+ return < span className = "text-muted-foreground" > { dir === 'asc' ? '↑' : '↓' } </ span > ;
26+ }
27+
1228interface DataTableProps < TData , TValue > {
1329 columns : ColumnDef < TData , TValue > [ ] ;
1430 paginatedData ?: PaginatedData < TData > ;
@@ -19,6 +35,7 @@ interface DataTableProps<TData, TValue> {
1935 isFetching ?: boolean ;
2036 isLoading ?: boolean ;
2137 searchable ?: boolean ;
38+ sortable ?: boolean ;
2239 onRowClick ?: ( row : TData ) => void ;
2340}
2441
@@ -32,6 +49,7 @@ export function DataTable<TData, TValue>({
3249 isFetching,
3350 isLoading,
3451 searchable,
52+ sortable = false ,
3553 onRowClick,
3654} : DataTableProps < TData , TValue > ) {
3755 // Use paginatedData.data if available, otherwise fall back to data prop
@@ -76,6 +94,18 @@ export function DataTable<TData, TValue>({
7694 urlObj . searchParams . set ( 'search' , searchQuery ) ;
7795 }
7896
97+ // Preserve the current sort parameters
98+ const currentParams = new URLSearchParams ( window . location . search ) ;
99+ const sortBy = currentParams . get ( 'sort_by' ) ;
100+ const sortDir = currentParams . get ( 'sort_dir' ) ;
101+
102+ if ( sortBy ) {
103+ urlObj . searchParams . set ( 'sort_by' , sortBy ) ;
104+ }
105+ if ( sortDir ) {
106+ urlObj . searchParams . set ( 'sort_dir' , sortDir ) ;
107+ }
108+
79109 router . get ( urlObj . toString ( ) , { } , { preserveState : true , preserveScroll : true } ) ;
80110 }
81111 } ;
@@ -98,6 +128,19 @@ export function DataTable<TData, TValue>({
98128 if ( searchQuery . length > 0 ) {
99129 url . searchParams . set ( 'search' , searchQuery ) ;
100130 }
131+
132+ // Preserve the current sort parameters
133+ const currentParams = new URLSearchParams ( window . location . search ) ;
134+ const sortBy = currentParams . get ( 'sort_by' ) ;
135+ const sortDir = currentParams . get ( 'sort_dir' ) ;
136+
137+ if ( sortBy ) {
138+ url . searchParams . set ( 'sort_by' , sortBy ) ;
139+ }
140+ if ( sortDir ) {
141+ url . searchParams . set ( 'sort_dir' , sortDir ) ;
142+ }
143+
101144 router . get (
102145 url . toString ( ) ,
103146 { } ,
@@ -141,9 +184,41 @@ export function DataTable<TData, TValue>({
141184 { table . getHeaderGroups ( ) . map ( ( headerGroup ) => (
142185 < TableRow key = { headerGroup . id } >
143186 { headerGroup . headers . map ( ( header ) => {
187+ const canSort = sortable && header . column . getCanSort ( ) ;
188+
189+ // determine unique key to use for sorting: use the column id provided by the table
190+ const sortKey = header . id ;
191+
144192 return (
145193 < TableHead key = { header . id } >
146- { header . isPlaceholder ? null : flexRender ( header . column . columnDef . header , header . getContext ( ) ) }
194+ { header . isPlaceholder ? null : canSort ? (
195+ < button
196+ type = "button"
197+ className = "flex cursor-pointer items-center gap-2"
198+ onClick = { ( ) => {
199+ // Build new URL preserving all existing params
200+ const url = new URL ( window . location . href ) ;
201+ const params = url . searchParams ;
202+
203+ const current = params . get ( 'sort_by' ) ;
204+ const currentDir = params . get ( 'sort_dir' ) || 'desc' ;
205+
206+ if ( current !== sortKey ) {
207+ params . set ( 'sort_by' , sortKey ) ;
208+ params . set ( 'sort_dir' , 'asc' ) ;
209+ } else {
210+ params . set ( 'sort_dir' , currentDir === 'asc' ? 'desc' : 'asc' ) ;
211+ }
212+
213+ router . get ( url . toString ( ) , { } , { preserveState : true , preserveScroll : true } ) ;
214+ } }
215+ >
216+ { flexRender ( header . column . columnDef . header , header . getContext ( ) ) }
217+ < SortIndicator sortKey = { sortKey } />
218+ </ button >
219+ ) : (
220+ flexRender ( header . column . columnDef . header , header . getContext ( ) )
221+ ) }
147222 </ TableHead >
148223 ) ;
149224 } ) }
0 commit comments