1let currentLeadId = null; 2 3/** 4 * Triggered when a table row is clicked 5 */ 6function selectLead(id, row) { 7 currentLeadId = id; 8 9 // 1. Highlight the selected row 10 document.querySelectorAll('tbody tr').forEach(r => r.classList.remove('selected')); 11 row.classList.add('selected'); 12 13 // 2. Extract data from the clicked row (cells index matches your table structure) 14 const name = row.cells[1].innerText; 15 const plate = row.cells[3].innerText; 16 const vehicle = row.cells[4].innerText + ' ' + row.cells[5].innerText; 17 const phone = row.getAttribute('data-phone'); // Get phone from data attribute 18 const initials = name.split(' ').map(n => n[0]).join('').substring(0, 2).toUpperCase(); 19 20 // 3. Update the Client Banner content 21 document.getElementById('bannerName').innerText = name; 22 document.getElementById('bannerInitials').innerText = initials; 23 document.getElementById('bannerVehicle').innerText = vehicle; 24 document.getElementById('bannerPlate').innerText = plate; 25 document.getElementById('bannerPhone').innerText = phone; 26 27// DYNAMIC URL LOGIC 28 const banner = document.getElementById('clientBanner'); 29 const eligibilityBtn = document.getElementById('eligibilityBtn'); 30 31// Simply grab the dynamic URL from the data attribute 32 const baseUrl = banner.getAttribute('data-base-url'); 33 34// Apply it to the button (no extra parameters) 35 if (eligibilityBtn) { 36 eligibilityBtn.href = baseUrl; 37 } 38 // 4. Show the Banner and Call Status sections 39 document.getElementById('clientBanner').classList.add('visible'); 40 document.getElementById('callStatusSection').classList.add('visible'); 41 42 // 5. Smooth scroll to the Banner 43 document.getElementById('clientBanner').scrollIntoView({ 44 behavior: 'smooth', 45 block: 'start' 46 }); 47 48} 49 50/** 51 * Handle "Oui/Non" buttons 52 */ 53 54 55function updateLeadStatus(leadId, value) { 56 // We proceed silently now for a better user experience 57 jQuery.ajax({ 58 type: "POST", 59 url: "/wp-content/themes/biomotors-child/core/ajax/valideContactLead.php", 60 data: {
61 lead_id: leadId, 62 status_value: value 63 }, 64 success: function(response) { 65 // UI Update: Find the row and update the badge visually 66 const row = document.querySelector(`tr[onclick*="selectLead(${leadId}"]`); 67 if (row) { 68 const badgeCell = row.cells[7]; // Statut is the 8th column 69 if (value === 1) { 70 badgeCell.innerHTML = '<span class="badge-contacted">Contacté</span>'; 71 // Success alert removed for smoother UX 72 } else { 73 badgeCell.innerHTML = '<span class="badge-pending">En attente</span>'; 74 // Success alert removed for smoother UX 75 } 76 } 77 }, 78 error: function() { 79 // Swapped to console.error so it doesn't interrupt the technician 80 console.error("Erreur lors de la mise à jour du statut pour le lead ID: " + leadId); 81 } 82 }); 83} 84 85/** 86 * Logic for the "Suivant" button in the banner 87 */ 88function nextLead() { 89 const rows = Array.from(document.querySelectorAll('tbody tr')); 90 const currentIndex = rows.findIndex(r => r.classList.contains('selected')); 91 const nextIndex = (currentIndex + 1) % rows.length; 92 93 document.querySelectorAll('.step').forEach(s => s.classList.remove('active')); 94 95 document.getElementById('guideSection').style.display = 'none'; 96 document.getElementById('miniStats').style.display = 'none'; 97 document.getElementById('voicemailSection').style.display = 'none'; 98 document.getElementById('voicemailRecallSection').style.display = 'none'; 99 document.getElementById('callResultSection').style.display = 'none'; 100 101 // Remove "active" class from the Yes/No call buttons 102 document.querySelectorAll('.call-btn').forEach(btn => btn.classList.remove('active')); 103 104 // Reset Stepper UI to step 1 (visually) 105 updateStepperUI(1); 106 107 // Simulate a click on the next row 108 rows[nextIndex].click(); 109} 110/** 111 * Toggles accordion steps 112 */ 113 114function toggleStep(header) { 115 const stepElement = header.parentElement; 116 const isOpening = !stepElement.classList.contains('active'); 117 118 // 1. Toggle ONLY this step (others remain as they were) 119 stepElement.classList.toggle('active'); 120 121 // 2. If we are opening the step, update the stepper and scroll to it 122 if (isOpening) { 123 const stepNumber = parseInt(stepElement.id.replace('step-', '')); 124 updateStepperUI(stepNumber); 125 126 // 3. Precision Scroll Logic 127 // We calculate the position manually to avoid the "middle of the page" issue 128 const headerHeight = 170; // Total height of your sticky banner + stepper 129 const elementPosition = stepElement.getBoundingClientRect().top; 130 const offsetPosition = elementPosition + window.pageYOffset - headerHeight; 131 132 window.scrollTo({ 133 top: offsetPosition, 134 behavior: 'smooth' 135 }); 136 } 137} 138 139 140function setCallStatus(status, event) { // <-- Added 'event' parameter 141 if (!currentLeadId) return; 142 143 // Fix: Remove 'active' from all, then add to the one clicked 144 document.querySelectorAll('.call-btn').forEach(btn => btn.classList.remove('active')); 145 146 // Safety check for the event 147 if (event && event.currentTarget) { 148 event.currentTarget.classList.add('active'); 149 } 150 151 // 2. UI Elements 152 const voicemailSec = document.getElementById('voicemailSection'); 153 const voicemailRecall = document.getElementById('voicemailRecallSection'); 154 const statsSec = document.getElementById('miniStats'); 155 const guideSec = document.getElementById('guideSection'); 156 const resultSec = document.getElementById('callResultSection'); 157 158 // 1. Update Database Status (Moved slightly down for logic flow) 159 let metaValue = (status === 'picked') ? 1 : 2; 160 updateLeadStatus(currentLeadId, metaValue); 161 162 // 3. Logic based on status 163 if (status === 'picked') { 164 // HIDE Voicemail, SHOW Guide 165 voicemailSec.style.display = 'none'; 166 voicemailRecall.style.display = 'none'; 167 statsSec.style.display = 'block'; 168 guideSec.style.display = 'block'; 169 170 // THIS IS NOW ACCESSIBLE because the code didn't crash above! 171 resultSec.style.display = 'block'; 172 173 // Reset Stepper to Step 1 174 updateStepperUI(1); 175 176 // Reset Accordions: Ensure ONLY Step 1 is open 177 document.querySelectorAll('.step').forEach(s => s.classList.remove('active')); 178 document.getElementById('step-1').classList.add('active'); 179 180 // INJECT DATA into script 181 const name = document.getElementById('bannerName').innerText; 182 const vehicle = document.getElementById('bannerVehicle').innerText; 183 const plate = document.getElementById('bannerPlate').innerText; 184 185 document.querySelectorAll('.dyn-client-name').forEach(el => el.innerText = name); 186 document.querySelectorAll('.dyn-client-vehicle').forEach(el => el.innerText = vehicle); 187 document.querySelectorAll('.dyn-client-plate').forEach(el => el.innerText = plate); 188 189 // Scroll to the start of the assistant 190 statsSec.scrollIntoView({ behavior: 'smooth' }); 191 192 } else if (status === 'voicemail') { 193 // SHOW Voicemail, HIDE Guide 194 statsSec.style.display = 'none'; 195 guideSec.style.display = 'none'; 196 resultSec.style.display = 'none'; 197 voicemailSec.style.display = 'block'; 198 voicemailRecall.style.display = 'block'; 199 200 const name = document.getElementById('bannerName').innerText; 201 const scriptName = document.querySelector('.banner-name-script'); 202 if (scriptName) scriptName.innerText = name; 203 204 voicemailSec.scrollIntoView({ behavior: 'smooth' }); 205 } 206 207 /* const bBar = document.getElementById('bottomBar'); 208 if (status === 'picked') { 209 // Show the bottom bar for live calls 210 if (bBar) bBar.classList.add('visible'); 211 } else { 212 // HIDE the bottom bar for voicemails to force the user to save the recall 213 if (bBar) bBar.classList.remove('visible'); 214 }*/ 215} 216 217/** 218 * Copy Plate Function 219 */ 220function copyPlate() { 221 const plate = document.querySelector('.dyn-client-plate').innerText; 222 navigator.clipboard.writeText(plate); 223 const btn = document.getElementById('copyPlateBtn'); 224 btn.innerText = "â Copié"; 225 setTimeout(() => { btn.innerText = "ð Copier l'immatriculation"; }, 2000); 226} 227 228// Helper to handle pill selection 229// Variables to hold selections until "Enregistrer" is clicked 230let selectedRecallDate = ""; 231let selectedRecallSlot = ""; 232 233/** 234 * Handles Date Selection (J+n or Date Picker) 235 */ 236 237function selectRecallDate(type, btn) { 238 let targetDate = new Date(); 239 240 if (type === 'J+1') targetDate.setDate(targetDate.getDate() + 1); 241 else if (type === 'J+3') targetDate.setDate(targetDate.getDate() + 3); 242 else if (type === 'J+7') targetDate.setDate(targetDate.getDate() + 7); 243 244 // FIX: Format to YYYY-MM-DD using local time (No UTC shift) 245 const year = targetDate.getFullYear();
246 const month = String(targetDate.getMonth() + 1).padStart(2, '0'); 247 const day = String(targetDate.getDate()).padStart(2, '0'); 248 selectedRecallDate = `${year}-${month}-${day}`; 249 250 document.querySelectorAll('#voicemailRecallSection .rp-pill').forEach(b => b.classList.remove('selected')); 251 btn.classList.add('selected'); 252 document.getElementById('vmCustomDate').value = ""; 253} 254/** 255 * Handles Time Slot Selection 256 */ 257function selectRecallSlot(val, btn) { 258 selectedRecallSlot = val; // Matin, Midi, Apres-Midi, Soir, Indifférent 259 260 // UI: Update Pills 261 document.querySelectorAll('#voicemailRecallSection .rp-pill-green').forEach(b => b.classList.remove('selected')); 262 btn.classList.add('selected'); 263} 264 265 266/** 267 * Saves everything to Database on click 268 */ 269function saveRecallAndNext() { 270 if (!currentLeadId) return; 271 272 // Check if user used the date picker instead of J+ pills 273 const customDate = document.getElementById('vmCustomDate').value; 274 const finalDate = customDate ? customDate : selectedRecallDate; 275 276 if (!finalDate || !selectedRecallSlot) { 277 alert("â ï¸ Choisissez au minimum une échéance (J+1, J+3, J+7 ou une date)."); 278 return; 279 } 280 281 jQuery.ajax({ 282 type: "POST", 283 url: "/wp-content/themes/biomotors-child/core/ajax/valideContactLead.php", 284 data: { 285 lead_id: currentLeadId, 286 status_value: 2, // En attente 287 lead_date_rappel: finalDate, 288 lead_creneau_rappel: selectedRecallSlot 289 }, 290 success: function() { 291 // --- date rappel UI UPDATE BLOCK --- 292 const row = document.querySelector(`tr[onclick*="selectLead(${currentLeadId}"]`); 293 if (row) { 294 const badgeCell = row.cells[7]; // This is the 'Statut' column 295 296 // Format the date from YYYY-MM-DD to DD/MM 297 const dateParts = finalDate.split('-'); 298 const formattedDate = dateParts[2] + '/' + dateParts[1]; 299 300 // Update the table cell immediately 301 badgeCell.innerHTML = `<span class="badge-pending">ð Rappel (${formattedDate})</span>`; 302 } 303 // --- date rappel UI UPDATE BLOCK --- 304 // 1. Hide the form 305 document.getElementById('vmRpForm').style.display = 'none'; 306 307 // 2. Set details in the confirmation screen 308 document.getElementById('vmRpConfDetail').textContent = finalDate + ' · ' + selectedRecallSlot; 309 310 // 3. Show the confirmation screen 311 document.getElementById('vmRpConfirmation').style.display = 'block'; 312 313 // 4. Wait 2 seconds, then go to next lead 314 setTimeout(() => { 315 // Reset for next lead 316 document.getElementById('vmRpForm').style.display = 'block'; 317 document.getElementById('vmRpConfirmation').style.display = 'none'; 318 319 // SAFETY CHECK: Only clear the note if the element actually exists 320 const noteField = document.getElementById('vmRpNote'); 321 if (noteField) noteField.value = ""; 322 323 // MANDATORY: Clear the "Répondeur" button active state 324 // This ensures the next lead starts with no buttons highlighted 325 document.querySelectorAll('.call-btn').forEach(btn => btn.classList.remove('active')); 326 nextLead(); 327 }, 2000); 328 } 329 }); 330} 331 332/** 333 * calc economy 334 */ 335 336let calcMode = 'pleins'; 337let currentSavingsMonth = 67; 338let currentSavingsYear = 804; 339 340function switchCalcMode(mode, btn) { 341 calcMode = mode; 342 document.querySelectorAll('.tabs .tab-btn').forEach(b => b.classList.remove('active')); 343 btn.classList.add('active'); 344 document.getElementById('calcPleins').style.display = mode === 'pleins' ? 'block' : 'none'; 345 document.getElementById('calcKm').style.display = mode === 'km' ? 'block' : 'none'; 346 updateCalc(); 347} 348 349 350function updateCalc() { 351 // Basic assumptions used in the template 352 const conso = 7.5; 353 const reservoir = parseFloat(document.getElementById('tankSize').value) || 50; 354 const surcon = 1.15; 355 // const pSP = 1.70; 356 // const pE85 = 0.75; 357 const pSP = parseFloat(bmSettings.priceSP95) || 1.70; 358 const pE85 = parseFloat(bmSettings.priceE85) || 0.75; 359 360 const msE85 = document.getElementById('msE85'); 361 if (msE85) { 362 msE85.textContent = pE85.toFixed(2).replace('.', ',') + ' â¬/L'; 363 } 364 365 366 let lpm; // Liters per month 367 368 if (calcMode === 'pleins') { 369 const p = parseFloat(document.getElementById('pleinsRange').value); 370 const pLabel = p % 1 === 0 ? p.toFixed(0) : p.toFixed(1).replace('.', ','); 371 document.getElementById('pleinsBig').textContent = pLabel + (p > 1 ? ' pleins / mois' : ' plein / mois'); 372 lpm = p * reservoir; 373 } else {
374 const km = parseInt(document.getElementById('kmRange').value); 375 document.getElementById('kmVal').textContent = km.toLocaleString('fr-FR'); 376 lpm = (km / 12 / 100) * conso; 377 } 378 379 // Calculation: Savings = Cost SP95 - Cost E85 (with 15% overconsumption) 380 currentSavingsMonth = Math.round(lpm * pSP - (lpm * surcon * pE85)); 381 currentSavingsYear = currentSavingsMonth * 12; 382 383 // Update Step 3 Display 384 document.getElementById('savMonth').textContent = currentSavingsMonth + ' â¬'; 385 document.getElementById('savYear').textContent = currentSavingsYear.toLocaleString('fr-FR') + ' â¬'; 386 document.getElementById('sav5Y').textContent = (currentSavingsYear * 5).toLocaleString('fr-FR') + ' â¬'; 387 388 // Update Dynamic text in the scripts 389 document.querySelectorAll('.dynSM').forEach(e => e.textContent = currentSavingsMonth + ' â¬'); 390 document.querySelectorAll('.dynSY').forEach(e => e.textContent = currentSavingsYear.toLocaleString('fr-FR') + ' â¬'); 391 392 // Update Mini-Stats Bar at the top 393 const msEco = document.getElementById('msEco'); 394 if(msEco) msEco.textContent = currentSavingsMonth + ' â¬/mois'; 395 396 updateMargin(); 397 398 399 //If it's a Leclerc account, update Step 4 and Step 5 boxes 400 if (typeof updateLeclercPriceDisplay === 'function') { 401 updateLeclercPriceDisplay(); 402 } 403 404 //Update the Financing timeline specifically 405 if (typeof updateFinancingDisplay === 'function') { 406 updateFinancingDisplay(); 407 } 408} 409 410// Initialize calc on load 411document.addEventListener('DOMContentLoaded', () => { 412 // 1. If the calculator exists, run the fuel savings math (Step 3) 413 if(document.getElementById('pleinsRange')) { 414 updateCalc(); 415 } 416 417 // 2. THE FIX: Initialize the Step 4 Pricing 418 // This forces the script to calculate the 25% discount immediately on load 419 updatePrices(); 420 updateMargin(); 421}); 422 423 424/** 425 * NEW HELPER: This creates the 25% and 40% prices on the fly 426 * using the prices you set in the WordPress Back Office. 427 */ 428function getDynamicPricing() { 429 const baseConnect = parseFloat(bmSettings.priceConnect) || 599.17; 430 const baseAir = parseFloat(bmSettings.priceAir) || 798.17; 431 432 return { 433 connect: { 434 pro: baseConnect, 435 r25: baseConnect * 0.75, // calculates -25% 436 r40: baseConnect * 0.60 // calculates -40% 437 }, 438 air: { 439 pro: baseAir, 440 r25: baseAir * 0.75, 441 r40: baseAir * 0.60 442 } 443 }; 444} 445 446let currentRemise = 25; 447let currentProduct = 'connect'; 448let currentPTTC = 959; 449let currentROI = 14; 450 451function switchRemise(val) { 452 currentRemise = val; 453 document.getElementById('r25btn').classList.toggle('active', val === 25); 454 document.getElementById('r40btn').classList.toggle('active', val === 40); 455 document.getElementById('r40note').style.display = (val === 40) ? 'block' : 'none'; 456 updatePrices(); 457 updateMargin(); 458} 459 460function selectProduct(card, type) { 461 currentProduct = type; 462 document.querySelectorAll('.product-card').forEach(c => c.classList.remove('selected')); 463 card.classList.add('selected'); 464 updateMargin(); 465} 466 467function updatePrices() { 468 469 const prConnect = document.getElementById('prConnect'); 470 if (!prConnect) return; 471 472 const prices = getDynamicPricing(); // Call the dynamic function 473 const k = (currentRemise === 40) ? 'r40' : 'r25'; 474 475 document.getElementById('prConnect').textContent = prices.connect[k].toFixed(2).replace('.', ',') + ' ⬠HT'; 476 document.getElementById('prAir').textContent = prices.air[k].toFixed(2).replace('.', ',') + ' ⬠HT'; 477} 478 479function updateMargin() { 480 const inputPose = document.getElementById('inputPose'); 481 if (!inputPose) return; 482 483 const prices = getDynamicPricing(); // Call the dynamic function 484 const k = (currentRemise === 40) ? 'r40' : 'r25'; 485 486 const pa = prices[currentProduct][k]; 487 const pose = parseFloat(document.getElementById('inputPose').value) || 0; 488 const marge = parseFloat(document.getElementById('margeRange').value) || 0; 489 490 document.getElementById('margeVal').textContent = marge + ' ⬠HT'; 491 document.getElementById('dispPA').textContent = pa.toFixed(2).replace('.', ',') + ' â¬'; 492 493 const totalHT = pa + pose + marge; 494 currentPTTC = Math.round(totalHT * 1.2); // +20% TVA 495 496 // ROI calculation: (Total Cost / Monthly Savings from Step 3) 497 currentROI = (currentSavingsMonth > 0) ? Math.ceil(currentPTTC / currentSavingsMonth) : 0; 498 499 document.getElementById('mCost').textContent = Math.round(pa + pose) + ' â¬'; 500 document.getElementById('mPrice').textContent = currentPTTC.toLocaleStr
500ing('fr-FR') + ' â¬'; 501 document.getElementById('mROI').textContent = currentROI + ' mois'; 502 503 // Update the visual scripts for the technician 504 document.querySelectorAll('.dynPTTC').forEach(e => e.textContent = currentPTTC.toLocaleString('fr-FR') + ' â¬'); 505 document.querySelectorAll('.dynROI').forEach(e => e.textContent = currentROI + ' mois'); 506} 507// --- NEW GLOBALS FOR CALL RESULT SECTION ONLY --- 508let selectedResultDateDirect = ""; 509let selectedResultSlotDirect = ""; 510let selectedCallResultValue = ""; 511 512/** 513 * Handles the selection of the main call result buttons 514 */ 515function setCallResult(result, btn) { 516 selectedCallResultValue = result; // 'intéressé', 'à recontacter', 'pas intéressé' 517 518 // UI: Update button visual state 519 document.querySelectorAll('#callResultSection .result-btn').forEach(b => b.classList.remove('chosen')); 520 btn.classList.add('chosen'); 521 522 const planner = document.getElementById('recallPlanner'); 523 524 if (result === 'à recontacter') { 525 // Show planner for "à recontacter" 526 planner.style.display = 'block'; 527 planner.scrollIntoView({ behavior: 'smooth', block: 'center' }); 528 } else { 529 // Hide planner and save immediately for "intéressé" or "pas intéressé" 530 planner.style.display = 'none'; 531 saveResultAndNext(); 532 } 533} 534 535/** 536 * Handles Date Selection for the NEW Result Planner 537 */ 538 539function selectRecallDateDirect(type, btn) { 540 let targetDate = new Date(); 541 542 if (type === 'J+1') targetDate.setDate(targetDate.getDate() + 1); 543 else if (type === 'J+3') targetDate.setDate(targetDate.getDate() + 3); 544 else if (type === 'J+7') targetDate.setDate(targetDate.getDate() + 7); 545 546 // FIX: Format to YYYY-MM-DD using local time 547 const year = targetDate.getFullYear();
548 const month = String(targetDate.getMonth() + 1).padStart(2, '0'); 549 const day = String(targetDate.getDate()).padStart(2, '0'); 550 selectedResultDateDirect = `${year}-${month}-${day}`; 551 552 document.querySelectorAll('#callResultSection .rp-pill').forEach(b => b.classList.remove('selected')); 553 btn.classList.add('selected'); 554 document.getElementById('rpCustomDate').value = ""; 555} 556 557/** 558 * Handles Slot Selection for the NEW Result Planner 559 */ 560function selectRecallSlotDirect(val, btn) { 561 selectedResultSlotDirect = val; 562 document.querySelectorAll('#callResultSection .rp-pill-green').forEach(b => b.classList.remove('selected')); 563 btn.classList.add('selected'); 564} 565 566/** 567 * Logic to save the result and move to next lead 568 */ 569function saveResultAndNext() { 570 if (!currentLeadId) return; 571 572 let leadStatus; 573 let finalDate = ""; 574 let finalSlot = ""; 575 let finalNote = document.getElementById('rpNote').value; 576 577 // Mapping: intéressé/pas intéressé -> Status 1 | à recontacter -> Status 2 578 if (selectedCallResultValue === 'à recontacter') { 579 leadStatus = 2; 580 const customDate = document.getElementById('rpCustomDate').value; 581 finalDate = customDate ? customDate : selectedResultDateDirect; 582 finalSlot = selectedResultSlotDirect; 583 584 if (!finalDate || !finalSlot) { 585 alert("â ï¸ Veuillez sélectionner une échéance et un créneau."); 586 return; 587 } 588 } else { 589 leadStatus = 1; 590 } 591 592 jQuery.ajax({ 593 type: "POST", 594 url: "/wp-content/themes/biomotors-child/core/ajax/valideContactLead.php", 595 data: { 596 lead_id: currentLeadId, 597 status_value: leadStatus, // lead_contacte 598 lead_resultat_appel: selectedCallResultValue, // New Meta Key 599 lead_date_rappel: finalDate, 600 lead_creneau_rappel: finalSlot, 601 lead_note_rappel: finalNote // New Meta Key 602 }, 603 success: function() { 604 605 const row = document.querySelector(`tr[onclick*="selectLead(${currentLeadId}"]`); 606 if (row) { 607 const badgeCell = row.cells[7]; // The Status column 608 // if (leadStatus === 2) { 609 // // Format the date for the badge (YYYY-MM-DD to DD/MM) 610 // // const dateParts = finalDate.split('-'); 611 // // const formattedDate = dateParts[2] + '/' + dateParts[1]; 612 // // badgeCell.innerHTML = `<span class="badge-pending">En attente (${formattedDate})</span>`; 613 // badgeCell.innerHTML = '<span class="badge-pending">En attente</span>'; 614 // } else { 615 // badgeCell.innerHTML = '<span class="badge-contacted">Contacté</span>'; 616 // } 617 if (leadStatus === 2) { 618 const dateParts = finalDate.split('-'); 619 const formattedDate = dateParts[2] + '/' + dateParts[1]; 620 badgeCell.innerHTML = `<span class="badge-pending">ð à recontacter (${formattedDate})</span>`; 621 } else if (selectedCallResultValue === 'intéressé') { 622 badgeCell.innerHTML = '<span class="badge-contacted">â Intéressé</span>'; 623 } else if (selectedCallResultValue === 'pas intéressé') { 624 badgeCell.innerHTML = '<span class="badge-new" style="background:#ffebee; color:#c62828; border:1px solid #ef5350;">Pas intéressé</span>'; 625 } else { 626 badgeCell.innerHTML = '<span class="badge-contacted">Contacté</span>'; 627 } 628 629 const noteInput = row.cells[8].querySelector('.lead-comment-input'); 630 if (noteInput) { 631 noteInput.value = finalNote; 632 } 633 } 634 635 if (leadStatus === 2) { 636 // Show confirmation animation 637 document.getElementById('rpForm').style.display = 'none'; 638 document.getElementById('rpConfDetail').textContent = finalDate + ' · ' + finalSlot; 639 document.getElementById('rpConfirmation').style.display = 'block'; 640 641 setTimeout(() => { 642 resetResultSection(); 643 nextLead(); 644 }, 2000); 645 } else { 646 resetResultSection(); 647 nextLead(); 648 } 649 } 650 }); 651} 652 653function resetResultSection() { 654 document.getElementById('callResultSection').style.display = 'none'; 655 document.getElementById('recallPlanner').style.display = 'none'; 656 document.getElementById('rpForm').style.display = 'block'; 657 document.getElementById('rpConfirmation').style.display = 'none'; 658 document.getElementById('rpNote').value = ""; 659 selectedCallResultValue = ""; 660 selectedResultDateDirect = ""; 661 selectedResultSlotDirect = ""; 662} 663 664/** 665 * Updates the horizontal stepper UI based on the active step number 666 */ 667function updateStepperUI(activeStep) { 668 for (let i = 1; i <= 7; i++) { 669 const item = document.getElementById('st-' + i); 670 if (!item) continue; 671 672 item.classList.remove('active', 'completed'); 673 674 if (i === activeStep) { 675 item.classList.add('active'); 676 } else if (i < activeStep) { 677 item.classList.add('completed'); 678 } 679 } 680} 681 682/** 683 * Allows clicking a stepper circle to jump directly to a step 684 */ 685function jumpToStep(n) { 686 const targetStep = document.getElementById('step-' + n);
687 if (targetStep) { 688 const header = targetStep.querySelector('.step-header'); 689 toggleStep(header); 690 } 691} 692 693/** 694 * stop the row click from triggering when clicking the note field 695 */ 696function stopNotePropagation(e) { 697 if (e && e.stopPropagation) { 698 e.stopPropagation(); 699 } 700} 701 702 703function saveQuickNote(leadId, element) { 704 const noteValue = element.value; 705 const wrapper = element.closest('.comment-wrapper'); 706 707 jQuery.ajax({ 708 type: "POST", 709 url: "/wp-content/themes/biomotors-child/core/ajax/valideContactLead.php", 710 data: { 711 lead_id: leadId, 712 lead_note_rappel: noteValue 713 }, 714 success: function() { 715 // VISUAL FEEDBACK 716 wrapper.classList.add('comment-saved'); 717 718 // Remove the effect after 2 seconds 719 setTimeout(() => { 720 wrapper.classList.remove('comment-saved'); 721 }, 2000); 722 }, 723 error: function() { 724 element.style.borderColor = "red"; 725 } 726 }); 727} 728 729// document.addEventListener('DOMContentLoaded', () => { 730// const toggleBtn = document.getElementById('darkModeToggle'); 731// const themeIcon = document.getElementById('themeIcon'); 732// const themeText = document.getElementById('themeText'); 733// const body = document.body; 734// 735// // 1. Check for saved preference 736// const currentTheme = localStorage.getItem('theme'); 737// if (currentTheme === 'dark') { 738// body.classList.add('dark-mode'); 739// updateToggleUI(true); 740// } 741// 742// // 2. Listen for clicks 743// toggleBtn.addEventListener('click', () => { 744// body.classList.toggle('dark-mode'); 745// 746// let isDark = body.classList.contains('dark-mode'); 747// localStorage.setItem('theme', isDark ? 'dark' : 'light'); 748// 749// updateToggleUI(isDark); 750// }); 751// 752// function updateToggleUI(isDark) { 753// themeIcon.innerText = isDark ? 'âï¸' : 'ð'; 754// themeText.innerText = isDark ? 'Mode Clair' : 'Mode Sombre'; 755// } 756// }); 757 758document.addEventListener('DOMContentLoaded', function() { 759 const btn = document.getElementById('darkModeToggle'); 760 if (!btn) return; // Exit if button is hidden (not itdesk) 761 762 const icon = document.getElementById('themeIcon'); 763 const text = document.getElementById('themeText'); 764 const body = document.body; 765 766 btn.addEventListener('click', function() { 767 // Toggle the class 768 body.classList.toggle('dark-mode'); 769 770 // Update Button Appearance based on the NEW state 771 if (body.classList.contains('dark-mode')) { 772 icon.textContent = 'âï¸'; 773 text.textContent = 'Mode Clair'; 774 btn.style.background = 'var(--white)'; 775 } else { 776 icon.textContent = 'ð'; 777 text.textContent = 'Mode Sombre'; 778 btn.style.background = 'var(--bm-green)'; 779 } 780 }); 781}); 782 783function toggleNote(btn) { 784 const wrapper = btn.closest('.comment-wrapper'); 785 const textarea = wrapper.querySelector('.lead-comment-input'); 786 787 textarea.classList.toggle('is-expanded'); 788 789 if (textarea.classList.contains('is-expanded')) { 790 btn.textContent = 'Réduire'; 791 } else { 792 btn.textContent = 'Voir +'; 793 } 794} 795//////LECLERC 796// let currentLeclercPrice = 690; 797 798/** 799 * Handles product selection for Leclerc users 800 * @param {HTMLElement} element - The clicked product card 801 * @param {string} type - 'standard' or 'air' 802 * @param {number} price - 690 or 890 803 */ 804function selectProductLeclerc(element, type, price) { 805 const step4 = document.getElementById('step-4'); 806 const cards = step4.querySelectorAll('.product-card'); 807 cards.forEach(card => card.classList.remove('selected')); 808 element.classList.add('selected'); 809 currentLeclercPrice = price; 810 updateLeclercPriceDisplay(); 811} 812// function updateLeclercPriceDisplay() { 813// // Grab the monthly savings from Step 3 814// const savingsElement = document.getElementById('savMonth'); 815// const monthlySavings = parseInt(savingsElement.textContent.replace(/[^0-9]/g, '')) || 1; 816// 817// // Calculate the new ROI 818// const roiMonths = Math.ceil(currentLeclercPrice / monthlySavings); 819// 820// //Update the Summary Boxes 821// const priceBox = document.getElementById('mPriceLeclerc'); 822// const roiBox = document.getElementById('mROILeclerc'); 823// 824// if (priceBox) priceBox.textContent = currentLeclercPrice + ' â¬'; 825// if (roiBox) roiBox.textContent = roiMonths + ' mois'; 826// 827// //Update the Pitch text 828// document.querySelectorAll('.dynPTTC').forEach(el => el.textContent = currentLeclercPrice + ' â¬'); 829// document.querySelectorAll('.dynROI').forEach(el => el.textContent = roiMonths + ' mois'); 830// 831// if (typeof updateFinancingDisplay === 'function') updateFinancingDisplay(); 832// } 833 834function updateLeclercPriceDisplay() { 835 const savingsElement = document.getElementById('savMonth'); 836 const monthlySavings = parseInt(savingsElement.textContent.replace(/[^0-9]/g, '')) || 1; 837 838 // ROI calculation based on whichever price is currently in the "bucket" 839 const roiMonths = Math.ceil(currentLeclercPrice / monthlySavings); 840 841 // Update Step 4 Summary Boxes 842 const priceBox = document.getElementById('mPriceLeclerc'); 843 const roiBox = document.getElementById('mROILeclerc'); 844 845 if (priceBox) priceBox.textContent = currentLeclercPrice + ' â¬'; 846 if (roiBox) roiBox.textContent = roiMonths + ' mois'; 847 848 // Update all dynamic text spans (The Pitch) 849 document.querySelectorAll('.dynPTTC').forEach(el => el.textContent = currentLeclercPrice + ' â¬'); 850 document.querySelectorAll('.dynROI').forEach(el => el.textContent = roiMonths + ' mois'); 851 852 // IMPORTANT: This pushes the new price into Step 5 853 if (typeof updateFinancingDisplay === 'function') { 854 updateFinancingDisplay(); 855 } 856} 857 858let currentFinNb = 10; // Default to 10x 859const LECLERC_FINANCING = {
860 690: { 4: 172.50, 10: 69, 12: 57.50 }, 861 890: { 4: 222.50, 10: 89, 12: 74.17 } 862}; 863 864function selectFinancing(nb, card) { 865 currentFinNb = nb; 866 867 // 1. UI: Handle the visual highlight 868 const grid = document.getElementById('finGrid'); 869 if (grid) { 870 grid.querySelectorAll('.fin-card').forEach(c => { 871 // Reset all cards to the "unselected" style 872 c.classList.remove('selected'); 873 c.style.background = "rgba(82,183,136,0.04)"; 874 c.style.border = "2px solid rgba(82,183,136,0.1)"; 875 c.style.boxShadow = "none"; 876 }); 877 878 // Apply the "selected" style to the clicked card 879 card.classList.add('selected'); 880 card.style.background = "rgba(82,183,136,0.1)"; 881 card.style.border = "2px solid #52b788"; 882 card.style.boxShadow = "0 0 0 1px #52b788"; 883 } 884 885 // 2. Update the math and text 886 updateFinancingDisplay(); 887} 888 889let currentLeclercPrice = parseFloat(bmSettings.priceLeclercStandard) || 690; 890function updateFinancingDisplay() { 891 const finGrid = document.getElementById('finGrid'); 892 if (!finGrid) return; 893 894 const savingsText = document.getElementById('savMonth').textContent; 895 const monthlySavings = parseInt(savingsText.replace(/[^0-9]/g, '')) || 0; 896 897 // A. CALCULATE installments on the fly 898 const pay4 = currentLeclercPrice / 4; 899 const pay10 = currentLeclercPrice / 10; 900 const pay12 = currentLeclercPrice / 12; 901 902 // B. Update the Grid monthly values (the text inside the cards) 903 document.getElementById('fin0m').textContent = currentLeclercPrice + ' â¬'; 904 document.getElementById('fin4m').textContent = pay4.toFixed(2).replace('.', ',') + ' â¬/mois'; 905 document.getElementById('fin10m').textContent = pay10.toFixed(2).replace('.', ',') + ' â¬/mois'; 906 document.getElementById('fin12m').textContent = pay12.toFixed(2).replace('.', ',') + ' â¬/mois'; 907 908 // C. Get the payment for the CURRENTLY SELECTED term (4, 10, or 12) 909 let monthlyPayment = 0; 910 if (currentFinNb === 4) monthlyPayment = pay4; 911 else if (currentFinNb === 10) monthlyPayment = pay10; 912 else if (currentFinNb === 12) monthlyPayment = pay12; 913 914 // D. Update Bilan Mensuel (Timeline) 915 const detailBlock = document.getElementById('finDetailBlock'); 916 if (currentFinNb === 0) { 917 detailBlock.style.display = 'none'; 918 } else { 919 detailBlock.style.display = 'block'; 920 const gainDuring = monthlySavings - monthlyPayment; 921 922 document.getElementById('finNbLabel').textContent = currentFinNb + 'Ã'; 923 document.getElementById('finDuringVal').textContent = (gainDuring >= 0 ? '+' : '') + Math.round(gainDuring) + ' â¬/mois'; 924 document.getElementById('finAfterVal').textContent = '+' + monthlySavings + ' â¬/mois'; 925 926 document.getElementById('finDuringSub').textContent = gainDuring >= 0 ? "Vous gagnez déjà de l'argent" : "Un petit effort temporaire"; 927 document.getElementById('finDuringVal').style.color = gainDuring >= 0 ? "#f4a261" : "#e74c3c"; 928 } 929 930 // E. Update Pitch Text at the bottom 931 const pitchNb = document.getElementById('finPitchNb'); 932 const pitchMonthly = document.getElementById('finPitchMonthly'); 933 const pitchGain = document.getElementById('finPitchGain'); 934 935 if (pitchNb) pitchNb.textContent = currentFinNb === 0 ? "comptant" : currentFinNb + " fois"; 936 if (pitchMonthly) pitchMonthly.textContent = (currentFinNb === 0 ? currentLeclercPrice : monthlyPayment.toFixed(2).replace('.', ',')) + " â¬"; 937 if (pitchGain) { 938 const totalGain = Math.round(monthlySavings - monthlyPayment); 939 pitchGain.textContent = (totalGain >= 0 ? totalGain : 0) + " â¬"; 940 } 941} 942// function updateFinancingDisplay() { 943// // A. Get current savings from Step 3 944// const savingsText = document.getElementById('savMonth').textContent; 945// const monthlySavings = parseInt(savingsText.replace(/[^0-9]/g, '')) || 0; 946// 947// // B. Get monthly payment for the selected product and term 948// const productTerms = LECLERC_FINANCING[currentLeclercPrice]; 949// const monthlyPayment = currentFinNb === 0 ? 0 : productTerms[currentFinNb]; 950// 951// // C. Calculate Grid monthly values (so they update if product changes) 952// document.getElementById('fin0m').textContent = currentLeclercPrice + ' â¬'; 953// document.getElementById('fin4m').textContent = productTerms[4].toFixed(2).replace('.', ',') + ' â¬/mois'; 954// document.getElementById('fin10m').textContent = productTerms[10].toFixed(2).replace('.', ',') + ' â¬/mois'; 955// document.getElementById('fin12m').textContent = productTerms[12].toFixed(2).replace('.', ',') + ' â¬/mois'; 956// 957// // D. Update Bilan Mensuel (Timeline) 958// const detailBlock = document.getElementById('finDetailBlock'); 959// if (currentFinNb === 0) { 960// detailBlock.style.display = 'none'; 961// } else { 962// detailBlock.style.display = 'block'; 963// const gainDuring = monthlySavings - monthlyPayment; 964// 965// document.getElementById('finNbLabel').textContent = currentFinNb + 'Ã'; 966// document.getElementById('finDuringVal').textContent = (gainDuring >= 0 ? '+' : '') + Math.round(gainDuring) + ' â¬/mois'; 967// document.getElementById('finAfterVal').textContent = '+' + monthlySavings + ' â¬/mois'; 968// 969// // Update labels based on gain 970// document.getElementById('finDuringSub').textContent = gainDuring >= 0 ? "Vous gagnez déjà de l'argent" : "Un petit effort temporaire";
971// document.getElementById('finDuringVal').style.color = gainDuring >= 0 ? "#f4a261" : "#e74c3c"; 972// } 973// 974// // E. Update Pitch Text 975// const pitchNb = document.getElementById('finPitchNb'); 976// const pitchMonthly = document.getElementById('finPitchMonthly'); 977// const pitchGain = document.getElementById('finPitchGain'); 978// 979// if (pitchNb) pitchNb.textContent = currentFinNb === 0 ? "comptant" : currentFinNb + " fois"; 980// if (pitchMonthly) pitchMonthly.textContent = (currentFinNb === 0 ? currentLeclercPrice : monthlyPayment.toFixed(2).replace('.', ',')) + " â¬"; 981// if (pitchGain) { 982// const totalGain = Math.round(monthlySavings - monthlyPayment); 983// pitchGain.textContent = (totalGain >= 0 ? totalGain : 0) + " â¬"; 984// } 985// }
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.