@micha.bigler/ui-core-micha 2.2.3 → 2.2.4
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/AllowedEmailDomainsManager.js +43 -0
- package/dist/components/QrSignupManager.js +222 -12
- package/dist/components/RegistrationMethodsManager.js +2 -40
- package/dist/i18n/authTranslations.js +192 -0
- package/dist/pages/AccountPage.js +2 -1
- package/package.json +1 -1
- package/src/components/AllowedEmailDomainsManager.jsx +87 -0
- package/src/components/QrSignupManager.jsx +292 -43
- package/src/components/RegistrationMethodsManager.jsx +0 -66
- package/src/i18n/authTranslations.ts +192 -0
- package/src/pages/AccountPage.jsx +20 -5
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import React, { useEffect, useState } from 'react';
|
|
3
|
+
import { Alert, Box, Button, TextField, Typography, } from '@mui/material';
|
|
4
|
+
import { useTranslation } from 'react-i18next';
|
|
5
|
+
import { updateAuthPolicy } from '../auth/authApi';
|
|
6
|
+
export function AllowedEmailDomainsManager({ domains = [], enabled = false, onPolicyChange, }) {
|
|
7
|
+
const { t } = useTranslation();
|
|
8
|
+
const [domainsText, setDomainsText] = useState('');
|
|
9
|
+
const [busy, setBusy] = useState(false);
|
|
10
|
+
const [error, setError] = useState('');
|
|
11
|
+
const [success, setSuccess] = useState('');
|
|
12
|
+
useEffect(() => {
|
|
13
|
+
setDomainsText((domains || []).join('\n'));
|
|
14
|
+
}, [domains]);
|
|
15
|
+
const handleSave = async () => {
|
|
16
|
+
setBusy(true);
|
|
17
|
+
setError('');
|
|
18
|
+
setSuccess('');
|
|
19
|
+
try {
|
|
20
|
+
const allowedEmailDomains = domainsText
|
|
21
|
+
.split(/\r?\n/)
|
|
22
|
+
.map((value) => value.trim())
|
|
23
|
+
.filter(Boolean);
|
|
24
|
+
const next = await updateAuthPolicy({
|
|
25
|
+
allowed_email_domains: allowedEmailDomains,
|
|
26
|
+
});
|
|
27
|
+
setDomainsText(((next === null || next === void 0 ? void 0 : next.allowed_email_domains) || allowedEmailDomains).join('\n'));
|
|
28
|
+
setSuccess(t('Auth.AUTH_POLICY_SAVE_SUCCESS', 'Authentication settings saved.'));
|
|
29
|
+
if (onPolicyChange)
|
|
30
|
+
onPolicyChange(next);
|
|
31
|
+
}
|
|
32
|
+
catch (err) {
|
|
33
|
+
setError(t((err === null || err === void 0 ? void 0 : err.code) || 'Auth.AUTH_POLICY_UPDATE_FAILED', 'Could not save authentication settings.'));
|
|
34
|
+
}
|
|
35
|
+
finally {
|
|
36
|
+
setBusy(false);
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
if (!enabled) {
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
return (_jsxs(Box, { children: [_jsx(Typography, { variant: "h6", gutterBottom: true, children: t('Auth.ALLOWED_EMAIL_DOMAINS_TITLE', 'Allowed Email Domains') }), _jsx(Typography, { variant: "body2", sx: { mb: 2, color: 'text.secondary' }, children: t('Auth.ALLOWED_EMAIL_DOMAINS_CARD_HINT', 'Only addresses from these domains can use email-domain sign-up.') }), error && _jsx(Alert, { severity: "error", sx: { mb: 2 }, children: error }), success && _jsx(Alert, { severity: "success", sx: { mb: 2 }, children: success }), _jsx(TextField, { label: t('Auth.ALLOWED_EMAIL_DOMAINS_LABEL', 'Allowed email domains'), helperText: t('Auth.ALLOWED_EMAIL_DOMAINS_HINT', 'One domain per line, e.g. example.org. You can leave this empty temporarily.'), multiline: true, minRows: 4, fullWidth: true, value: domainsText, onChange: (event) => setDomainsText(event.target.value), disabled: busy }), _jsx(Button, { variant: "contained", sx: { mt: 2 }, onClick: handleSave, disabled: busy, children: t('Common.SAVE', 'Save') })] }));
|
|
43
|
+
}
|
|
@@ -1,32 +1,72 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import React, { useEffect, useRef, useState } from 'react';
|
|
3
|
-
import { Alert, Box, Button, TextField, Typography, } from '@mui/material';
|
|
2
|
+
import React, { useEffect, useMemo, useRef, useState } from 'react';
|
|
3
|
+
import { Alert, Box, Button, Stack, TextField, Typography, } from '@mui/material';
|
|
4
4
|
import { useTranslation } from 'react-i18next';
|
|
5
5
|
import { QRCodeSVG } from 'qrcode.react';
|
|
6
|
-
import { createSignupQr } from '../auth/authApi';
|
|
7
|
-
|
|
6
|
+
import { createSignupQr, updateAuthPolicy } from '../auth/authApi';
|
|
7
|
+
const DEFAULT_EXPIRY_DAYS = 90;
|
|
8
|
+
function clampExpiryDays(value) {
|
|
9
|
+
const parsed = parseInt(value, 10);
|
|
10
|
+
if (!Number.isFinite(parsed) || parsed < 1) {
|
|
11
|
+
return DEFAULT_EXPIRY_DAYS;
|
|
12
|
+
}
|
|
13
|
+
return parsed;
|
|
14
|
+
}
|
|
15
|
+
function escapeHtml(value) {
|
|
16
|
+
return String(value !== null && value !== void 0 ? value : '')
|
|
17
|
+
.replace(/&/g, '&')
|
|
18
|
+
.replace(/</g, '<')
|
|
19
|
+
.replace(/>/g, '>')
|
|
20
|
+
.replace(/"/g, '"')
|
|
21
|
+
.replace(/'/g, ''');
|
|
22
|
+
}
|
|
23
|
+
export function QrSignupManager({ enabled = false, expiryDays = DEFAULT_EXPIRY_DAYS, onPolicyChange, }) {
|
|
8
24
|
const { t } = useTranslation();
|
|
9
|
-
const
|
|
25
|
+
const qrWrapperRef = useRef(null);
|
|
26
|
+
const [currentExpiryDays, setCurrentExpiryDays] = useState(String(expiryDays || DEFAULT_EXPIRY_DAYS));
|
|
10
27
|
const [busy, setBusy] = useState(false);
|
|
28
|
+
const [savingPolicy, setSavingPolicy] = useState(false);
|
|
11
29
|
const [error, setError] = useState('');
|
|
12
30
|
const [success, setSuccess] = useState('');
|
|
13
31
|
const [result, setResult] = useState(null);
|
|
32
|
+
const [copyState, setCopyState] = useState('idle');
|
|
14
33
|
const hasGeneratedRef = useRef(false);
|
|
15
|
-
|
|
34
|
+
useEffect(() => {
|
|
35
|
+
setCurrentExpiryDays(String(expiryDays || DEFAULT_EXPIRY_DAYS));
|
|
36
|
+
}, [expiryDays]);
|
|
37
|
+
const formattedExpiry = useMemo(() => {
|
|
38
|
+
if (!(result === null || result === void 0 ? void 0 : result.expires_at)) {
|
|
39
|
+
return '';
|
|
40
|
+
}
|
|
41
|
+
const parsed = new Date(result.expires_at);
|
|
42
|
+
if (Number.isNaN(parsed.getTime())) {
|
|
43
|
+
return result.expires_at;
|
|
44
|
+
}
|
|
45
|
+
return new Intl.DateTimeFormat(undefined, {
|
|
46
|
+
year: 'numeric',
|
|
47
|
+
month: '2-digit',
|
|
48
|
+
day: '2-digit',
|
|
49
|
+
hour: '2-digit',
|
|
50
|
+
minute: '2-digit',
|
|
51
|
+
}).format(parsed);
|
|
52
|
+
}, [result]);
|
|
53
|
+
const generate = async (daysOverride) => {
|
|
16
54
|
if (!enabled) {
|
|
17
55
|
setResult(null);
|
|
18
56
|
return;
|
|
19
57
|
}
|
|
58
|
+
const nextDays = clampExpiryDays(daysOverride !== null && daysOverride !== void 0 ? daysOverride : currentExpiryDays);
|
|
20
59
|
setBusy(true);
|
|
21
60
|
setError('');
|
|
22
61
|
setSuccess('');
|
|
62
|
+
setCopyState('idle');
|
|
23
63
|
try {
|
|
24
64
|
const data = await createSignupQr({
|
|
25
|
-
|
|
65
|
+
expires_minutes: nextDays * 24 * 60,
|
|
26
66
|
});
|
|
27
67
|
setResult(data);
|
|
28
68
|
hasGeneratedRef.current = true;
|
|
29
|
-
setSuccess(t('Auth.
|
|
69
|
+
setSuccess(t('Auth.SIGNUP_QR_CREATE_SUCCESS', 'New QR signup link created.'));
|
|
30
70
|
}
|
|
31
71
|
catch (err) {
|
|
32
72
|
setError(t((err === null || err === void 0 ? void 0 : err.code) || 'Auth.SIGNUP_QR_CREATE_FAILED', 'Could not create signup QR.'));
|
|
@@ -40,6 +80,7 @@ export function QrSignupManager({ enabled = false }) {
|
|
|
40
80
|
setResult(null);
|
|
41
81
|
setError('');
|
|
42
82
|
setSuccess('');
|
|
83
|
+
setCopyState('idle');
|
|
43
84
|
hasGeneratedRef.current = false;
|
|
44
85
|
return;
|
|
45
86
|
}
|
|
@@ -51,15 +92,17 @@ export function QrSignupManager({ enabled = false }) {
|
|
|
51
92
|
setBusy(true);
|
|
52
93
|
setError('');
|
|
53
94
|
setSuccess('');
|
|
95
|
+
setCopyState('idle');
|
|
54
96
|
try {
|
|
97
|
+
const days = clampExpiryDays(expiryDays);
|
|
55
98
|
const data = await createSignupQr({
|
|
56
|
-
|
|
99
|
+
expires_minutes: days * 24 * 60,
|
|
57
100
|
});
|
|
58
101
|
if (!active)
|
|
59
102
|
return;
|
|
60
103
|
setResult(data);
|
|
61
104
|
hasGeneratedRef.current = true;
|
|
62
|
-
setSuccess(t('Auth.
|
|
105
|
+
setSuccess(t('Auth.SIGNUP_QR_CREATE_SUCCESS', 'New QR signup link created.'));
|
|
63
106
|
}
|
|
64
107
|
catch (err) {
|
|
65
108
|
if (!active)
|
|
@@ -76,6 +119,173 @@ export function QrSignupManager({ enabled = false }) {
|
|
|
76
119
|
return () => {
|
|
77
120
|
active = false;
|
|
78
121
|
};
|
|
79
|
-
}, [enabled,
|
|
80
|
-
|
|
122
|
+
}, [enabled, expiryDays, t]);
|
|
123
|
+
const handleSavePolicy = async () => {
|
|
124
|
+
const nextDays = clampExpiryDays(currentExpiryDays);
|
|
125
|
+
setSavingPolicy(true);
|
|
126
|
+
setError('');
|
|
127
|
+
setSuccess('');
|
|
128
|
+
try {
|
|
129
|
+
const next = await updateAuthPolicy({
|
|
130
|
+
signup_qr_expiry_days: nextDays,
|
|
131
|
+
});
|
|
132
|
+
setCurrentExpiryDays(String((next === null || next === void 0 ? void 0 : next.signup_qr_expiry_days) || nextDays));
|
|
133
|
+
setSuccess(t('Auth.AUTH_POLICY_SAVE_SUCCESS', 'Authentication settings saved.'));
|
|
134
|
+
if (onPolicyChange)
|
|
135
|
+
onPolicyChange(next);
|
|
136
|
+
}
|
|
137
|
+
catch (err) {
|
|
138
|
+
setError(t((err === null || err === void 0 ? void 0 : err.code) || 'Auth.AUTH_POLICY_UPDATE_FAILED', 'Could not save authentication settings.'));
|
|
139
|
+
}
|
|
140
|
+
finally {
|
|
141
|
+
setSavingPolicy(false);
|
|
142
|
+
}
|
|
143
|
+
};
|
|
144
|
+
const handleCopyLink = async () => {
|
|
145
|
+
var _a;
|
|
146
|
+
const signupUrl = result === null || result === void 0 ? void 0 : result.signup_url;
|
|
147
|
+
if (!signupUrl || !((_a = navigator === null || navigator === void 0 ? void 0 : navigator.clipboard) === null || _a === void 0 ? void 0 : _a.writeText)) {
|
|
148
|
+
setCopyState('error');
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
try {
|
|
152
|
+
await navigator.clipboard.writeText(signupUrl);
|
|
153
|
+
setCopyState('copied');
|
|
154
|
+
}
|
|
155
|
+
catch (_error) {
|
|
156
|
+
setCopyState('error');
|
|
157
|
+
}
|
|
158
|
+
};
|
|
159
|
+
const handleSavePdf = () => {
|
|
160
|
+
var _a;
|
|
161
|
+
if (!(result === null || result === void 0 ? void 0 : result.signup_url)) {
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
const svgMarkup = (_a = qrWrapperRef.current) === null || _a === void 0 ? void 0 : _a.innerHTML;
|
|
165
|
+
if (!svgMarkup) {
|
|
166
|
+
setError(t('Auth.SIGNUP_QR_PDF_NOT_READY', 'The QR image is not ready yet. Please try again.'));
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
const printWindow = window.open('', '_blank', 'width=960,height=900');
|
|
170
|
+
if (!printWindow) {
|
|
171
|
+
setError(t('Auth.SIGNUP_QR_PDF_BLOCKED', 'Popup blocked. Please allow popups to save the QR card as PDF.'));
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
const safeUrl = escapeHtml(result.signup_url);
|
|
175
|
+
const safeExpiresAt = escapeHtml(formattedExpiry || result.expires_at || '');
|
|
176
|
+
printWindow.document.write(`
|
|
177
|
+
<!DOCTYPE html>
|
|
178
|
+
<html>
|
|
179
|
+
<head>
|
|
180
|
+
<meta charset="utf-8" />
|
|
181
|
+
<title>${escapeHtml(t('Auth.SIGNUP_QR_MANAGER_TITLE', 'QR Signup'))}</title>
|
|
182
|
+
<style>
|
|
183
|
+
body {
|
|
184
|
+
margin: 0;
|
|
185
|
+
padding: 32px;
|
|
186
|
+
font-family: Arial, sans-serif;
|
|
187
|
+
background: #f5f7fb;
|
|
188
|
+
color: #122033;
|
|
189
|
+
}
|
|
190
|
+
.card {
|
|
191
|
+
max-width: 720px;
|
|
192
|
+
margin: 0 auto;
|
|
193
|
+
border: 1px solid #d9e2f2;
|
|
194
|
+
border-radius: 20px;
|
|
195
|
+
background: #ffffff;
|
|
196
|
+
padding: 32px;
|
|
197
|
+
box-sizing: border-box;
|
|
198
|
+
}
|
|
199
|
+
.eyebrow {
|
|
200
|
+
display: inline-block;
|
|
201
|
+
padding: 6px 10px;
|
|
202
|
+
border-radius: 999px;
|
|
203
|
+
background: #e8f0ff;
|
|
204
|
+
color: #23408e;
|
|
205
|
+
font-size: 12px;
|
|
206
|
+
font-weight: 700;
|
|
207
|
+
letter-spacing: 0.06em;
|
|
208
|
+
text-transform: uppercase;
|
|
209
|
+
}
|
|
210
|
+
h1 {
|
|
211
|
+
margin: 16px 0 8px;
|
|
212
|
+
font-size: 28px;
|
|
213
|
+
line-height: 1.2;
|
|
214
|
+
}
|
|
215
|
+
.qr-box {
|
|
216
|
+
display: flex;
|
|
217
|
+
justify-content: center;
|
|
218
|
+
align-items: center;
|
|
219
|
+
padding: 24px;
|
|
220
|
+
border-radius: 16px;
|
|
221
|
+
background: #ffffff;
|
|
222
|
+
border: 1px solid #d9e2f2;
|
|
223
|
+
}
|
|
224
|
+
.meta {
|
|
225
|
+
margin-top: 20px;
|
|
226
|
+
padding: 16px;
|
|
227
|
+
border-radius: 16px;
|
|
228
|
+
background: #f8faff;
|
|
229
|
+
border: 1px solid #d9e2f2;
|
|
230
|
+
word-break: break-word;
|
|
231
|
+
font-size: 14px;
|
|
232
|
+
line-height: 1.5;
|
|
233
|
+
}
|
|
234
|
+
@media print {
|
|
235
|
+
body {
|
|
236
|
+
background: #ffffff;
|
|
237
|
+
padding: 0;
|
|
238
|
+
}
|
|
239
|
+
.card {
|
|
240
|
+
border: 0;
|
|
241
|
+
border-radius: 0;
|
|
242
|
+
max-width: none;
|
|
243
|
+
padding: 0;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
</style>
|
|
247
|
+
</head>
|
|
248
|
+
<body>
|
|
249
|
+
<div class="card">
|
|
250
|
+
<div class="eyebrow">${escapeHtml(t('Auth.SIGNUP_QR_MANAGER_TITLE', 'QR Signup'))}</div>
|
|
251
|
+
<h1>${escapeHtml(t('Auth.SIGNUP_QR_PRINT_TITLE', 'Sign-Up Access'))}</h1>
|
|
252
|
+
<div class="qr-box">${svgMarkup}</div>
|
|
253
|
+
<div class="meta">
|
|
254
|
+
<strong>${escapeHtml(t('Auth.SIGNUP_QR_LINK_LABEL', 'Signup link'))}</strong><br />
|
|
255
|
+
<a href="${safeUrl}">${safeUrl}</a><br /><br />
|
|
256
|
+
<strong>${escapeHtml(t('Auth.SIGNUP_QR_VALID_UNTIL', 'Valid until'))}</strong>: ${safeExpiresAt}
|
|
257
|
+
</div>
|
|
258
|
+
</div>
|
|
259
|
+
<script>
|
|
260
|
+
window.addEventListener('load', function () {
|
|
261
|
+
window.focus();
|
|
262
|
+
window.print();
|
|
263
|
+
});
|
|
264
|
+
</script>
|
|
265
|
+
</body>
|
|
266
|
+
</html>
|
|
267
|
+
`);
|
|
268
|
+
printWindow.document.close();
|
|
269
|
+
};
|
|
270
|
+
if (!enabled) {
|
|
271
|
+
return null;
|
|
272
|
+
}
|
|
273
|
+
return (_jsxs(Box, { children: [_jsx(Typography, { variant: "h6", gutterBottom: true, children: t('Auth.SIGNUP_QR_MANAGER_TITLE', 'QR Signup') }), _jsx(Typography, { variant: "body2", sx: { mb: 2, color: 'text.secondary' }, children: t('Auth.SIGNUP_QR_MANAGER_HINT', 'Save the default validity, then generate and share QR signup links below.') }), error && _jsx(Alert, { severity: "error", sx: { mb: 2 }, children: error }), success && _jsx(Alert, { severity: "success", sx: { mb: 2 }, children: success }), copyState === 'copied' && (_jsx(Alert, { severity: "success", sx: { mb: 2 }, children: t('Auth.SIGNUP_QR_LINK_COPIED', 'Signup link copied.') })), copyState === 'error' && (_jsx(Alert, { severity: "warning", sx: { mb: 2 }, children: t('Auth.SIGNUP_QR_COPY_UNAVAILABLE', 'Copying the link is not available in this browser.') })), _jsxs(Stack, { direction: { xs: 'column', sm: 'row' }, spacing: 1.5, alignItems: { sm: 'flex-start' }, children: [_jsx(TextField, { label: t('Auth.SIGNUP_QR_EXPIRY_DAYS_LABEL', 'QR signup validity (days)'), helperText: t('Auth.SIGNUP_QR_EXPIRY_DAYS_HINT', 'Default validity for newly generated QR signup links.'), type: "number", value: currentExpiryDays, onChange: (event) => setCurrentExpiryDays(event.target.value), disabled: savingPolicy || busy, sx: { flex: 1 } }), _jsx(Button, { variant: "contained", onClick: handleSavePolicy, disabled: savingPolicy || busy, sx: { minWidth: 120, mt: { sm: '8px' } }, children: t('Common.SAVE', 'Save') })] }), (result === null || result === void 0 ? void 0 : result.signup_url) && (_jsxs(Box, { sx: { mt: 3 }, children: [_jsx(Box, { sx: {
|
|
274
|
+
display: 'flex',
|
|
275
|
+
justifyContent: 'center',
|
|
276
|
+
alignItems: 'center',
|
|
277
|
+
borderRadius: 3,
|
|
278
|
+
border: '1px solid',
|
|
279
|
+
borderColor: 'divider',
|
|
280
|
+
bgcolor: '#ffffff',
|
|
281
|
+
py: 3,
|
|
282
|
+
px: 2,
|
|
283
|
+
}, children: _jsx(Box, { ref: qrWrapperRef, children: _jsx(QRCodeSVG, { value: result.signup_url, size: 220, includeMargin: true }) }) }), _jsxs(Box, { sx: {
|
|
284
|
+
mt: 2,
|
|
285
|
+
borderRadius: 3,
|
|
286
|
+
p: 2,
|
|
287
|
+
bgcolor: 'grey.50',
|
|
288
|
+
border: '1px solid',
|
|
289
|
+
borderColor: 'divider',
|
|
290
|
+
}, children: [_jsx(Typography, { variant: "subtitle2", gutterBottom: true, children: t('Auth.SIGNUP_QR_ACCESS_TITLE', 'Signup Access') }), _jsxs(Typography, { variant: "body2", color: "text.secondary", children: [t('Auth.SIGNUP_QR_VALID_UNTIL', 'Valid until'), ": ", formattedExpiry || result.expires_at] })] }), _jsxs(Stack, { direction: { xs: 'column', sm: 'row' }, spacing: 1, sx: { mt: 2 }, children: [_jsx(Button, { variant: "outlined", onClick: () => generate(), disabled: busy || savingPolicy, children: t('Auth.SIGNUP_QR_NEW_BUTTON', 'New QR-Code') }), _jsx(Button, { variant: "outlined", onClick: handleCopyLink, disabled: !(result === null || result === void 0 ? void 0 : result.signup_url) || busy, children: t('Auth.SIGNUP_QR_COPY_BUTTON', 'Copy Link') }), _jsx(Button, { variant: "outlined", onClick: handleSavePdf, disabled: !(result === null || result === void 0 ? void 0 : result.signup_url) || busy, children: t('Auth.SIGNUP_QR_PDF_BUTTON', 'Save as PDF') })] })] }))] }));
|
|
81
291
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import React, { useEffect, useState } from 'react';
|
|
3
|
-
import { Alert, Box,
|
|
3
|
+
import { Alert, Box, FormControlLabel, Stack, Switch, Typography, } from '@mui/material';
|
|
4
4
|
import { useTranslation } from 'react-i18next';
|
|
5
5
|
import { fetchAuthPolicy, updateAuthPolicy } from '../auth/authApi';
|
|
6
6
|
const EMPTY_POLICY = {
|
|
@@ -9,16 +9,11 @@ const EMPTY_POLICY = {
|
|
|
9
9
|
allow_self_signup_open: false,
|
|
10
10
|
allow_self_signup_email_domain: false,
|
|
11
11
|
allow_self_signup_qr: false,
|
|
12
|
-
allowed_email_domains: [],
|
|
13
12
|
required_auth_factor_count: 1,
|
|
14
|
-
signup_qr_expiry_days: 90,
|
|
15
13
|
};
|
|
16
14
|
export function RegistrationMethodsManager({ onPolicyChange }) {
|
|
17
15
|
const { t } = useTranslation();
|
|
18
16
|
const [policy, setPolicy] = useState(EMPTY_POLICY);
|
|
19
|
-
const [domainsText, setDomainsText] = useState('');
|
|
20
|
-
const [signupQrExpiryDays, setSignupQrExpiryDays] = useState(String(EMPTY_POLICY.signup_qr_expiry_days));
|
|
21
|
-
const [busy, setBusy] = useState(false);
|
|
22
17
|
const [busyField, setBusyField] = useState('');
|
|
23
18
|
const [error, setError] = useState('');
|
|
24
19
|
const [success, setSuccess] = useState('');
|
|
@@ -30,8 +25,6 @@ export function RegistrationMethodsManager({ onPolicyChange }) {
|
|
|
30
25
|
if (!active)
|
|
31
26
|
return;
|
|
32
27
|
setPolicy((prev) => (Object.assign(Object.assign({}, prev), data)));
|
|
33
|
-
setDomainsText(((data === null || data === void 0 ? void 0 : data.allowed_email_domains) || []).join('\n'));
|
|
34
|
-
setSignupQrExpiryDays(String((data === null || data === void 0 ? void 0 : data.signup_qr_expiry_days) || EMPTY_POLICY.signup_qr_expiry_days));
|
|
35
28
|
if (onPolicyChange)
|
|
36
29
|
onPolicyChange(data);
|
|
37
30
|
}
|
|
@@ -54,7 +47,6 @@ export function RegistrationMethodsManager({ onPolicyChange }) {
|
|
|
54
47
|
try {
|
|
55
48
|
const next = await updateAuthPolicy({ [field]: checked });
|
|
56
49
|
setPolicy((prev) => (Object.assign(Object.assign({}, prev), next)));
|
|
57
|
-
setDomainsText(((next === null || next === void 0 ? void 0 : next.allowed_email_domains) || []).join('\n'));
|
|
58
50
|
setSuccess(t('Auth.AUTH_POLICY_SAVE_SUCCESS', 'Authentication settings saved.'));
|
|
59
51
|
if (onPolicyChange)
|
|
60
52
|
onPolicyChange(next);
|
|
@@ -67,35 +59,5 @@ export function RegistrationMethodsManager({ onPolicyChange }) {
|
|
|
67
59
|
setBusyField('');
|
|
68
60
|
}
|
|
69
61
|
};
|
|
70
|
-
|
|
71
|
-
setBusy(true);
|
|
72
|
-
setError('');
|
|
73
|
-
setSuccess('');
|
|
74
|
-
try {
|
|
75
|
-
const allowed_email_domains = domainsText
|
|
76
|
-
.split(/\r?\n/)
|
|
77
|
-
.map((value) => value.trim())
|
|
78
|
-
.filter(Boolean);
|
|
79
|
-
const parsedExpiryDays = parseInt(signupQrExpiryDays, 10);
|
|
80
|
-
const next = await updateAuthPolicy({
|
|
81
|
-
allowed_email_domains,
|
|
82
|
-
signup_qr_expiry_days: Number.isFinite(parsedExpiryDays) && parsedExpiryDays > 0
|
|
83
|
-
? parsedExpiryDays
|
|
84
|
-
: EMPTY_POLICY.signup_qr_expiry_days,
|
|
85
|
-
});
|
|
86
|
-
setPolicy((prev) => (Object.assign(Object.assign({}, prev), next)));
|
|
87
|
-
setDomainsText(((next === null || next === void 0 ? void 0 : next.allowed_email_domains) || allowed_email_domains).join('\n'));
|
|
88
|
-
setSignupQrExpiryDays(String((next === null || next === void 0 ? void 0 : next.signup_qr_expiry_days) || EMPTY_POLICY.signup_qr_expiry_days));
|
|
89
|
-
setSuccess(t('Auth.AUTH_POLICY_SAVE_SUCCESS', 'Authentication settings saved.'));
|
|
90
|
-
if (onPolicyChange)
|
|
91
|
-
onPolicyChange(next);
|
|
92
|
-
}
|
|
93
|
-
catch (err) {
|
|
94
|
-
setError(t((err === null || err === void 0 ? void 0 : err.code) || 'Auth.AUTH_POLICY_UPDATE_FAILED', 'Could not save authentication settings.'));
|
|
95
|
-
}
|
|
96
|
-
finally {
|
|
97
|
-
setBusy(false);
|
|
98
|
-
}
|
|
99
|
-
};
|
|
100
|
-
return (_jsxs(Box, { children: [_jsx(Typography, { variant: "h6", gutterBottom: true, children: t('Auth.REGISTRATION_METHODS_TITLE', 'Registration Methods') }), _jsx(Typography, { variant: "body2", sx: { mb: 2, color: 'text.secondary' }, children: t('Auth.REGISTRATION_METHODS_HINT', 'Choose which signup and invite flows are active for this app.') }), error && _jsx(Alert, { severity: "error", sx: { mb: 2 }, children: error }), success && _jsx(Alert, { severity: "success", sx: { mb: 2 }, children: success }), _jsxs(Stack, { spacing: 1, children: [_jsx(FormControlLabel, { control: (_jsx(Switch, { checked: Boolean(policy.allow_admin_invite), onChange: toggle('allow_admin_invite'), disabled: Boolean(busyField) })), label: t('Auth.ADMIN_INVITE_LABEL', 'Admin invite') }), _jsx(FormControlLabel, { control: (_jsx(Switch, { checked: Boolean(policy.allow_self_signup_access_code), onChange: toggle('allow_self_signup_access_code'), disabled: Boolean(busyField) })), label: t('Auth.SIGNUP_ACCESS_CODE_LABEL', 'Self-signup with access code') }), _jsx(FormControlLabel, { control: (_jsx(Switch, { checked: Boolean(policy.allow_self_signup_open), onChange: toggle('allow_self_signup_open'), disabled: Boolean(busyField) })), label: t('Auth.SIGNUP_OPEN_LABEL', 'Open self-signup') }), _jsx(FormControlLabel, { control: (_jsx(Switch, { checked: Boolean(policy.allow_self_signup_email_domain), onChange: toggle('allow_self_signup_email_domain'), disabled: Boolean(busyField) })), label: t('Auth.SIGNUP_EMAIL_DOMAIN_LABEL', 'Self-signup by email domain') }), _jsx(FormControlLabel, { control: (_jsx(Switch, { checked: Boolean(policy.allow_self_signup_qr), onChange: toggle('allow_self_signup_qr'), disabled: Boolean(busyField) })), label: t('Auth.SIGNUP_QR_LABEL', 'Self-signup by QR') })] }), policy.allow_self_signup_email_domain && !(policy.allowed_email_domains || []).length && (_jsx(Alert, { severity: "info", sx: { mt: 2 }, children: t('Auth.EMAIL_DOMAIN_CURRENTLY_BLOCKED_HINT', 'Email-domain signup is enabled, but it stays blocked until at least one allowed domain is saved.') })), _jsx(TextField, { label: t('Auth.ALLOWED_EMAIL_DOMAINS_LABEL', 'Allowed email domains'), helperText: t('Auth.ALLOWED_EMAIL_DOMAINS_HINT', 'One domain per line, e.g. example.org. You can leave this empty temporarily.'), multiline: true, minRows: 3, fullWidth: true, sx: { mt: 2 }, value: domainsText, onChange: (event) => setDomainsText(event.target.value) }), _jsx(TextField, { label: t('Auth.SIGNUP_QR_EXPIRY_DAYS_LABEL', 'QR signup validity (days)'), helperText: t('Auth.SIGNUP_QR_EXPIRY_DAYS_HINT', 'Default validity for newly generated QR signup links.'), type: "number", fullWidth: true, sx: { mt: 2 }, value: signupQrExpiryDays, onChange: (event) => setSignupQrExpiryDays(event.target.value) }), _jsx(Button, { variant: "contained", sx: { mt: 2 }, onClick: save, disabled: busy, children: t('Common.SAVE', 'Save') })] }));
|
|
62
|
+
return (_jsxs(Box, { children: [_jsx(Typography, { variant: "h6", gutterBottom: true, children: t('Auth.REGISTRATION_METHODS_TITLE', 'Registration Methods') }), _jsx(Typography, { variant: "body2", sx: { mb: 2, color: 'text.secondary' }, children: t('Auth.REGISTRATION_METHODS_HINT', 'Choose which signup and invite flows are active for this app.') }), error && _jsx(Alert, { severity: "error", sx: { mb: 2 }, children: error }), success && _jsx(Alert, { severity: "success", sx: { mb: 2 }, children: success }), _jsxs(Stack, { spacing: 1, children: [_jsx(FormControlLabel, { control: (_jsx(Switch, { checked: Boolean(policy.allow_admin_invite), onChange: toggle('allow_admin_invite'), disabled: Boolean(busyField) })), label: t('Auth.ADMIN_INVITE_LABEL', 'Admin invite') }), _jsx(FormControlLabel, { control: (_jsx(Switch, { checked: Boolean(policy.allow_self_signup_access_code), onChange: toggle('allow_self_signup_access_code'), disabled: Boolean(busyField) })), label: t('Auth.SIGNUP_ACCESS_CODE_LABEL', 'Self-signup with access code') }), _jsx(FormControlLabel, { control: (_jsx(Switch, { checked: Boolean(policy.allow_self_signup_open), onChange: toggle('allow_self_signup_open'), disabled: Boolean(busyField) })), label: t('Auth.SIGNUP_OPEN_LABEL', 'Open self-signup') }), _jsx(FormControlLabel, { control: (_jsx(Switch, { checked: Boolean(policy.allow_self_signup_email_domain), onChange: toggle('allow_self_signup_email_domain'), disabled: Boolean(busyField) })), label: t('Auth.SIGNUP_EMAIL_DOMAIN_LABEL', 'Self-signup by email domain') }), _jsx(FormControlLabel, { control: (_jsx(Switch, { checked: Boolean(policy.allow_self_signup_qr), onChange: toggle('allow_self_signup_qr'), disabled: Boolean(busyField) })), label: t('Auth.SIGNUP_QR_LABEL', 'Self-signup by QR') })] }), policy.allow_self_signup_email_domain && !(policy.allowed_email_domains || []).length && (_jsx(Alert, { severity: "info", sx: { mt: 2 }, children: t('Auth.EMAIL_DOMAIN_CURRENTLY_BLOCKED_HINT', 'Email-domain signup is enabled, but it stays blocked until at least one allowed domain is saved.') }))] }));
|
|
101
63
|
}
|
|
@@ -1248,6 +1248,198 @@ export const authTranslations = {
|
|
|
1248
1248
|
"en": "Expired",
|
|
1249
1249
|
"sw": "Muda umekwisha"
|
|
1250
1250
|
},
|
|
1251
|
+
"Auth.AUTH_POLICY_FETCH_FAILED": {
|
|
1252
|
+
"de": "Die Authentifizierungsrichtlinie konnte nicht geladen werden.",
|
|
1253
|
+
"fr": "Impossible de charger la politique d'authentification.",
|
|
1254
|
+
"en": "Could not load authentication policy.",
|
|
1255
|
+
"sw": "Haikuweza kupakia sera ya uthibitishaji."
|
|
1256
|
+
},
|
|
1257
|
+
"Auth.AUTH_POLICY_UPDATE_FAILED": {
|
|
1258
|
+
"de": "Die Authentifizierungseinstellungen konnten nicht gespeichert werden.",
|
|
1259
|
+
"fr": "Impossible d'enregistrer les paramètres d'authentification.",
|
|
1260
|
+
"en": "Could not save authentication settings.",
|
|
1261
|
+
"sw": "Haikuweza kuhifadhi mipangilio ya uthibitishaji."
|
|
1262
|
+
},
|
|
1263
|
+
"Auth.AUTH_POLICY_SAVE_SUCCESS": {
|
|
1264
|
+
"de": "Die Authentifizierungseinstellungen wurden gespeichert.",
|
|
1265
|
+
"fr": "Les paramètres d'authentification ont été enregistrés.",
|
|
1266
|
+
"en": "Authentication settings saved.",
|
|
1267
|
+
"sw": "Mipangilio ya uthibitishaji imehifadhiwa."
|
|
1268
|
+
},
|
|
1269
|
+
"Auth.REGISTRATION_METHODS_TITLE": {
|
|
1270
|
+
"de": "Registrierungsmethoden",
|
|
1271
|
+
"fr": "Méthodes d'inscription",
|
|
1272
|
+
"en": "Registration Methods",
|
|
1273
|
+
"sw": "Mbinu za kujisajili"
|
|
1274
|
+
},
|
|
1275
|
+
"Auth.REGISTRATION_METHODS_HINT": {
|
|
1276
|
+
"de": "Wählen Sie aus, welche Registrierungs- und Einladungswege für diese App aktiv sind.",
|
|
1277
|
+
"fr": "Choisissez quels flux d'inscription et d'invitation sont actifs pour cette application.",
|
|
1278
|
+
"en": "Choose which signup and invite flows are active for this app.",
|
|
1279
|
+
"sw": "Chagua njia zipi za kujisajili na za mwaliko zimewashwa kwa programu hii."
|
|
1280
|
+
},
|
|
1281
|
+
"Auth.ADMIN_INVITE_LABEL": {
|
|
1282
|
+
"de": "Admin-Einladung",
|
|
1283
|
+
"fr": "Invitation admin",
|
|
1284
|
+
"en": "Admin invite",
|
|
1285
|
+
"sw": "Mwaliko wa msimamizi"
|
|
1286
|
+
},
|
|
1287
|
+
"Auth.SIGNUP_ACCESS_CODE_LABEL": {
|
|
1288
|
+
"de": "Selbstregistrierung mit Zugangscode",
|
|
1289
|
+
"fr": "Auto-inscription avec code d'accès",
|
|
1290
|
+
"en": "Self-signup with access code",
|
|
1291
|
+
"sw": "Kujisajili mwenyewe kwa msimbo wa ufikiaji"
|
|
1292
|
+
},
|
|
1293
|
+
"Auth.SIGNUP_OPEN_LABEL": {
|
|
1294
|
+
"de": "Offene Selbstregistrierung",
|
|
1295
|
+
"fr": "Auto-inscription ouverte",
|
|
1296
|
+
"en": "Open self-signup",
|
|
1297
|
+
"sw": "Kujisajili mwenyewe kwa wazi"
|
|
1298
|
+
},
|
|
1299
|
+
"Auth.SIGNUP_EMAIL_DOMAIN_LABEL": {
|
|
1300
|
+
"de": "Selbstregistrierung per E-Mail-Domain",
|
|
1301
|
+
"fr": "Auto-inscription par domaine e-mail",
|
|
1302
|
+
"en": "Self-signup by email domain",
|
|
1303
|
+
"sw": "Kujisajili mwenyewe kwa domeni ya barua pepe"
|
|
1304
|
+
},
|
|
1305
|
+
"Auth.SIGNUP_QR_LABEL": {
|
|
1306
|
+
"de": "Selbstregistrierung per QR",
|
|
1307
|
+
"fr": "Auto-inscription par QR",
|
|
1308
|
+
"en": "Self-signup by QR",
|
|
1309
|
+
"sw": "Kujisajili mwenyewe kwa QR"
|
|
1310
|
+
},
|
|
1311
|
+
"Auth.EMAIL_DOMAIN_CURRENTLY_BLOCKED_HINT": {
|
|
1312
|
+
"de": "Die Registrierung per E-Mail-Domain ist aktiviert, bleibt aber gesperrt, bis mindestens eine erlaubte Domain gespeichert ist.",
|
|
1313
|
+
"fr": "L'inscription par domaine e-mail est activée, mais reste bloquée tant qu'au moins un domaine autorisé n'est pas enregistré.",
|
|
1314
|
+
"en": "Email-domain signup is enabled, but it stays blocked until at least one allowed domain is saved.",
|
|
1315
|
+
"sw": "Usajili wa domeni ya barua pepe umewashwa, lakini unabaki kuzuiwa hadi angalau domeni moja iliyoidhinishwa ihifadhiwe."
|
|
1316
|
+
},
|
|
1317
|
+
"Auth.ALLOWED_EMAIL_DOMAINS_TITLE": {
|
|
1318
|
+
"de": "Erlaubte E-Mail-Domains",
|
|
1319
|
+
"fr": "Domaines e-mail autorisés",
|
|
1320
|
+
"en": "Allowed Email Domains",
|
|
1321
|
+
"sw": "Domeni za barua pepe zinazoruhusiwa"
|
|
1322
|
+
},
|
|
1323
|
+
"Auth.ALLOWED_EMAIL_DOMAINS_CARD_HINT": {
|
|
1324
|
+
"de": "Nur Adressen aus diesen Domains dürfen die Registrierung per E-Mail-Domain verwenden.",
|
|
1325
|
+
"fr": "Seules les adresses de ces domaines peuvent utiliser l'inscription par domaine e-mail.",
|
|
1326
|
+
"en": "Only addresses from these domains can use email-domain sign-up.",
|
|
1327
|
+
"sw": "Ni anwani kutoka kwenye domeni hizi tu zinaweza kutumia kujisajili kwa domeni ya barua pepe."
|
|
1328
|
+
},
|
|
1329
|
+
"Auth.ALLOWED_EMAIL_DOMAINS_LABEL": {
|
|
1330
|
+
"de": "Erlaubte E-Mail-Domains",
|
|
1331
|
+
"fr": "Domaines e-mail autorisés",
|
|
1332
|
+
"en": "Allowed email domains",
|
|
1333
|
+
"sw": "Domeni za barua pepe zinazoruhusiwa"
|
|
1334
|
+
},
|
|
1335
|
+
"Auth.ALLOWED_EMAIL_DOMAINS_HINT": {
|
|
1336
|
+
"de": "Eine Domain pro Zeile, z. B. example.org. Sie können das Feld vorübergehend leer lassen.",
|
|
1337
|
+
"fr": "Un domaine par ligne, p. ex. example.org. Vous pouvez laisser ce champ vide temporairement.",
|
|
1338
|
+
"en": "One domain per line, e.g. example.org. You can leave this empty temporarily.",
|
|
1339
|
+
"sw": "Domeni moja kwa kila mstari, kwa mfano example.org. Unaweza kuiacha tupu kwa muda."
|
|
1340
|
+
},
|
|
1341
|
+
"Auth.SIGNUP_QR_MANAGER_TITLE": {
|
|
1342
|
+
"de": "QR-Registrierung",
|
|
1343
|
+
"fr": "Inscription par QR",
|
|
1344
|
+
"en": "QR Signup",
|
|
1345
|
+
"sw": "Usajili wa QR"
|
|
1346
|
+
},
|
|
1347
|
+
"Auth.SIGNUP_QR_MANAGER_HINT": {
|
|
1348
|
+
"de": "Speichern Sie zuerst die Standard-Gültigkeit und erzeugen Sie darunter neue QR-Registrierungslinks.",
|
|
1349
|
+
"fr": "Enregistrez d'abord la validité par défaut, puis générez ci-dessous de nouveaux liens d'inscription QR.",
|
|
1350
|
+
"en": "Save the default validity, then generate and share QR signup links below.",
|
|
1351
|
+
"sw": "Hifadhi muda wa kawaida wa uhalali kwanza, kisha tengeneza na ushiriki viungo vya usajili wa QR hapa chini."
|
|
1352
|
+
},
|
|
1353
|
+
"Auth.SIGNUP_QR_EXPIRY_DAYS_LABEL": {
|
|
1354
|
+
"de": "QR-Registrierung gültig (Tage)",
|
|
1355
|
+
"fr": "Validité de l'inscription QR (jours)",
|
|
1356
|
+
"en": "QR signup validity (days)",
|
|
1357
|
+
"sw": "Uhalali wa usajili wa QR (siku)"
|
|
1358
|
+
},
|
|
1359
|
+
"Auth.SIGNUP_QR_EXPIRY_DAYS_HINT": {
|
|
1360
|
+
"de": "Standard-Gültigkeit für neu erzeugte QR-Registrierungslinks.",
|
|
1361
|
+
"fr": "Validité par défaut des nouveaux liens d'inscription QR générés.",
|
|
1362
|
+
"en": "Default validity for newly generated QR signup links.",
|
|
1363
|
+
"sw": "Muda wa kawaida wa uhalali wa viungo vipya vya usajili wa QR."
|
|
1364
|
+
},
|
|
1365
|
+
"Auth.SIGNUP_QR_CREATE_SUCCESS": {
|
|
1366
|
+
"de": "Ein neuer QR-Registrierungslink wurde erstellt.",
|
|
1367
|
+
"fr": "Un nouveau lien d'inscription QR a été créé.",
|
|
1368
|
+
"en": "New QR signup link created.",
|
|
1369
|
+
"sw": "Kiungo kipya cha usajili wa QR kimetengenezwa."
|
|
1370
|
+
},
|
|
1371
|
+
"Auth.SIGNUP_QR_CREATE_FAILED": {
|
|
1372
|
+
"de": "Der QR-Registrierungslink konnte nicht erstellt werden.",
|
|
1373
|
+
"fr": "Impossible de créer le lien d'inscription QR.",
|
|
1374
|
+
"en": "Could not create signup QR.",
|
|
1375
|
+
"sw": "Haikuweza kuunda QR ya usajili."
|
|
1376
|
+
},
|
|
1377
|
+
"Auth.SIGNUP_QR_ACCESS_TITLE": {
|
|
1378
|
+
"de": "Registrierungszugang",
|
|
1379
|
+
"fr": "Accès à l'inscription",
|
|
1380
|
+
"en": "Signup Access",
|
|
1381
|
+
"sw": "Ufikiaji wa usajili"
|
|
1382
|
+
},
|
|
1383
|
+
"Auth.SIGNUP_QR_LINK_LABEL": {
|
|
1384
|
+
"de": "Registrierungslink",
|
|
1385
|
+
"fr": "Lien d'inscription",
|
|
1386
|
+
"en": "Signup link",
|
|
1387
|
+
"sw": "Kiungo cha usajili"
|
|
1388
|
+
},
|
|
1389
|
+
"Auth.SIGNUP_QR_VALID_UNTIL": {
|
|
1390
|
+
"de": "Gültig bis",
|
|
1391
|
+
"fr": "Valable jusqu'au",
|
|
1392
|
+
"en": "Valid until",
|
|
1393
|
+
"sw": "Halali hadi"
|
|
1394
|
+
},
|
|
1395
|
+
"Auth.SIGNUP_QR_NEW_BUTTON": {
|
|
1396
|
+
"de": "Neuer QR-Code",
|
|
1397
|
+
"fr": "Nouveau code QR",
|
|
1398
|
+
"en": "New QR-Code",
|
|
1399
|
+
"sw": "Msimbo mpya wa QR"
|
|
1400
|
+
},
|
|
1401
|
+
"Auth.SIGNUP_QR_COPY_BUTTON": {
|
|
1402
|
+
"de": "Link kopieren",
|
|
1403
|
+
"fr": "Copier le lien",
|
|
1404
|
+
"en": "Copy Link",
|
|
1405
|
+
"sw": "Nakili kiungo"
|
|
1406
|
+
},
|
|
1407
|
+
"Auth.SIGNUP_QR_PDF_BUTTON": {
|
|
1408
|
+
"de": "Als PDF speichern",
|
|
1409
|
+
"fr": "Enregistrer en PDF",
|
|
1410
|
+
"en": "Save as PDF",
|
|
1411
|
+
"sw": "Hifadhi kama PDF"
|
|
1412
|
+
},
|
|
1413
|
+
"Auth.SIGNUP_QR_LINK_COPIED": {
|
|
1414
|
+
"de": "Der Registrierungslink wurde kopiert.",
|
|
1415
|
+
"fr": "Le lien d'inscription a été copié.",
|
|
1416
|
+
"en": "Signup link copied.",
|
|
1417
|
+
"sw": "Kiungo cha usajili kimenakiliwa."
|
|
1418
|
+
},
|
|
1419
|
+
"Auth.SIGNUP_QR_COPY_UNAVAILABLE": {
|
|
1420
|
+
"de": "Das Kopieren des Links ist in diesem Browser nicht verfügbar.",
|
|
1421
|
+
"fr": "La copie du lien n'est pas disponible dans ce navigateur.",
|
|
1422
|
+
"en": "Copying the link is not available in this browser.",
|
|
1423
|
+
"sw": "Kunakili kiungo hakupatikani katika kivinjari hiki."
|
|
1424
|
+
},
|
|
1425
|
+
"Auth.SIGNUP_QR_PDF_NOT_READY": {
|
|
1426
|
+
"de": "Das QR-Bild ist noch nicht bereit. Bitte versuchen Sie es erneut.",
|
|
1427
|
+
"fr": "L'image QR n'est pas encore prête. Veuillez réessayer.",
|
|
1428
|
+
"en": "The QR image is not ready yet. Please try again.",
|
|
1429
|
+
"sw": "Picha ya QR bado haijawa tayari. Tafadhali jaribu tena."
|
|
1430
|
+
},
|
|
1431
|
+
"Auth.SIGNUP_QR_PDF_BLOCKED": {
|
|
1432
|
+
"de": "Popup blockiert. Bitte erlauben Sie Popups, um die QR-Karte als PDF zu speichern.",
|
|
1433
|
+
"fr": "Fenêtre contextuelle bloquée. Veuillez autoriser les popups pour enregistrer la carte QR en PDF.",
|
|
1434
|
+
"en": "Popup blocked. Please allow popups to save the QR card as PDF.",
|
|
1435
|
+
"sw": "Dirisha ibukizi limezuiwa. Tafadhali ruhusu madirisha ibukizi ili kuhifadhi kadi ya QR kama PDF."
|
|
1436
|
+
},
|
|
1437
|
+
"Auth.SIGNUP_QR_PRINT_TITLE": {
|
|
1438
|
+
"de": "Registrierungszugang",
|
|
1439
|
+
"fr": "Accès à l'inscription",
|
|
1440
|
+
"en": "Sign-Up Access",
|
|
1441
|
+
"sw": "Ufikiaji wa kujisajili"
|
|
1442
|
+
},
|
|
1251
1443
|
"Common.YES": {
|
|
1252
1444
|
"de": "Ja",
|
|
1253
1445
|
"fr": "Oui",
|
|
@@ -13,6 +13,7 @@ import { SecurityComponent } from '../components/SecurityComponent';
|
|
|
13
13
|
import { UserListComponent } from '../components/UserListComponent';
|
|
14
14
|
import { UserInviteComponent } from '../components/UserInviteComponent';
|
|
15
15
|
import { AccessCodeManager } from '../components/AccessCodeManager';
|
|
16
|
+
import { AllowedEmailDomainsManager } from '../components/AllowedEmailDomainsManager';
|
|
16
17
|
import { RegistrationMethodsManager } from '../components/RegistrationMethodsManager';
|
|
17
18
|
import { AuthFactorRequirementCard } from '../components/AuthFactorRequirementCard';
|
|
18
19
|
import { QrSignupManager } from '../components/QrSignupManager';
|
|
@@ -91,5 +92,5 @@ export function AccountPage({ userListExtraColumns = [], userListExtraRowActions
|
|
|
91
92
|
const activeExtraTab = builtInTabValues.has(safeTab)
|
|
92
93
|
? null
|
|
93
94
|
: extraTabs.find((tab) => tab.value === safeTab);
|
|
94
|
-
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: [(isSuperUser || perms.can_invite) && (_jsx(Paper, { variant: "outlined", sx: { p: 2.5, borderRadius: 2 }, children: _jsx(RegistrationMethodsManager, { onPolicyChange: setAuthPolicy }) })), (isSuperUser || perms.can_invite) && (_jsx(Paper, { variant: "outlined", sx: { p: 2.5, borderRadius: 2 }, children: _jsx(AuthFactorRequirementCard, {}) })), (isSuperUser || perms.can_invite) && (_jsx(Paper, { variant: "outlined", sx: { p: 2.5, borderRadius: 2 }, children: _jsx(UserInviteComponent, {}) })), (isSuperUser || perms.can_manage_access_codes) && (_jsxs(Paper, { variant: "outlined", sx: { p: 2.5, borderRadius: 2 }, children: [_jsx(Typography, { variant: "body2", sx: { mb: 2, color: 'text.secondary' }, children: t('Account.ACCESS_CODES_HINT', 'Manage access codes for self-registration.') }), _jsx(AccessCodeManager, {})] })), (isSuperUser || perms.can_invite) && showBulkInviteCsvTab && (_jsx(Paper, { variant: "outlined", sx: { p: 2.5, borderRadius: 2 }, children: _jsx(BulkInviteCsvTab, Object.assign({}, bulkInviteCsvProps)) })), (isSuperUser || perms.can_invite) && (_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) }) }))] }) })), 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 }) }))] }));
|
|
95
|
+
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: [(isSuperUser || perms.can_invite) && (_jsx(Paper, { variant: "outlined", sx: { p: 2.5, borderRadius: 2 }, children: _jsx(RegistrationMethodsManager, { onPolicyChange: setAuthPolicy }) })), (isSuperUser || perms.can_invite) && (_jsx(Paper, { variant: "outlined", sx: { p: 2.5, borderRadius: 2 }, children: _jsx(AuthFactorRequirementCard, {}) })), (isSuperUser || perms.can_invite) && 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, {}) })), (isSuperUser || perms.can_manage_access_codes) && 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: "body2", sx: { mb: 2, color: 'text.secondary' }, children: t('Account.ACCESS_CODES_HINT', 'Manage access codes for self-registration.') }), _jsx(AccessCodeManager, {})] })), (isSuperUser || perms.can_invite) && 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)) })), (isSuperUser || perms.can_invite) && 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 }) })), (isSuperUser || perms.can_invite) && 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, onPolicyChange: setAuthPolicy }) }))] }) })), 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 }) }))] }));
|
|
95
96
|
}
|