Spaces:
Running
Running
| <html lang="fr"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"> | |
| <title>TIE - Combien gagnez-vous vraiment ?</title> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <link rel="preconnect" href="https://fonts.googleapis.com"> | |
| <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> | |
| <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet"> | |
| <style> | |
| body { | |
| font-family: 'Inter', ui-sans-serif, system-ui, sans-serif; | |
| } | |
| input[type=number]::-webkit-inner-spin-button, | |
| input[type=number]::-webkit-outer-spin-button { | |
| -webkit-appearance: none; | |
| margin: 0; | |
| } | |
| input[type=number] { | |
| -moz-appearance: textfield; | |
| } | |
| .step-container { | |
| display: none; | |
| animation: fadeIn 0.3s ease-out; | |
| } | |
| .step-container.active { | |
| display: block; | |
| } | |
| .analysis-container { | |
| display: none; | |
| animation: fadeIn 0.35s ease-out; | |
| } | |
| .analysis-container.active { | |
| display: block; | |
| } | |
| .info-box { | |
| display: none; | |
| animation: fadeIn 0.25s ease-out; | |
| } | |
| .info-box.active { | |
| display: block; | |
| } | |
| @keyframes fadeIn { | |
| from { opacity: 0; transform: translateY(8px); } | |
| to { opacity: 1; transform: translateY(0); } | |
| } | |
| </style> | |
| </head> | |
| <body class="bg-[#0b0b0f] text-white antialiased min-h-screen"> | |
| <header class="sticky top-0 z-50 bg-[#0b0b0f]/90 backdrop-blur-xl border-b border-white/5"> | |
| <div class="max-w-3xl mx-auto px-5 py-4 flex items-center justify-between"> | |
| <a href="index.html" class="text-sm text-white/60 hover:text-white transition flex items-center gap-2"> | |
| <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m15 18-6-6 6-6"/></svg> | |
| Retour | |
| </a> | |
| <div class="text-lg font-semibold tracking-tight text-white">TIE</div> | |
| </div> | |
| </header> | |
| <main class="max-w-3xl mx-auto px-5 pt-6 pb-28"> | |
| <div class="text-[11px] text-white/50 mb-3 uppercase tracking-wide">Simulation rapide</div> | |
| <h1 class="text-[26px] leading-tight font-semibold text-white mb-3">Combien gagnez-vous vraiment ?</h1> | |
| <p class="text-sm text-white/60 mb-6">En 30 secondes, estimez la rentabilité réelle d'une course.</p> | |
| <div class="mb-6"> | |
| <div class="flex justify-between items-center mb-2"> | |
| <span class="text-xs text-white/45">Étape <span id="stepLabel">1</span> sur 4</span> | |
| </div> | |
| <div class="w-full h-1.5 bg-white/10 rounded-full overflow-hidden"> | |
| <div id="progressFill" class="h-full bg-emerald-500 rounded-full transition-all duration-300 ease-out" style="width: 25%"></div> | |
| </div> | |
| </div> | |
| <!-- Step 1 --> | |
| <div id="step1" class="step-container active"> | |
| <div class="bg-[#151821] border border-white/10 rounded-2xl p-5"> | |
| <label class="block text-sm text-white/80 mb-1.5 font-medium">Montant reçu pour la course (€)</label> | |
| <p class="text-[11px] text-white/40 mb-3"> | |
| Indiquez le montant net versé par la plateforme ou le montant facturé si c’est un client privé. | |
| </p> | |
| <input | |
| type="number" | |
| id="price" | |
| placeholder="Ex : 15" | |
| step="0.01" | |
| class="w-full rounded-xl bg-black/40 border border-white/10 px-4 py-3 text-white text-sm outline-none focus:border-emerald-500/50 transition mb-4" | |
| > | |
| <button id="btnStep1Next" class="w-full py-3 rounded-xl bg-emerald-500 text-black font-medium text-sm hover:opacity-90 transition"> | |
| Suivant | |
| </button> | |
| </div> | |
| </div> | |
| <!-- Step 2 --> | |
| <div id="step2" class="step-container"> | |
| <div class="bg-[#151821] border border-white/10 rounded-2xl p-5"> | |
| <label class="block text-sm text-white/80 mb-4 font-medium">Distance et temps total</label> | |
| <div class="space-y-3 mb-4"> | |
| <div> | |
| <label class="block text-xs text-white/60 mb-1.5">Distance totale parcourue (km)</label> | |
| <p class="text-[11px] text-white/40 mb-1.5">Inclure l’approche + la course</p> | |
| <input | |
| type="number" | |
| id="distance" | |
| placeholder="Ex : 8" | |
| step="0.1" | |
| class="w-full rounded-xl bg-black/40 border border-white/10 px-4 py-3 text-white text-sm outline-none focus:border-emerald-500/50 transition" | |
| > | |
| </div> | |
| <div> | |
| <label class="block text-xs text-white/60 mb-1.5">Temps total mobilisé (minutes)</label> | |
| <p class="text-[11px] text-white/40 mb-1.5">Inclure l’approche, l’attente et le trajet</p> | |
| <input | |
| type="number" | |
| id="time" | |
| placeholder="Ex : 20" | |
| class="w-full rounded-xl bg-black/40 border border-white/10 px-4 py-3 text-white text-sm outline-none focus:border-emerald-500/50 transition" | |
| > | |
| </div> | |
| </div> | |
| <div class="flex gap-3"> | |
| <button id="btnStep2Back" class="w-1/3 py-3 rounded-xl bg-white/5 text-white border border-white/10 text-sm font-medium hover:bg-white/10 transition"> | |
| Retour | |
| </button> | |
| <button id="btnStep2Next" class="w-2/3 py-3 rounded-xl bg-emerald-500 text-black font-medium text-sm hover:opacity-90 transition"> | |
| Suivant | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Step 3 --> | |
| <div id="step3" class="step-container"> | |
| <div class="bg-[#151821] border border-white/10 rounded-2xl p-5"> | |
| <label class="block text-sm text-white/80 mb-4 font-medium">Plateforme utilisée</label> | |
| <div class="grid grid-cols-2 gap-2 mb-4" id="platformButtons"> | |
| <button type="button" data-value="uber" class="platform-btn flex-1 text-xs py-2.5 rounded-full border border-white/10 text-white/60 bg-transparent transition hover:border-white/30">Uber</button> | |
| <button type="button" data-value="bolt" class="platform-btn flex-1 text-xs py-2.5 rounded-full border border-white/10 text-white/60 bg-transparent transition hover:border-white/30">Bolt</button> | |
| <button type="button" data-value="heetch" class="platform-btn flex-1 text-xs py-2.5 rounded-full border border-white/10 text-white/60 bg-transparent transition hover:border-white/30">Heetch</button> | |
| <button type="button" data-value="direct" class="platform-btn flex-1 text-xs py-2.5 rounded-full border border-white/10 text-white/60 bg-transparent transition hover:border-white/30">Client privé</button> | |
| </div> | |
| <input type="hidden" id="platform" value=""> | |
| <div class="flex gap-3"> | |
| <button id="btnStep3Back" class="w-1/3 py-3 rounded-xl bg-white/5 text-white border border-white/10 text-sm font-medium hover:bg-white/10 transition"> | |
| Retour | |
| </button> | |
| <button id="btnStep3Next" class="w-2/3 py-3 rounded-xl bg-emerald-500 text-black font-medium text-sm hover:opacity-90 transition disabled:opacity-40 disabled:cursor-not-allowed" disabled> | |
| Suivant | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Step 4 --> | |
| <div id="step4" class="step-container"> | |
| <div class="bg-[#151821] border border-white/10 rounded-2xl p-5"> | |
| <label class="block text-sm text-white/80 mb-4 font-medium">Type de véhicule</label> | |
| <div class="grid grid-cols-3 gap-2 mb-4" id="vehicleButtons"> | |
| <button type="button" data-value="thermique" class="vehicle-btn flex-1 text-xs py-2.5 rounded-full border border-white/10 text-white/60 bg-transparent transition hover:border-white/30">Thermique</button> | |
| <button type="button" data-value="hybride" class="vehicle-btn flex-1 text-xs py-2.5 rounded-full border border-white/10 text-white/60 bg-transparent transition hover:border-white/30">Hybride</button> | |
| <button type="button" data-value="electrique" class="vehicle-btn flex-1 text-xs py-2.5 rounded-full border border-white/10 text-white/60 bg-transparent transition hover:border-white/30">Électrique</button> | |
| </div> | |
| <input type="hidden" id="vehicle" value=""> | |
| <div class="flex gap-3"> | |
| <button id="btnStep4Back" class="w-1/3 py-3 rounded-xl bg-white/5 text-white border border-white/10 text-sm font-medium hover:bg-white/10 transition"> | |
| Retour | |
| </button> | |
| <button id="btnStep4Calculate" class="w-2/3 py-3 rounded-xl bg-emerald-500 text-black font-medium text-sm hover:opacity-90 transition disabled:opacity-40 disabled:cursor-not-allowed" disabled> | |
| Calculer ma rentabilité | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Result --> | |
| <div id="resultScreen" class="step-container"> | |
| <div id="resultBlock" class="mt-2 rounded-2xl border border-emerald-500/30 bg-[#1a1f2b] p-5"> | |
| <div class="text-[11px] text-emerald-400/70 mb-2 uppercase tracking-wide">Résultat</div> | |
| <div id="mainResult" class="text-[32px] font-semibold text-white mb-2">Il vous reste : 0,00 €</div> | |
| <div id="hourlyRate" class="text-sm text-white/60 mb-1">Soit 0,00 €/heure</div> | |
| <div id="statusLine" class="text-sm text-white/60 mt-1 mb-5">Rentabilité correcte</div> | |
| <button id="btnRevealAnalysis" class="w-full py-3 rounded-xl bg-emerald-500 text-black font-medium text-sm hover:opacity-90 transition mb-3"> | |
| Découvrir l'analyse TIE | |
| </button> | |
| <button id="btnResetResult" class="w-full py-2 text-xs text-white/60 hover:text-white transition"> | |
| Refaire une simulation | |
| </button> | |
| </div> | |
| <div id="analysisContainer" class="analysis-container mt-4 rounded-2xl border border-emerald-500/30 bg-[#1a1f2b] p-5"> | |
| <div class="mb-4 rounded-xl bg-black/30 border border-white/10 p-4"> | |
| <div class="text-[11px] text-emerald-400/80 uppercase tracking-wide mb-2">Lecture TIE</div> | |
| <div id="analysisHeadline" class="text-base font-semibold text-white mb-1">Analyse de votre course</div> | |
| <div id="analysisSummary" class="text-sm text-white/65 leading-relaxed"> | |
| Résumé de votre rentabilité. | |
| </div> | |
| </div> | |
| <div class="rounded-xl bg-black/20 border border-white/10 p-4 mb-4"> | |
| <div class="flex items-center justify-between mb-3"> | |
| <div class="text-sm font-medium text-white">Détail estimatif</div> | |
| <button | |
| id="btnChargesInfo" | |
| type="button" | |
| class="inline-flex items-center justify-center w-6 h-6 rounded-full border border-white/10 text-[11px] text-white/60 hover:text-white hover:border-white/30 transition" | |
| aria-expanded="false" | |
| aria-controls="chargesInfoBox" | |
| >i</button> | |
| </div> | |
| <div class="space-y-2 text-xs text-white/55"> | |
| <div class="flex justify-between gap-4"> | |
| <span>CA brut estimé à déclarer</span> | |
| <span id="grossRevenueDetail" class="text-white/85">0,00 €</span> | |
| </div> | |
| <div class="flex justify-between gap-4"> | |
| <span>Énergie</span> | |
| <span id="energyDetail" class="text-white/85">-0,00 €</span> | |
| </div> | |
| <div class="flex justify-between gap-4"> | |
| <span>Usure véhicule</span> | |
| <span id="wearDetail" class="text-white/85">-0,00 €</span> | |
| </div> | |
| <div class="flex justify-between gap-4"> | |
| <span>Charges sociales & activité</span> | |
| <span id="chargesDetail" class="text-white/85">-0,00 €</span> | |
| </div> | |
| </div> | |
| <div id="chargesInfoBox" class="info-box mt-3 rounded-xl bg-white/5 border border-white/10 p-3"> | |
| <div class="text-[11px] text-white/65 leading-relaxed"> | |
| Les charges varient selon votre statut. Cette estimation est basée sur un profil <span class="text-white font-medium">micro-entrepreneur</span>. | |
| Pour les plateformes, le <span class="text-white font-medium">CA brut déclaré</span> est reconstitué à partir d’une commission moyenne indicative, car les commissions réelles peuvent varier. | |
| </div> | |
| </div> | |
| </div> | |
| <div class="rounded-xl bg-black/40 border border-white/10 p-4 mb-4"> | |
| <div class="text-sm font-medium text-white mb-3">Analyse</div> | |
| <div id="analysisBullets" class="space-y-2 text-sm text-white/70"></div> | |
| </div> | |
| <div id="tieComparison" class="hidden rounded-xl bg-emerald-500/10 border border-emerald-500/20 p-4 text-sm text-emerald-300"></div> | |
| </div> | |
| </div> | |
| </main> | |
| <script> | |
| let currentStep = 1; | |
| const totalSteps = 4; | |
| let formData = { | |
| price: 0, | |
| distance: 0, | |
| time: 0, | |
| platform: '', | |
| vehicle: '' | |
| }; | |
| function formatMoney(value) { | |
| return value.toFixed(2).replace('.', ',') + ' €'; | |
| } | |
| function formatNumber(value) { | |
| return value.toFixed(2).replace('.', ','); | |
| } | |
| function getVehicleLabel(vehicle) { | |
| if (vehicle === 'thermique') return 'Thermique'; | |
| if (vehicle === 'hybride') return 'Hybride'; | |
| if (vehicle === 'electrique') return 'Électrique'; | |
| return '-'; | |
| } | |
| function getEstimatedCommissionRate(platform) { | |
| if (platform === 'uber') return 0.25; | |
| if (platform === 'bolt') return 0.20; | |
| if (platform === 'heetch') return 0.15; | |
| if (platform === 'direct') return 0; | |
| return 0.20; | |
| } | |
| function getEnergyCostPerKm(vehicle) { | |
| if (vehicle === 'thermique') return 0.117; | |
| if (vehicle === 'hybride') return 0.081; | |
| if (vehicle === 'electrique') return 0.03; | |
| return 0.117; | |
| } | |
| function updateProgress() { | |
| const progress = (currentStep / totalSteps) * 100; | |
| document.getElementById('progressFill').style.width = progress + '%'; | |
| document.getElementById('stepLabel').textContent = Math.min(currentStep, totalSteps); | |
| } | |
| function showStep(step) { | |
| document.querySelectorAll('.step-container').forEach(el => el.classList.remove('active')); | |
| if (step === 5) { | |
| document.getElementById('resultScreen').classList.add('active'); | |
| document.getElementById('stepLabel').textContent = '4'; | |
| document.getElementById('progressFill').style.width = '100%'; | |
| currentStep = 4; | |
| return; | |
| } | |
| currentStep = step; | |
| const target = document.getElementById('step' + step); | |
| if (target) { | |
| target.classList.add('active'); | |
| } | |
| updateProgress(); | |
| } | |
| function ensureLoadingScreen() { | |
| let loadingScreen = document.getElementById('loadingScreen'); | |
| if (loadingScreen) return loadingScreen; | |
| const resultScreen = document.getElementById('resultScreen'); | |
| loadingScreen = document.createElement('div'); | |
| loadingScreen.id = 'loadingScreen'; | |
| loadingScreen.className = 'step-container'; | |
| loadingScreen.innerHTML = ` | |
| <div class="bg-[#151821] border border-white/10 rounded-2xl p-6 text-center"> | |
| <div class="w-14 h-14 mx-auto mb-4 rounded-full border-2 border-white/10 border-t-emerald-500 animate-spin"></div> | |
| <div class="text-sm font-medium text-white mb-1">Analyse de votre course</div> | |
| <div class="text-xs text-white/50">TIE calcule votre rentabilité...</div> | |
| </div> | |
| `; | |
| resultScreen.parentNode.insertBefore(loadingScreen, resultScreen); | |
| return loadingScreen; | |
| } | |
| function showLoadingScreen() { | |
| ensureLoadingScreen(); | |
| document.querySelectorAll('.step-container').forEach(el => el.classList.remove('active')); | |
| document.getElementById('loadingScreen').classList.add('active'); | |
| document.getElementById('stepLabel').textContent = '4'; | |
| document.getElementById('progressFill').style.width = '100%'; | |
| } | |
| function setSelectedButtons(buttons, activeBtn) { | |
| buttons.forEach(btn => { | |
| btn.classList.remove('bg-white', 'text-black', 'border-white'); | |
| btn.classList.add('bg-transparent', 'text-white/60', 'border-white/10'); | |
| }); | |
| activeBtn.classList.remove('bg-transparent', 'text-white/60', 'border-white/10'); | |
| activeBtn.classList.add('bg-white', 'text-black', 'border-white'); | |
| } | |
| function buildAnalysis(result) { | |
| const bullets = []; | |
| if (result.classification === 'Peu rentable') { | |
| bullets.push("Le montant reçu est trop faible par rapport à la distance et au temps mobilisé."); | |
| } else if (result.classification === 'Rentabilité moyenne') { | |
| bullets.push("La course reste acceptable, mais votre marge est encore fragile."); | |
| } else { | |
| bullets.push("La course présente un bon équilibre entre prix, distance et temps."); | |
| } | |
| if (result.platform !== 'direct') { | |
| bullets.push(`Le montant saisi est traité comme votre net réellement versé. Le CA brut estimé à déclarer est reconstitué à ${formatMoney(result.estimatedGrossRevenue)}.`); | |
| } else { | |
| bullets.push(`Comme il s’agit d’un client privé, le montant saisi correspond directement à votre CA brut estimé à déclarer : ${formatMoney(result.estimatedGrossRevenue)}.`); | |
| } | |
| if (result.vehicle === 'thermique') { | |
| bullets.push("Avec un thermique, les courses trop faibles au kilomètre dégradent vite votre marge."); | |
| } else if (result.vehicle === 'hybride') { | |
| bullets.push("L’hybride protège mieux votre marge, mais il ne compense pas un tarif trop bas."); | |
| } else { | |
| bullets.push("L’électrique réduit fortement votre coût énergie, mais le temps mobilisé reste décisif."); | |
| } | |
| return bullets; | |
| } | |
| function calculateAndShow() { | |
| const price = parseFloat(formData.price); | |
| const distance = parseFloat(formData.distance); | |
| const totalMinutes = parseFloat(formData.time); | |
| const platform = formData.platform; | |
| const vehicle = formData.vehicle; | |
| if (!price || price <= 0 || !distance || distance <= 0 || !totalMinutes || totalMinutes <= 0 || !vehicle || !platform) { | |
| alert('Veuillez compléter correctement toutes les informations.'); | |
| return; | |
| } | |
| const pricePerKm = price / distance; | |
| const pricePerMinute = price / totalMinutes; | |
| let classification = ''; | |
| let distanceScore = 0; | |
| let timeScore = 0; | |
| let vehicleScore = 0; | |
| let scoreTotal = 0; | |
| if (pricePerKm < 1.00) { | |
| classification = 'Peu rentable'; | |
| scoreTotal = 0; | |
| } else { | |
| if (pricePerKm < 1.20) { | |
| distanceScore = 0; | |
| } else if (pricePerKm < 1.80) { | |
| distanceScore = 1; | |
| } else { | |
| distanceScore = 2; | |
| } | |
| if (pricePerMinute < 0.50) { | |
| timeScore = 0; | |
| } else if (pricePerMinute < 0.80) { | |
| timeScore = 1; | |
| } else { | |
| timeScore = 2; | |
| } | |
| if (vehicle === 'thermique') { | |
| vehicleScore = 0; | |
| } else if (vehicle === 'hybride') { | |
| vehicleScore = 1; | |
| } else if (vehicle === 'electrique') { | |
| vehicleScore = 2; | |
| } | |
| scoreTotal = distanceScore + timeScore + vehicleScore; | |
| if (scoreTotal <= 2) { | |
| classification = 'Peu rentable'; | |
| } else if (scoreTotal <= 4) { | |
| classification = 'Rentabilité moyenne'; | |
| } else { | |
| classification = 'Rentable'; | |
| } | |
| } | |
| const estimatedCommissionRate = getEstimatedCommissionRate(platform); | |
| const estimatedGrossRevenue = platform === 'direct' | |
| ? price | |
| : price / (1 - estimatedCommissionRate); | |
| const energyCostPerKm = getEnergyCostPerKm(vehicle); | |
| const energyCost = distance * energyCostPerKm; | |
| const wearCostPerKm = 0.20; | |
| const wearCost = distance * wearCostPerKm; | |
| const chargesRate = 0.22; | |
| const chargesCost = estimatedGrossRevenue * chargesRate; | |
| const estimatedRemaining = price - energyCost - wearCost - chargesCost; | |
| const privatePrice = distance * 2; | |
| const privateGrossRevenue = privatePrice; | |
| const privateChargesCost = privateGrossRevenue * chargesRate; | |
| const privateRemaining = privatePrice - energyCost - wearCost - privateChargesCost; | |
| const privateDifference = privateRemaining - estimatedRemaining; | |
| let statusText = ''; | |
| let summaryText = ''; | |
| if (classification === 'Peu rentable') { | |
| statusText = 'Cette course vous rapporte trop peu pour la distance totale parcourue.'; | |
| summaryText = 'Marge faible : cette course use votre temps et votre véhicule plus qu’elle ne vous rémunère.'; | |
| } else if (classification === 'Rentabilité moyenne') { | |
| statusText = 'Cette course peut se défendre, mais sa marge reste fragile.'; | |
| summaryText = 'Marge correcte mais fragile : intéressante seulement si l’approche est faible ou bien optimisée.'; | |
| } else { | |
| statusText = 'Cette course présente un bon équilibre entre prix, distance et temps.'; | |
| summaryText = 'Bonne course : vous avez ici un niveau de rémunération cohérent avec votre mobilisation.'; | |
| } | |
| const analysisBullets = buildAnalysis({ | |
| classification, | |
| estimatedGrossRevenue, | |
| platform, | |
| vehicle | |
| }); | |
| window.calculationResult = { | |
| classification, | |
| pricePerKm, | |
| pricePerMinute, | |
| vehicleLabel: getVehicleLabel(vehicle), | |
| estimatedRemaining, | |
| privatePrice, | |
| privateRemaining, | |
| privateDifference, | |
| estimatedGrossRevenue, | |
| estimatedCommissionRate, | |
| energyCost, | |
| wearCost, | |
| chargesCost, | |
| statusText, | |
| summaryText, | |
| analysisBullets, | |
| platform, | |
| vehicle, | |
| price, | |
| distance, | |
| totalMinutes | |
| }; | |
| showLoadingScreen(); | |
| setTimeout(() => { | |
| document.getElementById('mainResult').textContent = formatMoney(estimatedRemaining); | |
| document.getElementById('hourlyRate').innerHTML = | |
| `${formatNumber((estimatedRemaining / totalMinutes) * 60)} €/heure estimé<br>` + | |
| `Prix/km : ${formatNumber(pricePerKm)} € · Prix/minute : ${formatNumber(pricePerMinute)} €`; | |
| document.getElementById('statusLine').textContent = statusText; | |
| document.getElementById('analysisContainer').classList.remove('active'); | |
| document.getElementById('btnRevealAnalysis').style.display = 'block'; | |
| document.getElementById('chargesInfoBox').classList.remove('active'); | |
| document.getElementById('btnChargesInfo').setAttribute('aria-expanded', 'false'); | |
| showStep(5); | |
| }, 1200); | |
| } | |
| // Step 1 | |
| document.getElementById('btnStep1Next').addEventListener('click', () => { | |
| const price = parseFloat(document.getElementById('price').value); | |
| if (!price || price <= 0) { | |
| alert('Veuillez entrer un prix valide.'); | |
| return; | |
| } | |
| formData.price = price; | |
| showStep(2); | |
| }); | |
| // Step 2 | |
| document.getElementById('btnStep2Back').addEventListener('click', () => showStep(1)); | |
| document.getElementById('btnStep2Next').addEventListener('click', () => { | |
| const distance = parseFloat(document.getElementById('distance').value); | |
| const time = parseFloat(document.getElementById('time').value); | |
| if (!distance || distance <= 0) { | |
| alert('Veuillez entrer une distance valide.'); | |
| return; | |
| } | |
| if (!time || time <= 0) { | |
| alert('Veuillez entrer un temps valide.'); | |
| return; | |
| } | |
| formData.distance = distance; | |
| formData.time = time; | |
| showStep(3); | |
| }); | |
| // Step 3 | |
| const platformBtns = document.querySelectorAll('.platform-btn'); | |
| const btnStep3Next = document.getElementById('btnStep3Next'); | |
| platformBtns.forEach(btn => { | |
| btn.addEventListener('click', () => { | |
| setSelectedButtons(platformBtns, btn); | |
| formData.platform = btn.dataset.value; | |
| document.getElementById('platform').value = btn.dataset.value; | |
| btnStep3Next.disabled = false; | |
| }); | |
| }); | |
| document.getElementById('btnStep3Back').addEventListener('click', () => showStep(2)); | |
| document.getElementById('btnStep3Next').addEventListener('click', () => { | |
| if (!formData.platform) { | |
| alert('Veuillez sélectionner une plateforme.'); | |
| return; | |
| } | |
| showStep(4); | |
| }); | |
| // Step 4 | |
| const vehicleBtns = document.querySelectorAll('.vehicle-btn'); | |
| const btnStep4Calculate = document.getElementById('btnStep4Calculate'); | |
| vehicleBtns.forEach(btn => { | |
| btn.addEventListener('click', () => { | |
| setSelectedButtons(vehicleBtns, btn); | |
| formData.vehicle = btn.dataset.value; | |
| document.getElementById('vehicle').value = btn.dataset.value; | |
| btnStep4Calculate.disabled = false; | |
| }); | |
| }); | |
| document.getElementById('btnStep4Back').addEventListener('click', () => showStep(3)); | |
| document.getElementById('btnStep4Calculate').addEventListener('click', calculateAndShow); | |
| // Analysis reveal | |
| document.getElementById('btnRevealAnalysis').addEventListener('click', () => { | |
| const result = window.calculationResult; | |
| if (!result) { | |
| alert("Aucune analyse disponible. Veuillez recalculer la simulation."); | |
| return; | |
| } | |
| document.getElementById('analysisHeadline').textContent = | |
| result.classification === 'Rentable' | |
| ? 'Votre course semble bien calibrée' | |
| : result.classification === 'Rentabilité moyenne' | |
| ? 'Votre course reste acceptable, mais surveillez votre marge' | |
| : 'Votre course paraît peu rentable'; | |
| document.getElementById('analysisSummary').textContent = result.summaryText; | |
| document.getElementById('grossRevenueDetail').textContent = formatMoney(result.estimatedGrossRevenue); | |
| document.getElementById('energyDetail').textContent = '-' + formatMoney(result.energyCost); | |
| document.getElementById('wearDetail').textContent = '-' + formatMoney(result.wearCost); | |
| document.getElementById('chargesDetail').textContent = '-' + formatMoney(result.chargesCost); | |
| const bulletsContainer = document.getElementById('analysisBullets'); | |
| bulletsContainer.innerHTML = result.analysisBullets | |
| .map(item => `<div class="flex gap-2"><span class="text-emerald-400">•</span><span>${item}</span></div>`) | |
| .join(''); | |
| const tieComparison = document.getElementById('tieComparison'); | |
| if (result.privateDifference > 0) { | |
| tieComparison.textContent = | |
| `À titre indicatif, un client privé facturé à 2 €/km sur cette même course pourrait vous laisser environ ${formatMoney(result.privateDifference)} de plus.`; | |
| tieComparison.classList.remove('hidden'); | |
| } else { | |
| tieComparison.textContent = ''; | |
| tieComparison.classList.add('hidden'); | |
| } | |
| document.getElementById('analysisContainer').classList.add('active'); | |
| document.getElementById('btnRevealAnalysis').style.display = 'none'; | |
| document.getElementById('analysisContainer').scrollIntoView({ | |
| behavior: 'smooth', | |
| block: 'nearest' | |
| }); | |
| }); | |
| // Info button | |
| document.getElementById('btnChargesInfo').addEventListener('click', () => { | |
| const box = document.getElementById('chargesInfoBox'); | |
| const btn = document.getElementById('btnChargesInfo'); | |
| const isOpen = box.classList.contains('active'); | |
| box.classList.toggle('active'); | |
| btn.setAttribute('aria-expanded', String(!isOpen)); | |
| }); | |
| // Reset | |
| function resetSimulation() { | |
| formData = { | |
| price: 0, | |
| distance: 0, | |
| time: 0, | |
| platform: '', | |
| vehicle: '' | |
| }; | |
| window.calculationResult = null; | |
| document.getElementById('price').value = ''; | |
| document.getElementById('distance').value = ''; | |
| document.getElementById('time').value = ''; | |
| document.getElementById('platform').value = ''; | |
| document.getElementById('vehicle').value = ''; | |
| platformBtns.forEach(b => { | |
| b.classList.remove('bg-white', 'text-black', 'border-white'); | |
| b.classList.add('bg-transparent', 'text-white/60', 'border-white/10'); | |
| }); | |
| vehicleBtns.forEach(b => { | |
| b.classList.remove('bg-white', 'text-black', 'border-white'); | |
| b.classList.add('bg-transparent', 'text-white/60', 'border-white/10'); | |
| }); | |
| btnStep3Next.disabled = true; | |
| btnStep4Calculate.disabled = true; | |
| document.getElementById('analysisContainer').classList.remove('active'); | |
| document.getElementById('btnRevealAnalysis').style.display = 'block'; | |
| document.getElementById('tieComparison').classList.add('hidden'); | |
| document.getElementById('tieComparison').textContent = ''; | |
| document.getElementById('chargesInfoBox').classList.remove('active'); | |
| document.getElementById('btnChargesInfo').setAttribute('aria-expanded', 'false'); | |
| const loadingScreen = document.getElementById('loadingScreen'); | |
| if (loadingScreen) loadingScreen.classList.remove('active'); | |
| showStep(1); | |
| currentStep = 1; | |
| updateProgress(); | |
| window.scrollTo({ top: 0, behavior: 'smooth' }); | |
| } | |
| document.getElementById('btnResetResult').addEventListener('click', resetSimulation); | |
| document.getElementById('price').addEventListener('keypress', (e) => { | |
| if (e.key === 'Enter') document.getElementById('btnStep1Next').click(); | |
| }); | |
| document.getElementById('time').addEventListener('keypress', (e) => { | |
| if (e.key === 'Enter') document.getElementById('btnStep2Next').click(); | |
| }); | |
| </script> | |
| </body> | |
| </html> |