// app/admin/stats-client.tsx 'use client'; import React, { useEffect, useState } from 'react'; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from '@/components/ui/table'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { Badge } from '@/components/ui/badge'; import { Skeleton } from '@/components/ui/skeleton'; import { AlertCircle } from 'lucide-react'; interface VideoStats { id: string; title: string; playlistTitle: string; uploaderName: string; views: number; totalViewers: number; completions: number; completionRate: string; likes: number; comments: number; engagement: number; avgPercentWatched: number; totalSecondsWatched: number; totalSegments: number; durationSec: number | null; createdAt: string; } export default function StatsClient() { const [stats, setStats] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [sortBy, setSortBy] = useState('views'); const [sortOrder, setSortOrder] = useState<'asc' | 'desc'>('desc'); useEffect(() => { const fetchStats = async () => { try { setLoading(true); const response = await fetch('/api/admin/stats'); if (!response.ok) throw new Error('Failed to fetch stats'); const data = await response.json(); setStats(data); } catch (err) { setError(err instanceof Error ? err.message : 'An error occurred'); } finally { setLoading(false); } }; fetchStats(); }, []); const handleSort = (column: keyof VideoStats) => { if (sortBy === column) { setSortOrder(sortOrder === 'asc' ? 'desc' : 'asc'); } else { setSortBy(column); setSortOrder('desc'); } }; const sortedStats = [...stats].sort((a, b) => { const aVal = a[sortBy]; const bVal = b[sortBy]; if (typeof aVal === 'string' && typeof bVal === 'string') { return sortOrder === 'asc' ? aVal.localeCompare(bVal) : bVal.localeCompare(aVal); } const aNum = typeof aVal === 'number' ? aVal : 0; const bNum = typeof bVal === 'number' ? bVal : 0; return sortOrder === 'asc' ? aNum - bNum : bNum - aNum; }); const formatDuration = (seconds: number | null) => { if (!seconds) return 'N/A'; const hours = Math.floor(seconds / 3600); const minutes = Math.floor((seconds % 3600) / 60); const secs = seconds % 60; return `${hours}h ${minutes}m ${secs}s`; }; const SortHeader = ({ column, label }: { column: keyof VideoStats; label: string }) => ( handleSort(column)} >
{label} {sortBy === column && ( {sortOrder === 'asc' ? '↑' : '↓'} )}
); if (error) { return (

Error loading stats

{error}

); } return (

Video Statistics

Performance metrics for all videos in the system

{stats.length} Videos
{/* Summary Cards */}
Total Views
{stats.reduce((sum, s) => sum + s.views, 0).toLocaleString()}
Unique Viewers
{stats.reduce((sum, s) => sum + s.totalViewers, 0).toLocaleString()}
Total Engagements
{stats.reduce((sum, s) => sum + s.engagement, 0).toLocaleString()}

Likes + Comments

Avg. Completion Rate
{( stats.reduce((sum, s) => sum + parseFloat(s.completionRate), 0) / (stats.length || 1) ).toFixed(1)} %
{/* Data Table */} Video Performance Details
{loading ? (
{[...Array(5)].map((_, i) => ( ))}
) : stats.length === 0 ? (
No videos found
) : ( {sortedStats.map((video) => (
{video.title} {video.playlistTitle}
{video.uploaderName} {video.views} {video.totalViewers} {video.completions} = 50 ? 'default' : parseFloat(video.completionRate) >= 25 ? 'secondary' : 'destructive' } > {video.completionRate}% {video.avgPercentWatched.toFixed(1)}% {video.engagement} {video.likes} {video.comments} {video.totalSecondsWatched.toLocaleString()}s {formatDuration(video.durationSec)}
))}
)}
); }