Full-Site Template Architecture
This document explains how the template system for center websites works – from the v0 generation to integration into cockpitOS.
Concept: Smartphone with Different Cover
Website templates are layout skins over the same technical structure – like a smartphone: the same technology inside, different appearance due to the cover.
- Same functionality: Home, Shops, News, Services, Center Map, Contact – all pages
- Same data: Comes from the cockpitOS dashboard (central data source)
- Different design: Layout, colors, typography, map style – your v0 template
The template defines the cover, not the logic. Chatbot and Center Map (WayfindingMap) are global cockpitOS components – they are injected, not implemented in the template.
Architecture Overview
┌─────────────────────────────────────────────────────────────────┐
│ COCKPITOS DASHBOARD │
│ Center configuration, shops, news, events, services, etc. │
└─────────────────────────────┬───────────────────────────────────┘
│ Data (API)
▼
┌─────────────────────────────────────────────────────────────────┐
│ CENTER-WEBSITE (Next.js) │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ Layout (centerConfig, websiteTemplate) │ │
│ │ ├── Template Layout? (Header + Footer) OR Standard │ │
│ │ ├── {children} = Page Content │ │
│ │ └── Chatbot, MetaTags, Analytics (global) │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │ │
│ ┌───────────────────────────▼─────────────────────────────────┐ │
│ │ Page (e.g. /shops) │ │
│ │ getPageComponent(template, 'shops') │ │
│ │ ├── Template Shops? → pages/shops.tsx (v0) │ │
│ │ └── OR ShopsPageClient (Standard) │ │
│ └─────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
Data Flow
- Center active →
websiteTemplatefrom DB (e.g.'cev-ai-theme'ornull) - Layout: If the template has a layout →
CevLayout(or template layout) wraps{children}, otherwise standard (SiteHeader + ConditionalFooter) - Page:
getPageComponent(template, pageType)→ template component OR standard - Props: Server loads data (shops, news, etc.) and passes it to the component
Fallback: No template or template has no component for this page → standard cockpitOS component.
Page Types (PageTypes)
| PageType | Route | Template File | Standard Component |
|---|---|---|---|
| home | /{slug} | index.tsx | CenterHomepage |
| shops | /{slug}/shops | pages/shops.tsx | ShopsPageClient |
| shop-detail | /{slug}/shops/[shopSlug] | pages/shop-detail.tsx | ShopDetailClient |
| news | /{slug}/news | pages/news.tsx | NewsPageClient |
| news-detail | /{slug}/news/[id] | pages/news-detail.tsx | ContentDetailClient |
| event-detail | /{slug}/events/[id] | pages/event-detail.tsx | ContentDetailClient |
| services | /{slug}/services | pages/services.tsx | ServicesPageClient |
| service-detail | /{slug}/services/[slug] | pages/service-detail.tsx | ServiceDetailClient |
| wayfinding | /{slug}/wayfinding | pages/wayfinding.tsx | WayfindingPageClient |
| gastronomy | /{slug}/gastronomy | pages/gastronomy.tsx | GastronomyPageClient |
| restaurant-detail | /{slug}/gastronomy/[id] | pages/restaurant-detail.tsx | RestaurantDetailClient |
| offices | /{slug}/offices | pages/offices.tsx | HealthCenterPageClient |
| parking | /{slug}/parking | pages/parking.tsx | ParkingPageClient |
| opening-hours | /{slug}/opening-hours | pages/opening-hours.tsx | OpeningHoursPageClient |
| impressum | /{slug}/impressum | pages/impressum.tsx | ImpressumPageClient |
| data-protection | /{slug}/datenschutz | pages/datenschutz.tsx | DataProtectionPageClient |
| contact | /{slug}/kontakt | pages/contact.tsx | ContactPageClient |
| to-go | /{slug}/to-go/[qrCodeId] | pages/to-go.tsx | ToGoPageClient |
Registry & Integration
Templates are registered in apps/center-website/lib/template-manifest-loaders.ts per ID as dynamic import(); at runtime, load-template-manifest.ts (React cache) loads exactly one manifest per request/center. template-registry.ts (Server/RSC) builds from that getHomepageTemplate, getPageComponent, getLayoutComponent, etc. (async, each await in the pages).
Client (cookie banner, overlay, chat trigger) uses template-client-registry.ts – does not bind the server registry with CenterHomepage.
Older documentation still shows a static registry example; the manifest map + lazy load is essential.
// Layout (Header + Footer)
const TEMPLATE_LAYOUT_REGISTRY = {
'cev-ai-theme': dynamic(() => import('@/components/templates/cev-ai-theme/layout').then(m => m.CevLayout)),
};
// Per-page components
const TEMPLATE_PAGE_REGISTRY = {
'cev-ai-theme': {
shops: dynamic(() => import('@/components/templates/cev-ai-theme/pages/shops').then(m => m.default)),
news: dynamic(() => import('@/components/templates/cev-ai-theme/pages/news').then(m => m.default)),
// ... additional pages
},
};
Adding a New Template:
- v0 generates template according to Website Prompt
- Download ZIP, unpack
- Move to
apps/center-website/components/templates/[name]/ template-meta.tscreate (including optionalcustomOverlayChrome,customChatTrigger, if the template sets its own overlay or chat triggers in the manifest) and include it inmanifest.tsvia...templateMeta- Add an entry in
template-manifest-loaders.tsfortemplate-id: () => import('@/components/templates/.../manifest'); include the sametemplateMetaintemplate-metadata-registry.tsunderTEMPLATE_STATIC_METAS(same order as before) - Data binding: Adjust route – e.g.
loadShopsfor shops page,loadCenterplanfor shop-detail (Center plan with shop highlight) - Approve in the dashboard (Template Submission)
- Center: Website tab → Activation → Select Template
Metadata vs. Heavy Registry: dynamic-center-config, Theme-SSR and homepage flags use template-metadata-registry.ts. load-template-manifest + template-manifest-loaders pull only one manifest.ts per center; template-client-registry supplies cookie/overlay/chat on the client without server registry.
Integration Insights (CEV): Shops page needs shops prop (loadShops); Shop detail requires floors prop (loadCenterplan) and CenterPlanEmbed slot. Details see Website Prompt – Integration Insights.
Security for Existing Centers
- websiteTemplate = null (default) → No change, Palais Vest etc. continue to use the standard layout
- Migration additive: Only new column
websiteTemplate, no data change - Fallback: Template unknown or page not existing → standard component
Workflow for Content Creators
- Copy Prompt – Website Template Prompt (from TASK)
- Screenshots – Section-wise (Hero, Maps, Footer) for v0
- v0 – Insert screenshots + prompt, generate
- ZIP – Download, check
- Upload – Dashboard → Templates → Upload
- Integration – Developer: take over into the repo, enter in registry, approve
Related Documentation
- Website Template Prompt – Complete v0 prompt
- Creating Website Templates – Step-by-step guide
- Upload Template – Upload workflow
- Technical Architecture:
docs/FULL-SITE-TEMPLATE-ARCHITECTURE.mdin project root (Codebase)
Nutzungsstatistik: Seitenaufrufe werden anonymisiert erfasst. Im Umami-Dashboard nach diesem Pfad filtern: /en/templates/full-site-template-architektur