/** * Admin Dashboard Page * System statistics and user management for administrators */ import { useState, useEffect } from 'react' import { useNavigate } from 'react-router-dom' import { useTranslation } from 'react-i18next' import { apiClientV2 } from '@/services/apiV2' import type { SystemStats, UserWithStats, TopUser, TranslationStats, StorageStats } from '@/types/apiV2' import { Users, ClipboardList, Activity, TrendingUp, RefreshCw, Shield, CheckCircle2, XCircle, Clock, Loader2, Languages, Coins, HardDrive, Trash2, } from 'lucide-react' import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card' import { Button } from '@/components/ui/button' import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from '@/components/ui/table' import { Badge } from '@/components/ui/badge' export default function AdminDashboardPage() { const { t, i18n } = useTranslation() const navigate = useNavigate() const [stats, setStats] = useState(null) const [users, setUsers] = useState([]) const [topUsers, setTopUsers] = useState([]) const [translationStats, setTranslationStats] = useState(null) const [storageStats, setStorageStats] = useState(null) const [cleanupLoading, setCleanupLoading] = useState(false) const [loading, setLoading] = useState(true) const [error, setError] = useState('') // Fetch all data const fetchData = async () => { try { setLoading(true) setError('') const [statsData, usersData, topUsersData, translationStatsData, storageStatsData] = await Promise.all([ apiClientV2.getSystemStats(), apiClientV2.listUsers({ page: 1, page_size: 10 }), apiClientV2.getTopUsers({ metric: 'tasks', limit: 5 }), apiClientV2.getTranslationStats(), apiClientV2.getStorageStats(), ]) setStats(statsData) setUsers(usersData.users) setTopUsers(topUsersData) setTranslationStats(translationStatsData) setStorageStats(storageStatsData) } catch (err: any) { console.error('Failed to fetch admin data:', err) setError(err.response?.data?.detail || t('admin.loadFailed')) } finally { setLoading(false) } } useEffect(() => { fetchData() }, []) // Format date based on current locale const formatDate = (dateStr: string | null) => { if (!dateStr) return '-' const date = new Date(dateStr) return date.toLocaleString(i18n.language === 'zh-TW' ? 'zh-TW' : 'en-US') } // Handle cleanup trigger const handleCleanup = async () => { try { setCleanupLoading(true) const result = await apiClientV2.triggerCleanup() alert(t('admin.storage.cleanupResult', { users: result.users_processed, files: result.total_files_deleted, mb: (result.total_bytes_freed / 1024 / 1024).toFixed(2) })) // Refresh storage stats const newStorageStats = await apiClientV2.getStorageStats() setStorageStats(newStorageStats) } catch (err: any) { console.error('Cleanup failed:', err) alert(t('admin.storage.cleanupFailed')) } finally { setCleanupLoading(false) } } if (loading) { return (

{t('admin.loadingDashboard')}

) } if (error) { return (

{t('errors.loadFailed')}

{error}

) } return (
{/* Header */}

{t('admin.title')}

{t('admin.subtitle')}

{/* System Statistics */} {stats && (
{t('admin.totalUsers')}
{stats.total_users}

{t('admin.activeUsers')}: {stats.active_users}

{t('admin.totalTasks')}
{stats.total_tasks}
{t('admin.pendingTasks')}
{stats.task_stats.pending}
{t('admin.processingTasks')}
{stats.task_stats.processing}
{t('admin.completedTasks')}
{stats.task_stats.completed}

{t('admin.failedTasks')}: {stats.task_stats.failed}

)} {/* Translation Statistics */} {translationStats && ( {t('admin.translationStats.title')} {t('admin.translationStats.description')}
{t('admin.translationStats.totalTranslations')}
{translationStats.total_translations.toLocaleString()}

{t('admin.translationStats.last30Days')}: {translationStats.last_30_days.count}

{t('admin.translationStats.totalTokens')}
{translationStats.total_tokens.toLocaleString()}

{t('admin.translationStats.last30Days')}: {translationStats.last_30_days.tokens.toLocaleString()}

{t('admin.translationStats.totalCharacters')}
{translationStats.total_characters.toLocaleString()}
{t('admin.translationStats.estimatedCost')}
${translationStats.estimated_cost.toFixed(2)}

USD

{/* Language Breakdown */} {translationStats.by_language.length > 0 && (

{t('admin.translationStats.languageBreakdown')}

{translationStats.by_language.map((lang) => ( {lang.language}: {lang.count} {t('admin.translationStats.count')} ({lang.tokens.toLocaleString()} {t('admin.translationStats.tokens')}) ))}
)} {/* Recent Translations */} {translationStats.recent_translations.length > 0 && (

{t('admin.translationStats.recentTranslations')}

{t('admin.table.taskId')} {t('admin.table.targetLang')} {t('admin.table.tokenCount')} {t('admin.table.charCount')} {t('admin.table.cost')} {t('admin.table.processingTime')} {t('admin.table.time')} {translationStats.recent_translations.slice(0, 10).map((tr) => ( {tr.task_id.substring(0, 8)}... {tr.target_lang} {tr.total_tokens.toLocaleString()} {tr.total_characters.toLocaleString()} ${tr.estimated_cost.toFixed(4)} {tr.processing_time_seconds.toFixed(1)}s {formatDate(tr.created_at)} ))}
)}
)} {/* Storage Management */} {storageStats && (
{t('admin.storage.title')} {t('admin.storage.description')}
{t('admin.storage.totalTasks')}
{storageStats.total_tasks.toLocaleString()}
{t('admin.storage.tasksWithFiles')}
{storageStats.tasks_with_files.toLocaleString()}
{t('admin.storage.filesDeleted')}
{storageStats.tasks_files_deleted.toLocaleString()}
{t('admin.storage.softDeleted')}
{storageStats.soft_deleted_tasks.toLocaleString()}
{/* Disk Usage */}

{t('admin.storage.diskUsage')}

{storageStats.disk_usage.uploads_mb} MB
{t('admin.storage.uploadsSize')}
{storageStats.disk_usage.results_mb} MB
{t('admin.storage.resultsSize')}
{storageStats.disk_usage.total_mb} MB
{t('admin.storage.totalSize')}
)} {/* Top Users */} {topUsers.length > 0 && ( {t('admin.topUsers.title')} {t('admin.topUsers.description')} # Email {t('admin.topUsers.displayName')} {t('admin.topUsers.totalTasks')} {t('admin.topUsers.completedTasks')} {topUsers.map((user, index) => ( {index + 1} {user.email} {user.display_name || '-'} {user.task_count} {user.completed_tasks} ))}
)} {/* Recent Users */} {t('admin.recentUsers.title')} {t('admin.recentUsers.description')} {users.length === 0 ? (

{t('admin.recentUsers.noUsers')}

) : ( Email {t('admin.recentUsers.displayName')} {t('admin.recentUsers.registeredAt')} {t('admin.recentUsers.lastLogin')} {t('admin.recentUsers.status')} {t('admin.recentUsers.taskCount')} {users.map((user) => ( {user.email} {user.display_name || '-'} {formatDate(user.created_at)} {formatDate(user.last_login)} {user.is_active ? t('common.active') : t('common.inactive')}
{user.task_count}
{t('admin.recentUsers.completedCount')}: {user.completed_tasks} | {t('admin.recentUsers.failedCount')}: {user.failed_tasks}
))}
)}
) }