831 lines
39 KiB
PHP
831 lines
39 KiB
PHP
<?php
|
|
ob_start();
|
|
session_start();
|
|
|
|
$action = $_POST['action'] ?? $_GET['action'] ?? '';
|
|
$isAjax = isset($_POST['ajax']) || isset($_GET['ajax']) || (isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] === 'XMLHttpRequest');
|
|
|
|
require_once __DIR__ . '/db/config.php';
|
|
|
|
// Action handling
|
|
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action'])) {
|
|
$action = $_POST['action'];
|
|
|
|
if ($action === 'add_category') {
|
|
$name_en = $_POST['cat_name_en'];
|
|
$name_ar = $_POST['cat_name_ar'];
|
|
$stmt = db()->prepare("INSERT INTO categories (name_en, name_ar) VALUES (?, ?)");
|
|
$stmt->execute([$name_en, $name_ar]);
|
|
|
|
if ($isAjax) {
|
|
header('Content-Type: application/json');
|
|
$categories = db()->query("SELECT * FROM categories ORDER BY name_en ASC")->fetchAll();
|
|
ob_start();
|
|
foreach($categories as $cat): ?>
|
|
<tr>
|
|
<td><?= $cat['name_en'] ?></td>
|
|
<td><?= $cat['name_ar'] ?></td>
|
|
<td class="text-end">
|
|
<button class="btn btn-sm btn-light p-1 me-1" onclick='editCategory(<?= json_encode($cat) ?>)'><i class="bi bi-pencil text-primary"></i></button>
|
|
<button class="btn btn-sm btn-light p-1" onclick="confirmDeleteAjax('category', <?= $cat['id'] ?>)"><i class="bi bi-trash text-danger"></i></button>
|
|
</td>
|
|
</tr>
|
|
<?php endforeach;
|
|
$html = ob_get_clean();
|
|
echo json_encode(['success' => true, 'html' => $html]);
|
|
exit;
|
|
}
|
|
header('Location: items.php?success=category_added');
|
|
exit;
|
|
} elseif ($action === 'edit_category') {
|
|
$id = $_POST['id'];
|
|
$name_en = $_POST['cat_name_en'];
|
|
$name_ar = $_POST['cat_name_ar'];
|
|
$stmt = db()->prepare("UPDATE categories SET name_en = ?, name_ar = ? WHERE id = ?");
|
|
$stmt->execute([$name_en, $name_ar, $id]);
|
|
|
|
if ($isAjax) {
|
|
header('Content-Type: application/json');
|
|
$categories = db()->query("SELECT * FROM categories ORDER BY name_en ASC")->fetchAll();
|
|
ob_start();
|
|
foreach($categories as $cat): ?>
|
|
<tr>
|
|
<td><?= $cat['name_en'] ?></td>
|
|
<td><?= $cat['name_ar'] ?></td>
|
|
<td class="text-end">
|
|
<button class="btn btn-sm btn-light p-1 me-1" onclick='editCategory(<?= json_encode($cat) ?>)'><i class="bi bi-pencil text-primary"></i></button>
|
|
<button class="btn btn-sm btn-light p-1" onclick="confirmDeleteAjax('category', <?= $cat['id'] ?>)"><i class="bi bi-trash text-danger"></i></button>
|
|
</td>
|
|
</tr>
|
|
<?php endforeach;
|
|
$html = ob_get_clean();
|
|
echo json_encode(['success' => true, 'html' => $html]);
|
|
exit;
|
|
}
|
|
header('Location: items.php?success=category_updated');
|
|
exit;
|
|
} elseif ($action === 'delete_category') {
|
|
$id = $_POST['id'];
|
|
$stmt = db()->prepare("DELETE FROM categories WHERE id = ?");
|
|
$stmt->execute([$id]);
|
|
|
|
if ($isAjax) {
|
|
header('Content-Type: application/json');
|
|
$categories = db()->query("SELECT * FROM categories ORDER BY name_en ASC")->fetchAll();
|
|
ob_start();
|
|
foreach($categories as $cat): ?>
|
|
<tr>
|
|
<td><?= $cat['name_en'] ?></td>
|
|
<td><?= $cat['name_ar'] ?></td>
|
|
<td class="text-end">
|
|
<button class="btn btn-sm btn-light p-1 me-1" onclick='editCategory(<?= json_encode($cat) ?>)'><i class="bi bi-pencil text-primary"></i></button>
|
|
<button class="btn btn-sm btn-light p-1" onclick="confirmDeleteAjax('category', <?= $cat['id'] ?>)"><i class="bi bi-trash text-danger"></i></button>
|
|
</td>
|
|
</tr>
|
|
<?php endforeach;
|
|
$html = ob_get_clean();
|
|
echo json_encode(['success' => true, 'html' => $html]);
|
|
exit;
|
|
}
|
|
header('Location: items.php?success=category_deleted');
|
|
exit;
|
|
} elseif ($action === 'add_item') {
|
|
$category_id = $_POST['category_id'] ?: null;
|
|
$name_en = $_POST['name_en'];
|
|
$name_ar = $_POST['name_ar'];
|
|
$vat_percent = $_POST['vat_percent'] ?: 0;
|
|
|
|
$image_url = null;
|
|
if (isset($_FILES['image']) && $_FILES['image']['error'] === 0) {
|
|
$ext = pathinfo($_FILES['image']['name'], PATHINFO_EXTENSION);
|
|
$filename = 'item_' . uniqid('', true) . '.' . $ext;
|
|
$upload_dir = __DIR__ . '/assets/images/items/';
|
|
if (!is_dir($upload_dir)) mkdir($upload_dir, 0777, true);
|
|
if (move_uploaded_file($_FILES['image']['tmp_name'], $upload_dir . $filename)) {
|
|
$image_url = 'assets/images/items/' . $filename;
|
|
}
|
|
}
|
|
|
|
$stmt = db()->prepare("INSERT INTO items (category_id, name_en, name_ar, vat_percent, image_url) VALUES (?, ?, ?, ?, ?)");
|
|
$stmt->execute([$category_id, $name_en, $name_ar, $vat_percent, $image_url]);
|
|
header('Location: items.php?success=item_added');
|
|
exit;
|
|
} elseif ($action === 'edit_item') {
|
|
$id = $_POST['id'];
|
|
$category_id = $_POST['category_id'] ?: null;
|
|
$name_en = $_POST['name_en'];
|
|
$name_ar = $_POST['name_ar'];
|
|
$vat_percent = $_POST['vat_percent'] ?: 0;
|
|
|
|
$image_url = null;
|
|
if (isset($_FILES['image']) && $_FILES['image']['error'] === 0) {
|
|
$ext = pathinfo($_FILES['image']['name'], PATHINFO_EXTENSION);
|
|
$filename = 'item_' . uniqid('', true) . '.' . $ext;
|
|
$upload_dir = __DIR__ . '/assets/images/items/';
|
|
if (!is_dir($upload_dir)) mkdir($upload_dir, 0777, true);
|
|
if (move_uploaded_file($_FILES['image']['tmp_name'], $upload_dir . $filename)) {
|
|
$image_url = 'assets/images/items/' . $filename;
|
|
}
|
|
}
|
|
|
|
if ($image_url) {
|
|
$stmt = db()->prepare("UPDATE items SET category_id = ?, name_en = ?, name_ar = ?, vat_percent = ?, image_url = ? WHERE id = ?");
|
|
$stmt->execute([$category_id, $name_en, $name_ar, $vat_percent, $image_url, $id]);
|
|
} else {
|
|
$stmt = db()->prepare("UPDATE items SET category_id = ?, name_en = ?, name_ar = ?, vat_percent = ? WHERE id = ?");
|
|
$stmt->execute([$category_id, $name_en, $name_ar, $vat_percent, $id]);
|
|
}
|
|
header('Location: items.php?success=item_updated');
|
|
exit;
|
|
} elseif ($action === 'delete_item') {
|
|
$id = $_POST['id'];
|
|
$stmt = db()->prepare("DELETE FROM items WHERE id = ?");
|
|
$stmt->execute([$id]);
|
|
header('Location: items.php?success=item_deleted');
|
|
exit;
|
|
} elseif ($action === 'add_service') {
|
|
$name_en = $_POST['svc_name_en'];
|
|
$name_ar = $_POST['svc_name_ar'];
|
|
$stmt = db()->prepare("INSERT INTO services (name_en, name_ar) VALUES (?, ?)");
|
|
$stmt->execute([$name_en, $name_ar]);
|
|
|
|
if ($isAjax) {
|
|
header('Content-Type: application/json');
|
|
echo json_encode(['success' => true, 'html' => renderServiceList($lang)]);
|
|
exit;
|
|
}
|
|
header('Location: items.php?success=service_added');
|
|
exit;
|
|
} elseif ($action === 'edit_service') {
|
|
$id = $_POST['id'];
|
|
$name_en = $_POST['svc_name_en'];
|
|
$name_ar = $_POST['svc_name_ar'];
|
|
$stmt = db()->prepare("UPDATE services SET name_en = ?, name_ar = ? WHERE id = ?");
|
|
$stmt->execute([$name_en, $name_ar, $id]);
|
|
|
|
if ($isAjax) {
|
|
header('Content-Type: application/json');
|
|
echo json_encode(['success' => true, 'html' => renderServiceList($lang)]);
|
|
exit;
|
|
}
|
|
header('Location: items.php?success=service_updated');
|
|
exit;
|
|
} elseif ($action === 'delete_service') {
|
|
$id = $_POST['id'];
|
|
$stmt = db()->prepare("DELETE FROM services WHERE id = ?");
|
|
$stmt->execute([$id]);
|
|
|
|
if ($isAjax) {
|
|
header('Content-Type: application/json');
|
|
echo json_encode(['success' => true, 'html' => renderServiceList($lang)]);
|
|
exit;
|
|
}
|
|
header('Location: items.php?success=service_deleted');
|
|
exit;
|
|
} elseif ($action === 'update_price') {
|
|
$item_id = $_POST['item_id'];
|
|
$service_id = $_POST['service_id'];
|
|
$price = $_POST['price'];
|
|
|
|
// Handle empty/invalid price
|
|
if ($price === '') $price = 0;
|
|
$price = str_replace(',', '.', $price); // Handle comma as decimal separator
|
|
$price = (float)$price;
|
|
|
|
// Determine branch_id from session (if available)
|
|
$branch_id = $_SESSION['branch_id'] ?? null;
|
|
if ($branch_id === 'all') $branch_id = null;
|
|
|
|
// Final sanity check for branch_id to avoid FK errors if session has stale ID
|
|
if ($branch_id) {
|
|
$chk = db()->prepare("SELECT id FROM branches WHERE id = ?");
|
|
$chk->execute([$branch_id]);
|
|
if (!$chk->fetch()) $branch_id = null;
|
|
}
|
|
|
|
try {
|
|
if ($branch_id) {
|
|
$stmt = db()->prepare("INSERT INTO prices (item_id, service_id, price, branch_id)
|
|
VALUES (?, ?, ?, ?)
|
|
ON DUPLICATE KEY UPDATE price = ?");
|
|
$stmt->execute([$item_id, $service_id, $price, $branch_id, $price]);
|
|
} else {
|
|
$stmt = db()->prepare("INSERT INTO prices (item_id, service_id, price)
|
|
VALUES (?, ?, ?)
|
|
ON DUPLICATE KEY UPDATE price = ?");
|
|
$stmt->execute([$item_id, $service_id, $price, $price]);
|
|
}
|
|
|
|
if ($isAjax) {
|
|
header('Content-Type: application/json');
|
|
echo json_encode(['success' => true]);
|
|
exit;
|
|
}
|
|
|
|
header('Location: items.php?success=price_updated');
|
|
exit;
|
|
} catch (Exception $e) {
|
|
if ($isAjax) {
|
|
header('Content-Type: application/json');
|
|
echo json_encode(['success' => false, 'error' => $e->getMessage()]);
|
|
exit;
|
|
}
|
|
header('Location: items.php?error=save_failed');
|
|
exit;
|
|
}
|
|
}
|
|
}
|
|
|
|
// NOW Include header
|
|
$title = 'items';
|
|
require_once __DIR__ . '/includes/header.php';
|
|
|
|
// Filter logic
|
|
$search = $_GET['search'] ?? '';
|
|
$category_filter = $_GET['category_id'] ?? '';
|
|
|
|
$categories = db()->query("SELECT * FROM categories ORDER BY name_en ASC")->fetchAll();
|
|
|
|
$query = "SELECT i.*, c.name_en as cat_en, c.name_ar as cat_ar
|
|
FROM items i
|
|
LEFT JOIN categories c ON i.category_id = c.id";
|
|
$params = [];
|
|
$where = [];
|
|
|
|
if ($search) {
|
|
$where[] = "(i.name_en LIKE ? OR i.name_ar LIKE ?)";
|
|
$params[] = "%$search%";
|
|
$params[] = "%$search%";
|
|
}
|
|
|
|
if ($category_filter) {
|
|
$where[] = "i.category_id = ?";
|
|
$params[] = $category_filter;
|
|
}
|
|
|
|
if ($where) {
|
|
$query .= " WHERE " . implode(" AND ", $where);
|
|
}
|
|
|
|
$query .= " ORDER BY i.name_en ASC";
|
|
$stmt = db()->prepare($query);
|
|
$stmt->execute($params);
|
|
$items = $stmt->fetchAll();
|
|
|
|
$services = db()->query("SELECT * FROM services ORDER BY name_en ASC")->fetchAll();
|
|
|
|
$stmt = db()->prepare("SELECT * FROM prices ");
|
|
$stmt->execute();
|
|
$prices_raw = $stmt->fetchAll();
|
|
$prices = [];
|
|
foreach ($prices_raw as $p) {
|
|
$prices[$p['item_id']][$p['service_id']] = $p['price'];
|
|
}
|
|
|
|
function renderServiceList($lang) {
|
|
$services = db()->query("SELECT * FROM services ORDER BY name_en ASC")->fetchAll();
|
|
ob_start();
|
|
foreach($services as $svc): ?>
|
|
<tr>
|
|
<td><?= $svc['name_en'] ?></td>
|
|
<td><?= $svc['name_ar'] ?></td>
|
|
<td class="text-end">
|
|
<button class="btn btn-sm btn-light p-1 me-1" onclick='editService(<?= json_encode($svc) ?>)'><i class="bi bi-pencil text-primary"></i></button>
|
|
<button class="btn btn-sm btn-light p-1" onclick="confirmDeleteAjax('service', <?= $svc['id'] ?>)"><i class="bi bi-trash text-danger"></i></button>
|
|
</td>
|
|
</tr>
|
|
<?php endforeach;
|
|
return ob_get_clean();
|
|
}
|
|
?>
|
|
|
|
<div class="d-flex justify-content-between align-items-center mb-4">
|
|
<div>
|
|
<h2 class="fw-bold mb-1"><?= __('items') ?></h2>
|
|
<p class="text-muted small mb-0"><?= __('manage_items_categories_services') ?? 'Manage your items, categories and services' ?></p>
|
|
</div>
|
|
<div class="d-flex gap-2">
|
|
<button type="button" class="btn btn-light shadow-sm px-3" style="border-radius: 12px;" data-bs-toggle="modal" data-bs-target="#categoriesModal">
|
|
<i class="bi bi-tags me-2 text-primary"></i> <?= __('categories') ?>
|
|
</button>
|
|
<button type="button" class="btn btn-light shadow-sm px-3" style="border-radius: 12px;" data-bs-toggle="modal" data-bs-target="#servicesModal">
|
|
<i class="bi bi-gear me-2 text-primary"></i> <?= __('services') ?>
|
|
</button>
|
|
<?php if (has_permission('add')): ?>
|
|
<button type="button" class="btn btn-primary shadow-sm px-4" style="border-radius: 12px;" data-bs-toggle="modal" data-bs-target="#itemModal" onclick="resetForm()">
|
|
<i class="bi bi-plus-lg me-2"></i> <?= __('add_item') ?>
|
|
</button>
|
|
<?php endif; ?>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="card border-0 shadow-sm mb-4" style="border-radius: 20px;">
|
|
<div class="card-body p-4">
|
|
<form method="GET" class="row g-3">
|
|
<div class="col-md-5">
|
|
<div class="input-group">
|
|
<span class="input-group-text bg-light border-0"><i class="bi bi-search"></i></span>
|
|
<input type="text" name="search" class="form-control bg-light border-0" placeholder="<?= __('search_items') ?>" value="<?= htmlspecialchars($search) ?>">
|
|
</div>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<select name="category_id" class="form-select bg-light border-0">
|
|
<option value=""><?= __('all_categories') ?></option>
|
|
<?php foreach($categories as $cat): ?>
|
|
<option value="<?= $cat['id'] ?>" <?= $category_filter == $cat['id'] ? 'selected' : '' ?> >
|
|
<?= $lang === 'ar' ? $cat['name_ar'] : $cat['name_en'] ?>
|
|
</option>
|
|
<?php endforeach; ?>
|
|
</select>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<button type="submit" class="btn btn-dark w-100" style="border-radius: 12px;"><?= __('filter') ?></button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row g-4">
|
|
<?php foreach($items as $item):
|
|
?><div class="col-md-6 col-lg-4 col-xl-3">
|
|
<div class="card h-100 border-0 shadow-sm item-card" style="border-radius: 20px; overflow: hidden;">
|
|
<div style="height: 180px; overflow: hidden; position: relative;">
|
|
<?php if ($item['image_url']): ?>
|
|
<img src="<?= htmlspecialchars($item['image_url']) ?>" class="card-img-top h-100 w-100 object-fit-cover" alt="<?= htmlspecialchars($item['name_en']) ?>">
|
|
<?php else:
|
|
?><div class="bg-light h-100 w-100 d-flex align-items-center justify-content-center">
|
|
<i class="bi bi-image text-muted opacity-25" style="font-size: 3rem;"></i>
|
|
</div>
|
|
<?php endif; ?>
|
|
<div class="position-absolute top-0 end-0 p-3">
|
|
<span class="badge bg-white text-dark shadow-sm py-2 px-3" style="border-radius: 10px;">
|
|
<?= htmlspecialchars($item['cat_en'] ?? __('uncategorized')) ?>
|
|
</span>
|
|
</div>
|
|
</div>
|
|
<div class="card-body p-4">
|
|
<h5 class="fw-bold mb-1"><?= htmlspecialchars($item['name_en']) ?></h5>
|
|
<p class="text-muted small mb-3"><?= htmlspecialchars($item['name_ar']) ?></p>
|
|
|
|
<div class="d-flex align-items-center justify-content-between mt-auto">
|
|
<div class="text-primary small fw-bold">
|
|
<?= count($services) ?> <?= __('services') ?>
|
|
</div>
|
|
<div class="btn-group">
|
|
<button class="btn btn-sm btn-light p-2" style="border-radius: 8px;" data-bs-toggle="modal" data-bs-target="#pricingModal<?= $item['id'] ?>">
|
|
<i class="bi bi-tag-fill text-primary"></i>
|
|
</button>
|
|
<?php if (has_permission('edit')): ?>
|
|
<button class="btn btn-sm btn-light p-2 mx-1" style="border-radius: 8px;" onclick="editItem(<?= htmlspecialchars(json_encode($item), ENT_QUOTES) ?>)">
|
|
<i class="bi bi-pencil text-primary"></i>
|
|
</button>
|
|
<?php endif; ?>
|
|
<?php if (has_permission('delete')): ?>
|
|
<button class="btn btn-sm btn-light p-2" style="border-radius: 8px;" onclick="confirmDeleteAjax('item', <?= $item['id'] ?>)">
|
|
<i class="bi bi-trash text-danger"></i>
|
|
</button>
|
|
<?php endif; ?>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<?php endforeach; ?>
|
|
</div>
|
|
|
|
<!-- Modals -->
|
|
|
|
<!-- Item Modal -->
|
|
<div class="modal fade" id="itemModal" tabindex="-1">
|
|
<div class="modal-dialog modal-dialog-centered">
|
|
<div class="modal-content" style="border-radius: 25px;">
|
|
<div class="modal-header border-0 p-4">
|
|
<h5 class="modal-title fw-bold" id="itemModalLabel"><?= __('add_item') ?></h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
|
</div>
|
|
<div class="modal-body p-4 pt-0">
|
|
<form id="itemForm" method="POST" enctype="multipart/form-data">
|
|
<input type="hidden" name="action" id="itemAction" value="add_item">
|
|
<input type="hidden" name="id" id="itemId">
|
|
|
|
<div class="text-center mb-4">
|
|
<div id="imagePreviewContainer" class="d-none mb-3">
|
|
<img id="imagePreview" src="" class="rounded-4 shadow-sm" style="max-height: 150px; width: auto;">
|
|
</div>
|
|
<label class="btn btn-light w-100 py-3 mb-0" style="border-radius: 15px; border: 2px dashed #dee2e6;">
|
|
<i class="bi bi-cloud-arrow-up fs-4 d-block mb-1"></i>
|
|
<span class="small text-muted"><?= __('upload_image') ?></span>
|
|
<input type="file" name="image" id="itemImageFile" class="d-none" accept="image/*">
|
|
</label>
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<label class="form-label small fw-bold"><?= __('name_en') ?></label>
|
|
<div class="input-group">
|
|
<input type="text" name="name_en" id="itemNameEn" class="form-control" placeholder="Item Name (English)" required>
|
|
<button type="button" class="btn btn-outline-secondary" onclick="translateField('itemNameEn', 'itemNameAr', 'en-ar')">
|
|
<i class="bi bi-translate"></i>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label class="form-label small fw-bold"><?= __('name_ar') ?></label>
|
|
<div class="input-group">
|
|
<input type="text" name="name_ar" id="itemNameAr" class="form-control text-end" placeholder="اسم الصنف (بالعربي)" required>
|
|
<button type="button" class="btn btn-outline-secondary" onclick="translateField('itemNameAr', 'itemNameEn', 'ar-en')">
|
|
<i class="bi bi-translate"></i>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label class="form-label small fw-bold"><?= __('category') ?></label>
|
|
<select name="category_id" id="itemCategory" class="form-select" required>
|
|
<option value=""><?= __('select_category') ?></option>
|
|
<?php foreach($categories as $cat):
|
|
?><option value="<?= $cat['id'] ?>"><?= $lang === 'ar' ? $cat['name_ar'] : $cat['name_en'] ?></option>
|
|
<?php endforeach; ?>
|
|
</select>
|
|
</div>
|
|
<div class="mb-4">
|
|
<label class="form-label small fw-bold"><?= __('vat_percent') ?> (%)</label>
|
|
<input type="number" step="0.01" name="vat_percent" id="itemVat" class="form-control" placeholder="0.00">
|
|
</div>
|
|
|
|
<div class="d-flex gap-2">
|
|
<button type="button" class="btn btn-light w-100 py-2 fw-bold" style="border-radius: 12px;" data-bs-dismiss="modal"><?= __('cancel') ?></button>
|
|
<button type="submit" class="btn btn-primary w-100 py-2 fw-bold" style="border-radius: 12px;"><?= __('save') ?></button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Categories Modal -->
|
|
<div class="modal fade" id="categoriesModal" tabindex="-1">
|
|
<div class="modal-dialog modal-lg modal-dialog-centered">
|
|
<div class="modal-content" style="border-radius: 25px;">
|
|
<div class="modal-header border-0 p-4">
|
|
<h5 class="modal-title fw-bold"><?= __('manage_categories') ?></h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
|
</div>
|
|
<div class="modal-body p-4 pt-0">
|
|
<div class="row g-4">
|
|
<div class="col-md-5">
|
|
<form id="categoryForm" method="POST">
|
|
<input type="hidden" name="action" id="catAction" value="add_category">
|
|
<input type="hidden" name="id" id="catId">
|
|
<input type="hidden" name="ajax" value="1">
|
|
|
|
<div class="mb-3">
|
|
<label class="form-label small fw-bold"><?= __('name_en') ?></label>
|
|
<div class="input-group">
|
|
<input type="text" name="cat_name_en" id="catNameEn" class="form-control" required>
|
|
<button type="button" class="btn btn-outline-secondary btn-sm" onclick="translateField('catNameEn', 'catNameAr', 'en-ar')">
|
|
<i class="bi bi-translate"></i>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label class="form-label small fw-bold"><?= __('name_ar') ?></label>
|
|
<div class="input-group">
|
|
<input type="text" name="cat_name_ar" id="catNameAr" class="form-control text-end" required>
|
|
<button type="button" class="btn btn-outline-secondary btn-sm" onclick="translateField('catNameAr', 'catNameEn', 'ar-en')">
|
|
<i class="bi bi-translate"></i>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<button type="submit" class="btn btn-primary w-100 fw-bold" style="border-radius: 10px;">
|
|
<span id="catSubmitBtn"><?= __('add_category') ?></span>
|
|
</button>
|
|
<button type="button" id="catCancelBtn" class="btn btn-light w-100 mt-2 d-none" onclick="resetCatForm()" style="border-radius: 10px;">
|
|
<?= __('cancel') ?>
|
|
</button>
|
|
</form>
|
|
</div>
|
|
<div class="col-md-7">
|
|
<div class="table-responsive" style="max-height: 400px; overflow-y: auto;">
|
|
<table class="table table-hover small">
|
|
<thead>
|
|
<tr>
|
|
<th><?= __('name_en') ?></th>
|
|
<th><?= __('name_ar') ?></th>
|
|
<th class="text-end"><?= __('actions') ?></th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="categoryTableBody">
|
|
<?php foreach($categories as $cat): ?>
|
|
<tr>
|
|
<td><?= $cat['name_en'] ?></td>
|
|
<td><?= $cat['name_ar'] ?></td>
|
|
<td class="text-end">
|
|
<button class="btn btn-sm btn-light p-1 me-1" onclick='editCategory(<?= json_encode($cat) ?>)'><i class="bi bi-pencil text-primary"></i></button>
|
|
<button class="btn btn-sm btn-light p-1" onclick="confirmDeleteAjax('category', <?= $cat['id'] ?>)"><i class="bi bi-trash text-danger"></i></button>
|
|
</td>
|
|
</tr>
|
|
<?php endforeach; ?>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Services Modal -->
|
|
<div class="modal fade" id="servicesModal" tabindex="-1">
|
|
<div class="modal-dialog modal-lg modal-dialog-centered">
|
|
<div class="modal-content" style="border-radius: 25px;">
|
|
<div class="modal-header border-0 p-4">
|
|
<h5 class="modal-title fw-bold"><?= __('manage_services') ?></h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
|
</div>
|
|
<div class="modal-body p-4 pt-0">
|
|
<div class="row g-4">
|
|
<div class="col-md-5">
|
|
<form id="serviceForm" method="POST">
|
|
<input type="hidden" name="action" id="svcAction" value="add_service">
|
|
<input type="hidden" name="id" id="svcId">
|
|
<input type="hidden" name="ajax" value="1">
|
|
|
|
<div class="mb-3">
|
|
<label class="form-label small fw-bold"><?= __('name_en') ?></label>
|
|
<div class="input-group">
|
|
<input type="text" name="svc_name_en" id="svcNameEn" class="form-control" required>
|
|
<button type="button" class="btn btn-outline-secondary btn-sm" onclick="translateField('svcNameEn', 'svcNameAr', 'en-ar')">
|
|
<i class="bi bi-translate"></i>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label class="form-label small fw-bold"><?= __('name_ar') ?></label>
|
|
<div class="input-group">
|
|
<input type="text" name="svc_name_ar" id="svcNameAr" class="form-control text-end" required>
|
|
<button type="button" class="btn btn-outline-secondary btn-sm" onclick="translateField('svcNameAr', 'svcNameEn', 'ar-en')">
|
|
<i class="bi bi-translate"></i>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<button type="submit" class="btn btn-primary w-100 fw-bold" style="border-radius: 10px;">
|
|
<span id="svcSubmitBtn"><?= __('add_service') ?></span>
|
|
</button>
|
|
<button type="button" id="svcCancelBtn" class="btn btn-light w-100 mt-2 d-none" onclick="resetSvcForm()" style="border-radius: 10px;">
|
|
<?= __('cancel') ?>
|
|
</button>
|
|
</form>
|
|
</div>
|
|
<div class="col-md-7">
|
|
<div class="table-responsive" style="max-height: 400px; overflow-y: auto;">
|
|
<table class="table table-hover small">
|
|
<thead>
|
|
<tr>
|
|
<th><?= __('name_en') ?></th>
|
|
<th><?= __('name_ar') ?></th>
|
|
<th class="text-end"><?= __('actions') ?></th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="serviceTableBody">
|
|
<?= renderServiceList($lang) ?>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Pricing Modals -->
|
|
<?php foreach($items as $item): ?>
|
|
<div class="modal fade" id="pricingModal<?= $item['id'] ?>" tabindex="-1">
|
|
<div class="modal-dialog modal-dialog-centered">
|
|
<div class="modal-content" style="border-radius: 25px;">
|
|
<div class="modal-header border-0 p-4">
|
|
<h5 class="modal-title fw-bold">
|
|
<?= __('set_prices_for') ?> <?= $lang === 'ar' ? $item['name_ar'] : $item['name_en'] ?>
|
|
</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
|
</div>
|
|
<div class="modal-body p-4 pt-0">
|
|
<div class="alert alert-light small border-0 mb-4" style="border-radius: 15px;">
|
|
<i class="bi bi-info-circle me-2"></i><?= __('prices_saved_automatically') ?>
|
|
</div>
|
|
|
|
<?php foreach($services as $svc): ?>
|
|
<div class="mb-3 d-flex align-items-center justify-content-between p-3 bg-light" style="border-radius: 15px;">
|
|
<div class="fw-bold small">
|
|
<?= $lang === 'ar' ? $svc['name_ar'] : $svc['name_en'] ?>
|
|
</div>
|
|
<div class="d-flex align-items-center" style="width: 150px;">
|
|
<input type="number" step="0.001"
|
|
class="form-control form-control-sm border-0 bg-white price-input"
|
|
style="border-radius: 10px;"
|
|
data-item-id="<?= $item['id'] ?>"
|
|
data-service-id="<?= $svc['id'] ?>"
|
|
value="<?= $prices[$item['id']][$svc['id']] ?? '' ?>"
|
|
placeholder="0.000">
|
|
<i class="bi bi-check-circle-fill text-success ms-2 success-indicator d-none"></i>
|
|
</div>
|
|
</div>
|
|
<?php endforeach; ?>
|
|
|
|
<button type="button" class="btn btn-dark w-100 py-3 fw-bold mt-3 shadow-sm" style="border-radius: 15px;" data-bs-dismiss="modal">
|
|
<?= __('done') ?>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<?php endforeach; ?>
|
|
|
|
<form id="deleteForm" method="POST" class="d-none">
|
|
<input type="hidden" name="action" id="deleteAction">
|
|
<input type="hidden" name="id" id="deleteId">
|
|
</form>
|
|
|
|
<script>
|
|
async function translateField(sourceId, targetId, direction) {
|
|
const sourceEl = document.getElementById(sourceId);
|
|
const targetEl = document.getElementById(targetId);
|
|
const text = sourceEl.value;
|
|
if (!text) return;
|
|
const btn = event.currentTarget;
|
|
const originalHtml = btn.innerHTML;
|
|
btn.disabled = true;
|
|
btn.innerHTML = '<span class="spinner-border spinner-border-sm"></span>';
|
|
try {
|
|
const response = await fetch('api/translate.php', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ text, direction })
|
|
});
|
|
const data = await response.json();
|
|
if (data.success) { targetEl.value = data.translated; }
|
|
} catch (e) { console.error(e); }
|
|
finally { btn.disabled = false; btn.innerHTML = originalHtml; }
|
|
}
|
|
|
|
function resetForm() {
|
|
document.getElementById('itemForm').reset();
|
|
document.getElementById('itemAction').value = 'add_item';
|
|
document.getElementById('itemModalLabel').innerText = '<?= __('add_item') ?>';
|
|
document.getElementById('imagePreviewContainer').classList.add('d-none');
|
|
}
|
|
|
|
function editItem(item) {
|
|
resetForm();
|
|
document.getElementById('itemAction').value = 'edit_item';
|
|
document.getElementById('itemId').value = item.id;
|
|
document.getElementById('itemNameEn').value = item.name_en;
|
|
document.getElementById('itemNameAr').value = item.name_ar;
|
|
document.getElementById('itemCategory').value = item.category_id || '';
|
|
document.getElementById('itemVat').value = item.vat_percent || '';
|
|
document.getElementById('itemModalLabel').innerText = '<?= __('edit_item') ?>';
|
|
|
|
if (item.image_url) {
|
|
document.getElementById('imagePreview').src = item.image_url;
|
|
document.getElementById('imagePreviewContainer').classList.remove('d-none');
|
|
}
|
|
|
|
new bootstrap.Modal(document.getElementById('itemModal')).show();
|
|
}
|
|
|
|
function confirmDeleteAjax(type, id) {
|
|
if (confirm('<?= __('confirm_delete') ?>')) {
|
|
document.getElementById('deleteAction').value = 'delete_' + type;
|
|
document.getElementById('deleteId').value = id;
|
|
document.getElementById('deleteForm').submit();
|
|
}
|
|
}
|
|
|
|
// Category AJAX
|
|
function editCategory(cat) {
|
|
document.getElementById('catAction').value = 'edit_category';
|
|
document.getElementById('catId').value = cat.id;
|
|
document.getElementById('catNameEn').value = cat.name_en;
|
|
document.getElementById('catNameAr').value = cat.name_ar;
|
|
document.getElementById('catSubmitBtn').innerText = '<?= __('save') ?>';
|
|
document.getElementById('catCancelBtn').classList.remove('d-none');
|
|
}
|
|
|
|
function resetCatForm() {
|
|
document.getElementById('categoryForm').reset();
|
|
document.getElementById('catAction').value = 'add_category';
|
|
document.getElementById('catSubmitBtn').innerText = '<?= __('add_category') ?>';
|
|
document.getElementById('catCancelBtn').classList.add('d-none');
|
|
}
|
|
|
|
document.getElementById('categoryForm').addEventListener('submit', async function(e) {
|
|
e.preventDefault();
|
|
const formData = new FormData(this);
|
|
try {
|
|
const response = await fetch('items.php', { method: 'POST', body: formData });
|
|
const data = await response.json();
|
|
if (data.success) {
|
|
document.getElementById('categoryTableBody').innerHTML = data.html;
|
|
resetCatForm();
|
|
} else {
|
|
alert(data.error || 'Failed to save category');
|
|
}
|
|
} catch (error) {
|
|
console.error('Error saving category:', error);
|
|
}
|
|
});
|
|
|
|
// Service AJAX
|
|
function editService(svc) {
|
|
document.getElementById('svcAction').value = 'edit_service';
|
|
document.getElementById('svcId').value = svc.id;
|
|
document.getElementById('svcNameEn').value = svc.name_en;
|
|
document.getElementById('svcNameAr').value = svc.name_ar;
|
|
document.getElementById('svcSubmitBtn').innerText = '<?= __('save') ?>';
|
|
document.getElementById('svcCancelBtn').classList.remove('d-none');
|
|
}
|
|
|
|
function resetSvcForm() {
|
|
document.getElementById('serviceForm').reset();
|
|
document.getElementById('svcAction').value = 'add_service';
|
|
document.getElementById('svcSubmitBtn').innerText = '<?= __('add_service') ?>';
|
|
document.getElementById('svcCancelBtn').classList.add('d-none');
|
|
}
|
|
|
|
// Service Form AJAX
|
|
document.getElementById('serviceForm').addEventListener('submit', async function(e) {
|
|
e.preventDefault();
|
|
const formData = new FormData(this);
|
|
try {
|
|
const response = await fetch('items.php', { method: 'POST', body: formData });
|
|
const data = await response.json();
|
|
if (data.success) {
|
|
document.getElementById('serviceTableBody').innerHTML = data.html;
|
|
resetSvcForm();
|
|
} else {
|
|
alert(data.error || 'Failed to save service');
|
|
}
|
|
} catch (error) {
|
|
console.error('Error saving service:', error);
|
|
}
|
|
});
|
|
|
|
// AJAX logic for prices
|
|
document.querySelectorAll('.price-input').forEach(input => {
|
|
let timeout = null;
|
|
input.addEventListener('input', function() {
|
|
if (this.readOnly || this.disabled) return;
|
|
clearTimeout(timeout);
|
|
const self = this;
|
|
timeout = setTimeout(async () => {
|
|
const formData = new FormData();
|
|
formData.append('action', 'update_price');
|
|
formData.append('ajax', '1');
|
|
formData.append('item_id', self.dataset.itemId);
|
|
formData.append('service_id', self.dataset.serviceId);
|
|
formData.append('price', self.value);
|
|
|
|
try {
|
|
const response = await fetch('items.php', {
|
|
method: 'POST',
|
|
body: formData
|
|
});
|
|
const data = await response.json();
|
|
if (data.success) {
|
|
showIndicator(self);
|
|
} else {
|
|
alert(data.error || 'Failed to update price');
|
|
}
|
|
} catch (error) {
|
|
console.error('Error updating price:', error);
|
|
alert('Error updating price: ' + error.message);
|
|
}
|
|
}, 500);
|
|
});
|
|
});
|
|
|
|
function showIndicator(input) {
|
|
const indicator = input.parentElement.querySelector('.success-indicator');
|
|
if (indicator) {
|
|
indicator.classList.remove('d-none');
|
|
setTimeout(() => {
|
|
indicator.classList.add('d-none');
|
|
}, 2000);
|
|
}
|
|
}
|
|
|
|
// Image preview logic
|
|
document.getElementById('itemImageFile').addEventListener('change', function(e) {
|
|
const file = e.target.files[0];
|
|
if (file) {
|
|
const reader = new FileReader();
|
|
reader.onload = function(e) {
|
|
document.getElementById('imagePreview').src = e.target.result;
|
|
document.getElementById('imagePreviewContainer').classList.remove('d-none');
|
|
}
|
|
reader.readAsDataURL(file);
|
|
}
|
|
});
|
|
</script>
|
|
|
|
<?php require_once __DIR__ . '/includes/footer.php'; ?>
|