lokesh341's picture
Update templates/menu.html (#1)
c836e6c verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Menu</title>
<!-- Bootstrap CSS -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/css/bootstrap.min.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons/font/bootstrap-icons.css" rel="stylesheet">
<!-- Preload Critical Resources -->
<link rel="preload" href="/static/placeholder.mp4" as="video">
{% for section, items in ordered_menu.items() %}
{% for item in items[:1] %}
<link rel="preload" href="{{ item.Video1__c | default('/static/placeholder.mp4') }}" as="video" fetchpriority="high">
{% endfor %}
{% endfor %}
<style>
body {
font-family: Arial, sans-serif;
background-color: #fdf4e3;
margin: 0;
padding: 0;
display: flex;
flex-direction: column;
padding-bottom: 70px;
}
.container {
max-width: 900px;
}
.menu-card {
max-width: 350px;
border-radius: 15px;
overflow: hidden;
background-color: #fff;
margin: auto;
display: flex;
flex-direction: column;
opacity: 0;
transition: opacity 0.3s ease-in-out;
box-shadow: 0 4px 8px rgba(0,0,0,0.1);
}
.menu-card.visible {
opacity: 1;
}
.menu-video {
height: 200px;
width: 100%;
object-fit: cover;
border-radius: 15px 15px 0 0;
opacity: 0;
transition: opacity 0.5s ease-in-out;
background-color: #000; /* Fallback color if video fails */
}
.menu-video.loaded {
opacity: 1;
}
.menu-card:hover .menu-video {
opacity: 1;
transform: scale(1.05); /* Slight zoom effect on hover */
}
.menu-card .card-body .card-title {
font-size: 1.2rem;
font-weight: 600;
margin: 10px 0;
color: #333333;
}
.menu-card .card-body .card-text.price {
font-size: 1rem;
font-weight: 500;
color: #000000;
margin-bottom: 5px;
}
.addbutton .btn {
background-color: #28a745;
color: white;
padding: 10px 20px;
font-size: 16px;
font-weight: bold;
border-radius: 5px;
border: none;
transition: background-color 0.3s ease;
margin-left: 13px;
}
.addbutton .btn:hover {
background-color: #218838;
}
.button-container {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 6px;
}
.customisable-text {
color: #0FAA39;
font-size: 10px;
font-weight: 500;
margin: 0;
text-align: center;
line-height: 1;
}
.btn-primary {
font-size: 12px;
font-weight: bold;
border-radius: 8px;
width: 70px;
height: 35px;
background-color: #0FAA39;
border-color: #0FAA39;
display: flex;
align-items: center;
justify-content: center;
padding: 0;
transition: background-color 0.3s ease, transform 0.1s ease;
}
.btn-primary:hover {
background-color: #0D9232;
border-color: #0D9232;
transform: scale(1.05);
}
.btn-primary:active,
.btn-primary:focus {
background-color: #0B7A29;
border-color: #0B7A29;
box-shadow: none;
transform: scale(0.98);
}
.avatar-dropdown-container {
position: absolute;
right: 10px;
top: 50%;
transform: translateY(-50%);
display: flex;
align-items: right;
justify-content: center;
}
.avatar-icon {
width: 40px;
height: 40px;
border-radius: 50%;
background-color: #007bff;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-size: 20px;
font-weight: bold;
}
.dropdown-menu {
position: absolute;
right: 0;
top: 100%;
background-color: #fff8f0;
border-radius: 5px;
width: 220px;
box-shadow: 0px 4px 8px rgba(0, 0, 0, 0.1);
display: none;
border: 1px solid #ffd8b1;
}
.dropdown-menu .dropdown-item {
padding: 12px 16px;
text-decoration: none;
color: #333;
border-bottom: 1px solid #ffd8b1;
display: block;
font-size: 15px;
transition: background-color 0.2s ease;
}
.dropdown-menu .dropdown-item:last-child {
border-bottom: none;
}
.dropdown-menu .dropdown-item:hover {
background-color: #ffe4c4;
color: #333;
}
.fixed-top-bar {
position: relative;
top: 0;
left: 0;
width: 100%;
height: 54px;
background: linear-gradient(45deg, #FFA07A, #FFB347);
color: white;
padding: 15px;
display: flex;
justify-content: space-between;
align-items: center;
z-index: 1000;
}
.search-bar-container {
position: absolute;
left: 20px;
top: 50%;
transform: translateY(-50%);
display: flex;
align-items: center;
width: 300px;
max-width: 90%;
position: relative;
}
.search-bar-container input {
width: 100%;
padding: 8px 40px 8px 40px;
font-size: 16px;
border-radius: 25px;
border: none;
background-color: #fff;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
outline: none;
}
.search-bar-container input::placeholder {
color: #888;
}
.search-icon {
position: absolute;
left: 15px;
font-size: 18px;
color: #888;
}
.mic-icon {
position: absolute;
right: 15px;
font-size: 18px;
color: #888;
cursor: pointer;
transition: color 0.3s ease;
}
.mic-icon.active {
color: #007bff;
}
.autocomplete-suggestions {
position: absolute;
top: 100%;
left: 0;
width: 100%;
max-height: 200px;
overflow-y: auto;
background-color: #fff;
border: 1px solid #ddd;
border-radius: 5px;
box-shadow: 0px 4px 8px rgba(0, 0, 0, 0.1);
z-index: 1000;
display: none;
}
.autocomplete-suggestions .suggestion-item {
padding: 8px 15px;
cursor: pointer;
font-size: 14px;
color: #333;
}
.autocomplete-suggestions .suggestion-item:hover {
background-color: #f1f1f1;
}
.addon-section {
background-color: #fff;
border: 2px solid #ffa500;
border-radius: 8px;
padding: 12px;
margin-bottom: 10px;
}
.addon-section h6 {
margin-bottom: 10px;
font-size: 1.1rem;
font-weight: bold;
color: #343a40;
}
.addon-section .form-check {
display: inline-flex;
align-items: center;
margin-left: 10px;
color: #343a40;
}
.addon-section .form-check-input {
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
width: 20px;
height: 20px;
border: 2px solid #343a40;
border-radius: 5px;
background-color: #f0f0f0;
position: relative;
margin-right: 10px;
}
.addon-section .form-check-input:checked {
background-color: #006400;
border-color: #006400;
}
.addon-section .form-check-input:checked::before {
content: '\2713';
font-size: 14px;
position: absolute;
top: 3px;
left: 4px;
color: white;
}
.addon-section .form-check-label {
font-size: 16px;
margin-left: 5px;
margin-right: 15px;
cursor: pointer;
display: inline-block;
vertical-align: middle;
}
form.text-center.mb-4 {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
margin-bottom: 5px;
}
.modal-header {
padding: 10px 15px;
}
.modal-title {
font-size: 16px;
font-weight: bold;
}
.modal-body {
max-height: 60vh;
overflow-y: auto;
padding: 15px;
}
.modal-body #modal-img {
max-height: 200px;
width: 100%;
object-fit: cover;
border-radius: 8px;
margin-bottom: 10px;
}
.modal-body #modal-name {
font-size: 20px;
font-weight: bold;
text-align: center;
margin-bottom: 5px;
color: #333333;
}
.modal-body #modal-price {
font-size: 16px;
font-weight: 500;
color: #000000;
text-align: center;
margin-bottom: 10px;
}
.modal-body #modal-description {
font-size: 14px;
color: #6c757d;
margin-bottom: 10px;
}
.modal-body .nutritional-info {
font-size: 12px;
color: #6c757d;
margin-bottom: 10px;
}
.modal-body #modal-addons h6,
.modal-body #first-row h6 {
font-size: 14px;
font-weight: bold;
margin-bottom: 10px;
}
.modal-footer {
display: flex;
align-items: center;
justify-content: space-between;
padding: 10px;
}
.modal-footer .d-flex {
display: flex;
align-items: center;
gap: 10px;
}
.modal-footer .btn {
height: 40px;
padding: 0 15px;
}
.modal-footer .form-control {
width: 50px;
height: 40px;
text-align: center;
}
.modal-footer .btn-primary {
background-color: #0FAA39;
border-color: #0FAA39;
font-weight: bold;
padding: 10px 20px;
height: 40px;
display: flex;
justify-content: center;
align-items: center;
width: auto;
}
.modal-footer .btn-outline-secondary {
height: 40px;
width: 40px;
}
.item-details {
display: none;
padding: 15px;
background-color: #f8f9fa;
border-radius: 8px;
margin-top: 10px;
margin: 10px 15px;
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
}
.item-details.show {
display: block;
}
.item-details h6 {
color: #0FAA39;
margin-bottom: 10px;
font-size: 1.1rem;
font-weight: bold;
}
.item-details p {
margin-bottom: 15px;
color: #555;
line-height: 1.5;
font-size: 0.9rem;
}
.toggle-details {
cursor: pointer;
color: #0FAA39;
font-size: 0.9rem;
text-align: left;
padding: 5px 0;
transition: color 0.3s ease;
display: block;
width: 100%;
margin-top: 5px;
}
.toggle-details:hover {
color: #0D9232;
text-decoration: underline;
}
.category-buttons {
display: flex;
flex-wrap: wrap;
gap: 10px;
justify-content: center;
margin-top: 10px;
}
.category-button {
background-color: #fff;
border: 2px solid #0FAA39;
color: #0FAA39;
padding: 5px 15px;
border-radius: 20px;
font-size: 0.9rem;
cursor: pointer;
transition: background-color 0.3s, color 0.3s;
}
.category-button.selected {
background-color: #0FAA39;
color: #fff;
border-color: #0FAA39;
}
.category-button:hover {
background-color: #e6f4ea;
}
.quantity-selector {
display: flex;
align-items: center;
gap: 5px;
}
.quantity-selector .btn {
width: 25px;
height: 25px;
padding: 0;
font-size: 12px;
line-height: 25px;
text-align: center;
}
.quantity-selector .quantity-display {
width: 25px;
text-align: center;
font-size: 12px;
font-weight: bold;
line-height: 25px;
}
.quantity-selector .quantity-to-add,
.quantity-selector .quantity-to-remove {
width: 45px;
height: 25px;
font-size: 12px;
padding: 0 5px;
}
.modal-dialog {
max-height: 90vh;
}
.modal-body::-webkit-scrollbar {
width: 8px;
}
.modal-body::-webkit-scrollbar-track {
background: #f1f1f1;
border-radius: 10px;
}
.modal-body::-webkit-scrollbar-thumb {
background: #0FAA39;
border-radius: 10px;
}
.modal-body::-webkit-scrollbar-thumb:hover {
background: #0D9232;
}
.btn-primary:disabled {
opacity: 0.65;
cursor: not-allowed;
}
.quantity-selector select {
width: 60px;
height: 35px;
padding: 5px;
border-radius: 5px;
border: 1px solid #ced4da;
}
#custom-dish-form {
position: relative;
padding-bottom: 80px;
}
#custom-dish-form .btn-primary {
position: absolute;
right: 15px;
bottom: 15px;
width: auto;
padding: 10px 20px;
}
.bottom-action-bar {
position: fixed;
bottom: 0;
left: 0;
right: 0;
background-color: white;
padding: 10px 20px;
display: flex;
justify-content: space-between;
align-items: center;
box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.1);
z-index: 1000;
max-width: 900px;
margin: 0 auto;
}
.bottom-action-bar .btn {
flex: 1;
margin: 0 5px;
padding: 10px 15px;
border-radius: 8px;
font-weight: bold;
font-size: 16px;
color: white;
display: flex;
align-items: center;
justify-content: center;
text-align: center;
min-width: 0;
white-space: nowrap;
}
.bottom-action-bar .btn-order-history {
background-color: #FFA07A;
border-color: #FFA07A;
}
.bottom-action-bar .btn-order-history:hover {
background-color: #FF8C61;
border-color: #FF8C61;
}
.bottom-action-bar .btn-view-cart {
background-color: #0FAA39;
border-color: #0FAA39;
}
.bottom-action-bar .btn-view-cart:hover {
background-color: #0D9232;
border-color: #0D9232;
}
.cart-icon-badge {
background-color: white;
color: #0FAA39;
border-radius: 50%;
width: 20px;
height: 20px;
display: inline-flex;
align-items: center;
justify-content: center;
font-size: 12px;
margin-left: 8px;
}
@media (max-width: 576px) {
.fixed-top-bar {
height: 60px;
padding: 10px;
}
.search-bar-container {
width: 80%;
max-width: 100%;
left: 10px;
top: 50%;
transform: translateY(-50%);
}
.search-bar-container input {
padding: 6px 35px 6px 35px;
font-size: 14px;
border-radius: 20px;
}
.search-icon {
left: 12px;
font-size: 16px;
}
.mic-icon {
right: 12px;
font-size: 16px;
}
.avatar-dropdown-container {
right: 10px;
}
.avatar-icon {
width: 40px;
height: 40px;
font-size: 20px;
}
.dropdown-menu {
width: 220px;
}
.dropdown-menu .dropdown-item {
padding: 12px 16px;
font-size: 15px;
}
.category-buttons {
gap: 8px;
}
.category-button {
padding: 4px 12px;
font-size: 0.85rem;
}
.modal-dialog {
max-width: 96%;
margin: 5px auto;
}
.modal-header {
padding: 5px 10px;
}
.modal-title {
font-size: 14px;
}
.modal-body {
max-height: 50vh;
padding: 8px;
}
.modal-body #modal-img {
max-height: 150px;
width: 100%;
max-width: 150px;
margin: 0 auto 5px;
display: block;
}
.modal-body #modal-name {
font-size: 18px;
margin-bottom: 3px;
}
.modal-body #modal-price {
font-size: 14px;
margin-bottom: 5px;
}
.modal-body #modal-description {
font-size: 12px;
margin-bottom: 5px;
}
.modal-body .nutritional-info {
font-size: 10px;
margin-bottom: 5px;
}
.modal-body #modal-addons h6,
.modal-body #first-row h6 {
font-size: 12px;
margin-bottom: 5px;
}
.modal-footer {
padding: 5px;
}
.modal-footer .btn {
height: 30px;
padding: 0 10px;
}
.modal-footer .form-control {
width: 30px;
height: 30px;
font-size: 12px;
font-weight: bold;
}
.modal-footer .btn-outline-secondary {
width: 25px;
height: 25px;
font-size: 12px;
line-height: 25px;
}
.modal-footer .btn-primary {
font-size: 12px;
height: 30px;
padding: 0 15px;
border-radius: 5px;
}
.btn-primary {
font-size: 10px;
width: 50px;
height: 25px;
}
.customisable-text {
font-size: 8px;
}
.button-container {
gap: 3px;
}
.quantity-selector .btn {
width: 18px;
height: 18px;
font-size: 9px;
line-height: 18px;
}
.quantity-selector .quantity-display {
width: 18px;
font-size: 9px;
line-height: 18px;
}
.quantity-selector .quantity-to-add,
.quantity-selector .quantity-to-remove {
width: 35px;
height: 18px;
font-size: 9px;
}
.quantity-selector select {
width: 50px;
height: 30px;
font-size: 12px;
}
.bottom-action-bar {
padding: 8px 10px;
}
.bottom-action-bar .btn {
padding: 8px 10px;
font-size: 14px;
}
.cart-icon-badge {
width: 18px;
height: 18px;
font-size: 10px;
margin-left: 5px;
}
.item-details {
padding: 10px;
margin: 5px 10px;
}
.item-details h6 {
font-size: 0.95rem;
}
.item-details p {
font-size: 0.8rem;
}
.toggle-details {
font-size: 0.8rem;
}
}
</style>
</head>
<body>
<div class="fixed-top-bar">
<div class="avatar-dropdown-container">
<div class="avatar-icon">
<span>{{ first_letter }}</span>
</div>
<div class="dropdown-menu">
<a href="{{ url_for('user_details.customer_details') }}" class="dropdown-item">View Profile</a>
<a href="{{ url_for('orderhistory.order_history') }}" class="dropdown-item">Order History</a>
<a href="{{ url_for('logout') }}" class="dropdown-item">Logout</a>
</div>
</div>
<div class="search-bar-container">
<input type="text" id="searchBar" class="form-control" placeholder="Search items or sections..." autocomplete="off">
<i class="bi bi-search search-icon"></i>
<i class="bi bi-mic mic-icon" id="micIcon"></i>
<div id="autocompleteSuggestions" class="autocomplete-suggestions"></div>
</div>
</div>
<form method="get" action="/menu" class="text-center mb-4" id="categoryForm">
<label class="form-label fw-bold">Select a Category:</label>
<div class="category-buttons">
{% for category in categories %}
<button type="button" class="category-button {% if selected_category == category %}selected{% endif %}" data-category="{{ category }}">{{ category }}</button>
{% endfor %}
<button type="button" class="category-button {% if selected_category == 'Customized Dish' %}selected{% endif %}" data-category="Customized Dish">Customized Dish</button>
</div>
<input type="hidden" name="category" id="selectedCategoryInput" value="{{ selected_category }}">
</form>
<div class="container mt-4">
{% if selected_category == "Customized Dish" %}
<div id="custom-dish-form" class="mt-4">
<h3>Create Your Custom Dish</h3>
<form method="POST" action="/customdish/generate_custom_dish">
<div class="mb-3">
<label for="custom-dish-name" class="form-label">Dish Name</label>
<input type="text" class="form-control" id="custom-dish-name" name="name" required>
</div>
<div class="mb-3 position-relative">
<label for="custom-dish-description" class="form-label">Dish Description</label>
<textarea class="form-control" id="custom-dish-description" name="description" required></textarea>
<div id="descriptionSuggestions" class="autocomplete-suggestions"></div>
</div>
<button type="submit" class="btn btn-primary">Submit Custom Dish</button>
</form>
</div>
{% else %}
{% if ordered_menu.items()|length == 0 %}
<p>No menu items available for this category.</p>
{% else %}
{% for section, items in ordered_menu.items() %}
<h3>{{ section }}</h3>
<div class="row">
{% for item in items %}
<div class="col-md-6 mb-4">
<div class="card menu-card">
<video
class="card-img-top menu-video"
muted
loop
preload="auto"
data-src="{{ item.Video1__c | default('/static/placeholder.mp4') }}"
poster="{{ item.Image1__c | default('/static/placeholder.jpg') }}"
width="350"
height="200"
onmouseover="this.play()"
onmouseout="this.pause(); this.currentTime = 0;"
onerror="this.poster='/static/placeholder.jpg';">
<source src="{{ item.Video1__c | default('/static/placeholder.mp4') }}" type="video/mp4">
Your browser does not support the video tag.
</video>
<div class="addbutton">
<div class="card-body d-flex align-items-center justify-content-between">
<div>
<h5 class="card-title">{{ item.Name | default('Unnamed Item') }}</h5>
<p class="card-text price">${{ item.Price__c | default('0.00') }}</p>
{% if item.Section__c != 'Soft Drinks' %}
<div class="toggle-details" data-item-name="{{ item.Name | default('Unnamed Item') }}">Show Details</div>
{% endif %}
</div>
<div class="d-flex flex-column align-item-center justify-content-center">
<div class="button-container"
data-item-name="{{ item.Name | default('Unnamed Item') }}"
data-item-price="{{ item.Price__c | default('0.00') }}"
data-item-image="{{ item.Image2__c | default(item.Image1__c) | default('/static/placeholder.jpg') }}"
data-item-section="{{ item.Section__c | default(section) }}"
data-item-category="{{ selected_category }}">
{% if item.Section__c == 'Soft Drinks' %}
<button class="btn btn-primary add-to-cart-btn" onclick="showSoftDrinkModal(this)">ADD</button>
{% else %}
<button class="btn btn-primary"
data-bs-toggle="modal"
data-bs-target="#itemModal"
onclick="showItemDetails('{{ item.Name | default('Unnamed Item') }}', '{{ item.Price__c | default('0.00') }}', '{{ item.Image2__c | default(item.Image1__c) }}', '{{ item.Description__c | default('No description') }}', '{{ item.IngredientsInfo__c | default('Not specified') }}', '{{ item.NutritionalInfo__c | default('Not available') }}', '{{ item.Allergens__c | default('None listed') }}', '{{ item.Section__c | default(section) }}', '{{ selected_category }}')">
ADD
</button>
{% endif %}
{% if item.Section__c != 'Apetizer' and item.Section__c != 'Customized dish' and item.Section__c !='Soft Drinks' %}
<span class="customisable-text">Customisable</span>
{% endif %}
</div>
</div>
</div>
</div>
{% if item.Section__c != 'Soft Drinks' %}
<div class="item-details" id="details-{{ item.Name | default('unnamed-item') | replace(' ', '-') }}">
<h6>Description</h6>
<p>{{ item.Description__c | default('No description available') }}</p>
<h6>Ingredientsinfo</h6>
<p>{{ item.Ingredientsinfo__c | default('Not specified') }}</p>
<h6>Nutritional Info</h6>
<p>{{ item.NutritionalInfo__c | default('Not available') }}</p>
<h6>Allergens</h6>
<p>{{ item.Allergens__c | default('None listed') }}</p>
</div>
{% endif %}
</div>
</div>
{% endfor %}
</div>
{% endfor %}
{% endif %}
{% endif %}
</div>
<div class="bottom-action-bar">
<a href="{{ url_for('orderhistory.order_history') }}" class="btn btn-order-history">
<i class="bi bi-clock-history"></i> Order History
</a>
<a href="{{ url_for('cart.cart') }}" class="btn btn-view-cart">
<i class="bi bi-cart"></i> View Cart
<span class="cart-icon-badge" id="cart-item-count">{{ cart_item_count }}</span>
</a>
</div>
<!-- Modal for Item Details -->
<div class="modal fade" id="itemModal" tabindex="-1" aria-labelledby="itemModalLabel" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="itemModalLabel">Item Details</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<img id="modal-img" class="img-fluid rounded mb-3 d-block mx-auto" alt="Item Image" style="max-height: 200px; object-fit: cover;">
<h5 id="modal-name" class="fw-bold text-center"></h5>
<p id="modal-price" class="text-muted text-center"></p>
<p id="modal-description" class="text-secondary"></p>
<div class="nutritional-info">
<strong>Ingredients:</strong> <span id="modal-ingredients"></span><br>
<strong>Nutrition:</strong> <span id="modal-nutrition"></span><br>
<strong>Allergens:</strong> <span id="modal-allergens"></span>
</div>
<div id="modal-addons" class="modal-addons mt-4">
<h6>Customization Options</h6>
<div id="addons-list" class="addons-container">Loading customization options...</div>
</div>
<div class="mt-4">
<h6>Custom Request</h6>
<textarea id="modal-instructions" class="form-control" placeholder="Enter any special instructions here..."></textarea>
</div>
<span id="modal-section" data-section="" data-category="" style="display: none;"></span>
</div>
<div class="modal-footer d-flex align-items-center justify-content-between">
<div class="d-flex align-items-center gap-2">
<button type="button" class="btn btn-outline-secondary" id="decreaseQuantity">-</button>
<input type="text" class="form-control text-center" id="quantityInput" value="1" readonly style="width: 50px;"/>
<button type="button" class="btn btn-outline-secondary" id="increaseQuantity">+</button>
</div>
<button type="button" class="btn btn-primary" onclick="addToCartFromModal()">Add to Cart</button>
</div>
</div>
</div>
</div>
<!-- Modal for Soft Drinks Quantity Selection -->
<div class="modal fade" id="softDrinkModal" tabindex="-1" aria-labelledby="softDrinkModalLabel" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content" style="border-radius: 15px; box-shadow: 0 4px 15px rgba(0,0,0,0.2);">
<div class="modal-header" style="background: linear-gradient(45deg, #0FAA39, #0D9232); color: white; border-radius: 15px 15px 0 0;">
<h5 class="modal-title" id="softDrinkModalLabel">Select Your Drink</h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body" style="padding: 20px;">
<div class="text-center mb-4">
<img id="soft-drink-image" class="img-fluid rounded mb-3" alt="Soft Drink Image" style="max-height: 150px; width: auto; object-fit: contain;">
<h5 id="soft-drink-name" class="fw-bold" style="color: #333;"></h5>
<p id="soft-drink-price" class="text-muted" style="font-size: 1.1rem;"></p>
</div>
<div class="d-flex justify-content-center align-items-center mb-4">
<div class="quantity-selector" style="background-color: #f8f9fa; padding: 10px; border-radius: 10px;">
<button type="button" class="btn btn-outline-secondary" id="soft-drink-decrease" style="width: 40px; height: 40px;">-</button>
<input type="text" class="form-control text-center mx-2" id="soft-drink-quantity" value="1" readonly style="width: 60px; font-weight: bold; font-size: 1.1rem;">
<button type="button" class="btn btn-outline-secondary" id="soft-drink-increase" style="width: 40px; height: 40px;">+</button>
</div>
</div>
</div>
<div class="modal-footer" style="border-top: none; padding: 0 20px 20px 20px; justify-content: center;">
<button type="button" class="btn btn-primary" onclick="addSoftDrinkToCart()" style="width: 200px; padding: 12px; font-size: 1.1rem; background-color: #0FAA39; border-color: #0FAA39;">Add to Cart</button>
</div>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/js/bootstrap.bundle.min.js"></script>
<script>
let isProcessingRequest = false;
let currentSoftDrinkButton = null;
const menuItems = [
{% for section, items in ordered_menu.items() %}
{% for item in items %}
"{{ item.Name | default('Unnamed Item') }}",
{% endfor %}
{% endfor %}
];
const ingredientsList = [
"Basmati Rice", "Bell Pepper", "Biryani Masala", "Butter", "Capsicum", "Cauliflower",
"Chickpea Flour (Besan)", "Chickpea Flour (for batter)", "Chickpeas (Channa)", "Chili Powder",
"Chili Sauce", "Coconut Milk", "Coriander Powder", "Cornflour", "Cream", "Cumin Powder",
"Cumin Seeds", "Curd (Yogurt)", "Curry Leaves", "Fish (e.g., King Fish or Salmon)",
"Fresh Coriander Leaves", "Garam Masala", "Garlic", "Ghee (Clarified Butter)", "Ginger",
"Ginger-Garlic Paste", "Goat Meat (Mutton)", "Green Chilies", "Honey",
"Kasuri Methi (dried fenugreek leaves)", "Lemon Juice", "Mango Puree", "Mint Leaves",
"Mixed Vegetables (Carrot, Peas, Potato, Cauliflower)", "Mixed Vegetables (Carrot, Peas, Potato)",
"Mustard Seeds", "Mutton (Goat Meat)", "Oil", "Oil (for frying)", "Onion",
"Paneer (Indian Cottage Cheese)", "Peas", "Potatoes", "Prawns", "Red Chili Powder",
"Rice Flour", "Saffron", "Salt", "Soy Sauce", "Spring Onion", "Tamarind (for sourness)",
"Tomato Ketchup", "Tomatoes", "Turmeric Powder", "Vinegar", "Water", "Wheat Flour (for dough)",
"Whole Wheat Flour", "Yogurt (Curd)"
];
function addToCartLocalStorage(payload) {
let cart = JSON.parse(localStorage.getItem('cart')) || [];
const existingItem = cart.find(item =>
item.itemName === payload.itemName &&
item.instructions === payload.instructions &&
JSON.stringify(item.addons) === JSON.stringify(payload.addons)
);
if (existingItem) {
existingItem.quantity = payload.quantity;
} else {
cart.push(payload);
}
localStorage.setItem('cart', JSON.stringify(cart));
return cart;
}
function removeFromCartLocalStorage(itemName, quantityToRemove, instructions, addons) {
let cart = JSON.parse(localStorage.getItem('cart')) || [];
const itemIndex = cart.findIndex(item =>
item.itemName === itemName &&
item.instructions === instructions &&
JSON.stringify(item.addons) === JSON.stringify(addons)
);
if (itemIndex !== -1) {
if (quantityToRemove >= cart[itemIndex].quantity) {
cart.splice(itemIndex, 1);
} else {
cart[itemIndex].quantity -= quantityToRemove;
}
}
localStorage.setItem('cart', JSON.stringify(cart));
return cart;
}
function getCartLocalStorage() {
return JSON.parse(localStorage.getItem('cart')) || [];
}
function debounce(func, wait) {
let timeout;
return function (...args) {
clearTimeout(timeout);
timeout = setTimeout(() => func.apply(this, args), wait);
};
}
function showSoftDrinkModal(button) {
currentSoftDrinkButton = button;
const buttonContainer = button.closest('.button-container');
const itemName = buttonContainer.getAttribute('data-item-name');
const itemPrice = buttonContainer.getAttribute('data-item-price');
const itemImage = buttonContainer.getAttribute('data-item-image');
document.getElementById('soft-drink-name').textContent = itemName;
document.getElementById('soft-drink-price').textContent = `$${itemPrice}`;
document.getElementById('soft-drink-quantity').value = '1';
const softDrinkImage = document.getElementById('soft-drink-image');
softDrinkImage.src = itemImage || '/static/placeholder.jpg';
const modal = new bootstrap.Modal(document.getElementById('softDrinkModal'));
modal.show();
}
function addSoftDrinkToCart() {
if (!currentSoftDrinkButton) return;
const buttonContainer = currentSoftDrinkButton.closest('.button-container');
const quantity = parseInt(document.getElementById('soft-drink-quantity').value) || 1;
const itemName = buttonContainer.getAttribute('data-item-name');
const itemPrice = parseFloat(buttonContainer.getAttribute('data-item-price'));
const itemImage = buttonContainer.getAttribute('data-item-image');
const section = buttonContainer.getAttribute('data-item-section');
const selectedCategory = buttonContainer.getAttribute('data-item-category');
const cartPayload = {
itemName: itemName,
itemPrice: itemPrice,
itemImage: itemImage,
section: section,
category: selectedCategory,
addons: [],
instructions: '',
quantity: quantity
};
fetch('/cart/add', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(cartPayload)
})
.then(response => response.json())
.then(data => {
if (data.success) {
updateCartUI(data.cart);
const modal = bootstrap.Modal.getInstance(document.getElementById('softDrinkModal'));
modal.hide();
} else {
console.error('Failed to add item to cart:', data.error);
const cart = addToCartLocalStorage(cartPayload);
updateCartUI(cart);
const modal = bootstrap.Modal.getInstance(document.getElementById('softDrinkModal'));
modal.hide();
}
})
.catch(err => {
console.error('Error adding item to cart:', err);
const cart = addToCartLocalStorage(cartPayload);
updateCartUI(cart);
const modal = bootstrap.Modal.getInstance(document.getElementById('softDrinkModal'));
modal.hide();
});
}
function updateCartUI(cart) {
if (!Array.isArray(cart)) {
console.error('Invalid cart data:', cart);
return;
}
let totalQuantity = 0;
cart.forEach(item => {
totalQuantity += item.quantity;
});
const cartItemCount = document.getElementById('cart-item-count');
if (cartItemCount) {
cartItemCount.innerText = totalQuantity;
cartItemCount.style.display = totalQuantity > 0 ? 'inline-flex' : 'none';
}
}
document.addEventListener('DOMContentLoaded', function () {
const avatarContainer = document.querySelector('.avatar-dropdown-container');
const dropdownMenu = document.querySelector('.dropdown-menu');
avatarContainer.addEventListener('click', function (event) {
event.stopPropagation();
dropdownMenu.style.display = dropdownMenu.style.display === 'block' ? 'none' : 'block';
});
document.addEventListener('click', function (event) {
if (!avatarContainer.contains(event.target)) {
dropdownMenu.style.display = 'none';
}
});
const dropdownItems = document.querySelectorAll('.dropdown-item');
dropdownItems.forEach(item => {
item.addEventListener('click', function () {
dropdownMenu.style.display = 'none';
});
});
const menuCards = document.querySelectorAll('.menu-card');
const menuVideos = document.querySelectorAll('.menu-video');
const cardObserver = new IntersectionObserver((entries, observer) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
entry.target.classList.add('visible');
observer.unobserve(entry.target);
}
});
}, {
root: null,
rootMargin: '0px',
threshold: 0.1
});
const videoObserver = new IntersectionObserver((entries, observer) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const video = entry.target;
const src = video.getAttribute('data-src');
if (src && !video.querySelector('source[src="' + src + '"]')) {
const source = video.querySelector('source');
source.src = src;
video.load();
}
video.classList.add('loaded');
observer.unobserve(video);
}
});
}, {
root: null,
rootMargin: '200px',
threshold: 0.01
});
menuCards.forEach(card => cardObserver.observe(card));
menuVideos.forEach(video => videoObserver.observe(video));
const toggleLinks = document.querySelectorAll('.toggle-details');
toggleLinks.forEach(link => {
link.addEventListener('click', function () {
const itemName = this.getAttribute('data-item-name').replace(/ /g, '-');
const detailsDiv = document.getElementById(`details-${itemName}`);
const isCurrentlyShown = detailsDiv.classList.contains('show');
document.querySelectorAll('.item-details.show').forEach(otherDetails => {
if (otherDetails !== detailsDiv) {
otherDetails.classList.remove('show');
const otherLink = otherDetails.previousElementSibling.querySelector('.toggle-details');
if (otherLink) {
otherLink.innerText = 'Show Details';
}
}
});
if (!isCurrentlyShown) {
detailsDiv.classList.add('show');
this.innerText = 'Hide Details';
} else {
detailsDiv.classList.remove('show');
this.innerText = 'Show Details';
}
});
});
const categoryButtons = document.querySelectorAll('.category-button');
const categoryForm = document.getElementById('categoryForm');
const selectedCategoryInput = document.getElementById('selectedCategoryInput');
if (!selectedCategoryInput.value) {
selectedCategoryInput.value = "All";
document.querySelector('.category-button[data-category="All"]').classList.add('selected');
}
categoryButtons.forEach(button => {
button.addEventListener('click', function () {
categoryButtons.forEach(btn => btn.classList.remove('selected'));
this.classList.add('selected');
selectedCategoryInput.value = this.getAttribute('data-category');
categoryForm.submit();
});
});
const searchBar = document.getElementById('searchBar');
const suggestionsContainer = document.getElementById('autocompleteSuggestions');
searchBar.addEventListener('input', function () {
const input = this.value.trim().toLowerCase();
suggestionsContainer.innerHTML = '';
suggestionsContainer.style.display = 'none';
if (input) {
const filteredItems = menuItems.filter(item =>
item.toLowerCase().includes(input)
);
if (filteredItems.length > 0) {
filteredItems.forEach(item => {
const suggestionDiv = document.createElement('div');
suggestionDiv.classList.add('suggestion-item');
suggestionDiv.innerText = item;
suggestionDiv.addEventListener('click', function () {
searchBar.value = item;
suggestionsContainer.style.display = 'none';
filterMenu();
});
suggestionsContainer.appendChild(suggestionDiv);
});
suggestionsContainer.style.display = 'block';
}
}
filterMenu();
});
document.addEventListener('click', function (event) {
if (!searchBar.contains(event.target) && !suggestionsContainer.contains(event.target)) {
suggestionsContainer.style.display = 'none';
}
});
const descriptionTextarea = document.getElementById('custom-dish-description');
const descriptionSuggestions = document.getElementById('descriptionSuggestions');
if (descriptionTextarea && descriptionSuggestions) {
let usedIngredients = new Set();
function updateUsedIngredients() {
const inputText = descriptionTextarea.value.trim();
usedIngredients.clear();
if (inputText) {
const words = inputText.split(/,\s*/).map(word => word.trim());
words.forEach(word => {
if (word && ingredientsList.includes(word)) {
usedIngredients.add(word);
}
});
}
}
descriptionTextarea.addEventListener('input', function () {
const inputText = this.value.trim();
const words = inputText.split(/,\s*/);
const lastWord = words[words.length - 1].trim().toLowerCase();
descriptionSuggestions.innerHTML = '';
descriptionSuggestions.style.display = 'none';
updateUsedIngredients();
if (lastWord) {
const filteredIngredients = ingredientsList.filter(ingredient =>
ingredient.toLowerCase().includes(lastWord) && !usedIngredients.has(ingredient)
);
if (filteredIngredients.length > 0) {
filteredIngredients.forEach(ingredient => {
const suggestionDiv = document.createElement('div');
suggestionDiv.classList.add('suggestion-item');
suggestionDiv.innerText = ingredient;
suggestionDiv.addEventListener('click', function () {
const currentValue = descriptionTextarea.value;
const lastCommaIndex = currentValue.lastIndexOf(',');
const baseText = lastCommaIndex !== -1 ? currentValue.substring(0, lastCommaIndex + 1) : '';
descriptionTextarea.value = baseText + (baseText ? ' ' : '') + ingredient + ', ';
descriptionSuggestions.style.display = 'none';
descriptionTextarea.focus();
updateUsedIngredients();
});
descriptionSuggestions.appendChild(suggestionDiv);
});
descriptionSuggestions.style.display = 'block';
}
}
});
document.addEventListener('click', function (event) {
if (!descriptionTextarea.contains(event.target) && !descriptionSuggestions.contains(event.target)) {
descriptionSuggestions.style.display = 'none';
}
});
}
fetch('/cart/get')
.then(response => {
if (!response.ok) {
throw new Error(`HTTP error! Status: ${response.status}`);
}
return response.json();
})
.then(data => {
if (data.success) {
updateCartUI(data.cart);
} else {
console.error('Failed to fetch cart:', data.error);
const cart = getCartLocalStorage();
updateCartUI(cart);
}
})
.catch(err => {
console.error('Error fetching cart:', err);
const cart = getCartLocalStorage();
updateCartUI(cart);
});
const preloadedVideos = document.querySelectorAll('link[rel="preload"][as="video"]');
preloadedVideos.forEach(link => {
const video = document.createElement('video');
video.src = link.href;
video.preload = 'auto';
});
const decreaseBtn = document.getElementById('decreaseQuantity');
const increaseBtn = document.getElementById('increaseQuantity');
const quantityInput = document.getElementById('quantityInput');
decreaseBtn.addEventListener('click', function () {
let currentQuantity = parseInt(quantityInput.value);
if (currentQuantity > 1) {
currentQuantity--;
quantityInput.value = currentQuantity;
}
});
increaseBtn.addEventListener('click', function () {
let currentQuantity = parseInt(quantityInput.value);
currentQuantity++;
quantityInput.value = currentQuantity;
});
const softDrinkDecreaseBtn = document.getElementById('soft-drink-decrease');
const softDrinkIncreaseBtn = document.getElementById('soft-drink-increase');
const softDrinkQuantityInput = document.getElementById('soft-drink-quantity');
softDrinkDecreaseBtn.addEventListener('click', function() {
let currentQuantity = parseInt(softDrinkQuantityInput.value);
if (currentQuantity > 1) {
currentQuantity--;
softDrinkQuantityInput.value = currentQuantity;
}
});
softDrinkIncreaseBtn.addEventListener('click', function() {
let currentQuantity = parseInt(softDrinkQuantityInput.value);
if (currentQuantity < 1000) {
currentQuantity++;
softDrinkQuantityInput.value = currentQuantity;
}
});
const micIcon = document.getElementById('micIcon');
if ('SpeechRecognition' in window || 'webkitSpeechRecognition' in window) {
const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
const recognition = new SpeechRecognition();
recognition.lang = 'en-US';
recognition.onstart = () => micIcon.classList.add('active');
recognition.onresult = (event) => {
searchBar.value = event.results[0][0].transcript.trim();
filterMenu();
};
recognition.onend = () => micIcon.classList.remove('active');
recognition.onerror = (event) => {
micIcon.classList.remove('active');
console.error('Speech error:', event.error);
};
micIcon.addEventListener('click', () => {
recognition.start();
});
} else {
micIcon.style.display = 'none';
}
});
function filterMenu() {
const input = document.getElementById('searchBar').value.trim().toLowerCase();
const sections = document.querySelectorAll('h3');
const items = document.querySelectorAll('.menu-card');
let matchedSections = new Set();
items.forEach(item => {
const itemName = item.querySelector('.card-title').innerText.toLowerCase();
const itemSection = item.closest('.row').previousElementSibling.innerText.toLowerCase();
if (itemName.includes(input) || (itemSection && itemSection.includes(input))) {
item.style.display = 'block';
item.classList.add('visible');
matchedSections.add(item.closest('.row'));
} else {
item.style.display = 'none';
}
});
sections.forEach(section => {
const sectionRow = section.nextElementSibling;
if (matchedSections.has(sectionRow)) {
section.style.display = 'block';
sectionRow.style.display = 'flex';
} else {
section.style.display = 'none';
sectionRow.style.display = 'none';
}
});
if (!input) {
sections.forEach(section => {
section.style.display = 'block';
section.nextElementSibling.style.display = 'flex';
});
items.forEach(item => {
item.style.display = 'block';
item.classList.add('visible');
});
}
}
function showItemDetails(name, price, image, description, ingredients, nutrition, allergens, section, selectedCategory) {
document.getElementById('modal-name').innerText = name;
document.getElementById('modal-price').innerText = `$${price}`;
const modalImg = document.getElementById('modal-img');
modalImg.src = image || '/static/placeholder.jpg';
document.getElementById('modal-description').innerText = description || 'No description available.';
document.getElementById('modal-ingredients').innerText = ingredients || 'Not specified';
document.getElementById('modal-nutrition').innerText = nutrition || 'Not available';
document.getElementById('modal-allergens').innerText = allergens || 'None listed';
document.getElementById('addons-list').innerHTML = 'Loading customization options...';
document.getElementById('modal-instructions').value = '';
const modalSectionEl = document.getElementById('modal-section');
modalSectionEl.setAttribute('data-section', section);
modalSectionEl.setAttribute('data-category', selectedCategory);
document.getElementById('quantityInput').value = 1;
fetch(`/api/addons?item_name=${encodeURIComponent(name)}&item_section=${encodeURIComponent(section)}`)
.then(response => response.json())
.then(data => {
const addonsList = document.getElementById('addons-list');
addonsList.innerHTML = '';
if (!data.success || !data.addons || data.addons.length === 0) {
addonsList.innerHTML = '<p>No customization options available.</p>';
return;
}
data.addons.forEach(addon => {
const sectionDiv = document.createElement('div');
sectionDiv.classList.add('addon-section');
const title = document.createElement('h6');
title.innerText = addon.name;
sectionDiv.appendChild(title);
const optionsContainer = document.createElement('div');
addon.options.forEach((option, index) => {
const optionId = `addon-${addon.name.replace(/\s+/g, '')}-${index}`;
const listItem = document.createElement('div');
listItem.classList.add('form-check');
listItem.innerHTML = `
<input type="checkbox" class="form-check-input addon-option" id="${optionId}" value="${option}"
data-name="${option}" data-group="${addon.name}" data-price="${addon.extra_charge ? addon.extra_charge_amount : 0}">
<label class="form-check-label" for="${optionId}">
${option} ${addon.extra_charge ? `($${addon.extra_charge_amount})` : ''}
</label>
`;
optionsContainer.appendChild(listItem);
});
sectionDiv.appendChild(optionsContainer);
addonsList.appendChild(sectionDiv);
});
})
.catch(err => {
console.error('Error fetching add-ons:', err);
document.getElementById('addons-list').innerHTML = '<p>Error loading customization options.</p>';
});
}
document.addEventListener('click', function(event) {
if (event.target.classList.contains('addon-option')) {
handleAddonClick(event.target);
}
});
function handleAddonClick(checkbox) {
const groupName = checkbox.getAttribute('data-group');
const isMultiSelectGroup = ["Extra Toppings", "Choose Raita/Sides", "Select Dip/Sauce", "Extra Add-ons", "Make it a Combo","Beverages","Sauces"].includes(groupName);
if (!isMultiSelectGroup) {
const checkboxes = document.querySelectorAll(`.addon-option[data-group="${groupName}"]`);
checkboxes.forEach(otherCheckbox => {
if (otherCheckbox !== checkbox) {
otherCheckbox.checked = false;
}
});
}
}
function addToCartFromModal() {
const itemName = document.getElementById('modal-name').innerText;
let itemPrice = parseFloat(document.getElementById('modal-price').innerText.replace('$', ''));
if (isNaN(itemPrice)) {
alert('Invalid price for the item. Please check the item details.');
return;
}
const itemImage = document.getElementById('modal-img').src;
const modalSectionEl = document.getElementById('modal-section');
const section = modalSectionEl.getAttribute('data-section');
const selectedCategory = modalSectionEl.getAttribute('data-category');
if (!itemName || !itemPrice || !section || !itemImage) {
console.error('Missing data for cart item:', { itemName, itemPrice, section, itemImage });
return;
}
const selectedAddOns = Array.from(
document.querySelectorAll('#addons-list input[type="checkbox"]:checked')
).map(addon => ({
name: addon.getAttribute('data-name') || 'Default Name',
price: parseFloat(addon.getAttribute('data-price') || 0)
}));
const quantity = parseInt(document.getElementById('quantityInput').value) || 1;
const instructions = document.getElementById('modal-instructions').value;
const cartPayload = {
itemName: itemName,
itemPrice: itemPrice,
itemImage: itemImage,
section: section,
category: selectedCategory,
addons: selectedAddOns,
instructions: instructions,
quantity: quantity
};
fetch('/cart/add', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(cartPayload)
})
.then(response => response.json())
.then(data => {
if (data.success) {
alert('Item added to cart successfully!');
updateCartUI(data.cart);
const modal = document.getElementById('itemModal');
const modalInstance = bootstrap.Modal.getInstance(modal);
modalInstance.hide();
} else {
console.error('Failed to add item to cart:', data.error);
alert(data.error || 'Failed to add item to cart.');
}
})
.catch(err => {
console.error('Error adding item to cart:', err);
const cart = addToCartLocalStorage(cartPayload);
updateCartUI(cart);
const modal = document.getElementById('itemModal');
const modalInstance = bootstrap.Modal.getInstance(modal);
modalInstance.hide();
});
}
</script>
</body>
</html>