import { createClient, SupabaseClient } from '@supabase/supabase-js'; import { VendorDailyRow } from '../types'; export type { VendorDailyRow }; const supabaseUrl = process.env.SUPABASE_URL || ''; const supabaseAnonKey = process.env.SUPABASE_ANON_KEY || ''; let _client: SupabaseClient | null = null; const getClient = (): SupabaseClient => { if (!_client) { if (!supabaseUrl || !supabaseAnonKey) { throw new Error('Supabase credentials not configured. Add SUPABASE_URL and SUPABASE_ANON_KEY to environment variables.'); } // Disable auth persistence to avoid LockManager timeout issues _client = createClient(supabaseUrl, supabaseAnonKey, { auth: { autoRefreshToken: false, persistSession: false, detectSessionInUrl: false, storage: false as any } }); } return _client; }; export interface VendorFilters { markets?: string[]; tags?: string[]; asins?: string[]; dateFrom?: string; dateTo?: string; years?: number[]; } export const fetchVendorData = async (filters: VendorFilters): Promise => { const allRows: VendorDailyRow[] = []; let offset = 0; const pageSize = 1000; let hasMore = true; while (hasMore) { let query = getClient() .from('vendor_daily_data') .select('*') .order('date', { ascending: true }) .range(offset, offset + pageSize - 1); if (filters.markets?.length) { query = query.in('market', filters.markets); } if (filters.tags?.length) { // Use ilike for partial match on tags query = query.or(filters.tags.map(t => `tags.ilike.%${t}%`).join(',')); } if (filters.asins?.length) { query = query.in('asin', filters.asins); } if (filters.dateFrom) { query = query.gte('date', filters.dateFrom); } if (filters.dateTo) { query = query.lte('date', filters.dateTo); } if (filters.years?.length) { // Filter by year using date range const yearFilters = filters.years.map(y => ({ from: `${y}-01-01`, to: `${y}-12-31` })); const dateConditions = yearFilters.flatMap(f => [ `date.gte.${f.from}`, `date.lte.${f.to}` ]); // Build OR condition for multiple years query = query.or( filters.years.map(y => `and(date.gte.${y}-01-01,date.lte.${y}-12-31)`).join(',') ); } const { data, error } = await query; if (error) throw error; if (data) { allRows.push(...data); hasMore = data.length === pageSize; offset += pageSize; } else { hasMore = false; } } return allRows; }; export const fetchVendorFilterOptions = async (): Promise<{ markets: string[]; tags: string[]; dateRange: { min: string; max: string } | null; }> => { const client = getClient(); const { data: marketData } = await client .from('vendor_daily_data') .select('market') .limit(100000); const { data: tagData } = await client .from('vendor_daily_data') .select('tags') .limit(100000); const { data: dateMin } = await client .from('vendor_daily_data') .select('date') .order('date', { ascending: true }) .limit(1); const { data: dateMax } = await client .from('vendor_daily_data') .select('date') .order('date', { ascending: false }) .limit(1); const markets = [...new Set((marketData || []).map((r: any) => r.market as string))].sort(); const tags = [...new Set((tagData || []).map((r: any) => r.tags as string).filter(Boolean))].sort(); return { markets, tags, dateRange: dateMin?.[0] && dateMax?.[0] ? { min: dateMin[0].date, max: dateMax[0].date } : null, }; };