Reparaturplan Fixes: Widget workspace_id check, total bug, context is_visible, permissions, fallbacks
Check Cross-Plugin Imports / check (push) Has been cancelled
Check Cross-Plugin Imports / check (push) Has been cancelled
Backend: - Widget total: 0 bug fixed (now returns len(widgets)) - Widget update/delete: now verifies workspace_id + tenant_id (was only tenant_id) - Workspace context: returns all modules with is_visible flag (was only visible modules) - is_workspace_manager() removed (Plan 4.2: no manager checks) - seed_default_workspace: removed hardcoded modules (Plan 4.7: no hardcoded tiles) - Workspace permissions registered in CORE_PERMISSIONS (Plan 2.3) Frontend: - Permission fallback removed: Sidebar/TopBar show nothing while loading (Plan 2.4) - workspaceStore isModuleVisible: fail-closed when isSystemAdmin undefined - WorkspaceManager: AVAILABLE_MODULES replaced with dynamic core+plugin items (Plan 4.4) Tests: - 17 backend tests (removed is_workspace_manager test, adapted widget/context tests) - 13 frontend tests (added undefined-isSystemAdmin test, adapted visibility tests)
This commit is contained in:
@@ -70,8 +70,8 @@ export function Sidebar() {
|
||||
// Use hasPermission directly — permissions are loaded via useUserPermissions hook
|
||||
const canAccess = (perm?: string): boolean => {
|
||||
if (!perm) return true;
|
||||
// While permissions are loading (undefined), show everything; backend 403 catches errors
|
||||
if (!user?.permissions && user?.is_system_admin === undefined) return true;
|
||||
// While permissions are loading (undefined), show nothing for protected items
|
||||
if (!user?.permissions && user?.is_system_admin === undefined) return false;
|
||||
return hasPermission(perm);
|
||||
};
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ export function TopBar() {
|
||||
const { hasPermission } = usePermission();
|
||||
// While permissions are loading (undefined), show everything; backend 403 catches errors
|
||||
const canAccess = (perm: string): boolean => {
|
||||
if (!user?.permissions && user?.is_system_admin === undefined) return true;
|
||||
if (!user?.permissions && user?.is_system_admin === undefined) return false;
|
||||
return hasPermission(perm);
|
||||
};
|
||||
const restoreWindow = useWindowStore((s) => s.restoreWindow);
|
||||
|
||||
@@ -1,21 +1,13 @@
|
||||
import { useState } from 'react';
|
||||
import { useState, useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useWorkspaces, useCreateWorkspace, useUpdateWorkspace, useDeleteWorkspace, useSetWorkspaceModules, useAssignWorkspaceUser, useRemoveWorkspaceUser, type Workspace, type WorkspaceModule } from '@/api/hooks/workspaces';
|
||||
import { usePluginStore } from '@/store/pluginStore';
|
||||
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' },
|
||||
// Static core menu items — same as Sidebar.tsx
|
||||
const CORE_MENU_ITEMS = [
|
||||
{ key: 'dashboard', label: 'Dashboard' },
|
||||
{ key: 'settings', label: 'Einstellungen' },
|
||||
{ key: 'audit', label: 'Audit-Log' },
|
||||
{ key: 'contacts', label: 'Kontakte' },
|
||||
];
|
||||
|
||||
export function WorkspaceManager() {
|
||||
@@ -25,6 +17,24 @@ export function WorkspaceManager() {
|
||||
const updateWs = useUpdateWorkspace();
|
||||
const deleteWs = useDeleteWorkspace();
|
||||
const setModules = useSetWorkspaceModules();
|
||||
const manifests = usePluginStore(s => s.manifests);
|
||||
|
||||
// Dynamically build available modules from core menu items + plugin manifests
|
||||
const availableModules = useMemo(() => {
|
||||
const pluginItems = manifests
|
||||
.flatMap(m => m.menu_items || [])
|
||||
.map(item => ({
|
||||
key: item.path.replace(/^\//, '').split('/')[0],
|
||||
label: item.label || item.label_key,
|
||||
}));
|
||||
// Merge core + plugin, deduplicate by key
|
||||
const seen = new Set<string>();
|
||||
return [...CORE_MENU_ITEMS, ...pluginItems].filter(m => {
|
||||
if (seen.has(m.key)) return false;
|
||||
seen.add(m.key);
|
||||
return true;
|
||||
});
|
||||
}, [manifests]);
|
||||
|
||||
const [showCreate, setShowCreate] = useState(false);
|
||||
const [editingId, setEditingId] = useState<string | null>(null);
|
||||
@@ -59,7 +69,7 @@ export function WorkspaceManager() {
|
||||
const openModuleEditor = (ws: Workspace) => {
|
||||
setModuleWsId(ws.id);
|
||||
const existing = ws.modules || [];
|
||||
setModuleConfig(AVAILABLE_MODULES.map(m => {
|
||||
setModuleConfig(availableModules.map(m => {
|
||||
const existingMod = existing.find(e => e.module_key === m.key);
|
||||
return {
|
||||
module_key: m.key,
|
||||
@@ -144,7 +154,7 @@ export function WorkspaceManager() {
|
||||
</div>
|
||||
<div className="grid grid-cols-2 md:grid-cols-3 gap-2">
|
||||
{moduleConfig.map(m => {
|
||||
const mod = AVAILABLE_MODULES.find(a => a.key === m.module_key);
|
||||
const mod = availableModules.find(a => a.key === m.module_key);
|
||||
return (
|
||||
<label key={m.module_key} className="flex items-center gap-2 p-2 border border-gray-200 dark:border-gray-700 rounded-md cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-800">
|
||||
<input
|
||||
|
||||
Reference in New Issue
Block a user