lokesh341 commited on
Commit
d258a99
·
verified ·
1 Parent(s): 168f51b

Rename template/combined_summary.html to templates/combined_summary.html

Browse files
template/combined_summary.html DELETED
@@ -1,166 +0,0 @@
1
- from flask import Blueprint, render_template, session, redirect, url_for
2
- from salesforce import get_salesforce_connection
3
- from flask import Blueprint, render_template, request, session, jsonify, redirect, url_for
4
- import os
5
- import re
6
- from salesforce import get_salesforce_connection
7
-
8
-
9
- combined_summary_blueprint = Blueprint('combined_summary', __name__)
10
-
11
- # Initialize Salesforce connection
12
- sf = get_salesforce_connection()
13
-
14
- def escape_soql(value):
15
- """Escape single quotes in SOQL query values to prevent injection."""
16
- if value:
17
- return value.replace("'", "\\'")
18
- return value
19
-
20
- @combined_summary_blueprint.route('/combined_summary')
21
- def combined_summary():
22
- email = session.get('user_email')
23
- if not email:
24
- print("No user email in session, redirecting to login")
25
- return redirect(url_for('login'))
26
-
27
- try:
28
- # Sanitize email for SOQL query
29
- safe_email = escape_soql(email)
30
-
31
- # ====== FETCH REWARDS ======
32
- reward_query = f"SELECT Id, Reward_Points__c FROM Customer_Login__c WHERE Email__c = '{safe_email}'"
33
- reward_data = sf.query_all(reward_query)
34
- if not reward_data.get("records"):
35
- print(f"No reward info found for email: {email}")
36
- return "Reward info not found", 404
37
-
38
- user_points = reward_data["records"][0].get("Reward_Points__c", 0)
39
-
40
- # Determine tier
41
- tiers = {
42
- "Bronze": 100,
43
- "Silver": 200,
44
- "Gold": 300,
45
- "Platinum": 500
46
- }
47
- current_tier, next_tier = "Bronze", "Silver"
48
- start_point, end_point = 0, 100
49
- if user_points >= 100 and user_points < 200:
50
- current_tier, next_tier = "Silver", "Gold"
51
- start_point, end_point = 100, 200
52
- elif user_points >= 200 and user_points < 300:
53
- current_tier, next_tier = "Gold", "Platinum"
54
- start_point, end_point = 200, 300
55
- elif user_points >= 300:
56
- current_tier, next_tier = "Platinum", "N/A"
57
- start_point, end_point = 300, 500
58
-
59
- progress_percentage = ((user_points - start_point) / (end_point - start_point)) * 100 if end_point != start_point else 100
60
- points_needed_for_next_tier = max(0, end_point - user_points)
61
-
62
- # ====== FETCH ORDER SUMMARY ======
63
- order_query = f"""
64
- SELECT Id, Customer_Name__c, Customer_Email__c, Total_Amount__c, Order_Details__c,
65
- Order_Status__c, Discount__c, Total_Bill__c
66
- FROM Order__c
67
- WHERE Customer_Email__c = '{safe_email}'
68
- ORDER BY CreatedDate DESC
69
- LIMIT 1
70
- """
71
- order_result = sf.query_all(order_query)
72
- if not order_result.get("records"):
73
- print(f"No order found for email: {email}")
74
- return "No order found", 404
75
-
76
- order = order_result["records"][0]
77
- order_details = order.get("Order_Details__c", "")
78
- order_items = []
79
- sector_names = set() # Use a set to ensure sector names are unique
80
-
81
- for line in order_details.split('\n'):
82
- item_parts = line.split('|')
83
- if len(item_parts) >= 5:
84
- item_name_raw = item_parts[0].strip()
85
- item_name = ' '.join(item_name_raw.split(' ')[:-1]).strip()
86
- safe_item_name = escape_soql(item_name)
87
-
88
- menu_query = f"""
89
- SELECT Name, Price__c, Image1__c,
90
- Ingredient_1__r.Ingredient_Name__c, Ingredient_1__r.Ingredient_Image__c,
91
- Ingredient_1__r.Health_Benefits__c, Ingredient_1__r.Fun_Facts__c,
92
- Ingredient_2__r.Ingredient_Name__c, Ingredient_2__r.Ingredient_Image__c,
93
- Ingredient_2__r.Health_Benefits__c, Ingredient_2__r.Fun_Facts__c,
94
- Sector__c
95
- FROM Menu_Item__c
96
- WHERE Name = '{safe_item_name}'
97
- """
98
- menu_result = sf.query_all(menu_query)
99
- ingredients = []
100
-
101
- if menu_result.get("records"):
102
- menu_item = menu_result["records"][0]
103
-
104
- # Process Ingredient 1 if it exists
105
- if menu_item.get('Ingredient_1__r') is not None:
106
- ingredients.append({
107
- "name": menu_item['Ingredient_1__r'].get('Ingredient_Name__c', ''),
108
- "image": menu_item['Ingredient_1__r'].get('Ingredient_Image__c', ''),
109
- "health_benefits": menu_item['Ingredient_1__r'].get('Health_Benefits__c', ''),
110
- "fun_facts": menu_item['Ingredient_1__r'].get('Fun_Facts__c', '')
111
- })
112
-
113
- # Process Ingredient 2 if it exists
114
- if menu_item.get('Ingredient_2__r') is not None:
115
- ingredients.append({
116
- "name": menu_item['Ingredient_2__r'].get('Ingredient_Name__c', ''),
117
- "image": menu_item['Ingredient_2__r'].get('Ingredient_Image__c', ''),
118
- "health_benefits": menu_item['Ingredient_2__r'].get('Health_Benefits__c', ''),
119
- "fun_facts": menu_item['Ingredient_2__r'].get('Fun_Facts__c', '')
120
- })
121
-
122
- # Process the Sector__c field from Menu_Item__c
123
- if menu_item.get('Sector__c'):
124
- sector_names.update(menu_item['Sector__c'].split(',')) # Add sectors to the set
125
-
126
- # Only add the item if ingredients are present
127
- order_items.append({
128
- "name": item_name,
129
- "price": menu_item.get("Price__c", 0),
130
- "image_url": menu_item.get("Image1__c", ''),
131
- "ingredients": ingredients
132
- })
133
-
134
- # Fetch the sector details from the Sector_Detail__c object
135
- sector_details = {}
136
- for sector_name in sector_names:
137
- safe_sector_name = escape_soql(sector_name.strip())
138
- sector_query = f"""
139
- SELECT Name, Image_URL__c, Description__c
140
- FROM Sector_Detail__c
141
- WHERE Name = '{safe_sector_name}'
142
- """
143
- sector_result = sf.query_all(sector_query)
144
- if sector_result.get("records"):
145
- sector_record = sector_result["records"][0]
146
- sector_details[sector_name] = {
147
- "image_url": sector_record.get('Image_URL__c', ''),
148
- "description": sector_record.get('Description__c', '')
149
- }
150
-
151
- return render_template(
152
- 'combined_summary.html',
153
- user_points=round(user_points),
154
- current_tier=current_tier,
155
- next_tier=next_tier,
156
- start_point=start_point,
157
- end_point=end_point,
158
- progress_percentage=round(progress_percentage),
159
- points_needed_for_next_tier=round(points_needed_for_next_tier),
160
- order_items=order_items,
161
- sector_details=sector_details
162
- )
163
-
164
- except Exception as e:
165
- print(f"Error in combined_summary: {str(e)}")
166
- return f"Error: {str(e)}", 500
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
templates/combined_summary.html ADDED
@@ -0,0 +1,606 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <meta http-equiv="x-ua-compatible" content="ie=edge">
7
+ <title>Order Summary</title>
8
+ <script src="https://cdn.tailwindcss.com"></script>
9
+ <style>
10
+ /* Custom animations */
11
+ @keyframes fadeIn {
12
+ from { opacity: 0; transform: translateY(10px); }
13
+ to { opacity: 1; transform: translateY(0); }
14
+ }
15
+ @keyframes lightMoveUp {
16
+ 0% { background-position: 0 100%; }
17
+ 100% { background-position: 0 0%; }
18
+ }
19
+ @keyframes shine {
20
+ from { transform: rotate(0deg) translateX(-100%); }
21
+ to { transform: rotate(25deg) translateX(100%); }
22
+ }
23
+ .modal, .popup {
24
+ animation: fadeIn 0.2s ease-in-out;
25
+ }
26
+ .progress-bar {
27
+ transition: width 0.6s ease-in-out;
28
+ }
29
+ .ingredient-image {
30
+ transition: transform 0.3s ease-in-out;
31
+ }
32
+ .ingredient-button:hover .ingredient-image {
33
+ transform: scale(1.1);
34
+ }
35
+ .tier-badge {
36
+ position: relative;
37
+ overflow: hidden;
38
+ background-color: #FFB347;
39
+ }
40
+ .tier-badge::after {
41
+ content: '';
42
+ position: absolute;
43
+ top: -50%;
44
+ left: -50%;
45
+ width: 200%;
46
+ height: 200%;
47
+ background: linear-gradient(45deg, transparent 0%, rgba(255, 255, 255, 0.2) 50%, transparent 100%);
48
+ animation: shine 3s infinite;
49
+ }
50
+ /* Custom class for order items */
51
+ .custom-class {
52
+ text-align: center;
53
+ border-radius: 8px;
54
+ border-width: 1px;
55
+ border-color: #E5E7EB;
56
+ padding: 20px !important;
57
+ margin-bottom: 12px;
58
+ background-color: #FFFFFF;
59
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
60
+ }
61
+ .history {
62
+ display: flex;
63
+ justify-content: center;
64
+ font-size: 1.75rem;
65
+ font-weight: 700;
66
+ padding: 1.5rem;
67
+ }
68
+ /* Scrollable sector images */
69
+ .sector-images-container {
70
+ display: flex;
71
+ overflow-x: auto;
72
+ padding: 10px 0;
73
+ gap: 15px;
74
+ }
75
+ .sector-image {
76
+ max-width: 150px;
77
+ height: 100px;
78
+ object-fit: cover;
79
+ cursor: pointer;
80
+ border-radius: 6px;
81
+ }
82
+ .sector-name {
83
+ text-align: center;
84
+ font-size: 0.75rem;
85
+ font-weight: 600;
86
+ margin-top: 6px;
87
+ max-width: 100px;
88
+ color: #1F2937;
89
+ }
90
+ /* Full-width gradient header */
91
+ .back-to-menu {
92
+ position: fixed;
93
+ top: 0;
94
+ left: 0;
95
+ right: 0;
96
+ display: flex;
97
+ align-items: center;
98
+ padding: 14px 24px;
99
+ background: linear-gradient(45deg, #FFA07A, #FFB347);
100
+ color: #FFFFFF;
101
+ font-size: 1.125rem;
102
+ font-weight: 600;
103
+ text-decoration: none;
104
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
105
+ z-index: 9999;
106
+ transition: background-color 0.3s ease, transform 0.2s ease;
107
+ }
108
+ .back-to-menu:hover {
109
+ background: linear-gradient(45deg, #FF8C61, #FF9E2C);
110
+ transform: translateY(-1px);
111
+ }
112
+ /* Content spacing */
113
+ .container {
114
+ margin-top: 90px;
115
+ }
116
+ /* Sector popup */
117
+ #sector-popup {
118
+ display: none;
119
+ position: fixed;
120
+ inset: 0;
121
+ background: rgba(17, 24, 39, 0.6);
122
+ justify-content: center;
123
+ align-items: center;
124
+ z-index: 10000;
125
+ animation: fadeIn 0.2s ease-in-out;
126
+ }
127
+ #popup-content {
128
+ background: linear-gradient(135deg, #FFFFFF 0%, #F9FAFB 100%);
129
+ padding: 24px;
130
+ border-radius: 12px;
131
+ width: 90%;
132
+ max-width: 550px;
133
+ max-height: 85vh;
134
+ overflow-y: auto;
135
+ box-shadow: 0 8px 24px rgba(0, 0, 0, 0.15);
136
+ border: 1px solid #E5E7EB;
137
+ position: relative;
138
+ contain: layout;
139
+ }
140
+ .popup-header {
141
+ position: relative;
142
+ padding-bottom: 12px;
143
+ margin-bottom: 16px;
144
+ border-bottom: 2px solid #FFB347;
145
+ display: flex;
146
+ justify-content: space-between;
147
+ align-items: center;
148
+ }
149
+ .popup-close {
150
+ background: #FFB347;
151
+ color: #FFFFFF;
152
+ border-radius: 50%;
153
+ width: 40px;
154
+ height: 40px;
155
+ display: flex;
156
+ align-items: center;
157
+ justify-content: center;
158
+ font-size: 24px;
159
+ font-weight: bold;
160
+ border: none;
161
+ cursor: pointer;
162
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
163
+ line-height: 1;
164
+ padding: 0;
165
+ position: relative;
166
+ overflow: hidden;
167
+ will-change: transform, background;
168
+ }
169
+ .popup-close::before {
170
+ content: '';
171
+ position: absolute;
172
+ top: 100%;
173
+ left: 0;
174
+ width: 100%;
175
+ height: 100%;
176
+ background: linear-gradient(to top, rgba(255, 255, 255, 0.2) 0%, rgba(255, 255, 255, 0) 100%);
177
+ animation: lightMoveUp 1.5s infinite linear;
178
+ z-index: 0;
179
+ }
180
+ .popup-close:hover {
181
+ background: #FF9E2C;
182
+ transform: scale(1.1);
183
+ }
184
+ .popup-close span {
185
+ position: relative;
186
+ z-index: 1;
187
+ }
188
+ /* Ingredient modal */
189
+ .ingredient-modal-container {
190
+ display: none;
191
+ position: fixed;
192
+ inset: 0;
193
+ background: rgba(0, 0, 0, 0.6);
194
+ justify-content: center;
195
+ align-items: center;
196
+ z-index: 10000;
197
+ animation: fadeIn 0.2s ease-in-out;
198
+ }
199
+ .ingredient-modal {
200
+ background: linear-gradient(135deg, #FFFFFF 0%, #F9FAFB 100%);
201
+ border-radius: 12px;
202
+ box-shadow: 0 8px 24px rgba(0, 0, 0, 0.15);
203
+ border: 1px solid #E5E7EB;
204
+ width: 90%;
205
+ max-width: 500px;
206
+ max-height: 90vh;
207
+ overflow-y: auto;
208
+ position: relative;
209
+ contain: layout;
210
+ }
211
+ .modal-section {
212
+ padding: 16px;
213
+ border-left: 4px solid #2DD4BF;
214
+ margin-bottom: 16px;
215
+ background: #F9FAFB;
216
+ border-radius: 6px;
217
+ }
218
+ /* Green border for ingredient images when section is visible */
219
+ .ingredients-section:not(.hidden) .ingredient-image {
220
+ border: 2px solid #10B981;
221
+ border-radius: 6px;
222
+ }
223
+ /* Prevent body scroll when modal is open */
224
+ body.modal-open {
225
+ overflow: hidden;
226
+ }
227
+ /* Fallback messages */
228
+ .no-data {
229
+ text-align: center;
230
+ color: #6B7280;
231
+ font-size: 1.125rem;
232
+ padding: 20px;
233
+ background: #F9FAFB;
234
+ border-radius: 8px;
235
+ margin-bottom: 20px;
236
+ }
237
+ /* Responsive adjustments */
238
+ @media (max-width: 640px) {
239
+ .back-to-menu {
240
+ padding: 12px 16px;
241
+ font-size: 1rem;
242
+ }
243
+ .container {
244
+ margin-top: 70px;
245
+ }
246
+ .history {
247
+ font-size: 1.5rem;
248
+ padding: 1rem;
249
+ }
250
+ .custom-class {
251
+ padding: 16px !important;
252
+ }
253
+ .sector-image {
254
+ max-width: 120px;
255
+ height: 80px;
256
+ }
257
+ #popup-content {
258
+ width: 95%;
259
+ padding: 16px;
260
+ max-height: 80vh;
261
+ }
262
+ .ingredient-modal {
263
+ width: 95%;
264
+ max-height: 80vh;
265
+ }
266
+ .popup-close {
267
+ width: 40px;
268
+ height: 40px;
269
+ font-size: 24px;
270
+ }
271
+ .popup-close::before {
272
+ animation: lightMoveUp 1.2s infinite linear;
273
+ }
274
+ }
275
+ </style>
276
+ </head>
277
+ <body class="bg-gray-50">
278
+ <a href="{{ url_for('menu.menu') }}" class="back-to-menu" aria-label="Go back to menu">
279
+ <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">
280
+ <path d="M15 18l-6-6 6-6"></path>
281
+ </svg>
282
+ Back to Menu
283
+ </a>
284
+
285
+ <div class="container mx-auto p-6 pt-2">
286
+ <!-- Order Confirmation -->
287
+ <div class="section bg-white shadow-sm rounded-xl p-6 mb-6">
288
+ <h1 class="text-center text-2xl font-bold text-orange-500">Order Confirmed!</h1>
289
+ <p class="text-center text-gray-600 mt-2">Estimated delivery time: {{ delivery_time }} minutes</p>
290
+ </div>
291
+
292
+ <!-- Reward Status Section -->
293
+ <div class="section bg-white rounded-xl shadow-lg p-6 mb-6">
294
+ <div class="flex items-center justify-between mb-4 cursor-pointer" onclick="toggleTierDetails()" aria-expanded="false" aria-controls="tierDetails">
295
+ <div id="tierBadge" class="w-8 h-8 p-1 rounded-full flex items-center justify-center tier-badge">
296
+ <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">
297
+ <path d="M6 9H4.5a2.5 2.5 0 0 1 0-5H6"></path>
298
+ <path d="M18 9h1.5a2.5 2.5 0 0 0 0-5H18"></path>
299
+ <path d="M4 22h16"></path>
300
+ <path d="M10 14.66V17c0 .55-.47.98-.97 1.21C7.85 18.75 7 20.24 7 22"></path>
301
+ <path d="M14 14.66V17c0 .55.47.98.97 1.21C16.15 18.75 17 20.24 17 22"></path>
302
+ <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>
303
+ <path d="M18 9H6a4 4 0 0 1 0-8h12a4 4 0 0 1 0 8Z"></path>
304
+ </svg>
305
+ </div>
306
+ <span class="ml-2 text-xl font-bold text-orange-500" id="currentTier">{{ current_tier }} Tier</span>
307
+ <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">
308
+ <path id="arrowPath" d="M19 9l-7 7-7-7"></path>
309
+ </svg>
310
+ </div>
311
+
312
+ <!-- Collapsible Content Section -->
313
+ <div id="tierDetails" class="tier-details hidden">
314
+ <div class="text-center mb-6">
315
+ <p class="text-gray-600">Valid through December {{ validity_year }}</p>
316
+ <p class="text-lg font-semibold mt-2 text-yellow-600" id="pointsDisplay">{{ user_points }} points</p>
317
+ </div>
318
+
319
+ <!-- Progress Bar -->
320
+ <div class="relative h-4 bg-gray-200 rounded-full overflow-hidden mb-4">
321
+ <div id="progressBar" class="absolute left-0 top-0 h-full progress-bar bg-teal-400" style="width: {{ progress_percentage }}%"></div>
322
+ </div>
323
+
324
+ <div class="flex justify-between text-sm text-gray-600 mb-4">
325
+ <span id="startPoint">{{ start_point }}</span>
326
+ <span id="endPoint">{{ end_point }}</span>
327
+ </div>
328
+
329
+ <p class="text-center text-gray-700">
330
+ You need <span class="font-semibold text-gray-800" id="pointsNeeded">{{ points_needed_for_next_tier }}</span> more
331
+ points to reach
332
+ <span class="font-semibold text-orange-500" id="nextTier">{{ next_tier }}</span>
333
+ </p>
334
+
335
+ <!-- Tier Benefits -->
336
+ <div class="mt-6 bg-gray-50 rounded-xl p-4">
337
+ <h3 class="text-lg font-semibold mb-4 text-gray-800" id="benefitsTitle">
338
+ Benefits of Reaching {{ next_tier }} Tier
339
+ </h3>
340
+ <ul class="space-y-3" id="benefitsList">
341
+ {% if next_tier == "Silver" %}
342
+ <li class="flex items-center">
343
+ <span class="text-2xl mr-3">🏷️</span>
344
+ <span class="text-gray-700">10% discount on next purchase</span>
345
+ </li>
346
+ <li class="flex items-center">
347
+ <span class="text-2xl mr-3">🍹</span>
348
+ <span class="text-gray-700">Free soft drink with your next order</span>
349
+ </li>
350
+ {% elif next_tier == "Gold" %}
351
+ <li class="flex items-center">
352
+ <span class="text-2xl mr-3">🏷️</span>
353
+ <span class="text-gray-700">15% discount on next purchase</span>
354
+ </li>
355
+ <li class="flex items-center">
356
+ <span class="text-2xl mr-3">🍰</span>
357
+ <span class="text-gray-700">Free dessert on next order</span>
358
+ </li>
359
+ {% elif next_tier == "Platinum" %}
360
+ <li class="flex items-center">
361
+ <span class="text-2xl mr-3">🏷️</span>
362
+ <span class="text-gray-700">20% discount on next purchase</span>
363
+ </li>
364
+ <li class="flex items-center">
365
+ <span class="text-2xl mr-3">🍴</span>
366
+ <span class="text-gray-700">Free entrée with your next order</span>
367
+ </li>
368
+ {% else %}
369
+ <li class="text-gray-700">You've reached the highest tier! Enjoy exclusive benefits.</li>
370
+ {% endif %}
371
+ </ul>
372
+ </div>
373
+ </div>
374
+ </div>
375
+
376
+ <!-- Ingredient History -->
377
+ <div class="history">
378
+ <h1 class="text-center text-2xl font-bold text-orange-500">Ingredient History</h1>
379
+ </div>
380
+ {% if sector_details %}
381
+ <div class="sector-images-container">
382
+ {% for sector_name, sector in sector_details.items() %}
383
+ <div class="sector-item">
384
+ <img src="{{ sector.image_url | default('/static/placeholder.jpg') }}" alt="{{ sector_name }}" class="sector-image lazyload" loading="lazy" onclick="showSectorDescription('{{ sector.description | escape }}')">
385
+ <h3 class="sector-name">{{ sector_name }}</h3>
386
+ </div>
387
+ {% endfor %}
388
+ </div>
389
+ {% else %}
390
+ <div class="no-data">No sector details available.</div>
391
+ {% endif %}
392
+
393
+ <!-- Popup for Sector Description -->
394
+ <div id="sector-popup" class="popup" style="display:none;" role="dialog" aria-modal="true" aria-labelledby="sector-popup-title">
395
+ <div id="popup-content">
396
+ <div class="popup-header flex justify-between items-center">
397
+ <h3 id="sector-popup-title" class="text-xl font-semibold text-gray-800">Origin History</h3>
398
+ <button onclick="closePopup()" class="popup-close" aria-label="Close sector description popup"><span>×</span></button>
399
+ </div>
400
+ <p id="sector-description" class="text-gray-700 leading-relaxed"></p>
401
+ </div>
402
+ </div>
403
+
404
+ <!-- Order Items Section -->
405
+ <div class="text-center text-2xl font-bold text-orange-500 mb-6">Previous Orders</div>
406
+ {% if order_items %}
407
+ {% for item in order_items %}
408
+ <div class="custom-class">
409
+ <img src="{{ item.image_url | default('/static/placeholder.jpg') }}" alt="{{ item.name | escape }}" class="w-full h-48 object-cover rounded-xl mb-4 lazyload" loading="lazy" />
410
+ <h3 class="text-lg font-semibold text-gray-800">{{ item.name | escape }}</h3>
411
+ <p class="text-gray-600 mb-2">${{ "%.2f"|format(item.price | default(0)) }}</p>
412
+
413
+ {% if item.ingredients %}
414
+ <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 }})" aria-expanded="false" aria-controls="ingredientsSection{{ loop.index }}">
415
+ Show Ingredients
416
+ </button>
417
+
418
+ <div id="ingredientsSection{{ loop.index }}" class="ingredients-section hidden mt-4">
419
+ <div class="grid grid-cols-2 gap-4">
420
+ {% for ingredient in item.ingredients %}
421
+ <button
422
+ onclick="showIngredientDetails(
423
+ '{{ loop.index }}',
424
+ '{{ ingredient.name | escape }}',
425
+ '{{ ingredient.image | default('/static/placeholder.jpg') | escape }}',
426
+ '{{ ingredient.health_benefits | escape }}',
427
+ '{{ ingredient.fun_facts | escape }}'
428
+ )"
429
+ class="relative group ingredient-button"
430
+ aria-label="View details for {{ ingredient.name }}"
431
+ >
432
+ <div class="overflow-hidden rounded-lg">
433
+ <img
434
+ src="{{ ingredient.image | default('/static/placeholder.jpg') }}"
435
+ alt="{{ ingredient.name | escape }}"
436
+ class="w-full h-32 object-cover ingredient-image lazyload"
437
+ loading="lazy"
438
+ />
439
+ </div>
440
+ <p class="mt-2 text-center text-sm font-medium text-gray-800">{{ ingredient.name | escape }}</p>
441
+ </button>
442
+ {% endfor %}
443
+ </div>
444
+ </div>
445
+ {% endif %}
446
+ </div>
447
+
448
+ <!-- Modal for Ingredients -->
449
+ <div id="ingredientModal{{ loop.index }}" class="ingredient-modal-container hidden modal" role="dialog" aria-modal="true" aria-labelledby="modalTitle{{ loop.index }}">
450
+ <div class="ingredient-modal">
451
+ <div class="p-6">
452
+ <div class="popup-header flex justify-between items-center mb-4">
453
+ <h3 id="modalTitle{{ loop.index }}" class="text-xl font-semibold text-gray-800"></h3>
454
+ <button onclick="closeModal({{ loop.index }})" class="popup-close" aria-label="Close ingredient details modal"><span>×</span></button>
455
+ </div>
456
+
457
+ <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" />
458
+
459
+ <div class="space-y-4">
460
+ <div class="modal-section">
461
+ <h4 class="font-semibold mb-2 text-gray-800 flex items-center">
462
+ <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">
463
+ <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>
464
+ </svg>
465
+ Health Benefits
466
+ </h4>
467
+ <ul id="healthBenefits{{ loop.index }}" class="list-disc list-inside text-gray-700"></ul>
468
+ </div>
469
+
470
+ <div class="modal-section">
471
+ <h4 class="font-semibold mb-2 text-gray-800 flex items-center">
472
+ <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">
473
+ <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>
474
+ </svg>
475
+ Fun Facts
476
+ </h4>
477
+ <ul id="funFacts{{ loop.index }}" class="list-disc list-inside text-gray-700"></ul>
478
+ </div>
479
+ </div>
480
+ </div>
481
+ </div>
482
+ </div>
483
+ {% endfor %}
484
+ {% else %}
485
+ <div class="no-data">No previous orders found.</div>
486
+ {% endif %}
487
+ </div>
488
+
489
+ <script>
490
+ // Debounce function for performance
491
+ function debounce(func, wait) {
492
+ let timeout;
493
+ return function executedFunction(...args) {
494
+ const later = () => {
495
+ clearTimeout(timeout);
496
+ func(...args);
497
+ };
498
+ clearTimeout(timeout);
499
+ timeout = setTimeout(later, wait);
500
+ };
501
+ }
502
+
503
+ // Show sector description popup
504
+ function showSectorDescription(description) {
505
+ try {
506
+ document.getElementById('sector-description').innerText = description || 'No description available.';
507
+ document.getElementById('sector-popup').style.display = 'flex';
508
+ document.body.classList.add('modal-open');
509
+ } catch (e) {
510
+ console.error('Error showing sector description:', e);
511
+ }
512
+ }
513
+
514
+ // Close sector popup
515
+ function closePopup() {
516
+ document.getElementById('sector-popup').style.display = 'none';
517
+ document.body.classList.remove('modal-open');
518
+ }
519
+
520
+ // Toggle tier details
521
+ function toggleTierDetails() {
522
+ const tierDetails = document.getElementById('tierDetails');
523
+ const arrowPath = document.getElementById('arrowPath');
524
+ const toggleButton = tierDetails.parentElement.querySelector('[aria-controls="tierDetails"]');
525
+ const isExpanded = !tierDetails.classList.contains('hidden');
526
+ tierDetails.classList.toggle('hidden');
527
+ arrowPath.setAttribute('d', tierDetails.classList.contains('hidden') ? 'M19 9l-7 7-7-7' : 'M19 15l-7-7-7 7');
528
+ toggleButton.setAttribute('aria-expanded', !isExpanded);
529
+ }
530
+
531
+ // Toggle ingredients section
532
+ function toggleIngredients(index) {
533
+ try {
534
+ const currentSection = document.getElementById('ingredientsSection' + index);
535
+ const currentButton = document.getElementById('ingredientsToggleButton' + index);
536
+ const isCurrentlyVisible = !currentSection.classList.contains('hidden');
537
+
538
+ document.querySelectorAll('[id^="ingredientsSection"]').forEach(section => section.classList.add('hidden'));
539
+ document.querySelectorAll('[id^="ingredientsToggleButton"]').forEach(button => {
540
+ button.textContent = 'Show Ingredients';
541
+ button.setAttribute('aria-expanded', 'false');
542
+ });
543
+
544
+ if (!isCurrentlyVisible) {
545
+ currentSection.classList.remove('hidden');
546
+ currentButton.textContent = 'Hide Ingredients';
547
+ currentButton.setAttribute('aria-expanded', 'true');
548
+ }
549
+ } catch (e) {
550
+ console.error('Error toggling ingredients:', e);
551
+ }
552
+ }
553
+
554
+ // Show ingredient details modal
555
+ function showIngredientDetails(index, name, image, healthBenefits, funFacts) {
556
+ try {
557
+ const healthBenefitsList = (healthBenefits || '').split(',').filter(item => item.trim());
558
+ const funFactsList = (funFacts || '').split(',').filter(item => item.trim());
559
+ document.getElementById("modalTitle" + index).textContent = name || 'Unknown Ingredient';
560
+ document.getElementById("modalImage" + index).src = image || '/static/placeholder.jpg';
561
+ const healthBenefitsUl = document.getElementById("healthBenefits" + index);
562
+ const funFactsUl = document.getElementById("funFacts" + index);
563
+ healthBenefitsUl.innerHTML = '';
564
+ funFactsUl.innerHTML = '';
565
+ healthBenefitsList.length ? healthBenefitsList.forEach(item => {
566
+ const li = document.createElement("li");
567
+ li.textContent = item.trim();
568
+ healthBenefitsUl.appendChild(li);
569
+ }) : healthBenefitsUl.innerHTML = '<li>No health benefits available.</li>';
570
+ funFactsList.length ? funFactsList.forEach(item => {
571
+ const li = document.createElement("li");
572
+ li.textContent = item.trim();
573
+ funFactsUl.appendChild(li);
574
+ }) : funFactsUl.innerHTML = '<li>No fun facts available.</li>';
575
+ document.getElementById("ingredientModal" + index).style.display = "flex";
576
+ document.body.classList.add('modal-open');
577
+ } catch (e) {
578
+ console.error('Error showing ingredient details:', e);
579
+ }
580
+ }
581
+
582
+ // Close ingredient modal
583
+ function closeModal(index) {
584
+ try {
585
+ document.getElementById("ingredientModal" + index).style.display = "none";
586
+ document.body.classList.remove('modal-open');
587
+ } catch (e) {
588
+ console.error('Error closing modal:', e);
589
+ }
590
+ }
591
+
592
+ // Close modals on outside click
593
+ document.addEventListener('click', function(e) {
594
+ if (e.target.id === 'sector-popup') {
595
+ closePopup();
596
+ }
597
+ document.querySelectorAll('.ingredient-modal-container').forEach(modal => {
598
+ if (e.target === modal) {
599
+ const index = modal.id.replace('ingredientModal', '');
600
+ closeModal(index);
601
+ }
602
+ });
603
+ });
604
+ </script>
605
+ </body>
606
+ </html>