From 641bef46cbf5e43faeaaa6d7bed823b8e4bc6138 Mon Sep 17 00:00:00 2001 From: moyoza Date: Thu, 29 Jan 2026 08:40:30 +0200 Subject: [PATCH] feat: Refine admin guest counting logic, increase stats refresh rate, and add guest count utility scripts. --- check-counts.js | 20 +++++++++++ check-counts.ts | 25 +++++++++++++ src/app/admin/page.tsx | 36 +++++++++---------- src/app/api/admin/stats/route.ts | 6 ++-- .../features/admin/admin-dashboard.tsx | 5 +++ src/contexts/admin-stats-context.tsx | 2 +- 6 files changed, 70 insertions(+), 24 deletions(-) create mode 100644 check-counts.js create mode 100644 check-counts.ts diff --git a/check-counts.js b/check-counts.js new file mode 100644 index 0000000..47f8693 --- /dev/null +++ b/check-counts.js @@ -0,0 +1,20 @@ +const { PrismaClient } = require('@prisma/client'); +const prisma = new PrismaClient(); + +async function main() { + const total = await prisma.guest.count(); + const confirmed = await prisma.guest.count({ + where: { + OR: [ + { rsvpStatus: 'ACCEPTED' }, + { AND: [{ rsvpStatus: null }, { isAttending: true }] } + ] + } + }); + console.log(`Total Guests: ${total}`); + console.log(`Confirmed Guests: ${confirmed}`); +} + +main() + .catch(e => console.error(e)) + .finally(async () => await prisma.$disconnect()); diff --git a/check-counts.ts b/check-counts.ts new file mode 100644 index 0000000..ee65c27 --- /dev/null +++ b/check-counts.ts @@ -0,0 +1,25 @@ +import { prisma } from "@/lib/db"; + +async function main() { + console.log('Checking database counts...'); + try { + const total = await prisma.guest.count(); + const confirmed = await prisma.guest.count({ + where: { + OR: [ + { rsvpStatus: 'ACCEPTED' }, + { AND: [{ rsvpStatus: 'PENDING' }, { isAttending: true }] } + ] + } + }); + console.log(`Total Guests: ${total}`); + console.log(`Confirmed Guests: ${confirmed}`); + + } catch (error) { + console.error('Error querying database:', error); + } +} + +main() + .catch(e => console.error(e)) + .finally(async () => await prisma.$disconnect()); diff --git a/src/app/admin/page.tsx b/src/app/admin/page.tsx index 993bdca..bd589e7 100644 --- a/src/app/admin/page.tsx +++ b/src/app/admin/page.tsx @@ -13,7 +13,7 @@ const AdminDashboard = dynamic( // This prevents server-only code from being evaluated during bundling if (typeof window === 'undefined') { // On server, return a no-op component that won't be used (ssr: false) - return Promise.resolve({ + return Promise.resolve({ default: () => { if (typeof window === 'undefined') { return null; @@ -43,8 +43,8 @@ const AdminDashboard = dynamic( const AdminLayout = dynamic( () => { if (typeof window === 'undefined') { - return Promise.resolve({ - default: () => null + return Promise.resolve({ + default: () => null }); } return import("@/components/features/admin/admin-layout").then((mod) => ({ default: mod.AdminLayout })); @@ -84,7 +84,7 @@ function AdminContent() { method: 'GET', credentials: 'include', }); - + if (response.ok) { let data; try { @@ -126,14 +126,14 @@ function AdminContent() { // Use API route for all authentication (handles both email and username) // This avoids client-side network issues and ensures proper cookie handling const isEmail = trimmedUsername.includes('@'); - + const response = await fetch('/api/admin/auth', { method: 'POST', credentials: 'include', headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ + body: JSON.stringify({ [isEmail ? 'email' : 'username']: trimmedUsername, - password: trimmedPassword + password: trimmedPassword }), }); @@ -141,24 +141,24 @@ function AdminContent() { try { // Read response body once const text = await response.text(); - + // Check content type const contentType = response.headers.get('content-type'); if (!contentType || !contentType.includes('application/json')) { // Handle non-JSON responses (e.g., HTML error pages, plain text errors) - const errorMessage = text.includes('Internal Server Error') + const errorMessage = text.includes('Internal Server Error') ? 'Server error occurred. Please try again or check server logs.' : text.substring(0, 200) || `Server error: ${response.status} ${response.statusText}`; - + // Only log full error in development if (process.env.NODE_ENV === 'development') { console.error('Non-JSON response received:', text.substring(0, 200)); } - + setError(errorMessage); return; } - + // Parse JSON data = text ? JSON.parse(text) : {}; } catch (parseError) { @@ -166,11 +166,11 @@ function AdminContent() { const errorMessage = response.status >= 500 ? 'Server error occurred. Please try again.' : 'Invalid response from server. Please check your connection.'; - + if (process.env.NODE_ENV === 'development') { console.error('Failed to parse response:', parseError); } - + setError(errorMessage); return; } @@ -201,7 +201,7 @@ function AdminContent() { const handleLogout = async () => { try { // Call API logout (handles Supabase sign out server-side) - await fetch('/api/admin/auth', { + await fetch('/api/admin/auth', { method: 'DELETE', credentials: 'include', }); @@ -365,16 +365,12 @@ function AdminContent() { ); } - const handleSettingsClick = () => { - // Navigate to settings tab - window.location.href = '/admin?tab=settings'; - }; return (
Loading dashboard...
}> - +
diff --git a/src/app/api/admin/stats/route.ts b/src/app/api/admin/stats/route.ts index af0b276..451ac93 100644 --- a/src/app/api/admin/stats/route.ts +++ b/src/app/api/admin/stats/route.ts @@ -35,7 +35,7 @@ async function fetchStatsFromDatabase() { { rsvpStatus: 'ACCEPTED' }, { AND: [ - { OR: [{ rsvpStatus: null }, { rsvpStatus: 'PENDING' }] }, + { rsvpStatus: 'PENDING' }, { isAttending: true } ] } @@ -48,7 +48,7 @@ async function fetchStatsFromDatabase() { { rsvpStatus: 'DECLINED' }, { AND: [ - { OR: [{ rsvpStatus: null }, { rsvpStatus: 'PENDING' }] }, + { rsvpStatus: 'PENDING' }, { isAttending: false } ] } @@ -62,7 +62,7 @@ async function fetchStatsFromDatabase() { { rsvpStatus: 'ACCEPTED' }, { AND: [ - { OR: [{ rsvpStatus: null }, { rsvpStatus: 'PENDING' }] }, + { rsvpStatus: 'PENDING' }, { isAttending: true } ] } diff --git a/src/components/features/admin/admin-dashboard.tsx b/src/components/features/admin/admin-dashboard.tsx index b3652f2..2220480 100644 --- a/src/components/features/admin/admin-dashboard.tsx +++ b/src/components/features/admin/admin-dashboard.tsx @@ -64,6 +64,11 @@ export function AdminDashboard({ const searchParams = useSearchParams(); const tabFromUrl = searchParams.get("tab") || defaultTab; const [activeTab, setActiveTab] = useState(tabFromUrl); + + useEffect(() => { + console.log("AdminDashboard mounted"); + }, []); + const { toast } = useToast(); // Unified Data Hooks diff --git a/src/contexts/admin-stats-context.tsx b/src/contexts/admin-stats-context.tsx index 3a458a1..911c533 100644 --- a/src/contexts/admin-stats-context.tsx +++ b/src/contexts/admin-stats-context.tsx @@ -105,7 +105,7 @@ export function AdminStatsProvider({ children }: AdminStatsProviderProps) { useEffect(() => { const interval = setInterval(() => { refresh(true); // Silent refresh - }, 60000); // 60 seconds + }, 5000); // 5 seconds return () => clearInterval(interval); }, [refresh]);