import { useState, useEffect } from 'react'; import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; import toast from 'react-hot-toast'; import { DndContext, closestCenter, KeyboardSensor, PointerSensor, useSensor, useSensors, type DragEndEvent, } from '@dnd-kit/core'; import { arrayMove, SortableContext, sortableKeyboardCoordinates, useSortable, verticalListSortingStrategy, } from '@dnd-kit/sortable'; import { CSS } from '@dnd-kit/utilities'; import { Plus, Trash2, Eye, EyeOff, GripVertical, Save } from 'lucide-react'; import { api } from '../../lib/api-client'; import { AppShell, PageHeader, PageBody } from '../../layout/AppShell'; import { Button, Card, CardContent, Input, Label, Select } from '../../components/ui/primitives'; interface Section { id: string; type: string; title: string | null; config: Record; sort_order: number; enabled: boolean; } const SECTION_TYPES = [ { value: 'hero', label: 'Hero banner' }, { value: 'featured-brands', label: 'Uitgelichte merken' }, { value: 'trending', label: 'Trending sneakers' }, { value: 'new-arrivals', label: 'Nieuwe binnen' }, { value: 'deals', label: 'Aanbiedingen' }, { value: 'collection', label: 'Collectie' }, { value: 'releases', label: 'Release kalender' }, { value: 'newsletter', label: 'Nieuwsbrief CTA' }, ]; export function HomepageEditor() { const qc = useQueryClient(); const [sections, setSections] = useState([]); const [dirty, setDirty] = useState(false); const { data, isLoading } = useQuery({ queryKey: ['homepage-sections'], queryFn: () => api.get<{ sections: Section[] }>('/homepage'), }); useEffect(() => { if (data?.sections) { setSections(data.sections); setDirty(false); } }, [data?.sections]); const reorderMut = useMutation({ mutationFn: (ids: string[]) => api.post('/homepage/reorder', { ids }), onSuccess: () => { toast.success('Volgorde opgeslagen'); qc.invalidateQueries({ queryKey: ['homepage-sections'] }); }, onError: (e: Error) => toast.error(e.message), }); const createMut = useMutation({ mutationFn: (body: Partial
) => api.post<{ section: Section }>('/homepage', body), onSuccess: () => qc.invalidateQueries({ queryKey: ['homepage-sections'] }), }); const updateMut = useMutation({ mutationFn: ({ id, body }: { id: string; body: Partial
}) => api.put<{ section: Section }>(`/homepage/${id}`, body), onSuccess: () => qc.invalidateQueries({ queryKey: ['homepage-sections'] }), }); const deleteMut = useMutation({ mutationFn: (id: string) => api.delete(`/homepage/${id}`), onSuccess: () => { toast.success('Sectie verwijderd'); qc.invalidateQueries({ queryKey: ['homepage-sections'] }); }, onError: (e: Error) => toast.error(e.message), }); const sensors = useSensors( useSensor(PointerSensor, { activationConstraint: { distance: 6 } }), useSensor(KeyboardSensor, { coordinateGetter: sortableKeyboardCoordinates }), ); function onDragEnd(ev: DragEndEvent) { const { active, over } = ev; if (!over || active.id === over.id) return; const oldIndex = sections.findIndex((s) => s.id === active.id); const newIndex = sections.findIndex((s) => s.id === over.id); setSections(arrayMove(sections, oldIndex, newIndex)); setDirty(true); } function addSection() { createMut.mutate({ type: 'hero', title: 'Nieuwe sectie', config: {}, sort_order: sections.length, enabled: true, }); } return ( {dirty && ( )} } /> {isLoading ? ( Laden... ) : sections.length === 0 ? (

Nog geen secties.

) : ( s.id)} strategy={verticalListSortingStrategy}>
{sections.map((s) => ( updateMut.mutate({ id: s.id, body })} onDelete={() => { if (confirm(`Sectie "${s.title ?? s.type}" verwijderen?`)) deleteMut.mutate(s.id); }} /> ))}
)}
); } function SortableSection({ section, onUpdate, onDelete, }: { section: Section; onUpdate: (body: Partial
) => void; onDelete: () => void; }) { const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({ id: section.id }); const style = { transform: CSS.Transform.toString(transform), transition, opacity: isDragging ? 0.5 : 1, }; const [local, setLocal] = useState(section); useEffect(() => setLocal(section), [section]); const dirty = JSON.stringify(local) !== JSON.stringify(section); return (
setLocal({ ...local, title: e.target.value })} />
{dirty && ( )}
); }