@micha.bigler/ui-core-micha 2.2.8 → 2.2.10
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/auth/apiClient.js
CHANGED
|
@@ -19,12 +19,15 @@ const PUBLIC_PATHS = [
|
|
|
19
19
|
"/reset", // Reset Link (/reset/:uid/:token)
|
|
20
20
|
"/welcome" // Optional: Falls Welcome auch öffentlich ist
|
|
21
21
|
];
|
|
22
|
+
function isPublicSitePath(pathname) {
|
|
23
|
+
return pathname === "/sites" || pathname.startsWith("/sites/");
|
|
24
|
+
}
|
|
22
25
|
function redirectToLoginOnce() {
|
|
23
26
|
if (!isBrowser())
|
|
24
27
|
return;
|
|
25
28
|
const currentPath = window.location.pathname;
|
|
26
29
|
// 1. Check: Sind wir auf einer öffentlichen Seite?
|
|
27
|
-
const isPublicPage = PUBLIC_PATHS.some(path => currentPath.startsWith(path));
|
|
30
|
+
const isPublicPage = isPublicSitePath(currentPath) || PUBLIC_PATHS.some(path => currentPath.startsWith(path));
|
|
28
31
|
// Wenn ja: NICHT weiterleiten. Der 401 Fehler wird an die Komponente durchgereicht.
|
|
29
32
|
if (isPublicPage)
|
|
30
33
|
return;
|
|
@@ -2,11 +2,13 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
import React, { useMemo, useState, useEffect, useCallback } from 'react';
|
|
3
3
|
import { Box, Typography, FormControl, Select, MenuItem, Button, Tooltip, CircularProgress, Alert, TextField, } from '@mui/material';
|
|
4
4
|
import { DataGrid } from '@mui/x-data-grid';
|
|
5
|
+
import CheckCircleIcon from '@mui/icons-material/CheckCircle';
|
|
6
|
+
import CancelIcon from '@mui/icons-material/Cancel';
|
|
5
7
|
import DeleteIcon from '@mui/icons-material/Delete';
|
|
6
8
|
import { useTranslation } from 'react-i18next';
|
|
7
9
|
import { fetchUsersList, deleteUser, updateUserRole } from '../auth/authApi';
|
|
8
10
|
const DEFAULT_ROLES = ['none', 'student', 'teacher', 'admin'];
|
|
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, }) {
|
|
11
|
+
export function UserListComponent({ roles = DEFAULT_ROLES, currentUser, extraColumns = [], extraRowActions = [], extraContext = null, refreshTrigger = 0, canEditUser, showNewColumn = true, showSuccessfulLoginColumn = true, showRoleColumn = true, onChangeRole = null, showDeleteAction = true, canDeleteUser = null, onDeleteUser = null, }) {
|
|
10
12
|
const { t } = useTranslation();
|
|
11
13
|
const [users, setUsers] = useState([]);
|
|
12
14
|
const [loading, setLoading] = useState(true);
|
|
@@ -121,6 +123,7 @@ export function UserListComponent({ roles = DEFAULT_ROLES, currentUser, extraCol
|
|
|
121
123
|
}
|
|
122
124
|
return (user === null || user === void 0 ? void 0 : user.username) || '';
|
|
123
125
|
};
|
|
126
|
+
const renderBooleanStatusIcon = (value, positiveLabel, negativeLabel) => (_jsx(Tooltip, { title: value ? positiveLabel : negativeLabel, children: _jsx("span", { children: value ? (_jsx(CheckCircleIcon, { color: "success", fontSize: "small" })) : (_jsx(CancelIcon, { color: "error", fontSize: "small" })) }) }));
|
|
124
127
|
const getExtraColumnSortValue = (column, user) => {
|
|
125
128
|
var _a;
|
|
126
129
|
const valueContext = { user, currentUser, extraContext, t };
|
|
@@ -207,36 +210,58 @@ export function UserListComponent({ roles = DEFAULT_ROLES, currentUser, extraCol
|
|
|
207
210
|
{
|
|
208
211
|
field: 'email',
|
|
209
212
|
headerName: t('Auth.EMAIL_LABEL', 'Email'),
|
|
210
|
-
minWidth:
|
|
211
|
-
|
|
213
|
+
minWidth: 220,
|
|
214
|
+
maxWidth: 340,
|
|
215
|
+
flex: 1,
|
|
212
216
|
},
|
|
213
217
|
{
|
|
214
218
|
field: 'name',
|
|
215
219
|
headerName: t('Profile.NAME_LABEL', 'Name'),
|
|
216
|
-
minWidth:
|
|
217
|
-
|
|
220
|
+
minWidth: 180,
|
|
221
|
+
maxWidth: 260,
|
|
222
|
+
flex: 0.9,
|
|
218
223
|
valueGetter: (_value, row) => getUserDisplayName(row),
|
|
219
224
|
},
|
|
220
|
-
|
|
225
|
+
];
|
|
226
|
+
if (showNewColumn) {
|
|
227
|
+
baseColumns.push({
|
|
228
|
+
field: 'is_new',
|
|
229
|
+
headerName: t('UserList.NEW', 'New'),
|
|
230
|
+
minWidth: 90,
|
|
231
|
+
maxWidth: 110,
|
|
232
|
+
flex: 0.35,
|
|
233
|
+
align: 'center',
|
|
234
|
+
headerAlign: 'center',
|
|
235
|
+
valueGetter: (_value, row) => { var _a; return Boolean(((_a = row === null || row === void 0 ? void 0 : row.profile) === null || _a === void 0 ? void 0 : _a.is_new) || (row === null || row === void 0 ? void 0 : row.is_new)); },
|
|
236
|
+
renderCell: (params) => {
|
|
237
|
+
var _a, _b, _c;
|
|
238
|
+
return renderBooleanStatusIcon(Boolean(((_b = (_a = params.row) === null || _a === void 0 ? void 0 : _a.profile) === null || _b === void 0 ? void 0 : _b.is_new) || ((_c = params.row) === null || _c === void 0 ? void 0 : _c.is_new)), t('Common.YES', 'Yes'), t('Common.NO', 'No'));
|
|
239
|
+
},
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
if (showSuccessfulLoginColumn) {
|
|
243
|
+
baseColumns.push({
|
|
221
244
|
field: 'successful_login',
|
|
222
245
|
headerName: t('UserList.SUCCESSFUL_LOGIN', 'Successful Login'),
|
|
223
|
-
minWidth:
|
|
224
|
-
|
|
246
|
+
minWidth: 120,
|
|
247
|
+
maxWidth: 150,
|
|
248
|
+
flex: 0.4,
|
|
249
|
+
align: 'center',
|
|
250
|
+
headerAlign: 'center',
|
|
225
251
|
valueGetter: (_value, row) => { var _a; return Boolean((_a = row === null || row === void 0 ? void 0 : row.successful_login) !== null && _a !== void 0 ? _a : row === null || row === void 0 ? void 0 : row.last_login); },
|
|
226
252
|
renderCell: (params) => {
|
|
227
253
|
var _a, _b, _c;
|
|
228
|
-
return (
|
|
229
|
-
? t('Common.YES', 'Yes')
|
|
230
|
-
: t('Common.NO', 'No') }));
|
|
254
|
+
return renderBooleanStatusIcon(Boolean((_b = (_a = params.row) === null || _a === void 0 ? void 0 : _a.successful_login) !== null && _b !== void 0 ? _b : (_c = params.row) === null || _c === void 0 ? void 0 : _c.last_login), t('Common.YES', 'Yes'), t('Common.NO', 'No'));
|
|
231
255
|
},
|
|
232
|
-
}
|
|
233
|
-
|
|
256
|
+
});
|
|
257
|
+
}
|
|
234
258
|
if (showRoleColumn) {
|
|
235
259
|
baseColumns.push({
|
|
236
260
|
field: 'role',
|
|
237
261
|
headerName: t('UserList.ROLE', 'Role'),
|
|
238
262
|
minWidth: 180,
|
|
239
|
-
|
|
263
|
+
maxWidth: 240,
|
|
264
|
+
flex: 0.7,
|
|
240
265
|
valueGetter: (_value, row) => (row === null || row === void 0 ? void 0 : row.role) || 'none',
|
|
241
266
|
renderCell: (params) => {
|
|
242
267
|
const user = params.row;
|
|
@@ -248,6 +273,7 @@ export function UserListComponent({ roles = DEFAULT_ROLES, currentUser, extraCol
|
|
|
248
273
|
field: `extra:${column.key}`,
|
|
249
274
|
headerName: typeof column.label === 'function' ? column.label(listContext) : column.label,
|
|
250
275
|
minWidth: Number(column.minWidth) || 180,
|
|
276
|
+
maxWidth: Number(column.maxWidth) || 320,
|
|
251
277
|
flex: Number(column.flex) || 0.9,
|
|
252
278
|
sortable: column.sortable !== false,
|
|
253
279
|
align: column.align || 'left',
|
|
@@ -270,7 +296,8 @@ export function UserListComponent({ roles = DEFAULT_ROLES, currentUser, extraCol
|
|
|
270
296
|
field: 'actions',
|
|
271
297
|
headerName: t('Common.ACTIONS', 'Actions'),
|
|
272
298
|
minWidth: Math.max(220, 110 + (visibleRowActions.length + (showDeleteAction ? 1 : 0)) * 110),
|
|
273
|
-
|
|
299
|
+
maxWidth: Math.max(360, 120 + (visibleRowActions.length + (showDeleteAction ? 1 : 0)) * 120),
|
|
300
|
+
flex: 1.1,
|
|
274
301
|
sortable: false,
|
|
275
302
|
filterable: false,
|
|
276
303
|
disableColumnMenu: true,
|
|
@@ -298,6 +325,8 @@ export function UserListComponent({ roles = DEFAULT_ROLES, currentUser, extraCol
|
|
|
298
325
|
}, [
|
|
299
326
|
t,
|
|
300
327
|
roles,
|
|
328
|
+
showNewColumn,
|
|
329
|
+
showSuccessfulLoginColumn,
|
|
301
330
|
showRoleColumn,
|
|
302
331
|
showDeleteAction,
|
|
303
332
|
visibleExtraColumns,
|
|
@@ -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, userListShowRoleColumn = true, userListOnChangeRole = null, userListShowDeleteAction = true, userListCanDeleteUser = null, userListOnDeleteUser = null, showBulkInviteCsvTab = false, bulkInviteCsvProps = {}, extraTabs = [], }) {
|
|
24
|
+
export function AccountPage({ userListExtraColumns = [], userListExtraRowActions = [], userListExtraContext = null, userListRefreshTrigger = 0, userListCanEditUser = null, userListShowNewColumn = true, userListShowSuccessfulLoginColumn = true, 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, 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 }) }))] }));
|
|
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, showNewColumn: userListShowNewColumn, showSuccessfulLoginColumn: userListShowSuccessfulLoginColumn, 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
package/src/auth/apiClient.jsx
CHANGED
|
@@ -24,13 +24,17 @@ const PUBLIC_PATHS = [
|
|
|
24
24
|
"/welcome" // Optional: Falls Welcome auch öffentlich ist
|
|
25
25
|
];
|
|
26
26
|
|
|
27
|
+
function isPublicSitePath(pathname) {
|
|
28
|
+
return pathname === "/sites" || pathname.startsWith("/sites/");
|
|
29
|
+
}
|
|
30
|
+
|
|
27
31
|
function redirectToLoginOnce() {
|
|
28
32
|
if (!isBrowser()) return;
|
|
29
33
|
|
|
30
34
|
const currentPath = window.location.pathname;
|
|
31
35
|
|
|
32
36
|
// 1. Check: Sind wir auf einer öffentlichen Seite?
|
|
33
|
-
const isPublicPage = PUBLIC_PATHS.some(path => currentPath.startsWith(path));
|
|
37
|
+
const isPublicPage = isPublicSitePath(currentPath) || PUBLIC_PATHS.some(path => currentPath.startsWith(path));
|
|
34
38
|
|
|
35
39
|
// Wenn ja: NICHT weiterleiten. Der 401 Fehler wird an die Komponente durchgereicht.
|
|
36
40
|
if (isPublicPage) return;
|
|
@@ -12,6 +12,8 @@ import {
|
|
|
12
12
|
TextField,
|
|
13
13
|
} from '@mui/material';
|
|
14
14
|
import { DataGrid } from '@mui/x-data-grid';
|
|
15
|
+
import CheckCircleIcon from '@mui/icons-material/CheckCircle';
|
|
16
|
+
import CancelIcon from '@mui/icons-material/Cancel';
|
|
15
17
|
import DeleteIcon from '@mui/icons-material/Delete';
|
|
16
18
|
import { useTranslation } from 'react-i18next';
|
|
17
19
|
import { fetchUsersList, deleteUser, updateUserRole } from '../auth/authApi';
|
|
@@ -26,6 +28,8 @@ export function UserListComponent({
|
|
|
26
28
|
extraContext = null,
|
|
27
29
|
refreshTrigger = 0,
|
|
28
30
|
canEditUser,
|
|
31
|
+
showNewColumn = true,
|
|
32
|
+
showSuccessfulLoginColumn = true,
|
|
29
33
|
showRoleColumn = true,
|
|
30
34
|
onChangeRole = null,
|
|
31
35
|
showDeleteAction = true,
|
|
@@ -159,6 +163,18 @@ export function UserListComponent({
|
|
|
159
163
|
return user?.username || '';
|
|
160
164
|
};
|
|
161
165
|
|
|
166
|
+
const renderBooleanStatusIcon = (value, positiveLabel, negativeLabel) => (
|
|
167
|
+
<Tooltip title={value ? positiveLabel : negativeLabel}>
|
|
168
|
+
<span>
|
|
169
|
+
{value ? (
|
|
170
|
+
<CheckCircleIcon color="success" fontSize="small" />
|
|
171
|
+
) : (
|
|
172
|
+
<CancelIcon color="error" fontSize="small" />
|
|
173
|
+
)}
|
|
174
|
+
</span>
|
|
175
|
+
</Tooltip>
|
|
176
|
+
);
|
|
177
|
+
|
|
162
178
|
const getExtraColumnSortValue = (column, user) => {
|
|
163
179
|
const valueContext = { user, currentUser, extraContext, t };
|
|
164
180
|
|
|
@@ -250,38 +266,63 @@ export function UserListComponent({
|
|
|
250
266
|
{
|
|
251
267
|
field: 'email',
|
|
252
268
|
headerName: t('Auth.EMAIL_LABEL', 'Email'),
|
|
253
|
-
minWidth:
|
|
254
|
-
|
|
269
|
+
minWidth: 220,
|
|
270
|
+
maxWidth: 340,
|
|
271
|
+
flex: 1,
|
|
255
272
|
},
|
|
256
273
|
{
|
|
257
274
|
field: 'name',
|
|
258
275
|
headerName: t('Profile.NAME_LABEL', 'Name'),
|
|
259
|
-
minWidth:
|
|
260
|
-
|
|
276
|
+
minWidth: 180,
|
|
277
|
+
maxWidth: 260,
|
|
278
|
+
flex: 0.9,
|
|
261
279
|
valueGetter: (_value, row) => getUserDisplayName(row),
|
|
262
280
|
},
|
|
263
|
-
|
|
281
|
+
];
|
|
282
|
+
|
|
283
|
+
if (showNewColumn) {
|
|
284
|
+
baseColumns.push({
|
|
285
|
+
field: 'is_new',
|
|
286
|
+
headerName: t('UserList.NEW', 'New'),
|
|
287
|
+
minWidth: 90,
|
|
288
|
+
maxWidth: 110,
|
|
289
|
+
flex: 0.35,
|
|
290
|
+
align: 'center',
|
|
291
|
+
headerAlign: 'center',
|
|
292
|
+
valueGetter: (_value, row) => Boolean(row?.profile?.is_new || row?.is_new),
|
|
293
|
+
renderCell: (params) => renderBooleanStatusIcon(
|
|
294
|
+
Boolean(params.row?.profile?.is_new || params.row?.is_new),
|
|
295
|
+
t('Common.YES', 'Yes'),
|
|
296
|
+
t('Common.NO', 'No'),
|
|
297
|
+
),
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
if (showSuccessfulLoginColumn) {
|
|
302
|
+
baseColumns.push({
|
|
264
303
|
field: 'successful_login',
|
|
265
304
|
headerName: t('UserList.SUCCESSFUL_LOGIN', 'Successful Login'),
|
|
266
|
-
minWidth:
|
|
267
|
-
|
|
305
|
+
minWidth: 120,
|
|
306
|
+
maxWidth: 150,
|
|
307
|
+
flex: 0.4,
|
|
308
|
+
align: 'center',
|
|
309
|
+
headerAlign: 'center',
|
|
268
310
|
valueGetter: (_value, row) => Boolean(row?.successful_login ?? row?.last_login),
|
|
269
|
-
renderCell: (params) => (
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
: t('Common.NO', 'No')}
|
|
274
|
-
</Typography>
|
|
311
|
+
renderCell: (params) => renderBooleanStatusIcon(
|
|
312
|
+
Boolean(params.row?.successful_login ?? params.row?.last_login),
|
|
313
|
+
t('Common.YES', 'Yes'),
|
|
314
|
+
t('Common.NO', 'No'),
|
|
275
315
|
),
|
|
276
|
-
}
|
|
277
|
-
|
|
316
|
+
});
|
|
317
|
+
}
|
|
278
318
|
|
|
279
319
|
if (showRoleColumn) {
|
|
280
320
|
baseColumns.push({
|
|
281
321
|
field: 'role',
|
|
282
322
|
headerName: t('UserList.ROLE', 'Role'),
|
|
283
323
|
minWidth: 180,
|
|
284
|
-
|
|
324
|
+
maxWidth: 240,
|
|
325
|
+
flex: 0.7,
|
|
285
326
|
valueGetter: (_value, row) => row?.role || 'none',
|
|
286
327
|
renderCell: (params) => {
|
|
287
328
|
const user = params.row;
|
|
@@ -304,6 +345,7 @@ export function UserListComponent({
|
|
|
304
345
|
field: `extra:${column.key}`,
|
|
305
346
|
headerName: typeof column.label === 'function' ? column.label(listContext) : column.label,
|
|
306
347
|
minWidth: Number(column.minWidth) || 180,
|
|
348
|
+
maxWidth: Number(column.maxWidth) || 320,
|
|
307
349
|
flex: Number(column.flex) || 0.9,
|
|
308
350
|
sortable: column.sortable !== false,
|
|
309
351
|
align: column.align || 'left',
|
|
@@ -330,7 +372,8 @@ export function UserListComponent({
|
|
|
330
372
|
field: 'actions',
|
|
331
373
|
headerName: t('Common.ACTIONS', 'Actions'),
|
|
332
374
|
minWidth: Math.max(220, 110 + (visibleRowActions.length + (showDeleteAction ? 1 : 0)) * 110),
|
|
333
|
-
|
|
375
|
+
maxWidth: Math.max(360, 120 + (visibleRowActions.length + (showDeleteAction ? 1 : 0)) * 120),
|
|
376
|
+
flex: 1.1,
|
|
334
377
|
sortable: false,
|
|
335
378
|
filterable: false,
|
|
336
379
|
disableColumnMenu: true,
|
|
@@ -394,6 +437,8 @@ export function UserListComponent({
|
|
|
394
437
|
}, [
|
|
395
438
|
t,
|
|
396
439
|
roles,
|
|
440
|
+
showNewColumn,
|
|
441
|
+
showSuccessfulLoginColumn,
|
|
397
442
|
showRoleColumn,
|
|
398
443
|
showDeleteAction,
|
|
399
444
|
visibleExtraColumns,
|
|
@@ -38,6 +38,8 @@ export function AccountPage({
|
|
|
38
38
|
userListExtraContext = null,
|
|
39
39
|
userListRefreshTrigger = 0,
|
|
40
40
|
userListCanEditUser = null,
|
|
41
|
+
userListShowNewColumn = true,
|
|
42
|
+
userListShowSuccessfulLoginColumn = true,
|
|
41
43
|
userListShowRoleColumn = true,
|
|
42
44
|
userListOnChangeRole = null,
|
|
43
45
|
userListShowDeleteAction = true,
|
|
@@ -232,6 +234,8 @@ export function AccountPage({
|
|
|
232
234
|
extraContext={userListExtraContext}
|
|
233
235
|
refreshTrigger={userListRefreshTrigger}
|
|
234
236
|
canEditUser={userListCanEditUser}
|
|
237
|
+
showNewColumn={userListShowNewColumn}
|
|
238
|
+
showSuccessfulLoginColumn={userListShowSuccessfulLoginColumn}
|
|
235
239
|
showRoleColumn={userListShowRoleColumn}
|
|
236
240
|
onChangeRole={userListOnChangeRole}
|
|
237
241
|
showDeleteAction={userListShowDeleteAction}
|