From fe7b2019c24dcf7e997ca9a5bb8828e1b7a6a538 Mon Sep 17 00:00:00 2001 From: Cristian Cruz Date: Sun, 19 Jul 2026 21:28:10 -0500 Subject: [PATCH] feat(ecommerce): migrate Ari Shopping to Next.js 15 headless storefront v4 --- workloads/ecommerce/.eslintrc.json | 6 + workloads/ecommerce/Dockerfile | 4 +- workloads/ecommerce/README.md | 71 +- workloads/ecommerce/TECHNOLOGY.md | 38 +- workloads/ecommerce/app/globals.css | 17 +- workloads/ecommerce/app/layout.tsx | 5 +- workloads/ecommerce/app/loading.tsx | 10 + workloads/ecommerce/app/page.tsx | 4 +- .../ecommerce/components/cart/cart-drawer.tsx | 2 +- .../components/catalog/catalog-client.tsx | 2 +- .../ecommerce/components/home/hero-bento.tsx | 31 +- .../ecommerce/components/layout/footer.tsx | 2 +- .../ecommerce/components/layout/navbar.tsx | 98 +- .../components/product/product-card.tsx | 23 +- .../components/product/product-carousel.tsx | 43 +- .../components/product/product-gallery.tsx | 67 +- .../components/search/predictive-search.tsx | 38 +- workloads/ecommerce/components/ui/sheet.tsx | 38 +- .../components/wishlist/wishlist-drawer.tsx | 47 + workloads/ecommerce/data/products.json | 51 +- workloads/ecommerce/lib/headless/types.ts | 1 + workloads/ecommerce/next-env.d.ts | 4 + workloads/ecommerce/next.config.ts | 17 + workloads/ecommerce/package-lock.json | 7144 +++++++++++++++++ workloads/ecommerce/package.json | 51 +- .../ecommerce/public/images/brand/favicon.png | Bin 33603 -> 8227 bytes .../public/images/brand/logo-ari.png | Bin 156305 -> 56284 bytes .../public/images/categories/figuras.webp | Bin 61680 -> 31178 bytes .../public/images/categories/marvel.webp | Bin 156726 -> 84414 bytes .../public/images/categories/mochilas.webp | Bin 156726 -> 84414 bytes .../public/images/categories/peluches.webp | Bin 89982 -> 47300 bytes .../public/images/collections/frozen.webp | Bin 25678 -> 17106 bytes .../images/collections/intensamente.webp | Bin 34368 -> 21496 bytes .../images/collections/lilo-stitch.webp | Bin 19036 -> 12134 bytes .../public/images/collections/marvel.webp | Bin 42370 -> 28336 bytes .../images/collections/mickey-minnie.webp | Bin 21304 -> 13510 bytes .../public/images/collections/toy-story.webp | Bin 31036 -> 20006 bytes .../public/images/hero/hero-real.webp | Bin 109730 -> 61718 bytes .../products/ari-001-toy-story-5-rex.webp | Bin 111334 -> 62060 bytes .../ari-002-toy-story-5-buzz-lightyear.webp | Bin 195528 -> 121496 bytes .../products/ari-003-toy-story-5-jessie.webp | Bin 199592 -> 136660 bytes .../products/ari-004-toy-story-5-woody.webp | Bin 187640 -> 115138 bytes .../products/ari-005-intensamente-furia.webp | Bin 154010 -> 98182 bytes .../ari-006-intensamente-tristeza.webp | Bin 167484 -> 107720 bytes .../ari-007-minnie-y-mickey-mouse.webp | Bin 171462 -> 107816 bytes .../products/ari-008-angel-lilo-y-stitch.webp | Bin 142446 -> 87764 bytes ...-009-mochilas-disney-coleccion-marvel.webp | Bin 301492 -> 219848 bytes ...hila-ligera-y-resistente-al-agua-de-d.webp | Bin 254682 -> 169108 bytes ...hila-escolar-de-spider-man-de-disney-.webp | Bin 351278 -> 254260 bytes ...ney-mochila-de-poliester-ultraligera-.webp | Bin 167464 -> 107006 bytes .../ari-013-mochila-pequena-bob-esponja.webp | Bin 128842 -> 86514 bytes ...hila-casual-de-gran-capacidad-con-est.webp | Bin 115834 -> 73080 bytes ...hila-escolar-estilo-britanico-de-disn.webp | Bin 47702 -> 28538 bytes ...ney-mochila-iron-man-de-poliester-ult.webp | Bin 62396 -> 39474 bytes ...hila-de-disney-marvel-capitan-america.webp | Bin 57066 -> 36778 bytes .../public/images/promo/hero-vibrant.webp | Bin 125156 -> 73298 bytes .../public/images/promo/promo-backpacks.webp | Bin 75942 -> 52744 bytes .../public/images/promo/promo-characters.webp | Bin 64210 -> 42316 bytes .../public/images/promo/promo-marvel.webp | Bin 62090 -> 42576 bytes .../thumbnails/ari-001-toy-story-5-rex.webp | Bin 0 -> 15128 bytes .../ari-002-toy-story-5-buzz-lightyear.webp | Bin 0 -> 23784 bytes .../ari-003-toy-story-5-jessie.webp | Bin 0 -> 22638 bytes .../thumbnails/ari-004-toy-story-5-woody.webp | Bin 0 -> 18580 bytes .../ari-005-intensamente-furia.webp | Bin 0 -> 24996 bytes .../ari-006-intensamente-tristeza.webp | Bin 0 -> 26226 bytes .../ari-007-minnie-y-mickey-mouse.webp | Bin 0 -> 27364 bytes .../ari-008-angel-lilo-y-stitch.webp | Bin 0 -> 22494 bytes ...-009-mochilas-disney-coleccion-marvel.webp | Bin 0 -> 37628 bytes ...hila-ligera-y-resistente-al-agua-de-d.webp | Bin 0 -> 39982 bytes ...hila-escolar-de-spider-man-de-disney-.webp | Bin 0 -> 48680 bytes ...ney-mochila-de-poliester-ultraligera-.webp | Bin 0 -> 22328 bytes .../ari-013-mochila-pequena-bob-esponja.webp | Bin 0 -> 23848 bytes ...hila-casual-de-gran-capacidad-con-est.webp | Bin 0 -> 21268 bytes ...hila-escolar-estilo-britanico-de-disn.webp | Bin 0 -> 7890 bytes ...ney-mochila-iron-man-de-poliester-ult.webp | Bin 0 -> 10414 bytes ...hila-de-disney-marvel-capitan-america.webp | Bin 0 -> 11470 bytes workloads/ecommerce/store/cart-store.ts | 13 +- workloads/ecommerce/store/wishlist-store.ts | 45 + workloads/ecommerce/tsconfig.tsbuildinfo | 1 + 79 files changed, 7686 insertions(+), 187 deletions(-) create mode 100644 workloads/ecommerce/.eslintrc.json create mode 100644 workloads/ecommerce/app/loading.tsx create mode 100644 workloads/ecommerce/components/wishlist/wishlist-drawer.tsx create mode 100644 workloads/ecommerce/package-lock.json create mode 100644 workloads/ecommerce/public/images/thumbnails/ari-001-toy-story-5-rex.webp create mode 100644 workloads/ecommerce/public/images/thumbnails/ari-002-toy-story-5-buzz-lightyear.webp create mode 100644 workloads/ecommerce/public/images/thumbnails/ari-003-toy-story-5-jessie.webp create mode 100644 workloads/ecommerce/public/images/thumbnails/ari-004-toy-story-5-woody.webp create mode 100644 workloads/ecommerce/public/images/thumbnails/ari-005-intensamente-furia.webp create mode 100644 workloads/ecommerce/public/images/thumbnails/ari-006-intensamente-tristeza.webp create mode 100644 workloads/ecommerce/public/images/thumbnails/ari-007-minnie-y-mickey-mouse.webp create mode 100644 workloads/ecommerce/public/images/thumbnails/ari-008-angel-lilo-y-stitch.webp create mode 100644 workloads/ecommerce/public/images/thumbnails/ari-009-mochilas-disney-coleccion-marvel.webp create mode 100644 workloads/ecommerce/public/images/thumbnails/ari-010-mochila-ligera-y-resistente-al-agua-de-d.webp create mode 100644 workloads/ecommerce/public/images/thumbnails/ari-011-mochila-escolar-de-spider-man-de-disney-.webp create mode 100644 workloads/ecommerce/public/images/thumbnails/ari-012-disney-mochila-de-poliester-ultraligera-.webp create mode 100644 workloads/ecommerce/public/images/thumbnails/ari-013-mochila-pequena-bob-esponja.webp create mode 100644 workloads/ecommerce/public/images/thumbnails/ari-014-mochila-casual-de-gran-capacidad-con-est.webp create mode 100644 workloads/ecommerce/public/images/thumbnails/ari-015-mochila-escolar-estilo-britanico-de-disn.webp create mode 100644 workloads/ecommerce/public/images/thumbnails/ari-016-disney-mochila-iron-man-de-poliester-ult.webp create mode 100644 workloads/ecommerce/public/images/thumbnails/ari-017-mochila-de-disney-marvel-capitan-america.webp create mode 100644 workloads/ecommerce/store/wishlist-store.ts create mode 100644 workloads/ecommerce/tsconfig.tsbuildinfo diff --git a/workloads/ecommerce/.eslintrc.json b/workloads/ecommerce/.eslintrc.json new file mode 100644 index 0000000..6b10a5b --- /dev/null +++ b/workloads/ecommerce/.eslintrc.json @@ -0,0 +1,6 @@ +{ + "extends": [ + "next/core-web-vitals", + "next/typescript" + ] +} diff --git a/workloads/ecommerce/Dockerfile b/workloads/ecommerce/Dockerfile index 87049fa..1697e32 100644 --- a/workloads/ecommerce/Dockerfile +++ b/workloads/ecommerce/Dockerfile @@ -1,7 +1,7 @@ FROM node:22-alpine AS deps WORKDIR /app -COPY package.json ./ -RUN npm install --no-audit --no-fund +COPY package.json package-lock.json ./ +RUN npm ci --no-audit --no-fund FROM node:22-alpine AS builder WORKDIR /app diff --git a/workloads/ecommerce/README.md b/workloads/ecommerce/README.md index c895be2..c158737 100644 --- a/workloads/ecommerce/README.md +++ b/workloads/ecommerce/README.md @@ -1,59 +1,30 @@ -# ARI Shopping Headless Storefront v4.0 +# ARI Shopping Headless Storefront v4.1 -Código base moderno para la tienda online ARI Shopping. +Actualización enfocada en UX, interactividad y rendimiento sobre Next.js 15.5.10. -## Stack +## Correcciones principales -- Next.js 15 con App Router y Server Components. -- React 19 y TypeScript estricto. -- Tailwind CSS 4. -- Componentes accesibles estilo shadcn/ui sobre Radix UI. -- Motion for React para microinteracciones. -- Zustand con persistencia local para el carrito. -- Adaptadores headless para catálogo local, Shopify Storefront API o Medusa. -- `next/image` para optimización de imágenes. -- Docker multi-stage con salida standalone. +- Encabezado blanco premium con acentos cyan, violeta, fucsia y navegación negra. +- Imágenes promocionales sin `opacity`, `mix-blend` ni filtros de color. +- Menú principal funcional mediante Drawer accesible de Radix UI. +- Cuenta y favoritos funcionales. +- Carrusel con flechas, teclado, scroll suave y `scroll-snap`. +- Zoom real de producto entre 100% y 300%, rueda, botones, doble clic y arrastre. +- Thumbnails WebP dedicados para tarjetas, búsqueda, carrito y favoritos. +- Recursos originales recomprimidos y caché HTTP para `/images`. +- `next/image` en modo `unoptimized` porque los recursos ya vienen procesados; evita latencia del optimizador durante la primera visita. +- Menos componentes animados con JavaScript; las tarjetas usan transiciones CSS. +- `content-visibility` para contenido fuera de pantalla. +- Pantalla `loading.tsx` para transiciones de ruta. -## Funcionalidad incluida - -- Header promocional cyan/amarillo/magenta. -- Búsqueda predictiva. -- Hero promocional Bento. -- Carrusel horizontal de novedades. -- Catálogo con búsqueda y filtros sin recarga. -- PDP dinámica con galería, zoom, variantes y recomendados. -- Botón móvil persistente para añadir al carrito. -- Carrito lateral con Zustand y persistencia. -- SEO y metadata con App Router. -- Endpoint `/api/health`. -- Arquitectura preparada para Shopify o Medusa. - -## Desarrollo +## Validación ```bash -cp .env.example .env.local -npm install -npm run dev +npm ci +npm run typecheck +npm run build ``` -## Variables headless +## Flujo -El valor predeterminado es: - -```text -HEADLESS_PROVIDER=mock -``` - -Proveedores preparados: - -```text -mock -shopify -medusa -``` - -Los adaptadores Shopify y Medusa incluyen la conexión inicial y un punto claro para completar el mapeo al modelo `Product`. - -## Compatibilidad GitOps - -El contenedor Next.js escucha en el puerto `80`, por lo que mantiene compatibilidad con el Service y Deployment existentes que apuntaban al frontend Nginx. +Gitea → Gitea Actions → Registry → actualización de `frontend.yaml` → Argo CD → Kubernetes. diff --git a/workloads/ecommerce/TECHNOLOGY.md b/workloads/ecommerce/TECHNOLOGY.md index 8c4938c..fffe8b5 100644 --- a/workloads/ecommerce/TECHNOLOGY.md +++ b/workloads/ecommerce/TECHNOLOGY.md @@ -1,29 +1,11 @@ -# Arquitectura tecnológica +# Tecnología — ARI Shopping v4.1 -```text -Browser - ↓ -Next.js 15 App Router - ├─ Server Components / SSR / metadata SEO - ├─ React Client Components - │ ├─ Motion microinteractions - │ ├─ Radix/shadcn UI - │ └─ Zustand persisted cart - ├─ Headless catalog interface - │ ├─ Local mock JSON - │ ├─ Shopify Storefront GraphQL - │ └─ Medusa Store REST - └─ next/image optimization - ↓ -Docker standalone :80 - ↓ -Gitea Actions → Registry → Argo CD → Kubernetes -``` - -## Decisiones - -- Mobile-first y componentes interactivos aislados como Client Components. -- Datos y layout principal renderizados desde Server Components. -- Modelo de dominio independiente del proveedor e-commerce. -- Carrito local persistente; posteriormente puede sincronizarse con checkout remoto. -- Las imágenes reales de Ari Shopping viven en `public/images`. +- Next.js 15.5.10.5.10 App Router y React 19. +- TypeScript estricto. +- Tailwind CSS 4. +- Radix UI para Drawer y Dialog. +- Motion usado selectivamente en el hero; las tarjetas usan CSS para reducir JavaScript. +- Zustand persistente para carrito y favoritos. +- Catálogo headless intercambiable: mock, Shopify o Medusa. +- Imágenes WebP preoptimizadas con thumbnails de 560 px y fuentes PDP de hasta 1200 px. +- Contenedor Next.js standalone, no-root, puerto 80 y `/api/health`. diff --git a/workloads/ecommerce/app/globals.css b/workloads/ecommerce/app/globals.css index 00d3b12..c94cc02 100644 --- a/workloads/ecommerce/app/globals.css +++ b/workloads/ecommerce/app/globals.css @@ -1,10 +1,10 @@ @import "tailwindcss"; @theme { - --color-ari-cyan: #22d3ee; + --color-ari-cyan: #06b6d4; --color-ari-magenta: #ec4899; --color-ari-yellow: #fde047; - --color-ari-blue: #0e7490; + --color-ari-blue: #0f172a; --animate-accordion-down: accordion-down .2s ease-out; --animate-accordion-up: accordion-up .2s ease-out; } @@ -15,6 +15,17 @@ :root { color-scheme: light; } * { border-color: rgb(226 232 240); } html { scroll-behavior: smooth; } -body { margin: 0; background: #fff; color: #0f172a; font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; } +body { margin: 0; background: #fff; color: #0f172a; font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; text-rendering: optimizeLegibility; } button, a { -webkit-tap-highlight-color: transparent; } +button:focus-visible, a:focus-visible, input:focus-visible, select:focus-visible { outline: 3px solid rgb(167 139 250 / .55); outline-offset: 3px; } ::selection { background: #fde047; color: #0f172a; } + +.content-auto { + content-visibility: auto; + contain-intrinsic-size: 1px 900px; +} + +@media (prefers-reduced-motion: reduce) { + html { scroll-behavior: auto; } + *, *::before, *::after { animation-duration: .01ms !important; animation-iteration-count: 1 !important; transition-duration: .01ms !important; } +} diff --git a/workloads/ecommerce/app/layout.tsx b/workloads/ecommerce/app/layout.tsx index 717e9f3..72e5b1e 100644 --- a/workloads/ecommerce/app/layout.tsx +++ b/workloads/ecommerce/app/layout.tsx @@ -3,6 +3,7 @@ import "./globals.css"; import { CartDrawer } from "@/components/cart/cart-drawer"; import { Footer } from "@/components/layout/footer"; import { Navbar } from "@/components/layout/navbar"; +import { WishlistDrawer } from "@/components/wishlist/wishlist-drawer"; import { getProducts } from "@/lib/headless/client"; export const metadata: Metadata = { @@ -12,9 +13,9 @@ export const metadata: Metadata = { openGraph: { title: "ARI Shopping", description: "Tesoros para cada aventura.", type: "website", locale: "es_CO" }, }; -export const viewport: Viewport = { width: "device-width", initialScale: 1, themeColor: "#22d3ee" }; +export const viewport: Viewport = { width: "device-width", initialScale: 1, themeColor: "#0f172a" }; export default async function RootLayout({ children }: Readonly<{ children: React.ReactNode }>) { const products = await getProducts(); - return
{children}