import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { useDropzone } from 'react-dropzone'; import { Box, Button, Card, CardActionArea, CardContent, CardMedia, ClickAwayListener, Divider, Grid, LinearProgress, Paper, Stack, TextField, Typography, } from '@mui/material'; import { useNavigate } from 'react-router-dom'; import { useAppStore } from '../store/useAppStore'; import { extractTextFromEpub } from '../utils/epubImport'; const dropzoneStyle = { border: '2px dashed rgba(108, 155, 207, 0.7)', borderRadius: 12, padding: '32px', textAlign: 'center', backgroundColor: 'rgba(108, 155, 207, 0.08)', cursor: 'pointer', }; const UploadStep = () => { const navigate = useNavigate(); const uploadedImages = useAppStore((state) => state.uploadedImages); const setUploadedImages = useAppStore((state) => state.setUploadedImages); const resetFromStep = useAppStore((state) => state.resetFromStep); const coverImageId = useAppStore((state) => state.coverImageId); const setCoverImageId = useAppStore((state) => state.setCoverImageId); const bookTitle = useAppStore((state) => state.bookTitle); const setBookTitle = useAppStore((state) => state.setBookTitle); const bookMetadata = useAppStore((state) => state.bookMetadata); const setBookMetadata = useAppStore((state) => state.setBookMetadata); const epubImports = useAppStore((state) => state.epubImports); const setEpubImports = useAppStore((state) => state.setEpubImports); const setOcrText = useAppStore((state) => state.setOcrText); const clearTranslation = useAppStore((state) => state.clearTranslation); const setError = useAppStore((state) => state.setError); const [searchResults, setSearchResults] = useState([]); const [searching, setSearching] = useState(false); const [searchError, setSearchError] = useState(null); const [selectedBookId, setSelectedBookId] = useState(bookMetadata?.id || null); const skipSearchRef = useRef(false); const [showResults, setShowResults] = useState(false); const [epubProcessing, setEpubProcessing] = useState(false); const onDrop = useCallback( async (acceptedFiles) => { if (!acceptedFiles.length) return; setEpubProcessing(true); const preservedMetadata = bookMetadata; const preservedTitle = bookTitle; resetFromStep('upload'); if (preservedMetadata) { skipSearchRef.current = true; setBookMetadata(preservedMetadata); setBookTitle(preservedTitle || preservedMetadata.title || ''); } else if (preservedTitle?.trim()) { skipSearchRef.current = true; setBookTitle(preservedTitle); } const imageFiles = []; const epubFiles = []; acceptedFiles.forEach((file) => { const isEpub = file.type === 'application/epub+zip' || file.name?.toLowerCase().endsWith('.epub'); if (isEpub) { epubFiles.push(file); } else { imageFiles.push(file); } }); if (imageFiles.length) { const mapped = imageFiles.map((file, index) => ({ id: crypto.randomUUID(), file, previewUrl: URL.createObjectURL(file), order: uploadedImages.length + index, filename: file.name, })); setUploadedImages([...uploadedImages, ...mapped]); } const importedEntries = []; if (epubFiles.length) { for (const file of epubFiles) { try { // eslint-disable-next-line no-await-in-loop const parsed = await extractTextFromEpub(file); importedEntries.push({ id: crypto.randomUUID(), filename: file.name, size: file.size, text: parsed.text, metadata: parsed.metadata, }); } catch (error) { setError(error.message || `${file.name} okunamadı.`); } } setEpubImports(importedEntries); clearTranslation(); const combinedText = importedEntries.map((entry) => entry.text).filter(Boolean).join('\n\n'); if (combinedText) { setOcrText(combinedText); } if (!preservedMetadata && importedEntries[0]?.metadata) { const meta = importedEntries[0].metadata; setBookMetadata({ id: `epub-${crypto.randomUUID()}`, title: meta.title || bookTitle || 'İsimsiz EPUB', subtitle: '', authors: meta.authors || [], publisher: meta.publisher || '', publishedDate: meta.publishedDate || '', description: meta.description || '', pageCount: null, categories: meta.categories || [], averageRating: null, ratingsCount: null, language: meta.language || '', infoLink: '', identifiers: meta.identifiers || [], thumbnail: null, }); if (!preservedTitle?.trim() && meta.title) { skipSearchRef.current = true; setBookTitle(meta.title); } } } else { setEpubImports([]); } setEpubProcessing(false); }, [ bookMetadata, bookTitle, clearTranslation, resetFromStep, setBookMetadata, setBookTitle, setEpubImports, setError, setOcrText, setUploadedImages, uploadedImages, ], ); const handleCoverToggle = (imageId) => { const nextId = coverImageId === imageId ? null : imageId; setCoverImageId(nextId); }; useEffect(() => { setSelectedBookId(bookMetadata?.id || null); }, [bookMetadata]); const normalizeVolume = useCallback((volume) => { const info = volume?.volumeInfo || {}; const identifiers = Array.isArray(info.industryIdentifiers) ? info.industryIdentifiers.map((identifier) => ({ type: identifier?.type, identifier: identifier?.identifier, })) : []; const thumbnail = info.imageLinks?.thumbnail?.replace('http://', 'https://') || info.imageLinks?.smallThumbnail?.replace('http://', 'https://') || null; return { id: volume.id, title: info.title || 'İsimsiz kitap', subtitle: info.subtitle || '', authors: info.authors || [], publisher: info.publisher || '', publishedDate: info.publishedDate || '', description: info.description || '', pageCount: info.pageCount || null, categories: info.categories || [], averageRating: info.averageRating || null, ratingsCount: info.ratingsCount || null, language: info.language || '', infoLink: info.infoLink || info.previewLink || '', identifiers, thumbnail, }; }, []); useEffect(() => { if (skipSearchRef.current) { skipSearchRef.current = false; return; } const query = bookTitle?.trim(); if (!query) { setSearchResults([]); setSearchError(null); setSearching(false); setShowResults(false); return; } const controller = new AbortController(); const timer = setTimeout(async () => { setSearching(true); setSearchError(null); try { const response = await fetch( `https://www.googleapis.com/books/v1/volumes?q=intitle:${encodeURIComponent(query)}&maxResults=5&printType=books`, { signal: controller.signal }, ); if (!response.ok) { throw new Error('Google Books sonuçları alınamadı.'); } const payload = await response.json(); const items = Array.isArray(payload.items) ? payload.items : []; const normalized = items.map((item) => normalizeVolume(item)); setSearchResults(normalized); setShowResults(Boolean(normalized.length)); if (!normalized.length) { setSearchError('Bu başlıkla eşleşen bir kayıt bulunamadı.'); } } catch (error) { if (controller.signal.aborted) return; setSearchResults([]); setSearchError(error.message || 'Google Books araması başarısız oldu.'); setShowResults(false); } finally { if (!controller.signal.aborted) { setSearching(false); } } }, 500); return () => { clearTimeout(timer); controller.abort(); }; }, [bookTitle, normalizeVolume]); const { getRootProps, getInputProps, isDragActive } = useDropzone({ onDrop, accept: { 'image/png': ['.png'], 'image/jpeg': ['.jpg', '.jpeg'], 'image/webp': ['.webp'], 'application/epub+zip': ['.epub'], 'application/zip': ['.epub'], 'application/x-zip-compressed': ['.epub'], 'application/octet-stream': ['.epub'], }, multiple: true, }); const handleTitleChange = (event) => { const value = event.target.value; setBookTitle(value); if (!value?.trim()) { setBookMetadata(null); setSelectedBookId(null); setSearchResults([]); setSearchError(null); setShowResults(false); } else if (bookMetadata && bookMetadata.title !== value) { setBookMetadata(null); setSelectedBookId(null); } setShowResults(Boolean(value?.trim())); }; const handleSelectBook = (book) => { skipSearchRef.current = true; setSelectedBookId(book.id); setBookMetadata(book); setBookTitle(book.title || ''); setSearchResults([]); setSearchError(null); setShowResults(false); }; const selectedBookSummary = useMemo(() => { if (!bookMetadata) return null; const authorsLine = bookMetadata.authors?.length ? bookMetadata.authors.join(', ') : null; const details = [ bookMetadata.publisher, bookMetadata.publishedDate, bookMetadata.pageCount ? `${bookMetadata.pageCount} sayfa` : null, ] .filter(Boolean) .join(' • '); return { authorsLine, details }; }, [bookMetadata]); const hasImages = uploadedImages.length > 0; const hasEpubImports = epubImports.length > 0; const canProceed = hasImages || hasEpubImports; const nextPath = hasImages ? '/crop' : '/ocr'; return ( {bookMetadata && ( Seçilen kitap: {bookMetadata.title} {bookMetadata.authors?.length ? ` • ${bookMetadata.authors.join(', ')}` : ''} )} Kitap adı setShowResults(false)}> searchResults.length && setShowResults(true)} InputProps={{ sx: { borderRadius: 2 } }} /> {showResults && searchResults.length > 0 && bookTitle?.trim() && ( theme.shadows[6], }} > Google Books sonuçları } spacing={0}> {searchResults.map((book) => { const detailLine = [ book.publisher, book.publishedDate, book.pageCount ? `${book.pageCount} sayfa` : null, ] .filter(Boolean) .join(' • '); const ratingLine = [ book.averageRating ? `Puan ${book.averageRating}/5` : null, book.ratingsCount ? `${book.ratingsCount} oy` : null, book.language ? book.language.toUpperCase() : null, ] .filter(Boolean) .join(' • '); const isSelected = selectedBookId === book.id; return ( handleSelectBook(book)} sx={{ px: 2, py: 2, display: 'flex', gap: 2, alignItems: 'flex-start', cursor: 'pointer', bgcolor: isSelected ? 'rgba(231,193,121,0.15)' : 'transparent', transition: 'background-color 0.2s ease', }} > {book.thumbnail ? ( ) : ( Kapak yok )} {book.title} {book.subtitle && ( {book.subtitle} )} {book.authors?.length ? book.authors.join(', ') : 'Yazar bilgisi bulunamadı'} {detailLine && ( {detailLine} )} {ratingLine && ( {ratingLine} )} {book.categories?.length > 0 && ( {book.categories.join(', ')} )} ); })} )} Google Books veritabanında arama yapmak için kitap adını yaz. Seçtiğin kaydın tüm meta bilgileri EPUB'a işlenecek. {searching && } {searchError && bookTitle.trim() && !searching && ( {searchError} )} Görselleri veya EPUB dosyasını sürükleyip bırak ya da tıkla .png, .jpg, .jpeg formatlarında çoklu görsel ya da .epub dosyaları yükleyebilirsin. {isDragActive && ( Bırak ve yükleyelim! )} {epubProcessing && ( EPUB içeriği ayrıştırılıyor... )} {epubImports.length > 0 && ( Yüklenen EPUB dosyaları ({epubImports.length}) {epubImports.map((item) => ( {item.metadata?.title || item.filename} {item.metadata?.authors?.length ? ( {item.metadata.authors.join(', ')} ) : ( Yazar bilgisi bulunamadı )} {[ item.metadata?.publisher, item.metadata?.language ? item.metadata.language.toUpperCase() : null, item.metadata?.publishedDate, ] .filter(Boolean) .join(' • ')} {item.filename} • {(item.size / (1024 * 1024)).toFixed(2)} MB ))} )} Yüklenen görseller ({uploadedImages.length}) {uploadedImages.length === 0 ? ( Henüz görsel yüklenmedi. ) : ( {uploadedImages.map((image) => ( {image.filename} ))} )} ); }; export default UploadStep;