/* Bonyan ERP — app shell + router (with multi-role permissions) */ const TITLES = { dashboard: "لوحة التحكم", pos: "فاتورة بيع جديدة", invoices: "الفواتير", inventory: "المواد", warehouses: "إدارة المخازن", customers: "العملاء وكشوف الحسابات", suppliers: "الموردون", accounts: "الحسابات وكشوف الديون", pricing: "قوائم التسعير", reports: "التقارير والتحليلات", settings: "إعدادات الفاتورة وهوية المحل", users: "صلاحيات المستخدمين", activity: "الحركات", }; const NAV_PERM = { dashboard: "dashboard", pos: "pos", invoices: "invoices_view", customers: "customers", suppliers: "suppliers", accounts: "customers", inventory: "inventory_view", warehouses: "inventory_edit", pricing: "pricing", reports: "reports", settings: "invoice_settings", users: "users_manage", activity: "activity", }; const SCREEN_ORDER = ["dashboard", "pos", "invoices", "customers", "suppliers", "accounts", "inventory", "warehouses", "pricing", "reports", "settings", "users", "activity"]; function App() { const isMobile = window.useBnyMobile(); const D = window.BnyData; // في الوضع المباشر قد تُستعاد جلسة محفوظة قبل الرسم (window.__bnyRestored) const [currentUser, setCurrentUser] = React.useState(() => window.__bnyRestored || null); const [screen, setScreen] = React.useState("dashboard"); const [query, setQuery] = React.useState(""); const [voice, setVoice] = React.useState(false); const [drawer, setDrawer] = React.useState(false); // درج القائمة الجانبي (الهاتف) const [theme, setTheme] = React.useState(() => { try { return localStorage.getItem("bny-theme") === "dark" ? "dark" : "light"; } catch (e) { return "light"; } }); // عرّف المستخدم الحالي عالميًا — يعتمده مسجّل الحركات (BnyOps.log) React.useEffect(() => { window.BnyCurrentUser = currentUser; }, [currentUser]); // طبّق السمة على عنصر واحفظها — يتبعها كامل التطبيق عبر متغيرات CSS React.useEffect(() => { document.documentElement.setAttribute("data-theme", theme); try { localStorage.setItem("bny-theme", theme); } catch (e) {} }, [theme]); const toggleTheme = () => setTheme((t) => (t === "dark" ? "light" : "dark")); const can = (id) => window.BnyCan(currentUser, NAV_PERM[id]); // tick يجبر إعادة تركيب الشاشة مع كل تنقّل — لتستهلك أوامر الصوت المعلّقة (BnyPending*) const [navTick, setNavTick] = React.useState(0); const nav = (id) => { setScreen(id); setQuery(""); setNavTick((t) => t + 1); }; const logout = () => { if (window.BnyConfigured && window.BnyAuth) { try { window.BnyAuth.logout(); } catch (e) {} } setCurrentUser(null); setScreen("dashboard"); }; // عند تسجيل الدخول/تبديل المستخدم: انتقل لأول شاشة مسموحة إن كانت الحالية ممنوعة React.useEffect(() => { if (currentUser && !can(screen)) { const first = SCREEN_ORDER.find(can); if (first) setScreen(first); } }, [currentUser]); // eslint-disable-line // بوابة الدخول: لا واجهة قبل تسجيل الدخول if (!currentUser) { return ; } const Screen = { dashboard: window.DashboardScreen, pos: window.PosScreen, invoices: window.InvoicesScreen, inventory: window.InventoryScreen, warehouses: window.WarehousesScreen, customers: window.CustomersScreen, suppliers: window.SuppliersScreen, accounts: window.AccountsScreen, pricing: window.PricingScreen, reports: window.ReportsScreen, settings: window.SettingsScreen, users: window.UsersScreen, activity: window.ActivityScreen, }[screen]; const content = can(screen) && Screen ? : ; const topbar = ( setVoice(true)} showSearch={screen !== "pos" && screen !== "invoices"} compact={isMobile} user={currentUser} onLogout={logout} theme={theme} onToggleTheme={toggleTheme} onMenu={() => setDrawer(true)} /> ); const overlay = ( <> setVoice(false)} onAction={(id) => { setVoice(false); nav(id); }} /> > ); if (isMobile) { return ( {topbar} {content} setDrawer(false)} /> {overlay} ); } return ( {topbar} {content} {overlay} ); } function NoAccess() { const I = window.BnyIcon; return ( لا تملك صلاحية الوصول هذه الشاشة غير متاحة لحسابك الحالي. ); } window.BnyApp = App;
هذه الشاشة غير متاحة لحسابك الحالي.