cloudcommerce 2.0.0 → 2.0.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 (114) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/ecomplus-stores/barra-doce/conf/firebase.json +7 -0
  3. package/ecomplus-stores/barra-doce/functions/many/package.json +3 -3
  4. package/ecomplus-stores/barra-doce/functions/ssr/astro.config.mjs +18 -3
  5. package/ecomplus-stores/barra-doce/functions/ssr/content/blog/3.md +24 -0
  6. package/ecomplus-stores/barra-doce/functions/ssr/content/blog/4.md +24 -0
  7. package/ecomplus-stores/barra-doce/functions/ssr/content/blog/5.md +24 -0
  8. package/ecomplus-stores/barra-doce/functions/ssr/content/blog/dos.md +24 -0
  9. package/ecomplus-stores/barra-doce/functions/ssr/content/blog/uno.md +24 -0
  10. package/ecomplus-stores/barra-doce/functions/ssr/content/extra-pages/contato.md +9 -0
  11. package/ecomplus-stores/barra-doce/functions/ssr/content/extra-pages/terms.md +20 -0
  12. package/ecomplus-stores/barra-doce/functions/ssr/content/extra-pages/trocas.md +9 -0
  13. package/ecomplus-stores/barra-doce/functions/ssr/content/pages/brands.json +16 -0
  14. package/ecomplus-stores/barra-doce/functions/ssr/content/pages/categories.json +16 -0
  15. package/ecomplus-stores/barra-doce/functions/ssr/content/pages/collections.json +16 -0
  16. package/ecomplus-stores/barra-doce/functions/ssr/content/pages/home.json +3 -3
  17. package/ecomplus-stores/barra-doce/functions/ssr/content/pages/search.json +7 -0
  18. package/ecomplus-stores/barra-doce/functions/ssr/package.json +10 -9
  19. package/ecomplus-stores/barra-doce/functions/ssr/src/assets/style.css +24 -10
  20. package/ecomplus-stores/barra-doce/functions/ssr/src/components/BannersGrid.astro +1 -1
  21. package/ecomplus-stores/barra-doce/functions/ssr/src/components/BlogPosts.vue +88 -0
  22. package/ecomplus-stores/barra-doce/functions/ssr/src/components/Breadcrumbs.vue +49 -0
  23. package/ecomplus-stores/barra-doce/functions/ssr/src/components/CartSidebar.vue +2 -2
  24. package/ecomplus-stores/barra-doce/functions/ssr/src/components/CheckoutPage.vue +14 -0
  25. package/ecomplus-stores/barra-doce/functions/ssr/src/components/Collapse.vue +4 -4
  26. package/ecomplus-stores/barra-doce/functions/ssr/src/components/ContentEntry.vue +23 -0
  27. package/ecomplus-stores/barra-doce/functions/ssr/src/components/DocBanners.vue +26 -0
  28. package/ecomplus-stores/barra-doce/functions/ssr/src/components/FooterStamps.vue +1 -1
  29. package/ecomplus-stores/barra-doce/functions/ssr/src/components/HeroSlider.vue +1 -1
  30. package/ecomplus-stores/barra-doce/functions/ssr/src/components/ImagesGallery.vue +5 -4
  31. package/ecomplus-stores/barra-doce/functions/ssr/src/components/LoginForm.vue +2 -2
  32. package/ecomplus-stores/barra-doce/functions/ssr/src/components/PageTitle.vue +43 -0
  33. package/ecomplus-stores/barra-doce/functions/ssr/src/components/Pagination.vue +67 -0
  34. package/ecomplus-stores/barra-doce/functions/ssr/src/components/Prices.vue +1 -1
  35. package/ecomplus-stores/barra-doce/functions/ssr/src/components/ProductCard.vue +3 -4
  36. package/ecomplus-stores/barra-doce/functions/ssr/src/components/ProductDetails.vue +4 -3
  37. package/ecomplus-stores/barra-doce/functions/ssr/src/components/ProductShelf.vue +34 -18
  38. package/ecomplus-stores/barra-doce/functions/ssr/src/components/ProductSpecifications.vue +1 -1
  39. package/ecomplus-stores/barra-doce/functions/ssr/src/components/SearchFilters.vue +122 -0
  40. package/ecomplus-stores/barra-doce/functions/ssr/src/components/SearchModal.vue +97 -1
  41. package/ecomplus-stores/barra-doce/functions/ssr/src/components/SearchShowcase.vue +169 -0
  42. package/ecomplus-stores/barra-doce/functions/ssr/src/components/ShopFooter.vue +10 -6
  43. package/ecomplus-stores/barra-doce/functions/ssr/src/components/ShopHeader.vue +64 -64
  44. package/ecomplus-stores/barra-doce/functions/ssr/src/components/ShopHeaderSubmenu.vue +1 -1
  45. package/ecomplus-stores/barra-doce/functions/ssr/src/components/ShopSidenav.vue +1 -1
  46. package/ecomplus-stores/barra-doce/functions/ssr/src/components/SkuSelector.vue +7 -7
  47. package/ecomplus-stores/barra-doce/functions/ssr/src/env.d.ts +4 -0
  48. package/ecomplus-stores/barra-doce/functions/ssr/src/layouts/Base.astro +1 -1
  49. package/ecomplus-stores/barra-doce/functions/ssr/src/layouts/PageFooter.astro +4 -1
  50. package/ecomplus-stores/barra-doce/functions/ssr/src/layouts/PageHeader.astro +2 -2
  51. package/ecomplus-stores/barra-doce/functions/ssr/src/main/Fallback.astro +28 -5
  52. package/ecomplus-stores/barra-doce/functions/ssr/src/main/Main.astro +26 -0
  53. package/ecomplus-stores/barra-doce/functions/ssr/src/main/content/Hero.astro +33 -0
  54. package/ecomplus-stores/barra-doce/functions/ssr/src/main/{Sections.astro → content/Sections.astro} +29 -6
  55. package/ecomplus-stores/barra-doce/functions/ssr/src/pages/[...slug].astro +35 -8
  56. package/ecomplus-stores/barra-doce/functions/ssr/src/pages/app/account.astro +6 -2
  57. package/ecomplus-stores/barra-doce/functions/ssr/src/pages/app/index.astro +24 -4
  58. package/ecomplus-stores/barra-doce/functions/ssr/src/pages/blog/[page].astro +71 -0
  59. package/ecomplus-stores/barra-doce/functions/ssr/src/pages/blog/_blog-pages.ts +9 -0
  60. package/ecomplus-stores/barra-doce/functions/ssr/src/pages/~fallback.astro +9 -2
  61. package/ecomplus-stores/barra-doce/functions/with-apps/package.json +3 -3
  62. package/ecomplus-stores/barra-doce/package.json +2 -2
  63. package/package.json +9 -9
  64. package/packages/api/package.json +1 -1
  65. package/packages/apps/affiliate-program/package.json +1 -1
  66. package/packages/apps/correios/package.json +2 -2
  67. package/packages/apps/custom-payment/package.json +1 -1
  68. package/packages/apps/custom-shipping/package.json +1 -1
  69. package/packages/apps/datafrete/package.json +2 -2
  70. package/packages/apps/discounts/package.json +1 -1
  71. package/packages/apps/emails/package.json +1 -1
  72. package/packages/apps/fb-conversions/package.json +1 -1
  73. package/packages/apps/flash-courier/package.json +3 -3
  74. package/packages/apps/frenet/package.json +2 -2
  75. package/packages/apps/galaxpay/package.json +2 -2
  76. package/packages/apps/google-analytics/package.json +2 -2
  77. package/packages/apps/jadlog/package.json +2 -2
  78. package/packages/apps/loyalty-points/package.json +1 -1
  79. package/packages/apps/mandae/package.json +3 -3
  80. package/packages/apps/melhor-envio/package.json +2 -2
  81. package/packages/apps/mercadopago/package.json +2 -2
  82. package/packages/apps/pagarme/package.json +2 -2
  83. package/packages/apps/pagarme-v5/package.json +2 -2
  84. package/packages/apps/paghiper/package.json +2 -2
  85. package/packages/apps/pix/package.json +2 -2
  86. package/packages/apps/tiny-erp/package.json +2 -2
  87. package/packages/apps/webhooks/package.json +2 -2
  88. package/packages/cli/package.json +1 -1
  89. package/packages/config/package.json +1 -1
  90. package/packages/emails/package.json +2 -2
  91. package/packages/eslint/package.json +3 -3
  92. package/packages/events/package.json +1 -1
  93. package/packages/feeds/package.json +1 -1
  94. package/packages/firebase/package.json +1 -1
  95. package/packages/i18n/package.json +1 -1
  96. package/packages/modules/package.json +2 -2
  97. package/packages/passport/package.json +1 -1
  98. package/packages/ssr/package.json +3 -3
  99. package/packages/storefront/config/astro/context-directive.mjs +16 -2
  100. package/packages/storefront/config/astro/index.d.ts +3 -1
  101. package/packages/storefront/package.json +6 -6
  102. package/packages/storefront/src/lib/components/SharedData.astro +1 -1
  103. package/packages/storefront/src/lib/composables/use-shared-data.ts +2 -1
  104. package/packages/storefront/src/lib/composables/use-sku-selector.ts +11 -7
  105. package/packages/test-base/package.json +1 -1
  106. package/packages/types/package.json +1 -1
  107. package/ecomplus-stores/barra-doce/functions/ssr/content/blog/.gitkeep +0 -0
  108. package/ecomplus-stores/barra-doce/functions/ssr/content/extra-pages/contato.json +0 -11
  109. package/ecomplus-stores/barra-doce/functions/ssr/content/extra-pages/terms.json +0 -11
  110. package/ecomplus-stores/barra-doce/functions/ssr/content/extra-pages/trocas.json +0 -11
  111. package/ecomplus-stores/barra-doce/functions/ssr/src/components/Breadcrumbs.astro +0 -44
  112. package/ecomplus-stores/barra-doce/functions/ssr/src/main/Home.astro +0 -37
  113. package/ecomplus-stores/barra-doce/functions/ssr/src/main/Wildcard.astro +0 -18
  114. package/ecomplus-stores/barra-doce/functions/ssr/src/pages/index.astro +0 -32
@@ -0,0 +1,26 @@
1
+ <template>
2
+ <section v-if="pictures.length" class="ui-section">
3
+ <ul class="flex flex-wrap gap-4 lg:flex-nowrap">
4
+ <li
5
+ v-for="(picture, i) in pictures" :key="i"
6
+ class="lg:flex-1 [&_img]:w-full [&_img]:object-cover"
7
+ >
8
+ <AImg :picture="picture" />
9
+ </li>
10
+ </ul>
11
+ </section>
12
+ </template>
13
+
14
+ <script setup lang="ts">
15
+ import type { Categories, Brands, Collections } from '@cloudcommerce/api/types';
16
+
17
+ export interface Props {
18
+ apiDoc?: Partial<Categories | Brands | Collections>;
19
+ title?: string;
20
+ }
21
+
22
+ const props = withDefaults(defineProps<Props>(), {
23
+ apiDoc: () => globalThis.$storefront.apiContext?.doc as Categories,
24
+ });
25
+ const pictures = computed(() => props.apiDoc.pictures || []);
26
+ </script>
@@ -44,7 +44,7 @@
44
44
  / {{ $settings.docNumber }}
45
45
  </div>
46
46
  <ALink href="https://www.ecomplus.io/" class="italic text-[#37003c]">
47
- powered by <b>E-Com Plus</b>
47
+ powered by <b>e-com.plus</b>
48
48
  </ALink>
49
49
  </div>
50
50
  </div>
@@ -1,5 +1,5 @@
1
1
  <template>
2
- <section class="relative mx-auto">
2
+ <section class="relative mx-auto mb-9 md:mb-12">
3
3
  <Carousel :autoplay="autoplay" class="secondary-subtle [&>*]:items-center">
4
4
  <li
5
5
  v-for="(slide, i) in slides"
@@ -8,7 +8,7 @@
8
8
  <li
9
9
  v-for="i in Math.ceil(pictures.length / 2)"
10
10
  :key="i"
11
- class="grid h-1/3 w-full grid-cols-2 gap-3 px-1 pb-3"
11
+ class="grid w-full grid-cols-2 gap-3 px-1 pb-3"
12
12
  :class="i === 1 && 'pt-1'"
13
13
  >
14
14
  <template
@@ -17,7 +17,7 @@
17
17
  >
18
18
  <button
19
19
  v-if="index < pictures.length"
20
- class="bg-secondary-100 h-full rounded"
20
+ class="bg-secondary-100 block rounded md:h-[175px]"
21
21
  @click="activeIndex = index"
22
22
  >
23
23
  <AImg
@@ -39,9 +39,10 @@
39
39
  class="hover:bg-primary-300/60 text-primary
40
40
  !bottom-3 !left-1/2 -ml-5 h-10 w-10
41
41
  rounded-full bg-white/60 text-xl
42
- opacity-0 shadow-sm ring-1 ring-black/5
43
- group-hover:opacity-90"
42
+ opacity-0 shadow-sm ring-1 ring-black/5 group-hover:opacity-90"
44
43
  />
44
+ <span class="absolute bottom-0 block h-2 w-full
45
+ bg-gradient-to-b from-transparent to-white"></span>
45
46
  </span>
46
47
  </template>
47
48
  </Carousel>
@@ -17,7 +17,7 @@
17
17
  type="email"
18
18
  placeholder="email@mail.com"
19
19
  v-model="email"
20
- class="mb-0.5 mt-2 w-full rounded"
20
+ class="mb-0.5 mt-2 w-full"
21
21
  required
22
22
  />
23
23
  <input
@@ -25,7 +25,7 @@
25
25
  type="password"
26
26
  :placeholder="$t.i19password"
27
27
  v-model="password"
28
- class="mb-0.5 mt-2 w-full rounded lowercase"
28
+ class="mb-0.5 mt-2 w-full lowercase"
29
29
  required
30
30
  />
31
31
  <div class="relative">
@@ -0,0 +1,43 @@
1
+ <template>
2
+ <section v-if="title" class="ui-section">
3
+ <div class="flex items-center gap-4">
4
+ <AImg
5
+ v-if="logo"
6
+ :picture="logo"
7
+ :alt="logo.alt || title"
8
+ class="h-auto max-w-[150px] xl:max-w-[200px]"
9
+ />
10
+ <h1 class="ui-title">
11
+ {{ title }}
12
+ </h1>
13
+ </div>
14
+ <div class="prose">
15
+ <p v-if="description">
16
+ {{ description }}
17
+ </p>
18
+ </div>
19
+ </section>
20
+ </template>
21
+
22
+ <script setup lang="ts">
23
+ import type { Categories, Brands, Collections } from '@cloudcommerce/api/types';
24
+
25
+ export interface Props {
26
+ apiDoc?: Partial<Categories | Brands | Collections>;
27
+ title?: string;
28
+ description?: string;
29
+ }
30
+
31
+ const props = withDefaults(defineProps<Props>(), {
32
+ apiDoc: () => globalThis.$storefront.apiContext?.doc as Categories,
33
+ });
34
+ const title = computed(() => {
35
+ return props.title || props.apiDoc?.name;
36
+ });
37
+ const logo = computed(() => {
38
+ return (props.apiDoc as Brands)?.logo;
39
+ });
40
+ const description = computed(() => {
41
+ return props.description || props.apiDoc?.short_description;
42
+ });
43
+ </script>
@@ -0,0 +1,67 @@
1
+ <template>
2
+ <ol class="text-base-900 flex justify-center gap-1.5
3
+ text-center text-sm font-medium leading-9">
4
+ <li>
5
+ <ALink
6
+ :href="prevPageLink"
7
+ @click.prevent="prevPageLink && emit('update:page', page - 1)"
8
+ class="mr-1 block min-w-9 rounded border px-3"
9
+ :class="prevPageLink
10
+ ? 'border-base-100 hover:bg-base-100'
11
+ : 'border-transparent text-base-500'"
12
+ >
13
+ <i class="i-arrow-right rotate-180"></i>
14
+ <span class="ml-1.5 hidden md:inline">{{ $t.i19previous }}</span>
15
+ </ALink>
16
+ </li>
17
+ <li v-for="(pageN, i) in pages" :key="pageN">
18
+ <ALink
19
+ :href="pageN !== page ? pageLinks[i] : null"
20
+ @click.prevent="emit('update:page', pageN)"
21
+ class="block w-9 rounded border ring-black/10"
22
+ :class="pageN === page
23
+ ? 'bg-base-50 border-base-100 ring text-base-700'
24
+ : 'border-transparent hover:bg-base-100'"
25
+ >
26
+ {{ pageN }}
27
+ </ALink>
28
+ </li>
29
+ <li>
30
+ <ALink
31
+ :href="nextPageLink"
32
+ @click.prevent="nextPageLink && emit('update:page', page + 1)"
33
+ class="ml-1 block min-w-9 rounded border px-3"
34
+ :class="nextPageLink
35
+ ? 'border-base-100 hover:bg-base-100'
36
+ : 'border-transparent text-base-500'"
37
+ >
38
+ <span class="mr-1.5 hidden md:inline">{{ $t.i19next }}</span>
39
+ <i class="i-arrow-right"></i>
40
+ </ALink>
41
+ </li>
42
+ </ol>
43
+ </template>
44
+
45
+ <script setup lang="ts">
46
+ import {
47
+ type Props as UsePaginationProps,
48
+ usePagination,
49
+ } from '@@sf/composables/use-pagination';
50
+
51
+ export interface Props extends UsePaginationProps {}
52
+
53
+ const props = withDefaults(defineProps<Props>(), {
54
+ page: 1,
55
+ maxPages: 7,
56
+ isUrlPath: false,
57
+ });
58
+ const emit = defineEmits<{
59
+ 'update:page': [value: number]
60
+ }>();
61
+ const {
62
+ pages,
63
+ pageLinks,
64
+ prevPageLink,
65
+ nextPageLink,
66
+ } = usePagination(props);
67
+ </script>
@@ -4,7 +4,7 @@
4
4
  [&>div]:[font-size:90%] [&_small]:lowercase [&_small]:[font-size:92%]"
5
5
  :class="isBig ? 'text-lg' : null"
6
6
  >
7
- <span v-if="comparePrice" class="text-base-500 mr-1 [font-size:87%]">
7
+ <span v-if="comparePrice" class="text-base-500 mr-1 text-[87%]">
8
8
  <small v-if="isLiteral">
9
9
  {{ `${$t.i19from} ` }}
10
10
  </small>
@@ -2,11 +2,11 @@
2
2
  <article
3
3
  ref="card"
4
4
  :data-sku="product.sku"
5
- class="group relative mx-auto h-full max-w-[350px] py-3"
5
+ class="group relative mx-auto h-full max-w-[350px] px-0.5 py-3 hover:z-[1]"
6
6
  >
7
7
  <ALink
8
8
  :href="link"
9
- class="flex h-full flex-col overflow-hidden rounded
9
+ class="flex h-full flex-col overflow-hidden rounded bg-white
10
10
  ring-black/5 group-hover:shadow group-hover:ring-1"
11
11
  >
12
12
  <div class="aspect-square p-2
@@ -44,8 +44,7 @@
44
44
  >
45
45
  -<strong>{{ discountPercentage }}</strong>%
46
46
  </span>
47
- <div class="relative z-10 flex grow flex-col justify-start
48
- bg-white/40 p-4 group-hover:backdrop-blur-md">
47
+ <div class="relative z-10 flex grow flex-col justify-between p-4">
49
48
  <div class="pb-2">
50
49
  <div v-if="isActive">
51
50
  <Prices :product="product" />
@@ -6,8 +6,9 @@
6
6
  <ImagesGallery :pictures="product.pictures" />
7
7
  </template>
8
8
  </div>
9
- <div class="top-0 py-4 lg:sticky">
10
- <h1 class="ui-text-brand text-base-700 text-xl">
9
+ <div class="lg:sticky-header:translate-y-14 top-0 py-4
10
+ transition-transform lg:sticky">
11
+ <h1 class="ui-text-brand text-secondary-900 text-lg">
11
12
  {{ title }}
12
13
  </h1>
13
14
  <div v-if="isActive" class="mt-5">
@@ -25,7 +26,7 @@
25
26
  </div>
26
27
  </Fade>
27
28
  <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
+ md:flex-nowrap lg:mt-4 lg:flex-wrap">
29
30
  <QuantitySelector
30
31
  v-model="quantity"
31
32
  :min="product.min_quantity"
@@ -1,48 +1,63 @@
1
1
  <template>
2
2
  <section class="ui-section">
3
- <div v-if="title" class="mx-auto mb-2 max-w-prose text-center">
3
+ <div v-if="title" class="mx-auto mb-4 max-w-prose text-center">
4
4
  <h2 class="ui-text-brand text-3xl">
5
5
  <ALink :href="titleLink" :class="titleLink ? 'ui-link' : 'text-base-700'">
6
6
  {{ title }}
7
7
  </ALink>
8
8
  </h2>
9
9
  </div>
10
- <Carousel class="group/shelf">
10
+ <Component
11
+ :is="isGrid ? 'ul' : 'Carousel'"
12
+ class="group/shelf"
13
+ :class="isGrid && 'grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4'"
14
+ >
11
15
  <li
12
16
  v-for="product in products"
13
17
  :key="product._id"
14
18
  class="shrink-0 basis-1/2 md:basis-1/3 lg:basis-1/4"
15
19
  >
16
- <ProductCard :product="product" />
20
+ <ProductCard :product="product" :list-name="listName" />
17
21
  </li>
18
- <template #controls>
19
- <div
20
- v-show="products.length > 2"
21
- class="text-primary text-3xl leading-none transition-opacity
22
- group-hover/shelf:opacity-90 lg:text-2xl lg:opacity-0"
23
- >
24
- <CarouselControl class="!lg:top-1/3 lg:hover:bg-primary-300/60 !-left-4 !top-1/2 h-12
25
- w-12 rounded bg-transparent ring-black/5
26
- lg:bg-white/80 lg:shadow-sm lg:ring-1" is-prev />
27
- <CarouselControl class="!lg:top-1/3 lg:hover:bg-primary-300/60 !-right-4 !top-1/2 h-12
28
- w-12 rounded bg-transparent ring-black/5
29
- lg:bg-white/80 lg:shadow-sm lg:ring-1" />
30
- </div>
22
+ <template v-if="!isGrid" #controls>
23
+ <DefineCarouselControl v-slot="{ isPrev }">
24
+ <CarouselControl
25
+ v-show="products.length > 2"
26
+ class=":uno: text-primary lg:hover:bg-primary-200 !top-1/2 h-12
27
+ w-12 rounded-full bg-transparent text-3xl leading-none
28
+ ring-black/5 backdrop-blur-md
29
+ transition-opacity group-hover/shelf:opacity-90
30
+ lg:bg-white/80 lg:text-2xl lg:opacity-0 lg:shadow-sm lg:ring-1"
31
+ :class="isPrev ? '!-left-4' : '!-right-4'"
32
+ :is-prev="isPrev"
33
+ />
34
+ </DefineCarouselControl>
35
+ <ReuseCarouselControl :is-prev="true" />
36
+ <ReuseCarouselControl />
31
37
  </template>
32
- </Carousel>
38
+ </Component>
39
+ <slot name="append" />
33
40
  </section>
34
41
  </template>
35
42
 
36
43
  <script setup lang="ts">
44
+ import { createReusableTemplate } from '@vueuse/core';
37
45
  import {
38
46
  type Props as UseProductShelfProps,
39
47
  useProductShelf,
40
48
  } from '@@sf/composables/use-product-shelf';
49
+ import { useId } from '@@sf/sf-lib';
41
50
  import ProductCard from '~/components/ProductCard.vue';
42
51
 
43
- export interface Props extends UseProductShelfProps {}
52
+ export interface Props extends UseProductShelfProps {
53
+ isGrid?: boolean;
54
+ }
44
55
 
45
56
  const props = defineProps<Props>();
57
+ const [
58
+ DefineCarouselControl,
59
+ ReuseCarouselControl,
60
+ ] = createReusableTemplate<{ isPrev?: boolean }>();
46
61
  const {
47
62
  title,
48
63
  titleLink,
@@ -52,4 +67,5 @@ const {
52
67
  if (import.meta.env.SSR) {
53
68
  await fetching;
54
69
  }
70
+ const listName = title.value || `Shelf ${useId()}`;
55
71
  </script>
@@ -9,7 +9,7 @@
9
9
  border-dashed py-2 sm:grid-cols-3 [&:not(:last-child)]:border-b"
10
10
  >
11
11
  <span class="text-base-700">
12
- {{ getGridTitle(`${gridId}`, grids) }}
12
+ {{ getGridTitle(`${gridId}`, grids || []) }}
13
13
  </span>
14
14
  <strong class="font-semibold sm:col-span-2">
15
15
  {{ getSpecTextValue(product, `${gridId}`, grids) }}
@@ -0,0 +1,122 @@
1
+ <template>
2
+ <aside class="bg-base-50/40 h-screen overflow-y-auto">
3
+ <header class="px-6 pb-3 pt-5 text-center">
4
+ <button
5
+ v-if="filtersCount"
6
+ class="ui-btn-sm ui-btn-danger py-1"
7
+ @click="clearFilters"
8
+ >
9
+ {{ $t.i19clearFilters }}
10
+ <i class="i-trash ml-0.5"></i>
11
+ </button>
12
+ <p v-else class="text-base-500 border border-transparent
13
+ pb-1 text-base font-semibold">
14
+ {{ $t.i19filterResults }}
15
+ </p>
16
+ </header>
17
+ <article v-if="resultBuckets" class="px-6 pb-3">
18
+ <Collapse
19
+ v-if="priceRanges"
20
+ :title="$t.i19price"
21
+ class="[&>summary]:text-xl"
22
+ open
23
+ >
24
+ <div class="space-y-1.5">
25
+ <div
26
+ v-for="({ range, key }) in priceRanges"
27
+ :key="`prices-${key}`"
28
+ class="flex items-center"
29
+ >
30
+ <input
31
+ type="radio"
32
+ :id="`priceRange-${key}`"
33
+ name="priceRange"
34
+ :value="key"
35
+ v-model="priceRangeKey"
36
+ />
37
+ <label
38
+ :for="`priceRange-${key}`"
39
+ class="cursor-pointer px-3 text-sm"
40
+ >
41
+ {{ getPriceRangeLabel(range) }}
42
+ <small class="text-base-500 font-medium">
43
+ ({{ range.count }})
44
+ </small>
45
+ </label>
46
+ </div>
47
+ <div
48
+ v-if="activeFilters['price<'] || activeFilters['price>']"
49
+ class="flex items-center"
50
+ >
51
+ <input
52
+ type="radio"
53
+ id="priceRange-null"
54
+ name="priceRange"
55
+ :value="null"
56
+ v-model="priceRangeKey"
57
+ />
58
+ <label for="priceRange-null" class="cursor-pointer px-3 text-sm">
59
+ {{ $t.i19anyPrice }}
60
+ </label>
61
+ </div>
62
+ </div>
63
+ </Collapse>
64
+ <Collapse
65
+ v-for="({ title, options, field }) in filterOptions"
66
+ :key="field"
67
+ :title="title"
68
+ class="[&>summary]:text-xl"
69
+ :open="!!activeFilters[field]"
70
+ >
71
+ <div class="space-y-1.5">
72
+ <div
73
+ v-for="(count, value, i) in options"
74
+ :key="`${field}-${i}`"
75
+ class="flex items-center"
76
+ >
77
+ <input
78
+ type="checkbox"
79
+ :id="`${field}-${value}`"
80
+ :value="value"
81
+ :checked="checkFilterOption(field, value)"
82
+ @change="toggleFilterOption(field, value)"
83
+ />
84
+ <label
85
+ :for="`${field}-${value}`"
86
+ class="cursor-pointer px-3 text-sm"
87
+ >
88
+ {{ value }}
89
+ <small class="text-base-500 font-medium">
90
+ ({{ count }})
91
+ </small>
92
+ </label>
93
+ </div>
94
+ </div>
95
+ </Collapse>
96
+ </article>
97
+ </aside>
98
+ </template>
99
+
100
+ <script setup lang="ts">
101
+ import {
102
+ type Props as UseSearchFiltersProps,
103
+ useSearchFilters,
104
+ } from '@@sf/composables/use-search-filters';
105
+ import Collapse from '~/components/Collapse.vue';
106
+
107
+ export interface Props extends UseSearchFiltersProps {}
108
+
109
+ const props = defineProps<Props>();
110
+ const {
111
+ resultBuckets,
112
+ activeFilters,
113
+ filtersCount,
114
+ clearFilters,
115
+ priceRanges,
116
+ priceRangeKey,
117
+ getPriceRangeLabel,
118
+ filterOptions,
119
+ checkFilterOption,
120
+ toggleFilterOption,
121
+ } = useSearchFilters(props);
122
+ </script>
@@ -1,6 +1,102 @@
1
1
  <template>
2
- <div class="bg-base-300 h-40 w-full"></div>
2
+ <article class="relative">
3
+ <Fade slide="down">
4
+ <section
5
+ v-if="linkHits.length"
6
+ class="bg-white p-4"
7
+ :class="!isFetching && products.length ? 'lg:rounded-t' : 'lg:rounded'"
8
+ >
9
+ <nav>
10
+ <Carousel :wrapper-key="term" class="px-6 [&>ul]:gap-3">
11
+ <li
12
+ v-for="({ title, href }, i) in linkHits"
13
+ :key="i"
14
+ class="shrink-0"
15
+ >
16
+ <ALink :href="href" class="ui-link">
17
+ {{ title }}
18
+ </ALink>
19
+ </li>
20
+ </Carousel>
21
+ </nav>
22
+ </section>
23
+ </Fade>
24
+ <Skeleton v-if="isFetching" class="absolute top-0 w-full px-5 pt-20" />
25
+ <Fade :is-leave-to="false">
26
+ <ProductShelf
27
+ v-if="!isFetching && products.length"
28
+ :products="products"
29
+ class="m-0 max-w-none bg-white px-4
30
+ lg:[&_[data-carousel-control=next]]:!-right-10
31
+ lg:[&_[data-carousel-control=previous]]:!-left-10"
32
+ :class="linkHits.length ? 'lg:rounded-b' : 'lg:rounded'"
33
+ />
34
+ </Fade>
35
+ <Fade :is-leave-to="false">
36
+ <section
37
+ v-if="!isFetching
38
+ && (searchHistory.length || productCount > products.length)"
39
+ class="bg-base-100/80 grid grid-cols-1 items-center
40
+ gap-4 p-4 backdrop-blur-md
41
+ md:grid-cols-2 md:rounded-b lg:mt-5 lg:grid-cols-3 lg:rounded"
42
+ >
43
+ <div class="flex items-center justify-end gap-4 md:order-2">
44
+ <p
45
+ v-if="productCount > 1"
46
+ class="text-base-900 text-right text-sm lowercase"
47
+ >
48
+ <strong>{{ productCount }}</strong> {{ $t.i19itemsFound }}
49
+ </p>
50
+ <a
51
+ v-if="productCount > products.length"
52
+ :href="getSearchUrl(term)"
53
+ class="ui-btn ui-btn-primary w-auto whitespace-nowrap text-center"
54
+ >
55
+ {{ $t.i19seeAll }}
56
+ </a>
57
+ </div>
58
+ <div class="lg:col-span-2">
59
+ <nav>
60
+ <Carousel :wrapper-key="term" class="px-6 [&>ul]:gap-2.5">
61
+ <li
62
+ v-for="(term, i) in searchHistory"
63
+ :key="i"
64
+ class="shrink-0"
65
+ >
66
+ <a
67
+ :href="getSearchUrl(term)"
68
+ class="ui-link text-base-700 text-sm font-normal"
69
+ >
70
+ {{ term }}
71
+ </a>
72
+ </li>
73
+ <template #previous>
74
+ <i class="i-clock m-0" :aria-label="$t.i19searchAgain"></i>
75
+ </template>
76
+ </Carousel>
77
+ </nav>
78
+ </div>
79
+ </section>
80
+ </Fade>
81
+ </article>
3
82
  </template>
4
83
 
5
84
  <script setup lang="ts">
85
+ import {
86
+ type Props as UseSearchModalProps,
87
+ useSearchModal,
88
+ } from '@@sf/composables/use-search-modal';
89
+ import { getSearchUrl } from '@@sf/sf-lib';
90
+ import ProductShelf from '~/components/ProductShelf.vue';
91
+
92
+ export interface Props extends UseSearchModalProps {}
93
+
94
+ const props = defineProps<Props>();
95
+ const {
96
+ searchHistory,
97
+ isFetching,
98
+ products,
99
+ productCount,
100
+ linkHits,
101
+ } = useSearchModal(props);
6
102
  </script>