From f255a20a931babc45e8a4c9e34d733f8a9eed83f Mon Sep 17 00:00:00 2001 From: kaitranntt Date: Tue, 9 Dec 2025 14:44:56 -0500 Subject: [PATCH] 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 --- docs/project-roadmap.md | 3 +- .../analytics/model-breakdown-chart.tsx | 19 ++---------- ui/src/lib/utils.ts | 26 +++++++++++++++++ ui/src/pages/analytics.tsx | 29 ++----------------- 4 files changed, 33 insertions(+), 44 deletions(-) diff --git a/docs/project-roadmap.md b/docs/project-roadmap.md index 8611f590..3df08fb0 100644 --- a/docs/project-roadmap.md +++ b/docs/project-roadmap.md @@ -589,6 +589,7 @@ src/types/ **Release Date**: 2025-12-08 #### UI Fixes & Improvements +- ✅ **Analytics UI Enhancements**: Standardized colors, fixed truncated model names, and ensured color consistency in the analytics dashboard. - ✅ **Auto-formatting**: 31 UI files auto-formatted for consistent styling. - ✅ **Fast Refresh Exports**: Resolved `react-refresh/only-export-components` by extracting `buttonVariants`, `useSidebar`, and `useWebSocketContext` to separate files. - ✅ **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/ --- **Document Status**: Living document, updated with each major release -**Last Updated**: 2025-12-08 (UI Layout Improvements) +**Last Updated**: 2025-12-09 (Analytics UI Enhancements) **Next Update**: v4.6.0 UI Enhancements Planning **Maintainer**: CCS Development Team \ No newline at end of file diff --git a/ui/src/components/analytics/model-breakdown-chart.tsx b/ui/src/components/analytics/model-breakdown-chart.tsx index cfc02294..e96045fe 100644 --- a/ui/src/components/analytics/model-breakdown-chart.tsx +++ b/ui/src/components/analytics/model-breakdown-chart.tsx @@ -9,7 +9,7 @@ import { useMemo } from 'react'; import { PieChart, Pie, Cell, ResponsiveContainer, Tooltip } from 'recharts'; import { Skeleton } from '@/components/ui/skeleton'; import type { ModelUsage } from '@/hooks/use-usage'; -import { cn } from '@/lib/utils'; +import { cn, getModelColor } from '@/lib/utils'; interface ModelBreakdownChartProps { data: ModelUsage[]; @@ -17,30 +17,17 @@ interface ModelBreakdownChartProps { className?: string; } -const COLORS = [ - '#0080FF', - '#00C49F', - '#FFBB28', - '#FF8042', - '#8884D8', - '#82CA9D', - '#FFC658', - '#8DD1E1', - '#D084D0', - '#87D068', -]; - export function ModelBreakdownChart({ data, isLoading, className }: ModelBreakdownChartProps) { const chartData = useMemo(() => { if (!data || data.length === 0) return []; - return data.map((item, index) => ({ + return data.map((item) => ({ name: item.model, value: item.tokens, cost: item.cost, requests: item.requests, percentage: item.percentage, - fill: COLORS[index % COLORS.length], + fill: getModelColor(item.model), })); }, [data]); diff --git a/ui/src/lib/utils.ts b/ui/src/lib/utils.ts index 2819a830..d683130e 100644 --- a/ui/src/lib/utils.ts +++ b/ui/src/lib/utils.ts @@ -4,3 +4,29 @@ import { twMerge } from 'tailwind-merge'; export function cn(...inputs: ClassValue[]) { return twMerge(clsx(inputs)); } + +export function getModelColor(model: string): string { + // Vibrant Tones Palette + const colors = [ + '#f94144', // Strawberry Red + '#f3722c', // Pumpkin Spice + '#f8961e', // Carrot Orange + '#f9844a', // Atomic Tangerine + '#f9c74f', // Tuscan Sun + '#90be6d', // Willow Green + '#43aa8b', // Seaweed + '#4d908e', // Dark Cyan + '#577590', // Blue Slate + '#277da1', // Cerulean + ]; + + // FNV-1a hash algorithm + let hash = 0x811c9dc5; + for (let i = 0; i < model.length; i++) { + hash ^= model.charCodeAt(i); + hash = Math.imul(hash, 0x01000193); + } + + // Ensure positive index + return colors[(hash >>> 0) % colors.length]; +} diff --git a/ui/src/pages/analytics.tsx b/ui/src/pages/analytics.tsx index 410ae7fa..d000c405 100644 --- a/ui/src/pages/analytics.tsx +++ b/ui/src/pages/analytics.tsx @@ -26,6 +26,7 @@ import { useRefreshUsage, useUsageStatus, } from '@/hooks/use-usage'; +import { getModelColor } from '@/lib/utils'; type ViewMode = 'daily' | 'monthly' | 'sessions'; @@ -214,10 +215,7 @@ export function AnalyticsPage() { className="w-2.5 h-2.5 rounded-full shrink-0" style={{ backgroundColor: getModelColor(model.model) }} /> - + {model.model} @@ -276,29 +274,6 @@ export function AnalyticsPage() { ); } -// Helper function to generate consistent colors for models -function getModelColor(model: string): string { - const colors = [ - '#0080FF', - '#00C49F', - '#FFBB28', - '#FF8042', - '#8884D8', - '#82CA9D', - '#FFC658', - '#8DD1E1', - '#D084D0', - '#87D068', - ]; - - let hash = 0; - for (let i = 0; i < model.length; i++) { - hash = model.charCodeAt(i) + ((hash << 5) - hash); - } - - return colors[Math.abs(hash) % colors.length]; -} - export function AnalyticsSkeleton() { return (