junaid17 commited on
Commit
2bbf568
·
verified ·
1 Parent(s): efafd26

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +563 -563
index.html CHANGED
@@ -1,563 +1,563 @@
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>Customer Segmentation API</title>
7
- <style>
8
- * {
9
- margin: 0;
10
- padding: 0;
11
- box-sizing: border-box;
12
- }
13
-
14
- :root {
15
- --dark-gray: #1a1a1a;
16
- --darker-gray: #0d0d0d;
17
- --light-gray: #2d2d2d;
18
- --accent: #4a90e2;
19
- --accent-hover: #357abd;
20
- --text: #e0e0e0;
21
- --text-secondary: #a0a0a0;
22
- --success: #4caf50;
23
- --error: #f44336;
24
- }
25
-
26
- body {
27
- font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
28
- background: linear-gradient(135deg, var(--darker-gray), var(--dark-gray));
29
- color: var(--text);
30
- min-height: 100vh;
31
- padding: 20px;
32
- line-height: 1.6;
33
- }
34
-
35
- .container {
36
- max-width: 1200px;
37
- margin: 0 auto;
38
- }
39
-
40
- .header {
41
- text-align: center;
42
- margin-bottom: 40px;
43
- animation: fadeInDown 1s ease-out;
44
- }
45
-
46
- .header h1 {
47
- font-size: 2.5rem;
48
- margin-bottom: 10px;
49
- background: linear-gradient(45deg, var(--accent), #66b3ff);
50
- -webkit-background-clip: text;
51
- -webkit-text-fill-color: transparent;
52
- background-clip: text;
53
- }
54
-
55
- .header p {
56
- color: var(--text-secondary);
57
- font-size: 1.1rem;
58
- }
59
-
60
- .main-content {
61
- display: grid;
62
- grid-template-columns: 1fr;
63
- gap: 30px;
64
- }
65
-
66
- @media (min-width: 768px) {
67
- .main-content {
68
- grid-template-columns: 1fr 1fr;
69
- }
70
- }
71
-
72
- .form-section, .result-section {
73
- background: var(--light-gray);
74
- padding: 30px;
75
- border-radius: 15px;
76
- box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3);
77
- animation: slideInUp 0.8s ease-out;
78
- }
79
-
80
- .form-section h2, .result-section h2 {
81
- margin-bottom: 25px;
82
- color: var(--accent);
83
- font-size: 1.8rem;
84
- }
85
-
86
- .form-group {
87
- margin-bottom: 20px;
88
- }
89
-
90
- .form-group label {
91
- display: block;
92
- margin-bottom: 8px;
93
- color: var(--text);
94
- font-weight: 500;
95
- }
96
-
97
- .form-group input {
98
- width: 100%;
99
- padding: 12px 15px;
100
- border: 2px solid #404040;
101
- border-radius: 8px;
102
- background: var(--darker-gray);
103
- color: var(--text);
104
- font-size: 1rem;
105
- transition: all 0.3s ease;
106
- }
107
-
108
- .form-group input:focus {
109
- outline: none;
110
- border-color: var(--accent);
111
- box-shadow: 0 0 0 3px rgba(74, 144, 226, 0.2);
112
- }
113
-
114
- .form-group input:hover {
115
- border-color: #5a9fe4;
116
- }
117
-
118
- .submit-btn {
119
- width: 100%;
120
- padding: 15px;
121
- background: linear-gradient(45deg, var(--accent), #66b3ff);
122
- color: white;
123
- border: none;
124
- border-radius: 8px;
125
- font-size: 1.1rem;
126
- font-weight: 600;
127
- cursor: pointer;
128
- transition: all 0.3s ease;
129
- margin-top: 10px;
130
- }
131
-
132
- .submit-btn:hover {
133
- transform: translateY(-2px);
134
- box-shadow: 0 5px 15px rgba(74, 144, 226, 0.4);
135
- }
136
-
137
- .submit-btn:active {
138
- transform: translateY(0);
139
- }
140
-
141
- .submit-btn:disabled {
142
- background: #555;
143
- cursor: not-allowed;
144
- transform: none;
145
- box-shadow: none;
146
- }
147
-
148
- .result-card {
149
- background: var(--darker-gray);
150
- padding: 25px;
151
- border-radius: 10px;
152
- border-left: 4px solid var(--accent);
153
- margin-bottom: 20px;
154
- animation: fadeIn 0.6s ease-out;
155
- }
156
-
157
- .result-card h3 {
158
- color: var(--accent);
159
- margin-bottom: 10px;
160
- font-size: 1.3rem;
161
- }
162
-
163
- .result-card p {
164
- color: var(--text-secondary);
165
- margin-bottom: 5px;
166
- }
167
-
168
- .loading {
169
- text-align: center;
170
- padding: 20px;
171
- display: none;
172
- }
173
-
174
- .spinner {
175
- border: 4px solid rgba(255, 255, 255, 0.3);
176
- border-radius: 50%;
177
- border-top: 4px solid var(--accent);
178
- width: 40px;
179
- height: 40px;
180
- animation: spin 1s linear infinite;
181
- margin: 0 auto 15px;
182
- }
183
-
184
- .error {
185
- background: rgba(244, 67, 54, 0.1);
186
- border: 1px solid var(--error);
187
- color: var(--error);
188
- padding: 15px;
189
- border-radius: 8px;
190
- margin: 15px 0;
191
- display: none;
192
- }
193
-
194
- .success {
195
- background: rgba(76, 175, 80, 0.1);
196
- border: 1px solid var(--success);
197
- color: var(--success);
198
- padding: 15px;
199
- border-radius: 8px;
200
- margin: 15px 0;
201
- display: none;
202
- }
203
-
204
- @keyframes fadeInDown {
205
- from {
206
- opacity: 0;
207
- transform: translateY(-30px);
208
- }
209
- to {
210
- opacity: 1;
211
- transform: translateY(0);
212
- }
213
- }
214
-
215
- @keyframes slideInUp {
216
- from {
217
- opacity: 0;
218
- transform: translateY(50px);
219
- }
220
- to {
221
- opacity: 1;
222
- transform: translateY(0);
223
- }
224
- }
225
-
226
- @keyframes fadeIn {
227
- from {
228
- opacity: 0;
229
- }
230
- to {
231
- opacity: 1;
232
- }
233
- }
234
-
235
- @keyframes spin {
236
- 0% { transform: rotate(0deg); }
237
- 100% { transform: rotate(360deg); }
238
- }
239
-
240
- @keyframes pulse {
241
- 0% { transform: scale(1); }
242
- 50% { transform: scale(1.05); }
243
- 100% { transform: scale(1); }
244
- }
245
-
246
- .pulse {
247
- animation: pulse 2s infinite;
248
- }
249
-
250
- .mobile-only {
251
- display: block;
252
- }
253
-
254
- @media (min-width: 768px) {
255
- .mobile-only {
256
- display: none;
257
- }
258
- }
259
-
260
- .desktop-only {
261
- display: none;
262
- }
263
-
264
- @media (min-width: 768px) {
265
- .desktop-only {
266
- display: block;
267
- }
268
- }
269
-
270
- .form-grid {
271
- display: grid;
272
- grid-template-columns: 1fr;
273
- gap: 15px;
274
- }
275
-
276
- @media (min-width: 768px) {
277
- .form-grid {
278
- grid-template-columns: 1fr 1fr;
279
- }
280
- }
281
-
282
- .input-pair {
283
- display: grid;
284
- grid-template-columns: 1fr 1fr;
285
- gap: 15px;
286
- }
287
-
288
- @media (max-width: 767px) {
289
- .input-pair {
290
- grid-template-columns: 1fr;
291
- }
292
- }
293
-
294
- .result-highlight {
295
- background: rgba(74, 144, 226, 0.1);
296
- padding: 15px;
297
- border-radius: 8px;
298
- margin: 10px 0;
299
- border: 1px solid var(--accent);
300
- }
301
-
302
- .result-title {
303
- font-size: 1.2rem;
304
- color: var(--accent);
305
- margin-bottom: 5px;
306
- }
307
-
308
- .result-content {
309
- color: var(--text);
310
- }
311
-
312
- .no-result {
313
- text-align: center;
314
- color: var(--text-secondary);
315
- padding: 40px 20px;
316
- font-style: italic;
317
- }
318
-
319
- .animate-in {
320
- animation: fadeIn 0.5s ease-out forwards;
321
- }
322
-
323
- .animate-out {
324
- animation: fadeOut 0.3s ease-out forwards;
325
- }
326
-
327
- @keyframes fadeOut {
328
- from { opacity: 1; }
329
- to { opacity: 0; }
330
- }
331
-
332
- .slide-up {
333
- animation: slideUp 0.5s ease-out;
334
- }
335
-
336
- @keyframes slideUp {
337
- from {
338
- transform: translateY(100%);
339
- opacity: 0;
340
- }
341
- to {
342
- transform: translateY(0);
343
- opacity: 1;
344
- }
345
- }
346
- </style>
347
- </head>
348
- <body>
349
- <div class="container">
350
- <div class="header">
351
- <h1>Customer Segmentation API</h1>
352
- <p>Enter customer data to predict their segment and get personalized recommendations</p>
353
- </div>
354
-
355
- <div class="main-content">
356
- <div class="form-section">
357
- <h2>Customer Data</h2>
358
- <form id="predictionForm">
359
- <div class="form-grid">
360
- <div class="form-group">
361
- <label for="age">Age</label>
362
- <input type="number" id="age" name="age" min="18" max="100" required placeholder="18-100">
363
- </div>
364
- <div class="form-group">
365
- <label for="income">Income</label>
366
- <input type="number" id="income" name="income" min="0" max="200000" required placeholder="0-200000">
367
- </div>
368
- </div>
369
-
370
- <div class="form-group">
371
- <label for="totalSpendings">Total Spendings</label>
372
- <input type="number" id="totalSpendings" name="totalSpendings" min="0" max="5000" required placeholder="0-5000">
373
- </div>
374
-
375
- <div class="input-pair">
376
- <div class="form-group">
377
- <label for="webPurchases">Web Purchases</label>
378
- <input type="number" id="webPurchases" name="webPurchases" min="0" max="100" required placeholder="0-100">
379
- </div>
380
- <div class="form-group">
381
- <label for="storePurchases">Store Purchases</label>
382
- <input type="number" id="storePurchases" name="storePurchases" min="0" max="100" required placeholder="0-100">
383
- </div>
384
- </div>
385
-
386
- <div class="input-pair">
387
- <div class="form-group">
388
- <label for="webVisits">Web Visits/Month</label>
389
- <input type="number" id="webVisits" name="webVisits" min="0" max="50" required placeholder="0-50">
390
- </div>
391
- <div class="form-group">
392
- <label for="recency">Recency (days)</label>
393
- <input type="number" id="recency" name="recency" min="0" max="365" required placeholder="0-365">
394
- </div>
395
- </div>
396
-
397
- <button type="submit" class="submit-btn pulse" id="predictBtn">
398
- Predict Customer Segment
399
- </button>
400
- </form>
401
-
402
- <div class="loading" id="loading">
403
- <div class="spinner"></div>
404
- <p>Analyzing customer data...</p>
405
- </div>
406
-
407
- <div class="error" id="error"></div>
408
- <div class="success" id="success"></div>
409
- </div>
410
-
411
- <div class="result-section">
412
- <h2>Prediction Result</h2>
413
- <div id="resultContent">
414
- <div class="no-result">
415
- <p>Enter customer data and click "Predict Customer Segment" to see results</p>
416
- </div>
417
- </div>
418
- </div>
419
- </div>
420
- </div>
421
-
422
- <script>
423
- document.addEventListener('DOMContentLoaded', function() {
424
- const form = document.getElementById('predictionForm');
425
- const predictBtn = document.getElementById('predictBtn');
426
- const loading = document.getElementById('loading');
427
- const error = document.getElementById('error');
428
- const success = document.getElementById('success');
429
- const resultContent = document.getElementById('resultContent');
430
-
431
- // Sample result template
432
- const resultTemplate = {
433
- "cluster_id": 3,
434
- "cluster_name": "Active Online-Focused Shoppers",
435
- "description": "High income, high spending, shops frequently both online and in-store—with strongest activity on web.",
436
- "recommendation": "Offer premium bundles, omnichannel loyalty rewards (e.g., buy online, pick up in-store + bonus points), and personalized cross-channel recommendations."
437
- };
438
-
439
- form.addEventListener('submit', async function(e) {
440
- e.preventDefault();
441
-
442
- // Show loading
443
- loading.style.display = 'block';
444
- error.style.display = 'none';
445
- success.style.display = 'none';
446
-
447
- // Disable button during prediction
448
- predictBtn.disabled = true;
449
- predictBtn.textContent = 'Analyzing...';
450
-
451
- try {
452
- // Get form data
453
- const formData = new FormData(form);
454
- const inputData = {
455
- Age: parseInt(formData.get('age')),
456
- Income: parseInt(formData.get('income')),
457
- Total_Spendings: parseInt(formData.get('totalSpendings')),
458
- NumWebPurchases: parseInt(formData.get('webPurchases')),
459
- NumStorePurchases: parseInt(formData.get('storePurchases')),
460
- NumWebVisitsMonth: parseInt(formData.get('webVisits')),
461
- Recency: parseInt(formData.get('recency'))
462
- };
463
-
464
- // Make API call
465
- const response = await fetch('http://localhost:8000/predict', {
466
- method: 'POST',
467
- headers: {
468
- 'Content-Type': 'application/json',
469
- },
470
- body: JSON.stringify(inputData)
471
- });
472
-
473
- if (!response.ok) {
474
- throw new Error(`API error: ${response.status}`);
475
- }
476
-
477
- const result = await response.json();
478
-
479
- // Display result
480
- displayResult(result);
481
- success.style.display = 'block';
482
- success.textContent = 'Prediction successful!';
483
-
484
- } catch (err) {
485
- console.error('Prediction error:', err);
486
- error.style.display = 'block';
487
- error.textContent = `Error: ${err.message || 'Failed to get prediction'}`;
488
-
489
- // Also show in result area
490
- resultContent.innerHTML = `
491
- <div class="error" style="display: block;">
492
- <p>Failed to get prediction: ${err.message || 'Unknown error'}</p>
493
- </div>
494
- `;
495
- } finally {
496
- // Hide loading and re-enable button
497
- loading.style.display = 'none';
498
- predictBtn.disabled = false;
499
- predictBtn.textContent = 'Predict Customer Segment';
500
- }
501
- });
502
-
503
- function displayResult(result) {
504
- resultContent.innerHTML = `
505
- <div class="result-card slide-up">
506
- <div class="result-highlight">
507
- <div class="result-title">Cluster ID: ${result.cluster_id}</div>
508
- <div class="result-content"><strong>Name:</strong> ${result.cluster_name}</div>
509
- </div>
510
-
511
- <div class="result-highlight">
512
- <div class="result-title">Description</div>
513
- <div class="result-content">${result.description}</div>
514
- </div>
515
-
516
- <div class="result-highlight">
517
- <div class="result-title">Recommendation</div>
518
- <div class="result-content">${result.recommendation}</div>
519
- </div>
520
- </div>
521
- `;
522
- }
523
-
524
- // Add input validation
525
- const inputs = form.querySelectorAll('input');
526
- inputs.forEach(input => {
527
- input.addEventListener('input', function() {
528
- const min = parseInt(this.min);
529
- const max = parseInt(this.max);
530
- const value = parseInt(this.value);
531
-
532
- if (value < min || value > max) {
533
- this.style.borderColor = 'var(--error)';
534
- } else {
535
- this.style.borderColor = '#404040';
536
- }
537
- });
538
- });
539
-
540
- // Add animation to form elements on focus
541
- inputs.forEach(input => {
542
- input.addEventListener('focus', function() {
543
- this.parentElement.style.transform = 'translateY(-2px)';
544
- });
545
-
546
- input.addEventListener('blur', function() {
547
- this.parentElement.style.transform = 'translateY(0)';
548
- });
549
- });
550
-
551
- // Add smooth scrolling for mobile
552
- if (window.innerWidth <= 768) {
553
- form.addEventListener('submit', function() {
554
- document.querySelector('.result-section').scrollIntoView({
555
- behavior: 'smooth'
556
- });
557
- });
558
- }
559
- });
560
- </script>
561
- </body>
562
- </html>
563
-
 
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>Customer Segmentation API</title>
7
+ <style>
8
+ * {
9
+ margin: 0;
10
+ padding: 0;
11
+ box-sizing: border-box;
12
+ }
13
+
14
+ :root {
15
+ --dark-gray: #1a1a1a;
16
+ --darker-gray: #0d0d0d;
17
+ --light-gray: #2d2d2d;
18
+ --accent: #4a90e2;
19
+ --accent-hover: #357abd;
20
+ --text: #e0e0e0;
21
+ --text-secondary: #a0a0a0;
22
+ --success: #4caf50;
23
+ --error: #f44336;
24
+ }
25
+
26
+ body {
27
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
28
+ background: linear-gradient(135deg, var(--darker-gray), var(--dark-gray));
29
+ color: var(--text);
30
+ min-height: 100vh;
31
+ padding: 20px;
32
+ line-height: 1.6;
33
+ }
34
+
35
+ .container {
36
+ max-width: 1200px;
37
+ margin: 0 auto;
38
+ }
39
+
40
+ .header {
41
+ text-align: center;
42
+ margin-bottom: 40px;
43
+ animation: fadeInDown 1s ease-out;
44
+ }
45
+
46
+ .header h1 {
47
+ font-size: 2.5rem;
48
+ margin-bottom: 10px;
49
+ background: linear-gradient(45deg, var(--accent), #66b3ff);
50
+ -webkit-background-clip: text;
51
+ -webkit-text-fill-color: transparent;
52
+ background-clip: text;
53
+ }
54
+
55
+ .header p {
56
+ color: var(--text-secondary);
57
+ font-size: 1.1rem;
58
+ }
59
+
60
+ .main-content {
61
+ display: grid;
62
+ grid-template-columns: 1fr;
63
+ gap: 30px;
64
+ }
65
+
66
+ @media (min-width: 768px) {
67
+ .main-content {
68
+ grid-template-columns: 1fr 1fr;
69
+ }
70
+ }
71
+
72
+ .form-section, .result-section {
73
+ background: var(--light-gray);
74
+ padding: 30px;
75
+ border-radius: 15px;
76
+ box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3);
77
+ animation: slideInUp 0.8s ease-out;
78
+ }
79
+
80
+ .form-section h2, .result-section h2 {
81
+ margin-bottom: 25px;
82
+ color: var(--accent);
83
+ font-size: 1.8rem;
84
+ }
85
+
86
+ .form-group {
87
+ margin-bottom: 20px;
88
+ }
89
+
90
+ .form-group label {
91
+ display: block;
92
+ margin-bottom: 8px;
93
+ color: var(--text);
94
+ font-weight: 500;
95
+ }
96
+
97
+ .form-group input {
98
+ width: 100%;
99
+ padding: 12px 15px;
100
+ border: 2px solid #404040;
101
+ border-radius: 8px;
102
+ background: var(--darker-gray);
103
+ color: var(--text);
104
+ font-size: 1rem;
105
+ transition: all 0.3s ease;
106
+ }
107
+
108
+ .form-group input:focus {
109
+ outline: none;
110
+ border-color: var(--accent);
111
+ box-shadow: 0 0 0 3px rgba(74, 144, 226, 0.2);
112
+ }
113
+
114
+ .form-group input:hover {
115
+ border-color: #5a9fe4;
116
+ }
117
+
118
+ .submit-btn {
119
+ width: 100%;
120
+ padding: 15px;
121
+ background: linear-gradient(45deg, var(--accent), #66b3ff);
122
+ color: white;
123
+ border: none;
124
+ border-radius: 8px;
125
+ font-size: 1.1rem;
126
+ font-weight: 600;
127
+ cursor: pointer;
128
+ transition: all 0.3s ease;
129
+ margin-top: 10px;
130
+ }
131
+
132
+ .submit-btn:hover {
133
+ transform: translateY(-2px);
134
+ box-shadow: 0 5px 15px rgba(74, 144, 226, 0.4);
135
+ }
136
+
137
+ .submit-btn:active {
138
+ transform: translateY(0);
139
+ }
140
+
141
+ .submit-btn:disabled {
142
+ background: #555;
143
+ cursor: not-allowed;
144
+ transform: none;
145
+ box-shadow: none;
146
+ }
147
+
148
+ .result-card {
149
+ background: var(--darker-gray);
150
+ padding: 25px;
151
+ border-radius: 10px;
152
+ border-left: 4px solid var(--accent);
153
+ margin-bottom: 20px;
154
+ animation: fadeIn 0.6s ease-out;
155
+ }
156
+
157
+ .result-card h3 {
158
+ color: var(--accent);
159
+ margin-bottom: 10px;
160
+ font-size: 1.3rem;
161
+ }
162
+
163
+ .result-card p {
164
+ color: var(--text-secondary);
165
+ margin-bottom: 5px;
166
+ }
167
+
168
+ .loading {
169
+ text-align: center;
170
+ padding: 20px;
171
+ display: none;
172
+ }
173
+
174
+ .spinner {
175
+ border: 4px solid rgba(255, 255, 255, 0.3);
176
+ border-radius: 50%;
177
+ border-top: 4px solid var(--accent);
178
+ width: 40px;
179
+ height: 40px;
180
+ animation: spin 1s linear infinite;
181
+ margin: 0 auto 15px;
182
+ }
183
+
184
+ .error {
185
+ background: rgba(244, 67, 54, 0.1);
186
+ border: 1px solid var(--error);
187
+ color: var(--error);
188
+ padding: 15px;
189
+ border-radius: 8px;
190
+ margin: 15px 0;
191
+ display: none;
192
+ }
193
+
194
+ .success {
195
+ background: rgba(76, 175, 80, 0.1);
196
+ border: 1px solid var(--success);
197
+ color: var(--success);
198
+ padding: 15px;
199
+ border-radius: 8px;
200
+ margin: 15px 0;
201
+ display: none;
202
+ }
203
+
204
+ @keyframes fadeInDown {
205
+ from {
206
+ opacity: 0;
207
+ transform: translateY(-30px);
208
+ }
209
+ to {
210
+ opacity: 1;
211
+ transform: translateY(0);
212
+ }
213
+ }
214
+
215
+ @keyframes slideInUp {
216
+ from {
217
+ opacity: 0;
218
+ transform: translateY(50px);
219
+ }
220
+ to {
221
+ opacity: 1;
222
+ transform: translateY(0);
223
+ }
224
+ }
225
+
226
+ @keyframes fadeIn {
227
+ from {
228
+ opacity: 0;
229
+ }
230
+ to {
231
+ opacity: 1;
232
+ }
233
+ }
234
+
235
+ @keyframes spin {
236
+ 0% { transform: rotate(0deg); }
237
+ 100% { transform: rotate(360deg); }
238
+ }
239
+
240
+ @keyframes pulse {
241
+ 0% { transform: scale(1); }
242
+ 50% { transform: scale(1.05); }
243
+ 100% { transform: scale(1); }
244
+ }
245
+
246
+ .pulse {
247
+ animation: pulse 2s infinite;
248
+ }
249
+
250
+ .mobile-only {
251
+ display: block;
252
+ }
253
+
254
+ @media (min-width: 768px) {
255
+ .mobile-only {
256
+ display: none;
257
+ }
258
+ }
259
+
260
+ .desktop-only {
261
+ display: none;
262
+ }
263
+
264
+ @media (min-width: 768px) {
265
+ .desktop-only {
266
+ display: block;
267
+ }
268
+ }
269
+
270
+ .form-grid {
271
+ display: grid;
272
+ grid-template-columns: 1fr;
273
+ gap: 15px;
274
+ }
275
+
276
+ @media (min-width: 768px) {
277
+ .form-grid {
278
+ grid-template-columns: 1fr 1fr;
279
+ }
280
+ }
281
+
282
+ .input-pair {
283
+ display: grid;
284
+ grid-template-columns: 1fr 1fr;
285
+ gap: 15px;
286
+ }
287
+
288
+ @media (max-width: 767px) {
289
+ .input-pair {
290
+ grid-template-columns: 1fr;
291
+ }
292
+ }
293
+
294
+ .result-highlight {
295
+ background: rgba(74, 144, 226, 0.1);
296
+ padding: 15px;
297
+ border-radius: 8px;
298
+ margin: 10px 0;
299
+ border: 1px solid var(--accent);
300
+ }
301
+
302
+ .result-title {
303
+ font-size: 1.2rem;
304
+ color: var(--accent);
305
+ margin-bottom: 5px;
306
+ }
307
+
308
+ .result-content {
309
+ color: var(--text);
310
+ }
311
+
312
+ .no-result {
313
+ text-align: center;
314
+ color: var(--text-secondary);
315
+ padding: 40px 20px;
316
+ font-style: italic;
317
+ }
318
+
319
+ .animate-in {
320
+ animation: fadeIn 0.5s ease-out forwards;
321
+ }
322
+
323
+ .animate-out {
324
+ animation: fadeOut 0.3s ease-out forwards;
325
+ }
326
+
327
+ @keyframes fadeOut {
328
+ from { opacity: 1; }
329
+ to { opacity: 0; }
330
+ }
331
+
332
+ .slide-up {
333
+ animation: slideUp 0.5s ease-out;
334
+ }
335
+
336
+ @keyframes slideUp {
337
+ from {
338
+ transform: translateY(100%);
339
+ opacity: 0;
340
+ }
341
+ to {
342
+ transform: translateY(0);
343
+ opacity: 1;
344
+ }
345
+ }
346
+ </style>
347
+ </head>
348
+ <body>
349
+ <div class="container">
350
+ <div class="header">
351
+ <h1>Customer Segmentation API</h1>
352
+ <p>Enter customer data to predict their segment and get personalized recommendations</p>
353
+ </div>
354
+
355
+ <div class="main-content">
356
+ <div class="form-section">
357
+ <h2>Customer Data</h2>
358
+ <form id="predictionForm">
359
+ <div class="form-grid">
360
+ <div class="form-group">
361
+ <label for="age">Age</label>
362
+ <input type="number" id="age" name="age" min="18" max="100" required placeholder="18-100">
363
+ </div>
364
+ <div class="form-group">
365
+ <label for="income">Income</label>
366
+ <input type="number" id="income" name="income" min="0" max="200000" required placeholder="0-200000">
367
+ </div>
368
+ </div>
369
+
370
+ <div class="form-group">
371
+ <label for="totalSpendings">Total Spendings</label>
372
+ <input type="number" id="totalSpendings" name="totalSpendings" min="0" max="5000" required placeholder="0-5000">
373
+ </div>
374
+
375
+ <div class="input-pair">
376
+ <div class="form-group">
377
+ <label for="webPurchases">Web Purchases</label>
378
+ <input type="number" id="webPurchases" name="webPurchases" min="0" max="100" required placeholder="0-100">
379
+ </div>
380
+ <div class="form-group">
381
+ <label for="storePurchases">Store Purchases</label>
382
+ <input type="number" id="storePurchases" name="storePurchases" min="0" max="100" required placeholder="0-100">
383
+ </div>
384
+ </div>
385
+
386
+ <div class="input-pair">
387
+ <div class="form-group">
388
+ <label for="webVisits">Web Visits/Month</label>
389
+ <input type="number" id="webVisits" name="webVisits" min="0" max="50" required placeholder="0-50">
390
+ </div>
391
+ <div class="form-group">
392
+ <label for="recency">Recency (days)</label>
393
+ <input type="number" id="recency" name="recency" min="0" max="365" required placeholder="0-365">
394
+ </div>
395
+ </div>
396
+
397
+ <button type="submit" class="submit-btn pulse" id="predictBtn">
398
+ Predict Customer Segment
399
+ </button>
400
+ </form>
401
+
402
+ <div class="loading" id="loading">
403
+ <div class="spinner"></div>
404
+ <p>Analyzing customer data...</p>
405
+ </div>
406
+
407
+ <div class="error" id="error"></div>
408
+ <div class="success" id="success"></div>
409
+ </div>
410
+
411
+ <div class="result-section">
412
+ <h2>Prediction Result</h2>
413
+ <div id="resultContent">
414
+ <div class="no-result">
415
+ <p>Enter customer data and click "Predict Customer Segment" to see results</p>
416
+ </div>
417
+ </div>
418
+ </div>
419
+ </div>
420
+ </div>
421
+
422
+ <script>
423
+ document.addEventListener('DOMContentLoaded', function() {
424
+ const form = document.getElementById('predictionForm');
425
+ const predictBtn = document.getElementById('predictBtn');
426
+ const loading = document.getElementById('loading');
427
+ const error = document.getElementById('error');
428
+ const success = document.getElementById('success');
429
+ const resultContent = document.getElementById('resultContent');
430
+
431
+ // Sample result template
432
+ const resultTemplate = {
433
+ "cluster_id": 3,
434
+ "cluster_name": "Active Online-Focused Shoppers",
435
+ "description": "High income, high spending, shops frequently both online and in-store—with strongest activity on web.",
436
+ "recommendation": "Offer premium bundles, omnichannel loyalty rewards (e.g., buy online, pick up in-store + bonus points), and personalized cross-channel recommendations."
437
+ };
438
+
439
+ form.addEventListener('submit', async function(e) {
440
+ e.preventDefault();
441
+
442
+ // Show loading
443
+ loading.style.display = 'block';
444
+ error.style.display = 'none';
445
+ success.style.display = 'none';
446
+
447
+ // Disable button during prediction
448
+ predictBtn.disabled = true;
449
+ predictBtn.textContent = 'Analyzing...';
450
+
451
+ try {
452
+ // Get form data
453
+ const formData = new FormData(form);
454
+ const inputData = {
455
+ Age: parseInt(formData.get('age')),
456
+ Income: parseInt(formData.get('income')),
457
+ Total_Spendings: parseInt(formData.get('totalSpendings')),
458
+ NumWebPurchases: parseInt(formData.get('webPurchases')),
459
+ NumStorePurchases: parseInt(formData.get('storePurchases')),
460
+ NumWebVisitsMonth: parseInt(formData.get('webVisits')),
461
+ Recency: parseInt(formData.get('recency'))
462
+ };
463
+
464
+ // Make API call
465
+ const response = await fetch('https://junaid17-customer-segmentation-api.hf.space/predict', {
466
+ method: 'POST',
467
+ headers: {
468
+ 'Content-Type': 'application/json',
469
+ },
470
+ body: JSON.stringify(inputData)
471
+ });
472
+
473
+ if (!response.ok) {
474
+ throw new Error(`API error: ${response.status}`);
475
+ }
476
+
477
+ const result = await response.json();
478
+
479
+ // Display result
480
+ displayResult(result);
481
+ success.style.display = 'block';
482
+ success.textContent = 'Prediction successful!';
483
+
484
+ } catch (err) {
485
+ console.error('Prediction error:', err);
486
+ error.style.display = 'block';
487
+ error.textContent = `Error: ${err.message || 'Failed to get prediction'}`;
488
+
489
+ // Also show in result area
490
+ resultContent.innerHTML = `
491
+ <div class="error" style="display: block;">
492
+ <p>Failed to get prediction: ${err.message || 'Unknown error'}</p>
493
+ </div>
494
+ `;
495
+ } finally {
496
+ // Hide loading and re-enable button
497
+ loading.style.display = 'none';
498
+ predictBtn.disabled = false;
499
+ predictBtn.textContent = 'Predict Customer Segment';
500
+ }
501
+ });
502
+
503
+ function displayResult(result) {
504
+ resultContent.innerHTML = `
505
+ <div class="result-card slide-up">
506
+ <div class="result-highlight">
507
+ <div class="result-title">Cluster ID: ${result.cluster_id}</div>
508
+ <div class="result-content"><strong>Name:</strong> ${result.cluster_name}</div>
509
+ </div>
510
+
511
+ <div class="result-highlight">
512
+ <div class="result-title">Description</div>
513
+ <div class="result-content">${result.description}</div>
514
+ </div>
515
+
516
+ <div class="result-highlight">
517
+ <div class="result-title">Recommendation</div>
518
+ <div class="result-content">${result.recommendation}</div>
519
+ </div>
520
+ </div>
521
+ `;
522
+ }
523
+
524
+ // Add input validation
525
+ const inputs = form.querySelectorAll('input');
526
+ inputs.forEach(input => {
527
+ input.addEventListener('input', function() {
528
+ const min = parseInt(this.min);
529
+ const max = parseInt(this.max);
530
+ const value = parseInt(this.value);
531
+
532
+ if (value < min || value > max) {
533
+ this.style.borderColor = 'var(--error)';
534
+ } else {
535
+ this.style.borderColor = '#404040';
536
+ }
537
+ });
538
+ });
539
+
540
+ // Add animation to form elements on focus
541
+ inputs.forEach(input => {
542
+ input.addEventListener('focus', function() {
543
+ this.parentElement.style.transform = 'translateY(-2px)';
544
+ });
545
+
546
+ input.addEventListener('blur', function() {
547
+ this.parentElement.style.transform = 'translateY(0)';
548
+ });
549
+ });
550
+
551
+ // Add smooth scrolling for mobile
552
+ if (window.innerWidth <= 768) {
553
+ form.addEventListener('submit', function() {
554
+ document.querySelector('.result-section').scrollIntoView({
555
+ behavior: 'smooth'
556
+ });
557
+ });
558
+ }
559
+ });
560
+ </script>
561
+ </body>
562
+ </html>
563
+