MerchFlow-AI / dashboard.html
Gaurav vashistha
Restore full brain logic and fix dashboard URL
d288e3a
<!DOCTYPE html>
<html class="dark" lang="en">
<head>
<meta charset="utf-8" />
<meta content="width=device-width, initial-scale=1.0" name="viewport" />
<title>MerchFlow AI Dashboard</title>
<script src="https://cdn.tailwindcss.com?plugins=forms,container-queries"></script>
<link href="https://fonts.googleapis.com" rel="preconnect" />
<link crossorigin="" href="https://fonts.gstatic.com" rel="preconnect" />
<link href="https://fonts.googleapis.com/css2?family=Spline+Sans:wght@300;400;500;600;700&amp;display=swap"
rel="stylesheet" />
<link
href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:wght,FILL@100..700,0..1&amp;display=swap"
rel="stylesheet" />
<script id="tailwind-config">
tailwind.config = {
darkMode: "class",
theme: {
extend: {
colors: {
"primary": "#3b82f6", // Electric Blue
"primary-hover": "#2563eb",
"secondary": "#6366f1", // Indigo accent
"background-light": "#f8fafc",
"background-dark": "#0f172a", // Deep Slate
"surface-dark": "#1e293b", // Slate 800
"surface-darker": "#020617", // Slate 950
"border-dark": "#334155", // Slate 700
},
fontFamily: {
"display": ["Spline Sans", "sans-serif"],
"mono": ["monospace"]
},
borderRadius: { "DEFAULT": "1rem", "lg": "2rem", "xl": "3rem", "full": "9999px" },
},
},
}
</script>
<style>
.custom-scrollbar::-webkit-scrollbar {
width: 8px;
height: 8px;
}
.custom-scrollbar::-webkit-scrollbar-track {
background: #020617;
}
.custom-scrollbar::-webkit-scrollbar-thumb {
background: #334155;
border-radius: 4px;
}
.custom-scrollbar::-webkit-scrollbar-thumb:hover {
background: #3b82f6;
}
</style>
</head>
<body
class="font-display bg-background-light dark:bg-background-dark text-slate-900 dark:text-white antialiased overflow-hidden h-screen flex flex-col">
<header
class="flex-none flex items-center justify-between whitespace-nowrap border-b border-solid border-border-dark px-6 py-4 bg-background-dark z-10">
<div class="flex items-center gap-3 text-white">
<div class="flex items-center justify-center size-10 rounded-full bg-primary/10 text-primary">
<span class="material-symbols-outlined text-2xl">all_inclusive</span>
</div>
<div>
<h2 class="text-white text-xl font-bold leading-tight tracking-tight">MerchFlow AI</h2>
<span class="text-xs text-primary/60 font-medium uppercase tracking-wider">Enterprise Edition</span>
</div>
</div>
<button id="deployBtn"
class="flex min-w-[84px] cursor-pointer items-center justify-center overflow-hidden rounded-full h-10 px-6 bg-primary hover:bg-primary-hover transition-colors text-white text-sm font-bold leading-normal tracking-[0.015em]">
<span class="material-symbols-outlined mr-2 text-lg">rocket_launch</span>
<span class="truncate">Deploy</span>
</button>
</header>
<main class="flex-1 flex overflow-hidden">
<div
class="flex-1 flex flex-col border-r border-border-dark min-w-[400px] overflow-y-auto custom-scrollbar p-8">
<div class="max-w-2xl w-full mx-auto flex flex-col gap-6 h-full">
<div class="flex items-center justify-between">
<h2 class="text-white tracking-light text-[28px] font-bold leading-tight">Input Data</h2>
<span
class="bg-surface-dark text-white px-3 py-1 rounded-full text-xs font-medium border border-border-dark">Step
1 of 2</span>
</div>
<div class="flex flex-col flex-1 max-h-[300px] min-h-[200px]">
<input type="file" id="fileInput" class="hidden" accept=".jpg,.jpeg,.png,.webp" />
<div id="dropZone"
class="group relative flex flex-col items-center justify-center gap-4 rounded-xl border-2 border-dashed border-slate-600 hover:border-primary/50 hover:bg-surface-dark transition-all cursor-pointer h-full w-full px-6 py-8">
<!-- Initial content populated by JS -->
<div
class="size-16 rounded-full bg-surface-dark group-hover:bg-primary/20 flex items-center justify-center transition-colors border border-border-dark group-hover:border-primary/30">
<span class="material-symbols-outlined text-3xl text-primary">cloud_upload</span>
</div>
<div class="flex flex-col items-center gap-1">
<p class="text-white text-lg font-bold leading-tight tracking-tight text-center">Drop
Product Image Here</p>
<p class="text-slate-400 text-sm font-normal text-center">Supports JPG, PNG, WEBP</p>
</div>
<button id="browseBtn"
class="mt-2 flex items-center justify-center rounded-full h-9 px-4 bg-slate-700 hover:bg-slate-600 text-white text-xs font-bold transition-colors">
Browse Files
</button>
<div
class="absolute inset-0 bg-gradient-to-b from-transparent to-black/10 pointer-events-none rounded-xl">
</div>
</div>
</div>
<div class="flex flex-col gap-3 flex-1">
<label class="flex items-center justify-between">
<span class="text-white text-base font-medium">Raw Product Specs</span>
<span class="text-xs text-slate-400">JSON or Plain Text</span>
</label>
<div class="relative flex-1">
<textarea
class="form-input w-full h-full resize-none rounded-xl text-white placeholder:text-slate-500 focus:outline-0 focus:ring-2 focus:ring-primary/50 border border-border-dark bg-surface-dark p-4 text-base font-normal leading-relaxed font-mono"
placeholder="Enter fabric details, dimensions, and SKU...
Example:
Material: 100% Recycled Polyester
Fit: Regular
SKU: JK-2024-WTR"></textarea>
</div>
</div>
<div class="pt-2">
<button id="startBtn"
class="group relative w-full cursor-pointer overflow-hidden rounded-xl h-16 bg-gradient-to-r from-primary to-blue-700 hover:from-blue-500 hover:to-primary transition-all shadow-[0_0_20px_rgba(59,130,246,0.3)]">
<div class="absolute inset-0 flex items-center justify-center gap-3">
<span
class="text-white text-lg font-bold tracking-wide group-hover:scale-105 transition-transform">Start
Agent Workflow</span>
<span
class="material-symbols-outlined text-white group-hover:translate-x-1 transition-transform">arrow_forward</span>
</div>
<div
class="absolute top-0 -inset-full h-full w-1/2 z-5 block transform -skew-x-12 bg-gradient-to-r from-transparent to-white opacity-20 group-hover:animate-shine">
</div>
</button>
</div>
</div>
</div>
<div class="flex-1 flex flex-col bg-surface-darker p-8 overflow-hidden relative">
<div class="absolute inset-0 opacity-5 pointer-events-none"
style="background-image: radial-gradient(#64748b 1px, transparent 1px); background-size: 24px 24px;">
</div>
<div class="max-w-3xl w-full mx-auto flex flex-col gap-6 h-full relative z-10">
<div class="flex items-center justify-between">
<h2 class="text-white tracking-light text-[28px] font-bold leading-tight">Generated Output</h2>
<div class="flex items-center gap-2">
<span
class="bg-surface-dark text-white px-3 py-1 rounded-full text-xs font-medium border border-border-dark">Step
2 of 2</span>
<button id="copyBtn"
class="p-2 hover:bg-surface-dark rounded-lg text-slate-400 hover:text-white transition-colors">
<span class="material-symbols-outlined text-xl">content_copy</span>
</button>
<button id="downloadBtn"
class="p-2 hover:bg-surface-dark rounded-lg text-slate-400 hover:text-white transition-colors">
<span class="material-symbols-outlined text-xl">download</span>
</button>
</div>
</div>
<div class="grid grid-cols-1 lg:grid-cols-3 gap-4">
<div
class="flex items-center gap-3 p-3 rounded-lg bg-surface-dark border border-border-dark shadow-sm">
<div class="relative size-3">
<span
class="animate-ping absolute inline-flex h-full w-full rounded-full bg-purple-500 opacity-75"></span>
<span class="relative inline-flex rounded-full size-3 bg-purple-500"></span>
</div>
<div class="flex flex-col overflow-hidden">
<span class="text-xs text-slate-400 truncate">Vision Agent</span>
<span class="text-sm font-bold text-white truncate">Gemini Pro 1.5</span>
</div>
<span class="material-symbols-outlined text-purple-500 ml-auto text-lg">visibility</span>
</div>
<div
class="flex items-center gap-3 p-3 rounded-lg bg-surface-dark border border-border-dark shadow-sm">
<div class="relative size-3">
<span class="relative inline-flex rounded-full size-3 bg-primary"></span>
</div>
<div class="flex flex-col overflow-hidden">
<span class="text-xs text-slate-400 truncate">Reasoning Agent</span>
<span class="text-sm font-bold text-white truncate">Llama 3 70B</span>
</div>
<span class="material-symbols-outlined text-primary ml-auto text-lg">psychology</span>
</div>
<div
class="flex items-center gap-3 p-3 rounded-lg bg-surface-dark border border-border-dark shadow-sm">
<div class="relative size-3">
<span class="relative inline-flex rounded-full size-3 bg-cyan-500"></span>
</div>
<div class="flex flex-col overflow-hidden">
<span class="text-xs text-slate-400 truncate">SEO Context</span>
<span class="text-sm font-bold text-white truncate">Pinecone DB</span>
</div>
<span class="material-symbols-outlined text-cyan-500 ml-auto text-lg">database</span>
</div>
</div>
<div
class="flex-1 rounded-xl bg-[#0d1117] border border-border-dark flex flex-col overflow-hidden shadow-2xl">
<div
class="flex items-center justify-between px-4 py-2 bg-surface-dark border-b border-border-dark">
<span class="text-xs font-mono text-slate-400">output.json</span>
<div class="flex gap-1.5">
<div class="size-2.5 rounded-full bg-red-500/20"></div>
<div class="size-2.5 rounded-full bg-yellow-500/20"></div>
<div class="size-2.5 rounded-full bg-green-500/20"></div>
</div>
</div>
<div class="flex-1 p-4 overflow-auto custom-scrollbar font-mono text-sm leading-6">
<pre><code id="jsonOutput" class="language-json"><span class="text-slate-500">1</span> <span class="text-yellow-500">{</span>
<span class="text-slate-500">2</span> <span class="text-primary">"product_analysis"</span><span class="text-white">:</span> <span class="text-yellow-500">{</span>
<span class="text-slate-500">3</span> <span class="text-primary">"title"</span><span class="text-white">:</span> <span class="text-sky-300">"Apex Terrain All-Weather Performance Jacket"</span><span class="text-white">,</span>
<span class="text-slate-500">4</span> <span class="text-primary">"category"</span><span class="text-white">:</span> <span class="text-sky-300">"Outerwear / Men's / Technical Shells"</span><span class="text-white">,</span>
<span class="text-slate-500">5</span> <span class="text-primary">"features"</span><span class="text-white">:</span> <span class="text-yellow-500">[</span>
<span class="text-slate-500">6</span> <span class="text-sky-300">"Gore-Tex Pro Membrane"</span><span class="text-white">,</span>
<span class="text-slate-500">7</span> <span class="text-sky-300">"Articulated Sleeves"</span><span class="text-white">,</span>
<span class="text-slate-500">8</span> <span class="text-sky-300">"Helmet-Compatible Hood"</span>
<span class="text-slate-500">9</span> <span class="text-yellow-500">]</span><span class="text-white">,</span>
<span class="text-slate-500">10</span> <span class="text-primary">"seo_tags"</span><span class="text-white">:</span> <span class="text-yellow-500">[</span>
<span class="text-slate-500">11</span> <span class="text-sky-300">"#hikinggear"</span><span class="text-white">,</span> <span class="text-sky-300">"#waterproof"</span><span class="text-white">,</span> <span class="text-sky-300">"#adventure"</span>
<span class="text-slate-500">12</span> <span class="text-yellow-500">]</span><span class="text-white">,</span>
<span class="text-slate-500">13</span> <span class="text-primary">"sentiment_score"</span><span class="text-white">:</span> <span class="text-purple-400">0.98</span><span class="text-white">,</span>
<span class="text-slate-500">14</span> <span class="text-primary">"market_fit"</span><span class="text-white">:</span> <span class="text-sky-300">"High Demand"</span>
<span class="text-slate-500">15</span> <span class="text-yellow-500">}</span><span class="text-white">,</span>
<span class="text-slate-500">16</span> <span class="text-primary">"deployment_status"</span><span class="text-white">:</span> <span class="text-sky-300">"Ready"</span>
<span class="text-slate-500">17</span> <span class="text-yellow-500">}</span></code></pre>
</div>
</div>
</div>
</div>
</main>
<script>
tailwind.config.theme.extend.animation = {
shine: 'shine 1s',
}
tailwind.config.theme.extend.keyframes = {
shine: {
'100%': { left: '125%' },
}
}
const dropZone = document.getElementById('dropZone');
const fileInput = document.getElementById('fileInput');
const startBtn = document.getElementById('startBtn');
const jsonOutput = document.getElementById('jsonOutput');
const deployBtn = document.getElementById('deployBtn');
const copyBtn = document.getElementById('copyBtn');
const downloadBtn = document.getElementById('downloadBtn');
let selectedFile = null;
let isCatalogGenerated = false;
// Default DropZone Content Template
const defaultDropZoneContent = `
<div class="size-16 rounded-full bg-surface-dark group-hover:bg-primary/20 flex items-center justify-center transition-colors border border-border-dark group-hover:border-primary/30">
<span class="material-symbols-outlined text-3xl text-primary">cloud_upload</span>
</div>
<div class="flex flex-col items-center gap-1">
<p class="text-white text-lg font-bold leading-tight tracking-tight text-center">Drop Product Image Here</p>
<p class="text-slate-400 text-sm font-normal text-center">Supports JPG, PNG, WEBP</p>
</div>
<button id="browseBtn" class="mt-2 flex items-center justify-center rounded-full h-9 px-4 bg-slate-700 hover:bg-slate-600 text-white text-xs font-bold transition-colors">
Browse Files
</button>
<div class="absolute inset-0 bg-gradient-to-b from-transparent to-black/10 pointer-events-none rounded-xl"></div>
`;
// Initialize DropZone with listeners
function initDropZone() {
const browseBtn = document.getElementById('browseBtn');
if (browseBtn) {
browseBtn.addEventListener('click', (e) => {
e.stopPropagation();
fileInput.click();
});
}
}
// Initial setup
initDropZone();
// File Input Change
fileInput.addEventListener('change', (e) => {
if (e.target.files.length > 0) {
handleFile(e.target.files[0]);
}
});
// Drag & Drop
dropZone.addEventListener('dragover', (e) => {
e.preventDefault();
dropZone.classList.add('border-primary');
});
dropZone.addEventListener('dragleave', (e) => {
e.preventDefault();
dropZone.classList.remove('border-primary');
});
dropZone.addEventListener('drop', (e) => {
e.preventDefault();
dropZone.classList.remove('border-primary');
if (e.dataTransfer.files.length > 0) {
handleFile(e.dataTransfer.files[0]);
}
});
function handleFile(file) {
selectedFile = file;
// Update UI to Selected State
dropZone.innerHTML = `
<div class="flex flex-col items-center justify-center gap-4 z-10">
<div class="size-16 rounded-full bg-surface-dark flex items-center justify-center border border-border-dark">
<span class="material-symbols-outlined text-3xl text-primary">description</span>
</div>
<div class="flex flex-col items-center gap-1">
<p class="text-white text-lg font-bold text-center">${file.name}</p>
<p class="text-slate-400 text-sm text-center">${(file.size / 1024).toFixed(1)} KB</p>
</div>
<button id="removeFileBtn" class="mt-2 flex items-center justify-center gap-2 rounded-full h-9 px-4 bg-slate-700 hover:bg-red-500/20 hover:text-red-500 hover:border-red-500/50 border border-transparent transition-all text-white text-xs font-bold">
<span class="material-symbols-outlined text-base">close</span>
<span>Remove File</span>
</button>
</div>
<div class="absolute inset-0 bg-gradient-to-b from-transparent to-black/10 pointer-events-none rounded-xl"></div>
`;
// Add listener to the new remove button
document.getElementById('removeFileBtn').addEventListener('click', (e) => {
e.stopPropagation();
resetUploadUI();
});
}
function resetUploadUI() {
selectedFile = null;
fileInput.value = ""; // Clear input
dropZone.innerHTML = defaultDropZoneContent;
initDropZone(); // Re-attach browse listener
}
// Deploy Button
deployBtn.addEventListener('click', () => {
if (!isCatalogGenerated) {
alert("Please generate a catalog first before deploying.");
return;
}
const originalContent = `
<span class="material-symbols-outlined mr-2 text-lg">rocket_launch</span>
<span class="truncate">Deploy</span>
`;
deployBtn.disabled = true;
// Spinner
deployBtn.innerHTML = `
<svg class="animate-spin -ml-1 mr-2 h-4 w-4 text-white" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
</svg>
Processing...
`;
setTimeout(() => {
alert("Success: Catalog pushed to Shopify!");
deployBtn.innerHTML = originalContent;
deployBtn.disabled = false;
}, 1500);
});
// Copy Button
copyBtn.addEventListener('click', () => {
const textToCopy = jsonOutput.innerText;
navigator.clipboard.writeText(textToCopy).then(() => {
const originalIcon = copyBtn.innerHTML;
copyBtn.innerHTML = '<span class="material-symbols-outlined text-xl text-green-500">check</span>';
setTimeout(() => {
copyBtn.innerHTML = originalIcon;
}, 2000);
});
});
// Download Button
downloadBtn.addEventListener('click', () => {
const dataStr = "data:text/json;charset=utf-8," + encodeURIComponent(jsonOutput.innerText);
const downloadAnchorNode = document.createElement('a');
downloadAnchorNode.setAttribute("href", dataStr);
downloadAnchorNode.setAttribute("download", "merchflow_catalog.json");
document.body.appendChild(downloadAnchorNode);
downloadAnchorNode.click();
downloadAnchorNode.remove();
});
// Start Workflow
startBtn.addEventListener('click', async () => {
if (!selectedFile) {
alert("Please select a file first.");
return;
}
// Show loading state
startBtn.innerHTML = '<div class="absolute inset-0 flex items-center justify-center gap-3"><span class="text-white text-lg font-bold tracking-wide">Processing...</span></div>';
startBtn.disabled = true;
const formData = new FormData();
formData.append('file', selectedFile);
try {
// Determine API URL - expecting localhost for this demo
const response = await fetch('/generate-catalog', {
method: 'POST',
body: formData
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
// Format JSON for display
jsonOutput.textContent = JSON.stringify(data, null, 2);
jsonOutput.className = "language-json";
// Allow deployment
isCatalogGenerated = true;
} catch (error) {
console.error("Error:", error);
// Fallback simulation for demo purposes if backend isn't running
console.log("Backend failed, simulating success for demo.");
const demoData = {
"product_analysis": {
"title": "Simulated Product Title",
"category": "Men's Apparel",
"features": ["Feature 1", "Feature 2"],
"seo_tags": ["#demo", "#test"],
"sentiment_score": 0.95
},
"status": "Generated (Simulation)"
};
jsonOutput.textContent = JSON.stringify(demoData, null, 2);
isCatalogGenerated = true;
// Use this to show error if strictly required:
// jsonOutput.textContent = JSON.stringify({ error: error.message }, null, 2);
} finally {
// Reset button
startBtn.innerHTML = '<div class="absolute inset-0 flex items-center justify-center gap-3"><span class="text-white text-lg font-bold tracking-wide group-hover:scale-105 transition-transform">Start Agent Workflow</span><span class="material-symbols-outlined text-white group-hover:translate-x-1 transition-transform">arrow_forward</span></div><div class="absolute top-0 -inset-full h-full w-1/2 z-5 block transform -skew-x-12 bg-gradient-to-r from-transparent to-white opacity-20 group-hover:animate-shine"></div>';
startBtn.disabled = false;
}
});
</script>
</body>
</html>