Mega push vol 7 mvp lesgoooo

This commit is contained in:
AlacrisDevs
2026-02-07 21:47:47 +02:00
parent dcee479839
commit d22847f555
75 changed files with 7685 additions and 892 deletions

176
src/lib/api/budget.ts Normal file
View File

@@ -0,0 +1,176 @@
import type { SupabaseClient } from '@supabase/supabase-js';
import type { BudgetCategory, BudgetItem } from '$lib/supabase/types';
import { createLogger } from '$lib/utils/logger';
const log = createLogger('api.budget');
// Helper to cast supabase for tables not yet in generated types
function db(supabase: SupabaseClient) {
return supabase as any;
}
// ============================================================
// Budget Categories
// ============================================================
export async function fetchBudgetCategories(
supabase: SupabaseClient,
departmentId: string
): Promise<BudgetCategory[]> {
const { data, error } = await db(supabase)
.from('budget_categories')
.select('*')
.eq('department_id', departmentId)
.order('sort_order');
if (error) {
log.error('fetchBudgetCategories failed', { error, data: { departmentId } });
throw error;
}
return (data ?? []) as BudgetCategory[];
}
export async function createBudgetCategory(
supabase: SupabaseClient,
departmentId: string,
name: string,
color?: string
): Promise<BudgetCategory> {
const { data, error } = await db(supabase)
.from('budget_categories')
.insert({
department_id: departmentId,
name,
color: color ?? '#6366f1',
})
.select()
.single();
if (error) {
log.error('createBudgetCategory failed', { error, data: { departmentId, name } });
throw error;
}
return data as BudgetCategory;
}
export async function updateBudgetCategory(
supabase: SupabaseClient,
categoryId: string,
params: Partial<Pick<BudgetCategory, 'name' | 'color' | 'sort_order'>>
): Promise<BudgetCategory> {
const { data, error } = await db(supabase)
.from('budget_categories')
.update(params)
.eq('id', categoryId)
.select()
.single();
if (error) {
log.error('updateBudgetCategory failed', { error, data: { categoryId } });
throw error;
}
return data as BudgetCategory;
}
export async function deleteBudgetCategory(
supabase: SupabaseClient,
categoryId: string
): Promise<void> {
const { error } = await db(supabase)
.from('budget_categories')
.delete()
.eq('id', categoryId);
if (error) {
log.error('deleteBudgetCategory failed', { error, data: { categoryId } });
throw error;
}
}
// ============================================================
// Budget Items
// ============================================================
export async function fetchBudgetItems(
supabase: SupabaseClient,
departmentId: string
): Promise<BudgetItem[]> {
const { data, error } = await db(supabase)
.from('budget_items')
.select('*')
.eq('department_id', departmentId)
.order('sort_order');
if (error) {
log.error('fetchBudgetItems failed', { error, data: { departmentId } });
throw error;
}
return (data ?? []) as BudgetItem[];
}
export async function createBudgetItem(
supabase: SupabaseClient,
departmentId: string,
params: {
description: string;
item_type: 'income' | 'expense';
planned_amount?: number;
actual_amount?: number;
category_id?: string | null;
notes?: string;
}
): Promise<BudgetItem> {
const { data, error } = await db(supabase)
.from('budget_items')
.insert({
department_id: departmentId,
description: params.description,
item_type: params.item_type,
planned_amount: params.planned_amount ?? 0,
actual_amount: params.actual_amount ?? 0,
category_id: params.category_id ?? null,
notes: params.notes ?? null,
})
.select()
.single();
if (error) {
log.error('createBudgetItem failed', { error, data: { departmentId, description: params.description } });
throw error;
}
return data as BudgetItem;
}
export async function updateBudgetItem(
supabase: SupabaseClient,
itemId: string,
params: Partial<Pick<BudgetItem, 'description' | 'item_type' | 'planned_amount' | 'actual_amount' | 'category_id' | 'notes' | 'sort_order'>>
): Promise<BudgetItem> {
const { data, error } = await db(supabase)
.from('budget_items')
.update({ ...params, updated_at: new Date().toISOString() })
.eq('id', itemId)
.select()
.single();
if (error) {
log.error('updateBudgetItem failed', { error, data: { itemId } });
throw error;
}
return data as BudgetItem;
}
export async function deleteBudgetItem(
supabase: SupabaseClient,
itemId: string
): Promise<void> {
const { error } = await db(supabase)
.from('budget_items')
.delete()
.eq('id', itemId);
if (error) {
log.error('deleteBudgetItem failed', { error, data: { itemId } });
throw error;
}
}

147
src/lib/api/contacts.ts Normal file
View File

@@ -0,0 +1,147 @@
import type { SupabaseClient } from '@supabase/supabase-js';
import type { DepartmentContact } from '$lib/supabase/types';
import { createLogger } from '$lib/utils/logger';
const log = createLogger('api.contacts');
// Helper to cast supabase for tables not yet in generated types
function db(supabase: SupabaseClient) {
return supabase as any;
}
export const CONTACT_CATEGORIES = [
'general',
'vendor',
'sponsor',
'speaker',
'venue',
'catering',
'av_tech',
'transport',
'security',
'media',
] as const;
export type ContactCategory = (typeof CONTACT_CATEGORIES)[number];
export const CATEGORY_LABELS: Record<string, string> = {
general: 'General',
vendor: 'Vendor',
sponsor: 'Sponsor',
speaker: 'Speaker',
venue: 'Venue',
catering: 'Catering',
av_tech: 'AV / Tech',
transport: 'Transport',
security: 'Security',
media: 'Media',
};
export const CATEGORY_ICONS: Record<string, string> = {
general: 'person',
vendor: 'storefront',
sponsor: 'handshake',
speaker: 'mic',
venue: 'location_on',
catering: 'restaurant',
av_tech: 'settings_input_hdmi',
transport: 'local_shipping',
security: 'shield',
media: 'videocam',
};
// ============================================================
// CRUD
// ============================================================
export async function fetchContacts(
supabase: SupabaseClient,
departmentId: string
): Promise<DepartmentContact[]> {
const { data, error } = await db(supabase)
.from('department_contacts')
.select('*')
.eq('department_id', departmentId)
.order('name');
if (error) {
log.error('fetchContacts failed', { error, data: { departmentId } });
throw error;
}
return (data ?? []) as DepartmentContact[];
}
export async function createContact(
supabase: SupabaseClient,
departmentId: string,
params: {
name: string;
role?: string;
company?: string;
email?: string;
phone?: string;
website?: string;
notes?: string;
category?: string;
color?: string;
},
userId?: string
): Promise<DepartmentContact> {
const { data, error } = await db(supabase)
.from('department_contacts')
.insert({
department_id: departmentId,
name: params.name,
role: params.role ?? null,
company: params.company ?? null,
email: params.email ?? null,
phone: params.phone ?? null,
website: params.website ?? null,
notes: params.notes ?? null,
category: params.category ?? 'general',
color: params.color ?? '#00A3E0',
created_by: userId ?? null,
})
.select()
.single();
if (error) {
log.error('createContact failed', { error, data: { departmentId, name: params.name } });
throw error;
}
return data as DepartmentContact;
}
export async function updateContact(
supabase: SupabaseClient,
contactId: string,
params: Partial<Pick<DepartmentContact, 'name' | 'role' | 'company' | 'email' | 'phone' | 'website' | 'notes' | 'category' | 'color'>>
): Promise<DepartmentContact> {
const { data, error } = await db(supabase)
.from('department_contacts')
.update({ ...params, updated_at: new Date().toISOString() })
.eq('id', contactId)
.select()
.single();
if (error) {
log.error('updateContact failed', { error, data: { contactId } });
throw error;
}
return data as DepartmentContact;
}
export async function deleteContact(
supabase: SupabaseClient,
contactId: string
): Promise<void> {
const { error } = await db(supabase)
.from('department_contacts')
.delete()
.eq('id', contactId);
if (error) {
log.error('deleteContact failed', { error, data: { contactId } });
throw error;
}
}

View File

@@ -0,0 +1,354 @@
import type { SupabaseClient } from '@supabase/supabase-js';
import type { Database, DepartmentDashboard, DashboardPanel, DepartmentChecklist, DepartmentChecklistItem, DepartmentNote, ModuleType, LayoutPreset } from '$lib/supabase/types';
import { createLogger } from '$lib/utils/logger';
const log = createLogger('api.department-dashboard');
// ============================================================
// Dashboard
// ============================================================
export interface DashboardWithPanels extends DepartmentDashboard {
panels: DashboardPanel[];
}
export async function fetchDashboard(
supabase: SupabaseClient<Database>,
departmentId: string
): Promise<DashboardWithPanels | null> {
const { data, error } = await supabase
.from('department_dashboards')
.select('*, panels:dashboard_panels(*)')
.eq('department_id', departmentId)
.single();
if (error) {
if (error.code === 'PGRST116') return null;
log.error('fetchDashboard failed', { error, data: { departmentId } });
throw error;
}
const dashboard = data as any;
return {
...dashboard,
panels: (dashboard.panels ?? []).sort((a: DashboardPanel, b: DashboardPanel) => a.position - b.position),
};
}
export async function updateDashboardLayout(
supabase: SupabaseClient<Database>,
dashboardId: string,
layout: LayoutPreset
): Promise<DepartmentDashboard> {
const { data, error } = await supabase
.from('department_dashboards')
.update({ layout, updated_at: new Date().toISOString() })
.eq('id', dashboardId)
.select()
.single();
if (error) {
log.error('updateDashboardLayout failed', { error, data: { dashboardId, layout } });
throw error;
}
return data as unknown as DepartmentDashboard;
}
// ============================================================
// Panels
// ============================================================
export async function addPanel(
supabase: SupabaseClient<Database>,
dashboardId: string,
module: ModuleType,
position: number,
width: string = 'half'
): Promise<DashboardPanel> {
const { data, error } = await supabase
.from('dashboard_panels')
.insert({ dashboard_id: dashboardId, module, position, width })
.select()
.single();
if (error) {
log.error('addPanel failed', { error, data: { dashboardId, module } });
throw error;
}
return data as unknown as DashboardPanel;
}
export async function updatePanel(
supabase: SupabaseClient<Database>,
panelId: string,
params: Partial<Pick<DashboardPanel, 'position' | 'width' | 'config'>>
): Promise<DashboardPanel> {
const { data, error } = await supabase
.from('dashboard_panels')
.update(params)
.eq('id', panelId)
.select()
.single();
if (error) {
log.error('updatePanel failed', { error, data: { panelId } });
throw error;
}
return data as unknown as DashboardPanel;
}
export async function removePanel(
supabase: SupabaseClient<Database>,
panelId: string
): Promise<void> {
const { error } = await supabase
.from('dashboard_panels')
.delete()
.eq('id', panelId);
if (error) {
log.error('removePanel failed', { error, data: { panelId } });
throw error;
}
}
// ============================================================
// Checklists
// ============================================================
export interface ChecklistWithItems extends DepartmentChecklist {
items: DepartmentChecklistItem[];
}
export async function fetchChecklists(
supabase: SupabaseClient<Database>,
departmentId: string
): Promise<ChecklistWithItems[]> {
const { data: checklists, error } = await supabase
.from('department_checklists')
.select('*')
.eq('department_id', departmentId)
.order('sort_order');
if (error) {
log.error('fetchChecklists failed', { error, data: { departmentId } });
throw error;
}
if (!checklists || checklists.length === 0) return [];
const checklistIds = checklists.map(c => c.id);
const { data: items, error: itemsError } = await supabase
.from('department_checklist_items')
.select('*')
.in('checklist_id', checklistIds)
.order('sort_order');
if (itemsError) {
log.error('fetchChecklistItems failed', { error: itemsError });
throw itemsError;
}
const itemsByChecklist: Record<string, DepartmentChecklistItem[]> = {};
for (const item of (items ?? [])) {
if (!itemsByChecklist[item.checklist_id]) itemsByChecklist[item.checklist_id] = [];
itemsByChecklist[item.checklist_id].push(item as unknown as DepartmentChecklistItem);
}
return checklists.map(c => ({
...(c as unknown as DepartmentChecklist),
items: itemsByChecklist[c.id] ?? [],
}));
}
export async function createChecklist(
supabase: SupabaseClient<Database>,
departmentId: string,
title: string,
userId?: string
): Promise<DepartmentChecklist> {
const { data, error } = await supabase
.from('department_checklists')
.insert({ department_id: departmentId, title, created_by: userId ?? null })
.select()
.single();
if (error) {
log.error('createChecklist failed', { error, data: { departmentId, title } });
throw error;
}
return data as unknown as DepartmentChecklist;
}
export async function deleteChecklist(
supabase: SupabaseClient<Database>,
checklistId: string
): Promise<void> {
const { error } = await supabase
.from('department_checklists')
.delete()
.eq('id', checklistId);
if (error) {
log.error('deleteChecklist failed', { error, data: { checklistId } });
throw error;
}
}
export async function renameChecklist(
supabase: SupabaseClient<Database>,
checklistId: string,
title: string
): Promise<DepartmentChecklist> {
const { data, error } = await supabase
.from('department_checklists')
.update({ title })
.eq('id', checklistId)
.select()
.single();
if (error) {
log.error('renameChecklist failed', { error, data: { checklistId, title } });
throw error;
}
return data as unknown as DepartmentChecklist;
}
// ============================================================
// Checklist Items
// ============================================================
export async function addChecklistItem(
supabase: SupabaseClient<Database>,
checklistId: string,
content: string,
sortOrder: number = 0
): Promise<DepartmentChecklistItem> {
const { data, error } = await supabase
.from('department_checklist_items')
.insert({ checklist_id: checklistId, content, sort_order: sortOrder })
.select()
.single();
if (error) {
log.error('addChecklistItem failed', { error, data: { checklistId, content } });
throw error;
}
return data as unknown as DepartmentChecklistItem;
}
export async function updateChecklistItem(
supabase: SupabaseClient<Database>,
itemId: string,
params: Partial<Pick<DepartmentChecklistItem, 'content' | 'is_completed' | 'assigned_to' | 'due_date' | 'sort_order'>>
): Promise<DepartmentChecklistItem> {
const { data, error } = await supabase
.from('department_checklist_items')
.update({ ...params, updated_at: new Date().toISOString() })
.eq('id', itemId)
.select()
.single();
if (error) {
log.error('updateChecklistItem failed', { error, data: { itemId } });
throw error;
}
return data as unknown as DepartmentChecklistItem;
}
export async function deleteChecklistItem(
supabase: SupabaseClient<Database>,
itemId: string
): Promise<void> {
const { error } = await supabase
.from('department_checklist_items')
.delete()
.eq('id', itemId);
if (error) {
log.error('deleteChecklistItem failed', { error, data: { itemId } });
throw error;
}
}
export async function toggleChecklistItem(
supabase: SupabaseClient<Database>,
itemId: string,
isCompleted: boolean
): Promise<DepartmentChecklistItem> {
return updateChecklistItem(supabase, itemId, { is_completed: isCompleted });
}
// ============================================================
// Notes
// ============================================================
export async function fetchNotes(
supabase: SupabaseClient<Database>,
departmentId: string
): Promise<DepartmentNote[]> {
const { data, error } = await supabase
.from('department_notes')
.select('*')
.eq('department_id', departmentId)
.order('sort_order');
if (error) {
log.error('fetchNotes failed', { error, data: { departmentId } });
throw error;
}
return (data ?? []) as unknown as DepartmentNote[];
}
export async function createNote(
supabase: SupabaseClient<Database>,
departmentId: string,
title: string,
userId?: string
): Promise<DepartmentNote> {
const { data, error } = await supabase
.from('department_notes')
.insert({ department_id: departmentId, title, created_by: userId ?? null })
.select()
.single();
if (error) {
log.error('createNote failed', { error, data: { departmentId, title } });
throw error;
}
return data as unknown as DepartmentNote;
}
export async function updateNote(
supabase: SupabaseClient<Database>,
noteId: string,
params: Partial<Pick<DepartmentNote, 'title' | 'content' | 'sort_order'>>
): Promise<DepartmentNote> {
const { data, error } = await supabase
.from('department_notes')
.update({ ...params, updated_at: new Date().toISOString() })
.eq('id', noteId)
.select()
.single();
if (error) {
log.error('updateNote failed', { error, data: { noteId } });
throw error;
}
return data as unknown as DepartmentNote;
}
export async function deleteNote(
supabase: SupabaseClient<Database>,
noteId: string
): Promise<void> {
const { error } = await supabase
.from('department_notes')
.delete()
.eq('id', noteId);
if (error) {
log.error('deleteNote failed', { error, data: { noteId } });
throw error;
}
}

176
src/lib/api/schedule.ts Normal file
View File

@@ -0,0 +1,176 @@
import type { SupabaseClient } from '@supabase/supabase-js';
import type { ScheduleStage, ScheduleBlock } from '$lib/supabase/types';
import { createLogger } from '$lib/utils/logger';
const log = createLogger('api.schedule');
// Helper to cast supabase for tables not yet in generated types
function db(supabase: SupabaseClient) {
return supabase as any;
}
// ============================================================
// Stages
// ============================================================
export async function fetchStages(
supabase: SupabaseClient,
departmentId: string
): Promise<ScheduleStage[]> {
const { data, error } = await db(supabase)
.from('schedule_stages')
.select('*')
.eq('department_id', departmentId)
.order('sort_order');
if (error) {
log.error('fetchStages failed', { error, data: { departmentId } });
throw error;
}
return (data ?? []) as ScheduleStage[];
}
export async function createStage(
supabase: SupabaseClient,
departmentId: string,
name: string,
color?: string
): Promise<ScheduleStage> {
const { data, error } = await db(supabase)
.from('schedule_stages')
.insert({ department_id: departmentId, name, color: color ?? '#6366f1' })
.select()
.single();
if (error) {
log.error('createStage failed', { error, data: { departmentId, name } });
throw error;
}
return data as ScheduleStage;
}
export async function updateStage(
supabase: SupabaseClient,
stageId: string,
params: Partial<Pick<ScheduleStage, 'name' | 'color' | 'sort_order'>>
): Promise<ScheduleStage> {
const { data, error } = await db(supabase)
.from('schedule_stages')
.update(params)
.eq('id', stageId)
.select()
.single();
if (error) {
log.error('updateStage failed', { error, data: { stageId } });
throw error;
}
return data as ScheduleStage;
}
export async function deleteStage(
supabase: SupabaseClient,
stageId: string
): Promise<void> {
const { error } = await db(supabase)
.from('schedule_stages')
.delete()
.eq('id', stageId);
if (error) {
log.error('deleteStage failed', { error, data: { stageId } });
throw error;
}
}
// ============================================================
// Blocks
// ============================================================
export async function fetchBlocks(
supabase: SupabaseClient,
departmentId: string
): Promise<ScheduleBlock[]> {
const { data, error } = await db(supabase)
.from('schedule_blocks')
.select('*')
.eq('department_id', departmentId)
.order('start_time');
if (error) {
log.error('fetchBlocks failed', { error, data: { departmentId } });
throw error;
}
return (data ?? []) as ScheduleBlock[];
}
export async function createBlock(
supabase: SupabaseClient,
departmentId: string,
params: {
title: string;
start_time: string;
end_time: string;
stage_id?: string | null;
description?: string;
color?: string;
speaker?: string;
},
userId?: string
): Promise<ScheduleBlock> {
const { data, error } = await db(supabase)
.from('schedule_blocks')
.insert({
department_id: departmentId,
title: params.title,
start_time: params.start_time,
end_time: params.end_time,
stage_id: params.stage_id ?? null,
description: params.description ?? null,
color: params.color ?? '#6366f1',
speaker: params.speaker ?? null,
created_by: userId ?? null,
})
.select()
.single();
if (error) {
log.error('createBlock failed', { error, data: { departmentId, title: params.title } });
throw error;
}
return data as ScheduleBlock;
}
export async function updateBlock(
supabase: SupabaseClient,
blockId: string,
params: Partial<Pick<ScheduleBlock, 'title' | 'description' | 'start_time' | 'end_time' | 'stage_id' | 'color' | 'speaker' | 'sort_order'>>
): Promise<ScheduleBlock> {
const { data, error } = await db(supabase)
.from('schedule_blocks')
.update({ ...params, updated_at: new Date().toISOString() })
.eq('id', blockId)
.select()
.single();
if (error) {
log.error('updateBlock failed', { error, data: { blockId } });
throw error;
}
return data as ScheduleBlock;
}
export async function deleteBlock(
supabase: SupabaseClient,
blockId: string
): Promise<void> {
const { error } = await db(supabase)
.from('schedule_blocks')
.delete()
.eq('id', blockId);
if (error) {
log.error('deleteBlock failed', { error, data: { blockId } });
throw error;
}
}

301
src/lib/api/sponsors.ts Normal file
View File

@@ -0,0 +1,301 @@
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;
}
}
// ============================================================
// 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;
}
}