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