import { useMutation } from '@tanstack/react-query' import { GripVertical, Trash2 } from 'lucide-react' import { useState } from 'react' import { useTranslation } from 'react-i18next' import { addCollectionShow, createCollection } from '@/features/admin/collections/api' import { Button } from '@/shared/ui/button' import { Input } from '@/shared/ui/input' import { cn } from '@/shared/lib/cn' import { type ClipDragItem, readDragItem } from './dnd' import { formatClock } from './format' /** * Сборка рекламного блока: ролики перетаскиваются в упорядоченный список, под ним — суммарная * длительность. Сохраняется обычной коллекцией — отдельной сущности «блок» в модели нет (см. 3.7). */ export function BlockBuilder({ onSaved, onError, }: Readonly<{ onSaved: () => void onError: (error: unknown) => void }>) { const { t } = useTranslation() const [name, setName] = useState('') const [items, setItems] = useState([]) const [over, setOver] = useState(false) const [dragged, setDragged] = useState(null) const total = items.reduce((sum, item) => sum + item.seconds, 0) const saveMutation = useMutation({ mutationFn: async () => { const { id } = await createCollection({ name: name.trim() }) // Порядок задаётся порядком добавления: коллекция ставит позицию в конец. for (const item of items) await addCollectionShow(id, item.id) }, onSuccess: () => { setName('') setItems([]) onSaved() }, onError, }) const drop = (event: React.DragEvent) => { event.preventDefault() setOver(false) const item = readDragItem(event) // Блок из блоков собрать нельзя: коллекция хранит шоу, а не вложенные коллекции. if (item?.kind !== 'clip') return setItems((current) => [...current, item]) } const removeAt = (index: number) => setItems((current) => current.filter((_, i) => i !== index)) /** Перестановка внутри черновика — до сохранения всё живёт в состоянии, запросов нет. */ const reorder = (target: number) => { if (dragged === null || dragged === target) return setItems((current) => { const next = [...current] const [moved] = next.splice(dragged, 1) next.splice(target, 0, moved) return next }) setDragged(null) } return (

{t('admin.interstitials.blockBuilder')}

{ e.preventDefault() setOver(true) }} onDragLeave={() => setOver(false)} onDrop={drop} className={cn( 'crt-panel flex min-h-32 flex-col rounded-md border border-dashed border-border', over && 'border-primary bg-primary/5', )} > {items.length === 0 ? (

{t('admin.interstitials.dropHint')}

) : (
    {items.map((item, index) => (
  • setDragged(index)} onDragOver={(e) => e.preventDefault()} onDrop={(e) => { e.stopPropagation() reorder(index) }} className="flex items-center gap-2 px-3 py-1.5" > {index + 1} {item.name} {formatClock(item.seconds)}
  • ))}
)}
{t('admin.interstitials.blockTotal')} {formatClock(total)}
setName(e.target.value)} />
) }