Yocapp's picture
génère moi le visuel à partir de ce code suivant : <!DOCTYPE html><html lang="fr"><head>  <meta charset="UTF-8">  <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.
e4e7c58 verified
Raw
History Blame Contribute Delete
36.4 kB
window.onerror = function(msg, url, line) {
console.error('ERREUR JS :', msg, line);
};
const state = {
isReturnTrip: false,
profile: { firstName: 'Marie', lastName: 'Dupont', phone: '06 12 34 56 78', email: 'marie@email.com', address: '' },
customerType: 'particulier',
billingAddress: '',
company: {
name: '',
siret: '',
vat: '',
billingEmail: ''
},
drivers: [
{ id: 'karim', name: 'Karim', photo: 'https://images.unsplash.com/photo-1500648767791-00dcc994a43e?w=100&h=100&fit=crop&crop=face', status: 'Disponible', vehicle: 'Mercedes Classe E', rate: 2, rides: 18, favorite: true, available: true, phone: '+33612345678' },
{ id: 'sofiane', name: 'Sofiane', photo: 'https://images.unsplash.com/photo-1506794778202-cad84cf45f1d?w=100&h=100&fit=crop&crop=face', status: 'Disponible', vehicle: 'Tesla Model 3', rate: 2.2, rides: 11, favorite: false, available: true, phone: '+33623456789' },
{ id: 'nadia', name: 'Nadia', photo: 'https://images.unsplash.com/photo-1494790108377-be9c29b29330?w=100&h=100&fit=crop&crop=face', status: 'Occupé', vehicle: 'Peugeot 508', rate: 1.9, rides: 7, favorite: false, available: false, phone: '+33634567890' },
{ id: 'rachid', name: 'Rachid', photo: 'https://images.unsplash.com/photo-1531123897727-8f129e1688ce?w=100&h=100&fit=crop&crop=face', status: 'Hors ligne', vehicle: 'Toyota Corolla', rate: 2, rides: 5, favorite: false, available: false, phone: '+33645678901' }
],
favoriteAddresses: [
{ id:'home', label:'Maison', tag:'maison', icon:'home', address:'12 Rue de la Paix, Paris' },
{ id:'work', label:'Travail', tag:'travail', icon:'briefcase', address:'Bureau, Paris' },
{ id:'airport', label:'Aéroport', tag:'aéroport', icon:'plane', address:'Aéroport CDG, Terminal 2' },
{ id:'station', label:'Gare', tag:'gare', icon:'train', address:'Gare de Lyon, Paris' }
],
rides: [
{ id: 1, date: "Aujourd'hui à 14:30", from: '12 Rue de la Paix', to: 'Aéroport CDG', driver: 'Karim', status: 'Confirmée', amount: '52 €', filter: 'today', vehicle: 'Mercedes Classe E', distance: '24 km', rate: 2, passengers: 1, luggage: 1, animal: false },
{ id: 2, date: 'Envoyée il y a 5 min', from: 'Domicile', to: 'Gare de Lyon', driver: 'Demande groupée', status: 'En attente', amount: null, filter: 'today', vehicle: '-', distance: '-', rate: null, passengers: 1, luggage: 1, animal: false },
{ id: 3, date: 'Demain à 09:00', from: 'Orly', to: 'La Défense', driver: 'Sofiane', status: 'Confirmée', amount: '45 €', filter: 'upcoming', vehicle: 'Tesla Model 3', distance: '22 km', rate: 2.2, passengers: 2, luggage: 2, animal: false },
{ id: 4, date: 'Hier à 18:15', from: 'Bureau', to: 'Domicile', driver: 'Karim', status: 'Terminée', amount: '28 €', filter: 'past', vehicle: 'Mercedes Classe E', distance: '14 km', rate: 2, passengers: 1, luggage: 0, animal: false },
{ id: 5, date: '12/01/2026', from: 'Domicile', to: 'Gare du Nord', driver: 'Nadia', status: 'Annulée', amount: '-', filter: 'cancelled', vehicle: 'Peugeot 508', distance: '-', rate: 1.9, passengers: 1, luggage: 1, animal: false }
],
relatives: [
{ firstName: 'Lucas', lastName: 'Dupont', phone: '06 23 45 67 89' }
],
segment: 'fav',
when: 'now',
passengers: 1,
luggage: 1,
animal: false,
forOther: false,
currentRide: null
};
const views = ['home', 'drivers', 'rides', 'profile'];
function showView(name) {
views.forEach(v => {
const el = document.getElementById('view-' + v);
if (el) {
el.classList.add('hidden');
el.classList.remove('flex');
el.classList.remove('block');
}
});
const target = document.getElementById('view-' + name);
if (target) {
target.classList.remove('hidden');
if (target.tagName.toLowerCase() === 'main') {
target.classList.add('block');
} else {
target.classList.add('block');
}
target.scrollTop = 0;
}
document.querySelectorAll('.nav-btn').forEach(btn => {
if (btn.dataset.view === name) {
btn.classList.remove('text-white/40');
btn.classList.add('text-emerald-400');
} else {
btn.classList.remove('text-emerald-400');
btn.classList.add('text-white/40');
}
});
}
function openModal(id) {
const el = document.getElementById(id);
if (!el) return;
el.classList.remove('hidden');
void el.offsetWidth;
el.classList.add('active');
lucide.createIcons();
}
function closeModal(id) {
const el = document.getElementById(id);
if (!el) return;
el.classList.remove('active');
setTimeout(() => el.classList.add('hidden'), 300);
}
function renderDrivers() {
const container = document.getElementById('drivers-list');
if (!container) return;
if (state.drivers.length === 0) {
container.innerHTML = '<p class="text-sm text-white/45 text-center py-8">Aucun chauffeur dans votre réseau.</p>';
lucide.createIcons();
return;
}
container.innerHTML = state.drivers.map(d => {
const statusColor = d.status === 'Disponible' ? 'text-emerald-400' : d.status === 'Occupé' ? 'text-amber-400' : 'text-white/40';
return `
<div class="bg-[#151821] border border-white/10 rounded-2xl p-4 shadow-sm flex items-center gap-4">
<div class="relative shrink-0">
<img src="${d.photo}" class="w-12 h-12 rounded-full object-cover opacity-90 border border-white/10">
<span class="absolute bottom-0 right-0 w-3 h-3 ${d.status === 'Disponible' ? 'bg-emerald-400' : d.status === 'Occupé' ? 'bg-amber-400' : 'bg-gray-500'} rounded-full border-[2.5px] border-[#151821]"></span>
</div>
<div class="flex-1 min-w-0">
<div class="flex items-center gap-2">
<p class="text-sm font-semibold text-white">${d.name}</p>
<button onclick="setFavoriteDriver('${d.id}')" class="press">
<i data-lucide="star" class="w-4 h-4 ${d.favorite ? 'text-yellow-400 fill-yellow-400' : 'text-white/30'}"></i>
</button>
</div>
<p class="text-xs ${statusColor}">${d.status}</p>
<p class="text-xs text-white/45 mt-0.5">${d.vehicle}${d.rate.toFixed(1).replace('.0','')} €/km • ${d.rides} courses</p>
</div>
<div class="flex gap-2 items-center">
${d.phone ? `<a href="tel:${d.phone}" class="press h-8 w-8 rounded-full bg-[#1a1f2b] border border-white/10 flex items-center justify-center text-emerald-400"><i data-lucide="phone" class="w-4 h-4"></i></a>` : ''}
<button onclick="selectDriver('${d.id}')" class="press h-8 px-3 rounded-full bg-emerald-500/10 border border-emerald-500/20 text-xs font-semibold text-emerald-400 hover:bg-emerald-500/20 transition-colors">Choisir</button>
<button onclick="removeDriver('${d.id}')" class="press h-8 w-8 rounded-full bg-[#1a1f2b] border border-white/10 flex items-center justify-center text-red-400">
<i data-lucide="trash-2" class="w-4 h-4"></i>
</button>
</div>
</div>
`;
}).join('');
lucide.createIcons();
}
function setFavoriteDriver(id) {
state.drivers.forEach(d => d.favorite = (d.id === id));
renderDrivers();
renderSelectedDriver();
renderEstimate();
}
function removeDriver(id) {
const wasFav = state.drivers.find(d => d.id === id)?.favorite;
state.drivers = state.drivers.filter(d => d.id !== id);
if (wasFav && state.drivers.length > 0) {
state.drivers[0].favorite = true;
}
renderDrivers();
renderSelectedDriver();
renderEstimate();
}
function selectDriver(id) {
setFavoriteDriver(id);
setSegment('fav');
showView('home');
}
function selectDriverFromModal(id) {
setFavoriteDriver(id);
setSegment('fav');
renderSelectedDriver();
renderEstimate();
closeModal('modal-select-driver');
closeModal('modal-available-drivers');
}
function renderSelectedDriver() {
const block = document.getElementById('driver-block');
if (!block) return;
if (state.segment === 'group') {
block.innerHTML = `
<div class="flex items-center gap-3 pointer-events-none">
<div class="w-8 h-8 rounded-full bg-emerald-500/10 border border-emerald-500/20 flex items-center justify-center text-emerald-400">
<i data-lucide="users" class="w-4 h-4"></i>
</div>
<div>
<p class="text-sm font-medium text-white">Tous mes chauffeurs</p>
<p class="text-[10px] text-emerald-400">Demande groupée</p>
</div>
</div>
<div class="flex items-center gap-2 pointer-events-none">
<i data-lucide="chevron-right" class="w-4 h-4 text-white/30"></i>
</div>
`;
} else {
const fav = state.drivers.find(d => d.favorite) || state.drivers[0];
if (!fav) {
block.innerHTML = '<p class="text-sm text-white/45">Aucun chauffeur favori</p>';
lucide.createIcons();
return;
}
block.innerHTML = `
<div class="flex items-center gap-3 pointer-events-none">
<div class="relative">
<img src="${fav.photo}" class="w-8 h-8 rounded-full object-cover border border-white/10">
<span class="absolute bottom-0 right-0 w-2.5 h-2.5 ${fav.status === 'Disponible' ? 'bg-emerald-400' : fav.status === 'Occupé' ? 'bg-amber-400' : 'bg-gray-500'} rounded-full border-2 border-[#0b0b0f]"></span>
</div>
<div>
<p class="text-sm font-medium text-white">${fav.name}</p>
<p class="text-[10px] ${fav.status === 'Disponible' ? 'text-emerald-400' : fav.status === 'Occupé' ? 'text-amber-400' : 'text-white/40'}">${fav.status}</p>
</div>
</div>
<div class="flex items-center gap-2 pointer-events-auto">
${fav.phone ? `<a href="tel:${fav.phone}" onclick="event.stopPropagation()" class="press h-7 w-7 rounded-full bg-[#1a1f2b] border border-white/10 flex items-center justify-center text-emerald-400"><i data-lucide="phone" class="w-3.5 h-3.5"></i></a>` : ''}
<i data-lucide="chevron-right" class="w-4 h-4 text-white/30 pointer-events-none"></i>
</div>
`;
}
block.classList.add('cursor-pointer');
block.onclick = openSelectDriverSheet;
lucide.createIcons();
}
function renderEstimate() {
const dep = document.getElementById('input-depart')?.value?.trim();
const arr = document.getElementById('input-arrivee')?.value?.trim();
const block = document.getElementById('estimate-block');
const priceEl = document.getElementById('estimate-price');
const preview = document.getElementById('route-preview');
if (!dep || !arr) {
block?.classList.add('hidden');
preview?.classList.add('hidden');
return;
}
block?.classList.remove('hidden');
preview?.classList.remove('hidden');
const distance = 12;
let price = 0;
if (state.segment === 'fav') {
const fav = state.drivers.find(d => d.favorite) || state.drivers[0];
if (fav) price = distance * fav.rate;
} else {
const availableRates = state.drivers.filter(d => d.available).map(d => d.rate);
const minRate = availableRates.length > 0 ? Math.min(...availableRates) : 0;
price = distance * minRate;
}
if (priceEl) priceEl.textContent = price.toFixed(2).replace('.00','') + ' €';
}
function setSegment(type) {
state.segment = type;
const favBtn = document.getElementById('seg-fav');
const groupBtn = document.getElementById('seg-group');
if (type === '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';
} 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';
}
renderSelectedDriver();
renderEstimate();
}
function addStop() {
const container = document.getElementById('extra-stops');
const div = document.createElement('div');
div.className = 'flex items-center gap-3 bg-[#0b0b0f] border border-white/10 rounded-xl px-3 py-3 animate-fade-in';
div.innerHTML = `
<div class="w-2 h-2 rounded-full bg-amber-400 shrink-0"></div>
<input type="text" placeholder="Adresse de l'arrêt" class="w-full bg-transparent text-sm text-white placeholder-white/30 focus:outline-none" />
<button onclick="this.parentElement.remove()" class="text-white/30 hover:text-red-400 transition-colors press"><i data-lucide="x" class="w-4 h-4"></i></button>
`;
container.appendChild(div);
lucide.createIcons();
}
function setWhen(type) {
state.when = type;
const btnNow = document.getElementById('btn-now');
const btnLater = document.getElementById('btn-later');
if (type === 'now') {
btnNow.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';
btnLater.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 {
btnNow.className = 'press flex-1 py-2.5 rounded-xl border text-sm font-medium transition-all bg-transparent border-white/10 text-white/45';
btnLater.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';
openModal('modal-datetime');
}
}
function confirmDateTime() {
closeModal('modal-datetime');
}
function adjust(key, delta) {
state[key] = Math.max(0, state[key] + delta);
if (key === 'passengers' && state[key] < 1) state[key] = 1;
const el = document.getElementById('val-' + key);
if (el) el.textContent = state[key];
}
function toggleAnimal() {
state.animal = !state.animal;
const btn = document.getElementById('btn-animal');
if (state.animal) {
btn.textContent = 'Oui';
btn.classList.remove('border-white/10','text-white/45');
btn.classList.add('border-emerald-500/30','bg-emerald-500/10','text-emerald-400');
} else {
btn.textContent = 'Non';
btn.classList.add('border-white/10','text-white/45');
btn.classList.remove('border-emerald-500/30','bg-emerald-500/10','text-emerald-400');
}
}
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.classList.remove('border-white/10','text-white/45');
btn.classList.add('border-emerald-500/30','bg-emerald-500/10','text-emerald-400');
fields.classList.remove('hidden');
} else {
btn.textContent = 'Non';
btn.classList.add('border-white/10','text-white/45');
btn.classList.remove('border-emerald-500/30','bg-emerald-500/10','text-emerald-400');
fields.classList.add('hidden');
}
}
function sendRequest() {
state.isReturnTrip = false;
renderReturnBadge();
openModal('modal-loading');
const title = document.getElementById('loading-title');
const sub = document.getElementById('loading-sub');
title.textContent = 'Demande envoyée';
if (state.segment === 'group') {
sub.textContent = 'Vos chauffeurs de confiance reçoivent la demande…';
setTimeout(() => { sub.textContent = 'Karim consulte…'; }, 1800);
setTimeout(() => { sub.textContent = 'Sofiane consulte…'; }, 2800);
setTimeout(() => { sub.textContent = 'Sofiane confirme votre trajet'; title.textContent = 'Course confirmée'; }, 3800);
setTimeout(() => { closeModal('modal-loading'); openModal('modal-return'); }, 4800);
} else {
const fav = state.drivers.find(d => d.favorite) || { name: 'Karim' };
sub.textContent = fav.name + ' consulte votre demande…';
setTimeout(() => { sub.textContent = 'Votre chauffeur vérifie sa disponibilité'; }, 1800);
setTimeout(() => { sub.textContent = fav.name + ' confirme votre trajet'; title.textContent = 'Course confirmée'; }, 3200);
setTimeout(() => { closeModal('modal-loading'); openModal('modal-return'); }, 4200);
}
}
function renderReturnBadge() {
const badge = document.getElementById('return-badge');
if (!badge) return;
if (state.isReturnTrip) {
badge.classList.remove('hidden');
} else {
badge.classList.add('hidden');
}
}
function handleReturn(yes) {
closeModal('modal-return');
if (yes) {
const dep = document.getElementById('input-depart').value;
const arr = document.getElementById('input-arrivee').value;
document.getElementById('input-depart').value = arr;
document.getElementById('input-arrivee').value = dep;
document.getElementById('extra-stops').innerHTML = '';
state.isReturnTrip = true;
renderReturnBadge();
renderEstimate();
window.scrollTo({top:0, behavior:'smooth'});
} else {
document.getElementById('input-depart').value = '';
document.getElementById('input-arrivee').value = '';
document.getElementById('extra-stops').innerHTML = '';
state.isReturnTrip = false;
renderReturnBadge();
renderEstimate();
}
}
function showManualCode() {
document.getElementById('manual-code-area').classList.remove('hidden');
}
function addManualCode() {
const val = document.getElementById('manual-code').value;
if (val.trim()) {
closeModal('modal-qr');
}
}
function renderRides(filter) {
const filters = ['today','upcoming','cancelled','past'];
filters.forEach(f => {
const btn = document.getElementById('filter-' + f);
if (!btn) return;
if (f === filter) {
btn.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';
} else {
btn.className = 'press px-3 py-1.5 rounded-full text-xs font-medium bg-[#151821] text-white/60 border border-white/10';
}
});
const list = document.getElementById('rides-list');
const filtered = state.rides.filter(r => r.filter === filter);
if (filtered.length === 0) {
list.innerHTML = '<p class="text-sm text-white/45 text-center py-8">Aucune course dans cette catégorie.</p>';
return;
}
list.innerHTML = filtered.map(r => {
const statusColor = r.status === 'Confirmée' ? 'bg-emerald-500/10 text-emerald-400' : r.status === 'En attente' ? 'bg-amber-500/10 text-amber-400' : r.status === 'Annulée' ? 'bg-red-500/10 text-red-400' : 'bg-blue-500/10 text-blue-400';
return `
<div class="bg-[#151821] border border-white/10 rounded-2xl p-4">
<div class="flex items-center justify-between mb-3">
<span class="text-xs text-white/45">${r.date}</span>
<span class="px-2 py-0.5 rounded-full ${statusColor} text-[10px] font-medium">${r.status}</span>
</div>
<p class="text-sm text-white">${r.from}${r.to}</p>
<div class="flex items-center justify-between mt-3">
<p class="text-xs text-white/45">Chauffeur : ${r.driver}</p>
${r.amount ? `<p class="text-xs font-medium text-white">${r.amount}</p>` : ''}
</div>
<button onclick="openRideDetail(${r.id})" class="press mt-3 w-full py-2 rounded-xl bg-[#1a1f2b] border border-white/10 text-xs font-medium text-white/80">Détail</button>
</div>
`;
}).join('');
}
function openRideDetail(id) {
const ride = state.rides.find(r => r.id === id);
if (!ride) return;
state.currentRide = ride;
const content = document.getElementById('ride-detail-content');
const statusColor = ride.status === 'Confirmée' ? 'text-emerald-400' : ride.status === 'En attente' ? 'text-amber-400' : ride.status === 'Annulée' ? 'text-red-400' : 'text-blue-400';
let docButton = '';
if (ride.status === 'Annulée') {
docButton = '<p class="text-xs text-white/40 mt-2">Aucun document disponible pour une course annulée.</p>';
} else if (ride.filter === 'today' || ride.filter === 'upcoming') {
docButton = `<button onclick="openDocument('bdc')" class="press mt-3 w-full py-3 rounded-xl bg-emerald-500 text-[#0b0b0f] text-sm font-semibold">Voir le bon de commande</button>`;
} else if (ride.filter === 'past') {
docButton = `<button onclick="openDocument('invoice')" class="press mt-3 w-full py-3 rounded-xl bg-emerald-500 text-[#0b0b0f] text-sm font-semibold">Voir la facture</button>`;
}
content.innerHTML = `
<div class="space-y-3">
<div class="flex justify-between items-center">
<span class="text-xs text-white/45">Date / Heure</span>
<span class="text-sm text-white">${ride.date}</span>
</div>
<div class="flex justify-between items-center">
<span class="text-xs text-white/45">Trajet</span>
<span class="text-sm text-white text-right">${ride.from}${ride.to}</span>
</div>
<div class="flex justify-between items-center">
<span class="text-xs text-white/45">Chauffeur</span>
<span class="text-sm text-white">${ride.driver}</span>
</div>
<div class="flex justify-between items-center">
<span class="text-xs text-white/45">Véhicule</span>
<span class="text-sm text-white">${ride.vehicle}</span>
</div>
<div class="flex justify-between items-center">
<span class="text-xs text-white/45">Distance</span>
<span class="text-sm text-white">${ride.distance}</span>
</div>
${ride.rate ? `<div class="flex justify-between items-center"><span class="text-xs text-white/45">Tarif/km</span><span class="text-sm text-white">${ride.rate} €/km</span></div>` : ''}
${ride.amount && ride.amount !== '-' ? `<div class="flex justify-between items-center pt-2 border-t border-white/10"><span class="text-xs text-white/45">Montant</span><span class="text-sm font-semibold text-white">${ride.amount}</span></div>` : ''}
<div class="flex justify-between items-center">
<span class="text-xs text-white/45">Statut</span>
<span class="text-sm font-medium ${statusColor}">${ride.status}</span>
</div>
<div class="flex justify-between items-center">
<span class="text-xs text-white/45">Passagers</span>
<span class="text-sm text-white">${ride.passengers}</span>
</div>
<div class="flex justify-between items-center">
<span class="text-xs text-white/45">Bagages</span>
<span class="text-sm text-white">${ride.luggage}</span>
</div>
${docButton}
</div>
`;
openModal('modal-ride-detail');
}
function openDocument(type) {
const ride = state.currentRide;
if (!ride) return;
if (type === 'bdc') {
document.getElementById('bdc-date').textContent = new Date().toLocaleDateString('fr-FR');
document.getElementById('bdc-datetime').textContent = ride.date;
document.getElementById('bdc-trajet').textContent = ride.from + ' → ' + ride.to;
document.getElementById('bdc-distance').textContent = 'Distance estimée : ' + (ride.distance || '12 km');
document.getElementById('bdc-rate').textContent = 'Tarif kilométrique : ' + (ride.rate ? ride.rate + ' €/km' : 'sur devis');
document.getElementById('bdc-price').textContent = 'Prix indicatif TTC : ' + (ride.amount || '24 €');
openModal('modal-bdc');
} else if (type === 'invoice') {
document.getElementById('invoice-date').textContent = new Date().toLocaleDateString('fr-FR');
document.getElementById('invoice-prestation').textContent = ride.date;
document.getElementById('invoice-time').textContent = ride.date.split('à')[1]?.trim() || '14:30';
openModal('modal-invoice');
}
}
function renderProfile() {
const container = document.getElementById('relatives-list');
if (!container) return;
container.innerHTML = state.relatives.map(r => `
<div class="bg-[#1a1f2b] border border-white/10 rounded-xl p-3 flex items-center justify-between">
<div>
<p class="text-sm font-medium text-white">${r.firstName} ${r.lastName}</p>
<p class="text-xs text-white/45">${r.phone}</p>
</div>
</div>
`).join('');
}
function addRelative() {
const first = document.getElementById('rel-firstname').value.trim();
const last = document.getElementById('rel-lastname').value.trim();
const phone = document.getElementById('rel-phone').value.trim();
if (!first || !last) return;
state.relatives.push({ firstName: first, lastName: last, phone: phone || '—' });
document.getElementById('rel-firstname').value = '';
document.getElementById('rel-lastname').value = '';
document.getElementById('rel-phone').value = '';
renderProfile();
closeModal('modal-add-relative');
}
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)';
}
}
function openSelectDriverSheet() {
const container = document.getElementById('select-driver-list');
if (!container) return;
if (state.drivers.length === 0) {
container.innerHTML = '<p class="text-sm text-white/45 text-center py-4">Aucun chauffeur.</p>';
} else {
container.innerHTML = state.drivers.map(d => {
const statusColor = d.status === 'Disponible' ? 'text-emerald-400' : d.status === 'Occupé' ? 'text-amber-400' : 'text-white/40';
return `
<div class="bg-[#1a1f2b] border border-white/10 rounded-xl p-3 flex items-center gap-3">
<div class="relative shrink-0">
<img src="${d.photo}" class="w-10 h-10 rounded-full object-cover border border-white/10">
<span class="absolute bottom-0 right-0 w-2.5 h-2.5 ${d.status === 'Disponible' ? 'bg-emerald-400' : d.status === 'Occupé' ? 'bg-amber-400' : 'bg-gray-500'} rounded-full border-2 border-[#1a1f2b]"></span>
</div>
<div class="flex-1 min-w-0">
<p class="text-sm font-medium text-white">${d.name}</p>
<p class="text-xs ${statusColor}">${d.status}</p>
<p class="text-xs text-white/45">${d.vehicle}${d.rate.toFixed(1).replace('.0','')} €/km</p>
</div>
<div class="flex gap-2 items-center">
${d.phone ? `<a href="tel:${d.phone}" class="press h-8 w-8 rounded-full bg-[#151821] border border-white/10 flex items-center justify-center text-emerald-400"><i data-lucide="phone" class="w-4 h-4"></i></a>` : ''}
<button onclick="selectDriverFromModal('${d.id}')" class="press h-8 px-3 rounded-full bg-emerald-500/10 border border-emerald-500/20 text-xs font-semibold text-emerald-400">Choisir</button>
</div>
</div>
`;
}).join('');
}
openModal('modal-select-driver');
}
function openAvailableDriversSheet() {
const container = document.getElementById('available-drivers-list');
if (!container) return;
const available = state.drivers.filter(d => d.available);
if (available.length === 0) {
container.innerHTML = '<p class="text-sm text-white/45 text-center py-4">Aucun chauffeur disponible.</p>';
} else {
container.innerHTML = available.map(d => `
<div class="bg-[#1a1f2b] border border-white/10 rounded-xl p-3 flex items-center gap-3">
<div class="relative shrink-0">
<img src="${d.photo}" class="w-10 h-10 rounded-full object-cover border border-white/10">
<span class="absolute bottom-0 right-0 w-2.5 h-2.5 bg-emerald-400 rounded-full border-2 border-[#1a1f2b]"></span>
</div>
<div class="flex-1 min-w-0">
<p class="text-sm font-medium text-white">${d.name}</p>
<p class="text-xs text-emerald-400">Disponible</p>
<p class="text-xs text-white/45">${d.vehicle}</p>
</div>
<div class="flex gap-2 items-center">
${d.phone ? `<a href="tel:${d.phone}" class="press h-8 w-8 rounded-full bg-[#151821] border border-white/10 flex items-center justify-center text-emerald-400"><i data-lucide="phone" class="w-4 h-4"></i></a>` : ''}
<button onclick="selectDriverFromModal('${d.id}')" class="press h-8 px-3 rounded-full bg-emerald-500/10 border border-emerald-500/20 text-xs font-semibold text-emerald-400">Choisir</button>
</div>
</div>
`).join('');
}
openModal('modal-available-drivers');
}
function renderFavoriteAddressPills() {
const departContainer = document.getElementById('favorite-depart-pills');
const arriveeContainer = document.getElementById('favorite-arrivee-pills');
if (!departContainer || !arriveeContainer) return;
const pills = (target) => state.favoriteAddresses.map(a => `
<button onclick="setFavoriteAddress('${target}', '${a.id}')" class="press shrink-0 bg-[#151821] border border-white/10 rounded-full px-3 py-1.5 text-xs text-white/55 flex items-center gap-1.5 hover:border-emerald-500/30 hover:text-emerald-400 transition-colors">
<i data-lucide="${a.icon || 'map-pin'}" class="w-3 h-3"></i>
${a.label}
</button>
`).join('');
departContainer.innerHTML = pills('depart');
arriveeContainer.innerHTML = pills('arrivee');
lucide.createIcons();
}
function setFavoriteAddress(target, id) {
const addr = state.favoriteAddresses.find(a => a.id === id);
if (!addr) return;
if (target === 'depart') {
const input = document.getElementById('input-depart');
if (input) input.value = addr.address;
} else if (target === 'arrivee') {
const input = document.getElementById('input-arrivee');
if (input) input.value = addr.address;
}
renderEstimate();
}
function renderFavoriteAddressesProfile() {
const container = document.getElementById('profile-favorite-addresses');
if (!container) return;
if (state.favoriteAddresses.length === 0) {
container.innerHTML = '<p class="text-xs text-white/45">Aucune adresse favorite.</p>';
return;
}
container.innerHTML = state.favoriteAddresses.map(a => `
<div onclick="openEditAddress('${a.id}')" class="cursor-pointer bg-[#1a1f2b] border border-white/10 rounded-xl p-3 flex items-center justify-between press">
<div class="min-w-0">
<div class="flex items-center gap-2">
<i data-lucide="${a.icon || 'map-pin'}" class="w-4 h-4 text-white/45"></i>
<p class="text-sm font-medium text-white">${a.label}</p>
</div>
<p class="text-xs text-white/45 mt-0.5 truncate">${a.address}</p>
</div>
<span class="text-[10px] text-white/30 uppercase tracking-wider border border-white/10 px-2 py-0.5 rounded-full shrink-0">${a.tag}</span>
</div>
`).join('');
lucide.createIcons();
}
function addFavoriteAddress() {
const label = document.getElementById('addr-label').value.trim();
const address = document.getElementById('addr-address').value.trim();
const tag = document.getElementById('addr-tag').value.trim() || label.toLowerCase();
const icon = document.getElementById('addr-icon')?.value || 'map-pin';
if (!label || !address) return;
const id = 'addr_' + Date.now();
state.favoriteAddresses.push({ id, label, tag, icon, address });
document.getElementById('addr-label').value = '';
document.getElementById('addr-address').value = '';
document.getElementById('addr-tag').value = '';
if (document.getElementById('addr-icon')) document.getElementById('addr-icon').value = 'map-pin';
closeModal('modal-add-address');
renderFavoriteAddressesProfile();
renderFavoriteAddressPills();
}
let editingAddressId = null;
function openEditAddress(id) {
const addr = state.favoriteAddresses.find(a => a.id === id);
if (!addr) return;
editingAddressId = id;
document.getElementById('edit-addr-id').value = id;
document.getElementById('edit-addr-label').value = addr.label;
document.getElementById('edit-addr-address').value = addr.address;
document.getElementById('edit-addr-tag').value = addr.tag;
const iconSelect = document.getElementById('edit-addr-icon');
if (iconSelect) iconSelect.value = addr.icon || 'map-pin';
openModal('modal-edit-address');
}
function saveEditedAddress() {
if (!editingAddressId) return;
const addr = state.favoriteAddresses.find(a => a.id === editingAddressId);
if (!addr) return;
addr.label = document.getElementById('edit-addr-label').value.trim();
addr.address = document.getElementById('edit-addr-address').value.trim();
addr.tag = document.getElementById('edit-addr-tag').value.trim();
const iconSelect = document.getElementById('edit-addr-icon');
if (iconSelect) addr.icon = iconSelect.value;
editingAddressId = null;
closeModal('modal-edit-address');
renderFavoriteAddressesProfile();
renderFavoriteAddressPills();
}
function deleteFavoriteAddress(id) {
if (!id && editingAddressId) id = editingAddressId;
if (!id) return;
state.favoriteAddresses = state.favoriteAddresses.filter(a => a.id !== id);
editingAddressId = null;
closeModal('modal-edit-address');
renderFavoriteAddressesProfile();
renderFavoriteAddressPills();
}
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';
if (companyFields) 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';
if (companyFields) companyFields.classList.remove('hidden');
}
}
function saveProfile() {
const first = document.getElementById('profile-firstname').value.trim();
const last = document.getElementById('profile-lastname').value.trim();
const phone = document.getElementById('profile-phone').value.trim();
const email = document.getElementById('profile-email').value.trim();
const address = document.getElementById('profile-address').value.trim();
if (!first || !last) return;
state.profile = { firstName: first, lastName: last, phone, email, address };
state.billingAddress = address;
const nameDisplay = document.getElementById('profile-name-display');
const contactDisplay = document.getElementById('profile-contact-display');
const typeDisplay = document.getElementById('profile-type-display');
if (nameDisplay) nameDisplay.textContent = first + ' ' + last;
if (contactDisplay) contactDisplay.textContent = (phone ? phone + ' • ' : '') + (email || '');
if (typeDisplay) {
if (state.customerType === 'professionnel' && state.company.name) {
typeDisplay.textContent = 'Professionnel • ' + state.company.name;
} else {
typeDisplay.textContent = state.customerType === 'particulier' ? 'Particulier' : 'Professionnel';
}
}
if (state.customerType === 'professionnel') {
state.company.name = document.getElementById('company-name').value.trim();
state.company.siret = document.getElementById('company-siret').value.trim();
state.company.vat = document.getElementById('company-vat').value.trim();
state.company.billingEmail = document.getElementById('company-billing-email').value.trim();
}
closeModal('modal-edit-profile');
}
window.addEventListener('DOMContentLoaded', () => {
lucide.createIcons();
showView('home');
renderDrivers();
renderSelectedDriver();
renderRides('today');
renderProfile();
renderFavoriteAddressPills();
renderFavoriteAddressesProfile();
const today = new Date().toISOString().split('T')[0];
const dp = document.getElementById('date-picker');
if (dp) dp.value = today;
const tp = document.getElementById('time-picker');
if (tp) tp.value = '14:30';
const depInput = document.getElementById('input-depart');
const arrInput = document.getElementById('input-arrivee');
if (depInput) depInput.addEventListener('input', renderEstimate);
if (arrInput) arrInput.addEventListener('input', renderEstimate);
});