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
- Propósito
- Stack
- Estructura
- Desarrollo
- Secciones de la página
- Hook
useCtaClick - Integración con Analytics
- Formulario de lista de espera
- 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ía | Uso |
|---|---|
| Next.js 16 (App Router) | Framework base, 'use client' solo donde es necesario |
| Tailwind CSS v4 | Estilos, sin tailwind.config |
@repo/ui | Todos los componentes de sección (Hero, Footer, etc.) |
@repo/analytics | GA4, GTM, Facebook Pixel — montados en layout |
@repo/schemas | Validación del formulario de lista de espera con Zod |
@repo/app-services | saveSurvey — 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
# 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ón | Componente | CTA 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:
- Registra el evento
landing_cta_clicken GA4. - Hace scroll suave hasta el elemento destino (por defecto
#waitlist-form). - Ejecuta un callback adicional opcional.
API
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
'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
source | Secció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
// 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
// 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ámetro | Tipo | Ejemplo |
|---|---|---|
cta_source | string | 'hero' |
scroll_target | string | 'waitlist-form' |
page_path | string | '/landing' |
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
# 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ón | URL | Descripción |
|---|---|---|
| UI Showcase | /ui | Catálogo interactivo de todos los componentes de @repo/ui con variantes, props y ejemplos copiables |
| Docs | /doc | Visor 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
# 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:
@import 'tailwindcss';
@plugin 'tailwindcss-animate';
@source '../../../packages/ui/src';
@import '../../../packages/ui/src/styles/globals.css';
@sourcees 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/):
// 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
- Crear el componente en
packages/ui/src/components/base/oshared/. - Exportarlo desde el
index.tscorrespondiente. - Crear
apps/documentation/app/(dev)/ui/base/[nombre]/page.tsxcon:- Descripción breve, variantes visuales, casos de uso con datos Nidus y tabla de props.
Agregar Documentación de una Nueva App
- Crear un
README.mden la raíz de la nueva app (apps/[nueva-app]/README.md). - La sección
/doc/appslo detecta automáticamente y genera la entrada en el índice y la sidebar.
Stack
| Tecnología | Uso |
|---|---|
| Next.js 16 (App Router) | Framework base, RSC por defecto |
| Tailwind CSS v4 | Estilos, sin tailwind.config |
@repo/ui | Componentes del sistema de diseño |
@repo/icons | Iconografía (lucide-react) |
fs (Node.js) | Lectura de Markdown en tiempo de build/request |
MarkdownViewer | Renderizado de contenido Markdown con resaltado de sintaxis |