BiryaniHubflaskhema6 / templates /combined_summary.html
nagasurendra's picture
Update templates/combined_summary.html
d295a56 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="x-ua-compatible" content="ie=edge">
<title>Order Summary</title>
<script src="https://cdn.tailwindcss.com"></script>
<style>
/* Custom animations */
@keyframes fadeIn {
from { opacity: 0; transform: translateY(10px); }
to { opacity: 1; transform: translateY(0); }
}
@keyframes lightMoveUp {
0% { background-position: 0 100%; }
100% { background-position: 0 0%; }
}
@keyframes shine {
from { transform: rotate(0deg) translateX(-100%); }
to { transform: rotate(25deg) translateX(100%); }
}
@keyframes pulse {
0% { box-shadow: 0 0 0 0 rgba(255, 179, 71, 0.7); }
70% { box-shadow: 0 0 0 10px rgba(255, 179, 71, 0); }
100% { box-shadow: 0 0 0 0 rgba(255, 179, 71, 0); }
}
@keyframes slideDown {
from { max-height: 0; opacity: 0; }
to { max-height: 1000px; opacity: 1; }
}
.modal, .popup {
animation: fadeIn 0.3s ease-in-out;
}
.progress-bar {
transition: width 0.6s ease-in-out, background 0.3s ease-in-out;
}
/* Progress bar color ranges */
.progress-bar.range-0-100 {
background: linear-gradient(90deg, #2DD4BF, #5EEAD4);
}
.progress-bar.range-100-200 {
background: linear-gradient(90deg, #10B981, #34D399);
}
.progress-bar.range-200-plus {
background: linear-gradient(90deg, #F59E0B, #FBBF24);
}
.ingredient-button:hover .ingredient-image {
transform: scale(1.1);
border-color: #10B981;
}
.tier-badge {
position: relative;
overflow: hidden;
background-color: #FFB347;
animation: pulse 2s infinite;
will-change: box-shadow;
}
.tier-badge::after {
content: '';
position: absolute;
top: -50%;
left: -50%;
width: 200%;
height: 200%;
background: linear-gradient(45deg, transparent 0%, rgba(255, 255, 255, 0.3) 50%, transparent 100%);
animation: shine 3s infinite;
}
/* Custom class for order items */
.custom-class {
text-align: center;
border-radius: 12px;
border-width: 1px;
border-color: #E5E7EB;
padding: 24px !important;
margin-bottom: 16px;
background-color: #FFFFFF;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
transition: transform 0.2s ease-in-out, box-shadow 0.2s ease-in-out;
width: 100%;
max-width: clamp(288px, 100%, 488px);
margin-left: auto;
margin-right: auto;
}
.custom-class:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
}
/* Order image wrapper for positioning text */
.order-image-wrapper {
position: relative;
display: block;
margin-bottom: 16px;
}
/* Order image styles */
.order-image {
max-width: 100%;
max-height: 330px;
width: auto;
height: auto;
aspect-ratio: 4/3;
object-fit: contain;
border-radius: 10px;
border: 3px solid #D1D5DB;
box-sizing: border-box;
box-shadow: 0 6px 12px rgba(0, 0, 0, 0.15);
transition: transform 0.2s ease-in-out, box-shadow 0.2s ease-in-out;
will-change: transform, box-shadow;
display: block;
margin: 0 auto 20px;
}
.order-image:hover {
transform: scale(1.04);
box-shadow: 0 8px 16px rgba(0, 0, 0, 0.2);
}
/* Fallback for placeholder images */
.order-image[src$="placeholder.jpg"] {
background: linear-gradient(135deg, #F9FAFB, #E5E7EB);
object-fit: contain;
}
/* Item name below image */
.order-item-name {
display: block;
text-align: center;
color: #000000;
font-size: 1.5rem;
font-weight: 600;
margin-top: 12px;
padding: 8px 16px;
background: #FFFFFF;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
max-width: 100%;
transition: transform 0.2s ease-in-out, box-shadow 0.2s ease-in-out;
}
.order-image-wrapper:hover .order-item-name {
transform: translateY(-2px);
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
}
.history {
display: flex;
justify-content: center;
font-size: 1.75rem;
font-weight: 700;
padding: 1.5rem;
background: linear-gradient(45deg, #FFF7ED, #FFE4D6);
border-radius: 8px;
margin-bottom: 1rem;
}
/* Scrollable sector images */
.sector-images-container {
position: relative;
display: flex;
flex-wrap: nowrap;
overflow-x: auto;
overflow-y: hidden;
padding: 0 10px;
gap: 20px;
width: 100%;
max-width: none;
scroll-behavior: smooth;
scrollbar-width: thin;
scrollbar-color: #FFB347 #E5E7EB;
}
.sector-images-container::-webkit-scrollbar {
height: 8px;
}
.sector-images-container::-webkit-scrollbar-track {
background: #E5E7EB;
}
.sector-images-container::-webkit-scrollbar-thumb {
background: #FFB347;
border-radius: 4px;
}
.sector-item {
display: flex;
flex-direction: column;
align-items: center;
margin: 0 auto;
flex-shrink: 0;
background: #FFF7ED;
border-radius: 8px;
padding: 8px;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.05);
transition: transform 0.2s ease-in-out, box-shadow 0.2s ease-in-out;
}
.sector-item:hover {
transform: translateY(-2px);
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
}
.sector-image {
max-width: 150px;
max-height: 100px;
width: auto;
height: auto;
aspect-ratio: 3/2;
object-fit: contain;
cursor: pointer;
border-radius: 8px;
border: 1px solid #D1D5DB;
box-sizing: border-box;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
transition: transform 0.15s ease-in-out;
will-change: transform;
}
.sector-image:hover {
transform: scale(1.08);
}
.sector-name {
text-align: center;
font-size: 0.75rem;
font-weight: 600;
margin-top: 6px;
max-width: 150px;
color: #1F2937;
}
/* Scroll buttons */
.scroll-button {
position: absolute;
top: 50%;
transform: translateY(-50%);
background: #FFB347;
color: #FFFFFF;
width: 40px;
height: 40px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 20px;
cursor: pointer;
opacity: 0;
transition: opacity 0.3s ease-in-out, transform 0.2s ease-in-out;
z-index: 10;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.scroll-button:hover {
background: #FF9E2C;
transform: translateY(-50%) scale(1.1);
}
.sector-images-container:hover .scroll-button {
opacity: 1;
}
.scroll-button.left {
left: 0;
}
.scroll-button.right {
right: 0;
}
/* Full-width gradient header */
.back-to-menu {
position: fixed;
top: 0;
left: 0;
right: 0;
display: flex;
align-items: center;
padding: 14px 24px;
background: linear-gradient(45deg, #FFA07A, #FFB347);
color: #FFFFFF;
font-size: 1.125rem;
font-weight: 600;
text-decoration: none;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
z-index: 9999;
transition: background-color 0.3s ease, transform 0.2s ease;
}
.back-to-menu:hover {
background: linear-gradient(45deg, #FF8C61, #FF9E2C);
transform: translateY(-1px);
}
/* Content spacing */
.container {
margin-top: 90px;
max-width: 100%;
padding: 20px;
}
/* Sector popup */
#sector-popup {
display: none;
position: fixed;
inset: 0;
background: rgba(17, 24, 39, 0.6);
justify-content: center;
align-items: center;
z-index: 10000;
animation: fadeIn 0.3s ease-in-out;
}
#popup-content {
background: linear-gradient(135deg, #FFFFFF 0%, #F9FAFB 100%);
padding: 24px;
border-radius: 12px;
width: 90%;
max-width: 550px;
max-height: 85vh;
overflow-y: auto;
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.15);
border: 1px solid #E5E7EB;
position: relative;
contain: layout;
}
.popup-header {
position: relative;
padding-bottom: 12px;
margin-bottom: 16px;
border-bottom: 2px solid #FFB347;
display: flex;
justify-content: space-between;
align-items: center;
}
.popup-close {
background: #FFB347;
color: #FFFFFF;
border-radius: 50%;
width: 40px;
height: 40px;
display: flex;
align-items: center;
justify-content: center;
font-size: 24px;
font-weight: bold;
border: none;
cursor: pointer;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
line-height: 1;
padding: 0;
position: relative;
overflow: hidden;
will-change: transform, background;
}
.popup-close::before {
content: '';
position: absolute;
top: 100%;
left: 0;
width: 100%;
height: 100%;
background: linear-gradient(to top, rgba(255, 255, 255, 0.2) 0%, rgba(255, 255, 255, 0) 100%);
animation: lightMoveUp 1.5s infinite linear;
z-index: 0;
}
.popup-close:hover {
background: #FF9E2C;
transform: scale(1.1);
}
.popup-close span {
position: relative;
z-index: 1;
}
/* Ingredient modal */
.ingredient-modal-container {
display: none;
position: fixed;
inset: 0;
background: rgba(0, 0, 0, 0.6);
justify-content: center;
align-items: center;
z-index: 10000;
animation: fadeIn 0.3s ease-in-out;
}
.ingredient-modal {
background: linear-gradient(135deg, #FFFFFF 0%, #F9FAFB 100%);
border-radius: 12px;
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.15);
border: 1px solid #E5E7EB;
width: 90%;
max-width: 500px;
max-height: 90vh;
overflow-y: auto;
position: relative;
contain: layout;
}
.modal-section {
padding: 16px;
border-left: 4px solid #2DD4BF;
margin-bottom: 16px;
background: #F9FAFB;
border-radius: 6px;
}
/* Ingredient images */
.ingredient-image {
max-width: 100%;
max-height: 100px;
width: 100%;
height: 100px;
aspect-ratio: 1/1;
object-fit: cover;
border-radius: 6px;
border: 1px solid #D1D5DB;
transition: transform 0.3s ease-in-out, border-color 0.3s ease-in-out;
}
/* Hover effect for ingredient buttons */
.ingredient-button:hover .ingredient-image {
transform: scale(1.1);
border-color: #10B981;
}
/* Green border when section is visible */
.ingredients выполнение-section:not(.hidden) .ingredient-image {
border: 2px solid #10B981;
}
/* Ensure ingredient section is smooth */
.ingredients-section {
transition: opacity 0.3s ease-in-out, max-height 0.3s ease-in-out;
max-height: 0;
opacity: 0;
overflow: hidden;
}
.ingredients-section:not(.hidden) {
max-height: 1000px;
opacity: 1;
}
/* Prevent body scroll when modal is open */
body.modal-open {
overflow: hidden;
}
/* Fallback messages */
.no-data {
text-align: center;
color: #6B7280;
font-size: 1.125rem;
padding: 20px;
background: #F9FAFB;
border-radius: 8px;
margin-bottom: 20px;
}
/* Order confirmed section */
.order-confirmed {
background: linear-gradient(135deg, #FFF7ED 0%, #FFE4D6 100%);
position: relative;
overflow: hidden;
}
.order-confirmed::before {
content: '✔';
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
font-size: 120px;
color: rgba(16, 185, 129, 0.1);
z-index: 0;
}
.order-confirmed h1 {
font-size: 2.25rem;
z-index: 1;
position: relative;
}
/* Tier details animation */
.tier-details {
overflow: hidden;
transition: max-height 0.5s ease-in-out, opacity 0.3s ease-in-out;
max-height: 0;
opacity: 0;
}
.tier-details:not(.hidden) {
max-height: 1000px;
opacity: 1;
animation: slideDown 0.5s ease-in-out;
}
/* Price badge */
.price-badge {
display: inline-block;
background: #FFE4D6;
padding: 6px 14px;
border-radius: 14px;
font-weight: 600;
color: #C2410C;
}
/* Show Ingredients button focus style */
.ingredients-toggle-button:focus {
outline: none;
transform: scale(1.05);
}
/* Responsive adjustments */
@media (max-width: 768px) {
.custom-class {
padding: 20px !important;
max-width: 400px;
}
.order-image {
max-height: 270px;
}
.order-item-name {
font-size: 1.25rem;
padding: 6px 12px;
}
.grid {
grid-template-columns: repeat(2, 1fr);
}
}
@media (max-width: 480px) {
.custom-class {
max-width: 336px;
}
.order-image {
max-height: 225px;
}
.order-item-name {
font-size: 1.125rem;
}
}
@media (max-width: 360px) {
.custom-class {
max-width: 296px;
}
.order-image {
max-height: 195px;
}
.order-item-name {
font-size: 1rem;
padding: 4px 10px;
}
}
@media (max-width: 640px) {
.back-to-menu {
padding: 12px 16px;
font-size: 1rem;
}
.container {
margin-top: 70px;
padding: 16px;
}
.history {
font-size: 1.5rem;
padding: 1rem;
}
.sector-image {
max-width: 120px;
max-height: 80px;
}
.sector-name {
max-width: 120px;
}
.sector-item {
padding: 6px;
}
.order-confirmed h1 {
font-size: 1.75rem;
}
.scroll-button {
width: 32px;
height: 32px;
font-size: 16px;
}
#popup-content {
width: 95%;
padding: 16px;
max-height: 80vh;
}
.ingredient-modal {
width: 95%;
max-height: 80vh;
}
.popup-close {
width: 40px;
height: 40px;
font-size: 24px;
}
.popup-close::before {
animation: lightMoveUp 1.2s infinite linear;
}
.ingredient-image {
max-height: 80px;
height: 80px;
}
.grid {
grid-template-columns: repeat(2, 1fr);
}
}
@media (min-width: 768px) {
.grid {
grid-template-columns: repeat(3, 1fr);
}
}
</style>
</head>
<body class="bg-gray-50">
<a href="{{ url_for('menu.menu') }}" class="back-to-menu" aria-label="Go back to menu">
<svg xmlns="http://www.w3.org/2000/svg" class="w-6 h-6 mr-2" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M15 18l-6-6 6-6"></path>
</svg>
Back to Menu
</a>
<div class="container mx-auto p-6 pt-2">
<!-- Order Confirmation -->
<div class="section bg-white shadow-sm rounded-xl p-6 mb-6 order-confirmed" role="alert">
<h1 class="text-center text-2xl font-bold text-orange-500">Order Confirmed!</h1>
<p class="text-center text-gray-600 mt-2">Estimated delivery time: {{ delivery_time }} minutes</p>
</div>
<!-- Reward Status Section -->
<div class="section bg-white rounded-xl shadow-lg p-6 mb-6">
<div class="flex items-center justify-between mb-4 cursor-pointer" onclick="toggleTierDetails()" aria-expanded="false" aria-controls="tierDetails">
<div id="tierBadge" class="w-8 h-8 p-1 rounded-full flex items-center justify-center tier-badge">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="text-white">
<path d="M6 9H4.5a2.5 2.5 0 0 1 0-5H6"></path>
<path d="M18 9h1.5a2.5 2.5 0 0 0 0-5H18"></path>
<path d="M4 22h16"></path>
<path d="M10 14.66V17c0 .55-.47.98-.97 1.21C7.85 18.75 7 20.24 7 22"></path>
<path d="M14 14.66V17c0 .55.47.98.97 1.21C16.15 18.75 17 20.24 17 22"></path>
<path d="M9 9c0 .97.64 1.79 1.5 2.05A2 2 0 0 1 12 13c.82 0 1.54-.39 2-1"></path>
<path d="M18 9H6a4 4 0 0 1 0-8h12a4 4 0 0 1 0 8Z"></path>
</svg>
</div>
<span class="ml-2 text-xl font-bold text-orange-500" id="currentTier">{{ current_tier }} Tier</span>
<svg id="arrowIcon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="text-gray-600">
<path id="arrowPath" d="M19 9l-7 7-7-7"></path>
</svg>
</div>
<!-- Collapsible Content Section -->
<div id="tierDetails" class="tier-details hidden">
<div class="text-center mb-6">
<p class="text-gray-600">Valid through December {{ validity_year }}</p>
<p class="text-lg font-semibold mt-2 text-yellow-600" id="pointsDisplay">{{ user_points }} points</p>
</div>
<!-- Progress Bar -->
<div class="relative h-4 bg-gray-200 rounded-full overflow-hidden mb-4">
<div id="progressBar" class="absolute left-0 top-0 h-full progress-bar" style="width: {{ progress_percentage }}%"></div>
</div>
<div class="flex justify-between text-sm text-gray-600 mb-4">
<span id="startPoint">{{ start_point }}</span>
<span id="endPoint">{{ end_point }}</span>
</div>
<p class="text-center text-gray-700">
You need <span class="font-semibold text-gray-800" id="pointsNeeded">{{ points_needed_for_next_tier }}</span> more
points to reach
<span class="font-semibold text-orange-500" id="nextTier">{{ next_tier }}</span>
</p>
<!-- Tier Benefits -->
<div class="mt-6 bg-gray-50 rounded-xl p-4">
<h3 class="text-lg font-semibold mb-4 text-gray-800" id="benefitsTitle">
Benefits of Reaching {{ next_tier }} Tier
</h3>
<ul class="space-y-3" id="benefitsList">
{% if next_tier == "Silver" %}
<li class="flex items-center">
<span class="text-2xl mr-3">🏷️</span>
<span class="text-gray-700">10% discount on next purchase</span>
</li>
<li class="flex items-center">
<span class="text-2xl mr-3">🍹</span>
<span class="text-gray-700">Free soft drink with your next order</span>
</li>
{% elif next_tier == "Gold" %}
<li class="flex items-center">
<span class="text-2xl mr-3">🏷️</span>
<span class="text-gray-700">15% discount on next purchase</span>
</li>
<li class="flex items-center">
<span class="text-2xl mr-3">🍰</span>
<span class="text-gray-700">Free dessert on next order</span>
</li>
{% elif next_tier == "Platinum" %}
<li class="flex items-center">
<span class="text-2xl mr-3">🏷️</span>
<span class="text-gray-700">20% discount on next purchase</span>
</li>
<li class="flex items-center">
<span class="text-2xl mr-3">🍴</span>
<span class="text-gray-700">Free entrée with your next order</span>
</li>
{% else %}
<li class="text-gray-700">You've reached the highest tier! Enjoy exclusive benefits.</li>
{% endif %}
</ul>
</div>
</div>
</div>
<!-- Ingredient History -->
<div class="history">
<h1 class="text-center text-2xl font-bold text-orange-500">Ingredient History</h1>
</div>
{% if sector_details %}
<div class="sector-images-container" role="region" aria-label="Ingredient History Carousel">
<button class="scroll-button left" onclick="scrollContainer('left')" aria-label="Scroll left">
<svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M15 18l-6-6 6-6"></path>
</svg>
</button>
{% for sector_name, sector in sector_details.items() %}
<div class="sector-item">
<img
src="{{ sector.image_url | default('/static/placeholder.jpg') }}"
alt="{{ sector_name }}"
class="sector-image lazyload"
loading="lazy"
onclick="showSectorDescription('{{ sector.description | escape }}')"
role="img"
aria-describedby="sector-name-{{ loop.index }}"
onerror="this.src='/static/placeholder.jpg'; this.alt='Placeholder image'"
>
<h3 id="sector-name-{{ loop.index }}" class="sector-name">{{ sector_name }}</h3>
</div>
{% endfor %}
<button class="scroll-button right" onclick="scrollContainer('right')" aria-label="Scroll right">
<svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M9 18l6-6-6-6"></path>
</svg>
</button>
</div>
{% else %}
<div class="no-data">No sector details available.</div>
{% endif %}
<!-- Popup for Sector Description -->
<div id="sector-popup" class="popup" style="display:none;" role="dialog" aria-modal="true" aria-labelledby="sector-popup-title">
<div id="popup-content">
<div class="popup-header flex justify-between items-center">
<h3 id="sector-popup-title" class="text-xl font-semibold text-gray-800">Origin History</h3>
<button onclick="closePopup()" class="popup-close" aria-label="Close sector description popup"><span>×</span></button>
</div>
<p id="sector-description" class="text-gray-700 leading-relaxed"></p>
</div>
</div>
<!-- Order Items Section -->
<div class="text-center text-2xl font-bold text-orange-500 mb-6">Previous Orders</div>
{% if order_items %}
{% for item in order_items %}
<div class="custom-class">
<div class="order-image-wrapper">
<img
src="{{ item.image_url | default('/static/placeholder.jpg') }}"
alt="{{ item.name | escape }}"
class="order-image lazyload"
loading="lazy"
decoding="async"
role="img"
aria-label="Image of {{ item.name | escape }}"
aria-describedby="order-price-{{ loop.index }}"
onerror="this.src='/static/placeholder.jpg'; this.alt='Placeholder image'"
/>
<h3 class="order-item-name" id="order-name-{{ loop.index }}">{{ item.name | escape }}</h3>
</div>
<p id="order-price-{{ loop.index }}" class="text-gray-600 mb-2 price-badge">${{ "%.2f"|format(item.price | default(0)) }}</p>
{% if item.category != 'Soft Drink' %}
<button
id="ingredientsToggleButton{{ loop.index }}"
class="ingredients-toggle-button text-green-600 text-sm font-semibold mt-2 ml-2 text-left pb-4 hover:text-green-700"
onclick="toggleIngredients({{ loop.index }})"
aria-expanded="false"
aria-controls="ingredientsSection{{ loop.index }}"
data-index="{{ loop.index }}"
>
Show Ingredients
</button>
<div id="ingredientsSection{{ loop.index }}" class="ingredients-section hidden mt-4" aria-hidden="true">
{% if item.ingredients and item.ingredients|length > 0 %}
<div class="grid grid-cols-2 gap-4 sm:grid-cols-3">
{% for ingredient in item.ingredients %}
<button
class="relative group ingredient-button focus:outline-none focus:ring-2 focus:ring-green-500"
aria-label="View details for {{ ingredient.name | escape }}"
data-ingredient='{
"index": "{{ loop.index }}",
"name": "{{ ingredient.name | escape }}",
"image": "{{ ingredient.image | default('/static/placeholder.jpg') | escape }}",
"health_benefits": "{{ ingredient.health_benefits | escape }}",
"fun_facts": "{{ ingredient.fun_facts | escape }}"
}'
onclick="showIngredientDetails(this)"
>
<div class="overflow-hidden rounded-lg">
<img
src="{{ ingredient.image | default('/static/placeholder.jpg') }}"
alt="{{ ingredient.name | escape }}"
class="ingredient-image lazyload"
loading="lazy"
decoding="async"
onerror="this.src='/static/placeholder.jpg'; this.alt='Placeholder image'"
/>
</div>
<p class="mt-2 text-center text-sm font-medium text-gray-800">{{ ingredient.name | escape }}</p>
</button>
{% endfor %}
</div>
{% else %}
<p class="text-gray-600 text-center">No ingredients available for this item.</p>
{% endif %}
</div>
{% endif %}
</div>
<!-- Modal for Ingredients -->
{% if item.category != 'Soft Drink' %}
<div id="ingredientModal{{ loop.index }}" class="ingredient-modal-container hidden modal" role="dialog" aria-modal="true" aria-labelledby="modalTitle{{ loop.index }}">
<div class="ingredient-modal">
<div class="p-6">
<div class="popup-header flex justify-between items-center mb-4">
<h3 id="modalTitle{{ loop.index }}" class="text-xl font-semibold text-gray-800"></h3>
<button onclick="closeModal({{ loop.index }})" class="popup-close" aria-label="Close ingredient details modal"><span>×</span></button>
</div>
<img id="modalImage{{ loop.index }}" src="" alt="" class="w-full max-h-48 object-contain rounded-xl mb-4 border border-gray-200 lazyload" loading="lazy" decoding="async" />
<div class="space-y-4">
<div class="modal-section">
<h4 class="font-semibold mb-2 text-gray-800 flex items-center">
<svg class="w-5 h-5 mr-2 text-teal-400" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"></path>
</svg>
Health Benefits
</h4>
<ul id="healthBenefits{{ loop.index }}" class="list-disc list-inside text-gray-700"></ul>
</div>
<div class="modal-section">
<h4 class="font-semibold mb-2 text-gray-800 flex items-center">
<svg class="w-5 h-5 mr-2 text-orange-500" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path>
</svg>
Fun Facts
</h4>
<ul id="funFacts{{ loop.index }}" class="list-disc list-inside text-gray-700"></ul>
</div>
</div>
</div>
</div>
</div>
{% endif %}
{% endfor %}
{% else %}
<div class="no-data">No previous orders found.</div>
{% endif %}
</div>
<script>
// Debounce function for performance
function debounce(func, wait) {
let timeout;
return function executedFunction(...args) {
const later = () => {
clearTimeout(timeout);
func(...args);
};
clearTimeout(timeout);
timeout = setTimeout(later, wait);
};
}
// Scroll container function
function scrollContainer(direction) {
try {
const container = document.querySelector('.sector-images-container');
const scrollAmount = 200;
if (direction === 'left') {
container.scrollLeft -= scrollAmount;
} else {
container.scrollLeft += scrollAmount;
}
} catch (e) {
console.error('Error scrolling container:', e);
}
}
// Show sector description popup
function showSectorDescription(description) {
try {
document.getElementById('sector-description').innerText = description || 'No description available.';
document.getElementById('sector-popup').style.display = 'flex';
document.body.classList.add('modal-open');
} catch (e) {
console.error('Error showing sector description:', e);
}
}
// Close sector popup
function closePopup() {
try {
document.getElementById('sector-popup').style.display = 'none';
document.body.classList.remove('modal-open');
} catch (e) {
console.error('Error closing popup:', e);
}
}
// Toggle tier details with animation
function toggleTierDetails() {
try {
const tierDetails = document.getElementById('tierDetails');
const arrowPath = document.getElementById('arrowPath');
const toggleButton = tierDetails.parentElement.querySelector('[aria-controls="tierDetails"]');
const isExpanded = !tierDetails.classList.contains('hidden');
tierDetails.classList.toggle('hidden');
arrowPath.setAttribute('d', tierDetails.classList.contains('hidden') ? 'M19 9l-7 7-7-7' : 'M19 15l-7-7-7 7');
toggleButton.setAttribute('aria-expanded', !isExpanded);
} catch (e) {
console.error('Error toggling tier details:', e);
}
}
// Toggle ingredients section
function toggleIngredients(index) {
try {
const section = document.getElementById(`ingredientsSection${index}`);
const button = document.getElementById(`ingredientsToggleButton${index}`);
if (!section || !button) {
console.error(`Elements not found for index ${index}`);
return;
}
const isHidden = section.classList.contains('hidden');
// Close all other ingredient sections
document.querySelectorAll('.ingredients-section').forEach(s => {
s.classList.add('hidden');
s.setAttribute('aria-hidden', 'true');
});
document.querySelectorAll('[id^="ingredientsToggleButton"]').forEach(b => {
b.textContent = 'Show Ingredients';
b.setAttribute('aria-expanded', 'false');
});
// Toggle the current section
if (isHidden) {
section.classList.remove('hidden');
section.setAttribute('aria-hidden', 'false');
button.textContent = 'Hide Ingredients';
button.setAttribute('aria-expanded', 'true');
} else {
section.classList.add('hidden');
section.setAttribute('aria-hidden', 'true');
button.textContent = 'Show Ingredients';
button.setAttribute('aria-expanded', 'false');
}
} catch (e) {
console.error('Error toggling ingredients:', e);
}
}
// Show ingredient details modal
function showIngredientDetails(button) {
try {
const data = JSON.parse(button.getAttribute('data-ingredient'));
const { index, name, image, health_benefits, fun_facts } = data;
const healthBenefitsList = (health_benefits || '').split(',').filter(item => item.trim());
const funFactsList = (fun_facts || '').split(',').filter(item => item.trim());
const modal = document.getElementById(`ingredientModal${index}`);
if (!modal) {
console.error(`Modal not found for index ${index}`);
return;
}
document.getElementById(`modalTitle${index}`).textContent = name || 'Unknown Ingredient';
const modalImage = document.getElementById(`modalImage${index}`);
modalImage.src = image || '/static/placeholder.jpg';
modalImage.alt = name || 'Ingredient image';
const healthBenefitsUl = document.getElementById(`healthBenefits${index}`);
const funFactsUl = document.getElementById(`funFacts${index}`);
healthBenefitsUl.innerHTML = healthBenefitsList.length
? healthBenefitsList.map(item => `<li>${item.trim()}</li>`).join('')
: '<li>No health benefits available.</li>';
funFactsUl.innerHTML = funFactsList.length
? funFactsList.map(item => `<li>${item.trim()}</li>`).join('')
: '<li>No fun facts available.</li>';
modal.style.display = 'flex';
document.body.classList.add('modal-open');
} catch (e) {
console.error('Error showing ingredient details:', e);
}
}
// Close ingredient modal
function closeModal(index) {
try {
const modal = document.getElementById(`ingredientModal${index}`);
if (modal) {
modal.style.display = 'none';
document.body.classList.remove('modal-open');
}
} catch (e) {
console.error('Error closing modal:', e);
}
}
// Set progress bar color based on points
document.addEventListener('DOMContentLoaded', () => {
try {
const progressBar = document.getElementById('progressBar');
const pointsDisplay = document.getElementById('pointsDisplay');
const points = parseInt(pointsDisplay.textContent, 10) || 0;
if (points <= 100) {
progressBar.classList.add('range-0-100');
} else if (points <= 200) {
progressBar.classList.add('range-100-200');
} else {
progressBar.classList.add('range-200-plus');
}
} catch (e) {
console.error('Error setting progress bar color:', e);
}
// Lazy load images with IntersectionObserver
const images = document.querySelectorAll('img.lazyload');
if ('IntersectionObserver' in window) {
const observer = new IntersectionObserver((entries, observer) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const img = entry.target;
img.src = img.src || '/static/placeholder.jpg';
img.classList.remove('lazyload');
observer.unobserve(img);
}
});
});
images.forEach(img => observer.observe(img));
} else {
images.forEach(img => {
img.src = img.src || '/static/placeholder.jpg';
img.classList.remove('lazyload');
});
}
});
// Close modals on outside click
document.addEventListener('click', (e) => {
try {
if (e.target.id === 'sector-popup') {
closePopup();
}
document.querySelectorAll('.ingredient-modal-container').forEach(modal => {
if (e.target === modal) {
const index = modal.id.replace('ingredientModal', '');
closeModal(index);
}
});
} catch (e) {
console.error('Error handling outside click:', e);
}
});
// Keyboard navigation for scroll buttons
document.querySelectorAll('.scroll-button').forEach(button => {
button.addEventListener('keydown', function(e) {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
scrollContainer(button.classList.contains('left') ? 'left' : 'right');
}
});
});
// Keyboard navigation for toggle buttons
document.querySelectorAll('[id^="ingredientsToggleButton"]').forEach(button => {
button.addEventListener('keydown', (e) => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
const index = button.getAttribute('data-index');
toggleIngredients(index);
}
});
});
</script>
</body>
</html>