// ========================== // DATA & STATE // ========================== const state = { view: 'home', mode: 'course', segment: 'fav', when: 'now', duration: 120, passengers: 1, luggage: 1, animal: false, forOther: false, customWelcome: false, need: 'none', customerType: 'particulier', selectedDriver: { id: 1, name: 'Karim', available: true, img: 'https://images.unsplash.com/photo-1500648767791-00dcc994a43e?w=100&h=100&fit=crop&crop=face' }, profile: { firstname: 'Marie', lastname: 'Dupont', phone: '06 12 34 56 78', email: 'marie@email.com', type: 'particulier' } }; const drivers = [ { id: 1, name: 'Karim B.', available: true, img: 'https://images.unsplash.com/photo-1500648767791-00dcc994a43e?w=100&h=100&fit=crop&crop=face', rating: 4.9, trips: 124 }, { id: 2, name: 'Sophie L.', available: true, img: 'https://images.unsplash.com/photo-1438761681033-6461ffad8d80?w=100&h=100&fit=crop&crop=face', rating: 4.8, trips: 89 }, { id: 3, name: 'Thomas R.', available: false, img: 'https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?w=100&h=100&fit=crop&crop=face', rating: 4.9, trips: 210 }, { id: 4, name: 'Amira D.', available: true, img: 'https://images.unsplash.com/photo-1544005313-94ddf0286df2?w=100&h=100&fit=crop&crop=face', rating: 5.0, trips: 56 } ]; let rides = [ { id: 1, status: 'confirmed', depart: '12 Rue de la Paix, Paris', arrivee: 'Aéroport CDG, Terminal 2', driver: 'Karim', price: 52, date: 'Aujourd\'hui', time: '14:30', category: 'today' }, { id: 2, status: 'pending', depart: 'Domicile', arrivee: 'Gare de Lyon, Paris', driver: 'Demande groupée', price: 28, date: 'Aujourd\'hui', time: '16:00', category: 'today' }, { id: 3, status: 'past', depart: 'Maison', arrivee: 'Bureau', driver: 'Sophie', price: 18, date: 'Hier', time: '08:30', category: 'past' }, { id: 4, status: 'cancelled', depart: 'Restaurant', arrivee: 'Maison', driver: 'Thomas', price: 0, date: '12 jan.', time: '22:00', category: 'cancelled' } ]; let relatives = [ { id: 1, firstname: 'Jean', lastname: 'Dupont', phone: '06 98 76 54 32' } ]; let favoriteAddresses = [ { id: 1, label: 'Maison', address: '12 Rue de la Paix, 75002 Paris', tag: 'maison', icon: 'home' }, { id: 2, label: 'Bureau', address: '88 Avenue des Champs-Élysées, 75008 Paris', tag: 'travail', icon: 'briefcase' } ]; let extraStopsCount = 0; // ========================== // INIT // ========================== document.addEventListener('DOMContentLoaded', () => { lucide.createIcons(); renderDrivers(); renderRides('today'); renderRelatives(); renderFavoriteAddresses(); renderFavoritePills(); updateDriverBlock(); updateDisposalPrice(); // Simulate route preview when typing const departInput = document.getElementById('input-depart'); const arriveeInput = document.getElementById('input-arrivee'); [departInput, arriveeInput].forEach(input => { input.addEventListener('input', () => { const show = departInput.value.length > 3 && arriveeInput.value.length > 3; const routePreview = document.getElementById('route-preview'); const estimateBlock = document.getElementById('estimate-block'); if (show) { routePreview.classList.remove('hidden'); estimateBlock.classList.remove('hidden'); } else { routePreview.classList.add('hidden'); estimateBlock.classList.add('hidden'); } }); }); }); // ========================== // NAVIGATION // ========================== function showView(viewName) { document.querySelectorAll('.view-section').forEach(el => el.classList.add('hidden')); const target = document.getElementById(`view-${viewName}`); if (target) target.classList.remove('hidden'); document.querySelectorAll('.nav-btn').forEach(btn => { if (btn.dataset.view === viewName) { btn.classList.add('text-emerald-400'); btn.classList.remove('text-white/40'); } else { btn.classList.remove('text-emerald-400'); btn.classList.add('text-white/40'); } }); window.scrollTo(0, 0); lucide.createIcons(); } // ========================== // MODALS // ========================== function openModal(id) { const modal = document.getElementById(id); if (!modal) return; modal.classList.remove('hidden'); document.body.style.overflow = 'hidden'; lucide.createIcons(); } function closeModal(id) { const modal = document.getElementById(id); if (!modal) return; modal.classList.add('hidden'); document.body.style.overflow = ''; } // ========================== // HOME / BOOKING LOGIC // ========================== function setMode(mode) { state.mode = mode; const courseBtn = document.getElementById('seg-mode-course'); const disposalBtn = document.getElementById('seg-mode-disposal'); const courseInputs = document.getElementById('course-inputs'); const disposalFields = document.getElementById('disposal-fields'); if (mode === 'course') { courseBtn.className = 'press flex-1 py-2 rounded-lg text-xs font-medium transition-all bg-emerald-500/10 text-emerald-400 border border-emerald-500/20'; disposalBtn.className = 'press flex-1 py-2 rounded-lg text-xs font-medium transition-all text-white/45 hover:text-white/70'; courseInputs.classList.remove('hidden'); disposalFields.classList.add('hidden'); } else { disposalBtn.className = 'press flex-1 py-2 rounded-lg text-xs font-medium transition-all bg-emerald-500/10 text-emerald-400 border border-emerald-500/20'; courseBtn.className = 'press flex-1 py-2 rounded-lg text-xs font-medium transition-all text-white/45 hover:text-white/70'; courseInputs.classList.add('hidden'); disposalFields.classList.remove('hidden'); } } function setSegment(seg) { state.segment = seg; const favBtn = document.getElementById('seg-fav'); const groupBtn = document.getElementById('seg-group'); if (seg === 'fav') { favBtn.className = 'press flex-1 py-2 rounded-lg text-xs font-medium transition-all bg-emerald-500/10 text-emerald-400 border border-emerald-500/20'; groupBtn.className = 'press flex-1 py-2 rounded-lg text-xs font-medium transition-all text-white/45 hover:text-white/70'; updateDriverBlock(); } else { groupBtn.className = 'press flex-1 py-2 rounded-lg text-xs font-medium transition-all bg-emerald-500/10 text-emerald-400 border border-emerald-500/20'; favBtn.className = 'press flex-1 py-2 rounded-lg text-xs font-medium transition-all text-white/45 hover:text-white/70'; document.querySelector('#driver-block div div:nth-child(2) p:first-child').textContent = 'Tous mes chauffeurs'; document.querySelector('#driver-block div div:nth-child(2) p:nth-child(2)').textContent = '2 disponibles'; document.querySelector('#driver-block img').src = 'https://images.unsplash.com/photo-1494790108377-be9c29b29330?w=100&h=100&fit=crop&crop=face'; } } function setWhen(when) { state.when = when; const nowBtn = document.getElementById('btn-now'); const laterBtn = document.getElementById('btn-later'); if (when === 'now') { nowBtn.className = 'press flex-1 py-2.5 rounded-xl border text-sm font-medium transition-all bg-emerald-500/10 border-emerald-500/30 text-emerald-400'; laterBtn.className = 'press flex-1 py-2.5 rounded-xl border text-sm font-medium transition-all bg-transparent border-white/10 text-white/45'; } else { laterBtn.className = 'press flex-1 py-2.5 rounded-xl border text-sm font-medium transition-all bg-emerald-500/10 border-emerald-500/30 text-emerald-400'; nowBtn.className = 'press flex-1 py-2.5 rounded-xl border text-sm font-medium transition-all bg-transparent border-white/10 text-white/45'; openModal('modal-datetime'); } } function confirmDateTime() { const date = document.getElementById('date-picker').value; const time = document.getElementById('time-picker').value; if (date && time) { document.getElementById('btn-later').textContent = `${date.split('-').reverse().join('/')} ${time}`; closeModal('modal-datetime'); } } function addStop() { const container = document.getElementById('extra-stops'); const id = ++extraStopsCount; const stopHTML = `
`; container.insertAdjacentHTML('beforeend', stopHTML); lucide.createIcons(); } function removeStop(id) { const el = document.getElementById(`stop-${id}`); if (el) el.remove(); } function sendRequest() { openModal('modal-loading'); setTimeout(() => { closeModal('modal-loading'); openModal('modal-return'); // Add pending ride demo rides.unshift({ id: Date.now(), status: 'pending', depart: document.getElementById('input-depart').value || 'Adresse saisie', arrivee: document.getElementById('input-arrivee').value || 'Destination', driver: state.segment === 'fav' ? state.selectedDriver.name : 'Demande groupée', price: state.mode === 'course' ? 24 : 80, date: 'Aujourd\'hui', time: 'À venir', category: 'today' }); renderRides('today'); }, 2000); } function handleReturn(returnTrip) { closeModal('modal-return'); if (returnTrip) { document.getElementById('return-badge').classList.remove('hidden'); const temp = document.getElementById('input-depart').value; document.getElementById('input-depart').value = document.getElementById('input-arrivee').value; document.getElementById('input-arrivee').value = temp; window.scrollTo({ top: 0, behavior: 'smooth' }); } else { document.getElementById('return-badge').classList.add('hidden'); } } // ========================== // DISPOSAL // ========================== function adjustDuration(minutes) { state.duration = Math.max(30, state.duration + minutes); const h = Math.floor(state.duration / 60); const m = state.duration % 60; document.getElementById('disposal-duration').textContent = `${h}h${m.toString().padStart(2, '0')}`; updateDisposalPrice(); } function updateDisposalPrice() { const hours = state.duration / 60; const price = Math.round(hours * 40); document.getElementById('disposal-price').textContent = `${price} €`; } function toggleChip(btn) { const isActive = btn.classList.contains('bg-emerald-500/10'); if (isActive) { btn.className = 'press px-3 py-1.5 rounded-full text-xs font-medium border border-white/10 text-white/55 hover:border-emerald-500/30 hover:text-emerald-400 transition-colors'; } else { btn.className = 'press px-3 py-1.5 rounded-full text-xs font-medium border border-emerald-500/20 bg-emerald-500/10 text-emerald-400 transition-colors'; } } function toggleCustomWelcome() { state.customWelcome = !state.customWelcome; const btn = document.getElementById('btn-custom-welcome'); const fields = document.getElementById('custom-welcome-fields'); if (state.customWelcome) { btn.textContent = 'Oui'; btn.className = 'press px-3 py-1.5 rounded-lg border border-emerald-500/30 text-xs font-medium text-emerald-400 bg-emerald-500/10 transition-all'; fields.classList.remove('hidden'); } else { btn.textContent = 'Non'; btn.className = 'press px-3 py-1.5 rounded-lg border border-white/10 text-xs font-medium text-white/45 transition-all'; fields.classList.add('hidden'); } } function setNeed(btn, need) { state.need = need; document.querySelectorAll('.need-chip').forEach(chip => { chip.className = 'press need-chip px-3 py-1.5 rounded-full text-xs font-medium border border-white/10 text-white/55 hover:border-emerald-500/30 hover:text-emerald-400 transition-all'; }); btn.className = 'press need-chip px-3 py-1.5 rounded-full text-xs font-medium border border-emerald-500/20 bg-emerald-500/10 text-emerald-400 transition-all'; document.getElementById('need-other-field').classList.toggle('hidden', need !== 'other'); } // ========================== // DETAILS MODAL // ========================== function adjust(type, delta) { if (type === 'passengers') { state.passengers = Math.max(1, state.passengers + delta); document.getElementById('val-passengers').textContent = state.passengers; } else if (type === 'luggage') { state.luggage = Math.max(0, state.luggage + delta); document.getElementById('val-luggage').textContent = state.luggage; } } function toggleAnimal() { state.animal = !state.animal; const btn = document.getElementById('btn-animal'); if (state.animal) { btn.textContent = 'Oui'; btn.className = 'press px-3 py-1.5 rounded-lg border border-emerald-500/30 text-xs font-medium text-emerald-400 bg-emerald-500/10 transition-all'; } else { btn.textContent = 'Non'; btn.className = 'press px-3 py-1.5 rounded-lg border border-white/10 text-xs font-medium text-white/45 transition-all'; } } function toggleForOther() { state.forOther = !state.forOther; const btn = document.getElementById('btn-for-other'); const fields = document.getElementById('fields-for-other'); if (state.forOther) { btn.textContent = 'Oui'; btn.className = 'press px-3 py-1.5 rounded-lg border border-emerald-500/30 text-xs font-medium text-emerald-400 bg-emerald-500/10 transition-all'; fields.classList.remove('hidden'); } else { btn.textContent = 'Non'; btn.className = 'press px-3 py-1.5 rounded-lg border border-white/10 text-xs font-medium text-white/45 transition-all'; fields.classList.add('hidden'); } } // ========================== // DRIVERS // ========================== function renderDrivers() { const container = document.getElementById('drivers-list'); container.innerHTML = drivers.map(d => `

${d.name}

${d.available ? 'Disponible' : 'Indisponible'}

${d.rating} ★
`).join(''); lucide.createIcons(); } function openAvailableDriversSheet() { const list = document.getElementById('available-drivers-list'); const available = drivers.filter(d => d.available); list.innerHTML = available.map(d => `

${d.name}

Disponible maintenant

`).join(''); openModal('modal-available-drivers'); lucide.createIcons(); } function openSelectDriverSheet() { const list = document.getElementById('select-driver-list'); list.innerHTML = drivers.map(d => `

${d.name}

${d.available ? 'Disponible' : 'Indisponible'}

${state.selectedDriver.id === d.id ? 'Sélectionné' : ''}
`).join(''); openModal('modal-select-driver'); lucide.createIcons(); } function selectDriver(id) { const d = drivers.find(x => x.id === id); if (d) { state.selectedDriver = d; updateDriverBlock(); } } function updateDriverBlock() { const block = document.getElementById('driver-block'); block.querySelector('img').src = state.selectedDriver.img; block.querySelector('p.text-sm').textContent = state.selectedDriver.name; block.querySelector('p.text-\\[10px\\]').textContent = state.selectedDriver.available ? 'Disponible' : 'Indisponible'; block.querySelector('p.text-\\[10px\\]').className = `text-[10px] ${state.selectedDriver.available ? 'text-emerald-400' : 'text-white/40'}`; } // ========================== // RIDES // ========================== function renderRides(filter) { document.querySelectorAll('[id^="filter-"]').forEach(btn => { btn.className = 'press px-3 py-1.5 rounded-full text-xs font-medium bg-[#151821] text-white/60 border border-white/10'; }); const activeBtn = document.getElementById(`filter-${filter}`); if (activeBtn) activeBtn.className = 'press px-3 py-1.5 rounded-full text-xs font-medium bg-emerald-500/10 text-emerald-400 border border-emerald-500/20'; const filtered = rides.filter(r => r.category === filter); const container = document.getElementById('rides-list'); if (filtered.length === 0) { container.innerHTML = '

Aucune course

'; return; } container.innerHTML = filtered.map(r => `
${getStatusLabel(r.status)} ${r.date} • ${r.time}

${r.depart}

${r.arrivee}

${r.driver} ${r.price > 0 ? r.price + ' €' : '-'}
`).join(''); } function getStatusColor(status) { if (status === 'confirmed') return 'text-emerald-400'; if (status === 'pending') return 'text-amber-400'; if (status === 'cancelled') return 'text-red-400'; return 'text-white/45'; } function getStatusLabel(status) { if (status === 'confirmed') return 'Confirmée'; if (status === 'pending') return 'En attente'; if (status === 'cancelled') return 'Annulée'; return 'Terminée'; } function openRideDetail(id) { const r = rides.find(x => x.id === id); if (!r) return; const content = document.getElementById('ride-detail-content'); content.innerHTML = `
Statut ${getStatusLabel(r.status)}
Départ ${r.depart}
Arrivée ${r.arrivee}
Chauffeur ${r.driver}
Date ${r.date} à ${r.time}
Prix estimé ${r.price > 0 ? r.price + ' €' : '-'}
${r.status === 'past' ? `
` : ''} `; openModal('modal-ride-detail'); lucide.createIcons(); } // ========================== // PROFILE // ========================== function setCustomerType(type) { state.customerType = type; const partBtn = document.getElementById('seg-type-particulier'); const proBtn = document.getElementById('seg-type-professionnel'); const companyFields = document.getElementById('company-fields'); if (type === 'particulier') { partBtn.className = 'press flex-1 py-2 rounded-lg text-xs font-medium transition-all bg-emerald-500/10 text-emerald-400 border border-emerald-500/20'; proBtn.className = 'press flex-1 py-2 rounded-lg text-xs font-medium transition-all text-white/45 hover:text-white/70'; companyFields.classList.add('hidden'); } else { proBtn.className = 'press flex-1 py-2 rounded-lg text-xs font-medium transition-all bg-emerald-500/10 text-emerald-400 border border-emerald-500/20'; partBtn.className = 'press flex-1 py-2 rounded-lg text-xs font-medium transition-all text-white/45 hover:text-white/70'; companyFields.classList.remove('hidden'); } } function saveProfile() { const firstname = document.getElementById('profile-firstname').value; const lastname = document.getElementById('profile-lastname').value; const phone = document.getElementById('profile-phone').value; const email = document.getElementById('profile-email').value; state.profile = { ...state.profile, firstname, lastname, phone, email, type: state.customerType }; document.getElementById('profile-name-display').textContent = `${firstname} ${lastname}`; document.getElementById('profile-contact-display').textContent = `${phone} • ${email}`; document.getElementById('profile-type-display').textContent = state.customerType === 'particulier' ? 'Particulier' : 'Professionnel'; document.querySelector('h1').textContent = `Bonjour ${firstname}`; closeModal('modal-edit-profile'); } function renderRelatives() { const container = document.getElementById('relatives-list'); container.innerHTML = relatives.map(r => `

${r.firstname} ${r.lastname}

${r.phone}

`).join(''); lucide.createIcons(); } function addRelative() { const firstname = document.getElementById('rel-firstname').value; const lastname = document.getElementById('rel-lastname').value; const phone = document.getElementById('rel-phone').value; if (firstname && lastname && phone) { relatives.push({ id: Date.now(), firstname, lastname, phone }); renderRelatives(); closeModal('modal-add-relative'); document.getElementById('rel-firstname').value = ''; document.getElementById('rel-lastname').value = ''; document.getElementById('rel-phone').value = ''; } } function renderFavoriteAddresses() { const container = document.getElementById('profile-favorite-addresses'); const departPills = document.getElementById('favorite-depart-pills'); const arriveePills = document.getElementById('favorite-arrivee-pills'); const html = favoriteAddresses.map(a => `

${a.label}

${a.address}

`).join(''); container.innerHTML = html; const pillHtml = favoriteAddresses.map(a => ` `).join(''); if (departPills) departPills.innerHTML = pillHtml; if (arriveePills) arriveePills.innerHTML = pillHtml; lucide.createIcons(); } function fillAddress(address) { const active = document.activeElement; if (active && active.id === 'input-arrivee') { active.value = address; } else { document.getElementById('input-depart').value = address; } } function addFavoriteAddress() { const label = document.getElementById('addr-label').value; const address = document.getElementById('addr-address').value; const tag = document.getElementById('addr-tag').value; const icon = document.getElementById('addr-icon').value; if (label && address) { favoriteAddresses.push({ id: Date.now(), label, address, tag, icon }); renderFavoriteAddresses(); closeModal('modal-add-address'); document.getElementById('addr-label').value = ''; document.getElementById('addr-address').value = ''; document.getElementById('addr-tag').value = ''; } } function openEditAddress(id) { const a = favoriteAddresses.find(x => x.id === id); if (!a) return; document.getElementById('edit-addr-id').value = a.id; document.getElementById('edit-addr-label').value = a.label; document.getElementById('edit-addr-address').value = a.address; document.getElementById('edit-addr-tag').value = a.tag || ''; document.getElementById('edit-addr-icon').value = a.icon; openModal('modal-edit-address'); } function saveEditedAddress() { const id = parseInt(document.getElementById('edit-addr-id').value); const a = favoriteAddresses.find(x => x.id === id); if (a) { a.label = document.getElementById('edit-addr-label').value; a.address = document.getElementById('edit-addr-address').value; a.tag = document.getElementById('edit-addr-tag').value; a.icon = document.getElementById('edit-addr-icon').value; renderFavoriteAddresses(); closeModal('modal-edit-address'); } } function deleteFavoriteAddress() { const id = parseInt(document.getElementById('edit-addr-id').value); favoriteAddresses = favoriteAddresses.filter(x => x.id !== id); renderFavoriteAddresses(); closeModal('modal-edit-address'); } function renderFavoritePills() { renderFavoriteAddresses(); } // ========================== // QR & MISC // ========================== function showManualCode() { document.getElementById('manual-code-area').classList.remove('hidden'); } function addManualCode() { const code = document.getElementById('manual-code').value; if (code) { closeModal('modal-qr'); // Demo: add a new driver drivers.push({ id: Date.now(), name: `Chauffeur ${code.slice(0,4)}`, available: true, img: 'https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?w=100&h=100&fit=crop&crop=face', rating: 5.0, trips: 0 }); renderDrivers(); document.getElementById('manual-code').value = ''; document.getElementById('manual-code-area').classList.add('hidden'); } } function toggleFaq(btn) { const answer = btn.nextElementSibling; const icon = btn.querySelector('.faq-icon'); if (answer.classList.contains('hidden')) { answer.classList.remove('hidden'); icon.style.transform = 'rotate(180deg)'; } else { answer.classList.add('hidden'); icon.style.transform = 'rotate(0deg)'; } }