# Dashboard Design Specification

## Overview

Dashboard 是用户进入系统后的第一个页面，用于快速了解：
- 数据标注质量和进度
- 当前模型状态和性能
- 系统最近发生的活动

**目标用户**：使用文档标注系统的客户，需要监控文档处理状态、标注质量和模型训练进度。

---

## 1. UI Layout

### 1.1 Overall Structure

```
+------------------------------------------------------------------+
|  Header: Logo + Navigation + User Menu                            |
+------------------------------------------------------------------+
|                                                                    |
|  Stats Cards Row (4 cards, equal width)                           |
|                                                                    |
|  +---------------------------+  +------------------------------+  |
|  | Data Quality Panel (50%)  |  | Active Model Panel (50%)     |  |
|  +---------------------------+  +------------------------------+  |
|                                                                    |
|  +--------------------------------------------------------------+  |
|  | Recent Activity Panel (full width)                            |  |
|  +--------------------------------------------------------------+  |
|                                                                    |
|  +--------------------------------------------------------------+  |
|  | System Status Bar (full width)                                |  |
|  +--------------------------------------------------------------+  |
+------------------------------------------------------------------+
```

### 1.2 Responsive Breakpoints

| Breakpoint | Layout |
|------------|--------|
| Desktop (>1200px) | 4 cards row, 2-column panels |
| Tablet (768-1200px) | 2x2 cards, 2-column panels |
| Mobile (<768px) | 1 card per row, stacked panels |

---

## 2. Component Specifications

### 2.1 Stats Cards Row

4 个等宽卡片，显示核心统计数据。

```
+-------------+ +-------------+ +-------------+ +-------------+
|   [icon]    | |   [icon]    | |   [icon]    | |   [icon]    |
|     38      | |     25      | |      8      | |      5      |
| Total Docs  | |  Complete   | | Incomplete  | |   Pending   |
+-------------+ +-------------+ +-------------+ +-------------+
```

| Card | Icon | Value | Label | Color | Click Action |
|------|------|-------|-------|-------|--------------|
| Total Documents | FileText | `total_documents` | "Total Documents" | Gray | Navigate to Documents page |
| Complete | CheckCircle | `annotation_complete` | "Complete" | Green | Navigate to Documents (filter: complete) |
| Incomplete | AlertCircle | `annotation_incomplete` | "Incomplete" | Orange | Navigate to Documents (filter: incomplete) |
| Pending | Clock | `pending` | "Pending" | Blue | Navigate to Documents (filter: pending) |

**Card Design:**
- Background: White with subtle border
- Icon: 24px, positioned top-left
- Value: 32px bold font
- Label: 14px muted color
- Hover: Slight shadow elevation
- Padding: 16px

### 2.2 Data Quality Panel

左侧面板，显示标注完整度和质量指标。

```
+---------------------------+
| DATA QUALITY              |
| +-----------+             |
| |           |             |
| |   78%     |  Annotation |
| |           |  Complete   |
| +-----------+             |
|                           |
| Complete: 25              |
| Incomplete: 8             |
| Pending: 5                |
|                           |
| [View Incomplete Docs]    |
+---------------------------+
```

**Components:**

| Element | Spec |
|---------|------|
| Title | "DATA QUALITY", 14px uppercase, muted |
| Progress Ring | 120px diameter, stroke width 12px |
| Percentage | 36px bold, centered in ring |
| Label | "Annotation Complete", 14px, below ring |
| Stats List | 14px, icon + label + value per row |
| Action Button | Text button, primary color |

**Progress Ring Colors:**
- Complete portion: Green (#22C55E)
- Remaining: Gray (#E5E7EB)

**Completeness Calculation:**
```
completeness_rate = annotation_complete / (annotation_complete + annotation_incomplete) * 100
```

### 2.3 Active Model Panel

右侧面板，显示当前生产模型信息。

```
+-------------------------------+
| ACTIVE MODEL                  |
|                               |
| v1.2.0 - Invoice Model        |
| ----------------------------- |
|                               |
| mAP        Precision  Recall  |
| 95.1%      94%       92%      |
|                               |
| Activated: 2024-01-20         |
| Documents: 500                |
|                               |
| [Training] Run-2024-02 [====] |
+-------------------------------+
```

**Components:**

| Element | Spec |
|---------|------|
| Title | "ACTIVE MODEL", 14px uppercase, muted |
| Version + Name | 18px bold (version) + 16px regular (name) |
| Divider | 1px border, full width |
| Metrics Row | 3 columns, equal width |
| Metric Value | 24px bold |
| Metric Label | 12px muted, below value |
| Info Rows | 14px, label: value format |
| Training Indicator | Shows when training is running |

**Metric Colors:**
- mAP >= 90%: Green
- mAP 80-90%: Yellow
- mAP < 80%: Red

**Empty State (No Active Model):**
```
+-------------------------------+
| ACTIVE MODEL                  |
|                               |
|    [icon: Model]              |
|    No Active Model            |
|                               |
|    Train and activate a       |
|    model to see stats here    |
|                               |
|    [Go to Training]           |
+-------------------------------+
```

**Training In Progress:**
```
| Training: Run-2024-02         |
| [=========>          ] 45%    |
| Started 2 hours ago           |
```

### 2.4 Recent Activity Panel

全宽面板，显示最近 10 条系统活动。

```
+--------------------------------------------------------------+
| RECENT ACTIVITY                                     [See All] |
+--------------------------------------------------------------+
| [rocket] Activated model v1.2.0                    2 hours ago|
| [check]  Training complete: Run-2024-01, mAP 95.1%   yesterday|
| [edit]   Modified INV-001.pdf invoice_number         yesterday|
| [doc]    Uploaded INV-005.pdf                        2 days ago|
| [doc]    Uploaded INV-004.pdf                        2 days ago|
| [x]      Training failed: Run-2024-00                3 days ago|
+--------------------------------------------------------------+
```

**Activity Item Layout:**

```
[Icon] [Description]                              [Timestamp]
```

| Element | Spec |
|---------|------|
| Icon | 16px, color based on type |
| Description | 14px, truncate if too long |
| Timestamp | 12px muted, right-aligned |
| Row Height | 40px |
| Hover | Background highlight |

**Activity Types and Icons:**

| Type | Icon | Color | Description Format |
|------|------|-------|-------------------|
| document_uploaded | FileText | Blue | "Uploaded {filename}" |
| annotation_modified | Edit | Orange | "Modified {filename} {field_name}" |
| training_completed | CheckCircle | Green | "Training complete: {task_name}, mAP {mAP}%" |
| training_failed | XCircle | Red | "Training failed: {task_name}" |
| model_activated | Rocket | Purple | "Activated model {version}" |

**Timestamp Formatting:**
- < 1 minute: "just now"
- < 1 hour: "{n} minutes ago"
- < 24 hours: "{n} hours ago"
- < 7 days: "yesterday" / "{n} days ago"
- >= 7 days: "Jan 15" (date format)

**Empty State:**
```
+--------------------------------------------------------------+
| RECENT ACTIVITY                                               |
|                                                              |
|              [icon: Activity]                                 |
|              No recent activity                              |
|                                                              |
|    Start by uploading documents or creating training jobs    |
+--------------------------------------------------------------+
```

### 2.5 System Status Bar

底部状态栏，显示系统健康状态。

```
+--------------------------------------------------------------+
| Backend API: [*] Online   Database: [*] Connected   GPU: [*] Available |
+--------------------------------------------------------------+
```

| Status | Icon | Color |
|--------|------|-------|
| Online/Connected/Available | Filled circle | Green |
| Degraded/Slow | Filled circle | Yellow |
| Offline/Error/Unavailable | Filled circle | Red |

---

## 3. API Endpoints

### 3.1 Dashboard Statistics

```
GET /api/v1/admin/dashboard/stats
```

**Response:**
```json
{
  "total_documents": 38,
  "annotation_complete": 25,
  "annotation_incomplete": 8,
  "pending": 5,
  "completeness_rate": 75.76
}
```

**Calculation Logic:**

```python
# annotation_complete: labeled documents with core fields
SELECT COUNT(*) FROM admin_documents d
WHERE d.status = 'labeled'
AND EXISTS (
    SELECT 1 FROM admin_annotations a
    WHERE a.document_id = d.document_id
    AND a.class_id IN (0, 3)  -- invoice_number OR ocr_number
)
AND EXISTS (
    SELECT 1 FROM admin_annotations a
    WHERE a.document_id = d.document_id
    AND a.class_id IN (4, 5)  -- bankgiro OR plusgiro
)

# annotation_incomplete: labeled but missing core fields
SELECT COUNT(*) FROM admin_documents d
WHERE d.status = 'labeled'
AND NOT (/* above conditions */)

# pending: pending + auto_labeling
SELECT COUNT(*) FROM admin_documents
WHERE status IN ('pending', 'auto_labeling')
```

### 3.2 Active Model Info

```
GET /api/v1/admin/dashboard/active-model
```

**Response (with active model):**
```json
{
  "model": {
    "version_id": "uuid",
    "version": "1.2.0",
    "name": "Invoice Model",
    "metrics_mAP": 0.951,
    "metrics_precision": 0.94,
    "metrics_recall": 0.92,
    "document_count": 500,
    "activated_at": "2024-01-20T15:00:00Z"
  },
  "running_training": {
    "task_id": "uuid",
    "name": "Run-2024-02",
    "status": "running",
    "started_at": "2024-01-25T10:00:00Z",
    "progress": 45
  }
}
```

**Response (no active model):**
```json
{
  "model": null,
  "running_training": null
}
```

### 3.3 Recent Activity

```
GET /api/v1/admin/dashboard/activity?limit=10
```

**Response:**
```json
{
  "activities": [
    {
      "type": "model_activated",
      "description": "Activated model v1.2.0",
      "timestamp": "2024-01-25T12:00:00Z",
      "metadata": {
        "version_id": "uuid",
        "version": "1.2.0"
      }
    },
    {
      "type": "training_completed",
      "description": "Training complete: Run-2024-01, mAP 95.1%",
      "timestamp": "2024-01-24T18:30:00Z",
      "metadata": {
        "task_id": "uuid",
        "task_name": "Run-2024-01",
        "mAP": 0.951
      }
    }
  ]
}
```

**Activity Aggregation Query:**

```sql
-- Union all activity sources, ordered by timestamp DESC, limit 10
(
  SELECT 'document_uploaded' as type,
         filename as entity_name,
         created_at as timestamp,
         document_id as entity_id
  FROM admin_documents
  ORDER BY created_at DESC
  LIMIT 10
)
UNION ALL
(
  SELECT 'annotation_modified' as type,
         -- join to get filename and field name
         ...
  FROM annotation_history
  ORDER BY created_at DESC
  LIMIT 10
)
UNION ALL
(
  SELECT CASE WHEN status = 'completed' THEN 'training_completed'
              WHEN status = 'failed' THEN 'training_failed' END as type,
         name as entity_name,
         completed_at as timestamp,
         task_id as entity_id
  FROM training_tasks
  WHERE status IN ('completed', 'failed')
  ORDER BY completed_at DESC
  LIMIT 10
)
UNION ALL
(
  SELECT 'model_activated' as type,
         version as entity_name,
         activated_at as timestamp,
         version_id as entity_id
  FROM model_versions
  WHERE activated_at IS NOT NULL
  ORDER BY activated_at DESC
  LIMIT 10
)
ORDER BY timestamp DESC
LIMIT 10
```

---

## 4. UX Interactions

### 4.1 Loading States

| Component | Loading State |
|-----------|--------------|
| Stats Cards | Skeleton placeholder (gray boxes) |
| Data Quality Ring | Skeleton circle |
| Active Model | Skeleton lines |
| Recent Activity | Skeleton list items (5 rows) |

**Loading Duration Thresholds:**
- < 300ms: No loading state shown
- 300ms - 3s: Show skeleton
- > 3s: Show skeleton + "Taking longer than expected" message

### 4.2 Error States

| Error Type | Display |
|------------|---------|
| API Error | Toast notification + retry button in affected panel |
| Network Error | Full page overlay with retry option |
| Partial Failure | Show available data, error badge on failed sections |

### 4.3 Refresh Behavior

| Trigger | Behavior |
|---------|----------|
| Page Load | Fetch all data |
| Manual Refresh | Button in header, refetch all |
| Auto Refresh | Every 30 seconds for activity panel |
| Focus Return | Refetch if page was hidden > 5 minutes |

### 4.4 Click Actions

| Element | Action |
|---------|--------|
| Total Documents card | Navigate to `/documents` |
| Complete card | Navigate to `/documents?filter=complete` |
| Incomplete card | Navigate to `/documents?filter=incomplete` |
| Pending card | Navigate to `/documents?filter=pending` |
| "View Incomplete Docs" button | Navigate to `/documents?filter=incomplete` |
| Activity item | Navigate to related entity |
| "Go to Training" button | Navigate to `/training` |
| Active Model version | Navigate to `/models/{version_id}` |

### 4.5 Tooltips

| Element | Tooltip Content |
|---------|----------------|
| Completeness % | "25 of 33 labeled documents have complete annotations" |
| mAP metric | "Mean Average Precision at IoU 0.5" |
| Precision metric | "Proportion of correct positive predictions" |
| Recall metric | "Proportion of actual positives correctly identified" |
| Incomplete count | "Documents labeled but missing invoice_number/ocr_number or bankgiro/plusgiro" |

---

## 5. Data Model

### 5.1 TypeScript Types

```typescript
// Dashboard Stats
interface DashboardStats {
  total_documents: number;
  annotation_complete: number;
  annotation_incomplete: number;
  pending: number;
  completeness_rate: number;
}

// Active Model
interface ActiveModelInfo {
  model: ModelVersion | null;
  running_training: RunningTraining | null;
}

interface ModelVersion {
  version_id: string;
  version: string;
  name: string;
  metrics_mAP: number;
  metrics_precision: number;
  metrics_recall: number;
  document_count: number;
  activated_at: string;
}

interface RunningTraining {
  task_id: string;
  name: string;
  status: 'running';
  started_at: string;
  progress: number;
}

// Activity
interface Activity {
  type: ActivityType;
  description: string;
  timestamp: string;
  metadata: Record<string, unknown>;
}

type ActivityType =
  | 'document_uploaded'
  | 'annotation_modified'
  | 'training_completed'
  | 'training_failed'
  | 'model_activated';

// Activity Response
interface ActivityResponse {
  activities: Activity[];
}
```

### 5.2 React Query Hooks

```typescript
// useDashboardStats
const useDashboardStats = () => {
  return useQuery({
    queryKey: ['dashboard', 'stats'],
    queryFn: () => api.get('/admin/dashboard/stats'),
    refetchInterval: 30000, // 30 seconds
  });
};

// useActiveModel
const useActiveModel = () => {
  return useQuery({
    queryKey: ['dashboard', 'active-model'],
    queryFn: () => api.get('/admin/dashboard/active-model'),
    refetchInterval: 60000, // 1 minute
  });
};

// useRecentActivity
const useRecentActivity = (limit = 10) => {
  return useQuery({
    queryKey: ['dashboard', 'activity', limit],
    queryFn: () => api.get(`/admin/dashboard/activity?limit=${limit}`),
    refetchInterval: 30000,
  });
};
```

---

## 6. Annotation Completeness Definition

### 6.1 Core Fields

A document is **complete** when it has annotations for:

| Requirement | Fields | Logic |
|-------------|--------|-------|
| Identifier | `invoice_number` (class_id=0) OR `ocr_number` (class_id=3) | At least one |
| Payment Account | `bankgiro` (class_id=4) OR `plusgiro` (class_id=5) | At least one |

### 6.2 Status Categories

| Category | Criteria |
|----------|----------|
| **Complete** | status=labeled AND has identifier AND has payment account |
| **Incomplete** | status=labeled AND (missing identifier OR missing payment account) |
| **Pending** | status IN (pending, auto_labeling) |

### 6.3 Filter Implementation

```sql
-- Complete documents
WHERE status = 'labeled'
AND document_id IN (
  SELECT document_id FROM admin_annotations WHERE class_id IN (0, 3)
)
AND document_id IN (
  SELECT document_id FROM admin_annotations WHERE class_id IN (4, 5)
)

-- Incomplete documents
WHERE status = 'labeled'
AND (
  document_id NOT IN (
    SELECT document_id FROM admin_annotations WHERE class_id IN (0, 3)
  )
  OR document_id NOT IN (
    SELECT document_id FROM admin_annotations WHERE class_id IN (4, 5)
  )
)
```

---

## 7. Implementation Checklist

### Backend
- [ ] Create `/api/v1/admin/dashboard/stats` endpoint
- [ ] Create `/api/v1/admin/dashboard/active-model` endpoint
- [ ] Create `/api/v1/admin/dashboard/activity` endpoint
- [ ] Add completeness calculation logic to document repository
- [ ] Implement activity aggregation query

### Frontend
- [ ] Create `DashboardOverview` component
- [ ] Create `StatsCard` component
- [ ] Create `DataQualityPanel` component with progress ring
- [ ] Create `ActiveModelPanel` component
- [ ] Create `RecentActivityPanel` component
- [ ] Create `SystemStatusBar` component
- [ ] Add React Query hooks for dashboard data
- [ ] Implement loading skeletons
- [ ] Implement error states
- [ ] Add navigation actions
- [ ] Add tooltips

### Testing
- [ ] Unit tests for completeness calculation
- [ ] Unit tests for activity aggregation
- [ ] Integration tests for dashboard endpoints
- [ ] E2E tests for dashboard interactions
