Saltar al contenido
Volver a proyectos seleccionados
Demo en vivo · Stripe en modo de prueba
Logotipo de KROMA

KROMA

Una cafetería con pedido anticipado construida de principio a fin: una tienda en vivo con pago con Stripe y una trastienda real, con tablero de cocina, jornada de servicio y cifras.

Imagen principal de la tienda KROMA

Una cafetería de especialidad ficticia construida como un sistema completo de pedido anticipado, no como una maqueta. Los clientes recorren un menú diario en vivo, personalizan una bebida, pagan con Stripe y siguen el pedido; el personal trabaja ese mismo pedido en un tablero de cocina, gestiona una jornada de servicio que termina con un arqueo de caja y consulta las cifras del local, mientras que los precios, el stock, el estado del pago y los permisos se deciden en Postgres, sin confiar en el navegador.

// Capa de datos

Lógica primero en Postgres

Los precios, el stock, los permisos y los informes viven en funciones SQL protegidas por Row Level Security.

// Pagos

Pagar antes de pedir

Un pedido con tarjeta solo se crea cuando Stripe confirma el pago; los fallos se reembolsan.

// Zona del personal

Tablero de cocina Realtime

Supabase Realtime sobre los pedidos, con un sondeo de respaldo cada 30 segundos.

// Verificación

Probado en dos capas

node:test para la lógica de la aplicación, más pruebas SQL que se ejecutan dentro de una transacción revertida.

// resumen

Qué es KROMA

KROMA es una cafetería de especialidad ficticia construida como una aplicación web completa, con forma de producción. Un cliente recorre un menú diario en vivo, personaliza una bebida, paga con tarjeta a través de Stripe o en la barra, y sigue el pedido; el personal trabaja ese mismo pedido en un tablero de cocina y gestiona desde la misma aplicación el stock, el menú, los reembolsos y el arqueo de caja de fin de día. Es un proyecto de portafolio —el local es ficticio y los pagos se ejecutan en modo de prueba de Stripe—, pero el flujo que hay detrás es real.

Cada pedido se valora, se descuenta del stock y se comprueba en cuanto a permisos en Postgres, así que la tienda, el tablero de cocina y el informe del día leen el mismo registro de pedido.

// 01. Contexto estratégico

Por qué KROMA está construido así

01 // Problema

Un botón de pago no es una tienda

Las demos de pedido anticipado suelen terminar en un botón de Pedir: un menú estático y nada detrás. Lo que distingue a un sistema que funciona de una maqueta es todo lo que esas demos omiten: el stock que se agota a media mañana, un estado de pago que hay que verificar en el servidor y una segunda interfaz para las personas que realmente preparan el pedido.

Dinámica clave: Dos interfaces, un pedido
02 // Solución

Un registro de pedido, dos interfaces

KROMA está construido como un sistema de dos caras. La página de pedido del cliente y el tablero de cocina del personal leen y hacen avanzar el mismo registro de pedido. Las funciones de Postgres calculan el precio de cada pedido, descuentan el stock y deciden quién puede hacer qué; la aplicación Next.js las invoca.

Dinámica clave: Las reglas viven junto a los datos
03 // Resultado

Una tienda que gestiona su propio día

Una tarea diaria de Vercel Cron abre la jornada de servicio, y el personal la cierra contando el cajón frente a un informe generado. La demo es segura para mostrarla a desconocidos: los pagos con tarjeta se ejecutan en modo de prueba de Stripe, y el PIN del personal es una credencial real con permiso de escritura que no se publica.

Dinámica clave: Jornada abierta por cron, cierre contado
// principio de arquitectura
“El navegador propone; Postgres decide. El cliente envía ID de artículos, cantidades y nombres de modificadores: cada precio, cambio de stock y comprobación de permisos se resuelve en la base de datos.”
— Principio de arquitectura
// 02. Recorrido del producto

Vea KROMA en acción

Un recorrido por la tienda del cliente y la zona del personal: qué hace cada parte y cómo se siente al usarla

Lado del cliente

Lo que encuentra un invitado o un socio, desde explorar el menú hasta recoger el pedido

01Tienda

Tienda con menú en vivo

Un menú diario en vivo presentado como una lista editorial en lugar de una cuadrícula de tarjetas. El stock y los precios vienen directamente de Postgres, así que una hornada agotada aparece como terminada en menos de un minuto.

Captura de pantalla de la tienda KROMA con la lista editorial del menú y su panel de vista previa fijo
  • Menú diario en vivo: Los artículos, los precios y el stock por artículo se leen de Postgres y se revalidan cada 30 segundos. Si la consulta no devuelve nada, la tienda recurre a una instantánea local de menu.json.
  • Portada WebGL con mapa de profundidad: Un fragment shader desplaza la foto de portada a lo largo de un mapa de profundidad en escala de grises según se mueve el cursor. Recurre a una imagen fija sin WebGL, si falla la carga o con movimiento reducido.
  • Lista de menú editorial: Filas de ancho completo separadas por líneas finas, con un panel de vista previa fijo en escritorio que cambia la fotografía al pasar el cursor o al enfocar con el teclado.
  • Filtrado por categorías: Navegación en forma de píldoras cuyo relleno activo se desliza entre categorías en lugar de saltar.
  • Personalización del artículo: Un panel deslizante lateral para modificadores y variantes, con el precio recalculándose a medida que usted elige.
  • Estados de stock: Un artículo agotado muestra “Agotado por hoy”; con cinco o menos disponibles muestra “Solo quedan N”.
Next.js 16SupabaseFramer MotionWebGLRevalidación cada 30 s
02Pago y seguimiento

Pedir, pagar y seguir

Pague con tarjeta a través de Stripe Checkout o elija pagar en la barra, y luego siga el pedido en una página que se puede compartir. Un pedido con tarjeta solo existe cuando Stripe confirma el pago.

  • Invitado o cuenta: Se puede pedir sin cuenta. El carrito de un cliente con sesión iniciada se guarda en Postgres, y el carrito de invitado se fusiona con él al iniciar sesión.
  • Stripe Checkout: Los pagos con tarjeta se ejecutan en modo de prueba de Stripe. La sesión se valora a partir de un presupuesto calculado en el servidor y caduca a los 30 minutos.
  • Pago en la barra: Un pedido en el mostrador se registra como pendiente; el barista anota efectivo o tarjeta al marcarlo como pagado.
  • Control dietético: Las dietas y alérgenos guardados señalan en el carrito los artículos en conflicto antes del pago.
  • Seguimiento del pedido: Una página compartible /order/[token] muestra el pedido pasando de pagado a en preparación y a listo, con actualización cada 15 segundos. Antes de que llegue a la barra, el cliente puede cancelarlo, y un pago en línea se reembolsa.
  • Aviso de pedido listo: Se envía una notificación Web Push cuando el pedido está listo, con un correo electrónico de respaldo si el push no está disponible.
  • Recibos: Una página de recibo imprimible con código de barras y línea de IVA, además de un recibo enviado por correo con Resend cuando hay una dirección disponible.
Stripe CheckoutStripe WebhooksWeb Push (VAPID)ResendServer Actions
03Cuentas

Cuenta y tarjeta de sellos

Iniciar sesión con correo o con Google desbloquea una tarjeta de sellos, un “lo de siempre” con un toque, el historial de pedidos y las preferencias dietéticas guardadas.

  • Inicio de sesión: Correo y contraseña con correo de confirmación y restablecimiento de contraseña, o Google OAuth.
  • Tarjeta de sellos: Solo las categorías de bebidas suman un sello. Una cuenta nueva empieza con 2 sellos y una tarjeta completa tiene 10. El saldo se deriva del historial de pedidos en lugar de almacenarse, así que un pedido cancelado o reembolsado se lleva sus sellos consigo.
  • Artículo gratis: Una tarjeta completa se canjea en el pago por una unidad de un artículo.
  • Lo de siempre: El artículo que el cliente ha pedido más veces, con los modificadores de su último pedido, listo para repetir.
  • Preferencias: Un nombre visible más un nombre de barra aparte que se canta en el pase, dietas (vegana, vegetariana, pescetariana, sin gluten) y alérgenos que evitar.
  • Historial de pedidos: Pedidos anteriores con un botón para repetirlos.
Supabase AuthGoogle OAuthRow Level SecuritySaldo derivado

Lado del personal

La trastienda en /dashboard, tras una cuenta real y un PIN de 4 dígitos

PIN de 4 dígitos

Acceso del personal en dos capas

El iPad de detrás de la barra mantiene una sesión de larga duración para que el tablero pueda mostrarse; una persona desbloquea el derecho a escribir con un PIN de 4 dígitos. El PIN se comprueba solo dentro de Postgres con pgcrypto, cinco intentos fallidos bloquean a esa persona durante 15 minutos y cada desbloqueo queda registrado.

Captura de pantalla del teclado PIN del personal de KROMA sobre la superficie oscura de la pantalla de cocina
04Pantalla de cocina

Tablero de pedidos

Un tablero oscuro y de alto contraste pensado para la línea de servicio. Los pedidos avanzan por cuatro carriles —En el pase, En preparación, Listo en la barra, Recogido— y la página del cliente los sigue.

Captura de pantalla del tablero de cocina de KROMA con los pedidos repartidos en los cuatro carriles y sus indicadores de antigüedad
  • Cola en vivo: Supabase Realtime sobre la tabla de pedidos recarga el tablero. Un indicador de conexión muestra en vivo, reconectando o sin conexión, un sondeo cada 30 segundos cubre los huecos y las escrituras se desactivan sin conexión.
  • Indicador de antigüedad: Cada pedido muestra un cronómetro en marcha y pasa de reciente a templado a los 5 minutos y a retrasado a los 10.
  • Aviso de pedido nuevo: Suena un sonido cuando aparece un pedido nuevo en el tablero, una vez que el dispositivo se ha activado con un toque.
  • Avanzar y deshacer: Un toque hace avanzar un pedido. Devolverlo un carril atrás está abierto a cualquiera durante 90 segundos; pasado ese tiempo, solo pueden hacerlo un encargado o un propietario.
  • Notas, anulaciones y reembolsos: Cualquier persona de turno puede añadir una nota. Los encargados y propietarios pueden anular o reembolsar, y el reembolso de Stripe de un pedido en línea se emite desde la aplicación.
  • Descuentos e invitaciones: Los encargados y propietarios pueden aplicar un porcentaje, un importe fijo o una invitación completa, con un motivo obligatorio.
  • Instalable: El tablero se instala como una PWA a pantalla completa en horizontal.
Supabase RealtimeRPC de PostgresPWAFramer Motion
05Jornada de servicio

Abrir, vender y cerrar

La venta se organiza por jornadas. Alguien abre la tienda con los recuentos del stock de referencia, una tienda cerrada rechaza pedidos, y un encargado o propietario la cierra contando el cajón frente a un informe generado.

  • Apertura del servicio: La apertura restablece el stock de cada artículo a su nivel de referencia, o a los recuentos introducidos en la pantalla de apertura. Cualquier rol puede abrir, y dos dispositivos que pulsen Abrir producen una sola apertura.
  • Tienda cerrada: Mientras no haya ninguna jornada abierta, hacer un pedido provoca el mensaje “La panadería está cerrada.”
  • Números de ticket: Cada jornada numera sus tickets desde #001.
  • Forma de pago: En el mostrador, el barista registra si un pedido se pagó en efectivo o con tarjeta.
  • Arqueo de caja: La pantalla de cierre admite un recuento por denominación y muestra la diferencia respecto al efectivo esperado: fondo de caja más cobros en efectivo menos reembolsos en efectivo.
  • Protección de cierre: Una jornada no puede cerrarse mientras queden pedidos en el pase; el error enumera sus números de ticket.
  • Informe del día: Pedidos, ingresos, neto e IVA; totales de efectivo, tarjeta y en línea; importes con descuento, anulados, reembolsados y desechados; y lo que queda en stock.
  • IVA: Los precios siguen incluyendo IVA. El IVA se extrae por categoría (11 % en todo lo que se vende hoy) y se guarda como instantánea en cada línea de pedido.
RPC de PostgresJornadas de servicioIVAVercel Cron
06Analítica

Cifras y libro de registro

Una vista del local para encargados y propietarios, agregada en Postgres en lugar de en el navegador, de modo que la comprobación de permisos está donde se suma el dinero.

  • Ingresos: Los cobros en una ventana de fechas elegida, filtrables por categoría.
  • Detrás de la barra: Por persona: pedidos iniciados, anulados y reembolsados, con tiempos tomados de las mismas filas de pedido.
  • Libro de registro: El rastro de auditoría, filtrado y paginado: desbloqueos, movimientos de pedidos, descuentos, límites de turno, apertura y cierre, y ediciones del menú.
  • Control de permisos: Cada agregado vuelve a leer el rol de quien llama en la base de datos y rechaza a cualquiera por debajo de encargado.
RPC de PostgresRegistro de auditoríaControl por rol
Administración del menú

Editar el menú en vivo

Los encargados y propietarios editan el menú desde el panel: reordenan artículos, abren la ficha del artículo con su editor de modificadores, gestionan categorías y eliminan artículos. Cada escritura pasa por una función con permisos comprobados, y los pedidos anteriores conservan el nombre y el precio de su artículo.

// 03. Arquitectura y topología

Arquitectura full-stack serverless

Una aplicación Next.js en Vercel frente a un proyecto de Supabase, con Stripe, cron y push conectados mediante rutas de servidor

topology.ascii
    [ Browser · Customer & Staff PWAs ]                           [ Vercel Cron ]
      │                           │                                     │
      │                           │ HTTPS                               │ Bearer secret
      │                           ▼                                   ▼
      │                    ┌────────────────────────────────────────────────────────────────┐
      │                    │                Next.js 16 · React 19  (Vercel)                 │
      │                    ├────────────────────────────────────────────────────────────────┤
      │                    │ • Server Components & Server Actions                           │
      │                    │ • Route Handlers: Stripe webhook · cron · auth callbacks       │
      │                    │ • Signed cookie that unlocks staff writes                      │
      │                    └───────────┬─────────────────────┬───────────────────┬──────────┘
      │                                │ RPC                 │ API               │ Push · email
      │                                ▼                   ▼                  ▼
      │                     ┌──────────────────────┐  ┌───────────────┐  ┌───────────────────┐
      └─ RPC · Realtime ──►│ Supabase             │  │ Stripe        │  │ Notifications     │
                            ├──────────────────────┤  ├───────────────┤  ├───────────────────┤
                            │ • Postgres + RLS     │  │ • Checkout    │  │ • Web Push (VAPID)│
                            │ • Security-definer   │  │ • Refunds     │  │ • Resend email    │
                            │   RPCs               │  │ • Webhooks    │  └───────────────────┘
                            │ • Auth               │  └───────────────┘
                            │ • Realtime           │
                            └──────────────────────┘

Matriz de decisiones técnicas y justificación

Dónde reside cada responsabilidad y por qué está ahí

Capa y alcanceTecnología elegidaCapacidades clave y justificación
Frontend
Framework de la aplicación
Next.js 16 (App Router) + React 19

Componentes de servidor, acciones de servidor, una sola frontera de cliente

La tienda es un componente de servidor que obtiene el menú y lo revalida cada 30 segundos; una única frontera de cliente guarda el estado de filtros y carrito. Las mutaciones son acciones de servidor, y los route handlers cubren el webhook de Stripe, la tarea cron y los callbacks de autenticación.
Persistencia
Base de datos y lógica
Supabase Postgres

Reglas de negocio como RPC security-definer, RLS sobre los datos de clientes

La valoración de precios, el stock, los números de ticket, los permisos, los reembolsos y los informes viven en funciones SQL gestionadas como migraciones versionadas, así que la tienda, el tablero y la tarea cron pasan por las mismas reglas.
Autenticación
Identidad
Supabase Auth + tabla staff

Correo/Google para clientes; sesión de estación más PIN para el personal

Los clientes inician sesión con Supabase Auth. El personal trabaja a través de una estación que puede leer, mientras que un PIN por persona desbloquea las escrituras, de modo que cada cambio se atribuye a alguien.
Comercio
Pagos
Stripe Checkout + webhooks

Primero se paga, después existe el pedido

La Checkout Session se valora a partir de un presupuesto de la base de datos. El pedido se crea solo después del pago, desde el webhook o la página de retorno, y se reembolsa si no puede registrarse.
Tiempo real
Actualizaciones en vivo
Supabase Realtime

Suscribirse ligero, recuperar completo

El tablero de cocina escucha los cambios en la tabla de pedidos y recarga mediante una sola RPC, con un sondeo cada 30 segundos como respaldo. Las páginas de pedido de los clientes sondean cada 15 segundos.
Operaciones
Tareas programadas
Vercel Cron

Una ruta diaria autenticada a las 03:00 UTC

La tarea libera los pedidos en línea caducados, abre la jornada de servicio a través de la misma RPC que usa un barista y elimina las suscripciones push obsoletas.
Mensajería
Notificaciones
Web Push (VAPID) + Resend

Primero push, el correo como respaldo

Los avisos de pedido listo salen como Web Push mediante un service worker mínimo; si no se entrega ningún push, se envía un correo cuando existe una dirección. Los recibos se envían por correo con Resend.
Diseño
Interfaz
Tailwind CSS v4 + Framer Motion + WebGL

Líneas finas, etiquetas mono, alternativas para movimiento reducido

La estructura viene de líneas de un solo píxel en lugar de tarjetas o sombras. Los tokens de movimiento se comparten, y cada animación ambiental se degrada a un estado fijo y utilizable con movimiento reducido.
// 04. Bajo el capó

Construido para mantener correctos el dinero, el stock y los permisos

Cuatro decisiones que mantienen unido el sistema

// 01 • Pagos y pedidos

Pagar antes de que exista el pedido

Un pedido con tarjeta solo se crea a partir de una sesión de Stripe ya pagada, de modo que un pedido con tarjeta sin pagar nunca llega a la tabla de pedidos.

  • Valorado en la base de datos: El presupuesto previo al pago y la inserción posterior al pago comparten un mismo resolutor de líneas. El cliente envía ID de artículos, cantidades y nombres de modificadores; cada precio se lee de menu_items.
  • Webhook y página de retorno: checkout.session.completed y la redirección de confirmación llaman ambos a un mismo manejador, idempotente sobre stripe_session_id.
  • Reembolso en caso de fallo: Si una sesión pagada no puede convertirse en pedido, se reembolsa automáticamente con una clave de idempotencia por sesión.
  • Firma comprobada: El webhook rechaza cualquier solicitud que no lleve una firma de Stripe válida.
  • Stripe
  • Webhooks
  • Idempotencia
  • Postgres RPC
// 02 • Datos y seguridad

Postgres como autoridad

Row Level Security y las funciones security-definer colocan las reglas junto a los datos, de modo que ninguna ruta del cliente puede saltárselas.

  • Datos de cliente acotados: Las políticas RLS limitan a los clientes a sus propios carritos, perfiles, favoritos y pedidos.
  • Pedidos de invitado por token: Los invitados no tienen ninguna política sobre los pedidos. Un token imposible de adivinar da acceso exactamente a un pedido mediante order_by_token(), que omite el token, el ID de usuario y las columnas de Stripe.
  • Instantáneas: El nombre del artículo, el precio, el tipo de IVA y la elegibilidad para sello se copian en cada línea de pedido, de modo que las ediciones posteriores del menú no reescriben la historia.
  • Saldos derivados: El saldo de la tarjeta de sellos es una consulta sobre el historial de pedidos, no un contador almacenado.
  • Probado en SQL: Las pruebas SQL ejercitan el esquema real dentro de una transacción que se revierte, mientras que node:test cubre la lógica del lado de la aplicación.
  • Row Level Security
  • security definer
  • Supabase
  • node:test
// 03 • Personal y permisos

Roles, PIN y un rastro de auditoría

Los roles de propietario, encargado y personal los aplica una única función staff_can() que invocan las políticas RLS y cada RPC de escritura.

  • Una sola función de permisos: El espejo en TypeScript existe solo para ocultar botones; la frontera es la base de datos.
  • Relectura del actor: Cada RPC de escritura lee el actor de la tabla staff, así que desactivar a alguien detiene su siguiente escritura aunque su cookie siga siendo válida.
  • Bloqueo por PIN: Cinco PIN fallidos bloquean a una persona durante 15 minutos y escriben un evento staff.locked.
  • Reclamar la propiedad: La primera cuenta con sesión iniciada que llama a claim_owner() pasa a ser propietaria y la puerta se cierra tras ella: sin ninguna credencial precargada.
  • Registro de auditoría: Las acciones escriben en un registro staff_events que los encargados y propietarios pueden leer.
  • staff_can()
  • pgcrypto
  • Bloqueo
  • Registro de auditoría
// 04 • Operaciones

Una jornada de servicio que se gestiona sola

Una tarea diaria y una apertura a prueba de condiciones de carrera mantienen la tienda funcionando sin ningún paso manual.

  • Cron diario: vercel.ts programa /api/cron/release-holds a las 03:00 UTC, autenticado con un token bearer CRON_SECRET.
  • Qué hace: Libera los pedidos en línea caducados, abre la jornada mediante open_service (actuando como la persona activa del personal con el alta más antigua) y elimina las suscripciones push de más de 24 horas.
  • Apertura a prueba de carreras: La apertura es una inserción sobre la clave primaria de la jornada, de modo que las aperturas simultáneas dejan una sola jornada y nunca restablecen el stock de la mañana.
  • Stock serializado: Las filas del menú se bloquean con for update cuando se descuenta el stock, así que dos compradores que buscan el último artículo hacen cola.
  • Tickets numerados: Los números de ticket salen de la fila de la jornada, dentro de la misma función de creación de pedidos que descuenta el stock.
  • Vercel Cron
  • open_service
  • Bloqueos de fila
  • Jornadas de servicio
// 05. Desafíos y obstáculos

Obstáculos de ingeniería y soluciones

Donde el dinero real, el stock real y el personal real hicieron que el enfoque fácil fuera erróneo

// 01 • Concurrencia y dinero

Dos personas, un último bollo

El obstáculo

Un pedido en línea ya no reserva stock mientras el cliente está en Stripe, así que dos personas pueden pagar por el último artículo. El presupuesto rechaza un artículo que ya se agotó, pero no puede evitar esa carrera.

La resolución

La función de creación de pedidos bloquea las filas del menú con for update antes de descontar el stock. La inserción del perdedor lanza un error y el manejador de pagos le reembolsa. Los reintentos son seguros: los pedidos son únicos por stripe_session_id, el webhook y la página de retorno comparten un mismo manejador, y el reembolso lleva una clave de idempotencia por sesión.

Impacto: Un pago se convierte, como máximo, en un pedido o se reembolsa. La contrapartida, aceptada deliberadamente, es que el perdedor paga y se le reembolsa en lugar de ser bloqueado antes.

  • Bloqueos de fila
  • Idempotencia
  • Reembolso automático
  • Stripe
// 02 • Transporte de datos

Un carrito que cabe en los metadatos de Stripe

El obstáculo

El pedido lo crea, después del pago, código que solo ve la Checkout Session, así que el carrito tiene que viajar con ella, y los valores de los metadatos de Stripe son cortos.

La resolución

packItems() serializa el carrito a JSON y lo divide en claves items_N de 500 caracteres, limitadas a 45 para dejar espacio al nombre, las notas y los ID. unpackItems() lo reensambla, y la base de datos reconstruye y revalora el pedido a partir de los ID de artículos en lugar de fiarse de los metadatos.

Impacto: No existe ninguna fila de pedido en borrador antes del pago, así que un pago abandonado no deja nada atrás. Los pedidos están limitados a 50 líneas.

  • Metadatos de Stripe
  • Fragmentación
  • Server Actions
// 03 • Seguridad e identidad

El iPad que nunca cierra sesión

El obstáculo

El dispositivo de detrás de la barra permanece con la sesión iniciada todo el día, así que su sesión no puede decir quién hizo qué, y un iPad robado no debería poder reembolsar a nadie.

La resolución

Una estación mantiene la sesión de larga duración para que el tablero pueda leer, pero una restricción de la base de datos prohíbe que una estación tenga PIN, de modo que nunca se puede generar una sesión de actor para ella. Las personas desbloquean las escrituras con un PIN de 4 dígitos comprobado en Postgres, y cada RPC vuelve a leer el actor. Las políticas del personal que consultaban la tabla staff entraban en recursión, así que un auxiliar security-definer is_staff() rompe el ciclo.

Impacto: Las escrituras se atribuyen a una persona, la desactivación surte efecto en la siguiente escritura, y poder ver el tablero nunca confiere la capacidad de modificarlo.

  • PIN + pgcrypto
  • Bloqueo
  • security definer
  • RLS
// 06. Stack tecnológico

Con qué está construido

Desglose capa por capa de los frameworks, servicios y herramientas

Frontend y UI
  • Next.js 16
  • React 19
  • Tailwind CSS v4
  • shadcn/ui
  • Base UI
  • Framer Motion
Backend y API
  • Supabase Postgres
  • Stripe Checkout
  • Stripe Webhooks
Tiempo real y WSS
  • Supabase Realtime
Colas en segundo plano
  • Vercel Cron
Almacenamiento y medios
  • ImageKit
Autenticación y seguridad
  • Supabase Auth
  • Row Level Security
  • pgcrypto
Comunicaciones
  • Web Push (VAPID)
  • Resend
DevOps y despliegue
  • Vercel
  • pnpm
// 07. Ejecución local y evaluación

Pruébelo en vivo y reprodúzcalo en local

La tienda en kroma.lenardtunya.com se abre sola cada mañana, así que siempre hay un menú en vivo del que pedir. Los pagos con tarjeta se ejecutan en modo de prueba de Stripe: use 4242 4242 4242 4242, cualquier fecha de caducidad futura y cualquier CVC.

El panel del personal es real, y precisamente por eso su PIN no se publica: es una credencial con permiso de escritura sobre datos de demostración en vivo. Para verlo, ejecute el proyecto en local (el seed incluye su propio PIN desechable) o póngase en contacto conmigo para una demostración interactiva.

local-setup.sh
# 1. Clone and install
git clone https://github.com/JustTunya/kroma.git
cd kroma
pnpm install

# 2. Start local Postgres + Auth, seeded from supabase/seed.sql
supabase start

# 3. Configure environment variables (see README for each one)
cp .env.local.example .env.local

# 4. Start the app
pnpm dev
# Storefront: http://localhost:3000
# Dashboard:  http://localhost:3000/dashboard

# 5. Unlock the staff dashboard (local seed only)
#    a. Sign up at /auth/sign-up, confirm via the local Inbucket mail UI
#       (`supabase status` prints its URL, usually http://localhost:54324)
#    b. Link that account to the seeded staff row:
#       update staff set user_id = (select id from auth.users
#         where email = 'you@example.com') where display_name = 'Demo Owner';
#    c. Sign in, open /dashboard, enter the seeded PIN 1234, then open the shop

Avisos de evaluación y detalles de la simulación

  • Local simulado: KROMA es ficticio. Los pagos con tarjeta se ejecutan en modo de prueba de Stripe sin cargos reales, y los textos sobre tueste y origen de la tienda son ambientación, no una cadena de suministro real.
  • Web Push real, sin SMS: Los avisos de pedido listo son notificaciones reales de VAPID Web Push, con un correo electrónico de respaldo. No existe ninguna vía por SMS ni por número de teléfono.
  • Licencia PolyForm Noncommercial: El código fuente es público para su lectura, estudio y uso personal o educativo bajo PolyForm Noncommercial 1.0.0. El uso comercial, incluido ejecutarlo como un local real, requiere una licencia aparte.

Descubra KROMA en acción

De acceso público para evaluación técnica, revisión de código e inspección de la arquitectura.