Lab-site
Sitio público por laboratorio (*.ixiclinic.com) — React 19 + Vite + React Router, carrito con Zustand y endpoints públicos del portal.
El lab-site (apps/lab-site) es el sitio público de cada laboratorio: catálogo de pruebas,
agenda de citas, consulta de resultados y carrito de compra, sin necesidad de login. Cada tenant
tiene su propio sitio, resuelto por subdominio.
| Stack | React 19, Vite 6, React Router 7, Zustand 5, Tailwind 4 |
| Puerto local | 5173 (vite --port 5173) |
| Producción | *.ixiclinic.com (un sitio por laboratorio) |
| Paquete | @ixiclinic/lab-site |
Nota: No confundir con el Portal. El lab-site es público y sin login (vitrina + carrito); el portal exige autenticación de paciente o empresa.
Stack
SPA con Vite 6 y React 19. Ruteo con React Router 7 (react-router-dom), estado del
carrito con Zustand 5, estilos con Tailwind 4, animaciones con framer-motion e iconos
lucide-react. Importa tipos compartidos desde @ixiclinic/types.
Resolución de tenant
El tenant se resuelve del host en apps/lab-site/src/lib/api.ts (función getTenantSlug()):
- Producción: se toma el subdominio de
*.ixiclinic.com(p. ej.cerilabdecerilab.ixiclinic.com). - Desarrollo: en
localhost/127.0.0.1se usa el query param?tenant=<slug>; si falta, cae al defaultcerilab.
El slug resuelto se exporta como tenantSlug y lo consumen tanto el cliente HTTP como el store del
carrito.
Comunicación con el API
El cliente (apps/lab-site/src/lib/api.ts) usa endpoints públicos del portal
/api/portal/{slug}/... (sin auth). Expone dos helpers:
portalGet<T>(path: string): Promise<T> // GET /api/portal/<slug><path>
portalPost<T>(path: string, body: unknown): Promise<T> // POST /api/portal/<slug><path>La base se toma de VITE_API_URL (default http://localhost:5000). No hay cookies ni tokens: son
rutas públicas.
Carrito (Zustand, persistido por tenant)
El store apps/lab-site/src/store/cart.ts usa el middleware persist de Zustand. La clave de
almacenamiento incluye el slug del tenant para aislar el carrito por laboratorio:
{ name: `ixiclinic-cart-${tenantSlug}` }El store maneja addItem, removeItem, updateQuantity, clearCart y los selectores derivados
total() e itemCount().
Estructura (apps/lab-site/src/)
pages/—Home,Catalog,Citas,Results,Locations,NotFound.components/—Header,Footer,Layout,HeroSlider,WhatsAppButtonysections/(secciones del website builder:HeroSection,ServicesSection,FaqSection,TestimonialsSection,StatsSection,ContactSection,CtaSection,AppointmentCtaSection,InfoBarSection, más unSectionRenderer).hooks/—use-tenant,use-website,use-page-title.lib/—api.ts,icons.ts,utils.ts.
Build
pnpm build ejecuta tsc -b && vite build. Para desarrollo:
pnpm --filter @ixiclinic/lab-site dev (puerto 5173).