khy_admin/admin.html

728 lines
28 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Product Management - KHY Admin</title>
<script src="https://cdn.tailwindcss.com"></script>
<style>
.admin-container {
max-width: 1200px;
margin: 0 auto;
padding: 20px;
}
.product-card {
border: 1px solid #e5e7eb;
border-radius: 8px;
padding: 16px;
margin-bottom: 16px;
}
.form-group {
margin-bottom: 16px;
}
.form-group label {
display: block;
margin-bottom: 4px;
font-weight: 600;
}
.form-group input,
.form-group textarea,
.form-group select {
width: 100%;
padding: 8px;
border: 1px solid #d1d5db;
border-radius: 4px;
}
.btn {
padding: 8px 16px;
border-radius: 4px;
cursor: pointer;
border: none;
}
.btn-primary {
background: #3b82f6;
color: white;
}
.btn-danger {
background: #ef4444;
color: white;
}
.btn-success {
background: #10b981;
color: white;
}
.color-input {
width: 60px;
height: 40px;
border: none;
border-radius: 4px;
}
.image-preview {
width: 100px;
height: 100px;
object-fit: cover;
border-radius: 4px;
}
</style>
</head>
<body class="bg-gray-50">
<div class="admin-container">
<h1 class="text-3xl font-bold mb-8">Product Management</h1>
<!-- Add New Product Form -->
<div class="bg-white p-6 rounded-lg shadow mb-8">
<h2 class="text-xl font-semibold mb-4">Add New Product</h2>
<!-- Preview Section -->
<div
id="previewSection"
class="hidden mb-6 p-4 bg-gray-50 rounded-lg border"
>
<h3 class="font-semibold mb-3">Preview</h3>
<div id="previewContent"></div>
</div>
<form id="productForm">
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div class="form-group">
<label>Product Name</label>
<input type="text" id="productName" required />
</div>
<div class="form-group">
<label>Category</label>
<select id="productCategory" required>
<option value="seating">Seating</option>
<option value="tables">Tables</option>
<option value="storage">Storage</option>
<option value="workspace">Workspace</option>
</select>
</div>
<div class="form-group">
<label>Price</label>
<input type="number" id="productPrice" step="0.01" required />
</div>
<div class="form-group">
<label>Model Number</label>
<input type="text" id="productModel" />
</div>
<div class="form-group">
<label>Short Description</label>
<textarea id="productDescription" rows="2"></textarea>
</div>
<div class="form-group">
<label>Main Image URL</label>
<input
type="text"
id="productImage"
placeholder="assets/images/product.jpg"
/>
</div>
<div class="form-group">
<label>In Stock</label>
<select id="productInStock">
<option value="true">Yes</option>
<option value="false">No</option>
</select>
</div>
<div class="form-group">
<label>Rating</label>
<input
type="number"
id="productRating"
step="0.1"
min="0"
max="5"
value="4.0"
/>
</div>
</div>
<div class="mt-4">
<h3 class="font-semibold mb-2">Available Sizes</h3>
<div id="sizesContainer">
<div class="flex gap-2 mb-2">
<input
type="text"
placeholder="Size (e.g., S, M, L)"
class="size-input"
/>
<button
type="button"
onclick="removeSize(this)"
class="btn btn-danger"
>
Remove
</button>
</div>
</div>
<button type="button" onclick="addSize()" class="btn btn-primary">
Add Size
</button>
</div>
<div class="mt-4">
<h3 class="font-semibold mb-2">Available Colors</h3>
<div id="colorsContainer">
<div class="flex gap-2 mb-2 items-center">
<input
type="text"
placeholder="Color Name"
class="color-name"
/>
<input type="color" class="color-input" value="#000000" />
<button
type="button"
onclick="removeColor(this)"
class="btn btn-danger"
>
Remove
</button>
</div>
</div>
<button type="button" onclick="addColor()" class="btn btn-primary">
Add Color
</button>
</div>
<div class="mt-6 flex gap-4">
<button type="submit" class="btn btn-success">Add Product</button>
<button
type="button"
onclick="previewProduct()"
class="btn btn-primary"
>
Preview Product
</button>
<button
type="button"
onclick="cancelEdit()"
class="btn btn-danger hidden"
id="cancelEditBtn"
>
Cancel Edit
</button>
</div>
</form>
</div>
<!-- Products List -->
<div class="bg-white p-6 rounded-lg shadow">
<div class="flex justify-between items-center mb-4">
<h2 class="text-xl font-semibold">Current Products</h2>
<div class="flex gap-2">
<button onclick="previewWebsite()" class="btn btn-primary">
👀 Preview Website
</button>
<button onclick="downloadProductsJSON()" class="btn btn-primary">
Download products.json
</button>
<button onclick="deployToProduction()" class="btn btn-success">
🚀 Copy to Main Website
</button>
</div>
</div>
<div id="productsList"></div>
</div>
</div>
<script>
let products = [];
// Load products on page load
async function loadProducts() {
try {
const response = await fetch("data/products.json");
const data = await response.json();
products = data.products;
displayProducts();
} catch (error) {
console.error("Error loading products:", error);
alert("Error loading products. Please refresh the page.");
}
}
// Display products
function displayProducts() {
const container = document.getElementById("productsList");
container.innerHTML = "";
products.forEach((product, index) => {
const productCard = document.createElement("div");
productCard.className = "product-card";
productCard.innerHTML = `
<div class="flex justify-between items-start">
<div class="flex gap-4">
<img src="${product.image}" alt="${product.name}" class="image-preview" onerror="this.src='assets/images/placeholder.jpg'">
<div>
<h3 class="font-semibold">${product.name}</h3>
<p class="text-gray-600">${product.category}$${product.price}</p>
<p class="text-sm text-gray-500">${product.description}</p>
</div>
</div>
<div class="flex gap-2">
<button onclick="editProduct(${index})" class="btn btn-primary">Edit</button>
<button onclick="deleteProduct(${index})" class="btn btn-danger">Delete</button>
</div>
</div>
`;
container.appendChild(productCard);
});
}
// Add new product or update existing
document
.getElementById("productForm")
.addEventListener("submit", function (e) {
e.preventDefault();
const sizes = Array.from(document.querySelectorAll(".size-input"))
.map((input) => input.value)
.filter((size) => size.trim());
const colors = Array.from(document.querySelectorAll(".color-name"))
.map((input, index) => {
const colorInput =
input.parentElement.querySelector(".color-input");
return {
name: input.value,
value: colorInput.value,
selected: index === 0,
};
})
.filter((color) => color.name.trim());
const productData = {
name: document.getElementById("productName").value,
description: document.getElementById("productDescription").value,
image: document.getElementById("productImage").value,
alt: document.getElementById("productName").value,
category: document.getElementById("productCategory").value,
modelNo: document.getElementById("productModel").value,
tags: [document.getElementById("productCategory").value],
sizes: sizes,
colors: colors,
selectedSize: sizes[0] || "M",
selectedColor: colors[0]?.name || "Default",
price: parseFloat(document.getElementById("productPrice").value),
originalPrice: parseFloat(
document.getElementById("productPrice").value
),
rating: document.getElementById("productRating").value,
reviews: 0,
inStock: document.getElementById("productInStock").value === "true",
images: [document.getElementById("productImage").value],
descriptionLong: [
document.getElementById("productDescription").value,
],
additionalInformation: {
Material: "Premium materials",
Dimensions: "See size options",
Warranty: "3 years",
},
reviewsCount: 0,
galleryPairs: [
{
left: document.getElementById("productImage").value,
right: document.getElementById("productImage").value,
},
],
dimensions: "See size options",
salesPackage: "1 unit",
configuration: "Standard",
fillingMaterial: "High-density foam",
finishType: "Premium finish",
adjustableHeadrest: "No",
maxLoadCapacity: "150kg",
originOfManufacture: "Ghana",
weight: "25kg",
seatHeight: "45cm",
legHeight: "10cm",
warrantyServiceType: "Standard warranty service",
coveredInWarranty: "Manufacturing defects",
notCoveredInWarranty: "Wear and tear not covered",
};
const form = document.getElementById("productForm");
const editIndex = form.dataset.editIndex;
if (editIndex !== undefined) {
// Update existing product
productData.id = products[editIndex].id;
products[editIndex] = productData;
alert("Product updated successfully!");
// Reset form to add mode
form.dataset.editIndex = "";
form.querySelector("button[type='submit']").textContent =
"Add Product";
} else {
// Add new product
productData.id = Math.max(...products.map((p) => p.id)) + 1;
products.push(productData);
alert("Product added successfully!");
}
displayProducts();
document.getElementById("productForm").reset();
hidePreview();
});
// Helper functions
function addSize() {
const container = document.getElementById("sizesContainer");
const div = document.createElement("div");
div.className = "flex gap-2 mb-2";
div.innerHTML = `
<input type="text" placeholder="Size (e.g., S, M, L)" class="size-input">
<button type="button" onclick="removeSize(this)" class="btn btn-danger">Remove</button>
`;
container.appendChild(div);
}
function removeSize(button) {
button.parentElement.remove();
}
function addColor() {
const container = document.getElementById("colorsContainer");
const div = document.createElement("div");
div.className = "flex gap-2 mb-2 items-center";
div.innerHTML = `
<input type="text" placeholder="Color Name" class="color-name">
<input type="color" class="color-input" value="#000000">
<button type="button" onclick="removeColor(this)" class="btn btn-danger">Remove</button>
`;
container.appendChild(div);
}
function removeColor(button) {
button.parentElement.remove();
}
function editProduct(index) {
const product = products[index];
// Populate form with existing product data
document.getElementById("productName").value = product.name;
document.getElementById("productDescription").value =
product.description;
document.getElementById("productImage").value = product.image;
document.getElementById("productCategory").value = product.category;
document.getElementById("productPrice").value = product.price;
document.getElementById("productModel").value = product.modelNo || "";
document.getElementById("productInStock").value =
product.inStock.toString();
document.getElementById("productRating").value = product.rating;
// Clear existing sizes and colors
document.getElementById("sizesContainer").innerHTML = "";
document.getElementById("colorsContainer").innerHTML = "";
// Add existing sizes
product.sizes.forEach((size) => {
addSize();
const sizeInputs = document.querySelectorAll(".size-input");
sizeInputs[sizeInputs.length - 1].value = size;
});
// Add existing colors
product.colors.forEach((color) => {
addColor();
const colorInputs = document.querySelectorAll(".color-name");
const colorPickers = document.querySelectorAll(".color-input");
colorInputs[colorInputs.length - 1].value = color.name;
colorPickers[colorPickers.length - 1].value = color.value;
});
// Change form to edit mode
const form = document.getElementById("productForm");
form.dataset.editIndex = index;
form.querySelector("button[type='submit']").textContent =
"Update Product";
document.getElementById("cancelEditBtn").classList.remove("hidden");
// Scroll to form
form.scrollIntoView({ behavior: "smooth" });
// Show preview
showPreview(product);
}
function deleteProduct(index) {
const product = products[index];
const confirmMessage = `Are you sure you want to delete "${product.name}"?\n\nThis action cannot be undone.`;
if (confirm(confirmMessage)) {
products.splice(index, 1);
displayProducts();
alert(`"${product.name}" has been deleted successfully!`);
}
}
function previewProduct() {
const sizes = Array.from(document.querySelectorAll(".size-input"))
.map((input) => input.value)
.filter((size) => size.trim());
const colors = Array.from(document.querySelectorAll(".color-name"))
.map((input, index) => {
const colorInput =
input.parentElement.querySelector(".color-input");
return {
name: input.value,
value: colorInput.value,
selected: index === 0,
};
})
.filter((color) => color.name.trim());
const previewProduct = {
name: document.getElementById("productName").value || "Product Name",
description:
document.getElementById("productDescription").value ||
"Product description",
image:
document.getElementById("productImage").value ||
"assets/images/placeholder.jpg",
category:
document.getElementById("productCategory").value || "seating",
price: document.getElementById("productPrice").value || "0",
modelNo: document.getElementById("productModel").value || "",
sizes: sizes,
colors: colors,
inStock: document.getElementById("productInStock").value === "true",
rating: document.getElementById("productRating").value || "4.0",
};
showPreview(previewProduct);
}
function showPreview(product) {
const previewSection = document.getElementById("previewSection");
const previewContent = document.getElementById("previewContent");
previewContent.innerHTML = `
<div class="flex gap-4">
<img src="${product.image}" alt="${
product.name
}" class="w-24 h-24 object-cover rounded" onerror="this.src='assets/images/placeholder.jpg'">
<div class="flex-1">
<h4 class="font-semibold text-lg">${product.name}</h4>
<p class="text-gray-600">${product.category}$${
product.price
}</p>
<p class="text-sm text-gray-500 mb-2">${product.description}</p>
<div class="text-xs text-gray-400">
<p><strong>Model:</strong> ${product.modelNo || "N/A"}</p>
<p><strong>Sizes:</strong> ${
product.sizes.length > 0 ? product.sizes.join(", ") : "None"
}</p>
<p><strong>Colors:</strong> ${
product.colors.length > 0
? product.colors.map((c) => c.name).join(", ")
: "None"
}</p>
<p><strong>In Stock:</strong> ${
product.inStock ? "Yes" : "No"
}</p>
<p><strong>Rating:</strong> ${product.rating}/5</p>
</div>
</div>
</div>
`;
previewSection.classList.remove("hidden");
previewSection.scrollIntoView({ behavior: "smooth", block: "nearest" });
}
function hidePreview() {
const previewSection = document.getElementById("previewSection");
previewSection.classList.add("hidden");
}
function cancelEdit() {
const form = document.getElementById("productForm");
form.dataset.editIndex = "";
form.querySelector("button[type='submit']").textContent = "Add Product";
document.getElementById("cancelEditBtn").classList.add("hidden");
document.getElementById("productForm").reset();
hidePreview();
}
function downloadProductsJSON() {
const data = {
products: products,
categories: [
{
id: "seating",
name: "Seating",
description:
"Office chairs, lounge chairs, and seating solutions",
},
{
id: "tables",
name: "Tables",
description: "Conference tables, workstations, and dining tables",
},
{
id: "storage",
name: "Storage",
description:
"Storage units, lockers, and organizational solutions",
},
{
id: "workspace",
name: "Workspace",
description: "Pods, phone booths, and collaborative spaces",
},
],
pagination: {
itemsPerPage: 16,
totalItems: products.length,
currentPage: 1,
totalPages: Math.ceil(products.length / 16),
},
};
const blob = new Blob([JSON.stringify(data, null, 2)], {
type: "application/json",
});
const url = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = url;
a.download = "products.json";
a.click();
URL.revokeObjectURL(url);
alert(`Downloaded products.json with ${products.length} products!`);
}
function previewWebsite() {
const baseUrl =
window.location.origin +
window.location.pathname.replace("admin.html", "");
const previewMessage = `Preview Website\n\nThis will help you preview how your products will look on the website.\n\nChoose your preferred method:\n\nOption 1: Auto-start server (Recommended)\n- Click OK to get instructions for running: npm run preview\n- This will start the server and open all preview pages automatically\n\nOption 2: Open preview pages now\n- Click Cancel to open preview pages in current browser\n- Make sure you have a server running first`;
if (confirm(previewMessage)) {
// Show instructions for auto-start
alert(
`🚀 Auto-Start Preview Server\n\nTo automatically start the server and open preview pages:\n\n1. Open Terminal/Command Prompt\n2. Navigate to the admin folder\n3. Run: npm run preview\n\nThis will:\n- Start the local server\n- Open admin dashboard\n- Open homepage preview\n- Open product catalog preview\n- Open product details preview\n\nAll in one command! 🎉`
);
} else {
// Open preview pages in current browser
const pages = [
{ url: "index.html", name: "Homepage" },
{ url: "product-catalog.html", name: "Product Catalog" },
{ url: "product-detail.html", name: "Product Details" },
];
pages.forEach((page, index) => {
setTimeout(() => {
window.open(page.url, `_blank`);
}, index * 500); // Stagger opening to avoid popup blockers
});
// Show helpful message
setTimeout(() => {
alert(
`👀 Preview Pages Opened\n\nOpened in new tabs:\n- Homepage\n- Product Catalog\n- Product Details\n\nIf tabs didn't open, check your popup blocker settings.\n\n💡 For the best experience, run: npm run preview`
);
}, 2000);
}
}
function deployToProduction() {
const confirmMessage = `Copy to Main Website\n\nThis will copy your updated product data to the main KHY website.\n\nAre you sure you want to proceed?\n\nThis will:\n- Stop the preview server (if running)\n- Copy admin/data/products.json → ../data/products.json\n- Copy admin/assets/images/ → ../assets/images/\n- Create a backup of current main website data\n- Update the main KHY website immediately\n\nMake sure you've previewed your changes first!`;
if (confirm(confirmMessage)) {
// Show deployment instructions
alert(
`Copy Instructions\n\nTo copy your changes to the main KHY website:\n\n1. Open Terminal/Command Prompt\n2. Navigate to the admin folder\n3. Run: npm run deploy\n\nThis will:\n- Automatically stop the preview server\n- Copy admin/data/products.json → main website data/products.json\n- Copy admin/assets/images/ → main website assets/images/\n\nYour main KHY website will be updated immediately!\n\nThe preview server will be automatically stopped during deployment.\n\nNote: If this is your first time, run 'npm run setup' first to configure the deploy paths.`
);
}
}
// Server management
let previewTabs = [];
// Function to kill preview server
function killPreviewServer() {
// This would need to be implemented with a backend endpoint
// For now, we'll show instructions
alert(
`Stop Preview Server\n\nTo stop the preview server:\n\n1. Open Terminal/Command Prompt\n2. Navigate to the admin folder\n3. Run: npm run preview:kill\n\nOr press Ctrl+C in the terminal where the server is running.`
);
}
// Function to track preview tabs
function trackPreviewTab(tab) {
previewTabs.push(tab);
// Check if tab is closed
const checkClosed = setInterval(() => {
if (tab.closed) {
previewTabs = previewTabs.filter((t) => t !== tab);
clearInterval(checkClosed);
// If no preview tabs are open, offer to kill server
if (previewTabs.length === 0) {
setTimeout(() => {
if (
confirm(
`All Preview Tabs Closed\n\nAll preview tabs have been closed.\n\nWould you like to stop the preview server?\n\nThis will free up the port for other uses.`
)
) {
killPreviewServer();
}
}, 1000);
}
}
}, 1000);
}
// Enhanced preview function with tab tracking
function previewWebsiteWithTracking() {
const baseUrl =
window.location.origin +
window.location.pathname.replace("admin.html", "");
const previewMessage = `Preview Website\n\nThis will help you preview how your products will look on the website.\n\nChoose your preferred method:\n\nOption 1: Auto-start server (Recommended)\n- Click OK to get instructions for running: npm run preview\n- This will start the server and open all preview pages automatically\n\nOption 2: Open preview pages now\n- Click Cancel to open preview pages in current browser\n- Make sure you have a server running first`;
if (confirm(previewMessage)) {
// Show instructions for auto-start
alert(
`Auto-Start Preview Server\n\nTo automatically start the server and open preview pages:\n\n1. Open Terminal/Command Prompt\n2. Navigate to the admin folder\n3. Run: npm run preview\n\nThis will:\n- Start the local server\n- Open admin dashboard\n- Open homepage preview\n- Open product catalog preview\n- Open product details preview\n\nAll in one command!\n\nThe server will automatically stop when you close preview tabs or deploy changes.`
);
} else {
// Open preview pages in current browser with tracking
const pages = [
{ url: "index.html", name: "Homepage" },
{ url: "product-catalog.html", name: "Product Catalog" },
{ url: "product-detail.html", name: "Product Details" },
];
pages.forEach((page, index) => {
setTimeout(() => {
const tab = window.open(page.url, `_blank`);
if (tab) {
trackPreviewTab(tab);
}
}, index * 500); // Stagger opening to avoid popup blockers
});
// Show helpful message
setTimeout(() => {
alert(
`Preview Pages Opened\n\nOpened in new tabs:\n- Homepage\n- Product Catalog\n- Product Details\n\nIf tabs didn't open, check your popup blocker settings.\n\nFor the best experience, run: npm run preview\n\nThe server will be automatically stopped when you close all preview tabs.`
);
}, 2000);
}
}
// Override the original preview function
window.previewWebsite = previewWebsiteWithTracking;
// Load products when page loads
loadProducts();
</script>
</body>
</html>