38471-vm/price_checker.php
2026-03-01 18:25:59 +00:00

286 lines
9.8 KiB
PHP

<?php
require_once __DIR__ . '/db/config.php';
// Fetch outlets for the selector
try {
$outlets = db()->query("SELECT id, name FROM outlets WHERE status = 'active'")->fetchAll(PDO::FETCH_ASSOC);
} catch (Exception $e) {
$outlets = [];
}
// Simple API endpoint for price lookup
if (isset($_GET['action']) && $_GET['action'] === 'lookup') {
header('Content-Type: application/json');
$sku = $_GET['sku'] ?? '';
$outlet_id = $_GET['outlet_id'] ?? ($_COOKIE['preferred_outlet'] ?? null);
if (empty($sku)) {
echo json_encode(['error' => 'Empty SKU']);
exit;
}
try {
$sql = "SELECT name_en, name_ar, sale_price, image_path FROM stock_items WHERE sku = ?";
$params = [$sku];
if ($outlet_id) {
$sql .= " AND outlet_id = ?";
$params[] = $outlet_id;
}
$sql .= " LIMIT 1";
$stmt = db()->prepare($sql);
$stmt->execute($params);
$item = $stmt->fetch(PDO::FETCH_ASSOC);
if ($item) {
echo json_encode([
'success' => true,
'item' => [
'name' => $item['name_en'] ?: $item['name_ar'],
'price' => number_format($item['sale_price'], 3),
'image' => $item['image_path'] ?: null
]
]);
} else {
echo json_encode(['success' => false, 'message' => 'Item not found']);
}
} catch (Exception $e) {
echo json_encode(['error' => 'Database error']);
}
exit;
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>Price Checker</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
body {
background-color: #f0f2f5;
height: 100vh;
display: flex;
flex-direction: column;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}
.checker-container {
max-width: 500px;
margin: auto;
width: 100%;
padding: 20px;
}
.card {
border-radius: 20px;
border: none;
box-shadow: 0 10px 30px rgba(0,0,0,0.08);
}
#sku-input {
font-size: 1.5rem;
text-align: center;
border-radius: 15px;
padding: 15px;
border: 2px solid #dee2e6;
background: #fff;
}
#sku-input:focus {
border-color: #0d6efd;
box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.1);
}
.result-card {
display: none;
margin-top: 20px;
animation: slideUp 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275);
}
@keyframes slideUp {
from { opacity: 0; transform: translateY(30px); }
to { opacity: 1; transform: translateY(0); }
}
.price-tag {
font-size: 3.5rem;
font-weight: 800;
color: #198754;
line-height: 1;
}
.currency {
font-size: 1.2rem;
vertical-align: super;
margin-left: 5px;
color: #6c757d;
}
.item-image {
max-width: 100%;
height: 200px;
object-fit: contain;
margin-bottom: 20px;
border-radius: 15px;
}
.outlet-badge {
position: absolute;
top: 20px;
right: 20px;
font-size: 0.8rem;
}
.settings-btn {
position: fixed;
top: 15px;
right: 15px;
color: #6c757d;
z-index: 100;
}
</style>
</head>
<body>
<a href="#" class="settings-btn" data-bs-toggle="modal" data-bs-target="#settingsModal">
<i class="fas fa-cog fa-lg"></i>
</a>
<div class="checker-container">
<div class="text-center mb-4">
<h1 class="fw-bold text-primary">Price Checker</h1>
<p class="text-muted">Quickly check item prices</p>
</div>
<div class="card p-4">
<form id="checker-form">
<div class="mb-0">
<input type="text" id="sku-input" class="form-control" placeholder="Scan Barcode..." autofocus autocomplete="off">
</div>
<button type="submit" class="btn btn-primary w-100 py-3 mt-3 d-none">CHECK PRICE</button>
</form>
</div>
<div id="result" class="result-card">
<div class="card p-4 text-center position-relative">
<div id="item-info">
<img id="item-img" src="" alt="" class="item-image d-none">
<h2 id="item-name" class="fw-bold mb-3"></h2>
<div class="price-tag mb-0">
<span id="item-price"></span>
<span class="currency">OMR</span>
</div>
</div>
</div>
</div>
<div id="error-msg" class="alert alert-warning mt-3 d-none text-center rounded-pill"></div>
<div id="loading" class="text-center mt-3 d-none">
<div class="spinner-border text-primary" role="status">
<span class="visually-hidden">Loading...</span>
</div>
</div>
</div>
<!-- Settings Modal -->
<div class="modal fade" id="settingsModal" tabindex="-1">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content border-0 shadow" style="border-radius: 20px;">
<div class="modal-header border-0">
<h5 class="modal-title fw-bold">Settings</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<label class="form-label fw-bold">Select Outlet</label>
<select id="outlet-select" class="form-select form-select-lg" style="border-radius: 12px;">
<option value="">All Outlets</option>
<?php foreach ($outlets as $o): ?>
<option value="<?= $o['id'] ?>" <?= ($_COOKIE['preferred_outlet'] ?? '') == $o['id'] ? 'selected' : '' ?>>
<?= htmlspecialchars($o['name']) ?>
</option>
<?php endforeach; ?>
</select>
<p class="small text-muted mt-2">Prices will be checked for the selected outlet.</p>
</div>
<div class="modal-footer border-0">
<button type="button" class="btn btn-primary w-100 py-2" style="border-radius: 12px;" data-bs-dismiss="modal" id="save-settings">Save Settings</button>
</div>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
<script>
const form = document.getElementById('checker-form');
const input = document.getElementById('sku-input');
const resultDiv = document.getElementById('result');
const itemName = document.getElementById('item-name');
const itemPrice = document.getElementById('item-price');
const itemImg = document.getElementById('item-img');
const errorMsg = document.getElementById('error-msg');
const loading = document.getElementById('loading');
const outletSelect = document.getElementById('outlet-select');
// Keep input focused
setInterval(() => {
if (document.activeElement !== input && document.activeElement.tagName !== 'SELECT' && !document.querySelector('.modal.show')) {
input.focus();
}
}, 1000);
// Save outlet preference
document.getElementById('save-settings').addEventListener('click', () => {
document.cookie = `preferred_outlet=${outletSelect.value}; path=/; max-age=${60*60*24*365}`;
});
form.addEventListener('submit', async (e) => {
e.preventDefault();
const sku = input.value.trim();
if (!sku) return;
errorMsg.classList.add('d-none');
loading.classList.remove('d-none');
try {
const response = await fetch(`?action=lookup&sku=${encodeURIComponent(sku)}`);
const data = await response.json();
loading.classList.add('d-none');
if (data.success) {
itemName.textContent = data.item.name;
itemPrice.textContent = data.item.price;
if (data.item.image) {
itemImg.src = data.item.image;
itemImg.classList.remove('d-none');
} else {
itemImg.classList.add('d-none');
}
resultDiv.style.display = 'block';
input.value = '';
// Vibrate if supported
if (navigator.vibrate) navigator.vibrate(50);
} else {
resultDiv.style.display = 'none';
errorMsg.textContent = data.message || 'Item not found';
errorMsg.classList.remove('d-none');
input.value = '';
if (navigator.vibrate) navigator.vibrate([100, 50, 100]);
}
} catch (err) {
loading.classList.add('d-none');
errorMsg.textContent = 'Connection error';
errorMsg.classList.remove('d-none');
}
});
// Auto-submit after 400ms of inactivity if something was typed (typical for barcode scanners)
let timer;
input.addEventListener('input', () => {
clearTimeout(timer);
timer = setTimeout(() => {
if (input.value.length > 2) {
form.dispatchEvent(new Event('submit'));
}
}, 400);
});
</script>
</body>
</html>