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,169 @@
1
+ <template>
2
+ <section
3
+ v-if="searchTerm"
4
+ class="ui-section-slim text-base-700 px-6 text-center text-xl lowercase"
5
+ :class="hasFiltersBar ? 'relative z-[14] -mb-4' : 'mb-0'"
6
+ >
7
+ {{ $t.i19searchingFor }}
8
+ <h1 class="text-base-800 inline font-bold italic">
9
+ {{ searchTerm }}
10
+ </h1>
11
+ </section>
12
+ <div
13
+ v-if="isFetching && !products.length"
14
+ class="ui-section relative min-h-[300px]"
15
+ >
16
+ <Skeleton class="absolute top-0 w-full px-5" is-bold is-large />
17
+ </div>
18
+ <article ref="showcase" class="relative">
19
+ <Fade>
20
+ <section
21
+ v-if="hasFiltersBar"
22
+ class="ui-section-slim
23
+ sticky-header:translate-y-16 lg:sticky-header:translate-y-14
24
+ to-base-100 sticky top-0 z-[12] flex items-center justify-between
25
+ rounded-b bg-white/80 px-6 py-4 shadow-sm backdrop-blur-sm
26
+ transition-transform"
27
+ >
28
+ <strong class="text-base-700 font-medium lowercase">
29
+ <template v-if="resultMeta.count > 1">
30
+ {{ resultMeta.count }}
31
+ <span class="hidden lg:inline">{{ $t.i19itemsFound }}</span>
32
+ <span class="lg:hidden">{{ $t.i19products }}</span>
33
+ </template>
34
+ </strong>
35
+ <div class="flex items-center gap-4">
36
+ <span class="hidden text-xl leading-none md:block">
37
+ <i class="i-adjustments-horizontal text-base-600 m-0"></i>
38
+ </span>
39
+ <button
40
+ @click="isFiltersOpen = !isFiltersOpen"
41
+ class="ui-btn-sm ui-btn-secondary relative"
42
+ >
43
+ <span class="hidden md:inline">
44
+ {{ $t.i19filterProducts }}
45
+ </span>
46
+ <span class="md:hidden">
47
+ <i class="i-adjustments-horizontal mr-1"></i>
48
+ {{ $t.i19filterOut }}
49
+ </span>
50
+ <span
51
+ v-if="filtersCount"
52
+ class="ui-badge-pill-sm outline-secondary/80 outline-3
53
+ absolute -right-1.5 -top-1 outline"
54
+ >
55
+ {{ filtersCount }}
56
+ </span>
57
+ </button>
58
+ <Listbox
59
+ v-model="sortOption"
60
+ as="div"
61
+ class="text-base-800 relative text-sm"
62
+ >
63
+ <ListboxButton class="ui-btn-sm ui-btn-secondary">
64
+ {{ $t.i19sort }}
65
+ </ListboxButton>
66
+ <Fade>
67
+ <ListboxOptions class="divide-base-100
68
+ absolute right-0 mt-2 divide-y rounded bg-white
69
+ shadow ring-1 ring-black/5 focus:outline-none">
70
+ <ListboxOption
71
+ v-for="({ label, value }) in sortOptions"
72
+ :key="value || 'sort'"
73
+ :value="value"
74
+ as="template"
75
+ v-slot="{ selected, active }"
76
+ >
77
+ <li>
78
+ <component
79
+ :is="selected ? 'div' : 'button'"
80
+ class="flex w-full py-2 pl-3 pr-6"
81
+ :class="!selected && active
82
+ && 'bg-secondary-100 text-secondary'"
83
+ >
84
+ <div class="w-5">
85
+ <i v-show="selected" class="i-check"></i>
86
+ </div>
87
+ {{ label }}
88
+ </component>
89
+ </li>
90
+ </ListboxOption>
91
+ </ListboxOptions>
92
+ </Fade>
93
+ </Listbox>
94
+ </div>
95
+ </section>
96
+ </Fade>
97
+ <ProductShelf :products="products" is-grid>
98
+ <template #append>
99
+ <Fade slide="down">
100
+ <nav v-if="!isFetching && totalPages > 1" class="mt-3 lg:mt-4">
101
+ <Pagination
102
+ v-model:page="searchEngine.pageNumber.value"
103
+ :total-pages="totalPages"
104
+ />
105
+ </nav>
106
+ </Fade>
107
+ </template>
108
+ </ProductShelf>
109
+ <Fade>
110
+ <div
111
+ v-if="isFetching"
112
+ class="absolute left-0 top-0 z-[14] h-full w-full bg-white/40"
113
+ ></div>
114
+ </Fade>
115
+ </article>
116
+ <Drawer
117
+ v-model="isFiltersOpen"
118
+ placement="end"
119
+ :backdrop-target="null"
120
+ :can-lock-scroll="false"
121
+ class="!z-[80] bg-white shadow [&_[data-drawer-close]]:bg-white/80"
122
+ >
123
+ <SearchFilters :search-engine="searchEngine" />
124
+ </Drawer>
125
+ </template>
126
+
127
+ <script setup lang="ts">
128
+ import {
129
+ useSearchShowcase,
130
+ type Props as UseSearchShowcaseProps,
131
+ } from '@@sf/composables/use-search-showcase';
132
+ import {
133
+ Listbox,
134
+ ListboxButton,
135
+ ListboxOptions,
136
+ ListboxOption,
137
+ } from '@headlessui/vue';
138
+ import Drawer from '@@sf/components/Drawer.vue';
139
+ import Pagination from '~/components/Pagination.vue';
140
+ import ProductShelf from '~/components/ProductShelf.vue';
141
+ import SearchFilters from '~/components/SearchFilters.vue';
142
+
143
+ export interface Props extends UseSearchShowcaseProps {}
144
+
145
+ const props = withDefaults(defineProps<Props>(), {
146
+ canUseUrlParams: true,
147
+ });
148
+ const showcase = ref<HTMLElement | null>(null);
149
+ const {
150
+ searchEngine,
151
+ fetching,
152
+ isFetching,
153
+ products,
154
+ resultMeta,
155
+ totalPages,
156
+ filtersCount,
157
+ sortOptions,
158
+ sortOption,
159
+ } = useSearchShowcase({ ...props, showcase });
160
+ if (import.meta.env.SSR) {
161
+ await fetching;
162
+ }
163
+ const wasFetched = computed(() => searchEngine.wasFetched.value);
164
+ const searchTerm = computed(() => searchEngine.term.value);
165
+ const hasFiltersBar = computed(() => {
166
+ return wasFetched && !!(resultMeta.value.count > 4 || filtersCount.value);
167
+ });
168
+ const isFiltersOpen = ref(false);
169
+ </script>
@@ -1,8 +1,8 @@
1
1
  <template>
2
- <footer class="bg-base-50 border-base-100 w-screen border-t py-2">
2
+ <footer class="bg-base-50 border-base-100 mt-auto w-screen border-t py-2">
3
3
  <div class="ui-section">
4
- <div class="border-base-200 flex flex-wrap
5
- justify-between gap-y-5 border-b pb-7 sm:gap-x-10 lg:flex-nowrap">
4
+ <div class="border-base-200 flex flex-wrap justify-between
5
+ gap-y-5 border-b pb-7 sm:gap-x-10 lg:flex-nowrap">
6
6
  <div class="shrink">
7
7
  <slot name="logo" />
8
8
  <div v-if="$settings.description" class="prose">
@@ -30,7 +30,7 @@
30
30
  </ALink>
31
31
  </div>
32
32
  </div>
33
- <div v-if="categories?.length" class="basis-1/2 sm:basis-auto">
33
+ <div v-if="categories?.length" class="basis-1/2 pr-2 sm:basis-auto sm:pr-0">
34
34
  <div class="mb-2.5 text-lg font-medium">
35
35
  {{ categoriesColTitle || $t.i19categories }}
36
36
  </div>
@@ -51,7 +51,7 @@
51
51
  </li>
52
52
  </ul>
53
53
  </div>
54
- <div v-if="pageLinks?.length" class="basis-1/2 sm:basis-auto">
54
+ <div v-if="pageLinks?.length" class="basis-1/2 pl-2 sm:basis-auto sm:pl-0">
55
55
  <div class="mb-2.5 text-lg font-medium">
56
56
  {{ pagesColTitle || $t.i19institutional }}
57
57
  </div>
@@ -61,7 +61,11 @@
61
61
  ? 'grid md:grid-cols-2 gap-x-5 gap-y-1.5' : 'space-y-1.5'"
62
62
  >
63
63
  <li v-for="({ title, href }, i) in pageLinks" :key="`p-${i}`">
64
- <ALink :href="href" class="ui-link text-base-600">
64
+ <ALink
65
+ :href="href"
66
+ class="ui-link"
67
+ :class="title === 'Blog' ? 'text-base-900' : 'text-base-600'"
68
+ >
65
69
  {{ title }}
66
70
  </ALink>
67
71
  </li>
@@ -11,7 +11,7 @@
11
11
  sm:pr-2.5 lg:w-[calc(100vw-1rem)] lg:px-1 xl:max-w-[82rem] 2xl:max-w-[85rem]">
12
12
  <div class="flex items-center justify-between">
13
13
  <div
14
- class="basis-1/4 lg:me-5 lg:basis-auto"
14
+ class="basis-1/4 lg:mr-5 lg:basis-auto"
15
15
  :class="!isSticky ? 'lg:hidden' : null"
16
16
  >
17
17
  <button
@@ -31,48 +31,50 @@
31
31
  <div class="lg:grow" :class="isSticky ? '[&_img]:max-w-[170px]' : null">
32
32
  <slot name="logo" />
33
33
  </div>
34
- <div class="flex basis-1/4 items-center justify-end gap-3 px-2 lg:gap-4">
35
- <form
36
- action="/s"
37
- method="get"
38
- class="relative mb-1 hidden w-[400px] lg:block"
39
- @submit.prevent="searchTerm
40
- ? (isSearchOpen = !isSearchOpen)
41
- : searchInput?.focus()"
42
- >
34
+ <div class="text-base-800 flex basis-1/4
35
+ items-center justify-end gap-3 px-2 lg:gap-4">
36
+ <form action="/s" method="get" class="relative h-12">
43
37
  <label for="HeaderSearch" class="sr-only">
44
38
  {{ $t.i19searchProducts }}
45
39
  </label>
46
- <input
47
- ref="searchInput"
48
- type="search"
49
- id="HeaderSearch"
50
- name="term"
51
- v-model="searchTerm"
52
- :placeholder="$t.i19searchProducts"
53
- class="from-secondary-50/10 to-secondary-50/80 border-secondary-200 w-full rounded-md
54
- bg-gradient-to-br py-2.5 pe-10
55
- shadow-sm sm:text-sm"
56
- />
57
- <span class="absolute inset-y-0 end-0 grid place-content-center px-1">
40
+ <div
41
+ class="absolute -top-0.5 right-0 items-stretch
42
+ opacity-0 transition-opacity lg:z-auto lg:flex lg:opacity-100"
43
+ :class="isSearchOpen ? 'flex opacity-100' : '-z-10'"
44
+ >
45
+ <div class="hidden w-12 bg-gradient-to-r from-transparent
46
+ to-white sm:block"></div>
47
+ <input
48
+ ref="searchInput"
49
+ type="search"
50
+ id="HeaderSearch"
51
+ name="q"
52
+ v-model.trim="searchTerm"
53
+ :placeholder="$t.i19searchProducts"
54
+ class="from-secondary-50/10 to-secondary-50/80
55
+ border-secondary-200 placeholder:text-secondary-700/60
56
+ text-secondary-700 h-12 w-72 rounded-md bg-gradient-to-br
57
+ py-2.5 pl-4 pr-10 shadow-sm lg:w-[400px]"
58
+ @focusin="isSearchOpen = true"
59
+ />
60
+ </div>
61
+ <span class="absolute -top-0.5 right-0 grid">
58
62
  <button
59
63
  type="submit"
60
- class="text-secondary-600 hover:text-primary p-1 text-2xl"
64
+ :aria-label="$t.i19searchProducts"
65
+ class="hover:text-primary flex h-12
66
+ flex-col justify-center lg:px-2.5"
67
+ :class="isSearchOpen
68
+ ? 'text-primary px-2.5'
69
+ : 'text-secondary-600'"
70
+ @click="toggleSearch"
61
71
  >
62
72
  <span class="sr-only">{{ $t.i19search }}</span>
63
- <i class="i-search-3-line"></i>
73
+ <i class="i-search-3-line mb-0 h-7 w-7"></i>
64
74
  </button>
65
75
  </span>
66
76
  </form>
67
- <button
68
- :aria-label="$t.i19searchProducts"
69
- @click="isSearchOpen = !isSearchOpen"
70
- class="lg:hidden"
71
- >
72
- <i class="i-search-3-line text-base-700 hover:text-primary
73
- h-7 w-7 hover:scale-110 active:scale-125"></i>
74
- </button>
75
- <AccountMenu class="hidden sm:block">
77
+ <AccountMenu v-if="!isMobile" class="hidden sm:block">
76
78
  <template #button="{ open }">
77
79
  <i
78
80
  class="i-user-2-line text-base-600 hover:text-primary
@@ -91,15 +93,16 @@
91
93
  <i class="i-shopping-bag-3-fill group-hover:text-primary h-7 w-7
92
94
  group-hover:scale-110 group-active:scale-125"></i>
93
95
  <span
94
- v-if="delayedTotalItems"
96
+ v-if="cartTotalItems"
95
97
  class="ui-badge-pill-sm absolute -right-1.5 -top-1"
96
98
  >
97
- {{ delayedTotalItems }}
99
+ {{ cartTotalItems }}
98
100
  </span>
99
101
  </a>
100
102
  </div>
101
103
  </div>
102
104
  <ShopHeaderMenu
105
+ v-if="!isMobile"
103
106
  v-show="!isSticky"
104
107
  v-bind="{ inlineMenuTrees }"
105
108
  class="mt-3 hidden px-3 lg:block 2xl:px-8"
@@ -119,14 +122,21 @@
119
122
  </Drawer>
120
123
  <Drawer
121
124
  v-model="isSearchOpen"
125
+ :is-hidden="!quickSearchTerm"
122
126
  :has-close-button="false"
127
+ :anchor-el="searchInput?.parentElement"
128
+ position="absolute"
123
129
  placement="top"
130
+ animation="scale"
131
+ max-width="55rem"
132
+ class="bg-transparent lg:mt-10"
133
+ :class="isSticky ? 'mt-2 md:mt-3' : 'mt-3 sm:mt-4 md:mt-5'"
124
134
  >
125
135
  <Suspense>
126
- <SearchModal v-if="isSearchOpenOnce" />
136
+ <SearchModal v-if="isSearchOpenOnce" :term="quickSearchTerm" />
127
137
  <template #fallback>
128
138
  <div class="container mx-auto">
129
- <Skeleton class="p-6" is-large />
139
+ <Skeleton class="p-6" is-large is-bold />
130
140
  </div>
131
141
  </template>
132
142
  </Suspense>
@@ -136,11 +146,14 @@
136
146
  v-model="isCartOpen"
137
147
  placement="end"
138
148
  backdrop-target="#teleported-overlap"
149
+ class="shadow"
139
150
  >
140
151
  <Suspense>
141
152
  <CartSidebar v-if="isCartOpenOnce" @close="isCartOpen = false" />
142
153
  <template #fallback>
143
- <Skeleton class="px-6 pt-16" is-bold />
154
+ <div class="h-full bg-white">
155
+ <Skeleton class="px-6 pt-16" is-bold />
156
+ </div>
144
157
  </template>
145
158
  </Suspense>
146
159
  </Drawer>
@@ -149,12 +162,11 @@
149
162
  </template>
150
163
 
151
164
  <script setup lang="ts">
152
- import { watchOnce } from '@vueuse/core';
153
- import { totalItems } from '@@sf/state/shopping-cart';
154
165
  import {
155
166
  type Props as UseShopHeaderProps,
156
167
  useShopHeader,
157
168
  } from '@@sf/composables/use-shop-header';
169
+ import { isMobile } from '@@sf/sf-lib';
158
170
  import Drawer from '@@sf/components/Drawer.vue';
159
171
  import ShopSidenav from '~/components/ShopSidenav.vue';
160
172
  import ShopHeaderMenu from '~/components/ShopHeaderMenu.vue';
@@ -166,38 +178,26 @@ const SearchModal = defineAsyncComponent(() => import('~/components/SearchModal.
166
178
  const CartSidebar = defineAsyncComponent(() => import('~/components/CartSidebar.vue'));
167
179
  const props = defineProps<Props>();
168
180
  const header = ref<HTMLElement | null>(null);
181
+ const searchInput = ref<HTMLInputElement | null>(null);
169
182
  const {
170
183
  isSticky,
171
184
  positionY,
172
185
  categoryTrees,
173
186
  inlineMenuTrees,
174
- } = useShopHeader({ ...props, header });
187
+ isSearchOpen,
188
+ isSearchOpenOnce,
189
+ searchTerm,
190
+ quickSearchTerm,
191
+ toggleSearch,
192
+ isCartOpen,
193
+ isCartOpenOnce,
194
+ cartTotalItems,
195
+ handleOnMounted,
196
+ } = useShopHeader({ ...props, header, searchInput });
175
197
  const isSidenavOpen = ref(false);
176
- const isSearchOpen = ref(false);
177
- const isSearchOpenOnce = ref(false);
178
- watchOnce(isSearchOpen, () => {
179
- isSearchOpenOnce.value = true;
180
- });
181
- const isCartOpen = ref(false);
182
- const isCartOpenOnce = ref(false);
183
- watchOnce(isCartOpen, () => {
184
- isCartOpenOnce.value = true;
185
- });
186
198
  const isMounted = ref(false);
187
- const delayedTotalItems = ref(0);
188
199
  onMounted(() => {
189
200
  isMounted.value = true;
190
- watch(totalItems, (newTotalItems, prevTotalItems) => {
191
- if (typeof prevTotalItems === 'number') {
192
- if (prevTotalItems < newTotalItems) {
193
- isCartOpen.value = true;
194
- } else if (prevTotalItems && !newTotalItems) {
195
- isCartOpen.value = false;
196
- }
197
- }
198
- delayedTotalItems.value = newTotalItems;
199
- }, { immediate: true });
201
+ handleOnMounted();
200
202
  });
201
- const searchTerm = ref('');
202
- const searchInput = ref<HTMLElement | null>(null);
203
203
  </script>
@@ -9,7 +9,7 @@
9
9
  <Fade>
10
10
  <PopoverPanel
11
11
  v-slot="{ close }"
12
- class="Popover text-base-700 absolute top-3 z-20
12
+ class="Popover text-base-700 absolute top-3 z-50
13
13
  !transform rounded bg-white/80 px-7 py-5 text-base
14
14
  shadow backdrop-blur-md"
15
15
  :class="countMenuCols === 1 ? 'w-auto'
@@ -1,5 +1,5 @@
1
1
  <template>
2
- <aside class="flex h-full flex-col">
2
+ <aside class="flex h-full flex-col bg-white">
3
3
  <nav class="grow py-4">
4
4
  <ul class="relative h-full">
5
5
  <ShopSidenavCategory
@@ -1,6 +1,6 @@
1
1
  <template>
2
2
  <div>
3
- <div v-for="(options, gridId) in variationsGrids" :key="gridId">
3
+ <div v-for="(options, gridId) in variationsGrids" :key="gridId" class="mt-3">
4
4
  <span class="text-base-700 text-sm font-medium">
5
5
  {{ getGridTitle(gridId) }}:
6
6
  <strong v-if="selectedOptions[gridId]" class="text-base-800">
@@ -10,17 +10,17 @@
10
10
  <ul v-if="options.length < 7" class="mt-2 flex gap-2">
11
11
  <li v-for="(optionText, i) in options" :key="`${gridId}-${i}`">
12
12
  <button
13
- class="ring-secondary/60 rounded border"
13
+ class="ring-primary/60 rounded border"
14
14
  :class="[
15
15
  selectedOptions[gridId] === optionText
16
- ? 'border-secondary ring-2'
16
+ ? 'border-primary ring-2'
17
17
  : null,
18
18
  gridId === 'colors'
19
- ? 'h-9 w-9 text-[0]'
20
- : 'px-2 py-1',
19
+ ? 'h-9 w-9 text-[0px]'
20
+ : 'px-2.5 py-2 leading-none',
21
21
  activeVariationsGrids[gridId].includes(optionText)
22
- ? 'text-base-800 bg-base-100 border-base-400'
23
- : 'text-base-500 bg-base-200 border-base-300'
22
+ ? 'text-primary-950 bg-primary-100 border-primary-400'
23
+ : 'text-primary-600 bg-primary-200 border-primary-300'
24
24
  ]"
25
25
  :style="gridId === 'colors' ? getColorOptionBg(optionText) : undefined"
26
26
  @click="selectOption({ optionText, gridId, gridIndex: i })"
@@ -12,3 +12,7 @@ declare module '*.vue' {
12
12
  const component: DefineComponent<{}, {}, any>;
13
13
  export default component;
14
14
  }
15
+
16
+ interface ImportMetaEnv {
17
+ readonly BUILD_MINIMAL: boolean | undefined;
18
+ }
@@ -3,7 +3,7 @@ import SfBase from '@@sf/layouts/Base.astro';
3
3
  import InlineScripts from '~/scripts/InlineScripts.astro';
4
4
  import '~/assets/style.css';
5
5
  import 'uno.css';
6
- import '@fontsource/croissant-one/index.css';
6
+ import '@fontsource/league-spartan/700.css';
7
7
  ---
8
8
 
9
9
  <SfBase>
@@ -38,11 +38,14 @@ if (pagesList?.isActive) {
38
38
  pageLinks = [];
39
39
  const pageSlugs = await getContent('extra-pages/');
40
40
  for (let i = 0; i < pageSlugs.length; i++) {
41
- const { title } = await getContent(`extra-pages/${pageSlugs[i]}`);
41
+ const { title } = await getContent(`extra-pages/${pageSlugs[i]}`) || {};
42
42
  if (title) {
43
43
  pageLinks.push({ title, href: `/p/${pageSlugs[i]}` });
44
44
  }
45
45
  }
46
+ if ((await getContent('blog/')).length) {
47
+ pageLinks.push({ title: 'Blog', href: '/blog/1/' });
48
+ }
46
49
  }
47
50
  }
48
51
  ---
@@ -19,8 +19,8 @@ const { getInlineClientJS } = await useSharedData({ field: 'categories' });
19
19
  {pitchBar.slides.length > 1 && <PitchBar {...pitchBar} client:idle />}
20
20
  {pitchBar.slides.length === 1 && <PitchBar {...pitchBar} />}
21
21
  </slot>
22
- <script is:inline set:html={getInlineClientJS()} />
23
- <ShopHeader {...shopHeader} client:context>
22
+ <script async is:inline set:html={getInlineClientJS()} />
23
+ <ShopHeader {...shopHeader} client:context="data:categories">
24
24
  <Fragment slot="logo">
25
25
  <a href="/" class="inline-block">
26
26
  <LogoHeading class="inline-block">
@@ -1,10 +1,33 @@
1
1
  ---
2
+ import {
3
+ i19home,
4
+ i19error,
5
+ i19errorMsg,
6
+ i19notFound,
7
+ } from '@@i18n';
2
8
  ---
3
9
 
4
10
  <main>
5
- <h1>Error :/</h1>
6
- <p class="instructions">
7
- Check out the <code>src/pages</code> directory to get started.<br/>
8
- <strong>Code Challenge:</strong> Tweak the "Welcome to Astro" message above.
9
- </p>
11
+ <article class="prose mx-auto px-8 pt-20 pb-32">
12
+ <h1>{i19error} :/</h1>
13
+ <p class="hidden text-xl" id="errorMsg">
14
+ {i19errorMsg}
15
+ </p>
16
+ <p class="hidden text-xl" id="404Msg">
17
+ {i19notFound} (404)
18
+ </p>
19
+ <p class="text-xl" id="loadingMsg">
20
+ ... ... ...
21
+ </p>
22
+ <a href="/" class="text-2xl">
23
+ <i class="i-arrow-right rotate-180 mr-1"></i>
24
+ {i19home}
25
+ </a>
26
+ </article>
27
+ <script>
28
+ const msgId = window.location.search.includes('status=404')
29
+ ? '404Msg' : '5xxMsg'
30
+ document.getElementById(msgId)!.style.display = 'block';
31
+ document.getElementById('loadingMsg')!.style.display = 'none';
32
+ </script>
10
33
  </main>
@@ -0,0 +1,26 @@
1
+ ---
2
+ import api from '@cloudcommerce/api';
3
+ import SharedData from '@@sf/components/SharedData.astro';
4
+ import Hero from '~/main/content/Hero.astro';
5
+ import Sections from '~/main/content/Sections.astro';
6
+
7
+ const { routeContext } = Astro.locals;
8
+ const { isSearchPage, fetchingApiContext } = routeContext;
9
+ const sharedData: Record<string, any> = {};
10
+ if (isSearchPage || fetchingApiContext) {
11
+ const [
12
+ { data: { result: grids } },
13
+ ] = await Promise.all([
14
+ api.get('grids'),
15
+ fetchingApiContext,
16
+ ]);
17
+ sharedData.grids = grids;
18
+ }
19
+ ---
20
+
21
+ <main>
22
+ {Object.entries(sharedData).map(([field, value]) =>
23
+ <SharedData field={field} value={value} />)}
24
+ <Hero />
25
+ <Sections />
26
+ </main>
@@ -0,0 +1,33 @@
1
+ ---
2
+ import { usePageHero } from '@@sf/layouts/use-page-main';
3
+ import BannerPictures from '@@sf/components/BannerPictures.astro';
4
+ import HeroSlider from '~/components/HeroSlider.vue';
5
+
6
+ const { routeContext } = Astro.locals;
7
+ const { heroSlider } = await usePageHero({ routeContext });
8
+ const heroSlides = heroSlider.slides;
9
+ ---
10
+
11
+ {heroSlider.slides.length > 0 &&
12
+ <HeroSlider {...heroSlider} client:idle>
13
+ {heroSlides[0] && <BannerPictures
14
+ slot="picture-0" {...heroSlides[0]} index={0}
15
+ widths={[640, 856, 1024, 1536]} class="max-w-screen-sm sm:max-w-screen-lg" />}
16
+ {heroSlides[1] && <BannerPictures
17
+ slot="picture-1" {...heroSlides[1]} index={1}
18
+ widths={[856, 1024, 1536]} class="max-w-screen-sm sm:max-w-screen-lg" />}
19
+ {heroSlides[2] && <BannerPictures
20
+ slot="picture-2" {...heroSlides[2]} index={2}
21
+ widths={[856, 1024, 1536]} class="max-w-screen-sm sm:max-w-screen-lg" />}
22
+ {heroSlides[3] && <BannerPictures
23
+ slot="picture-3" {...heroSlides[3]} index={3}
24
+ widths={[856, 1024, 1536]} class="max-w-screen-sm sm:max-w-screen-lg" />}
25
+ {heroSlides[4] && <BannerPictures
26
+ slot="picture-4" {...heroSlides[4]} index={4}
27
+ widths={[856, 1024, 1536]} class="max-w-screen-sm sm:max-w-screen-lg" />}
28
+ {heroSlides[5] && <BannerPictures
29
+ slot="picture-5" {...heroSlides[5]} index={5}
30
+ widths={[856, 1024, 1536]} class="max-w-screen-sm sm:max-w-screen-lg" />}
31
+ <!-- https://github.com/withastro/astro/issues/5066#issuecomment-1277998365 -->
32
+ </HeroSlider>
33
+ }