Skip to content

Commit f255a20

Browse files
committed
feat(analytics): refactor model color management and fix UI display issues
- Extract getModelColor utility to lib/utils.ts with improved FNV-1a hash algorithm - Replace hardcoded color palette with vibrant tones palette - Remove color constants from model-breakdown-chart.tsx - Fix truncated model names in analytics display - Update project roadmap with analytics enhancements
1 parent 00cf5c2 commit f255a20

File tree

4 files changed

+33
-44
lines changed

4 files changed

+33
-44
lines changed

docs/project-roadmap.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -589,6 +589,7 @@ src/types/
589589
**Release Date**: 2025-12-08
590590

591591
#### UI Fixes & Improvements
592+
-**Analytics UI Enhancements**: Standardized colors, fixed truncated model names, and ensured color consistency in the analytics dashboard.
592593
-**Auto-formatting**: 31 UI files auto-formatted for consistent styling.
593594
-**Fast Refresh Exports**: Resolved `react-refresh/only-export-components` by extracting `buttonVariants`, `useSidebar`, and `useWebSocketContext` to separate files.
594595
-**React Hooks Issues**: Fixed `react-hooks/purity` (`Math.random()` in `useMemo` for `sidebar.tsx`) and `react-hooks/set-state-in-effect` (`use-theme.ts`, `settings.tsx`).
@@ -821,6 +822,6 @@ src/types/
821822
---
822823

823824
**Document Status**: Living document, updated with each major release
824-
**Last Updated**: 2025-12-08 (UI Layout Improvements)
825+
**Last Updated**: 2025-12-09 (Analytics UI Enhancements)
825826
**Next Update**: v4.6.0 UI Enhancements Planning
826827
**Maintainer**: CCS Development Team

ui/src/components/analytics/model-breakdown-chart.tsx

Lines changed: 3 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -9,38 +9,25 @@ import { useMemo } from 'react';
99
import { PieChart, Pie, Cell, ResponsiveContainer, Tooltip } from 'recharts';
1010
import { Skeleton } from '@/components/ui/skeleton';
1111
import type { ModelUsage } from '@/hooks/use-usage';
12-
import { cn } from '@/lib/utils';
12+
import { cn, getModelColor } from '@/lib/utils';
1313

1414
interface ModelBreakdownChartProps {
1515
data: ModelUsage[];
1616
isLoading?: boolean;
1717
className?: string;
1818
}
1919

20-
const COLORS = [
21-
'#0080FF',
22-
'#00C49F',
23-
'#FFBB28',
24-
'#FF8042',
25-
'#8884D8',
26-
'#82CA9D',
27-
'#FFC658',
28-
'#8DD1E1',
29-
'#D084D0',
30-
'#87D068',
31-
];
32-
3320
export function ModelBreakdownChart({ data, isLoading, className }: ModelBreakdownChartProps) {
3421
const chartData = useMemo(() => {
3522
if (!data || data.length === 0) return [];
3623

37-
return data.map((item, index) => ({
24+
return data.map((item) => ({
3825
name: item.model,
3926
value: item.tokens,
4027
cost: item.cost,
4128
requests: item.requests,
4229
percentage: item.percentage,
43-
fill: COLORS[index % COLORS.length],
30+
fill: getModelColor(item.model),
4431
}));
4532
}, [data]);
4633

ui/src/lib/utils.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,29 @@ import { twMerge } from 'tailwind-merge';
44
export function cn(...inputs: ClassValue[]) {
55
return twMerge(clsx(inputs));
66
}
7+
8+
export function getModelColor(model: string): string {
9+
// Vibrant Tones Palette
10+
const colors = [
11+
'#f94144', // Strawberry Red
12+
'#f3722c', // Pumpkin Spice
13+
'#f8961e', // Carrot Orange
14+
'#f9844a', // Atomic Tangerine
15+
'#f9c74f', // Tuscan Sun
16+
'#90be6d', // Willow Green
17+
'#43aa8b', // Seaweed
18+
'#4d908e', // Dark Cyan
19+
'#577590', // Blue Slate
20+
'#277da1', // Cerulean
21+
];
22+
23+
// FNV-1a hash algorithm
24+
let hash = 0x811c9dc5;
25+
for (let i = 0; i < model.length; i++) {
26+
hash ^= model.charCodeAt(i);
27+
hash = Math.imul(hash, 0x01000193);
28+
}
29+
30+
// Ensure positive index
31+
return colors[(hash >>> 0) % colors.length];
32+
}

ui/src/pages/analytics.tsx

Lines changed: 2 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import {
2626
useRefreshUsage,
2727
useUsageStatus,
2828
} from '@/hooks/use-usage';
29+
import { getModelColor } from '@/lib/utils';
2930

3031
type ViewMode = 'daily' | 'monthly' | 'sessions';
3132

@@ -214,10 +215,7 @@ export function AnalyticsPage() {
214215
className="w-2.5 h-2.5 rounded-full shrink-0"
215216
style={{ backgroundColor: getModelColor(model.model) }}
216217
/>
217-
<span
218-
className="font-medium truncate max-w-[150px]"
219-
title={model.model}
220-
>
218+
<span className="font-medium" title={model.model}>
221219
{model.model}
222220
</span>
223221
</div>
@@ -276,29 +274,6 @@ export function AnalyticsPage() {
276274
);
277275
}
278276

279-
// Helper function to generate consistent colors for models
280-
function getModelColor(model: string): string {
281-
const colors = [
282-
'#0080FF',
283-
'#00C49F',
284-
'#FFBB28',
285-
'#FF8042',
286-
'#8884D8',
287-
'#82CA9D',
288-
'#FFC658',
289-
'#8DD1E1',
290-
'#D084D0',
291-
'#87D068',
292-
];
293-
294-
let hash = 0;
295-
for (let i = 0; i < model.length; i++) {
296-
hash = model.charCodeAt(i) + ((hash << 5) - hash);
297-
}
298-
299-
return colors[Math.abs(hash) % colors.length];
300-
}
301-
302277
export function AnalyticsSkeleton() {
303278
return (
304279
<div className="space-y-4 h-full overflow-hidden">

0 commit comments

Comments
 (0)