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

structured email; smooth cooldown

Browse files
backend/server.py CHANGED
@@ -200,7 +200,7 @@ 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').capitalize()
204
  rating = feedback.get('rating', 0)
205
  details = feedback.get('details') or "No text provided"
206
  usecase = feedback.get('usecase') or "Not specified"
@@ -208,42 +208,64 @@ def send_feedback_email(api_key, feedback):
208
  emojis = feedback.get('emojis', {})
209
  priorities = feedback.get('priorities', [])
210
 
211
- emoji_section = ""
212
- if emojis:
213
- emoji_section = "<p><strong>Experience Metrics:</strong></p><ul style='font-size: 13px;'>"
214
- for key, val in emojis.items():
215
- if val:
216
- label = key.replace('fb-', '').replace('-', ' ').title()
217
- emoji_section += f"<li>{label}: <span style='color:#c89a6c;'>{val.capitalize()}</span></li>"
218
- emoji_section += "</ul>"
 
 
 
 
 
 
 
 
 
 
 
219
 
220
  priority_section = ""
221
  if priorities:
222
- priority_section = "<p><strong>Priorities:</strong></p><ul style='font-size: 13px;'>"
 
223
  for p in priorities:
224
- priority_section += f"<li>{p.replace('-', ' ').capitalize()}</li>"
225
- priority_section += "</ul>"
226
 
227
  html_body = f"""
228
- <div style="font-family: sans-serif; color: #333; max-width: 600px; border: 1px solid #eee; padding: 25px; border-radius: 12px; line-height: 1.5;">
229
- <h2 style="color: #8b5e3c; margin-top: 0;">New UrbanFlow Feedback</h2>
230
-
231
- <table style="width: 100%; border-collapse: collapse; font-size: 14px;">
232
- <tr><td style="padding: 5px 0;"><strong>Category:</strong></td><td>{fb_type}</td></tr>
233
- <tr><td style="padding: 5px 0;"><strong>Rating:</strong></td><td>{'β˜…' * rating}{'β˜†' * (5-rating)} ({rating}/5)</td></tr>
234
- <tr><td style="padding: 5px 0;"><strong>Usecase:</strong></td><td>{usecase}</td></tr>
235
- </table>
236
-
237
- <hr style="border: 0; border-top: 1px solid #eee; margin: 20px 0;">
238
 
239
- {emoji_section}
240
- {priority_section}
241
-
242
- <p><strong>Detailed Feedback:</strong></p>
243
- <div style="background: #f9f9f9; padding: 15px; border-radius: 8px; font-size: 14px; color: #555; white-space: pre-wrap;">{details}</div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
244
 
245
- <hr style="border: 0; border-top: 1px solid #eee; margin: 20px 0;">
246
- <p style="font-size: 11px; color: #999; text-align: center;">Sent via UrbanFlow Demo Platform Engine</p>
247
  </div>
248
  """
249
 
 
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"
 
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
 
frontend/js/vehicles.js CHANGED
@@ -145,13 +145,17 @@
145
  }
146
 
147
  async function submitFeedback() {
148
- const type = document.getElementById('fb-type').value;
 
 
 
 
 
149
  const text = document.getElementById('fb-text').value.trim();
150
- const usecase = document.getElementById('fb-usecase').value;
151
 
152
  const priorities = [];
153
  document.querySelectorAll('#fb-priorities .fb-chip.active').forEach(c => {
154
- priorities.push(c.getAttribute('data-val'));
155
  });
156
 
157
  if (!text && _fbRating === 0 && !_fbEmojis['fb-recommend'] && !_fbEmojis['fb-security'] && !_fbEmojis['fb-integration'] && !_fbEmojis['fb-ease']) {
@@ -162,8 +166,8 @@
162
  const payload = {
163
  rating: _fbRating,
164
  emojis: _fbEmojis,
165
- type: type,
166
- usecase: usecase,
167
  priorities: priorities,
168
  details: text,
169
  timestamp: new Date().toISOString()
@@ -188,11 +192,10 @@
188
  _fbRating = 0;
189
  setRating(0);
190
 
191
- // Start Cooldown (60 seconds)
192
  const btn = document.getElementById('fb-submit-btn');
193
  const wrap = document.getElementById('fb-cooldown-wrap');
194
  const bar = document.getElementById('fb-cooldown-bar');
195
- const timerEl = document.getElementById('fb-cooldown-timer');
196
 
197
  btn.disabled = true;
198
  btn.style.opacity = '0.5';
@@ -200,15 +203,17 @@
200
  btn.innerText = 'Feedback Received';
201
  wrap.classList.remove('hidden');
202
 
203
- let timeLeft = 60;
204
- const total = 60;
205
- const interval = setInterval(() => {
206
- timeLeft -= 1;
207
- timerEl.innerText = timeLeft;
208
- bar.style.width = (timeLeft / total * 100) + '%';
 
209
 
210
- if (timeLeft <= 0) {
211
- clearInterval(interval);
 
212
  btn.disabled = false;
213
  btn.style.opacity = '1';
214
  btn.style.cursor = 'pointer';
@@ -216,7 +221,8 @@
216
  wrap.classList.add('hidden');
217
  bar.style.width = '100%';
218
  }
219
- }, 1000);
 
220
  } else {
221
  showToast('Failed to submit β€” please try again', 'error');
222
  }
 
145
  }
146
 
147
  async function submitFeedback() {
148
+ const typeEl = document.getElementById('fb-type');
149
+ const typeText = typeEl.selectedIndex >= 0 ? typeEl.options[typeEl.selectedIndex].text : "";
150
+
151
+ const usecaseEl = document.getElementById('fb-usecase');
152
+ const usecaseText = usecaseEl.selectedIndex >= 0 ? usecaseEl.options[usecaseEl.selectedIndex].text : "";
153
+
154
  const text = document.getElementById('fb-text').value.trim();
 
155
 
156
  const priorities = [];
157
  document.querySelectorAll('#fb-priorities .fb-chip.active').forEach(c => {
158
+ priorities.push(c.innerText);
159
  });
160
 
161
  if (!text && _fbRating === 0 && !_fbEmojis['fb-recommend'] && !_fbEmojis['fb-security'] && !_fbEmojis['fb-integration'] && !_fbEmojis['fb-ease']) {
 
166
  const payload = {
167
  rating: _fbRating,
168
  emojis: _fbEmojis,
169
+ type: typeText,
170
+ usecase: usecaseText,
171
  priorities: priorities,
172
  details: text,
173
  timestamp: new Date().toISOString()
 
192
  _fbRating = 0;
193
  setRating(0);
194
 
195
+ // Start Smooth Cooldown (60 seconds)
196
  const btn = document.getElementById('fb-submit-btn');
197
  const wrap = document.getElementById('fb-cooldown-wrap');
198
  const bar = document.getElementById('fb-cooldown-bar');
 
199
 
200
  btn.disabled = true;
201
  btn.style.opacity = '0.5';
 
203
  btn.innerText = 'Feedback Received';
204
  wrap.classList.remove('hidden');
205
 
206
+ const startTime = Date.now();
207
+ const duration = 60000;
208
+
209
+ function updateSmoothBar() {
210
+ const elapsed = Date.now() - startTime;
211
+ const remaining = Math.max(0, duration - elapsed);
212
+ bar.style.width = (remaining / duration * 100) + '%';
213
 
214
+ if (remaining > 0) {
215
+ requestAnimationFrame(updateSmoothBar);
216
+ } else {
217
  btn.disabled = false;
218
  btn.style.opacity = '1';
219
  btn.style.cursor = 'pointer';
 
221
  wrap.classList.add('hidden');
222
  bar.style.width = '100%';
223
  }
224
+ }
225
+ requestAnimationFrame(updateSmoothBar);
226
  } else {
227
  showToast('Failed to submit β€” please try again', 'error');
228
  }
frontend/vehicles.html CHANGED
@@ -833,11 +833,11 @@
833
  </button>
834
 
835
  <!-- Cooldown Progress Bar -->
836
- <div id="fb-cooldown-wrap" class="w-full mt-4 hidden">
837
- <div class="w-full h-1 bg-neutral-900 rounded-full overflow-hidden">
838
- <div id="fb-cooldown-bar" class="h-full w-full transition-all duration-100 ease-linear rounded-full" style="background:var(--cocoa-l)"></div>
839
  </div>
840
- <p class="text-[9px] font-bold text-center mt-2 uppercase tracking-[0.2em]" style="color:#555">Anti-Spam Cooldown: <span id="fb-cooldown-timer">60</span>s</p>
841
  </div>
842
 
843
  </div>
 
833
  </button>
834
 
835
  <!-- Cooldown Progress Bar -->
836
+ <div id="fb-cooldown-wrap" class="w-full mt-5 hidden">
837
+ <div class="w-full h-[1.5px] bg-neutral-900 rounded-full overflow-hidden">
838
+ <div id="fb-cooldown-bar" class="h-full w-full rounded-full" style="background:var(--cocoa-l)"></div>
839
  </div>
840
+ <p class="text-[9px] font-bold text-center mt-2.5 uppercase tracking-[0.25em]" style="color:#444">Ready to send again in a moment...</p>
841
  </div>
842
 
843
  </div>