Files
pi-skill/skills/scripts/templates/src/lib/supabase.ts
2026-05-25 16:41:08 +07:00

403 lines
11 KiB
TypeScript

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<Database> = 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<Database> = 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<string, unknown> | 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<string, unknown> | null;
};
Update: {
status?: 'pending' | 'processing' | 'completed' | 'failed' | 'refunded';
reference?: string | null;
metadata?: Record<string, unknown> | 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;
}