Skip to main content
VSM Store uses Supabase (managed PostgreSQL) as its only data store. All tables have Row Level Security (RLS) enabled. The schema is managed through SQL migration files in supabase/migrations/.

Core Tables

products

The main product catalog. Supports two catalog sections, expiring badge flags, and variant-ready structure.
ColumnTypeDescription
iduuidPrimary key
nametextProduct display name
slugtextURL-safe identifier (unique per section)
descriptiontext | nullLong-form HTML/markdown description
short_descriptiontext | nullOne-line summary
pricenumericCurrent price in MXN
compare_at_pricenumeric | nullStrike-through original price
stockintegerAvailable units
skutext | nullStock-keeping unit
section'vape' | '420'Catalog section
category_iduuidFK → categories.id
tagstext[]Searchable tag array
imagestext[]Array of image URLs
cover_imagetext | nullPrimary display image
statusProductStatusSee enum below
is_featuredbooleanShown in featured section
is_featured_untiltimestamptz | nullAuto-expiry for featured badge
is_newbooleanShown in new arrivals
is_new_untiltimestamptz | nullAuto-expiry for new badge
is_bestsellerbooleanShown in bestsellers
is_bestseller_untiltimestamptz | nullAuto-expiry for bestseller badge
is_activebooleanWhether product is visible in storefront
created_attimestamptzRow creation timestamp
updated_attimestamptzLast modification timestamp
interface Product {
    id: string; name: string; slug: string;
    description: string | null; short_description: string | null;
    price: number; compare_at_price: number | null;
    stock: number; sku: string | null;
    section: 'vape' | '420'; category_id: string;
    tags: string[]; status: 'active' | 'legacy' | 'discontinued' | 'coming_soon';
    images: string[]; cover_image: string | null;
    is_featured: boolean; is_featured_until: string | null;
    is_new: boolean; is_new_until: string | null;
    is_bestseller: boolean; is_bestseller_until: string | null;
    is_active: boolean;
    created_at: string; updated_at: string;
}

categories

Hierarchical categories. Parent–child relationships use self-referential parent_id.
ColumnTypeDescription
iduuidPrimary key
nametextDisplay name
slugtextURL slug
section'vape' | '420'Which catalog section this belongs to
parent_iduuid | nullFK → categories.id (null = top-level)
order_indexintegerManual sort order
is_activebooleanWhether visible in storefront
interface Category {
    id: string; name: string; slug: string;
    section: Section; parent_id: string | null;
    order_index: number; is_active: boolean;
}

customer_profiles

Extended user data linked to auth.users. Created on signup via createCustomerProfile().
ColumnTypeDescription
iduuidPK = auth.uid()
full_nametextDisplay name
phonetext | nullPhone number
whatsapptext | nullWhatsApp number
birthdatedate | nullDate of birth
tiertext'bronze' | 'silver' | 'gold' | 'platinum'
total_ordersintegerLifetime order count
total_spentnumericLifetime spend in MXN (used for tier calculation)
pointsintegerCached V-Coins balance
account_statustext'active' | 'suspended' | 'banned'
referral_codetext | nullUnique referral code
ai_preferencesjsonb | nullAI personalization preferences
interface CustomerProfile {
    id: string;
    email: string;
    full_name: string | null;
    phone: string | null;
    whatsapp: string | null;
    birthdate: string | null;
    tier: 'bronze' | 'silver' | 'gold' | 'platinum';  // not "customer_tier"
    total_orders: number;
    total_spent: number;
    points: number;
    account_status: 'active' | 'suspended' | 'banned';
}

orders

Order records created at checkout. items is a JSONB column storing the full CartItem[] snapshot.
ColumnTypeDescription
iduuidPrimary key
order_numbertextAuto-generated human-readable number
customer_iduuidFK → auth.users.id
itemsjsonbSnapshot of CartItem[] at order time
subtotalnumericPre-discount total
shipping_costnumericShipping fee in MXN
discountnumericApplied discount amount
totalnumericFinal amount charged
statusOrderStatusSee enum below
payment_methodtext'whatsapp' | 'mercadopago' | 'cash' | 'transfer'
payment_statustext'pending' | 'paid' | 'failed' | 'refunded'
mp_preference_idtext | nullMercado Pago preference ID
whatsapp_sentbooleanWhether WhatsApp order message was sent

addresses

Customer shipping and billing addresses.
ColumnTypeDescription
iduuidPrimary key
customer_iduuidFK → auth.users.id
typetext'shipping' | 'billing'
streettextStreet address
citytextCity
statetextState/province
zip_codetextPostal code
is_defaultbooleanWhether this is the default address

coupons

Discount codes applicable at checkout.
ColumnTypeDescription
iduuidPrimary key
codetextThe coupon code customers enter
discount_typetext'fixed' | 'percent'
discount_valuenumericAmount or percentage to deduct
min_purchasenumericMinimum order total to apply
max_usesintegerUsage limit (null = unlimited)

loyalty_points

Appendable ledger of loyalty point transactions. Balance is derived by summing rows.
ColumnTypeDescription
iduuidPrimary key
customer_iduuidFK → auth.users.id
pointsintegerPoints earned or burned (positive = earned)
transaction_typetext'earned' | 'burned'
order_iduuid | nullFK → orders.id (if earned from order)
descriptiontextHuman-readable reason
Loyalty points accrue at 10 points per $100 MXN spent. Balance is queried via a Supabase RPC function.

admin_users

Simple role table. Presence of a row grants admin access via AdminGuard.
ColumnTypeDescription
iduuidPK = auth.uid()
roletext'admin' (extensible for future roles)

store_settings

Key-value store for dynamic store configuration managed from the admin panel.
ColumnTypeDescription
keytextSetting identifier (primary key)
valuejsonbSetting value (any JSON type)
updated_attimestamptzLast modification timestamp

Type Definitions

Section

// src/types/constants.ts
type Section = 'vape' | '420';
  • vape — Mods, Atomizadores, Líquidos, Coils, Accesorios Vape
  • 420 — Vaporizers, Fumables, Comestibles, Concentrados, Tópicos, Accesorios 420

ProductStatus

// src/types/constants.ts
type ProductStatus = 'active' | 'legacy' | 'discontinued' | 'coming_soon';
StatusBehavior
activeVisible and purchasable
legacyVisible but no longer sold
discontinuedCannot be added to cart (addItem guards against this)
coming_soonVisible but not yet purchasable

OrderStatus

type OrderStatus = 'pending' | 'confirmed' | 'processing' | 'shipped' | 'delivered' | 'cancelled';

RLS Security Model

All tables have Row Level Security (RLS) enabled. The admin_users table acts as the permission gatekeeper.
ScopePolicy
CustomersCan only read/write their own rows in customer_profiles, orders, addresses, loyalty_points
PublicCan read products (active only) and categories (active only)
AdminsHave full access to all tables via policies that check EXISTS (SELECT 1 FROM admin_users WHERE id = auth.uid())
AnonCan read public product/category data; cannot write to any table
The anonymous key used client-side can only perform what RLS policies allow — there is no way to bypass them from the browser.

Migration History

All 52 migration files live in supabase/migrations/. The table below covers the foundational migrations:
FilePurpose
001_initial_schema.sqlCore schema: products, categories, orders, addresses, coupons (27 KB)
002_users_system.sqlcustomer_profiles table and auth triggers
003_admin_users.sqladmin_users table + RLS policies
20240214_add_payment_fields.sqlMercado Pago preference fields on orders
20260216145500_create_store_settings.sqlstore_settings key-value table
20260216_admin_customer_notes.sqlAdmin notes system for CRM
20260216_bank_account_info.sqlBank transfer info in store settings
20260216_expiring_badges_and_cover.sqlis_*_until expiry columns + cover_image
20260216_god_mode_notifications.sqlAdmin God Mode notification system
20260216_monitoring_system.sqlApp health monitoring infrastructure
20260306_product_variations.sqlProduct variant system
20260302_flash_deals.sqlFlash deals / limited-time promotions
20260304_customer_wishlists.sqlCustomer wishlist persistence
20260308_loyalty_referrals.sqlReferral system for loyalty program
20260310_wheel_system.sqlSpin-the-wheel loyalty game
20260312_neural_search_infra.sqlVector search infrastructure for AI
20260315_cesarin_os.sqlCesarinOS AI admin assistant
20260317_admin_refactor_phase_1.sqlAdmin panel DB refactor
20260317_store_knowledge.sqlStore knowledge base for AI functions
Migrations are append-only. Never modify or delete existing migration files — add new ones instead.

Build docs developers (and LLMs) love