api-test / index.html
vdimsa's picture
Add 3 files
1affa63 verified
Raw
History Blame Contribute Delete
13.5 kB
<!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">
<!-- Header -->
<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>
<!-- Search Form -->
<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>
<!-- Error Message -->
<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>
<!-- Authentication Error -->
<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>
<!-- Product Card -->
<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;
}
// Show loading spinner
loadingSpinner.classList.remove('hidden');
// Hide previous results and errors
errorAlert.classList.add('hidden');
authErrorAlert.classList.add('hidden');
productContainer.classList.add('hidden');
try {
// Call our backend API route
const response = await fetch(`/api/products`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json'
},
body: JSON.stringify({ sku: sku })
});
// Check response status first
if (response.status === 401 || response.status === 403) {
throw new Error('AUTH_REQUIRED');
}
// Check if response is JSON
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');
}
// Display product data
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) {
// Set product details
document.getElementById('productName').textContent = product.name || 'No name available';
document.getElementById('productDescription').textContent = product.shortDescription || product.description || 'No description available';
// Format price with currency
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 || '';
// Set product image
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';
}
// Set availability
const availabilityBadge = document.getElementById('availabilityBadge');
if (product.available || product.inStock) {
availabilityBadge.classList.remove('hidden');
} else {
availabilityBadge.classList.add('hidden');
}
// Show product container
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>