// public_html/js/home.js // Check Auth & Personalization let token = localStorage.getItem('token'); const userName = localStorage.getItem('userName'); const userId = localStorage.getItem('userId'); if (userName) document.getElementById('greeting').innerText = `مرحباً، ${userName.split(' ')[0]} 👋`; // 🟢 Fetch Pricing Settings let pricingConfig = null; async function fetchPricingConfig() { try { const res = await fetch(`${API_URL}/api/orders/price-config`, { headers: { 'Authorization': `Bearer ${token}` } }); if (res.ok) { pricingConfig = await res.json(); console.log('💰 Pricing Loaded:', pricingConfig); } } catch (e) { console.error('Error loading pricing', e); } } if (token) fetchPricingConfig(); // 🗺️ Map Initialization & Modal Logic let map, currentSelectionMode, mapInitialized = false; function openMapModal(mode) { currentSelectionMode = mode; const title = mode === 'pickup' ? 'تحديد موقع الاستلام' : 'تحديد وجهة التسليم'; document.getElementById('modalTitle').innerText = title; const modalEl = document.getElementById('locationPickerModal'); const modal = new bootstrap.Modal(modalEl); modal.show(); // Refresh map on modal show if (!mapInitialized) { setTimeout(initMap, 400); } else { setTimeout(() => map.invalidateSize(), 400); } } function initMap() { if (mapInitialized) return; try { // 🟢 Use 'pickerMap' ID map = L.map('pickerMap', { zoomControl: false, attributionControl: false }).setView([15.5007, 32.5599], 13); const darkLayer = L.tileLayer('https://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}{r}.png', { maxZoom: 19 }); const googleHybrid = L.tileLayer('https://{s}.google.com/vt/lyrs=s,h&x={x}&y={y}&z={z}', { maxZoom: 20, subdomains: ['mt0', 'mt1', 'mt2', 'mt3'], attribution: 'Google' }).addTo(map); L.control.layers({ "قمر صناعي 🛰️": googleHybrid, "الوضع الليلي 🌙": darkLayer }, null, { position: 'topright' }).addTo(map); mapInitialized = true; setTimeout(locateMe, 500); } catch (err) { console.error('Map Init Error:', err); alert('فشل تهيئة الخريطة، يرجى المحاولة مرة أخرى'); } } function confirmLocationSelection() { const center = map.getCenter(); const mode = currentSelectionMode; document.getElementById(`${mode}-lat`).value = center.lat; document.getElementById(`${mode}-lng`).value = center.lng; document.getElementById(`${mode}-addr`).value = `موقع محدد (${center.lat.toFixed(4)}, ${center.lng.toFixed(4)})`; // Auto reverse geocode for better UI fetch(`https://nominatim.openstreetmap.org/reverse?format=jsonv2&lat=${center.lat}&lon=${center.lng}`) .then(res => res.json()) .then(data => { if (data.display_name) { const addr = data.address.road || data.address.suburb || data.display_name.split(',')[0]; document.getElementById(`${mode}-addr`).value = addr; } }).catch(() => { }); bootstrap.Modal.getInstance(document.getElementById('locationPickerModal')).hide(); } function locateMe() { if (!navigator.geolocation) return; navigator.geolocation.getCurrentPosition( (pos) => map.setView([pos.coords.latitude, pos.coords.longitude], 16), (err) => console.log('Geolocation disabled/failed'), { enableHighAccuracy: true } ); } // 🟢 Utils function calculatePrice() { // Basic Client-Side Estimation const type = document.getElementById('distance-type').value; const priceInput = document.getElementById('price'); if (type === 'short') priceInput.value = 3000; else if (type === 'medium') priceInput.value = 6000; else if (type === 'long') priceInput.value = 10000; } function previewImage(input) { if (input.files && input.files[0]) { const reader = new FileReader(); reader.onload = (e) => { document.getElementById('img-preview').classList.remove('d-none'); document.getElementById('img-preview').querySelector('img').src = e.target.result; } reader.readAsDataURL(input.files[0]); } } function clearImage() { document.getElementById('parcel-image').value = ''; document.getElementById('img-preview').classList.add('d-none'); } // 🖼️ Client-Side Compression using browser-image-compression async function compressImage(file) { const options = { maxSizeMB: 0.5, // Max size in MB (very efficient) maxWidthOrHeight: 1280, // Good for mobile screens useWebWorker: true, // Run in background to avoid freezing UI fileType: 'image/jpeg' }; try { const compressedFile = await imageCompression(file, options); // Convert Blob to Base64 for the backend return await imageCompression.getDataUrlFromFile(compressedFile); } catch (error) { console.error('Compression Error:', error); return await imageCompression.getDataUrlFromFile(file); } } // 🚀 Validation & Submission function validateOrder() { const pLat = document.getElementById('pickup-lat').value; const dLat = document.getElementById('dropoff-lat').value; const pPhone = document.getElementById('pickup-phone').value; const dPhone = document.getElementById('dropoff-phone').value; const price = document.getElementById('price').value; const warn = (msg) => { Swal.fire({ icon: 'warning', text: msg, confirmButtonText: 'حسناً', confirmButtonColor: '#0a8754' }); return false; }; if (!pLat) return warn('يرجى تحديد موقع الاستلام من الخريطة'); if (!dLat) return warn('يرجى تحديد وجهة التسليم من الخريطة'); if (!pPhone || pPhone.length < 10) return warn('رقم هاتف المرسل غير صحيح'); if (!dPhone || dPhone.length < 10) return warn('رقم هاتف المستلم غير صحيح'); if (!price || price <= 0) return warn('يرجى تحديد سعر العرض'); return true; } async function createOrder() { // Guard if (!localStorage.getItem('token')) { const res = await Swal.fire({ title: 'تسجيل الدخول مطلوب', text: 'عشان تقدر تطلب كابتن، لازم تسجل دخولك أولاً.', icon: 'info', showCancelButton: true, confirmButtonText: 'تسجيل الدخول', cancelButtonText: 'إلغاء', confirmButtonColor: '#0a8754' }); if (res.isConfirmed) window.location.href = 'client-login.html'; return; } if (!validateOrder()) return; const btn = document.getElementById('submit-btn'); const originalHTML = btn.innerHTML; btn.disabled = true; btn.innerHTML = ' جاري الإرسال...'; let base64Image = null; const fileInput = document.getElementById('parcel-image'); if (fileInput.files.length > 0) { base64Image = await compressImage(fileInput.files[0]); } const data = { pickup: { address: document.getElementById('pickup-addr').value, contactName: document.getElementById('pickup-name').value, contactPhone: document.getElementById('pickup-phone').value, lat: parseFloat(document.getElementById('pickup-lat').value), lng: parseFloat(document.getElementById('pickup-lng').value) }, dropoff: { address: document.getElementById('dropoff-addr').value, receiverName: document.getElementById('dropoff-name').value, receiverPhone: document.getElementById('dropoff-phone').value, lat: parseFloat(document.getElementById('dropoff-lat').value), lng: parseFloat(document.getElementById('dropoff-lng').value) }, details: document.getElementById('details').value, distanceType: document.getElementById('distance-type').value, price: parseFloat(document.getElementById('price').value), parcelImage: base64Image }; try { const res = await fetch(`${API_URL}/api/orders`, { method: 'POST', headers: { 'Authorization': `Bearer ${localStorage.getItem('token')}`, 'Content-Type': 'application/json' }, body: JSON.stringify(data) }); if (res.ok) { Swal.fire({ icon: 'success', title: 'تم الطلب بنجاح! 🎉', text: 'جاري البحث عن كابتن...', timer: 3000, showConfirmButton: false }); setTimeout(() => window.location.href = 'client-my-orders.html', 3000); } else { const err = await res.json(); Swal.fire({ icon: 'error', text: err.message || 'فشل إرسال الطلب' }); btn.disabled = false; btn.innerHTML = originalHTML; } } catch (err) { Swal.fire({ icon: 'error', text: 'حدث خطأ في الاتصال بالسيرفر' }); btn.disabled = false; btn.innerHTML = originalHTML; } } // Notification Init if (userId && typeof initNotificationSocket === 'function') initNotificationSocket(userId); // 🔌 Offline Handling window.addEventListener('online', () => { Swal.fire({ position: 'top-end', icon: 'success', title: 'رجع الإنترنت! 🌐', showConfirmButton: false, timer: 1500, toast: true }); }); window.addEventListener('offline', () => { Swal.fire({ position: 'top-end', icon: 'warning', title: 'انقطع الاتصال بالإنترنت ⚠️', showConfirmButton: false, timer: 3000, toast: true }); });