|
|
<!DOCTYPE html> |
|
|
<html lang="en"> |
|
|
<head> |
|
|
<meta charset="UTF-8"> |
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
|
|
<title>Data Insights Explorer</title> |
|
|
<script src="https://cdn.tailwindcss.com"></script> |
|
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> |
|
|
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script> |
|
|
<style> |
|
|
@import url('https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap'); |
|
|
|
|
|
body { |
|
|
font-family: 'Poppins', sans-serif; |
|
|
background-color: #f5f7fa; |
|
|
color: #2d3748; |
|
|
} |
|
|
|
|
|
.data-window { |
|
|
transition: all 0.3s ease; |
|
|
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05); |
|
|
} |
|
|
|
|
|
.data-window:hover { |
|
|
transform: translateY(-2px); |
|
|
box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04); |
|
|
} |
|
|
|
|
|
.prompt-card { |
|
|
transition: all 0.2s ease; |
|
|
} |
|
|
|
|
|
.prompt-card:hover { |
|
|
transform: scale(1.03); |
|
|
} |
|
|
|
|
|
.gradient-bg { |
|
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); |
|
|
} |
|
|
|
|
|
.window-tabs { |
|
|
border-bottom: 2px solid #e2e8f0; |
|
|
} |
|
|
|
|
|
.window-tab { |
|
|
transition: all 0.2s ease; |
|
|
} |
|
|
|
|
|
.window-tab.active { |
|
|
border-bottom: 3px solid #667eea; |
|
|
color: #667eea; |
|
|
font-weight: 500; |
|
|
} |
|
|
|
|
|
.window-tab:hover:not(.active) { |
|
|
border-bottom: 3px solid #a0aec0; |
|
|
color: #4a5568; |
|
|
} |
|
|
|
|
|
.typewriter { |
|
|
overflow: hidden; |
|
|
border-right: 2px solid #667eea; |
|
|
white-space: nowrap; |
|
|
margin: 0 auto; |
|
|
letter-spacing: 1px; |
|
|
animation: typing 3.5s steps(40, end), blink-caret 0.75s step-end infinite; |
|
|
} |
|
|
|
|
|
@keyframes typing { |
|
|
from { width: 0 } |
|
|
to { width: 100% } |
|
|
} |
|
|
|
|
|
@keyframes blink-caret { |
|
|
from, to { border-color: transparent } |
|
|
50% { border-color: #667eea } |
|
|
} |
|
|
|
|
|
.pulse { |
|
|
animation: pulse 2s infinite; |
|
|
} |
|
|
|
|
|
@keyframes pulse { |
|
|
0% { transform: scale(1); } |
|
|
50% { transform: scale(1.05); } |
|
|
100% { transform: scale(1); } |
|
|
} |
|
|
|
|
|
.slide-fade-enter-active { |
|
|
transition: all 0.3s ease-out; |
|
|
} |
|
|
|
|
|
.slide-fade-leave-active { |
|
|
transition: all 0.3s cubic-bezier(1, 0.5, 0.8, 1); |
|
|
} |
|
|
|
|
|
.slide-fade-enter-from, |
|
|
.slide-fade-leave-to { |
|
|
transform: translateX(20px); |
|
|
opacity: 0; |
|
|
} |
|
|
|
|
|
.modal-overlay { |
|
|
background-color: rgba(0, 0, 0, 0.5); |
|
|
} |
|
|
|
|
|
.modal-container { |
|
|
max-height: 90vh; |
|
|
} |
|
|
</style> |
|
|
</head> |
|
|
<body class="min-h-screen"> |
|
|
<div class="flex flex-col h-screen"> |
|
|
|
|
|
<header class="gradient-bg text-white p-4 shadow-lg"> |
|
|
<div class="container mx-auto flex justify-between items-center"> |
|
|
<div class="flex items-center space-x-3"> |
|
|
<i class="fas fa-chart-network text-3xl"></i> |
|
|
<h1 class="text-2xl font-bold">Data Insights Explorer</h1> |
|
|
</div> |
|
|
<div class="flex items-center space-x-4"> |
|
|
<div class="relative group"> |
|
|
<button id="clientSwitcher" class="flex items-center space-x-2 bg-white/20 px-3 py-1 rounded-full hover:bg-white/30 transition"> |
|
|
<i class="fas fa-building"></i> |
|
|
<span id="currentClientName">Select Client</span> |
|
|
<i class="fas fa-chevron-down text-xs"></i> |
|
|
</button> |
|
|
<div id="clientDropdown" class="hidden absolute right-0 mt-2 w-64 bg-white rounded-md shadow-lg z-50"> |
|
|
<div class="p-3 border-b"> |
|
|
<div class="flex justify-between items-center"> |
|
|
<h3 class="font-medium text-gray-700">Clients & Properties</h3> |
|
|
<button onclick="showAddClientModal()" class="text-indigo-600 hover:text-indigo-800 text-sm flex items-center"> |
|
|
<i class="fas fa-plus mr-1"></i> Add |
|
|
</button> |
|
|
</div> |
|
|
</div> |
|
|
<div id="clientList" class="max-h-60 overflow-y-auto"> |
|
|
|
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
<div class="flex items-center space-x-2 bg-white/20 px-3 py-1 rounded-full"> |
|
|
<i class="fas fa-user-circle"></i> |
|
|
<span>Account</span> |
|
|
</div> |
|
|
<button class="bg-white text-indigo-700 px-4 py-2 rounded-full font-medium hover:bg-indigo-50 transition"> |
|
|
<i class="fas fa-sign-out-alt mr-2"></i>Logout |
|
|
</button> |
|
|
</div> |
|
|
</div> |
|
|
</header> |
|
|
|
|
|
|
|
|
<main class="flex-1 container mx-auto p-4 grid grid-cols-1 lg:grid-cols-4 gap-6"> |
|
|
|
|
|
<div class="lg:col-span-1 space-y-4"> |
|
|
<div class="bg-white rounded-xl p-4 shadow-md"> |
|
|
<h2 class="text-lg font-semibold mb-3 text-indigo-700 flex items-center"> |
|
|
<i class="fas fa-bolt mr-2"></i> Quick Insights |
|
|
</h2> |
|
|
<p class="text-sm text-gray-600 mb-4">Click any prompt to get instant analysis</p> |
|
|
|
|
|
<div class="grid grid-cols-1 gap-3"> |
|
|
<div class="prompt-card bg-indigo-50 p-3 rounded-lg cursor-pointer hover:bg-indigo-100" onclick="runPrompt('summary')"> |
|
|
<div class="flex items-center"> |
|
|
<div class="bg-indigo-100 p-2 rounded-full mr-3"> |
|
|
<i class="fas fa-chart-pie text-indigo-600"></i> |
|
|
</div> |
|
|
<div> |
|
|
<h3 class="font-medium">Performance Summary</h3> |
|
|
<p class="text-xs text-gray-600">Get an overview of key metrics</p> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<div class="prompt-card bg-green-50 p-3 rounded-lg cursor-pointer hover:bg-green-100" onclick="runPrompt('traffic')"> |
|
|
<div class="flex items-center"> |
|
|
<div class="bg-green-100 p-2 rounded-full mr-3"> |
|
|
<i class="fas fa-users text-green-600"></i> |
|
|
</div> |
|
|
<div> |
|
|
<h3 class="font-medium">Traffic Sources</h3> |
|
|
<p class="text-xs text-gray-600">Analyze where visitors come from</p> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<div class="prompt-card bg-yellow-50 p-3 rounded-lg cursor-pointer hover:bg-yellow-100" onclick="runPrompt('keywords')"> |
|
|
<div class="flex items-center"> |
|
|
<div class="bg-yellow-100 p-2 rounded-full mr-3"> |
|
|
<i class="fas fa-key text-yellow-600"></i> |
|
|
</div> |
|
|
<div> |
|
|
<h3 class="font-medium">Top Keywords</h3> |
|
|
<p class="text-xs text-gray-600">See what's driving search traffic</p> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<div class="prompt-card bg-red-50 p-3 rounded-lg cursor-pointer hover:bg-red-100" onclick="runPrompt('issues')"> |
|
|
<div class="flex items-center"> |
|
|
<div class="bg-red-100 p-2 rounded-full mr-3"> |
|
|
<i class="fas fa-exclamation-triangle text-red-600"></i> |
|
|
</div> |
|
|
<div> |
|
|
<h3 class="font-medium">Critical Issues</h3> |
|
|
<p class="text-xs text-gray-600">Identify urgent problems</p> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<div class="prompt-card bg-purple-50 p-3 rounded-lg cursor-pointer hover:bg-purple-100" onclick="runPrompt('comparison')"> |
|
|
<div class="flex items-center"> |
|
|
<div class="bg-purple-100 p-2 rounded-full mr-3"> |
|
|
<i class="fas fa-balance-scale text-purple-600"></i> |
|
|
</div> |
|
|
<div> |
|
|
<h3 class="font-medium">Period Comparison</h3> |
|
|
<p class="text-xs text-gray-600">Compare performance over time</p> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<div class="prompt-card bg-blue-50 p-3 rounded-lg cursor-pointer hover:bg-blue-100" onclick="runPrompt('recommendations')"> |
|
|
<div class="flex items-center"> |
|
|
<div class="bg-blue-100 p-2 rounded-full mr-3"> |
|
|
<i class="fas fa-lightbulb text-blue-600"></i> |
|
|
</div> |
|
|
<div> |
|
|
<h3 class="font-medium">Optimization Tips</h3> |
|
|
<p class="text-xs text-gray-600">Get actionable recommendations</p> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<div class="bg-white rounded-xl p-4 shadow-md"> |
|
|
<h2 class="text-lg font-semibold mb-3 text-indigo-700 flex items-center"> |
|
|
<i class="fas fa-history mr-2"></i> Recent Queries |
|
|
</h2> |
|
|
<div class="space-y-2"> |
|
|
<div class="text-sm p-2 hover:bg-gray-50 rounded cursor-pointer" onclick="runPrompt('traffic last week')"> |
|
|
<div class="flex justify-between"> |
|
|
<span>Traffic last week</span> |
|
|
<span class="text-gray-400">2h ago</span> |
|
|
</div> |
|
|
</div> |
|
|
<div class="text-sm p-2 hover:bg-gray-50 rounded cursor-pointer" onclick="runPrompt('top pages')"> |
|
|
<div class="flex justify-between"> |
|
|
<span>Top performing pages</span> |
|
|
<span class="text-gray-400">1d ago</span> |
|
|
</div> |
|
|
</div> |
|
|
<div class="text-sm p-2 hover:bg-gray-50 rounded cursor-pointer" onclick="runPrompt('bounce rate')"> |
|
|
<div class="flex justify-between"> |
|
|
<span>Bounce rate analysis</span> |
|
|
<span class="text-gray-400">3d ago</span> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div class="lg:col-span-3 space-y-6"> |
|
|
|
|
|
<div id="clientInfoBar" class="bg-white rounded-xl p-4 shadow-md hidden"> |
|
|
<div class="flex justify-between items-center"> |
|
|
<div> |
|
|
<h2 class="font-bold text-lg" id="activeClientTitle">Client Name</h2> |
|
|
<p class="text-sm text-gray-600" id="activeClientProperties">Properties: 0</p> |
|
|
</div> |
|
|
<div class="flex space-x-3"> |
|
|
<button onclick="showEditClientModal()" class="text-indigo-600 hover:text-indigo-800 flex items-center text-sm"> |
|
|
<i class="fas fa-edit mr-1"></i> Edit |
|
|
</button> |
|
|
<button onclick="showAddPropertyModal()" class="bg-indigo-600 text-white px-3 py-1 rounded-full text-sm hover:bg-indigo-700 flex items-center"> |
|
|
<i class="fas fa-plus mr-1"></i> Add Property |
|
|
</button> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div class="bg-white rounded-xl p-4 shadow-md"> |
|
|
<div class="flex items-center space-x-3"> |
|
|
<div class="flex-1 relative"> |
|
|
<input type="text" id="queryInput" placeholder="Ask about your analytics data (e.g. 'Show traffic trends for the past month')" |
|
|
class="w-full p-4 pr-12 rounded-lg border border-gray-300 focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500"> |
|
|
<button onclick="submitQuery()" class="absolute right-3 top-1/2 transform -translate-y-1/2 bg-indigo-600 text-white p-2 rounded-full hover:bg-indigo-700 transition"> |
|
|
<i class="fas fa-paper-plane"></i> |
|
|
</button> |
|
|
</div> |
|
|
<button onclick="voiceInput()" class="bg-indigo-100 text-indigo-700 p-3 rounded-full hover:bg-indigo-200 transition"> |
|
|
<i class="fas fa-microphone"></i> |
|
|
</button> |
|
|
</div> |
|
|
<div class="mt-2 flex flex-wrap gap-2"> |
|
|
<span class="text-xs text-gray-500">Try:</span> |
|
|
<span class="text-xs text-indigo-600 hover:underline cursor-pointer" onclick="runPrompt('compare mobile vs desktop conversions')">compare mobile vs desktop conversions</span> |
|
|
<span class="text-xs text-indigo-600 hover:underline cursor-pointer" onclick="runPrompt('show top exit pages')">show top exit pages</span> |
|
|
<span class="text-xs text-indigo-600 hover:underline cursor-pointer" onclick="runPrompt('analyze click-through rates')">analyze click-through rates</span> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div id="windowsContainer" class="space-y-6"> |
|
|
|
|
|
<div class="data-window bg-white rounded-xl overflow-hidden"> |
|
|
<div class="window-tabs flex border-b border-gray-200"> |
|
|
<div class="window-tab active px-4 py-3 font-medium">Welcome</div> |
|
|
</div> |
|
|
<div class="p-6"> |
|
|
<div class="flex items-center mb-6"> |
|
|
<div class="bg-indigo-100 p-3 rounded-full mr-4"> |
|
|
<i class="fas fa-robot text-indigo-600 text-2xl"></i> |
|
|
</div> |
|
|
<div> |
|
|
<h2 class="text-xl font-bold text-gray-800">Hello there! 👋</h2> |
|
|
<p class="text-gray-600">I'm your Data Insights Assistant, ready to help you explore your Google Analytics and Search Console data.</p> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-4"> |
|
|
<div class="bg-indigo-50 p-4 rounded-lg"> |
|
|
<h3 class="font-semibold text-indigo-700 mb-2 flex items-center"> |
|
|
<i class="fas fa-question-circle mr-2"></i> How to use |
|
|
</h3> |
|
|
<ul class="text-sm space-y-2 text-gray-700"> |
|
|
<li class="flex items-start"> |
|
|
<i class="fas fa-check-circle text-indigo-500 mt-1 mr-2"></i> |
|
|
<span>Click any quick insight button to get started</span> |
|
|
</li> |
|
|
<li class="flex items-start"> |
|
|
<i class="fas fa-check-circle text-indigo-500 mt-1 mr-2"></i> |
|
|
<span>Type your question in natural language</span> |
|
|
</li> |
|
|
<li class="flex items-start"> |
|
|
<i class="fas fa-check-circle text-indigo-500 mt-1 mr-2"></i> |
|
|
<span>Explore multiple data views simultaneously</span> |
|
|
</li> |
|
|
</ul> |
|
|
</div> |
|
|
|
|
|
<div class="bg-green-50 p-4 rounded-lg"> |
|
|
<h3 class="font-semibold text-green-700 mb-2 flex items-center"> |
|
|
<i class="fas fa-lightbulb mr-2"></i> Try asking |
|
|
</h3> |
|
|
<ul class="text-sm space-y-2 text-gray-700"> |
|
|
<li class="flex items-start"> |
|
|
<i class="fas fa-arrow-right text-green-500 mt-1 mr-2"></i> |
|
|
<span class="cursor-pointer hover:underline" onclick="runPrompt('show me traffic sources for last month')">"Show me traffic sources for last month"</span> |
|
|
</li> |
|
|
<li class="flex items-start"> |
|
|
<i class="fas fa-arrow-right text-green-500 mt-1 mr-2"></i> |
|
|
<span class="cursor-pointer hover:underline" onclick="runPrompt('compare mobile vs desktop performance')">"Compare mobile vs desktop performance"</span> |
|
|
</li> |
|
|
<li class="flex items-start"> |
|
|
<i class="fas fa-arrow-right text-green-500 mt-1 mr-2"></i> |
|
|
<span class="cursor-pointer hover:underline" onclick="runPrompt('what are my top performing pages')">"What are my top performing pages?"</span> |
|
|
</li> |
|
|
</ul> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<div class="mt-6 bg-gradient-to-r from-indigo-50 to-blue-50 p-4 rounded-lg border border-indigo-100"> |
|
|
<div class="flex items-center"> |
|
|
<div class="bg-indigo-100 p-2 rounded-full mr-3"> |
|
|
<i class="fas fa-star text-indigo-600"></i> |
|
|
</div> |
|
|
<h3 class="font-semibold">Pro Tip</h3> |
|
|
</div> |
|
|
<p class="mt-2 text-sm text-gray-700">You can ask follow-up questions on any result by clicking the "Ask More" button that appears in each data window. This maintains context for deeper analysis.</p> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div id="sampleWindow" class="data-window bg-white rounded-xl overflow-hidden hidden"> |
|
|
<div class="window-tabs flex border-b border-gray-200"> |
|
|
<div class="window-tab active px-4 py-3 font-medium">Analysis</div> |
|
|
<div class="window-tab px-4 py-3 font-medium">Data</div> |
|
|
<div class="window-tab px-4 py-3 font-medium">Details</div> |
|
|
<div class="flex-1 flex justify-end items-center pr-3"> |
|
|
<button class="text-gray-400 hover:text-gray-600 p-1"> |
|
|
<i class="fas fa-expand"></i> |
|
|
</button> |
|
|
<button class="text-gray-400 hover:text-gray-600 p-1 ml-1"> |
|
|
<i class="fas fa-times"></i> |
|
|
</button> |
|
|
</div> |
|
|
</div> |
|
|
<div class="p-6"> |
|
|
<div class="flex justify-between items-start mb-4"> |
|
|
<div> |
|
|
<h3 class="font-semibold text-lg" id="windowTitle">Sample Analysis</h3> |
|
|
<p class="text-sm text-gray-500" id="windowSubtitle">Generated just now</p> |
|
|
</div> |
|
|
<button class="bg-indigo-50 text-indigo-600 px-3 py-1 rounded-full text-sm hover:bg-indigo-100 flex items-center"> |
|
|
<i class="fas fa-comment-dots mr-1"></i> Ask More |
|
|
</button> |
|
|
</div> |
|
|
|
|
|
<div class="window-content"> |
|
|
|
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</main> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div id="addClientModal" class="fixed inset-0 z-50 flex items-center justify-center hidden"> |
|
|
<div class="modal-overlay absolute inset-0 bg-black opacity-50"></div> |
|
|
<div class="modal-container bg-white w-11/12 md:max-w-md mx-auto rounded shadow-lg z-50 overflow-y-auto"> |
|
|
<div class="modal-content py-4 text-left px-6"> |
|
|
<div class="flex justify-between items-center pb-3"> |
|
|
<p class="text-2xl font-bold">Add New Client</p> |
|
|
<button onclick="hideModal('addClientModal')" class="modal-close cursor-pointer z-50"> |
|
|
<i class="fas fa-times"></i> |
|
|
</button> |
|
|
</div> |
|
|
<div class="space-y-4"> |
|
|
<div> |
|
|
<label class="block text-gray-700 text-sm font-bold mb-2" for="clientName"> |
|
|
Client Name |
|
|
</label> |
|
|
<input type="text" id="clientName" class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline"> |
|
|
</div> |
|
|
<div> |
|
|
<label class="block text-gray-700 text-sm font-bold mb-2" for="clientIndustry"> |
|
|
Industry |
|
|
</label> |
|
|
<input type="text" id="clientIndustry" class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline"> |
|
|
</div> |
|
|
<div> |
|
|
<label class="block text-gray-700 text-sm font-bold mb-2" for="clientNotes"> |
|
|
Notes |
|
|
</label> |
|
|
<textarea id="clientNotes" rows="3" class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline"></textarea> |
|
|
</div> |
|
|
</div> |
|
|
<div class="flex justify-end pt-4"> |
|
|
<button onclick="hideModal('addClientModal')" class="px-4 py-2 text-gray-600 rounded mr-2 hover:bg-gray-100"> |
|
|
Cancel |
|
|
</button> |
|
|
<button onclick="addNewClient()" class="px-4 py-2 bg-indigo-600 text-white rounded hover:bg-indigo-700"> |
|
|
Save Client |
|
|
</button> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div id="editClientModal" class="fixed inset-0 z-50 flex items-center justify-center hidden"> |
|
|
<div class="modal-overlay absolute inset-0 bg-black opacity-50"></div> |
|
|
<div class="modal-container bg-white w-11/12 md:max-w-md mx-auto rounded shadow-lg z-50 overflow-y-auto"> |
|
|
<div class="modal-content py-4 text-left px-6"> |
|
|
<div class="flex justify-between items-center pb-3"> |
|
|
<p class="text-2xl font-bold">Edit Client</p> |
|
|
<button onclick="hideModal('editClientModal')" class="modal-close cursor-pointer z-50"> |
|
|
<i class="fas fa-times"></i> |
|
|
</button> |
|
|
</div> |
|
|
<div class="space-y-4"> |
|
|
<div> |
|
|
<label class="block text-gray-700 text-sm font-bold mb-2" for="editClientName"> |
|
|
Client Name |
|
|
</label> |
|
|
<input type="text" id="editClientName" class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline"> |
|
|
</div> |
|
|
<div> |
|
|
<label class="block text-gray-700 text-sm font-bold mb-2" for="editClientIndustry"> |
|
|
Industry |
|
|
</label> |
|
|
<input type="text" id="editClientIndustry" class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline"> |
|
|
</div> |
|
|
<div> |
|
|
<label class="block text-gray-700 text-sm font-bold mb-2" for="editClientNotes"> |
|
|
Notes |
|
|
</label> |
|
|
<textarea id="editClientNotes" rows="3" class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline"></textarea> |
|
|
</div> |
|
|
</div> |
|
|
<div class="flex justify-end pt-4"> |
|
|
<button onclick="hideModal('editClientModal')" class="px-4 py-2 text-gray-600 rounded mr-2 hover:bg-gray-100"> |
|
|
Cancel |
|
|
</button> |
|
|
<button onclick="updateClient()" class="px-4 py-2 bg-indigo-600 text-white rounded hover:bg-indigo-700"> |
|
|
Save Changes |
|
|
</button> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div id="addPropertyModal" class="fixed inset-0 z-50 flex items-center justify-center hidden"> |
|
|
<div class="modal-overlay absolute inset-0 bg-black opacity-50"></div> |
|
|
<div class="modal-container bg-white w-11/12 md:max-w-md mx-auto rounded shadow-lg z-50 overflow-y-auto"> |
|
|
<div class="modal-content py-4 text-left px-6"> |
|
|
<div class="flex justify-between items-center pb-3"> |
|
|
<p class="text-2xl font-bold">Add New Property</p> |
|
|
<button onclick="hideModal('addPropertyModal')" class="modal-close cursor-pointer z-50"> |
|
|
<i class="fas fa-times"></i> |
|
|
</button> |
|
|
</div> |
|
|
<div class="space-y-4"> |
|
|
<div> |
|
|
<label class="block text-gray-700 text-sm font-bold mb-2" for="propertyName"> |
|
|
Property Name |
|
|
</label> |
|
|
<input type="text" id="propertyName" class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline"> |
|
|
</div> |
|
|
<div> |
|
|
<label class="block text-gray-700 text-sm font-bold mb-2" for="propertyUrl"> |
|
|
Website URL |
|
|
</label> |
|
|
<input type="url" id="propertyUrl" class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline"> |
|
|
</div> |
|
|
<div> |
|
|
<label class="block text-gray-700 text-sm font-bold mb-2" for="propertyType"> |
|
|
Property Type |
|
|
</label> |
|
|
<select id="propertyType" class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline"> |
|
|
<option value="website">Website</option> |
|
|
<option value="mobile-app">Mobile App</option> |
|
|
<option value="ecommerce">E-commerce</option> |
|
|
<option value="blog">Blog</option> |
|
|
<option value="saas">SaaS Platform</option> |
|
|
</select> |
|
|
</div> |
|
|
<div class="grid grid-cols-2 gap-4"> |
|
|
<div> |
|
|
<label class="block text-gray-700 text-sm font-bold mb-2" for="gaPropertyId"> |
|
|
GA Property ID |
|
|
</label> |
|
|
<input type="text" id="gaPropertyId" class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline"> |
|
|
</div> |
|
|
<div> |
|
|
<label class="block text-gray-700 text-sm font-bold mb-2" for="gscPropertyId"> |
|
|
GSC Property ID |
|
|
</label> |
|
|
<input type="text" id="gscPropertyId" class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline"> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
<div class="flex justify-end pt-4"> |
|
|
<button onclick="hideModal('addPropertyModal')" class="px-4 py-2 text-gray-600 rounded mr-2 hover:bg-gray-100"> |
|
|
Cancel |
|
|
</button> |
|
|
<button onclick="addNewProperty()" class="px-4 py-2 bg-indigo-600 text-white rounded hover:bg-indigo-700"> |
|
|
Add Property |
|
|
</button> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<script> |
|
|
// Client and property management |
|
|
let clients = [ |
|
|
{ |
|
|
id: 'client1', |
|
|
name: 'Acme Corp', |
|
|
industry: 'E-commerce', |
|
|
notes: 'Primary client, focus on conversion optimization', |
|
|
properties: [ |
|
|
{ |
|
|
id: 'prop1', |
|
|
name: 'Main Website', |
|
|
url: 'https://acme.com', |
|
|
type: 'ecommerce', |
|
|
gaId: 'UA-12345678-1', |
|
|
gscId: 'https://acme.com' |
|
|
}, |
|
|
{ |
|
|
id: 'prop2', |
|
|
name: 'Blog', |
|
|
url: 'https://blog.acme.com', |
|
|
type: 'blog', |
|
|
gaId: 'UA-12345678-2', |
|
|
gscId: 'https://blog.acme.com' |
|
|
} |
|
|
] |
|
|
}, |
|
|
{ |
|
|
id: 'client2', |
|
|
name: 'TechStart', |
|
|
industry: 'SaaS', |
|
|
notes: 'Early stage startup, tracking growth metrics', |
|
|
properties: [ |
|
|
{ |
|
|
id: 'prop3', |
|
|
name: 'Web App', |
|
|
url: 'https://app.techstart.com', |
|
|
type: 'saas', |
|
|
gaId: 'UA-87654321-1', |
|
|
gscId: 'https://app.techstart.com' |
|
|
} |
|
|
] |
|
|
} |
|
|
]; |
|
|
|
|
|
let currentClientId = null; |
|
|
let currentPropertyId = null; |
|
|
|
|
|
// Initialize the app |
|
|
document.addEventListener('DOMContentLoaded', function() { |
|
|
// Set up event listeners for window tabs |
|
|
document.querySelectorAll('.window-tab').forEach(tab => { |
|
|
tab.addEventListener('click', function() { |
|
|
const parentWindow = this.closest('.data-window'); |
|
|
parentWindow.querySelectorAll('.window-tab').forEach(t => t.classList.remove('active')); |
|
|
this.classList.add('active'); |
|
|
}); |
|
|
}); |
|
|
|
|
|
// Client switcher dropdown |
|
|
document.getElementById('clientSwitcher').addEventListener('click', function(e) { |
|
|
e.stopPropagation(); |
|
|
const dropdown = document.getElementById('clientDropdown'); |
|
|
dropdown.classList.toggle('hidden'); |
|
|
}); |
|
|
|
|
|
// Close dropdown when clicking outside |
|
|
document.addEventListener('click', function() { |
|
|
document.getElementById('clientDropdown').classList.add('hidden'); |
|
|
}); |
|
|
|
|
|
// Initialize client list |
|
|
renderClientList(); |
|
|
|
|
|
// Select first client by default |
|
|
if (clients.length > 0) { |
|
|
selectClient(clients[0].id); |
|
|
} |
|
|
}); |
|
|
|
|
|
// Render client list in dropdown |
|
|
function renderClientList() { |
|
|
const clientList = document.getElementById('clientList'); |
|
|
clientList.innerHTML = ''; |
|
|
|
|
|
if (clients.length === 0) { |
|
|
clientList.innerHTML = '<div class="p-3 text-center text-gray-500">No clients added yet</div>'; |
|
|
return; |
|
|
} |
|
|
|
|
|
clients.forEach(client => { |
|
|
const clientItem = document.createElement('div'); |
|
|
clientItem.className = 'border-b border-gray-200 last:border-b-0'; |
|
|
|
|
|
clientItem.innerHTML = ` |
|
|
<div class="p-3 hover:bg-gray-50 cursor-pointer" onclick="selectClient('${client.id}')"> |
|
|
<div class="flex justify-between items-center"> |
|
|
<div> |
|
|
<h4 class="font-medium">${client.name}</h4> |
|
|
<p class="text-xs text-gray-500">${client.industry} • ${client.properties.length} properties</p> |
|
|
</div> |
|
|
<i class="fas fa-chevron-right text-gray-400 text-xs"></i> |
|
|
</div> |
|
|
</div> |
|
|
<div id="properties-${client.id}" class="hidden bg-gray-50 pl-4"> |
|
|
${client.properties.map(prop => ` |
|
|
<div class="p-2 hover:bg-gray-100 cursor-pointer flex items-center" onclick="selectProperty('${client.id}', '${prop.id}')"> |
|
|
<i class="fas fa-globe mr-2 text-gray-500 text-sm"></i> |
|
|
<span>${prop.name}</span> |
|
|
${currentPropertyId === prop.id ? '<i class="fas fa-check ml-2 text-indigo-600 text-xs"></i>' : ''} |
|
|
</div> |
|
|
`).join('')} |
|
|
<div class="p-2 hover:bg-gray-100 cursor-pointer text-indigo-600 text-sm flex items-center" onclick="showAddPropertyModal('${client.id}')"> |
|
|
<i class="fas fa-plus mr-2"></i> |
|
|
<span>Add Property</span> |
|
|
</div> |
|
|
</div> |
|
|
`; |
|
|
|
|
|
clientList.appendChild(clientItem); |
|
|
}); |
|
|
} |
|
|
|
|
|
// Select a client |
|
|
function selectClient(clientId) { |
|
|
currentClientId = clientId; |
|
|
const client = clients.find(c => c.id === clientId); |
|
|
|
|
|
// Update UI |
|
|
document.getElementById('currentClientName').textContent = client.name; |
|
|
document.getElementById('clientInfoBar').classList.remove('hidden'); |
|
|
document.getElementById('activeClientTitle').textContent = client.name; |
|
|
document.getElementById('activeClientProperties').textContent = `Properties: ${client.properties.length}`; |
|
|
|
|
|
// Show properties for this client in dropdown |
|
|
const propertyContainer = document.getElementById(`properties-${clientId}`); |
|
|
propertyContainer.classList.toggle('hidden'); |
|
|
|
|
|
// Auto-select first property if none selected |
|
|
if (client.properties.length > 0 && !currentPropertyId) { |
|
|
selectProperty(clientId, client.properties[0].id); |
|
|
} |
|
|
|
|
|
// Close dropdown |
|
|
document.getElementById('clientDropdown').classList.add('hidden'); |
|
|
|
|
|
// Update edit modal fields |
|
|
document.getElementById('editClientName').value = client.name; |
|
|
document.getElementById('editClientIndustry').value = client.industry; |
|
|
document.getElementById('editClientNotes').value = client.notes; |
|
|
} |
|
|
|
|
|
// Select a property |
|
|
function selectProperty(clientId, propertyId) { |
|
|
currentPropertyId = propertyId; |
|
|
|
|
|
// Update UI to show selected property |
|
|
document.querySelectorAll('[id^="properties-"]').forEach(el => { |
|
|
el.querySelectorAll('.fa-check').forEach(icon => { |
|
|
icon.classList.add('hidden'); |
|
|
}); |
|
|
}); |
|
|
|
|
|
const checkIcon = document.querySelector(`[onclick="selectProperty('${clientId}', '${propertyId}')"] .fa-check`); |
|
|
if (checkIcon) { |
|
|
checkIcon.classList.remove('hidden'); |
|
|
} |
|
|
|
|
|
// In a real app, you would load data for this property |
|
|
console.log(`Selected property ${propertyId} for client ${clientId}`); |
|
|
} |
|
|
|
|
|
// Show add client modal |
|
|
function showAddClientModal() { |
|
|
document.getElementById('addClientModal').classList.remove('hidden'); |
|
|
document.getElementById('clientName').focus(); |
|
|
} |
|
|
|
|
|
// Show edit client modal |
|
|
function showEditClientModal() { |
|
|
if (!currentClientId) return; |
|
|
|
|
|
const client = clients.find(c => c.id === currentClientId); |
|
|
if (!client) return; |
|
|
|
|
|
document.getElementById('editClientName').value = client.name; |
|
|
document.getElementById('editClientIndustry').value = client.industry; |
|
|
document.getElementById('editClientNotes').value = client.notes; |
|
|
|
|
|
document.getElementById('editClientModal').classList.remove('hidden'); |
|
|
} |
|
|
|
|
|
// Show add property modal |
|
|
function showAddPropertyModal(clientId) { |
|
|
if (clientId) { |
|
|
// If called from a specific client's "Add Property" button |
|
|
currentClientId = clientId; |
|
|
} |
|
|
|
|
|
if (!currentClientId) { |
|
|
alert('Please select a client first'); |
|
|
return; |
|
|
} |
|
|
|
|
|
// Clear form |
|
|
document.getElementById('propertyName').value = ''; |
|
|
document.getElementById('propertyUrl').value = ''; |
|
|
document.getElementById('propertyType').value = 'website'; |
|
|
document.getElementById('gaPropertyId').value = ''; |
|
|
document.getElementById('gscPropertyId').value = ''; |
|
|
|
|
|
document.getElementById('addPropertyModal').classList.remove('hidden'); |
|
|
document.getElementById('propertyName').focus(); |
|
|
} |
|
|
|
|
|
// Hide any modal |
|
|
function hideModal(modalId) { |
|
|
document.getElementById(modalId).classList.add('hidden'); |
|
|
} |
|
|
|
|
|
// Add new client |
|
|
function addNewClient() { |
|
|
const name = document.getElementById('clientName').value.trim(); |
|
|
const industry = document.getElementById('clientIndustry').value.trim(); |
|
|
const notes = document.getElementById('clientNotes').value.trim(); |
|
|
|
|
|
if (!name) { |
|
|
alert('Client name is required'); |
|
|
return; |
|
|
} |
|
|
|
|
|
const newClient = { |
|
|
id: 'client' + Date.now(), |
|
|
name, |
|
|
industry, |
|
|
notes, |
|
|
properties: [] |
|
|
}; |
|
|
|
|
|
clients.push(newClient); |
|
|
renderClientList(); |
|
|
selectClient(newClient.id); |
|
|
hideModal('addClientModal'); |
|
|
} |
|
|
|
|
|
// Update client |
|
|
function updateClient() { |
|
|
if (!currentClientId) return; |
|
|
|
|
|
const name = document.getElementById('editClientName').value.trim(); |
|
|
const industry = document.getElementById('editClientIndustry').value.trim(); |
|
|
const notes = document.getElementById('editClientNotes').value.trim(); |
|
|
|
|
|
if (!name) { |
|
|
alert('Client name is required'); |
|
|
return; |
|
|
} |
|
|
|
|
|
const clientIndex = clients.findIndex(c => c.id === currentClientId); |
|
|
if (clientIndex !== -1) { |
|
|
clients[clientIndex].name = name; |
|
|
clients[clientIndex].industry = industry; |
|
|
clients[clientIndex].notes = notes; |
|
|
|
|
|
// Update UI |
|
|
document.getElementById('currentClientName').textContent = name; |
|
|
document.getElementById('activeClientTitle').textContent = name; |
|
|
renderClientList(); |
|
|
hideModal('editClientModal'); |
|
|
} |
|
|
} |
|
|
|
|
|
// Add new property |
|
|
function addNewProperty() { |
|
|
if (!currentClientId) return; |
|
|
|
|
|
const name = document.getElementById('propertyName').value.trim(); |
|
|
const url = document.getElementById('propertyUrl').value.trim(); |
|
|
const type = document.getElementById('propertyType').value; |
|
|
const gaId = document.getElementById('gaPropertyId').value.trim(); |
|
|
const gscId = document.getElementById('gscPropertyId').value.trim(); |
|
|
|
|
|
if (!name || !url) { |
|
|
alert('Property name and URL are required'); |
|
|
return; |
|
|
} |
|
|
|
|
|
const newProperty = { |
|
|
id: 'prop' + Date.now(), |
|
|
name, |
|
|
url, |
|
|
type, |
|
|
gaId, |
|
|
gscId |
|
|
}; |
|
|
|
|
|
const clientIndex = clients.findIndex(c => c.id === currentClientId); |
|
|
if (clientIndex !== -1) { |
|
|
clients[clientIndex].properties.push(newProperty); |
|
|
|
|
|
// Update UI |
|
|
document.getElementById('activeClientProperties').textContent = `Properties: ${clients[clientIndex].properties.length}`; |
|
|
renderClientList(); |
|
|
selectProperty(currentClientId, newProperty.id); |
|
|
hideModal('addPropertyModal'); |
|
|
} |
|
|
} |
|
|
|
|
|
// Sample data for demonstration |
|
|
const sampleData = { |
|
|
summary: { |
|
|
title: "Website Performance Summary", |
|
|
subtitle: "Last 30 days compared to previous period", |
|
|
content: ` |
|
|
<div class="grid grid-cols-1 md:grid-cols-3 gap-4 mb-6"> |
|
|
<div class="bg-blue-50 p-4 rounded-lg"> |
|
|
<div class="flex justify-between items-start"> |
|
|
<div> |
|
|
<p class="text-sm text-blue-700">Users</p> |
|
|
<p class="text-2xl font-bold">12,453 <span class="text-sm font-normal text-green-600">(+8.2%)</span></p> |
|
|
</div> |
|
|
<div class="bg-blue-100 p-2 rounded-full"> |
|
|
<i class="fas fa-users text-blue-600"></i> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
<div class="bg-purple-50 p-4 rounded-lg"> |
|
|
<div class="flex justify-between items-start"> |
|
|
<div> |
|
|
<p class="text-sm text-purple-700">Avg. Session</p> |
|
|
<p class="text-2xl font-bold">2:45 <span class="text-sm font-normal text-red-600">(-0.5%)</span></p> |
|
|
</div> |
|
|
<div class="bg-purple-100 p-2 rounded-full"> |
|
|
<i class="fas fa-clock text-purple-600"></i> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
<div class="bg-green-50 p-4 rounded-lg"> |
|
|
<div class="flex justify-between items-start"> |
|
|
<div> |
|
|
<p class="text-sm text-green-700">Conversion</p> |
|
|
<p class="text-2xl font-bold">3.2% <span class="text-sm font-normal text-green-600">(+1.1%)</span></p> |
|
|
</div> |
|
|
<div class="bg-green-100 p-2 rounded-full"> |
|
|
<i class="fas fa-percentage text-green-600"></i> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<div class="mb-6"> |
|
|
<canvas id="summaryChart" height="200"></canvas> |
|
|
</div> |
|
|
|
|
|
<div class="bg-indigo-50 p-4 rounded-lg"> |
|
|
<h4 class="font-medium text-indigo-700 mb-2">Key Insights</h4> |
|
|
<ul class="list-disc pl-5 space-y-1 text-sm text-gray-700"> |
|
|
<li>Mobile traffic increased by 12% while desktop remained stable</li> |
|
|
<li>Blog posts about "SEO tips" drove 22% of new users</li> |
|
|
<li>Bounce rate improved by 1.8 percentage points</li> |
|
|
<li>Conversion rates peaked on Tuesdays and Thursdays</li> |
|
|
</ul> |
|
|
</div> |
|
|
` |
|
|
}, |
|
|
traffic: { |
|
|
title: "Traffic Sources Analysis", |
|
|
subtitle: "Breakdown by channel for the last 30 days", |
|
|
content: ` |
|
|
<div class="mb-6"> |
|
|
<canvas id="trafficChart" height="200"></canvas> |
|
|
</div> |
|
|
|
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-6"> |
|
|
<div> |
|
|
<h4 class="font-medium text-gray-700 mb-2">Top Sources</h4> |
|
|
<div class="bg-white border border-gray-200 rounded-lg overflow-hidden"> |
|
|
<table class="min-w-full divide-y divide-gray-200"> |
|
|
<thead class="bg-gray-50"> |
|
|
<tr> |
|
|
<th class="px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase">Source</th> |
|
|
<th class="px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase">Users</th> |
|
|
<th class="px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase">% Change</th> |
|
|
</tr> |
|
|
</thead> |
|
|
<tbody class="divide-y divide-gray-200"> |
|
|
<tr> |
|
|
<td class="px-4 py-2 text-sm">Organic Search</td> |
|
|
<td class="px-4 py-2 text-sm">6,542</td> |
|
|
<td class="px-4 py-2 text-sm text-green-600">+12%</td> |
|
|
</tr> |
|
|
<tr> |
|
|
<td class="px-4 py-2 text-sm">Direct</td> |
|
|
<td class="px-4 py-2 text-sm">3,210</td> |
|
|
<td class="px-4 py-2 text-sm text-red-600">-2%</td> |
|
|
</tr> |
|
|
<tr> |
|
|
<td class="px-4 py-2 text-sm">Social</td> |
|
|
<td class="px-4 py-2 text-sm">1,876</td> |
|
|
<td class="px-4 py-2 text-sm text-green-600">+24%</td> |
|
|
</tr> |
|
|
<tr> |
|
|
<td class="px-4 py-2 text-sm">Referral</td> |
|
|
<td class="px-4 py-2 text-sm">825</td> |
|
|
<td class="px-4 py-2 text-sm text-green-600">+8%</td> |
|
|
</tr> |
|
|
</tbody> |
|
|
</table> |
|
|
</div> |
|
|
</div> |
|
|
<div> |
|
|
<h4 class="font-medium text-gray-700 mb-2">Recommendations</h4> |
|
|
<div class="space-y-3"> |
|
|
<div class="flex items-start"> |
|
|
<div class="bg-blue-100 p-1 rounded-full mr-3 mt-1"> |
|
|
<i class="fas fa-bullseye text-blue-600 text-xs"></i> |
|
|
</div> |
|
|
<div> |
|
|
<p class="text-sm font-medium">Double down on social</p> |
|
|
<p class="text-xs text-gray-600">Your social traffic is growing rapidly. Consider increasing your social media ad budget.</p> |
|
|
</div> |
|
|
</div> |
|
|
<div class="flex items-start"> |
|
|
<div class="bg-green-100 p-1 rounded-full mr-3 mt-1"> |
|
|
<i class="fas fa-search text-green-600 text-xs"></i> |
|
|
</div> |
|
|
<div> |
|
|
<p class="text-sm font-medium">Optimize for search</p> |
|
|
<p class="text-xs text-gray-600">Create more content targeting long-tail keywords in your niche.</p> |
|
|
</div> |
|
|
</div> |
|
|
<div class="flex items-start"> |
|
|
<div class="bg-purple-100 p-1 rounded-full mr-3 mt-1"> |
|
|
<i class="fas fa-link text-purple-600 text-xs"></i> |
|
|
</div> |
|
|
<div> |
|
|
<p class="text-sm font-medium">Build partnerships</p> |
|
|
<p class="text-xs text-gray-600">Reach out to industry websites for guest posting and backlink opportunities.</p> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
` |
|
|
}, |
|
|
keywords: { |
|
|
title: "Top Performing Keywords", |
|
|
subtitle: "From Google Search Console - Last 30 days", |
|
|
content: ` |
|
|
<div class="mb-6"> |
|
|
<div class="flex justify-between items-center mb-3"> |
|
|
<h4 class="font-medium text-gray-700">Keyword Performance</h4> |
|
|
<div class="flex space-x-2"> |
|
|
<button class="bg-indigo-100 text-indigo-700 px-3 py-1 rounded-full text-xs">Impressions</button> |
|
|
<button class="bg-gray-100 text-gray-700 px-3 py-1 rounded-full text-xs">Clicks</button> |
|
|
<button class="bg-gray-100 text-gray-700 px-3 py-1 rounded-full text-xs">CTR</button> |
|
|
<button class="bg-gray-100 text-gray-700 px-3 py-1 rounded-full text-xs">Position</button> |
|
|
</div> |
|
|
</div> |
|
|
<canvas id="keywordsChart" height="200"></canvas> |
|
|
</div> |
|
|
|
|
|
<div class="grid grid-cols-1 gap-4"> |
|
|
<div> |
|
|
<h4 class="font-medium text-gray-700 mb-2">Top 10 Keywords by Clicks</h4> |
|
|
<div class="bg-white border border-gray-200 rounded-lg overflow-hidden"> |
|
|
<table class="min-w-full divide-y divide-gray-200"> |
|
|
<thead class="bg-gray-50"> |
|
|
<tr> |
|
|
<th class="px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase">Keyword</th> |
|
|
<th class="px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase">Clicks</th> |
|
|
<th class="px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase">Impressions</th> |
|
|
<th class="px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase">CTR</th> |
|
|
<th class="px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase">Position</th> |
|
|
</tr> |
|
|
</thead> |
|
|
<tbody class="divide-y divide-gray-200"> |
|
|
<tr> |
|
|
<td class="px-4 py-2 text-sm">seo best practices</td> |
|
|
<td class="px-4 py-2 text-sm">1,245</td> |
|
|
<td class="px-4 py-2 text-sm">8,762</td> |
|
|
<td class="px-4 py-2 text-sm">14.2%</td> |
|
|
<td class="px-4 py-2 text-sm">3.2</td> |
|
|
</tr> |
|
|
<tr> |
|
|
<td class="px-4 py-2 text-sm">content marketing</td> |
|
|
<td class="px-4 py-2 text-sm">876</td> |
|
|
<td class="px-4 py-2 text-sm">5,432</td> |
|
|
<td class="px-4 py-2 text-sm">16.1%</td> |
|
|
<td class="px-4 py-2 text-sm">4.5</td> |
|
|
</tr> |
|
|
<tr> |
|
|
<td class="px-4 py-2 text-sm">google analytics</td> |
|
|
<td class="px-4 py-2 text-sm">765</td> |
|
|
<td class="px-4 py-2 text-sm">9,123</td> |
|
|
<td class="px-4 py-2 text-sm">8.4%</td> |
|
|
<td class="px-4 py-2 text-sm">7.8</td> |
|
|
</tr> |
|
|
<tr> |
|
|
<td class="px-4 py-2 text-sm">website optimization</td> |
|
|
<td class="px-4 py-2 text-sm">543</td> |
|
|
<td class="px-4 py-2 text-sm">3,210</td> |
|
|
<td class="px-4 py-2 text-sm">16.9%</td> |
|
|
<td class="px-4 py-2 text-sm">5.1</td> |
|
|
</tr> |
|
|
</tbody> |
|
|
</table> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<div class="mt-6 bg-yellow-50 p-4 rounded-lg border border-yellow-200"> |
|
|
<div class="flex items-center"> |
|
|
<div class="bg-yellow-100 p-2 rounded-full mr-3"> |
|
|
<i class="fas fa-lightbulb text-yellow-600"></i> |
|
|
</div> |
|
|
<h4 class="font-medium">Optimization Opportunity</h4> |
|
|
</div> |
|
|
<p class="mt-2 text-sm text-gray-700">The keyword "google analytics" has high impressions but relatively low CTR. Consider updating your meta description to be more compelling and ensure the page fully answers search intent.</p> |
|
|
</div> |
|
|
` |
|
|
} |
|
|
}; |
|
|
|
|
|
// Run a predefined prompt |
|
|
function runPrompt(promptType) { |
|
|
if (!currentClientId || !currentPropertyId) { |
|
|
alert('Please select a client and property first'); |
|
|
return; |
|
|
} |
|
|
|
|
|
// In a real app, this would call your LLM API with client/property context |
|
|
// For demo, we'll use sample data |
|
|
const data = sampleData[promptType] || { |
|
|
title: "Analysis: " + promptType, |
|
|
subtitle: "For " + getCurrentPropertyName(), |
|
|
content: `<div class="typewriter">Analyzing ${getCurrentPropertyName()} data about "${promptType}". This would show real insights from your Google Analytics and Search Console data in a production environment.</div>` |
|
|
}; |
|
|
|
|
|
createDataWindow(data); |
|
|
|
|
|
// Scroll to the new window |
|
|
setTimeout(() => { |
|
|
document.querySelector('#windowsContainer').lastElementChild.scrollIntoView({ |
|
|
behavior: 'smooth' |
|
|
}); |
|
|
}, 100); |
|
|
} |
|
|
|
|
|
// Get current property name |
|
|
function getCurrentPropertyName() { |
|
|
if (!currentClientId || !currentPropertyId) return 'selected property'; |
|
|
|
|
|
const client = clients.find(c => c.id === currentClientId); |
|
|
if (!client) return 'selected property'; |
|
|
|
|
|
const property = client.properties.find(p => p.id === currentPropertyId); |
|
|
return property ? property.name : 'selected property'; |
|
|
} |
|
|
|
|
|
// Submit a custom query |
|
|
function submitQuery() { |
|
|
const query = document.getElementById('queryInput').value.trim(); |
|
|
if (!query) return; |
|
|
|
|
|
if (!currentClientId || !currentPropertyId) { |
|
|
alert('Please select a client and property first'); |
|
|
return; |
|
|
} |
|
|
|
|
|
// Clear input |
|
|
document.getElementById('queryInput').value = ''; |
|
|
|
|
|
// Create a window with loading state |
|
|
const loadingWindow = { |
|
|
title: "Analyzing: " + query, |
|
|
subtitle: "For " + getCurrentPropertyName(), |
|
|
content: `<div class="flex items-center justify-center py-8"> |
|
|
<div class="animate-spin rounded-full h-12 w-12 border-t-2 border-b-2 border-indigo-500"></div> |
|
|
</div>` |
|
|
}; |
|
|
|
|
|
const windowElement = createDataWindow(loadingWindow); |
|
|
|
|
|
// Simulate API call delay |
|
|
setTimeout(() => { |
|
|
// In a real app, you'd get actual data from your LLM API |
|
|
const response = { |
|
|
title: "Analysis: " + query, |
|
|
subtitle: "For " + getCurrentPropertyName(), |
|
|
content: `<div class="mb-4"> |
|
|
<p>Here's the analysis for: <strong>"${query}"</strong></p> |
|
|
<p class="text-sm text-gray-500">Client: ${getCurrentClientName()} • Property: ${getCurrentPropertyName()}</p> |
|
|
</div> |
|
|
<div class="bg-indigo-50 p-4 rounded-lg"> |
|
|
<p class="text-sm">In a production environment, this would show real data visualizations and insights from your Google Analytics and Search Console data based on your specific question.</p> |
|
|
</div> |
|
|
<div class="mt-4"> |
|
|
<canvas id="dynamicChart" height="200"></canvas> |
|
|
</div> |
|
|
<div class="mt-6 grid grid-cols-1 md:grid-cols-2 gap-4"> |
|
|
<div class="bg-white border border-gray-200 rounded-lg p-4"> |
|
|
<h4 class="font-medium mb-2">Key Findings</h4> |
|
|
<ul class="list-disc pl-5 space-y-1 text-sm"> |
|
|
<li>This would show actual findings from your data</li> |
|
|
<li>Each insight would be relevant to your query</li> |
|
|
<li>The LLM would generate actionable recommendations</li> |
|
|
</ul> |
|
|
</div> |
|
|
<div class="bg-white border border-gray-200 rounded-lg p-4"> |
|
|
<h4 class="font-medium mb-2">Recommendations</h4> |
|
|
<ul class="list-disc pl-5 space-y-1 text-sm"> |
|
|
<li>Specific actions based on your data</li> |
|
|
<li>Prioritized by potential impact</li> |
|
|
<li>Tailored to your business goals</li> |
|
|
</ul> |
|
|
</div> |
|
|
</div>` |
|
|
}; |
|
|
|
|
|
// Update the window with actual content |
|
|
updateWindowContent(windowElement, response); |
|
|
|
|
|
// Create charts (demo only) |
|
|
setTimeout(() => { |
|
|
createDemoChart('dynamicChart', 'line'); |
|
|
}, 100); |
|
|
}, 1500); |
|
|
} |
|
|
|
|
|
// Get current client name |
|
|
function getCurrentClientName() { |
|
|
if (!currentClientId) return 'selected client'; |
|
|
|
|
|
const client = clients.find(c => c.id === currentClientId); |
|
|
return client ? client.name : 'selected client'; |
|
|
} |
|
|
|
|
|
// Create a new data window |
|
|
function createDataWindow(data) { |
|
|
const template = document.getElementById('sampleWindow'); |
|
|
const clone = template.cloneNode(true); |
|
|
clone.id = ''; |
|
|
clone.classList.remove('hidden'); |
|
|
|
|
|
// Set title and subtitle |
|
|
clone.querySelector('#windowTitle').textContent = data.title; |
|
|
clone.querySelector('#windowSubtitle').textContent = data.subtitle; |
|
|
|
|
|
// Set content |
|
|
const contentContainer = clone.querySelector('.window-content'); |
|
|
contentContainer.innerHTML = data.content; |
|
|
|
|
|
// Add to container |
|
|
document.getElementById('windowsContainer').appendChild(clone); |
|
|
|
|
|
// Create charts if they exist in the content |
|
|
if (data.content.includes('summaryChart')) { |
|
|
createDemoChart('summaryChart', 'bar'); |
|
|
} |
|
|
if (data.content.includes('trafficChart')) { |
|
|
createDemoChart('trafficChart', 'doughnut'); |
|
|
} |
|
|
if (data.content.includes('keywordsChart')) { |
|
|
createDemoChart('keywordsChart', 'bar'); |
|
|
} |
|
|
|
|
|
return clone; |
|
|
} |
|
|
|
|
|
// Update window content |
|
|
function updateWindowContent(windowElement, data) { |
|
|
windowElement.querySelector('#windowTitle').textContent = data.title; |
|
|
windowElement.querySelector('#windowSubtitle').textContent = data.subtitle; |
|
|
windowElement.querySelector('.window-content').innerHTML = data.content; |
|
|
} |
|
|
|
|
|
// Create demo charts |
|
|
function createDemoChart(chartId, type) { |
|
|
const ctx = document.getElementById(chartId).getContext('2d'); |
|
|
|
|
|
let config = {}; |
|
|
|
|
|
if (type === 'bar') { |
|
|
config = { |
|
|
type: 'bar', |
|
|
data: { |
|
|
labels: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun'], |
|
|
datasets: [{ |
|
|
label: 'Current Period', |
|
|
data: [1200, 1900, 1700, 2100, 2300, 2500], |
|
|
backgroundColor: 'rgba(99, 102, 241, 0.6)', |
|
|
borderColor: 'rgba(99, 102, 241, 1)', |
|
|
borderWidth: 1 |
|
|
}, { |
|
|
label: 'Previous Period', |
|
|
data: [1000, 1700, 1500, 1900, 2000, 2200], |
|
|
backgroundColor: 'rgba(203, 213, 225, 0.6)', |
|
|
borderColor: 'rgba(203, 213, 225, 1)', |
|
|
borderWidth: 1 |
|
|
}] |
|
|
}, |
|
|
options: { |
|
|
responsive: true, |
|
|
plugins: { |
|
|
legend: { |
|
|
position: 'top', |
|
|
}, |
|
|
tooltip: { |
|
|
mode: 'index', |
|
|
intersect: false, |
|
|
} |
|
|
}, |
|
|
scales: { |
|
|
y: { |
|
|
beginAtZero: true |
|
|
} |
|
|
} |
|
|
} |
|
|
}; |
|
|
} else if (type === 'doughnut') { |
|
|
config = { |
|
|
type: 'doughnut', |
|
|
data: { |
|
|
labels: ['Organic Search', 'Direct', 'Social', 'Referral', 'Email'], |
|
|
datasets: [{ |
|
|
data: [45, 25, 15, 10, 5], |
|
|
backgroundColor: [ |
|
|
'rgba(99, 102, 241, 0.7)', |
|
|
'rgba(167, 139, 250, 0.7)', |
|
|
'rgba(74, 222, 128, 0.7)', |
|
|
'rgba(248, 113, 113, 0.7)', |
|
|
'rgba(251, 191, 36, 0.7)' |
|
|
], |
|
|
borderWidth: 1 |
|
|
}] |
|
|
}, |
|
|
options: { |
|
|
responsive: true, |
|
|
plugins: { |
|
|
legend: { |
|
|
position: 'right', |
|
|
} |
|
|
} |
|
|
} |
|
|
}; |
|
|
} else if (type === 'line') { |
|
|
config = { |
|
|
type: 'line', |
|
|
data: { |
|
|
labels: ['Week 1', 'Week 2', 'Week 3', 'Week 4'], |
|
|
datasets: [{ |
|
|
label: 'Performance Metric', |
|
|
data: [65, 59, 80, 81], |
|
|
fill: false, |
|
|
backgroundColor: 'rgba(99, 102, 241, 0.6)', |
|
|
borderColor: 'rgba(99, 102, 241, 1)', |
|
|
tension: 0.1 |
|
|
}] |
|
|
}, |
|
|
options: { |
|
|
responsive: true, |
|
|
scales: { |
|
|
y: { |
|
|
beginAtZero: false |
|
|
} |
|
|
} |
|
|
} |
|
|
}; |
|
|
} |
|
|
|
|
|
new Chart(ctx, config); |
|
|
} |
|
|
|
|
|
// Voice input function |
|
|
function voiceInput() { |
|
|
alert("In a real implementation, this would activate the browser's speech recognition API to capture your voice query."); |
|
|
} |
|
|
</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=vikasrij/askanalytics2" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> |
|
|
</html> |