subbuok / templates /menu.html
lokesh341's picture
Update templates/menu.html (#4)
bdf48df verified
raw
history blame
70.5 kB
<!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/[email protected]/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 {
background-color: #f8f9fa;
border-radius: 8px;
padding: 10px;
margin: 10px 15px;
display: none;
}
.item-details.show {
display: block;
}
.item-details h6 {
font-size: 0.9rem;
font-weight: bold;
margin-bottom: 5px;
color: #333333;
}
.item-details p {
font-size: 0.85rem;
margin-bottom: 5px;
color: #333;
}
.item-details .nutritional-info {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 10px;
background-color: #e9ecef;
padding: 10px;
border-radius: 5px;
font-size: 0.85rem;
line-height: 1.5;
}
.toggle-details {
cursor: pointer;
color: #0FAA39;
font-size: 0.9rem;
margin-left: 15px;
margin-bottom: 10px;
display: inline-block;
}
.toggle-details:hover {
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;
}
}
</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">${{ item.Price__c | default('0.00') }}</p>
</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.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.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>
<!-- Only show toggle-details if not Soft Drinks -->
{% if item.Section__c != 'Soft Drinks' %}
<div class="toggle-details" data-item-name="{{ item.Name | default('Unnamed Item') }}">Show Details</div>
<div class="item-details" id="details-{{ item.Name | default('unnamed-item') | replace(' ', '-') }}"></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
</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 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">
<div class="modal-header">
<h5 class="modal-title" id="softDrinkModalLabel">Select Quantity</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div class="text-center mb-3">
<h5 id="soft-drink-name"></h5>
<p id="soft-drink-price"></p>
</div>
<div class="d-flex justify-content-center align-items-center mb-4">
<button type="button" class="btn btn-outline-secondary" id="soft-drink-decrease">-</button>
<input type="text" class="form-control text-center mx-2" id="soft-drink-quantity" value="1" readonly style="width: 60px;">
<button type="button" class="btn btn-outline-secondary" id="soft-drink-increase">+</button>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-primary" onclick="addSoftDrinkToCart()">Add to Cart</button>
</div>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/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)"
];
const menuItemDetails = {
"Veg Manchurian": {
ingredients: "Cauliflower, Onion, Ginger, Garlic, Soy Sauce, Cornflour, Green Chilies, Capsicum, Spring Onion",
nutritionalInfo: { calories: 250, protein: 5, carbs: 35, fats: 12, fiber: 3, sugar: 2 },
allergens: "Soy, Gluten"
},
"Veg Biryani": {
ingredients: "Basmati Rice, Mixed Vegetables (Carrot, Peas, Potato, Cauliflower), Ginger, Garlic, Biryani Masala, Mint Leaves, Curd, Onion, Ghee",
nutritionalInfo: { calories: 300, protein: 6, carbs: 50, fats: 12, fiber: 5, sugar: 3 },
allergens: "Dairy"
},
"Sukka Gosht (Goat)": {
ingredients: "Goat Meat, Ginger-Garlic Paste, Green Chilies, Onion, Tomatoes, Garam Masala, Coriander Powder, Cumin Powder, Fresh Coriander",
nutritionalInfo: { calories: 450, protein: 35, carbs: 10, fats: 30, fiber: 2, sugar: 1 },
allergens: "None"
},
"Samosa": {
ingredients: "Potatoes, Peas, Onion, Ginger, Cumin Seeds, Garam Masala, Wheat Flour (for dough), Oil (for frying)",
nutritionalInfo: { calories: 150, protein: 3, carbs: 25, fats: 7, fiber: 2, sugar: 1 },
allergens: "Gluten"
},
"Roti": {
ingredients: "Whole Wheat Flour, Water, Salt",
nutritionalInfo: { calories: 150, protein: 4, carbs: 30, fats: 1, fiber: 3, sugar: 0 },
allergens: "Gluten"
},
"Prawn Fry": {
ingredients: "Prawns, Garlic, Ginger, Chili Powder, Coriander Powder, Cumin Powder, Lemon Juice, Oil",
nutritionalInfo: { calories: 350, protein: 25, carbs: 10, fats: 20, fiber: 1, sugar: 1 },
allergens: "Shellfish"
},
"Paneer Butter Masala": {
ingredients: "Paneer, Butter, Cream, Tomato Puree, Onion, Ginger, Garlic, Garam Masala",
nutritionalInfo: { calories: 400, protein: 15, carbs: 20, fats: 25, fiber: 2, sugar: 3 },
allergens: "Dairy"
},
"Paneer Biryani": {
ingredients: "Paneer, Basmati Rice, Mixed Vegetables (Carrot, Peas, Potato), Onion, Tomatoes, Biryani Masala, Mint Leaves, Curd",
nutritionalInfo: { calories: 350, protein: 12, carbs: 55, fats: 15, fiber: 4, sugar: 3 },
allergens: "Dairy"
},
"Onion Pakoda": {
ingredients: "Onion, Chickpea Flour (Besan), Rice Flour, Green Chilies, Cumin Seeds, Ginger, Turmeric Powder, Oil (for frying)",
nutritionalInfo: { calories: 200, protein: 5, carbs: 30, fats: 8, fiber: 3, sugar: 2 },
allergens: "Gluten (if cross-contamination)"
},
"Mutton Biryani": {
ingredients: "Mutton, Basmati Rice, Onion, Tomatoes, Ginger-Garlic Paste, Biryani Masala, Mint Leaves, Yogurt, Ghee",
nutritionalInfo: { calories: 500, protein: 30, carbs: 50, fats: 25, fiber: 4, sugar: 3 },
allergens: "Dairy"
},
"Fish Curry": {
ingredients: "Fish (any firm fish like Salmon or King Fish), Onion, Tomato, Ginger-Garlic Paste, Curry Leaves, Coconut Milk, Tamarind, Mustard Seeds",
nutritionalInfo: { calories: 300, protein: 25, carbs: 10, fats: 20, fiber: 2, sugar: 1 },
allergens: "Fish"
},
"Fiery Mango Glaze Chicken": {
ingredients: "Chicken, Mango Puree, Chili Sauce, Soy Sauce, Honey, Garlic, Ginger, Lemon Juice",
nutritionalInfo: { calories: 350, protein: 30, carbs: 15, fats: 18, fiber: 1, sugar: 5 },
allergens: "Soy"
},
"Chilli Gobi": {
ingredients: "Cauliflower, Onion, Green Chilies, Soy Sauce, Cornflour, Garlic, Ginger, Cumin Powder",
nutritionalInfo: { calories: 250, protein: 6, carbs: 35, fats: 12, fiber: 3, sugar: 2 },
allergens: "Soy, Gluten"
},
"Chilli Chicken": {
ingredients: "Chicken, Bell Pepper, Onion, Green Chilies, Soy Sauce, Cornflour, Garlic, Ginger",
nutritionalInfo: { calories: 400, protein: 35, carbs: 20, fats: 18, fiber: 2, sugar: 2 },
allergens: "Soy, Gluten"
},
"Chicken Manchurian": {
ingredients: "Chicken, Onion, Garlic, Ginger, Soy Sauce, Cornflour, Green Chilies, Capsicum",
nutritionalInfo: { calories: 350, protein: 25, carbs: 20, fats: 18, fiber: 2, sugar: 2 },
allergens: "Soy, Gluten"
},
"Chicken Curry": {
ingredients: "Chicken, Onion, Tomatoes, Ginger-Garlic Paste, Garam Masala, Coconut Milk, Coriander Leaves",
nutritionalInfo: { calories: 350, protein: 28, carbs: 15, fats: 12, fiber: 4, sugar: 2 },
allergens: "None"
},
"Chicken Biryani": {
ingredients: "Chicken, Basmati Rice, Onion, Tomatoes, Ginger-Garlic Paste, Biryani Masala, Mint Leaves, Curd",
nutritionalInfo: { calories: 500, protein: 35, carbs: 60, fats: 20, fiber: 5, sugar: 3 },
allergens: "Dairy"
},
"Channa Masala": {
ingredients: "Chickpeas, Onion, Tomatoes, Ginger-Garlic Paste, Garam Masala, Coriander Powder, Cumin Seeds, Lemon Juice",
nutritionalInfo: { calories: 250, protein: 10, carbs: 45, fats: 5, fiber: 6, sugar: 2 },
allergens: "None"
}
};
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');
document.getElementById('soft-drink-name').textContent = itemName;
document.getElementById('soft-drink-price').textContent = `$${itemPrice}`;
document.getElementById('soft-drink-quantity').value = '1';
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');
menuCards.forEach(card => {
const itemName = card.querySelector('.card-title').innerText;
const detailsDiv = card.querySelector('.item-details');
const detailsData = menuItemDetails[itemName];
if (detailsDiv && detailsData) { // Only populate if details div exists (not Soft Drinks)
detailsDiv.innerHTML = `
<h6>Ingredients</h6>
<p>${detailsData.ingredients}</p>
<h6>Nutritional Info</h6>
<div class="nutritional-info">
<span>Calories: ${detailsData.nutritionalInfo.calories} kcal</span>
<span>Carbs: ${detailsData.nutritionalInfo.carbs}g</span>
<span>Protein: ${detailsData.nutritionalInfo.protein}g</span>
<span>Fats: ${detailsData.nutritionalInfo.fats}g</span>
</div>
<h6>Allergens</h6>
<p>${detailsData.allergens}</p>
`;
} else if (detailsDiv) {
detailsDiv.innerHTML = '<p>No details available for this item.</p>';
}
});
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}`);
detailsDiv.classList.toggle('show');
this.innerText = detailsDiv.classList.contains('show') ? 'Hide Details' : '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;
});
// Soft Drinks modal quantity controls
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 < 10) {
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, 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('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"].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>