| 1 |
let currentForm = ""; |
| 2 |
|
| 3 |
function showMessage(message, type = "info") { |
| 4 |
let container = document.getElementById("message-container"); |
| 5 |
|
| 6 |
// Si le container n'existe pas, le créer |
| 7 |
if (!container) { |
| 8 |
container = document.createElement("div"); |
| 9 |
container.id = "message-container"; |
| 10 |
const authContainer = document.querySelector(".ai-auth-container"); |
| 11 |
if (authContainer) { |
| 12 |
authContainer.appendChild(container); |
| 13 |
} |
| 14 |
} |
| 15 |
|
| 16 |
container.innerHTML = `<div class="ai-message ${type}">${message}</div>`; |
| 17 |
container.scrollIntoView({ behavior: "smooth" }); |
| 18 |
} |
| 19 |
|
| 20 |
function setLoading(buttonId, isLoading) { |
| 21 |
const button = document.getElementById(buttonId); |
| 22 |
|
| 23 |
if (!button) { |
| 24 |
console.error(`Button with id '${buttonId}' not found`); |
| 25 |
return; |
| 26 |
} |
| 27 |
|
| 28 |
let loading = button.querySelector(".ai-loading"); |
| 29 |
|
| 30 |
// Si l'élément loading n'existe pas, le créer |
| 31 |
if (!loading) { |
| 32 |
loading = document.createElement("span"); |
| 33 |
loading.className = "ai-loading"; |
| 34 |
loading.style.display = "none"; |
| 35 |
button.appendChild(loading); |
| 36 |
} |
| 37 |
|
| 38 |
// Stocker le texte original dans un attribut data si pas déjà fait |
| 39 |
if (!button.dataset.originalText) { |
| 40 |
button.dataset.originalText = button.textContent.trim(); |
| 41 |
} |
| 42 |
|
| 43 |
if (isLoading) { |
| 44 |
loading.style.display = "inline-block"; |
| 45 |
button.disabled = true; |
| 46 |
button.textContent = ""; |
| 47 |
button.appendChild(loading); |
| 48 |
button.appendChild(document.createTextNode(button.dataset.originalText)); |
| 49 |
} else { |
| 50 |
loading.style.display = "none"; |
| 51 |
button.disabled = false; |
| 52 |
button.textContent = button.dataset.originalText; |
| 53 |
} |
| 54 |
} |
| 55 |
|
| 56 |
function toggleForm(formType) { |
| 57 |
const container = document.querySelector(".ai-auth-container"); |
| 58 |
const hiddenSignup = document.getElementById("hidden-signup-form"); |
| 59 |
const hiddenSignin = document.getElementById("hidden-signin-form"); |
| 60 |
|
| 61 |
if (formType === "signup") { |
| 62 |
container.innerHTML = hiddenSignup.innerHTML; |
| 63 |
currentForm = "signup"; |
| 64 |
setupFormListeners(); |
| 65 |
} else { |
| 66 |
container.innerHTML = hiddenSignin.innerHTML; |
| 67 |
currentForm = "signin"; |
| 68 |
setupFormListeners(); |
| 69 |
} |
| 70 |
|
| 71 |
// Ajouter l'animation |
| 72 |
container.classList.remove("ai-fade-in"); |
| 73 |
void container.offsetWidth; // Trigger reflow |
| 74 |
container.classList.add("ai-fade-in"); |
| 75 |
} |
| 76 |
|
| 77 |
function setupFormListeners() { |
| 78 |
const signupForm = |
| 79 |
document.getElementById("signup-form") || |
| 80 |
document.getElementById("hidden-signup-form-element"); |
| 81 |
const signinForm = |
| 82 |
document.getElementById("signin-form") || |
| 83 |
document.getElementById("hidden-signin-form-element"); |
| 84 |
|
| 85 |
if (signupForm) { |
| 86 |
signupForm.addEventListener("submit", handleSignup); |
| 87 |
|
| 88 |
// Ajouter un listener pour la checkbox des conditions d'utilisation |
| 89 |
const termsCheckbox = signupForm.querySelector("#terms-checkbox"); |
| 90 |
if (termsCheckbox) { |
| 91 |
termsCheckbox.addEventListener("change", function () { |
| 92 |
const checkboxLabel = this.closest(".ai-checkbox-label"); |
| 93 |
if (checkboxLabel) { |
| 94 |
if (this.checked) { |
| 95 |
checkboxLabel.classList.remove("error"); |
| 96 |
} |
| 97 |
} |
| 98 |
}); |
| 99 |
} |
| 100 |
} |
| 101 |
|
| 102 |
if (signinForm) { |
| 103 |
signinForm.addEventListener("submit", handleSignin); |
| 104 |
} |
| 105 |
} |
| 106 |
|
| 107 |
async function handleSignup(e) { |
| 108 |
e.preventDefault(); |
| 109 |
const form = e.target; |
| 110 |
const email = form.email.value; |
| 111 |
const password = form.password.value; |
| 112 |
const confirmPassword = form.confirm_password.value; |
| 113 |
const termsAccepted = form.terms_accepted && form.terms_accepted.checked; |
| 114 |
|
| 115 |
// Récupérer le token Turnstile selon le formulaire |
| 116 |
let captcha_token = ""; |
| 117 |
// if (currentForm === 'signup') { |
| 118 |
// captcha_token = window.turnstile && turnstile.getResponse('cf-turnstile-signup'); |
| 119 |
// } else { |
| 120 |
// captcha_token = window.turnstile && turnstile.getResponse('cf-turnstile-hidden-signup'); |
| 121 |
// } |
| 122 |
// if (!captcha_token) { |
| 123 |
// showMessage('Please complete the captcha.', 'error'); |
| 124 |
// return; |
| 125 |
// } |
| 126 |
|
| 127 |
if (password !== confirmPassword) { |
| 128 |
showMessage("Passwords do not match", "error"); |
| 129 |
return; |
| 130 |
} |
| 131 |
|
| 132 |
if (password.length < 8) { |
| 133 |
showMessage("Password must be at least 8 characters long", "error"); |
| 134 |
return; |
| 135 |
} |
| 136 |
|
| 137 |
if (!termsAccepted) { |
| 138 |
showMessage( |
| 139 |
"You must accept the Terms of Service and Privacy Policy to create an account", |
| 140 |
"error" |
| 141 |
); |
| 142 |
// Ajouter une classe d'erreur visuelle à la checkbox |
| 143 |
const checkboxLabel = document.querySelector(".ai-checkbox-label"); |
| 144 |
if (checkboxLabel) { |
| 145 |
checkboxLabel.classList.add("error"); |
| 146 |
// Retirer la classe d'erreur après 3 secondes |
| 147 |
setTimeout(() => { |
| 148 |
checkboxLabel.classList.remove("error"); |
| 149 |
}, 3000); |
| 150 |
} |
| 151 |
return; |
| 152 |
} |
| 153 |
|
| 154 |
// Déterminer l'ID du bouton selon le formulaire actuel |
| 155 |
const buttonId = |
| 156 |
currentForm === "signup" ? "signup-btn" : "hidden-signup-btn"; |
| 157 |
setLoading(buttonId, true); |
| 158 |
|
| 159 |
// Récupérer le nom de domaine du site |
| 160 |
const siteDomain = (typeof aiBuilderVars !== 'undefined' && aiBuilderVars.siteDomain) |
| 161 |
? aiBuilderVars.siteDomain |
| 162 |
: window.location.hostname; |
| 163 |
|
| 164 |
try { |
| 165 |
const response = await fetch(`${window.config.apiUrl}/auth/signup`, { |
| 166 |
method: "POST", |
| 167 |
headers: { "Content-Type": "application/json" }, |
| 168 |
body: JSON.stringify({ email, password, captcha_token, domain: siteDomain }), |
| 169 |
}); |
| 170 |
|
| 171 |
console.log(response); |
| 172 |
const data = await response.json(); |
| 173 |
|
| 174 |
if (response.ok) { |
| 175 |
// Marquer l'inscription comme réussie |
| 176 |
await fetch(ajaxurl, { |
| 177 |
method: "POST", |
| 178 |
headers: { "Content-Type": "application/x-www-form-urlencoded" }, |
| 179 |
body: |
| 180 |
"action=aibui_set_signup_success&nonce=" + aiBuilderVars.nonce, |
| 181 |
}); |
| 182 |
|
| 183 |
showMessage( |
| 184 |
"Account created successfully! Please check your email to verify your account.", |
| 185 |
"success" |
| 186 |
); |
| 187 |
|
| 188 |
// Basculer vers le formulaire de connexion après 3 secondes |
| 189 |
setTimeout(() => { |
| 190 |
toggleForm("signin"); |
| 191 |
showMessage( |
| 192 |
"Before signing in, please check your email and click the verification link to activate your account.", |
| 193 |
"info" |
| 194 |
); |
| 195 |
}, 3000); |
| 196 |
} else { |
| 197 |
showMessage(data.message || "Signup failed. Please try again.", "error"); |
| 198 |
} |
| 199 |
} catch (error) { |
| 200 |
console.log(error); |
| 201 |
showMessage("Network error. Please check your connection.", "error"); |
| 202 |
} finally { |
| 203 |
setLoading(buttonId, false); |
| 204 |
// Réinitialiser le widget Turnstile |
| 205 |
// if (currentForm === 'signup') { |
| 206 |
// window.turnstile && turnstile.reset('cf-turnstile-signup'); |
| 207 |
// } else { |
| 208 |
// window.turnstile && turnstile.reset('cf-turnstile-hidden-signup'); |
| 209 |
// } |
| 210 |
} |
| 211 |
} |
| 212 |
|
| 213 |
async function handleSignin(e) { |
| 214 |
e.preventDefault(); |
| 215 |
const form = e.target; |
| 216 |
const email = form.email.value; |
| 217 |
const password = form.password.value; |
| 218 |
|
| 219 |
// Récupérer le token Turnstile selon le formulaire |
| 220 |
let captcha_token = ""; |
| 221 |
// if (currentForm === 'signin') { |
| 222 |
// captcha_token = window.turnstile && turnstile.getResponse('cf-turnstile-signin'); |
| 223 |
// } else { |
| 224 |
// captcha_token = window.turnstile && turnstile.getResponse('cf-turnstile-hidden-signin'); |
| 225 |
// } |
| 226 |
// if (!captcha_token) { |
| 227 |
// showMessage('Please complete the captcha.', 'error'); |
| 228 |
// return; |
| 229 |
// } |
| 230 |
|
| 231 |
// Déterminer l'ID du bouton selon le formulaire actuel |
| 232 |
const buttonId = |
| 233 |
currentForm === "signin" ? "signin-btn" : "hidden-signin-btn"; |
| 234 |
setLoading(buttonId, true); |
| 235 |
|
| 236 |
console.log("🔐 Attempting signin for:", email); |
| 237 |
|
| 238 |
try { |
| 239 |
const response = await fetch(`${window.config.apiUrl}/auth/signin`, { |
| 240 |
method: "POST", |
| 241 |
headers: { "Content-Type": "application/json" }, |
| 242 |
body: JSON.stringify({ email, password, captcha_token }), |
| 243 |
}); |
| 244 |
|
| 245 |
const data = await response.json(); |
| 246 |
console.log("🔐 Signin response:", { status: response.status, data }); |
| 247 |
|
| 248 |
if (response.ok && data.token) { |
| 249 |
console.log("� |
| 250 |
Token received:", data.token.substring(0, 20) + "..."); |
| 251 |
console.log("� |
| 252 |
Token length:", data.token.length); |
| 253 |
console.log( |
| 254 |
"� |
| 255 |
Token format check:", |
| 256 |
data.token.split(".").length === 3 |
| 257 |
? "Valid JWT format" |
| 258 |
: "Invalid JWT format" |
| 259 |
); |
| 260 |
|
| 261 |
// Vérifier le format du token reçu |
| 262 |
if (data.token.split(".").length !== 3) { |
| 263 |
console.error("❌ Invalid JWT format received from signin API"); |
| 264 |
showMessage("Invalid token received from server", "error"); |
| 265 |
return; |
| 266 |
} |
| 267 |
|
| 268 |
// Sauvegarder le token JWT |
| 269 |
const saveResponse = await fetch(ajaxurl, { |
| 270 |
method: "POST", |
| 271 |
headers: { "Content-Type": "application/x-www-form-urlencoded" }, |
| 272 |
body: `action=aibui_save_token&token=${encodeURIComponent( |
| 273 |
data.token |
| 274 |
)}&nonce=${aiBuilderVars.nonce}`, |
| 275 |
}); |
| 276 |
|
| 277 |
const saveData = await saveResponse.json(); |
| 278 |
console.log("💾 Token save response:", saveData); |
| 279 |
|
| 280 |
showMessage("Sign in successful! Redirecting...", "success"); |
| 281 |
|
| 282 |
// Recharger la page pour afficher le dashboard |
| 283 |
location.reload(); |
| 284 |
} else { |
| 285 |
console.error("❌ Signin failed:", data); |
| 286 |
showMessage( |
| 287 |
data.message || "Sign in failed. Please check your credentials.", |
| 288 |
"error" |
| 289 |
); |
| 290 |
} |
| 291 |
} catch (error) { |
| 292 |
console.error("❌ Signin network error:", error); |
| 293 |
showMessage("Network error. Please check your connection.", "error"); |
| 294 |
} finally { |
| 295 |
setLoading(buttonId, false); |
| 296 |
// Réinitialiser le widget Turnstile |
| 297 |
// if (currentForm === 'signin') { |
| 298 |
// window.turnstile && turnstile.reset('cf-turnstile-signin'); |
| 299 |
// } else { |
| 300 |
// window.turnstile && turnstile.reset('cf-turnstile-hidden-signin'); |
| 301 |
// } |
| 302 |
} |
| 303 |
} |
| 304 |
|
| 305 |
async function signOut() { |
| 306 |
try { |
| 307 |
await fetch(ajaxurl, { |
| 308 |
method: "POST", |
| 309 |
headers: { "Content-Type": "application/x-www-form-urlencoded" }, |
| 310 |
body: "action=aibui_signout&nonce=" + aiBuilderVars.nonce, |
| 311 |
}); |
| 312 |
|
| 313 |
showMessage("Signed out successfully", "success"); |
| 314 |
|
| 315 |
setTimeout(() => { |
| 316 |
location.reload(); |
| 317 |
}, 1500); |
| 318 |
} catch (error) { |
| 319 |
showMessage("Error signing out", "error"); |
| 320 |
} |
| 321 |
} |
| 322 |
|
| 323 |
// Initialiser les listeners |
| 324 |
document.addEventListener("DOMContentLoaded", function () { |
| 325 |
// Déterminer le formulaire actuel basé sur le contenu de la page |
| 326 |
const signupForm = document.getElementById("signup-form"); |
| 327 |
const signinForm = document.getElementById("signin-form"); |
| 328 |
const dashboardContent = document.querySelector(".ai-dashboard-content"); |
| 329 |
|
| 330 |
if (signupForm) { |
| 331 |
currentForm = "signup"; |
| 332 |
} else if (signinForm) { |
| 333 |
currentForm = "signin"; |
| 334 |
} else if (dashboardContent) { |
| 335 |
currentForm = "dashboard"; |
| 336 |
} |
| 337 |
|
| 338 |
setupFormListeners(); |
| 339 |
|
| 340 |
// Si on est sur le dashboard, charger les informations du compte |
| 341 |
if (currentForm === "dashboard") { |
| 342 |
loadUserAccountInfo(); |
| 343 |
} |
| 344 |
}); |
| 345 |
|
| 346 |
// Fonction pour charger les informations du compte utilisateur |
| 347 |
async function loadUserAccountInfo() { |
| 348 |
// Éviter les appels multiples |
| 349 |
if (window.isLoadingUserInfo) { |
| 350 |
return; |
| 351 |
} |
| 352 |
window.isLoadingUserInfo = true; |
| 353 |
|
| 354 |
console.log("🔍 Loading user account info..."); |
| 355 |
|
| 356 |
// Récupérer le token depuis les options WordPress via AJAX |
| 357 |
try { |
| 358 |
const tokenResponse = await fetch(ajaxurl, { |
| 359 |
method: "POST", |
| 360 |
headers: { "Content-Type": "application/x-www-form-urlencoded" }, |
| 361 |
body: "action=aibui_get_token&nonce=" + aiBuilderVars.nonce, |
| 362 |
}); |
| 363 |
|
| 364 |
const tokenData = await tokenResponse.json(); |
| 365 |
console.log("📡 Token response:", tokenData); |
| 366 |
|
| 367 |
if (!tokenData.success || !tokenData.data.token) { |
| 368 |
console.error("❌ No token found:", tokenData); |
| 369 |
showMessage("No authentication token found", "error"); |
| 370 |
// Supprimer le token invalide et rediriger vers la connexion |
| 371 |
await fetch(ajaxurl, { |
| 372 |
method: "POST", |
| 373 |
headers: { "Content-Type": "application/x-www-form-urlencoded" }, |
| 374 |
body: "action=aibui_signout&nonce=" + aiBuilderVars.nonce, |
| 375 |
}); |
| 376 |
setTimeout(() => { |
| 377 |
location.reload(); |
| 378 |
}, 2000); |
| 379 |
return; |
| 380 |
} |
| 381 |
|
| 382 |
const jwtToken = tokenData.data.token; |
| 383 |
// Vérifier le format du token |
| 384 |
if (jwtToken.split(".").length !== 3) { |
| 385 |
console.error( |
| 386 |
"❌ Invalid JWT format - token should have 3 parts separated by dots" |
| 387 |
); |
| 388 |
showMessage("Invalid token format", "error"); |
| 389 |
return; |
| 390 |
} |
| 391 |
const response = await fetch(`${window.config.apiUrl}/user/profile`, { |
| 392 |
method: "GET", |
| 393 |
headers: { |
| 394 |
Authorization: `Bearer ${jwtToken}`, |
| 395 |
"Content-Type": "application/json", |
| 396 |
}, |
| 397 |
}); |
| 398 |
|
| 399 |
console.log("🌐 API Response status:", response.status); |
| 400 |
console.log( |
| 401 |
"🌐 API Response headers:", |
| 402 |
Object.fromEntries(response.headers.entries()) |
| 403 |
); |
| 404 |
|
| 405 |
if (response.ok) { |
| 406 |
const userData = await response.json(); |
| 407 |
console.log("� |
| 408 |
User data received:", userData); |
| 409 |
displayUserInfo(userData?.user); |
| 410 |
} else if (response.status === 401) { |
| 411 |
console.error("❌ 401 Unauthorized - Token might be invalid"); |
| 412 |
|
| 413 |
// Essayer de récupérer plus d'informations sur l'erreur |
| 414 |
try { |
| 415 |
const errorData = await response.text(); |
| 416 |
console.error("❌ Error response body:", errorData); |
| 417 |
} catch (e) { |
| 418 |
console.error("❌ Could not read error response"); |
| 419 |
} |
| 420 |
|
| 421 |
showMessage("Authentication failed. Please sign in again.", "error"); |
| 422 |
// Supprimer le token invalide |
| 423 |
await fetch(ajaxurl, { |
| 424 |
method: "POST", |
| 425 |
headers: { "Content-Type": "application/x-www-form-urlencoded" }, |
| 426 |
body: "action=aibui_signout&nonce=" + aiBuilderVars.nonce, |
| 427 |
}); |
| 428 |
// Rediriger vers la connexion sans recharger en boucle |
| 429 |
setTimeout(() => { |
| 430 |
window.location.href = aiBuilderVars.accountUrl; |
| 431 |
}, 2000); |
| 432 |
} else { |
| 433 |
console.error("❌ API Error:", response.status, response.statusText); |
| 434 |
showMessage("Failed to load account information", "error"); |
| 435 |
} |
| 436 |
} catch (error) { |
| 437 |
console.error("❌ Network error:", error); |
| 438 |
showMessage("Network error while loading account information", "error"); |
| 439 |
} finally { |
| 440 |
window.isLoadingUserInfo = false; |
| 441 |
} |
| 442 |
} |
| 443 |
|
| 444 |
// Fonction pour afficher les informations utilisateur |
| 445 |
function displayUserInfo(userData) { |
| 446 |
console.log("🔍 User data:", userData); |
| 447 |
// Afficher le plan |
| 448 |
const planBadge = document.getElementById("plan-badge"); |
| 449 |
if (planBadge) { |
| 450 |
const plan = userData.plan || "basic"; |
| 451 |
planBadge.textContent = plan.charAt(0).toUpperCase() + plan.slice(1); |
| 452 |
console.log("🔍 Plan:", planBadge.textContent); |
| 453 |
planBadge.className = `ai-plan-badge ${plan}`; |
| 454 |
} |
| 455 |
|
| 456 |
// Afficher les crédits |
| 457 |
const creditsDisplay = document.getElementById("credits-display"); |
| 458 |
const creditsBreakdown = userData.aiCredits || { |
| 459 |
onAccountCreation: 0, |
| 460 |
monthlySubscription: 0, |
| 461 |
paid: 0, |
| 462 |
}; |
| 463 |
|
| 464 |
// Calculer le total des crédits |
| 465 |
const totalCredits = |
| 466 |
creditsBreakdown.onAccountCreation + |
| 467 |
creditsBreakdown.monthlySubscription + |
| 468 |
creditsBreakdown.paid; |
| 469 |
|
| 470 |
if (creditsDisplay) { |
| 471 |
creditsDisplay.innerHTML = ` |
| 472 |
<span>${totalCredits}</span> |
| 473 |
<span style="font-size: 14px; color: #666;">credits</span> |
| 474 |
`; |
| 475 |
} |
| 476 |
|
| 477 |
// Afficher le détail des crédits |
| 478 |
const creationCredits = document.getElementById("creation-credits"); |
| 479 |
const monthlyCredits = document.getElementById("monthly-credits"); |
| 480 |
const paidCredits = document.getElementById("paid-credits"); |
| 481 |
|
| 482 |
if (creationCredits) |
| 483 |
creationCredits.textContent = creditsBreakdown.onAccountCreation; |
| 484 |
if (monthlyCredits) |
| 485 |
monthlyCredits.textContent = creditsBreakdown.monthlySubscription; |
| 486 |
if (paidCredits) paidCredits.textContent = creditsBreakdown.paid; |
| 487 |
} |
| 488 |
|