Files
admin d775d5ad85
CI / CD Pipeline / build (push) Successful in 4m43s
CI / CD Pipeline / deploy (push) Successful in 1m15s
690403:2205 Modify AI (Add Gemma4 & PaddleOCR
2026-04-03 22:05:34 +07:00

13 KiB

Task FE-AI-03: Frontend Human-in-the-Loop Interface

Phase: Step 3 - AI Verification & User Experience (Next.js) ADR Compliance: ADR-018 (AI Boundary), ADR-019 (UUID Strategy) Priority: 🔴 Critical - Human validation layer for AI outputs

Context: เป็นส่วนสำคัญที่สุดในการเปลี่ยนข้อมูลที่ AI สกัดได้ให้เป็นข้อมูลที่มีคุณภาพ (Verified Data) ตามกฎ ADR-018 โดยเน้นการสร้าง UI ที่ใช้งานง่ายสำหรับทั้ง Admin (เอกสารเก่า) และ User (เอกสารใหม่)


🖥️ Implementation Tasks

AI-3.1: Reusable AI Review Components

  • AiSuggestionField Component:

    // components/ai/ai-suggestion-field.tsx
    interface AiSuggestionFieldProps {
      value: string;
      suggestion?: string;
      confidence?: number;
      onAccept: () => void;
      onReject: () => void;
      onEdit: (newValue: string) => void;
    }
    

    Features:

    • AI icon with confidence badge ( 95%)
    • Yellow highlight for AI-suggested values
    • Accept/Reject/Edit actions
    • Tooltip showing raw AI extraction
  • DocumentComparisonView Component:

    // components/ai/document-comparison-view.tsx
    interface DocumentComparisonViewProps {
      fileUrl: string;
      extractedData: ExtractionResult;
      formData: FormData;
      onFieldUpdate: (field: string, value: string) => void;
    }
    

    Features:

    • PDF viewer sidebar (react-pdf)
    • Form fields with AI suggestions
    • Side-by-side comparison layout
    • Real-time validation feedback
  • Client-side Validation Integration:

    // Validation schema with confidence thresholds
    const documentSchema = z.object({
      subject: z.string().min(1, "จำเป็นต้องระบุชื่อเรื่อง"),
      documentDate: z.string().refine(validateThaiDate),
      discipline: z.enum(['Civil', 'Mechanical', 'Electrical', 'Architectural'])
    });
    
    // React Hook Form integration
    const form = useForm({
      resolver: zodResolver(documentSchema),
      mode: 'onChange',
      defaultValues: aiSuggestions
    });
    

AI-3.2: Legacy Migration Dashboard (Admin Interface)

  • Migration List Page:

    // app/(admin)/admin/migration/page.tsx
    interface MigrationListProps {
      status?: MigrationStatus;
      confidenceRange?: [number, number];
      dateRange?: [Date, Date];
    }
    

    Features:

    • Paginated table with sorting/filtering
    • Status badges (Pending/Verified/Failed)
    • Confidence score heat map (red/yellow/green)
    • Bulk selection for actions
  • Filter System:

    // Filter components
    const StatusFilter = () => (
      <Select value={selectedStatus} onValueChange={setSelectedStatus}>
        <SelectItem value="PENDING_REVIEW">รอตรวจสอบ</SelectItem>
        <SelectItem value="VERIFIED">ผ่านการตรวจสอบ</SelectItem>
        <SelectItem value="FAILED">ล้มเหลว</SelectItem>
      </Select>
    );
    
    const ConfidenceFilter = () => (
      <Slider
        min={0}
        max={100}
        value={confidenceRange}
        onValueChange={setConfidenceRange}
        marks={[{value: 60, label: 'ต่ำ'}, {value: 85, label: 'ปานกลาง'}, {value: 95, label: 'สูง'}]}
      />
    );
    
  • Bulk Actions Implementation:

    // Bulk verification for high-confidence items
    const handleBulkVerify = async (selectedIds: string[]) => {
      const confirmed = await confirm({
        title: "ยืนยันการนำเข้าข้อมูล",
        description: `จะยืนยันนำเข้าเอกสาร ${selectedIds.length} รายการที่มีความมั่นใจ >95% หรือไม่?`
      });
    
      if (confirmed) {
        await Promise.all(
          selectedIds.map(publicId =>
            api.migration.update(publicId, { status: 'VERIFIED' })
          )
        );
      }
    };
    
  • Error Logging UI:

    • Error details modal for failed extractions
    • OCR error screenshots
    • AI response raw text viewer
    • Retry mechanism with different parameters

AI-3.3: Real-time Ingestion Integration (User Interface)

  • RFA Creation Flow Enhancement:

    // app/(dashboard)/rfas/create/page.tsx
    const [isProcessing, setIsProcessing] = useState(false);
    const [aiSuggestions, setAiSuggestions] = useState<ExtractionResult | null>(null);
    
    const handleFileUpload = async (file: File) => {
      setIsProcessing(true);
      try {
        // 1. Upload file to temporary storage
        const uploadResult = await api.storage.uploadTemp(file);
    
        // 2. Trigger AI extraction
        const extraction = await api.ai.extract({
          publicId: uploadResult.publicId,
          context: 'ingestion'
        });
    
        // 3. Apply suggestions to form
        setAiSuggestions(extraction);
        form.reset(extraction.suggestions);
      } finally {
        setIsProcessing(false);
      }
    };
    
  • Processing State UI:

    // Loading component during AI processing
    const AiProcessingIndicator = () => (
      <Card className="border-yellow-200 bg-yellow-50">
        <CardContent className="flex items-center space-x-3 p-4">
          <Loader2 className="h-5 w-5 animate-spin text-yellow-600" />
          <div>
            <p className="font-medium text-yellow-800">AI กำลังวิเคราะห์เอกสาร...</p>
            <p className="text-sm text-yellow-600">กรุณารอสักครู่ (ประมาณ 15-30 วินาที)</p>
          </div>
        </CardContent>
      </Card>
    );
    
  • Auto-fill with User Override:

    // Form field with AI suggestion
    const FormFieldWithAi = ({ name, label }: { name: string; label: string }) => {
      const { control, watch } = useFormContext();
      const value = watch(name);
      const suggestion = aiSuggestions?.suggestions[name];
      const confidence = aiSuggestions?.confidence[name];
    
      return (
        <FormField
          control={control}
          name={name}
          render={({ field }) => (
            <FormItem>
              <FormLabel className="flex items-center gap-2">
                {label}
                {suggestion && confidence && (
                  <Badge variant="secondary" className="text-xs">
                     AI {Math.round(confidence * 100)}%
                  </Badge>
                )}
              </FormLabel>
              <FormControl>
                <Input
                  {...field}
                  className={suggestion && value === suggestion ? 'bg-yellow-50' : ''}
                />
              </FormControl>
            </FormItem>
          )}
        />
      );
    };
    
  • Raw Text Comparison Toggle:

    // Collapsible panel showing OCR text
    const OcrTextViewer = ({ extractedText }: { extractedText: string }) => (
      <Collapsible>
        <CollapsibleTrigger asChild>
          <Button variant="ghost" size="sm" className="text-blue-600">
            <Eye className="h-4 w-4 mr-2" />
            ดูข้อความดิบจาก AI
          </Button>
        </CollapsibleTrigger>
        <CollapsibleContent>
          <Card className="mt-2">
            <CardContent className="p-4">
              <pre className="text-sm bg-gray-50 p-3 rounded overflow-auto max-h-48">
                {extractedText}
              </pre>
            </CardContent>
          </Card>
        </CollapsibleContent>
      </Collapsible>
    );
    

AI-3.4: Human-AI Feedback Loop Implementation

  • Feedback Collection System:

    // Track user corrections for AI improvement
    const trackUserCorrection = async (
      field: string,
      aiSuggestion: string,
      userCorrection: string,
      documentPublicId: string
    ) => {
      await api.ai.feedback.create({
        documentPublicId,
        field,
        aiSuggestion,
        userCorrection,
        timestamp: new Date().toISOString(),
        userAgent: navigator.userAgent
      });
    };
    
  • Accuracy Analytics Dashboard:

    // Admin dashboard for AI performance
    const AiPerformanceDashboard = () => {
      const [metrics, setMetrics] = useState<PerformanceMetrics>();
    
      useEffect(() => {
        const loadMetrics = async () => {
          const data = await api.ai.analytics.getPerformance();
          setMetrics(data);
        };
        loadMetrics();
      }, []);
    
      return (
        <div className="grid grid-cols-1 md:grid-cols-3 gap-6">
          <Card>
            <CardHeader>
              <CardTitle className="text-sm">ความแม่นยำโดยรวม</CardTitle>
            </CardHeader>
            <CardContent>
              <div className="text-2xl font-bold text-green-600">
                {metrics?.overallAccuracy}%
              </div>
            </CardContent>
          </Card>
    
          <Card>
            <CardHeader>
              <CardTitle className="text-sm">อัตราการแก้ไขโดยผู้ใช้</CardTitle>
            </CardHeader>
            <CardContent>
              <div className="text-2xl font-bold text-blue-600">
                {metrics?.userCorrectionRate}%
              </div>
            </CardContent>
          </Card>
    
          <Card>
            <CardHeader>
              <CardTitle className="text-sm">เวลาประมวลผลเฉลี่ย</CardTitle>
            </CardHeader>
            <CardContent>
              <div className="text-2xl font-bold text-purple-600">
                {metrics?.avgProcessingTime}s
              </div>
            </CardContent>
          </Card>
        </div>
      );
    };
    
  • Feedback Data Structure:

    // Types for feedback collection
    interface AiFeedbackDto {
      documentPublicId: string;
      field: string;
      aiSuggestion: string;
      userCorrection: string;
      confidence: number;
      timestamp: string;
      userAgent: string;
    }
    
    interface PerformanceMetrics {
      overallAccuracy: number;
      userCorrectionRate: number;
      avgProcessingTime: number;
      fieldAccuracy: Record<string, number>;
      modelPerformance: Record<string, number>;
    }
    

🎨 UX/UI Design Guidelines

Design Principles

  • Trust through Transparency: Always show AI confidence and sources
  • Human Control First: User can override any AI suggestion
  • Progressive Disclosure: Hide complexity, show details on demand
  • Thai Language First: All UI text in Thai, engineering terms in context

Visual Indicators

// Confidence score color coding
const getConfidenceColor = (confidence: number) => {
  if (confidence >= 0.95) return 'text-green-600 bg-green-50';
  if (confidence >= 0.85) return 'text-yellow-600 bg-yellow-50';
  return 'text-red-600 bg-red-50';
};

// AI suggestion highlighting
const aiSuggestionStyles = {
  backgroundColor: '#fef3c7', // yellow-50
  borderLeft: '3px solid #f59e0b', // yellow-500
  padding: '0.5rem'
};

🔴 Critical Rules (Non-Negotiable)

  1. ADR-019 UUID Strategy:

    • All API calls use publicId (string) only
    • NEVER use integer IDs or fallback patterns
    • Type safety: publicId?: string in interfaces
  2. ADR-018 AI Boundary:

    • Frontend communicates with DMS API only
    • NO direct calls to n8n, Ollama, or PaddleOCR
    • AI processing via /api/ai/extract endpoint only
  3. Thai Language Standards:

    • All UI text in Thai (i18n keys)
    • Code comments in Thai
    • Engineering terms preserved in original language
  4. Security Requirements:

    • File uploads through StorageService only
    • Proper error handling without exposing system details
    • Rate limiting on AI endpoints
  5. Data Integrity:

    • All AI suggestions require explicit user confirmation
    • Audit trail for all user corrections
    • Validation before form submission

  • ADR-018: AI Boundary Policy - Security requirements
  • ADR-019: Hybrid Identifier Strategy - UUID usage patterns
  • ADR-020: AI Intelligence Integration - Architecture overview
  • 05-03-frontend-guidelines.md: Next.js patterns and conventions
  • 05-08-i18n-guidelines.md: Thai language implementation

📝 Component Library Usage

Shadcn/UI Components

// Required components for AI features
import {
  Card,
  CardContent,
  CardHeader,
  CardTitle,
  Badge,
  Button,
  Input,
  Select,
  Slider,
  Collapsible,
  Dialog,
  Table,
  Pagination
} from '@/components/ui';

// Custom AI components
import { AiSuggestionField } from '@/components/ai/ai-suggestion-field';
import { DocumentComparisonView } from '@/components/ai/document-comparison-view';
import { AiProcessingIndicator } from '@/components/ai/processing-indicator';

Tailwind CSS Classes

/* AI-specific utility classes */
.ai-suggestion {
  @apply bg-yellow-50 border-l-4 border-yellow-500 p-3 rounded;
}

.ai-high-confidence {
  @apply text-green-600 bg-green-50 border-green-500;
}

.ai-medium-confidence {
  @apply text-yellow-600 bg-yellow-50 border-yellow-500;
}

.ai-low-confidence {
  @apply text-red-600 bg-red-50 border-red-500;
}