lead-qualification / templates /analytics.html
sksameermujahid's picture
Upload 19 files
c061318 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Lead Qualification Analytics Dashboard</title>
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet">
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<style>
body {
background-color: #f8f9fa;
padding-top: 2rem;
}
.header {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
padding: 2rem 0;
margin-bottom: 2rem;
}
.stats-card {
background: white;
padding: 1.5rem;
border-radius: 10px;
box-shadow: 0 0 10px rgba(0,0,0,0.1);
margin-bottom: 1rem;
text-align: center;
}
.stats-number {
font-size: 2.5rem;
font-weight: bold;
color: #007bff;
}
.stats-label {
color: #6c757d;
font-size: 1rem;
margin-top: 0.5rem;
}
.chart-container {
background: white;
padding: 2rem;
border-radius: 10px;
box-shadow: 0 0 10px rgba(0,0,0,0.1);
margin-bottom: 2rem;
}
.activity-list {
background: white;
padding: 2rem;
border-radius: 10px;
box-shadow: 0 0 10px rgba(0,0,0,0.1);
margin-bottom: 2rem;
}
.activity-item {
padding: 1rem;
border-bottom: 1px solid #e9ecef;
display: flex;
justify-content: space-between;
align-items: center;
}
.activity-item:last-child {
border-bottom: none;
}
.activity-icon {
width: 40px;
height: 40px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-size: 1.2rem;
}
.activity-icon.click {
background: #28a745;
}
.activity-icon.search {
background: #007bff;
}
.activity-icon.visit {
background: #ffc107;
}
.activity-icon.email {
background: #6c757d;
}
.clear-data-btn {
background: #dc3545;
color: white;
border: none;
padding: 0.5rem 1rem;
border-radius: 5px;
cursor: pointer;
}
.clear-data-btn:hover {
background: #c82333;
}
.export-btn {
background: #28a745;
color: white;
border: none;
padding: 0.5rem 1rem;
border-radius: 5px;
cursor: pointer;
}
.export-btn:hover {
background: #218838;
}
.lead-score-card {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
padding: 2rem;
border-radius: 15px;
text-align: center;
margin-bottom: 2rem;
}
.lead-score-number {
font-size: 4rem;
font-weight: bold;
margin-bottom: 1rem;
}
.lead-score-label {
font-size: 1.2rem;
opacity: 0.9;
}
.lead-status {
padding: 0.5rem 1rem;
border-radius: 20px;
font-weight: bold;
margin-top: 1rem;
}
.lead-status.hot {
background: #dc3545;
}
.lead-status.warm {
background: #ffc107;
color: #212529;
}
.lead-status.cold {
background: #6c757d;
}
</style>
</head>
<body>
<div class="container-fluid">
<!-- Header -->
<div class="header">
<div class="container">
<div class="row align-items-center">
<div class="col-md-8">
<h1><i class="fas fa-chart-bar"></i> Lead Qualification Analytics Dashboard</h1>
<p class="mb-0">Track user behavior and lead qualification metrics</p>
</div>
<div class="col-md-4 text-end">
<a href="/" class="btn btn-outline-light me-2">
<i class="fas fa-home"></i> Home
</a>
<button class="btn btn-outline-light me-2" onclick="exportData()">
<i class="fas fa-download"></i> Export
</button>
<button class="btn btn-light" onclick="clearData()">
<i class="fas fa-trash"></i> Clear
</button>
</div>
</div>
</div>
</div>
<div class="container">
<!-- Lead Score Card -->
<div class="row mb-4">
<div class="col-md-12">
<div class="lead-score-card">
<div class="lead-score-number" id="leadScore">0</div>
<div class="lead-score-label">Lead Qualification Score</div>
<div class="lead-status" id="leadStatus">Cold</div>
</div>
</div>
</div>
<!-- Action Buttons -->
<div class="row mb-4">
<div class="col-md-6">
<button class="clear-data-btn me-2" onclick="clearData()">
<i class="fas fa-trash"></i> Clear All Data
</button>
<button class="export-btn" onclick="exportData()">
<i class="fas fa-download"></i> Export Data
</button>
</div>
<div class="col-md-6 text-end">
<small class="text-muted">Last updated: <span id="lastUpdated"></span></small>
</div>
</div>
<!-- Stats Cards -->
<div class="row mb-4">
<div class="col-md-2">
<div class="stats-card">
<div class="stats-number" id="totalClicks">0</div>
<div class="stats-label">Total Clicks</div>
</div>
</div>
<div class="col-md-2">
<div class="stats-card">
<div class="stats-number" id="totalSearches">0</div>
<div class="stats-label">Total Searches</div>
</div>
</div>
<div class="col-md-2">
<div class="stats-card">
<div class="stats-number" id="uniqueProperties">0</div>
<div class="stats-label">Unique Properties</div>
</div>
</div>
<div class="col-md-2">
<div class="stats-card">
<div class="stats-number" id="totalViewTime">0s</div>
<div class="stats-label">Total View Time</div>
</div>
</div>
<div class="col-md-2">
<div class="stats-card">
<div class="stats-number" id="avgViewDuration">0s</div>
<div class="stats-label">Avg View Duration</div>
</div>
</div>
<div class="col-md-2">
<div class="stats-card">
<div class="stats-number" id="visitRequests">0</div>
<div class="stats-label">Visit Requests</div>
</div>
</div>
</div>
<!-- Charts Row -->
<div class="row mb-4">
<div class="col-md-6">
<div class="chart-container">
<h5><i class="fas fa-chart-pie"></i> Property Type Preferences</h5>
<canvas id="propertyTypeChart"></canvas>
</div>
</div>
<div class="col-md-6">
<div class="chart-container">
<h5><i class="fas fa-chart-bar"></i> Price Range Preferences</h5>
<canvas id="priceRangeChart"></canvas>
</div>
</div>
</div>
<!-- Feature Preferences -->
<div class="row mb-4">
<div class="col-md-12">
<div class="chart-container">
<h5><i class="fas fa-star"></i> Feature Preferences</h5>
<canvas id="featureChart"></canvas>
</div>
</div>
</div>
<!-- Detailed View Analytics -->
<div class="row mb-4">
<div class="col-md-12">
<div class="chart-container">
<h5><i class="fas fa-clock"></i> Detailed View Analytics</h5>
<canvas id="viewDurationChart"></canvas>
</div>
</div>
</div>
<!-- Recent Activity -->
<div class="row">
<div class="col-md-12">
<div class="activity-list">
<h5><i class="fas fa-history"></i> Recent Activity</h5>
<div id="activityList">
<p class="text-muted">No activity recorded yet.</p>
</div>
</div>
</div>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"></script>
<script>
class LeadAnalyticsDashboard {
constructor() {
this.trackingData = this.loadTrackingData();
this.init();
}
loadTrackingData() {
const data = localStorage.getItem('userTrackingData');
return data ? JSON.parse(data) : {
clickedProperties: [],
detailedViews: [],
searchHistory: [],
features: [],
priceRanges: [],
propertyTypes: []
};
}
init() {
this.updateStats();
this.createCharts();
this.displayRecentActivity();
this.updateLastUpdated();
this.updateLeadScore();
this.generateInsights();
}
updateStats() {
document.getElementById('totalClicks').textContent = this.trackingData.clickedProperties.length;
document.getElementById('totalSearches').textContent = this.trackingData.searchHistory.length;
// Unique properties clicked
const uniqueProperties = new Set(this.trackingData.clickedProperties.map(p => p.id)).size;
document.getElementById('uniqueProperties').textContent = uniqueProperties;
// Total view time
const totalViewTime = this.trackingData.detailedViews.reduce((sum, view) => sum + view.totalDuration, 0);
document.getElementById('totalViewTime').textContent = this.formatDuration(totalViewTime);
// Average view duration
if (this.trackingData.detailedViews.length > 0) {
const totalDuration = this.trackingData.detailedViews.reduce((sum, view) => sum + view.totalDuration, 0);
const totalViews = this.trackingData.detailedViews.reduce((sum, view) => sum + view.viewCount, 0);
const avgDuration = totalViews > 0 ? Math.round(totalDuration / totalViews) : 0;
document.getElementById('avgViewDuration').textContent = this.formatDuration(avgDuration);
}
// Visit requests (from localStorage or session)
const visitHistory = JSON.parse(localStorage.getItem('visitHistory') || '[]');
document.getElementById('visitRequests').textContent = visitHistory.length;
}
generateInsights() {
const insights = this.analyzeUserBehavior();
this.displayInsights(insights);
}
analyzeUserBehavior() {
const insights = {
preferredTypes: {},
preferredPriceRanges: {},
engagementLevel: 'low',
sessionDuration: 0,
topProperties: [],
recommendations: []
};
// Analyze property type preferences
this.trackingData.propertyTypes.forEach(type => {
insights.preferredTypes[type] = (insights.preferredTypes[type] || 0) + 1;
});
// Analyze price range preferences
this.trackingData.priceRanges.forEach(range => {
insights.preferredPriceRanges[range] = (insights.preferredPriceRanges[range] || 0) + 1;
});
// Calculate engagement level
const totalInteractions = this.trackingData.clickedProperties.length + this.trackingData.detailedViews.length;
const totalTime = this.trackingData.detailedViews.reduce((sum, view) => sum + view.totalDuration, 0);
if (totalInteractions >= 10 && totalTime >= 60) {
insights.engagementLevel = 'high';
} else if (totalInteractions >= 5 && totalTime >= 30) {
insights.engagementLevel = 'medium';
}
insights.sessionDuration = totalTime;
// Get top properties by engagement
insights.topProperties = this.trackingData.detailedViews
.sort((a, b) => b.totalDuration - a.totalDuration)
.slice(0, 3);
// Generate recommendations
insights.recommendations = this.generateRecommendations();
return insights;
}
generateRecommendations() {
const recommendations = [];
const insights = this.analyzeUserBehavior();
// Recommendation 1: Based on most viewed property type
const topType = Object.keys(insights.preferredTypes)
.sort((a, b) => insights.preferredTypes[b] - insights.preferredTypes[a])[0];
if (topType) {
recommendations.push({
type: 'property_type',
message: `You show strong interest in ${topType} properties. Consider exploring more ${topType} options.`,
priority: 'high'
});
}
// Recommendation 2: Based on price range
const topPriceRange = Object.keys(insights.preferredPriceRanges)
.sort((a, b) => insights.preferredPriceRanges[b] - insights.preferredPriceRanges[a])[0];
if (topPriceRange) {
recommendations.push({
type: 'price_range',
message: `Your preferred price range is ${this.formatPriceRange(topPriceRange)}. Focus on properties in this range.`,
priority: 'medium'
});
}
// Recommendation 3: Based on engagement
if (insights.engagementLevel === 'high') {
recommendations.push({
type: 'engagement',
message: 'High engagement detected! You\'re ready for personalized recommendations and follow-up.',
priority: 'high'
});
} else if (insights.engagementLevel === 'medium') {
recommendations.push({
type: 'engagement',
message: 'Good engagement level. Consider scheduling property visits for interested properties.',
priority: 'medium'
});
}
// Recommendation 4: Based on session duration
if (insights.sessionDuration < 30) {
recommendations.push({
type: 'duration',
message: 'Short session duration. Consider providing more detailed property information.',
priority: 'low'
});
}
return recommendations;
}
displayInsights(insights) {
// Create insights section if it doesn't exist
let insightsSection = document.getElementById('insightsSection');
if (!insightsSection) {
insightsSection = document.createElement('div');
insightsSection.id = 'insightsSection';
insightsSection.className = 'row mb-4';
insightsSection.innerHTML = `
<div class="col-md-12">
<div class="chart-container">
<h5><i class="fas fa-lightbulb"></i> AI-Powered Insights & Recommendations</h5>
<div id="insightsContent"></div>
</div>
</div>
`;
// Insert after the charts row
const chartsRow = document.querySelector('.row.mb-4:nth-of-type(3)');
if (chartsRow) {
chartsRow.parentNode.insertBefore(insightsSection, chartsRow.nextSibling);
}
}
const insightsContent = document.getElementById('insightsContent');
let insightsHTML = '';
// Display engagement summary
insightsHTML += `
<div class="row mb-3">
<div class="col-md-6">
<div class="alert alert-info">
<h6><i class="fas fa-chart-line"></i> Engagement Summary</h6>
<p><strong>Level:</strong> <span class="badge bg-${insights.engagementLevel === 'high' ? 'success' : insights.engagementLevel === 'medium' ? 'warning' : 'secondary'}">${insights.engagementLevel.toUpperCase()}</span></p>
<p><strong>Session Duration:</strong> ${this.formatDuration(insights.sessionDuration)}</p>
<p><strong>Total Interactions:</strong> ${this.trackingData.clickedProperties.length + this.trackingData.detailedViews.length}</p>
</div>
</div>
<div class="col-md-6">
<div class="alert alert-success">
<h6><i class="fas fa-star"></i> Top Property Types</h6>
${Object.entries(insights.preferredTypes)
.sort(([,a], [,b]) => b - a)
.slice(0, 3)
.map(([type, count]) => `<p><strong>${type}:</strong> ${count} interactions</p>`)
.join('')}
</div>
</div>
</div>
`;
// Display recommendations
insightsHTML += `
<div class="row">
<div class="col-md-12">
<h6><i class="fas fa-recommendations"></i> AI Recommendations</h6>
${insights.recommendations.map(rec => `
<div class="alert alert-${rec.priority === 'high' ? 'danger' : rec.priority === 'medium' ? 'warning' : 'info'}">
<i class="fas fa-${rec.type === 'property_type' ? 'home' : rec.type === 'price_range' ? 'money-bill' : rec.type === 'engagement' ? 'chart-line' : 'clock'}"></i>
${rec.message}
</div>
`).join('')}
</div>
</div>
`;
// Display top properties
if (insights.topProperties.length > 0) {
insightsHTML += `
<div class="row mt-3">
<div class="col-md-12">
<h6><i class="fas fa-trophy"></i> Most Engaged Properties</h6>
<div class="row">
${insights.topProperties.map(prop => `
<div class="col-md-4">
<div class="card">
<div class="card-body">
<h6 class="card-title">${prop.propertyName.substring(0, 30)}...</h6>
<p class="card-text">
<strong>Type:</strong> ${prop.propertyType}<br>
<strong>Price:</strong> ₹${this.formatPrice(prop.price)}<br>
<strong>Duration:</strong> ${this.formatDuration(prop.totalDuration)}<br>
<strong>Views:</strong> ${prop.viewCount}
</p>
</div>
</div>
</div>
`).join('')}
</div>
</div>
</div>
`;
}
insightsContent.innerHTML = insightsHTML;
}
createCharts() {
this.createPropertyTypeChart();
this.createPriceRangeChart();
this.createFeatureChart();
this.createViewDurationChart();
}
createPropertyTypeChart() {
const ctx = document.getElementById('propertyTypeChart').getContext('2d');
const typeCounts = {};
this.trackingData.propertyTypes.forEach(type => {
typeCounts[type] = (typeCounts[type] || 0) + 1;
});
if (Object.keys(typeCounts).length === 0) {
ctx.canvas.style.display = 'none';
return;
}
new Chart(ctx, {
type: 'doughnut',
data: {
labels: Object.keys(typeCounts),
datasets: [{
data: Object.values(typeCounts),
backgroundColor: [
'#FF6384',
'#36A2EB',
'#FFCE56',
'#4BC0C0',
'#9966FF',
'#FF9F40'
]
}]
},
options: {
responsive: true,
plugins: {
legend: {
position: 'bottom'
}
}
}
});
}
createPriceRangeChart() {
const ctx = document.getElementById('priceRangeChart').getContext('2d');
const rangeCounts = {};
this.trackingData.priceRanges.forEach(range => {
rangeCounts[range] = (rangeCounts[range] || 0) + 1;
});
if (Object.keys(rangeCounts).length === 0) {
ctx.canvas.style.display = 'none';
return;
}
const labels = {
'0-500000': 'Under ₹5L',
'500000-1000000': '₹5L - ₹10L',
'1000000-2000000': '₹10L - ₹20L',
'2000000-5000000': '₹20L - ₹50L',
'5000000-10000000': '₹50L - ₹1Cr',
'10000000+': 'Above ₹1Cr'
};
new Chart(ctx, {
type: 'bar',
data: {
labels: Object.keys(rangeCounts).map(key => labels[key] || key),
datasets: [{
label: 'Clicks',
data: Object.values(rangeCounts),
backgroundColor: '#007bff'
}]
},
options: {
responsive: true,
scales: {
y: {
beginAtZero: true
}
}
}
});
}
createFeatureChart() {
const ctx = document.getElementById('featureChart').getContext('2d');
const featureCounts = {};
this.trackingData.features.forEach(feature => {
featureCounts[feature] = (featureCounts[feature] || 0) + 1;
});
if (Object.keys(featureCounts).length === 0) {
ctx.canvas.style.display = 'none';
return;
}
// Sort by count and take top 10
const sortedFeatures = Object.entries(featureCounts)
.sort(([,a], [,b]) => b - a)
.slice(0, 10);
new Chart(ctx, {
type: 'horizontalBar',
data: {
labels: sortedFeatures.map(([feature]) => feature),
datasets: [{
label: 'Clicks',
data: sortedFeatures.map(([,count]) => count),
backgroundColor: '#28a745'
}]
},
options: {
responsive: true,
scales: {
x: {
beginAtZero: true
}
}
}
});
}
createViewDurationChart() {
const ctx = document.getElementById('viewDurationChart').getContext('2d');
const detailedViews = this.trackingData.detailedViews;
if (detailedViews.length === 0) {
ctx.canvas.style.display = 'none';
return;
}
// Sort by total duration and take top 10
const sortedViews = detailedViews
.sort((a, b) => b.totalDuration - a.totalDuration)
.slice(0, 10);
new Chart(ctx, {
type: 'bar',
data: {
labels: sortedViews.map(view => view.propertyName.substring(0, 20) + '...'),
datasets: [{
label: 'Total Duration (seconds)',
data: sortedViews.map(view => view.totalDuration),
backgroundColor: '#ff6384'
}, {
label: 'View Count',
data: sortedViews.map(view => view.viewCount),
backgroundColor: '#36a2eb'
}]
},
options: {
responsive: true,
scales: {
y: {
beginAtZero: true
}
}
}
});
}
displayRecentActivity() {
const container = document.getElementById('activityList');
const allActivities = [];
// Add clicked properties
this.trackingData.clickedProperties.forEach(prop => {
allActivities.push({
type: 'click',
text: `Clicked on ${prop.name}`,
timestamp: new Date(prop.timestamp),
price: prop.price,
propertyType: prop.type
});
});
// Add detailed views
this.trackingData.detailedViews.forEach(view => {
allActivities.push({
type: 'detailed_view',
text: `Viewed ${view.propertyName} for ${this.formatDuration(view.totalDuration)}`,
timestamp: new Date(view.lastViewed),
price: view.price,
propertyType: view.propertyType,
viewCount: view.viewCount,
totalDuration: view.totalDuration
});
});
// Add searches
this.trackingData.searchHistory.forEach(search => {
allActivities.push({
type: 'search',
text: `Searched for "${search.query}"`,
timestamp: new Date(search.timestamp),
filters: search.filters
});
});
// Add visit requests
const visitHistory = JSON.parse(localStorage.getItem('visitHistory') || '[]');
visitHistory.forEach(visit => {
allActivities.push({
type: 'visit',
text: `Requested visit for ${visit.propertyName}`,
timestamp: new Date(visit.timestamp),
propertyType: visit.propertyType
});
});
// Sort by timestamp (most recent first)
allActivities.sort((a, b) => b.timestamp - a.timestamp);
if (allActivities.length === 0) {
container.innerHTML = '<p class="text-muted">No activity recorded yet.</p>';
return;
}
const activitiesHTML = allActivities.slice(0, 20).map(activity => {
let iconClass, icon;
if (activity.type === 'click') {
iconClass = 'click';
icon = 'fas fa-mouse-pointer';
} else if (activity.type === 'detailed_view') {
iconClass = 'visit';
icon = 'fas fa-clock';
} else if (activity.type === 'search') {
iconClass = 'search';
icon = 'fas fa-search';
} else {
iconClass = 'visit';
icon = 'fas fa-calendar';
}
return `
<div class="activity-item">
<div class="d-flex align-items-center">
<div class="activity-icon ${iconClass} me-3">
<i class="${icon}"></i>
</div>
<div>
<div class="fw-bold">${activity.text}</div>
<small class="text-muted">${activity.timestamp.toLocaleString()}</small>
${activity.price ? `<br><small class="text-primary">₹${this.formatPrice(activity.price)}</small>` : ''}
${activity.viewCount ? `<br><small class="text-success">Viewed ${activity.viewCount} times</small>` : ''}
</div>
</div>
</div>
`;
}).join('');
container.innerHTML = activitiesHTML;
}
updateLastUpdated() {
const now = new Date();
document.getElementById('lastUpdated').textContent = now.toLocaleString();
}
updateLeadScore() {
// Calculate lead score based on user behavior
let score = 0;
// Points for clicks
score += this.trackingData.clickedProperties.length * 5;
// Points for detailed views
score += this.trackingData.detailedViews.length * 10;
// Points for searches
score += this.trackingData.searchHistory.length * 3;
// Points for visit requests
const visitHistory = JSON.parse(localStorage.getItem('visitHistory') || '[]');
score += visitHistory.length * 15;
// Points for time spent
const totalViewTime = this.trackingData.detailedViews.reduce((sum, view) => sum + view.totalDuration, 0);
score += Math.min(totalViewTime / 60, 20); // Max 20 points for time
// Cap score at 100
score = Math.min(score, 100);
document.getElementById('leadScore').textContent = Math.round(score);
// Update lead status
const statusElement = document.getElementById('leadStatus');
statusElement.className = 'lead-status';
if (score >= 70) {
statusElement.textContent = 'Hot Lead';
statusElement.classList.add('hot');
} else if (score >= 40) {
statusElement.textContent = 'Warm Lead';
statusElement.classList.add('warm');
} else {
statusElement.textContent = 'Cold Lead';
statusElement.classList.add('cold');
}
}
formatPrice(price) {
if (price >= 10000000) {
return (price / 10000000).toFixed(1) + 'Cr';
} else if (price >= 100000) {
return (price / 100000).toFixed(1) + 'L';
} else {
return price.toLocaleString();
}
}
formatDuration(seconds) {
if (seconds < 60) {
return seconds + 's';
} else if (seconds < 3600) {
const minutes = Math.floor(seconds / 60);
const remainingSeconds = seconds % 60;
return minutes + 'm ' + remainingSeconds + 's';
} else {
const hours = Math.floor(seconds / 3600);
const minutes = Math.floor((seconds % 3600) / 60);
return hours + 'h ' + minutes + 'm';
}
}
formatPriceRange(range) {
const ranges = {
'0-500000': 'Under ₹5L',
'500000-1000000': '₹5L - ₹10L',
'1000000-2000000': '₹10L - ₹20L',
'2000000-5000000': '₹20L - ₹50L',
'5000000-10000000': '₹50L - ₹1Cr',
'10000000+': 'Above ₹1Cr'
};
return ranges[range] || range;
}
}
// Global functions for buttons
function clearData() {
if (confirm('Are you sure you want to clear all tracking data? This cannot be undone.')) {
localStorage.removeItem('userTrackingData');
localStorage.removeItem('userSessionId');
localStorage.removeItem('visitHistory');
location.reload();
}
}
function exportData() {
const trackingData = localStorage.getItem('userTrackingData');
const visitHistory = localStorage.getItem('visitHistory');
const exportData = {
tracking_data: trackingData ? JSON.parse(trackingData) : {},
visit_history: visitHistory ? JSON.parse(visitHistory) : [],
export_timestamp: new Date().toISOString()
};
const blob = new Blob([JSON.stringify(exportData, null, 2)], { type: 'application/json' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'lead_analytics_data.json';
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
}
// Initialize dashboard
document.addEventListener('DOMContentLoaded', () => {
new LeadAnalyticsDashboard();
});
</script>
</body>
</html>