Files
OCR/FRONTEND_API.md
beabigegg 9cf36d8e21 fix: resolve 7 frontend-backend API inconsistencies and add comprehensive documentation
- Fixed LoginResponse: added expires_in field
- Renamed format to file_format across FileInfo interface
- Updated ProcessRequest: replaced confidence_threshold with detect_layout
- Modified ProcessResponse: added message and total_files, removed task_id
- Removed non-existent getTaskStatus API call
- Fixed getOCRResult parameter from taskId to fileId
- Commented out translation config APIs pending backend implementation

Documentation:
- Added API_REFERENCE.md: Complete API endpoint inventory
- Added API_FIX_SUMMARY.md: Detailed before/after comparison of all fixes
- Added FRONTEND_API.md: Frontend integration documentation
- Added FRONTEND_QUICK_START.md: Quick start guide
- Added FRONTEND_UPGRADE_SUMMARY.md: Upgrade summary

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-13 08:54:37 +08:00

22 KiB

Tool_OCR Frontend API Documentation

Version: 0.1.0 Last Updated: 2025-01-13 Purpose: Complete documentation of frontend architecture, component structure, API integration, and dependencies


Table of Contents

  1. Project Overview
  2. Technology Stack
  3. Component Architecture
  4. Page → API Dependency Matrix
  5. Component Tree Structure
  6. State Management Strategy
  7. Route Configuration
  8. API Integration Patterns
  9. UI/UX Design System
  10. Error Handling Patterns
  11. Deployment Configuration

Project Overview

Tool_OCR 前端是一個基於 React 18 + Vite 的現代化 OCR 文件處理系統,提供企業級的使用者介面和體驗。

Key Features

  • 批次檔案上傳: 支援拖放上傳,多檔案批次處理
  • 即時進度追蹤: 使用輪詢機制顯示 OCR 處理進度
  • 結果預覽: Markdown 和 JSON 雙格式預覽
  • 靈活匯出: 支援 TXT、JSON、Excel、Markdown、PDF、ZIP 多種格式
  • 規則管理: 可自訂匯出規則和 CSS 模板
  • 響應式設計: 適配桌面和平板裝置

Technology Stack

Core Dependencies

{
  "@tanstack/react-query": "^5.90.7",    // Server state management
  "react": "^19.2.0",                     // UI framework
  "react-dom": "^19.2.0",
  "react-router-dom": "^7.9.5",           // Routing
  "vite": "^7.2.2",                       // Build tool
  "typescript": "~5.9.3"                  // Type safety
}

UI & Styling

{
  "tailwindcss": "^4.1.17",               // CSS framework
  "class-variance-authority": "^0.7.0",   // Component variants
  "clsx": "^2.1.1",                       // Class name utility
  "tailwind-merge": "^3.4.0",             // Tailwind class merge
  "lucide-react": "^0.553.0"              // Icon library
}

State & Data

{
  "zustand": "^5.0.8",                    // Client state
  "axios": "^1.13.2",                     // HTTP client
  "react-dropzone": "^14.3.8",            // File upload
  "react-markdown": "^9.0.1"              // Markdown rendering
}

Internationalization

{
  "i18next": "^25.6.2",
  "react-i18next": "^16.3.0"
}

Component Architecture

Atomic Design Structure

frontend/src/
├── components/
│   ├── ui/                 # Atomic components (shadcn/ui)
│   │   ├── button.tsx
│   │   ├── card.tsx
│   │   ├── input.tsx
│   │   ├── label.tsx
│   │   ├── select.tsx
│   │   ├── badge.tsx
│   │   ├── progress.tsx
│   │   ├── alert.tsx
│   │   ├── dialog.tsx
│   │   ├── tabs.tsx
│   │   ├── table.tsx
│   │   └── toast.tsx
│   ├── FileUpload.tsx      # Drag-and-drop upload component
│   ├── ResultsTable.tsx    # OCR results display table
│   ├── MarkdownPreview.tsx # Markdown content renderer
│   └── Layout.tsx          # Main app layout with sidebar
├── pages/
│   ├── LoginPage.tsx       # Authentication
│   ├── UploadPage.tsx      # File upload and selection
│   ├── ProcessingPage.tsx  # OCR processing status
│   ├── ResultsPage.tsx     # Results viewing and preview
│   ├── ExportPage.tsx      # Export configuration and download
│   └── SettingsPage.tsx    # User settings and rules management
├── store/
│   ├── authStore.ts        # Authentication state (Zustand)
│   └── uploadStore.ts      # Upload batch state (Zustand)
├── services/
│   └── api.ts              # API client (Axios)
├── types/
│   └── api.ts              # TypeScript type definitions
├── lib/
│   └── utils.ts            # Utility functions
├── i18n/
│   └── index.ts            # i18n configuration
└── styles/
    └── index.css           # Global styles and CSS variables

Page → API Dependency Matrix

Page/Component API Endpoints Used HTTP Method Purpose Polling
LoginPage /api/v1/auth/login POST User authentication No
UploadPage /api/v1/upload POST Upload files for OCR No
ProcessingPage /api/v1/ocr/process POST Start OCR processing No
/api/v1/batch/{batch_id}/status GET Poll batch status Yes (2s)
ResultsPage /api/v1/batch/{batch_id}/status GET Load completed files No
/api/v1/ocr/result/{file_id} GET Get OCR result details No
/api/v1/export/pdf/{file_id} GET Download PDF export No
ExportPage /api/v1/export POST Export batch results No
/api/v1/export/rules GET List export rules No
/api/v1/export/rules POST Create new rule No
/api/v1/export/rules/{rule_id} PUT Update existing rule No
/api/v1/export/rules/{rule_id} DELETE Delete rule No
/api/v1/export/css-templates GET List CSS templates No
SettingsPage /api/v1/export/rules GET Manage export rules No

Component Tree Structure

App
├── Router (React Router)
│   ├── PublicRoute
│   │   └── LoginPage
│   │       ├── Form (username + password)
│   │       ├── Button (submit)
│   │       └── Alert (error display)
│   └── ProtectedRoute (requires authentication)
│       └── Layout
│           ├── Sidebar
│           │   ├── Logo
│           │   ├── Navigation Links
│           │   │   ├── UploadPage link
│           │   │   ├── ProcessingPage link
│           │   │   ├── ResultsPage link
│           │   │   ├── ExportPage link
│           │   │   └── SettingsPage link
│           │   └── User Section + Logout
│           ├── TopBar
│           │   ├── SearchInput
│           │   └── NotificationBell
│           └── MainContent (Outlet)
│               ├── UploadPage
│               │   ├── FileUpload (react-dropzone)
│               │   ├── FileList (selected files)
│               │   └── UploadButton
│               ├── ProcessingPage
│               │   ├── ProgressBar
│               │   ├── StatsCards (completed/processing/failed)
│               │   ├── FileStatusList
│               │   └── ActionButtons
│               ├── ResultsPage
│               │   ├── FileList (left sidebar)
│               │   │   ├── SearchInput
│               │   │   └── FileItems
│               │   └── PreviewPanel (right)
│               │       ├── StatsCards
│               │       ├── Tabs (Markdown/JSON)
│               │       ├── MarkdownPreview
│               │       └── JSONViewer
│               ├── ExportPage
│               │   ├── FormatSelector
│               │   ├── RuleSelector
│               │   ├── CSSTemplateSelector
│               │   ├── OptionsForm
│               │   └── ExportButton
│               └── SettingsPage
│                   ├── UserInfo
│                   ├── ExportRulesManager
│                   │   ├── RuleList
│                   │   ├── CreateRuleDialog
│                   │   ├── EditRuleDialog
│                   │   └── DeleteConfirmDialog
│                   └── SystemSettings

State Management Strategy

Client State (Zustand)

authStore.ts - Authentication State

interface AuthState {
  user: User | null
  isAuthenticated: boolean
  setUser: (user: User | null) => void
  logout: () => void
}

uploadStore.ts - Upload Batch State

interface UploadState {
  batchId: number | null
  files: FileInfo[]
  uploadProgress: number
  setBatchId: (id: number) => void
  setFiles: (files: FileInfo[]) => void
  setUploadProgress: (progress: number) => void
  reset: () => void
}

Server State (React Query)

  • Caching: Automatic caching with stale-while-revalidate strategy
  • Polling: Automatic refetch for batch status every 2 seconds during processing
  • Error Handling: Built-in error retry and error state management
  • Optimistic Updates: For export rules CRUD operations

Query Keys

// Batch status polling
['batchStatus', batchId]

// OCR result for specific file
['ocrResult', fileId]

// Export rules list
['exportRules']

// CSS templates list
['cssTemplates']

Route Configuration

Route Component Access Level Description Protected
/login LoginPage Public User authentication No
/ Layout (redirect to /upload) Private Main layout wrapper Yes
/upload UploadPage Private File upload interface Yes
/processing ProcessingPage Private OCR processing status Yes
/results ResultsPage Private View OCR results Yes
/export ExportPage Private Export configuration Yes
/settings SettingsPage Private User settings Yes

Protected Route Implementation

function ProtectedRoute({ children }: { children: React.ReactNode }) {
  const isAuthenticated = useAuthStore((state) => state.isAuthenticated)

  if (!isAuthenticated) {
    return <Navigate to="/login" replace />
  }

  return <>{children}</>
}

API Integration Patterns

API Client Configuration

Base URL: http://localhost:12010/api/v1

Request Interceptor: Adds JWT token to Authorization header

this.client.interceptors.request.use((config) => {
  if (this.token) {
    config.headers.Authorization = `Bearer ${this.token}`
  }
  return config
})

Response Interceptor: Handles 401 errors and redirects to login

this.client.interceptors.response.use(
  (response) => response,
  (error: AxiosError<ApiError>) => {
    if (error.response?.status === 401) {
      this.clearToken()
      window.location.href = '/login'
    }
    return Promise.reject(error)
  }
)

Authentication Flow

// 1. Login
const response = await apiClient.login({ username, password })
// Response: { access_token, token_type, expires_in }

// 2. Store token
localStorage.setItem('auth_token', response.access_token)

// 3. Set user in store
setUser({ id: 1, username })

// 4. Navigate to /upload
navigate('/upload')

File Upload Flow

// 1. Prepare FormData
const formData = new FormData()
files.forEach((file) => formData.append('files', file))

// 2. Upload files
const response = await apiClient.uploadFiles(files)
// Response: { batch_id, files: FileInfo[] }

// 3. Store batch info
setBatchId(response.batch_id)
setFiles(response.files)

// 4. Navigate to /processing
navigate('/processing')

OCR Processing Flow

// 1. Start OCR processing
await apiClient.processOCR({ batch_id, lang: 'ch', detect_layout: true })
// Response: { message, batch_id, total_files, status }

// 2. Poll batch status every 2 seconds
const { data: batchStatus } = useQuery({
  queryKey: ['batchStatus', batchId],
  queryFn: () => apiClient.getBatchStatus(batchId),
  refetchInterval: (query) => {
    const status = query.state.data?.batch.status
    if (status === 'completed' || status === 'failed') return false
    return 2000 // Poll every 2 seconds
  },
})

// 3. Auto-redirect when completed
useEffect(() => {
  if (batchStatus?.batch.status === 'completed') {
    navigate('/results')
  }
}, [batchStatus?.batch.status])

Results Viewing Flow

// 1. Load batch status
const { data: batchStatus } = useQuery({
  queryKey: ['batchStatus', batchId],
  queryFn: () => apiClient.getBatchStatus(batchId),
})

// 2. Select a file
setSelectedFileId(fileId)

// 3. Load OCR result for selected file
const { data: ocrResult } = useQuery({
  queryKey: ['ocrResult', selectedFileId],
  queryFn: () => apiClient.getOCRResult(selectedFileId),
  enabled: !!selectedFileId,
})

// 4. Display in Markdown or JSON format
<Tabs>
  <TabsContent value="markdown">
    <ReactMarkdown>{ocrResult.markdown_content}</ReactMarkdown>
  </TabsContent>
  <TabsContent value="json">
    <pre>{JSON.stringify(ocrResult.json_data, null, 2)}</pre>
  </TabsContent>
</Tabs>

Export Flow

// 1. Select export format and options
const exportData = {
  batch_id: batchId,
  format: 'pdf',
  rule_id: selectedRuleId,
  css_template: 'academic',
  options: { include_metadata: true }
}

// 2. Request export
const blob = await apiClient.exportResults(exportData)

// 3. Trigger download
downloadBlob(blob, `ocr-results-${batchId}.pdf`)

UI/UX Design System

Color Palette (CSS Variables)

/* Primary - Professional Blue */
--primary: 217 91% 60%;           /* #3b82f6 */
--primary-foreground: 0 0% 100%;

/* Secondary - Gray-Blue */
--secondary: 220 15% 95%;
--secondary-foreground: 220 15% 25%;

/* Accent - Vibrant Teal */
--accent: 173 80% 50%;
--accent-foreground: 0 0% 100%;

/* Success */
--success: 142 72% 45%;           /* #16a34a */
--success-foreground: 0 0% 100%;

/* Destructive */
--destructive: 0 85% 60%;         /* #ef4444 */
--destructive-foreground: 0 0% 100%;

/* Warning */
--warning: 38 92% 50%;
--warning-foreground: 0 0% 100%;

/* Background */
--background: 220 15% 97%;        /* #fafafa */
--card: 0 0% 100%;                /* #ffffff */
--sidebar: 220 25% 12%;           /* Dark blue-gray */

/* Borders */
--border: 220 13% 88%;
--radius: 0.5rem;

Typography

  • Font Family: System font stack (native)
  • Page Title: 1.875rem (30px), font-weight: 700
  • Section Title: 1.125rem (18px), font-weight: 600
  • Body Text: 0.875rem (14px), font-weight: 400
  • Small Text: 0.75rem (12px)

Spacing Scale

--spacing-xs: 0.25rem;   /* 4px */
--spacing-sm: 0.5rem;    /* 8px */
--spacing-md: 1rem;      /* 16px */
--spacing-lg: 1.5rem;    /* 24px */
--spacing-xl: 2rem;      /* 32px */

Component Variants

Button Variants:

  • default: Primary blue background
  • outline: Border only
  • secondary: Muted background
  • destructive: Red for delete actions
  • ghost: No background, hover effect

Alert Variants:

  • default: Neutral gray
  • info: Blue
  • success: Green
  • warning: Yellow
  • destructive: Red

Badge Variants:

  • default: Gray
  • success: Green
  • warning: Yellow
  • destructive: Red
  • secondary: Muted

Responsive Breakpoints

// Tailwind breakpoints
sm: '640px',    // Mobile landscape
md: '768px',    // Tablet
lg: '1024px',   // Desktop (primary support)
xl: '1280px',   // Large desktop
2xl: '1536px'   // Extra large

Primary Support: Desktop (>= 1024px) Secondary Support: Tablet (768px - 1023px) Optional: Mobile (< 768px)


Error Handling Patterns

Global Error Boundary

class ErrorBoundary extends Component<Props, State> {
  static getDerivedStateFromError(error: Error): State {
    return { hasError: true, error }
  }

  componentDidCatch(error: Error, errorInfo: ErrorInfo) {
    console.error('Uncaught error:', error, errorInfo)
  }

  render() {
    if (this.state.hasError) {
      return <ErrorFallbackUI error={this.state.error} />
    }
    return this.props.children
  }
}

API Error Handling

try {
  await apiClient.uploadFiles(files)
} catch (err: any) {
  const errorDetail = err.response?.data?.detail

  toast({
    title: t('upload.uploadError'),
    description: Array.isArray(errorDetail)
      ? errorDetail.map(e => e.msg || e.message).join(', ')
      : errorDetail || t('errors.networkError'),
    variant: 'destructive',
  })
}

Form Validation

// Client-side validation
if (selectedFiles.length === 0) {
  toast({
    title: t('errors.validationError'),
    description: '請選擇至少一個檔案',
    variant: 'destructive',
  })
  return
}

// Backend validation errors
if (err.response?.status === 422) {
  const errors = err.response.data.detail
  // Display validation errors to user
}

Loading States

// Query loading state
const { data, isLoading, error } = useQuery({
  queryKey: ['batchStatus', batchId],
  queryFn: () => apiClient.getBatchStatus(batchId),
})

if (isLoading) return <LoadingSpinner />
if (error) return <ErrorAlert error={error} />
if (!data) return <EmptyState />

// Mutation loading state
const mutation = useMutation({
  mutationFn: apiClient.uploadFiles,
  onSuccess: () => { /* success */ },
  onError: () => { /* error */ },
})

<Button disabled={mutation.isPending}>
  {mutation.isPending ? <Loader2 className="animate-spin" /> : '上傳'}
</Button>

Deployment Configuration

Environment Variables

# .env.production
VITE_API_BASE_URL=http://localhost:12010
VITE_APP_NAME=Tool_OCR
VITE_APP_VERSION=0.1.0

Build Configuration

vite.config.ts:

export default defineConfig({
  plugins: [react()],
  server: {
    port: 12011,
    proxy: {
      '/api': {
        target: 'http://localhost:12010',
        changeOrigin: true,
      },
    },
  },
  build: {
    outDir: 'dist',
    sourcemap: false,
    rollupOptions: {
      output: {
        manualChunks: {
          vendor: ['react', 'react-dom', 'react-router-dom'],
          ui: ['@tanstack/react-query', 'zustand', 'lucide-react'],
        },
      },
    },
  },
})

Build Commands

# Development
npm run dev

# Production build
npm run build

# Preview production build
npm run preview

Nginx Configuration

server {
    listen 80;
    server_name tool-ocr.example.com;
    root /path/to/Tool_OCR/frontend/dist;

    # Frontend static files
    location / {
        try_files $uri $uri/ /index.html;
    }

    # API reverse proxy
    location /api {
        proxy_pass http://127.0.0.1:12010;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }

    # Static assets caching
    location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2)$ {
        expires 1y;
        add_header Cache-Control "public, immutable";
    }
}

Performance Optimization

Code Splitting

  • Vendor Bundle: React, React Router, React Query (separate chunk)
  • UI Bundle: Zustand, Lucide React, UI components
  • Route-based Splitting: Lazy load pages with React.lazy()

Caching Strategy

  • React Query Cache: 5 minutes stale time for most queries
  • Polling Interval: 2 seconds during OCR processing
  • Infinite Cache: Export rules (rarely change)

Asset Optimization

  • Images: Convert to WebP format, use appropriate sizes
  • Fonts: System font stack (no custom fonts)
  • Icons: Lucide React (tree-shakeable)

Testing Strategy

Component Testing (Planned)

// Example: UploadPage.test.tsx
import { render, screen, fireEvent } from '@testing-library/react'
import { UploadPage } from '@/pages/UploadPage'

describe('UploadPage', () => {
  it('should display file upload area', () => {
    render(<UploadPage />)
    expect(screen.getByText(/拖放檔案/i)).toBeInTheDocument()
  })

  it('should allow file selection', async () => {
    render(<UploadPage />)
    const file = new File(['content'], 'test.pdf', { type: 'application/pdf' })
    // Test file upload
  })
})

API Integration Testing

  • Mock API Responses: Use MSW (Mock Service Worker)
  • Error Scenarios: Test 401, 404, 500 responses
  • Loading States: Test skeleton/spinner display

Accessibility Standards

WCAG 2.1 AA Compliance

  • Keyboard Navigation: All interactive elements accessible via keyboard
  • Focus Indicators: Visible focus states on all inputs and buttons
  • ARIA Labels: Proper labels for screen readers
  • Color Contrast: Minimum 4.5:1 ratio for text
  • Alt Text: All images have descriptive alt attributes

Semantic HTML

// Use semantic elements
<nav>      // Navigation
<main>     // Main content
<aside>    // Sidebar
<article>  // Independent content
<section>  // Grouped content

Browser Compatibility

Minimum Supported Versions

  • Chrome: 90+
  • Firefox: 88+
  • Edge: 90+
  • Safari: 14+

Polyfills Required

  • None (modern build target: ES2020)

Development Workflow

Local Development

# 1. Install dependencies
npm install

# 2. Start dev server
npm run dev
# Frontend: http://localhost:12011
# API Proxy: http://localhost:12011/api -> http://localhost:12010/api

# 3. Build for production
npm run build

# 4. Preview production build
npm run preview

Code Style

  • Formatter: Prettier (automatic on save)
  • Linter: ESLint
  • Type Checking: TypeScript strict mode

Known Issues & Limitations

Current Limitations

  1. No Real-time WebSocket: Uses HTTP polling for progress updates
  2. No Offline Support: Requires active internet connection
  3. No Mobile Optimization: Primarily designed for desktop/tablet
  4. Translation Feature Stub: Planned for Phase 5
  5. File Size Limit: Frontend validates 50MB per file, backend may differ

Future Improvements

  • Implement WebSocket for real-time updates
  • Add dark mode toggle
  • Mobile responsive design
  • Implement translation feature
  • Add E2E tests with Playwright
  • PWA support for offline capability

Maintenance & Updates

Update Checklist

When updating API contracts:

  1. Update TypeScript types in @/types/api.ts
  2. Update API client methods in @/services/api.ts
  3. Update this documentation (FRONTEND_API.md)
  4. Update corresponding page components
  5. Test integration thoroughly

Dependency Updates

# Check for updates
npm outdated

# Update dependencies
npm update

# Update to latest (breaking changes possible)
npm install <package>@latest

Contact & Support

Frontend Developer: Claude Code Documentation Version: 0.1.0 Last Updated: 2025-01-13

For API questions, refer to:

  • API_REFERENCE.md - Complete API documentation
  • backend_api.md - Backend implementation details
  • FastAPI Swagger UI: http://localhost:12010/docs

End of Documentation