Yocapp's picture
Initial DeepSite commit
d5414ad verified
Raw
History Blame Contribute Delete
29.7 kB
// ==========================
// 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 = `
<div id="stop-${id}" class="flex items-center gap-3 bg-[#0b0b0f] border border-white/10 rounded-xl px-3 py-3">
<div class="w-2 h-2 rounded-full bg-amber-400 shrink-0"></div>
<input type="text" placeholder="Arrêt intermédiaire" class="w-full bg-transparent text-sm text-white placeholder-white/30 focus:outline-none" />
<button onclick="removeStop(${id})" class="press text-white/30 hover:text-red-400 transition-colors">
<i data-lucide="x" class="w-4 h-4"></i>
</button>
</div>
`;
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 => `
<div class="flex items-center justify-between bg-[#151821] border border-white/10 rounded-xl px-3 py-3">
<div class="flex items-center gap-3">
<div class="relative">
<img src="${d.img}" 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.available ? 'bg-emerald-400' : 'bg-gray-500'} rounded-full border-2 border-[#151821]"></span>
</div>
<div>
<p class="text-sm font-medium text-white">${d.name}</p>
<p class="text-[10px] ${d.available ? 'text-emerald-400' : 'text-white/40'}">${d.available ? 'Disponible' : 'Indisponible'}</p>
</div>
</div>
<div class="flex items-center gap-2">
<span class="text-xs text-white/45">${d.rating} ★</span>
<i data-lucide="chevron-right" class="w-4 h-4 text-white/30"></i>
</div>
</div>
`).join('');
lucide.createIcons();
}
function openAvailableDriversSheet() {
const list = document.getElementById('available-drivers-list');
const available = drivers.filter(d => d.available);
list.innerHTML = available.map(d => `
<div class="flex items-center justify-between bg-[#0b0b0f] border border-white/10 rounded-xl px-3 py-3">
<div class="flex items-center gap-3">
<img src="${d.img}" class="w-10 h-10 rounded-full object-cover border border-white/10">
<div>
<p class="text-sm font-medium text-white">${d.name}</p>
<p class="text-[10px] text-emerald-400">Disponible maintenant</p>
</div>
</div>
<button onclick="selectDriver(${d.id}); closeModal('modal-available-drivers')" class="press px-3 py-1.5 rounded-lg bg-emerald-500/10 border border-emerald-500/20 text-emerald-400 text-xs font-medium">Choisir</button>
</div>
`).join('');
openModal('modal-available-drivers');
lucide.createIcons();
}
function openSelectDriverSheet() {
const list = document.getElementById('select-driver-list');
list.innerHTML = drivers.map(d => `
<div onclick="selectDriver(${d.id}); closeModal('modal-select-driver')" class="cursor-pointer flex items-center justify-between bg-[#0b0b0f] border border-white/10 rounded-xl px-3 py-3">
<div class="flex items-center gap-3 pointer-events-none">
<div class="relative">
<img src="${d.img}" 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.available ? 'bg-emerald-400' : 'bg-gray-500'} rounded-full border-2 border-[#0b0b0f]"></span>
</div>
<div>
<p class="text-sm font-medium text-white">${d.name}</p>
<p class="text-[10px] ${d.available ? 'text-emerald-400' : 'text-white/40'}">${d.available ? 'Disponible' : 'Indisponible'}</p>
</div>
</div>
${state.selectedDriver.id === d.id ? '<span class="text-emerald-400 text-xs font-medium">Sélectionné</span>' : ''}
</div>
`).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 = '<p class="text-sm text-white/45 text-center py-8">Aucune course</p>';
return;
}
container.innerHTML = filtered.map(r => `
<div onclick="openRideDetail(${r.id})" class="cursor-pointer bg-[#151821] border border-white/10 rounded-xl p-4 space-y-2">
<div class="flex items-center justify-between">
<span class="text-xs font-medium uppercase tracking-wider ${getStatusColor(r.status)}">${getStatusLabel(r.status)}</span>
<span class="text-xs text-white/45">${r.date}${r.time}</span>
</div>
<div class="space-y-1">
<div class="flex items-center gap-2">
<div class="w-1.5 h-1.5 rounded-full bg-emerald-400"></div>
<p class="text-sm text-white/80 truncate">${r.depart}</p>
</div>
<div class="flex items-center gap-2">
<div class="w-1.5 h-1.5 rounded-full bg-red-400"></div>
<p class="text-sm text-white/80 truncate">${r.arrivee}</p>
</div>
</div>
<div class="flex items-center justify-between pt-2 border-t border-white/10">
<span class="text-xs text-white/45">${r.driver}</span>
<span class="text-sm font-semibold text-white">${r.price > 0 ? r.price + ' €' : '-'}</span>
</div>
</div>
`).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 = `
<div class="space-y-3 text-white/65">
<div class="flex justify-between py-2 border-b border-white/10">
<span>Statut</span>
<span class="${getStatusColor(r.status)}">${getStatusLabel(r.status)}</span>
</div>
<div class="flex justify-between py-2 border-b border-white/10">
<span>Départ</span>
<span class="text-white text-right">${r.depart}</span>
</div>
<div class="flex justify-between py-2 border-b border-white/10">
<span>Arrivée</span>
<span class="text-white text-right">${r.arrivee}</span>
</div>
<div class="flex justify-between py-2 border-b border-white/10">
<span>Chauffeur</span>
<span class="text-white">${r.driver}</span>
</div>
<div class="flex justify-between py-2 border-b border-white/10">
<span>Date</span>
<span class="text-white">${r.date} à ${r.time}</span>
</div>
<div class="flex justify-between py-2">
<span>Prix estimé</span>
<span class="text-white font-medium">${r.price > 0 ? r.price + ' €' : '-'}</span>
</div>
</div>
${r.status === 'past' ? `
<div class="flex gap-3 mt-4">
<button onclick="openModal('modal-bdc')" class="press flex-1 h-11 rounded-xl bg-[#1a1f2b] border border-white/10 text-white text-xs font-medium">Bon de commande</button>
<button onclick="openModal('modal-invoice')" class="press flex-1 h-11 rounded-xl bg-emerald-500 text-[#0b0b0f] text-xs font-semibold">Facture</button>
</div>
` : ''}
`;
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 => `
<div class="flex items-center justify-between bg-[#151821] border border-white/10 rounded-xl px-3 py-3">
<div>
<p class="text-sm text-white font-medium">${r.firstname} ${r.lastname}</p>
<p class="text-xs text-white/45">${r.phone}</p>
</div>
<a href="tel:${r.phone}" 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>
</div>
`).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 => `
<div onclick="openEditAddress(${a.id})" class="cursor-pointer flex items-center gap-3 bg-[#151821] border border-white/10 rounded-xl px-3 py-3">
<div class="w-8 h-8 rounded-lg bg-[#1a1f2b] border border-white/10 flex items-center justify-center shrink-0">
<i data-lucide="${a.icon}" class="w-4 h-4 text-white/60"></i>
</div>
<div class="flex-1 min-w-0">
<p class="text-sm text-white font-medium">${a.label}</p>
<p class="text-xs text-white/45 truncate">${a.address}</p>
</div>
<i data-lucide="chevron-right" class="w-4 h-4 text-white/30 shrink-0"></i>
</div>
`).join('');
container.innerHTML = html;
const pillHtml = favoriteAddresses.map(a => `
<button onclick="fillAddress('${a.address}')" class="press shrink-0 bg-[#1a1f2b] border border-white/10 rounded-full px-2.5 py-1 text-[10px] text-white/65 flex items-center gap-1">
<i data-lucide="${a.icon}" class="w-3 h-3"></i>
${a.label}
</button>
`).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)';
}
}