feat: add analytics summary API, dashboard page with stats grid, and blog post detail page.

This commit is contained in:
Timo
2026-01-02 18:40:51 +01:00
parent 0302821f0f
commit d0a114c1c3
4 changed files with 51 additions and 18 deletions

View File

@@ -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;