Kitap arama lsitesi z-index düzenlendi.

This commit is contained in:
2025-11-17 22:13:18 +03:00
parent 1949aefdf0
commit 27b6ccaff8

View File

@@ -7,6 +7,7 @@ import {
CardActionArea, CardActionArea,
CardContent, CardContent,
CardMedia, CardMedia,
ClickAwayListener,
Divider, Divider,
Grid, Grid,
LinearProgress, LinearProgress,
@@ -43,6 +44,7 @@ const UploadStep = () => {
const [searchError, setSearchError] = useState(null); const [searchError, setSearchError] = useState(null);
const [selectedBookId, setSelectedBookId] = useState(bookMetadata?.id || null); const [selectedBookId, setSelectedBookId] = useState(bookMetadata?.id || null);
const skipSearchRef = useRef(false); const skipSearchRef = useRef(false);
const [showResults, setShowResults] = useState(false);
const onDrop = useCallback( const onDrop = useCallback(
(acceptedFiles) => { (acceptedFiles) => {
@@ -110,6 +112,7 @@ const UploadStep = () => {
setSearchResults([]); setSearchResults([]);
setSearchError(null); setSearchError(null);
setSearching(false); setSearching(false);
setShowResults(false);
return; return;
} }
const controller = new AbortController(); const controller = new AbortController();
@@ -128,6 +131,7 @@ const UploadStep = () => {
const items = Array.isArray(payload.items) ? payload.items : []; const items = Array.isArray(payload.items) ? payload.items : [];
const normalized = items.map((item) => normalizeVolume(item)); const normalized = items.map((item) => normalizeVolume(item));
setSearchResults(normalized); setSearchResults(normalized);
setShowResults(Boolean(normalized.length));
if (!normalized.length) { if (!normalized.length) {
setSearchError('Bu başlıkla eşleşen bir kayıt bulunamadı.'); setSearchError('Bu başlıkla eşleşen bir kayıt bulunamadı.');
} }
@@ -135,6 +139,7 @@ const UploadStep = () => {
if (controller.signal.aborted) return; if (controller.signal.aborted) return;
setSearchResults([]); setSearchResults([]);
setSearchError(error.message || 'Google Books araması başarısız oldu.'); setSearchError(error.message || 'Google Books araması başarısız oldu.');
setShowResults(false);
} finally { } finally {
if (!controller.signal.aborted) { if (!controller.signal.aborted) {
setSearching(false); setSearching(false);
@@ -164,10 +169,12 @@ const UploadStep = () => {
setSelectedBookId(null); setSelectedBookId(null);
setSearchResults([]); setSearchResults([]);
setSearchError(null); setSearchError(null);
setShowResults(false);
} else if (bookMetadata && bookMetadata.title !== value) { } else if (bookMetadata && bookMetadata.title !== value) {
setBookMetadata(null); setBookMetadata(null);
setSelectedBookId(null); setSelectedBookId(null);
} }
setShowResults(Boolean(value?.trim()));
}; };
const handleSelectBook = (book) => { const handleSelectBook = (book) => {
@@ -177,6 +184,7 @@ const UploadStep = () => {
setBookTitle(book.title || ''); setBookTitle(book.title || '');
setSearchResults([]); setSearchResults([]);
setSearchError(null); setSearchError(null);
setShowResults(false);
}; };
const selectedBookSummary = useMemo(() => { const selectedBookSummary = useMemo(() => {
@@ -200,17 +208,144 @@ const UploadStep = () => {
{bookMetadata.authors?.length ? `${bookMetadata.authors.join(', ')}` : ''} {bookMetadata.authors?.length ? `${bookMetadata.authors.join(', ')}` : ''}
</Typography> </Typography>
)} )}
<Box> <Box sx={{ position: 'relative', zIndex: 1 }}>
<Typography variant="h6" gutterBottom> <Typography variant="h6" gutterBottom>
Kitap adı Kitap adı
</Typography> </Typography>
<TextField <ClickAwayListener onClickAway={() => setShowResults(false)}>
fullWidth <Box sx={{ position: 'relative' }}>
placeholder="Örn. Yapay Zeka İmparatorluğu" <TextField
value={bookTitle} fullWidth
onChange={handleTitleChange} placeholder="Örn. Yapay Zeka İmparatorluğu"
InputProps={{ sx: { borderRadius: 2 } }} value={bookTitle}
/> onChange={handleTitleChange}
onFocus={() => searchResults.length && setShowResults(true)}
InputProps={{ sx: { borderRadius: 2 } }}
/>
{showResults && searchResults.length > 0 && bookTitle?.trim() && (
<Paper
variant="outlined"
sx={{
position: 'absolute',
top: 'calc(100% + 8px)',
left: 0,
right: 0,
borderRadius: 1.2,
zIndex: 10,
maxHeight: 420,
display: 'flex',
flexDirection: 'column',
overflow: 'hidden',
boxShadow: (theme) => theme.shadows[6],
}}
>
<Typography variant="subtitle2" sx={{ px: 2, pt: 2, pb: 1, color: 'text.secondary' }}>
Google Books sonuçları
</Typography>
<Divider />
<Box sx={{ flex: 1, overflowY: 'auto', pr: 1.5 }}>
<Stack divider={<Divider />} 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 (
<Box
key={book.id}
role="button"
onClick={() => 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',
}}
>
<Box
sx={{
width: 64,
height: 96,
borderRadius: 0.3,
overflow: 'hidden',
bgcolor: '#f0ece4',
flexShrink: 0,
border: '1px solid',
borderColor: 'divider',
}}
>
{book.thumbnail ? (
<Box
component="img"
src={book.thumbnail}
alt={`${book.title} kapak görseli`}
sx={{ width: '100%', height: '100%', objectFit: 'cover' }}
loading="lazy"
/>
) : (
<Stack alignItems="center" justifyContent="center" sx={{ height: '100%' }}>
<Typography variant="caption" color="text.secondary">
Kapak yok
</Typography>
</Stack>
)}
</Box>
<Box>
<Typography variant="subtitle1" sx={{ fontWeight: 600 }}>
{book.title}
</Typography>
{book.subtitle && (
<Typography variant="body2" color="text.secondary">
{book.subtitle}
</Typography>
)}
<Typography
variant="body2"
color="text.secondary"
sx={{ fontStyle: 'italic', mt: 0.5 }}
>
{book.authors?.length ? book.authors.join(', ') : 'Yazar bilgisi bulunamadı'}
</Typography>
{detailLine && (
<Typography variant="caption" color="text.secondary" display="block" mt={0.5}>
{detailLine}
</Typography>
)}
{ratingLine && (
<Typography variant="caption" color="text.secondary" display="block">
{ratingLine}
</Typography>
)}
{book.categories?.length > 0 && (
<Typography variant="caption" color="text.secondary" display="block" mt={0.5}>
{book.categories.join(', ')}
</Typography>
)}
</Box>
</Box>
);
})}
</Stack>
</Box>
</Paper>
)}
</Box>
</ClickAwayListener>
<Typography variant="body2" color="text.secondary" mt={1}> <Typography variant="body2" color="text.secondary" mt={1}>
Google Books veritabanında arama yapmak için kitap adını yaz. Seçtiğin kaydın tüm meta bilgileri EPUB&apos;a işlenecek. Google Books veritabanında arama yapmak için kitap adını yaz. Seçtiğin kaydın tüm meta bilgileri EPUB&apos;a işlenecek.
</Typography> </Typography>
@@ -220,111 +355,6 @@ const UploadStep = () => {
{searchError} {searchError}
</Typography> </Typography>
)} )}
{searchResults.length > 0 && bookTitle?.trim() && (
<Paper variant="outlined" sx={{ mt: 2 }}>
<Typography variant="subtitle2" sx={{ px: 2, pt: 2, pb: 1, color: 'text.secondary' }}>
Google Books sonuçları
</Typography>
<Divider />
<Stack divider={<Divider />} 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 (
<Box
key={book.id}
role="button"
onClick={() => 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',
}}
>
<Box
sx={{
width: 64,
height: 96,
borderRadius: 0.3,
overflow: 'hidden',
bgcolor: '#f0ece4',
flexShrink: 0,
border: '1px solid',
borderColor: 'divider',
}}
>
{book.thumbnail ? (
<Box
component="img"
src={book.thumbnail}
alt={`${book.title} kapak görseli`}
sx={{ width: '100%', height: '100%', objectFit: 'cover' }}
loading="lazy"
/>
) : (
<Stack alignItems="center" justifyContent="center" sx={{ height: '100%' }}>
<Typography variant="caption" color="text.secondary">
Kapak yok
</Typography>
</Stack>
)}
</Box>
<Box>
<Typography variant="subtitle1" sx={{ fontWeight: 600 }}>
{book.title}
</Typography>
{book.subtitle && (
<Typography variant="body2" color="text.secondary">
{book.subtitle}
</Typography>
)}
<Typography
variant="body2"
color="text.secondary"
sx={{ fontStyle: 'italic', mt: 0.5 }}
>
{book.authors?.length ? book.authors.join(', ') : 'Yazar bilgisi bulunamadı'}
</Typography>
{detailLine && (
<Typography variant="caption" color="text.secondary" display="block" mt={0.5}>
{detailLine}
</Typography>
)}
{ratingLine && (
<Typography variant="caption" color="text.secondary" display="block">
{ratingLine}
</Typography>
)}
{book.categories?.length > 0 && (
<Typography variant="caption" color="text.secondary" display="block" mt={0.5}>
{book.categories.join(', ')}
</Typography>
)}
</Box>
</Box>
);
})}
</Stack>
</Paper>
)}
</Box> </Box>
<Box {...getRootProps()} sx={dropzoneStyle}> <Box {...getRootProps()} sx={dropzoneStyle}>
<input {...getInputProps()} /> <input {...getInputProps()} />