11import { useState , useEffect } from 'react' ;
2- import { Button , Badge , Modal , Spinner , GridView } from '@objectql/ui' ;
2+ import { Button , Badge , Modal , Spinner , GridView , Input } from '@objectql/ui' ;
33import { ObjectForm } from './ObjectForm' ;
44import { cn } from '../../lib/utils' ;
55// import { useRouter } from ... passed as prop
@@ -12,13 +12,27 @@ interface ObjectListViewProps {
1212 objectSchema : any ;
1313}
1414
15+ interface SortConfig {
16+ columnId : string ;
17+ direction : 'asc' | 'desc' ;
18+ }
19+
1520export function ObjectListView ( { objectName, user, isCreating, navigate, objectSchema } : ObjectListViewProps ) {
1621 const [ data , setData ] = useState < any [ ] > ( [ ] ) ;
1722 const [ loading , setLoading ] = useState ( false ) ;
1823 const [ error , setError ] = useState < string | null > ( null ) ;
1924 const [ viewMode , setViewMode ] = useState < 'table' | 'grid' > ( 'table' ) ;
25+ const [ sortConfig , setSortConfig ] = useState < SortConfig [ ] > ( [ ] ) ;
26+ const [ searchTerm , setSearchTerm ] = useState ( '' ) ;
2027
2128 const label = objectSchema ?. label || objectSchema ?. title || objectName ;
29+
30+ // Debounce search term
31+ const [ debouncedSearch , setDebouncedSearch ] = useState ( searchTerm ) ;
32+ useEffect ( ( ) => {
33+ const timer = setTimeout ( ( ) => setDebouncedSearch ( searchTerm ) , 500 ) ;
34+ return ( ) => clearTimeout ( timer ) ;
35+ } , [ searchTerm ] ) ;
2236
2337 const getFieldLabel = ( key : string ) => {
2438 if ( ! objectSchema || ! objectSchema . fields ) return key ;
@@ -49,7 +63,35 @@ export function ObjectListView({ objectName, user, isCreating, navigate, objectS
4963 setLoading ( true ) ;
5064 setError ( null ) ;
5165
52- fetch ( `/api/object/${ objectName } ` , { headers : getHeaders ( ) } )
66+ const params = new URLSearchParams ( ) ;
67+ if ( sortConfig . length > 0 ) {
68+ // API expects sort=field:order or multiple
69+ const sortParam = sortConfig . map ( s => `${ s . columnId } :${ s . direction } ` ) . join ( ',' ) ;
70+ params . append ( 'sort' , sortParam ) ;
71+ }
72+
73+ if ( debouncedSearch ) {
74+ // Simple search implementation: try to search in name or title or description fields
75+ // Or search in all text fields
76+ const textFields = objectSchema ?. fields ?
77+ Object . entries ( objectSchema . fields )
78+ . filter ( ( [ _ , field ] : [ string , any ] ) => ! field . type || field . type === 'string' )
79+ . map ( ( [ key ] ) => key )
80+ : [ 'name' , 'title' , 'description' , 'email' ] ;
81+
82+ if ( textFields . length > 0 ) {
83+ // Construct array-based filters: [['field', 'contains', 'val'], 'or', ['field2', ...]]
84+ const searchFilters : any [ ] = [ ] ;
85+ textFields . forEach ( ( field , index ) => {
86+ if ( index > 0 ) searchFilters . push ( 'or' ) ;
87+ searchFilters . push ( [ field , 'contains' , debouncedSearch ] ) ;
88+ } ) ;
89+ // If sending strict JSON array for unified query
90+ params . append ( 'filters' , JSON . stringify ( searchFilters ) ) ;
91+ }
92+ }
93+
94+ fetch ( `/api/object/${ objectName } ?${ params . toString ( ) } ` , { headers : getHeaders ( ) } )
5395 . then ( async res => {
5496 if ( ! res . ok ) {
5597 const contentType = res . headers . get ( "content-type" ) ;
@@ -75,7 +117,7 @@ export function ObjectListView({ objectName, user, isCreating, navigate, objectS
75117
76118 useEffect ( ( ) => {
77119 if ( user && objectName ) fetchData ( ) ;
78- } , [ objectName , user ] ) ;
120+ } , [ objectName , user , sortConfig , debouncedSearch ] ) ;
79121
80122 const handleCreate = ( formData : any ) => {
81123 fetch ( `/api/object/${ objectName } ` , {
@@ -171,6 +213,14 @@ export function ObjectListView({ objectName, user, isCreating, navigate, objectS
171213 </ h3 >
172214 </ div >
173215 < div className = "flex items-center gap-2" >
216+ < div className = "w-64" >
217+ < Input
218+ placeholder = "Search..."
219+ value = { searchTerm }
220+ onChange = { ( e ) => setSearchTerm ( e . target . value ) }
221+ className = "h-9"
222+ />
223+ </ div >
174224 < div className = "inline-flex rounded-lg border border-stone-200 bg-stone-50 p-1" >
175225 < button
176226 onClick = { ( ) => setViewMode ( 'table' ) }
@@ -246,6 +296,8 @@ export function ObjectListView({ objectName, user, isCreating, navigate, objectS
246296 onCellEdit = { handleCellEdit }
247297 onDelete = { handleDelete }
248298 emptyMessage = { `No ${ label . toLowerCase ( ) } found` }
299+ enableSorting = { true }
300+ onSortChange = { setSortConfig }
249301 />
250302 ) : (
251303 < div className = "bg-white rounded-xl border border-stone-200 shadow-sm overflow-hidden" >
0 commit comments