Files
invoice-master-poc-v2/frontend/src/components/DashboardOverview.tsx
2026-02-01 22:40:41 +01:00

140 lines
4.1 KiB
TypeScript

import React from 'react'
import { FileText, CheckCircle, AlertCircle, Clock, RefreshCw } from 'lucide-react'
import {
StatsCard,
DataQualityPanel,
ActiveModelPanel,
RecentActivityPanel,
SystemStatusBar,
} from './dashboard/index'
import { useDashboard } from '../hooks/useDashboard'
interface DashboardOverviewProps {
onNavigate: (view: string) => void
}
export const DashboardOverview: React.FC<DashboardOverviewProps> = ({ onNavigate }) => {
const {
stats,
model,
runningTraining,
activities,
isLoading,
error,
} = useDashboard()
const handleStatsClick = (filter?: string) => {
if (filter) {
onNavigate(`documents?status=${filter}`)
} else {
onNavigate('documents')
}
}
if (error) {
return (
<div className="p-8 max-w-7xl mx-auto">
<div className="bg-red-50 border border-red-200 rounded-lg p-6 text-center">
<AlertCircle className="w-12 h-12 text-red-500 mx-auto mb-4" />
<h2 className="text-lg font-semibold text-red-800 mb-2">
Failed to load dashboard
</h2>
<p className="text-sm text-red-600 mb-4">
{error instanceof Error ? error.message : 'An unexpected error occurred'}
</p>
<button
onClick={() => window.location.reload()}
className="inline-flex items-center gap-2 px-4 py-2 bg-red-100 hover:bg-red-200 text-red-800 rounded-md text-sm font-medium transition-colors"
>
<RefreshCw className="w-4 h-4" />
Retry
</button>
</div>
</div>
)
}
return (
<div className="p-8 max-w-7xl mx-auto animate-fade-in">
{/* Header */}
<div className="mb-8">
<h1 className="text-3xl font-bold text-warm-text-primary tracking-tight">
Dashboard
</h1>
<p className="text-sm text-warm-text-muted mt-1">
Overview of your document annotation system
</p>
</div>
{/* Stats Cards Row */}
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 mb-8">
<StatsCard
label="Total Documents"
value={stats?.total_documents ?? 0}
icon={FileText}
iconColor="text-warm-text-primary"
iconBgColor="bg-warm-bg"
isLoading={isLoading}
onClick={() => handleStatsClick()}
/>
<StatsCard
label="Complete"
value={stats?.annotation_complete ?? 0}
icon={CheckCircle}
iconColor="text-warm-state-success"
iconBgColor="bg-green-50"
isLoading={isLoading}
onClick={() => handleStatsClick('labeled')}
/>
<StatsCard
label="Incomplete"
value={stats?.annotation_incomplete ?? 0}
icon={AlertCircle}
iconColor="text-orange-600"
iconBgColor="bg-orange-50"
isLoading={isLoading}
onClick={() => handleStatsClick('labeled')}
/>
<StatsCard
label="Pending"
value={stats?.pending ?? 0}
icon={Clock}
iconColor="text-blue-600"
iconBgColor="bg-blue-50"
isLoading={isLoading}
onClick={() => handleStatsClick('pending')}
/>
</div>
{/* Two-column layout: Data Quality + Active Model */}
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6 mb-8">
<DataQualityPanel
completenessRate={stats?.completeness_rate ?? 0}
completeCount={stats?.annotation_complete ?? 0}
incompleteCount={stats?.annotation_incomplete ?? 0}
pendingCount={stats?.pending ?? 0}
isLoading={isLoading}
onViewIncomplete={() => handleStatsClick('labeled')}
/>
<ActiveModelPanel
model={model}
runningTraining={runningTraining}
isLoading={isLoading}
onGoToTraining={() => onNavigate('training')}
/>
</div>
{/* Recent Activity */}
<div className="mb-8">
<RecentActivityPanel
activities={activities}
isLoading={isLoading}
/>
</div>
{/* System Status */}
<SystemStatusBar />
</div>
)
}