Skip to main content
PROVESA Web uses multiple content tables to manage dynamic homepage sections, pages, and user submissions. This reference documents all content-related schemas.

Hero Slides

Manages the homepage hero/banner carousel slides.

Table: hero_slides

id
serial
required
Primary key. Auto-incrementing integer.
imageUrl
text
required
URL to the slide background image.Example: "https://res.cloudinary.com/.../hero-1.jpg"
title
text
default:""
required
Main slide title text.
highlight
text
default:""
Highlighted portion of the title (displayed with special styling).
description
text
default:""
Slide description/subtitle text.
badge
text
default:""
Small badge text displayed above the title.
align
text
default:"left"
required
Text alignment for the slide content.Values: "left" | "center" | "right"
highlightColor
text
default:"text-accent-yellow"
CSS class for highlight text color.Example: "text-accent-yellow", "text-primary"
sortOrder
integer
default:0
required
Display order (lower numbers appear first).
createdAt
timestamp
When the slide was created.Default: defaultNow()

Example Usage

import { heroSlides } from './schemas/hero_slides.schema';
import { db } from './db';
import { asc } from 'drizzle-orm';

// Get all slides ordered by sortOrder
const slides = await db
  .select()
  .from(heroSlides)
  .orderBy(asc(heroSlides.sortOrder));

// Create a new slide
await db.insert(heroSlides).values({
  imageUrl: 'https://res.cloudinary.com/.../banner.jpg',
  title: 'Distribuidor Mayorista',
  highlight: 'de Confianza',
  description: 'Los mejores precios para su negocio',
  badge: 'Desde 2006',
  align: 'left',
  highlightColor: 'text-accent-yellow',
  sortOrder: 1
});

Products

Homepage product sections with images, features, and categories.

Table: products

id
serial
required
Primary key.
name
text
default:""
required
Product name/title.
description
text
default:""
Product description text.
images
jsonb
default:"[]"
Array of image URLs for the product carousel.Type: string[]Example:
[
  "https://res.cloudinary.com/.../product-1.jpg",
  "https://res.cloudinary.com/.../product-2.jpg"
]
features
jsonb
default:"[]"
Array of product features with icons.Type: Array<{ title: string, desc: string, icon: string }>Example:
[
  {
    "title": "Calidad Garantizada",
    "desc": "Productos certificados",
    "icon": "mdi:certificate"
  },
  {
    "title": "Entrega Rápida",
    "desc": "24-48 horas",
    "icon": "mdi:truck-fast"
  }
]
categories
jsonb
default:"[]"
Array of numbered categories.Type: Array<{ num: string, name: string, desc: string }>Example:
[
  {
    "num": "01",
    "name": "Abarrotes",
    "desc": "Productos básicos de consumo diario"
  },
  {
    "num": "02",
    "name": "Bebidas",
    "desc": "Refrescos, jugos y más"
  }
]
align
text
default:"left"
required
Content alignment.Values: "left" | "right"
accentColor
text
default:"primary"
required
Accent color theme.Values: "primary" | "accent"
displayType
text
default:"features"
required
Which content to display.Values: "features" | "categories"
sortOrder
integer
default:0
required
Display order.
createdAt
timestamp
Created timestamp.

Example Usage

import { products } from './schemas/products.schema';
import { db } from './db';

await db.insert(products).values({
  name: 'Abarrotes Premium',
  description: 'La mejor selección de productos básicos',
  images: [
    'https://res.cloudinary.com/.../abarrotes-1.jpg',
    'https://res.cloudinary.com/.../abarrotes-2.jpg'
  ],
  features: [
    { title: 'Variedad', desc: '500+ productos', icon: 'mdi:basket' },
    { title: 'Frescura', desc: 'Productos frescos', icon: 'mdi:leaf' }
  ],
  align: 'left',
  accentColor: 'primary',
  displayType: 'features',
  sortOrder: 1
});

Manage footer information and branch locations. Singleton table - should only have one row.
id
serial
required
Primary key.
description
text
default:""
Company description text.
address
text
default:""
Main office address.
phone
text
default:""
Primary phone number.
mobile
text
default:""
Mobile/cell phone number.
email
text
default:""
Contact email address.
facebookUrl
text
default:""
Facebook page URL.
instagramUrl
text
default:""
Instagram profile URL.
tiktokUrl
text
default:""
TikTok profile URL.
whatsappUrl
text
default:""
WhatsApp link URL.
updatedAt
timestamp
Last updated timestamp.
Branch locations displayed in footer.
id
serial
required
Primary key.
name
text
required
Branch name.
address
text
default:""
Branch address.
sortOrder
integer
default:0
required
Display order.
createdAt
timestamp
Created timestamp.

Example Usage

import { footerInfo, footerBranches } from './schemas/footer.schema';
import { db } from './db';

// Get footer info (singleton)
const [footer] = await db.select().from(footerInfo).limit(1);

// Get all branches
const branches = await db
  .select()
  .from(footerBranches)
  .orderBy(asc(footerBranches.sortOrder));

Editable legal content pages (Privacy Policy, Terms, etc.).
id
serial
required
Primary key.
slug
text
required
URL slug identifier.Constraints: .unique()Examples: "privacidad", "terminos"
title
text
default:""
required
Page title.
content
text
default:""
required
Page content (supports HTML or Markdown).
updatedAt
timestamp
Last updated timestamp.

Example Usage

import { legalPages } from './schemas/legal_pages.schema';
import { db } from './db';
import { eq } from 'drizzle-orm';

// Get privacy policy
const [privacy] = await db
  .select()
  .from(legalPages)
  .where(eq(legalPages.slug, 'privacidad'));

// Create/update terms page
await db
  .insert(legalPages)
  .values({
    slug: 'terminos',
    title: 'Términos y Condiciones',
    content: '<h1>Términos...</h1>'
  })
  .onConflictDoUpdate({
    target: legalPages.slug,
    set: { content: '<h1>Términos...</h1>' }
  });

Nosotros (About) Schemas

Two tables manage the “About Us” section.

Table: nosotros_config

Singleton table for the homepage “Nosotros” teaser section.
id
serial
required
Primary key.
badge
text
default:"Nuestra Esencia"
required
Badge text above title.
title
text
default:"Más que distribuidores, somos"
required
Section title.
titleHighlight
text
default:"socios estratégicos"
required
Highlighted portion of title.
description
text
required
Section description.
ctaText
text
default:"Conocer Más"
required
Call-to-action button text.
CTA button link.
colors
jsonb
Color configuration object.Type:
{
  bg: string;
  accent: string;
  textMain: string;
  textMuted: string;
  iconColor: string;
  statBg: string;
}
Default:
{
  "bg": "#455dd9",
  "accent": "#ffd100",
  "textMain": "#ffffff",
  "textMuted": "#bfdbfe",
  "iconColor": "#ffd100",
  "statBg": "rgba(255,255,255,0.08)"
}
stats
jsonb
Statistics array.Type: Array<{ number: string, label: string }>Default:
[
  { "number": "18+", "label": "Años" },
  { "number": "100%", "label": "Seriedad" },
  { "number": "500+", "label": "Clientes" }
]
updatedAt
timestamp
Last updated.

Table: nosotros_page

Singleton table for the full /nosotros page content.
id
serial
required
Primary key.
heroBadge
text
default:"Desde 2006"
required
Hero section badge.
heroTitle
text
default:"Aliados de su Crecimiento"
required
Hero section title.
heroDescription
text
required
Hero section description.
historyTitle
text
default:"Nuestra Historia"
required
History section title.
historyParagraphs
jsonb
Array of history paragraphs (HTML supported).Type: string[]Example:
[
  "<strong>PROVESA SCC</strong> nació en...",
  "Fundada oficialmente...",
  "Hoy, con más de 18 años..."
]
historyImageUrl
text
required
History section image URL.
missionTitle
text
default:"Nuestra Misión"
required
Mission section title.
missionText
text
required
Mission statement text.
visionTitle
text
default:"Nuestra Visión"
required
Vision section title.
visionText
text
required
Vision statement text.
Gallery images for mission/vision section.Type: Array<{ url: string, alt: string }>Example:
[
  { "url": "https://...", "alt": "Logística" },
  { "url": "https://...", "alt": "Almacén" }
]
updatedAt
timestamp
Last updated.

Sugerencias (Suggestions)

User feedback and suggestion submissions.

Table: sugerencias

id
uuid
required
Primary key (auto-generated UUID).
tipo
text
required
Suggestion type/category.Examples: "Sugerencia de Servicio", "Nuevo Producto Requerido", "Reclamo", "Felicitación"
nombre
text
Submitter’s name (optional).
mensaje
text
required
Suggestion message/content.
leido
boolean
default:false
required
Whether the suggestion has been read by an admin.
createdAt
timestamp
required
Submission timestamp.

Table: sugerencias_config

Singleton table for suggestion type options.
id
serial
required
Primary key.
opciones
jsonb
required
Array of suggestion type options.Type: string[]Default:
[
  "Sugerencia de Servicio",
  "Nuevo Producto Requerido",
  "Reclamo",
  "Felicitación"
]

Example Usage

import { sugerencias } from './schemas/sugerencias.schema';
import { db } from './db';

// Create a suggestion
await db.insert(sugerencias).values({
  tipo: 'Sugerencia de Servicio',
  nombre: 'Juan Pérez',
  mensaje: 'Me gustaría que ofrecieran...'
});

// Get unread suggestions
const unread = await db
  .select()
  .from(sugerencias)
  .where(eq(sugerencias.leido, false));

Postulaciones (Job Applications)

Job application submissions.

Table: postulaciones

id
uuid
required
Primary key (auto-generated UUID).
nombre
text
required
Applicant’s full name.
telefono
text
required
Phone number.
email
text
required
Email address.
sucursal
text
required
Branch location they’re applying to.
cvUrl
text
URL to uploaded CV/resume (Cloudinary).
mensaje
text
Optional message/cover letter.
createdAt
timestamp
required
Submission timestamp.

Table: empleo_sucursales

Available branches for job applications.
id
serial
required
Primary key.
nombre
text
required
Branch name.
activa
boolean
default:true
required
Whether the branch is actively hiring.
createdAt
timestamp
required
Created timestamp.

Example Usage

import { postulaciones, empleoSucursales } from './schemas';
import { db } from './db';

// Get active branches
const branches = await db
  .select()
  .from(empleoSucursales)
  .where(eq(empleoSucursales.activa, true));

// Submit application
await db.insert(postulaciones).values({
  nombre: 'María González',
  telefono: '+593987654321',
  email: 'maria@example.com',
  sucursal: 'La Concordia',
  cvUrl: 'https://res.cloudinary.com/.../cv.pdf',
  mensaje: 'Me interesa trabajar con ustedes...'
});

Concursos (Contests)

Manages contests/sweepstakes and winners.

Table: concursos

id
serial
required
Primary key.
title
text
default:""
required
Contest title.
titleHighlight
text
default:""
Highlighted portion of title.
description
text
default:""
Contest description.
imageUrl
text
default:""
Contest banner/image (Cloudinary).
badgeText
text
default:"Sorteo Activo"
Badge text (e.g., “Sorteo Activo”).
closeDate
text
default:""
Contest end date (as formatted string).
prizeName
text
default:""
Name of the prize.
ctaText
text
default:"Ver Marcas Auspiciantes"
Call-to-action button text.
disclaimer
text
default:""
Contest rules/disclaimer text.
isActive
boolean
default:false
required
Whether the contest is currently active.
sortOrder
integer
default:0
required
Display order.
createdAt
timestamp
Created timestamp.

Table: concursos_ganadores

Contest winners.
id
serial
required
Primary key.
concursoId
integer
Foreign key to concursos.id.Constraints:
  • References concursos.id
  • onDelete: 'cascade' - Deletes winners when contest is deleted
winnerName
text
default:""
required
Winner’s name.
prize
text
default:""
Prize won.
testimonial
text
default:""
Winner’s testimonial/quote.
imageUrl
text
default:""
Winner’s photo (Cloudinary).
dateLabel
text
default:""
Date label (e.g., “Ganador Mayo 2024”).
sortOrder
integer
default:0
required
Display order.
createdAt
timestamp
Created timestamp.

Example Usage

import { concursos, concursosGanadores } from './schemas/concursos.schema';
import { db } from './db';

// Get active contests
const activeContests = await db
  .select()
  .from(concursos)
  .where(eq(concursos.isActive, true))
  .orderBy(asc(concursos.sortOrder));

// Get winners for a contest
const winners = await db
  .select()
  .from(concursosGanadores)
  .where(eq(concursosGanadores.concursoId, 1))
  .orderBy(asc(concursosGanadores.sortOrder));

Database Migrations

Generate and apply migrations:
npm run db:generate
npm run db:migrate