'use client'; import { useEffect, useState, useCallback } from 'react'; import { aiService } from '@/lib/services/ai.service'; import { migrationService } from '@/lib/services/migration.service'; import { AiMigrationLog, AiMigrationLogStatus } from '@/types/ai'; import { MigrationReviewQueueItem, MigrationReviewStatus } from '@/types/migration'; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table'; import { Checkbox } from '@/components/ui/checkbox'; import { Badge } from '@/components/ui/badge'; import { toast } from 'sonner'; import { Button } from '@/components/ui/button'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'; import { Textarea } from '@/components/ui/textarea'; import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'; import { Sheet, SheetContent, SheetHeader, SheetTitle } from '@/components/ui/sheet'; import { format } from 'date-fns'; import { EyeIcon, FileXIcon, CheckCircleIcon, XCircleIcon, RefreshCwIcon } from 'lucide-react'; import Link from 'next/link'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { getApiErrorMessage } from '@/types/api-error'; import { v4 as uuidv4 } from 'uuid'; // --- AI Migration Tab --- function AiMigrationTab() { const [items, setItems] = useState([]); const [loading, setLoading] = useState(true); const [submitting, setSubmitting] = useState(false); const [statusFilter, setStatusFilter] = useState('PENDING_REVIEW'); // ADR-019: ใช้ publicId (string) สำหรับ selection const [selectedPublicIds, setSelectedPublicIds] = useState([]); const [errorMessage, setErrorMessage] = useState(null); // Sheet สำหรับ inline review const [reviewItem, setReviewItem] = useState(null); const [adminFeedback, setAdminFeedback] = useState(''); const fetchData = useCallback(async () => { try { setLoading(true); setErrorMessage(null); const res = await aiService.getMigrationList({ status: statusFilter === 'ALL' ? undefined : (statusFilter as AiMigrationLogStatus), limit: 50, }); setItems(Array.isArray(res.items) ? res.items : []); setSelectedPublicIds([]); } catch (error: unknown) { setItems([]); setErrorMessage(getApiErrorMessage(error, 'ไม่สามารถโหลดข้อมูล AI Migration Logs ได้')); } finally { setLoading(false); } }, [statusFilter]); useEffect(() => { fetchData(); }, [fetchData]); // ADR-019: toggle โดยใช้ publicId (string) ไม่ใช่ numeric id const handleToggleSelectAll = () => { if (selectedPublicIds.length === items.length) { setSelectedPublicIds([]); } else { setSelectedPublicIds(items.map((i) => i.publicId)); } }; const handleToggleSelect = (publicId: string) => { setSelectedPublicIds((prev) => prev.includes(publicId) ? prev.filter((id) => id !== publicId) : [...prev, publicId] ); }; // Bulk verify รายการที่เลือก (ADR-019: ใช้ publicId) const handleBulkVerify = async () => { if (selectedPublicIds.length === 0) return; try { setSubmitting(true); await Promise.all( selectedPublicIds.map((publicId) => aiService.updateMigration( publicId, // ADR-019: UUID เท่านั้น { status: AiMigrationLogStatus.VERIFIED }, `bulk-verify-${publicId}-${uuidv4()}` ) ) ); toast.success(`ยืนยัน ${selectedPublicIds.length} รายการเรียบร้อย`); await fetchData(); } catch (_error) { toast.error('การยืนยันแบบกลุ่มล้มเหลว'); } finally { setSubmitting(false); } }; // อัปเดตสถานะ item เดี่ยว (ADR-019: ใช้ publicId) const handleUpdateStatus = async (status: AiMigrationLogStatus) => { if (!reviewItem) return; try { setSubmitting(true); await aiService.updateMigration( reviewItem.publicId, // ADR-019: UUID เท่านั้น { status, adminFeedback: adminFeedback || undefined }, `review-${reviewItem.publicId}-${uuidv4()}` ); const label = status === AiMigrationLogStatus.VERIFIED ? 'ยืนยัน' : 'ปฏิเสธ'; toast.success(`${label}เอกสารเรียบร้อย`); setReviewItem(null); setAdminFeedback(''); await fetchData(); } catch (_error) { toast.error('ไม่สามารถอัปเดตสถานะได้'); } finally { setSubmitting(false); } }; // สีของ confidence badge const getConfidenceVariant = (score?: number): 'default' | 'secondary' | 'destructive' | 'outline' => { if (!score) return 'destructive'; if (score >= 0.95) return 'default'; if (score >= 0.75) return 'secondary'; return 'destructive'; }; // สีของ status badge const getStatusVariant = (status: AiMigrationLogStatus): 'default' | 'secondary' | 'destructive' | 'outline' => { switch (status) { case AiMigrationLogStatus.VERIFIED: case AiMigrationLogStatus.IMPORTED: return 'default'; case AiMigrationLogStatus.FAILED: return 'destructive'; default: return 'outline'; } }; const statusLabels: Record = { PENDING_REVIEW: 'รอตรวจสอบ', VERIFIED: 'ผ่านการตรวจสอบ', IMPORTED: 'นำเข้าแล้ว', FAILED: 'ล้มเหลว', }; return ( <>
AI Migration Logs
{selectedPublicIds.length > 0 && ( )}
{errorMessage && (
{errorMessage}
)} {loading ? (
กำลังโหลด...
) : items.length === 0 ? (
ไม่มีรายการ
) : (
0 && selectedPublicIds.length === items.length} onCheckedChange={handleToggleSelectAll} aria-label="เลือกทั้งหมด" /> ไฟล์ต้นทาง ความมั่นใจ AI สถานะ วันที่สร้าง การดำเนินการ {items.map((item) => ( // ADR-019: ใช้ publicId เป็น key handleToggleSelect(item.publicId)} aria-label={`เลือก ${item.publicId}`} /> {item.sourceFile} {item.confidenceScore ? (item.confidenceScore * 100).toFixed(1) + '%' : 'N/A'} {statusLabels[item.status] ?? item.status} {format(new Date(item.createdAt), 'dd MMM yyyy, HH:mm')} ))}
)}
{/* Inline Review Sheet */} { if (!open) { setReviewItem(null); setAdminFeedback(''); } }} > ตรวจสอบ AI Migration Log {reviewItem && (

Public ID (ADR-019)

{reviewItem.publicId}

ไฟล์ต้นทาง

{reviewItem.sourceFile}

ความมั่นใจ AI

{reviewItem.confidenceScore ? (reviewItem.confidenceScore * 100).toFixed(1) + '%' : 'N/A'}
{reviewItem.aiExtractedMetadata && Object.keys(reviewItem.aiExtractedMetadata).length > 0 && (

ข้อมูลที่ AI สกัดได้

{Object.entries(reviewItem.aiExtractedMetadata).map(([k, v]) => (
{k}: {String(v)}
))}
)}