@@ -35,7 +35,8 @@ import { cn } from '@/lib/utils'
3535import type { Session } from 'next-auth'
3636import { create } from 'zustand'
3737
38- interface AgentData {
38+ // Basic agent info from SSR (no metrics)
39+ interface AgentBasicInfo {
3940 id : string
4041 name : string
4142 description ?: string
@@ -47,15 +48,29 @@ interface AgentData {
4748 }
4849 version : string
4950 created_at : string
51+ tags ?: string [ ]
52+ }
53+
54+ // Metrics loaded client-side
55+ interface AgentMetrics {
56+ usage_count : number
57+ weekly_runs : number
58+ weekly_spent : number
59+ total_spent : number
60+ avg_cost_per_invocation : number
61+ unique_users : number
62+ last_used ?: string
63+ }
64+
65+ // Combined data for display
66+ interface AgentData extends AgentBasicInfo {
5067 usage_count ?: number
5168 weekly_runs ?: number
5269 weekly_spent ?: number
5370 total_spent ?: number
5471 avg_cost_per_invocation ?: number
5572 unique_users ?: number
5673 last_used ?: string
57- version_stats ?: Record < string , any >
58- tags ?: string [ ]
5974}
6075
6176interface AgentStoreState {
@@ -92,7 +107,7 @@ interface PublisherProfileResponse {
92107}
93108
94109interface AgentStoreClientProps {
95- initialAgents : AgentData [ ]
110+ initialAgents : AgentBasicInfo [ ]
96111 initialPublishers : PublisherProfileResponse [ ]
97112 session : Session | null
98113 searchParams : { [ key : string ] : string | string [ ] | undefined }
@@ -199,24 +214,42 @@ export default function AgentStoreClient({
199214 loadingStateRef . current = { isLoadingMore, hasMore }
200215 } , [ isLoadingMore , hasMore ] )
201216
202- // Hydrate agents client-side if SSR provided none (build-time fallback)
203- const { data : hydratedAgents } = useQuery < AgentData [ ] > ( {
204- queryKey : [ 'agents' ] ,
217+ // Fetch metrics client-side - this is the progressive loading part
218+ const { data : metricsMap , isLoading : isLoadingMetrics } = useQuery <
219+ Record < string , AgentMetrics >
220+ > ( {
221+ queryKey : [ 'agents-metrics' ] ,
205222 queryFn : async ( ) => {
206- const response = await fetch ( '/api/agents' )
223+ const response = await fetch ( '/api/agents/metrics ' )
207224 if ( ! response . ok ) {
208- throw new Error ( `Failed to fetch agents : ${ response . statusText } ` )
225+ throw new Error ( `Failed to fetch metrics : ${ response . statusText } ` )
209226 }
210227 return response . json ( )
211228 } ,
212- enabled : ( initialAgents ?. length ?? 0 ) === 0 ,
213229 staleTime : 600000 , // 10 minutes
214230 } )
215231
216- // Prefer hydrated data if present; else use SSR data
217- const agents = useMemo ( ( ) => {
218- return hydratedAgents ?? initialAgents
219- } , [ hydratedAgents , initialAgents ] )
232+ // Combine basic agent info with metrics when available
233+ const agents : AgentData [ ] = useMemo ( ( ) => {
234+ if ( ! initialAgents ?. length ) return [ ]
235+
236+ return initialAgents . map ( ( agent ) => {
237+ // Key matches how metrics are stored: publisherId/agentName
238+ const metricsKey = `${ agent . publisher . id } /${ agent . name } `
239+ const metrics = metricsMap ?. [ metricsKey ]
240+
241+ return {
242+ ...agent ,
243+ usage_count : metrics ?. usage_count ,
244+ weekly_runs : metrics ?. weekly_runs ,
245+ weekly_spent : metrics ?. weekly_spent ,
246+ total_spent : metrics ?. total_spent ,
247+ avg_cost_per_invocation : metrics ?. avg_cost_per_invocation ,
248+ unique_users : metrics ?. unique_users ,
249+ last_used : metrics ?. last_used ,
250+ }
251+ } )
252+ } , [ initialAgents , metricsMap ] )
220253
221254 const editorsChoice = useMemo ( ( ) => {
222255 return agents . filter ( ( agent ) => EDITORS_CHOICE_AGENTS . includes ( agent . id ) )
@@ -503,55 +536,75 @@ export default function AgentStoreClient({
503536 </ Badge >
504537 ) }
505538 </ div >
506- { agent . last_used && (
507- < span
508- className = "text-xs text-muted-foreground/60"
509- title = { new Date ( agent . last_used ) . toLocaleString ( ) }
510- >
511- Used < RelativeTime date = { agent . last_used } />
512- </ span >
539+ { isLoadingMetrics ? (
540+ < div className = "h-4 w-20 bg-muted/30 rounded animate-pulse" />
541+ ) : (
542+ agent . last_used && (
543+ < span
544+ className = "text-xs text-muted-foreground/60"
545+ title = { new Date ( agent . last_used ) . toLocaleString ( ) }
546+ >
547+ Used < RelativeTime date = { agent . last_used } />
548+ </ span >
549+ )
513550 ) }
514551 </ div >
515552 </ div >
516553
517- { /* Metrics Grid - Redesigned for better readability */ }
554+ { /* Metrics Grid - Shows skeleton while loading */ }
518555 < div className = "grid grid-cols-2 gap-3 py-3 border-t border-border/30" >
519556 < div className = "space-y-1" >
520557 < div className = "flex items-center gap-2" >
521558 < TrendingUp className = "h-4 w-4 text-emerald-400" />
522- < span className = "font-semibold text-emerald-400" >
523- { formatCurrency ( agent . weekly_spent ) }
524- </ span >
559+ { isLoadingMetrics ? (
560+ < div className = "h-5 w-12 bg-muted/50 rounded animate-pulse" />
561+ ) : (
562+ < span className = "font-semibold text-emerald-400" >
563+ { formatCurrency ( agent . weekly_spent ) }
564+ </ span >
565+ ) }
525566 </ div >
526567 < p className = "text-xs text-muted-foreground" > Weekly spend</ p >
527568 </ div >
528569
529570 < div className = "space-y-1" >
530571 < div className = "flex items-center gap-2" >
531572 < Play className = "h-4 w-4 text-muted-foreground" />
532- < span className = "font-semibold" >
533- { formatUsageCount ( agent . weekly_runs ) }
534- </ span >
573+ { isLoadingMetrics ? (
574+ < div className = "h-5 w-10 bg-muted/50 rounded animate-pulse" />
575+ ) : (
576+ < span className = "font-semibold" >
577+ { formatUsageCount ( agent . weekly_runs ) }
578+ </ span >
579+ ) }
535580 </ div >
536581 < p className = "text-xs text-muted-foreground" > Weekly runs</ p >
537582 </ div >
538583
539584 < div className = "space-y-1" >
540585 < div className = "flex items-center gap-2" >
541586 < DollarSign className = "h-4 w-4 text-muted-foreground" />
542- < span className = "font-semibold" >
543- { formatCurrency ( agent . avg_cost_per_invocation ) }
544- </ span >
587+ { isLoadingMetrics ? (
588+ < div className = "h-5 w-12 bg-muted/50 rounded animate-pulse" />
589+ ) : (
590+ < span className = "font-semibold" >
591+ { formatCurrency ( agent . avg_cost_per_invocation ) }
592+ </ span >
593+ ) }
545594 </ div >
546595 < p className = "text-xs text-muted-foreground" > Per run</ p >
547596 </ div >
548597
549598 < div className = "space-y-1" >
550599 < div className = "flex items-center gap-2" >
551600 < Users className = "h-4 w-4 text-muted-foreground" />
552- < span className = "font-semibold" >
553- { agent . unique_users || 0 }
554- </ span >
601+ { isLoadingMetrics ? (
602+ < div className = "h-5 w-8 bg-muted/50 rounded animate-pulse" />
603+ ) : (
604+ < span className = "font-semibold" >
605+ { agent . unique_users || 0 }
606+ </ span >
607+ ) }
555608 </ div >
556609 < p className = "text-xs text-muted-foreground" > Users</ p >
557610 </ div >
0 commit comments