// File: frontend/app/(protected)/admin/users/_components/user-form-dialog.jsx 'use client'; import { useState, useEffect } from 'react'; import { api } from '@/lib/api'; import { Button } from '@/components/ui/button'; import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter } from '@/components/ui/dialog'; import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; import { Switch } from '@/components/ui/switch'; import { Checkbox } from "@/components/ui/checkbox"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; import { Trash2 } from 'lucide-react'; import { ScrollArea } from '@/components/ui/scroll-area'; export function UserFormDialog({ user, isOpen, setIsOpen, onSuccess }) { // State for form fields const [formData, setFormData] = useState({}); const [selectedSystemRoles, setSelectedSystemRoles] = useState(new Set()); // State for project role assignments const [projectRoles, setProjectRoles] = useState([]); const [selectedProjectId, setSelectedProjectId] = useState(''); const [selectedRoleId, setSelectedRoleId] = useState(''); // State for prerequisite data (fetched once) const [allRoles, setAllRoles] = useState([]); const [allProjects, setAllProjects] = useState([]); // UI State const [isLoading, setIsLoading] = useState(false); const [error, setError] = useState(''); const isEditMode = !!user; // Effect to fetch prerequisite data (all roles and projects) when dialog opens useEffect(() => { const fetchPrerequisites = async () => { try { const [rolesRes, projectsRes] = await Promise.all([ api.get('/rbac/roles'), api.get('/projects'), ]); setAllRoles(rolesRes.data); setAllProjects(projectsRes.data); } catch (err) { console.error('Failed to fetch prerequisites', err); setError('Could not load required data (roles, projects).'); } }; if (isOpen) { fetchPrerequisites(); } }, [isOpen]); // Effect to set up the form when the user prop changes (for editing) or when opening for creation useEffect(() => { const setupForm = async () => { if (isEditMode) { // Edit mode: populate form with user data setFormData({ username: user.username, email: user.email, first_name: user.first_name || '', last_name: user.last_name || '', is_active: user.is_active, }); setSelectedSystemRoles(new Set(user.Roles?.map(role => role.id) || [])); // Fetch this user's specific project roles try { const res = await api.get(`/rbac/user-project-roles?userId=${user.id}`); setProjectRoles(res.data); } catch (err) { console.error("Failed to fetch user's project roles", err); setProjectRoles([]); } } else { // Create mode: reset all fields setFormData({ username: '', email: '', password: '', first_name: '', last_name: '', is_active: true }); setSelectedSystemRoles(new Set()); setProjectRoles([]); } // Reset local state setError(''); setSelectedProjectId(''); setSelectedRoleId(''); }; if (isOpen) { setupForm(); } }, [user, isOpen]); const handleInputChange = (e) => { const { id, value } = e.target; setFormData((prev) => ({ ...prev, [id]: value })); }; const handleSystemRoleChange = (roleId) => { setSelectedSystemRoles(prev => { const newSet = new Set(prev); if (newSet.has(roleId)) newSet.delete(roleId); else newSet.add(roleId); return newSet; }); }; const handleAddProjectRole = async () => { if (!selectedProjectId || !selectedRoleId) { setError("Please select both a project and a role."); return; } setIsLoading(true); setError(''); try { await api.post('/rbac/user-project-roles', { userId: user.id, projectId: selectedProjectId, roleId: selectedRoleId }); // Refresh the list after adding const res = await api.get(`/rbac/user-project-roles?userId=${user.id}`); setProjectRoles(res.data); setSelectedProjectId(''); setSelectedRoleId(''); } catch(err) { setError(err.response?.data?.message || 'Failed to add project role.'); } finally { setIsLoading(false); } }; const handleRemoveProjectRole = async (assignment) => { setIsLoading(true); setError(''); try { await api.delete('/rbac/user-project-roles', { data: { userId: user.id, projectId: assignment.project_id, roleId: assignment.role_id } }); // Refresh list visually without another API call setProjectRoles(prev => prev.filter(p => p.id !== assignment.id)); } catch(err) { setError(err.response?.data?.message || 'Failed to remove project role.'); } finally { setIsLoading(false); } }; const handleSaveUserDetails = async (e) => { e.preventDefault(); setIsLoading(true); setError(''); const payload = { ...formData, roles: Array.from(selectedSystemRoles) }; try { if (isEditMode) { await api.put(`/users/${user.id}`, payload); } else { await api.post('/users', payload); } onSuccess(); // Tell the parent page to refresh its data setIsOpen(false); // Close the dialog } catch (err) { setError(err.response?.data?.message || 'An unexpected error occurred.'); } finally { setIsLoading(false); } }; return (
{isEditMode ? `Edit User: ${user.username}` : 'Create New User'}
{/* Section 1: User Details & System Roles */}

User Details & System Roles

{!isEditMode && (
)}
{allRoles.map(role => (
handleSystemRoleChange(role.id)} />
))}
setFormData(prev => ({...prev, is_active: checked}))} />
{/* Section 2: Project Role Assignments */}

Project Role Assignments

{isEditMode ? ( <>

Assign New Project Role

Current Assignments

{projectRoles.length > 0 ? projectRoles.map(pr => (
{pr.Project.name} as {pr.Role.name}
)) :

No project assignments.

}
) :

Save the user first to assign project roles.

}
{error &&

{error}

}
); }