@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.
Files changed (358) hide show
  1. package/LICENSE.md +46 -0
  2. package/README.md +175 -0
  3. package/bin/cli.js +439 -0
  4. package/package.json +43 -0
  5. package/src/colors.js +100 -0
  6. package/src/presets.js +241 -0
  7. package/src/theme.js +253 -0
  8. package/template/.claude/agents/assets-marca.md +40 -0
  9. package/template/.claude/agents/avaliador-visual.md +48 -0
  10. package/template/.claude/agents/branding-briefing.md +401 -0
  11. package/template/.claude/agents/conteudo-secao.md +46 -0
  12. package/template/.claude/agents/paginas-legais.md +44 -0
  13. package/template/.claude/settings.json +3 -0
  14. package/template/.claude/skills/frontend-design/ATTRIBUTION.md +3 -0
  15. package/template/.claude/skills/frontend-design/LICENSE.txt +177 -0
  16. package/template/.claude/skills/frontend-design/SKILL.md +55 -0
  17. package/template/.claude/skills/web-design-guidelines/ATTRIBUTION.md +3 -0
  18. package/template/.claude/skills/web-design-guidelines/SKILL.md +50 -0
  19. package/template/.claude/skills/web-design-guidelines/guidelines-snapshot.md +180 -0
  20. package/template/.env.example +158 -0
  21. package/template/.mcp.json.example +14 -0
  22. package/template/.nvmrc +1 -0
  23. package/template/CLAUDE.md +333 -0
  24. package/template/COVERAGE.md +80 -0
  25. package/template/DEPLOY.md +179 -0
  26. package/template/QA.md +108 -0
  27. package/template/README.md +152 -0
  28. package/template/agents/CONSTRUCAO.md +207 -0
  29. package/template/agents/MANAGER.md +192 -0
  30. package/template/agents/PADROES.md +255 -0
  31. package/template/agents/definitions/00-scaffold.md +56 -0
  32. package/template/agents/definitions/01-layout.md +47 -0
  33. package/template/agents/definitions/02-homepage.md +70 -0
  34. package/template/agents/definitions/03-catalog.md +86 -0
  35. package/template/agents/definitions/04-pdp.md +97 -0
  36. package/template/agents/definitions/05-cart.md +116 -0
  37. package/template/agents/definitions/06-checkout.md +146 -0
  38. package/template/agents/definitions/07-auth.md +130 -0
  39. package/template/agents/definitions/08-customer.md +55 -0
  40. package/template/agents/definitions/09-promotions.md +62 -0
  41. package/template/agents/definitions/10-feedback.md +128 -0
  42. package/template/agents/definitions/11-seo-infra.md +159 -0
  43. package/template/agents/definitions/12-deploy.md +173 -0
  44. package/template/agents/definitions/13-cro.md +275 -0
  45. package/template/agents/definitions/14-seo.md +388 -0
  46. package/template/agents/definitions/15-branding.md +26 -0
  47. package/template/agents/definitions/16-qa-visual.md +138 -0
  48. package/template/agents/definitions/17-aeo.md +257 -0
  49. package/template/app/(loja)/busca/page.tsx +72 -0
  50. package/template/app/(loja)/carrinho/oferta/page.tsx +57 -0
  51. package/template/app/(loja)/carrinho/page.tsx +160 -0
  52. package/template/app/(loja)/categoria/[tagSlug]/layout.tsx +31 -0
  53. package/template/app/(loja)/categoria/[tagSlug]/loading.tsx +17 -0
  54. package/template/app/(loja)/categoria/[tagSlug]/page.tsx +49 -0
  55. package/template/app/(loja)/checkout/page.tsx +80 -0
  56. package/template/app/(loja)/checkout/pix/[ref]/page.tsx +133 -0
  57. package/template/app/(loja)/conta/assinaturas/[referenceId]/page.tsx +112 -0
  58. package/template/app/(loja)/conta/assinaturas/page.tsx +51 -0
  59. package/template/app/(loja)/conta/enderecos/page.tsx +20 -0
  60. package/template/app/(loja)/conta/entrar/page.tsx +119 -0
  61. package/template/app/(loja)/conta/page.tsx +45 -0
  62. package/template/app/(loja)/conta/pedidos/[referenceId]/page.tsx +49 -0
  63. package/template/app/(loja)/conta/pedidos/page.tsx +104 -0
  64. package/template/app/(loja)/conta/preferencias/page.tsx +24 -0
  65. package/template/app/(loja)/devolucoes/page.tsx +36 -0
  66. package/template/app/(loja)/layout.tsx +22 -0
  67. package/template/app/(loja)/oferta/page.tsx +57 -0
  68. package/template/app/(loja)/page.tsx +86 -0
  69. package/template/app/(loja)/pedido/[referenceId]/page.tsx +78 -0
  70. package/template/app/(loja)/privacidade/page.tsx +113 -0
  71. package/template/app/(loja)/produto/[productSlug]/layout.tsx +23 -0
  72. package/template/app/(loja)/produto/[productSlug]/loading.tsx +20 -0
  73. package/template/app/(loja)/produto/[productSlug]/page.tsx +372 -0
  74. package/template/app/(loja)/produtos/loading.tsx +16 -0
  75. package/template/app/(loja)/produtos/page.tsx +33 -0
  76. package/template/app/(loja)/termos/page.tsx +104 -0
  77. package/template/app/acesso/page.tsx +129 -0
  78. package/template/app/api/account/addresses/route.ts +39 -0
  79. package/template/app/api/account/exists/route.ts +21 -0
  80. package/template/app/api/account/me/route.ts +25 -0
  81. package/template/app/api/account/otp/route.ts +26 -0
  82. package/template/app/api/account/preferences/route.ts +19 -0
  83. package/template/app/api/account/signin/route.ts +27 -0
  84. package/template/app/api/account/signout/route.ts +9 -0
  85. package/template/app/api/acesso/route.ts +171 -0
  86. package/template/app/api/capi/route.ts +34 -0
  87. package/template/app/api/cart/coupon/route.ts +49 -0
  88. package/template/app/api/cart/items/route.ts +45 -0
  89. package/template/app/api/cart/link/route.ts +69 -0
  90. package/template/app/api/cart/route.ts +113 -0
  91. package/template/app/api/cart/share/route.ts +21 -0
  92. package/template/app/api/cep/[code]/route.ts +18 -0
  93. package/template/app/api/checkout/address/route.ts +24 -0
  94. package/template/app/api/checkout/email/route.ts +41 -0
  95. package/template/app/api/checkout/installments/route.ts +16 -0
  96. package/template/app/api/checkout/route.ts +110 -0
  97. package/template/app/api/checkout/shipping/route.ts +65 -0
  98. package/template/app/api/checkout-destination/route.ts +31 -0
  99. package/template/app/api/order/[ref]/route.ts +17 -0
  100. package/template/app/api/payment-link/route.ts +28 -0
  101. package/template/app/api/revalidate/route.ts +52 -0
  102. package/template/app/api/shipping/quote/route.ts +29 -0
  103. package/template/app/api/subscriptions/[id]/route.ts +52 -0
  104. package/template/app/api/track/route.ts +28 -0
  105. package/template/app/api/unbox/catalogo/route.ts +136 -0
  106. package/template/app/api/unbox/paginas/route.ts +167 -0
  107. package/template/app/api/unbox/vitrine/route.ts +84 -0
  108. package/template/app/api/webhooks/unbox/route.ts +97 -0
  109. package/template/app/apple-icon.svg +5 -0
  110. package/template/app/error.tsx +15 -0
  111. package/template/app/globals.css +457 -0
  112. package/template/app/icon.svg +4 -0
  113. package/template/app/layout.tsx +90 -0
  114. package/template/app/llms.txt/route.ts +43 -0
  115. package/template/app/manifest.ts +21 -0
  116. package/template/app/not-found.tsx +18 -0
  117. package/template/app/opengraph-image.tsx +35 -0
  118. package/template/app/robots.ts +31 -0
  119. package/template/app/sitemap.ts +42 -0
  120. package/template/bootstrap.sh +87 -0
  121. package/template/components/account/account-shell.tsx +47 -0
  122. package/template/components/account/address-book.tsx +147 -0
  123. package/template/components/account/preferences-form.tsx +77 -0
  124. package/template/components/account/reorder-button.tsx +47 -0
  125. package/template/components/account/signout-button.tsx +20 -0
  126. package/template/components/account/subscription-actions.tsx +204 -0
  127. package/template/components/account-nav.tsx +41 -0
  128. package/template/components/address-fields.tsx +102 -0
  129. package/template/components/analytics/data-layer-ready.tsx +16 -0
  130. package/template/components/analytics/purchase-tracker.tsx +32 -0
  131. package/template/components/brand-search.tsx +34 -0
  132. package/template/components/cart/cart-provider.tsx +340 -0
  133. package/template/components/cart/mini-cart.tsx +228 -0
  134. package/template/components/cart-button.tsx +35 -0
  135. package/template/components/catalog/catalog-client.tsx +523 -0
  136. package/template/components/catalog/grid-skeleton.tsx +19 -0
  137. package/template/components/catalog/pager.tsx +66 -0
  138. package/template/components/catalog/product-grid-card.tsx +52 -0
  139. package/template/components/catalog/product-grid.tsx +27 -0
  140. package/template/components/checkout/checkout-client.tsx +1333 -0
  141. package/template/components/chrome/announce-bar.tsx +57 -0
  142. package/template/components/chrome/chrome-recipe.ts +13 -0
  143. package/template/components/chrome/footers/captura-botao.tsx +28 -0
  144. package/template/components/chrome/footers/colunas.tsx +110 -0
  145. package/template/components/chrome/footers/conversao.tsx +90 -0
  146. package/template/components/chrome/footers/editorial.tsx +98 -0
  147. package/template/components/chrome/footers/minimal.tsx +55 -0
  148. package/template/components/chrome/header-bar-mobile.tsx +64 -0
  149. package/template/components/chrome/headers/centralizado.tsx +71 -0
  150. package/template/components/chrome/headers/classico.tsx +64 -0
  151. package/template/components/chrome/headers/compacto.tsx +66 -0
  152. package/template/components/chrome/headers/equilibrado.tsx +73 -0
  153. package/template/components/chrome/headers/imersivo.tsx +62 -0
  154. package/template/components/chrome/registry.ts +70 -0
  155. package/template/components/chrome/solid-on-scroll.tsx +39 -0
  156. package/template/components/empty-state.tsx +22 -0
  157. package/template/components/home/combo-card-compact.tsx +67 -0
  158. package/template/components/home/combos-home.tsx +64 -0
  159. package/template/components/home/combos-section.tsx +177 -0
  160. package/template/components/home/home-recipe.ts +41 -0
  161. package/template/components/home/sections/attributes-marquee.tsx +178 -0
  162. package/template/components/home/sections/benefits.tsx +80 -0
  163. package/template/components/home/sections/bloco-html.tsx +47 -0
  164. package/template/components/home/sections/catalogo.ts +165 -0
  165. package/template/components/home/sections/category-pills.tsx +32 -0
  166. package/template/components/home/sections/combos-carousel.tsx +141 -0
  167. package/template/components/home/sections/comparison.tsx +73 -0
  168. package/template/components/home/sections/founder-story.tsx +40 -0
  169. package/template/components/home/sections/hero.tsx +187 -0
  170. package/template/components/home/sections/kits.tsx +11 -0
  171. package/template/components/home/sections/media-cards.tsx +70 -0
  172. package/template/components/home/sections/newsletter.tsx +62 -0
  173. package/template/components/home/sections/product-showcase.tsx +175 -0
  174. package/template/components/home/sections/purchase-hero.tsx +187 -0
  175. package/template/components/home/sections/quote-banner.tsx +50 -0
  176. package/template/components/home/sections/registry.ts +186 -0
  177. package/template/components/home/sections/reviews-carousel.tsx +66 -0
  178. package/template/components/home/sections/reviews.tsx +68 -0
  179. package/template/components/home/sections/ritual.tsx +96 -0
  180. package/template/components/home/sections/savings.tsx +47 -0
  181. package/template/components/home/sections/social-row.tsx +65 -0
  182. package/template/components/home/sections/spec-table.tsx +117 -0
  183. package/template/components/home/sections/stars.tsx +12 -0
  184. package/template/components/home/sections/stats-grid.tsx +54 -0
  185. package/template/components/home/sections/trust-bar.tsx +56 -0
  186. package/template/components/home/sections/trust-strip.tsx +40 -0
  187. package/template/components/home/sections/video-wall.tsx +59 -0
  188. package/template/components/landing/landing-recipe.ts +20 -0
  189. package/template/components/landing/oferta-sections.tsx +40 -0
  190. package/template/components/landing/product-picker.tsx +257 -0
  191. package/template/components/mobile-nav.tsx +71 -0
  192. package/template/components/order-status.tsx +163 -0
  193. package/template/components/powered-by-unbox.tsx +27 -0
  194. package/template/components/product/pdp/buy-box.tsx +604 -0
  195. package/template/components/product/pdp/catalog-grid.tsx +113 -0
  196. package/template/components/product/pdp/faq-modelo.ts +86 -0
  197. package/template/components/product/pdp/gallery.tsx +87 -0
  198. package/template/components/product/pdp/interactive.tsx +200 -0
  199. package/template/components/product/pdp/newsletter.tsx +64 -0
  200. package/template/components/product/pdp/payment-chips.tsx +22 -0
  201. package/template/components/product/pdp/pdp-view.tsx +227 -0
  202. package/template/components/product/pdp/recommendations.tsx +144 -0
  203. package/template/components/product/pdp/sections.tsx +173 -0
  204. package/template/components/quantity-stepper.tsx +36 -0
  205. package/template/components/search-box.tsx +37 -0
  206. package/template/components/site-footer.tsx +59 -0
  207. package/template/components/site-header.tsx +64 -0
  208. package/template/components/ui/accordion.tsx +72 -0
  209. package/template/components/ui/alert-dialog.tsx +187 -0
  210. package/template/components/ui/alert.tsx +76 -0
  211. package/template/components/ui/aspect-ratio.tsx +22 -0
  212. package/template/components/ui/avatar.tsx +109 -0
  213. package/template/components/ui/badge.tsx +52 -0
  214. package/template/components/ui/breadcrumb.tsx +125 -0
  215. package/template/components/ui/button.tsx +58 -0
  216. package/template/components/ui/card.tsx +103 -0
  217. package/template/components/ui/carousel.tsx +242 -0
  218. package/template/components/ui/command.tsx +196 -0
  219. package/template/components/ui/dialog.tsx +160 -0
  220. package/template/components/ui/dropdown-menu.tsx +268 -0
  221. package/template/components/ui/foto.tsx +54 -0
  222. package/template/components/ui/input-group.tsx +158 -0
  223. package/template/components/ui/input-otp.tsx +87 -0
  224. package/template/components/ui/input.tsx +20 -0
  225. package/template/components/ui/label.tsx +20 -0
  226. package/template/components/ui/navigation-menu.tsx +168 -0
  227. package/template/components/ui/pagination.tsx +132 -0
  228. package/template/components/ui/popover.tsx +90 -0
  229. package/template/components/ui/progress.tsx +83 -0
  230. package/template/components/ui/radio-group.tsx +38 -0
  231. package/template/components/ui/scroll-area.tsx +55 -0
  232. package/template/components/ui/select.tsx +201 -0
  233. package/template/components/ui/separator.tsx +25 -0
  234. package/template/components/ui/sheet.tsx +138 -0
  235. package/template/components/ui/skeleton.tsx +13 -0
  236. package/template/components/ui/sonner.tsx +44 -0
  237. package/template/components/ui/switch.tsx +32 -0
  238. package/template/components/ui/table.tsx +116 -0
  239. package/template/components/ui/tabs.tsx +82 -0
  240. package/template/components/ui/textarea.tsx +18 -0
  241. package/template/components/ui/toggle-group.tsx +89 -0
  242. package/template/components/ui/toggle.tsx +45 -0
  243. package/template/components/ui/tooltip.tsx +66 -0
  244. package/template/components.json +25 -0
  245. package/template/eslint.config.mjs +36 -0
  246. package/template/gitignore +21 -0
  247. package/template/lib/analytics.ts +369 -0
  248. package/template/lib/api.ts +60 -0
  249. package/template/lib/capi.ts +87 -0
  250. package/template/lib/cart-link.ts +51 -0
  251. package/template/lib/cart-normalize.ts +142 -0
  252. package/template/lib/cart-recovery.ts +23 -0
  253. package/template/lib/cart-response.ts +48 -0
  254. package/template/lib/catalog-map.ts +54 -0
  255. package/template/lib/catalog.ts +3 -0
  256. package/template/lib/checkout-lock.ts +21 -0
  257. package/template/lib/checkout-nav.ts +22 -0
  258. package/template/lib/config.ts +51 -0
  259. package/template/lib/crm.ts +40 -0
  260. package/template/lib/customer-session.ts +21 -0
  261. package/template/lib/dataloader.ts +23 -0
  262. package/template/lib/editable/config.ts +11 -0
  263. package/template/lib/editable/document.ts +1762 -0
  264. package/template/lib/editable/index.ts +3 -0
  265. package/template/lib/editable/primitives.tsx +821 -0
  266. package/template/lib/editable/provider.tsx +861 -0
  267. package/template/lib/editable/rastreio-navegacao.tsx +65 -0
  268. package/template/lib/editable/rastreio.tsx +168 -0
  269. package/template/lib/editable/server.ts +189 -0
  270. package/template/lib/editable/tokens.ts +22 -0
  271. package/template/lib/editable/verify.ts +24 -0
  272. package/template/lib/enrichment/combos.ts +184 -0
  273. package/template/lib/enrichment/index.ts +188 -0
  274. package/template/lib/enrichment/products.json +1 -0
  275. package/template/lib/env-check.ts +47 -0
  276. package/template/lib/format.ts +130 -0
  277. package/template/lib/icons.ts +67 -0
  278. package/template/lib/json-ld.ts +9 -0
  279. package/template/lib/llms-txt.ts +75 -0
  280. package/template/lib/mockup.ts +24 -0
  281. package/template/lib/newsletter.ts +12 -0
  282. package/template/lib/orders.ts +160 -0
  283. package/template/lib/queries.ts +66 -0
  284. package/template/lib/ratelimit.ts +47 -0
  285. package/template/lib/rotas-editaveis.ts +192 -0
  286. package/template/lib/sanitize.ts +36 -0
  287. package/template/lib/schemas.ts +78 -0
  288. package/template/lib/session.ts +98 -0
  289. package/template/lib/store-config.ts +67 -0
  290. package/template/lib/unbox/client.ts +829 -0
  291. package/template/lib/unbox/customer.ts +224 -0
  292. package/template/lib/unbox/errors.ts +97 -0
  293. package/template/lib/unbox/index.ts +10 -0
  294. package/template/lib/unbox/store.ts +113 -0
  295. package/template/lib/unbox/types.ts +195 -0
  296. package/template/lib/unbox/webhooks.ts +66 -0
  297. package/template/lib/utils.ts +6 -0
  298. package/template/lib/vitrine.ts +242 -0
  299. package/template/lib/webhook-store.ts +22 -0
  300. package/template/middleware.ts +178 -0
  301. package/template/next.config.ts +63 -0
  302. package/template/package.json +62 -0
  303. package/template/postcss.config.mjs +5 -0
  304. package/template/public/brand/coll/default.png +0 -0
  305. package/template/public/brand/hero-desktop.svg +12 -0
  306. package/template/public/brand/hero-mobile.svg +12 -0
  307. package/template/public/brand/heros/boutique-desktop.svg +11 -0
  308. package/template/public/brand/heros/boutique-mobile.svg +11 -0
  309. package/template/public/brand/heros/editorial-desktop.svg +11 -0
  310. package/template/public/brand/heros/editorial-mobile.svg +11 -0
  311. package/template/public/brand/heros/essencial-desktop.svg +12 -0
  312. package/template/public/brand/heros/essencial-mobile.svg +12 -0
  313. package/template/public/brand/heros/promocional-desktop.svg +12 -0
  314. package/template/public/brand/heros/promocional-mobile.svg +12 -0
  315. package/template/public/brand/logo-chrome.svg +6 -0
  316. package/template/public/brand/logo-white.svg +6 -0
  317. package/template/public/brand/logo.svg +7 -0
  318. package/template/public/brand/pay-amex.webp +0 -0
  319. package/template/public/brand/pay-elo.webp +0 -0
  320. package/template/public/brand/pay-mastercard.webp +0 -0
  321. package/template/public/brand/pay-pix.webp +0 -0
  322. package/template/public/brand/pay-visa.webp +0 -0
  323. package/template/public/brand/ph/avatar-a.svg +5 -0
  324. package/template/public/brand/ph/avatar-b.svg +5 -0
  325. package/template/public/brand/ph/avatar-c.svg +5 -0
  326. package/template/public/brand/ph/avatar-d.svg +5 -0
  327. package/template/public/brand/ph/photo-a.svg +7 -0
  328. package/template/public/brand/ph/photo-b.svg +6 -0
  329. package/template/public/brand/ph/photo-c.svg +6 -0
  330. package/template/public/brand/ph/poster-a.svg +6 -0
  331. package/template/public/brand/ph/poster-b.svg +6 -0
  332. package/template/public/brand/ph/poster-c.svg +6 -0
  333. package/template/public/unbox/powered-by-fundo-preto.png +0 -0
  334. package/template/public/unbox/powered-by-transparente.png +0 -0
  335. package/template/public/unbox/powered-by.png +0 -0
  336. package/template/scripts/abandoned-cart.ts +82 -0
  337. package/template/scripts/check-editable.mjs +467 -0
  338. package/template/scripts/check-honestidade.mjs +111 -0
  339. package/template/scripts/check-placeholder.mjs +200 -0
  340. package/template/scripts/check-recipe.mjs +76 -0
  341. package/template/scripts/check-unbox-brand.mjs +298 -0
  342. package/template/scripts/contraste.js +177 -0
  343. package/template/scripts/dump-catalog.ts +95 -0
  344. package/template/scripts/load-env.ts +13 -0
  345. package/template/scripts/medir-sistema.mjs +56 -0
  346. package/template/scripts/place-order-pix.ts +61 -0
  347. package/template/scripts/qa-screenshots.mjs +150 -0
  348. package/template/scripts/sistema.py +231 -0
  349. package/template/scripts/subscribe-webhook.ts +36 -0
  350. package/template/scripts/test-live.ts +186 -0
  351. package/template/scripts/vocabulario.py +264 -0
  352. package/template/tsconfig.json +27 -0
  353. package/tools/LEIA-ME.md +39 -0
  354. package/tools/check-template-neutro.mjs +111 -0
  355. package/tools/notion-doc.mjs +191 -0
  356. package/tools/tokenize-neutrals.mjs +0 -0
  357. package/tools/tokenize-radius.mjs +52 -0
  358. package/tools/workflow-storefront.legado.js +1207 -0
@@ -0,0 +1,35 @@
1
+ import { ImageResponse } from "next/og";
2
+
3
+ // Imagem de compartilhamento (Open Graph / Twitter) gerada na hora: nome da loja sobre a cor
4
+ // primária. Existe para NENHUM compartilhamento sair sem imagem — antes saía. Quando a marca
5
+ // tiver uma imagem própria (1200×630), troque por um arquivo public/brand/og.png e aponte
6
+ // `openGraph.images` no layout; até lá, esta serve e nunca é placeholder de outra loja.
7
+ export const runtime = "edge";
8
+ export const alt = "Minha Loja";
9
+ export const size = { width: 1200, height: 630 };
10
+ export const contentType = "image/png";
11
+
12
+ const SITE_NAME = "Minha Loja";
13
+ // Hex literal é obrigatório aqui: o satori (ImageResponse) não lê variável de CSS. O CLI
14
+ // reescreve estas duas linhas com a cor da marca no scaffold (src/theme.js). Se o medidor de
15
+ // sistema acusar estes dois hex, é exceção declarada, não vazamento.
16
+ const PRIMARY = "#18181B";
17
+ const PRIMARY_FG = "#FFFFFF";
18
+
19
+ export default function OgImage() {
20
+ return new ImageResponse(
21
+ (
22
+ <div
23
+ style={{
24
+ width: "100%", height: "100%", display: "flex", flexDirection: "column",
25
+ alignItems: "flex-start", justifyContent: "flex-end", padding: 72,
26
+ background: PRIMARY, color: PRIMARY_FG, fontFamily: "system-ui, sans-serif",
27
+ }}
28
+ >
29
+ <div style={{ fontSize: 84, fontWeight: 800, letterSpacing: -2, lineHeight: 1.05 }}>{SITE_NAME}</div>
30
+ <div style={{ marginTop: 18, fontSize: 30, opacity: 0.8 }}>Loja online</div>
31
+ </div>
32
+ ),
33
+ size,
34
+ );
35
+ }
@@ -0,0 +1,31 @@
1
+ import type { MetadataRoute } from "next";
2
+
3
+ const siteUrl = process.env.NEXT_PUBLIC_SITE_URL ?? "http://localhost:3000";
4
+
5
+ // Áreas privadas/transacionais não indexáveis (doc 10).
6
+ // EXPORTADA de propósito: é a MESMA lista que decide quais páginas o editor da Unbox oferece ao
7
+ // lojista (ver lib/rotas-editaveis.ts). Uma lista só, mantida por quem cuida de SEO: bloquear uma
8
+ // área aqui a tira do editor junto, e não existe segunda lista para alguém esquecer.
9
+ export const DISALLOW = ["/conta", "/carrinho", "/checkout", "/pedido", "/api"];
10
+
11
+ // Agentes de IA explicitamente bem-vindos (descoberta de agentes): conteúdo público —
12
+ // catálogo (/produto/, /categoria/, /produtos) — liberado para indexação e respostas.
13
+ const AI_AGENTS = [
14
+ "GPTBot", "OAI-SearchBot", "ChatGPT-User",
15
+ "ClaudeBot", "Claude-Web", "anthropic-ai",
16
+ "PerplexityBot", "Perplexity-User",
17
+ "Google-Extended", "Applebot-Extended",
18
+ "Amazonbot", "meta-externalagent", "CCBot",
19
+ ];
20
+
21
+ export default function robots(): MetadataRoute.Robots {
22
+ return {
23
+ rules: [
24
+ { userAgent: "*", allow: "/", disallow: DISALLOW },
25
+ // mesma política para os agentes de IA — público liberado, privado bloqueado
26
+ { userAgent: AI_AGENTS, allow: ["/", "/produto/", "/categoria/", "/produtos"], disallow: DISALLOW },
27
+ ],
28
+ sitemap: `${siteUrl}/sitemap.xml`,
29
+ host: siteUrl,
30
+ };
31
+ }
@@ -0,0 +1,42 @@
1
+ import type { MetadataRoute } from "next";
2
+ import { loadAllCatalogItems } from "@/lib/dataloader";
3
+ import { getTopTags } from "@/lib/queries";
4
+
5
+ export const revalidate = 3600;
6
+
7
+ const siteUrl = process.env.NEXT_PUBLIC_SITE_URL ?? "http://localhost:3000";
8
+
9
+ export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
10
+ const base: MetadataRoute.Sitemap = [
11
+ { url: siteUrl, changeFrequency: "daily", priority: 1 },
12
+ { url: `${siteUrl}/produtos`, changeFrequency: "daily", priority: 0.9 },
13
+ ];
14
+
15
+ // produtos (paginado por offset até hasNextPage===false; só os visíveis — doc 10)
16
+ let products: MetadataRoute.Sitemap = [];
17
+ try {
18
+ const items = await loadAllCatalogItems();
19
+ products = items
20
+ .map((n: any) => n.product)
21
+ .filter((p: any) => p && p.isVisible !== false && p.slug)
22
+ .map((p: any) => ({
23
+ url: `${siteUrl}/produto/${encodeURIComponent(p.slug)}`,
24
+ changeFrequency: "weekly" as const,
25
+ priority: 0.8,
26
+ }));
27
+ } catch {
28
+ /* falha de rede não deve quebrar o sitemap inteiro */
29
+ }
30
+
31
+ let categories: MetadataRoute.Sitemap = [];
32
+ try {
33
+ const tags = await getTopTags();
34
+ categories = (tags as any[])
35
+ .filter((t) => t.isVisible !== false && t.slug)
36
+ .map((t) => ({ url: `${siteUrl}/categoria/${encodeURIComponent(t.slug)}`, changeFrequency: "weekly" as const, priority: 0.6 }));
37
+ } catch {
38
+ /* idem */
39
+ }
40
+
41
+ return [...base, ...categories, ...products];
42
+ }
@@ -0,0 +1,87 @@
1
+ #!/bin/bash
2
+ set -e
3
+
4
+ echo ""
5
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
6
+ echo " Unbox Storefront — Bootstrap"
7
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
8
+ echo ""
9
+
10
+ # ── 1. Credenciais ───────────────────────────────────────────────────────────
11
+ echo "Você já tem as credenciais da loja Unbox?"
12
+ echo "(UNBOX_API_KEY, UNBOX_USER, UNBOX_PASS)"
13
+ read -rp " [s/N] " HAS_CREDS
14
+ echo ""
15
+
16
+ # ── 2. CRO Package ───────────────────────────────────────────────────────────
17
+ echo "Gostaria de ativar o Unbox AI CRO Package?"
18
+ echo ""
19
+ echo " Módulos disponíveis:"
20
+ echo " [1] Prova Social no PDP — badge de vendas + avaliações"
21
+ echo " [2] Urgência de Estoque — 'Restam X unidades'"
22
+ echo " [3] Countdown Flash Sale — banner com temporizador"
23
+ echo " [4] Barra de Frete Grátis — progress bar no carrinho"
24
+ echo " [5] Upsell Pós-Checkout — recomendações na confirmação"
25
+ echo " [6] Recently Viewed — produtos vistos (localStorage)"
26
+ echo " [7] Sticky CTA Mobile — botão fixo no scroll do PDP"
27
+ echo " [8] Trust Strip Customizável — ícones de confiança"
28
+ echo " [9] Kits & Combos com Cross-sell — seção de kits na home"
29
+ echo " [10] Brindes (Gift with Purchase) — brinde ao atingir valor mínimo"
30
+ echo ""
31
+ echo " ⚠️ Módulos 4 e 10 precisam de configuração no painel Unbox antes do lançamento."
32
+ echo ""
33
+ echo " Digite 'all', números separados por vírgula (ex: 1,4,7), ou Enter para pular."
34
+ read -rp " CRO modules: " CRO_CHOICE
35
+ echo ""
36
+
37
+ # Salva escolha para o agente 13-cro usar depois
38
+ if [ -n "$CRO_CHOICE" ] && [ "$CRO_CHOICE" != "none" ]; then
39
+ echo "$CRO_CHOICE" > .cro-modules
40
+ echo " ✓ Escolha salva em .cro-modules"
41
+ echo " Execute o agente 13-cro após o setup para implementar os módulos."
42
+ echo " (agents/definitions/13-cro.md)"
43
+ else
44
+ echo " CRO Package pulado. Você pode ativar depois com o agente 13-cro."
45
+ fi
46
+ echo ""
47
+
48
+ # ── 3. Node 22 LTS ───────────────────────────────────────────────────────────
49
+ echo "→ Garantindo Node 22 LTS..."
50
+ nvm install 22 --silent && nvm use 22 --silent
51
+
52
+ # ── 4. Remove lock file global que conflita com npm ──────────────────────────
53
+ rm -f ~/package-lock.json
54
+
55
+ # ── 5. Dependências ──────────────────────────────────────────────────────────
56
+ echo "→ Instalando dependências (pode demorar ~2 min)..."
57
+ npm install --silent
58
+
59
+ # ── 6. .env.local ────────────────────────────────────────────────────────────
60
+ if [ ! -f .env.local ]; then
61
+ cp .env.example .env.local
62
+ fi
63
+
64
+ # ── 7. Modo com ou sem credenciais ───────────────────────────────────────────
65
+ if [[ "$HAS_CREDS" =~ ^[Ss]$ ]]; then
66
+ echo ""
67
+ echo " Preencha .env.local com as credenciais antes de continuar:"
68
+ echo " UNBOX_API_KEY, UNBOX_USER, UNBOX_PASS, SESSION_SECRET, NEXT_PUBLIC_SITE_URL"
69
+ echo ""
70
+ read -rp " Pressione Enter quando .env.local estiver preenchido..."
71
+ echo ""
72
+ echo "→ Testando conexão com a API Unbox..."
73
+ npm run unbox:test
74
+ echo ""
75
+ echo " Conectado. Rodando em modo completo."
76
+ else
77
+ echo " MODO MOCKUP — o layout vai abrir mas páginas que dependem"
78
+ echo " da API (catálogo, produto, checkout) vão mostrar erro."
79
+ echo " Preencha .env.local e rode 'npm run unbox:test' antes do QA."
80
+ echo ""
81
+ fi
82
+
83
+ echo ""
84
+ echo "→ Iniciando dev server..."
85
+ echo " (primeira compilação demora ~3 min — aguarde o 'Ready' aparecer)"
86
+ echo ""
87
+ npm run dev
@@ -0,0 +1,47 @@
1
+ import Link from "next/link";
2
+ import { House, Package, Gear, CaretRight } from "@phosphor-icons/react/dist/ssr";
3
+ import { SignOutButton } from "@/components/account/signout-button";
4
+
5
+ // Menu enxuto por decisão de produto: Assinaturas e Endereços saíram da navegação
6
+ // (as rotas /conta/assinaturas e /conta/enderecos continuam existindo pra deep links,
7
+ // ex.: e-mail de assinatura). Pra reexibir, basta recolocar os itens aqui.
8
+ const NAV = [
9
+ { href: "/conta", label: "Visão geral", icon: House },
10
+ { href: "/conta/pedidos", label: "Pedidos", icon: Package },
11
+ { href: "/conta/preferencias", label: "Preferências", icon: Gear },
12
+ ];
13
+
14
+ export function AccountShell({ title, children }: { title: string; children: React.ReactNode }) {
15
+ return (
16
+ <div className="store-layout full-bleed bg-white text-[var(--store-ink)]">
17
+ <div className="mx-auto max-w-[1240px] px-4 py-7 sm:px-6">
18
+ {/* breadcrumb */}
19
+ <div className="flex items-center gap-2 text-[13px] font-medium text-[var(--store-muted)]">
20
+ <Link href="/" className="no-underline hover:text-[var(--store-ink)]">Início</Link>
21
+ <CaretRight className="text-[11px]" />
22
+ <span className="font-semibold text-[var(--store-ink)]">Minha conta</span>
23
+ </div>
24
+
25
+ <div className="mt-6 grid items-start gap-7 md:grid-cols-[248px_1fr]">
26
+ <aside className="h-fit rounded-xl border border-[var(--store-line)] bg-white p-3 md:sticky md:top-6">
27
+ <nav className="flex flex-col gap-1">
28
+ {NAV.map(({ href, label, icon: Icon }) => (
29
+ <Link key={href} href={href} className="flex items-center gap-2.5 rounded-xl px-3.5 py-2.5 text-[14px] font-semibold text-[var(--store-ink-2)] no-underline transition-colors hover:bg-[var(--store-primary-soft,#F1F1F3)] hover:text-[var(--store-primary,#18181B)]">
30
+ <Icon weight="bold" className="text-[18px] text-[var(--store-primary,#18181B)]" /> {label}
31
+ </Link>
32
+ ))}
33
+ <div className="mt-1.5 border-t border-[var(--store-surface-2)] pt-1.5">
34
+ <SignOutButton />
35
+ </div>
36
+ </nav>
37
+ </aside>
38
+
39
+ <section>
40
+ <h1 className="font-display mb-5 text-[27px] font-extrabold leading-[1.12]">{title}</h1>
41
+ {children}
42
+ </section>
43
+ </div>
44
+ </div>
45
+ </div>
46
+ );
47
+ }
@@ -0,0 +1,147 @@
1
+ "use client";
2
+
3
+ import * as React from "react";
4
+ import { useRouter } from "next/navigation";
5
+ import { toast } from "sonner";
6
+ import { Plus, PencilSimple, Trash, CircleNotch } from "@phosphor-icons/react/dist/ssr";
7
+ import {
8
+ Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter, DialogDescription,
9
+ } from "@/components/ui/dialog";
10
+ import { AddressFields, emptyAddress, type AddressValue } from "@/components/address-fields";
11
+ import { EmptyState } from "@/components/empty-state";
12
+
13
+ const BTN_PRIMARY = "font-display inline-flex h-11 cursor-pointer items-center justify-center gap-2 rounded-xl bg-[var(--store-primary,#18181B)] px-5 text-[14px] font-bold text-white transition-colors hover:bg-[var(--store-primary-dark,#09090B)] disabled:opacity-60";
14
+ const BTN_OUTLINE = "inline-flex h-11 cursor-pointer items-center justify-center gap-2 rounded-xl border-[1.5px] border-[var(--store-line-2)] bg-white px-4 text-[14px] font-bold text-[var(--store-ink-2)] transition-colors hover:border-[var(--store-primary,#18181B)] hover:text-[var(--store-primary,#18181B)]";
15
+
16
+ interface AddressBookItem {
17
+ _id: string;
18
+ alias?: string;
19
+ fullName?: string;
20
+ postal?: string;
21
+ address1?: string;
22
+ number?: string;
23
+ neighborhood?: string;
24
+ city?: string;
25
+ region?: string;
26
+ isShippingDefault?: boolean;
27
+ isBillingDefault?: boolean;
28
+ }
29
+
30
+ export function AddressBook({ initial }: { initial: AddressBookItem[] }) {
31
+ const router = useRouter();
32
+ const [open, setOpen] = React.useState(false);
33
+ const [editingId, setEditingId] = React.useState<string | undefined>();
34
+ const [value, setValue] = React.useState<AddressValue>(emptyAddress);
35
+ const [busy, setBusy] = React.useState(false);
36
+
37
+ const openNew = () => {
38
+ setEditingId(undefined);
39
+ setValue(emptyAddress);
40
+ setOpen(true);
41
+ };
42
+ const openEdit = (a: AddressBookItem) => {
43
+ setEditingId(a._id);
44
+ setValue({
45
+ ...emptyAddress,
46
+ fullName: a.fullName ?? "",
47
+ postal: a.postal ?? "",
48
+ address1: a.address1 ?? "",
49
+ number: a.number ?? "",
50
+ neighborhood: a.neighborhood ?? "",
51
+ city: a.city ?? "",
52
+ region: a.region ?? "",
53
+ });
54
+ setOpen(true);
55
+ };
56
+
57
+ async function save() {
58
+ setBusy(true);
59
+ try {
60
+ const res = await fetch("/api/account/addresses", {
61
+ method: "POST",
62
+ headers: { "Content-Type": "application/json" },
63
+ body: JSON.stringify({ address: { ...value, _id: editingId } }),
64
+ });
65
+ const data = await res.json().catch(() => ({}));
66
+ if (!res.ok) throw new Error(data?.error || "Erro ao salvar.");
67
+ toast.success("Endereço salvo.");
68
+ setOpen(false);
69
+ router.refresh();
70
+ } catch (e: any) {
71
+ toast.error(e.message);
72
+ } finally {
73
+ setBusy(false);
74
+ }
75
+ }
76
+
77
+ async function remove(id: string) {
78
+ try {
79
+ const res = await fetch("/api/account/addresses", {
80
+ method: "DELETE",
81
+ headers: { "Content-Type": "application/json" },
82
+ body: JSON.stringify({ ids: [id] }),
83
+ });
84
+ if (!res.ok) throw new Error((await res.json().catch(() => ({})))?.error || "Erro ao remover.");
85
+ toast.success("Endereço removido.");
86
+ router.refresh();
87
+ } catch (e: any) {
88
+ toast.error(e.message);
89
+ }
90
+ }
91
+
92
+ return (
93
+ <div>
94
+ <div className="mb-4 flex justify-end">
95
+ <button type="button" onClick={openNew} className={BTN_PRIMARY}><Plus weight="bold" /> Novo endereço</button>
96
+ </div>
97
+
98
+ {initial.length === 0 ? (
99
+ <EmptyState title="Nenhum endereço salvo" description="Adicione um endereço para agilizar suas compras." />
100
+ ) : (
101
+ <ul className="grid gap-3 sm:grid-cols-2">
102
+ {initial.map((a) => (
103
+ <li key={a._id} className="flex flex-col rounded-lg border border-[var(--store-line)] bg-white p-4">
104
+ <div className="text-sm">
105
+ <p className="font-display text-[15px] font-bold text-[var(--store-ink)]">{a.alias || a.fullName}</p>
106
+ <p className="mt-0.5 text-[var(--store-ink-2)]">{a.address1}, {a.number} · {a.neighborhood}</p>
107
+ <p className="text-[var(--store-muted)]">{a.city}/{a.region} · {a.postal}</p>
108
+ {(a.isShippingDefault || a.isBillingDefault) && (
109
+ <div className="mt-2.5 flex flex-wrap gap-1.5">
110
+ {a.isShippingDefault && <Tag>Entrega padrão</Tag>}
111
+ {a.isBillingDefault && <Tag>Cobrança padrão</Tag>}
112
+ </div>
113
+ )}
114
+ </div>
115
+ <div className="mt-3.5 flex gap-2 border-t border-[var(--store-surface-2)] pt-3">
116
+ <button type="button" onClick={() => openEdit(a)} className="inline-flex cursor-pointer items-center gap-1.5 text-[13px] font-bold text-[var(--store-primary,#18181B)]"><PencilSimple weight="bold" /> Editar</button>
117
+ <button type="button" onClick={() => remove(a._id)} className="inline-flex cursor-pointer items-center gap-1.5 text-[13px] font-bold text-[var(--store-muted)] transition-colors hover:text-[var(--store-sale)]"><Trash weight="bold" /> Remover</button>
118
+ </div>
119
+ </li>
120
+ ))}
121
+ </ul>
122
+ )}
123
+
124
+ <Dialog open={open} onOpenChange={setOpen}>
125
+ <DialogContent className="max-w-lg">
126
+ <DialogHeader>
127
+ <DialogTitle>{editingId ? "Editar endereço" : "Novo endereço"}</DialogTitle>
128
+ <DialogDescription>Preencha os dados de entrega.</DialogDescription>
129
+ </DialogHeader>
130
+ <AddressFields value={value} onChange={setValue} />
131
+ <DialogFooter>
132
+ <button type="button" onClick={() => setOpen(false)} className={BTN_OUTLINE}>Cancelar</button>
133
+ <button type="button" onClick={save} disabled={busy} className={BTN_PRIMARY}>
134
+ {busy ? <CircleNotch className="animate-spin" /> : null} Salvar
135
+ </button>
136
+ </DialogFooter>
137
+ </DialogContent>
138
+ </Dialog>
139
+ </div>
140
+ );
141
+ }
142
+
143
+ function Tag({ children }: { children: React.ReactNode }) {
144
+ return (
145
+ <span className="inline-flex items-center rounded-full border border-[var(--store-primary-soft)] bg-[var(--store-primary-soft,#F1F1F3)] px-2.5 py-0.5 text-[11.5px] font-bold text-[var(--store-primary,#18181B)]">{children}</span>
146
+ );
147
+ }
@@ -0,0 +1,77 @@
1
+ "use client";
2
+
3
+ import * as React from "react";
4
+ import { toast } from "sonner";
5
+
6
+ export function PreferencesForm({
7
+ initial,
8
+ }: {
9
+ initial: { receiveNewOrderEmail: boolean; reuseDataBetweenShops: boolean };
10
+ }) {
11
+ const [state, setState] = React.useState(initial);
12
+ const [saving, setSaving] = React.useState(false);
13
+
14
+ async function update(patch: Partial<typeof state>) {
15
+ const prev = state;
16
+ const next = { ...state, ...patch };
17
+ setState(next);
18
+ setSaving(true);
19
+ try {
20
+ const res = await fetch("/api/account/preferences", {
21
+ method: "PATCH",
22
+ headers: { "Content-Type": "application/json" },
23
+ body: JSON.stringify(patch),
24
+ });
25
+ if (!res.ok) throw new Error((await res.json().catch(() => ({})))?.error || "Erro ao salvar.");
26
+ toast.success("Preferências atualizadas.");
27
+ } catch (e: any) {
28
+ toast.error(e.message);
29
+ setState(prev); // reverte
30
+ } finally {
31
+ setSaving(false);
32
+ }
33
+ }
34
+
35
+ return (
36
+ <div className="flex flex-col divide-y divide-[var(--store-surface-2)] rounded-xl border border-[var(--store-line)] bg-white px-5">
37
+ <Row
38
+ label="Receber e-mail a cada novo pedido"
39
+ desc="Avisamos quando um pedido é criado."
40
+ checked={state.receiveNewOrderEmail}
41
+ disabled={saving}
42
+ onChange={(v) => update({ receiveNewOrderEmail: v })}
43
+ />
44
+ <Row
45
+ label="Reutilizar meus dados entre lojas Unbox"
46
+ desc="Agiliza o checkout em outras lojas da rede."
47
+ checked={state.reuseDataBetweenShops}
48
+ disabled={saving}
49
+ onChange={(v) => update({ reuseDataBetweenShops: v })}
50
+ />
51
+ </div>
52
+ );
53
+ }
54
+
55
+ function Row({ label, desc, checked, onChange, disabled }: {
56
+ label: string; desc: string; checked: boolean; onChange: (v: boolean) => void; disabled?: boolean;
57
+ }) {
58
+ return (
59
+ <div className="flex items-center justify-between gap-4 py-4">
60
+ <div>
61
+ <div className="text-[14.5px] font-bold text-[var(--store-ink)]">{label}</div>
62
+ <div className="mt-0.5 text-[13px] text-[var(--store-muted)]">{desc}</div>
63
+ </div>
64
+ <button
65
+ type="button"
66
+ role="switch"
67
+ aria-checked={checked}
68
+ disabled={disabled}
69
+ onClick={() => onChange(!checked)}
70
+ className="relative h-[26px] w-[46px] shrink-0 cursor-pointer rounded-full transition-colors disabled:opacity-60"
71
+ style={{ background: checked ? "var(--store-primary,#18181B)" : "var(--store-line-2)" }}
72
+ >
73
+ <span className="absolute top-[3px] h-[20px] w-[20px] rounded-full bg-white shadow-[0_1px_3px_rgba(0,0,0,.2)] transition-all" style={{ left: checked ? 23 : 3 }} />
74
+ </button>
75
+ </div>
76
+ );
77
+ }
@@ -0,0 +1,47 @@
1
+ "use client";
2
+
3
+ import * as React from "react";
4
+ import { ArrowsClockwise, Spinner } from "@phosphor-icons/react/dist/ssr";
5
+ import { useCart } from "@/components/cart/cart-provider";
6
+
7
+ export interface ReorderItem {
8
+ productId: string;
9
+ variantId: string;
10
+ price: number;
11
+ quantity: number;
12
+ title?: string;
13
+ thumbnail?: string;
14
+ }
15
+
16
+ export function ReorderButton({ items }: { items: ReorderItem[] }) {
17
+ const { add, setOpen } = useCart();
18
+ const [busy, setBusy] = React.useState(false);
19
+
20
+ if (items.length === 0) return null;
21
+
22
+ async function reorder() {
23
+ setBusy(true);
24
+ try {
25
+ for (const it of items) {
26
+ await add(
27
+ { productId: it.productId, variantId: it.variantId, price: it.price, quantity: it.quantity, title: it.title, thumbnail: it.thumbnail },
28
+ { silent: true },
29
+ );
30
+ }
31
+ setOpen(true);
32
+ } finally {
33
+ setBusy(false);
34
+ }
35
+ }
36
+
37
+ return (
38
+ <button
39
+ type="button"
40
+ onClick={reorder}
41
+ disabled={busy}
42
+ className="font-display inline-flex h-11 cursor-pointer items-center justify-center gap-2 rounded-xl bg-[var(--store-primary,#18181B)] px-5 text-[14px] font-bold text-white transition-colors hover:bg-[var(--store-primary-dark,#09090B)] disabled:opacity-60"
43
+ >
44
+ {busy ? <Spinner className="animate-spin" /> : <ArrowsClockwise weight="bold" />} Refazer pedido
45
+ </button>
46
+ );
47
+ }
@@ -0,0 +1,20 @@
1
+ "use client";
2
+
3
+ import { SignOut } from "@phosphor-icons/react/dist/ssr";
4
+
5
+ export function SignOutButton() {
6
+ return (
7
+ <button
8
+ type="button"
9
+ onClick={async () => {
10
+ await fetch("/api/account/signout", { method: "POST" });
11
+ // Documento inteiro (não router.push): garante que nenhum payload RSC logado
12
+ // sobreviva ao fim da sessão — mesma classe de bug do pós-login.
13
+ window.location.assign("/");
14
+ }}
15
+ className="flex w-full cursor-pointer items-center gap-2.5 rounded-xl px-3.5 py-2.5 text-[14px] font-semibold text-[var(--store-muted)] transition-colors hover:bg-[var(--store-sale-soft)] hover:text-[var(--store-sale)]"
16
+ >
17
+ <SignOut weight="bold" className="text-[18px]" /> Sair
18
+ </button>
19
+ );
20
+ }