| <!DOCTYPE html> |
| <html lang="en"> |
| <head> |
| <meta charset="UTF-8"> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| <title>Deraah Product API Tester</title> |
| <script src="https://cdn.tailwindcss.com"></script> |
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> |
| <style> |
| .fade-in { |
| animation: fadeIn 0.3s ease-in-out; |
| } |
| |
| @keyframes fadeIn { |
| from { opacity: 0; transform: translateY(10px); } |
| to { opacity: 1; transform: translateY(0); } |
| } |
| |
| .product-card { |
| transition: all 0.3s ease; |
| } |
| |
| .product-card:hover { |
| transform: translateY(-5px); |
| box-shadow: 0 10px 20px rgba(0, 0, 0, 0.1); |
| } |
| |
| .input-focus { |
| transition: all 0.3s ease; |
| } |
| |
| .input-focus:focus { |
| border-color: #8b5cf6; |
| box-shadow: 0 0 0 3px rgba(139, 92, 246, 0.2); |
| } |
| </style> |
| </head> |
| <body class="bg-gray-50 min-h-screen"> |
| <div class="container mx-auto px-4 py-12"> |
| <div class="max-w-3xl mx-auto"> |
| |
| <div class="text-center mb-10"> |
| <h1 class="text-4xl font-bold text-purple-800 mb-2">Deraah Product API Tester</h1> |
| <p class="text-lg text-gray-600">Enter a SKU to fetch live product information</p> |
| </div> |
| |
| |
| <div class="bg-white rounded-xl shadow-md p-6 mb-8"> |
| <form id="productForm" class="space-y-4"> |
| <div> |
| <label for="sku" class="block text-sm font-medium text-gray-700 mb-1">Product SKU</label> |
| <div class="relative"> |
| <input |
| type="text" |
| id="sku" |
| name="sku" |
| placeholder="e.g. 10411131442000-8050" |
| class="w-full px-4 py-3 rounded-lg border border-gray-300 input-focus focus:outline-none" |
| required |
| > |
| <div class="absolute inset-y-0 right-0 flex items-center pr-3 pointer-events-none hidden" id="loadingSpinner"> |
| <i class="fas fa-spinner fa-spin text-purple-600"></i> |
| </div> |
| </div> |
| <p class="mt-1 text-sm text-gray-500">Enter a valid product SKU</p> |
| </div> |
| |
| <button |
| type="submit" |
| class="w-full bg-purple-600 hover:bg-purple-700 text-white font-medium py-3 px-4 rounded-lg transition duration-300 flex items-center justify-center" |
| > |
| <i class="fas fa-search mr-2"></i> Fetch Product |
| </button> |
| </form> |
| </div> |
| |
| |
| <div id="errorAlert" class="hidden bg-red-50 border-l-4 border-red-500 p-4 mb-8 rounded-lg"> |
| <div class="flex"> |
| <div class="flex-shrink-0"> |
| <i class="fas fa-exclamation-circle text-red-500"></i> |
| </div> |
| <div class="ml-3"> |
| <p class="text-sm text-red-700" id="errorMessage">Product not found. Please check the SKU and try again.</p> |
| </div> |
| </div> |
| </div> |
| |
| |
| <div id="authErrorAlert" class="hidden bg-yellow-50 border-l-4 border-yellow-500 p-4 mb-8 rounded-lg"> |
| <div class="flex"> |
| <div class="flex-shrink-0"> |
| <i class="fas fa-exclamation-triangle text-yellow-500"></i> |
| </div> |
| <div class="ml-3"> |
| <p class="text-sm text-yellow-700" id="authErrorMessage">Authentication required. Please contact support.</p> |
| </div> |
| </div> |
| </div> |
| |
| |
| <div id="productContainer" class="hidden"> |
| <div class="product-card bg-white rounded-xl shadow-md overflow-hidden fade-in"> |
| <div class="md:flex"> |
| <div class="md:flex-shrink-0 md:w-1/3"> |
| <img id="productImage" class="h-48 w-full object-cover md:h-full" src="" alt="Product image"> |
| </div> |
| <div class="p-8 md:w-2/3"> |
| <div class="flex justify-between items-start"> |
| <div> |
| <h2 id="productName" class="text-2xl font-bold text-gray-800 mb-2"></h2> |
| <span id="availabilityBadge" class="inline-flex items-center px-3 py-1 rounded-full text-sm font-medium hidden"> |
| <span class="w-2 h-2 mr-2 rounded-full bg-green-500"></span> |
| Available |
| </span> |
| </div> |
| <div id="productPrice" class="text-xl font-semibold text-purple-700"></div> |
| </div> |
| <p id="productDescription" class="mt-4 text-gray-600"></p> |
| |
| <div class="mt-6"> |
| <div class="flex items-center"> |
| <span class="text-gray-500 mr-2">SKU:</span> |
| <span id="productSku" class="font-medium text-gray-700"></span> |
| </div> |
| </div> |
| </div> |
| </div> |
| </div> |
| </div> |
| </div> |
| </div> |
|
|
| <script> |
| document.addEventListener('DOMContentLoaded', function() { |
| const form = document.getElementById('productForm'); |
| const errorAlert = document.getElementById('errorAlert'); |
| const authErrorAlert = document.getElementById('authErrorAlert'); |
| const productContainer = document.getElementById('productContainer'); |
| const loadingSpinner = document.getElementById('loadingSpinner'); |
| |
| form.addEventListener('submit', async function(e) { |
| e.preventDefault(); |
| |
| const sku = document.getElementById('sku').value.trim(); |
| |
| if (!sku) { |
| showError('Please enter a SKU'); |
| return; |
| } |
| |
| |
| loadingSpinner.classList.remove('hidden'); |
| |
| errorAlert.classList.add('hidden'); |
| authErrorAlert.classList.add('hidden'); |
| productContainer.classList.add('hidden'); |
| |
| try { |
| |
| const response = await fetch(`/api/products`, { |
| method: 'POST', |
| headers: { |
| 'Content-Type': 'application/json', |
| 'Accept': 'application/json' |
| }, |
| body: JSON.stringify({ sku: sku }) |
| }); |
| |
| |
| if (response.status === 401 || response.status === 403) { |
| throw new Error('AUTH_REQUIRED'); |
| } |
| |
| |
| const contentType = response.headers.get('content-type'); |
| if (!contentType || !contentType.includes('application/json')) { |
| const text = await response.text(); |
| if (text.includes('<html') || text.includes('<!DOCTYPE html')) { |
| throw new Error('AUTH_REQUIRED'); |
| } |
| throw new Error('Invalid response format from server'); |
| } |
| |
| const productData = await response.json(); |
| |
| if (!response.ok || productData.error) { |
| throw new Error(productData.message || 'Failed to fetch product'); |
| } |
| |
| if (!productData || Object.keys(productData).length === 0) { |
| throw new Error('Product not found'); |
| } |
| |
| |
| displayProduct(productData); |
| } catch (error) { |
| console.error('Fetch error:', error); |
| if (error.message === 'AUTH_REQUIRED') { |
| showAuthError('Authentication required. Please contact support.'); |
| } else { |
| showError(error.message || 'An error occurred while fetching product data. Please try again.'); |
| } |
| } finally { |
| loadingSpinner.classList.add('hidden'); |
| } |
| }); |
| |
| function showError(message) { |
| document.getElementById('errorMessage').textContent = message; |
| errorAlert.classList.remove('hidden'); |
| productContainer.classList.add('hidden'); |
| authErrorAlert.classList.add('hidden'); |
| } |
| |
| function showAuthError(message) { |
| document.getElementById('authErrorMessage').textContent = message; |
| authErrorAlert.classList.remove('hidden'); |
| productContainer.classList.add('hidden'); |
| errorAlert.classList.add('hidden'); |
| } |
| |
| function displayProduct(product) { |
| |
| document.getElementById('productName').textContent = product.name || 'No name available'; |
| document.getElementById('productDescription').textContent = product.shortDescription || product.description || 'No description available'; |
| |
| |
| if (product.price && product.currency) { |
| const formattedPrice = new Intl.NumberFormat(undefined, { |
| style: 'currency', |
| currency: product.currency |
| }).format(product.price); |
| document.getElementById('productPrice').textContent = formattedPrice; |
| } else if (product.price) { |
| document.getElementById('productPrice').textContent = `$${product.price.toFixed(2)}`; |
| } else { |
| document.getElementById('productPrice').textContent = 'Price not available'; |
| } |
| |
| document.getElementById('productSku').textContent = product.sku || product.id || ''; |
| |
| |
| const productImage = document.getElementById('productImage'); |
| if (product.images && product.images.length > 0) { |
| productImage.src = product.images[0]; |
| productImage.alt = product.name || 'Product image'; |
| } else if (product.image) { |
| productImage.src = product.image; |
| productImage.alt = product.name || 'Product image'; |
| } else { |
| productImage.src = 'https://via.placeholder.com/400x300?text=No+Image+Available'; |
| productImage.alt = 'No image available'; |
| } |
| |
| |
| const availabilityBadge = document.getElementById('availabilityBadge'); |
| if (product.available || product.inStock) { |
| availabilityBadge.classList.remove('hidden'); |
| } else { |
| availabilityBadge.classList.add('hidden'); |
| } |
| |
| |
| productContainer.classList.remove('hidden'); |
| } |
| }); |
| </script> |
| <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=vdimsa/api-test" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> |
| </html> |