diff --git a/assets/css/custom.css b/assets/css/custom.css
new file mode 100644
index 0000000..0e5af3d
--- /dev/null
+++ b/assets/css/custom.css
@@ -0,0 +1,140 @@
+:root {
+ --primary: #0f172a;
+ --primary-light: #1e293b;
+ --accent: #3b82f6;
+ --bg: #f8fafc;
+ --surface: #ffffff;
+ --text: #334155;
+ --text-muted: #64748b;
+ --border: #e2e8f0;
+ --radius: 4px;
+ --sidebar-width: 250px;
+}
+
+body {
+ font-family: 'Inter', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
+ background-color: var(--bg);
+ color: var(--text);
+ margin: 0;
+ overflow-x: hidden;
+}
+
+[dir="rtl"] {
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
+}
+
+/* Sidebar */
+.sidebar {
+ width: var(--sidebar-width);
+ height: 100vh;
+ background-color: var(--primary);
+ color: white;
+ position: fixed;
+ top: 0;
+ transition: all 0.3s;
+ z-index: 1000;
+}
+
+[dir="ltr"] .sidebar { left: 0; }
+[dir="rtl"] .sidebar { right: 0; }
+
+.sidebar-header {
+ padding: 1.5rem;
+ font-size: 1.25rem;
+ font-weight: 700;
+ border-bottom: 1px solid var(--primary-light);
+}
+
+.nav-link {
+ color: #cbd5e1;
+ padding: 0.75rem 1.5rem;
+ display: flex;
+ align-items: center;
+ text-decoration: none;
+ transition: background 0.2s;
+}
+
+.nav-link:hover, .nav-link.active {
+ background-color: var(--primary-light);
+ color: white;
+}
+
+.nav-link i {
+ width: 20px;
+ margin-right: 10px;
+}
+
+.nav-section-title {
+ font-size: 0.7rem;
+ font-weight: 600;
+ color: #64748b !important;
+ letter-spacing: 0.05em;
+}
+
+[dir="rtl"] .nav-link i {
+ margin-right: 0;
+ margin-left: 10px;
+}
+
+/* Main Content */
+.main-content {
+ margin-left: var(--sidebar-width);
+ padding: 2rem;
+ transition: all 0.3s;
+}
+
+[dir="rtl"] .main-content {
+ margin-left: 0;
+ margin-right: var(--sidebar-width);
+}
+
+.topbar {
+ background: var(--surface);
+ border-bottom: 1px solid var(--border);
+ padding: 1rem 2rem;
+ margin: -2rem -2rem 2rem -2rem;
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+}
+
+/* UI Components */
+.card {
+ background: var(--surface);
+ border: 1px solid var(--border);
+ border-radius: var(--radius);
+ box-shadow: 0 1px 2px rgba(0,0,0,0.05);
+}
+
+.btn-primary {
+ background-color: var(--accent);
+ border: none;
+ border-radius: var(--radius);
+}
+
+.table {
+ color: var(--text);
+}
+
+.table th {
+ font-weight: 600;
+ color: var(--text-muted);
+ text-transform: uppercase;
+ font-size: 0.75rem;
+ letter-spacing: 0.05em;
+ border-bottom: 2px solid var(--border);
+}
+
+/* RTL Adjustments */
+[dir="rtl"] .ms-auto {
+ margin-right: auto !important;
+ margin-left: 0 !important;
+}
+
+[dir="rtl"] .text-end {
+ text-align: left !important;
+}
+
+[dir="rtl"] .text-start {
+ text-align: right !important;
+}
diff --git a/assets/js/main.js b/assets/js/main.js
new file mode 100644
index 0000000..d809544
--- /dev/null
+++ b/assets/js/main.js
@@ -0,0 +1,30 @@
+document.addEventListener('DOMContentLoaded', function() {
+ const langToggle = document.getElementById('langToggle');
+
+ // Check for saved language
+ const currentLang = localStorage.getItem('lang') || 'en';
+ setLanguage(currentLang);
+
+ if (langToggle) {
+ langToggle.addEventListener('click', function() {
+ const newLang = document.documentElement.lang === 'en' ? 'ar' : 'en';
+ setLanguage(newLang);
+ });
+ }
+});
+
+function setLanguage(lang) {
+ document.documentElement.lang = lang;
+ document.documentElement.dir = lang === 'ar' ? 'rtl' : 'ltr';
+ localStorage.setItem('lang', lang);
+
+ // Update UI text
+ document.querySelectorAll('[data-en]').forEach(el => {
+ el.textContent = el.getAttribute(`data-${lang}`);
+ });
+
+ // Update buttons/inputs
+ document.querySelectorAll('input[data-en-placeholder]').forEach(el => {
+ el.placeholder = el.getAttribute(`data-${lang}-placeholder`);
+ });
+}
diff --git a/db/migrations/20260216_add_stock_tables.sql b/db/migrations/20260216_add_stock_tables.sql
new file mode 100644
index 0000000..95995ef
--- /dev/null
+++ b/db/migrations/20260216_add_stock_tables.sql
@@ -0,0 +1,30 @@
+CREATE TABLE IF NOT EXISTS stock_categories (
+ id INT AUTO_INCREMENT PRIMARY KEY,
+ name_en VARCHAR(255) NOT NULL,
+ name_ar VARCHAR(255) NOT NULL,
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
+);
+
+CREATE TABLE IF NOT EXISTS stock_units (
+ id INT AUTO_INCREMENT PRIMARY KEY,
+ name_en VARCHAR(255) NOT NULL,
+ name_ar VARCHAR(255) NOT NULL,
+ short_name_en VARCHAR(50) NOT NULL,
+ short_name_ar VARCHAR(50) NOT NULL,
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
+);
+
+CREATE TABLE IF NOT EXISTS stock_items (
+ id INT AUTO_INCREMENT PRIMARY KEY,
+ category_id INT,
+ unit_id INT,
+ name_en VARCHAR(255) NOT NULL,
+ name_ar VARCHAR(255) NOT NULL,
+ sku VARCHAR(100) UNIQUE,
+ purchase_price DECIMAL(15, 2) DEFAULT 0.00,
+ sale_price DECIMAL(15, 2) DEFAULT 0.00,
+ stock_quantity DECIMAL(15, 2) DEFAULT 0.00,
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ FOREIGN KEY (category_id) REFERENCES stock_categories(id) ON DELETE SET NULL,
+ FOREIGN KEY (unit_id) REFERENCES stock_units(id) ON DELETE SET NULL
+);
diff --git a/index.php b/index.php
index 7205f3d..49d8844 100644
--- a/index.php
+++ b/index.php
@@ -1,150 +1,681 @@
prepare("INSERT INTO customers (name, email, phone, balance) VALUES (?, ?, ?, ?)");
+ $stmt->execute([$name, $email, $phone, $balance]);
+ $message = "Customer added successfully!";
+ }
+ }
+
+ if (isset($_POST['add_category'])) {
+ $name_en = $_POST['name_en'] ?? '';
+ $name_ar = $_POST['name_ar'] ?? '';
+ if ($name_en && $name_ar) {
+ $stmt = db()->prepare("INSERT INTO stock_categories (name_en, name_ar) VALUES (?, ?)");
+ $stmt->execute([$name_en, $name_ar]);
+ $message = "Category added successfully!";
+ }
+ }
+
+ if (isset($_POST['add_unit'])) {
+ $name_en = $_POST['name_en'] ?? '';
+ $name_ar = $_POST['name_ar'] ?? '';
+ $short_en = $_POST['short_en'] ?? '';
+ $short_ar = $_POST['short_ar'] ?? '';
+ if ($name_en && $name_ar) {
+ $stmt = db()->prepare("INSERT INTO stock_units (name_en, name_ar, short_name_en, short_name_ar) VALUES (?, ?, ?, ?)");
+ $stmt->execute([$name_en, $name_ar, $short_en, $short_ar]);
+ $message = "Unit added successfully!";
+ }
+ }
+
+ if (isset($_POST['add_item'])) {
+ $cat_id = $_POST['category_id'] ?: null;
+ $unit_id = $_POST['unit_id'] ?: null;
+ $supplier_id = $_POST['supplier_id'] ?: null;
+ $name_en = $_POST['name_en'] ?? '';
+ $name_ar = $_POST['name_ar'] ?? '';
+ $sku = $_POST['sku'] ?? '';
+ $p_price = (float)($_POST['purchase_price'] ?? 0);
+ $s_price = (float)($_POST['sale_price'] ?? 0);
+ $qty = (float)($_POST['stock_quantity'] ?? 0);
+ $min_stock = (float)($_POST['min_stock_level'] ?? 0);
+ $expiry = $_POST['expiry_date'] ?: null;
+
+ $image_path = null;
+ if (isset($_FILES['image']) && $_FILES['image']['error'] === UPLOAD_ERR_OK) {
+ $ext = pathinfo($_FILES['image']['name'], PATHINFO_EXTENSION);
+ $filename = uniqid('item_') . '.' . $ext;
+ $target = 'uploads/items/' . $filename;
+ if (move_uploaded_file($_FILES['image']['tmp_name'], $target)) {
+ $image_path = $target;
+ }
+ }
+
+ if ($name_en && $name_ar) {
+ $stmt = db()->prepare("INSERT INTO stock_items (category_id, unit_id, supplier_id, name_en, name_ar, sku, purchase_price, sale_price, stock_quantity, min_stock_level, expiry_date, image_path) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
+ $stmt->execute([$cat_id, $unit_id, $supplier_id, $name_en, $name_ar, $sku, $p_price, $s_price, $qty, $min_stock, $expiry, $image_path]);
+ $message = "Item added successfully!";
+ }
+ }
+
+ if (isset($_POST['delete_item'])) {
+ $id = (int)$_POST['id'];
+ // Optional: delete image file
+ $item = db()->prepare("SELECT image_path FROM stock_items WHERE id = ?");
+ $item->execute([$id]);
+ $path = $item->fetchColumn();
+ if ($path && file_exists($path)) {
+ unlink($path);
+ }
+
+ $stmt = db()->prepare("DELETE FROM stock_items WHERE id = ?");
+ $stmt->execute([$id]);
+ $message = "Item deleted successfully!";
+ }
+}
+
+// Routing & Data Fetching
+$page = $_GET['page'] ?? 'dashboard';
+$data = [];
+
+switch ($page) {
+ case 'customers':
+ $data['customers'] = db()->query("SELECT * FROM customers ORDER BY id DESC")->fetchAll();
+ break;
+ case 'categories':
+ $data['categories'] = db()->query("SELECT * FROM stock_categories ORDER BY id DESC")->fetchAll();
+ break;
+ case 'units':
+ $data['units'] = db()->query("SELECT * FROM stock_units ORDER BY id DESC")->fetchAll();
+ break;
+ case 'items':
+ $data['items'] = db()->query("SELECT i.*, c.name_en as cat_en, c.name_ar as cat_ar, u.short_name_en as unit_en, u.short_name_ar as unit_ar, s.name as supplier_name
+ FROM stock_items i
+ LEFT JOIN stock_categories c ON i.category_id = c.id
+ LEFT JOIN stock_units u ON i.unit_id = u.id
+ LEFT JOIN customers s ON i.supplier_id = s.id
+ ORDER BY i.id DESC")->fetchAll();
+ $data['categories'] = db()->query("SELECT * FROM stock_categories ORDER BY name_en ASC")->fetchAll();
+ $data['units'] = db()->query("SELECT * FROM stock_units ORDER BY name_en ASC")->fetchAll();
+ $data['suppliers'] = db()->query("SELECT * FROM customers ORDER BY name ASC")->fetchAll();
+ break;
+ default:
+ $data['customers'] = db()->query("SELECT * FROM customers ORDER BY id DESC LIMIT 5")->fetchAll();
+ $data['categories'] = db()->query("SELECT * FROM stock_categories ORDER BY name_en ASC")->fetchAll();
+ $data['units'] = db()->query("SELECT * FROM stock_units ORDER BY name_en ASC")->fetchAll();
+ $data['suppliers'] = db()->query("SELECT * FROM customers ORDER BY name ASC")->fetchAll();
+ // Dashboard stats
+ $data['stats'] = [
+ 'total_customers' => db()->query("SELECT COUNT(*) FROM customers")->fetchColumn(),
+ 'total_items' => db()->query("SELECT COUNT(*) FROM stock_items")->fetchColumn(),
+ ];
+ break;
+}
+
+$projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? 'Accounting System';
?>
-
+
-
-
- New Style
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+ Accounting Admin
+
+
+
+
+
+
-
-
-
Analyzing your requirements and generating your website…
-
- Loading…
-
-
= ($_SERVER['HTTP_HOST'] ?? '') === 'appwizzy.com' ? 'AppWizzy' : 'Flatlogic' ?> AI is collecting your requirements and applying the first changes.
-
This page will update automatically as the plan is implemented.
-
Runtime: PHP = htmlspecialchars($phpVersion) ?> — UTC = htmlspecialchars($now) ?>
+
+
+
+
+
+
+ ['en' => 'Dashboard', 'ar' => 'لوحة القيادة'],
+ 'customers' => ['en' => 'Customers', 'ar' => 'العملاء'],
+ 'categories' => ['en' => 'Stock Categories', 'ar' => 'فئات المخزون'],
+ 'units' => ['en' => 'Stock Units', 'ar' => 'وحدات المخزون'],
+ 'items' => ['en' => 'Stock Items', 'ar' => 'أصناف المخزون'],
+ ];
+ $currTitle = $titles[$page] ?? $titles['dashboard'];
+ ?>
+ = $currTitle['en'] ?>
+
+
+
+
+
+
+
+
+
+
+
+
+ = $message ?>
+
+
+
+
+
+
+
+
+
Total Sales
+
$24,500
+
+
+
+
+
Total Customers
+
= $data['stats']['total_customers'] ?>
+
+
+
+
+
Total Items
+
= $data['stats']['total_items'] ?>
+
+
+
-
-
+
+
+
+
+
+
Recent Customers
+
+ View All
+
+
+
+
+
+
+ | Name |
+ Phone |
+ Balance |
+
+
+
+
+
+ | = htmlspecialchars($c['name']) ?> |
+ = htmlspecialchars($c['phone']) ?> |
+ $= number_format((float)$c['balance'], 2) ?> |
+
+
+
+
+
+
+
+
+
+
Quick Links
+
+
+
+
+
+
+
+
+
+
+
+
Customer Management
+
+
+
+
+
+
+ | Name |
+ Email |
+ Phone |
+ Balance |
+
+
+
+
+
+ | = htmlspecialchars($c['name']) ?> |
+ = htmlspecialchars($c['email']) ?> |
+ = htmlspecialchars($c['phone']) ?> |
+ $= number_format((float)$c['balance'], 2) ?> |
+
+
+
+
+
+
+
+
+
+
+
Stock Categories
+
+
+
+
+
+
+ | ID |
+ Name (EN) |
+ Name (AR) |
+
+
+
+
+
+ | = $cat['id'] ?> |
+ = htmlspecialchars($cat['name_en']) ?> |
+ = htmlspecialchars($cat['name_ar']) ?> |
+
+
+
+
+
+
+
+
+
+
+
Stock Units
+
+
+
+
+
+
+ | Name (EN) |
+ Short (EN) |
+ Name (AR) |
+ Short (AR) |
+
+
+
+
+
+ | = htmlspecialchars($u['name_en']) ?> |
+ = htmlspecialchars($u['short_name_en']) ?> |
+ = htmlspecialchars($u['name_ar']) ?> |
+ = htmlspecialchars($u['short_name_ar']) ?> |
+
+
+
+
+
+
+
+
+
+
+
Stock Items
+
+
+
+
+
+
+ | Image |
+ SKU |
+ Name |
+ Category |
+ Supplier |
+ Stock Level |
+ Expiry |
+ Actions |
+
+
+
+
+
+
+
+
+
+
+
+
+
+ |
+ = htmlspecialchars($item['sku']) ?> |
+
+ = htmlspecialchars($item['name_en']) ?>
+ = htmlspecialchars($item['name_ar']) ?>
+ |
+ = htmlspecialchars($item['cat_en']) ?> |
+ = htmlspecialchars($item['supplier_name'] ?? '---') ?> |
+
+
+ = number_format((float)$item['stock_quantity'], 2) ?>
+ Min: = number_format((float)$item['min_stock_level'], 2) ?>
+
+ Low Stock
+
+
+ |
+ = $item['expiry_date'] ?: '---' ?> |
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+  ?>)
+
+
+
+
+
+ SKU = htmlspecialchars($item['sku'] ?: '---') ?>
+ Category = htmlspecialchars($item['cat_en'] ?: '---') ?>
+ Supplier = htmlspecialchars($item['supplier_name'] ?? '---') ?>
+ Unit = htmlspecialchars($item['unit_en'] ?: '---') ?>
+ Purchase Price $= number_format((float)$item['purchase_price'], 2) ?>
+ Sale Price $= number_format((float)$item['sale_price'], 2) ?>
+ Stock = number_format((float)$item['stock_quantity'], 2) ?>
+ Min Stock = number_format((float)$item['min_stock_level'], 2) ?>
+ Expiry Date = $item['expiry_date'] ?: '---' ?>
+
+
+
+
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+