Spaces:
Running
Running
✅ Current state of the prompt Area What’s already in the file Core layout Single-page dark UI (glassmorphism) with side-rail navigation, top bar, KPI cards, What-If, Models, Stores, AI charts, Integrations. Products module Bulk-import (CSV → array), live stock-badge (< 10 turns red), quick-add FAB ( wired to Add Modal), searchable via the command palette (add product, find SKU 123). Integrations Grid filtered by type (Ads, Stores, Email, Automation). Each card supports Connect / Disconnect via dynamic modal and saves state to localStorage. n8n Automations Table + actions (activate/deactivate, run, logs) backed by your /api/automations façade → n8n REST endpoints. Web-socket hooks are ready for live toasts. Command palette Global CMD-K with fuzzy actions for products, models, stores, automations. Accessibility & theming Keyboard-focusable, ARIA labels on floating buttons, custom accent color & theme persistence. Data persistence Everything (theme, lang, plugins, products, stock, workflows) stored or cached in localStorage until you wire a real backend. Charts Lazy-initialised Chart.js (bar + doughnut) and colour-synced to --accent. ⸻ 🔎 What’s left to make it production-ready Task Notes Backend endpoints /api/products, /api/stock-alerts, /api/automations, /api/plugins. Mocked now → swap with real API gateway / n8n proxy. Auth Add JWT / session handling around every fetch call (currently none). Validation & error UI Forms and connect modals assume success. Add try/catch + toast on failure. i18n cleanup Arabic bundle can be removed if you’ve decided on English-only. Security headers CSP, SRI for CDNs, referrer-policy, etc. Performance Purge unused Tailwind classes in build step (@tailwindcss/jit or content: purge). Unit tests Cypress/Playwright for the dashboard flows, Vitest for stores/models helpers. ⸻ 🚀 Next steps 1. Wire the real API • Drop-in replace the fake fetch calls with your gateway URLs. • Pass the n8n token server-side and expose only minimal routes to the browser. 2. Run an integration smoke-test • Import a CSV with 50 products → check badge colours. • Connect WooCommerce sandbox → verify webhook fires and appears in Automations. • Trigger a TikTok-Ads workflow via the dashboard and watch live logs. 3. Deploy preview • Build with tailwindcss -m and host on Vercel / Netlify for quick stakeholder review. ⸻ ℹ️ Anything else? If the checklist above looks good, yes, you’re done with the prompt and can move on to implementation. Need something tweaked or extra (PWA install, multi-tenant routing, light theme, etc.)? Just let me know and we’ll bolt it on before you cut code. - Initial Deployment
a85d356 verified | <html lang="en" x-data="storeAi" :dir="dir" :class="theme"> | |
| <head> | |
| <meta charset="UTF-8"/> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"/> | |
| <title>Store AI – Control Center</title> | |
| =========== FLOATING ACTION CLUSTER ================== --> | |
| <div class="fixed bottom-6 right-6 flex flex-col items-end space-y-3"> | |
| <button @click="openSettings=true" aria-label="Open settings" | |
| class="glass p-3 rounded-full shadow-lg fab-shadow transition hover:scale-110 tooltip" | |
| :title="t.settings"> | |
| <i class="fas fa-cog"></i> | |
| <span class="tooltip-text" x-text="t.settings"></span> | |
| </button> | |
| <button @click="openAdd=true" aria-label="Add new integration" | |
| class="glass p-4 rounded-full shadow-lg fab-shadow transition hover:scale-110 neon"> | |
| <i class="fas fa-plus"></i> | |
| </button> | |
| </div> | |
| ================== FLOATING ACTION CLUSTER ================== --> | |
| <div class="fixed bottom-6 right-6 flex flex-col items-end space-y-3"> | |
| <button @click="openSettings=true" aria-label="Open settings" | |
| class="glass p-3 rounded-full shadow-lg fab-shadow transition hover:scale-110 tooltip" | |
| :title="t.settings"> | |
| <i class="fas fa-cog"></i> | |
| <span class="tooltip-text" x-text="t.settings"></span> | |
| </button> | |
| <button @click="openAdd=true" aria-label="Add new integration" | |
| class="glass p-4 rounded-full shadow-lg fab-shadow transition hover:scale-110 neon"> | |
| <i class="fas fa-plus"></i> | |
| </button> | |
| </div> | |
| ======= | |
| <!-- ================== FLOATING ACTION CLUSTER ================== --> | |
| <div class="fixed bottom-6 right-6 flex flex-col items-end space-y-3"> | |
| <button @click="openSettings=true" aria-label="Open settings" | |
| class="glass p-3 rounded-full shadow-lg fab-shadow transition hover:scale-110 tooltip" | |
| :title="t.settings"> | |
| <i class="fas fa-cog"></i> | |
| <span class="tooltip-text" x-text="t.settings"></span> | |
| </button> | |
| <button @click="openProductModal=true" aria-label="Add product" | |
| class="glass p-3 rounded-full shadow-lg fab-shadow transition hover:scale-110 tooltip" | |
| :title="t.addProduct"> | |
| <i class="fas fa-box-open"></i> | |
| <span class="tooltip-text" x-text="t.addProduct"></span> | |
| </button> | |
| <button @click="openAdd=true" aria-label="Add new integration" | |
| class="glass p-4 rounded-full shadow-lg fab-shadow transition hover:scale-110 neon"> | |
| <i class="fas fa-plus"></i> | |
| </button> | |
| </div> | |
| ================== FLOATING ACTION CLUSTER ================== --> | |
| <div class="fixed bottom-6 right-6 flex flex-col items-end space-y-3"> | |
| <button @click="openSettings=true" aria-label="Open settings" | |
| class="glass p-3 rounded-full shadow-lg fab-shadow transition hover:scale-110 tooltip" | |
| :title="t.settings"> | |
| <i class="fas fa-cog"></i> | |
| <span class="tooltip-text" x-text="t.settings"></span> | |
| </button> | |
| <button @click="openAddProduct=true" aria-label="Add product" | |
| class="glass p-3 rounded-full shadow-lg fab-shadow transition hover:scale-110 tooltip" | |
| :title="t.addProduct"> | |
| <i class="fas fa-box-open"></i> | |
| <span class="tooltip-text" x-text="t.addProduct"></span> | |
| </button> | |
| <button @click="openAdd=true" aria-label="Add new integration" | |
| class="glass p-4 rounded-full shadow-lg fab-shadow transition hover:scale-110 neon"> | |
| <i class="fas fa-plus"></i> | |
| </button> | |
| </div> | |
| TailwindCSS CDN --> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <script> | |
| tailwind.config = { | |
| darkMode: 'class', | |
| theme: { | |
| extend: { | |
| colors: { | |
| accent: 'var(--accent)', | |
| primary: { | |
| 50: '#f0f9ff', | |
| 100: '#e0f2fe', | |
| 200: '#bae6fd', | |
| 300: '#7dd3fc', | |
| 400: '#38bdf8', | |
| 500: '#0ea5e9', | |
| 600: '#0284c7', | |
| 700: '#0369a1', | |
| 800: '#075985', | |
| 900: '#0c4a6e', | |
| } | |
| } | |
| } | |
| } | |
| } | |
| </script> | |
| <!-- Fonts & Icons --> | |
| <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&display=swap" rel="stylesheet"/> | |
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"/> | |
| <style> | |
| :root { | |
| --accent: #38bdf8; | |
| } | |
| body { | |
| font-family: 'Inter', sans-serif; | |
| background: #111827; | |
| color: #e5e7eb; | |
| } | |
| .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; | |
| } | |
| .fab-shadow { | |
| box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4); | |
| } | |
| .slide-enter { | |
| animation: slide 0.3s ease-out forwards; | |
| } | |
| @keyframes slide { | |
| from { transform: translateX(-100%); } | |
| to { transform: translateX(0); } | |
| } | |
| .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); | |
| } | |
| .neon { | |
| border: 1px solid var(--accent); | |
| box-shadow: 0 0 6px var(--accent); | |
| } | |
| .progress-ring { | |
| transform: rotate(-90deg); | |
| } | |
| .progress-ring-circle { | |
| stroke-dasharray: 283; | |
| stroke-dashoffset: 283; | |
| transition: stroke-dashoffset 0.5s; | |
| } | |
| .tooltip { | |
| position: relative; | |
| } | |
| .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; | |
| } | |
| .animate-pulse { | |
| animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite; | |
| } | |
| @keyframes pulse { | |
| 0%, 100% { opacity: 1; } | |
| 50% { opacity: 0.5; } | |
| } | |
| .loading-bar { | |
| position: relative; | |
| overflow: hidden; | |
| } | |
| .loading-bar::after { | |
| content: ''; | |
| position: absolute; | |
| top: 0; | |
| left: 0; | |
| right: 0; | |
| bottom: 0; | |
| background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent); | |
| animation: loading 1.5s infinite; | |
| } | |
| @keyframes loading { | |
| 0% { transform: translateX(-100%); } | |
| 100% { transform: translateX(100%); } | |
| } | |
| </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 rtl:space-x-reverse"> | |
| <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 @click="openCmdK = true" | |
| 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 x-text="t.searchCmd"></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 @click="toggleLang()" | |
| class="text-sm font-semibold hover:opacity-80 transition" | |
| :title="currentLang==='en'?'Switch to Arabic':'Switch to English'"> | |
| <span x-text="currentLang==='en'?'العربية':'EN'"></span> | |
| </button> | |
| <button @click="toggleTheme()" | |
| class="p-1.5 rounded-full hover:bg-slate-700 transition tooltip" | |
| :title="theme==='dark'?'Switch to Light Mode':'Switch to Dark Mode'"> | |
| <i class="fas text-yellow-300" :class="theme==='dark'?'fa-sun':'fa-moon'"></i> | |
| <span class="tooltip-text" x-text="theme==='dark'?t.lightMode:t.darkMode"></span> | |
| </button> | |
| <button x-show="window.webkitSpeechRecognition" @click="startVoice()" | |
| class="p-1.5 rounded-full hover:bg-slate-700 transition tooltip" | |
| :title="t.voiceCommand"> | |
| <i class="fas fa-microphone text-red-400"></i> | |
| <span class="tooltip-text" x-text="t.voiceCommand"></span> | |
| </button> | |
| <div class="relative"> | |
| <button @click="notificationsOpen = !notificationsOpen" | |
| class="p-1.5 rounded-full hover:bg-slate-700 transition relative"> | |
| <i class="fas fa-bell"></i> | |
| <span x-show="unreadNotifications > 0" | |
| class="absolute -top-1 -right-1 bg-red-500 text-white text-xs rounded-full h-4 w-4 flex items-center justify-center"> | |
| <span x-text="unreadNotifications"></span> | |
| </span> | |
| </button> | |
| <div x-show="notificationsOpen" @click.away="notificationsOpen = false" | |
| class="absolute right-0 mt-2 w-72 glass rounded-lg shadow-lg z-50 max-h-96 overflow-y-auto"> | |
| <div class="p-3 border-b border-slate-700 flex justify-between items-center"> | |
| <h3 class="font-bold" x-text="t.notifications"></h3> | |
| <button @click="markAllAsRead()" class="text-xs text-accent hover:underline" x-text="t.markAllRead"></button> | |
| </div> | |
| <template x-for="(note, index) in notifications" :key="index"> | |
| <div class="p-3 border-b border-slate-700 hover:bg-slate-700 cursor-pointer" | |
| :class="{'bg-slate-700/50': !note.read}" | |
| @click="markAsRead(index)"> | |
| <p class="text-sm" x-text="note.message"></p> | |
| <p class="text-xs text-slate-400 mt-1" x-text="note.time"></p> | |
| </div> | |
| </template> | |
| <div x-show="notifications.length === 0" class="p-4 text-center text-sm text-slate-400"> | |
| <p x-text="t.noNotifications"></p> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="relative"> | |
| <button @click="profileOpen = !profileOpen" | |
| 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 x-show="profileOpen" @click.away="profileOpen = false" | |
| class="absolute right-0 mt-2 w-48 glass rounded-lg shadow-lg z-50"> | |
| <div class="p-3 border-b border-slate-700"> | |
| <p class="font-medium" x-text="user.name"></p> | |
| <p class="text-xs text-slate-400" x-text="user.email"></p> | |
| </div> | |
| <div class="p-1"> | |
| <a href="#" class="block px-3 py-2 text-sm hover:bg-slate-700 rounded" x-text="t.profile"></a> | |
| <a href="#" class="block px-3 py-2 text-sm hover:bg-slate-700 rounded" x-text="t.settings"></a> | |
| <button @click="logout()" class="block w-full text-left px-3 py-2 text-sm hover:bg-slate-700 rounded text-red-400" x-text="t.logout"></button> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </header> | |
| <!-- ================== SIDE RAIL ================== --> | |
| <aside x-show="sideOpen" @click.away="sideOpen=false" | |
| 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 transition-transform duration-300" | |
| :class="sideOpen ? 'translate-x-0' : '-translate-x-full'"> | |
| <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 @click="sideOpen = false" 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" x-text="t.plan"></span> | |
| <span class="text-xs px-2 py-1 bg-emerald-600/20 text-emerald-400 rounded-full" x-text="user.plan"></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: ' + user.usage + '%'"></div> | |
| </div> | |
| <div class="flex justify-between text-xs text-slate-400"> | |
| <span x-text="user.usage + '% ' + t.used"></span> | |
| <span x-text="t.remaining + ' ' + (100 - user.usage) + '%'"></span> | |
| </div> | |
| </div> | |
| </div> | |
| <nav class="space-y-1"> | |
| <template x-for="(item,i) in navItems" :key="item.label"> | |
| <a :href="'#'+item.id" | |
| @click="scrollToSection(item.id)" | |
| class="flex items-center space-x-3 rtl:space-x-reverse px-3 py-2 rounded-md hover:bg-slate-700 transition" | |
| :class="activeSection===item.id?'bg-slate-700/50':''"> | |
| <i :class="item.icon + ' text-sm w-5 text-center'"></i> | |
| <span x-text="item.label"></span> | |
| <span x-show="item.badge" class="ml-auto text-xs px-2 py-1 rounded-full bg-accent/20 text-accent" x-text="item.badge"></span> | |
| </a> | |
| </template> | |
| </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" x-text="t.needHelp"></p> | |
| <button @click="openSupport = true" class="text-xs text-accent font-medium hover:underline" x-text="t.contactSupport"></button> | |
| </div> | |
| </div> | |
| </aside> | |
| <!-- mobile hamburger --> | |
| <button @click="sideOpen=!sideOpen" | |
| 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" x-text="t.dashboard"></h2> | |
| <div class="flex items-center space-x-2"> | |
| <div class="relative"> | |
| <select x-model="timeRange" class="glass text-sm px-3 py-1.5 rounded appearance-none pr-7"> | |
| <option value="today" x-text="t.today"></option> | |
| <option value="week" x-text="t.thisWeek"></option> | |
| <option value="month" x-text="t.thisMonth"></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 @click="refreshData()" class="glass p-1.5 rounded hover:bg-slate-700 transition tooltip" :title="t.refresh"> | |
| <i class="fas fa-sync-alt text-sm" :class="{'animate-spin': isRefreshing}"></i> | |
| <span class="tooltip-text" x-text="t.refresh"></span> | |
| </button> | |
| </div> | |
| </div> | |
| <div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4"> | |
| <template x-for="(k,i) in kpis" :key="k.label"> | |
| <div class="kpi-card glass p-4 rounded-xl" :class="{'animate-pulse': k.loading}"> | |
| <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="k.icon + ' text-accent text-sm'"></i> | |
| </div> | |
| <span class="text-sm" x-text="k.label"></span> | |
| </div> | |
| <button @click="showKpiDetails(k)" 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" x-text="k.value"></p> | |
| <div class="flex items-center"> | |
| <span class="text-xs" :class="k.trend > 0 ? 'text-emerald-400' : 'text-red-400'"> | |
| <i :class="k.trend > 0 ? 'fas fa-caret-up' : 'fas fa-caret-down'"></i> | |
| <span x-text="Math.abs(k.trend) + '%'"></span> | |
| </span> | |
| <span class="text-xs text-slate-400 ml-2" x-text="'vs ' + (timeRange === 'today' ? t.yesterday : timeRange === 'week' ? t.lastWeek : t.lastMonth)"></span> | |
| </div> | |
| </div> | |
| </template> | |
| </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" x-text="t.conversionRate"></span> | |
| <span class="text-xs px-2 py-1 bg-emerald-600/20 text-emerald-400 rounded-full" x-text="'+2.4%'"></span> | |
| </div> | |
| <div class="h-24"> | |
| <canvas id="miniChart1"></canvas> | |
| </div> | |
| </div> | |
| <div class="glass rounded-xl p-4"> | |
| <div class="flex items-center justify-between mb-2"> | |
| <span class="text-sm" x-text="t.avgOrderValue"></span> | |
| <span class="text-xs px-2 py-1 bg-red-600/20 text-red-400 rounded-full" x-text="'-1.2%'"></span> | |
| </div> | |
| <div class="h-24"> | |
| <canvas id="miniChart2"></canvas> | |
| </div> | |
| </div> | |
| <div class="glass rounded-xl p-4"> | |
| <div class="flex items-center justify-between mb-2"> | |
| <span class="text-sm" x-text="t.newCustomers"></span> | |
| <span class="text-xs px-2 py-1 bg-emerald-600/20 text-emerald-400 rounded-full" x-text="'+8.7%'"></span> | |
| </div> | |
| <div class="h-24"> | |
| <canvas id="miniChart3"></canvas> | |
| </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" x-text="t.whatIf"></h2> | |
| <button @click="saveScenario()" class="text-sm glass px-3 py-1 rounded hover:bg-slate-700" x-text="t.saveScenario"></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 x-text="t.discount"></span> | |
| <span class="text-xs text-slate-400" x-text="'Current: ' + currentDiscount + '%'"></span> | |
| </label> | |
| <input type="range" min="0" max="50" x-model="discount" class="w-full accent-accent mb-1"> | |
| <div class="flex justify-between text-xs text-slate-400"> | |
| <span>0%</span> | |
| <span x-text="discount + '%'"></span> | |
| <span>50%</span> | |
| </div> | |
| </div> | |
| <div> | |
| <label class="text-sm flex items-center justify-between"> | |
| <span x-text="t.budget"></span> | |
| <span class="text-xs text-slate-400" x-text="'Current: $' + currentBudget"></span> | |
| </label> | |
| <input type="range" min="0" max="2000" step="50" x-model="budget" class="w-full accent-accent mb-1"> | |
| <div class="flex justify-between text-xs text-slate-400"> | |
| <span>$0</span> | |
| <span x-text="'$' + budget"></span> | |
| <span>$2000</span> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="glass rounded-xl p-5"> | |
| <h3 class="text-sm font-medium mb-3" x-text="t.projectedResults"></h3> | |
| <div class="space-y-3"> | |
| <div class="flex items-center justify-between"> | |
| <span class="text-sm" x-text="t.revenue"></span> | |
| <span class="font-medium" x-text="'$' + ((budget*2.15)-(discount*36)).toFixed(0)"></span> | |
| </div> | |
| <div class="flex items-center justify-between"> | |
| <span class="text-sm" x-text="t.profit"></span> | |
| <span class="font-medium" x-text="'$' + ((budget*1.45)-(discount*28)).toFixed(0)"></span> | |
| </div> | |
| <div class="flex items-center justify-between"> | |
| <span class="text-sm" x-text="t.newCustomers"></span> | |
| <span class="font-medium" x-text="Math.round(budget/25)"></span> | |
| </div> | |
| <div class="flex items-center justify-between"> | |
| <span class="text-sm" x-text="t.roi"></span> | |
| <span class="font-medium" x-text="Math.round(((budget*1.45)-(discount*28))/budget*100) + '%'"></span> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="glass rounded-xl p-5"> | |
| <h3 class="text-sm font-medium mb-3" x-text="t.savedScenarios"></h3> | |
| <div class="space-y-2"> | |
| <template x-for="(scenario, index) in savedScenarios" :key="index"> | |
| <div class="flex items-center justify-between p-2 hover:bg-slate-700 rounded cursor-pointer" @click="loadScenario(index)"> | |
| <div> | |
| <p class="text-sm font-medium" x-text="scenario.name"></p> | |
| <p class="text-xs text-slate-400" x-text="'Discount: ' + scenario.discount + '% | Budget: $' + scenario.budget"></p> | |
| </div> | |
| <button @click.stop="deleteScenario(index)" class="text-red-400 hover:text-red-300 p-1"> | |
| <i class="fas fa-trash text-xs"></i> | |
| </button> | |
| </div> | |
| </template> | |
| <div x-show="savedScenarios.length === 0" class="text-center py-4 text-sm text-slate-400"> | |
| <p x-text="t.noScenarios"></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" x-text="t.models"></h2> | |
| <div class="flex items-center space-x-2"> | |
| <button @click="openAddModel = true" 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 x-text="t.addModel"></span> | |
| </button> | |
| <div class="relative"> | |
| <select x-model="modelFilter" class="glass text-sm px-3 py-1 rounded appearance-none pr-7"> | |
| <option value="all" x-text="t.allModels"></option> | |
| <option value="active" x-text="t.active"></option> | |
| <option value="inactive" x-text="t.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" x-text="t.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" x-text="t.action"></th> | |
| </tr> | |
| </thead> | |
| <tbody> | |
| <template x-for="m in filteredModels" :key="m.name"> | |
| <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="m.icon + ' text-accent'"></i> | |
| <span x-text="m.name"></span> | |
| </div> | |
| </td> | |
| <td class="p-3" x-text="m.provider"></td> | |
| <td class="p-3" x-text="m.size"></td> | |
| <td class="p-3"> | |
| <span class="px-2 py-1 rounded-full text-xs" | |
| :class="m.active ? 'bg-emerald-600/20 text-emerald-400' : 'bg-slate-700/50 text-slate-400'" | |
| x-text="m.active ? t.active : t.inactive"></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: ' + m.usage + '%'"></div> | |
| </div> | |
| </td> | |
| <td class="p-3"> | |
| <div class="flex items-center space-x-2"> | |
| <button @click="toggleModelStatus(m)" | |
| class="text-xs px-2 py-1 rounded hover:bg-slate-700" | |
| :class="m.active ? 'text-red-400' : 'text-emerald-400'" | |
| x-text="m.active ? t.deactivate : t.activate"></button> | |
| <button @click="removeModel(m)" class="text-xs px-2 py-1 rounded hover:bg-slate-700 text-slate-400" x-text="t.remove"></button> | |
| </div> | |
| </td> | |
| </tr> | |
| </template> | |
| </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" x-text="t.stores"></h2> | |
| <div class="flex items-center space-x-2"> | |
| <button @click="openAddStore = true" 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 x-text="t.addStore"></span> | |
| </button> | |
| <div class="relative"> | |
| <select x-model="storeFilter" class="glass text-sm px-3 py-1 rounded appearance-none pr-7"> | |
| <option value="all" x-text="t.allStores"></option> | |
| <option value="healthy" x-text="t.healthy"></option> | |
| <option value="warning" x-text="t.warning"></option> | |
| <option value="critical" x-text="t.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" x-text="t.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" x-text="t.action"></th> | |
| </tr> | |
| </thead> | |
| <tbody> | |
| <template x-for="s in filteredStores" :key="s.name"> | |
| <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="s.icon + ' text-accent'"></i> | |
| <span x-text="s.name"></span> | |
| </div> | |
| </td> | |
| <td class="p-3" x-text="s.platform"></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="currentColor" | |
| fill="transparent" | |
| r="45" | |
| cx="50" | |
| cy="50" | |
| :stroke="s.health > 90 ? '#10b981' : s.health > 70 ? '#f59e0b' : '#ef4444'" | |
| :stroke-dashoffset="283 - (283 * s.health / 100)" /> | |
| </svg> | |
| <span class="absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 text-xs font-bold" | |
| :class="s.health > 90 ? 'text-emerald-400' : s.health > 70 ? 'text-yellow-400' : 'text-red-400'" | |
| x-text="s.health + '%'"></span> | |
| </div> | |
| <span class="text-xs" :class="s.health > 90 ? 'text-emerald-400' : s.health > 70 ? 'text-yellow-400' : 'text-red-400'"> | |
| <i :class="s.trend > 0 ? 'fas fa-caret-up' : 'fas fa-caret-down'"></i> | |
| <span x-text="Math.abs(s.trend) + '%'"></span> | |
| </span> | |
| </div> | |
| </td> | |
| <td class="p-3 text-xs text-slate-400" x-text="formatDate(s.lastSync)"></td> | |
| <td class="p-3"> | |
| <div class="flex items-center space-x-2"> | |
| <button @click="syncStore(s)" class="text-xs px-2 py-1 rounded hover:bg-slate-700 text-accent" x-text="t.sync"></button> | |
| <button @click="inspectStore(s)" class="text-xs px-2 py-1 rounded hover:bg-slate-700" x-text="t.inspect"></button> | |
| </div> | |
| </td> | |
| </tr> | |
| </template> | |
| </tbody> | |
| </table> | |
| </div> | |
| </section> | |
| <!-- PRODUCTS TABLE --> | |
| <section id="products" class="scroll-mt-20"> | |
| <h2 class="text-xl font-bold mb-4">Products</h2> | |
| <!-- Bulk-Import Button --> | |
| <div class="mb-3 flex items-center space-x-3"> | |
| <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" | |
| @change="importCSV($event)" class="hidden"> | |
| </label> | |
| </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> | |
| <template x-for="p in products" :key="p.id"> | |
| <tr class="border-t border-slate-700"> | |
| <td class="p-3" x-text="p.id"></td> | |
| <td class="p-3" x-text="p.name"></td> | |
| <td class="p-3" x-text="p.sku"></td> | |
| <td class="p-3" x-text="' <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 x-model="chartTimeRange" class="glass text-sm px-3 py-1 rounded appearance-none pr-7"> | |
| <option value="7d" x-text="t.last7Days"></option> | |
| <option value="30d" x-text="t.last30Days"></option> | |
| <option value="90d" x-text="t.last90Days"></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" x-text="t.apiUsage"></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" x-text="t.thisMonth"></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" x-text="t.lastMonth"></span> | |
| </div> | |
| </div> | |
| </div> | |
| <canvas id="apiChart" height="250"></canvas> | |
| </div> | |
| <div class="glass rounded-xl p-4"> | |
| <div class="flex items-center justify-between mb-4"> | |
| <h3 class="font-medium" x-text="t.providerDistribution"></h3> | |
| <div class="relative"> | |
| <select x-model="distributionFilter" class="glass text-xs px-2 py-1 rounded appearance-none pr-6"> | |
| <option value="requests" x-text="t.requests"></option> | |
| <option value="cost" x-text="t.cost"></option> | |
| <option value="tokens" x-text="t.tokens"></option> | |
| </select> | |
| <i class="fas fa-chevron-down absolute right-1 top-1/2 transform -translate-y-1/2 text-xs pointer-events-none"></i> | |
| </div> | |
| </div> | |
| <canvas id="providerChart" height="250"></canvas> | |
| </div> | |
| </div> | |
| <!-- Cost Summary --> | |
| <div class="glass rounded-xl p-4 mt-6"> | |
| <h3 class="font-medium mb-4" x-text="t.costSummary"></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" x-text="t.thisMonth"></p> | |
| <p class="text-xl font-bold" x-text="'$' + costs.current"></p> | |
| </div> | |
| <div class="bg-slate-800/50 rounded-lg p-3"> | |
| <p class="text-sm text-slate-400 mb-1" x-text="t.lastMonth"></p> | |
| <p class="text-xl font-bold" x-text="'$' + costs.last"></p> | |
| </div> | |
| <div class="bg-slate-800/50 rounded-lg p-3"> | |
| <p class="text-sm text-slate-400 mb-1" x-text="t.change"></p> | |
| <p class="text-xl font-bold" :class="costs.change > 0 ? 'text-emerald-400' : 'text-red-400'" | |
| x-text="(costs.change > 0 ? '+' : '') + costs.change + '%'"></p> | |
| </div> | |
| <div class="bg-slate-800/50 rounded-lg p-3"> | |
| <p class="text-sm text-slate-400 mb-1" x-text="t.forecast"></p> | |
| <p class="text-xl font-bold" x-text="'$' + costs.forecast"></p> | |
| </div> | |
| </div> | |
| </div> | |
| </section> | |
| <!-- ADS INTEGRATIONS --> | |
| <section id="ads" class="scroll-mt-20"> | |
| <div class="flex items-center justify-between mb-4"> | |
| <h2 class="text-xl font-bold" x-text="t.ads"></h2> | |
| <button @click="openAddIntegration = true" 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 x-text="t.addIntegration"></span> | |
| </button> | |
| </div> | |
| <!-- Filter Buttons --> | |
| <div class="flex flex-wrap gap-2 mb-4"> | |
| <template x-for="f in ['All','Ads','Stores','Email','Automation']" :key="f"> | |
| <button @click="filter=f.toLowerCase()" | |
| class="px-3 py-1 rounded-md text-sm transition" | |
| :class="filter===f.toLowerCase()?'bg-accent text-white':'glass hover:bg-slate-700'" | |
| x-text="f"></button> | |
| </template> | |
| </div> | |
| <!-- Plugins Grid --> | |
| <div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-4"> | |
| <template x-for="p in filteredPlugins" :key="p.id"> | |
| <div class="glass rounded-xl p-4 hover:bg-slate-700/50 transition cursor-pointer" @click="openConnectModal(p)"> | |
| <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="p.icon + ' text-accent text-lg'"></i> | |
| </div> | |
| <span class="text-xs px-2 py-1 rounded-full" | |
| :class="p.connected ? 'bg-emerald-600/20 text-emerald-400' : 'bg-slate-700/50 text-slate-400'" | |
| x-text="p.connected ? 'Connected' : 'Disconnected'"></span> | |
| </div> | |
| <p class="font-semibold mb-1" x-text="p.name"></p> | |
| <p class="text-xs text-slate-400 mb-3" x-text="p.description"></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: ' + (p.connected ? '100' : '0') + '%'"></div> | |
| </div> | |
| <div class="flex justify-between text-xs text-slate-400"> | |
| <span x-text="p.connected ? 'Connected' : 'Not connected'"></span> | |
| <span x-text="p.type === 'ads' ? 'Ads' : p.type === 'e-commerce' ? 'E-commerce' : p.type === 'email' ? 'Email' : 'Automation'"></span> | |
| </div> | |
| </div> | |
| </template> | |
| </div> | |
| </section> | |
| </main> | |
| <!-- ================== FLOATING ACTION CLUSTER ================== --> | |
| <div class="fixed bottom-6 right-6 flex flex-col items-end space-y-3"> | |
| <button @click="openSettings=true" aria-label="Open settings" | |
| class="glass p-3 rounded-full shadow-lg fab-shadow transition hover:scale-110 tooltip" | |
| :title="t.settings"> | |
| <i class="fas fa-cog"></i> | |
| <span class="tooltip-text" x-text="t.settings"></span> | |
| </button> | |
| <button @click="openAdd=true" aria-label="Add new integration" | |
| class="glass p-4 rounded-full shadow-lg fab-shadow transition hover:scale-110 neon"> | |
| <i class="fas fa-plus"></i> | |
| </button> | |
| </div> | |
| <!-- ================== MODALS ================== --> | |
| <!-- Command Palette --> | |
| <div x-show="openCmdK" @click.away="openCmdK=false" | |
| class="fixed inset-0 bg-black/50 z-50 flex 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" | |
| x-ref="cmdInput" | |
| @keydown.escape="openCmdK=false" | |
| @keyup="filterCommands($event.target.value)"> | |
| </div> | |
| <div class="max-h-96 overflow-y-auto"> | |
| <template x-for="cmd in filteredCommands" :key="cmd.label"> | |
| <button @click="executeCommand(cmd)" | |
| class="w-full text-left p-3 hover:bg-slate-700 flex items-center space-x-3"> | |
| <i :class="cmd.icon + ' text-sm w-5 text-center text-slate-400'"></i> | |
| <div> | |
| <p x-text="cmd.label"></p> | |
| <p class="text-xs text-slate-400" x-text="cmd.description"></p> | |
| </div> | |
| <kbd class="ml-auto text-xs px-2 py-1 bg-slate-700 rounded" x-text="cmd.shortcut"></kbd> | |
| </button> | |
| </template> | |
| <div x-show="filteredCommands.length === 0" class="p-4 text-center text-sm text-slate-400"> | |
| <p x-text="t.noCommands"></p> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Add Modal --> | |
| <div x-show="openAdd" @click.away="openAdd=false" | |
| class="fixed inset-0 bg-black/50 z-50 flex items-center justify-center p-4"> | |
| <div class="glass w-full max-w-md rounded-xl overflow-hidden"> | |
| <div class="p-4 border-b border-slate-700 flex justify-between items-center"> | |
| <h3 class="text-lg font-bold" x-text="t.addNew"></h3> | |
| <button @click="openAdd=false" class="p-1 rounded-full hover:bg-slate-700"> | |
| <i class="fas fa-times"></i> | |
| </button> | |
| </div> | |
| <div class="p-4 max-h-96 overflow-y-auto"> | |
| <div class="relative mb-4"> | |
| <i class="fas fa-search absolute left-3 top-1/2 transform -translate-y-1/2 text-sm text-slate-400"></i> | |
| <input type="search" placeholder="Search integrations..." | |
| class="w-full glass pl-9 pr-3 py-2 rounded outline-none" | |
| x-model="integrationSearch"> | |
| </div> | |
| <div class="space-y-2"> | |
| <template x-for="plugin in filteredAvailablePlugins" :key="plugin.id"> | |
| <button @click="addPlugin(plugin)" | |
| class="w-full glass p-3 rounded text-left flex items-center space-x-3 hover:bg-slate-700"> | |
| <i :class="plugin.icon + ' text-accent'"></i> | |
| <div> | |
| <p x-text="plugin.name"></p> | |
| <p class="text-xs text-slate-400" x-text="plugin.description"></p> | |
| </div> | |
| </button> | |
| </template> | |
| <div x-show="filteredAvailablePlugins.length === 0" class="p-4 text-center text-sm text-slate-400"> | |
| <p x-text="t.noIntegrations"></p> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Settings Modal --> | |
| <div x-show="openSettings" @click.away="openSettings=false" | |
| class="fixed inset-0 bg-black/50 z-50 flex items-center justify-center p-4"> | |
| <div class="glass w-full max-w-md rounded-xl overflow-hidden"> | |
| <div class="p-4 border-b border-slate-700 flex justify-between items-center"> | |
| <h3 class="text-lg font-bold">Settings</h3> | |
| <button @click="openSettings=false" class="p-1 rounded-full hover:bg-slate-700"> | |
| <i class="fas fa-times"></i> | |
| </button> | |
| </div> | |
| <div class="p-4 max-h-96 overflow-y-auto space-y-4"> | |
| <div> | |
| <label class="block text-sm mb-2">Accent Color</label> | |
| <input type="color" x-model="accent" @change="updateAccentColor" class="w-full h-10 rounded"> | |
| </div> | |
| <div> | |
| <label class="block text-sm mb-2">Theme</label> | |
| <div class="flex space-x-2"> | |
| <button @click="theme='dark'" :class="theme==='dark'?'bg-accent text-white':'glass'" class="px-3 py-1.5 rounded text-sm"> | |
| Dark | |
| </button> | |
| <button @click="theme=''" :class="theme===''?'bg-accent text-white':'glass'" class="px-3 py-1.5 rounded text-sm"> | |
| Light | |
| </button> | |
| </div> | |
| </div> | |
| <div> | |
| <label class="block text-sm mb-2">Language</label> | |
| <div class="flex space-x-2"> | |
| <button @click="currentLang='en'" :class="currentLang==='en'?'bg-accent text-white':'glass'" class="px-3 py-1.5 rounded text-sm"> | |
| English | |
| </button> | |
| <button @click="currentLang='ar'" :class="currentLang==='ar'?'bg-accent text-white':'glass'" class="px-3 py-1.5 rounded text-sm"> | |
| العربية | |
| </button> | |
| </div> | |
| </div> | |
| <div> | |
| <label class="block text-sm mb-2">Notifications</label> | |
| <div class="space-y-2"> | |
| <label class="flex items-center space-x-2"> | |
| <input type="checkbox" x-model="settings.emailNotifications" class="rounded accent-accent"> | |
| <span class="text-sm" x-text="t.emailNotifications"></span> | |
| </label> | |
| <label class="flex items-center space-x-2"> | |
| <input type="checkbox" x-model="settings.pushNotifications" class="rounded accent-accent"> | |
| <span class="text-sm" x-text="t.pushNotifications"></span> | |
| </label> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="p-4 border-t border-slate-700 flex justify-end"> | |
| <button @click="saveSettings()" class="px-4 py-2 bg-accent rounded text-white hover:bg-accent/90" x-text="t.save"></button> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Support Modal --> | |
| <div x-show="openSupport" @click.away="openSupport=false" | |
| class="fixed inset-0 bg-black/50 z-50 flex items-center justify-center p-4"> | |
| <div class="glass w-full max-w-md rounded-xl overflow-hidden"> | |
| <div class="p-4 border-b border-slate-700 flex justify-between items-center"> | |
| <h3 class="text-lg font-bold" x-text="t.contactSupport"></h3> | |
| <button @click="openSupport=false" class="p-1 rounded-full hover:bg-slate-700"> | |
| <i class="fas fa-times"></i> | |
| </button> | |
| </div> | |
| <div class="p-4 space-y-4"> | |
| <div> | |
| <label class="block text-sm mb-1" x-text="t.subject"></label> | |
| <input type="text" x-model="support.subject" class="w-full glass px-3 py-2 rounded"> | |
| </div> | |
| <div> | |
| <label class="block text-sm mb-1" x-text="t.message"></label> | |
| <textarea x-model="support.message" rows="5" class="w-full glass px-3 py-2 rounded"></textarea> | |
| </div> | |
| <div> | |
| <label class="block text-sm mb-1" x-text="t.attachment"></label> | |
| <div class="glass rounded p-3 text-center border-2 border-dashed border-slate-700 cursor-pointer hover:bg-slate-700/50"> | |
| <i class="fas fa-cloud-upload-alt text-2xl text-slate-400 mb-2"></i> | |
| <p class="text-sm" x-text="t.uploadFile"></p> | |
| <p class="text-xs text-slate-400" x-text="t.maxSize"></p> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="p-4 border-t border-slate-700 flex justify-end space-x-2"> | |
| <button @click="openSupport=false" class="px-4 py-2 glass rounded hover:bg-slate-700" x-text="t.cancel"></button> | |
| <button @click="sendSupportRequest()" class="px-4 py-2 bg-accent rounded text-white hover:bg-accent/90" x-text="t.send"></button> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Add Model Modal --> | |
| <div x-show="openAddModel" @click.away="openAddModel=false" | |
| class="fixed inset-0 bg-black/50 z-50 flex items-center justify-center p-4"> | |
| <div class="glass w-full max-w-md rounded-xl overflow-hidden"> | |
| <div class="p-4 border-b border-slate-700 flex justify-between items-center"> | |
| <h3 class="text-lg font-bold" x-text="t.addModel"></h3> | |
| <button @click="openAddModel=false" class="p-1 rounded-full hover:bg-slate-700"> | |
| <i class="fas fa-times"></i> | |
| </button> | |
| </div> | |
| <div class="p-4 space-y-4"> | |
| <div> | |
| <label class="block text-sm mb-1" x-text="t.modelName"></label> | |
| <input type="text" x-model="newModel.name" class="w-full glass px-3 py-2 rounded"> | |
| </div> | |
| <div> | |
| <label class="block text-sm mb-1" x-text="t.provider"></label> | |
| <select x-model="newModel.provider" class="w-full glass px-3 py-2 rounded"> | |
| <option value="OpenAI">OpenAI</option> | |
| <option value="Meta">Meta</option> | |
| <option value="Google">Google</option> | |
| <option value="Anthropic">Anthropic</option> | |
| </select> | |
| </div> | |
| <div> | |
| <label class="block text-sm mb-1" x-text="t.size"></label> | |
| <input type="text" x-model="newModel.size" class="w-full glass px-3 py-2 rounded"> | |
| </div> | |
| </div> | |
| <div class="p-4 border-t border-slate-700 flex justify-end space-x-2"> | |
| <button @click="openAddModel=false" class="px-4 py-2 glass rounded hover:bg-slate-700" x-text="t.cancel"></button> | |
| <button @click="addNewModel()" class="px-4 py-2 bg-accent rounded text-white hover:bg-accent/90" x-text="t.add"></button> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Bulk Import Modal --> | |
| <div x-show="openBulkImport" @click.away="openBulkImport=false" | |
| class="fixed inset-0 bg-black/50 z-50 flex items-center justify-center p-4"> | |
| <div class="glass w-full max-w-md rounded-xl overflow-hidden"> | |
| <div class="p-4 border-b border-slate-700 flex justify-between items-center"> | |
| <h3 class="text-lg font-bold" x-text="t.bulkImport"></h3> | |
| <button @click="openBulkImport=false" class="p-1 rounded-full hover:bg-slate-700"> | |
| <i class="fas fa-times"></i> | |
| </button> | |
| </div> | |
| <div class="p-4 space-y-4"> | |
| <div class="border-2 border-dashed border-slate-700 rounded-lg p-6 text-center cursor-pointer hover:bg-slate-700/20" | |
| @click="$refs.csvInput.click()"> | |
| <i class="fas fa-file-csv text-3xl text-accent mb-2"></i> | |
| <p class="text-sm" x-text="t.uploadCsv"></p> | |
| <p class="text-xs text-slate-400" x-text="t.csvFormat"></p> | |
| <input type="file" x-ref="csvInput" accept=".csv" class="hidden" @change="handleCsvUpload"> | |
| </div> | |
| <div x-show="csvPreview.length > 0" class="glass rounded-lg p-3 max-h-64 overflow-y-auto"> | |
| <table class="w-full text-xs"> | |
| <thead> | |
| <tr class="border-b border-slate-700"> | |
| <th class="p-1 text-left" x-text="t.sku"></th> | |
| <th class="p-1 text-left" x-text="t.name"></th> | |
| <th class="p-1 text-left" x-text="t.stock"></th> | |
| </tr> | |
| </thead> | |
| <tbody> | |
| <template x-for="(row, i) in csvPreview" :key="i"> | |
| <tr class="border-b border-slate-700"> | |
| <td class="p-1" x-text="row.sku"></td> | |
| <td class="p-1" x-text="row.name"></td> | |
| <td class="p-1" x-text="row.stock"></td> | |
| </tr> | |
| </template> | |
| </tbody> | |
| </table> | |
| </div> | |
| </div> | |
| <div class="p-4 border-t border-slate-700 flex justify-end space-x-2"> | |
| <button @click="openBulkImport=false" class="px-4 py-2 glass rounded hover:bg-slate-700" x-text="t.cancel"></button> | |
| <button @click="importProducts()" | |
| class="px-4 py-2 bg-accent rounded text-white hover:bg-accent/90" | |
| :disabled="csvPreview.length === 0" | |
| x-text="t.import"></button> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Product Modal --> | |
| <div x-show="openProductModal" @click.away="openProductModal=false" | |
| class="fixed inset-0 bg-black/50 z-50 flex items-center justify-center"> | |
| <div class="glass w-full max-w-md rounded-xl p-6"> | |
| <h3 class="text-lg font-bold mb-4" | |
| x-text="editingProduct ? 'Edit product' : 'Add product'"></h3> | |
| <form @submit.prevent="saveProduct"> | |
| <input type="text" placeholder="Name" x-model="form.name" | |
| class="w-full glass px-3 py-2 rounded mb-3" required> | |
| <input type="text" placeholder="SKU" x-model="form.sku" | |
| class="w-full glass px-3 py-2 rounded mb-3" required> | |
| <input type="number" placeholder="Price" x-model.number="form.price" | |
| class="w-full glass px-3 py-2 rounded mb-3" required> | |
| <input type="number" placeholder="Stock" x-model.number="form.stock" | |
| class="w-full glass px-3 py-2 rounded mb-4" required> | |
| <button class="w-full glass p-2 rounded text-accent" | |
| x-text="editingProduct ? 'Update' : 'Add'"></button> | |
| </form> | |
| </div> | |
| </div> | |
| <!-- Add Store Modal --> | |
| <div x-show="openAddStore" @click.away="openAddStore=false" | |
| class="fixed inset-0 bg-black/50 z-50 flex items-center justify-center p-4"> | |
| <div class="glass w-full max-w-md rounded-xl overflow-hidden"> | |
| <div class="p-4 border-b border-slate-700 flex justify-between items-center"> | |
| <h3 class="text-lg font-bold" x-text="t.addStore"></h3> | |
| <button @click="openAddStore=false" class="p-1 rounded-full hover:bg-slate-700"> | |
| <i class="fas fa-times"></i> | |
| </button> | |
| </div> | |
| <div class="p-4 space-y-4"> | |
| <div> | |
| <label class="block text-sm mb-1" x-text="t.storeName"></label> | |
| <input type="text" x-model="newStore.name" class="w-full glass px-3 py-2 rounded"> | |
| </div> | |
| <div> | |
| <label class="block text-sm mb-1" x-text="t.platform"></label> | |
| <select x-model="newStore.platform" class="w-full glass px-3 py-2 rounded"> | |
| <option value="Shopify">Shopify</option> | |
| <option value="WooCommerce">WooCommerce</option> | |
| <option value="BigCommerce">BigCommerce</option> | |
| <option value="Magento">Magento</option> | |
| </select> | |
| </div> | |
| <div> | |
| <label class="block text-sm mb-1" x-text="t.storeUrl"></label> | |
| <input type="url" x-model="newStore.url" class="w-full glass px-3 py-2 rounded" placeholder="https://"> | |
| </div> | |
| <div> | |
| <label class="block text-sm mb-1" x-text="t.apiKey"></label> | |
| <input type="password" x-model="newStore.apiKey" class="w-full glass px-3 py-2 rounded"> | |
| </div> | |
| </div> | |
| <div class="p-4 border-t border-slate-700 flex justify-end space-x-2"> | |
| <button @click="openAddStore=false" class="px-4 py-2 glass rounded hover:bg-slate-700" x-text="t.cancel"></button> | |
| <button @click="addNewStore()" class="px-4 py-2 bg-accent rounded text-white hover:bg-accent/90" x-text="t.add"></button> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Connect Modal --> | |
| <div x-show="selectedPlugin" @click.away="selectedPlugin=null" | |
| class="fixed inset-0 bg-black/50 z-50 flex items-center justify-center p-4"> | |
| <div class="glass w-full max-w-md rounded-xl overflow-hidden"> | |
| <div class="p-4 border-b border-slate-700 flex justify-between items-center"> | |
| <h3 class="text-lg font-bold" x-text="'Connect '+selectedPlugin?.name"></h3> | |
| <button @click="selectedPlugin=null" class="p-1 rounded-full hover:bg-slate-700"> | |
| <i class="fas fa-times"></i> | |
| </button> | |
| </div> | |
| <div class="p-4 space-y-4"> | |
| <!-- Ads --> | |
| <template x-if="selectedPlugin?.type==='ads'"> | |
| <div> | |
| <label class="text-sm mb-1 block" x-text="t.adAccountId"></label> | |
| <input x-model="apiKey" | |
| placeholder="Ad Account ID" | |
| class="w-full glass px-3 py-2 rounded mb-3"> | |
| </div> | |
| </template> | |
| <!-- E-commerce --> | |
| <template x-if="selectedPlugin?.type==='e-commerce'"> | |
| <div> | |
| <label class="text-sm mb-1 block" x-text="t.storeUrl"></label> | |
| <input x-model="apiKey" | |
| placeholder="https://store.com" | |
| class="w-full glass px-3 py-2 rounded mb-3"> | |
| <label class="text-sm mb-1 block" x-text="t.apiKey"></label> | |
| <input x-model="apiSecret" | |
| type="password" | |
| placeholder="API Key" | |
| class="w-full glass px-3 py-2 rounded"> | |
| </div> | |
| </template> | |
| <!-- Email --> | |
| <template x-if="selectedPlugin?.type==='email'"> | |
| <div class="space-y-3"> | |
| <div> | |
| <label class="text-sm mb-1 block" x-text="t.smtpHost"></label> | |
| <input x-model="apiKey" | |
| placeholder="smtp.example.com" | |
| class="w-full glass px-3 py-2 rounded"> | |
| </div> | |
| <div class="grid grid-cols-2 gap-3"> | |
| <div> | |
| <label class="text-sm mb-1 block" x-text="t.username"></label> | |
| <input x-model="emailUser" | |
| placeholder="Username" | |
| class="w-full glass px-3 py-2 rounded"> | |
| </div> | |
| <div> | |
| <label class="text-sm mb-1 block" x-text="t.password"></label> | |
| <input x-model="emailPass" | |
| type="password" | |
| placeholder="Password" | |
| class="w-full glass px-3 py-2 rounded"> | |
| </div> | |
| </div> | |
| <div class="grid grid-cols-2 gap-3"> | |
| <div> | |
| <label class="text-sm mb-1 block" x-text="t.port"></label> | |
| <input x-model="emailPort" | |
| type="number" | |
| placeholder="587" | |
| class="w-full glass px-3 py-2 rounded"> | |
| </div> | |
| <div> | |
| <label class="text-sm mb-1 block" x-text="t.security"></label> | |
| <select x-model="emailSecurity" class="w-full glass px-3 py-2 rounded"> | |
| <option value="tls">TLS</option> | |
| <option value="ssl">SSL</option> | |
| <option value="none">None</option> | |
| </select> | |
| </div> | |
| </div> | |
| </div> | |
| </template> | |
| <!-- Automation --> | |
| <template x-if="selectedPlugin?.type==='automation'"> | |
| <div> | |
| <label class="text-sm mb-1 block" x-text="t.webhookUrl"></label> | |
| <input x-model="apiKey" | |
| placeholder="https://n8n.hook.com" | |
| class="w-full glass px-3 py-2 rounded mb-3"> | |
| <label class="text-sm mb-1 block" x-text="t.secretKey"></label> | |
| <input x-model="apiSecret" | |
| type="password" | |
| placeholder="Secret Key" | |
| class="w-full glass px-3 py-2 rounded"> | |
| </div> | |
| </template> | |
| </div> | |
| <div class="p-4 border-t border-slate-700 flex justify-end space-x-2"> | |
| <button @click="selectedPlugin=null" class="px-4 py-2 glass rounded hover:bg-slate-700" x-text="t.cancel"></button> | |
| <button @click="connectPlugin(selectedPlugin)" | |
| class="px-4 py-2 rounded text-white flex items-center space-x-2" | |
| :class="selectedPlugin?.connected ? 'bg-red-500 hover:bg-red-600' : 'bg-accent hover:bg-accent/90'" | |
| x-text="selectedPlugin?.connected ? t.disconnect : t.connect"> | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- KPI Details Modal --> | |
| <div x-show="selectedKpi" @click.away="selectedKpi=null" | |
| class="fixed inset-0 bg-black/50 z-50 flex items-center justify-center p-4"> | |
| <div class="glass w-full max-w-md rounded-xl overflow-hidden"> | |
| <div class="p-4 border-b border-slate-700 flex justify-between items-center"> | |
| <h3 class="text-lg font-bold" x-text="selectedKpi?.label"></h3> | |
| <button @click="selectedKpi=null" class="p-1 rounded-full hover:bg-slate-700"> | |
| <i class="fas fa-times"></i> | |
| </button> | |
| </div> | |
| <div class="p-4"> | |
| <div class="flex items-center justify-between mb-4"> | |
| <div> | |
| <p class="text-2xl font-bold" x-text="selectedKpi?.value"></p> | |
| <p class="text-sm" :class="selectedKpi?.trend > 0 ? 'text-emerald-400' : 'text-red-400'"> | |
| <i :class="selectedKpi?.trend > 0 ? 'fas fa-caret-up' : 'fas fa-caret-down'"></i> | |
| <span x-text="Math.abs(selectedKpi?.trend) + '%'"></span> | |
| <span class="text-slate-400 ml-1" x-text="'vs ' + (timeRange === 'today' ? t.yesterday : timeRange === 'week' ? t.lastWeek : t.lastMonth)"></span> | |
| </p> | |
| </div> | |
| <div class="w-16 h-16"> | |
| <canvas x-ref="kpiChart"></canvas> | |
| </div> | |
| </div> | |
| <div class="h-48 mb-4"> | |
| <canvas x-ref="kpiDetailChart"></canvas> | |
| </div> | |
| <div class="grid grid-cols-2 gap-4"> | |
| <div class="glass rounded p-3"> | |
| <p class="text-sm text-slate-400 mb-1" x-text="t.avgDaily"></p> | |
| <p class="font-medium" x-text="selectedKpi?.avgDaily"></p> | |
| </div> | |
| <div class="glass rounded p-3"> | |
| <p class="text-sm text-slate-400 mb-1" x-text="t.peakTime"></p> | |
| <p class="font-medium" x-text="selectedKpi?.peakTime"></p> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- ================== SCRIPTS ================== --> | |
| <script src="https://cdn.jsdelivr.net/npm/chart.js"></script> | |
| <script src="https://unpkg.com/alpinejs@3.x.x/dist/cdn.min.js" defer></script> | |
| <script> | |
| </script> | |
| <script src="https://unpkg.com/alpinejs@3.x.x/dist/cdn.min.js" defer></script> | |
| <script> | |
| document.addEventListener('alpine:init', () => { | |
| Alpine.data('storeAi', () => ({ | |
| /* state */ | |
| sideOpen: false, | |
| openCmdK: false, openAdd: false, openSettings: false, openSupport: false, | |
| openAddModel: false, openAddStore: false, openAddIntegration: false, openAddProduct: false, openBulkImport: false, | |
| csvPreview: [], | |
| currentLang: localStorage.getItem('lang') || 'en', | |
| theme: localStorage.getItem('theme') || 'dark', | |
| accent: localStorage.getItem('accent') || '#38bdf8', | |
| discount: 10, budget: 500, currentDiscount: 15, currentBudget: 750, | |
| activeSection: 'kpi', | |
| apiKey: '', apiSecret: '', emailUser: '', emailPass: '', emailPort: '', emailSecurity: 'tls', | |
| selectedPlugin: null, selectedKpi: null, | |
| filter: 'all', modelFilter: 'all', storeFilter: 'all', timeRange: 'today', | |
| chartTimeRange: '7d', distributionFilter: 'requests', | |
| integrationSearch: '', | |
| notificationsOpen: false, profileOpen: false, | |
| isRefreshing: false, | |
| user: { | |
| name: 'AI Admin', | |
| email: 'admin@storeai.com', | |
| plan: 'Pro', | |
| usage: 65 | |
| }, | |
| settings: JSON.parse(localStorage.getItem('settings')) || { | |
| emailNotifications: true, | |
| pushNotifications: true | |
| }, | |
| support: { | |
| subject: '', | |
| message: '' | |
| }, | |
| newModel: { | |
| name: '', | |
| provider: 'OpenAI', | |
| size: '' | |
| }, | |
| newStore: { | |
| name: '', | |
| platform: 'Shopify', | |
| url: '', | |
| apiKey: '' | |
| }, | |
| savedScenarios: JSON.parse(localStorage.getItem('scenarios')) || [], | |
| notifications: [ | |
| { message: 'New update available for Store AI', time: '2 min ago', read: false }, | |
| { message: 'Your store FashionHub needs attention', time: '1 hour ago', read: false }, | |
| { message: 'New model Llama-3 has been released', time: '3 hours ago', read: true }, | |
| { message: 'Your subscription will renew in 7 days', time: '1 day ago', read: true } | |
| ], | |
| commands: [ | |
| { label: 'Refresh Data', description: 'Reload all dashboard data', icon: 'fas fa-sync-alt', shortcut: 'F5', action: 'refreshData' }, | |
| { label: 'Open Settings', description: 'Open application settings', icon: 'fas fa-cog', shortcut: '⌘S', action: 'openSettings' }, | |
| { label: 'Add Integration', description: 'Add a new integration', icon: 'fas fa-plus', shortcut: '⌘N', action: 'openAdd' }, | |
| { label: 'View Dashboard', description: 'Go to dashboard overview', icon: 'fas fa-tachometer-alt', shortcut: '⌘1', action: 'scrollToSection', param: 'kpi' }, | |
| { label: 'View Models', description: 'Go to AI models section', icon: 'fas fa-brain', shortcut: '⌘2', action: 'scrollToSection', param: 'models' }, | |
| { label: 'View Stores', description: 'Go to stores section', icon: 'fas fa-store', shortcut: '⌘3', action: 'scrollToSection', param: 'stores' }, | |
| { label: 'View Integrations', description: 'Go to integrations section', icon: 'fas fa-bullhorn', shortcut: '⌘4', action: 'scrollToSection', param: 'ads' } | |
| ], | |
| filteredCommands: [], | |
| kpis: [ | |
| { label: 'Revenue', value: '$1,247', icon: 'fas fa-dollar-sign', trend: 5.2, loading: false, | |
| avgDaily: '$415', peakTime: '2-4 PM' }, | |
| { label: 'Orders', value: 28, icon: 'fas fa-shopping-cart', trend: -2.1, loading: false, | |
| avgDaily: '9', peakTime: '12-2 PM' }, | |
| { label: 'Conversion', value: '12.2%', icon: 'fas fa-chart-line', trend: 1.8, loading: false, | |
| avgDaily: '10.5%', peakTime: '6-8 PM' }, | |
| { label: 'Health', value: '97%', icon: 'fas fa-heartbeat', trend: 0.5, loading: false, | |
| avgDaily: '96%', peakTime: 'All day' } | |
| ], | |
| models: [ | |
| { name: 'GPT-3.5', provider: 'OpenAI', size: '800 MB', icon: 'fas fa-robot', active: true, usage: 75 }, | |
| { name: 'Llama-2', provider: 'Meta', size: '3.9 GB', icon: 'fab fa-meta', active: true, usage: 45 }, | |
| { name: 'Claude', provider: 'Anthropic', size: '1.2 GB', icon: 'fas fa-user-astronaut', active: false, usage: 15 }, | |
| { name: 'PaLM-2', provider: 'Google', size: '2.1 GB', icon: 'fab fa-google', active: true, usage: 30 } | |
| ], | |
| stores: [ | |
| { name: 'FashionHub', platform: 'Shopify', health: 94, trend: 2, lastSync: new Date(Date.now() - 3600000), icon: 'fas fa-tshirt' }, | |
| { name: 'GadgetStore', platform: 'WooCommerce', health: 87, trend: -1, lastSync: new Date(Date.now() - 7200000), icon: 'fas fa-mobile-screen' }, | |
| { name: 'BookNook', platform: 'Shopify', health: 72, trend: -5, lastSync: new Date(Date.now() - 86400000), icon: 'fas fa-book' }, | |
| { name: 'HomeEssentials', platform: 'BigCommerce', health: 95, trend: 3, lastSync: new Date(Date.now() - 1800000), icon: 'fas fa-home' } | |
| ], | |
| costs: { | |
| current: 1245, | |
| last: 1080, | |
| change: 15.3, | |
| forecast: 1450 | |
| }, | |
| navItems: [ | |
| { id: 'kpi', label: 'Dashboard', icon: 'fas fa-tachometer-alt', badge: '3' }, | |
| { id: 'sim', label: 'What-If', icon: 'fas fa-sliders-h' }, | |
| { id: 'models', label: 'Models', icon: 'fas fa-brain' }, | |
| { id: 'stores', label: 'Stores', icon: 'fas fa-store' }, | |
| { id: 'products', label: 'Products', icon: 'fas fa-box' }, | |
| { id: 'ai', label: 'AI', icon: 'fas fa-chart-bar' }, | |
| { id: 'ads', label: 'Integrations', icon: 'fas fa-bullhorn', badge: '2' } | |
| ], | |
| plugins: [], | |
| allAvailablePlugins: [ | |
| { | |
| id: 'woocommerce', | |
| name: 'WooCommerce', | |
| icon: 'fab fa-wordpress', | |
| type: 'e-commerce', | |
| connected: false, | |
| description: 'Connect your WooCommerce store' | |
| }, | |
| { | |
| id: 'shopify', | |
| name: 'Shopify', | |
| icon: 'fab fa-shopify', | |
| type: 'e-commerce', | |
| connected: true, | |
| description: 'Connect your Shopify store' | |
| }, | |
| { | |
| id: 'meta', | |
| name: 'Meta Ads', | |
| icon | |
| </body> | |
| </html> |