@thunderid/react 0.13.0 → 0.13.1
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/api/getUsersMeMeta.d.ts +22 -0
- package/dist/api/getUsersMeMeta.d.ts.map +1 -0
- package/dist/api/getUsersMeMeta.js +32 -0
- package/dist/api/getUsersMeMeta.js.map +1 -0
- package/dist/cjs/api/getUsersMeMeta.cjs +34 -0
- package/dist/cjs/api/getUsersMeMeta.cjs.map +1 -0
- package/dist/cjs/components/adapters/GoogleButton.cjs +425 -31
- package/dist/cjs/components/adapters/GoogleButton.cjs.map +1 -1
- package/dist/cjs/components/presentation/UserProfile/BaseUserProfile.cjs +91 -35
- package/dist/cjs/components/presentation/UserProfile/BaseUserProfile.cjs.map +1 -1
- package/dist/cjs/components/presentation/UserProfile/UserProfile.cjs +4 -2
- package/dist/cjs/components/presentation/UserProfile/UserProfile.cjs.map +1 -1
- package/dist/cjs/contexts/ThunderID/ThunderIDContext.cjs +1 -0
- package/dist/cjs/contexts/ThunderID/ThunderIDContext.cjs.map +1 -1
- package/dist/cjs/contexts/ThunderID/ThunderIDProvider.cjs +32 -13
- package/dist/cjs/contexts/ThunderID/ThunderIDProvider.cjs.map +1 -1
- package/dist/cjs/contexts/User/UserContext.cjs +2 -1
- package/dist/cjs/contexts/User/UserContext.cjs.map +1 -1
- package/dist/cjs/contexts/User/UserProvider.cjs +5 -3
- package/dist/cjs/contexts/User/UserProvider.cjs.map +1 -1
- package/dist/cjs/index.cjs +2 -0
- package/dist/components/adapters/GoogleButton.d.ts.map +1 -1
- package/dist/components/adapters/GoogleButton.js +425 -31
- package/dist/components/adapters/GoogleButton.js.map +1 -1
- package/dist/components/presentation/UserProfile/BaseUserProfile.d.ts +2 -1
- package/dist/components/presentation/UserProfile/BaseUserProfile.d.ts.map +1 -1
- package/dist/components/presentation/UserProfile/BaseUserProfile.js +91 -35
- package/dist/components/presentation/UserProfile/BaseUserProfile.js.map +1 -1
- package/dist/components/presentation/UserProfile/UserProfile.d.ts.map +1 -1
- package/dist/components/presentation/UserProfile/UserProfile.js +5 -3
- package/dist/components/presentation/UserProfile/UserProfile.js.map +1 -1
- package/dist/contexts/ThunderID/ThunderIDContext.d.ts +5 -1
- package/dist/contexts/ThunderID/ThunderIDContext.d.ts.map +1 -1
- package/dist/contexts/ThunderID/ThunderIDContext.js +1 -0
- package/dist/contexts/ThunderID/ThunderIDContext.js.map +1 -1
- package/dist/contexts/ThunderID/ThunderIDProvider.d.ts.map +1 -1
- package/dist/contexts/ThunderID/ThunderIDProvider.js +32 -13
- package/dist/contexts/ThunderID/ThunderIDProvider.js.map +1 -1
- package/dist/contexts/User/UserContext.d.ts +2 -1
- package/dist/contexts/User/UserContext.d.ts.map +1 -1
- package/dist/contexts/User/UserContext.js +2 -1
- package/dist/contexts/User/UserContext.js.map +1 -1
- package/dist/contexts/User/UserProvider.d.ts +5 -2
- package/dist/contexts/User/UserProvider.d.ts.map +1 -1
- package/dist/contexts/User/UserProvider.js +5 -3
- package/dist/contexts/User/UserProvider.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/package.json +2 -2
|
@@ -53,12 +53,14 @@ const readonlyFields = [
|
|
|
53
53
|
"id",
|
|
54
54
|
"isReadOnly",
|
|
55
55
|
"ouId",
|
|
56
|
-
"username"
|
|
56
|
+
"username",
|
|
57
|
+
"sub"
|
|
57
58
|
];
|
|
58
|
-
const BaseUserProfile = ({ fallback = null, className = "", cardLayout = true, profile, flattenedProfile, mode = "inline", title, attributeMapping = {}, editable = true, onOpenChange, onUpdate, open = false, error = null, isLoading = false, preferences, showFields = [], hideFields = [], displayNameAttributes = [] }) => {
|
|
59
|
+
const BaseUserProfile = ({ fallback = null, className = "", cardLayout = true, profile, flattenedProfile, userSchema, mode = "inline", title, attributeMapping = {}, editable = true, onOpenChange, onUpdate, open = false, error = null, isLoading = false, preferences, showFields = [], hideFields = [], displayNameAttributes = [] }) => {
|
|
59
60
|
const { theme, colorScheme } = require_useTheme.default();
|
|
60
61
|
const [editedUser, setEditedUser] = (0, react.useState)(flattenedProfile || profile);
|
|
61
62
|
const [editingFields, setEditingFields] = (0, react.useState)({});
|
|
63
|
+
const [fieldErrors, setFieldErrors] = (0, react.useState)({});
|
|
62
64
|
const { t } = require_useTranslation.default(preferences?.i18n);
|
|
63
65
|
(0, react.useEffect)(() => {
|
|
64
66
|
const nextUser = flattenedProfile ?? profile;
|
|
@@ -77,15 +79,11 @@ const BaseUserProfile = ({ fallback = null, className = "", cardLayout = true, p
|
|
|
77
79
|
editingFields
|
|
78
80
|
]);
|
|
79
81
|
/**
|
|
80
|
-
* Determines if a field should be visible based on showFields, hideFields, and fieldsToSkip arrays.
|
|
81
|
-
*
|
|
82
|
-
* 1. fieldsToSkip (always hidden) - highest priority
|
|
83
|
-
* 2. hideFields (explicitly hidden)
|
|
84
|
-
* 3. showFields (explicitly shown, if array is not empty)
|
|
85
|
-
* 4. Default behavior (show all fields not in fieldsToSkip)
|
|
82
|
+
* Determines if a field should be visible based on showFields, hideFields, and fallback fieldsToSkip arrays.
|
|
83
|
+
* When isSchemaBased is true, fieldsToSkip is bypassed so schema-defined fields render dynamically.
|
|
86
84
|
*/
|
|
87
|
-
const shouldShowField = (0, react.useCallback)((fieldName) => {
|
|
88
|
-
if (fieldsToSkip.includes(fieldName)) return false;
|
|
85
|
+
const shouldShowField = (0, react.useCallback)((fieldName, isSchemaBased = false) => {
|
|
86
|
+
if (!isSchemaBased && fieldsToSkip.includes(fieldName)) return false;
|
|
89
87
|
if (hideFields.length > 0 && hideFields.includes(fieldName)) return false;
|
|
90
88
|
if (showFields.length > 0) return showFields.includes(fieldName);
|
|
91
89
|
return true;
|
|
@@ -111,12 +109,11 @@ const BaseUserProfile = ({ fallback = null, className = "", cardLayout = true, p
|
|
|
111
109
|
const { type, displayName, description, name } = schema;
|
|
112
110
|
const fieldLabel = displayName || description || name || "value";
|
|
113
111
|
switch (type) {
|
|
114
|
-
case "DATE_TIME":
|
|
115
|
-
case "
|
|
116
|
-
|
|
117
|
-
default: return `Enter your ${fieldLabel.toLowerCase()}`;
|
|
112
|
+
case "DATE_TIME":
|
|
113
|
+
case "STRING":
|
|
114
|
+
default: return t("elements.fields.generic.placeholder", { field: fieldLabel.toLowerCase() });
|
|
118
115
|
}
|
|
119
|
-
}, []);
|
|
116
|
+
}, [t]);
|
|
120
117
|
const formatLabel = (0, react.useCallback)((key) => key.split(/(?=[A-Z])|_/).map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join(" "), []);
|
|
121
118
|
const styles = require_BaseUserProfile_styles.default(theme, colorScheme);
|
|
122
119
|
const ObjectDisplay = ({ data }) => {
|
|
@@ -147,11 +144,36 @@ const BaseUserProfile = ({ fallback = null, className = "", cardLayout = true, p
|
|
|
147
144
|
const handleFieldSave = (0, react.useCallback)((schema) => {
|
|
148
145
|
if (!onUpdate || !schema.name) return;
|
|
149
146
|
const fieldName = schema.name;
|
|
147
|
+
const currentUser$1 = flattenedProfile || profile;
|
|
150
148
|
let fieldValue;
|
|
151
149
|
if (editedUser && fieldName && editedUser[fieldName] !== void 0) fieldValue = editedUser[fieldName];
|
|
150
|
+
else if (currentUser$1?.attributes?.[fieldName] !== void 0) fieldValue = currentUser$1.attributes[fieldName];
|
|
152
151
|
else if (flattenedProfile?.[fieldName] !== void 0) fieldValue = flattenedProfile[fieldName];
|
|
153
152
|
else fieldValue = "";
|
|
154
153
|
if (Array.isArray(fieldValue)) fieldValue = fieldValue.filter((v) => v !== void 0 && v !== null && v !== "");
|
|
154
|
+
const strVal = String(fieldValue ?? "").trim();
|
|
155
|
+
schema.displayName || schema.name && (0, __thunderid_browser.startCase)(schema.name);
|
|
156
|
+
if (schema.required && !strVal) {
|
|
157
|
+
setFieldErrors((prev) => ({
|
|
158
|
+
...prev,
|
|
159
|
+
[fieldName]: t("validations.required.field.error")
|
|
160
|
+
}));
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
if (schema.regex && strVal) try {
|
|
164
|
+
if (!new RegExp(schema.regex).test(strVal)) {
|
|
165
|
+
setFieldErrors((prev) => ({
|
|
166
|
+
...prev,
|
|
167
|
+
[fieldName]: t("validation.pattern.invalid")
|
|
168
|
+
}));
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
} catch (e) {}
|
|
172
|
+
setFieldErrors((prev) => {
|
|
173
|
+
const next = { ...prev };
|
|
174
|
+
delete next[fieldName];
|
|
175
|
+
return next;
|
|
176
|
+
});
|
|
155
177
|
let payload = {};
|
|
156
178
|
set(payload, fieldName, fieldValue);
|
|
157
179
|
onUpdate(payload);
|
|
@@ -159,15 +181,23 @@ const BaseUserProfile = ({ fallback = null, className = "", cardLayout = true, p
|
|
|
159
181
|
}, [
|
|
160
182
|
editedUser,
|
|
161
183
|
flattenedProfile,
|
|
184
|
+
profile,
|
|
162
185
|
onUpdate,
|
|
163
|
-
toggleFieldEdit
|
|
186
|
+
toggleFieldEdit,
|
|
187
|
+
t
|
|
164
188
|
]);
|
|
165
189
|
const handleFieldCancel = (0, react.useCallback)((fieldName) => {
|
|
166
190
|
const currentUser$1 = flattenedProfile || profile;
|
|
191
|
+
const initialVal = currentUser$1?.attributes?.[fieldName] ?? currentUser$1?.[fieldName];
|
|
167
192
|
setEditedUser((prev) => ({
|
|
168
193
|
...prev,
|
|
169
|
-
[fieldName]:
|
|
194
|
+
[fieldName]: initialVal
|
|
170
195
|
}));
|
|
196
|
+
setFieldErrors((prev) => {
|
|
197
|
+
const next = { ...prev };
|
|
198
|
+
delete next[fieldName];
|
|
199
|
+
return next;
|
|
200
|
+
});
|
|
171
201
|
toggleFieldEdit(fieldName);
|
|
172
202
|
}, [
|
|
173
203
|
flattenedProfile,
|
|
@@ -341,13 +371,21 @@ const BaseUserProfile = ({ fallback = null, className = "", cardLayout = true, p
|
|
|
341
371
|
if (!(hasValue || isFieldEditing || editable && schema.mutability === "READ_WRITE")) return null;
|
|
342
372
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
343
373
|
className: styles.field,
|
|
344
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.
|
|
374
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
345
375
|
className: styles.fieldInner,
|
|
346
|
-
children: renderSchemaField(schema, isFieldEditing, (value) => {
|
|
376
|
+
children: [renderSchemaField(schema, isFieldEditing, (value) => {
|
|
347
377
|
const tempEditedUser = { ...editedUser };
|
|
348
378
|
tempEditedUser[schema.name] = value;
|
|
349
379
|
setEditedUser(tempEditedUser);
|
|
350
|
-
}, () => toggleFieldEdit(schema.name))
|
|
380
|
+
}, () => toggleFieldEdit(schema.name)), fieldErrors[schema.name] && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
381
|
+
style: {
|
|
382
|
+
color: "#d32f2f",
|
|
383
|
+
fontSize: "0.8rem",
|
|
384
|
+
marginTop: "4px",
|
|
385
|
+
fontWeight: 500
|
|
386
|
+
},
|
|
387
|
+
children: fieldErrors[schema.name]
|
|
388
|
+
})]
|
|
351
389
|
}), editable && schema.mutability !== "READ_ONLY" && !isReadonlyField && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
352
390
|
className: styles.fieldActions,
|
|
353
391
|
children: [isFieldEditing && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_Button.default, {
|
|
@@ -377,13 +415,38 @@ const BaseUserProfile = ({ fallback = null, className = "", cardLayout = true, p
|
|
|
377
415
|
if (!profile && !flattenedProfile) return fallback;
|
|
378
416
|
const containerClasses = require_emotion.cx(styles.root, cardLayout ? styles.card : "", (0, __thunderid_browser.withVendorCSSClassPrefix)("user-profile"), className);
|
|
379
417
|
const currentUser = flattenedProfile || profile;
|
|
380
|
-
const
|
|
418
|
+
const renderProfileContent = () => {
|
|
381
419
|
if (!currentUser) return null;
|
|
382
420
|
const displayName = require_getDisplayName.default(mergedMappings, profile, displayNameAttributes);
|
|
383
|
-
|
|
421
|
+
let schemaItems = [];
|
|
422
|
+
if (userSchema && typeof userSchema === "object" && Object.keys(userSchema).length > 0) schemaItems = Object.entries(userSchema).filter(([key, metaAttr]) => {
|
|
423
|
+
if (metaAttr?.credential) return false;
|
|
424
|
+
return shouldShowField(key, true);
|
|
425
|
+
}).map(([key, metaAttr]) => {
|
|
426
|
+
const val = editedUser?.[key] ?? currentUser?.attributes?.[key] ?? currentUser?.[key] ?? "";
|
|
427
|
+
const isReadonly = metaAttr.readOnly === true || metaAttr.mutability === "READ_ONLY" || readonlyFields.includes(key);
|
|
428
|
+
return {
|
|
429
|
+
name: key,
|
|
430
|
+
displayName: metaAttr.displayName ?? (key ? (0, __thunderid_browser.startCase)(key) : ""),
|
|
431
|
+
type: (metaAttr.type ?? "STRING").toUpperCase(),
|
|
432
|
+
regex: metaAttr.regex,
|
|
433
|
+
required: !!metaAttr.required,
|
|
434
|
+
mutability: isReadonly ? "READ_ONLY" : "READ_WRITE",
|
|
435
|
+
value: val
|
|
436
|
+
};
|
|
437
|
+
});
|
|
438
|
+
else schemaItems = Object.entries(currentUser).filter(([key, value]) => {
|
|
384
439
|
if (!shouldShowField(key)) return false;
|
|
385
440
|
return value !== void 0 && value !== "" && value !== null;
|
|
386
|
-
}).sort(([a], [b]) => a.localeCompare(b))
|
|
441
|
+
}).sort(([a], [b]) => a.localeCompare(b)).map(([key, value]) => {
|
|
442
|
+
const isReadonly = readonlyFields.includes(key);
|
|
443
|
+
return {
|
|
444
|
+
name: key,
|
|
445
|
+
displayName: (0, __thunderid_browser.startCase)(key),
|
|
446
|
+
mutability: isReadonly ? "READ_ONLY" : "READ_WRITE",
|
|
447
|
+
value
|
|
448
|
+
};
|
|
449
|
+
});
|
|
387
450
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
388
451
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
389
452
|
className: styles.profileSummary,
|
|
@@ -408,17 +471,10 @@ const BaseUserProfile = ({ fallback = null, className = "", cardLayout = true, p
|
|
|
408
471
|
]
|
|
409
472
|
}),
|
|
410
473
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_Divider.default, {}),
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
value
|
|
416
|
-
};
|
|
417
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
418
|
-
className: styles.info,
|
|
419
|
-
children: renderUserInfo(schemaWithValue)
|
|
420
|
-
}, key);
|
|
421
|
-
})
|
|
474
|
+
schemaItems.map((schemaWithValue) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
475
|
+
className: styles.info,
|
|
476
|
+
children: renderUserInfo(schemaWithValue)
|
|
477
|
+
}, schemaWithValue.name))
|
|
422
478
|
] });
|
|
423
479
|
};
|
|
424
480
|
const profileContent = /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(require_Card.default, {
|
|
@@ -429,7 +485,7 @@ const BaseUserProfile = ({ fallback = null, className = "", cardLayout = true, p
|
|
|
429
485
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_Alert.default.Title, { children: t("errors.heading") || "Error" }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_Alert.default.Description, { children: error })]
|
|
430
486
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
431
487
|
className: styles.infoContainer,
|
|
432
|
-
children:
|
|
488
|
+
children: renderProfileContent()
|
|
433
489
|
})]
|
|
434
490
|
});
|
|
435
491
|
if (mode === "popup") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_Dialog.default, {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BaseUserProfile.cjs","names":["fieldsToSkip: string[]","readonlyFields: string[]","BaseUserProfile: FC<BaseUserProfileProps>","useTheme","useTranslation","shouldShowField: any","toggleFieldEdit: any","getFieldPlaceholder: any","fieldLabel: any","formatLabel: any","styles: any","useStyles","ObjectDisplay: FC<{data: unknown}>","keys: any","current: any","i: any","key: any","handleFieldSave: any","fieldName: string","fieldValue: any","payload: Record<string, any>","handleFieldCancel: any","currentUser: any","currentUser","mergedMappings: Record<string, string | string[]>","label: any","displayValue: string","displayValue","hasValues: any","isEditable: any","isEditable","currentValue: any","fieldValues: string[]","MultiInput","cx","Button","commonProps: any","field: ReactElement","TextField","DatePicker","Checkbox","hasValue: any","isFieldEditing: any","isReadonlyField: any","tempEditedUser: any","containerClasses: any","displayName: any","getDisplayName","profileEntries: any","Avatar","getMappedUserProfileValue","Typography","Divider","schemaWithValue: any","profileContent: any","CardPrimitive","AlertPrimitive","DialogPrimitive"],"sources":["../../../../../src/components/presentation/UserProfile/BaseUserProfile.tsx"],"sourcesContent":["// Copyright 2025 The ThunderID Authors\n// SPDX-License-Identifier: Apache-2.0\n\nimport {User, withVendorCSSClassPrefix, bem, Preferences, startCase} from '@thunderid/browser';\nimport {FC, ReactElement, useState, useCallback, useEffect} from 'react';\nimport useStyles from './BaseUserProfile.styles';\nimport useTheme from '../../../contexts/Theme/useTheme';\nimport useTranslation from '../../../hooks/useTranslation';\nimport {cx} from '../../../styles/emotion';\nimport getDisplayName from '../../../utils/getDisplayName';\nimport getMappedUserProfileValue from '../../../utils/getMappedUserProfileValue';\nimport AlertPrimitive from '../../primitives/Alert/Alert';\nimport {Avatar} from '../../primitives/Avatar/Avatar';\nimport Button from '../../primitives/Button/Button';\nimport CardPrimitive from '../../primitives/Card/Card';\nimport Checkbox from '../../primitives/Checkbox/Checkbox';\nimport DatePicker from '../../primitives/DatePicker/DatePicker';\nimport DialogPrimitive from '../../primitives/Dialog/Dialog';\nimport Divider from '../../primitives/Divider/Divider';\nimport MultiInput from '../../primitives/MultiInput/MultiInput';\nimport TextField from '../../primitives/TextField/TextField';\nimport Typography from '../../primitives/Typography/Typography';\n\ninterface ExtendedFlatSchema {\n path?: string;\n schemaId?: string;\n}\n\ninterface Schema extends ExtendedFlatSchema {\n caseExact?: boolean;\n description?: string;\n displayName?: string;\n displayOrder?: string;\n multiValued?: boolean;\n mutability?: string;\n name?: string;\n required?: boolean;\n returned?: string;\n subAttributes?: Schema[];\n type?: string;\n uniqueness?: string;\n value?: any;\n}\n\nexport interface BaseUserProfileProps {\n attributeMapping?: {\n [key: string]: string | string[] | undefined;\n firstName?: string | string[];\n lastName?: string | string[];\n picture?: string | string[];\n username?: string | string[];\n };\n cardLayout?: boolean;\n className?: string;\n displayNameAttributes?: string[];\n editable?: boolean;\n error?: string | null;\n fallback?: ReactElement | null;\n flattenedProfile?: User;\n hideFields?: string[];\n isLoading?: boolean;\n mode?: 'inline' | 'popup';\n onOpenChange?: (open: boolean) => void;\n onUpdate?: (payload: any) => Promise<void>;\n open?: boolean;\n /**\n * Component-level preferences to override global i18n and theme settings.\n * Preferences are deep-merged with global ones, with component preferences\n * taking precedence. Affects this component and all its descendants.\n */\n preferences?: Preferences;\n profile?: User;\n showFields?: string[];\n\n title?: string;\n}\n\n// Fields to skip based on schema.name\nconst fieldsToSkip: string[] = [\n 'roles.default',\n 'active',\n 'groups',\n 'accountLocked',\n 'accountDisabled',\n 'oneTimePassword',\n 'userSourceId',\n 'idpType',\n 'localCredentialExists',\n 'active',\n 'ResourceType',\n 'ExternalID',\n 'MetaData',\n 'verifiedMobileNumbers',\n 'verifiedEmailAddresses',\n 'phoneNumbers.mobile',\n 'emailAddresses',\n 'preferredMFAOption',\n 'attributes',\n 'picture',\n 'isReadOnly',\n 'isReadonly',\n];\n\n// Fields that should be readonly\nconst readonlyFields: string[] = ['attributes', 'id', 'isReadOnly', 'ouId', 'username'];\n\nconst BaseUserProfile: FC<BaseUserProfileProps> = ({\n fallback = null,\n className = '',\n cardLayout = true,\n profile,\n flattenedProfile,\n mode = 'inline',\n title,\n attributeMapping = {},\n editable = true,\n onOpenChange,\n onUpdate,\n open = false,\n error = null,\n isLoading = false,\n preferences,\n showFields = [],\n hideFields = [],\n displayNameAttributes = [],\n}: BaseUserProfileProps): ReactElement | null => {\n const {theme, colorScheme} = useTheme();\n const [editedUser, setEditedUser] = useState(flattenedProfile || profile);\n const [editingFields, setEditingFields] = useState<Record<string, boolean>>({});\n const {t} = useTranslation(preferences?.i18n);\n\n useEffect(() => {\n const nextUser = flattenedProfile ?? profile;\n if (!nextUser) return;\n\n setEditedUser((prev: any) => {\n if (!prev) return nextUser;\n\n const updated = {...nextUser};\n Object.keys(prev).forEach((key) => {\n if (editingFields[key]) {\n updated[key] = prev[key];\n }\n });\n return updated;\n });\n }, [flattenedProfile, profile, editingFields]);\n\n /**\n * Determines if a field should be visible based on showFields, hideFields, and fieldsToSkip arrays.\n * Priority order:\n * 1. fieldsToSkip (always hidden) - highest priority\n * 2. hideFields (explicitly hidden)\n * 3. showFields (explicitly shown, if array is not empty)\n * 4. Default behavior (show all fields not in fieldsToSkip)\n */\n const shouldShowField: any = useCallback(\n (fieldName: string): boolean => {\n // Always skip fields in the hardcoded fieldsToSkip array\n if (fieldsToSkip.includes(fieldName)) {\n return false;\n }\n\n // If hideFields is provided and contains this field, hide it\n if (hideFields.length > 0 && hideFields.includes(fieldName)) {\n return false;\n }\n\n // If showFields is provided and not empty, only show fields in that array\n if (showFields.length > 0) {\n return showFields.includes(fieldName);\n }\n\n return true;\n },\n [showFields, hideFields],\n );\n\n const PencilIcon = (): any => (\n <svg\n width=\"16\"\n height=\"16\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n >\n <path d=\"M17 3a2.828 2.828 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5L17 3z\" />\n </svg>\n );\n\n const toggleFieldEdit: any = useCallback((fieldName: string) => {\n setEditingFields((prev: any) => ({\n ...prev,\n [fieldName]: !prev[fieldName],\n }));\n }, []);\n\n const getFieldPlaceholder: any = useCallback((schema: Schema): string => {\n const {type, displayName, description, name} = schema;\n\n const fieldLabel: any = displayName || description || name || 'value';\n\n switch (type) {\n case 'DATE_TIME':\n return `Enter your ${fieldLabel.toLowerCase()}`;\n case 'BOOLEAN':\n return `Select ${fieldLabel.toLowerCase()}`;\n case 'COMPLEX':\n return `Enter ${fieldLabel.toLowerCase()} details`;\n default:\n return `Enter your ${fieldLabel.toLowerCase()}`;\n }\n }, []);\n\n const formatLabel: any = useCallback(\n (key: string): string =>\n key\n .split(/(?=[A-Z])|_/)\n .map((word: any) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())\n .join(' '),\n [],\n );\n\n const styles: any = useStyles(theme, colorScheme);\n\n const ObjectDisplay: FC<{data: unknown}> = ({data}: {data: unknown}): ReactElement | null => {\n if (!data || typeof data !== 'object') return null;\n\n return (\n <table className={styles.value}>\n <tbody>\n {Object.entries(data).map(([key, value]: any) => (\n <tr key={key}>\n <td className={styles.objectKey}>\n <strong>{formatLabel(key)}:</strong>\n </td>\n <td className={styles.objectValue}>\n {typeof value === 'object' ? <ObjectDisplay data={value} /> : String(value)}\n </td>\n </tr>\n ))}\n </tbody>\n </table>\n );\n };\n\n function set(obj: Record<string, any>, path: string, value: any): void {\n const keys: any = path.split('.');\n let current: any = obj;\n\n for (let i: any = 0; i < keys.length; i += 1) {\n const key: any = keys[i];\n\n if (i === keys.length - 1) {\n current[key] = value;\n } else {\n if (!current[key] || typeof current[key] !== 'object') {\n current[key] = {};\n }\n current = current[key];\n }\n }\n }\n\n const handleFieldSave: any = useCallback(\n (schema: Schema): void => {\n if (!onUpdate || !schema.name) return;\n\n const fieldName: string = schema.name;\n let fieldValue: any;\n if (editedUser && fieldName && editedUser[fieldName] !== undefined) {\n fieldValue = editedUser[fieldName];\n } else if (flattenedProfile?.[fieldName] !== undefined) {\n fieldValue = flattenedProfile[fieldName];\n } else {\n fieldValue = '';\n }\n\n if (Array.isArray(fieldValue)) {\n fieldValue = fieldValue.filter((v: any) => v !== undefined && v !== null && v !== '');\n }\n\n let payload: Record<string, any> = {};\n set(payload, fieldName, fieldValue);\n\n onUpdate(payload);\n\n toggleFieldEdit(fieldName);\n },\n [editedUser, flattenedProfile, onUpdate, toggleFieldEdit],\n );\n\n const handleFieldCancel: any = useCallback(\n (fieldName: string) => {\n const currentUser: any = flattenedProfile || profile;\n setEditedUser((prev: any) => ({\n ...prev,\n [fieldName]: currentUser[fieldName],\n }));\n toggleFieldEdit(fieldName);\n },\n [flattenedProfile, profile, toggleFieldEdit],\n );\n\n const defaultAttributeMappings: Record<string, string | string[]> = {\n email: ['emails', 'email'],\n firstName: ['name.givenName', 'given_name'],\n lastName: ['name.familyName', 'family_name'],\n picture: ['profile', 'profileUrl', 'picture', 'URL'],\n username: ['userName', 'username', 'user_name'],\n };\n\n const mergedMappings: Record<string, string | string[]> = Object.fromEntries(\n Object.entries({...defaultAttributeMappings, ...attributeMapping}).filter(\n (entry): entry is [string, string | string[]] => entry[1] !== undefined,\n ),\n );\n\n const renderSchemaField = (\n schema: Schema,\n isEditing: boolean,\n onEditValue?: (value: any) => void,\n onStartEdit?: () => void,\n ): ReactElement | null => {\n if (!schema) return null;\n const {value, displayName, description, name, type, required, mutability, subAttributes, multiValued} = schema;\n const label: any = displayName || description || (name ? startCase(name) : '');\n\n if (subAttributes && Array.isArray(subAttributes)) {\n return (\n <>\n {subAttributes.map((subAttr: any, index: any) => {\n let displayValue: string;\n if (Array.isArray(subAttr.value)) {\n displayValue = subAttr.value\n .map((item: any) => (typeof item === 'object' ? JSON.stringify(item) : String(item)))\n .join(', ');\n } else if (typeof subAttr.value === 'object') {\n displayValue = JSON.stringify(subAttr.value);\n } else {\n displayValue = String(subAttr.value);\n }\n\n return (\n <div key={index} className={styles.field}>\n <span className={styles.label}>{subAttr.displayName || subAttr.description || ''}</span>\n <div className={styles.value}>{displayValue}</div>\n </div>\n );\n })}\n </>\n );\n }\n\n if (Array.isArray(value) || multiValued) {\n const hasValues: any = Array.isArray(value)\n ? value.length > 0\n : value !== undefined && value !== null && value !== '';\n const isEditable: any = editable && mutability !== 'READ_ONLY' && !readonlyFields.includes(name || '');\n\n if (isEditing && onEditValue && isEditable) {\n let currentValue: any;\n if (editedUser && name && editedUser[name] !== undefined) {\n currentValue = editedUser[name];\n } else if (flattenedProfile && name && flattenedProfile[name] !== undefined) {\n currentValue = flattenedProfile[name];\n } else {\n currentValue = value;\n }\n\n let fieldValues: string[];\n if (Array.isArray(currentValue)) {\n fieldValues = currentValue.map(String);\n } else if (currentValue !== undefined && currentValue !== null && currentValue !== '') {\n fieldValues = [String(currentValue)];\n } else {\n fieldValues = [];\n }\n\n return (\n <>\n <span className={styles.label}>{label}</span>\n <div className={styles.value}>\n <MultiInput\n values={fieldValues}\n onChange={(newValues: any): void => {\n if (multiValued || Array.isArray(currentValue)) {\n onEditValue(newValues);\n } else {\n onEditValue(newValues[0] || '');\n }\n }}\n placeholder={getFieldPlaceholder(schema)}\n fieldType={type as 'STRING' | 'DATE_TIME' | 'BOOLEAN'}\n type={type === 'DATE_TIME' ? 'date' : 'text'}\n required={required}\n />\n </div>\n </>\n );\n }\n\n let displayValue: string;\n if (hasValues) {\n if (Array.isArray(value)) {\n displayValue = value\n .map((item: any) => (typeof item === 'object' ? JSON.stringify(item) : String(item)))\n .join(', ');\n } else {\n displayValue = String(value);\n }\n } else if (isEditable) {\n displayValue = getFieldPlaceholder(schema);\n } else {\n displayValue = '-';\n }\n\n return (\n <>\n <span className={styles.label}>{label}</span>\n <div className={cx(styles.value, !hasValues ? styles.valuePlaceholder : '')}>\n {!hasValues && isEditable && onStartEdit ? (\n <Button\n onClick={onStartEdit}\n variant=\"text\"\n color=\"secondary\"\n size=\"small\"\n title=\"Click to edit\"\n className={styles.editButton}\n >\n {displayValue}\n </Button>\n ) : (\n displayValue\n )}\n </div>\n </>\n );\n }\n if (type === 'COMPLEX' && typeof value === 'object') {\n return <ObjectDisplay data={value} />;\n }\n\n if (isEditing && onEditValue && mutability !== 'READ_ONLY' && !readonlyFields.includes(name || '')) {\n let fieldValue: any;\n if (editedUser && name && editedUser[name] !== undefined) {\n fieldValue = editedUser[name];\n } else if (flattenedProfile && name && flattenedProfile[name] !== undefined) {\n fieldValue = flattenedProfile[name];\n } else {\n fieldValue = value || '';\n }\n\n const commonProps: any = {\n label: undefined,\n onChange: (e: any) => onEditValue(e.target ? e.target.value : e),\n placeholder: getFieldPlaceholder(schema),\n required,\n value: fieldValue,\n // Removed inline style, use .styles.ts for marginBottom if needed\n };\n let field: ReactElement;\n switch (type) {\n case 'STRING':\n field = <TextField {...commonProps} />;\n break;\n case 'DATE_TIME':\n field = <DatePicker {...commonProps} />;\n break;\n case 'BOOLEAN':\n field = (\n <Checkbox\n {...commonProps}\n checked={!!fieldValue}\n onChange={(e: any): void => {\n onEditValue(e.target.checked);\n }}\n />\n );\n break;\n case 'COMPLEX':\n field = (\n <textarea\n value={fieldValue}\n onChange={(e: any): void => onEditValue(e.target.value)}\n placeholder={getFieldPlaceholder(schema)}\n required={required}\n className={styles.complexTextarea}\n />\n );\n break;\n default:\n field = <TextField {...commonProps} />;\n }\n return (\n <>\n <span className={styles.label}>{label}</span>\n <div className={styles.value}>{field}</div>\n </>\n );\n }\n\n const hasValue: any = value !== undefined && value !== null && value !== '';\n const isEditable: any = editable && mutability !== 'READ_ONLY' && !readonlyFields.includes(name || '');\n\n let displayValue: string;\n if (hasValue) {\n displayValue = String(value);\n } else if (isEditable) {\n displayValue = getFieldPlaceholder(schema);\n } else {\n displayValue = '-';\n }\n\n return (\n <>\n <span className={styles.label}>{label}</span>\n <div className={cx(styles.value, !hasValue ? styles.valuePlaceholder : '')}>\n {!hasValue && isEditable && onStartEdit ? (\n <Button\n onClick={onStartEdit}\n variant=\"text\"\n color=\"secondary\"\n size=\"small\"\n title=\"Click to edit\"\n className={styles.editButton}\n >\n {displayValue}\n </Button>\n ) : (\n displayValue\n )}\n </div>\n </>\n );\n };\n\n const renderUserInfo = (schema: Schema): any => {\n if (!schema?.name) return null;\n\n const hasValue: any = schema.value !== undefined && schema.value !== '' && schema.value !== null;\n const isFieldEditing: any = editingFields[schema.name];\n const isReadonlyField: any = readonlyFields.includes(schema.name);\n\n const shouldShow: any = hasValue || isFieldEditing || (editable && schema.mutability === 'READ_WRITE');\n\n if (!shouldShow) {\n return null;\n }\n\n return (\n <div className={styles.field}>\n <div className={styles.fieldInner}>\n {renderSchemaField(\n schema,\n isFieldEditing,\n (value: any) => {\n const tempEditedUser: any = {...editedUser};\n tempEditedUser[schema.name!] = value;\n setEditedUser(tempEditedUser);\n },\n () => toggleFieldEdit(schema.name),\n )}\n </div>\n {editable && schema.mutability !== 'READ_ONLY' && !isReadonlyField && (\n <div className={styles.fieldActions}>\n {isFieldEditing && (\n <>\n <Button size=\"small\" color=\"primary\" variant=\"solid\" onClick={(): any => handleFieldSave(schema)}>\n Save\n </Button>\n <Button\n size=\"small\"\n color=\"secondary\"\n variant=\"solid\"\n onClick={(): any => handleFieldCancel(schema.name)}\n >\n Cancel\n </Button>\n </>\n )}\n {!isFieldEditing && hasValue && (\n <Button\n size=\"small\"\n color=\"tertiary\"\n variant=\"icon\"\n onClick={(): any => toggleFieldEdit(schema.name)}\n title=\"Edit\"\n className={styles.editButton}\n >\n <PencilIcon />\n </Button>\n )}\n </div>\n )}\n </div>\n );\n };\n\n if (!profile && !flattenedProfile) {\n return fallback;\n }\n\n const containerClasses: any = cx(\n styles.root,\n cardLayout ? styles.card : '',\n withVendorCSSClassPrefix('user-profile'),\n className,\n );\n\n const currentUser: any = flattenedProfile || profile;\n\n const renderProfileWithoutSchemas = (): any => {\n if (!currentUser) return null;\n\n const displayName: any = getDisplayName(mergedMappings, profile!, displayNameAttributes);\n\n const profileEntries: any = Object.entries(currentUser)\n .filter(([key, value]: [string, any]) => {\n if (!shouldShowField(key)) return false;\n\n return value !== undefined && value !== '' && value !== null;\n })\n .sort(([a]: [string, ...any[]], [b]: [string, ...any[]]) => a.localeCompare(b));\n\n return (\n <>\n <div className={styles.profileSummary}>\n <Avatar\n imageUrl={getMappedUserProfileValue('picture', mergedMappings, currentUser)}\n name={displayName}\n size={70}\n alt={`${displayName}'s avatar`}\n isLoading={isLoading}\n />\n <Typography variant=\"h3\" fontWeight=\"medium\">\n {displayName}\n </Typography>\n {getMappedUserProfileValue('email', mergedMappings, currentUser) && (\n <Typography variant=\"body2\" color=\"textSecondary\">\n {getMappedUserProfileValue('email', mergedMappings, currentUser)}\n </Typography>\n )}\n </div>\n <Divider />\n {profileEntries.map(([key, value]: any) => {\n const isReadonly = readonlyFields.includes(key);\n const schema: Schema = {name: key, mutability: isReadonly ? 'READ_ONLY' : 'READ_WRITE'};\n const schemaWithValue: any = {...schema, value};\n\n return (\n <div key={key} className={styles.info}>\n {renderUserInfo(schemaWithValue)}\n </div>\n );\n })}\n </>\n );\n };\n\n const profileContent: any = (\n <CardPrimitive className={containerClasses}>\n {error && (\n <AlertPrimitive\n variant=\"error\"\n className={cx(withVendorCSSClassPrefix(bem('user-profile', 'alert')), styles.alert)}\n >\n <AlertPrimitive.Title>{t('errors.heading') || 'Error'}</AlertPrimitive.Title>\n <AlertPrimitive.Description>{error}</AlertPrimitive.Description>\n </AlertPrimitive>\n )}\n <div className={styles.infoContainer}>{renderProfileWithoutSchemas()}</div>\n </CardPrimitive>\n );\n\n if (mode === 'popup') {\n return (\n <DialogPrimitive open={open} onOpenChange={onOpenChange}>\n <DialogPrimitive.Content>\n <DialogPrimitive.Heading>{title ?? t('user.profile.heading')}</DialogPrimitive.Heading>\n <div className={styles.popup}>{profileContent}</div>\n </DialogPrimitive.Content>\n </DialogPrimitive>\n );\n }\n\n return profileContent;\n};\n\nexport default BaseUserProfile;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"BaseUserProfile.cjs","names":["fieldsToSkip: string[]","readonlyFields: string[]","BaseUserProfile: FC<BaseUserProfileProps>","useTheme","useTranslation","toggleFieldEdit: any","getFieldPlaceholder: any","fieldLabel: any","formatLabel: any","styles: any","useStyles","ObjectDisplay: FC<{data: unknown}>","keys: any","current: any","i: any","key: any","handleFieldSave: any","fieldName: string","currentUser: any","fieldValue: any","currentUser","payload: Record<string, any>","handleFieldCancel: any","mergedMappings: Record<string, string | string[]>","label: any","displayValue: string","displayValue","hasValues: any","isEditable: any","isEditable","currentValue: any","fieldValues: string[]","MultiInput","cx","Button","commonProps: any","field: ReactElement","TextField","DatePicker","Checkbox","hasValue: any","isFieldEditing: any","isReadonlyField: any","tempEditedUser: any","containerClasses: any","displayName: any","getDisplayName","schemaItems: Schema[]","Avatar","getMappedUserProfileValue","Typography","Divider","profileContent: any","CardPrimitive","AlertPrimitive","DialogPrimitive"],"sources":["../../../../../src/components/presentation/UserProfile/BaseUserProfile.tsx"],"sourcesContent":["// Copyright 2025 The ThunderID Authors\n// SPDX-License-Identifier: Apache-2.0\n\nimport {User, withVendorCSSClassPrefix, bem, Preferences, startCase, AttributeSchema} from '@thunderid/browser';\nimport {FC, ReactElement, useState, useCallback, useEffect} from 'react';\nimport useStyles from './BaseUserProfile.styles';\nimport useTheme from '../../../contexts/Theme/useTheme';\nimport useTranslation from '../../../hooks/useTranslation';\nimport {cx} from '../../../styles/emotion';\nimport getDisplayName from '../../../utils/getDisplayName';\nimport getMappedUserProfileValue from '../../../utils/getMappedUserProfileValue';\nimport AlertPrimitive from '../../primitives/Alert/Alert';\nimport {Avatar} from '../../primitives/Avatar/Avatar';\nimport Button from '../../primitives/Button/Button';\nimport CardPrimitive from '../../primitives/Card/Card';\nimport Checkbox from '../../primitives/Checkbox/Checkbox';\nimport DatePicker from '../../primitives/DatePicker/DatePicker';\nimport DialogPrimitive from '../../primitives/Dialog/Dialog';\nimport Divider from '../../primitives/Divider/Divider';\nimport MultiInput from '../../primitives/MultiInput/MultiInput';\nimport TextField from '../../primitives/TextField/TextField';\nimport Typography from '../../primitives/Typography/Typography';\n\ninterface ExtendedFlatSchema {\n path?: string;\n schemaId?: string;\n}\n\ninterface Schema extends ExtendedFlatSchema {\n caseExact?: boolean;\n description?: string;\n displayName?: string;\n displayOrder?: string;\n multiValued?: boolean;\n mutability?: string;\n name?: string;\n regex?: string;\n required?: boolean;\n returned?: string;\n subAttributes?: Schema[];\n type?: string;\n uniqueness?: string;\n value?: any;\n}\n\nexport interface BaseUserProfileProps {\n attributeMapping?: {\n [key: string]: string | string[] | undefined;\n firstName?: string | string[];\n lastName?: string | string[];\n picture?: string | string[];\n username?: string | string[];\n };\n cardLayout?: boolean;\n className?: string;\n displayNameAttributes?: string[];\n editable?: boolean;\n error?: string | null;\n fallback?: ReactElement | null;\n flattenedProfile?: User;\n hideFields?: string[];\n isLoading?: boolean;\n mode?: 'inline' | 'popup';\n onOpenChange?: (open: boolean) => void;\n onUpdate?: (payload: any) => Promise<void>;\n open?: boolean;\n /**\n * Component-level preferences to override global i18n and theme settings.\n * Preferences are deep-merged with global ones, with component preferences\n * taking precedence. Affects this component and all its descendants.\n */\n preferences?: Preferences;\n profile?: User;\n showFields?: string[];\n\n title?: string;\n userSchema?: Record<string, AttributeSchema> | null;\n}\n\n// Fields to skip based on schema.name\nconst fieldsToSkip: string[] = [\n 'roles.default',\n 'active',\n 'groups',\n 'accountLocked',\n 'accountDisabled',\n 'oneTimePassword',\n 'userSourceId',\n 'idpType',\n 'localCredentialExists',\n 'active',\n 'ResourceType',\n 'ExternalID',\n 'MetaData',\n 'verifiedMobileNumbers',\n 'verifiedEmailAddresses',\n 'phoneNumbers.mobile',\n 'emailAddresses',\n 'preferredMFAOption',\n 'attributes',\n 'picture',\n 'isReadOnly',\n 'isReadonly',\n];\n\n// Fields that should be readonly\nconst readonlyFields: string[] = ['attributes', 'id', 'isReadOnly', 'ouId', 'username', 'sub'];\n\nconst BaseUserProfile: FC<BaseUserProfileProps> = ({\n fallback = null,\n className = '',\n cardLayout = true,\n profile,\n flattenedProfile,\n userSchema,\n mode = 'inline',\n title,\n attributeMapping = {},\n editable = true,\n onOpenChange,\n onUpdate,\n open = false,\n error = null,\n isLoading = false,\n preferences,\n showFields = [],\n hideFields = [],\n displayNameAttributes = [],\n}: BaseUserProfileProps): ReactElement | null => {\n const {theme, colorScheme} = useTheme();\n const [editedUser, setEditedUser] = useState(flattenedProfile || profile);\n const [editingFields, setEditingFields] = useState<Record<string, boolean>>({});\n const [fieldErrors, setFieldErrors] = useState<Record<string, string>>({});\n const {t} = useTranslation(preferences?.i18n);\n\n useEffect(() => {\n const nextUser = flattenedProfile ?? profile;\n if (!nextUser) return;\n\n setEditedUser((prev: any) => {\n if (!prev) return nextUser;\n\n const updated = {...nextUser};\n Object.keys(prev).forEach((key) => {\n if (editingFields[key]) {\n updated[key] = prev[key];\n }\n });\n return updated;\n });\n }, [flattenedProfile, profile, editingFields]);\n\n /**\n * Determines if a field should be visible based on showFields, hideFields, and fallback fieldsToSkip arrays.\n * When isSchemaBased is true, fieldsToSkip is bypassed so schema-defined fields render dynamically.\n */\n const shouldShowField = useCallback(\n (fieldName: string, isSchemaBased: boolean = false): boolean => {\n // For fallback without schema metadata, skip internal system fields\n if (!isSchemaBased && fieldsToSkip.includes(fieldName)) {\n return false;\n }\n\n // If hideFields is provided and contains this field, hide it\n if (hideFields.length > 0 && hideFields.includes(fieldName)) {\n return false;\n }\n\n // If showFields is provided and not empty, only show fields in that array\n if (showFields.length > 0) {\n return showFields.includes(fieldName);\n }\n\n return true;\n },\n [showFields, hideFields],\n );\n\n const PencilIcon = (): any => (\n <svg\n width=\"16\"\n height=\"16\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n >\n <path d=\"M17 3a2.828 2.828 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5L17 3z\" />\n </svg>\n );\n\n const toggleFieldEdit: any = useCallback((fieldName: string) => {\n setEditingFields((prev: any) => ({\n ...prev,\n [fieldName]: !prev[fieldName],\n }));\n }, []);\n\n const getFieldPlaceholder: any = useCallback(\n (schema: Schema): string => {\n const {type, displayName, description, name} = schema;\n\n const fieldLabel: any = displayName || description || name || 'value';\n\n switch (type) {\n case 'DATE_TIME':\n case 'STRING':\n default:\n return t('elements.fields.generic.placeholder', {field: fieldLabel.toLowerCase()});\n }\n },\n [t],\n );\n\n const formatLabel: any = useCallback(\n (key: string): string =>\n key\n .split(/(?=[A-Z])|_/)\n .map((word: any) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())\n .join(' '),\n [],\n );\n\n const styles: any = useStyles(theme, colorScheme);\n\n const ObjectDisplay: FC<{data: unknown}> = ({data}: {data: unknown}): ReactElement | null => {\n if (!data || typeof data !== 'object') return null;\n\n return (\n <table className={styles.value}>\n <tbody>\n {Object.entries(data).map(([key, value]: any) => (\n <tr key={key}>\n <td className={styles.objectKey}>\n <strong>{formatLabel(key)}:</strong>\n </td>\n <td className={styles.objectValue}>\n {typeof value === 'object' ? <ObjectDisplay data={value} /> : String(value)}\n </td>\n </tr>\n ))}\n </tbody>\n </table>\n );\n };\n\n function set(obj: Record<string, any>, path: string, value: any): void {\n const keys: any = path.split('.');\n let current: any = obj;\n\n for (let i: any = 0; i < keys.length; i += 1) {\n const key: any = keys[i];\n\n if (i === keys.length - 1) {\n current[key] = value;\n } else {\n if (!current[key] || typeof current[key] !== 'object') {\n current[key] = {};\n }\n current = current[key];\n }\n }\n }\n\n const handleFieldSave: any = useCallback(\n (schema: Schema): void => {\n if (!onUpdate || !schema.name) return;\n\n const fieldName: string = schema.name;\n const currentUser: any = flattenedProfile || profile;\n let fieldValue: any;\n if (editedUser && fieldName && editedUser[fieldName] !== undefined) {\n fieldValue = editedUser[fieldName];\n } else if (currentUser?.attributes?.[fieldName] !== undefined) {\n fieldValue = currentUser.attributes[fieldName];\n } else if (flattenedProfile?.[fieldName] !== undefined) {\n fieldValue = flattenedProfile[fieldName];\n } else {\n fieldValue = '';\n }\n\n if (Array.isArray(fieldValue)) {\n fieldValue = fieldValue.filter((v: any) => v !== undefined && v !== null && v !== '');\n }\n\n const strVal = String(fieldValue ?? '').trim();\n const fieldLabel = schema.displayName || (schema.name ? startCase(schema.name) : 'Field');\n\n // 1. Required validation\n if (schema.required && !strVal) {\n setFieldErrors((prev: Record<string, string>) => ({\n ...prev,\n [fieldName]: t('validations.required.field.error'),\n }));\n return;\n }\n\n // 2. Regex validation\n if (schema.regex && strVal) {\n try {\n const reg = new RegExp(schema.regex);\n if (!reg.test(strVal)) {\n setFieldErrors((prev: Record<string, string>) => ({\n ...prev,\n [fieldName]: t('validation.pattern.invalid'),\n }));\n return;\n }\n } catch (e) {\n // ignore invalid regex syntax safely\n }\n }\n\n // Clear field error if valid\n setFieldErrors((prev: Record<string, string>) => {\n const next = {...prev};\n delete next[fieldName];\n return next;\n });\n\n let payload: Record<string, any> = {};\n set(payload, fieldName, fieldValue);\n\n onUpdate(payload);\n\n toggleFieldEdit(fieldName);\n },\n [editedUser, flattenedProfile, profile, onUpdate, toggleFieldEdit, t],\n );\n\n const handleFieldCancel: any = useCallback(\n (fieldName: string) => {\n const currentUser: any = flattenedProfile || profile;\n const initialVal = currentUser?.attributes?.[fieldName] ?? currentUser?.[fieldName];\n setEditedUser((prev: any) => ({\n ...prev,\n [fieldName]: initialVal,\n }));\n setFieldErrors((prev: Record<string, string>) => {\n const next = {...prev};\n delete next[fieldName];\n return next;\n });\n toggleFieldEdit(fieldName);\n },\n [flattenedProfile, profile, toggleFieldEdit],\n );\n\n const defaultAttributeMappings: Record<string, string | string[]> = {\n email: ['emails', 'email'],\n firstName: ['name.givenName', 'given_name'],\n lastName: ['name.familyName', 'family_name'],\n picture: ['profile', 'profileUrl', 'picture', 'URL'],\n username: ['userName', 'username', 'user_name'],\n };\n\n const mergedMappings: Record<string, string | string[]> = Object.fromEntries(\n Object.entries({...defaultAttributeMappings, ...attributeMapping}).filter(\n (entry): entry is [string, string | string[]] => entry[1] !== undefined,\n ),\n );\n\n const renderSchemaField = (\n schema: Schema,\n isEditing: boolean,\n onEditValue?: (value: any) => void,\n onStartEdit?: () => void,\n ): ReactElement | null => {\n if (!schema) return null;\n const {value, displayName, description, name, type, required, mutability, subAttributes, multiValued} = schema;\n const label: any = displayName || description || (name ? startCase(name) : '');\n\n if (subAttributes && Array.isArray(subAttributes)) {\n return (\n <>\n {subAttributes.map((subAttr: any, index: any) => {\n let displayValue: string;\n if (Array.isArray(subAttr.value)) {\n displayValue = subAttr.value\n .map((item: any) => (typeof item === 'object' ? JSON.stringify(item) : String(item)))\n .join(', ');\n } else if (typeof subAttr.value === 'object') {\n displayValue = JSON.stringify(subAttr.value);\n } else {\n displayValue = String(subAttr.value);\n }\n\n return (\n <div key={index} className={styles.field}>\n <span className={styles.label}>{subAttr.displayName || subAttr.description || ''}</span>\n <div className={styles.value}>{displayValue}</div>\n </div>\n );\n })}\n </>\n );\n }\n\n if (Array.isArray(value) || multiValued) {\n const hasValues: any = Array.isArray(value)\n ? value.length > 0\n : value !== undefined && value !== null && value !== '';\n const isEditable: any = editable && mutability !== 'READ_ONLY' && !readonlyFields.includes(name || '');\n\n if (isEditing && onEditValue && isEditable) {\n let currentValue: any;\n if (editedUser && name && editedUser[name] !== undefined) {\n currentValue = editedUser[name];\n } else if (flattenedProfile && name && flattenedProfile[name] !== undefined) {\n currentValue = flattenedProfile[name];\n } else {\n currentValue = value;\n }\n\n let fieldValues: string[];\n if (Array.isArray(currentValue)) {\n fieldValues = currentValue.map(String);\n } else if (currentValue !== undefined && currentValue !== null && currentValue !== '') {\n fieldValues = [String(currentValue)];\n } else {\n fieldValues = [];\n }\n\n return (\n <>\n <span className={styles.label}>{label}</span>\n <div className={styles.value}>\n <MultiInput\n values={fieldValues}\n onChange={(newValues: any): void => {\n if (multiValued || Array.isArray(currentValue)) {\n onEditValue(newValues);\n } else {\n onEditValue(newValues[0] || '');\n }\n }}\n placeholder={getFieldPlaceholder(schema)}\n fieldType={type as 'STRING' | 'DATE_TIME' | 'BOOLEAN'}\n type={type === 'DATE_TIME' ? 'date' : 'text'}\n required={required}\n />\n </div>\n </>\n );\n }\n\n let displayValue: string;\n if (hasValues) {\n if (Array.isArray(value)) {\n displayValue = value\n .map((item: any) => (typeof item === 'object' ? JSON.stringify(item) : String(item)))\n .join(', ');\n } else {\n displayValue = String(value);\n }\n } else if (isEditable) {\n displayValue = getFieldPlaceholder(schema);\n } else {\n displayValue = '-';\n }\n\n return (\n <>\n <span className={styles.label}>{label}</span>\n <div className={cx(styles.value, !hasValues ? styles.valuePlaceholder : '')}>\n {!hasValues && isEditable && onStartEdit ? (\n <Button\n onClick={onStartEdit}\n variant=\"text\"\n color=\"secondary\"\n size=\"small\"\n title=\"Click to edit\"\n className={styles.editButton}\n >\n {displayValue}\n </Button>\n ) : (\n displayValue\n )}\n </div>\n </>\n );\n }\n if (type === 'COMPLEX' && typeof value === 'object') {\n return <ObjectDisplay data={value} />;\n }\n\n if (isEditing && onEditValue && mutability !== 'READ_ONLY' && !readonlyFields.includes(name || '')) {\n let fieldValue: any;\n if (editedUser && name && editedUser[name] !== undefined) {\n fieldValue = editedUser[name];\n } else if (flattenedProfile && name && flattenedProfile[name] !== undefined) {\n fieldValue = flattenedProfile[name];\n } else {\n fieldValue = value || '';\n }\n\n const commonProps: any = {\n label: undefined,\n onChange: (e: any) => onEditValue(e.target ? e.target.value : e),\n placeholder: getFieldPlaceholder(schema),\n required,\n value: fieldValue,\n // Removed inline style, use .styles.ts for marginBottom if needed\n };\n let field: ReactElement;\n switch (type) {\n case 'STRING':\n field = <TextField {...commonProps} />;\n break;\n case 'DATE_TIME':\n field = <DatePicker {...commonProps} />;\n break;\n case 'BOOLEAN':\n field = (\n <Checkbox\n {...commonProps}\n checked={!!fieldValue}\n onChange={(e: any): void => {\n onEditValue(e.target.checked);\n }}\n />\n );\n break;\n case 'COMPLEX':\n field = (\n <textarea\n value={fieldValue}\n onChange={(e: any): void => onEditValue(e.target.value)}\n placeholder={getFieldPlaceholder(schema)}\n required={required}\n className={styles.complexTextarea}\n />\n );\n break;\n default:\n field = <TextField {...commonProps} />;\n }\n return (\n <>\n <span className={styles.label}>{label}</span>\n <div className={styles.value}>{field}</div>\n </>\n );\n }\n\n const hasValue: any = value !== undefined && value !== null && value !== '';\n const isEditable: any = editable && mutability !== 'READ_ONLY' && !readonlyFields.includes(name || '');\n\n let displayValue: string;\n if (hasValue) {\n displayValue = String(value);\n } else if (isEditable) {\n displayValue = getFieldPlaceholder(schema);\n } else {\n displayValue = '-';\n }\n\n return (\n <>\n <span className={styles.label}>{label}</span>\n <div className={cx(styles.value, !hasValue ? styles.valuePlaceholder : '')}>\n {!hasValue && isEditable && onStartEdit ? (\n <Button\n onClick={onStartEdit}\n variant=\"text\"\n color=\"secondary\"\n size=\"small\"\n title=\"Click to edit\"\n className={styles.editButton}\n >\n {displayValue}\n </Button>\n ) : (\n displayValue\n )}\n </div>\n </>\n );\n };\n\n const renderUserInfo = (schema: Schema): any => {\n if (!schema?.name) return null;\n\n const hasValue: any = schema.value !== undefined && schema.value !== '' && schema.value !== null;\n const isFieldEditing: any = editingFields[schema.name];\n const isReadonlyField: any = readonlyFields.includes(schema.name);\n\n const shouldShow: any = hasValue || isFieldEditing || (editable && schema.mutability === 'READ_WRITE');\n\n if (!shouldShow) {\n return null;\n }\n\n return (\n <div className={styles.field}>\n <div className={styles.fieldInner}>\n {renderSchemaField(\n schema,\n isFieldEditing,\n (value: any) => {\n const tempEditedUser: any = {...editedUser};\n tempEditedUser[schema.name!] = value;\n setEditedUser(tempEditedUser);\n },\n () => toggleFieldEdit(schema.name),\n )}\n {fieldErrors[schema.name] && (\n <div style={{color: '#d32f2f', fontSize: '0.8rem', marginTop: '4px', fontWeight: 500}}>\n {fieldErrors[schema.name]}\n </div>\n )}\n </div>\n {editable && schema.mutability !== 'READ_ONLY' && !isReadonlyField && (\n <div className={styles.fieldActions}>\n {isFieldEditing && (\n <>\n <Button size=\"small\" color=\"primary\" variant=\"solid\" onClick={(): any => handleFieldSave(schema)}>\n Save\n </Button>\n <Button\n size=\"small\"\n color=\"secondary\"\n variant=\"solid\"\n onClick={(): any => handleFieldCancel(schema.name)}\n >\n Cancel\n </Button>\n </>\n )}\n {!isFieldEditing && hasValue && (\n <Button\n size=\"small\"\n color=\"tertiary\"\n variant=\"icon\"\n onClick={(): any => toggleFieldEdit(schema.name)}\n title=\"Edit\"\n className={styles.editButton}\n >\n <PencilIcon />\n </Button>\n )}\n </div>\n )}\n </div>\n );\n };\n\n if (!profile && !flattenedProfile) {\n return fallback;\n }\n\n const containerClasses: any = cx(\n styles.root,\n cardLayout ? styles.card : '',\n withVendorCSSClassPrefix('user-profile'),\n className,\n );\n\n const currentUser: any = flattenedProfile || profile;\n\n const renderProfileContent = (): any => {\n if (!currentUser) return null;\n\n const displayName: any = getDisplayName(mergedMappings, profile!, displayNameAttributes);\n\n let schemaItems: Schema[] = [];\n\n if (userSchema && typeof userSchema === 'object' && Object.keys(userSchema).length > 0) {\n schemaItems = Object.entries(userSchema)\n .filter(([key, metaAttr]: [string, AttributeSchema]) => {\n if (metaAttr?.credential) return false;\n return shouldShowField(key, true);\n })\n .map(([key, metaAttr]: [string, AttributeSchema]) => {\n const val = editedUser?.[key] ?? currentUser?.attributes?.[key] ?? currentUser?.[key] ?? '';\n\n const isReadonly =\n metaAttr.readOnly === true || metaAttr.mutability === 'READ_ONLY' || readonlyFields.includes(key);\n\n return {\n name: key,\n displayName: metaAttr.displayName ?? (key ? startCase(key) : ''),\n type: (metaAttr.type ?? 'STRING').toUpperCase(),\n regex: metaAttr.regex,\n required: !!metaAttr.required,\n mutability: isReadonly ? 'READ_ONLY' : 'READ_WRITE',\n value: val,\n };\n });\n } else {\n const profileEntries: any = Object.entries(currentUser)\n .filter(([key, value]: [string, any]) => {\n if (!shouldShowField(key)) return false;\n\n return value !== undefined && value !== '' && value !== null;\n })\n .sort(([a]: [string, ...any[]], [b]: [string, ...any[]]) => a.localeCompare(b));\n\n schemaItems = profileEntries.map(([key, value]: any) => {\n const isReadonly = readonlyFields.includes(key);\n return {\n name: key,\n displayName: startCase(key),\n mutability: isReadonly ? 'READ_ONLY' : 'READ_WRITE',\n value,\n };\n });\n }\n\n return (\n <>\n <div className={styles.profileSummary}>\n <Avatar\n imageUrl={getMappedUserProfileValue('picture', mergedMappings, currentUser)}\n name={displayName}\n size={70}\n alt={`${displayName}'s avatar`}\n isLoading={isLoading}\n />\n <Typography variant=\"h3\" fontWeight=\"medium\">\n {displayName}\n </Typography>\n {getMappedUserProfileValue('email', mergedMappings, currentUser) && (\n <Typography variant=\"body2\" color=\"textSecondary\">\n {getMappedUserProfileValue('email', mergedMappings, currentUser)}\n </Typography>\n )}\n </div>\n <Divider />\n {schemaItems.map((schemaWithValue: Schema) => (\n <div key={schemaWithValue.name} className={styles.info}>\n {renderUserInfo(schemaWithValue)}\n </div>\n ))}\n </>\n );\n };\n\n const profileContent: any = (\n <CardPrimitive className={containerClasses}>\n {error && (\n <AlertPrimitive\n variant=\"error\"\n className={cx(withVendorCSSClassPrefix(bem('user-profile', 'alert')), styles.alert)}\n >\n <AlertPrimitive.Title>{t('errors.heading') || 'Error'}</AlertPrimitive.Title>\n <AlertPrimitive.Description>{error}</AlertPrimitive.Description>\n </AlertPrimitive>\n )}\n <div className={styles.infoContainer}>{renderProfileContent()}</div>\n </CardPrimitive>\n );\n\n if (mode === 'popup') {\n return (\n <DialogPrimitive open={open} onOpenChange={onOpenChange}>\n <DialogPrimitive.Content>\n <DialogPrimitive.Heading>{title ?? t('user.profile.heading')}</DialogPrimitive.Heading>\n <div className={styles.popup}>{profileContent}</div>\n </DialogPrimitive.Content>\n </DialogPrimitive>\n );\n }\n\n return profileContent;\n};\n\nexport default BaseUserProfile;\n"],"mappings":"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"}
|
|
@@ -46,8 +46,8 @@ react_jsx_runtime = require_rolldown_runtime.__toESM(react_jsx_runtime);
|
|
|
46
46
|
* ```
|
|
47
47
|
*/
|
|
48
48
|
const UserProfile = ({ preferences, editable = true,...rest }) => {
|
|
49
|
-
const { baseUrl, instanceId, preferences: contextPreferences } = require_useThunderID.default();
|
|
50
|
-
const { profile, flattenedProfile, onUpdateProfile } = require_useUser.default();
|
|
49
|
+
const { baseUrl, endpoints, instanceId, preferences: contextPreferences } = require_useThunderID.default();
|
|
50
|
+
const { profile, flattenedProfile, onUpdateProfile, userSchema } = require_useUser.default();
|
|
51
51
|
const resolvedPreferences = {
|
|
52
52
|
...contextPreferences,
|
|
53
53
|
...preferences,
|
|
@@ -68,6 +68,7 @@ const UserProfile = ({ preferences, editable = true,...rest }) => {
|
|
|
68
68
|
});
|
|
69
69
|
onUpdateProfile(await require_updateMeProfile.default({
|
|
70
70
|
baseUrl,
|
|
71
|
+
url: (0, __thunderid_browser.resolveResourceEndpoint)("usersMe", { endpoints }),
|
|
71
72
|
instanceId,
|
|
72
73
|
payload: updatedAttributes
|
|
73
74
|
}));
|
|
@@ -80,6 +81,7 @@ const UserProfile = ({ preferences, editable = true,...rest }) => {
|
|
|
80
81
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_BaseUserProfile.default, {
|
|
81
82
|
profile: profile ?? void 0,
|
|
82
83
|
flattenedProfile: flattenedProfile ?? void 0,
|
|
84
|
+
userSchema: userSchema ?? void 0,
|
|
83
85
|
editable: isEditableProfile,
|
|
84
86
|
onUpdate: isEditableProfile ? handleProfileUpdate : void 0,
|
|
85
87
|
error,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"UserProfile.cjs","names":["UserProfile: FC<UserProfileProps>","useThunderID","useUser","useTranslation","isEditableProfile: boolean","updatedAttributes: Record<string, unknown>","updateMeProfile","caughtError: unknown","message: string","ThunderIDError","BaseUserProfile"],"sources":["../../../../../src/components/presentation/UserProfile/UserProfile.tsx"],"sourcesContent":["// Copyright 2025 The ThunderID Authors\n// SPDX-License-Identifier: Apache-2.0\n\nimport {ThunderIDError, User, deepMerge} from '@thunderid/browser';\nimport {FC, ReactElement, useState} from 'react';\n// eslint-disable-next-line import/no-named-as-default\nimport BaseUserProfile, {BaseUserProfileProps} from './BaseUserProfile';\nimport updateMeProfile from '../../../api/updateMeProfile';\nimport useThunderID from '../../../contexts/ThunderID/useThunderID';\nimport useUser from '../../../contexts/User/useUser';\nimport useTranslation from '../../../hooks/useTranslation';\n\n/**\n * Props for the UserProfile component.\n * Extends BaseUserProfileProps but makes the user prop optional since it will be obtained from useThunderID\n */\nexport type UserProfileProps = Omit<BaseUserProfileProps, 'user' | 'profile' | 'flattenedProfile'>;\n\n/**\n * UserProfile component displays the authenticated user's profile information in a\n * structured and styled format. It shows user details such as display name, email,\n * username, and other available profile information from ThunderID.\n *\n * This component is the React-specific implementation that uses the BaseUserProfile\n * and automatically retrieves the user data from ThunderID context if not provided.\n *\n * @example\n * ```tsx\n * // Basic usage - will use user from ThunderID context\n * <UserProfile />\n *\n * // With explicit user data\n * <UserProfile user={specificUser} />\n *\n * // With card layout and custom fallback\n * <UserProfile\n * cardLayout={true}\n * fallback={<div>Please sign in to view your profile</div>}\n * />\n *\n * // With field filtering - only show specific fields\n * <UserProfile\n * showFields={['name.givenName', 'name.familyName', 'emails']}\n * />\n *\n * // With field hiding - hide specific fields\n * <UserProfile\n * hideFields={['phoneNumbers', 'addresses']}\n * />\n * ```\n */\nconst UserProfile: FC<UserProfileProps> = ({preferences, editable = true, ...rest}: UserProfileProps): ReactElement => {\n const {baseUrl, instanceId, preferences: contextPreferences} = useThunderID();\n const {profile, flattenedProfile, onUpdateProfile} = useUser();\n const resolvedPreferences = {\n ...contextPreferences,\n ...preferences,\n user: {\n ...contextPreferences?.user,\n ...preferences?.user,\n },\n };\n const {t} = useTranslation(resolvedPreferences?.i18n);\n const isEditableProfile: boolean = resolvedPreferences?.user?.fetchUserProfile === false ? false : editable;\n\n const [error, setError] = useState<string | null>(null);\n\n const handleProfileUpdate = async (payload: Record<string, unknown>): Promise<void> => {\n setError(null);\n\n try {\n const updatedAttributes: Record<string, unknown> = deepMerge(\n (profile?.['attributes'] as Record<string, unknown>) ?? {},\n payload,\n );\n\n Object.keys(updatedAttributes).forEach((key) => {\n if (updatedAttributes[key] === undefined || updatedAttributes[key] === null) {\n delete updatedAttributes[key];\n }\n });\n\n const response: User = await updateMeProfile({baseUrl, instanceId
|
|
1
|
+
{"version":3,"file":"UserProfile.cjs","names":["UserProfile: FC<UserProfileProps>","useThunderID","useUser","useTranslation","isEditableProfile: boolean","updatedAttributes: Record<string, unknown>","updateMeProfile","caughtError: unknown","message: string","ThunderIDError","BaseUserProfile"],"sources":["../../../../../src/components/presentation/UserProfile/UserProfile.tsx"],"sourcesContent":["// Copyright 2025 The ThunderID Authors\n// SPDX-License-Identifier: Apache-2.0\n\nimport {ThunderIDError, User, deepMerge, resolveResourceEndpoint} from '@thunderid/browser';\nimport {FC, ReactElement, useState} from 'react';\n// eslint-disable-next-line import/no-named-as-default\nimport BaseUserProfile, {BaseUserProfileProps} from './BaseUserProfile';\nimport updateMeProfile from '../../../api/updateMeProfile';\nimport useThunderID from '../../../contexts/ThunderID/useThunderID';\nimport useUser from '../../../contexts/User/useUser';\nimport useTranslation from '../../../hooks/useTranslation';\n\n/**\n * Props for the UserProfile component.\n * Extends BaseUserProfileProps but makes the user prop optional since it will be obtained from useThunderID\n */\nexport type UserProfileProps = Omit<BaseUserProfileProps, 'user' | 'profile' | 'flattenedProfile'>;\n\n/**\n * UserProfile component displays the authenticated user's profile information in a\n * structured and styled format. It shows user details such as display name, email,\n * username, and other available profile information from ThunderID.\n *\n * This component is the React-specific implementation that uses the BaseUserProfile\n * and automatically retrieves the user data from ThunderID context if not provided.\n *\n * @example\n * ```tsx\n * // Basic usage - will use user from ThunderID context\n * <UserProfile />\n *\n * // With explicit user data\n * <UserProfile user={specificUser} />\n *\n * // With card layout and custom fallback\n * <UserProfile\n * cardLayout={true}\n * fallback={<div>Please sign in to view your profile</div>}\n * />\n *\n * // With field filtering - only show specific fields\n * <UserProfile\n * showFields={['name.givenName', 'name.familyName', 'emails']}\n * />\n *\n * // With field hiding - hide specific fields\n * <UserProfile\n * hideFields={['phoneNumbers', 'addresses']}\n * />\n * ```\n */\nconst UserProfile: FC<UserProfileProps> = ({preferences, editable = true, ...rest}: UserProfileProps): ReactElement => {\n const {baseUrl, endpoints, instanceId, preferences: contextPreferences} = useThunderID();\n const {profile, flattenedProfile, onUpdateProfile, userSchema} = useUser();\n const resolvedPreferences = {\n ...contextPreferences,\n ...preferences,\n user: {\n ...contextPreferences?.user,\n ...preferences?.user,\n },\n };\n const {t} = useTranslation(resolvedPreferences?.i18n);\n const isEditableProfile: boolean = resolvedPreferences?.user?.fetchUserProfile === false ? false : editable;\n\n const [error, setError] = useState<string | null>(null);\n\n const handleProfileUpdate = async (payload: Record<string, unknown>): Promise<void> => {\n setError(null);\n\n try {\n const updatedAttributes: Record<string, unknown> = deepMerge(\n (profile?.['attributes'] as Record<string, unknown>) ?? {},\n payload,\n );\n\n Object.keys(updatedAttributes).forEach((key) => {\n if (updatedAttributes[key] === undefined || updatedAttributes[key] === null) {\n delete updatedAttributes[key];\n }\n });\n\n const response: User = await updateMeProfile({\n baseUrl,\n url: resolveResourceEndpoint('usersMe', {endpoints}),\n instanceId,\n payload: updatedAttributes,\n });\n onUpdateProfile(response);\n } catch (caughtError: unknown) {\n let message: string = t('user.profile.update.generic.error');\n\n if (caughtError instanceof ThunderIDError) {\n message = caughtError?.message;\n }\n\n setError(message);\n }\n };\n\n return (\n <BaseUserProfile\n profile={profile ?? undefined}\n flattenedProfile={flattenedProfile ?? undefined}\n userSchema={userSchema ?? undefined}\n editable={isEditableProfile}\n onUpdate={isEditableProfile ? handleProfileUpdate : undefined}\n error={error}\n preferences={resolvedPreferences}\n {...rest}\n />\n );\n};\n\nexport default UserProfile;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAmDA,MAAMA,eAAqC,EAAC,aAAa,WAAW,KAAM,GAAG,WAA0C;CACrH,MAAM,EAAC,SAAS,WAAW,YAAY,aAAa,uBAAsBC,8BAAc;CACxF,MAAM,EAAC,SAAS,kBAAkB,iBAAiB,eAAcC,yBAAS;CAC1E,MAAM,sBAAsB;EAC1B,GAAG;EACH,GAAG;EACH,MAAM;GACJ,GAAG,oBAAoB;GACvB,GAAG,aAAa;GACjB;EACF;CACD,MAAM,EAAC,MAAKC,+BAAe,qBAAqB,KAAK;CACrD,MAAMC,oBAA6B,qBAAqB,MAAM,qBAAqB,QAAQ,QAAQ;CAEnG,MAAM,CAAC,OAAO,gCAAoC,KAAK;CAEvD,MAAM,sBAAsB,OAAO,YAAoD;AACrF,WAAS,KAAK;AAEd,MAAI;GACF,MAAMC,uDACH,UAAU,iBAA6C,EAAE,EAC1D,QACD;AAED,UAAO,KAAK,kBAAkB,CAAC,SAAS,QAAQ;AAC9C,QAAI,kBAAkB,SAAS,UAAa,kBAAkB,SAAS,KACrE,QAAO,kBAAkB;KAE3B;AAQF,mBANuB,MAAMC,gCAAgB;IAC3C;IACA,sDAA6B,WAAW,EAAC,WAAU,CAAC;IACpD;IACA,SAAS;IACV,CAAC,CACuB;WAClBC,aAAsB;GAC7B,IAAIC,UAAkB,EAAE,oCAAoC;AAE5D,OAAI,uBAAuBC,mCACzB,WAAU,aAAa;AAGzB,YAAS,QAAQ;;;AAIrB,QACE,2CAACC;EACC,SAAS,WAAW;EACpB,kBAAkB,oBAAoB;EACtC,YAAY,cAAc;EAC1B,UAAU;EACV,UAAU,oBAAoB,sBAAsB;EAC7C;EACP,aAAa;EACb,GAAI;GACJ;;AAIN,0BAAe"}
|
|
@@ -43,6 +43,7 @@ const ThunderIDContext = (0, react.createContext)({
|
|
|
43
43
|
signUp: () => Promise.resolve({}),
|
|
44
44
|
signUpUrl: void 0,
|
|
45
45
|
user: null,
|
|
46
|
+
userSchema: null,
|
|
46
47
|
vendor: __thunderid_browser.VendorConstants.VENDOR_PREFIX
|
|
47
48
|
});
|
|
48
49
|
ThunderIDContext.displayName = "ThunderIDContext";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ThunderIDContext.cjs","names":["ThunderIDContext: Context<ThunderIDContextProps | null>","VendorConstants"],"sources":["../../../../src/contexts/ThunderID/ThunderIDContext.ts"],"sourcesContent":["// Copyright 2025 The ThunderID Authors\n// SPDX-License-Identifier: Apache-2.0\n\nimport {\n FlowMetadataResponse,\n HttpRequestConfig,\n HttpResponse,\n IdToken,\n OIDCDiscoveryApiResponse,\n SignInOptions,\n TokenExchangeRequestConfig,\n TokenResponse,\n VendorConstants,\n} from '@thunderid/browser';\nimport {Context, createContext} from 'react';\nimport {ThunderIDReactConfig} from '../../models/config';\nimport ThunderIDReactClient from '../../ThunderIDReactClient';\n\n/**\n * Props interface of {@link ThunderIDContext}\n */\nexport type ThunderIDContextProps = {\n afterSignInUrl: string | undefined;\n applicationId: string | undefined;\n baseUrl: string | undefined;\n clientId: string | undefined;\n /**\n * Optional endpoint URL overrides from the client config. Used to resolve resource-server\n * endpoints (e.g. flow/user-management) independently of `baseUrl` — see `resolveResourceEndpoint`.\n */\n endpoints?: ThunderIDReactConfig['endpoints'];\n preferences?: ThunderIDReactConfig['preferences'];\n scopes: string | string[] | undefined;\n /**\n * OIDC discovery data.\n */\n discovery: {\n /**\n * The response from the `.well-known/openid-configuration` endpoint.\n * Contains server capabilities, supported endpoints, and metadata.\n * `null` while loading or when discovery has not been fetched.\n */\n wellKnown: OIDCDiscoveryApiResponse | null;\n };\n /**\n * Swaps the current access token with a new one based on the provided configuration (with a grant type).\n * @param config - Configuration for the token exchange request.\n * @returns A promise that resolves to the token response or the raw response.\n */\n exchangeToken: (config: TokenExchangeRequestConfig) => Promise<TokenResponse | Response>;\n /**\n * Retrieves the access token stored in the storage.\n * This function retrieves the access token and returns it.\n * @remarks This does not work in the `webWorker` or any other worker environment.\n * @returns A promise that resolves to the access token.\n */\n getAccessToken: () => Promise<string>;\n /**\n * Function to retrieve the decoded ID token.\n * This function decodes the ID token and returns its payload.\n * It can be used to access user claims and other information contained in the ID token.\n *\n * @returns A promise that resolves to the decoded ID token payload.\n */\n getDecodedIdToken: () => Promise<IdToken>;\n /**\n * Function to retrieve the ID token.\n * This function retrieves the ID token and returns it.\n *\n * @returns A promise that resolves to the ID token.\n */\n getIdToken: () => Promise<string>;\n /**\n * Returns the underlying StorageManager instance for reading and writing SDK-managed storage.\n */\n getStorageManager: () => Promise<any>;\n /**\n * HTTP request function to make API calls.\n * @param requestConfig - Configuration for the HTTP request.\n * @returns A promise that resolves to the HTTP response.\n */\n http: {\n /**\n * Makes an HTTP request using the provided configuration.\n * @param requestConfig - Configuration for the HTTP request.\n * @returns A promise that resolves to the HTTP response.\n */\n request: (requestConfig?: HttpRequestConfig) => Promise<HttpResponse<any>>;\n /**\n * Makes multiple HTTP requests based on the provided configuration.\n * @param requestConfigs - Set of configurations for the HTTP requests.\n * @returns A promise that resolves to an array of HTTP responses.\n */\n requestAll: (requestConfigs?: HttpRequestConfig[]) => Promise<HttpResponse<any>[]>;\n };\n /**\n * Instance ID for multi-instance support.\n */\n instanceId: number;\n isInitialized: boolean;\n /**\n * Flag indicating whether the SDK is working in the background.\n */\n isLoading: boolean;\n /**\n * Flag indicating whether flow metadata is currently being fetched.\n */\n isMetaLoading: boolean;\n /**\n * Flag indicating whether the user is signed in or not.\n */\n isSignedIn: boolean;\n\n /**\n * Flow metadata returned by `GET /flow/meta` (v2 platform only).\n * `null` while loading or when `resolveFromMeta` is disabled.\n */\n meta: FlowMetadataResponse | null;\n\n organizationHandle: string | undefined;\n\n /**\n * Re-initializes the client with a new configuration.\n *\n * @remarks\n * This can be partial configuration to update only specific fields.\n *\n * @param config - New configuration to re-initialize the client with.\n * @returns Promise resolving to boolean indicating success.\n */\n reInitialize: (config: Partial<ThunderIDReactConfig>) => Promise<boolean>;\n\n /**\n * Recovery function to initiate the account/password recovery flow.\n */\n recover: (...args: any[]) => Promise<any>;\n\n /**\n * Resolves `{{ t(key) }}` and `{{ meta(path) }}` template expressions in a string,\n * using the current i18n translation function and flow metadata from context.\n *\n * Useful in render-props patterns where consumers need to expand template strings\n * that come from the server (e.g. component labels, placeholders, headings).\n *\n * @example\n * const {resolveFlowTemplateLiterals} = useThunderID();\n * resolveFlowTemplateLiterals('{{ t(signin.heading.label) }}') // → 'Sign In'\n * resolveFlowTemplateLiterals('Login to {{ meta(application.name) }}') // → 'Login to My App'\n */\n resolveFlowTemplateLiterals: (text: string | undefined) => string;\n\n /**\n * Sign-in function to initiate the authentication process.\n * @remark This is the programmatic version of the `SignInButton` component.\n * TODO: Fix the types.\n */\n signIn: (...args: any) => Promise<any>;\n\n /**\n * Optional additional parameters to be sent in the sign-in request.\n * This can include custom parameters that your authorization server supports.\n * These parameters will be included in the authorization request sent to the server.\n * If not provided, no additional parameters will be sent.\n *\n * @example\n * signInOptions: { prompt: \"login\", fidp: \"OrganizationSSO\" }\n */\n signInOptions?: SignInOptions;\n\n /**\n * Optional token request configuration. params are appended to the token endpoint POST body.\n *\n * @example\n * tokenRequest: { params: { resource: \"https://api.example.com\" } }\n */\n tokenRequest?: {\n params?: Record<string, unknown>;\n };\n\n /**\n * Silent sign-in function to re-authenticate the user without user interaction.\n * @remark This is the programmatic version of the `SilentSignIn` component.\n */\n signInSilently: ThunderIDReactClient['signInSilently'];\n\n signInUrl: string | undefined;\n /**\n * Sign-out function to terminate the authentication session.\n * @remark This is the programmatic version of the `SignOutButton` component.\n * FIXME: Fix the types.\n */\n signOut: any;\n\n /**\n * Sign-up function to initiate the registration process.\n * @remark This is the programmatic version of the `SignUpButton` component.\n */\n signUp: (...args: any[]) => Promise<any>;\n\n signUpUrl: string | undefined;\n\n user: any;\n\n /**\n * Vendor/brand namespace used to prefix storage keys, cookie names, and CSS class names.\n * Resolved from the `vendor` config option, defaulting to `'thunderid'`.\n */\n vendor: string;\n} & Pick<ThunderIDReactClient, 'clearSession'>;\n\n/**\n * Context object for managing the Authentication flow builder core context.\n */\nconst ThunderIDContext: Context<ThunderIDContextProps | null> = createContext<null | ThunderIDContextProps>({\n afterSignInUrl: undefined,\n applicationId: undefined,\n baseUrl: undefined,\n clearSession: () => {},\n clientId: undefined,\n preferences: undefined,\n scopes: undefined,\n discovery: {\n wellKnown: null,\n },\n exchangeToken: null as unknown as ThunderIDContextProps['exchangeToken'],\n getAccessToken: null as unknown as ThunderIDContextProps['getAccessToken'],\n getDecodedIdToken: null as unknown as ThunderIDContextProps['getDecodedIdToken'],\n getIdToken: null as unknown as ThunderIDContextProps['getIdToken'],\n getStorageManager: () => Promise.resolve(null),\n http: {\n request: () => null as unknown as Promise<HttpResponse<any>>,\n requestAll: () => null as unknown as Promise<HttpResponse<any>[]>,\n },\n instanceId: 0,\n isInitialized: false,\n isLoading: true,\n isMetaLoading: false,\n isSignedIn: false,\n meta: null,\n organizationHandle: undefined,\n reInitialize: null as unknown as ThunderIDContextProps['reInitialize'],\n recover: () => Promise.resolve({} as any),\n resolveFlowTemplateLiterals: (text: string | undefined) => text ?? '',\n signIn: () => Promise.resolve({} as any),\n signInSilently: () => Promise.resolve({} as any),\n signInUrl: undefined,\n signOut: () => Promise.resolve({} as any),\n signUp: () => Promise.resolve({} as any),\n signUpUrl: undefined,\n user: null,\n vendor: VendorConstants.VENDOR_PREFIX,\n});\n\nThunderIDContext.displayName = 'ThunderIDContext';\n\nexport default ThunderIDContext;\n"],"mappings":";;;;;;;;;;AAqNA,MAAMA,4CAAsG;CAC1G,gBAAgB;CAChB,eAAe;CACf,SAAS;CACT,oBAAoB;CACpB,UAAU;CACV,aAAa;CACb,QAAQ;CACR,WAAW,EACT,WAAW,MACZ;CACD,eAAe;CACf,gBAAgB;CAChB,mBAAmB;CACnB,YAAY;CACZ,yBAAyB,QAAQ,QAAQ,KAAK;CAC9C,MAAM;EACJ,eAAe;EACf,kBAAkB;EACnB;CACD,YAAY;CACZ,eAAe;CACf,WAAW;CACX,eAAe;CACf,YAAY;CACZ,MAAM;CACN,oBAAoB;CACpB,cAAc;CACd,eAAe,QAAQ,QAAQ,EAAE,CAAQ;CACzC,8BAA8B,SAA6B,QAAQ;CACnE,cAAc,QAAQ,QAAQ,EAAE,CAAQ;CACxC,sBAAsB,QAAQ,QAAQ,EAAE,CAAQ;CAChD,WAAW;CACX,eAAe,QAAQ,QAAQ,EAAE,CAAQ;CACzC,cAAc,QAAQ,QAAQ,EAAE,CAAQ;CACxC,WAAW;CACX,MAAM;CACN,QAAQC,oCAAgB;CACzB,CAAC;AAEF,iBAAiB,cAAc;AAE/B,+BAAe"}
|
|
1
|
+
{"version":3,"file":"ThunderIDContext.cjs","names":["ThunderIDContext: Context<ThunderIDContextProps | null>","VendorConstants"],"sources":["../../../../src/contexts/ThunderID/ThunderIDContext.ts"],"sourcesContent":["// Copyright 2025 The ThunderID Authors\n// SPDX-License-Identifier: Apache-2.0\n\nimport {\n FlowMetadataResponse,\n HttpRequestConfig,\n HttpResponse,\n IdToken,\n OIDCDiscoveryApiResponse,\n SignInOptions,\n TokenExchangeRequestConfig,\n TokenResponse,\n VendorConstants,\n AttributeSchema,\n} from '@thunderid/browser';\nimport {Context, createContext} from 'react';\nimport {ThunderIDReactConfig} from '../../models/config';\nimport ThunderIDReactClient from '../../ThunderIDReactClient';\n\n/**\n * Props interface of {@link ThunderIDContext}\n */\nexport type ThunderIDContextProps = {\n afterSignInUrl: string | undefined;\n applicationId: string | undefined;\n baseUrl: string | undefined;\n clientId: string | undefined;\n /**\n * Optional endpoint URL overrides from the client config. Used to resolve resource-server\n * endpoints (e.g. flow/user-management) independently of `baseUrl` — see `resolveResourceEndpoint`.\n */\n endpoints?: ThunderIDReactConfig['endpoints'];\n preferences?: ThunderIDReactConfig['preferences'];\n scopes: string | string[] | undefined;\n /**\n * OIDC discovery data.\n */\n discovery: {\n /**\n * The response from the `.well-known/openid-configuration` endpoint.\n * Contains server capabilities, supported endpoints, and metadata.\n * `null` while loading or when discovery has not been fetched.\n */\n wellKnown: OIDCDiscoveryApiResponse | null;\n };\n /**\n * Swaps the current access token with a new one based on the provided configuration (with a grant type).\n * @param config - Configuration for the token exchange request.\n * @returns A promise that resolves to the token response or the raw response.\n */\n exchangeToken: (config: TokenExchangeRequestConfig) => Promise<TokenResponse | Response>;\n /**\n * Retrieves the access token stored in the storage.\n * This function retrieves the access token and returns it.\n * @remarks This does not work in the `webWorker` or any other worker environment.\n * @returns A promise that resolves to the access token.\n */\n getAccessToken: () => Promise<string>;\n /**\n * Function to retrieve the decoded ID token.\n * This function decodes the ID token and returns its payload.\n * It can be used to access user claims and other information contained in the ID token.\n *\n * @returns A promise that resolves to the decoded ID token payload.\n */\n getDecodedIdToken: () => Promise<IdToken>;\n /**\n * Function to retrieve the ID token.\n * This function retrieves the ID token and returns it.\n *\n * @returns A promise that resolves to the ID token.\n */\n getIdToken: () => Promise<string>;\n /**\n * Returns the underlying StorageManager instance for reading and writing SDK-managed storage.\n */\n getStorageManager: () => Promise<any>;\n /**\n * HTTP request function to make API calls.\n * @param requestConfig - Configuration for the HTTP request.\n * @returns A promise that resolves to the HTTP response.\n */\n http: {\n /**\n * Makes an HTTP request using the provided configuration.\n * @param requestConfig - Configuration for the HTTP request.\n * @returns A promise that resolves to the HTTP response.\n */\n request: (requestConfig?: HttpRequestConfig) => Promise<HttpResponse<any>>;\n /**\n * Makes multiple HTTP requests based on the provided configuration.\n * @param requestConfigs - Set of configurations for the HTTP requests.\n * @returns A promise that resolves to an array of HTTP responses.\n */\n requestAll: (requestConfigs?: HttpRequestConfig[]) => Promise<HttpResponse<any>[]>;\n };\n /**\n * Instance ID for multi-instance support.\n */\n instanceId: number;\n isInitialized: boolean;\n /**\n * Flag indicating whether the SDK is working in the background.\n */\n isLoading: boolean;\n /**\n * Flag indicating whether flow metadata is currently being fetched.\n */\n isMetaLoading: boolean;\n /**\n * Flag indicating whether the user is signed in or not.\n */\n isSignedIn: boolean;\n\n /**\n * Flow metadata returned by `GET /flow/meta` (v2 platform only).\n * `null` while loading or when `resolveFromMeta` is disabled.\n */\n meta: FlowMetadataResponse | null;\n\n organizationHandle: string | undefined;\n\n /**\n * Re-initializes the client with a new configuration.\n *\n * @remarks\n * This can be partial configuration to update only specific fields.\n *\n * @param config - New configuration to re-initialize the client with.\n * @returns Promise resolving to boolean indicating success.\n */\n reInitialize: (config: Partial<ThunderIDReactConfig>) => Promise<boolean>;\n\n /**\n * Recovery function to initiate the account/password recovery flow.\n */\n recover: (...args: any[]) => Promise<any>;\n\n /**\n * Resolves `{{ t(key) }}` and `{{ meta(path) }}` template expressions in a string,\n * using the current i18n translation function and flow metadata from context.\n *\n * Useful in render-props patterns where consumers need to expand template strings\n * that come from the server (e.g. component labels, placeholders, headings).\n *\n * @example\n * const {resolveFlowTemplateLiterals} = useThunderID();\n * resolveFlowTemplateLiterals('{{ t(signin.heading.label) }}') // → 'Sign In'\n * resolveFlowTemplateLiterals('Login to {{ meta(application.name) }}') // → 'Login to My App'\n */\n resolveFlowTemplateLiterals: (text: string | undefined) => string;\n\n /**\n * Sign-in function to initiate the authentication process.\n * @remark This is the programmatic version of the `SignInButton` component.\n * TODO: Fix the types.\n */\n signIn: (...args: any) => Promise<any>;\n\n /**\n * Optional additional parameters to be sent in the sign-in request.\n * This can include custom parameters that your authorization server supports.\n * These parameters will be included in the authorization request sent to the server.\n * If not provided, no additional parameters will be sent.\n *\n * @example\n * signInOptions: { prompt: \"login\", fidp: \"OrganizationSSO\" }\n */\n signInOptions?: SignInOptions;\n\n /**\n * Optional token request configuration. params are appended to the token endpoint POST body.\n *\n * @example\n * tokenRequest: { params: { resource: \"https://api.example.com\" } }\n */\n tokenRequest?: {\n params?: Record<string, unknown>;\n };\n\n /**\n * Silent sign-in function to re-authenticate the user without user interaction.\n * @remark This is the programmatic version of the `SilentSignIn` component.\n */\n signInSilently: ThunderIDReactClient['signInSilently'];\n\n signInUrl: string | undefined;\n /**\n * Sign-out function to terminate the authentication session.\n * @remark This is the programmatic version of the `SignOutButton` component.\n * FIXME: Fix the types.\n */\n signOut: any;\n\n /**\n * Sign-up function to initiate the registration process.\n * @remark This is the programmatic version of the `SignUpButton` component.\n */\n signUp: (...args: any[]) => Promise<any>;\n\n signUpUrl: string | undefined;\n\n user: any;\n\n /**\n * Schema metadata for the user type returned by GET /users/me/meta.\n */\n userSchema: Record<string, AttributeSchema> | null;\n\n /**\n * Vendor/brand namespace used to prefix storage keys, cookie names, and CSS class names.\n * Resolved from the `vendor` config option, defaulting to `'thunderid'`.\n */\n vendor: string;\n} & Pick<ThunderIDReactClient, 'clearSession'>;\n\n/**\n * Context object for managing the Authentication flow builder core context.\n */\nconst ThunderIDContext: Context<ThunderIDContextProps | null> = createContext<null | ThunderIDContextProps>({\n afterSignInUrl: undefined,\n applicationId: undefined,\n baseUrl: undefined,\n clearSession: () => {},\n clientId: undefined,\n preferences: undefined,\n scopes: undefined,\n discovery: {\n wellKnown: null,\n },\n exchangeToken: null as unknown as ThunderIDContextProps['exchangeToken'],\n getAccessToken: null as unknown as ThunderIDContextProps['getAccessToken'],\n getDecodedIdToken: null as unknown as ThunderIDContextProps['getDecodedIdToken'],\n getIdToken: null as unknown as ThunderIDContextProps['getIdToken'],\n getStorageManager: () => Promise.resolve(null),\n http: {\n request: () => null as unknown as Promise<HttpResponse<any>>,\n requestAll: () => null as unknown as Promise<HttpResponse<any>[]>,\n },\n instanceId: 0,\n isInitialized: false,\n isLoading: true,\n isMetaLoading: false,\n isSignedIn: false,\n meta: null,\n organizationHandle: undefined,\n reInitialize: null as unknown as ThunderIDContextProps['reInitialize'],\n recover: () => Promise.resolve({} as any),\n resolveFlowTemplateLiterals: (text: string | undefined) => text ?? '',\n signIn: () => Promise.resolve({} as any),\n signInSilently: () => Promise.resolve({} as any),\n signInUrl: undefined,\n signOut: () => Promise.resolve({} as any),\n signUp: () => Promise.resolve({} as any),\n signUpUrl: undefined,\n user: null,\n userSchema: null,\n vendor: VendorConstants.VENDOR_PREFIX,\n});\n\nThunderIDContext.displayName = 'ThunderIDContext';\n\nexport default ThunderIDContext;\n"],"mappings":";;;;;;;;;;AA2NA,MAAMA,4CAAsG;CAC1G,gBAAgB;CAChB,eAAe;CACf,SAAS;CACT,oBAAoB;CACpB,UAAU;CACV,aAAa;CACb,QAAQ;CACR,WAAW,EACT,WAAW,MACZ;CACD,eAAe;CACf,gBAAgB;CAChB,mBAAmB;CACnB,YAAY;CACZ,yBAAyB,QAAQ,QAAQ,KAAK;CAC9C,MAAM;EACJ,eAAe;EACf,kBAAkB;EACnB;CACD,YAAY;CACZ,eAAe;CACf,WAAW;CACX,eAAe;CACf,YAAY;CACZ,MAAM;CACN,oBAAoB;CACpB,cAAc;CACd,eAAe,QAAQ,QAAQ,EAAE,CAAQ;CACzC,8BAA8B,SAA6B,QAAQ;CACnE,cAAc,QAAQ,QAAQ,EAAE,CAAQ;CACxC,sBAAsB,QAAQ,QAAQ,EAAE,CAAQ;CAChD,WAAW;CACX,eAAe,QAAQ,QAAQ,EAAE,CAAQ;CACzC,cAAc,QAAQ,QAAQ,EAAE,CAAQ;CACxC,WAAW;CACX,MAAM;CACN,YAAY;CACZ,QAAQC,oCAAgB;CACzB,CAAC;AAEF,iBAAiB,cAAc;AAE/B,+BAAe"}
|
|
@@ -10,6 +10,7 @@ const require_I18nProvider = require('../I18n/I18nProvider.cjs');
|
|
|
10
10
|
const require_ThemeProvider = require('../Theme/ThemeProvider.cjs');
|
|
11
11
|
const require_UserProvider = require('../User/UserProvider.cjs');
|
|
12
12
|
const require_getUsersMe = require('../../api/getUsersMe.cjs');
|
|
13
|
+
const require_getUsersMeMeta = require('../../api/getUsersMeMeta.cjs');
|
|
13
14
|
let __thunderid_browser = require("@thunderid/browser");
|
|
14
15
|
__thunderid_browser = require_rolldown_runtime.__toESM(__thunderid_browser);
|
|
15
16
|
let react = require("react");
|
|
@@ -30,6 +31,7 @@ const ThunderIDProvider = ({ afterSignInUrl, afterSignOutUrl, baseUrl: initialBa
|
|
|
30
31
|
const [isInitializedSync, setIsInitializedSync] = (0, react.useState)(false);
|
|
31
32
|
const [isLoadingSync, setIsLoadingSync] = (0, react.useState)(true);
|
|
32
33
|
const [userProfile, setUserProfile] = (0, react.useState)(null);
|
|
34
|
+
const [userSchema, setUserSchema] = (0, react.useState)(null);
|
|
33
35
|
const [baseUrl, setBaseUrl] = (0, react.useState)(initialBaseUrl ?? "");
|
|
34
36
|
const [config, setConfig] = (0, react.useState)({
|
|
35
37
|
afterSignInUrl: afterSignInUrl ?? window.location.origin,
|
|
@@ -75,19 +77,33 @@ const ThunderIDProvider = ({ afterSignInUrl, afterSignOutUrl, baseUrl: initialBa
|
|
|
75
77
|
const claims = (0, __thunderid_browser.extractUserClaimsFromIdToken)(decodedToken);
|
|
76
78
|
let profileData = claims;
|
|
77
79
|
const currentSignInStatus = await client.isSignedIn();
|
|
78
|
-
if (currentSignInStatus && shouldFetchProfile)
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
80
|
+
if (currentSignInStatus && shouldFetchProfile) {
|
|
81
|
+
try {
|
|
82
|
+
const fetchedProfile = await require_getUsersMe.default({
|
|
83
|
+
baseUrl: resolvedBaseUrl,
|
|
84
|
+
url: (0, __thunderid_browser.resolveResourceEndpoint)("usersMe", config),
|
|
85
|
+
instanceId
|
|
86
|
+
});
|
|
87
|
+
profileData = {
|
|
88
|
+
...claims,
|
|
89
|
+
...fetchedProfile
|
|
90
|
+
};
|
|
91
|
+
} catch (err) {
|
|
92
|
+
logger.warn("Failed to fetch user profile from /users/me:", err);
|
|
93
|
+
}
|
|
94
|
+
try {
|
|
95
|
+
const metaRes = await require_getUsersMeMeta.default({
|
|
96
|
+
baseUrl: resolvedBaseUrl,
|
|
97
|
+
url: (0, __thunderid_browser.resolveResourceEndpoint)("usersMeMeta", config),
|
|
98
|
+
instanceId
|
|
99
|
+
});
|
|
100
|
+
if (metaRes?.schema) setUserSchema(metaRes.schema);
|
|
101
|
+
else setUserSchema(null);
|
|
102
|
+
} catch (err) {
|
|
103
|
+
setUserSchema(null);
|
|
104
|
+
logger.warn("Failed to fetch user schema metadata from /users/me/meta:", err);
|
|
105
|
+
}
|
|
106
|
+
} else setUserSchema(null);
|
|
91
107
|
setUser(profileData);
|
|
92
108
|
setUserProfile({
|
|
93
109
|
flattenedProfile: (0, __thunderid_browser.generateFlattenedUserProfile)(profileData, []),
|
|
@@ -296,6 +312,7 @@ const ThunderIDProvider = ({ afterSignInUrl, afterSignOutUrl, baseUrl: initialBa
|
|
|
296
312
|
signUpUrl,
|
|
297
313
|
syncSession,
|
|
298
314
|
user,
|
|
315
|
+
userSchema,
|
|
299
316
|
vendor: (0, __thunderid_browser.getVendorPrefix)(config.vendor)
|
|
300
317
|
}), [
|
|
301
318
|
applicationId,
|
|
@@ -315,6 +332,7 @@ const ThunderIDProvider = ({ afterSignInUrl, afterSignOutUrl, baseUrl: initialBa
|
|
|
315
332
|
signIn,
|
|
316
333
|
signInSilently,
|
|
317
334
|
user,
|
|
335
|
+
userSchema,
|
|
318
336
|
client,
|
|
319
337
|
signInOptions,
|
|
320
338
|
tokenRequest,
|
|
@@ -348,6 +366,7 @@ const ThunderIDProvider = ({ afterSignInUrl, afterSignOutUrl, baseUrl: initialBa
|
|
|
348
366
|
},
|
|
349
367
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_FlowProvider.default, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_UserProvider.default, {
|
|
350
368
|
profile: userProfile,
|
|
369
|
+
userSchema,
|
|
351
370
|
onUpdateProfile: handleProfileUpdate,
|
|
352
371
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_ComponentRendererProvider.default, {
|
|
353
372
|
renderers: extensions?.components?.renderers ?? {},
|