diff --git a/backend/internal/api/handlers/core_handlers.go b/backend/internal/api/handlers/core_handlers.go index 68a3771..eae2668 100644 --- a/backend/internal/api/handlers/core_handlers.go +++ b/backend/internal/api/handlers/core_handlers.go @@ -242,8 +242,18 @@ func (h *CoreHandlers) ListCompanies(w http.ResponseWriter, r *http.Request) { // @Router /api/v1/users [post] func (h *CoreHandlers) CreateUser(w http.ResponseWriter, r *http.Request) { ctx := r.Context() - tenantID, ok := ctx.Value(middleware.ContextTenantID).(string) - if !ok || tenantID == "" { + tenantID, _ := ctx.Value(middleware.ContextTenantID).(string) + + userRoles := middleware.ExtractRoles(ctx.Value(middleware.ContextRoles)) + isAdmin := false + for _, role := range userRoles { + if role == "ADMIN" || role == "SUPERADMIN" || role == "admin" || role == "superadmin" { + isAdmin = true + break + } + } + + if !isAdmin && tenantID == "" { http.Error(w, "Tenant ID not found in context", http.StatusForbidden) return } diff --git a/backend/internal/core/dto/user_auth.go b/backend/internal/core/dto/user_auth.go index 818a4ab..9fc8c25 100644 --- a/backend/internal/core/dto/user_auth.go +++ b/backend/internal/core/dto/user_auth.go @@ -16,7 +16,9 @@ type CreateUserRequest struct { Name string `json:"name"` Email string `json:"email"` Password string `json:"password"` - Roles []string `json:"roles"` // e.g. ["RECRUITER"] + Roles []string `json:"roles"` + Status *string `json:"status,omitempty"` + TenantID *string `json:"companyId,omitempty"` // Optional, mainly for superads to assign user to company } type UpdateUserRequest struct { diff --git a/backend/internal/core/usecases/user/create_user.go b/backend/internal/core/usecases/user/create_user.go index 12cff17..06552b4 100644 --- a/backend/internal/core/usecases/user/create_user.go +++ b/backend/internal/core/usecases/user/create_user.go @@ -36,10 +36,19 @@ func (uc *CreateUserUseCase) Execute(ctx context.Context, input dto.CreateUserRe } // 3. Create Entity - // Note: We enforce currentTenantID to ensure isolation. - user := entity.NewUser("", currentTenantID, input.Name, input.Email) + // Note: We enforce currentTenantID unless it's empty (SuperAdmin context) and input provides one. + tenantID := currentTenantID + if tenantID == "" && input.TenantID != nil { + tenantID = *input.TenantID + } + + user := entity.NewUser("", tenantID, input.Name, input.Email) user.PasswordHash = hashed + if input.Status != nil { + user.Status = *input.Status + } + // Assign roles for _, r := range input.Roles { user.AssignRole(entity.Role{Name: r}) diff --git a/frontend/src/app/dashboard/users/page.tsx b/frontend/src/app/dashboard/users/page.tsx index 10aa0c1..a359570 100644 --- a/frontend/src/app/dashboard/users/page.tsx +++ b/frontend/src/app/dashboard/users/page.tsx @@ -19,7 +19,7 @@ import { import { Label } from "@/components/ui/label" import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select" import { Plus, Search, Trash2, Loader2, RefreshCw, Pencil } from "lucide-react" -import { usersApi, type ApiUser } from "@/lib/api" +import { usersApi, adminCompaniesApi, type ApiUser, type AdminCompany } from "@/lib/api" import { getCurrentUser, isAdminUser } from "@/lib/auth" import { toast } from "sonner" import { Skeleton } from "@/components/ui/skeleton" @@ -41,11 +41,15 @@ export default function AdminUsersPage() { const [creating, setCreating] = useState(false) const [updating, setUpdating] = useState(false) const [selectedUser, setSelectedUser] = useState(null) + const [companies, setCompanies] = useState([]) + const [currentUser, setCurrentUser] = useState(null) const [formData, setFormData] = useState({ name: "", email: "", password: "", role: "candidate", + status: "active", + companyId: "", }) const [editFormData, setEditFormData] = useState({ name: "", @@ -60,9 +64,23 @@ export default function AdminUsersPage() { router.push("/dashboard") return } + setCurrentUser(user as ApiUser) // Casting safe here due to check loadUsers() + + if (user?.role === 'superadmin') { + loadCompanies() + } }, [router]) + const loadCompanies = async () => { + try { + const data = await adminCompaniesApi.list(undefined, 1, 100) + setCompanies(data.data || []) + } catch (error) { + console.error("Error loading companies:", error) + } + } + const limit = 10 const totalPages = Math.max(1, Math.ceil(totalUsers / limit)) @@ -84,10 +102,14 @@ export default function AdminUsersPage() { const handleCreate = async () => { try { setCreating(true) - await usersApi.create(formData) + const payload = { + ...formData, + roles: [formData.role], // Backend expects array + } + await usersApi.create(payload) toast.success("User created successfully!") setIsDialogOpen(false) - setFormData({ name: "", email: "", password: "", role: "candidate" }) + setFormData({ name: "", email: "", password: "", role: "candidate", status: "active", companyId: "" }) setPage(1) loadUsers(1) } catch (error) { @@ -228,7 +250,6 @@ export default function AdminUsersPage() { />
-
+ {currentUser?.role === 'superadmin' && ( +
+ + +
+ )} +
+ + +