KillerKing93/Transformers-TextEngine-OpenAPI
0
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>AI Marketplace Demo - Qwen3-4B-Instruct</title>7 <style>8 * {9 margin: 0;10 padding: 0;11 box-sizing: border-box;12 }13 14 body {15 font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;16 background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);17 min-height: 100vh;18 padding: 20px;19 }20 21 .container {22 max-width: 1200px;23 margin: 0 auto;24 background: white;25 border-radius: 15px;26 box-shadow: 0 20px 60px rgba(0,0,0,0.3);27 padding: 30px;28 }29 30 h1 {31 color: #667eea;32 margin-bottom: 10px;33 font-size: 2.5em;34 }35 36 .subtitle {37 color: #666;38 margin-bottom: 30px;39 font-size: 1.1em;40 }41 42 .tabs {43 display: flex;44 gap: 10px;45 margin-bottom: 30px;46 border-bottom: 2px solid #e0e0e0;47 }48 49 .tab {50 padding: 12px 24px;51 cursor: pointer;52 border: none;53 background: none;54 font-size: 16px;55 color: #666;56 border-bottom: 3px solid transparent;57 transition: all 0.3s;58 }59 60 .tab:hover {61 color: #667eea;62 }63 64 .tab.active {65 color: #667eea;66 border-bottom-color: #667eea;67 font-weight: bold;68 }69 70 .tab-content {71 display: none;72 }73 74 .tab-content.active {75 display: block;76 }77 78 .form-group {79 margin-bottom: 20px;80 }81 82 label {83 display: block;84 margin-bottom: 8px;85 color: #333;86 font-weight: 600;87 }88 89 input, select, textarea {90 width: 100%;91 padding: 12px;92 border: 2px solid #e0e0e0;93 border-radius: 8px;94 font-size: 14px;95 transition: border-color 0.3s;96 }97 98 input:focus, select:focus, textarea:focus {99 outline: none;100 border-color: #667eea;101 }102 103 textarea {104 resize: vertical;105 min-height: 100px;106 }107 108 button {109 background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);110 color: white;111 padding: 12px 30px;112 border: none;113 border-radius: 8px;114 font-size: 16px;115 font-weight: 600;116 cursor: pointer;117 transition: transform 0.2s, box-shadow 0.2s;118 }119 120 button:hover {121 transform: translateY(-2px);122 box-shadow: 0 5px 15px rgba(102, 126, 234, 0.4);123 }124 125 button:active {126 transform: translateY(0);127 }128 129 .response {130 margin-top: 20px;131 padding: 20px;132 border-radius: 8px;133 background: #f8f9fa;134 border-left: 4px solid #667eea;135 }136 137 .response h3 {138 color: #667eea;139 margin-bottom: 10px;140 }141 142 .response pre {143 background: #2d2d2d;144 color: #f8f8f2;145 padding: 15px;146 border-radius: 5px;147 overflow-x: auto;148 font-size: 13px;149 line-height: 1.5;150 }151 152 .error {153 border-left-color: #e74c3c;154 }155 156 .error h3 {157 color: #e74c3c;158 }159 160 .product-card {161 background: white;162 border: 2px solid #e0e0e0;163 border-radius: 8px;164 padding: 15px;165 margin-bottom: 15px;166 }167 168 .product-card h4 {169 color: #667eea;170 margin-bottom: 10px;171 }172 173 .product-info {174 display: grid;175 grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));176 gap: 10px;177 margin-top: 10px;178 }179 180 .info-item {181 color: #666;182 font-size: 14px;183 }184 185 .info-item strong {186 color: #333;187 }188 189 .loading {190 display: none;191 text-align: center;192 padding: 20px;193 color: #667eea;194 }195 196 .loading.active {197 display: block;198 }199 200 .spinner {201 border: 4px solid #f3f3f3;202 border-top: 4px solid #667eea;203 border-radius: 50%;204 width: 40px;205 height: 40px;206 animation: spin 1s linear infinite;207 margin: 0 auto 10px;208 }209 210 @keyframes spin {211 0% { transform: rotate(0deg); }212 100% { transform: rotate(360deg); }213 }214 215 .config {216 background: #fff3cd;217 padding: 15px;218 border-radius: 8px;219 margin-bottom: 20px;220 border-left: 4px solid #ffc107;221 }222 223 .config input {224 margin-top: 5px;225 }226 </style>227</head>228<body>229 <div class="container">230 <h1>๐ค AI Marketplace Demo</h1>231 <p class="subtitle">Test marketplace API endpoints with Qwen3-4B-Instruct</p>232 233 <div class="config">234 <label>API Base URL:</label>235 <input type="text" id="apiBase" value="http://localhost:3000" placeholder="http://localhost:3000">236 </div>237 238 <div class="tabs">239 <button class="tab active" onclick="switchTab('search')">AI Search</button>240 <button class="tab" onclick="switchTab('products')">Products</button>241 <button class="tab" onclick="switchTab('suppliers')">Suppliers</button>242 <button class="tab" onclick="switchTab('users')">Users</button>243 </div>244 245 <!-- AI Search Tab -->246 <div id="search" class="tab-content active">247 <h2>๐ AI-Powered Product Search</h2>248 <p style="color: #666; margin-bottom: 20px;">Natural language product recommendations with location awareness</p>249 250 <div class="form-group">251 <label>User ID (must have AI access enabled):</label>252 <input type="number" id="searchUserId" value="1" placeholder="1">253 </div>254 255 <div class="form-group">256 <label>Query (in Indonesian or English):</label>257 <textarea id="searchQuery" placeholder="Saya butuh laptop gaming di Jakarta, budget 12 juta">laptop gaming Jakarta budget 12 juta</textarea>258 </div>259 260 <button onclick="aiSearch()">๐ Search with AI</button>261 262 <div id="searchLoading" class="loading">263 <div class="spinner"></div>264 <p>AI is analyzing your query...</p>265 </div>266 267 <div id="searchResponse"></div>268 </div>269 270 <!-- Products Tab -->271 <div id="products" class="tab-content">272 <h2>๐ฆ Product Management</h2>273 274 <h3 style="margin-top: 30px; color: #667eea;">List Products</h3>275 <div class="form-group">276 <label>Category (optional):</label>277 <input type="text" id="listCategory" placeholder="laptop, smartphone, etc.">278 </div>279 <button onclick="listProducts()">List Products</button>280 281 <h3 style="margin-top: 30px; color: #667eea;">Search Products (Location-Aware)</h3>282 <div class="form-group">283 <label>Search Query:</label>284 <input type="text" id="productQuery" placeholder="gaming laptop" value="laptop">285 </div>286 <div class="form-group">287 <label>User Latitude (for distance sorting):</label>288 <input type="number" step="any" id="userLat" placeholder="-6.2088" value="-6.2088">289 </div>290 <div class="form-group">291 <label>User Longitude:</label>292 <input type="number" step="any" id="userLon" placeholder="106.8456" value="106.8456">293 </div>294 <button onclick="searchProducts()">Search Products</button>295 296 <div id="productsLoading" class="loading">297 <div class="spinner"></div>298 <p>Loading products...</p>299 </div>300 301 <div id="productsResponse"></div>302 </div>303 304 <!-- Suppliers Tab -->305 <div id="suppliers" class="tab-content">306 <h2>๐ช Supplier Management</h2>307 308 <h3 style="margin-top: 30px; color: #667eea;">Register Supplier</h3>309 <div class="form-group">310 <label>Name:</label>311 <input type="text" id="supplierName" placeholder="John Doe">312 </div>313 <div class="form-group">314 <label>Business Name:</label>315 <input type="text" id="businessName" placeholder="Tech Store Jakarta">316 </div>317 <div class="form-group">318 <label>Email:</label>319 <input type="email" id="supplierEmail" placeholder="john@techstore.com">320 </div>321 <div class="form-group">322 <label>City:</label>323 <input type="text" id="supplierCity" placeholder="Jakarta">324 </div>325 <div class="form-group">326 <label>Latitude:</label>327 <input type="number" step="any" id="supplierLat" placeholder="-6.2088" value="-6.2088">328 </div>329 <div class="form-group">330 <label>Longitude:</label>331 <input type="number" step="any" id="supplierLon" placeholder="106.8456" value="106.8456">332 </div>333 <button onclick="registerSupplier()">Register Supplier</button>334 335 <h3 style="margin-top: 30px; color: #667eea;">List Suppliers</h3>336 <button onclick="listSuppliers()">List All Suppliers</button>337 338 <div id="suppliersLoading" class="loading">339 <div class="spinner"></div>340 <p>Loading suppliers...</p>341 </div>342 343 <div id="suppliersResponse"></div>344 </div>345 346 <!-- Users Tab -->347 <div id="users" class="tab-content">348 <h2>๐ค User Management</h2>349 350 <h3 style="margin-top: 30px; color: #667eea;">Register User</h3>351 <div class="form-group">352 <label>Name:</label>353 <input type="text" id="userName" placeholder="Jane Smith">354 </div>355 <div class="form-group">356 <label>Email:</label>357 <input type="email" id="userEmail" placeholder="jane@email.com">358 </div>359 <div class="form-group">360 <label>City:</label>361 <input type="text" id="userCity" placeholder="Jakarta">362 </div>363 <div class="form-group">364 <label>Latitude:</label>365 <input type="number" step="any" id="userLatReg" placeholder="-6.2088" value="-6.2088">366 </div>367 <div class="form-group">368 <label>Longitude:</label>369 <input type="number" step="any" id="userLonReg" placeholder="106.8456" value="106.8456">370 </div>371 <div class="form-group">372 <label>373 <input type="checkbox" id="aiAccess" checked style="width: auto; margin-right: 5px;">374 Enable AI Access (Premium Feature)375 </label>376 </div>377 <button onclick="registerUser()">Register User</button>378 379 <div id="usersLoading" class="loading">380 <div class="spinner"></div>381 <p>Processing...</p>382 </div>383 384 <div id="usersResponse"></div>385 </div>386 </div>387 388 <script>389 const getApiBase = () => document.getElementById('apiBase').value;390 391 function switchTab(tabName) {392 // Hide all tabs393 document.querySelectorAll('.tab-content').forEach(content => {394 content.classList.remove('active');395 });396 document.querySelectorAll('.tab').forEach(tab => {397 tab.classList.remove('active');398 });399 400 // Show selected tab401 document.getElementById(tabName).classList.add('active');402 event.target.classList.add('active');403 }404 405 function showLoading(id) {406 document.getElementById(id).classList.add('active');407 }408 409 function hideLoading(id) {410 document.getElementById(id).classList.remove('active');411 }412 413 function showResponse(id, data, isError = false) {414 const responseDiv = document.getElementById(id);415 const className = isError ? 'response error' : 'response';416 responseDiv.innerHTML = `417 <div class="${className}">418 <h3>${isError ? 'โ Error' : 'โ
Success'}</h3>419 <pre>${JSON.stringify(data, null, 2)}</pre>420 </div>421 `;422 }423 424 async function aiSearch() {425 const userId = document.getElementById('searchUserId').value;426 const query = document.getElementById('searchQuery').value;427 428 if (!userId || !query) {429 alert('Please fill in all fields');430 return;431 }432 433 showLoading('searchLoading');434 document.getElementById('searchResponse').innerHTML = '';435 436 try {437 const response = await fetch(`${getApiBase()}/api/chat/search`, {438 method: 'POST',439 headers: { 'Content-Type': 'application/json' },440 body: JSON.stringify({ user_id: parseInt(userId), query })441 });442 443 const data = await response.json();444 hideLoading('searchLoading');445 446 if (response.ok) {447 showResponse('searchResponse', data);448 } else {449 showResponse('searchResponse', data, true);450 }451 } catch (error) {452 hideLoading('searchLoading');453 showResponse('searchResponse', { error: error.message }, true);454 }455 }456 457 async function listProducts() {458 const category = document.getElementById('listCategory').value;459 const url = category460 ? `${getApiBase()}/api/products?category=${encodeURIComponent(category)}`461 : `${getApiBase()}/api/products`;462 463 showLoading('productsLoading');464 document.getElementById('productsResponse').innerHTML = '';465 466 try {467 const response = await fetch(url);468 const data = await response.json();469 hideLoading('productsLoading');470 471 if (response.ok) {472 showResponse('productsResponse', data);473 } else {474 showResponse('productsResponse', data, true);475 }476 } catch (error) {477 hideLoading('productsLoading');478 showResponse('productsResponse', { error: error.message }, true);479 }480 }481 482 async function searchProducts() {483 const q = document.getElementById('productQuery').value;484 const lat = document.getElementById('userLat').value;485 const lon = document.getElementById('userLon').value;486 487 if (!q) {488 alert('Please enter a search query');489 return;490 }491 492 const params = new URLSearchParams({ q });493 if (lat) params.append('user_lat', lat);494 if (lon) params.append('user_lon', lon);495 496 showLoading('productsLoading');497 document.getElementById('productsResponse').innerHTML = '';498 499 try {500 const response = await fetch(`${getApiBase()}/api/products/search?${params}`);501 const data = await response.json();502 hideLoading('productsLoading');503 504 if (response.ok) {505 showResponse('productsResponse', data);506 } else {507 showResponse('productsResponse', data, true);508 }509 } catch (error) {510 hideLoading('productsLoading');511 showResponse('productsResponse', { error: error.message }, true);512 }513 }514 515 async function registerSupplier() {516 const data = {517 name: document.getElementById('supplierName').value,518 business_name: document.getElementById('businessName').value,519 email: document.getElementById('supplierEmail').value,520 city: document.getElementById('supplierCity').value,521 latitude: parseFloat(document.getElementById('supplierLat').value),522 longitude: parseFloat(document.getElementById('supplierLon').value)523 };524 525 if (!data.name || !data.business_name || !data.email || !data.city) {526 alert('Please fill in all required fields');527 return;528 }529 530 showLoading('suppliersLoading');531 document.getElementById('suppliersResponse').innerHTML = '';532 533 try {534 const response = await fetch(`${getApiBase()}/api/suppliers/register`, {535 method: 'POST',536 headers: { 'Content-Type': 'application/json' },537 body: JSON.stringify(data)538 });539 540 const result = await response.json();541 hideLoading('suppliersLoading');542 543 if (response.ok) {544 showResponse('suppliersResponse', result);545 } else {546 showResponse('suppliersResponse', result, true);547 }548 } catch (error) {549 hideLoading('suppliersLoading');550 showResponse('suppliersResponse', { error: error.message }, true);551 }552 }553 554 async function listSuppliers() {555 showLoading('suppliersLoading');556 document.getElementById('suppliersResponse').innerHTML = '';557 558 try {559 const response = await fetch(`${getApiBase()}/api/suppliers`);560 const data = await response.json();561 hideLoading('suppliersLoading');562 563 if (response.ok) {564 showResponse('suppliersResponse', data);565 } else {566 showResponse('suppliersResponse', data, true);567 }568 } catch (error) {569 hideLoading('suppliersLoading');570 showResponse('suppliersResponse', { error: error.message }, true);571 }572 }573 574 async function registerUser() {575 const data = {576 name: document.getElementById('userName').value,577 email: document.getElementById('userEmail').value,578 city: document.getElementById('userCity').value,579 latitude: parseFloat(document.getElementById('userLatReg').value),580 longitude: parseFloat(document.getElementById('userLonReg').value),581 ai_access_enabled: document.getElementById('aiAccess').checked582 };583 584 if (!data.name || !data.email || !data.city) {585 alert('Please fill in all required fields');586 return;587 }588 589 showLoading('usersLoading');590 document.getElementById('usersResponse').innerHTML = '';591 592 try {593 const response = await fetch(`${getApiBase()}/api/users/register`, {594 method: 'POST',595 headers: { 'Content-Type': 'application/json' },596 body: JSON.stringify(data)597 });598 599 const result = await response.json();600 hideLoading('usersLoading');601 602 if (response.ok) {603 showResponse('usersResponse', result);604 } else {605 showResponse('usersResponse', result, true);606 }607 } catch (error) {608 hideLoading('usersLoading');609 showResponse('usersResponse', { error: error.message }, true);610 }611 }612 </script>613</body>614</html>615 