diff --git a/frontend/src/components/settings/WorkspaceManager.tsx b/frontend/src/components/settings/WorkspaceManager.tsx new file mode 100644 index 0000000..11fc4f7 --- /dev/null +++ b/frontend/src/components/settings/WorkspaceManager.tsx @@ -0,0 +1,235 @@ +import { useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import { useWorkspaces, useCreateWorkspace, useUpdateWorkspace, useDeleteWorkspace, useSetWorkspaceModules, useAssignWorkspaceUser, useRemoveWorkspaceUser, type Workspace, type WorkspaceModule } from '@/api/hooks/workspaces'; +import { LayoutGrid, Plus, Trash2, Edit, Users, Save, X, Check } from 'lucide-react'; + +const AVAILABLE_MODULES = [ + { key: 'contacts', label: 'Kontakte' }, + { key: 'calendar', label: 'Kalender' }, + { key: 'mail', label: 'E-Mail' }, + { key: 'tasks', label: 'Aufgaben' }, + { key: 'dms', label: 'Dokumente' }, + { key: 'kommunikation', label: 'Kommunikation' }, + { key: 'reports', label: 'Reports' }, + { key: 'automation', label: 'Automation' }, + { key: 'tags', label: 'Tags' }, + { key: 'dashboard', label: 'Dashboard' }, + { key: 'settings', label: 'Einstellungen' }, + { key: 'audit', label: 'Audit-Log' }, +]; + +export function WorkspaceManager() { + const { t } = useTranslation(); + const { data: wsData } = useWorkspaces(); + const createWs = useCreateWorkspace(); + const updateWs = useUpdateWorkspace(); + const deleteWs = useDeleteWorkspace(); + const setModules = useSetWorkspaceModules(); + + const [showCreate, setShowCreate] = useState(false); + const [editingId, setEditingId] = useState(null); + const [editName, setEditName] = useState(''); + const [editDesc, setEditDesc] = useState(''); + const [editIcon, setEditIcon] = useState('LayoutGrid'); + const [editDefault, setEditDefault] = useState(false); + + const [moduleWsId, setModuleWsId] = useState(null); + const [moduleConfig, setModuleConfig] = useState([]); + + const workspaces = wsData?.items || []; + + const handleCreate = async () => { + await createWs.mutateAsync({ name: editName, icon: editIcon, description: editDesc, is_default: editDefault }); + setShowCreate(false); + setEditName(''); setEditDesc(''); setEditDefault(false); + }; + + const handleSaveEdit = async () => { + if (!editingId) return; + await updateWs.mutateAsync({ id: editingId, name: editName, description: editDesc, is_default: editDefault }); + setEditingId(null); + }; + + const handleDelete = async (id: string) => { + if (confirm('Workspace wirklich löschen?')) { + await deleteWs.mutateAsync(id); + } + }; + + const openModuleEditor = (ws: Workspace) => { + setModuleWsId(ws.id); + const existing = ws.modules || []; + setModuleConfig(AVAILABLE_MODULES.map(m => { + const existingMod = existing.find(e => e.module_key === m.key); + return { + module_key: m.key, + is_visible: existingMod?.is_visible ?? false, + menu_order: existingMod?.menu_order ?? 0, + config: existingMod?.config || {}, + }; + })); + }; + + const saveModules = async () => { + if (!moduleWsId) return; + await setModules.mutateAsync({ workspaceId: moduleWsId, modules: moduleConfig }); + setModuleWsId(null); + }; + + const toggleModule = (key: string) => { + setModuleConfig(prev => prev.map(m => + m.module_key === key ? { ...m, is_visible: !m.is_visible } : m + )); + }; + + return ( +
+
+

{t('workspaces.title', 'Workspaces')}

+ +
+ + {showCreate && ( +
+

{t('workspaces.createNew', 'Neuen Workspace erstellen')}

+ setEditName(e.target.value)} + className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md bg-white dark:bg-gray-900 text-sm" + /> + setEditDesc(e.target.value)} + className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md bg-white dark:bg-gray-900 text-sm" + /> + +
+ + +
+
+ )} + + {moduleWsId && ( +
+
+

{t('workspaces.moduleConfig', 'Module konfigurieren')}

+
+ + +
+
+
+ {moduleConfig.map(m => { + const mod = AVAILABLE_MODULES.find(a => a.key === m.module_key); + return ( + + ); + })} +
+
+ )} + +
+ {workspaces.length === 0 && ( +

+ {t('workspaces.empty', 'Keine Workspaces vorhanden. Erstellen Sie einen neuen Workspace.')} +

+ )} + {workspaces.map(ws => ( +
+ {editingId === ws.id ? ( +
+ setEditName(e.target.value)} className="w-full px-3 py-2 border rounded-md text-sm" /> + setEditDesc(e.target.value)} className="w-full px-3 py-2 border rounded-md text-sm" placeholder="Beschreibung" /> + +
+ + +
+
+ ) : ( +
+
+ +
+
+ {ws.name} + {ws.is_default && Standard} +
+ {ws.description &&

{ws.description}

} +
+ {ws.user_count || 0} User + {ws.modules?.length || 0} Module +
+
+
+
+ + + +
+
+ )} +
+ ))} +
+
+ ); +} diff --git a/frontend/src/pages/SettingsRechte.tsx b/frontend/src/pages/SettingsRechte.tsx index 936d423..b91e934 100644 --- a/frontend/src/pages/SettingsRechte.tsx +++ b/frontend/src/pages/SettingsRechte.tsx @@ -17,7 +17,8 @@ import { EmptyState } from '@/components/ui/EmptyState'; import { useToast } from '@/components/ui/Toast'; import { ConfirmDialog } from '@/components/ui/ConfirmDialog'; import { apiDelete } from '@/api/client'; -import { Search, Trash2, Shield, Users, Share2, History } from 'lucide-react'; +import { Search, Trash2, Shield, Users, Share2, History, LayoutGrid } from 'lucide-react'; +import { WorkspaceManager } from '@/components/settings/WorkspaceManager'; import clsx from 'clsx'; // ─── Helpers ──────────────────────────────────────────────────────────────── @@ -372,6 +373,7 @@ export function SettingsRechtePage() { { key: 'rollen', label: 'Rollen', icon: Shield, permission: 'roles:read' }, { key: 'gruppen', label: 'Gruppen', icon: Users, permission: 'groups:read' }, { key: 'freigaben', label: 'Freigaben', icon: Share2, permission: 'settings:read' }, + { key: 'workspaces', label: 'Workspaces', icon: LayoutGrid, permission: 'workspaces:read' }, { key: 'audit', label: 'Audit', icon: History, permission: 'audit:read' }, ].filter((tab) => hasPermission(tab.permission)); @@ -408,6 +410,7 @@ export function SettingsRechtePage() { {activeTab === 'rollen' && } {activeTab === 'gruppen' && } {activeTab === 'freigaben' && } + {activeTab === 'workspaces' && } {activeTab === 'audit' && }