// admin.jsx — admin panel: products CRUD, orders, stats, customers function AdminLayout({ route, setRoute, lang, children }) { const t = (ar, en) => lang === 'ar' ? ar : en; const items = [ { id: 'admin-products', label: t('المنتجات', 'Products'), icon: }, { id: 'admin-orders', label: t('الطلبات', 'Orders'), icon: }, { id: 'admin-stats', label: t('الإحصائيات', 'Analytics'), icon: }, { id: 'admin-customers', label: t('العملاء', 'Customers'), icon: }, { id: 'admin-settings', label: t('الإعدادات', 'Settings'), icon: }, ]; return (
{children}
); } // ── Products ─────────────────────────────────────────────── function AdminProducts({ lang }) { const t = (ar, en) => lang === 'ar' ? ar : en; const [products, setProducts] = React.useState([]); const [loading, setLoading] = React.useState(true); const [q, setQ] = React.useState(''); const [familyFilter, setFamilyFilter] = React.useState('all'); const [edit, setEdit] = React.useState(null); // product being edited (modal) const [creating, setCreating] = React.useState(false); const load = React.useCallback(() => { setLoading(true); apiGet('products').then(d => { setProducts(Array.isArray(d) ? d : []); setLoading(false); }); }, []); React.useEffect(() => { load(); }, [load]); const filtered = products.filter(p => { if (familyFilter !== 'all' && p.family !== familyFilter) return false; if (q && !p.name_ar.includes(q) && !p.name_en.toLowerCase().includes(q.toLowerCase())) return false; return true; }); const save = async (p) => { if (creating) await apiPost('products', p); else await apiPut('products', p.id, p); setEdit(null); setCreating(false); load(); }; const del = async (id) => { if (!confirm(t('حذف المنتج؟', 'Delete this product?'))) return; await apiDel('products', id); load(); }; return ( <>
{t('الإدارة · 01', 'Manage · 01')}

{t('المنتجات', 'Products')}

{products.length} {t('منتج', 'products')} · {products.filter(p => p.stock < 10).length} {t('مخزون منخفض', 'low stock')}
setQ(e.target.value)} />
{filtered.map(p => { const fam = SCENT_FAMILIES.find(f => f.id === p.family); const stockClass = p.stock < 8 ? 'crit' : p.stock < 15 ? 'low' : ''; return ( ); })}
{t('المنتج', 'Product')} {t('العائلة', 'Family')} {t('التركيز', 'Concentration')} {t('المخزون', 'Stock')} {t('السعر', 'Price')} {t('التقييم', 'Rating')}
N°{p.id.slice(1)}
{p.name_ar}
{p.name_en}
{lang === 'ar' ? fam.ar : fam.en} {p.concentration}
{p.stock}
{p.price} AED {p.rating}
{edit && { setEdit(null); setCreating(false); }} lang={lang} creating={creating} />} ); } function ProductEditor({ product, onSave, onClose, lang, creating }) { const t = (ar, en) => lang === 'ar' ? ar : en; const [p, setP] = React.useState(product); const upd = (k, v) => setP({ ...p, [k]: v }); const updSize = (i, k, v) => { const sizes = [...p.sizes]; sizes[i] = { ...sizes[i], [k]: Number(v) }; setP({ ...p, sizes }); }; return (
e.stopPropagation()}>
{t('معاينة', 'Preview')}
ISTABRAQ
{p.name_en || 'NAME'}
{SCENT_FAMILIES.map(f => (
{creating ? t('منتج جديد', 'New product') : t('تعديل منتج', 'Edit product')}

{p.name_ar || t('بلا اسم', 'Untitled')}

upd('name_ar', e.target.value)} />
upd('name_en', e.target.value)} />
upd('origin', e.target.value)} placeholder="e.g. Mysore" />
upd('year', +e.target.value)} />
upd('top_ar', e.target.value)} />
upd('heart_ar', e.target.value)} />
upd('base_ar', e.target.value)} />
upd('price', +e.target.value)} />
upd('stock', +e.target.value)} />
{t('الأحجام والأسعار', 'Sizes & prices')}
{p.sizes.map((s, i) => (
updSize(i, 'price', e.target.value)} style={{ fontSize: 18, fontFamily: 'var(--f-en-head)', fontStyle: 'italic' }} />
))}
); } // ── Orders ───────────────────────────────────────────────── function AdminOrders({ lang }) { const t = (ar, en) => lang === 'ar' ? ar : en; const [orders, setOrders] = React.useState([]); const [statusFilter, setStatusFilter] = React.useState('all'); React.useEffect(() => { apiGet('orders').then(d => setOrders(Array.isArray(d) ? d : [])); }, []); const filtered = orders.filter(o => statusFilter === 'all' || o.status === statusFilter); const setStatus = (id, status) => { setOrders(orders.map(o => o.id === id ? { ...o, status } : o)); apiPut('orders', id, { status }); }; const weekSales = orders.reduce((s, o) => s + (o.total || 0), 0); const tabs = [ { id: 'all', label: t('الكل', 'All'), count: orders.length }, { id: 'pending', label: t('قيد الانتظار', 'Pending'), count: orders.filter(o => o.status === 'pending').length }, { id: 'shipped', label: t('تم الشحن', 'Shipped'), count: orders.filter(o => o.status === 'shipped').length }, { id: 'delivered', label: t('تم التسليم', 'Delivered'), count: orders.filter(o => o.status === 'delivered').length }, { id: 'cancelled', label: t('ملغية', 'Cancelled'), count: orders.filter(o => o.status === 'cancelled').length }, ]; const statusLabel = (s) => ({ pending: t('قيد الانتظار', 'Pending'), shipped: t('تم الشحن', 'Shipped'), delivered: t('تم التسليم', 'Delivered'), cancelled: t('ملغية', 'Cancelled'), })[s]; return ( <>
{t('الإدارة · 02', 'Manage · 02')}

{t('الطلبات', 'Orders')}

{orders.length} {t('طلب · إجمالي المبيعات', 'orders · total sales')} AED {weekSales.toLocaleString()}
{tabs.map(tab => ( ))}
{filtered.map(o => ( ))}
{t('رقم الطلب', 'Order')} {t('العميل', 'Customer')} {t('المدينة', 'City')} {t('التاريخ', 'Date')} {t('العناصر', 'Items')} {t('المجموع', 'Total')} {t('الحالة', 'Status')}
{o.id}
{o.customer}
{o.email}
{o.city} {o.date} {o.items} AED {o.total.toLocaleString()}
); } // ── Stats / Analytics ────────────────────────────────────── function AdminStats({ lang }) { const t = (ar, en) => lang === 'ar' ? ar : en; const [stats, setStats] = React.useState(null); const [fin, setFin] = React.useState(null); const [products, setProducts] = React.useState([]); React.useEffect(() => { apiGet('stats').then(setStats); apiGet('finance').then(setFin); apiGet('products').then(d => setProducts(Array.isArray(d) ? d : [])); }, []); if (!stats || !fin) return
; const series = fin.series.slice(-14).map(s => s.rev); const max = Math.max(1, ...series); const peak = Math.max(...series); const peakIdx = series.indexOf(peak); const cards = [ { lbl: t('الإيرادات', 'Revenue'), val: `AED ${(stats.revenue / 1000).toFixed(1)}K` }, { lbl: t('الطلبات', 'Orders'), val: String(stats.orders) }, { lbl: t('العملاء', 'Customers'), val: String(stats.customers) }, { lbl: t('متوسط قيمة الطلب', 'Avg order value'), val: `AED ${stats.aov}` }, ]; const fsplit = (stats.family_split || []).filter(x => x.value > 0); const total = fsplit.reduce((s, x) => s + x.value, 0) || 1; let acc = 0; const r = 70, circ = 2 * Math.PI * r; const lowStock = [...products].sort((a, b) => a.stock - b.stock).slice(0, 6); return ( <>
{t('الإدارة · 03', 'Manage · 03')}

{t('الإحصائيات', 'Analytics')}

{t('من بياناتك الفعلية', 'From your real data')}
{cards.map((c, i) => (
{c.lbl}
{c.val}
))}

{t('الإيرادات اليومية · AED', 'Daily revenue · AED')}

peak: {peak.toLocaleString()} AED
{series.map((v, i) => (
0 ? ' peak' : '')} style={{ height: `${(v / max) * 100}%` }} title={`AED ${v}`} /> ))}
{t('آخر ١٤ يوماً', 'Last 14 days')}

{t('المبيعات حسب العائلة', 'Sales by family')}

{fsplit.length === 0 ? (
{t('تظهر بعد أول عملية بيع', 'appears after the first sale')}
) : (
{fsplit.map((s) => { const fam = SCENT_FAMILIES.find(f => f.id === s.id) || { tone: '#8C4A2B' }; const len = (s.value / total) * circ; const seg = ( ); acc += len + 2; return seg; })}
100%
{t('المبيعات', 'sales')}
{fsplit.map(s => { const fam = SCENT_FAMILIES.find(f => f.id === s.id) || { tone: '#8C4A2B', ar: s.id, en: s.id }; return (
{lang === 'ar' ? fam.ar : fam.en} {Math.round(s.value / total * 100)}%
); })}
)}

{t('المخزون · الأقل توفّراً', 'Inventory · lowest stock')}

{products.length === 0 ? (
{t('أضف منتجات من قسم المنتجات', 'Add products in the Products section')}
) : ( {lowStock.map(p => { const fam = SCENT_FAMILIES.find(f => f.id === p.family); return ( ); })}
{t('المنتج', 'Product')}{t('العائلة', 'Family')}{t('السعر', 'Price')}{t('المخزون', 'Stock')}
{lang === 'ar' ? p.name_ar : p.name_en}
{lang === 'ar' ? p.name_en : p.name_ar}
{fam ? (lang === 'ar' ? fam.ar : fam.en) : '—'} AED {p.price.toLocaleString()}
{p.stock}
)}
); } // ── Customers ────────────────────────────────────────────── function AdminCustomers({ lang }) { const t = (ar, en) => lang === 'ar' ? ar : en; const [CUSTOMERS, setCustomers] = React.useState([]); React.useEffect(() => { apiGet('customers').then(d => setCustomers(Array.isArray(d) ? d : [])); }, []); const vip = CUSTOMERS.filter(c => c.tier === 'VIP').length; return ( <>
{t('الإدارة · 04', 'Manage · 04')}

{t('العملاء', 'Customers')}

{CUSTOMERS.length} {t('عميل', 'customers')} · {vip} VIP
{CUSTOMERS.map(c => ( ))}
{t('الاسم', 'Name')} {t('البريد', 'Email')} {t('الطلبات', 'Orders')} {t('الإنفاق', 'Spent')} {t('المستوى', 'Tier')}
{c.name[0]}
{c.name}
{c.email} {c.orders} AED {c.spent.toLocaleString()} {c.tier}
); } function AdminSettings({ lang }) { const t = (ar, en) => lang === 'ar' ? ar : en; const [s, setS] = React.useState(null); const [saved, setSaved] = React.useState(false); const [busy, setBusy] = React.useState(false); React.useEffect(() => { apiGet('settings').then(d => setS(d || {})); }, []); const upd = (k, v) => { setS({ ...s, [k]: v }); setSaved(false); }; const save = async () => { setBusy(true); await apiPut('settings', null, s); setBusy(false); setSaved(true); }; if (!s) return
; return ( <>
{t('الإدارة · 05', 'Manage · 05')}

{t('الإعدادات', 'Settings')}

{t('بيانات المتجر، الشحن، الضريبة وبوابة الدفع', 'Store, shipping, tax & payment')}
{saved && {t('تم الحفظ ✓', 'Saved ✓')}}

{t('بيانات المتجر', 'Store info')}

upd('store_name_ar', e.target.value)} />
upd('store_name_en', e.target.value)} />
upd('contact_email', e.target.value)} />
upd('contact_phone', e.target.value)} />
upd('address', e.target.value)} />
upd('trn', e.target.value)} />

{t('الشحن والضريبة', 'Shipping & tax')}

upd('vat_rate', e.target.value)} />
upd('free_ship_over', e.target.value)} />
upd('flat_shipping', e.target.value)} />

{t('بوابة الدفع', 'Payment gateway')}

upd('pay_publishable', e.target.value)} placeholder="pk_..." />
upd('pay_secret', e.target.value)} placeholder="sk_..." />
{t('أدخل مفاتيح حسابك من المزوّد لتفعيل الدفع بالبطاقة عند الشراء.', 'Enter your provider keys to enable card payment at checkout.')}
); } window.AdminLayout = AdminLayout; window.AdminProducts = AdminProducts; window.AdminOrders = AdminOrders; window.AdminStats = AdminStats; window.AdminCustomers = AdminCustomers; window.AdminSettings = AdminSettings;