Autosave: 20260419-074108

This commit is contained in:
Flatlogic Bot 2026-04-19 07:41:01 +00:00
parent 02afd76d75
commit 4f9eddc419
14 changed files with 1328 additions and 93 deletions

56
api/settings.php Normal file
View File

@ -0,0 +1,56 @@
<?php
require_once __DIR__ . '/../includes/app.php';
require_auth();
$user = current_user();
if (!in_array($user['role'], ['owner', 'manager'])) {
set_flash('danger', tr('غير مصرح لك.', 'Unauthorized.'));
redirect_to('../index.php');
exit;
}
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$pdo = db();
$keys = [
'company_name_ar', 'company_name_en', 'vat_percentage',
'company_vat_number', 'company_phone', 'company_email', 'company_address'
];
$stmt = $pdo->prepare("INSERT INTO settings (setting_key, setting_value) VALUES (?, ?) ON DUPLICATE KEY UPDATE setting_value = VALUES(setting_value)");
foreach ($keys as $key) {
if (isset($_POST[$key])) {
$stmt->execute([$key, $_POST[$key]]);
}
}
// Handle logo upload
$uploadDir = __DIR__ . '/../assets/images/';
if (!is_dir($uploadDir)) {
mkdir($uploadDir, 0777, true);
}
if (isset($_FILES['company_logo']) && $_FILES['company_logo']['error'] === UPLOAD_ERR_OK) {
$ext = pathinfo($_FILES['company_logo']['name'], PATHINFO_EXTENSION);
$filename = 'logo_' . time() . '.' . $ext;
if (move_uploaded_file($_FILES['company_logo']['tmp_name'], $uploadDir . $filename)) {
$stmt->execute(['company_logo', 'assets/images/' . $filename]);
}
}
// Handle favicon upload
if (isset($_FILES['company_favicon']) && $_FILES['company_favicon']['error'] === UPLOAD_ERR_OK) {
$ext = pathinfo($_FILES['company_favicon']['name'], PATHINFO_EXTENSION);
$filename = 'favicon_' . time() . '.' . $ext;
if (move_uploaded_file($_FILES['company_favicon']['tmp_name'], $uploadDir . $filename)) {
$stmt->execute(['company_favicon', 'assets/images/' . $filename]);
}
}
set_flash('success', tr('تم حفظ الإعدادات بنجاح.', 'Settings saved successfully.'));
// Redirect back to referring page
$referer = $_SERVER['HTTP_REFERER'] ?? '../index.php';
header('Location: ' . $referer);
exit;
}

View File

@ -2,4 +2,4 @@
# 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
127.0.0.1 FALSE / FALSE 0 PHPSESSID 6qft39lctsp4e64kmen99qtqfo

691
edit_sale.php Normal file
View File

@ -0,0 +1,691 @@
<?php
require_once __DIR__ . '/includes/app.php';
$user = require_roles(['owner', 'manager', 'cashier']);
$editSaleId = (int)($_GET['id'] ?? 0);
$editSale = null;
if ($editSaleId > 0) {
$stmt = db()->prepare('SELECT * FROM sales_orders WHERE id = :id');
$stmt->execute([':id' => $editSaleId]);
$editSale = $stmt->fetch();
}
if (!$editSale) {
die(tr('الفاتورة غير موجودة.', 'Invoice not found.'));
}
if ($user['role'] !== 'owner' && $editSale['branch_code'] !== $user['branch_code']) {
die(tr('غير مصرح لك.', 'Unauthorized.'));
}
$pageTitle = tr('تعديل فاتورة', 'Edit Invoice') . ' #' . h($editSale['receipt_no']);
$activeNav = 'sales';
$error = '';
$catalog = catalog();
$allowedBranches = $user['role'] === 'owner' ? array_keys(branches()) : [$user['branch_code']];
try {
$customers = db()->query('SELECT id, name, phone FROM customers ORDER BY name ASC')->fetchAll();
} catch (Throwable $e) {
$customers = [];
}
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$branchCode = trim((string) ($_POST['branch_code'] ?? ''));
$customerName = trim((string) ($_POST['customer_name'] ?? ''));
$paymentMethod = trim((string) ($_POST['payment_method'] ?? 'cash'));
$saleStatus = trim((string) ($_POST['sale_status'] ?? 'completed'));
$notes = trim((string) ($_POST['notes'] ?? ''));
$cartJson = (string) ($_POST['cart_json'] ?? '[]');
$items = json_decode($cartJson, true);
if (!in_array($branchCode, $allowedBranches, true)) {
$error = tr('اختر فرعاً صالحاً لهذه الصلاحية.', 'Choose a valid branch for this role.');
} elseif (!in_array($paymentMethod, ['cash', 'card', 'transfer'], true)) {
$error = tr('اختر طريقة دفع صحيحة.', 'Choose a valid payment method.');
} elseif (!is_array($items) || $items === []) {
$error = tr('أضف صنفاً واحداً على الأقل إلى الفاتورة.', 'Add at least one item to the invoice.');
} else {
$normalized = [];
$subtotal = 0.0;
$itemCount = 0;
foreach ($items as $item) {
$sku = (string) ($item['sku'] ?? '');
$qty = (int) ($item['qty'] ?? 0);
if (!isset($catalog[$sku]) || $qty < 1) {
continue;
}
$product = $catalog[$sku];
$price = (float) $product['price'];
$lineTotal = $price * $qty;
$normalized[] = [
'sku' => $sku,
'name_ar' => $product['name_ar'],
'name_en' => $product['name_en'],
'qty' => $qty,
'price' => $price,
'line_total' => $lineTotal,
];
$subtotal += $lineTotal;
$itemCount += $qty;
}
if ($normalized === []) {
$error = tr('الفاتورة غير صالحة بعد التحقق من الأصناف.', 'The invoice is invalid after product validation.');
} else {
$cashierName = current_lang() === 'ar' ? $user['name_ar'] : $user['name_en'];
$stmt = db()->prepare('UPDATE sales_orders SET
branch_code = :branch_code,
customer_name = :customer_name,
payment_method = :payment_method,
items_json = :items_json,
item_count = :item_count,
subtotal = :subtotal,
total_amount = :total_amount,
status = :status,
notes = :notes
WHERE id = :id');
$stmt->execute([
':branch_code' => $branchCode,
':customer_name' => $customerName !== '' ? $customerName : null,
':payment_method' => $paymentMethod,
':items_json' => json_encode($normalized, JSON_UNESCAPED_UNICODE),
':item_count' => $itemCount,
':subtotal' => $subtotal,
':total_amount' => $subtotal,
':status' => $saleStatus,
':notes' => $notes !== '' ? $notes : null,
':id' => $editSaleId,
]);
set_flash('success', tr('تم تحديث الفاتورة بنجاح.', 'Invoice updated successfully.'));
redirect_to('sale.php', ['id' => $editSaleId]);
}
}
}
require __DIR__ . '/includes/header.php';
?>
<style>
.smart-form-card {
background: #fff;
border-radius: 12px;
box-shadow: 0 4px 15px rgba(0,0,0,0.05);
border: 1px solid #edf2f9;
margin-bottom: 2rem;
}
.smart-form-header {
padding: 1.5rem 2rem;
border-bottom: 1px solid #edf2f9;
background-color: #fcfdfd;
border-radius: 12px 12px 0 0;
}
.smart-form-body {
padding: 2rem;
}
.section-title {
font-size: 1.1rem;
font-weight: 600;
color: #2c3e50;
margin-bottom: 1.5rem;
display: flex;
align-items: center;
gap: 0.5rem;
}
.form-label {
font-weight: 500;
color: #495057;
margin-bottom: 0.4rem;
}
.custom-input {
border: 1px solid #ced4da;
border-radius: 8px;
padding: 0.6rem 1rem;
font-size: 0.95rem;
transition: all 0.2s ease-in-out;
}
.custom-input:focus {
border-color: #3b82f6;
box-shadow: 0 0 0 0.25rem rgba(59, 130, 246, 0.25);
}
.search-wrapper {
position: relative;
max-width: 600px;
margin-bottom: 2rem;
}
.search-icon {
position: absolute;
top: 50%;
left: 1rem;
transform: translateY(-50%);
color: #6c757d;
}
[dir="rtl"] .search-icon {
left: auto;
right: 1rem;
}
.search-input {
padding-left: 2.5rem;
}
[dir="rtl"] .search-input {
padding-left: 1rem;
padding-right: 2.5rem;
}
.item-search-dropdown {
position: absolute;
top: 100%;
left: 0;
right: 0;
background: #fff;
border-radius: 8px;
box-shadow: 0 10px 30px rgba(0,0,0,0.1);
z-index: 1000;
max-height: 300px;
overflow-y: auto;
display: none;
border: 1px solid #edf2f9;
margin-top: 0.5rem;
}
.item-search-dropdown.show { display: block; }
.search-item-row {
padding: 0.75rem 1rem;
cursor: pointer;
border-bottom: 1px solid #edf2f9;
transition: background 0.15s;
}
.search-item-row:hover { background: #f8f9fa; }
.search-item-row:last-child { border-bottom: none; }
.table-modern {
width: 100%;
border-collapse: separate;
border-spacing: 0;
border: 1px solid #edf2f9;
border-radius: 8px;
overflow: hidden;
}
.table-modern th {
background: #f8f9fa;
padding: 1rem;
font-weight: 600;
color: #495057;
border-bottom: 1px solid #edf2f9;
font-size: 0.9rem;
}
.table-modern td {
padding: 1rem;
vertical-align: middle;
border-bottom: 1px solid #edf2f9;
}
.table-modern tr:last-child td {
border-bottom: none;
}
.qty-control {
width: 80px;
text-align: center;
border: 1px solid #ced4da;
border-radius: 6px;
padding: 0.4rem;
}
.btn-remove {
color: #dc3545;
background: rgba(220, 53, 69, 0.1);
border: none;
width: 32px;
height: 32px;
border-radius: 6px;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.2s;
}
.btn-remove:hover {
background: #dc3545;
color: #fff;
}
.totals-box {
background: #f8f9fa;
border-radius: 8px;
padding: 1.5rem;
border: 1px solid #edf2f9;
}
.totals-row {
display: flex;
justify-content: space-between;
margin-bottom: 0.75rem;
color: #495057;
}
.totals-row.grand-total {
margin-top: 1rem;
padding-top: 1rem;
border-top: 1px solid #dee2e6;
font-size: 1.25rem;
font-weight: 700;
color: #212529;
margin-bottom: 0;
}
</style>
<div class="container-fluid py-4">
<div class="d-flex justify-content-between align-items-center mb-4">
<h3 class="fw-bold mb-0 text-dark"><?= h($pageTitle) ?></h3>
<a href="sales.php" class="btn btn-outline-secondary btn-sm">
<i class="bi bi-arrow-left"></i> <?= h(tr('عودة للمبيعات', 'Back to Sales')) ?>
</a>
</div>
<?php if ($error !== ''): ?>
<div class="alert alert-danger rounded-3 shadow-sm mb-4"><i class="bi bi-exclamation-triangle-fill me-2"></i><?= h($error) ?></div>
<?php endif; ?>
<form method="post" id="smart-sale-form">
<input type="hidden" name="cart_json" id="cart_json" value="[]">
<div class="row">
<div class="col-lg-8">
<!-- Items Section -->
<div class="smart-form-card">
<div class="smart-form-header">
<div class="section-title mb-0">
<i class="bi bi-cart-plus text-primary"></i> <?= h(tr('عناصر الفاتورة', 'Invoice Items')) ?>
</div>
</div>
<div class="smart-form-body">
<!-- Search Bar -->
<div class="search-wrapper">
<i class="bi bi-search search-icon"></i>
<input type="text" id="itemSearchInput" class="form-control custom-input search-input form-control-lg" placeholder="<?= h(tr('ابحث بالاسم أو الباركود...', 'Search by name or barcode...')) ?>" autocomplete="off">
<div id="itemDropdown" class="item-search-dropdown"></div>
</div>
<!-- Table -->
<div class="table-responsive">
<table class="table-modern" id="invoiceTable">
<thead>
<tr>
<th width="45%"><?= h(tr('المنتج', 'Product')) ?></th>
<th width="15%" class="text-center"><?= h(tr('السعر', 'Price')) ?></th>
<th width="15%" class="text-center"><?= h(tr('الكمية', 'Qty')) ?></th>
<th width="20%" class="text-center"><?= h(tr('الإجمالي', 'Total')) ?></th>
<th width="5%"></th>
</tr>
</thead>
<tbody id="invoiceLines">
<tr id="emptyInvoiceRow">
<td colspan="5" class="text-center py-5 text-muted">
<i class="bi bi-inbox fs-1 d-block mb-2 text-light"></i>
<?= h(tr('لم يتم إضافة أي منتجات بعد.', 'No products added yet.')) ?>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
<div class="col-lg-4">
<!-- Settings Section -->
<div class="smart-form-card">
<div class="smart-form-header">
<div class="section-title mb-0">
<i class="bi bi-receipt text-primary"></i> <?= h(tr('تفاصيل الفاتورة', 'Invoice Details')) ?>
</div>
</div>
<div class="smart-form-body">
<div class="mb-3">
<label class="form-label"><?= h(tr('الفرع', 'Branch')) ?></label>
<select class="form-select custom-input" name="branch_code" <?= count($allowedBranches) === 1 ? 'readonly' : '' ?>>
<?php foreach ($allowedBranches as $branchCode): ?>
<option value="<?= h($branchCode) ?>" <?= $branchCode === $editSale['branch_code'] ? 'selected' : '' ?>><?= h(branch_label($branchCode)) ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="mb-3 position-relative">
<label class="form-label"><?= h(tr('العميل', 'Customer')) ?></label>
<div class="input-group">
<input type="text" id="formCustomer" name="customer_name" class="form-control custom-input" style="border-right-width: 1px;" placeholder="<?= h(tr('بحث (اسم أو هاتف)', 'Search (Name or Phone)')) ?>" autocomplete="off" value="<?= h($editSale['customer_name'] ?? '' ) ?>">
<button class="btn btn-outline-primary px-3" style="border-radius: 0 8px 8px 0;" type="button" onclick="openNewCustomerModal()" title="<?= h(tr('إضافة عميل', 'Add Customer')) ?>">
<i class="bi bi-person-plus-fill"></i>
</button>
</div>
<div id="formCustomerDropdown" class="item-search-dropdown w-100" style="top: 100%;"></div>
</div>
<div class="mb-3">
<label class="form-label"><?= h(tr('نوع العملية', 'Entry Type')) ?></label>
<select class="form-select custom-input" name="sale_status">
<option value="completed" <?= $editSale['status'] === 'completed' ? 'selected' : '' ?>><?= h(tr('فاتورة بيع (تم الدفع)', 'Sale Bill (Paid)')) ?></option>
<option value="order" <?= $editSale['status'] === 'order' ? 'selected' : '' ?>><?= h(tr('طلب مسبق (دفع لاحق)', 'Order (Pay Later)')) ?></option>
</select>
</div>
<div class="mb-3">
<label class="form-label"><?= h(tr('طريقة الدفع', 'Payment Method')) ?></label>
<select class="form-select custom-input" name="payment_method">
<option value="cash" <?= $editSale['payment_method'] === 'cash' ? 'selected' : '' ?>><?= h(tr('نقداً', 'Cash')) ?></option>
<option value="card" <?= $editSale['payment_method'] === 'card' ? 'selected' : '' ?>><?= h(tr('بطاقة ائتمان', 'Credit Card')) ?></option>
<option value="transfer" <?= $editSale['payment_method'] === 'transfer' ? 'selected' : '' ?>><?= h(tr('تحويل بنكي', 'Bank Transfer')) ?></option>
</select>
</div>
<div class="mb-4">
<label class="form-label"><?= h(tr('ملاحظات (اختياري)', 'Notes (Optional)')) ?></label>
<textarea class="form-control custom-input" name="notes" rows="2" placeholder="<?= h(tr('أي ملاحظات إضافية...', 'Any additional notes...')) ?>"><?= h($editSale['notes'] ?? '' ) ?></textarea>
</div>
<!-- Summary -->
<div class="totals-box mb-4">
<div class="totals-row">
<span><?= h(tr('المجموع الفرعي', 'Subtotal')) ?></span>
<span id="displaySubtotal" class="fw-medium">0.000</span>
</div>
<div class="totals-row">
<span><?= h(tr('الضريبة (' . get_setting('vat_percentage', 5) . '%)', 'VAT (' . get_setting('vat_percentage', 5) . '%)')) ?></span>
<span class="text-success small"><?= h(tr('مشمولة', 'Included')) ?></span>
</div>
<div class="totals-row grand-total">
<span><?= h(tr('الإجمالي', 'Total')) ?></span>
<span id="displayTotal" class="text-primary">0.000 <?= h(tr('ر.ع', 'OMR')) ?></span>
</div>
</div>
<button type="submit" class="btn btn-primary w-100 py-2 fs-5 rounded-3 shadow-sm">
<i class="bi bi-check-circle me-1"></i> <?= h(tr('حفظ الفاتورة', 'Save Invoice')) ?>
</button>
</div>
</div>
</div>
</div>
</form>
</div>
<!-- New Customer Modal -->
<div class="modal fade" id="newCustomerModal" tabindex="-1" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered modal-sm">
<div class="modal-content border-0 shadow-lg" style="border-radius: 16px;">
<div class="modal-header border-0 pb-0">
<h5 class="modal-title fw-bold"><?= h(tr('إضافة عميل', 'Add Customer')) ?></h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div class="mb-3">
<label class="form-label text-muted small mb-1"><?= h(tr('الاسم', 'Name')) ?> <span class="text-danger">*</span></label>
<input type="text" id="ncName" class="form-control rounded-3">
</div>
<div class="mb-3">
<label class="form-label text-muted small mb-1"><?= h(tr('رقم الهاتف', 'Phone')) ?></label>
<input type="text" id="ncPhone" class="form-control rounded-3" dir="ltr">
</div>
<div class="d-grid mt-4">
<button class="btn btn-primary rounded-pill fw-semibold shadow-sm" onclick="saveNewCustomer()"><?= h(tr('حفظ العميل', 'Save Customer')) ?></button>
</div>
</div>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
<script>
const catalogData = <?= json_encode($catalog, JSON_UNESCAPED_UNICODE) ?>;
const catalogArray = Object.values(catalogData);
let invoiceItems = {};
// Prepopulate from editSale
const initialItemsJson = <?= empty($editSale['items_json']) ? '[]' : $editSale['items_json'] ?>;
initialItemsJson.forEach(item => {
invoiceItems[item.sku] = {
sku: item.sku,
name: '<?= current_lang() ?>' === 'ar' ? item.name_ar : item.name_en,
price: parseFloat(item.price),
qty: parseInt(item.qty)
};
});
// renderInvoice();
const searchInput = document.getElementById('itemSearchInput');
const dropdown = document.getElementById('itemDropdown');
const tbody = document.getElementById('invoiceLines');
const emptyRow = document.getElementById('emptyInvoiceRow');
const cartJson = document.getElementById('cart_json');
const currencySuffix = ' <?= h(tr('ر.ع', 'OMR')) ?>';
// Customers Logic
let customersData = <?= json_encode($customers, JSON_UNESCAPED_UNICODE) ?>;
const custInput = document.getElementById('formCustomer');
const custDropdown = document.getElementById('formCustomerDropdown');
custInput.addEventListener('input', function() {
const q = this.value.toLowerCase().trim();
custDropdown.innerHTML = '';
if (q.length < 2) {
custDropdown.classList.remove('show');
return;
}
const matches = customersData.filter(c =>
c.name.toLowerCase().includes(q) ||
(c.phone && c.phone.toLowerCase().includes(q))
).slice(0, 5);
if (matches.length > 0) {
matches.forEach(c => {
const div = document.createElement('div');
div.className = 'search-item-row';
div.innerHTML = `<strong>${c.name}</strong> ${c.phone ? '<small class="text-muted ms-2">'+c.phone+'</small>' : ''}`;
div.onclick = function() {
custInput.value = c.name + (c.phone ? ' - ' + c.phone : '');
custDropdown.classList.remove('show');
};
custDropdown.appendChild(div);
});
custDropdown.classList.add('show');
} else {
custDropdown.classList.remove('show');
}
});
document.addEventListener('click', function(e) {
if (!custInput.contains(e.target) && !custDropdown.contains(e.target)) {
custDropdown.classList.remove('show');
}
});
let newCustomerModalObj = null;
function openNewCustomerModal() {
if (!newCustomerModalObj) {
newCustomerModalObj = new bootstrap.Modal(document.getElementById('newCustomerModal'));
}
document.getElementById('ncName').value = '';
document.getElementById('ncPhone').value = '';
newCustomerModalObj.show();
}
async function saveNewCustomer() {
const name = document.getElementById('ncName').value.trim();
const phone = document.getElementById('ncPhone').value.trim();
if (!name) {
alert('<?= h(tr('الاسم مطلوب', 'Name is required')) ?>');
return;
}
const formData = new FormData();
formData.append('name', name);
formData.append('phone', phone);
try {
const res = await fetch('api/customers.php', {
method: 'POST',
body: formData
});
const data = await res.json();
if (data.success) {
customersData.push(data.customer);
custInput.value = data.customer.name + (data.customer.phone ? ' - ' + data.customer.phone : '');
newCustomerModalObj.hide();
const Toast = Swal.mixin({ toast: true, position: 'top-end', showConfirmButton: false, timer: 2000 });
Toast.fire({ icon: 'success', title: '<?= h(tr('تم إضافة العميل', 'Customer added')) ?>' });
} else {
alert(data.error);
}
} catch(err) {
alert('Error saving customer');
}
}
// Search logic
searchInput.addEventListener('input', function() {
const q = this.value.toLowerCase().trim();
dropdown.innerHTML = '';
if (q === '') {
dropdown.classList.remove('show');
return;
}
const matches = catalogArray.filter(item => {
const nameAr = (item.name_ar || '').toLowerCase();
const nameEn = (item.name_en || '').toLowerCase();
const sku = (item.sku || '').toLowerCase();
return nameAr.includes(q) || nameEn.includes(q) || sku.includes(q);
}).slice(0, 6);
if (matches.length > 0) {
matches.forEach(item => {
const div = document.createElement('div');
div.className = 'search-item-row d-flex justify-content-between align-items-center';
const name = '<?= current_lang() ?>' === 'ar' ? item.name_ar : item.name_en;
div.innerHTML = `
<div>
<div class="fw-medium text-dark">${name}</div>
<div class="text-muted small">SKU: ${item.sku}</div>
</div>
<div class="fw-semibold text-primary">${parseFloat(item.price).toFixed(3)}</div>
`;
div.onclick = () => {
addItemToInvoice(item.sku);
searchInput.value = '';
dropdown.classList.remove('show');
searchInput.focus();
};
dropdown.appendChild(div);
});
dropdown.classList.add('show');
} else {
dropdown.classList.remove('show');
}
});
// Barcode scanner integration on enter
searchInput.addEventListener('keypress', function(e) {
if (e.key === 'Enter') {
e.preventDefault();
const q = this.value.trim();
if(q === '') return;
const match = catalogArray.find(item => item.sku === q);
if (match) {
addItemToInvoice(match.sku);
searchInput.value = '';
dropdown.classList.remove('show');
}
}
});
document.addEventListener('click', function(e) {
if (!searchInput.contains(e.target) && !dropdown.contains(e.target)) {
dropdown.classList.remove('show');
}
});
function addItemToInvoice(sku) {
if (invoiceItems[sku]) {
invoiceItems[sku].qty += 1;
} else {
const item = catalogData[sku];
invoiceItems[sku] = {
sku: sku,
name: '<?= current_lang() ?>' === 'ar' ? item.name_ar : item.name_en,
price: parseFloat(item.price),
qty: 1
};
}
renderInvoice();
}
function changeQty(sku, newQty) {
const qty = parseInt(newQty);
if (isNaN(qty) || qty < 1) {
delete invoiceItems[sku];
} else {
invoiceItems[sku].qty = qty;
}
renderInvoice();
}
function removeItem(sku) {
delete invoiceItems[sku];
renderInvoice();
}
function renderInvoice() {
const skus = Object.keys(invoiceItems);
if (skus.length === 0) {
tbody.innerHTML = '';
tbody.appendChild(emptyRow);
updateTotals(0);
cartJson.value = '[]';
return;
}
tbody.innerHTML = '';
let totalAmount = 0;
const cartData = [];
skus.forEach(sku => {
const item = invoiceItems[sku];
const lineTotal = item.qty * item.price;
totalAmount += lineTotal;
cartData.push({ sku: item.sku, qty: item.qty });
const tr = document.createElement('tr');
tr.innerHTML = `
<td>
<div class="fw-medium text-dark">${item.name}</div>
<div class="text-muted small">SKU: ${item.sku}</div>
</td>
<td class="text-center text-muted align-middle">${item.price.toFixed(3)}</td>
<td class="text-center align-middle">
<input type="number" class="qty-control mx-auto fw-medium" min="1" value="${item.qty}" onchange="changeQty('${sku}', this.value)" onkeyup="if(event.key==='Enter') changeQty('${sku}', this.value)">
</td>
<td class="text-center fw-semibold text-dark align-middle">${lineTotal.toFixed(3)}</td>
<td class="text-center align-middle">
<button type="button" class="btn-remove mx-auto" onclick="removeItem('${sku}')" title="<?= h(tr('إزالة', 'Remove')) ?>">
<i class="bi bi-trash"></i>
</button>
</td>
`;
tbody.appendChild(tr);
});
updateTotals(totalAmount);
cartJson.value = JSON.stringify(cartData);
}
function updateTotals(total) {
document.getElementById('displaySubtotal').innerText = total.toFixed(3);
document.getElementById('displayTotal').innerText = total.toFixed(3) + currencySuffix;
}
renderInvoice();
// Intercept form submission to check if items exist
document.getElementById('smart-sale-form').addEventListener('submit', function(e) {
if (Object.keys(invoiceItems).length === 0) {
e.preventDefault();
alert('<?= h(tr('الرجاء إضافة أصناف للفاتورة أولاً.', 'Please add items to the invoice first.')) ?>');
}
});
</script>
<?php require __DIR__ . '/includes/footer.php'; ?>

View File

@ -9,9 +9,35 @@ require_once __DIR__ . '/../db/config.php';
date_default_timezone_set('UTC');
function get_settings(): array
{
static $settings = null;
if ($settings === null) {
$pdo = db();
try {
$stmt = $pdo->query("SELECT setting_key, setting_value FROM settings");
$settings = [];
while ($row = $stmt->fetch()) {
$settings[$row['setting_key']] = $row['setting_value'];
}
} catch (Exception $e) {
$settings = [];
}
}
return $settings;
}
function get_setting(string $key, $default = '')
{
$settings = get_settings();
return $settings[$key] ?? $default;
}
function app_name(): string
{
return 'حلوى الريامي | Al Riyami Sweets';
return get_setting('company_name_ar', 'حلوى الريامي') . ' | ' . get_setting('company_name_en', 'Al Riyami Sweets');
}
function current_lang(): string
@ -195,7 +221,7 @@ function catalog(): array
{
try {
$db = db();
$stmt = $db->query("SELECT * FROM items");
$stmt = $db->query("SELECT items.*, units.name_ar as u_name_ar, units.name_en as u_name_en FROM items LEFT JOIN units ON items.unit_id = units.id");
$items = $stmt->fetchAll(PDO::FETCH_ASSOC);
$catalog = [];
foreach ($items as $item) {
@ -209,8 +235,9 @@ function catalog(): array
"category_id" => $item["category_id"],
"supplier_id" => $item["supplier_id"],
"image_url" => $item["image_url"],
"unit_ar" => "قطعة",
"unit_en" => "pcs"
"unit_id" => $item["unit_id"],
"unit_ar" => $item["u_name_ar"] ?? "قطعة",
"unit_en" => $item["u_name_en"] ?? "pcs"
];
}
return $catalog;

View File

@ -9,6 +9,7 @@ $isPublic = !isset($user) || !$user;
</div> <!-- /#page-content-wrapper -->
</div> <!-- /#wrapper -->
<?php endif; ?>
<?php require_once __DIR__ . "/footer_settings.php"; ?>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js" crossorigin="anonymous"></script>
<script>

View File

@ -0,0 +1,65 @@
<?php if (isset($user) && $user && in_array($user['role'], ['owner', 'manager'])): ?>
<!-- Settings Modal -->
<div class="modal fade" id="settingsModal" tabindex="-1" aria-hidden="true">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<form action="api/settings.php" method="POST" enctype="multipart/form-data">
<div class="modal-header">
<h5 class="modal-title"><?= h(tr('إعدادات الشركة', 'Company Settings')) ?></h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div class="row g-3">
<div class="col-md-6">
<label class="form-label"><?= h(tr('اسم الشركة (عربي)', 'Company Name (AR)')) ?></label>
<input type="text" class="form-control" name="company_name_ar" value="<?= h(get_setting('company_name_ar')) ?>" required>
</div>
<div class="col-md-6">
<label class="form-label"><?= h(tr('اسم الشركة (إنجليزي)', 'Company Name (EN)')) ?></label>
<input type="text" class="form-control" name="company_name_en" value="<?= h(get_setting('company_name_en')) ?>" required>
</div>
<div class="col-md-6">
<label class="form-label"><?= h(tr('النسبة الضريبية %', 'VAT Percentage %')) ?></label>
<input type="number" step="0.01" class="form-control" name="vat_percentage" value="<?= h(get_setting('vat_percentage', 5)) ?>" required>
</div>
<div class="col-md-6">
<label class="form-label"><?= h(tr('الرقم الضريبي', 'VAT Number')) ?></label>
<input type="text" class="form-control" name="company_vat_number" value="<?= h(get_setting('company_vat_number')) ?>">
</div>
<div class="col-md-6">
<label class="form-label"><?= h(tr('رقم الهاتف', 'Phone Number')) ?></label>
<input type="text" class="form-control" name="company_phone" value="<?= h(get_setting('company_phone')) ?>">
</div>
<div class="col-md-6">
<label class="form-label"><?= h(tr('البريد الإلكتروني', 'Email')) ?></label>
<input type="email" class="form-control" name="company_email" value="<?= h(get_setting('company_email')) ?>">
</div>
<div class="col-md-12">
<label class="form-label"><?= h(tr('العنوان', 'Address')) ?></label>
<textarea class="form-control" name="company_address" rows="2"><?= h(get_setting('company_address')) ?></textarea>
</div>
<div class="col-md-6">
<label class="form-label"><?= h(tr('الشعار (Logo)', 'Logo')) ?></label>
<input type="file" class="form-control" name="company_logo" accept="image/*">
<?php if (get_setting('company_logo')): ?>
<div class="mt-2"><img src="<?= h(get_setting('company_logo')) ?>" height="50" style="background: #f8f9fa; padding: 5px; border-radius: 4px;"></div>
<?php endif; ?>
</div>
<div class="col-md-6">
<label class="form-label"><?= h(tr('الأيقونة (Favicon)', 'Favicon')) ?></label>
<input type="file" class="form-control" name="company_favicon" accept="image/x-icon,image/png,image/jpeg">
<?php if (get_setting('company_favicon')): ?>
<div class="mt-2"><img src="<?= h(get_setting('company_favicon')) ?>" height="32" style="background: #f8f9fa; padding: 2px; border-radius: 4px;"></div>
<?php endif; ?>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal"><?= h(tr('إلغاء', 'Cancel')) ?></button>
<button type="submit" class="btn btn-primary"><?= h(tr('حفظ التغييرات', 'Save Changes')) ?></button>
</div>
</form>
</div>
</div>
</div>
<?php endif; ?>

View File

@ -32,7 +32,12 @@ $isPublic = !isset($user) || !$user;
<?php endif; ?>
<meta name="theme-color" content="#343a40" />
<?php if (get_setting('company_favicon')): ?>
<link rel="icon" href="<?= h(get_setting('company_favicon')) ?>">
<?php endif; ?>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" crossorigin="anonymous">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css">
<link rel="stylesheet" href="assets/css/custom.css?v=<?= h($assetVersion) ?>">
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
@ -47,8 +52,13 @@ $isPublic = !isset($user) || !$user;
<div class="d-flex" id="wrapper">
<!-- Sidebar -->
<div class="border-end bg-dark text-white shadow-sm" id="sidebar-wrapper">
<div class="sidebar-heading text-center py-4 fs-5 fw-bold text-uppercase border-bottom border-secondary">
<i class="bi bi-shop me-2"></i><?= h(tr('حلوى الريامي', 'Al Riyami Sweets')) ?>
<div class="sidebar-heading text-center py-4 fs-5 fw-bold text-uppercase border-bottom border-secondary d-flex flex-column align-items-center">
<?php if (get_setting('company_logo')): ?>
<img src="<?= h(get_setting('company_logo')) ?>" alt="Logo" style="max-height: 50px; margin-bottom: 10px; background: white; padding: 5px; border-radius: 5px;">
<?php else: ?>
<i class="bi bi-shop me-2 fs-2 mb-2"></i>
<?php endif; ?>
<span><?= h(current_lang() === 'ar' ? get_setting('company_name_ar', 'حلوى الريامي') : get_setting('company_name_en', 'Al Riyami Sweets')) ?></span>
</div>
<div class="p-3 text-center border-bottom border-secondary">
@ -70,13 +80,13 @@ $isPublic = !isset($user) || !$user;
<i class="bi bi-journal-text"></i> <?= h(tr('المبيعات', 'Sales')) ?>
</a>
<a class="list-group-item list-group-item-action <?= in_array($activeNav, ['stock', 'categories']) ? '' : 'collapsed' ?>" data-bs-toggle="collapse" href="#collapseStock" role="button" aria-expanded="<?= in_array($activeNav, ['stock', 'categories']) ? 'true' : 'false' ?>" aria-controls="collapseStock">
<a class="list-group-item list-group-item-action <?= in_array($activeNav, ['stock', 'categories', 'units']) ? '' : 'collapsed' ?>" data-bs-toggle="collapse" href="#collapseStock" role="button" aria-expanded="<?= in_array($activeNav, ['stock', 'categories', 'units']) ? 'true' : 'false' ?>" aria-controls="collapseStock">
<div class="d-flex justify-content-between align-items-center w-100">
<span><i class="bi bi-box-seam"></i> <?= h(tr('المخزون', 'Inventory')) ?></span>
<i class="bi bi-chevron-down toggle-icon" style="transition: transform 0.2s;"></i>
</div>
</a>
<div class="collapse <?= in_array($activeNav, ['stock', 'categories']) ? 'show' : '' ?>" id="collapseStock">
<div class="collapse <?= in_array($activeNav, ['stock', 'categories', 'units']) ? 'show' : '' ?>" id="collapseStock">
<div class="list-group list-group-flush" style="background-color: rgba(0,0,0,0.15);">
<a class="list-group-item list-group-item-action <?= $activeNav === 'stock' ? 'active' : '' ?>" href="<?= h(url_for('stock.php')) ?>" style="padding-left: 2.5rem; padding-right: 2.5rem;">
<i class="bi bi-dot"></i> <?= h(tr('قائمة الأصناف', 'Items List')) ?>
@ -84,6 +94,9 @@ $isPublic = !isset($user) || !$user;
<a class="list-group-item list-group-item-action <?= $activeNav === 'categories' ? 'active' : '' ?>" href="<?= h(url_for('categories.php')) ?>" style="padding-left: 2.5rem; padding-right: 2.5rem;">
<i class="bi bi-dot"></i> <?= h(tr('التصنيفات', 'Categories')) ?>
</a>
<a class="list-group-item list-group-item-action <?= $activeNav === 'units' ? 'active' : '' ?>" href="<?= h(url_for('units.php')) ?>" style="padding-left: 2.5rem; padding-right: 2.5rem;">
<i class="bi bi-dot"></i> <?= h(tr('الوحدات', 'Units')) ?>
</a>
</div>
</div>
@ -106,6 +119,12 @@ $isPublic = !isset($user) || !$user;
<i class="bi bi-people"></i> <?= h(tr('المستخدمون والأدوار', 'Users & Roles')) ?>
</a>
<?php endif; ?>
<?php if ($user && in_array($user['role'], ['owner', 'manager'])): ?>
<a class="list-group-item list-group-item-action" href="#" data-bs-toggle="modal" data-bs-target="#settingsModal">
<i class="bi bi-gear"></i> <?= h(tr('إعدادات الشركة', 'Company Settings')) ?>
</a>
<?php endif; ?>
</div>
</div>
<!-- /#sidebar-wrapper -->

View File

@ -360,7 +360,7 @@ require __DIR__ . '/header.php';
<span id="displaySubtotal" class="fw-medium">0.000</span>
</div>
<div class="totals-row">
<span><?= h(tr('الضريبة (15%)', 'VAT (15%)')) ?></span>
<span><?= h(tr('الضريبة (' . get_setting('vat_percentage', 5) . '%)', 'VAT (' . get_setting('vat_percentage', 5) . '%)')) ?></span>
<span class="text-success small"><?= h(tr('مشمولة', 'Included')) ?></span>
</div>
<div class="totals-row grand-total">

251
login.php
View File

@ -6,7 +6,18 @@ if (current_user()) {
}
$error = '';
$flash = pull_flash();
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
if (isset($_POST['action']) && $_POST['action'] === 'reset_password') {
$reset_username = trim((string) ($_POST['reset_username'] ?? ''));
if ($reset_username !== '') {
// Mock sending reset link
set_flash('success', tr('تم إرسال رابط إعادة تعيين كلمة المرور إلى بريدك الإلكتروني (تجريبي).', 'Password reset link has been sent to your email (Demo).'));
}
redirect_to('login.php');
}
$username = trim((string) ($_POST['username'] ?? ''));
$password = trim((string) ($_POST['password'] ?? ''));
@ -37,74 +48,202 @@ $accounts = demo_users();
<meta property="og:description" content="<?= h($projectDescription) ?>" />
<meta property="twitter:description" content="<?= h($projectDescription) ?>" />
<?php else: ?>
<meta name="description" content="<?= h(tr('تسجيل الدخول إلى مساحة مبيعات حلوى الريامي متعددة الفروع.', 'Sign in to the multi-branch Al Riyami Sweets sales workspace.')) ?>" />
<meta name="description" content="<?= h(tr('تسجيل الدخول إلى مساحة المبيعات.', 'Sign in to the sales workspace.')) ?>" />
<?php endif; ?>
<?php if ($projectImageUrl): ?>
<meta property="og:image" content="<?= h($projectImageUrl) ?>" />
<meta property="twitter:image" content="<?= h($projectImageUrl) ?>" />
<?php endif; ?>
<?php if (get_setting('company_favicon')): ?>
<link rel="icon" href="<?= h(get_setting('company_favicon')) ?>">
<?php endif; ?>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
<link rel="stylesheet" href="assets/css/custom.css?v=<?= h($assetVersion) ?>">
<style>
body {
background-color: #f8f9fa;
display: flex;
align-items: center;
min-height: 100vh;
padding: 2rem 0;
}
.auth-card {
border-radius: 20px;
box-shadow: 0 20px 40px rgba(0,0,0,0.08);
background: #fff;
overflow: hidden;
border: none;
}
.auth-sidebar {
background: linear-gradient(135deg, #0d6efd 0%, #0a58ca 100%);
color: white;
padding: 3rem;
display: flex;
flex-direction: column;
justify-content: space-between;
}
.auth-form-container {
padding: 4rem 3rem;
}
.company-logo {
max-height: 90px;
border-radius: 12px;
margin-bottom: 1.5rem;
box-shadow: 0 4px 12px rgba(0,0,0,0.05);
padding: 10px;
background: #fff;
}
.demo-account {
border-radius: 12px;
transition: all 0.2s;
}
.demo-account:hover {
background-color: #f8f9fa;
transform: translateY(-2px);
border-color: #dee2e6 !important;
}
.stat-chip {
background: rgba(255,255,255,0.1);
border: 1px solid rgba(255,255,255,0.2);
}
</style>
</head>
<body class="auth-body">
<main class="auth-shell container-fluid">
<div class="row g-4 align-items-stretch justify-content-center">
<div class="col-lg-5">
<section class="auth-panel h-100">
<div class="eyebrow mb-3"><?= h(tr('MVP جاهز للاستخدام', 'MVP ready to use')) ?></div>
<h1 class="auth-title"><?= h(tr('حلوى الريامي', 'Al Riyami Sweets')) ?></h1>
<p class="auth-subtitle"><?= h(tr('تسجيل دخول ثنائي اللغة مع أدوار منفصلة للمالك ومدير الفرع والكاشير.', 'Bilingual role-based access for owner, branch manager, and cashier.')) ?></p>
<div class="mini-grid mt-4">
<div class="stat-chip"><strong>3</strong><span><?= h(tr('أدوار', 'roles')) ?></span></div>
<div class="stat-chip"><strong>3</strong><span><?= h(tr('فروع', 'branches')) ?></span></div>
<div class="stat-chip"><strong>2</strong><span><?= h(tr('لغات', 'languages')) ?></span></div>
</div>
<div class="alert alert-light border mt-4 mb-0">
<div class="fw-semibold mb-1"><?= h(tr('أول قيمة عملية', 'First practical value')) ?></div>
<div class="small text-muted"><?= h(tr('ابدأ ببيع POS سريع، ثم راجع المبيعات والمخزون والتقارير من نفس الواجهة.', 'Start with a fast POS sale, then review sales, stock, and reports from one workspace.')) ?></div>
</div>
</section>
</div>
<div class="col-lg-4">
<section class="auth-panel h-100">
<div class="d-flex justify-content-between align-items-center mb-3">
<h2 class="h4 mb-0"><?= h(tr('تسجيل الدخول', 'Sign in')) ?></h2>
<div class="language-switcher">
<a class="btn btn-sm <?= current_lang() === 'ar' ? 'btn-dark' : 'btn-outline-secondary' ?>" href="<?= h(url_for('login.php', ['lang' => 'ar'])) ?>">AR</a>
<a class="btn btn-sm <?= current_lang() === 'en' ? 'btn-dark' : 'btn-outline-secondary' ?>" href="<?= h(url_for('login.php', ['lang' => 'en'])) ?>">EN</a>
<body>
<main class="container">
<div class="row justify-content-center">
<div class="col-xl-10">
<div class="card auth-card">
<div class="row g-0 align-items-stretch">
<div class="col-lg-5 auth-sidebar d-none d-lg-flex">
<div>
<div class="eyebrow text-white-50 mb-3"><?= h(tr('مرحباً بك مجدداً', 'Welcome Back')) ?></div>
<h2 class="display-6 fw-bold mb-4"><?= h(current_lang() === 'ar' ? get_setting('company_name_ar', 'نظام إدارة') : get_setting('company_name_en', 'Management System')) ?></h2>
<p class="lead opacity-75"><?= h(tr('نظام متكامل لتسجيل المبيعات، وإدارة المخزون، والتقارير في واجهة واحدة.', 'Integrated system for logging sales, managing inventory, and reports in one interface.')) ?></p>
<div class="mini-grid mt-5 gap-3 d-flex flex-wrap">
<div class="stat-chip text-white px-3 py-2 rounded"><strong>3</strong> <span><?= h(tr('أدوار', 'Roles')) ?></span></div>
<div class="stat-chip text-white px-3 py-2 rounded"><strong>3</strong> <span><?= h(tr('فروع', 'Branches')) ?></span></div>
<div class="stat-chip text-white px-3 py-2 rounded"><strong>2</strong> <span><?= h(tr('لغات', 'Languages')) ?></span></div>
</div>
</div>
<div class="mt-5 pt-5 border-top border-light border-opacity-25 text-white-50 small">
&copy; <?= date('Y') ?> <?= h($projectName) ?>
</div>
</div>
<div class="col-lg-7 auth-form-container">
<div class="d-flex justify-content-between align-items-center mb-4">
<div class="language-switcher">
<a class="btn btn-sm <?= current_lang() === 'ar' ? 'btn-primary' : 'btn-light text-dark' ?> rounded-pill px-3" href="<?= h(url_for('login.php', ['lang' => 'ar'])) ?>">AR</a>
<a class="btn btn-sm <?= current_lang() === 'en' ? 'btn-primary' : 'btn-light text-dark' ?> rounded-pill px-3" href="<?= h(url_for('login.php', ['lang' => 'en'])) ?>">EN</a>
</div>
</div>
<div class="text-center mb-5">
<?php if (get_setting('company_logo')): ?>
<img src="<?= h(get_setting('company_logo')) ?>" alt="Logo" class="company-logo">
<?php else: ?>
<div class="company-logo mx-auto bg-primary bg-opacity-10 text-primary d-flex align-items-center justify-content-center" style="width: 90px; height: 90px; font-size: 2.5rem; font-weight: bold;">
<?= h(mb_substr(current_lang() === 'ar' ? get_setting('company_name_ar', 'نظام') : get_setting('company_name_en', 'System'), 0, 1)) ?>
</div>
<?php endif; ?>
<h3 class="fw-bold mb-1"><?= h(tr('تسجيل الدخول', 'Sign in to your account')) ?></h3>
<p class="text-muted"><?= h(tr('أدخل بيانات الاعتماد الخاصة بك للوصول', 'Enter your credentials to access your account')) ?></p>
</div>
<?php if ($flash): ?>
<div class="alert alert-<?= h($flash['type'] === 'error' ? 'danger' : $flash['type']) ?> alert-dismissible fade show rounded-3" role="alert">
<?= h($flash['message']) ?>
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
<?php endif; ?>
<?php if ($error !== ''): ?>
<div class="alert alert-danger alert-dismissible fade show rounded-3" role="alert">
<?= h($error) ?>
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
<?php endif; ?>
<form method="post" class="mb-5">
<div class="mb-3">
<label class="form-label fw-semibold" for="username"><?= h(tr('اسم المستخدم', 'Username')) ?></label>
<input id="username" name="username" class="form-control form-control-lg rounded-3 bg-light border-0" autocomplete="username" required>
</div>
<div class="mb-4">
<div class="d-flex justify-content-between align-items-center mb-1">
<label class="form-label fw-semibold mb-0" for="password"><?= h(tr('كلمة المرور', 'Password')) ?></label>
<a href="#" class="text-decoration-none small text-primary fw-medium" data-bs-toggle="modal" data-bs-target="#resetPasswordModal"><?= h(tr('نسيت كلمة المرور؟', 'Forgot password?')) ?></a>
</div>
<input id="password" name="password" type="password" class="form-control form-control-lg rounded-3 bg-light border-0" autocomplete="current-password" required>
</div>
<button class="btn btn-primary btn-lg w-100 rounded-3 shadow-sm fw-semibold" type="submit"><?= h(tr('دخول', 'Sign in')) ?></button>
</form>
<div class="position-relative mb-4">
<hr class="text-muted opacity-25">
<span class="position-absolute top-50 start-50 translate-middle bg-white px-3 small text-muted fw-medium"><?= h(tr('حسابات تجريبية سريعة', 'Quick demo access')) ?></span>
</div>
<div class="row g-2">
<?php foreach ($accounts as $account): ?>
<div class="col-12 col-md-4">
<button type="button"
class="btn btn-outline-secondary border-1 w-100 p-3 text-start demo-account h-100"
data-username="<?= h($account['username']) ?>"
data-password="<?= h($account['password']) ?>">
<div class="fw-bold mb-1 text-dark text-truncate"><?= h(current_lang() === 'ar' ? $account['name_ar'] : $account['name_en']) ?></div>
<div class="small text-muted" style="font-size: 0.75rem;"><?= h(role_label($account['role'])) ?></div>
</button>
</div>
<?php endforeach; ?>
</div>
</div>
</div>
<?php if ($error !== ''): ?>
<div class="alert alert-warning"><?= h($error) ?></div>
<?php endif; ?>
<form method="post" class="d-grid gap-3">
<div>
<label class="form-label" for="username"><?= h(tr('اسم المستخدم', 'Username')) ?></label>
<input id="username" name="username" class="form-control form-control-lg" autocomplete="username" required>
</div>
<div>
<label class="form-label" for="password"><?= h(tr('كلمة المرور', 'Password')) ?></label>
<input id="password" name="password" type="password" class="form-control form-control-lg" autocomplete="current-password" required>
</div>
<button class="btn btn-dark btn-lg" type="submit"><?= h(tr('دخول إلى التطبيق', 'Enter app')) ?></button>
</form>
<div class="divider-label"><?= h(tr('حسابات تجريبية', 'Demo accounts')) ?></div>
<div class="d-grid gap-2">
<?php foreach ($accounts as $account): ?>
<button type="button"
class="btn btn-outline-secondary text-start demo-account"
data-username="<?= h($account['username']) ?>"
data-password="<?= h($account['password']) ?>">
<div class="fw-semibold"><?= h(current_lang() === 'ar' ? $account['name_ar'] : $account['name_en']) ?></div>
<div class="small text-muted"><?= h(role_label($account['role'])) ?> · <?= h(branch_label($account['branch_code'])) ?></div>
</button>
<?php endforeach; ?>
</div>
</section>
</div>
</div>
</div>
</main>
<!-- Reset Password Modal -->
<div class="modal fade" id="resetPasswordModal" tabindex="-1" aria-labelledby="resetPasswordModalLabel" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content border-0 shadow-lg rounded-4">
<div class="modal-header border-bottom-0 pb-0 pt-4 px-4">
<h5 class="modal-title fw-bold" id="resetPasswordModalLabel"><?= h(tr('استعادة كلمة المرور', 'Reset Password')) ?></h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<form method="post">
<input type="hidden" name="action" value="reset_password">
<div class="modal-body px-4 py-4">
<p class="text-muted mb-4"><?= h(tr('أدخل اسم المستخدم أو البريد الإلكتروني وسنقوم بإرسال رابط لإعادة تعيين كلمة المرور الخاصة بك.', 'Enter your username or email and we will send you a link to reset your password.')) ?></p>
<div class="mb-3">
<label for="reset_username" class="form-label fw-semibold"><?= h(tr('البريد الإلكتروني / اسم المستخدم', 'Email / Username')) ?></label>
<input type="text" class="form-control form-control-lg bg-light border-0 rounded-3" id="reset_username" name="reset_username" required>
</div>
</div>
<div class="modal-footer border-top-0 pt-0 pb-4 px-4">
<button type="button" class="btn btn-light rounded-3 px-4" data-bs-dismiss="modal"><?= h(tr('إلغاء', 'Cancel')) ?></button>
<button type="submit" class="btn btn-primary rounded-3 px-4 fw-semibold"><?= h(tr('إرسال الرابط', 'Send Link')) ?></button>
</div>
</form>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" crossorigin="anonymous"></script>
<script src="assets/js/main.js?v=<?= h($assetVersion) ?>"></script>
<script>
document.addEventListener('DOMContentLoaded', () => {
document.querySelectorAll('.demo-account').forEach(btn => {
btn.addEventListener('click', () => {
document.getElementById('username').value = btn.dataset.username;
document.getElementById('password').value = btn.dataset.password;
btn.closest('form')?.submit() || document.querySelector('form').submit();
});
});
});
</script>
</body>
</html>
</html>

View File

@ -17,9 +17,9 @@ if (!$sale) {
}
// Receipt Configuration
$storeName = tr('متجر فلات لوجيك', 'Flatlogic Store');
$storeAddress = tr('شارع الملك فهد، الرياض، السعودية', 'King Fahd Road, Riyadh, KSA');
$vatNo = '300123456789012';
$storeName = current_lang() === 'ar' ? get_setting('company_name_ar', 'حلوى الريامي') : get_setting('company_name_en', 'Al Riyami Sweets');
$storeAddress = get_setting('company_address', '');
$vatNo = get_setting('company_vat_number', '300123456789012');
$registerNo = 'REG-01';
?>
<!DOCTYPE html>
@ -173,10 +173,14 @@ $registerNo = 'REG-01';
<!-- Logo -->
<div class="text-center logo-area">
<!-- SVG Placeholder Logo -->
<svg width="60" height="60" viewBox="0 0 100 100" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="100" height="100" rx="20" fill="#000"/>
<path d="M50 20 L80 80 L20 80 Z" fill="#fff"/>
</svg>
<?php if (get_setting('company_logo')): ?>
<img src="<?= h(get_setting('company_logo')) ?>" alt="Logo" style="max-height: 80px; max-width: 150px;">
<?php else: ?>
<svg width="60" height="60" viewBox="0 0 100 100" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="100" height="100" rx="20" fill="#000"/>
<path d="M50 20 L80 80 L20 80 Z" fill="#fff"/>
</svg>
<?php endif; ?>
</div>
<!-- Store Info -->
@ -184,7 +188,7 @@ $registerNo = 'REG-01';
<div class="font-bold" style="font-size: 16px;"><?= h($storeName) ?></div>
<div><?= h($storeAddress) ?></div>
<div>VAT: <?= h($vatNo) ?></div>
<div><?= h(tr('هاتف', 'Tel')) ?>: 920000000</div>
<div><?= h(tr('هاتف', 'Tel')) ?>: <?= h(get_setting('company_phone', '')) ?></div>
</div>
<div class="divider"></div>
@ -244,7 +248,7 @@ $registerNo = 'REG-01';
<span><?= number_format((float)$sale['subtotal'], 3) ?></span>
</div>
<div class="totals-row">
<span><?= h(tr('ضريبة القيمة المضافة (15%)', 'VAT (15%)')) ?></span>
<span><?= h(tr('ضريبة القيمة المضافة (' . get_setting('vat_percentage', 5) . '%)', 'VAT (' . get_setting('vat_percentage', 5) . '%)')) ?></span>
<span><?= h(tr('شامل', 'Inclusive')) ?></span>
</div>
<div class="totals-row grand-total">

View File

@ -15,11 +15,11 @@ if ($id > 0) {
}
// Company Info for Invoice
$companyName = tr('متجر فلات لوجيك', 'Flatlogic Store');
$companyAddress = tr('شارع الملك فهد، الرياض، المملكة العربية السعودية', 'King Fahd Road, Riyadh, KSA');
$companyVat = '300123456789012';
$companyName = current_lang() === 'ar' ? get_setting('company_name_ar', 'حلوى الريامي') : get_setting('company_name_en', 'Al Riyami Sweets');
$companyAddress = get_setting('company_address', '');
$companyVat = get_setting('company_vat_number', '300123456789012');
$companyEmail = 'info@flatlogic.com';
$companyPhone = '920000000';
$companyPhone = '<?= h(get_setting('company_phone', '')) ?>';
require __DIR__ . '/includes/header.php';
?>
@ -319,9 +319,14 @@ require __DIR__ . '/includes/header.php';
<a href="sales.php" class="btn btn-outline-secondary btn-sm">
<i class="bi bi-arrow-<?= current_lang() === 'ar' ? 'right' : 'left' ?> me-1"></i> <?= h(tr('رجوع للسجل', 'Back to ledger')) ?>
</a>
<button onclick="window.print()" class="btn btn-dark btn-sm px-4">
<i class="bi bi-printer me-2"></i><?= h(tr('طباعة الفاتورة', 'Print Invoice')) ?>
</button>
<div>
<a href="<?= h(url_for('edit_sale.php', ['id' => $sale['id']])) ?>" class="btn btn-outline-primary btn-sm me-2">
<i class="bi bi-pencil me-1"></i><?= h(tr('تعديل الفاتورة', 'Edit Invoice')) ?>
</a>
<button onclick="window.print()" class="btn btn-dark btn-sm px-4">
<i class="bi bi-printer me-2"></i><?= h(tr('طباعة الفاتورة', 'Print Invoice')) ?>
</button>
</div>
</div>
<!-- Formal A4 Invoice -->
@ -419,7 +424,7 @@ require __DIR__ . '/includes/header.php';
<td class="total-amount"><?= h(number_format((float) $sale['subtotal'], 3)) ?></td>
</tr>
<tr>
<td class="total-label"><?= h(tr('ضريبة القيمة المضافة (15%)', 'VAT (15%)')) ?></td>
<td class="total-label"><?= h(tr('ضريبة القيمة المضافة (' . get_setting('vat_percentage', 5) . '%)', 'VAT (' . get_setting('vat_percentage', 5) . '%)')) ?></td>
<td class="total-amount"><?= h(tr('شامل', 'Inclusive')) ?></td>
</tr>
<tr class="grand-total-row">

View File

@ -165,9 +165,9 @@ require __DIR__ . '/includes/header.php';
<a class="btn btn-sm btn-light text-primary border me-1" href="<?= h(url_for('sale.php', ['id' => $sale['id']])) ?>" title="<?= h(tr('تفاصيل', 'Detail')) ?>">
<i class="bi bi-eye"></i>
</a>
<button class="btn btn-sm btn-outline-secondary rounded-circle shadow-sm ms-1" style="width: 34px; height: 34px; padding: 0;" onclick="mockEdit()" title="<?= h(tr('تعديل', 'Edit')) ?>">
<a class="btn btn-sm btn-outline-secondary rounded-circle shadow-sm ms-1" style="width: 34px; height: 34px; padding: 0; line-height: 32px; text-align: center;" href="<?= h(url_for('edit_sale.php', ['id' => $sale['id']])) ?>" title="<?= h(tr('تعديل', 'Edit')) ?>">
<i class="bi bi-pencil"></i>
</button>
</a>
<button class="btn btn-sm btn-outline-danger rounded-circle shadow-sm ms-1" style="width: 34px; height: 34px; padding: 0;" onclick="mockDelete()" title="<?= h(tr('حذف', 'Delete')) ?>">
<i class="bi bi-trash"></i>
</button>

View File

@ -16,9 +16,10 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action'])) {
$name = $_POST['name'] ?? '';
$price = (float)($_POST['price'] ?? 0);
$base_stock = (int)($_POST['base_stock'] ?? 0);
$vat = (float)($_POST['vat'] ?? 5);
$vat = (float)($_POST['vat'] ?? get_setting('vat_percentage', 5));
$category_id = !empty($_POST['category_id']) ? (int)$_POST['category_id'] : null;
$supplier_id = !empty($_POST['supplier_id']) ? (int)$_POST['supplier_id'] : null;
$unit_id = !empty($_POST['unit_id']) ? (int)$_POST['unit_id'] : null;
if (!$sku || !$name) {
echo json_encode(['success' => false, 'error' => 'Missing SKU or Name']);
@ -49,8 +50,8 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action'])) {
exit;
}
$sql = "UPDATE items SET sku=?, name=?, price=?, base_stock=?, vat=?, category_id=?, supplier_id=? " . ($image_url ? ", image_url=?" : "") . " WHERE sku=?";
$params = [$sku, $name, $price, $base_stock, $vat, $category_id, $supplier_id];
$sql = "UPDATE items SET sku=?, name=?, price=?, base_stock=?, vat=?, category_id=?, supplier_id=?, unit_id=? " . ($image_url ? ", image_url=?" : "") . " WHERE sku=?";
$params = [$sku, $name, $price, $base_stock, $vat, $category_id, $supplier_id, $unit_id];
if ($image_url) {
$params[] = $image_url;
}
@ -62,8 +63,8 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action'])) {
echo json_encode(['success' => false, 'error' => 'SKU already exists']);
exit;
}
$stmt = $pdo->prepare("INSERT INTO items (sku, name, price, base_stock, vat, category_id, supplier_id, image_url) VALUES (?, ?, ?, ?, ?, ?, ?, ?)");
$stmt->execute([$sku, $name, $price, $base_stock, $vat, $category_id, $supplier_id, $image_url]);
$stmt = $pdo->prepare("INSERT INTO items (sku, name, price, base_stock, vat, category_id, supplier_id, unit_id, image_url) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)");
$stmt->execute([$sku, $name, $price, $base_stock, $vat, $category_id, $supplier_id, $unit_id, $image_url]);
}
echo json_encode(['success' => true]);
@ -105,6 +106,7 @@ try {
$pdo = db();
$categories = $pdo->query('SELECT id, name_ar, name_en FROM categories ORDER BY name_ar ASC')->fetchAll();
$suppliers = $pdo->query('SELECT id, name FROM suppliers ORDER BY name ASC')->fetchAll();
$units = $pdo->query('SELECT id, name_ar, name_en FROM units ORDER BY name_ar ASC')->fetchAll();
} catch (Throwable $e) {
// Ignore if not present
}
@ -235,7 +237,7 @@ require __DIR__ . '/includes/header.php';
<?php endif; ?>
</td>
<td class="text-end pe-3">
<button class="btn btn-sm btn-outline-primary rounded-circle shadow-sm" style="width: 34px; height: 34px; padding: 0;" 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')) ?>">
<button class="btn btn-sm btn-outline-primary rounded-circle shadow-sm" style="width: 34px; height: 34px; padding: 0;" onclick="openItemModal('<?= h($row['sku']) ?>', '<?= h(addslashes($row['name'])) ?>', '<?= h($row['price']) ?>', '<?= h($row['base_stock']) ?>', '<?= h($row['vat'] ?? get_setting('vat_percentage', 5)) ?>', '<?= h($row['category_id'] ?? '') ?>', '<?= h($row['supplier_id'] ?? '') ?>', '<?= h($row['unit_id'] ?? '') ?>', '<?= h($row['image_url'] ?? '') ?>')" data-bs-toggle="tooltip" title="<?= h(tr('تعديل', 'Edit')) ?>">
<i class="bi bi-pencil"></i>
</button>
<button class="btn btn-sm btn-outline-danger rounded-circle shadow-sm ms-1" style="width: 34px; height: 34px; padding: 0;" onclick="deleteItem('<?= h(addslashes($row['sku'])) ?>')" data-bs-toggle="tooltip" title="<?= h(tr('حذف', 'Delete')) ?>">
@ -302,9 +304,18 @@ require __DIR__ . '/includes/header.php';
</div>
<div class="col-4">
<label class="form-label"><?= h(tr('الضريبة (VAT %)', 'VAT %')) ?></label>
<input type="number" step="0.001" class="form-control" id="item_vat" value="5" required>
<input type="number" step="0.001" class="form-control" id="item_vat" value="<?= h(get_setting('vat_percentage', 5)) ?>" required>
</div>
</div>
<div class="mb-3">
<label class="form-label"><?= h(tr('الوحدة', 'Unit')) ?></label>
<select class="form-select" id="item_unit">
<option value=""><?= h(tr('-- اختر الوحدة --', '-- Select Unit --')) ?></option>
<?php foreach($units as $unit): ?>
<option value="<?= h($unit['id']) ?>"><?= h(current_lang() === 'ar' ? $unit['name_ar'] : $unit['name_en']) ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="mb-3">
<label class="form-label"><?= h(tr('التصنيف', 'Category')) ?></label>
<select class="form-select" id="item_category">
@ -393,7 +404,7 @@ document.addEventListener('DOMContentLoaded', function () {
}
});
function openItemModal(sku = '', name = '', price = '', base_stock = '', vat = '5', category_id = '', supplier_id = '', image_url = '') {
function openItemModal(sku = '', name = '', price = '', base_stock = '', vat = '<?= h(get_setting('vat_percentage', 5)) ?>', category_id = '', supplier_id = '', unit_id = '', image_url = '') {
document.getElementById('item_original_sku').value = sku;
document.getElementById('item_existing_image_url').value = image_url;
document.getElementById('item_sku').value = sku;
@ -403,6 +414,7 @@ function openItemModal(sku = '', name = '', price = '', base_stock = '', vat = '
document.getElementById('item_vat').value = vat;
document.getElementById('item_category').value = category_id;
document.getElementById('item_supplier').value = supplier_id;
document.getElementById('item_unit').value = unit_id;
// Remove old image preview if any
const oldPreview = document.getElementById('image_preview');
@ -438,6 +450,7 @@ async function handleItemSubmit(e) {
formData.append('vat', document.getElementById('item_vat').value);
formData.append('category_id', document.getElementById('item_category').value);
formData.append('supplier_id', document.getElementById('item_supplier').value);
formData.append('unit_id', document.getElementById('item_unit').value);
formData.append('existing_image_url', document.getElementById('item_existing_image_url').value);
const picInput = document.getElementById('item_picture');

215
units.php Normal file
View File

@ -0,0 +1,215 @@
<?php
require_once __DIR__ . '/includes/app.php';
$user = require_auth();
$pageTitle = tr('الوحدات', 'Units');
$activeNav = 'units';
$pdo = db();
// Handle Form Submission
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$action = $_POST['action'] ?? '';
if ($action === 'create') {
$stmt = $pdo->prepare('INSERT INTO units (name_ar, name_en) VALUES (?, ?)');
$stmt->execute([$_POST['name_ar'], $_POST['name_en']]);
set_flash('success', tr('تمت إضافة الوحدة بنجاح', 'Unit added successfully'));
redirect_to('units.php');
} elseif ($action === 'edit') {
$stmt = $pdo->prepare('UPDATE units SET name_ar = ?, name_en = ? WHERE id = ?');
$stmt->execute([$_POST['name_ar'], $_POST['name_en'], $_POST['id']]);
set_flash('success', tr('تم التحديث بنجاح', 'Updated successfully'));
redirect_to('units.php');
} elseif ($action === 'delete') {
$stmt = $pdo->prepare('DELETE FROM units WHERE id = ?');
$stmt->execute([$_POST['id']]);
set_flash('success', tr('تم الحذف بنجاح', 'Deleted successfully'));
redirect_to('units.php');
}
}
// Pagination & Search
$page = max(1, (int)($_GET['p'] ?? 1));
$limit = 10;
$offset = ($page - 1) * $limit;
$search = $_GET['q'] ?? '';
$where = '1=1';
$params = [];
if ($search) {
$where .= ' AND (name_ar LIKE ? OR name_en LIKE ?)';
$params[] = "%$search%";
$params[] = "%$search%";
}
$totalStmt = $pdo->prepare("SELECT COUNT(*) FROM units WHERE $where");
$totalStmt->execute($params);
$total = $totalStmt->fetchColumn();
$totalPages = ceil($total / $limit);
$queryStmt = $pdo->prepare("SELECT * FROM units WHERE $where ORDER BY id DESC LIMIT $limit OFFSET $offset");
$queryStmt->execute($params);
$items = $queryStmt->fetchAll();
require __DIR__ . '/includes/header.php';
?>
<section class="surface-card mb-4">
<div class="d-flex justify-content-between align-items-center mb-3">
<div>
<h3 class="h5 mb-2"><i class="bi bi-rulers me-2"></i><?= h($pageTitle) ?></h3>
<p class="text-muted mb-0"><?= h(tr('إدارة وحدات القياس للأصناف', 'Manage measurement units for items')) ?></p>
</div>
<button type="button" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#addModal">
<i class="bi bi-plus-lg"></i> <?= h(tr('إضافة وحدة', 'Add Unit')) ?>
</button>
</div>
<form class="d-flex mb-3" method="GET" action="units.php">
<div class="input-group" style="max-width: 400px;">
<input type="text" name="q" class="form-control" placeholder="<?= h(tr('بحث...', 'Search...')) ?>" value="<?= h($search) ?>">
<button class="btn btn-outline-secondary" type="submit"><i class="bi bi-search"></i></button>
</div>
</form>
</section>
<section class="surface-card">
<div class="table-responsive shadow-sm" style="border-radius: 12px; overflow: hidden; border: 1px solid rgba(0,0,0,0.05);">
<table class="table table-hover align-middle mb-0 text-center" style="background-color: #fff;">
<thead style="background: linear-gradient(90deg, #0d6efd, #0dcaf0);">
<tr>
<th class="text-white border-0 py-3 fw-semibold bg-transparent">ID</th>
<th class="text-white border-0 py-3 fw-semibold bg-transparent"><?= h(tr('الاسم (عربي)', 'Name (AR)')) ?></th>
<th class="text-white border-0 py-3 fw-semibold bg-transparent"><?= h(tr('الاسم (إنجليزي)', 'Name (EN)')) ?></th>
<th class="text-white border-0 py-3 fw-semibold bg-transparent"><?= h(tr('إجراءات', 'Actions')) ?></th>
</tr>
</thead>
<tbody class="border-top-0">
<?php if(empty($items)): ?>
<tr><td colspan="4" class="text-center text-muted py-4"><?= h(tr('لا توجد بيانات', 'No data found')) ?></td></tr>
<?php endif; ?>
<?php foreach ($items as $item): ?>
<tr>
<td><?= h($item['id']) ?></td>
<td><?= h($item['name_ar']) ?></td>
<td><?= h($item['name_en']) ?></td>
<td>
<button class="btn btn-sm btn-outline-primary rounded-circle shadow-sm" style="width: 34px; height: 34px; padding: 0;" onclick="editItem(<?= htmlspecialchars(json_encode($item)) ?>)" title="<?= h(tr('تعديل', 'Edit')) ?>">
<i class="bi bi-pencil"></i>
</button>
<button class="btn btn-sm btn-outline-danger rounded-circle shadow-sm ms-1" style="width: 34px; height: 34px; padding: 0;" onclick="deleteItem(<?= $item['id'] ?>)" title="<?= h(tr('حذف', 'Delete')) ?>">
<i class="bi bi-trash"></i>
</button>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<?php if ($totalPages > 1): ?>
<nav class="mt-4">
<ul class="pagination justify-content-center mb-0">
<?php for($i=1; $i<=$totalPages; $i++): ?>
<li class="page-item <?= $i === $page ? 'active' : '' ?>">
<a class="page-link" href="<?= h(url_for('units.php', ['p' => $i, 'q' => $search])) ?>"><?= $i ?></a>
</li>
<?php endfor; ?>
</ul>
</nav>
<?php endif; ?>
</section>
<!-- Add Modal -->
<div class="modal fade" id="addModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<form method="POST" action="units.php">
<input type="hidden" name="action" value="create">
<div class="modal-header">
<h5 class="modal-title"><?= h(tr('إضافة وحدة', 'Add Unit')) ?></h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<div class="mb-3">
<label class="form-label"><?= h(tr('الاسم (عربي)', 'Name (AR)')) ?></label>
<input type="text" name="name_ar" class="form-control" required>
</div>
<div class="mb-3">
<label class="form-label"><?= h(tr('الاسم (إنجليزي)', 'Name (EN)')) ?></label>
<input type="text" name="name_en" class="form-control" required>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal"><?= h(tr('إلغاء', 'Cancel')) ?></button>
<button type="submit" class="btn btn-primary"><?= h(tr('حفظ', 'Save')) ?></button>
</div>
</form>
</div>
</div>
</div>
<!-- Edit Modal -->
<div class="modal fade" id="editModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<form method="POST" action="units.php">
<input type="hidden" name="action" value="edit">
<input type="hidden" name="id" id="edit_id">
<div class="modal-header">
<h5 class="modal-title"><?= h(tr('تعديل', 'Edit')) ?></h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<div class="mb-3">
<label class="form-label"><?= h(tr('الاسم (عربي)', 'Name (AR)')) ?></label>
<input type="text" name="name_ar" id="edit_name_ar" class="form-control" required>
</div>
<div class="mb-3">
<label class="form-label"><?= h(tr('الاسم (إنجليزي)', 'Name (EN)')) ?></label>
<input type="text" name="name_en" id="edit_name_en" class="form-control" required>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal"><?= h(tr('إلغاء', 'Cancel')) ?></button>
<button type="submit" class="btn btn-primary"><?= h(tr('حفظ', 'Save')) ?></button>
</div>
</form>
</div>
</div>
</div>
<!-- Delete Form -->
<form id="deleteForm" method="POST" action="units.php" style="display:none;">
<input type="hidden" name="action" value="delete">
<input type="hidden" name="id" id="delete_id">
</form>
<script>
function editItem(item) {
document.getElementById('edit_id').value = item.id;
document.getElementById('edit_name_ar').value = item.name_ar;
document.getElementById('edit_name_en').value = item.name_en;
new bootstrap.Modal(document.getElementById('editModal')).show();
}
function deleteItem(id) {
Swal.fire({
title: '<?= h(tr('هل أنت متأكد؟', 'Are you sure?')) ?>',
text: '<?= h(tr('لن تتمكن من التراجع عن هذا!', "You won't be able to revert this!")) ?>',
icon: 'warning',
showCancelButton: true,
confirmButtonColor: '#dc3545',
cancelButtonColor: '#6c757d',
confirmButtonText: '<?= h(tr('نعم، احذف', 'Yes, delete it!')) ?>',
cancelButtonText: '<?= h(tr('إلغاء', 'Cancel')) ?>'
}).then((result) => {
if (result.isConfirmed) {
document.getElementById('delete_id').value = id;
document.getElementById('deleteForm').submit();
}
});
}
</script>
<?php require __DIR__ . '/includes/footer.php'; ?>