38471-vm/customer-display.php
2026-05-08 17:40:59 +00:00

995 lines
37 KiB
PHP

<?php
require_once 'db/config.php';
$settings = [];
try {
$rows = db()->query("SELECT * FROM settings")->fetchAll(PDO::FETCH_ASSOC);
foreach ($rows as $r) {
$settings[$r['key']] = $r['value'];
}
} catch (Exception $e) {
// Fallback if DB fails
}
$title = $settings['customer_display_greeting_title'] ?? 'Welcome';
$subtitle = $settings['customer_display_greeting_text'] ?? 'We are ready to serve you.';
$slides = [];
if (!empty($settings['display_slide_1'])) $slides[] = $settings['display_slide_1'];
if (!empty($settings['display_slide_2'])) $slides[] = $settings['display_slide_2'];
if (!empty($settings['display_slide_3'])) $slides[] = $settings['display_slide_3'];
// Fallbacks
if (empty($slides)) {
$slides = [
'https://images.unsplash.com/photo-1441986300917-64674bd600d8?q=80&w=1920&auto=format&fit=crop',
'https://images.unsplash.com/photo-1472851294608-415170d4e897?q=80&w=1920&auto=format&fit=crop',
'https://images.unsplash.com/photo-1556742049-0cfed4f7a07d?q=80&w=1920&auto=format&fit=crop',
'https://images.unsplash.com/photo-1528698827591-e19ccd7bc23d?q=80&w=1920&auto=format&fit=crop'
];
}
$footerBrand = 'Meezan Accounting System';
$footerPoweredBy = 'omanapps.cloud';
$footerWhatsapp = '+96899359472';
$footerEmail = 'aalabry@gmail.com';
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Customer Display | Meezan Accounting System</title>
<meta name="description" content="Customer-facing POS display with a live basket, payment summary, and transaction totals.">
<meta name="robots" content="noindex, nofollow">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.1/font/bootstrap-icons.css">
<link rel="stylesheet" href="assets/css/custom.css?v=<?= time() ?>">
<style>
html, body {
height: 100%;
}
body {
background:
radial-gradient(circle at top left, rgba(59, 130, 246, 0.16), transparent 26%),
radial-gradient(circle at top right, rgba(15, 23, 42, 0.09), transparent 28%),
linear-gradient(180deg, #f8fbff 0%, var(--bg) 100%);
color: var(--text);
min-height: 100vh;
overflow: hidden;
display: flex;
flex-direction: column;
transition: background-color 0.3s, color 0.3s;
}
.header {
display: flex;
justify-content: space-between;
align-items: center;
gap: 1rem;
padding: 1rem 1.35rem;
background: rgba(255, 255, 255, 0.88);
border-bottom: 1px solid rgba(226, 232, 240, 0.9);
backdrop-filter: blur(22px);
box-shadow: 0 10px 30px rgba(15, 23, 42, 0.06);
}
.brand-wrap {
display: flex;
align-items: center;
gap: 0.9rem;
min-width: 0;
}
.brand-mark {
width: 52px;
height: 52px;
border-radius: 16px;
display: grid;
place-items: center;
background: linear-gradient(135deg, var(--accent), #0ea5e9);
color: #fff;
font-size: 1.35rem;
box-shadow: 0 14px 34px rgba(59, 130, 246, 0.25);
flex-shrink: 0;
}
.brand-title {
font-size: 1.2rem;
font-weight: 800;
color: var(--primary);
letter-spacing: -0.02em;
}
.brand-subtitle {
font-size: 0.92rem;
color: var(--text-muted);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.header-actions {
display: flex;
align-items: center;
justify-content: flex-end;
gap: 0.75rem;
flex-wrap: wrap;
}
.status-chip,
.customer-chip {
display: inline-flex;
align-items: center;
gap: 0.55rem;
padding: 0.7rem 0.95rem;
border-radius: 999px;
background: rgba(255, 255, 255, 0.92);
border: 1px solid rgba(203, 213, 225, 0.95);
box-shadow: 0 10px 22px rgba(15, 23, 42, 0.06);
color: var(--text);
font-weight: 600;
}
.status-chip {
color: var(--accent);
}
.customer-chip {
min-width: 150px;
justify-content: center;
}
.fullscreen-btn {
width: 46px;
height: 46px;
border-radius: 14px;
border: 1px solid rgba(203, 213, 225, 0.95);
background: rgba(255, 255, 255, 0.92);
color: var(--primary);
box-shadow: 0 10px 22px rgba(15, 23, 42, 0.06);
transition: transform 0.18s ease, box-shadow 0.18s ease, border-color 0.18s ease;
}
.fullscreen-btn:hover {
transform: translateY(-1px);
border-color: rgba(59, 130, 246, 0.45);
box-shadow: 0 14px 26px rgba(15, 23, 42, 0.08);
}
#activeCart,
#welcomeScreen {
flex: 1 1 auto;
min-height: 0;
}
.content {
display: grid;
grid-template-columns: minmax(0, 1.7fr) minmax(320px, 0.95fr);
gap: 1.25rem;
padding: 1.25rem;
overflow: hidden;
}
.items-section,
.summary-card,
.grand-total {
border-radius: 26px;
border: 1px solid rgba(226, 232, 240, 0.9);
box-shadow: 0 18px 45px rgba(15, 23, 42, 0.08);
}
.items-section {
background: rgba(255, 255, 255, 0.9);
overflow: hidden;
display: flex;
flex-direction: column;
min-height: 0;
}
.section-head {
display: flex;
justify-content: space-between;
align-items: flex-end;
gap: 1rem;
padding: 1.4rem 1.5rem 1.1rem;
border-bottom: 1px solid rgba(226, 232, 240, 0.85);
background: linear-gradient(180deg, rgba(248, 250, 252, 0.9), rgba(255, 255, 255, 0.7));
}
.section-kicker {
font-size: 0.78rem;
font-weight: 700;
letter-spacing: 0.14em;
text-transform: uppercase;
color: var(--accent);
margin-bottom: 0.45rem;
}
.section-title {
margin: 0;
font-size: clamp(1.45rem, 2vw, 2rem);
font-weight: 800;
letter-spacing: -0.03em;
color: var(--primary);
}
.section-summary {
padding: 0.7rem 0.95rem;
border-radius: 999px;
background: rgba(59, 130, 246, 0.08);
color: var(--accent);
font-weight: 700;
white-space: nowrap;
}
.items-list {
flex: 1;
min-height: 0;
overflow-y: auto;
padding: 1rem 1.1rem 1.2rem;
}
.cart-item {
display: flex;
justify-content: space-between;
align-items: center;
gap: 1rem;
padding: 1rem 1.15rem;
margin-bottom: 0.85rem;
background: linear-gradient(135deg, rgba(255, 255, 255, 0.94), rgba(248, 250, 252, 0.94));
border: 1px solid rgba(226, 232, 240, 0.9);
border-radius: 22px;
box-shadow: 0 10px 24px rgba(15, 23, 42, 0.05);
}
.item-copy {
min-width: 0;
}
.item-name {
display: flex;
flex-direction: column;
gap: 0.12rem;
font-size: clamp(1rem, 1.3vw, 1.2rem);
font-weight: 800;
color: var(--primary);
word-break: break-word;
}
.item-name-primary {
color: var(--primary);
font-weight: 800;
line-height: 1.12;
}
.item-name-secondary {
color: var(--text-muted);
font-size: 0.82em;
font-weight: 600;
line-height: 1.08;
}
.item-name-ar {
direction: rtl;
}
.item-name-en {
direction: ltr;
}
.item-details {
color: var(--text-muted);
font-size: clamp(0.88rem, 1vw, 0.98rem);
margin-top: 0.3rem;
}
.item-price {
font-size: clamp(1rem, 1.35vw, 1.3rem);
font-weight: 800;
color: var(--accent);
white-space: nowrap;
text-align: end;
}
.totals-section {
display: flex;
flex-direction: column;
gap: 1rem;
min-width: 0;
min-height: 0;
}
.summary-card {
background: rgba(255, 255, 255, 0.92);
padding: 1.45rem 1.45rem 1.3rem;
}
.summary-eyebrow {
font-size: 0.78rem;
font-weight: 700;
letter-spacing: 0.14em;
text-transform: uppercase;
color: var(--accent);
margin-bottom: 0.7rem;
}
.summary-heading {
margin: 0;
font-size: clamp(1.2rem, 1.8vw, 1.55rem);
font-weight: 800;
letter-spacing: -0.03em;
color: var(--primary);
max-width: 18ch;
}
.summary-stats {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 0.8rem;
margin: 1.15rem 0 1.25rem;
}
.stat-pill {
padding: 0.95rem 1rem;
border-radius: 20px;
background: linear-gradient(180deg, rgba(248, 250, 252, 0.95), rgba(255, 255, 255, 0.92));
border: 1px solid rgba(226, 232, 240, 0.9);
}
.stat-label {
display: block;
font-size: 0.82rem;
color: var(--text-muted);
margin-bottom: 0.25rem;
}
.stat-value {
display: block;
font-size: clamp(1.1rem, 1.5vw, 1.45rem);
font-weight: 800;
color: var(--primary);
}
.total-rows {
display: flex;
flex-direction: column;
gap: 0;
}
.total-row {
display: flex;
justify-content: space-between;
align-items: center;
gap: 1rem;
padding: 0.95rem 0;
font-size: clamp(1rem, 1.2vw, 1.08rem);
color: var(--text-muted);
border-bottom: 1px solid rgba(226, 232, 240, 0.85);
}
.total-row span:last-child {
font-weight: 700;
color: var(--text);
text-align: end;
}
.total-row:last-child {
border-bottom: 0;
padding-bottom: 0;
}
.total-row.text-danger span:last-child {
color: #dc2626;
}
.total-row.text-success span:last-child {
color: #059669;
}
.grand-total {
background: linear-gradient(135deg, var(--primary), var(--accent));
color: white;
padding: 1.35rem 1.45rem 1.45rem;
margin-top: auto;
box-shadow: 0 20px 50px rgba(15, 23, 42, 0.18);
}
.grand-total-top {
display: flex;
justify-content: space-between;
align-items: flex-start;
gap: 1rem;
margin-bottom: 0.9rem;
}
.grand-total-label {
font-size: 0.82rem;
font-weight: 700;
letter-spacing: 0.16em;
text-transform: uppercase;
opacity: 0.78;
}
.grand-total-note {
margin-top: 0.45rem;
font-size: 0.96rem;
opacity: 0.88;
}
.grand-total-currency {
padding: 0.48rem 0.8rem;
border-radius: 999px;
background: rgba(255, 255, 255, 0.16);
border: 1px solid rgba(255, 255, 255, 0.18);
font-weight: 800;
letter-spacing: 0.08em;
white-space: nowrap;
}
.grand-total-amount {
font-size: clamp(2.6rem, 5.2vw, 4.65rem);
font-weight: 800;
line-height: 1.02;
letter-spacing: -0.05em;
word-break: break-word;
}
.display-footer {
display: flex;
align-items: center;
justify-content: space-between;
gap: 0.9rem 1.2rem;
flex-wrap: wrap;
padding: 0.95rem 1.3rem 1rem;
background: rgba(255, 255, 255, 0.84);
border-top: 1px solid rgba(226, 232, 240, 0.9);
backdrop-filter: blur(22px);
box-shadow: 0 -10px 24px rgba(15, 23, 42, 0.04);
}
.footer-brand {
font-weight: 800;
color: var(--primary);
letter-spacing: -0.02em;
}
.footer-meta {
display: flex;
flex-wrap: wrap;
justify-content: flex-end;
gap: 0.7rem;
}
.footer-pill {
display: inline-flex;
align-items: center;
gap: 0.45rem;
padding: 0.55rem 0.8rem;
border-radius: 999px;
background: rgba(248, 250, 252, 0.96);
border: 1px solid rgba(226, 232, 240, 0.92);
color: var(--text);
font-weight: 600;
font-size: 0.92rem;
}
.footer-pill i {
color: var(--accent);
}
.welcome-screen {
position: relative;
display: flex;
align-items: center;
justify-content: center;
margin: 1.25rem;
border-radius: 28px;
overflow: hidden;
text-align: center;
color: white;
background: #0f172a;
box-shadow: 0 20px 50px rgba(15, 23, 42, 0.18);
}
.slideshow-bg {
position: absolute;
inset: 0;
z-index: 1;
}
.slideshow-bg::after {
content: '';
position: absolute;
inset: 0;
background: linear-gradient(135deg, rgba(2, 6, 23, 0.86), rgba(15, 23, 42, 0.62), rgba(59, 130, 246, 0.28));
z-index: 2;
}
.slide {
position: absolute;
inset: 0;
background-size: cover;
background-position: center;
opacity: 0;
transform: scale(1.08);
transition: opacity 2s ease-in-out, transform 10s ease;
}
.slide.active {
opacity: 1;
transform: scale(1);
z-index: 1;
}
.welcome-content {
position: relative;
z-index: 10;
max-width: min(760px, calc(100% - 3rem));
padding: 2.25rem 2.4rem;
border-radius: 28px;
background: rgba(15, 23, 42, 0.34);
border: 1px solid rgba(255, 255, 255, 0.18);
backdrop-filter: blur(16px);
box-shadow: 0 16px 40px rgba(2, 6, 23, 0.2);
animation: fadeIn 1s ease-out;
}
.welcome-tag {
display: inline-flex;
align-items: center;
gap: 0.5rem;
padding: 0.55rem 0.9rem;
border-radius: 999px;
margin-bottom: 1.1rem;
background: rgba(255, 255, 255, 0.12);
border: 1px solid rgba(255, 255, 255, 0.16);
font-size: 0.9rem;
font-weight: 700;
letter-spacing: 0.08em;
text-transform: uppercase;
}
.welcome-logo {
max-height: 14vh;
max-width: min(260px, 58vw);
margin-bottom: 1.35rem;
filter: drop-shadow(0 4px 10px rgba(0, 0, 0, 0.32));
}
.welcome-icon {
font-size: 11vmin;
margin-bottom: 1rem;
color: rgba(255, 255, 255, 0.84);
text-shadow: 0 4px 10px rgba(0, 0, 0, 0.3);
}
.welcome-title {
margin: 0;
font-size: clamp(2rem, 4vw, 3.3rem);
font-weight: 800;
letter-spacing: -0.04em;
}
.welcome-subtitle {
margin: 1rem auto 0;
max-width: 28ch;
font-size: clamp(1rem, 1.7vw, 1.45rem);
color: rgba(255, 255, 255, 0.82);
}
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(18px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
::-webkit-scrollbar {
width: 8px;
height: 8px;
}
::-webkit-scrollbar-track {
background: rgba(226, 232, 240, 0.45);
border-radius: 999px;
}
::-webkit-scrollbar-thumb {
background: rgba(148, 163, 184, 0.7);
border-radius: 999px;
}
::-webkit-scrollbar-thumb:hover {
background: rgba(100, 116, 139, 0.95);
}
@media (max-width: 1200px) {
.content {
grid-template-columns: minmax(0, 1.45fr) 320px;
}
}
@media (max-width: 992px) {
body {
overflow: auto;
}
.header,
.display-footer {
padding-left: 1rem;
padding-right: 1rem;
}
.content {
grid-template-columns: 1fr;
padding: 1rem;
overflow: visible;
}
.welcome-screen {
margin: 1rem;
min-height: 62vh;
}
.summary-heading {
max-width: none;
}
}
@media (max-height: 820px) {
.header {
padding-top: 0.9rem;
padding-bottom: 0.9rem;
}
.content {
padding-top: 1rem;
padding-bottom: 1rem;
}
.section-head {
padding-top: 1.15rem;
padding-bottom: 0.95rem;
}
.summary-card,
.grand-total {
padding-top: 1.15rem;
padding-bottom: 1.15rem;
}
.grand-total-amount {
font-size: clamp(2.3rem, 4.5vw, 3.9rem);
}
}
</style>
</head>
<body>
<header class="header">
<div class="brand-wrap">
<div class="brand-mark">
<i class="bi bi-display"></i>
</div>
<div>
<div class="brand-title">Customer Display</div>
<div class="brand-subtitle">Live order summary for the customer-facing screen</div>
</div>
</div>
<div class="header-actions">
<div id="debugInfo" class="status-chip" style="display: none;">
<i class="bi bi-bag-check"></i>
<span id="debugInfoText">Items: 0</span>
</div>
<div class="customer-chip">
<i class="bi bi-person-circle"></i>
<span id="customerName">Welcome</span>
</div>
<button onclick="toggleFullScreen()" class="fullscreen-btn" title="Toggle Fullscreen" aria-label="Toggle Fullscreen">
<i class="bi bi-arrows-fullscreen"></i>
</button>
</div>
</header>
<div id="activeCart" class="content" style="display: none;">
<section class="items-section" aria-label="Current basket items">
<div class="section-head">
<div>
<div class="section-kicker" id="sectionKicker">Current Basket</div>
<h1 class="section-title" id="sectionTitle">Order Items</h1>
</div>
<div class="section-summary" id="sectionSummaryText">0 items • 0 qty</div>
</div>
<div id="itemsList" class="items-list"></div>
</section>
<aside class="totals-section" aria-label="Payment summary">
<div class="summary-card">
<div class="summary-eyebrow" id="summaryEyebrow">Payment Summary</div>
<h2 class="summary-heading" id="summaryHeading">Clean, balanced totals with less visual weight.</h2>
<div class="summary-stats">
<div class="stat-pill">
<span class="stat-label" id="labelItemsStat">Items</span>
<span class="stat-value" id="displayItemCount">0</span>
</div>
<div class="stat-pill">
<span class="stat-label" id="labelQuantityStat">Quantity</span>
<span class="stat-value" id="displayQuantity">0</span>
</div>
</div>
<div class="total-rows">
<div class="total-row">
<span id="labelSubtotal">Subtotal</span>
<span id="displaySubtotal">OMR 0.000</span>
</div>
<div id="displayTaxRow" class="total-row text-muted">
<span id="labelVAT">VAT</span>
<span id="displayTax">OMR 0.000</span>
</div>
<div id="displayDiscountRow" class="total-row text-danger" style="display: none;">
<span id="labelDiscount">Discount</span>
<span id="displayDiscount">- OMR 0.000</span>
</div>
<div id="displayLoyaltyRow" class="total-row text-success" style="display: none;">
<span id="labelLoyalty">Loyalty Redeemed</span>
<span id="displayLoyalty">- OMR 0.000</span>
</div>
</div>
</div>
<div class="grand-total">
<div class="grand-total-top">
<div>
<div class="grand-total-label" id="labelTotal">Total to Pay</div>
<div class="grand-total-note" id="labelTotalNote">Please review before payment</div>
</div>
<div class="grand-total-currency" id="displayCurrency">OMR</div>
</div>
<div class="grand-total-amount"><span id="displayTotalValue">0.000</span></div>
</div>
</aside>
</div>
<div id="welcomeScreen" class="welcome-screen">
<div class="slideshow-bg" id="slideshow">
<?php foreach ($slides as $i => $url): ?>
<div class="slide <?= $i === 0 ? 'active' : '' ?>" style="background-image: url('<?= htmlspecialchars($url) ?>');"></div>
<?php endforeach; ?>
</div>
<div class="welcome-content">
<div class="welcome-tag"><i class="bi bi-stars"></i> <?= htmlspecialchars($footerBrand) ?></div>
<?php if (!empty($settings['company_logo']) && file_exists($settings['company_logo'])): ?>
<img src="<?= htmlspecialchars($settings['company_logo']) ?>?v=<?= time() ?>" alt="Store Logo" class="welcome-logo">
<?php elseif (file_exists('uploads/logo.png')): ?>
<img src="uploads/logo.png?v=<?= time() ?>" alt="Store Logo" class="welcome-logo">
<?php else: ?>
<div class="welcome-icon"><i class="bi bi-shop"></i></div>
<?php endif; ?>
<h1 class="welcome-title"><?= htmlspecialchars($title) ?></h1>
<p class="welcome-subtitle"><?= htmlspecialchars($subtitle) ?></p>
</div>
</div>
<footer class="display-footer">
<div class="footer-brand"><?= htmlspecialchars($footerBrand) ?></div>
<div class="footer-meta">
<span class="footer-pill"><i class="bi bi-globe2"></i> Powered By: <?= htmlspecialchars($footerPoweredBy) ?></span>
<span class="footer-pill"><i class="bi bi-whatsapp"></i> WhatsApp: <?= htmlspecialchars($footerWhatsapp) ?></span>
<span class="footer-pill"><i class="bi bi-envelope"></i> Email: <?= htmlspecialchars($footerEmail) ?></span>
</div>
</footer>
<script>
let lastTimestamp = 0;
let currentCurrency = 'OMR';
function formatMoney(amount) {
return currentCurrency + ' ' + parseFloat(amount || 0).toFixed(3);
}
function formatMoneyValue(amount) {
return parseFloat(amount || 0).toFixed(3);
}
function formatQuantity(value) {
const qty = parseFloat(value || 0);
return Number.isInteger(qty) ? qty.toFixed(0) : qty.toFixed(2);
}
function decodeHtmlText(value) {
const textarea = document.createElement('textarea');
textarea.innerHTML = String(value ?? '');
return textarea.value;
}
function hasArabicText(value) {
return /[؀-ۿ]/.test(String(value || ''));
}
function normalizeNameText(value) {
return decodeHtmlText(String(value ?? '').replace(/<[^>]*>/g, ' '))
.replace(/\s+/g, ' ')
.trim();
}
function extractItemNameLines(item, isAr) {
const directLines = [
normalizeNameText(item.nameAr || item.name_ar || ''),
normalizeNameText(item.nameEn || item.name_en || '')
].filter(Boolean);
if (directLines.length > 0) {
return [...new Set(directLines)];
}
const rawName = String(item.name || '').trim();
if (!rawName) {
return [isAr ? 'صنف' : 'Item'];
}
const parsedLines = decodeHtmlText(
rawName
.replace(/<\s*br\s*\/?\s*>/gi, '\n')
.replace(/<\/\s*(div|p|li|tr|td|h[1-6])\s*>/gi, '\n')
.replace(/<[^>]*>/g, '')
)
.split(/\n+/)
.map(line => line.replace(/\s+/g, ' ').trim())
.filter(Boolean);
const uniqueLines = [...new Set(parsedLines)];
if (uniqueLines.length > 0) {
return uniqueLines.slice(0, 2);
}
const fallback = normalizeNameText(rawName);
return [fallback || (isAr ? 'صنف' : 'Item')];
}
function renderItemName(container, item, isAr) {
container.textContent = '';
const nameLines = extractItemNameLines(item, isAr);
nameLines.forEach((line, index) => {
const lineEl = document.createElement('div');
const arabicLine = hasArabicText(line);
lineEl.className = index === 0
? (arabicLine ? 'item-name-primary item-name-ar' : 'item-name-primary item-name-en')
: (arabicLine ? 'item-name-secondary item-name-ar' : 'item-name-secondary item-name-en');
lineEl.textContent = line;
container.appendChild(lineEl);
});
}
function maximizeWindow() {
try {
window.moveTo(0, 0);
window.resizeTo(screen.availWidth, screen.availHeight);
window.focus();
} catch (e) {}
}
function setLanguageLabels(isAr) {
if (isAr) {
document.getElementById('sectionKicker').innerText = 'السلة الحالية';
document.getElementById('sectionTitle').innerText = 'الأصناف المختارة';
document.getElementById('summaryEyebrow').innerText = 'ملخص الدفع';
document.getElementById('summaryHeading').innerText = 'عرض أوضح ومتوازن لإجمالي الطلب.';
document.getElementById('labelItemsStat').innerText = 'الأصناف';
document.getElementById('labelQuantityStat').innerText = 'الكمية';
document.getElementById('labelSubtotal').innerText = 'المجموع قبل الضريبة';
document.getElementById('labelVAT').innerText = 'الضريبة';
document.getElementById('labelDiscount').innerText = 'الخصم';
document.getElementById('labelLoyalty').innerText = 'استخدام الولاء';
document.getElementById('labelTotal').innerText = 'الإجمالي المطلوب';
document.getElementById('labelTotalNote').innerText = 'يرجى مراجعة الفاتورة قبل الدفع';
} else {
document.getElementById('sectionKicker').innerText = 'Current Basket';
document.getElementById('sectionTitle').innerText = 'Order Items';
document.getElementById('summaryEyebrow').innerText = 'Payment Summary';
document.getElementById('summaryHeading').innerText = 'Clean, balanced totals with less visual weight.';
document.getElementById('labelItemsStat').innerText = 'Items';
document.getElementById('labelQuantityStat').innerText = 'Quantity';
document.getElementById('labelSubtotal').innerText = 'Subtotal';
document.getElementById('labelVAT').innerText = 'VAT';
document.getElementById('labelDiscount').innerText = 'Discount';
document.getElementById('labelLoyalty').innerText = 'Loyalty Redeemed';
document.getElementById('labelTotal').innerText = 'Total to Pay';
document.getElementById('labelTotalNote').innerText = 'Please review before payment';
}
}
function updateDisplay(data) {
if (!data) return;
lastTimestamp = data.timestamp || 0;
currentCurrency = data.currency || 'OMR';
const items = Array.isArray(data.items) ? data.items : [];
const isAr = items.some(item => hasArabicText([
item.nameAr,
item.name_ar,
item.name,
item.nameEn,
item.name_en
].filter(Boolean).join(' ')));
setLanguageLabels(isAr);
const totalQuantity = items.reduce((sum, item) => {
return sum + (parseFloat(item.quantity || item.qty) || 0);
}, 0);
const itemLabel = isAr ? 'صنف' : 'items';
const qtyLabel = isAr ? 'كمية' : 'qty';
document.getElementById('sectionSummaryText').innerText = `${items.length} ${itemLabel} • ${formatQuantity(totalQuantity)} ${qtyLabel}`;
document.getElementById('displayItemCount').innerText = items.length;
document.getElementById('displayQuantity').innerText = formatQuantity(totalQuantity);
document.getElementById('debugInfoText').innerText = (isAr ? 'الأصناف: ' : 'Items: ') + items.length;
if (items.length > 0) {
document.getElementById('welcomeScreen').style.display = 'none';
document.getElementById('activeCart').style.display = 'grid';
document.getElementById('debugInfo').style.display = 'inline-flex';
} else {
document.getElementById('welcomeScreen').style.display = 'flex';
document.getElementById('activeCart').style.display = 'none';
document.getElementById('debugInfo').style.display = 'none';
document.getElementById('customerName').innerText = isAr ? 'أهلاً وسهلاً' : 'Welcome';
return;
}
if (data.customerName || data.customer_name) {
document.getElementById('customerName').innerText = data.customerName || data.customer_name;
} else {
document.getElementById('customerName').innerText = isAr ? 'أهلاً وسهلاً' : 'Welcome';
}
const list = document.getElementById('itemsList');
list.innerHTML = '';
items.forEach(item => {
const qty = parseFloat(item.quantity || item.qty) || 0;
const price = parseFloat(item.price) || 0;
const total = item.total ? parseFloat(item.total) : (qty * price);
const itemDiscount = parseFloat(item.discount) || 0;
let details = `${formatQuantity(qty)} x ${formatMoney(price)}`;
if (itemDiscount > 0) {
details += isAr
? ` • خصم ${formatMoney(itemDiscount)}`
: ` • Disc ${formatMoney(itemDiscount)}`;
}
const row = document.createElement('div');
row.className = 'cart-item';
const copy = document.createElement('div');
copy.className = 'item-copy';
const name = document.createElement('div');
name.className = 'item-name';
renderItemName(name, item, isAr);
const detail = document.createElement('div');
detail.className = 'item-details';
detail.textContent = details;
const priceTag = document.createElement('div');
priceTag.className = 'item-price';
priceTag.textContent = formatMoney(total);
copy.appendChild(name);
copy.appendChild(detail);
row.appendChild(copy);
row.appendChild(priceTag);
list.appendChild(row);
});
list.scrollTop = list.scrollHeight;
const vat = parseFloat(data.vat) || 0;
const subtotal = parseFloat(data.subtotal) || 0;
const subtotalBeforeVat = Math.max(subtotal - vat, 0);
document.getElementById('displaySubtotal').innerText = formatMoney(subtotalBeforeVat);
document.getElementById('displayTax').innerText = formatMoney(vat);
const discount = parseFloat(data.discount) || 0;
if (discount > 0) {
document.getElementById('displayDiscountRow').style.display = 'flex';
document.getElementById('displayDiscount').innerText = '- ' + formatMoney(discount);
} else {
document.getElementById('displayDiscountRow').style.display = 'none';
}
const loyalty = parseFloat(data.loyalty) || 0;
if (loyalty > 0) {
document.getElementById('displayLoyaltyRow').style.display = 'flex';
document.getElementById('displayLoyalty').innerText = '- ' + formatMoney(loyalty);
} else {
document.getElementById('displayLoyaltyRow').style.display = 'none';
}
document.getElementById('displayCurrency').innerText = currentCurrency;
document.getElementById('displayTotalValue').innerText = formatMoneyValue(data.total || 0);
}
window.addEventListener('storage', (e) => {
if (e.key === 'pos_cart_update') {
try {
const data = JSON.parse(e.newValue);
updateDisplay(data);
} catch (err) {
console.error('Error parsing cart data', err);
}
}
});
setInterval(() => {
const stored = localStorage.getItem('pos_cart_update');
if (stored) {
try {
const data = JSON.parse(stored);
if (data.timestamp && data.timestamp !== lastTimestamp) {
updateDisplay(data);
}
} catch (e) {}
}
}, 1000);
const stored = localStorage.getItem('pos_cart_update');
if (stored) {
try {
updateDisplay(JSON.parse(stored));
} catch (e) {}
}
function toggleFullScreen() {
if (!document.fullscreenElement) {
document.documentElement.requestFullscreen();
} else if (document.exitFullscreen) {
document.exitFullscreen();
}
}
window.addEventListener('load', () => {
maximizeWindow();
setTimeout(maximizeWindow, 250);
const slides = document.querySelectorAll('.slide');
let currentSlide = 0;
if (slides.length > 0) {
setInterval(() => {
slides[currentSlide].classList.remove('active');
currentSlide = (currentSlide + 1) % slides.length;
slides[currentSlide].classList.add('active');
}, 6000);
}
});
</script>
</body>
</html>