E-commerce · Retail de tecnología

PCCoreTienda de tecnología con el catálogo del mayorista al día y el seguimiento de reparaciones del taller

Tienda online de PCCore: catálogo sincronizado con el mayorista, pago con PayPhone, transferencia o efectivo, y seguimiento de reparaciones por QR.

Tecnologías

  • Next.js 16
  • React 19
  • TypeScript
  • NestJS 11
  • PostgreSQL
  • Bun + Turborepo
  • Tailwind CSS v4
  • shadcn/ui
  • next-intl
  • React Hook Form + Zod
  • TipTap
  • Recharts
  • React Email + Resend
  • ExcelJS
  • sharp
  • Cloudflare R2
  • PayPhone
  • Bun test · Playwright
  • Docker
  • Railway
Página de inicio de la tienda online de PCCore, con buscador, menú de categorías, consulta de equipos en reparación y acceso a servicio técnico
  • 22

    secciones en el panel de administración

  • 9

    correos transaccionales editables

  • 3

    formas de pago

Mi rol

Desarrollo full stack de la tienda - catálogo, checkout, cuentas de cliente y panel de administración -, desde la integración con el mayorista y la migración de la tienda anterior hasta el despliegue en Railway. Entregado a través de NM Tech Studio.

El problema

PCCore vende computadoras, impresoras y accesorios en Durán, y además repara equipos. Su tienda online anterior corría en Stock Manager Advance, sobre MySQL, separada del sistema del taller: eran el mismo negocio y los mismos clientes, pero quien ya había dejado una laptop en reparación era un desconocido al comprar, y saber en qué iba una reparación o cuánto iba a costar obligaba a llamar al local. La tienda nueva tenía que vender en línea con el catálogo de su mayorista, Siglo 21, y a la vez ser la puerta de entrada al servicio técnico.

Qué construí

  • Tienda con catálogo filtrable por categoría, marca y rango de precio, búsqueda, ficha de producto con galería, carrito y checkout para invitado o cliente registrado.
  • Pagos con PayPhone, transferencia bancaria o efectivo, y entrega a domicilio con tarifa por provincia o ciudad, o retiro en tienda.
  • Cuentas de cliente con panel, historial de pedidos, direcciones, perfil y cambio de contraseña.
  • Sincronización automática con la API del mayorista Siglo 21: precio con el margen configurado, categoría, marca, ficha técnica e imágenes alojadas en Cloudflare R2.
  • Seguimiento público de reparaciones: el QR impreso en la orden del taller, o el número de orden con la cédula, muestra la etapa, el informe del técnico, las notas y el presupuesto.
  • Página de servicio técnico editable desde el panel - servicios, proceso, zonas de cobertura y preguntas frecuentes - con un formulario que arma la solicitud y la envía por WhatsApp.
  • Panel de administración con 22 secciones en cinco grupos: general, catálogo, ventas, marketing y sistema.
  • Nueve correos transaccionales enviados con Resend - confirmación del pedido, pago recibido, listo para retirar, recordatorio de pago y otros -, con plantillas editables y vista previa en el panel.
  • Analítica propia: visitas, vistas de producto y búsquedas en ventanas de 7, 30 y 90 días, con los productos más vistos y lo que más se busca.

Cómo está estructurado

La tienda es NovaCart - la misma plataforma de Rolo Electronics - en su propio workspace de Bun con Turborepo, dentro del monorepo del taller. Se despliega aparte y con el taller comparte solo la base PostgreSQL.

  • apps/web: Next.js 16 con la tienda y el panel de administración en una sola aplicación; las escrituras son server actions y las rutas de API quedan para exportaciones, pagos y consultas públicas.
  • apps/api: NestJS 11 para la autenticación y las tareas programadas - la sincronización con el mayorista, la purga de productos borrados y los respaldos -.
  • packages/db con 59 migraciones SQL, fuente única del esquema, y packages/auth-contract con el contrato JWT que comparten la web y la API.
  • Datos por SQL parametrizado sobre pg a través de cuatro fábricas de clientes, sesión con JWT HS256 que la web verifica localmente con el secreto compartido, y archivos en Cloudflare R2.
  • Refresh tokens rotativos: cada uno se usa una sola vez y se guarda como hash SHA-256; si alguien reutiliza uno viejo, se cierran todas las sesiones del usuario. El ingreso se bloquea tras intentos fallidos.
  • Rutas en español (/productos, /servicio-tecnico, /seguimiento) con inglés y francés activables desde el panel; el sitemap y el hreflang anuncian solo los idiomas activos.
  • Despliegue en Railway como dos servicios - la API y la web -, cada uno con su Dockerfile, contenedor sin root y healthcheck, que en la API comprueba también PostgreSQL. Los respaldos de la tienda van a R2 con su propia programación.
  • Pruebas con Bun test en la web y la API, que corren en CI junto al build en cada push y pull request, más cinco archivos de pruebas end-to-end con Playwright.

El catálogo, sincronizado con el mayorista

La API de la tienda recorre la lista de productos de Siglo 21 por páginas y la convierte en productos de la tienda, con precio, categoría, marca, ficha técnica e imágenes.

  • Pasadas automáticas dentro del horario que se fija en el panel - por defecto, de lunes a viernes de 8:00 a 18:00, hora de Ecuador -, con intervalo y tamaño de lote configurables y un botón para correrla a mano en cualquier momento.
  • Precio = costo ÷ (1 − margen) y, si la tienda publica con IVA incluido, × (1 + IVA). El margen es un porcentaje del precio de venta, no un recargo sobre el costo: con un costo de $1.123,19 y un margen del 20%, la venta neta es $1.403,99, y con el 15% de IVA se publica en $1.614,59.
  • Nombre, precio y descripción se pueden fijar a mano en cada producto, o sacarlo de la sincronización; la pasada respeta lo fijado y deja en el historial del producto lo que el proveedor quiso cambiar, firmado como «Siglo 21».
  • La ficha técnica que el proveedor manda en HTML se limpia antes de guardarse y solo conserva tablas, listas y texto.
  • Reglas de categorías portadas de un plugin de WordPress, y alias de marcas - «HPINC» termina en «HP» - para que la pasada siguiente no vuelva a crear el duplicado.
  • Cada pasada queda registrada con lo creado, lo actualizado y lo desactivado, y su detalle se abre desde el panel.

Seguimiento de reparaciones desde la tienda

El taller y la tienda comparten la base, así que el seguimiento no pasa por la API del ERP: la página lee la orden directamente, solo en lectura, y se la muestra al cliente con la misma credencial que tiene en el papel.

  • Dos entradas: el QR impreso en la orden abre /seguimiento con su token y la ficha aparece sin escribir nada; a mano, se consulta con el número de orden y la cédula del titular.
  • La ficha muestra la etapa - recibido, en proceso, listo, entregado -, el equipo, la falla, los accesorios, el informe del técnico, las notas del taller y el presupuesto con sus importes.
  • Cada consulta es una lectura fresca, sin caché de página: una respuesta guardada mostraría «en reparación» horas después de que el equipo estuviera listo.
  • Un token que no existe no termina en un 404 sino en el formulario con un aviso: el papel puede estar borroso y el camino manual sigue abierto.
  • En el teléfono la ficha queda justo debajo del título, para que quien escanea el QR vea el estado sin bajar.
  • Las URL con token llevan noindex y el sitemap solo publica el formulario vacío; la cabecera de la tienda lleva el acceso «Consulta tu equipo».

SEO local para el servicio técnico

La página de servicio técnico es la que responde a quien busca una reparación en Durán y Guayaquil, así que su marcado y su texto salen de los mismos datos que se editan en el panel.

  • Datos estructurados ComputerStore - un subtipo de LocalBusiness - desde un editor de datos del negocio: dirección, teléfono, horario y coordenadas.
  • Un grafo con el negocio, el Service con su catálogo de servicios y las zonas atendidas - Durán, Guayaquil y Guayas -, el FAQPage con las mismas preguntas que se ven en pantalla y la ruta de migas.
  • Teléfono y horario también en texto rastreable, leídos de la misma configuración que el marcado para que nunca se contradigan; la pregunta del horario desaparece si no hay horario cargado.
  • Enlaces a las categorías del catálogo desde el servicio técnico: quien viene por una reparación muchas veces necesita el repuesto, y la página dejó de ser un callejón sin salida para los rastreadores.
  • robots.txt excluye panel, cuenta, carrito, checkout y autenticación en los tres idiomas, además de las rutas de API y los parámetros de filtro del listado; el sitemap se regenera cada hora.
  • Título absoluto en servicio técnico, para que la plantilla no repita la marca y empuje el título más allá del corte de Google.

Por dentro del sitio

  • PCCore: Catálogo con filtros por precio, categoría y marca
    Catálogo con filtros por precio, categoría y marca.
  • PCCore: Ficha de producto con galería, precio, carrito, compra directa y consulta por WhatsApp
    Ficha de producto con galería, precio, carrito, compra directa y consulta por WhatsApp.
  • PCCore: Página de servicio técnico que lleva clientes al taller
    Página de servicio técnico que lleva clientes al taller.
  • PCCore: Seguimiento de reparaciones: número de orden o QR más la cédula del titular
    Seguimiento de reparaciones: número de orden o QR más la cédula del titular.

El panel de administración

Cada módulo resuelve una parte de la operación diaria del negocio, sin salir del panel.

  • Resumen

    Ventas y su tendencia, pedidos por estado, los productos más vendidos y las visitas de los últimos 30 días, con accesos rápidos al resto del panel.

  • Analítica

    Visitas, vistas de producto y búsquedas propias en ventanas de 7, 30 y 90 días, con los productos más vistos y las búsquedas más frecuentes.

  • Productos

    Alta, edición e importación desde Excel, galería de imágenes, detección de duplicados, historial de cambios y campos fijados a mano para que la sincronización no los pise.

  • Categorías y marcas

    La taxonomía que alimenta los filtros de la tienda; las variantes de marca que manda el proveedor se funden por alias en una sola.

  • Reseñas

    Moderación de las reseñas de producto, una por una o en lote.

  • Pedidos

    Estados según se envíe o se retire, edición de líneas y envío, cobro de saldo, recordatorio de pago, recibos de pago adjuntos e impresión; la barra lateral cuenta los que esperan algo.

  • Checkouts abandonados

    Quien completó sus datos en el checkout y no pagó, con nombre y teléfono para llamarle.

  • Clientes

    Ficha de cada comprador con su documento, sus pedidos, lo gastado y el ticket promedio, y la opción de bloquear la cuenta.

  • Cupones

    Descuentos con sus reglas de uso, validados en el checkout.

  • Envíos

    Tarifas por provincia o ciudad sobre las 24 provincias del Ecuador, costo propio por producto y retiro en tienda sin cargo.

  • Pagos

    PayPhone con su comisión trasladada al total, transferencia con las cuentas bancarias de la tienda y efectivo.

  • Correos y boletín

    Nueve plantillas transaccionales con variables y vista previa, y la lista de suscriptores del boletín.

  • Contenido

    Portada, banners, franjas de productos, testimonios, preguntas frecuentes, páginas y la página de servicio técnico, editables sin desplegar código.

  • Integración Siglo 21

    Margen, horario y lote de la sincronización, historial de cada pasada con su detalle, y cuántos precios cambiaría un margen nuevo antes de guardarlo.

  • Ajustes

    Identidad y colores, impuestos, idiomas activos, datos del negocio para el marcado estructurado, cabecera y pie, y el interruptor que cierra la tienda.

  • Usuarios, roles y respaldos

    Roles administrador, editor y lector, roles a medida sobre 14 recursos, y respaldos de la tienda en R2, todo reservado al administrador.

Decisiones técnicas

  • Una base compartida con el taller, con dueños separados: la tienda conserva sus 59 migraciones SQL y el taller las suyas en Prisma. Los cruces son lecturas puntuales - el seguimiento lee las órdenes, el checkout lee el padrón de clientes -, y el respaldo de la tienda enumera sus propias tablas, porque las dos plataformas conviven en el mismo esquema: el taller nombra las suyas en español y la tienda en inglés.
  • PostgreSQL plano sin reescribir la lógica: un constructor de consultas traduce a SQL parametrizado sobre pg la misma interfaz encadenable, estilo PostgREST, que usa todo el código. Como cada acceso a datos pasa por cuatro fábricas de clientes, cambiar el cliente bastó para mover la aplicación entera sin tocar reglas de negocio.
  • La comisión de PayPhone se traslada dividiendo, no sumando: con las tarifas publicadas, la pasarela se queda con un 5,75% de lo que cobra, así que sumarle ese porcentaje a $100 cobra $105,75 y deja $99,67. Dividiendo se cobran $106,10 y a la tienda le llegan exactamente $100, calculado al centavo para no cobrarle de más al comprador.
  • Primero lo gratis: para completar los datos del comprador, la cédula o el RUC se buscan en checkouts anteriores, en pedidos y en el padrón del taller, y solo si nadie lo conoce se consulta un registro externo que cobra por cada consulta.
  • El envío se cobra por producto y por unidad, no por pedido: un producto puede traer su propio costo y el resto paga la tarifa del destino. Un 0 significa «viaja gratis» y el vacío cae en la tarifa, y la misma función calcula lo que muestra el carrito y lo que se guarda en el pedido.
  • Los estados del pedido son los mismos para envío y retiro, pero se nombran según el caso: un pedido que el cliente pasa a recoger no figura como «Enviado» sino como «Listo para retirar». Partir los estados en dos habría roto el historial, los filtros y las consultas.

Retos

  • Proteger la única consulta pública que toca la base sin sesión: 60 pedidos simultáneos al seguimiento llevaban la portada de 1,3 s a 7,0 s. Quedó limitada a 30 consultas por minuto por IP, a 3 conexiones de un pool de 10 y a 15 segundos de memoria, así una avalancha vuelve lenta esa página y no deja al catálogo ni al checkout sin conexiones.
  • Que una falla del proveedor no vacíe la vitrina: una página vacía devuelta con HTTP 200 puede confundirse con el final del catálogo. Si una pasada intenta desactivar más del 30% de los productos activos - y más de 50 -, la baja se cancela y queda registrada; y un producto sin imagen espera en borrador hasta que llega una.
  • Descargar imágenes de un tercero sin abrirle una puerta al servidor: solo HTTPS, nada de localhost ni redes privadas, cada redirección revalidada con un máximo de tres, 20 segundos y 12 MB por archivo, y solo tipos de imagen. Lo que pasa se convierte a WebP y se aloja en R2, con hasta 3 imágenes por producto.
  • Recuperar la galería que la migración original había dejado atrás: en Stock Manager Advance las fotos secundarias vivían en una segunda tabla que no se leyó. Se recuperaron sin alterar ninguna imagen principal.

Resultado

La tienda corre en producción en Railway - su API y su web - sobre el mismo PostgreSQL que el taller. El catálogo se actualiza desde el mayorista dentro del horario que fija el negocio, respetando lo que se fijó a mano; el cliente paga con PayPhone, transferencia o efectivo, y quien ya pasó por el taller encuentra sus datos cargados al comprar. La tienda es también la puerta del servicio técnico: recibe solicitudes por WhatsApp y muestra en qué va cada reparación, sin llamadas y sin crear una cuenta.

¿Necesitas algo parecido?

Cuéntame qué estás construyendo y te respondo con alcance, stack y una fecha realista.