hazo_auth 4.5.0 → 4.5.2
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.
- package/dist/app/api/hazo_auth/user_management/users/route.d.ts +4 -1
- package/dist/app/api/hazo_auth/user_management/users/route.d.ts.map +1 -1
- package/dist/app/api/hazo_auth/user_management/users/route.js +75 -8
- package/dist/components/layouts/user_management/index.d.ts.map +1 -1
- package/dist/components/layouts/user_management/index.js +61 -1
- package/package.json +1 -1
|
@@ -14,6 +14,7 @@ export declare function GET(request: NextRequest): Promise<NextResponse<{
|
|
|
14
14
|
label: string;
|
|
15
15
|
badge_color: string;
|
|
16
16
|
}[];
|
|
17
|
+
multi_tenancy_enabled: boolean;
|
|
17
18
|
users: {
|
|
18
19
|
id: unknown;
|
|
19
20
|
name: {} | null;
|
|
@@ -25,10 +26,12 @@ export declare function GET(request: NextRequest): Promise<NextResponse<{
|
|
|
25
26
|
profile_picture_url: {} | null;
|
|
26
27
|
profile_source: {} | null;
|
|
27
28
|
user_type: string | null;
|
|
29
|
+
org_id: string | null | undefined;
|
|
30
|
+
root_org_id: string | null | undefined;
|
|
28
31
|
}[];
|
|
29
32
|
}>>;
|
|
30
33
|
/**
|
|
31
|
-
* PATCH - Update user (deactivate: set is_active to false)
|
|
34
|
+
* PATCH - Update user (deactivate: set is_active to false, assign org, etc.)
|
|
32
35
|
*/
|
|
33
36
|
export declare function PATCH(request: NextRequest): Promise<NextResponse<{
|
|
34
37
|
error: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"route.d.ts","sourceRoot":"","sources":["../../../../../../src/app/api/hazo_auth/user_management/users/route.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"route.d.ts","sourceRoot":"","sources":["../../../../../../src/app/api/hazo_auth/user_management/users/route.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAsBxD,eAAO,MAAM,OAAO,kBAAkB,CAAC;AAGvC;;;GAGG;AACH,wBAAsB,GAAG,CAAC,OAAO,EAAE,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;IA+E7C;AAED;;GAEG;AACH,wBAAsB,KAAK,CAAC,OAAO,EAAE,WAAW;;;;IA2L/C;AAED;;GAEG;AACH,wBAAsB,IAAI,CAAC,OAAO,EAAE,WAAW;;;;IA2E9C"}
|
|
@@ -9,6 +9,10 @@ import { request_password_reset } from "../../../../../lib/services/password_res
|
|
|
9
9
|
import { get_auth_cache } from "../../../../../lib/auth/auth_cache";
|
|
10
10
|
import { get_auth_utility_config } from "../../../../../lib/auth_utility_config.server";
|
|
11
11
|
import { is_user_types_enabled, get_all_user_types, get_user_types_config, } from "../../../../../lib/user_types_config.server";
|
|
12
|
+
import { is_multi_tenancy_enabled } from "../../../../../lib/multi_tenancy_config.server";
|
|
13
|
+
import { get_org_by_id, can_add_user_to_org, } from "../../../../../lib/services/org_service";
|
|
14
|
+
import { get_org_cache } from "../../../../../lib/auth/org_cache";
|
|
15
|
+
import { get_multi_tenancy_config } from "../../../../../lib/multi_tenancy_config.server";
|
|
12
16
|
// section: route_config
|
|
13
17
|
export const dynamic = 'force-dynamic';
|
|
14
18
|
// section: api_handler
|
|
@@ -42,10 +46,13 @@ export async function GET(request) {
|
|
|
42
46
|
badge_color: t.badge_color,
|
|
43
47
|
}))
|
|
44
48
|
: [];
|
|
49
|
+
// Check if multi-tenancy is enabled
|
|
50
|
+
const multi_tenancy_enabled = is_multi_tenancy_enabled();
|
|
45
51
|
return NextResponse.json({
|
|
46
52
|
success: true,
|
|
47
53
|
user_types_enabled,
|
|
48
54
|
available_user_types,
|
|
55
|
+
multi_tenancy_enabled,
|
|
49
56
|
users: users.map((user) => ({
|
|
50
57
|
id: user.id,
|
|
51
58
|
name: user.name || null,
|
|
@@ -57,6 +64,9 @@ export async function GET(request) {
|
|
|
57
64
|
profile_picture_url: user.profile_picture_url || null,
|
|
58
65
|
profile_source: user.profile_source || null,
|
|
59
66
|
user_type: user.user_type || null,
|
|
67
|
+
// Include org info when multi-tenancy is enabled
|
|
68
|
+
org_id: multi_tenancy_enabled ? user.org_id || null : undefined,
|
|
69
|
+
root_org_id: multi_tenancy_enabled ? user.root_org_id || null : undefined,
|
|
60
70
|
})),
|
|
61
71
|
}, { status: 200 });
|
|
62
72
|
}
|
|
@@ -73,13 +83,13 @@ export async function GET(request) {
|
|
|
73
83
|
}
|
|
74
84
|
}
|
|
75
85
|
/**
|
|
76
|
-
* PATCH - Update user (deactivate: set is_active to false)
|
|
86
|
+
* PATCH - Update user (deactivate: set is_active to false, assign org, etc.)
|
|
77
87
|
*/
|
|
78
88
|
export async function PATCH(request) {
|
|
79
89
|
const logger = create_app_logger();
|
|
80
90
|
try {
|
|
81
91
|
const body = await request.json();
|
|
82
|
-
const { user_id, is_active, user_type } = body;
|
|
92
|
+
const { user_id, is_active, user_type, org_id } = body;
|
|
83
93
|
// user_id is always required
|
|
84
94
|
if (!user_id) {
|
|
85
95
|
return NextResponse.json({ error: "user_id is required" }, { status: 400 });
|
|
@@ -88,6 +98,7 @@ export async function PATCH(request) {
|
|
|
88
98
|
const update_data = {
|
|
89
99
|
changed_at: new Date().toISOString(),
|
|
90
100
|
};
|
|
101
|
+
const hazoConnect = get_hazo_connect_instance();
|
|
91
102
|
// Handle is_active if provided
|
|
92
103
|
if (typeof is_active === "boolean") {
|
|
93
104
|
update_data.is_active = is_active;
|
|
@@ -108,20 +119,53 @@ export async function PATCH(request) {
|
|
|
108
119
|
}
|
|
109
120
|
}
|
|
110
121
|
}
|
|
122
|
+
// Handle org_id if provided (only when multi-tenancy is enabled)
|
|
123
|
+
if (org_id !== undefined) {
|
|
124
|
+
if (!is_multi_tenancy_enabled()) {
|
|
125
|
+
return NextResponse.json({ error: "Multi-tenancy is not enabled" }, { status: 400 });
|
|
126
|
+
}
|
|
127
|
+
// Allow null to clear the org assignment
|
|
128
|
+
if (org_id === null || org_id === "") {
|
|
129
|
+
update_data.org_id = null;
|
|
130
|
+
update_data.root_org_id = null;
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
// Validate org exists
|
|
134
|
+
const org_result = await get_org_by_id(hazoConnect, org_id);
|
|
135
|
+
if (!org_result.success || !org_result.org) {
|
|
136
|
+
return NextResponse.json({ error: "Organization not found" }, { status: 400 });
|
|
137
|
+
}
|
|
138
|
+
const org = org_result.org;
|
|
139
|
+
// Check if org is active
|
|
140
|
+
if (org.active === false) {
|
|
141
|
+
return NextResponse.json({ error: "Cannot assign user to inactive organization" }, { status: 400 });
|
|
142
|
+
}
|
|
143
|
+
// Check user limit
|
|
144
|
+
const limit_check = await can_add_user_to_org(hazoConnect, org_id);
|
|
145
|
+
if (limit_check.success && !limit_check.can_add) {
|
|
146
|
+
return NextResponse.json({ error: limit_check.reason || "Organization user limit reached" }, { status: 400 });
|
|
147
|
+
}
|
|
148
|
+
// Set org_id and calculate root_org_id
|
|
149
|
+
update_data.org_id = org_id;
|
|
150
|
+
update_data.root_org_id = org.root_org_id || org_id;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
111
153
|
// Ensure there's something to update besides changed_at
|
|
112
154
|
if (Object.keys(update_data).length === 1) {
|
|
113
155
|
return NextResponse.json({ error: "No valid fields to update" }, { status: 400 });
|
|
114
156
|
}
|
|
115
|
-
const hazoConnect = get_hazo_connect_instance();
|
|
116
157
|
const users_service = createCrudService(hazoConnect, "hazo_users");
|
|
117
158
|
// Update user
|
|
118
159
|
await users_service.updateById(user_id, update_data);
|
|
119
|
-
// Invalidate
|
|
120
|
-
|
|
160
|
+
// Invalidate caches
|
|
161
|
+
let cache_invalidated = false;
|
|
162
|
+
// Invalidate user auth cache if user deactivated or org changed
|
|
163
|
+
if (is_active === false || org_id !== undefined) {
|
|
121
164
|
try {
|
|
122
|
-
const
|
|
123
|
-
const
|
|
124
|
-
|
|
165
|
+
const auth_config = get_auth_utility_config();
|
|
166
|
+
const auth_cache = get_auth_cache(auth_config.cache_max_users, auth_config.cache_ttl_minutes, auth_config.cache_max_age_minutes);
|
|
167
|
+
auth_cache.invalidate_user(user_id);
|
|
168
|
+
cache_invalidated = true;
|
|
125
169
|
}
|
|
126
170
|
catch (cache_error) {
|
|
127
171
|
// Log but don't fail user update if cache invalidation fails
|
|
@@ -134,11 +178,34 @@ export async function PATCH(request) {
|
|
|
134
178
|
});
|
|
135
179
|
}
|
|
136
180
|
}
|
|
181
|
+
// If org changed, also invalidate org cache for old and new orgs
|
|
182
|
+
if (org_id !== undefined && is_multi_tenancy_enabled()) {
|
|
183
|
+
try {
|
|
184
|
+
const mt_config = get_multi_tenancy_config();
|
|
185
|
+
const org_cache = get_org_cache(mt_config.org_cache_max_entries, mt_config.org_cache_ttl_minutes);
|
|
186
|
+
// Invalidate for the new org if set
|
|
187
|
+
if (org_id && typeof org_id === "string") {
|
|
188
|
+
org_cache.invalidate(org_id);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
catch (cache_error) {
|
|
192
|
+
// Log but don't fail user update if cache invalidation fails
|
|
193
|
+
const cache_error_message = cache_error instanceof Error ? cache_error.message : "Unknown error";
|
|
194
|
+
logger.warn("user_management_org_cache_invalidation_failed", {
|
|
195
|
+
filename: get_filename(),
|
|
196
|
+
line_number: get_line_number(),
|
|
197
|
+
user_id,
|
|
198
|
+
org_id,
|
|
199
|
+
error: cache_error_message,
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
}
|
|
137
203
|
logger.info("user_management_user_updated", {
|
|
138
204
|
filename: get_filename(),
|
|
139
205
|
line_number: get_line_number(),
|
|
140
206
|
user_id,
|
|
141
207
|
updated_fields: Object.keys(update_data).filter((k) => k !== "changed_at"),
|
|
208
|
+
cache_invalidated,
|
|
142
209
|
});
|
|
143
210
|
return NextResponse.json({ success: true }, { status: 200 });
|
|
144
211
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/layouts/user_management/index.tsx"],"names":[],"mappings":"AA0DA,4CAA4C;AAC5C,MAAM,MAAM,cAAc,GAAG;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,yBAAyB,GAAG;IACtC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,oDAAoD;IACpD,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,4DAA4D;IAC5D,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,iEAAiE;IACjE,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,6DAA6D;IAC7D,kBAAkB,CAAC,EAAE,cAAc,EAAE,CAAC;CACvC,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/layouts/user_management/index.tsx"],"names":[],"mappings":"AA0DA,4CAA4C;AAC5C,MAAM,MAAM,cAAc,GAAG;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,yBAAyB,GAAG;IACtC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,oDAAoD;IACpD,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,4DAA4D;IAC5D,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,iEAAiE;IACjE,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,6DAA6D;IAC7D,kBAAkB,CAAC,EAAE,cAAc,EAAE,CAAC;CACvC,CAAC;AA8BF;;;;;;;;;;GAUG;AACH,wBAAgB,oBAAoB,CAAC,EAAE,SAAS,EAAE,YAAoB,EAAE,mBAA2B,EAAE,gBAAwB,EAAE,kBAAuB,EAAE,EAAE,yBAAyB,2CAi8ClL"}
|
|
@@ -73,6 +73,8 @@ export function UserManagementLayout({ className, hrbacEnabled = false, multiTen
|
|
|
73
73
|
const [selectedUser, setSelectedUser] = useState(null);
|
|
74
74
|
const [usersActionLoading, setUsersActionLoading] = useState(false);
|
|
75
75
|
const [userTypeUpdateLoading, setUserTypeUpdateLoading] = useState(false);
|
|
76
|
+
const [orgUpdateLoading, setOrgUpdateLoading] = useState(false);
|
|
77
|
+
const [availableOrgs, setAvailableOrgs] = useState([]);
|
|
76
78
|
// Tab 3: Permissions state
|
|
77
79
|
const [permissions, setPermissions] = useState([]);
|
|
78
80
|
const [permissionsLoading, setPermissionsLoading] = useState(true);
|
|
@@ -112,6 +114,29 @@ export function UserManagementLayout({ className, hrbacEnabled = false, multiTen
|
|
|
112
114
|
}
|
|
113
115
|
void loadUsers();
|
|
114
116
|
}, [showUsersTab, loadUsers]);
|
|
117
|
+
// Load organizations (only if multi-tenancy is enabled and user has permission)
|
|
118
|
+
useEffect(() => {
|
|
119
|
+
if (!multiTenancyEnabled || !showUsersTab) {
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
const loadOrgs = async () => {
|
|
123
|
+
try {
|
|
124
|
+
const response = await fetch(`${apiBasePath}/org_management/orgs`);
|
|
125
|
+
const data = await response.json();
|
|
126
|
+
if (data.success && Array.isArray(data.orgs)) {
|
|
127
|
+
setAvailableOrgs(data.orgs.map((org) => ({
|
|
128
|
+
id: org.id,
|
|
129
|
+
name: org.name,
|
|
130
|
+
})));
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
catch (error) {
|
|
134
|
+
// Silently fail - orgs dropdown will just be empty
|
|
135
|
+
console.error("Failed to load organizations:", error);
|
|
136
|
+
}
|
|
137
|
+
};
|
|
138
|
+
void loadOrgs();
|
|
139
|
+
}, [multiTenancyEnabled, showUsersTab, apiBasePath]);
|
|
115
140
|
// Load permissions (only if user has permission)
|
|
116
141
|
useEffect(() => {
|
|
117
142
|
if (!showPermissionsTab) {
|
|
@@ -263,6 +288,41 @@ export function UserManagementLayout({ className, hrbacEnabled = false, multiTen
|
|
|
263
288
|
setUserTypeUpdateLoading(false);
|
|
264
289
|
}
|
|
265
290
|
};
|
|
291
|
+
// Handle org change
|
|
292
|
+
const handleOrgChange = async (newOrgId) => {
|
|
293
|
+
if (!selectedUser)
|
|
294
|
+
return;
|
|
295
|
+
setOrgUpdateLoading(true);
|
|
296
|
+
try {
|
|
297
|
+
const response = await fetch(`${apiBasePath}/user_management/users`, {
|
|
298
|
+
method: "PATCH",
|
|
299
|
+
headers: {
|
|
300
|
+
"Content-Type": "application/json",
|
|
301
|
+
},
|
|
302
|
+
body: JSON.stringify({
|
|
303
|
+
user_id: selectedUser.id,
|
|
304
|
+
org_id: newOrgId || null,
|
|
305
|
+
}),
|
|
306
|
+
});
|
|
307
|
+
const data = await response.json();
|
|
308
|
+
if (data.success) {
|
|
309
|
+
toast.success("Organization updated successfully");
|
|
310
|
+
// Update local state
|
|
311
|
+
setSelectedUser(Object.assign(Object.assign({}, selectedUser), { org_id: newOrgId || null }));
|
|
312
|
+
// Reload users list to get updated root_org_id from server
|
|
313
|
+
await loadUsers();
|
|
314
|
+
}
|
|
315
|
+
else {
|
|
316
|
+
toast.error(data.error || "Failed to update organization");
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
catch (error) {
|
|
320
|
+
toast.error("Failed to update organization");
|
|
321
|
+
}
|
|
322
|
+
finally {
|
|
323
|
+
setOrgUpdateLoading(false);
|
|
324
|
+
}
|
|
325
|
+
};
|
|
266
326
|
// Handle migrate permissions
|
|
267
327
|
const handleMigratePermissions = async () => {
|
|
268
328
|
var _a, _b;
|
|
@@ -552,7 +612,7 @@ export function UserManagementLayout({ className, hrbacEnabled = false, multiTen
|
|
|
552
612
|
minute: "2-digit",
|
|
553
613
|
second: "2-digit",
|
|
554
614
|
timeZoneName: "short",
|
|
555
|
-
}) })) : (_jsx("span", { className: "text-muted-foreground", children: "-" })) })] }), userTypesEnabled && (_jsxs("div", { className: "cls_user_management_user_detail_field_user_type flex flex-col gap-2", children: [_jsx(Label, { className: "cls_user_management_user_detail_label font-semibold", children: "User Type" }), _jsxs("div", { className: "cls_user_management_user_detail_user_type_value flex items-center gap-2", children: [_jsxs(Select, { value: selectedUser.user_type || "", onValueChange: (value) => handleUserTypeChange(value), disabled: userTypeUpdateLoading, children: [_jsx(SelectTrigger, { className: "w-48", children: _jsx(SelectValue, { placeholder: "Select user type" }) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: "", children: "None" }), availableUserTypes.map((type) => (_jsx(SelectItem, { value: type.key, children: type.label }, type.key)))] })] }), userTypeUpdateLoading && (_jsx(Loader2, { className: "h-4 w-4 animate-spin text-muted-foreground" }))] })] }))] })) }), _jsx(DialogFooter, { className: "cls_user_management_user_detail_dialog_footer", children: _jsx(Button, { onClick: () => setUserDetailDialogOpen(false), variant: "outline", className: "cls_user_management_user_detail_dialog_close", children: "Close" }) })] }) }), _jsx(Dialog, { open: assignRolesDialogOpen, onOpenChange: setAssignRolesDialogOpen, children: _jsxs(DialogContent, { className: "cls_user_management_assign_roles_dialog max-w-4xl max-h-[80vh] overflow-y-auto", children: [_jsxs(DialogHeader, { children: [_jsx(DialogTitle, { children: "Assign Roles to User" }), _jsxs(DialogDescription, { children: ["Select roles to assign to ", (selectedUser === null || selectedUser === void 0 ? void 0 : selectedUser.name) || (selectedUser === null || selectedUser === void 0 ? void 0 : selectedUser.email_address), ". Check the roles you want to assign, then click Save."] })] }), _jsx("div", { className: "cls_user_management_assign_roles_dialog_content py-4", children: _jsx(RolesMatrix, { add_button_enabled: false, role_name_selection_enabled: true, permissions_read_only: true, show_save_cancel: true, user_id: selectedUser === null || selectedUser === void 0 ? void 0 : selectedUser.id, onSave: (data) => {
|
|
615
|
+
}) })) : (_jsx("span", { className: "text-muted-foreground", children: "-" })) })] }), userTypesEnabled && (_jsxs("div", { className: "cls_user_management_user_detail_field_user_type flex flex-col gap-2", children: [_jsx(Label, { className: "cls_user_management_user_detail_label font-semibold", children: "User Type" }), _jsxs("div", { className: "cls_user_management_user_detail_user_type_value flex items-center gap-2", children: [_jsxs(Select, { value: selectedUser.user_type || "", onValueChange: (value) => handleUserTypeChange(value), disabled: userTypeUpdateLoading, children: [_jsx(SelectTrigger, { className: "w-48", children: _jsx(SelectValue, { placeholder: "Select user type" }) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: "", children: "None" }), availableUserTypes.map((type) => (_jsx(SelectItem, { value: type.key, children: type.label }, type.key)))] })] }), userTypeUpdateLoading && (_jsx(Loader2, { className: "h-4 w-4 animate-spin text-muted-foreground" }))] })] })), multiTenancyEnabled && (_jsxs("div", { className: "cls_user_management_user_detail_field_org flex flex-col gap-2", children: [_jsx(Label, { className: "cls_user_management_user_detail_label font-semibold", children: "Organization" }), _jsxs("div", { className: "cls_user_management_user_detail_org_value flex items-center gap-2", children: [_jsxs(Select, { value: selectedUser.org_id || "", onValueChange: (value) => handleOrgChange(value), disabled: orgUpdateLoading, children: [_jsx(SelectTrigger, { className: "w-64", children: _jsx(SelectValue, { placeholder: "Select organization" }) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: "", children: "None" }), availableOrgs.map((org) => (_jsx(SelectItem, { value: org.id, children: org.name }, org.id)))] })] }), orgUpdateLoading && (_jsx(Loader2, { className: "h-4 w-4 animate-spin text-muted-foreground" }))] })] }))] })) }), _jsx(DialogFooter, { className: "cls_user_management_user_detail_dialog_footer", children: _jsx(Button, { onClick: () => setUserDetailDialogOpen(false), variant: "outline", className: "cls_user_management_user_detail_dialog_close", children: "Close" }) })] }) }), _jsx(Dialog, { open: assignRolesDialogOpen, onOpenChange: setAssignRolesDialogOpen, children: _jsxs(DialogContent, { className: "cls_user_management_assign_roles_dialog max-w-4xl max-h-[80vh] overflow-y-auto", children: [_jsxs(DialogHeader, { children: [_jsx(DialogTitle, { children: "Assign Roles to User" }), _jsxs(DialogDescription, { children: ["Select roles to assign to ", (selectedUser === null || selectedUser === void 0 ? void 0 : selectedUser.name) || (selectedUser === null || selectedUser === void 0 ? void 0 : selectedUser.email_address), ". Check the roles you want to assign, then click Save."] })] }), _jsx("div", { className: "cls_user_management_assign_roles_dialog_content py-4", children: _jsx(RolesMatrix, { add_button_enabled: false, role_name_selection_enabled: true, permissions_read_only: true, show_save_cancel: true, user_id: selectedUser === null || selectedUser === void 0 ? void 0 : selectedUser.id, onSave: (data) => {
|
|
556
616
|
// Data is already saved by RolesMatrix component
|
|
557
617
|
console.log("User roles saved:", data);
|
|
558
618
|
// Refresh users list to show updated roles
|