SaaS Background

Plataforma SaaS para Operaciones Logísticas

Control integral de Pre-Alertas, Bodegas, Salidas y Costos Operativos

Next.js 14 React 19 Tailwind CSS Supabase TypeScript Zod

# Introducción: Complejidad Logística

VertS-app es una aplicación SaaS interna diseñada específicamente para orquestar la logística operativa profunda. En el mundo de la gestión de carga y paquetería, mantener el control desde que una prealerta entra al sistema hasta que la mercancía se entrega es un desafío crítico que el software tradicional rara vez resuelve de forma integrada y ágil.

Las operaciones diarias incluyen múltiples actores: clientes o empresas gestionadas, proveedores logísticos con tarifas dinámicas y personal de bodega. El sistema debía consolidar todo esto en un panel de control rápido, altamente reactivo y confiable.

El Reto: Reemplazar hojas de cálculo, correos descentralizados y procesos manuales con una herramienta centralizada capaz de manejar multi-tenant (múltiples empresas), cálculos de tarifas complejos y rastreo estricto de paquetes y finanzas (invoices, costos de proveedor).

El Objetivo: Construir un ERP modular con Next.js 14 utilizando Supabase para habilitar reportes en tiempo real, seguridad avanzada por roles (RLS), y una experiencia de usuario (UX) diseñada para flujos rápidos como la recepción masiva de mercadería.

# Módulos Core: El Ecosistema

La aplicación está estructurada en módulos seguros basados en roles, accesibles desde un dashboard principal. Cada módulo resuelve una fricción operativa específica:

📦
Bodega
Recepciones
Gestión de entrada, pre-alertas y facturas.
🚚
Despacho
Entregas
Rastreo de salida de paquetes consolidados.
⚖️
Finanzas
Tarifas
Calculadoras de pricing y reglas de negocio.
📊
Datos
Reportes
Exportación XLSX, CSV, y PDF. Dashboard KPIs.

# Arquitectura & Stack Tecnológico

En el lado del cliente, la app saca ventaja del App Router de Next.js para un renderizado híbrido rápido. Las interfaces están construidas con Tailwind CSS y componentes de Radix UI (vía Shadcn/UI), asegurando accesibilidad y un diseño moderno e iterativo. Los formularios complejos y las validaciones se manejan limpiamente con React Hook Form acoplado con Zod.

El backend se apoya intensamente en Supabase (PostgreSQL). Todo el almacenamiento base, la autenticación y las reglas de autorización están manejadas a nivel de base de datos a través de Row Level Security (RLS). Esto simplifica el frontend al no tener que preocuparnos de filtrado de datos del cliente: la base de datos ya sabe quién pregunta y qué tiene derecho a ver.

# Seguridad Multi-tenant & RLS

Aplicaciones como esta requieren aislar estrictamente los datos de las diferentes compañías cliente y usuarios bajo un mismo ecosistema. Utilizamos un esquema relacional donde cada entidad se asocia a firmas maestras y cada tabla critica cuenta con restricciones nativas.

Perfiles & Roles

Auth Users -> Tabla Perfiles

Integración sobre la tabla auth nativa para roles a medida.

Seguro

Row Level Security

Policy: auth.uid() = user_id

Todas las consultas PostgreSQL filtran las filas nativamente.

Inquebrantable

Storage Buckets

Políticas sobre Archivos

Imágenes de ID, Invoices y PDFs protegidos en buckets RLS.

Privado

Al aprovechar las herramientas de base de datos nativas (Postgres), la aplicación evita vulnerabilidades comunes en la capa del backend tradicional y permite escalar la arquitectura sin reescribir la lógica de autorización cada vez que un nuevo módulo se agrega.

Vistazo al Núcleo de Server Actions

Un ejemplo de cómo la aplicación se integra fluidamente con las funciones de Next.js Server Components. Observa la verificación segura de autenticación y la inserción de datos inferida por perfiles, de manera de proteger el aislamiento de los 'tenants' (empresas).

src/app/actions/reception.ts
"text-slate-500">// --- Next.js Server Action - Supabase RLS ---
"text-purple-400">import { createServerClient } "text-purple-400">from '@supabase/ssr'
"text-purple-400">import { cookies } "text-purple-400">from 'next/headers'

"text-purple-400">export "text-purple-400">async "text-purple-400">function createReception(data: ReceptionData) {
  "text-purple-400">const cookieStore = cookies()
  "text-purple-400">const supabase = createServerClient(
    process.env.NEXT_PUBLIC_SUPABASE_URL!,
    process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
    {
      cookies: {
        get(name: string) {
          "text-purple-400">return cookieStore.get(name)?.value
        },
      },
    }
  )

  "text-slate-500">// 1. Ver"text-purple-400">ify Authentication & Role
  "text-purple-400">const { data: { user }, error: authError } = "text-purple-400">await supabase.auth.getUser()
  "text-purple-400">if (authError || !user) "text-purple-400">throw "text-purple-400">new Error('Unauthorized')

  "text-slate-500">// 2. Insert with strict RLS (empresa_id inferred "text-purple-400">from user profile securely)
  "text-purple-400">const { data: profile } = "text-purple-400">await supabase
    ."text-purple-400">from('perfiles')
    .select('empresa_id')
    .eq('id', user.id)
    .single()

  "text-purple-400">const { data: "text-purple-400">newReception, error: insertError } = "text-purple-400">await supabase
    ."text-purple-400">from('recepciones')
    .insert([{ 
      ...data, 
      estado: 'RECIBIDO',
      creado_por: user.id,
      empresa_id: profile.empresa_id "text-slate-500">// Multi-tenant isolation
    }])
    .select()
    .single()

  "text-purple-400">if (insertError) "text-purple-400">throw "text-purple-400">new Error('Failed to create reception')
  
  "text-purple-400">return { success: true, data: "text-purple-400">newReception }
}

# Infraestructura & Setup

La configuración, el despliegue y las contribuciones están fuertemente estandarizadas:

  • Despliegue Vercel: Integración CI/CD continua. Despliegues 'zero-config' al momento de empujar código.
  • Base de Datos: Migraciones y push de schemas a través de las CLI de Supabase.
  • Variables de Entorno: Conexiones aisladas y anónimas mediante tokens dedicados (Anon Keys).

¿Interesado en saber más sobre la arquitectura técnica?

Contáctame