Landing

apps/landing/README.md

apps/landing

Página de marketing pública de Nidus. Presenta el producto, comunica el valor de la plataforma y captura leads mediante un formulario de lista de espera.

Índice

  1. Propósito
  2. Stack
  3. Estructura
  4. Desarrollo
  5. Secciones de la página
  6. Hook useCtaClick
  7. Integración con Analytics
  8. Formulario de lista de espera
  9. Variables de entorno

1. Propósito

La landing page tiene un único objetivo: convertir visitantes en leads calificados. Las secciones explican el problema que resuelve Nidus, muestran cómo funciona y dirigen al usuario al formulario de acceso anticipado.


2. Stack

TecnologíaUso
Next.js 16 (App Router)Framework base, 'use client' solo donde es necesario
Tailwind CSS v4Estilos, sin tailwind.config
@repo/uiTodos los componentes de sección (Hero, Footer, etc.)
@repo/analyticsGA4, GTM, Facebook Pixel — montados en layout
@repo/schemasValidación del formulario de lista de espera con Zod
@repo/app-servicessaveSurvey — persistencia en Firestore

3. Estructura

apps/landing/ ├── app/ │ ├── layout.tsx # Layout raíz: fuentes, metadata, AnalyticsProvider │ ├── page.tsx # Página principal (Client Component) │ └── globals.css # Tailwind + tokens de @repo/ui ├── components/ │ ├── waitlist-form/ # Formulario de lista de espera (nombre, email, teléfono, etc.) │ └── analytics/ │ └── analytics-tracker.tsx # Re-tracking de PageView en SPA navigation ├── hooks/ │ └── use-cta-click.tsx # Hook centralizado para clicks de CTA (scroll + GA4) ├── services/ # Lógica de envío del formulario ├── module/ # Constantes y helpers del módulo landing └── public/ # Assets estáticos (imágenes, manifest)

4. Desarrollo

bash
# Solo esta app
pnpm dev --filter=landing

# Todo el monorepo
pnpm dev

La app corre en http://localhost:3000 por defecto.


5. Secciones de la página

app/page.tsx es un Client Component que compone todas las secciones:

SecciónComponenteCTA hacia
Hero<Hero>#waitlist-form
Carrusel de soluciones<SolutionCarousel>
Banner intermedio<BannerSectionOptionOne>#waitlist-form
Cómo funciona<DemoSection>#waitlist-form
Audiencias<RoleSection>
Formulario de lista<BannerSectionOptionTwo>
Tarjetas de problema<ProblemCardsContainer>
Footer<LandingFooter>#waitlist-form

Todos los componentes son de @repo/ui. Los 4 componentes con CTA reciben la prop onCtaClick que se instancia con el hook useCtaClick.


6. Hook useCtaClick

Archivo: hooks/use-cta-click.tsx

Centraliza la lógica de todos los botones de llamada a la acción de la landing:

  1. Registra el evento landing_cta_click en GA4.
  2. Hace scroll suave hasta el elemento destino (por defecto #waitlist-form).
  3. Ejecuta un callback adicional opcional.

API

ts
function useCtaClick(options: IUseCtaClickOptions): () => void;

interface IUseCtaClickOptions {
  source: string; // Identificador del lugar donde se invoca el CTA
  targetId?: string; // ID del elemento destino del scroll (default: 'waitlist-form')
  onClick?: () => void; // Callback adicional opcional
}

Uso en app/page.tsx

tsx
'use client';
import { useCtaClick } from '@/hooks/use-cta-click';

export default function Home() {
  const heroCtaClick = useCtaClick({ source: 'hero' });
  const bannerSectionOneCtaClick = useCtaClick({ source: 'banner_section_one' });
  const demoSectionCtaClick = useCtaClick({ source: 'demo_section' });
  const footerCtaClick = useCtaClick({ source: 'landing_footer' });

  return (
    <main>
      <Hero onCtaClick={heroCtaClick} ... />
      <BannerSectionOptionOne onCtaClick={bannerSectionOneCtaClick} ... />
      <DemoSection onCtaClick={demoSectionCtaClick} />
      <LandingFooter onCtaClick={footerCtaClick} ... />
    </main>
  );
}

Valores de source en uso

sourceSección
'hero'Hero principal
'banner_section_one'Banner de conversión
'demo_section'Sección "Así funciona"
'landing_footer'Footer de la landing

7. Integración con Analytics

La landing usa @repo/analytics en tres capas:

1. Proveedor en el layout

tsx
// app/layout.tsx
import { AnalyticsProvider } from '@repo/analytics';

export default function RootLayout({ children }) {
  return (
    <html>
      <body>
        <AnalyticsProvider>{children}</AnalyticsProvider>
      </body>
    </html>
  );
}

2. Re-tracking de pageviews

tsx
// components/analytics/analytics-tracker.tsx
'use client';
import { AnalyticsTracker } from '@repo/analytics/tracker';

Escucha cambios de ruta y re-dispara el evento de page_view en GTM y Facebook Pixel en cada navegación SPA.

3. Evento personalizado por clic de CTA

El hook useCtaClick emite el evento landing_cta_click a GA4 con los siguientes parámetros:

ParámetroTipoEjemplo
cta_sourcestring'hero'
scroll_targetstring'waitlist-form'
page_pathstring'/landing'
ts
import { trackGA4Event } from '@repo/analytics';

trackGA4Event('landing_cta_click', {
  cta_source: 'hero',
  scroll_target: 'waitlist-form',
  page_path: window.location.pathname,
});

8. Formulario de lista de espera

Componente: components/waitlist-form/waitlist-form.tsx

Captura nombre, email, teléfono, rol, métodos de control, tamaño del barrio y disposición a pagar. Valida con createSurveyDataSchema de @repo/schemas y persiste con saveSurvey de @repo/app-services.

El formulario está montado dentro de <BannerSectionOptionTwo> en app/page.tsx, en la sección con id="waitlist-form".


9. Variables de entorno

bash
# apps/landing/.env.local
NEXT_PUBLIC_GTM_ID=GTM-XXXXXXXX
NEXT_PUBLIC_FB_PIXEL_ID=1234567890123456

Para configurar en Firebase App Hosting, ver la sección Agregar variables a App Hosting del README de @repo/analytics.

Propósito

Esta app tiene dos secciones principales:

SecciónURLDescripción
UI Showcase/uiCatálogo interactivo de todos los componentes de @repo/ui con variantes, props y ejemplos copiables
Docs/docVisor de documentación técnica: README, CONTRIBUTING, WORKFLOW, agentes de IA, skills y READMEs de cada app

Estructura de Rutas

app/ ├── page.tsx # Landing con accesos rápidos a /ui y /doc ├── globals.css # Importa Tailwind v4 + tokens de @repo/ui ├── layout.tsx # Layout raíz con fuentes y metadata └── (dev)/ ├── ui/ │ ├── page.tsx # Índice de componentes (base + shared) │ ├── base/ # Showcase de primitivos: badge, button, card... │ └── shared/ # Showcase de compuestos: logo, sidebar... └── doc/ ├── layout.tsx # Layout con sidebar colapsable ├── page.tsx # Índice general de documentación ├── readme/page.tsx # Lee README.md del monorepo ├── contributing/page.tsx# Lee CONTRIBUTING.md del monorepo ├── workflow/page.tsx # Lee WORKFLOW.md del monorepo ├── apps/ │ ├── page.tsx # Índice de apps con documentación │ └── [app]/page.tsx # Lee apps/[nombre]/README.md dinámicamente ├── agents/ │ ├── page.tsx # Índice de agentes de Copilot │ └── [name]/page.tsx # Lee .github/agents/[nombre].md └── skills/ ├── page.tsx # Índice de skills de Copilot └── [name]/page.tsx # Lee .github/skills/[nombre]/SKILL.md

Desarrollo

bash
# Solo esta app
pnpm dev --filter=documentation

# Todo el monorepo
pnpm dev

La app corre en http://localhost:3001 por defecto (el puerto puede variar si hay otras apps corriendo).

Convenciones

CSS y Estilos

El archivo app/globals.css es el punto de entrada de Tailwind para esta app. El orden de imports es crítico:

css
@import 'tailwindcss';
@plugin 'tailwindcss-animate';
@source '../../../packages/ui/src';
@import '../../../packages/ui/src/styles/globals.css';
  • @source es necesario para que Tailwind detecte las clases usadas en @repo/ui.
  • Los tokens de color (primary, secondary, tertiary, quaternary) y las variables de tema vienen de @repo/ui/src/styles/globals.css.

Lectura de Archivos del Monorepo

Las páginas del /doc leen archivos Markdown directamente del sistema de archivos con fs.readFileSync. La ruta base es relativa al process.cwd() de la app (apps/documentation/):

ts
// README del monorepo raíz
path.join(process.cwd(), '..', '..', 'README.md');

// README de otra app
path.join(process.cwd(), '..', '..', 'apps', 'landing', 'README.md');

// Agente de Copilot
path.join(process.cwd(), '..', '..', '.github', 'agents', 'architect.md');

Agregar un Nuevo Componente al Showcase

  1. Crear el componente en packages/ui/src/components/base/ o shared/.
  2. Exportarlo desde el index.ts correspondiente.
  3. Crear apps/documentation/app/(dev)/ui/base/[nombre]/page.tsx con:
    • Descripción breve, variantes visuales, casos de uso con datos Nidus y tabla de props.

Agregar Documentación de una Nueva App

  1. Crear un README.md en la raíz de la nueva app (apps/[nueva-app]/README.md).
  2. La sección /doc/apps lo detecta automáticamente y genera la entrada en el índice y la sidebar.

Stack

TecnologíaUso
Next.js 16 (App Router)Framework base, RSC por defecto
Tailwind CSS v4Estilos, sin tailwind.config
@repo/uiComponentes del sistema de diseño
@repo/iconsIconografía (lucide-react)
fs (Node.js)Lectura de Markdown en tiempo de build/request
MarkdownViewerRenderizado de contenido Markdown con resaltado de sintaxis