import React, { useState, useEffect } from 'react'; import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, Legend, ResponsiveContainer, BarChart, Bar, PieChart, Pie, Cell, AreaChart, Area } from 'recharts'; import { Upload, FileText, TrendingUp, Users, Eye, MousePointer, Clock, Globe, Calendar, Download } from 'lucide-react'; import Papa from 'papaparse'; const GAAnalyzer = () => { const [data, setData] = useState(null); const [loading, setLoading] = useState(false); const [activeTab, setActiveTab] = useState('overview'); const [dateRange, setDateRange] = useState('30'); const [error, setError] = useState(''); const handleFileUpload = (event) => { const file = event.target.files[0]; if (!file) return; setLoading(true); setError(''); Papa.parse(file, { header: true, dynamicTyping: true, skipEmptyLines: true, complete: (results) => { try { processGAData(results.data); setLoading(false); } catch (err) { setError('Error processing file: ' + err.message); setLoading(false); } }, error: (error) => { setError('Error reading file: ' + error.message); setLoading(false); } }); }; const processGAData = (rawData) => { // Process the raw GA data into useful formats const processed = { raw: rawData, overview: calculateOverviewMetrics(rawData), timeSeriesData: prepareTimeSeriesData(rawData), topPages: getTopPages(rawData), trafficSources: getTrafficSources(rawData), deviceData: getDeviceData(rawData), locationData: getLocationData(rawData), bounceRateData: getBounceRateData(rawData), conversionData: getConversionData(rawData) }; setData(processed); }; const calculateOverviewMetrics = (data) => { if (!data || data.length === 0) return {}; const metrics = data.reduce((acc, row) => { // Handle different possible column names from GA exports const users = row.Users || row['Active Users'] || row.users || 0; const sessions = row.Sessions || row.sessions || 0; const pageviews = row.Pageviews || row['Screen Page Views'] || row.pageviews || 0; const bounceRate = row['Bounce Rate'] || row.bounceRate || 0; const avgSessionDuration = row['Average Session Duration'] || row.avgSessionDuration || 0; acc.totalUsers += users; acc.totalSessions += sessions; acc.totalPageviews += pageviews; acc.bounceRateSum += bounceRate; acc.sessionDurationSum += avgSessionDuration; acc.rows += 1; return acc; }, { totalUsers: 0, totalSessions: 0, totalPageviews: 0, bounceRateSum: 0, sessionDurationSum: 0, rows: 0 }); return { totalUsers: metrics.totalUsers, totalSessions: metrics.totalSessions, totalPageviews: metrics.totalPageviews, avgBounceRate: metrics.rows ? (metrics.bounceRateSum / metrics.rows).toFixed(2) : 0, avgSessionDuration: metrics.rows ? (metrics.sessionDurationSum / metrics.rows).toFixed(2) : 0, pagesPerSession: metrics.totalSessions ? (metrics.totalPageviews / metrics.totalSessions).toFixed(2) : 0 }; }; const prepareTimeSeriesData = (data) => { if (!data) return []; // Group by date and sum metrics const dateGroups = {}; data.forEach(row => { const date = row.Date || row.date || row.Day || row.day || 'Unknown'; if (!dateGroups[date]) { dateGroups[date] = { date, users: 0, sessions: 0, pageviews: 0, bounceRate: 0, count: 0 }; } dateGroups[date].users += row.Users || row['Active Users'] || row.users || 0; dateGroups[date].sessions += row.Sessions || row.sessions || 0; dateGroups[date].pageviews += row.Pageviews || row['Screen Page Views'] || row.pageviews || 0; dateGroups[date].bounceRate += row['Bounce Rate'] || row.bounceRate || 0; dateGroups[date].count += 1; }); return Object.values(dateGroups) .map(group => ({ ...group, bounceRate: group.count ? (group.bounceRate / group.count).toFixed(2) : 0 })) .sort((a, b) => new Date(a.date) - new Date(b.date)); }; const getTopPages = (data) => { if (!data) return []; const pageGroups = {}; data.forEach(row => { const page = row['Page Path'] || row['Page path and screen class'] || row.pagePath || row.page || 'Unknown'; if (!pageGroups[page]) { pageGroups[page] = { page, pageviews: 0, uniquePageviews: 0, avgTimeOnPage: 0, bounceRate: 0, count: 0 }; } pageGroups[page].pageviews += row.Pageviews || row['Screen Page Views'] || row.pageviews || 0; pageGroups[page].uniquePageviews += row['Unique Pageviews'] || row.uniquePageviews || 0; pageGroups[page].avgTimeOnPage += row['Average Time on Page'] || row.avgTimeOnPage || 0; pageGroups[page].bounceRate += row['Bounce Rate'] || row.bounceRate || 0; pageGroups[page].count += 1; }); return Object.values(pageGroups) .map(group => ({ ...group, avgTimeOnPage: group.count ? (group.avgTimeOnPage / group.count).toFixed(2) : 0, bounceRate: group.count ? (group.bounceRate / group.count).toFixed(2) : 0 })) .sort((a, b) => b.pageviews - a.pageviews) .slice(0, 10); }; const getTrafficSources = (data) => { if (!data) return []; const sourceGroups = {}; data.forEach(row => { const source = row['Source'] || row['Session source'] || row.source || 'Unknown'; if (!sourceGroups[source]) { sourceGroups[source] = { source, sessions: 0, users: 0, bounceRate: 0, count: 0 }; } sourceGroups[source].sessions += row.Sessions || row.sessions || 0; sourceGroups[source].users += row.Users || row['Active Users'] || row.users || 0; sourceGroups[source].bounceRate += row['Bounce Rate'] || row.bounceRate || 0; sourceGroups[source].count += 1; }); return Object.values(sourceGroups) .map(group => ({ ...group, bounceRate: group.count ? (group.bounceRate / group.count).toFixed(2) : 0 })) .sort((a, b) => b.sessions - a.sessions) .slice(0, 10); }; const getDeviceData = (data) => { if (!data) return []; const deviceGroups = {}; data.forEach(row => { const device = row['Device Category'] || row['Device category'] || row.deviceCategory || 'Unknown'; if (!deviceGroups[device]) { deviceGroups[device] = { device, sessions: 0, users: 0 }; } deviceGroups[device].sessions += row.Sessions || row.sessions || 0; deviceGroups[device].users += row.Users || row['Active Users'] || row.users || 0; }); return Object.values(deviceGroups).sort((a, b) => b.sessions - a.sessions); }; const getLocationData = (data) => { if (!data) return []; const locationGroups = {}; data.forEach(row => { const location = row.Country || row.country || row.City || row.city || 'Unknown'; if (!locationGroups[location]) { locationGroups[location] = { location, sessions: 0, users: 0 }; } locationGroups[location].sessions += row.Sessions || row.sessions || 0; locationGroups[location].users += row.Users || row['Active Users'] || row.users || 0; }); return Object.values(locationGroups) .sort((a, b) => b.sessions - a.sessions) .slice(0, 15); }; const getBounceRateData = (data) => { if (!data) return []; return prepareTimeSeriesData(data).map(d => ({ date: d.date, bounceRate: parseFloat(d.bounceRate) })); }; const getConversionData = (data) => { if (!data) return []; const conversionMetrics = data.reduce((acc, row) => { acc.goalCompletions += row['Goal Completions'] || row.goalCompletions || 0; acc.goalConversionRate += row['Goal Conversion Rate'] || row.goalConversionRate || 0; acc.ecommerceConversionRate += row['Ecommerce Conversion Rate'] || row.ecommerceConversionRate || 0; acc.revenue += row.Revenue || row.revenue || 0; acc.count += 1; return acc; }, { goalCompletions: 0, goalConversionRate: 0, ecommerceConversionRate: 0, revenue: 0, count: 0 }); return { totalGoalCompletions: conversionMetrics.goalCompletions, avgGoalConversionRate: conversionMetrics.count ? (conversionMetrics.goalConversionRate / conversionMetrics.count).toFixed(2) : 0, avgEcommerceConversionRate: conversionMetrics.count ? (conversionMetrics.ecommerceConversionRate / conversionMetrics.count).toFixed(2) : 0, totalRevenue: conversionMetrics.revenue.toFixed(2) }; }; const generateReport = () => { if (!data) return; const report = ` GOOGLE ANALYTICS REPORT Generated on: ${new Date().toLocaleString()} OVERVIEW METRICS ================ Total Users: ${data.overview.totalUsers?.toLocaleString() || 'N/A'} Total Sessions: ${data.overview.totalSessions?.toLocaleString() || 'N/A'} Total Pageviews: ${data.overview.totalPageviews?.toLocaleString() || 'N/A'} Average Bounce Rate: ${data.overview.avgBounceRate}% Pages per Session: ${data.overview.pagesPerSession} Average Session Duration: ${data.overview.avgSessionDuration}s TOP PERFORMING PAGES =================== ${data.topPages?.slice(0, 5).map((page, i) => `${i + 1}. ${page.page}: ${page.pageviews.toLocaleString()} pageviews` ).join('\n') || 'No page data available'} TOP TRAFFIC SOURCES ================== ${data.trafficSources?.slice(0, 5).map((source, i) => `${i + 1}. ${source.source}: ${source.sessions.toLocaleString()} sessions` ).join('\n') || 'No traffic source data available'} DEVICE BREAKDOWN =============== ${data.deviceData?.map(device => `${device.device}: ${device.sessions.toLocaleString()} sessions (${device.users.toLocaleString()} users)` ).join('\n') || 'No device data available'} `; const blob = new Blob([report], { type: 'text/plain' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `ga-report-${new Date().toISOString().split('T')[0]}.txt`; a.click(); URL.revokeObjectURL(url); }; const COLORS = ['#8884d8', '#82ca9d', '#ffc658', '#ff7300', '#00ff00', '#ff00ff', '#00ffff', '#ff0000']; const MetricCard = ({ icon: Icon, title, value, change, color = 'blue' }) => (
{title}
{value || 'N/A'}
{change && (= 0 ? 'text-green-600' : 'text-red-600'}`}> {change >= 0 ? '+' : ''}{change}%
)}Upload your GA data export to get comprehensive insights and reports
Export your data from GA4 or Universal Analytics as CSV and upload it here for analysis
{loading && (Page | Pageviews | Bounce Rate | Avg Time on Page |
---|---|---|---|
{page.page} | {page.pageviews.toLocaleString()} | {page.bounceRate}% | {page.avgTimeOnPage}s |
Goal Completions
{data.conversionData.totalGoalCompletions}
Goal Conversion Rate
{data.conversionData.avgGoalConversionRate}%
Ecommerce Conv. Rate
{data.conversionData.avgEcommerceConversionRate}%
Total Revenue
${data.conversionData.totalRevenue}
Data processed: {data.raw?.length || 0} rows | Generated on: {new Date().toLocaleString()}