diff --git a/ui/src/components/analytics/sessions-table.tsx b/ui/src/components/analytics/sessions-table.tsx
deleted file mode 100644
index 956ac4a6..00000000
--- a/ui/src/components/analytics/sessions-table.tsx
+++ /dev/null
@@ -1,269 +0,0 @@
-/**
- * Sessions Table Component
- *
- * Displays session history with pagination and filtering.
- * Shows session duration, tokens, cost, and metadata.
- */
-
-import { useState, useMemo } from 'react';
-import { formatDistanceToNow } from 'date-fns';
-import {
- Table,
- TableBody,
- TableCell,
- TableHead,
- TableHeader,
- TableRow,
-} from '@/components/ui/table';
-import { Button } from '@/components/ui/button';
-import { Input } from '@/components/ui/input';
-import { Badge } from '@/components/ui/badge';
-import { Skeleton } from '@/components/ui/skeleton';
-import { ChevronLeft, ChevronRight, Search, Clock, Zap, DollarSign } from 'lucide-react';
-import { cn } from '@/lib/utils';
-import type { PaginatedSessions } from '@/hooks/use-usage';
-
-interface SessionsTableProps {
- data?: PaginatedSessions;
- isLoading?: boolean;
-}
-
-export function SessionsTable({ data, isLoading }: SessionsTableProps) {
- const [searchTerm, setSearchTerm] = useState('');
- const [currentPage, setCurrentPage] = useState(0);
-
- // Get sessions array (stable reference for memoization)
- const sessions = useMemo(() => data?.sessions ?? [], [data?.sessions]);
-
- // Filter sessions based on search term
- const filteredSessions = useMemo(() => {
- if (!searchTerm) return sessions;
-
- const term = searchTerm.toLowerCase();
- return sessions.filter(
- (session) =>
- session.profile.toLowerCase().includes(term) ||
- session.model.toLowerCase().includes(term) ||
- session.id.toLowerCase().includes(term)
- );
- }, [sessions, searchTerm]);
-
- // Pagination for filtered data
- const pageSize = 10;
- const paginatedSessions = useMemo(() => {
- if (!filteredSessions) return [];
- const start = currentPage * pageSize;
- return filteredSessions.slice(start, start + pageSize);
- }, [filteredSessions, currentPage]);
-
- const totalPages = Math.ceil((filteredSessions?.length || 0) / pageSize);
-
- if (isLoading) {
- return ;
- }
-
- if (!data || data.sessions.length === 0) {
- return (
-
-
-
No sessions found
-
Start using Claude Code to see session history
-
- );
- }
-
- return (
-
- {/* Search Bar */}
-
-
-
- {
- setSearchTerm(e.target.value);
- setCurrentPage(0);
- }}
- className="pl-8"
- />
-
-
-
- {/* Table */}
-
-
-
-
- Session ID
- Profile
- Model
- Duration
- Tokens
- Cost
- Requests
- Last Used
-
-
-
- {paginatedSessions.map((session) => (
-
- {session.id.slice(0, 8)}...
-
- {session.profile}
-
- {session.model}
- {session.duration ? formatDuration(session.duration) : '-'}
-
-
-
- {formatNumber(session.tokens)}
-
-
-
-
- $
- {session.cost.toFixed(4)}
-
-
- {session.requests}
-
- {formatDistanceToNow(new Date(session.startTime), { addSuffix: true })}
-
-
- ))}
-
-
-
-
- {/* Pagination */}
- {totalPages > 1 && (
-
-
- Showing {currentPage * pageSize + 1} to{' '}
- {Math.min((currentPage + 1) * pageSize, filteredSessions?.length || 0)} of{' '}
- {filteredSessions?.length} sessions
-
-
-
-
- {Array.from({ length: Math.min(5, totalPages) }, (_, i) => {
- const page = i;
- return (
-
- );
- })}
-
-
-
-
- )}
-
- );
-}
-
-// Helper functions
-function formatDuration(ms: number): string {
- const seconds = Math.floor(ms / 1000);
- const minutes = Math.floor(seconds / 60);
- const hours = Math.floor(minutes / 60);
-
- if (hours > 0) {
- return `${hours}h ${minutes % 60}m`;
- }
- if (minutes > 0) {
- return `${minutes}m ${seconds % 60}s`;
- }
- return `${seconds}s`;
-}
-
-function formatNumber(num: number): string {
- if (num >= 1000000) {
- return `${(num / 1000000).toFixed(1)}M`;
- }
- if (num >= 1000) {
- return `${(num / 1000).toFixed(1)}K`;
- }
- return num.toLocaleString();
-}
-
-// Skeleton loading state
-function SessionsTableSkeleton() {
- return (
-
-
-
-
-
-
-
-
- Session ID
- Profile
- Model
- Duration
- Tokens
- Cost
- Requests
- Last Used
-
-
-
- {[1, 2, 3, 4, 5].map((i) => (
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- ))}
-
-
-
-
- );
-}
diff --git a/ui/src/hooks/use-usage.ts b/ui/src/hooks/use-usage.ts
index 2a19800a..69dfe88e 100644
--- a/ui/src/hooks/use-usage.ts
+++ b/ui/src/hooks/use-usage.ts
@@ -175,22 +175,6 @@ export function useModelUsage(options?: UsageQueryOptions) {
});
}
-export function useSessions(options?: UsageQueryOptions) {
- return useQuery({
- queryKey: ['usage', 'sessions', options],
- queryFn: () => usageApi.sessions(options),
- staleTime: 60 * 1000, // 1 minute
- });
-}
-
-export function useMonthlyUsage(months?: number, profile?: string) {
- return useQuery({
- queryKey: ['usage', 'monthly', months, profile],
- queryFn: () => usageApi.monthly(months, profile),
- staleTime: 5 * 60 * 1000, // 5 minutes
- });
-}
-
/**
* Hook to refresh all usage data
* Clears server-side cache and invalidates React Query cache
diff --git a/ui/src/pages/analytics.tsx b/ui/src/pages/analytics.tsx
index d000c405..6f102607 100644
--- a/ui/src/pages/analytics.tsx
+++ b/ui/src/pages/analytics.tsx
@@ -1,14 +1,13 @@
/**
* Analytics Page
*
- * Displays Claude Code usage analytics with charts and tables.
- * Features daily/monthly views, trend charts, model breakdown, and session history.
+ * Displays Claude Code usage analytics with charts.
+ * Features trend charts, model breakdown, and cost analysis.
*/
import { useState, useMemo } from 'react';
import type { DateRange } from 'react-day-picker';
import { startOfMonth, subDays, formatDistanceToNow } from 'date-fns';
-import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { Button } from '@/components/ui/button';
import { Skeleton } from '@/components/ui/skeleton';
@@ -16,27 +15,22 @@ import { DateRangeFilter } from '@/components/analytics/date-range-filter';
import { UsageSummaryCards } from '@/components/analytics/usage-summary-cards';
import { UsageTrendChart } from '@/components/analytics/usage-trend-chart';
import { ModelBreakdownChart } from '@/components/analytics/model-breakdown-chart';
-import { SessionsTable } from '@/components/analytics/sessions-table';
-import { TrendingUp, PieChart, Clock, Calendar, RefreshCw } from 'lucide-react';
+import { TrendingUp, PieChart, RefreshCw } from 'lucide-react';
import {
useUsageSummary,
useUsageTrends,
useModelUsage,
- useSessions,
useRefreshUsage,
useUsageStatus,
} from '@/hooks/use-usage';
import { getModelColor } from '@/lib/utils';
-type ViewMode = 'daily' | 'monthly' | 'sessions';
-
export function AnalyticsPage() {
// Default to last 30 days
const [dateRange, setDateRange] = useState({
from: subDays(new Date(), 30),
to: new Date(),
});
- const [viewMode, setViewMode] = useState('daily');
const [isRefreshing, setIsRefreshing] = useState(false);
// Refresh hook
@@ -61,10 +55,6 @@ export function AnalyticsPage() {
const { data: summary, isLoading: isSummaryLoading } = useUsageSummary(apiOptions);
const { data: trends, isLoading: isTrendsLoading } = useUsageTrends(apiOptions);
const { data: models, isLoading: isModelsLoading } = useModelUsage(apiOptions);
- const { data: sessions, isLoading: isSessionsLoading } = useSessions({
- ...apiOptions,
- limit: 50,
- });
const { data: status } = useUsageStatus();
// Format "Last updated" text
@@ -90,6 +80,7 @@ export function AnalyticsPage() {
{ label: '7D', range: { from: subDays(new Date(), 7), to: new Date() } },
{ label: '30D', range: { from: subDays(new Date(), 30), to: new Date() } },
{ label: 'Month', range: { from: startOfMonth(new Date()), to: new Date() } },
+ { label: 'All Time', range: { from: undefined, to: new Date() } },
]}
/>
{lastUpdatedText && (
@@ -112,163 +103,101 @@ export function AnalyticsPage() {
{/* Summary Cards */}
- {/* Main Content Tabs */}
- setViewMode(v as ViewMode)}
- className="flex-1 flex flex-col min-h-0"
- >
-
-
- Daily
-
-
- Monthly
-
-
- Sessions
-
-
+ {/* Main Content */}
+
+ {/* Usage Trend Chart - Full Width */}
+
+
+
+
+ Usage Trends
+
+
+
+
+
+
-
- {/* Daily View */}
-
- {/* Usage Trend Chart - Full Width */}
-
-
-
-
- Usage Trends
-
-
-
-
-
-
-
- {/* Bottom Row - Model Usage & Cost */}
-
- {/* Model Distribution */}
-
-
-
-
- Model Usage
-
-
-
-
-
-
+ {/* Model Distribution */}
+
+
+
+
+ Model Usage
+
+
+
+
+
+
+
+
+ {models?.slice(0, 8).map((model) => (
+
+
+
+
+
+ {model.model}
+
+
+ {model.percentage.toFixed(1)}%
+
+
+
-
- {models?.slice(0, 8).map((model) => (
-
+ ))}
+
+
+
+
+
+ {/* Cost Breakdown */}
+
+
+ Cost by Model
+
+
+ {isModelsLoading ? (
+
+ ) : (
+
+ {[...(models || [])]
+ .sort((a, b) => b.cost - a.cost)
+ .map((model) => (
+
+
-
-
-
- {model.model}
-
-
- {model.percentage.toFixed(1)}%
-
-
-
+
+ {model.model}
+
- ))}
-
-
-
-
-
- {/* Cost Breakdown */}
-
-
- Cost by Model
-
-
- {isModelsLoading ? (
-
- ) : (
-
- {[...(models || [])]
- .sort((a, b) => b.cost - a.cost)
- .map((model) => (
-
-
-
- ${model.cost.toFixed(4)}
-
-
- ))}
-
- )}
-
-
-
-
-
- {/* Monthly View */}
-
-
-
-
-
- Monthly Overview
-
-
-
-
-
-
-
-
- {/* Sessions View */}
-
-
-
-
-
- Session History
-
-
-
-
-
+
+ ${model.cost.toFixed(4)}
+
+
+ ))}
-
-
-
+ )}
+
+
-
+
);