@micha.bigler/ui-core-micha 2.2.7 → 2.2.8
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.
|
@@ -6,7 +6,7 @@ import DeleteIcon from '@mui/icons-material/Delete';
|
|
|
6
6
|
import { useTranslation } from 'react-i18next';
|
|
7
7
|
import { fetchUsersList, deleteUser, updateUserRole } from '../auth/authApi';
|
|
8
8
|
const DEFAULT_ROLES = ['none', 'student', 'teacher', 'admin'];
|
|
9
|
-
export function UserListComponent({ roles = DEFAULT_ROLES, currentUser, extraColumns = [], extraRowActions = [], extraContext = null, refreshTrigger = 0, canEditUser, }) {
|
|
9
|
+
export function UserListComponent({ roles = DEFAULT_ROLES, currentUser, extraColumns = [], extraRowActions = [], extraContext = null, refreshTrigger = 0, canEditUser, showRoleColumn = true, onChangeRole = null, showDeleteAction = true, canDeleteUser = null, onDeleteUser = null, }) {
|
|
10
10
|
const { t } = useTranslation();
|
|
11
11
|
const [users, setUsers] = useState([]);
|
|
12
12
|
const [loading, setLoading] = useState(true);
|
|
@@ -44,7 +44,12 @@ export function UserListComponent({ roles = DEFAULT_ROLES, currentUser, extraCol
|
|
|
44
44
|
if (!window.confirm(t('UserList.DELETE_CONFIRM', 'Are you sure you want to delete this user?')))
|
|
45
45
|
return;
|
|
46
46
|
try {
|
|
47
|
-
|
|
47
|
+
if (typeof onDeleteUser === 'function') {
|
|
48
|
+
await onDeleteUser({ userId, currentUser, extraContext, t, reloadUsers: loadUsers });
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
await deleteUser(userId);
|
|
52
|
+
}
|
|
48
53
|
setUsers(prev => prev.filter(u => u.id !== userId));
|
|
49
54
|
}
|
|
50
55
|
catch (err) {
|
|
@@ -53,7 +58,19 @@ export function UserListComponent({ roles = DEFAULT_ROLES, currentUser, extraCol
|
|
|
53
58
|
};
|
|
54
59
|
const handleChangeRole = async (userId, newRole) => {
|
|
55
60
|
try {
|
|
56
|
-
|
|
61
|
+
if (typeof onChangeRole === 'function') {
|
|
62
|
+
await onChangeRole({
|
|
63
|
+
userId,
|
|
64
|
+
newRole,
|
|
65
|
+
currentUser,
|
|
66
|
+
extraContext,
|
|
67
|
+
t,
|
|
68
|
+
reloadUsers: loadUsers,
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
await updateUserRole(userId, newRole);
|
|
73
|
+
}
|
|
57
74
|
await loadUsers();
|
|
58
75
|
}
|
|
59
76
|
catch (err) {
|
|
@@ -84,6 +101,12 @@ export function UserListComponent({ roles = DEFAULT_ROLES, currentUser, extraCol
|
|
|
84
101
|
}
|
|
85
102
|
return defaultCanEdit(targetUser);
|
|
86
103
|
};
|
|
104
|
+
const canDelete = (targetUser) => {
|
|
105
|
+
if (typeof canDeleteUser === 'function') {
|
|
106
|
+
return Boolean(canDeleteUser({ targetUser, currentUser, extraContext }));
|
|
107
|
+
}
|
|
108
|
+
return canEdit(targetUser);
|
|
109
|
+
};
|
|
87
110
|
const listContext = useMemo(() => ({
|
|
88
111
|
currentUser,
|
|
89
112
|
extraContext,
|
|
@@ -207,7 +230,9 @@ export function UserListComponent({ roles = DEFAULT_ROLES, currentUser, extraCol
|
|
|
207
230
|
: t('Common.NO', 'No') }));
|
|
208
231
|
},
|
|
209
232
|
},
|
|
210
|
-
|
|
233
|
+
];
|
|
234
|
+
if (showRoleColumn) {
|
|
235
|
+
baseColumns.push({
|
|
211
236
|
field: 'role',
|
|
212
237
|
headerName: t('UserList.ROLE', 'Role'),
|
|
213
238
|
minWidth: 180,
|
|
@@ -217,8 +242,8 @@ export function UserListComponent({ roles = DEFAULT_ROLES, currentUser, extraCol
|
|
|
217
242
|
const user = params.row;
|
|
218
243
|
return (_jsx(FormControl, { size: "small", fullWidth: true, sx: controlSx, disabled: !canEdit(user), children: _jsx(Select, { value: user.role || 'none', onChange: (event) => handleChangeRole(user.id, event.target.value), variant: "outlined", children: roles.map((role) => _jsx(MenuItem, { value: role, children: role }, role)) }) }));
|
|
219
244
|
},
|
|
220
|
-
}
|
|
221
|
-
|
|
245
|
+
});
|
|
246
|
+
}
|
|
222
247
|
const mappedExtraColumns = visibleExtraColumns.map((column) => ({
|
|
223
248
|
field: `extra:${column.key}`,
|
|
224
249
|
headerName: typeof column.label === 'function' ? column.label(listContext) : column.label,
|
|
@@ -237,10 +262,14 @@ export function UserListComponent({ roles = DEFAULT_ROLES, currentUser, extraCol
|
|
|
237
262
|
reloadUsers: loadUsers,
|
|
238
263
|
}),
|
|
239
264
|
}));
|
|
265
|
+
const hasActionColumn = showDeleteAction || visibleRowActions.length > 0;
|
|
266
|
+
if (!hasActionColumn) {
|
|
267
|
+
return [...baseColumns, ...mappedExtraColumns];
|
|
268
|
+
}
|
|
240
269
|
const actionColumn = {
|
|
241
270
|
field: 'actions',
|
|
242
271
|
headerName: t('Common.ACTIONS', 'Actions'),
|
|
243
|
-
minWidth: Math.max(220, 110 + visibleRowActions.length * 110),
|
|
272
|
+
minWidth: Math.max(220, 110 + (visibleRowActions.length + (showDeleteAction ? 1 : 0)) * 110),
|
|
244
273
|
flex: 1.4,
|
|
245
274
|
sortable: false,
|
|
246
275
|
filterable: false,
|
|
@@ -262,13 +291,15 @@ export function UserListComponent({ roles = DEFAULT_ROLES, currentUser, extraCol
|
|
|
262
291
|
return (_jsx(Button, { size: "small", variant: "outlined", onClick: () => runRowAction(action, user), disabled: isBusy || isDisabled, sx: actionButtonSx, children: typeof action.label === 'function'
|
|
263
292
|
? action.label({ user, t, currentUser, canEdit: canEdit(user) })
|
|
264
293
|
: action.label }, `${action.key}-${user.id}`));
|
|
265
|
-
}), _jsx(Tooltip, { title: t('Common.DELETE', 'Delete'), children: _jsx("span", { children: _jsx(Button, { size: "small", variant: "outlined", color: "error", startIcon: _jsx(DeleteIcon, {}), onClick: () => handleDelete(user.id), disabled: !
|
|
294
|
+
}), showDeleteAction && (_jsx(Tooltip, { title: t('Common.DELETE', 'Delete'), children: _jsx("span", { children: _jsx(Button, { size: "small", variant: "outlined", color: "error", startIcon: _jsx(DeleteIcon, {}), onClick: () => handleDelete(user.id), disabled: !canDelete(user), sx: actionButtonSx, children: t('Common.DELETE', 'Delete') }) }) }))] }));
|
|
266
295
|
},
|
|
267
296
|
};
|
|
268
297
|
return [...baseColumns, ...mappedExtraColumns, actionColumn];
|
|
269
298
|
}, [
|
|
270
299
|
t,
|
|
271
300
|
roles,
|
|
301
|
+
showRoleColumn,
|
|
302
|
+
showDeleteAction,
|
|
272
303
|
visibleExtraColumns,
|
|
273
304
|
visibleRowActions,
|
|
274
305
|
listContext,
|
|
@@ -277,6 +308,7 @@ export function UserListComponent({ roles = DEFAULT_ROLES, currentUser, extraCol
|
|
|
277
308
|
extraContext,
|
|
278
309
|
loadUsers,
|
|
279
310
|
canEdit,
|
|
311
|
+
canDelete,
|
|
280
312
|
]);
|
|
281
313
|
return (_jsxs(Box, { children: [_jsx(Typography, { variant: "h6", gutterBottom: true, children: t('UserList.TITLE', 'All Users') }), _jsx(Box, { sx: { mb: 2, maxWidth: 420 }, children: _jsx(TextField, { fullWidth: true, size: "small", label: t('Common.SEARCH', 'Search'), placeholder: t('UserList.SEARCH_PLACEHOLDER', 'Search users...'), value: searchQuery, onChange: (event) => setSearchQuery(event.target.value) }) }), error && _jsx(Alert, { severity: "error", sx: { mb: 2 }, children: t(error) }), loading && (_jsx(Box, { sx: { display: 'flex', justifyContent: 'center', p: 3 }, children: _jsx(CircularProgress, {}) })), !loading && (_jsx(Box, { sx: { width: '100%', minHeight: 520 }, children: _jsx(DataGrid, { rows: filteredUsers, columns: columns, disableRowSelectionOnClick: true, showToolbar: true, getRowHeight: () => 'auto', pageSizeOptions: [10, 25, 50, 100], initialState: {
|
|
282
314
|
sorting: { sortModel: [{ field: 'email', sort: 'asc' }] },
|
|
@@ -21,7 +21,7 @@ import { QrSignupValidityManager } from '../components/QrSignupValidityManager';
|
|
|
21
21
|
import { SupportRecoveryRequestsTab } from '../components/SupportRecoveryRequestsTab';
|
|
22
22
|
import { BulkInviteCsvTab } from '../components/BulkInviteCsvTab';
|
|
23
23
|
import { fetchAuthPolicy, updateUserProfile } from '../auth/authApi'; // Ggf. Pfad anpassen
|
|
24
|
-
export function AccountPage({ userListExtraColumns = [], userListExtraRowActions = [], userListExtraContext = null, userListRefreshTrigger = 0, userListCanEditUser = null, showBulkInviteCsvTab = false, bulkInviteCsvProps = {}, extraTabs = [], }) {
|
|
24
|
+
export function AccountPage({ userListExtraColumns = [], userListExtraRowActions = [], userListExtraContext = null, userListRefreshTrigger = 0, userListCanEditUser = null, userListShowRoleColumn = true, userListOnChangeRole = null, userListShowDeleteAction = true, userListCanDeleteUser = null, userListOnDeleteUser = null, showBulkInviteCsvTab = false, bulkInviteCsvProps = {}, extraTabs = [], }) {
|
|
25
25
|
var _a;
|
|
26
26
|
const { t } = useTranslation();
|
|
27
27
|
const { user, login, loading } = useContext(AuthContext);
|
|
@@ -129,5 +129,5 @@ export function AccountPage({ userListExtraColumns = [], userListExtraRowActions
|
|
|
129
129
|
const activeExtraTab = builtInTabValues.has(safeTab)
|
|
130
130
|
? null
|
|
131
131
|
: extraTabs.find((tab) => tab.value === safeTab);
|
|
132
|
-
return (_jsxs(WidePage, { title: t('Account.TITLE', 'Account & Administration'), children: [_jsx(Helmet, { children: _jsxs("title", { children: [t('Account.PAGE_TITLE', 'Account'), " \u2013 ", user.email] }) }), _jsx(Tabs, { value: safeTab, onChange: handleTabChange, variant: "scrollable", scrollButtons: "auto", sx: { mb: 3, borderBottom: 1, borderColor: 'divider' }, children: tabs.map((tab) => (_jsx(Tab, { label: tab.label, value: tab.value }, tab.value))) }), safeTab === 'profile' && (_jsx(Box, { sx: { mt: 2 }, children: _jsx(ProfileComponent, { onSubmit: handleProfileSubmit, showName: true, showPrivacy: true, showCookies: true }) })), safeTab === 'security' && (_jsx(Box, { sx: { mt: 2 }, children: _jsx(SecurityComponent, { fromRecovery: fromRecovery, fromWeakLogin: fromWeakLogin }) })), safeTab === 'users' && (_jsx(Box, { sx: { mt: 2 }, children: _jsx(UserListComponent, { roles: activeRoles, currentUser: user, extraColumns: userListExtraColumns, extraRowActions: userListExtraRowActions, extraContext: userListExtraContext, refreshTrigger: userListRefreshTrigger, canEditUser: userListCanEditUser }) })), safeTab === 'invite' && (_jsx(Box, { sx: { mt: 2 }, children: _jsxs(Stack, { spacing: 2.5, children: [canViewAuthPolicy && (_jsx(Paper, { variant: "outlined", sx: { p: 2.5, borderRadius: 2 }, children: _jsx(AuthFactorRequirementCard, { canEdit: canWriteAuthPolicy, policy: authPolicy }) })), canViewAuthPolicy && (_jsx(Paper, { variant: "outlined", sx: { p: 2.5, borderRadius: 2 }, children: _jsx(RegistrationMethodsManager, { policy: authPolicy, error: authPolicyError, onPolicyChange: setAuthPolicy, canEdit: canWriteAuthPolicy }) })), canSendInvites && Boolean(authPolicy === null || authPolicy === void 0 ? void 0 : authPolicy.allow_admin_invite) && (_jsx(Paper, { variant: "outlined", sx: { p: 2.5, borderRadius: 2 }, children: _jsx(UserInviteComponent, {}) })), canManageAccessCodes && Boolean(authPolicy === null || authPolicy === void 0 ? void 0 : authPolicy.allow_self_signup_access_code) && (_jsxs(Paper, { variant: "outlined", sx: { p: 2.5, borderRadius: 2 }, children: [_jsx(Typography, { variant: "h6", gutterBottom: true, children: t('Auth.ACCESS_CODE_MANAGER_TITLE', 'Access Codes') }), _jsx(Typography, { variant: "body2", sx: { mb: 2, color: 'text.secondary' }, children: t('Account.ACCESS_CODES_HINT', 'Manage access codes for self-registration.') }), _jsx(AccessCodeManager, {})] })), canSendInvites && showBulkInviteCsvTab && Boolean(authPolicy === null || authPolicy === void 0 ? void 0 : authPolicy.allow_admin_invite) && (_jsx(Paper, { variant: "outlined", sx: { p: 2.5, borderRadius: 2 }, children: _jsx(BulkInviteCsvTab, Object.assign({}, bulkInviteCsvProps)) })), canViewAuthPolicy && Boolean(authPolicy === null || authPolicy === void 0 ? void 0 : authPolicy.allow_self_signup_email_domain) && (_jsx(Paper, { variant: "outlined", sx: { p: 2.5, borderRadius: 2 }, children: _jsx(AllowedEmailDomainsManager, { enabled: Boolean(authPolicy === null || authPolicy === void 0 ? void 0 : authPolicy.allow_self_signup_email_domain), domains: (authPolicy === null || authPolicy === void 0 ? void 0 : authPolicy.allowed_email_domains) || [], onPolicyChange: setAuthPolicy, canEdit: canWriteAuthPolicy }) })), canViewAuthPolicy && Boolean(authPolicy === null || authPolicy === void 0 ? void 0 : authPolicy.allow_self_signup_qr) && (_jsx(Paper, { variant: "outlined", sx: { p: 2.5, borderRadius: 2 }, children: _jsx(QrSignupValidityManager, { enabled: Boolean(authPolicy === null || authPolicy === void 0 ? void 0 : authPolicy.allow_self_signup_qr), expiryDays: authPolicy === null || authPolicy === void 0 ? void 0 : authPolicy.signup_qr_expiry_days, onPolicyChange: setAuthPolicy, canEdit: canWriteAuthPolicy }) })), canManageSignupQr && Boolean(authPolicy === null || authPolicy === void 0 ? void 0 : authPolicy.allow_self_signup_qr) && (_jsx(Paper, { variant: "outlined", sx: { p: 2.5, borderRadius: 2 }, children: _jsx(QrSignupManager, { enabled: Boolean(authPolicy === null || authPolicy === void 0 ? void 0 : authPolicy.allow_self_signup_qr), expiryDays: authPolicy === null || authPolicy === void 0 ? void 0 : authPolicy.signup_qr_expiry_days }) }))] }) })), safeTab === 'support' && (_jsx(Box, { sx: { mt: 2 }, children: _jsx(SupportRecoveryRequestsTab, {}) })), activeExtraTab && (_jsx(Box, { sx: { mt: 2 }, children: (_a = activeExtraTab.render) === null || _a === void 0 ? void 0 : _a.call(activeExtraTab, { user, perms, isSuperUser, t }) }))] }));
|
|
132
|
+
return (_jsxs(WidePage, { title: t('Account.TITLE', 'Account & Administration'), children: [_jsx(Helmet, { children: _jsxs("title", { children: [t('Account.PAGE_TITLE', 'Account'), " \u2013 ", user.email] }) }), _jsx(Tabs, { value: safeTab, onChange: handleTabChange, variant: "scrollable", scrollButtons: "auto", sx: { mb: 3, borderBottom: 1, borderColor: 'divider' }, children: tabs.map((tab) => (_jsx(Tab, { label: tab.label, value: tab.value }, tab.value))) }), safeTab === 'profile' && (_jsx(Box, { sx: { mt: 2 }, children: _jsx(ProfileComponent, { onSubmit: handleProfileSubmit, showName: true, showPrivacy: true, showCookies: true }) })), safeTab === 'security' && (_jsx(Box, { sx: { mt: 2 }, children: _jsx(SecurityComponent, { fromRecovery: fromRecovery, fromWeakLogin: fromWeakLogin }) })), safeTab === 'users' && (_jsx(Box, { sx: { mt: 2 }, children: _jsx(UserListComponent, { roles: activeRoles, currentUser: user, extraColumns: userListExtraColumns, extraRowActions: userListExtraRowActions, extraContext: userListExtraContext, refreshTrigger: userListRefreshTrigger, canEditUser: userListCanEditUser, showRoleColumn: userListShowRoleColumn, onChangeRole: userListOnChangeRole, showDeleteAction: userListShowDeleteAction, canDeleteUser: userListCanDeleteUser, onDeleteUser: userListOnDeleteUser }) })), safeTab === 'invite' && (_jsx(Box, { sx: { mt: 2 }, children: _jsxs(Stack, { spacing: 2.5, children: [canViewAuthPolicy && (_jsx(Paper, { variant: "outlined", sx: { p: 2.5, borderRadius: 2 }, children: _jsx(AuthFactorRequirementCard, { canEdit: canWriteAuthPolicy, policy: authPolicy }) })), canViewAuthPolicy && (_jsx(Paper, { variant: "outlined", sx: { p: 2.5, borderRadius: 2 }, children: _jsx(RegistrationMethodsManager, { policy: authPolicy, error: authPolicyError, onPolicyChange: setAuthPolicy, canEdit: canWriteAuthPolicy }) })), canSendInvites && Boolean(authPolicy === null || authPolicy === void 0 ? void 0 : authPolicy.allow_admin_invite) && (_jsx(Paper, { variant: "outlined", sx: { p: 2.5, borderRadius: 2 }, children: _jsx(UserInviteComponent, {}) })), canManageAccessCodes && Boolean(authPolicy === null || authPolicy === void 0 ? void 0 : authPolicy.allow_self_signup_access_code) && (_jsxs(Paper, { variant: "outlined", sx: { p: 2.5, borderRadius: 2 }, children: [_jsx(Typography, { variant: "h6", gutterBottom: true, children: t('Auth.ACCESS_CODE_MANAGER_TITLE', 'Access Codes') }), _jsx(Typography, { variant: "body2", sx: { mb: 2, color: 'text.secondary' }, children: t('Account.ACCESS_CODES_HINT', 'Manage access codes for self-registration.') }), _jsx(AccessCodeManager, {})] })), canSendInvites && showBulkInviteCsvTab && Boolean(authPolicy === null || authPolicy === void 0 ? void 0 : authPolicy.allow_admin_invite) && (_jsx(Paper, { variant: "outlined", sx: { p: 2.5, borderRadius: 2 }, children: _jsx(BulkInviteCsvTab, Object.assign({}, bulkInviteCsvProps)) })), canViewAuthPolicy && Boolean(authPolicy === null || authPolicy === void 0 ? void 0 : authPolicy.allow_self_signup_email_domain) && (_jsx(Paper, { variant: "outlined", sx: { p: 2.5, borderRadius: 2 }, children: _jsx(AllowedEmailDomainsManager, { enabled: Boolean(authPolicy === null || authPolicy === void 0 ? void 0 : authPolicy.allow_self_signup_email_domain), domains: (authPolicy === null || authPolicy === void 0 ? void 0 : authPolicy.allowed_email_domains) || [], onPolicyChange: setAuthPolicy, canEdit: canWriteAuthPolicy }) })), canViewAuthPolicy && Boolean(authPolicy === null || authPolicy === void 0 ? void 0 : authPolicy.allow_self_signup_qr) && (_jsx(Paper, { variant: "outlined", sx: { p: 2.5, borderRadius: 2 }, children: _jsx(QrSignupValidityManager, { enabled: Boolean(authPolicy === null || authPolicy === void 0 ? void 0 : authPolicy.allow_self_signup_qr), expiryDays: authPolicy === null || authPolicy === void 0 ? void 0 : authPolicy.signup_qr_expiry_days, onPolicyChange: setAuthPolicy, canEdit: canWriteAuthPolicy }) })), canManageSignupQr && Boolean(authPolicy === null || authPolicy === void 0 ? void 0 : authPolicy.allow_self_signup_qr) && (_jsx(Paper, { variant: "outlined", sx: { p: 2.5, borderRadius: 2 }, children: _jsx(QrSignupManager, { enabled: Boolean(authPolicy === null || authPolicy === void 0 ? void 0 : authPolicy.allow_self_signup_qr), expiryDays: authPolicy === null || authPolicy === void 0 ? void 0 : authPolicy.signup_qr_expiry_days }) }))] }) })), safeTab === 'support' && (_jsx(Box, { sx: { mt: 2 }, children: _jsx(SupportRecoveryRequestsTab, {}) })), activeExtraTab && (_jsx(Box, { sx: { mt: 2 }, children: (_a = activeExtraTab.render) === null || _a === void 0 ? void 0 : _a.call(activeExtraTab, { user, perms, isSuperUser, t }) }))] }));
|
|
133
133
|
}
|
package/package.json
CHANGED
|
@@ -26,6 +26,11 @@ export function UserListComponent({
|
|
|
26
26
|
extraContext = null,
|
|
27
27
|
refreshTrigger = 0,
|
|
28
28
|
canEditUser,
|
|
29
|
+
showRoleColumn = true,
|
|
30
|
+
onChangeRole = null,
|
|
31
|
+
showDeleteAction = true,
|
|
32
|
+
canDeleteUser = null,
|
|
33
|
+
onDeleteUser = null,
|
|
29
34
|
}) {
|
|
30
35
|
const { t } = useTranslation();
|
|
31
36
|
const [users, setUsers] = useState([]);
|
|
@@ -63,7 +68,11 @@ export function UserListComponent({
|
|
|
63
68
|
const handleDelete = async (userId) => {
|
|
64
69
|
if (!window.confirm(t('UserList.DELETE_CONFIRM', 'Are you sure you want to delete this user?'))) return;
|
|
65
70
|
try {
|
|
66
|
-
|
|
71
|
+
if (typeof onDeleteUser === 'function') {
|
|
72
|
+
await onDeleteUser({ userId, currentUser, extraContext, t, reloadUsers: loadUsers });
|
|
73
|
+
} else {
|
|
74
|
+
await deleteUser(userId);
|
|
75
|
+
}
|
|
67
76
|
setUsers(prev => prev.filter(u => u.id !== userId));
|
|
68
77
|
} catch (err) {
|
|
69
78
|
alert(t(err.code || 'Auth.USER_DELETE_FAILED'));
|
|
@@ -72,7 +81,18 @@ export function UserListComponent({
|
|
|
72
81
|
|
|
73
82
|
const handleChangeRole = async (userId, newRole) => {
|
|
74
83
|
try {
|
|
75
|
-
|
|
84
|
+
if (typeof onChangeRole === 'function') {
|
|
85
|
+
await onChangeRole({
|
|
86
|
+
userId,
|
|
87
|
+
newRole,
|
|
88
|
+
currentUser,
|
|
89
|
+
extraContext,
|
|
90
|
+
t,
|
|
91
|
+
reloadUsers: loadUsers,
|
|
92
|
+
});
|
|
93
|
+
} else {
|
|
94
|
+
await updateUserRole(userId, newRole);
|
|
95
|
+
}
|
|
76
96
|
await loadUsers();
|
|
77
97
|
} catch (err) {
|
|
78
98
|
alert(t(err.code || 'Auth.USER_ROLE_UPDATE_FAILED'));
|
|
@@ -102,6 +122,13 @@ export function UserListComponent({
|
|
|
102
122
|
return defaultCanEdit(targetUser);
|
|
103
123
|
};
|
|
104
124
|
|
|
125
|
+
const canDelete = (targetUser) => {
|
|
126
|
+
if (typeof canDeleteUser === 'function') {
|
|
127
|
+
return Boolean(canDeleteUser({ targetUser, currentUser, extraContext }));
|
|
128
|
+
}
|
|
129
|
+
return canEdit(targetUser);
|
|
130
|
+
};
|
|
131
|
+
|
|
105
132
|
const listContext = useMemo(() => ({
|
|
106
133
|
currentUser,
|
|
107
134
|
extraContext,
|
|
@@ -247,7 +274,10 @@ export function UserListComponent({
|
|
|
247
274
|
</Typography>
|
|
248
275
|
),
|
|
249
276
|
},
|
|
250
|
-
|
|
277
|
+
];
|
|
278
|
+
|
|
279
|
+
if (showRoleColumn) {
|
|
280
|
+
baseColumns.push({
|
|
251
281
|
field: 'role',
|
|
252
282
|
headerName: t('UserList.ROLE', 'Role'),
|
|
253
283
|
minWidth: 180,
|
|
@@ -267,8 +297,8 @@ export function UserListComponent({
|
|
|
267
297
|
</FormControl>
|
|
268
298
|
);
|
|
269
299
|
},
|
|
270
|
-
}
|
|
271
|
-
|
|
300
|
+
});
|
|
301
|
+
}
|
|
272
302
|
|
|
273
303
|
const mappedExtraColumns = visibleExtraColumns.map((column) => ({
|
|
274
304
|
field: `extra:${column.key}`,
|
|
@@ -290,10 +320,16 @@ export function UserListComponent({
|
|
|
290
320
|
}),
|
|
291
321
|
}));
|
|
292
322
|
|
|
323
|
+
const hasActionColumn = showDeleteAction || visibleRowActions.length > 0;
|
|
324
|
+
|
|
325
|
+
if (!hasActionColumn) {
|
|
326
|
+
return [...baseColumns, ...mappedExtraColumns];
|
|
327
|
+
}
|
|
328
|
+
|
|
293
329
|
const actionColumn = {
|
|
294
330
|
field: 'actions',
|
|
295
331
|
headerName: t('Common.ACTIONS', 'Actions'),
|
|
296
|
-
minWidth: Math.max(220, 110 + visibleRowActions.length * 110),
|
|
332
|
+
minWidth: Math.max(220, 110 + (visibleRowActions.length + (showDeleteAction ? 1 : 0)) * 110),
|
|
297
333
|
flex: 1.4,
|
|
298
334
|
sortable: false,
|
|
299
335
|
filterable: false,
|
|
@@ -332,21 +368,23 @@ export function UserListComponent({
|
|
|
332
368
|
);
|
|
333
369
|
})}
|
|
334
370
|
|
|
335
|
-
|
|
336
|
-
<
|
|
337
|
-
<
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
371
|
+
{showDeleteAction && (
|
|
372
|
+
<Tooltip title={t('Common.DELETE', 'Delete')}>
|
|
373
|
+
<span>
|
|
374
|
+
<Button
|
|
375
|
+
size="small"
|
|
376
|
+
variant="outlined"
|
|
377
|
+
color="error"
|
|
378
|
+
startIcon={<DeleteIcon />}
|
|
379
|
+
onClick={() => handleDelete(user.id)}
|
|
380
|
+
disabled={!canDelete(user)}
|
|
381
|
+
sx={actionButtonSx}
|
|
382
|
+
>
|
|
383
|
+
{t('Common.DELETE', 'Delete')}
|
|
384
|
+
</Button>
|
|
385
|
+
</span>
|
|
386
|
+
</Tooltip>
|
|
387
|
+
)}
|
|
350
388
|
</Box>
|
|
351
389
|
);
|
|
352
390
|
},
|
|
@@ -356,6 +394,8 @@ export function UserListComponent({
|
|
|
356
394
|
}, [
|
|
357
395
|
t,
|
|
358
396
|
roles,
|
|
397
|
+
showRoleColumn,
|
|
398
|
+
showDeleteAction,
|
|
359
399
|
visibleExtraColumns,
|
|
360
400
|
visibleRowActions,
|
|
361
401
|
listContext,
|
|
@@ -364,6 +404,7 @@ export function UserListComponent({
|
|
|
364
404
|
extraContext,
|
|
365
405
|
loadUsers,
|
|
366
406
|
canEdit,
|
|
407
|
+
canDelete,
|
|
367
408
|
]);
|
|
368
409
|
|
|
369
410
|
return (
|
|
@@ -38,6 +38,11 @@ export function AccountPage({
|
|
|
38
38
|
userListExtraContext = null,
|
|
39
39
|
userListRefreshTrigger = 0,
|
|
40
40
|
userListCanEditUser = null,
|
|
41
|
+
userListShowRoleColumn = true,
|
|
42
|
+
userListOnChangeRole = null,
|
|
43
|
+
userListShowDeleteAction = true,
|
|
44
|
+
userListCanDeleteUser = null,
|
|
45
|
+
userListOnDeleteUser = null,
|
|
41
46
|
showBulkInviteCsvTab = false,
|
|
42
47
|
bulkInviteCsvProps = {},
|
|
43
48
|
extraTabs = [],
|
|
@@ -227,6 +232,11 @@ export function AccountPage({
|
|
|
227
232
|
extraContext={userListExtraContext}
|
|
228
233
|
refreshTrigger={userListRefreshTrigger}
|
|
229
234
|
canEditUser={userListCanEditUser}
|
|
235
|
+
showRoleColumn={userListShowRoleColumn}
|
|
236
|
+
onChangeRole={userListOnChangeRole}
|
|
237
|
+
showDeleteAction={userListShowDeleteAction}
|
|
238
|
+
canDeleteUser={userListCanDeleteUser}
|
|
239
|
+
onDeleteUser={userListOnDeleteUser}
|
|
230
240
|
/>
|
|
231
241
|
</Box>
|
|
232
242
|
)}
|