import React, { useState, useEffect, useMemo, useCallback } from 'react'; import { useTranslation } from 'react-i18next'; import { DndContext, closestCenter, KeyboardSensor, PointerSensor, useSensor, useSensors, DragEndEvent, } from '@dnd-kit/core'; import { arrayMove, SortableContext, sortableKeyboardCoordinates, verticalListSortingStrategy, } from '@dnd-kit/sortable'; import { SortableMenuItem } from '@/components/layout/SortableMenuItem'; import { Button } from '@/components/ui/Button'; import { Card } from '@/components/ui/Card'; import { Skeleton } from '@/components/ui/Skeleton'; import { useToast } from '@/components/ui/Toast'; import { usePluginStore, PluginMenuItem } from '@/store/pluginStore'; import { useMenuOrder, useUpdateMenuOrder } from '@/api/users'; import { GripVertical, RotateCcw, Save } from 'lucide-react'; interface MenuEntry { id: string; label: string; icon: string; isGroup?: boolean; } const DEFAULT_ORDER = [ 'dashboard', 'contacts', 'calendar', 'mail', 'tasks', 'dms', 'automation', 'reports', 'communication', 'ai-assistant', 'settings', ]; export function SettingsMenuOrderPage() { const { t } = useTranslation(); const toast = useToast(); const manifests = usePluginStore((s) => s.manifests); const { data: menuOrderData, isLoading: orderLoading } = useMenuOrder(); const updateMutation = useUpdateMenuOrder(); const [items, setItems] = useState([]); const pluginMenuItems = useMemo( () => manifests .flatMap((m) => m.menu_items) .sort((a, b) => a.order - b.order), [manifests] ); // Build all menu entries from static + plugin items const allEntries = useMemo(() => { const entries: MenuEntry[] = []; // Static entries entries.push({ id: 'dashboard', label: t('nav.dashboard'), icon: 'Home' }); entries.push({ id: 'contacts', label: t('nav.contacts'), icon: 'Users' }); // Plugin entries const groups = new Map(); const singles: PluginMenuItem[] = []; for (const item of pluginMenuItems) { if (item.group) { if (!groups.has(item.group)) groups.set(item.group, []); groups.get(item.group)!.push(item); } else { singles.push(item); } } for (const item of singles) { entries.push({ id: item.path, label: t(item.label_key, item.label), icon: item.icon, }); } for (const [group, groupItems] of groups) { entries.push({ id: group, label: group, icon: groupItems[0].icon, isGroup: true, }); } // Settings at the end entries.push({ id: 'settings', label: t('nav.settings'), icon: 'Settings' }); return entries; }, [pluginMenuItems, t]); // Apply saved order or default useEffect(() => { if (orderLoading) return; const savedOrder = menuOrderData?.menu_order; if (savedOrder && savedOrder.length > 0) { // Sort entries by saved order, keeping any new entries at the end const orderMap = new Map(savedOrder.map((id, idx) => [id, idx])); const sorted = [...allEntries].sort((a, b) => { const aIdx = orderMap.get(a.id); const bIdx = orderMap.get(b.id); if (aIdx !== undefined && bIdx !== undefined) return aIdx - bIdx; if (aIdx !== undefined) return -1; if (bIdx !== undefined) return 1; return 0; }); setItems(sorted); } else { // Use default order const orderMap = new Map(DEFAULT_ORDER.map((id, idx) => [id, idx])); const sorted = [...allEntries].sort((a, b) => { const aIdx = orderMap.get(a.id); const bIdx = orderMap.get(b.id); if (aIdx !== undefined && bIdx !== undefined) return aIdx - bIdx; if (aIdx !== undefined) return -1; if (bIdx !== undefined) return 1; return 0; }); setItems(sorted); } }, [allEntries, menuOrderData, orderLoading]); const sensors = useSensors( useSensor(PointerSensor, { activationConstraint: { distance: 8 }, }), useSensor(KeyboardSensor, { coordinateGetter: sortableKeyboardCoordinates, }) ); const handleDragEnd = useCallback((event: DragEndEvent) => { const { active, over } = event; if (!over || active.id === over.id) return; setItems((prev) => { const oldIndex = prev.findIndex((item) => item.id === active.id); const newIndex = prev.findIndex((item) => item.id === over.id); if (oldIndex === -1 || newIndex === -1) return prev; return arrayMove(prev, oldIndex, newIndex); }); }, []); const handleSave = async () => { try { const order = items.map((item) => item.id); await updateMutation.mutateAsync(order); toast.success(t('settings.saved', 'Gespeichert')); } catch (err: any) { toast.error(err.message || t('settings.saveFailed', 'Speichern fehlgeschlagen')); } }; const handleReset = () => { const orderMap = new Map(DEFAULT_ORDER.map((id, idx) => [id, idx])); const sorted = [...allEntries].sort((a, b) => { const aIdx = orderMap.get(a.id); const bIdx = orderMap.get(b.id); if (aIdx !== undefined && bIdx !== undefined) return aIdx - bIdx; if (aIdx !== undefined) return -1; if (bIdx !== undefined) return 1; return 0; }); setItems(sorted); }; if (orderLoading) { return (
); } return (

{t('settings.menuOrder', 'Menü-Reihenfolge')}

{t('settings.menuOrderHint', 'Ziehen Sie die Menü-Einträge per Drag & Drop in die gewünschte Reihenfolge.')}

item.id)} strategy={verticalListSortingStrategy} >
{items.map((item) => ( ))}
); }