@micha.bigler/ui-core-micha 1.4.17 → 1.4.19
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/authApi.js
CHANGED
|
@@ -13,9 +13,8 @@ function bufferToBase64URL(buffer) {
|
|
|
13
13
|
return base64.replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '');
|
|
14
14
|
}
|
|
15
15
|
function serializeCredential(credential) {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
}
|
|
16
|
+
// Wir nutzen IMMER die manuelle Serialisierung, um sicherzugehen,
|
|
17
|
+
// dass alles Base64URL-kodiert ist (kompatibel mit Allauth).
|
|
19
18
|
const p = {
|
|
20
19
|
id: credential.id,
|
|
21
20
|
rawId: bufferToBase64URL(credential.rawId),
|
|
@@ -36,6 +35,7 @@ function serializeCredential(credential) {
|
|
|
36
35
|
if (credential.response.userHandle) {
|
|
37
36
|
p.response.userHandle = bufferToBase64URL(credential.response.userHandle);
|
|
38
37
|
}
|
|
38
|
+
// Extension Results direkt übernehmen, falls vorhanden
|
|
39
39
|
if (typeof credential.getClientExtensionResults === 'function') {
|
|
40
40
|
p.clientExtensionResults = credential.getClientExtensionResults();
|
|
41
41
|
}
|
|
@@ -1,74 +1,37 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
// src/components/ProfileComponent.jsx
|
|
3
|
-
import React, { useEffect, useState } from 'react';
|
|
4
|
-
import axios from 'axios';
|
|
3
|
+
import React, { useEffect, useState, useContext } from 'react';
|
|
5
4
|
import { Box, Stack, TextField, FormControlLabel, Checkbox, Button, CircularProgress, Alert, Typography, } from '@mui/material';
|
|
6
5
|
import { useTranslation } from 'react-i18next';
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
*
|
|
11
|
-
* - Loads the current user from `${USERS_BASE}/current/`
|
|
12
|
-
* - Shows basic fields (name, email, etc.)
|
|
13
|
-
* - Optional privacy/cookie checkboxes
|
|
14
|
-
* - Calls onSubmit(payload) to save changes
|
|
15
|
-
*/
|
|
16
|
-
export function ProfileComponent({ onLoad, onSubmit, submitText, // optional, falls der Host etwas Eigenes anzeigen will
|
|
17
|
-
showName = true, showPrivacy = true, showCookies = true, }) {
|
|
6
|
+
// WICHTIG: Importiere den Context, um die bereits geladenen Daten zu nutzen
|
|
7
|
+
import { AuthContext } from '../auth/AuthContext';
|
|
8
|
+
export function ProfileComponent({ onSubmit, submitText, showName = true, showPrivacy = true, showCookies = true, }) {
|
|
18
9
|
const { t } = useTranslation();
|
|
19
|
-
|
|
10
|
+
// WICHTIG: Wir holen den User direkt aus dem globalen State
|
|
11
|
+
// Das verhindert den doppelten Request und den ReferenceError
|
|
12
|
+
const { user, loading: authLoading } = useContext(AuthContext);
|
|
20
13
|
const [saving, setSaving] = useState(false);
|
|
21
14
|
const [errorKey, setErrorKey] = useState(null);
|
|
22
15
|
const [successKey, setSuccessKey] = useState(null);
|
|
23
|
-
|
|
16
|
+
// Lokaler State für das Formular
|
|
24
17
|
const [username, setUsername] = useState('');
|
|
25
18
|
const [email, setEmail] = useState('');
|
|
26
19
|
const [firstName, setFirstName] = useState('');
|
|
27
20
|
const [lastName, setLastName] = useState('');
|
|
28
21
|
const [acceptedPrivacy, setAcceptedPrivacy] = useState(false);
|
|
29
22
|
const [acceptedCookies, setAcceptedCookies] = useState(false);
|
|
30
|
-
//
|
|
23
|
+
// Synchronisiere Formular-Daten, sobald der User aus dem Context da ist
|
|
31
24
|
useEffect(() => {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
if (!mounted)
|
|
43
|
-
return;
|
|
44
|
-
const data = res.data;
|
|
45
|
-
setUserId((_a = data.id) !== null && _a !== void 0 ? _a : null);
|
|
46
|
-
setUsername((_b = data.username) !== null && _b !== void 0 ? _b : '');
|
|
47
|
-
setEmail((_c = data.email) !== null && _c !== void 0 ? _c : '');
|
|
48
|
-
setFirstName((_d = data.first_name) !== null && _d !== void 0 ? _d : '');
|
|
49
|
-
setLastName((_e = data.last_name) !== null && _e !== void 0 ? _e : '');
|
|
50
|
-
setAcceptedPrivacy(Boolean(data.accepted_privacy_statement));
|
|
51
|
-
setAcceptedCookies(Boolean(data.accepted_convenience_cookies));
|
|
52
|
-
if (onLoad) {
|
|
53
|
-
onLoad(data);
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
catch (err) {
|
|
57
|
-
if (!mounted)
|
|
58
|
-
return;
|
|
59
|
-
// Kein Leak von Backend-Texten → stabiler Code
|
|
60
|
-
setErrorKey('Auth.PROFILE_LOAD_FAILED');
|
|
61
|
-
}
|
|
62
|
-
finally {
|
|
63
|
-
if (mounted)
|
|
64
|
-
setLoading(false);
|
|
65
|
-
}
|
|
66
|
-
};
|
|
67
|
-
loadUser();
|
|
68
|
-
return () => {
|
|
69
|
-
mounted = false;
|
|
70
|
-
};
|
|
71
|
-
}, [onLoad]);
|
|
25
|
+
var _a, _b, _c, _d;
|
|
26
|
+
if (user) {
|
|
27
|
+
setUsername((_a = user.username) !== null && _a !== void 0 ? _a : '');
|
|
28
|
+
setEmail((_b = user.email) !== null && _b !== void 0 ? _b : '');
|
|
29
|
+
setFirstName((_c = user.first_name) !== null && _c !== void 0 ? _c : '');
|
|
30
|
+
setLastName((_d = user.last_name) !== null && _d !== void 0 ? _d : '');
|
|
31
|
+
setAcceptedPrivacy(Boolean(user.accepted_privacy_statement));
|
|
32
|
+
setAcceptedCookies(Boolean(user.accepted_convenience_cookies));
|
|
33
|
+
}
|
|
34
|
+
}, [user]);
|
|
72
35
|
const handleSubmit = async (event) => {
|
|
73
36
|
event.preventDefault();
|
|
74
37
|
if (!onSubmit)
|
|
@@ -79,7 +42,6 @@ showName = true, showPrivacy = true, showCookies = true, }) {
|
|
|
79
42
|
const payload = {
|
|
80
43
|
first_name: firstName,
|
|
81
44
|
last_name: lastName,
|
|
82
|
-
// Serializer-Felder sind flach (`source="profile.*"` im Backend)
|
|
83
45
|
accepted_privacy_statement: acceptedPrivacy,
|
|
84
46
|
accepted_convenience_cookies: acceptedCookies,
|
|
85
47
|
};
|
|
@@ -88,20 +50,22 @@ showName = true, showPrivacy = true, showCookies = true, }) {
|
|
|
88
50
|
setSuccessKey('Profile.UPDATE_SUCCESS');
|
|
89
51
|
}
|
|
90
52
|
catch (err) {
|
|
91
|
-
//
|
|
92
|
-
|
|
53
|
+
// eslint-disable-next-line no-console
|
|
54
|
+
console.error("Profile update error:", err);
|
|
93
55
|
setErrorKey(err.code || 'Auth.PROFILE_UPDATE_FAILED');
|
|
94
56
|
}
|
|
95
57
|
finally {
|
|
96
58
|
setSaving(false);
|
|
97
59
|
}
|
|
98
60
|
};
|
|
99
|
-
|
|
61
|
+
// Wenn der AuthContext noch initial lädt
|
|
62
|
+
if (authLoading) {
|
|
100
63
|
return (_jsx(Box, { sx: { display: 'flex', justifyContent: 'center', py: 4 }, children: _jsx(CircularProgress, {}) }));
|
|
101
64
|
}
|
|
102
|
-
//
|
|
103
|
-
|
|
104
|
-
|
|
65
|
+
// Falls kein User eingeloggt ist
|
|
66
|
+
if (!user) {
|
|
67
|
+
return (_jsx(Alert, { severity: "warning", children: t('Auth.NOT_LOGGED_IN', 'User not logged in.') }));
|
|
68
|
+
}
|
|
105
69
|
const submitLabel = submitText || t('Profile.SAVE_BUTTON');
|
|
106
70
|
const submitLabelLoading = t('Profile.SAVE_BUTTON_LOADING');
|
|
107
71
|
return (_jsxs(Box, { component: "form", onSubmit: handleSubmit, sx: { maxWidth: 600, display: 'flex', flexDirection: 'column', gap: 2 }, children: [errorKey && (_jsx(Alert, { severity: "error", children: t(errorKey) })), successKey && (_jsx(Alert, { severity: "success", children: t(successKey) })), _jsxs(Stack, { spacing: 2, children: [_jsx(TextField, { label: t('Profile.USERNAME_LABEL'), value: username, fullWidth: true, disabled: true }), _jsx(TextField, { label: t('Auth.EMAIL_LABEL'), type: "email", value: email, fullWidth: true, disabled: true })] }), showName && (_jsxs(Stack, { spacing: 2, direction: { xs: 'column', sm: 'row' }, children: [_jsx(TextField, { label: t('Profile.FIRST_NAME_LABEL'), value: firstName, onChange: (e) => setFirstName(e.target.value), fullWidth: true }), _jsx(TextField, { label: t('Profile.LAST_NAME_LABEL'), value: lastName, onChange: (e) => setLastName(e.target.value), fullWidth: true })] })), (showPrivacy || showCookies) && (_jsxs(Box, { sx: { mt: 1 }, children: [_jsx(Typography, { variant: "subtitle1", gutterBottom: true, children: t('Profile.PRIVACY_SECTION_TITLE') }), _jsxs(Stack, { spacing: 1, children: [showPrivacy && (_jsx(FormControlLabel, { control: _jsx(Checkbox, { checked: acceptedPrivacy, onChange: (e) => setAcceptedPrivacy(e.target.checked) }), label: t('Profile.ACCEPT_PRIVACY_LABEL') })), showCookies && (_jsx(FormControlLabel, { control: _jsx(Checkbox, { checked: acceptedCookies, onChange: (e) => setAcceptedCookies(e.target.checked) }), label: t('Profile.ACCEPT_COOKIES_LABEL') }))] })] })), _jsx(Box, { sx: { mt: 2 }, children: _jsx(Button, { type: "submit", variant: "contained", disabled: saving, children: saving ? submitLabelLoading : submitLabel }) })] }));
|
package/package.json
CHANGED
package/src/auth/authApi.jsx
CHANGED
|
@@ -15,10 +15,8 @@ function bufferToBase64URL(buffer) {
|
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
function serializeCredential(credential) {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
}
|
|
21
|
-
|
|
18
|
+
// Wir nutzen IMMER die manuelle Serialisierung, um sicherzugehen,
|
|
19
|
+
// dass alles Base64URL-kodiert ist (kompatibel mit Allauth).
|
|
22
20
|
const p = {
|
|
23
21
|
id: credential.id,
|
|
24
22
|
rawId: bufferToBase64URL(credential.rawId),
|
|
@@ -43,7 +41,8 @@ function serializeCredential(credential) {
|
|
|
43
41
|
if (credential.response.userHandle) {
|
|
44
42
|
p.response.userHandle = bufferToBase64URL(credential.response.userHandle);
|
|
45
43
|
}
|
|
46
|
-
|
|
44
|
+
|
|
45
|
+
// Extension Results direkt übernehmen, falls vorhanden
|
|
47
46
|
if (typeof credential.getClientExtensionResults === 'function') {
|
|
48
47
|
p.clientExtensionResults = credential.getClientExtensionResults();
|
|
49
48
|
}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
// src/components/ProfileComponent.jsx
|
|
2
|
-
import React, { useEffect, useState } from 'react';
|
|
3
|
-
import axios from 'axios';
|
|
2
|
+
import React, { useEffect, useState, useContext } from 'react';
|
|
4
3
|
import {
|
|
5
4
|
Box,
|
|
6
5
|
Stack,
|
|
@@ -13,83 +12,45 @@ import {
|
|
|
13
12
|
Typography,
|
|
14
13
|
} from '@mui/material';
|
|
15
14
|
import { useTranslation } from 'react-i18next';
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
* ProfileComponent
|
|
20
|
-
*
|
|
21
|
-
* - Loads the current user from `${USERS_BASE}/current/`
|
|
22
|
-
* - Shows basic fields (name, email, etc.)
|
|
23
|
-
* - Optional privacy/cookie checkboxes
|
|
24
|
-
* - Calls onSubmit(payload) to save changes
|
|
25
|
-
*/
|
|
15
|
+
// WICHTIG: Importiere den Context, um die bereits geladenen Daten zu nutzen
|
|
16
|
+
import { AuthContext } from '../auth/AuthContext';
|
|
17
|
+
|
|
26
18
|
export function ProfileComponent({
|
|
27
|
-
onLoad,
|
|
28
19
|
onSubmit,
|
|
29
|
-
submitText,
|
|
20
|
+
submitText,
|
|
30
21
|
showName = true,
|
|
31
22
|
showPrivacy = true,
|
|
32
23
|
showCookies = true,
|
|
33
24
|
}) {
|
|
34
25
|
const { t } = useTranslation();
|
|
26
|
+
|
|
27
|
+
// WICHTIG: Wir holen den User direkt aus dem globalen State
|
|
28
|
+
// Das verhindert den doppelten Request und den ReferenceError
|
|
29
|
+
const { user, loading: authLoading } = useContext(AuthContext);
|
|
35
30
|
|
|
36
|
-
const [loading, setLoading] = useState(true);
|
|
37
31
|
const [saving, setSaving] = useState(false);
|
|
38
|
-
|
|
39
32
|
const [errorKey, setErrorKey] = useState(null);
|
|
40
33
|
const [successKey, setSuccessKey] = useState(null);
|
|
41
34
|
|
|
42
|
-
|
|
35
|
+
// Lokaler State für das Formular
|
|
43
36
|
const [username, setUsername] = useState('');
|
|
44
37
|
const [email, setEmail] = useState('');
|
|
45
|
-
|
|
46
38
|
const [firstName, setFirstName] = useState('');
|
|
47
39
|
const [lastName, setLastName] = useState('');
|
|
48
|
-
|
|
49
40
|
const [acceptedPrivacy, setAcceptedPrivacy] = useState(false);
|
|
50
41
|
const [acceptedCookies, setAcceptedCookies] = useState(false);
|
|
51
42
|
|
|
52
|
-
//
|
|
43
|
+
// Synchronisiere Formular-Daten, sobald der User aus dem Context da ist
|
|
53
44
|
useEffect(() => {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
withCredentials: true,
|
|
64
|
-
});
|
|
65
|
-
if (!mounted) return;
|
|
66
|
-
|
|
67
|
-
const data = res.data;
|
|
68
|
-
setUserId(data.id ?? null);
|
|
69
|
-
setUsername(data.username ?? '');
|
|
70
|
-
setEmail(data.email ?? '');
|
|
71
|
-
setFirstName(data.first_name ?? '');
|
|
72
|
-
setLastName(data.last_name ?? '');
|
|
73
|
-
setAcceptedPrivacy(Boolean(data.accepted_privacy_statement));
|
|
74
|
-
setAcceptedCookies(Boolean(data.accepted_convenience_cookies));
|
|
75
|
-
|
|
76
|
-
if (onLoad) {
|
|
77
|
-
onLoad(data);
|
|
78
|
-
}
|
|
79
|
-
} catch (err) {
|
|
80
|
-
if (!mounted) return;
|
|
81
|
-
// Kein Leak von Backend-Texten → stabiler Code
|
|
82
|
-
setErrorKey('Auth.PROFILE_LOAD_FAILED');
|
|
83
|
-
} finally {
|
|
84
|
-
if (mounted) setLoading(false);
|
|
85
|
-
}
|
|
86
|
-
};
|
|
87
|
-
|
|
88
|
-
loadUser();
|
|
89
|
-
return () => {
|
|
90
|
-
mounted = false;
|
|
91
|
-
};
|
|
92
|
-
}, [onLoad]);
|
|
45
|
+
if (user) {
|
|
46
|
+
setUsername(user.username ?? '');
|
|
47
|
+
setEmail(user.email ?? '');
|
|
48
|
+
setFirstName(user.first_name ?? '');
|
|
49
|
+
setLastName(user.last_name ?? '');
|
|
50
|
+
setAcceptedPrivacy(Boolean(user.accepted_privacy_statement));
|
|
51
|
+
setAcceptedCookies(Boolean(user.accepted_convenience_cookies));
|
|
52
|
+
}
|
|
53
|
+
}, [user]);
|
|
93
54
|
|
|
94
55
|
const handleSubmit = async (event) => {
|
|
95
56
|
event.preventDefault();
|
|
@@ -102,7 +63,6 @@ export function ProfileComponent({
|
|
|
102
63
|
const payload = {
|
|
103
64
|
first_name: firstName,
|
|
104
65
|
last_name: lastName,
|
|
105
|
-
// Serializer-Felder sind flach (`source="profile.*"` im Backend)
|
|
106
66
|
accepted_privacy_statement: acceptedPrivacy,
|
|
107
67
|
accepted_convenience_cookies: acceptedCookies,
|
|
108
68
|
};
|
|
@@ -111,15 +71,16 @@ export function ProfileComponent({
|
|
|
111
71
|
await onSubmit(payload);
|
|
112
72
|
setSuccessKey('Profile.UPDATE_SUCCESS');
|
|
113
73
|
} catch (err) {
|
|
114
|
-
//
|
|
115
|
-
|
|
74
|
+
// eslint-disable-next-line no-console
|
|
75
|
+
console.error("Profile update error:", err);
|
|
116
76
|
setErrorKey(err.code || 'Auth.PROFILE_UPDATE_FAILED');
|
|
117
77
|
} finally {
|
|
118
78
|
setSaving(false);
|
|
119
79
|
}
|
|
120
80
|
};
|
|
121
81
|
|
|
122
|
-
|
|
82
|
+
// Wenn der AuthContext noch initial lädt
|
|
83
|
+
if (authLoading) {
|
|
123
84
|
return (
|
|
124
85
|
<Box sx={{ display: 'flex', justifyContent: 'center', py: 4 }}>
|
|
125
86
|
<CircularProgress />
|
|
@@ -127,9 +88,15 @@ export function ProfileComponent({
|
|
|
127
88
|
);
|
|
128
89
|
}
|
|
129
90
|
|
|
130
|
-
//
|
|
131
|
-
|
|
132
|
-
|
|
91
|
+
// Falls kein User eingeloggt ist
|
|
92
|
+
if (!user) {
|
|
93
|
+
return (
|
|
94
|
+
<Alert severity="warning">
|
|
95
|
+
{t('Auth.NOT_LOGGED_IN', 'User not logged in.')}
|
|
96
|
+
</Alert>
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
|
|
133
100
|
const submitLabel = submitText || t('Profile.SAVE_BUTTON');
|
|
134
101
|
const submitLabelLoading = t('Profile.SAVE_BUTTON_LOADING');
|
|
135
102
|
|
|
@@ -150,7 +117,7 @@ export function ProfileComponent({
|
|
|
150
117
|
</Alert>
|
|
151
118
|
)}
|
|
152
119
|
|
|
153
|
-
{/*
|
|
120
|
+
{/* Read-Only Felder */}
|
|
154
121
|
<Stack spacing={2}>
|
|
155
122
|
<TextField
|
|
156
123
|
label={t('Profile.USERNAME_LABEL')}
|
|
@@ -167,7 +134,7 @@ export function ProfileComponent({
|
|
|
167
134
|
/>
|
|
168
135
|
</Stack>
|
|
169
136
|
|
|
170
|
-
{/*
|
|
137
|
+
{/* Editierbare Felder */}
|
|
171
138
|
{showName && (
|
|
172
139
|
<Stack spacing={2} direction={{ xs: 'column', sm: 'row' }}>
|
|
173
140
|
<TextField
|
|
@@ -185,7 +152,6 @@ export function ProfileComponent({
|
|
|
185
152
|
</Stack>
|
|
186
153
|
)}
|
|
187
154
|
|
|
188
|
-
{/* Privacy / Cookies */}
|
|
189
155
|
{(showPrivacy || showCookies) && (
|
|
190
156
|
<Box sx={{ mt: 1 }}>
|
|
191
157
|
<Typography variant="subtitle1" gutterBottom>
|
|
@@ -233,4 +199,4 @@ export function ProfileComponent({
|
|
|
233
199
|
);
|
|
234
200
|
}
|
|
235
201
|
|
|
236
|
-
export default ProfileComponent;
|
|
202
|
+
export default ProfileComponent;
|