551 lines
27 KiB
PHP
551 lines
27 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
require_once __DIR__ . '/db/config.php';
|
|
require_once __DIR__ . '/includes/functions.php';
|
|
|
|
require_permission('pos_view');
|
|
|
|
$pdo = db();
|
|
$currentUser = get_logged_user();
|
|
|
|
// Fetch outlets based on user assignment
|
|
if (has_permission('all')) {
|
|
$outlets = $pdo->query("SELECT * FROM outlets ORDER BY name")->fetchAll();
|
|
} else {
|
|
$stmt = $pdo->prepare("
|
|
SELECT o.* FROM outlets o
|
|
JOIN user_outlets uo ON o.id = uo.outlet_id
|
|
WHERE uo.user_id = ?
|
|
ORDER BY o.name
|
|
");
|
|
$stmt->execute([$currentUser['id']]);
|
|
$outlets = $stmt->fetchAll();
|
|
}
|
|
|
|
$outlet_id = isset($_GET['outlet_id']) ? (int)$_GET['outlet_id'] : (count($outlets) > 0 ? (int)$outlets[0]['id'] : 1);
|
|
|
|
// Security check: ensure user has access to this outlet
|
|
if (!has_permission('all')) {
|
|
$has_access = false;
|
|
foreach ($outlets as $o) {
|
|
if ($o['id'] == $outlet_id) {
|
|
$has_access = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!$has_access && count($outlets) > 0) {
|
|
$outlet_id = (int)$outlets[0]['id'];
|
|
}
|
|
}
|
|
|
|
$categories = $pdo->query("SELECT * FROM categories ORDER BY sort_order")->fetchAll();
|
|
$all_products = $pdo->query("SELECT p.*, c.name as category_name FROM products p JOIN categories c ON p.category_id = c.id")->fetchAll();
|
|
$payment_types = $pdo->query("SELECT * FROM payment_types WHERE is_active = 1 ORDER BY id")->fetchAll();
|
|
|
|
// Fetch variants
|
|
$variants_raw = $pdo->query("SELECT * FROM product_variants ORDER BY price_adjustment ASC")->fetchAll();
|
|
$variants_by_product = [];
|
|
foreach ($variants_raw as $v) {
|
|
$variants_by_product[$v['product_id']][] = $v;
|
|
}
|
|
|
|
$table_id = $_GET['table'] ?? '1'; // Default table
|
|
$settings = get_company_settings();
|
|
$order_type = $_GET['order_type'] ?? 'takeaway';
|
|
|
|
$current_outlet_name = 'Unknown Outlet';
|
|
foreach ($outlets as $o) {
|
|
if ($o['id'] == $outlet_id) {
|
|
$current_outlet_name = $o['name'];
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Fetch Loyalty Settings
|
|
$loyalty_stmt = $pdo->query("SELECT * FROM loyalty_settings WHERE id = 1");
|
|
$loyalty_settings = $loyalty_stmt->fetch(PDO::FETCH_ASSOC);
|
|
if (!$loyalty_settings) {
|
|
$loyalty_settings = ['is_enabled' => 0, 'points_per_order' => 0, 'points_for_free_meal' => 0];
|
|
}
|
|
?>
|
|
<!doctype html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="utf-8" />
|
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
<title><?= htmlspecialchars($settings['company_name']) ?> - POS</title>
|
|
<?php if (!empty($settings['favicon_url'])): ?>
|
|
<link rel="icon" href="<?= htmlspecialchars($settings['favicon_url']) ?>">
|
|
<?php endif; ?>
|
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.5/font/bootstrap-icons.css" rel="stylesheet">
|
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700&family=Noto+Sans+Arabic:wght@400;600;700&display=swap" rel="stylesheet">
|
|
<link rel="stylesheet" href="assets/css/custom.css?v=<?= time() ?>">
|
|
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
|
|
<style>
|
|
body { height: 100vh; overflow: hidden; font-family: 'Inter', 'Noto Sans Arabic', sans-serif; } /* Fix body for scrolling areas */
|
|
.scrollable-y { overflow-y: auto; height: 100%; scrollbar-width: thin; }
|
|
.category-sidebar { height: calc(100vh - 60px); background: #f8f9fa; }
|
|
.product-area { height: calc(100vh - 60px); background: #fff; }
|
|
.cart-sidebar { height: calc(100vh - 60px); background: #fff; border-left: 1px solid #dee2e6; display: flex; flex-direction: column; }
|
|
.product-card { transition: transform 0.1s; cursor: pointer; }
|
|
.product-card:active { transform: scale(0.98); }
|
|
.category-btn { text-align: left; border: none; background: none; padding: 10px 15px; width: 100%; display: block; border-radius: 8px; color: #333; font-weight: 500; font-size: 0.95rem; }
|
|
.category-btn:hover { background-color: #e9ecef; }
|
|
.category-btn.active { background-color: #0d6efd; color: white; }
|
|
.search-dropdown { position: absolute; width: 100%; z-index: 1000; max-height: 200px; overflow-y: auto; display: none; }
|
|
.payment-btn { height: 70px; font-size: 1.05rem; font-weight: bold; }
|
|
.btn-lg { font-size: 1.1rem; padding: 0.75rem 1rem; }
|
|
.btn-sm { font-size: 0.85rem; }
|
|
.navbar-brand { font-size: 1.15rem; }
|
|
.card-title { font-size: 0.9rem; }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<!-- Navbar -->
|
|
<nav class="navbar navbar-expand-lg navbar-light bg-white border-bottom shadow-sm" style="height: 60px;">
|
|
<div class="container-fluid">
|
|
<a href="pos.php" class="navbar-brand d-flex align-items-center gap-2">
|
|
<?php if (!empty($settings['logo_url'])): ?>
|
|
<img src="<?= htmlspecialchars($settings['logo_url']) ?>" alt="Logo" style="height: 32px; width: auto;">
|
|
<?php endif; ?>
|
|
<span class="fw-bold d-none d-md-block"><?= htmlspecialchars($settings['company_name']) ?></span>
|
|
</a>
|
|
|
|
<div class="d-flex align-items-center gap-2">
|
|
<?php if (has_permission('kitchen_view')): ?>
|
|
<a href="kitchen.php" class="btn btn-sm btn-outline-secondary">Kitchen View</a>
|
|
<?php endif; ?>
|
|
|
|
<?php if (has_permission('pos_add')): ?>
|
|
<button class="btn btn-sm btn-outline-danger" id="recall-bill-btn"><i class="bi bi-clock-history"></i> Recall Bill</button>
|
|
<?php endif; ?>
|
|
|
|
<button class="btn btn-sm btn-outline-warning" onclick="showRatingQR()">
|
|
<i class="bi bi-qr-code"></i> Rating QR
|
|
</button>
|
|
|
|
<div id="current-table-display" class="badge bg-light text-dark border px-3 py-2" style="display: none; font-size: 0.9rem;">
|
|
Table <?= htmlspecialchars((string)$table_id) ?>
|
|
</div>
|
|
|
|
<div class="dropdown">
|
|
<button class="btn btn-sm btn-outline-dark dropdown-toggle" type="button" data-bs-toggle="dropdown">
|
|
<i class="bi bi-person-circle"></i> <?= htmlspecialchars($currentUser['username']) ?>
|
|
</button>
|
|
<ul class="dropdown-menu dropdown-menu-end">
|
|
<li><a class="dropdown-item" href="admin/"><i class="bi bi-shield-lock"></i> Admin Panel</a></li>
|
|
<li><hr class="dropdown-divider"></li>
|
|
<li><a class="dropdown-item text-danger" href="logout.php"><i class="bi bi-box-arrow-right"></i> Logout</a></li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</nav>
|
|
|
|
<div class="container-fluid p-0">
|
|
<div class="row g-0">
|
|
|
|
<!-- Left Sidebar: Categories -->
|
|
<div class="col-md-2 d-none d-md-block category-sidebar scrollable-y p-3 border-end">
|
|
<h6 class="text-uppercase text-muted small fw-bold mb-3 ms-1">Categories</h6>
|
|
<button class="category-btn active mb-1" onclick="filterCategory('all', this)">
|
|
<i class="bi bi-grid me-2"></i> All Items
|
|
</button>
|
|
<?php foreach ($categories as $category): ?>
|
|
<button class="category-btn mb-1" onclick="filterCategory(<?= $category['id'] ?>, this)">
|
|
<div class="d-flex align-items-center">
|
|
<?php if (!empty($category['image_url'])): ?>
|
|
<img src="<?= htmlspecialchars($category['image_url']) ?>" style="width: 24px; height: 24px; border-radius: 4px; object-fit: cover;" class="me-2">
|
|
<?php else: ?>
|
|
<i class="bi bi-tag me-2"></i>
|
|
<?php endif; ?>
|
|
<div>
|
|
<div class="category-name"><?= htmlspecialchars($category['name']) ?></div>
|
|
<?php if (!empty($category['name_ar'])): ?>
|
|
<div class="small opacity-75" dir="rtl" style="font-size: 0.75rem;"><?= htmlspecialchars($category['name_ar']) ?></div>
|
|
<?php endif; ?>
|
|
</div>
|
|
</div>
|
|
</button>
|
|
<?php endforeach; ?>
|
|
</div>
|
|
|
|
<!-- Middle: Products -->
|
|
<div class="col-md-7 col-12 product-area scrollable-y p-4 bg-light">
|
|
<!-- Mobile Category Select (Visible only on small screens) -->
|
|
<div class="d-md-none mb-3">
|
|
<select class="form-select" onchange="filterCategory(this.value)">
|
|
<option value="all">All Categories</option>
|
|
<?php foreach ($categories as $cat): ?>
|
|
<option value="<?= $cat['id'] ?>"><?= htmlspecialchars($cat['name']) ?> <?= !empty($cat['name_ar']) ? '(' . $cat['name_ar'] . ')' : '' ?></option>
|
|
<?php endforeach; ?>
|
|
</select>
|
|
</div>
|
|
|
|
<!-- Product Search Bar & Outlet Selector -->
|
|
<div class="mb-3 row g-2">
|
|
<div class="col-md-4 col-12">
|
|
<select class="form-select shadow-sm" id="outlet-select" onchange="const url = new URL(window.location); url.searchParams.set('outlet_id', this.value); window.location = url;">
|
|
<?php if (empty($outlets)): ?>
|
|
<option value="1">Main Outlet</option>
|
|
<?php else: ?>
|
|
<?php foreach ($outlets as $outlet): ?>
|
|
<option value="<?= $outlet['id'] ?>" <?= $outlet_id == $outlet['id'] ? 'selected' : '' ?>>
|
|
<?= htmlspecialchars($outlet['name']) ?>
|
|
</option>
|
|
<?php endforeach; ?>
|
|
<?php endif; ?>
|
|
</select>
|
|
</div>
|
|
<div class="col-md-8 col-12">
|
|
<div class="input-group shadow-sm">
|
|
<span class="input-group-text bg-white border-end-0"><i class="bi bi-search text-muted"></i></span>
|
|
<input type="text" id="product-search-input" class="form-control border-start-0 ps-0" placeholder="Search products..." autocomplete="off">
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row row-cols-2 row-cols-lg-5 g-3" id="products-container">
|
|
<?php foreach ($all_products as $product):
|
|
$has_variants = !empty($variants_by_product[$product['id']]);
|
|
$effective_price = get_product_price($product);
|
|
$is_promo = $effective_price < $product['price'];
|
|
$stock = intval($product['stock_quantity'] ?? 0);
|
|
?>
|
|
<div class="col product-item" data-category-id="<?= $product['category_id'] ?>">
|
|
<div class="card h-100 border-0 shadow-sm product-card add-to-cart"
|
|
data-id="<?= $product['id'] ?>"
|
|
data-name="<?= htmlspecialchars($product['name']) ?>"
|
|
data-name-ar="<?= htmlspecialchars($product['name_ar'] ?? '') ?>"
|
|
data-price="<?= $effective_price ?>"
|
|
data-has-variants="<?= $has_variants ? 'true' : 'false' ?>">
|
|
<div class="position-relative">
|
|
<img src="https://picsum.photos/seed/<?= $product['id'] ?>/300/200" class="card-img-top object-fit-cover" alt="..." style="height: 120px;">
|
|
<div class="position-absolute bottom-0 end-0 m-2">
|
|
<?php if ($is_promo): ?>
|
|
<span class="badge bg-danger rounded-pill shadow-sm"><?= format_currency($effective_price) ?></span>
|
|
<span class="badge bg-dark rounded-pill opacity-75 text-decoration-line-through x-small" style="font-size: 0.7rem;"><?= format_currency($product['price']) ?></span>
|
|
<?php else: ?>
|
|
<span class="badge bg-dark rounded-pill"><?= format_currency($product['price']) ?></span>
|
|
<?php endif; ?>
|
|
</div>
|
|
<?php if ($is_promo): ?>
|
|
<div class="position-absolute top-0 start-0 m-2">
|
|
<span class="badge bg-warning text-dark fw-bold rounded-pill">SALE</span>
|
|
</div>
|
|
<?php endif; ?>
|
|
|
|
<!-- Stock Indicator -->
|
|
<div class="position-absolute top-0 end-0 m-2">
|
|
<span class="badge <?= $stock <= 5 ? 'bg-danger' : 'bg-success' ?> opacity-75" style="font-size: 0.7rem;">
|
|
Stock: <?= $stock ?>
|
|
</span>
|
|
</div>
|
|
</div>
|
|
<div class="card-body p-2">
|
|
<h6 class="card-title fw-bold small mb-1 text-truncate"><?= htmlspecialchars($product['name']) ?></h6>
|
|
<?php if (!empty($product['name_ar'])): ?>
|
|
<div class="text-primary small fw-semibold text-truncate mb-1" dir="rtl" style="font-size: 0.8rem;"><?= htmlspecialchars($product['name_ar']) ?></div>
|
|
<?php endif; ?>
|
|
<p class="card-text small text-muted text-truncate mb-0"><?= htmlspecialchars($product['category_name']) ?></p>
|
|
<?php if ($has_variants): ?>
|
|
<span class="badge bg-light text-secondary border mt-1">Options</span>
|
|
<?php endif; ?>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<?php endforeach; ?>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Right Sidebar: Cart & Order Info -->
|
|
<div class="col-md-3 col-12 cart-sidebar border-start p-0 d-flex flex-column">
|
|
|
|
<!-- Top Section: Customer & Type -->
|
|
<div class="p-3 border-bottom bg-white">
|
|
<!-- Order Type -->
|
|
<div class="btn-group w-100 mb-3" role="group">
|
|
<input type="radio" class="btn-check" name="order_type" id="ot-takeaway" value="takeaway" <?= $order_type === 'takeaway' ? 'checked' : '' ?>>
|
|
<label class="btn btn-outline-primary btn-sm" for="ot-takeaway">Takeaway</label>
|
|
|
|
<input type="radio" class="btn-check" name="order_type" id="ot-dine-in" value="dine-in" <?= $order_type === 'dine-in' ? 'checked' : '' ?>>
|
|
<label class="btn btn-outline-primary btn-sm" for="ot-dine-in">Dine-In</label>
|
|
|
|
<input type="radio" class="btn-check" name="order_type" id="ot-delivery" value="delivery" <?= $order_type === 'delivery' ? 'checked' : '' ?>>
|
|
<label class="btn btn-outline-primary btn-sm" for="ot-delivery">Delivery</label>
|
|
</div>
|
|
|
|
<!-- Customer Search -->
|
|
<div class="position-relative">
|
|
<div class="input-group input-group-sm">
|
|
<span class="input-group-text bg-white border-end-0"><i class="bi bi-person"></i></span>
|
|
<input type="text" class="form-control border-start-0 ps-0" id="customer-search" placeholder="Search Customer..." autocomplete="off">
|
|
<button class="btn btn-outline-secondary d-none" type="button" id="clear-customer"><i class="bi bi-x"></i></button>
|
|
<button class="btn btn-outline-primary" type="button" data-bs-toggle="modal" data-bs-target="#addCustomerModal" title="Add New Customer">
|
|
<i class="bi bi-plus-lg"></i>
|
|
</button>
|
|
</div>
|
|
<div class="list-group shadow-sm search-dropdown" id="customer-results"></div>
|
|
<input type="hidden" id="selected-customer-id">
|
|
|
|
<div id="customer-info" class="small text-success mt-1 d-none">
|
|
<i class="bi bi-check-circle-fill me-1"></i> <span id="customer-name-display"></span>
|
|
</div>
|
|
|
|
<!-- Loyalty Section (Hidden if disabled) -->
|
|
<?php if ($loyalty_settings['is_enabled']): ?>
|
|
<div id="loyalty-section" class="d-none mt-2 p-2 bg-warning-subtle rounded border border-warning">
|
|
<div class="d-flex justify-content-between align-items-center">
|
|
<div>
|
|
<span class="d-block fw-bold small text-warning-emphasis">Loyalty Points</span>
|
|
<span id="loyalty-points-display" class="fw-bold fs-5">0</span>
|
|
</div>
|
|
<div class="d-flex flex-column gap-1">
|
|
<button id="redeem-loyalty-btn" class="btn btn-sm btn-success shadow-sm" disabled>
|
|
<i class="bi bi-gift-fill me-1"></i> Redeem
|
|
</button>
|
|
<button id="view-points-history-btn" class="btn btn-sm btn-outline-primary border-0 shadow-none p-0 text-decoration-underline" style="font-size: 0.7rem;">
|
|
<i class="bi bi-clock-history"></i> View History
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<div id="loyalty-message" class="small text-muted mt-1 fst-italic" style="font-size: 0.75rem;"></div>
|
|
</div>
|
|
<?php endif; ?>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Cart Items (Flex Grow) -->
|
|
<div class="flex-grow-1 overflow-auto p-3 bg-white" id="cart-items">
|
|
<div class="text-center text-muted mt-5">
|
|
<i class="bi bi-basket3 fs-1 text-light"></i>
|
|
<p class="mt-2">Cart is empty</p>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Bottom: Totals & Action -->
|
|
<div class="p-3 border-top bg-light mt-auto">
|
|
<div class="d-flex justify-content-between mb-2">
|
|
<span class="text-muted">Subtotal</span>
|
|
<span class="fw-bold" id="cart-subtotal"><?= format_currency(0) ?></span>
|
|
</div>
|
|
|
|
<!-- VAT Field -->
|
|
<div class="d-flex justify-content-between align-items-center mb-2">
|
|
<span class="text-muted">VAT (<?= htmlspecialchars((string)($settings['vat_rate'] ?? 0)) ?>%)</span>
|
|
<div class="input-group input-group-sm w-50">
|
|
<span class="input-group-text bg-white border-end-0 text-muted">+</span>
|
|
<input type="number" id="cart-vat-input" class="form-control border-start-0 text-end bg-white" value="0" step="0.01" readonly>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="d-flex justify-content-between mb-3">
|
|
<span class="fs-5 fw-bold">Total</span>
|
|
<span class="fs-4 fw-bold text-primary" id="cart-total-price"><?= format_currency(0) ?></span>
|
|
</div>
|
|
|
|
<?php if (has_permission('pos_add')): ?>
|
|
<div class="mb-2">
|
|
<button class="btn btn-outline-danger w-100 py-2 fw-bold mb-2 btn-sm" onclick="clearCart()">
|
|
<i class="bi bi-trash me-1"></i> CLEAR CART
|
|
</button>
|
|
<div class="d-flex gap-2">
|
|
<button class="btn btn-primary w-50 btn-lg shadow-sm fw-bold" id="quick-order-btn" disabled>
|
|
QUICK ORDER <i class="bi bi-lightning-fill ms-1"></i>
|
|
</button>
|
|
<button class="btn btn-warning w-50 btn-lg shadow-sm text-white fw-bold" id="place-order-btn" disabled>
|
|
PLACE ORDER <i class="bi bi-clock ms-1"></i>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<?php else: ?>
|
|
<div class="alert alert-warning small py-2 mb-0 text-center">
|
|
<i class="bi bi-info-circle me-1"></i> View Only Mode
|
|
</div>
|
|
<?php endif; ?>
|
|
|
|
<div class="text-center mt-3 text-muted small" style="font-size: 0.7rem;">
|
|
Powered By Abidarcafe @2026
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Toast Container -->
|
|
<div class="toast-container position-fixed bottom-0 start-50 translate-middle-x p-3" id="toast-container" style="z-index: 1060;"></div>
|
|
|
|
<!-- Recall Order Modal -->
|
|
<div class="modal fade" id="recallOrderModal" tabindex="-1" aria-hidden="true">
|
|
<div class="modal-dialog modal-lg modal-dialog-centered">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title fw-bold">Recall Unpaid Bill</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
</div>
|
|
<div class="modal-body bg-light">
|
|
<div id="recall-orders-list" class="list-group">
|
|
<!-- Orders injected via JS -->
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Table Selection Modal -->
|
|
<div class="modal fade" id="tableSelectionModal" tabindex="-1" aria-hidden="true" data-bs-backdrop="static">
|
|
<div class="modal-dialog modal-lg modal-dialog-centered">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title fw-bold" id="tableSelectionModalLabel">Select Table</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close" id="close-table-modal" style="display:none;"></button>
|
|
</div>
|
|
<div class="modal-body bg-light">
|
|
<div id="table-list-container" class="row g-3"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Variant Selection Modal -->
|
|
<div class="modal fade" id="variantSelectionModal" tabindex="-1" aria-hidden="true">
|
|
<div class="modal-dialog modal-dialog-centered">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title" id="variantModalTitle">Select Option</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<div id="variant-list" class="list-group">
|
|
<!-- Variants injected by JS -->
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Add Customer Modal -->
|
|
<div class="modal fade" id="addCustomerModal" tabindex="-1" aria-hidden="true">
|
|
<div class="modal-dialog modal-dialog-centered modal-sm">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title fs-6 fw-bold">Add New 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 for="new-customer-name" class="form-label small text-muted">Customer Name</label>
|
|
<input type="text" class="form-control form-control-sm" id="new-customer-name" placeholder="Enter name">
|
|
</div>
|
|
<div class="mb-3">
|
|
<label for="new-customer-phone" class="form-label small text-muted">Phone Number</label>
|
|
<input type="text" class="form-control form-control-sm" id="new-customer-phone" placeholder="Enter phone">
|
|
</div>
|
|
<div class="d-grid">
|
|
<button type="button" class="btn btn-primary btn-sm" id="save-new-customer">Save Customer</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Points History Modal -->
|
|
<div class="modal fade" id="pointsHistoryModal" tabindex="-1" aria-hidden="true">
|
|
<div class="modal-dialog modal-dialog-centered">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title fw-bold">Points History</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
</div>
|
|
<div class="modal-body p-0">
|
|
<div class="table-responsive">
|
|
<table class="table table-sm table-hover mb-0">
|
|
<thead class="table-light">
|
|
<tr>
|
|
<th class="ps-3">Date</th>
|
|
<th>Action</th>
|
|
<th class="text-end pe-3">Points</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="points-history-body">
|
|
<!-- History rows injected via JS -->
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
<div id="points-history-empty" class="text-center py-4 text-muted d-none">
|
|
<i class="bi bi-calendar-x fs-2"></i>
|
|
<p class="mt-2">No history found</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Payment Selection Modal -->
|
|
<div class="modal fade" id="paymentSelectionModal" tabindex="-1" aria-hidden="true" data-bs-backdrop="static">
|
|
<div class="modal-dialog modal-dialog-centered">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title fw-bold">Select Payment Method</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" id="payment-methods-container">
|
|
<!-- Injected via JS -->
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Rating QR Modal -->
|
|
<div class="modal fade" id="ratingQRModal" tabindex="-1" aria-hidden="true">
|
|
<div class="modal-dialog modal-dialog-centered">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title fw-bold">Rating & Feedback QR</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
</div>
|
|
<div class="modal-body text-center p-4">
|
|
<p class="text-muted mb-4">Ask your customers to scan this QR code to rate your service and staff.</p>
|
|
<div id="rating-qr-container" class="mb-4">
|
|
<!-- QR Code will be generated here -->
|
|
<div class="bg-light p-4 rounded-4 d-inline-block shadow-sm">
|
|
<?php
|
|
$protocol = isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on' ? "https" : "http";
|
|
$host = $_SERVER['HTTP_HOST'];
|
|
$ratingUrl = $protocol . "://" . $host . "/rate.php";
|
|
|
|
// Using a public QR generator for simplicity
|
|
$qrUrl = "https://api.qrserver.com/v1/create-qr-code/?size=250x250&data=" . urlencode($ratingUrl);
|
|
?>
|
|
<img src="<?= $qrUrl ?>" alt="Rating QR Code" class="img-fluid" style="width: 200px; height: 200px;">
|
|
</div>
|
|
</div>
|
|
<div class="alert alert-info small py-2">
|
|
<i class="bi bi-link-45deg me-1"></i> URL: <a href="<?= $ratingUrl ?>" target="_blank" class="text-decoration-none"><?= $ratingUrl ?></a>
|
|
</div>
|
|
<button class="btn btn-primary w-100 rounded-pill py-2 fw-bold" onclick="window.print()">
|
|
<i class="bi bi-printer me-2"></i> Print QR Code
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
|
<script src="assets/js/main.js?v=<?= time() ?>"></script>
|
|
<script>
|
|
function showRatingQR() {
|
|
const modal = new bootstrap.Modal(document.getElementById('ratingQRModal'));
|
|
modal.show();
|
|
}
|
|
</script>
|
|
</body>
|
|
</html>
|