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;