"use client"; import { useState, useCallback, useRef } from "react"; import { useTranslations } from "next-intl"; import { PageLayout } from "@/components/ui/PageLayout"; import { PrimaryButton, LightButton } from "@/design-system/base/button"; import { Input } from "@/design-system/base/input"; import { Select } from "@/design-system/base/select"; import { Card } from "@/design-system/base/card"; import { toast } from "sonner"; import { Upload, FileImage, Loader2 } from "lucide-react"; import { actionProcessOCR } from "@/modules/ocr/ocr-action"; import type { ActionOutputDeck } from "@/modules/deck/deck-action-dto"; interface ActionOutputProcessOCR { success: boolean; message: string; data?: { pairsCreated: number; sourceLanguage?: string; targetLanguage?: string; }; } interface OCRClientProps { initialDecks: ActionOutputDeck[]; } export function OCRClient({ initialDecks }: OCRClientProps) { const t = useTranslations("ocr"); const fileInputRef = useRef(null); const [decks, setDecks] = useState(initialDecks); const [selectedFile, setSelectedFile] = useState(null); const [previewUrl, setPreviewUrl] = useState(null); const [selectedDeckId, setSelectedDeckId] = useState( initialDecks.length > 0 ? initialDecks[0].id : null ); const [sourceLanguage, setSourceLanguage] = useState(""); const [targetLanguage, setTargetLanguage] = useState(""); const [isProcessing, setIsProcessing] = useState(false); const [ocrResult, setOcrResult] = useState(null); const handleFileChange = useCallback((file: File | null) => { if (!file) return; if (!file.type.startsWith("image/")) { toast.error(t("invalidFileType")); return; } const url = URL.createObjectURL(file); setPreviewUrl(url); setSelectedFile(file); setOcrResult(null); }, [t]); const handleDrop = useCallback((e: React.DragEvent) => { e.preventDefault(); const file = e.dataTransfer.files[0]; handleFileChange(file); }, [handleFileChange]); const handleDragOver = useCallback((e: React.DragEvent) => { e.preventDefault(); }, []); const fileToBase64 = async (file: File): Promise => { return new Promise((resolve, reject) => { const reader = new FileReader(); reader.onload = () => { const result = reader.result as string; const base64 = result.split(",")[1]; resolve(base64); }; reader.onerror = reject; reader.readAsDataURL(file); }); }; const handleProcess = async () => { if (!selectedFile) { toast.error(t("noImage")); return; } if (!selectedDeckId) { toast.error(t("noDeck")); return; } setIsProcessing(true); setOcrResult(null); try { const base64 = await fileToBase64(selectedFile); const result = await actionProcessOCR({ imageBase64: base64, deckId: selectedDeckId, sourceLanguage: sourceLanguage || undefined, targetLanguage: targetLanguage || undefined, }); if (result.success && result.data) { setOcrResult(result); const deckName = decks.find(d => d.id === selectedDeckId)?.name || ""; toast.success(t("ocrSuccess", { count: result.data.pairsCreated, deck: deckName })); } else { toast.error(result.message || t("ocrFailed")); } } catch { toast.error(t("processingFailed")); } finally { setIsProcessing(false); } }; const handleSave = async () => { if (!ocrResult || !selectedDeckId) { toast.error(t("noResultsToSave")); return; } try { const deckName = decks.find(d => d.id === selectedDeckId)?.name || "Unknown"; toast.success(t("savedToDeck", { deckName })); } catch (error) { toast.error(t("saveFailed")); } }; const clearImage = () => { if (previewUrl) { URL.revokeObjectURL(previewUrl); } setPreviewUrl(null); setSelectedFile(null); setOcrResult(null); if (fileInputRef.current) { fileInputRef.current.value = ""; } }; return (

{t("title")}

{t("description")}

{/* Upload Section */}

{t("uploadSection")}

fileInputRef.current?.click()} className="border-2 border-dashed border-gray-300 rounded-lg p-8 text-center cursor-pointer hover:border-primary-500 hover:bg-primary-50 transition-colors" > {previewUrl ? (
Preview

{t("changeImage")}

) : (

{t("dropOrClick")}

{t("supportedFormats")}

)}
handleFileChange(e.target.files?.[0] || null)} className="hidden" />
{/* Deck Selection */}

{t("deckSelection")}

{/* Language Hints */}

{t("languageHints")}

setSourceLanguage(e.target.value)} className="w-full" /> setTargetLanguage(e.target.value)} className="w-full" />
{/* Process Button */}
{t("processButton")}
{/* Results Preview */} {ocrResult && ocrResult.data && (

{t("resultsPreview")}

{t("extractedPairs", { count: ocrResult.data.pairsCreated })}

{ocrResult.data.sourceLanguage && (
{t("detectedSourceLanguage")}: {ocrResult.data.sourceLanguage}
)} {ocrResult.data.targetLanguage && (
{t("detectedTargetLanguage")}: {ocrResult.data.targetLanguage}
)}
{t("saveButton")}
)}
); }