1
1
import { clsx as cx } from 'clsx'
2
- import { Show , createMemo , createSignal , onMount } from 'solid-js'
2
+ import { Show , createMemo , createSignal , onMount , For } from 'solid-js'
3
3
import { useDevtoolsOnClose } from './contexts'
4
4
import { useStyles } from './useStyles'
5
5
import { useLocalStorage } from './useLocalStorage'
@@ -56,7 +56,7 @@ function CollectionItem({
56
56
onSelect,
57
57
} : {
58
58
collection : CollectionMetadata
59
- isActive : boolean
59
+ isActive : Accessor < boolean >
60
60
onSelect : ( collection : CollectionMetadata ) => void
61
61
} ) {
62
62
const styles = useStyles ( )
@@ -65,7 +65,7 @@ function CollectionItem({
65
65
< div
66
66
class = { cx (
67
67
styles ( ) . collectionItem ,
68
- isActive ? styles ( ) . collectionItemActive : ''
68
+ isActive ( ) ? styles ( ) . collectionItemActive : ''
69
69
) }
70
70
onClick = { ( ) => onSelect ( collection ) }
71
71
>
@@ -102,27 +102,68 @@ export const BaseTanStackDbDevtoolsPanel = function BaseTanStackDbDevtoolsPanel(
102
102
'' ,
103
103
)
104
104
105
+ const [ selectedView , setSelectedView ] = createSignal < 'collections' | 'transactions' > ( 'collections' )
106
+ const [ selectedTransaction , setSelectedTransaction ] = createSignal < string | null > ( null )
107
+
105
108
const [ collections , setCollections ] = createSignal < CollectionMetadata [ ] > ( [ ] )
106
109
107
110
// Poll for collections data
108
111
onMount ( ( ) => {
109
112
const updateCollections = ( ) => {
110
113
if ( typeof window === 'undefined' ) return
111
-
112
114
try {
113
- const metadata = registry ( ) . getAllCollectionMetadata ( )
114
- setCollections ( metadata )
115
+ const collections = registry ( ) . getAllCollectionMetadata ( )
116
+ setCollections ( collections )
115
117
} catch ( error ) {
116
- console . warn ( 'Error fetching collections metadata:' , error )
118
+ // Silently handle errors when fetching collections metadata
117
119
}
118
120
}
119
-
120
121
updateCollections ( )
121
122
const intervalId = setInterval ( updateCollections , POLLING_INTERVAL_MS )
122
-
123
123
return ( ) => clearInterval ( intervalId )
124
124
} )
125
125
126
+ // Helper function to detect if collections data actually changed
127
+ const hasCollectionsChanged = ( oldCollections : CollectionMetadata [ ] , newCollections : CollectionMetadata [ ] ) : boolean => {
128
+ if ( oldCollections . length !== newCollections . length ) return true
129
+
130
+ // Create maps for O(1) lookup by ID
131
+ const oldMap = new Map ( oldCollections . map ( c => [ c . id , c ] ) )
132
+ const newMap = new Map ( newCollections . map ( c => [ c . id , c ] ) )
133
+
134
+ // Check if any collection data changed by comparing by ID
135
+ for ( const [ id , old ] of oldMap ) {
136
+ const new_ = newMap . get ( id )
137
+ if ( ! new_ ) return true // Collection was removed
138
+
139
+ if ( old . status !== new_ . status ) return true
140
+ if ( old . size !== new_ . size ) return true
141
+ if ( old . hasTransactions !== new_ . hasTransactions ) return true
142
+ if ( old . transactionCount !== new_ . transactionCount ) return true
143
+ }
144
+
145
+ // Check if any new collections were added
146
+ for ( const [ id ] of newMap ) {
147
+ if ( ! oldMap . has ( id ) ) return true
148
+ }
149
+
150
+ return false
151
+ }
152
+
153
+ // --- Fix: Ensure selection is always valid and highlight updates instantly ---
154
+ // If the selected collection disappears, auto-select the first available one
155
+ createMemo ( ( ) => {
156
+ const ids = collections ( ) . map ( c => c . id )
157
+ if ( ids . length === 0 ) {
158
+ setActiveCollectionId ( '' ) // always a string
159
+ return
160
+ }
161
+ if ( ! ids . includes ( activeCollectionId ( ) ) ) {
162
+ setActiveCollectionId ( ids [ 0 ] ?? '' )
163
+ }
164
+ } )
165
+ // --- End fix ---
166
+
126
167
const activeCollection = createMemo ( ( ) => {
127
168
const active = collections ( ) . find ( c => c . id === activeCollectionId ( ) )
128
169
return active || collections ( ) [ 0 ]
@@ -138,24 +179,7 @@ export const BaseTanStackDbDevtoolsPanel = function BaseTanStackDbDevtoolsPanel(
138
179
)
139
180
} )
140
181
141
- const collectionDetails = createMemo ( ( ) => {
142
- const active = activeCollection ( )
143
- if ( ! active ) return null
144
-
145
- try {
146
- const collection = registry ( ) . getCollection ( active . id )
147
- const metadata = registry ( ) . getCollectionMetadata ( active . id )
148
-
149
- return {
150
- collection,
151
- metadata,
152
- transactions : registry ( ) . getTransactions ( active . id ) ,
153
- }
154
- } catch ( error ) {
155
- console . warn ( 'Error getting collection details:' , error )
156
- return null
157
- }
158
- } )
182
+
159
183
160
184
return (
161
185
< div
@@ -203,29 +227,98 @@ export const BaseTanStackDbDevtoolsPanel = function BaseTanStackDbDevtoolsPanel(
203
227
< Logo />
204
228
</ div >
205
229
< div class = { styles ( ) . collectionsExplorerContainer } >
206
- < div class = { styles ( ) . collectionsExplorer } >
207
- < div class = { styles ( ) . collectionsHeader } >
208
- < div > Collections ({ collections ( ) . length } )</ div >
209
- </ div >
210
-
211
- < div class = { styles ( ) . collectionsList } >
212
- < Show
213
- when = { sortedCollections ( ) . length > 0 }
214
- fallback = {
215
- < div style = { { padding : '16px' , color : '#666' } } >
216
- No collections found
217
- </ div >
218
- }
219
- >
220
- { sortedCollections ( ) . map ( ( collection ) => (
221
- < CollectionItem
222
- collection = { collection }
223
- isActive = { collection . id === activeCollectionId ( ) }
224
- onSelect = { ( c ) => setActiveCollectionId ( c . id ) }
225
- />
226
- ) ) }
227
- </ Show >
228
- </ div >
230
+ { /* Tab Navigation */ }
231
+ < div class = { styles ( ) . tabNav } >
232
+ < button
233
+ onClick = { ( ) => setSelectedView ( 'collections' ) }
234
+ class = { cx (
235
+ styles ( ) . tabBtn ,
236
+ selectedView ( ) === 'collections' && styles ( ) . tabBtnActive
237
+ ) }
238
+ >
239
+ Collections
240
+ </ button >
241
+ < button
242
+ onClick = { ( ) => setSelectedView ( 'transactions' ) }
243
+ class = { cx (
244
+ styles ( ) . tabBtn ,
245
+ selectedView ( ) === 'transactions' && styles ( ) . tabBtnActive
246
+ ) }
247
+ >
248
+ Transactions ({ registry ( ) . getTransactions ( ) . length } )
249
+ </ button >
250
+ </ div >
251
+
252
+ { /* Content based on selected view */ }
253
+ < div class = { styles ( ) . sidebarContent } >
254
+ < Show when = { selectedView ( ) === 'collections' } >
255
+ < div class = { styles ( ) . collectionsExplorer } >
256
+ < div class = { styles ( ) . collectionsHeader } >
257
+ < div > Collections ({ collections ( ) . length } )</ div >
258
+ </ div >
259
+
260
+ < div class = { styles ( ) . collectionsList } >
261
+ < Show
262
+ when = { sortedCollections ( ) . length > 0 }
263
+ fallback = {
264
+ < div style = { { padding : '16px' , color : '#666' } } >
265
+ No collections found
266
+ </ div >
267
+ }
268
+ >
269
+ < For each = { sortedCollections ( ) } > { ( collection ) =>
270
+ < CollectionItem
271
+ collection = { collection }
272
+ isActive = { ( ) => collection . id === activeCollectionId ( ) }
273
+ onSelect = { ( c ) => setActiveCollectionId ( c . id ) }
274
+ />
275
+ } </ For >
276
+ </ Show >
277
+ </ div >
278
+ </ div >
279
+ </ Show >
280
+
281
+ < Show when = { selectedView ( ) === 'transactions' } >
282
+ < div class = { styles ( ) . transactionsExplorer } >
283
+ < div class = { styles ( ) . collectionsHeader } >
284
+ < div > Transactions ({ registry ( ) . getTransactions ( ) . length } )</ div >
285
+ </ div >
286
+
287
+ < div class = { styles ( ) . collectionsList } >
288
+ < Show
289
+ when = { registry ( ) . getTransactions ( ) . length > 0 }
290
+ fallback = {
291
+ < div style = { { padding : '16px' , color : '#666' } } >
292
+ No transactions found
293
+ </ div >
294
+ }
295
+ >
296
+ { registry ( ) . getTransactions ( ) . map ( ( transaction ) => (
297
+ < div
298
+ class = { cx (
299
+ styles ( ) . collectionItem ,
300
+ selectedTransaction ( ) === transaction . id && styles ( ) . collectionItemActive
301
+ ) }
302
+ onClick = { ( ) => setSelectedTransaction ( transaction . id ) }
303
+ >
304
+ < div class = { styles ( ) . collectionName } >
305
+ Transaction { transaction . id . slice ( 0 , 8 ) } ...
306
+ </ div >
307
+ < div class = { styles ( ) . collectionCount } >
308
+ { transaction . mutations . length } mutations
309
+ </ div >
310
+ < div class = { cx (
311
+ styles ( ) . collectionStatus ,
312
+ transaction . state === 'error' ? styles ( ) . collectionStatusError : ''
313
+ ) } >
314
+ { transaction . state }
315
+ </ div >
316
+ </ div >
317
+ ) ) }
318
+ </ Show >
319
+ </ div >
320
+ </ div >
321
+ </ Show >
229
322
</ div >
230
323
</ div >
231
324
</ div >
0 commit comments