cloudcommerce 0.22.0 → 0.22.2

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 (182) hide show
  1. package/.editorconfig +13 -0
  2. package/.gitmodules +3 -0
  3. package/.vscode/settings.json +2 -1
  4. package/CHANGELOG.md +28 -0
  5. package/ecomplus-stores/barra-doce/.devcontainer/devcontainer.json +30 -0
  6. package/ecomplus-stores/barra-doce/.editorconfig +13 -0
  7. package/ecomplus-stores/barra-doce/.eslintrc.cjs +3 -0
  8. package/ecomplus-stores/barra-doce/.firebaserc +5 -0
  9. package/ecomplus-stores/barra-doce/.github/build-and-deploy +1 -0
  10. package/ecomplus-stores/barra-doce/.github/renovate.json +5 -0
  11. package/ecomplus-stores/barra-doce/.github/workflows/build-and-deploy.yml +36 -0
  12. package/ecomplus-stores/barra-doce/.github/workflows/calibreapp-image-actions.yml +23 -0
  13. package/ecomplus-stores/barra-doce/.gitpod.yml +12 -0
  14. package/ecomplus-stores/barra-doce/.nvmrc +1 -0
  15. package/ecomplus-stores/barra-doce/.vscode/extensions.json +8 -0
  16. package/ecomplus-stores/barra-doce/.vscode/launch.json +11 -0
  17. package/ecomplus-stores/barra-doce/.vscode/settings.json +10 -0
  18. package/ecomplus-stores/barra-doce/LICENSE.md +230 -0
  19. package/ecomplus-stores/barra-doce/README.md +31 -0
  20. package/ecomplus-stores/barra-doce/SETUP.md +117 -0
  21. package/ecomplus-stores/{iluminim/README.pt-BR.md → barra-doce/SETUP.pt-BR.md} +5 -1
  22. package/ecomplus-stores/barra-doce/functions/config.json +3 -0
  23. package/ecomplus-stores/barra-doce/functions/example.env +10 -0
  24. package/ecomplus-stores/barra-doce/functions/many/index.js +14 -0
  25. package/ecomplus-stores/barra-doce/functions/many/package.json +22 -0
  26. package/ecomplus-stores/barra-doce/functions/ssr/.eslintrc.cjs +6 -0
  27. package/ecomplus-stores/barra-doce/functions/ssr/astro.config.mjs +4 -0
  28. package/ecomplus-stores/barra-doce/functions/ssr/content/blog/.gitkeep +0 -0
  29. package/ecomplus-stores/barra-doce/functions/ssr/content/extra-pages/.gitkeep +0 -0
  30. package/ecomplus-stores/barra-doce/functions/ssr/content/layout.json +37 -0
  31. package/ecomplus-stores/barra-doce/functions/ssr/content/pages/home.json +33 -0
  32. package/ecomplus-stores/barra-doce/functions/ssr/content/settings.json +48 -0
  33. package/ecomplus-stores/barra-doce/functions/ssr/index.js +18 -0
  34. package/ecomplus-stores/barra-doce/functions/ssr/package.json +31 -0
  35. package/ecomplus-stores/barra-doce/functions/ssr/public/admin/.gitkeep +2 -0
  36. package/ecomplus-stores/barra-doce/functions/ssr/public/assets/.gitkeep +2 -0
  37. package/ecomplus-stores/barra-doce/functions/ssr/public/img/icon.png +0 -0
  38. package/ecomplus-stores/barra-doce/functions/ssr/public/img/large-icon.png +0 -0
  39. package/ecomplus-stores/barra-doce/functions/ssr/public/img/uploads/banner-forma-decora.jpg +0 -0
  40. package/ecomplus-stores/barra-doce/functions/ssr/public/img/uploads/banner2.webp +0 -0
  41. package/ecomplus-stores/barra-doce/functions/ssr/public/img/uploads/ecom-icon.png +0 -0
  42. package/ecomplus-stores/barra-doce/functions/ssr/public/img/uploads/headphone.webp +0 -0
  43. package/ecomplus-stores/barra-doce/functions/ssr/public/img/uploads/logo-barradoce.webp +0 -0
  44. package/ecomplus-stores/barra-doce/functions/ssr/public/img/uploads/logo.png +0 -0
  45. package/ecomplus-stores/barra-doce/functions/ssr/public/img/uploads/og-image.png +0 -0
  46. package/ecomplus-stores/barra-doce/functions/ssr/public/img/uploads/passion.webp +0 -0
  47. package/ecomplus-stores/barra-doce/functions/ssr/public/img/uploads/rect8589.png +0 -0
  48. package/ecomplus-stores/barra-doce/functions/ssr/public/img/uploads/rect859.png +0 -0
  49. package/ecomplus-stores/barra-doce/functions/ssr/public/img/uploads/rect89.webp +0 -0
  50. package/ecomplus-stores/barra-doce/functions/ssr/public/robots.txt +8 -0
  51. package/ecomplus-stores/barra-doce/functions/ssr/scripts/build.sh +14 -0
  52. package/ecomplus-stores/barra-doce/functions/ssr/src/assets/style.css +65 -0
  53. package/ecomplus-stores/barra-doce/functions/ssr/src/components/AccountMenu.vue +104 -0
  54. package/ecomplus-stores/barra-doce/functions/ssr/src/components/Banner.vue +64 -0
  55. package/ecomplus-stores/barra-doce/functions/ssr/src/components/BannersGrid.astro +25 -0
  56. package/ecomplus-stores/barra-doce/functions/ssr/src/components/CartSidebar.vue +35 -0
  57. package/ecomplus-stores/barra-doce/functions/ssr/src/components/HeroSlider.vue +51 -0
  58. package/ecomplus-stores/barra-doce/functions/ssr/src/components/PitchBar.vue +53 -0
  59. package/ecomplus-stores/barra-doce/functions/ssr/src/components/Prices.vue +96 -0
  60. package/ecomplus-stores/barra-doce/functions/ssr/src/components/ProductCard.vue +117 -0
  61. package/ecomplus-stores/barra-doce/functions/ssr/src/components/ProductShelf.vue +60 -0
  62. package/ecomplus-stores/barra-doce/functions/ssr/src/components/SearchModal.vue +6 -0
  63. package/ecomplus-stores/barra-doce/functions/ssr/src/components/ShopHeader.vue +136 -0
  64. package/ecomplus-stores/barra-doce/functions/ssr/src/components/ShopHeaderMenu.vue +58 -0
  65. package/ecomplus-stores/barra-doce/functions/ssr/src/components/ShopHeaderSubmenu.vue +88 -0
  66. package/ecomplus-stores/barra-doce/functions/ssr/src/components/ShopSidenav.vue +61 -0
  67. package/ecomplus-stores/barra-doce/functions/ssr/src/components/ShopSidenavCategory.vue +80 -0
  68. package/ecomplus-stores/barra-doce/functions/ssr/src/env.d.ts +13 -0
  69. package/ecomplus-stores/barra-doce/functions/ssr/src/layouts/Base.astro +15 -0
  70. package/ecomplus-stores/barra-doce/functions/ssr/src/layouts/Checkout.astro +0 -0
  71. package/ecomplus-stores/barra-doce/functions/ssr/src/layouts/PageHeader.astro +38 -0
  72. package/ecomplus-stores/barra-doce/functions/ssr/src/main/Fallback.astro +10 -0
  73. package/ecomplus-stores/barra-doce/functions/ssr/src/main/Home.astro +29 -0
  74. package/ecomplus-stores/barra-doce/functions/ssr/src/main/Sections.astro +29 -0
  75. package/ecomplus-stores/barra-doce/functions/ssr/src/main/Wildcard.astro +18 -0
  76. package/ecomplus-stores/barra-doce/functions/ssr/src/pages/[...slug].astro +40 -0
  77. package/ecomplus-stores/barra-doce/functions/ssr/src/pages/_vue.ts +3 -0
  78. package/ecomplus-stores/barra-doce/functions/ssr/src/pages/app/account.astro +0 -0
  79. package/ecomplus-stores/barra-doce/functions/ssr/src/pages/app/index.astro +0 -0
  80. package/ecomplus-stores/barra-doce/functions/ssr/src/pages/fallback.astro +25 -0
  81. package/ecomplus-stores/barra-doce/functions/ssr/src/pages/index.astro +35 -0
  82. package/ecomplus-stores/barra-doce/functions/ssr/src/scripts/InlineScripts.astro +10 -0
  83. package/ecomplus-stores/barra-doce/functions/ssr/tailwind.config.cjs +19 -0
  84. package/ecomplus-stores/barra-doce/functions/ssr/tsconfig.json +12 -0
  85. package/ecomplus-stores/barra-doce/functions/ssr/uno.config.cjs +5 -0
  86. package/ecomplus-stores/barra-doce/functions/with-apps/index.js +12 -0
  87. package/ecomplus-stores/barra-doce/functions/with-apps/package.json +22 -0
  88. package/ecomplus-stores/barra-doce/package.json +28 -0
  89. package/ecomplus-stores/barra-doce/scripts/install.sh +14 -0
  90. package/ecomplus-stores/iluminim/.editorconfig +13 -0
  91. package/ecomplus-stores/iluminim/README.md +11 -93
  92. package/ecomplus-stores/iluminim/SETUP.md +117 -0
  93. package/ecomplus-stores/iluminim/SETUP.pt-BR.md +117 -0
  94. package/ecomplus-stores/iluminim/functions/many/package.json +3 -3
  95. package/ecomplus-stores/iluminim/functions/ssr/package.json +6 -6
  96. package/ecomplus-stores/iluminim/functions/ssr/src/components/ShopHeaderSubmenu.vue +1 -1
  97. package/ecomplus-stores/iluminim/functions/ssr/src/components/ShopSidenav.vue +2 -2
  98. package/ecomplus-stores/iluminim/functions/ssr/src/components/ShopSidenavCategory.vue +4 -2
  99. package/ecomplus-stores/iluminim/functions/ssr/src/layouts/PageHeader.astro +6 -1
  100. package/ecomplus-stores/iluminim/functions/with-apps/package.json +3 -3
  101. package/ecomplus-stores/iluminim/package.json +1 -1
  102. package/package.json +6 -6
  103. package/packages/api/package.json +1 -1
  104. package/packages/apps/affilate-program/package.json +1 -1
  105. package/packages/apps/correios/package.json +1 -1
  106. package/packages/apps/custom-payment/package.json +1 -1
  107. package/packages/apps/custom-shipping/package.json +1 -1
  108. package/packages/apps/datafrete/package.json +1 -1
  109. package/packages/apps/discounts/package.json +1 -1
  110. package/packages/apps/emails/package.json +1 -1
  111. package/packages/apps/fb-conversions/package.json +2 -2
  112. package/packages/apps/flash-courier/package.json +1 -1
  113. package/packages/apps/frenet/package.json +1 -1
  114. package/packages/apps/galaxpay/package.json +1 -1
  115. package/packages/apps/google-analytics/package.json +1 -1
  116. package/packages/apps/jadlog/package.json +1 -1
  117. package/packages/apps/loyalty-points/package.json +1 -1
  118. package/packages/apps/melhor-envio/package.json +1 -1
  119. package/packages/apps/mercadopago/package.json +1 -1
  120. package/packages/apps/pagarme/package.json +1 -1
  121. package/packages/apps/paghiper/package.json +1 -1
  122. package/packages/apps/pix/package.json +1 -1
  123. package/packages/apps/tiny-erp/package.json +1 -1
  124. package/packages/apps/webhooks/package.json +1 -1
  125. package/packages/cli/lib/build.js +15 -4
  126. package/packages/cli/lib/cli.js +3 -3
  127. package/packages/cli/package.json +1 -1
  128. package/packages/cli/src/build.ts +17 -4
  129. package/packages/cli/src/cli.ts +3 -3
  130. package/packages/config/package.json +1 -1
  131. package/packages/emails/package.json +1 -1
  132. package/packages/events/package.json +1 -1
  133. package/packages/feeds/package.json +1 -1
  134. package/packages/firebase/package.json +1 -1
  135. package/packages/i18n/lib/dirname.js +3 -1
  136. package/packages/i18n/lib/dirname.js.map +1 -1
  137. package/packages/i18n/package.json +1 -1
  138. package/packages/i18n/scripts/build-txt.mjs +2 -1
  139. package/packages/i18n/src/dirname.ts +3 -1
  140. package/packages/modules/package.json +1 -1
  141. package/packages/passport/package.json +1 -1
  142. package/packages/ssr/package.json +2 -2
  143. package/packages/storefront/astro.config.mjs +2 -1
  144. package/packages/storefront/dist/client/_astro/HeroSlider.cea1f361.js +1 -0
  145. package/packages/storefront/dist/client/_astro/{ProductShelf.9e290480.js → ProductShelf.35614736.js} +1 -1
  146. package/packages/storefront/dist/client/_astro/ShopHeader.dbe9c1f1.js +4 -0
  147. package/packages/storefront/dist/client/_astro/{_...slug_.c85b8978.css → _...slug_.c13e0486.css} +1 -1
  148. package/packages/storefront/dist/client/assets/.gitkeep +2 -0
  149. package/packages/storefront/dist/client/sw.js +1 -1
  150. package/packages/storefront/dist/server/chunks/{_...7e1df40a.mjs → _...fac94758.mjs} +3 -3
  151. package/packages/storefront/dist/server/chunks/{account@_@astro.5f1dba7a.mjs → account@_@astro.460623ee.mjs} +3 -3
  152. package/packages/storefront/dist/server/chunks/{astro.2d3ebf0f.mjs → astro.e0968ba7.mjs} +201 -156
  153. package/packages/storefront/dist/server/chunks/{endpoint@_@js.80de0568.mjs → endpoint@_@js.35380ea5.mjs} +3 -3
  154. package/packages/storefront/dist/server/chunks/{fallback@_@astro.02a45b47.mjs → fallback@_@astro.dbc26d2d.mjs} +3 -3
  155. package/packages/storefront/dist/server/chunks/{index@_@astro.7cb07e52.mjs → index@_@astro.c91e679e.mjs} +3 -3
  156. package/packages/storefront/dist/server/chunks/{index@_@astro.23b50301.mjs → index@_@astro.fec107b9.mjs} +3 -3
  157. package/packages/storefront/dist/server/chunks/pages/{_...slug_.astro.4358f614.mjs → _...slug_.astro.2e2d4770.mjs} +81 -39
  158. package/packages/storefront/dist/server/chunks/pages/{account.astro.afae5a12.mjs → account.astro.0003ca94.mjs} +1 -1
  159. package/packages/storefront/dist/server/chunks/pages/{endpoint.js.60fd21aa.mjs → endpoint.js.6f0f4ede.mjs} +1 -1
  160. package/packages/storefront/dist/server/chunks/pages/{fallback.astro.e5566925.mjs → fallback.astro.fc8bc7e7.mjs} +2 -2
  161. package/packages/storefront/dist/server/chunks/pages/{index.astro.5bc6b9d8.mjs → index.astro.03adcc55.mjs} +6 -6
  162. package/packages/storefront/dist/server/entry.mjs +9 -9
  163. package/packages/storefront/dist/server/renderers.mjs +2 -2
  164. package/packages/storefront/package.json +5 -5
  165. package/packages/storefront/src/lib/$storefront.d.ts +1 -0
  166. package/packages/storefront/src/lib/components/SharedData.astro +13 -0
  167. package/packages/storefront/src/lib/composables/use-prices.ts +1 -1
  168. package/packages/storefront/src/lib/composables/use-shop-header-submenu.ts +10 -1
  169. package/packages/storefront/src/lib/composables/use-shop-header.ts +13 -13
  170. package/packages/storefront/src/lib/layouts/BaseHead.astro +1 -1
  171. package/packages/storefront/src/lib/layouts/use-page-header.ts +14 -2
  172. package/packages/storefront/src/lib/ssr-context.ts +1 -0
  173. package/packages/types/package.json +1 -1
  174. package/pnpm-workspace.yaml +2 -0
  175. package/packages/storefront/dist/client/_astro/HeroSlider.0890631f.js +0 -1
  176. package/packages/storefront/dist/client/_astro/ShopHeader.82ae97a5.js +0 -4
  177. package/packages/storefront/dist/client/assets/cms-preview.css +0 -274
  178. package/packages/storefront/dist/client/assets/cms.css +0 -114
  179. package/packages/storefront/dist/client/assets/cvv.png +0 -0
  180. package/packages/storefront/dist/client/assets/img-placeholder.png +0 -0
  181. package/packages/storefront/dist/client/assets/payments.png +0 -0
  182. package/packages/storefront/dist/client/assets/ssl-safe.png +0 -0
@@ -0,0 +1,53 @@
1
+ <template>
2
+ <div class="bg-base-100 relative z-20">
3
+ <div class="container md:w-2/3 mx-auto px-3 py-1">
4
+ <Carousel :autoplay="countValidSlides > 1 ? 7000 : undefined">
5
+ <li
6
+ v-for="(slide, i) in slides"
7
+ :key="i"
8
+ class="shrink-0 basis-full h-full text-center"
9
+ >
10
+ <component
11
+ :is="slide.href ? 'ALink' : 'span'"
12
+ :href="slide.href"
13
+ :target="slide.target"
14
+ class="inline-block px-8"
15
+ :class="slide.href ? 'hover:underline' : null"
16
+ >
17
+ <span
18
+ v-if="parsedContents[i]"
19
+ v-html="parsedContents[i]"
20
+ class="prose text-sm text-base-800"
21
+ ></span>
22
+ </component>
23
+ </li>
24
+ <template #controls>
25
+ <div
26
+ v-show="countValidSlides > 1"
27
+ class="text-xl leading-none text-base-400"
28
+ >
29
+ <CarouselControl class="pr-2 bg-base-100 hover:text-base-700" is-prev />
30
+ <CarouselControl class="pl-2 bg-base-100 hover:text-base-700" />
31
+ </div>
32
+ </template>
33
+ </Carousel>
34
+ </div>
35
+ </div>
36
+ </template>
37
+
38
+ <script setup lang="ts">
39
+ import {
40
+ type Props as UsePitchBarProps,
41
+ usePitchBar,
42
+ } from '@@sf/composables/use-pitch-bar';
43
+ import Carousel from '@@sf/components/Carousel.vue';
44
+ import CarouselControl from '@@sf/components/CarouselControl.vue';
45
+
46
+ export interface Props extends UsePitchBarProps {}
47
+
48
+ const props = defineProps<Props>();
49
+ const {
50
+ parsedContents,
51
+ countValidSlides,
52
+ } = usePitchBar(props);
53
+ </script>
@@ -0,0 +1,96 @@
1
+ <template>
2
+ <div
3
+ class="text-base-600
4
+ [&>div]:[font-size:90%] [&_small]:lowercase [&_small]:[font-size:92%]"
5
+ :class="isBig ? 'text-lg' : null"
6
+ data-prices
7
+ >
8
+ <span v-if="comparePrice" class="text-base-500 mr-1 [font-size:87%]">
9
+ <small v-if="isLiteral">
10
+ {{ `${$t.i19from} ` }}
11
+ </small>
12
+ <s>{{ $money(comparePrice) }}</s>
13
+ <small v-if="isLiteral">
14
+ {{ ` ${$t.i19to}` }}
15
+ </small>
16
+ </span>
17
+ <strong
18
+ class="inline-block text-base-800"
19
+ :class="isBig ? 'text-5xl block' : null"
20
+ >
21
+ <small v-if="hasVariedPrices">
22
+ {{ `${$t.i19asOf} ` }}
23
+ </small>
24
+ {{ $money(salePrice) }}
25
+ </strong>
26
+ <Fade slide="down">
27
+ <div v-if="cashbackValue && hasCashback" class="relative z-10">
28
+ <span :data-tooltip="$t.i19get$1back
29
+ .replace('$1', $percentage(cashbackPercentage))">
30
+ <i class="i-back-fill mr-1"></i>
31
+ <span class="font-medium">
32
+ {{ $money(cashbackValue) }}
33
+ </span>
34
+ <small> cashback</small>
35
+ </span>
36
+ </div>
37
+ </Fade>
38
+ <Fade slide="down">
39
+ <div v-if="installmentValue && hasPriceOptions">
40
+ <small v-if="isLiteral">
41
+ {{ `${$t.i19upTo} ` }}
42
+ </small>
43
+ {{ installmentsNumber }}x
44
+ <small v-if="isLiteral">
45
+ {{ ` ${$t.i19of} ` }}
46
+ </small>
47
+ <span>{{ $money(installmentValue) }}</span>
48
+ <small v-if="!monthlyInterest && isLiteral">
49
+ {{ $t.i19interestFree }}
50
+ </small>
51
+ </div>
52
+ </Fade>
53
+ <Fade slide="down">
54
+ <div v-if="priceWithDiscount < salePrice && hasPriceOptions">
55
+ <small v-if="!discountLabel">
56
+ {{ `${$t.i19asOf} ` }}
57
+ </small>
58
+ <span>{{ $money(priceWithDiscount) }}</span>
59
+ <small v-if="discountLabel">
60
+ {{ ` ${discountLabel}` }}
61
+ </small>
62
+ </div>
63
+ </Fade>
64
+ </div>
65
+ </template>
66
+
67
+ <script setup lang="ts">
68
+ import {
69
+ type Props as UsePricesProps,
70
+ usePrices,
71
+ } from '@@sf/composables/use-prices';
72
+
73
+ export interface Props extends UsePricesProps {
74
+ isBig?: boolean;
75
+ isLiteral?: boolean;
76
+ hasCashback?: boolean;
77
+ hasPriceOptions?: boolean;
78
+ }
79
+
80
+ const props = withDefaults(defineProps<Props>(), {
81
+ hasCashback: true,
82
+ hasPriceOptions: true,
83
+ });
84
+ const {
85
+ hasVariedPrices,
86
+ salePrice,
87
+ comparePrice,
88
+ cashbackPercentage,
89
+ cashbackValue,
90
+ installmentsNumber,
91
+ monthlyInterest,
92
+ installmentValue,
93
+ priceWithDiscount,
94
+ discountLabel,
95
+ } = usePrices(props);
96
+ </script>
@@ -0,0 +1,117 @@
1
+ <template>
2
+ <article
3
+ ref="card"
4
+ :data-sku="product.sku"
5
+ class="relative h-full max-w-[350px] mx-auto py-3 group"
6
+ >
7
+ <component
8
+ :is="link ? 'ALink' : 'span'"
9
+ :href="link"
10
+ class="flex flex-col h-full rounded overflow-hidden
11
+ group-hover:shadow group-hover:ring-1 ring-black/5"
12
+ >
13
+ <div class="aspect-square p-2
14
+ motion-safe:group-hover:scale-110 transition-transform">
15
+ <div class="relative w-full h-full bg-white rounded overflow-hidden
16
+ group-hover:rounded-none">
17
+ <template v-if="images?.length">
18
+ <AImg
19
+ :picture="images[0]"
20
+ :alt="title"
21
+ class="absolute top-0 left-0 block w-full h-full object-cover"
22
+ />
23
+ <AImg
24
+ v-if="images[1] && wasHoveredOnce"
25
+ :picture="images[1]"
26
+ :alt="title"
27
+ class="absolute top-0 left-0 block w-full h-full object-cover
28
+ opacity-0 group-hover:opacity-100 transition-opacity
29
+ motion-safe:duration-300 text-transparent z-10"
30
+ />
31
+ </template>
32
+ <div
33
+ v-else
34
+ class="w-full h-full bg-gradient-to-br from-base-50/20 to-base-100"
35
+ />
36
+ </div>
37
+ </div>
38
+ <span
39
+ v-if="discountPercentage"
40
+ class=":uno: absolute top-9 right-2
41
+ group-hover:scale-110 group-hover:translate-x-2 transition-transform
42
+ bg-secondary/70 text-on-secondary text-xs
43
+ py-0.5 pr-1.5 pl-3 [clip-path:polygon(20%_0,100%_0,100%_100%,0_100%)]"
44
+ >
45
+ -<strong>{{ discountPercentage }}</strong>%
46
+ </span>
47
+ <div class="relative flex flex-col grow justify-start p-4
48
+ group-hover:backdrop-blur-md bg-white/40 z-10">
49
+ <div class="pb-2">
50
+ <div v-if="isActive">
51
+ <Prices :product="product" />
52
+ </div>
53
+ <span v-else class="ui-badge bg-warning-100 text-warning-700">
54
+ {{ !isInStock ? $t.i19outOfStock : $t.i19inactive }}
55
+ </span>
56
+ </div>
57
+ <component
58
+ :is="headingTag"
59
+ class="ui-link no-underline line-clamp-2 text-sm"
60
+ :class="[
61
+ isActive ? 'text-base-700' : 'text-base-500',
62
+ link ? 'group-hover:underline group-hover:text-primary' : null,
63
+ ]"
64
+ >
65
+ {{ title }}
66
+ </component>
67
+ <button
68
+ v-if="isActive && !hasVariations"
69
+ class="absolute -top-12 left-1/2 -ml-7 w-14 h-14 lg:left-4 lg:ml-0
70
+ primary opacity-75 hover:opacity-100
71
+ rounded-full text-3xl leading-none p-2 z-10"
72
+ @click.prevent="addProductToCart(product)"
73
+ >
74
+ <i class="i-shopping-bag-3-line"></i>
75
+ </button>
76
+ </div>
77
+ </component>
78
+ </article>
79
+ </template>
80
+
81
+ <script setup lang="ts">
82
+ import { ref } from 'vue';
83
+ import { watchOnce, useElementHover } from '@vueuse/core';
84
+ import { addProductToCart } from '@@sf/state/shopping-cart';
85
+ import {
86
+ type Props as UseProductCardProps,
87
+ useProductCard,
88
+ } from '@@sf/composables/use-product-card';
89
+ import Prices from '~/components/Prices.vue';
90
+
91
+ export type Props = UseProductCardProps & {
92
+ headingTag?: 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6';
93
+ };
94
+
95
+ const props = withDefaults(defineProps<Props>(), {
96
+ headingTag: 'h3',
97
+ });
98
+ const {
99
+ product,
100
+ title,
101
+ link,
102
+ images,
103
+ isInStock,
104
+ isActive,
105
+ discountPercentage,
106
+ hasVariations,
107
+ } = useProductCard({
108
+ product: props.product,
109
+ productId: props.productId,
110
+ } as UseProductCardProps);
111
+ const card = ref<HTMLElement | null>(null);
112
+ const isHovered = useElementHover(card);
113
+ const wasHoveredOnce = ref(false);
114
+ watchOnce(isHovered, () => {
115
+ wasHoveredOnce.value = true;
116
+ });
117
+ </script>
@@ -0,0 +1,60 @@
1
+ <template>
2
+ <section class="ui-section">
3
+ <div v-if="title" class="max-w-prose mx-auto text-center mb-2">
4
+ <h2 class="ui-text-brand text-3xl">
5
+ <ALink v-if="titleLink" :href="titleLink" class="ui-link">
6
+ {{ title }}
7
+ </ALink>
8
+ <span v-else class="text-base-700">
9
+ {{ title }}
10
+ </span>
11
+ </h2>
12
+ </div>
13
+ <Carousel class="group/shelf">
14
+ <li
15
+ v-for="product in products"
16
+ :key="product._id"
17
+ class="basis-1/2 md:basis-1/3 lg:basis-1/4 shrink-0"
18
+ >
19
+ <ProductCard :product="product" />
20
+ </li>
21
+ <template #controls>
22
+ <div
23
+ v-show="products.length > 2"
24
+ class="text-3xl lg:text-2xl leading-none text-primary
25
+ lg:opacity-0 group-hover/shelf:opacity-90 transition-opacity"
26
+ >
27
+ <CarouselControl class="!top-1/2 !lg:top-1/3 !-left-4 w-12 h-12
28
+ bg-transparent lg:bg-white/80 lg:hover:bg-primary-300/60 rounded
29
+ lg:shadow-sm lg:ring-1 ring-black/5" is-prev />
30
+ <CarouselControl class="!top-1/2 !lg:top-1/3 !-right-4 w-12 h-12
31
+ bg-transparent lg:bg-white/80 lg:hover:bg-primary-300/60 rounded
32
+ lg:shadow-sm lg:ring-1 ring-black/5" />
33
+ </div>
34
+ </template>
35
+ </Carousel>
36
+ </section>
37
+ </template>
38
+
39
+ <script setup lang="ts">
40
+ import {
41
+ type Props as UseProductShelfProps,
42
+ useProductShelf,
43
+ } from '@@sf/composables/use-product-shelf';
44
+ import Carousel from '@@sf/components/Carousel.vue';
45
+ import CarouselControl from '@@sf/components/CarouselControl.vue';
46
+ import ProductCard from '~/components/ProductCard.vue';
47
+
48
+ export interface Props extends UseProductShelfProps {}
49
+
50
+ const props = defineProps<Props>();
51
+ const {
52
+ title,
53
+ titleLink,
54
+ fetching,
55
+ products,
56
+ } = useProductShelf(props);
57
+ if (import.meta.env.SSR) {
58
+ await fetching;
59
+ }
60
+ </script>
@@ -0,0 +1,6 @@
1
+ <template>
2
+ <div class="w-full h-40 bg-base-300"></div>
3
+ </template>
4
+
5
+ <script setup lang="ts">
6
+ </script>
@@ -0,0 +1,136 @@
1
+ <template>
2
+ <header
3
+ ref="header"
4
+ class="relative top-0 z-50 transition-colors"
5
+ :class="[
6
+ isSticky && !isSidenavOpen ? 'bg-white/80' : 'bg-white',
7
+ isSticky ? 'backdrop-blur-md shadow py-2 md:py-3' : 'py-3 sm:py-4 md:py-5',
8
+ ]"
9
+ >
10
+ <div class="container 2xl:max-w-7xl
11
+ mx-auto px-1 lg:pr-3 lg:pl-5 xl:pr-7 xl:pl-9 2xl:pr-0 2xl:pl-2
12
+ flex md:grid grid-flow-col grid-cols-3 lg:grid-cols-none lg:auto-cols-max
13
+ justify-between items-center">
14
+ <div class="basis-1/4 lg:hidden">
15
+ <button
16
+ class="px-2 my-1"
17
+ :aria-label="$t.i19toggleMenu"
18
+ @click="isSidenavOpen = !isSidenavOpen"
19
+ >
20
+ <i
21
+ class="text-base-500 text-3xl"
22
+ :class="isSidenavOpen ? 'i-close' : 'i-menu-line'"
23
+ ></i>
24
+ </button>
25
+ </div>
26
+ <slot name="logo" />
27
+ <ShopHeaderMenu class="hidden lg:block" v-bind="{ inlineMenuTrees }" />
28
+ <div class="basis-1/4 px-2 flex justify-end items-center gap-3 lg:gap-4">
29
+ <button
30
+ :aria-label="$t.i19searchProducts"
31
+ @click="isSearchOpen = !isSearchOpen"
32
+ >
33
+ <i class="i-search-3-line w-7 h-7
34
+ text-base-700 hover:text-primary hover:scale-110 active:scale-125"></i>
35
+ </button>
36
+ <AccountMenu
37
+ class="hidden sm:block"
38
+ :aria-label="$t.i19myAccount"
39
+ :service-links="serviceLinks"
40
+ >
41
+ <template #button="{ open }">
42
+ <i
43
+ class="i-user-2-line w-7 h-7
44
+ text-base-600 hover:text-primary hover:scale-110 active:scale-125"
45
+ :class="open ? 'text-base-800 scale-110' : null"
46
+ ></i>
47
+ </template>
48
+ </AccountMenu>
49
+ <button
50
+ :aria-label="$t.i19openCart"
51
+ @click="isCartOpen = !isCartOpen"
52
+ class="relative group text-primary-500"
53
+ >
54
+ <i class="i-shopping-bag-3-fill w-7 h-7 group-hover:text-primary
55
+ group-hover:scale-110 group-active:scale-125"></i>
56
+ <span
57
+ v-if="delayedTotalItems"
58
+ class="ui-badge-pill-sm absolute -top-1 -right-1.5"
59
+ >
60
+ {{ delayedTotalItems }}
61
+ </span>
62
+ </button>
63
+ </div>
64
+ </div>
65
+ <Drawer
66
+ v-model="isSidenavOpen"
67
+ :has-close-button="false"
68
+ position="absolute"
69
+ :class="isSticky ? 'mt-2 md:mt-3' : 'mt-3 sm:mt-4 md:mt-5'"
70
+ :style="{ height: `calc(100vh - ${positionY}px + .5rem)` }"
71
+ >
72
+ <ShopSidenav class="bg-white pt-6" v-bind="{ categoryTrees }" />
73
+ </Drawer>
74
+ <Drawer
75
+ v-model="isSearchOpen"
76
+ :has-close-button="false"
77
+ placement="top"
78
+ >
79
+ <SearchModal />
80
+ </Drawer>
81
+ <Teleport v-if="isMounted" to="#teleported-overlap">
82
+ <Drawer
83
+ v-model="isCartOpen"
84
+ placement="end"
85
+ backdrop-target="#teleported-overlap"
86
+ >
87
+ <CartSidebar />
88
+ </Drawer>
89
+ </Teleport>
90
+ </header>
91
+ </template>
92
+
93
+ <script setup lang="ts">
94
+ import { ref, watch, onMounted } from 'vue';
95
+ import { totalItems } from '@@sf/state/shopping-cart';
96
+ import {
97
+ type Props as UseShopHeaderProps,
98
+ useShopHeader,
99
+ } from '@@sf/composables/use-shop-header';
100
+ import Drawer from '@@sf/components/Drawer.vue';
101
+ import ShopSidenav from '~/components/ShopSidenav.vue';
102
+ import ShopHeaderMenu from '~/components/ShopHeaderMenu.vue';
103
+ import SearchModal from '~/components/SearchModal.vue';
104
+ import AccountMenu from '~/components/AccountMenu.vue';
105
+ import CartSidebar from '~/components/CartSidebar.vue';
106
+
107
+ export interface Props extends Omit<UseShopHeaderProps, 'header'> {
108
+ serviceLinks?: Array<{
109
+ title: string;
110
+ href: string;
111
+ }>;
112
+ }
113
+
114
+ const props = defineProps<Props>();
115
+ const header = ref<HTMLElement | null>(null);
116
+ const {
117
+ isSticky,
118
+ positionY,
119
+ categoryTrees,
120
+ inlineMenuTrees,
121
+ } = useShopHeader({ ...props, header });
122
+ const isSidenavOpen = ref(false);
123
+ const isSearchOpen = ref(false);
124
+ const isCartOpen = ref(false);
125
+ const isMounted = ref(false);
126
+ const delayedTotalItems = ref(0);
127
+ onMounted(() => {
128
+ isMounted.value = true;
129
+ watch(totalItems, (newTotalItems, prevTotalItems) => {
130
+ if (prevTotalItems && prevTotalItems < newTotalItems) {
131
+ isCartOpen.value = true;
132
+ }
133
+ delayedTotalItems.value = newTotalItems;
134
+ }, { immediate: true });
135
+ });
136
+ </script>
@@ -0,0 +1,58 @@
1
+ <template>
2
+ <nav>
3
+ <ul
4
+ class="flex gap-4 xl:gap-5 text-base-700"
5
+ :class="inlineMenuTrees.length < 7 ? 'text-base' : 'text-sm'"
6
+ >
7
+ <li v-for="(categoryTree, i) in inlineMenuTrees" :key="i">
8
+ <ShopHeaderSubmenu
9
+ v-if="categoryTree.subcategories.length"
10
+ :category-tree="categoryTree"
11
+ >
12
+ <template #button="{ open }">
13
+ <span class="hover:text-primary group">
14
+ <h3
15
+ class="group-hover:underline inline"
16
+ :class="[
17
+ inlineMenuTrees.length < 7 ? 'decoration-2' : null,
18
+ open ? 'underline' : null,
19
+ ]"
20
+ >
21
+ {{ categoryTree.name }}
22
+ </h3>
23
+ <i
24
+ v-if="hasOneLevelSubcategories"
25
+ class="i-down-line ml-1 transition-transform
26
+ text-base-400 group-hover:text-primary-subtle"
27
+ :class="open ? 'rotate-180' : null"
28
+ ></i>
29
+ </span>
30
+ </template>
31
+ </ShopHeaderSubmenu>
32
+ <a
33
+ v-else
34
+ :href="`/${categoryTree.slug}`"
35
+ class="hover:text-primary hover:underline"
36
+ :class="inlineMenuTrees.length < 7 ? 'decoration-2' : null"
37
+ >
38
+ <h3 class="inline">{{ categoryTree.name }}</h3>
39
+ </a>
40
+ </li>
41
+ </ul>
42
+ </nav>
43
+ </template>
44
+
45
+ <script setup lang="ts">
46
+ import type { CategoryTree } from '@@sf/composables/use-shop-header';
47
+ import { computed } from 'vue';
48
+ import ShopHeaderSubmenu from '~/components/ShopHeaderSubmenu.vue';
49
+
50
+ export interface Props {
51
+ inlineMenuTrees: CategoryTree[];
52
+ }
53
+
54
+ const props = defineProps<Props>();
55
+ const hasOneLevelSubcategories = computed(() => {
56
+ return !!props.inlineMenuTrees.find(({ subcategories }) => !subcategories.length);
57
+ });
58
+ </script>
@@ -0,0 +1,88 @@
1
+ <template>
2
+ <Popover v-slot="{ open }">
3
+ <PopoverButton class="outline-none">
4
+ <slot name="button" v-bind="{ open }" />
5
+ </PopoverButton>
6
+ <div class="relative">
7
+ <Fade>
8
+ <PopoverPanel
9
+ class="absolute z-20 top-3 left-1/2 -translate-x-1/2 !transform
10
+ px-6 py-4 rounded backdrop-blur-md shadow bg-white/80
11
+ text-base text-base-700"
12
+ :class="countMenuCols === 1 ? 'w-60'
13
+ : countMenuCols === 2 ? `w-screen ${(categoryPicture ? 'max-w-lg' : 'max-w-sm')}`
14
+ : countMenuCols === 3 ? `w-screen ${(categoryPicture ? 'max-w-xl' : 'max-w-md')}`
15
+ : countMenuCols < 6 ? 'w-screen max-w-3xl' : 'w-screen max-w-5xl'"
16
+ >
17
+ <div class="flex gap-6 w-full">
18
+ <ul v-if="subcategoryLinks.length" class="flex-1">
19
+ <li
20
+ v-for="(subcategory, i) in subcategoryLinks"
21
+ :key="`link-${i}`"
22
+ :class="subcategoryLinks.length > 10 ? 'text-sm mb-1' : 'mb-2'"
23
+ >
24
+ <a :href="`/${subcategory.slug}`" class="hover:text-primary">
25
+ <h3>{{ subcategory.name }}</h3>
26
+ </a>
27
+ </li>
28
+ </ul>
29
+ <div
30
+ v-for="(subcategory, i) in subcategoryCols"
31
+ :key="i"
32
+ class="flex-1"
33
+ >
34
+ <a :href="`/${subcategory.slug}`" class="hover:text-primary">
35
+ <h3>{{ subcategory.name }}</h3>
36
+ </a>
37
+ <ul class="text-sm text-base-600 mt-1 mb-1.5">
38
+ <li
39
+ v-for="(nestedSubcategory, ii) in subcategory.subcategories"
40
+ :key="`${i}-${ii}`"
41
+ class="mb-0.5"
42
+ >
43
+ <a
44
+ :href="`/${nestedSubcategory.slug}`"
45
+ class="hover:text-primary hover:underline"
46
+ >
47
+ <h3>{{ nestedSubcategory.name }}</h3>
48
+ </a>
49
+ </li>
50
+ </ul>
51
+ </div>
52
+ <div
53
+ v-if="categoryPicture"
54
+ :class="countMenuCols === 2 ? 'basis-1/2'
55
+ : countMenuCols < 5 ? 'basis-2/5' : 'basis-1/3'"
56
+ >
57
+ <AImg :picture="categoryPicture" class="ml-auto rounded" />
58
+ </div>
59
+ </div>
60
+ <a
61
+ :href="`/${categoryTree.slug}`"
62
+ class="block mt-1 text-xs text-base-600 leading-snug underline"
63
+ >
64
+ {{ $t.i19seeAll$1Category.replace('$1', categoryTree.name) }}
65
+ </a>
66
+ </PopoverPanel>
67
+ </Fade>
68
+ </div>
69
+ </Popover>
70
+ </template>
71
+
72
+ <script setup lang="ts">
73
+ import { Popover, PopoverButton, PopoverPanel } from '@headlessui/vue';
74
+ import {
75
+ type Props as UseShopHeaderSubmenuProps,
76
+ useShopHeaderSubmenu,
77
+ } from '@@sf/composables/use-shop-header-submenu';
78
+
79
+ export interface Props extends UseShopHeaderSubmenuProps {}
80
+
81
+ const props = defineProps<Props>();
82
+ const {
83
+ categoryPicture,
84
+ subcategoryLinks,
85
+ subcategoryCols,
86
+ countMenuCols,
87
+ } = useShopHeaderSubmenu(props);
88
+ </script>
@@ -0,0 +1,61 @@
1
+ <template>
2
+ <aside class="flex flex-col h-full">
3
+ <nav class="py-4 grow">
4
+ <ul class="relative h-full">
5
+ <ShopSidenavCategory
6
+ v-for="(categoryTree, i) in categoryTrees"
7
+ :key="i"
8
+ :category-tree="categoryTree"
9
+ />
10
+ </ul>
11
+ </nav>
12
+ <footer class="text-base">
13
+ <div class="flex items-center px-2 py-4 bg-base-100">
14
+ <AccountLink class="p-2 grow flex items-center gap-3">
15
+ <i class="i-user-2-line text-4xl text-base-500 m-0"></i>
16
+ <span class="leading-tight">
17
+ {{ `${$t.i19hello} ${customerName || $t.i19visitor}` }}
18
+ <small class="block lowercase font-semibold text-primary">
19
+ {{ $t.i19myOrders }}, {{ $t.i19myAccount }}
20
+ </small>
21
+ </span>
22
+ </AccountLink>
23
+ <button
24
+ v-if="isLogged"
25
+ @click="logout"
26
+ class="p-2 text-right text-base-800"
27
+ >
28
+ <span class="text-base-600">{{ $t.i19logout }}</span>
29
+ <i class="i-align-arrow-left-fill text-lg ml-1"></i>
30
+ </button>
31
+ </div>
32
+ <ul class="flex gap-3 p-4 bg-base-200 text-xl text-base-700">
33
+ <li v-for="(href, network) in socialNetworks" :key="network">
34
+ <SocialNetworkLink :network="network" class="p-1 active:text-primary" />
35
+ </li>
36
+ </ul>
37
+ </footer>
38
+ </aside>
39
+ </template>
40
+
41
+ <script setup lang="ts">
42
+ import type { CategoryTree } from '@@sf/composables/use-shop-header';
43
+ import { onMounted } from 'vue';
44
+ import {
45
+ customerName,
46
+ initializeFirebaseAuth,
47
+ isLogged,
48
+ logout,
49
+ } from '@@sf/state/customer-session';
50
+ import { socialNetworks } from '@@sf/server-data';
51
+ import AccountLink from '@@sf/components/AccountLink.vue';
52
+ import SocialNetworkLink from '@@sf/components/SocialNetworkLink.vue';
53
+ import ShopSidenavCategory from '~/components/ShopSidenavCategory.vue';
54
+
55
+ export interface Props {
56
+ categoryTrees: CategoryTree[];
57
+ }
58
+
59
+ defineProps<Props>();
60
+ onMounted(() => initializeFirebaseAuth());
61
+ </script>