pvanand commited on
Commit
2f09e0e
·
verified ·
1 Parent(s): 4890167

Update static/ui/index.html

Browse files
Files changed (1) hide show
  1. static/ui/index.html +164 -273
static/ui/index.html CHANGED
@@ -1,295 +1,186 @@
1
- <!doctype html>
2
- <html class="dark">
3
  <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no" />
6
- <meta name="description" content="Discover amazing ML apps made by the community" />
7
-
8
- <!-- Existing meta tags remain the same -->
9
-
10
- <title>AI Web Scraper Chat - Hugging Face Space</title>
11
-
12
- <!-- Custom CSS -->
13
  <style>
14
- :root {
15
- --primary-color: #4F46E5;
16
- --secondary-color: #6B7280;
17
- --background-color: #1F2937;
18
- --text-color: #F9FAFB;
19
- --border-color: #374151;
20
- --hover-color: #6366F1;
21
- }
22
-
23
- /* Base styles */
24
  body {
25
- font-family: 'Source Sans Pro', -apple-system, BlinkMacSystemFont, sans-serif;
26
- background-color: var(--background-color);
27
- color: var(--text-color);
28
- margin: 0;
29
- padding: 0;
30
- min-height: 100vh;
31
- display: flex;
32
- flex-direction: column;
33
- }
34
-
35
- /* Header styles */
36
- .main-header {
37
- background: linear-gradient(to bottom, #2D3748, var(--background-color));
38
- padding: 1rem 2rem;
39
- box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
40
- position: sticky;
41
- top: 0;
42
- z-index: 100;
43
- }
44
-
45
- .header-container {
46
- max-width: 1200px;
47
- margin: 0 auto;
48
- display: flex;
49
- justify-content: space-between;
50
- align-items: center;
51
- }
52
-
53
- .logo-section {
54
- display: flex;
55
- align-items: center;
56
- gap: 1rem;
57
- }
58
-
59
- .logo {
60
- width: 40px;
61
- height: 40px;
62
- }
63
-
64
- .site-title {
65
- font-size: 1.5rem;
66
- font-weight: 600;
67
- color: var(--text-color);
68
- text-decoration: none;
69
- }
70
-
71
- /* Navigation styles */
72
- .main-nav {
73
- display: flex;
74
- gap: 2rem;
75
- align-items: center;
76
- }
77
-
78
- .nav-link {
79
- color: var(--text-color);
80
- text-decoration: none;
81
- padding: 0.5rem 1rem;
82
- border-radius: 4px;
83
- transition: all 0.2s ease;
84
- }
85
-
86
- .nav-link:hover {
87
- background-color: var(--hover-color);
88
- }
89
-
90
- /* Button styles */
91
- .btn {
92
- background-color: var(--primary-color);
93
- color: var(--text-color);
94
- padding: 0.75rem 1.5rem;
95
- border: none;
96
- border-radius: 6px;
97
- font-weight: 600;
98
- cursor: pointer;
99
- transition: all 0.2s ease;
100
- }
101
-
102
- .btn:hover {
103
- background-color: var(--hover-color);
104
- transform: translateY(-1px);
105
- box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
106
- }
107
-
108
- /* Main content area */
109
- .main-content {
110
- flex: 1;
111
- padding: 2rem;
112
- max-width: 1200px;
113
- margin: 0 auto;
114
- width: 100%;
115
- }
116
-
117
- /* Iframe container */
118
- .space-iframe-container {
119
- background-color: #ffffff;
120
- border-radius: 12px;
121
- box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
122
- overflow: hidden;
123
- margin-top: 2rem;
124
- }
125
-
126
- .space-iframe {
127
- width: 100%;
128
- height: calc(100vh - 200px);
129
- border: none;
130
- }
131
-
132
- /* Loading spinner */
133
- .spinner-overlay {
134
- position: fixed;
135
- top: 0;
136
- left: 0;
137
- right: 0;
138
- bottom: 0;
139
- background-color: rgba(0, 0, 0, 0.7);
140
- display: flex;
141
- justify-content: center;
142
- align-items: center;
143
- z-index: 1000;
144
  }
145
-
146
- .spinner {
147
- width: 40px;
148
- height: 40px;
149
- border: 4px solid #f3f3f3;
150
- border-top: 4px solid var(--primary-color);
151
- border-radius: 50%;
152
- animation: spin 1s linear infinite;
153
  }
154
-
155
- /* Back to top button */
156
- .back-to-top {
157
- position: fixed;
158
- bottom: 2rem;
159
- right: 2rem;
160
- background-color: var(--primary-color);
161
- color: var(--text-color);
162
- width: 40px;
163
- height: 40px;
164
- border-radius: 50%;
165
- display: flex;
166
- justify-content: center;
167
- align-items: center;
168
- cursor: pointer;
169
  opacity: 0;
170
- transition: opacity 0.3s ease;
171
- border: none;
172
  }
173
-
174
- .back-to-top.visible {
175
- opacity: 1;
176
  }
177
-
178
- /* Mobile styles */
179
- @media (max-width: 768px) {
180
- .main-nav {
181
- display: none;
182
- }
183
-
184
- .mobile-menu-btn {
185
- display: block;
186
- }
187
-
188
- .main-content {
189
- padding: 1rem;
190
- }
191
-
192
- .space-iframe {
193
- height: calc(100vh - 150px);
194
- }
195
  }
196
-
197
- /* Animations */
198
- @keyframes spin {
199
- 0% { transform: rotate(0deg); }
200
- 100% { transform: rotate(360deg); }
201
  }
202
  </style>
203
  </head>
204
- <body>
205
- <!-- Header -->
206
- <header class="main-header">
207
- <div class="header-container">
208
- <div class="logo-section">
209
- <img src="/front/assets/huggingface_logo-noborder.svg" alt="Hugging Face Logo" class="logo">
210
- <a href="/" class="site-title">AI Web Scraper Chat</a>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
211
  </div>
212
-
213
- <nav class="main-nav">
214
- <a href="#" class="nav-link">Home</a>
215
- <a href="#" class="nav-link">Documentation</a>
216
- <a href="#" class="nav-link">Community</a>
217
- <button class="btn">Get Started</button>
218
- </nav>
219
-
220
- <button class="mobile-menu-btn" aria-label="Toggle mobile menu">
221
- <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
222
- <path d="M3 12h18M3 6h18M3 18h18"></path>
223
- </svg>
224
- </button>
225
- </div>
226
- </header>
227
-
228
- <!-- Main content -->
229
- <main class="main-content">
230
- <div class="space-iframe-container">
231
- <iframe
232
- src="https://elevatics-ai-web-scraper-chat.hf.space/"
233
- class="space-iframe"
234
- allow="accelerometer; ambient-light-sensor; autoplay; battery; camera; clipboard-read; clipboard-write; display-capture; document-domain; encrypted-media; fullscreen; geolocation; gyroscope; layout-animations; legacy-image-formats; magnetometer; microphone; midi; oversized-images; payment; picture-in-picture; publickey-credentials-get; sync-xhr; usb; vr; wake-lock; xr-spatial-tracking"
235
- sandbox="allow-downloads allow-forms allow-modals allow-pointer-lock allow-popups allow-popups-to-escape-sandbox allow-same-origin allow-scripts allow-storage-access-by-user-activation"
236
- scrolling="no">
237
- </iframe>
238
- </div>
239
- </main>
240
 
241
- <!-- Loading spinner -->
242
- <div class="spinner-overlay" id="loading-spinner" style="display: none;">
243
- <div class="spinner"></div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
244
  </div>
245
 
246
- <!-- Back to top button -->
247
- <button class="back-to-top" id="backToTop" aria-label="Back to top">
248
- <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
249
- <path d="M12 19V5M5 12l7-7 7 7"></path>
250
- </svg>
251
- </button>
252
-
253
- <!-- JavaScript -->
254
  <script>
255
- // Loading spinner
256
- window.addEventListener('load', () => {
257
- document.getElementById('loading-spinner').style.display = 'none';
258
- });
259
-
260
- // Back to top button
261
- const backToTopButton = document.getElementById('backToTop');
262
 
263
- window.addEventListener('scroll', () => {
264
- if (window.scrollY > 300) {
265
- backToTopButton.classList.add('visible');
266
- } else {
267
- backToTopButton.classList.remove('visible');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
268
  }
269
- });
270
-
271
- backToTopButton.addEventListener('click', () => {
272
- window.scrollTo({ top: 0, behavior: 'smooth' });
273
- });
274
-
275
- // Mobile menu
276
- const mobileMenuBtn = document.querySelector('.mobile-menu-btn');
277
- const mainNav = document.querySelector('.main-nav');
278
-
279
- mobileMenuBtn.addEventListener('click', () => {
280
- mainNav.style.display = mainNav.style.display === 'flex' ? 'none' : 'flex';
281
- });
282
-
283
- // Responsive iframe height
284
- function adjustIframeHeight() {
285
- const iframe = document.querySelector('.space-iframe');
286
- const viewportHeight = window.innerHeight;
287
- const headerHeight = document.querySelector('.main-header').offsetHeight;
288
- iframe.style.height = `${viewportHeight - headerHeight - 40}px`;
289
- }
290
-
291
- window.addEventListener('resize', adjustIframeHeight);
292
- adjustIframeHeight();
293
  </script>
294
  </body>
295
  </html>
 
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>Electronics Store</title>
7
+ <script src="https://unpkg.com/vue@3.3.4/dist/vue.global.prod.js"></script>
8
+ <link href="https://cdn.jsdelivr.net/npm/tailwindcss@2.2.19/dist/tailwind.min.css" rel="stylesheet">
9
+ <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
 
 
 
10
  <style>
 
 
 
 
 
 
 
 
 
 
11
  body {
12
+ font-family: 'Inter', sans-serif;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
13
  }
14
+ .fade-enter-active, .fade-leave-active {
15
+ transition: opacity 0.3s ease;
 
 
 
 
 
 
16
  }
17
+ .fade-enter-from, .fade-leave-to {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
18
  opacity: 0;
 
 
19
  }
20
+ .product-card {
21
+ transform: translateY(0);
22
+ transition: all 0.3s ease;
23
  }
24
+ .product-card:hover {
25
+ transform: translateY(-5px);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
26
  }
27
+ .stock-badge {
28
+ transition: all 0.3s ease;
 
 
 
29
  }
30
  </style>
31
  </head>
32
+ <body class="bg-gray-50">
33
+ <div id="app" class="min-h-screen">
34
+ <!-- Header -->
35
+ <header class="bg-white shadow-sm">
36
+ <div class="container mx-auto px-4 py-6">
37
+ <h1 class="text-4xl font-bold text-gray-900">Electronics Store</h1>
38
+ </div>
39
+ </header>
40
+
41
+ <main class="container mx-auto px-4 py-8">
42
+ <!-- Filters Section -->
43
+ <div class="bg-white rounded-xl shadow-sm p-6 mb-8">
44
+ <div class="flex flex-col md:flex-row gap-4 items-center justify-between">
45
+ <div class="flex gap-4 w-full md:w-auto">
46
+ <select v-model="selectedCategory"
47
+ class="w-full md:w-48 p-2.5 border border-gray-200 rounded-lg bg-gray-50 focus:ring-2 focus:ring-blue-500 focus:border-blue-500">
48
+ <option value="">All Categories</option>
49
+ <option v-for="category in categories" :key="category" :value="category">
50
+ {{ category }}
51
+ </option>
52
+ </select>
53
+
54
+ <select v-model="sortBy"
55
+ class="w-full md:w-48 p-2.5 border border-gray-200 rounded-lg bg-gray-50 focus:ring-2 focus:ring-blue-500 focus:border-blue-500">
56
+ <option value="price-asc">Price: Low to High</option>
57
+ <option value="price-desc">Price: High to Low</option>
58
+ <option value="rating">Top Rated</option>
59
+ </select>
60
+ </div>
61
+
62
+ <div class="text-sm text-gray-500">
63
+ {{ filteredProducts.length }} products found
64
+ </div>
65
+ </div>
66
  </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
67
 
68
+ <!-- Products Grid -->
69
+ <transition-group name="fade" tag="div"
70
+ class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6">
71
+ <div v-for="product in filteredProducts"
72
+ :key="product.id"
73
+ class="product-card bg-white rounded-xl shadow-sm overflow-hidden">
74
+
75
+ <div class="p-6">
76
+ <!-- Product Category Badge -->
77
+ <div class="inline-block px-3 py-1 rounded-full text-xs font-medium bg-blue-50 text-blue-600 mb-4">
78
+ {{ product.category }}
79
+ </div>
80
+
81
+ <!-- Product Name -->
82
+ <h2 class="text-xl font-semibold text-gray-900 mb-4">{{ product.name }}</h2>
83
+
84
+ <!-- Specifications -->
85
+ <div class="space-y-3 mb-6">
86
+ <div v-for="(value, key) in product.specs"
87
+ :key="key"
88
+ class="flex justify-between text-sm">
89
+ <span class="text-gray-500">{{ key }}</span>
90
+ <span class="text-gray-900 font-medium">{{ value }}</span>
91
+ </div>
92
+ </div>
93
+
94
+ <!-- Price and Rating -->
95
+ <div class="flex items-center justify-between pt-4 border-t border-gray-100">
96
+ <div class="text-2xl font-bold text-gray-900">
97
+ ${{ product.price.toLocaleString() }}
98
+ </div>
99
+ <div class="flex items-center gap-1">
100
+ <span class="text-yellow-400 text-lg">★</span>
101
+ <span class="font-medium">{{ product.rating }}</span>
102
+ </div>
103
+ </div>
104
+
105
+ <!-- Stock Status -->
106
+ <div class="mt-4">
107
+ <span class="stock-badge px-3 py-1 rounded-full text-sm font-medium"
108
+ :class="{
109
+ 'bg-red-50 text-red-600': product.stock < 10,
110
+ 'bg-green-50 text-green-600': product.stock >= 10
111
+ }">
112
+ {{ product.stock }} in stock
113
+ </span>
114
+ </div>
115
+ </div>
116
+ </div>
117
+ </transition-group>
118
+
119
+ <!-- Empty State -->
120
+ <div v-if="filteredProducts.length === 0"
121
+ class="text-center py-12">
122
+ <p class="text-gray-500">No products found matching your criteria</p>
123
+ </div>
124
+ </main>
125
  </div>
126
 
 
 
 
 
 
 
 
 
127
  <script>
128
+ const { createApp, ref, computed } = Vue
 
 
 
 
 
 
129
 
130
+ createApp({
131
+ setup() {
132
+ const products = ref([])
133
+ const selectedCategory = ref('')
134
+ const sortBy = ref('price-asc')
135
+
136
+ // Fetch products from API
137
+ fetch('/api/electronics/products')
138
+ .then(response => response.json())
139
+ .then(data => {
140
+ products.value = data.products || []
141
+ })
142
+ .catch(error => {
143
+ console.error('Error fetching products:', error)
144
+ })
145
+
146
+ const categories = computed(() => {
147
+ if (!products.value?.length) return []
148
+ return [...new Set(products.value.map(p => p.category))]
149
+ })
150
+
151
+ const filteredProducts = computed(() => {
152
+ if (!products.value?.length) return []
153
+
154
+ let filtered = [...products.value]
155
+
156
+ if (selectedCategory.value) {
157
+ filtered = filtered.filter(p => p.category === selectedCategory.value)
158
+ }
159
+
160
+ switch (sortBy.value) {
161
+ case 'price-asc':
162
+ filtered.sort((a, b) => a.price - b.price)
163
+ break
164
+ case 'price-desc':
165
+ filtered.sort((a, b) => b.price - a.price)
166
+ break
167
+ case 'rating':
168
+ filtered.sort((a, b) => b.rating - a.rating)
169
+ break
170
+ }
171
+
172
+ return filtered
173
+ })
174
+
175
+ return {
176
+ products,
177
+ categories,
178
+ selectedCategory,
179
+ sortBy,
180
+ filteredProducts
181
+ }
182
  }
183
+ }).mount('#app')
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
184
  </script>
185
  </body>
186
  </html>