Spaces:
Sleeping
Sleeping
| <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/bootstrap@5.1.3/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/bootstrap@5.1.3/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> |