All Prompts
All Prompts

dashboardanalyticschartchart.jstailwindinteractiveresponsivestatstabs
Marketing Analytics Dashboard with Chart.js
Интерактивный дашборд маркетинговой аналитики с чартами Chart.js. Отображает KPI, каналы, статистику. Адаптивный дизайн, Tailwind CSS.
Prompt
<html lang="en"><head><meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Marketing Analytics Dashboard</title>
<!-- Inter font -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin="">
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600&display=swap" rel="stylesheet">
<!-- Tailwind CDN -->
<script src="https://cdn.tailwindcss.com"></script>
<!-- Chart.js -->
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.1/dist/chart.umd.min.js"></script>
<!-- Lucide Icons -->
<script src="https://unpkg.com/lucide@latest"></script>
<link id="all-fonts-link-font-manrope" rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Manrope:wght@300;400;500;600;700;800&display=swap"><style id="all-fonts-style-font-manrope">.font-manrope { font-family: 'Manrope', sans-serif !important; }</style></head>
<body class="min-h-screen antialiased selection:bg-emerald-400/20 selection:text-emerald-300 text-white font-[Inter] bg-gradient-to-br from-[#0f1419] via-[#1a1f29] to-[#0d1117]" style="background-image: radial-gradient(circle at 25% 25%, rgba(16, 185, 129, 0.05) 0%, transparent 50%), radial-gradient(circle at 75% 75%, rgba(59, 130, 246, 0.03) 0%, transparent 50%);">
<!-- Ambient light effects -->
<div class="fixed inset-0 overflow-hidden pointer-events-none">
<div class="absolute -top-40 -right-40 w-80 h-80 bg-emerald-500/10 rounded-full blur-3xl animate-pulse" style="animation-duration: 4s;"></div>
<div class="absolute -bottom-40 -left-40 w-80 h-80 bg-blue-500/8 rounded-full blur-3xl animate-pulse" style="animation-duration: 6s; animation-delay: 2s;"></div>
</div>
<main class="relative w-full pt-6 pr-6 pb-6 pl-6">
<div class="max-w-7xl mr-auto ml-auto">
<div class="shadow-black/50 ring-1 ring-white/[0.08] border-white/[0.05] overflow-hidden animate-[slideUp_0.8s_ease-out_0.2s_both] bg-gradient-to-b from-[#0f1419]/90 via-[#131a24]/95 to-[#0a0f14]/90 border rounded-3xl shadow-2xl backdrop-blur-xl">
<!-- Enhanced Header -->
<div class="sm:px-6 lg:px-6 pt-6 pr-6 pb-6 pl-6">
<div class="flex items-center justify-between">
<div class="flex items-center gap-4">
<div class="h-10 w-10 rounded-xl bg-gradient-to-br from-emerald-500/20 to-emerald-600/10 flex items-center justify-center ring-1 ring-emerald-500/20 shadow-lg shadow-emerald-500/10">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" data-lucide="trending-up" class="lucide lucide-trending-up h-5 w-5 text-emerald-400"><path d="M16 7h6v6"></path><path d="m22 7-8.5 8.5-5-5L2 17"></path></svg>
</div>
<div class="">
<div class="text-lg font-semibold tracking-tight text-white font-sans" style="">Traffic Acquisition</div>
<div class="text-sm text-white/50 font-sans" style="">Multi-channel performance overview</div>
</div>
</div>
<div class="flex items-center gap-3">
<div class="hidden sm:flex items-center gap-2 px-3 py-1.5 rounded-lg bg-emerald-500/10 ring-1 ring-emerald-500/20">
<div class="h-2 w-2 rounded-full bg-emerald-400 animate-pulse"></div>
<span class="text-sm text-emerald-300 font-medium font-sans" style="">Live</span>
</div>
<!-- Date Range Dropdown -->
<div class="relative">
<button id="dateRangeBtn" class="inline-flex items-center gap-2.5 h-9 px-4 rounded-xl bg-white/[0.06] hover:bg-white/[0.12] transition-all duration-200 ring-1 ring-white/10 hover:ring-white/20 text-sm text-white/90 hover:text-white shadow-lg backdrop-blur-sm">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" data-lucide="calendar" class="lucide lucide-calendar h-4 w-4"><path d="M8 2v4"></path><path d="M16 2v4"></path><rect width="18" height="18" x="3" y="4" rx="2"></rect><path d="M3 10h18"></path></svg>
<span id="currentDateRange" class="font-sans">Last 30 Days</span>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" data-lucide="chevron-down" class="lucide lucide-chevron-down h-4 w-4 opacity-70 transition-transform duration-200" id="dropdownArrow"><path d="m6 9 6 6 6-6"></path></svg>
</button>
<!-- Dropdown Menu -->
<div id="dateRangeDropdown" class="hidden absolute right-0 mt-2 w-48 rounded-xl bg-[#0f1419]/95 backdrop-blur-xl ring-1 ring-white/10 shadow-2xl z-50">
<div class="p-2">
<button data-range="7" class="w-full text-left px-3 py-2 text-sm text-white/70 hover:text-white hover:bg-white/5 rounded-lg transition-colors">Last 7 Days</button>
<button data-range="30" class="w-full text-left px-3 py-2 text-sm text-white/90 bg-white/5 rounded-lg">Last 30 Days</button>
<button data-range="90" class="w-full text-left px-3 py-2 text-sm text-white/70 hover:text-white hover:bg-white/5 rounded-lg transition-colors">Last 90 Days</button>
<button data-range="365" class="w-full text-left px-3 py-2 text-sm text-white/70 hover:text-white hover:bg-white/5 rounded-lg transition-colors">Last Year</button>
</div>
</div>
</div>
<!-- Menu Button -->
<div class="relative">
<button id="menuBtn" class="h-9 w-9 inline-flex items-center justify-center rounded-xl bg-white/[0.06] hover:bg-white/[0.12] transition-all duration-200 ring-1 ring-white/10 hover:ring-white/20 shadow-lg backdrop-blur-sm group">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" data-lucide="more-horizontal" class="lucide lucide-more-horizontal h-4 w-4 text-white/70 group-hover:text-white/90"><circle cx="12" cy="12" r="1"></circle><circle cx="19" cy="12" r="1"></circle><circle cx="5" cy="12" r="1"></circle></svg>
</button>
<!-- Menu Dropdown -->
<div id="menuDropdown" class="hidden absolute right-0 mt-2 w-52 rounded-xl bg-[#0f1419]/95 backdrop-blur-xl ring-1 ring-white/10 shadow-2xl z-50">
<div class="p-2">
<button class="w-full text-left px-3 py-2 text-sm text-white/70 hover:text-white hover:bg-white/5 rounded-lg transition-colors flex items-center gap-3">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path><polyline points="7,10 12,15 17,10"></polyline><line x1="12" x2="12" y1="15" y2="3"></line></svg>
Export Data
</button>
<button class="w-full text-left px-3 py-2 text-sm text-white/70 hover:text-white hover:bg-white/5 rounded-lg transition-colors flex items-center gap-3">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M12 20h9"></path><path d="M16.5 3.5a2.12 2.12 0 0 1 3 3L7 19l-4 1 1-4Z"></path></svg>
Edit Report
</button>
<button class="w-full text-left px-3 py-2 text-sm text-white/70 hover:text-white hover:bg-white/5 rounded-lg transition-colors flex items-center gap-3">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9"></path><path d="M13.73 21a2 2 0 0 1-3.46 0"></path></svg>
Set Alerts
</button>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Enhanced Tabs -->
<div class="px-6 sm:px-8 lg:px-10 animate-[slideUp_0.8s_ease-out_0.4s_both]">
<div class="flex items-center gap-8 text-sm" id="tabContainer">
<button class="tab-btn active relative pb-4 font-medium transition-colors group" data-tab="performance">
<span class="font-sans">Channel Performance</span>
<span class="tab-indicator absolute -bottom-[1px] left-0 right-0 h-[2px] rounded-full bg-gradient-to-r from-emerald-400 to-emerald-500 shadow-lg shadow-emerald-500/50"></span>
</button>
<button class="tab-btn pb-4 text-white/50 hover:text-white/80 transition-colors relative group" data-tab="campaign">
<span class="font-sans">Campaign Analysis</span>
<span class="tab-indicator absolute -bottom-[1px] left-0 right-0 h-[2px] rounded-full bg-white/20 scale-x-0 group-hover:scale-x-100 transition-transform origin-left"></span>
</button>
<button class="tab-btn pb-4 text-white/50 hover:text-white/80 transition-colors relative group" data-tab="attribution">
<span class="font-sans">Attribution Model</span>
<span class="tab-indicator absolute -bottom-[1px] left-0 right-0 h-[2px] rounded-full bg-white/20 scale-x-0 group-hover:scale-x-100 transition-transform origin-left"></span>
</button>
<button class="tab-btn pb-4 text-white/50 hover:text-white/80 transition-colors relative group" data-tab="cohort">
<span class="font-sans">Cohort Analysis</span>
<span class="tab-indicator absolute -bottom-[1px] left-0 right-0 h-[2px] rounded-full bg-white/20 scale-x-0 group-hover:scale-x-100 transition-transform origin-left"></span>
</button>
</div>
</div>
<div class="h-px bg-gradient-to-r from-transparent via-white/10 to-transparent"></div>
<!-- Enhanced Content -->
<div class="sm:px-6 lg:px-6 pt-6 pr-6 pb-6 pl-6">
<div class="flex flex-col xl:flex-row xl:items-start gap-6 xl:gap-6">
<!-- Left: Enhanced Stats -->
<div class="xl:w-[380px] flex-shrink-0 animate-[slideRight_0.8s_ease-out_0.6s_both]">
<div class="space-y-6">
<!-- Primary Metric -->
<div class="relative">
<div class="absolute -inset-2 bg-gradient-to-r from-emerald-500/10 via-transparent to-blue-500/10 rounded-2xl blur-xl"></div>
<div class="relative">
<div class="flex flex-col gap-2 text-center mb-3 items-start">
<div id="primaryMetric" class="md:text-8xl text-8xl font-light text-white tracking-tight font-manrope">+127%</div>
</div>
<div id="metricDescription" class="text-white/60 text-left mb-1">Total acquisition growth</div>
<div id="metricDetails" class="text-sm text-white/40 font-sans">143,829 new users acquired</div>
</div>
</div>
<!-- Real-time Stats Grid -->
<div class="grid grid-cols-2 gap-6">
<div class="p-4 rounded-xl bg-gradient-to-br from-white/[0.03] to-white/[0.01] ring-1 ring-white/[0.08]">
<div class="text-2xl font-semibold text-white mb-1" id="ctrValue">3.42%</div>
<div class="text-sm text-white/50">Avg CTR</div>
<div class="flex items-center gap-1 mt-2">
<div class="h-1.5 w-1.5 rounded-full bg-emerald-400"></div>
<span class="text-xs text-emerald-400">+0.8%</span>
</div>
</div>
<div class="p-4 rounded-xl bg-gradient-to-br from-white/[0.03] to-white/[0.01] ring-1 ring-white/[0.08]">
<div class="text-2xl font-semibold text-white mb-1" id="cpcValue">$2.14</div>
<div class="text-sm text-white/50">Avg CPC</div>
<div class="flex items-center gap-1 mt-2">
<div class="h-1.5 w-1.5 rounded-full bg-rose-400"></div>
<span class="text-xs text-rose-400">+$0.23</span>
</div>
</div>
</div>
<!-- Interactive Insights Panel -->
<div class="p-5 rounded-xl bg-gradient-to-br from-white/[0.03] to-white/[0.01] ring-1 ring-white/[0.08] backdrop-blur-sm">
<div class="flex items-start gap-3 mb-4">
<div class="h-8 w-8 rounded-lg bg-violet-500/15 flex items-center justify-center flex-shrink-0">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" data-lucide="lightbulb" class="lucide lucide-lightbulb h-4 w-4 text-violet-400"><path d="M15 14c.2-1 .7-1.7 1.5-2.5 1-.9 1.5-2.2 1.5-3.5A6 6 0 0 0 6 8c0 1 .2 2.2 1.5 3.5.7.7 1.3 1.5 1.5 2.5"></path><path d="M9 18h6"></path><path d="M10 22h4"></path></svg>
</div>
<div class="">
<h3 class="text-lg font-medium text-white mb-2">Strategic Insights</h3>
<div id="insightText" class="space-y-2.5 text-sm text-white/70 leading-relaxed"><p class="font-sans">Google Ads and LinkedIn are driving the highest quality leads with 2.3x better LTV. Consider reallocating 15% of Meta budget.</p></div>
</div>
</div>
<div class="flex items-center gap-2 pt-3 border-t border-white/[0.06]">
<button id="refreshInsights" class="text-xs text-violet-400 hover:text-violet-300 font-medium flex items-center gap-1 transition-colors">
<span class="font-sans">Refresh analysis</span>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="h-3 w-3"><path d="M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8"></path><path d="M21 3v5h-5"></path><path d="M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16"></path><path d="M8 16H3v5"></path></svg>
</button>
</div>
</div>
</div>
</div>
<!-- Right: Interactive Chart -->
<div class="flex-1 animate-[slideLeft_0.8s_ease-out_0.8s_both]">
<div class="relative h-full">
<div class="absolute -inset-1 bg-gradient-to-r from-emerald-500/5 via-blue-500/5 to-violet-500/5 rounded-2xl blur-sm"></div>
<div class="relative h-full ring-1 ring-white/[0.08] flex flex-col bg-gradient-to-br from-white/[0.03] via-white/[0.01] to-transparent rounded-2xl pt-6 pr-6 pb-6 pl-6 backdrop-blur-sm">
<div class="flex-1 h-full min-h-[400px]">
<canvas id="trafficChart" class="h-full w-full" style="box-sizing: border-box; display: block; min-height: 400px; height: 400px; width: 778px;" width="1556" height="800"></canvas>
</div>
</div>
</div>
<!-- Dynamic date labels -->
<div id="dateLabels" class="mt-6 grid grid-cols-7 gap-2 text-center">
<div class="text-xs text-white/40 font-sans">
Fri<span class="text-white/60 font-medium font-sans">Aug 15</span>
</div>
<div class="text-xs text-white/40 font-sans">
Sat<span class="text-white/60 font-medium font-sans">Aug 16</span>
</div>
<div class="text-xs text-white/40 font-sans">
Sun<span class="text-white/60 font-medium font-sans">Aug 17</span>
</div>
<div class="text-xs text-white/40 font-sans">
Mon<span class="text-white/60 font-medium font-sans">Aug 18</span>
</div>
<div class="text-xs text-white/40 font-sans">
Tue<span class="text-white/60 font-medium font-sans">Aug 19</span>
</div>
<div class="text-xs text-white/40 font-sans">
Wed<span class="text-white/60 font-medium font-sans">Aug 20</span>
</div>
<div class="text-xs text-white/40 font-sans">
Thu<span class="text-white/60 font-medium font-sans">Aug 21</span>
</div>
</div>
</div>
</div>
<!-- Interactive Channel Legend -->
<div class="mt-6 animate-[fadeIn_0.8s_ease-out_1s_both]">
<div class="grid grid-cols-2 md:grid-cols-5 gap-6" id="channelLegend">
<div class="flex items-center gap-3 p-3 rounded-lg bg-gradient-to-br from-emerald-500/20 to-transparent ring-1 ring-white/5 hover:ring-white/10 transition-all duration-200 cursor-pointer group">
<div class="h-3 w-3 rounded-full bg-gradient-to-r from-emerald-400 to-emerald-500"></div>
<div class="flex-1 min-w-0">
<div class="text-sm font-medium text-white group-hover:text-white transition-colors">Google Ads</div>
<div class="text-xs text-white/50">43K users • 25.3%</div>
</div>
</div>
<div class="flex items-center gap-3 p-3 rounded-lg bg-gradient-to-br from-amber-500/20 to-transparent ring-1 ring-white/5 hover:ring-white/10 transition-all duration-200 cursor-pointer group">
<div class="h-3 w-3 rounded-full bg-gradient-to-r from-amber-400 to-amber-500"></div>
<div class="flex-1 min-w-0">
<div class="text-sm font-medium text-white group-hover:text-white transition-colors">Organic Search</div>
<div class="text-xs text-white/50">32K users • 18.8%</div>
</div>
</div>
<div class="flex items-center gap-3 p-3 rounded-lg bg-gradient-to-br from-rose-500/20 to-transparent ring-1 ring-white/5 hover:ring-white/10 transition-all duration-200 cursor-pointer group">
<div class="h-3 w-3 rounded-full bg-gradient-to-r from-rose-400 to-rose-500"></div>
<div class="flex-1 min-w-0">
<div class="text-sm font-medium text-white group-hover:text-white transition-colors">Meta Ads</div>
<div class="text-xs text-white/50">24K users • 14.1%</div>
</div>
</div>
<div class="flex items-center gap-3 p-3 rounded-lg bg-gradient-to-br from-blue-500/20 to-transparent ring-1 ring-white/5 hover:ring-white/10 transition-all duration-200 cursor-pointer group">
<div class="h-3 w-3 rounded-full bg-gradient-to-r from-blue-400 to-blue-500"></div>
<div class="flex-1 min-w-0">
<div class="text-sm font-medium text-white group-hover:text-white transition-colors">LinkedIn Ads</div>
<div class="text-xs text-white/50">19K users • 11.2%</div>
</div>
</div>
<div class="flex items-center gap-3 p-3 rounded-lg bg-gradient-to-br from-violet-500/20 to-transparent ring-1 ring-white/5 hover:ring-white/10 transition-all duration-200 cursor-pointer group">
<div class="h-3 w-3 rounded-full bg-gradient-to-r from-violet-400 to-violet-500"></div>
<div class="flex-1 min-w-0">
<div class="text-sm font-medium text-white group-hover:text-white transition-colors">Direct/Email</div>
<div class="text-xs text-white/50">25K users • 14.7%</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</main>
<style>
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
@keyframes slideUp {
from { opacity: 0; transform: translateY(20px); }
to { opacity: 1; transform: translateY(0); }
}
@keyframes slideRight {
from { opacity: 0; transform: translateX(-30px); }
to { opacity: 1; transform: translateX(0); }
}
@keyframes slideLeft {
from { opacity: 0; transform: translateX(30px); }
to { opacity: 1; transform: translateX(0); }
}
.tab-btn.active {
color: white;
}
.tab-btn.active .tab-indicator {
background: linear-gradient(to right, #10b981, #059669);
box-shadow: 0 0 10px rgba(16, 185, 129, 0.5);
transform: scaleX(1);
}
.tab-btn:not(.active) .tab-indicator {
background: rgba(255, 255, 255, 0.2);
transform: scaleX(0);
transition: transform 0.2s ease;
}
.tab-btn:not(.active):hover .tab-indicator {
transform: scaleX(1);
}
</style>
<script>
// Initialize Lucide icons
window.addEventListener('DOMContentLoaded', () => {
lucide.createIcons();
});
// Data store
const dataStore = {
performance: {
7: { data: [12, 19, 15, 8, 12, 7, 5], growth: '+89%', users: '45,231', insights: 'Short-term campaigns are performing well. Google Ads leading with strong weekend performance.' },
30: { data: [43, 32, 24, 19, 25, 15, 12], growth: '+127%', users: '143,829', insights: 'Google Ads and LinkedIn are driving the highest quality leads with 2.3x better LTV. Consider reallocating 15% of Meta budget.' },
90: { data: [65, 48, 38, 42, 35, 28, 22], growth: '+156%', users: '287,446', insights: 'Seasonal trends show strong Q4 performance. Organic search gaining momentum with improved SEO efforts.' },
365: { data: [120, 95, 78, 85, 72, 58, 45], growth: '+203%', users: '1,247,832', insights: 'Year-over-year growth accelerating. Direct traffic and email campaigns showing excellent retention rates.' }
},
campaign: {
7: { data: [8, 12, 9, 6, 10, 5, 4], growth: '+67%', users: '28,445', insights: 'Campaign optimization showing early results. A/B tests indicate 23% improvement in conversion rates.' },
30: { data: [32, 28, 22, 18, 21, 14, 11], growth: '+94%', users: '98,234', insights: 'Top-performing campaigns identified. Creative refresh needed for underperforming ad sets in Meta platform.' },
90: { data: [58, 44, 35, 38, 32, 26, 20], growth: '+134%', users: '215,678', insights: 'Campaign lifecycle analysis complete. Seasonal adjustments recommended for holiday performance boost.' },
365: { data: [98, 78, 65, 72, 58, 48, 38], growth: '+178%', users: '892,445', insights: 'Annual campaign performance exceeding targets. Budget reallocation towards high-intent keywords recommended.' }
},
attribution: {
7: { data: [15, 22, 18, 12, 16, 9, 7], growth: '+76%', users: '38,992', insights: 'Multi-touch attribution showing YouTube and email as key assist channels in conversion path.' },
30: { data: [48, 38, 28, 23, 29, 18, 14], growth: '+112%', users: '156,772', insights: 'Attribution modeling reveals 40% of conversions involve 3+ touchpoints. Social proof crucial in final stage.' },
90: { data: [72, 56, 42, 48, 38, 32, 25], growth: '+145%', users: '298,883', insights: 'Cross-device attribution patterns emerging. Mobile discovery to desktop conversion path most valuable.' },
365: { data: [135, 108, 88, 95, 78, 65, 52], growth: '+189%', users: '1,189,223', insights: 'First-click attribution showing content marketing driving 60% more qualified leads than previously measured.' }
},
cohort: {
7: { data: [10, 16, 13, 9, 11, 6, 5], growth: '+72%', users: '32,187', insights: 'Weekly cohorts showing improved day-7 retention. Onboarding flow changes having positive impact.' },
30: { data: [38, 30, 21, 17, 23, 13, 10], growth: '+103%', users: '124,556', insights: 'Monthly cohort analysis reveals strong product-market fit signals. 30-day retention up 28% vs last period.' },
90: { data: [68, 52, 39, 44, 34, 29, 23], growth: '+149%', users: '267,889', insights: 'Quarterly cohorts demonstrate sustainable growth. User lifetime value increasing across all acquisition channels.' },
365: { data: [125, 98, 82, 89, 72, 61, 48], growth: '+195%', users: '1,098,776', insights: 'Annual cohorts show exceptional retention patterns. Premium feature adoption correlating with 3x higher LTV.' }
}
};
const channels = [
{ name: 'Google Ads', color: 'emerald', bgColor: 'from-emerald-500/20', textColor: 'text-emerald-400' },
{ name: 'Organic Search', color: 'amber', bgColor: 'from-amber-500/20', textColor: 'text-amber-400' },
{ name: 'Meta Ads', color: 'rose', bgColor: 'from-rose-500/20', textColor: 'text-rose-400' },
{ name: 'LinkedIn Ads', color: 'blue', bgColor: 'from-blue-500/20', textColor: 'text-blue-400' },
{ name: 'Direct/Email', color: 'violet', bgColor: 'from-violet-500/20', textColor: 'text-violet-400' }
];
let currentTab = 'performance';
let currentDateRange = 30;
let chart;
// Initialize app
function initializeApp() {
setupEventListeners();
updateChart();
updateDateLabels();
updateLegend();
updateStats();
startLiveUpdates();
}
function setupEventListeners() {
// Date range dropdown
const dateRangeBtn = document.getElementById('dateRangeBtn');
const dateRangeDropdown = document.getElementById('dateRangeDropdown');
const dropdownArrow = document.getElementById('dropdownArrow');
dateRangeBtn.addEventListener('click', (e) => {
e.stopPropagation();
dateRangeDropdown.classList.toggle('hidden');
dropdownArrow.style.transform = dateRangeDropdown.classList.contains('hidden') ? 'rotate(0deg)' : 'rotate(180deg)';
});
// Date range options
dateRangeDropdown.querySelectorAll('button[data-range]').forEach(btn => {
btn.addEventListener('click', (e) => {
const range = parseInt(e.target.dataset.range);
currentDateRange = range;
document.getElementById('currentDateRange').textContent = `Last ${range === 365 ? 'Year' : range + ' Days'}`;
// Update active state
dateRangeDropdown.querySelectorAll('button').forEach(b => {
b.className = 'w-full text-left px-3 py-2 text-sm text-white/70 hover:text-white hover:bg-white/5 rounded-lg transition-colors';
});
e.target.className = 'w-full text-left px-3 py-2 text-sm text-white/90 bg-white/5 rounded-lg';
dateRangeDropdown.classList.add('hidden');
dropdownArrow.style.transform = 'rotate(0deg)';
updateChart();
updateDateLabels();
updateStats();
});
});
// Menu dropdown
const menuBtn = document.getElementById('menuBtn');
const menuDropdown = document.getElementById('menuDropdown');
menuBtn.addEventListener('click', (e) => {
e.stopPropagation();
menuDropdown.classList.toggle('hidden');
});
// Tab switching
document.querySelectorAll('.tab-btn').forEach(btn => {
btn.addEventListener('click', (e) => {
const tabName = e.target.closest('.tab-btn').dataset.tab;
switchTab(tabName);
});
});
// Refresh insights
document.getElementById('refreshInsights').addEventListener('click', refreshInsights);
// Close dropdowns when clicking outside
document.addEventListener('click', () => {
dateRangeDropdown.classList.add('hidden');
menuDropdown.classList.add('hidden');
dropdownArrow.style.transform = 'rotate(0deg)';
});
}
function switchTab(tabName) {
currentTab = tabName;
// Update tab appearance
document.querySelectorAll('.tab-btn').forEach(btn => {
btn.classList.remove('active');
btn.classList.add('text-white/50');
btn.classList.remove('text-white');
});
document.querySelector(`[data-tab="${tabName}"]`).classList.add('active');
document.querySelector(`[data-tab="${tabName}"]`).classList.remove('text-white/50');
document.querySelector(`[data-tab="${tabName}"]`).classList.add('text-white');
updateChart();
updateStats();
}
function updateChart() {
const canvas = document.getElementById('trafficChart');
const ctx = canvas.getContext('2d');
const currentData = dataStore[currentTab][currentDateRange];
function createGradient(color1, color2, opacity1 = 0.9, opacity2 = 0.1) {
const { height } = canvas.getBoundingClientRect();
const gradient = ctx.createLinearGradient(0, 0, 0, height);
gradient.addColorStop(0, color1.replace('1)', `${opacity1})`));
gradient.addColorStop(1, color2.replace('1)', `${opacity2})`));
return gradient;
}
const emeraldGrad = createGradient('rgba(16,185,129,1)', 'rgba(16,185,129,1)', 0.95, 0.08);
const amberGrad = createGradient('rgba(245,158,11,1)', 'rgba(245,158,11,1)', 0.95, 0.08);
const roseGrad = createGradient('rgba(244,63,94,1)', 'rgba(244,63,94,1)', 0.95, 0.08);
const blueGrad = createGradient('rgba(59,130,246,1)', 'rgba(59,130,246,1)', 0.95, 0.08);
const violetGrad = createGradient('rgba(139,92,246,1)', 'rgba(139,92,246,1)', 0.95, 0.08);
const data = {
labels: channels.map(c => c.name.split(' ')[0]),
datasets: [{
data: currentData.data,
backgroundColor: [emeraldGrad, amberGrad, roseGrad, blueGrad, violetGrad],
borderColor: [
'rgba(16,185,129,0.8)', 'rgba(245,158,11,0.8)', 'rgba(244,63,94,0.8)',
'rgba(59,130,246,0.8)', 'rgba(139,92,246,0.8)'
],
borderWidth: 2,
borderRadius: 16,
borderSkipped: false,
barPercentage: 0.7,
categoryPercentage: 0.8
}]
};
const options = {
responsive: true,
maintainAspectRatio: false,
animation: { duration: 750, easing: 'easeInOutQuart' },
plugins: {
legend: { display: false },
tooltip: {
backgroundColor: 'rgba(15,20,25,0.95)',
titleColor: '#fff',
bodyColor: 'rgba(255,255,255,0.9)',
padding: 16,
displayColors: true,
borderColor: 'rgba(255,255,255,0.1)',
borderWidth: 1,
cornerRadius: 12,
titleFont: { family: 'Inter', size: 14, weight: '600' },
bodyFont: { family: 'Inter', size: 13 },
callbacks: {
title: function(context) {
return channels[context[0].dataIndex].name;
},
label: function(context) {
return `${context.parsed.y}K visitors`;
},
afterBody: function(context) {
const percentage = ((context[0].parsed.y / currentData.data.reduce((a, b) => a + b, 0)) * 100).toFixed(1);
return `${percentage}% of total traffic`;
}
}
}
},
layout: { padding: { top: 20, right: 20, bottom: 20, left: 20 } },
scales: {
x: {
grid: { display: false, drawBorder: false },
ticks: {
color: 'rgba(255,255,255,0.4)',
font: { family: 'Inter', size: 12, weight: '500' },
padding: 12
},
border: { display: false }
},
y: {
grid: { color: 'rgba(255,255,255,0.05)', drawBorder: false },
ticks: {
color: 'rgba(255,255,255,0.4)',
font: { family: 'Inter', size: 11 },
padding: 16,
callback: function(value) { return value + 'K'; }
},
border: { display: false }
}
},
interaction: { intersect: false, mode: 'index' }
};
if (chart) chart.destroy();
chart = new Chart(ctx, { type: 'bar', data, options });
}
function updateDateLabels() {
const dateLabels = document.getElementById('dateLabels');
const baseDate = new Date();
const labels = [];
for (let i = 6; i >= 0; i--) {
const date = new Date(baseDate);
date.setDate(date.getDate() - i);
const dayName = date.toLocaleDateString('en-US', { weekday: 'short' });
const monthDay = date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' });
labels.push(`
<div class="text-xs text-white/40 font-sans">
${dayName}<span class="text-white/60 font-medium font-sans">${monthDay}</span>
</div>
`);
}
dateLabels.innerHTML = labels.join('');
}
function updateLegend() {
const legend = document.getElementById('channelLegend');
const currentData = dataStore[currentTab][currentDateRange];
legend.innerHTML = channels.map((channel, index) => {
const value = currentData.data[index];
const percentage = ((value / currentData.data.reduce((a, b) => a + b, 0)) * 100).toFixed(1);
return `
<div class="flex items-center gap-3 p-3 rounded-lg bg-gradient-to-br ${channel.bgColor} to-transparent ring-1 ring-white/5 hover:ring-white/10 transition-all duration-200 cursor-pointer group">
<div class="h-3 w-3 rounded-full bg-gradient-to-r from-${channel.color}-400 to-${channel.color}-500"></div>
<div class="flex-1 min-w-0">
<div class="text-sm font-medium text-white group-hover:text-white transition-colors">${channel.name}</div>
<div class="text-xs text-white/50">${value}K users • ${percentage}%</div>
</div>
</div>
`;
}).join('');
}
function updateStats() {
const currentData = dataStore[currentTab][currentDateRange];
document.getElementById('primaryMetric').textContent = currentData.growth;
document.getElementById('metricDescription').textContent = getMetricDescription(currentTab);
document.getElementById('metricDetails').textContent = `${currentData.users} new users acquired`;
// Update insight text
document.getElementById('insightText').innerHTML = `<p class="font-sans">${currentData.insights}</p>`;
}
function getMetricDescription(tab) {
const descriptions = {
performance: 'Total acquisition growth',
campaign: 'Campaign performance improvement',
attribution: 'Attribution model effectiveness',
cohort: 'Cohort retention improvement'
};
return descriptions[tab] || 'Total acquisition growth';
}
function refreshInsights() {
const btn = document.getElementById('refreshInsights');
const icon = btn.querySelector('svg');
// Animate refresh button
icon.style.transform = 'rotate(360deg)';
icon.style.transition = 'transform 0.5s ease';
setTimeout(() => {
icon.style.transform = 'rotate(0deg)';
updateStats();
}, 500);
}
function startLiveUpdates() {
// Simulate live CTR and CPC updates
setInterval(() => {
const ctr = (3.2 + Math.random() * 0.5).toFixed(2);
const cpc = (2.0 + Math.random() * 0.4).toFixed(2);
document.getElementById('ctrValue').textContent = ctr + '%';
document.getElementById('cpcValue').textContent = '$' + cpc;
}, 5000);
// Simulate chart data updates every 30 seconds
setInterval(() => {
const currentData = dataStore[currentTab][currentDateRange];
const variation = 0.1; // 10% variation
currentData.data = currentData.data.map(value => {
const change = (Math.random() - 0.5) * variation * value;
return Math.max(1, Math.round(value + change));
});
updateChart();
updateLegend();
}, 30000);
}
// Initialize when DOM is loaded
document.addEventListener('DOMContentLoaded', initializeApp);
</script>
</body></html>