---
name: create-umbraco-lp
description: Guia a criação de uma landing page nova ponta-a-ponta a partir do template MORPH_Umbraco_LP_Template (Umbraco 17 + Next.js): coleta decisões, scaffolda, sobe e valida, customiza branding e módulos, e entrega checklist final.
version: core
when-to-invoke: ao iniciar um novo projeto de LP com o template Umbraco genérico
user-invokable: true
cliVersion: "8.37.1"
stacks:
  - umbraco
---

# create-umbraco-lp

## Objetivo

Conduzir um agente (ou dev) na criação de uma nova landing page usando o template canônico `MORPH_Umbraco_LP_Template` (repo privado, branch `master`). Cobre do clone ao smoke-test, passando por branding, módulos de conteúdo e configuração de integração de lead.

## Quando invocar

Ao iniciar um projeto de LP do zero — quando o usuário disser "criar uma LP", "novo projeto Umbraco", "bootstrapar LP", "nova landing page", ou mencionar `/create-umbraco-lp`. Não invocar se o projeto já existe (repositório com `.env` presente); nesse caso, ir direto ao passo de customização ou ao troubleshoot.

## O que fazer

### Passo 1 — Coletar decisões (uma pergunta por vez via AskUserQuestion)

Faça cada pergunta separadamente e aguarde a resposta antes de seguir. Não liste todas de uma vez.

**Pergunta 1:** Nome do projeto (ex.: "Reserva das Flores").

**Pergunta 2:** Integração de lead. Use `AskUserQuestion` com as opções:
- `none` — sem integração (form recebe lead mas não envia a lugar nenhum; útil para fase de dev).
- `ghl` — GoHighLevel (requer API token GHL; configurado depois no backoffice).
- `prospectpro` — ProspectPRO (requer URL de API + token; configurado depois no backoffice).
- `webhook` — Webhook genérico (requer URL do webhook; configurado depois no backoffice).

**Pergunta 3:** Origins permitidas pelo CORS do backend — o domínio real do frontend em produção, separado por vírgula. Em dev local, `http://localhost:3000` já é o padrão gravado pelo script; pergunte apenas se houver domínio de produção a configurar agora.

**Pergunta 4 (opcional):** Seed de demo? Se sim, o boot semeará um Hero e um módulo de Contato de exemplo, facilitando a visualização imediata. Recomendado para projetos novos. Use `AskUserQuestion` com opções Sim / Não.

Persista as respostas em variáveis para uso nos passos seguintes.

---

### Passo 2 — Scaffold: clonar template e rodar `new-lp`

**2.1 — Clonar o template para o diretório do novo projeto:**

O repo do template é **privado** — prefira `gh repo clone` (usa o token já autenticado). Comando idêntico em bash e PowerShell:

```bash
# Clonar o template canônico (privado) para o diretório do novo projeto.
# Branch padrão master (já generalizado) — sem checkout de branch.
gh repo clone polymorphism-tech/MORPH_Umbraco_LP_Template <NomeDoProjeto>
cd <NomeDoProjeto>
# (opcional) desacoplar do histórico do template para o projeto ter o seu:
#   rm -rf .git && git init    (PowerShell: Remove-Item .git -Recurse -Force; git init)
```

- **Fallback** se `gh` não estiver disponível: `git clone https://github.com/polymorphism-tech/MORPH_Umbraco_LP_Template.git <NomeDoProjeto>` (ou SSH `git@github.com:polymorphism-tech/MORPH_Umbraco_LP_Template.git`) — exige credencial HTTPS ou chave SSH com acesso à org.
- Se o clone for por cópia local (sibling repo), substitua por `cp -r` ou `xcopy` preservando o conteúdo de `backend/uSync/` e `scripts/`.

**2.2 — Executar o script de bootstrap:**

No Windows (PowerShell):
```powershell
.\scripts\new-lp.ps1 -ProjectName "<NomeDoProjeto>" -LeadIntegration "<none|ghl|prospectpro|webhook>" -ResetDb -EnableDemoSeed
```

`-ResetDb` deve ser passado sempre no primeiro boot de um projeto novo; `-EnableDemoSeed` apenas se demo seed foi escolhido no passo 1.

No Linux / macOS (bash):
```bash
./scripts/new-lp.sh "<NomeDoProjeto>" \
  --lead-integration <none|ghl|prospectpro|webhook> \
  --reset-db \
  --demo-seed   # se demo seed foi escolhido
```

O script escreve `.env` na raiz do repo com os campos:
```
PROJECT_NAME=<NomeDoProjeto>
UMBRACO_ADMIN_EMAIL=admin@<slug>.com
UMBRACO_ADMIN_PASSWORD=<gerada>
UMBRACO_GLOBAL_ID=<uuid-gerado>
ALLOWED_ORIGINS=http://localhost:3000
REVALIDATE_SECRET=<gerado>
FRONTEND_URL=http://localhost:3000
LEADS_INTEGRATION=<integração-escolhida>
```

`FRONTEND_URL` (junto com `REVALIDATE_SECRET`) alimenta o `RevalidateOnPublishHandler` do
backend: a cada publish no Umbraco, ele chama `/api/revalidate` do frontend sozinho, em
código — não depende de configurar nada em Settings > Webhooks no backoffice. **Em
produção, atualize `FRONTEND_URL` para o domínio público real do frontend.**

E gera `backend/appsettings.Development.json` com o bloco Unattended install do Umbraco (usando o email/senha computados pelo script). Isso significa que um projeto recém-scaffoldado **auto-instala no `dotnet run`** — sem necessidade de rodar o instalador UI manualmente.

E escreve `frontend/.env.local` com:
```
NEXT_PUBLIC_UMBRACO_URL=https://localhost:44324
NEXT_PUBLIC_SITE_URL=http://localhost:3000
REVALIDATE_SECRET=<mesmo-secret>
```

`NEXT_PUBLIC_SITE_URL` é usada por `robots.ts`/`sitemap.ts`. **Em produção, precisa ser
setada como Build Variable no Coolify** — o `Dockerfile` do template
declara `ARG NEXT_PUBLIC_SITE_URL` (e `ARG NEXT_PUBLIC_UMBRACO_URL`) justamente para isso;
sem marcar como Build Variable, a env var não chega ao `next build` e o `robots.txt`/
`sitemap.xml` publicam o domínio placeholder do template (`minha-landing-page.com.br`) em
produção — já aconteceu num projeto real.

> **Nota de overwrite:** se `.env` já existe, `new-lp.ps1` prompta (Read-Host) para sobrescrever — portanto, uma execução não-interativa em um diretório que já tem `.env` vai bloquear na prompt. Execute em um scaffold fresco, ou responda à prompt interativamente.

> **Nota importante:** `ALLOWED_ORIGINS` é gravado com `http://localhost:3000` pelo script independentemente do que foi informado no passo 1. Se o usuário especificou um domínio de produção, a forma de aplicá-lo depende do modo de execução:
> - **Docker Compose:** edite `.env` na raiz: `ALLOWED_ORIGINS=https://meuprojeto.com,http://localhost:3000`. O `docker-compose.yml` injeta essa variável e ela é lida pelo `builder.Configuration` em `Program.cs`.
> - **`dotnet run` local:** o `.env` **não** é lido automaticamente pelo `dotnet run`. Edite `backend/appsettings.json`, campo `AllowedOrigins` (array de strings), ou exporte a variável no shell antes de rodar (`$env:AllowedOrigins="https://meuprojeto.com,http://localhost:3000"` no PowerShell). O `Program.cs` lê `builder.Configuration.GetSection("AllowedOrigins")`, que é populado via `appsettings.json` ou env var do processo.

> `LEADS_INTEGRATION` NÃO está no `.env.example` — é uma variável extra gravada pelo script na seção `LEADS_INTEGRATION=`. O seed a lê via `Environment.GetEnvironmentVariable("LEADS_INTEGRATION")` para criar o node `Configurações de Lead` com o valor correto.

Guarde as credenciais exibidas na saída do script (`Admin:`, `Senha:`, `GUID:`).

---

### Passo 3 — Boot + verificar

**3.1 — Subir o backend (Umbraco 17 / .NET 10):**

```bash
cd backend
dotnet run
# ou, com IIS Express: perfil "Umbraco.Web.UI"
```

Portas padrão (dev local):
- HTTP: `http://localhost:19223`
- HTTPS: `https://localhost:44324` (IIS Express)

O banco de dados é SQLite em `backend/umbraco/Data/Umbraco.sqlite.db`. O flag `-ResetDb` / `--reset-db` apaga `Umbraco.sqlite.db*` e `TEMP/ExamineIndexes` antes do boot — use sempre no primeiro boot de um projeto novo para garantir um estado limpo.

> **⚠️ Primeiro `dotnet run` leva vários minutos:** cold compile com NuGet restore + build. Não é um hang — seja paciente.

**3.2 — Aguardar o uSync e o seed (após compilar):**

Na primeira subida, o Umbraco executa a instalação unattended (via `backend/appsettings.Development.json` configurado pelo script), depois o uSync importa todos os Content Types de `backend/uSync/v17/` (`ImportAtStartup: "All"`), e então o `SeedContentHandler` cria:
- O node raiz `lpHome` (home da LP, com `PROJECT_NAME` como título).
- O node `Configurações de Lead` (`leadsConfig`, com `LEADS_INTEGRATION` semeado como `["<integração>"]`).

Aguarde as seguintes linhas de log (strings exatas do `SeedContentHandler.cs`) antes de prosseguir:
- Home: `SeedContent: node created and published successfully.`
- Leads: `SeedContent: leads config node created and published.`

(Se os nodes já existiam de um boot anterior: `SeedContent: home already exists (...), skipping.` e `SeedContent: leads config node already exists, skipping.` — também é OK.)

**3.3 — Verificar a Delivery API:**

```bash
curl "http://localhost:19223/umbraco/delivery/api/v2/content?contentType=lpHome&take=1"
# Deve retornar { total: 1, items: [...] }
```

**3.4 — Verificar o endpoint Ping de leads:**

```bash
curl http://localhost:19223/api/leads
# Deve retornar { "ok": true, "service": "leads" } (prod)
# Em Development: inclui "integration", "configFound", "ready"
```

Se `configFound: false`, o node `leadsConfig` não foi criado pelo seed — verifique os logs e o passo 2.2.

**3.5 — Subir o frontend (Next.js):**

```bash
cd frontend
npm install
npm run dev        # com TLS
# ou:
npm run dev:no-tls # sem TLS (mais simples em dev inicial)
```

Acesse `http://localhost:3000`. A home deve renderizar com o conteúdo semeado.

---

### Passo 4 — Customizar

**4.1 — Branding e cores (frontend):**

Edite `frontend/src/app/globals.css`. As variáveis CSS de base do tema são definidas no bloco `:root` e expostas via `@theme inline` para o Tailwind v4:

```css
/* frontend/src/app/globals.css */
:root {
  --background: #ffffff;       /* troque pela cor de fundo da LP */
  --foreground: #171717;       /* troque pela cor de texto principal */
  /* adicione variáveis de cores de marca, tipografia, etc. */
}
```

Para tipografias customizadas, importe via `next/font` em `frontend/src/app/layout.tsx` e exponha como variável CSS.

> **Se a LP nasce cinematográfica (scroll-driven):** o template não traz `gsap` nem `lenis`; adicione ao
> `frontend/package.json` e monte o runtime de motion como componente `'use client'` montado uma vez na
> page, com guard de SSR no registro do ScrollTrigger. O conhecimento está nos standards
> `frontend/scroll-driven/smooth-scroll.md` (wiring Lenis com o ticker do GSAP, `window.__lenis`,
> armadilha de HMR), `frame-scrub.md` (canvas quadro a quadro, extração ffmpeg, poster no mobile) e
> `scroll-components.md` (pin, modal, parallax, reveal). Regra de assets que vale desde o primeiro dia:
> **sequência de frames é asset estrutural em `frontend/public/` com cache-bust `?v=N` manual, e só o
> poster vira mídia editável no backoffice.** Frames na Media Library perdem o webp (o ImageSharp do
> Umbraco 17 exige URL assinada por HMAC, não relaxável por config) e viram 145 requests assinadas
> por visitante.

**4.2 — Módulos disponíveis (Content Types *Section):**

O template já inclui os seguintes módulos versionados em `backend/uSync/v17/ContentTypes/`:

| Alias (Content Type) | Propósito |
|---|---|
| `heroSection` | Hero principal com título, subtítulo, CTA e imagem |
| `contatoSection` | Formulário de contato / captação de lead |
| `diferencialSection` | Lista de diferenciais (cada item: `diferencialItem`) |
| `galeriaSection` | Galeria de imagens |
| `videoSection` | Embed de vídeo |
| `testimonialSection` | Seção de depoimentos (cada item: `testimonialItem`) |
| `equipeSection` | Seção de equipe (cada membro: `equipeMembro`) |
| `tipologiasSection` | Tipologias/plantas (cada tipo: `tipologiaTipo`) |
| `certificacoesSection` | Certificações (cada item: `certificacaoItem`) |
| `localizacaoSection` | Seção de localização |
| `customHtmlSection` | HTML livre (escape hatch) |

Para adicionar ou reordenar módulos: acesse o backoffice em `https://localhost:44324/umbraco`, faça login com as credenciais geradas, abra o node `lpHome` e adicione os módulos desejados.

**4.3 — Configurar integração de lead no backoffice:**

Acesse o backoffice → Content → `Configurações de Lead`. Preencha os campos conforme a integração escolhida:

- **GHL:** preencha obrigatoriamente `ghlApiToken` (Private Integration Token da sub-conta) **e** `ghlLocationId` (ID da sub-conta/location). Sem `ghlLocationId`, os leads são enviados ao GHL mas vão para a conta errada ou falham silenciosamente. Campos opcionais de mapeamento: `ghlFieldFonte`, `ghlFieldCanal`, `ghlFieldSegmento`, `ghlFieldConsent`, `ghlFieldObs`, `ghlTags`.
- **ProspectPRO:** `ppApiUrl` + `ppApiToken`.
- **Webhook genérico:** `whUrl` (URL do webhook; o payload é o lead completo em JSON).

> Esses tokens **nunca são semeados pelo script** (por design: "Template genérico — NÃO semear tokens/ids"). O node é criado com campos em branco para o editor preencher. Tokens não aparecem na Delivery API pública (`leadsConfig` está em `DisallowedContentTypeAliases`).

> **Dropdown Flexível — gotcha:** o campo `leadIntegration` é um `Umbraco.DropDown.Flexible`. Mesmo com `Multiple: false`, o valor é persistido internamente como JSON array (ex.: `["ghl"]`). O seed já escreve no formato correto via `JsonSerializer.Serialize(new[]{ value })`. Ao ler no controller, `node.Value<string>("leadIntegration")` desembrulha para a string simples.

---

### Passo 5 — Checklist de entrega

Antes de fazer deploy ou entregar o projeto ao cliente:

- [ ] `dotnet build` sem warnings no backend.
- [ ] `npm run lint` no frontend sem erros (ESLint via `eslint.config.mjs`).
- [ ] `npm run build` no frontend (requer backend acessível em `NEXT_PUBLIC_UMBRACO_URL` — ou `E2E_FAKE_DATA=true` para CI sem backend).
- [ ] `GET /api/leads` retorna `{ ok: true, service: "leads" }`.
- [ ] Formulário de lead submete e a resposta é `{ ok: true }` (ou `{ ok: true, skipped: true }` se tokens ainda não configurados — comportamento esperado em dev).
- [ ] Delivery API responde com o node `lpHome` populado: `/umbraco/delivery/api/v2/content?contentType=lpHome&take=1`.
- [ ] `.env` contém `ALLOWED_ORIGINS` com o domínio de produção correto (separado por vírgula se múltiplos).
- [ ] `UMBRACO_GLOBAL_ID` é único para este projeto (gerado pelo script — não reutilizar de outro projeto).
- [ ] SEO: preencher no backoffice, aba SEO do node `lpHome` — Page Title, Meta Title, Meta Description, **Social Image** (1200x630, sem ela não há `og:image` e o link não gera preview no WhatsApp/redes), Canonical Url (só a URL, sem tag HTML).
- [ ] Favicon: preencher o campo **Favicon** no backoffice (aba Configuration) — `app/icon.tsx`/`apple-icon.tsx` já leem esse campo dinamicamente; sem ele, cai no favicon estático genérico do template.
- [ ] `NEXT_PUBLIC_SITE_URL` **e** `NEXT_PUBLIC_UMBRACO_URL` setadas como **Build Variable** no provedor (não só runtime) — conferir `robots.txt`/`sitemap.xml` em produção com o domínio real após o primeiro deploy.
- [ ] `FRONTEND_URL` + `REVALIDATE_SECRET` setadas no backend em produção (revalidação automática pós-publish via `RevalidateOnPublishHandler`) — testar: publicar algo no backoffice e conferir no log do backend a linha `RevalidateOnPublish: cache do frontend invalidado após publish.`
- [ ] No Index: confirmar que está **desligado** no backoffice antes de ir pra produção (o campo é opt-in pra esconder do Google, não deveria vir ligado por padrão).
- [ ] Variáveis sensíveis (`UMBRACO_ADMIN_PASSWORD`, `REVALIDATE_SECRET`, tokens de integração) não estão commitadas no repositório (`.env` deve estar no `.gitignore`).
- [ ] Em produção com Docker Compose: `docker-compose up --build` e confirmar que backend e frontend sobem com as variáveis do `.env`.
- [ ] `browserslist` presente em `frontend/package.json` — já vem do template; só confirmar que não foi removido/sobrescrito durante a customização.
- [ ] Se o cliente vai subir fotos de câmera/celular pelo Media Picker (galeria, hero, etc.): confirmar que `ResizeLargeMediaUploadsHandler` está registrado em `backend/Program.cs` (já vem do template) — sem ele, upload cru de 10+ MB pode virar 500 intermitente sob concorrência no otimizador do Next. Setar `MEDIA_BACKFILL_SECRET` em produção se for preciso reprocessar mídia já publicada (ver seção "Mídia" do README do template).

> **Nota Docker — `LEADS_INTEGRATION` e `SEED_DEMO_CONTENT`:** o `docker-compose.yml` **não** injeta essas duas variáveis no container do backend. Mesmo que estejam no `.env`, o seed sempre inicializa `leadIntegration` como `"none"` no boot Docker. Isso é esperado: o editor deve configurar a integração manualmente no backoffice após o primeiro boot. Se precisar seeding automático da integração via Docker, adicione a variável na seção `environment` do serviço `backend` em `docker-compose.yml` manualmente.

## Output esperado

- Repositório do projeto com `.env` e `frontend/.env.local` configurados.
- Backend Umbraco subindo com node `lpHome` e node `Configurações de Lead` semeados.
- Frontend Next.js renderizando a home com conteúdo da Delivery API.
- `GET /api/leads` respondendo `{ ok: true, service: "leads" }`.
- Backoffice acessível para o editor adicionar módulos e configurar tokens de integração.

## Referências

- `polymorphism-tech/MORPH_Umbraco_LP_Template` (privado, branch `master`) — template canônico: `scripts/new-lp.ps1`, `scripts/new-lp.sh`, `backend/Infrastructure/SeedContentHandler.cs`, `backend/Controllers/LeadController.cs`, `backend/uSync/v17/`.
- `references/umbraco-lp-patterns.md` (carregue sob demanda) — padrões e gotchas detalhados: controller público (seção 1), DI multi-sink (seção 2), leitura server-side de config oculta (seção 3), segurança Delivery API (seção 4), uSync config-as-code (seção 5), dropdown gotcha (seção 6), seed idempotente (seção 7), endpoint Ping (seção 10), skip silencioso em dev (seção 11), build Next.js exige backend (seção 12).
- `backend/appsettings.json` — `Umbraco:CMS:DeliveryApi:DisallowedContentTypeAliases` e `uSync:Settings:ImportAtStartup`.
- `backend/Properties/launchSettings.json` — portas de dev: HTTP `:19223`, HTTPS `:44324`.
- `docker-compose.yml` — orquestração de produção (backend porta `5001`, frontend `3000`).
