geethareddy's picture
Update templates/menu.html
2b0bdff 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/[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-card.highlighted {
border: 3px solid #0FAA39;
box-shadow: 0 0 10px rgba(15, 170, 57, 0.5);
}
.menu-video {
height: 200px;
width: 100%;
object-fit: contain;
border-radius: 15px 15px 0 0;
opacity: 0;
transition: opacity 0.5s ease-in-out;
background-color: #000;
}
.menu-video.loaded {
opacity: 1;
}
.menu-card img {
width: 100%; /* Ensure image takes up full width */
height: 200px; /* Set fixed height */
object-fit: cover; /* Ensure the image fills the container without distortion */
object-position: center; /* Center the image within the container */
}
.menu-card:hover .menu-video {
opacity: 1;
transform: scale(1.05);
}
.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: center;
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-bar-container input:focus {
border-bottom: 2px solid #FFA07A;
}
.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;
}
/* Addon Section */
.addon-section {
background-color: #fff;
border-radius: 10px;
margin-bottom: 15px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
overflow: hidden;
transition: transform 0.2s ease;
}
.addon-section:hover {
transform: translateY(-2px);
}
.addon-section h6 {
margin: 0;
padding: 12px 15px;
font-size: 1.1rem;
font-weight: 600;
color: #fff;
background: linear-gradient(45deg, #FFA07A, #FFB347);
cursor: pointer;
display: flex;
justify-content: space-between;
align-items: center;
}
.addon-section h6::after {
content: '\25BC';
font-size: 0.9rem;
transition: transform 0.2s ease;
}
.addon-section.collapsed h6::after {
transform: rotate(180deg);
}
.addon-section .form-check {
display: flex;
flex-direction: row-reverse;
align-items: center;
justify-content: space-between;
margin: 10px 15px;
padding: 0;
}
.addon-section .form-check-input {
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
width: 20px;
height: 20px;
border: 2px solid #6c757d;
border-radius: 4px;
background-color: #fff;
position: relative;
margin-left: 10px;
cursor: pointer;
transition: all 0.2s ease;
}
.addon-section .form-check-input:checked {
background-color: #0FAA39;
border-color: #0FAA39;
}
.addon-section .form-check-input:checked::before {
content: '\2713';
font-size: 12px;
position: absolute;
top: 2px;
left: 4px;
color: white;
animation: checkmark 0.2s ease-in-out;
}
.addon-section .form-check-input:hover {
transform: scale(1.1);
border-color: #0FAA39;
}
.addon-section .form-check-input:focus {
outline: none;
box-shadow: 0 0 0 3px rgba(15, 170, 57, 0.2);
}
.addon-section .form-check-label {
font-size: 0.95rem;
color: #343a40;
cursor: pointer;
flex: 1;
padding: 5px 8px;
border-radius: 4px;
transition: background-color 0.2s ease;
text-align: left;
}
.addon-section .form-check-label:hover {
background-color: #e6f4ea;
}
.addon-section .form-check-label .extra-charge {
color: #FFA07A;
font-weight: 600;
margin-left: 8px;
}
.addon-options {
max-height: 500px;
opacity: 1;
overflow: hidden;
transition: max-height 0.3s ease, opacity 0.3s ease;
}
.addon-options.collapsed {
max-height: 0;
opacity: 0;
}
.addon-loading {
display: flex;
justify-content: center;
align-items: center;
padding: 15px;
}
.addon-loading::after {
content: '';
width: 20px;
height: 20px;
border: 3px solid #0FAA39;
border-top: 3px solid transparent;
border-radius: 50%;
animation: spin 1s linear infinite;
}
/* Soft Drinks Modal Styling */
#softDrinkModal .modal-dialog {
max-width: 400px;
margin: 1.75rem auto;
animation: fadeInModal 0.3s ease-out;
}
#softDrinkModal .modal-content {
border-radius: 12px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
background-color: #fff;
overflow: hidden;
}
#softDrinkModal .modal-header {
background: linear-gradient(45deg, #FFA07A, #FFB347);
border-top-left-radius: 12px;
border-top-right-radius: 12px;
padding: 12px 15px;
border-bottom: none;
}
#softDrinkModal .modal-title {
font-size: 1.3rem;
font-weight: 600;
color: #fff;
}
#softDrinkModal .modal-body {
padding: 20px;
text-align: center;
}
#softDrinkModal .modal-body img {
max-height: 160px;
width: 100%;
object-fit: cover;
border-radius: 8px;
margin-bottom: 15px;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1);
}
#softDrinkModal .modal-body h5 {
font-size: 1.4rem;
font-weight: 600;
color: #333333;
margin-bottom: 10px;
}
#softDrinkModal .modal-body p {
font-size: 1.2rem;
font-weight: 500;
color: #000000;
margin-bottom: 20px;
}
#softDrinkModal .modal-body p::before {
content: '$';
color: #FFA07A;
font-weight: 600;
margin-right: 2px;
}
#softDrinkModal .quantity-controls {
display: flex;
justify-content: center;
align-items: center;
gap: 12px;
margin-bottom: 20px;
}
#softDrinkModal .quantity-controls .btn {
width: 32px;
height: 32px;
border-radius: 50%;
padding: 0;
font-size: 1.1rem;
line-height: 32px;
text-align: center;
background-color: #e9ecef;
border: none;
color: #333;
transition: background-color 0.2s ease, transform 0.1s ease;
}
#softDrinkModal .quantity-controls .btn:hover {
background-color: #d1d4d7;
transform: scale(1.1);
}
#softDrinkModal .quantity-controls .btn:active {
transform: scale(0.95);
}
#softDrinkModal .quantity-controls input {
width: 50px;
height: 32px;
text-align: center;
font-size: 1rem;
font-weight: 600;
border: 1px solid #ced4da;
border-radius: 6px;
background-color: #f8f9fa;
}
#softDrinkModal .modal-footer {
padding: 15px;
border-top: none;
display: flex;
justify-content: space-between;
align-items: center;
}
#softDrinkModal .modal-footer .btn-primary {
background-color: #0FAA39;
border-color: #0FAA39;
padding: 12px 30px;
font-size: 1.1rem;
font-weight: 600;
border-radius: 8px;
transition: background-color 0.2s ease, transform 0.1s ease;
width: auto;
}
#softDrinkModal .modal-footer .btn-primary:hover {
background-color: #0D9232;
transform: scale(1.05);
}
#softDrinkModal .modal-footer .btn-primary:active {
background-color: #0B7A29;
transform: scale(0.98);
}
#softDrinkModal .modal-footer .quantity-controls-footer {
display: flex;
gap: 12px;
}
#softDrinkModal .modal-footer .quantity-controls-footer .btn {
width: 32px;
height: 32px;
border-radius: 50%;
padding: 0;
font-size: 1.1rem;
line-height: 32px;
text-align: center;
background-color: #e9ecef;
border: none;
color: #333;
transition: background-color 0.2s ease, transform 0.1s ease;
}
#softDrinkModal .modal-footer .quantity-controls-footer .btn:hover {
background-color: #d1d4d7;
transform: scale(1.1);
}
#softDrinkModal .modal-footer .quantity-controls-footer .btn:active {
transform: scale(0.95);
}
/* Filter Form Styling */
#filter-form {
display: flex;
flex-direction: column;
align-items: flex-start; /* Align to the left */
justify-content: flex-start; /* Align to the top */
margin-bottom: 20px;
margin-left: 10px; /* Added 10px gap to the left */
}
.form-label {
font-size: 1.2rem;
font-weight: 600;
color: #333;
margin-bottom: 10px;
}
.toggle-container {
display: flex;
align-items: center;
gap: 10px;
margin: 8px 0;
}
.custom-toggle {
appearance: none;
width: 48px;
height: 24px;
background-color: #ccc;
border-radius: 12px;
position: relative;
cursor: pointer;
transition: background-color 0.3s ease;
}
.custom-toggle:checked {
background: linear-gradient(45deg, #32CD32, #3CB371); /* Changed to green color when checked */
}
.custom-toggle::before {
content: '';
position: absolute;
top: 2px;
left: 2px;
width: 20px;
height: 20px;
background-color: #fff;
border-radius: 50%;
transition: transform 0.3s ease;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
}
.custom-toggle:checked::before {
transform: translateX(24px); /* Moves the toggle to the right */
}
.toggle-container label {
font-size: 1rem;
color: #333;
cursor: pointer;
}
@keyframes checkmark {
from { transform: scale(0); }
to { transform: scale(1); }
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(10px); }
to { opacity: 1; transform: translateY(0); }
}
@keyframes fadeInModal {
from { opacity: 0; transform: translateY(-20px); }
to { opacity: 1; transform: translateY(0); }
}
.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: 15px;
}
.modal-body #modal-addons h5,
.modal-body #first-row h6 {
font-size: 1.2rem;
font-weight: 600;
margin-bottom: 15px;
color: #343a40;
}
.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;
}
.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;
}
/* Mobile-Specific Styles */
@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;
}
.modal-dialog {
max-width: 90%;
margin: 10px auto;
}
.modal-header {
padding: 8px 12px;
}
.modal-title {
font-size: 14px;
}
.modal-body {
max-height: 50vh;
padding: 12px;
}
.modal-body #modal-img {
max-height: 150px;
width: 100%;
max-width: 150px;
margin: 0 auto 8px;
display: block;
}
.modal-body #modal-name {
font-size: 18px;
margin-bottom: 5px;
}
.modal-body #modal-price {
font-size: 14px;
margin-bottom: 8px;
}
.modal-body #modal-description {
font-size: 12px;
margin-bottom: 10px;
}
.modal-body .nutritional-info {
font-size: 10px;
margin-bottom: 5px;
}
.modal-body #modal-addons h5,
.modal-body #first-row h6 {
font-size: 1rem;
margin-bottom: 10px;
}
.modal-footer {
padding: 8px;
}
.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;
}
/* Mobile-Specific Addon Styles */
.addon-section {
margin-bottom: 10px;
}
.addon-section h6 {
font-size: 1rem;
padding: 10px 12px;
}
.addon-section .form-check {
margin: 8px 12px;
}
.addon-section .form-check-input {
width: 18px;
height: 18px;
margin-left: 8px;
}
.addon-section .form-check-input:checked::before {
font-size: 10px;
top: 2px;
left: 3px;
}
.addon-section .form-check-label {
font-size: 0.85rem;
padding: 4px 6px;
}
.addon-section .form-check-label .extra-charge {
margin-left: 6px;
font-size: 0.8rem;
}
/* Mobile-Specific Soft Drinks Modal Styles */
#softDrinkModal .modal-dialog {
max-width: 90%;
}
#softDrinkModal .modal-content {
border-radius: 10px;
}
#softDrinkModal .modal-header {
padding: 10px 12px;
}
#softDrinkModal .modal-title {
font-size: 1.2rem;
}
#softDrinkModal .modal-body {
padding: 15px;
}
#softDrinkModal .modal-body img {
max-height: 130px;
margin-bottom: 12px;
}
#softDrinkModal .modal-body h5 {
font-size: 1.2rem;
margin-bottom: 8px;
}
#softDrinkModal .modal-body p {
font-size: 1rem;
margin-bottom: 15px;
}
#softDrinkModal .quantity-controls .btn {
width: 28px;
height: 28px;
font-size: 1rem;
line-height: 28px;
}
#softDrinkModal .quantity-controls input {
width: 45px;
height: 28px;
font-size: 0.9rem;
}
#softDrinkModal .modal-footer {
padding: 10px;
}
#softDrinkModal .modal-footer .btn-primary {
padding: 10px 25px;
font-size: 1rem;
}
#softDrinkModal .modal-footer .quantity-controls-footer .btn {
width: 28px;
height: 28px;
font-size: 1rem;
line-height: 28px;
}
/* Mobile-Specific Filter Form Styles */
#filter-form {
margin-bottom: 15px;
}
.form-label {
font-size: 1rem;
}
.toggle-container {
margin: 6px 0;
}
.custom-toggle {
width: 40px;
height: 20px;
}
.custom-toggle::before {
width: 16px;
height: 16px;
top: 2px;
left: 2px;
}
.custom-toggle:checked::before {
transform: translateX(20px);
}
.toggle-container label {
font-size: 0.9rem;
}
}
.chat-container {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: 'Segoe UI', Arial, sans-serif;
background-color: #f4f7fa;
overflow-x: hidden;
line-height: 1.5;
}
.chat-container {
width: 100%;
max-width: 800px;
height: 100vh;
margin: 0 auto;
background-color: #ffffff;
border-radius: 12px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
display: flex;
flex-direction: column;
overflow: hidden;
}
.chat-header {
background-color: #0078d4;
color: #ffffff;
padding: 12px;
text-align: center;
font-size: 18px;
font-weight: 600;
flex-shrink: 0;
}
.chat-messages {
flex: 1;
overflow-y: auto;
padding: 15px;
scroll-behavior: smooth;
}
.bot-message, .user-message {
padding: 10px 15px;
margin: 8px 0;
border-radius: 12px;
max-width: 80%;
font-size: 14px;
line-height: 1.4;
}
.bot-message {
background-color: #e8f0fe;
color: #333;
}
.user-message {
background-color: #0078d4;
color: #ffffff;
margin-left: auto;
}
.chat-input {
display: flex;
padding: 10px;
border-top: 1px solid #e0e0e0;
background-color: #f9f9f9;
flex-shrink: 0;
}
.chat-input input {
flex: 1;
padding: 10px;
border: 1px solid #ccc;
border-radius: 8px;
font-size: 14px;
outline: none;
}
.chat-input button {
padding: 10px 20px;
margin-left: 10px;
background-color: #0078d4;
color: #ffffff;
border: none;
border-radius: 8px;
cursor: pointer;
font-size: 14px;
transition: background-color 0.2s;
}
.chat-input button:hover {
background-color: #005ea2;
}
.ingredients-list, .menu-items-list, .customization-ingredients-list {
display: flex;
flex-wrap: nowrap; /* Ensure items stay in a single row */
overflow-x: auto; /* Allow horizontal scrolling */
padding: 10px;
margin: 10px 0;
background-color: #f5f7fa;
border-radius: 10px;
gap: 15px;
scroll-behavior: smooth;
}
.ingredient-item, .menu-item {
flex: 0 0 auto; /* Ensure items don't stretch */
width: 200px; /* Set a fixed width for each item */
background-color: #ffffff;
border: 1px solid #e0e0e0;
border-radius: 10px;
padding: 10px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.05);
transition: transform 0.2s;
text-align: center;
}
.ingredient-item:hover, .menu-item:hover {
transform: translateY(-2px);
}
.ingredient-item img, .menu-item img {
width: 100%;
height: 120px;
object-fit: cover;
border-radius: 8px;
margin-bottom: 8px;
}
.ingredient-item div, .menu-item div {
font-size: 14px;
font-weight: 600;
color: #333;
margin-bottom: 5px;
}
.ingredient-item button, .menu-item button {
padding: 8px;
background-color: #28a745;
color: #ffffff;
border: none;
border-radius: 6px;
cursor: pointer;
font-size: 12px;
width: 100%;
}
.ingredient-item button:hover, .menu-item button:hover {
background-color: #218838;
}
.selected-ingredients, .selected-customization-ingredients {
background-color: #f1f8ff;
padding: 15px;
border: 1px solid #b3d7ff;
border-radius: 10px;
margin: 10px 0;
font-size: 14px;
display: flex;
flex-wrap: wrap;
gap: 10px;
}
.selected-ingredients div, .selected-customization-ingredients div {
background-color: #d6e9ff;
padding: 5px 10px;
border-radius: 6px;
font-size: 13px;
}
.customization-input {
margin: 10px 0;
}
.customization-input textarea, .selected-customization-ingredients textarea {
width: 100%;
padding: 8px;
border: 1px solid #b3d7ff;
border-radius: 6px;
font-size: 14px;
outline: none;
margin-bottom: 10px;
}
.customization-input button, .submit-button, .submit-customization-button, .submit-cart-button {
padding: 10px 20px;
background-color: #0078d4;
color: #ffffff;
border: none;
border-radius: 8px;
cursor: pointer;
font-size: 14px;
transition: background-color 0.2s;
}
.customization-input button:hover, .submit-button:hover, .submit-customization-button:hover, .submit-cart-button:hover {
background-color: #005ea2;
}
.option-button {
padding: 10px 20px;
color: #ffffff;
border: none;
border-radius: 8px;
cursor: pointer;
font-size: 14px;
margin: 5px;
transition: background-color 0.2s;
}
.option-button.green {
background-color: #28a745;
}
.option-button.green:hover {
background-color: #218838;
}
.option-button.red {
background-color: #dc3545;
}
.option-button.red:hover {
background-color: #c82333;
}
.cart-items {
background-color: #f1f8ff;
padding: 15px;
border: 1px solid #b3d7ff;
border-radius: 10px;
margin: 10px 0;
font-size: 14px;
}
.cart-item {
display: flex;
align-items: center;
background-color: #d6e9ff;
padding: 5px 10px;
border-radius: 6px;
font-size: 13px;
margin: 5px 0;
}
.cart-item img {
width: 30px;
height: 30px;
object-fit: cover;
border-radius: 6px;
margin-right: 8px;
}
.cart-item .remove-button {
padding: 5px 10px;
margin-left: 8px;
background-color: #dc3545;
color: #ffffff;
border: none;
border-radius: 6px;
cursor: pointer;
font-size: 12px;
}
.cart-item .remove-button:hover {
background-color: #c82333;
}
/* Responsive Design */
@media (max-width: 480px) {
.chat-container {
border-radius: 0;
box-shadow: none;
}
.chat-header {
font-size: 16px;
padding: 10px;
}
.chat-messages {
padding: 10px;
}
.bot-message, .user-message {
font-size: 13px;
padding: 8px 12px;
margin: 5px 0;
}
.chat-input input {
font-size: 13px;
padding: 8px;
}
.chat-input button {
font-size: 13px;
padding: 8px 15px;
}
.ingredients-list, .menu-items-list, .customization-ingredients-list {
flex-direction: row; /* Keep the items in a row on mobile */
gap: 10px;
}
.ingredient-item, .menu-item {
flex: 0 0 150px; /* Adjust the item width to fit smaller screens */
}
.ingredient-item img, .menu-item img {
height: 100px;
}
.ingredient-item div, .menu-item div {
font-size: 13px;
}
.ingredient-item button, .menu-item button {
font-size: 11px;
padding: 6px;
}
.option-button {
font-size: 13px;
padding: 8px 15px;
}
.selected-ingredients, .selected-customization-ingredients, .cart-items {
padding: 10px;
gap: 8px;
}
.cart-item {
font-size: 12px;
padding: 4px 8px;
}
.cart-item img {
width: 25px;
height: 25px;
}
}
@media (min-width: 481px) and (max-width: 768px) {
.chat-container {
max-width: 600px;
}
.chat-header {
font-size: 17px;
padding: 11px;
}
.chat-messages {
padding: 12px;
}
.ingredient-item, .menu-item {
flex: 0 0 180px;
}
.ingredient-item img, .menu-item img {
height: 110px;
}
}
</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('combined_summary.combined_summary') }}" class="dropdown-item">MY Summary</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>
</div>
<form method="get" action="/menu" class="text-center mb-4" id="filter-form">
<label class="form-label fw-bold">Filters:</label>
<div class="toggle-container">
<!-- Veg Only Toggle -->
<input type="checkbox" id="veg-toggle" name="veg"
{% if selected_category == "Veg" %}checked{% endif %}
class="custom-toggle" onchange="handleToggle('veg')"
aria-label="Toggle Vegetarian filter">
<label for="veg-toggle">Veg</label>
</div>
<div class="toggle-container">
<!-- Customized Dish Toggle -->
<input type="radio" id="category-CustomizedDish" name="category" value="Customized Dish"
{% if selected_category == "Customized Dish" %}checked{% endif %}
class="custom-toggle" onchange="handleToggle('custom')"
aria-label="Toggle Customized Dish filter">
<label for="category-CustomizedDish">Customized Dish</label>
</div>
</form>
<div class="container mt-4">
{% if selected_category == "Customized Dish" %}
<div id="custom-dish-form" class="mt-4">
<div class="chat-container">
<div class="chat-header">🍳 Chef Bot</div>
<div class="chat-messages" id="chatMessages">
<!-- Initial message will be set by JavaScript based on session -->
</div>
<div class="chat-input">
<input type="text" id="userInput" placeholder="Type your name or message...">
<button onclick="sendMessage()">Send</button>
</div>
</div>
</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" data-item-name="{{ item.Name | default('Unnamed Item') }}" data-item-section="{{ item.Section__c | default(section) }}">
<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>
</div>
<div class="d-flex flex-column align-items-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 }}"
data-item-description="{{ item.Description__c | default('No description') }}"
data-item-image2="{{ item.Image2__c | default(item.Image1__c) }}">
{% 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>
{% if item.Section__c != 'Apetizer' and item.Section__c != 'Customized dish' %}
<span class="customisable-text">Customisable</span>
{% endif %}
{% endif %}
</div>
</div>
</div>
</div>
{% 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(' ', '-') }}">
<h6>Description</h6>
<p>{{ item.Description__c | default('No description available') }}</p>
<h6>Ingredients</h6>
<p>{{ item.Ingredientsinfo__c | default('Not specified') }}</p>
<h6>Nutritional Info</h6>
<p class="nutritional-info">{{ 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 id="cart-item-count" class="cart-icon-badge" style="display: none;">0</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 id="modal-addons" class="modal-addons mt-4">
<h5>Customization Options</h5>
<div id="addons-list" class="addons-container addon-loading"></div>
</div>
<div class="mt-4">
<h6>Special Instructions</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">
<img id="soft-drink-image" class="img-fluid rounded mb-3 d-block mx-auto" alt="Soft Drink Image">
<div class="text-center mb-3">
<h5 id="soft-drink-name"></h5>
<p id="soft-drink-price"></p>
</div>
<div class="quantity-controls">
<button type="button" class="btn" id="soft-drink-decrease" onclick="updateSoftDrinkQuantity(-1)" aria-label="Decrease quantity by 1">-</button>
<input type="text" class="form-control text-center" id="soft-drink-quantity" value="1" readonly aria-label="Selected quantity">
<button type="button" class="btn" id="soft-drink-increase" onclick="updateSoftDrinkQuantity(1)" aria-label="Increase quantity by 1">+</button>
</div>
</div>
<div class="modal-footer">
<div class="quantity-controls-footer">
<button type="button" class="btn" id="soft-drink-decrease-footer" onclick="updateSoftDrinkQuantity(-1)" aria-label="Decrease quantity by 1">-1</button>
<button type="button" class="btn" id="soft-drink-increase-footer" onclick="updateSoftDrinkQuantity(1)" aria-label="Increase quantity by 1">+1</button>
</div>
<button type="button" class="btn btn-primary" onclick="addSoftDrinkToCart()" aria-label="Add soft drink to cart">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;
let baseItemPrice = 0;
const menuItems = [
{% for section, items in ordered_menu.items() %}
{% for item in items %}
{
name: "{{ item.Name | default('Unnamed Item') }}",
section: "{{ item.Section__c | default(section) }}"
},
{% 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 updateModalPrice() {
const selectedAddOns = Array.from(
document.querySelectorAll('#addons-list input[type="checkbox"]:checked')
).map(addon => parseFloat(addon.getAttribute('data-price')) || 0);
const totalAddOnPrice = selectedAddOns.reduce((sum, price) => sum + price, 0);
const totalPrice = baseItemPrice + totalAddOnPrice;
document.getElementById('modal-price').innerText = `$${totalPrice.toFixed(2)}`;
}
function updateSoftDrinkQuantity(delta) {
const quantityInput = document.getElementById('soft-drink-quantity');
let currentQuantity = parseInt(quantityInput.value) || 1;
currentQuantity = Math.max(1, currentQuantity + delta);
quantityInput.value = currentQuantity;
}
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';
document.getElementById('soft-drink-image').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';
}
}
window.most_common_addons = {{ most_common_addons | tojson }};
console.log("Most common add-ons: ", window.most_common_addons);
function showItemDetails(name, price, image, description, section, selectedCategory) {
document.getElementById('modal-name').innerText = name;
baseItemPrice = parseFloat(price) || 0;
document.getElementById('modal-price').innerText = `$${baseItemPrice.toFixed(2)}`;
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 = '';
document.getElementById('addons-list').classList.add('addon-loading');
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.classList.remove('addon-loading');
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');
optionsContainer.classList.add('addon-options');
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}"
aria-label="Select ${option} for ${addon.name}">
<label class="form-check-label" for="${optionId}">
${option} ${addon.extra_charge ? `<span class="extra-charge">($${addon.extra_charge_amount.toFixed(2)})</span>` : ''}
</label>
`;
optionsContainer.appendChild(listItem);
});
sectionDiv.appendChild(optionsContainer);
addonsList.appendChild(sectionDiv);
});
const addonSections = addonsList.querySelectorAll('.addon-section');
addonSections.forEach(section => {
const title = section.querySelector('h6');
const options = section.querySelector('.addon-options');
title.addEventListener('click', () => {
section.classList.toggle('collapsed');
options.classList.toggle('collapsed');
});
});
document.querySelectorAll('.addon-option').forEach(checkbox => {
checkbox.addEventListener('change', updateModalPrice);
});
document.querySelectorAll('.addon-option').forEach(checkbox => {
checkbox.addEventListener('change', function () {
const groupName = this.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 && this.checked) {
document.querySelectorAll(`.addon-option[data-group="${groupName}"]`).forEach(otherCheckbox => {
if (otherCheckbox !== this) {
otherCheckbox.checked = false;
}
});
}
});
});
// Pre-select the highest-count spice level
if (window.most_common_addons && window.most_common_addons.length > 0) {
const checkboxes = document.querySelectorAll('.addon-option');
const categorySelection = {
"Select Spice Level": null,
"Choose Spice Level": null,
"Raita/Sides": [],
};
// First pass: Find and select the highest-count spice level
for (let spice of window.most_common_addons) {
const isSpiceLevel = ["Mild", "Medium", "Spicy", "Extra Spicy"].includes(spice); // Define valid spice levels
if (isSpiceLevel) {
checkboxes.forEach(checkbox => {
const checkboxName = checkbox.getAttribute('data-name').trim();
const checkboxGroup = checkbox.getAttribute('data-group');
if ((checkboxGroup === "Select Spice Level" || checkboxGroup === "Choose Spice Level") &&
checkboxName === spice && categorySelection[checkboxGroup] === null) {
console.log(`Pre-selecting highest-count spice level: ${checkboxName}`);
checkbox.checked = true;
categorySelection[checkboxGroup] = checkboxName;
}
});
if (categorySelection["Select Spice Level"] || categorySelection["Choose Spice Level"]) break; // Stop after selecting the first spice
}
}
// Second pass: Select other non-spice add-ons (e.g., Raita/Sides)
checkboxes.forEach(checkbox => {
const checkboxName = checkbox.getAttribute('data-name').trim();
const checkboxGroup = checkbox.getAttribute('data-group');
if (checkboxGroup === "Raita/Sides" && window.most_common_addons.includes(checkboxName)) {
console.log(`Pre-selecting add-on: ${checkboxName}`);
checkbox.checked = true;
categorySelection["Raita/Sides"].push(checkboxName);
}
});
}
})
.catch(err => {
console.error('Error fetching add-ons:', err);
document.getElementById('addons-list').classList.remove('addon-loading');
document.getElementById('addons-list').innerHTML = '<p>Error loading customization options.</p>';
});
}
function addToCartFromModal() {
if (isProcessingRequest) return;
isProcessingRequest = true;
const modalSectionEl = document.getElementById('modal-section');
const section = modalSectionEl.getAttribute('data-section');
const selectedCategory = modalSectionEl.getAttribute('data-category');
const itemName = document.getElementById('modal-name').innerText;
const itemPrice = parseFloat(document.getElementById('modal-price').innerText.replace('$', '')) || 0;
const itemImage = document.getElementById('modal-img').src;
const quantity = parseInt(document.getElementById('quantityInput').value) || 1;
const instructions = document.getElementById('modal-instructions').value;
const selectedAddOns = Array.from(
document.querySelectorAll('#addons-list input[type="checkbox"]:checked')
).map(addon => ({
name: addon.getAttribute('data-name'),
price: parseFloat(addon.getAttribute('data-price')) || 0
}));
if (!itemName || isNaN(itemPrice) || !section || !itemImage || quantity < 1) {
console.error('Invalid cart item data:', { itemName, itemPrice, section, itemImage, quantity });
alert('Invalid item data. Please try again.');
isProcessingRequest = false;
return;
}
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) {
updateCartUI(data.cart);
const modal = bootstrap.Modal.getInstance(document.getElementById('itemModal'));
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('itemModal'));
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('itemModal'));
modal.hide();
})
.finally(() => {
isProcessingRequest = false;
});
}
function handleToggle(source) {
const form = document.getElementById("filter-form");
const veg = document.getElementById("veg-toggle");
const custom = document.getElementById("category-CustomizedDish");
if (source === 'veg') {
if (veg.checked) {
custom.checked = false;
}
} else if (source === 'custom') {
if (custom.checked) {
veg.checked = false;
}
}
if (!custom.checked && !veg.checked) {
custom.checked = false;
}
form.submit();
}
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 searchBar = document.getElementById('searchBar');
searchBar.addEventListener('click', function () {
window.location.href = '/search';
});
const selectedItem = localStorage.getItem('selectedItem');
if (selectedItem) {
try {
const { name, section } = JSON.parse(selectedItem);
const menuCards = document.querySelectorAll('.menu-card');
let targetCard = null;
let buttonContainer = null;
menuCards.forEach(card => {
const itemName = card.getAttribute('data-item-name');
const itemSection = card.getAttribute('data-item-section');
if (itemName === name && itemSection === section) {
targetCard = card;
buttonContainer = card.querySelector('.button-container');
card.classList.add('highlighted');
card.scrollIntoView({ behavior: 'smooth', block: 'center' });
const toggleLink = card.querySelector('.toggle-details');
if (toggleLink) {
toggleLink.click();
}
}
});
if (buttonContainer) {
if (section === 'Soft Drinks') {
showSoftDrinkModal(buttonContainer.querySelector('.add-to-cart-btn'));
} else {
const name = buttonContainer.getAttribute('data-item-name');
const price = buttonContainer.getAttribute('data-item-price');
const image = buttonContainer.getAttribute('data-item-image2');
const description = buttonContainer.getAttribute('data-item-description');
const category = buttonContainer.getAttribute('data-item-category');
showItemDetails(name, price, image, description, section, category);
const modal = new bootstrap.Modal(document.getElementById('itemModal'));
modal.show();
}
}
} catch (err) {
console.error('Error parsing selected item:', err);
}
localStorage.removeItem('selectedItem');
}
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 nextSibling = video.nextElementSibling;
if (nextSibling && nextSibling.tagName === 'SOURCE') {
nextSibling.src = src;
} else {
const source = video.querySelector('source');
if (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 = document.querySelector(`.toggle-details[data-item-name="${otherDetails.id.replace('details-', '').replace(/-/g, ' ')}"]`);
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 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 quantity = parseInt(quantityInput.value) || 1;
quantity = Math.max(1, quantity - 1);
quantityInput.value = quantity;
});
increaseBtn.addEventListener('click', function () {
let quantity = parseInt(quantityInput.value) || 1;
quantity += 1;
quantityInput.value = quantity;
});
});
let currentStep = 'greeting'; // other possible values: 'food_type', 'select_ingredients', 'menu_display', 'customization', 'post_cart'
let conversation = [];
let selectedIngredients = [];
let selectedMenuItem = null;
let cart = [];
// Get the user name passed from Flask (via Jinja)
const userName = "{{ user_name }}"; // Jinja syntax to inject user_name from Flask session
window.onload = function() {
if (userName) {
// If user_name exists, greet the user directly and proceed to food preference
conversation.push({ role: 'bot', message: `Nice to meet you, ${userName}! 😊 Let's create your perfect meal! What type of food would you prefer?` });
displayConversation(); // Display the current conversation
displayOptions([
{ text: 'Vegetarian', class: 'green' },
{ text: 'Non-Vegetarian', class: 'red' }
]);
} else {
// Ask for the name if it's not found
conversation.push({ role: 'bot', message: "Hi there! I'm Chef Bot! May I know your name?" });
displayConversation(); // Display the conversation
}
};
// Function to add messages to the chat
function addMessage(role, message) {
const chatMessages = document.getElementById('chatMessages');
if (!chatMessages) {
console.error('Chat messages container not found!');
return;
}
const messageDiv = document.createElement('div');
messageDiv.className = role === 'bot' ? 'bot-message' : 'user-message';
messageDiv.textContent = message;
chatMessages.appendChild(messageDiv);
chatMessages.scrollTop = chatMessages.scrollHeight;
}
// Function to display all conversation messages
function displayConversation() {
const chatMessages = document.getElementById('chatMessages');
chatMessages.innerHTML = ''; // Clear previous messages
conversation.forEach(msg => {
const messageDiv = document.createElement('div');
messageDiv.className = msg.role === 'bot' ? 'bot-message' : 'user-message';
messageDiv.textContent = msg.message;
chatMessages.appendChild(messageDiv);
});
chatMessages.scrollTop = chatMessages.scrollHeight; // Scroll to bottom
}
// Function to handle user input
function sendMessage() {
const userInput = document.getElementById('userInput').value.trim();
if (userInput) {
addMessage('user', userInput);
conversation.push({ role: 'user', message: userInput });
document.getElementById('userInput').value = ''; // Clear input field
setTimeout(() => handleResponse(userInput), 500);
} else {
console.warn('Empty message!');
}
}
function handleResponse(userInput) {
const lastMessage = conversation[conversation.length - 1].message.toLowerCase();
let botResponse = '';
let options = [];
// If user has already selected food type, proceed without repeating the question
if (lastMessage.includes('non-vegetarian')) {
conversation.push({ role: 'user', message: 'Non-Vegetarian' });
console.log("Food preference selected: Non-Vegetarian");
botResponse = 'Great choice! 🍽️ Please select a non-vegetarian option:';
fetchIngredients('non-vegetarian');
return;
} else if (lastMessage.includes('vegetarian')) {
conversation.push({ role: 'user', message: 'Vegetarian' });
console.log("Food preference selected: Vegetarian");
botResponse = 'Great choice! 🍽️ Here are some vegetarian ingredients:';
fetchIngredients('vegetarian');
return;
} else if (lastMessage.includes('chicken') || lastMessage.includes('beef') || lastMessage.includes('lamb')) {
conversation.push({ role: 'user', message: lastMessage });
console.log(`Non-veg option selected: ${lastMessage}`);
botResponse = `Great! Here are some ${lastMessage} ingredients available:`;
fetchIngredients(lastMessage.toLowerCase());
return;
} else if (lastMessage.includes('yes') && selectedMenuItem) {
botResponse = 'Here are some ingredients to customize your dish:';
handleYesResponse();
return;
} else if (lastMessage.includes('no') && selectedMenuItem) {
// Submit the dish without customization
submitCustomizationIngredients();
return;
} else if (lastMessage.includes('yes') && currentStep === 'post_cart') {
// After user says 'yes', ask again for food preference (veg or non-veg)
botResponse = `Let's get started again! What type of food would you prefer this time?`;
options = [
{ text: 'Vegetarian', class: 'green' },
{ text: 'Non-Vegetarian', class: 'red' }
];
currentStep = 'food_type';
addMessage('bot', botResponse);
displayOptions(options);
return;
} else if (lastMessage.includes('non-vegetarian') && currentStep === 'food_type') {
// Handle non-vegetarian selection
conversation.push({ role: 'user', message: 'Non-Vegetarian' });
console.log("Food preference selected: Non-Vegetarian");
botResponse = 'Great choice! 🍽️ Please select a non-vegetarian option:';
fetchIngredients('non-vegetarian');
return;
} else if (lastMessage.includes('vegetarian') && currentStep === 'food_type') {
// Handle vegetarian selection
conversation.push({ role: 'user', message: 'Vegetarian' });
console.log("Food preference selected: Vegetarian");
botResponse = 'Great choice! 🍽️ Here are some vegetarian ingredients:';
fetchIngredients('vegetarian');
return;
} else if (lastMessage.includes('no') && currentStep === 'post_cart') {
addMessage('bot', 'Awesome! 🧾 Here’s your final cart:');
displayCart(); // Optional: show final cart again
addMessage('bot', 'Thank you for your order! 👨‍🍳🍲');
currentStep = 'end';
return;
}
addMessage('bot', botResponse);
if (options.length > 0) {
displayOptions(options);
}
displayCart();
}
function handleYesResponse() {
if (!selectedMenuItem) {
addMessage('bot', 'No dish selected. Please choose a dish first.');
return;
}
const botResponse = `Here is your selected dish: ${selectedMenuItem.name}`;
addMessage('bot', botResponse);
// Display selected menu item
const chatMessages = document.getElementById('chatMessages');
const menuItemDiv = document.createElement('div');
menuItemDiv.className = 'menu-item';
const img = document.createElement('img');
img.src = selectedMenuItem.image_url || 'https://via.placeholder.com/120';
img.alt = selectedMenuItem.name;
const name = document.createElement('div');
name.textContent = selectedMenuItem.name;
menuItemDiv.appendChild(img);
menuItemDiv.appendChild(name);
chatMessages.appendChild(menuItemDiv);
// Fetch ingredients for customization
fetchIngredientsForCustomization('both');
}
function fetchIngredientsForCustomization(foodPreference) {
fetch('/get_ingredients', { // Reuse existing endpoint
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ dietary_preference: foodPreference })
})
.then(response => response.json())
.then(data => {
if (data.error) {
addMessage('bot', `Sorry, there was an error fetching ingredients: ${data.error}`);
} else {
const ingredients = data.ingredients || [];
addMessage('bot', 'Please select ingredients to customize:');
displayCustomizationIngredients(ingredients);
}
})
.catch(error => {
addMessage('bot', `Error: Unable to connect to the ingredient database. ${error.message}`);
});
}
function displayCustomizationIngredients(ingredients) {
const chatMessages = document.getElementById('chatMessages');
if (!chatMessages) {
console.error('Chat messages container not found for customization ingredients!');
return;
}
let ingredientsList = document.querySelector('.customization-ingredients-list');
if (!ingredientsList) {
ingredientsList = document.createElement('div');
ingredientsList.className = 'customization-ingredients-list';
chatMessages.appendChild(ingredientsList);
} else {
ingredientsList.innerHTML = '';
}
ingredients.forEach(ingredient => {
const item = document.createElement('div');
item.className = 'ingredient-item';
const img = document.createElement('img');
img.src = ingredient.image_url || 'https://via.placeholder.com/120';
img.alt = ingredient.name;
const name = document.createElement('div');
name.textContent = ingredient.name;
const button = document.createElement('button');
button.textContent = 'Add';
button.onclick = () => {
if (!selectedIngredients.some(item => item.name === ingredient.name)) {
selectedIngredients.push({ name: ingredient.name, image_url: ingredient.image_url });
displaySelectedCustomizationIngredients();
}
};
item.appendChild(img);
item.appendChild(name);
item.appendChild(button);
ingredientsList.appendChild(item);
});
displaySelectedCustomizationIngredients();
}
function displaySelectedCustomizationIngredients() {
const chatMessages = document.getElementById('chatMessages');
if (!chatMessages) {
console.error('Chat messages container not found for selected customization ingredients!');
return;
}
let selectedArea = document.querySelector('.selected-customization-ingredients');
if (!selectedArea) {
selectedArea = document.createElement('div');
selectedArea.className = 'selected-customization-ingredients';
chatMessages.appendChild(selectedArea);
} else {
selectedArea.innerHTML = '';
}
const selectedIngredientsText = selectedIngredients.length > 0
? `${selectedMenuItem.name} with ${selectedIngredients.map(ingredient => ingredient.name).join(', ')}`
: selectedMenuItem.name;
const ingredientsDiv = document.createElement('div');
ingredientsDiv.textContent = selectedIngredientsText;
selectedArea.appendChild(ingredientsDiv);
selectedIngredients.forEach(ingredient => {
const div = document.createElement('div');
div.textContent = ingredient.name;
const removeButton = document.createElement('button');
removeButton.textContent = 'X';
removeButton.style.marginLeft = '5px';
removeButton.style.padding = '2px 5px';
removeButton.style.backgroundColor = '#dc3545';
removeButton.style.color = '#ffffff';
removeButton.style.border = 'none';
removeButton.style.borderRadius = '4px';
removeButton.style.cursor = 'pointer';
removeButton.onclick = () => {
selectedIngredients = selectedIngredients.filter(item => item.name !== ingredient.name);
displaySelectedCustomizationIngredients();
};
div.appendChild(removeButton);
selectedArea.appendChild(div);
});
if (!document.querySelector('.submit-customization-button')) {
const textarea = document.createElement('textarea');
textarea.placeholder = 'Enter any special instructions...';
selectedArea.appendChild(textarea);
const submitButton = document.createElement('button');
submitButton.className = 'submit-customization-button';
submitButton.textContent = 'Submit Customization';
submitButton.onclick = submitCustomizationIngredients;
selectedArea.appendChild(submitButton);
}
}
function submitCustomizationIngredients() {
if (!selectedMenuItem) {
addMessage('bot', 'No dish selected. Please choose a dish first.');
return;
}
const textarea = document.querySelector('.selected-customization-ingredients textarea');
const instructions = textarea ? textarea.value.trim() : '';
fetch('/submit_customization_ingredients', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
menu_item: selectedMenuItem,
ingredients: selectedIngredients,
instructions: instructions
})
})
.then(response => response.json())
.then(data => {
if (data.success) {
addToCart({ ...selectedMenuItem, instructions, ingredients: selectedIngredients });
currentStep = 'post_cart';
addMessage('bot', 'Customization submitted successfully! Item added to cart.');
// Redirect to the cart page
window.location.href = '/cart/cart'; // This will redirect the user to the cart page
} else {
addMessage('bot', `Error: ${data.error}`);
}
})
.catch(error => {
addMessage('bot', `Error submitting customization: ${error.message}`);
});
}
function fetchIngredients(foodPreference) {
fetch('/get_ingredients', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ dietary_preference: foodPreference })
})
.then(response => response.json())
.then(data => {
if (data.error) {
addMessage('bot', `Sorry, there was an error fetching ingredients: ${data.error}`);
} else {
const ingredients = data.ingredients || [];
addMessage('bot', 'Please select ingredients:');
displayIngredientsList(ingredients);
displaySelectedIngredients();
}
})
.catch(error => {
addMessage('bot', `Error: Unable to connect to the ingredient database. ${error.message}`);
});
}
function fetchMenuItems(params) {
fetch('/get_menu_items', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(params)
})
.then(response => response.json())
.then(data => {
if (data.error) {
addMessage('bot', `Sorry, there was an error fetching menu items: ${data.error}`);
} else {
const menuItems = data.menu_items || [];
addMessage('bot', 'Here are some dishes based on your selection:');
displayMenuItems(menuItems);
}
})
.catch(error => {
addMessage('bot', `Error: Unable to connect to the menu database. ${error.message}`);
});
}
function displayIngredientsList(ingredients) {
const chatMessages = document.getElementById('chatMessages');
if (!chatMessages) {
console.error('Chat messages container not found for ingredients!');
return;
}
let ingredientsList = document.querySelector('.ingredients-list');
if (!ingredientsList) {
ingredientsList = document.createElement('div');
ingredientsList.className = 'ingredients-list';
chatMessages.appendChild(ingredientsList);
} else {
ingredientsList.innerHTML = '';
}
ingredients.forEach(ingredient => {
const item = document.createElement('div');
item.className = 'ingredient-item';
const img = document.createElement('img');
img.src = ingredient.image_url || 'https://via.placeholder.com/120';
img.alt = ingredient.name;
const name = document.createElement('div');
name.textContent = ingredient.name;
const button = document.createElement('button');
button.textContent = 'Add';
button.onclick = () => {
if (!selectedIngredients.some(item => item.name === ingredient.name)) {
selectedIngredients.push(ingredient);
displaySelectedIngredients();
}
};
item.appendChild(img);
item.appendChild(name);
item.appendChild(button);
ingredientsList.appendChild(item);
});
}
function displayMenuItems(menuItems) {
const chatMessages = document.getElementById('chatMessages');
if (!chatMessages) {
console.error('Chat messages container not found for menu items!');
return;
}
let menuItemsList = document.querySelector('.menu-items-list');
if (!menuItemsList) {
menuItemsList = document.createElement('div');
menuItemsList.className = 'menu-items-list';
chatMessages.appendChild(menuItemsList);
} else {
menuItemsList.innerHTML = '';
}
menuItems.forEach(item => {
const menuItem = document.createElement('div');
menuItem.className = 'menu-item';
const img = document.createElement('img');
img.src = item.image_url || 'https://via.placeholder.com/120';
img.alt = item.name;
const name = document.createElement('div');
name.textContent = item.name;
const button = document.createElement('button');
button.textContent = 'Add to Cart';
button.onclick = () => {
selectedMenuItem = item;
addMessage('bot', `World-class selection! Would you like to customize your dish further?`);
const options = [
{ text: 'Yes', class: 'green' },
{ text: 'No', class: 'red' }
];
displayOptions(options);
};
menuItem.appendChild(img);
menuItem.appendChild(name);
menuItem.appendChild(button);
menuItemsList.appendChild(menuItem);
});
}
function displaySelectedIngredients() {
const chatMessages = document.getElementById('chatMessages');
if (!chatMessages) {
console.error('Chat messages container not found for selected ingredients!');
return;
}
let selectedArea = document.querySelector('.selected-ingredients');
if (!selectedArea) {
selectedArea = document.createElement('div');
selectedArea.className = 'selected-ingredients';
chatMessages.appendChild(selectedArea);
} else {
selectedArea.innerHTML = '';
}
selectedIngredients.forEach(ingredient => {
const div = document.createElement('div');
div.textContent = ingredient.name;
const removeButton = document.createElement('button');
removeButton.textContent = 'X';
removeButton.style.marginLeft = '5px';
removeButton.style.padding = '2px 5px';
removeButton.style.backgroundColor = '#dc3545';
removeButton.style.color = '#ffffff';
removeButton.style.border = 'none';
removeButton.style.borderRadius = '4px';
removeButton.style.cursor = 'pointer';
removeButton.onclick = () => {
selectedIngredients = selectedIngredients.filter(item => item.name !== ingredient.name);
displaySelectedIngredients();
};
div.appendChild(removeButton);
selectedArea.appendChild(div);
});
if (selectedIngredients.length > 0) {
let submitButton = document.querySelector('.submit-button');
if (!submitButton) {
submitButton = document.createElement('button');
submitButton.className = 'submit-button';
submitButton.textContent = 'Submit Ingredients';
submitButton.onclick = submitIngredients;
chatMessages.appendChild(submitButton);
}
}
}
function submitIngredients() {
if (selectedIngredients.length === 0) {
addMessage('bot', 'No ingredients selected. Please choose some ingredients.');
return;
}
const ingredientNames = selectedIngredients.map(ingredient => ingredient.name.toLowerCase()).join(' ');
fetchMenuItems({ ingredient_names: ingredientNames });
}
function addToCart(item) {
cart.push(item);
console.log('Cart:', cart);
displayCart();
}
function displayCart() {
const chatMessages = document.getElementById('chatMessages');
if (!chatMessages) {
console.error('Chat messages container not found for cart!');
return;
}
let cartArea = document.querySelector('.cart-items');
if (!cartArea) {
cartArea = document.createElement('div');
cartArea.className = 'cart-items';
chatMessages.appendChild(cartArea);
} else {
cartArea.innerHTML = '';
}
if (cart.length > 0) {
const label = document.createElement('div');
label.textContent = 'Cart:';
cartArea.appendChild(label);
cart.forEach((item, index) => {
const itemDiv = document.createElement('div');
itemDiv.className = 'cart-item';
const img = document.createElement('img');
img.src = item.image_url || 'https://via.placeholder.com/30';
img.alt = item.name;
const name = document.createElement('div');
const text = item.instructions
? `${item.name} (${item.instructions})`
: item.name;
name.textContent = item.ingredients.length > 0
? `${text} with ${item.ingredients.map(i => i.name).join(', ')}`
: text;
const removeButton = document.createElement('button');
removeButton.className = 'remove-button';
removeButton.textContent = 'X';
removeButton.onclick = () => {
cart.splice(index, 1);
displayCart();
};
itemDiv.appendChild(img);
itemDiv.appendChild(name);
itemDiv.appendChild(removeButton);
cartArea.appendChild(itemDiv);
});
let submitCartButton = document.querySelector('.submit-cart-button');
if (!submitCartButton) {
submitCartButton = document.createElement('button');
submitCartButton.className = 'submit-cart-button';
submitCartButton.textContent = 'Submit Cart';
submitCartButton.onclick = submitCart;
cartArea.appendChild(submitCartButton);
}
}
}
function submitCart() {
if (cart.length === 0) {
addMessage('bot', 'Your cart is empty!');
return;
}
fetch('/submit_customization_ingredients', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ items: cart })
})
.then(response => response.json())
.then(data => {
if (data.success) {
addMessage('bot', 'Cart submitted successfully!');
cart = [];
displayCart();
} else {
addMessage('bot', `Error submitting cart: ${data.error}`);
}
})
.catch(error => {
addMessage('bot', `Error submitting cart: ${error.message}`);
});
}
function displayOptions(options) {
const chatMessages = document.getElementById('chatMessages');
if (!chatMessages) {
console.error('Chat messages container not found for options!');
return;
}
console.log('Displaying options:', options);
options.forEach(opt => {
const button = document.createElement('button');
button.textContent = opt.text;
button.className = `option-button ${opt.class}`;
button.onclick = () => {
addMessage('user', opt.text);
conversation.push({ role: 'user', message: opt.text });
console.log(`User selected option: ${opt.text}`);
setTimeout(() => handleResponse(opt.text), 500);
};
chatMessages.appendChild(button);
});
chatMessages.appendChild(document.createElement('br'));
chatMessages.scrollTop = chatMessages.scrollHeight;
console.log('Options displayed');
}
document.getElementById('userInput').addEventListener('keypress', function(e) {
if (e.key === 'Enter') {
sendMessage();
}
});
console.log('Script loaded successfully');
</script>
</body>
</html>