@@ -31,23 +31,40 @@ const getCardTotalPrice = ( card ) => {
3131 return ( Number . isFinite ( unitPrice ) ? unitPrice : 0 ) * ( Number . isFinite ( quantity ) ? quantity : 0 ) ;
3232} ;
3333
34- const TableView = ( { aggregatedData = [ ] , onDeleteCard, onUpdateCard, sortConfig, handleSortChange } ) => {
34+ const TableView = ( { aggregatedData = [ ] , onDeleteCard, onUpdateCard, sortConfig, handleSortChange, selectedCardIds , onSelectedCardIdsChange , folderNameMap = { } } ) => {
3535 const safeCards = Array . isArray ( aggregatedData ) ? aggregatedData : [ ] ;
3636 const isExternallySorted = Boolean ( sortConfig && typeof handleSortChange === 'function' ) ;
3737 const [ internalSortConfig , setInternalSortConfig ] = useState ( { key : DEFAULT_SORT_KEY , direction : 'ascending' } ) ;
3838 const activeSortConfig = isExternallySorted ? sortConfig : internalSortConfig ;
3939 const [ edit , setEdit ] = useState ( { } ) ;
4040 const [ editValues , setEditValues ] = useState ( { } ) ;
4141 const [ notification , setNotification ] = useState ( { show : false , message : '' } ) ;
42- const [ selectedRows , setSelectedRows ] = useState ( ( ) => new Set ( ) ) ;
42+ const [ internalSelectedRows , setInternalSelectedRows ] = useState ( ( ) => new Set ( ) ) ;
4343 const [ lastCheckedId , setLastCheckedId ] = useState ( null ) ;
44+ const isSelectionControlled = selectedCardIds instanceof Set && typeof onSelectedCardIdsChange === 'function' ;
45+ const selectedRows = isSelectionControlled ? selectedCardIds : internalSelectedRows ;
46+ const updateSelectedRows = useCallback (
47+ ( updater ) => {
48+ if ( isSelectionControlled ) {
49+ onSelectedCardIdsChange ( updater ) ;
50+ return ;
51+ }
52+
53+ setInternalSelectedRows ( updater ) ;
54+ } ,
55+ [ isSelectionControlled , onSelectedCardIdsChange ] ,
56+ ) ;
4457 const safeIds = useMemo (
4558 ( ) => safeCards . map ( ( card ) => card ?. _id ) . filter ( Boolean ) ,
4659 [ safeCards ] ,
4760 ) ;
4861
4962 useEffect ( ( ) => {
50- setSelectedRows ( ( prev ) => {
63+ if ( isSelectionControlled ) {
64+ return ;
65+ }
66+
67+ setInternalSelectedRows ( ( prev ) => {
5168 const validIdSet = new Set ( safeIds ) ;
5269 let changed = false ;
5370 const next = new Set ( ) ;
@@ -63,7 +80,7 @@ const TableView = ( { aggregatedData = [], onDeleteCard, onUpdateCard, sortConfi
6380 }
6481 return next ;
6582 } ) ;
66- } , [ safeIds ] ) ;
83+ } , [ isSelectionControlled , safeIds ] ) ;
6784
6885 const showNotification = useCallback ( ( message ) => {
6986 setNotification ( { show : true , message } ) ;
@@ -197,7 +214,7 @@ const TableView = ( { aggregatedData = [], onDeleteCard, onUpdateCard, sortConfi
197214 ( event , cardId ) => {
198215 const { checked } = event . target ;
199216 const isShift = event . nativeEvent ?. shiftKey ;
200- setSelectedRows ( ( prev ) => {
217+ updateSelectedRows ( ( prev ) => {
201218 const next = new Set ( prev ) ;
202219 if ( isShift && lastCheckedId && lastCheckedId !== cardId ) {
203220 const start = displayedRowIds . indexOf ( lastCheckedId ) ;
@@ -219,12 +236,12 @@ const TableView = ( { aggregatedData = [], onDeleteCard, onUpdateCard, sortConfi
219236 } ) ;
220237 setLastCheckedId ( cardId ) ;
221238 } ,
222- [ displayedRowIds , lastCheckedId ] ,
239+ [ displayedRowIds , lastCheckedId , updateSelectedRows ] ,
223240 ) ;
224241
225242 const applySelectionToDisplayed = useCallback (
226243 ( shouldSelect ) => {
227- setSelectedRows ( ( prev ) => {
244+ updateSelectedRows ( ( prev ) => {
228245 const next = new Set ( prev ) ;
229246 displayedRowIds . forEach ( ( id ) => {
230247 if ( shouldSelect ) next . add ( id ) ;
@@ -233,7 +250,7 @@ const TableView = ( { aggregatedData = [], onDeleteCard, onUpdateCard, sortConfi
233250 return next ;
234251 } ) ;
235252 } ,
236- [ displayedRowIds ] ,
253+ [ displayedRowIds , updateSelectedRows ] ,
237254 ) ;
238255
239256 const handleHeaderSelectAll = useCallback (
@@ -250,9 +267,9 @@ const TableView = ( { aggregatedData = [], onDeleteCard, onUpdateCard, sortConfi
250267 } , [ applySelectionToDisplayed , isAllSelected ] ) ;
251268
252269 const clearSelection = useCallback ( ( ) => {
253- setSelectedRows ( new Set ( ) ) ;
270+ updateSelectedRows ( ( ) => new Set ( ) ) ;
254271 setLastCheckedId ( null ) ;
255- } , [ ] ) ;
272+ } , [ updateSelectedRows ] ) ;
256273
257274 const handleBulkDelete = useCallback ( async ( ) => {
258275 const selectedIds = Array . from ( selectedRows ) ;
@@ -278,6 +295,13 @@ const TableView = ( { aggregatedData = [], onDeleteCard, onUpdateCard, sortConfi
278295
279296 const selectedCount = selectedRows . size ;
280297
298+ const getFolderLabels = useCallback (
299+ ( card ) => ( Array . isArray ( card ?. folderIds ) ? card . folderIds : [ ] )
300+ . map ( ( folderId ) => folderNameMap [ String ( folderId ) ] )
301+ . filter ( Boolean ) ,
302+ [ folderNameMap ] ,
303+ ) ;
304+
281305 return (
282306 < div className = "max-w-full overflow-x-auto" >
283307 < Notification
@@ -346,6 +370,9 @@ const TableView = ( { aggregatedData = [], onDeleteCard, onUpdateCard, sortConfi
346370 < th onClick = { ( ) => handleSort ( 'condition' ) } className = "cursor-pointer border border-white/10 px-3 py-2 text-center font-semibold uppercase tracking-wide" >
347371 Condition { getSortArrow ( 'condition' ) }
348372 </ th >
373+ < th className = "border border-white/10 px-3 py-2 text-center font-semibold uppercase tracking-wide" >
374+ Folders
375+ </ th >
349376 < th onClick = { ( ) => handleSort ( 'marketPrice' ) } className = "cursor-pointer border border-white/10 px-3 py-2 text-center font-semibold uppercase tracking-wide" >
350377 Unit Price { getSortArrow ( 'marketPrice' ) }
351378 </ th >
@@ -443,6 +470,17 @@ const TableView = ( { aggregatedData = [], onDeleteCard, onUpdateCard, sortConfi
443470 < td className = "whitespace-nowrap px-3 py-2 text-center sm:text-left" > { card ?. printing } </ td >
444471 < td className = "whitespace-nowrap px-3 py-2 text-center sm:text-left" > { card ?. rarity } </ td >
445472 < td className = "whitespace-nowrap px-3 py-2 text-center sm:text-left" > { card ?. condition } </ td >
473+ < td className = "min-w-40 px-3 py-2 text-center sm:text-left" >
474+ < div className = "flex flex-wrap justify-center gap-1 sm:justify-start" >
475+ { getFolderLabels ( card ) . length > 0 ? getFolderLabels ( card ) . map ( ( label ) => (
476+ < span key = { label } className = "rounded-full border border-indigo-300/30 bg-indigo-500/15 px-2 py-0.5 text-[0.68rem] font-semibold uppercase tracking-wide text-indigo-50" >
477+ { label }
478+ </ span >
479+ ) ) : (
480+ < span className = "text-white/35" > -</ span >
481+ ) }
482+ </ div >
483+ </ td >
446484 < td className = "whitespace-nowrap px-3 py-2 text-center sm:text-left" > { Number . isFinite ( Number ( card ?. marketPrice ) ) ? Number ( card . marketPrice ) . toFixed ( 2 ) : card ?. marketPrice ?? '0.00' } </ td >
447485 < td className = "whitespace-nowrap px-3 py-2 text-center sm:text-left font-semibold" > { getCardTotalPrice ( card ) . toFixed ( 2 ) } </ td >
448486 < td className = "flex items-center gap-2 px-3 py-2" >
0 commit comments