ixiclinicDocs
DesarrolladoresApps del monorepo

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.

StackReact 19, Vite 6, React Router 7, Zustand 5, Tailwind 4
Puerto local5173 (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. cerilab de cerilab.ixiclinic.com).
  • Desarrollo: en localhost/127.0.0.1 se usa el query param ?tenant=<slug>; si falta, cae al default cerilab.

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, WhatsAppButton y sections/ (secciones del website builder: HeroSection, ServicesSection, FaqSection, TestimonialsSection, StatsSection, ContactSection, CtaSection, AppointmentCtaSection, InfoBarSection, más un SectionRenderer).
  • 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).

On this page