@micha.bigler/ui-core-micha 2.2.3 → 2.2.5

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.
@@ -497,6 +497,12 @@ export const authTranslations = {
497
497
  "en": "Add code manually",
498
498
  "sw": "Ongeza msimbo mwenyewe"
499
499
  },
500
+ "Auth.ACCESS_CODE_MANAGER_TITLE": {
501
+ "de": "Zugangscodes",
502
+ "fr": "Codes d'accès",
503
+ "en": "Access Codes",
504
+ "sw": "Misimbo ya ufikiaji"
505
+ },
500
506
  "Auth.ACCESS_CODE_LABEL": {
501
507
  "de": "Zugangscode",
502
508
  "fr": "Code d'accès",
@@ -509,6 +515,24 @@ export const authTranslations = {
509
515
  "en": "Add",
510
516
  "sw": "Ongeza"
511
517
  },
518
+ "Auth.ACCESS_CODE_COPY_BUTTON": {
519
+ "de": "Kopieren",
520
+ "fr": "Copier",
521
+ "en": "Copy",
522
+ "sw": "Nakili"
523
+ },
524
+ "Auth.ACCESS_CODE_COPY_SUCCESS": {
525
+ "de": "Code kopiert.",
526
+ "fr": "Code copié.",
527
+ "en": "Code copied.",
528
+ "sw": "Msimbo umenakiliwa."
529
+ },
530
+ "Auth.ACCESS_CODE_COPY_FALLBACK": {
531
+ "de": "Code markieren und mit Strg+C kopieren.",
532
+ "fr": "Sélectionnez le code et copiez-le avec Ctrl+C.",
533
+ "en": "Select the code and copy it with Ctrl+C.",
534
+ "sw": "Chagua msimbo na unakili kwa Ctrl+C."
535
+ },
512
536
  "Auth.ACCESS_CODE_LIST_FAILED": {
513
537
  "de": "Zugangscodes konnten nicht geladen werden.",
514
538
  "fr": "Impossible de charger les codes d'accès.",
@@ -1295,6 +1319,216 @@ export const authTranslations = {
1295
1319
  "en": "Expired",
1296
1320
  "sw": "Muda umekwisha"
1297
1321
  },
1322
+ "Auth.AUTH_POLICY_FETCH_FAILED": {
1323
+ "de": "Die Authentifizierungsrichtlinie konnte nicht geladen werden.",
1324
+ "fr": "Impossible de charger la politique d'authentification.",
1325
+ "en": "Could not load authentication policy.",
1326
+ "sw": "Haikuweza kupakia sera ya uthibitishaji."
1327
+ },
1328
+ "Auth.AUTH_POLICY_UPDATE_FAILED": {
1329
+ "de": "Die Authentifizierungseinstellungen konnten nicht gespeichert werden.",
1330
+ "fr": "Impossible d'enregistrer les paramètres d'authentification.",
1331
+ "en": "Could not save authentication settings.",
1332
+ "sw": "Haikuweza kuhifadhi mipangilio ya uthibitishaji."
1333
+ },
1334
+ "Auth.AUTH_POLICY_SAVE_SUCCESS": {
1335
+ "de": "Die Authentifizierungseinstellungen wurden gespeichert.",
1336
+ "fr": "Les paramètres d'authentification ont été enregistrés.",
1337
+ "en": "Authentication settings saved.",
1338
+ "sw": "Mipangilio ya uthibitishaji imehifadhiwa."
1339
+ },
1340
+ "Auth.REGISTRATION_METHODS_TITLE": {
1341
+ "de": "Registrierungsmethoden",
1342
+ "fr": "Méthodes d'inscription",
1343
+ "en": "Registration Methods",
1344
+ "sw": "Mbinu za kujisajili"
1345
+ },
1346
+ "Auth.REGISTRATION_METHODS_HINT": {
1347
+ "de": "Wählen Sie aus, welche Registrierungs- und Einladungswege für diese App aktiv sind.",
1348
+ "fr": "Choisissez quels flux d'inscription et d'invitation sont actifs pour cette application.",
1349
+ "en": "Choose which signup and invite flows are active for this app.",
1350
+ "sw": "Chagua njia zipi za kujisajili na za mwaliko zimewashwa kwa programu hii."
1351
+ },
1352
+ "Auth.ADMIN_INVITE_LABEL": {
1353
+ "de": "Admin-Einladung",
1354
+ "fr": "Invitation admin",
1355
+ "en": "Admin invite",
1356
+ "sw": "Mwaliko wa msimamizi"
1357
+ },
1358
+ "Auth.SIGNUP_ACCESS_CODE_LABEL": {
1359
+ "de": "Selbstregistrierung mit Zugangscode",
1360
+ "fr": "Auto-inscription avec code d'accès",
1361
+ "en": "Self-signup with access code",
1362
+ "sw": "Kujisajili mwenyewe kwa msimbo wa ufikiaji"
1363
+ },
1364
+ "Auth.SIGNUP_OPEN_LABEL": {
1365
+ "de": "Offene Selbstregistrierung",
1366
+ "fr": "Auto-inscription ouverte",
1367
+ "en": "Open self-signup",
1368
+ "sw": "Kujisajili mwenyewe kwa wazi"
1369
+ },
1370
+ "Auth.SIGNUP_EMAIL_DOMAIN_LABEL": {
1371
+ "de": "Selbstregistrierung per E-Mail-Domain",
1372
+ "fr": "Auto-inscription par domaine e-mail",
1373
+ "en": "Self-signup by email domain",
1374
+ "sw": "Kujisajili mwenyewe kwa domeni ya barua pepe"
1375
+ },
1376
+ "Auth.SIGNUP_QR_LABEL": {
1377
+ "de": "Selbstregistrierung per QR",
1378
+ "fr": "Auto-inscription par QR",
1379
+ "en": "Self-signup by QR",
1380
+ "sw": "Kujisajili mwenyewe kwa QR"
1381
+ },
1382
+ "Auth.EMAIL_DOMAIN_CURRENTLY_BLOCKED_HINT": {
1383
+ "de": "Die Registrierung per E-Mail-Domain ist aktiviert, bleibt aber gesperrt, bis mindestens eine erlaubte Domain gespeichert ist.",
1384
+ "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é.",
1385
+ "en": "Email-domain signup is enabled, but it stays blocked until at least one allowed domain is saved.",
1386
+ "sw": "Usajili wa domeni ya barua pepe umewashwa, lakini unabaki kuzuiwa hadi angalau domeni moja iliyoidhinishwa ihifadhiwe."
1387
+ },
1388
+ "Auth.ALLOWED_EMAIL_DOMAINS_TITLE": {
1389
+ "de": "Erlaubte E-Mail-Domains",
1390
+ "fr": "Domaines e-mail autorisés",
1391
+ "en": "Allowed Email Domains",
1392
+ "sw": "Domeni za barua pepe zinazoruhusiwa"
1393
+ },
1394
+ "Auth.ALLOWED_EMAIL_DOMAINS_CARD_HINT": {
1395
+ "de": "Nur Adressen aus diesen Domains dürfen die Registrierung per E-Mail-Domain verwenden.",
1396
+ "fr": "Seules les adresses de ces domaines peuvent utiliser l'inscription par domaine e-mail.",
1397
+ "en": "Only addresses from these domains can use email-domain sign-up.",
1398
+ "sw": "Ni anwani kutoka kwenye domeni hizi tu zinaweza kutumia kujisajili kwa domeni ya barua pepe."
1399
+ },
1400
+ "Auth.ALLOWED_EMAIL_DOMAINS_LABEL": {
1401
+ "de": "Erlaubte E-Mail-Domains",
1402
+ "fr": "Domaines e-mail autorisés",
1403
+ "en": "Allowed email domains",
1404
+ "sw": "Domeni za barua pepe zinazoruhusiwa"
1405
+ },
1406
+ "Auth.ALLOWED_EMAIL_DOMAINS_HINT": {
1407
+ "de": "Eine Domain pro Zeile, z. B. example.org. Sie können das Feld vorübergehend leer lassen.",
1408
+ "fr": "Un domaine par ligne, p. ex. example.org. Vous pouvez laisser ce champ vide temporairement.",
1409
+ "en": "One domain per line, e.g. example.org. You can leave this empty temporarily.",
1410
+ "sw": "Domeni moja kwa kila mstari, kwa mfano example.org. Unaweza kuiacha tupu kwa muda."
1411
+ },
1412
+ "Auth.SIGNUP_QR_MANAGER_TITLE": {
1413
+ "de": "QR-Registrierung",
1414
+ "fr": "Inscription par QR",
1415
+ "en": "QR Signup",
1416
+ "sw": "Usajili wa QR"
1417
+ },
1418
+ "Auth.SIGNUP_QR_MANAGER_HINT": {
1419
+ "de": "Erzeugen und teilen Sie hier QR-Registrierungslinks.",
1420
+ "fr": "Générez et partagez ici des liens d'inscription QR.",
1421
+ "en": "Generate and share QR signup links below.",
1422
+ "sw": "Tengeneza na ushiriki viungo vya usajili wa QR hapa chini."
1423
+ },
1424
+ "Auth.SIGNUP_QR_VALIDITY_TITLE": {
1425
+ "de": "QR-Registrierung Gültigkeit",
1426
+ "fr": "Validité de l'inscription QR",
1427
+ "en": "QR Signup Validity",
1428
+ "sw": "Uhalali wa usajili wa QR"
1429
+ },
1430
+ "Auth.SIGNUP_QR_VALIDITY_HINT": {
1431
+ "de": "Legen Sie die Standard-Gültigkeit für neu erzeugte QR-Registrierungslinks fest.",
1432
+ "fr": "Définissez la validité par défaut des nouveaux liens d'inscription QR générés.",
1433
+ "en": "Set the default validity for newly generated QR signup links.",
1434
+ "sw": "Weka muda wa kawaida wa uhalali wa viungo vipya vya usajili wa QR."
1435
+ },
1436
+ "Auth.SIGNUP_QR_EXPIRY_DAYS_LABEL": {
1437
+ "de": "QR-Registrierung gültig (Tage)",
1438
+ "fr": "Validité de l'inscription QR (jours)",
1439
+ "en": "QR signup validity (days)",
1440
+ "sw": "Uhalali wa usajili wa QR (siku)"
1441
+ },
1442
+ "Auth.SIGNUP_QR_EXPIRY_DAYS_HINT": {
1443
+ "de": "Standard-Gültigkeit für neu erzeugte QR-Registrierungslinks.",
1444
+ "fr": "Validité par défaut des nouveaux liens d'inscription QR générés.",
1445
+ "en": "Default validity for newly generated QR signup links.",
1446
+ "sw": "Muda wa kawaida wa uhalali wa viungo vipya vya usajili wa QR."
1447
+ },
1448
+ "Auth.SIGNUP_QR_CREATE_SUCCESS": {
1449
+ "de": "Ein neuer QR-Registrierungslink wurde erstellt.",
1450
+ "fr": "Un nouveau lien d'inscription QR a été créé.",
1451
+ "en": "New QR signup link created.",
1452
+ "sw": "Kiungo kipya cha usajili wa QR kimetengenezwa."
1453
+ },
1454
+ "Auth.SIGNUP_QR_CREATE_FAILED": {
1455
+ "de": "Der QR-Registrierungslink konnte nicht erstellt werden.",
1456
+ "fr": "Impossible de créer le lien d'inscription QR.",
1457
+ "en": "Could not create signup QR.",
1458
+ "sw": "Haikuweza kuunda QR ya usajili."
1459
+ },
1460
+ "Auth.SIGNUP_QR_ACCESS_TITLE": {
1461
+ "de": "Registrierungszugang",
1462
+ "fr": "Accès à l'inscription",
1463
+ "en": "Signup Access",
1464
+ "sw": "Ufikiaji wa usajili"
1465
+ },
1466
+ "Auth.SIGNUP_QR_LINK_LABEL": {
1467
+ "de": "Registrierungslink",
1468
+ "fr": "Lien d'inscription",
1469
+ "en": "Signup link",
1470
+ "sw": "Kiungo cha usajili"
1471
+ },
1472
+ "Auth.SIGNUP_QR_VALID_UNTIL": {
1473
+ "de": "Gültig bis",
1474
+ "fr": "Valable jusqu'au",
1475
+ "en": "Valid until",
1476
+ "sw": "Halali hadi"
1477
+ },
1478
+ "Auth.SIGNUP_QR_NEW_BUTTON": {
1479
+ "de": "Neuer QR-Code",
1480
+ "fr": "Nouveau code QR",
1481
+ "en": "New QR-Code",
1482
+ "sw": "Msimbo mpya wa QR"
1483
+ },
1484
+ "Auth.SIGNUP_QR_COPY_BUTTON": {
1485
+ "de": "Link kopieren",
1486
+ "fr": "Copier le lien",
1487
+ "en": "Copy Link",
1488
+ "sw": "Nakili kiungo"
1489
+ },
1490
+ "Auth.SIGNUP_QR_PDF_BUTTON": {
1491
+ "de": "Als PDF speichern",
1492
+ "fr": "Enregistrer en PDF",
1493
+ "en": "Save as PDF",
1494
+ "sw": "Hifadhi kama PDF"
1495
+ },
1496
+ "Auth.SIGNUP_QR_LINK_COPIED": {
1497
+ "de": "Der Registrierungslink wurde kopiert.",
1498
+ "fr": "Le lien d'inscription a été copié.",
1499
+ "en": "Signup link copied.",
1500
+ "sw": "Kiungo cha usajili kimenakiliwa."
1501
+ },
1502
+ "Auth.SIGNUP_QR_COPY_UNAVAILABLE": {
1503
+ "de": "Das Kopieren des Links ist in diesem Browser nicht verfügbar.",
1504
+ "fr": "La copie du lien n'est pas disponible dans ce navigateur.",
1505
+ "en": "Copying the link is not available in this browser.",
1506
+ "sw": "Kunakili kiungo hakupatikani katika kivinjari hiki."
1507
+ },
1508
+ "Auth.SIGNUP_QR_PDF_NOT_READY": {
1509
+ "de": "Das QR-Bild ist noch nicht bereit. Bitte versuchen Sie es erneut.",
1510
+ "fr": "L'image QR n'est pas encore prête. Veuillez réessayer.",
1511
+ "en": "The QR image is not ready yet. Please try again.",
1512
+ "sw": "Picha ya QR bado haijawa tayari. Tafadhali jaribu tena."
1513
+ },
1514
+ "Auth.SIGNUP_QR_PDF_BLOCKED": {
1515
+ "de": "Popup blockiert. Bitte erlauben Sie Popups, um die QR-Karte als PDF zu speichern.",
1516
+ "fr": "Fenêtre contextuelle bloquée. Veuillez autoriser les popups pour enregistrer la carte QR en PDF.",
1517
+ "en": "Popup blocked. Please allow popups to save the QR card as PDF.",
1518
+ "sw": "Dirisha ibukizi limezuiwa. Tafadhali ruhusu madirisha ibukizi ili kuhifadhi kadi ya QR kama PDF."
1519
+ },
1520
+ "Auth.SIGNUP_QR_PRINT_TITLE": {
1521
+ "de": "Registrierungszugang",
1522
+ "fr": "Accès à l'inscription",
1523
+ "en": "Sign-Up Access",
1524
+ "sw": "Ufikiaji wa kujisajili"
1525
+ },
1526
+ "Auth.SAVE_BUTTON_LOADING": {
1527
+ "de": "Speichern…",
1528
+ "fr": "Enregistrement…",
1529
+ "en": "Saving…",
1530
+ "sw": "Inahifadhi..."
1531
+ },
1298
1532
  "Common.YES": {
1299
1533
  "de": "Ja",
1300
1534
  "fr": "Oui",
@@ -1,4 +1,4 @@
1
- import React, { useContext, useMemo, useState } from 'react';
1
+ import React, { useContext, useEffect, useMemo, useState } from 'react';
2
2
  import { Helmet } from 'react-helmet';
3
3
  import { useSearchParams } from 'react-router-dom';
4
4
  import {
@@ -23,12 +23,14 @@ import { SecurityComponent } from '../components/SecurityComponent';
23
23
  import { UserListComponent } from '../components/UserListComponent';
24
24
  import { UserInviteComponent } from '../components/UserInviteComponent';
25
25
  import { AccessCodeManager } from '../components/AccessCodeManager';
26
+ import { AllowedEmailDomainsManager } from '../components/AllowedEmailDomainsManager';
26
27
  import { RegistrationMethodsManager } from '../components/RegistrationMethodsManager';
27
28
  import { AuthFactorRequirementCard } from '../components/AuthFactorRequirementCard';
28
29
  import { QrSignupManager } from '../components/QrSignupManager';
30
+ import { QrSignupValidityManager } from '../components/QrSignupValidityManager';
29
31
  import { SupportRecoveryRequestsTab } from '../components/SupportRecoveryRequestsTab';
30
32
  import { BulkInviteCsvTab } from '../components/BulkInviteCsvTab';
31
- import { updateUserProfile } from '../auth/authApi'; // Ggf. Pfad anpassen
33
+ import { fetchAuthPolicy, updateUserProfile } from '../auth/authApi'; // Ggf. Pfad anpassen
32
34
 
33
35
  export function AccountPage({
34
36
  userListExtraColumns = [],
@@ -44,6 +46,7 @@ export function AccountPage({
44
46
  const { user, login, loading } = useContext(AuthContext);
45
47
  const [searchParams, setSearchParams] = useSearchParams();
46
48
  const [authPolicy, setAuthPolicy] = useState(null);
49
+ const [authPolicyError, setAuthPolicyError] = useState('');
47
50
 
48
51
  // 1. URL State Management
49
52
  const currentTabRaw = searchParams.get('tab') || 'profile';
@@ -69,6 +72,38 @@ export function AccountPage({
69
72
  login(updatedUser);
70
73
  };
71
74
 
75
+ useEffect(() => {
76
+ let active = true;
77
+ const canLoadPolicy = Boolean(user) && (isSuperUser || perms.can_invite || perms.can_manage_access_codes);
78
+
79
+ if (!canLoadPolicy) {
80
+ setAuthPolicy(null);
81
+ setAuthPolicyError('');
82
+ return undefined;
83
+ }
84
+
85
+ const loadPolicy = async () => {
86
+ try {
87
+ const data = await fetchAuthPolicy();
88
+ if (!active) return;
89
+ setAuthPolicy(data);
90
+ setAuthPolicyError('');
91
+ } catch (err) {
92
+ if (!active) return;
93
+ setAuthPolicy(null);
94
+ setAuthPolicyError(
95
+ t(err?.code || 'Auth.AUTH_POLICY_FETCH_FAILED', 'Could not load authentication policy.'),
96
+ );
97
+ }
98
+ };
99
+
100
+ loadPolicy();
101
+
102
+ return () => {
103
+ active = false;
104
+ };
105
+ }, [user, isSuperUser, perms.can_invite, perms.can_manage_access_codes, t]);
106
+
72
107
  // 3. Dynamic Tabs (angepasst für Superuser)
73
108
  const tabs = useMemo(() => {
74
109
  if (!user) return [];
@@ -194,7 +229,11 @@ export function AccountPage({
194
229
  <Stack spacing={2.5}>
195
230
  {(isSuperUser || perms.can_invite) && (
196
231
  <Paper variant="outlined" sx={{ p: 2.5, borderRadius: 2 }}>
197
- <RegistrationMethodsManager onPolicyChange={setAuthPolicy} />
232
+ <RegistrationMethodsManager
233
+ policy={authPolicy}
234
+ error={authPolicyError}
235
+ onPolicyChange={setAuthPolicy}
236
+ />
198
237
  </Paper>
199
238
  )}
200
239
 
@@ -204,14 +243,17 @@ export function AccountPage({
204
243
  </Paper>
205
244
  )}
206
245
 
207
- {(isSuperUser || perms.can_invite) && (
246
+ {(isSuperUser || perms.can_invite) && Boolean(authPolicy?.allow_admin_invite) && (
208
247
  <Paper variant="outlined" sx={{ p: 2.5, borderRadius: 2 }}>
209
248
  <UserInviteComponent />
210
249
  </Paper>
211
250
  )}
212
251
 
213
- {(isSuperUser || perms.can_manage_access_codes) && (
252
+ {(isSuperUser || perms.can_manage_access_codes) && Boolean(authPolicy?.allow_self_signup_access_code) && (
214
253
  <Paper variant="outlined" sx={{ p: 2.5, borderRadius: 2 }}>
254
+ <Typography variant="h6" gutterBottom>
255
+ {t('Auth.ACCESS_CODE_MANAGER_TITLE', 'Access Codes')}
256
+ </Typography>
215
257
  <Typography variant="body2" sx={{ mb: 2, color: 'text.secondary' }}>
216
258
  {t('Account.ACCESS_CODES_HINT', 'Manage access codes for self-registration.')}
217
259
  </Typography>
@@ -219,15 +261,38 @@ export function AccountPage({
219
261
  </Paper>
220
262
  )}
221
263
 
222
- {(isSuperUser || perms.can_invite) && showBulkInviteCsvTab && (
264
+ {(isSuperUser || perms.can_invite) && showBulkInviteCsvTab && Boolean(authPolicy?.allow_admin_invite) && (
223
265
  <Paper variant="outlined" sx={{ p: 2.5, borderRadius: 2 }}>
224
266
  <BulkInviteCsvTab {...bulkInviteCsvProps} />
225
267
  </Paper>
226
268
  )}
227
269
 
228
- {(isSuperUser || perms.can_invite) && (
270
+ {(isSuperUser || perms.can_invite) && Boolean(authPolicy?.allow_self_signup_email_domain) && (
271
+ <Paper variant="outlined" sx={{ p: 2.5, borderRadius: 2 }}>
272
+ <AllowedEmailDomainsManager
273
+ enabled={Boolean(authPolicy?.allow_self_signup_email_domain)}
274
+ domains={authPolicy?.allowed_email_domains || []}
275
+ onPolicyChange={setAuthPolicy}
276
+ />
277
+ </Paper>
278
+ )}
279
+
280
+ {(isSuperUser || perms.can_invite) && Boolean(authPolicy?.allow_self_signup_qr) && (
281
+ <Paper variant="outlined" sx={{ p: 2.5, borderRadius: 2 }}>
282
+ <QrSignupValidityManager
283
+ enabled={Boolean(authPolicy?.allow_self_signup_qr)}
284
+ expiryDays={authPolicy?.signup_qr_expiry_days}
285
+ onPolicyChange={setAuthPolicy}
286
+ />
287
+ </Paper>
288
+ )}
289
+
290
+ {(isSuperUser || perms.can_invite) && Boolean(authPolicy?.allow_self_signup_qr) && (
229
291
  <Paper variant="outlined" sx={{ p: 2.5, borderRadius: 2 }}>
230
- <QrSignupManager enabled={Boolean(authPolicy?.allow_self_signup_qr)} />
292
+ <QrSignupManager
293
+ enabled={Boolean(authPolicy?.allow_self_signup_qr)}
294
+ expiryDays={authPolicy?.signup_qr_expiry_days}
295
+ />
231
296
  </Paper>
232
297
  )}
233
298
  </Stack>