@thunderid/react 1.13.0 → 1.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/dist/api/auth/executeEmbeddedSelfFlow.d.ts +36 -0
  2. package/dist/api/auth/executeEmbeddedSelfFlow.d.ts.map +1 -0
  3. package/dist/api/auth/executeEmbeddedSelfFlow.js +30 -0
  4. package/dist/api/auth/executeEmbeddedSelfFlow.js.map +1 -0
  5. package/dist/cjs/api/auth/executeEmbeddedSelfFlow.cjs +32 -0
  6. package/dist/cjs/api/auth/executeEmbeddedSelfFlow.cjs.map +1 -0
  7. package/dist/cjs/components/account/UserProfile/BaseUserProfile.cjs +195 -46
  8. package/dist/cjs/components/account/UserProfile/BaseUserProfile.cjs.map +1 -1
  9. package/dist/cjs/components/account/UserProfile/BaseUserProfile.styles.cjs +57 -4
  10. package/dist/cjs/components/account/UserProfile/BaseUserProfile.styles.cjs.map +1 -1
  11. package/dist/cjs/components/account/UserProfile/UserProfile.cjs +13 -1
  12. package/dist/cjs/components/account/UserProfile/UserProfile.cjs.map +1 -1
  13. package/dist/cjs/components/account/VerifyAttribute/BaseVerifyAttribute.cjs +358 -0
  14. package/dist/cjs/components/account/VerifyAttribute/BaseVerifyAttribute.cjs.map +1 -0
  15. package/dist/cjs/components/account/VerifyAttribute/BaseVerifyAttribute.styles.cjs +84 -0
  16. package/dist/cjs/components/account/VerifyAttribute/BaseVerifyAttribute.styles.cjs.map +1 -0
  17. package/dist/cjs/components/account/VerifyAttribute/VerifyAttribute.cjs +94 -0
  18. package/dist/cjs/components/account/VerifyAttribute/VerifyAttribute.cjs.map +1 -0
  19. package/dist/cjs/components/auth/flow/AuthOptionFactory.cjs +20 -0
  20. package/dist/cjs/components/auth/flow/AuthOptionFactory.cjs.map +1 -1
  21. package/dist/cjs/contexts/ComponentRenderer/ComponentRendererContext.cjs.map +1 -1
  22. package/dist/cjs/contexts/ThunderID/ThunderIDProvider.cjs +14 -0
  23. package/dist/cjs/contexts/ThunderID/ThunderIDProvider.cjs.map +1 -1
  24. package/dist/cjs/index.cjs +30 -0
  25. package/dist/components/account/UserProfile/BaseUserProfile.d.ts +27 -1
  26. package/dist/components/account/UserProfile/BaseUserProfile.d.ts.map +1 -1
  27. package/dist/components/account/UserProfile/BaseUserProfile.js +197 -48
  28. package/dist/components/account/UserProfile/BaseUserProfile.js.map +1 -1
  29. package/dist/components/account/UserProfile/BaseUserProfile.styles.d.ts.map +1 -1
  30. package/dist/components/account/UserProfile/BaseUserProfile.styles.js +57 -4
  31. package/dist/components/account/UserProfile/BaseUserProfile.styles.js.map +1 -1
  32. package/dist/components/account/UserProfile/UserProfile.d.ts +7 -1
  33. package/dist/components/account/UserProfile/UserProfile.d.ts.map +1 -1
  34. package/dist/components/account/UserProfile/UserProfile.js +14 -2
  35. package/dist/components/account/UserProfile/UserProfile.js.map +1 -1
  36. package/dist/components/account/VerifyAttribute/BaseVerifyAttribute.d.ts +113 -0
  37. package/dist/components/account/VerifyAttribute/BaseVerifyAttribute.d.ts.map +1 -0
  38. package/dist/components/account/VerifyAttribute/BaseVerifyAttribute.js +354 -0
  39. package/dist/components/account/VerifyAttribute/BaseVerifyAttribute.js.map +1 -0
  40. package/dist/components/account/VerifyAttribute/BaseVerifyAttribute.styles.d.ts +10 -0
  41. package/dist/components/account/VerifyAttribute/BaseVerifyAttribute.styles.d.ts.map +1 -0
  42. package/dist/components/account/VerifyAttribute/BaseVerifyAttribute.styles.js +82 -0
  43. package/dist/components/account/VerifyAttribute/BaseVerifyAttribute.styles.js.map +1 -0
  44. package/dist/components/account/VerifyAttribute/VerifyAttribute.d.ts +41 -0
  45. package/dist/components/account/VerifyAttribute/VerifyAttribute.d.ts.map +1 -0
  46. package/dist/components/account/VerifyAttribute/VerifyAttribute.js +90 -0
  47. package/dist/components/account/VerifyAttribute/VerifyAttribute.js.map +1 -0
  48. package/dist/components/auth/flow/AuthOptionFactory.d.ts +5 -1
  49. package/dist/components/auth/flow/AuthOptionFactory.d.ts.map +1 -1
  50. package/dist/components/auth/flow/AuthOptionFactory.js +20 -1
  51. package/dist/components/auth/flow/AuthOptionFactory.js.map +1 -1
  52. package/dist/contexts/ComponentRenderer/ComponentRendererContext.d.ts +1 -1
  53. package/dist/contexts/ComponentRenderer/ComponentRendererContext.d.ts.map +1 -1
  54. package/dist/contexts/ComponentRenderer/ComponentRendererContext.js.map +1 -1
  55. package/dist/contexts/ThunderID/ThunderIDProvider.d.ts.map +1 -1
  56. package/dist/contexts/ThunderID/ThunderIDProvider.js +14 -0
  57. package/dist/contexts/ThunderID/ThunderIDProvider.js.map +1 -1
  58. package/dist/index.d.ts +7 -1
  59. package/dist/index.d.ts.map +1 -1
  60. package/dist/index.js +5 -2
  61. package/package.json +2 -2
@@ -0,0 +1,36 @@
1
+ import { EmbeddedSelfFlowResponse, ExecuteEmbeddedSelfFlowConfig as BaseExecuteEmbeddedSelfFlowConfig } from '@thunderid/browser';
2
+ /**
3
+ * Configuration for the executeEmbeddedSelfFlow request (React-specific)
4
+ */
5
+ export interface ExecuteEmbeddedSelfFlowConfig extends Omit<BaseExecuteEmbeddedSelfFlowConfig, 'fetcher'> {
6
+ /**
7
+ * Optional custom fetcher function. If not provided, the ThunderID SPA client's httpClient is used,
8
+ * which attaches the signed-in user's access token.
9
+ */
10
+ fetcher?: (url: string, config: RequestInit) => Promise<Response>;
11
+ /**
12
+ * Optional instance ID for multi-instance support. Defaults to 0.
13
+ */
14
+ instanceId?: number;
15
+ }
16
+ /**
17
+ * Executes one step of a `SELF` flow for the signed-in user. Uses the ThunderID SPA
18
+ * client's httpClient by default, which attaches the access token the flow identifies the user by.
19
+ *
20
+ * @param config - Configuration object with URL, payload and optional request config.
21
+ * @returns A promise that resolves with the flow step.
22
+ * @example
23
+ * ```typescript
24
+ * const step = await executeEmbeddedSelfFlow({
25
+ * baseUrl: "https://localhost:8090",
26
+ * payload: {
27
+ * flowType: EmbeddedFlowType.Self,
28
+ * handle: "default-self-attribute-verification-flow",
29
+ * inputs: {attribute: "email"},
30
+ * },
31
+ * });
32
+ * ```
33
+ */
34
+ declare const executeEmbeddedSelfFlow: ({ fetcher, instanceId, ...requestConfig }: ExecuteEmbeddedSelfFlowConfig) => Promise<EmbeddedSelfFlowResponse>;
35
+ export default executeEmbeddedSelfFlow;
36
+ //# sourceMappingURL=executeEmbeddedSelfFlow.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"executeEmbeddedSelfFlow.d.ts","sourceRoot":"","sources":["../../../src/api/auth/executeEmbeddedSelfFlow.ts"],"names":[],"mappings":"AAGA,OAAO,EACL,wBAAwB,EAGxB,6BAA6B,IAAI,iCAAiC,EACnE,MAAM,oBAAoB,CAAC;AAE5B;;GAEG;AACH,MAAM,WAAW,6BAA8B,SAAQ,IAAI,CAAC,iCAAiC,EAAE,SAAS,CAAC;IACvG;;;OAGG;IACH,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;IAClE;;OAEG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,QAAA,MAAM,uBAAuB,GAAU,2CAIpC,6BAA6B,KAAG,OAAO,CAAC,wBAAwB,CAI/D,CAAC;AAEL,eAAe,uBAAuB,CAAC"}
@@ -0,0 +1,30 @@
1
+ import { createHttpClientFetcher, executeEmbeddedSelfFlow } from "@thunderid/browser";
2
+
3
+ //#region src/api/auth/executeEmbeddedSelfFlow.ts
4
+ /**
5
+ * Executes one step of a `SELF` flow for the signed-in user. Uses the ThunderID SPA
6
+ * client's httpClient by default, which attaches the access token the flow identifies the user by.
7
+ *
8
+ * @param config - Configuration object with URL, payload and optional request config.
9
+ * @returns A promise that resolves with the flow step.
10
+ * @example
11
+ * ```typescript
12
+ * const step = await executeEmbeddedSelfFlow({
13
+ * baseUrl: "https://localhost:8090",
14
+ * payload: {
15
+ * flowType: EmbeddedFlowType.Self,
16
+ * handle: "default-self-attribute-verification-flow",
17
+ * inputs: {attribute: "email"},
18
+ * },
19
+ * });
20
+ * ```
21
+ */
22
+ const executeEmbeddedSelfFlow$1 = async ({ fetcher, instanceId = 0,...requestConfig }) => executeEmbeddedSelfFlow({
23
+ ...requestConfig,
24
+ fetcher: fetcher ?? createHttpClientFetcher(instanceId)
25
+ });
26
+ var executeEmbeddedSelfFlow_default = executeEmbeddedSelfFlow$1;
27
+
28
+ //#endregion
29
+ export { executeEmbeddedSelfFlow_default as default };
30
+ //# sourceMappingURL=executeEmbeddedSelfFlow.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"executeEmbeddedSelfFlow.js","names":["executeEmbeddedSelfFlow","baseExecuteEmbeddedSelfFlow"],"sources":["../../../src/api/auth/executeEmbeddedSelfFlow.ts"],"sourcesContent":["// Copyright 2026 The ThunderID Authors\n// SPDX-License-Identifier: Apache-2.0\n\nimport {\n EmbeddedSelfFlowResponse,\n createHttpClientFetcher,\n executeEmbeddedSelfFlow as baseExecuteEmbeddedSelfFlow,\n ExecuteEmbeddedSelfFlowConfig as BaseExecuteEmbeddedSelfFlowConfig,\n} from '@thunderid/browser';\n\n/**\n * Configuration for the executeEmbeddedSelfFlow request (React-specific)\n */\nexport interface ExecuteEmbeddedSelfFlowConfig extends Omit<BaseExecuteEmbeddedSelfFlowConfig, 'fetcher'> {\n /**\n * Optional custom fetcher function. If not provided, the ThunderID SPA client's httpClient is used,\n * which attaches the signed-in user's access token.\n */\n fetcher?: (url: string, config: RequestInit) => Promise<Response>;\n /**\n * Optional instance ID for multi-instance support. Defaults to 0.\n */\n instanceId?: number;\n}\n\n/**\n * Executes one step of a `SELF` flow for the signed-in user. Uses the ThunderID SPA\n * client's httpClient by default, which attaches the access token the flow identifies the user by.\n *\n * @param config - Configuration object with URL, payload and optional request config.\n * @returns A promise that resolves with the flow step.\n * @example\n * ```typescript\n * const step = await executeEmbeddedSelfFlow({\n * baseUrl: \"https://localhost:8090\",\n * payload: {\n * flowType: EmbeddedFlowType.Self,\n * handle: \"default-self-attribute-verification-flow\",\n * inputs: {attribute: \"email\"},\n * },\n * });\n * ```\n */\nconst executeEmbeddedSelfFlow = async ({\n fetcher,\n instanceId = 0,\n ...requestConfig\n}: ExecuteEmbeddedSelfFlowConfig): Promise<EmbeddedSelfFlowResponse> =>\n baseExecuteEmbeddedSelfFlow({\n ...requestConfig,\n fetcher: fetcher ?? createHttpClientFetcher(instanceId),\n });\n\nexport default executeEmbeddedSelfFlow;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AA2CA,MAAMA,4BAA0B,OAAO,EACrC,SACA,aAAa,EACb,GAAG,oBAEHC,wBAA4B;CAC1B,GAAG;CACH,SAAS,WAAW,wBAAwB,WAAW;CACxD,CAAC;AAEJ,sCAAeD"}
@@ -0,0 +1,32 @@
1
+ const require_rolldown_runtime = require('../../_virtual/rolldown_runtime.cjs');
2
+ let __thunderid_browser = require("@thunderid/browser");
3
+ __thunderid_browser = require_rolldown_runtime.__toESM(__thunderid_browser);
4
+
5
+ //#region src/api/auth/executeEmbeddedSelfFlow.ts
6
+ /**
7
+ * Executes one step of a `SELF` flow for the signed-in user. Uses the ThunderID SPA
8
+ * client's httpClient by default, which attaches the access token the flow identifies the user by.
9
+ *
10
+ * @param config - Configuration object with URL, payload and optional request config.
11
+ * @returns A promise that resolves with the flow step.
12
+ * @example
13
+ * ```typescript
14
+ * const step = await executeEmbeddedSelfFlow({
15
+ * baseUrl: "https://localhost:8090",
16
+ * payload: {
17
+ * flowType: EmbeddedFlowType.Self,
18
+ * handle: "default-self-attribute-verification-flow",
19
+ * inputs: {attribute: "email"},
20
+ * },
21
+ * });
22
+ * ```
23
+ */
24
+ const executeEmbeddedSelfFlow = async ({ fetcher, instanceId = 0,...requestConfig }) => (0, __thunderid_browser.executeEmbeddedSelfFlow)({
25
+ ...requestConfig,
26
+ fetcher: fetcher ?? (0, __thunderid_browser.createHttpClientFetcher)(instanceId)
27
+ });
28
+ var executeEmbeddedSelfFlow_default = executeEmbeddedSelfFlow;
29
+
30
+ //#endregion
31
+ exports.default = executeEmbeddedSelfFlow_default;
32
+ //# sourceMappingURL=executeEmbeddedSelfFlow.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"executeEmbeddedSelfFlow.cjs","names":[],"sources":["../../../../src/api/auth/executeEmbeddedSelfFlow.ts"],"sourcesContent":["// Copyright 2026 The ThunderID Authors\n// SPDX-License-Identifier: Apache-2.0\n\nimport {\n EmbeddedSelfFlowResponse,\n createHttpClientFetcher,\n executeEmbeddedSelfFlow as baseExecuteEmbeddedSelfFlow,\n ExecuteEmbeddedSelfFlowConfig as BaseExecuteEmbeddedSelfFlowConfig,\n} from '@thunderid/browser';\n\n/**\n * Configuration for the executeEmbeddedSelfFlow request (React-specific)\n */\nexport interface ExecuteEmbeddedSelfFlowConfig extends Omit<BaseExecuteEmbeddedSelfFlowConfig, 'fetcher'> {\n /**\n * Optional custom fetcher function. If not provided, the ThunderID SPA client's httpClient is used,\n * which attaches the signed-in user's access token.\n */\n fetcher?: (url: string, config: RequestInit) => Promise<Response>;\n /**\n * Optional instance ID for multi-instance support. Defaults to 0.\n */\n instanceId?: number;\n}\n\n/**\n * Executes one step of a `SELF` flow for the signed-in user. Uses the ThunderID SPA\n * client's httpClient by default, which attaches the access token the flow identifies the user by.\n *\n * @param config - Configuration object with URL, payload and optional request config.\n * @returns A promise that resolves with the flow step.\n * @example\n * ```typescript\n * const step = await executeEmbeddedSelfFlow({\n * baseUrl: \"https://localhost:8090\",\n * payload: {\n * flowType: EmbeddedFlowType.Self,\n * handle: \"default-self-attribute-verification-flow\",\n * inputs: {attribute: \"email\"},\n * },\n * });\n * ```\n */\nconst executeEmbeddedSelfFlow = async ({\n fetcher,\n instanceId = 0,\n ...requestConfig\n}: ExecuteEmbeddedSelfFlowConfig): Promise<EmbeddedSelfFlowResponse> =>\n baseExecuteEmbeddedSelfFlow({\n ...requestConfig,\n fetcher: fetcher ?? createHttpClientFetcher(instanceId),\n });\n\nexport default executeEmbeddedSelfFlow;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AA2CA,MAAM,0BAA0B,OAAO,EACrC,SACA,aAAa,EACb,GAAG,qEAEyB;CAC1B,GAAG;CACH,SAAS,4DAAmC,WAAW;CACxD,CAAC;AAEJ,sCAAe"}
@@ -5,6 +5,8 @@ const require_useTranslation = require('../../../hooks/useTranslation.cjs');
5
5
  const require_Button = require('../../primitives/Button/Button.cjs');
6
6
  const require_Typography = require('../../primitives/Typography/Typography.cjs');
7
7
  const require_Card = require('../../primitives/Card/Card.cjs');
8
+ const require_CircleAlert = require('../../primitives/Icons/CircleAlert.cjs');
9
+ const require_CircleCheck = require('../../primitives/Icons/CircleCheck.cjs');
8
10
  const require_Alert = require('../../primitives/Alert/Alert.cjs');
9
11
  const require_TextField = require('../../primitives/TextField/TextField.cjs');
10
12
  const require_Checkbox = require('../../primitives/Checkbox/Checkbox.cjs');
@@ -46,7 +48,8 @@ const fieldsToSkip = [
46
48
  "attributes",
47
49
  "picture",
48
50
  "isReadOnly",
49
- "isReadonly"
51
+ "isReadonly",
52
+ __thunderid_browser.AttributeVerificationConstants.VERIFIED_ATTRIBUTES_KEY
50
53
  ];
51
54
  const readonlyFields = [
52
55
  "attributes",
@@ -56,12 +59,44 @@ const readonlyFields = [
56
59
  "username",
57
60
  "sub"
58
61
  ];
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 = [] }) => {
62
+ const isStatusType = (type) => !!type && __thunderid_browser.AttributeVerificationConstants.VERIFIABLE_ATTRIBUTE_TYPES.includes(type.toLowerCase());
63
+ const isVerifiedAfterSave = (type) => !!type && __thunderid_browser.AttributeVerificationConstants.VERIFY_AFTER_SAVE_ATTRIBUTE_TYPES.includes(type.toLowerCase());
64
+ 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 = [], renderVerification = void 0 }) => {
60
65
  const { theme, colorScheme } = require_useTheme.default();
61
66
  const [editedUser, setEditedUser] = (0, react.useState)(flattenedProfile || profile);
62
67
  const [editingFields, setEditingFields] = (0, react.useState)({});
63
68
  const [fieldErrors, setFieldErrors] = (0, react.useState)({});
64
- const { t } = require_useTranslation.default(preferences?.i18n);
69
+ const [verifyingField, setVerifyingField] = (0, react.useState)(null);
70
+ const [verifiedNotice, setVerifiedNotice] = (0, react.useState)(null);
71
+ const [pendingVerification, setPendingVerification] = (0, react.useState)(null);
72
+ const [savingFields, setSavingFields] = (0, react.useState)({});
73
+ const { t, currentLanguage } = require_useTranslation.default(preferences?.i18n);
74
+ const reportedVerifiedAttributes = (0, react.useMemo)(() => (0, __thunderid_browser.getVerifiedAttributes)(profile ?? flattenedProfile), [profile, flattenedProfile]);
75
+ const supportsVerification = reportedVerifiedAttributes !== void 0;
76
+ const verifiedAttributes = (0, react.useMemo)(() => reportedVerifiedAttributes ?? {}, [reportedVerifiedAttributes]);
77
+ (0, react.useEffect)(() => {
78
+ if (!pendingVerification) return;
79
+ if (error) {
80
+ setPendingVerification(null);
81
+ return;
82
+ }
83
+ const { name, previousValue } = pendingVerification;
84
+ const storedUser = flattenedProfile ?? profile;
85
+ const storedValue = (storedUser?.["attributes"])?.[name] ?? storedUser?.[name];
86
+ if (storedValue === previousValue) return;
87
+ if (supportsVerification && typeof storedValue === "string" && storedValue.trim() !== "" && !verifiedAttributes[name]) {
88
+ setVerifiedNotice(null);
89
+ setVerifyingField(name);
90
+ }
91
+ setPendingVerification(null);
92
+ }, [
93
+ pendingVerification,
94
+ flattenedProfile,
95
+ profile,
96
+ error,
97
+ verifiedAttributes,
98
+ supportsVerification
99
+ ]);
65
100
  (0, react.useEffect)(() => {
66
101
  const nextUser = flattenedProfile ?? profile;
67
102
  if (!nextUser) return;
@@ -116,6 +151,7 @@ const BaseUserProfile = ({ fallback = null, className = "", cardLayout = true, p
116
151
  }, [t]);
117
152
  const formatLabel = (0, react.useCallback)((key) => key.split(/(?=[A-Z])|_/).map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join(" "), []);
118
153
  const styles = require_BaseUserProfile_styles.default(theme, colorScheme);
154
+ const verificationStyles = styles;
119
155
  const ObjectDisplay = ({ data }) => {
120
156
  if (!data || typeof data !== "object") return null;
121
157
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("table", {
@@ -176,15 +212,32 @@ const BaseUserProfile = ({ fallback = null, className = "", cardLayout = true, p
176
212
  });
177
213
  let payload = {};
178
214
  set(payload, fieldName, fieldValue);
179
- onUpdate(payload);
215
+ setSavingFields((prev) => ({
216
+ ...prev,
217
+ [fieldName]: true
218
+ }));
219
+ Promise.resolve(onUpdate(payload)).catch(() => setPendingVerification(null)).finally(() => setSavingFields((prev) => {
220
+ const next = { ...prev };
221
+ delete next[fieldName];
222
+ return next;
223
+ }));
180
224
  toggleFieldEdit(fieldName);
225
+ const savedUser = flattenedProfile ?? profile;
226
+ const storedValue = (savedUser?.["attributes"])?.[fieldName] ?? savedUser?.[fieldName];
227
+ if (renderVerification && isVerifiedAfterSave(schema.type) && (0, __thunderid_browser.isVerifiableAttribute)(userSchema, fieldName) && typeof fieldValue === "string" && fieldValue.trim() !== "" && fieldValue !== storedValue) setPendingVerification({
228
+ name: fieldName,
229
+ previousValue: storedValue
230
+ });
231
+ else setPendingVerification(null);
181
232
  }, [
182
233
  editedUser,
183
234
  flattenedProfile,
184
235
  profile,
185
236
  onUpdate,
186
237
  toggleFieldEdit,
187
- t
238
+ t,
239
+ renderVerification,
240
+ userSchema
188
241
  ]);
189
242
  const handleFieldCancel = (0, react.useCallback)((fieldName) => {
190
243
  const currentUser$1 = flattenedProfile || profile;
@@ -363,53 +416,142 @@ const BaseUserProfile = ({ fallback = null, className = "", cardLayout = true, p
363
416
  }) : displayValue
364
417
  })] });
365
418
  };
419
+ const renderVerificationStatus = (schema) => {
420
+ const verification = verifiedAttributes[schema.name];
421
+ let verifiedAtTitle;
422
+ if (verification?.verifiedAt) {
423
+ const verifiedAt = new Date(verification.verifiedAt);
424
+ verifiedAtTitle = t("user.verification.status.verified_at", { date: Number.isNaN(verifiedAt.getTime()) ? verification.verifiedAt : verifiedAt.toLocaleString(currentLanguage) });
425
+ }
426
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
427
+ className: require_emotion.cx((0, __thunderid_browser.withVendorCSSClassPrefix)((0, __thunderid_browser.bem)("user-profile", "verification-status")), (0, __thunderid_browser.withVendorCSSClassPrefix)((0, __thunderid_browser.bem)("user-profile", "verification-status", verification ? "verified" : "unverified")), verificationStyles["verificationStatus"], verification ? verificationStyles["verified"] : verificationStyles["unverified"]),
428
+ title: verifiedAtTitle,
429
+ "data-testid": `thunderid-verification-status-${schema.name}`,
430
+ children: [
431
+ verification ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_CircleCheck.default, {
432
+ width: 12,
433
+ height: 12,
434
+ "aria-hidden": "true"
435
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_CircleAlert.default, {
436
+ width: 12,
437
+ height: 12,
438
+ "aria-hidden": "true"
439
+ }),
440
+ verification ? t("user.verification.status.verified") : t("user.verification.status.unverified"),
441
+ verifiedAtTitle && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
442
+ className: verificationStyles["visuallyHidden"],
443
+ children: ` (${verifiedAtTitle})`
444
+ })
445
+ ]
446
+ });
447
+ };
448
+ const renderVerificationPanel = (schema) => {
449
+ if (!renderVerification || !schema.name || verifyingField !== schema.name) return null;
450
+ const attributeDisplayName = schema.displayName ?? (0, __thunderid_browser.startCase)(schema.name);
451
+ const panel = /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
452
+ className: require_emotion.cx((0, __thunderid_browser.withVendorCSSClassPrefix)((0, __thunderid_browser.bem)("user-profile", "verification")), verificationStyles["verification"], mode === "popup" ? verificationStyles["verificationInline"] : ""),
453
+ children: renderVerification({
454
+ attribute: schema.name,
455
+ attributeDisplayName,
456
+ onCancel: () => setVerifyingField(null),
457
+ onComplete: () => {
458
+ setVerifyingField(null);
459
+ setVerifiedNotice(attributeDisplayName);
460
+ }
461
+ })
462
+ });
463
+ if (mode === "popup") return panel;
464
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_Dialog.default, {
465
+ open: true,
466
+ onOpenChange: (isOpen) => {
467
+ if (!isOpen) setVerifyingField(null);
468
+ },
469
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_Dialog.default.Content, {
470
+ "aria-label": t("user.verification.heading", { attribute: attributeDisplayName }),
471
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
472
+ className: verificationStyles["verificationDialog"],
473
+ children: panel
474
+ })
475
+ })
476
+ });
477
+ };
366
478
  const renderUserInfo = (schema) => {
367
479
  if (!schema?.name) return null;
368
480
  const hasValue = schema.value !== void 0 && schema.value !== "" && schema.value !== null;
369
481
  const isFieldEditing = editingFields[schema.name];
370
482
  const isReadonlyField = readonlyFields.includes(schema.name);
371
483
  if (!(hasValue || isFieldEditing || editable && schema.mutability === "READ_WRITE")) return null;
484
+ const storedUser = flattenedProfile ?? profile;
485
+ const storedValue = (storedUser?.["attributes"])?.[schema.name] ?? storedUser?.[schema.name];
486
+ const showsVerificationStatus = supportsVerification && !!hasValue && !isFieldEditing && !savingFields[schema.name] && schema.value === storedValue && isStatusType(schema.type);
487
+ const canVerify = editable && !!renderVerification && showsVerificationStatus && !verifiedAttributes[schema.name] && verifyingField !== schema.name && (0, __thunderid_browser.isVerifiableAttribute)(userSchema, schema.name) && typeof schema.value === "string";
372
488
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
373
489
  className: styles.field,
374
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
375
- className: styles.fieldInner,
376
- children: [renderSchemaField(schema, isFieldEditing, (value) => {
377
- const tempEditedUser = { ...editedUser };
378
- tempEditedUser[schema.name] = value;
379
- setEditedUser(tempEditedUser);
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
- })]
389
- }), editable && schema.mutability !== "READ_ONLY" && !isReadonlyField && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
390
- className: styles.fieldActions,
391
- children: [isFieldEditing && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_Button.default, {
392
- size: "small",
393
- color: "primary",
394
- variant: "solid",
395
- onClick: () => handleFieldSave(schema),
396
- children: "Save"
397
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_Button.default, {
398
- size: "small",
399
- color: "secondary",
400
- variant: "solid",
401
- onClick: () => handleFieldCancel(schema.name),
402
- children: "Cancel"
403
- })] }), !isFieldEditing && hasValue && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_Button.default, {
404
- size: "small",
405
- color: "tertiary",
406
- variant: "icon",
407
- onClick: () => toggleFieldEdit(schema.name),
408
- title: "Edit",
409
- className: styles.editButton,
410
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PencilIcon, {})
411
- })]
412
- })]
490
+ children: [
491
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
492
+ className: styles.fieldInner,
493
+ children: [
494
+ renderSchemaField(schema, isFieldEditing, (value) => {
495
+ const tempEditedUser = { ...editedUser };
496
+ tempEditedUser[schema.name] = value;
497
+ setEditedUser(tempEditedUser);
498
+ }, () => toggleFieldEdit(schema.name)),
499
+ showsVerificationStatus && renderVerificationStatus(schema),
500
+ fieldErrors[schema.name] && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
501
+ style: {
502
+ color: "#d32f2f",
503
+ fontSize: "0.8rem",
504
+ marginTop: "4px",
505
+ fontWeight: 500
506
+ },
507
+ children: fieldErrors[schema.name]
508
+ })
509
+ ]
510
+ }),
511
+ canVerify && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
512
+ className: verificationStyles["fieldActions"],
513
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_Button.default, {
514
+ size: "small",
515
+ color: "primary",
516
+ variant: "outline",
517
+ onClick: () => {
518
+ setVerifiedNotice(null);
519
+ setVerifyingField(schema.name);
520
+ },
521
+ className: (0, __thunderid_browser.withVendorCSSClassPrefix)((0, __thunderid_browser.bem)("user-profile", "verify-button")),
522
+ "data-testid": `thunderid-verify-${schema.name}`,
523
+ children: t("user.verification.buttons.verify.text")
524
+ })
525
+ }),
526
+ editable && schema.mutability !== "READ_ONLY" && !isReadonlyField && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
527
+ className: styles.fieldActions,
528
+ children: [isFieldEditing && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_Button.default, {
529
+ size: "small",
530
+ color: "primary",
531
+ variant: "solid",
532
+ onClick: () => handleFieldSave(schema),
533
+ children: "Save"
534
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_Button.default, {
535
+ size: "small",
536
+ color: "secondary",
537
+ variant: "solid",
538
+ onClick: () => handleFieldCancel(schema.name),
539
+ children: "Cancel"
540
+ })] }), !isFieldEditing && hasValue && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_Button.default, {
541
+ size: "small",
542
+ color: "tertiary",
543
+ variant: "icon",
544
+ onClick: () => {
545
+ if (verifyingField === schema.name) setVerifyingField(null);
546
+ setVerifiedNotice(null);
547
+ toggleFieldEdit(schema.name);
548
+ },
549
+ title: "Edit",
550
+ className: styles.editButton,
551
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PencilIcon, {})
552
+ })]
553
+ })
554
+ ]
413
555
  });
414
556
  };
415
557
  if (!profile && !flattenedProfile) return fallback;
@@ -448,6 +590,13 @@ const BaseUserProfile = ({ fallback = null, className = "", cardLayout = true, p
448
590
  };
449
591
  });
450
592
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
593
+ verifiedNotice && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_Alert.default, {
594
+ variant: "success",
595
+ role: "status",
596
+ className: require_emotion.cx((0, __thunderid_browser.withVendorCSSClassPrefix)((0, __thunderid_browser.bem)("user-profile", "verification-notice")), verificationStyles["alert"]),
597
+ "data-testid": "thunderid-verification-notice",
598
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_Alert.default.Description, { children: t("user.verification.success", { attribute: verifiedNotice }) })
599
+ }),
451
600
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
452
601
  className: styles.profileSummary,
453
602
  children: [
@@ -471,9 +620,9 @@ const BaseUserProfile = ({ fallback = null, className = "", cardLayout = true, p
471
620
  ]
472
621
  }),
473
622
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_Divider.default, {}),
474
- schemaItems.map((schemaWithValue) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
623
+ schemaItems.map((schemaWithValue) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
475
624
  className: styles.info,
476
- children: renderUserInfo(schemaWithValue)
625
+ children: [renderUserInfo(schemaWithValue), renderVerificationPanel(schemaWithValue)]
477
626
  }, schemaWithValue.name))
478
627
  ] });
479
628
  };