nivakaran commited on
Commit
cde478d
·
verified ·
1 Parent(s): 9834d8a

Upload index.html

Browse files
Files changed (1) hide show
  1. templates/index.html +664 -0
templates/index.html ADDED
@@ -0,0 +1,664 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ <title>Food Delivery Time Prediction</title>
7
+ <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
8
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
9
+ <style>
10
+ * {
11
+ margin: 0;
12
+ padding: 0;
13
+ box-sizing: border-box;
14
+ }
15
+
16
+ body {
17
+ font-family: 'Inter', sans-serif;
18
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
19
+ min-height: 100vh;
20
+ padding: 20px;
21
+ }
22
+
23
+ .container {
24
+ max-width: 1000px;
25
+ margin: 0 auto;
26
+ background: rgba(255, 255, 255, 0.95);
27
+ backdrop-filter: blur(20px);
28
+ border-radius: 24px;
29
+ box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1);
30
+ overflow: hidden;
31
+ }
32
+
33
+ .header {
34
+ background: linear-gradient(135deg, #ff6b6b, #ee5a24);
35
+ padding: 40px;
36
+ text-align: center;
37
+ color: white;
38
+ position: relative;
39
+ overflow: hidden;
40
+ }
41
+
42
+ .header::before {
43
+ content: '';
44
+ position: absolute;
45
+ top: -50%;
46
+ left: -50%;
47
+ width: 200%;
48
+ height: 200%;
49
+ background: radial-gradient(circle, rgba(255,255,255,0.1) 0%, transparent 70%);
50
+ animation: pulse 4s ease-in-out infinite;
51
+ }
52
+
53
+ @keyframes pulse {
54
+ 0%, 100% { transform: scale(1); opacity: 0.5; }
55
+ 50% { transform: scale(1.1); opacity: 0.3; }
56
+ }
57
+
58
+ .header h1 {
59
+ font-size: 2.5rem;
60
+ font-weight: 700;
61
+ margin-bottom: 10px;
62
+ position: relative;
63
+ z-index: 1;
64
+ }
65
+
66
+ .header p {
67
+ font-size: 1.1rem;
68
+ opacity: 0.9;
69
+ position: relative;
70
+ z-index: 1;
71
+ }
72
+
73
+ .form-container {
74
+ padding: 40px;
75
+ }
76
+
77
+ .train-btn {
78
+ width: 100%;
79
+ padding: 16px;
80
+ background: linear-gradient(135deg, #f093fb, #f5576c);
81
+ color: white;
82
+ border: none;
83
+ border-radius: 12px;
84
+ font-size: 1rem;
85
+ font-weight: 600;
86
+ cursor: pointer;
87
+ transition: all 0.3s ease;
88
+ text-transform: uppercase;
89
+ letter-spacing: 1px;
90
+ margin-bottom: 32px;
91
+ position: relative;
92
+ overflow: hidden;
93
+ }
94
+
95
+ .train-btn:hover {
96
+ transform: translateY(-2px);
97
+ box-shadow: 0 10px 25px rgba(240, 147, 251, 0.3);
98
+ }
99
+
100
+ .train-btn:disabled {
101
+ opacity: 0.6;
102
+ cursor: not-allowed;
103
+ transform: none;
104
+ }
105
+
106
+ .train-btn::before {
107
+ content: '';
108
+ position: absolute;
109
+ top: 0;
110
+ left: -100%;
111
+ width: 100%;
112
+ height: 100%;
113
+ background: linear-gradient(90deg, transparent, rgba(255,255,255,0.2), transparent);
114
+ transition: left 0.5s;
115
+ }
116
+
117
+ .train-btn:hover::before {
118
+ left: 100%;
119
+ }
120
+
121
+ .form-grid {
122
+ display: grid;
123
+ grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
124
+ gap: 24px;
125
+ margin-bottom: 32px;
126
+ }
127
+
128
+ .form-group {
129
+ position: relative;
130
+ }
131
+
132
+ .form-group label {
133
+ display: block;
134
+ font-weight: 600;
135
+ color: #2d3748;
136
+ margin-bottom: 8px;
137
+ font-size: 0.9rem;
138
+ text-transform: uppercase;
139
+ letter-spacing: 0.5px;
140
+ }
141
+
142
+ .form-group input,
143
+ .form-group select {
144
+ width: 100%;
145
+ padding: 16px 20px;
146
+ border: 2px solid #e2e8f0;
147
+ border-radius: 12px;
148
+ font-size: 1rem;
149
+ transition: all 0.3s ease;
150
+ background: white;
151
+ color: #2d3748;
152
+ }
153
+
154
+ .form-group input:focus,
155
+ .form-group select:focus {
156
+ outline: none;
157
+ border-color: #667eea;
158
+ box-shadow: 0 0 0 4px rgba(102, 126, 234, 0.1);
159
+ transform: translateY(-2px);
160
+ }
161
+
162
+ .form-group input:hover,
163
+ .form-group select:hover {
164
+ border-color: #cbd5e0;
165
+ transform: translateY(-1px);
166
+ }
167
+
168
+ .form-group select {
169
+ cursor: pointer;
170
+ appearance: none;
171
+ background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='m6 8 4 4 4-4'/%3e%3c/svg%3e");
172
+ background-position: right 12px center;
173
+ background-repeat: no-repeat;
174
+ background-size: 16px;
175
+ }
176
+
177
+ .submit-btn {
178
+ width: 100%;
179
+ padding: 18px;
180
+ background: linear-gradient(135deg, #667eea, #764ba2);
181
+ color: white;
182
+ border: none;
183
+ border-radius: 12px;
184
+ font-size: 1.1rem;
185
+ font-weight: 600;
186
+ cursor: pointer;
187
+ transition: all 0.3s ease;
188
+ text-transform: uppercase;
189
+ letter-spacing: 1px;
190
+ position: relative;
191
+ overflow: hidden;
192
+ }
193
+
194
+ .submit-btn:hover {
195
+ transform: translateY(-2px);
196
+ box-shadow: 0 10px 25px rgba(102, 126, 234, 0.3);
197
+ }
198
+
199
+ .submit-btn:active {
200
+ transform: translateY(0);
201
+ }
202
+
203
+ .submit-btn::before {
204
+ content: '';
205
+ position: absolute;
206
+ top: 0;
207
+ left: -100%;
208
+ width: 100%;
209
+ height: 100%;
210
+ background: linear-gradient(90deg, transparent, rgba(255,255,255,0.2), transparent);
211
+ transition: left 0.5s;
212
+ }
213
+
214
+ .submit-btn:hover::before {
215
+ left: 100%;
216
+ }
217
+
218
+ .result {
219
+ margin-top: 32px;
220
+ padding: 24px;
221
+ background: linear-gradient(135deg, #48bb78, #38a169);
222
+ border-radius: 16px;
223
+ color: white;
224
+ text-align: center;
225
+ opacity: 0;
226
+ transform: translateY(20px);
227
+ transition: all 0.5s ease;
228
+ }
229
+
230
+ .result.show {
231
+ opacity: 1;
232
+ transform: translateY(0);
233
+ }
234
+
235
+ .error-result {
236
+ margin-top: 32px;
237
+ padding: 24px;
238
+ background: linear-gradient(135deg, #e53e3e, #c53030);
239
+ border-radius: 16px;
240
+ color: white;
241
+ text-align: center;
242
+ opacity: 0;
243
+ transform: translateY(20px);
244
+ transition: all 0.5s ease;
245
+ }
246
+
247
+ .error-result.show {
248
+ opacity: 1;
249
+ transform: translateY(0);
250
+ }
251
+
252
+ .result-icon {
253
+ font-size: 3rem;
254
+ margin-bottom: 16px;
255
+ animation: bounce 2s infinite;
256
+ }
257
+
258
+ @keyframes bounce {
259
+ 0%, 20%, 50%, 80%, 100% { transform: translateY(0); }
260
+ 40% { transform: translateY(-10px); }
261
+ 60% { transform: translateY(-5px); }
262
+ }
263
+
264
+ .result-text {
265
+ font-size: 1.2rem;
266
+ font-weight: 600;
267
+ }
268
+
269
+ .loading {
270
+ display: none;
271
+ justify-content: center;
272
+ align-items: center;
273
+ margin-top: 20px;
274
+ padding: 20px;
275
+ background: linear-gradient(135deg, #667eea, #764ba2);
276
+ border-radius: 16px;
277
+ color: white;
278
+ }
279
+
280
+ .loading.show {
281
+ display: flex;
282
+ }
283
+
284
+ .spinner {
285
+ width: 40px;
286
+ height: 40px;
287
+ border: 4px solid rgba(255, 255, 255, 0.3);
288
+ border-top: 4px solid white;
289
+ border-radius: 50%;
290
+ animation: spin 1s linear infinite;
291
+ margin-right: 12px;
292
+ }
293
+
294
+ @keyframes spin {
295
+ 0% { transform: rotate(0deg); }
296
+ 100% { transform: rotate(360deg); }
297
+ }
298
+
299
+ .feature-icons {
300
+ display: grid;
301
+ grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
302
+ gap: 20px;
303
+ margin-top: 32px;
304
+ padding-top: 32px;
305
+ border-top: 2px solid #e2e8f0;
306
+ }
307
+
308
+ .feature-icon {
309
+ text-align: center;
310
+ padding: 20px;
311
+ border-radius: 12px;
312
+ background: linear-gradient(135deg, #f7fafc, #edf2f7);
313
+ transition: transform 0.3s ease;
314
+ }
315
+
316
+ .feature-icon:hover {
317
+ transform: translateY(-5px);
318
+ }
319
+
320
+ .feature-icon i {
321
+ font-size: 2rem;
322
+ color: #667eea;
323
+ margin-bottom: 8px;
324
+ }
325
+
326
+ .feature-icon span {
327
+ display: block;
328
+ font-size: 0.8rem;
329
+ color: #4a5568;
330
+ font-weight: 500;
331
+ }
332
+
333
+ @media (max-width: 768px) {
334
+ .form-grid {
335
+ grid-template-columns: 1fr;
336
+ gap: 20px;
337
+ }
338
+
339
+ .header h1 {
340
+ font-size: 2rem;
341
+ }
342
+
343
+ .form-container {
344
+ padding: 24px;
345
+ }
346
+
347
+ .header {
348
+ padding: 32px 24px;
349
+ }
350
+ }
351
+ </style>
352
+ </head>
353
+ <body>
354
+ <div class="container">
355
+ <div class="header">
356
+ <h1><i class="fas fa-motorcycle"></i> Food Delivery Time Prediction</h1>
357
+ <p>Advanced AI-powered delivery time estimation system</p>
358
+ </div>
359
+
360
+ <div class="form-container">
361
+ <!-- Training Section -->
362
+ <button type="button" class="train-btn" id="train-btn">
363
+ <i class="fas fa-graduation-cap"></i> Train Model
364
+ </button>
365
+
366
+ <form id="prediction-form">
367
+ <div class="form-grid">
368
+ <div class="form-group">
369
+ <label for="multiple_deliveries"><i class="fas fa-boxes"></i> Multiple Deliveries</label>
370
+ <input type="number" id="multiple_deliveries" name="multiple_deliveries" step="1" min="0" placeholder="e.g., 1" required>
371
+ </div>
372
+
373
+ <div class="form-group">
374
+ <label for="Road_traffic_density"><i class="fas fa-road"></i> Road Traffic Density</label>
375
+ <select id="Road_traffic_density" name="Road_traffic_density" required>
376
+ <option value="" disabled selected>Select traffic level</option>
377
+ <option value="Low">Low</option>
378
+ <option value="Medium">Medium</option>
379
+ <option value="High">High</option>
380
+ <option value="Jam">Jam</option>
381
+ </select>
382
+ </div>
383
+
384
+ <div class="form-group">
385
+ <label for="Vehicle_condition"><i class="fas fa-car"></i> Vehicle Condition</label>
386
+ <select id="Vehicle_condition" name="Vehicle_condition" required>
387
+ <option value="" disabled selected>Select condition</option>
388
+ <option value="Poor">Poor</option>
389
+ <option value="Good">Good</option>
390
+ <option value="Excellent">Excellent</option>
391
+ </select>
392
+ </div>
393
+
394
+ <div class="form-group">
395
+ <label for="Delivery_person_Ratings"><i class="fas fa-star"></i> Delivery Person Ratings (1-5)</label>
396
+ <input type="number" id="Delivery_person_Ratings" name="Delivery_person_Ratings" step="0.1" min="1" max="5" placeholder="e.g., 4.5" required>
397
+ </div>
398
+
399
+ <div class="form-group">
400
+ <label for="distance_deliveries"><i class="fas fa-calculator"></i> Distance × Deliveries</label>
401
+ <input type="number" id="distance_deliveries" name="distance_deliveries" step="0.1" placeholder="e.g., 10.5" required>
402
+ </div>
403
+
404
+ <div class="form-group">
405
+ <label for="Weather_conditions"><i class="fas fa-cloud-sun"></i> Weather Conditions</label>
406
+ <select id="Weather_conditions" name="Weather_conditions" required>
407
+ <option value="" disabled selected>Select weather</option>
408
+ <option value="Sunny">Sunny</option>
409
+ <option value="Cloudy">Cloudy</option>
410
+ <option value="Fog">Fog</option>
411
+ <option value="Sandstorms">Sandstorms</option>
412
+ <option value="Stormy">Stormy</option>
413
+ <option value="Windy">Windy</option>
414
+ </select>
415
+ </div>
416
+
417
+ <div class="form-group">
418
+ <label for="Festival"><i class="fas fa-calendar-alt"></i> Festival</label>
419
+ <select id="Festival" name="Festival" required>
420
+ <option value="" disabled selected>Festival period?</option>
421
+ <option value="No">No</option>
422
+ <option value="Yes">Yes</option>
423
+ </select>
424
+ </div>
425
+
426
+ <div class="form-group">
427
+ <label for="distance_traffic"><i class="fas fa-chart-line"></i> Distance × Traffic</label>
428
+ <input type="number" id="distance_traffic" name="distance_traffic" step="0.1" placeholder="e.g., 15.2" required>
429
+ </div>
430
+
431
+ <div class="form-group">
432
+ <label for="distance"><i class="fas fa-route"></i> Distance (km)</label>
433
+ <input type="number" id="distance" name="distance" step="0.1" min="0" placeholder="e.g., 5.0" required>
434
+ </div>
435
+
436
+ <div class="form-group">
437
+ <label for="Delivery_person_Age"><i class="fas fa-user"></i> Delivery Person Age</label>
438
+ <input type="number" id="Delivery_person_Age" name="Delivery_person_Age" step="1" min="18" max="60" placeholder="e.g., 30" required>
439
+ </div>
440
+
441
+ <div class="form-group">
442
+ <label for="prep_traffic"><i class="fas fa-clock"></i> Prep Time × Traffic</label>
443
+ <input type="number" id="prep_traffic" name="prep_traffic" step="0.1" placeholder="e.g., 12.0" required>
444
+ </div>
445
+
446
+ <div class="form-group">
447
+ <label for="City"><i class="fas fa-city"></i> City</label>
448
+ <select id="City" name="City" required>
449
+ <option value="" disabled selected>Select city type</option>
450
+ <option value="Urban">Urban</option>
451
+ <option value="Semi-Urban">Semi-Urban</option>
452
+ <option value="Metropolitan">Metropolitan</option>
453
+ </select>
454
+ </div>
455
+ </div>
456
+
457
+ <button type="submit" class="submit-btn" id="predict-btn">
458
+ <i class="fas fa-magic"></i> Predict Delivery Time
459
+ </button>
460
+ </form>
461
+
462
+ <div class="loading" id="loading">
463
+ <div class="spinner"></div>
464
+ <span id="loading-text">Processing...</span>
465
+ </div>
466
+
467
+ <div id="result" class="result">
468
+ <div class="result-icon">🚀</div>
469
+ <div class="result-text"></div>
470
+ </div>
471
+
472
+ <div id="error-result" class="error-result">
473
+ <div class="result-icon">❌</div>
474
+ <div class="result-text"></div>
475
+ </div>
476
+
477
+ <div class="feature-icons">
478
+ <div class="feature-icon">
479
+ <i class="fas fa-brain"></i>
480
+ <span>AI Powered</span>
481
+ </div>
482
+ <div class="feature-icon">
483
+ <i class="fas fa-tachometer-alt"></i>
484
+ <span>Real-time</span>
485
+ </div>
486
+ <div class="feature-icon">
487
+ <i class="fas fa-chart-bar"></i>
488
+ <span>Accurate</span>
489
+ </div>
490
+ <div class="feature-icon">
491
+ <i class="fas fa-mobile-alt"></i>
492
+ <span>Responsive</span>
493
+ </div>
494
+ </div>
495
+ </div>
496
+ </div>
497
+
498
+ <script>
499
+ // Helper function to show loading
500
+ function showLoading(message = "Processing...") {
501
+ document.getElementById('loading-text').textContent = message;
502
+ document.getElementById('loading').classList.add('show');
503
+ document.getElementById('result').classList.remove('show');
504
+ document.getElementById('error-result').classList.remove('show');
505
+ }
506
+
507
+ // Helper function to hide loading
508
+ function hideLoading() {
509
+ document.getElementById('loading').classList.remove('show');
510
+ }
511
+
512
+ // Helper function to show success result
513
+ function showSuccess(message) {
514
+ document.getElementById('result').classList.add('show');
515
+ document.querySelector('#result .result-text').innerHTML = message;
516
+ document.getElementById('result').scrollIntoView({
517
+ behavior: 'smooth',
518
+ block: 'center'
519
+ });
520
+ }
521
+
522
+ // Helper function to show error result
523
+ function showError(message) {
524
+ document.getElementById('error-result').classList.add('show');
525
+ document.querySelector('#error-result .result-text').innerHTML = message;
526
+ document.getElementById('error-result').scrollIntoView({
527
+ behavior: 'smooth',
528
+ block: 'center'
529
+ });
530
+ }
531
+
532
+ // Training functionality - ONLY when train button is clicked
533
+ document.getElementById('train-btn').addEventListener('click', async function() {
534
+ const trainBtn = this;
535
+
536
+ // Show loading and disable button
537
+ showLoading("Training model with latest data...");
538
+ trainBtn.disabled = true;
539
+ trainBtn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Training in Progress...';
540
+
541
+ try {
542
+ const response = await fetch('/train', {
543
+ method: 'GET',
544
+ headers: {
545
+ 'Content-Type': 'application/json',
546
+ }
547
+ });
548
+
549
+ hideLoading();
550
+
551
+ if (response.ok) {
552
+ const text = await response.text();
553
+ showSuccess(
554
+ `<strong>✅ Model Training Completed Successfully!</strong><br>
555
+ <small>The AI model has been retrained with the latest data and is ready for predictions</small>`
556
+ );
557
+ } else {
558
+ const errorText = await response.text();
559
+ throw new Error(`Training failed: ${response.status} - ${errorText}`);
560
+ }
561
+
562
+ } catch (error) {
563
+ console.error('Training error:', error);
564
+ hideLoading();
565
+ showError(
566
+ `<strong>Training Failed!</strong><br>
567
+ <small>Error: ${error.message}<br>
568
+ Please check your server connection and try again.</small>`
569
+ );
570
+ } finally {
571
+ // Re-enable button
572
+ trainBtn.disabled = false;
573
+ trainBtn.innerHTML = '<i class="fas fa-graduation-cap"></i> Train Model';
574
+ }
575
+ });
576
+
577
+ // Prediction functionality - when form is submitted
578
+ document.getElementById('prediction-form').addEventListener('submit', async function(e) {
579
+ e.preventDefault();
580
+
581
+ const predictBtn = document.getElementById('predict-btn');
582
+
583
+ // Show loading and disable predict button
584
+ showLoading("Calculating delivery time...");
585
+ predictBtn.disabled = true;
586
+ predictBtn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Predicting...';
587
+
588
+ // Collect form data
589
+ const formData = new FormData(this);
590
+
591
+ try {
592
+ const response = await fetch('/predict', {
593
+ method: 'POST',
594
+ body: formData
595
+ });
596
+
597
+ const data = await response.json();
598
+ hideLoading();
599
+
600
+ if (response.ok && data.prediction !== undefined) {
601
+ // Show successful prediction result
602
+ showSuccess(
603
+ `<strong>Estimated Delivery Time: ${data.prediction} minutes</strong><br>
604
+ <small>Based on current conditions and AI analysis</small>`
605
+ );
606
+ } else {
607
+ // Show error from backend
608
+ throw new Error(data.error || 'Prediction failed');
609
+ }
610
+
611
+ } catch (error) {
612
+ console.error('Prediction error:', error);
613
+ hideLoading();
614
+ showError(
615
+ `<strong>Prediction Failed!</strong><br>
616
+ <small>Error: ${error.message}<br>
617
+ Please check your input values and try again.</small>`
618
+ );
619
+ } finally {
620
+ // Re-enable predict button
621
+ predictBtn.disabled = false;
622
+ predictBtn.innerHTML = '<i class="fas fa-magic"></i> Predict Delivery Time';
623
+ }
624
+ });
625
+
626
+ // Add input animations
627
+ document.querySelectorAll('input, select').forEach(element => {
628
+ element.addEventListener('focus', function() {
629
+ this.parentElement.style.transform = 'scale(1.02)';
630
+ });
631
+
632
+ element.addEventListener('blur', function() {
633
+ this.parentElement.style.transform = 'scale(1)';
634
+ });
635
+ });
636
+
637
+ // Add form validation feedback
638
+ document.querySelectorAll('input[required], select[required]').forEach(element => {
639
+ element.addEventListener('invalid', function() {
640
+ this.style.borderColor = '#e53e3e';
641
+ this.style.boxShadow = '0 0 0 4px rgba(229, 62, 62, 0.1)';
642
+ });
643
+
644
+ element.addEventListener('input', function() {
645
+ if (this.validity.valid) {
646
+ this.style.borderColor = '#48bb78';
647
+ this.style.boxShadow = '0 0 0 4px rgba(72, 187, 120, 0.1)';
648
+ } else {
649
+ this.style.borderColor = '#e2e8f0';
650
+ this.style.boxShadow = 'none';
651
+ }
652
+ });
653
+ });
654
+
655
+ // Clear results when form is modified
656
+ document.querySelectorAll('input, select').forEach(element => {
657
+ element.addEventListener('input', function() {
658
+ document.getElementById('result').classList.remove('show');
659
+ document.getElementById('error-result').classList.remove('show');
660
+ });
661
+ });
662
+ </script>
663
+ </body>
664
+ </html>