hazo_auth 3.0.3 → 4.0.0

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.
Files changed (67) hide show
  1. package/README.md +146 -0
  2. package/SETUP_CHECKLIST.md +369 -0
  3. package/dist/app/api/hazo_auth/library_photo/[category]/[filename]/route.d.ts +2 -2
  4. package/dist/app/api/hazo_auth/library_photo/[category]/[filename]/route.d.ts.map +1 -1
  5. package/dist/app/api/hazo_auth/library_photo/[category]/[filename]/route.js +1 -1
  6. package/dist/app/api/hazo_auth/profile_picture/[filename]/route.d.ts +2 -2
  7. package/dist/app/api/hazo_auth/profile_picture/[filename]/route.d.ts.map +1 -1
  8. package/dist/app/api/hazo_auth/profile_picture/[filename]/route.js +1 -1
  9. package/dist/components/layouts/my_settings/components/profile_picture_library_tab.d.ts.map +1 -1
  10. package/dist/components/layouts/my_settings/components/profile_picture_library_tab.js +2 -2
  11. package/dist/components/layouts/rbac_test/index.d.ts +15 -0
  12. package/dist/components/layouts/rbac_test/index.d.ts.map +1 -0
  13. package/dist/components/layouts/rbac_test/index.js +378 -0
  14. package/dist/components/layouts/shared/components/password_field.js +1 -1
  15. package/dist/components/layouts/shared/components/sidebar_layout_wrapper.d.ts.map +1 -1
  16. package/dist/components/layouts/shared/components/sidebar_layout_wrapper.js +2 -2
  17. package/dist/components/layouts/shared/components/two_column_auth_layout.js +1 -1
  18. package/dist/components/layouts/user_management/components/roles_matrix.d.ts +2 -3
  19. package/dist/components/layouts/user_management/components/roles_matrix.d.ts.map +1 -1
  20. package/dist/components/layouts/user_management/components/roles_matrix.js +133 -8
  21. package/dist/components/layouts/user_management/components/scope_hierarchy_tab.d.ts +12 -0
  22. package/dist/components/layouts/user_management/components/scope_hierarchy_tab.d.ts.map +1 -0
  23. package/dist/components/layouts/user_management/components/scope_hierarchy_tab.js +291 -0
  24. package/dist/components/layouts/user_management/components/scope_labels_tab.d.ts +13 -0
  25. package/dist/components/layouts/user_management/components/scope_labels_tab.d.ts.map +1 -0
  26. package/dist/components/layouts/user_management/components/scope_labels_tab.js +158 -0
  27. package/dist/components/layouts/user_management/components/user_scopes_tab.d.ts +11 -0
  28. package/dist/components/layouts/user_management/components/user_scopes_tab.d.ts.map +1 -0
  29. package/dist/components/layouts/user_management/components/user_scopes_tab.js +267 -0
  30. package/dist/components/layouts/user_management/index.d.ts +9 -2
  31. package/dist/components/layouts/user_management/index.d.ts.map +1 -1
  32. package/dist/components/layouts/user_management/index.js +22 -6
  33. package/dist/components/ui/select.d.ts +14 -0
  34. package/dist/components/ui/select.d.ts.map +1 -0
  35. package/dist/components/ui/select.js +59 -0
  36. package/dist/components/ui/tree-view.d.ts +108 -0
  37. package/dist/components/ui/tree-view.d.ts.map +1 -0
  38. package/dist/components/ui/tree-view.js +194 -0
  39. package/dist/lib/auth/auth_types.d.ts +45 -0
  40. package/dist/lib/auth/auth_types.d.ts.map +1 -1
  41. package/dist/lib/auth/auth_types.js +13 -0
  42. package/dist/lib/auth/hazo_get_auth.server.d.ts +4 -2
  43. package/dist/lib/auth/hazo_get_auth.server.d.ts.map +1 -1
  44. package/dist/lib/auth/hazo_get_auth.server.js +107 -3
  45. package/dist/lib/auth/scope_cache.d.ts +92 -0
  46. package/dist/lib/auth/scope_cache.d.ts.map +1 -0
  47. package/dist/lib/auth/scope_cache.js +171 -0
  48. package/dist/lib/scope_hierarchy_config.server.d.ts +39 -0
  49. package/dist/lib/scope_hierarchy_config.server.d.ts.map +1 -0
  50. package/dist/lib/scope_hierarchy_config.server.js +96 -0
  51. package/dist/lib/services/email_service.d.ts.map +1 -1
  52. package/dist/lib/services/email_service.js +7 -2
  53. package/dist/lib/services/profile_picture_service.d.ts +1 -7
  54. package/dist/lib/services/profile_picture_service.d.ts.map +1 -1
  55. package/dist/lib/services/profile_picture_service.js +77 -32
  56. package/dist/lib/services/registration_service.js +1 -1
  57. package/dist/lib/services/scope_labels_service.d.ts +48 -0
  58. package/dist/lib/services/scope_labels_service.d.ts.map +1 -0
  59. package/dist/lib/services/scope_labels_service.js +277 -0
  60. package/dist/lib/services/scope_service.d.ts +114 -0
  61. package/dist/lib/services/scope_service.d.ts.map +1 -0
  62. package/dist/lib/services/scope_service.js +582 -0
  63. package/dist/lib/services/user_scope_service.d.ts +74 -0
  64. package/dist/lib/services/user_scope_service.d.ts.map +1 -0
  65. package/dist/lib/services/user_scope_service.js +415 -0
  66. package/hazo_auth_config.example.ini +1 -1
  67. package/package.json +5 -3
@@ -0,0 +1,158 @@
1
+ // file_description: Scope Labels tab component for configuring friendly names for scope levels
2
+ // section: client_directive
3
+ "use client";
4
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
5
+ // section: imports
6
+ import { useState, useEffect, useCallback } from "react";
7
+ import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from "../../../ui/table";
8
+ import { Button } from "../../../ui/button";
9
+ import { Input } from "../../../ui/input";
10
+ import { Label } from "../../../ui/label";
11
+ import { Loader2, Save } from "lucide-react";
12
+ import { toast } from "sonner";
13
+ import { useHazoAuthConfig } from "../../../../contexts/hazo_auth_provider";
14
+ const SCOPE_LEVELS = [
15
+ "hazo_scopes_l1",
16
+ "hazo_scopes_l2",
17
+ "hazo_scopes_l3",
18
+ "hazo_scopes_l4",
19
+ "hazo_scopes_l5",
20
+ "hazo_scopes_l6",
21
+ "hazo_scopes_l7",
22
+ ];
23
+ // section: component
24
+ /**
25
+ * Scope Labels tab component for configuring friendly names for scope levels
26
+ * Shows all 7 scope levels with their current labels from database
27
+ * Empty inputs for levels without labels - no placeholders
28
+ * @param props - Component props
29
+ * @returns Scope Labels tab component
30
+ */
31
+ export function ScopeLabelsTab({ className, defaultOrg = "" }) {
32
+ const { apiBasePath } = useHazoAuthConfig();
33
+ // State - simple record of scope_type to label string (empty string if not set)
34
+ const [labels, setLabels] = useState(() => {
35
+ const initial = {};
36
+ for (const level of SCOPE_LEVELS) {
37
+ initial[level] = "";
38
+ }
39
+ return initial;
40
+ });
41
+ const [originalLabels, setOriginalLabels] = useState(null);
42
+ const [loading, setLoading] = useState(true);
43
+ const [saving, setSaving] = useState(false);
44
+ const [org, setOrg] = useState(defaultOrg);
45
+ // Load labels from database (only real DB records, not synthetic defaults)
46
+ const loadLabels = useCallback(async () => {
47
+ if (!org.trim()) {
48
+ // Reset to empty if no org
49
+ const empty = {};
50
+ for (const level of SCOPE_LEVELS) {
51
+ empty[level] = "";
52
+ }
53
+ setLabels(empty);
54
+ setOriginalLabels(empty);
55
+ setLoading(false);
56
+ return;
57
+ }
58
+ setLoading(true);
59
+ try {
60
+ // Fetch WITHOUT defaults - only get actual DB records
61
+ const params = new URLSearchParams({ org: org.trim(), include_defaults: "false" });
62
+ const response = await fetch(`${apiBasePath}/scope_management/labels?${params}`);
63
+ const data = await response.json();
64
+ if (data.success) {
65
+ // Start with empty labels
66
+ const newLabels = {};
67
+ for (const level of SCOPE_LEVELS) {
68
+ newLabels[level] = "";
69
+ }
70
+ // Fill in labels from database
71
+ const dbLabels = data.labels || [];
72
+ for (const dbLabel of dbLabels) {
73
+ if (dbLabel.scope_type && dbLabel.label) {
74
+ newLabels[dbLabel.scope_type] = dbLabel.label;
75
+ }
76
+ }
77
+ setLabels(newLabels);
78
+ setOriginalLabels(Object.assign({}, newLabels));
79
+ }
80
+ else {
81
+ toast.error(data.error || "Failed to load labels");
82
+ }
83
+ }
84
+ catch (_a) {
85
+ toast.error("Failed to load labels");
86
+ }
87
+ finally {
88
+ setLoading(false);
89
+ }
90
+ }, [apiBasePath, org]);
91
+ // Load labels when org changes
92
+ useEffect(() => {
93
+ void loadLabels();
94
+ }, [loadLabels]);
95
+ // Handle label change
96
+ const handleLabelChange = (level, value) => {
97
+ setLabels((prev) => (Object.assign(Object.assign({}, prev), { [level]: value })));
98
+ };
99
+ // Check if there are unsaved changes
100
+ const hasChanges = () => {
101
+ if (!originalLabels)
102
+ return false;
103
+ for (const level of SCOPE_LEVELS) {
104
+ if (labels[level] !== originalLabels[level]) {
105
+ return true;
106
+ }
107
+ }
108
+ return false;
109
+ };
110
+ // Save all labels - send all non-empty labels to be upserted
111
+ const handleSave = async () => {
112
+ if (!org.trim()) {
113
+ toast.error("Organization is required");
114
+ return;
115
+ }
116
+ // Collect all non-empty labels
117
+ const labelsToSave = [];
118
+ for (const level of SCOPE_LEVELS) {
119
+ const label = labels[level].trim();
120
+ if (label) {
121
+ labelsToSave.push({
122
+ scope_type: level,
123
+ label: label,
124
+ });
125
+ }
126
+ }
127
+ setSaving(true);
128
+ try {
129
+ const response = await fetch(`${apiBasePath}/scope_management/labels`, {
130
+ method: "PUT",
131
+ headers: { "Content-Type": "application/json" },
132
+ body: JSON.stringify({
133
+ org: org.trim(),
134
+ labels: labelsToSave,
135
+ }),
136
+ });
137
+ const data = await response.json();
138
+ if (data.success) {
139
+ toast.success("Labels saved successfully");
140
+ // Reload to get fresh state
141
+ await loadLabels();
142
+ }
143
+ else {
144
+ toast.error(data.error || "Failed to save labels");
145
+ }
146
+ }
147
+ catch (_a) {
148
+ toast.error("Failed to save labels");
149
+ }
150
+ finally {
151
+ setSaving(false);
152
+ }
153
+ };
154
+ return (_jsxs("div", { className: `cls_scope_labels_tab flex flex-col gap-4 w-full ${className || ""}`, children: [_jsxs("div", { className: "cls_scope_labels_header flex items-center justify-between gap-4 flex-wrap", children: [_jsx("div", { className: "cls_scope_labels_header_left flex items-center gap-4", children: _jsxs("div", { className: "cls_scope_labels_org_input flex items-center gap-2", children: [_jsx(Label, { htmlFor: "labels_org", className: "text-sm font-medium", children: "Organization:" }), _jsx(Input, { id: "labels_org", value: org, onChange: (e) => setOrg(e.target.value), placeholder: "Enter organization name", className: "w-[200px]" })] }) }), _jsx("div", { className: "cls_scope_labels_header_right", children: _jsx(Button, { onClick: handleSave, disabled: saving || !hasChanges() || !org.trim(), variant: "default", size: "sm", children: saving ? (_jsxs(_Fragment, { children: [_jsx(Loader2, { className: "h-4 w-4 mr-2 animate-spin" }), "Saving..."] })) : (_jsxs(_Fragment, { children: [_jsx(Save, { className: "h-4 w-4 mr-2" }), "Save Changes"] })) }) })] }), loading ? (_jsx("div", { className: "cls_scope_labels_loading flex items-center justify-center p-8", children: _jsx(Loader2, { className: "h-6 w-6 animate-spin text-slate-400" }) })) : (_jsx("div", { className: "cls_scope_labels_table_container border rounded-lg overflow-auto w-full", children: _jsxs(Table, { className: "cls_scope_labels_table w-full", children: [_jsx(TableHeader, { children: _jsxs(TableRow, { children: [_jsx(TableHead, { className: "w-[180px]", children: "Scope Level" }), _jsx(TableHead, { children: "Label" })] }) }), _jsx(TableBody, { children: SCOPE_LEVELS.map((level) => {
155
+ const label = labels[level];
156
+ return (_jsxs(TableRow, { children: [_jsx(TableCell, { className: "font-mono text-sm", children: level }), _jsx(TableCell, { children: _jsx(Input, { value: label, onChange: (e) => handleLabelChange(level, e.target.value), className: "max-w-[400px]", disabled: !org.trim() }) })] }, level));
157
+ }) })] }) })), !org.trim() && (_jsx("div", { className: "cls_scope_labels_info text-sm text-muted-foreground text-center p-4 bg-muted/50 rounded-lg", children: "Enter an organization name to customize scope labels." }))] }));
158
+ }
@@ -0,0 +1,11 @@
1
+ export type UserScopesTabProps = {
2
+ className?: string;
3
+ };
4
+ /**
5
+ * User Scopes tab component for assigning scopes to users
6
+ * Two-panel layout: Users list | Scope assignments
7
+ * @param props - Component props
8
+ * @returns User Scopes tab component
9
+ */
10
+ export declare function UserScopesTab({ className }: UserScopesTabProps): import("react/jsx-runtime").JSX.Element;
11
+ //# sourceMappingURL=user_scopes_tab.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"user_scopes_tab.d.ts","sourceRoot":"","sources":["../../../../../src/components/layouts/user_management/components/user_scopes_tab.tsx"],"names":[],"mappings":"AAoDA,MAAM,MAAM,kBAAkB,GAAG;IAC/B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AA6EF;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,EAAE,SAAS,EAAE,EAAE,kBAAkB,2CA8f9D"}
@@ -0,0 +1,267 @@
1
+ // file_description: User Scopes tab component for assigning scopes to users in HRBAC
2
+ // section: client_directive
3
+ "use client";
4
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
5
+ // section: imports
6
+ import { useState, useEffect, useCallback, useMemo } from "react";
7
+ import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from "../../../ui/table";
8
+ import { Button } from "../../../ui/button";
9
+ import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from "../../../ui/dialog";
10
+ import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, } from "../../../ui/alert-dialog";
11
+ import { Input } from "../../../ui/input";
12
+ import { Label } from "../../../ui/label";
13
+ import { Avatar, AvatarFallback, AvatarImage } from "../../../ui/avatar";
14
+ import { TreeView } from "../../../ui/tree-view";
15
+ import { Loader2, Plus, Trash2, Search, CircleCheck, CircleX, ChevronRight, Building2, FolderTree, } from "lucide-react";
16
+ import { toast } from "sonner";
17
+ import { useHazoAuthConfig } from "../../../../contexts/hazo_auth_provider";
18
+ const SCOPE_LEVEL_LABELS = {
19
+ hazo_scopes_l1: "Level 1",
20
+ hazo_scopes_l2: "Level 2",
21
+ hazo_scopes_l3: "Level 3",
22
+ hazo_scopes_l4: "Level 4",
23
+ hazo_scopes_l5: "Level 5",
24
+ hazo_scopes_l6: "Level 6",
25
+ hazo_scopes_l7: "Level 7",
26
+ };
27
+ // Convert ScopeTreeNode to TreeDataItem format for selection
28
+ function convertToTreeData(nodes) {
29
+ return nodes.map((node) => {
30
+ const hasChildren = node.children && node.children.length > 0;
31
+ const item = {
32
+ id: node.id,
33
+ name: `${node.name} (${node.seq})`,
34
+ icon: Building2,
35
+ scopeData: node,
36
+ };
37
+ if (hasChildren) {
38
+ item.children = convertToTreeData(node.children);
39
+ }
40
+ return item;
41
+ });
42
+ }
43
+ // section: component
44
+ /**
45
+ * User Scopes tab component for assigning scopes to users
46
+ * Two-panel layout: Users list | Scope assignments
47
+ * @param props - Component props
48
+ * @returns User Scopes tab component
49
+ */
50
+ export function UserScopesTab({ className }) {
51
+ const { apiBasePath } = useHazoAuthConfig();
52
+ // Users state
53
+ const [users, setUsers] = useState([]);
54
+ const [usersLoading, setUsersLoading] = useState(true);
55
+ const [userSearch, setUserSearch] = useState("");
56
+ const [selectedUser, setSelectedUser] = useState(null);
57
+ // User scopes state
58
+ const [userScopes, setUserScopes] = useState([]);
59
+ const [scopesLoading, setScopesLoading] = useState(false);
60
+ const [inheritedLevels, setInheritedLevels] = useState([]);
61
+ // Add scope dialog state
62
+ const [addDialogOpen, setAddDialogOpen] = useState(false);
63
+ const [scopeTree, setScopeTree] = useState([]);
64
+ const [treeLoading, setTreeLoading] = useState(false);
65
+ const [selectedTreeItem, setSelectedTreeItem] = useState();
66
+ const [actionLoading, setActionLoading] = useState(false);
67
+ // Delete scope dialog state
68
+ const [deleteDialogOpen, setDeleteDialogOpen] = useState(false);
69
+ const [scopeToDelete, setScopeToDelete] = useState(null);
70
+ // Load users
71
+ useEffect(() => {
72
+ const loadUsers = async () => {
73
+ setUsersLoading(true);
74
+ try {
75
+ const response = await fetch(`${apiBasePath}/user_management/users`);
76
+ const data = await response.json();
77
+ if (data.success) {
78
+ setUsers(data.users || []);
79
+ }
80
+ else {
81
+ toast.error(data.error || "Failed to load users");
82
+ }
83
+ }
84
+ catch (error) {
85
+ toast.error("Failed to load users");
86
+ }
87
+ finally {
88
+ setUsersLoading(false);
89
+ }
90
+ };
91
+ void loadUsers();
92
+ }, [apiBasePath]);
93
+ // Load user scopes when user selected
94
+ const loadUserScopes = useCallback(async () => {
95
+ if (!selectedUser) {
96
+ setUserScopes([]);
97
+ setInheritedLevels([]);
98
+ return;
99
+ }
100
+ setScopesLoading(true);
101
+ try {
102
+ const params = new URLSearchParams({
103
+ user_id: selectedUser.id,
104
+ include_effective: "true",
105
+ });
106
+ const response = await fetch(`${apiBasePath}/user_management/users/scopes?${params}`);
107
+ const data = await response.json();
108
+ if (data.success) {
109
+ setUserScopes(data.direct_scopes || []);
110
+ setInheritedLevels(data.inherited_scope_types || []);
111
+ }
112
+ else {
113
+ toast.error(data.error || "Failed to load user scopes");
114
+ }
115
+ }
116
+ catch (error) {
117
+ toast.error("Failed to load user scopes");
118
+ }
119
+ finally {
120
+ setScopesLoading(false);
121
+ }
122
+ }, [apiBasePath, selectedUser]);
123
+ useEffect(() => {
124
+ void loadUserScopes();
125
+ }, [loadUserScopes]);
126
+ // Load scope tree for add dialog (all scopes across all orgs)
127
+ const loadScopeTree = useCallback(async () => {
128
+ setTreeLoading(true);
129
+ try {
130
+ const params = new URLSearchParams({ action: "tree_all" });
131
+ const response = await fetch(`${apiBasePath}/scope_management/scopes?${params}`);
132
+ const data = await response.json();
133
+ if (data.success) {
134
+ setScopeTree(data.trees || []);
135
+ }
136
+ else {
137
+ setScopeTree([]);
138
+ }
139
+ }
140
+ catch (error) {
141
+ setScopeTree([]);
142
+ }
143
+ finally {
144
+ setTreeLoading(false);
145
+ }
146
+ }, [apiBasePath]);
147
+ useEffect(() => {
148
+ if (addDialogOpen) {
149
+ void loadScopeTree();
150
+ }
151
+ }, [addDialogOpen, loadScopeTree]);
152
+ // Filter users by search
153
+ const filteredUsers = users.filter((user) => {
154
+ var _a;
155
+ const search = userSearch.toLowerCase();
156
+ return ((((_a = user.name) === null || _a === void 0 ? void 0 : _a.toLowerCase().includes(search)) || false) ||
157
+ user.email_address.toLowerCase().includes(search));
158
+ });
159
+ // Get user initials
160
+ const getUserInitials = (user) => {
161
+ var _a, _b;
162
+ if (user.name) {
163
+ const parts = user.name.trim().split(" ");
164
+ if (parts.length >= 2) {
165
+ return `${parts[0][0]}${parts[parts.length - 1][0]}`.toUpperCase();
166
+ }
167
+ return ((_a = user.name[0]) === null || _a === void 0 ? void 0 : _a.toUpperCase()) || "";
168
+ }
169
+ return ((_b = user.email_address[0]) === null || _b === void 0 ? void 0 : _b.toUpperCase()) || "?";
170
+ };
171
+ // Convert tree to TreeDataItem format
172
+ const treeData = useMemo(() => {
173
+ return convertToTreeData(scopeTree);
174
+ }, [scopeTree]);
175
+ // Handle tree item selection
176
+ const handleTreeSelectChange = (item) => {
177
+ setSelectedTreeItem(item);
178
+ };
179
+ // Handle add scope
180
+ const handleAddScope = async () => {
181
+ if (!selectedUser || !(selectedTreeItem === null || selectedTreeItem === void 0 ? void 0 : selectedTreeItem.scopeData)) {
182
+ toast.error("Please select a scope from the tree");
183
+ return;
184
+ }
185
+ const scope = selectedTreeItem.scopeData;
186
+ setActionLoading(true);
187
+ try {
188
+ const response = await fetch(`${apiBasePath}/user_management/users/scopes`, {
189
+ method: "POST",
190
+ headers: { "Content-Type": "application/json" },
191
+ body: JSON.stringify({
192
+ user_id: selectedUser.id,
193
+ scope_type: scope.level,
194
+ scope_id: scope.id,
195
+ scope_seq: scope.seq,
196
+ }),
197
+ });
198
+ const data = await response.json();
199
+ if (data.success) {
200
+ toast.success("Scope assigned successfully");
201
+ setAddDialogOpen(false);
202
+ setSelectedTreeItem(undefined);
203
+ await loadUserScopes();
204
+ }
205
+ else {
206
+ toast.error(data.error || "Failed to assign scope");
207
+ }
208
+ }
209
+ catch (error) {
210
+ toast.error("Failed to assign scope");
211
+ }
212
+ finally {
213
+ setActionLoading(false);
214
+ }
215
+ };
216
+ // Handle remove scope
217
+ const handleRemoveScope = async () => {
218
+ if (!selectedUser || !scopeToDelete)
219
+ return;
220
+ setActionLoading(true);
221
+ try {
222
+ const params = new URLSearchParams({
223
+ user_id: selectedUser.id,
224
+ scope_type: scopeToDelete.scope_type,
225
+ scope_id: scopeToDelete.scope_id,
226
+ });
227
+ const response = await fetch(`${apiBasePath}/user_management/users/scopes?${params}`, {
228
+ method: "DELETE",
229
+ });
230
+ const data = await response.json();
231
+ if (data.success) {
232
+ toast.success("Scope removed successfully");
233
+ setDeleteDialogOpen(false);
234
+ setScopeToDelete(null);
235
+ await loadUserScopes();
236
+ }
237
+ else {
238
+ toast.error(data.error || "Failed to remove scope");
239
+ }
240
+ }
241
+ catch (error) {
242
+ toast.error("Failed to remove scope");
243
+ }
244
+ finally {
245
+ setActionLoading(false);
246
+ }
247
+ };
248
+ // Get level label
249
+ const getLevelLabel = (level) => {
250
+ return SCOPE_LEVEL_LABELS[level] || level;
251
+ };
252
+ return (_jsxs("div", { className: `cls_user_scopes_tab flex flex-col lg:flex-row gap-4 w-full min-h-[500px] ${className || ""}`, children: [_jsxs("div", { className: "cls_user_scopes_users_panel w-full lg:w-1/3 flex flex-col border rounded-lg", children: [_jsxs("div", { className: "cls_user_scopes_users_header p-4 border-b bg-muted/30", children: [_jsx("h3", { className: "font-semibold mb-2", children: "Select User" }), _jsxs("div", { className: "relative", children: [_jsx(Search, { className: "absolute left-2 top-2.5 h-4 w-4 text-muted-foreground" }), _jsx(Input, { value: userSearch, onChange: (e) => setUserSearch(e.target.value), placeholder: "Search users...", className: "pl-8" })] })] }), _jsx("div", { className: "cls_user_scopes_users_list flex-1 overflow-auto", children: usersLoading ? (_jsx("div", { className: "flex items-center justify-center p-8", children: _jsx(Loader2, { className: "h-6 w-6 animate-spin text-slate-400" }) })) : filteredUsers.length === 0 ? (_jsx("div", { className: "text-center text-muted-foreground p-8", children: "No users found." })) : (_jsx("div", { className: "divide-y", children: filteredUsers.map((user) => (_jsxs("div", { className: `cls_user_scopes_user_item flex items-center gap-3 p-3 cursor-pointer hover:bg-muted/50 transition-colors ${(selectedUser === null || selectedUser === void 0 ? void 0 : selectedUser.id) === user.id ? "bg-muted" : ""}`, onClick: () => setSelectedUser(user), children: [_jsxs(Avatar, { className: "h-8 w-8", children: [_jsx(AvatarImage, { src: user.profile_picture_url || undefined }), _jsx(AvatarFallback, { className: "bg-slate-200 text-slate-600 text-xs", children: getUserInitials(user) })] }), _jsxs("div", { className: "flex-1 min-w-0", children: [_jsx("p", { className: "text-sm font-medium truncate", children: user.name || user.email_address }), user.name && (_jsx("p", { className: "text-xs text-muted-foreground truncate", children: user.email_address }))] }), (selectedUser === null || selectedUser === void 0 ? void 0 : selectedUser.id) === user.id && (_jsx(ChevronRight, { className: "h-4 w-4 text-muted-foreground" }))] }, user.id))) })) })] }), _jsxs("div", { className: "cls_user_scopes_assignments_panel w-full lg:w-2/3 flex flex-col border rounded-lg", children: [_jsxs("div", { className: "cls_user_scopes_assignments_header p-4 border-b bg-muted/30 flex items-center justify-between", children: [_jsxs("div", { children: [_jsx("h3", { className: "font-semibold", children: selectedUser
253
+ ? `Scopes for ${selectedUser.name || selectedUser.email_address}`
254
+ : "Select a user to view scopes" }), selectedUser && inheritedLevels.length > 0 && (_jsxs("p", { className: "text-xs text-muted-foreground mt-1", children: ["Inherits access to: ", inheritedLevels.map(getLevelLabel).join(", ")] }))] }), selectedUser && (_jsxs(Button, { onClick: () => {
255
+ setSelectedTreeItem(undefined);
256
+ setAddDialogOpen(true);
257
+ }, variant: "default", size: "sm", children: [_jsx(Plus, { className: "h-4 w-4 mr-2" }), "Add Scope"] }))] }), _jsx("div", { className: "cls_user_scopes_assignments_content flex-1 overflow-auto", children: !selectedUser ? (_jsx("div", { className: "flex items-center justify-center h-full text-muted-foreground", children: "Select a user from the left panel to manage their scope assignments." })) : scopesLoading ? (_jsx("div", { className: "flex items-center justify-center h-full", children: _jsx(Loader2, { className: "h-6 w-6 animate-spin text-slate-400" }) })) : userScopes.length === 0 ? (_jsxs("div", { className: "flex flex-col items-center justify-center h-full gap-4 text-muted-foreground", children: [_jsx("p", { children: "No scopes assigned to this user." }), _jsxs(Button, { onClick: () => {
258
+ setSelectedTreeItem(undefined);
259
+ setAddDialogOpen(true);
260
+ }, 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: "Level" }), _jsx(TableHead, { children: "Scope Seq" }), _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: getLevelLabel(scope.scope_type) }), _jsx(TableCell, { className: "font-mono text-sm", children: scope.scope_seq }), _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: () => {
261
+ setScopeToDelete(scope);
262
+ setDeleteDialogOpen(true);
263
+ }, variant: "outline", size: "sm", className: "text-destructive", children: _jsx(Trash2, { className: "h-4 w-4" }) }) })] }, `${scope.scope_type}-${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: [SCOPE_LEVEL_LABELS[selectedTreeItem.scopeData.level], " -", " ", selectedTreeItem.scopeData.seq, " (", selectedTreeItem.scopeData.org, ")"] })] }))] }), _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_seq, "\" 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 inherited 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: () => {
264
+ setDeleteDialogOpen(false);
265
+ setScopeToDelete(null);
266
+ }, children: "Cancel" })] })] }) })] }));
267
+ }
@@ -1,13 +1,20 @@
1
1
  export type UserManagementLayoutProps = {
2
2
  className?: string;
3
+ /** Whether HRBAC is enabled (passed from server) */
4
+ hrbacEnabled?: boolean;
5
+ /** Default organization for HRBAC scopes */
6
+ defaultOrg?: string;
3
7
  };
4
8
  /**
5
- * User Management layout component with three tabs
9
+ * User Management layout component with tabs for managing users, roles, permissions, and HRBAC scopes
6
10
  * Tab 1: Manage Users - data table with user details and actions
7
11
  * Tab 2: Roles - roles-permissions matrix
8
12
  * Tab 3: Permissions - manage permissions from DB and config
13
+ * Tab 4: Scope Labels - customize scope level labels (if HRBAC enabled)
14
+ * Tab 5: Scope Hierarchy - manage HRBAC scopes (if HRBAC enabled)
15
+ * Tab 6: User Scopes - assign scopes to users (if HRBAC enabled)
9
16
  * @param props - Component props
10
17
  * @returns User Management layout component
11
18
  */
12
- export declare function UserManagementLayout({ className }: UserManagementLayoutProps): import("react/jsx-runtime").JSX.Element;
19
+ export declare function UserManagementLayout({ className, hrbacEnabled, defaultOrg }: UserManagementLayoutProps): import("react/jsx-runtime").JSX.Element;
13
20
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/layouts/user_management/index.tsx"],"names":[],"mappings":"AA6CA,MAAM,MAAM,yBAAyB,GAAG;IACtC,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAsBF;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAAC,EAAE,SAAS,EAAE,EAAE,yBAAyB,2CAmsC5E"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/layouts/user_management/index.tsx"],"names":[],"mappings":"AAgDA,MAAM,MAAM,yBAAyB,GAAG;IACtC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,oDAAoD;IACpD,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,4CAA4C;IAC5C,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC;AAsBF;;;;;;;;;;GAUG;AACH,wBAAgB,oBAAoB,CAAC,EAAE,SAAS,EAAE,YAAoB,EAAE,UAAe,EAAE,EAAE,yBAAyB,2CAivCnH"}
@@ -1,4 +1,4 @@
1
- // file_description: User Management layout component with three tabs for managing users, roles, and permissions
1
+ // file_description: User Management layout component with tabs for managing users, roles, permissions, and HRBAC scopes
2
2
  // section: client_directive
3
3
  "use client";
4
4
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
@@ -14,20 +14,26 @@ import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, D
14
14
  import { Input } from "../../ui/input";
15
15
  import { Label } from "../../ui/label";
16
16
  import { RolesMatrix } from "./components/roles_matrix";
17
+ import { ScopeHierarchyTab } from "./components/scope_hierarchy_tab";
18
+ import { ScopeLabelsTab } from "./components/scope_labels_tab";
19
+ import { UserScopesTab } from "./components/user_scopes_tab";
17
20
  import { UserX, KeyRound, Edit, Trash2, Loader2, CircleCheck, CircleX, Plus, UserPlus } from "lucide-react";
18
21
  import { toast } from "sonner";
19
22
  import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "../../ui/tooltip";
20
23
  import { useHazoAuthConfig } from "../../../contexts/hazo_auth_provider";
21
24
  // section: component
22
25
  /**
23
- * User Management layout component with three tabs
26
+ * User Management layout component with tabs for managing users, roles, permissions, and HRBAC scopes
24
27
  * Tab 1: Manage Users - data table with user details and actions
25
28
  * Tab 2: Roles - roles-permissions matrix
26
29
  * Tab 3: Permissions - manage permissions from DB and config
30
+ * Tab 4: Scope Labels - customize scope level labels (if HRBAC enabled)
31
+ * Tab 5: Scope Hierarchy - manage HRBAC scopes (if HRBAC enabled)
32
+ * Tab 6: User Scopes - assign scopes to users (if HRBAC enabled)
27
33
  * @param props - Component props
28
34
  * @returns User Management layout component
29
35
  */
30
- export function UserManagementLayout({ className }) {
36
+ export function UserManagementLayout({ className, hrbacEnabled = false, defaultOrg = "" }) {
31
37
  const { apiBasePath } = useHazoAuthConfig();
32
38
  // Permission checks
33
39
  const authResult = use_hazo_auth();
@@ -37,11 +43,18 @@ export function UserManagementLayout({ className }) {
37
43
  authResult.permissions.includes("admin_role_management");
38
44
  const hasPermissionManagementPermission = authResult.authenticated &&
39
45
  authResult.permissions.includes("admin_permission_management");
46
+ const hasScopeHierarchyPermission = authResult.authenticated &&
47
+ authResult.permissions.includes("admin_scope_hierarchy_management");
48
+ const hasUserScopeAssignmentPermission = authResult.authenticated &&
49
+ authResult.permissions.includes("admin_user_scope_assignment");
40
50
  // Determine which tabs to show
41
51
  const showUsersTab = hasUserManagementPermission;
42
52
  const showRolesTab = hasRoleManagementPermission;
43
53
  const showPermissionsTab = hasPermissionManagementPermission;
44
- const hasAnyPermission = showUsersTab || showRolesTab || showPermissionsTab;
54
+ const showScopeHierarchyTab = hrbacEnabled && hasScopeHierarchyPermission;
55
+ const showScopeLabelsTab = hrbacEnabled && hasScopeHierarchyPermission;
56
+ const showUserScopesTab = hrbacEnabled && hasUserScopeAssignmentPermission;
57
+ const hasAnyPermission = showUsersTab || showRolesTab || showPermissionsTab || showScopeHierarchyTab || showScopeLabelsTab || showUserScopesTab;
45
58
  // Tab 1: Users state
46
59
  const [users, setUsers] = useState([]);
47
60
  const [usersLoading, setUsersLoading] = useState(true);
@@ -420,7 +433,10 @@ export function UserManagementLayout({ className }) {
420
433
  };
421
434
  return (_jsxs("div", { className: `cls_user_management_layout flex flex-col gap-4 w-full ${className || ""}`, children: [authResult.loading ? (_jsx("div", { className: "cls_user_management_permissions_loading flex items-center justify-center p-8", children: _jsx(Loader2, { className: "h-6 w-6 animate-spin text-slate-400" }) })) : !hasAnyPermission ? (_jsxs("div", { className: "cls_user_management_no_permissions flex flex-col items-center justify-center p-8 gap-4", children: [_jsx("p", { className: "text-lg font-semibold text-slate-700", children: "Access Denied" }), _jsx("p", { className: "text-sm text-muted-foreground text-center", children: "You don't have permission to access User Management. Please contact your administrator." })] })) : (_jsxs(Tabs, { defaultValue: showUsersTab ? "users" :
422
435
  showRolesTab ? "roles" :
423
- showPermissionsTab ? "permissions" : "users", className: "cls_user_management_tabs w-full", children: [_jsxs(TabsList, { className: "cls_user_management_tabs_list flex w-full", children: [showUsersTab && (_jsx(TabsTrigger, { value: "users", className: "cls_user_management_tabs_trigger flex-1", children: "Manage Users" })), showRolesTab && (_jsx(TabsTrigger, { value: "roles", className: "cls_user_management_tabs_trigger flex-1", children: "Roles" })), showPermissionsTab && (_jsx(TabsTrigger, { value: "permissions", className: "cls_user_management_tabs_trigger flex-1", children: "Permissions" }))] }), showUsersTab && (_jsx(TabsContent, { value: "users", className: "cls_user_management_tab_users w-full", children: usersLoading ? (_jsx("div", { className: "cls_user_management_users_loading flex items-center justify-center p-8", children: _jsx(Loader2, { className: "h-6 w-6 animate-spin text-slate-400" }) })) : (_jsx("div", { className: "cls_user_management_users_table_container border rounded-lg overflow-auto w-full", children: _jsxs(Table, { className: "cls_user_management_users_table w-full", children: [_jsx(TableHeader, { className: "cls_user_management_users_table_header", children: _jsxs(TableRow, { className: "cls_user_management_users_table_header_row", children: [_jsx(TableHead, { className: "cls_user_management_users_table_header_profile_pic w-16", children: "Photo" }), _jsx(TableHead, { className: "cls_user_management_users_table_header_id", children: "ID" }), _jsx(TableHead, { className: "cls_user_management_users_table_header_name", children: "Name" }), _jsx(TableHead, { className: "cls_user_management_users_table_header_email", children: "Email" }), _jsx(TableHead, { className: "cls_user_management_users_table_header_email_verified", children: "Email Verified" }), _jsx(TableHead, { className: "cls_user_management_users_table_header_is_active", children: "Active" }), _jsx(TableHead, { className: "cls_user_management_users_table_header_last_logon", children: "Last Logon" }), _jsx(TableHead, { className: "cls_user_management_users_table_header_created_at", children: "Created At" }), _jsx(TableHead, { className: "cls_user_management_users_table_header_actions text-right", children: "Actions" })] }) }), _jsx(TableBody, { className: "cls_user_management_users_table_body", children: users.length === 0 ? (_jsx(TableRow, { className: "cls_user_management_users_table_row_empty", children: _jsx(TableCell, { colSpan: 9, className: "text-center text-muted-foreground py-8", children: "No users found." }) })) : (users.map((user) => (_jsxs(TableRow, { className: "cls_user_management_users_table_row cursor-pointer hover:bg-muted/50", onClick: () => {
436
+ showPermissionsTab ? "permissions" :
437
+ showScopeLabelsTab ? "scope_labels" :
438
+ showScopeHierarchyTab ? "scope_hierarchy" :
439
+ showUserScopesTab ? "user_scopes" : "users", className: "cls_user_management_tabs w-full", children: [_jsxs(TabsList, { className: "cls_user_management_tabs_list flex w-full flex-wrap", children: [showUsersTab && (_jsx(TabsTrigger, { value: "users", className: "cls_user_management_tabs_trigger flex-1", children: "Manage Users" })), showRolesTab && (_jsx(TabsTrigger, { value: "roles", className: "cls_user_management_tabs_trigger flex-1", children: "Roles" })), showPermissionsTab && (_jsx(TabsTrigger, { value: "permissions", className: "cls_user_management_tabs_trigger flex-1", children: "Permissions" })), showScopeLabelsTab && (_jsx(TabsTrigger, { value: "scope_labels", className: "cls_user_management_tabs_trigger flex-1", children: "Scope Labels" })), showScopeHierarchyTab && (_jsx(TabsTrigger, { value: "scope_hierarchy", className: "cls_user_management_tabs_trigger flex-1", children: "Scope Hierarchy" })), showUserScopesTab && (_jsx(TabsTrigger, { value: "user_scopes", className: "cls_user_management_tabs_trigger flex-1", children: "User Scopes" }))] }), showUsersTab && (_jsx(TabsContent, { value: "users", className: "cls_user_management_tab_users w-full", children: usersLoading ? (_jsx("div", { className: "cls_user_management_users_loading flex items-center justify-center p-8", children: _jsx(Loader2, { className: "h-6 w-6 animate-spin text-slate-400" }) })) : (_jsx("div", { className: "cls_user_management_users_table_container border rounded-lg overflow-auto w-full", children: _jsxs(Table, { className: "cls_user_management_users_table w-full", children: [_jsx(TableHeader, { className: "cls_user_management_users_table_header", children: _jsxs(TableRow, { className: "cls_user_management_users_table_header_row", children: [_jsx(TableHead, { className: "cls_user_management_users_table_header_profile_pic w-16", children: "Photo" }), _jsx(TableHead, { className: "cls_user_management_users_table_header_id", children: "ID" }), _jsx(TableHead, { className: "cls_user_management_users_table_header_name", children: "Name" }), _jsx(TableHead, { className: "cls_user_management_users_table_header_email", children: "Email" }), _jsx(TableHead, { className: "cls_user_management_users_table_header_email_verified", children: "Email Verified" }), _jsx(TableHead, { className: "cls_user_management_users_table_header_is_active", children: "Active" }), _jsx(TableHead, { className: "cls_user_management_users_table_header_last_logon", children: "Last Logon" }), _jsx(TableHead, { className: "cls_user_management_users_table_header_created_at", children: "Created At" }), _jsx(TableHead, { className: "cls_user_management_users_table_header_actions text-right", children: "Actions" })] }) }), _jsx(TableBody, { className: "cls_user_management_users_table_body", children: users.length === 0 ? (_jsx(TableRow, { className: "cls_user_management_users_table_row_empty", children: _jsx(TableCell, { colSpan: 9, className: "text-center text-muted-foreground py-8", children: "No users found." }) })) : (users.map((user) => (_jsxs(TableRow, { className: "cls_user_management_users_table_row cursor-pointer hover:bg-muted/50", onClick: () => {
424
440
  setSelectedUser(user);
425
441
  setUserDetailDialogOpen(true);
426
442
  }, children: [_jsx(TableCell, { className: "cls_user_management_users_table_cell_profile_pic", children: _jsxs(Avatar, { className: "cls_user_management_users_table_avatar h-8 w-8", children: [_jsx(AvatarImage, { src: user.profile_picture_url || undefined, alt: user.name ? `Profile picture of ${user.name}` : "Profile picture", className: "cls_user_management_users_table_avatar_image" }), _jsx(AvatarFallback, { className: "cls_user_management_users_table_avatar_fallback bg-slate-200 text-slate-600 text-xs", children: getUserInitials(user) })] }) }), _jsxs(TableCell, { className: "cls_user_management_users_table_cell_id font-mono text-xs", children: [user.id.substring(0, 8), "..."] }), _jsx(TableCell, { className: "cls_user_management_users_table_cell_name", children: user.name || "-" }), _jsx(TableCell, { className: "cls_user_management_users_table_cell_email", children: user.email_address }), _jsx(TableCell, { className: "cls_user_management_users_table_cell_email_verified", children: user.email_verified ? (_jsx("span", { className: "text-green-600", children: "Yes" })) : (_jsx("span", { className: "text-red-600", children: "No" })) }), _jsx(TableCell, { className: "cls_user_management_users_table_cell_is_active", children: user.is_active ? (_jsx("span", { className: "text-green-600", children: "Active" })) : (_jsx("span", { className: "text-red-600", children: "Inactive" })) }), _jsx(TableCell, { className: "cls_user_management_users_table_cell_last_logon", children: user.last_logon
@@ -445,7 +461,7 @@ export function UserManagementLayout({ className }) {
445
461
  setEditingPermission(permission);
446
462
  setEditDescription(permission.description);
447
463
  setEditPermissionDialogOpen(true);
448
- }, variant: "outline", size: "sm", className: "cls_user_management_permissions_table_action_edit", children: [_jsx(Edit, { className: "h-4 w-4 mr-1" }), "Edit"] }), _jsxs(Button, { onClick: () => handleDeletePermission(permission), disabled: permissionsActionLoading, variant: "outline", size: "sm", className: "cls_user_management_permissions_table_action_delete text-destructive", children: [_jsx(Trash2, { className: "h-4 w-4 mr-1" }), "Delete"] })] })) }) })] }, `${permission.source}-${permission.id}-${permission.permission_name}`)))) })] }) }))] }) }))] })), _jsx(AlertDialog, { open: deactivateDialogOpen, onOpenChange: setDeactivateDialogOpen, children: _jsxs(AlertDialogContent, { className: "cls_user_management_deactivate_dialog", children: [_jsxs(AlertDialogHeader, { children: [_jsx(AlertDialogTitle, { children: "Deactivate User" }), _jsxs(AlertDialogDescription, { children: ["Are you sure you want to deactivate ", (selectedUser === null || selectedUser === void 0 ? void 0 : selectedUser.name) || (selectedUser === null || selectedUser === void 0 ? void 0 : selectedUser.email_address), "? They will not be able to log in until reactivated."] })] }), _jsxs(AlertDialogFooter, { className: "cls_user_management_deactivate_dialog_footer", children: [_jsx(AlertDialogAction, { onClick: handleDeactivateUser, disabled: usersActionLoading, className: "cls_user_management_deactivate_dialog_confirm", children: usersActionLoading ? (_jsxs(_Fragment, { children: [_jsx(Loader2, { className: "h-4 w-4 mr-2 animate-spin" }), "Deactivating..."] })) : ("Deactivate") }), _jsx(AlertDialogCancel, { onClick: () => {
464
+ }, variant: "outline", size: "sm", className: "cls_user_management_permissions_table_action_edit", children: [_jsx(Edit, { className: "h-4 w-4 mr-1" }), "Edit"] }), _jsxs(Button, { onClick: () => handleDeletePermission(permission), disabled: permissionsActionLoading, variant: "outline", size: "sm", className: "cls_user_management_permissions_table_action_delete text-destructive", children: [_jsx(Trash2, { className: "h-4 w-4 mr-1" }), "Delete"] })] })) }) })] }, `${permission.source}-${permission.id}-${permission.permission_name}`)))) })] }) }))] }) })), showScopeLabelsTab && (_jsx(TabsContent, { value: "scope_labels", className: "cls_user_management_tab_scope_labels w-full", children: _jsx(ScopeLabelsTab, { defaultOrg: defaultOrg }) })), showScopeHierarchyTab && (_jsx(TabsContent, { value: "scope_hierarchy", className: "cls_user_management_tab_scope_hierarchy w-full", children: _jsx(ScopeHierarchyTab, { defaultOrg: defaultOrg }) })), showUserScopesTab && (_jsx(TabsContent, { value: "user_scopes", className: "cls_user_management_tab_user_scopes w-full", children: _jsx(UserScopesTab, {}) }))] })), _jsx(AlertDialog, { open: deactivateDialogOpen, onOpenChange: setDeactivateDialogOpen, children: _jsxs(AlertDialogContent, { className: "cls_user_management_deactivate_dialog", children: [_jsxs(AlertDialogHeader, { children: [_jsx(AlertDialogTitle, { children: "Deactivate User" }), _jsxs(AlertDialogDescription, { children: ["Are you sure you want to deactivate ", (selectedUser === null || selectedUser === void 0 ? void 0 : selectedUser.name) || (selectedUser === null || selectedUser === void 0 ? void 0 : selectedUser.email_address), "? They will not be able to log in until reactivated."] })] }), _jsxs(AlertDialogFooter, { className: "cls_user_management_deactivate_dialog_footer", children: [_jsx(AlertDialogAction, { onClick: handleDeactivateUser, disabled: usersActionLoading, className: "cls_user_management_deactivate_dialog_confirm", children: usersActionLoading ? (_jsxs(_Fragment, { children: [_jsx(Loader2, { className: "h-4 w-4 mr-2 animate-spin" }), "Deactivating..."] })) : ("Deactivate") }), _jsx(AlertDialogCancel, { onClick: () => {
449
465
  setDeactivateDialogOpen(false);
450
466
  setSelectedUser(null);
451
467
  }, className: "cls_user_management_deactivate_dialog_cancel", children: "Cancel" })] })] }) }), _jsx(AlertDialog, { open: resetPasswordDialogOpen, onOpenChange: setResetPasswordDialogOpen, children: _jsxs(AlertDialogContent, { className: "cls_user_management_reset_password_dialog", children: [_jsxs(AlertDialogHeader, { children: [_jsx(AlertDialogTitle, { children: "Reset Password" }), _jsxs(AlertDialogDescription, { children: ["Send a password reset email to ", selectedUser === null || selectedUser === void 0 ? void 0 : selectedUser.email_address, "? They will receive a link to reset their password."] })] }), _jsxs(AlertDialogFooter, { className: "cls_user_management_reset_password_dialog_footer", children: [_jsx(AlertDialogAction, { onClick: handleResetPassword, disabled: usersActionLoading, className: "cls_user_management_reset_password_dialog_confirm", children: usersActionLoading ? (_jsxs(_Fragment, { children: [_jsx(Loader2, { className: "h-4 w-4 mr-2 animate-spin" }), "Sending..."] })) : ("Send Reset Email") }), _jsx(AlertDialogCancel, { onClick: () => {
@@ -0,0 +1,14 @@
1
+ import * as React from "react";
2
+ import * as SelectPrimitive from "@radix-ui/react-select";
3
+ declare const Select: React.FC<SelectPrimitive.SelectProps>;
4
+ declare const SelectGroup: React.ForwardRefExoticComponent<SelectPrimitive.SelectGroupProps & React.RefAttributes<HTMLDivElement>>;
5
+ declare const SelectValue: React.ForwardRefExoticComponent<SelectPrimitive.SelectValueProps & React.RefAttributes<HTMLSpanElement>>;
6
+ declare const SelectTrigger: React.ForwardRefExoticComponent<Omit<SelectPrimitive.SelectTriggerProps & React.RefAttributes<HTMLButtonElement>, "ref"> & React.RefAttributes<HTMLButtonElement>>;
7
+ declare const SelectScrollUpButton: React.ForwardRefExoticComponent<Omit<SelectPrimitive.SelectScrollUpButtonProps & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
8
+ declare const SelectScrollDownButton: React.ForwardRefExoticComponent<Omit<SelectPrimitive.SelectScrollDownButtonProps & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
9
+ declare const SelectContent: React.ForwardRefExoticComponent<Omit<SelectPrimitive.SelectContentProps & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
10
+ declare const SelectLabel: React.ForwardRefExoticComponent<Omit<SelectPrimitive.SelectLabelProps & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
11
+ declare const SelectItem: React.ForwardRefExoticComponent<Omit<SelectPrimitive.SelectItemProps & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
12
+ declare const SelectSeparator: React.ForwardRefExoticComponent<Omit<SelectPrimitive.SelectSeparatorProps & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
13
+ export { Select, SelectGroup, SelectValue, SelectTrigger, SelectContent, SelectLabel, SelectItem, SelectSeparator, SelectScrollUpButton, SelectScrollDownButton, };
14
+ //# sourceMappingURL=select.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"select.d.ts","sourceRoot":"","sources":["../../../src/components/ui/select.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAC9B,OAAO,KAAK,eAAe,MAAM,wBAAwB,CAAA;AAKzD,QAAA,MAAM,MAAM,uCAAuB,CAAA;AAEnC,QAAA,MAAM,WAAW,yGAAwB,CAAA;AAEzC,QAAA,MAAM,WAAW,0GAAwB,CAAA;AAEzC,QAAA,MAAM,aAAa,oKAiBjB,CAAA;AAGF,QAAA,MAAM,oBAAoB,qKAcxB,CAAA;AAGF,QAAA,MAAM,sBAAsB,uKAc1B,CAAA;AAIF,QAAA,MAAM,aAAa,8JA6BjB,CAAA;AAGF,QAAA,MAAM,WAAW,4JASf,CAAA;AAGF,QAAA,MAAM,UAAU,2JAoBd,CAAA;AAGF,QAAA,MAAM,eAAe,gKASnB,CAAA;AAGF,OAAO,EACL,MAAM,EACN,WAAW,EACX,WAAW,EACX,aAAa,EACb,aAAa,EACb,WAAW,EACX,UAAU,EACV,eAAe,EACf,oBAAoB,EACpB,sBAAsB,GACvB,CAAA"}