1import { showMessageToUser } from '../ajax.js'; 2 3const PASSWORD_EYE_SVG = `<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M2.062 12.348a1 1 0 0 1 0-.696C3.423 7.51 7.36 4.5 12 4.5s8.577 3.01 9.938 7.152a1 1 0 0 1 0 .696C20.577 16.49 16.64 19.5 12 19.5s-8.577-3.01-9.938-7.152"/><path d="M12 15.5a3.5 3.5 0 1 0 0-7 3.5 3.5 0 0 0 0 7"/></svg>`; 4const PASSWORD_EYE_OFF_SVG = `<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M10.733 5.076A10.744 10.744 0 0 1 12 5c4.636 0 8.573 3.01 9.938 7.152a1 1 0 0 1 0 .696 10.1 10.1 0 0 1-1.479 2.658"/><path d="M6.61 6.61A10.61 10.61 0 0 0 2.062 11.652a1 1 0 0 0 0 .696C3.423 16.49 7.36 19.5 12 19.5c1.735 0 3.37-.422 4.808-1.168"/><path d="M9.9 9.9a3.5 3.5 0 0 0 4.2 4.2"/><path d="M14.12 14.12 9.88 9.88"/><path d="M3 3l18 18"/></svg>`; 5 6let loginSubmitInFlight = false; 7let lastAutoSubmittedOtp = ''; 8const AUTH_MODE_PASSWORD = 1; 9const AUTH_MODE_SMS_OTP = 2; 10 11function normalizeAuthMode(value) { 12 return Number(value) === AUTH_MODE_SMS_OTP || String(value ?? '').trim().toLowerCase() === 'sms_otp' 13 ? AUTH_MODE_SMS_OTP 14 : AUTH_MODE_PASSWORD; 15} 16 17const loginState = { 18 resolvedEmail: '', 19 authMode: 0, 20 challengeId: 0, 21}; 22 23const forgotPasswordState = { 24 active: false, 25 step: 'none', 26 email: '', 27 challengeId: 0, 28}; 29 30function qs(sel, root = document) { 31 return root.querySelector(sel); 32} 33 34function showEl(el) { 35 if (!el) return; 36 el.classList.remove('hidden'); 37} 38 39function hideEl(el) { 40 if (!el) return; 41 el.classList.add('hidden'); 42} 43 44function showLoginMessage(message, type = 'error') { 45 const text = String(message || '').trim(); 46 if (!text) { 47 return; 48 } 49 50 showMessageToUser(text, type); 51 window.scrollTo(0, 0); 52} 53 54function showOtpSentMessage(isResend = false) { 55 showLoginMessage( 56 isResend 57 ? 'ש××× × ×§×× ××××ת ××ש ×-SMS. ××ת×× ×©×××§× ×ספר ×©× ××ת ×¢× ×©××§×× ××××¢.' 58 : 'ש××× × ×§×× ××××ת ×-SMS. ××ת×× ×©×××§× ×ספר ×©× ××ת ×¢× ×©××§×× ××××¢.', 59 'success' 60 ); 61} 62 63function showForgotPasswordOtpSentMessage() { 64 showLoginMessage('×× ××ש××× ×§××× ××ער×ת, × ×©×× ×§×× ××××ת ×× ××× ××ש××× ××××. ××ת×× ×©×××§× ×ספר ×©× ××ת ×¢× ×©××§×× ××××¢.', 'success'); 65} 66 67function setBusy(isBusy) { 68 const loading = qs('#LoadingDiv'); 69 const cover = qs('#cover'); 70 71 if (loading) { 72 loading.classList.toggle('is-active', !!isBusy); 73 } 74 if (cover) { 75 cover.classList.toggle('is-active', !!isBusy); 76 } 77} 78 79function getUserNameValue() { 80 const el = qs('#Inp_LogIn_UserName'); 81 return el ? String(el.value || '') : ''; 82} 83 84function getPasswordValue() { 85 const el = qs('#Inp_LogIn_Password'); 86 return el ? String(el.value || '') : ''; 87} 88 89function getOtpValue() { 90 const el = qs('#Inp_LogIn_OtpCode'); 91 return el ? String(el.value || '') : ''; 92} 93 94function getRestorePhoneValue() { 95 const el = qs('#Inp_LogIn_restorePassword'); 96 return el ? String(el.value || '') : ''; 97} 98 99function getForgotUserNameValue() { 100 const el = qs('#Inp_ForgotPassword_UserName'); 101 return el ? String(el.value || '') : ''; 102} 103 104function getForgotOtpValue() { 105 const el = qs('#Inp_ForgotPassword_OtpCode'); 106 return el ? String(el.value || '') : ''; 107} 108 109function getForgotNewPasswordValue() { 110 const el = qs('#Inp_ForgotPassword_NewPassword'); 111 return el ? String(el.value || '') : ''; 112} 113 114function getForgotConfirmPasswordValue() { 115 const el = qs('#Inp_ForgotPassword_ConfirmPassword'); 116 return el ? String(el.value || '') : ''; 117} 118 119function setLoginPasswordVisibility(visible) { 120 const passwordInput = qs('#Inp_LogIn_Password'); 121 const toggleBtn = qs('#loginPasswordToggle'); 122 if (!passwordInput || !toggleBtn) { 123 return; 124 } 125 126 passwordInput.type = visible ? 'text' : 'password'; 127 toggleBtn.innerHTML = visible ? PASSWORD_EYE_OFF_SVG : PASSWORD_EYE_SVG; 128 toggleBtn.setAttribute('aria-label', visible ? '×סתר ס×ס××' : '××¦× ×¡×ס××'); 129 toggleBtn.setAttribute('title', visible ? '×סתר ס×ס××' : '××¦× ×¡×ס××'); 130 toggleBtn.setAttribute('aria-pressed', visible ? 'true' : 'false'); 131 toggleBtn.dataset.visible = visible ? '1' : '0'; 132} 133 134function setTitle(text) { 135 const el = qs('#loginTitle'); 136 if (el) { 137 el.textContent = String(text || '').trim(); 138 } 139} 140 141function setSubtitle(text, isOtpSubtitle = false) { 142 const el = qs('#loginSubtitle'); 143 if (el) { 144 el.textContent = String(text || '').trim(); 145 el.classList.toggle('login-subtitle-otp', !!isOtpSubtitle); 146 } 147} 148 149function setConnectButtonText(text) { 150 const btn = qs('#connect'); 151 if (btn) { 152 btn.textContent = text; 153 } 154} 155 156function renderOtpHelpText(el, maskedPhone) { 157 if (!el) { 158 return; 159 } 160 161 if (!maskedPhone) { 162 el.textContent = 'ש××× × ×§×× ××××ת ×-SMS'; 163 return; 164 } 165 166 const match = String(maskedPhone).match(/^(.*-)(\d{4})$/); 167 el.textContent = 'ש××× × ×§×× ××××ת ×× '; 168 169 if (!match) { 170 el.append(document.createTextNode(maskedPhone)); 171 return; 172 } 173 174 el.append(document.createTextNode(match[1])); 175 const lastDigits = document.createElement('span'); 176 lastDigits.className = 'login-otp-last-digits'; 177 lastDigits.textContent = match[2]; 178 el.append(lastDigits); 179} 180 181function hideForgotPasswordElements() { 182 hideEl(qs('#forgotUserNameField')); 183 hideEl(qs('#forgotOtpField')); 184 hideEl(qs('#forgotNewPasswordField')); 185 hideEl(qs('#forgotConfirmPasswordField')); 186 hideEl(qs('#forgotPasswordPrimaryAction')); 187 hideEl(qs('#forgotPasswordSecondaryActions')); 188 hideEl(qs('#forgotPasswordBackActions')); 189} 190 191function clearForgotPasswordInputs({ keepEmail = false, keepOtp = false } = {}) { 192 const emailInput = qs('#Inp_ForgotPassword_UserName'); 193 const otpInput = qs('#Inp_ForgotPassword_OtpCode'); 194 const newPasswordInput = qs('#Inp_ForgotPassword_NewPassword'); 195 const confirmPasswordInput = qs('#Inp_ForgotPassword_ConfirmPassword'); 196 197 if (emailInput && !keepEmail) { 198 emailInput.value = ''; 199 } 200 if (otpInput && !keepOtp) { 201 otpInput.value = ''; 202 } 203 if (newPasswordInput) { 204 newPasswordInput.value = ''; 205 } 206 if (confirmPasswordInput) { 207 confirmPasswordInput.value = ''; 208 } 209} 210 211function enterForgotPasswordRequestMode(prefillEmail = '') { 212 forgotPasswordState.active = true;
213 forgotPasswordState.step = 'request'; 214 forgotPasswordState.email = String(prefillEmail || '').trim(); 215 forgotPasswordState.challengeId = 0; 216 217 hideEl(qs('#userName')); 218 hideEl(qs('#password')); 219 hideEl(qs('#otpField')); 220 hideEl(qs('#otpHelpText')); 221 hideEl(qs('#connect')); 222 hideEl(qs('#otpActions')); 223 hideEl(qs('#loginSupportActions')); 224 hideForgotPasswordElements(); 225 226 showEl(qs('#forgotUserNameField')); 227 showEl(qs('#forgotPasswordPrimaryAction')); 228 showEl(qs('#forgotPasswordBackActions')); 229 230 setTitle('××פ×ס ס×ס××'); 231 setSubtitle('××× ×©× ×שת×ש, ××× ××ש××× ×§××× ××ער×ת × ×©×× ×§×× ××××ת ×× ××× ××ש××× ××××'); 232 233 const primaryBtn = qs('#forgotPasswordPrimaryAction'); 234 if (primaryBtn) { 235 primaryBtn.textContent = 'ש×× ×§×× ××××ת'; 236 } 237 238 clearForgotPasswordInputs({ keepEmail: true }); 239 const emailInput = qs('#Inp_ForgotPassword_UserName'); 240 if (emailInput) { 241 emailInput.value = forgotPasswordState.email; 242 emailInput.focus(); 243 } 244} 245 246function enterForgotPasswordOtpMode(email, challengeId) { 247 forgotPasswordState.active = true; 248 forgotPasswordState.step = 'otp'; 249 forgotPasswordState.email = String(email || '').trim(); 250 forgotPasswordState.challengeId = Number(challengeId || 0); 251 252 hideForgotPasswordElements(); 253 showEl(qs('#forgotOtpField')); 254 showEl(qs('#forgotPasswordPrimaryAction')); 255 showEl(qs('#forgotPasswordSecondaryActions')); 256 showEl(qs('#forgotPasswordBackActions')); 257 258 setTitle('××××ת ×§××'); 259 setSubtitle('×× ××ש××× ×§××× ××ער×ת, × ×©×× ×§×× ××××ת ×× ××× ××ש××× ××××'); 260 261 const primaryBtn = qs('#forgotPasswordPrimaryAction'); 262 if (primaryBtn) { 263 primaryBtn.textContent = '××ת ×§××'; 264 } 265 266 clearForgotPasswordInputs({ keepEmail: true }); 267 const emailInput = qs('#Inp_ForgotPassword_UserName'); 268 if (emailInput) { 269 emailInput.value = forgotPasswordState.email; 270 } 271 const otpInput = qs('#Inp_ForgotPassword_OtpCode'); 272 if (otpInput) { 273 otpInput.focus(); 274 } 275} 276 277function enterForgotPasswordNewPasswordMode() { 278 forgotPasswordState.active = true; 279 forgotPasswordState.step = 'password'; 280 281 hideForgotPasswordElements(); 282 showEl(qs('#forgotNewPasswordField')); 283 showEl(qs('#forgotConfirmPasswordField')); 284 showEl(qs('#forgotPasswordPrimaryAction')); 285 showEl(qs('#forgotPasswordBackActions')); 286 287 setTitle('×××רת ס×ס×× ××ש×'); 288 setSubtitle('×××× × ×¡×ס×× ×××©× ×××©×¨× ×××ª× ××× ××ש××× ×ת ×××פ×ס'); 289 290 const primaryBtn = qs('#forgotPasswordPrimaryAction'); 291 if (primaryBtn) { 292 primaryBtn.textContent = 'ש××ר ס×ס×× ××ש×'; 293 } 294 295 clearForgotPasswordInputs({ keepEmail: true }); 296 const passwordInput = qs('#Inp_ForgotPassword_NewPassword'); 297 if (passwordInput) { 298 passwordInput.focus(); 299 } 300} 301 302function enterForgotPasswordSuccessMode() { 303 forgotPasswordState.active = true; 304 forgotPasswordState.step = 'success'; 305 forgotPasswordState.challengeId = 0; 306 307 hideForgotPasswordElements(); 308 showEl(qs('#forgotPasswordPrimaryAction')); 309 310 setTitle('×ס×ס×× ×¢×××× ×'); 311 setSubtitle('×ס×ס×× ×¢×××× × ××צ×××. × ××ª× ××ת××ר ××ער×ת.'); 312 313 const primaryBtn = qs('#forgotPasswordPrimaryAction'); 314 if (primaryBtn) { 315 primaryBtn.textContent = '×××¨× ××ת××ר×ת'; 316 primaryBtn.focus(); 317 } 318} 319 320function exitForgotPasswordMode() { 321 const emailToRestore = forgotPasswordState.email || getForgotUserNameValue().trim(); 322 forgotPasswordState.active = false; 323 forgotPasswordState.step = 'none'; 324 forgotPasswordState.email = ''; 325 forgotPasswordState.challengeId = 0; 326 327 hideForgotPasswordElements(); 328 showEl(qs('#loginSupportActions')); 329 resetAuthFlow({ clearPassword: false }); 330 331 const loginUserInput = qs('#Inp_LogIn_UserName'); 332 if (loginUserInput && emailToRestore) { 333 loginUserInput.value = emailToRestore; 334 } 335 if (loginUserInput) { 336 loginUserInput.focus(); 337 } 338} 339 340function resetAuthFlow({ clearPassword = true } = {}) { 341 loginState.resolvedEmail = ''; 342 loginState.authMode = 0; 343 loginState.challengeId = 0; 344 lastAutoSubmittedOtp = ''; 345 346 setTitle('××× ××¡× ××ער×ת'); 347 showEl(qs('#userName')); 348 showEl(qs('#password')); 349 showEl(qs('#connect')); 350 showEl(qs('#loginSupportActions')); 351 hideEl(qs('#otpField')); 352 hideEl(qs('#otpHelpText')); 353 hideEl(qs('#otpActions')); 354 hideForgotPasswordElements(); 355 setSubtitle('×××× × ×©× ×שת×ש ×ס×ס×× ××× ×××ש××'); 356 setConnectButtonText('×ת××ר ××ער×ת'); 357 358 const otpInput = qs('#Inp_LogIn_OtpCode'); 359 if (otpInput) { 360 otpInput.value = ''; 361 } 362 363 const otpHelp = qs('#otpHelpText'); 364 if (otpHelp) { 365 otpHelp.textContent = ''; 366 } 367 368 if (clearPassword) { 369 const passInput = qs('#Inp_LogIn_Password'); 370 if (passInput) { 371 passInput.value = ''; 372 } 373 } 374} 375 376function setPasswordMode(email) { 377 loginState.resolvedEmail = email; 378 loginState.authMode = AUTH_MODE_PASSWORD; 379 loginState.challengeId = 0; 380 lastAutoSubmittedOtp = ''; 381 382 setTitle('××× ××¡× ××ער×ת'); 383 showEl(qs('#userName')); 384 showEl(qs('#password')); 385 showEl(qs('#connect')); 386 showEl(qs('#loginSupportActions')); 387 hideEl(qs('#otpField')); 388 hideEl(qs('#otpHelpText')); 389 hideEl(qs('#otpActions')); 390 hideForgotPasswordElements(); 391 setSubtitle('×××× × ×¡×ס×× ××ת××ר×'); 392 setConnectButtonText('×ת××ר ××ער×ת'); 393 394 const otpInput = qs('#Inp_LogIn_OtpCode'); 395 if (otpInput) { 396 otpInput.value = ''; 397 } 398 399 const passInput = qs('#Inp_LogIn_Password'); 400 if (passInput) { 401 passInput.focus(); 402 } 403} 404 405function enterSmsMode(email, otpResult) { 406 loginState.resolvedEmail = email; 407 loginState.authMode = AUTH_MODE_SMS_OTP; 408 loginState.challengeId = Number(otpResult?.ChallengeId || 0); 409 lastAutoSubmittedOtp = ''; 410 411 setTitle('××× ××¡× ××ער×ת'); 412 showEl(qs('#userName')); 413 showEl(qs('#password')); 414 showEl(qs('#otpField')); 415 showEl(qs('#otpHelpText')); 416 showEl(qs('#otpActions')); 417 showEl(qs('#connect')); 418 hideEl(qs('#loginSupportActions')); 419 hideForgotPasswordElements(); 420 setConnectButtonText('××ת ××ת××ר'); 421 422 const maskedPhone = String(otpResult?.MaskedPhone || '').trim(); 423 setSubtitle('×ס×ס×× ×××ת×. ×××× × ×§×× ××××ת ×©× ×©×× ×-SMS', true); 424 const otpHelp = qs('#otpHelpText'); 425 if (otpHelp) {
426 renderOtpHelpText(otpHelp, maskedPhone); 427 } 428 429 const otpInput = qs('#Inp_LogIn_OtpCode'); 430 if (otpInput) { 431 otpInput.value = ''; 432 otpInput.focus(); 433 } 434} 435 436async function postJson(url, body) { 437 const res = await fetch(url, { 438 method: 'POST', 439 headers: { 'Content-Type': 'application/json; charset=utf-8' }, 440 credentials: 'same-origin', 441 body: JSON.stringify(body || {}), 442 }); 443 444 const data = await res.json().catch(() => ({})); 445 if (!res.ok || !data || Number(data.isSuccess || 0) !== 1) { 446 const msg = String(data?.message || data?.error || `HTTP ${res.status}` || 'ש×××× ×שרת').trim(); 447 throw new Error(msg || 'ש×××× ×שרת'); 448 } 449 450 return data; 451} 452 453async function resolveAuthMode(email) { 454 if (loginState.resolvedEmail === email && loginState.authMode) { 455 return { AuthMode: loginState.authMode }; 456 } 457 458 const out = await postJson('/crm/api/login/ResolveAuthMode.php', { 459 UserName: email, 460 }); 461 const authMode = normalizeAuthMode(out?.result?.AuthMode); 462 463 loginState.resolvedEmail = email; 464 loginState.authMode = authMode; 465 loginState.challengeId = 0; 466 467 return { AuthMode: authMode }; 468} 469 470async function requestOtp(email, password) { 471 const out = await postJson('/crm/api/login/RequestOtp.php', { 472 UserName: email, 473 password, 474 }); 475 return out.result || {}; 476} 477 478async function requestPasswordResetOtp(email) { 479 const out = await postJson('/crm/api/login/RequestPasswordResetOtp.php', { 480 UserName: email, 481 }); 482 return out.result || {}; 483} 484 485async function verifyOtp(challengeId, otpCode) { 486 const out = await postJson('/crm/api/login/VerifyOtp.php', { 487 ChallengeId: challengeId, 488 OtpCode: otpCode, 489 }); 490 return out.result || {}; 491} 492 493async function verifyPasswordResetOtp(challengeId, otpCode) { 494 const out = await postJson('/crm/api/login/VerifyPasswordResetOtp.php', { 495 ChallengeId: challengeId, 496 OtpCode: otpCode, 497 }); 498 return out.result || {}; 499} 500 501async function savePasswordReset(password, passwordConfirm) { 502 const out = await postJson('/crm/api/login/SavePasswordReset.php', { 503 Password: password, 504 PasswordConfirm: passwordConfirm, 505 }); 506 return out.result || {}; 507} 508 509async function loginWithPassword(email, password) { 510 const out = await postJson('/crm/api/login/MakeLogin.php', { 511 UserName: email, 512 password, 513 }); 514 return out.result || {}; 515} 516 517function consumeSafeReturnUrl() { 518 let stored = ''; 519 try { 520 stored = sessionStorage.getItem('crm_return_url') || ''; 521 } catch (_) { 522 return ''; 523 } 524 try { 525 sessionStorage.removeItem('crm_return_url'); 526 } catch (_) {} 527 if (!stored) return ''; 528 try { 529 const u = new URL(stored, window.location.origin); 530 if (u.origin !== window.location.origin) return ''; 531 const p = u.pathname; 532 const allowed = p.startsWith('/crm/') || p === '/super-sales' || p.startsWith('/super-sales/'); 533 if (!allowed) return ''; 534 if (p.toLowerCase().endsWith('/crm/login.php')) return ''; 535 return u.href; 536 } catch (_) { 537 return ''; 538 } 539} 540 541function normalizeLoginDefaultRoute(route) { 542 const normalized = String(route || '').trim().toLowerCase(); 543 return ['leads', 'events', 'invitations'].includes(normalized) ? normalized : 'leads'; 544} 545 546function redirectAfterLogin(legacyResult) { 547 const returnUrl = consumeSafeReturnUrl(); 548 if (returnUrl) { 549 window.location.href = returnUrl; 550 return; 551 } 552 553 const idx = Number(legacyResult?.index || 0); 554 const type = Number(legacyResult?.type || 0); 555 const businessId = legacyResult?.businessId ? String(legacyResult.businessId) : ''; 556 557 if (idx === 1) { 558 window.location.href = '/crm/AddComment.php'; 559 return; 560 } 561 562 if (type === 1) { 563 let url = '/crm/'; 564 if (businessId) { 565 url = '/crm/?id=' + encodeURIComponent(businessId); 566 } 567 window.location.href = url; 568 return; 569 } 570 571 const defaultRoute = normalizeLoginDefaultRoute(legacyResult?.defaultRoute); 572 let url = '/crm/#' + defaultRoute; 573 if (businessId) { 574 url = '/crm/?id=' + encodeURIComponent(businessId) + '#' + defaultRoute; 575 } 576 window.location.href = url; 577} 578 579async function handleConnectClick(event) { 580 if (event) { 581 event.preventDefault(); 582 } 583 584 if (forgotPasswordState.active || loginSubmitInFlight) { 585 return; 586 } 587 588 const userName = getUserNameValue().trim(); 589 if (!userName) { 590 showLoginMessage('× × ××××× ×©× ×שת×ש'); 591 return; 592 } 593 594 window.scrollTo(0, 0); 595 loginSubmitInFlight = true; 596 setBusy(true); 597 598 try { 599 if (loginState.challengeId > 0 && loginState.resolvedEmail === userName) { 600 const otpCode = getOtpValue().trim(); 601 if (!/^\d{6}$/.test(otpCode)) { 602 showLoginMessage('× × ××××× ×§×× ××××ת ×× 6 ספר×ת'); 603 return; 604 } 605 606 const loginResult = await verifyOtp(loginState.challengeId, otpCode); 607 redirectAfterLogin(loginResult); 608 return; 609 } 610 611 const password = getPasswordValue(); 612 if (!password) { 613 const passInput = qs('#Inp_LogIn_Password'); 614 if (passInput) { 615 passInput.focus(); 616 } 617 showLoginMessage('× × ××××× ×¡×ס××'); 618 return; 619 } 620 621 const loginResult = await loginWithPassword(userName, password); 622 if (Number(loginResult?.otpRequired || 0) === 1 || Number(loginResult?.ChallengeId || 0) > 0) { 623 enterSmsMode(userName, loginResult); 624 showOtpSentMessage(false); 625 return; 626 } 627 628 redirectAfterLogin(loginResult); 629 } catch (error) { 630 const msg = String(error?.message || '').trim(); 631 if (msg && msg !== 'USER_NOT_CONNECT') { 632 showLoginMessage(msg); 633 } 634 } finally { 635 loginSubmitInFlight = false;
636 setBusy(false); 637 } 638} 639 640function handleLoginOtpInput() { 641 if (forgotPasswordState.active || loginSubmitInFlight || loginState.authMode !== AUTH_MODE_SMS_OTP || loginState.challengeId <= 0) { 642 return; 643 } 644 645 const userName = getUserNameValue().trim(); 646 const otpCode = getOtpValue().trim(); 647 if (loginState.resolvedEmail !== userName || !/^\d{6}$/.test(otpCode) || otpCode === lastAutoSubmittedOtp) { 648 return; 649 } 650 651 lastAutoSubmittedOtp = otpCode; 652 handleConnectClick(); 653} 654 655async function handleForgotPasswordPrimaryAction(event) { 656 if (event) { 657 event.preventDefault(); 658 } 659 660 if (!forgotPasswordState.active || loginSubmitInFlight) { 661 return; 662 } 663 664 if (forgotPasswordState.step === 'success') { 665 exitForgotPasswordMode(); 666 return; 667 } 668 669 loginSubmitInFlight = true; 670 setBusy(true); 671 672 try { 673 if (forgotPasswordState.step === 'request') { 674 const email = getForgotUserNameValue().trim(); 675 if (!email) { 676 showLoginMessage('× × ××××× ×©× ×שת×ש'); 677 return; 678 } 679 680 const result = await requestPasswordResetOtp(email); 681 enterForgotPasswordOtpMode(email, Number(result?.ChallengeId || 0)); 682 showForgotPasswordOtpSentMessage(); 683 return; 684 } 685 686 if (forgotPasswordState.step === 'otp') { 687 const otpCode = getForgotOtpValue().trim(); 688 if (!/^\d{6}$/.test(otpCode)) { 689 showLoginMessage('× × ××××× ×§×× ××××ת ×× 6 ספר×ת'); 690 return; 691 } 692 693 await verifyPasswordResetOtp(forgotPasswordState.challengeId, otpCode); 694 enterForgotPasswordNewPasswordMode(); 695 return; 696 } 697 698 if (forgotPasswordState.step === 'password') { 699 await savePasswordReset(getForgotNewPasswordValue(), getForgotConfirmPasswordValue()); 700 enterForgotPasswordSuccessMode(); 701 showLoginMessage('×ס×ס×× ×¢×××× × ××צ×××. × ××ª× ××ת××ר ××ער×ת.', 'success'); 702 } 703 } catch (error) { 704 const msg = String(error?.message || '').trim(); 705 showLoginMessage(msg || 'ש×××× ×ת×××× ××פ×ס ×ס×ס××'); 706 } finally { 707 loginSubmitInFlight = false; 708 setBusy(false); 709 } 710} 711 712async function handleForgotPasswordResend(event) { 713 if (event) { 714 event.preventDefault(); 715 } 716 717 if (!forgotPasswordState.active || forgotPasswordState.step !== 'otp' || loginSubmitInFlight) { 718 return; 719 } 720 721 const email = forgotPasswordState.email || getForgotUserNameValue().trim(); 722 if (!email) { 723 showLoginMessage('× × ××××× ×©× ×שת×ש'); 724 enterForgotPasswordRequestMode(''); 725 return; 726 } 727 728 loginSubmitInFlight = true; 729 setBusy(true); 730 731 try { 732 const result = await requestPasswordResetOtp(email); 733 enterForgotPasswordOtpMode(email, Number(result?.ChallengeId || 0)); 734 showForgotPasswordOtpSentMessage(); 735 } catch (error) { 736 const msg = String(error?.message || '').trim(); 737 showLoginMessage(msg || 'ש×××× ×ש×××ת ×§×× ×××××ת'); 738 } finally { 739 loginSubmitInFlight = false; 740 setBusy(false); 741 } 742} 743 744async function sendNewLink() { 745 const userName = getUserNameValue(); 746 const password = getPasswordValue(); 747 748 setBusy(true); 749 try { 750 const res = await fetch('/crm/api/SendNewLink.php', { 751 method: 'POST', 752 headers: { 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' }, 753 body: new URLSearchParams({ email: userName, password }).toString(), 754 credentials: 'same-origin' 755 }); 756 757 const text = await res.text(); 758 if (String(text).trim() !== '0') { 759 hideEl(qs('#sendNewLink')); 760 showEl(qs('#sendNewLink_message')); 761 } 762 } catch (e) { 763 } finally { 764 setBusy(false); 765 } 766} 767 768async function restorePassword() { 769 const tel_manager = getRestorePhoneValue(); 770 const showProgress = qs('#showProgress'); 771 772 showEl(showProgress); 773 774 try { 775 const res = await fetch('/crm/api/RestorePassword.php', { 776 method: 'POST', 777 headers: { 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' }, 778 body: new URLSearchParams({ tel_manager }).toString(), 779 credentials: 'same-origin' 780 }); 781 782 const data = await res.json(); 783 if (data && data.bool === true) { 784 const el = qs('#Inp_LogIn_restorePassword'); 785 if (el) el.value = ''; 786 } 787 } catch (e) { 788 } finally { 789 hideEl(showProgress); 790 } 791} 792 793function bindLoginHandlers() { 794 const btnConnect = qs('#connect'); 795 const btnSendNewLink = qs('#sendNewLink'); 796 const btnRestoreToggle = qs('#RestoreMyPassword_div'); 797 const btnRestore = qs('#restorePassword'); 798 const btnResendOtp = qs('#resendOtp'); 799 const btnChangeUser = qs('#changeUser'); 800 const btnForgotPasswordLink = qs('#forgotPasswordLink'); 801 const btnForgotPasswordPrimaryAction = qs('#forgotPasswordPrimaryAction'); 802 const btnForgotPasswordResend = qs('#forgotPasswordResendOtp'); 803 const btnForgotPasswordBack = qs('#forgotPasswordBackToLogin'); 804 805 if (btnConnect && btnConnect.dataset.bound === '1') { 806 return; 807 } 808 809 const winWidth = window.innerWidth || 0; 810 if (winWidth < 600) { 811 const topBar = qs('#topBar');
812 if (topBar) topBar.remove(); 813 } 814 815 const inpUser = qs('#Inp_LogIn_UserName'); 816 const inpOtp = qs('#Inp_LogIn_OtpCode'); 817 const inpRestore = qs('#Inp_LogIn_restorePassword'); 818 if (inpUser && inpRestore) { 819 inpUser.addEventListener('blur', () => { 820 inpRestore.value = inpUser.value; 821 }); 822 } 823 if (inpUser) { 824 inpUser.addEventListener('input', () => { 825 const currentValue = String(inpUser.value || '').trim(); 826 if (currentValue !== loginState.resolvedEmail) { 827 resetAuthFlow({ clearPassword: false }); 828 } 829 }); 830 } 831 832 const inpPass = qs('#Inp_LogIn_Password'); 833 const btnPasswordToggle = qs('#loginPasswordToggle'); 834 if (inpPass) { 835 inpPass.addEventListener('input', () => { 836 if (loginState.challengeId > 0) { 837 resetAuthFlow({ clearPassword: false }); 838 } 839 }); 840 841 const hebrewReg = /^(?=.*[×-ת])/; 842 inpPass.addEventListener('keypress', () => { 843 if (hebrewReg.test(String(inpPass.value || ''))) { 844 showLoginMessage('×ס×ס×× ×©××× ×ª ××××× ××ת××ת ××¢×ר×ת, ×× × ××× ×××ש.'); 845 } 846 }); 847 } 848 if (inpOtp) { 849 inpOtp.addEventListener('input', handleLoginOtpInput); 850 } 851 852 if (btnPasswordToggle) { 853 setLoginPasswordVisibility(btnPasswordToggle.dataset.visible === '1'); 854 btnPasswordToggle.addEventListener('click', () => { 855 setLoginPasswordVisibility(btnPasswordToggle.dataset.visible !== '1'); 856 if (inpPass) { 857 inpPass.focus(); 858 } 859 }); 860 } 861 862 if (btnSendNewLink) { 863 btnSendNewLink.addEventListener('click', (e) => { 864 e.preventDefault(); 865 sendNewLink(); 866 }); 867 } 868 869 if (btnRestoreToggle) { 870 btnRestoreToggle.addEventListener('click', (e) => { 871 e.preventDefault(); 872 const box = qs('#RestoreMyPassword'); 873 if (box) { 874 showEl(box); 875 } 876 if (inpRestore) { 877 inpRestore.focus(); 878 } 879 }); 880 } 881 882 if (btnRestore) { 883 btnRestore.addEventListener('click', (e) => { 884 e.preventDefault(); 885 restorePassword(); 886 }); 887 } 888 889 if (btnResendOtp) { 890 btnResendOtp.addEventListener('click', async (event) => { 891 event.preventDefault(); 892 893 if (loginSubmitInFlight) { 894 return; 895 } 896 897 const userName = getUserNameValue().trim(); 898 const password = getPasswordValue(); 899 if (!userName) { 900 showLoginMessage('× × ××××× ×©× ×שת×ש'); 901 return; 902 } 903 if (!password) { 904 showLoginMessage('× × ××××× ×¡×ס××'); 905 return; 906 } 907 908 loginSubmitInFlight = true; 909 setBusy(true); 910 try { 911 const otpResult = await requestOtp(userName, password); 912 enterSmsMode(userName, otpResult); 913 showOtpSentMessage(true); 914 } catch (error) { 915 const msg = String(error?.message || '').trim(); 916 if (msg) { 917 showLoginMessage(msg); 918 } 919 } finally { 920 loginSubmitInFlight = false; 921 setBusy(false); 922 } 923 }); 924 } 925 926 if (btnChangeUser) { 927 btnChangeUser.addEventListener('click', (event) => { 928 event.preventDefault(); 929 resetAuthFlow(); 930 const inp = qs('#Inp_LogIn_UserName'); 931 if (inp) { 932 inp.focus(); 933 } 934 }); 935 } 936 937 if (btnForgotPasswordLink) { 938 btnForgotPasswordLink.addEventListener('click', (event) => { 939 event.preventDefault(); 940 enterForgotPasswordRequestMode(getUserNameValue().trim()); 941 }); 942 } 943 944 if (btnForgotPasswordPrimaryAction) { 945 btnForgotPasswordPrimaryAction.addEventListener('click', handleForgotPasswordPrimaryAction); 946 } 947 948 if (btnForgotPasswordResend) { 949 btnForgotPasswordResend.addEventListener('click', handleForgotPasswordResend); 950 } 951 952 if (btnForgotPasswordBack) { 953 btnForgotPasswordBack.addEventListener('click', (event) => { 954 event.preventDefault(); 955 exitForgotPasswordMode(); 956 }); 957 } 958 959 if (btnConnect) { 960 btnConnect.dataset.bound = '1'; 961 btnConnect.addEventListener('click', handleConnectClick); 962 } 963 964 document.addEventListener('keypress', (e) => { 965 const target = e.target; 966 if (!target || target.tagName !== 'INPUT') return; 967 if (e.which !== 13 && e.key !== 'Enter') return; 968 969 const id = target.getAttribute('id') || ''; 970 if (id === 'Inp_LogIn_restorePassword') { 971 const b = qs('#restorePassword'); 972 if (b) b.click(); 973 e.preventDefault(); 974 return false; 975 } 976 977 if (forgotPasswordState.active) { 978 const b = qs('#forgotPasswordPrimaryAction'); 979 if (b) b.click(); 980 e.preventDefault(); 981 return false;
982 } 983 984 const b = qs('#connect'); 985 if (b) b.click(); 986 e.preventDefault(); 987 return false; 988 }); 989} 990 991resetAuthFlow({ clearPassword: false }); 992bindLoginHandlers(); 993 994window.SendNewLink = sendNewLink;
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.