mirror of
https://github.com/pupperpowell/bibdle.git
synced 2026-04-05 17:33:31 -04:00
Redesign stats page with dark theme and enhanced statistics
- Implement dark gradient background with glassmorphism cards - Add new statistics: worst day, best book, most seen book, unique books by testament - Design mobile-first responsive grid layout with optimized spacing - Update Container component to support dark theme (bg-white/10, border-white/20) - Calculate book-specific stats by linking completions to daily verses - Improve visual hierarchy with icons and color-coded stat cards Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,7 +1,8 @@
|
||||
import { db } from '$lib/server/db';
|
||||
import { dailyCompletions, type DailyCompletion } from '$lib/server/db/schema';
|
||||
import { dailyCompletions, dailyVerses, type DailyCompletion } from '$lib/server/db/schema';
|
||||
import { eq, desc } from 'drizzle-orm';
|
||||
import type { PageServerLoad } from './$types';
|
||||
import { bibleBooks } from '$lib/types/bible';
|
||||
|
||||
export const load: PageServerLoad = async ({ url, locals }) => {
|
||||
// Check if user is authenticated
|
||||
@@ -51,7 +52,12 @@ export const load: PageServerLoad = async ({ url, locals }) => {
|
||||
},
|
||||
currentStreak: 0,
|
||||
bestStreak: 0,
|
||||
recentCompletions: []
|
||||
recentCompletions: [],
|
||||
worstDay: null,
|
||||
bestBook: null,
|
||||
mostSeenBook: null,
|
||||
totalBooksSeenOT: 0,
|
||||
totalBooksSeenNT: 0
|
||||
},
|
||||
user: locals.user,
|
||||
session: locals.session
|
||||
@@ -133,6 +139,66 @@ export const load: PageServerLoad = async ({ url, locals }) => {
|
||||
grade: getGradeFromGuesses(c.guessCount)
|
||||
}));
|
||||
|
||||
// Calculate worst day (highest guess count)
|
||||
const worstDay = completions.reduce((max, c) =>
|
||||
c.guessCount > max.guessCount ? c : max,
|
||||
completions[0]
|
||||
);
|
||||
|
||||
// Get all daily verses to link completions to books
|
||||
const allVerses = await db
|
||||
.select()
|
||||
.from(dailyVerses);
|
||||
|
||||
// Create a map of date -> bookId
|
||||
const dateToBookId = new Map(allVerses.map(v => [v.date, v.bookId]));
|
||||
|
||||
// Calculate book-specific stats
|
||||
const bookStats = new Map<string, { count: number; totalGuesses: number }>();
|
||||
|
||||
for (const completion of completions) {
|
||||
const bookId = dateToBookId.get(completion.date);
|
||||
if (bookId) {
|
||||
const existing = bookStats.get(bookId) || { count: 0, totalGuesses: 0 };
|
||||
bookStats.set(bookId, {
|
||||
count: existing.count + 1,
|
||||
totalGuesses: existing.totalGuesses + completion.guessCount
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Find book you know the best (lowest avg guesses)
|
||||
let bestBook: { bookId: string; avgGuesses: number; count: number } | null = null;
|
||||
for (const [bookId, stats] of bookStats.entries()) {
|
||||
const avgGuesses = stats.totalGuesses / stats.count;
|
||||
if (!bestBook || avgGuesses < bestBook.avgGuesses) {
|
||||
bestBook = { bookId, avgGuesses, count: stats.count };
|
||||
}
|
||||
}
|
||||
|
||||
// Find most seen book
|
||||
let mostSeenBook: { bookId: string; count: number } | null = null;
|
||||
for (const [bookId, stats] of bookStats.entries()) {
|
||||
if (!mostSeenBook || stats.count > mostSeenBook.count) {
|
||||
mostSeenBook = { bookId, count: stats.count };
|
||||
}
|
||||
}
|
||||
|
||||
// Count unique books by testament
|
||||
const oldTestamentBooks = new Set<string>();
|
||||
const newTestamentBooks = new Set<string>();
|
||||
|
||||
for (const [bookId, _] of bookStats.entries()) {
|
||||
const book = bibleBooks.find(b => b.id === bookId);
|
||||
if (book) {
|
||||
if (book.testament === 'old') {
|
||||
oldTestamentBooks.add(bookId);
|
||||
} else {
|
||||
newTestamentBooks.add(bookId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
stats: {
|
||||
totalSolves,
|
||||
@@ -140,7 +206,22 @@ export const load: PageServerLoad = async ({ url, locals }) => {
|
||||
gradeDistribution,
|
||||
currentStreak,
|
||||
bestStreak,
|
||||
recentCompletions
|
||||
recentCompletions,
|
||||
worstDay: {
|
||||
date: worstDay.date,
|
||||
guessCount: worstDay.guessCount
|
||||
},
|
||||
bestBook: bestBook ? {
|
||||
bookId: bestBook.bookId,
|
||||
avgGuesses: Math.round(bestBook.avgGuesses * 100) / 100,
|
||||
count: bestBook.count
|
||||
} : null,
|
||||
mostSeenBook: mostSeenBook ? {
|
||||
bookId: mostSeenBook.bookId,
|
||||
count: mostSeenBook.count
|
||||
} : null,
|
||||
totalBooksSeenOT: oldTestamentBooks.size,
|
||||
totalBooksSeenNT: newTestamentBooks.size
|
||||
},
|
||||
user: locals.user,
|
||||
session: locals.session
|
||||
|
||||
Reference in New Issue
Block a user