diff --git a/assets/css/custom.css b/assets/css/custom.css index ea58ee6..1b2dfec 100644 --- a/assets/css/custom.css +++ b/assets/css/custom.css @@ -79,8 +79,10 @@ body { z-index: 1000; } -[dir="ltr"] .sidebar { left: 0; } -[dir="rtl"] .sidebar { right: 0; } +@media (min-width: 1200px) { + [dir="ltr"] .sidebar { left: 0; } + [dir="rtl"] .sidebar { right: 0; } +} .sidebar-header { padding: 1rem; @@ -255,12 +257,13 @@ body { margin-right: var(--sidebar-width); } -@media (max-width: 991.98px) { - .sidebar { +@media (max-width: 1199.98px) { + [dir="ltr"] .sidebar { left: calc(-1 * var(--sidebar-width)); } - .sidebar.show { + [dir="ltr"] .sidebar.show { left: 0; + box-shadow: 0 0 50px rgba(0,0,0,0.3); } [dir="rtl"] .sidebar { right: calc(-1 * var(--sidebar-width)); @@ -268,11 +271,12 @@ body { } [dir="rtl"] .sidebar.show { right: 0; + box-shadow: 0 0 50px rgba(0,0,0,0.3); } .main-content { margin-left: 0 !important; margin-right: 0 !important; - width: 100%; + width: 100% !important; } } diff --git a/assets/js/main.js b/assets/js/main.js index 4773bb5..71b4bf4 100644 --- a/assets/js/main.js +++ b/assets/js/main.js @@ -31,6 +31,17 @@ document.addEventListener('DOMContentLoaded', function() { overlay.classList.remove('show'); }); + // Close sidebar when a link is clicked on mobile + const sidebarLinks = document.querySelectorAll('.sidebar .nav-link'); + sidebarLinks.forEach(link => { + link.addEventListener('click', function() { + if (window.innerWidth <= 1199.98) { + sidebar.classList.remove('show'); + overlay.classList.remove('show'); + } + }); + }); + // Sidebar Accordion logic: Close other collapses when one is opened const sidebarCollapses = document.querySelectorAll('.sidebar .collapse'); sidebarCollapses.forEach(collapseEl => { diff --git a/get_schema.php b/get_schema.php new file mode 100644 index 0000000..c9246d7 --- /dev/null +++ b/get_schema.php @@ -0,0 +1,10 @@ +query("SHOW TABLES")->fetchAll(PDO::FETCH_COLUMN); +foreach ($tables as $t) { + echo "TABLE: $t\n"; + $cols = db()->query("SHOW COLUMNS FROM $t")->fetchAll(PDO::FETCH_ASSOC); + foreach ($cols as $c) { + echo " - {$c['Field']} ({$c['Type']})\n"; + } +} diff --git a/includes/lang.php b/includes/lang.php index 9c93b55..b75b43d 100644 --- a/includes/lang.php +++ b/includes/lang.php @@ -34,6 +34,7 @@ $translations = [ 'cashflow_report' => 'Cashflow Report', 'expiry_report' => 'Expiry Report', 'low_stock_report' => 'Low Stock Report', + 'expense_report' => 'Expense Report', 'loyalty_history' => 'Loyalty History', 'management' => 'Management', 'payment_methods' => 'Payment Methods', @@ -128,6 +129,7 @@ $translations = [ 'cashflow_report' => 'تقرير التدفق النقدي', 'expiry_report' => 'تقرير الصلاحية', 'low_stock_report' => 'تقرير انخفاض المخزون', + 'expense_report' => 'تقرير المصاريف', 'loyalty_history' => 'سجل الولاء', 'management' => 'الإدارة', 'payment_methods' => 'طرق الدفع', diff --git a/index.php b/index.php index 8ba2269..be38c72 100644 --- a/index.php +++ b/index.php @@ -1871,15 +1871,28 @@ if (isset($_POST['add_hr_department'])) { } if (isset($_POST['save_backup_settings'])) { - if (can('users_view')) { $limit = (int)($_POST['backup_limit'] ?? 5); $auto = $_POST['backup_auto_enabled'] ?? '0'; $time = $_POST['backup_time'] ?? '00:00'; - $db = db(); $stmt = $db->prepare("INSERT INTO settings (`key`, `value`) VALUES ('backup_limit', ?), ('backup_auto_enabled', ?), ('backup_time', ?) ON DUPLICATE KEY UPDATE `value` = VALUES(`value`)"); $stmt->execute([$limit, $auto, $time]); - $message = "Backup settings saved successfully!"; + $message = "Backup settings updated."; + } + + if (isset($_GET['download_backup'])) { + $filename = basename($_GET['download_backup']); + $filepath = __DIR__ . '/backups/' . $filename; + if (file_exists($filepath)) { + header('Content-Description: File Transfer'); + header('Content-Type: application/octet-stream'); + header('Content-Disposition: attachment; filename="' . $filename . '"'); + header('Expires: 0'); + header('Cache-Control: must-revalidate'); + header('Pragma: public'); + header('Content-Length: ' . filesize($filepath)); + readfile($filepath); + exit; } } @@ -1983,6 +1996,7 @@ $page_permissions = [ 'accounting' => 'accounting_view', 'expense_categories' => 'expense_categories_view', 'expenses' => 'expenses_view', + 'expense_report' => 'expenses_view', 'items' => 'items_view', 'categories' => 'categories_view', 'units' => 'units_view', @@ -2085,6 +2099,7 @@ $permission_groups = [ 'Reports' => [ 'customer_statement' => 'Customer Statement', 'supplier_statement' => 'Supplier Statement', + 'expense_report' => 'Expense Report', 'cashflow_report' => 'Cashflow Report', 'expiry_report' => 'Expiry Report', 'low_stock_report' => 'Low Stock Report', @@ -2107,19 +2122,31 @@ $permission_groups = [ if ($page === 'export') { $type = $_GET['type'] ?? 'sales'; - $filename = $type . "_export_" . date('Y-m-d') . ".csv"; + $format = $_GET['format'] ?? 'csv'; + $filename = $type . "_export_" . date('Y-m-d') . ($format === 'excel' ? ".xls" : ".csv"); - header('Content-Type: text/csv; charset=utf-8'); - header('Content-Disposition: attachment; filename=' . $filename); - $output = fopen('php://output', 'w'); - - // Add UTF-8 BOM for Excel - fprintf($output, chr(0xEF).chr(0xBB).chr(0xBF)); + if ($format === 'excel') { + header('Content-Type: application/vnd.ms-excel; charset=utf-8'); + header('Content-Disposition: attachment; filename=' . $filename); + echo "
| " . htmlspecialchars($h) . " | "; + echo "
|---|
| " . htmlspecialchars((string)$val) . " | "; + echo "