Tutorial: Crear una Página
Las páginas en este proyecto son delegadores — no contienen lógica ni UI directamente. Su único trabajo es invocar el layout correcto según la vertical activa. Toda la lógica real vive en los layouts.
Concepto clave
Section titled “Concepto clave”app/pages/webinars/index.vue → 7 líneas, solo invoca layoutapp/layouts/pages/inspiria/webinars.vue → 200+ líneas, toda la lógica y UIEsto permite que la misma ruta (/webinars) muestre una UI completamente diferente según la vertical activa, sin tocar el archivo de página.
Página común vs específica de vertical
Section titled “Página común vs específica de vertical”| Tipo | Layout en | Se selecciona con |
|---|---|---|
| Común (login, registro, cuenta) | layouts/pages/login.vue | commonLayout: true |
| Específica (cursos, lives, books) | layouts/pages/inspiria/courses.vue | commonLayout: false |
Ejemplo: Página de Webinars (específica de Inspiria)
Section titled “Ejemplo: Página de Webinars (específica de Inspiria)”-
Crea el archivo de página (thin page)
Crea
app/pages/webinars/index.vue:<script setup lang="ts">import { chooseLayoutPage } from '~/core/plugins'const layout = chooseLayoutPage({ page: 'webinars', commonLayout: false })</script><template><NuxtLayout :name="layout"></NuxtLayout></template>Eso es todo. La página solo hace dos cosas:
- Llama a
chooseLayoutPagepara resolver qué layout usar - Renderiza ese layout con
<NuxtLayout>
- Llama a
-
Crea el layout (donde vive la lógica real)
Crea
app/layouts/pages/inspiria/webinars.vue:<template><section class="webinars-page container mx-auto px-4 py-8"><h1 class="text-h1 font-bold text-graphite mb-6">{{ pageData?.title }}</h1><div v-if="isPending" class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6"><AESkeletonLoader v-for="i in 6" :key="i" /></div><div v-else class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6"><AEMainCardv-for="webinar in data":key="webinar.id":product="webinar"/></div></section></template><script setup lang="ts">const { data: pageData } = await useLocales<{ title: string }>('webinars')const { data, isPending } = useServices('getWebinars')</script>El layout va en
layouts/pages/inspiria/porque es específico de la vertical Inspiria. Si fuera una página común (compartida), iría directamente enlayouts/pages/. -
Registra la página como tipo válido
En
app/types/pages.d.ts, agrega el nombre de la página al tipoLayoutPage:export type LayoutPage =| 'courses'| 'lives'| 'books'| 'webinars' // ← agregar aquí| // ... otras páginasEsto permite que TypeScript valide los nombres en
chooseLayoutPagey enexclusivePages. -
Registra en exclusivePages de la vertical
Abre
app/constants/verticals/inspiria.tsy agrega la página:export const INSPIRIA: InspiriaVertical = {// ...exclusivePages: ['books', 'presentials', 'courses', 'webinars'],} -
Crea las traducciones del contenido
app/locales/inspiria/es/webinars.json {"title": "Webinars","subtitle": "Formación en directo con expertos"}app/locales/inspiria/en/webinars.json {"title": "Webinars","subtitle": "Live training with experts"} -
(Opcional) Registra traducciones de la ruta en i18n
Si la URL debe traducirse entre idiomas:
// Configuración de pages en i18n{webinars: {es: '/webinars',en: '/webinars','pt-BR': '/webinars',},}
Ejemplo: Página común (compartida entre verticales)
Section titled “Ejemplo: Página común (compartida entre verticales)”Para una página que todas las verticales comparten (login, soporte, etc.):
<script setup lang="ts">import { chooseLayoutPage } from '~/core/plugins'const layout = chooseLayoutPage({ page: 'support', commonLayout: true })</script><template> <NuxtLayout :name="layout"></NuxtLayout></template>El layout va en app/layouts/pages/support.vue (sin subcarpeta de vertical).
Resumen de la estructura
Section titled “Resumen de la estructura”pages/└── webinars/ └── index.vue → thin page (7 líneas)
layouts/└── pages/ ├── support.vue → layout COMÚN (todas las verticales) └── inspiria/ └── webinars.vue → layout ESPECÍFICO (solo Inspiria)La magia del multi-vertical: si mañana se agrega otra vertical con su propia versión de “webinars”, solo se crea layouts/pages/otra-vertical/webinars.vue — la misma página (pages/webinars/index.vue) ya funciona sin cambios.