diff --git a/src/App.tsx b/src/App.tsx index 94ef3f5..d1d5982 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -13,6 +13,7 @@ import ProjectDetailPage from '@/pages/ProjectDetailPage' import MilestonesPage from '@/pages/MilestonesPage' import MilestoneDetailPage from '@/pages/MilestoneDetailPage' import NotificationsPage from '@/pages/NotificationsPage' +import RoleEditorPage from '@/pages/RoleEditorPage' import MonitorPage from '@/pages/MonitorPage' import axios from 'axios' @@ -66,7 +67,8 @@ export default function App() { - } /> + } /> + } /> } /> } /> @@ -91,6 +93,7 @@ export default function App() { } /> } /> } /> + } /> } /> } /> diff --git a/src/components/Sidebar.tsx b/src/components/Sidebar.tsx index 1b0ade5..3395448 100644 --- a/src/components/Sidebar.tsx +++ b/src/components/Sidebar.tsx @@ -34,6 +34,7 @@ export default function Sidebar({ user, onLogout }: Props) { { to: '/projects', icon: '📁', label: 'Projects' }, { to: '/notifications', icon: '🔔', label: 'Notifications' + (unreadCount > 0 ? ' (' + unreadCount + ')' : '') }, { to: '/monitor', icon: '📡', label: 'Monitor' }, + ...(user.is_admin ? [{ to: '/roles', icon: '🔐', label: 'Roles' }] : []), ] : [ { to: '/monitor', icon: '📡', label: 'Monitor' }, ] diff --git a/src/pages/RoleEditorPage.tsx b/src/pages/RoleEditorPage.tsx new file mode 100644 index 0000000..ba483f8 --- /dev/null +++ b/src/pages/RoleEditorPage.tsx @@ -0,0 +1,184 @@ +import { useState, useEffect } from 'react' +import api from '@/services/api' + +interface Permission { + id: number + name: string + description: string + category: string +} + +interface Role { + id: number + name: string + description: string + is_global: boolean + permission_ids: number[] +} + +export default function RoleEditorPage() { + const [roles, setRoles] = useState([]) + const [permissions, setPermissions] = useState([]) + const [selectedRole, setSelectedRole] = useState(null) + const [loading, setLoading] = useState(true) + const [saving, setSaving] = useState(false) + const [message, setMessage] = useState('') + + useEffect(() => { + fetchData() + }, []) + + const fetchData = async () => { + try { + const [rolesRes, permsRes] = await Promise.all([ + api.get('/roles'), + api.get('/roles/permissions') + ]) + setRoles(rolesRes.data) + setPermissions(permsRes.data) + } catch (err) { + console.error('Failed to fetch data:', err) + } finally { + setLoading(false) + } + } + + const handlePermissionToggle = (permId: number) => { + if (!selectedRole) return + const newPermIds = selectedRole.permission_ids.includes(permId) + ? selectedRole.permission_ids.filter(id => id !== permId) + : [...selectedRole.permission_ids, permId] + setSelectedRole({ ...selectedRole, permission_ids: newPermIds }) + } + + const handleSave = async () => { + if (!selectedRole) return + setSaving(true) + setMessage('') + try { + await api.post(`/roles/${selectedRole.id}/permissions`, { + permission_ids: selectedRole.permission_ids + }) + setMessage('Saved successfully!') + fetchData() + } catch (err: any) { + setMessage(err.response?.data?.detail || 'Failed to save') + } finally { + setSaving(false) + } + } + + const groupedPermissions = permissions.reduce((acc, p) => { + if (!acc[p.category]) acc[p.category] = [] + acc[p.category].push(p) + return acc + }, {} as Record) + + if (loading) return Loading... + + return ( + + 🔐 Role Editor + + Configure permissions for each role. Only admins can edit roles. + + + {message && ( + + {message} + + )} + + + {/* Role List */} + + Roles + + {roles.map(role => ( + setSelectedRole({ ...role })} + style={{ + padding: '12px', + border: selectedRole?.id === role.id ? '2px solid #007bff' : '1px solid #ddd', + borderRadius: '6px', + cursor: 'pointer', + backgroundColor: selectedRole?.id === role.id ? '#f0f8ff' : 'white' + }} + > + {role.name} + {role.is_global && 🌟} + {role.description} + + {role.permission_ids.length} permissions + + + ))} + + + + {/* Permission Editor */} + + {selectedRole ? ( + <> + Permissions for: {selectedRole.name} + + {Object.entries(groupedPermissions).map(([category, perms]) => ( + + + {category} + + + {perms.map(perm => ( + + handlePermissionToggle(perm.id)} + /> + + {perm.name} + {perm.description} + + + ))} + + + ))} + + + {saving ? 'Saving...' : 'Save Changes'} + + > + ) : ( + + Select a role to edit its permissions + + )} + + + + ) +}
+ Configure permissions for each role. Only admins can edit roles. +