@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,255 @@
|
|
|
1
|
+
# PADRÕES: biblioteca de layout da foundation
|
|
2
|
+
|
|
3
|
+
Repertório pro agente de Branding (15) decidir **estrutura visual** com critério, sem
|
|
4
|
+
reinventar código. A regra de ouro: **layout muda pela RECEITA**
|
|
5
|
+
(`components/home/home-recipe.ts`), pelas **fontes** (bloco `UNBOX-FONTS` de
|
|
6
|
+
`app/layout.tsx`) e pelos **tokens** (`app/globals.css`), nunca reescrevendo o JSX de uma
|
|
7
|
+
seção pra ela "virar outra coisa". Variante nova = arquivo novo em
|
|
8
|
+
`components/home/sections/` + registro no `registry.ts` + entrada aqui.
|
|
9
|
+
|
|
10
|
+
O mesmo vale pro **chrome** (header e rodapé): muda pela receita
|
|
11
|
+
`components/chrome/chrome-recipe.ts`, nunca reescrevendo `site-header.tsx`/`site-footer.tsx`
|
|
12
|
+
(esses são só a casca: fetch, wrapper e o selo obrigatório). Ver seção 2c.
|
|
13
|
+
|
|
14
|
+
Complementos gerais (skills em `.claude/skills/`): `frontend-design` (direção estética na
|
|
15
|
+
hora de construir, use pra fugir de escolha "de template") e `web-design-guidelines`
|
|
16
|
+
(auditoria de UI/acessibilidade, rode antes de declarar pronto).
|
|
17
|
+
|
|
18
|
+
## Composição da home: impacto, não comprimento
|
|
19
|
+
|
|
20
|
+
Não existe teto de seções, e o medidor (`npm run unbox:medir`) reporta o número sem reprovar. O
|
|
21
|
+
que existe é uma pergunta por seção: **o que esta seção faz que a anterior não fez?** Seção que
|
|
22
|
+
repete a promessa da anterior com outras palavras alonga a página sem acrescentar motivo de
|
|
23
|
+
compra, e é assim que uma home vira rolagem.
|
|
24
|
+
|
|
25
|
+
A receita default tem 16 seções porque é uma biblioteca, não uma recomendação: o briefing escolhe.
|
|
26
|
+
Sinais de que sobrou seção: duas seções seguidas com o mesmo tipo de argumento, um bloco que só
|
|
27
|
+
existe para ocupar espaço entre dois que importam, e qualquer seção que o cliente não saberia
|
|
28
|
+
descrever depois de ver a página uma vez.
|
|
29
|
+
|
|
30
|
+
## O header segue o estilo da marca
|
|
31
|
+
|
|
32
|
+
A biblioteca tem 5 headers e a escolha é do briefing, não do preset. O preset dá o ponto de
|
|
33
|
+
partida; a marca decide. Um roteiro curto para escolher, em vez de aceitar o que veio:
|
|
34
|
+
|
|
35
|
+
| a marca é | header que costuma servir |
|
|
36
|
+
|---|---|
|
|
37
|
+
| fotográfica, com hero de imagem forte | `imersivo` (transparente sobre o hero, sólido ao rolar) |
|
|
38
|
+
| catálogo grande, busca é o começo da visita | `classico` (busca visível) |
|
|
39
|
+
| poucos produtos, identidade no logo | `centralizado` ou `equilibrado` |
|
|
40
|
+
| conteúdo denso, o header não é protagonista | `compacto` |
|
|
41
|
+
|
|
42
|
+
Trocar é uma linha em `components/chrome/chrome-recipe.ts`. O que não vale é deixar o default
|
|
43
|
+
porque ninguém olhou: o header aparece em toda página, e é a primeira coisa que faz uma loja
|
|
44
|
+
parecer com outra.
|
|
45
|
+
|
|
46
|
+
## 1. Os 4 estilos (presets do CLI)
|
|
47
|
+
|
|
48
|
+
O scaffold já aplicou um (campo `estilo` do `marca/briefing.json`). Seu papel no briefing é
|
|
49
|
+
VALIDAR contra as referências reais do cliente e ajustar, trocar o estilo inteiro é raro;
|
|
50
|
+
ajustar receita/fontes/variantes é comum.
|
|
51
|
+
|
|
52
|
+
| Estilo | Quando usar | Quando NÃO usar | Chrome | Radius | Display |
|
|
53
|
+
|---|---|---|---|---|---|
|
|
54
|
+
| **essencial** | qualquer segmento, cliente sem opinião forte de design | marca com identidade visual forte já definida | brand-dark | 0.875rem | Poppins |
|
|
55
|
+
| **promocional** | ticket médio, kits/combos, oferta agressiva, food/consumo recorrente | marca premium (fica "feirão") | brand-dark | 1.25rem (pill) | Barlow Condensed |
|
|
56
|
+
| **editorial** | assinatura/clube, curadoria, marca com história pra contar | catálogo grande de conveniência | ink-dark | 0.5rem | Fraunces (serif) |
|
|
57
|
+
| **boutique** | premium, minimal, autoral, poucos SKUs | loja de volume com muitas categorias | ink-dark | 0.25rem | Playfair Display (serif) |
|
|
58
|
+
|
|
59
|
+
Receitas default de cada um: ver o `components/home/home-recipe.ts` gerado neste projeto
|
|
60
|
+
(a fonte delas vive no CLI `create-unbox-store`, fora deste repo).
|
|
61
|
+
|
|
62
|
+
## 1b. Eixos de composição (a linguagem visual além de cor/fonte)
|
|
63
|
+
|
|
64
|
+
Tokens no bloco CONFIGURE de `globals.css`, mudam o "jeito" da loja inteira sem tocar em
|
|
65
|
+
componente. Cada estilo já vem com uma combinação; ajuste por marca livremente:
|
|
66
|
+
|
|
67
|
+
| Eixo | Token | O que muda | Valores dos estilos |
|
|
68
|
+
|---|---|---|---|
|
|
69
|
+
| Ritmo vertical | `--section-gap` | respiro entre seções (denso ↔ arejado) | essencial 52px · promocional 44px · editorial 72px · boutique 88px |
|
|
70
|
+
| Largura | `--container-max` | largura do conteúdo das seções | 1240px · 1240px · 1080px · 1000px |
|
|
71
|
+
| Chrome dos cards | `--card-bg`/`--card-border`/`--card-shadow` (classe `.store-card`) | bordado clássico · sombra flutuante · preenchido flat · hairline | essencial bordado · promocional sombra · editorial preenchido · boutique hairline |
|
|
72
|
+
|
|
73
|
+
Combinar eixos + fontes + neutros + radius = linguagens genuinamente diferentes com as MESMAS
|
|
74
|
+
seções. Se ainda assim a referência da marca pedir outra cara pra uma seção específica, o
|
|
75
|
+
caminho é **variante nova** (nível 2 abaixo), nunca editar a existente.
|
|
76
|
+
|
|
77
|
+
## 1c. Dials do briefing (variância · motion · densidade)
|
|
78
|
+
|
|
79
|
+
`marca/briefing.json` traz `dials` (1-10) com o default do estilo. Eles são a *temperatura*
|
|
80
|
+
da loja, e você revalida cada um no briefing contra as referências da marca:
|
|
81
|
+
|
|
82
|
+
| Dial | O que governa | Onde mexer |
|
|
83
|
+
|---|---|---|
|
|
84
|
+
| `variancia` | quão ousada é a composição: variantes menos óbvias, chrome mais autoral | receita da home e `chrome-recipe.ts` |
|
|
85
|
+
| `motion` | quanta animação (0 desliga tudo) | token `--motion` em `globals.css` |
|
|
86
|
+
| `densidade` | quanto conteúdo por tela | `--section-gap`, `--container-max`, nº de seções da receita |
|
|
87
|
+
|
|
88
|
+
**A paleta neutra é canvas provisória, não escolha estética.** A loja nasce em cinza + tinta
|
|
89
|
+
porque é a tela mais neutra pra qualquer marca cair bem em cima, aplicar as cores REAIS é o
|
|
90
|
+
primeiro ato do briefing, antes de qualquer seção. O `npm run build` avisa enquanto a paleta
|
|
91
|
+
default estiver no ar.
|
|
92
|
+
|
|
93
|
+
## 2. Catálogo de seções e variantes
|
|
94
|
+
|
|
95
|
+
| Seção | O que faz | Variantes | Critério |
|
|
96
|
+
|---|---|---|---|
|
|
97
|
+
| `hero` | primeira dobra | `imagem-full` (banner clicável), `carousel` (banners com setas), `split-editorial` (texto+CTA / imagem), `minimal-texto` (tipográfico, sem imagem), `imagem-imersiva` (foto sangrando até o topo, por baixo do header, par do header `imersivo`) | **full-bleed é o default** (`imagem-imersiva`): banner em card arredondado é o tell nº 1 de "site de template". História/proposta clara → split; sem foto decente AINDA → minimal-texto (nunca deixe hero com foto ruim); `imagem-full` só quando a marca QUISER o banner contido |
|
|
98
|
+
| `trust-bar` | selos de confiança claros (`items` via receita; some sem itens) | única | logo abaixo do hero em lojas novas (dá segurança); em boutique costuma sair |
|
|
99
|
+
| `category-pills` | chips de categorias | única | só faz sentido com 3+ categorias reais |
|
|
100
|
+
| `combos-carousel` | produtos em destaque com add-to-cart (âncora `#destaques`) | única | quase sempre presente; é o coração comercial |
|
|
101
|
+
| `savings` | comparativo de economia | única (some sozinha sem desconto real) | lojas de kit/combo; NUNCA force com desconto fake |
|
|
102
|
+
| `reviews` | depoimentos | `grid` (3 cards), `faixa` (linha discreta) | grid quando reviews são um ativo; faixa em estilos minimais; SEMPRE conteúdo real ou remova |
|
|
103
|
+
| `kits` | kits temáticos curados (`lib/enrichment/combos.ts`) | única (some com `COMBOS` vazio) | popular os combos reais primeiro |
|
|
104
|
+
| `trust-strip` | faixa escura de diferenciais | única | reforço no fim da página; redundante se trust-bar já diz o mesmo |
|
|
105
|
+
| `newsletter` | captura de e-mail (`title` + `action` obrigatórios; some sem eles) | `bloco` (CTA forte), `inline` (linha fina) | bloco em lojas promocionais; inline em editorial/boutique; form ainda é TODO de integração |
|
|
106
|
+
|
|
107
|
+
**Biblioteca de conversão** (portada de uma loja Unbox real em produção, genericizadas, todas aceitam
|
|
108
|
+
conteúdo via `props` com defaults neutros):
|
|
109
|
+
|
|
110
|
+
| Seção | O que faz | Regra de conteúdo |
|
|
111
|
+
|---|---|---|
|
|
112
|
+
| `attributes-marquee` | marquee infinito de atributos do produto (vegano, artesanal...) | atributos VERDADEIROS do produto; ícones por nome (leaf, drop, heart, shield...) |
|
|
113
|
+
| `benefits` | grid 2+foto+2 de benefícios | sem promessa de resultado inventada |
|
|
114
|
+
| `stats-grid` | 4 cards de destaque (com números OU qualitativos) | número SÓ com pesquisa/dado real (passe `source`); default é qualitativo de propósito |
|
|
115
|
+
| `quote-banner` | faixa foto + citação grande | citação da própria marca/fundador, nunca depoimento inventado |
|
|
116
|
+
| `media-cards` | carrossel de cards de imagem com legenda (ingredientes, materiais, bastidores) | fotos reais assim que existirem |
|
|
117
|
+
| `spec-table` | ficha técnica com modal (nutricional, composição, medidas) | dados REAIS do produto |
|
|
118
|
+
| `ritual` | crossfade de fotos + checklist "como usar" |, |
|
|
119
|
+
| `founder-story` | card de história com foto de fundo + assinatura (âncora #nossa-historia) | história real |
|
|
120
|
+
| `video-wall` | grade 9:16 de vídeo-depoimentos com play inline | SÓ vídeo real; sem vídeo, remova da receita |
|
|
121
|
+
| `social-row` | cards de comunidade/creators com @handle | gente real com autorização; seguidores só se verdadeiros |
|
|
122
|
+
| `reviews-carousel` | carrossel de reviews em card bordado com pílulas de tema | reviews reais |
|
|
123
|
+
| `comparison` | tabela "aqui vs por aí" estrutural | compare atributos, sem citar concorrente nem inventar |
|
|
124
|
+
| `product-showcase` | vitrine de cards com fundo colorido, produtos e preços do CATÁLOGO | `ctaHref: "#comprar"` pra mandar pro hero de compra |
|
|
125
|
+
| `purchase-hero` | **o bloco de conversão principal**: galeria + tiers de quantidade + vantagens + garantia (âncora `#comprar`) | produto/preço do catálogo (data.featured); tiers de `QUANTITY_TIERS`; CTA → passo 2 |
|
|
126
|
+
|
|
127
|
+
## 2c. Catálogo de header e rodapé (o chrome)
|
|
128
|
+
|
|
129
|
+
O chrome aparece em TODAS as páginas, é ele que mais pesa na sensação de "loja igual às
|
|
130
|
+
outras". Troque em `components/chrome/chrome-recipe.ts` (1 linha cada). `npm run typecheck`
|
|
131
|
+
acusa nome inexistente.
|
|
132
|
+
|
|
133
|
+
| Header | O que é | Quando usar | Quando NÃO usar |
|
|
134
|
+
|---|---|---|---|
|
|
135
|
+
| `classico` | logo esq · busca · ações · faixa de categorias abaixo | catálogo grande, busca é o caminho principal | marca que quer respiro |
|
|
136
|
+
| `compacto` | uma barra fina, categorias inline, busca em ícone | promocional/campanha: menos altura, mais tela pra oferta | catálogo com muitas categorias (não cabem) |
|
|
137
|
+
| `centralizado` | logo grande no centro, ações nas pontas, nav centralizada abaixo | editorial, assinatura, wordmark bonito | loja de volume onde busca manda |
|
|
138
|
+
| `equilibrado` | UMA linha: categorias à esquerda, logo ao centro, busca/conta/carrinho em ícone à direita | logo no centro sem gastar duas faixas; catálogo de 2 a 6 categorias | muitas categorias (empurram o logo do centro) |
|
|
139
|
+
| `imersivo` | barra mínima, menu em drawer (inclusive desktop), **transparente sobre hero de imagem** e sólido ao rolar | marca com fotografia boa, catálogo curto, premium | catálogo grande, ou loja sem foto real |
|
|
140
|
+
|
|
141
|
+
| Rodapé | O que é | Quando usar |
|
|
142
|
+
|---|---|---|
|
|
143
|
+
| `colunas` | trust strip + 4 colunas + pagamento | loja completa, navegação secundária importa |
|
|
144
|
+
| `conversao` | captura de e-mail em destaque + links enxutos | promocional: rodapé como última conversão |
|
|
145
|
+
| `editorial` | manifesto da marca + colunas discretas + redes | marca com história, tom de revista |
|
|
146
|
+
| `minimal` | logo, uma linha de links, pagamento discreto | catálogo curto, estética premium/minimal |
|
|
147
|
+
|
|
148
|
+
**Regras do chrome:**
|
|
149
|
+
- Variante muda composição e hierarquia, **nunca capacidade**: busca, conta, carrinho,
|
|
150
|
+
categorias e links legais existem em todas. Trocar o chrome não pode tirar função da loja.
|
|
151
|
+
- O `imersivo` só fica transparente com o hero `imagem-imersiva` na receita da home (é a
|
|
152
|
+
única variante que emite `.hero-imersivo`). Em qualquer outra página, e em qualquer home
|
|
153
|
+
sem esse hero, ele é sólido. Sem foto boa da marca, use outro hero: o header se ajusta
|
|
154
|
+
sozinho, e foto ruim em tela cheia é pior que header sólido.
|
|
155
|
+
- A barra inferior do rodapé (© + selo Powered by Unbox) é da CASCA, não da variante: é
|
|
156
|
+
contrato da plataforma e o build falha sem ela.
|
|
157
|
+
|
|
158
|
+
## 2b. O funil de compra (purchase-hero → picker → checkout)
|
|
159
|
+
|
|
160
|
+
O padrão das lojas Unbox de produção: o **hero de compra é uma SEÇÃO da home** (ou topo da
|
|
161
|
+
PDP/landing), não uma página escondida. Fluxo:
|
|
162
|
+
|
|
163
|
+
1. **`purchase-hero`** (seção, âncora `#comprar`): quantidade via `QUANTITY_TIERS`
|
|
164
|
+
(`lib/store-config.ts`) com preço real por unidade. CTAs de outras seções apontam pra cá
|
|
165
|
+
(`href`/`ctaHref: "#comprar"`).
|
|
166
|
+
2. **`/carrinho/oferta`** (picker, tela split sem chrome): escolha de produtos/variantes com
|
|
167
|
+
stepper + **frequência de envio**, assinatura com o desconto REAL da política da loja
|
|
168
|
+
(`recurringOrdersPolicy`) vs compra única. Só aparece se a política existe e há produto
|
|
169
|
+
com `recurrenceAllowed`.
|
|
170
|
+
3. `/checkout`.
|
|
171
|
+
|
|
172
|
+
`/oferta` existe como deep-link de campanha (mesma seção + pilha de convencimento de
|
|
173
|
+
`components/landing/landing-recipe.ts`). Regras: preços SEMPRE do catálogo; `offPct`/
|
|
174
|
+
`perks`/`badge` dos tiers só quando a promoção equivalente EXISTE no painel Unbox (senão o
|
|
175
|
+
total diverge no checkout, defaults saem zerados por isso).
|
|
176
|
+
|
|
177
|
+
## 3. Receitas por objetivo
|
|
178
|
+
|
|
179
|
+
- **Subir ticket médio:** promocional; `combos-carousel` + `savings` logo após o hero;
|
|
180
|
+
`kits` populado; newsletter `bloco` prometendo oferta (só se a oferta existir).
|
|
181
|
+
- **Assinatura/clube:** editorial; hero `split-editorial` contando a proposta; reviews
|
|
182
|
+
`faixa`; newsletter `inline` como "entrar pra lista"; assinatura destacada na PDP (a
|
|
183
|
+
PDP já suporta; não mexa na estrutura dela nesta versão).
|
|
184
|
+
- **Marca premium/minimal:** boutique; 5 a 6 seções NO MÁXIMO; hero `minimal-texto` até
|
|
185
|
+
existir fotografia à altura; sem `savings`, sem `trust-strip`.
|
|
186
|
+
- **Cliente não sabe / loja generalista:** essencial completo; corte o que não tiver
|
|
187
|
+
conteúdo real (reviews fake NÃO, ou dado real, ou remove).
|
|
188
|
+
|
|
189
|
+
## 4. Como aplicar mudanças
|
|
190
|
+
|
|
191
|
+
1. **Ordem/presença/variante de seção:** editar `components/home/home-recipe.ts`.
|
|
192
|
+
2. **Copy do hero (split/minimal):** `props` da entrada `hero` na receita
|
|
193
|
+
(`title`, `subtitle`, `ctaLabel`, `ctaHref`).
|
|
194
|
+
3. **Imagens do hero:** troque os arquivos em `public/brand/` e os paths nos `props`
|
|
195
|
+
(`.webp` otimizado; os `.svg` gerados são placeholder até existir imagem real).
|
|
196
|
+
4. **Tipografia:** bloco `UNBOX-FONTS` em `app/layout.tsx` (só Google Fonts com pesos
|
|
197
|
+
500-800; corpo legível + display com personalidade).
|
|
198
|
+
5. **Neutros/radius/chrome:** tokens em `app/globals.css` (bloco CONFIGURE). Cor SEMPRE
|
|
199
|
+
via `var(--store-*)`, nunca hex solto.
|
|
200
|
+
6. **Composição (ritmo/largura/cards):** eixos `--section-gap`, `--container-max` e
|
|
201
|
+
`--card-*` no mesmo bloco CONFIGURE (ver seção 1b).
|
|
202
|
+
7. **Variante nova de seção:** criar o componente em `components/home/sections/`,
|
|
203
|
+
registrar no `registry.ts`, documentar aqui. Antes de desenhar, carregue o skill
|
|
204
|
+
`frontend-design` e faça uma escolha estética própria da marca, variante nova que
|
|
205
|
+
parece a original com outra cor não justifica existir. `npm run typecheck` valida a
|
|
206
|
+
receita.
|
|
207
|
+
8. **Chrome (header/rodapé):** trocar a variante = editar `components/chrome/chrome-recipe.ts`
|
|
208
|
+
(ver seção 2c). Variante NOVA = arquivo em `components/chrome/headers/` ou `footers/` +
|
|
209
|
+
entrada no `components/chrome/registry.ts` (e no `HEADER_SHELL`, se for header) +
|
|
210
|
+
linha na tabela daqui. A variante desenha só o MIOLO: o `<header>`/`<footer>` com
|
|
211
|
+
`store-layout site-chrome` e o selo Powered by são da casca, e é assim que a loja garante
|
|
212
|
+
que o chrome some no checkout e que o contrato não cai.
|
|
213
|
+
|
|
214
|
+
## 4b. O que NÃO importamos de frameworks externos de "taste"
|
|
215
|
+
|
|
216
|
+
Avaliamos a `taste-skill` (anti-slop) e extraímos o que serve: os dials (1c), o "design read"
|
|
217
|
+
do agente 15, os anti-tells de copy (5) e a medição de composição (`npm run unbox:receita`).
|
|
218
|
+
**Recusamos de propósito**, não "conserte" isto depois:
|
|
219
|
+
|
|
220
|
+
- **Ban de serifa** (Fraunces/Playfair): são o display de dois estilos nossos, escolha decidida.
|
|
221
|
+
- **Ban de paleta**: a crítica ao bege premium procede e já foi resolvida trocando os neutros;
|
|
222
|
+
quem decide a cor da marca é o briefing, não um doc externo.
|
|
223
|
+
- **Stack de motion com dependência nova** (GSAP/motion): nosso motion é CSS puro, sem JS.
|
|
224
|
+
- **Imagens de Picsum**: foto aleatória numa loja real é pior que placeholder declarado.
|
|
225
|
+
- **Design systems oficiais** (Material/Fluent/Carbon): a loja é shadcn/Base UI + tokens `--store-*`.
|
|
226
|
+
|
|
227
|
+
## 5. Anti-padrões
|
|
228
|
+
|
|
229
|
+
- 9 seções numa loja minimal (boutique com receita do essencial).
|
|
230
|
+
- **Toda loja com a mesma composição.** Se as referências da marca pedem outra linguagem
|
|
231
|
+
e você entregou o default da biblioteca, o briefing falhou, use os eixos (1b) e
|
|
232
|
+
variantes novas.
|
|
233
|
+
- Remover toda âncora de confiança de loja nova (algum trust fica).
|
|
234
|
+
- Prova social inventada em QUALQUER estilo, dado real, estado honesto, ou decisão
|
|
235
|
+
EXPLÍCITA do lojista registrada (`marca/honestidade-permitido.txt`). Nunca por omissão.
|
|
236
|
+
- Hex solto no JSX; fonte fora do bloco UNBOX-FONTS; receita reescrita como JSX.
|
|
237
|
+
- Editar destrutivamente variante existente da biblioteca (crie uma nova).
|
|
238
|
+
- Mexer no motor (`lib/unbox`, `app/api`, checkout) por causa de layout.
|
|
239
|
+
- **Copy que denuncia texto de IA** (some com ela; se a marca quiser algo assim, tem que ser
|
|
240
|
+
decisão declarada dela): verbos de enchimento ("Elevate", "Seamless", "Unleash",
|
|
241
|
+
"Revolucione"), eyebrows numeradas ("001 · Recursos"), rótulos de etapa por número
|
|
242
|
+
(Etapa 1/2/3, use o verbo direto: Instale, Configure), "role para explorar"/scroll cues,
|
|
243
|
+
rótulos de versão (v1.4.2, BETA) fora de contexto, nomes genéricos em depoimento
|
|
244
|
+
(João Silva, Maria S.), legendas decorativas de foto ("Estudo de campo nº 12").
|
|
245
|
+
- Travessão na copy voltada ao cliente final. **Fios/traços decorativos contam**: o
|
|
246
|
+
risquinho antes de eyebrow/kicker lê como "cara de AI" pro cliente, mesma proibição.
|
|
247
|
+
- Classe de marca apontando pra variável de fonte que só existe no `@theme inline` do
|
|
248
|
+
Tailwind (ex.: `var(--font-mono)`): funciona por acaso dentro de `.store-layout` e cai
|
|
249
|
+
em serifa fora dele. Classes de fonte apontam pra variável do **next/font** com stack
|
|
250
|
+
completa de fallback.
|
|
251
|
+
- **`text-white` (ou cor cravada) sobre fundo de cor de marca.** A cor da marca muda por
|
|
252
|
+
loja: na loja de referência o CTA era escuro e funcionou; numa bifurcação virou
|
|
253
|
+
verde-limão e o texto caiu pra 1,39:1 de contraste. Sobre `--store-cta` use SEMPRE
|
|
254
|
+
`--store-cta-fg`; sobre `--store-chrome-bg` use `--store-chrome-text`. Branco fixo só
|
|
255
|
+
sobre `--store-primary` (o CLI garante primary escuro o bastante).
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# Agente 00 — Scaffold
|
|
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
|
+
Criar a estrutura completa do projeto Next.js 15: arquivos de configuração, fundação compartilhada
|
|
9
|
+
(`lib/`), shell de `app/layout.tsx` e todas as rotas como placeholders para os outros agentes preencherem.
|
|
10
|
+
|
|
11
|
+
## Dependências
|
|
12
|
+
Nenhuma — este agente roda primeiro.
|
|
13
|
+
|
|
14
|
+
## Arquivos a criar
|
|
15
|
+
|
|
16
|
+
| Arquivo | Descrição |
|
|
17
|
+
|---------|-----------|
|
|
18
|
+
| `package.json` | Next.js 15 + Base UI + shadcn + Tailwind v4 |
|
|
19
|
+
| `tsconfig.json` | Padrão Next.js com paths `@/*` |
|
|
20
|
+
| `next.config.ts` | Minimal, domains de imagem em branco |
|
|
21
|
+
| `app/globals.css` | `@import "tailwindcss"` + variáveis CSS de tema |
|
|
22
|
+
| `app/layout.tsx` | RootLayout shell (providers placeholder) |
|
|
23
|
+
| `lib/unbox.ts` | UnboxClient singleton server-only |
|
|
24
|
+
| `lib/auth.ts` | getCustomerToken/setCustomerToken/clearCustomerToken |
|
|
25
|
+
| `lib/feedback.ts` | Re-export friendlyError, cartEventLabel do SDK |
|
|
26
|
+
| `lib/metadata.ts` | buildMetadata(product) → Metadata do Next.js |
|
|
27
|
+
| `.env.local.example` | Template de variáveis de ambiente |
|
|
28
|
+
| `middleware.ts` | Proteção das rotas /conta/* |
|
|
29
|
+
| Todas as rotas | Placeholders com comentário `// TODO: agente X` |
|
|
30
|
+
|
|
31
|
+
## SDK
|
|
32
|
+
```ts
|
|
33
|
+
import "server-only"
|
|
34
|
+
import { UnboxClient } from "@payflows/unbox-sdk"
|
|
35
|
+
|
|
36
|
+
let _client: UnboxClient | null = null
|
|
37
|
+
|
|
38
|
+
export async function getUnboxClient(): Promise<UnboxClient> {
|
|
39
|
+
if (_client) return _client
|
|
40
|
+
_client = new UnboxClient({
|
|
41
|
+
apiKey: process.env.UNBOX_API_KEY!,
|
|
42
|
+
shopId: process.env.UNBOX_SHOP_ID!,
|
|
43
|
+
})
|
|
44
|
+
await _client.signIn(process.env.UNBOX_USER!, process.env.UNBOX_PASS!)
|
|
45
|
+
return _client
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Regras
|
|
50
|
+
- `UNBOX_API_KEY` nunca com prefixo `NEXT_PUBLIC_`
|
|
51
|
+
- `lib/unbox.ts` deve ter `import "server-only"` na primeira linha
|
|
52
|
+
- Token de cliente é separado — `lib/auth.ts` usa cookies httpOnly
|
|
53
|
+
- Tailwind v4: `app/globals.css` deve ter `@import "tailwindcss"`
|
|
54
|
+
|
|
55
|
+
## Saída esperada
|
|
56
|
+
Lista de todos os arquivos criados com conteúdo completo.
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# Agente 01 — Layout
|
|
2
|
+
|
|
3
|
+
## Escopo
|
|
4
|
+
Chrome global da loja: header (barra de anúncio + nav + busca + carrinho), footer (trust strip +
|
|
5
|
+
colunas de links + pagamento) e navegação mobile. Usado em toda página via `app/layout.tsx`.
|
|
6
|
+
|
|
7
|
+
## Dependências
|
|
8
|
+
- Agente 00 (Scaffold) — `lib/config.ts`, `lib/unbox/*`, tokens de cor em `app/globals.css`
|
|
9
|
+
|
|
10
|
+
## Arquivos proprietários
|
|
11
|
+
|
|
12
|
+
| Arquivo | Descrição |
|
|
13
|
+
|---------|-----------|
|
|
14
|
+
| `app/layout.tsx` | RootLayout — monta `<SiteHeader>`, `{children}`, `<SiteFooter>`, providers (`CartProvider`, `Toaster`) |
|
|
15
|
+
| `components/site-header.tsx` | Barra de anúncio + header sticky (logo, busca, conta, carrinho) + nav de categorias |
|
|
16
|
+
| `components/site-footer.tsx` | Trust strip, colunas de links, `PaymentChips`, logo em fundo escuro |
|
|
17
|
+
| `components/mobile-nav.tsx` | Drawer mobile com categorias (`NavCategory[]`) |
|
|
18
|
+
| `components/account-nav.tsx` | Ícone/menu de conta no header desktop |
|
|
19
|
+
|
|
20
|
+
## Regra de ouro — degradação sem credenciais
|
|
21
|
+
Header e footer **sempre** buscam dados da loja (`getShopData`, `getTopTags`) com `.catch()` e
|
|
22
|
+
fallback, nunca deixam o erro subir — é o que permite o "modo mockup" (sem `.env.local`
|
|
23
|
+
preenchido) funcionar sem crashar a página inteira:
|
|
24
|
+
|
|
25
|
+
```tsx
|
|
26
|
+
const [shop, tags] = await Promise.all([
|
|
27
|
+
getShopData().catch(() => null),
|
|
28
|
+
getTopTags().catch(() => []),
|
|
29
|
+
]);
|
|
30
|
+
const shopName = shop?.name ?? "Minha Loja";
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Qualquer novo dado buscado no chrome global (header/footer) precisa seguir esse padrão — uma
|
|
34
|
+
página de dados (catálogo, PDP, checkout) pode mostrar erro, mas o chrome nunca pode derrubar o
|
|
35
|
+
layout inteiro.
|
|
36
|
+
|
|
37
|
+
## Categorias (nav)
|
|
38
|
+
`NavCategory[]` vem de `getTopTags()` — **sem** filtro `isTopLevel` (ver doc 05-cart/lib/unbox/client.ts:
|
|
39
|
+
forçar `isTopLevel: true` esconde categorias reais marcadas `false` no admin). Filtra por
|
|
40
|
+
`isVisible !== false` e ordena por `position`. Reusado pelo header desktop e pelo `mobile-nav.tsx`.
|
|
41
|
+
|
|
42
|
+
## Tokens de marca
|
|
43
|
+
Logo: `public/brand/logo.svg` (fundo claro, header) e `logo-white.svg` (fundo escuro, footer).
|
|
44
|
+
Cores via CSS vars `--store-primary`, `--store-cta`, `--store-chrome-bg`/`--store-chrome-text`
|
|
45
|
+
(bloco "CONFIGURE AQUI" em `app/globals.css`) — **nunca hardcode hex** nos componentes de layout;
|
|
46
|
+
sempre `bg-[var(--store-X,#fallback)]` para que a customização do CLI (`create-unbox-store`)
|
|
47
|
+
realmente se propague.
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# Agente 02 — Homepage
|
|
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
|
+
Página inicial: hero banner, promoções da loja (shopSales), produtos em destaque e grade de categorias.
|
|
9
|
+
|
|
10
|
+
## Dependências
|
|
11
|
+
- Agente 00 (Scaffold)
|
|
12
|
+
- Agente 03 (Catalog) — reutiliza `ProductCard`
|
|
13
|
+
- Agente 09 (Promotions) — reutiliza `ShopSalesBanner`
|
|
14
|
+
|
|
15
|
+
## Arquivos proprietários
|
|
16
|
+
|
|
17
|
+
| Arquivo | Descrição |
|
|
18
|
+
|---------|-----------|
|
|
19
|
+
| `app/(loja)/page.tsx` | Server Component com Suspense por seção |
|
|
20
|
+
| `components/home/HeroBanner.tsx` | Banner principal com CTA |
|
|
21
|
+
| `components/home/FeaturedProducts.tsx` | Grade de 8 produtos em destaque |
|
|
22
|
+
| `components/home/CategoryGrid.tsx` | Grade visual de categorias |
|
|
23
|
+
| `app/loading.tsx` | Skeleton da homepage |
|
|
24
|
+
|
|
25
|
+
## Dados da API
|
|
26
|
+
|
|
27
|
+
```ts
|
|
28
|
+
import { getUnboxClient } from "@/lib/unbox"
|
|
29
|
+
import { unstable_cache } from "next/cache"
|
|
30
|
+
|
|
31
|
+
const getHomeData = unstable_cache(async () => {
|
|
32
|
+
const client = await getUnboxClient()
|
|
33
|
+
const [shop, catalog, tags] = await Promise.all([
|
|
34
|
+
client.getShop(), // shopSales
|
|
35
|
+
client.getCatalog({ first: 8, sortBy: "updatedAt", sortOrder: "desc" }),
|
|
36
|
+
client.getTags(true), // categorias
|
|
37
|
+
])
|
|
38
|
+
return {
|
|
39
|
+
shopSales: shop.shopSales ?? [],
|
|
40
|
+
products: catalog.nodes.map(n => n.product),
|
|
41
|
+
categories: tags.filter(t => t.isVisible),
|
|
42
|
+
}
|
|
43
|
+
}, ["home-data"], { revalidate: 300 })
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Estrutura de app/(loja)/page.tsx
|
|
47
|
+
|
|
48
|
+
```tsx
|
|
49
|
+
export default async function HomePage() {
|
|
50
|
+
const { shopSales, products, categories } = await getHomeData()
|
|
51
|
+
return (
|
|
52
|
+
<main id="main-content">
|
|
53
|
+
<ShopSalesBanner sales={shopSales} />
|
|
54
|
+
<HeroBanner />
|
|
55
|
+
<Suspense fallback={<ProductGridSkeleton count={8} />}>
|
|
56
|
+
<FeaturedProducts products={products} />
|
|
57
|
+
</Suspense>
|
|
58
|
+
<CategoryGrid categories={categories} />
|
|
59
|
+
</main>
|
|
60
|
+
)
|
|
61
|
+
}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## SEO
|
|
65
|
+
```ts
|
|
66
|
+
export const metadata: Metadata = {
|
|
67
|
+
title: "Nome da Loja — Compre Online",
|
|
68
|
+
description: "...",
|
|
69
|
+
}
|
|
70
|
+
```
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# Agente 03 — Catalog (Listagem de Produtos)
|
|
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
|
+
Páginas de catálogo geral, categoria/tag e busca. O `ProductCard` gerado aqui é reutilizado por
|
|
9
|
+
Homepage e outros agentes.
|
|
10
|
+
|
|
11
|
+
## Dependências
|
|
12
|
+
- Agente 00 (Scaffold)
|
|
13
|
+
|
|
14
|
+
## Arquivos proprietários
|
|
15
|
+
|
|
16
|
+
| Arquivo | Descrição |
|
|
17
|
+
|---------|-----------|
|
|
18
|
+
| `app/(loja)/produtos/page.tsx` | Catálogo completo, paginado por offset (searchParams) |
|
|
19
|
+
| `app/(loja)/categoria/[tagSlug]/page.tsx` | Produtos de uma categoria |
|
|
20
|
+
| `app/(loja)/busca/page.tsx` | Resultados de busca (?q=) |
|
|
21
|
+
| `app/(loja)/produtos/loading.tsx` | Skeleton do grid |
|
|
22
|
+
| `components/catalog/ProductCard.tsx` | **Componente central — reutilizado em todo o storefront** |
|
|
23
|
+
| `components/catalog/ProductGrid.tsx` | Grid responsivo (1→2→3→4 colunas) |
|
|
24
|
+
| `components/catalog/Pagination.tsx` | Links de paginação via URL (?page=) |
|
|
25
|
+
| `components/catalog/FilterBar.tsx` | Filtros de categoria e ordenação |
|
|
26
|
+
| `components/catalog/SearchBar.tsx` | Campo de busca client (redireciona para /busca) |
|
|
27
|
+
|
|
28
|
+
## Dados da API
|
|
29
|
+
|
|
30
|
+
```ts
|
|
31
|
+
// Catálogo geral
|
|
32
|
+
const result = await client.getCatalog({
|
|
33
|
+
first: 24,
|
|
34
|
+
offset: (page - 1) * 24, // page vem de searchParams.page
|
|
35
|
+
sortBy: "updatedAt",
|
|
36
|
+
sortOrder: "desc",
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
// Categoria
|
|
40
|
+
const tags = await client.getTags(true)
|
|
41
|
+
const tag = tags.find(t => t.slug === tagSlug)
|
|
42
|
+
const result = await client.getCatalog({ first: 24, offset: 0, tagIds: [tag._id] })
|
|
43
|
+
|
|
44
|
+
// Busca
|
|
45
|
+
const result = await client.getCatalog({ first: 24, searchText: q })
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## ProductCard — campos obrigatórios
|
|
49
|
+
|
|
50
|
+
```tsx
|
|
51
|
+
interface ProductCardProps {
|
|
52
|
+
product: {
|
|
53
|
+
_id: string
|
|
54
|
+
title: string
|
|
55
|
+
slug: string
|
|
56
|
+
imageUrls?: string[]
|
|
57
|
+
pricing?: Array<{ displayPrice: string; price: number | null }>
|
|
58
|
+
variants?: Array<{ pricing?: Array<{ compareAtPrice?: { displayAmount: string } | null }> }>
|
|
59
|
+
isSoldOut: boolean
|
|
60
|
+
isBackorder?: boolean
|
|
61
|
+
isLowQuantity?: boolean
|
|
62
|
+
recurrenceAllowed?: boolean
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Link: /p/[product.slug]
|
|
67
|
+
// Imagem: product.imageUrls?.[0] (Next.js <Image>)
|
|
68
|
+
// Preço: product.pricing?.[0]?.displayPrice
|
|
69
|
+
// Preço de: product.variants?.[0]?.pricing?.[0]?.compareAtPrice?.displayAmount
|
|
70
|
+
// Badges: "Esgotado" | "Pré-venda" | "Últimas unidades" | "Assine e Poupe"
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## generateStaticParams (categoria)
|
|
74
|
+
|
|
75
|
+
```ts
|
|
76
|
+
export async function generateStaticParams() {
|
|
77
|
+
const client = await getUnboxClient()
|
|
78
|
+
const tags = await client.getTags(true)
|
|
79
|
+
return tags.filter(t => t.isVisible).map(t => ({ tagSlug: t.slug }))
|
|
80
|
+
}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## Paginação
|
|
84
|
+
- URL-based: `?page=2` via Link (não state de cliente — SSR-friendly)
|
|
85
|
+
- Mostrar total de resultados (`result.totalCount`)
|
|
86
|
+
- Desabilitar "anterior" na página 1, "próxima" se `!result.pageInfo?.hasNextPage`
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# Agente 04 — PDP (Product Detail Page)
|
|
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
|
+
Página de detalhe de produto: galeria de imagens, seleção de variante, toggle assinatura,
|
|
9
|
+
botão de adicionar ao carrinho.
|
|
10
|
+
|
|
11
|
+
## Dependências
|
|
12
|
+
- Agente 00 (Scaffold)
|
|
13
|
+
- Agente 05 (Cart) — AddToCartForm chama /api/cart
|
|
14
|
+
|
|
15
|
+
## Arquivos proprietários
|
|
16
|
+
|
|
17
|
+
| Arquivo | Descrição |
|
|
18
|
+
|---------|-----------|
|
|
19
|
+
| `app/(loja)/produto/[productSlug]/page.tsx` | Server Component com SSG + ISR (revalidate: 3600) |
|
|
20
|
+
| `app/(loja)/produto/[productSlug]/loading.tsx` | Skeleton de PDP |
|
|
21
|
+
| `components/pdp/ProductImages.tsx` | Galeria: imagem principal + miniaturas |
|
|
22
|
+
| `components/pdp/ProductInfo.tsx` | Título, preço, badges, short description |
|
|
23
|
+
| `components/pdp/VariantSelector.tsx` | Botões de seleção de variante (client) |
|
|
24
|
+
| `components/pdp/SubscriptionToggle.tsx` | Toggle Avulso/Assine, seleção de frequência (client) |
|
|
25
|
+
| `components/pdp/AddToCartForm.tsx` | Quantidade + botão Adicionar (client) |
|
|
26
|
+
| `components/pdp/ProductDescription.tsx` | Descrição longa + Informações Adicionais (accordion) |
|
|
27
|
+
|
|
28
|
+
## Dados da API
|
|
29
|
+
|
|
30
|
+
```ts
|
|
31
|
+
export const revalidate = 3600 // ISR: revalidar a cada 1h
|
|
32
|
+
|
|
33
|
+
const item = await client.getProductBySlug(params.productSlug)
|
|
34
|
+
// item.product.variants[] — variantes para seleção
|
|
35
|
+
// item.product.recurrenceAllowed — exibir SubscriptionToggle
|
|
36
|
+
// item.product.pricing[].displayPrice — preço base
|
|
37
|
+
// item.shortDescription — texto acima do fold
|
|
38
|
+
// item.product.description — descrição longa (HTML)
|
|
39
|
+
// item.product.additionalInformation — accordion extra
|
|
40
|
+
|
|
41
|
+
// Frequências de assinatura (se recurrenceAllowed):
|
|
42
|
+
const shop = await client.getShop()
|
|
43
|
+
const frequencies = shop.recurringOrdersPolicy?.frequencyOptions ?? []
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## generateStaticParams
|
|
47
|
+
|
|
48
|
+
```ts
|
|
49
|
+
export async function generateStaticParams() {
|
|
50
|
+
const client = await getUnboxClient()
|
|
51
|
+
const catalog = await client.getCatalog({ first: 200 })
|
|
52
|
+
return catalog.nodes.map(n => ({ productSlug: n.product.slug }))
|
|
53
|
+
}
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## SubscriptionToggle — lógica crítica
|
|
57
|
+
|
|
58
|
+
```
|
|
59
|
+
- Só exibir se product.recurrenceAllowed === true
|
|
60
|
+
- "Avulso" (default) → isRecurring: false
|
|
61
|
+
- "Assinar" → isRecurring: true + selecionar frequência
|
|
62
|
+
- A FREQUÊNCIA vai no placeOrder (orderRecurrence), NÃO no cartItem
|
|
63
|
+
- No cartItem: apenas isRecurring: true (sem frequencyId — campo não existe no live)
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## AddToCartForm
|
|
67
|
+
|
|
68
|
+
```tsx
|
|
69
|
+
// Client component
|
|
70
|
+
async function handleAddToCart() {
|
|
71
|
+
const res = await fetch("/api/cart", {
|
|
72
|
+
method: "POST",
|
|
73
|
+
body: JSON.stringify({
|
|
74
|
+
action: "add",
|
|
75
|
+
productId: product._id,
|
|
76
|
+
productVariantId: selectedVariantId,
|
|
77
|
+
price: selectedVariant.pricing?.[0]?.price ?? 0,
|
|
78
|
+
quantity,
|
|
79
|
+
isRecurring,
|
|
80
|
+
}),
|
|
81
|
+
})
|
|
82
|
+
if (!res.ok) toast.error(friendlyError(await res.json()))
|
|
83
|
+
else { openCart(); toast.success("Produto adicionado!") }
|
|
84
|
+
}
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## SEO
|
|
88
|
+
|
|
89
|
+
```ts
|
|
90
|
+
export async function generateMetadata({ params }) {
|
|
91
|
+
const item = await client.getProductBySlug(params.productSlug)
|
|
92
|
+
return buildMetadata(item.product, `${SITE_URL}/p/${params.productSlug}`)
|
|
93
|
+
}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## JSON-LD
|
|
97
|
+
Incluir `<script type="application/ld+json">` com Product schema na página.
|