@@ -9,7 +9,7 @@ const STATS_ROW_CLASSES = "grid gap-[12px]";
99const STATS_ROW_3_CLASSES = "grid-cols-3" ;
1010const STATS_ROW_4_CLASSES = "grid-cols-4" ;
1111const STAT_CARD_CLASSES = "border border-border-muted bg-surface-muted px-[18px] py-[14px]" ;
12- const STAT_CARD_SKELETON_CLASSES = "h-[64px] opacity-40 " ;
12+ const STAT_CARD_SKELETON_CLASSES = "animate-pulse " ;
1313const STAT_VALUE_CLASSES = "text-[1.3rem] font-bold leading-[1.2] whitespace-nowrap text-foreground" ;
1414const STAT_LABEL_CLASSES =
1515 "mt-[2px] text-[0.7rem] font-semibold uppercase tracking-[0.04em] whitespace-nowrap text-foreground-subtle" ;
@@ -19,6 +19,15 @@ const MODEL_LIST_CLASSES = "mt-[6px] mb-0 list-none p-0";
1919const MODEL_LIST_ITEM_CLASSES = "flex items-center justify-between py-[2px] text-[0.8rem]" ;
2020const MODEL_NAME_CLASSES = "font-mono text-[0.78rem] text-foreground-muted" ;
2121const MODEL_COUNT_CLASSES = "text-[0.75rem] text-foreground-subtle" ;
22+ const STATS_STATUS_CLASSES = "text-[0.72rem] font-semibold uppercase tracking-[0.06em] text-foreground-subtle" ;
23+ const SKELETON_BAR_CLASSES = "rounded-sm bg-border-muted/70" ;
24+ const SKELETON_VALUE_CLASSES = `${ SKELETON_BAR_CLASSES } h-[22px] w-[58%]` ;
25+ const SKELETON_LABEL_CLASSES = `${ SKELETON_BAR_CLASSES } mt-[10px] h-[10px] w-[42%]` ;
26+ const SKELETON_TOKEN_VALUE_CLASSES = `${ SKELETON_BAR_CLASSES } h-[20px] w-[70%]` ;
27+ const SKELETON_TOKEN_LABEL_CLASSES = `${ SKELETON_BAR_CLASSES } mt-[8px] h-[9px] w-[48%]` ;
28+ const SKELETON_MODEL_ROW_CLASSES = "flex items-center justify-between gap-[12px]" ;
29+ const SKELETON_MODEL_NAME_CLASSES = `${ SKELETON_BAR_CLASSES } h-[10px] w-[44%]` ;
30+ const SKELETON_MODEL_COUNT_CLASSES = `${ SKELETON_BAR_CLASSES } h-[10px] w-[24%]` ;
2231
2332const ONE_BILLION = 1_000_000_000 ;
2433const ONE_MILLION = 1_000_000 ;
@@ -49,19 +58,54 @@ function totalTokens(usage: ModelTokenUsage): number {
4958export type DashboardStatsProps = {
5059 stats : Stats | null ;
5160 loading ?: boolean | undefined ;
61+ refreshing ?: boolean | undefined ;
5262 error ?: string | undefined ;
5363 onRetry ?: ( ( ) => void ) | undefined ;
5464} ;
5565
56- export function DashboardStats ( { stats, loading, error, onRetry } : DashboardStatsProps ) {
66+ export function DashboardStats ( { stats, loading, refreshing , error, onRetry } : DashboardStatsProps ) {
5767 if ( loading ) {
5868 return (
59- < div className = { DASHBOARD_STATS_CLASSES } >
69+ < div className = { DASHBOARD_STATS_CLASSES } aria-busy = "true" role = "status" aria-label = "Loading stats" >
70+ < div className = { STATS_STATUS_CLASSES } > Loading stats...</ div >
6071 < div className = { `${ STATS_ROW_CLASSES } ${ STATS_ROW_3_CLASSES } ` } >
6172 { [ "skeleton-0" , "skeleton-1" , "skeleton-2" ] . map ( ( key ) => (
62- < div key = { key } className = { `${ STAT_CARD_CLASSES } ${ STAT_CARD_SKELETON_CLASSES } ` } />
73+ < div key = { key } className = { `${ STAT_CARD_CLASSES } ${ STAT_CARD_SKELETON_CLASSES } ` } >
74+ < div className = { SKELETON_VALUE_CLASSES } />
75+ < div className = { SKELETON_LABEL_CLASSES } />
76+ </ div >
6377 ) ) }
6478 </ div >
79+ < div className = { `${ STATS_ROW_CLASSES } ${ STATS_ROW_3_CLASSES } ` } >
80+ { [ "skeleton-3" , "skeleton-4" , "skeleton-5" ] . map ( ( key ) => (
81+ < div key = { key } className = { `${ STAT_CARD_CLASSES } ${ STAT_CARD_SKELETON_CLASSES } ` } >
82+ < div className = { SKELETON_VALUE_CLASSES } />
83+ < div className = { SKELETON_LABEL_CLASSES } />
84+ </ div >
85+ ) ) }
86+ </ div >
87+ < div className = { `${ STAT_CARD_CLASSES } ${ STAT_CARD_SKELETON_CLASSES } ` } >
88+ < div className = { `${ SKELETON_LABEL_CLASSES } mt-0 w-[18%]` } />
89+ < div className = { `${ STATS_ROW_CLASSES } ${ STATS_ROW_4_CLASSES } ${ TOKEN_ROW_CLASSES } ` } >
90+ { [ "token-0" , "token-1" , "token-2" , "token-3" ] . map ( ( key ) => (
91+ < div key = { key } >
92+ < div className = { SKELETON_TOKEN_VALUE_CLASSES } />
93+ < div className = { SKELETON_TOKEN_LABEL_CLASSES } />
94+ </ div >
95+ ) ) }
96+ </ div >
97+ </ div >
98+ < div className = { `${ STAT_CARD_CLASSES } ${ STAT_CARD_SKELETON_CLASSES } ` } >
99+ < div className = { `${ SKELETON_LABEL_CLASSES } mt-0 w-[14%]` } />
100+ < ul className = { MODEL_LIST_CLASSES } >
101+ { [ "model-0" , "model-1" , "model-2" , "model-3" ] . map ( ( key ) => (
102+ < li key = { key } className = { `${ MODEL_LIST_ITEM_CLASSES } ${ SKELETON_MODEL_ROW_CLASSES } ` } >
103+ < div className = { SKELETON_MODEL_NAME_CLASSES } />
104+ < div className = { SKELETON_MODEL_COUNT_CLASSES } />
105+ </ li >
106+ ) ) }
107+ </ ul >
108+ </ div >
65109 </ div >
66110 ) ;
67111 }
@@ -78,6 +122,7 @@ export function DashboardStats({ stats, loading, error, onRetry }: DashboardStat
78122
79123 return (
80124 < div className = { DASHBOARD_STATS_CLASSES } >
125+ { refreshing ? < div className = { STATS_STATUS_CLASSES } > Refreshing stats...</ div > : null }
81126 < div className = { `${ STATS_ROW_CLASSES } ${ STATS_ROW_3_CLASSES } ` } >
82127 < div className = { STAT_CARD_CLASSES } >
83128 < div className = { STAT_VALUE_CLASSES } > { fmt . format ( stats . projects ) } </ div >
0 commit comments