/* Bonyan ERP — Dashboard / home (لوحة مدير النظام) */
function DashboardScreen({ onNav }) {
const I = window.BnyIcon;
const D = window.BnyData;
const { StatCard, Card, Badge, Money, Button } = window.BonyanDesignSystem_ad3ca0;
const isMobile = window.useBnyMobile();
window.useBnyInvoices(); // إعادة الرسم عند تعديل/حذف فاتورة من أي مكان
const openInv = (no) => window.BnyOpenInvoice(no);
// اشتقاقات حيّة
const outOfStock = window.BnyOutOfStockMaterials();
const lowStock = window.BnyLowStockMaterials();
const topProducts = window.BnyTopProductsThisMonth(5);
const stagnant = window.BnyStagnantMaterials();
const stagDays = D.stagnantDays || 30;
const aging = window.BnyDebtAging();
// يشمل ديون الدولار، ويرتّب حسب القيمة المكافئة بالدينار (١٠٠$ = ١٥٠٬٠٠٠ د.ع)
const debts = D.customers.filter((c) => c.balance > 0 || c.balanceUsd > 0).sort((a, b) => window.BnyDebtIqd(b) - window.BnyDebtIqd(a));
// مؤشرات حيّة (تظهر صفرًا للمشترك الجديد — لا أرقام وهمية)
const _today = new Date().toISOString().slice(0, 10);
const salesMonth = (D.invoices || []).filter((i) => i.type === "sale" && i.cur === "iqd" && (i.date || "").slice(0, 7) === _today.slice(0, 7))
.reduce((s, i) => s + (Number(i.total) || 0), 0);
const invToday = (D.invoices || []).filter((i) => i.date === _today).length;
const [showStockModal, setShowStockModal] = React.useState(false);
const [selCustomer, setSelCustomer] = React.useState(null);
const quick = [
{ label: "فاتورة بيع", icon: I.invoice, accent: true, go: "pos" },
{ label: "إضافة عميل", icon: I.users, go: "customers" },
{ label: "تسديد دين", icon: I.wallet, go: "customers" },
{ label: "فاتورة شراء", icon: I.supplier, go: "suppliers" },
];
const sectionTwo = { display: "grid", gridTemplateColumns: isMobile ? "1fr" : "1fr 1fr", gap: "var(--space-7)" };
return (
{/* quick actions */}
{quick.map((q) => (
))}
{/* KPIs — بطاقة تنبيه المخزون بارزة وقابلة للنقر */}
} value={salesMonth.toLocaleString("en-US")} unit="د.ع" />
} value={debts.reduce((s, c) => s + c.balance, 0).toLocaleString("en-US")} unit="د.ع" />
} value={String(invToday)} />
setShowStockModal(true)} />
{/* تنبيه: مواد تحتاج شراء (نافدة + ستنفد قريبًا) — قصير + عرض الكل */}
{ window.BnyPendingInvFilter = "reorder"; onNav("inventory"); }} />
{/* الرسم البياني — منحنى خطي مقارن مع Tooltip */}
onNav("reports")}>التقارير}>
مرّر أو انقر فوق أي شهر لعرض الرقم التفصيلي.
p.m)}
series={[{ name: "الأرباح", color: "var(--brand)", values: D.profit.map((p) => p.v) }]}
unit="مليون د.ع" />
{/* أعلى الديون المعلقة + أعمار الديون */}
onNav("customers")}>الكل}>
{debts.map((c, i) => {
const over = c.balance > c.limit;
return (
setSelCustomer(c)} title="اعرض معلومات العميل"
onMouseEnter={(e) => (e.currentTarget.style.background = "var(--surface-hover)")}
onMouseLeave={(e) => (e.currentTarget.style.background = "transparent")}
style={{ display: "flex", alignItems: "center", gap: 12, padding: "11px 8px", margin: "0 -8px", borderRadius: "var(--radius-md)", borderBottom: i < debts.length - 1 ? "1px solid var(--border-subtle)" : "none", cursor: "pointer", transition: "background var(--dur-fast,160ms) ease" }}>
{c.name.split(" ").slice(0, 2).map((w) => w[0]).join("")}
{c.name}
{over ?
تجاوز السقف :
ضمن الحد}
{c.balance > 0 && }
{c.balanceUsd > 0 && }
);
})}
{debts.length === 0 &&
لا ديون معلّقة
}
{/* أعلى 5 منتجات هذا الشهر + المواد الراكدة */}
{topProducts.map((p, i) => (
{i + 1}
{p.name}
{p.qty.toLocaleString("en-US")} {p.unit} مُباعة
))}
{topProducts.length === 0 &&
لا مبيعات لهذا الشهر بعد
}
onNav("inventory")}>ضبط المدة}>
لم تُبَع منذ {stagDays} يومًا أو أكثر — تُضبط المدة من إدارة المخازن.
{stagnant.slice(0, 6).map((x, i, arr) => (
onNav("inventory")} style={{ display: "flex", alignItems: "center", gap: 12, padding: "11px 0", borderBottom: i < arr.length - 1 ? "1px solid var(--border-subtle)" : "none", cursor: "pointer" }}>
{x.m.name}
رصيد {x.m.stock.toLocaleString("en-US")} {x.m.unit} · {x.m.wh}
{x.lastSold
? آخر بيع
{window.BnyDate(x.lastSold)}
: لم تُبَع}
))}
{stagnant.length === 0 &&
لا مواد راكدة ✓
}
{showStockModal && setShowStockModal(false)} onGo={() => { setShowStockModal(false); onNav("inventory"); }} />}
{selCustomer && setSelCustomer(null)} onNav={onNav} />}
);
}
/* بطاقة تنبيه المخزون — لون تحذيري بارز + قابلة للنقر */
function StockAlertCard({ outCount, lowCount, onClick }) {
const I = window.BnyIcon;
const critical = outCount > 0;
// ألوان صريحة (hex) — لصق الشفافية على var() يُبطل الخلفية فيصبح النص غير مقروء
const bg = critical ? "#D93B3B" : "#E08A00";
const bg2 = critical ? "#B82F2F" : "#B86F00";
return (
);
}
/* نافذة قائمة المواد التي تحتاج إعادة طلب (النافدة ثم المنخفضة) */
function StockAlertModal({ out, low, onClose, onGo }) {
const I = window.BnyIcon;
const { Button } = window.BonyanDesignSystem_ad3ca0;
const lowOnly = low.filter((m) => m.stock > 0); // المنخفضة غير النافدة
const Row = (m, tone) => (
{m.name}
{m.sku} · {m.wh}
{m.stock.toLocaleString("en-US")} {m.unit}
{tone === "out" ? "نفدت" : "منخفضة"} · حد {m.low}
);
return (
e.stopPropagation()} className="bny-card" style={{ width: "100%", maxWidth: 540, maxHeight: "85vh", display: "flex", flexDirection: "column", padding: 0 }}>
مواد تحتاج إعادة طلب
{out.length > 0 &&
نفدت ({out.length})
}
{out.map((m) => Row(m, "out"))}
{lowOnly.length > 0 &&
منخفضة ({lowOnly.length})
}
{lowOnly.map((m) => Row(m, "low"))}
{out.length === 0 && lowOnly.length === 0 &&
كل المواد ضمن الحد ✓
}
} onClick={onGo}>فتح إدارة المخزن
);
}
/* كارت أعلى الفواتير تأخّرًا — انقر أي فاتورة لفتحها/تعديلها/حذفها */
function OverdueInvoicesCard({ list, onOpen }) {
const I = window.BnyIcon;
const { Card, Money } = window.BonyanDesignSystem_ad3ca0;
const top = (list || []).slice(0, 5);
return (
انقر أي فاتورة لفتحها وتعديلها أو حذفها.
{top.map((r, i) => (
onOpen(r.no)} title="افتح الفاتورة"
onMouseEnter={(e) => (e.currentTarget.style.background = "var(--surface-hover)")}
onMouseLeave={(e) => (e.currentTarget.style.background = "transparent")}
style={{ display: "flex", alignItems: "center", gap: 10, padding: "11px 8px", margin: "0 -8px", borderRadius: "var(--radius-md)", borderBottom: i < top.length - 1 ? "1px solid var(--border-subtle)" : "none", cursor: "pointer", transition: "background var(--dur-fast,160ms) ease" }}>
#{r.no}
{r.party}
متأخرة {r.age} يومًا
))}
{top.length === 0 &&
لا فواتير متأخّرة ✓
}
);
}
/* نافذة معلومات العميل الكاملة + رابط واتساب مباشر */
function CustomerInfoModal({ customer: c, onClose, onNav }) {
const I = window.BnyIcon;
const D = window.BnyData;
const { Button } = window.BonyanDesignSystem_ad3ca0;
const org = c.org ? (D.organizations || []).find((o) => o.id === c.org) : null;
const shop = (D.shop && D.shop.name) || "بُنيان";
const waMsg = "السلام عليكم " + c.name + "، نذكّركم بالرصيد المستحق لدى " + shop + (c.balance > 0 ? " بمبلغ " + c.balance.toLocaleString("en-US") + " د.ع" : "") + ". شكرًا لتعاملكم.";
const wa = window.BnyWaLink(c.phone, waMsg);
const initials = c.name.split(" ").slice(0, 2).map((w) => w[0]).join("");
const Field = (icon, label, value, href) => value ? (
{icon}
{label}
{href ?
{value}
:
{value}}
) : null;
return (
e.stopPropagation()} className="bny-card" style={{ width: "100%", maxWidth: 460, maxHeight: "88vh", display: "flex", flexDirection: "column", padding: 0 }}>
{initials}
{c.name}
{c.title || (org ? org.name : (c.type === "نقدي" ? "عميل نقدي" : "عميل آجل"))}
{Field(
, "الهاتف", c.phone, c.phone ? "tel:" + c.phone : null)}
{org && Field(
, "الجهة", org.name)}
{org && org.phone && Field(
, "هاتف الجهة", org.phone, "tel:" + org.phone)}
{Field(
, "العنوان", c.address)}
{Field(
, "النوع", c.type)}
الرصيد المستحق (د.ع)
{(c.balance || 0).toLocaleString("en-US")}
{c.balanceUsd ?
بالدولار ($)
{(c.balanceUsd || 0).toLocaleString("en-US")}
: null}
{c.limitEnabled &&
c.limit ? "var(--danger)" : "var(--text-muted)", marginTop: 10 }}>
السقف الائتماني: {c.limit.toLocaleString("en-US")} د.ع {c.balance > c.limit ? "— تجاوز السقف!" : ""}
}
{wa &&
}
} onClick={() => { onClose(); onNav("customers"); }}>كشف الحساب
);
}
/* كارت «مواد تحتاج شراء» — تنبيه قصير (5) + عرض الكل (يفتح المخزن مفلترًا) */
function ReorderCard({ items, onSeeAll }) {
const I = window.BnyIcon;
const { Card, Button } = window.BonyanDesignSystem_ad3ca0;
const all = items || [];
const top = all.slice(0, 5);
return (
} onClick={onSeeAll}>عرض الكل{all.length > 5 ? " (" + all.length + ")" : ""}}>
مواد نفدت أو قاربت على النفاد — راجعها لإعادة الطلب.
{top.map((m, i) => {
const out = Number(m.stock) === 0;
return (
{m.name}
{m.wh} · حد التنبيه {m.low} {m.unit}
{m.stock.toLocaleString("en-US")} {m.unit}
{out ? "نفدت" : "ستنفد قريبًا"}
);
})}
{top.length === 0 &&
كل المواد ضمن الحد ✓
}
);
}
window.DashboardScreen = DashboardScreen;