feat(storefront): barra de navegación desktop con categorías, personajes, ayuda y ubicación

- Desktop xl+: las 6 categorías raíz directo en la barra con desplegable
  de subcategorías; lg-xl mantiene "Departamentos"; "Personajes"
  (colecciones) y "Ayuda" a la derecha; hamburguesa solo en mobile.
- "Enviar a <nombre> · <ciudad>" (location-store persistido) que precarga
  departamento/ciudad en el checkout; "Hola X / Mi cuenta" y "Cambios y
  devoluciones" en xl+.
- Header compacto al hacer scroll en lg+ (histéresis 160/40px).
- Fix de menús (también en DepartmentsMegaMenu, ya presente en prod): el
  click de mouse ya no cierra el menú recién abierto por hover
  (keepOpenAfterHover) y el hover solo reacciona a pointerType "mouse",
  así el mouseleave sintético de touch no autocierra en tablets.

Revisado por el subagente frontend-critic (3 hallazgos, todos corregidos y
re-verificados) + Playwright contra build de producción local: sin solapes
a 1024/1280/1440/1920, click real, touch, mobile 390/320 sin cambios.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_018r19s4H8MBGegqToqw5guP
This commit is contained in:
devopsandClaude Opus 5.5 committed 2026-09-27 05:22:21 +00:00
1 parent 503f56977d
commit e6f031a366
4 files changed
+399 -33

No files matched your search

@@ -3,12 +3,13 @@
import Image from "next/image";
import Link from "next/link";
import { ArrowLeft, Landmark, MapPin, MessageCircle, Truck } from "lucide-react";
import { type FormEvent, useState } from "react";
import { type FormEvent, useEffect, useState } from "react";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { ciudadesPorDepartamento, departamentos } from "@/lib/data/colombia";
import { formatMoney } from "@/lib/utils";
import { useCartStore } from "@/store/cart-store";
import { useLocationStore } from "@/store/location-store";
const SELECT_CLASS =
"h-12 w-full rounded-full border border-slate-200 bg-white px-5 text-sm text-slate-950 outline-none transition focus:border-cyan-500 focus:ring-4 focus:ring-cyan-300/30 disabled:opacity-50";
@@ -72,12 +73,24 @@ export function CheckoutClient() {
const [docType, setDocType] = useState<"CC" | "CE" | "NIT" | "TI" | "PP">("CC");
const [docNumber, setDocNumber] = useState("");
// Ciudad elegida en el header ("Enviar a ... · Cali"): precarga el
// departamento/ciudad solo si el cliente todavia no eligio nada aca.
const savedDepartmentId = useLocationStore((state) => state.departmentId);
const savedCityId = useLocationStore((state) => state.cityId);
const locationHydrated = useLocationStore((state) => state.hasHydrated);
const saveLocation = useLocationStore((state) => state.setLocation);
useEffect(() => {
if (!locationHydrated || !savedDepartmentId || !savedCityId) return;
setForm((current) => (current.departmentId ? current : { ...current, departmentId: savedDepartmentId, cityId: savedCityId }));
}, [locationHydrated, savedDepartmentId, savedCityId]);
const itemsSubtotal = items.reduce((sum, item) => sum + (item.product.price ?? 0) * item.quantity, 0);
const currency = items[0]?.product.currency;
async function submitAddress(event: FormEvent) {
event.preventDefault();
if (!cartId || !selectedCiudad || !selectedDepartamento) return;
saveLocation(selectedDepartamento.id, selectedCiudad.id);
setLoading(true);
setError(null);
try {