286 lines
9.8 KiB
PHP
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>
|