Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/ProyectoFerretek/FerreMarket/llms.txt

Use this file to discover all available pages before exploring further.

FerreMarket uses Supabase (PostgreSQL) as its backend database. All data operations — reads, inserts, updates, and deletes — flow through the Supabase JavaScript client initialised in src/lib/supabase/Supabase.tsx. The schema described on this page is inferred from the TypeScript interfaces in src/types/index.ts and the query patterns in src/data/mockData.ts; refer to your live Supabase project for the authoritative column definitions and constraints.

Supabase Client Initialisation

The client is created once and exported as a default singleton from src/lib/supabase/Supabase.tsx. It reads the project URL and anonymous key from Vite environment variables so that no credentials are compiled into source code.
import { createClient } from '@supabase/supabase-js'

const supabaseUrl = import.meta.env.VITE_SUPABASE_URL;
const supabaseKey = import.meta.env.VITE_SUPABASE_ANON_KEY;
const supabase = createClient(supabaseUrl, supabaseKey);

export default supabase;
Both variables must be set in your .env file (see .env.example):
VITE_SUPABASE_URL="https://<project-ref>.supabase.co"
VITE_SUPABASE_ANON_KEY="<your-anon-key>"
Never commit real keys to version control. The anon key is safe to expose to the browser only because Row Level Security (RLS) policies restrict what it can access. The service role key must stay server-side (Edge Functions only).

Tables

The following tables are queried directly by the application. Column names are inferred from the select, insert, and update call arguments in src/data/mockData.ts.

productos

Stores the hardware product catalog.
ColumnType (inferred)Description
idinteger / bigintPrimary key — used with Number(id) in queries
skutextStock-keeping unit code; also used as the Cloudflare R2 object key
nombretextProduct display name
descripciontextFull product description
precionumericUnit price (CLP)
categoriatextCategory identifier (foreign key to categorias.id)
stockintegerUnits currently in inventory
imagentextCDN URL to the product image
destacadobooleanFeatured flag

clientes

Stores both individual and business customer records. A single tipo column ('individual' or 'empresarial') discriminates the subtype.
ColumnType (inferred)Description
idinteger / bigintPrimary key
tipotextCustomer subtype: 'individual' or 'empresarial'
nombretextFirst name (individual) or commercial name (empresa)
apellidostextLast name(s) — individual only
emailtextContact email; used as a lookup key in obtenerClienteIdByMail
telefonotextContact phone number
direcciontextPostal address
runtextChilean national ID — individual only
ruttextChilean tax ID — empresarial only
girotextBusiness activity description — empresarial only
razonsocialtextLegal registered name — empresarial only (note: stored lowercase)
estadotext'activo' or 'inactivo'
notastextInternal free-text notes
ultimacompratextISO 8601 timestamp of last purchase (updated on every new Venta)
fechaCreaciontextISO 8601 creation timestamp
ultimaModificaciontextISO 8601 last-modified timestamp

ventas

Stores sales transaction headers. Line items are stored separately in ventas_productos.
ColumnType (inferred)Description
idinteger / bigintPrimary key; returned via .select() after insert
fechatextISO 8601 timestamp of the sale (America/Santiago timezone)
clienteintegerForeign key → clientes.id
totalnumericGrand total of the transaction (CLP)
metodo_pagotextPayment method (mapped to metodoPago in the TypeScript layer)
estadotext'completada', 'pendiente', or 'cancelada'

ventas_productos

Join table that stores the line items for each sale. This is queried alongside ventas to reconstitute the full Venta object with its productos array.
ColumnType (inferred)Description
idinteger / bigintPrimary key
venta_idintegerForeign key → ventas.id
producto_idintegerForeign key → productos.id
cantidadintegerQuantity of the product sold
precio_unitarionumericUnit price at the time of sale (snapshot)
ventas_productos is also used to compute “featured products” by ordering on cantidad DESC — the products appearing most often across all sales are surfaced as top sellers.

usuarios

Stores application user profiles. Each row is linked to a Supabase Auth identity via the uid column, which holds the Auth UUID.
ColumnType (inferred)Description
idinteger / bigintInternal primary key
uiduuid / textSupabase Auth UUID (auth.users.id)
nombretextDisplay name
emailtextLogin email address
roltext'admin', 'usuario', or 'cliente'
estadotext'activo' or 'inactivo'
fecha_creaciontextISO 8601 creation timestamp (mapped to fechaCreacion)
ultima_modificaciontextISO 8601 last-modified timestamp (mapped to ultimaModificacion)
ultimo_accesotextISO 8601 last login timestamp (mapped to ultimoAcceso)

promociones

Stores discount promotion definitions and their running analytics. Column names follow a snake_case convention that is remapped to camelCase in the TypeScript layer by obtenerPromociones.
ColumnType (inferred)Description
idinteger / bigintPrimary key
codigotextPromo code entered at checkout
nombretextInternal promotion name
tipotextDiscount type (e.g. 'porcentaje', 'monto_fijo')
valornumericDiscount magnitude
valor_maximonumericMaximum discount cap (CLP) for percentage promotions
monto_minimonumericMinimum cart total (CLP) to activate the promo
fecha_iniciotextISO 8601 start date/time
fecha_fintextISO 8601 expiry date/time
limite_total_usosintegerGlobal redemption cap
limite_usos_por_clienteintegerPer-customer redemption cap
usos_actualesintegerRunning redemption count
estadotextPromotion status
aplica_atextApplicability scope: 'todos', 'productos', or 'categorias'
productos_incluidostext[]Array of product IDs the promo applies to
productos_excluidostext[]Array of product IDs excluded from the promo
categorias_incluidastext[]Array of category IDs the promo applies to
categorias_excluidastext[]Array of category IDs excluded from the promo
tipo_clientetextCustomer type restriction
combinablebooleanWhether the promo stacks with others
descripciontextMarketing description
fecha_creaciontextISO 8601 creation timestamp
creado_portextName/ID of the creating user
ingreso_generadonumericTotal revenue attributed to this promo (CLP)
valor_promedio_compranumericAverage order value of redemption transactions (CLP)
tasa_conversionnumericRedemption conversion rate
horarios_usotext[]Time-slot usage log
productos_vendidostext[]Product IDs sold under this promotion

Common Query Patterns

The following examples are extracted directly from src/data/mockData.ts and illustrate the Supabase query patterns used across the application.

Fetch all products

const { data: productos } = await supabase
  .from("productos")
  .select("*");

Calculate total inventory value

Selects only the two columns needed for the computation to minimise payload size.
const { data: productos, error } = await supabase
  .from("productos")
  .select("precio, stock");

// Then sum client-side:
for (const producto of productos) {
  totalValor += producto.precio * (producto.stock || 0);
}

Count products in a category

const { data: productos, error } = await supabase
  .from("productos")
  .select("id")
  .eq("categoria", categoriaId);

const cantidadProductos = productos ? productos.length : 0;

Fetch top-selling products (by quantity across all sales)

const { data: productos } = await supabase
  .from("ventas_productos")
  .select("producto_id, cantidad")
  .order("cantidad", { ascending: false })
  .limit(limit);

Fetch recent sales (ordered, paginated)

const { data: ventas, error } = await supabase
  .from("ventas")
  .select("*")
  .order("fecha", { ascending: false })
  .limit(cantidad);

Get a customer’s total spend

const { data: ventas, error } = await supabase
  .from("ventas")
  .select("total")
  .eq("cliente", Number(clienteId));

const totalCompras = ventas.reduce((acc, venta) => acc + (venta.total || 0), 0);

Look up a customer ID by email

const { data, error } = await supabase
  .from("clientes")
  .select("id")
  .eq("email", email)
  .single();

Get the internal user ID from a Supabase Auth UUID

// Pattern inferred from agregarUsuario — uid is stored alongside the internal id
const { data: usuarios } = await supabase
  .from("usuarios")
  .select("*");

// The uid column holds the Supabase Auth UUID
// e.g. usuario.uid === session.user.id

Row Level Security

Supabase Row Level Security (RLS) should be enabled on every table. The anon key bundled with the frontend client has read-only (or no) access to sensitive tables by default; write operations are gated behind authenticated sessions and RLS policies. Recommended policy approach:
TableAnon readAuth readAuth writeNotes
productos✅ publicadmin onlyProduct catalog can be public
clientes✅ own recordadmin / selfPII — restrict tightly
ventas✅ own recordadmin / POS roleFinancial data
ventas_productos✅ own recordadmin / POS roleLinked to ventas
usuarios✅ own recordadmin onlyUse service role for creation
promociones✅ active onlyadmin onlyActive promos needed at checkout
Admin operations that require the Supabase service role key (bypassing RLS) must never run in the browser. Route them exclusively through Edge Functions as FerreMarket does for user management.

Edge Functions

Three Supabase Edge Functions are invoked from src/context/AuthContext.tsx for user-lifecycle operations that require elevated privileges. These functions run server-side with the service role key and are never exposed to the client directly.

create-user

Creates a new Supabase Auth user (auth.users) and returns the resulting user object. Called during user onboarding from the admin panel.
const { data, error } = await supabase.functions.invoke("create-user", {
  body: {
    email: email.toLowerCase(),
    password: password,
  },
});

delete-user

Deletes a Supabase Auth user by their UUID. Called when an admin removes a user from the system.
const { error } = await supabase.functions.invoke("delete-user", {
  method: "DELETE",
  body: {
    userId: uid,
  },
});

send-password-recovery-email

Triggers a password recovery email for the specified address. Used on the forgot-password flow.
const { error } = await supabase.functions.invoke("send-password-recovery-email", {
  body: {
    email: email.toLowerCase(),
  },
});
Deploy Edge Functions with supabase functions deploy <function-name>. Each function should validate the caller’s JWT and confirm the requesting user has the admin role before performing destructive operations.

This schema is inferred from the application source code in src/data/mockData.ts and src/types/index.ts. Always consult your live Supabase project’s Table Editor and Database → Schema view for the authoritative column types, constraints, indexes, and RLS policies.

Build docs developers (and LLMs) love