Nicholai 16cee69250 __Admin dashboard scaffolded with D1 database and R2 file uploads__
This commit implements the core admin dashboard functionality including NextAuth authentication, Cloudflare D1 database integration with complete schema, and Cloudflare R2 file upload system for portfolio images. Features include artist management, appointment scheduling, and data migration capabilities.
2025-09-17 16:08:34 -06:00

146 lines
5.1 KiB
TypeScript

import { NextRequest, NextResponse } from 'next/server'
import { getServerSession } from 'next-auth'
import { authOptions } from '@/lib/auth'
import { getDB } from '@/lib/db'
export const dynamic = "force-dynamic";
export async function GET(request: NextRequest, { params }: { params?: any } = {}, context?: any) {
try {
const session = await getServerSession(authOptions)
if (!session?.user) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
}
const db = getDB(context?.env)
// Get artist statistics
const artistStats = await db.prepare(`
SELECT
COUNT(*) as total,
SUM(CASE WHEN is_active = 1 THEN 1 ELSE 0 END) as active,
SUM(CASE WHEN is_active = 0 THEN 1 ELSE 0 END) as inactive
FROM artists
`).first()
// Get appointment statistics
const appointmentStats = await db.prepare(`
SELECT
COUNT(*) as total,
SUM(CASE WHEN status = 'PENDING' THEN 1 ELSE 0 END) as pending,
SUM(CASE WHEN status = 'CONFIRMED' THEN 1 ELSE 0 END) as confirmed,
SUM(CASE WHEN status = 'IN_PROGRESS' THEN 1 ELSE 0 END) as inProgress,
SUM(CASE WHEN status = 'COMPLETED' THEN 1 ELSE 0 END) as completed,
SUM(CASE WHEN status = 'CANCELLED' THEN 1 ELSE 0 END) as cancelled,
SUM(CASE WHEN strftime('%Y-%m', start_time) = strftime('%Y-%m', 'now') THEN 1 ELSE 0 END) as thisMonth,
SUM(CASE WHEN strftime('%Y-%m', start_time) = strftime('%Y-%m', 'now', '-1 month') THEN 1 ELSE 0 END) as lastMonth,
SUM(CASE WHEN status = 'COMPLETED' THEN COALESCE(total_amount, 0) ELSE 0 END) as revenue
FROM appointments
`).first()
// Get portfolio statistics
const portfolioStats = await db.prepare(`
SELECT
COUNT(*) as totalImages,
SUM(CASE WHEN date(created_at) >= date('now', '-7 days') THEN 1 ELSE 0 END) as recentUploads
FROM portfolio_images
WHERE is_public = 1
`).first()
// Get file upload statistics
const fileStats = await db.prepare(`
SELECT
COUNT(*) as totalUploads,
SUM(size) as totalSize,
SUM(CASE WHEN date(created_at) >= date('now', '-7 days') THEN 1 ELSE 0 END) as recentUploads
FROM file_uploads
`).first()
// Get monthly appointment data for the last 6 months
const monthlyData = await db.prepare(`
SELECT
strftime('%Y-%m', start_time) as month,
COUNT(*) as appointments,
SUM(CASE WHEN status = 'COMPLETED' THEN COALESCE(total_amount, 0) ELSE 0 END) as revenue
FROM appointments
WHERE start_time >= date('now', '-6 months')
GROUP BY strftime('%Y-%m', start_time)
ORDER BY month
`).all()
// Format monthly data
const formattedMonthlyData = (monthlyData.results || []).map((row: any) => ({
month: new Date(row.month + '-01').toLocaleDateString('en-US', { month: 'short', year: 'numeric' }),
appointments: row.appointments || 0,
revenue: row.revenue || 0,
}))
// Create status distribution data
const statusData = [
{
name: 'Pending',
value: (appointmentStats as any)?.pending || 0,
color: '#f59e0b',
},
{
name: 'Confirmed',
value: (appointmentStats as any)?.confirmed || 0,
color: '#3b82f6',
},
{
name: 'In Progress',
value: (appointmentStats as any)?.inProgress || 0,
color: '#10b981',
},
{
name: 'Completed',
value: (appointmentStats as any)?.completed || 0,
color: '#6b7280',
},
{
name: 'Cancelled',
value: (appointmentStats as any)?.cancelled || 0,
color: '#ef4444',
},
].filter(item => item.value > 0) // Only include statuses with values
const stats = {
artists: {
total: (artistStats as any)?.total || 0,
active: (artistStats as any)?.active || 0,
inactive: (artistStats as any)?.inactive || 0,
},
appointments: {
total: (appointmentStats as any)?.total || 0,
pending: (appointmentStats as any)?.pending || 0,
confirmed: (appointmentStats as any)?.confirmed || 0,
inProgress: (appointmentStats as any)?.inProgress || 0,
completed: (appointmentStats as any)?.completed || 0,
cancelled: (appointmentStats as any)?.cancelled || 0,
thisMonth: (appointmentStats as any)?.thisMonth || 0,
lastMonth: (appointmentStats as any)?.lastMonth || 0,
revenue: (appointmentStats as any)?.revenue || 0,
},
portfolio: {
totalImages: (portfolioStats as any)?.totalImages || 0,
recentUploads: (portfolioStats as any)?.recentUploads || 0,
},
files: {
totalUploads: (fileStats as any)?.totalUploads || 0,
totalSize: (fileStats as any)?.totalSize || 0,
recentUploads: (fileStats as any)?.recentUploads || 0,
},
monthlyData: formattedMonthlyData,
statusData,
}
return NextResponse.json(stats)
} catch (error) {
console.error('Error fetching dashboard stats:', error)
return NextResponse.json(
{ error: 'Failed to fetch dashboard statistics' },
{ status: 500 }
)
}
}