feat: add analytics summary API, dashboard page with stats grid, and blog post detail page.
This commit is contained in:
@@ -140,8 +140,17 @@ export async function GET(request: NextRequest) {
|
||||
|
||||
// Calculate trends
|
||||
const scansTrend = calculateTrend(totalScans, previousTotalScans);
|
||||
const avgScansTrend = calculateTrend(avgScansPerQR, previousAvgScansPerQR);
|
||||
|
||||
|
||||
// New Conversion Rate Logic: (Unique Scans / Total Scans) * 100
|
||||
// This represents "Engagement Efficiency" - how many scans are from fresh users
|
||||
const currentConversion = totalScans > 0 ? Math.round((uniqueScans / totalScans) * 100) : 0;
|
||||
|
||||
const previousConversion = previousTotalScans > 0
|
||||
? Math.round((previousUniqueScans / previousTotalScans) * 100)
|
||||
: 0;
|
||||
|
||||
const avgScansTrend = calculateTrend(currentConversion, previousConversion);
|
||||
|
||||
// Device stats
|
||||
const deviceStats = qrCodes.flatMap(qr => qr.scans)
|
||||
.reduce((acc, scan) => {
|
||||
@@ -149,12 +158,12 @@ export async function GET(request: NextRequest) {
|
||||
acc[device] = (acc[device] || 0) + 1;
|
||||
return acc;
|
||||
}, {} as Record<string, number>);
|
||||
|
||||
|
||||
const mobileScans = (deviceStats.mobile || 0) + (deviceStats.tablet || 0);
|
||||
const mobilePercentage = totalScans > 0
|
||||
? Math.round((mobileScans / totalScans) * 100)
|
||||
const mobilePercentage = totalScans > 0
|
||||
? Math.round((mobileScans / totalScans) * 100)
|
||||
: 0;
|
||||
|
||||
|
||||
// Country stats (current period)
|
||||
const countryStats = qrCodes.flatMap(qr => qr.scans)
|
||||
.reduce((acc, scan) => {
|
||||
@@ -172,8 +181,8 @@ export async function GET(request: NextRequest) {
|
||||
}, {} as Record<string, number>);
|
||||
|
||||
const topCountry = Object.entries(countryStats)
|
||||
.sort(([,a], [,b]) => b - a)[0];
|
||||
|
||||
.sort(([, a], [, b]) => b - a)[0];
|
||||
|
||||
// Daily scan counts for chart (current period)
|
||||
const dailyScans = qrCodes.flatMap(qr => qr.scans).reduce((acc, scan) => {
|
||||
const date = new Date(scan.ts).toISOString().split('T')[0];
|
||||
@@ -215,7 +224,7 @@ export async function GET(request: NextRequest) {
|
||||
summary: {
|
||||
totalScans,
|
||||
uniqueScans,
|
||||
avgScansPerQR,
|
||||
avgScansPerQR: currentConversion, // Now sending Unique Rate instead of Avg per QR
|
||||
mobilePercentage,
|
||||
topCountry: topCountry ? topCountry[0] : 'N/A',
|
||||
topCountryPercentage: topCountry && totalScans > 0
|
||||
@@ -228,7 +237,7 @@ export async function GET(request: NextRequest) {
|
||||
},
|
||||
deviceStats,
|
||||
countryStats: Object.entries(countryStats)
|
||||
.sort(([,a], [,b]) => b - a)
|
||||
.sort(([, a], [, b]) => b - a)
|
||||
.slice(0, 10)
|
||||
.map(([country, count]) => {
|
||||
const previousCount = previousCountryStats[country] || 0;
|
||||
|
||||
Reference in New Issue
Block a user