hazo_auth 5.1.10 → 5.1.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"user_scopes_tab.d.ts","sourceRoot":"","sources":["../../../../../src/components/layouts/user_management/components/user_scopes_tab.tsx"],"names":[],"mappings":"AAqDA,MAAM,MAAM,kBAAkB,GAAG;IAC/B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AA4DF;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,EAAE,SAAS,EAAE,EAAE,kBAAkB,2CAqf9D"}
1
+ {"version":3,"file":"user_scopes_tab.d.ts","sourceRoot":"","sources":["../../../../../src/components/layouts/user_management/components/user_scopes_tab.tsx"],"names":[],"mappings":"AA4DA,MAAM,MAAM,kBAAkB,GAAG;IAC/B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAkEF;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,EAAE,SAAS,EAAE,EAAE,kBAAkB,2CA0jB9D"}
@@ -11,6 +11,7 @@ import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, D
11
11
  import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, } from "../../../ui/alert-dialog.js";
12
12
  import { Input } from "../../../ui/input.js";
13
13
  import { Label } from "../../../ui/label.js";
14
+ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "../../../ui/select.js";
14
15
  import { Avatar, AvatarFallback, AvatarImage } from "../../../ui/avatar.js";
15
16
  import { TreeView } from "../../../ui/tree-view.js";
16
17
  import { Loader2, Plus, Trash2, Search, CircleCheck, CircleX, ChevronRight, Building2, FolderTree, } from "lucide-react";
@@ -56,6 +57,10 @@ export function UserScopesTab({ className }) {
56
57
  const [treeLoading, setTreeLoading] = useState(false);
57
58
  const [selectedTreeItem, setSelectedTreeItem] = useState();
58
59
  const [actionLoading, setActionLoading] = useState(false);
60
+ // Roles state
61
+ const [roles, setRoles] = useState([]);
62
+ const [rolesLoading, setRolesLoading] = useState(false);
63
+ const [selectedRoleId, setSelectedRoleId] = useState("");
59
64
  // Delete scope dialog state
60
65
  const [deleteDialogOpen, setDeleteDialogOpen] = useState(false);
61
66
  const [scopeToDelete, setScopeToDelete] = useState(null);
@@ -136,11 +141,37 @@ export function UserScopesTab({ className }) {
136
141
  setTreeLoading(false);
137
142
  }
138
143
  }, [apiBasePath]);
144
+ // Load roles for add dialog
145
+ const loadRoles = useCallback(async () => {
146
+ var _a;
147
+ setRolesLoading(true);
148
+ try {
149
+ const response = await fetch(`${apiBasePath}/user_management/roles`);
150
+ const data = await response.json();
151
+ if (data.success) {
152
+ setRoles(data.roles || []);
153
+ // Auto-select first role if available and none selected
154
+ if (!selectedRoleId && ((_a = data.roles) === null || _a === void 0 ? void 0 : _a.length) > 0) {
155
+ setSelectedRoleId(data.roles[0].id);
156
+ }
157
+ }
158
+ else {
159
+ setRoles([]);
160
+ }
161
+ }
162
+ catch (error) {
163
+ setRoles([]);
164
+ }
165
+ finally {
166
+ setRolesLoading(false);
167
+ }
168
+ }, [apiBasePath, selectedRoleId]);
139
169
  useEffect(() => {
140
170
  if (addDialogOpen) {
141
171
  void loadScopeTree();
172
+ void loadRoles();
142
173
  }
143
- }, [addDialogOpen, loadScopeTree]);
174
+ }, [addDialogOpen, loadScopeTree, loadRoles]);
144
175
  // Filter users by search
145
176
  const filteredUsers = users.filter((user) => {
146
177
  var _a;
@@ -174,6 +205,10 @@ export function UserScopesTab({ className }) {
174
205
  toast.error("Please select a scope from the tree");
175
206
  return;
176
207
  }
208
+ if (!selectedRoleId) {
209
+ toast.error("Please select a role");
210
+ return;
211
+ }
177
212
  const scope = selectedTreeItem.scopeData;
178
213
  setActionLoading(true);
179
214
  try {
@@ -183,6 +218,7 @@ export function UserScopesTab({ className }) {
183
218
  body: JSON.stringify({
184
219
  user_id: selectedUser.id,
185
220
  scope_id: scope.id,
221
+ role_id: selectedRoleId,
186
222
  }),
187
223
  });
188
224
  const data = await response.json();
@@ -190,6 +226,7 @@ export function UserScopesTab({ className }) {
190
226
  toast.success("Scope assigned successfully");
191
227
  setAddDialogOpen(false);
192
228
  setSelectedTreeItem(undefined);
229
+ setSelectedRoleId("");
193
230
  await loadUserScopes();
194
231
  }
195
232
  else {
@@ -245,7 +282,7 @@ export function UserScopesTab({ className }) {
245
282
  }, variant: "outline", size: "sm", children: [_jsx(Plus, { className: "h-4 w-4 mr-2" }), "Assign First Scope"] })] })) : (_jsxs(Table, { className: "w-full", children: [_jsx(TableHeader, { children: _jsxs(TableRow, { children: [_jsx(TableHead, { children: "Scope Name" }), _jsx(TableHead, { children: "Level" }), _jsx(TableHead, { children: "Scope ID" }), _jsx(TableHead, { children: "Assigned" }), _jsx(TableHead, { className: "text-right w-[80px]", children: "Actions" })] }) }), _jsx(TableBody, { children: userScopes.map((scope) => (_jsxs(TableRow, { children: [_jsx(TableCell, { className: "font-medium", children: scope.scope_name || "Unknown" }), _jsx(TableCell, { className: "text-sm", children: scope.level || "-" }), _jsxs(TableCell, { className: "font-mono text-xs text-muted-foreground", children: [scope.scope_id.substring(0, 8), "..."] }), _jsx(TableCell, { className: "text-sm text-muted-foreground", children: new Date(scope.created_at).toLocaleDateString() }), _jsx(TableCell, { className: "text-right", children: _jsx(Button, { onClick: () => {
246
283
  setScopeToDelete(scope);
247
284
  setDeleteDialogOpen(true);
248
- }, variant: "outline", size: "sm", className: "text-destructive", children: _jsx(Trash2, { className: "h-4 w-4" }) }) })] }, scope.scope_id))) })] })) })] }), _jsx(Dialog, { open: addDialogOpen, onOpenChange: setAddDialogOpen, children: _jsxs(DialogContent, { className: "cls_user_scopes_add_dialog sm:max-w-[500px]", children: [_jsxs(DialogHeader, { children: [_jsx(DialogTitle, { children: "Add Scope Assignment" }), _jsxs(DialogDescription, { children: ["Select a scope from the tree to assign to", " ", (selectedUser === null || selectedUser === void 0 ? void 0 : selectedUser.name) || (selectedUser === null || selectedUser === void 0 ? void 0 : selectedUser.email_address), "."] })] }), _jsxs("div", { className: "flex flex-col gap-4 py-4", children: [_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: "Select Scope" }), treeLoading ? (_jsx("div", { className: "flex items-center justify-center p-8 border rounded-lg", children: _jsx(Loader2, { className: "h-6 w-6 animate-spin text-slate-400" }) })) : scopeTree.length === 0 ? (_jsxs("div", { className: "flex flex-col items-center justify-center p-6 border rounded-lg border-dashed", children: [_jsx(FolderTree, { className: "h-8 w-8 text-muted-foreground mb-2" }), _jsx("p", { className: "text-sm text-muted-foreground text-center", children: "No scopes available. Create scopes in the Scope Hierarchy tab first." })] })) : (_jsx("div", { className: "border rounded-lg max-h-[300px] overflow-auto", children: _jsx(TreeView, { data: treeData, expandAll: true, defaultNodeIcon: Building2, defaultLeafIcon: Building2, onSelectChange: handleTreeSelectChange, initialSelectedItemId: selectedTreeItem === null || selectedTreeItem === void 0 ? void 0 : selectedTreeItem.id, className: "w-full" }) }))] }), (selectedTreeItem === null || selectedTreeItem === void 0 ? void 0 : selectedTreeItem.scopeData) && (_jsxs("div", { className: "p-3 border rounded-lg bg-muted/50", children: [_jsxs("p", { className: "text-sm", children: [_jsx("span", { className: "font-medium", children: "Selected:" }), " ", selectedTreeItem.scopeData.name] }), _jsxs("p", { className: "text-xs text-muted-foreground", children: [selectedTreeItem.scopeData.level, " - ID: ", selectedTreeItem.scopeData.id.substring(0, 8), "..."] })] }))] }), _jsxs(DialogFooter, { children: [_jsx(Button, { onClick: handleAddScope, disabled: actionLoading || !(selectedTreeItem === null || selectedTreeItem === void 0 ? void 0 : selectedTreeItem.scopeData), variant: "default", children: actionLoading ? (_jsxs(_Fragment, { children: [_jsx(Loader2, { className: "h-4 w-4 mr-2 animate-spin" }), "Assigning..."] })) : (_jsxs(_Fragment, { children: [_jsx(CircleCheck, { className: "h-4 w-4 mr-2" }), "Assign Scope"] })) }), _jsxs(Button, { onClick: () => setAddDialogOpen(false), variant: "outline", children: [_jsx(CircleX, { className: "h-4 w-4 mr-2" }), "Cancel"] })] })] }) }), _jsx(AlertDialog, { open: deleteDialogOpen, onOpenChange: setDeleteDialogOpen, children: _jsxs(AlertDialogContent, { children: [_jsxs(AlertDialogHeader, { children: [_jsx(AlertDialogTitle, { children: "Remove Scope Assignment" }), _jsxs(AlertDialogDescription, { children: ["Are you sure you want to remove the scope \"", (scopeToDelete === null || scopeToDelete === void 0 ? void 0 : scopeToDelete.scope_name) || (scopeToDelete === null || scopeToDelete === void 0 ? void 0 : scopeToDelete.scope_id.substring(0, 8)), "\" from", " ", (selectedUser === null || selectedUser === void 0 ? void 0 : selectedUser.name) || (selectedUser === null || selectedUser === void 0 ? void 0 : selectedUser.email_address), "? This will also revoke access to any child scopes."] })] }), _jsxs(AlertDialogFooter, { children: [_jsx(AlertDialogAction, { onClick: handleRemoveScope, disabled: actionLoading, children: actionLoading ? (_jsxs(_Fragment, { children: [_jsx(Loader2, { className: "h-4 w-4 mr-2 animate-spin" }), "Removing..."] })) : ("Remove") }), _jsx(AlertDialogCancel, { onClick: () => {
285
+ }, variant: "outline", size: "sm", className: "text-destructive", children: _jsx(Trash2, { className: "h-4 w-4" }) }) })] }, scope.scope_id))) })] })) })] }), _jsx(Dialog, { open: addDialogOpen, onOpenChange: setAddDialogOpen, children: _jsxs(DialogContent, { className: "cls_user_scopes_add_dialog sm:max-w-[500px]", children: [_jsxs(DialogHeader, { children: [_jsx(DialogTitle, { children: "Add Scope Assignment" }), _jsxs(DialogDescription, { children: ["Select a scope from the tree to assign to", " ", (selectedUser === null || selectedUser === void 0 ? void 0 : selectedUser.name) || (selectedUser === null || selectedUser === void 0 ? void 0 : selectedUser.email_address), "."] })] }), _jsxs("div", { className: "flex flex-col gap-4 py-4", children: [_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: "Select Scope" }), treeLoading ? (_jsx("div", { className: "flex items-center justify-center p-8 border rounded-lg", children: _jsx(Loader2, { className: "h-6 w-6 animate-spin text-slate-400" }) })) : scopeTree.length === 0 ? (_jsxs("div", { className: "flex flex-col items-center justify-center p-6 border rounded-lg border-dashed", children: [_jsx(FolderTree, { className: "h-8 w-8 text-muted-foreground mb-2" }), _jsx("p", { className: "text-sm text-muted-foreground text-center", children: "No scopes available. Create scopes in the Scope Hierarchy tab first." })] })) : (_jsx("div", { className: "border rounded-lg max-h-[300px] overflow-auto", children: _jsx(TreeView, { data: treeData, expandAll: true, defaultNodeIcon: Building2, defaultLeafIcon: Building2, onSelectChange: handleTreeSelectChange, initialSelectedItemId: selectedTreeItem === null || selectedTreeItem === void 0 ? void 0 : selectedTreeItem.id, className: "w-full" }) }))] }), (selectedTreeItem === null || selectedTreeItem === void 0 ? void 0 : selectedTreeItem.scopeData) && (_jsxs("div", { className: "p-3 border rounded-lg bg-muted/50", children: [_jsxs("p", { className: "text-sm", children: [_jsx("span", { className: "font-medium", children: "Selected:" }), " ", selectedTreeItem.scopeData.name] }), _jsxs("p", { className: "text-xs text-muted-foreground", children: [selectedTreeItem.scopeData.level, " - ID: ", selectedTreeItem.scopeData.id.substring(0, 8), "..."] })] })), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: "Assign Role" }), rolesLoading ? (_jsxs("div", { className: "flex items-center gap-2 p-2 text-sm text-muted-foreground", children: [_jsx(Loader2, { className: "h-4 w-4 animate-spin" }), "Loading roles..."] })) : roles.length === 0 ? (_jsx("p", { className: "text-sm text-muted-foreground", children: "No roles available. Create roles in the Roles tab first." })) : (_jsxs(Select, { value: selectedRoleId, onValueChange: setSelectedRoleId, children: [_jsx(SelectTrigger, { className: "w-full", children: _jsx(SelectValue, { placeholder: "Select a role" }) }), _jsx(SelectContent, { children: roles.map((role) => (_jsxs(SelectItem, { value: role.id, children: [role.name, role.description && (_jsxs("span", { className: "text-muted-foreground ml-2", children: ["- ", role.description] }))] }, role.id))) })] }))] })] }), _jsxs(DialogFooter, { children: [_jsx(Button, { onClick: handleAddScope, disabled: actionLoading || !(selectedTreeItem === null || selectedTreeItem === void 0 ? void 0 : selectedTreeItem.scopeData) || !selectedRoleId, variant: "default", children: actionLoading ? (_jsxs(_Fragment, { children: [_jsx(Loader2, { className: "h-4 w-4 mr-2 animate-spin" }), "Assigning..."] })) : (_jsxs(_Fragment, { children: [_jsx(CircleCheck, { className: "h-4 w-4 mr-2" }), "Assign Scope"] })) }), _jsxs(Button, { onClick: () => setAddDialogOpen(false), variant: "outline", children: [_jsx(CircleX, { className: "h-4 w-4 mr-2" }), "Cancel"] })] })] }) }), _jsx(AlertDialog, { open: deleteDialogOpen, onOpenChange: setDeleteDialogOpen, children: _jsxs(AlertDialogContent, { children: [_jsxs(AlertDialogHeader, { children: [_jsx(AlertDialogTitle, { children: "Remove Scope Assignment" }), _jsxs(AlertDialogDescription, { children: ["Are you sure you want to remove the scope \"", (scopeToDelete === null || scopeToDelete === void 0 ? void 0 : scopeToDelete.scope_name) || (scopeToDelete === null || scopeToDelete === void 0 ? void 0 : scopeToDelete.scope_id.substring(0, 8)), "\" from", " ", (selectedUser === null || selectedUser === void 0 ? void 0 : selectedUser.name) || (selectedUser === null || selectedUser === void 0 ? void 0 : selectedUser.email_address), "? This will also revoke access to any child scopes."] })] }), _jsxs(AlertDialogFooter, { children: [_jsx(AlertDialogAction, { onClick: handleRemoveScope, disabled: actionLoading, children: actionLoading ? (_jsxs(_Fragment, { children: [_jsx(Loader2, { className: "h-4 w-4 mr-2 animate-spin" }), "Removing..."] })) : ("Remove") }), _jsx(AlertDialogCancel, { onClick: () => {
249
286
  setDeleteDialogOpen(false);
250
287
  setScopeToDelete(null);
251
288
  }, children: "Cancel" })] })] }) })] }));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hazo_auth",
3
- "version": "5.1.10",
3
+ "version": "5.1.11",
4
4
  "description": "Zero-config authentication UI components for Next.js with RBAC, OAuth, scope-based multi-tenancy, and invitations",
5
5
  "keywords": [
6
6
  "authentication",