cloudcommerce 0.8.7 → 0.9.0
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.
- package/.github/renovate.json +1 -2
- package/CHANGELOG.md +44 -0
- package/ecomplus-stores/monocard/functions/core/package.json +1 -1
- package/ecomplus-stores/monocard/functions/events/package.json +2 -2
- package/ecomplus-stores/monocard/functions/modules/package.json +2 -2
- package/ecomplus-stores/monocard/functions/passport/package.json +2 -2
- package/ecomplus-stores/monocard/functions/ssr/content/layout.json +37 -0
- package/ecomplus-stores/monocard/functions/ssr/package.json +6 -6
- package/ecomplus-stores/monocard/functions/ssr/src/components/FeatureTabs.astro +1 -1
- package/ecomplus-stores/monocard/functions/ssr/src/components/FeatureTabs.vue +23 -10
- package/ecomplus-stores/monocard/functions/ssr/src/components/PitchBar.vue +2 -2
- package/ecomplus-stores/monocard/functions/ssr/src/components/ShopHeader.vue +11 -10
- package/ecomplus-stores/monocard/functions/ssr/src/components/ShopSidenavCategory.vue +2 -2
- package/ecomplus-stores/monocard/functions/ssr/src/layouts/Pages.astro +28 -14
- package/ecomplus-stores/monocard/functions/ssr/src/main/Home.astro +3 -3
- package/ecomplus-stores/monocard/functions/ssr/src/main/Wildcard.astro +9 -7
- package/ecomplus-stores/monocard/functions/ssr/src/pages/[...slug].astro +2 -2
- package/ecomplus-stores/monocard/functions/ssr/src/pages/fallback.astro +1 -1
- package/ecomplus-stores/monocard/functions/ssr/src/pages/index.astro +2 -2
- package/ecomplus-stores/monocard/package.json +1 -1
- package/package.json +10 -10
- package/packages/api/lib/api.d.ts +27 -11
- package/packages/api/package.json +1 -1
- package/packages/apps/correios/package.json +3 -3
- package/packages/apps/custom-payment/package.json +1 -1
- package/packages/apps/custom-shipping/package.json +1 -1
- package/packages/apps/datafrete/package.json +3 -3
- package/packages/apps/discounts/package.json +1 -1
- package/packages/apps/emails/lib/functios-lib/abandoned-carts.js +2 -1
- package/packages/apps/emails/lib/functios-lib/abandoned-carts.js.map +1 -1
- package/packages/apps/emails/package.json +2 -2
- package/packages/apps/emails/src/functios-lib/abandoned-carts.ts +2 -1
- package/packages/apps/fb-conversions/package.json +2 -2
- package/packages/apps/frenet/lib/functions-lib/tracking-codes.js +2 -1
- package/packages/apps/frenet/lib/functions-lib/tracking-codes.js.map +1 -1
- package/packages/apps/frenet/package.json +3 -3
- package/packages/apps/frenet/src/functions-lib/tracking-codes.ts +2 -1
- package/packages/apps/galaxpay/lib/functions-lib/ecom/events-to-galaxpay.js +1 -1
- package/packages/apps/galaxpay/lib/functions-lib/ecom/events-to-galaxpay.js.map +1 -1
- package/packages/apps/galaxpay/lib/functions-lib/galaxpay/webhook.js +101 -37
- package/packages/apps/galaxpay/lib/functions-lib/galaxpay/webhook.js.map +1 -1
- package/packages/apps/galaxpay/package.json +3 -3
- package/packages/apps/galaxpay/src/functions-lib/ecom/events-to-galaxpay.ts +1 -1
- package/packages/apps/galaxpay/src/functions-lib/galaxpay/webhook.ts +123 -39
- package/packages/apps/google-analytics/package.json +3 -3
- package/packages/apps/infinitepay/package.json +3 -3
- package/packages/apps/jadlog/package.json +2 -2
- package/packages/apps/loyalty-points/package.json +1 -1
- package/packages/apps/melhor-envio/lib/functions-lib/tracking-codes.js +1 -1
- package/packages/apps/melhor-envio/lib/functions-lib/tracking-codes.js.map +1 -1
- package/packages/apps/melhor-envio/package.json +3 -3
- package/packages/apps/melhor-envio/src/functions-lib/tracking-codes.ts +1 -1
- package/packages/apps/mercadopago/package.json +3 -3
- package/packages/apps/pagarme/package.json +3 -3
- package/packages/apps/paghiper/package.json +3 -3
- package/packages/apps/pix/package.json +3 -3
- package/packages/apps/tiny-erp/package.json +3 -3
- package/packages/apps/webhooks/package.json +3 -3
- package/packages/cli/package.json +1 -1
- package/packages/config/package.json +1 -1
- package/packages/emails/package.json +2 -2
- package/packages/events/package.json +2 -2
- package/packages/firebase/package.json +2 -2
- package/packages/i18n/package.json +1 -1
- package/packages/modules/package.json +3 -3
- package/packages/passport/package.json +2 -2
- package/packages/ssr/package.json +5 -5
- package/packages/storefront/client.d.ts +1 -1
- package/packages/storefront/config/storefront.cms.cjs +17 -12
- package/packages/storefront/config/storefront.cms.mjs +17 -12
- package/packages/storefront/dist/client/_astro/Carousel.dfb3f26a.js +1 -0
- package/packages/storefront/dist/client/_astro/HeroSlider.34436243.js +1 -0
- package/packages/storefront/dist/client/_astro/PitchBar.1c993792.js +1 -0
- package/packages/storefront/dist/client/_astro/Prices.70885f17.js +1 -0
- package/packages/storefront/dist/client/_astro/ShopHeader.e901fad5.js +4 -0
- package/packages/storefront/dist/client/_astro/_...slug_.9a31c59e.css +1 -0
- package/packages/storefront/dist/client/_astro/_plugin-vue_export-helper.0c2b7f88.js +1 -0
- package/packages/storefront/dist/client/_astro/client.4e825332.js +1 -0
- package/packages/storefront/dist/client/_astro/{firebase-app.d090c84e.js → firebase-app.247497b8.js} +22 -22
- package/packages/storefront/dist/client/_astro/{format-money.ab6b71eb.js → format-money.f09b89ed.js} +1 -1
- package/packages/storefront/dist/client/_astro/{hoisted.541b5c05.js → hoisted.56fa2eff.js} +1 -1
- package/packages/storefront/dist/client/_astro/index.1eaf97c3.css +1 -0
- package/packages/storefront/dist/client/_astro/index.9018a8da.js +1 -0
- package/packages/storefront/dist/client/_astro/{modules-info.06ac0727.js → modules-info.2a72e536.js} +1 -1
- package/packages/storefront/dist/client/_astro/runtime-dom.esm-bundler.1fd80976.js +1 -0
- package/packages/storefront/dist/client/_astro/session-utm.ac492493.js +1 -0
- package/packages/storefront/dist/client/img/uploads/ecom-icon.png +0 -0
- package/packages/storefront/dist/client/img/uploads/logo.png +0 -0
- package/packages/storefront/dist/client/manifest.webmanifest +1 -1
- package/packages/storefront/dist/client/sw.js +1 -1
- package/packages/storefront/dist/client/workbox-e0d788d4.js +1 -1
- package/packages/storefront/dist/server/chunks/{astro.9781c0a7.mjs → astro.39f7f387.mjs} +910 -295
- package/packages/storefront/dist/server/chunks/pages/{all.b355675e.mjs → all.b15cad2d.mjs} +408 -246
- package/packages/storefront/dist/server/entry.mjs +32 -19
- package/packages/storefront/dist/server/manifest.webmanifest +1 -1
- package/packages/storefront/package.json +15 -15
- package/packages/storefront/server.d.ts +1 -1
- package/packages/storefront/src/images/use-ssr-picture.ts +30 -10
- package/packages/storefront/src/lib/assets/base.css +4 -2
- package/packages/storefront/src/lib/cms.d.ts +14 -18
- package/packages/storefront/src/lib/components/Carousel.vue +16 -10
- package/packages/storefront/src/lib/components/CarouselControl.vue +2 -2
- package/packages/storefront/src/lib/components/Drawer.vue +5 -5
- package/packages/storefront/src/lib/components/HeroPicture.astro +54 -0
- package/packages/storefront/src/lib/components/LoginForm.vue +3 -2
- package/packages/storefront/src/lib/components/SocialNetworkLink.vue +2 -2
- package/packages/storefront/src/lib/components/_injection-keys.ts +5 -3
- package/packages/storefront/src/lib/components/globals/AImg.vue +4 -5
- package/packages/storefront/src/lib/components/globals/ALink.vue +2 -2
- package/packages/storefront/src/lib/components/globals/Fade.vue +5 -5
- package/packages/storefront/src/lib/composables/use-hero-slider.ts +38 -0
- package/packages/storefront/src/lib/composables/use-prices.ts +6 -3
- package/packages/storefront/src/lib/composables/use-shop-header.ts +30 -14
- package/packages/storefront/src/lib/composables/use-sticky-header.ts +15 -12
- package/packages/storefront/src/lib/layouts/Base.astro +1 -1
- package/packages/storefront/src/lib/layouts/BaseBody.astro +2 -2
- package/packages/storefront/src/lib/layouts/BaseHead.astro +2 -2
- package/packages/storefront/src/lib/layouts/sections/use-hero-section.ts +48 -0
- package/packages/storefront/src/lib/layouts/use-home-main.ts +16 -0
- package/packages/storefront/src/lib/layouts/use-page-layout.ts +41 -0
- package/packages/storefront/src/lib/pages/_vue.ts +1 -1
- package/packages/storefront/src/lib/scripts/modules-info-preset.ts +5 -2
- package/packages/storefront/src/lib/scripts/session-utm.ts +6 -1
- package/packages/storefront/src/lib/ssr-context.ts +24 -15
- package/packages/storefront/src/lib/state/customer-session.ts +14 -2
- package/packages/storefront/src/lib/state/shopping-cart/add-cart-item.ts +8 -10
- package/packages/storefront/src/lib/state/shopping-cart.ts +4 -3
- package/packages/storefront/src/vue-globals.d.ts +3 -2
- package/packages/storefront/tailwind.config.cjs +10 -3
- package/packages/storefront/tsconfig.json +3 -1
- package/packages/storefront/uno.config.cjs +8 -4
- package/packages/types/package.json +1 -1
- package/ecomplus-stores/monocard/functions/ssr/content/code.json +0 -5
- package/ecomplus-stores/monocard/functions/ssr/content/footer.json +0 -46
- package/ecomplus-stores/monocard/functions/ssr/content/header.json +0 -23
- package/ecomplus-stores/monocard/functions/ssr/content/menu.json +0 -6
- package/ecomplus-stores/monocard/functions/ssr/content/metatags.json +0 -5
- package/ecomplus-stores/monocard/functions/ssr/src/components/header/HeaderButtonLink.vue +0 -5
- package/ecomplus-stores/monocard/functions/ssr/src/components/header/HeaderButtons.vue +0 -39
- package/ecomplus-stores/monocard/functions/ssr/src/components/header/HeaderNav.vue +0 -61
- package/ecomplus-stores/monocard/functions/ssr/src/components/header/HeaderNavLink.vue +0 -5
- package/packages/storefront/dist/client/_astro/PitchBar.5d55c359.js +0 -1
- package/packages/storefront/dist/client/_astro/Prices.55399c72.js +0 -1
- package/packages/storefront/dist/client/_astro/ProductCard.7e891c08.js +0 -1
- package/packages/storefront/dist/client/_astro/ShopHeader.6bbe1a17.js +0 -4
- package/packages/storefront/dist/client/_astro/_...slug_.45f0edaf.css +0 -1
- package/packages/storefront/dist/client/_astro/_plugin-vue_export-helper.77ed7c18.js +0 -1
- package/packages/storefront/dist/client/_astro/client.0de2f274.js +0 -1
- package/packages/storefront/dist/client/_astro/index.ff4f0b30.js +0 -1
- package/packages/storefront/dist/client/_astro/runtime-dom.esm-bundler.d2f39f33.js +0 -1
- package/packages/storefront/dist/client/_astro/session-utm.72684b84.js +0 -1
- package/packages/storefront/dist/client/img/uploads/icon.png +0 -0
- package/packages/storefront/dist/client/img/uploads/large-icon.png +0 -0
- package/packages/storefront/dist/client/img/uploads/logo.webp +0 -0
- package/packages/storefront/src/lib/components/ProductCard.vue +0 -36
- package/packages/storefront/src/lib/layouts/PagesHeader.astro +0 -51
- /package/packages/storefront/dist/client/_astro/{index.0c833781.css → index.e56fc6b3.css} +0 -0
- /package/packages/storefront/dist/client/_astro/{server.1bc2fa51.css → server.1dabec03.css} +0 -0
|
@@ -95,10 +95,10 @@ const onEnter = (el: HTMLElement) => {
|
|
|
95
95
|
el.style.visibility = 'hidden';
|
|
96
96
|
// eslint-disable-next-line no-shadow
|
|
97
97
|
const { width, height } = getComputedStyle(el);
|
|
98
|
-
el.style.width = isSlideY.value ?
|
|
99
|
-
el.style.height = isSlideY.value ? '0' :
|
|
100
|
-
el.style.position =
|
|
101
|
-
el.style.visibility =
|
|
98
|
+
el.style.width = isSlideY.value ? '' : '0';
|
|
99
|
+
el.style.height = isSlideY.value ? '0' : '';
|
|
100
|
+
el.style.position = '';
|
|
101
|
+
el.style.visibility = '';
|
|
102
102
|
// Force repaint to make sure the animation is triggered correctly
|
|
103
103
|
// eslint-disable-next-line no-unused-expressions
|
|
104
104
|
getComputedStyle(el)[isSlideY.value ? 'height' : 'width'];
|
|
@@ -113,7 +113,7 @@ const onEnter = (el: HTMLElement) => {
|
|
|
113
113
|
};
|
|
114
114
|
const onAfterEnter = (el: HTMLElement) => {
|
|
115
115
|
if (props.slide && !props.isFloating && props.isEnterFrom) {
|
|
116
|
-
el.style[isSlideY.value ? 'height' : 'width'] =
|
|
116
|
+
el.style[isSlideY.value ? 'height' : 'width'] = '';
|
|
117
117
|
}
|
|
118
118
|
};
|
|
119
119
|
const onLeave = (el: HTMLElement) => {
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { computed } from 'vue';
|
|
2
|
+
import { parseShippingPhrase } from '@@sf/state/modules-info';
|
|
3
|
+
|
|
4
|
+
export interface Props {
|
|
5
|
+
autoplay?: number;
|
|
6
|
+
slides: Array<{
|
|
7
|
+
img?: string;
|
|
8
|
+
alt?: string;
|
|
9
|
+
mobileImg?: string;
|
|
10
|
+
href?: string;
|
|
11
|
+
title?: string;
|
|
12
|
+
subtitle?: string;
|
|
13
|
+
buttonLink?: string;
|
|
14
|
+
buttonText?: string;
|
|
15
|
+
}>;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const useHeroSlider = (props: Props) => {
|
|
19
|
+
const parsedSlides = computed(() => {
|
|
20
|
+
return props.slides.map((slide) => {
|
|
21
|
+
const title = slide.title
|
|
22
|
+
? parseShippingPhrase(slide.title).value : '';
|
|
23
|
+
const subtitle = slide.subtitle
|
|
24
|
+
? parseShippingPhrase(slide.subtitle).value : '';
|
|
25
|
+
const buttonText = slide.buttonText
|
|
26
|
+
? parseShippingPhrase(slide.buttonText).value : '';
|
|
27
|
+
return {
|
|
28
|
+
...slide,
|
|
29
|
+
hasHeader: Boolean(title || subtitle || buttonText),
|
|
30
|
+
};
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
return {
|
|
34
|
+
parsedSlides,
|
|
35
|
+
};
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
export default useHeroSlider;
|
|
@@ -10,10 +10,13 @@ export interface Props {
|
|
|
10
10
|
isAmountTotal?: boolean;
|
|
11
11
|
installmentsOption?: ListPaymentsResponse['installments_option'];
|
|
12
12
|
discountOption?: ListPaymentsResponse['discount_option'];
|
|
13
|
-
loyaltyPointsProgram?: ListPaymentsResponse['loyalty_points_programs']['k'];
|
|
13
|
+
loyaltyPointsProgram?: Exclude<ListPaymentsResponse['loyalty_points_programs'], undefined>['k'];
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
-
const getPriceWithDiscount = (
|
|
16
|
+
const getPriceWithDiscount = (
|
|
17
|
+
price: number,
|
|
18
|
+
discount: Exclude<Props['discountOption'], undefined>,
|
|
19
|
+
) => {
|
|
17
20
|
const { type, value } = discount;
|
|
18
21
|
let priceWithDiscount: number;
|
|
19
22
|
if (value) {
|
|
@@ -134,7 +137,7 @@ const usePrices = (props: Props) => {
|
|
|
134
137
|
const programIds = Object.keys(pointsPrograms);
|
|
135
138
|
for (let i = 0; i < programIds.length; i++) {
|
|
136
139
|
const program = pointsPrograms[programIds[i]];
|
|
137
|
-
if (program && program.earn_percentage > 0) {
|
|
140
|
+
if (program?.earn_percentage && program.earn_percentage > 0) {
|
|
138
141
|
return program;
|
|
139
142
|
}
|
|
140
143
|
}
|
|
@@ -1,27 +1,44 @@
|
|
|
1
1
|
import type { Ref } from 'vue';
|
|
2
|
-
import type { CategoriesList } from '@cloudcommerce/api/types';
|
|
2
|
+
import type { ResourceId, Categories, CategoriesList } from '@cloudcommerce/api/types';
|
|
3
3
|
import { computed } from 'vue';
|
|
4
4
|
import useStickyHeader from '@@sf/composables/use-sticky-header';
|
|
5
5
|
|
|
6
6
|
export interface Props {
|
|
7
|
-
header: Ref<HTMLElement>;
|
|
7
|
+
header: Ref<HTMLElement | null>;
|
|
8
8
|
categories: CategoriesList;
|
|
9
9
|
menuCategorySlugs?: string[];
|
|
10
10
|
menuRandomCategories?: number;
|
|
11
11
|
isAlphabeticalSortSubmenu?: boolean;
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
type
|
|
15
|
-
|
|
14
|
+
type MainCategory = Partial<Categories> & {
|
|
15
|
+
_id: ResourceId,
|
|
16
|
+
name: string,
|
|
17
|
+
slug: string,
|
|
18
|
+
parent: undefined,
|
|
19
|
+
};
|
|
20
|
+
type Subcategory = Partial<Categories> & {
|
|
21
|
+
_id: ResourceId,
|
|
22
|
+
name: string,
|
|
23
|
+
slug: string,
|
|
24
|
+
parent: Exclude<Categories['parent'], undefined>,
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export type SubcategoryTree = Subcategory & {
|
|
28
|
+
subcategories: Array<SubcategoryTree>,
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export type CategoryTree = MainCategory & {
|
|
32
|
+
subcategories: Array<SubcategoryTree>,
|
|
16
33
|
};
|
|
17
34
|
|
|
18
35
|
const filterMainCategories = (
|
|
19
36
|
categories: CategoriesList,
|
|
20
37
|
featuredSlugs?: string[],
|
|
21
38
|
) => {
|
|
22
|
-
const mainCategories = categories.filter(({ slug, parent }) => {
|
|
23
|
-
return slug && !parent;
|
|
24
|
-
})
|
|
39
|
+
const mainCategories = categories.filter(({ name, slug, parent }) => {
|
|
40
|
+
return name && slug && !parent;
|
|
41
|
+
}) as Array<MainCategory>;
|
|
25
42
|
if (featuredSlugs?.length) {
|
|
26
43
|
mainCategories.sort((a, b) => {
|
|
27
44
|
const indexA = featuredSlugs.indexOf(a.slug);
|
|
@@ -42,13 +59,13 @@ const filterSubcategories = (
|
|
|
42
59
|
parentCategory: CategoriesList[0],
|
|
43
60
|
isAlphabeticalSort = false,
|
|
44
61
|
) => {
|
|
45
|
-
const subcategories = categories.filter(({ slug, parent }) => {
|
|
46
|
-
if (slug && parent) {
|
|
62
|
+
const subcategories = categories.filter(({ name, slug, parent }) => {
|
|
63
|
+
if (name && slug && parent) {
|
|
47
64
|
return parent._id === parentCategory._id
|
|
48
65
|
|| (parent.slug && parent.slug === parentCategory.slug);
|
|
49
66
|
}
|
|
50
67
|
return false;
|
|
51
|
-
})
|
|
68
|
+
}) as Array<Subcategory>;
|
|
52
69
|
if (isAlphabeticalSort) {
|
|
53
70
|
subcategories.sort((a, b) => {
|
|
54
71
|
if (a.name < b.name) return -1;
|
|
@@ -71,7 +88,7 @@ const useShopHeader = ({
|
|
|
71
88
|
staticY,
|
|
72
89
|
} = useStickyHeader({ header });
|
|
73
90
|
const positionY = computed(() => {
|
|
74
|
-
return isSticky.value ? header.value
|
|
91
|
+
return isSticky.value ? header.value?.offsetHeight : staticY.value;
|
|
75
92
|
});
|
|
76
93
|
const mainCategories = filterMainCategories(categories, menuCategorySlugs);
|
|
77
94
|
const getSubcategories = (parentCategory: CategoriesList[0]) => {
|
|
@@ -81,7 +98,8 @@ const useShopHeader = ({
|
|
|
81
98
|
!!isAlphabeticalSortSubmenu,
|
|
82
99
|
);
|
|
83
100
|
};
|
|
84
|
-
const getCategoryTree =
|
|
101
|
+
const getCategoryTree = <T extends CategoriesList[0]>(parentCategory: T):
|
|
102
|
+
T & { subcategories: SubcategoryTree[] } => {
|
|
85
103
|
return {
|
|
86
104
|
...parentCategory,
|
|
87
105
|
subcategories: getSubcategories(parentCategory).map((subcategory) => {
|
|
@@ -121,5 +139,3 @@ export {
|
|
|
121
139
|
filterMainCategories,
|
|
122
140
|
filterSubcategories,
|
|
123
141
|
};
|
|
124
|
-
|
|
125
|
-
export type { CategoryTree };
|
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
} from '@vueuse/core';
|
|
14
14
|
|
|
15
15
|
export interface Props {
|
|
16
|
-
header: Ref<HTMLElement>;
|
|
16
|
+
header: Ref<HTMLElement | null>;
|
|
17
17
|
canSetStyles?: boolean;
|
|
18
18
|
canCreateHeightDiv?: boolean;
|
|
19
19
|
isShownOnScrollDown?: boolean;
|
|
@@ -38,11 +38,11 @@ const useStickyHeader = (props: Props) => {
|
|
|
38
38
|
const isSticky = computed(() => ready.value && y.value > staticY.value * 1.2);
|
|
39
39
|
const transition = ref('none');
|
|
40
40
|
watch(isSticky, async (_isSticky) => {
|
|
41
|
-
if (canSetStyles) {
|
|
42
|
-
header.value.style.position = _isSticky ? 'sticky' :
|
|
41
|
+
if (canSetStyles && header.value) {
|
|
42
|
+
header.value.style.position = _isSticky ? 'sticky' : '';
|
|
43
43
|
}
|
|
44
44
|
if (heightDiv) {
|
|
45
|
-
heightDiv.style.height = _isSticky ? `${staticHeight.value}px` :
|
|
45
|
+
heightDiv.style.height = _isSticky ? `${staticHeight.value}px` : '';
|
|
46
46
|
}
|
|
47
47
|
if (!_isSticky) {
|
|
48
48
|
start();
|
|
@@ -58,10 +58,12 @@ const useStickyHeader = (props: Props) => {
|
|
|
58
58
|
});
|
|
59
59
|
if (!import.meta.env.SSR) {
|
|
60
60
|
onMounted(() => {
|
|
61
|
+
if (!header.value) throw new Error('<header> (props.header) not set');
|
|
61
62
|
const fixHeight = () => {
|
|
62
|
-
|
|
63
|
+
const headerElm = header.value as HTMLElement;
|
|
64
|
+
staticHeight.value = headerElm.offsetHeight;
|
|
63
65
|
staticY.value = staticHeight.value
|
|
64
|
-
+ window.pageYOffset +
|
|
66
|
+
+ window.pageYOffset + headerElm.getBoundingClientRect().top;
|
|
65
67
|
start();
|
|
66
68
|
};
|
|
67
69
|
const imgs = header.value.getElementsByTagName('IMG');
|
|
@@ -80,22 +82,23 @@ const useStickyHeader = (props: Props) => {
|
|
|
80
82
|
if (canSetStyles) {
|
|
81
83
|
header.value.style.willChange = 'transform';
|
|
82
84
|
watch([isSticky, isScrollUp], ([_isSticky, _isScrollUp]) => {
|
|
83
|
-
let opacity: string
|
|
84
|
-
let transform: string
|
|
85
|
+
let opacity: string = '';
|
|
86
|
+
let transform: string = '';
|
|
85
87
|
if (_isSticky && (!_isScrollUp || isShownOnScrollDown)) {
|
|
86
88
|
opacity = '0';
|
|
87
89
|
transform = 'translateY(-100%)';
|
|
88
90
|
}
|
|
89
|
-
header.value
|
|
90
|
-
|
|
91
|
+
const headerElm = header.value as HTMLElement;
|
|
92
|
+
headerElm.style.opacity = opacity;
|
|
93
|
+
headerElm.style.transform = transform;
|
|
91
94
|
});
|
|
92
95
|
watch(transition, (_transition) => {
|
|
93
|
-
header.value.style.transition = _transition;
|
|
96
|
+
(header.value as HTMLElement).style.transition = _transition;
|
|
94
97
|
});
|
|
95
98
|
}
|
|
96
99
|
if (canCreateHeightDiv) {
|
|
97
100
|
heightDiv = document.createElement('div');
|
|
98
|
-
header.value.parentElement
|
|
101
|
+
header.value.parentElement?.insertBefore(heightDiv, header.value);
|
|
99
102
|
}
|
|
100
103
|
});
|
|
101
104
|
}
|
|
@@ -16,10 +16,10 @@ const { settings } = Astro.props.pageContext;
|
|
|
16
16
|
|
|
17
17
|
<body>
|
|
18
18
|
<div id="teleported-overlap" class="relative z-50"></div>
|
|
19
|
-
<div id="teleported-top" class="relative z-
|
|
19
|
+
<div id="teleported-top" class="relative z-10"></div>
|
|
20
20
|
<slot />
|
|
21
21
|
<slot name="before-body-end" />
|
|
22
|
-
<div id="teleported-bottom" class="relative z-
|
|
22
|
+
<div id="teleported-bottom" class="relative z-10"></div>
|
|
23
23
|
{settings.icon &&
|
|
24
24
|
<Picture
|
|
25
25
|
src={settings.icon}
|
|
@@ -31,11 +31,11 @@ const description = state.meta_description || state.short_description || setting
|
|
|
31
31
|
const favicon = settings.icon ? getIconUrl(32) : '/favicon.ico';
|
|
32
32
|
const shortcutIcon = settings.icon ? getIconUrl(192) : null;
|
|
33
33
|
const canonicalUrl = new URL(Astro.url.pathname, Astro.site || `https://${domain}`);
|
|
34
|
-
const cmsMetatags = await cms('
|
|
34
|
+
const { metatags: cmsMetatags } = await cms('layout');
|
|
35
35
|
const ogLocale = lang.length === 2 ? lang : lang.substring(0, 2) + lang.slice(3).toUpperCase();
|
|
36
36
|
let ogImage: string | undefined;
|
|
37
37
|
if (apiDoc) {
|
|
38
|
-
const picture = getImg(state,
|
|
38
|
+
const picture = getImg(state, undefined, 'zoom');
|
|
39
39
|
ogImage = picture && picture.url;
|
|
40
40
|
}
|
|
41
41
|
if (!ogImage) {
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import type { PageContext } from '@@sf/ssr-context';
|
|
2
|
+
import type { CmsHome } from '@@sf/cms';
|
|
3
|
+
import type { Props as UseHeroSliderProps } from '@@sf/composables/use-hero-slider';
|
|
4
|
+
|
|
5
|
+
export type HeroSliderProps = Omit<UseHeroSliderProps, 'slides'> & {
|
|
6
|
+
slides: Array<Omit<UseHeroSliderProps['slides'][0], 'img'> & { img: string }>,
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
export interface Props {
|
|
10
|
+
pageContext: PageContext;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const useHeroSection = async ({ pageContext }: Props) => {
|
|
14
|
+
const { cmsContent } = pageContext;
|
|
15
|
+
const heroSlider: HeroSliderProps = { slides: [] };
|
|
16
|
+
const cmsHero: CmsHome['hero'] | undefined = cmsContent?.hero;
|
|
17
|
+
if (cmsHero) {
|
|
18
|
+
heroSlider.autoplay = cmsHero.autoplay;
|
|
19
|
+
const now = Date.now();
|
|
20
|
+
cmsHero.slides?.forEach(({
|
|
21
|
+
img,
|
|
22
|
+
start,
|
|
23
|
+
end,
|
|
24
|
+
mobile_img: mobileImg,
|
|
25
|
+
button_link: buttonLink,
|
|
26
|
+
button_text: buttonText,
|
|
27
|
+
...rest
|
|
28
|
+
}) => {
|
|
29
|
+
if (!img) return;
|
|
30
|
+
if (start && new Date(start).getTime() < now) return;
|
|
31
|
+
if (end && new Date(end).getTime() > now) return;
|
|
32
|
+
heroSlider.slides.push({
|
|
33
|
+
...rest,
|
|
34
|
+
img,
|
|
35
|
+
mobileImg,
|
|
36
|
+
buttonLink,
|
|
37
|
+
buttonText,
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
return {
|
|
42
|
+
heroSlider,
|
|
43
|
+
};
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
export default useHeroSection;
|
|
47
|
+
|
|
48
|
+
export { useHeroSection };
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { PageContext } from '@@sf/ssr-context';
|
|
2
|
+
import useHeroSection from '@@sf/layouts/sections/use-hero-section';
|
|
3
|
+
|
|
4
|
+
export interface Props {
|
|
5
|
+
pageContext: PageContext;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
const useHomeMain = async ({ pageContext }: Props) => {
|
|
9
|
+
return {
|
|
10
|
+
...(await useHeroSection({ pageContext })),
|
|
11
|
+
};
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export default useHomeMain;
|
|
15
|
+
|
|
16
|
+
export { useHomeMain };
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { PageContext } from '@@sf/ssr-context';
|
|
2
|
+
import type { CmsLayout } from '@@sf/cms';
|
|
3
|
+
import type { Props as PitchBarProps } from '@@sf/composables/use-pitch-bar';
|
|
4
|
+
import type { Props as UseShopHeaderProps } from '@@sf/composables/use-shop-header';
|
|
5
|
+
|
|
6
|
+
type ShopHeaderProps = Omit<UseShopHeaderProps, 'header'> & {
|
|
7
|
+
serviceLinks?: CmsLayout['service_links'],
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export interface Props {
|
|
11
|
+
pageContext: PageContext;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const usePageLayout = async ({ pageContext }: Props) => {
|
|
15
|
+
const { apiState, cms } = pageContext;
|
|
16
|
+
const {
|
|
17
|
+
header: cmsHeader,
|
|
18
|
+
service_links: cmsServiceLinks,
|
|
19
|
+
} = await cms('layout');
|
|
20
|
+
const pitchBar: PitchBarProps = { slides: [] };
|
|
21
|
+
if (cmsHeader?.pitch_bar) {
|
|
22
|
+
pitchBar.slides = cmsHeader.pitch_bar;
|
|
23
|
+
}
|
|
24
|
+
const shopHeader: ShopHeaderProps = {
|
|
25
|
+
categories: apiState.categories || [],
|
|
26
|
+
menuCategorySlugs: cmsHeader.inline_menu_categories?.featured,
|
|
27
|
+
menuRandomCategories: cmsHeader.inline_menu_categories?.random,
|
|
28
|
+
isAlphabeticalSortSubmenu: cmsHeader.alphabetical_sort_submenu,
|
|
29
|
+
serviceLinks: cmsServiceLinks,
|
|
30
|
+
};
|
|
31
|
+
return {
|
|
32
|
+
pitchBar,
|
|
33
|
+
shopHeader,
|
|
34
|
+
};
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export default usePageLayout;
|
|
38
|
+
|
|
39
|
+
export { usePageLayout };
|
|
40
|
+
|
|
41
|
+
export type { PitchBarProps, ShopHeaderProps };
|
|
@@ -11,7 +11,7 @@ const formatPercentage = (value: number, digits = 1) => {
|
|
|
11
11
|
const createApp = (app: App) => {
|
|
12
12
|
app.use({
|
|
13
13
|
// eslint-disable-next-line no-shadow
|
|
14
|
-
install: (app: App, options
|
|
14
|
+
install: (app: App, options?: Record<string, any>) => {
|
|
15
15
|
// @ts-ignore
|
|
16
16
|
app.config.globalProperties.$t = (dict, lang) => {
|
|
17
17
|
// @ts-ignore
|
|
@@ -37,8 +37,11 @@ const getModulesInfoPreset = (settingsModules = globalThis.storefront.settings.m
|
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
const settingsShipping = settingsModules.calculate_shipping;
|
|
40
|
-
|
|
41
|
-
|
|
40
|
+
const freeShippingFromValue = settingsShipping?.free_shipping_from_value;
|
|
41
|
+
if (typeof freeShippingFromValue === 'number') {
|
|
42
|
+
modulesInfoPreset.calculate_shipping = {
|
|
43
|
+
free_shipping_from_value: freeShippingFromValue,
|
|
44
|
+
};
|
|
42
45
|
}
|
|
43
46
|
}
|
|
44
47
|
return modulesInfoPreset;
|
|
@@ -3,7 +3,12 @@ let utm: { [k: string]: string } = {};
|
|
|
3
3
|
|
|
4
4
|
if (!import.meta.env.SSR) {
|
|
5
5
|
const storageKey = 'ecomUtm';
|
|
6
|
-
|
|
6
|
+
const storedValue = sessionStorage.getItem(storageKey);
|
|
7
|
+
try {
|
|
8
|
+
utm = (storedValue && JSON.parse(storedValue)) || {};
|
|
9
|
+
} catch {
|
|
10
|
+
utm = {};
|
|
11
|
+
}
|
|
7
12
|
|
|
8
13
|
let isCurrentUrl;
|
|
9
14
|
const urlParams = new URLSearchParams(window.location.search);
|
|
@@ -2,9 +2,10 @@ import type { AstroGlobal } from 'astro';
|
|
|
2
2
|
import type { BaseConfig } from '@cloudcommerce/config';
|
|
3
3
|
import type { ApiError, ApiEndpoint } from '@cloudcommerce/api';
|
|
4
4
|
import type { CategoriesList, BrandsList } from '@cloudcommerce/api/types';
|
|
5
|
-
import type { CMS, CmsSettings } from './cms';
|
|
5
|
+
import type { CMS, CmsSettings, CmsHome } from './cms';
|
|
6
6
|
import { EventEmitter } from 'node:events';
|
|
7
7
|
import api from '@cloudcommerce/api';
|
|
8
|
+
// @ts-ignore
|
|
8
9
|
import _getConfig from '../../storefront.config.mjs';
|
|
9
10
|
|
|
10
11
|
type StorefrontConfig = {
|
|
@@ -17,8 +18,6 @@ type StorefrontConfig = {
|
|
|
17
18
|
primaryColor: CmsSettings['primary_color'],
|
|
18
19
|
secondaryColor: CmsSettings['secondary_color'],
|
|
19
20
|
settings: CmsSettings,
|
|
20
|
-
dirContent: string,
|
|
21
|
-
// eslint-disable-next-line no-unused-vars
|
|
22
21
|
cms: CMS,
|
|
23
22
|
};
|
|
24
23
|
|
|
@@ -60,16 +59,15 @@ const loadPageContext = async (Astro: Readonly<AstroGlobal>, {
|
|
|
60
59
|
const startedAt = Date.now();
|
|
61
60
|
const urlPath = Astro.url.pathname;
|
|
62
61
|
const isHomepage = urlPath === '/';
|
|
63
|
-
const { slug } = Astro.params;
|
|
64
62
|
const config = getConfig();
|
|
65
63
|
globalThis.storefront.settings = config.settings;
|
|
66
|
-
let cmsContent: Record<string, any> | undefined;
|
|
64
|
+
let cmsContent: CmsHome | Record<string, any> | null | undefined;
|
|
67
65
|
let apiResource: 'products' | 'categories' | 'brands' | 'collections' | undefined;
|
|
68
66
|
let apiDoc: Record<string, any> | undefined;
|
|
69
67
|
const apiState: {
|
|
70
68
|
categories?: CategoriesList,
|
|
71
69
|
brands?: BrandsList,
|
|
72
|
-
[k: string]: Record<string, any
|
|
70
|
+
[k: string]: Record<string, any> | undefined,
|
|
73
71
|
} = {};
|
|
74
72
|
const apiOptions = {
|
|
75
73
|
fetch,
|
|
@@ -79,11 +77,16 @@ const loadPageContext = async (Astro: Readonly<AstroGlobal>, {
|
|
|
79
77
|
null, // fetch by slug
|
|
80
78
|
...apiPrefetchEndpoints.map((endpoint) => api.get(endpoint, apiOptions)),
|
|
81
79
|
];
|
|
82
|
-
if (
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
}
|
|
86
|
-
|
|
80
|
+
if (isHomepage) {
|
|
81
|
+
cmsContent = await config.cms('home');
|
|
82
|
+
} else {
|
|
83
|
+
const { slug } = Astro.params;
|
|
84
|
+
if (slug) {
|
|
85
|
+
if (cmsCollection) {
|
|
86
|
+
cmsContent = await config.cms(`${cmsCollection}/${slug}`);
|
|
87
|
+
} else {
|
|
88
|
+
apiFetchings[0] = api.get(`slugs/${slug}`, apiOptions);
|
|
89
|
+
}
|
|
87
90
|
}
|
|
88
91
|
}
|
|
89
92
|
try {
|
|
@@ -91,10 +94,15 @@ const loadPageContext = async (Astro: Readonly<AstroGlobal>, {
|
|
|
91
94
|
if (slugResponse) {
|
|
92
95
|
apiResource = slugResponse.data.resource;
|
|
93
96
|
apiDoc = slugResponse.data.doc;
|
|
94
|
-
|
|
97
|
+
if (apiDoc) {
|
|
98
|
+
apiState[`${apiResource}/${apiDoc._id}`] = apiDoc;
|
|
99
|
+
}
|
|
95
100
|
}
|
|
96
|
-
prefetchResponses.forEach((
|
|
97
|
-
|
|
101
|
+
prefetchResponses.forEach((response) => {
|
|
102
|
+
if (response) {
|
|
103
|
+
const { config: { endpoint }, data } = response;
|
|
104
|
+
apiState[endpoint.replace(/\?.*$/, '')] = data.result || data;
|
|
105
|
+
}
|
|
98
106
|
});
|
|
99
107
|
} catch (err: any) {
|
|
100
108
|
const error: ApiError = err;
|
|
@@ -129,7 +137,7 @@ const loadPageContext = async (Astro: Readonly<AstroGlobal>, {
|
|
|
129
137
|
}
|
|
130
138
|
if (apiDoc) {
|
|
131
139
|
globalThis.storefront.context = {
|
|
132
|
-
resource: apiResource,
|
|
140
|
+
resource: apiResource as Exclude<typeof apiResource, undefined>,
|
|
133
141
|
doc: apiDoc as any,
|
|
134
142
|
timestamp: Date.now(),
|
|
135
143
|
};
|
|
@@ -141,6 +149,7 @@ const loadPageContext = async (Astro: Readonly<AstroGlobal>, {
|
|
|
141
149
|
apiResource,
|
|
142
150
|
apiDoc,
|
|
143
151
|
apiState,
|
|
152
|
+
getContent: config.cms,
|
|
144
153
|
};
|
|
145
154
|
emitter.emit('load', pageContext);
|
|
146
155
|
return pageContext;
|
|
@@ -49,8 +49,18 @@ const logout = () => {
|
|
|
49
49
|
firebaseAuth.signOut();
|
|
50
50
|
};
|
|
51
51
|
|
|
52
|
+
const throwNoAuth = (msg = 'Not authenticated') => {
|
|
53
|
+
const err: any = new Error(msg);
|
|
54
|
+
err.isNoAuth = true;
|
|
55
|
+
throw err;
|
|
56
|
+
};
|
|
57
|
+
|
|
52
58
|
const authenticate = async () => {
|
|
53
|
-
const authToken = await firebaseAuth.currentUser
|
|
59
|
+
const authToken = await firebaseAuth.currentUser?.getIdToken();
|
|
60
|
+
if (!authToken) {
|
|
61
|
+
throwNoAuth('Can\'t get Firebase user ID token');
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
54
64
|
const { domain } = window.storefront.settings;
|
|
55
65
|
try {
|
|
56
66
|
const resAuth = await fetch(`https://${domain}/api/passport/token`, {
|
|
@@ -70,12 +80,14 @@ const getAccessToken = async () => {
|
|
|
70
80
|
if (!isAuthenticated.value) {
|
|
71
81
|
await authenticate();
|
|
72
82
|
}
|
|
83
|
+
if (!session.auth) return throwNoAuth();
|
|
73
84
|
return session.auth.access_token;
|
|
74
85
|
};
|
|
75
86
|
|
|
76
87
|
const fetchCustomer = async () => {
|
|
77
88
|
const accessToken = await getAccessToken();
|
|
78
|
-
const
|
|
89
|
+
const auth = session.auth as Exclude<typeof session.auth, null>;
|
|
90
|
+
const { data } = await api.get(`customers/${auth.customer_id}`, {
|
|
79
91
|
accessToken,
|
|
80
92
|
});
|
|
81
93
|
session.customer = data;
|
|
@@ -3,18 +3,14 @@ import { randomObjectId } from '@ecomplus/utils';
|
|
|
3
3
|
|
|
4
4
|
type CartItem = CartSet['items'][0];
|
|
5
5
|
|
|
6
|
-
const matchItemsFlags = (item: CartItem,
|
|
7
|
-
if (!item.flags && !
|
|
6
|
+
const matchItemsFlags = (item: CartItem, { flags: newItemFlags }: CartItem) => {
|
|
7
|
+
if (!item.flags && !newItemFlags) {
|
|
8
8
|
return true;
|
|
9
9
|
}
|
|
10
|
-
if (
|
|
11
|
-
!item.flags
|
|
12
|
-
|| !newItem.flags
|
|
13
|
-
|| item.flags.length !== newItem.flags.length
|
|
14
|
-
) {
|
|
10
|
+
if (!item.flags || !newItemFlags || item.flags.length !== newItemFlags.length) {
|
|
15
11
|
return false;
|
|
16
12
|
}
|
|
17
|
-
return item.flags.every((flag) =>
|
|
13
|
+
return item.flags.every((flag) => newItemFlags.includes(flag));
|
|
18
14
|
};
|
|
19
15
|
|
|
20
16
|
const addCartItem = (cart: CartSet, newItem: CartItem): null | CartItem => {
|
|
@@ -47,7 +43,7 @@ const addCartItem = (cart: CartSet, newItem: CartItem): null | CartItem => {
|
|
|
47
43
|
}
|
|
48
44
|
}
|
|
49
45
|
}
|
|
50
|
-
const itemCopy = { ...newItem };
|
|
46
|
+
const itemCopy: CartItem = { ...newItem };
|
|
51
47
|
if (
|
|
52
48
|
!newItem._id
|
|
53
49
|
|| newItem._id === newItem.variation_id
|
|
@@ -56,9 +52,11 @@ const addCartItem = (cart: CartSet, newItem: CartItem): null | CartItem => {
|
|
|
56
52
|
itemCopy._id = randomObjectId();
|
|
57
53
|
}
|
|
58
54
|
if (newItem.customizations) {
|
|
55
|
+
const customizationsCopy: Exclude<CartItem['customizations'], undefined> = [];
|
|
59
56
|
newItem.customizations.forEach((customization, i) => {
|
|
60
|
-
|
|
57
|
+
customizationsCopy[i] = { ...customization };
|
|
61
58
|
});
|
|
59
|
+
itemCopy.customizations = customizationsCopy;
|
|
62
60
|
}
|
|
63
61
|
items.push(itemCopy);
|
|
64
62
|
return itemCopy;
|
|
@@ -12,19 +12,20 @@ const cart = useStorage<CartSet>(storageKey, emptyCart);
|
|
|
12
12
|
|
|
13
13
|
const cartItems = computed(() => {
|
|
14
14
|
return cart.items.map((item) => {
|
|
15
|
-
|
|
15
|
+
let finalPrice = item.kit_product?.price && item.kit_product.pack_quantity
|
|
16
16
|
? item.kit_product.price / item.kit_product.pack_quantity
|
|
17
17
|
: item.price;
|
|
18
18
|
if (Array.isArray(item.customizations)) {
|
|
19
19
|
item.customizations.forEach((customization) => {
|
|
20
20
|
if (customization.add_to_price) {
|
|
21
21
|
const { type, addition } = customization.add_to_price;
|
|
22
|
-
|
|
22
|
+
finalPrice += type === 'fixed'
|
|
23
23
|
? addition
|
|
24
24
|
: item.price * (addition / 100);
|
|
25
25
|
}
|
|
26
26
|
});
|
|
27
27
|
}
|
|
28
|
+
item.final_price = finalPrice;
|
|
28
29
|
const min = item.min_quantity || 1;
|
|
29
30
|
const max = item.max_quantity;
|
|
30
31
|
if (
|
|
@@ -41,7 +42,7 @@ const cartItems = computed(() => {
|
|
|
41
42
|
});
|
|
42
43
|
const subtotal = computed(() => {
|
|
43
44
|
return cartItems.value.reduce((acc, item) => {
|
|
44
|
-
return acc + (item.quantity * item.final_price);
|
|
45
|
+
return acc + (item.quantity * (item.final_price || item.price));
|
|
45
46
|
}, 0);
|
|
46
47
|
});
|
|
47
48
|
const shoppingCart = computed({
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
// eslint-disable-next-line import/no-extraneous-dependencies
|
|
3
3
|
import '@vue/runtime-core';
|
|
4
4
|
import type { FormatPercentage } from '@@sf/pages/_vue';
|
|
5
|
+
import type { CmsSettings } from '@@sf/cms';
|
|
5
6
|
|
|
6
7
|
type Dictionary = Omit<typeof import('@@i18n'),
|
|
7
8
|
'i19StoreApiResources' | 'i19ApiActions' | 'i19TransactionsType' | 'i19StateRegister' |
|
|
@@ -18,8 +19,8 @@ declare module '@vue/runtime-core' {
|
|
|
18
19
|
};
|
|
19
20
|
$money: typeof import('@ecomplus/utils')['formatMoney'];
|
|
20
21
|
$percentage: FormatPercentage;
|
|
21
|
-
$settings:
|
|
22
|
-
$context: typeof
|
|
22
|
+
$settings: Partial<CmsSettings>;
|
|
23
|
+
$context: typeof window.storefront.context;
|
|
23
24
|
}
|
|
24
25
|
|
|
25
26
|
export interface GlobalComponents {
|