import { supabase } from './supabase'; import type { Database } from './database.types'; export interface ApiResponse { data: T | null; error: string | null; success: boolean; } export interface EventStats { ticketsSold: number; ticketsAvailable: number; checkedIn: number; totalRevenue: number; netRevenue: number; } export interface EventDetails { id: string; title: string; description: string; venue: string; start_time: string; slug: string; organization_id: string; } class ApiClient { private authenticated = false; private session: any = null; private userOrganizationId: string | null = null; async initialize(): Promise { try { const { data: { session }, error: sessionError } = await supabase.auth.getSession(); if (sessionError) { this.authenticated = false; return false; } if (!session) { this.authenticated = false; return false; } this.session = session; this.authenticated = true; // Get user's organization const { data: userRecord, error: userError } = await supabase .from('users') .select('organization_id') .eq('id', session.user.id) .single(); if (userError) { } this.userOrganizationId = userRecord?.organization_id || null; return true; } catch (error) { this.authenticated = false; return false; } } private async ensureAuthenticated(): Promise { if (!this.authenticated) { const initialized = await this.initialize(); if (!initialized) { // Don't automatically redirect here - let the component handle it return false; } } return true; } async getEventDetails(eventId: string): Promise> { try { if (!(await this.ensureAuthenticated())) { return { data: null, error: 'Authentication required', success: false }; } const { data: event, error } = await supabase .from('events') .select('*') .eq('id', eventId) .single(); if (error) { return { data: null, error: error.message, success: false }; } return { data: event, error: null, success: true }; } catch (error) { return { data: null, error: 'Failed to load event details', success: false }; } } async getEventStats(eventId: string): Promise> { try { if (!(await this.ensureAuthenticated())) { return { data: null, error: 'Authentication required', success: false }; } // Load ticket sales data const { data: tickets, error: ticketsError } = await supabase .from('tickets') .select(` id, price, checked_in, refund_status, ticket_types ( id, quantity_available ) `) .eq('event_id', eventId); if (ticketsError) { return { data: null, error: ticketsError.message, success: false }; } // Load ticket types for capacity calculation const { data: ticketTypes, error: ticketTypesError } = await supabase .from('ticket_types') .select('id, quantity_available') .eq('event_id', eventId) .eq('is_active', true); if (ticketTypesError) { return { data: null, error: ticketTypesError.message, success: false }; } // Calculate stats const ticketsSold = tickets?.length || 0; // Only count non-refunded tickets for revenue const activeTickets = tickets?.filter(ticket => !ticket.refund_status || ticket.refund_status === null ) || []; const totalRevenue = activeTickets.reduce((sum, ticket) => sum + ticket.price, 0) || 0; const netRevenue = totalRevenue * 0.97; // Assuming 3% platform fee const checkedIn = tickets?.filter(ticket => ticket.checked_in).length || 0; const totalCapacity = ticketTypes?.reduce((sum, type) => sum + type.quantity_available, 0) || 0; const ticketsAvailable = totalCapacity - ticketsSold; const stats: EventStats = { ticketsSold, ticketsAvailable, checkedIn, totalRevenue, netRevenue }; return { data: stats, error: null, success: true }; } catch (error) { return { data: null, error: 'Failed to load event statistics', success: false }; } } async getTicketTypes(eventId: string): Promise> { try { if (!(await this.ensureAuthenticated())) { return { data: null, error: 'Authentication required', success: false }; } const { data, error } = await supabase .from('ticket_types') .select('*') .eq('event_id', eventId) .eq('is_active', true) .order('display_order', { ascending: true }); if (error) { return { data: null, error: error.message, success: false }; } return { data: data || [], error: null, success: true }; } catch (error) { return { data: null, error: 'Failed to load ticket types', success: false }; } } async getOrders(eventId: string): Promise> { try { if (!(await this.ensureAuthenticated())) { return { data: null, error: 'Authentication required', success: false }; } const { data, error } = await supabase .from('tickets') .select(` *, ticket_types ( name, price ) `) .eq('event_id', eventId) .order('created_at', { ascending: false }); if (error) { return { data: null, error: error.message, success: false }; } return { data: data || [], error: null, success: true }; } catch (error) { return { data: null, error: 'Failed to load orders', success: false }; } } async getPresaleCodes(eventId: string): Promise> { try { if (!(await this.ensureAuthenticated())) { return { data: null, error: 'Authentication required', success: false }; } const { data, error } = await supabase .from('presale_codes') .select('*') .eq('event_id', eventId) .order('created_at', { ascending: false }); if (error) { return { data: null, error: error.message, success: false }; } return { data: data || [], error: null, success: true }; } catch (error) { return { data: null, error: 'Failed to load presale codes', success: false }; } } async getDiscountCodes(eventId: string): Promise> { try { if (!(await this.ensureAuthenticated())) { return { data: null, error: 'Authentication required', success: false }; } const { data, error } = await supabase .from('discount_codes') .select('*') .eq('event_id', eventId) .order('created_at', { ascending: false }); if (error) { return { data: null, error: error.message, success: false }; } return { data: data || [], error: null, success: true }; } catch (error) { return { data: null, error: 'Failed to load discount codes', success: false }; } } async getAttendees(eventId: string): Promise> { try { if (!(await this.ensureAuthenticated())) { return { data: null, error: 'Authentication required', success: false }; } const { data, error } = await supabase .from('tickets') .select(` *, ticket_types ( name ) `) .eq('event_id', eventId) .is('refund_status', null) .order('created_at', { ascending: false }); if (error) { return { data: null, error: error.message, success: false }; } return { data: data || [], error: null, success: true }; } catch (error) { return { data: null, error: 'Failed to load attendees', success: false }; } } // Utility methods getUserOrganizationId(): string | null { return this.userOrganizationId; } getUser(): any { return this.session?.user || null; } getSession(): any { return this.session; } isAuthenticated(): boolean { return this.authenticated; } async logout(): Promise { await supabase.auth.signOut(); this.authenticated = false; this.session = null; this.userOrganizationId = null; } // Generic authenticated request helper async makeAuthenticatedRequest( url: string, options: RequestInit = {} ): Promise> { try { if (!(await this.ensureAuthenticated())) { return { data: null, error: 'Authentication required', success: false }; } const response = await fetch(url, { ...options, headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${this.session?.access_token}`, ...options.headers, }, }); if (!response.ok) { const errorText = await response.text(); return { data: null, error: errorText || `HTTP ${response.status}`, success: false }; } const data = await response.json(); return { data, error: null, success: true }; } catch (error) { return { data: null, error: error instanceof Error ? error.message : 'Request failed', success: false }; } } } // Export a singleton instance export const apiClient = new ApiClient(); // Export a hook-like function for easier use in components export async function useApi() { if (!apiClient.isAuthenticated()) { await apiClient.initialize(); } return apiClient; } // Export the makeAuthenticatedRequest function for direct use export async function makeAuthenticatedRequest( url: string, options: RequestInit = {} ): Promise> { return apiClient.makeAuthenticatedRequest(url, options); }