cloudcommerce 0.33.0 → 0.33.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 (173) hide show
  1. package/.github/workflows/test-apps.yml +2 -2
  2. package/CHANGELOG.md +37 -0
  3. package/action.yml +2 -2
  4. package/ecomplus-stores/barra-doce/.github/workflows/build-and-deploy.yml +1 -1
  5. package/ecomplus-stores/barra-doce/.github/workflows/calibreapp-image-actions.yml +1 -1
  6. package/ecomplus-stores/barra-doce/.vscode/settings.json +3 -0
  7. package/ecomplus-stores/barra-doce/functions/many/package.json +3 -3
  8. package/ecomplus-stores/barra-doce/functions/ssr/content/extra-pages/terms.json +1 -1
  9. package/ecomplus-stores/barra-doce/functions/ssr/content/pages/home.json +20 -0
  10. package/ecomplus-stores/barra-doce/functions/ssr/content/pages/products.json +2 -1
  11. package/ecomplus-stores/barra-doce/functions/ssr/content/settings.json +4 -1
  12. package/ecomplus-stores/barra-doce/functions/ssr/package.json +9 -8
  13. package/ecomplus-stores/barra-doce/functions/ssr/public/robots.txt +1 -1
  14. package/ecomplus-stores/barra-doce/functions/ssr/src/assets/style.css +15 -4
  15. package/ecomplus-stores/barra-doce/functions/ssr/src/components/AccountMenu.vue +5 -3
  16. package/ecomplus-stores/barra-doce/functions/ssr/src/components/AccountPage.vue +62 -0
  17. package/ecomplus-stores/barra-doce/functions/ssr/src/components/Banner.vue +6 -3
  18. package/ecomplus-stores/barra-doce/functions/ssr/src/components/Breadcrumbs.astro +44 -0
  19. package/ecomplus-stores/barra-doce/functions/ssr/src/components/CartItem.vue +11 -11
  20. package/ecomplus-stores/barra-doce/functions/ssr/src/components/CartSidebar.vue +16 -13
  21. package/ecomplus-stores/barra-doce/functions/ssr/src/components/CheckoutPage.vue +33 -0
  22. package/ecomplus-stores/barra-doce/functions/ssr/src/components/Collapse.vue +19 -0
  23. package/ecomplus-stores/barra-doce/functions/ssr/src/components/DocDescription.vue +28 -0
  24. package/ecomplus-stores/barra-doce/functions/ssr/src/components/FooterStamps.vue +62 -0
  25. package/ecomplus-stores/barra-doce/functions/ssr/src/components/HeroSlider.vue +14 -9
  26. package/ecomplus-stores/barra-doce/functions/ssr/src/components/ImagesGallery.vue +151 -0
  27. package/ecomplus-stores/barra-doce/functions/ssr/src/components/LoginForm.vue +107 -0
  28. package/ecomplus-stores/barra-doce/functions/ssr/src/components/PitchBar.vue +6 -8
  29. package/ecomplus-stores/barra-doce/functions/ssr/src/components/Prices.vue +3 -3
  30. package/ecomplus-stores/barra-doce/functions/ssr/src/components/ProductCard.vue +22 -22
  31. package/ecomplus-stores/barra-doce/functions/ssr/src/components/ProductDetails.vue +122 -0
  32. package/ecomplus-stores/barra-doce/functions/ssr/src/components/ProductShelf.vue +10 -12
  33. package/ecomplus-stores/barra-doce/functions/ssr/src/components/ProductSpecifications.vue +42 -0
  34. package/ecomplus-stores/barra-doce/functions/ssr/src/components/SearchModal.vue +1 -1
  35. package/ecomplus-stores/barra-doce/functions/ssr/src/components/ShopFooter.vue +7 -58
  36. package/ecomplus-stores/barra-doce/functions/ssr/src/components/ShopHeader.vue +33 -34
  37. package/ecomplus-stores/barra-doce/functions/ssr/src/components/ShopHeaderMenu.vue +5 -5
  38. package/ecomplus-stores/barra-doce/functions/ssr/src/components/ShopHeaderSubmenu.vue +19 -11
  39. package/ecomplus-stores/barra-doce/functions/ssr/src/components/ShopSidenav.vue +10 -11
  40. package/ecomplus-stores/barra-doce/functions/ssr/src/components/ShopSidenavCategory.vue +9 -10
  41. package/ecomplus-stores/barra-doce/functions/ssr/src/components/SkuSelector.vue +58 -0
  42. package/ecomplus-stores/barra-doce/functions/ssr/src/layouts/PageFooter.astro +3 -1
  43. package/ecomplus-stores/barra-doce/functions/ssr/src/layouts/PageHeader.astro +1 -2
  44. package/ecomplus-stores/barra-doce/functions/ssr/src/main/Home.astro +1 -2
  45. package/ecomplus-stores/barra-doce/functions/ssr/src/main/Sections.astro +25 -2
  46. package/ecomplus-stores/barra-doce/functions/ssr/src/main/Wildcard.astro +12 -12
  47. package/ecomplus-stores/barra-doce/functions/ssr/src/pages/[...slug].astro +2 -0
  48. package/ecomplus-stores/barra-doce/functions/ssr/src/pages/_vue.ts +17 -1
  49. package/ecomplus-stores/barra-doce/functions/ssr/src/pages/app/account.astro +34 -0
  50. package/ecomplus-stores/barra-doce/functions/ssr/src/pages/app/index.astro +62 -0
  51. package/ecomplus-stores/barra-doce/functions/ssr/src/pages/index.astro +0 -5
  52. package/ecomplus-stores/barra-doce/functions/ssr/src/pages/~fallback.astro +0 -2
  53. package/ecomplus-stores/barra-doce/functions/ssr/tailwind.config.cjs +0 -1
  54. package/ecomplus-stores/barra-doce/functions/with-apps/package.json +3 -3
  55. package/ecomplus-stores/barra-doce/package.json +2 -2
  56. package/ecomplus-stores/monocard/.editorconfig +13 -0
  57. package/ecomplus-stores/monocard/.eslintrc.cjs +1 -1
  58. package/ecomplus-stores/monocard/.vscode/settings.json +5 -1
  59. package/ecomplus-stores/monocard/functions/many/package.json +3 -3
  60. package/ecomplus-stores/monocard/functions/ssr/content/extra-pages/terms.json +1 -1
  61. package/ecomplus-stores/monocard/functions/ssr/content/pages/home.json +1 -44
  62. package/ecomplus-stores/monocard/functions/ssr/content/pages/products.json +2 -1
  63. package/ecomplus-stores/monocard/functions/ssr/content/settings.json +4 -1
  64. package/ecomplus-stores/monocard/functions/ssr/package.json +8 -7
  65. package/ecomplus-stores/monocard/functions/ssr/public/robots.txt +2 -4
  66. package/ecomplus-stores/monocard/functions/ssr/src/assets/style.css +7 -2
  67. package/ecomplus-stores/monocard/functions/ssr/src/components/AccountMenu.vue +16 -14
  68. package/ecomplus-stores/monocard/functions/ssr/src/components/AccountPage.vue +62 -0
  69. package/ecomplus-stores/monocard/functions/ssr/src/components/Banner.vue +3 -3
  70. package/ecomplus-stores/monocard/functions/ssr/src/components/Breadcrumbs.astro +1 -1
  71. package/ecomplus-stores/monocard/functions/ssr/src/components/CartItem.vue +11 -11
  72. package/ecomplus-stores/monocard/functions/ssr/src/components/CartSidebar.vue +16 -13
  73. package/ecomplus-stores/monocard/functions/ssr/src/components/CheckoutPage.vue +33 -0
  74. package/ecomplus-stores/monocard/functions/ssr/src/components/Collapse.vue +19 -0
  75. package/ecomplus-stores/monocard/functions/ssr/src/components/DemoVideo.vue +1 -1
  76. package/ecomplus-stores/monocard/functions/ssr/src/components/DocDescription.vue +3 -8
  77. package/ecomplus-stores/monocard/functions/ssr/src/components/FeatureTabs.vue +73 -79
  78. package/ecomplus-stores/monocard/functions/ssr/src/components/FooterStamps.vue +63 -0
  79. package/ecomplus-stores/monocard/functions/ssr/src/components/ImagesGallery.vue +154 -0
  80. package/ecomplus-stores/monocard/functions/ssr/src/components/LoginForm.vue +107 -0
  81. package/ecomplus-stores/monocard/functions/ssr/src/components/LottiePhoneNFC.vue +1 -3
  82. package/ecomplus-stores/monocard/functions/ssr/src/components/MonocardCustomizer.vue +21 -22
  83. package/ecomplus-stores/monocard/functions/ssr/src/components/PitchBar.vue +7 -7
  84. package/ecomplus-stores/monocard/functions/ssr/src/components/Prices.vue +3 -3
  85. package/ecomplus-stores/monocard/functions/ssr/src/components/ProductCard.vue +24 -24
  86. package/ecomplus-stores/monocard/functions/ssr/src/components/ProductDetails.vue +122 -0
  87. package/ecomplus-stores/monocard/functions/ssr/src/components/ProductShelf.vue +11 -11
  88. package/ecomplus-stores/monocard/functions/ssr/src/components/ProductSpecifications.vue +42 -0
  89. package/ecomplus-stores/monocard/functions/ssr/src/components/SearchModal.vue +1 -1
  90. package/ecomplus-stores/monocard/functions/ssr/src/components/ShopFooter.vue +10 -62
  91. package/ecomplus-stores/monocard/functions/ssr/src/components/ShopHeader.vue +25 -31
  92. package/ecomplus-stores/monocard/functions/ssr/src/components/ShopHeaderMenu.vue +8 -8
  93. package/ecomplus-stores/monocard/functions/ssr/src/components/ShopSidenav.vue +10 -11
  94. package/ecomplus-stores/monocard/functions/ssr/src/components/ShopSidenavCategory.vue +9 -10
  95. package/ecomplus-stores/monocard/functions/ssr/src/components/SkuSelector.vue +58 -0
  96. package/ecomplus-stores/monocard/functions/ssr/src/layouts/PageFooter.astro +4 -3
  97. package/ecomplus-stores/monocard/functions/ssr/src/layouts/PageHeader.astro +1 -1
  98. package/ecomplus-stores/monocard/functions/ssr/src/main/Home.astro +2 -1
  99. package/ecomplus-stores/monocard/functions/ssr/src/main/Sections.astro +18 -4
  100. package/ecomplus-stores/monocard/functions/ssr/src/main/Wildcard.astro +10 -1
  101. package/ecomplus-stores/monocard/functions/ssr/src/pages/app/account.astro +34 -0
  102. package/ecomplus-stores/monocard/functions/ssr/src/pages/app/index.astro +62 -0
  103. package/ecomplus-stores/monocard/functions/ssr/src/pages/index.astro +0 -5
  104. package/ecomplus-stores/monocard/functions/ssr/src/pages/~fallback.astro +0 -2
  105. package/ecomplus-stores/monocard/functions/ssr/tailwind.config.cjs +0 -1
  106. package/ecomplus-stores/monocard/functions/with-apps/package.json +3 -3
  107. package/ecomplus-stores/monocard/package.json +2 -2
  108. package/package.json +7 -7
  109. package/packages/api/package.json +1 -1
  110. package/packages/apps/affilate-program/package.json +1 -1
  111. package/packages/apps/correios/package.json +3 -3
  112. package/packages/apps/custom-payment/package.json +1 -1
  113. package/packages/apps/custom-shipping/package.json +1 -1
  114. package/packages/apps/datafrete/package.json +2 -2
  115. package/packages/apps/discounts/package.json +1 -1
  116. package/packages/apps/emails/package.json +1 -1
  117. package/packages/apps/fb-conversions/package.json +3 -3
  118. package/packages/apps/flash-courier/package.json +2 -2
  119. package/packages/apps/frenet/package.json +2 -2
  120. package/packages/apps/galaxpay/package.json +2 -2
  121. package/packages/apps/google-analytics/package.json +2 -2
  122. package/packages/apps/jadlog/package.json +2 -2
  123. package/packages/apps/loyalty-points/package.json +1 -1
  124. package/packages/apps/mandae/package.json +2 -2
  125. package/packages/apps/melhor-envio/package.json +2 -2
  126. package/packages/apps/mercadopago/package.json +2 -2
  127. package/packages/apps/pagarme/package.json +2 -2
  128. package/packages/apps/pagarme-v5/package.json +3 -3
  129. package/packages/apps/paghiper/package.json +2 -2
  130. package/packages/apps/pix/package.json +2 -2
  131. package/packages/apps/tiny-erp/package.json +2 -2
  132. package/packages/apps/webhooks/package.json +2 -2
  133. package/packages/cli/package.json +1 -1
  134. package/packages/config/package.json +1 -1
  135. package/packages/emails/package.json +4 -4
  136. package/packages/eslint/package.json +4 -4
  137. package/packages/events/package.json +1 -1
  138. package/packages/feeds/package.json +1 -1
  139. package/packages/firebase/package.json +2 -2
  140. package/packages/i18n/package.json +1 -1
  141. package/packages/modules/package.json +2 -2
  142. package/packages/passport/package.json +1 -1
  143. package/packages/ssr/package.json +2 -2
  144. package/packages/storefront/client.d.ts +3 -0
  145. package/packages/storefront/config/astro/context-directive.mjs +2 -2
  146. package/packages/storefront/package.json +9 -7
  147. package/packages/storefront/src/analytics/event-to-fbq.ts +82 -0
  148. package/packages/storefront/src/analytics/event-to-ttq.ts +15 -0
  149. package/packages/storefront/src/helpers/afetch.ts +20 -8
  150. package/packages/storefront/src/helpers/sf-utils.ts +15 -0
  151. package/packages/storefront/src/lib/components/Carousel.vue +19 -14
  152. package/packages/storefront/src/lib/composables/use-product-card.ts +12 -0
  153. package/packages/storefront/src/lib/layouts/Base.astro +1 -0
  154. package/packages/storefront/src/lib/layouts/BaseHead.astro +9 -2
  155. package/packages/storefront/src/lib/scripts/push-analytics-events.ts +88 -0
  156. package/packages/storefront/src/lib/scripts/session-utm.ts +16 -6
  157. package/packages/storefront/src/lib/scripts/vbeta-app.ts +4 -0
  158. package/packages/storefront/src/lib/ssr-context.ts +2 -2
  159. package/packages/storefront/src/lib/state/shopping-cart.ts +35 -2
  160. package/packages/storefront/src/lib/state/use-analytics.ts +283 -0
  161. package/packages/test-base/package.json +1 -1
  162. package/packages/types/package.json +1 -1
  163. package/ecomplus-stores/barra-doce/functions/ssr/public/img/uploads/banner2.webp +0 -0
  164. package/ecomplus-stores/barra-doce/functions/ssr/public/img/uploads/headphone.webp +0 -0
  165. package/ecomplus-stores/barra-doce/functions/ssr/public/img/uploads/logo.png +0 -0
  166. package/ecomplus-stores/barra-doce/functions/ssr/public/img/uploads/passion.webp +0 -0
  167. package/ecomplus-stores/barra-doce/functions/ssr/public/img/uploads/rect8589.png +0 -0
  168. package/ecomplus-stores/barra-doce/functions/ssr/public/img/uploads/rect859.png +0 -0
  169. package/ecomplus-stores/barra-doce/functions/ssr/public/img/uploads/rect89.webp +0 -0
  170. package/ecomplus-stores/barra-doce/functions/ssr/src/layouts/Checkout.astro +0 -0
  171. package/ecomplus-stores/monocard/functions/ssr/content/extra-pages/contato.json +0 -11
  172. package/ecomplus-stores/monocard/functions/ssr/public/img/uploads/fluxo.png +0 -0
  173. package/ecomplus-stores/monocard/functions/ssr/src/layouts/Checkout.astro +0 -0
@@ -0,0 +1,62 @@
1
+ <template>
2
+ <div class="ui-section">
3
+ <div class="flex flex-wrap items-start justify-between gap-5 lg:flex-nowrap">
4
+ <ul class="mx-auto flex items-center gap-x-6 gap-y-3
5
+ overflow-x-auto md:mx-0 md:flex-wrap md:overflow-hidden lg:gap-x-8">
6
+ <li v-for="(stamp, i) in stamps" :key="i">
7
+ <ALink :href="stamp.href?.replace('{domain}', $settings.domain || '')">
8
+ <slot :name="`picture-${i}`" />
9
+ <span v-if="!stamp.img" class="flex items-center">
10
+ <span
11
+ v-if="stamp.icon"
12
+ class="[&>*]:from-success-200 [&>*]:to-success-700
13
+ mr-2 text-4xl [&>*]:bg-gradient-to-br"
14
+ >
15
+ <i v-if="stamp.icon === 'lock'" class="i-lock-closed"></i>
16
+ <i v-else-if="stamp.icon === 'check'" class="i-check-badge"></i>
17
+ <i v-else class="i-arrow-path-rounded-square"></i>
18
+ </span>
19
+ <span class="text-base-600 max-w-[140px] text-sm font-medium">
20
+ {{ stamp.alt }}
21
+ <i
22
+ v-if="stamp.href && stamp.href.charAt(0) !== '/'"
23
+ class="bg-base-400 i-arrow-top-right-on-square ml-0.5"
24
+ ></i>
25
+ </span>
26
+ </span>
27
+ </ALink>
28
+ </li>
29
+ </ul>
30
+ <div class="mx-auto flex flex-wrap items-center justify-end
31
+ gap-4 overflow-x-auto text-2xl md:mx-0 md:flex-nowrap md:overflow-hidden">
32
+ <PaymentMethodFlag
33
+ v-for="paymentMethod in $settings.paymentMethods"
34
+ :key="paymentMethod"
35
+ :flag="paymentMethod"
36
+ />
37
+ </div>
38
+ </div>
39
+ <div class="mt-7 justify-between gap-4
40
+ text-center text-xs md:flex md:text-left">
41
+ <div class="mb-3 md:mb-0">
42
+ @ {{ new Date().getFullYear() }} {{ $settings.corporateName }}
43
+ {{ $settings.address ? `/ ${$settings.address}` : '' }}
44
+ / {{ $settings.docNumber }}
45
+ </div>
46
+ <ALink href="https://www.ecomplus.io/" class="italic text-[#37003c]">
47
+ powered by <b>E-Com Plus</b>
48
+ </ALink>
49
+ </div>
50
+ </div>
51
+ </template>
52
+
53
+ <script setup lang="ts">
54
+ import type { LayoutContent } from '@@sf/content';
55
+ import PaymentMethodFlag from '@@sf/components/PaymentMethodFlag.vue';
56
+
57
+ export interface Props {
58
+ stamps?: LayoutContent['footer']['stamps'];
59
+ }
60
+
61
+ defineProps<Props>();
62
+ </script>
@@ -18,15 +18,18 @@
18
18
  <template #controls>
19
19
  <div
20
20
  v-show="slides.length > 1"
21
- class="absolute z-10 bottom-5 flex justify-end items-center
22
- w-screen right-5 xl:right-auto xl:max-w-screen-xl xl:left-1/2 xl:-ms-[640px]"
21
+ class="absolute bottom-5 right-5 z-10 flex w-screen
22
+ items-center justify-end xl:left-1/2 xl:right-auto xl:-ms-[640px] xl:max-w-screen-xl"
23
23
  >
24
- <div class="relative w-20 h-10 rounded-full
25
- bg-white/50 text-primary shadow-sm ring-1 ring-black/5">
26
- <CarouselControl class="w-10 h-10 rounded-full hover:bg-primary/10" is-prev>
27
- <i class="i-arrow-left-line"></i>
24
+ <div
25
+ class="text-primary relative h-10 w-20 rounded-full bg-white/50
26
+ shadow-sm ring-1 ring-black/5 transition-opacity"
27
+ :class="isMounted ? 'opacity-100' : 'opacity-20 [&>*]:cursor-wait'"
28
+ >
29
+ <CarouselControl class="hover:bg-primary/10 h-10 w-10 rounded-full" is-prev>
30
+ <i class="i-arrow-right-line rotate-180"></i>
28
31
  </CarouselControl>
29
- <CarouselControl class="w-10 h-10 rounded-full hover:bg-primary/10">
32
+ <CarouselControl class="hover:bg-primary/10 h-10 w-10 rounded-full">
30
33
  <i class="i-arrow-right-line"></i>
31
34
  </CarouselControl>
32
35
  </div>
@@ -38,8 +41,6 @@
38
41
 
39
42
  <script setup lang="ts">
40
43
  import type { Props as UseBannerProps } from '@@sf/composables/use-banner';
41
- import Carousel from '@@sf/components/Carousel.vue';
42
- import CarouselControl from '@@sf/components/CarouselControl.vue';
43
44
  import Banner from '~/components/Banner.vue';
44
45
 
45
46
  export type Props = {
@@ -48,4 +49,8 @@ export type Props = {
48
49
  }
49
50
 
50
51
  defineProps<Props>();
52
+ const isMounted = ref(false);
53
+ onMounted(() => {
54
+ isMounted.value = true;
55
+ });
51
56
  </script>
@@ -0,0 +1,151 @@
1
+ <template>
2
+ <div class="-mx-4 flex w-screen gap-3 sm:mx-0 sm:w-full md:h-[525px] 2xl:gap-5">
3
+ <Carousel
4
+ v-if="pictures.length > 1"
5
+ axis="y"
6
+ class="group hidden w-[300px] shrink-0 md:block"
7
+ >
8
+ <li
9
+ v-for="i in Math.ceil(pictures.length / 2)"
10
+ :key="i"
11
+ class="grid h-1/3 w-full grid-cols-2 gap-3 px-1 pb-3"
12
+ :class="i === 1 && 'pt-1'"
13
+ >
14
+ <template
15
+ v-for="index in [1, 2].map(ii => (i - 1) * 2 + (ii - 1))"
16
+ :key="`${i}-${index}`"
17
+ >
18
+ <button
19
+ v-if="index < pictures.length"
20
+ class="bg-secondary-100 h-full rounded"
21
+ @click="activeIndex = index"
22
+ >
23
+ <AImg
24
+ :picture="pictures[index]"
25
+ class="ring-secondary/10 h-full w-full rounded border-2
26
+ object-cover opacity-90 transition-colors"
27
+ :class="index === activeIndex
28
+ ? 'border-secondary/50 ring-4 cursor-auto'
29
+ : 'border-transparent hover:border-primary'
30
+ + ' hover:ring-4 hover:ring-primary/20'"
31
+ />
32
+ </button>
33
+ </template>
34
+ </li>
35
+ <template #controls>
36
+ <span>
37
+ <CarouselControl
38
+ v-if="pictures.length > 6"
39
+ class="hover:bg-primary-300/60 text-primary
40
+ !bottom-3 !left-1/2 -ml-5 h-10 w-10
41
+ rounded-full bg-white/60 text-xl
42
+ opacity-0 shadow-sm ring-1 ring-black/5
43
+ group-hover:opacity-90"
44
+ />
45
+ </span>
46
+ </template>
47
+ </Carousel>
48
+ <div class="relative aspect-square h-full grow md:aspect-auto">
49
+ <Carousel
50
+ as="div"
51
+ v-model:index="activeIndex"
52
+ class="text-base-600 [&_i]:i-arrow-right-line mx-auto
53
+ h-full w-full max-w-[525px] [&>*]:h-full [&_i]:mx-2 [&_i]:text-2xl"
54
+ :class="isLoadingLightbox && 'opacity-80'"
55
+ :id="psId"
56
+ >
57
+ <ALink
58
+ v-for="(picture, i) in pictures"
59
+ :key="`big-${i}`"
60
+ :href="picture.zoom?.size && picture.zoom.url"
61
+ :data-pswp-width="getImgSizes(picture.zoom || '').width"
62
+ :data-pswp-height="getImgSizes(picture.zoom || '').height"
63
+ target="_blank"
64
+ rel="noreferrer"
65
+ class="shrink-0 basis-full"
66
+ :class="picture.zoom?.size && 'cursor-zoom-in'"
67
+ v-once
68
+ @click.prevent="() => picture.zoom?.size && zoom(i)"
69
+ >
70
+ <AImg
71
+ :picture="picture"
72
+ preferred-size="big"
73
+ class="h-full w-full rounded object-cover"
74
+ :fetchpriority="i === 0 ? 'high' : 'low'"
75
+ :loading="i === 0 ? 'eager' : 'lazy'"
76
+ />
77
+ </ALink>
78
+ </Carousel>
79
+ <i
80
+ v-if="isLoadingLightbox"
81
+ class="i-arrow-path bg-base-200 absolute
82
+ left-1/2 top-1/2 -ml-7 -mt-7 h-14 w-14 animate-spin"
83
+ :aria-label="$t.i19loading"
84
+ ></i>
85
+ <ul
86
+ v-if="pictures.length > 1"
87
+ class="mt-2 flex justify-center gap-1.5 md:hidden"
88
+ >
89
+ <li v-for="i in pictures.length" :key="`d-${i}`">
90
+ <button
91
+ class="bg-base-700 block h-1.5 w-3 rounded-full"
92
+ :class="activeIndex !== i - 1 && 'opacity-40'"
93
+ :aria-label="`${$t.i19picture} ${i}`"
94
+ @click="activeIndex = i - 1"
95
+ />
96
+ </li>
97
+ </ul>
98
+ </div>
99
+ </div>
100
+ </template>
101
+
102
+ <script setup lang="ts">
103
+ import type { Products } from '@cloudcommerce/api/types';
104
+ import { imgSizes as getImgSizes } from '@ecomplus/utils';
105
+ import { useId } from '@@sf/sf-lib';
106
+
107
+ export interface Props {
108
+ pictures: Exclude<Products['pictures'], undefined>;
109
+ }
110
+
111
+ defineProps<Props>();
112
+ const activeIndex = ref(0);
113
+ const psId = ref('');
114
+ let lightbox: any;
115
+ const isLoadingLightbox = ref(false);
116
+ const zoom = (index: number) => {
117
+ psId.value = `ps-${useId()}`;
118
+ if (!lightbox && !isLoadingLightbox.value) {
119
+ isLoadingLightbox.value = true;
120
+ const styleId = 'photoswipe-style';
121
+ Promise.all([
122
+ // @ts-ignore
123
+ import('photoswipe/lightbox'),
124
+ import('photoswipe'),
125
+ !document.getElementById(styleId) && import('photoswipe/style.css?inline'),
126
+ ]).then(([
127
+ { default: PhotoSwipeLightbox },
128
+ { default: pswpModule },
129
+ cssImport,
130
+ ]) => {
131
+ if (cssImport) {
132
+ const { default: css } = cssImport;
133
+ const style = document.createElement('style');
134
+ style.id = styleId;
135
+ style.textContent = css;
136
+ document.head.appendChild(style);
137
+ }
138
+ lightbox = new PhotoSwipeLightbox({
139
+ gallery: `#${psId.value} > div`,
140
+ children: 'a',
141
+ pswpModule,
142
+ showAnimationDuration: 300,
143
+ hideAnimationDuration: 300,
144
+ });
145
+ lightbox.init();
146
+ lightbox.loadAndOpen(index);
147
+ isLoadingLightbox.value = false;
148
+ });
149
+ }
150
+ };
151
+ </script>
@@ -0,0 +1,107 @@
1
+ <template>
2
+ <form
3
+ class="text-base-800 w-full max-w-sm bg-white p-6 text-base md:p-8"
4
+ @submit.prevent="() => submitLogin(loginLinkActionUrl)"
5
+ >
6
+ <slot name="head" />
7
+ <p class="text-base-600 mb-4 text-2xl font-light">
8
+ {{ $t.i19hello }}
9
+ <span v-if="customerName">{{ customerName }}</span>
10
+ <span v-else class="lowercase">{{ $t.i19visitor }}</span>
11
+ </p>
12
+ <label v-if="isLinkSignIn" for="login-form-email">
13
+ {{ $t.i19sendLoginLinkByEmail }}
14
+ </label>
15
+ <input
16
+ id="login-form-email"
17
+ type="email"
18
+ placeholder="email@mail.com"
19
+ v-model="email"
20
+ class="mb-0.5 mt-2 w-full rounded"
21
+ required
22
+ />
23
+ <input
24
+ v-if="!isLinkSignIn"
25
+ type="password"
26
+ :placeholder="$t.i19password"
27
+ v-model="password"
28
+ class="mb-0.5 mt-2 w-full rounded lowercase"
29
+ required
30
+ />
31
+ <div class="relative">
32
+ <div
33
+ class="transition-opacity"
34
+ :class="isEmailSentMsg && 'opacity-0 invisible'"
35
+ >
36
+ <a
37
+ v-show="!isSignUp && !isLinkSignInBlocked"
38
+ href="?password"
39
+ class="ui-link text-base-500 text-right text-sm lowercase"
40
+ @click.prevent="isLinkSignIn = !isLinkSignIn"
41
+ >
42
+ {{ isLinkSignIn ? $t.i19enterWithPassword : $t.i19iForgotMyPassword }}
43
+ </a>
44
+ <button
45
+ type="submit"
46
+ :disabled="isSubmitting"
47
+ class="ui-btn-lg ui-btn-primary mb-2 mt-5 w-full"
48
+ :class="isSubmitting && 'opacity-50'"
49
+ >
50
+ {{ isSignUp ? $t.i19signUp : $t.i19accessMyAccount }}
51
+ </button>
52
+ </div>
53
+ <Fade speed="slow">
54
+ <p
55
+ v-if="isEmailSentMsg"
56
+ class="text-success-800 absolute left-0 top-0 mt-4 font-medium"
57
+ >
58
+ <i class="i-check mr-1"></i>
59
+ {{ $t.i19emailWasSentMsg }}
60
+ </p>
61
+ </Fade>
62
+ <a
63
+ href="?sign_up"
64
+ class="ui-btn-lg ui-btn-contrast block w-full text-center"
65
+ @click.prevent="isSignUp = !isSignUp"
66
+ >
67
+ {{ isSignUp ? $t.i19accessMyAccount : $t.i19createAnAccount }}
68
+ </a>
69
+ </div>
70
+ </form>
71
+ </template>
72
+
73
+ <script lang="ts" setup>
74
+ import useLoginForm from '@@sf/composables/use-login-form';
75
+ import { customerName } from '@@sf/state/customer-session';
76
+
77
+ export interface Props {
78
+ loginLinkActionUrl?: string | null;
79
+ }
80
+
81
+ defineProps<Props>();
82
+ const {
83
+ isLinkSignIn,
84
+ isSignUp,
85
+ email,
86
+ password,
87
+ isSubmitting,
88
+ submitLogin,
89
+ } = useLoginForm();
90
+ const isEmailSentMsg = ref(false);
91
+ const countEmailsSent = ref(0);
92
+ const isLinkSignInBlocked = computed(() => {
93
+ return countEmailsSent.value > 9;
94
+ });
95
+ watch((isSubmitting), () => {
96
+ if (!isSubmitting.value && isLinkSignIn.value) {
97
+ isEmailSentMsg.value = true;
98
+ countEmailsSent.value += 1;
99
+ }
100
+ });
101
+ watch([email, isSignUp], () => {
102
+ isEmailSentMsg.value = false;
103
+ });
104
+ watch(isLinkSignInBlocked, () => {
105
+ isLinkSignIn.value = false;
106
+ });
107
+ </script>
@@ -1,11 +1,11 @@
1
1
  <template>
2
2
  <div class="bg-base-100 relative z-20">
3
- <div class="container lg:w-2/3 mx-auto px-3 py-1">
3
+ <div class="container mx-auto px-3 py-1 lg:w-2/3">
4
4
  <Carousel :autoplay="countValidSlides > 1 ? 7000 : undefined">
5
5
  <li
6
6
  v-for="(slide, i) in slides"
7
7
  :key="i"
8
- class="shrink-0 basis-full h-full text-center"
8
+ class="h-full shrink-0 basis-full text-center"
9
9
  >
10
10
  <ALink
11
11
  :href="slide.href"
@@ -16,17 +16,17 @@
16
16
  <span
17
17
  v-if="parsedContents[i]"
18
18
  v-html="parsedContents[i]"
19
- class="prose text-sm text-base-800"
19
+ class="prose text-base-800 text-sm"
20
20
  ></span>
21
21
  </ALink>
22
22
  </li>
23
23
  <template #controls>
24
24
  <div
25
25
  v-show="countValidSlides > 1"
26
- class="text-xl leading-none text-base-400"
26
+ class="text-base-400 text-xl leading-none"
27
27
  >
28
- <CarouselControl class="pr-2 bg-base-100 hover:text-base-700" is-prev />
29
- <CarouselControl class="pl-2 bg-base-100 hover:text-base-700" />
28
+ <CarouselControl class="bg-base-100 hover:text-base-700 pr-2" is-prev />
29
+ <CarouselControl class="bg-base-100 hover:text-base-700 pl-2" />
30
30
  </div>
31
31
  </template>
32
32
  </Carousel>
@@ -39,8 +39,6 @@ import {
39
39
  type Props as UsePitchBarProps,
40
40
  usePitchBar,
41
41
  } from '@@sf/composables/use-pitch-bar';
42
- import Carousel from '@@sf/components/Carousel.vue';
43
- import CarouselControl from '@@sf/components/CarouselControl.vue';
44
42
 
45
43
  export interface Props extends UsePitchBarProps {}
46
44
 
@@ -1,7 +1,7 @@
1
1
  <template>
2
2
  <div
3
3
  class="text-base-600
4
- [&>div]:[font-size:90%] [&_small]:[font-size:92%] [&_small]:lowercase"
4
+ [&>div]:[font-size:90%] [&_small]:lowercase [&_small]:[font-size:92%]"
5
5
  :class="isBig ? 'text-lg' : null"
6
6
  >
7
7
  <span v-if="comparePrice" class="text-base-500 mr-1 [font-size:87%]">
@@ -14,8 +14,8 @@
14
14
  </small>
15
15
  </span>
16
16
  <strong
17
- class="inline-block text-base-800"
18
- :class="isBig ? 'text-5xl block' : null"
17
+ class="text-base-800"
18
+ :class="isBig ? 'text-4xl block' : 'inline-block'"
19
19
  >
20
20
  <small v-if="hasVariedPrices">
21
21
  {{ `${$t.i19asOf} ` }}
@@ -2,49 +2,50 @@
2
2
  <article
3
3
  ref="card"
4
4
  :data-sku="product.sku"
5
- class="relative h-full max-w-[350px] mx-auto py-3 group"
5
+ class="group relative mx-auto h-full max-w-[350px] py-3"
6
6
  >
7
7
  <ALink
8
8
  :href="link"
9
- class="flex flex-col h-full rounded overflow-hidden
10
- group-hover:shadow group-hover:ring-1 ring-black/5"
9
+ class="flex h-full flex-col overflow-hidden rounded
10
+ ring-black/5 group-hover:shadow group-hover:ring-1"
11
11
  >
12
12
  <div class="aspect-square p-2
13
- motion-safe:group-hover:scale-110 transition-transform">
14
- <div class="relative w-full h-full bg-white rounded overflow-hidden
13
+ transition-transform motion-safe:group-hover:scale-110">
14
+ <div class="relative h-full w-full overflow-hidden rounded bg-white
15
15
  group-hover:rounded-none">
16
16
  <span v-if="images?.length" class="text-xs text-opacity-70">
17
17
  <AImg
18
18
  :picture="images[0]"
19
19
  :alt="title"
20
- class="absolute top-0 left-0 block w-full h-full object-cover"
20
+ class="absolute left-0 top-0 block h-full w-full object-cover"
21
21
  />
22
22
  <AImg
23
23
  v-if="images[1] && wasHoveredOnce"
24
24
  :picture="images[1]"
25
25
  :alt="title"
26
- class="absolute top-0 left-0 block w-full h-full object-cover
27
- opacity-0 group-hover:opacity-100 transition-opacity
28
- motion-safe:duration-300 text-transparent z-10"
26
+ class="absolute left-0 top-0 z-10 block h-full w-full
27
+ object-cover text-transparent opacity-0
28
+ transition-opacity group-hover:opacity-100 motion-safe:duration-300"
29
29
  />
30
30
  </span>
31
31
  <div
32
32
  v-else
33
- class="w-full h-full bg-gradient-to-br from-base-50/20 to-base-100"
33
+ class="from-base-50/20 to-base-100 h-full w-full bg-gradient-to-br"
34
34
  />
35
35
  </div>
36
36
  </div>
37
37
  <span
38
38
  v-if="discountPercentage"
39
- class=":uno: absolute top-9 right-2
40
- group-hover:scale-110 group-hover:translate-x-2 transition-transform
41
- bg-secondary/70 text-on-secondary text-xs
42
- py-0.5 pr-1.5 pl-3 [clip-path:polygon(20%_0,100%_0,100%_100%,0_100%)]"
39
+ class=":uno: bg-secondary/70 text-on-secondary absolute
40
+ right-2 top-9 py-0.5
41
+ pl-3 pr-1.5 text-xs
42
+ transition-transform [clip-path:polygon(20%_0,100%_0,100%_100%,0_100%)]
43
+ group-hover:translate-x-2 group-hover:scale-110"
43
44
  >
44
45
  -<strong>{{ discountPercentage }}</strong>%
45
46
  </span>
46
- <div class="relative flex flex-col grow justify-start p-4
47
- group-hover:backdrop-blur-md bg-white/40 z-10">
47
+ <div class="relative z-10 flex grow flex-col justify-start
48
+ bg-white/40 p-4 group-hover:backdrop-blur-md">
48
49
  <div class="pb-2">
49
50
  <div v-if="isActive">
50
51
  <Prices :product="product" />
@@ -55,19 +56,19 @@
55
56
  </div>
56
57
  <component
57
58
  :is="headingTag"
58
- class="ui-link no-underline line-clamp-2 text-sm"
59
+ class="ui-link line-clamp-2 text-sm no-underline"
59
60
  :class="[
60
61
  isActive ? 'text-base-700' : 'text-base-500',
61
- link ? 'group-hover:underline group-hover:text-primary' : null,
62
+ link ? 'group-hover:text-primary group-hover:underline' : null,
62
63
  ]"
63
64
  >
64
65
  {{ title }}
65
66
  </component>
66
67
  <button
67
68
  v-if="isActive && !hasVariations"
68
- class="absolute -top-12 left-1/2 -ml-7 w-14 h-14 lg:-top-13 lg:left-4 lg:ml-0
69
- primary opacity-75 hover:opacity-100
70
- rounded-full text-3xl leading-none p-2 z-10"
69
+ class="lg:-top-13 primary absolute -top-12 left-1/2 z-10 -ml-7 h-14 w-14
70
+ rounded-full p-2 text-3xl
71
+ leading-none opacity-75 hover:opacity-100 lg:left-4 lg:ml-0"
71
72
  @click.prevent="addProductToCart(product)"
72
73
  :aria-label="$t.i19addToCart"
73
74
  >
@@ -79,7 +80,6 @@
79
80
  </template>
80
81
 
81
82
  <script setup lang="ts">
82
- import { ref } from 'vue';
83
83
  import { watchOnce, useElementHover } from '@vueuse/core';
84
84
  import { addProductToCart } from '@@sf/state/shopping-cart';
85
85
  import {
@@ -0,0 +1,122 @@
1
+ <template>
2
+ <section class="ui-section relative grid grid-cols-1 items-start
3
+ gap-5 md:gap-4 lg:grid-cols-4 2xl:gap-6">
4
+ <div class="w-full lg:col-span-3">
5
+ <template v-if="product.pictures?.length">
6
+ <ImagesGallery :pictures="product.pictures" />
7
+ </template>
8
+ </div>
9
+ <div class="top-0 py-4 lg:sticky">
10
+ <h1 class="ui-text-brand text-base-700 text-xl">
11
+ {{ title }}
12
+ </h1>
13
+ <div v-if="isActive" class="mt-5">
14
+ <Prices :product="product" is-big is-literal />
15
+ <SkuSelector
16
+ v-if="product.variations"
17
+ :variations="product.variations"
18
+ v-model:variation-id="variationId"
19
+ class="my-4"
20
+ />
21
+ <Fade slide="down">
22
+ <div v-if="hasSkuSelectionAlert" class="ui-alert inline-block">
23
+ {{ $t.i19selectVariationMsg }}
24
+ <i class="i-arrow-right-line ml-1 -rotate-90"></i>
25
+ </div>
26
+ </Fade>
27
+ <div class="mt-5 flex flex-wrap items-center gap-x-4 gap-y-2
28
+ overflow-hidden md:flex-nowrap lg:mt-4 lg:flex-wrap">
29
+ <QuantitySelector
30
+ v-model="quantity"
31
+ :min="product.min_quantity"
32
+ :max="product.quantity"
33
+ class="border-base-100 rounded md:mr-5
34
+ lg:mb-2 lg:mr-auto lg:border-2"
35
+ />
36
+ <CheckoutLink
37
+ class="ui-btn-lg ui-btn-primary grow text-center"
38
+ to="checkout"
39
+ :cart-item="{
40
+ product_id: product._id,
41
+ quantity,
42
+ variation_id: variationId || undefined,
43
+ }"
44
+ :data-tooltip="!isSkuSelected ? $t.i19chooseProductDetailsToBuy : null"
45
+ @click="checkVariation"
46
+ >
47
+ <i class="i-chevron-double-right mr-1"></i>
48
+ {{ $t.i19buy }}
49
+ </CheckoutLink>
50
+ <button
51
+ class="ui-btn-lg ui-btn-contrast grow"
52
+ @click.prevent="addToCart"
53
+ :data-tooltip="!isSkuSelected ? $t.i19chooseProductDetailsToBuy : null"
54
+ >
55
+ {{ $t.i19addToCart }}
56
+ </button>
57
+ </div>
58
+ </div>
59
+ </div>
60
+ <div class="w-full lg:col-span-3">
61
+ <slot name="description" />
62
+ </div>
63
+ </section>
64
+ </template>
65
+
66
+ <script setup lang="ts">
67
+ import type { ResourceId, Products } from '@cloudcommerce/api/types';
68
+ import { useUrlSearchParams } from '@vueuse/core';
69
+ import { addProductToCart } from '@@sf/state/shopping-cart';
70
+ import { useProductCard } from '@@sf/composables/use-product-card';
71
+ import CheckoutLink from '@@sf/components/CheckoutLink.vue';
72
+ import QuantitySelector from '@@sf/components/QuantitySelector.vue';
73
+ import Prices from '~/components/Prices.vue';
74
+ import ImagesGallery from '~/components/ImagesGallery.vue';
75
+ import SkuSelector from '~/components/SkuSelector.vue';
76
+
77
+ export interface Props {
78
+ product?: Products;
79
+ }
80
+
81
+ const props = withDefaults(defineProps<Props>(), {
82
+ product: () => globalThis.$storefront.apiContext?.doc as Products,
83
+ });
84
+ const {
85
+ product,
86
+ title,
87
+ isActive,
88
+ } = useProductCard<Products>(props);
89
+ const quantity = ref(product.min_quantity || 1);
90
+ const params = useUrlSearchParams('history');
91
+ const hasSkuSelectionAlert = ref(false);
92
+ const variationId = ref<ResourceId | null>(null);
93
+ watch(variationId, (_variationId) => {
94
+ if (_variationId) {
95
+ params.var = _variationId;
96
+ hasSkuSelectionAlert.value = false;
97
+ }
98
+ });
99
+ onMounted(() => {
100
+ watch(params, ({ var: variation }) => {
101
+ if (typeof variation === 'string' && variation) {
102
+ variationId.value = variation as ResourceId;
103
+ }
104
+ }, { immediate: true });
105
+ });
106
+ const isSkuSelected = computed(() => {
107
+ return Boolean(!product.variations?.length || variationId.value);
108
+ });
109
+ const checkVariation = (ev?: Event) => {
110
+ if (!isSkuSelected.value) {
111
+ if (ev) ev.preventDefault();
112
+ hasSkuSelectionAlert.value = true;
113
+ } else {
114
+ hasSkuSelectionAlert.value = false;
115
+ }
116
+ return !hasSkuSelectionAlert.value;
117
+ };
118
+ const addToCart = () => {
119
+ if (!checkVariation()) return;
120
+ addProductToCart(product, variationId.value ? variationId.value : undefined);
121
+ };
122
+ </script>