'use client' import { useEffect, useState } from 'react' import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card' import { ChartContainer, ChartTooltip, ChartTooltipContent, ChartLegend, ChartLegendContent, type ChartConfig, } from '@/components/ui/chart' import { Bar, BarChart, CartesianGrid, XAxis, YAxis } from 'recharts' import { Badge } from '@/components/ui/badge' import { PaginationControls } from '@/components/pagination-controls' import { usePagination } from '@/hooks/use-pagination' import { formatCost, formatNumber } from '@/lib/format' import { Camera, Zap, Clock, TrendingUp, Image as ImageIcon, } from 'lucide-react' interface ImageInfo { filename: string sessionId: string timestamp: string sizeBytes: number project: string } interface ImageAnalysis { overview: { totalImages: number sessionsWithImages: number totalSessions: number percentWithImages: number totalSizeMB: number avgSizeKB: number byMediaType: Record } byProject: { name: string; count: number }[] byHour: { hour: number; count: number }[] byDate: { date: string; count: number }[] comparison: { withImages: { sessions: number; avgMessages: number; avgCost: number; avgDuration: number; avgTools: number } withoutImages: { sessions: number; avgMessages: number; avgCost: number; avgDuration: number; avgTools: number } } screenshotFrequency: { totalGaps: number medianMinutes: number meanMinutes: number rapidFireCount: number rapidFirePercent: number under5Count: number under5Percent: number } topSessions: { sessionId: string imageCount: number project: string messages: number cost: number date: string }[] allImages: ImageInfo[] } const projectConfig = { count: { label: 'Images', color: 'var(--chart-3)' }, } satisfies ChartConfig const hourConfig = { count: { label: 'Images', color: 'var(--chart-1)' }, } satisfies ChartConfig const dateConfig = { count: { label: 'Images', color: 'var(--chart-8)' }, } satisfies ChartConfig const comparisonConfig = { withImages: { label: 'With Images', color: 'var(--chart-1)' }, withoutImages: { label: 'Text Only', color: 'var(--chart-4)' }, } satisfies ChartConfig export function ScreenshotsAnalysis() { const [data, setData] = useState(null) const [loading, setLoading] = useState(true) useEffect(() => { fetch('/api/images-analysis') .then((r) => r.json()) .then((d) => { setData(d); setLoading(false) }) .catch(() => setLoading(false)) }, []) if (loading) return
Loading image analysis...
if (!data) return
No image data available
const { overview, comparison, screenshotFrequency } = data const costMultiplier = comparison.withoutImages.avgCost > 0 ? (comparison.withImages.avgCost / comparison.withoutImages.avgCost).toFixed(1) : '?' const msgMultiplier = comparison.withoutImages.avgMessages > 0 ? (comparison.withImages.avgMessages / comparison.withoutImages.avgMessages).toFixed(1) : '?' const comparisonData = [ { metric: 'Avg Cost', withImages: comparison.withImages.avgCost, withoutImages: comparison.withoutImages.avgCost, }, { metric: 'Avg Messages', withImages: comparison.withImages.avgMessages, withoutImages: comparison.withoutImages.avgMessages, }, { metric: 'Avg Tools', withImages: comparison.withImages.avgTools, withoutImages: comparison.withoutImages.avgTools, }, ] return (
{/* Hero insight */}

Sessions with Images Cost {costMultiplier}x More

Sessions where you shared images averaged {formatCost(comparison.withImages.avgCost)} vs{' '} {formatCost(comparison.withoutImages.avgCost)} for text-only sessions. They also had {msgMultiplier}x more messages and{' '} {Math.round(comparison.withImages.avgTools / Math.max(1, comparison.withoutImages.avgTools))}x more tool calls. Images signal complex visual work — UI debugging, design review, and iterative refinement.

{/* Stat cards */}
Total Images
{formatNumber(overview.totalImages)}

{overview.totalSizeMB} MB on disk

Sessions with Images
{overview.percentWithImages}%

{overview.sessionsWithImages} of {overview.totalSessions}

Avg Size
{overview.avgSizeKB} KB

{overview.byMediaType['image/png'] || 0} PNG, {overview.byMediaType['image/jpeg'] || 0} JPEG

Rapid Fire
{screenshotFrequency.rapidFirePercent}%

{screenshotFrequency.rapidFireCount} sent <2 min apart

Median Gap
{screenshotFrequency.medianMinutes} min

Between consecutive images

{/* Comparison chart + By hour */}
Image vs Text-Only Sessions Average metrics comparison } /> } /> Images by Hour When do you share images? `${h}:00`} /> `${h}:00 - ${h}:59`} /> } />
{/* Daily timeline + By project */}
Daily Image Activity Images shared per day } /> Images by Project Which projects need the most visual feedback? } />
{/* Most image-heavy sessions */} Most Image-Heavy Sessions Your biggest visual collaboration marathons
{data.topSessions.map((s, i) => (
#{i + 1} {s.date}
{s.imageCount}
images
{s.project} {' · '}{formatCost(s.cost)}{' · '}{formatNumber(s.messages)} msgs
))}
{/* All images grid with pagination */}
) } function ImageGallery({ images }: { images: ImageInfo[] }) { const pagination = usePagination(images, 16) if (images.length === 0) return null return ( All Images {images.length} images across all sessions
{pagination.pageItems.map((img) => (
{`Image
{img.project} {new Date(img.timestamp).toLocaleDateString(undefined, { month: 'short', day: 'numeric' })} {Math.round(img.sizeBytes / 1024)} KB
))}
) }