@unbox-plus/cli 0.20.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +46 -0
- package/README.md +175 -0
- package/bin/cli.js +439 -0
- package/package.json +43 -0
- package/src/colors.js +100 -0
- package/src/presets.js +241 -0
- package/src/theme.js +253 -0
- package/template/.claude/agents/assets-marca.md +40 -0
- package/template/.claude/agents/avaliador-visual.md +48 -0
- package/template/.claude/agents/branding-briefing.md +401 -0
- package/template/.claude/agents/conteudo-secao.md +46 -0
- package/template/.claude/agents/paginas-legais.md +44 -0
- package/template/.claude/settings.json +3 -0
- package/template/.claude/skills/frontend-design/ATTRIBUTION.md +3 -0
- package/template/.claude/skills/frontend-design/LICENSE.txt +177 -0
- package/template/.claude/skills/frontend-design/SKILL.md +55 -0
- package/template/.claude/skills/web-design-guidelines/ATTRIBUTION.md +3 -0
- package/template/.claude/skills/web-design-guidelines/SKILL.md +50 -0
- package/template/.claude/skills/web-design-guidelines/guidelines-snapshot.md +180 -0
- package/template/.env.example +158 -0
- package/template/.mcp.json.example +14 -0
- package/template/.nvmrc +1 -0
- package/template/CLAUDE.md +333 -0
- package/template/COVERAGE.md +80 -0
- package/template/DEPLOY.md +179 -0
- package/template/QA.md +108 -0
- package/template/README.md +152 -0
- package/template/agents/CONSTRUCAO.md +207 -0
- package/template/agents/MANAGER.md +192 -0
- package/template/agents/PADROES.md +255 -0
- package/template/agents/definitions/00-scaffold.md +56 -0
- package/template/agents/definitions/01-layout.md +47 -0
- package/template/agents/definitions/02-homepage.md +70 -0
- package/template/agents/definitions/03-catalog.md +86 -0
- package/template/agents/definitions/04-pdp.md +97 -0
- package/template/agents/definitions/05-cart.md +116 -0
- package/template/agents/definitions/06-checkout.md +146 -0
- package/template/agents/definitions/07-auth.md +130 -0
- package/template/agents/definitions/08-customer.md +55 -0
- package/template/agents/definitions/09-promotions.md +62 -0
- package/template/agents/definitions/10-feedback.md +128 -0
- package/template/agents/definitions/11-seo-infra.md +159 -0
- package/template/agents/definitions/12-deploy.md +173 -0
- package/template/agents/definitions/13-cro.md +275 -0
- package/template/agents/definitions/14-seo.md +388 -0
- package/template/agents/definitions/15-branding.md +26 -0
- package/template/agents/definitions/16-qa-visual.md +138 -0
- package/template/agents/definitions/17-aeo.md +257 -0
- package/template/app/(loja)/busca/page.tsx +72 -0
- package/template/app/(loja)/carrinho/oferta/page.tsx +57 -0
- package/template/app/(loja)/carrinho/page.tsx +160 -0
- package/template/app/(loja)/categoria/[tagSlug]/layout.tsx +31 -0
- package/template/app/(loja)/categoria/[tagSlug]/loading.tsx +17 -0
- package/template/app/(loja)/categoria/[tagSlug]/page.tsx +49 -0
- package/template/app/(loja)/checkout/page.tsx +80 -0
- package/template/app/(loja)/checkout/pix/[ref]/page.tsx +133 -0
- package/template/app/(loja)/conta/assinaturas/[referenceId]/page.tsx +112 -0
- package/template/app/(loja)/conta/assinaturas/page.tsx +51 -0
- package/template/app/(loja)/conta/enderecos/page.tsx +20 -0
- package/template/app/(loja)/conta/entrar/page.tsx +119 -0
- package/template/app/(loja)/conta/page.tsx +45 -0
- package/template/app/(loja)/conta/pedidos/[referenceId]/page.tsx +49 -0
- package/template/app/(loja)/conta/pedidos/page.tsx +104 -0
- package/template/app/(loja)/conta/preferencias/page.tsx +24 -0
- package/template/app/(loja)/devolucoes/page.tsx +36 -0
- package/template/app/(loja)/layout.tsx +22 -0
- package/template/app/(loja)/oferta/page.tsx +57 -0
- package/template/app/(loja)/page.tsx +86 -0
- package/template/app/(loja)/pedido/[referenceId]/page.tsx +78 -0
- package/template/app/(loja)/privacidade/page.tsx +113 -0
- package/template/app/(loja)/produto/[productSlug]/layout.tsx +23 -0
- package/template/app/(loja)/produto/[productSlug]/loading.tsx +20 -0
- package/template/app/(loja)/produto/[productSlug]/page.tsx +372 -0
- package/template/app/(loja)/produtos/loading.tsx +16 -0
- package/template/app/(loja)/produtos/page.tsx +33 -0
- package/template/app/(loja)/termos/page.tsx +104 -0
- package/template/app/acesso/page.tsx +129 -0
- package/template/app/api/account/addresses/route.ts +39 -0
- package/template/app/api/account/exists/route.ts +21 -0
- package/template/app/api/account/me/route.ts +25 -0
- package/template/app/api/account/otp/route.ts +26 -0
- package/template/app/api/account/preferences/route.ts +19 -0
- package/template/app/api/account/signin/route.ts +27 -0
- package/template/app/api/account/signout/route.ts +9 -0
- package/template/app/api/acesso/route.ts +171 -0
- package/template/app/api/capi/route.ts +34 -0
- package/template/app/api/cart/coupon/route.ts +49 -0
- package/template/app/api/cart/items/route.ts +45 -0
- package/template/app/api/cart/link/route.ts +69 -0
- package/template/app/api/cart/route.ts +113 -0
- package/template/app/api/cart/share/route.ts +21 -0
- package/template/app/api/cep/[code]/route.ts +18 -0
- package/template/app/api/checkout/address/route.ts +24 -0
- package/template/app/api/checkout/email/route.ts +41 -0
- package/template/app/api/checkout/installments/route.ts +16 -0
- package/template/app/api/checkout/route.ts +110 -0
- package/template/app/api/checkout/shipping/route.ts +65 -0
- package/template/app/api/checkout-destination/route.ts +31 -0
- package/template/app/api/order/[ref]/route.ts +17 -0
- package/template/app/api/payment-link/route.ts +28 -0
- package/template/app/api/revalidate/route.ts +52 -0
- package/template/app/api/shipping/quote/route.ts +29 -0
- package/template/app/api/subscriptions/[id]/route.ts +52 -0
- package/template/app/api/track/route.ts +28 -0
- package/template/app/api/unbox/catalogo/route.ts +136 -0
- package/template/app/api/unbox/paginas/route.ts +167 -0
- package/template/app/api/unbox/vitrine/route.ts +84 -0
- package/template/app/api/webhooks/unbox/route.ts +97 -0
- package/template/app/apple-icon.svg +5 -0
- package/template/app/error.tsx +15 -0
- package/template/app/globals.css +457 -0
- package/template/app/icon.svg +4 -0
- package/template/app/layout.tsx +90 -0
- package/template/app/llms.txt/route.ts +43 -0
- package/template/app/manifest.ts +21 -0
- package/template/app/not-found.tsx +18 -0
- package/template/app/opengraph-image.tsx +35 -0
- package/template/app/robots.ts +31 -0
- package/template/app/sitemap.ts +42 -0
- package/template/bootstrap.sh +87 -0
- package/template/components/account/account-shell.tsx +47 -0
- package/template/components/account/address-book.tsx +147 -0
- package/template/components/account/preferences-form.tsx +77 -0
- package/template/components/account/reorder-button.tsx +47 -0
- package/template/components/account/signout-button.tsx +20 -0
- package/template/components/account/subscription-actions.tsx +204 -0
- package/template/components/account-nav.tsx +41 -0
- package/template/components/address-fields.tsx +102 -0
- package/template/components/analytics/data-layer-ready.tsx +16 -0
- package/template/components/analytics/purchase-tracker.tsx +32 -0
- package/template/components/brand-search.tsx +34 -0
- package/template/components/cart/cart-provider.tsx +340 -0
- package/template/components/cart/mini-cart.tsx +228 -0
- package/template/components/cart-button.tsx +35 -0
- package/template/components/catalog/catalog-client.tsx +523 -0
- package/template/components/catalog/grid-skeleton.tsx +19 -0
- package/template/components/catalog/pager.tsx +66 -0
- package/template/components/catalog/product-grid-card.tsx +52 -0
- package/template/components/catalog/product-grid.tsx +27 -0
- package/template/components/checkout/checkout-client.tsx +1333 -0
- package/template/components/chrome/announce-bar.tsx +57 -0
- package/template/components/chrome/chrome-recipe.ts +13 -0
- package/template/components/chrome/footers/captura-botao.tsx +28 -0
- package/template/components/chrome/footers/colunas.tsx +110 -0
- package/template/components/chrome/footers/conversao.tsx +90 -0
- package/template/components/chrome/footers/editorial.tsx +98 -0
- package/template/components/chrome/footers/minimal.tsx +55 -0
- package/template/components/chrome/header-bar-mobile.tsx +64 -0
- package/template/components/chrome/headers/centralizado.tsx +71 -0
- package/template/components/chrome/headers/classico.tsx +64 -0
- package/template/components/chrome/headers/compacto.tsx +66 -0
- package/template/components/chrome/headers/equilibrado.tsx +73 -0
- package/template/components/chrome/headers/imersivo.tsx +62 -0
- package/template/components/chrome/registry.ts +70 -0
- package/template/components/chrome/solid-on-scroll.tsx +39 -0
- package/template/components/empty-state.tsx +22 -0
- package/template/components/home/combo-card-compact.tsx +67 -0
- package/template/components/home/combos-home.tsx +64 -0
- package/template/components/home/combos-section.tsx +177 -0
- package/template/components/home/home-recipe.ts +41 -0
- package/template/components/home/sections/attributes-marquee.tsx +178 -0
- package/template/components/home/sections/benefits.tsx +80 -0
- package/template/components/home/sections/bloco-html.tsx +47 -0
- package/template/components/home/sections/catalogo.ts +165 -0
- package/template/components/home/sections/category-pills.tsx +32 -0
- package/template/components/home/sections/combos-carousel.tsx +141 -0
- package/template/components/home/sections/comparison.tsx +73 -0
- package/template/components/home/sections/founder-story.tsx +40 -0
- package/template/components/home/sections/hero.tsx +187 -0
- package/template/components/home/sections/kits.tsx +11 -0
- package/template/components/home/sections/media-cards.tsx +70 -0
- package/template/components/home/sections/newsletter.tsx +62 -0
- package/template/components/home/sections/product-showcase.tsx +175 -0
- package/template/components/home/sections/purchase-hero.tsx +187 -0
- package/template/components/home/sections/quote-banner.tsx +50 -0
- package/template/components/home/sections/registry.ts +186 -0
- package/template/components/home/sections/reviews-carousel.tsx +66 -0
- package/template/components/home/sections/reviews.tsx +68 -0
- package/template/components/home/sections/ritual.tsx +96 -0
- package/template/components/home/sections/savings.tsx +47 -0
- package/template/components/home/sections/social-row.tsx +65 -0
- package/template/components/home/sections/spec-table.tsx +117 -0
- package/template/components/home/sections/stars.tsx +12 -0
- package/template/components/home/sections/stats-grid.tsx +54 -0
- package/template/components/home/sections/trust-bar.tsx +56 -0
- package/template/components/home/sections/trust-strip.tsx +40 -0
- package/template/components/home/sections/video-wall.tsx +59 -0
- package/template/components/landing/landing-recipe.ts +20 -0
- package/template/components/landing/oferta-sections.tsx +40 -0
- package/template/components/landing/product-picker.tsx +257 -0
- package/template/components/mobile-nav.tsx +71 -0
- package/template/components/order-status.tsx +163 -0
- package/template/components/powered-by-unbox.tsx +27 -0
- package/template/components/product/pdp/buy-box.tsx +604 -0
- package/template/components/product/pdp/catalog-grid.tsx +113 -0
- package/template/components/product/pdp/faq-modelo.ts +86 -0
- package/template/components/product/pdp/gallery.tsx +87 -0
- package/template/components/product/pdp/interactive.tsx +200 -0
- package/template/components/product/pdp/newsletter.tsx +64 -0
- package/template/components/product/pdp/payment-chips.tsx +22 -0
- package/template/components/product/pdp/pdp-view.tsx +227 -0
- package/template/components/product/pdp/recommendations.tsx +144 -0
- package/template/components/product/pdp/sections.tsx +173 -0
- package/template/components/quantity-stepper.tsx +36 -0
- package/template/components/search-box.tsx +37 -0
- package/template/components/site-footer.tsx +59 -0
- package/template/components/site-header.tsx +64 -0
- package/template/components/ui/accordion.tsx +72 -0
- package/template/components/ui/alert-dialog.tsx +187 -0
- package/template/components/ui/alert.tsx +76 -0
- package/template/components/ui/aspect-ratio.tsx +22 -0
- package/template/components/ui/avatar.tsx +109 -0
- package/template/components/ui/badge.tsx +52 -0
- package/template/components/ui/breadcrumb.tsx +125 -0
- package/template/components/ui/button.tsx +58 -0
- package/template/components/ui/card.tsx +103 -0
- package/template/components/ui/carousel.tsx +242 -0
- package/template/components/ui/command.tsx +196 -0
- package/template/components/ui/dialog.tsx +160 -0
- package/template/components/ui/dropdown-menu.tsx +268 -0
- package/template/components/ui/foto.tsx +54 -0
- package/template/components/ui/input-group.tsx +158 -0
- package/template/components/ui/input-otp.tsx +87 -0
- package/template/components/ui/input.tsx +20 -0
- package/template/components/ui/label.tsx +20 -0
- package/template/components/ui/navigation-menu.tsx +168 -0
- package/template/components/ui/pagination.tsx +132 -0
- package/template/components/ui/popover.tsx +90 -0
- package/template/components/ui/progress.tsx +83 -0
- package/template/components/ui/radio-group.tsx +38 -0
- package/template/components/ui/scroll-area.tsx +55 -0
- package/template/components/ui/select.tsx +201 -0
- package/template/components/ui/separator.tsx +25 -0
- package/template/components/ui/sheet.tsx +138 -0
- package/template/components/ui/skeleton.tsx +13 -0
- package/template/components/ui/sonner.tsx +44 -0
- package/template/components/ui/switch.tsx +32 -0
- package/template/components/ui/table.tsx +116 -0
- package/template/components/ui/tabs.tsx +82 -0
- package/template/components/ui/textarea.tsx +18 -0
- package/template/components/ui/toggle-group.tsx +89 -0
- package/template/components/ui/toggle.tsx +45 -0
- package/template/components/ui/tooltip.tsx +66 -0
- package/template/components.json +25 -0
- package/template/eslint.config.mjs +36 -0
- package/template/gitignore +21 -0
- package/template/lib/analytics.ts +369 -0
- package/template/lib/api.ts +60 -0
- package/template/lib/capi.ts +87 -0
- package/template/lib/cart-link.ts +51 -0
- package/template/lib/cart-normalize.ts +142 -0
- package/template/lib/cart-recovery.ts +23 -0
- package/template/lib/cart-response.ts +48 -0
- package/template/lib/catalog-map.ts +54 -0
- package/template/lib/catalog.ts +3 -0
- package/template/lib/checkout-lock.ts +21 -0
- package/template/lib/checkout-nav.ts +22 -0
- package/template/lib/config.ts +51 -0
- package/template/lib/crm.ts +40 -0
- package/template/lib/customer-session.ts +21 -0
- package/template/lib/dataloader.ts +23 -0
- package/template/lib/editable/config.ts +11 -0
- package/template/lib/editable/document.ts +1762 -0
- package/template/lib/editable/index.ts +3 -0
- package/template/lib/editable/primitives.tsx +821 -0
- package/template/lib/editable/provider.tsx +861 -0
- package/template/lib/editable/rastreio-navegacao.tsx +65 -0
- package/template/lib/editable/rastreio.tsx +168 -0
- package/template/lib/editable/server.ts +189 -0
- package/template/lib/editable/tokens.ts +22 -0
- package/template/lib/editable/verify.ts +24 -0
- package/template/lib/enrichment/combos.ts +184 -0
- package/template/lib/enrichment/index.ts +188 -0
- package/template/lib/enrichment/products.json +1 -0
- package/template/lib/env-check.ts +47 -0
- package/template/lib/format.ts +130 -0
- package/template/lib/icons.ts +67 -0
- package/template/lib/json-ld.ts +9 -0
- package/template/lib/llms-txt.ts +75 -0
- package/template/lib/mockup.ts +24 -0
- package/template/lib/newsletter.ts +12 -0
- package/template/lib/orders.ts +160 -0
- package/template/lib/queries.ts +66 -0
- package/template/lib/ratelimit.ts +47 -0
- package/template/lib/rotas-editaveis.ts +192 -0
- package/template/lib/sanitize.ts +36 -0
- package/template/lib/schemas.ts +78 -0
- package/template/lib/session.ts +98 -0
- package/template/lib/store-config.ts +67 -0
- package/template/lib/unbox/client.ts +829 -0
- package/template/lib/unbox/customer.ts +224 -0
- package/template/lib/unbox/errors.ts +97 -0
- package/template/lib/unbox/index.ts +10 -0
- package/template/lib/unbox/store.ts +113 -0
- package/template/lib/unbox/types.ts +195 -0
- package/template/lib/unbox/webhooks.ts +66 -0
- package/template/lib/utils.ts +6 -0
- package/template/lib/vitrine.ts +242 -0
- package/template/lib/webhook-store.ts +22 -0
- package/template/middleware.ts +178 -0
- package/template/next.config.ts +63 -0
- package/template/package.json +62 -0
- package/template/postcss.config.mjs +5 -0
- package/template/public/brand/coll/default.png +0 -0
- package/template/public/brand/hero-desktop.svg +12 -0
- package/template/public/brand/hero-mobile.svg +12 -0
- package/template/public/brand/heros/boutique-desktop.svg +11 -0
- package/template/public/brand/heros/boutique-mobile.svg +11 -0
- package/template/public/brand/heros/editorial-desktop.svg +11 -0
- package/template/public/brand/heros/editorial-mobile.svg +11 -0
- package/template/public/brand/heros/essencial-desktop.svg +12 -0
- package/template/public/brand/heros/essencial-mobile.svg +12 -0
- package/template/public/brand/heros/promocional-desktop.svg +12 -0
- package/template/public/brand/heros/promocional-mobile.svg +12 -0
- package/template/public/brand/logo-chrome.svg +6 -0
- package/template/public/brand/logo-white.svg +6 -0
- package/template/public/brand/logo.svg +7 -0
- package/template/public/brand/pay-amex.webp +0 -0
- package/template/public/brand/pay-elo.webp +0 -0
- package/template/public/brand/pay-mastercard.webp +0 -0
- package/template/public/brand/pay-pix.webp +0 -0
- package/template/public/brand/pay-visa.webp +0 -0
- package/template/public/brand/ph/avatar-a.svg +5 -0
- package/template/public/brand/ph/avatar-b.svg +5 -0
- package/template/public/brand/ph/avatar-c.svg +5 -0
- package/template/public/brand/ph/avatar-d.svg +5 -0
- package/template/public/brand/ph/photo-a.svg +7 -0
- package/template/public/brand/ph/photo-b.svg +6 -0
- package/template/public/brand/ph/photo-c.svg +6 -0
- package/template/public/brand/ph/poster-a.svg +6 -0
- package/template/public/brand/ph/poster-b.svg +6 -0
- package/template/public/brand/ph/poster-c.svg +6 -0
- package/template/public/unbox/powered-by-fundo-preto.png +0 -0
- package/template/public/unbox/powered-by-transparente.png +0 -0
- package/template/public/unbox/powered-by.png +0 -0
- package/template/scripts/abandoned-cart.ts +82 -0
- package/template/scripts/check-editable.mjs +467 -0
- package/template/scripts/check-honestidade.mjs +111 -0
- package/template/scripts/check-placeholder.mjs +200 -0
- package/template/scripts/check-recipe.mjs +76 -0
- package/template/scripts/check-unbox-brand.mjs +298 -0
- package/template/scripts/contraste.js +177 -0
- package/template/scripts/dump-catalog.ts +95 -0
- package/template/scripts/load-env.ts +13 -0
- package/template/scripts/medir-sistema.mjs +56 -0
- package/template/scripts/place-order-pix.ts +61 -0
- package/template/scripts/qa-screenshots.mjs +150 -0
- package/template/scripts/sistema.py +231 -0
- package/template/scripts/subscribe-webhook.ts +36 -0
- package/template/scripts/test-live.ts +186 -0
- package/template/scripts/vocabulario.py +264 -0
- package/template/tsconfig.json +27 -0
- package/tools/LEIA-ME.md +39 -0
- package/tools/check-template-neutro.mjs +111 -0
- package/tools/notion-doc.mjs +191 -0
- package/tools/tokenize-neutrals.mjs +0 -0
- package/tools/tokenize-radius.mjs +52 -0
- package/tools/workflow-storefront.legado.js +1207 -0
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
# Agente 11 — SEO + Infraestrutura
|
|
2
|
+
|
|
3
|
+
> ⚠️ **Doc de plano (histórico).** A implementação entregue pela foundation difere em nomes e
|
|
4
|
+
> caminhos de arquivo citados abaixo. O mapa REAL de arquivos por área é a tabela "Agentes
|
|
5
|
+
> 00-11" do `agents/MANAGER.md` — em conflito, valem o MANAGER e o código.
|
|
6
|
+
|
|
7
|
+
## Escopo
|
|
8
|
+
SEO técnico: sitemap dinâmico, robots.txt, metadados estruturados, JSON-LD e helpers de metadata.
|
|
9
|
+
|
|
10
|
+
## Dependências
|
|
11
|
+
- Agente 00 (Scaffold) — `lib/metadata.ts` base
|
|
12
|
+
|
|
13
|
+
## Arquivos proprietários
|
|
14
|
+
|
|
15
|
+
| Arquivo | Descrição |
|
|
16
|
+
|---------|-----------|
|
|
17
|
+
| `app/sitemap.ts` | Sitemap dinâmico com produtos + categorias |
|
|
18
|
+
| `app/robots.ts` | robots.txt com bloqueio de /conta e /api |
|
|
19
|
+
| `lib/metadata.ts` | Helpers buildMetadata, buildCategoryMetadata, buildProductJsonLd |
|
|
20
|
+
| `app/(loja)/pedido/[referenceId]/page.tsx` | Confirmação de pedido (noindex) |
|
|
21
|
+
|
|
22
|
+
## lib/metadata.ts (completo)
|
|
23
|
+
|
|
24
|
+
```ts
|
|
25
|
+
import type { Metadata } from "next"
|
|
26
|
+
|
|
27
|
+
const SITE = process.env.NEXT_PUBLIC_SITE_NAME ?? "Loja"
|
|
28
|
+
const URL = process.env.NEXT_PUBLIC_SITE_URL ?? "https://seudominio.com.br"
|
|
29
|
+
|
|
30
|
+
export function buildMetadata(
|
|
31
|
+
product: { title: string; pageTitle?: string; metaDescription?: string; description?: string; imageUrls?: string[]; slug: string },
|
|
32
|
+
canonical?: string,
|
|
33
|
+
): Metadata {
|
|
34
|
+
const title = product.pageTitle ?? product.title
|
|
35
|
+
const description = product.metaDescription ?? product.description?.replace(/<[^>]+>/g, "").slice(0, 160)
|
|
36
|
+
const image = product.imageUrls?.[0]
|
|
37
|
+
const url = canonical ?? `${URL}/p/${product.slug}`
|
|
38
|
+
return {
|
|
39
|
+
title: `${title} | ${SITE}`,
|
|
40
|
+
description,
|
|
41
|
+
alternates: { canonical: url },
|
|
42
|
+
openGraph: {
|
|
43
|
+
title, description, url, type: "website",
|
|
44
|
+
images: image ? [{ url: image, alt: title, width: 800, height: 800 }] : [],
|
|
45
|
+
},
|
|
46
|
+
twitter: {
|
|
47
|
+
card: "summary_large_image", title, description,
|
|
48
|
+
images: image ? [image] : [],
|
|
49
|
+
},
|
|
50
|
+
robots: { index: true, follow: true },
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function buildCategoryMetadata(tag: {
|
|
55
|
+
name: string; displayTitle?: string; description?: string; slug: string
|
|
56
|
+
}): Metadata {
|
|
57
|
+
const title = tag.displayTitle ?? tag.name
|
|
58
|
+
return {
|
|
59
|
+
title: `${title} | ${SITE}`,
|
|
60
|
+
description: tag.description,
|
|
61
|
+
alternates: { canonical: `${URL}/c/${tag.slug}` },
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function buildProductJsonLd(product: {
|
|
66
|
+
title: string; description?: string; imageUrls?: string[];
|
|
67
|
+
variants?: Array<{ sku?: string | null }>
|
|
68
|
+
isSoldOut: boolean; pricing?: Array<{ price: number | null }>
|
|
69
|
+
}) {
|
|
70
|
+
return {
|
|
71
|
+
"@context": "https://schema.org",
|
|
72
|
+
"@type": "Product",
|
|
73
|
+
name: product.title,
|
|
74
|
+
description: product.description?.replace(/<[^>]+>/g, ""),
|
|
75
|
+
image: product.imageUrls ?? [],
|
|
76
|
+
sku: product.variants?.[0]?.sku,
|
|
77
|
+
offers: {
|
|
78
|
+
"@type": "Offer",
|
|
79
|
+
availability: product.isSoldOut
|
|
80
|
+
? "https://schema.org/OutOfStock"
|
|
81
|
+
: "https://schema.org/InStock",
|
|
82
|
+
price: product.pricing?.[0]?.price ?? 0,
|
|
83
|
+
priceCurrency: "BRL",
|
|
84
|
+
},
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## app/sitemap.ts — paginação correta
|
|
90
|
+
|
|
91
|
+
```ts
|
|
92
|
+
// Iterar catálogo completo com paginação (não buscar tudo com first grande)
|
|
93
|
+
let offset = 0
|
|
94
|
+
for (;;) {
|
|
95
|
+
const page = await client.getCatalog({ first: 100, offset })
|
|
96
|
+
// processar page.nodes
|
|
97
|
+
if (!page.pageInfo?.hasNextPage) break
|
|
98
|
+
offset += 100
|
|
99
|
+
}
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## app/robots.ts
|
|
103
|
+
|
|
104
|
+
```ts
|
|
105
|
+
import type { MetadataRoute } from "next"
|
|
106
|
+
export default function robots(): MetadataRoute.Robots {
|
|
107
|
+
return {
|
|
108
|
+
rules: [
|
|
109
|
+
{
|
|
110
|
+
userAgent: "*",
|
|
111
|
+
allow: "/",
|
|
112
|
+
disallow: ["/conta/", "/checkout", "/pedido/", "/api/", "/busca?"],
|
|
113
|
+
},
|
|
114
|
+
],
|
|
115
|
+
sitemap: `${process.env.NEXT_PUBLIC_SITE_URL}/sitemap.xml`,
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
## Variáveis NEXT_PUBLIC seguras
|
|
121
|
+
|
|
122
|
+
```ts
|
|
123
|
+
// PERMITIDOS como NEXT_PUBLIC (não são credenciais):
|
|
124
|
+
NEXT_PUBLIC_SITE_NAME=Minha Loja
|
|
125
|
+
NEXT_PUBLIC_SITE_URL=https://seudominio.com.br
|
|
126
|
+
|
|
127
|
+
// PROIBIDOS como NEXT_PUBLIC:
|
|
128
|
+
// NEXT_PUBLIC_UNBOX_API_KEY ← NUNCA
|
|
129
|
+
// NEXT_PUBLIC_UNBOX_SHOP_ID ← evitar (não é credencial mas não precisa ser público)
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
## Página de confirmação de pedido
|
|
133
|
+
|
|
134
|
+
```tsx
|
|
135
|
+
// app/(loja)/pedido/[referenceId]/page.tsx
|
|
136
|
+
export const metadata: Metadata = {
|
|
137
|
+
robots: { index: false, follow: false } // noindex — evitar indexar URLs de pedido
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
async function getOrder(referenceId: string) {
|
|
141
|
+
const client = await getUnboxClient()
|
|
142
|
+
// buscar por referenceId — ver SDK
|
|
143
|
+
return client.getOrderByReferenceId(referenceId)
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Status: orderStatusLabel(order.status) — NUNCA displayStatus(language)
|
|
147
|
+
// Rastreio: tracking.trackingCode (não trackingUrl no contexto cliente)
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
## Performance — checklist
|
|
151
|
+
|
|
152
|
+
```
|
|
153
|
+
- getCatalog para homepage: unstable_cache({ revalidate: 300 })
|
|
154
|
+
- getProductBySlug (PDP): export const revalidate = 3600
|
|
155
|
+
- getTags: unstable_cache({ revalidate: 3600 })
|
|
156
|
+
- getShop/shopSales: unstable_cache({ revalidate: 300 })
|
|
157
|
+
- Sitemap: sem cache explícito (Next.js revalida conforme revalidate da rota)
|
|
158
|
+
- Imagens: next/image com sizes corretos para evitar oversized downloads
|
|
159
|
+
```
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
# Agente 12 — Deploy (Vercel)
|
|
2
|
+
|
|
3
|
+
## Escopo
|
|
4
|
+
Preparar e executar o primeiro deploy da loja no Vercel: Node version, env vars nos três
|
|
5
|
+
ambientes (Production / Preview / Development) e configuração do projeto.
|
|
6
|
+
|
|
7
|
+
**REGRA DE OURO DO PRIMEIRO DEPLOY: sem git, sem GitHub.** O `vercel` sobe a pasta local
|
|
8
|
+
direto, e a conta Vercel pode ser criada com email (na tela de login, ignorar o "Continue
|
|
9
|
+
with GitHub"). NUNCA peça pro lojista criar conta GitHub ou inicializar git pra ver a loja
|
|
10
|
+
no ar — isso é atrito desnecessário. Git/GitHub é um opcional de CI/CD pra DEPOIS da loja
|
|
11
|
+
publicada, e só se o cliente quiser.
|
|
12
|
+
|
|
13
|
+
## Dependências
|
|
14
|
+
- Todos os outros agentes concluídos
|
|
15
|
+
- `.env.local` preenchido com as credenciais reais da loja
|
|
16
|
+
- Conta Vercel (login por email serve)
|
|
17
|
+
|
|
18
|
+
## O que este agente faz
|
|
19
|
+
|
|
20
|
+
1. Garante Node 22 LTS (`nvm use 22`)
|
|
21
|
+
2. Remove lock file global que conflita com npm
|
|
22
|
+
3. Valida conexão com a API Unbox antes de subir
|
|
23
|
+
4. Cria projeto no Vercel e sobe as env vars
|
|
24
|
+
5. Configura Framework Preset como **Next.js**
|
|
25
|
+
6. Executa o primeiro deploy de produção (upload direto, sem repositório)
|
|
26
|
+
7. (Opcional, se o cliente pedir) conecta GitHub pra redeploy automático
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## Passo a passo
|
|
31
|
+
|
|
32
|
+
### 1. Ambiente local
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
nvm install 22 && nvm use 22 # Node 22 LTS obrigatório (v26 causa startup lento)
|
|
36
|
+
rm -f ~/package-lock.json # evita conflito com npm install
|
|
37
|
+
npm install
|
|
38
|
+
npm run unbox:test # valida credenciais antes de qualquer coisa
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### 2. Vercel — entrar e linkar o projeto
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
npx vercel login # abre o browser; login por email funciona (não precisa GitHub)
|
|
45
|
+
npx vercel link # cria novo projeto na conta (ou linka um existente)
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Durante o `vercel link`, responder:
|
|
49
|
+
- **Set up and deploy?** → No (vamos configurar antes)
|
|
50
|
+
- **Link to existing project?** → depende (Yes se já existe, No para criar)
|
|
51
|
+
|
|
52
|
+
### 3. Env vars — CRÍTICO
|
|
53
|
+
|
|
54
|
+
Adicionar **todas** as variáveis **nos 3 ambientes** (Production + Preview + Development) antes do
|
|
55
|
+
primeiro build. Sem elas o build falha com `UnboxError: signin HTTP 403`.
|
|
56
|
+
|
|
57
|
+
#### Via CLI (recomendado — selecionar todos os 3 ambientes no prompt):
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
npx vercel env add UNBOX_PARTNER_API_KEY # modelo recomendado (ou UNBOX_API_KEY, por loja)
|
|
61
|
+
npx vercel env add UNBOX_CAPTCHA_BYPASS # obrigatório com a key de parceiro (placeOrder/OTP)
|
|
62
|
+
npx vercel env add UNBOX_USER
|
|
63
|
+
npx vercel env add UNBOX_PASS
|
|
64
|
+
npx vercel env add SESSION_SECRET
|
|
65
|
+
npx vercel env add NEXT_PUBLIC_SITE_URL
|
|
66
|
+
npx vercel env add NEXT_PUBLIC_SITE_NAME
|
|
67
|
+
# opcionais mas recomendados:
|
|
68
|
+
npx vercel env add UNBOX_WEBHOOK_SECRET
|
|
69
|
+
npx vercel env add REVALIDATE_SECRET
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
> Quando o CLI perguntar "Which environments?", escolher **Production, Preview, Development** (todos).
|
|
73
|
+
|
|
74
|
+
#### Ou via dashboard:
|
|
75
|
+
|
|
76
|
+
> Settings → Environment Variables → Add
|
|
77
|
+
|
|
78
|
+
Para cada variável, marcar os checkboxes: **Production ✅ Preview ✅ Development ✅**
|
|
79
|
+
|
|
80
|
+
| Variável | Obrigatório | Descrição |
|
|
81
|
+
|---|---|---|
|
|
82
|
+
| `UNBOX_PARTNER_API_KEY` | ✅ (recomendado) | Key única do parceiro; dispensa `UNBOX_API_KEY` |
|
|
83
|
+
| `UNBOX_CAPTCHA_BYPASS` | ✅ com key de parceiro | Segredo do header x-captcha-verification (sem ele: CAPTCHA_MALFORMED_ERROR no pagamento) |
|
|
84
|
+
| `UNBOX_API_KEY` | ✅ se não houver key de parceiro | Chave de API da loja (modelo antigo) |
|
|
85
|
+
| `UNBOX_USER` | ✅ | Usuário de API |
|
|
86
|
+
| `UNBOX_PASS` | ✅ | Senha de API |
|
|
87
|
+
| `SESSION_SECRET` | ✅ | String aleatória forte (≥ 32 chars) |
|
|
88
|
+
| `NEXT_PUBLIC_SITE_URL` | ✅ | URL pública (`https://minhaloja.com.br`) |
|
|
89
|
+
| `NEXT_PUBLIC_SITE_NAME` | ✅ | Nome da loja (título, OG, manifest, JSON-LD) |
|
|
90
|
+
| `UNBOX_SHOP_ID` | — | Extraído do JWT se vazio |
|
|
91
|
+
| `UNBOX_SHOP_SLUG` | — | Extraído do JWT se vazio |
|
|
92
|
+
| `UNBOX_WEBHOOK_SECRET` | — | Secret dos webhooks |
|
|
93
|
+
| `REVALIDATE_SECRET` | — | Protege `/api/revalidate` |
|
|
94
|
+
| `NEXT_PUBLIC_GA_ID` | — | GA4 measurement ID |
|
|
95
|
+
| `NEXT_PUBLIC_META_PIXEL_ID` | — | Meta Pixel ID |
|
|
96
|
+
| `META_CAPI_TOKEN` | — | Meta Conversions API token |
|
|
97
|
+
|
|
98
|
+
### 4. Framework Preset — CRÍTICO
|
|
99
|
+
|
|
100
|
+
> Settings → Build & Development Settings → Framework Preset → **Next.js**
|
|
101
|
+
|
|
102
|
+
**Nunca deixar como "Other"** — o Vercel não configura o build command correto e o deploy falha.
|
|
103
|
+
|
|
104
|
+
### 5. Primeiro deploy
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
npx vercel --prod
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Upload direto da pasta local; a URL de produção sai no final. Sem repositório envolvido.
|
|
111
|
+
|
|
112
|
+
### 6. Opcional (só se o cliente pedir): redeploy automático via GitHub
|
|
113
|
+
|
|
114
|
+
Com a loja já no ar, quem quiser que todo push em `main` redeploye sozinho:
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
# .gitignore já está correto no template — não alterar
|
|
118
|
+
git init
|
|
119
|
+
git add .
|
|
120
|
+
git commit -m "chore: initial storefront"
|
|
121
|
+
git remote add origin <repo-url>
|
|
122
|
+
git push -u origin main
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
E no dashboard: Settings → Git → conectar o repositório.
|
|
126
|
+
|
|
127
|
+
> `git add -A` pode travar em repos grandes. O `.gitignore` já exclui `node_modules/` e `.next/`.
|
|
128
|
+
|
|
129
|
+
---
|
|
130
|
+
|
|
131
|
+
## Erros comuns
|
|
132
|
+
|
|
133
|
+
| Erro | Causa | Solução |
|
|
134
|
+
|---|---|---|
|
|
135
|
+
| `UnboxError: signin HTTP 403` | Env vars ausentes | Adicionar em todos os 3 ambientes e fazer Redeploy |
|
|
136
|
+
| Build falha silenciosamente | Framework Preset "Other" | Settings → Build → alterar para **Next.js** |
|
|
137
|
+
| `npm ERR!` no install | `~/package-lock.json` global | `rm -f ~/package-lock.json` |
|
|
138
|
+
| Startup lento (> 5 min) em dev | Node.js v26 | `nvm use 22` |
|
|
139
|
+
| `Project already exists` | Nome duplicado na conta | Usar nome diferente (ex: `minhaloja-app`) |
|
|
140
|
+
| Prerender error na home | API inacessível durante build | Checar credenciais + `npm run unbox:test` |
|
|
141
|
+
|
|
142
|
+
---
|
|
143
|
+
|
|
144
|
+
## Redeploy após mudar env vars
|
|
145
|
+
|
|
146
|
+
Após alterar variáveis no dashboard:
|
|
147
|
+
|
|
148
|
+
1. Deployments → último deploy → **Redeploy**
|
|
149
|
+
|
|
150
|
+
Ou pela CLI:
|
|
151
|
+
|
|
152
|
+
```bash
|
|
153
|
+
npx vercel --prod
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
(Quem conectou GitHub também pode só dar `git push origin main`.)
|
|
157
|
+
|
|
158
|
+
---
|
|
159
|
+
|
|
160
|
+
## QA pré-go-live
|
|
161
|
+
|
|
162
|
+
Após o deploy, rodar o checklist em `QA.md` **com credenciais reais**.
|
|
163
|
+
|
|
164
|
+
> Sem `.env.local` preenchido e `npm run unbox:test` passando, os fluxos de carrinho,
|
|
165
|
+
> checkout e login não funcionam — o QA só vale com a API conectada.
|
|
166
|
+
|
|
167
|
+
## Saída esperada
|
|
168
|
+
|
|
169
|
+
- Projeto no Vercel configurado e com todas as env vars
|
|
170
|
+
- Framework Preset = Next.js
|
|
171
|
+
- Primeira URL de produção retornada pelo `npx vercel --prod` (sem git/GitHub envolvidos)
|
|
172
|
+
- Confirmação de que a home carrega sem erros (prerender passou)
|
|
173
|
+
- QA.md executado e sem itens pendentes
|
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
# Agente 13 — Unbox AI CRO Package
|
|
2
|
+
|
|
3
|
+
## Objetivo
|
|
4
|
+
|
|
5
|
+
Ativar os módulos de otimização de conversão (CRO) escolhidos pelo usuário. Cada módulo é independente — ative apenas os que fazem sentido para a loja atual.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Apresentação ao usuário
|
|
10
|
+
|
|
11
|
+
Ao iniciar este agente, exiba o menu abaixo e pergunte quais módulos ativar:
|
|
12
|
+
|
|
13
|
+
```
|
|
14
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
15
|
+
Unbox AI CRO Package — Módulos disponíveis
|
|
16
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
17
|
+
|
|
18
|
+
[1] Prova Social no PDP
|
|
19
|
+
Avaliações reais em destaque (badge "X compraram hoje" só com contador do backend)
|
|
20
|
+
⚙️ Requer: reviews reais em lib/enrichment/products.json. NÃO existe valor mockup:
|
|
21
|
+
sem dado, o módulo fica bloqueado (regra do CLAUDE.md e gate unbox:honestidade)
|
|
22
|
+
|
|
23
|
+
[2] Urgência de Estoque
|
|
24
|
+
"Restam X unidades" quando stock ≤ threshold configurável
|
|
25
|
+
⚙️ Requer: campo de estoque na API Unbox (productStock)
|
|
26
|
+
|
|
27
|
+
[3] Countdown Flash Sale
|
|
28
|
+
Banner com temporizador regressivo para ofertas com prazo
|
|
29
|
+
⚙️ Requer: definir data/hora de fim da promoção
|
|
30
|
+
|
|
31
|
+
[4] Barra de Frete Grátis
|
|
32
|
+
Progress bar no carrinho: "Faltam R$X para frete grátis"
|
|
33
|
+
⚙️ Requer: definir threshold de frete grátis (ex: R$149)
|
|
34
|
+
|
|
35
|
+
[5] Upsell Pós-Checkout
|
|
36
|
+
Seção "Você também vai gostar" na página de pedido confirmado
|
|
37
|
+
⚙️ Requer: lógica de recomendação (por categoria ou manual)
|
|
38
|
+
|
|
39
|
+
[6] Recently Viewed
|
|
40
|
+
Histórico de produtos vistos (localStorage, sem backend)
|
|
41
|
+
⚙️ Sem dependências — funciona sem credenciais
|
|
42
|
+
|
|
43
|
+
[7] Sticky CTA Mobile
|
|
44
|
+
Botão "Adicionar ao carrinho" fixo no scroll do PDP (mobile)
|
|
45
|
+
⚙️ Sem dependências
|
|
46
|
+
|
|
47
|
+
[8] Trust Strip Customizável
|
|
48
|
+
Ícones de confiança editáveis: entrega, troca, pagamento seguro
|
|
49
|
+
⚙️ Sem dependências — apenas editar os textos
|
|
50
|
+
|
|
51
|
+
[9] Kits & Combos com Cross-sell
|
|
52
|
+
Seção de kits na home com produtos complementares
|
|
53
|
+
⚙️ Requer: definir os combos em lib/enrichment/combos.ts
|
|
54
|
+
|
|
55
|
+
[10] Brindes (Gift with Purchase)
|
|
56
|
+
Exibe brinde desbloqueado quando carrinho atinge valor mínimo
|
|
57
|
+
⚠️ Requer configuração no back-end Unbox ANTES do lançamento
|
|
58
|
+
⚙️ Requer: regra de brinde ativa no painel + SKU do brinde
|
|
59
|
+
|
|
60
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
61
|
+
⚠️ Módulos 4 e 10 dependem de configuração no back-end
|
|
62
|
+
antes de ir ao ar. Veja a seção "Pré-requisitos de back-end".
|
|
63
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
64
|
+
Opções: "all", números separados por vírgula (ex: 1,4,7), ou "none"
|
|
65
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Aguarde a resposta antes de qualquer implementação.
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
|
|
72
|
+
## Módulos — especificação técnica
|
|
73
|
+
|
|
74
|
+
### [1] Prova Social no PDP
|
|
75
|
+
|
|
76
|
+
**Arquivo:** `components/product/pdp/social-proof.tsx`
|
|
77
|
+
|
|
78
|
+
**Contador de compras ("X pessoas compraram hoje"): BLOQUEADO.** A API pública da loja não
|
|
79
|
+
expõe volume de vendas por SKU e o enrichment não tem campo pra isso (`lib/enrichment/index.ts`).
|
|
80
|
+
Um número "configurável" ou "hardcoded por SKU" é fabricação, e o `unbox:honestidade` derruba o
|
|
81
|
+
build (`\d+ pessoas compraram`, `compraram nas últimas 24h`). Só implemente se o lojista fornecer
|
|
82
|
+
um contador REAL (integração própria), e registre a fonte no `marca/DESIGN-<MARCA>.md`.
|
|
83
|
+
|
|
84
|
+
O que este módulo faz HOJE: destacar as avaliações reais do produto (`reviews` do enrichment)
|
|
85
|
+
logo abaixo do preço no `buy-box.tsx`: nota média + 1 depoimento curto + link pra seção completa.
|
|
86
|
+
Sem reviews, o bloco não existe.
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
### [2] Urgência de Estoque
|
|
91
|
+
|
|
92
|
+
**Arquivo:** `components/product/pdp/stock-urgency.tsx`
|
|
93
|
+
|
|
94
|
+
```tsx
|
|
95
|
+
// Exibe "Restam 8 unidades" quando o estoque REAL <= THRESHOLD
|
|
96
|
+
const THRESHOLD = 15; // configurável
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Inserir no `buy-box.tsx`, abaixo do seletor de quantidade.
|
|
100
|
+
|
|
101
|
+
**Dependência API:** o catálogo público NÃO expõe quantidade (`productStock` não existe; só
|
|
102
|
+
`isSoldOut`). Quantidade real vem de `simpleInventory` na API de parceiros (`agents/COVERAGE.md`),
|
|
103
|
+
via `withStoreClient` no servidor. Sem esse dado, o módulo fica BLOQUEADO: contador que "desce
|
|
104
|
+
sozinho" ou threshold sem estoque real é fabricação.
|
|
105
|
+
|
|
106
|
+
---
|
|
107
|
+
|
|
108
|
+
### [3] Countdown Flash Sale
|
|
109
|
+
|
|
110
|
+
**Arquivo:** `components/home/flash-sale-banner.tsx`
|
|
111
|
+
|
|
112
|
+
```tsx
|
|
113
|
+
// Banner full-width com countdown regressivo
|
|
114
|
+
// Configurar: SALE_END_DATE (ISO string) e mensagem da oferta
|
|
115
|
+
const SALE_END_DATE = "2025-12-31T23:59:59-03:00"; // TODO: atualizar
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
Inserir na home, entre o hero e os combos. Condicionado: se `Date.now() < SALE_END_DATE`, exibe; caso contrário, não renderiza (null).
|
|
119
|
+
|
|
120
|
+
---
|
|
121
|
+
|
|
122
|
+
### [4] Barra de Frete Grátis
|
|
123
|
+
|
|
124
|
+
⚠️ **A foundation JÁ tem isso** — régua de frete grátis no mini-cart, checkout, buy-box e
|
|
125
|
+
header, todos lendo `FREE_SHIPPING_THRESHOLD` de `lib/store-config.ts` (que nasce `null` =
|
|
126
|
+
nada renderiza até existir regra real; ver `npm run unbox:dump`). Este módulo NÃO cria
|
|
127
|
+
componente novo nem segundo limiar: se a régua não aparece, o motivo é o threshold `null` —
|
|
128
|
+
preencha a store-config com a regra REAL. Só crie UI adicional (ex.: barra no topo da página
|
|
129
|
+
de carrinho) consumindo o MESMO `FREE_SHIPPING_THRESHOLD`, nunca um número hardcoded.
|
|
130
|
+
|
|
131
|
+
---
|
|
132
|
+
|
|
133
|
+
### [5] Upsell Pós-Checkout
|
|
134
|
+
|
|
135
|
+
**Arquivo:** `components/order/upsell-section.tsx`
|
|
136
|
+
|
|
137
|
+
```tsx
|
|
138
|
+
// Recomendações na página de pedido confirmado
|
|
139
|
+
// Fonte: últimos produtos vistos (localStorage) ou lista manual
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
Inserir em `app/(loja)/pedido/[referenceId]/page.tsx` (confirmação de pedido).
|
|
143
|
+
|
|
144
|
+
---
|
|
145
|
+
|
|
146
|
+
### [6] Recently Viewed
|
|
147
|
+
|
|
148
|
+
**Arquivo:** `lib/recently-viewed.ts` + `components/product/recently-viewed.tsx`
|
|
149
|
+
|
|
150
|
+
```ts
|
|
151
|
+
// localStorage: salva array de slugs visitados (max 8)
|
|
152
|
+
// Hook: useRecentlyViewed()
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
- Salvar no `useEffect` do PDP (client component).
|
|
156
|
+
- Exibir no carrinho e na home como seção "Vistos recentemente".
|
|
157
|
+
- **Sem dependências externas** — funciona com qualquer storeToken.
|
|
158
|
+
|
|
159
|
+
---
|
|
160
|
+
|
|
161
|
+
### [7] Sticky CTA Mobile
|
|
162
|
+
|
|
163
|
+
**Arquivo:** `components/product/pdp/sticky-cta.tsx`
|
|
164
|
+
|
|
165
|
+
```tsx
|
|
166
|
+
// Visível apenas em mobile (md:hidden)
|
|
167
|
+
// Aparece após scroll passar o botão principal (IntersectionObserver)
|
|
168
|
+
// Posição: fixed bottom-0, acima da nav bar (bottom-[84px])
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
Inserir no layout do PDP (`app/(loja)/produto/[productSlug]/page.tsx`).
|
|
172
|
+
|
|
173
|
+
---
|
|
174
|
+
|
|
175
|
+
### [8] Trust Strip Customizável
|
|
176
|
+
|
|
177
|
+
⚠️ **A foundation JÁ tem trust strips** — `components/home/sections/trust-strip.tsx` (escura)
|
|
178
|
+
e `trust-bar.tsx` (clara), registradas no `registry.ts` e documentadas no `agents/PADROES.md`.
|
|
179
|
+
Este módulo customiza os ITENS das existentes via receita: `props.items: [{ icon?, text }]`
|
|
180
|
+
(ícones em `TRUST_ICONS`, `trust-bar.tsx`). Sem `items`, as duas seções NÃO renderizam: os
|
|
181
|
+
selos são promessas do lojista ("entrega em 24h", "garantia de 1 ano"), não da foundation.
|
|
182
|
+
|
|
183
|
+
**Arquivo (só se precisar de variante nova):** `components/home/sections/` + registro no registry
|
|
184
|
+
|
|
185
|
+
```tsx
|
|
186
|
+
// Array configurável de itens: { icon, label, sub }
|
|
187
|
+
const TRUST_ITEMS = [
|
|
188
|
+
{ icon: "Truck", label: "Entrega rápida", sub: "Prazo conforme CEP" },
|
|
189
|
+
{ icon: "RotateCcw", label: "Troca sem burocracia", sub: "Até 7 dias após recebimento" },
|
|
190
|
+
{ icon: "Shield", label: "Pagamento seguro", sub: "SSL + antifraude" },
|
|
191
|
+
{ icon: "Award", label: "Produto original", sub: "Nota fiscal inclusa" },
|
|
192
|
+
];
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
Reutilizável: inserir no PDP, na home e/ou no checkout.
|
|
196
|
+
|
|
197
|
+
---
|
|
198
|
+
|
|
199
|
+
### [9] Kits & Combos com Cross-sell
|
|
200
|
+
|
|
201
|
+
**Arquivo:** `lib/enrichment/combos.ts` (já existe, vazio por padrão)
|
|
202
|
+
|
|
203
|
+
```ts
|
|
204
|
+
// Preencher com os combos da loja:
|
|
205
|
+
export const COMBOS: ComboDef[] = [
|
|
206
|
+
{
|
|
207
|
+
id: "kit-essencial",
|
|
208
|
+
name: "Kit Essencial",
|
|
209
|
+
description: "Os mais vendidos juntos",
|
|
210
|
+
items: [{ family: "FAMILIA_A" }, { family: "FAMILIA_B" }],
|
|
211
|
+
},
|
|
212
|
+
];
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
Os dados vivem em `lib/enrichment/combos.ts` (`COMBOS` + `resolveCombos`, chamado em `app/(loja)/page.tsx`; a seção da home é `kits`)
|
|
216
|
+
|
|
217
|
+
---
|
|
218
|
+
|
|
219
|
+
### [10] Brindes (Gift with Purchase)
|
|
220
|
+
|
|
221
|
+
**Arquivo:** `components/cart/gift-indicator.tsx`
|
|
222
|
+
|
|
223
|
+
```tsx
|
|
224
|
+
// Exibe brinde desbloqueado quando carrinho atinge valor mínimo.
|
|
225
|
+
// A lógica de elegibilidade e o SKU do brinde vêm da API Unbox
|
|
226
|
+
// (campo `gifts` retornado no payload do carrinho).
|
|
227
|
+
//
|
|
228
|
+
// ⚠️ PRÉ-REQUISITO DE BACK-END — configurar no painel Unbox:
|
|
229
|
+
// 1. Criar regra de brinde (mínimo de valor, SKU do produto brinde)
|
|
230
|
+
// 2. Garantir que o SKU do brinde está com estoque ativo
|
|
231
|
+
// Sem isso, o componente renderiza vazio — nunca exibe brinde falso.
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
**Comportamento:**
|
|
235
|
+
- Carrinho abaixo do mínimo: `"Adicione mais R$X e ganhe [nome do brinde]"` com progress bar
|
|
236
|
+
- Carrinho acima do mínimo: `"🎁 Brinde desbloqueado: [nome do brinde]!"` em destaque
|
|
237
|
+
- Sem regra configurada no back: componente não renderiza (null)
|
|
238
|
+
|
|
239
|
+
Inserir no `app/(loja)/carrinho/page.tsx`, abaixo da barra de frete grátis (módulo 4) se ativo, senão no topo do sumário.
|
|
240
|
+
|
|
241
|
+
**Dependência API:** `cart.gifts[]` — array de brindes elegíveis retornado pela Unbox API.
|
|
242
|
+
|
|
243
|
+
---
|
|
244
|
+
|
|
245
|
+
## Pré-requisitos de back-end
|
|
246
|
+
|
|
247
|
+
> Estes módulos exibem dados que vêm do painel Unbox. **Configurar antes do lançamento da loja.**
|
|
248
|
+
|
|
249
|
+
| Módulo | O que configurar no painel Unbox |
|
|
250
|
+
|--------|----------------------------------|
|
|
251
|
+
| **[4] Barra de Frete Grátis** | Regra de frete grátis: valor mínimo do pedido, CEPs/regiões elegíveis |
|
|
252
|
+
| **[10] Brindes** | Regra de brinde: valor mínimo, SKU do produto brinde, estoque ativo |
|
|
253
|
+
|
|
254
|
+
Sem essas configurações, os componentes renderizam vazios (não inventam dados). Isso é intencional — nunca exibir promoção que não existe.
|
|
255
|
+
|
|
256
|
+
---
|
|
257
|
+
|
|
258
|
+
## Fluxo do agente
|
|
259
|
+
|
|
260
|
+
1. **Apresentar menu** e aguardar escolha.
|
|
261
|
+
2. **Confirmar escopo:** listar módulos selecionados e suas dependências.
|
|
262
|
+
3. **Perguntar configurações** específicas de cada módulo (ex: threshold de frete, data do flash sale).
|
|
263
|
+
4. **Implementar** um módulo por vez, em ordem numérica.
|
|
264
|
+
5. **Para cada módulo:** criar/editar arquivo → inserir no componente pai → confirmar sem erros de TypeScript.
|
|
265
|
+
6. **Ao final:** listar TODOs de configuração que ainda precisam de dados reais (ex: `SALE_END_DATE`, combos, reviews).
|
|
266
|
+
|
|
267
|
+
---
|
|
268
|
+
|
|
269
|
+
## Regras
|
|
270
|
+
|
|
271
|
+
- **Nunca ativar módulo sem perguntar.** O template base é intencionalmente simples.
|
|
272
|
+
- **Módulos são opcionais e independentes** — remover um não quebra os outros.
|
|
273
|
+
- Usar os **brand tokens** (`--store-primary`, `--store-cta`) para cores, nunca hardcodar hex.
|
|
274
|
+
- Módulos sem dependência de API (6, 7, 8) podem ser ativados mesmo em modo mockup.
|
|
275
|
+
- Módulos com dependência (1, 2, 3, 4, 5, 9) devem exibir aviso se `.env.local` não estiver configurado.
|