Файловый менеджер - Редактировать - /home/gqdcvggs/imators.systems/quarter-app/header-mobile.php
Назад
<!DOCTYPE html> <html lang="fr"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <link rel="icon" type="image/png" href="logo_new.png"> <title>Vert Chasseur</title> <meta name="description" content="Vert Chasseur est un quartier à Uccle où vous trouverez de la joie et un havre de paix accueillant. Découvrez les commerces partenaire du quartier et les événements qui pourrait vous intéresser."> <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=Poppins:wght@300;400;500;600&display=swap" rel="stylesheet"> <link href="https://fonts.googleapis.com/css2?family=Princess+Sofia&display=swap" rel="stylesheet"> <link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" /> <script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script> <script defer src="https://cloud.umami.is/script.js" data-website-id="6ec552ef-4a61-4f71-a55f-4464703af623"></script> <style> body { font-family: 'Poppins', sans-serif; letter-spacing: -0.01em; padding-bottom: 80px; } .card-hover { transition: transform 0.3s ease; } .card-hover:hover { transform: translateY(-4px); } .arrow-hover { transition: transform 0.3s ease; } .card-hover:hover .arrow-hover { transform: translateX(8px); } .txt-chasseur { font-family: "Princess Sofia", serif; font-weight: 400; font-style: normal; } .bottom-nav { backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px); } .nav-item { transition: all 0.3s ease; } .nav-item:hover { transform: translateY(-2px); } .nav-item.active { color: #10b981; } .nav-item.active svg { stroke: #10b981; } .delivery-modal { position: fixed; bottom: 0; left: 0; right: 0; background: white; border-top-left-radius: 20px; border-top-right-radius: 20px; transform: translateY(100%); transition: transform 0.3s ease; z-index: 2000; box-shadow: 0 -10px 30px rgba(0, 0, 0, 0.2); max-height: 80vh; overflow-y: auto; } .dark .delivery-modal { background: #1c1917; color: white; } .delivery-modal.show { transform: translateY(0); } .delivery-modal-handle { width: 40px; height: 4px; background: #d6d3d1; border-radius: 2px; margin: 12px auto 20px; } .dark .delivery-modal-handle { background: #57534e; } .modal-backdrop { position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0, 0, 0, 0.5); z-index: 1999; opacity: 0; visibility: hidden; transition: opacity 0.3s ease, visibility 0.3s ease; } .modal-backdrop.show { opacity: 1; visibility: visible; } #map { height: 200px; border-radius: 0.5rem; } .step { opacity: 0.5; } .step.active { opacity: 1; color: #059669; } .step.completed { opacity: 1; color: #10b981; } .leaflet-control-container { display: none !important; } </style> </head> <body> <nav class="bottom-nav fixed bottom-0 left-0 right-0 bg-white/90 dark:bg-black/90 border-t border-gray-200 dark:border-gray-700 z-40"> <div class="flex justify-around items-center py-2"> <a href="/media-mobile" class="nav-item flex flex-col items-center p-3 text-gray-600 dark:text-gray-400"> <svg class="w-6 h-6 mb-1" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M19 20H5a2 2 0 01-2-2V6a2 2 0 012-2h10a2 2 0 012 2v1m2 13a2 2 0 01-2-2V7m2 13a2 2 0 002-2V9.5a2 2 0 00-2-2h-2m-4-3H9M7 16h6M7 8h6v4H7V8z"/> </svg> <span class="text-xs">Actualité</span> </a> <a href="/resident-dashboard" class="nav-item flex flex-col items-center p-3 text-gray-600 dark:text-gray-400"> <svg class="w-6 h-6 mb-1" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6"/> </svg> <span class="text-xs">Compte</span> </a> <a href="/shop-mobile" class="nav-item flex flex-col items-center p-3 text-gray-600 dark:text-gray-400"> <svg class="w-6 h-6 mb-1" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M16 11V7a4 4 0 00-8 0v4M5 9h14l1 12H4L5 9z"/> </svg> <span class="text-xs">Commerce</span> </a> </div> </nav> <script> let currentStep = 1; let selectedStore = null; let cart = []; let deliveryData = { storeId: null, items: [], deliveryTime: null, address: null, deliveryPersonId: null, orderId: null }; let deliveryMap; let trackingInterval; let userRating = 0; function openDeliveryModal() { document.getElementById('modalBackdrop').classList.add('show'); document.getElementById('deliveryModal').classList.add('show'); document.body.style.overflow = 'hidden'; loadFromMemoryDelivery(); loadStoresFromDB(); generateTimeSlotsDelivery(); } function closeDeliveryModal() { document.getElementById('modalBackdrop').classList.remove('show'); document.getElementById('deliveryModal').classList.remove('show'); document.body.style.overflow = 'auto'; } function saveToMemoryDelivery() { localStorage.setItem('deliveryData', JSON.stringify(deliveryData)); localStorage.setItem('currentStepDelivery', currentStep.toString()); localStorage.setItem('cartDelivery', JSON.stringify(cart)); } function loadFromMemoryDelivery() { const savedData = localStorage.getItem('deliveryData'); const savedStep = localStorage.getItem('currentStepDelivery'); const savedCart = localStorage.getItem('cartDelivery'); if (savedData && savedStep) { deliveryData = JSON.parse(savedData); currentStep = parseInt(savedStep); cart = savedCart ? JSON.parse(savedCart) : []; if (currentStep > 1) { showStepDelivery(currentStep); if (currentStep === 9) { startTracking(); } } } } function nextStep(step) { document.querySelectorAll('.step-content').forEach(el => el.classList.add('hidden')); document.getElementById('step' + step).classList.remove('hidden'); currentStep = step; saveToMemoryDelivery(); if (step === 4) { loadUserAddressFromDB(); setTimeout(initializeDeliveryMap, 100); } else if (step === 5) { checkDeliveryAvailability(); } } function showStepDelivery(step) { document.querySelectorAll('.step-content').forEach(el => el.classList.add('hidden')); document.getElementById('step' + step).classList.remove('hidden'); } async function loadStoresFromDB() { const stores = [ {id: 1, name: 'Boulangerie Martin', type: 'boulangerie', icon: '🥖', color: 'from-amber-50 to-orange-50 dark:from-amber-900/20 dark:to-orange-900/20 border-amber-200 dark:border-amber-800'}, {id: 2, name: 'Épicerie Bio Nature', type: 'épicerie', icon: '🥬', color: 'from-green-50 to-emerald-50 dark:from-green-900/20 dark:to-emerald-900/20 border-green-200 dark:border-green-800'}, {id: 3, name: 'Pharmacie Central', type: 'pharmacie', icon: '💊', color: 'from-blue-50 to-cyan-50 dark:from-blue-900/20 dark:to-cyan-900/20 border-blue-200 dark:border-blue-800'}, {id: 4, name: 'Café des Amis', type: 'café', icon: '☕', color: 'from-stone-50 to-neutral-50 dark:from-stone-900/20 dark:to-neutral-900/20 border-stone-200 dark:border-stone-800'}, {id: 5, name: 'Fleuriste Bella', type: 'fleuriste', icon: '🌸', color: 'from-pink-50 to-rose-50 dark:from-pink-900/20 dark:to-rose-900/20 border-pink-200 dark:border-pink-800'} ]; const storeList = document.getElementById('storeList'); storeList.innerHTML = ''; stores.forEach(store => { const div = document.createElement('div'); div.className = `bg-gradient-to-r ${store.color} p-4 rounded-xl cursor-pointer hover:shadow-md transition-all duration-300 border store-card`; div.innerHTML = ` <div class="flex items-center space-x-4"> <div class="text-3xl">${store.icon}</div> <div class="flex-1"> <h4 class="font-semibold text-stone-900 dark:text-white">${store.name}</h4> <p class="text-stone-600 dark:text-stone-400 text-sm capitalize">${store.type}</p> </div> <div class="w-6 h-6 border-2 border-stone-300 dark:border-stone-600 rounded-full store-checkbox"></div> </div> `; div.onclick = () => selectStore(store, div); storeList.appendChild(div); }); } function selectStore(store, element) { selectedStore = store; deliveryData.storeId = store.id; document.querySelectorAll('.store-card').forEach(card => { card.classList.remove('ring-2', 'ring-blue-500', 'bg-blue-50'); const checkbox = card.querySelector('.store-checkbox'); checkbox.classList.remove('bg-blue-500', 'border-blue-500'); checkbox.innerHTML = ''; }); element.classList.add('ring-2', 'ring-blue-500'); const checkbox = element.querySelector('.store-checkbox'); checkbox.classList.add('bg-blue-500', 'border-blue-500'); checkbox.innerHTML = '<div class="w-2 h-2 bg-white rounded-full mx-auto mt-1"></div>'; document.getElementById('btn1').disabled = false; loadProductsFromDB(store.id); } async function loadProductsFromDB(storeId) { const allProducts = { 1: [ // Boulangerie Martin {id: 1, name: 'Pain de campagne', price: 3.20, image: 'https://images.unsplash.com/photo-1549931319-a545dcf3bc73?w=150&h=150&fit=crop&crop=center', category: 'Pains'}, {id: 2, name: 'Croissant au beurre', price: 1.40, image: 'https://images.unsplash.com/photo-1555507036-ab794f4eeab3?w=150&h=150&fit=crop&crop=center', category: 'Viennoiseries'}, {id: 3, name: 'Tarte aux fraises', price: 14.50, image: 'https://images.unsplash.com/photo-1565958011703-44f9829ba187?w=150&h=150&fit=crop&crop=center', category: 'Pâtisseries'}, {id: 4, name: 'Pain aux chocolats', price: 1.60, image: 'https://images.unsplash.com/photo-1608198093002-ad4e005484ec?w=150&h=150&fit=crop&crop=center', category: 'Viennoiseries'}, {id: 21, name: 'Baguette tradition', price: 1.10, image: 'https://images.unsplash.com/photo-1534620808146-d33bb39128b2?w=150&h=150&fit=crop&crop=center', category: 'Pains'}, {id: 22, name: 'Éclair au café', price: 3.20, image: 'https://images.unsplash.com/photo-1578985545062-69928b1d9587?w=150&h=150&fit=crop&crop=center', category: 'Pâtisseries'} ], 2: [ // Épicerie Bio Nature {id: 5, name: 'Pommes bio du verger', price: 4.20, image: 'https://images.unsplash.com/photo-1560806887-1e4cd0b6cbd6?w=150&h=150&fit=crop&crop=center', category: 'Fruits'}, {id: 6, name: 'Lait d\'amande bio', price: 3.40, image: 'https://images.unsplash.com/photo-1550583724-b2692b85b150?w=150&h=150&fit=crop&crop=center', category: 'Boissons'}, {id: 7, name: 'Quinoa blanc bio', price: 5.80, image: 'https://images.unsplash.com/photo-1586201375761-83865001e30c?w=150&h=150&fit=crop&crop=center', category: 'Céréales'}, {id: 8, name: 'Miel d\'acacia local', price: 9.50, image: 'https://images.unsplash.com/photo-1587049633312-d628ae50a8ae?w=150&h=150&fit=crop&crop=center', category: 'Épicerie'}, {id: 23, name: 'Avocat bio', price: 2.10, image: 'https://images.unsplash.com/photo-1523049673857-eb18f1d7b578?w=150&h=150&fit=crop&crop=center', category: 'Fruits'}, {id: 24, name: 'Huile d\'olive extra', price: 12.90, image: 'https://images.unsplash.com/photo-1474979266404-7eaacbcd87c5?w=150&h=150&fit=crop&crop=center', category: 'Épicerie'} ], 3: [ // Pharmacie Central {id: 9, name: 'Paracétamol 500mg', price: 3.80, image: 'https://images.unsplash.com/photo-1584308666744-24d5c474f2ae?w=150&h=150&fit=crop&crop=center', category: 'Médicaments'}, {id: 10, name: 'Vitamine C 1000mg', price: 8.90, image: 'https://images.unsplash.com/photo-1559757148-5c350d0d3c56?w=150&h=150&fit=crop&crop=center', category: 'Vitamines'}, {id: 11, name: 'Masques FFP2', price: 12.50, image: 'https://images.unsplash.com/photo-1584634731339-252c581abfc5?w=150&h=150&fit=crop&crop=center', category: 'Protection'}, {id: 12, name: 'Gel hydroalcoolique', price: 4.90, image: 'https://images.unsplash.com/photo-1584744982491-665216d95f8b?w=150&h=150&fit=crop&crop=center', category: 'Hygiène'}, {id: 25, name: 'Thermomètre digital', price: 15.90, image: 'https://images.unsplash.com/photo-1559757175-0eb30cd8c063?w=150&h=150&fit=crop&crop=center', category: 'Matériel'}, {id: 26, name: 'Pansements assortis', price: 6.20, image: 'https://images.unsplash.com/photo-1563213126-a4273aed2016?w=150&h=150&fit=crop&crop=center', category: 'Soins'} ], 4: [ // Café des Amis {id: 13, name: 'Expresso italien', price: 2.20, image: 'https://images.unsplash.com/photo-1510707577352-8002e62de5a8?w=150&h=150&fit=crop&crop=center', category: 'Boissons chaudes'}, {id: 14, name: 'Cappuccino crémeux', price: 3.80, image: 'https://images.unsplash.com/photo-1534778101976-62847782c213?w=150&h=150&fit=crop&crop=center', category: 'Boissons chaudes'}, {id: 15, name: 'Sandwich club', price: 7.50, image: 'https://images.unsplash.com/photo-1539252554453-80ab65ce3586?w=150&h=150&fit=crop&crop=center', category: 'Snacks'}, {id: 16, name: 'Muffin chocolat', price: 3.90, image: 'https://images.unsplash.com/photo-1607958996333-41aef7caefaa?w=150&h=150&fit=crop&crop=center', category: 'Pâtisseries'}, {id: 27, name: 'Latte macchiato', price: 4.20, image: 'https://images.unsplash.com/photo-1485808191679-5f86510681a2?w=150&h=150&fit=crop&crop=center', category: 'Boissons chaudes'}, {id: 28, name: 'Salade César', price: 9.80, image: 'https://images.unsplash.com/photo-1512852939750-1305098529bf?w=150&h=150&fit=crop&crop=center', category: 'Plats'} ], 5: [ // Fleuriste Bella {id: 17, name: 'Bouquet de roses rouges', price: 28.00, image: 'https://images.unsplash.com/photo-1518895949257-7621c3c786d7?w=150&h=150&fit=crop&crop=center', category: 'Bouquets'}, {id: 18, name: 'Monstera deliciosa', price: 18.90, image: 'https://images.unsplash.com/photo-1506905925346-21bda4d32df4?w=150&h=150&fit=crop&crop=center', category: 'Plantes'}, {id: 19, name: 'Orchidée blanche', price: 24.50, image: 'https://images.unsplash.com/photo-1452827073306-6e6e661baf57?w=150&h=150&fit=crop&crop=center', category: 'Plantes'}, {id: 20, name: 'Composition printanière', price: 22.00, image: 'https://images.unsplash.com/photo-1490750967868-88aa4486c946?w=150&h=150&fit=crop&crop=center', category: 'Bouquets'}, {id: 29, name: 'Cactus décoratif', price: 12.90, image: 'https://images.unsplash.com/photo-1486920456546-a0e6fb7c5c8b?w=150&h=150&fit=crop&crop=center', category: 'Plantes'}, {id: 30, name: 'Tulipes colorées', price: 16.50, image: 'https://images.unsplash.com/photo-1520763185298-1b434c919102?w=150&h=150&fit=crop&crop=center', category: 'Bouquets'} ] }; const products = allProducts[storeId] || []; const productList = document.getElementById('productList'); const selectedStoreDiv = document.getElementById('selectedStore'); selectedStoreDiv.innerHTML = ` <div class="flex items-center space-x-3"> <span class="text-2xl">${selectedStore.icon || '🏪'}</span> <div> <h4 class="font-semibold text-stone-900 dark:text-white">${selectedStore.name}</h4> <p class="text-stone-600 dark:text-stone-400 text-sm">${products.length} articles disponibles</p> </div> </div> `; productList.innerHTML = ''; const categories = [...new Set(products.map(p => p.category))]; categories.forEach(category => { const categoryProducts = products.filter(p => p.category === category); const categoryDiv = document.createElement('div'); categoryDiv.className = 'mb-6'; categoryDiv.innerHTML = ` <h5 class="font-medium text-stone-800 dark:text-white mb-3 text-sm uppercase tracking-wide">${category}</h5> <div class="grid grid-cols-1 gap-3"> ${categoryProducts.map(product => ` <div class="bg-white dark:bg-stone-800 rounded-xl p-4 border border-stone-200 dark:border-stone-700 hover:shadow-md transition-all duration-300 product-item" data-product-id="${product.id}"> <div class="flex items-center space-x-4"> <div class="w-16 h-16 rounded-lg overflow-hidden bg-stone-100 dark:bg-stone-700 flex-shrink-0"> <img src="${product.image}" alt="${product.name}" class="w-full h-full object-cover" onerror="this.src='data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNjQiIGhlaWdodD0iNjQiIHZpZXdCb3g9IjAgMCA2NCA2NCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHJlY3Qgd2lkdGg9IjY0IiBoZWlnaHQ9IjY0IiBmaWxsPSIjRjNGNEY2Ii8+CjxwYXRoIGQ9Ik0yMCAyMEg0NFY0NEgyMFYyMFoiIHN0cm9rZT0iIzlDQTNBRiIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiLz4KPHBhdGggZD0iTTI4IDI4TDM2IDM2IiBzdHJva2U9IiM5Q0EzQUYiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIi8+CjxwYXRoIGQ9Ik0zNiAyOEwyOCAzNiIgc3Ryb2tlPSIjOUNBM0FGIiBzdHJva2Utd2lkdGg9IjIiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIvPgo8L3N2Zz4K'"> </div> <div class="flex-1 min-w-0"> <h6 class="font-medium text-stone-900 dark:text-white text-sm truncate">${product.name}</h6> <p class="text-green-600 dark:text-green-400 font-semibold text-lg">${product.price.toFixed(2)}€</p> </div> <button onclick="addToCart(${product.id}, '${product.name.replace(/'/g, "\\'")}', ${product.price})" class="bg-blue-600 hover:bg-blue-700 text-white px-4 py-2 rounded-lg text-sm font-medium transition-all duration-300 flex items-center space-x-1 add-btn" data-product-id="${product.id}"> <span class="add-icon">+</span> <span class="add-text">Ajouter</span> </button> </div> </div> `).join('')} </div> `; productList.appendChild(categoryDiv); }); } function addToCart(productId, productName, productPrice) { const existingItem = cart.find(item => item.id === productId); if (existingItem) { existingItem.quantity++; } else { cart.push({ id: productId, name: productName, price: parseFloat(productPrice), quantity: 1 }); } deliveryData.items = cart; updateCartDisplayDelivery(); saveToMemoryDelivery(); const button = document.querySelector(`button[data-product-id="${productId}"]`); if (button) { const addIcon = button.querySelector('.add-icon'); const addText = button.querySelector('.add-text'); button.classList.remove('bg-blue-600', 'hover:bg-blue-700'); button.classList.add('bg-green-600'); addIcon.textContent = '✓'; addText.textContent = 'Ajouté'; setTimeout(() => { button.classList.remove('bg-green-600'); button.classList.add('bg-blue-600', 'hover:bg-blue-700'); addIcon.textContent = '+'; addText.textContent = 'Ajouter'; }, 1500); } } function updateCartDisplayDelivery() { const cartItems = document.getElementById('cartItems'); const cartTotal = document.getElementById('cartTotal'); const cartCount = document.getElementById('cartCount'); const clearCartBtn = document.getElementById('clearCartBtn'); if (cart.length === 0) { cartItems.innerHTML = '<p class="text-stone-500 dark:text-stone-400 text-sm">Votre panier est vide</p>'; cartTotal.innerHTML = 'Total: 0.00€'; cartCount.textContent = '0'; clearCartBtn.classList.add('hidden'); document.getElementById('btn2').disabled = true; return; } let html = '<div class="space-y-2">'; let total = 0; let totalItems = 0; cart.forEach(item => { const itemTotal = item.price * item.quantity; total += itemTotal; totalItems += item.quantity; html += ` <div class="flex justify-between items-center bg-white dark:bg-stone-700 p-2 rounded text-sm"> <span class="flex-1 truncate">${item.name}</span> <div class="flex items-center space-x-2"> <button onclick="changeQuantity(${item.id}, -1)" class="text-red-500 hover:text-red-700 w-6 h-6 flex items-center justify-center rounded-full hover:bg-red-50 dark:hover:bg-red-900/20">-</button> <span class="text-stone-500 min-w-[2rem] text-center">x${item.quantity}</span> <button onclick="changeQuantity(${item.id}, 1)" class="text-green-500 hover:text-green-700 w-6 h-6 flex items-center justify-center rounded-full hover:bg-green-50 dark:hover:bg-green-900/20">+</button> </div> <span class="font-medium ml-2">${itemTotal.toFixed(2)}€</span> </div> `; }); html += '</div>'; cartItems.innerHTML = html; cartTotal.innerHTML = `Total: ${total.toFixed(2)}€`; cartCount.textContent = totalItems.toString(); clearCartBtn.classList.remove('hidden'); document.getElementById('btn2').disabled = false; } function changeQuantity(productId, change) { const item = cart.find(item => item.id === productId); if (item) { item.quantity += change; if (item.quantity <= 0) { cart = cart.filter(cartItem => cartItem.id !== productId); } } deliveryData.items = cart; updateCartDisplayDelivery(); saveToMemoryDelivery(); } function clearCart() { if (confirm('Êtes-vous sûr de vouloir vider votre panier ?')) { cart = []; deliveryData.items = []; updateCartDisplayDelivery(); saveToMemoryDelivery(); } } function generateTimeSlotsDelivery() { const select = document.getElementById('deliveryTime'); select.innerHTML = '<option value="">Choisir un créneau</option>'; const now = new Date(); const currentHour = now.getHours(); const currentMinute = now.getMinutes(); const isWeekend = now.getDay() === 0 || now.getDay() === 6; let availableSlots = []; // Créneaux aujourd'hui if (isWeekend) { for (let hour = 14; hour <= 20; hour++) { if (hour > currentHour || (hour === currentHour && currentMinute < 30)) { availableSlots.push(`${hour.toString().padStart(2, '0')}:00`); if (hour < 20) { availableSlots.push(`${hour.toString().padStart(2, '0')}:30`); } } } } else { if (currentHour < 13 || (currentHour === 12 && currentMinute < 40)) { availableSlots.push('12:40', '13:00', '13:20', '13:40'); } if (currentHour < 17 || (currentHour === 15 && currentMinute < 20)) { availableSlots.push('15:20', '15:40', '16:00', '16:20', '16:40', '17:00'); } } // Si pas de créneaux aujourd'hui, ajouter ceux de demain if (availableSlots.length === 0) { const tomorrow = new Date(now); tomorrow.setDate(tomorrow.getDate() + 1); const tomorrowIsWeekend = tomorrow.getDay() === 0 || tomorrow.getDay() === 6; if (tomorrowIsWeekend) { for (let hour = 14; hour <= 20; hour++) { availableSlots.push(`Demain ${hour.toString().padStart(2, '0')}:00`); if (hour < 20) { availableSlots.push(`Demain ${hour.toString().padStart(2, '0')}:30`); } } } else { availableSlots.push('Demain 12:40', 'Demain 13:00', 'Demain 13:20', 'Demain 13:40'); availableSlots.push('Demain 15:20', 'Demain 15:40', 'Demain 16:00', 'Demain 16:20', 'Demain 16:40', 'Demain 17:00'); } } if (availableSlots.length === 0) { const option = document.createElement('option'); option.value = ''; option.textContent = 'Aucun créneau disponible'; option.disabled = true; select.appendChild(option); } else { availableSlots.forEach(slot => { const option = document.createElement('option'); option.value = slot; option.textContent = slot; select.appendChild(option); }); } select.onchange = function() { deliveryData.deliveryTime = this.value; document.getElementById('btn3').disabled = !this.value; saveToMemoryDelivery(); }; } async function loadUserAddressFromDB() { try { const response = await fetch('api/get-user-address.php'); const data = await response.json(); document.getElementById('addressInput').value = data.address; deliveryData.address = data.address; document.getElementById('btn4').disabled = false; saveToMemoryDelivery(); } catch (error) { console.error('Erreur chargement adresse:', error); } } function initializeDeliveryMap() { if (deliveryMap) return; deliveryMap = L.map('map').setView([50.8200, 4.3600], 16); const isDark = document.documentElement.classList.contains('dark'); const tileUrl = isDark ? 'https://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}{r}.png' : 'https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}{r}.png'; L.tileLayer(tileUrl, { attribution: '', subdomains: 'abcd' }).addTo(deliveryMap); const marker = L.marker([50.8205, 4.3605]).addTo(deliveryMap); marker.bindPopup(deliveryData.address).openPopup(); } async function checkDeliveryAvailability() { try { const response = await fetch('api/check-delivery-availability.php', { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({ deliveryTime: deliveryData.deliveryTime, date: new Date().toISOString().split('T')[0] }) }); const data = await response.json(); if (data.available) { showDeliveryPerson(data.deliveryPerson); } else { document.getElementById('deliveryCheck').innerHTML = ` <div class="text-center text-red-600"> <p>Aucun livreur disponible pour ce créneau.</p> <button onclick="nextStep(3)" class="mt-4 bg-gray-600 text-white px-4 py-2 rounded">Choisir un autre créneau</button> </div> `; } } catch (error) { console.error('Erreur vérification disponibilité:', error); } } function showDeliveryPerson(person) { deliveryData.deliveryPersonId = person.id; document.getElementById('deliveryPerson').innerHTML = ` <div class="flex flex-col items-center"> <img src="${person.photo}" alt="${person.name}" class="w-20 h-20 rounded-full mb-3 object-cover"> <p class="text-lg font-semibold">${person.name}, prendra en charge votre commande !</p> </div> `; calculateTotalWithDelivery(person); nextStep(6); } async function calculateTotalWithDelivery(deliveryPerson) { const subtotal = cart.reduce((sum, item) => sum + (item.price * item.quantity), 0); try { const response = await fetch('api/calculate-delivery-fee.php', { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({ storeId: deliveryData.storeId, deliveryPersonId: deliveryPerson.id, customerAddress: deliveryData.address }) }); const data = await response.json(); const deliveryFee = data.deliveryFee; const total = subtotal + deliveryFee; document.getElementById('orderSummary').innerHTML = ` <h3 class="font-semibold mb-3">Récapitulatif de commande</h3> <div class="space-y-2 text-sm"> <div class="flex justify-between"> <span>Sous-total articles</span> <span>${subtotal.toFixed(2)}€</span> </div> <div class="flex justify-between"> <span>Frais de livraison (${data.distance}km)</span> <span>${deliveryFee.toFixed(2)}€</span> </div> <hr> <div class="flex justify-between font-bold"> <span>Total</span> <span>${total.toFixed(2)}€</span> </div> </div> `; } catch (error) { console.error('Erreur calcul frais livraison:', error); } } async function processPayment() { nextStep(7); try { const response = await fetch('api/create-payment-intent.php', { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify(deliveryData) }); const data = await response.json(); if (data.error) { alert('Erreur: ' + data.error); return; } const stripe = Stripe(data.publishableKey); const elements = stripe.elements(); const cardElement = elements.create('card'); cardElement.mount('#card-element'); document.getElementById('pay-button').onclick = async function() { const {error} = await stripe.confirmCardPayment(data.clientSecret, { payment_method: { card: cardElement } }); if (error) { alert('Erreur de paiement: ' + error.message); } else { await saveOrderToDB(); nextStep(8); } }; } catch (error) { console.error('Erreur paiement:', error); alert('Erreur lors de l\'initialisation du paiement'); } } async function saveOrderToDB() { try { const response = await fetch('api/save-order.php', { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify(deliveryData) }); const data = await response.json(); deliveryData.orderId = data.orderId; saveToMemoryDelivery(); } catch (error) { console.error('Erreur sauvegarde commande:', error); } } function startTracking() { nextStep(9); trackingInterval = setInterval(checkOrderStatus, 1000); } async function checkOrderStatus() { try { const response = await fetch('api/check-order-status.php', { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({orderId: deliveryData.orderId}) }); const data = await response.json(); updateTrackingStatus(data.status); if (data.status === 'completed') { clearInterval(trackingInterval); setTimeout(() => nextStep(10), 2000); } } catch (error) { console.error('Erreur vérification statut:', error); } } function updateTrackingStatus(status) { const statusMap = { 'en_route_magasin': 1, 'arrive_magasin': 2, 'en_route_domicile': 3, 'completed': 4 }; const currentStatusNum = statusMap[status] || 0; ['status1', 'status2', 'status3', 'status4'].forEach((id, index) => { const element = document.getElementById(id); const circle = element.querySelector('.w-4'); if (index < currentStatusNum) { element.classList.add('completed'); circle.classList.remove('bg-gray-300'); circle.classList.add('bg-green-500'); } else if (index === currentStatusNum - 1) { element.classList.add('active'); circle.classList.remove('bg-gray-300'); circle.classList.add('bg-blue-500'); } }); } function setRating(rating) { userRating = rating; document.querySelectorAll('.star').forEach((star, index) => { if (index < rating) { star.classList.remove('text-gray-300'); star.classList.add('text-yellow-500'); } else { star.classList.remove('text-yellow-500'); star.classList.add('text-gray-300'); } }); document.getElementById('submitRating').disabled = false; } async function submitRating() { try { await fetch('api/submit-rating.php', { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({ orderId: deliveryData.orderId, rating: userRating, deliveryPersonId: deliveryData.deliveryPersonId }) }); localStorage.removeItem('deliveryData'); localStorage.removeItem('currentStepDelivery'); localStorage.removeItem('cartDelivery'); alert('Merci pour votre évaluation !'); closeDeliveryModal(); location.reload(); } catch (error) { console.error('Erreur soumission note:', error); } } async function fetchWeather() { try { const apiKey = 'fa1a36a37c3f4e7ab7e233043252305'; const city = 'Uccle,Belgium'; const url = `https://api.weatherapi.com/v1/current.json?key=${apiKey}&q=${city}&aqi=no`; const response = await fetch(url); if (!response.ok) { throw new Error('Erreur API Weather'); } const data = await response.json(); if (data && data.current && data.current.temp_c) { const temperature = Math.round(data.current.temp_c); const tempElement = document.getElementById('tempDesktop'); if (tempElement) { tempElement.textContent = `${temperature}°C`; } } else { throw new Error('Données météo non trouvées'); } } catch (error) { console.error('Erreur météo:', error); const tempElement = document.getElementById('tempDesktop'); if (tempElement) { tempElement.textContent = '--°C'; } } } function setActiveNavItem() { const currentPath = window.location.pathname; const navItems = document.querySelectorAll('.nav-item'); navItems.forEach(item => { item.classList.remove('active'); const href = item.getAttribute('href'); if (href === currentPath || (currentPath === '/' && href === '/')) { item.classList.add('active'); } }); } window.addEventListener('beforeunload', saveToMemoryDelivery); document.addEventListener('DOMContentLoaded', function() { fetchWeather(); setInterval(fetchWeather, 5000); setActiveNavItem(); }); </script> </body> </html>
| ver. 1.6 |
Github
|
.
| PHP 8.1.33 | Генерация страницы: 0 |
proxy
|
phpinfo
|
Настройка