hazo_auth 5.1.9 → 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.
- package/dist/components/layouts/user_management/components/app_user_data_editor.d.ts +3 -1
- package/dist/components/layouts/user_management/components/app_user_data_editor.d.ts.map +1 -1
- package/dist/components/layouts/user_management/components/app_user_data_editor.js +37 -4
- package/dist/components/layouts/user_management/components/user_scopes_tab.d.ts.map +1 -1
- package/dist/components/layouts/user_management/components/user_scopes_tab.js +39 -2
- package/dist/components/layouts/user_management/index.d.ts.map +1 -1
- package/dist/components/layouts/user_management/index.js +5 -0
- package/package.json +1 -1
|
@@ -5,8 +5,10 @@ export type AppUserDataEditorProps = {
|
|
|
5
5
|
currentData: Record<string, unknown> | null;
|
|
6
6
|
/** Callback when data is saved */
|
|
7
7
|
onSave?: (newData: Record<string, unknown>) => void;
|
|
8
|
+
/** Callback when data is cleared */
|
|
9
|
+
onClear?: () => void;
|
|
8
10
|
/** Whether the editor is read-only (no edit button) */
|
|
9
11
|
readOnly?: boolean;
|
|
10
12
|
};
|
|
11
|
-
export declare function AppUserDataEditor({ userId, currentData, onSave, readOnly, }: AppUserDataEditorProps): import("react/jsx-runtime").JSX.Element;
|
|
13
|
+
export declare function AppUserDataEditor({ userId, currentData, onSave, onClear, readOnly, }: AppUserDataEditorProps): import("react/jsx-runtime").JSX.Element;
|
|
12
14
|
//# sourceMappingURL=app_user_data_editor.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app_user_data_editor.d.ts","sourceRoot":"","sources":["../../../../../src/components/layouts/user_management/components/app_user_data_editor.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"app_user_data_editor.d.ts","sourceRoot":"","sources":["../../../../../src/components/layouts/user_management/components/app_user_data_editor.tsx"],"names":[],"mappings":"AA6CA,MAAM,MAAM,sBAAsB,GAAG;IACnC,sBAAsB;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,kCAAkC;IAClC,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IAC5C,kCAAkC;IAClC,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;IACpD,oCAAoC;IACpC,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,uDAAuD;IACvD,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB,CAAC;AAgDF,wBAAgB,iBAAiB,CAAC,EAChC,MAAM,EACN,WAAW,EACX,MAAM,EACN,OAAO,EACP,QAAgB,GACjB,EAAE,sBAAsB,2CAkXxB"}
|
|
@@ -2,14 +2,15 @@
|
|
|
2
2
|
// Renders form fields based on JSON schema from config
|
|
3
3
|
// section: client_directive
|
|
4
4
|
"use client";
|
|
5
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
5
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
6
6
|
// section: imports
|
|
7
7
|
import { useState, useEffect, useCallback } from "react";
|
|
8
8
|
import { Button } from "../../../ui/button.js";
|
|
9
9
|
import { Input } from "../../../ui/input.js";
|
|
10
10
|
import { Label } from "../../../ui/label.js";
|
|
11
11
|
import { Switch } from "../../../ui/switch.js";
|
|
12
|
-
import {
|
|
12
|
+
import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, } from "../../../ui/alert-dialog.js";
|
|
13
|
+
import { ChevronDown, ChevronRight, Edit, Loader2, Save, Trash2, X } from "lucide-react";
|
|
13
14
|
import { toast } from "sonner";
|
|
14
15
|
import { useHazoAuthConfig } from "../../../../contexts/hazo_auth_provider.js";
|
|
15
16
|
// section: helpers
|
|
@@ -49,13 +50,15 @@ function setNestedValue(obj, path, value) {
|
|
|
49
50
|
return result;
|
|
50
51
|
}
|
|
51
52
|
// section: component
|
|
52
|
-
export function AppUserDataEditor({ userId, currentData, onSave, readOnly = false, }) {
|
|
53
|
+
export function AppUserDataEditor({ userId, currentData, onSave, onClear, readOnly = false, }) {
|
|
53
54
|
const { apiBasePath } = useHazoAuthConfig();
|
|
54
55
|
const [schemaResponse, setSchemaResponse] = useState(null);
|
|
55
56
|
const [schemaLoading, setSchemaLoading] = useState(true);
|
|
56
57
|
const [isEditing, setIsEditing] = useState(false);
|
|
57
58
|
const [editData, setEditData] = useState({});
|
|
58
59
|
const [saving, setSaving] = useState(false);
|
|
60
|
+
const [clearing, setClearing] = useState(false);
|
|
61
|
+
const [showClearConfirmation, setShowClearConfirmation] = useState(false);
|
|
59
62
|
const [expandedSections, setExpandedSections] = useState(new Set());
|
|
60
63
|
// Load schema on mount
|
|
61
64
|
useEffect(() => {
|
|
@@ -121,6 +124,36 @@ export function AppUserDataEditor({ userId, currentData, onSave, readOnly = fals
|
|
|
121
124
|
setSaving(false);
|
|
122
125
|
}
|
|
123
126
|
}, [apiBasePath, userId, editData, onSave]);
|
|
127
|
+
// Clear app_user_data - uses user_management/users PATCH endpoint with null
|
|
128
|
+
const handleClear = useCallback(async () => {
|
|
129
|
+
setClearing(true);
|
|
130
|
+
try {
|
|
131
|
+
const response = await fetch(`${apiBasePath}/user_management/users`, {
|
|
132
|
+
method: "PATCH",
|
|
133
|
+
headers: { "Content-Type": "application/json" },
|
|
134
|
+
body: JSON.stringify({
|
|
135
|
+
user_id: userId,
|
|
136
|
+
app_user_data: null,
|
|
137
|
+
}),
|
|
138
|
+
});
|
|
139
|
+
const data = await response.json();
|
|
140
|
+
if (data.success) {
|
|
141
|
+
toast.success("App user data cleared");
|
|
142
|
+
setShowClearConfirmation(false);
|
|
143
|
+
onClear === null || onClear === void 0 ? void 0 : onClear();
|
|
144
|
+
}
|
|
145
|
+
else {
|
|
146
|
+
toast.error(data.error || "Failed to clear");
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
catch (error) {
|
|
150
|
+
toast.error("Failed to clear app user data");
|
|
151
|
+
console.error("Clear error:", error);
|
|
152
|
+
}
|
|
153
|
+
finally {
|
|
154
|
+
setClearing(false);
|
|
155
|
+
}
|
|
156
|
+
}, [apiBasePath, userId, onClear]);
|
|
124
157
|
// Update field value
|
|
125
158
|
const updateField = useCallback((path, value) => {
|
|
126
159
|
setEditData((prev) => setNestedValue(prev, path, value));
|
|
@@ -200,5 +233,5 @@ export function AppUserDataEditor({ userId, currentData, onSave, readOnly = fals
|
|
|
200
233
|
return (_jsx("div", { className: "text-sm", children: currentData && Object.keys(currentData).length > 0 ? (_jsx("pre", { className: "border rounded-lg p-2 bg-slate-50 overflow-x-auto text-xs", children: JSON.stringify(currentData, null, 2) })) : (_jsx("span", { className: "text-muted-foreground", children: "No app user data" })) }));
|
|
201
234
|
}
|
|
202
235
|
// Render schema-based editor
|
|
203
|
-
return (_jsxs("div", { className: "flex flex-col gap-4", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsx("h3", { className: "text-sm font-semibold uppercase tracking-wide text-muted-foreground", children: "App User Data" }), !readOnly && !isEditing && (_jsxs(Button, { variant: "outline", size: "sm", onClick: handleEdit, className: "h-8", children: [_jsx(Edit, { className: "h-3.5 w-3.5 mr-1.5" }), "Edit"] }))] }), _jsx("div", { className: "flex flex-col gap-3", children: Object.entries(schemaResponse.schema.properties).map(([sectionKey, sectionSchema]) => renderSection(sectionKey, sectionSchema)) }), isEditing && (_jsxs("div", { className: "flex justify-end gap-2 pt-3 border-t", children: [_jsxs(Button, { variant: "outline", size: "sm", onClick: handleCancel, disabled: saving, className: "h-9", children: [_jsx(X, { className: "h-4 w-4 mr-1.5" }), "Cancel"] }), _jsxs(Button, { size: "sm", onClick: handleSave, disabled: saving, className: "h-9", children: [saving ? (_jsx(Loader2, { className: "h-4 w-4 mr-1.5 animate-spin" })) : (_jsx(Save, { className: "h-4 w-4 mr-1.5" })), "Save"] })] }))] }));
|
|
236
|
+
return (_jsxs("div", { className: "flex flex-col gap-4", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsx("h3", { className: "text-sm font-semibold uppercase tracking-wide text-muted-foreground", children: "App User Data" }), !readOnly && !isEditing && (_jsxs("div", { className: "flex gap-2", children: [_jsxs(Button, { variant: "outline", size: "sm", onClick: handleEdit, className: "h-8", children: [_jsx(Edit, { className: "h-3.5 w-3.5 mr-1.5" }), "Edit"] }), currentData && Object.keys(currentData).length > 0 && (_jsxs(Button, { variant: "outline", size: "sm", onClick: () => setShowClearConfirmation(true), className: "h-8 text-destructive hover:text-destructive hover:bg-destructive/10", children: [_jsx(Trash2, { className: "h-3.5 w-3.5 mr-1.5" }), "Clear"] }))] }))] }), _jsx("div", { className: "flex flex-col gap-3", children: Object.entries(schemaResponse.schema.properties).map(([sectionKey, sectionSchema]) => renderSection(sectionKey, sectionSchema)) }), isEditing && (_jsxs("div", { className: "flex justify-end gap-2 pt-3 border-t", children: [_jsxs(Button, { variant: "outline", size: "sm", onClick: handleCancel, disabled: saving, className: "h-9", children: [_jsx(X, { className: "h-4 w-4 mr-1.5" }), "Cancel"] }), _jsxs(Button, { size: "sm", onClick: handleSave, disabled: saving, className: "h-9", children: [saving ? (_jsx(Loader2, { className: "h-4 w-4 mr-1.5 animate-spin" })) : (_jsx(Save, { className: "h-4 w-4 mr-1.5" })), "Save"] })] })), _jsx(AlertDialog, { open: showClearConfirmation, onOpenChange: setShowClearConfirmation, children: _jsxs(AlertDialogContent, { children: [_jsxs(AlertDialogHeader, { children: [_jsx(AlertDialogTitle, { children: "Clear App User Data?" }), _jsx(AlertDialogDescription, { children: "This will permanently delete all app user data for this user. This action cannot be undone." })] }), _jsxs(AlertDialogFooter, { children: [_jsx(AlertDialogCancel, { disabled: clearing, children: "Cancel" }), _jsx(AlertDialogAction, { onClick: handleClear, disabled: clearing, className: "bg-destructive text-destructive-foreground hover:bg-destructive/90", children: clearing ? (_jsxs(_Fragment, { children: [_jsx(Loader2, { className: "h-4 w-4 mr-1.5 animate-spin" }), "Clearing..."] })) : ("Clear Data") })] })] }) })] }));
|
|
204
237
|
}
|
|
@@ -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":"
|
|
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" })] })] }) })] }));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/layouts/user_management/index.tsx"],"names":[],"mappings":"AAyDA,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,iEAAiE;IACjE,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,6DAA6D;IAC7D,kBAAkB,CAAC,EAAE,cAAc,EAAE,CAAC;CACvC,CAAC;AAuJF;;;;;;;;;;GAUG;AACH,wBAAgB,oBAAoB,CAAC,EAAE,SAAS,EAAE,YAAoB,EAAE,gBAAwB,EAAE,kBAAuB,EAAE,EAAE,yBAAyB,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/layouts/user_management/index.tsx"],"names":[],"mappings":"AAyDA,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,iEAAiE;IACjE,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,6DAA6D;IAC7D,kBAAkB,CAAC,EAAE,cAAc,EAAE,CAAC;CACvC,CAAC;AAuJF;;;;;;;;;;GAUG;AACH,wBAAgB,oBAAoB,CAAC,EAAE,SAAS,EAAE,YAAoB,EAAE,gBAAwB,EAAE,kBAAuB,EAAE,EAAE,yBAAyB,2CA82CrJ"}
|
|
@@ -582,6 +582,11 @@ export function UserManagementLayout({ className, hrbacEnabled = false, userType
|
|
|
582
582
|
// Also update in the users list
|
|
583
583
|
setUsers((prevUsers) => prevUsers.map((u) => u.id === selectedUser.id
|
|
584
584
|
? Object.assign(Object.assign({}, u), { app_user_data: newData }) : u));
|
|
585
|
+
}, onClear: () => {
|
|
586
|
+
// Clear app_user_data in both selectedUser and users list
|
|
587
|
+
setSelectedUser((prev) => prev ? Object.assign(Object.assign({}, prev), { app_user_data: null }) : null);
|
|
588
|
+
setUsers((prevUsers) => prevUsers.map((u) => u.id === selectedUser.id
|
|
589
|
+
? Object.assign(Object.assign({}, u), { app_user_data: null }) : u));
|
|
585
590
|
} }) }) })] })) }), _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) => {
|
|
586
591
|
// Data is already saved by RolesMatrix component
|
|
587
592
|
console.log("User roles saved:", data);
|
package/package.json
CHANGED