Initial: pi-skill — 68 skills, 43 extensions, 11 themes for Pi
This commit is contained in:
402
skills/scripts/templates/src/lib/supabase.ts
Normal file
402
skills/scripts/templates/src/lib/supabase.ts
Normal file
@@ -0,0 +1,402 @@
|
||||
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;
|
||||
}
|
||||
Reference in New Issue
Block a user