cloudcommerce 0.21.0 → 0.22.1

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 (188) hide show
  1. package/.gitmodules +3 -0
  2. package/.nvmrc +1 -0
  3. package/.vscode/settings.json +2 -1
  4. package/CHANGELOG.md +36 -0
  5. package/action.yml +37 -13
  6. package/ecomplus-stores/iluminim/.devcontainer/devcontainer.json +30 -0
  7. package/ecomplus-stores/iluminim/.eslintrc.cjs +3 -0
  8. package/ecomplus-stores/iluminim/.firebaserc +5 -0
  9. package/ecomplus-stores/iluminim/.github/renovate.json +5 -0
  10. package/ecomplus-stores/iluminim/.github/workflows/build-and-deploy.yml +36 -0
  11. package/ecomplus-stores/iluminim/.github/workflows/calibreapp-image-actions.yml +23 -0
  12. package/ecomplus-stores/iluminim/.gitpod.yml +12 -0
  13. package/ecomplus-stores/iluminim/.nvmrc +1 -0
  14. package/ecomplus-stores/iluminim/.vscode/extensions.json +8 -0
  15. package/ecomplus-stores/iluminim/.vscode/launch.json +11 -0
  16. package/ecomplus-stores/iluminim/.vscode/settings.json +10 -0
  17. package/ecomplus-stores/iluminim/README.md +113 -0
  18. package/ecomplus-stores/iluminim/README.pt-BR.md +113 -0
  19. package/ecomplus-stores/iluminim/functions/config.json +3 -0
  20. package/ecomplus-stores/iluminim/functions/example.env +10 -0
  21. package/ecomplus-stores/iluminim/functions/many/index.js +14 -0
  22. package/ecomplus-stores/iluminim/functions/many/package.json +22 -0
  23. package/ecomplus-stores/iluminim/functions/ssr/.eslintrc.cjs +6 -0
  24. package/ecomplus-stores/iluminim/functions/ssr/astro.config.mjs +4 -0
  25. package/ecomplus-stores/iluminim/functions/ssr/content/blog/.gitkeep +0 -0
  26. package/ecomplus-stores/iluminim/functions/ssr/content/extra-pages/.gitkeep +0 -0
  27. package/ecomplus-stores/iluminim/functions/ssr/content/layout.json +33 -0
  28. package/ecomplus-stores/iluminim/functions/ssr/content/pages/home.json +70 -0
  29. package/ecomplus-stores/iluminim/functions/ssr/content/settings.json +49 -0
  30. package/ecomplus-stores/iluminim/functions/ssr/index.js +18 -0
  31. package/ecomplus-stores/iluminim/functions/ssr/package.json +31 -0
  32. package/ecomplus-stores/iluminim/functions/ssr/public/admin/.gitkeep +2 -0
  33. package/ecomplus-stores/iluminim/functions/ssr/public/img/icon.png +0 -0
  34. package/ecomplus-stores/iluminim/functions/ssr/public/img/large-icon.png +0 -0
  35. package/ecomplus-stores/iluminim/functions/ssr/public/img/uploads/banner-chamada-desktop-9x81zmd91q.webp +0 -0
  36. package/ecomplus-stores/iluminim/functions/ssr/public/img/uploads/banner2.webp +0 -0
  37. package/ecomplus-stores/iluminim/functions/ssr/public/img/uploads/chamada-mobile-q1c6om6jx4.webp +0 -0
  38. package/ecomplus-stores/iluminim/functions/ssr/public/img/uploads/ecom-icon.png +0 -0
  39. package/ecomplus-stores/iluminim/functions/ssr/public/img/uploads/headphone.webp +0 -0
  40. package/ecomplus-stores/iluminim/functions/ssr/public/img/uploads/logo.webp +0 -0
  41. package/ecomplus-stores/iluminim/functions/ssr/public/img/uploads/og-image.png +0 -0
  42. package/ecomplus-stores/iluminim/functions/ssr/public/img/uploads/passion.webp +0 -0
  43. package/ecomplus-stores/iluminim/functions/ssr/public/img/uploads/rect8589.png +0 -0
  44. package/ecomplus-stores/iluminim/functions/ssr/public/img/uploads/rect859.png +0 -0
  45. package/ecomplus-stores/iluminim/functions/ssr/public/img/uploads/rect89.webp +0 -0
  46. package/ecomplus-stores/iluminim/functions/ssr/public/robots.txt +8 -0
  47. package/ecomplus-stores/iluminim/functions/ssr/scripts/build.sh +14 -0
  48. package/ecomplus-stores/iluminim/functions/ssr/src/assets/style.css +65 -0
  49. package/ecomplus-stores/iluminim/functions/ssr/src/components/AccountMenu.vue +104 -0
  50. package/ecomplus-stores/iluminim/functions/ssr/src/components/Banner.vue +59 -0
  51. package/ecomplus-stores/iluminim/functions/ssr/src/components/BannersGrid.astro +25 -0
  52. package/ecomplus-stores/iluminim/functions/ssr/src/components/CartSidebar.vue +35 -0
  53. package/ecomplus-stores/iluminim/functions/ssr/src/components/Countdown.vue +79 -0
  54. package/ecomplus-stores/iluminim/functions/ssr/src/components/HeroSlider.vue +52 -0
  55. package/ecomplus-stores/iluminim/functions/ssr/src/components/PitchBar.vue +57 -0
  56. package/ecomplus-stores/iluminim/functions/ssr/src/components/Prices.vue +96 -0
  57. package/ecomplus-stores/iluminim/functions/ssr/src/components/ProductCard.vue +118 -0
  58. package/ecomplus-stores/iluminim/functions/ssr/src/components/ProductShelf.vue +60 -0
  59. package/ecomplus-stores/iluminim/functions/ssr/src/components/ProductsCountdown.vue +20 -0
  60. package/ecomplus-stores/iluminim/functions/ssr/src/components/SearchModal.vue +6 -0
  61. package/ecomplus-stores/iluminim/functions/ssr/src/components/ShopHeader.vue +137 -0
  62. package/ecomplus-stores/iluminim/functions/ssr/src/components/ShopHeaderMenu.vue +58 -0
  63. package/ecomplus-stores/iluminim/functions/ssr/src/components/ShopHeaderSubmenu.vue +88 -0
  64. package/ecomplus-stores/iluminim/functions/ssr/src/components/ShopSidenav.vue +61 -0
  65. package/ecomplus-stores/iluminim/functions/ssr/src/components/ShopSidenavCategory.vue +78 -0
  66. package/ecomplus-stores/iluminim/functions/ssr/src/env.d.ts +13 -0
  67. package/ecomplus-stores/iluminim/functions/ssr/src/layouts/Base.astro +16 -0
  68. package/ecomplus-stores/iluminim/functions/ssr/src/layouts/Checkout.astro +0 -0
  69. package/ecomplus-stores/iluminim/functions/ssr/src/layouts/PageHeader.astro +33 -0
  70. package/ecomplus-stores/iluminim/functions/ssr/src/main/Fallback.astro +10 -0
  71. package/ecomplus-stores/iluminim/functions/ssr/src/main/Home.astro +49 -0
  72. package/ecomplus-stores/iluminim/functions/ssr/src/main/Sections.astro +42 -0
  73. package/ecomplus-stores/iluminim/functions/ssr/src/main/Wildcard.astro +18 -0
  74. package/ecomplus-stores/iluminim/functions/ssr/src/pages/[...slug].astro +40 -0
  75. package/ecomplus-stores/iluminim/functions/ssr/src/pages/_vue.ts +3 -0
  76. package/ecomplus-stores/iluminim/functions/ssr/src/pages/app/account.astro +0 -0
  77. package/ecomplus-stores/iluminim/functions/ssr/src/pages/app/index.astro +0 -0
  78. package/ecomplus-stores/iluminim/functions/ssr/src/pages/fallback.astro +25 -0
  79. package/ecomplus-stores/iluminim/functions/ssr/src/pages/index.astro +35 -0
  80. package/ecomplus-stores/iluminim/functions/ssr/src/scripts/InlineScripts.astro +10 -0
  81. package/ecomplus-stores/iluminim/functions/ssr/tailwind.config.cjs +13 -0
  82. package/ecomplus-stores/iluminim/functions/ssr/tsconfig.json +12 -0
  83. package/ecomplus-stores/iluminim/functions/ssr/uno.config.cjs +5 -0
  84. package/ecomplus-stores/iluminim/functions/with-apps/index.js +12 -0
  85. package/ecomplus-stores/iluminim/functions/with-apps/package.json +22 -0
  86. package/ecomplus-stores/iluminim/package.json +28 -0
  87. package/ecomplus-stores/iluminim/scripts/install.sh +14 -0
  88. package/ecomplus-stores/monocard/.eslintrc.cjs +3 -0
  89. package/ecomplus-stores/monocard/.nvmrc +1 -0
  90. package/ecomplus-stores/tia-sonia/.eslintrc.cjs +3 -0
  91. package/ecomplus-stores/tia-sonia/.nvmrc +1 -0
  92. package/package.json +6 -6
  93. package/packages/api/package.json +1 -1
  94. package/packages/api/types.d.ts +1 -1
  95. package/packages/apps/affilate-program/package.json +1 -1
  96. package/packages/apps/correios/package.json +1 -1
  97. package/packages/apps/custom-payment/package.json +1 -1
  98. package/packages/apps/custom-shipping/package.json +1 -1
  99. package/packages/apps/datafrete/package.json +1 -1
  100. package/packages/apps/discounts/package.json +1 -1
  101. package/packages/apps/emails/package.json +1 -1
  102. package/packages/apps/fb-conversions/package.json +2 -2
  103. package/packages/apps/flash-courier/package.json +1 -1
  104. package/packages/apps/frenet/package.json +1 -1
  105. package/packages/apps/galaxpay/package.json +1 -1
  106. package/packages/apps/google-analytics/package.json +1 -1
  107. package/packages/apps/jadlog/package.json +1 -1
  108. package/packages/apps/loyalty-points/package.json +1 -1
  109. package/packages/apps/melhor-envio/package.json +1 -1
  110. package/packages/apps/mercadopago/package.json +1 -1
  111. package/packages/apps/pagarme/package.json +1 -1
  112. package/packages/apps/paghiper/package.json +1 -1
  113. package/packages/apps/pix/package.json +1 -1
  114. package/packages/apps/tiny-erp/package.json +1 -1
  115. package/packages/apps/webhooks/package.json +1 -1
  116. package/packages/cli/lib/build.js +15 -4
  117. package/packages/cli/lib/cli.js +3 -3
  118. package/packages/cli/package.json +1 -1
  119. package/packages/cli/src/build.ts +17 -4
  120. package/packages/cli/src/cli.ts +3 -3
  121. package/packages/config/package.json +1 -1
  122. package/packages/emails/package.json +1 -1
  123. package/packages/events/package.json +1 -1
  124. package/packages/feeds/package.json +1 -1
  125. package/packages/firebase/package.json +1 -1
  126. package/packages/i18n/lib/dirname.js +3 -1
  127. package/packages/i18n/lib/dirname.js.map +1 -1
  128. package/packages/i18n/lib/en_us/i19hour.txt +1 -0
  129. package/packages/i18n/lib/en_us/i19hours.txt +1 -0
  130. package/packages/i18n/lib/en_us/i19milliseconds.txt +1 -0
  131. package/packages/i18n/lib/en_us/i19minute.txt +1 -0
  132. package/packages/i18n/lib/en_us/i19minutes.txt +1 -0
  133. package/packages/i18n/lib/en_us/i19second.txt +1 -0
  134. package/packages/i18n/lib/en_us/i19seconds.txt +1 -0
  135. package/packages/i18n/lib/en_us.d.ts +7 -0
  136. package/packages/i18n/lib/en_us.js +7 -0
  137. package/packages/i18n/lib/en_us.js.map +1 -1
  138. package/packages/i18n/lib/pt_br/i19home.txt +1 -1
  139. package/packages/i18n/lib/pt_br/i19hour.txt +1 -0
  140. package/packages/i18n/lib/pt_br/i19hours.txt +1 -0
  141. package/packages/i18n/lib/pt_br/i19milliseconds.txt +1 -0
  142. package/packages/i18n/lib/pt_br/i19minute.txt +1 -0
  143. package/packages/i18n/lib/pt_br/i19minutes.txt +1 -0
  144. package/packages/i18n/lib/pt_br/i19second.txt +1 -0
  145. package/packages/i18n/lib/pt_br/i19seconds.txt +1 -0
  146. package/packages/i18n/lib/pt_br.d.ts +8 -1
  147. package/packages/i18n/lib/pt_br.js +8 -1
  148. package/packages/i18n/lib/pt_br.js.map +1 -1
  149. package/packages/i18n/package.json +1 -1
  150. package/packages/i18n/scripts/build-txt.mjs +2 -1
  151. package/packages/i18n/src/dirname.ts +3 -1
  152. package/packages/i18n/src/en_us.ts +7 -0
  153. package/packages/i18n/src/pt_br.ts +8 -1
  154. package/packages/modules/package.json +1 -1
  155. package/packages/passport/package.json +1 -1
  156. package/packages/ssr/package.json +2 -2
  157. package/packages/storefront/.eslintrc.cjs +8 -1
  158. package/packages/storefront/astro.config.mjs +2 -1
  159. package/packages/storefront/dist/client/_astro/HeroSlider.0890631f.js +1 -0
  160. package/packages/storefront/dist/client/_astro/{_...slug_.c85b8978.css → _...slug_.2f4f6929.css} +1 -1
  161. package/packages/storefront/dist/client/assets/.gitkeep +2 -0
  162. package/packages/storefront/dist/client/sw.js +1 -1
  163. package/packages/storefront/dist/server/chunks/{_...49a92e87.mjs → _...d496b072.mjs} +4 -4
  164. package/packages/storefront/dist/server/chunks/{account@_@astro.5388531b.mjs → account@_@astro.1b311a3d.mjs} +4 -4
  165. package/packages/storefront/dist/server/chunks/{astro.161c5a41.mjs → astro.e0968ba7.mjs} +841 -765
  166. package/packages/storefront/dist/server/chunks/{endpoint@_@js.fe113d0d.mjs → endpoint@_@js.631ff6c5.mjs} +4 -4
  167. package/packages/storefront/dist/server/chunks/{fallback@_@astro.23b17e3d.mjs → fallback@_@astro.12cf85ba.mjs} +4 -4
  168. package/packages/storefront/dist/server/chunks/{index@_@astro.2527898d.mjs → index@_@astro.69eb5e8a.mjs} +4 -4
  169. package/packages/storefront/dist/server/chunks/{index@_@astro.433809ce.mjs → index@_@astro.a20d91aa.mjs} +4 -4
  170. package/packages/storefront/dist/server/chunks/pages/{_...slug_.astro.1d0e7f10.mjs → _...slug_.astro.c93fc36d.mjs} +12 -12
  171. package/packages/storefront/dist/server/chunks/pages/{account.astro.7539e037.mjs → account.astro.0003ca94.mjs} +3 -3
  172. package/packages/storefront/dist/server/chunks/pages/{endpoint.js.b71736ca.mjs → endpoint.js.630b1212.mjs} +1 -1
  173. package/packages/storefront/dist/server/chunks/pages/{fallback.astro.15292657.mjs → fallback.astro.58c7e581.mjs} +5 -5
  174. package/packages/storefront/dist/server/chunks/pages/{index.astro.9592fbd6.mjs → index.astro.6efe85be.mjs} +32 -31
  175. package/packages/storefront/dist/server/entry.mjs +10 -10
  176. package/packages/storefront/dist/server/renderers.mjs +3 -3
  177. package/packages/storefront/package.json +5 -5
  178. package/packages/storefront/src/lib/layouts/use-page-main.ts +18 -5
  179. package/packages/types/package.json +1 -1
  180. package/pnpm-workspace.yaml +2 -2
  181. package/packages/storefront/dist/client/_astro/HeroSlider.eb156f18.js +0 -1
  182. package/packages/storefront/src/lib/composables/use-hero-slider.ts +0 -26
  183. /package/{packages/storefront/dist/client → ecomplus-stores/iluminim/functions/ssr/public}/assets/cms-preview.css +0 -0
  184. /package/{packages/storefront/dist/client → ecomplus-stores/iluminim/functions/ssr/public}/assets/cms.css +0 -0
  185. /package/{packages/storefront/dist/client → ecomplus-stores/iluminim/functions/ssr/public}/assets/cvv.png +0 -0
  186. /package/{packages/storefront/dist/client → ecomplus-stores/iluminim/functions/ssr/public}/assets/img-placeholder.png +0 -0
  187. /package/{packages/storefront/dist/client → ecomplus-stores/iluminim/functions/ssr/public}/assets/payments.png +0 -0
  188. /package/{packages/storefront/dist/client → ecomplus-stores/iluminim/functions/ssr/public}/assets/ssl-safe.png +0 -0
@@ -0,0 +1,57 @@
1
+ <template>
2
+ <div class="bg-black relative z-20">
3
+ <div class="container md:w-2/3 mx-auto px-3 py-1 md:py-1.5">
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 md:text-base lg:tracking-wide
21
+ text-base-200 uppercase font-semibold
22
+ [&_b]:text-base-100 [&_strong]:font-black
23
+ [&_strong]:text-transparent [&_strong]:bg-clip-text [&_strong]:bg-gradient-to-r
24
+ [&_strong]:from-yellow-200 [&_strong]:to-yellow-400"
25
+ ></span>
26
+ </component>
27
+ </li>
28
+ <template #controls>
29
+ <div
30
+ v-show="countValidSlides > 1"
31
+ class="text-xl leading-none text-base-300"
32
+ >
33
+ <CarouselControl class="pr-2 bg-black hover:text-white" is-prev />
34
+ <CarouselControl class="pl-2 bg-black hover:text-white" />
35
+ </div>
36
+ </template>
37
+ </Carousel>
38
+ </div>
39
+ </div>
40
+ </template>
41
+
42
+ <script setup lang="ts">
43
+ import {
44
+ type Props as UsePitchBarProps,
45
+ usePitchBar,
46
+ } from '@@sf/composables/use-pitch-bar';
47
+ import Carousel from '@@sf/components/Carousel.vue';
48
+ import CarouselControl from '@@sf/components/CarouselControl.vue';
49
+
50
+ export interface Props extends UsePitchBarProps {}
51
+
52
+ const props = defineProps<Props>();
53
+ const {
54
+ parsedContents,
55
+ countValidSlides,
56
+ } = usePitchBar(props);
57
+ </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-arrow-uturn-left 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,118 @@
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-between p-4
48
+ group-hover:backdrop-blur-md bg-white/40 z-10">
49
+ <component
50
+ :is="headingTag"
51
+ class="ui-link uppercase font-bold text-sm no-underline line-clamp-2"
52
+ :class="[
53
+ isActive ? 'text-base-700' : 'text-base-500',
54
+ link ? 'group-hover:underline group-hover:text-primary' : null,
55
+ ]"
56
+ >
57
+ {{ title }}
58
+ </component>
59
+ <div class="pt-2">
60
+ <div v-if="isActive">
61
+ <Prices :product="product" />
62
+ </div>
63
+ <span v-else class="ui-badge bg-warning-100 text-warning-700">
64
+ {{ !isInStock ? $t.i19outOfStock : $t.i19inactive }}
65
+ </span>
66
+ </div>
67
+ <button
68
+ v-if="isActive && !hasVariations"
69
+ class=":uno: hidden md:block ui-btn-sm ui-btn-primary
70
+ absolute -top-6 left-0 w-full rounded-none
71
+ opacity-0 group-hover:opacity-100 transition -z-10 group-hover:z-10"
72
+ @click.prevent="addProductToCart(product)"
73
+ >
74
+ <i class="i-plus-20-solid mr-0.5"></i>
75
+ {{ $t.i19addToCart }}
76
+ </button>
77
+ </div>
78
+ </component>
79
+ </article>
80
+ </template>
81
+
82
+ <script setup lang="ts">
83
+ import { ref } from 'vue';
84
+ import { watchOnce, useElementHover } from '@vueuse/core';
85
+ import { addProductToCart } from '@@sf/state/shopping-cart';
86
+ import {
87
+ type Props as UseProductCardProps,
88
+ useProductCard,
89
+ } from '@@sf/composables/use-product-card';
90
+ import Prices from '~/components/Prices.vue';
91
+
92
+ export type Props = UseProductCardProps & {
93
+ headingTag?: 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6';
94
+ };
95
+
96
+ const props = withDefaults(defineProps<Props>(), {
97
+ headingTag: 'h3',
98
+ });
99
+ const {
100
+ product,
101
+ title,
102
+ link,
103
+ images,
104
+ isInStock,
105
+ isActive,
106
+ discountPercentage,
107
+ hasVariations,
108
+ } = useProductCard({
109
+ product: props.product,
110
+ productId: props.productId,
111
+ } as UseProductCardProps);
112
+ const card = ref<HTMLElement | null>(null);
113
+ const isHovered = useElementHover(card);
114
+ const wasHoveredOnce = ref(false);
115
+ watchOnce(isHovered, () => {
116
+ wasHoveredOnce.value = true;
117
+ });
118
+ </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 !-left-4 w-12 h-12
28
+ bg-transparent lg:bg-white/80 lg:hover:bg-primary-300/60 rounded-full
29
+ lg:shadow-sm lg:ring-1 ring-black/5" is-prev />
30
+ <CarouselControl class="!top-1/2 !-right-4 w-12 h-12
31
+ bg-transparent lg:bg-white/80 lg:hover:bg-primary-300/60 rounded-full
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,20 @@
1
+ <template>
2
+ <section class="ui-section">
3
+ <Countdown />
4
+ <article v-for="product in products" :key="product._id">
5
+ <ProductCard :product="product" />
6
+ </article>
7
+ </section>
8
+ </template>
9
+
10
+ <script setup lang="ts">
11
+ import type { ProductItem } from '@@sf/composables/use-product-card';
12
+ import ProductCard from '~/components/ProductCard.vue';
13
+ import Countdown from '~/components/Countdown.vue';
14
+
15
+ export interface Props {
16
+ products: ProductItem[];
17
+ }
18
+
19
+ defineProps<Props>();
20
+ </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,137 @@
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-bars-3-bottom-left'"
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
29
+ flex justify-end items-center gap-3 lg:gap-4 text-base-800">
30
+ <button
31
+ :aria-label="$t.i19searchProducts"
32
+ @click="isSearchOpen = !isSearchOpen"
33
+ >
34
+ <i class="i-magnifying-glass w-7 h-7
35
+ hover:text-primary hover:scale-110 active:scale-125"></i>
36
+ </button>
37
+ <AccountMenu
38
+ class="hidden sm:block"
39
+ :aria-label="$t.i19myAccount"
40
+ :service-links="serviceLinks"
41
+ >
42
+ <template #button="{ open }">
43
+ <i
44
+ class="i-user-circle w-7 h-7
45
+ hover:text-primary hover:scale-110 active:scale-125"
46
+ :class="open ? 'text-black scale-110' : null"
47
+ ></i>
48
+ </template>
49
+ </AccountMenu>
50
+ <button
51
+ :aria-label="$t.i19openCart"
52
+ @click="isCartOpen = !isCartOpen"
53
+ class="relative group"
54
+ >
55
+ <i class="i-carbon:shopping-cart w-7 h-7 group-hover:text-primary
56
+ text-secondary-900 group-hover:scale-110 group-active:scale-125"></i>
57
+ <span
58
+ v-if="delayedTotalItems"
59
+ class="ui-badge-pill-sm absolute -top-1 -right-1.5"
60
+ >
61
+ {{ delayedTotalItems }}
62
+ </span>
63
+ </button>
64
+ </div>
65
+ </div>
66
+ <Drawer
67
+ v-model="isSidenavOpen"
68
+ :has-close-button="false"
69
+ position="absolute"
70
+ :class="isSticky ? 'mt-2 md:mt-3' : 'mt-3 sm:mt-4 md:mt-5'"
71
+ :style="{ height: `calc(100vh - ${positionY}px + .5rem)` }"
72
+ >
73
+ <ShopSidenav class="bg-white pt-6" v-bind="{ categoryTrees }" />
74
+ </Drawer>
75
+ <Drawer
76
+ v-model="isSearchOpen"
77
+ :has-close-button="false"
78
+ placement="top"
79
+ >
80
+ <SearchModal />
81
+ </Drawer>
82
+ <Teleport v-if="isMounted" to="#teleported-overlap">
83
+ <Drawer
84
+ v-model="isCartOpen"
85
+ placement="end"
86
+ backdrop-target="#teleported-overlap"
87
+ >
88
+ <CartSidebar />
89
+ </Drawer>
90
+ </Teleport>
91
+ </header>
92
+ </template>
93
+
94
+ <script setup lang="ts">
95
+ import { ref, watch, onMounted } from 'vue';
96
+ import { totalItems } from '@@sf/state/shopping-cart';
97
+ import {
98
+ type Props as UseShopHeaderProps,
99
+ useShopHeader,
100
+ } from '@@sf/composables/use-shop-header';
101
+ import Drawer from '@@sf/components/Drawer.vue';
102
+ import ShopSidenav from '~/components/ShopSidenav.vue';
103
+ import ShopHeaderMenu from '~/components/ShopHeaderMenu.vue';
104
+ import SearchModal from '~/components/SearchModal.vue';
105
+ import AccountMenu from '~/components/AccountMenu.vue';
106
+ import CartSidebar from '~/components/CartSidebar.vue';
107
+
108
+ export interface Props extends Omit<UseShopHeaderProps, 'header'> {
109
+ serviceLinks?: Array<{
110
+ title: string;
111
+ href: string;
112
+ }>;
113
+ }
114
+
115
+ const props = defineProps<Props>();
116
+ const header = ref<HTMLElement | null>(null);
117
+ const {
118
+ isSticky,
119
+ positionY,
120
+ categoryTrees,
121
+ inlineMenuTrees,
122
+ } = useShopHeader({ ...props, header });
123
+ const isSidenavOpen = ref(false);
124
+ const isSearchOpen = ref(false);
125
+ const isCartOpen = ref(false);
126
+ const isMounted = ref(false);
127
+ const delayedTotalItems = ref(0);
128
+ onMounted(() => {
129
+ isMounted.value = true;
130
+ watch(totalItems, (newTotalItems, prevTotalItems) => {
131
+ if (prevTotalItems && prevTotalItems < newTotalItems) {
132
+ isCartOpen.value = true;
133
+ }
134
+ delayedTotalItems.value = newTotalItems;
135
+ }, { immediate: true });
136
+ });
137
+ </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-chevron-down 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="subcategory._id"
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>