import { createClient, SupabaseClient } from '@supabase/supabase-js'; import type { Database } from './types'; const supabaseUrl = import.meta.env.SUPABASE_URL; const supabaseAnonKey = import.meta.env.SUPABASE_ANON_KEY; if (!supabaseUrl || !supabaseAnonKey) { console.warn('Supabase credentials not configured. Set SUPABASE_URL and SUPABASE_ANON_KEY in .env'); } export const supabase: SupabaseClient = createClient( supabaseUrl || 'http://localhost:54321', supabaseAnonKey || 'placeholder-anon-key', { auth: { persistSession: true, autoRefreshToken: true, }, } ); // Admin client with service role key - bypasses RLS export const supabaseAdmin: SupabaseClient = createClient( supabaseUrl || 'http://localhost:54321', supabaseAnonKey || 'placeholder-anon-key', { auth: { persistSession: false, }, } ); // Database types for Supabase export interface Database { public: { Tables: { users: { Row: { id: string; email: string; name: string | null; role: 'customer' | 'vendor' | 'admin'; avatar_url: string | null; phone: string | null; created_at: string; updated_at: string; }; Insert: { id?: string; email: string; name?: string | null; role?: 'customer' | 'vendor' | 'admin'; avatar_url?: string | null; phone?: string | null; }; Update: { id?: string; email?: string; name?: string | null; role?: 'customer' | 'vendor' | 'admin'; avatar_url?: string | null; phone?: string | null; updated_at?: string; }; }; profiles: { Row: { id: string; user_id: string; first_name: string | null; last_name: string | null; phone: string | null; address: string | null; city: string | null; province: string | null; postal_code: string | null; created_at: string; updated_at: string; }; Insert: { id?: string; user_id: string; first_name?: string | null; last_name?: string | null; phone?: string | null; address?: string | null; city?: string | null; province?: string | null; postal_code?: string | null; }; Update: { first_name?: string | null; last_name?: string | null; phone?: string | null; address?: string | null; city?: string | null; province?: string | null; postal_code?: string | null; updated_at?: string; }; }; categories: { Row: { id: string; name: string; slug: string; description: string | null; image_url: string | null; parent_id: string | null; sort_order: number; is_active: boolean; created_at: string; }; Insert: { id?: string; name: string; slug: string; description?: string | null; image_url?: string | null; parent_id?: string | null; sort_order?: number; is_active?: boolean; }; Update: { name?: string; slug?: string; description?: string | null; image_url?: string | null; parent_id?: string | null; sort_order?: number; is_active?: boolean; }; }; products: { Row: { id: string; vendor_id: string | null; category_id: string | null; name: string; slug: string; description: string | null; price: number; compare_at_price: number | null; sku: string | null; barcode: string | null; inventory: number; images: string[]; status: 'draft' | 'active' | 'archived'; created_at: string; updated_at: string; }; Insert: { id?: string; vendor_id?: string | null; category_id?: string | null; name: string; slug: string; description?: string | null; price: number; compare_at_price?: number | null; sku?: string | null; barcode?: string | null; inventory?: number; images?: string[]; status?: 'draft' | 'active' | 'archived'; }; Update: { vendor_id?: string | null; category_id?: string | null; name?: string; slug?: string; description?: string | null; price?: number; compare_at_price?: number | null; sku?: string | null; barcode?: string | null; inventory?: number; images?: string[]; status?: 'draft' | 'active' | 'archived'; updated_at?: string; }; }; orders: { Row: { id: string; order_number: string; user_id: string | null; guest_email: string | null; status: 'pending' | 'confirmed' | 'processing' | 'shipped' | 'delivered' | 'cancelled'; payment_status: 'pending' | 'paid' | 'failed' | 'refunded'; subtotal: number; tax: number; shipping: number; discount: number; total: number; shipping_name: string | null; shipping_phone: string | null; shipping_address: string | null; shipping_city: string | null; shipping_province: string | null; shipping_postal_code: string | null; notes: string | null; created_at: string; updated_at: string; }; Insert: { id?: string; order_number: string; user_id?: string | null; guest_email?: string | null; status?: 'pending' | 'confirmed' | 'processing' | 'shipped' | 'delivered' | 'cancelled'; payment_status?: 'pending' | 'paid' | 'failed' | 'refunded'; subtotal: number; tax: number; shipping: number; discount?: number; total: number; shipping_name?: string | null; shipping_phone?: string | null; shipping_address?: string | null; shipping_city?: string | null; shipping_province?: string | null; shipping_postal_code?: string | null; notes?: string | null; }; Update: { status?: 'pending' | 'confirmed' | 'processing' | 'shipped' | 'delivered' | 'cancelled'; payment_status?: 'pending' | 'paid' | 'failed' | 'refunded'; subtotal?: number; tax?: number; shipping?: number; discount?: number; total?: number; shipping_name?: string | null; shipping_phone?: string | null; shipping_address?: string | null; shipping_city?: string | null; shipping_province?: string | null; shipping_postal_code?: string | null; notes?: string | null; updated_at?: string; }; }; order_items: { Row: { id: string; order_id: string; product_id: string; product_name: string; product_image: string | null; price: number; quantity: number; total: number; created_at: string; }; Insert: { id?: string; order_id: string; product_id: string; product_name: string; product_image?: string | null; price: number; quantity: number; total: number; }; Update: { product_name?: string; product_image?: string | null; price?: number; quantity?: number; total?: number; }; }; cart_items: { Row: { id: string; user_id: string; product_id: string; quantity: number; created_at: string; updated_at: string; }; Insert: { id?: string; user_id: string; product_id: string; quantity: number; }; Update: { quantity?: number; updated_at?: string; }; }; payments: { Row: { id: string; order_id: string; method: 'promptpay' | 'credit_card' | 'bank_transfer' | 'cash_on_delivery'; amount: number; status: 'pending' | 'processing' | 'completed' | 'failed' | 'refunded'; reference: string | null; metadata: Record | null; created_at: string; updated_at: string; }; Insert: { id?: string; order_id: string; method: 'promptpay' | 'credit_card' | 'bank_transfer' | 'cash_on_delivery'; amount: number; status?: 'pending' | 'processing' | 'completed' | 'failed' | 'refunded'; reference?: string | null; metadata?: Record | null; }; Update: { status?: 'pending' | 'processing' | 'completed' | 'failed' | 'refunded'; reference?: string | null; metadata?: Record | null; updated_at?: string; }; }; }; Views: { [_ in never]: never; }; Functions: { [_ in never]: never; }; Enums: { [_ in never]: never; }; }; } // Helper functions for common queries export async function getProductBySlug(slug: string) { const { data, error } = await supabase .from('products') .select('*, categories(*)') .eq('slug', slug) .eq('status', 'active') .single(); if (error) throw error; return data; } export async function getProductsByCategory(categorySlug: string, limit = 20, offset = 0) { const { data, error } = await supabase .from('products') .select('*, categories(*)') .eq('categories.slug', categorySlug) .eq('status', 'active') .range(offset, offset + limit - 1); if (error) throw error; return data; } export async function getFeaturedProducts(limit = 10) { const { data, error } = await supabase .from('products') .select('*, categories(*)') .eq('status', 'active') .order('created_at', { ascending: false }) .limit(limit); if (error) throw error; return data; } export async function getUserOrders(userId: string) { const { data, error } = await supabase .from('orders') .select('*, order_items(*, products(*))') .eq('user_id', userId) .order('created_at', { ascending: false }); if (error) throw error; return data; } export async function getOrderByNumber(orderNumber: string) { const { data, error } = await supabase .from('orders') .select('*, order_items(*, products(*))') .eq('order_number', orderNumber) .single(); if (error) throw error; return data; } export async function updateProductInventory(productId: string, quantity: number) { const { data, error } = await supabase.rpc('decrement_inventory', { product_id: productId, quantity: quantity, }); if (error) throw error; return data; }