Skip to content

Commit

Permalink
feat: reject empty checkout (#770)
Browse files Browse the repository at this point in the history
* feat: reject empty checkout

* feat: reject empty checkout

* code refactor

---------

Co-authored-by: Maximilian Röll <[email protected]>
  • Loading branch information
csandru-plenty and maxiroellplenty authored Oct 31, 2024
1 parent eb6666d commit e69f256
Show file tree
Hide file tree
Showing 10 changed files with 50 additions and 41 deletions.
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

0 comments on commit e69f256

Please sign in to comment.