@micha.bigler/ui-core-micha 3.7.0 → 3.9.0
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/CHANGELOG.md +25 -0
- package/DESIGN.md +8 -0
- package/WORK_ORDERS.md +2 -0
- package/dist/auth/apiClient.js +8 -0
- package/dist/components/AccessCodeManager.js +5 -5
- package/dist/components/AccessCodeSingleUseToggle.js +3 -2
- package/dist/components/AllowedEmailDomainsManager.js +3 -2
- package/dist/components/AuthFactorRequirementCard.js +3 -2
- package/dist/components/BulkInviteCsvTab.js +3 -2
- package/dist/components/MFAComponent.js +9 -8
- package/dist/components/MfaLoginComponent.js +7 -6
- package/dist/components/PasskeysComponent.js +7 -6
- package/dist/components/ProfileComponent.js +4 -3
- package/dist/components/QrSignupManager.js +4 -3
- package/dist/components/QrSignupValidityManager.js +3 -2
- package/dist/components/RegistrationMethodsManager.js +3 -2
- package/dist/components/SecurityComponent.js +5 -4
- package/dist/components/SupportRecoveryRequestsTab.js +8 -7
- package/dist/components/UserInviteComponent.js +3 -2
- package/dist/components/UserListComponent.js +7 -6
- package/dist/i18n/authTranslations.js +420 -0
- package/dist/pages/AccountPage.js +3 -2
- package/dist/pages/LoginPage.js +10 -9
- package/dist/pages/PasswordChangePage.js +4 -3
- package/dist/pages/PasswordInvitePage.js +7 -6
- package/dist/pages/PasswordResetRequestPage.js +5 -4
- package/dist/pages/SignUpPage.js +7 -6
- package/dist/pages/SignupConfirmPage.js +7 -6
- package/dist/theme/createAppTheme.js +62 -9
- package/dist/theme/tokens.js +1 -1
- package/dist/utils/auth-errors.js +45 -8
- package/dist/utils/resolveErrorText.js +24 -0
- package/package.json +1 -1
- package/src/auth/apiClient.jsx +9 -0
- package/src/components/AccessCodeManager.jsx +5 -5
- package/src/components/AccessCodeSingleUseToggle.jsx +3 -2
- package/src/components/AllowedEmailDomainsManager.jsx +3 -2
- package/src/components/AuthFactorRequirementCard.jsx +3 -2
- package/src/components/BulkInviteCsvTab.jsx +3 -2
- package/src/components/MFAComponent.jsx +9 -8
- package/src/components/MfaLoginComponent.jsx +7 -6
- package/src/components/PasskeysComponent.jsx +7 -6
- package/src/components/ProfileComponent.jsx +4 -3
- package/src/components/QrSignupManager.jsx +4 -3
- package/src/components/QrSignupValidityManager.jsx +3 -2
- package/src/components/RegistrationMethodsManager.jsx +3 -2
- package/src/components/SecurityComponent.jsx +5 -4
- package/src/components/SupportRecoveryRequestsTab.jsx +9 -8
- package/src/components/UserInviteComponent.jsx +3 -2
- package/src/components/UserListComponent.jsx +7 -6
- package/src/i18n/authTranslations.ts +421 -0
- package/src/pages/AccountPage.jsx +3 -4
- package/src/pages/LoginPage.jsx +10 -9
- package/src/pages/PasswordChangePage.jsx +4 -3
- package/src/pages/PasswordInvitePage.jsx +7 -6
- package/src/pages/PasswordResetRequestPage.jsx +5 -4
- package/src/pages/SignUpPage.jsx +7 -6
- package/src/pages/SignupConfirmPage.jsx +7 -6
- package/src/theme/createAppTheme.js +68 -4
- package/src/theme/tokens.js +1 -1
- package/src/utils/auth-errors.js +44 -7
- package/src/utils/resolveErrorText.js +28 -0
- package/tests/AccountPage.test.jsx +27 -20
- package/tests/PasswordInvitePage.test.jsx +28 -21
- package/tests/QrSignupManager.registrationContext.test.jsx +5 -1
- package/tests/SignUpPage.test.jsx +14 -3
- package/tests/UserListComponent.test.jsx +22 -15
- package/tests/apiClientLanguage.test.js +33 -0
- package/tests/authErrors.test.js +31 -0
- package/tests/createAppThemePageContrast.test.js +108 -0
- package/tests/errorCallSiteGuard.test.js +32 -0
- package/tests/loginPageErrorResolution.test.jsx +102 -0
- package/tests/resolveErrorText.test.js +72 -0
- package/work-orders/UCM-AUTH-10.md +490 -0
- package/work-orders/UCM-THEME-14.md +3 -1
- package/work-orders/UCM-THEME-15.md +356 -0
|
@@ -15,13 +15,14 @@ import {
|
|
|
15
15
|
import { useTranslation } from 'react-i18next';
|
|
16
16
|
import { authenticateWithMFA, fetchCurrentUser, requestMfaSupportHelp } from '../auth/authApi';
|
|
17
17
|
import { loginWithPasskey } from '../utils/authService';
|
|
18
|
+
import { resolveErrorText } from '../utils/resolveErrorText';
|
|
18
19
|
|
|
19
20
|
export const MFA_LOGIN_ALERT_SX = { mb: 2 };
|
|
20
21
|
export const MFA_LOGIN_DIVIDER_SX = { my: 2 };
|
|
21
22
|
export const MFA_LOGIN_TEXT_FIELD_SX = { mb: 2 };
|
|
22
23
|
|
|
23
24
|
export function MfaLoginComponent({ availableTypes, identifier, onSuccess, onCancel }) {
|
|
24
|
-
const { t } = useTranslation();
|
|
25
|
+
const { t, i18n } = useTranslation();
|
|
25
26
|
const [code, setCode] = useState('');
|
|
26
27
|
const [submitting, setSubmitting] = useState(false);
|
|
27
28
|
const [errorKey, setErrorKey] = useState(null);
|
|
@@ -54,7 +55,7 @@ export function MfaLoginComponent({ availableTypes, identifier, onSuccess, onCan
|
|
|
54
55
|
method: isRecovery ? 'recovery_code' : 'totp',
|
|
55
56
|
});
|
|
56
57
|
} catch (err) {
|
|
57
|
-
setErrorKey(err
|
|
58
|
+
setErrorKey(resolveErrorText(i18n, err, 'Auth.MFA_AUTHENTICATE_FAILED'));
|
|
58
59
|
} finally {
|
|
59
60
|
setSubmitting(false);
|
|
60
61
|
}
|
|
@@ -73,11 +74,11 @@ export function MfaLoginComponent({ availableTypes, identifier, onSuccess, onCan
|
|
|
73
74
|
// Detailed error handling
|
|
74
75
|
if (err.code === 'Auth.PASSKEY_CANCELLED') {
|
|
75
76
|
// User cancelled - show specific key or just ignore
|
|
76
|
-
setErrorKey('Auth.PASSKEY_CANCELLED');
|
|
77
|
+
setErrorKey(t('Auth.PASSKEY_CANCELLED'));
|
|
77
78
|
} else {
|
|
78
79
|
// eslint-disable-next-line no-console
|
|
79
80
|
console.error(err);
|
|
80
|
-
setErrorKey('Auth.PASSKEY_FAILED');
|
|
81
|
+
setErrorKey(t('Auth.PASSKEY_FAILED'));
|
|
81
82
|
}
|
|
82
83
|
} finally {
|
|
83
84
|
setSubmitting(false);
|
|
@@ -101,7 +102,7 @@ export function MfaLoginComponent({ availableTypes, identifier, onSuccess, onCan
|
|
|
101
102
|
setHelpDialogOpen(false);
|
|
102
103
|
setHelpMessage('');
|
|
103
104
|
} catch (err) {
|
|
104
|
-
setErrorKey(err
|
|
105
|
+
setErrorKey(resolveErrorText(i18n, err, 'Auth.MFA_HELP_REQUEST_FAILED'));
|
|
105
106
|
} finally {
|
|
106
107
|
setSubmitting(false);
|
|
107
108
|
}
|
|
@@ -118,7 +119,7 @@ export function MfaLoginComponent({ availableTypes, identifier, onSuccess, onCan
|
|
|
118
119
|
|
|
119
120
|
{errorKey && (
|
|
120
121
|
<Alert severity="error" sx={MFA_LOGIN_ALERT_SX}>
|
|
121
|
-
{
|
|
122
|
+
{errorKey}
|
|
122
123
|
</Alert>
|
|
123
124
|
)}
|
|
124
125
|
|
|
@@ -21,12 +21,13 @@ import { useTranslation } from 'react-i18next';
|
|
|
21
21
|
import { fetchPasskeys, deletePasskey } from '../auth/authApi';
|
|
22
22
|
import { registerPasskey } from '../utils/authService';
|
|
23
23
|
import { FEATURES } from '../auth/authConfig';
|
|
24
|
+
import { resolveErrorText } from '../utils/resolveErrorText';
|
|
24
25
|
|
|
25
26
|
export const PASSKEYS_ALERT_SX = { mb: 2 };
|
|
26
27
|
export const PASSKEYS_DIVIDER_SX = { mb: 2 };
|
|
27
28
|
|
|
28
29
|
export function PasskeysComponent() {
|
|
29
|
-
const { t } = useTranslation();
|
|
30
|
+
const { t, i18n } = useTranslation();
|
|
30
31
|
|
|
31
32
|
const [passkeys, setPasskeys] = useState([]);
|
|
32
33
|
const [loading, setLoading] = useState(true);
|
|
@@ -48,7 +49,7 @@ export function PasskeysComponent() {
|
|
|
48
49
|
const data = await fetchPasskeys();
|
|
49
50
|
setPasskeys(Array.isArray(data) ? data : []);
|
|
50
51
|
} catch (err) {
|
|
51
|
-
setErrorKey(err
|
|
52
|
+
setErrorKey(resolveErrorText(i18n, err, 'Auth.PASSKEY_LIST_FAILED'));
|
|
52
53
|
} finally {
|
|
53
54
|
setLoading(false);
|
|
54
55
|
setReloading(false);
|
|
@@ -68,7 +69,7 @@ export function PasskeysComponent() {
|
|
|
68
69
|
setErrorKey(null);
|
|
69
70
|
|
|
70
71
|
if (!FEATURES.passkeysEnabled || !passkeysSupported) {
|
|
71
|
-
setErrorKey('Auth.PASSKEY_NOT_AVAILABLE_ENV');
|
|
72
|
+
setErrorKey(t('Auth.PASSKEY_NOT_AVAILABLE_ENV'));
|
|
72
73
|
return;
|
|
73
74
|
}
|
|
74
75
|
|
|
@@ -85,7 +86,7 @@ export function PasskeysComponent() {
|
|
|
85
86
|
await loadPasskeys();
|
|
86
87
|
} catch (err) {
|
|
87
88
|
// registerPasskey wirft normalisierte Errors mit .code
|
|
88
|
-
setErrorKey(err
|
|
89
|
+
setErrorKey(resolveErrorText(i18n, err, 'Auth.PASSKEY_CREATE_FAILED'));
|
|
89
90
|
} finally {
|
|
90
91
|
setCreating(false);
|
|
91
92
|
}
|
|
@@ -101,7 +102,7 @@ export function PasskeysComponent() {
|
|
|
101
102
|
setPasskeys((prev) => prev.filter((pk) => pk.id !== id));
|
|
102
103
|
setMessageKey('Auth.PASSKEY_DELETE_SUCCESS');
|
|
103
104
|
} catch (err) {
|
|
104
|
-
setErrorKey(err
|
|
105
|
+
setErrorKey(resolveErrorText(i18n, err, 'Auth.PASSKEY_DELETE_FAILED'));
|
|
105
106
|
} finally {
|
|
106
107
|
setDeletingIds((prev) => {
|
|
107
108
|
const next = new Set(prev);
|
|
@@ -146,7 +147,7 @@ export function PasskeysComponent() {
|
|
|
146
147
|
)}
|
|
147
148
|
{errorKey && (
|
|
148
149
|
<Alert severity="error" sx={PASSKEYS_ALERT_SX}>
|
|
149
|
-
{
|
|
150
|
+
{errorKey}
|
|
150
151
|
</Alert>
|
|
151
152
|
)}
|
|
152
153
|
|
|
@@ -18,6 +18,7 @@ import { useTranslation } from 'react-i18next';
|
|
|
18
18
|
// WICHTIG: Importiere den Context, um die bereits geladenen Daten zu nutzen
|
|
19
19
|
import { AuthContext } from '../auth/AuthContext';
|
|
20
20
|
import { fetchCookieStatement, fetchPrivacyStatement } from '../auth/authApi';
|
|
21
|
+
import { resolveErrorText } from '../utils/resolveErrorText';
|
|
21
22
|
|
|
22
23
|
export function ProfileComponent({
|
|
23
24
|
onSubmit,
|
|
@@ -29,7 +30,7 @@ export function ProfileComponent({
|
|
|
29
30
|
privacyStatementText = null,
|
|
30
31
|
cookieStatementText = null,
|
|
31
32
|
}) {
|
|
32
|
-
const { t } = useTranslation();
|
|
33
|
+
const { t, i18n } = useTranslation();
|
|
33
34
|
|
|
34
35
|
// WICHTIG: Wir holen den User direkt aus dem globalen State
|
|
35
36
|
// Das verhindert den doppelten Request und den ReferenceError
|
|
@@ -140,7 +141,7 @@ export function ProfileComponent({
|
|
|
140
141
|
} catch (err) {
|
|
141
142
|
// eslint-disable-next-line no-console
|
|
142
143
|
console.error("Profile update error:", err);
|
|
143
|
-
setErrorKey(err
|
|
144
|
+
setErrorKey(resolveErrorText(i18n, err, 'Auth.PROFILE_UPDATE_FAILED'));
|
|
144
145
|
} finally {
|
|
145
146
|
setSaving(false);
|
|
146
147
|
}
|
|
@@ -175,7 +176,7 @@ export function ProfileComponent({
|
|
|
175
176
|
>
|
|
176
177
|
{errorKey && (
|
|
177
178
|
<Alert severity="error">
|
|
178
|
-
{
|
|
179
|
+
{errorKey}
|
|
179
180
|
</Alert>
|
|
180
181
|
)}
|
|
181
182
|
{successKey && (
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
import { useTranslation } from 'react-i18next';
|
|
11
11
|
import { QRCodeSVG } from 'qrcode.react';
|
|
12
12
|
import { createSignupQr } from '../auth/authApi';
|
|
13
|
+
import { resolveErrorText } from '../utils/resolveErrorText';
|
|
13
14
|
|
|
14
15
|
export const QR_SIGNUP_MANAGER_ALERT_SX = { mb: 2 };
|
|
15
16
|
|
|
@@ -47,7 +48,7 @@ export function QrSignupManager({
|
|
|
47
48
|
registrationContext = null,
|
|
48
49
|
defaultMaxRedemptions = DEFAULT_MAX_REDEMPTIONS,
|
|
49
50
|
}) {
|
|
50
|
-
const { t } = useTranslation();
|
|
51
|
+
const { t, i18n } = useTranslation();
|
|
51
52
|
const qrWrapperRef = useRef(null);
|
|
52
53
|
const [busy, setBusy] = useState(false);
|
|
53
54
|
const [error, setError] = useState('');
|
|
@@ -112,7 +113,7 @@ export function QrSignupManager({
|
|
|
112
113
|
hasGeneratedRef.current = true;
|
|
113
114
|
setSuccess(t('Auth.SIGNUP_QR_CREATE_SUCCESS', 'New QR signup link created.'));
|
|
114
115
|
} catch (err) {
|
|
115
|
-
setError(
|
|
116
|
+
setError(resolveErrorText(i18n, err, 'Auth.SIGNUP_QR_CREATE_FAILED'));
|
|
116
117
|
} finally {
|
|
117
118
|
setBusy(false);
|
|
118
119
|
}
|
|
@@ -158,7 +159,7 @@ export function QrSignupManager({
|
|
|
158
159
|
setSuccess(t('Auth.SIGNUP_QR_CREATE_SUCCESS', 'New QR signup link created.'));
|
|
159
160
|
} catch (err) {
|
|
160
161
|
if (!active) return;
|
|
161
|
-
setError(
|
|
162
|
+
setError(resolveErrorText(i18n, err, 'Auth.SIGNUP_QR_CREATE_FAILED'));
|
|
162
163
|
} finally {
|
|
163
164
|
if (active) {
|
|
164
165
|
setBusy(false);
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
} from '@mui/material';
|
|
10
10
|
import { useTranslation } from 'react-i18next';
|
|
11
11
|
import { updateAuthPolicy } from '../auth/authApi';
|
|
12
|
+
import { resolveErrorText } from '../utils/resolveErrorText';
|
|
12
13
|
|
|
13
14
|
export const QR_SIGNUP_VALIDITY_ALERT_SX = { mb: 2 };
|
|
14
15
|
export const QR_SIGNUP_VALIDITY_FIELD_SX = { flex: 1 };
|
|
@@ -30,7 +31,7 @@ export function QrSignupValidityManager({
|
|
|
30
31
|
onPolicyChange,
|
|
31
32
|
canEdit = true,
|
|
32
33
|
}) {
|
|
33
|
-
const { t } = useTranslation();
|
|
34
|
+
const { t, i18n } = useTranslation();
|
|
34
35
|
const [currentExpiryDays, setCurrentExpiryDays] = useState(String(expiryDays || DEFAULT_EXPIRY_DAYS));
|
|
35
36
|
const [busy, setBusy] = useState(false);
|
|
36
37
|
const [error, setError] = useState('');
|
|
@@ -53,7 +54,7 @@ export function QrSignupValidityManager({
|
|
|
53
54
|
setSuccess(t('Auth.AUTH_POLICY_SAVE_SUCCESS', 'Authentication settings saved.'));
|
|
54
55
|
if (onPolicyChange) onPolicyChange(next);
|
|
55
56
|
} catch (err) {
|
|
56
|
-
setError(
|
|
57
|
+
setError(resolveErrorText(i18n, err, 'Auth.AUTH_POLICY_UPDATE_FAILED'));
|
|
57
58
|
} finally {
|
|
58
59
|
setBusy(false);
|
|
59
60
|
}
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
} from '@mui/material';
|
|
11
11
|
import { useTranslation } from 'react-i18next';
|
|
12
12
|
import { updateAuthPolicy } from '../auth/authApi';
|
|
13
|
+
import { resolveErrorText } from '../utils/resolveErrorText';
|
|
13
14
|
|
|
14
15
|
export const REGISTRATION_METHODS_ALERT_SX = { mb: 2 };
|
|
15
16
|
export const REGISTRATION_METHODS_INFO_SX = { mt: 2 };
|
|
@@ -31,7 +32,7 @@ export function RegistrationMethodsManager({
|
|
|
31
32
|
onPolicyChange,
|
|
32
33
|
canEdit = true,
|
|
33
34
|
}) {
|
|
34
|
-
const { t } = useTranslation();
|
|
35
|
+
const { t, i18n } = useTranslation();
|
|
35
36
|
const [policyState, setPolicyState] = useState(EMPTY_POLICY);
|
|
36
37
|
const [busyField, setBusyField] = useState('');
|
|
37
38
|
const [saveError, setSaveError] = useState('');
|
|
@@ -57,7 +58,7 @@ export function RegistrationMethodsManager({
|
|
|
57
58
|
if (onPolicyChange) onPolicyChange(next);
|
|
58
59
|
} catch (err) {
|
|
59
60
|
setPolicyState((prev) => ({ ...prev, [field]: previous }));
|
|
60
|
-
setSaveError(
|
|
61
|
+
setSaveError(resolveErrorText(i18n, err, 'Auth.AUTH_POLICY_UPDATE_FAILED'));
|
|
61
62
|
} finally {
|
|
62
63
|
setBusyField('');
|
|
63
64
|
}
|
|
@@ -14,6 +14,7 @@ import { MFAComponent } from './MFAComponent';
|
|
|
14
14
|
import { changePassword } from '../auth/authApi';
|
|
15
15
|
import { startSocialLogin } from '../utils/authService';
|
|
16
16
|
import { AuthContext } from '../auth/AuthContext';
|
|
17
|
+
import { resolveErrorText } from '../utils/resolveErrorText';
|
|
17
18
|
|
|
18
19
|
export const SECURITY_ALERT_SX = { mb: 2 };
|
|
19
20
|
export const SECURITY_DIVIDER_SX = { my: 3 };
|
|
@@ -22,7 +23,7 @@ export function SecurityComponent({
|
|
|
22
23
|
fromRecovery = false,
|
|
23
24
|
fromWeakLogin = false,
|
|
24
25
|
}) {
|
|
25
|
-
const { t } = useTranslation();
|
|
26
|
+
const { t, i18n } = useTranslation();
|
|
26
27
|
const { authMethods } = useContext(AuthContext);
|
|
27
28
|
|
|
28
29
|
const [messageKey, setMessageKey] = useState(null);
|
|
@@ -60,7 +61,7 @@ export function SecurityComponent({
|
|
|
60
61
|
callbackUrl: `${window.location.origin}/account?tab=security`,
|
|
61
62
|
});
|
|
62
63
|
} catch (err) {
|
|
63
|
-
setErrorKey(err
|
|
64
|
+
setErrorKey(resolveErrorText(i18n, err, 'Auth.SOCIAL_LOGIN_FAILED'));
|
|
64
65
|
}
|
|
65
66
|
};
|
|
66
67
|
|
|
@@ -71,7 +72,7 @@ export function SecurityComponent({
|
|
|
71
72
|
await changePassword(currentPassword, newPassword);
|
|
72
73
|
setMessageKey('Auth.RESET_PASSWORD_SUCCESS');
|
|
73
74
|
} catch (err) {
|
|
74
|
-
setErrorKey(err
|
|
75
|
+
setErrorKey(resolveErrorText(i18n, err, 'Auth.PASSWORD_CHANGE_FAILED'));
|
|
75
76
|
}
|
|
76
77
|
};
|
|
77
78
|
|
|
@@ -96,7 +97,7 @@ export function SecurityComponent({
|
|
|
96
97
|
)}
|
|
97
98
|
{errorKey && (
|
|
98
99
|
<Alert severity="error" sx={SECURITY_ALERT_SX}>
|
|
99
|
-
{
|
|
100
|
+
{errorKey}
|
|
100
101
|
</Alert>
|
|
101
102
|
)}
|
|
102
103
|
|
|
@@ -24,6 +24,7 @@ import {
|
|
|
24
24
|
} from '@mui/material';
|
|
25
25
|
import CloseIcon from '@mui/icons-material/Close';
|
|
26
26
|
import { useTranslation } from 'react-i18next';
|
|
27
|
+
import { resolveErrorText } from '../utils/resolveErrorText';
|
|
27
28
|
import { AuthContext } from '../auth/AuthContext';
|
|
28
29
|
import {
|
|
29
30
|
approveRecoveryRequest,
|
|
@@ -63,7 +64,7 @@ export function SupportRecoveryRequestsTab() {
|
|
|
63
64
|
const data = await fetchRecoveryRequests(statusFilter);
|
|
64
65
|
setRequests(Array.isArray(data) ? data : []);
|
|
65
66
|
} catch (err) {
|
|
66
|
-
setErrorKey(err
|
|
67
|
+
setErrorKey(resolveErrorText(i18n, err, 'Support.RECOVERY_REQUESTS_LOAD_FAILED'));
|
|
67
68
|
} finally {
|
|
68
69
|
setLoading(false);
|
|
69
70
|
}
|
|
@@ -88,7 +89,7 @@ export function SupportRecoveryRequestsTab() {
|
|
|
88
89
|
});
|
|
89
90
|
setAllUsers(list);
|
|
90
91
|
} catch (err) {
|
|
91
|
-
|
|
92
|
+
setAgentErrorKey(resolveErrorText(i18n, err, 'Auth.USER_LIST_FAILED'));
|
|
92
93
|
} finally {
|
|
93
94
|
setAgentLoading(false);
|
|
94
95
|
}
|
|
@@ -124,7 +125,7 @@ export function SupportRecoveryRequestsTab() {
|
|
|
124
125
|
await loadRequests();
|
|
125
126
|
closeDialog();
|
|
126
127
|
} catch (err) {
|
|
127
|
-
setErrorKey(err
|
|
128
|
+
setErrorKey(resolveErrorText(i18n, err, 'Support.RECOVERY_REQUEST_APPROVE_FAILED'));
|
|
128
129
|
}
|
|
129
130
|
};
|
|
130
131
|
|
|
@@ -136,7 +137,7 @@ export function SupportRecoveryRequestsTab() {
|
|
|
136
137
|
await loadRequests();
|
|
137
138
|
closeDialog();
|
|
138
139
|
} catch (err) {
|
|
139
|
-
setErrorKey(err
|
|
140
|
+
setErrorKey(resolveErrorText(i18n, err, 'Support.RECOVERY_REQUEST_REJECT_FAILED'));
|
|
140
141
|
}
|
|
141
142
|
};
|
|
142
143
|
|
|
@@ -167,7 +168,7 @@ export function SupportRecoveryRequestsTab() {
|
|
|
167
168
|
setSelectedAgentCandidate(null);
|
|
168
169
|
await loadUsers();
|
|
169
170
|
} catch (err) {
|
|
170
|
-
setAgentErrorKey(err
|
|
171
|
+
setAgentErrorKey(resolveErrorText(i18n, err, 'Auth.USER_SUPPORT_UPDATE_FAILED'));
|
|
171
172
|
} finally {
|
|
172
173
|
setAgentActionUserId(null);
|
|
173
174
|
}
|
|
@@ -185,7 +186,7 @@ export function SupportRecoveryRequestsTab() {
|
|
|
185
186
|
}
|
|
186
187
|
await loadUsers();
|
|
187
188
|
} catch (err) {
|
|
188
|
-
setAgentErrorKey(err
|
|
189
|
+
setAgentErrorKey(resolveErrorText(i18n, err, 'Auth.USER_SUPPORT_UPDATE_FAILED'));
|
|
189
190
|
} finally {
|
|
190
191
|
setAgentActionUserId(null);
|
|
191
192
|
}
|
|
@@ -216,7 +217,7 @@ export function SupportRecoveryRequestsTab() {
|
|
|
216
217
|
|
|
217
218
|
{agentErrorKey && (
|
|
218
219
|
<Alert severity="error" sx={SUPPORT_RECOVERY_ALERT_SX}>
|
|
219
|
-
{
|
|
220
|
+
{agentErrorKey}
|
|
220
221
|
</Alert>
|
|
221
222
|
)}
|
|
222
223
|
|
|
@@ -310,7 +311,7 @@ export function SupportRecoveryRequestsTab() {
|
|
|
310
311
|
|
|
311
312
|
{errorKey && (
|
|
312
313
|
<Alert severity="error" sx={SUPPORT_RECOVERY_ALERT_SX}>
|
|
313
|
-
{
|
|
314
|
+
{errorKey}
|
|
314
315
|
</Alert>
|
|
315
316
|
)}
|
|
316
317
|
|
|
@@ -2,6 +2,7 @@ import React, { useState } from 'react';
|
|
|
2
2
|
import { Box, TextField, Button, Typography, Alert, CircularProgress } from '@mui/material';
|
|
3
3
|
import { sendAdminInvite } from '../auth/authApi';
|
|
4
4
|
import { useTranslation } from 'react-i18next';
|
|
5
|
+
import { resolveErrorText } from '../utils/resolveErrorText';
|
|
5
6
|
|
|
6
7
|
export const USER_INVITE_ACTION_SX = {
|
|
7
8
|
minWidth: 120,
|
|
@@ -11,7 +12,7 @@ export const USER_INVITE_ACTION_SX = {
|
|
|
11
12
|
export const USER_INVITE_ALERT_SX = { mb: 2 };
|
|
12
13
|
|
|
13
14
|
export function UserInviteComponent() { // FIX: Removed apiUrl prop
|
|
14
|
-
const { t } = useTranslation();
|
|
15
|
+
const { t, i18n } = useTranslation();
|
|
15
16
|
const [inviteEmail, setInviteEmail] = useState('');
|
|
16
17
|
const [message, setMessage] = useState('');
|
|
17
18
|
const [error, setError] = useState('');
|
|
@@ -31,7 +32,7 @@ export function UserInviteComponent() { // FIX: Removed apiUrl prop
|
|
|
31
32
|
// err.message contains normalized text or code from authApi
|
|
32
33
|
// eslint-disable-next-line no-console
|
|
33
34
|
console.error('Error inviting user:', err);
|
|
34
|
-
setError(
|
|
35
|
+
setError(resolveErrorText(i18n, err, 'Auth.INVITE_FAILED'));
|
|
35
36
|
} finally {
|
|
36
37
|
setLoading(false);
|
|
37
38
|
}
|
|
@@ -24,6 +24,7 @@ import CancelIcon from '@mui/icons-material/Cancel';
|
|
|
24
24
|
import DeleteIcon from '@mui/icons-material/Delete';
|
|
25
25
|
import { useTranslation } from 'react-i18next';
|
|
26
26
|
import { fetchUsersList, deleteUser, updateUserRole } from '../auth/authApi';
|
|
27
|
+
import { resolveErrorText } from '../utils/resolveErrorText';
|
|
27
28
|
|
|
28
29
|
const DEFAULT_ROLES = ['none', 'student', 'teacher', 'admin'];
|
|
29
30
|
|
|
@@ -49,7 +50,7 @@ export function UserListComponent({
|
|
|
49
50
|
canDeleteUser = null,
|
|
50
51
|
onDeleteUser = null,
|
|
51
52
|
}) {
|
|
52
|
-
const { t } = useTranslation();
|
|
53
|
+
const { t, i18n } = useTranslation();
|
|
53
54
|
const [users, setUsers] = useState([]);
|
|
54
55
|
const [loading, setLoading] = useState(true);
|
|
55
56
|
const [error, setError] = useState(null);
|
|
@@ -75,7 +76,7 @@ export function UserListComponent({
|
|
|
75
76
|
});
|
|
76
77
|
setUsers(list);
|
|
77
78
|
} catch (err) {
|
|
78
|
-
setError(err
|
|
79
|
+
setError(resolveErrorText(i18n, err, 'Auth.USER_LIST_FAILED'));
|
|
79
80
|
} finally {
|
|
80
81
|
setLoading(false);
|
|
81
82
|
}
|
|
@@ -99,7 +100,7 @@ export function UserListComponent({
|
|
|
99
100
|
}
|
|
100
101
|
setUsers(prev => prev.filter(u => u.id !== userId));
|
|
101
102
|
} catch (err) {
|
|
102
|
-
alert(
|
|
103
|
+
alert(resolveErrorText(i18n, err, 'Auth.USER_DELETE_FAILED'));
|
|
103
104
|
}
|
|
104
105
|
};
|
|
105
106
|
|
|
@@ -120,7 +121,7 @@ export function UserListComponent({
|
|
|
120
121
|
await loadUsers();
|
|
121
122
|
alert(t('UserList.ROLE_UPDATE_SUCCESS', 'Role updated.'));
|
|
122
123
|
} catch (err) {
|
|
123
|
-
alert(
|
|
124
|
+
alert(resolveErrorText(i18n, err, 'Auth.USER_ROLE_UPDATE_FAILED'));
|
|
124
125
|
}
|
|
125
126
|
};
|
|
126
127
|
|
|
@@ -276,7 +277,7 @@ export function UserListComponent({
|
|
|
276
277
|
});
|
|
277
278
|
} catch (err) {
|
|
278
279
|
// eslint-disable-next-line no-alert
|
|
279
|
-
alert(err
|
|
280
|
+
alert(resolveErrorText(i18n, err, 'Common.OPERATION_FAILED'));
|
|
280
281
|
} finally {
|
|
281
282
|
setRowActionLoading((prev) => ({ ...prev, [actionId]: false }));
|
|
282
283
|
}
|
|
@@ -514,7 +515,7 @@ export function UserListComponent({
|
|
|
514
515
|
/>
|
|
515
516
|
</Box>
|
|
516
517
|
|
|
517
|
-
{error && <Alert severity="error" sx={USER_LIST_ALERT_SX}>{
|
|
518
|
+
{error && <Alert severity="error" sx={USER_LIST_ALERT_SX}>{error}</Alert>}
|
|
518
519
|
|
|
519
520
|
{loading && (
|
|
520
521
|
<Box sx={{ display: 'flex', justifyContent: 'center', p: 3 }}>
|