Refactor media upload handling in MediaPanel: remove direct upload logic and integrate useUploadStore for file enqueueing. Update translations for upload status and add UploadSnackbar component to display upload notifications in the UI.
This commit is contained in:
@@ -8,7 +8,8 @@ import { Badge, type BadgeProps } from '@/shared/ui/badge'
|
||||
import { Button } from '@/shared/ui/button'
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/shared/ui/select'
|
||||
import { toast } from '@/shared/ui/toast-store'
|
||||
import { deleteMedia, listMedia, uploadMedia } from './api'
|
||||
import { deleteMedia, listMedia } from './api'
|
||||
import { useUploadStore } from './upload-store'
|
||||
|
||||
const PAGE_SIZE = 20
|
||||
|
||||
@@ -42,10 +43,8 @@ export function MediaPanel() {
|
||||
const { t } = useTranslation()
|
||||
const queryClient = useQueryClient()
|
||||
const fileInput = useRef<HTMLInputElement>(null)
|
||||
const [upload, setUpload] = useState<{ current: number; total: number; percent: number } | null>(
|
||||
null,
|
||||
)
|
||||
const [filter, setFilter] = useState<MediaFilter>('active')
|
||||
const enqueue = useUploadStore((s) => s.enqueue)
|
||||
|
||||
const { data, isLoading } = useQuery({
|
||||
queryKey: ['admin', 'media', filter],
|
||||
@@ -63,45 +62,6 @@ export function MediaPanel() {
|
||||
|
||||
const deleteMutation = useMutation({ mutationFn: deleteMedia, onSuccess: invalidate, onError })
|
||||
|
||||
// Массовая загрузка: файлы отправляются по одному (обработка всё равно в очереди по одному),
|
||||
// прогресс — «текущий/всего · %». Дубликаты по имени пропускаются заранее (сервер тоже отклонит).
|
||||
const handleFiles = async (files: FileList) => {
|
||||
const list = Array.from(files)
|
||||
|
||||
let existing = new Set<string>()
|
||||
try {
|
||||
const all = await listMedia({
|
||||
page: 1,
|
||||
pageSize: 1000,
|
||||
statuses: ['Pending', 'Processing', 'Ready'],
|
||||
})
|
||||
existing = new Set(all.items.map((a) => a.originalFileName))
|
||||
} catch {
|
||||
// Не удалось получить список — положимся на серверную проверку дубликатов.
|
||||
}
|
||||
|
||||
const toUpload = list.filter((f) => !existing.has(f.name))
|
||||
const skipped = list.length - toUpload.length
|
||||
|
||||
let uploaded = 0
|
||||
for (let i = 0; i < toUpload.length; i++) {
|
||||
setUpload({ current: i + 1, total: toUpload.length, percent: 0 })
|
||||
try {
|
||||
await uploadMedia(toUpload[i], (percent) =>
|
||||
setUpload({ current: i + 1, total: toUpload.length, percent }),
|
||||
)
|
||||
uploaded++
|
||||
invalidate()
|
||||
} catch (error) {
|
||||
onError(error)
|
||||
}
|
||||
}
|
||||
setUpload(null)
|
||||
if (fileInput.current) fileInput.current.value = ''
|
||||
if (uploaded > 0) toast.success(t('admin.media.uploadedCount', { count: uploaded }))
|
||||
if (skipped > 0) toast.message(t('admin.media.skippedDuplicates', { count: skipped }))
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-4">
|
||||
<div className="flex flex-wrap items-center justify-between gap-2">
|
||||
@@ -120,11 +80,6 @@ export function MediaPanel() {
|
||||
</Select>
|
||||
</div>
|
||||
<div className="flex items-center gap-3">
|
||||
{upload != null && (
|
||||
<span className="text-sm text-muted-foreground">
|
||||
{upload.current}/{upload.total} · {upload.percent}%
|
||||
</span>
|
||||
)}
|
||||
<input
|
||||
ref={fileInput}
|
||||
type="file"
|
||||
@@ -133,10 +88,11 @@ export function MediaPanel() {
|
||||
className="hidden"
|
||||
onChange={(e) => {
|
||||
const files = e.target.files
|
||||
if (files && files.length > 0) void handleFiles(files)
|
||||
if (files && files.length > 0) void enqueue(Array.from(files))
|
||||
e.target.value = ''
|
||||
}}
|
||||
/>
|
||||
<Button size="sm" disabled={upload != null} onClick={() => fileInput.current?.click()}>
|
||||
<Button size="sm" onClick={() => fileInput.current?.click()}>
|
||||
<Upload className="h-4 w-4" />
|
||||
{t('admin.media.upload')}
|
||||
</Button>
|
||||
|
||||
Reference in New Issue
Block a user