@@ -8,7 +8,7 @@ import { Select, SelectTrigger, SelectValue, SelectContent, SelectItem } from '@
88import { Switch } from '@/components/ui/switch'
99import { Label } from '@/components/ui/label'
1010import { ChartContainer , ChartTooltip , ChartTooltipContent , type ChartConfig } from '@/components/ui/chart'
11- import { Area , AreaChart , XAxis , YAxis , CartesianGrid } from 'recharts'
11+ import { Area , AreaChart , XAxis , YAxis , CartesianGrid , RadialBarChart , RadialBar , PolarAngleAxis } from 'recharts'
1212import { HugeiconsIcon } from '@hugeicons/react'
1313import { Cancel01Icon , Loading03Icon } from '@hugeicons/core-free-icons'
1414import {
@@ -632,13 +632,6 @@ function ViboraInstancesTab() {
632632 )
633633}
634634
635- // Helper to get color class based on usage percentage
636- function getUsageColor ( percent : number ) : string {
637- if ( percent >= 90 ) return 'bg-red-500'
638- if ( percent >= 70 ) return 'bg-yellow-500'
639- return 'bg-green-500'
640- }
641-
642635// Helper to get text color class based on usage percentage
643636function getUsageTextColor ( percent : number ) : string {
644637 if ( percent >= 90 ) return 'text-red-500'
@@ -671,10 +664,75 @@ function formatResetDate(resetAt: string): string {
671664 return date . toLocaleDateString ( [ ] , { month : 'short' , day : 'numeric' , hour : '2-digit' , minute : '2-digit' } )
672665}
673666
667+ // Radial gauge component for usage visualization
668+ function UsageGauge ( {
669+ percent,
670+ label,
671+ subtitle,
672+ color,
673+ } : {
674+ percent : number
675+ label : string
676+ subtitle : string
677+ color : string
678+ } ) {
679+ const data = [ { value : Math . min ( percent , 100 ) , fill : color } ]
680+
681+ return (
682+ < Card className = "p-4" >
683+ < div className = "flex items-center gap-4" >
684+ < div className = "relative size-24 shrink-0" >
685+ < RadialBarChart
686+ width = { 96 }
687+ height = { 96 }
688+ cx = { 48 }
689+ cy = { 48 }
690+ innerRadius = { 32 }
691+ outerRadius = { 44 }
692+ barSize = { 10 }
693+ data = { data }
694+ startAngle = { 90 }
695+ endAngle = { - 270 }
696+ >
697+ < PolarAngleAxis
698+ type = "number"
699+ domain = { [ 0 , 100 ] }
700+ angleAxisId = { 0 }
701+ tick = { false }
702+ />
703+ < RadialBar
704+ background = { { fill : 'hsl(var(--muted))' } }
705+ dataKey = "value"
706+ cornerRadius = { 5 }
707+ angleAxisId = { 0 }
708+ />
709+ </ RadialBarChart >
710+ < div className = "absolute inset-0 flex items-center justify-center" >
711+ < span className = { `text-lg font-semibold tabular-nums ${ getUsageTextColor ( percent ) } ` } >
712+ { percent . toFixed ( 0 ) } %
713+ </ span >
714+ </ div >
715+ </ div >
716+ < div className = "flex-1 min-w-0" >
717+ < h3 className = "text-sm font-medium truncate" > { label } </ h3 >
718+ < p className = "text-xs text-muted-foreground mt-1" > { subtitle } </ p >
719+ </ div >
720+ </ div >
721+ </ Card >
722+ )
723+ }
724+
674725function ClaudeUsageLimitsTab ( ) {
675726 const { t } = useTranslation ( 'monitoring' )
676727 const { data : usage , isLoading, error } = useClaudeUsage ( )
677728
729+ // Get color based on usage level
730+ const getGaugeColor = ( percent : number ) : string => {
731+ if ( percent >= 90 ) return '#ef4444' // red-500
732+ if ( percent >= 70 ) return '#eab308' // yellow-500
733+ return '#22c55e' // green-500
734+ }
735+
678736 return (
679737 < div className = "space-y-4" >
680738 { isLoading && (
@@ -697,91 +755,48 @@ function ClaudeUsageLimitsTab() {
697755
698756 { usage && usage . available && (
699757 < div className = "space-y-3" >
700- { /* 5-Hour Block */ }
701- { usage . fiveHour && (
702- < Card className = "p-4" >
703- < div className = "flex items-center justify-between mb-2" >
704- < h3 className = "text-sm font-medium" > { t ( 'usage.fiveHourBlock' ) } </ h3 >
705- < span className = { `text-sm font-medium tabular-nums ${ getUsageTextColor ( usage . fiveHour . percentUsed ) } ` } >
706- { usage . fiveHour . percentUsed . toFixed ( 1 ) } %
707- </ span >
708- </ div >
709- < div className = "h-2 w-full overflow-hidden rounded-full bg-muted" >
710- < div
711- className = { `h-full transition-all ${ getUsageColor ( usage . fiveHour . percentUsed ) } ` }
712- style = { { width : `${ Math . min ( usage . fiveHour . percentUsed , 100 ) } %` } }
713- />
714- </ div >
715- < p className = "mt-2 text-xs text-muted-foreground" >
716- { t ( 'usage.resetsIn' , { time : formatTimeRemaining ( usage . fiveHour . timeRemainingMinutes ) } ) }
717- </ p >
718- </ Card >
719- ) }
758+ { /* Main usage gauges */ }
759+ < div className = "grid gap-3 sm:grid-cols-2" >
760+ { /* 5-Hour Block */ }
761+ { usage . fiveHour && (
762+ < UsageGauge
763+ percent = { usage . fiveHour . percentUsed }
764+ label = { t ( 'usage.fiveHourBlock' ) }
765+ subtitle = { t ( 'usage.resetsIn' , { time : formatTimeRemaining ( usage . fiveHour . timeRemainingMinutes ) } ) }
766+ color = { getGaugeColor ( usage . fiveHour . percentUsed ) }
767+ />
768+ ) }
720769
721- { /* 7-Day Rolling */ }
722- { usage . sevenDay && (
723- < Card className = "p-4" >
724- < div className = "flex items-center justify-between mb-2" >
725- < h3 className = "text-sm font-medium" > { t ( 'usage.sevenDayRolling' ) } </ h3 >
726- < span className = { `text-sm font-medium tabular-nums ${ getUsageTextColor ( usage . sevenDay . percentUsed ) } ` } >
727- { usage . sevenDay . percentUsed . toFixed ( 1 ) } %
728- </ span >
729- </ div >
730- < div className = "h-2 w-full overflow-hidden rounded-full bg-muted" >
731- < div
732- className = { `h-full transition-all ${ getUsageColor ( usage . sevenDay . percentUsed ) } ` }
733- style = { { width : `${ Math . min ( usage . sevenDay . percentUsed , 100 ) } %` } }
734- />
735- </ div >
736- < p className = "mt-2 text-xs text-muted-foreground" >
737- { t ( 'usage.resetsAt' , { time : formatResetDate ( usage . sevenDay . resetAt ) } ) }
738- { ' · ' }
739- { t ( 'usage.weekProgress' , { percent : usage . sevenDay . weekProgressPercent } ) }
740- </ p >
741- </ Card >
742- ) }
770+ { /* 7-Day Rolling */ }
771+ { usage . sevenDay && (
772+ < UsageGauge
773+ percent = { usage . sevenDay . percentUsed }
774+ label = { t ( 'usage.sevenDayRolling' ) }
775+ subtitle = { `${ t ( 'usage.resetsAt' , { time : formatResetDate ( usage . sevenDay . resetAt ) } ) } · ${ t ( 'usage.weekProgress' , { percent : usage . sevenDay . weekProgressPercent } ) } ` }
776+ color = { getGaugeColor ( usage . sevenDay . percentUsed ) }
777+ />
778+ ) }
779+ </ div >
743780
744781 { /* Model-specific limits (Opus/Sonnet) */ }
745782 { ( usage . sevenDayOpus || usage . sevenDaySonnet ) && (
746783 < div className = "grid gap-3 sm:grid-cols-2" >
747784 { usage . sevenDayOpus && (
748- < Card className = "p-4" >
749- < div className = "flex items-center justify-between mb-2" >
750- < h3 className = "text-sm font-medium" > { t ( 'usage.opusWeekly' ) } </ h3 >
751- < span className = { `text-sm font-medium tabular-nums ${ getUsageTextColor ( usage . sevenDayOpus . percentUsed ) } ` } >
752- { usage . sevenDayOpus . percentUsed . toFixed ( 1 ) } %
753- </ span >
754- </ div >
755- < div className = "h-2 w-full overflow-hidden rounded-full bg-muted" >
756- < div
757- className = { `h-full transition-all ${ getUsageColor ( usage . sevenDayOpus . percentUsed ) } ` }
758- style = { { width : `${ Math . min ( usage . sevenDayOpus . percentUsed , 100 ) } %` } }
759- />
760- </ div >
761- < p className = "mt-2 text-xs text-muted-foreground" >
762- { t ( 'usage.resetsAt' , { time : formatResetDate ( usage . sevenDayOpus . resetAt ) } ) }
763- </ p >
764- </ Card >
785+ < UsageGauge
786+ percent = { usage . sevenDayOpus . percentUsed }
787+ label = { t ( 'usage.opusWeekly' ) }
788+ subtitle = { t ( 'usage.resetsAt' , { time : formatResetDate ( usage . sevenDayOpus . resetAt ) } ) }
789+ color = { getGaugeColor ( usage . sevenDayOpus . percentUsed ) }
790+ />
765791 ) }
766792
767793 { usage . sevenDaySonnet && (
768- < Card className = "p-4" >
769- < div className = "flex items-center justify-between mb-2" >
770- < h3 className = "text-sm font-medium" > { t ( 'usage.sonnetWeekly' ) } </ h3 >
771- < span className = { `text-sm font-medium tabular-nums ${ getUsageTextColor ( usage . sevenDaySonnet . percentUsed ) } ` } >
772- { usage . sevenDaySonnet . percentUsed . toFixed ( 1 ) } %
773- </ span >
774- </ div >
775- < div className = "h-2 w-full overflow-hidden rounded-full bg-muted" >
776- < div
777- className = { `h-full transition-all ${ getUsageColor ( usage . sevenDaySonnet . percentUsed ) } ` }
778- style = { { width : `${ Math . min ( usage . sevenDaySonnet . percentUsed , 100 ) } %` } }
779- />
780- </ div >
781- < p className = "mt-2 text-xs text-muted-foreground" >
782- { t ( 'usage.resetsAt' , { time : formatResetDate ( usage . sevenDaySonnet . resetAt ) } ) }
783- </ p >
784- </ Card >
794+ < UsageGauge
795+ percent = { usage . sevenDaySonnet . percentUsed }
796+ label = { t ( 'usage.sonnetWeekly' ) }
797+ subtitle = { t ( 'usage.resetsAt' , { time : formatResetDate ( usage . sevenDaySonnet . resetAt ) } ) }
798+ color = { getGaugeColor ( usage . sevenDaySonnet . percentUsed ) }
799+ />
785800 ) }
786801 </ div >
787802 ) }
0 commit comments