store1 / index.html
fsalmansour's picture
تمّ التوسّع في المتطلبات ليشمل: 1. دفع الإشعارات (Push Notifications) لتنبيهات المخزون و وصول ردود المساعد. 2. دعم الملفات (صور / PDF ≤ 5 MB) ضمن الدردشة. 3. رسائل صوتيّة (تسجيل ميكروفون، رفع الـ Blob، اختياريًا Whisper لتحويل الكلام إلى نص). أدناه برومت واحد بالإنجليزي يضيف كل هذه الميزات إلى الصفحة السابقة — انسخه إلى DeepSite ثم Generate. ⸻ # === ADD-ON FEATURES === ## Push Notifications • Ask permission on first load or via Settings toggle “Enable Push Notifications”. • Register `sw.js` service-worker (inline snippet) that listens for “push” and shows system notification with icon **fa-bell**. • Front-end helper `notify(title, body)` called when: 1) assistant sends a chat reply **while chat panel is closed**, 2) any product stock drops below 10 (badge red → notification “Low stock on SKU …”). • Fallback: if permission !== ‘granted’ just show in-app toast. ## Chat – File & Voice ### UI additions • Inside chat composer add two buttons before Send: 1. **fa-paperclip** opens hidden `<input type=file multiple accept="image/*,.pdf">`. 2. **fa-microphone-lines** toggles `recording` state → show red circle blink; on second click stops and sends audio. • Show each attachment as a bubble: - Images: thumbnail preview; click = full-screen lightbox. - PDF / other: **fa-file** icon + filename (download link). - Voice: inline `<audio controls>` player. ### JS logic ```javascript attachments: [], recording:false, mediaRec:null, chunks:[], handleFileUpload(e){[...e.target.files].forEach(f=>this.attachments.push(f));}, toggleRecord(){ if(!this.recording){ navigator.mediaDevices.getUserMedia({audio:true}).then(stream=>{ this.mediaRec = new MediaRecorder(stream); this.mediaRec.ondataavailable = ev => this.chunks.push(ev.data); this.mediaRec.onstop = ()=>{ const blob = new Blob(this.chunks,{type:'audio/webm'}); this.attachments.push(blob); this.chunks=[]; }; this.mediaRec.start(); this.recording=true; }); }else{ this.mediaRec.stop(); this.recording=false; } }, • On sendMessage() embed attachments array; upload each file to /api/upload (return URL) then include markdown links in message. Reset attachments=[]. Bulk Import Endpoint /api/products/bulk receives CSV, returns JSON array → merge into products[] and recalc stock alerts. Service Worker (include at bottom) <script> if('serviceWorker' in navigator){navigator.serviceWorker.register('/sw.js');} </script> sw.js (auto-generated by DeepSite) listens for “push” & shows notifications. === UPDATE PROMPT SECTIONS FROM PREVIOUS SPEC === ➤ Settings modal: toggle fa-bell “Enable Push Notifications”. ➤ Products section: call notify() when stock drops < 10. ➤ Chat sendMessage(): when panel hidden, call notify('Store AI', 'Assistant replied…'). ➤ Command Palette: new commands “enable notifications”, “disable notifications”, “send test push”. === EVERYTHING ELSE remains exactly as in the last spec (dark neon, glass, FABs, localStorage, speech-to-text, etc.) === --- > بعد التوليد: > • ارفع ملفّ `sw.js` البسيط (يدرّج كود `self.addEventListener('push', … )`). > • اربط `/api/upload`, `/api/products/bulk`، وWeb-Push back-end (أو Firebase Cloud Messaging) حسب بنيتك. > > لأي استفسار إضافي أنا حاضر. - Follow Up Deployment
f6f3698 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Store AI – Control Center</title>
<!-- Tailwind CSS -->
<script src="https://cdn.tailwindcss.com"></script>
<script>
tailwind.config = {
darkMode: 'class',
theme: {
extend: {
colors: {
accent: '#38bdf8',
primary: {
50: '#f0f9ff',
100: '#e0f2fe',
200: '#bae6fd',
300: '#7dd3fc',
400: '#38bdf8',
500: '#0ea5e9',
600: '#0284c7',
700: '#0369a1',
800: '#075985',
900: '#0c4a6e',
}
}
}
}
}
</script>
<!-- Font Awesome -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" />
<!-- Google Fonts -->
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&display=swap" rel="stylesheet" />
<style>
:root { --accent: #38bdf8; }
body {
font-family: 'Inter', sans-serif;
background: #111827;
color: #e5e7eb;
}
.blink { animation: blink 1s infinite; }
@keyframes blink { 50% { opacity: 0; } }
.glass {
backdrop-filter: blur(25px) saturate(200%);
border: 1px solid rgba(255, 255, 255, 0.18);
background: rgba(30, 41, 59, 0.5);
}
.glass-light {
background: rgba(248, 250, 252, 0.6);
color: #0f172a;
}
.kpi-card {
transition: transform 0.3s, box-shadow 0.3s;
}
.kpi-card:hover {
transform: translateY(-4px);
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.3);
}
.tooltip:hover .tooltip-text {
visibility: visible;
opacity: 1;
}
.tooltip-text {
visibility: hidden;
opacity: 0;
position: absolute;
z-index: 1;
bottom: 100%;
left: 50%;
transform: translateX(-50%);
background: rgba(30, 41, 59, 0.9);
color: white;
padding: 0.5rem;
border-radius: 0.5rem;
font-size: 0.75rem;
white-space: nowrap;
transition: opacity 0.3s;
}
@keyframes pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.5; }
}
.animate-pulse {
animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
}
.fab-shadow {
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4);
}
.progress-ring {
transform: rotate(-90deg);
}
.progress-ring-circle {
stroke-dasharray: 283;
stroke-dashoffset: 283;
transition: stroke-dashoffset 0.5s;
}
</style>
</head>
<body class="bg-slate-900 text-slate-200 antialiased overflow-x-hidden">
<!-- App Bar -->
<header class="glass fixed top-0 left-0 right-0 z-50">
<div class="container mx-auto px-4 h-16 flex items-center justify-between">
<!-- Logo -->
<div class="flex items-center space-x-3">
<div class="w-9 h-9 rounded-full bg-gradient-to-br from-indigo-500 to-cyan-400 flex items-center justify-center">
<i class="fas fa-brain text-white text-sm"></i>
</div>
<span class="font-extrabold text-lg hidden sm:inline">Store AI</span>
</div>
<!-- Desktop Command Palette -->
<div class="hidden md:flex flex-1 max-w-xs mx-4">
<button id="openCommand" class="w-full glass text-sm px-3 py-2 rounded-md flex items-center space-x-2 hover:bg-slate-700 transition">
<i class="fas fa-search text-xs"></i>
<span>Search commands...</span>
<kbd class="ml-auto text-xs px-2 py-1 bg-slate-700 rounded">⌘K</kbd>
</button>
</div>
<!-- Nav Icons -->
<div class="flex items-center space-x-3">
<button class="text-sm font-semibold hover:opacity-80 transition" title="Switch to Arabic">EN</button>
<button id="themeToggle" class="p-1.5 rounded-full hover:bg-slate-700 transition tooltip">
<i class="fas fa-sun text-yellow-300"></i>
<span class="tooltip-text">Light Mode</span>
</button>
<button class="p-1.5 rounded-full hover:bg-slate-700 transition tooltip">
<i class="fas fa-microphone text-red-400"></i>
<span class="tooltip-text">Voice Command</span>
</button>
<div class="relative">
<button class="p-1.5 rounded-full hover:bg-slate-700 relative">
<i class="fas fa-bell"></i>
<span class="absolute -top-1 -right-1 bg-red-500 text-white text-xs rounded-full h-4 w-4 flex items-center justify-center">2</span>
</button>
</div>
<div class="relative">
<button class="flex items-center space-x-2 hover:bg-slate-700 transition rounded-full p-1">
<div class="w-8 h-8 rounded-full bg-gradient-to-br from-purple-500 to-pink-500 flex items-center justify-center">
<span class="text-xs font-bold">AI</span>
</div>
</button>
</div>
</div>
</div>
</header>
<!-- Side Rail -->
<aside id="sidebar" class="glass fixed inset-y-0 left-0 z-40 w-64 p-4 space-y-5 overflow-y-auto lg:relative lg:translate-x-0 transform -translate-x-full lg:translate-x-0 transition-transform duration-300">
<div class="flex items-center justify-between mb-6">
<div class="flex items-center space-x-3">
<div class="w-9 h-9 rounded-full bg-gradient-to-br from-indigo-500 to-cyan-400 flex items-center justify-center">
<i class="fas fa-brain text-white text-sm"></i>
</div>
<span class="font-extrabold text-lg">Store AI</span>
</div>
<button id="closeSidebar" class="lg:hidden p-1 rounded-full hover:bg-slate-700">
<i class="fas fa-times"></i>
</button>
</div>
<div class="mb-6">
<div class="glass rounded-lg p-3">
<div class="flex items-center justify-between mb-2">
<span class="text-xs font-medium">Plan</span>
<span class="text-xs px-2 py-1 bg-emerald-600/20 text-emerald-400 rounded-full">Pro</span>
</div>
<div class="w-full bg-slate-700 rounded-full h-1.5 mb-1">
<div class="bg-accent h-1.5 rounded-full" style="width: 65%"></div>
</div>
<div class="flex justify-between text-xs text-slate-400">
<span>65% used</span>
<span>remaining 35%</span>
</div>
</div>
</div>
<nav class="space-y-1">
<a href="#kpi" class="flex items-center space-x-3 px-3 py-2 rounded-md hover:bg-slate-700 transition bg-slate-700/50">
<i class="fas fa-tachometer-alt text-sm w-5 text-center"></i>
<span>Dashboard</span>
<span class="ml-auto text-xs px-2 py-1 rounded-full bg-accent/20 text-accent">3</span>
</a>
<a href="#sim" class="flex items-center space-x-3 px-3 py-2 rounded-md hover:bg-slate-700 transition">
<i class="fas fa-sliders-h text-sm w-5 text-center"></i>
<span>What-If</span>
</a>
<a href="#models" class="flex items-center space-x-3 px-3 py-2 rounded-md hover:bg-slate-700 transition">
<i class="fas fa-brain text-sm w-5 text-center"></i>
<span>Models</span>
</a>
<a href="#stores" class="flex items-center space-x-3 px-3 py-2 rounded-md hover:bg-slate-700 transition">
<i class="fas fa-store text-sm w-5 text-center"></i>
<span>Stores</span>
</a>
<a href="#products" class="flex items-center space-x-3 px-3 py-2 rounded-md hover:bg-slate-700 transition">
<i class="fas fa-box text-sm w-5 text-center"></i>
<span>Products</span>
</a>
<a href="#ai" class="flex items-center space-x-3 px-3 py-2 rounded-md hover:bg-slate-700 transition">
<i class="fas fa-chart-bar text-sm w-5 text-center"></i>
<span>AI</span>
</a>
<a href="#ads" class="flex items-center space-x-3 px-3 py-2 rounded-md hover:bg-slate-700 transition">
<i class="fas fa-bullhorn text-sm w-5 text-center"></i>
<span>Integrations</span>
<span class="ml-auto text-xs px-2 py-1 rounded-full bg-accent/20 text-accent">2</span>
</a>
</nav>
<div class="absolute bottom-4 left-4 right-4">
<div class="glass rounded-lg p-3 text-center">
<p class="text-xs mb-1">Need help with Store AI?</p>
<button class="text-xs text-accent font-medium hover:underline">Contact Support</button>
</div>
</div>
</aside>
<!-- Mobile Hamburger -->
<button id="openSidebar" class="glass fixed top-20 left-4 z-30 p-2 rounded-full lg:hidden fab-shadow">
<i class="fas fa-bars"></i>
</button>
<!-- Main Content -->
<main class="pt-20 lg:pl-72 px-4 pb-16 space-y-16">
<!-- KPI Section -->
<section id="kpi" class="scroll-mt-20">
<div class="flex items-center justify-between mb-6">
<h2 class="text-2xl font-extrabold">Dashboard</h2>
<div class="flex items-center space-x-2">
<div class="relative">
<select class="glass text-sm px-3 py-1.5 rounded appearance-none pr-7">
<option value="today">Today</option>
<option value="week">This Week</option>
<option value="month">This Month</option>
</select>
<i class="fas fa-chevron-down absolute right-2 top-1/2 transform -translate-y-1/2 text-xs pointer-events-none"></i>
</div>
<button class="glass p-1.5 rounded hover:bg-slate-700 transition">
<i class="fas fa-sync-alt text-sm"></i>
</button>
</div>
</div>
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4">
<div class="kpi-card glass p-4 rounded-xl">
<div class="flex items-center justify-between mb-2">
<div class="flex items-center">
<div class="w-8 h-8 rounded-full bg-accent/10 flex items-center justify-center mr-2">
<i class="fas fa-dollar-sign text-accent text-sm"></i>
</div>
<span class="text-sm">Revenue</span>
</div>
<button class="text-xs text-slate-400 hover:text-accent">
<i class="fas fa-info-circle"></i>
</button>
</div>
<p class="text-2xl font-bold mb-1">$1,247</p>
<div class="flex items-center">
<span class="text-xs text-emerald-400">
<i class="fas fa-caret-up"></i>
<span>5.2%</span>
</span>
<span class="text-xs text-slate-400 ml-2">vs yesterday</span>
</div>
</div>
<div class="kpi-card glass p-4 rounded-xl">
<div class="flex items-center justify-between mb-2">
<div class="flex items-center">
<div class="w-8 h-8 rounded-full bg-accent/10 flex items-center justify-center mr-2">
<i class="fas fa-shopping-cart text-accent text-sm"></i>
</div>
<span class="text-sm">Orders</span>
</div>
<button class="text-xs text-slate-400 hover:text-accent">
<i class="fas fa-info-circle"></i>
</button>
</div>
<p class="text-2xl font-bold mb-1">28</p>
<div class="flex items-center">
<span class="text-xs text-red-400">
<i class="fas fa-caret-down"></i>
<span>2.1%</span>
</span>
<span class="text-xs text-slate-400 ml-2">vs yesterday</span>
</div>
</div>
<div class="kpi-card glass p-4 rounded-xl">
<div class="flex items-center justify-between mb-2">
<div class="flex items-center">
<div class="w-8 h-8 rounded-full bg-accent/10 flex items-center justify-center mr-2">
<i class="fas fa-chart-line text-accent text-sm"></i>
</div>
<span class="text-sm">Conversion</span>
</div>
<button class="text-xs text-slate-400 hover:text-accent">
<i class="fas fa-info-circle"></i>
</button>
</div>
<p class="text-2xl font-bold mb-1">12.2%</p>
<div class="flex items-center">
<span class="text-xs text-emerald-400">
<i class="fas fa-caret-up"></i>
<span>1.8%</span>
</span>
<span class="text-xs text-slate-400 ml-2">vs yesterday</span>
</div>
</div>
<div class="kpi-card glass p-4 rounded-xl">
<div class="flex items-center justify-between mb-2">
<div class="flex items-center">
<div class="w-8 h-8 rounded-full bg-accent/10 flex items-center justify-center mr-2">
<i class="fas fa-heartbeat text-accent text-sm"></i>
</div>
<span class="text-sm">Health</span>
</div>
<button class="text-xs text-slate-400 hover:text-accent">
<i class="fas fa-info-circle"></i>
</button>
</div>
<p class="text-2xl font-bold mb-1">97%</p>
<div class="flex items-center">
<span class="text-xs text-emerald-400">
<i class="fas fa-caret-up"></i>
<span>0.5%</span>
</span>
<span class="text-xs text-slate-400 ml-2">vs yesterday</span>
</div>
</div>
</div>
<!-- Mini charts row -->
<div class="grid grid-cols-1 md:grid-cols-3 gap-4 mt-4">
<div class="glass rounded-xl p-4">
<div class="flex items-center justify-between mb-2">
<span class="text-sm">Conversion Rate</span>
<span class="text-xs px-2 py-1 bg-emerald-600/20 text-emerald-400 rounded-full">+2.4%</span>
</div>
<div class="h-24">
<div class="w-full h-full flex items-center justify-center text-slate-400">
<i class="fas fa-chart-line text-3xl"></i>
</div>
</div>
</div>
<div class="glass rounded-xl p-4">
<div class="flex items-center justify-between mb-2">
<span class="text-sm">Avg Order Value</span>
<span class="text-xs px-2 py-1 bg-red-600/20 text-red-400 rounded-full">-1.2%</span>
</div>
<div class="h-24">
<div class="w-full h-full flex items-center justify-center text-slate-400">
<i class="fas fa-chart-bar text-3xl"></i>
</div>
</div>
</div>
<div class="glass rounded-xl p-4">
<div class="flex items-center justify-between mb-2">
<span class="text-sm">New Customers</span>
<span class="text-xs px-2 py-1 bg-emerald-600/20 text-emerald-400 rounded-full">+8.7%</span>
</div>
<div class="h-24">
<div class="w-full h-full flex items-center justify-center text-slate-400">
<i class="fas fa-chart-pie text-3xl"></i>
</div>
</div>
</div>
</div>
</section>
<!-- What-If Sim -->
<section id="sim" class="scroll-mt-20">
<div class="flex items-center justify-between mb-4">
<h2 class="text-xl font-bold">What-If Simulator</h2>
<button class="text-sm glass px-3 py-1 rounded hover:bg-slate-700">Save Scenario</button>
</div>
<div class="grid grid-cols-1 lg:grid-cols-3 gap-6">
<div class="glass rounded-xl p-5">
<div class="space-y-4">
<div>
<label class="text-sm flex items-center justify-between">
<span>Discount</span>
<span class="text-xs text-slate-400">Current: 15%</span>
</label>
<input type="range" min="0" max="50" value="10" class="w-full accent-accent mb-1">
<div class="flex justify-between text-xs text-slate-400">
<span>0%</span>
<span>10%</span>
<span>50%</span>
</div>
</div>
<div>
<label class="text-sm flex items-center justify-between">
<span>Budget</span>
<span class="text-xs text-slate-400">Current: $750</span>
</label>
<input type="range" min="0" max="2000" step="50" value="500" class="w-full accent-accent mb-1">
<div class="flex justify-between text-xs text-slate-400">
<span>$0</span>
<span>$500</span>
<span>$2000</span>
</div>
</div>
</div>
</div>
<div class="glass rounded-xl p-5">
<h3 class="text-sm font-medium mb-3">Projected Results</h3>
<div class="space-y-3">
<div class="flex items-center justify-between">
<span class="text-sm">Revenue</span>
<span class="font-medium">$1,075</span>
</div>
<div class="flex items-center justify-between">
<span class="text-sm">Profit</span>
<span class="font-medium">$725</span>
</div>
<div class="flex items-center justify-between">
<span class="text-sm">New Customers</span>
<span class="font-medium">20</span>
</div>
<div class="flex items-center justify-between">
<span class="text-sm">ROI</span>
<span class="font-medium">145%</span>
</div>
</div>
</div>
<div class="glass rounded-xl p-5">
<h3 class="text-sm font-medium mb-3">Saved Scenarios</h3>
<div class="space-y-2">
<div class="flex items-center justify-between p-2 hover:bg-slate-700 rounded cursor-pointer">
<div>
<p class="text-sm font-medium">Summer Sale</p>
<p class="text-xs text-slate-400">Discount: 20% | Budget: $1000</p>
</div>
<button class="text-red-400 hover:text-red-300 p-1">
<i class="fas fa-trash text-xs"></i>
</button>
</div>
<div class="flex items-center justify-between p-2 hover:bg-slate-700 rounded cursor-pointer">
<div>
<p class="text-sm font-medium">Black Friday</p>
<p class="text-xs text-slate-400">Discount: 30% | Budget: $1500</p>
</div>
<button class="text-red-400 hover:text-red-300 p-1">
<i class="fas fa-trash text-xs"></i>
</button>
</div>
<div class="text-center py-4 text-sm text-slate-400">
<p>No saved scenarios</p>
</div>
</div>
</div>
</div>
</section>
<!-- Models Table -->
<section id="models" class="scroll-mt-20">
<div class="flex items-center justify-between mb-4">
<h2 class="text-xl font-bold">AI Models</h2>
<div class="flex items-center space-x-2">
<button class="text-sm glass px-3 py-1 rounded hover:bg-slate-700 flex items-center space-x-1">
<i class="fas fa-plus text-xs"></i>
<span>Add Model</span>
</button>
<div class="relative">
<select class="glass text-sm px-3 py-1 rounded appearance-none pr-7">
<option value="all">All Models</option>
<option value="active">Active</option>
<option value="inactive">Inactive</option>
</select>
<i class="fas fa-chevron-down absolute right-2 top-1/2 transform -translate-y-1/2 text-xs pointer-events-none"></i>
</div>
</div>
</div>
<div class="glass rounded-xl overflow-x-auto">
<table class="w-full text-sm">
<thead class="bg-slate-800">
<tr>
<th class="p-3 text-left">Model</th>
<th class="p-3 text-left">Provider</th>
<th class="p-3 text-left">Size</th>
<th class="p-3 text-left">Status</th>
<th class="p-3 text-left">Usage</th>
<th class="p-3">Action</th>
</tr>
</thead>
<tbody>
<tr class="border-t border-slate-700 hover:bg-slate-800/50">
<td class="p-3">
<div class="flex items-center space-x-2">
<i class="fas fa-robot text-accent"></i>
<span>GPT-3.5</span>
</div>
</td>
<td class="p-3">OpenAI</td>
<td class="p-3">800 MB</td>
<td class="p-3">
<span class="px-2 py-1 rounded-full text-xs bg-emerald-600/20 text-emerald-400">Active</span>
</td>
<td class="p-3">
<div class="w-20 h-1.5 bg-slate-700 rounded-full overflow-hidden">
<div class="h-full bg-accent rounded-full" style="width: 75%"></div>
</div>
</td>
<td class="p-3">
<div class="flex items-center space-x-2">
<button class="text-xs px-2 py-1 rounded hover:bg-slate-700 text-red-400">Deactivate</button>
<button class="text-xs px-2 py-1 rounded hover:bg-slate-700 text-slate-400">Remove</button>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</section>
<!-- Stores Table -->
<section id="stores" class="scroll-mt-20">
<div class="flex items-center justify-between mb-4">
<h2 class="text-xl font-bold">Stores</h2>
<div class="flex items-center space-x-2">
<button class="text-sm glass px-3 py-1 rounded hover:bg-slate-700 flex items-center space-x-1">
<i class="fas fa-plus text-xs"></i>
<span>Add Store</span>
</button>
<div class="relative">
<select class="glass text-sm px-3 py-1 rounded appearance-none pr-7">
<option value="all">All Stores</option>
<option value="healthy">Healthy</option>
<option value="warning">Warning</option>
<option value="critical">Critical</option>
</select>
<i class="fas fa-chevron-down absolute right-2 top-1/2 transform -translate-y-1/2 text-xs pointer-events-none"></i>
</div>
</div>
</div>
<div class="glass rounded-xl overflow-x-auto">
<table class="w-full text-sm">
<thead class="bg-slate-800">
<tr>
<th class="p-3 text-left">Store</th>
<th class="p-3 text-left">Platform</th>
<th class="p-3 text-left">Health</th>
<th class="p-3 text-left">Last Sync</th>
<th class="p-3">Action</th>
</tr>
</thead>
<tbody>
<tr class="border-t border-slate-700 hover:bg-slate-800/50">
<td class="p-3">
<div class="flex items-center space-x-2">
<i class="fas fa-tshirt text-accent"></i>
<span>FashionHub</span>
</div>
</td>
<td class="p-3">Shopify</td>
<td class="p-3">
<div class="flex items-center space-x-2">
<div class="relative w-8 h-8">
<svg class="w-8 h-8 progress-ring" viewBox="0 0 100 100">
<circle class="text-slate-700" stroke-width="8" stroke="currentColor" fill="transparent" r="45" cx="50" cy="50" />
<circle class="progress-ring-circle" stroke-width="8" stroke-linecap="round" stroke="#10b981" fill="transparent" r="45" cx="50" cy="50" stroke-dashoffset="283 - (283 * 94 / 100)" />
</svg>
<span class="absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 text-xs font-bold text-emerald-400">94%</span>
</div>
<span class="text-xs text-emerald-400">
<i class="fas fa-caret-up"></i>
<span>2%</span>
</span>
</div>
</td>
<td class="p-3 text-xs text-slate-400">1 hour ago</td>
<td class="p-3">
<div class="flex items-center space-x-2">
<button class="text-xs px-2 py-1 rounded hover:bg-slate-700 text-accent">Sync</button>
<button class="text-xs px-2 py-1 rounded hover:bg-slate-700">Inspect</button>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</section>
<!-- Products Table -->
<section id="products" class="scroll-mt-20">
<div class="flex items-center justify-between mb-4">
<h2 class="text-xl font-bold">Products</h2>
<div class="flex items-center space-x-2">
<button class="text-sm glass px-3 py-1 rounded hover:bg-slate-700 flex items-center space-x-1">
<i class="fas fa-plus text-xs"></i>
<span>Add Product</span>
</button>
<label class="glass px-3 py-1 rounded cursor-pointer text-sm">
<i class="fas fa-file-upload mr-1"></i> Import CSV
<input type="file" accept=".csv" class="hidden" />
</label>
</div>
</div>
<div class="glass rounded-xl overflow-x-auto">
<table class="w-full text-sm">
<thead class="bg-slate-800">
<tr>
<th class="p-3 text-left">#</th>
<th class="p-3 text-left">Name</th>
<th class="p-3 text-left">SKU</th>
<th class="p-3 text-left">Price</th>
<th class="p-3 text-left">Stock</th>
<th class="p-3 text-left">Action</th>
</tr>
</thead>
<tbody>
<tr class="border-t border-slate-700">
<td class="p-3">1</td>
<td class="p-3">Premium T-Shirt</td>
<td class="p-3">TSH-001</td>
<td class="p-3">$29.99</td>
<td class="p-3">
<span class="px-2 py-1 rounded-full text-xs bg-emerald-600">42</span>
</td>
<td class="p-3">
<button class="text-accent hover:underline">Edit</button>
</td>
</tr>
</tbody>
</table>
</div>
</section>
<!-- AI Usage Section -->
<section id="ai" class="scroll-mt-20">
<div class="flex items-center justify-between mb-4">
<h2 class="text-xl font-bold">AI Usage</h2>
<div class="relative">
<select class="glass text-sm px-3 py-1 rounded appearance-none pr-7">
<option value="7d">Last 7 Days</option>
<option value="30d">Last 30 Days</option>
<option value="90d">Last 90 Days</option>
</select>
<i class="fas fa-chevron-down absolute right-2 top-1/2 transform -translate-y-1/2 text-xs pointer-events-none"></i>
</div>
</div>
<div class="grid md:grid-cols-2 gap-6">
<div class="glass rounded-xl p-4">
<div class="flex items-center justify-between mb-4">
<h3 class="font-medium">API Usage</h3>
<div class="flex items-center space-x-2">
<div class="flex items-center">
<div class="w-2 h-2 rounded-full bg-accent mr-1"></div>
<span class="text-xs">This Month</span>
</div>
<div class="flex items-center">
<div class="w-2 h-2 rounded-full bg-slate-500 mr-1"></div>
<span class="text-xs">Last Month</span>
</div>
</div>
</div>
<div class="h-48">
<div class="w-full h-full flex items-center justify-center text-slate-400">
<i class="fas fa-chart-line text-4xl"></i>
</div>
</div>
</div>
</div>
<div class="glass rounded-xl p-4 mt-6">
<h3 class="font-medium mb-4">Cost Summary</h3>
<div class="grid grid-cols-1 md:grid-cols-4 gap-4">
<div class="bg-slate-800/50 rounded-lg p-3">
<p class="text-sm text-slate-400 mb-1">This Month</p>
<p class="text-xl font-bold">$1,245</p>
</div>
<div class="bg-slate-800/50 rounded-lg p-3">
<p class="text-sm text-slate-400 mb-1">Last Month</p>
<p class="text-xl font-bold">$1,080</p>
</div>
<div class="bg-slate-800/50 rounded-lg p-3">
<p class="text-sm text-slate-400 mb-1">Change</p>
<p class="text-xl font-bold text-emerald-400">+15.3%</p>
</div>
<div class="bg-slate-800/50 rounded-lg p-3">
<p class="text-sm text-slate-400 mb-1">Forecast</p>
<p class="text-xl font-bold">$1,450</p>
</div>
</div>
</div>
</section>
<!-- Products Section -->
<section id="products" class="scroll-mt-20">
<div class="flex items-center justify-between mb-4">
<h2 class="text-xl font-bold">Products</h2>
<div class="flex items-center space-x-2">
<button id="bulkImportBtn" class="text-sm glass px-3 py-1 rounded hover:bg-slate-700 flex items-center space-x-1">
<i class="fas fa-file-import text-xs"></i>
<span>Bulk Import</span>
</button>
<button class="text-sm glass px-3 py-1 rounded hover:bg-slate-700 flex items-center space-x-1">
<i class="fas fa-plus text-xs"></i>
<span>Add Product</span>
</button>
</div>
</div>
<div class="glass rounded-xl overflow-x-auto">
<table id="productsTable" class="w-full text-sm">
<thead class="bg-slate-800">
<tr>
<th class="p-3 text-left">Name</th>
<th class="p-3 text-left">SKU</th>
<th class="p-3 text-left">Price</th>
<th class="p-3 text-left">Stock</th>
<th class="p-3 text-left">Status</th>
</tr>
</thead>
<tbody></tbody>
</table>
</div>
</section>
<!-- Integrations -->
<section id="ads" class="scroll-mt-20">
<div class="flex items-center justify-between mb-4">
<h2 class="text-xl font-bold">Integrations</h2>
<button class="text-sm glass px-3 py-1 rounded hover:bg-slate-700 flex items-center space-x-1">
<i class="fas fa-plus text-xs"></i>
<span>Add Integration</span>
</button>
</div>
<div class="flex flex-wrap gap-2 mb-4">
<button class="px-3 py-1 rounded-md text-sm transition bg-accent text-white">All</button>
<button class="px-3 py-1 rounded-md text-sm transition glass hover:bg-slate-700">Ads</button>
<button class="px-3 py-1 rounded-md text-sm transition glass hover:bg-slate-700">Stores</button>
<button class="px-3 py-1 rounded-md text-sm transition glass hover:bg-slate-700">Email</button>
<button class="px-3 py-1 rounded-md text-sm transition glass hover:bg-slate-700">Automation</button>
</div>
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-4">
<div class="glass rounded-xl p-4 hover:bg-slate-700/50 transition cursor-pointer">
<div class="flex items-start justify-between mb-3">
<div class="w-10 h-10 rounded-lg bg-accent/10 flex items-center justify-center">
<i class="fab fa-wordpress text-accent text-lg"></i>
</div>
<span class="text-xs px-2 py-1 rounded-full bg-slate-700/50 text-slate-400">Disconnected</span>
</div>
<p class="font-semibold mb-1">WooCommerce</p>
<p class="text-xs text-slate-400 mb-3">Connect your WooCommerce store</p>
<div class="w-full bg-slate-700 rounded-full h-1.5 mb-1">
<div class="bg-accent h-1.5 rounded-full" style="width: 0%"></div>
</div>
<div class="flex justify-between text-xs text-slate-400">
<span>Not connected</span>
<span>E-commerce</span>
</div>
</div>
<div class="glass rounded-xl p-4 hover:bg-slate-700/50 transition cursor-pointer">
<div class="flex items-start justify-between mb-3">
<div class="w-10 h-10 rounded-lg bg-accent/10 flex items-center justify-center">
<i class="fab fa-shopify text-accent text-lg"></i>
</div>
<span class="text-xs px-2 py-1 rounded-full bg-emerald-600/20 text-emerald-400">Connected</span>
</div>
<p class="font-semibold mb-1">Shopify</p>
<p class="text-xs text-slate-400 mb-3">Connect your Shopify store</p>
<div class="w-full bg-slate-700 rounded-full h-1.5 mb-1">
<div class="bg-accent h-1.5 rounded-full" style="width: 100%"></div>
</div>
<div class="flex justify-between text-xs text-slate-400">
<span>Connected</span>
<span>E-commerce</span>
</div>
</div>
</div>
</section>
</main>
<!-- n8n Hidden Section -->
<section id="n8n" class="scroll-mt-20 hidden">
<div class="flex items-center justify-between mb-4">
<h2 class="text-xl font-bold">n8n Automation</h2>
<button id="refreshWorkflowsBtn" class="text-sm glass px-3 py-1 rounded hover:bg-slate-700">
<i class="fas fa-sync-alt mr-1"></i>Refresh
</button>
</div>
<div id="workflowsList" class="glass rounded-xl p-4">
<p class="text-slate-400">Loading workflows…</p>
</div>
</section>
<!-- Command Palette Modal -->
<div id="commandModal" class="fixed inset-0 bg-black/50 z-50 hidden items-start justify-center pt-20">
<div class="glass w-full max-w-lg rounded-xl overflow-hidden">
<div class="p-4 border-b border-slate-700 flex items-center">
<i class="fas fa-search text-sm mr-2 text-slate-400"></i>
<input type="search" placeholder="Type to search..." class="w-full bg-transparent outline-none placeholder-slate-400" />
</div>
<div class="max-h-96 overflow-y-auto">
<button class="w-full text-left p-3 hover:bg-slate-700 flex items-center space-x-3">
<i class="fas fa-sync-alt text-sm w-5 text-center text-slate-400"></i>
<div>
<p>Refresh Data</p>
<p class="text-xs text-slate-400">Reload all dashboard data</p>
</div>
<kbd class="ml-auto text-xs px-2 py-1 bg-slate-700 rounded">F5</kbd>
</button>
<button class="w-full text-left p-3 hover:bg-slate-700 flex items-center space-x-3">
<i class="fas fa-cog text-sm w-5 text-center text-slate-400"></i>
<div>
<p>Open Settings</p>
<p class="text-xs text-slate-400">Open application settings</p>
</div>
<kbd class="ml-auto text-xs px-2 py-1 bg-slate-700 rounded">⌘S</kbd>
</button>
</div>
</div>
</div>
<!-- Floating Action Cluster -->
<div class="fixed bottom-6 right-6 flex flex-col items-end space-y-3">
<button id="settingsFab" class="glass p-3 rounded-full shadow-lg fab-shadow transition hover:scale-110" aria-label="Settings">
<i class="fas fa-cog"></i>
</button>
<button id="addProductFab" class="glass p-3 rounded-full shadow-lg fab-shadow transition hover:scale-110 neon" aria-label="Add Product">
<i class="fas fa-box-open"></i>
</button>
<button id="chatToggleFab" class="glass p-4 rounded-full shadow-lg fab-shadow transition hover:scale-110" aria-label="Toggle Chat">
<i class="fas fa-robot"></i>
</button>
</div>
<!-- Live Chat Panel -->
<div id="chatPanel" class="fixed inset-x-0 bottom-0 z-50 glass rounded-t-xl shadow-2xl transform translate-y-full transition-transform duration-300 md:max-w-md md:mx-auto">
<header class="flex items-center justify-between p-4 border-b border-slate-700">
<div class="flex items-center space-x-2">
<i class="fas fa-robot text-accent"></i>
<span class="font-bold">Live Chat</span>
</div>
<button id="closeChat" class="p-1 rounded-full hover:bg-slate-700">
<i class="fas fa-times"></i>
</button>
</header>
<div id="chatMessages" class="h-[60vh] md:h-[70vh] overflow-y-auto p-4 space-y-3"></div>
<footer class="p-4 border-t border-slate-700 flex items-center space-x-2">
<button id="emojiToggle" class="p-2 rounded-full hover:bg-slate-700">
<i class="far fa-face-smile"></i>
</button>
<label class="p-2 rounded-full hover:bg-slate-700 cursor-pointer">
<i class="fas fa-paperclip"></i>
<input type="file" multiple accept="image/*,.pdf" class="hidden" @change="handleFileUpload($event)">
</label>
<button id="recordBtn" :class="recording ? 'text-red-400 blink' : ''" class="p-2 rounded-full hover:bg-slate-700" @click="toggleRecord">
<i :class="recording ? 'fas fa-stop' : 'fas fa-microphone-lines'"></i>
</button>
<input id="chatInput" type="text" placeholder="Type a message…" class="flex-1 bg-transparent outline-none" />
<button id="sendMessage" class="p-2 rounded-full hover:bg-slate-700">
<i class="fas fa-paper-plane"></i>
</button>
</footer>
</div>
<!-- Settings Modal -->
<div id="settingsModal" class="fixed inset-0 bg-black/50 z-50 hidden items-center justify-center">
<div class="glass w-full max-w-md rounded-xl p-6 space-y-4">
<div class="flex items-center justify-between">
<h3 class="text-lg font-bold">Settings</h3>
<button id="closeSettings" class="p-1 rounded-full hover:bg-slate-700">
<i class="fas fa-times"></i>
</button>
</div>
<div>
<label class="block text-sm mb-1">Accent Color</label>
<input id="accentColorPicker" type="color" value="#38bdf8" class="w-full h-10 rounded cursor-pointer" />
</div>
<div>
<label class="block text-sm mb-1">Theme</label>
<button id="themeToggleBtn" class="glass px-3 py-1 rounded">Toggle Dark / Light</button>
</div>
<div>
<label class="flex items-center space-x-2">
<input id="pushToggle" type="checkbox" class="accent-accent">
<span class="text-sm">Enable Push Notifications</span>
</label>
</div>
<div class="text-right">
<button id="saveSettings" class="px-4 py-2 bg-accent rounded hover:bg-opacity-80">Save</button>
</div>
</div>
</div>
<script src="https://unpkg.com/alpinejs@3.x.x/dist/cdn.min.js" defer></script>
<script>
// Service-Worker registration
if ('serviceWorker' in navigator) {
const swCode = `
self.addEventListener('push', e => {
const data = e.data?.json() ?? { title: 'Store AI', body: 'New message' };
e.waitUntil(
self.registration.showNotification(data.title, {
body: data.body,
icon: '/favicon.ico',
badge: '/favicon.ico'
})
);
});
`;
const blob = new Blob([swCode], { type: 'text/javascript' });
navigator.serviceWorker.register(URL.createObjectURL(blob))
.then(() => Notification.requestPermission());
}
// Push helper
function notify(title, body) {
if (Notification.permission === 'granted') {
navigator.serviceWorker.getRegistration().then(reg => {
reg?.showNotification(title, { body, icon: 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGNsYXNzPSJ3LTQgaC00IiBmaWxsPSIjZmZmIiB2aWV3Qm94PSIwIDAgNDQ4IDUxMiI+PHBhdGggZD0iTTIyNCAxMzZjMCA4LjgtNy4yIDE2LTE2IDE2cy0xNi03LjItMTYtMTZWNTEyYzAgOC44IDcuMiAxNiAxNiAxNnMxNi03LjIgMTYtMTZWMTM2em0tOTYgMGMwIDguOC03LjIgMTYtMTYgMTZzLTE2LTcuMi0xNi0xNlY1MTJjMCA4LjggNy4yIDE2IDE2IDE2czE2LTcuMiAxNi0xNlYxMzZ6bTMyMCAwYzAgOC44LTcuMiAxNi0xNiAxNnMtMTYtNy4yLTE2LTE2VjUxMmMwIDguOCA3LjIgMTYgMTYgMTZzMTYtNy4yIDE2LTE2VjEzNnoiLz48L3N2Zz4=' });
} else {
// fallback toast
const toast = document.createElement('div');
toast.className = 'fixed top-20 right-4 glass px-4 py-2 rounded shadow';
toast.textContent = `${title}: ${body}`;
document.body.appendChild(toast);
setTimeout(() => toast.remove(), 3000);
}
}
// Persisted state keys
const STORAGE_KEYS = ['lang', 'theme', 'accent', 'plugins', 'products', 'chat'];
// Default state
const storeAi = () => ({
theme: 'dark',
accent: '#38bdf8',
lang: 'en',
products: [],
openCmdK: false,
chatOpen: false,
emojisEnabled: false,
messages: [],
attachments: [],
recording: false,
mediaRec: null,
chunks: [],
init() {
// Load persisted state
STORAGE_KEYS.forEach(k => {
const val = localStorage.getItem(k);
if (val) { this[k] = k === 'products' ? JSON.parse(val) : val; }
});
this.applyTheme();
this.applyAccent();
this.loadProductsTable();
this.loadChat();
// Sidebar toggle
document.getElementById('openSidebar').addEventListener('click', () => {
document.getElementById('sidebar').classList.remove('-translate-x-full');
});
document.getElementById('closeSidebar').addEventListener('click', () => {
document.getElementById('sidebar').classList.add('-translate-x-full');
});
// Command Palette
document.addEventListener('keydown', e => {
if ((e.metaKey || e.ctrlKey) && e.key === 'k') {
e.preventDefault();
this.openCmdK = true;
this.$nextTick(() => this.$refs.cmdInput?.focus());
}
if (e.key === 'Escape') {
this.openCmdK = false;
this.chatOpen = false;
document.getElementById('settingsModal').classList.add('hidden');
}
});
// Floating buttons
document.getElementById('settingsFab').addEventListener('click', () => {
document.getElementById('settingsModal').classList.remove('hidden');
});
document.getElementById('closeSettings').addEventListener('click', () => {
document.getElementById('settingsModal').classList.add('hidden');
});
document.getElementById('addProductFab').addEventListener('click', () => this.openAddProductModal());
document.getElementById('chatToggleFab').addEventListener('click', () => this.toggleChat());
document.getElementById('closeChat').addEventListener('click', () => this.toggleChat());
document.getElementById('sendMessage').addEventListener('click', () => this.sendChatMessage());
document.getElementById('chatInput').addEventListener('keydown', e => {
if (e.key === 'Enter') this.sendChatMessage();
});
document.getElementById('emojiToggle').addEventListener('click', () => this.emojisEnabled = !this.emojisEnabled);
// Settings modal
document.getElementById('saveSettings').addEventListener('click', () => this.saveSettings());
document.getElementById('themeToggleBtn').addEventListener('click', () => {
this.theme = this.theme === 'dark' ? 'light' : 'dark';
this.applyTheme();
});
// Bulk import
document.getElementById('bulkImportBtn').addEventListener('click', () => {
const inp = document.createElement('input');
inp.type = 'file';
inp.accept = '.csv';
inp.onchange = e => {
const file = e.target.files[0];
const reader = new FileReader();
reader.onload = () => {
const lines = reader.result.split('\n').slice(1);
const prods = lines.map(l => {
const [name, sku, price, stock] = l.split(',');
return { name, sku, price: parseFloat(price), stock: parseInt(stock) };
}).filter(p => p.name);
this.products = [...this.products, ...prods];
this.saveProducts();
};
reader.readAsText(file);
};
inp.click();
});
// Observer for nav highlighting
const sections = document.querySelectorAll('section[id]');
const navLinks = document.querySelectorAll('nav a');
const observer = new IntersectionObserver(entries => {
entries.forEach(entry => {
if (entry.isIntersecting) {
navLinks.forEach(link => link.classList.toggle('bg-slate-700/50', link.getAttribute('href') === `#${entry.target.id}`));
}
});
}, { threshold: 0.4 });
sections.forEach(s => observer.observe(s));
},
applyTheme() {
document.documentElement.classList.toggle('dark', this.theme === 'dark');
localStorage.setItem('theme', this.theme);
},
applyAccent() {
document.documentElement.style.setProperty('--accent', this.accent);
localStorage.setItem('accent', this.accent);
},
saveSettings() {
this.accent = document.getElementById('accentColorPicker').value;
this.applyAccent();
// Push toggle
const pushOn = document.getElementById('pushToggle').checked;
if (pushOn && Notification.permission !== 'granted') {
Notification.requestPermission().then(p => {
if (p === 'granted') notify('Push enabled', 'You will receive system notifications.');
});
}
document.getElementById('settingsModal').classList.add('hidden');
},
openAddProductModal() {
const name = prompt('Product Name');
const sku = prompt('SKU');
const price = parseFloat(prompt('Price'));
const stock = parseInt(prompt('Stock'));
if (name && sku && !isNaN(price) && !isNaN(stock)) {
this.products.push({ name, sku, price, stock });
this.saveProducts();
}
},
saveProducts() {
localStorage.setItem('products', JSON.stringify(this.products));
this.products.forEach(p => {
if (p.stock < 10) notify('Low stock', `SKU ${p.sku} has ${p.stock} left`);
});
this.loadProductsTable();
},
loadProductsTable() {
const tbody = document.querySelector('#productsTable tbody');
tbody.innerHTML = '';
this.products.forEach(p => {
const tr = document.createElement('tr');
tr.className = 'border-t border-slate-700 hover:bg-slate-800/50';
tr.innerHTML = `
<td class="p-3">${p.name}</td>
<td class="p-3">${p.sku}</td>
<td class="p-3">${p.price.toFixed(2)}</td>
<td class="p-3">
<span class="px-2 py-1 rounded-full text-xs ${p.stock < 10 ? 'bg-red-600' : 'bg-emerald-600'}">${p.stock}</span>
</td>
<td class="p-3">
<span class="text-xs px-2 py-1 rounded-full bg-slate-700">Active</span>
</td>
`;
tbody.appendChild(tr);
});
},
toggleChat() {
this.chatOpen = !this.chatOpen;
const panel = document.getElementById('chatPanel');
panel.classList.toggle('translate-y-full', !this.chatOpen);
panel.classList.toggle('translate-y-0', this.chatOpen);
if (this.chatOpen) this.scrollToBottom();
},
sendChatMessage() {
const input = document.getElementById('chatInput');
const text = input.value.trim();
if (!text && this.attachments.length === 0) return;
const msg = {
role: 'user',
content: text,
attachments: [...this.attachments]
};
this.messages.push(msg);
this.messages.push({ role: 'assistant', content: '...', emojis: this.emojisEnabled ? ['🤖'] : [] });
input.value = '';
this.attachments = [];
this.saveChat();
this.scrollToBottom();
// Simulate response
setTimeout(() => {
const last = this.messages[this.messages.length - 1];
last.content = 'Got your message! Anything else?';
this.saveChat();
if (!this.chatOpen) notify('Store AI', last.content);
}, 500);
},
scrollToBottom() {
this.$nextTick(() => {
const box = document.getElementById('chatMessages');
box.scrollTop = box.scrollHeight;
});
},
saveChat() {
localStorage.setItem('chat', JSON.stringify(this.messages));
},
loadChat() {
this.messages = JSON.parse(localStorage.getItem('chat') || '[]');
this.$nextTick(() => {
const box = document.getElementById('chatMessages');
box.innerHTML = '';
this.messages.forEach(m => {
const div = document.createElement('div');
div.className = `flex ${m.role === 'user' ? 'justify-end' : 'justify-start'}`;
let attachHTML = '';
(m.attachments || []).forEach(f => {
const url = URL.createObjectURL(f);
if (f.type.startsWith('image/')) {
attachHTML += `<img src="${url}" class="rounded mb-1 max-h-32 cursor-pointer" onclick="window.open('${url}')"/>`;
} else if (f.type === 'application/pdf') {
attachHTML += `<a href="${url}" download="${f.name}" class="text-sm underline flex items-center"><i class="fas fa-file mr-1"></i>${f.name}</a>`;
} else if (f.type.startsWith('audio/')) {
attachHTML += `<audio controls src="${url}" class="w-full"></audio>`;
}
});
div.innerHTML = `
<div class="max-w-[70%] px-3 py-2 rounded-xl ${
m.role === 'user' ? 'bg-accent text-white' : 'glass'
}">
<span>${m.content}</span>
${m.emojis?.length ? `<div class="text-xs mt-1">${m.emojis.join('')}</div>` : ''}
<div class="mt-1">${attachHTML}</div>
</div>
`;
box.appendChild(div);
});
});
},
handleFileUpload(e) {
[...e.target.files].forEach(f => this.attachments.push(f));
},
toggleRecord() {
if (!this.recording) {
navigator.mediaDevices.getUserMedia({ audio: true }).then(stream => {
this.mediaRec = new MediaRecorder(stream);
this.mediaRec.ondataavailable = ev => this.chunks.push(ev.data);
this.mediaRec.onstop = () => {
const blob = new Blob(this.chunks, { type: 'audio/webm' });
this.attachments.push(blob);
this.chunks = [];
};
this.mediaRec.start();
this.recording = true;
});
} else {
this.mediaRec.stop();
this.recording = false;
}
},
// Command palette handler
runCommand(cmd) {
cmd = cmd.toLowerCase();
if (cmd.includes('add product')) this.openAddProductModal();
if (cmd.includes('bulk import')) document.getElementById('bulkImportBtn').click();
if (cmd.includes('open n8n')) document.getElementById('n8n').classList.remove('hidden');
if (cmd.startsWith('find sku')) {
const sku = cmd.split('sku')[1]?.trim();
if (sku) {
const row = [...document.querySelectorAll('#productsTable tbody tr')].find(tr => tr.children[1].textContent === sku);
row?.scrollIntoView({ behavior: 'smooth' });
row?.classList.add('ring', 'ring-accent');
setTimeout(() => row?.classList.remove('ring', 'ring-accent'), 2000);
}
}
}
});
Alpine.data('storeAi', storeAi);
// --- Push Notification helpers ---
function notify(title, body) {
if (Notification.permission === 'granted') {
navigator.serviceWorker.getRegistration().then(reg => {
if (reg) reg.showNotification(title, { body, icon: 'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/svgs/solid/bell.svg' });
});
} else {
// fallback toast
const toast = document.createElement('div');
toast.className = 'fixed top-20 right-4 glass px-4 py-2 rounded shadow text-sm';
toast.textContent = `${title}: ${body}`;
document.body.appendChild(toast);
setTimeout(() => toast.remove(), 3000);
}
}
// --- File & voice handlers inside Alpine ---
document.getElementById('filePicker').addEventListener('change', e => {
[...e.target.files].forEach(f => Alpine.store('storeAi').attachments.push(f));
});
document.getElementById('recordBtn').addEventListener('click', () => Alpine.store('storeAi').toggleRecord());
// --- Inline Service-Worker registration ---
if ('serviceWorker' in navigator) {
const swCode = `
self.addEventListener('push', e => {
const data = e.data?.json() || { title: 'Store AI', body: 'New message' };
e.waitUntil(
self.registration.showNotification(data.title, {
body: data.body,
icon: 'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/svgs/solid/bell.svg'
})
);
});
`;
const blob = new Blob([swCode], { type: 'text/javascript' });
navigator.serviceWorker.register(URL.createObjectURL(blob));
}
</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=fsalmansour/store1" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
</html>