@@ -3,7 +3,7 @@ import { UsageTable } from "@opencode-ai/console-core/schema/billing.sql.js"
33import { KeyTable } from "@opencode-ai/console-core/schema/key.sql.js"
44import { UserTable } from "@opencode-ai/console-core/schema/user.sql.js"
55import { AuthTable } from "@opencode-ai/console-core/schema/auth.sql.js"
6- import { createAsync , query , useParams } from "@solidjs/router"
6+ import { useParams } from "@solidjs/router"
77import { createEffect , createMemo , onCleanup , Show , For } from "solid-js"
88import { createStore } from "solid-js/store"
99import { withActor } from "~/context/auth.withActor"
@@ -94,8 +94,6 @@ async function getCosts(workspaceID: string, year: number, month: number) {
9494 } , workspaceID )
9595}
9696
97- const queryCosts = query ( getCosts , "costs.get" )
98-
9997const MODEL_COLORS : Record < string , string > = {
10098 "claude-sonnet-4-5" : "#D4745C" ,
10199 "claude-sonnet-4" : "#E8B4A4" ,
@@ -160,45 +158,25 @@ export function GraphSection() {
160158 keyDropdownOpen : false ,
161159 colorScheme : "light" as "light" | "dark" ,
162160 } )
163- const initialData = createAsync ( ( ) => queryCosts ( params . id ! , store . year , store . month ) )
164-
165- createEffect ( ( ) => {
166- if ( typeof window === "undefined" ) return
167-
168- const mediaQuery = window . matchMedia ( "(prefers-color-scheme: dark)" )
169- setStore ( { colorScheme : mediaQuery . matches ? "dark" : "light" } )
170-
171- const handleColorSchemeChange = ( e : MediaQueryListEvent ) => {
172- setStore ( { colorScheme : e . matches ? "dark" : "light" } )
173- }
174-
175- mediaQuery . addEventListener ( "change" , handleColorSchemeChange )
176- onCleanup ( ( ) => mediaQuery . removeEventListener ( "change" , handleColorSchemeChange ) )
177- } )
178-
179161 const onPreviousMonth = async ( ) => {
180162 const month = store . month === 0 ? 11 : store . month - 1
181163 const year = store . month === 0 ? store . year - 1 : store . year
182- const data = await getCosts ( params . id ! , year , month )
183- setStore ( { month, year, data } )
164+ setStore ( { month, year } )
184165 }
185166
186167 const onNextMonth = async ( ) => {
187168 const month = store . month === 11 ? 0 : store . month + 1
188169 const year = store . month === 11 ? store . year + 1 : store . year
189- setStore ( { month, year, data : await getCosts ( params . id ! , year , month ) } )
170+ setStore ( { month, year } )
190171 }
191172
192173 const onSelectModel = ( model : string | null ) => setStore ( { model, modelDropdownOpen : false } )
193174
194175 const onSelectKey = ( keyID : string | null ) => setStore ( { key : keyID , keyDropdownOpen : false } )
195176
196- const getData = createMemo ( ( ) => store . data ?? initialData ( ) )
197-
198177 const getModels = createMemo ( ( ) => {
199- const data = getData ( )
200- if ( ! data ?. usage ) return [ ]
201- return Array . from ( new Set ( data . usage . map ( ( row ) => row . model ) ) ) . sort ( )
178+ if ( ! store . data ?. usage ) return [ ]
179+ return Array . from ( new Set ( store . data . usage . map ( ( row ) => row . model ) ) ) . sort ( )
202180 } )
203181
204182 const getDates = createMemo ( ( ) => {
@@ -221,9 +199,7 @@ export function GraphSection() {
221199 const isCurrentMonth = ( ) => store . year === now . getFullYear ( ) && store . month === now . getMonth ( )
222200
223201 const chartConfig = createMemo ( ( ) : ChartConfiguration | null => {
224- if ( typeof window === "undefined" ) return null
225-
226- const data = getData ( )
202+ const data = store . data
227203 const dates = getDates ( )
228204 if ( ! data ?. usage ?. length ) return null
229205
@@ -365,15 +341,32 @@ export function GraphSection() {
365341 }
366342 } )
367343
344+ createEffect ( async ( ) => {
345+ const data = await getCosts ( params . id ! , store . year , store . month )
346+ setStore ( { data } )
347+ } )
348+
368349 createEffect ( ( ) => {
369350 const config = chartConfig ( )
370351 if ( ! config || ! canvasRef ) return
371352
372353 if ( chartInstance ) chartInstance . destroy ( )
373354 chartInstance = new Chart ( canvasRef , config )
355+
356+ onCleanup ( ( ) => chartInstance ?. destroy ( ) )
374357 } )
375358
376- onCleanup ( ( ) => chartInstance ?. destroy ( ) )
359+ createEffect ( ( ) => {
360+ const mediaQuery = window . matchMedia ( "(prefers-color-scheme: dark)" )
361+ setStore ( { colorScheme : mediaQuery . matches ? "dark" : "light" } )
362+
363+ const handleColorSchemeChange = ( e : MediaQueryListEvent ) => {
364+ setStore ( { colorScheme : e . matches ? "dark" : "light" } )
365+ }
366+
367+ mediaQuery . addEventListener ( "change" , handleColorSchemeChange )
368+ onCleanup ( ( ) => mediaQuery . removeEventListener ( "change" , handleColorSchemeChange ) )
369+ } )
377370
378371 return (
379372 < section class = { styles . root } >
@@ -382,55 +375,53 @@ export function GraphSection() {
382375 < p > Usage costs broken down by model.</ p >
383376 </ div >
384377
385- < Show when = { getData ( ) } >
386- < div data-slot = "filter-container" >
387- < div data-slot = "month-picker" >
388- < button data-slot = "month-button" onClick = { onPreviousMonth } >
389- < IconChevronLeft />
378+ < div data-slot = "filter-container" >
379+ < div data-slot = "month-picker" >
380+ < button data-slot = "month-button" onClick = { onPreviousMonth } >
381+ < IconChevronLeft />
382+ </ button >
383+ < span data-slot = "month-label" > { formatMonthYear ( ) } </ span >
384+ < button data-slot = "month-button" onClick = { onNextMonth } disabled = { isCurrentMonth ( ) } >
385+ < IconChevronRight />
386+ </ button >
387+ </ div >
388+ < Dropdown
389+ trigger = { store . model === null ? "All Models" : store . model }
390+ open = { store . modelDropdownOpen }
391+ onOpenChange = { ( open ) => setStore ( { modelDropdownOpen : open } ) }
392+ >
393+ < >
394+ < button data-slot = "model-item" onClick = { ( ) => onSelectModel ( null ) } >
395+ < span > All Models</ span >
390396 </ button >
391- < span data-slot = "month-label" > { formatMonthYear ( ) } </ span >
392- < button data-slot = "month-button" onClick = { onNextMonth } disabled = { isCurrentMonth ( ) } >
393- < IconChevronRight />
397+ < For each = { getModels ( ) } >
398+ { ( model ) => (
399+ < button data-slot = "model-item" onClick = { ( ) => onSelectModel ( model ) } >
400+ < span > { model } </ span >
401+ </ button >
402+ ) }
403+ </ For >
404+ </ >
405+ </ Dropdown >
406+ < Dropdown
407+ trigger = { getKeyName ( store . key ) }
408+ open = { store . keyDropdownOpen }
409+ onOpenChange = { ( open ) => setStore ( { keyDropdownOpen : open } ) }
410+ >
411+ < >
412+ < button data-slot = "model-item" onClick = { ( ) => onSelectKey ( null ) } >
413+ < span > All Keys</ span >
394414 </ button >
395- </ div >
396- < Dropdown
397- trigger = { store . model === null ? "All Models" : store . model }
398- open = { store . modelDropdownOpen }
399- onOpenChange = { ( open ) => setStore ( { modelDropdownOpen : open } ) }
400- >
401- < >
402- < button data-slot = "model-item" onClick = { ( ) => onSelectModel ( null ) } >
403- < span > All Models</ span >
404- </ button >
405- < For each = { getModels ( ) } >
406- { ( model ) => (
407- < button data-slot = "model-item" onClick = { ( ) => onSelectModel ( model ) } >
408- < span > { model } </ span >
409- </ button >
410- ) }
411- </ For >
412- </ >
413- </ Dropdown >
414- < Dropdown
415- trigger = { getKeyName ( store . key ) }
416- open = { store . keyDropdownOpen }
417- onOpenChange = { ( open ) => setStore ( { keyDropdownOpen : open } ) }
418- >
419- < >
420- < button data-slot = "model-item" onClick = { ( ) => onSelectKey ( null ) } >
421- < span > All Keys</ span >
422- </ button >
423- < For each = { getData ( ) ?. keys || [ ] } >
424- { ( key ) => (
425- < button data-slot = "model-item" onClick = { ( ) => onSelectKey ( key . id ) } >
426- < span > { key . displayName } </ span >
427- </ button >
428- ) }
429- </ For >
430- </ >
431- </ Dropdown >
432- </ div >
433- </ Show >
415+ < For each = { store . data ?. keys || [ ] } >
416+ { ( key ) => (
417+ < button data-slot = "model-item" onClick = { ( ) => onSelectKey ( key . id ) } >
418+ < span > { key . displayName } </ span >
419+ </ button >
420+ ) }
421+ </ For >
422+ </ >
423+ </ Dropdown >
424+ </ div >
434425
435426 < Show
436427 when = { chartConfig ( ) }
0 commit comments