@@ -31,6 +33,7 @@
@@ -44,6 +47,7 @@
@@ -56,11 +60,12 @@
-
+
import { SfBottomNavigation, SfCircleIcon } from '@storefront-ui/vue';
import { defineComponent, useRouter, useContext } from '@nuxtjs/composition-api';
-import { useUiState } from '~/composables';
+import { useUiState } from '~/composables/useUiState';
import { useUser } from '~/modules/customer/composables/useUser';
import SvgImage from '~/components/General/SvgImage.vue';
import { useCategoryStore } from '~/modules/catalog/category/stores/category';
@@ -86,6 +91,7 @@ import { useCategoryStore } from '~/modules/catalog/category/stores/category';
const MobileCategorySidebar = () => import('~/modules/catalog/category/components/sidebar/MobileCategorySidebar/MobileCategorySidebar.vue');
export default defineComponent({
+ name: 'BottomNavigation',
components: {
SfBottomNavigation,
SfCircleIcon,
@@ -103,6 +109,15 @@ export default defineComponent({
const { isAuthenticated } = useUser();
const router = useRouter();
const { app } = useContext();
+
+ const handleHomeClick = async () => {
+ const homePath = app.localeRoute({ name: 'home' });
+ await router.push(homePath);
+ if (isMobileMenuOpen.value) {
+ toggleMobileMenu();
+ }
+ };
+
const handleAccountClick = async () => {
if (isAuthenticated.value) {
await router.push(app.localeRoute({ name: 'customer' }));
@@ -127,7 +142,7 @@ export default defineComponent({
toggleMobileMenu,
loadCategoryMenu,
handleAccountClick,
- app,
+ handleHomeClick,
};
},
});
diff --git a/components/CartSidebar.vue b/components/CartSidebar.vue
index d8d333b..1c9e0ea 100644
--- a/components/CartSidebar.vue
+++ b/components/CartSidebar.vue
@@ -92,6 +92,7 @@
updateItemQty(params),
1000,
);
@@ -548,31 +549,12 @@ export default defineComponent({
}
}
- &__actions {
- transition: opacity 150ms ease-in-out;
- }
-
- &__save,
- &__compare {
- --button-padding: 0;
-
- &:focus {
- --cp-save-opacity: 1;
- --cp-compare-opacity: 1;
- }
- }
-
- &__save {
- opacity: var(--cp-save-opacity, 0);
- }
-
- &__compare {
- opacity: var(--cp-compare-opacity, 0);
+ ::v-deep .sf-collected-product__actions {
+ display: none;
}
&:hover {
- --cp-save-opacity: 1;
- --cp-compare-opacity: 1;
+ --collected-product-configuration-display: initial;
@include for-desktop {
.collected-product__properties {
display: none;
diff --git a/components/Checkout/UserBillingAddresses.vue b/components/Checkout/UserBillingAddresses.vue
deleted file mode 100644
index 8321b87..0000000
--- a/components/Checkout/UserBillingAddresses.vue
+++ /dev/null
@@ -1,111 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/components/CurrencySelector/CurrenciesModal.vue b/components/CurrencySelector/CurrenciesModal.vue
index 8f2ba80..5df7fdc 100644
--- a/components/CurrencySelector/CurrenciesModal.vue
+++ b/components/CurrencySelector/CurrenciesModal.vue
@@ -48,7 +48,10 @@ export default defineComponent({
},
props: {
isModalOpen: Boolean,
- selectedCurrency: String,
+ selectedCurrency: {
+ type: String,
+ default: '',
+ },
},
emits: ['closeModal'],
setup() {
@@ -58,7 +61,7 @@ export default defineComponent({
load: loadCurrencies,
} = useCurrency();
- const availableCurrencies = computed(() => currencies.value?.available_currency_codes || []);
+ const availableCurrencies = computed(() => currencies.value?.available_currency_codes || []);
onMounted(() => {
if (currencies.value && currencies.value?.available_currency_codes) return;
diff --git a/components/CurrencySelector/__tests__/CurrencySelector.spec.ts b/components/CurrencySelector/__tests__/CurrencySelector.spec.ts
new file mode 100644
index 0000000..72484a9
--- /dev/null
+++ b/components/CurrencySelector/__tests__/CurrencySelector.spec.ts
@@ -0,0 +1,80 @@
+import userEvent from '@testing-library/user-event';
+import { render } from '~/test-utils';
+import CurrenciesModal from '~/components/CurrencySelector/CurrenciesModal.vue';
+import { useCurrencyMock } from '~/test-utils/mocks/useCurrency';
+import { useCurrency } from '~/composables';
+import { currencyDataMock } from '~/components/CurrencySelector/__tests__/currencyData.mock';
+
+jest.mock('~/composables', () => {
+ const originalModule = jest.requireActual('~/composables');
+ return {
+ ...originalModule,
+ useCurrency: jest.fn(),
+ };
+});
+
+describe('', () => {
+ it('does not render when closed', () => {
+ (useCurrency as jest.Mock).mockReturnValue(useCurrencyMock);
+ const { queryByRole } = render(CurrenciesModal, {
+ props: {
+ isModalOpen: false,
+ },
+ });
+
+ expect(queryByRole('heading', { name: /change store/i })).toBeNull();
+ });
+
+ it('emits "closeModal" event on close', async () => {
+ const user = userEvent.setup();
+
+ (useCurrency as jest.Mock).mockReturnValue(useCurrencyMock);
+ const { getAllByRole, emitted } = render(CurrenciesModal, {
+ props: {
+ isModalOpen: true,
+ },
+ });
+
+ const closeBtn = getAllByRole('button', { name: /close/i })[0];
+
+ await user.click(closeBtn);
+
+ expect(emitted()).toHaveProperty('closeModal');
+ });
+
+ it('renders list of available stores', () => {
+ useCurrencyMock.currency.value = currencyDataMock;
+
+ (useCurrency as jest.Mock).mockReturnValue(useCurrencyMock);
+
+ const { getByRole, getByText } = render(CurrenciesModal, {
+ props: {
+ isModalOpen: true,
+ },
+ });
+
+ expect(getByRole('heading', { name: /choose currency/i })).toBeTruthy();
+ expect(getByText(/eur/i)).toBeTruthy();
+ expect(getByText(/pln/i)).toBeTruthy();
+ expect(getByText(/usd/i)).toBeTruthy();
+ });
+
+ it('on currency selection executes method that must trigger currency switch', async () => {
+ const user = userEvent.setup();
+ useCurrencyMock.currency.value = currencyDataMock;
+
+ (useCurrency as jest.Mock).mockReturnValue(useCurrencyMock);
+
+ const { getByText } = render(CurrenciesModal, {
+ props: {
+ isLangModalOpen: true,
+ storeConfig: { store_code: 'default' },
+ },
+ });
+
+ const eurSwitchBtn = getByText(/eur/i);
+ await user.click(eurSwitchBtn);
+
+ expect(useCurrencyMock.change).toBeCalledWith({ id: 'EUR' });
+ });
+});
diff --git a/components/CurrencySelector/__tests__/currencyData.mock.ts b/components/CurrencySelector/__tests__/currencyData.mock.ts
new file mode 100644
index 0000000..dfe0305
--- /dev/null
+++ b/components/CurrencySelector/__tests__/currencyData.mock.ts
@@ -0,0 +1,31 @@
+export const currencyDataMock = {
+ __typename: 'Currency',
+ available_currency_codes: [
+ 'EUR',
+ 'PLN',
+ 'USD',
+ ],
+ base_currency_code: 'USD',
+ base_currency_symbol: '$',
+ default_display_currecy_code: null,
+ default_display_currecy_symbol: null,
+ default_display_currency_code: 'USD',
+ default_display_currency_symbol: '$',
+ exchange_rates: [
+ {
+ __typename: 'ExchangeRate',
+ currency_to: 'EUR',
+ rate: 0.93,
+ },
+ {
+ __typename: 'ExchangeRate',
+ currency_to: 'PLN',
+ rate: 4.29,
+ },
+ {
+ __typename: 'ExchangeRate',
+ currency_to: 'USD',
+ rate: 1,
+ },
+ ],
+};
diff --git a/components/Header/Navigation/HeaderNavigationSubcategories.vue b/components/Header/Navigation/HeaderNavigationSubcategories.vue
index 490f8ff..b83802c 100644
--- a/components/Header/Navigation/HeaderNavigationSubcategories.vue
+++ b/components/Header/Navigation/HeaderNavigationSubcategories.vue
@@ -181,7 +181,7 @@ export default defineComponent({
.header-navigation {
&__subcategories {
position: absolute;
- z-index: 10;
+ z-index: 1;
background-color: #fff;
box-shadow: 0 3px var(--c-primary);
left: 0;
diff --git a/components/Header/SearchBar/SearchBar.vue b/components/Header/SearchBar/SearchBar.vue
index efb3b06..df2a5d3 100644
--- a/components/Header/SearchBar/SearchBar.vue
+++ b/components/Header/SearchBar/SearchBar.vue
@@ -5,8 +5,8 @@
aria-label="Search"
class="sf-header__search"
:value="term"
- @input="handleSearch"
- @keydown.enter="handleSearch($event)"
+ @input="debouncedHandleSearch($event)"
+ @keyup.enter="handleKeydownEnter($event.target.value) /* https://github.com/vuestorefront/storefront-ui/issues/2453#issuecomment-1160231619 */"
@keydown.tab="hideSearch"
@focus="showSearch"
@click="showSearch"
@@ -53,7 +53,7 @@ import { SfButton, SfSearchBar } from '@storefront-ui/vue';
import {
defineComponent, ref, watch, useRoute,
} from '@nuxtjs/composition-api';
-import debounce from 'lodash.debounce';
+import { debounce } from 'lodash-es';
import { clickOutside } from '~/utilities/directives/click-outside/click-outside-directive';
import SvgImage from '~/components/General/SvgImage.vue';
import { useProduct } from '~/modules/catalog/product/composables/useProduct';
@@ -130,7 +130,7 @@ export default defineComponent({
}
};
- const handleSearch = debounce(async (searchTerm: string) => {
+ const rawHandleSearch = async (searchTerm: string) => {
term.value = searchTerm;
if (term.value.length < props.minTermLen) return;
@@ -141,7 +141,15 @@ export default defineComponent({
}) as unknown as Products;
emit('set-search-results', productList!.items);
- }, 1000);
+ };
+
+ const debouncedHandleSearch = debounce(rawHandleSearch, 1000);
+
+ const handleKeydownEnter = (searchTerm: string) => {
+ // cancel debounce timeout started by typing into the searchbar - this is to avoid making two network requests instead of one
+ debouncedHandleSearch.cancel();
+ rawHandleSearch(searchTerm);
+ };
watch(route, () => {
hideSearch();
@@ -153,7 +161,9 @@ export default defineComponent({
showSearch,
hideSearch,
toggleSearch,
- handleSearch,
+ rawHandleSearch,
+ debouncedHandleSearch,
+ handleKeydownEnter,
term,
};
},
diff --git a/components/Header/SearchBar/SearchResults.vue b/components/Header/SearchBar/SearchResults.vue
index c229458..4fe1794 100644
--- a/components/Header/SearchBar/SearchResults.vue
+++ b/components/Header/SearchBar/SearchResults.vue
@@ -202,6 +202,7 @@ export default defineComponent({
flex-direction: row;
flex: 1;
}
+ width: 100%;
}
&__results {
flex: 1;
@@ -226,7 +227,7 @@ export default defineComponent({
&--mobile {
display: flex;
flex-wrap: wrap;
- justify-content: space-around;
+ justify-content: flex-start;
background: var(--c-white);
padding: var(--spacer-base) var(--spacer-sm);
--product-card-max-width: 9rem;
diff --git a/components/MobileStoreBanner.vue b/components/MobileStoreBanner.vue
index c7175ff..da552a1 100644
--- a/components/MobileStoreBanner.vue
+++ b/components/MobileStoreBanner.vue
@@ -13,13 +13,15 @@
data-testid="banner-cta-button"
link="#"
>
-
+
+
+
-
+
+
+
@@ -92,4 +96,9 @@ export default defineComponent({
}
}
}
+// override ".sf-banner img" style spill
+::v-deep .app-store-image img {
+ position: initial;
+ margin-top: var(--spacer-base);
+}
diff --git a/components/SkeletonLoader/index.vue b/components/SkeletonLoader/index.vue
index 2c7357f..9992e22 100644
--- a/components/SkeletonLoader/index.vue
+++ b/components/SkeletonLoader/index.vue
@@ -9,7 +9,7 @@
diff --git a/modules/catalog/product/components/product-types/bundle/BundleProductSelector.vue b/modules/catalog/product/components/product-types/bundle/BundleProductSelector.vue
index 1af5ef4..dee0e8a 100644
--- a/modules/catalog/product/components/product-types/bundle/BundleProductSelector.vue
+++ b/modules/catalog/product/components/product-types/bundle/BundleProductSelector.vue
@@ -73,6 +73,12 @@
>
Add to Cart
+
+
+
diff --git a/components/Checkout/UserShippingAddresses.vue b/modules/checkout/components/UserShippingAddresses.vue
similarity index 53%
rename from components/Checkout/UserShippingAddresses.vue
rename to modules/checkout/components/UserShippingAddresses.vue
index b47c921..0a25420 100644
--- a/components/Checkout/UserShippingAddresses.vue
+++ b/modules/checkout/components/UserShippingAddresses.vue
@@ -2,16 +2,20 @@
-
+
+
+ {{ shippingAddress.countryName }}
+
+
@@ -28,12 +32,13 @@
diff --git a/components/Checkout/VsfPaymentProvider.vue b/modules/checkout/components/VsfPaymentProvider.vue
similarity index 100%
rename from components/Checkout/VsfPaymentProvider.vue
rename to modules/checkout/components/VsfPaymentProvider.vue
diff --git a/components/Checkout/VsfShippingProvider.vue b/modules/checkout/components/VsfShippingProvider.vue
similarity index 100%
rename from components/Checkout/VsfShippingProvider.vue
rename to modules/checkout/components/VsfShippingProvider.vue
diff --git a/modules/checkout/components/styles/userAddresses.scss b/modules/checkout/components/styles/userAddresses.scss
new file mode 100644
index 0000000..12ee42a
--- /dev/null
+++ b/modules/checkout/components/styles/userAddresses.scss
@@ -0,0 +1,27 @@
+.address {
+ margin-bottom: var(--spacer-base);
+ @include for-desktop {
+ margin-right: var(--spacer-sm);
+ display: flex;
+ width: calc(50% - var(--spacer-sm));
+ flex-direction: column;
+ }
+}
+
+.addresses {
+ margin-bottom: var(--spacer-xl);
+ @include for-desktop {
+ display: flex;
+ flex-wrap: wrap;
+ margin-right: var(--spacer-sm)
+ }
+}
+
+.setAsDefault {
+ margin-bottom: var(--spacer-xl);
+}
+
+.sf-divider,
+.form__action-button--margin-bottom {
+ margin-bottom: var(--spacer-xl);
+}
diff --git a/modules/checkout/composables/useBilling/commands/saveBillingAddressCommand.ts b/modules/checkout/composables/useBilling/commands/saveBillingAddressCommand.ts
index 5261258..f536fc1 100644
--- a/modules/checkout/composables/useBilling/commands/saveBillingAddressCommand.ts
+++ b/modules/checkout/composables/useBilling/commands/saveBillingAddressCommand.ts
@@ -2,7 +2,7 @@ import { Logger } from '~/helpers/logger';
import { BillingCartAddress, Maybe, SetBillingAddressOnCartInput } from '~/modules/GraphQL/types';
export const saveBillingAddressCommand = {
- execute: async (context, cartId, billingDetails): Promise
> => {
+ execute: async (context, cartId, billingDetails, customQuery): Promise> => {
const {
apartment,
neighborhood,
@@ -29,6 +29,7 @@ export const saveBillingAddressCommand = {
const { data } = await context.$vsf.$magento.api.setBillingAddressOnCart(
setBillingAddressOnCartInput,
+ customQuery,
);
Logger.debug('[Result]:', { data });
diff --git a/modules/checkout/composables/useBilling/index.ts b/modules/checkout/composables/useBilling/index.ts
index fd69851..a1d8a0e 100644
--- a/modules/checkout/composables/useBilling/index.ts
+++ b/modules/checkout/composables/useBilling/index.ts
@@ -50,13 +50,13 @@ export function useBilling(): UseBillingInterface {
return billingInfo;
};
- const save = async ({ billingDetails }: UseBillingSaveParams): Promise> => {
+ const save = async ({ billingDetails, customQuery = null }: UseBillingSaveParams): Promise> => {
Logger.debug('useBilling.save');
let billingInfo = null;
try {
loading.value = true;
- billingInfo = await saveBillingAddressCommand.execute(context, cart.value.id, billingDetails);
+ billingInfo = await saveBillingAddressCommand.execute(context, cart.value.id, billingDetails, customQuery);
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
/**
diff --git a/modules/checkout/composables/useBilling/useBilling.ts b/modules/checkout/composables/useBilling/useBilling.ts
index 2fed259..d17253b 100644
--- a/modules/checkout/composables/useBilling/useBilling.ts
+++ b/modules/checkout/composables/useBilling/useBilling.ts
@@ -44,9 +44,9 @@ export type UseBillingLoadParams = ComposableFunctionArgs<{}>;
/**
* The params object accepted by the `save` method in the {@link useBilling|useBilling()} composable
*/
-export interface UseBillingSaveParams {
+export type UseBillingSaveParams = ComposableFunctionArgs<{
billingDetails: BillingDetails;
-}
+}>;
/**
* Data and methods returned from the {@link useBilling|useBilling()} composable
diff --git a/modules/checkout/composables/useCart/commands/addItemCommand.ts b/modules/checkout/composables/useCart/commands/addItemCommand.ts
index 9c69328..92e0c1d 100644
--- a/modules/checkout/composables/useCart/commands/addItemCommand.ts
+++ b/modules/checkout/composables/useCart/commands/addItemCommand.ts
@@ -1,12 +1,10 @@
import { VsfContext } from '~/composables/context';
import { Logger } from '~/helpers/logger';
import {
- AddConfigurableProductsToCartInput,
- AddDownloadableProductsToCartInput,
- AddVirtualProductsToCartInput,
Cart,
CartItemInput,
} from '~/modules/GraphQL/types';
+import { CustomQuery } from '~/types/core';
/** Params object used to add items to a cart */
export declare type AddProductsToCartInput = {
@@ -21,6 +19,8 @@ export const addItemCommand = {
product,
quantity,
currentCart,
+ productConfiguration,
+ customQuery,
},
) => {
Logger.debug('[Magento]: Add item to cart', {
@@ -30,7 +30,7 @@ export const addItemCommand = {
});
const apiState = context.$magento.config.state;
- const currentCartId = apiState.getCartId();
+ const cartId = apiState.getCartId();
if (!product) {
return;
@@ -39,7 +39,7 @@ export const addItemCommand = {
switch (product.__typename) {
case 'SimpleProduct':
const simpleCartInput: AddProductsToCartInput = {
- cartId: currentCartId,
+ cartId,
cartItems: [
{
quantity,
@@ -48,9 +48,13 @@ export const addItemCommand = {
],
};
- const simpleProduct = await context.$magento.api.addProductsToCart(simpleCartInput);
+ const simpleProduct = await context.$magento.api.addProductsToCart(simpleCartInput, customQuery as CustomQuery);
- Logger.debug('[Result]:', { data: simpleProduct });
+ Logger.debug('[Result]:', { data: simpleProduct.data });
+
+ if (simpleProduct.data.addProductsToCart.user_errors.length > 0) {
+ throw new Error(String(simpleProduct.data.addProductsToCart.user_errors[0].message));
+ }
// eslint-disable-next-line consistent-return
return simpleProduct
@@ -58,29 +62,29 @@ export const addItemCommand = {
.addProductsToCart
.cart as unknown as Cart;
case 'ConfigurableProduct':
- const cartItems = [
- {
- parent_sku: product.sku,
- data: {
+ const selectedOptions = Object.values(productConfiguration as object);
+
+ const configurableCartInput: AddProductsToCartInput = {
+ cartId,
+ cartItems: [
+ {
quantity,
- sku: product.configurable_product_options_selection?.variant?.sku || '',
+ sku: product.sku,
+ selected_options: selectedOptions,
},
- },
- ];
-
- const configurableCartInput: AddConfigurableProductsToCartInput = {
- cart_id: currentCartId,
- cart_items: cartItems,
+ ],
};
- const configurableProduct = await context.$magento.api.addConfigurableProductsToCart(configurableCartInput);
+ const configurableProduct = await context.$magento.api.addProductsToCart(configurableCartInput, customQuery as CustomQuery);
+ Logger.debug('[Result]:', { data: configurableProduct.data });
- Logger.debug('[Result]:', { data: configurableProduct });
+ if (configurableProduct.data.addProductsToCart.user_errors.length > 0) {
+ throw new Error(String(configurableProduct.data.addProductsToCart.user_errors[0].message));
+ }
// eslint-disable-next-line consistent-return
- return configurableProduct
- .data
- .addConfigurableProductsToCart
+ return configurableProduct.data
+ .addProductsToCart
.cart as unknown as Cart;
case 'BundleProduct':
const createEnteredOptions = () =>
@@ -91,7 +95,7 @@ export const addItemCommand = {
}));
const bundleCartInput: AddProductsToCartInput = {
- cartId: currentCartId,
+ cartId,
cartItems: [
{
quantity,
@@ -101,60 +105,65 @@ export const addItemCommand = {
],
};
- const bundleProduct = await context.$magento.api.addProductsToCart(bundleCartInput);
+ const bundleProduct = await context.$magento.api.addProductsToCart(bundleCartInput, customQuery as CustomQuery);
Logger.debug('[Result]:', { data: bundleProduct });
+ if (bundleProduct.data.addProductsToCart.user_errors.length > 0) {
+ throw new Error(String(bundleProduct.data.addProductsToCart.user_errors[0].message));
+ }
+
// eslint-disable-next-line consistent-return
return bundleProduct
.data
.addProductsToCart
.cart as unknown as Cart;
case 'DownloadableProduct':
- const downloadableCartItems = [
- {
- data: {
+ const downloadableCartInput: AddProductsToCartInput = {
+ cartId,
+ cartItems: [
+ {
quantity,
sku: product.sku,
},
- downloadable_product_links: product.downloadable_product_links.map((dpl) => ({ link_id: dpl.id })),
- },
- ];
-
- const downloadableCartInput: AddDownloadableProductsToCartInput = {
- cart_id: currentCartId,
- cart_items: downloadableCartItems,
+ ],
};
- const downloadableProduct = await context.$magento.api.addDownloadableProductsToCart(downloadableCartInput);
+ const downloadableProduct = await context.$magento.api.addProductsToCart(downloadableCartInput, customQuery as CustomQuery);
Logger.debug('[Result DownloadableProduct]:', { data: downloadableProduct });
+ if (downloadableProduct.data.addProductsToCart.user_errors.length > 0) {
+ throw new Error(String(downloadableProduct.data.addProductsToCart.user_errors[0].message));
+ }
+
// eslint-disable-next-line consistent-return
return downloadableProduct
.data
- .addDownloadableProductsToCart
+ .addProductsToCart
.cart as unknown as Cart;
case 'VirtualProduct':
- const virtualCartInput: AddVirtualProductsToCartInput = {
- cart_id: currentCartId,
- cart_items: [
+ const virtualCartInput: AddProductsToCartInput = {
+ cartId,
+ cartItems: [
{
- data: {
- quantity,
- sku: product.sku,
- },
+ quantity,
+ sku: product.sku,
},
],
};
- const virtualProduct = await context.$magento.api.addVirtualProductsToCart(virtualCartInput);
+ const virtualProduct = await context.$magento.api.addProductsToCart(virtualCartInput, customQuery as CustomQuery);
Logger.debug('[Result VirtualProduct]:', { data: virtualProduct });
+ if (downloadableProduct.data.addProductsToCart.user_errors.length > 0) {
+ throw new Error(String(downloadableProduct.data.addProductsToCart.user_errors[0].message));
+ }
+
// eslint-disable-next-line consistent-return
return virtualProduct
.data
- .addVirtualProductsToCart
+ .addProductsToCart
.cart as unknown as Cart;
default:
// eslint-disable-next-line no-underscore-dangle
diff --git a/modules/checkout/composables/useCart/commands/clearCartCommand.ts b/modules/checkout/composables/useCart/commands/clearCartCommand.ts
deleted file mode 100644
index 5360c27..0000000
--- a/modules/checkout/composables/useCart/commands/clearCartCommand.ts
+++ /dev/null
@@ -1,7 +0,0 @@
-import { VsfContext } from '~/composables/context';
-
-export const clearCartCommand = {
- execute: (context: VsfContext) => {
- context.$magento.config.state.setCartId(null);
- },
-};
diff --git a/modules/checkout/composables/useCart/commands/loadTotalQtyCommand.ts b/modules/checkout/composables/useCart/commands/loadTotalQtyCommand.ts
index cb56872..2a02c90 100644
--- a/modules/checkout/composables/useCart/commands/loadTotalQtyCommand.ts
+++ b/modules/checkout/composables/useCart/commands/loadTotalQtyCommand.ts
@@ -1,13 +1,14 @@
import { Logger } from '~/helpers/logger';
import { VsfContext } from '~/composables/context';
+import { ComposableFunctionArgs } from '~/composables';
export const loadTotalQtyCommand = {
- execute: async (context: VsfContext) => {
+ execute: async (context: VsfContext, params?: ComposableFunctionArgs<{}>) => {
Logger.debug('[Magento]: Load cart total qty');
const apiState = context.$magento.config.state;
if (apiState.getCartId()) {
- const { data } : any = await context.$magento.api.cartTotalQty(apiState.getCartId());
+ const { data } : any = await context.$magento.api.cartTotalQty(apiState.getCartId(), params?.customQuery ?? null);
return data?.cart?.total_quantity ?? 0;
}
diff --git a/modules/checkout/composables/useCart/commands/removeItemCommand.ts b/modules/checkout/composables/useCart/commands/removeItemCommand.ts
index 3801176..5f56694 100644
--- a/modules/checkout/composables/useCart/commands/removeItemCommand.ts
+++ b/modules/checkout/composables/useCart/commands/removeItemCommand.ts
@@ -1,11 +1,12 @@
import { Logger } from '~/helpers/logger';
import { Cart, RemoveItemFromCartInput } from '~/modules/GraphQL/types';
import { VsfContext } from '~/composables/context';
+import { CustomQuery } from '~/types/core';
export const removeItemCommand = {
execute: async (
context: VsfContext,
- { currentCart, product },
+ { currentCart, product, customQuery },
) => {
Logger.debug('[Magento]: Remove item from cart', {
product,
@@ -23,7 +24,7 @@ export const removeItemCommand = {
cart_item_uid: item.uid,
};
- const { data } = await context.$magento.api.removeItemFromCart(removeItemParams);
+ const { data } = await context.$magento.api.removeItemFromCart(removeItemParams, customQuery as CustomQuery);
Logger.debug('[Result]:', { data });
diff --git a/modules/checkout/composables/useCart/index.ts b/modules/checkout/composables/useCart/index.ts
index e7c16ee..c573572 100644
--- a/modules/checkout/composables/useCart/index.ts
+++ b/modules/checkout/composables/useCart/index.ts
@@ -3,7 +3,6 @@ import {
} from '@nuxtjs/composition-api';
import { addItemCommand } from '~/modules/checkout/composables/useCart/commands/addItemCommand';
import { applyCouponCommand } from '~/modules/checkout/composables/useCart/commands/applyCouponCommand';
-import { clearCartCommand } from '~/modules/checkout/composables/useCart/commands/clearCartCommand';
import { loadCartCommand } from '~/modules/checkout/composables/useCart/commands/loadCartCommand';
import { loadTotalQtyCommand } from '~/modules/checkout/composables/useCart/commands/loadTotalQtyCommand';
import { removeCouponCommand } from '~/modules/checkout/composables/useCart/commands/removeCouponCommand';
@@ -13,8 +12,9 @@ import { Logger } from '~/helpers/logger';
import { Cart, CartItemInterface, ProductInterface } from '~/modules/GraphQL/types';
import { useCartStore } from '~/modules/checkout/stores/cart';
import { useWishlist } from '~/modules/wishlist/composables/useWishlist';
-import { UseCartErrors, UseCartInterface } from './useCart';
import { Product } from '~/modules/catalog/product/types';
+import { ComposableFunctionArgs } from '~/composables';
+import { UseCartErrors, UseCartInterface } from './useCart';
/**
* Allows loading and manipulating cart of the current user.
@@ -90,7 +90,7 @@ PRODUCT
try {
loading.value = true;
- clearCartCommand.execute(context);
+ context.$magento.config.state.removeCartId();
const loadedCart = await loadCartCommand.execute(context, { customQuery });
cartStore.$patch((state) => {
@@ -104,12 +104,12 @@ PRODUCT
}
};
- const loadTotalQty = async (): Promise => {
+ const loadTotalQty = async (params?: ComposableFunctionArgs<{}>): Promise => {
Logger.debug('useCart.loadTotalQty');
try {
loading.value = true;
- const totalQuantity = await loadTotalQtyCommand.execute(context);
+ const totalQuantity = await loadTotalQtyCommand.execute(context, params);
cartStore.$patch((state) => {
state.cart.total_quantity = totalQuantity;
@@ -122,7 +122,9 @@ PRODUCT
}
};
- const addItem = async ({ product, quantity }): Promise => {
+ const addItem = async ({
+ product, quantity, productConfiguration, customQuery,
+ }): Promise => {
Logger.debug('useCart.addItem', { product, quantity });
try {
@@ -136,7 +138,10 @@ PRODUCT
currentCart: cart.value,
product,
quantity,
+ productConfiguration,
+ customQuery,
});
+
error.value.addItem = null;
cartStore.$patch((state) => {
state.cart = updatedCart;
@@ -155,7 +160,7 @@ PRODUCT
}
};
- const removeItem = async ({ product }) => {
+ const removeItem = async ({ product, customQuery }) => {
Logger.debug('useCart.removeItem', { product });
try {
@@ -163,6 +168,7 @@ PRODUCT
const updatedCart = await removeItemCommand.execute(context, {
currentCart: cart.value,
product,
+ customQuery,
});
error.value.removeItem = null;
diff --git a/modules/checkout/composables/useCart/useCart.ts b/modules/checkout/composables/useCart/useCart.ts
index 96073ff..8cd34e6 100644
--- a/modules/checkout/composables/useCart/useCart.ts
+++ b/modules/checkout/composables/useCart/useCart.ts
@@ -8,6 +8,7 @@ import { Product } from '~/modules/catalog/product/types';
export type UseCartAddItemParams = ComposableFunctionArgs<{
product: PRODUCT;
quantity: number;
+ productConfiguration?: { [key: string]: string };
}>;
/**
diff --git a/modules/checkout/composables/useGetShippingMethods/commands/getCustomerShippingMethodsCommand.ts b/modules/checkout/composables/useGetShippingMethods/commands/getCustomerShippingMethodsCommand.ts
index 30f32b2..a2ffa58 100644
--- a/modules/checkout/composables/useGetShippingMethods/commands/getCustomerShippingMethodsCommand.ts
+++ b/modules/checkout/composables/useGetShippingMethods/commands/getCustomerShippingMethodsCommand.ts
@@ -1,9 +1,12 @@
import { AvailableShippingMethod } from '~/modules/GraphQL/types';
import { VsfContext } from '~/composables/context';
+import { ComposableFunctionArgs } from '~/composables';
export const getCustomerShippingMethodsCommand = {
- execute: async (context: VsfContext): Promise => {
- const { data: { customerCart: { shipping_addresses: shippingAddresses } } } = await context.$magento.api.getAvailableCustomerShippingMethods();
+ execute: async (context: VsfContext, params: ComposableFunctionArgs<{}>): Promise => {
+ const {
+ data: { customerCart: { shipping_addresses: shippingAddresses } },
+ } = await context.$magento.api.getAvailableCustomerShippingMethods(params?.customQuery ?? null);
return shippingAddresses[0]?.available_shipping_methods ?? [];
},
};
diff --git a/modules/checkout/composables/useGetShippingMethods/commands/getGuestShippingMethodsCommand.ts b/modules/checkout/composables/useGetShippingMethods/commands/getGuestShippingMethodsCommand.ts
index 531158c..2212e2b 100644
--- a/modules/checkout/composables/useGetShippingMethods/commands/getGuestShippingMethodsCommand.ts
+++ b/modules/checkout/composables/useGetShippingMethods/commands/getGuestShippingMethodsCommand.ts
@@ -4,7 +4,7 @@ import { AvailableShippingMethod } from '~/modules/GraphQL/types';
export const getGuestShippingMethodsCommand = {
execute: async (context: Context['app'], params: ComposableFunctionArgs<{ cartId: string }>): Promise => {
- const { data } = await context.$vsf.$magento.api.getAvailableShippingMethods({ cartId: params.cartId });
+ const { data } = await context.$vsf.$magento.api.getAvailableShippingMethods({ cartId: params.cartId }, params?.customQuery ?? null);
const hasAddresses = data.cart.shipping_addresses.length > 0;
return hasAddresses ? data?.cart?.shipping_addresses[0]?.available_shipping_methods : [];
diff --git a/modules/checkout/composables/useGetShippingMethods/index.ts b/modules/checkout/composables/useGetShippingMethods/index.ts
index aa67991..545900c 100644
--- a/modules/checkout/composables/useGetShippingMethods/index.ts
+++ b/modules/checkout/composables/useGetShippingMethods/index.ts
@@ -30,7 +30,7 @@ export function useGetShippingMethods(): UseGetShippingMethodsInterface => {
- const { data } = await context.app.$vsf.$magento.api.placeOrder({ cart_id: cartId });
+ execute: async (context: UseContextReturn, cartId: string, params?: ComposableFunctionArgs<{}>): Promise => {
+ const { data } = await context.app.$vsf.$magento.api.placeOrder({ cart_id: cartId }, params?.customQuery ?? null);
return data?.placeOrder ?? null;
},
diff --git a/modules/checkout/composables/useMakeOrder/index.ts b/modules/checkout/composables/useMakeOrder/index.ts
index 687cc05..3ec8b7a 100644
--- a/modules/checkout/composables/useMakeOrder/index.ts
+++ b/modules/checkout/composables/useMakeOrder/index.ts
@@ -4,6 +4,7 @@ import { placeOrderCommand } from '~/modules/checkout/composables/useMakeOrder/c
import useCart from '~/modules/checkout/composables/useCart';
import type { PlaceOrderOutput } from '~/modules/GraphQL/types';
import type { UseMakeOrderErrors, UseMakeOrderInterface } from './useMakeOrder';
+import { ComposableFunctionArgs } from '~/composables';
/**
* Allows making an order from a cart.
@@ -16,12 +17,12 @@ export function useMakeOrder(): UseMakeOrderInterface {
const { cart } = useCart();
const context = useContext();
- const make = async (): Promise => {
+ const make = async (params?: ComposableFunctionArgs<{}>): Promise => {
Logger.debug('useMakeOrder.make');
let placedOrder = null;
try {
loading.value = true;
- placedOrder = await placeOrderCommand.execute(context, cart.value.id);
+ placedOrder = await placeOrderCommand.execute(context, cart.value.id, params?.customQuery ?? null);
error.value.make = null;
} catch (err) {
error.value.make = err;
diff --git a/modules/checkout/composables/useMakeOrder/useMakeOrder.ts b/modules/checkout/composables/useMakeOrder/useMakeOrder.ts
index 32ff0dd..b2812fc 100644
--- a/modules/checkout/composables/useMakeOrder/useMakeOrder.ts
+++ b/modules/checkout/composables/useMakeOrder/useMakeOrder.ts
@@ -1,5 +1,6 @@
import type { Ref } from '@nuxtjs/composition-api';
import type { PlaceOrderOutput } from '~/modules/GraphQL/types';
+import { ComposableFunctionArgs } from '~/composables';
/**
* The {@link useMakeOrder} error object. The properties values' are the errors
@@ -15,7 +16,7 @@ export interface UseMakeOrderErrors {
*/
export interface UseMakeOrderInterface {
/** Makes an order with current cart. */
- make(): Promise;
+ make(params?: ComposableFunctionArgs<{}>): Promise;
/**
* Contains errors from any of the composable methods.
diff --git a/modules/checkout/composables/usePaymentProvider/commands/getAvailablePaymentMethodsCommand.ts b/modules/checkout/composables/usePaymentProvider/commands/getAvailablePaymentMethodsCommand.ts
index db6714b..fb46c47 100644
--- a/modules/checkout/composables/usePaymentProvider/commands/getAvailablePaymentMethodsCommand.ts
+++ b/modules/checkout/composables/usePaymentProvider/commands/getAvailablePaymentMethodsCommand.ts
@@ -1,9 +1,9 @@
-import { UseContextReturn } from '~/types/core';
+import { CustomQuery, UseContextReturn } from '~/types/core';
import type { AvailablePaymentMethod } from '~/modules/GraphQL/types';
export const getAvailablePaymentMethodsCommand = {
- execute: async (context: UseContextReturn, cartId: string): Promise => {
- const { data } = await context.app.$vsf.$magento.api.getAvailablePaymentMethods({ cartId });
+ execute: async (context: UseContextReturn, cartId: string, customQuery?: CustomQuery): Promise => {
+ const { data } = await context.app.$vsf.$magento.api.getAvailablePaymentMethods({ cartId }, customQuery);
return data?.cart?.available_payment_methods ?? [];
},
diff --git a/modules/checkout/composables/usePaymentProvider/commands/setPaymentMethodOnCartCommand.ts b/modules/checkout/composables/usePaymentProvider/commands/setPaymentMethodOnCartCommand.ts
index 09fa903..b3c0e45 100644
--- a/modules/checkout/composables/usePaymentProvider/commands/setPaymentMethodOnCartCommand.ts
+++ b/modules/checkout/composables/usePaymentProvider/commands/setPaymentMethodOnCartCommand.ts
@@ -4,7 +4,7 @@ import type { PaymentMethodParams } from '../usePaymentProvider';
export const setPaymentMethodOnCartCommand = {
execute: async (context: UseContextReturn, params: PaymentMethodParams): Promise => {
- const { data } = await context.app.$vsf.$magento.api.setPaymentMethodOnCart(params);
+ const { data } = await context.app.$vsf.$magento.api.setPaymentMethodOnCart(params, params?.customQuery ?? null);
return data?.setPaymentMethodOnCart?.cart.available_payment_methods ?? [];
},
diff --git a/modules/checkout/composables/usePaymentProvider/index.ts b/modules/checkout/composables/usePaymentProvider/index.ts
index f554dc8..1e5a184 100644
--- a/modules/checkout/composables/usePaymentProvider/index.ts
+++ b/modules/checkout/composables/usePaymentProvider/index.ts
@@ -10,6 +10,7 @@ import type {
UsePaymentProviderSaveParams,
PaymentMethodParams,
} from './usePaymentProvider';
+import { CustomQuery } from '~/types/core';
/**
* Allows loading the available payment
@@ -37,6 +38,7 @@ export function usePaymentProvider(): UsePaymentProviderInterface {
payment_method: {
...params.paymentMethod,
},
+ customQuery: params.customQuery,
};
result = await setPaymentMethodOnCartCommand.execute(context, paymentMethodParams);
@@ -52,13 +54,13 @@ export function usePaymentProvider(): UsePaymentProviderInterface {
return result;
};
- const load = async () => {
+ const load = async (customQuery?: CustomQuery) => {
Logger.debug('usePaymentProvider.load');
let result = null;
try {
loading.value = true;
- result = await getAvailablePaymentMethodsCommand.execute(context, cart.value.id);
+ result = await getAvailablePaymentMethodsCommand.execute(context, cart.value.id, customQuery);
error.value.load = null;
} catch (err) {
error.value.load = err;
diff --git a/modules/checkout/composables/usePaymentProvider/usePaymentProvider.ts b/modules/checkout/composables/usePaymentProvider/usePaymentProvider.ts
index 6dd9720..1987a8f 100644
--- a/modules/checkout/composables/usePaymentProvider/usePaymentProvider.ts
+++ b/modules/checkout/composables/usePaymentProvider/usePaymentProvider.ts
@@ -1,11 +1,12 @@
import type { Ref } from '@nuxtjs/composition-api';
import type { ComposableFunctionArgs } from '~/composables/types';
import type { AvailablePaymentMethod, PaymentMethodInput } from '~/modules/GraphQL/types';
+import { CustomQuery } from '~/types/core';
-export interface PaymentMethodParams {
+export type PaymentMethodParams = ComposableFunctionArgs<{
cart_id: string;
payment_method: PaymentMethodInput;
-}
+}>;
/**
* The {@link usePaymentProvider} error object. The properties values' are the
@@ -39,7 +40,7 @@ export interface UsePaymentProviderInterface {
error: Readonly[>;
/** Loads the available payment methods for current cart. */
- load(): Promise;
+ load(customQuery?: CustomQuery): Promise;
/**
* Saves the payment method for current cart. It returns the updated available
diff --git a/modules/checkout/composables/useShippingProvider/commands/setShippingMethodsOnCartCommand.ts b/modules/checkout/composables/useShippingProvider/commands/setShippingMethodsOnCartCommand.ts
index c4c6527..471b539 100644
--- a/modules/checkout/composables/useShippingProvider/commands/setShippingMethodsOnCartCommand.ts
+++ b/modules/checkout/composables/useShippingProvider/commands/setShippingMethodsOnCartCommand.ts
@@ -1,9 +1,9 @@
-import { UseContextReturn } from '~/types/core';
+import { CustomQuery, UseContextReturn } from '~/types/core';
import type { SetShippingMethodsOnCartInput, Cart } from '~/modules/GraphQL/types';
export const setShippingMethodsOnCartCommand = {
- execute: async (context: UseContextReturn, shippingMethodParams: SetShippingMethodsOnCartInput): Promise => {
- const { data } = await context.app.$vsf.$magento.api.setShippingMethodsOnCart(shippingMethodParams);
+ execute: async (context: UseContextReturn, shippingMethodParams: SetShippingMethodsOnCartInput, customQuery: CustomQuery): Promise => {
+ const { data } = await context.app.$vsf.$magento.api.setShippingMethodsOnCart(shippingMethodParams, customQuery);
// TODO: Find out why 'Cart' doesn't match the type of the response data.
return (data?.setShippingMethodsOnCart?.cart as unknown as Cart) ?? null;
diff --git a/modules/checkout/composables/useShippingProvider/index.ts b/modules/checkout/composables/useShippingProvider/index.ts
index 628113a..80e7932 100644
--- a/modules/checkout/composables/useShippingProvider/index.ts
+++ b/modules/checkout/composables/useShippingProvider/index.ts
@@ -26,7 +26,7 @@ export function useShippingProvider(): UseShippingProviderInterface {
const { cart, setCart, load: loadCart } = useCart();
const context = useContext();
- const save = async ({ shippingMethod }: UseShippingProviderSaveParams) => {
+ const save = async ({ shippingMethod, customQuery = null }: UseShippingProviderSaveParams) => {
Logger.debug('useShippingProvider.save');
let result = null;
try {
@@ -37,7 +37,7 @@ export function useShippingProvider(): UseShippingProviderInterface {
shipping_methods: [shippingMethod],
};
- const cartData = await setShippingMethodsOnCartCommand.execute(context, shippingMethodParams);
+ const cartData = await setShippingMethodsOnCartCommand.execute(context, shippingMethodParams, customQuery);
Logger.debug('[Result]:', { cartData });
setCart(cartData);
diff --git a/modules/checkout/getters/cartGetters.ts b/modules/checkout/getters/cartGetters.ts
index 0b03027..8819800 100644
--- a/modules/checkout/getters/cartGetters.ts
+++ b/modules/checkout/getters/cartGetters.ts
@@ -5,12 +5,12 @@ import type { Price } from '~/modules/catalog/types';
import type { ProductAttribute, Product } from '~/modules/catalog/product/types';
import { PaymentMethodInterface } from '~/modules/checkout/types';
import {
- CartItemInterface,
Cart,
Discount,
ProductInterface,
SelectedShippingMethod,
ConfigurableCartItem,
+ CartItemInterface,
} from '~/modules/GraphQL/types';
import { CartGetters as CartGettersBase, CartDiscount, Coupon } from '~/getters/types';
import { getName, getSlug as getSlugGetter, getProductThumbnailImage } from '~/modules/catalog/product/getters/productGetters';
diff --git a/modules/checkout/pages/Checkout.vue b/modules/checkout/pages/Checkout.vue
index 5b14e5b..4c2000d 100644
--- a/modules/checkout/pages/Checkout.vue
+++ b/modules/checkout/pages/Checkout.vue
@@ -44,7 +44,7 @@ import {
} from '@nuxtjs/composition-api';
import cartGetters from '~/modules/checkout/getters/cartGetters';
import useCart from '~/modules/checkout/composables/useCart';
-import CartPreview from '~/components/Checkout/CartPreview.vue';
+import CartPreview from '~/modules/checkout/components/CartPreview.vue';
export default defineComponent({
name: 'CheckoutPage',
diff --git a/modules/checkout/pages/Checkout/Billing.vue b/modules/checkout/pages/Checkout/Billing.vue
index 98d3626..0135227 100644
--- a/modules/checkout/pages/Checkout/Billing.vue
+++ b/modules/checkout/pages/Checkout/Billing.vue
@@ -13,7 +13,7 @@
:label="$t('My billing and shipping address are the same')"
name="copyShippingAddress"
class="form__element"
- @change="handleCheckSameAddress"
+ @change="handleCheckSameAddress($event)"
/>
+ >
+
+ {{ shippingDetailsCountryName }}
+
+
]
@@ -34,12 +38,13 @@
v-if="!sameAsShipping && isAuthenticated && hasSavedBillingAddress"
v-model="setAsDefault"
v-e2e="'billing-addresses'"
- :current-address-id="currentAddressId || NOT_SELECTED_ADDRESS"
+ :current-address-id="currentAddressId"
:billing-addresses="addresses"
- @setCurrentAddress="handleSetCurrentAddress"
+ :countries="countries"
+ @setCurrentAddress="handleSetCurrentAddress($event)"
/>
changeBillingDetails('firstname', firstname)"
+ @input="changeBillingDetails('firstname', $event)"
/>
changeBillingDetails('lastname', lastname)"
+ @input="changeBillingDetails('lastname', $event)"
/>
changeBillingDetails('street', street)"
+ @input="changeBillingDetails('street', $event)"
/>
changeBillingDetails('apartment', apartment)"
+ @input="changeBillingDetails('apartment', $event)"
/>
changeBillingDetails('city', city)"
+ @input="changeBillingDetails('city', $event)"
/>
changeBillingDetails('region', region)"
+ @input="changeBillingDetails('region', $event)"
/>
changeBillingDetails('region', state)"
+ @input="changeBillingDetails('region', $event)"
>
+
+
changeBillingDetails('postcode', postcode)"
+ @input="changeBillingDetails('postcode', $event)"
/>
changeBillingDetails('telephone', telephone)"
+ @input="changeBillingDetails('telephone', $event)"
/>
import('~/components/Checkout/UserBillingAddresses.vue'),
+ UserBillingAddresses: () => import('~/modules/checkout/components/UserBillingAddresses.vue'),
UserAddressDetails,
},
setup() {
const router = useRouter();
const { app } = useContext();
const shippingDetails = ref(null);
- const billingAddress = ref(null);
const userBilling = ref(null);
const {
@@ -361,24 +364,25 @@ export default defineComponent({
const countries = ref([]);
const country = ref(null);
+
+ const shippingDetailsCountryName = computed(() => countries
+ .value
+ .find((countryItem) => countryItem.id === shippingDetails.value?.country.code)?.full_name_locale ?? '');
+
const { isAuthenticated } = useUser();
let oldBilling : CheckoutAddressForm | null = null;
const sameAsShipping = ref(false);
- const billingDetails = ref(
- billingAddress.value
- ? addressFromApiToForm(billingAddress.value)
- : getInitialCheckoutAddressForm(),
- );
- const currentAddressId = ref(NOT_SELECTED_ADDRESS);
+ const billingDetails = ref(getInitialCheckoutAddressForm());
+
+ const currentAddressId = ref(null);
const setAsDefault = ref(false);
const isFormSubmitted = ref(false);
- const canAddNewAddress = ref(true);
+ const isAddNewAddressFormVisible = ref(true);
const isBillingDetailsStepCompleted = ref(false);
const addresses = computed(() => (userBilling.value ? userBillingGetters.getAddresses(userBilling.value) : []));
const canMoveForward = computed(() => !loading.value && billingDetails.value && Object.keys(
- // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
billingDetails.value,
).length > 0);
@@ -389,7 +393,6 @@ export default defineComponent({
return addresses.value.length > 0;
});
- // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
const countriesList = computed(() => addressGetter.countriesList(countries.value));
const regionInformation = computed(() => addressGetter.regionList(country.value));
@@ -398,12 +401,12 @@ export default defineComponent({
const billingDetailsData = {
billingDetails: {
...billingDetails.value,
- customerAddressId: addressId,
+ customerAddressId: addressId === null ? null : String(addressId),
sameAsShipping: sameAsShipping.value,
},
};
await save(billingDetailsData);
- if (addressId !== NOT_SELECTED_ADDRESS && setAsDefault.value) {
+ if (addressId !== null && setAsDefault.value) {
const [chosenAddress] = userBillingGetters.getAddresses(
userBilling.value,
{ id: addressId },
@@ -416,20 +419,20 @@ export default defineComponent({
}
reset();
await mergeItem('checkout', { billing: billingDetailsData });
- await router.push(`${app.localePath('/checkout/payment')}`);
+ await router.push(app.localeRoute({ name: 'payment' }));
isBillingDetailsStepCompleted.value = true;
};
- const handleCheckSameAddress = async () => {
- sameAsShipping.value = !sameAsShipping.value;
- if (sameAsShipping.value) {
+ const handleCheckSameAddress = async (value: boolean) => {
+ sameAsShipping.value = value;
+ if (value) {
shippingDetails.value = await loadShipping();
- country.value = await searchCountry({ id: (shippingDetails.value as ShippingCartAddress).country.code });
+ country.value = await searchCountry({ id: (shippingDetails.value).country.code });
oldBilling = { ...billingDetails.value };
billingDetails.value = {
...formatAddressReturnToData(shippingDetails.value),
};
- currentAddressId.value = NOT_SELECTED_ADDRESS;
+ currentAddressId.value = null;
setAsDefault.value = false;
if (billingDetails.value.country_code) {
country.value = await searchCountry({ id: billingDetails?.value.country_code });
@@ -443,85 +446,74 @@ export default defineComponent({
};
const handleAddNewAddressBtnClick = () => {
- currentAddressId.value = NOT_SELECTED_ADDRESS;
+ currentAddressId.value = null;
billingDetails.value = getInitialCheckoutAddressForm();
- canAddNewAddress.value = true;
+ isAddNewAddressFormVisible.value = true;
isBillingDetailsStepCompleted.value = false;
};
- const handleSetCurrentAddress = (addr: CustomerAddress) => {
- billingDetails.value = { ...addressFromApiToForm(addr) };
- currentAddressId.value = String(addr?.id);
- canAddNewAddress.value = false;
+ const handleSetCurrentAddress = async (customerAddress: CustomerAddress) => {
+ const id = customerAddress?.id;
+ currentAddressId.value = id;
+ if (id) {
+ isAddNewAddressFormVisible.value = false;
+ }
+ billingDetails.value = addressFromApiToForm(customerAddress);
+ country.value = customerAddress.country_code ? await searchCountry({ id: customerAddress.country_code }) : null;
isBillingDetailsStepCompleted.value = false;
};
- const changeBillingDetails = (field: string, value: unknown) => {
- billingDetails.value = {
- ...billingDetails.value,
- [field]: value,
- };
+ const changeBillingDetails = (field: keyof CheckoutAddressForm, value: string) => {
+ billingDetails.value[field] = value;
+ currentAddressId.value = null;
isBillingDetailsStepCompleted.value = false;
- currentAddressId.value = NOT_SELECTED_ADDRESS;
- };
-
- const selectDefaultAddress = () => {
- const defaultAddress = userBillingGetters.getAddresses(
- userBilling.value,
- { default_billing: true },
- );
- if (defaultAddress && defaultAddress.length > 0) {
- handleSetCurrentAddress(defaultAddress[0]);
- }
};
const changeCountry = async (id: string) => {
changeBillingDetails('country_code', id);
- country.value = await searchCountry({ id });
+ const newCountry = await searchCountry({ id });
+ billingDetails.value.region = '';
+ country.value = newCountry;
};
- watch(billingAddress, (addr) => {
- billingDetails.value = addr ? addressFromApiToForm(addr) : getInitialCheckoutAddressForm();
- });
-
onMounted(async () => {
- const validStep = await isPreviousStepValid('shipping');
+ const validStep = await isPreviousStepValid('user-account');
if (!validStep) {
- await router.push(app.localePath('/checkout/user-account'));
+ await router.push(app.localeRoute({ name: 'user-account' }));
}
+ const [loadedBillingInfoBoundToCart, loadedUserBilling, loadedCountries] = await Promise.all([
+ loadBilling(),
+ loadUserBilling(),
+ loadCountries(),
+ ]);
+ const [defaultAddress = null] = userBillingGetters.getAddresses(loadedUserBilling, { default_shipping: true });
+ const wasBillingAddressAlreadySetOnCart = Boolean(loadedBillingInfoBoundToCart);
+
+ // keep in mind default billing address is set on a customer's cart during cart creation
+ if (wasBillingAddressAlreadySetOnCart) {
+ const userAddressIdenticalToSavedCartAddress = findUserAddressIdenticalToSavedCartAddress(
+ loadedUserBilling?.addresses,
+ loadedBillingInfoBoundToCart,
+ );
- const [loadedCountries, loadedBilling] = await Promise.all([loadCountries(), loadBilling()]);
- countries.value = loadedCountries;
- billingAddress.value = loadedBilling;
-
+ handleSetCurrentAddress({ ...loadedBillingInfoBoundToCart, id: userAddressIdenticalToSavedCartAddress?.id });
+ } else if (defaultAddress) {
+ handleSetCurrentAddress(defaultAddress);
+ }
if (billingDetails.value?.country_code) {
country.value = await searchCountry({ id: billingDetails.value.country_code });
}
-
- if (!(userBilling.value as Customer)?.addresses && isAuthenticated.value) {
- userBilling.value = await loadUserBilling();
- }
- const billingAddresses = userBilling.value ? userBillingGetters.getAddresses(userBilling.value) : [];
-
- if (!billingAddresses || billingAddresses.length === 0) {
- return;
- }
-
- // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
- const hasEmptyBillingDetails = !billingDetails.value || Object.keys(billingDetails.value).length === 0;
- if (hasEmptyBillingDetails) {
- selectDefaultAddress();
- return;
- }
- canAddNewAddress.value = false;
+ userBilling.value = loadedUserBilling;
+ countries.value = loadedCountries;
});
return {
- canAddNewAddress,
+ isAddNewAddressFormVisible,
canMoveForward,
changeCountry,
changeBillingDetails,
countriesList,
+ countries,
country,
currentAddressId,
handleAddNewAddressBtnClick,
@@ -533,12 +525,12 @@ export default defineComponent({
isFormSubmitted,
isBillingDetailsStepCompleted,
loading,
- NOT_SELECTED_ADDRESS,
regionInformation,
searchCountry,
setAsDefault,
billingDetails,
sameAsShipping,
+ shippingDetailsCountryName,
addresses,
};
},
diff --git a/modules/checkout/pages/Checkout/Payment.vue b/modules/checkout/pages/Checkout/Payment.vue
index fe7da42..6bb896f 100644
--- a/modules/checkout/pages/Checkout/Payment.vue
+++ b/modules/checkout/pages/Checkout/Payment.vue
@@ -68,10 +68,7 @@
@@ -178,6 +175,7 @@ import {
useContext,
onMounted,
} from '@nuxtjs/composition-api';
+
import cartGetters from '~/modules/checkout/getters/cartGetters';
import { useImage } from '~/composables';
import useMakeOrder from '~/modules/checkout/composables/useMakeOrder';
@@ -185,7 +183,7 @@ import useCart from '~/modules/checkout/composables/useCart';
import getShippingMethodPrice from '~/helpers/checkout/getShippingMethodPrice';
import { removeItem } from '~/helpers/asyncLocalStorage';
import { isPreviousStepValid } from '~/helpers/checkout/steps';
-import type { BundleCartItem, ConfigurableCartItem } from '~/modules/GraphQL/types';
+import type { BundleCartItem, ConfigurableCartItem, CartItemInterface } from '~/modules/GraphQL/types';
export default defineComponent({
name: 'ReviewOrderAndPayment',
@@ -199,7 +197,7 @@ export default defineComponent({
SfProperty,
SfLink,
SfImage,
- VsfPaymentProvider: () => import('~/components/Checkout/VsfPaymentProvider.vue'),
+ VsfPaymentProvider: () => import('~/modules/checkout/components/VsfPaymentProvider.vue'),
},
setup() {
const order = ref(null);
@@ -243,7 +241,7 @@ export default defineComponent({
);
const { getMagentoImage, imageSizes } = useImage();
-
+ const getRowTotal = (product: CartItemInterface) => cartGetters.getItemPrice(product).regular - cartGetters.getItemPrice(product).special;
return {
cart,
cartGetters,
@@ -263,6 +261,7 @@ export default defineComponent({
getBundles,
getMagentoImage,
imageSizes,
+ getRowTotal,
};
},
});
diff --git a/modules/checkout/pages/Checkout/Shipping.vue b/modules/checkout/pages/Checkout/Shipping.vue
index 8ee8445..c0908df 100644
--- a/modules/checkout/pages/Checkout/Shipping.vue
+++ b/modules/checkout/pages/Checkout/Shipping.vue
@@ -3,21 +3,22 @@
@@ -264,7 +261,6 @@ import {
import {
ref,
computed,
- watch,
onMounted,
defineComponent,
useRouter,
@@ -274,21 +270,19 @@ import { required, min, digits } from 'vee-validate/dist/rules';
import { ValidationProvider, ValidationObserver, extend } from 'vee-validate';
import userShippingGetters from '~/modules/customer/getters/userShippingGetters';
import addressGetter from '~/modules/customer/getters/addressGetter';
-import {
- useCountrySearch,
-} from '~/composables';
+import { useCountrySearch } from '~/composables';
import type {
- Country, AvailableShippingMethod, ShippingCartAddress, CustomerAddress, Customer,
+ Country, AvailableShippingMethod, CustomerAddress, Customer,
} from '~/modules/GraphQL/types';
import useShipping from '~/modules/checkout/composables/useShipping';
import useUser from '~/modules/customer/composables/useUser';
import useUserAddress from '~/modules/customer/composables/useUserAddress';
-import { addressFromApiToForm, CheckoutAddressForm, getInitialCheckoutAddressForm } from '~/helpers/checkout/address';
+import {
+ addressFromApiToForm, CheckoutAddressForm, findUserAddressIdenticalToSavedCartAddress, getInitialCheckoutAddressForm,
+} from '~/helpers/checkout/address';
import { mergeItem } from '~/helpers/asyncLocalStorage';
import { isPreviousStepValid } from '~/helpers/checkout/steps';
-const NOT_SELECTED_ADDRESS = '';
-
extend('required', {
...required,
message: 'This field is required',
@@ -311,13 +305,12 @@ export default defineComponent({
SfSelect,
ValidationProvider,
ValidationObserver,
- UserShippingAddresses: () => import('~/components/Checkout/UserShippingAddresses.vue'),
- VsfShippingProvider: () => import('~/components/Checkout/VsfShippingProvider.vue'),
+ UserShippingAddresses: () => import('~/modules/checkout/components/UserShippingAddresses.vue'),
+ VsfShippingProvider: () => import('~/modules/checkout/components/VsfShippingProvider.vue'),
},
setup() {
const router = useRouter();
const { app } = useContext();
- const address = ref(null);
const userShipping = ref(null);
const {
load: loadShipping,
@@ -336,13 +329,13 @@ export default defineComponent({
const countries = ref([]);
const country = ref(null);
const { isAuthenticated } = useUser();
- const shippingDetails = ref(address.value ? addressFromApiToForm(address.value) : getInitialCheckoutAddressForm());
+ const shippingDetails = ref(getInitialCheckoutAddressForm());
const shippingMethods = ref([]);
- const currentAddressId = ref(NOT_SELECTED_ADDRESS);
+ const currentAddressId = ref(null);
- const setAsDefault = ref(false);
+ const isSetAsDefaultRequested = ref(false);
const isFormSubmitted = ref(false);
- const canAddNewAddress = ref(true);
+ const isAddNewAddressFormVisible = ref(true);
const isShippingDetailsStepCompleted = ref(false);
const addresses = computed(() => userShippingGetters.getAddresses(userShipping.value));
@@ -373,12 +366,12 @@ export default defineComponent({
const shippingInfo = await saveShipping({ shippingDetails: shippingDetailsData });
shippingMethods.value = shippingInfo?.available_shipping_methods ?? [];
- if (addressId !== NOT_SELECTED_ADDRESS && setAsDefault.value) {
+ if (addressId !== null && isSetAsDefaultRequested.value) {
const [chosenAddress] = userShippingGetters.getAddresses(
userShipping.value,
{ id: addressId },
);
- chosenAddress.default_shipping = setAsDefault.value;
+ chosenAddress.default_shipping = isSetAsDefaultRequested.value;
if (chosenAddress) {
await setDefaultAddress({ address: chosenAddress });
userShipping.value = await loadUserShipping(true);
@@ -389,85 +382,72 @@ export default defineComponent({
};
const handleAddNewAddressBtnClick = () => {
- currentAddressId.value = NOT_SELECTED_ADDRESS;
+ currentAddressId.value = null;
shippingDetails.value = getInitialCheckoutAddressForm();
- canAddNewAddress.value = true;
+ isAddNewAddressFormVisible.value = true;
isShippingDetailsStepCompleted.value = false;
};
- const handleSetCurrentAddress = (addr: CustomerAddress) => {
- shippingDetails.value = { ...addressFromApiToForm(addr) };
- currentAddressId.value = String(addr?.id);
- canAddNewAddress.value = false;
+ const handleSetCurrentAddress = async (customerAddress: CustomerAddress) => {
+ const id = customerAddress?.id;
+ currentAddressId.value = id;
+ if (id) {
+ isAddNewAddressFormVisible.value = false;
+ }
+ shippingDetails.value = addressFromApiToForm(customerAddress);
+ country.value = customerAddress.country_code ? await searchCountry({ id: customerAddress.country_code }) : null;
isShippingDetailsStepCompleted.value = false;
};
- const changeShippingDetails = (field: string, value: unknown) => {
- shippingDetails.value = {
- ...shippingDetails.value,
- [field]: value,
- };
+ const changeShippingDetails = (field: keyof CheckoutAddressForm, value: string) => {
+ shippingDetails.value[field] = value;
isShippingDetailsStepCompleted.value = false;
- currentAddressId.value = NOT_SELECTED_ADDRESS;
- };
-
- const selectDefaultAddress = () => {
- const defaultAddress = userShippingGetters.getAddresses(
- userShipping.value,
- { default_shipping: true },
- ) as [CustomerAddress] | [];
- if (defaultAddress && defaultAddress.length > 0) {
- handleSetCurrentAddress(defaultAddress[0]);
- }
+ currentAddressId.value = null;
};
const changeCountry = async (id: string) => {
changeShippingDetails('country_code', id);
- country.value = await searchCountry({ id });
+ const newCountry = await searchCountry({ id });
+ shippingDetails.value.region = '';
+ country.value = newCountry;
};
- watch(address, (addr) => {
- shippingDetails.value = addr ? addressFromApiToForm(addr) : getInitialCheckoutAddressForm();
- });
onMounted(async () => {
const validStep = await isPreviousStepValid('user-account');
if (!validStep) {
- await router.push(app.localePath('/checkout/user-account'));
+ await router.push(app.localeRoute({ name: 'user-account' }));
}
- const [loadedShippingInfo, loadedUserShipping, loadedCountries] = await Promise.all([
+ const [loadedShippingInfoBoundToCart, loadedUserShipping, loadedCountries] = await Promise.all([
loadShipping(),
loadUserShipping(),
loadCountries(),
]);
- address.value = loadedShippingInfo;
- userShipping.value = loadedUserShipping;
- countries.value = loadedCountries;
+ const [defaultAddress = null] = userShippingGetters.getAddresses(loadedUserShipping, { default_shipping: true });
+ const wasShippingAddressAlreadySetOnCart = Boolean(loadedShippingInfoBoundToCart);
+ if (wasShippingAddressAlreadySetOnCart) {
+ const userAddressIdenticalToSavedCartAddress = findUserAddressIdenticalToSavedCartAddress(
+ loadedUserShipping?.addresses,
+ loadedShippingInfoBoundToCart,
+ );
+ handleSetCurrentAddress({ ...loadedShippingInfoBoundToCart, id: userAddressIdenticalToSavedCartAddress?.id });
+ } else if (defaultAddress) {
+ handleSetCurrentAddress(defaultAddress);
+ }
if (shippingDetails.value?.country_code) {
country.value = await searchCountry({ id: shippingDetails.value.country_code });
}
-
- const shippingAddresses = userShippingGetters.getAddresses(
- userShipping.value,
- );
-
- if (!shippingAddresses || shippingAddresses.length === 0) {
- return;
- }
-
- const hasEmptyShippingDetails = !shippingDetails.value
- || Object.keys(shippingDetails.value).length === 0;
- if (hasEmptyShippingDetails) {
- selectDefaultAddress();
- }
+ userShipping.value = loadedUserShipping;
+ countries.value = loadedCountries;
});
return {
- canAddNewAddress,
+ isAddNewAddressFormVisible,
canMoveForward,
changeCountry,
changeShippingDetails,
+ countries,
countriesList,
country,
currentAddressId,
@@ -479,10 +459,9 @@ export default defineComponent({
isFormSubmitted,
isShippingDetailsStepCompleted,
isShippingLoading,
- NOT_SELECTED_ADDRESS,
regionInformation,
searchCountry,
- setAsDefault,
+ isSetAsDefaultRequested,
shippingDetails,
shippingMethods,
addresses,
diff --git a/modules/checkout/pages/Checkout/UserAccount.vue b/modules/checkout/pages/Checkout/UserAccount.vue
index 0ebc0d8..a6939fe 100644
--- a/modules/checkout/pages/Checkout/UserAccount.vue
+++ b/modules/checkout/pages/Checkout/UserAccount.vue
@@ -147,9 +147,7 @@ import {
required, min, email,
} from 'vee-validate/dist/rules';
import { ValidationProvider, ValidationObserver, extend } from 'vee-validate';
-import {
- useUiNotification, useGuestUser,
-} from '~/composables';
+import { useGuestUser } from '~/composables';
import useCart from '~/modules/checkout/composables/useCart';
import { useUser } from '~/modules/customer/composables/useUser';
import { getItem, mergeItem } from '~/helpers/asyncLocalStorage';
@@ -208,15 +206,13 @@ export default defineComponent({
error: errorUser,
} = useUser();
- const { send: sendNotification } = useUiNotification();
-
const isFormSubmitted = ref(false);
const createUserAccount = ref(false);
const loginUserAccount = ref(false);
const loading = computed(() => loadingUser.value || loadingGuestUser.value);
const canMoveForward = computed(() => !(loading.value));
- const hasError = computed(() => errorUser.value.register || errorGuestUser.value.attachToCart);
+ const anyError = computed(() => errorUser.value.login || errorUser.value.register || errorGuestUser.value.attachToCart);
type Form = {
firstname: string,
@@ -249,9 +245,9 @@ export default defineComponent({
}
await (
- !createUserAccount.value
- ? attachToCart({ email: form.value.email, cart })
- : register({ user: form.value })
+ createUserAccount.value
+ ? register({ user: form.value })
+ : attachToCart({ email: form.value.email, cart })
);
}
@@ -270,24 +266,14 @@ export default defineComponent({
});
}
- if (!hasError.value) {
+ if (!anyError.value) {
await mergeItem('checkout', { 'user-account': form.value });
- await router.push(`${app.localePath('/checkout/shipping')}`);
+ await router.push(app.localeRoute({ name: 'shipping' }));
reset();
isFormSubmitted.value = true;
- } else {
- sendNotification({
- id: Symbol('user_form_error'),
- message: 'Something went wrong during form submission. Please try again later',
- type: 'danger',
- icon: 'error',
- persist: false,
- title: 'Error',
- });
}
if (isRecaptchaEnabled.value) {
- // reset recaptcha
$recaptcha.reset();
}
};
@@ -332,7 +318,6 @@ export default defineComponent({