Skip to content

Commit ffb9cfc

Browse files
committed
more
1 parent b852821 commit ffb9cfc

File tree

6 files changed

+361
-35
lines changed

6 files changed

+361
-35
lines changed

packages/db-devtools/src/BaseTanStackDbDevtoolsPanel.tsx

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,12 @@ import { useDevtoolsOnClose } from "./contexts"
44
import { useStyles } from "./useStyles"
55
import { useLocalStorage } from "./useLocalStorage"
66
import {
7+
CollectionDetailsPanel,
78
CollectionsPanel,
9+
GenericDetailsPanel,
810
Logo,
911
TabNavigation,
1012
TransactionsPanel,
11-
UnifiedDetailsPanel,
1213
} from "./components"
1314
import type { Accessor, JSX } from "solid-js"
1415
import type {
@@ -193,11 +194,16 @@ export const BaseTanStackDbDevtoolsPanel =
193194
</div>
194195

195196
<div class={styles().secondContainer}>
196-
<UnifiedDetailsPanel
197-
selectedView={selectedView}
198-
activeCollection={activeCollection}
199-
activeTransaction={activeTransaction}
200-
/>
197+
<Show when={selectedView() === `collections`}>
198+
<CollectionDetailsPanel activeCollection={activeCollection} />
199+
</Show>
200+
<Show when={selectedView() === `transactions`}>
201+
<GenericDetailsPanel
202+
selectedView={selectedView}
203+
activeCollection={activeCollection}
204+
activeTransaction={activeTransaction}
205+
/>
206+
</Show>
201207
</div>
202208
</div>
203209
)
Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
import { Show, createMemo, createSignal } from "solid-js"
2+
import { clsx as cx } from "clsx"
3+
import { useStyles } from "../useStyles"
4+
import { getDevtoolsRegistry } from "../devtools"
5+
import { Explorer } from "./Explorer"
6+
import { TransactionsPanel } from "./TransactionsPanel"
7+
import { GenericDetailsPanel } from "./DetailsPanel"
8+
import type { CollectionMetadata } from "../types"
9+
import type { Accessor } from "solid-js"
10+
11+
export interface CollectionDetailsPanelProps {
12+
activeCollection: Accessor<CollectionMetadata | undefined>
13+
}
14+
15+
type CollectionTab = `summary` | `config` | `state` | `transactions` | `data`
16+
17+
export function CollectionDetailsPanel({
18+
activeCollection,
19+
}: CollectionDetailsPanelProps) {
20+
const styles = useStyles()
21+
const [selectedTab, setSelectedTab] = createSignal<CollectionTab>(`summary`)
22+
const [selectedTransaction, setSelectedTransaction] = createSignal<
23+
string | null
24+
>(null)
25+
const registry = getDevtoolsRegistry()
26+
27+
const collection = createMemo(() => {
28+
const metadata = activeCollection()
29+
if (!metadata || !registry) return null
30+
31+
// Get the actual collection instance
32+
const collectionInstance = registry.getCollection(metadata.id)
33+
return { metadata, instance: collectionInstance }
34+
})
35+
36+
const collectionTransactions = createMemo(() => {
37+
const metadata = activeCollection()
38+
if (!metadata || !registry) return []
39+
40+
return registry.getTransactions(metadata.id)
41+
})
42+
43+
const activeTransaction = createMemo(() => {
44+
const transactions = collectionTransactions()
45+
const selectedId = selectedTransaction()
46+
return transactions.find((t) => t.id === selectedId)
47+
})
48+
49+
const tabs: Array<{ id: CollectionTab; label: string }> = [
50+
{ id: `summary`, label: `Summary` },
51+
{ id: `config`, label: `Config` },
52+
{ id: `state`, label: `State` },
53+
{ id: `transactions`, label: `Transactions` },
54+
{ id: `data`, label: `Data` },
55+
]
56+
57+
const renderTabContent = () => {
58+
const currentCollection = collection()
59+
if (!currentCollection) return null
60+
61+
const { metadata, instance } = currentCollection
62+
63+
switch (selectedTab()) {
64+
case `summary`: {
65+
return (
66+
<Explorer
67+
label="Collection Metadata"
68+
value={() => metadata}
69+
defaultExpanded={{}}
70+
/>
71+
)
72+
}
73+
74+
case `config`: {
75+
return instance ? (
76+
<Explorer
77+
label="Collection Config"
78+
value={() => instance.config}
79+
defaultExpanded={{}}
80+
/>
81+
) : (
82+
<div class={styles().noDataMessage}>
83+
Collection instance not available
84+
</div>
85+
)
86+
}
87+
88+
case `state`: {
89+
if (!instance) {
90+
return (
91+
<div class={styles().noDataMessage}>
92+
Collection instance not available
93+
</div>
94+
)
95+
}
96+
97+
const stateData = {
98+
syncedData: instance.syncedData,
99+
optimisticUpserts: instance.optimisticUpserts,
100+
optimisticDeletes: instance.optimisticDeletes,
101+
}
102+
103+
return (
104+
<Explorer
105+
label="Collection State"
106+
value={() => stateData}
107+
defaultExpanded={{}}
108+
/>
109+
)
110+
}
111+
112+
case `transactions`: {
113+
const transactions = collectionTransactions()
114+
return (
115+
<div class={styles().splitPanelContainer}>
116+
<div class={styles().splitPanelLeft}>
117+
<TransactionsPanel
118+
transactions={() => transactions}
119+
selectedTransaction={selectedTransaction}
120+
onSelectTransaction={setSelectedTransaction}
121+
/>
122+
</div>
123+
<div class={styles().splitPanelRight}>
124+
<GenericDetailsPanel
125+
selectedView={() => `transactions` as const}
126+
activeCollection={() => undefined}
127+
activeTransaction={activeTransaction}
128+
isSubPanel={true}
129+
/>
130+
</div>
131+
</div>
132+
)
133+
}
134+
135+
case `data`: {
136+
return (
137+
<div class={styles().noDataMessage}>Grid view coming soon...</div>
138+
)
139+
}
140+
141+
default:
142+
return null
143+
}
144+
}
145+
146+
return (
147+
<Show
148+
when={activeCollection()}
149+
fallback={
150+
<div class={styles().detailsPanel}>
151+
<div class={styles().detailsHeader}>
152+
Select a collection to view details
153+
</div>
154+
</div>
155+
}
156+
>
157+
{(collectionMetadata) => (
158+
<div class={styles().detailsPanel}>
159+
<div class={styles().detailsHeaderRow}>
160+
<div class={styles().detailsHeader}>{collectionMetadata().id}</div>
161+
<div class={styles().collectionTabNav}>
162+
{tabs.map((tab) => (
163+
<button
164+
onClick={() => setSelectedTab(tab.id)}
165+
class={cx(
166+
styles().collectionTabBtn,
167+
selectedTab() === tab.id && styles().collectionTabBtnActive
168+
)}
169+
>
170+
{tab.label}
171+
{tab.id === `transactions` &&
172+
collectionTransactions().length > 0 && (
173+
<span class={styles().tabBadge}>
174+
{collectionTransactions().length}
175+
</span>
176+
)}
177+
</button>
178+
))}
179+
</div>
180+
</div>
181+
182+
{/* Tab Content */}
183+
<div
184+
class={
185+
selectedTab() === `transactions`
186+
? styles().detailsContentNoPadding
187+
: styles().detailsContent
188+
}
189+
>
190+
{renderTabContent()}
191+
</div>
192+
</div>
193+
)}
194+
</Show>
195+
)
196+
}

packages/db-devtools/src/components/DetailsPanel.tsx

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ export interface DetailsPanelProps {
88
selectedView: Accessor<`collections` | `transactions`>
99
activeCollection: Accessor<CollectionMetadata | undefined>
1010
activeTransaction: Accessor<TransactionDetails | undefined>
11+
isSubPanel?: boolean
1112
}
1213

1314
export function DetailsPanel({
@@ -83,10 +84,11 @@ export function TransactionDetailsPanel({
8384
)
8485
}
8586

86-
export function UnifiedDetailsPanel({
87+
export function GenericDetailsPanel({
8788
selectedView,
8889
activeCollection,
8990
activeTransaction,
91+
isSubPanel = false,
9092
}: DetailsPanelProps) {
9193
const styles = useStyles()
9294

@@ -127,15 +129,27 @@ export function UnifiedDetailsPanel({
127129
when={activeTransaction()}
128130
fallback={
129131
<div class={styles().detailsPanel}>
130-
<div class={styles().detailsHeader}>
132+
<div
133+
class={
134+
isSubPanel
135+
? styles().transactionSubHeader
136+
: styles().transactionHeader
137+
}
138+
>
131139
Select a transaction to view details
132140
</div>
133141
</div>
134142
}
135143
>
136144
{(transaction) => (
137145
<div class={styles().detailsPanel}>
138-
<div class={styles().detailsHeader}>
146+
<div
147+
class={
148+
isSubPanel
149+
? styles().transactionSubHeader
150+
: styles().transactionHeader
151+
}
152+
>
139153
Transaction {transaction().id}
140154
</div>
141155
<div class={styles().detailsContent}>

packages/db-devtools/src/components/Explorer.tsx

Lines changed: 26 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,9 @@ function displayValue(value: unknown): string {
9696
if (typeof value === `number`) return value.toString()
9797
if (typeof value === `boolean`) return value.toString()
9898
if (typeof value === `function`) return `function`
99+
if (value instanceof Date) return `Date('${value.toISOString()}')`
100+
if (value instanceof Map) return `Map(${value.size})`
101+
if (value instanceof Set) return `Set(${value.size})`
99102
if (Array.isArray(value)) return `Array(${value.length})`
100103
if (typeof value === `object`) return `Object`
101104
return String(value)
@@ -135,6 +138,23 @@ export function Explorer({
135138
value: d,
136139
})
137140
)
141+
} else if (value() instanceof Map) {
142+
// Map
143+
entries = Array.from((value() as Map<unknown, unknown>).entries()).map(
144+
([key, val]) =>
145+
makeProperty({
146+
label: String(key),
147+
value: val,
148+
})
149+
)
150+
} else if (value() instanceof Set) {
151+
// Set
152+
entries = Array.from(value() as Set<unknown>, (val, i) =>
153+
makeProperty({
154+
label: i.toString(),
155+
value: val,
156+
})
157+
)
138158
} else if (
139159
value() !== null &&
140160
typeof value() === `object` &&
@@ -164,12 +184,11 @@ export function Explorer({
164184
const subEntryPages = createMemo(() => chunkArray(subEntries(), pageSize))
165185

166186
const [expandedPages, setExpandedPages] = createSignal<Array<number>>([])
167-
const [valueSnapshot, setValueSnapshot] = createSignal(undefined)
168187
const styles = useStyles()
169188

170-
const refreshValueSnapshot = () => {
171-
setValueSnapshot((value() as () => any)())
172-
}
189+
// const refreshValueSnapshot = () => {
190+
// setValueSnapshot((value() as () => any)())
191+
// }
173192

174193
const handleEntry = (entry: Entry) => (
175194
<Explorer
@@ -237,18 +256,9 @@ export function Explorer({
237256
</>
238257
) : type() === `function` ? (
239258
<>
240-
<Explorer
241-
label={
242-
<button
243-
onClick={refreshValueSnapshot}
244-
class={styles().refreshValueBtn}
245-
>
246-
<span>{rest.label}</span> 🔄{` `}
247-
</button>
248-
}
249-
value={valueSnapshot}
250-
defaultExpanded={{}}
251-
/>
259+
<span>{rest.label}:</span>
260+
{` `}
261+
<span class={styles().value}>{displayValue(value())}</span>
252262
</>
253263
) : (
254264
<>
Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,11 @@
1+
export { CollectionsPanel } from "./CollectionsPanel"
2+
export { DetailsPanel, GenericDetailsPanel } from "./DetailsPanel"
3+
export { Explorer } from "./Explorer"
14
export { Logo } from "./Logo"
2-
export { CollectionStats } from "./CollectionStats"
3-
export { TransactionStats } from "./TransactionStats"
4-
export { CollectionItem } from "./CollectionItem"
5+
export { TabNavigation } from "./TabNavigation"
56
export { TransactionItem } from "./TransactionItem"
6-
export { CollectionsPanel } from "./CollectionsPanel"
7+
export { TransactionStats } from "./TransactionStats"
78
export { TransactionsPanel } from "./TransactionsPanel"
8-
export {
9-
DetailsPanel,
10-
TransactionDetailsPanel,
11-
UnifiedDetailsPanel,
12-
} from "./DetailsPanel"
13-
export { TabNavigation } from "./TabNavigation"
9+
export { CollectionItem } from "./CollectionItem"
10+
export { CollectionStats } from "./CollectionStats"
11+
export { CollectionDetailsPanel } from "./CollectionDetailsPanel"

0 commit comments

Comments
 (0)