Shyamnath's picture
feat(core): implement unified database with multi-hotel support and session management
90537f3
{% extends "base.html" %}
{% block title %}Menu - Table {{ table_number }} - Tabble-v3{% endblock %}
{% block extra_css %}
<style>
.menu-header {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
padding: 2rem 0;
}
.dish-card {
transition: transform 0.2s;
height: 100%;
}
.dish-card:hover {
transform: translateY(-2px);
}
.dish-image {
height: 200px;
object-fit: cover;
}
.category-tab {
border-radius: 25px;
margin: 0 5px;
}
.cart-fab {
position: fixed;
bottom: 20px;
right: 20px;
width: 60px;
height: 60px;
border-radius: 50%;
z-index: 1000;
}
.cart-count {
position: absolute;
top: -5px;
right: -5px;
background: #dc3545;
color: white;
border-radius: 50%;
width: 25px;
height: 25px;
display: flex;
align-items: center;
justify-content: center;
font-size: 12px;
font-weight: bold;
}
</style>
{% endblock %}
{% block content %}
<!-- Menu Header -->
<div class="menu-header">
<div class="container text-center">
<h2>
<i class="fas fa-utensils me-2"></i>
Restaurant Menu
</h2>
<p class="mb-0">Table {{ table_number }} | Order ID: {{ unique_id }}</p>
</div>
</div>
<div class="container mt-4">
<!-- Table Info & Quick Actions -->
<div class="row mb-4">
<div class="col-md-8">
<div class="card">
<div class="card-body py-2">
<div class="row align-items-center">
<div class="col-sm-6">
<small class="text-muted">Dining at:</small>
<strong class="d-block">Demo Restaurant</strong>
</div>
<div class="col-sm-6 text-sm-end">
<small class="text-muted">Table {{ table_number }}</small>
<div class="badge bg-success ms-2">
<i class="fas fa-wifi me-1"></i>Connected
</div>
</div>
</div>
</div>
</div>
</div>
<div class="col-md-4">
<div class="d-flex gap-2">
<button class="btn btn-outline-primary flex-fill" onclick="viewOrders()">
<i class="fas fa-history me-1"></i>Orders
</button>
<button class="btn btn-outline-success flex-fill" onclick="callWaiter()">
<i class="fas fa-bell me-1"></i>Call Waiter
</button>
</div>
</div>
</div>
<!-- Category Tabs -->
<div class="row mb-4">
<div class="col-12">
<div class="d-flex flex-wrap justify-content-center">
<button class="btn btn-primary category-tab active" onclick="filterCategory('all')">
All Items
</button>
<button class="btn btn-outline-primary category-tab" onclick="filterCategory('starters')">
Starters
</button>
<button class="btn btn-outline-primary category-tab" onclick="filterCategory('main-course')">
Main Course
</button>
<button class="btn btn-outline-primary category-tab" onclick="filterCategory('beverages')">
Beverages
</button>
<button class="btn btn-outline-primary category-tab" onclick="filterCategory('desserts')">
Desserts
</button>
</div>
</div>
</div>
<!-- Menu Items -->
<div class="row" id="menu-items">
<!-- Sample Dish 1 -->
<div class="col-md-4 col-sm-6 mb-4 menu-item" data-category="main-course">
<div class="card dish-card">
<img src="https://images.unsplash.com/photo-1563379091339-03246963d96c?w=400&h=200&fit=crop"
class="card-img-top dish-image" alt="Chicken Biryani">
<div class="card-body">
<div class="d-flex justify-content-between align-items-start mb-2">
<h6 class="card-title mb-0">Chicken Biryani</h6>
<span class="badge bg-warning">
<i class="fas fa-pepper-hot"></i>
</span>
</div>
<p class="card-text text-muted small">
Aromatic basmati rice cooked with tender chicken and traditional spices
</p>
<div class="d-flex justify-content-between align-items-center">
<strong class="text-success">₹250</strong>
<div class="btn-group btn-group-sm">
<button class="btn btn-outline-secondary" onclick="updateQuantity('biryani', -1)">-</button>
<span class="btn btn-outline-secondary" id="qty-biryani">0</span>
<button class="btn btn-outline-secondary" onclick="updateQuantity('biryani', 1)">+</button>
</div>
</div>
</div>
</div>
</div>
<!-- Sample Dish 2 -->
<div class="col-md-4 col-sm-6 mb-4 menu-item" data-category="starters">
<div class="card dish-card">
<img src="https://images.unsplash.com/photo-1599487488170-d11ec9c172f0?w=400&h=200&fit=crop"
class="card-img-top dish-image" alt="Paneer Tikka">
<div class="card-body">
<div class="d-flex justify-content-between align-items-start mb-2">
<h6 class="card-title mb-0">Paneer Tikka</h6>
<span class="badge bg-success">
<i class="fas fa-leaf"></i>
</span>
</div>
<p class="card-text text-muted small">
Marinated cottage cheese cubes grilled to perfection
</p>
<div class="d-flex justify-content-between align-items-center">
<strong class="text-success">₹180</strong>
<div class="btn-group btn-group-sm">
<button class="btn btn-outline-secondary" onclick="updateQuantity('paneer', -1)">-</button>
<span class="btn btn-outline-secondary" id="qty-paneer">0</span>
<button class="btn btn-outline-secondary" onclick="updateQuantity('paneer', 1)">+</button>
</div>
</div>
</div>
</div>
</div>
<!-- Sample Dish 3 -->
<div class="col-md-4 col-sm-6 mb-4 menu-item" data-category="beverages">
<div class="card dish-card">
<img src="https://images.unsplash.com/photo-1571091718767-18b5b1457add?w=400&h=200&fit=crop"
class="card-img-top dish-image" alt="Mango Lassi">
<div class="card-body">
<div class="d-flex justify-content-between align-items-start mb-2">
<h6 class="card-title mb-0">Mango Lassi</h6>
<span class="badge bg-info">
<i class="fas fa-snowflake"></i>
</span>
</div>
<p class="card-text text-muted small">
Refreshing yogurt-based drink with fresh mango pulp
</p>
<div class="d-flex justify-content-between align-items-center">
<strong class="text-success">₹80</strong>
<div class="btn-group btn-group-sm">
<button class="btn btn-outline-secondary" onclick="updateQuantity('lassi', -1)">-</button>
<span class="btn btn-outline-secondary" id="qty-lassi">0</span>
<button class="btn btn-outline-secondary" onclick="updateQuantity('lassi', 1)">+</button>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Empty Menu State -->
<div id="no-items" class="text-center py-5" style="display: none;">
<i class="fas fa-search fa-3x text-muted mb-3"></i>
<p class="text-muted">No items found in this category</p>
</div>
</div>
<!-- Floating Cart Button -->
<button class="btn btn-success cart-fab" onclick="openCart()" id="cart-btn" style="display: none;">
<i class="fas fa-shopping-cart"></i>
<span class="cart-count" id="cart-count">0</span>
</button>
<!-- Cart Modal -->
<div class="modal fade" id="cartModal" tabindex="-1">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">
<i class="fas fa-shopping-cart me-2"></i>Your Order
</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<div id="cart-items">
<!-- Cart items will be populated here -->
</div>
<hr>
<div class="d-flex justify-content-between">
<strong>Total: ₹<span id="cart-total">0</span></strong>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">
Continue Shopping
</button>
<button type="button" class="btn btn-success" onclick="placeOrder()">
<i class="fas fa-check me-2"></i>Place Order
</button>
</div>
</div>
</div>
</div>
{% endblock %}
{% block extra_js %}
<script>
let cart = {};
let dishes = {
'biryani': { name: 'Chicken Biryani', price: 250 },
'paneer': { name: 'Paneer Tikka', price: 180 },
'lassi': { name: 'Mango Lassi', price: 80 }
};
function filterCategory(category) {
// Update active tab
document.querySelectorAll('.category-tab').forEach(tab => {
tab.classList.remove('active', 'btn-primary');
tab.classList.add('btn-outline-primary');
});
event.target.classList.remove('btn-outline-primary');
event.target.classList.add('active', 'btn-primary');
// Filter items
const items = document.querySelectorAll('.menu-item');
let visibleCount = 0;
items.forEach(item => {
const itemCategory = item.dataset.category;
const show = category === 'all' || itemCategory === category;
item.style.display = show ? 'block' : 'none';
if (show) visibleCount++;
});
document.getElementById('no-items').style.display = visibleCount === 0 ? 'block' : 'none';
}
function updateQuantity(dishId, change) {
if (!cart[dishId]) cart[dishId] = 0;
cart[dishId] += change;
if (cart[dishId] < 0) cart[dishId] = 0;
if (cart[dishId] === 0) delete cart[dishId];
document.getElementById(`qty-${dishId}`).textContent = cart[dishId] || 0;
updateCartDisplay();
}
function updateCartDisplay() {
const totalItems = Object.values(cart).reduce((sum, qty) => sum + qty, 0);
const cartBtn = document.getElementById('cart-btn');
const cartCount = document.getElementById('cart-count');
if (totalItems > 0) {
cartBtn.style.display = 'block';
cartCount.textContent = totalItems;
} else {
cartBtn.style.display = 'none';
}
}
function openCart() {
const cartItems = document.getElementById('cart-items');
const cartTotal = document.getElementById('cart-total');
let html = '';
let total = 0;
for (let dishId in cart) {
const dish = dishes[dishId];
const qty = cart[dishId];
const subtotal = dish.price * qty;
total += subtotal;
html += `
<div class="d-flex justify-content-between align-items-center mb-2">
<div>
<strong>${dish.name}</strong>
<small class="text-muted d-block">₹${dish.price} each</small>
</div>
<div class="d-flex align-items-center">
<span class="me-3">Qty: ${qty}</span>
<strong>₹${subtotal}</strong>
</div>
</div>
`;
}
if (html === '') {
html = '<p class="text-muted text-center">Your cart is empty</p>';
}
cartItems.innerHTML = html;
cartTotal.textContent = total;
new bootstrap.Modal(document.getElementById('cartModal')).show();
}
function placeOrder() {
if (Object.keys(cart).length === 0) {
alert('Your cart is empty!');
return;
}
const total = Object.keys(cart).reduce((sum, dishId) => {
return sum + (dishes[dishId].price * cart[dishId]);
}, 0);
const orderSummary = Object.keys(cart).map(dishId => {
return `${cart[dishId]}x ${dishes[dishId].name}`;
}).join(', ');
if (confirm(`Place order for ₹${total}?\n\nItems: ${orderSummary}`)) {
alert('Order placed successfully! You will receive updates on the status.');
cart = {};
updateCartDisplay();
// Reset all quantities
Object.keys(dishes).forEach(dishId => {
document.getElementById(`qty-${dishId}`).textContent = '0';
});
bootstrap.Modal.getInstance(document.getElementById('cartModal')).hide();
}
}
function viewOrders() {
alert('Order history feature will show your past orders');
}
function callWaiter() {
if (confirm('Call waiter to your table?')) {
alert('Waiter has been notified and will be at your table shortly!');
}
}
</script>
{% endblock %}