366 lines
9.2 KiB
TypeScript
366 lines
9.2 KiB
TypeScript
import type { SupabaseClient } from '@supabase/supabase-js';
|
|
import type { SponsorTier, Sponsor, SponsorDeliverable } from '$lib/supabase/types';
|
|
import { createLogger } from '$lib/utils/logger';
|
|
|
|
const log = createLogger('api.sponsors');
|
|
|
|
// Helper to cast supabase for tables not yet in generated types
|
|
function db(supabase: SupabaseClient) {
|
|
return supabase as any;
|
|
}
|
|
|
|
export const SPONSOR_STATUSES = ['prospect', 'contacted', 'confirmed', 'declined', 'active'] as const;
|
|
export type SponsorStatus = (typeof SPONSOR_STATUSES)[number];
|
|
|
|
export const STATUS_LABELS: Record<string, string> = {
|
|
prospect: 'Prospect',
|
|
contacted: 'Contacted',
|
|
confirmed: 'Confirmed',
|
|
declined: 'Declined',
|
|
active: 'Active',
|
|
};
|
|
|
|
export const STATUS_COLORS: Record<string, string> = {
|
|
prospect: '#94a3b8',
|
|
contacted: '#F59E0B',
|
|
confirmed: '#10B981',
|
|
declined: '#EF4444',
|
|
active: '#6366f1',
|
|
};
|
|
|
|
// ============================================================
|
|
// Sponsor Tiers
|
|
// ============================================================
|
|
|
|
export async function fetchSponsorTiers(
|
|
supabase: SupabaseClient,
|
|
departmentId: string
|
|
): Promise<SponsorTier[]> {
|
|
const { data, error } = await db(supabase)
|
|
.from('sponsor_tiers')
|
|
.select('*')
|
|
.eq('department_id', departmentId)
|
|
.order('sort_order');
|
|
|
|
if (error) {
|
|
log.error('fetchSponsorTiers failed', { error, data: { departmentId } });
|
|
throw error;
|
|
}
|
|
return (data ?? []) as SponsorTier[];
|
|
}
|
|
|
|
export async function createSponsorTier(
|
|
supabase: SupabaseClient,
|
|
departmentId: string,
|
|
name: string,
|
|
amount?: number,
|
|
color?: string
|
|
): Promise<SponsorTier> {
|
|
const { data, error } = await db(supabase)
|
|
.from('sponsor_tiers')
|
|
.insert({
|
|
department_id: departmentId,
|
|
name,
|
|
amount: amount ?? 0,
|
|
color: color ?? '#F59E0B',
|
|
})
|
|
.select()
|
|
.single();
|
|
|
|
if (error) {
|
|
log.error('createSponsorTier failed', { error, data: { departmentId, name } });
|
|
throw error;
|
|
}
|
|
return data as SponsorTier;
|
|
}
|
|
|
|
export async function updateSponsorTier(
|
|
supabase: SupabaseClient,
|
|
tierId: string,
|
|
params: Partial<Pick<SponsorTier, 'name' | 'amount' | 'color' | 'sort_order'>>
|
|
): Promise<SponsorTier> {
|
|
const { data, error } = await db(supabase)
|
|
.from('sponsor_tiers')
|
|
.update(params)
|
|
.eq('id', tierId)
|
|
.select()
|
|
.single();
|
|
|
|
if (error) {
|
|
log.error('updateSponsorTier failed', { error, data: { tierId } });
|
|
throw error;
|
|
}
|
|
return data as SponsorTier;
|
|
}
|
|
|
|
export async function deleteSponsorTier(
|
|
supabase: SupabaseClient,
|
|
tierId: string
|
|
): Promise<void> {
|
|
const { error } = await db(supabase)
|
|
.from('sponsor_tiers')
|
|
.delete()
|
|
.eq('id', tierId);
|
|
|
|
if (error) {
|
|
log.error('deleteSponsorTier failed', { error, data: { tierId } });
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
// ============================================================
|
|
// Event-wide Tier + Sponsor fetching
|
|
// ============================================================
|
|
|
|
export async function fetchEventSponsorTiers(
|
|
supabase: SupabaseClient,
|
|
eventId: string
|
|
): Promise<SponsorTier[]> {
|
|
const { data, error } = await db(supabase)
|
|
.from('sponsor_tiers')
|
|
.select('*, event_departments!inner(event_id)')
|
|
.eq('event_departments.event_id', eventId)
|
|
.order('sort_order');
|
|
|
|
if (error) {
|
|
log.error('fetchEventSponsorTiers failed', { error, data: { eventId } });
|
|
throw error;
|
|
}
|
|
return (data ?? []).map((d: any) => {
|
|
const { event_departments, ...tier } = d;
|
|
return tier;
|
|
}) as SponsorTier[];
|
|
}
|
|
|
|
export async function fetchEventSponsors(
|
|
supabase: SupabaseClient,
|
|
eventId: string
|
|
): Promise<Sponsor[]> {
|
|
const { data, error } = await db(supabase)
|
|
.from('sponsors')
|
|
.select('*, event_departments!inner(event_id)')
|
|
.eq('event_departments.event_id', eventId)
|
|
.order('name');
|
|
|
|
if (error) {
|
|
log.error('fetchEventSponsors failed', { error, data: { eventId } });
|
|
throw error;
|
|
}
|
|
return (data ?? []).map((d: any) => {
|
|
const { event_departments, ...sponsor } = d;
|
|
return sponsor;
|
|
}) as Sponsor[];
|
|
}
|
|
|
|
export async function fetchEventDeliverables(
|
|
supabase: SupabaseClient,
|
|
eventId: string
|
|
): Promise<SponsorDeliverable[]> {
|
|
const { data, error } = await db(supabase)
|
|
.from('sponsor_deliverables')
|
|
.select('*, sponsors!inner(department_id, event_departments!inner(event_id))')
|
|
.eq('sponsors.event_departments.event_id', eventId)
|
|
.order('sort_order');
|
|
|
|
if (error) {
|
|
log.error('fetchEventDeliverables failed', { error, data: { eventId } });
|
|
throw error;
|
|
}
|
|
return (data ?? []).map((d: any) => {
|
|
const { sponsors, ...del } = d;
|
|
return del;
|
|
}) as SponsorDeliverable[];
|
|
}
|
|
|
|
// ============================================================
|
|
// Sponsors
|
|
// ============================================================
|
|
|
|
export async function fetchSponsors(
|
|
supabase: SupabaseClient,
|
|
departmentId: string
|
|
): Promise<Sponsor[]> {
|
|
const { data, error } = await db(supabase)
|
|
.from('sponsors')
|
|
.select('*')
|
|
.eq('department_id', departmentId)
|
|
.order('name');
|
|
|
|
if (error) {
|
|
log.error('fetchSponsors failed', { error, data: { departmentId } });
|
|
throw error;
|
|
}
|
|
return (data ?? []) as Sponsor[];
|
|
}
|
|
|
|
export async function createSponsor(
|
|
supabase: SupabaseClient,
|
|
departmentId: string,
|
|
params: {
|
|
name: string;
|
|
tier_id?: string | null;
|
|
contact_name?: string;
|
|
contact_email?: string;
|
|
contact_phone?: string;
|
|
website?: string;
|
|
logo_url?: string;
|
|
status?: SponsorStatus;
|
|
amount?: number;
|
|
notes?: string;
|
|
}
|
|
): Promise<Sponsor> {
|
|
const { data, error } = await db(supabase)
|
|
.from('sponsors')
|
|
.insert({
|
|
department_id: departmentId,
|
|
name: params.name,
|
|
tier_id: params.tier_id ?? null,
|
|
contact_name: params.contact_name ?? null,
|
|
contact_email: params.contact_email ?? null,
|
|
contact_phone: params.contact_phone ?? null,
|
|
website: params.website ?? null,
|
|
logo_url: params.logo_url ?? null,
|
|
status: params.status ?? 'prospect',
|
|
amount: params.amount ?? 0,
|
|
notes: params.notes ?? null,
|
|
})
|
|
.select()
|
|
.single();
|
|
|
|
if (error) {
|
|
log.error('createSponsor failed', { error, data: { departmentId, name: params.name } });
|
|
throw error;
|
|
}
|
|
return data as Sponsor;
|
|
}
|
|
|
|
export async function updateSponsor(
|
|
supabase: SupabaseClient,
|
|
sponsorId: string,
|
|
params: Partial<Pick<Sponsor, 'name' | 'tier_id' | 'contact_name' | 'contact_email' | 'contact_phone' | 'website' | 'logo_url' | 'status' | 'amount' | 'notes'>>
|
|
): Promise<Sponsor> {
|
|
const { data, error } = await db(supabase)
|
|
.from('sponsors')
|
|
.update({ ...params, updated_at: new Date().toISOString() })
|
|
.eq('id', sponsorId)
|
|
.select()
|
|
.single();
|
|
|
|
if (error) {
|
|
log.error('updateSponsor failed', { error, data: { sponsorId } });
|
|
throw error;
|
|
}
|
|
return data as Sponsor;
|
|
}
|
|
|
|
export async function deleteSponsor(
|
|
supabase: SupabaseClient,
|
|
sponsorId: string
|
|
): Promise<void> {
|
|
const { error } = await db(supabase)
|
|
.from('sponsors')
|
|
.delete()
|
|
.eq('id', sponsorId);
|
|
|
|
if (error) {
|
|
log.error('deleteSponsor failed', { error, data: { sponsorId } });
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
// ============================================================
|
|
// Sponsor Deliverables
|
|
// ============================================================
|
|
|
|
export async function fetchDeliverables(
|
|
supabase: SupabaseClient,
|
|
sponsorId: string
|
|
): Promise<SponsorDeliverable[]> {
|
|
const { data, error } = await db(supabase)
|
|
.from('sponsor_deliverables')
|
|
.select('*')
|
|
.eq('sponsor_id', sponsorId)
|
|
.order('sort_order');
|
|
|
|
if (error) {
|
|
log.error('fetchDeliverables failed', { error, data: { sponsorId } });
|
|
throw error;
|
|
}
|
|
return (data ?? []) as SponsorDeliverable[];
|
|
}
|
|
|
|
export async function fetchAllDeliverables(
|
|
supabase: SupabaseClient,
|
|
sponsorIds: string[]
|
|
): Promise<SponsorDeliverable[]> {
|
|
if (sponsorIds.length === 0) return [];
|
|
const { data, error } = await db(supabase)
|
|
.from('sponsor_deliverables')
|
|
.select('*')
|
|
.in('sponsor_id', sponsorIds)
|
|
.order('sort_order');
|
|
|
|
if (error) {
|
|
log.error('fetchAllDeliverables failed', { error, data: { sponsorIds } });
|
|
throw error;
|
|
}
|
|
return (data ?? []) as SponsorDeliverable[];
|
|
}
|
|
|
|
export async function createDeliverable(
|
|
supabase: SupabaseClient,
|
|
sponsorId: string,
|
|
description: string,
|
|
dueDate?: string
|
|
): Promise<SponsorDeliverable> {
|
|
const { data, error } = await db(supabase)
|
|
.from('sponsor_deliverables')
|
|
.insert({
|
|
sponsor_id: sponsorId,
|
|
description,
|
|
due_date: dueDate ?? null,
|
|
})
|
|
.select()
|
|
.single();
|
|
|
|
if (error) {
|
|
log.error('createDeliverable failed', { error, data: { sponsorId, description } });
|
|
throw error;
|
|
}
|
|
return data as SponsorDeliverable;
|
|
}
|
|
|
|
export async function updateDeliverable(
|
|
supabase: SupabaseClient,
|
|
deliverableId: string,
|
|
params: Partial<Pick<SponsorDeliverable, 'description' | 'is_completed' | 'due_date' | 'sort_order'>>
|
|
): Promise<SponsorDeliverable> {
|
|
const { data, error } = await db(supabase)
|
|
.from('sponsor_deliverables')
|
|
.update({ ...params, updated_at: new Date().toISOString() })
|
|
.eq('id', deliverableId)
|
|
.select()
|
|
.single();
|
|
|
|
if (error) {
|
|
log.error('updateDeliverable failed', { error, data: { deliverableId } });
|
|
throw error;
|
|
}
|
|
return data as SponsorDeliverable;
|
|
}
|
|
|
|
export async function deleteDeliverable(
|
|
supabase: SupabaseClient,
|
|
deliverableId: string
|
|
): Promise<void> {
|
|
const { error } = await db(supabase)
|
|
.from('sponsor_deliverables')
|
|
.delete()
|
|
.eq('id', deliverableId);
|
|
|
|
if (error) {
|
|
log.error('deleteDeliverable failed', { error, data: { deliverableId } });
|
|
throw error;
|
|
}
|
|
}
|