Skip to content

Commit

Permalink
feat: add checkout translations and cart refresh (#433)
Browse files Browse the repository at this point in the history
* feat: add checkout translations and cart refresh

* Update .changeset/heavy-meals-approve.md

---------

Co-authored-by: Patryk Tomczyk <13100280+patzick@users.noreply.github.com>
  • Loading branch information
mdanilowicz and patzick committed Oct 23, 2023
1 parent 0ae8730 commit 43510a1
Show file tree
Hide file tree
Showing 17 changed files with 152 additions and 25 deletions.
5 changes: 5 additions & 0 deletions .changeset/heavy-meals-approve.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"vue-demo-store": minor
---

Add a new `getErrorsCodes` method for `useCartNotification` that returns cart errors without displaying it.
5 changes: 5 additions & 0 deletions .changeset/light-tables-clap.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"vue-demo-store": minor
---

Move checkout errors translations to the Frontend side
5 changes: 5 additions & 0 deletions .changeset/pink-rocks-provide.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"vue-demo-store": minor
---

Refresh items list on the checkout after qty update
5 changes: 5 additions & 0 deletions .changeset/tall-ligers-develop.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@shopware-pwa/composables-next": patch
---

`changeProductQuantity` returns whole cart response
5 changes: 5 additions & 0 deletions .changeset/wise-plums-sell.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@shopware-pwa/types": minor
---

Add CartError type
7 changes: 6 additions & 1 deletion packages/composables/src/useCart.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,10 @@ export type UseCartReturn = {
/**
* Changes the quantity of a product in the cart
*/
changeProductQuantity(params: { id: string; quantity: number }): void;
changeProductQuantity(params: {
id: string;
quantity: number;
}): Promise<Cart>;
/**
* The number of items in the cart
*/
Expand Down Expand Up @@ -141,6 +144,8 @@ export function useCartFunction(): UseCartReturn {
);
_storeCart.value = result;
setCartErrors(result);

return result;
}

async function submitPromotionCode(promotionCode: string) {
Expand Down
9 changes: 6 additions & 3 deletions packages/composables/src/useCartItem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import type {
PropertyGroupOptionCart,
ProductResponse,
CartProductItem,
Cart,
} from "@shopware-pwa/types";

import { getMainImageUrl } from "@shopware-pwa/helpers-next";
Expand Down Expand Up @@ -56,7 +57,7 @@ export type UseCartItemReturn = {
/**
* Changes the current item quantity in the cart
*/
changeItemQuantity(quantity: number): Promise<void>;
changeItemQuantity(quantity: number): Promise<Cart>;
/**
* Removes the current item from the cart
*/
Expand Down Expand Up @@ -119,11 +120,13 @@ export function useCartItem(cartItem: Ref<LineItem>): UseCartItemReturn {
await refreshCart(newCart);
}

async function changeItemQuantity(quantity: number): Promise<void> {
await changeProductQuantity({
async function changeItemQuantity(quantity: number): Promise<Cart> {
const result = await changeProductQuantity({
id: cartItem.value.id,
quantity: quantity,
});

return result;
}

/**
Expand Down
16 changes: 9 additions & 7 deletions packages/types/shopware-6-client/models/checkout/cart/Cart.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,15 @@ import { EntityError } from "../../common/EntityError";
* @beta
*/
export type CartErrors = {
[key: string]: {
code: number;
key: string;
level: number;
message: string;
messageKey: string;
};
[key: string]: CartError;
};

export type CartError = {
code: number;
key: string;
level: number;
message: string;
messageKey: string;
};

/**
Expand Down
21 changes: 16 additions & 5 deletions templates/vue-demo-store/components/checkout/CheckoutCartItem.vue
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,10 @@ const props = withDefaults(
const { cartItem } = toRefs(props);
const isLoading = ref(false);
const { codeErrorsNotification } = useCartNotification();
const { getErrorsCodes } = useCartNotification();
const { refreshCart } = useCart();
const { pushError } = useNotifications();
const { t } = useI18n();
const {
itemOptions,
Expand All @@ -29,13 +32,21 @@ const {
const quantity = ref();
syncRefs(itemQuantity, quantity);
const updateQuantity = async (quantity: number | undefined) => {
if (quantity === itemQuantity.value) return;
const updateQuantity = async (quantityInput: number | undefined) => {
if (quantityInput === itemQuantity.value) return;
isLoading.value = true;
await changeItemQuantity(Number(quantity));
codeErrorsNotification();
const response = await changeItemQuantity(Number(quantityInput));
// Refresh cart after qty update
await refreshCart(response);
// Make sure that qty is the same as it is in the response
quantity.value = itemQuantity.value;
getErrorsCodes()?.forEach((element) => {
pushError(t(`errors.${element.messageKey}`, { ...element }));
});
isLoading.value = false;
};
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,23 @@
<script setup lang="ts">
import type { LineItem } from "@shopware-pwa/types";
const { appliedPromotionCodes, addPromotionCode, removeItem } = useCart();
const { codeErrorsNotification } = useCartNotification();
const { getErrorsCodes } = useCartNotification();
const { pushError } = useNotifications();
const { t } = useI18n();
const addPromotionCodeHandler = async (code: string) => {
await addPromotionCode(code);
codeErrorsNotification();
getErrorsCodes()?.forEach((element) => {
pushError(t(`errors.${element.messageKey}`, { ...element }));
});
promoCode.value = "";
};
const removeItemHandler = (appliedPromotionCode: LineItem) => {
removeItem(appliedPromotionCode);
codeErrorsNotification();
getErrorsCodes()?.forEach((element) => {
pushError(t(`errors.${element.messageKey}`, { ...element }));
});
};
const showPromotionCodes = computed(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
<script setup lang="ts">
import type { Product } from "@shopware-pwa/types";
const { pushSuccess } = useNotifications();
const { pushSuccess, pushError } = useNotifications();
const props = defineProps<{
product: Product;
}>();
const { product } = toRefs(props);
const { codeErrorsNotification } = useCartNotification();
const { getErrorsCodes } = useCartNotification();
const { t } = useI18n();
const { addToCart, quantity } = useAddToCart(product);
const addToCartProxy = async () => {
await addToCart();
codeErrorsNotification();
getErrorsCodes()?.forEach((element) => {
pushError(t(`errors.${element.messageKey}`, { ...element }));
});
pushSuccess(`${props.product?.translated?.name} has been added to cart.`);
};
Expand Down
6 changes: 4 additions & 2 deletions templates/vue-demo-store/components/product/ProductCard.vue
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import type {
const { pushSuccess, pushError } = useNotifications();
const { t } = useI18n();
const { codeErrorsNotification } = useCartNotification();
const { getErrorsCodes } = useCartNotification();
const localePath = useLocalePath();
const { formatLink } = useInternationalization(localePath);
Expand Down Expand Up @@ -62,7 +62,9 @@ const toggleWishlistProduct = async () => {
const addToCartProxy = async () => {
await addToCart();
codeErrorsNotification();
getErrorsCodes()?.forEach((element) => {
pushError(t(`errors.${element.messageKey}`, { ...element }));
});
pushSuccess(
t(`cart.messages.addedToCart`, { p: props.product?.translated?.name }),
);
Expand Down
21 changes: 20 additions & 1 deletion templates/vue-demo-store/composables/useCartNotification.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import type { CartErrors } from "@shopware-pwa/types";
import type { CartErrors, CartError } from "@shopware-pwa/types";

const successCodes = ["promotion-discount-added"];

export type useCartNotificationReturn = {
codeErrorsNotification(): void;
getErrorsCodes(): CartError[] | undefined;
};

/**
Expand Down Expand Up @@ -33,7 +34,25 @@ export function useCartNotification(): useCartNotificationReturn {
});
};

/**
* Get errors codes without displaying
*
* @returns CartError[] | undefined
*/
const getErrorsCodes = () => {
const errors: CartErrors | null = consumeCartErrors();
if (!errors) return;

return Object.keys(errors).reduce((acc, element) => {
if (!successCodes.includes(errors[element].messageKey))
acc.push(errors[element]);

return acc;
}, [] as CartError[]);
};

return {
codeErrorsNotification,
getErrorsCodes,
};
}
2 changes: 2 additions & 0 deletions templates/vue-demo-store/i18n/de-DE/de-DE.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import listing from "./listing.json";
import product from "./product.json";
import newsletter from "./newsletter.json";
import validations from "./validations.json";
import errors from "./errors.json";

export default {
...account,
Expand All @@ -22,4 +23,5 @@ export default {
...product,
...newsletter,
...validations,
...errors,
};
23 changes: 23 additions & 0 deletions templates/vue-demo-store/i18n/de-DE/errors.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"errors": {
"product-stock-reached": "Das Produkt {name} ist nur noch %quantity% mal verfügbar",
"product-out-of-stock": "Das Produkt {name} ist nicht mehr verfügbar",
"purchase-steps-quantity": "Das Produkt {name} ist nicht in der gewünschten Menge verfügbar. Die Anzahl wurde auf %quantity% geändert.",
"min-order-quantity": "Die Anzahl an Produkten {name} hat die Mindestbestellmenge nicht erreicht. Die Anzahl wurde automatisch auf %quantity% erhöht.",
"shipping-method-blocked": "Die Versandart {name} ist für Ihren aktuellen Warenkorb gesperrt.",
"shipping-method-changed": "Die Versandart {oldShippingMethodName} ist für Ihren aktuellen Warenkorb nicht verfügbar, die Versandart wurde auf {newShippingMethodName} geändert.",
"payment-method-blocked": "Die Zahlungsart {name} ist für Ihren aktuellen Warenkorb gesperrt.",
"payment-method-changed": "Die Zahlungsart {oldPaymentMethodName} ist für Ihren aktuellen Warenkorb nicht verfügbar, die Zahlungsart wurde auf {newPaymentMethodName} geändert.",
"promotion-not-found": "Der Gutschein-Code {code} existiert nicht.",
"promotion-not-eligible": "Der Gutschein-Code wurde gespeichert, aber nicht auf den Warenkorb angewendet, da die Voraussetzungen dafür nicht zutreffen. Sobald die Voraussetzungen zutreffen, wird er automatisch hinzugefügt.",
"promotion-excluded": "Mindestens ein Rabatt wurde wegen Konflikten mit anderen Rabatten aus dem Warenkorb entfernt. Sobald die Bedingungen wieder zutreffen, wird der Rabatt automatisch hinzugefügt.",
"auto-promotion-not-found": "Die Rabattaktion {name} ist nicht länger gültig!",
"shipping-address-blocked": "Lieferungen an die gewählte Lieferadresse sind nicht möglich.",
"billing-address-blocked": "Rechnungen können nicht an die gewählte Rechnungsadresse ausgestellt werden.",
"shipping-address-invalid": "Die gewählte Versandadresse ist nicht gültig oder unvollständig. Bitte prüfen Sie Ihre Angaben.",
"billing-address-invalid": "Die gewählte Rechnungsadresse ist nicht gültig oder unvollständig. Bitte prüfen Sie Ihre Angaben.",
"cart-merged-hint": "Der aktuelle Warenkorb enthält u.U. zusätzliche Produkte, die noch von einem früheren Besuch gespeichert waren.",
"product-not-found": "Das Produkt wurde nicht gefunden.",
"salutation-missing": "Es wurde keine Anrede konfiguriert, bitte wählen Sie beim Abschluss Ihrer Bestellung eine Anrede aus."
}
}
2 changes: 2 additions & 0 deletions templates/vue-demo-store/i18n/en-GB/en-GB.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import listing from "./listing.json";
import product from "./product.json";
import newsletter from "./newsletter.json";
import validations from "./validations.json";
import errors from "./errors.json";

export default {
...form,
Expand All @@ -22,4 +23,5 @@ export default {
...product,
...newsletter,
...validations,
...errors,
};
23 changes: 23 additions & 0 deletions templates/vue-demo-store/i18n/en-GB/errors.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"errors": {
"product-stock-reached": "The product {name} is only available {quantity} times",
"product-out-of-stock": "The product {name} is not available any more",
"purchase-steps-quantity": "The product {name} is not available in this quantity. The quantity was changed to %quantity%",
"min-order-quantity": "The quantity of product {name} did not meet the minimum order quantity threshold. The quantity has automatically been increased to %quantity%.",
"shipping-method-blocked": "The shipping method {name} is blocked for your current shopping cart.",
"shipping-method-changed": "{oldShippingMethodName} shipping is not available for your current cart, the shipping was changed to {newShippingMethodName}.",
"payment-method-blocked": "The payment method {name} is blocked for your current shopping cart.",
"payment-method-changed": "{oldPaymentMethodName} payment is not available for your current cart, the payment was changed to {newPaymentMethodName}.",
"promotion-not-found": "Promotion with code {code} could not be found.",
"auto-promotion-not-found": "Promotion {name} no longer valid!",
"promotion-not-eligible": "Promotion code valid - however, not all conditions were met and the discount was not applied. Once all conditions are met, the discount will be applied automatically.",
"promotion-excluded": "One or more discounts have been removed from the shopping cart, due to conflicts with other discounts. Once the conditions are met again, the discounts will be applied automatically.",
"shipping-address-blocked": "Shipping to the selected shipping address is currently not possible.",
"billing-address-blocked": "Billing to the selected address is not possible.",
"shipping-address-invalid": "The selected shipping address is not valid or incomplete. Please check your entries.",
"billing-address-invalid": "The selected billing address is not valid or incomplete. Please check your entries.",
"cart-merged-hint": "The current shopping cart might contain additional products that have been added and saved during a previous visit.",
"product-not-found": "The product could not be found.",
"salutation-missing": "A salutation is missing from your profile, please choose one during checkout."
}
}

2 comments on commit 43510a1

@vercel
Copy link

@vercel vercel bot commented on 43510a1 Oct 23, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

frontends-demo – ./templates/vue-demo-store

frontends-demo-git-main-shopware-frontends.vercel.app
frontends-demo.vercel.app
frontends-demo-shopware-frontends.vercel.app

@vercel
Copy link

@vercel vercel bot commented on 43510a1 Oct 23, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.