⏱️ مدة الزيارة الحالية: 00:00
📸
الرف (قبل)
📸
الرف (بعد)
🎯 تتبع الهدف للمجموعة: الكل 0%
المحقق: 0 كرتون / الهدف: 50 كرتون
const dbName = "ElmandobDB"; const dbVersion = 1; let db; async function initDB() { return new Promise((resolve, reject) => { const request = indexedDB.open(dbName, dbVersion); request.onupgradeneeded = (e) => { db = e.target.result; if (!db.objectStoreNames.contains('history')) { db.createObjectStore('history', { keyPath: 'visit_id' }); } }; request.onsuccess = (e) => { db = e.target.result; resolve(db); }; request.onerror = (e) => reject("IndexedDB Error: " + e.target.errorCode); }); } async function saveHistoryDB(data) { const tx = db.transaction('history', 'readwrite'); const store = tx.objectStore('history'); return new Promise((resolve, reject) => { const request = store.put(data); request.onsuccess = () => resolve(); request.onerror = () => reject(); }); } async function getAllHistoryDB() { const tx = db.transaction('history', 'readonly'); const store = tx.objectStore('history'); return new Promise((resolve) => { const request = store.getAll(); request.onsuccess = () => resolve(request.result || []); }); } async function deleteHistoryDB(id) { const tx = db.transaction('history', 'readwrite'); const store = tx.objectStore('history'); return new Promise((resolve) => { const request = store.delete(id); request.onsuccess = () => resolve(); }); } const simpleHash = (str) => window.secureHash(str); let products = JSON.parse(localStorage.getItem('mandob_p_26')) || []; let markets = JSON.parse(localStorage.getItem('mandob_m_26')) || []; let categories = JSON.parse(localStorage.getItem('mandob_cats')) || ["عام"]; let categoryTargets = JSON.parse(localStorage.getItem('mandob_cat_targets')) || {}; let history = []; let companyName = localStorage.getItem('mandob_co_name') || 'Mandob System'; let companyLogo = localStorage.getItem('mandob_logo') || null; let dailyTarget = parseInt(localStorage.getItem('daily_target_26')) || 50; let cart = { sales: {}, returns: {}, missing: [], inventory: {} }; let currentCat = "الكل"; let locationData = "غير مححدد"; let sigs = {}; let salesChart = null; let targetCatExcel = ""; let marketWeights = JSON.parse(localStorage.getItem('m_weights')) || {}; let productWeights = JSON.parse(localStorage.getItem('p_weights')) || {}; let targetGroups = JSON.parse(localStorage.getItem('target_groups_26')) || []; let groupTargets = JSON.parse(localStorage.getItem('group_targets_26')) || {}; let promos = JSON.parse(localStorage.getItem('mandob_promos')) || {}; // تخزين العروض لكل سوق let visitStartTime = null; let timerInterval = null; let shelfPhotos = { before: [], after: [] }; async function init() { await initDB(); history = await getAllHistoryDB(); check(); applyCompanyName(); renderMarkets(); renderTabs(); renderProducts(); loadProfile(); updateBadge(); updateHubButtons(); renderAdminCategories(); renderAdminTargetGroups(); updateTargetUI(); const today = new Date().toLocaleDateString('en-CA'); document.getElementById('history_date_filter').value = today; if(localStorage.getItem('mandob_theme') === 'dark') document.body.classList.add('dark-mode'); } function compressImage(file, maxWidth = 800, quality = 0.6) { return new Promise((resolve) => { const reader = new FileReader(); reader.readAsDataURL(file); reader.onload = (e) => { const img = new Image(); img.src = e.target.result; img.onload = () => { const canvas = document.createElement('canvas'); let width = img.width; let height = img.height; if (width > maxWidth) { height = (maxWidth / width) * height; width = maxWidth; } canvas.width = width; canvas.height = height; const ctx = canvas.getContext('2d'); ctx.drawImage(img, 0, 0, width, height); canvas.toBlob((blob) => resolve(blob), 'image/jpeg', quality); }; }; }); } async function handleShelfPhotos(input, type) { const files = Array.from(input.files); for (const file of files) { const compressedBlob = await compressImage(file, 1024, 0.7); shelfPhotos[type].push(compressedBlob); renderPhotoPreviews(type); } } function deletePhoto(type, index) { shelfPhotos[type].splice(index, 1); renderPhotoPreviews(type); } function renderPhotoPreviews(type) { const container = document.getElementById(`preview-${type}`); container.innerHTML = ""; shelfPhotos[type].forEach((blob, index) => { const url = URL.createObjectURL(blob); const wrapper = document.createElement('div'); wrapper.className = "photo-thumb-wrapper"; wrapper.innerHTML = `
×
`; container.appendChild(wrapper); }); } function handleStartVisit() { const market = document.getElementById('market_select').value; if(!market) return alert("يرجى اختيار سوق أولاً"); startVisitTimer(); alert(`تم بدء الزيارة لسوق: ${market}`); } function startVisitTimer() { if(timerInterval) clearInterval(timerInterval); visitStartTime = Date.now(); document.getElementById('timer-display').style.display = 'block'; timerInterval = setInterval(() => { const elapsed = Date.now() - visitStartTime; const mins = Math.floor(elapsed / 60000); const secs = Math.floor((elapsed % 60000) / 1000); document.getElementById('timer-clock').innerText = `${mins.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`; }, 1000); } function getVisitDuration() { if(!visitStartTime) return "00:00"; const elapsed = Date.now() - visitStartTime; const mins = Math.floor(elapsed / 60000); const secs = Math.floor((elapsed % 60000) / 1000); return `${mins}:${secs.toString().padStart(2, '0')} دقيقة`; } function updateTargetUI() { const today = new Date().toLocaleDateString('en-CA'); const currentMonth = today.substring(0, 7); const todaySales = history.filter(h => h.date === today).reduce((acc, visit) => { let visitSum = 0; for (let pName in visit.sales) { const prod = products.find(p => p.name === pName); if (currentCat === "الكل" || (prod && (prod.targetGroup === currentCat || prod.cat === currentCat))) { visitSum += visit.sales[pName]; } } return acc + visitSum; }, 0); let cartSum = 0; for (let pName in cart.sales) { const prod = products.find(p => p.name === pName); if (currentCat === "الكل" || (prod && (prod.targetGroup === currentCat || prod.cat === currentCat))) { cartSum += cart.sales[pName]; } } const totalToday = todaySales + cartSum; const monthlySalesTotal = history.filter(h => h.date.startsWith(currentMonth)).reduce((acc, visit) => { let visitSum = 0; for (let pName in visit.sales) { const prod = products.find(p => p.name === pName); if (currentCat === "الكل" || (prod && (prod.targetGroup === currentCat || prod.cat === currentCat))) { visitSum += visit.sales[pName]; } } return acc + visitSum; }, 0); const totalAchievedThisMonth = monthlySalesTotal + cartSum; let targetVal = 0; if (currentCat === "الكل") { targetVal = dailyTarget; } else if (groupTargets[currentCat]) { targetVal = groupTargets[currentCat]; } else { targetVal = categoryTargets[currentCat] || 50; } let percent = Math.min(100, Math.round((totalAchievedThisMonth / targetVal) * 100)) || 0; let remaining = Math.max(0, targetVal - totalAchievedThisMonth); document.getElementById('target-label-text').innerText = `🎯 تتبع هدف المجموعة: ${currentCat}`; document.getElementById('target-bar').style.width = percent + '%'; document.getElementById('target-percent').innerText = percent + '%'; document.getElementById('target-text').innerHTML = ` المحقق شهرياً للمجموعة: ${totalAchievedThisMonth} كرتون / الهدف: ${targetVal}
المتبقي للمجموعة: ${remaining} كرتون | مبيعات اليوم: ${totalToday} `; } function saveGroupTarget(group, val) { groupTargets[group] = parseInt(val) || 0; localStorage.setItem('group_targets_26', JSON.stringify(groupTargets)); updateTargetUI(); } function renderAdminTargetGroups() { const container = document.getElementById('admin-target-groups-list'); if (targetGroups.length === 0) { container.innerHTML = '

لا توجد مجموعات تارجت. ارفع ملف Excel أولاً.

'; return; } container.innerHTML = targetGroups.map(g => `
${g}
`).join(''); } function handleTargetGroupImport(input) { const file = input.files[0]; if(!file) return; const reader = new FileReader(); reader.onload = function(e) { const data = new Uint8Array(e.target.result); const workbook = XLSX.read(data, {type: 'array'}); const worksheet = workbook.Sheets[workbook.SheetNames[0]]; const json = XLSX.utils.sheet_to_json(worksheet, {header: 1}); let newGroups = []; json.forEach(row => { if(row[0] && row[0].toString().trim() !== "") newGroups.push(row[0].toString().trim()); }); targetGroups = [...new Set([...targetGroups, ...newGroups])]; localStorage.setItem('target_groups_26', JSON.stringify(targetGroups)); renderAdminTargetGroups(); renderTabs(); alert("✅ تم استيراد المجموعات بنجاح"); }; reader.readAsArrayBuffer(file); } function updateProductGroup(index, groupName) { products[index].targetGroup = groupName; saveToLocal(); } function checkOOSPrediction() { const market = document.getElementById('market_select').value; const zone = document.getElementById('oos-prediction-zone'); if(!market || products.length === 0) { zone.innerHTML = ''; return; } const marketVisits = history.filter(h => h.market === market); let suggestions = []; for(let prodName in cart.inventory) { const currentInv = cart.inventory[prodName]; let totalSales = 0; let visitCount = 0; marketVisits.forEach(v => {if(v.sales && v.sales[prodName]) { totalSales += v.sales[prodName]; visitCount++; }}); const avgSales = visitCount > 0 ? (totalSales / visitCount) : 0; if(avgSales > 0 && currentInv < avgSales) { const needed = Math.ceil(avgSales - currentInv); suggestions.push(`المنتج *${prodName}* متوسط سحبه ${avgSales.toFixed(1)} كرتون. جردك الحالي (${currentInv}) لا يكفي. اقترح طلب ${needed} كرتون.`); } } if(suggestions.length > 0) { zone.innerHTML = `
🧠 خوارزمية التنبؤ الذكي (Elmandob AI):
${suggestions.map(s => `

• ${s}

`).join('')}
`; } else { zone.innerHTML = ''; } } async function renderAndSavePDF(container, fileName) { const canvas = await html2canvas(container, { scale: 1.5, useCORS: true, logging: false, backgroundColor: "#ffffff", windowWidth: 800 }); const imgData = canvas.toDataURL('image/jpeg', 0.85); const imgWidth = canvas.width; const imgHeight = canvas.height; const pdfWidth = 210; const pdfHeight = (imgHeight * pdfWidth) / imgWidth; const pdf = new window.jspdf.jsPDF({ orientation: 'p', unit: 'mm', format: [pdfWidth, pdfHeight], putOnlyUsedFonts: true }); pdf.addImage(imgData, 'JPEG', 0, 0, pdfWidth, pdfHeight, undefined, 'FAST'); pdf.save(fileName + ".pdf"); container.innerHTML = ""; } async function generateSpecialPDF(market, reportType) { const renderZone = document.getElementById('pdf-render-zone'); let title = reportType === 'prediction' ? `تقرير التنبؤ - ${market}` : (reportType==='target'?'تقرير التارجت والأداء الشهري':'خطة توزيع التارجت الذكية'); let repName = localStorage.getItem('rep_name_fixed') || 'غير معرف'; let repID = localStorage.getItem('fixed_rep_id') || 'غير مسجل'; const currentMonthStr = new Date().toLocaleDateString('en-CA').substring(0, 7); let contentHTML = `
${companyLogo ? `` : `

${companyName}

`}

${title}

المندوب: ${repName} | الرقم الوظيفي: ${repID}

${(reportType !== 'target' && reportType !== 'distribute') ? `

السوق المختار: ${market}

` : ''}

توقيت التقرير: ${new Date().toLocaleString('ar-EG')}

`; if(reportType === 'prediction') { const marketVisits = history.filter(h => h.market === market); contentHTML += `

🔮 تحليل التنبؤ بالطلب المستقبلي:

`; products.forEach(p => { let total = 0, count = 0; marketVisits.forEach(v => { if(v.sales && v.sales[p.name]) { total += v.sales[p.name]; count++; } }); let avg = count > 0 ? (total/count).toFixed(1) : 0; contentHTML += ``; }); contentHTML += `
الصنفمتوسط السحبالتوقع القادمتوصية المخزون
${p.name}${avg}${Math.ceil(avg * 1.2)}${avg > 0 ? 'دعم المخزون' : 'تنشيط'}
`; } else if(reportType === 'target') { const monthlyAllVisits = history.filter(h => h.date.startsWith(currentMonthStr)); contentHTML += `

📊 تحليل الأداء الشهري حسب مجموعات التارجت:

`; let groupSums = {}; targetGroups.forEach(g => groupSums[g] = 0); groupSums["غير مصنف"] = 0; monthlyAllVisits.forEach(visit => { for(let pName in visit.sales) { const prod = products.find(p => p.name === pName); let gName = (prod && prod.targetGroup) ? prod.targetGroup : "غير مصنف"; groupSums[gName] = (groupSums[gName] || 0) + visit.sales[pName]; } }); let grandTotalAchieved = 0; let totalTargetValues = 0; for(let g in groupSums) { let target = groupTargets[g] || 0; let achieved = groupSums[g]; let remaining = Math.max(0, target - achieved); let percent = target > 0 ? Math.round((achieved/target)*100) : 0; grandTotalAchieved += achieved; totalTargetValues += target; contentHTML += ``; } contentHTML += `
المجموعة الهدف المطلوب المحقق الكلي المتبقي نسبة الإنجاز
${g} ${target} ك ${achieved} ك ${remaining} ك ${percent}%
إجمالي المحقق العام
${grandTotalAchieved} كرتون
إجمالي الهدف العام
${totalTargetValues} كرتون
`; } else if(reportType === 'distribute') { contentHTML += `

📦 توزيع التارجت الذكي المقترح:

`; targetGroups.forEach(g => { let totalCatTarget = groupTargets[g] || 0; if(totalCatTarget <= 0) return; contentHTML += `

المجموعة المستهدفة: ${g} (${totalCatTarget} كرتون)

`; let totalWeight = 0; markets.forEach(m => totalWeight += (marketWeights[m] || 1)); markets.forEach(m => { let w = marketWeights[m] || 1; let share = Math.round((w / totalWeight) * totalCatTarget); contentHTML += ``; }); contentHTML += `
اسم السوقكفاءة السوقالحصة المقترحة
${m}${w === 2 ? 'قوي جداً' : (w === 1.5 ? 'متوسط' : 'ناشئ')}${share} كرتون
`; }); contentHTML += `
`; } contentHTML += `
تم توليد هذا التقرير آلياً بواسطة نظام Elmandob Smart 2026
`; renderZone.innerHTML = contentHTML; await renderAndSavePDF(renderZone, `Elmandob_${reportType}_Report`); } async function generateDailyPDF() { const filterDate = document.getElementById('history_date_filter').value; const filtered = history.filter(h => h.date === filterDate); if(filtered.length === 0) return alert("لا توجد مبيعات في هذا التاريخ"); let repName = localStorage.getItem('rep_name_fixed') || 'غير معرف'; let repID = localStorage.getItem('fixed_rep_id') || 'غير مسجل'; const renderZone = document.getElementById('pdf-render-zone'); let content = `
${companyLogo ? `` : `

${companyName}

`}

ملخص المبيعات والنشاط اليومي

المندوب: ${repName} | الرقم الوظيفي: ${repID}

التاريخ المختار: ${filterDate} | وقت الإصدار: ${new Date().toLocaleTimeString('ar-EG')}

`; for (let v of filtered) { let total = Object.values(v.sales).reduce((a,b)=>a+b, 0); content += `
🏪 السوق: ${v.market} ⏰ إغلاق: ${v.time} | ⏳ مدة الزيارة: ${v.duration || '--'}

إجمالي المبيعات: ${total} كرتون

`; if((v.shelfPhotos?.before?.length) || (v.shelfPhotos?.after?.length)) { content += `

📸 توثيق حالة الرف:

`; if(v.shelfPhotos.before && v.shelfPhotos.before.length) { for(let blob of v.shelfPhotos.before) { const src = URL.createObjectURL(blob); content += `
قبل
`; } } if(v.shelfPhotos.after && v.shelfPhotos.after.length) { for(let blob of v.shelfPhotos.after) { const src = URL.createObjectURL(blob); content += `
بعد
`; } } content += `
`; } content += `
`; } content += `
Elmandob - Daily Advanced Report System
`; renderZone.innerHTML = content; await renderAndSavePDF(renderZone, `Daily_Report_${filterDate}`); } async function generateProfessionalPDF(visitId, type = 'all', marketName = '') { const visits = history.filter(h => h.market === (marketName || '')); const v = visitId ? history.find(h => h.visit_id === visitId) : (visits.length > 0 ? visits[visits.length - 1] : null); if(!v) return alert("لا توجد بيانات لهذه الزيارة"); const renderZone = document.getElementById('pdf-render-zone'); let reportTitle = "تقرير الطلبية وجرد الزيارة"; let themeColor = "#3b82f6"; let allItems = Array.from(new Set([...Object.keys(v.sales || {}), ...Object.keys(v.returns || {}), ...Object.keys(v.inventory || {}), ...(v.missing || [])])); let filteredItems = allItems.filter(p => { if(type === 'sales') return (v.sales[p] > 0); if(type === 'returns') return (v.returns[p] > 0); if(type === 'inventory') return (v.inventory[p] > 0); if(type === 'missing') return (v.missing && v.missing.includes(p)); return (v.sales[p] > 0); }); if(type === 'returns') { reportTitle = "تقرير المرتجعات"; themeColor = "#f59e0b"; } else if(type === 'missing') { reportTitle = "تقرير النواقص"; themeColor = "#ef4444"; } else if(type === 'inventory') { reportTitle = "تقرير جرد الرف"; themeColor = "#8b5cf6"; } if(filteredItems.length === 0) return alert("لا توجد بيانات مسجلة لهذا النوع من التقارير في هذه الزيارة"); let rows = filteredItems.map(p => { let s = v.sales[p] || 0, r = v.returns[p] || 0, inv = v.inventory[p] || 0, isM = (v.missing && v.missing.includes(p))? 'نعم':'-'; if(type === 'all') return `${p}${s} ك${r} ح${inv} ح${isM}`; if(type === 'returns') return `${p}${r} حبة`; if(type === 'missing') return `${p}مفقود / ناقص`; if(type === 'inventory') return `${p}${inv} حبة جرد`; }).join(''); let tableHeader = `اسم المنتجالطلبيةالمرتجعالجردنقص`; if(type === 'returns') tableHeader = `اسم المنتجكمية المرتجع`; if(type === 'missing') tableHeader = `اسم المنتجالحالة`; if(type === 'inventory') tableHeader = `اسم المنتجكمية الجرد`; let photosHTML = ''; if(type === 'all' && ((v.shelfPhotos?.before?.length) || (v.shelfPhotos?.after?.length))) { photosHTML = `

📸 توثيق حالة الرف (صور الزيارة):

`; if(v.shelfPhotos.before && v.shelfPhotos.before.length) { photosHTML += `

📦 صور الرف (قبل الزيارة):

`; for(let blob of v.shelfPhotos.before) { const src = URL.createObjectURL(blob); photosHTML += `
`; } photosHTML += `
`; } if(v.shelfPhotos.after && v.shelfPhotos.after.length) { photosHTML += `

✅ صور الرف (بعد الترتيب):

`; for(let blob of v.shelfPhotos.after) { const src = URL.createObjectURL(blob); photosHTML += `
`; } photosHTML += `
`; } photosHTML += `
`; } renderZone.innerHTML = `
${companyLogo ? `` : `

${companyName}

`}

${reportTitle}

👤 المندوب: ${v.rep_name}
🆔 الرقم الوظيفي: ${v.rep_id}
🏪 السوق: ${v.market}
⏱️ مدة الزيارة: ${v.duration || '--'}
📅 التاريخ: ${v.date}
⏰ التوقيت: ${v.time}
${tableHeader}${rows}
📝 ملاحظات الزيارة:

${v.note || 'لا توجد ملاحظات إضافية'}

📍 موقع الزيارة الجغرافي: عرض المكان على خرائط Google 🔗
(الإحداثيات: ${v.location || 'غير متوفرة'})
${photosHTML}
توقيع المندوب
${v.sigs?.rep ? `` : '__________'}
توقيع المشرف
${v.sigs?.sup ? `` : '__________'}
أمين العهدة
${v.sigs?.store ? `` : '__________'}
تم استخراج التقرير بواسطة Elmandob Smart - El Mandob
`; await renderAndSavePDF(renderZone, `${reportTitle}_${v.market}_${v.date}`); } function handleLogoUpload(input) { const file = input.files[0]; if (file) { const reader = new FileReader(); reader.onload = function(e) { companyLogo = e.target.result; localStorage.setItem('mandob_logo', companyLogo); applyCompanyName(); alert("✅ تم رفع اللوجو بنجاح"); }; reader.readAsDataURL(file); } } function removeLogo() { companyLogo = null; localStorage.removeItem('mandob_logo'); applyCompanyName(); alert("تم حذف اللوجو والعودة لاسم الشركة النصي"); } function exportBackup() { const data = { products, markets, categories, history, categoryTargets, companyName, companyLogo, marketWeights, productWeights, targetGroups, groupTargets, profileId: localStorage.getItem('fixed_rep_id'), repName: localStorage.getItem('rep_name_fixed'), repEmail: localStorage.getItem('rep_email'), repPass: localStorage.getItem('rep_pass_secure'), backupDate: new Date().toLocaleString() }; const blob = new Blob([JSON.stringify(data)], { type: 'application/json' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `Mandob_Backup_${new Date().toLocaleDateString()}.json`; a.click(); alert("✅ تم إنشاء نسخة احتياطية."); } function importBackup(input) { const file = input.files[0]; if(!file) return; const reader = new FileReader(); reader.onload = function(e) { try { const data = JSON.parse(e.target.result); if(confirm("استبدال البيانات؟")) { products = data.products || []; markets = data.markets || []; categories = data.categories || ["عام"]; history = data.history || []; categoryTargets = data.categoryTargets || {}; marketWeights = data.marketWeights || {}; productWeights = data.productWeights || {}; targetGroups = data.targetGroups || []; groupTargets = data.groupTargets || {}; companyName = data.companyName || 'Mandob System'; companyLogo = data.companyLogo || null; localStorage.setItem('fixed_rep_id', data.profileId || ''); localStorage.setItem('rep_name_fixed', data.repName || ''); localStorage.setItem('rep_email', data.repEmail || ''); localStorage.setItem('rep_pass_secure', data.repPass || ''); saveToLocal(); if(companyLogo) localStorage.setItem('mandob_logo', companyLogo); alert("✅ تم الاستعادة بنجاح!"); location.reload(); } } catch(err) { alert("⚠️ ملف غير صالح"); } }; reader.readAsText(file); } function triggerExcel(cat) { targetCatExcel = cat; document.getElementById('excelImport').click(); } function handleExcelImport(input) { const file = input.files[0]; if(!file) return; const reader = new FileReader(); reader.onload = function(e) { const data = new Uint8Array(e.target.result); const workbook = XLSX.read(data, {type: 'array'}); const firstSheetName = workbook.SheetNames[0]; const worksheet = workbook.Sheets[firstSheetName]; const json = XLSX.utils.sheet_to_json(worksheet, {header: 1}); let addedCount = 0; json.forEach(row => { const pName = row[0]; const pPack = row[1] || 1; if(pName && pName.toString().trim() !== "") { if(!products.find(p => p.name === pName && p.cat === targetCatExcel)) { products.push({ name: pName.toString().trim(), cat: targetCatExcel, pack: parseInt(pPack), targetGroup: "" }); addedCount++; } } }); saveToLocal(); renderProducts(); renderAdminProducts(); alert(`✅ تم استيراد ${addedCount} منتج`); input.value = ""; }; reader.readAsArrayBuffer(file); } function applyCompanyName() { const logoHTML = companyLogo ? `` : ''; document.getElementById('app-logo-place').innerHTML = logoHTML; document.getElementById('-logo-place').innerHTML = logoHTML; if (companyLogo) { document.getElementById('app-logo-text').style.display = 'none'; document.getElementById('-logo-text').style.display = 'none'; } else { document.getElementById('app-logo-text').style.display = 'block'; document.getElementById('-logo-text').style.display = 'block'; document.getElementById('app-logo-text').innerText = companyName; document.getElementById('-logo-text').innerText = companyName; } document.getElementById('app-title-head').innerText = companyName + " 2026"; } function updateCompanyName() { const newName = document.getElementById('company_name_input').value; if(newName) { companyName = newName; localStorage.setItem('mandob_co_name', newName); applyCompanyName(); alert("تم التحديث"); } } // --- دوال واجهة المستخدم للمشروع (Shadow Edition) --- /** * دالة إظهار نموذج تسجيل الدخول * تم تصحيح الـ ID ليطابق auth-forms-layer */ window.showForm = function() { const authContainer = document.getElementById('auth-forms-layer'); if (!authContainer) return; authContainer.innerHTML = `

👤 تسجيل دخول المندوب

ليس لديك حساب مندوب؟

إرسال طلب انضمام جديد 📝

`; } /** * دالة إظهار نموذج تسجيل حساب جديد */ window.showRegisterForm = function() { const authContainer = document.getElementById('auth-forms-layer'); if (!authContainer) return; authContainer.innerHTML = `

📝 طلب انضمام مندوب

لديك حساب بالفعل؟

سجل دخول من هنا 🔑

`; } // تأكد من استدعاء هذه الدالة في مراقب الحالة عند عدم وجود مستخدم /** * دالة إظهار نموذج تسجيل حساب جديد * تقوم بإرسال البيانات بوضعية "Pending" لانتظار موافقة الأدمن */ function showRegisterForm() { const authContainer = document.getElementById('auth-forms'); if (!authContainer) return; authContainer.innerHTML = `

📝 طلب انضمام مندوب

لديك حساب بالفعل؟

سجل دخول من هنا 🔑

`; } /** * دالة التبديل بين الوضع الليلي والنهاري * تحفظ التفضيلات في المتصفح لاستعادتها لاحقاً */ function toggleTheme() { document.body.classList.toggle('dark-mode'); const isDark = document.body.classList.contains('dark-mode'); localStorage.setItem('mandob_theme', isDark ? 'dark' : 'light'); // إشعار بسيط للمستخدم (اختياري) if(typeof showToast === "function") { showToast(isDark ? "🌙 تم تفعيل الوضع الليلي" : "☀️ تم تفعيل الوضع النهاري"); } } function setWeight(type, name, val) { if(type === 'market') marketWeights[name] = val; else productWeights[name] = val; saveToLocal(); if(type === 'market') renderMarkets(); else renderAdminProducts(); } function renderMarkets() { const sel = document.getElementById('market_select'); if(!sel) return; sel.innerHTML = `` + markets.map(m => ``).join(''); document.getElementById('admin-m-list').innerHTML = markets.map((m, i) => { let w = marketWeights[m] || 1; return `
${m}
`; }).join(''); } function addMarket() { const val = document.getElementById('new_m_name').value; if(val) { markets.push(val); saveToLocal(); renderMarkets(); document.getElementById('new_m_name').value = ''; updateHubButtons(); } } function deleteMarket(i) { markets.splice(i, 1); saveToLocal(); renderMarkets(); updateHubButtons(); } function addCategory() { const val = document.getElementById('new_cat_name').value; if(val) { categories.push(val); saveToLocal(); renderTabs(); renderAdminCategories(); document.getElementById('new_cat_name').value = ''; } } function deleteCategory(i) { if(categories.length > 1) { categories.splice(i, 1); saveToLocal(); renderTabs(); renderAdminCategories(); } else { alert("يجب وجود قسم واحد على الأقل"); } } function renderAdminCategories() { document.getElementById('admin-cat-list').innerHTML = categories.map((c, i) => `
${c}
`).join(''); } function addNewProduct() { const name = document.getElementById('new_p_name').value; const cat = document.getElementById('new_p_cat').value; const pack = document.getElementById('new_p_pack').value || 1; if(name && cat) { products.push({name, cat, pack: parseInt(pack), targetGroup: ""}); saveToLocal(); renderProducts(); renderAdminProducts(); document.getElementById('new_p_name').value = ''; document.getElementById('new_p_pack').value = ''; alert("تمت الإضافة"); } } function deleteProduct(i) { products.splice(i, 1); saveToLocal(); renderProducts(); renderAdminProducts(); } function renderAdminProducts() { document.getElementById('admin-p-list').innerHTML = products.map((p, i) => { let w = productWeights[p.name] || 1; let groupOptions = ``; targetGroups.forEach(g => { groupOptions += ``; }); return `
${p.name}
`; }).join(''); } function renderTabs() { const cats = ["الكل", ...categories, ...targetGroups]; const uniqueCats = [...new Set(cats)]; document.getElementById('cat-tabs').innerHTML = uniqueCats.map(c => ``).join(''); document.getElementById('new_p_cat').innerHTML = categories.map(c=>``).join(''); } function renderProducts() { const sGrid = document.getElementById('sales-grid'); const rGrid = document.getElementById('returns-grid'); const mGrid = document.getElementById('missing-grid'); const iGrid = document.getElementById('inventory-grid'); // التحقق من الصفحة النشطة حالياً لتوفير الموارد const activePage = document.querySelector('.page-content[style*="display: block"]')?.id; // تفريغ المحتوى القديم مرة واحدة if(sGrid) sGrid.innerHTML = ''; if(rGrid) rGrid.innerHTML = ''; if(mGrid) mGrid.innerHTML = ''; if(iGrid) iGrid.innerHTML = ''; if(products.length === 0) { if(document.getElementById('no-products-msg')) document.getElementById('no-products-msg').style.display = 'block'; return; } if(document.getElementById('no-products-msg')) document.getElementById('no-products-msg').style.display = 'none'; const inventoriedNames = Object.keys(cart.inventory); const hasActiveInventory = inventoriedNames.length > 0; const market = document.getElementById('market_select').value; const today = new Date().toISOString().split('T')[0]; // استخدام متغيرات لتجميع الكود (أسرع بـ 10 أضعاف من التحديث المباشر للـ innerHTML) let sHTML = '', rHTML = '', mHTML = '', iHTML = ''; products.forEach(p => { const matchesFilter = (currentCat === "الكل" || p.cat === currentCat || p.targetGroup === currentCat); if(!matchesFilter) return; const isMissingAutomatically = hasActiveInventory && !inventoriedNames.includes(p.name); const isMissingGlobal = cart.missing.includes(p.name) || isMissingAutomatically; // قسم المبيعات if(activePage === 'page-sales') { const v = cart.sales[p.name] || 0; let w = productWeights[p.name] || 1; let color = w === 2 ? 'var(--success)' : (w === 1.5 ? 'var(--warning)' : 'var(--danger)'); let missingBadge = isMissingGlobal ? `
ناقص
` : ''; let invVal = cart.inventory[p.name] || 0; let inventoryBadge = invVal > 0 ? `
${invVal} جرد
` : ''; let promoBadge = ''; if(promos[market] && promos[market][p.name]) { const pData = promos[market][p.name]; if(today >= pData.start && today <= pData.end) { // تم تغيير right إلى left وتغيير الخلفية إلى لون أصفر فاتح #fef08a promoBadge = `
عرض 🔥
`; } } // سطر واحد فقط للإضافة (تم حذف التكرار) sHTML += `
${missingBadge}${inventoryBadge}${promoBadge}
${p.name}
📦 ${p.pack || 1} حبة/كرتون
`; } // قسم المرتجعات if(activePage === 'page-returns') { const rv = cart.returns[p.name] || 0; rHTML += `
${p.name}
${p.pack || 1} حبة/كرتون
`; } // قسم الجرد if(activePage === 'page-inventory') { const iv = cart.inventory[p.name] || 0; iHTML += `
${p.name}
${p.pack || 1} حبة/كرتون
`; } // قسم النواقص if(activePage === 'page-missing') { const isManualMissing = cart.missing.includes(p.name); const isHighlighted = isManualMissing || isMissingAutomatically; mHTML += `
${p.name}
`; } }); // تحديث الواجهة مرة واحدة فقط (السرعة القصوى) if(sGrid) sGrid.innerHTML = sHTML; if(rGrid) rGrid.innerHTML = rHTML; if(mGrid) mHTML !== '' ? mGrid.innerHTML = mHTML : ''; if(iGrid) iGrid.innerHTML = iHTML; } function updateQty(name, type, delta) { cart[type][name] = Math.max(0, (cart[type][name] || 0) + delta); if(cart[type][name] === 0) delete cart[type][name]; updateUI(); } function toggleMissing(name) { if(cart.missing.includes(name)) cart.missing = cart.missing.filter(i => i !== name); else cart.missing.push(name); updateUI(); } function removeFromCart(name, type) { if (type === 'missing') cart.missing = cart.missing.filter(i => i !== name); else delete cart[type][name]; updateUI(); } function updateUI() { updateBadge(); // طلب تحديث الواجهة فقط في أوقات الفراغ لضمان سلاسة الحركة requestAnimationFrame(() => { renderProducts(); updateSummary(); updateTargetUI(); checkOOSPrediction(); }); } function updateBadge() { const count = Object.keys(cart.sales).length + Object.keys(cart.returns).length + Object.keys(cart.inventory).length + cart.missing.length; document.getElementById('cart-badge').innerText = count; } function updateSummary() { let html = '

📋 مراجعة الزيارة الحالية:


'; for(let n in cart.sales) html += `
🚀 مبيعات: ${n} (${cart.sales[n]})
`; for(let n in cart.returns) html += `
🔄 مرتجع: ${n} (${cart.returns[n]})
`; for(let n in cart.inventory) html += `
📋 جرد: ${n} (${cart.inventory[n]})
`; if(cart.missing.length) { cart.missing.forEach(n => { html += `
❌ ناقص: ${n}
`; }); } document.getElementById('cart-summary').innerHTML = html; } function initSigs() { ['rep', 'sup', 'store'].forEach(id => { const canvas = document.getElementById('sig-'+id); if(canvas) { sigs[id] = new SignaturePad(canvas, { backgroundColor: 'white' }); const r = window.devicePixelRatio || 1; canvas.width = canvas.offsetWidth * r; canvas.height = canvas.offsetHeight * r; canvas.getContext("2d").scale(r, r); } }); } function clearSigs() { Object.values(sigs).forEach(s => s && s.clear()); } async function finalizeVisit() { const name = document.getElementById('rep_name').value; const id = document.getElementById('rep_id').value; const market = document.getElementById('market_select').value; const btn = document.getElementById('btn-finalize'); if(!market) return alert("يرجى اختيار سوق أولاً"); if(!name || !id) return alert("أكمل بيانات المندوب"); const inventoriedNames = Object.keys(cart.inventory); if(inventoriedNames.length > 0) { products.forEach(p => { if(!inventoriedNames.includes(p.name) && !cart.missing.includes(p.name)) { cart.missing.push(p.name); } }); } const hasData = Object.keys(cart.sales).length > 0 || Object.keys(cart.returns).length > 0 || Object.keys(cart.inventory).length > 0 || cart.missing.length > 0; if (!hasData) return alert("⚠️ لا يمكن إغلاق الزيارة بدون بيانات."); if(sigs.rep && sigs.rep.isEmpty()) return alert("يجب توقيع المندوب"); btn.disabled = true; btn.innerText = "جاري الحفظ في IndexedDB... ⏳"; if(timerInterval) clearInterval(timerInterval); const duration = getVisitDuration(); const finalDate = new Date().toLocaleDateString('en-CA'); let dataToSave = { visit_id: Date.now(), rep_name: name, rep_id: id, market: market, sales: {...cart.sales}, returns: {...cart.returns}, inventory: {...cart.inventory}, missing: [...cart.missing], location: locationData, note: document.getElementById('visit_note').value, sigs: { rep: sigs.rep.toDataURL('image/png'), sup: sigs.sup.isEmpty() ? null : sigs.sup.toDataURL('image/png'), store: sigs.store.isEmpty() ? null : sigs.store.toDataURL('image/png') }, date: finalDate, time: new Date().toLocaleTimeString('ar-EG'), duration: duration, shelfPhotos: { before: [...shelfPhotos.before], after: [...shelfPhotos.after] } }; try { await saveHistoryDB(dataToSave); history = await getAllHistoryDB(); completeFinalization(finalDate, btn); } catch (e) { alert("فشل الحفظ! تأكد من وجود مساحة على الجهاز."); btn.disabled = false; } } function completeFinalization(finalDate, btn) { cart = { sales: {}, returns: {}, missing: [], inventory: {} }; shelfPhotos = { before: [], after: [] }; clearSigs(); document.getElementById('visit_note').value = ""; document.getElementById('history_date_filter').value = finalDate; document.getElementById('preview-before').innerHTML = ""; document.getElementById('preview-after').innerHTML = ""; document.getElementById('timer-display').style.display = 'none'; visitStartTime = null; updateUI(); alert("✅ تم إغلاق الزيارة وحفظ البيانات في IndexedDB بنجاح."); btn.disabled = false; btn.innerText = "إغلاق الزيارة واعتماد الطلب ✅"; showPage('page-history', document.querySelector('.nav-item:nth-child(7)')); } function updateHubButtons() { const market = document.getElementById('market_select').value; const container = document.getElementById('hub-buttons-container'); if(!market) return; container.innerHTML = `

${market}

🚀 تقارير الزيارة ( PDF )

🧠 ذكاء الأعمال (التنبؤ والتارجت)

`; } function shareSpecificWA(visitId, type = 'all', marketName = '') { const visits = history.filter(h => h.market === marketName); const v = visitId ? history.find(h => h.visit_id === visitId) : (visits.length > 0 ? visits[visits.length - 1] : null); if(!v) return alert("لا توجد بيانات"); let msg = `*🚀 تقرير زيارة - ${companyName}*\n`; msg += `*👤 المندوب:* ${v.rep_name}\n`; msg += `*🆔 الرقم الوظيفي:* ${v.rep_id}\n`; msg += `*🏪 السوق:* ${v.market}\n`; msg += `*⏳ مدة الزيارة:* ${v.duration || '--'}\n`; msg += `----------------------------\n`; if(v.sales && Object.keys(v.sales).length > 0) { msg += `*📦 المبيعات:*\n`; for(let n in v.sales) msg += `• ${n}: *${v.sales[n]}*\n`; } msg += `\n----------------------------\n`; msg += `*Elmandob System 2026*`; window.open(`https://wa.me/?text=${encodeURIComponent(msg)}`); } async function renderHistory() { const filterDate = document.getElementById('history_date_filter').value; const filtered = history.filter(h => h.date === filterDate); document.getElementById('history-list').innerHTML = filtered.slice().reverse().map(v => `
${v.market} ${v.time}
⏳ المدة: ${v.duration || '--'} | التاريخ: ${v.date}
`).join(''); initSalesChart(filtered); } function initSalesChart(data) { const canvas = document.getElementById('salesChart'); if(!canvas) return; const ctx = canvas.getContext('2d'); if (salesChart) salesChart.destroy(); salesChart = new Chart(ctx, { type: 'bar', data: { labels: data.map(v => v.market), datasets: [{ label: 'مبيعات (كرتون)', data: data.map(v => Object.values(v.sales).reduce((a,b)=>a+b, 0)), backgroundColor: '#3b82f6' }] } }); } function shareDailyWA() { const filterDate = document.getElementById('history_date_filter').value; const filtered = history.filter(h => h.date === filterDate); let msg = `*📊 ملخص اليوم (${filterDate})*\n`; filtered.forEach(v => { msg += `• ${v.market}: ${Object.values(v.sales).reduce((a,b)=>a+b,0)} ك (مدة: ${v.duration || '--'})\n`; }); window.open(`https://wa.me/?text=${encodeURIComponent(msg)}`); } async function deleteVisit(id) { if(confirm("حذف الزيارة نهائياً من الجهاز?")) { await deleteHistoryDB(id); history = await getAllHistoryDB(); renderHistory(); } } function showPage(id, el) { document.querySelectorAll('.page-content').forEach(p => p.style.display = 'none'); document.getElementById(id).style.display = 'block'; const filterablePages = ['page-sales', 'page-inventory', 'page-returns', 'page-missing']; if (filterablePages.includes(id)) { document.getElementById('filter-wrapper').style.display = 'block'; } else { document.getElementById('filter-wrapper').style.display = 'none'; } document.querySelectorAll('.nav-item').forEach(n => n.classList.remove('active')); if(el) el.classList.add('active'); if(id === 'page-cart') setTimeout(initSigs, 100); if(id === 'page-history') renderHistory(); if(id === 'page-admin') { renderAdminProducts(); renderAdminCategories(); renderMarkets(); renderAdminTargetGroups(); updatePromoSelectors(); // التحديث الجديد renderAdminPromos(); // التحديث الجديد } if(id === 'page-reports-hub') updateHubButtons(); if(id === 'page-sales') updateTargetUI(); renderProducts(); } function getLocation() { if (navigator.geolocation) { const options = { enableHighAccuracy: true, // تفعيل أعلى دقة ممكنة باستخدام GPS timeout: 10000, // انتظار 10 ثوانٍ كحد أقصى maximumAge: 0 // عدم استخدام موقع قديم مخزن }; document.getElementById('loc-display').innerText = "⌛ جاري تحديد الموقع بدقة..."; navigator.geolocation.getCurrentPosition(pos => { const lat = pos.coords.latitude; const lng = pos.coords.longitude; // حفظ الإحداثيات بدقة 6 أرقام عشرية (دقة تصل لـ 10 سم) locationData = `${lat.toFixed(6)},${lng.toFixed(6)}`; document.getElementById('loc-display').innerText = "✅ الموقع مسجل بدقة عالية"; }, err => { document.getElementById('loc-display').innerText = "❌ فشل تحديد الموقع، تأكد من الـ GPS"; }, options); } } function saveProfile() { localStorage.setItem('fixed_rep_id', document.getElementById('rep_id').value); alert("✅ تم حفظ البيانات"); } function loadProfile() { document.getElementById('rep_name').value = localStorage.getItem('rep_name_fixed') || ''; document.getElementById('rep_id').value = localStorage.getItem('fixed_rep_id') || ''; } function saveToLocal() { localStorage.setItem('mandob_p_26', JSON.stringify(products)); localStorage.setItem('mandob_m_26', JSON.stringify(markets)); localStorage.setItem('mandob_cats', JSON.stringify(categories)); localStorage.setItem('mandob_cat_targets', JSON.stringify(categoryTargets)); localStorage.setItem('m_weights', JSON.stringify(marketWeights)); localStorage.setItem('p_weights', JSON.stringify(productWeights)); localStorage.setItem('target_groups_26', JSON.stringify(targetGroups)); localStorage.setItem('group_targets_26', JSON.stringify(groupTargets)); } function masterDelete() { if(confirm("حذف شامل؟")) { localStorage.clear(); indexedDB.deleteDatabase(dbName); location.reload(); } } function updateUserPassword() { const newPass = document.getElementById('change_pass_input').value; if(newPass.length < 4) return alert("كلمة المرور ضعيفة جداً"); localStorage.setItem('rep_pass_secure', simpleHash(newPass)); document.getElementById('change_pass_input').value = ""; alert("✅ تم تشفير وتحديث كلمة المرور بنجاح"); } window.onload = init; // تسجيل Service Worker لـ PWA if ('serviceWorker' in navigator) { window.addEventListener('load', () => { navigator.serviceWorker.register('sw.js') .then(reg => console.log('PWA Service Worker Registered')) .catch(err => console.log('PWA Service Worker Failed', err)); }); } // وظيفة إضافة عرض جديد function addPromo() { const m = document.getElementById('promo_market_sel').value; const p = document.getElementById('promo_prod_sel').value; const start = document.getElementById('promo_start').value; const end = document.getElementById('promo_end').value; if(!m || !p || !start || !end) return alert("برجاء إكمال بيانات العرض (السوق، الصنف، التواريخ)"); if(!promos[m]) promos[m] = {}; promos[m][p] = { start, end }; localStorage.setItem('mandob_promos', JSON.stringify(promos)); renderAdminPromos(); alert("✅ تم إضافة العرض بنجاح"); } // وظيفة حذف عرض function deletePromo(market, prod) { if(confirm("هل تريد حذف هذا العرض؟")) { delete promos[market][prod]; localStorage.setItem('mandob_promos', JSON.stringify(promos)); renderAdminPromos(); } } // وظيفة عرض العروض في صفحة الضبط function renderAdminPromos() { const m = document.getElementById('promo_market_sel').value; const list = document.getElementById('admin-promo-list'); if(!m) { list.innerHTML = ""; return; } let html = ""; if(promos[m]) { for(let pName in promos[m]) { const d = promos[m][pName]; html += `
${pName}
من ${d.start} إلى ${d.end}
`; } } list.innerHTML = html || "

لا توجد عروض مسجلة لهذا السوق

"; } // تحديث القوائم عند فتح صفحة الضبط // ابحث عن وظيفة showPage وفي حالة id === 'page-admin' أضف هؤلاء: function updatePromoSelectors() { document.getElementById('promo_market_sel').innerHTML = `` + markets.map(m => ``).join(''); document.getElementById('promo_prod_sel').innerHTML = `` + products.map(p => ``).join(''); }