diff --git a/workloads/ecommerce/README.md b/workloads/ecommerce/README.md new file mode 100644 index 0000000..c895be2 --- /dev/null +++ b/workloads/ecommerce/README.md @@ -0,0 +1,59 @@ +# ARI Shopping Headless Storefront v4.0 + +Código base moderno para la tienda online ARI Shopping. + +## Stack + +- 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. + +## 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 + +```bash +cp .env.example .env.local +npm install +npm run dev +``` + +## Variables headless + +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. diff --git a/workloads/ecommerce/TECHNOLOGY.md b/workloads/ecommerce/TECHNOLOGY.md new file mode 100644 index 0000000..8c4938c --- /dev/null +++ b/workloads/ecommerce/TECHNOLOGY.md @@ -0,0 +1,29 @@ +# Arquitectura tecnológica + +```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`. diff --git a/workloads/ecommerce/app/api/health/route.ts b/workloads/ecommerce/app/api/health/route.ts new file mode 100644 index 0000000..80ffa12 --- /dev/null +++ b/workloads/ecommerce/app/api/health/route.ts @@ -0,0 +1,3 @@ +export function GET() { + return Response.json({ status: "ok", service: "ari-shopping-frontend", version: "4.0.0" }, { status: 200 }); +} diff --git a/workloads/ecommerce/app/catalog/page.tsx b/workloads/ecommerce/app/catalog/page.tsx new file mode 100644 index 0000000..4b9c963 --- /dev/null +++ b/workloads/ecommerce/app/catalog/page.tsx @@ -0,0 +1,26 @@ +import type { Metadata } from "next"; +import { CatalogClient } from "@/components/catalog/catalog-client"; +import { getProducts } from "@/lib/headless/client"; + +export const metadata: Metadata = { title: "Catálogo", description: "Explora todos los productos de Ari Shopping." }; + +type CatalogPageProps = { + searchParams: Promise>; +}; + +function single(value: string | string[] | undefined): string { + return Array.isArray(value) ? value[0] ?? "" : value ?? ""; +} + +export default async function CatalogPage({ searchParams }: CatalogPageProps) { + const params = await searchParams; + const products = await getProducts(); + return ( + + ); +} diff --git a/workloads/ecommerce/app/globals.css b/workloads/ecommerce/app/globals.css new file mode 100644 index 0000000..00d3b12 --- /dev/null +++ b/workloads/ecommerce/app/globals.css @@ -0,0 +1,20 @@ +@import "tailwindcss"; + +@theme { + --color-ari-cyan: #22d3ee; + --color-ari-magenta: #ec4899; + --color-ari-yellow: #fde047; + --color-ari-blue: #0e7490; + --animate-accordion-down: accordion-down .2s ease-out; + --animate-accordion-up: accordion-up .2s ease-out; +} + +@keyframes accordion-down { from { height: 0; } to { height: var(--radix-accordion-content-height); } } +@keyframes accordion-up { from { height: var(--radix-accordion-content-height); } to { height: 0; } } + +: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; } +button, a { -webkit-tap-highlight-color: transparent; } +::selection { background: #fde047; color: #0f172a; } diff --git a/workloads/ecommerce/app/layout.tsx b/workloads/ecommerce/app/layout.tsx new file mode 100644 index 0000000..717e9f3 --- /dev/null +++ b/workloads/ecommerce/app/layout.tsx @@ -0,0 +1,20 @@ +import type { Metadata, Viewport } from "next"; +import "./globals.css"; +import { CartDrawer } from "@/components/cart/cart-drawer"; +import { Footer } from "@/components/layout/footer"; +import { Navbar } from "@/components/layout/navbar"; +import { getProducts } from "@/lib/headless/client"; + +export const metadata: Metadata = { + metadataBase: new URL(process.env.NEXT_PUBLIC_SITE_URL ?? "https://shop.cruzcloud.net"), + title: { default: "ARI Shopping | Juguetes, regalos y variedades", template: "%s | ARI Shopping" }, + description: "Tienda online de juguetes, personajes, mochilas y regalos con atención personalizada.", + 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 default async function RootLayout({ children }: Readonly<{ children: React.ReactNode }>) { + const products = await getProducts(); + return
{children}