Subh775 commited on
Commit
fd6dc9d
·
1 Parent(s): 6df8f0e

typography; mapping; reordering; state reset in form submission..

Browse files
Files changed (2) hide show
  1. backend/server.py +75 -46
  2. frontend/js/vehicles.js +4 -2
backend/server.py CHANGED
@@ -200,73 +200,102 @@ FEEDBACK_PATH = Path(tempfile.gettempdir()) / "urbanflow_feedback.json"
200
  def send_feedback_email(api_key, feedback):
201
  try:
202
  resend.api_key = api_key
203
- fb_type = feedback.get('type') or 'General'
204
- rating = feedback.get('rating', 0)
205
- details = feedback.get('details') or "No text provided"
206
- usecase = feedback.get('usecase') or "Not specified"
207
-
208
  emojis = feedback.get('emojis', {})
209
  priorities = feedback.get('priorities', [])
210
 
211
- def get_emoji_row(label, choice):
212
- options = ["Poor", "Fair", "Good", "Great"]
213
- # Map choice to title case for comparison
 
 
 
 
 
214
  c = (choice or "").title()
215
 
216
- row = f"<div style='margin-bottom: 12px;'><span style='font-size: 11px; font-weight: bold; color: #777; text-transform: uppercase;'>{label}</span><br><div style='margin-top: 4px;'>"
217
  for opt in options:
218
  if c == opt:
219
- row += f"<span style='display: inline-block; background: #c89a6c; color: #000; font-size: 10px; font-weight: bold; padding: 3px 10px; border-radius: 4px; margin-right: 6px;'>{opt}</span>"
220
  else:
221
- row += f"<span style='display: inline-block; background: #f5f5f5; color: #bbb; font-size: 10px; padding: 2px 9px; border-radius: 4px; border: 1px solid #eee; margin-right: 6px;'>{opt}</span>"
222
  row += "</div></div>"
223
  return row
224
 
 
225
  metrics_html = ""
226
- metrics_html += get_emoji_row("Recommend Product", emojis.get("fb-recommend"))
227
- metrics_html += get_emoji_row("Security Assessment", emojis.get("fb-security"))
228
- metrics_html += get_emoji_row("Integration Ready", emojis.get("fb-integration"))
229
- metrics_html += get_emoji_row("Ease of Use", emojis.get("fb-ease"))
 
230
 
 
231
  priority_section = ""
232
- if priorities:
233
- priority_section = "<p style='font-size: 11px; font-weight: bold; color: #777; text-transform: uppercase; margin-bottom: 8px;'>Feature Prioritization:</p>"
234
- priority_section += "<div style='display: flex; flex-wrap: wrap; gap: 6px;'>"
235
  for p in priorities:
236
- priority_section += f"<span style='display: inline-block; background: #eee; border-left: 3px solid #c89a6c; padding: 6px 12px; font-size: 12px; font-weight: 600; margin-bottom: 6px;'>{p}</span> "
237
- priority_section += "</div>"
238
-
239
- html_body = f"""
240
- <div style="font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; color: #333; max-width: 600px; border: 1px solid #ddd; padding: 40px; border-radius: 16px; line-height: 1.6;">
241
- <div style="text-align: center; margin-bottom: 30px;">
242
- <h2 style="color: #8b5e3c; margin: 0; font-size: 24px; letter-spacing: -0.5px;">UrbanFlow Intelligence</h2>
243
- <p style="color: #999; font-size: 12px; text-transform: uppercase; tracking: 1px;">Session Feedback Artifact</p>
244
- </div>
245
-
246
- <div style="background: #fafafa; padding: 20px; border-radius: 12px; margin-bottom: 30px;">
247
- <table style="width: 100%; font-size: 14px;">
248
- <tr><td style="color: #777; width: 40%; padding: 4px 0;">Primary Use Case:</td><td style="font-weight: 600;">{usecase}</td></tr>
249
- <tr><td style="color: #777; padding: 4px 0;">Feedback Category:</td><td style="font-weight: 600;">{fb_type}</td></tr>
250
- <tr><td style="color: #777; padding: 4px 0;">Overall Rating:</td><td style="font-weight: 600; color: #c89a6c;">{'★' * rating}{'☆' * (5-rating)} ({rating}/5)</td></tr>
251
  </table>
252
  </div>
253
-
254
- <div style="margin-bottom: 30px;">
255
- {metrics_html}
 
 
 
 
 
256
  </div>
 
 
 
 
 
 
 
 
 
 
257
 
258
- <div style="margin-bottom: 30px;">
259
- {priority_section}
260
- </div>
 
 
 
 
 
 
 
 
261
 
262
- <div style="margin-bottom: 10px;">
263
- <p style="font-size: 11px; font-weight: bold; color: #777; text-transform: uppercase;">Detailed Word Feedback:</p>
264
- <div style="background: #fff; border: 1px solid #eee; padding: 20px; border-radius: 8px; font-size: 14px; color: #444; white-space: pre-wrap; line-height: 1.8;">{details}</div>
 
 
 
 
 
 
 
 
 
 
 
265
  </div>
266
-
267
- <hr style="border: 0; border-top: 1px solid #eee; margin: 40px 0;">
268
- <p style="font-size: 10px; color: #aaa; text-align: center; font-style: italic;">Automated transmission from UrbanFlow Inference Engine v1.0</p>
269
- </div>
270
  """
271
 
272
  resend.Emails.send({
 
200
  def send_feedback_email(api_key, feedback):
201
  try:
202
  resend.api_key = api_key
 
 
 
 
 
203
  emojis = feedback.get('emojis', {})
204
  priorities = feedback.get('priorities', [])
205
 
206
+ # Check if it's stars-only (no emojis, no priorities, no text)
207
+ has_emojis = any(v for v in emojis.values())
208
+ has_priorities = len(priorities) > 0
209
+ has_text = bool(details and details.strip() and details != "No text provided")
210
+ is_stars_only = not (has_emojis or has_priorities or has_text)
211
+
212
+ def get_emoji_row(label, choice, custom_options=None):
213
+ options = custom_options or ["Poor", "Fair", "Good", "Great"]
214
  c = (choice or "").title()
215
 
216
+ row = f"<div style='margin-bottom: 16px;'><span style='font-size: 10px; font-weight: 800; color: #888; text-transform: uppercase; letter-spacing: 1px;'>{label}</span><br><div style='margin-top: 6px;'>"
217
  for opt in options:
218
  if c == opt:
219
+ row += f"<span style='display: inline-block; background: #c89a6c; color: #000; font-size: 10px; font-weight: bold; padding: 4px 12px; border-radius: 6px; margin-right: 8px;'>{opt}</span>"
220
  else:
221
+ row += f"<span style='display: inline-block; background: #fff; color: #ccc; font-size: 10px; padding: 3px 11px; border-radius: 6px; border: 1px solid #eee; margin-right: 8px;'>{opt}</span>"
222
  row += "</div></div>"
223
  return row
224
 
225
+ # Section 1: Experience Metrics (Above)
226
  metrics_html = ""
227
+ if not is_stars_only:
228
+ metrics_html += get_emoji_row("Recommend Product", emojis.get("fb-recommend"), ["Unlikely", "Maybe", "Likely", "Highly"])
229
+ metrics_html += get_emoji_row("Security Assessment", emojis.get("fb-security"))
230
+ metrics_html += get_emoji_row("Integration Readiness", emojis.get("fb-integration"))
231
+ metrics_html += get_emoji_row("Ease of Use", emojis.get("fb-ease"))
232
 
233
+ # Section 2: Priorities (Below)
234
  priority_section = ""
235
+ if has_priorities:
236
+ priority_section = "<p style='font-size: 10px; font-weight: 800; color: #888; text-transform: uppercase; letter-spacing: 1px; margin-bottom: 10px;'>Feature Prioritization:</p>"
 
237
  for p in priorities:
238
+ priority_section += f"<div style='background: #fff; border-left: 4px solid #c89a6c; padding: 10px 16px; font-size: 13px; font-weight: 600; margin-bottom: 8px; border-top: 1px solid #f0f0f0; border-right: 1px solid #f0f0f0; border-bottom: 1px solid #f0f0f0; border-radius: 0 8px 8px 0;'>{p}</div>"
239
+
240
+ # Categorization & Feedback Content
241
+ categorization_html = ""
242
+ if not is_stars_only:
243
+ categorization_html = f"""
244
+ <div style="background: #fafafa; padding: 25px; border-radius: 16px; margin: 30px 0; border: 1px solid #f0f0f0;">
245
+ <table style="width: 100%; font-size: 13px;">
246
+ <tr><td style="color: #999; width: 40%; padding: 6px 0; text-transform: uppercase; font-size: 9px; font-weight: 800; letter-spacing: 0.5px;">Primary Use Case</td><td style="font-weight: 700; color: #333;">{usecase}</td></tr>
247
+ <tr><td style="color: #999; padding: 6px 0; text-transform: uppercase; font-size: 9px; font-weight: 800; letter-spacing: 0.5px;">Feedback Category</td><td style="font-weight: 700; color: #333;">{fb_type}</td></tr>
 
 
 
 
 
248
  </table>
249
  </div>
250
+ """
251
+
252
+ detailed_feedback_html = ""
253
+ if has_text:
254
+ detailed_feedback_html = f"""
255
+ <div style="margin-top: 30px;">
256
+ <p style="font-size: 10px; font-weight: 800; color: #888; text-transform: uppercase; letter-spacing: 1px; margin-bottom: 12px;">Detailed Word Feedback</p>
257
+ <div style="background: #fff; border: 1px solid #eee; padding: 25px; border-radius: 14px; font-size: 14px; color: #444; white-space: pre-wrap; line-height: 1.8; box-shadow: 0 2px 10px rgba(0,0,0,0.02);">{details}</div>
258
  </div>
259
+ """
260
+
261
+ # Header with Rating
262
+ header_rating = f"""
263
+ <div style="text-align: center; margin-bottom: 40px; padding: 20px; background: linear-gradient(180deg, #fff 0%, #fafafa 100%); border-radius: 20px;">
264
+ <h2 style="color: #8b5e3c; margin: 0; font-size: 26px; font-weight: 900; letter-spacing: -1px;">UrbanFlow Intelligence</h2>
265
+ <div style="margin-top: 15px; font-size: 22px; color: #c89a6c; letter-spacing: 4px;">{'★' * rating}{'☆' * (5-rating)}</div>
266
+ <p style="color: #aaa; font-size: 10px; text-transform: uppercase; letter-spacing: 2px; margin-top: 10px; font-weight: 700;">Overall Experience: {rating}/5 Stars</p>
267
+ </div>
268
+ """
269
 
270
+ # Assemble the body
271
+ final_content = header_rating
272
+ if is_stars_only:
273
+ final_content += "<div style='text-align: center; padding: 40px; color: #777; font-style: italic; font-size: 14px;'>User submitted an overall star rating with no additional details.</div>"
274
+ else:
275
+ final_content += f"""
276
+ <div style="margin-bottom: 40px;">{metrics_html}</div>
277
+ {categorization_html}
278
+ <div style="margin-bottom: 40px;">{priority_section}</div>
279
+ {detailed_feedback_html}
280
+ """
281
 
282
+ html_body = f"""
283
+ <!DOCTYPE html>
284
+ <html>
285
+ <head>
286
+ <link href="https://fonts.googleapis.com/css2?family=Montserrat:wght@400;600;700;800;900&display=swap" rel="stylesheet">
287
+ </head>
288
+ <body style="margin: 0; padding: 0; background-color: #ffffff;">
289
+ <div style="font-family: 'Montserrat', sans-serif; color: #333; max-width: 600px; margin: 40px auto; padding: 0 20px;">
290
+ {final_content}
291
+
292
+ <div style="margin-top: 60px; padding-top: 30px; border-top: 1px solid #eee; text-align: center;">
293
+ <p style="font-size: 10px; color: #bbb; text-transform: uppercase; letter-spacing: 2px; font-weight: 700; margin: 0;">Inference Engine Feedback Capture</p>
294
+ <p style="font-size: 9px; color: #ddd; margin-top: 5px;">&copy; 2026 UrbanFlow. All rights reserved.</p>
295
+ </div>
296
  </div>
297
+ </body>
298
+ </html>
 
 
299
  """
300
 
301
  resend.Emails.send({
frontend/js/vehicles.js CHANGED
@@ -158,8 +158,8 @@
158
  priorities.push(c.innerText);
159
  });
160
 
161
- if (!text && _fbRating === 0 && !_fbEmojis['fb-recommend'] && !_fbEmojis['fb-security'] && !_fbEmojis['fb-integration'] && !_fbEmojis['fb-ease']) {
162
- showToast("Please provide a rating, some emojis, or word feedback", "error");
163
  return;
164
  }
165
 
@@ -180,6 +180,8 @@
180
  if (res.ok) {
181
  showToast('Thank you for your feedback!', 'success');
182
  document.getElementById('fb-text').value = '';
 
 
183
  document.querySelectorAll('#fb-priorities .fb-chip').forEach(c => c.classList.remove('active'));
184
 
185
  // Reset Emojis
 
158
  priorities.push(c.innerText);
159
  });
160
 
161
+ if (_fbRating === 0 && !text && !Object.values(_fbEmojis).some(v => v) && priorities.length === 0) {
162
+ showToast("Please provide a star rating or some detailed feedback", "error");
163
  return;
164
  }
165
 
 
180
  if (res.ok) {
181
  showToast('Thank you for your feedback!', 'success');
182
  document.getElementById('fb-text').value = '';
183
+ document.getElementById('fb-usecase').selectedIndex = 0;
184
+ document.getElementById('fb-type').selectedIndex = 0;
185
  document.querySelectorAll('#fb-priorities .fb-chip').forEach(c => c.classList.remove('active'));
186
 
187
  // Reset Emojis