Files
CrazeAnalytix/services/supabase.ts
christian.vidalandClaude Sonnet 4.6 ce13d17eda fix(vendor): pass bsrData prop to VendorDataView - resolves black screen
VendorDataView was refactored to receive BSR data as a prop but App.tsx
was not updated to pass it, causing the component to crash on undefined
and show a blank screen. Also fixes supabase.ts type errors and removes
dead code.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-02 18:32:38 +01:00

133 lines
3.6 KiB
TypeScript

/// <reference types="node" />
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: {
getItem: (_key: string) => null,
setItem: (_key: string, _value: string) => {},
removeItem: (_key: string) => {},
},
}
});
}
return _client;
};
export interface VendorFilters {
markets?: string[];
tags?: string[];
asins?: string[];
dateFrom?: string;
dateTo?: string;
years?: number[];
}
export const fetchVendorData = async (filters: VendorFilters): Promise<VendorDailyRow[]> => {
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) {
// 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 = Array.from(new Set<string>((marketData || []).map((r: any) => r.market as string))).sort();
const tags = Array.from(new Set<string>((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,
};
};