diff --git a/assets/css/custom.css b/assets/css/custom.css
index 1b2dfec..6a5a1b6 100644
--- a/assets/css/custom.css
+++ b/assets/css/custom.css
@@ -22,6 +22,39 @@ body.theme-dark {
--border: #334155;
}
+body.theme-nord {
+ --primary: #2e3440;
+ --primary-light: #3b4252;
+ --accent: #88c0d0;
+ --bg: #eceff4;
+ --surface: #ffffff;
+ --text: #2e3440;
+ --text-muted: #4c566a;
+ --border: #d8dee9;
+}
+
+body.theme-dracula {
+ --primary: #282a36;
+ --primary-light: #44475a;
+ --accent: #bd93f9;
+ --bg: #1e1f29;
+ --surface: #282a36;
+ --text: #f8f8f2;
+ --text-muted: #6272a4;
+ --border: #44475a;
+}
+
+body.theme-citrus {
+ --primary: #365314;
+ --primary-light: #4d7c0f;
+ --accent: #84cc16;
+ --bg: #f7fee7;
+ --surface: #ffffff;
+ --text: #1a2e05;
+ --text-muted: #65a30d;
+ --border: #ecfccb;
+}
+
body.theme-ocean {
--primary: #083344;
--primary-light: #164e63;
@@ -79,9 +112,13 @@ body {
z-index: 1000;
}
+/* Base positioning for desktop */
@media (min-width: 1200px) {
- [dir="ltr"] .sidebar { left: 0; }
- [dir="rtl"] .sidebar { right: 0; }
+ [dir="ltr"] .sidebar { left: 0 !important; right: auto !important; }
+ [dir="rtl"] .sidebar { right: 0 !important; left: auto !important; }
+
+ [dir="ltr"] .main-content { margin-left: var(--sidebar-width) !important; margin-right: 0 !important; }
+ [dir="rtl"] .main-content { margin-left: 0 !important; margin-right: var(--sidebar-width) !important; }
}
.sidebar-header {
@@ -157,7 +194,7 @@ body {
}
.pos-cart {
width: 400px;
- background: #fff;
+ background: var(--surface);
border-radius: 12px;
box-shadow: 0 4px 15px rgba(0,0,0,0.05);
display: flex;
@@ -170,9 +207,9 @@ body {
gap: 15px;
}
.product-card {
- background: #fff;
+ background: var(--surface);
border-radius: 10px;
- border: 1px solid #edf2f7;
+ border: 1px solid var(--border);
padding: 15px;
text-align: center;
cursor: pointer;
@@ -184,7 +221,7 @@ body {
.product-card:hover {
transform: translateY(-5px);
box-shadow: 0 10px 20px rgba(0,0,0,0.05);
- border-color: #3b82f6;
+ border-color: var(--accent);
}
.product-card img {
width: 100%;
@@ -195,7 +232,7 @@ body {
}
.product-card .price {
font-weight: 700;
- color: #2d3748;
+ color: var(--text);
}
.cart-items {
flex: 1;
@@ -208,11 +245,11 @@ body {
align-items: center;
margin-bottom: 15px;
padding-bottom: 15px;
- border-bottom: 1px solid #edf2f7;
+ border-bottom: 1px solid var(--border);
}
.cart-total {
padding: 20px;
- background: #f8fafc;
+ background: var(--bg);
border-bottom-left-radius: 12px;
border-bottom-right-radius: 12px;
}
@@ -225,16 +262,17 @@ body {
width: 24px;
height: 24px;
border-radius: 50%;
- border: 1px solid #e2e8f0;
- background: #fff;
+ border: 1px solid var(--border);
+ background: var(--surface);
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
font-size: 14px;
+ color: var(--text);
}
.qty-btn:hover {
- background: #edf2f7;
+ background: var(--bg);
}
[dir="rtl"] .nav-link i {
@@ -244,7 +282,6 @@ body {
/* Main Content */
.main-content {
- margin-left: var(--sidebar-width);
padding: 2rem;
transition: all 0.3s;
min-height: 100vh;
@@ -252,26 +289,24 @@ body {
flex-direction: column;
}
-[dir="rtl"] .main-content {
- margin-left: 0;
- margin-right: var(--sidebar-width);
-}
+/* Desktop margins handled in the top @media block */
@media (max-width: 1199.98px) {
[dir="ltr"] .sidebar {
- left: calc(-1 * var(--sidebar-width));
+ left: calc(-1 * var(--sidebar-width)) !important;
}
[dir="ltr"] .sidebar.show {
- left: 0;
- box-shadow: 0 0 50px rgba(0,0,0,0.3);
+ left: 0 !important;
+ box-shadow: 0 0 50px rgba(0,0,0,0.3) !important;
}
[dir="rtl"] .sidebar {
- right: calc(-1 * var(--sidebar-width));
- left: auto;
+ right: calc(-1 * var(--sidebar-width)) !important;
+ left: auto !important;
}
[dir="rtl"] .sidebar.show {
- right: 0;
- box-shadow: 0 0 50px rgba(0,0,0,0.3);
+ right: 0 !important;
+ left: auto !important;
+ box-shadow: 0 0 50px rgba(0,0,0,0.3) !important;
}
.main-content {
margin-left: 0 !important;
@@ -348,6 +383,75 @@ body {
}
/* RTL Adjustments */
+/* Theme Compatibility Overrides */
+body.theme-dark, body.theme-dracula {
+ color-scheme: dark;
+}
+
+body:not(.theme-default) .text-dark {
+ color: var(--text) !important;
+}
+
+body:not(.theme-default) .bg-light,
+body:not(.theme-default) .bg-white {
+ background-color: var(--surface) !important;
+}
+
+body:not(.theme-default) .border-dark {
+ border-color: var(--border) !important;
+}
+
+body:not(.theme-default) .list-group-item {
+ background-color: var(--surface);
+ color: var(--text);
+ border-color: var(--border);
+}
+
+body:not(.theme-default) .list-group-item-action:hover {
+ background-color: var(--bg);
+ color: var(--accent);
+}
+
+body:not(.theme-default) .dropdown-menu {
+ background-color: var(--surface);
+ border-color: var(--border);
+}
+
+body:not(.theme-default) .dropdown-item {
+ color: var(--text);
+}
+
+body:not(.theme-default) .dropdown-item:hover {
+ background-color: var(--primary-light);
+ color: #fff;
+}
+
+body:not(.theme-default) .alert-warning {
+ background-color: var(--primary-light);
+ border-color: var(--accent);
+ color: var(--text);
+}
+
+body:not(.theme-default) .modal-content {
+ background-color: var(--surface);
+ color: var(--text);
+ border-color: var(--border);
+}
+
+body:not(.theme-default) .form-control,
+body:not(.theme-default) .form-select {
+ background-color: var(--bg);
+ border-color: var(--border);
+ color: var(--text);
+}
+
+body:not(.theme-default) .form-control:focus,
+body:not(.theme-default) .form-select:focus {
+ background-color: var(--bg);
+ border-color: var(--accent);
+ color: var(--text);
+}
+
[dir="rtl"] .ms-auto {
margin-right: auto !important;
margin-left: 0 !important;
diff --git a/assets/js/main.js b/assets/js/main.js
index 71b4bf4..2975cea 100644
--- a/assets/js/main.js
+++ b/assets/js/main.js
@@ -1,9 +1,9 @@
document.addEventListener('DOMContentLoaded', function() {
const langToggle = document.getElementById('langToggle');
- // Check for saved language
- const currentLang = localStorage.getItem('lang') || 'en';
- setLanguage(currentLang);
+ // Use the language set by PHP as initial source of truth
+ const initialLang = document.documentElement.lang || 'ar';
+ localStorage.setItem('lang', initialLang);
if (langToggle) {
langToggle.addEventListener('click', function() {
@@ -63,7 +63,7 @@ document.addEventListener('DOMContentLoaded', function() {
const theme = this.getAttribute('data-theme');
// Remove all existing theme classes
- const themes = ['theme-default', 'theme-dark', 'theme-ocean', 'theme-forest', 'theme-sunset'];
+ const themes = ['theme-default', 'theme-dark', 'theme-ocean', 'theme-forest', 'theme-sunset', 'theme-nord', 'theme-dracula', 'theme-citrus'];
document.body.classList.remove(...themes);
// Add new theme class
@@ -92,6 +92,7 @@ document.addEventListener('DOMContentLoaded', function() {
function setLanguage(lang) {
document.documentElement.lang = lang;
document.documentElement.dir = lang === 'ar' ? 'rtl' : 'ltr';
+ document.body.dir = lang === 'ar' ? 'rtl' : 'ltr';
localStorage.setItem('lang', lang);
// Update UI text
diff --git a/db/migrations/20260221_add_theme_to_users.sql b/db/migrations/20260221_add_theme_to_users.sql
new file mode 100644
index 0000000..3547ce1
--- /dev/null
+++ b/db/migrations/20260221_add_theme_to_users.sql
@@ -0,0 +1,2 @@
+-- Add theme column to users table
+ALTER TABLE users ADD COLUMN theme VARCHAR(20) DEFAULT 'default' AFTER profile_pic;
\ No newline at end of file
diff --git a/index.php b/index.php
index 92ac092..fa958fa 100644
--- a/index.php
+++ b/index.php
@@ -463,7 +463,7 @@ if (isset($_GET['action']) || isset($_POST['action'])) {
exit;
}
$theme = $_POST['theme'] ?? 'default';
- $allowed = ['default', 'dark', 'ocean', 'forest', 'sunset'];
+ $allowed = ['default', 'dark', 'ocean', 'forest', 'sunset', 'nord', 'dracula', 'citrus'];
if (!in_array($theme, $allowed)) $theme = 'default';
$stmt = db()->prepare("UPDATE users SET theme = ? WHERE id = ?");
@@ -2095,67 +2095,67 @@ $data = [
];
$permission_groups = [
- 'General' => ['dashboard' => 'Dashboard'],
+ 'General' => ['dashboard' => __('dashboard')],
'Inventory' => [
- 'items' => 'Items',
- 'categories' => 'Categories',
- 'units' => 'Units'
+ 'items' => __('items'),
+ 'categories' => __('categories'),
+ 'units' => __('units')
],
'Customers' => [
- 'customers' => 'Customers'
+ 'customers' => __('customers')
],
'Suppliers' => [
- 'suppliers' => 'Suppliers'
+ 'suppliers' => __('suppliers')
],
'POS' => [
- 'pos' => 'POS'
+ 'pos' => __('pos')
],
'Sales' => [
- 'sales' => 'Sales',
- 'sales_returns' => 'Sales Returns',
- 'quotations' => 'Quotations'
+ 'sales' => __('sales'),
+ 'sales_returns' => __('sales_returns'),
+ 'quotations' => __('quotations')
],
'Purchases' => [
- 'purchases' => 'Purchases',
- 'lpos' => 'LPOs',
- 'purchase_returns' => 'Purchase Returns'
+ 'purchases' => __('purchases'),
+ 'lpos' => __('lpos'),
+ 'purchase_returns' => __('purchase_returns')
],
'Expenses' => [
- 'expense_categories' => 'Expense Categories',
- 'expenses' => 'Expenses'
+ 'expense_categories' => __('expense_categories'),
+ 'expenses' => __('expenses')
],
'Accounting' => [
- 'accounting' => 'Journal Entries',
- 'trial_balance' => 'Trial Balance',
- 'profit_loss' => 'Profit & Loss',
- 'balance_sheet' => 'Balance Sheet',
- 'vat_report' => 'VAT Report'
+ 'accounting' => __('accounting'),
+ 'trial_balance' => __('trial_balance'),
+ 'profit_loss' => __('profit_loss'),
+ 'balance_sheet' => __('balance_sheet'),
+ 'vat_report' => __('vat_report')
],
'HR' => [
- 'hr_departments' => 'Departments',
- 'hr_employees' => 'Employees',
- 'hr_attendance' => 'Attendance',
- 'hr_payroll' => 'Payroll'
+ 'hr_departments' => __('departments'),
+ 'hr_employees' => __('employees'),
+ 'hr_attendance' => __('attendance'),
+ 'hr_payroll' => __('payroll')
],
'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',
- 'loyalty_history' => 'Loyalty History'
+ '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'),
+ 'loyalty_history' => __('loyalty_history')
],
'Settings' => [
- 'payment_methods' => 'Payment Methods',
- 'devices' => 'Biometric Devices',
- 'settings' => 'Company Settings'
+ 'payment_methods' => __('payment_methods'),
+ 'devices' => __('devices'),
+ 'settings' => __('settings')
],
'Administration' => [
- 'role_groups' => 'Role Groups',
- 'users' => 'Users',
- 'cash_registers' => 'Cash Registers',
- 'register_sessions' => 'Register Sessions',
+ 'role_groups' => __('role_groups'),
+ 'users' => __('users'),
+ 'cash_registers' => __('cash_registers'),
+ 'register_sessions' => __('register_sessions'),
'logs' => 'System Logs'
]
];
@@ -2992,6 +2992,30 @@ $projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? 'Accounting System';