mirror of
https://github.com/tiennm99/ccs.git
synced 2026-07-17 06:17:09 +00:00
fix(analytics): stabilize responsive row alignment
This commit is contained in:
@@ -32,7 +32,7 @@ export function CliproxyStatsCard({
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<Card className={cn('flex flex-col h-full', className)}>
|
||||
<Card className={cn('flex flex-col h-full min-h-0 overflow-hidden gap-0 py-0', className)}>
|
||||
<CardHeader className="px-3 py-2">
|
||||
<CardTitle className="text-base font-semibold flex items-center gap-2">
|
||||
<Server className="h-4 w-4" />
|
||||
@@ -52,7 +52,12 @@ export function CliproxyStatsCard({
|
||||
// Proxy not running
|
||||
if (!status?.running) {
|
||||
return (
|
||||
<Card className={cn('flex flex-col h-full border-dashed', className)}>
|
||||
<Card
|
||||
className={cn(
|
||||
'flex flex-col h-full min-h-0 overflow-hidden gap-0 py-0 border-dashed',
|
||||
className
|
||||
)}
|
||||
>
|
||||
<CardHeader className="px-3 py-2">
|
||||
<div className="flex items-center justify-between">
|
||||
<CardTitle className="text-base font-semibold flex items-center gap-2">
|
||||
@@ -76,7 +81,12 @@ export function CliproxyStatsCard({
|
||||
// Error fetching stats
|
||||
if (error) {
|
||||
return (
|
||||
<Card className={cn('flex flex-col h-full border-destructive/50', className)}>
|
||||
<Card
|
||||
className={cn(
|
||||
'flex flex-col h-full min-h-0 overflow-hidden gap-0 py-0 border-destructive/50',
|
||||
className
|
||||
)}
|
||||
>
|
||||
<CardHeader className="px-3 py-2">
|
||||
<div className="flex items-center justify-between">
|
||||
<CardTitle className="text-base font-semibold flex items-center gap-2">
|
||||
@@ -110,7 +120,7 @@ export function CliproxyStatsCard({
|
||||
const maxModelRequests = models.length > 0 ? models[0][1] : 1;
|
||||
|
||||
return (
|
||||
<Card className={cn('flex flex-col h-full overflow-hidden', className)}>
|
||||
<Card className={cn('flex flex-col h-full min-h-0 overflow-hidden gap-0 py-0', className)}>
|
||||
<CardHeader className="px-3 py-2 border-b bg-muted/5">
|
||||
<div className="flex items-center justify-between">
|
||||
<CardTitle className="text-base font-semibold flex items-center gap-2">
|
||||
|
||||
@@ -70,7 +70,7 @@ export function DateRangeFilter({
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={cn('flex items-center gap-2', className)}>
|
||||
<div className={cn('flex flex-wrap items-center gap-2', className)}>
|
||||
{presets.map((preset) => (
|
||||
<Button
|
||||
key={preset.label}
|
||||
@@ -87,7 +87,7 @@ export function DateRangeFilter({
|
||||
id="date"
|
||||
variant={'outline'}
|
||||
className={cn(
|
||||
'w-auto min-w-[240px] justify-start text-left font-normal',
|
||||
'w-auto min-w-[200px] sm:min-w-[240px] justify-start text-left font-normal',
|
||||
!value && 'text-muted-foreground'
|
||||
)}
|
||||
>
|
||||
|
||||
@@ -34,12 +34,12 @@ export function ModelBreakdownChart({ data, isLoading, className }: ModelBreakdo
|
||||
}, [data]);
|
||||
|
||||
if (isLoading) {
|
||||
return <Skeleton className={cn('h-[300px] w-full', className)} />;
|
||||
return <Skeleton className={cn('h-full min-h-[100px] w-full', className)} />;
|
||||
}
|
||||
|
||||
if (!data || data.length === 0) {
|
||||
return (
|
||||
<div className={cn('h-[300px] flex items-center justify-center', className)}>
|
||||
<div className={cn('h-full min-h-[100px] flex items-center justify-center', className)}>
|
||||
<p className="text-muted-foreground">No model data available</p>
|
||||
</div>
|
||||
);
|
||||
@@ -72,8 +72,8 @@ export function ModelBreakdownChart({ data, isLoading, className }: ModelBreakdo
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={cn('w-full', className)}>
|
||||
<ResponsiveContainer width="100%" height={250}>
|
||||
<div className={cn('w-full h-full min-h-[100px]', className)}>
|
||||
<ResponsiveContainer width="100%" height="100%">
|
||||
<PieChart>
|
||||
<Pie
|
||||
data={chartData}
|
||||
|
||||
@@ -55,7 +55,7 @@ export function SessionStatsCard({ data, isLoading, className }: SessionStatsCar
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<Card className={cn('flex flex-col h-full', className)}>
|
||||
<Card className={cn('flex flex-col h-full min-h-0 overflow-hidden gap-0 py-0', className)}>
|
||||
<CardHeader className="px-3 py-2">
|
||||
<Skeleton className="h-5 w-32" />
|
||||
</CardHeader>
|
||||
@@ -68,7 +68,7 @@ export function SessionStatsCard({ data, isLoading, className }: SessionStatsCar
|
||||
|
||||
if (!stats) {
|
||||
return (
|
||||
<Card className={cn('flex flex-col h-full', className)}>
|
||||
<Card className={cn('flex flex-col h-full min-h-0 overflow-hidden gap-0 py-0', className)}>
|
||||
<CardHeader className="px-3 py-2">
|
||||
<CardTitle className="text-base font-semibold flex items-center gap-2">
|
||||
<Terminal className="w-4 h-4" />
|
||||
@@ -83,14 +83,16 @@ export function SessionStatsCard({ data, isLoading, className }: SessionStatsCar
|
||||
}
|
||||
|
||||
return (
|
||||
<Card className={cn('flex flex-col h-full shadow-sm', className)}>
|
||||
<Card
|
||||
className={cn('flex flex-col h-full min-h-0 overflow-hidden gap-0 py-0 shadow-sm', className)}
|
||||
>
|
||||
<CardHeader className="px-3 py-2">
|
||||
<CardTitle className="text-base font-semibold flex items-center gap-2">
|
||||
<Terminal className="w-4 h-4" />
|
||||
Session Stats
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="px-3 pb-3 pt-0 flex-1 flex flex-col gap-4">
|
||||
<CardContent className="px-3 pb-3 pt-0 flex-1 min-h-0 flex flex-col gap-3">
|
||||
{/* Key Metrics Grid */}
|
||||
<div className="grid grid-cols-2 gap-2">
|
||||
{/* Total Sessions */}
|
||||
@@ -119,12 +121,12 @@ export function SessionStatsCard({ data, isLoading, className }: SessionStatsCar
|
||||
</div>
|
||||
|
||||
{/* Recent Activity List */}
|
||||
<div className="flex-1 space-y-2">
|
||||
<div className="flex-1 min-h-0 space-y-2">
|
||||
<div className="flex items-center gap-1 text-xs text-muted-foreground font-medium mb-1">
|
||||
<Clock className="w-3 h-3" />
|
||||
Recent Activity
|
||||
</div>
|
||||
<div className="space-y-1.5">
|
||||
<div className="space-y-1.5 max-h-full overflow-y-auto pr-1">
|
||||
{stats.recentSessions.map((session) => (
|
||||
<div
|
||||
key={session.sessionId}
|
||||
|
||||
@@ -23,7 +23,7 @@ export function UsageSummaryCards({ data, isLoading }: UsageSummaryCardsProps) {
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<div className="grid grid-cols-2 md:grid-cols-3 xl:grid-cols-5 gap-4">
|
||||
<div className="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-5 gap-4">
|
||||
{[1, 2, 3, 4, 5].map((i) => (
|
||||
<Card key={i}>
|
||||
<CardContent className="p-6">
|
||||
@@ -95,7 +95,7 @@ export function UsageSummaryCards({ data, isLoading }: UsageSummaryCardsProps) {
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="grid grid-cols-2 md:grid-cols-3 xl:grid-cols-5 gap-4">
|
||||
<div className="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-5 gap-4">
|
||||
{cards.map((card, index) => {
|
||||
const Icon = card.icon;
|
||||
return (
|
||||
|
||||
@@ -109,7 +109,7 @@ function SidebarProvider({
|
||||
} as React.CSSProperties
|
||||
}
|
||||
className={cn(
|
||||
'group/sidebar-wrapper has-data-[variant=inset]:bg-sidebar flex min-h-svh w-full',
|
||||
'group/sidebar-wrapper has-data-[variant=inset]:bg-sidebar flex h-svh min-h-0 w-full overflow-hidden',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
|
||||
@@ -30,12 +30,12 @@ export function AnalyticsHeader({
|
||||
viewMode,
|
||||
}: AnalyticsHeaderProps) {
|
||||
return (
|
||||
<div className="flex items-center justify-between shrink-0">
|
||||
<div className="flex flex-col gap-3 shrink-0 xl:flex-row xl:items-center xl:justify-between">
|
||||
<div>
|
||||
<h1 className="text-xl font-semibold">Analytics</h1>
|
||||
<p className="text-sm text-muted-foreground">Track usage & insights</p>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="flex flex-wrap items-center gap-2 xl:justify-end">
|
||||
<Button
|
||||
variant={viewMode === 'hourly' ? 'default' : 'outline'}
|
||||
size="sm"
|
||||
@@ -45,6 +45,7 @@ export function AnalyticsHeader({
|
||||
24H
|
||||
</Button>
|
||||
<DateRangeFilter
|
||||
className="flex-wrap"
|
||||
value={dateRange}
|
||||
onChange={onDateRangeChange}
|
||||
presets={[
|
||||
|
||||
@@ -44,9 +44,9 @@ export function ChartsGrid({
|
||||
const { privacyMode } = usePrivacy();
|
||||
|
||||
return (
|
||||
<div className="flex-1 flex flex-col min-h-0 gap-4">
|
||||
<div className="min-h-0 grid gap-4 lg:grid-rows-[minmax(260px,1.2fr)_minmax(220px,0.9fr)]">
|
||||
{/* Usage Trend Chart - Full Width */}
|
||||
<Card className="flex flex-col flex-1 min-h-0 max-h-[500px] overflow-hidden shadow-sm">
|
||||
<Card className="flex flex-col h-full min-h-[220px] lg:min-h-[240px] overflow-hidden gap-0 py-0 shadow-sm">
|
||||
<CardHeader className="px-3 py-2 shrink-0">
|
||||
<CardTitle className="text-base font-semibold flex items-center gap-2">
|
||||
<TrendingUp className="w-4 h-4" />
|
||||
@@ -63,7 +63,7 @@ export function ChartsGrid({
|
||||
</Card>
|
||||
|
||||
{/* Bottom Row */}
|
||||
<div className="grid grid-cols-1 lg:grid-cols-10 gap-4 h-auto lg:h-[180px] shrink-0">
|
||||
<div className="grid grid-cols-1 lg:grid-cols-10 gap-4 h-auto min-h-[220px] lg:h-full lg:min-h-[220px] lg:grid-rows-[minmax(0,1fr)] lg:[&>*]:min-h-0">
|
||||
{/* Cost by Model */}
|
||||
<CostByModelCard
|
||||
models={models}
|
||||
@@ -73,7 +73,7 @@ export function ChartsGrid({
|
||||
/>
|
||||
|
||||
{/* Model Distribution */}
|
||||
<Card className="flex flex-col h-full min-h-0 shadow-sm lg:col-span-2">
|
||||
<Card className="flex flex-col h-full min-h-0 overflow-hidden gap-0 py-0 shadow-sm lg:col-span-2">
|
||||
<CardHeader className="px-3 py-2">
|
||||
<CardTitle className="text-base font-semibold flex items-center gap-2">
|
||||
<PieChart className="w-4 h-4" />
|
||||
|
||||
@@ -26,7 +26,7 @@ export function CostByModelCard({
|
||||
privacyMode,
|
||||
}: CostByModelCardProps) {
|
||||
return (
|
||||
<Card className="flex flex-col h-full min-h-0 shadow-sm lg:col-span-4">
|
||||
<Card className="flex flex-col h-full min-h-0 overflow-hidden gap-0 py-0 shadow-sm lg:col-span-4">
|
||||
<CardHeader className="px-3 py-2">
|
||||
<CardTitle className="text-base font-semibold flex items-center gap-2">
|
||||
<DollarSign className="w-4 h-4" />
|
||||
|
||||
@@ -40,7 +40,7 @@ export function AnalyticsPage() {
|
||||
} = useAnalyticsPage();
|
||||
|
||||
return (
|
||||
<div className="flex flex-col h-full overflow-hidden px-4 pt-4 pb-50 gap-4">
|
||||
<div className="grid h-full min-h-0 grid-rows-[auto_auto_minmax(0,1fr)] gap-4 overflow-y-auto px-4 py-4">
|
||||
{/* Header */}
|
||||
<AnalyticsHeader
|
||||
dateRange={dateRange}
|
||||
|
||||
Reference in New Issue
Block a user