feat: drag-and-drop photo reordering in category form

Thumbnails are draggable — drag one over another to swap positions.
Dragged item shows opacity/scale hint while dragging.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-23 13:28:08 +03:00
parent 31d135563f
commit 036885dd24

View File

@@ -47,6 +47,7 @@ function CategoryForm({ category, onClose, onSave }: CategoryFormProps) {
const [saving, setSaving] = useState(false) const [saving, setSaving] = useState(false)
const [saveError, setSaveError] = useState('') const [saveError, setSaveError] = useState('')
const [dragging, setDragging] = useState(false) const [dragging, setDragging] = useState(false)
const [dragSrcIdx, setDragSrcIdx] = useState<number | null>(null)
const fileRef = useRef<HTMLInputElement>(null) const fileRef = useRef<HTMLInputElement>(null)
const toggleAmenity = (a: string) => const toggleAmenity = (a: string) =>
@@ -245,8 +246,30 @@ function CategoryForm({ category, onClose, onSave }: CategoryFormProps) {
</div> </div>
<div className="flex gap-2 overflow-x-auto pb-1"> <div className="flex gap-2 overflow-x-auto pb-1">
{photos.map((p, i) => ( {photos.map((p, i) => (
<button key={i} onClick={() => setPhotoIdx(i)} <button
className={cn('w-14 h-14 rounded-lg overflow-hidden border-2 shrink-0', i === photoIdx ? 'border-brand-500' : 'border-transparent')}> key={i}
draggable
onDragStart={() => setDragSrcIdx(i)}
onDragOver={e => {
e.preventDefault()
if (dragSrcIdx === null || dragSrcIdx === i) return
setPhotos(prev => {
const next = [...prev]
const [moved] = next.splice(dragSrcIdx, 1)
next.splice(i, 0, moved)
return next
})
setPhotoIdx(i)
setDragSrcIdx(i)
}}
onDragEnd={() => setDragSrcIdx(null)}
onClick={() => setPhotoIdx(i)}
className={cn(
'w-14 h-14 rounded-lg overflow-hidden border-2 shrink-0 transition-all cursor-grab active:cursor-grabbing',
i === photoIdx ? 'border-brand-500' : 'border-transparent',
dragSrcIdx === i && 'opacity-50 scale-95',
)}
>
<img src={p} alt="" className="w-full h-full object-cover" /> <img src={p} alt="" className="w-full h-full object-cover" />
</button> </button>
))} ))}