feat: setup items, categories, and suppliers database tables with real demo data and images from pexels
This commit is contained in:
parent
ccaa56bcff
commit
033b73cd60
@ -136,4 +136,19 @@ body.auth-body {
|
|||||||
/* Sidebar Sub-menu */
|
/* Sidebar Sub-menu */
|
||||||
[data-bs-toggle="collapse"][aria-expanded="true"] .toggle-icon {
|
[data-bs-toggle="collapse"][aria-expanded="true"] .toggle-icon {
|
||||||
transform: rotate(180deg);
|
transform: rotate(180deg);
|
||||||
}
|
}
|
||||||
|
/* Fix for btn-close in modal-header */
|
||||||
|
.modal-header {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
.modal-header .btn-close {
|
||||||
|
margin: 0;
|
||||||
|
position: absolute;
|
||||||
|
top: 1rem;
|
||||||
|
}
|
||||||
|
[dir="rtl"] .modal-header .btn-close {
|
||||||
|
left: 1rem;
|
||||||
|
}
|
||||||
|
[dir="ltr"] .modal-header .btn-close {
|
||||||
|
right: 1rem;
|
||||||
|
}
|
||||||
|
|||||||
BIN
assets/images/items/18311171.jpg
Normal file
BIN
assets/images/items/18311171.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 9.4 KiB |
BIN
assets/images/items/18641665.jpg
Normal file
BIN
assets/images/items/18641665.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.9 KiB |
BIN
assets/images/items/2537658.jpg
Normal file
BIN
assets/images/items/2537658.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 14 KiB |
BIN
assets/images/items/29765806.jpg
Normal file
BIN
assets/images/items/29765806.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 9.3 KiB |
BIN
assets/images/items/9058886.jpg
Normal file
BIN
assets/images/items/9058886.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 7.4 KiB |
5
cookies.txt
Normal file
5
cookies.txt
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
# Netscape HTTP Cookie File
|
||||||
|
# https://curl.se/docs/http-cookies.html
|
||||||
|
# This file was generated by libcurl! Edit at your own risk.
|
||||||
|
|
||||||
|
127.0.0.1 FALSE / FALSE 0 PHPSESSID b0jh7ohno1tnaa8tkh48odf595
|
||||||
36
db_seed.php
Normal file
36
db_seed.php
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
<?php
|
||||||
|
require 'db/config.php';
|
||||||
|
$db = db();
|
||||||
|
|
||||||
|
// 1. Create items table
|
||||||
|
$db->exec("
|
||||||
|
CREATE TABLE IF NOT EXISTS items (
|
||||||
|
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
sku VARCHAR(50) NOT NULL UNIQUE,
|
||||||
|
name VARCHAR(200) NOT NULL,
|
||||||
|
price DECIMAL(10,3) NOT NULL,
|
||||||
|
base_stock INT NOT NULL DEFAULT 0,
|
||||||
|
vat DECIMAL(5,3) NOT NULL DEFAULT 5.000,
|
||||||
|
category_id INT UNSIGNED NULL,
|
||||||
|
supplier_id INT UNSIGNED NULL,
|
||||||
|
image_url VARCHAR(255) NULL,
|
||||||
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
FOREIGN KEY (category_id) REFERENCES categories(id) ON DELETE SET NULL,
|
||||||
|
FOREIGN KEY (supplier_id) REFERENCES suppliers(id) ON DELETE SET NULL
|
||||||
|
);
|
||||||
|
");
|
||||||
|
|
||||||
|
// 2. Insert Categories
|
||||||
|
$db->exec("INSERT IGNORE INTO categories (id, name_ar, name_en) VALUES
|
||||||
|
(1, 'إلكترونيات', 'Electronics'),
|
||||||
|
(2, 'إكسسوارات', 'Accessories'),
|
||||||
|
(3, 'ملابس', 'Clothing');");
|
||||||
|
|
||||||
|
// 3. Insert Suppliers
|
||||||
|
$db->exec("INSERT IGNORE INTO suppliers (id, name, contact_person, phone) VALUES
|
||||||
|
(1, 'TechCorp', 'John Doe', '123456789'),
|
||||||
|
(2, 'ElectroWholesale', 'Jane Smith', '987654321'),
|
||||||
|
(3, 'StyleCo', 'Mike Johnson', '555666777');");
|
||||||
|
|
||||||
|
echo "Database schema and base entities created.\n";
|
||||||
|
|
||||||
@ -193,14 +193,30 @@ function can_access_branch(string $branchCode): bool
|
|||||||
|
|
||||||
function catalog(): array
|
function catalog(): array
|
||||||
{
|
{
|
||||||
return [
|
try {
|
||||||
'baklava_box' => ['sku' => 'baklava_box', 'name_ar' => 'بقلاوة مشكلة', 'name_en' => 'Mixed Baklava Box', 'price' => 18.50, 'base_stock' => 72, 'unit_ar' => 'علبة', 'unit_en' => 'box'],
|
$db = db();
|
||||||
'date_truffles' => ['sku' => 'date_truffles', 'name_ar' => 'ترافل التمر', 'name_en' => 'Date Truffles', 'price' => 9.25, 'base_stock' => 120, 'unit_ar' => 'علبة', 'unit_en' => 'box'],
|
$stmt = $db->query("SELECT * FROM items");
|
||||||
'saffron_maamoul' => ['sku' => 'saffron_maamoul', 'name_ar' => 'معمول الزعفران', 'name_en' => 'Saffron Maamoul', 'price' => 7.80, 'base_stock' => 88, 'unit_ar' => 'صندوق', 'unit_en' => 'pack'],
|
$items = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
'pistachio_bites' => ['sku' => 'pistachio_bites', 'name_ar' => 'لقيمات الفستق', 'name_en' => 'Pistachio Bites', 'price' => 11.40, 'base_stock' => 64, 'unit_ar' => 'علبة', 'unit_en' => 'box'],
|
$catalog = [];
|
||||||
'halwa_classic' => ['sku' => 'halwa_classic', 'name_ar' => 'حلوى عمانية كلاسيك', 'name_en' => 'Classic Omani Halwa', 'price' => 6.20, 'base_stock' => 150, 'unit_ar' => 'عبوة', 'unit_en' => 'jar'],
|
foreach ($items as $item) {
|
||||||
'gift_tin' => ['sku' => 'gift_tin', 'name_ar' => 'علبة هدايا فاخرة', 'name_en' => 'Premium Gift Tin', 'price' => 24.00, 'base_stock' => 36, 'unit_ar' => 'علبة', 'unit_en' => 'tin'],
|
$catalog[$item["sku"]] = [
|
||||||
];
|
"sku" => $item["sku"],
|
||||||
|
"name_ar" => $item["name"],
|
||||||
|
"name_en" => $item["name"],
|
||||||
|
"price" => (float)$item["price"],
|
||||||
|
"base_stock" => (int)$item["base_stock"],
|
||||||
|
"vat" => (float)$item["vat"],
|
||||||
|
"category_id" => $item["category_id"],
|
||||||
|
"supplier_id" => $item["supplier_id"],
|
||||||
|
"image_url" => $item["image_url"],
|
||||||
|
"unit_ar" => "قطعة",
|
||||||
|
"unit_en" => "pcs"
|
||||||
|
];
|
||||||
|
}
|
||||||
|
return $catalog;
|
||||||
|
} catch (Throwable $e) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function product_label(string $sku): string
|
function product_label(string $sku): string
|
||||||
@ -210,12 +226,13 @@ function product_label(string $sku): string
|
|||||||
return $sku;
|
return $sku;
|
||||||
}
|
}
|
||||||
|
|
||||||
return current_lang() === 'ar' ? $item['name_ar'] : $item['name_en'];
|
return current_lang() === "ar" ? $item["name_ar"] : $item["name_en"];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function currency(float $amount): string
|
function currency(float $amount): string
|
||||||
{
|
{
|
||||||
return number_format($amount, 2) . ' ' . tr('ر.ع', 'OMR');
|
return number_format($amount, 3) . ' ' . tr('ر.ع', 'OMR');
|
||||||
}
|
}
|
||||||
|
|
||||||
function sale_mode_label(string $mode): string
|
function sale_mode_label(string $mode): string
|
||||||
@ -440,7 +457,11 @@ function stock_snapshot(): array
|
|||||||
'base_stock' => $base,
|
'base_stock' => $base,
|
||||||
'sold' => $used,
|
'sold' => $used,
|
||||||
'available' => max(0, $base - $used),
|
'available' => max(0, $base - $used),
|
||||||
'price' => (float) $item['price'],
|
'price' => $item['price'],
|
||||||
|
'category_id' => $item['category_id'],
|
||||||
|
'supplier_id' => $item['supplier_id'],
|
||||||
|
'image_url' => $item['image_url'],
|
||||||
|
'vat' => $item['vat'],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
25
includes/pexels.php
Normal file
25
includes/pexels.php
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
<?php
|
||||||
|
function pexels_key() {
|
||||||
|
$k = getenv('PEXELS_KEY');
|
||||||
|
return $k && strlen($k) > 0 ? $k : 'Vc99rnmOhHhJAbgGQoKLZtsaIVfkeownoQNbTj78VemUjKh08ZYRbf18';
|
||||||
|
}
|
||||||
|
function pexels_get($url) {
|
||||||
|
$ch = curl_init();
|
||||||
|
curl_setopt_array($ch, [
|
||||||
|
CURLOPT_URL => $url,
|
||||||
|
CURLOPT_RETURNTRANSFER => true,
|
||||||
|
CURLOPT_HTTPHEADER => [ 'Authorization: '. pexels_key() ],
|
||||||
|
CURLOPT_TIMEOUT => 15,
|
||||||
|
]);
|
||||||
|
$resp = curl_exec($ch);
|
||||||
|
$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||||
|
curl_close($ch);
|
||||||
|
if ($code >= 200 && $code < 300 && $resp) return json_decode($resp, true);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
function download_to($srcUrl, $destPath) {
|
||||||
|
$data = file_get_contents($srcUrl);
|
||||||
|
if ($data === false) return false;
|
||||||
|
if (!is_dir(dirname($destPath))) mkdir(dirname($destPath), 0775, true);
|
||||||
|
return file_put_contents($destPath, $data) !== false;
|
||||||
|
}
|
||||||
17
patch_app.php
Normal file
17
patch_app.php
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<?php
|
||||||
|
$content = file_get_contents('includes/app.php');
|
||||||
|
$content = str_replace(
|
||||||
|
'available' => max(0, $base - $used),
|
||||||
|
'price' => $item['price']
|
||||||
|
,
|
||||||
|
'available' => max(0, $base - $used),
|
||||||
|
'price' => $item['price'],
|
||||||
|
'category_id' => $item['category_id'],
|
||||||
|
'supplier_id' => $item['supplier_id'],
|
||||||
|
'image_url' => $item['image_url'],
|
||||||
|
'vat' => $item['vat']
|
||||||
|
,
|
||||||
|
$content
|
||||||
|
);
|
||||||
|
file_put_contents('includes/app.php', $content);
|
||||||
|
|
||||||
19
patch_modal.php
Normal file
19
patch_modal.php
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
<?php
|
||||||
|
$content = file_get_contents('stock.php');
|
||||||
|
$content = str_replace(
|
||||||
|
"function openItemModal(sku = '', name = '', price = '', base_stock = '') {",
|
||||||
|
"function openItemModal(sku = '', name = '', price = '', base_stock = '', vat = '5', category_id = '', supplier_id = '', image_url = '') {",
|
||||||
|
$content
|
||||||
|
);
|
||||||
|
$content = str_replace(
|
||||||
|
"document.getElementById('item_vat').value = '5';",
|
||||||
|
"document.getElementById('item_vat').value = vat;\n document.getElementById('item_category').value = category_id;\n document.getElementById('item_supplier').value = supplier_id;\n \n // Remove old image preview if any\n const oldPreview = document.getElementById('image_preview');\n if (oldPreview) oldPreview.remove();\n \n if (image_url) {\n const preview = document.createElement('img');\n preview.id = 'image_preview';\n preview.src = image_url;\n preview.style.maxHeight = '100px';\n preview.className = 'mt-2 rounded';\n document.getElementById('item_picture').parentElement.appendChild(preview);\n }",
|
||||||
|
$content
|
||||||
|
);
|
||||||
|
$content = str_replace(
|
||||||
|
"document.getElementById('item_category').value = '';\n document.getElementById('item_supplier').value = '';",
|
||||||
|
"",
|
||||||
|
$content
|
||||||
|
);
|
||||||
|
file_put_contents('stock.php', $content);
|
||||||
|
|
||||||
4
patch_table.php
Normal file
4
patch_table.php
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
<?php
|
||||||
|
$content = file_get_contents('stock.php');
|
||||||
|
$content = preg_replace('/(\s*)<td><\?= h\(\$row[\'name\']\) \?><\/td>/', "$1<td>\n$1 <?php if (!empty(\$row['image_url'])): ?>\n$1 <img src=\"<?= h(\$row['image_url']) ?>\" alt=\"\" class=\"rounded\" style=\"width: 40px; height: 40px; object-fit: cover;\">\n$1 <?php else: ?>\n$1 <div class=\"bg-light rounded d-flex align-items-center justify-content-center text-muted\" style=\"width: 40px; height: 40px;\"><i class=\"bi bi-image\"></i></div>\n$1 <?php endif; ?>\n$1</td>\n$1<td><?= h(\$row['name']) ?></td>", $content);
|
||||||
|
file_put_contents('stock.php', $content);
|
||||||
@ -10,11 +10,11 @@ $allPurchases = purchase_pipeline();
|
|||||||
$search = $_GET['q'] ?? '';
|
$search = $_GET['q'] ?? '';
|
||||||
$filteredPurchases = [];
|
$filteredPurchases = [];
|
||||||
if ($search) {
|
if ($search) {
|
||||||
$lowerSearch = mb_strtolower($search);
|
$lowerSearch = strtolower($search);
|
||||||
foreach ($allPurchases as $key => $row) {
|
foreach ($allPurchases as $key => $row) {
|
||||||
if (
|
if (
|
||||||
str_contains(mb_strtolower($row['supplier']), $lowerSearch) ||
|
str_contains(strtolower((string)$row['supplier']), $lowerSearch) ||
|
||||||
str_contains(mb_strtolower($row['reference']), $lowerSearch)
|
str_contains(strtolower((string)$row['reference']), $lowerSearch)
|
||||||
) {
|
) {
|
||||||
$filteredPurchases[$key] = $row;
|
$filteredPurchases[$key] = $row;
|
||||||
}
|
}
|
||||||
|
|||||||
61
seed_items.php
Normal file
61
seed_items.php
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
<?php
|
||||||
|
require 'db/config.php';
|
||||||
|
require 'includes/pexels.php';
|
||||||
|
|
||||||
|
$db = db();
|
||||||
|
|
||||||
|
// Define items to insert
|
||||||
|
$items = [
|
||||||
|
['sku' => '10000001', 'name' => 'Laptop Pro 15', 'price' => 1200.000, 'base_stock' => 10, 'category_id' => 1, 'supplier_id' => 1, 'query' => 'laptop'],
|
||||||
|
['sku' => '10000002', 'name' => 'Wireless Mouse', 'price' => 25.500, 'base_stock' => 50, 'category_id' => 2, 'supplier_id' => 2, 'query' => 'computer mouse'],
|
||||||
|
['sku' => '10000003', 'name' => 'Mechanical Keyboard', 'price' => 85.000, 'base_stock' => 30, 'category_id' => 2, 'supplier_id' => 1, 'query' => 'keyboard'],
|
||||||
|
['sku' => '10000004', 'name' => 'USB-C Hub', 'price' => 40.000, 'base_stock' => 100, 'category_id' => 2, 'supplier_id' => 2, 'query' => 'usb cable'],
|
||||||
|
['sku' => '10000005', 'name' => 'Cotton T-Shirt', 'price' => 15.000, 'base_stock' => 200, 'category_id' => 3, 'supplier_id' => 3, 'query' => 't-shirt'],
|
||||||
|
];
|
||||||
|
|
||||||
|
foreach ($items as $item) {
|
||||||
|
// Check if sku already exists
|
||||||
|
$stmt = $db->prepare("SELECT id FROM items WHERE sku = ?");
|
||||||
|
$stmt->execute([$item['sku']]);
|
||||||
|
if ($stmt->fetch()) {
|
||||||
|
echo "SKU {" . $item['sku'] . "} already exists.\n";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$imageUrl = null;
|
||||||
|
$localImagePath = null;
|
||||||
|
|
||||||
|
// Fetch image from pexels
|
||||||
|
$url = 'https://api.pexels.com/v1/search?query=' . urlencode($item['query']) . '&orientation=square&per_page=1&page=1';
|
||||||
|
$data = pexels_get($url);
|
||||||
|
if ($data && !empty($data['photos'])) {
|
||||||
|
$photo = $data['photos'][0];
|
||||||
|
$src = $photo['src']['medium'] ?? ($photo['src']['original'] ?? null);
|
||||||
|
if ($src) {
|
||||||
|
$dest = __DIR__ . '/assets/images/items/' . $photo['id'] . '.jpg';
|
||||||
|
if (download_to($src, $dest)) {
|
||||||
|
$localImagePath = 'assets/images/items/' . $photo['id'] . '.jpg';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$localImagePath) {
|
||||||
|
$localImagePath = 'https://picsum.photos/400?random=' . rand(1, 1000); // fallback
|
||||||
|
}
|
||||||
|
|
||||||
|
$stmt = $db->prepare("INSERT INTO items (sku, name, price, base_stock, vat, category_id, supplier_id, image_url) VALUES (?, ?, ?, ?, ?, ?, ?, ?)");
|
||||||
|
$stmt->execute([
|
||||||
|
$item['sku'],
|
||||||
|
$item['name'],
|
||||||
|
$item['price'],
|
||||||
|
$item['base_stock'],
|
||||||
|
5.000, // default VAT
|
||||||
|
$item['category_id'],
|
||||||
|
$item['supplier_id'],
|
||||||
|
$localImagePath
|
||||||
|
]);
|
||||||
|
echo "Inserted {" . $item['name'] . "}\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "Done seeding items.\n";
|
||||||
|
|
||||||
89
stock.php
89
stock.php
@ -22,21 +22,23 @@ try {
|
|||||||
// Ignore if not present
|
// Ignore if not present
|
||||||
}
|
}
|
||||||
|
|
||||||
// Search logic
|
// Search and filter logic
|
||||||
$search = $_GET['q'] ?? '';
|
$search = $_GET['q'] ?? '';
|
||||||
|
$catFilter = $_GET['category'] ?? '';
|
||||||
|
$supFilter = $_GET['supplier'] ?? '';
|
||||||
$filteredStock = [];
|
$filteredStock = [];
|
||||||
if ($search && empty($dbError)) {
|
|
||||||
$lowerSearch = mb_strtolower($search);
|
if (empty($dbError)) {
|
||||||
|
$lowerSearch = strtolower($search);
|
||||||
foreach ($allStock as $key => $row) {
|
foreach ($allStock as $key => $row) {
|
||||||
if (
|
$matchSearch = !$search || str_contains(strtolower((string)$row['sku']), $lowerSearch) || str_contains(strtolower((string)$row['name']), $lowerSearch);
|
||||||
str_contains(mb_strtolower($row['sku']), $lowerSearch) ||
|
$matchCat = !$catFilter || (isset($row['category_id']) && $row['category_id'] == $catFilter);
|
||||||
str_contains(mb_strtolower($row['name']), $lowerSearch)
|
$matchSup = !$supFilter || (isset($row['supplier_id']) && $row['supplier_id'] == $supFilter);
|
||||||
) {
|
|
||||||
|
if ($matchSearch && $matchCat && $matchSup) {
|
||||||
$filteredStock[$key] = $row;
|
$filteredStock[$key] = $row;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
$filteredStock = $allStock;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pagination logic
|
// Pagination logic
|
||||||
@ -63,10 +65,32 @@ require __DIR__ . '/includes/header.php';
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<form class="d-flex mb-3" method="GET" action="stock.php">
|
<form class="mb-4" method="GET" action="stock.php">
|
||||||
<div class="input-group" style="max-width: 400px;">
|
<div class="row g-2">
|
||||||
<input type="text" name="q" class="form-control" placeholder="<?= h(tr('بحث برمز الصنف أو الاسم...', 'Search by SKU or name...')) ?>" value="<?= h($search) ?>">
|
<div class="col-md-4">
|
||||||
<button class="btn btn-outline-secondary" type="submit"><i class="bi bi-search"></i></button>
|
<input type="text" name="q" class="form-control" placeholder="<?= h(tr('بحث برمز الصنف أو الاسم...', 'Search by SKU or name...')) ?>" value="<?= h($search) ?>">
|
||||||
|
</div>
|
||||||
|
<div class="col-md-3">
|
||||||
|
<select name="category" class="form-select">
|
||||||
|
<option value=""><?= h(tr('الكل (التصنيف)', 'All Categories')) ?></option>
|
||||||
|
<?php foreach($categories as $cat): ?>
|
||||||
|
<option value="<?= h($cat['id']) ?>" <?= ($catFilter == $cat['id']) ? 'selected' : '' ?>><?= h(current_lang() === 'ar' ? $cat['name_ar'] : $cat['name_en']) ?></option>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-3">
|
||||||
|
<select name="supplier" class="form-select">
|
||||||
|
<option value=""><?= h(tr('الكل (المورد)', 'All Suppliers')) ?></option>
|
||||||
|
<?php foreach($suppliers as $sup): ?>
|
||||||
|
<option value="<?= h($sup['id']) ?>" <?= ($supFilter == $sup['id']) ? 'selected' : '' ?>><?= h($sup['name']) ?></option>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-2">
|
||||||
|
<button class="btn btn-primary w-100" type="submit">
|
||||||
|
<i class="bi bi-funnel"></i> <?= h(tr('تصفية', 'Filter')) ?>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</section>
|
</section>
|
||||||
@ -77,9 +101,10 @@ require __DIR__ . '/includes/header.php';
|
|||||||
<?php else: ?>
|
<?php else: ?>
|
||||||
<div class="table-responsive">
|
<div class="table-responsive">
|
||||||
<table class="table app-table align-middle mb-0">
|
<table class="table app-table align-middle mb-0">
|
||||||
<thead>
|
<thead class="table-primary">
|
||||||
<tr>
|
<tr>
|
||||||
<th>SKU</th>
|
<th>SKU</th>
|
||||||
|
<th width="60"><?= h(tr('صورة', 'Pic')) ?></th>
|
||||||
<th><?= h(tr('الصنف', 'Product')) ?></th>
|
<th><?= h(tr('الصنف', 'Product')) ?></th>
|
||||||
<th><?= h(tr('السعر', 'Price')) ?></th>
|
<th><?= h(tr('السعر', 'Price')) ?></th>
|
||||||
<th><?= h(tr('افتتاحي', 'Opening')) ?></th>
|
<th><?= h(tr('افتتاحي', 'Opening')) ?></th>
|
||||||
@ -109,7 +134,7 @@ require __DIR__ . '/includes/header.php';
|
|||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
</td>
|
</td>
|
||||||
<td class="text-end">
|
<td class="text-end">
|
||||||
<button class="btn btn-sm btn-light text-primary border" onclick="openItemModal('<?= h($row['sku']) ?>', '<?= h(addslashes($row['name'])) ?>', '<?= h($row['price']) ?>', '<?= h($row['base_stock']) ?>')" data-bs-toggle="tooltip" title="<?= h(tr('تعديل', 'Edit')) ?>">
|
<button class="btn btn-sm btn-light text-primary border" onclick="openItemModal('<?= h($row['sku']) ?>', '<?= h(addslashes($row['name'])) ?>', '<?= h($row['price']) ?>', '<?= h($row['base_stock']) ?>', '<?= h($row['vat'] ?? 5) ?>', '<?= h($row['category_id'] ?? '') ?>', '<?= h($row['supplier_id'] ?? '') ?>', '<?= h($row['image_url'] ?? '') ?>')" data-bs-toggle="tooltip" title="<?= h(tr('تعديل', 'Edit')) ?>">
|
||||||
<i class="bi bi-pencil"></i>
|
<i class="bi bi-pencil"></i>
|
||||||
</button>
|
</button>
|
||||||
<button class="btn btn-sm btn-light text-danger border" onclick="mockDelete()" data-bs-toggle="tooltip" title="<?= h(tr('حذف', 'Delete')) ?>">
|
<button class="btn btn-sm btn-light text-danger border" onclick="mockDelete()" data-bs-toggle="tooltip" title="<?= h(tr('حذف', 'Delete')) ?>">
|
||||||
@ -127,7 +152,7 @@ require __DIR__ . '/includes/header.php';
|
|||||||
<ul class="pagination justify-content-center mb-0">
|
<ul class="pagination justify-content-center mb-0">
|
||||||
<?php for($i=1; $i<=$totalPages; $i++): ?>
|
<?php for($i=1; $i<=$totalPages; $i++): ?>
|
||||||
<li class="page-item <?= $i === $page ? 'active' : '' ?>">
|
<li class="page-item <?= $i === $page ? 'active' : '' ?>">
|
||||||
<a class="page-link" href="<?= h(url_for('stock.php', ['p' => $i, 'q' => $search])) ?>"><?= $i ?></a>
|
<a class="page-link" href="<?= h(url_for('stock.php', ['p' => $i, 'q' => $search, 'category' => $catFilter, 'supplier' => $supFilter])) ?>"><?= $i ?></a>
|
||||||
</li>
|
</li>
|
||||||
<?php endfor; ?>
|
<?php endfor; ?>
|
||||||
</ul>
|
</ul>
|
||||||
@ -141,9 +166,9 @@ require __DIR__ . '/includes/header.php';
|
|||||||
<div class="modal-dialog">
|
<div class="modal-dialog">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<form onsubmit="handleItemSubmit(event)">
|
<form onsubmit="handleItemSubmit(event)">
|
||||||
<div class="modal-header">
|
<div class="modal-header bg-primary text-white ">
|
||||||
<h5 class="modal-title" id="itemModalLabel"><?= h(tr('إضافة / تعديل صنف', 'Add / Edit Item')) ?></h5>
|
<h5 class="modal-title" id="itemModalLabel"><?= h(tr('إضافة / تعديل صنف', 'Add / Edit Item')) ?></h5>
|
||||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
<button type="button" class="btn-close btn-close-white " data-bs-dismiss="modal" aria-label="Close" ></button>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<div class="mb-3 text-center">
|
<div class="mb-3 text-center">
|
||||||
@ -166,7 +191,7 @@ require __DIR__ . '/includes/header.php';
|
|||||||
<div class="row mb-3">
|
<div class="row mb-3">
|
||||||
<div class="col-4">
|
<div class="col-4">
|
||||||
<label class="form-label"><?= h(tr('السعر', 'Price')) ?></label>
|
<label class="form-label"><?= h(tr('السعر', 'Price')) ?></label>
|
||||||
<input type="number" step="0.01" class="form-control" id="item_price" required>
|
<input type="number" step="0.001" class="form-control" id="item_price" required>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-4">
|
<div class="col-4">
|
||||||
<label class="form-label"><?= h(tr('الرصيد الافتتاحي', 'Opening Stock')) ?></label>
|
<label class="form-label"><?= h(tr('الرصيد الافتتاحي', 'Opening Stock')) ?></label>
|
||||||
@ -174,7 +199,7 @@ require __DIR__ . '/includes/header.php';
|
|||||||
</div>
|
</div>
|
||||||
<div class="col-4">
|
<div class="col-4">
|
||||||
<label class="form-label"><?= h(tr('الضريبة (VAT %)', 'VAT %')) ?></label>
|
<label class="form-label"><?= h(tr('الضريبة (VAT %)', 'VAT %')) ?></label>
|
||||||
<input type="number" step="0.01" class="form-control" id="item_vat" value="5" required>
|
<input type="number" step="0.001" class="form-control" id="item_vat" value="5" required>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
@ -227,15 +252,29 @@ document.addEventListener('DOMContentLoaded', function () {
|
|||||||
itemModalObj = new bootstrap.Modal(document.getElementById('itemModal'));
|
itemModalObj = new bootstrap.Modal(document.getElementById('itemModal'));
|
||||||
});
|
});
|
||||||
|
|
||||||
function openItemModal(sku = '', name = '', price = '', base_stock = '') {
|
function openItemModal(sku = '', name = '', price = '', base_stock = '', vat = '5', category_id = '', supplier_id = '', image_url = '') {
|
||||||
document.getElementById('item_sku').value = sku;
|
document.getElementById('item_sku').value = sku;
|
||||||
document.getElementById('item_name').value = name;
|
document.getElementById('item_name').value = name;
|
||||||
document.getElementById('item_price').value = price;
|
document.getElementById('item_price').value = price;
|
||||||
document.getElementById('item_base_stock').value = base_stock;
|
document.getElementById('item_base_stock').value = base_stock;
|
||||||
document.getElementById('item_vat').value = '5';
|
document.getElementById('item_vat').value = vat;
|
||||||
|
document.getElementById('item_category').value = category_id;
|
||||||
|
document.getElementById('item_supplier').value = supplier_id;
|
||||||
|
|
||||||
|
// Remove old image preview if any
|
||||||
|
const oldPreview = document.getElementById('image_preview');
|
||||||
|
if (oldPreview) oldPreview.remove();
|
||||||
|
|
||||||
|
if (image_url) {
|
||||||
|
const preview = document.createElement('img');
|
||||||
|
preview.id = 'image_preview';
|
||||||
|
preview.src = image_url;
|
||||||
|
preview.style.maxHeight = '100px';
|
||||||
|
preview.className = 'mt-2 rounded';
|
||||||
|
document.getElementById('item_picture').parentElement.appendChild(preview);
|
||||||
|
}
|
||||||
document.getElementById('item_picture').value = '';
|
document.getElementById('item_picture').value = '';
|
||||||
document.getElementById('item_category').value = '';
|
|
||||||
document.getElementById('item_supplier').value = '';
|
|
||||||
|
|
||||||
itemModalObj.show();
|
itemModalObj.show();
|
||||||
}
|
}
|
||||||
@ -273,4 +312,4 @@ function mockDelete() {
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<?php require __DIR__ . '/includes/footer.php'; ?>
|
<?php require __DIR__ . '/includes/footer.php'; ?>
|
||||||
@ -10,12 +10,12 @@ $allAccounts = demo_users();
|
|||||||
$search = $_GET['q'] ?? '';
|
$search = $_GET['q'] ?? '';
|
||||||
$filteredAccounts = [];
|
$filteredAccounts = [];
|
||||||
if ($search) {
|
if ($search) {
|
||||||
$lowerSearch = mb_strtolower($search);
|
$lowerSearch = strtolower($search);
|
||||||
foreach ($allAccounts as $key => $acc) {
|
foreach ($allAccounts as $key => $acc) {
|
||||||
if (
|
if (
|
||||||
str_contains(mb_strtolower($acc['name_ar']), $lowerSearch) ||
|
str_contains(strtolower((string)$acc['name_ar']), $lowerSearch) ||
|
||||||
str_contains(mb_strtolower($acc['name_en']), $lowerSearch) ||
|
str_contains(strtolower((string)$acc['name_en']), $lowerSearch) ||
|
||||||
str_contains(mb_strtolower($acc['username']), $lowerSearch)
|
str_contains(strtolower((string)$acc['username']), $lowerSearch)
|
||||||
) {
|
) {
|
||||||
$filteredAccounts[$key] = $acc;
|
$filteredAccounts[$key] = $acc;
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user