deploy
This commit is contained in:
@@ -0,0 +1,138 @@
|
||||
import { useRef } from 'react'
|
||||
import DeleteOutlineOutlinedIcon from '@mui/icons-material/DeleteOutlineOutlined'
|
||||
import Box from '@mui/material/Box'
|
||||
import Button from '@mui/material/Button'
|
||||
import IconButton from '@mui/material/IconButton'
|
||||
import Stack from '@mui/material/Stack'
|
||||
import Tooltip from '@mui/material/Tooltip'
|
||||
import Typography from '@mui/material/Typography'
|
||||
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
|
||||
import { deleteGalleryImage, fetchAdminGallery } from '@/entities/gallery/api/gallery-api'
|
||||
import { uploadAdminProductImages } from '@/entities/product/api/product-api'
|
||||
import { invalidateQueryKeys } from '@/shared/lib/invalidate-query-keys'
|
||||
|
||||
export function AdminGalleryPage() {
|
||||
const queryClient = useQueryClient()
|
||||
const fileInputRef = useRef<HTMLInputElement>(null)
|
||||
|
||||
const galleryQuery = useQuery({
|
||||
queryKey: ['admin', 'gallery'],
|
||||
queryFn: fetchAdminGallery,
|
||||
})
|
||||
|
||||
const uploadMut = useMutation({
|
||||
mutationFn: (files: File[]) => uploadAdminProductImages(files),
|
||||
onSuccess: () => {
|
||||
void invalidateQueryKeys(queryClient, [['admin', 'gallery']])
|
||||
if (fileInputRef.current) {
|
||||
fileInputRef.current.value = ''
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
const deleteMut = useMutation({
|
||||
mutationFn: (id: string) => deleteGalleryImage(id),
|
||||
onSuccess: () => {
|
||||
void invalidateQueryKeys(queryClient, [['admin', 'gallery']])
|
||||
},
|
||||
})
|
||||
|
||||
const items = galleryQuery.data?.items ?? []
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Typography variant="h4" gutterBottom>
|
||||
Галерея
|
||||
</Typography>
|
||||
<Typography variant="body2" color="text.secondary" sx={{ mb: 2 }}>
|
||||
Изображения без привязки к товару можно загружать здесь; их же можно добавить в карточку товара через «Из
|
||||
галереи». Удаление из списка стирает файл с диска, если оно не используется в товаре.
|
||||
</Typography>
|
||||
|
||||
<Stack direction="row" spacing={2} sx={{ mb: 3, alignItems: 'center', flexWrap: 'wrap' }}>
|
||||
<Button variant="contained" component="label" disabled={uploadMut.isPending}>
|
||||
Загрузить файлы
|
||||
<input
|
||||
ref={fileInputRef}
|
||||
hidden
|
||||
type="file"
|
||||
accept="image/png,image/jpeg,image/webp"
|
||||
multiple
|
||||
onChange={(e) => {
|
||||
const files = e.target.files
|
||||
if (!files?.length) return
|
||||
uploadMut.mutate(Array.from(files))
|
||||
}}
|
||||
/>
|
||||
</Button>
|
||||
{uploadMut.isPending && <Typography color="text.secondary">Загрузка…</Typography>}
|
||||
{uploadMut.isError && (
|
||||
<Typography color="error">
|
||||
{uploadMut.error instanceof Error ? uploadMut.error.message : 'Ошибка загрузки'}
|
||||
</Typography>
|
||||
)}
|
||||
{deleteMut.isError && (
|
||||
<Typography color="error">
|
||||
{deleteMut.error instanceof Error ? deleteMut.error.message : 'Ошибка удаления'}
|
||||
</Typography>
|
||||
)}
|
||||
</Stack>
|
||||
|
||||
{galleryQuery.isError && (
|
||||
<Typography color="error" sx={{ mb: 2 }}>
|
||||
Не удалось загрузить список.
|
||||
</Typography>
|
||||
)}
|
||||
|
||||
<Box
|
||||
sx={{
|
||||
display: 'grid',
|
||||
gridTemplateColumns: 'repeat(auto-fill, minmax(140px, 1fr))',
|
||||
gap: 2,
|
||||
}}
|
||||
>
|
||||
{items.map((item) => (
|
||||
<Box
|
||||
key={item.id}
|
||||
sx={{
|
||||
position: 'relative',
|
||||
borderRadius: 1,
|
||||
overflow: 'hidden',
|
||||
border: 1,
|
||||
borderColor: 'divider',
|
||||
aspectRatio: '1',
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
component="img"
|
||||
src={item.url}
|
||||
alt=""
|
||||
sx={{ width: '100%', height: '100%', objectFit: 'cover', display: 'block' }}
|
||||
/>
|
||||
<Tooltip title="Удалить из галереи">
|
||||
<IconButton
|
||||
size="small"
|
||||
color="error"
|
||||
sx={{
|
||||
position: 'absolute',
|
||||
top: 4,
|
||||
right: 4,
|
||||
bgcolor: 'background.paper',
|
||||
'&:hover': { bgcolor: 'error.light', color: 'error.contrastText' },
|
||||
}}
|
||||
disabled={deleteMut.isPending}
|
||||
onClick={() => deleteMut.mutate(item.id)}
|
||||
>
|
||||
<DeleteOutlineOutlinedIcon fontSize="small" />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
</Box>
|
||||
))}
|
||||
</Box>
|
||||
|
||||
{!galleryQuery.isLoading && items.length === 0 && (
|
||||
<Typography color="text.secondary">Пока нет загруженных изображений.</Typography>
|
||||
)}
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user