Tienda en línea de accesorios y regalos (Nicaragua). El proyecto es una
migración del template estático IsaWebPlantilla a una aplicación real con
Next.js 16 (App Router) + TypeScript + Prisma + PostgreSQL (Supabase),
según el documento de arquitectura acordado para el proyecto.
Reemplazar el catálogo estático original por una aplicación con dos partes que comparten la misma base de datos:
- La tienda pública (
app/(site)) — donde el cliente navega el catálogo, arma su carrito, se registra/inicia sesión y hace el pedido. - El panel administrativo (
app/(admin)) — donde el staff y los administradores de IsaStore cargan productos, revisan pedidos, ven reportes de ventas y gestionan cuentas de otros miembros del equipo.
No son dos proyectos separados: es una sola aplicación Next.js con dos grupos de rutas independientes, cada uno con su propio layout, su propia sesión de autenticación y sus propias reglas de acceso (ver Los dos sistemas).
- Next.js 16 (App Router, TypeScript, Turbopack)
- Prisma 7 + PostgreSQL (pensado para Supabase) — ver la nota sobre Prisma 7 más abajo, trae cambios importantes respecto a versiones previas
- Supabase Auth — login real de clientes y de staff/admin, con dos sesiones independientes (ver más abajo)
- Zustand — carrito y wishlist, persistidos en
localStorage - Bootstrap 5 — CSS del template original (tienda), servido desde
/public/assets; el panel admin usa una plantilla aparte (DreamsPOS) en/public/admin-assets - PayPal (
@paypal/react-paypal-js) y Google Maps (@react-google-maps/api) — paquetes instalados, integración pendiente (ver Estado del proyecto)
No hay una suite de pruebas automatizadas (no hay Jest/Vitest/Playwright configurado todavía).
Aunque viven en el mismo repo y la misma base de datos, la tienda y el panel admin están deliberadamente separados a nivel de sesión:
- Sesión de cliente (
sb-customer-auth) — se inicia en/cuenta, y es la que exige/checkoutpara poder completar un pedido. Cualquier persona se puede registrar sola con su correo real. - Sesión de staff/admin (
sb-admin-auth) — se inicia en/admin-login, y es la que exige todo lo que está bajo/admin/*. Las cuentas de staff/admin no se auto-registran: las crea un administrador desde/admin/usuarios/nuevocon una contraseña fija que le entrega a la persona por fuera del sistema.
Son dos cookies distintas manejadas por el mismo middleware
(proxy.ts + lib/supabase/middleware.ts), así que iniciar sesión en el
panel como staff nunca cierra ni pisa la sesión de cliente de esa misma
persona en /cuenta, y viceversa. El rol (customer / staff / admin)
se guarda en app_metadata de Supabase Auth, que solo se puede escribir
con la Service Role Key — un usuario no puede otorgarse un rol más alto por
su cuenta.
Dentro del panel, algunas secciones son solo para admin (Reportes,
Usuarios, borrar productos) — staff puede operar el día a día (cargar
productos, ver ventas) pero no esas pantallas. Esto se refuerza en dos
capas: el middleware bloquea /admin/* a quien no tenga sesión de
staff/admin, y requireRole() (lib/auth/session.ts) vuelve a validar el
rol exacto dentro de cada página que lo necesita.
app/
(site)/ tienda pública: shop, producto/[slug], carrito,
checkout, cuenta, favoritos, blog, contacto
(admin)/ panel: admin-login, admin/ (dashboard, productos,
categorías, ventas, reportes, reseñas, usuarios)
api/auth/ logout unificado (cierra la sesión del scope indicado)
components/ componentes de la tienda + components/admin/
lib/
actions.ts server actions públicas (signIn, signUp, createReview)
admin/ server actions y utilidades del panel
auth/ sesión actual, traducción de errores de Supabase
checkout/ createOrder (crea el pedido con su método de pago)
supabase/ clientes de Supabase (browser, server, admin, middleware)
cart-store.ts carrito (Zustand + localStorage)
wishlist-store.ts favoritos (Zustand + localStorage)
categories.ts árbol de categorías
prisma.ts cliente Prisma (adapter-pg)
prisma/
schema.prisma modelo de datos (fuente de verdad en Prisma)
schema.sql el mismo esquema en SQL plano, listo para ejecutar
migrations/ migraciones aplicadas, incluida la sincronización
con Supabase Auth (triggers de auth.users -> public.users)
scripts/ scripts de una sola vez: seed de categorías/productos
de ejemplo, backfill de descripciones, export a CSV
public/assets/ CSS/JS/imágenes del template de la tienda
public/admin-assets/ CSS/JS/imágenes del template del panel (DreamsPOS)
public/uploads/ imágenes subidas desde el panel (no se versiona el
contenido, solo la carpeta — ver .gitignore)
proxy.ts middleware: refresca ambas sesiones y aplica el
gateo de /admin y /checkout
El esquema completo vive en prisma/schema.prisma (y su equivalente en SQL
plano en prisma/schema.sql). Resumen de las tablas principales:
| Modelo | Para qué sirve |
|---|---|
User |
Cuenta de cliente o staff/admin. El id es el mismo UUID que Supabase Auth le da a esa persona (sincronizado por trigger). |
Address |
Direcciones de envío/facturación de un usuario. |
Category |
Categorías y subcategorías del catálogo (autorelación padre-hijo). |
Product / ProductImage / ProductVariant |
Productos, sus imágenes y variantes opcionales (talla/color). |
Review |
Reseñas de producto (rating 1-5). |
Cart / CartItem |
Carrito persistido en base de datos (por usuario o por invitado) — el carrito que usa la tienda hoy vive en localStorage, esta tabla está preparada para cuando se quiera sincronizar entre dispositivos. |
Wishlist |
Favoritos guardados por usuario — mismo caso que el carrito: hoy la wishlist de la tienda vive en localStorage. |
Coupon |
Cupones de descuento. |
Order / OrderItem |
Pedidos y sus líneas (con snapshot del nombre/precio al momento de compra). |
Payment |
Registro de pagos (PayPal u otro proveedor), con la respuesta cruda de la API para auditoría. |
StoreLocation |
Ubicaciones físicas de la tienda (mapa de contacto). |
BlogPost |
Artículos del blog. |
NewsletterSubscriber |
Suscriptores al newsletter. |
Requisitos: Node.js 20+, una base de datos PostgreSQL (el proyecto está pensado para Supabase, que además provee la autenticación), y npm.
git clone <url-del-repo>
cd IsaStore-Ecommerce
npm install
cp .env.example .env.local # completar según la sección de abajo
npx prisma generate
npx prisma db push # crea las tablas (alternativa: correr prisma/schema.sql a mano)
npm run devLa app queda en http://localhost:3000 — la tienda en /, el login de
staff/admin en /admin-login.
Todas están documentadas con más detalle en .env.example; resumen por
grupo:
- Base de datos —
DATABASE_URL(conexión pooled, puerto 6543, la usa la app en runtime) yDIRECT_URL(conexión directa, puerto 5432, la usa el CLI de Prisma para migrar — las migraciones no funcionan bien a través de un pooler de transacciones). - Supabase Auth (obligatorias, sin esto no funciona ningún
login/registro):
NEXT_PUBLIC_SUPABASE_URL,NEXT_PUBLIC_SUPABASE_ANON_KEY,SUPABASE_SERVICE_ROLE_KEY(esta última nunca debe llegar al navegador — es la que puede crear staff y asignar roles). - Google Maps —
NEXT_PUBLIC_GOOGLE_MAPS_API_KEY(habilitar Maps JavaScript API, Places API y Geocoding API). Todavía no está conectada en el código (ver estado del proyecto). - PayPal —
PAYPAL_MODE,PAYPAL_CLIENT_ID,PAYPAL_CLIENT_SECRET,NEXT_PUBLIC_PAYPAL_CLIENT_ID,PAYPAL_WEBHOOK_ID. Con estas vacías, el checkout muestra el botón de PayPal deshabilitado ("Próximamente"). - Correo (SMTP) — pensado para las confirmaciones de pedido por correo;
las variables ya están en
.env.examplepero el envío de correos todavía no está implementado en el código (ver estado del proyecto). - App —
NEXT_PUBLIC_SITE_URL(usada para construir URLs absolutas).
scripts/ tiene scripts de una sola vez, pensados para correrse con
node scripts/<archivo>.cjs (leen .env.local ellos mismos):
seed-categories.cjs— crea las categorías y subcategorías reales del catálogo (idempotente, se puede correr más de una vez).seed-accesorios-subcategorias.cjs,seed-cadenas-subcategorias.cjs,seed-maquillaje-subcategorias.cjs,seed-peluches-subcategorias.cjs,seed-ropa-subcategorias.cjs,seed-empty-categories.cjs— cargan productos de ejemplo por subcategoría (sin imagen real).backfill-product-descriptions.cjs— completa descripción/atributos de productos que no los tengan.exportar-productos.cjs— exporta el catálogo actual a un CSV.
Este proyecto usa Prisma 7, que trajo varios cambios importantes respecto a versiones anteriores:
- La URL de conexión ya no va en
prisma/schema.prisma— vive enprisma.config.ts(usaDIRECT_URL, la conexión sin pooling, porque las migraciones no corren bien a través de pgbouncer). PrismaClientya no se puede instanciar sin argumentos: ahora requiere un driver adapter. Este proyecto usa@prisma/adapter-pgsobre unPooldepg(verlib/prisma.ts), conectado conDATABASE_URL(la conexión pooled).- El generador se dejó como
provider = "prisma-client-js"sinoutputa propósito (no el nuevo"prisma-client") — es lo que evita el errorCannot find module '.prisma/client/default'con Turbopack en Next.js 16. Si de todos modos aparece ese error, agregar ennext.config.ts:turbopack: { resolveAlias: { ".prisma/client/default": "./node_modules/.prisma/client/default.js", }, },
npx prisma generate descarga el motor de Prisma desde binaries.prisma.sh
— si trabajás detrás de un proxy o firewall que bloquee ese dominio, ese
paso puntual va a fallar aunque la configuración esté bien.
- Catálogo (
/shop,/producto/[slug]) con filtro por categoría, subcategoría, precio y rating. - Carrito (
/carrito) y wishlist (/favoritos), ambos enlocalStorage. - Registro/login real de clientes (
/cuenta) y de staff/admin (/admin-login), con Supabase Auth y sesiones separadas. - Checkout con un método de pago activo: efectivo en tienda (el pedido se marca pagado al crearse, porque el cliente paga al recoger en persona). El botón de PayPal está en la UI pero deshabilitado hasta cargar credenciales reales.
- Reseñas de producto (
WriteReviewForm), se publican sin aprobación manual. - Panel admin: productos (alta/edición/borrado), categorías/subcategorías,
ventas (listado + detalle imprimible tipo factura), reportes (ventas por
fecha, stock bajo, pedidos por estado/método de pago — solo
admin), reseñas (solo lectura + borrar), usuarios de staff (soloadmin).
- Cobro real con PayPal — conectar el botón (Orders API v2: crear y
capturar la orden desde el servidor) y, si se quiere una confirmación
confiable sin depender del navegador del cliente, un webhook de PayPal.
La tabla
Paymenty el campoOrder.paymentStatusya están listos para recibir esto. - Correo de confirmación de pedido — las variables SMTP ya están en
.env.example, pero hoy no se envía ningún correo al completar un pedido (solo se muestra un modal en pantalla). - Mapa de Google en
/contactoy autocompletado de dirección en/checkout— el paquete está instalado, la UI tiene el espacio reservado ("Mapa próximamente"), falta la integración. - Productos relacionados en la ficha de producto — no hay lógica de Prisma para esto todavía.
- Migrar carrito/wishlist de
localStoragea las tablasCart/Wishlistde la base de datos, para que persistan entre dispositivos (ya existe login real de clientes, así que esto ya no está bloqueado por falta de autenticación). - Blog (
/blog) conectado aBlogPost— listo para publicar, falta contenido real.
tmp/(salida de unnpm run buildde verificación) quedó en el repo como carpeta sin trackear; ya se agregó a.gitignorepara que no se vuelva a commitear por accidente, pero la carpeta en sí sigue en disco — se puede borrar sin riesgo (rm -rf tmp/) cuando quieras.productos-export.csvyprompts-fotos-productos.xlsxestán sueltos en la raíz del repo (salida descripts/exportar-productos.cjsy un archivo de trabajo aparte). No los toqué porque no son código ni configuración — si querés, se pueden mover a una carpetadata/para que la raíz del repo quede solo con configuración y código.- Hay bastante trabajo ya hecho sin commitear (auth, checkout, reseñas,
wishlist, varias páginas del panel, migraciones de Supabase Auth) —
conviene revisar
git statusy commitear en bloques lógicos antes de seguir agregando cambios encima.