Spaces:
Sleeping
Sleeping
<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%); } | |
} | |
.modal, .popup { | |
animation: fadeIn 0.2s ease-in-out; | |
} | |
.progress-bar { | |
transition: width 0.6s ease-in-out; | |
} | |
.ingredient-image { | |
transition: transform 0.3s ease-in-out; | |
} | |
.ingredient-button:hover .ingredient-image { | |
transform: scale(1.1); | |
} | |
.tier-badge { | |
position: relative; | |
overflow: hidden; | |
background-color: #F97316; | |
} | |
.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.2) 50%, transparent 100%); | |
animation: shine 3s infinite; | |
} | |
/* Custom class for order items */ | |
.custom-class { | |
text-align: center; | |
border-radius: 8px; | |
border-width: 1px; | |
border-color: #E5E7EB; | |
padding: 20px ; | |
margin-bottom: 12px; | |
background-color: #FFFFFF; | |
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05); | |
} | |
.history { | |
display: flex; | |
justify-content: center; | |
font-size: 1.75rem; | |
font-weight: 700; | |
padding: 1.5rem; | |
} | |
/* Scrollable sector images */ | |
.sector-images-container { | |
display: flex; | |
overflow-x: auto; | |
padding: 10px 0; | |
gap: 15px; | |
} | |
.sector-image { | |
max-width: 150px; | |
height: 100px; | |
object-fit: cover; | |
cursor: pointer; | |
border-radius: 6px; | |
} | |
.sector-name { | |
text-align: center; | |
font-size: 0.75rem; | |
font-weight: 600; | |
margin-top: 6px; | |
max-width: 100px; | |
color: #1F2937; | |
} | |
/* Full-width orange header */ | |
.back-to-menu { | |
position: fixed; | |
top: 0; | |
left: 0; | |
right: 0; | |
display: flex; | |
align-items: center; | |
padding: 14px 24px; | |
background-color: #F97316; | |
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-color: #EA580C; | |
transform: translateY(-1px); | |
} | |
/* Content spacing */ | |
.container { | |
margin-top: 90px; | |
} | |
/* Sector popup */ | |
#sector-popup { | |
display: none; | |
position: fixed; | |
inset: 0; | |
background: rgba(17, 24, 39, 0.6); | |
display: flex; | |
justify-content: center; | |
align-items: center; | |
z-index: 10000; | |
animation: fadeIn 0.2s 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 #F97316; | |
display: flex; | |
justify-content: space-between; | |
align-items: center; | |
} | |
.popup-close { | |
background: #F97316; | |
color: #FFFFFF; | |
border-radius: 50%; | |
width: 36px; /* Increased for better usability */ | |
height: 36px; | |
display: flex; | |
align-items: center; | |
justify-content: center; | |
font-size: 22px; /* Adjusted to fit new size */ | |
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: #EA580C; | |
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); | |
display: flex; | |
justify-content: center; | |
align-items: center; | |
z-index: 10000; | |
animation: fadeIn 0.2s 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; | |
} | |
/* Green border for ingredient images when section is visible */ | |
.ingredients-section:not(.hidden) .ingredient-image { | |
border: 2px solid #10B981; | |
border-radius: 6px; | |
} | |
/* Prevent body scroll when modal is open */ | |
body.modal-open { | |
overflow: hidden; | |
} | |
/* Responsive adjustments */ | |
@media (max-width: 640px) { | |
.back-to-menu { | |
padding: 12px 16px; | |
font-size: 1rem; | |
} | |
.container { | |
margin-top: 70px; | |
} | |
.history { | |
font-size: 1.5rem; | |
padding: 1rem; | |
} | |
.custom-class { | |
padding: 16px ; | |
} | |
.sector-image { | |
max-width: 120px; | |
height: 80px; | |
} | |
#popup-content { | |
width: 95%; | |
padding: 16px; | |
max-height: 80vh; | |
} | |
.ingredient-modal { | |
width: 95%; | |
max-height: 80vh; | |
} | |
.popup-close { | |
width: 40px; /* Increased for touch target (WCAG) */ | |
height: 40px; | |
font-size: 24px; /* Adjusted to fit new size */ | |
} | |
.popup-close::before { | |
animation: lightMoveUp 1.2s infinite linear; | |
} | |
} | |
</style> | |
</head> | |
<body class="bg-gray-50"> | |
<a href="/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"> | |
<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: 35 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()"> | |
<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 2024</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 bg-teal-400" | |
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> | |
{% 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> | |
<div class="sector-images-container"> | |
{% for sector_name, sector in sector_details.items() %} | |
<div class="sector-item"> | |
<img src="{{ sector.image_url }}" alt="{{ sector_name }}" class="sector-image lazyload" loading="lazy" | |
onclick="showSectorDescription('{{ sector.description }}')"> | |
<h3 class="sector-name">{{ sector_name }}</h3> | |
</div> | |
{% endfor %} | |
</div> | |
<!-- Popup for Sector Description --> | |
<div id="sector-popup" class="popup" style="display:none;" role="dialog" aria-modal="true"> | |
<div id="popup-content"> | |
<div class="popup-header flex justify-between items-center"> | |
<h3 class="text-xl font-semibold text-gray-800">Origin History</h3> | |
<button onclick="closePopup()" class="popup-close"><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> | |
{% for item in order_items %} | |
<div class="custom-class"> | |
<img src="{{ item.image_url }}" alt="{{ item.name }}" class="w-full h-48 object-cover rounded-xl mb-4 lazyload" loading="lazy" /> | |
<h3 class="text-lg font-semibold text-gray-800">{{ item.name }}</h3> | |
<p class="text-gray-600 mb-2">${{ "%.2f"|format(item.price) }}</p> | |
{% if item.ingredients %} | |
<button id="ingredientsToggleButton{{ loop.index }}" class="text-green-600 text-sm font-semibold mt-2 ml-2 text-left pb-4 hover:text-green-700" onclick="toggleIngredients({{ loop.index }})"> | |
Show Ingredients | |
</button> | |
<div id="ingredientsSection{{ loop.index }}" class="ingredients-section hidden mt-4"> | |
<div class="grid grid-cols-2 gap-4"> | |
{% for ingredient in item.ingredients %} | |
<button | |
onclick="showIngredientDetails( | |
'{{ loop.index }}', | |
'{{ ingredient.name }}', | |
'{{ ingredient.image }}', | |
'{{ ingredient.health_benefits }}', | |
'{{ ingredient.fun_facts }}' | |
)" | |
class="relative group ingredient-button" | |
> | |
<div class="overflow-hidden rounded-lg"> | |
<img | |
src="{{ ingredient.image }}" | |
alt="{{ ingredient.name }}" | |
class="w-full h-32 object-cover ingredient-image lazyload" | |
loading="lazy" | |
/> | |
</div> | |
<p class="mt-2 text-center text-sm font-medium text-gray-800">{{ ingredient.name }}</p> | |
</button> | |
{% endfor %} | |
</div> | |
</div> | |
{% endif %} | |
</div> | |
<!-- Modal for Ingredients --> | |
<div id="ingredientModal{{ loop.index }}" class="ingredient-modal-container hidden modal" role="dialog" aria-modal="true"> | |
<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"><span>×</span></button> | |
</div> | |
<img id="modalImage{{ loop.index }}" src="" alt="" class="w-full h-48 object-cover rounded-xl mb-4 border border-gray-200 lazyload" loading="lazy" /> | |
<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> | |
{% endfor %} | |
</div> | |
<script> | |
function debounce(func, wait) { | |
let timeout; | |
return function executedFunction(...args) { | |
const later = () => { | |
clearTimeout(timeout); | |
func(...args); | |
}; | |
clearTimeout(timeout); | |
timeout = setTimeout(later, wait); | |
}; | |
} | |
function showSectorDescription(description) { | |
document.getElementById('sector-description').innerText = description; | |
document.getElementById('sector-popup').style.display = 'flex'; | |
document.body.classList.add('modal-open'); | |
} | |
function closePopup() { | |
document.getElementById('sector-popup').style.display = 'none'; | |
document.body.classList.remove('modal-open'); | |
} | |
function toggleTierDetails() { | |
const tierDetails = document.getElementById('tierDetails'); | |
const arrowPath = document.getElementById('arrowPath'); | |
tierDetails.classList.toggle('hidden'); | |
arrowPath.setAttribute('d', tierDetails.classList.contains('hidden') ? 'M19 9l-7 7-7-7' : 'M19 15l-7-7-7 7'); | |
} | |
const ingredients = {{ order_items|tojson }}; | |
function toggleIngredients(index) { | |
const currentSection = document.getElementById('ingredientsSection' + index); | |
const currentButton = document.getElementById('ingredientsToggleButton' + index); | |
const isCurrentlyVisible = !currentSection.classList.contains('hidden'); | |
document.querySelectorAll('[id^="ingredientsSection"]').forEach(section => section.classList.add('hidden')); | |
document.querySelectorAll('[id^="ingredientsToggleButton"]').forEach(button => button.textContent = 'Show Ingredients'); | |
if (!isCurrentlyVisible) { | |
currentSection.classList.remove('hidden'); | |
currentButton.textContent = 'Hide Ingredients'; | |
} | |
} | |
function showIngredientDetails(index, name, image, healthBenefits, funFacts) { | |
const healthBenefitsList = healthBenefits.split(','); | |
const funFactsList = funFacts.split(','); | |
document.getElementById("modalTitle" + index).textContent = name; | |
document.getElementById("modalImage" + index).src = image; | |
const healthBenefitsUl = document.getElementById("healthBenefits" + index); | |
const funFactsUl = document.getElementById("funFacts" + index); | |
healthBenefitsUl.innerHTML = ''; | |
funFactsUl.innerHTML = ''; | |
healthBenefitsList.forEach(item => { | |
const li = document.createElement("li"); | |
li.textContent = item.trim(); | |
healthBenefitsUl.appendChild(li); | |
}); | |
funFactsList.forEach(item => { | |
const li = document.createElement("li"); | |
li.textContent = item.trim(); | |
funFactsUl.appendChild(li); | |
}); | |
document.getElementById("ingredientModal" + index).style.display = "flex"; | |
document.body.classList.add('modal-open'); | |
} | |
function closeModal(index) { | |
document.getElementById("ingredientModal" + index).style.display = "none"; | |
document.body.classList.remove('modal-open'); | |
} | |
document.addEventListener('click', function(e) { | |
if (e.target.closest('#sector-popup') && e.target === document.getElementById('sector-popup')) { | |
closePopup(); | |
} | |
document.querySelectorAll('.ingredient-modal-container').forEach(modal => { | |
if (e.target.closest(modal) && e.target === modal) { | |
const index = modal.id.replace('ingredientModal', ''); | |
closeModal(index); | |
} | |
}); | |
}); | |
</script> | |
</body> | |
</html> |