@ollaid/native-sso 2.1.3 → 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 +202 -55
- 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 +2075 -202
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +7 -5
- package/dist/index.js +2076 -203
- 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);
|
|
@@ -2527,17 +2749,11 @@ function LoginModal({
|
|
|
2527
2749
|
setPhone(initialPhone);
|
|
2528
2750
|
}
|
|
2529
2751
|
}, [open, initialPhone]);
|
|
2530
|
-
const
|
|
2531
|
-
|
|
2532
|
-
const
|
|
2533
|
-
|
|
2534
|
-
|
|
2535
|
-
setLoginSuccess(true);
|
|
2536
|
-
} else {
|
|
2537
|
-
setLocalError("Erreur lors de la finalisation de la connexion");
|
|
2538
|
-
}
|
|
2539
|
-
} catch {
|
|
2540
|
-
setLocalError("Erreur de connexion au serveur");
|
|
2752
|
+
const finalizeLogin = (result) => {
|
|
2753
|
+
if (result.success && result.user) {
|
|
2754
|
+
const token = localStorage.getItem("native_auth_token") || localStorage.getItem("auth_token") || "";
|
|
2755
|
+
setLoginData({ token, user: result.user });
|
|
2756
|
+
setLoginSuccess(true);
|
|
2541
2757
|
}
|
|
2542
2758
|
};
|
|
2543
2759
|
const handleEmailCheck = async () => {
|
|
@@ -2567,7 +2783,7 @@ function LoginModal({
|
|
|
2567
2783
|
return;
|
|
2568
2784
|
}
|
|
2569
2785
|
const result = await submitPassword(password);
|
|
2570
|
-
|
|
2786
|
+
finalizeLogin(result);
|
|
2571
2787
|
};
|
|
2572
2788
|
const handleEmailOtpVerify = async () => {
|
|
2573
2789
|
setLocalError(null);
|
|
@@ -2577,7 +2793,7 @@ function LoginModal({
|
|
|
2577
2793
|
return;
|
|
2578
2794
|
}
|
|
2579
2795
|
const result = await submitOtp(emailOtpCode);
|
|
2580
|
-
|
|
2796
|
+
finalizeLogin(result);
|
|
2581
2797
|
};
|
|
2582
2798
|
const handlePhoneInit = async () => {
|
|
2583
2799
|
setLocalError(null);
|
|
@@ -2597,7 +2813,7 @@ function LoginModal({
|
|
|
2597
2813
|
return;
|
|
2598
2814
|
}
|
|
2599
2815
|
const result = await submitOtp(phoneOtpCode);
|
|
2600
|
-
|
|
2816
|
+
finalizeLogin(result);
|
|
2601
2817
|
};
|
|
2602
2818
|
const handleAccessOtpSubmit = async () => {
|
|
2603
2819
|
setLocalError(null);
|
|
@@ -2607,17 +2823,13 @@ function LoginModal({
|
|
|
2607
2823
|
return;
|
|
2608
2824
|
}
|
|
2609
2825
|
const result = await loginWithAccessOtp(accessOtpCode);
|
|
2610
|
-
|
|
2611
|
-
if (result.callback_token) {
|
|
2612
|
-
await exchangeCallbackToken(result.callback_token);
|
|
2613
|
-
}
|
|
2614
|
-
}
|
|
2826
|
+
finalizeLogin(result);
|
|
2615
2827
|
};
|
|
2616
2828
|
const handleGrantAccess = async () => {
|
|
2617
2829
|
setLocalError(null);
|
|
2618
2830
|
clearError();
|
|
2619
2831
|
const result = await grantAccess();
|
|
2620
|
-
|
|
2832
|
+
finalizeLogin(result);
|
|
2621
2833
|
};
|
|
2622
2834
|
const handleResendOTP = async () => {
|
|
2623
2835
|
if (resendCooldown > 0) return;
|
|
@@ -3188,11 +3400,13 @@ function PhoneInput({
|
|
|
3188
3400
|
ccphone = DEFAULT_DIAL_CODE,
|
|
3189
3401
|
onCcphoneChange,
|
|
3190
3402
|
disabled = false,
|
|
3403
|
+
readOnly = false,
|
|
3191
3404
|
error,
|
|
3192
3405
|
placeholder = "77 123 45 67",
|
|
3193
3406
|
lockCcphone = false
|
|
3194
3407
|
}) {
|
|
3195
|
-
const
|
|
3408
|
+
const normalizedCcphone = ccphone || DEFAULT_DIAL_CODE;
|
|
3409
|
+
const selectedCountry = getCountryByDialCode(normalizedCcphone) || COUNTRIES_SORTED_BY_CODE[0];
|
|
3196
3410
|
const [dropdownOpen, setDropdownOpen] = useState(false);
|
|
3197
3411
|
const dropdownRef = useRef(null);
|
|
3198
3412
|
useEffect(() => {
|
|
@@ -3204,16 +3418,18 @@ function PhoneInput({
|
|
|
3204
3418
|
return () => document.removeEventListener("mousedown", handler);
|
|
3205
3419
|
}, [dropdownOpen]);
|
|
3206
3420
|
const handleChange = (e) => {
|
|
3421
|
+
if (disabled || readOnly) return;
|
|
3207
3422
|
const cleaned = e.target.value.replace(/\D/g, "");
|
|
3208
3423
|
onChange(cleaned.slice(0, selectedCountry.digits));
|
|
3209
3424
|
};
|
|
3210
|
-
const
|
|
3211
|
-
if (
|
|
3212
|
-
|
|
3213
|
-
|
|
3214
|
-
|
|
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));
|
|
3215
3430
|
};
|
|
3216
3431
|
const handleSelect = (dialCode) => {
|
|
3432
|
+
if (disabled || readOnly) return;
|
|
3217
3433
|
if (onCcphoneChange) onCcphoneChange(dialCode);
|
|
3218
3434
|
setDropdownOpen(false);
|
|
3219
3435
|
};
|
|
@@ -3224,8 +3440,8 @@ function PhoneInput({
|
|
|
3224
3440
|
border: `2px solid ${error ? "#ef4444" : "#d1d5db"}`,
|
|
3225
3441
|
borderRadius: "0.5rem",
|
|
3226
3442
|
overflow: "visible",
|
|
3227
|
-
opacity: disabled ? 0.
|
|
3228
|
-
backgroundColor: disabled ? "#
|
|
3443
|
+
opacity: disabled || readOnly ? 0.75 : 1,
|
|
3444
|
+
backgroundColor: disabled || readOnly ? "#f9fafb" : "white",
|
|
3229
3445
|
position: "relative"
|
|
3230
3446
|
}, children: [
|
|
3231
3447
|
onCcphoneChange && !lockCcphone ? /* @__PURE__ */ jsxs("div", { ref: dropdownRef, style: { position: "relative" }, children: [
|
|
@@ -3233,24 +3449,24 @@ function PhoneInput({
|
|
|
3233
3449
|
"button",
|
|
3234
3450
|
{
|
|
3235
3451
|
type: "button",
|
|
3236
|
-
onClick: () => !disabled && setDropdownOpen(!dropdownOpen),
|
|
3237
|
-
disabled,
|
|
3452
|
+
onClick: () => !(disabled || readOnly) && setDropdownOpen(!dropdownOpen),
|
|
3453
|
+
disabled: disabled || readOnly,
|
|
3238
3454
|
style: {
|
|
3239
3455
|
display: "flex",
|
|
3240
3456
|
alignItems: "center",
|
|
3241
|
-
gap: "0.
|
|
3242
|
-
padding: "0.
|
|
3457
|
+
gap: "0.35rem",
|
|
3458
|
+
padding: "0.45rem 0.6rem",
|
|
3243
3459
|
backgroundColor: "#f9fafb",
|
|
3244
3460
|
borderRight: "1px solid #e5e7eb",
|
|
3245
3461
|
border: "none",
|
|
3246
|
-
cursor: disabled ? "not-allowed" : "pointer",
|
|
3462
|
+
cursor: disabled || readOnly ? "not-allowed" : "pointer",
|
|
3247
3463
|
fontWeight: 500,
|
|
3248
3464
|
color: "#374151",
|
|
3249
|
-
fontSize: "0.
|
|
3465
|
+
fontSize: "0.85rem",
|
|
3250
3466
|
whiteSpace: "nowrap"
|
|
3251
3467
|
},
|
|
3252
3468
|
children: [
|
|
3253
|
-
/* @__PURE__ */ jsx("span", { style: { fontSize: "
|
|
3469
|
+
/* @__PURE__ */ jsx("span", { style: { fontSize: "1rem" }, children: selectedCountry.flag }),
|
|
3254
3470
|
/* @__PURE__ */ jsx("span", { children: selectedCountry.dialCode }),
|
|
3255
3471
|
/* @__PURE__ */ jsx("span", { style: { fontSize: "0.625rem", marginLeft: "0.125rem" }, children: "▼" })
|
|
3256
3472
|
]
|
|
@@ -3279,15 +3495,15 @@ function PhoneInput({
|
|
|
3279
3495
|
alignItems: "center",
|
|
3280
3496
|
gap: "0.5rem",
|
|
3281
3497
|
width: "100%",
|
|
3282
|
-
padding: "0.
|
|
3498
|
+
padding: "0.45rem 0.75rem",
|
|
3283
3499
|
border: "none",
|
|
3284
3500
|
cursor: "pointer",
|
|
3285
|
-
fontSize: "0.
|
|
3286
|
-
backgroundColor: c.dialCode ===
|
|
3501
|
+
fontSize: "0.85rem",
|
|
3502
|
+
backgroundColor: c.dialCode === normalizedCcphone ? "#f3f4f6" : "transparent",
|
|
3287
3503
|
textAlign: "left"
|
|
3288
3504
|
},
|
|
3289
3505
|
onMouseEnter: (e) => e.currentTarget.style.backgroundColor = "#f3f4f6",
|
|
3290
|
-
onMouseLeave: (e) => e.currentTarget.style.backgroundColor = c.dialCode ===
|
|
3506
|
+
onMouseLeave: (e) => e.currentTarget.style.backgroundColor = c.dialCode === normalizedCcphone ? "#f3f4f6" : "transparent",
|
|
3291
3507
|
children: [
|
|
3292
3508
|
/* @__PURE__ */ jsx("span", { style: { fontWeight: 600, color: "#374151", minWidth: "1.75rem" }, children: c.code }),
|
|
3293
3509
|
/* @__PURE__ */ jsx("span", { children: c.flag }),
|
|
@@ -3298,8 +3514,8 @@ function PhoneInput({
|
|
|
3298
3514
|
)) })
|
|
3299
3515
|
] }) : (
|
|
3300
3516
|
/* Locked view: only flag + dialCode */
|
|
3301
|
-
/* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: "0.
|
|
3302
|
-
/* @__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 }),
|
|
3303
3519
|
/* @__PURE__ */ jsx("span", { style: { fontWeight: 500, color: "#374151" }, children: selectedCountry.dialCode })
|
|
3304
3520
|
] })
|
|
3305
3521
|
),
|
|
@@ -3308,11 +3524,13 @@ function PhoneInput({
|
|
|
3308
3524
|
{
|
|
3309
3525
|
type: "tel",
|
|
3310
3526
|
inputMode: "numeric",
|
|
3311
|
-
value
|
|
3527
|
+
value,
|
|
3312
3528
|
onChange: handleChange,
|
|
3529
|
+
onPaste: handlePaste,
|
|
3313
3530
|
disabled,
|
|
3531
|
+
readOnly,
|
|
3314
3532
|
placeholder,
|
|
3315
|
-
style: { flex: 1, padding: "0.
|
|
3533
|
+
style: { flex: 1, padding: "0.45rem 0.6rem", fontSize: "0.95rem", border: "none", outline: "none", backgroundColor: "transparent" }
|
|
3316
3534
|
}
|
|
3317
3535
|
)
|
|
3318
3536
|
] }),
|
|
@@ -4283,6 +4501,414 @@ function SignupModal({ open, onOpenChange, onSwitchToLogin, onSignupSuccess, saa
|
|
|
4283
4501
|
)
|
|
4284
4502
|
] });
|
|
4285
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
|
+
};
|
|
4286
4912
|
const C = {
|
|
4287
4913
|
primary: "#002147",
|
|
4288
4914
|
accent: "#e8430a",
|
|
@@ -4292,21 +4918,87 @@ const C = {
|
|
|
4292
4918
|
gray700: "#374151",
|
|
4293
4919
|
white: "#ffffff"
|
|
4294
4920
|
};
|
|
4295
|
-
function OnboardingModal({ open, onOpenChange, user, onComplete, onSkip }) {
|
|
4296
|
-
|
|
4297
|
-
const
|
|
4298
|
-
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 || "");
|
|
4299
4930
|
const [photoPreview, setPhotoPreview] = useState("");
|
|
4300
|
-
const [
|
|
4931
|
+
const [avatarPickerOpen, setAvatarPickerOpen] = useState(false);
|
|
4932
|
+
const [pendingAvatarSrc, setPendingAvatarSrc] = useState(null);
|
|
4933
|
+
const [avatarUploading, setAvatarUploading] = useState(false);
|
|
4934
|
+
const avatarFileInputRef = useRef(null);
|
|
4301
4935
|
const [ccphone, setCcphone] = useState(user.ccphone || "+221");
|
|
4302
4936
|
const [phone, setPhone] = useState(user.phone || "");
|
|
4303
|
-
const [email, setEmail] = useState("");
|
|
4304
|
-
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);
|
|
4305
4941
|
const [submitting, setSubmitting] = useState(false);
|
|
4306
4942
|
const [fileError, setFileError] = useState("");
|
|
4307
|
-
const
|
|
4308
|
-
|
|
4309
|
-
|
|
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];
|
|
4310
5002
|
if (!file) return;
|
|
4311
5003
|
if (file.size > 2 * 1024 * 1024) {
|
|
4312
5004
|
setFileError("Le fichier dépasse 2 Mo. Veuillez choisir une image plus légère.");
|
|
@@ -4314,152 +5006,970 @@ function OnboardingModal({ open, onOpenChange, user, onComplete, onSkip }) {
|
|
|
4314
5006
|
return;
|
|
4315
5007
|
}
|
|
4316
5008
|
setFileError("");
|
|
4317
|
-
setPhotoFile(file);
|
|
4318
5009
|
const reader = new FileReader();
|
|
4319
5010
|
reader.onload = () => setPhotoPreview(reader.result);
|
|
4320
5011
|
reader.readAsDataURL(file);
|
|
4321
5012
|
}, []);
|
|
4322
|
-
const canSubmit =
|
|
4323
|
-
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 () => {
|
|
4324
5015
|
if (!canSubmit) return;
|
|
4325
5016
|
setSubmitting(true);
|
|
4326
|
-
|
|
4327
|
-
|
|
4328
|
-
|
|
4329
|
-
|
|
4330
|
-
|
|
4331
|
-
|
|
4332
|
-
|
|
4333
|
-
|
|
4334
|
-
|
|
4335
|
-
|
|
4336
|
-
}
|
|
4337
|
-
|
|
4338
|
-
|
|
4339
|
-
|
|
4340
|
-
|
|
4341
|
-
|
|
4342
|
-
|
|
4343
|
-
|
|
4344
|
-
|
|
4345
|
-
|
|
4346
|
-
|
|
4347
|
-
|
|
4348
|
-
|
|
4349
|
-
|
|
4350
|
-
|
|
4351
|
-
|
|
4352
|
-
|
|
4353
|
-
|
|
4354
|
-
|
|
4355
|
-
|
|
4356
|
-
|
|
4357
|
-
|
|
4358
|
-
|
|
4359
|
-
|
|
4360
|
-
|
|
4361
|
-
|
|
4362
|
-
|
|
4363
|
-
|
|
4364
|
-
|
|
4365
|
-
|
|
4366
|
-
|
|
4367
|
-
|
|
4368
|
-
|
|
4369
|
-
|
|
4370
|
-
|
|
4371
|
-
|
|
4372
|
-
|
|
4373
|
-
|
|
4374
|
-
|
|
4375
|
-
|
|
4376
|
-
|
|
4377
|
-
|
|
4378
|
-
|
|
4379
|
-
|
|
4380
|
-
|
|
4381
|
-
|
|
4382
|
-
|
|
4383
|
-
|
|
4384
|
-
|
|
4385
|
-
|
|
4386
|
-
|
|
4387
|
-
|
|
4388
|
-
|
|
4389
|
-
|
|
4390
|
-
|
|
4391
|
-
|
|
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;
|
|
5188
|
+
}
|
|
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;
|
|
5268
|
+
}
|
|
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" });
|
|
5277
|
+
}
|
|
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" })
|
|
5318
|
+
] });
|
|
5319
|
+
const renderSuccess = () => /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
5320
|
+
/* @__PURE__ */ jsxs(DialogHeader, { children: [
|
|
5321
|
+
/* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: "0.5rem", marginBottom: "0.25rem" }, children: [
|
|
5322
|
+
/* @__PURE__ */ jsx(ShieldIcon, {}),
|
|
5323
|
+
/* @__PURE__ */ jsx(DialogTitle, { children: "Profil enregistré" })
|
|
5324
|
+
] }),
|
|
5325
|
+
/* @__PURE__ */ jsx(DialogDescription, { children: submitMessage || "Vos informations ont été mises à jour." })
|
|
5326
|
+
] }),
|
|
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
|
+
] })
|
|
4392
5495
|
] }),
|
|
4393
|
-
|
|
4394
|
-
/* @__PURE__ */ jsxs(
|
|
4395
|
-
"Adresse email
|
|
4396
|
-
/* @__PURE__ */ jsx("
|
|
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" })
|
|
4397
5500
|
] }),
|
|
4398
5501
|
/* @__PURE__ */ jsx(
|
|
4399
5502
|
"input",
|
|
4400
5503
|
{
|
|
4401
5504
|
type: "email",
|
|
4402
|
-
value:
|
|
4403
|
-
|
|
4404
|
-
placeholder: "email@exemple.com",
|
|
5505
|
+
value: currentEmailLabel,
|
|
5506
|
+
readOnly: true,
|
|
4405
5507
|
style: {
|
|
4406
5508
|
width: "100%",
|
|
4407
|
-
height: "2.
|
|
5509
|
+
height: "2.25rem",
|
|
4408
5510
|
padding: "0 0.75rem",
|
|
4409
5511
|
border: `1px solid ${C.gray200}`,
|
|
4410
5512
|
borderRadius: "0.375rem",
|
|
4411
5513
|
fontSize: "0.875rem",
|
|
4412
5514
|
color: C.gray700,
|
|
4413
|
-
backgroundColor:
|
|
5515
|
+
backgroundColor: "#f9fafb",
|
|
4414
5516
|
outline: "none"
|
|
4415
5517
|
}
|
|
4416
5518
|
}
|
|
4417
5519
|
)
|
|
4418
5520
|
] }),
|
|
4419
|
-
/* @__PURE__ */ jsxs("
|
|
4420
|
-
display: "
|
|
4421
|
-
|
|
4422
|
-
|
|
4423
|
-
|
|
4424
|
-
fontSize: "0.875rem",
|
|
4425
|
-
color: C.gray700
|
|
4426
|
-
}, children: [
|
|
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" }),
|
|
5523
|
+
/* @__PURE__ */ jsx(PhoneInput, { value: phone, onChange: setPhone, ccphone, onCcphoneChange: setCcphone })
|
|
5524
|
+
] }),
|
|
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" }),
|
|
4427
5527
|
/* @__PURE__ */ jsx(
|
|
4428
5528
|
"input",
|
|
4429
5529
|
{
|
|
4430
|
-
type: "
|
|
4431
|
-
|
|
4432
|
-
onChange: (e) =>
|
|
4433
|
-
|
|
5530
|
+
type: "email",
|
|
5531
|
+
value: email,
|
|
5532
|
+
onChange: (e) => setEmail(e.target.value),
|
|
5533
|
+
placeholder: "email@exemple.com",
|
|
5534
|
+
style: {
|
|
5535
|
+
width: "100%",
|
|
5536
|
+
height: "2.25rem",
|
|
5537
|
+
padding: "0 0.75rem",
|
|
5538
|
+
border: `1px solid ${C.gray200}`,
|
|
5539
|
+
borderRadius: "0.375rem",
|
|
5540
|
+
fontSize: "0.875rem",
|
|
5541
|
+
color: C.gray700,
|
|
5542
|
+
backgroundColor: C.white,
|
|
5543
|
+
outline: "none"
|
|
5544
|
+
}
|
|
4434
5545
|
}
|
|
4435
|
-
)
|
|
4436
|
-
|
|
5546
|
+
)
|
|
5547
|
+
] }),
|
|
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
|
+
] })
|
|
4437
5627
|
] })
|
|
4438
5628
|
] }) }),
|
|
4439
|
-
/* @__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: [
|
|
4440
5639
|
/* @__PURE__ */ jsx(
|
|
4441
5640
|
Button,
|
|
4442
5641
|
{
|
|
4443
|
-
|
|
4444
|
-
|
|
4445
|
-
|
|
4446
|
-
|
|
5642
|
+
type: "button",
|
|
5643
|
+
variant: "outline",
|
|
5644
|
+
onClick: () => onOpenChange(false),
|
|
5645
|
+
disabled: submitting,
|
|
5646
|
+
style: { minWidth: "6.5rem", height: "2.35rem" },
|
|
5647
|
+
children: "Fermer"
|
|
4447
5648
|
}
|
|
4448
5649
|
),
|
|
4449
5650
|
/* @__PURE__ */ jsx(
|
|
4450
5651
|
Button,
|
|
4451
5652
|
{
|
|
4452
|
-
|
|
4453
|
-
onClick: onSkip,
|
|
4454
|
-
disabled: submitting,
|
|
4455
|
-
style: {
|
|
4456
|
-
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"
|
|
4457
5661
|
}
|
|
4458
5662
|
)
|
|
4459
|
-
] })
|
|
4460
|
-
|
|
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
|
+
] });
|
|
4461
5971
|
}
|
|
4462
|
-
function DebugPanel({ saasApiUrl, iamApiUrl }) {
|
|
5972
|
+
function DebugPanel({ saasApiUrl, iamApiUrl, onOpenLogin, onOpenSignup, onOpenOnboarding, onResetProfilePrompt }) {
|
|
4463
5973
|
const [logs, setLogs] = useState([]);
|
|
4464
5974
|
const [expanded, setExpanded] = useState(true);
|
|
4465
5975
|
const [selectedLog, setSelectedLog] = useState(null);
|
|
@@ -4553,7 +6063,7 @@ function DebugPanel({ saasApiUrl, iamApiUrl }) {
|
|
|
4553
6063
|
maxHeight: "300px",
|
|
4554
6064
|
overflowY: "auto"
|
|
4555
6065
|
}, children: [
|
|
4556
|
-
/* @__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: [
|
|
4557
6067
|
/* @__PURE__ */ jsxs("span", { children: [
|
|
4558
6068
|
"SaaS: ",
|
|
4559
6069
|
/* @__PURE__ */ jsx("span", { style: { color: "#6366f1" }, children: saasApiUrl })
|
|
@@ -4561,6 +6071,56 @@ function DebugPanel({ saasApiUrl, iamApiUrl }) {
|
|
|
4561
6071
|
/* @__PURE__ */ jsxs("span", { children: [
|
|
4562
6072
|
"IAM: ",
|
|
4563
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
|
+
)
|
|
4564
6124
|
] })
|
|
4565
6125
|
] }),
|
|
4566
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: [
|
|
@@ -4616,6 +6176,11 @@ function DebugPanel({ saasApiUrl, iamApiUrl }) {
|
|
|
4616
6176
|
] });
|
|
4617
6177
|
}
|
|
4618
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;
|
|
4619
6184
|
const COLORS = {
|
|
4620
6185
|
primary: "#002147",
|
|
4621
6186
|
primaryForeground: "#ffffff",
|
|
@@ -4652,7 +6217,10 @@ const ShieldCheckIcon = ({ size = 24, color = COLORS.accent }) => /* @__PURE__ *
|
|
|
4652
6217
|
/* @__PURE__ */ jsx("path", { d: "m9 12 2 2 4-4" })
|
|
4653
6218
|
] });
|
|
4654
6219
|
function needsOnboarding(user) {
|
|
4655
|
-
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);
|
|
4656
6224
|
}
|
|
4657
6225
|
const GRADIENT_STYLE_ID = "ollaid-sso-gradient-anim";
|
|
4658
6226
|
function injectGradientAnimation() {
|
|
@@ -4709,6 +6277,7 @@ function NativeSSOPage({
|
|
|
4709
6277
|
const [loginInitialPhone, setLoginInitialPhone] = useState();
|
|
4710
6278
|
const [showOnboarding, setShowOnboarding] = useState(false);
|
|
4711
6279
|
const [pendingSession, setPendingSession] = useState(null);
|
|
6280
|
+
const [debugOnboardingState, setDebugOnboardingState] = useState(null);
|
|
4712
6281
|
const [session, setSession] = useState(() => {
|
|
4713
6282
|
try {
|
|
4714
6283
|
const token = localStorage.getItem(STORAGE.AUTH_TOKEN) || localStorage.getItem(STORAGE.TOKEN);
|
|
@@ -4719,6 +6288,135 @@ function NativeSSOPage({
|
|
|
4719
6288
|
return null;
|
|
4720
6289
|
});
|
|
4721
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]);
|
|
4722
6420
|
useEffect(() => {
|
|
4723
6421
|
injectGradientAnimation();
|
|
4724
6422
|
const root = document.documentElement;
|
|
@@ -4778,40 +6476,166 @@ function NativeSSOPage({
|
|
|
4778
6476
|
account_type: user.account_type || "user"
|
|
4779
6477
|
};
|
|
4780
6478
|
setModal("none");
|
|
4781
|
-
|
|
4782
|
-
|
|
4783
|
-
|
|
4784
|
-
|
|
4785
|
-
|
|
6479
|
+
persistSessionUser(token, userObj);
|
|
6480
|
+
syncProfilePrompt({ token, user: userObj });
|
|
6481
|
+
void refreshSessionProfile(true);
|
|
6482
|
+
if (!needsOnboarding(userObj)) {
|
|
6483
|
+
markProfilePromptComplete();
|
|
4786
6484
|
onLoginSuccess == null ? void 0 : onLoginSuccess(token, user);
|
|
4787
6485
|
if (redirectAfterLogin) window.location.href = redirectAfterLogin;
|
|
4788
6486
|
}
|
|
4789
|
-
}, [onLoginSuccess, redirectAfterLogin]);
|
|
6487
|
+
}, [onLoginSuccess, redirectAfterLogin, persistSessionUser, syncProfilePrompt, refreshSessionProfile]);
|
|
4790
6488
|
const handleOnboardingComplete = useCallback((data) => {
|
|
4791
|
-
|
|
4792
|
-
|
|
4793
|
-
|
|
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;
|
|
4794
6525
|
setShowOnboarding(false);
|
|
4795
|
-
setSession({ token: pendingSession.token, user: updatedUser });
|
|
4796
6526
|
setPendingSession(null);
|
|
4797
|
-
|
|
4798
|
-
|
|
4799
|
-
|
|
4800
|
-
|
|
4801
|
-
|
|
4802
|
-
|
|
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(() => {
|
|
4803
6537
|
setShowOnboarding(false);
|
|
4804
|
-
setSession(pendingSession);
|
|
4805
6538
|
setPendingSession(null);
|
|
4806
|
-
|
|
4807
|
-
|
|
4808
|
-
}, [pendingSession, onLoginSuccess, redirectAfterLogin]);
|
|
6539
|
+
setDebugOnboardingState(null);
|
|
6540
|
+
}, []);
|
|
4809
6541
|
const handleLogout = useCallback(async () => {
|
|
4810
6542
|
await logout();
|
|
4811
6543
|
setSession(null);
|
|
6544
|
+
setPendingSession(null);
|
|
6545
|
+
setDebugOnboardingState(null);
|
|
6546
|
+
clearOnboardingTimers();
|
|
4812
6547
|
onLogout == null ? void 0 : onLogout();
|
|
4813
6548
|
if (redirectAfterLogout) window.location.href = redirectAfterLogout;
|
|
4814
|
-
}, [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]);
|
|
4815
6639
|
const containerStyle = {
|
|
4816
6640
|
minHeight: "100vh",
|
|
4817
6641
|
backgroundColor: COLORS.primary,
|
|
@@ -4878,7 +6702,33 @@ function NativeSSOPage({
|
|
|
4878
6702
|
/* @__PURE__ */ jsx("p", { style: { fontSize: "0.875rem", color: COLORS.muted, textAlign: "center", marginTop: "0.25rem" }, children: "Vous êtes connecté à votre compte Ollaid SSO" }),
|
|
4879
6703
|
/* @__PURE__ */ jsx("div", { style: { marginTop: "1.5rem" }, children: /* @__PURE__ */ jsx(Button, { variant: "outline", onClick: handleLogout, style: { width: "100%" }, children: "Déconnexion" }) })
|
|
4880
6704
|
] }) }),
|
|
4881
|
-
/* @__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
|
+
)
|
|
4882
6732
|
] });
|
|
4883
6733
|
}
|
|
4884
6734
|
return /* @__PURE__ */ jsxs("div", { style: containerStyle, children: [
|
|
@@ -4930,19 +6780,32 @@ function NativeSSOPage({
|
|
|
4930
6780
|
defaultAccountType: accountType
|
|
4931
6781
|
}
|
|
4932
6782
|
),
|
|
4933
|
-
pendingSession && /* @__PURE__ */ jsx(
|
|
6783
|
+
(pendingSession || debugOnboardingState) && /* @__PURE__ */ jsx(
|
|
4934
6784
|
OnboardingModal,
|
|
4935
6785
|
{
|
|
4936
6786
|
open: showOnboarding,
|
|
4937
6787
|
onOpenChange: (open) => {
|
|
4938
6788
|
if (!open) handleOnboardingSkip();
|
|
4939
6789
|
},
|
|
4940
|
-
|
|
6790
|
+
onDismiss: handleOnboardingDismiss,
|
|
6791
|
+
user: (debugOnboardingState || pendingSession).user,
|
|
6792
|
+
variant: debugOnboardingState ? "edit" : "missing",
|
|
6793
|
+
profileHydrating,
|
|
4941
6794
|
onComplete: handleOnboardingComplete,
|
|
4942
6795
|
onSkip: handleOnboardingSkip
|
|
4943
6796
|
}
|
|
4944
6797
|
),
|
|
4945
|
-
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
|
+
)
|
|
4946
6809
|
] });
|
|
4947
6810
|
}
|
|
4948
6811
|
const NativeSSOContext = createContext(null);
|
|
@@ -5141,8 +7004,10 @@ export {
|
|
|
5141
7004
|
NativeSSOProvider,
|
|
5142
7005
|
OTPInput,
|
|
5143
7006
|
OnboardingModal,
|
|
7007
|
+
PROFILE_STORAGE as PROFILE_PROMPT_KEYS,
|
|
5144
7008
|
PasswordRecoveryModal,
|
|
5145
7009
|
PhoneInput,
|
|
7010
|
+
STORAGE as STORAGE_KEYS,
|
|
5146
7011
|
SignupModal,
|
|
5147
7012
|
clearAuthToken,
|
|
5148
7013
|
getAccountType,
|
|
@@ -5151,13 +7016,21 @@ export {
|
|
|
5151
7016
|
getCountryByCode,
|
|
5152
7017
|
getCountryByDialCode,
|
|
5153
7018
|
getDefaultCountry,
|
|
7019
|
+
getDeviceId,
|
|
5154
7020
|
getNativeAuthConfig,
|
|
7021
|
+
getProfilePromptState,
|
|
7022
|
+
getSessionUuid,
|
|
5155
7023
|
iamAccountService,
|
|
5156
7024
|
logout,
|
|
7025
|
+
markProfilePromptComplete,
|
|
5157
7026
|
mobilePasswordService,
|
|
5158
7027
|
nativeAuthService,
|
|
7028
|
+
profileChangeService,
|
|
7029
|
+
profileMediaService,
|
|
5159
7030
|
searchCountries,
|
|
5160
7031
|
setNativeAuthConfig,
|
|
7032
|
+
setProfilePromptState,
|
|
7033
|
+
snoozeProfilePrompt,
|
|
5161
7034
|
useLogout,
|
|
5162
7035
|
useMobilePassword,
|
|
5163
7036
|
useMobileRegistration,
|