diff --git a/app.js b/app.js
index 2cc2c9d..5cfaa97 100644
--- a/app.js
+++ b/app.js
@@ -45,6 +45,37 @@ let customRows = [];
let customIdCounter = 0;
let orderLog = [];
+// =========================================================
+// 1ب) اتصال بالباك إند (api.php) — مصدر واحد للأوردرات والعملاء
+// على كل الأجهزة (بدل localStorage)
+// =========================================================
+const API_BASE = 'https://gamboola-api.momaher.site/api.php';
+let serverOrders = []; // آخر نسخة معروفة من الأوردرات على السيرفر
+let editingOrderId = null; // لو مش null، يبقى إحنا بنعدّل أوردر موجود مش بننشئ واحد جديد
+
+async function apiCall(actionQuery, method = 'GET', body){
+ const url = `${API_BASE}?action=${actionQuery}`;
+ const opts = { method };
+ if(body !== undefined){
+ opts.headers = { 'Content-Type': 'application/json' };
+ opts.body = JSON.stringify(body);
+ }
+ const res = await fetch(url, opts);
+ let data;
+ try { data = await res.json(); } catch(e){ throw new Error('bad_response'); }
+ if(!data || data.ok !== true) throw new Error((data && data.error) || 'api_error');
+ return data;
+}
+
+async function refreshOrders(){
+ try {
+ const data = await apiCall('list_orders', 'GET');
+ serverOrders = data.orders || [];
+ } catch(e){
+ showToast('تعذّر تحميل الأوردرات من السيرفر — تأكدي من الاتصال');
+ }
+}
+
// =========================================================
// 2) Navigation (Bottom Nav + Screens)
// =========================================================
@@ -55,6 +86,7 @@ const SCREEN_TITLES = {
customer: 'بيانات العميل',
preview: 'إرسال الأوردر',
earnings: 'الأرباح',
+ customers: 'قاعدة العملاء',
};
function showScreen(name){
@@ -67,9 +99,7 @@ function showScreen(name){
window.scrollTo(0,0);
updateStickyBar();
if(name === 'earnings') renderCounter();
- // ملحوظة: render() بيتنادى من كل الـ inputs عند أي تغيير، وبيحدّث
- // الداشبورد ونص المعاينة أوتوماتيك، فمش محتاجين ننادي حاجة تانية هنا
- // (تجنّبًا لأي استدعاء متبادل بين الدوال).
+ if(name === 'customers') loadCustomersScreen();
render();
}
@@ -233,12 +263,6 @@ function loadRate(){ try { const v = localStorage.getItem('gamboola_rate'); if(v
function saveNotifyPhone(){ try { localStorage.setItem('gamboola_notify_phone', document.getElementById('notifyPhone').value); } catch(e){} }
function loadNotifyPhone(){ try { const v = localStorage.getItem('gamboola_notify_phone'); if(v) document.getElementById('notifyPhone').value = v; } catch(e){} }
-function loadOrderLog(){
- try { const v = localStorage.getItem('gamboola_order_log'); orderLog = v ? JSON.parse(v) : []; }
- catch(e){ orderLog = []; }
-}
-function saveOrderLog(){ try { localStorage.setItem('gamboola_order_log', JSON.stringify(orderLog)); } catch(e){} }
-
function todayStr(){
const d = new Date();
return `${d.getDate()}/${d.getMonth()+1}/${d.getFullYear()}`;
@@ -288,7 +312,6 @@ function render(){
document.getElementById('productsTotal').textContent = productsTotal + ' ج';
document.getElementById('grandTotal').textContent = grandTotal + ' ج';
- // ملخص شاشة "الأوردر"
const summaryEl = document.getElementById('orderSummaryList');
if(items.length === 0){
summaryEl.innerHTML = '
🛒مفيش منتجات متضافة للطلب لسه
';
@@ -308,14 +331,19 @@ function render(){
updateStickyBar(totalQty, grandTotal);
updateNavBadge(totalQty);
- // بنحدّث الداشبورد ونص الأوردر النهائي دايمًا (حسابات رخيصة)،
- // من غير أي استدعاء متبادل بين الدوال عشان نتجنب أي تكرار لا نهائي.
renderHome(items, productsTotal);
buildOutputText(items, shipping, grandTotal);
+ updateRegisterBtnLabel();
return { items, productsTotal, shipping, grandTotal };
}
+function updateRegisterBtnLabel(){
+ const btn = document.getElementById('registerOrderBtn');
+ if(!btn) return;
+ btn.textContent = editingOrderId ? '💾 حفظ التعديل' : '✅ تسجيل هذا الأوردر';
+}
+
// ⚠️ صيغة نص الأوردر — الأرقام الثابتة (1500 / 000000) والفواصل والترتيب
// منقولين حرفيًا زي ما اتأكد عليهم. ممنوع تتغير إلا بطلب صريح.
function buildOutputText(items, shipping, grandTotal){
@@ -389,39 +417,83 @@ function sendWhatsapp(){
}
// =========================================================
-// 8) عداد الأوردرات والأرباح (نفس المنطق الأصلي)
+// 8) عداد الأوردرات والأرباح — مصدره السيرفر (مش الجهاز) عشان
+// يتزامن على كل الأجهزة، ودعم تعديل أوردر موجود
// =========================================================
-function registerOrder(){
- const name = document.getElementById('custName').value || '(بدون اسم)';
- const grand = document.getElementById('grandTotal').textContent;
- const date = document.getElementById('orderDate').value || todayStr();
- orderLog.push({ name, grand, date, ts: Date.now() });
- saveOrderLog();
- renderCounter();
- renderHome();
- showToast('تم تسجيل الأوردر ✅');
+async function registerOrder(){
+ const { items, productsTotal, shipping, grandTotal } = render();
+ if(items.length === 0){
+ showToast('لازم تختاري منتج واحد على الأقل قبل التسجيل');
+ return;
+ }
+ const payload = {
+ customer_name: document.getElementById('custName').value || '',
+ phone1: document.getElementById('phone1').value || '',
+ phone2: document.getElementById('phone2').value || '',
+ address: document.getElementById('address').value || '',
+ agent_name: document.getElementById('agentName').value || '',
+ order_date: document.getElementById('orderDate').value || todayStr(),
+ items,
+ shipping,
+ products_total: productsTotal,
+ grand_total: grandTotal,
+ order_text: document.getElementById('outputText').value || '',
+ };
+
+ const btn = document.getElementById('registerOrderBtn');
+ if(btn) btn.disabled = true;
+ try {
+ if(editingOrderId){
+ await apiCall(`update_order&id=${editingOrderId}`, 'POST', payload);
+ showToast('تم تعديل الأوردر ✅');
+ editingOrderId = null;
+ } else {
+ await apiCall('create_order', 'POST', payload);
+ showToast('تم تسجيل الأوردر ✅');
+ }
+ await refreshOrders();
+ renderCounter();
+ renderHome();
+ updateRegisterBtnLabel();
+ } catch(e){
+ showToast('فشل الاتصال بالسيرفر — جربي تاني');
+ } finally {
+ if(btn) btn.disabled = false;
+ }
}
-function undoLastOrder(){
- if(orderLog.length === 0) return;
+async function undoLastOrder(){
+ if(serverOrders.length === 0) return;
if(!confirm('متأكدة تمسحي آخر أوردر مسجل؟')) return;
- orderLog.pop();
- saveOrderLog();
- renderCounter();
- renderHome();
+ const last = serverOrders[0];
+ try {
+ await apiCall(`delete_order&id=${last.id}`, 'POST');
+ await refreshOrders();
+ renderCounter();
+ renderHome();
+ } catch(e){
+ showToast('فشل حذف الأوردر — جربي تاني');
+ }
}
-function clearLog(){
+async function clearLog(){
+ if(serverOrders.length === 0) return;
if(!confirm('متأكدة تمسحي كل السجل والعدّاد يرجع صفر؟')) return;
- orderLog = [];
- saveOrderLog();
- renderCounter();
- renderHome();
+ try {
+ for(const o of serverOrders){
+ await apiCall(`delete_order&id=${o.id}`, 'POST');
+ }
+ await refreshOrders();
+ renderCounter();
+ renderHome();
+ } catch(e){
+ showToast('حصل خطأ أثناء مسح السجل — راجعي وحاولي تاني');
+ }
}
function renderCounter(){
const rate = Number(document.getElementById('rate').value) || 0;
- const count = orderLog.length;
+ const count = serverOrders.length;
const earned = count * rate;
document.getElementById('earnCount').textContent = count;
@@ -432,20 +504,100 @@ function renderCounter(){
if(count === 0){
logEl.innerHTML = '🗒️مفيش أوردرات مسجلة لسه
';
} else {
- const last15 = orderLog.slice(-15).reverse();
+ const last15 = serverOrders.slice(0, 15);
logEl.innerHTML = last15.map((o, i) => `
-
${o.grand}
-
#${count - i} — ${o.name}
-
${o.date}
+
${o.grand_total} ج
+
#${count - i} — ${o.customer_name || '(بدون اسم)'}
+
${o.order_date || ''}
+
`).join('');
+ logEl.querySelectorAll('[data-edit-order]').forEach(b => {
+ b.addEventListener('click', async () => {
+ const id = Number(b.dataset.editOrder);
+ b.disabled = true;
+ try {
+ const data = await apiCall(`get_order&id=${id}`, 'GET');
+ loadOrderIntoForm(data.order);
+ } catch(e){
+ showToast('تعذّر تحميل الأوردر للتعديل');
+ } finally {
+ b.disabled = false;
+ }
+ });
+ });
+ }
+}
+
+function loadOrderIntoForm(order){
+ resetCurrentOrder();
+ editingOrderId = null;
+
+ const items = order.items || [];
+ const usedIdx = new Set();
+
+ CATALOG.forEach((catObj, ci) => {
+ catObj.items.forEach((item, ii) => {
+ const idx = items.findIndex((it, i) => !usedIdx.has(i) && it.name === item.name);
+ if(idx === -1) return;
+ const it = items[idx];
+ usedIdx.add(idx);
+ const sizeIdx = item.sizes.findIndex(s => s[0] === it.size);
+ const sizeEl = document.getElementById(`size_${ci}_${ii}`);
+ if(sizeEl && sizeIdx !== -1) sizeEl.value = sizeIdx;
+ const qtyEl = document.getElementById(`qty_${ci}_${ii}`);
+ if(qtyEl) qtyEl.textContent = it.qty || 0;
+ });
+ });
+
+ items.forEach((it, i) => {
+ if(usedIdx.has(i)) return;
+ const id = customIdCounter++;
+ customRows.push({ id, name: it.name || '', size: it.size || '', price: it.price || 0, qty: it.qty || 0 });
+ });
+ renderCustomRows();
+
+ document.getElementById('custName').value = order.customer_name || '';
+ document.getElementById('phone1').value = order.phone1 || '';
+ document.getElementById('phone2').value = order.phone2 || '';
+ document.getElementById('address').value = order.address || '';
+ document.getElementById('agentName').value = order.agent_name || '';
+ document.getElementById('orderDate').value = order.order_date || todayStr();
+ document.getElementById('shipping').value = order.shipping || 0;
+
+ editingOrderId = order.id;
+ render();
+ showToast('وضع التعديل — عدّلي وبعدين اضغطي "حفظ التعديل"');
+ showScreen('products');
+}
+
+async function loadCustomersScreen(){
+ const el = document.getElementById('customersList');
+ if(!el) return;
+ el.innerHTML = 'جارِ التحميل...
';
+ try {
+ const data = await apiCall('list_customers', 'GET');
+ const customers = data.customers || [];
+ if(customers.length === 0){
+ el.innerHTML = '👤مفيش عملاء مسجلين لسه
';
+ return;
+ }
+ el.innerHTML = customers.map(c => `
+
+
${c.total_spent || 0} ج
+
${c.name || '(بدون اسم)'}${c.phone1 ? ' — ' + c.phone1 : ''}
+
${c.order_count || 0} أوردر${c.address ? ' • ' + c.address : ''}
+
+ `).join('');
+ } catch(e){
+ el.innerHTML = 'تعذّر تحميل قاعدة العملاء — تأكدي من الاتصال
';
}
}
function notifyWhatsapp(){
const rate = Number(document.getElementById('rate').value) || 0;
- const count = orderLog.length;
+ const count = serverOrders.length;
const earned = count * rate;
const msg = `تحديث أوردرات غمبولة 📊\nعدد الأوردرات: ${count}\nالمستحق: ${earned} جنيه`;
const text = encodeURIComponent(msg);
@@ -457,7 +609,6 @@ function notifyWhatsapp(){
// =========================================================
// 9) واجهة إضافية: Home dashboard / Sticky bar / Badge / Toast
// =========================================================
-// items/productsTotal اختياريين — لو معدّياش، بتتحسب من تاني (بدون ما تنادي render()).
function renderHome(items, productsTotal){
if(items === undefined){
const collected = collectItems();
@@ -465,15 +616,14 @@ function renderHome(items, productsTotal){
productsTotal = collected.productsTotal;
}
const rate = Number(document.getElementById('rate').value) || 0;
- document.getElementById('homeOrderCount').textContent = orderLog.length;
- document.getElementById('homeEarned').textContent = (orderLog.length * rate) + ' ج';
+ document.getElementById('homeOrderCount').textContent = serverOrders.length;
+ document.getElementById('homeEarned').textContent = (serverOrders.length * rate) + ' ج';
const shipping = Number(document.getElementById('shipping').value) || 0;
document.getElementById('homeCartItems').textContent = items.reduce((s,it)=>s+it.qty,0);
document.getElementById('homeCartTotal').textContent = (items.length ? productsTotal + shipping : 0) + ' ج';
}
-// totalQty/grandTotal اختياريين — لو معدّياش، بيتحسبوا من تاني.
function updateStickyBar(totalQty, grandTotal){
const bar = document.getElementById('stickyOrderBar');
const productsScreenActive = document.querySelector('.screen[data-screen="products"]').classList.contains('active');
@@ -533,6 +683,87 @@ function resetCurrentOrder(){
renderHome();
}
+// =========================================================
+// 9ب) استيراد أوردرات قديمة كانت متخزنة على الجهاز (localStorage) قبل
+// تحديث المزامنة — مرة واحدة بس، بموافقة صريحة من المستخدمة
+// =========================================================
+function parseOldGrand(grandStr){
+ const n = parseInt(String(grandStr).replace(/[^0-9]/g, ''), 10);
+ return isNaN(n) ? 0 : n;
+}
+
+function getOldLocalOrders(){
+ try {
+ const v = localStorage.getItem('gamboola_order_log');
+ return v ? JSON.parse(v) : [];
+ } catch(e){ return []; }
+}
+
+function checkOldLocalOrders(){
+ const banner = document.getElementById('importOldBanner');
+ if(!banner) return;
+ const migrated = localStorage.getItem('gamboola_migrated_v1') === '1';
+ const oldOrders = getOldLocalOrders();
+ const btn = document.getElementById('importOldBtn');
+ banner.classList.remove('hidden');
+
+ if(oldOrders.length === 0){
+ document.getElementById('importOldText').textContent =
+ 'فحصنا الجهاز ده (المتصفح اللي فاتح بيه دلوقتي) ومفيش بيانات أوردرات قديمة محفوظة فيه من قبل التحديث. لو الأوردرات القديمة كانت متسجلة من متصفح أو تطبيق تاني، افتحي الموقع من نفسه عشان يظهرلك عدد حقيقي هنا.';
+ if(btn) btn.disabled = true;
+ } else if(migrated){
+ document.getElementById('importOldText').textContent =
+ 'لقينا ' + oldOrders.length + ' أوردر قديم على الجهاز ده، لكن سبق واتعامل معاهم (استيراد أو تجاهل) قبل كده. لو عايزة تجربي تستوردي تاني، قوليلي.';
+ if(btn) btn.disabled = true;
+ } else {
+ document.getElementById('importOldText').textContent =
+ 'لقينا ' + oldOrders.length + ' أوردر مسجل على الجهاز ده من قبل التحديث، حابة تنقليهم للسيرفر عشان يتزامنوا مع باقي الأجهزة؟ (البيانات القديمة كانت بس اسم العميل والإجمالي والتاريخ، من غير تفاصيل المنتجات)';
+ if(btn) btn.disabled = false;
+ }
+}
+
+async function importOldLocalOrders(){
+ const oldOrders = getOldLocalOrders();
+ if(oldOrders.length === 0) return;
+ if(!confirm('هيتم استيراد ' + oldOrders.length + ' أوردر قديم للسيرفر. متأكدة؟')) return;
+
+ const btn = document.getElementById('importOldBtn');
+ if(btn) btn.disabled = true;
+ let success = 0, failed = 0;
+
+ for(const o of oldOrders){
+ const grand = parseOldGrand(o.grand);
+ const payload = {
+ customer_name: o.name || '',
+ phone1: '', phone2: '', address: '',
+ agent_name: '', order_date: o.date || '',
+ items: [],
+ shipping: 0,
+ products_total: grand,
+ grand_total: grand,
+ order_text: '',
+ };
+ try {
+ await apiCall('create_order', 'POST', payload);
+ success++;
+ } catch(e){ failed++; }
+ }
+
+ try { localStorage.setItem('gamboola_migrated_v1', '1'); } catch(e){}
+ if(btn) btn.disabled = false;
+ document.getElementById('importOldBanner').classList.add('hidden');
+ await refreshOrders();
+ renderCounter();
+ renderHome();
+ showToast(failed === 0 ? ('تم استيراد ' + success + ' أوردر ✅') : ('اتسجل ' + success + '، وفشل ' + failed));
+}
+
+function dismissImportOldOrders(){
+ if(!confirm('متأكدة تتجاهلي استيراد الأوردرات القديمة؟ مش هتظهر تاني.')) return;
+ try { localStorage.setItem('gamboola_migrated_v1', '1'); } catch(e){}
+ document.getElementById('importOldBanner').classList.add('hidden');
+}
+
// =========================================================
// 10) PWA: تسجيل Service Worker + زرار التثبيت
// =========================================================
@@ -567,7 +798,6 @@ function initInstallPrompt(){
showToast('تم تثبيت التطبيق 🎉');
});
- // iOS Safari: مفيش beforeinstallprompt، فبنعرض تلميح بسيط بدل ما نضايق المستخدم
const isIos = /iphone|ipad|ipod/i.test(navigator.userAgent);
const isStandalone = window.navigator.standalone === true || window.matchMedia('(display-mode: standalone)').matches;
const dismissed = localStorage.getItem('gamboola_ios_hint_dismissed') === '1';
@@ -600,14 +830,18 @@ function initFieldListeners(){
document.getElementById('clearLogBtn').addEventListener('click', clearLog);
document.getElementById('notifyWaBtn').addEventListener('click', notifyWhatsapp);
document.getElementById('startNewOrderBtn').addEventListener('click', () => {
+ editingOrderId = null;
resetCurrentOrder();
+ updateRegisterBtnLabel();
showScreen('products');
});
document.getElementById('goToCustomerBtn').addEventListener('click', () => showScreen('customer'));
document.getElementById('goToPreviewBtn').addEventListener('click', () => showScreen('preview'));
+ document.getElementById('importOldBtn').addEventListener('click', importOldLocalOrders);
+ document.getElementById('dismissImportBtn').addEventListener('click', dismissImportOldOrders);
}
-function init(){
+async function init(){
document.getElementById('orderDate').value = todayStr();
buildCategories();
renderCustomRows();
@@ -615,14 +849,15 @@ function init(){
loadAgentName();
loadRate();
loadNotifyPhone();
- loadOrderLog();
initFieldListeners();
initNav();
registerServiceWorker();
initInstallPrompt();
render();
+ await refreshOrders();
renderCounter();
renderHome();
+ checkOldLocalOrders();
showScreen('home');
}