Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: reject empty checkout #770

Merged
merged 6 commits into from
Oct 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions apps/web/composables/useCart/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ export interface UseCart {
setCart: SetCart;
clearCartItems: ClearCartItems;
lastUpdatedCartItem: Readonly<Ref<UseCartState['lastUpdatedCartItem']>>;
cartIsEmpty: ComputedRef<boolean>;
}

export type UseCartReturn = () => UseCart;
3 changes: 3 additions & 0 deletions apps/web/composables/useCart/useCart.ts
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,8 @@ export const useCart: UseCartReturn = () => {
}
};

const cartIsEmpty = computed(() => !state.value.data?.items?.length);

return {
setCart,
clearCartItems,
Expand All @@ -251,6 +253,7 @@ export const useCart: UseCartReturn = () => {
addItemsToCart,
deleteCartItem,
getCart,
cartIsEmpty,
...toRefs(state.value),
};
};
3 changes: 2 additions & 1 deletion apps/web/composables/useCheckout/useCheckout.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export const useCheckout = (cacheKey = '') => {
init: false,
}));

const { data: cart, getCart, clearCartItems, loading: cartLoading } = useCart();
const { data: cart, cartIsEmpty, getCart, clearCartItems, loading: cartLoading } = useCart();
const { checkboxValue: termsAccepted, setShowErrors } = useAgreementCheckbox('checkoutGeneralTerms');
const { shippingAsBilling } = useShippingAsBilling();
const { addresses: shippingAddresses, get: getShipping } = useAddressStore(AddressType.Shipping);
Expand Down Expand Up @@ -105,6 +105,7 @@ export const useCheckout = (cacheKey = '') => {
return {
...toRefs(state.value),
cart,
cartIsEmpty,
getCart,
clearCartItems,
cartLoading,
Expand Down
1 change: 1 addition & 0 deletions apps/web/lang/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -418,6 +418,7 @@
"editAddress": "Adresse bearbeiten",
"email": "E-Mail",
"emptyCart": "Ihr Warenkorb ist leer",
"emptyCartNotification": "Sie haben keine Artikel in Ihrem Warenkorb",
"emptyCartImgAlt": "Bild eines leeren Warenkorbs",
"emptyStateAltText": "Bild, das beschreibt, dass keine Produkte gefunden wurden",
"emptyStateText": "Wir haben keine Produkte mehr in dieser Kategorie.",
Expand Down
1 change: 1 addition & 0 deletions apps/web/lang/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -418,6 +418,7 @@
"editAddress": "Edit address",
"email": "Email",
"emptyCart": "Your cart is empty",
"emptyCartNotification": "You don't have any items in your cart",
"emptyCartImgAlt": "Image of an empty cart",
"emptyStateAltText": "Image that describes that no products were found",
"emptyStateText": "We no longer have products in this category.",
Expand Down
8 changes: 8 additions & 0 deletions apps/web/middleware/reject-empty-checkout.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export default defineNuxtRouteMiddleware(async () => {
const { getCart, cartIsEmpty } = useCart();
const localePath = useLocalePath();

await getCart();
if (!cartIsEmpty.value) return;
return navigateTo(localePath(paths.cart));
});
20 changes: 12 additions & 8 deletions apps/web/pages/cart.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@
<NuxtLayout
name="checkout"
:back-to-cart="false"
:back-label-desktop="$t('back')"
:back-label-mobile="$t('back')"
:heading="$t('myCart')"
:back-label-desktop="t('back')"
:back-label-mobile="t('back')"
:heading="t('myCart')"
>
<div v-if="cartNotEmpty" class="md:grid md:grid-cols-12 md:gap-x-6" data-testid="cart-page-content">
<div v-if="!cartIsEmpty" class="md:grid md:grid-cols-12 md:gap-x-6" data-testid="cart-page-content">
<div class="col-span-7 mb-2 md:mb-0">
<div v-for="(cartItem, index) in cart?.items" :key="cartItem.id">
<UiCartProductCard :cart-item="cartItem" :class="{ 'border-t': index === 0 }" />
Expand All @@ -24,7 +24,7 @@
size="lg"
class="w-full mb-4 md:mb-0"
>
{{ $t('goToCheckout') }}
{{ t('goToCheckout') }}
</UiButton>
<client-only>
<PayPalExpressButton :disabled="loading" class="mt-4" type="CartPreview" />
Expand All @@ -34,7 +34,7 @@
</div>
</div>
<div v-else class="flex items-center justify-center flex-col pt-24 pb-32" data-testid="cart-page-content">
<h2 class="mt-8 typography-headline-3 font-bold">{{ $t('emptyCart') }}</h2>
<h2 class="mt-8 typography-headline-3 font-bold">{{ t('emptyCart') }}</h2>
</div>
</NuxtLayout>
</template>
Expand All @@ -46,10 +46,14 @@ import { cartGetters } from '@plentymarkets/shop-api';
definePageMeta({ pageType: 'static' });

const NuxtLink = resolveComponent('NuxtLink');
const { send } = useNotification();
const { t } = useI18n();
const viewport = useViewport();
const localePath = useLocalePath();
const { isAuthorized } = useCustomer();
const { data: cart, loading } = useCart();
const cartNotEmpty = computed(() => (cart.value?.items?.length ?? 0) > 0);
const { data: cart, cartIsEmpty, loading } = useCart();
const goToCheckout = () => (isAuthorized.value ? localePath(paths.checkout) : localePath(paths.guestLogin));
onNuxtReady(() => {
if (cartIsEmpty.value) send({ type: 'neutral', message: t('emptyCartNotification') });
});
</script>
23 changes: 13 additions & 10 deletions apps/web/pages/checkout.vue
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ import { PayPalAddToCartCallback } from '~/components/PayPal/types';
definePageMeta({
layout: 'simplified-header-and-footer',
pageType: 'static',
middleware: ['reject-empty-checkout'],
});

const { send } = useNotification();
Expand All @@ -120,7 +121,7 @@ const { shippingPrivacyAgreement } = useAdditionalInformation();
const { checkboxValue: termsAccepted } = useAgreementCheckbox('checkoutGeneralTerms');
const {
cart,
getCart,
cartIsEmpty,
clearCartItems,
cartLoading,
anyAddressFormIsOpen,
Expand Down Expand Up @@ -154,18 +155,15 @@ onNuxtReady(async () => {
.catch((error) => useHandleError(error));
});

await getCart().then(
async () =>
await Promise.all([
useCartShippingMethods().getShippingMethods(),
usePaymentMethods().fetchPaymentMethods(),
useAggregatedCountries().fetchAggregatedCountries(),
]),
);
await Promise.all([
useCartShippingMethods().getShippingMethods(),
usePaymentMethods().fetchPaymentMethods(),
useAggregatedCountries().fetchAggregatedCountries(),
]);

const paypalCardDialog = ref(false);
const disableShippingPayment = computed(() => loadShipping.value || loadPayment.value);

const processingOrder = ref(false);
const paypalPaymentId = computed(() => {
if (!paymentMethods.value.list) return null;
return paymentProviderGetters.getIdByPaymentKey(paymentMethods.value.list, PayPalPaymentKey);
Expand Down Expand Up @@ -214,10 +212,15 @@ const handlePayPalExpress = (callback?: PayPalAddToCartCallback) => {
const order = async () => {
if (!readyToBuy()) return;

processingOrder.value = true;
const paymentMethodsById = _.keyBy(paymentMethods.value.list, 'id');

paymentMethodsById[selectedPaymentId.value].key === 'plentyPayPal'
? (paypalCardDialog.value = true)
: await handleRegularOrder();
};

watch(cartIsEmpty, async () => {
if (!processingOrder.value) await navigateTo(localePath(paths.cart));
});
</script>
30 changes: 8 additions & 22 deletions apps/web/pages/readonly-checkout.vue
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
<template>
<NuxtLayout
name="checkout"
:back-label-desktop="t('backToCart')"
:back-label-mobile="t('back')"
:heading="t('checkout')"
:back-label-desktop="$t('backToCart')"
:back-label-mobile="$t('back')"
:heading="$t('checkout')"
>
<div v-if="cart" class="md:grid md:grid-cols-12 md:gap-x-6">
<div class="col-span-7 mb-10 md:mb-0">
Expand Down Expand Up @@ -44,7 +44,7 @@
size="sm"
/>
<span v-else>
{{ t('buy') }}
{{ $t('buy') }}
</span>
</UiButton>
</OrderSummary>
Expand All @@ -65,7 +65,7 @@ definePageMeta({
const ID_CHECKBOX = '#terms-checkbox';

const { getSession } = useCustomer();
const { data: cart, clearCartItems, loading: cartLoading } = useCart();
const { data: cart, cartIsEmpty, clearCartItems, loading: cartLoading } = useCart();
const { data: billingAddresses, getAddresses: getBillingAddresses } = useAddress(AddressType.Billing);
const {
data: shippingAddresses,
Expand All @@ -78,8 +78,6 @@ const { loading: createOrderLoading, createOrder } = useMakeOrder();
const { shippingPrivacyAgreement } = useAdditionalInformation();
const { loading: executeOrderLoading, executeOrder } = usePayPal();
const route = useRoute();
const { send } = useNotification();
const { t } = useI18n();
const localePath = useLocalePath();
const { checkboxValue: termsAccepted, setShowErrors } = useAgreementCheckbox('checkoutGeneralTerms');

Expand All @@ -98,21 +96,8 @@ const loadAddresses = async () => {
await getShippingMethods();
};

const redirectBack = () => {
if (cart.value.items?.length === 0) {
send({
type: 'neutral',
message: t('emptyCart'),
});

navigateTo(localePath(paths.cart));
return true;
}
return false;
};

await getSession();
redirectBack();
if (cartIsEmpty.value) await navigateTo(localePath(paths.cart));
await loadAddresses();
await getShippingMethods();
await fetchPaymentMethods();
Expand All @@ -133,7 +118,8 @@ const validateTerms = (): boolean => {
};

const order = async () => {
if (redirectBack() || !validateTerms()) return;
if (cartIsEmpty.value) await navigateTo(localePath(paths.cart));
if (!validateTerms()) return;

const data = await createOrder({
paymentId: cart.value.methodOfPaymentId,
Expand Down
1 change: 1 addition & 0 deletions docs/changelog/changelog_en.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
- Started to unify the SDK/API error handling. Errors do now return keys that can be translated in the frontend.
- Manufacturer visual improvments
- Changed manufacturer translation text.
- In cases where the basket is empty during the checkout process, the system will now redirect to `/cart` and display a notification.

## v1.6.0 (2024-10-10) <a href="https://github.com/plentymarkets/plentyshop-pwa/compare/v1.5.0...v1.6.0" target="_blank" rel="noopener"><b>Overview of all changes</b></a>

Expand Down
Loading