@ollaid/native-sso 2.7.7 → 2.7.9

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/index.cjs CHANGED
@@ -11243,7 +11243,7 @@ function SuccessOrbit() {
11243
11243
  justifyContent: "center",
11244
11244
  zIndex: 2
11245
11245
  }, children: /* @__PURE__ */ jsxRuntime.jsx(IconLink, { style: { width: "1.7rem", height: "1.7rem", color: C$1.green } }) }),
11246
- orbitIcons.map((Icon, i) => {
11246
+ orbitIcons.map((Icon2, i) => {
11247
11247
  const angle = i * 360 / orbitIcons.length;
11248
11248
  return /* @__PURE__ */ jsxRuntime.jsx("div", { style: {
11249
11249
  position: "absolute",
@@ -11262,7 +11262,7 @@ function SuccessOrbit() {
11262
11262
  display: "flex",
11263
11263
  alignItems: "center",
11264
11264
  justifyContent: "center"
11265
- }, children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { style: { width: "1rem", height: "1rem", color: C$1.gray500 } }) }) }, i);
11265
+ }, children: /* @__PURE__ */ jsxRuntime.jsx(Icon2, { style: { width: "1rem", height: "1rem", color: C$1.gray500 } }) }) }, i);
11266
11266
  })
11267
11267
  ] });
11268
11268
  }
@@ -12146,26 +12146,41 @@ const C = {
12146
12146
  gray700: "#374151",
12147
12147
  white: "#ffffff"
12148
12148
  };
12149
- function OnboardingModal({ open, onOpenChange, onDismiss, user, variant = "missing", profileHydrating = false, onComplete, onSkip }) {
12149
+ function OnboardingModal({
12150
+ open,
12151
+ onOpenChange,
12152
+ onDismiss,
12153
+ user,
12154
+ variant = "missing",
12155
+ profileHydrating = false,
12156
+ hydrateProfile = true,
12157
+ onUserInfosSync,
12158
+ onComplete,
12159
+ onSkip
12160
+ }) {
12150
12161
  var _a;
12151
12162
  const isEditMode = variant === "edit";
12152
- const hasCurrentPhone = Boolean(user.phone);
12153
- const hasCurrentEmail = Boolean(user.email);
12163
+ const [hydratedUser, setHydratedUser] = react.useState(null);
12164
+ const [internalHydrating, setInternalHydrating] = react.useState(false);
12165
+ const hydrationKeyRef = react.useRef(null);
12166
+ const profileUser = hydratedUser ?? user;
12167
+ const hasCurrentPhone = Boolean(profileUser.phone);
12168
+ const hasCurrentEmail = Boolean(profileUser.email);
12154
12169
  const directPhoneEdit = !isEditMode || !hasCurrentPhone;
12155
12170
  const directEmailEdit = !isEditMode || !hasCurrentEmail;
12156
- !isEditMode ? !((_a = user.name) == null ? void 0 : _a.trim()) : true;
12157
- const [name, setName] = react.useState(user.name || "");
12171
+ !isEditMode ? !((_a = profileUser.name) == null ? void 0 : _a.trim()) : true;
12172
+ const [name, setName] = react.useState(profileUser.name || "");
12158
12173
  const [photoPreview, setPhotoPreview] = react.useState("");
12159
12174
  const [avatarPickerOpen, setAvatarPickerOpen] = react.useState(false);
12160
12175
  const [pendingAvatarSrc, setPendingAvatarSrc] = react.useState(null);
12161
12176
  const [avatarUploading, setAvatarUploading] = react.useState(false);
12162
12177
  const avatarFileInputRef = react.useRef(null);
12163
- const [ccphone, setCcphone] = react.useState(user.ccphone || "+221");
12164
- const [phone, setPhone] = react.useState(user.phone || "");
12165
- const [email, setEmail] = react.useState(user.email || "");
12166
- const [address, setAddress] = react.useState(user.address || "");
12167
- const [town, setTown] = react.useState(user.town || "");
12168
- const [country, setCountry] = react.useState(user.country || DEFAULT_COUNTRY_CODE);
12178
+ const [ccphone, setCcphone] = react.useState(profileUser.ccphone || "+221");
12179
+ const [phone, setPhone] = react.useState(profileUser.phone || "");
12180
+ const [email, setEmail] = react.useState(profileUser.email || "");
12181
+ const [address, setAddress] = react.useState(profileUser.address || "");
12182
+ const [town, setTown] = react.useState(profileUser.town || "");
12183
+ const [country, setCountry] = react.useState(profileUser.country || DEFAULT_COUNTRY_CODE);
12169
12184
  const [submitting, setSubmitting] = react.useState(false);
12170
12185
  const [fileError, setFileError] = react.useState("");
12171
12186
  const [contactFlow, setContactFlow] = react.useState(null);
@@ -12175,34 +12190,54 @@ function OnboardingModal({ open, onOpenChange, onDismiss, user, variant = "missi
12175
12190
  const [confirmAction, setConfirmAction] = react.useState(null);
12176
12191
  const [nowTick, setNowTick] = react.useState(Date.now());
12177
12192
  const userHydrationKey = [
12178
- user.reference || "",
12179
- user.name || "",
12180
- user.email || "",
12181
- user.ccphone || "",
12182
- user.phone || "",
12183
- user.address || "",
12184
- user.town || "",
12185
- user.country || "",
12186
- user.image_url || "",
12187
- user.auth_2fa ? "1" : "0"
12193
+ profileUser.reference || "",
12194
+ profileUser.name || "",
12195
+ profileUser.email || "",
12196
+ profileUser.ccphone || "",
12197
+ profileUser.phone || "",
12198
+ profileUser.address || "",
12199
+ profileUser.town || "",
12200
+ profileUser.country || "",
12201
+ profileUser.image_url || "",
12202
+ profileUser.auth_2fa ? "1" : "0"
12188
12203
  ].join("|");
12189
- const currentDialCode = (user.ccphone || ccphone || "+221").trim();
12204
+ const currentDialCode = (profileUser.ccphone || ccphone || "+221").trim();
12190
12205
  const baseSmsAllowed = currentDialCode === "+221";
12191
12206
  const contactSmsAllowed = contactFlow ? contactFlow.kind === "phone" ? contactFlow.newCcphone.trim() === "+221" : baseSmsAllowed : baseSmsAllowed;
12192
12207
  const lockModalClose = profileHydrating || submitting || submitState === "success" || avatarUploading || Boolean(contactFlow == null ? void 0 : contactFlow.loading);
12208
+ const syncFormFromUser = react.useCallback((nextUser) => {
12209
+ setName(nextUser.name || "");
12210
+ setPhotoPreview(isEditMode ? nextUser.image_url || "" : "");
12211
+ setCcphone(nextUser.ccphone || "+221");
12212
+ setPhone(nextUser.phone || "");
12213
+ setEmail(nextUser.email || "");
12214
+ setAddress(nextUser.address || "");
12215
+ setTown(nextUser.town || "");
12216
+ setCountry(nextUser.country || DEFAULT_COUNTRY_CODE);
12217
+ }, [isEditMode]);
12218
+ const toUserInfos = react.useCallback((nextUser) => ({
12219
+ reference: nextUser.reference,
12220
+ name: nextUser.name,
12221
+ email: nextUser.email || null,
12222
+ ccphone: nextUser.ccphone,
12223
+ phone: nextUser.phone,
12224
+ address: nextUser.address,
12225
+ town: nextUser.town,
12226
+ country: nextUser.country,
12227
+ image_url: nextUser.image_url,
12228
+ auth_2fa: nextUser.auth_2fa,
12229
+ alias_reference: nextUser.alias_reference,
12230
+ iam_reference: nextUser.iam_reference
12231
+ }), []);
12193
12232
  react.useEffect(() => {
12194
- if (!open) return;
12195
- setName(user.name || "");
12196
- setPhotoPreview(isEditMode ? user.image_url || "" : "");
12233
+ if (!open) {
12234
+ setHydratedUser(null);
12235
+ hydrationKeyRef.current = null;
12236
+ return;
12237
+ }
12197
12238
  setPendingAvatarSrc(null);
12198
12239
  setAvatarPickerOpen(false);
12199
12240
  setAvatarUploading(false);
12200
- setCcphone(user.ccphone || "+221");
12201
- setPhone(user.phone || "");
12202
- setEmail(user.email || "");
12203
- setAddress(user.address || "");
12204
- setTown(user.town || "");
12205
- setCountry(user.country || DEFAULT_COUNTRY_CODE);
12206
12241
  setSubmitting(false);
12207
12242
  setFileError("");
12208
12243
  setContactFlow(null);
@@ -12210,7 +12245,54 @@ function OnboardingModal({ open, onOpenChange, onDismiss, user, variant = "missi
12210
12245
  setSubmitMessage("");
12211
12246
  setSubmitError("");
12212
12247
  setConfirmAction(null);
12213
- }, [open, isEditMode, userHydrationKey, user.name, user.image_url, user.ccphone, user.phone, user.email, user.address, user.town, user.country]);
12248
+ syncFormFromUser(profileUser);
12249
+ }, [open, userHydrationKey, profileUser, syncFormFromUser]);
12250
+ react.useEffect(() => {
12251
+ if (!open || !hydratedUser) return;
12252
+ if (contactFlow || submitting || submitState !== "idle") return;
12253
+ syncFormFromUser(hydratedUser);
12254
+ }, [open, hydratedUser, contactFlow, submitting, submitState, syncFormFromUser]);
12255
+ react.useEffect(() => {
12256
+ if (!open || !hydrateProfile || profileHydrating || internalHydrating || hydratedUser) return;
12257
+ const key = userHydrationKey;
12258
+ if (hydrationKeyRef.current === key) return;
12259
+ hydrationKeyRef.current = key;
12260
+ let cancelled = false;
12261
+ setInternalHydrating(true);
12262
+ (async () => {
12263
+ try {
12264
+ const response = await profileService.getProfile();
12265
+ const payload = response.user_infos || response.user || {};
12266
+ const nextUser = {
12267
+ ...profileUser,
12268
+ name: payload.name || profileUser.name,
12269
+ email: payload.email ?? profileUser.email ?? null,
12270
+ ccphone: payload.ccphone || profileUser.ccphone,
12271
+ phone: payload.phone || profileUser.phone,
12272
+ address: payload.address || profileUser.address,
12273
+ town: payload.town || profileUser.town,
12274
+ country: payload.country || profileUser.country,
12275
+ image_url: payload.image_url || profileUser.image_url,
12276
+ auth_2fa: payload.auth_2fa ?? profileUser.auth_2fa,
12277
+ alias_reference: payload.alias_reference || profileUser.alias_reference,
12278
+ iam_reference: payload.iam_reference || profileUser.iam_reference
12279
+ };
12280
+ if (cancelled) return;
12281
+ setHydratedUser(nextUser);
12282
+ syncFormFromUser(nextUser);
12283
+ onUserInfosSync == null ? void 0 : onUserInfosSync({ source: "hydration", user_infos: toUserInfos(nextUser) });
12284
+ } catch {
12285
+ if (cancelled) return;
12286
+ } finally {
12287
+ if (!cancelled) {
12288
+ setInternalHydrating(false);
12289
+ }
12290
+ }
12291
+ })();
12292
+ return () => {
12293
+ cancelled = true;
12294
+ };
12295
+ }, [open, hydrateProfile, profileHydrating, internalHydrating, userHydrationKey, profileUser, onUserInfosSync, syncFormFromUser, toUserInfos]);
12214
12296
  react.useEffect(() => {
12215
12297
  if (!(contactFlow == null ? void 0 : contactFlow.resendAvailableAt)) return;
12216
12298
  const timer = window.setInterval(() => {
@@ -12246,9 +12328,9 @@ function OnboardingModal({ open, onOpenChange, onDismiss, user, variant = "missi
12246
12328
  setSubmitError("");
12247
12329
  const data = {
12248
12330
  name: name.trim(),
12249
- ccphone: directPhoneEdit ? ccphone : user.ccphone || void 0,
12250
- phone: directPhoneEdit ? phone : user.phone || void 0,
12251
- email: directEmailEdit ? email.trim() : user.email || void 0,
12331
+ ccphone: directPhoneEdit ? ccphone : profileUser.ccphone || void 0,
12332
+ phone: directPhoneEdit ? phone : profileUser.phone || void 0,
12333
+ email: directEmailEdit ? email.trim() : profileUser.email || void 0,
12252
12334
  ...address.trim() ? { address: address.trim() } : {},
12253
12335
  ...town.trim() ? { town: town.trim() } : {},
12254
12336
  ...country ? { country } : {}
@@ -12257,19 +12339,21 @@ function OnboardingModal({ open, onOpenChange, onDismiss, user, variant = "missi
12257
12339
  const response = await profileService.updateProfile(data);
12258
12340
  const payload = response.user_infos || response.user || {};
12259
12341
  const updatedUser = {
12260
- ...user,
12261
- name: payload.name || data.name || user.name,
12262
- email: payload.email ?? data.email ?? user.email,
12263
- ccphone: payload.ccphone || data.ccphone || user.ccphone,
12264
- phone: payload.phone || data.phone || user.phone,
12265
- address: payload.address || data.address || user.address,
12266
- town: payload.town || data.town || user.town,
12267
- country: payload.country || data.country || user.country,
12268
- image_url: payload.image_url || user.image_url,
12269
- auth_2fa: payload.auth_2fa ?? user.auth_2fa,
12270
- alias_reference: payload.alias_reference || user.alias_reference,
12271
- iam_reference: payload.iam_reference || user.iam_reference
12342
+ ...profileUser,
12343
+ name: payload.name || data.name || profileUser.name,
12344
+ email: payload.email ?? data.email ?? profileUser.email,
12345
+ ccphone: payload.ccphone || data.ccphone || profileUser.ccphone,
12346
+ phone: payload.phone || data.phone || profileUser.phone,
12347
+ address: payload.address || data.address || profileUser.address,
12348
+ town: payload.town || data.town || profileUser.town,
12349
+ country: payload.country || data.country || profileUser.country,
12350
+ image_url: payload.image_url || profileUser.image_url,
12351
+ auth_2fa: payload.auth_2fa ?? profileUser.auth_2fa,
12352
+ alias_reference: payload.alias_reference || profileUser.alias_reference,
12353
+ iam_reference: payload.iam_reference || profileUser.iam_reference
12272
12354
  };
12355
+ setHydratedUser(updatedUser);
12356
+ onUserInfosSync == null ? void 0 : onUserInfosSync({ source: "update", user_infos: toUserInfos(updatedUser) });
12273
12357
  onComplete({
12274
12358
  name: updatedUser.name,
12275
12359
  image_url: updatedUser.image_url,
@@ -12300,7 +12384,7 @@ function OnboardingModal({ open, onOpenChange, onDismiss, user, variant = "missi
12300
12384
  setSubmitState("error");
12301
12385
  setSubmitError(error instanceof Error ? error.message : "Erreur lors de l’enregistrement");
12302
12386
  }
12303
- }, [canSubmit, user, name, directPhoneEdit, directEmailEdit, ccphone, phone, email, address, town, country, onComplete, markSuccess]);
12387
+ }, [canSubmit, profileUser, name, directPhoneEdit, directEmailEdit, ccphone, phone, email, address, town, country, onComplete, onUserInfosSync, toUserInfos, markSuccess]);
12304
12388
  const openAvatarPicker = react.useCallback(() => {
12305
12389
  var _a2;
12306
12390
  (_a2 = avatarFileInputRef.current) == null ? void 0 : _a2.click();
@@ -12330,6 +12414,23 @@ function OnboardingModal({ open, onOpenChange, onDismiss, user, variant = "missi
12330
12414
  setPhotoPreview(URL.createObjectURL(blob));
12331
12415
  }
12332
12416
  if (Object.keys(payload).length > 0) {
12417
+ const updatedUser = {
12418
+ ...profileUser,
12419
+ reference: payload.reference || profileUser.reference,
12420
+ name: payload.name || profileUser.name,
12421
+ email: payload.email ?? profileUser.email ?? null,
12422
+ ccphone: payload.ccphone || profileUser.ccphone,
12423
+ phone: payload.phone || profileUser.phone,
12424
+ address: payload.address || profileUser.address,
12425
+ town: payload.town || profileUser.town,
12426
+ country: payload.country || profileUser.country,
12427
+ image_url: nextImage || profileUser.image_url,
12428
+ auth_2fa: payload.auth_2fa ?? profileUser.auth_2fa,
12429
+ alias_reference: payload.alias_reference || profileUser.alias_reference,
12430
+ iam_reference: payload.iam_reference || profileUser.iam_reference
12431
+ };
12432
+ setHydratedUser(updatedUser);
12433
+ onUserInfosSync == null ? void 0 : onUserInfosSync({ source: "avatar", user_infos: toUserInfos(updatedUser) });
12333
12434
  onComplete({
12334
12435
  name: payload.name,
12335
12436
  image_url: nextImage || void 0,
@@ -12340,18 +12441,18 @@ function OnboardingModal({ open, onOpenChange, onDismiss, user, variant = "missi
12340
12441
  town: payload.town || void 0,
12341
12442
  country: payload.country || void 0,
12342
12443
  user_infos: {
12343
- reference: payload.reference || user.reference,
12344
- name: payload.name || user.name,
12345
- email: payload.email ?? user.email ?? null,
12346
- ccphone: payload.ccphone || user.ccphone,
12347
- phone: payload.phone || user.phone,
12348
- address: payload.address || user.address,
12349
- town: payload.town || user.town,
12350
- country: payload.country || user.country,
12351
- image_url: nextImage || user.image_url,
12352
- auth_2fa: payload.auth_2fa ?? user.auth_2fa,
12353
- alias_reference: payload.alias_reference || user.alias_reference,
12354
- iam_reference: payload.iam_reference || user.iam_reference
12444
+ reference: payload.reference || profileUser.reference,
12445
+ name: payload.name || profileUser.name,
12446
+ email: payload.email ?? profileUser.email ?? null,
12447
+ ccphone: payload.ccphone || profileUser.ccphone,
12448
+ phone: payload.phone || profileUser.phone,
12449
+ address: payload.address || profileUser.address,
12450
+ town: payload.town || profileUser.town,
12451
+ country: payload.country || profileUser.country,
12452
+ image_url: nextImage || profileUser.image_url,
12453
+ auth_2fa: payload.auth_2fa ?? profileUser.auth_2fa,
12454
+ alias_reference: payload.alias_reference || profileUser.alias_reference,
12455
+ iam_reference: payload.iam_reference || profileUser.iam_reference
12355
12456
  }
12356
12457
  });
12357
12458
  }
@@ -12361,7 +12462,7 @@ function OnboardingModal({ open, onOpenChange, onDismiss, user, variant = "missi
12361
12462
  } finally {
12362
12463
  setAvatarUploading(false);
12363
12464
  }
12364
- }, [onComplete, markSuccess, user.reference, user.name, user.email, user.ccphone, user.phone, user.address, user.town, user.country, user.image_url, user.auth_2fa, user.alias_reference, user.iam_reference]);
12465
+ }, [onComplete, onUserInfosSync, toUserInfos, markSuccess, profileUser.reference, profileUser.name, profileUser.email, profileUser.ccphone, profileUser.phone, profileUser.address, profileUser.town, profileUser.country, profileUser.image_url, profileUser.auth_2fa, profileUser.alias_reference, profileUser.iam_reference]);
12365
12466
  const openContactFlow = react.useCallback((kind) => {
12366
12467
  setContactFlow({
12367
12468
  kind,
@@ -12369,7 +12470,7 @@ function OnboardingModal({ open, onOpenChange, onDismiss, user, variant = "missi
12369
12470
  requestId: null,
12370
12471
  method: kind === "email" ? "email" : currentDialCode === "+221" ? "phone" : "email",
12371
12472
  newEmail: "",
12372
- newCcphone: kind === "phone" ? user.ccphone || "+221" : "+221",
12473
+ newCcphone: kind === "phone" ? profileUser.ccphone || "+221" : "+221",
12373
12474
  newPhone: "",
12374
12475
  oldOtp: "",
12375
12476
  newOtp: "",
@@ -12377,18 +12478,18 @@ function OnboardingModal({ open, onOpenChange, onDismiss, user, variant = "missi
12377
12478
  error: "",
12378
12479
  resendAvailableAt: null
12379
12480
  });
12380
- }, [currentDialCode, user.ccphone]);
12481
+ }, [currentDialCode, profileUser.ccphone]);
12381
12482
  const closeContactFlow = react.useCallback(() => {
12382
12483
  setContactFlow(null);
12383
12484
  }, []);
12384
12485
  const updateFlow = react.useCallback((patch) => {
12385
12486
  setContactFlow((prev) => prev ? { ...prev, ...patch } : prev);
12386
12487
  }, []);
12387
- const currentPhoneDialDisplay = user.ccphone || ccphone || "+221";
12388
- const currentPhoneNumberDisplay = user.phone || phone || "";
12389
- const currentEmailLabel = user.email || email || "";
12488
+ const currentPhoneDialDisplay = profileUser.ccphone || ccphone || "+221";
12489
+ const currentPhoneNumberDisplay = profileUser.phone || phone || "";
12490
+ const currentEmailLabel = profileUser.email || email || "";
12390
12491
  const currentPhoneDialCode = currentPhoneDialDisplay.trim() || "+221";
12391
- const currentPhoneDigits = (user.phone || phone || "").replace(/\D/g, "");
12492
+ const currentPhoneDigits = (profileUser.phone || phone || "").replace(/\D/g, "");
12392
12493
  const formatPhoneDisplay = react.useCallback((dial, digits) => {
12393
12494
  const cleanDial = (dial || "+221").trim() || "+221";
12394
12495
  const cleanDigits = (digits || "").replace(/\D/g, "");
@@ -12444,13 +12545,15 @@ function OnboardingModal({ open, onOpenChange, onDismiss, user, variant = "missi
12444
12545
  const response = await profileChangeService.verifyNewOTP(contactFlow.requestId, contactFlow.newOtp);
12445
12546
  if (!response.success) throw new Error(response.message || "Code OTP incorrect");
12446
12547
  const updatedUser = {
12447
- ...user,
12548
+ ...profileUser,
12448
12549
  ...contactFlow.kind === "email" ? { email: contactFlow.newEmail.trim() } : { ccphone: contactFlow.newCcphone, phone: contactFlow.newPhone.trim() }
12449
12550
  };
12450
12551
  if (response.user && typeof response.user === "object") {
12451
12552
  Object.assign(updatedUser, response.user);
12452
12553
  }
12554
+ setHydratedUser(updatedUser);
12453
12555
  setContactFlow(null);
12556
+ onUserInfosSync == null ? void 0 : onUserInfosSync({ source: "contact", user_infos: toUserInfos(updatedUser) });
12454
12557
  onComplete({
12455
12558
  name: updatedUser.name,
12456
12559
  image_url: updatedUser.image_url,
@@ -12476,7 +12579,7 @@ function OnboardingModal({ open, onOpenChange, onDismiss, user, variant = "missi
12476
12579
  } catch (error) {
12477
12580
  updateFlow({ loading: false, error: error instanceof Error ? error.message : "Code OTP incorrect" });
12478
12581
  }
12479
- }, [contactFlow, onComplete, updateFlow, user, markSuccess]);
12582
+ }, [contactFlow, onComplete, onUserInfosSync, updateFlow, profileUser, toUserInfos, markSuccess]);
12480
12583
  const resendFlowOtp = react.useCallback(async () => {
12481
12584
  if (!(contactFlow == null ? void 0 : contactFlow.requestId) || contactFlow.loading) return;
12482
12585
  updateFlow({ loading: true, error: "" });
@@ -12629,7 +12732,7 @@ function OnboardingModal({ open, onOpenChange, onDismiss, user, variant = "missi
12629
12732
  cursor: avatarUploading ? "not-allowed" : "pointer"
12630
12733
  },
12631
12734
  children: [
12632
- photoPreview || user.image_url ? /* @__PURE__ */ jsxRuntime.jsx("img", { src: photoPreview || user.image_url, alt: "Photo de profil", style: { width: "100%", height: "100%", objectFit: "cover" } }) : /* @__PURE__ */ jsxRuntime.jsx("div", { style: { width: "100%", height: "100%", display: "flex", alignItems: "center", justifyContent: "center", color: C.gray500 }, children: /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "22", height: "22", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.6", strokeLinecap: "round", strokeLinejoin: "round", children: [
12735
+ photoPreview || profileUser.image_url ? /* @__PURE__ */ jsxRuntime.jsx("img", { src: photoPreview || profileUser.image_url, alt: "Photo de profil", style: { width: "100%", height: "100%", objectFit: "cover" } }) : /* @__PURE__ */ jsxRuntime.jsx("div", { style: { width: "100%", height: "100%", display: "flex", alignItems: "center", justifyContent: "center", color: C.gray500 }, children: /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "22", height: "22", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.6", strokeLinecap: "round", strokeLinejoin: "round", children: [
12633
12736
  /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "12", cy: "8", r: "4" }),
12634
12737
  /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M5.5 21a8.38 8.38 0 0 1 13 0" })
12635
12738
  ] }) }),
@@ -13197,7 +13300,248 @@ function OnboardingModal({ open, onOpenChange, onDismiss, user, variant = "missi
13197
13300
  renderConfirmDialog()
13198
13301
  ] });
13199
13302
  }
13200
- function DebugPanel({ saasApiUrl, iamApiUrl, onOpenLogin, onOpenSignup, onOpenOnboarding, onResetProfilePrompt }) {
13303
+ /**
13304
+ * @license lucide-react v0.462.0 - ISC
13305
+ *
13306
+ * This source code is licensed under the ISC license.
13307
+ * See the LICENSE file in the root directory of this source tree.
13308
+ */
13309
+ const toKebabCase = (string) => string.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase();
13310
+ const mergeClasses = (...classes) => classes.filter((className, index, array) => {
13311
+ return Boolean(className) && className.trim() !== "" && array.indexOf(className) === index;
13312
+ }).join(" ").trim();
13313
+ /**
13314
+ * @license lucide-react v0.462.0 - ISC
13315
+ *
13316
+ * This source code is licensed under the ISC license.
13317
+ * See the LICENSE file in the root directory of this source tree.
13318
+ */
13319
+ var defaultAttributes = {
13320
+ xmlns: "http://www.w3.org/2000/svg",
13321
+ width: 24,
13322
+ height: 24,
13323
+ viewBox: "0 0 24 24",
13324
+ fill: "none",
13325
+ stroke: "currentColor",
13326
+ strokeWidth: 2,
13327
+ strokeLinecap: "round",
13328
+ strokeLinejoin: "round"
13329
+ };
13330
+ /**
13331
+ * @license lucide-react v0.462.0 - ISC
13332
+ *
13333
+ * This source code is licensed under the ISC license.
13334
+ * See the LICENSE file in the root directory of this source tree.
13335
+ */
13336
+ const Icon = react.forwardRef(
13337
+ ({
13338
+ color = "currentColor",
13339
+ size = 24,
13340
+ strokeWidth = 2,
13341
+ absoluteStrokeWidth,
13342
+ className = "",
13343
+ children,
13344
+ iconNode,
13345
+ ...rest
13346
+ }, ref) => {
13347
+ return react.createElement(
13348
+ "svg",
13349
+ {
13350
+ ref,
13351
+ ...defaultAttributes,
13352
+ width: size,
13353
+ height: size,
13354
+ stroke: color,
13355
+ strokeWidth: absoluteStrokeWidth ? Number(strokeWidth) * 24 / Number(size) : strokeWidth,
13356
+ className: mergeClasses("lucide", className),
13357
+ ...rest
13358
+ },
13359
+ [
13360
+ ...iconNode.map(([tag, attrs]) => react.createElement(tag, attrs)),
13361
+ ...Array.isArray(children) ? children : [children]
13362
+ ]
13363
+ );
13364
+ }
13365
+ );
13366
+ /**
13367
+ * @license lucide-react v0.462.0 - ISC
13368
+ *
13369
+ * This source code is licensed under the ISC license.
13370
+ * See the LICENSE file in the root directory of this source tree.
13371
+ */
13372
+ const createLucideIcon = (iconName, iconNode) => {
13373
+ const Component = react.forwardRef(
13374
+ ({ className, ...props }, ref) => react.createElement(Icon, {
13375
+ ref,
13376
+ iconNode,
13377
+ className: mergeClasses(`lucide-${toKebabCase(iconName)}`, className),
13378
+ ...props
13379
+ })
13380
+ );
13381
+ Component.displayName = `${iconName}`;
13382
+ return Component;
13383
+ };
13384
+ /**
13385
+ * @license lucide-react v0.462.0 - ISC
13386
+ *
13387
+ * This source code is licensed under the ISC license.
13388
+ * See the LICENSE file in the root directory of this source tree.
13389
+ */
13390
+ const ExternalLink = createLucideIcon("ExternalLink", [
13391
+ ["path", { d: "M15 3h6v6", key: "1q9fwt" }],
13392
+ ["path", { d: "M10 14 21 3", key: "gplh6r" }],
13393
+ ["path", { d: "M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6", key: "a6xqqp" }]
13394
+ ]);
13395
+ /**
13396
+ * @license lucide-react v0.462.0 - ISC
13397
+ *
13398
+ * This source code is licensed under the ISC license.
13399
+ * See the LICENSE file in the root directory of this source tree.
13400
+ */
13401
+ const LoaderCircle = createLucideIcon("LoaderCircle", [
13402
+ ["path", { d: "M21 12a9 9 0 1 1-6.219-8.56", key: "13zald" }]
13403
+ ]);
13404
+ /**
13405
+ * @license lucide-react v0.462.0 - ISC
13406
+ *
13407
+ * This source code is licensed under the ISC license.
13408
+ * See the LICENSE file in the root directory of this source tree.
13409
+ */
13410
+ const LockKeyhole = createLucideIcon("LockKeyhole", [
13411
+ ["circle", { cx: "12", cy: "16", r: "1", key: "1au0dj" }],
13412
+ ["rect", { x: "3", y: "10", width: "18", height: "12", rx: "2", key: "6s8ecr" }],
13413
+ ["path", { d: "M7 10V7a5 5 0 0 1 10 0v3", key: "1pqi11" }]
13414
+ ]);
13415
+ /**
13416
+ * @license lucide-react v0.462.0 - ISC
13417
+ *
13418
+ * This source code is licensed under the ISC license.
13419
+ * See the LICENSE file in the root directory of this source tree.
13420
+ */
13421
+ const ShieldCheck = createLucideIcon("ShieldCheck", [
13422
+ [
13423
+ "path",
13424
+ {
13425
+ d: "M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z",
13426
+ key: "oel41y"
13427
+ }
13428
+ ],
13429
+ ["path", { d: "m9 12 2 2 4-4", key: "dzmm74" }]
13430
+ ]);
13431
+ const PasswordRedirectModal = ({ open, onOpenChange, saasApiUrl, iamApiUrl }) => {
13432
+ const [confirmOpen, setConfirmOpen] = react.useState(false);
13433
+ const [loading, setLoading] = react.useState(false);
13434
+ const [errorMessage, setErrorMessage] = react.useState(null);
13435
+ const apiBaseUrl = react.useMemo(() => {
13436
+ const source = saasApiUrl || iamApiUrl;
13437
+ if (!source) {
13438
+ return "";
13439
+ }
13440
+ const normalized = source.replace(/\/$/, "");
13441
+ return normalized.endsWith("/api") ? normalized : `${normalized}/api`;
13442
+ }, [saasApiUrl, iamApiUrl]);
13443
+ const buildUrl = (redirectUrl, magicToken) => {
13444
+ if (redirectUrl) {
13445
+ return redirectUrl;
13446
+ }
13447
+ if (magicToken) {
13448
+ const fallbackUrl = new URL("/auth/auto-connect", "https://iam.ollaid.com");
13449
+ fallbackUrl.searchParams.set("magic_token", magicToken);
13450
+ return fallbackUrl.toString();
13451
+ }
13452
+ return "";
13453
+ };
13454
+ const handleRedirect = async () => {
13455
+ if (loading) return;
13456
+ setLoading(true);
13457
+ setErrorMessage(null);
13458
+ try {
13459
+ const authToken = getAuthToken();
13460
+ const appAccessTokenRef = getNativeStorage().getItem(STORAGE.APP_ACCESS_TOKEN_REF);
13461
+ if (!authToken && !appAccessTokenRef) {
13462
+ throw new Error("Session Native SSO introuvable");
13463
+ }
13464
+ if (!apiBaseUrl) {
13465
+ throw new Error("saasApiUrl non configurée");
13466
+ }
13467
+ const result = await fetchWithTimeout(
13468
+ `${apiBaseUrl}/native/password-link`,
13469
+ {
13470
+ method: "POST",
13471
+ headers: getHeaders(authToken || void 0, true)
13472
+ },
13473
+ 2e4
13474
+ );
13475
+ if (!result.success) {
13476
+ throw new Error(result.message || "Impossible de générer le lien");
13477
+ }
13478
+ const redirectUrl = buildUrl(
13479
+ typeof result.redirect_url === "string" && result.redirect_url.length > 0 ? result.redirect_url : typeof result.sso_url === "string" && result.sso_url.length > 0 ? result.sso_url : "",
13480
+ result.magic_token
13481
+ );
13482
+ if (!redirectUrl) {
13483
+ throw new Error("URL de redirection manquante");
13484
+ }
13485
+ window.open(redirectUrl, "_blank", "noopener,noreferrer");
13486
+ onOpenChange(false);
13487
+ setConfirmOpen(false);
13488
+ } catch (error) {
13489
+ const message = error instanceof ApiError ? error.message : (error == null ? void 0 : error.message) || "Impossible de lancer le flux de mot de passe";
13490
+ setErrorMessage(message);
13491
+ } finally {
13492
+ setLoading(false);
13493
+ }
13494
+ };
13495
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
13496
+ /* @__PURE__ */ jsxRuntime.jsx(Dialog, { open, onOpenChange, children: /* @__PURE__ */ jsxRuntime.jsxs(DialogContent, { style: { maxWidth: "28rem", borderRadius: "16px", background: "#fff" }, children: [
13497
+ /* @__PURE__ */ jsxRuntime.jsxs(DialogHeader, { children: [
13498
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { margin: "0 auto 12px", width: 56, height: 56, borderRadius: "50%", background: "rgba(232, 67, 10, 0.1)", display: "flex", alignItems: "center", justifyContent: "center", color: "#e8430a" }, children: /* @__PURE__ */ jsxRuntime.jsx(LockKeyhole, { size: 28 }) }),
13499
+ /* @__PURE__ */ jsxRuntime.jsx(DialogTitle, { style: { textAlign: "center" }, children: "Mot de passe géré par OLLAID SSO" }),
13500
+ /* @__PURE__ */ jsxRuntime.jsx(DialogDescription, { style: { textAlign: "center" }, children: "Ce bouton sert à tester la redirection vers IAM et l'ouverture du modal de changement de mot de passe." })
13501
+ ] }),
13502
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { border: "1px solid #e5e7eb", borderRadius: 12, padding: 16, background: "#f8fafc", color: "#475569", fontSize: 13, display: "grid", gap: 8 }, children: [
13503
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", gap: 8, alignItems: "flex-start" }, children: [
13504
+ /* @__PURE__ */ jsxRuntime.jsx(ShieldCheck, { size: 16, color: "#e8430a" }),
13505
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Le SaaS agit comme relais. Le backend SaaS appelle IAM côté serveur, puis renvoie l'URL de redirection." })
13506
+ ] }),
13507
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", gap: 8, alignItems: "flex-start" }, children: [
13508
+ /* @__PURE__ */ jsxRuntime.jsx(ExternalLink, { size: 16, color: "#e8430a" }),
13509
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Une fenêtre IAM va s'ouvrir avec auto-connexion temporaire, sans appel direct du navigateur vers IAM." })
13510
+ ] })
13511
+ ] }),
13512
+ /* @__PURE__ */ jsxRuntime.jsxs(DialogFooter, { style: { gap: 12 }, children: [
13513
+ /* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "outline", onClick: () => onOpenChange(false), style: { flex: 1 }, disabled: loading, children: "Fermer" }),
13514
+ /* @__PURE__ */ jsxRuntime.jsx(Button, { onClick: () => setConfirmOpen(true), disabled: loading, style: { flex: 1 }, children: loading ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
13515
+ /* @__PURE__ */ jsxRuntime.jsx(LoaderCircle, { size: 16, style: { marginRight: 8, animation: "spin 1s linear infinite" } }),
13516
+ "Préparation..."
13517
+ ] }) : "Changer mon mot de passe" })
13518
+ ] })
13519
+ ] }) }),
13520
+ /* @__PURE__ */ jsxRuntime.jsx(Dialog, { open: confirmOpen, onOpenChange: (nextOpen) => {
13521
+ if (!loading) {
13522
+ setConfirmOpen(nextOpen);
13523
+ }
13524
+ }, children: /* @__PURE__ */ jsxRuntime.jsxs(DialogContent, { style: { maxWidth: "26rem", borderRadius: "16px", background: "#fff" }, children: [
13525
+ /* @__PURE__ */ jsxRuntime.jsxs(DialogHeader, { children: [
13526
+ /* @__PURE__ */ jsxRuntime.jsx(DialogTitle, { children: "Êtes-vous sûr de rediriger vers le SSO ?" }),
13527
+ /* @__PURE__ */ jsxRuntime.jsx(DialogDescription, { children: "Cette action ouvre IAM dans un nouvel onglet et prépare l'auto-connexion pour tester le changement de mot de passe." })
13528
+ ] }),
13529
+ errorMessage && /* @__PURE__ */ jsxRuntime.jsx("div", { style: { border: "1px solid #fecaca", background: "#fef2f2", color: "#b91c1c", borderRadius: 12, padding: 12, fontSize: 13 }, children: errorMessage }),
13530
+ loading && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", flexDirection: "column", alignItems: "center", gap: 10, padding: "8px 0 4px" }, children: [
13531
+ /* @__PURE__ */ jsxRuntime.jsx(LoaderCircle, { size: 26, style: { animation: "spin 1s linear infinite", color: "#e8430a" } }),
13532
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { fontSize: 13, color: "#475569", textAlign: "center" }, children: "Création de la session en cours..." })
13533
+ ] }),
13534
+ /* @__PURE__ */ jsxRuntime.jsxs(DialogFooter, { style: { gap: 12 }, children: [
13535
+ /* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "outline", onClick: () => setConfirmOpen(false), style: { flex: 1 }, disabled: loading, children: "Annuler" }),
13536
+ /* @__PURE__ */ jsxRuntime.jsx(Button, { onClick: handleRedirect, style: { flex: 1 }, disabled: loading, children: loading ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
13537
+ /* @__PURE__ */ jsxRuntime.jsx(LoaderCircle, { size: 16, style: { marginRight: 8, animation: "spin 1s linear infinite" } }),
13538
+ "Confirmer..."
13539
+ ] }) : "Confirmer" })
13540
+ ] })
13541
+ ] }) })
13542
+ ] });
13543
+ };
13544
+ function DebugPanel({ saasApiUrl, iamApiUrl, onOpenLogin, onOpenSignup, onOpenOnboarding, onOpenPassword, onResetProfilePrompt }) {
13201
13545
  const [logs, setLogs] = react.useState([]);
13202
13546
  const [expanded, setExpanded] = react.useState(true);
13203
13547
  const [selectedLog, setSelectedLog] = react.useState(null);
@@ -13337,6 +13681,18 @@ function DebugPanel({ saasApiUrl, iamApiUrl, onOpenLogin, onOpenSignup, onOpenOn
13337
13681
  children: "Infos profile"
13338
13682
  }
13339
13683
  ),
13684
+ /* @__PURE__ */ jsxRuntime.jsx(
13685
+ "button",
13686
+ {
13687
+ onClick: (e) => {
13688
+ e.stopPropagation();
13689
+ onOpenPassword == null ? void 0 : onOpenPassword();
13690
+ },
13691
+ disabled: !onOpenPassword,
13692
+ style: { background: "#334155", color: "#e2e8f0", border: "none", borderRadius: "4px", padding: "2px 8px", cursor: onOpenPassword ? "pointer" : "not-allowed", fontSize: "11px", opacity: onOpenPassword ? 1 : 0.5 },
13693
+ children: "Mot de passe"
13694
+ }
13695
+ ),
13340
13696
  /* @__PURE__ */ jsxRuntime.jsx(
13341
13697
  "button",
13342
13698
  {
@@ -13530,6 +13886,7 @@ function NativeSSOPage({
13530
13886
  const [showOnboarding, setShowOnboarding] = react.useState(false);
13531
13887
  const [pendingSession, setPendingSession] = react.useState(null);
13532
13888
  const [debugOnboardingState, setDebugOnboardingState] = react.useState(null);
13889
+ const [passwordRedirectOpen, setPasswordRedirectOpen] = react.useState(false);
13533
13890
  const [redirectingTarget, setRedirectingTarget] = react.useState(null);
13534
13891
  const [redirectingReason, setRedirectingReason] = react.useState(null);
13535
13892
  const [session, setSession] = react.useState(() => {
@@ -13806,6 +14163,28 @@ function NativeSSOPage({
13806
14163
  }
13807
14164
  void refreshSessionProfile(true);
13808
14165
  }, [pendingSession, debugOnboardingState, onOnboardingComplete, persistSessionUser, refreshSessionProfile]);
14166
+ const handleOnboardingUserInfosSync = react.useCallback((payload) => {
14167
+ const activeSession = debugOnboardingState || pendingSession;
14168
+ if (!activeSession) return;
14169
+ const syncedUser = {
14170
+ ...activeSession.sourceUser,
14171
+ ...payload.user_infos
14172
+ };
14173
+ persistSessionUser(activeSession.token, syncedUser);
14174
+ if (debugOnboardingState) {
14175
+ setDebugOnboardingState((prev) => prev ? {
14176
+ ...prev,
14177
+ user: syncedUser,
14178
+ sourceUser: syncedUser
14179
+ } : prev);
14180
+ } else {
14181
+ setPendingSession((prev) => prev ? {
14182
+ ...prev,
14183
+ user: syncedUser,
14184
+ sourceUser: syncedUser
14185
+ } : prev);
14186
+ }
14187
+ }, [debugOnboardingState, pendingSession, persistSessionUser]);
13809
14188
  const handleOnboardingSkip = react.useCallback(() => {
13810
14189
  const activeSession = debugOnboardingState || pendingSession;
13811
14190
  if (!activeSession) return;
@@ -13880,6 +14259,9 @@ function NativeSSOPage({
13880
14259
  setShowOnboarding(true);
13881
14260
  }
13882
14261
  }, [session, clearOnboardingTimers]);
14262
+ const openDebugPassword = react.useCallback(() => {
14263
+ setPasswordRedirectOpen(true);
14264
+ }, []);
13883
14265
  react.useEffect(() => {
13884
14266
  syncProfilePrompt(session);
13885
14267
  return () => {
@@ -14013,6 +14395,8 @@ function NativeSSOPage({
14013
14395
  user: (debugOnboardingState || pendingSession).user,
14014
14396
  variant: debugOnboardingState ? "edit" : "missing",
14015
14397
  profileHydrating,
14398
+ hydrateProfile: false,
14399
+ onUserInfosSync: handleOnboardingUserInfosSync,
14016
14400
  onComplete: handleOnboardingComplete,
14017
14401
  onSkip: handleOnboardingSkip
14018
14402
  }
@@ -14025,8 +14409,18 @@ function NativeSSOPage({
14025
14409
  onOpenLogin: openDebugLogin,
14026
14410
  onOpenSignup: openDebugSignup,
14027
14411
  onOpenOnboarding: openDebugOnboarding,
14412
+ onOpenPassword: openDebugPassword,
14028
14413
  onResetProfilePrompt: resetDebugProfilePrompt
14029
14414
  }
14415
+ ),
14416
+ /* @__PURE__ */ jsxRuntime.jsx(
14417
+ PasswordRedirectModal,
14418
+ {
14419
+ open: passwordRedirectOpen,
14420
+ onOpenChange: setPasswordRedirectOpen,
14421
+ saasApiUrl,
14422
+ iamApiUrl
14423
+ }
14030
14424
  )
14031
14425
  ] });
14032
14426
  }
@@ -14092,6 +14486,8 @@ function NativeSSOPage({
14092
14486
  user: (debugOnboardingState || pendingSession).user,
14093
14487
  variant: debugOnboardingState ? "edit" : "missing",
14094
14488
  profileHydrating,
14489
+ hydrateProfile: false,
14490
+ onUserInfosSync: handleOnboardingUserInfosSync,
14095
14491
  onComplete: handleOnboardingComplete,
14096
14492
  onSkip: handleOnboardingSkip
14097
14493
  }
@@ -14104,8 +14500,18 @@ function NativeSSOPage({
14104
14500
  onOpenLogin: openDebugLogin,
14105
14501
  onOpenSignup: openDebugSignup,
14106
14502
  onOpenOnboarding: openDebugOnboarding,
14503
+ onOpenPassword: openDebugPassword,
14107
14504
  onResetProfilePrompt: resetDebugProfilePrompt
14108
14505
  }
14506
+ ),
14507
+ /* @__PURE__ */ jsxRuntime.jsx(
14508
+ PasswordRedirectModal,
14509
+ {
14510
+ open: passwordRedirectOpen,
14511
+ onOpenChange: setPasswordRedirectOpen,
14512
+ saasApiUrl,
14513
+ iamApiUrl
14514
+ }
14109
14515
  )
14110
14516
  ] });
14111
14517
  }