@@ -12,7 +12,7 @@ import TableRow from '@mui/material/TableRow';
12
12
import { Box } from '@mui/system' ;
13
13
14
14
import { useTheme } from '@mui/material/styles' ;
15
- import { alpha , Collapse , Divider , Paper , ToggleButton , Tooltip } from "@mui/material" ;
15
+ import { alpha , Collapse , Divider , Paper , ToggleButton , Tooltip , CircularProgress } from "@mui/material" ;
16
16
17
17
import { TSelectableItemProps , createSelectable } from 'react-selectable-fast' ;
18
18
import { SelectableGroup } from 'react-selectable-fast' ;
@@ -163,6 +163,8 @@ export const SelectableDataGrid: React.FC<SelectableDataGridProps> = ({ tableId,
163
163
164
164
const [ rowsToDisplay , setRowsToDisplay ] = React . useState < any [ ] > ( rows ) ;
165
165
166
+ const [ isLoading , setIsLoading ] = React . useState < boolean > ( false ) ;
167
+
166
168
React . useEffect ( ( ) => {
167
169
// use this to handle cases when the table add new columns/remove new columns etc
168
170
$tableRef . current ?. clearSelection ( ) ;
@@ -214,7 +216,6 @@ export const SelectableDataGrid: React.FC<SelectableDataGridProps> = ({ tableId,
214
216
debouncedSearchHandler ( searchText ) ;
215
217
} , [ searchText , debouncedSearchHandler ] ) ;
216
218
217
-
218
219
const handleSelectionFinish = ( selected : any [ ] ) => {
219
220
let newSelectedCells = _ . uniq ( selected . map ( x => x . props . indices ) ) ;
220
221
setSelectedCells ( newSelectedCells ) ;
@@ -288,28 +289,70 @@ export const SelectableDataGrid: React.FC<SelectableDataGridProps> = ({ tableId,
288
289
} ;
289
290
} , [ ] ) ;
290
291
291
- // At the component level, add state for the menu
292
- const [ menuAnchorEl , setMenuAnchorEl ] = React . useState < null | HTMLElement > ( null ) ;
293
- const open = Boolean ( menuAnchorEl ) ;
294
-
295
- const handleMenuClick = ( event : React . MouseEvent < HTMLElement > ) => {
296
- setMenuAnchorEl ( event . currentTarget ) ;
297
- } ;
298
-
299
- const handleMenuClose = ( ) => {
300
- setMenuAnchorEl ( null ) ;
292
+ const fetchSortedVirtualData = ( columnIds : string [ ] , sortOrder : 'asc' | 'desc' ) => {
293
+ // Set loading to true when starting the fetch
294
+ setIsLoading ( true ) ;
295
+
296
+ // Use the SAMPLE_TABLE endpoint with appropriate ordering
297
+ fetch ( getUrls ( ) . SAMPLE_TABLE , {
298
+ method : 'POST' ,
299
+ headers : {
300
+ 'Content-Type' : 'application/json' ,
301
+ } ,
302
+ body : JSON . stringify ( {
303
+ table : tableId ,
304
+ size : 1000 ,
305
+ method : sortOrder === 'asc' ? 'head' : 'bottom' ,
306
+ order_by_fields : columnIds
307
+ } ) ,
308
+ } )
309
+ . then ( response => response . json ( ) )
310
+ . then ( data => {
311
+ if ( data . status === 'success' ) {
312
+ setRowsToDisplay ( data . rows ) ;
313
+ }
314
+ // Set loading to false when done
315
+ setIsLoading ( false ) ;
316
+ } )
317
+ . catch ( error => {
318
+ console . error ( 'Error fetching sorted table data:' , error ) ;
319
+ // Ensure loading is set to false even on error
320
+ setIsLoading ( false ) ;
321
+ } ) ;
301
322
} ;
302
323
303
324
return (
304
325
< Box className = "table-container table-container-small"
305
326
sx = { {
306
327
width : '100%' ,
307
328
height : '100%' ,
329
+ position : 'relative' ,
308
330
"& .MuiTableCell-root" : {
309
331
fontSize : 12 , maxWidth : "120px" , padding : "2px 6px" , cursor : "default" ,
310
332
overflow : "clip" , textOverflow : "ellipsis" , whiteSpace : "nowrap"
311
333
}
312
334
} } >
335
+ { /* Loading Overlay */ }
336
+ { isLoading && (
337
+ < Box sx = { {
338
+ position : 'absolute' ,
339
+ top : 0 ,
340
+ left : 0 ,
341
+ right : 0 ,
342
+ zIndex : 10 ,
343
+ display : 'flex' ,
344
+ alignItems : 'center' ,
345
+ justifyContent : 'center' ,
346
+ backgroundColor : 'rgba(255, 255, 255, 0.7)' ,
347
+ padding : '8px' ,
348
+ height : '100%' ,
349
+ borderTopLeftRadius : '4px' ,
350
+ borderTopRightRadius : '4px'
351
+ } } >
352
+ < CircularProgress size = { 24 } sx = { { mr : 1 } } />
353
+ < Typography variant = "body2" color = "darkgray" > Fetching data...</ Typography >
354
+ </ Box >
355
+ ) }
313
356
{ /* @ts -expect-error */ }
314
357
< SelectableGroup
315
358
ref = { $tableRef }
@@ -373,9 +416,23 @@ export const SelectableDataGrid: React.FC<SelectableDataGridProps> = ({ tableId,
373
416
active = { orderBy === columnDef . id }
374
417
direction = { orderBy === columnDef . id ? order : 'asc' }
375
418
onClick = { ( ) => {
376
- const newOrder = ( orderBy === columnDef . id && order === 'asc' ) ? 'desc' : 'asc' ;
419
+ let newOrder : 'asc' | 'desc' = 'asc' ;
420
+ let newOrderBy : string | undefined = columnDef . id ;
421
+ if ( orderBy === columnDef . id && order === 'asc' ) {
422
+ newOrder = 'desc' ;
423
+ } else if ( orderBy === columnDef . id && order === 'desc' ) {
424
+ newOrder = 'asc' ;
425
+ newOrderBy = undefined ;
426
+ } else {
427
+ newOrder = 'asc' ;
428
+ }
429
+
377
430
setOrder ( newOrder ) ;
378
- setOrderBy ( columnDef . id ) ;
431
+ setOrderBy ( newOrderBy ) ;
432
+
433
+ if ( virtual ) {
434
+ fetchSortedVirtualData ( newOrderBy ? [ newOrderBy ] : [ ] , newOrder ) ;
435
+ }
379
436
} }
380
437
>
381
438
< span role = "img" style = { { fontSize : "inherit" , padding : "2px" , display : "inline-flex" , alignItems : "center" } } >
@@ -447,12 +504,33 @@ export const SelectableDataGrid: React.FC<SelectableDataGridProps> = ({ tableId,
447
504
</ Typography >
448
505
{ virtual && (
449
506
< >
450
- < Tooltip title = "Sample data from this table" >
507
+ < Tooltip title = "view 1000 random rows from this table" >
451
508
< IconButton
452
509
size = "small"
453
510
color = "primary"
454
511
sx = { { marginRight : 1 } }
455
- onClick = { handleMenuClick }
512
+ onClick = { ( ) => {
513
+ fetch ( getUrls ( ) . SAMPLE_TABLE , {
514
+ method : 'POST' ,
515
+ headers : {
516
+ 'Content-Type' : 'application/json' ,
517
+ } ,
518
+ body : JSON . stringify ( {
519
+ table : tableId ,
520
+ size : 1000 ,
521
+ method : 'random'
522
+ } ) ,
523
+ } )
524
+ . then ( response => response . json ( ) )
525
+ . then ( data => {
526
+ if ( data . status === 'success' ) {
527
+ setRowsToDisplay ( data . rows ) ;
528
+ }
529
+ } )
530
+ . catch ( error => {
531
+ console . error ( 'Error sampling table:' , error ) ;
532
+ } ) ;
533
+ } }
456
534
>
457
535
< CasinoIcon sx = { {
458
536
fontSize : 18 ,
@@ -463,78 +541,6 @@ export const SelectableDataGrid: React.FC<SelectableDataGridProps> = ({ tableId,
463
541
} } />
464
542
</ IconButton >
465
543
</ Tooltip >
466
- < Menu
467
- anchorEl = { menuAnchorEl }
468
- open = { open }
469
- onClose = { handleMenuClose }
470
- slotProps = { {
471
- paper : {
472
- elevation : 3 ,
473
- sx : { minWidth : 180 }
474
- }
475
- } }
476
- >
477
- < Typography variant = "subtitle2" sx = { { px : 2 , py : 1 , fontSize : "12px" , color : 'darkgray' } } >
478
- Sample Method
479
- </ Typography >
480
- { [
481
- { label : "Top 1000 Rows" , method : "head" , icon : < ArrowUpwardIcon fontSize = "small" /> } ,
482
- { label : "Random 1000 Rows" , method : "random" , icon : < CasinoIcon fontSize = "small" /> } ,
483
- { label : "Bottom 1000 Rows" , method : "bottom" , icon : < ArrowDownwardIcon fontSize = "small" /> }
484
- ] . map ( ( option ) => (
485
- < MenuItem
486
- key = { option . method }
487
- sx = { {
488
- '& .MuiListItemText-primary' : { fontSize : "12px" } ,
489
- '& .MuiListItemIcon-root' : { minWidth : '24px' } ,
490
- '& .MuiSvgIcon-root' : { fontSize : '16px' }
491
- } }
492
- onClick = { ( ) => {
493
- handleMenuClose ( ) ;
494
-
495
- // Use fetch to get the sample data
496
- fetch ( getUrls ( ) . SAMPLE_TABLE , {
497
- method : 'POST' ,
498
- headers : {
499
- 'Content-Type' : 'application/json' ,
500
- } ,
501
- body : JSON . stringify ( {
502
- table : tableId ,
503
- size : 1000 ,
504
- method : option . method
505
- } ) ,
506
- } )
507
- . then ( response => response . json ( ) )
508
- . then ( data => {
509
- if ( data . status === 'success' ) {
510
- // Update rows state with the new sample
511
- console . log ( "sampled rows" , data . rows ) ;
512
-
513
- // Convert array rows to dictionary rows
514
- // This assumes the order of elements in each row matches the order of columnDefs
515
- const dictRows = data . rows . map ( ( row : any ) => {
516
- const dictRow : any = { } ;
517
- columnDefs . forEach ( ( col , index ) => {
518
- dictRow [ col . id ] = row [ index ] ;
519
- } ) ;
520
- return dictRow ;
521
- } ) ;
522
-
523
- setRowsToDisplay ( dictRows ) ;
524
- }
525
- } )
526
- . catch ( error => {
527
- console . error ( 'Error sampling table:' , error ) ;
528
- } ) ;
529
- } }
530
- >
531
- < ListItemIcon >
532
- { option . icon }
533
- </ ListItemIcon >
534
- < ListItemText > { option . label } </ ListItemText >
535
- </ MenuItem >
536
- ) ) }
537
- </ Menu >
538
544
</ >
539
545
) }
540
546
{ ! virtual && < Tooltip title = { `Download ${ tableName } as CSV` } >
0 commit comments