import { NextRequest, NextResponse } from 'next/server' import { supabaseAdmin } from '@/lib/supabase/admin' /** * @description Fetches comprehensive dashboard data including bookings, top performers, and activity feed */ export async function GET(request: NextRequest) { try { const { searchParams } = new URL(request.url) const locationId = searchParams.get('location_id') const startDate = searchParams.get('start_date') const endDate = searchParams.get('end_date') const staffId = searchParams.get('staff_id') const status = searchParams.get('status') const includeCustomers = searchParams.get('include_customers') === 'true' const includeTopPerformers = searchParams.get('include_top_performers') === 'true' const includeActivity = searchParams.get('include_activity') === 'true' // Get basic bookings data first let query = supabaseAdmin .from('bookings') .select('id, short_id, status, start_time_utc, end_time_utc, is_paid, created_at, customer_id, service_id, staff_id, resource_id') .order('start_time_utc', { ascending: true }) if (locationId) { query = query.eq('location_id', locationId) } if (startDate) { query = query.gte('start_time_utc', startDate) } if (endDate) { query = query.lte('end_time_utc', endDate) } if (staffId) { query = query.eq('staff_id', staffId) } if (status) { query = query.in('status', status.split(',')) } const { data: bookings, error } = await query if (error) { console.error('Aperture dashboard GET error:', error) return NextResponse.json( { error: error.message }, { status: 500 } ) } // Fetch related data for bookings const customerIds = bookings?.map(b => b.customer_id).filter(Boolean) || [] const serviceIds = bookings?.map(b => b.service_id).filter(Boolean) || [] const staffIds = bookings?.map(b => b.staff_id).filter(Boolean) || [] const resourceIds = bookings?.map(b => b.resource_id).filter(Boolean) || [] const [customers, services, staff, resources] = await Promise.all([ customerIds.length > 0 ? supabaseAdmin.from('customers').select('id, first_name, last_name, email').in('id', customerIds) : Promise.resolve({ data: [] }), serviceIds.length > 0 ? supabaseAdmin.from('services').select('id, name, duration_minutes, base_price').in('id', serviceIds) : Promise.resolve({ data: [] }), staffIds.length > 0 ? supabaseAdmin.from('staff').select('id, display_name').in('id', staffIds) : Promise.resolve({ data: [] }), resourceIds.length > 0 ? supabaseAdmin.from('resources').select('id, name, type').in('id', resourceIds) : Promise.resolve({ data: [] }) ]) const customerMap = new Map(customers.data?.map(c => [c.id, c]) || []) const serviceMap = new Map(services.data?.map(s => [s.id, s]) || []) const staffMap = new Map(staff.data?.map(s => [s.id, s]) || []) const resourceMap = new Map(resources.data?.map(r => [r.id, r]) || []) // Combine bookings with related data const bookingsWithRelations = bookings?.map(booking => ({ ...booking, customer: customerMap.get(booking.customer_id), service: serviceMap.get(booking.service_id), staff: staffMap.get(booking.staff_id), resource: resourceMap.get(booking.resource_id) })) || [] const response: any = { success: true, bookings: bookingsWithRelations } if (includeCustomers) { const { count: totalCustomers } = await supabaseAdmin .from('customers') .select('*', { count: 'exact', head: true }) const now = new Date() const todayStart = new Date(now.getFullYear(), now.getMonth(), now.getDate()) const monthStart = new Date(now.getFullYear(), now.getMonth(), 1) const { count: newCustomersToday } = await supabaseAdmin .from('customers') .select('*', { count: 'exact', head: true }) .gte('created_at', todayStart.toISOString()) const { count: newCustomersMonth } = await supabaseAdmin .from('customers') .select('*', { count: 'exact', head: true }) .gte('created_at', monthStart.toISOString()) response.customers = { total: totalCustomers || 0, newToday: newCustomersToday || 0, newMonth: newCustomersMonth || 0 } } if (includeTopPerformers) { const monthStart = new Date(new Date().getFullYear(), new Date().getMonth(), 1) // Get bookings data const { data: bookingsData } = await supabaseAdmin .from('bookings') .select('staff_id, total_amount, start_time_utc, end_time_utc') .eq('status', 'completed') .gte('end_time_utc', monthStart.toISOString()) // Get staff data separately const { data: staffData } = await supabaseAdmin .from('staff') .select('id, display_name, role') const staffMap = new Map(staffData?.map(s => [s.id, s]) || []) const staffPerformance = new Map() bookingsData?.forEach((booking: any) => { const staffId = booking.staff_id const staff = staffMap.get(staffId) if (!staffPerformance.has(staffId)) { staffPerformance.set(staffId, { staffId, displayName: staff?.display_name || 'Unknown', role: staff?.role || 'Unknown', totalBookings: 0, totalRevenue: 0, totalHours: 0 }) } const perf = staffPerformance.get(staffId) perf.totalBookings += 1 perf.totalRevenue += booking.total_amount || 0 const duration = booking.end_time_utc && booking.start_time_utc ? (new Date(booking.end_time_utc).getTime() - new Date(booking.start_time_utc).getTime()) / (1000 * 60 * 60) : 0 perf.totalHours += duration }) response.topPerformers = Array.from(staffPerformance.values()) .sort((a: any, b: any) => b.totalRevenue - a.totalRevenue) .slice(0, 10) } if (includeActivity) { // Get recent bookings const { data: recentBookings } = await supabaseAdmin .from('bookings') .select('id, short_id, status, start_time_utc, end_time_utc, created_at, customer_id, service_id, staff_id') .order('created_at', { ascending: false }) .limit(10) // Get related data const customerIds = recentBookings?.map(b => b.customer_id).filter(Boolean) || [] const serviceIds = recentBookings?.map(b => b.service_id).filter(Boolean) || [] const staffIds = recentBookings?.map(b => b.staff_id).filter(Boolean) || [] const [customers, services, staff] = await Promise.all([ customerIds.length > 0 ? supabaseAdmin.from('customers').select('id, first_name, last_name').in('id', customerIds) : Promise.resolve({ data: [] }), serviceIds.length > 0 ? supabaseAdmin.from('services').select('id, name').in('id', serviceIds) : Promise.resolve({ data: [] }), staffIds.length > 0 ? supabaseAdmin.from('staff').select('id, display_name').in('id', staffIds) : Promise.resolve({ data: [] }) ]) const customerMap = new Map(customers.data?.map(c => [c.id, c]) || []) const serviceMap = new Map(services.data?.map(s => [s.id, s]) || []) const staffMap = new Map(staff.data?.map(s => [s.id, s]) || []) const activityFeed = recentBookings?.map((booking: any) => { const customer = customerMap.get(booking.customer_id) const service = serviceMap.get(booking.service_id) const staffMember = staffMap.get(booking.staff_id) return { id: booking.id, type: 'booking', action: booking.status === 'completed' ? 'completed' : booking.status === 'confirmed' ? 'confirmed' : booking.status === 'cancelled' ? 'cancelled' : 'created', timestamp: booking.created_at, bookingShortId: booking.short_id, customerName: customer ? `${customer.first_name || ''} ${customer.last_name || ''}`.trim() : 'Unknown', serviceName: service?.name || 'Unknown', staffName: staffMember?.display_name || 'Unknown' } }) response.activityFeed = activityFeed } return NextResponse.json(response) } catch (error) { console.error('Aperture dashboard GET error:', error) return NextResponse.json( { error: 'Internal server error' }, { status: 500 } ) } }