@ollaid/native-sso 2.1.5 → 2.5.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.
- package/README.md +174 -25
- package/dist/components/AvatarCropModal.d.ts +16 -0
- package/dist/components/DebugPanel.d.ts +7 -2
- package/dist/components/LoginModal.d.ts +2 -2
- package/dist/components/NativeSSOPage.d.ts +8 -2
- package/dist/components/OnboardingModal.d.ts +14 -7
- package/dist/components/PasswordRecoveryModal.d.ts +1 -1
- package/dist/components/PhoneInput.d.ts +2 -1
- package/dist/components/SignupModal.d.ts +1 -1
- package/dist/components/ui.d.ts +4 -2
- package/dist/hooks/useLogout.d.ts +1 -1
- package/dist/hooks/useMobilePassword.d.ts +1 -1
- package/dist/hooks/useMobileRegistration.d.ts +1 -1
- package/dist/hooks/useNativeAuth.d.ts +1 -1
- package/dist/hooks/useTokenHealthCheck.d.ts +11 -1
- package/dist/index.cjs +2051 -168
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +7 -5
- package/dist/index.js +2052 -169
- package/dist/index.js.map +1 -1
- package/dist/provider.d.ts +1 -1
- package/dist/services/api.d.ts +28 -1
- package/dist/services/debugLogger.d.ts +1 -1
- package/dist/services/iamAccount.d.ts +1 -1
- package/dist/services/mobilePassword.d.ts +1 -1
- package/dist/services/mobileRegistration.d.ts +1 -1
- package/dist/services/nativeAuth.d.ts +3 -2
- package/dist/services/profile.d.ts +31 -0
- package/dist/services/profileChange.d.ts +30 -0
- package/dist/services/profileMedia.d.ts +16 -0
- package/dist/types/mobile.d.ts +1 -1
- package/dist/types/native.d.ts +23 -1
- package/package.json +2 -2
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 } from "react";
|
|
5
|
+
import { createContext, forwardRef, useEffect, useContext, useRef, useState, useCallback, useMemo } 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: [
|
|
@@ -119,7 +119,7 @@ function Dialog({ open, onOpenChange, children }) {
|
|
|
119
119
|
if (!open) return null;
|
|
120
120
|
return /* @__PURE__ */ jsx(DialogContext.Provider, { value: { open, onOpenChange }, children });
|
|
121
121
|
}
|
|
122
|
-
function DialogContent({ children, className = "" }) {
|
|
122
|
+
function DialogContent({ children, className = "", style, hideCloseButton = false }) {
|
|
123
123
|
const { onOpenChange } = useContext(DialogContext);
|
|
124
124
|
return /* @__PURE__ */ jsxs("div", { style: { position: "fixed", inset: 0, zIndex: 50, display: "flex", alignItems: "center", justifyContent: "center" }, children: [
|
|
125
125
|
/* @__PURE__ */ jsx(
|
|
@@ -151,10 +151,11 @@ function DialogContent({ children, className = "" }) {
|
|
|
151
151
|
maxHeight: "90vh",
|
|
152
152
|
display: "flex",
|
|
153
153
|
flexDirection: "column",
|
|
154
|
-
animation: "ollaid-modal-in 0.3s ease-out"
|
|
154
|
+
animation: "ollaid-modal-in 0.3s ease-out",
|
|
155
|
+
...style
|
|
155
156
|
},
|
|
156
157
|
children: [
|
|
157
|
-
/* @__PURE__ */ jsx(
|
|
158
|
+
!hideCloseButton && /* @__PURE__ */ jsx(
|
|
158
159
|
"button",
|
|
159
160
|
{
|
|
160
161
|
onClick: () => onOpenChange(false),
|
|
@@ -479,7 +480,20 @@ const setNativeAuthConfig = (newConfig) => {
|
|
|
479
480
|
};
|
|
480
481
|
const getNativeAuthConfig = () => ({ ...config });
|
|
481
482
|
const isDebugMode = () => config.debug === true;
|
|
483
|
+
const getIamApiBaseUrl = () => {
|
|
484
|
+
if (!config.iamApiUrl) {
|
|
485
|
+
throw new ApiError("iamApiUrl non configurée", "unknown");
|
|
486
|
+
}
|
|
487
|
+
return config.iamApiUrl;
|
|
488
|
+
};
|
|
482
489
|
const DEVICE_ID_KEY = "native_sso_device_id";
|
|
490
|
+
const SESSION_UUID_KEY = "native_sso_session_uuid";
|
|
491
|
+
function generateUuid() {
|
|
492
|
+
if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
|
|
493
|
+
return crypto.randomUUID();
|
|
494
|
+
}
|
|
495
|
+
return `uuid_${Date.now()}_${Math.random().toString(36).slice(2)}_${Math.random().toString(36).slice(2)}`;
|
|
496
|
+
}
|
|
483
497
|
const getDeviceId = () => {
|
|
484
498
|
if (typeof localStorage === "undefined") return "";
|
|
485
499
|
let deviceId = localStorage.getItem(DEVICE_ID_KEY);
|
|
@@ -489,13 +503,30 @@ const getDeviceId = () => {
|
|
|
489
503
|
}
|
|
490
504
|
return deviceId;
|
|
491
505
|
};
|
|
506
|
+
const getSessionUuid = () => {
|
|
507
|
+
if (typeof localStorage === "undefined") return "";
|
|
508
|
+
let uuid = localStorage.getItem(SESSION_UUID_KEY);
|
|
509
|
+
if (!uuid) {
|
|
510
|
+
uuid = generateUuid();
|
|
511
|
+
localStorage.setItem(SESSION_UUID_KEY, uuid);
|
|
512
|
+
}
|
|
513
|
+
return uuid;
|
|
514
|
+
};
|
|
492
515
|
const STORAGE = {
|
|
493
516
|
AUTH_TOKEN: "auth_token",
|
|
494
517
|
TOKEN: "token",
|
|
495
518
|
USER: "user",
|
|
496
519
|
ACCOUNT_TYPE: "account_type",
|
|
497
520
|
ALIAS_REFERENCE: "alias_reference",
|
|
498
|
-
APP_ACCESS_TOKEN_REF: "app_access_token_ref"
|
|
521
|
+
APP_ACCESS_TOKEN_REF: "app_access_token_ref",
|
|
522
|
+
REFRESH_TOKEN: "refresh_token",
|
|
523
|
+
TOKEN_EXPIRES_AT: "token_expires_at",
|
|
524
|
+
REFRESH_EXPIRES_AT: "refresh_expires_at"
|
|
525
|
+
};
|
|
526
|
+
const PROFILE_STORAGE = {
|
|
527
|
+
IMAGE_LAST_STATUS: "sso_image_last_status",
|
|
528
|
+
IMAGE_LAST_CHECK: "sso_image_last_check",
|
|
529
|
+
IMAGE_RECHECK_AT: "sso_image_recheck_at"
|
|
499
530
|
};
|
|
500
531
|
const setAuthToken = (token) => {
|
|
501
532
|
if (typeof localStorage !== "undefined") {
|
|
@@ -515,6 +546,9 @@ const clearAuthToken = () => {
|
|
|
515
546
|
localStorage.removeItem(STORAGE.ACCOUNT_TYPE);
|
|
516
547
|
localStorage.removeItem(STORAGE.ALIAS_REFERENCE);
|
|
517
548
|
localStorage.removeItem(STORAGE.APP_ACCESS_TOKEN_REF);
|
|
549
|
+
localStorage.removeItem(STORAGE.REFRESH_TOKEN);
|
|
550
|
+
localStorage.removeItem(STORAGE.TOKEN_EXPIRES_AT);
|
|
551
|
+
localStorage.removeItem(STORAGE.REFRESH_EXPIRES_AT);
|
|
518
552
|
}
|
|
519
553
|
};
|
|
520
554
|
const logout = async () => {
|
|
@@ -542,11 +576,53 @@ const getAccountType = () => {
|
|
|
542
576
|
if (typeof localStorage === "undefined") return null;
|
|
543
577
|
return localStorage.getItem(STORAGE.ACCOUNT_TYPE);
|
|
544
578
|
};
|
|
579
|
+
function getProfilePromptState() {
|
|
580
|
+
if (typeof localStorage === "undefined") {
|
|
581
|
+
return { lastStatus: null, lastCheckAt: null, recheckAt: null };
|
|
582
|
+
}
|
|
583
|
+
const lastStatusRaw = localStorage.getItem(PROFILE_STORAGE.IMAGE_LAST_STATUS);
|
|
584
|
+
const lastCheckRaw = localStorage.getItem(PROFILE_STORAGE.IMAGE_LAST_CHECK);
|
|
585
|
+
const recheckRaw = localStorage.getItem(PROFILE_STORAGE.IMAGE_RECHECK_AT);
|
|
586
|
+
const parseTs = (value) => {
|
|
587
|
+
if (!value) return null;
|
|
588
|
+
const parsed = Number(value);
|
|
589
|
+
return Number.isFinite(parsed) ? parsed : null;
|
|
590
|
+
};
|
|
591
|
+
return {
|
|
592
|
+
lastStatus: lastStatusRaw === null ? null : lastStatusRaw === "true",
|
|
593
|
+
lastCheckAt: parseTs(lastCheckRaw),
|
|
594
|
+
recheckAt: parseTs(recheckRaw)
|
|
595
|
+
};
|
|
596
|
+
}
|
|
597
|
+
function setProfilePromptState(status, recheckAt = null) {
|
|
598
|
+
if (typeof localStorage === "undefined") return;
|
|
599
|
+
localStorage.setItem(PROFILE_STORAGE.IMAGE_LAST_STATUS, status ? "true" : "false");
|
|
600
|
+
localStorage.setItem(PROFILE_STORAGE.IMAGE_LAST_CHECK, String(Date.now()));
|
|
601
|
+
if (recheckAt && Number.isFinite(recheckAt)) {
|
|
602
|
+
localStorage.setItem(PROFILE_STORAGE.IMAGE_RECHECK_AT, String(recheckAt));
|
|
603
|
+
} else {
|
|
604
|
+
localStorage.removeItem(PROFILE_STORAGE.IMAGE_RECHECK_AT);
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
function markProfilePromptComplete() {
|
|
608
|
+
setProfilePromptState(true, null);
|
|
609
|
+
}
|
|
610
|
+
function snoozeProfilePrompt(hours = 8) {
|
|
611
|
+
setProfilePromptState(false, Date.now() + hours * 60 * 60 * 1e3);
|
|
612
|
+
}
|
|
545
613
|
async function fetchWithTimeout(url, options, timeout) {
|
|
546
614
|
const controller = new AbortController();
|
|
547
615
|
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
|
548
616
|
const method = options.method || "GET";
|
|
549
|
-
|
|
617
|
+
let parsedBody = void 0;
|
|
618
|
+
if (typeof options.body === "string") {
|
|
619
|
+
try {
|
|
620
|
+
parsedBody = JSON.parse(options.body);
|
|
621
|
+
} catch {
|
|
622
|
+
parsedBody = options.body;
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
const logId = isDebugMode() ? logApiCall(method, url, parsedBody) : null;
|
|
550
626
|
const startTime = Date.now();
|
|
551
627
|
try {
|
|
552
628
|
const response = await fetch(url, {
|
|
@@ -602,6 +678,14 @@ function getHeaders(token, includeConfigPrefix = false) {
|
|
|
602
678
|
if (deviceId) {
|
|
603
679
|
headers["X-Device-Id"] = deviceId;
|
|
604
680
|
}
|
|
681
|
+
const sessionUuid = getSessionUuid();
|
|
682
|
+
if (sessionUuid) {
|
|
683
|
+
headers["X-Session-UUID"] = sessionUuid;
|
|
684
|
+
}
|
|
685
|
+
const appAccessTokenRef = typeof localStorage !== "undefined" ? localStorage.getItem(STORAGE.APP_ACCESS_TOKEN_REF) : null;
|
|
686
|
+
if (appAccessTokenRef) {
|
|
687
|
+
headers["X-App-Access-Token-Ref"] = appAccessTokenRef;
|
|
688
|
+
}
|
|
605
689
|
if (includeConfigPrefix && config.configPrefix) {
|
|
606
690
|
headers["X-IAM-Config-Prefix"] = config.configPrefix;
|
|
607
691
|
}
|
|
@@ -837,6 +921,15 @@ const nativeAuthService = {
|
|
|
837
921
|
if (response.user) {
|
|
838
922
|
setAuthUser(response.user);
|
|
839
923
|
}
|
|
924
|
+
if (response.expires_at && typeof localStorage !== "undefined") {
|
|
925
|
+
localStorage.setItem(STORAGE.TOKEN_EXPIRES_AT, response.expires_at);
|
|
926
|
+
}
|
|
927
|
+
if (response.refresh_token && typeof localStorage !== "undefined") {
|
|
928
|
+
localStorage.setItem(STORAGE.REFRESH_TOKEN, response.refresh_token);
|
|
929
|
+
}
|
|
930
|
+
if (response.refresh_expires_at && typeof localStorage !== "undefined") {
|
|
931
|
+
localStorage.setItem(STORAGE.REFRESH_EXPIRES_AT, response.refresh_expires_at);
|
|
932
|
+
}
|
|
840
933
|
if (response.app_access_token_ref && typeof localStorage !== "undefined") {
|
|
841
934
|
localStorage.setItem(STORAGE.APP_ACCESS_TOKEN_REF, response.app_access_token_ref);
|
|
842
935
|
}
|
|
@@ -881,6 +974,65 @@ const nativeAuthService = {
|
|
|
881
974
|
throw err;
|
|
882
975
|
}
|
|
883
976
|
},
|
|
977
|
+
async refresh() {
|
|
978
|
+
const cfg = getNativeAuthConfig();
|
|
979
|
+
if (!cfg.saasApiUrl) {
|
|
980
|
+
throw new ApiError("saasApiUrl non configurée", "unknown");
|
|
981
|
+
}
|
|
982
|
+
const refreshToken = typeof localStorage !== "undefined" ? localStorage.getItem(STORAGE.REFRESH_TOKEN) : null;
|
|
983
|
+
if (!refreshToken) {
|
|
984
|
+
return { success: false, error_type: "missing_refresh", message: "Refresh token manquant" };
|
|
985
|
+
}
|
|
986
|
+
if (isDebugMode()) {
|
|
987
|
+
console.log("📤 [SaaS] POST /native/refresh");
|
|
988
|
+
}
|
|
989
|
+
let response;
|
|
990
|
+
try {
|
|
991
|
+
response = await fetchWithTimeout(
|
|
992
|
+
`${cfg.saasApiUrl}/native/refresh`,
|
|
993
|
+
{
|
|
994
|
+
method: "POST",
|
|
995
|
+
headers: getHeaders(void 0, true),
|
|
996
|
+
body: JSON.stringify({ refresh_token: refreshToken })
|
|
997
|
+
},
|
|
998
|
+
cfg.timeout || 3e4
|
|
999
|
+
);
|
|
1000
|
+
} catch (err) {
|
|
1001
|
+
if (err instanceof ApiError) {
|
|
1002
|
+
if (err.statusCode === 401) {
|
|
1003
|
+
return {
|
|
1004
|
+
success: false,
|
|
1005
|
+
error_type: err.errorType || "invalid_refresh",
|
|
1006
|
+
message: err.message
|
|
1007
|
+
};
|
|
1008
|
+
}
|
|
1009
|
+
if (err.statusCode === 404) {
|
|
1010
|
+
return {
|
|
1011
|
+
success: false,
|
|
1012
|
+
error_type: "not_supported",
|
|
1013
|
+
message: "Endpoint refresh non disponible sur ce SaaS"
|
|
1014
|
+
};
|
|
1015
|
+
}
|
|
1016
|
+
}
|
|
1017
|
+
throw err;
|
|
1018
|
+
}
|
|
1019
|
+
if (response.success) {
|
|
1020
|
+
if (response.token) {
|
|
1021
|
+
setAuthToken(response.token);
|
|
1022
|
+
}
|
|
1023
|
+
if (typeof localStorage !== "undefined") {
|
|
1024
|
+
if (response.expires_at) localStorage.setItem(STORAGE.TOKEN_EXPIRES_AT, response.expires_at);
|
|
1025
|
+
if (response.refresh_token) localStorage.setItem(STORAGE.REFRESH_TOKEN, response.refresh_token);
|
|
1026
|
+
if (response.refresh_expires_at) localStorage.setItem(STORAGE.REFRESH_EXPIRES_AT, response.refresh_expires_at);
|
|
1027
|
+
if (response.app_access_token_ref) localStorage.setItem(STORAGE.APP_ACCESS_TOKEN_REF, response.app_access_token_ref);
|
|
1028
|
+
if (response.alias_reference) localStorage.setItem(STORAGE.ALIAS_REFERENCE, response.alias_reference);
|
|
1029
|
+
}
|
|
1030
|
+
if (response.user) {
|
|
1031
|
+
setAuthUser(response.user);
|
|
1032
|
+
}
|
|
1033
|
+
}
|
|
1034
|
+
return response;
|
|
1035
|
+
},
|
|
884
1036
|
async logout(token) {
|
|
885
1037
|
const config2 = getNativeAuthConfig();
|
|
886
1038
|
const iamToken = typeof localStorage !== "undefined" ? localStorage.getItem(STORAGE.AUTH_TOKEN) || localStorage.getItem(STORAGE.TOKEN) : null;
|
|
@@ -1583,11 +1735,7 @@ async function checkTokenValidity(saasApiUrl, token, debug) {
|
|
|
1583
1735
|
try {
|
|
1584
1736
|
const response = await fetch(`${saasApiUrl}/native/check-token`, {
|
|
1585
1737
|
method: "POST",
|
|
1586
|
-
headers:
|
|
1587
|
-
"Content-Type": "application/json",
|
|
1588
|
-
"Accept": "application/json",
|
|
1589
|
-
"Authorization": `Bearer ${token}`
|
|
1590
|
-
},
|
|
1738
|
+
headers: getHeaders(token, true),
|
|
1591
1739
|
signal: controller.signal
|
|
1592
1740
|
});
|
|
1593
1741
|
clearTimeout(timeoutId);
|
|
@@ -1596,7 +1744,7 @@ async function checkTokenValidity(saasApiUrl, token, debug) {
|
|
|
1596
1744
|
return { valid: false };
|
|
1597
1745
|
}
|
|
1598
1746
|
if (!response.ok) {
|
|
1599
|
-
if (debug) console.log(`🔄 [HealthCheck]
|
|
1747
|
+
if (debug) console.log(`🔄 [HealthCheck] Erreur serveur ${response.status} — session conservée`);
|
|
1600
1748
|
throw new Error(`server_error_${response.status}`);
|
|
1601
1749
|
}
|
|
1602
1750
|
const data = await response.json();
|
|
@@ -1604,11 +1752,8 @@ async function checkTokenValidity(saasApiUrl, token, debug) {
|
|
|
1604
1752
|
if (debug) console.log("🔄 [HealthCheck] Token valide ✅ — user_infos mis à jour");
|
|
1605
1753
|
return { valid: true, user: data.user };
|
|
1606
1754
|
}
|
|
1607
|
-
if (
|
|
1608
|
-
|
|
1609
|
-
return { valid: true, user: data.user };
|
|
1610
|
-
}
|
|
1611
|
-
return { valid: false };
|
|
1755
|
+
if (debug) console.log("🔄 [HealthCheck] Token valide ✅");
|
|
1756
|
+
return { valid: true, user: data.user };
|
|
1612
1757
|
} catch (error) {
|
|
1613
1758
|
clearTimeout(timeoutId);
|
|
1614
1759
|
if (error instanceof Error && error.message === "offline") {
|
|
@@ -1648,7 +1793,7 @@ function revokeOnIam(iamApiUrl, sanctumToken, debug) {
|
|
|
1648
1793
|
}
|
|
1649
1794
|
}
|
|
1650
1795
|
function useTokenHealthCheck(options) {
|
|
1651
|
-
const { enabled, saasApiUrl, iamApiUrl, onTokenInvalid, onUserUpdated, debug = false } = options;
|
|
1796
|
+
const { enabled, saasApiUrl, iamApiUrl, onTokenInvalid, onUnauthorized, onUserUpdated, debug = false } = options;
|
|
1652
1797
|
const timerRef = useRef(null);
|
|
1653
1798
|
const intervalRef = useRef(null);
|
|
1654
1799
|
const enabledRef = useRef(enabled);
|
|
@@ -1665,6 +1810,19 @@ function useTokenHealthCheck(options) {
|
|
|
1665
1810
|
try {
|
|
1666
1811
|
const result = await checkTokenValidity(saasApiUrl, token, debug);
|
|
1667
1812
|
if (!result.valid) {
|
|
1813
|
+
if (onUnauthorized) {
|
|
1814
|
+
try {
|
|
1815
|
+
const decision = await onUnauthorized();
|
|
1816
|
+
if (decision === "recovered") {
|
|
1817
|
+
if (debug) console.log("🔄 [HealthCheck] Session récupérée via refresh ✅");
|
|
1818
|
+
return;
|
|
1819
|
+
}
|
|
1820
|
+
if (decision === "noop") {
|
|
1821
|
+
} else if (decision === "invalid") {
|
|
1822
|
+
}
|
|
1823
|
+
} catch {
|
|
1824
|
+
}
|
|
1825
|
+
}
|
|
1668
1826
|
if (iamApiUrl) {
|
|
1669
1827
|
if (debug) console.log("🔄 [HealthCheck] Revocation IAM avec sanctum_token...");
|
|
1670
1828
|
revokeOnIam(iamApiUrl, token, debug);
|
|
@@ -1704,6 +1862,15 @@ function saveSession(exchangeResult, accountType) {
|
|
|
1704
1862
|
const sanctumToken = exchangeResult.auth_token || exchangeResult.token;
|
|
1705
1863
|
localStorage.setItem(STORAGE.AUTH_TOKEN, sanctumToken);
|
|
1706
1864
|
localStorage.setItem(STORAGE.TOKEN, sanctumToken);
|
|
1865
|
+
if (exchangeResult.expires_at) {
|
|
1866
|
+
localStorage.setItem(STORAGE.TOKEN_EXPIRES_AT, exchangeResult.expires_at);
|
|
1867
|
+
}
|
|
1868
|
+
if (exchangeResult.refresh_token) {
|
|
1869
|
+
localStorage.setItem(STORAGE.REFRESH_TOKEN, exchangeResult.refresh_token);
|
|
1870
|
+
}
|
|
1871
|
+
if (exchangeResult.refresh_expires_at) {
|
|
1872
|
+
localStorage.setItem(STORAGE.REFRESH_EXPIRES_AT, exchangeResult.refresh_expires_at);
|
|
1873
|
+
}
|
|
1707
1874
|
const baseUser = exchangeResult.user_infos ? { ...exchangeResult.user, ...exchangeResult.user_infos } : exchangeResult.user;
|
|
1708
1875
|
const aliasRef = ((_a = exchangeResult.user) == null ? void 0 : _a.alias_reference) || exchangeResult.alias_reference || "";
|
|
1709
1876
|
const iamRef = ((_b = exchangeResult.user) == null ? void 0 : _b.reference) || "";
|
|
@@ -1730,6 +1897,9 @@ function clearSession() {
|
|
|
1730
1897
|
localStorage.removeItem(STORAGE.ACCOUNT_TYPE);
|
|
1731
1898
|
localStorage.removeItem(STORAGE.ALIAS_REFERENCE);
|
|
1732
1899
|
localStorage.removeItem(STORAGE.APP_ACCESS_TOKEN_REF);
|
|
1900
|
+
localStorage.removeItem(STORAGE.REFRESH_TOKEN);
|
|
1901
|
+
localStorage.removeItem(STORAGE.TOKEN_EXPIRES_AT);
|
|
1902
|
+
localStorage.removeItem(STORAGE.REFRESH_EXPIRES_AT);
|
|
1733
1903
|
}
|
|
1734
1904
|
function getErrorMessage$1(err, context) {
|
|
1735
1905
|
if (err instanceof Error) {
|
|
@@ -1800,14 +1970,66 @@ function useNativeAuth(options) {
|
|
|
1800
1970
|
}
|
|
1801
1971
|
}
|
|
1802
1972
|
}, []);
|
|
1973
|
+
const refreshingRef = useRef(false);
|
|
1974
|
+
const tryRefreshSession = useCallback(async () => {
|
|
1975
|
+
if (refreshingRef.current) return "noop";
|
|
1976
|
+
refreshingRef.current = true;
|
|
1977
|
+
try {
|
|
1978
|
+
const refreshToken = localStorage.getItem(STORAGE.REFRESH_TOKEN);
|
|
1979
|
+
if (!refreshToken) {
|
|
1980
|
+
if (isDebug) console.log("🔐 [Refresh] Pas de refresh_token — impossible de recuperer");
|
|
1981
|
+
return "noop";
|
|
1982
|
+
}
|
|
1983
|
+
if (typeof navigator !== "undefined" && !navigator.onLine) {
|
|
1984
|
+
if (isDebug) console.log("🔐 [Refresh] Offline — skip");
|
|
1985
|
+
return "noop";
|
|
1986
|
+
}
|
|
1987
|
+
const res = await nativeAuthService.refresh();
|
|
1988
|
+
if (res.success) {
|
|
1989
|
+
const storedUser = localStorage.getItem(STORAGE.USER);
|
|
1990
|
+
if (storedUser) {
|
|
1991
|
+
try {
|
|
1992
|
+
const user = JSON.parse(storedUser);
|
|
1993
|
+
setState((prev) => ({ ...prev, user, status: "completed" }));
|
|
1994
|
+
} catch {
|
|
1995
|
+
}
|
|
1996
|
+
}
|
|
1997
|
+
return "recovered";
|
|
1998
|
+
}
|
|
1999
|
+
if (res.success === false && res.error_type === "invalid_refresh") {
|
|
2000
|
+
if (isDebug) console.log("🔐 [Refresh] invalid_refresh — deconnexion autorisee");
|
|
2001
|
+
return "invalid";
|
|
2002
|
+
}
|
|
2003
|
+
return "noop";
|
|
2004
|
+
} catch (err) {
|
|
2005
|
+
if (isDebug) console.log("🔐 [Refresh] Echec refresh (non-bloquant)");
|
|
2006
|
+
return "noop";
|
|
2007
|
+
} finally {
|
|
2008
|
+
refreshingRef.current = false;
|
|
2009
|
+
}
|
|
2010
|
+
}, [isDebug]);
|
|
1803
2011
|
useTokenHealthCheck({
|
|
1804
2012
|
enabled: state.status === "completed" && state.user !== null,
|
|
1805
2013
|
saasApiUrl,
|
|
1806
2014
|
iamApiUrl,
|
|
1807
2015
|
onTokenInvalid: handleTokenInvalid,
|
|
2016
|
+
onUnauthorized: tryRefreshSession,
|
|
1808
2017
|
onUserUpdated: handleUserUpdated,
|
|
1809
2018
|
debug: isDebug
|
|
1810
2019
|
});
|
|
2020
|
+
useEffect(() => {
|
|
2021
|
+
if (state.status !== "completed" || !state.user) return;
|
|
2022
|
+
const expiresAt = localStorage.getItem(STORAGE.TOKEN_EXPIRES_AT);
|
|
2023
|
+
if (!expiresAt) return;
|
|
2024
|
+
const ts = Date.parse(expiresAt);
|
|
2025
|
+
if (Number.isNaN(ts)) return;
|
|
2026
|
+
const marginMs = 5 * 60 * 1e3;
|
|
2027
|
+
const delay = Math.max(ts - Date.now() - marginMs, 30 * 1e3);
|
|
2028
|
+
const t = setTimeout(() => {
|
|
2029
|
+
tryRefreshSession();
|
|
2030
|
+
}, delay);
|
|
2031
|
+
return () => clearTimeout(t);
|
|
2032
|
+
}, [state.status, state.user, tryRefreshSession]);
|
|
1811
2033
|
useEffect(() => {
|
|
1812
2034
|
const storedToken = localStorage.getItem(STORAGE.AUTH_TOKEN) || localStorage.getItem(STORAGE.TOKEN);
|
|
1813
2035
|
const storedUser = localStorage.getItem(STORAGE.USER);
|
|
@@ -3178,11 +3400,13 @@ function PhoneInput({
|
|
|
3178
3400
|
ccphone = DEFAULT_DIAL_CODE,
|
|
3179
3401
|
onCcphoneChange,
|
|
3180
3402
|
disabled = false,
|
|
3403
|
+
readOnly = false,
|
|
3181
3404
|
error,
|
|
3182
3405
|
placeholder = "77 123 45 67",
|
|
3183
3406
|
lockCcphone = false
|
|
3184
3407
|
}) {
|
|
3185
|
-
const
|
|
3408
|
+
const normalizedCcphone = ccphone || DEFAULT_DIAL_CODE;
|
|
3409
|
+
const selectedCountry = getCountryByDialCode(normalizedCcphone) || COUNTRIES_SORTED_BY_CODE[0];
|
|
3186
3410
|
const [dropdownOpen, setDropdownOpen] = useState(false);
|
|
3187
3411
|
const dropdownRef = useRef(null);
|
|
3188
3412
|
useEffect(() => {
|
|
@@ -3194,16 +3418,18 @@ function PhoneInput({
|
|
|
3194
3418
|
return () => document.removeEventListener("mousedown", handler);
|
|
3195
3419
|
}, [dropdownOpen]);
|
|
3196
3420
|
const handleChange = (e) => {
|
|
3421
|
+
if (disabled || readOnly) return;
|
|
3197
3422
|
const cleaned = e.target.value.replace(/\D/g, "");
|
|
3198
3423
|
onChange(cleaned.slice(0, selectedCountry.digits));
|
|
3199
3424
|
};
|
|
3200
|
-
const
|
|
3201
|
-
if (
|
|
3202
|
-
|
|
3203
|
-
|
|
3204
|
-
|
|
3425
|
+
const handlePaste = (e) => {
|
|
3426
|
+
if (disabled || readOnly) return;
|
|
3427
|
+
e.preventDefault();
|
|
3428
|
+
const pasted = e.clipboardData.getData("text").replace(/\s+/g, "").replace(/\D/g, "");
|
|
3429
|
+
onChange(pasted.slice(0, selectedCountry.digits));
|
|
3205
3430
|
};
|
|
3206
3431
|
const handleSelect = (dialCode) => {
|
|
3432
|
+
if (disabled || readOnly) return;
|
|
3207
3433
|
if (onCcphoneChange) onCcphoneChange(dialCode);
|
|
3208
3434
|
setDropdownOpen(false);
|
|
3209
3435
|
};
|
|
@@ -3214,8 +3440,8 @@ function PhoneInput({
|
|
|
3214
3440
|
border: `2px solid ${error ? "#ef4444" : "#d1d5db"}`,
|
|
3215
3441
|
borderRadius: "0.5rem",
|
|
3216
3442
|
overflow: "visible",
|
|
3217
|
-
opacity: disabled ? 0.
|
|
3218
|
-
backgroundColor: disabled ? "#
|
|
3443
|
+
opacity: disabled || readOnly ? 0.75 : 1,
|
|
3444
|
+
backgroundColor: disabled || readOnly ? "#f9fafb" : "white",
|
|
3219
3445
|
position: "relative"
|
|
3220
3446
|
}, children: [
|
|
3221
3447
|
onCcphoneChange && !lockCcphone ? /* @__PURE__ */ jsxs("div", { ref: dropdownRef, style: { position: "relative" }, children: [
|
|
@@ -3223,24 +3449,24 @@ function PhoneInput({
|
|
|
3223
3449
|
"button",
|
|
3224
3450
|
{
|
|
3225
3451
|
type: "button",
|
|
3226
|
-
onClick: () => !disabled && setDropdownOpen(!dropdownOpen),
|
|
3227
|
-
disabled,
|
|
3452
|
+
onClick: () => !(disabled || readOnly) && setDropdownOpen(!dropdownOpen),
|
|
3453
|
+
disabled: disabled || readOnly,
|
|
3228
3454
|
style: {
|
|
3229
3455
|
display: "flex",
|
|
3230
3456
|
alignItems: "center",
|
|
3231
|
-
gap: "0.
|
|
3232
|
-
padding: "0.
|
|
3457
|
+
gap: "0.35rem",
|
|
3458
|
+
padding: "0.45rem 0.6rem",
|
|
3233
3459
|
backgroundColor: "#f9fafb",
|
|
3234
3460
|
borderRight: "1px solid #e5e7eb",
|
|
3235
3461
|
border: "none",
|
|
3236
|
-
cursor: disabled ? "not-allowed" : "pointer",
|
|
3462
|
+
cursor: disabled || readOnly ? "not-allowed" : "pointer",
|
|
3237
3463
|
fontWeight: 500,
|
|
3238
3464
|
color: "#374151",
|
|
3239
|
-
fontSize: "0.
|
|
3465
|
+
fontSize: "0.85rem",
|
|
3240
3466
|
whiteSpace: "nowrap"
|
|
3241
3467
|
},
|
|
3242
3468
|
children: [
|
|
3243
|
-
/* @__PURE__ */ jsx("span", { style: { fontSize: "
|
|
3469
|
+
/* @__PURE__ */ jsx("span", { style: { fontSize: "1rem" }, children: selectedCountry.flag }),
|
|
3244
3470
|
/* @__PURE__ */ jsx("span", { children: selectedCountry.dialCode }),
|
|
3245
3471
|
/* @__PURE__ */ jsx("span", { style: { fontSize: "0.625rem", marginLeft: "0.125rem" }, children: "▼" })
|
|
3246
3472
|
]
|
|
@@ -3269,15 +3495,15 @@ function PhoneInput({
|
|
|
3269
3495
|
alignItems: "center",
|
|
3270
3496
|
gap: "0.5rem",
|
|
3271
3497
|
width: "100%",
|
|
3272
|
-
padding: "0.
|
|
3498
|
+
padding: "0.45rem 0.75rem",
|
|
3273
3499
|
border: "none",
|
|
3274
3500
|
cursor: "pointer",
|
|
3275
|
-
fontSize: "0.
|
|
3276
|
-
backgroundColor: c.dialCode ===
|
|
3501
|
+
fontSize: "0.85rem",
|
|
3502
|
+
backgroundColor: c.dialCode === normalizedCcphone ? "#f3f4f6" : "transparent",
|
|
3277
3503
|
textAlign: "left"
|
|
3278
3504
|
},
|
|
3279
3505
|
onMouseEnter: (e) => e.currentTarget.style.backgroundColor = "#f3f4f6",
|
|
3280
|
-
onMouseLeave: (e) => e.currentTarget.style.backgroundColor = c.dialCode ===
|
|
3506
|
+
onMouseLeave: (e) => e.currentTarget.style.backgroundColor = c.dialCode === normalizedCcphone ? "#f3f4f6" : "transparent",
|
|
3281
3507
|
children: [
|
|
3282
3508
|
/* @__PURE__ */ jsx("span", { style: { fontWeight: 600, color: "#374151", minWidth: "1.75rem" }, children: c.code }),
|
|
3283
3509
|
/* @__PURE__ */ jsx("span", { children: c.flag }),
|
|
@@ -3288,8 +3514,8 @@ function PhoneInput({
|
|
|
3288
3514
|
)) })
|
|
3289
3515
|
] }) : (
|
|
3290
3516
|
/* Locked view: only flag + dialCode */
|
|
3291
|
-
/* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: "0.
|
|
3292
|
-
/* @__PURE__ */ jsx("span", { style: { fontSize: "
|
|
3517
|
+
/* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: "0.35rem", padding: "0.45rem 0.6rem", backgroundColor: "#f9fafb", borderRight: "1px solid #e5e7eb" }, children: [
|
|
3518
|
+
/* @__PURE__ */ jsx("span", { style: { fontSize: "1rem" }, children: selectedCountry.flag }),
|
|
3293
3519
|
/* @__PURE__ */ jsx("span", { style: { fontWeight: 500, color: "#374151" }, children: selectedCountry.dialCode })
|
|
3294
3520
|
] })
|
|
3295
3521
|
),
|
|
@@ -3298,11 +3524,13 @@ function PhoneInput({
|
|
|
3298
3524
|
{
|
|
3299
3525
|
type: "tel",
|
|
3300
3526
|
inputMode: "numeric",
|
|
3301
|
-
value
|
|
3527
|
+
value,
|
|
3302
3528
|
onChange: handleChange,
|
|
3529
|
+
onPaste: handlePaste,
|
|
3303
3530
|
disabled,
|
|
3531
|
+
readOnly,
|
|
3304
3532
|
placeholder,
|
|
3305
|
-
style: { flex: 1, padding: "0.
|
|
3533
|
+
style: { flex: 1, padding: "0.45rem 0.6rem", fontSize: "0.95rem", border: "none", outline: "none", backgroundColor: "transparent" }
|
|
3306
3534
|
}
|
|
3307
3535
|
)
|
|
3308
3536
|
] }),
|
|
@@ -4273,6 +4501,414 @@ function SignupModal({ open, onOpenChange, onSwitchToLogin, onSignupSuccess, saa
|
|
|
4273
4501
|
)
|
|
4274
4502
|
] });
|
|
4275
4503
|
}
|
|
4504
|
+
function getBaseUrl() {
|
|
4505
|
+
return getIamApiBaseUrl();
|
|
4506
|
+
}
|
|
4507
|
+
function getAuthHeaders(includeConfigPrefix = false) {
|
|
4508
|
+
const token = getAuthToken();
|
|
4509
|
+
return getHeaders(token || void 0, includeConfigPrefix);
|
|
4510
|
+
}
|
|
4511
|
+
const profileChangeService = {
|
|
4512
|
+
async requestEmailChange(newEmail, method = "phone") {
|
|
4513
|
+
return fetchWithTimeout(
|
|
4514
|
+
`${getBaseUrl()}/backoffice/profile/email/request`,
|
|
4515
|
+
{
|
|
4516
|
+
method: "POST",
|
|
4517
|
+
headers: getAuthHeaders(true),
|
|
4518
|
+
body: JSON.stringify({ new_email: newEmail, method })
|
|
4519
|
+
},
|
|
4520
|
+
3e4
|
|
4521
|
+
);
|
|
4522
|
+
},
|
|
4523
|
+
async requestPhoneChange(ccphone, phone, method = "email") {
|
|
4524
|
+
return fetchWithTimeout(
|
|
4525
|
+
`${getBaseUrl()}/backoffice/profile/phone/request`,
|
|
4526
|
+
{
|
|
4527
|
+
method: "POST",
|
|
4528
|
+
headers: getAuthHeaders(true),
|
|
4529
|
+
body: JSON.stringify({ ccphone, phone, method })
|
|
4530
|
+
},
|
|
4531
|
+
3e4
|
|
4532
|
+
);
|
|
4533
|
+
},
|
|
4534
|
+
async verifyOldOTP(requestId, otpCode) {
|
|
4535
|
+
return fetchWithTimeout(
|
|
4536
|
+
`${getBaseUrl()}/backoffice/profile/change/verify-old`,
|
|
4537
|
+
{
|
|
4538
|
+
method: "POST",
|
|
4539
|
+
headers: getAuthHeaders(true),
|
|
4540
|
+
body: JSON.stringify({ request_id: requestId, otp_code: otpCode })
|
|
4541
|
+
},
|
|
4542
|
+
3e4
|
|
4543
|
+
);
|
|
4544
|
+
},
|
|
4545
|
+
async verifyNewOTP(requestId, otpCode) {
|
|
4546
|
+
return fetchWithTimeout(
|
|
4547
|
+
`${getBaseUrl()}/backoffice/profile/change/verify-new`,
|
|
4548
|
+
{
|
|
4549
|
+
method: "POST",
|
|
4550
|
+
headers: getAuthHeaders(true),
|
|
4551
|
+
body: JSON.stringify({ request_id: requestId, otp_code: otpCode })
|
|
4552
|
+
},
|
|
4553
|
+
3e4
|
|
4554
|
+
);
|
|
4555
|
+
},
|
|
4556
|
+
async resendOTP(requestId) {
|
|
4557
|
+
return fetchWithTimeout(
|
|
4558
|
+
`${getBaseUrl()}/backoffice/profile/change/resend-otp`,
|
|
4559
|
+
{
|
|
4560
|
+
method: "POST",
|
|
4561
|
+
headers: getAuthHeaders(true),
|
|
4562
|
+
body: JSON.stringify({ request_id: requestId })
|
|
4563
|
+
},
|
|
4564
|
+
3e4
|
|
4565
|
+
);
|
|
4566
|
+
},
|
|
4567
|
+
async switchMethod(requestId, method) {
|
|
4568
|
+
return fetchWithTimeout(
|
|
4569
|
+
`${getBaseUrl()}/backoffice/profile/change/switch-method`,
|
|
4570
|
+
{
|
|
4571
|
+
method: "POST",
|
|
4572
|
+
headers: getAuthHeaders(true),
|
|
4573
|
+
body: JSON.stringify({ request_id: requestId, method })
|
|
4574
|
+
},
|
|
4575
|
+
3e4
|
|
4576
|
+
);
|
|
4577
|
+
}
|
|
4578
|
+
};
|
|
4579
|
+
const MIN_SIZE = 88;
|
|
4580
|
+
const HANDLE = 16;
|
|
4581
|
+
const OUTPUT = 512;
|
|
4582
|
+
function clamp(value, min, max) {
|
|
4583
|
+
return Math.max(min, Math.min(max, value));
|
|
4584
|
+
}
|
|
4585
|
+
function getContainRect(container, natural) {
|
|
4586
|
+
if (!container.width || !container.height || !natural.width || !natural.height) {
|
|
4587
|
+
return { x: 0, y: 0, width: 0, height: 0, scale: 1 };
|
|
4588
|
+
}
|
|
4589
|
+
const scale = Math.min(container.width / natural.width, container.height / natural.height);
|
|
4590
|
+
const width = natural.width * scale;
|
|
4591
|
+
const height = natural.height * scale;
|
|
4592
|
+
return {
|
|
4593
|
+
x: (container.width - width) / 2,
|
|
4594
|
+
y: (container.height - height) / 2,
|
|
4595
|
+
width,
|
|
4596
|
+
height,
|
|
4597
|
+
scale
|
|
4598
|
+
};
|
|
4599
|
+
}
|
|
4600
|
+
function AvatarCropModal({ open, imageSrc, onOpenChange, onCancel, onConfirm }) {
|
|
4601
|
+
const wrapRef = useRef(null);
|
|
4602
|
+
const imgRef = useRef(null);
|
|
4603
|
+
const dragRef = useRef(null);
|
|
4604
|
+
const [loading, setLoading] = useState(false);
|
|
4605
|
+
const [error, setError] = useState("");
|
|
4606
|
+
const [natural, setNatural] = useState({ width: 0, height: 0 });
|
|
4607
|
+
const [container, setContainer] = useState({ width: 0, height: 0 });
|
|
4608
|
+
const [crop, setCrop] = useState(null);
|
|
4609
|
+
const [dragMode, setDragMode] = useState(null);
|
|
4610
|
+
const frame = useMemo(() => getContainRect(container, natural), [container, natural]);
|
|
4611
|
+
useEffect(() => {
|
|
4612
|
+
if (!open) return;
|
|
4613
|
+
setLoading(false);
|
|
4614
|
+
setError("");
|
|
4615
|
+
setNatural({ width: 0, height: 0 });
|
|
4616
|
+
setCrop(null);
|
|
4617
|
+
setDragMode(null);
|
|
4618
|
+
dragRef.current = null;
|
|
4619
|
+
}, [open, imageSrc]);
|
|
4620
|
+
useEffect(() => {
|
|
4621
|
+
if (!open) return;
|
|
4622
|
+
const measure = () => {
|
|
4623
|
+
const el = wrapRef.current;
|
|
4624
|
+
if (!el) return;
|
|
4625
|
+
const rect = el.getBoundingClientRect();
|
|
4626
|
+
setContainer({ width: rect.width, height: rect.width });
|
|
4627
|
+
};
|
|
4628
|
+
measure();
|
|
4629
|
+
window.addEventListener("resize", measure);
|
|
4630
|
+
return () => window.removeEventListener("resize", measure);
|
|
4631
|
+
}, [open]);
|
|
4632
|
+
useEffect(() => {
|
|
4633
|
+
if (!open || !frame.width || !frame.height) return;
|
|
4634
|
+
setCrop((current) => {
|
|
4635
|
+
if (current) {
|
|
4636
|
+
const maxSize2 = Math.min(frame.width, frame.height);
|
|
4637
|
+
const size2 = clamp(current.size, MIN_SIZE, maxSize2);
|
|
4638
|
+
const x = clamp(current.x, frame.x, frame.x + frame.width - size2);
|
|
4639
|
+
const y = clamp(current.y, frame.y, frame.y + frame.height - size2);
|
|
4640
|
+
return { x, y, size: size2 };
|
|
4641
|
+
}
|
|
4642
|
+
const maxSize = Math.min(frame.width, frame.height);
|
|
4643
|
+
const size = clamp(Math.round(maxSize * 0.7), MIN_SIZE, maxSize);
|
|
4644
|
+
return {
|
|
4645
|
+
x: frame.x + Math.round((frame.width - size) / 2),
|
|
4646
|
+
y: frame.y + Math.round((frame.height - size) / 2),
|
|
4647
|
+
size
|
|
4648
|
+
};
|
|
4649
|
+
});
|
|
4650
|
+
}, [open, frame]);
|
|
4651
|
+
const startDrag = (mode, event) => {
|
|
4652
|
+
if (!crop || !mode) return;
|
|
4653
|
+
event.preventDefault();
|
|
4654
|
+
event.stopPropagation();
|
|
4655
|
+
dragRef.current = {
|
|
4656
|
+
mode,
|
|
4657
|
+
pointerId: event.pointerId,
|
|
4658
|
+
start: { x: event.clientX, y: event.clientY },
|
|
4659
|
+
startCrop: crop
|
|
4660
|
+
};
|
|
4661
|
+
setDragMode(mode);
|
|
4662
|
+
try {
|
|
4663
|
+
event.currentTarget.setPointerCapture(event.pointerId);
|
|
4664
|
+
} catch {
|
|
4665
|
+
}
|
|
4666
|
+
};
|
|
4667
|
+
const moveCrop = (clientX, clientY) => {
|
|
4668
|
+
const state = dragRef.current;
|
|
4669
|
+
if (!state || !crop || !frame.width || !frame.height) return;
|
|
4670
|
+
const dx = clientX - state.start.x;
|
|
4671
|
+
const dy = clientY - state.start.y;
|
|
4672
|
+
const maxSize = Math.min(frame.width, frame.height);
|
|
4673
|
+
if (state.mode === "move") {
|
|
4674
|
+
setCrop({
|
|
4675
|
+
...state.startCrop,
|
|
4676
|
+
x: clamp(state.startCrop.x + dx, frame.x, frame.x + frame.width - state.startCrop.size),
|
|
4677
|
+
y: clamp(state.startCrop.y + dy, frame.y, frame.y + frame.height - state.startCrop.size)
|
|
4678
|
+
});
|
|
4679
|
+
return;
|
|
4680
|
+
}
|
|
4681
|
+
let next = { ...state.startCrop };
|
|
4682
|
+
const anchorLeft = state.startCrop.x;
|
|
4683
|
+
const anchorTop = state.startCrop.y;
|
|
4684
|
+
const anchorRight = state.startCrop.x + state.startCrop.size;
|
|
4685
|
+
const anchorBottom = state.startCrop.y + state.startCrop.size;
|
|
4686
|
+
if (state.mode === "resize-se") {
|
|
4687
|
+
const size = clamp(Math.max(state.startCrop.size + dx, state.startCrop.size + dy), MIN_SIZE, maxSize);
|
|
4688
|
+
next = { x: anchorLeft, y: anchorTop, size };
|
|
4689
|
+
} else if (state.mode === "resize-sw") {
|
|
4690
|
+
const size = clamp(Math.max(state.startCrop.size - dx, state.startCrop.size + dy), MIN_SIZE, maxSize);
|
|
4691
|
+
next = { x: anchorRight - size, y: anchorTop, size };
|
|
4692
|
+
} else if (state.mode === "resize-ne") {
|
|
4693
|
+
const size = clamp(Math.max(state.startCrop.size + dx, state.startCrop.size - dy), MIN_SIZE, maxSize);
|
|
4694
|
+
next = { x: anchorLeft, y: anchorBottom - size, size };
|
|
4695
|
+
} else if (state.mode === "resize-nw") {
|
|
4696
|
+
const size = clamp(Math.max(state.startCrop.size - dx, state.startCrop.size - dy), MIN_SIZE, maxSize);
|
|
4697
|
+
next = { x: anchorRight - size, y: anchorBottom - size, size };
|
|
4698
|
+
}
|
|
4699
|
+
next.x = clamp(next.x, frame.x, frame.x + frame.width - next.size);
|
|
4700
|
+
next.y = clamp(next.y, frame.y, frame.y + frame.height - next.size);
|
|
4701
|
+
setCrop(next);
|
|
4702
|
+
};
|
|
4703
|
+
useEffect(() => {
|
|
4704
|
+
if (!dragMode) return;
|
|
4705
|
+
const onMove = (event) => {
|
|
4706
|
+
if (!dragRef.current || event.pointerId !== dragRef.current.pointerId) return;
|
|
4707
|
+
moveCrop(event.clientX, event.clientY);
|
|
4708
|
+
};
|
|
4709
|
+
const onUp = (event) => {
|
|
4710
|
+
if (!dragRef.current || event.pointerId !== dragRef.current.pointerId) return;
|
|
4711
|
+
dragRef.current = null;
|
|
4712
|
+
setDragMode(null);
|
|
4713
|
+
window.removeEventListener("pointermove", onMove);
|
|
4714
|
+
window.removeEventListener("pointerup", onUp);
|
|
4715
|
+
window.removeEventListener("pointercancel", onUp);
|
|
4716
|
+
};
|
|
4717
|
+
window.addEventListener("pointermove", onMove);
|
|
4718
|
+
window.addEventListener("pointerup", onUp);
|
|
4719
|
+
window.addEventListener("pointercancel", onUp);
|
|
4720
|
+
return () => {
|
|
4721
|
+
window.removeEventListener("pointermove", onMove);
|
|
4722
|
+
window.removeEventListener("pointerup", onUp);
|
|
4723
|
+
window.removeEventListener("pointercancel", onUp);
|
|
4724
|
+
};
|
|
4725
|
+
}, [dragMode]);
|
|
4726
|
+
const handleConfirm = async () => {
|
|
4727
|
+
if (!imageSrc || !imgRef.current || !crop || !natural.width || !natural.height) {
|
|
4728
|
+
setError("L’image n’est pas encore prête.");
|
|
4729
|
+
return;
|
|
4730
|
+
}
|
|
4731
|
+
const canvas = document.createElement("canvas");
|
|
4732
|
+
canvas.width = OUTPUT;
|
|
4733
|
+
canvas.height = OUTPUT;
|
|
4734
|
+
const ctx = canvas.getContext("2d");
|
|
4735
|
+
if (!ctx) {
|
|
4736
|
+
setError("Impossible de préparer le recadrage.");
|
|
4737
|
+
return;
|
|
4738
|
+
}
|
|
4739
|
+
const sx = (crop.x - frame.x) / frame.scale;
|
|
4740
|
+
const sy = (crop.y - frame.y) / frame.scale;
|
|
4741
|
+
const sSize = crop.size / frame.scale;
|
|
4742
|
+
setLoading(true);
|
|
4743
|
+
setError("");
|
|
4744
|
+
try {
|
|
4745
|
+
ctx.drawImage(imgRef.current, sx, sy, sSize, sSize, 0, 0, OUTPUT, OUTPUT);
|
|
4746
|
+
const blob = await new Promise((resolve) => canvas.toBlob(resolve, "image/jpeg", 0.92));
|
|
4747
|
+
if (!blob) {
|
|
4748
|
+
throw new Error("Impossible de générer l’image recadrée.");
|
|
4749
|
+
}
|
|
4750
|
+
await onConfirm(blob);
|
|
4751
|
+
} catch (err) {
|
|
4752
|
+
setError(err instanceof Error ? err.message : "Erreur lors de la validation.");
|
|
4753
|
+
} finally {
|
|
4754
|
+
setLoading(false);
|
|
4755
|
+
}
|
|
4756
|
+
};
|
|
4757
|
+
const overlayStyle = {
|
|
4758
|
+
position: "absolute",
|
|
4759
|
+
inset: 0,
|
|
4760
|
+
cursor: dragMode ? "grabbing" : "grab",
|
|
4761
|
+
touchAction: "none"
|
|
4762
|
+
};
|
|
4763
|
+
const cropStyle = crop ? {
|
|
4764
|
+
position: "absolute",
|
|
4765
|
+
left: `${crop.x}px`,
|
|
4766
|
+
top: `${crop.y}px`,
|
|
4767
|
+
width: `${crop.size}px`,
|
|
4768
|
+
height: `${crop.size}px`,
|
|
4769
|
+
border: "2px solid #ffffff",
|
|
4770
|
+
borderRadius: "0.5rem",
|
|
4771
|
+
boxShadow: "0 0 0 9999px rgba(0, 0, 0, 0.40)",
|
|
4772
|
+
boxSizing: "border-box",
|
|
4773
|
+
touchAction: "none"
|
|
4774
|
+
} : { display: "none" };
|
|
4775
|
+
const handleOuter = {
|
|
4776
|
+
position: "absolute",
|
|
4777
|
+
width: `${HANDLE * 2}px`,
|
|
4778
|
+
height: `${HANDLE * 2}px`,
|
|
4779
|
+
display: "flex",
|
|
4780
|
+
alignItems: "center",
|
|
4781
|
+
justifyContent: "center",
|
|
4782
|
+
zIndex: 2,
|
|
4783
|
+
touchAction: "none",
|
|
4784
|
+
userSelect: "none"
|
|
4785
|
+
};
|
|
4786
|
+
const handleInner = {
|
|
4787
|
+
width: `${HANDLE}px`,
|
|
4788
|
+
height: `${HANDLE}px`,
|
|
4789
|
+
borderRadius: "999px",
|
|
4790
|
+
border: "2px solid #002147",
|
|
4791
|
+
background: "#fff",
|
|
4792
|
+
boxShadow: "0 1px 2px rgba(0,0,0,0.18)"
|
|
4793
|
+
};
|
|
4794
|
+
return /* @__PURE__ */ jsx(Dialog, { open, onOpenChange: (next) => {
|
|
4795
|
+
if (!next) onCancel();
|
|
4796
|
+
onOpenChange(next);
|
|
4797
|
+
}, children: /* @__PURE__ */ jsxs(DialogContent, { style: { maxWidth: "28rem", padding: "0.8rem", maxHeight: "90vh" }, children: [
|
|
4798
|
+
/* @__PURE__ */ jsxs(DialogHeader, { children: [
|
|
4799
|
+
/* @__PURE__ */ jsx(DialogTitle, { children: "Recadrer la photo" }),
|
|
4800
|
+
/* @__PURE__ */ jsx(DialogDescription, { children: "Déplacez le carré, puis validez pour enregistrer l’avatar." })
|
|
4801
|
+
] }),
|
|
4802
|
+
/* @__PURE__ */ jsxs(DialogBody, { style: { maxHeight: "64vh", overflowY: "auto", paddingBottom: "0.25rem" }, children: [
|
|
4803
|
+
/* @__PURE__ */ jsxs(
|
|
4804
|
+
"div",
|
|
4805
|
+
{
|
|
4806
|
+
ref: wrapRef,
|
|
4807
|
+
style: {
|
|
4808
|
+
position: "relative",
|
|
4809
|
+
width: "100%",
|
|
4810
|
+
aspectRatio: "1 / 1",
|
|
4811
|
+
overflow: "hidden",
|
|
4812
|
+
borderRadius: "0.8rem",
|
|
4813
|
+
border: "1px solid #e5e7eb",
|
|
4814
|
+
background: "#0f172a"
|
|
4815
|
+
},
|
|
4816
|
+
children: [
|
|
4817
|
+
imageSrc && /* @__PURE__ */ jsx(
|
|
4818
|
+
"img",
|
|
4819
|
+
{
|
|
4820
|
+
ref: imgRef,
|
|
4821
|
+
src: imageSrc,
|
|
4822
|
+
alt: "Aperçu",
|
|
4823
|
+
onLoad: (e) => {
|
|
4824
|
+
setNatural({ width: e.currentTarget.naturalWidth, height: e.currentTarget.naturalHeight });
|
|
4825
|
+
},
|
|
4826
|
+
style: {
|
|
4827
|
+
position: "absolute",
|
|
4828
|
+
inset: 0,
|
|
4829
|
+
width: "100%",
|
|
4830
|
+
height: "100%",
|
|
4831
|
+
objectFit: "contain",
|
|
4832
|
+
userSelect: "none",
|
|
4833
|
+
pointerEvents: "none"
|
|
4834
|
+
}
|
|
4835
|
+
}
|
|
4836
|
+
),
|
|
4837
|
+
crop && /* @__PURE__ */ jsx("div", { style: overlayStyle, children: /* @__PURE__ */ jsxs("div", { style: cropStyle, onPointerDown: (e) => startDrag("move", e), children: [
|
|
4838
|
+
/* @__PURE__ */ jsx("div", { style: { ...handleOuter, left: `-${HANDLE * 0.5}px`, top: `-${HANDLE * 0.5}px`, cursor: "nwse-resize" }, onPointerDown: (e) => startDrag("resize-nw", e), children: /* @__PURE__ */ jsx("div", { style: handleInner }) }),
|
|
4839
|
+
/* @__PURE__ */ jsx("div", { style: { ...handleOuter, right: `-${HANDLE * 0.5}px`, top: `-${HANDLE * 0.5}px`, cursor: "nesw-resize" }, onPointerDown: (e) => startDrag("resize-ne", e), children: /* @__PURE__ */ jsx("div", { style: handleInner }) }),
|
|
4840
|
+
/* @__PURE__ */ jsx("div", { style: { ...handleOuter, left: `-${HANDLE * 0.5}px`, bottom: `-${HANDLE * 0.5}px`, cursor: "nesw-resize" }, onPointerDown: (e) => startDrag("resize-sw", e), children: /* @__PURE__ */ jsx("div", { style: handleInner }) }),
|
|
4841
|
+
/* @__PURE__ */ jsx("div", { style: { ...handleOuter, right: `-${HANDLE * 0.5}px`, bottom: `-${HANDLE * 0.5}px`, cursor: "nwse-resize" }, onPointerDown: (e) => startDrag("resize-se", e), children: /* @__PURE__ */ jsx("div", { style: handleInner }) })
|
|
4842
|
+
] }) })
|
|
4843
|
+
]
|
|
4844
|
+
}
|
|
4845
|
+
),
|
|
4846
|
+
/* @__PURE__ */ jsxs("div", { style: { marginTop: "0.9rem", display: "grid", gap: "0.6rem" }, children: [
|
|
4847
|
+
/* @__PURE__ */ jsx("div", { style: { fontSize: "0.8rem", color: "#6b7280", lineHeight: 1.4 }, children: "Faites glisser le carré pour cadrer l’image. Les coins redimensionnent le carré." }),
|
|
4848
|
+
error && /* @__PURE__ */ jsx("div", { style: { padding: "0.65rem 0.75rem", borderRadius: "0.5rem", border: "1px solid #fecaca", background: "#fef2f2", color: "#b91c1c", fontSize: "0.85rem", lineHeight: 1.4 }, children: error })
|
|
4849
|
+
] })
|
|
4850
|
+
] }),
|
|
4851
|
+
/* @__PURE__ */ jsxs(DialogFooter, { style: { flexDirection: "row", justifyContent: "space-between", alignItems: "center" }, children: [
|
|
4852
|
+
/* @__PURE__ */ jsx(Button, { variant: "outline", onClick: onCancel, disabled: loading, style: { minWidth: "6.5rem", height: "2.35rem" }, children: "Annuler" }),
|
|
4853
|
+
/* @__PURE__ */ jsx(Button, { onClick: handleConfirm, disabled: !imageSrc || loading || !crop, style: { minWidth: "7rem", height: "2.35rem" }, children: loading ? "Chargement..." : "Valider" })
|
|
4854
|
+
] })
|
|
4855
|
+
] }) });
|
|
4856
|
+
}
|
|
4857
|
+
const profileMediaService = {
|
|
4858
|
+
async uploadProfileImage(image, filename = "avatar.jpg") {
|
|
4859
|
+
const baseUrl = getIamApiBaseUrl();
|
|
4860
|
+
const token = getAuthToken();
|
|
4861
|
+
if (!token) {
|
|
4862
|
+
throw new Error("Token manquant");
|
|
4863
|
+
}
|
|
4864
|
+
const formData = new FormData();
|
|
4865
|
+
formData.append("image", image, filename);
|
|
4866
|
+
const headers = getHeaders(token, true);
|
|
4867
|
+
delete headers["Content-Type"];
|
|
4868
|
+
return fetchWithTimeout(
|
|
4869
|
+
`${baseUrl}/backoffice/profile/image`,
|
|
4870
|
+
{
|
|
4871
|
+
method: "POST",
|
|
4872
|
+
headers,
|
|
4873
|
+
body: formData
|
|
4874
|
+
},
|
|
4875
|
+
3e4
|
|
4876
|
+
);
|
|
4877
|
+
}
|
|
4878
|
+
};
|
|
4879
|
+
const profileService = {
|
|
4880
|
+
async getProfile() {
|
|
4881
|
+
const baseUrl = getIamApiBaseUrl();
|
|
4882
|
+
const token = getAuthToken();
|
|
4883
|
+
if (!token) {
|
|
4884
|
+
throw new Error("Token manquant");
|
|
4885
|
+
}
|
|
4886
|
+
return fetchWithTimeout(
|
|
4887
|
+
`${baseUrl}/backoffice/profile`,
|
|
4888
|
+
{
|
|
4889
|
+
method: "GET",
|
|
4890
|
+
headers: getHeaders(token, true)
|
|
4891
|
+
},
|
|
4892
|
+
2e4
|
|
4893
|
+
);
|
|
4894
|
+
},
|
|
4895
|
+
async updateProfile(data) {
|
|
4896
|
+
const baseUrl = getIamApiBaseUrl();
|
|
4897
|
+
const token = getAuthToken();
|
|
4898
|
+
if (!token) {
|
|
4899
|
+
throw new Error("Token manquant");
|
|
4900
|
+
}
|
|
4901
|
+
return fetchWithTimeout(
|
|
4902
|
+
`${baseUrl}/backoffice/profile`,
|
|
4903
|
+
{
|
|
4904
|
+
method: "PUT",
|
|
4905
|
+
headers: getHeaders(token, true),
|
|
4906
|
+
body: JSON.stringify(data)
|
|
4907
|
+
},
|
|
4908
|
+
3e4
|
|
4909
|
+
);
|
|
4910
|
+
}
|
|
4911
|
+
};
|
|
4276
4912
|
const C = {
|
|
4277
4913
|
primary: "#002147",
|
|
4278
4914
|
accent: "#e8430a",
|
|
@@ -4282,21 +4918,87 @@ const C = {
|
|
|
4282
4918
|
gray700: "#374151",
|
|
4283
4919
|
white: "#ffffff"
|
|
4284
4920
|
};
|
|
4285
|
-
function OnboardingModal({ open, onOpenChange, user, onComplete, onSkip }) {
|
|
4286
|
-
|
|
4287
|
-
const
|
|
4288
|
-
const
|
|
4921
|
+
function OnboardingModal({ open, onOpenChange, onDismiss, user, variant = "missing", profileHydrating = false, onComplete, onSkip }) {
|
|
4922
|
+
var _a;
|
|
4923
|
+
const isEditMode = variant === "edit";
|
|
4924
|
+
const hasCurrentPhone = Boolean(user.phone);
|
|
4925
|
+
const hasCurrentEmail = Boolean(user.email);
|
|
4926
|
+
const directPhoneEdit = !isEditMode || !hasCurrentPhone;
|
|
4927
|
+
const directEmailEdit = !isEditMode || !hasCurrentEmail;
|
|
4928
|
+
!isEditMode ? !((_a = user.name) == null ? void 0 : _a.trim()) : true;
|
|
4929
|
+
const [name, setName] = useState(user.name || "");
|
|
4289
4930
|
const [photoPreview, setPhotoPreview] = useState("");
|
|
4290
|
-
const [
|
|
4931
|
+
const [avatarPickerOpen, setAvatarPickerOpen] = useState(false);
|
|
4932
|
+
const [pendingAvatarSrc, setPendingAvatarSrc] = useState(null);
|
|
4933
|
+
const [avatarUploading, setAvatarUploading] = useState(false);
|
|
4934
|
+
const avatarFileInputRef = useRef(null);
|
|
4291
4935
|
const [ccphone, setCcphone] = useState(user.ccphone || "+221");
|
|
4292
4936
|
const [phone, setPhone] = useState(user.phone || "");
|
|
4293
|
-
const [email, setEmail] = useState("");
|
|
4294
|
-
const [
|
|
4937
|
+
const [email, setEmail] = useState(user.email || "");
|
|
4938
|
+
const [address, setAddress] = useState(user.address || "");
|
|
4939
|
+
const [town, setTown] = useState(user.town || "");
|
|
4940
|
+
const [country, setCountry] = useState(user.country || DEFAULT_COUNTRY_CODE);
|
|
4295
4941
|
const [submitting, setSubmitting] = useState(false);
|
|
4296
4942
|
const [fileError, setFileError] = useState("");
|
|
4297
|
-
const
|
|
4298
|
-
|
|
4299
|
-
|
|
4943
|
+
const [contactFlow, setContactFlow] = useState(null);
|
|
4944
|
+
const [submitState, setSubmitState] = useState("idle");
|
|
4945
|
+
const [submitMessage, setSubmitMessage] = useState("");
|
|
4946
|
+
const [submitError, setSubmitError] = useState("");
|
|
4947
|
+
const [confirmAction, setConfirmAction] = useState(null);
|
|
4948
|
+
const [nowTick, setNowTick] = useState(Date.now());
|
|
4949
|
+
const userHydrationKey = [
|
|
4950
|
+
user.reference || "",
|
|
4951
|
+
user.name || "",
|
|
4952
|
+
user.email || "",
|
|
4953
|
+
user.ccphone || "",
|
|
4954
|
+
user.phone || "",
|
|
4955
|
+
user.address || "",
|
|
4956
|
+
user.town || "",
|
|
4957
|
+
user.country || "",
|
|
4958
|
+
user.image_url || "",
|
|
4959
|
+
user.auth_2fa ? "1" : "0"
|
|
4960
|
+
].join("|");
|
|
4961
|
+
const currentDialCode = (user.ccphone || ccphone || "+221").trim();
|
|
4962
|
+
const baseSmsAllowed = currentDialCode === "+221";
|
|
4963
|
+
const contactSmsAllowed = contactFlow ? contactFlow.kind === "phone" ? contactFlow.newCcphone.trim() === "+221" : baseSmsAllowed : baseSmsAllowed;
|
|
4964
|
+
const lockModalClose = profileHydrating || submitting || submitState === "success" || avatarUploading || Boolean(contactFlow == null ? void 0 : contactFlow.loading);
|
|
4965
|
+
useEffect(() => {
|
|
4966
|
+
if (!open) return;
|
|
4967
|
+
setName(user.name || "");
|
|
4968
|
+
setPhotoPreview(isEditMode ? user.image_url || "" : "");
|
|
4969
|
+
setPendingAvatarSrc(null);
|
|
4970
|
+
setAvatarPickerOpen(false);
|
|
4971
|
+
setAvatarUploading(false);
|
|
4972
|
+
setCcphone(user.ccphone || "+221");
|
|
4973
|
+
setPhone(user.phone || "");
|
|
4974
|
+
setEmail(user.email || "");
|
|
4975
|
+
setAddress(user.address || "");
|
|
4976
|
+
setTown(user.town || "");
|
|
4977
|
+
setCountry(user.country || DEFAULT_COUNTRY_CODE);
|
|
4978
|
+
setSubmitting(false);
|
|
4979
|
+
setFileError("");
|
|
4980
|
+
setContactFlow(null);
|
|
4981
|
+
setSubmitState("idle");
|
|
4982
|
+
setSubmitMessage("");
|
|
4983
|
+
setSubmitError("");
|
|
4984
|
+
setConfirmAction(null);
|
|
4985
|
+
}, [open, isEditMode, userHydrationKey]);
|
|
4986
|
+
useEffect(() => {
|
|
4987
|
+
if (!(contactFlow == null ? void 0 : contactFlow.resendAvailableAt)) return;
|
|
4988
|
+
const timer = window.setInterval(() => {
|
|
4989
|
+
setNowTick(Date.now());
|
|
4990
|
+
}, 1e3);
|
|
4991
|
+
return () => window.clearInterval(timer);
|
|
4992
|
+
}, [contactFlow == null ? void 0 : contactFlow.resendAvailableAt]);
|
|
4993
|
+
const markSuccess = useCallback((message) => {
|
|
4994
|
+
setSubmitting(false);
|
|
4995
|
+
setSubmitState("success");
|
|
4996
|
+
setSubmitMessage(message);
|
|
4997
|
+
setSubmitError("");
|
|
4998
|
+
}, []);
|
|
4999
|
+
useCallback((e) => {
|
|
5000
|
+
var _a2;
|
|
5001
|
+
const file = (_a2 = e.target.files) == null ? void 0 : _a2[0];
|
|
4300
5002
|
if (!file) return;
|
|
4301
5003
|
if (file.size > 2 * 1024 * 1024) {
|
|
4302
5004
|
setFileError("Le fichier dépasse 2 Mo. Veuillez choisir une image plus légère.");
|
|
@@ -4304,87 +5006,524 @@ function OnboardingModal({ open, onOpenChange, user, onComplete, onSkip }) {
|
|
|
4304
5006
|
return;
|
|
4305
5007
|
}
|
|
4306
5008
|
setFileError("");
|
|
4307
|
-
setPhotoFile(file);
|
|
4308
5009
|
const reader = new FileReader();
|
|
4309
5010
|
reader.onload = () => setPhotoPreview(reader.result);
|
|
4310
5011
|
reader.readAsDataURL(file);
|
|
4311
5012
|
}, []);
|
|
4312
|
-
const canSubmit =
|
|
4313
|
-
const handleSubmit = useCallback(() => {
|
|
5013
|
+
const canSubmit = !avatarUploading && (name.trim().length > 0 && (directPhoneEdit ? phone.length >= 7 : true) && (directEmailEdit ? email.trim().length > 0 : true) && town.trim().length > 0);
|
|
5014
|
+
const handleSubmit = useCallback(async () => {
|
|
4314
5015
|
if (!canSubmit) return;
|
|
4315
5016
|
setSubmitting(true);
|
|
4316
|
-
|
|
4317
|
-
|
|
4318
|
-
|
|
5017
|
+
setSubmitState("submitting");
|
|
5018
|
+
setSubmitError("");
|
|
5019
|
+
const data = {
|
|
5020
|
+
name: name.trim(),
|
|
5021
|
+
ccphone: directPhoneEdit ? ccphone : user.ccphone || void 0,
|
|
5022
|
+
phone: directPhoneEdit ? phone : user.phone || void 0,
|
|
5023
|
+
email: directEmailEdit ? email.trim() : user.email || void 0,
|
|
5024
|
+
...address.trim() ? { address: address.trim() } : {},
|
|
5025
|
+
...town.trim() ? { town: town.trim() } : {},
|
|
5026
|
+
...country ? { country } : {}
|
|
5027
|
+
};
|
|
5028
|
+
try {
|
|
5029
|
+
const response = await profileService.updateProfile(data);
|
|
5030
|
+
const payload = response.user_infos || response.user || {};
|
|
5031
|
+
const updatedUser = {
|
|
5032
|
+
...user,
|
|
5033
|
+
name: payload.name || data.name || user.name,
|
|
5034
|
+
email: payload.email ?? data.email ?? user.email,
|
|
5035
|
+
ccphone: payload.ccphone || data.ccphone || user.ccphone,
|
|
5036
|
+
phone: payload.phone || data.phone || user.phone,
|
|
5037
|
+
address: payload.address || data.address || user.address,
|
|
5038
|
+
town: payload.town || data.town || user.town,
|
|
5039
|
+
country: payload.country || data.country || user.country,
|
|
5040
|
+
image_url: payload.image_url || user.image_url,
|
|
5041
|
+
auth_2fa: payload.auth_2fa ?? user.auth_2fa,
|
|
5042
|
+
alias_reference: payload.alias_reference || user.alias_reference,
|
|
5043
|
+
iam_reference: payload.iam_reference || user.iam_reference
|
|
5044
|
+
};
|
|
5045
|
+
onComplete({
|
|
5046
|
+
name: updatedUser.name,
|
|
5047
|
+
image_url: updatedUser.image_url,
|
|
5048
|
+
ccphone: updatedUser.ccphone,
|
|
5049
|
+
phone: updatedUser.phone,
|
|
5050
|
+
email: updatedUser.email || void 0,
|
|
5051
|
+
address: updatedUser.address,
|
|
5052
|
+
town: updatedUser.town,
|
|
5053
|
+
country: updatedUser.country,
|
|
5054
|
+
user_infos: {
|
|
5055
|
+
reference: updatedUser.reference,
|
|
5056
|
+
name: updatedUser.name,
|
|
5057
|
+
email: updatedUser.email || null,
|
|
5058
|
+
ccphone: updatedUser.ccphone,
|
|
5059
|
+
phone: updatedUser.phone,
|
|
5060
|
+
address: updatedUser.address,
|
|
5061
|
+
town: updatedUser.town,
|
|
5062
|
+
country: updatedUser.country,
|
|
5063
|
+
image_url: updatedUser.image_url,
|
|
5064
|
+
auth_2fa: updatedUser.auth_2fa,
|
|
5065
|
+
alias_reference: updatedUser.alias_reference,
|
|
5066
|
+
iam_reference: updatedUser.iam_reference
|
|
5067
|
+
}
|
|
5068
|
+
});
|
|
5069
|
+
markSuccess(response.message || "Mise à jour réussie");
|
|
5070
|
+
} catch (error) {
|
|
5071
|
+
setSubmitting(false);
|
|
5072
|
+
setSubmitState("error");
|
|
5073
|
+
setSubmitError(error instanceof Error ? error.message : "Erreur lors de l’enregistrement");
|
|
5074
|
+
}
|
|
5075
|
+
}, [canSubmit, name, user, directPhoneEdit, directEmailEdit, ccphone, phone, email, address, town, country, onComplete, markSuccess]);
|
|
5076
|
+
const openAvatarPicker = useCallback(() => {
|
|
5077
|
+
var _a2;
|
|
5078
|
+
(_a2 = avatarFileInputRef.current) == null ? void 0 : _a2.click();
|
|
5079
|
+
}, []);
|
|
5080
|
+
const handleAvatarSelected = useCallback((event) => {
|
|
5081
|
+
var _a2;
|
|
5082
|
+
const file = (_a2 = event.target.files) == null ? void 0 : _a2[0];
|
|
5083
|
+
if (!file) return;
|
|
5084
|
+
const reader = new FileReader();
|
|
5085
|
+
reader.onload = () => {
|
|
5086
|
+
setPendingAvatarSrc(reader.result);
|
|
5087
|
+
setAvatarPickerOpen(true);
|
|
5088
|
+
};
|
|
5089
|
+
reader.readAsDataURL(file);
|
|
5090
|
+
event.target.value = "";
|
|
5091
|
+
}, []);
|
|
5092
|
+
const handleAvatarConfirm = useCallback(async (blob) => {
|
|
5093
|
+
setAvatarUploading(true);
|
|
5094
|
+
try {
|
|
5095
|
+
const filename = `avatar-${Date.now()}.jpg`;
|
|
5096
|
+
const response = await profileMediaService.uploadProfileImage(blob, filename);
|
|
5097
|
+
const payload = response.user_infos || response.user || {};
|
|
5098
|
+
const nextImage = payload.image_url || payload.image;
|
|
5099
|
+
if (nextImage) {
|
|
5100
|
+
setPhotoPreview(nextImage);
|
|
5101
|
+
} else {
|
|
5102
|
+
setPhotoPreview(URL.createObjectURL(blob));
|
|
5103
|
+
}
|
|
5104
|
+
if (Object.keys(payload).length > 0) {
|
|
5105
|
+
onComplete({
|
|
5106
|
+
name: payload.name,
|
|
5107
|
+
image_url: nextImage || void 0,
|
|
5108
|
+
ccphone: payload.ccphone || void 0,
|
|
5109
|
+
phone: payload.phone || void 0,
|
|
5110
|
+
email: payload.email || void 0,
|
|
5111
|
+
address: payload.address || void 0,
|
|
5112
|
+
town: payload.town || void 0,
|
|
5113
|
+
country: payload.country || void 0,
|
|
5114
|
+
user_infos: {
|
|
5115
|
+
reference: payload.reference || user.reference,
|
|
5116
|
+
name: payload.name || user.name,
|
|
5117
|
+
email: payload.email ?? user.email ?? null,
|
|
5118
|
+
ccphone: payload.ccphone || user.ccphone,
|
|
5119
|
+
phone: payload.phone || user.phone,
|
|
5120
|
+
address: payload.address || user.address,
|
|
5121
|
+
town: payload.town || user.town,
|
|
5122
|
+
country: payload.country || user.country,
|
|
5123
|
+
image_url: nextImage || user.image_url,
|
|
5124
|
+
auth_2fa: payload.auth_2fa ?? user.auth_2fa,
|
|
5125
|
+
alias_reference: payload.alias_reference || user.alias_reference,
|
|
5126
|
+
iam_reference: payload.iam_reference || user.iam_reference
|
|
5127
|
+
}
|
|
5128
|
+
});
|
|
5129
|
+
}
|
|
5130
|
+
markSuccess("Mise à jour réussie");
|
|
5131
|
+
setPendingAvatarSrc(null);
|
|
5132
|
+
setAvatarPickerOpen(false);
|
|
5133
|
+
} finally {
|
|
5134
|
+
setAvatarUploading(false);
|
|
5135
|
+
}
|
|
5136
|
+
}, [onComplete, markSuccess]);
|
|
5137
|
+
const openContactFlow = useCallback((kind) => {
|
|
5138
|
+
setContactFlow({
|
|
5139
|
+
kind,
|
|
5140
|
+
phase: "request",
|
|
5141
|
+
requestId: null,
|
|
5142
|
+
method: kind === "email" ? "email" : currentDialCode === "+221" ? "phone" : "email",
|
|
5143
|
+
newEmail: "",
|
|
5144
|
+
newCcphone: kind === "phone" ? user.ccphone || "+221" : "+221",
|
|
5145
|
+
newPhone: "",
|
|
5146
|
+
oldOtp: "",
|
|
5147
|
+
newOtp: "",
|
|
5148
|
+
loading: false,
|
|
5149
|
+
error: "",
|
|
5150
|
+
resendAvailableAt: null
|
|
5151
|
+
});
|
|
5152
|
+
}, [currentDialCode, user.email, user.phone, user.ccphone]);
|
|
5153
|
+
const closeContactFlow = useCallback(() => {
|
|
5154
|
+
setContactFlow(null);
|
|
5155
|
+
}, []);
|
|
5156
|
+
const updateFlow = useCallback((patch) => {
|
|
5157
|
+
setContactFlow((prev) => prev ? { ...prev, ...patch } : prev);
|
|
5158
|
+
}, []);
|
|
5159
|
+
const currentPhoneDialDisplay = user.ccphone || ccphone || "+221";
|
|
5160
|
+
const currentPhoneNumberDisplay = user.phone || phone || "";
|
|
5161
|
+
const currentEmailLabel = user.email || email || "";
|
|
5162
|
+
const currentPhoneDialCode = currentPhoneDialDisplay.trim() || "+221";
|
|
5163
|
+
const currentPhoneDigits = (user.phone || phone || "").replace(/\D/g, "");
|
|
5164
|
+
const formatPhoneDisplay = useCallback((dial, digits) => {
|
|
5165
|
+
const cleanDial = (dial || "+221").trim() || "+221";
|
|
5166
|
+
const cleanDigits = (digits || "").replace(/\D/g, "");
|
|
5167
|
+
return cleanDigits ? `${cleanDial} ${cleanDigits}` : cleanDial;
|
|
5168
|
+
}, []);
|
|
5169
|
+
const currentEmailNormalized = currentEmailLabel.trim().toLowerCase();
|
|
5170
|
+
const requestEmailNormalized = (contactFlow == null ? void 0 : contactFlow.kind) === "email" ? contactFlow.newEmail.trim().toLowerCase() : "";
|
|
5171
|
+
const requestPhoneDialCode = (contactFlow == null ? void 0 : contactFlow.kind) === "phone" ? contactFlow.newCcphone.trim() || "+221" : "";
|
|
5172
|
+
const requestPhoneDigits = (contactFlow == null ? void 0 : contactFlow.kind) === "phone" ? contactFlow.newPhone.replace(/\D/g, "") : "";
|
|
5173
|
+
const emailIsCurrent = Boolean((contactFlow == null ? void 0 : contactFlow.kind) === "email" && requestEmailNormalized && requestEmailNormalized === currentEmailNormalized);
|
|
5174
|
+
const phoneIsCurrent = Boolean(
|
|
5175
|
+
(contactFlow == null ? void 0 : contactFlow.kind) === "phone" && requestPhoneDigits && requestPhoneDialCode === currentPhoneDialCode && requestPhoneDigits === currentPhoneDigits
|
|
5176
|
+
);
|
|
5177
|
+
const requestDuplicateMessage = emailIsCurrent ? "Vous ne pouvez pas écrire votre actuelle adresse email. Veuillez en choisir une autre." : phoneIsCurrent ? "Vous ne pouvez pas écrire votre actuel numéro de téléphone. Veuillez en choisir un autre." : "";
|
|
5178
|
+
const requestChange = useCallback(async () => {
|
|
5179
|
+
if (!contactFlow || contactFlow.loading) return;
|
|
5180
|
+
const sameEmail = contactFlow.kind === "email" && contactFlow.newEmail.trim().toLowerCase() === currentEmailNormalized;
|
|
5181
|
+
const samePhone = contactFlow.kind === "phone" && contactFlow.newCcphone.trim() === currentPhoneDialCode && contactFlow.newPhone.replace(/\D/g, "") === currentPhoneDigits;
|
|
5182
|
+
if (sameEmail || samePhone) {
|
|
5183
|
+
updateFlow({
|
|
5184
|
+
loading: false,
|
|
5185
|
+
error: sameEmail ? "Vous ne pouvez pas écrire votre actuelle adresse email. Veuillez en choisir une autre." : "Vous ne pouvez pas écrire votre actuel numéro de téléphone. Veuillez en choisir un autre."
|
|
5186
|
+
});
|
|
5187
|
+
return;
|
|
4319
5188
|
}
|
|
4320
|
-
|
|
4321
|
-
|
|
4322
|
-
|
|
5189
|
+
updateFlow({ loading: true, error: "" });
|
|
5190
|
+
try {
|
|
5191
|
+
const method = contactSmsAllowed ? contactFlow.method : "email";
|
|
5192
|
+
const response = contactFlow.kind === "email" ? await profileChangeService.requestEmailChange(contactFlow.newEmail.trim(), method) : await profileChangeService.requestPhoneChange(contactFlow.newCcphone, contactFlow.newPhone.trim(), method);
|
|
5193
|
+
if (!response.success || !response.request_id) {
|
|
5194
|
+
throw new Error(response.message || "Impossible de démarrer le changement");
|
|
5195
|
+
}
|
|
5196
|
+
updateFlow({ requestId: response.request_id, phase: "verify-old", loading: false, error: "", resendAvailableAt: Date.now() + 12e4 });
|
|
5197
|
+
} catch (error) {
|
|
5198
|
+
updateFlow({ loading: false, error: error instanceof Error ? error.message : "Erreur inconnue" });
|
|
5199
|
+
}
|
|
5200
|
+
}, [contactFlow, updateFlow, contactSmsAllowed, currentEmailNormalized, currentPhoneDialCode, currentPhoneDigits]);
|
|
5201
|
+
const verifyOld = useCallback(async () => {
|
|
5202
|
+
if (!(contactFlow == null ? void 0 : contactFlow.requestId) || contactFlow.loading) return;
|
|
5203
|
+
updateFlow({ loading: true, error: "" });
|
|
5204
|
+
try {
|
|
5205
|
+
const response = await profileChangeService.verifyOldOTP(contactFlow.requestId, contactFlow.oldOtp);
|
|
5206
|
+
if (!response.success) throw new Error(response.message || "Code OTP incorrect");
|
|
5207
|
+
updateFlow({ phase: "verify-new", loading: false, error: "", resendAvailableAt: Date.now() + 12e4 });
|
|
5208
|
+
} catch (error) {
|
|
5209
|
+
updateFlow({ loading: false, error: error instanceof Error ? error.message : "Code OTP incorrect" });
|
|
5210
|
+
}
|
|
5211
|
+
}, [contactFlow, updateFlow]);
|
|
5212
|
+
const verifyNew = useCallback(async () => {
|
|
5213
|
+
if (!(contactFlow == null ? void 0 : contactFlow.requestId) || contactFlow.loading) return;
|
|
5214
|
+
updateFlow({ loading: true, error: "" });
|
|
5215
|
+
try {
|
|
5216
|
+
const response = await profileChangeService.verifyNewOTP(contactFlow.requestId, contactFlow.newOtp);
|
|
5217
|
+
if (!response.success) throw new Error(response.message || "Code OTP incorrect");
|
|
5218
|
+
const updatedUser = {
|
|
5219
|
+
...user,
|
|
5220
|
+
...contactFlow.kind === "email" ? { email: contactFlow.newEmail.trim() } : { ccphone: contactFlow.newCcphone, phone: contactFlow.newPhone.trim() }
|
|
5221
|
+
};
|
|
5222
|
+
if (response.user && typeof response.user === "object") {
|
|
5223
|
+
Object.assign(updatedUser, response.user);
|
|
5224
|
+
}
|
|
5225
|
+
setContactFlow(null);
|
|
5226
|
+
onComplete({
|
|
5227
|
+
name: updatedUser.name,
|
|
5228
|
+
image_url: updatedUser.image_url,
|
|
5229
|
+
ccphone: updatedUser.ccphone,
|
|
5230
|
+
phone: updatedUser.phone,
|
|
5231
|
+
email: updatedUser.email || void 0,
|
|
5232
|
+
user_infos: {
|
|
5233
|
+
reference: updatedUser.reference,
|
|
5234
|
+
name: updatedUser.name,
|
|
5235
|
+
email: updatedUser.email || null,
|
|
5236
|
+
ccphone: updatedUser.ccphone,
|
|
5237
|
+
phone: updatedUser.phone,
|
|
5238
|
+
address: updatedUser.address,
|
|
5239
|
+
town: updatedUser.town,
|
|
5240
|
+
country: updatedUser.country,
|
|
5241
|
+
image_url: updatedUser.image_url,
|
|
5242
|
+
auth_2fa: updatedUser.auth_2fa,
|
|
5243
|
+
alias_reference: updatedUser.alias_reference,
|
|
5244
|
+
iam_reference: updatedUser.iam_reference
|
|
5245
|
+
}
|
|
5246
|
+
});
|
|
5247
|
+
markSuccess(contactFlow.kind === "email" ? "Email mis à jour avec succès." : "Téléphone mis à jour avec succès.");
|
|
5248
|
+
} catch (error) {
|
|
5249
|
+
updateFlow({ loading: false, error: error instanceof Error ? error.message : "Code OTP incorrect" });
|
|
5250
|
+
}
|
|
5251
|
+
}, [contactFlow, onComplete, updateFlow, user, markSuccess]);
|
|
5252
|
+
const resendFlowOtp = useCallback(async () => {
|
|
5253
|
+
if (!(contactFlow == null ? void 0 : contactFlow.requestId) || contactFlow.loading) return;
|
|
5254
|
+
updateFlow({ loading: true, error: "" });
|
|
5255
|
+
try {
|
|
5256
|
+
const response = await profileChangeService.resendOTP(contactFlow.requestId);
|
|
5257
|
+
if (!response.success) throw new Error(response.message || "Impossible de renvoyer le code");
|
|
5258
|
+
updateFlow({ loading: false, error: "", resendAvailableAt: Date.now() + 12e4 });
|
|
5259
|
+
} catch (error) {
|
|
5260
|
+
updateFlow({ loading: false, error: error instanceof Error ? error.message : "Erreur inconnue" });
|
|
5261
|
+
}
|
|
5262
|
+
}, [contactFlow, updateFlow]);
|
|
5263
|
+
const switchFlowMethod = useCallback(async (method) => {
|
|
5264
|
+
if (!contactFlow) return;
|
|
5265
|
+
if (contactFlow.phase === "request" || !contactFlow.requestId) {
|
|
5266
|
+
updateFlow({ method, error: "" });
|
|
5267
|
+
return;
|
|
4323
5268
|
}
|
|
4324
|
-
if (
|
|
4325
|
-
|
|
5269
|
+
if (contactFlow.loading) return;
|
|
5270
|
+
updateFlow({ loading: true, error: "", method });
|
|
5271
|
+
try {
|
|
5272
|
+
const response = await profileChangeService.switchMethod(contactFlow.requestId, method);
|
|
5273
|
+
if (!response.success) throw new Error(response.message || "Impossible de changer la méthode");
|
|
5274
|
+
updateFlow({ loading: false, error: "" });
|
|
5275
|
+
} catch (error) {
|
|
5276
|
+
updateFlow({ loading: false, error: error instanceof Error ? error.message : "Erreur inconnue" });
|
|
4326
5277
|
}
|
|
4327
|
-
|
|
4328
|
-
|
|
4329
|
-
|
|
4330
|
-
|
|
4331
|
-
|
|
5278
|
+
}, [contactFlow, updateFlow]);
|
|
5279
|
+
useEffect(() => {
|
|
5280
|
+
if (!contactFlow || contactFlow.kind !== "phone") return;
|
|
5281
|
+
if (contactFlow.newCcphone.trim() !== "+221" && contactFlow.method !== "email") {
|
|
5282
|
+
updateFlow({ method: "email" });
|
|
5283
|
+
}
|
|
5284
|
+
}, [contactFlow == null ? void 0 : contactFlow.kind, contactFlow == null ? void 0 : contactFlow.newCcphone, contactFlow == null ? void 0 : contactFlow.method, updateFlow]);
|
|
5285
|
+
const confirmBackToRequest = useCallback(() => {
|
|
5286
|
+
if (!contactFlow) return;
|
|
5287
|
+
updateFlow({
|
|
5288
|
+
phase: "request",
|
|
5289
|
+
requestId: null,
|
|
5290
|
+
oldOtp: "",
|
|
5291
|
+
newOtp: "",
|
|
5292
|
+
error: "",
|
|
5293
|
+
loading: false,
|
|
5294
|
+
resendAvailableAt: null
|
|
5295
|
+
});
|
|
5296
|
+
setConfirmAction(null);
|
|
5297
|
+
}, [contactFlow, updateFlow]);
|
|
5298
|
+
const confirmResendOtp = useCallback(async () => {
|
|
5299
|
+
if (!contactFlow) return;
|
|
5300
|
+
setConfirmAction(null);
|
|
5301
|
+
await resendFlowOtp();
|
|
5302
|
+
}, [contactFlow, resendFlowOtp]);
|
|
5303
|
+
const handleDialogOpenChange = useCallback((nextOpen) => {
|
|
5304
|
+
if (nextOpen) {
|
|
5305
|
+
onOpenChange(true);
|
|
5306
|
+
return;
|
|
5307
|
+
}
|
|
5308
|
+
if (lockModalClose) return;
|
|
5309
|
+
onOpenChange(false);
|
|
5310
|
+
}, [lockModalClose, onOpenChange]);
|
|
5311
|
+
const contactTargetLabel = contactFlow ? contactFlow.kind === "email" ? contactFlow.newEmail.trim() || currentEmailLabel || "votre adresse email" : formatPhoneDisplay(contactFlow.newCcphone, contactFlow.newPhone) : "";
|
|
5312
|
+
const currentReceiveLabel = (contactFlow ? contactSmsAllowed ? contactFlow.method : "email" : "email") === "phone" ? `SMS au ${(contactFlow == null ? void 0 : contactFlow.kind) === "phone" ? contactTargetLabel : formatPhoneDisplay(currentPhoneDialDisplay, currentPhoneNumberDisplay)}` : `email à ${(contactFlow == null ? void 0 : contactFlow.kind) === "email" ? contactTargetLabel : currentEmailLabel || "votre adresse email"}`;
|
|
5313
|
+
const resendCountdown = (contactFlow == null ? void 0 : contactFlow.resendAvailableAt) ? Math.max(0, Math.ceil((contactFlow.resendAvailableAt - nowTick) / 1e3)) : 0;
|
|
5314
|
+
const canResendOtp = Boolean(contactFlow == null ? void 0 : contactFlow.requestId) && !(contactFlow == null ? void 0 : contactFlow.loading) && resendCountdown === 0;
|
|
5315
|
+
const ShieldIcon = () => /* @__PURE__ */ jsxs("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: C.accent, strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
5316
|
+
/* @__PURE__ */ jsx("path", { 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" }),
|
|
5317
|
+
/* @__PURE__ */ jsx("path", { d: "m9 12 2 2 4-4" })
|
|
4332
5318
|
] });
|
|
4333
|
-
|
|
5319
|
+
const renderSuccess = () => /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
4334
5320
|
/* @__PURE__ */ jsxs(DialogHeader, { children: [
|
|
4335
5321
|
/* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: "0.5rem", marginBottom: "0.25rem" }, children: [
|
|
4336
5322
|
/* @__PURE__ */ jsx(ShieldIcon, {}),
|
|
4337
|
-
/* @__PURE__ */ jsx(DialogTitle, { children: "
|
|
5323
|
+
/* @__PURE__ */ jsx(DialogTitle, { children: "Profil enregistré" })
|
|
4338
5324
|
] }),
|
|
4339
|
-
/* @__PURE__ */ jsx(DialogDescription, { children: "
|
|
5325
|
+
/* @__PURE__ */ jsx(DialogDescription, { children: submitMessage || "Vos informations ont été mises à jour." })
|
|
4340
5326
|
] }),
|
|
4341
|
-
/* @__PURE__ */ jsx(DialogBody, { children: /* @__PURE__ */ jsxs("div", { style: { display: "flex", flexDirection: "column",
|
|
4342
|
-
|
|
4343
|
-
/* @__PURE__ */ jsx(
|
|
4344
|
-
/* @__PURE__ */
|
|
4345
|
-
|
|
4346
|
-
|
|
4347
|
-
|
|
4348
|
-
|
|
4349
|
-
|
|
4350
|
-
|
|
4351
|
-
|
|
4352
|
-
|
|
4353
|
-
|
|
4354
|
-
|
|
4355
|
-
|
|
4356
|
-
|
|
4357
|
-
|
|
4358
|
-
|
|
4359
|
-
|
|
4360
|
-
|
|
4361
|
-
|
|
4362
|
-
|
|
4363
|
-
|
|
4364
|
-
|
|
5327
|
+
/* @__PURE__ */ jsx(DialogBody, { style: { maxHeight: "52vh", overflowY: "auto", paddingBottom: "0.25rem" }, children: /* @__PURE__ */ jsxs("div", { style: { display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center", gap: "0.75rem", padding: "1.5rem 1rem", textAlign: "center" }, children: [
|
|
5328
|
+
/* @__PURE__ */ jsxs("svg", { width: "42", height: "42", viewBox: "0 0 24 24", fill: "none", stroke: "#16a34a", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
5329
|
+
/* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "10" }),
|
|
5330
|
+
/* @__PURE__ */ jsx("path", { d: "m9 12 2 2 4-4" })
|
|
5331
|
+
] }),
|
|
5332
|
+
/* @__PURE__ */ jsx("div", { style: { fontSize: "1rem", fontWeight: 700, color: C.gray700 }, children: "Mise à jour réussie" }),
|
|
5333
|
+
/* @__PURE__ */ jsx("div", { style: { fontSize: "0.875rem", color: C.gray500 }, children: "Vous pouvez fermer cette fenêtre maintenant." })
|
|
5334
|
+
] }) }),
|
|
5335
|
+
/* @__PURE__ */ jsx(DialogFooter, { style: { flexDirection: "row", alignItems: "center" }, children: /* @__PURE__ */ jsx(
|
|
5336
|
+
Button,
|
|
5337
|
+
{
|
|
5338
|
+
type: "button",
|
|
5339
|
+
variant: "outline",
|
|
5340
|
+
onClick: onDismiss,
|
|
5341
|
+
style: { width: "100%", height: "2.6rem" },
|
|
5342
|
+
children: "Fermer"
|
|
5343
|
+
}
|
|
5344
|
+
) })
|
|
5345
|
+
] });
|
|
5346
|
+
const renderHydration = () => /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
5347
|
+
/* @__PURE__ */ jsxs(DialogHeader, { children: [
|
|
5348
|
+
/* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: "0.5rem", marginBottom: "0.25rem" }, children: [
|
|
5349
|
+
/* @__PURE__ */ jsx(ShieldIcon, {}),
|
|
5350
|
+
/* @__PURE__ */ jsx(DialogTitle, { children: isEditMode ? "Modifier votre profil" : "Complétez votre profil" })
|
|
5351
|
+
] }),
|
|
5352
|
+
/* @__PURE__ */ jsx(DialogDescription, { children: "Récupération de vos infos depuis iam.ollaid.com en cours. Le SSO synchronise vos données avant l’affichage du formulaire." })
|
|
5353
|
+
] }),
|
|
5354
|
+
/* @__PURE__ */ jsx(DialogBody, { style: { maxHeight: "52vh", overflowY: "auto", paddingBottom: "0.25rem" }, children: /* @__PURE__ */ jsxs("div", { style: { display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center", gap: "0.9rem", padding: "2rem 1rem", textAlign: "center" }, children: [
|
|
5355
|
+
/* @__PURE__ */ jsx("svg", { width: "42", height: "42", viewBox: "0 0 24 24", fill: "none", stroke: C.primary, strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", style: { animation: "spin 1s linear infinite" }, children: /* @__PURE__ */ jsx("path", { d: "M21 12a9 9 0 1 1-6.219-8.56" }) }),
|
|
5356
|
+
/* @__PURE__ */ jsx("div", { style: { fontSize: "1rem", fontWeight: 700, color: C.gray700 }, children: "Récupération de vos informations" }),
|
|
5357
|
+
/* @__PURE__ */ jsx("div", { style: { fontSize: "0.875rem", color: C.gray500, lineHeight: 1.5 }, children: "Le modal sera rempli automatiquement dès que les données IAM seront prêtes." })
|
|
5358
|
+
] }) }),
|
|
5359
|
+
/* @__PURE__ */ jsx(DialogFooter, { style: { flexDirection: "row", justifyContent: "space-between", alignItems: "center" }, children: /* @__PURE__ */ jsx(
|
|
5360
|
+
Button,
|
|
5361
|
+
{
|
|
5362
|
+
type: "button",
|
|
5363
|
+
variant: "outline",
|
|
5364
|
+
onClick: () => setConfirmAction("cancel-load"),
|
|
5365
|
+
style: { minWidth: "6.5rem", height: "2.35rem" },
|
|
5366
|
+
children: "Fermer"
|
|
5367
|
+
}
|
|
5368
|
+
) })
|
|
5369
|
+
] });
|
|
5370
|
+
const renderBase = () => /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
5371
|
+
/* @__PURE__ */ jsxs(DialogHeader, { children: [
|
|
5372
|
+
/* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: "0.5rem", marginBottom: "0.25rem" }, children: [
|
|
5373
|
+
/* @__PURE__ */ jsx(ShieldIcon, {}),
|
|
5374
|
+
/* @__PURE__ */ jsx(DialogTitle, { children: isEditMode ? "Modifier votre profil" : "Complétez votre profil" })
|
|
5375
|
+
] }),
|
|
5376
|
+
/* @__PURE__ */ jsx(DialogDescription, { children: isEditMode ? "Mettez à jour vos informations sans quitter le SaaS." : "Ajoutez les informations manquantes pour finaliser votre compte." })
|
|
5377
|
+
] }),
|
|
5378
|
+
/* @__PURE__ */ jsx(DialogBody, { style: { maxHeight: "52vh", overflowY: "auto", paddingBottom: "0.25rem" }, children: /* @__PURE__ */ jsxs("div", { style: { display: "flex", flexDirection: "column", gap: "0.85rem", marginTop: "0.25rem", paddingRight: "0.25rem" }, children: [
|
|
5379
|
+
submitError && /* @__PURE__ */ jsx("div", { style: { padding: "0.75rem", borderRadius: "0.5rem", backgroundColor: "#fef2f2", border: "1px solid #fecaca", color: "#b91c1c", fontSize: "0.875rem" }, children: submitError }),
|
|
5380
|
+
submitState === "submitting" && /* @__PURE__ */ jsxs("div", { style: { padding: "0.75rem", borderRadius: "0.5rem", backgroundColor: "#eff6ff", border: "1px solid #bfdbfe", color: "#1d4ed8", fontSize: "0.875rem", display: "flex", alignItems: "center", gap: "0.5rem" }, children: [
|
|
5381
|
+
/* @__PURE__ */ jsx("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", style: { animation: "spin 1s linear infinite" }, children: /* @__PURE__ */ jsx("path", { d: "M21 12a9 9 0 1 1-6.219-8.56" }) }),
|
|
5382
|
+
"Enregistrement en cours..."
|
|
5383
|
+
] }),
|
|
5384
|
+
/* @__PURE__ */ jsxs("div", { style: { display: "grid", gridTemplateColumns: "4.25rem 1fr", gap: "0.75rem", alignItems: "center" }, children: [
|
|
5385
|
+
/* @__PURE__ */ jsxs("div", { style: { position: "relative" }, children: [
|
|
5386
|
+
/* @__PURE__ */ jsxs(
|
|
5387
|
+
"button",
|
|
5388
|
+
{
|
|
5389
|
+
type: "button",
|
|
5390
|
+
onClick: openAvatarPicker,
|
|
5391
|
+
disabled: avatarUploading,
|
|
5392
|
+
style: {
|
|
5393
|
+
width: "4.25rem",
|
|
5394
|
+
height: "4.25rem",
|
|
5395
|
+
borderRadius: "50%",
|
|
5396
|
+
border: `1px solid ${C.gray200}`,
|
|
5397
|
+
backgroundColor: C.gray100,
|
|
5398
|
+
overflow: "hidden",
|
|
5399
|
+
position: "relative",
|
|
5400
|
+
padding: 0,
|
|
5401
|
+
cursor: avatarUploading ? "not-allowed" : "pointer"
|
|
5402
|
+
},
|
|
5403
|
+
children: [
|
|
5404
|
+
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: [
|
|
5405
|
+
/* @__PURE__ */ jsx("circle", { cx: "12", cy: "8", r: "4" }),
|
|
5406
|
+
/* @__PURE__ */ jsx("path", { d: "M5.5 21a8.38 8.38 0 0 1 13 0" })
|
|
5407
|
+
] }) }),
|
|
5408
|
+
/* @__PURE__ */ jsx("div", { style: {
|
|
5409
|
+
position: "absolute",
|
|
5410
|
+
inset: 0,
|
|
5411
|
+
display: "flex",
|
|
5412
|
+
alignItems: "center",
|
|
5413
|
+
justifyContent: "center",
|
|
5414
|
+
background: avatarUploading ? "rgba(255,255,255,0.55)" : "rgba(0,0,0,0.08)"
|
|
5415
|
+
}, children: avatarUploading ? /* @__PURE__ */ jsx("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: C.primary, strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", style: { animation: "spin 1s linear infinite" }, children: /* @__PURE__ */ jsx("path", { d: "M21 12a9 9 0 1 1-6.219-8.56" }) }) : /* @__PURE__ */ jsxs("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: C.white, strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
5416
|
+
/* @__PURE__ */ jsx("path", { d: "M15 8h.01" }),
|
|
5417
|
+
/* @__PURE__ */ jsx("rect", { x: "3", y: "3", width: "18", height: "18", rx: "2", ry: "2" }),
|
|
5418
|
+
/* @__PURE__ */ jsx("circle", { cx: "8.5", cy: "8.5", r: "1.5" }),
|
|
5419
|
+
/* @__PURE__ */ jsx("path", { d: "m21 15-5-5L5 21" })
|
|
5420
|
+
] }) })
|
|
5421
|
+
]
|
|
5422
|
+
}
|
|
5423
|
+
),
|
|
5424
|
+
/* @__PURE__ */ jsx("input", { ref: avatarFileInputRef, type: "file", accept: "image/*", onChange: handleAvatarSelected, style: { display: "none" } })
|
|
5425
|
+
] }),
|
|
5426
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
5427
|
+
/* @__PURE__ */ jsx(Label, { style: { display: "block", marginBottom: "0.35rem", color: C.gray700, fontWeight: 500, fontSize: "0.875rem" }, children: "Nom complet" }),
|
|
5428
|
+
/* @__PURE__ */ jsx(
|
|
5429
|
+
"input",
|
|
5430
|
+
{
|
|
5431
|
+
type: "text",
|
|
5432
|
+
value: name,
|
|
5433
|
+
onChange: (e) => setName(e.target.value),
|
|
5434
|
+
placeholder: "Jean Dupont",
|
|
5435
|
+
style: {
|
|
5436
|
+
width: "100%",
|
|
5437
|
+
height: "2.25rem",
|
|
5438
|
+
padding: "0 0.75rem",
|
|
5439
|
+
border: `1px solid ${C.gray200}`,
|
|
5440
|
+
borderRadius: "0.375rem",
|
|
5441
|
+
fontSize: "0.875rem",
|
|
5442
|
+
color: C.gray700,
|
|
5443
|
+
backgroundColor: C.white,
|
|
5444
|
+
outline: "none"
|
|
5445
|
+
}
|
|
5446
|
+
}
|
|
5447
|
+
)
|
|
5448
|
+
] })
|
|
5449
|
+
] }),
|
|
5450
|
+
isEditMode && hasCurrentPhone && !contactFlow && /* @__PURE__ */ jsxs("div", { children: [
|
|
5451
|
+
/* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", justifyContent: "space-between", marginBottom: "0.35rem" }, children: [
|
|
5452
|
+
/* @__PURE__ */ jsx(Label, { style: { color: C.gray700, fontWeight: 500, fontSize: "0.875rem" }, children: "Numéro de téléphone" }),
|
|
5453
|
+
/* @__PURE__ */ jsx("button", { type: "button", onClick: () => openContactFlow("phone"), style: { border: "none", background: "transparent", padding: 0, color: C.accent, fontWeight: 700, fontSize: "0.8rem", cursor: "pointer" }, children: "Changer" })
|
|
5454
|
+
] }),
|
|
5455
|
+
/* @__PURE__ */ jsxs("div", { style: { display: "grid", gridTemplateColumns: "6rem 1fr", gap: "0.5rem" }, children: [
|
|
5456
|
+
/* @__PURE__ */ jsx(
|
|
5457
|
+
"input",
|
|
5458
|
+
{
|
|
5459
|
+
type: "text",
|
|
5460
|
+
value: currentPhoneDialDisplay,
|
|
5461
|
+
readOnly: true,
|
|
5462
|
+
style: {
|
|
5463
|
+
width: "100%",
|
|
5464
|
+
height: "2.25rem",
|
|
5465
|
+
padding: "0 0.65rem",
|
|
5466
|
+
border: `1px solid ${C.gray200}`,
|
|
5467
|
+
borderRadius: "0.375rem",
|
|
5468
|
+
fontSize: "0.875rem",
|
|
5469
|
+
color: C.gray700,
|
|
5470
|
+
backgroundColor: "#f9fafb",
|
|
5471
|
+
outline: "none"
|
|
5472
|
+
}
|
|
5473
|
+
}
|
|
5474
|
+
),
|
|
5475
|
+
/* @__PURE__ */ jsx(
|
|
5476
|
+
"input",
|
|
5477
|
+
{
|
|
5478
|
+
type: "text",
|
|
5479
|
+
value: currentPhoneNumberDisplay,
|
|
5480
|
+
readOnly: true,
|
|
5481
|
+
style: {
|
|
5482
|
+
width: "100%",
|
|
5483
|
+
height: "2.25rem",
|
|
5484
|
+
padding: "0 0.75rem",
|
|
5485
|
+
border: `1px solid ${C.gray200}`,
|
|
5486
|
+
borderRadius: "0.375rem",
|
|
5487
|
+
fontSize: "0.875rem",
|
|
5488
|
+
color: C.gray700,
|
|
5489
|
+
backgroundColor: "#f9fafb",
|
|
5490
|
+
outline: "none"
|
|
5491
|
+
}
|
|
5492
|
+
}
|
|
5493
|
+
)
|
|
5494
|
+
] })
|
|
5495
|
+
] }),
|
|
5496
|
+
isEditMode && hasCurrentEmail && !contactFlow && /* @__PURE__ */ jsxs("div", { children: [
|
|
5497
|
+
/* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", justifyContent: "space-between", marginBottom: "0.35rem" }, children: [
|
|
5498
|
+
/* @__PURE__ */ jsx(Label, { style: { color: C.gray700, fontWeight: 500, fontSize: "0.875rem" }, children: "Adresse email" }),
|
|
5499
|
+
/* @__PURE__ */ jsx("button", { type: "button", onClick: () => openContactFlow("email"), style: { border: "none", background: "transparent", padding: 0, color: C.accent, fontWeight: 700, fontSize: "0.8rem", cursor: "pointer" }, children: "Changer" })
|
|
5500
|
+
] }),
|
|
5501
|
+
/* @__PURE__ */ jsx(
|
|
5502
|
+
"input",
|
|
5503
|
+
{
|
|
5504
|
+
type: "email",
|
|
5505
|
+
value: currentEmailLabel,
|
|
5506
|
+
readOnly: true,
|
|
5507
|
+
style: {
|
|
5508
|
+
width: "100%",
|
|
5509
|
+
height: "2.25rem",
|
|
5510
|
+
padding: "0 0.75rem",
|
|
4365
5511
|
border: `1px solid ${C.gray200}`,
|
|
4366
5512
|
borderRadius: "0.375rem",
|
|
4367
|
-
cursor: "pointer",
|
|
4368
5513
|
fontSize: "0.875rem",
|
|
4369
5514
|
color: C.gray700,
|
|
4370
|
-
|
|
4371
|
-
|
|
4372
|
-
|
|
4373
|
-
|
|
4374
|
-
|
|
4375
|
-
/* @__PURE__ */ jsx("p", { style: { fontSize: "0.75rem", color: fileError ? "#dc2626" : C.gray500, marginTop: "0.25rem" }, children: fileError || "JPG, PNG. Max 2 Mo." })
|
|
4376
|
-
] })
|
|
4377
|
-
] })
|
|
5515
|
+
backgroundColor: "#f9fafb",
|
|
5516
|
+
outline: "none"
|
|
5517
|
+
}
|
|
5518
|
+
}
|
|
5519
|
+
)
|
|
4378
5520
|
] }),
|
|
4379
|
-
|
|
4380
|
-
/* @__PURE__ */ jsx(Label, { style: { display: "block", marginBottom: "0.
|
|
5521
|
+
(!isEditMode || !hasCurrentPhone) && /* @__PURE__ */ jsxs("div", { children: [
|
|
5522
|
+
/* @__PURE__ */ jsx(Label, { style: { display: "block", marginBottom: "0.35rem", color: C.gray700, fontWeight: 500, fontSize: "0.875rem" }, children: "Numéro de téléphone" }),
|
|
4381
5523
|
/* @__PURE__ */ jsx(PhoneInput, { value: phone, onChange: setPhone, ccphone, onCcphoneChange: setCcphone })
|
|
4382
5524
|
] }),
|
|
4383
|
-
|
|
4384
|
-
/* @__PURE__ */
|
|
4385
|
-
"Adresse email ",
|
|
4386
|
-
/* @__PURE__ */ jsx("span", { style: { color: C.gray500, fontWeight: 400 }, children: "(optionnel)" })
|
|
4387
|
-
] }),
|
|
5525
|
+
(!isEditMode || !hasCurrentEmail) && /* @__PURE__ */ jsxs("div", { children: [
|
|
5526
|
+
/* @__PURE__ */ jsx(Label, { style: { display: "block", marginBottom: "0.35rem", color: C.gray700, fontWeight: 500, fontSize: "0.875rem" }, children: "Adresse email" }),
|
|
4388
5527
|
/* @__PURE__ */ jsx(
|
|
4389
5528
|
"input",
|
|
4390
5529
|
{
|
|
@@ -4394,7 +5533,7 @@ function OnboardingModal({ open, onOpenChange, user, onComplete, onSkip }) {
|
|
|
4394
5533
|
placeholder: "email@exemple.com",
|
|
4395
5534
|
style: {
|
|
4396
5535
|
width: "100%",
|
|
4397
|
-
height: "2.
|
|
5536
|
+
height: "2.25rem",
|
|
4398
5537
|
padding: "0 0.75rem",
|
|
4399
5538
|
border: `1px solid ${C.gray200}`,
|
|
4400
5539
|
borderRadius: "0.375rem",
|
|
@@ -4406,50 +5545,431 @@ function OnboardingModal({ open, onOpenChange, user, onComplete, onSkip }) {
|
|
|
4406
5545
|
}
|
|
4407
5546
|
)
|
|
4408
5547
|
] }),
|
|
4409
|
-
/* @__PURE__ */ jsxs("
|
|
4410
|
-
|
|
4411
|
-
|
|
4412
|
-
|
|
4413
|
-
|
|
4414
|
-
|
|
4415
|
-
|
|
4416
|
-
|
|
4417
|
-
|
|
4418
|
-
|
|
4419
|
-
|
|
4420
|
-
|
|
4421
|
-
|
|
4422
|
-
|
|
4423
|
-
|
|
4424
|
-
|
|
4425
|
-
|
|
4426
|
-
|
|
5548
|
+
/* @__PURE__ */ jsxs("div", { style: { display: "grid", gap: "0.85rem" }, children: [
|
|
5549
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
5550
|
+
/* @__PURE__ */ jsx(Label, { style: { display: "block", marginBottom: "0.35rem", color: C.gray700, fontWeight: 500, fontSize: "0.875rem" }, children: "Adresse" }),
|
|
5551
|
+
/* @__PURE__ */ jsx(
|
|
5552
|
+
"input",
|
|
5553
|
+
{
|
|
5554
|
+
type: "text",
|
|
5555
|
+
value: address,
|
|
5556
|
+
onChange: (e) => setAddress(e.target.value),
|
|
5557
|
+
placeholder: "Rue, quartier, numéro",
|
|
5558
|
+
style: {
|
|
5559
|
+
width: "100%",
|
|
5560
|
+
height: "2.25rem",
|
|
5561
|
+
padding: "0 0.75rem",
|
|
5562
|
+
border: `1px solid ${C.gray200}`,
|
|
5563
|
+
borderRadius: "0.375rem",
|
|
5564
|
+
fontSize: "0.875rem",
|
|
5565
|
+
color: C.gray700,
|
|
5566
|
+
backgroundColor: C.white,
|
|
5567
|
+
outline: "none"
|
|
5568
|
+
}
|
|
5569
|
+
}
|
|
5570
|
+
)
|
|
5571
|
+
] }),
|
|
5572
|
+
/* @__PURE__ */ jsxs("div", { style: { display: "grid", gridTemplateColumns: "1fr 1fr", gap: "0.75rem" }, children: [
|
|
5573
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
5574
|
+
/* @__PURE__ */ jsxs(Label, { style: { display: "block", marginBottom: "0.35rem", color: C.gray700, fontWeight: 500, fontSize: "0.875rem" }, children: [
|
|
5575
|
+
"Ville ",
|
|
5576
|
+
/* @__PURE__ */ jsx("span", { style: { color: "#dc2626" }, children: "*" })
|
|
5577
|
+
] }),
|
|
5578
|
+
/* @__PURE__ */ jsx(
|
|
5579
|
+
"input",
|
|
5580
|
+
{
|
|
5581
|
+
type: "text",
|
|
5582
|
+
value: town,
|
|
5583
|
+
onChange: (e) => setTown(e.target.value),
|
|
5584
|
+
required: true,
|
|
5585
|
+
placeholder: "Dakar",
|
|
5586
|
+
style: {
|
|
5587
|
+
width: "100%",
|
|
5588
|
+
height: "2.25rem",
|
|
5589
|
+
padding: "0 0.75rem",
|
|
5590
|
+
border: `1px solid ${C.gray200}`,
|
|
5591
|
+
borderRadius: "0.375rem",
|
|
5592
|
+
fontSize: "0.875rem",
|
|
5593
|
+
color: C.gray700,
|
|
5594
|
+
backgroundColor: C.white,
|
|
5595
|
+
outline: "none"
|
|
5596
|
+
}
|
|
5597
|
+
}
|
|
5598
|
+
)
|
|
5599
|
+
] }),
|
|
5600
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
5601
|
+
/* @__PURE__ */ jsx(Label, { style: { display: "block", marginBottom: "0.35rem", color: C.gray700, fontWeight: 500, fontSize: "0.875rem" }, children: "Pays" }),
|
|
5602
|
+
/* @__PURE__ */ jsx(
|
|
5603
|
+
"select",
|
|
5604
|
+
{
|
|
5605
|
+
value: country,
|
|
5606
|
+
onChange: (e) => setCountry(e.target.value),
|
|
5607
|
+
style: {
|
|
5608
|
+
width: "100%",
|
|
5609
|
+
height: "2.25rem",
|
|
5610
|
+
padding: "0 0.6rem",
|
|
5611
|
+
border: `1px solid ${C.gray200}`,
|
|
5612
|
+
borderRadius: "0.375rem",
|
|
5613
|
+
fontSize: "0.875rem",
|
|
5614
|
+
color: C.gray700,
|
|
5615
|
+
backgroundColor: C.white,
|
|
5616
|
+
outline: "none"
|
|
5617
|
+
},
|
|
5618
|
+
children: COUNTRIES_SORTED_BY_CODE.map((c) => /* @__PURE__ */ jsxs("option", { value: c.code, children: [
|
|
5619
|
+
c.flag,
|
|
5620
|
+
" ",
|
|
5621
|
+
c.name
|
|
5622
|
+
] }, c.code))
|
|
5623
|
+
}
|
|
5624
|
+
)
|
|
5625
|
+
] })
|
|
5626
|
+
] })
|
|
4427
5627
|
] })
|
|
4428
5628
|
] }) }),
|
|
4429
|
-
/* @__PURE__ */
|
|
5629
|
+
/* @__PURE__ */ jsx(DialogFooter, { style: { flexDirection: "row", justifyContent: submitState === "success" ? "center" : "space-between", alignItems: "center" }, children: submitState === "success" ? /* @__PURE__ */ jsx(
|
|
5630
|
+
Button,
|
|
5631
|
+
{
|
|
5632
|
+
type: "button",
|
|
5633
|
+
variant: "outline",
|
|
5634
|
+
onClick: onDismiss,
|
|
5635
|
+
style: { width: "100%", height: "2.6rem" },
|
|
5636
|
+
children: "Fermer"
|
|
5637
|
+
}
|
|
5638
|
+
) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
4430
5639
|
/* @__PURE__ */ jsx(
|
|
4431
5640
|
Button,
|
|
4432
5641
|
{
|
|
4433
|
-
|
|
4434
|
-
|
|
4435
|
-
|
|
4436
|
-
|
|
5642
|
+
type: "button",
|
|
5643
|
+
variant: "outline",
|
|
5644
|
+
onClick: () => onOpenChange(false),
|
|
5645
|
+
disabled: submitting,
|
|
5646
|
+
style: { minWidth: "6.5rem", height: "2.35rem" },
|
|
5647
|
+
children: "Fermer"
|
|
4437
5648
|
}
|
|
4438
5649
|
),
|
|
4439
5650
|
/* @__PURE__ */ jsx(
|
|
4440
5651
|
Button,
|
|
4441
5652
|
{
|
|
4442
|
-
|
|
4443
|
-
onClick: onSkip,
|
|
4444
|
-
disabled: submitting,
|
|
4445
|
-
style: {
|
|
4446
|
-
children: "
|
|
5653
|
+
type: "button",
|
|
5654
|
+
onClick: isEditMode ? handleSubmit : onSkip,
|
|
5655
|
+
disabled: isEditMode ? !canSubmit || submitting : submitting,
|
|
5656
|
+
style: { minWidth: "7.25rem", height: "2.35rem", opacity: (isEditMode ? canSubmit && !submitting : !submitting) ? 1 : 0.5 },
|
|
5657
|
+
children: submitting ? /* @__PURE__ */ jsxs("span", { style: { display: "inline-flex", alignItems: "center", gap: "0.45rem" }, children: [
|
|
5658
|
+
/* @__PURE__ */ jsx("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", style: { animation: "spin 1s linear infinite" }, children: /* @__PURE__ */ jsx("path", { d: "M21 12a9 9 0 1 1-6.219-8.56" }) }),
|
|
5659
|
+
"Enregistrement..."
|
|
5660
|
+
] }) : isEditMode ? "Enregistrer" : "Plus tard"
|
|
4447
5661
|
}
|
|
4448
5662
|
)
|
|
4449
|
-
] })
|
|
4450
|
-
|
|
5663
|
+
] }) }),
|
|
5664
|
+
/* @__PURE__ */ jsx(
|
|
5665
|
+
AvatarCropModal,
|
|
5666
|
+
{
|
|
5667
|
+
open: avatarPickerOpen,
|
|
5668
|
+
imageSrc: pendingAvatarSrc,
|
|
5669
|
+
onOpenChange: setAvatarPickerOpen,
|
|
5670
|
+
onCancel: () => {
|
|
5671
|
+
setAvatarPickerOpen(false);
|
|
5672
|
+
setPendingAvatarSrc(null);
|
|
5673
|
+
},
|
|
5674
|
+
onConfirm: handleAvatarConfirm
|
|
5675
|
+
}
|
|
5676
|
+
)
|
|
5677
|
+
] });
|
|
5678
|
+
const renderContactFlow = () => {
|
|
5679
|
+
if (!contactFlow) return null;
|
|
5680
|
+
const isEmail = contactFlow.kind === "email";
|
|
5681
|
+
const targetLabel = isEmail ? contactFlow.newEmail || "la nouvelle adresse" : `${contactFlow.newCcphone} ${contactFlow.newPhone}`.trim();
|
|
5682
|
+
const sentToLabel = currentReceiveLabel;
|
|
5683
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
5684
|
+
/* @__PURE__ */ jsxs(DialogHeader, { children: [
|
|
5685
|
+
/* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: "0.5rem", marginBottom: "0.25rem" }, children: [
|
|
5686
|
+
/* @__PURE__ */ jsx(ShieldIcon, {}),
|
|
5687
|
+
/* @__PURE__ */ jsx(DialogTitle, { children: isEmail ? "Changer l’email" : "Changer le téléphone" })
|
|
5688
|
+
] }),
|
|
5689
|
+
/* @__PURE__ */ jsx(DialogDescription, { children: contactFlow.phase === "request" ? `Veuillez renseigner le nouveau ${isEmail ? "email" : "numéro de téléphone"} pour lancer la vérification.` : contactFlow.phase === "verify-old" ? `Le code OTP a été envoyé sur : ${sentToLabel}.` : `Le second code OTP a été envoyé sur : ${sentToLabel}.` })
|
|
5690
|
+
] }),
|
|
5691
|
+
/* @__PURE__ */ jsx(DialogBody, { style: { maxHeight: "52vh", overflowY: "auto", paddingBottom: "0.25rem" }, children: /* @__PURE__ */ jsxs("div", { style: { display: "flex", flexDirection: "column", gap: "1rem", marginTop: "0.75rem" }, children: [
|
|
5692
|
+
contactFlow.error && /* @__PURE__ */ jsx("div", { style: { padding: "0.75rem", borderRadius: "0.5rem", backgroundColor: "#fef2f2", color: "#b91c1c", fontSize: "0.875rem" }, children: contactFlow.error }),
|
|
5693
|
+
contactFlow.phase === "request" && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
5694
|
+
isEmail ? /* @__PURE__ */ jsxs("div", { children: [
|
|
5695
|
+
/* @__PURE__ */ jsxs("div", { style: { marginBottom: "0.5rem", padding: "0.7rem 0.75rem", borderRadius: "0.5rem", backgroundColor: "#f9fafb", border: "1px solid #e5e7eb", fontSize: "0.85rem", color: C.gray700, lineHeight: 1.45 }, children: [
|
|
5696
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
5697
|
+
/* @__PURE__ */ jsx("strong", { children: "Email actuel :" }),
|
|
5698
|
+
" ",
|
|
5699
|
+
currentEmailLabel || "non renseigné"
|
|
5700
|
+
] }),
|
|
5701
|
+
/* @__PURE__ */ jsx("div", { style: { marginTop: "0.2rem", color: C.gray500 }, children: "Veuillez renseigner le nouveau email pour lancer la vérification." })
|
|
5702
|
+
] }),
|
|
5703
|
+
/* @__PURE__ */ jsx(Label, { style: { display: "block", marginBottom: "0.5rem", color: C.gray700, fontWeight: 500 }, children: "Nouvel email" }),
|
|
5704
|
+
/* @__PURE__ */ jsx(
|
|
5705
|
+
"input",
|
|
5706
|
+
{
|
|
5707
|
+
type: "email",
|
|
5708
|
+
value: contactFlow.newEmail,
|
|
5709
|
+
onChange: (e) => updateFlow({ newEmail: e.target.value }),
|
|
5710
|
+
placeholder: "nouvel@email.com",
|
|
5711
|
+
style: {
|
|
5712
|
+
width: "100%",
|
|
5713
|
+
height: "2.5rem",
|
|
5714
|
+
padding: "0 0.75rem",
|
|
5715
|
+
border: `1px solid ${C.gray200}`,
|
|
5716
|
+
borderRadius: "0.375rem",
|
|
5717
|
+
fontSize: "0.875rem",
|
|
5718
|
+
color: C.gray700,
|
|
5719
|
+
backgroundColor: C.white,
|
|
5720
|
+
outline: "none"
|
|
5721
|
+
}
|
|
5722
|
+
}
|
|
5723
|
+
),
|
|
5724
|
+
emailIsCurrent && /* @__PURE__ */ jsx("span", { style: { display: "block", marginTop: "0.35rem", color: "#b91c1c", fontSize: "0.8rem", lineHeight: 1.35 }, children: requestDuplicateMessage })
|
|
5725
|
+
] }) : /* @__PURE__ */ jsxs("div", { children: [
|
|
5726
|
+
/* @__PURE__ */ jsxs("div", { style: { marginBottom: "0.5rem", padding: "0.7rem 0.75rem", borderRadius: "0.5rem", backgroundColor: "#f9fafb", border: "1px solid #e5e7eb", fontSize: "0.85rem", color: C.gray700, lineHeight: 1.45 }, children: [
|
|
5727
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
5728
|
+
/* @__PURE__ */ jsx("strong", { children: "Numéro actuel :" }),
|
|
5729
|
+
" ",
|
|
5730
|
+
formatPhoneDisplay(currentPhoneDialDisplay, currentPhoneNumberDisplay) || "non renseigné"
|
|
5731
|
+
] }),
|
|
5732
|
+
/* @__PURE__ */ jsx("div", { style: { marginTop: "0.2rem", color: C.gray500 }, children: "Veuillez renseigner le nouveau numéro de téléphone pour lancer la vérification." })
|
|
5733
|
+
] }),
|
|
5734
|
+
/* @__PURE__ */ jsx(Label, { style: { display: "block", marginBottom: "0.5rem", color: C.gray700, fontWeight: 500 }, children: "Nouveau téléphone" }),
|
|
5735
|
+
/* @__PURE__ */ jsx(
|
|
5736
|
+
PhoneInput,
|
|
5737
|
+
{
|
|
5738
|
+
value: contactFlow.newPhone,
|
|
5739
|
+
onChange: (value) => updateFlow({ newPhone: value }),
|
|
5740
|
+
ccphone: contactFlow.newCcphone,
|
|
5741
|
+
onCcphoneChange: (value) => updateFlow({ newCcphone: value, method: value.trim() === "+221" ? contactFlow.method : "email" })
|
|
5742
|
+
}
|
|
5743
|
+
),
|
|
5744
|
+
phoneIsCurrent && /* @__PURE__ */ jsx("span", { style: { display: "block", marginTop: "0.35rem", color: "#b91c1c", fontSize: "0.8rem", lineHeight: 1.35 }, children: requestDuplicateMessage })
|
|
5745
|
+
] }),
|
|
5746
|
+
contactSmsAllowed ? /* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: "0.5rem", flexWrap: "wrap" }, children: [
|
|
5747
|
+
/* @__PURE__ */ jsx(
|
|
5748
|
+
Button,
|
|
5749
|
+
{
|
|
5750
|
+
type: "button",
|
|
5751
|
+
variant: contactFlow.method === "email" ? "default" : "outline",
|
|
5752
|
+
onClick: () => switchFlowMethod("email"),
|
|
5753
|
+
style: { flex: 1 },
|
|
5754
|
+
children: "Code par email"
|
|
5755
|
+
}
|
|
5756
|
+
),
|
|
5757
|
+
/* @__PURE__ */ jsx(
|
|
5758
|
+
Button,
|
|
5759
|
+
{
|
|
5760
|
+
type: "button",
|
|
5761
|
+
variant: contactFlow.method === "phone" ? "default" : "outline",
|
|
5762
|
+
onClick: () => switchFlowMethod("phone"),
|
|
5763
|
+
style: { flex: 1 },
|
|
5764
|
+
children: "Code par téléphone"
|
|
5765
|
+
}
|
|
5766
|
+
)
|
|
5767
|
+
] }) : /* @__PURE__ */ jsx("div", { style: { padding: "0.75rem", borderRadius: "0.5rem", backgroundColor: "#f9fafb", border: "1px solid #e5e7eb", color: C.gray700, fontSize: "0.875rem", lineHeight: 1.45 }, children: "Le SMS est disponible uniquement pour les numéros sénégalais (+221). Le code sera envoyé par email." }),
|
|
5768
|
+
/* @__PURE__ */ jsxs("span", { style: { color: C.gray500, fontSize: "0.8rem", lineHeight: 1.35 }, children: [
|
|
5769
|
+
"Vous allez recevoir le code par ",
|
|
5770
|
+
currentReceiveLabel,
|
|
5771
|
+
"."
|
|
5772
|
+
] })
|
|
5773
|
+
] }),
|
|
5774
|
+
contactFlow.phase === "verify-old" && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
5775
|
+
/* @__PURE__ */ jsxs("div", { style: { padding: "0.75rem", borderRadius: "0.5rem", backgroundColor: "#f9fafb", border: "1px solid #e5e7eb", fontSize: "0.875rem", color: C.gray700 }, children: [
|
|
5776
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
5777
|
+
/* @__PURE__ */ jsxs("strong", { children: [
|
|
5778
|
+
isEmail ? "Email actuel" : "Numéro de téléphone actuel",
|
|
5779
|
+
" :"
|
|
5780
|
+
] }),
|
|
5781
|
+
" ",
|
|
5782
|
+
isEmail ? currentEmailLabel : formatPhoneDisplay(currentPhoneDialDisplay, currentPhoneNumberDisplay)
|
|
5783
|
+
] }),
|
|
5784
|
+
/* @__PURE__ */ jsxs("div", { style: { marginTop: "0.25rem" }, children: [
|
|
5785
|
+
/* @__PURE__ */ jsx("strong", { children: "Nouveau :" }),
|
|
5786
|
+
" ",
|
|
5787
|
+
targetLabel
|
|
5788
|
+
] }),
|
|
5789
|
+
/* @__PURE__ */ jsxs("div", { style: { marginTop: "0.25rem" }, children: [
|
|
5790
|
+
/* @__PURE__ */ jsx("strong", { children: "Envoi :" }),
|
|
5791
|
+
" ",
|
|
5792
|
+
sentToLabel
|
|
5793
|
+
] })
|
|
5794
|
+
] }),
|
|
5795
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
5796
|
+
/* @__PURE__ */ jsx(Label, { style: { display: "block", marginBottom: "0.5rem", color: C.gray700, fontWeight: 500 }, children: "Code reçu sur l'ancien contact" }),
|
|
5797
|
+
/* @__PURE__ */ jsx(
|
|
5798
|
+
OTPInput,
|
|
5799
|
+
{
|
|
5800
|
+
value: contactFlow.oldOtp,
|
|
5801
|
+
onChange: (value) => updateFlow({ oldOtp: value }),
|
|
5802
|
+
disabled: contactFlow.loading
|
|
5803
|
+
}
|
|
5804
|
+
)
|
|
5805
|
+
] }),
|
|
5806
|
+
/* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", justifyContent: "space-between", gap: "0.75rem", flexWrap: "wrap" }, children: [
|
|
5807
|
+
/* @__PURE__ */ jsx("div", { style: { fontSize: "0.82rem", color: C.gray500 }, children: canResendOtp ? "Vous pouvez renvoyer un nouveau code." : `Renvoyer un code dans ${resendCountdown}s` }),
|
|
5808
|
+
canResendOtp && /* @__PURE__ */ jsx(
|
|
5809
|
+
Button,
|
|
5810
|
+
{
|
|
5811
|
+
type: "button",
|
|
5812
|
+
variant: "outline",
|
|
5813
|
+
onClick: () => setConfirmAction("resend"),
|
|
5814
|
+
disabled: contactFlow.loading,
|
|
5815
|
+
style: { height: "2.35rem" },
|
|
5816
|
+
children: "Renvoyer le code"
|
|
5817
|
+
}
|
|
5818
|
+
)
|
|
5819
|
+
] })
|
|
5820
|
+
] }),
|
|
5821
|
+
contactFlow.phase === "verify-new" && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
5822
|
+
/* @__PURE__ */ jsxs("div", { style: { padding: "0.75rem", borderRadius: "0.5rem", backgroundColor: "#f9fafb", border: "1px solid #e5e7eb", fontSize: "0.875rem", color: C.gray700 }, children: [
|
|
5823
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
5824
|
+
/* @__PURE__ */ jsxs("strong", { children: [
|
|
5825
|
+
isEmail ? "Email actuel" : "Numéro de téléphone actuel",
|
|
5826
|
+
" :"
|
|
5827
|
+
] }),
|
|
5828
|
+
" ",
|
|
5829
|
+
isEmail ? currentEmailLabel : formatPhoneDisplay(currentPhoneDialDisplay, currentPhoneNumberDisplay)
|
|
5830
|
+
] }),
|
|
5831
|
+
/* @__PURE__ */ jsxs("div", { style: { marginTop: "0.25rem" }, children: [
|
|
5832
|
+
/* @__PURE__ */ jsx("strong", { children: "Nouveau :" }),
|
|
5833
|
+
" ",
|
|
5834
|
+
targetLabel
|
|
5835
|
+
] }),
|
|
5836
|
+
/* @__PURE__ */ jsxs("div", { style: { marginTop: "0.25rem" }, children: [
|
|
5837
|
+
/* @__PURE__ */ jsx("strong", { children: "Envoi :" }),
|
|
5838
|
+
" ",
|
|
5839
|
+
sentToLabel
|
|
5840
|
+
] })
|
|
5841
|
+
] }),
|
|
5842
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
5843
|
+
/* @__PURE__ */ jsx(Label, { style: { display: "block", marginBottom: "0.5rem", color: C.gray700, fontWeight: 500 }, children: "Code reçu sur le nouveau contact" }),
|
|
5844
|
+
/* @__PURE__ */ jsx(
|
|
5845
|
+
OTPInput,
|
|
5846
|
+
{
|
|
5847
|
+
value: contactFlow.newOtp,
|
|
5848
|
+
onChange: (value) => updateFlow({ newOtp: value }),
|
|
5849
|
+
disabled: contactFlow.loading
|
|
5850
|
+
}
|
|
5851
|
+
)
|
|
5852
|
+
] }),
|
|
5853
|
+
/* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", justifyContent: "space-between", gap: "0.75rem", flexWrap: "wrap" }, children: [
|
|
5854
|
+
/* @__PURE__ */ jsx("div", { style: { fontSize: "0.82rem", color: C.gray500 }, children: canResendOtp ? "Vous pouvez renvoyer un nouveau code." : `Renvoyer un code dans ${resendCountdown}s` }),
|
|
5855
|
+
canResendOtp && /* @__PURE__ */ jsx(
|
|
5856
|
+
Button,
|
|
5857
|
+
{
|
|
5858
|
+
type: "button",
|
|
5859
|
+
variant: "outline",
|
|
5860
|
+
onClick: () => setConfirmAction("resend"),
|
|
5861
|
+
disabled: contactFlow.loading,
|
|
5862
|
+
style: { height: "2.35rem" },
|
|
5863
|
+
children: "Renvoyer le code"
|
|
5864
|
+
}
|
|
5865
|
+
)
|
|
5866
|
+
] })
|
|
5867
|
+
] })
|
|
5868
|
+
] }) }),
|
|
5869
|
+
/* @__PURE__ */ jsx(DialogFooter, { style: { flexDirection: "row", justifyContent: "space-between", alignItems: "center" }, children: contactFlow.phase === "request" ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
5870
|
+
/* @__PURE__ */ jsx(
|
|
5871
|
+
Button,
|
|
5872
|
+
{
|
|
5873
|
+
type: "button",
|
|
5874
|
+
variant: "outline",
|
|
5875
|
+
onClick: closeContactFlow,
|
|
5876
|
+
disabled: contactFlow.loading,
|
|
5877
|
+
style: { minWidth: "6.5rem", height: "2.35rem" },
|
|
5878
|
+
children: "Fermer"
|
|
5879
|
+
}
|
|
5880
|
+
),
|
|
5881
|
+
/* @__PURE__ */ jsx(
|
|
5882
|
+
Button,
|
|
5883
|
+
{
|
|
5884
|
+
type: "button",
|
|
5885
|
+
onClick: requestChange,
|
|
5886
|
+
disabled: contactFlow.loading || (isEmail ? !contactFlow.newEmail.trim() : !contactFlow.newPhone.trim()) || emailIsCurrent || phoneIsCurrent,
|
|
5887
|
+
style: { minWidth: "7.25rem", height: "2.35rem", opacity: contactFlow.loading ? 0.5 : 1 },
|
|
5888
|
+
children: contactFlow.loading ? /* @__PURE__ */ jsxs("span", { style: { display: "inline-flex", alignItems: "center", gap: "0.45rem" }, children: [
|
|
5889
|
+
/* @__PURE__ */ jsx("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", style: { animation: "spin 1s linear infinite" }, children: /* @__PURE__ */ jsx("path", { d: "M21 12a9 9 0 1 1-6.219-8.56" }) }),
|
|
5890
|
+
"Envoi..."
|
|
5891
|
+
] }) : "Envoyer"
|
|
5892
|
+
}
|
|
5893
|
+
)
|
|
5894
|
+
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
5895
|
+
/* @__PURE__ */ jsx(
|
|
5896
|
+
Button,
|
|
5897
|
+
{
|
|
5898
|
+
type: "button",
|
|
5899
|
+
variant: "outline",
|
|
5900
|
+
onClick: () => setConfirmAction("back"),
|
|
5901
|
+
disabled: contactFlow.loading,
|
|
5902
|
+
style: { minWidth: "6.5rem", height: "2.35rem" },
|
|
5903
|
+
children: "Retour"
|
|
5904
|
+
}
|
|
5905
|
+
),
|
|
5906
|
+
/* @__PURE__ */ jsx(
|
|
5907
|
+
Button,
|
|
5908
|
+
{
|
|
5909
|
+
type: "button",
|
|
5910
|
+
onClick: contactFlow.phase === "verify-old" ? verifyOld : verifyNew,
|
|
5911
|
+
disabled: contactFlow.loading || contactFlow.phase === "verify-old" && contactFlow.oldOtp.length !== 6 || contactFlow.phase === "verify-new" && contactFlow.newOtp.length !== 6,
|
|
5912
|
+
style: { minWidth: "7.25rem", height: "2.35rem", opacity: contactFlow.loading ? 0.5 : 1 },
|
|
5913
|
+
children: contactFlow.loading ? /* @__PURE__ */ jsxs("span", { style: { display: "inline-flex", alignItems: "center", gap: "0.45rem" }, children: [
|
|
5914
|
+
/* @__PURE__ */ jsx("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", style: { animation: "spin 1s linear infinite" }, children: /* @__PURE__ */ jsx("path", { d: "M21 12a9 9 0 1 1-6.219-8.56" }) }),
|
|
5915
|
+
"Vérification..."
|
|
5916
|
+
] }) : contactFlow.phase === "verify-old" ? "Vérifier" : "Confirmer"
|
|
5917
|
+
}
|
|
5918
|
+
)
|
|
5919
|
+
] }) })
|
|
5920
|
+
] });
|
|
5921
|
+
};
|
|
5922
|
+
const renderConfirmDialog = () => {
|
|
5923
|
+
if (!confirmAction) return null;
|
|
5924
|
+
const config2 = confirmAction === "back" ? {
|
|
5925
|
+
title: "Retourner à l’étape précédente ?",
|
|
5926
|
+
description: "La vérification en cours sera interrompue. Vous pourrez reprendre le changement depuis le début.",
|
|
5927
|
+
confirmLabel: "Retourner"
|
|
5928
|
+
} : confirmAction === "resend" ? {
|
|
5929
|
+
title: "Renvoyer un nouveau code OTP ?",
|
|
5930
|
+
description: `Un nouveau code sera envoyé vers ${currentReceiveLabel}.`,
|
|
5931
|
+
confirmLabel: "Renvoyer"
|
|
5932
|
+
} : {
|
|
5933
|
+
title: "Annuler la récupération ?",
|
|
5934
|
+
description: "Les informations IAM ne seront pas récupérées maintenant. Vous pourrez relancer l’ouverture plus tard.",
|
|
5935
|
+
confirmLabel: "Annuler"
|
|
5936
|
+
};
|
|
5937
|
+
const confirm = async () => {
|
|
5938
|
+
if (confirmAction === "back") {
|
|
5939
|
+
confirmBackToRequest();
|
|
5940
|
+
} else if (confirmAction === "resend") {
|
|
5941
|
+
await confirmResendOtp();
|
|
5942
|
+
} else {
|
|
5943
|
+
setConfirmAction(null);
|
|
5944
|
+
onDismiss();
|
|
5945
|
+
}
|
|
5946
|
+
};
|
|
5947
|
+
return /* @__PURE__ */ jsx(Dialog, { open: Boolean(confirmAction), onOpenChange: (open2) => {
|
|
5948
|
+
if (!open2) setConfirmAction(null);
|
|
5949
|
+
}, children: /* @__PURE__ */ jsxs(DialogContent, { hideCloseButton: false, style: { maxWidth: "22rem", padding: "0.9rem 1rem", borderRadius: "0.7rem" }, children: [
|
|
5950
|
+
/* @__PURE__ */ jsxs(DialogHeader, { children: [
|
|
5951
|
+
/* @__PURE__ */ jsx("div", { style: { fontSize: "1rem", fontWeight: 600, color: "#111827" }, children: config2.title }),
|
|
5952
|
+
/* @__PURE__ */ jsx(DialogDescription, { children: config2.description })
|
|
5953
|
+
] }),
|
|
5954
|
+
/* @__PURE__ */ jsxs(DialogFooter, { style: { flexDirection: "row", justifyContent: "space-between", alignItems: "center" }, children: [
|
|
5955
|
+
/* @__PURE__ */ jsx(Button, { type: "button", variant: "outline", onClick: () => setConfirmAction(null), style: { minWidth: "6rem", height: "2.25rem" }, children: "Annuler" }),
|
|
5956
|
+
/* @__PURE__ */ jsx(Button, { type: "button", onClick: confirm, style: { minWidth: "6.5rem", height: "2.25rem" }, children: confirmAction === "resend" && (contactFlow == null ? void 0 : contactFlow.loading) ? "Envoi..." : config2.confirmLabel })
|
|
5957
|
+
] })
|
|
5958
|
+
] }) });
|
|
5959
|
+
};
|
|
5960
|
+
return /* @__PURE__ */ jsxs(Dialog, { open, onOpenChange: handleDialogOpenChange, children: [
|
|
5961
|
+
/* @__PURE__ */ jsx(
|
|
5962
|
+
DialogContent,
|
|
5963
|
+
{
|
|
5964
|
+
hideCloseButton: lockModalClose,
|
|
5965
|
+
style: { maxWidth: "26rem", padding: "0.75rem 0.8rem", borderRadius: "0.65rem", maxHeight: "88vh" },
|
|
5966
|
+
children: profileHydrating && submitState === "idle" && !contactFlow ? renderHydration() : contactFlow ? renderContactFlow() : submitState === "success" ? renderSuccess() : renderBase()
|
|
5967
|
+
}
|
|
5968
|
+
),
|
|
5969
|
+
renderConfirmDialog()
|
|
5970
|
+
] });
|
|
4451
5971
|
}
|
|
4452
|
-
function DebugPanel({ saasApiUrl, iamApiUrl }) {
|
|
5972
|
+
function DebugPanel({ saasApiUrl, iamApiUrl, onOpenLogin, onOpenSignup, onOpenOnboarding, onResetProfilePrompt }) {
|
|
4453
5973
|
const [logs, setLogs] = useState([]);
|
|
4454
5974
|
const [expanded, setExpanded] = useState(true);
|
|
4455
5975
|
const [selectedLog, setSelectedLog] = useState(null);
|
|
@@ -4543,7 +6063,7 @@ function DebugPanel({ saasApiUrl, iamApiUrl }) {
|
|
|
4543
6063
|
maxHeight: "300px",
|
|
4544
6064
|
overflowY: "auto"
|
|
4545
6065
|
}, children: [
|
|
4546
|
-
/* @__PURE__ */ jsxs("div", { style: { padding: "4px 12px", background: "#1e1e3f", fontSize: "10px", color: "#94a3b8", display: "flex", gap: "16px" }, children: [
|
|
6066
|
+
/* @__PURE__ */ jsxs("div", { style: { padding: "4px 12px", background: "#1e1e3f", fontSize: "10px", color: "#94a3b8", display: "flex", gap: "16px", alignItems: "center", flexWrap: "wrap" }, children: [
|
|
4547
6067
|
/* @__PURE__ */ jsxs("span", { children: [
|
|
4548
6068
|
"SaaS: ",
|
|
4549
6069
|
/* @__PURE__ */ jsx("span", { style: { color: "#6366f1" }, children: saasApiUrl })
|
|
@@ -4551,6 +6071,56 @@ function DebugPanel({ saasApiUrl, iamApiUrl }) {
|
|
|
4551
6071
|
/* @__PURE__ */ jsxs("span", { children: [
|
|
4552
6072
|
"IAM: ",
|
|
4553
6073
|
/* @__PURE__ */ jsx("span", { style: { color: "#6366f1" }, children: iamApiUrl })
|
|
6074
|
+
] }),
|
|
6075
|
+
/* @__PURE__ */ jsxs("div", { style: { marginLeft: "auto", display: "flex", gap: "6px", flexWrap: "wrap" }, children: [
|
|
6076
|
+
/* @__PURE__ */ jsx(
|
|
6077
|
+
"button",
|
|
6078
|
+
{
|
|
6079
|
+
onClick: (e) => {
|
|
6080
|
+
e.stopPropagation();
|
|
6081
|
+
onOpenLogin == null ? void 0 : onOpenLogin();
|
|
6082
|
+
},
|
|
6083
|
+
disabled: !onOpenLogin,
|
|
6084
|
+
style: { background: "#334155", color: "#e2e8f0", border: "none", borderRadius: "4px", padding: "2px 8px", cursor: onOpenLogin ? "pointer" : "not-allowed", fontSize: "11px", opacity: onOpenLogin ? 1 : 0.5 },
|
|
6085
|
+
children: "Connexion"
|
|
6086
|
+
}
|
|
6087
|
+
),
|
|
6088
|
+
/* @__PURE__ */ jsx(
|
|
6089
|
+
"button",
|
|
6090
|
+
{
|
|
6091
|
+
onClick: (e) => {
|
|
6092
|
+
e.stopPropagation();
|
|
6093
|
+
onOpenSignup == null ? void 0 : onOpenSignup();
|
|
6094
|
+
},
|
|
6095
|
+
disabled: !onOpenSignup,
|
|
6096
|
+
style: { background: "#334155", color: "#e2e8f0", border: "none", borderRadius: "4px", padding: "2px 8px", cursor: onOpenSignup ? "pointer" : "not-allowed", fontSize: "11px", opacity: onOpenSignup ? 1 : 0.5 },
|
|
6097
|
+
children: "Inscription"
|
|
6098
|
+
}
|
|
6099
|
+
),
|
|
6100
|
+
/* @__PURE__ */ jsx(
|
|
6101
|
+
"button",
|
|
6102
|
+
{
|
|
6103
|
+
onClick: (e) => {
|
|
6104
|
+
e.stopPropagation();
|
|
6105
|
+
onOpenOnboarding == null ? void 0 : onOpenOnboarding("current");
|
|
6106
|
+
},
|
|
6107
|
+
disabled: !onOpenOnboarding,
|
|
6108
|
+
style: { background: "#334155", color: "#e2e8f0", border: "none", borderRadius: "4px", padding: "2px 8px", cursor: onOpenOnboarding ? "pointer" : "not-allowed", fontSize: "11px", opacity: onOpenOnboarding ? 1 : 0.5 },
|
|
6109
|
+
children: "Infos profile"
|
|
6110
|
+
}
|
|
6111
|
+
),
|
|
6112
|
+
/* @__PURE__ */ jsx(
|
|
6113
|
+
"button",
|
|
6114
|
+
{
|
|
6115
|
+
onClick: (e) => {
|
|
6116
|
+
e.stopPropagation();
|
|
6117
|
+
onResetProfilePrompt == null ? void 0 : onResetProfilePrompt();
|
|
6118
|
+
},
|
|
6119
|
+
disabled: !onResetProfilePrompt,
|
|
6120
|
+
style: { background: "#334155", color: "#e2e8f0", border: "none", borderRadius: "4px", padding: "2px 8px", cursor: onResetProfilePrompt ? "pointer" : "not-allowed", fontSize: "11px", opacity: onResetProfilePrompt ? 1 : 0.5 },
|
|
6121
|
+
children: "Reset profil"
|
|
6122
|
+
}
|
|
6123
|
+
)
|
|
4554
6124
|
] })
|
|
4555
6125
|
] }),
|
|
4556
6126
|
logs.length === 0 ? /* @__PURE__ */ jsx("div", { style: { padding: "20px", textAlign: "center", color: "#64748b" }, children: "Aucun appel API. Tentez une connexion pour voir les logs." }) : /* @__PURE__ */ jsx("div", { children: logs.map((log) => /* @__PURE__ */ jsxs("div", { children: [
|
|
@@ -4606,6 +6176,11 @@ function DebugPanel({ saasApiUrl, iamApiUrl }) {
|
|
|
4606
6176
|
] });
|
|
4607
6177
|
}
|
|
4608
6178
|
const ollaidLogo = "data:image/png;base64,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";
|
|
6179
|
+
const PROFILE_PROMPT_DELAY_MS = 5 * 60 * 1e3;
|
|
6180
|
+
const PROFILE_PROMPT_SNOOZE_MS = 8 * 60 * 60 * 1e3;
|
|
6181
|
+
const PROFILE_SYNC_INTERVAL_MS = 30 * 60 * 1e3;
|
|
6182
|
+
const PROFILE_FETCH_RETRY_DELAY_MS = 1200;
|
|
6183
|
+
const PROFILE_FETCH_MAX_ATTEMPTS = 3;
|
|
4609
6184
|
const COLORS = {
|
|
4610
6185
|
primary: "#002147",
|
|
4611
6186
|
primaryForeground: "#ffffff",
|
|
@@ -4642,7 +6217,10 @@ const ShieldCheckIcon = ({ size = 24, color = COLORS.accent }) => /* @__PURE__ *
|
|
|
4642
6217
|
/* @__PURE__ */ jsx("path", { d: "m9 12 2 2 4-4" })
|
|
4643
6218
|
] });
|
|
4644
6219
|
function needsOnboarding(user) {
|
|
4645
|
-
return !user.image_url || !user.phone;
|
|
6220
|
+
return !user.image_url || !user.phone || !user.email;
|
|
6221
|
+
}
|
|
6222
|
+
function isProfileComplete(user) {
|
|
6223
|
+
return Boolean(user.image_url && user.phone && user.email);
|
|
4646
6224
|
}
|
|
4647
6225
|
const GRADIENT_STYLE_ID = "ollaid-sso-gradient-anim";
|
|
4648
6226
|
function injectGradientAnimation() {
|
|
@@ -4699,6 +6277,7 @@ function NativeSSOPage({
|
|
|
4699
6277
|
const [loginInitialPhone, setLoginInitialPhone] = useState();
|
|
4700
6278
|
const [showOnboarding, setShowOnboarding] = useState(false);
|
|
4701
6279
|
const [pendingSession, setPendingSession] = useState(null);
|
|
6280
|
+
const [debugOnboardingState, setDebugOnboardingState] = useState(null);
|
|
4702
6281
|
const [session, setSession] = useState(() => {
|
|
4703
6282
|
try {
|
|
4704
6283
|
const token = localStorage.getItem(STORAGE.AUTH_TOKEN) || localStorage.getItem(STORAGE.TOKEN);
|
|
@@ -4709,6 +6288,135 @@ function NativeSSOPage({
|
|
|
4709
6288
|
return null;
|
|
4710
6289
|
});
|
|
4711
6290
|
const { isDebug: resolvedDebug } = useNativeAuth({ saasApiUrl, iamApiUrl, configPrefix, autoLoadCredentials: true });
|
|
6291
|
+
const onboardingPromptTimerRef = useRef(null);
|
|
6292
|
+
const onboardingRecheckTimerRef = useRef(null);
|
|
6293
|
+
const profileSyncTimerRef = useRef(null);
|
|
6294
|
+
const profileHydrationRunningRef = useRef(false);
|
|
6295
|
+
const profileHydrationRequestedRef = useRef(null);
|
|
6296
|
+
const sessionRef = useRef(session);
|
|
6297
|
+
const [profileHydrating, setProfileHydrating] = useState(false);
|
|
6298
|
+
useEffect(() => {
|
|
6299
|
+
sessionRef.current = session;
|
|
6300
|
+
}, [session]);
|
|
6301
|
+
const clearOnboardingTimers = useCallback(() => {
|
|
6302
|
+
if (onboardingPromptTimerRef.current) {
|
|
6303
|
+
clearTimeout(onboardingPromptTimerRef.current);
|
|
6304
|
+
onboardingPromptTimerRef.current = null;
|
|
6305
|
+
}
|
|
6306
|
+
if (onboardingRecheckTimerRef.current) {
|
|
6307
|
+
clearTimeout(onboardingRecheckTimerRef.current);
|
|
6308
|
+
onboardingRecheckTimerRef.current = null;
|
|
6309
|
+
}
|
|
6310
|
+
}, []);
|
|
6311
|
+
const persistSessionUser = useCallback((token, user) => {
|
|
6312
|
+
setSession({ token, user });
|
|
6313
|
+
localStorage.setItem(STORAGE.USER, JSON.stringify(user));
|
|
6314
|
+
localStorage.setItem(STORAGE.AUTH_TOKEN, token);
|
|
6315
|
+
localStorage.setItem(STORAGE.TOKEN, token);
|
|
6316
|
+
}, []);
|
|
6317
|
+
const extractUserInfos = useCallback((payload) => {
|
|
6318
|
+
if (!payload || typeof payload !== "object") return null;
|
|
6319
|
+
const data = payload;
|
|
6320
|
+
const candidate = data.user_infos && typeof data.user_infos === "object" ? data.user_infos : data.user && typeof data.user === "object" ? data.user : data;
|
|
6321
|
+
return {
|
|
6322
|
+
reference: typeof candidate.reference === "string" ? candidate.reference : void 0,
|
|
6323
|
+
name: typeof candidate.name === "string" ? candidate.name : void 0,
|
|
6324
|
+
email: typeof candidate.email === "string" ? candidate.email : void 0,
|
|
6325
|
+
ccphone: typeof candidate.ccphone === "string" ? candidate.ccphone : void 0,
|
|
6326
|
+
phone: typeof candidate.phone === "string" ? candidate.phone : void 0,
|
|
6327
|
+
address: typeof candidate.address === "string" ? candidate.address : void 0,
|
|
6328
|
+
town: typeof candidate.town === "string" ? candidate.town : void 0,
|
|
6329
|
+
country: typeof candidate.country === "string" ? candidate.country : void 0,
|
|
6330
|
+
image_url: typeof candidate.image_url === "string" ? candidate.image_url : void 0,
|
|
6331
|
+
auth_2fa: typeof candidate.auth_2fa === "boolean" ? candidate.auth_2fa : void 0,
|
|
6332
|
+
alias_reference: typeof candidate.alias_reference === "string" ? candidate.alias_reference : void 0,
|
|
6333
|
+
iam_reference: typeof candidate.iam_reference === "string" ? candidate.iam_reference : void 0
|
|
6334
|
+
};
|
|
6335
|
+
}, []);
|
|
6336
|
+
const refreshSessionProfile = useCallback(async (force = false) => {
|
|
6337
|
+
const activeSession = sessionRef.current;
|
|
6338
|
+
if (!(activeSession == null ? void 0 : activeSession.token)) return null;
|
|
6339
|
+
if (profileHydrationRunningRef.current && !force) return activeSession.user;
|
|
6340
|
+
if (profileHydrationRequestedRef.current === activeSession.token && !force) return activeSession.user;
|
|
6341
|
+
profileHydrationRunningRef.current = true;
|
|
6342
|
+
profileHydrationRequestedRef.current = activeSession.token;
|
|
6343
|
+
setProfileHydrating(true);
|
|
6344
|
+
try {
|
|
6345
|
+
let lastError = null;
|
|
6346
|
+
for (let attempt = 1; attempt <= PROFILE_FETCH_MAX_ATTEMPTS; attempt += 1) {
|
|
6347
|
+
try {
|
|
6348
|
+
const response = await profileService.getProfile();
|
|
6349
|
+
const mergedInfos = extractUserInfos(response) || {};
|
|
6350
|
+
const mergedUser = { ...activeSession.user, ...mergedInfos };
|
|
6351
|
+
persistSessionUser(activeSession.token, mergedUser);
|
|
6352
|
+
return mergedUser;
|
|
6353
|
+
} catch (error) {
|
|
6354
|
+
lastError = error;
|
|
6355
|
+
if (attempt < PROFILE_FETCH_MAX_ATTEMPTS) {
|
|
6356
|
+
await new Promise((resolve) => setTimeout(resolve, PROFILE_FETCH_RETRY_DELAY_MS * attempt));
|
|
6357
|
+
}
|
|
6358
|
+
}
|
|
6359
|
+
}
|
|
6360
|
+
const isDev = Boolean(false);
|
|
6361
|
+
if (isDev) {
|
|
6362
|
+
console.warn("Profile hydration failed after retries", lastError);
|
|
6363
|
+
}
|
|
6364
|
+
return activeSession.user;
|
|
6365
|
+
} finally {
|
|
6366
|
+
profileHydrationRunningRef.current = false;
|
|
6367
|
+
setProfileHydrating(false);
|
|
6368
|
+
}
|
|
6369
|
+
}, [persistSessionUser, extractUserInfos]);
|
|
6370
|
+
const makeDebugOnboardingUser = useCallback((baseUser, preset) => {
|
|
6371
|
+
const normalized = { ...baseUser };
|
|
6372
|
+
if (preset === "current") return normalized;
|
|
6373
|
+
if (preset === "photo") return { ...normalized, image_url: "" };
|
|
6374
|
+
if (preset === "phone") return { ...normalized, phone: "", ccphone: "" };
|
|
6375
|
+
if (preset === "email") return { ...normalized, email: "" };
|
|
6376
|
+
return { ...normalized, image_url: "", phone: "", ccphone: "", email: "" };
|
|
6377
|
+
}, []);
|
|
6378
|
+
const syncProfilePrompt = useCallback((currentSession) => {
|
|
6379
|
+
clearOnboardingTimers();
|
|
6380
|
+
if (!currentSession) {
|
|
6381
|
+
setPendingSession(null);
|
|
6382
|
+
setShowOnboarding(false);
|
|
6383
|
+
return;
|
|
6384
|
+
}
|
|
6385
|
+
const promptState = getProfilePromptState();
|
|
6386
|
+
const now = Date.now();
|
|
6387
|
+
const onboardingVisible = showOnboarding || Boolean(pendingSession || debugOnboardingState);
|
|
6388
|
+
if (promptState.lastStatus === true || isProfileComplete(currentSession.user)) {
|
|
6389
|
+
markProfilePromptComplete();
|
|
6390
|
+
if (!onboardingVisible) {
|
|
6391
|
+
setPendingSession(null);
|
|
6392
|
+
setShowOnboarding(false);
|
|
6393
|
+
}
|
|
6394
|
+
return;
|
|
6395
|
+
}
|
|
6396
|
+
setPendingSession({ ...currentSession, sourceUser: currentSession.user });
|
|
6397
|
+
const scheduleOpen = () => {
|
|
6398
|
+
onboardingPromptTimerRef.current = setTimeout(() => {
|
|
6399
|
+
const latestPromptState = getProfilePromptState();
|
|
6400
|
+
if (latestPromptState.lastStatus === true) return;
|
|
6401
|
+
if (!currentSession) return;
|
|
6402
|
+
if (!isProfileComplete(currentSession.user)) {
|
|
6403
|
+
setShowOnboarding(true);
|
|
6404
|
+
}
|
|
6405
|
+
}, PROFILE_PROMPT_DELAY_MS);
|
|
6406
|
+
};
|
|
6407
|
+
if (promptState.recheckAt && promptState.recheckAt > now) {
|
|
6408
|
+
onboardingRecheckTimerRef.current = setTimeout(() => {
|
|
6409
|
+
const latestPromptState = getProfilePromptState();
|
|
6410
|
+
if (latestPromptState.lastStatus === true) return;
|
|
6411
|
+
if (!currentSession) return;
|
|
6412
|
+
if (!isProfileComplete(currentSession.user)) {
|
|
6413
|
+
scheduleOpen();
|
|
6414
|
+
}
|
|
6415
|
+
}, promptState.recheckAt - now);
|
|
6416
|
+
return;
|
|
6417
|
+
}
|
|
6418
|
+
scheduleOpen();
|
|
6419
|
+
}, [clearOnboardingTimers, showOnboarding, pendingSession, debugOnboardingState]);
|
|
4712
6420
|
useEffect(() => {
|
|
4713
6421
|
injectGradientAnimation();
|
|
4714
6422
|
const root = document.documentElement;
|
|
@@ -4768,40 +6476,166 @@ function NativeSSOPage({
|
|
|
4768
6476
|
account_type: user.account_type || "user"
|
|
4769
6477
|
};
|
|
4770
6478
|
setModal("none");
|
|
4771
|
-
|
|
4772
|
-
|
|
4773
|
-
|
|
4774
|
-
|
|
4775
|
-
|
|
6479
|
+
persistSessionUser(token, userObj);
|
|
6480
|
+
syncProfilePrompt({ token, user: userObj });
|
|
6481
|
+
void refreshSessionProfile(true);
|
|
6482
|
+
if (!needsOnboarding(userObj)) {
|
|
6483
|
+
markProfilePromptComplete();
|
|
4776
6484
|
onLoginSuccess == null ? void 0 : onLoginSuccess(token, user);
|
|
4777
6485
|
if (redirectAfterLogin) window.location.href = redirectAfterLogin;
|
|
4778
6486
|
}
|
|
4779
|
-
}, [onLoginSuccess, redirectAfterLogin]);
|
|
6487
|
+
}, [onLoginSuccess, redirectAfterLogin, persistSessionUser, syncProfilePrompt, refreshSessionProfile]);
|
|
4780
6488
|
const handleOnboardingComplete = useCallback((data) => {
|
|
4781
|
-
|
|
4782
|
-
|
|
4783
|
-
|
|
6489
|
+
const activeSession = debugOnboardingState || pendingSession;
|
|
6490
|
+
if (!activeSession) return;
|
|
6491
|
+
const updatedUser = { ...activeSession.sourceUser, ...data.user_infos || data };
|
|
6492
|
+
persistSessionUser(activeSession.token, updatedUser);
|
|
6493
|
+
onOnboardingComplete == null ? void 0 : onOnboardingComplete({
|
|
6494
|
+
name: updatedUser.name,
|
|
6495
|
+
image_url: updatedUser.image_url,
|
|
6496
|
+
ccphone: updatedUser.ccphone || void 0,
|
|
6497
|
+
phone: updatedUser.phone || void 0,
|
|
6498
|
+
email: updatedUser.email || void 0,
|
|
6499
|
+
address: updatedUser.address || void 0,
|
|
6500
|
+
town: updatedUser.town || void 0,
|
|
6501
|
+
country: updatedUser.country || void 0,
|
|
6502
|
+
user_infos: {
|
|
6503
|
+
reference: updatedUser.reference,
|
|
6504
|
+
name: updatedUser.name,
|
|
6505
|
+
email: updatedUser.email || null,
|
|
6506
|
+
ccphone: updatedUser.ccphone,
|
|
6507
|
+
phone: updatedUser.phone,
|
|
6508
|
+
address: updatedUser.address,
|
|
6509
|
+
town: updatedUser.town,
|
|
6510
|
+
country: updatedUser.country,
|
|
6511
|
+
image_url: updatedUser.image_url,
|
|
6512
|
+
auth_2fa: updatedUser.auth_2fa,
|
|
6513
|
+
alias_reference: updatedUser.alias_reference,
|
|
6514
|
+
iam_reference: updatedUser.iam_reference
|
|
6515
|
+
}
|
|
6516
|
+
});
|
|
6517
|
+
if (!debugOnboardingState) {
|
|
6518
|
+
markProfilePromptComplete();
|
|
6519
|
+
}
|
|
6520
|
+
void refreshSessionProfile(true);
|
|
6521
|
+
}, [pendingSession, debugOnboardingState, onOnboardingComplete, persistSessionUser, markProfilePromptComplete, refreshSessionProfile]);
|
|
6522
|
+
const handleOnboardingSkip = useCallback(() => {
|
|
6523
|
+
const activeSession = debugOnboardingState || pendingSession;
|
|
6524
|
+
if (!activeSession) return;
|
|
4784
6525
|
setShowOnboarding(false);
|
|
4785
|
-
setSession({ token: pendingSession.token, user: updatedUser });
|
|
4786
6526
|
setPendingSession(null);
|
|
4787
|
-
|
|
4788
|
-
|
|
4789
|
-
|
|
4790
|
-
|
|
4791
|
-
|
|
4792
|
-
|
|
6527
|
+
setDebugOnboardingState(null);
|
|
6528
|
+
if (!debugOnboardingState) {
|
|
6529
|
+
persistSessionUser(activeSession.token, activeSession.user);
|
|
6530
|
+
snoozeProfilePrompt(PROFILE_PROMPT_SNOOZE_MS / (60 * 60 * 1e3));
|
|
6531
|
+
onLoginSuccess == null ? void 0 : onLoginSuccess(activeSession.token, activeSession.user);
|
|
6532
|
+
if (redirectAfterLogin) window.location.href = redirectAfterLogin;
|
|
6533
|
+
return;
|
|
6534
|
+
}
|
|
6535
|
+
}, [pendingSession, debugOnboardingState, onLoginSuccess, redirectAfterLogin, persistSessionUser]);
|
|
6536
|
+
const handleOnboardingDismiss = useCallback(() => {
|
|
4793
6537
|
setShowOnboarding(false);
|
|
4794
|
-
setSession(pendingSession);
|
|
4795
6538
|
setPendingSession(null);
|
|
4796
|
-
|
|
4797
|
-
|
|
4798
|
-
}, [pendingSession, onLoginSuccess, redirectAfterLogin]);
|
|
6539
|
+
setDebugOnboardingState(null);
|
|
6540
|
+
}, []);
|
|
4799
6541
|
const handleLogout = useCallback(async () => {
|
|
4800
6542
|
await logout();
|
|
4801
6543
|
setSession(null);
|
|
6544
|
+
setPendingSession(null);
|
|
6545
|
+
setDebugOnboardingState(null);
|
|
6546
|
+
clearOnboardingTimers();
|
|
4802
6547
|
onLogout == null ? void 0 : onLogout();
|
|
4803
6548
|
if (redirectAfterLogout) window.location.href = redirectAfterLogout;
|
|
4804
|
-
}, [onLogout, redirectAfterLogout]);
|
|
6549
|
+
}, [onLogout, redirectAfterLogout, clearOnboardingTimers]);
|
|
6550
|
+
const openDebugLogin = useCallback(() => {
|
|
6551
|
+
clearOnboardingTimers();
|
|
6552
|
+
setShowOnboarding(false);
|
|
6553
|
+
setPendingSession(null);
|
|
6554
|
+
setDebugOnboardingState(null);
|
|
6555
|
+
setModal("login");
|
|
6556
|
+
}, [clearOnboardingTimers]);
|
|
6557
|
+
const openDebugSignup = useCallback(() => {
|
|
6558
|
+
clearOnboardingTimers();
|
|
6559
|
+
setShowOnboarding(false);
|
|
6560
|
+
setPendingSession(null);
|
|
6561
|
+
setDebugOnboardingState(null);
|
|
6562
|
+
setModal("signup");
|
|
6563
|
+
}, [clearOnboardingTimers]);
|
|
6564
|
+
const openDebugOnboarding = useCallback((preset = "current") => {
|
|
6565
|
+
if (!session) return;
|
|
6566
|
+
clearOnboardingTimers();
|
|
6567
|
+
setModal("none");
|
|
6568
|
+
const simulatedUser = makeDebugOnboardingUser(session.user, preset);
|
|
6569
|
+
setDebugOnboardingState({
|
|
6570
|
+
token: session.token,
|
|
6571
|
+
user: simulatedUser,
|
|
6572
|
+
sourceUser: session.user,
|
|
6573
|
+
preset,
|
|
6574
|
+
variant: "edit"
|
|
6575
|
+
});
|
|
6576
|
+
setPendingSession(null);
|
|
6577
|
+
setShowOnboarding(true);
|
|
6578
|
+
}, [session, clearOnboardingTimers, makeDebugOnboardingUser]);
|
|
6579
|
+
const resetDebugProfilePrompt = useCallback(() => {
|
|
6580
|
+
setProfilePromptState(false, Date.now() + PROFILE_PROMPT_SNOOZE_MS);
|
|
6581
|
+
if (session) {
|
|
6582
|
+
clearOnboardingTimers();
|
|
6583
|
+
setDebugOnboardingState(null);
|
|
6584
|
+
setPendingSession({ ...session, sourceUser: session.user });
|
|
6585
|
+
setShowOnboarding(true);
|
|
6586
|
+
}
|
|
6587
|
+
}, [session, clearOnboardingTimers]);
|
|
6588
|
+
useEffect(() => {
|
|
6589
|
+
syncProfilePrompt(session);
|
|
6590
|
+
return () => {
|
|
6591
|
+
clearOnboardingTimers();
|
|
6592
|
+
};
|
|
6593
|
+
}, [session, syncProfilePrompt, clearOnboardingTimers]);
|
|
6594
|
+
useEffect(() => {
|
|
6595
|
+
if (!(session == null ? void 0 : session.token)) return;
|
|
6596
|
+
if (!showOnboarding && !debugOnboardingState) return;
|
|
6597
|
+
void refreshSessionProfile();
|
|
6598
|
+
}, [session == null ? void 0 : session.token, showOnboarding, debugOnboardingState, refreshSessionProfile]);
|
|
6599
|
+
useEffect(() => {
|
|
6600
|
+
if (!(session == null ? void 0 : session.token)) return;
|
|
6601
|
+
const syncUserInfos = async () => {
|
|
6602
|
+
try {
|
|
6603
|
+
const response = await nativeAuthService.checkToken(session.token);
|
|
6604
|
+
if (!response.valid || !response.user) {
|
|
6605
|
+
await refreshSessionProfile(true);
|
|
6606
|
+
return;
|
|
6607
|
+
}
|
|
6608
|
+
const storedRaw = typeof localStorage !== "undefined" ? localStorage.getItem(STORAGE.USER) : null;
|
|
6609
|
+
let storedUser = session.user;
|
|
6610
|
+
if (storedRaw) {
|
|
6611
|
+
try {
|
|
6612
|
+
storedUser = JSON.parse(storedRaw);
|
|
6613
|
+
} catch {
|
|
6614
|
+
}
|
|
6615
|
+
}
|
|
6616
|
+
const mergedUser = { ...storedUser, ...response.user };
|
|
6617
|
+
persistSessionUser(session.token, mergedUser);
|
|
6618
|
+
if (isProfileComplete(mergedUser)) {
|
|
6619
|
+
markProfilePromptComplete();
|
|
6620
|
+
setShowOnboarding(false);
|
|
6621
|
+
setPendingSession(null);
|
|
6622
|
+
}
|
|
6623
|
+
} catch {
|
|
6624
|
+
await refreshSessionProfile(true);
|
|
6625
|
+
}
|
|
6626
|
+
};
|
|
6627
|
+
if (profileSyncTimerRef.current) {
|
|
6628
|
+
clearInterval(profileSyncTimerRef.current);
|
|
6629
|
+
profileSyncTimerRef.current = null;
|
|
6630
|
+
}
|
|
6631
|
+
profileSyncTimerRef.current = setInterval(syncUserInfos, PROFILE_SYNC_INTERVAL_MS);
|
|
6632
|
+
return () => {
|
|
6633
|
+
if (profileSyncTimerRef.current) {
|
|
6634
|
+
clearInterval(profileSyncTimerRef.current);
|
|
6635
|
+
profileSyncTimerRef.current = null;
|
|
6636
|
+
}
|
|
6637
|
+
};
|
|
6638
|
+
}, [session == null ? void 0 : session.token, persistSessionUser, refreshSessionProfile, markProfilePromptComplete]);
|
|
4805
6639
|
const containerStyle = {
|
|
4806
6640
|
minHeight: "100vh",
|
|
4807
6641
|
backgroundColor: COLORS.primary,
|
|
@@ -4868,7 +6702,33 @@ function NativeSSOPage({
|
|
|
4868
6702
|
/* @__PURE__ */ jsx("p", { style: { fontSize: "0.875rem", color: COLORS.muted, textAlign: "center", marginTop: "0.25rem" }, children: "Vous êtes connecté à votre compte Ollaid SSO" }),
|
|
4869
6703
|
/* @__PURE__ */ jsx("div", { style: { marginTop: "1.5rem" }, children: /* @__PURE__ */ jsx(Button, { variant: "outline", onClick: handleLogout, style: { width: "100%" }, children: "Déconnexion" }) })
|
|
4870
6704
|
] }) }),
|
|
4871
|
-
/* @__PURE__ */ jsx(Footer, { hideFooter })
|
|
6705
|
+
/* @__PURE__ */ jsx(Footer, { hideFooter }),
|
|
6706
|
+
(pendingSession || debugOnboardingState) && /* @__PURE__ */ jsx(
|
|
6707
|
+
OnboardingModal,
|
|
6708
|
+
{
|
|
6709
|
+
open: showOnboarding,
|
|
6710
|
+
onOpenChange: (open) => {
|
|
6711
|
+
if (!open) handleOnboardingSkip();
|
|
6712
|
+
},
|
|
6713
|
+
onDismiss: handleOnboardingDismiss,
|
|
6714
|
+
user: (debugOnboardingState || pendingSession).user,
|
|
6715
|
+
variant: debugOnboardingState ? "edit" : "missing",
|
|
6716
|
+
profileHydrating,
|
|
6717
|
+
onComplete: handleOnboardingComplete,
|
|
6718
|
+
onSkip: handleOnboardingSkip
|
|
6719
|
+
}
|
|
6720
|
+
),
|
|
6721
|
+
resolvedDebug && /* @__PURE__ */ jsx(
|
|
6722
|
+
DebugPanel,
|
|
6723
|
+
{
|
|
6724
|
+
saasApiUrl,
|
|
6725
|
+
iamApiUrl,
|
|
6726
|
+
onOpenLogin: openDebugLogin,
|
|
6727
|
+
onOpenSignup: openDebugSignup,
|
|
6728
|
+
onOpenOnboarding: openDebugOnboarding,
|
|
6729
|
+
onResetProfilePrompt: resetDebugProfilePrompt
|
|
6730
|
+
}
|
|
6731
|
+
)
|
|
4872
6732
|
] });
|
|
4873
6733
|
}
|
|
4874
6734
|
return /* @__PURE__ */ jsxs("div", { style: containerStyle, children: [
|
|
@@ -4920,19 +6780,32 @@ function NativeSSOPage({
|
|
|
4920
6780
|
defaultAccountType: accountType
|
|
4921
6781
|
}
|
|
4922
6782
|
),
|
|
4923
|
-
pendingSession && /* @__PURE__ */ jsx(
|
|
6783
|
+
(pendingSession || debugOnboardingState) && /* @__PURE__ */ jsx(
|
|
4924
6784
|
OnboardingModal,
|
|
4925
6785
|
{
|
|
4926
6786
|
open: showOnboarding,
|
|
4927
6787
|
onOpenChange: (open) => {
|
|
4928
6788
|
if (!open) handleOnboardingSkip();
|
|
4929
6789
|
},
|
|
4930
|
-
|
|
6790
|
+
onDismiss: handleOnboardingDismiss,
|
|
6791
|
+
user: (debugOnboardingState || pendingSession).user,
|
|
6792
|
+
variant: debugOnboardingState ? "edit" : "missing",
|
|
6793
|
+
profileHydrating,
|
|
4931
6794
|
onComplete: handleOnboardingComplete,
|
|
4932
6795
|
onSkip: handleOnboardingSkip
|
|
4933
6796
|
}
|
|
4934
6797
|
),
|
|
4935
|
-
resolvedDebug && /* @__PURE__ */ jsx(
|
|
6798
|
+
resolvedDebug && /* @__PURE__ */ jsx(
|
|
6799
|
+
DebugPanel,
|
|
6800
|
+
{
|
|
6801
|
+
saasApiUrl,
|
|
6802
|
+
iamApiUrl,
|
|
6803
|
+
onOpenLogin: openDebugLogin,
|
|
6804
|
+
onOpenSignup: openDebugSignup,
|
|
6805
|
+
onOpenOnboarding: openDebugOnboarding,
|
|
6806
|
+
onResetProfilePrompt: resetDebugProfilePrompt
|
|
6807
|
+
}
|
|
6808
|
+
)
|
|
4936
6809
|
] });
|
|
4937
6810
|
}
|
|
4938
6811
|
const NativeSSOContext = createContext(null);
|
|
@@ -5131,8 +7004,10 @@ export {
|
|
|
5131
7004
|
NativeSSOProvider,
|
|
5132
7005
|
OTPInput,
|
|
5133
7006
|
OnboardingModal,
|
|
7007
|
+
PROFILE_STORAGE as PROFILE_PROMPT_KEYS,
|
|
5134
7008
|
PasswordRecoveryModal,
|
|
5135
7009
|
PhoneInput,
|
|
7010
|
+
STORAGE as STORAGE_KEYS,
|
|
5136
7011
|
SignupModal,
|
|
5137
7012
|
clearAuthToken,
|
|
5138
7013
|
getAccountType,
|
|
@@ -5141,13 +7016,21 @@ export {
|
|
|
5141
7016
|
getCountryByCode,
|
|
5142
7017
|
getCountryByDialCode,
|
|
5143
7018
|
getDefaultCountry,
|
|
7019
|
+
getDeviceId,
|
|
5144
7020
|
getNativeAuthConfig,
|
|
7021
|
+
getProfilePromptState,
|
|
7022
|
+
getSessionUuid,
|
|
5145
7023
|
iamAccountService,
|
|
5146
7024
|
logout,
|
|
7025
|
+
markProfilePromptComplete,
|
|
5147
7026
|
mobilePasswordService,
|
|
5148
7027
|
nativeAuthService,
|
|
7028
|
+
profileChangeService,
|
|
7029
|
+
profileMediaService,
|
|
5149
7030
|
searchCountries,
|
|
5150
7031
|
setNativeAuthConfig,
|
|
7032
|
+
setProfilePromptState,
|
|
7033
|
+
snoozeProfilePrompt,
|
|
5151
7034
|
useLogout,
|
|
5152
7035
|
useMobilePassword,
|
|
5153
7036
|
useMobileRegistration,
|