From 5fb8f94ec55d31c0a9d7c95a8e272639bbab21ee Mon Sep 17 00:00:00 2001 From: Cristi Sandru <149154151+csandru-plenty@users.noreply.github.com> Date: Wed, 30 Oct 2024 13:38:16 +0200 Subject: [PATCH 1/3] fix: handle item maximum stock (#757) * fix: handle item maximum stock * add changelog * sdk bump * refactor translations --- .../ui/CartProductCard/CartProductCard.vue | 51 ++++++++++++++++--- .../ui/QuantitySelector/QuantitySelector.vue | 2 + apps/web/composables/useCart/useCart.ts | 17 ++++++- apps/web/lang/de.json | 3 +- apps/web/lang/en.json | 3 +- docs/changelog/changelog_en.md | 1 + package.json | 2 +- yarn.lock | 10 ++-- 8 files changed, 72 insertions(+), 17 deletions(-) diff --git a/apps/web/components/ui/CartProductCard/CartProductCard.vue b/apps/web/components/ui/CartProductCard/CartProductCard.vue index e55781d91..e96309170 100644 --- a/apps/web/components/ui/CartProductCard/CartProductCard.vue +++ b/apps/web/components/ui/CartProductCard/CartProductCard.vue @@ -95,10 +95,12 @@ {{ n(currentFullPrice || 0, 'currency') }} @@ -125,21 +127,25 @@ diff --git a/apps/web/composables/useCart/useCart.ts b/apps/web/composables/useCart/useCart.ts index 30d04fe5b..bdc017dfa 100644 --- a/apps/web/composables/useCart/useCart.ts +++ b/apps/web/composables/useCart/useCart.ts @@ -4,6 +4,7 @@ import { SetCartItemQuantityParams, DeleteCartItemParams, CartItem, + CartItemError, } from '@plentymarkets/shop-api'; import { type UseCartReturn, @@ -38,6 +39,10 @@ const migrateVariationData = (oldCart: Cart, nextCart: Cart = {} as Cart): Cart return nextCart; }; +const isCartItemError = (data: Cart | CartItemError): data is CartItemError => { + return 'availableStock' in data; +}; + /** * @description Composable for managing cart. * @returns UseCartReturn @@ -186,9 +191,19 @@ export const useCart: UseCartReturn = () => { cartItemId: params.cartItemId, }), ); + useHandleError(error.value); - state.value.data = migrateVariationData(state.value.data, data?.value?.data) ?? state.value.data; + if (isCartItemError(data.value?.data as unknown as Cart | CartItemError)) { + const { $i18n } = useNuxtApp(); + const { send } = useNotification(); + const responseData = data?.value?.data as CartItemError; + state.value.data.itemQuantity = responseData.availableStock; + + send({ message: $i18n.t('storefrontError.cart.reachedMaximumQuantity'), type: 'warning' }); + } else { + state.value.data = migrateVariationData(state.value.data, data?.value?.data as Cart) ?? state.value.data; + } return state.value.data; } catch (error) { diff --git a/apps/web/lang/de.json b/apps/web/lang/de.json index 5bd2af5e5..b65c0ea93 100644 --- a/apps/web/lang/de.json +++ b/apps/web/lang/de.json @@ -883,7 +883,8 @@ "validationFailed": "Die Adresse ist ungültig." }, "cart": { - "failedToRemoveItem": "Der Artikel konnte nicht entfernt werden." + "failedToRemoveItem": "Der Artikel konnte nicht entfernt werden.", + "reachedMaximumQuantity": "Die maximale Bestellmenge dieses Artikels wurde überschritten." }, "contact": { "emailAlreadyExists": "Die E-Mail Adresse existiert bereits.", diff --git a/apps/web/lang/en.json b/apps/web/lang/en.json index 44cf305de..35deb1028 100644 --- a/apps/web/lang/en.json +++ b/apps/web/lang/en.json @@ -883,7 +883,8 @@ "validationFailed": "The address is invalid." }, "cart": { - "failedToRemoveItem": "The item could not be removed." + "failedToRemoveItem": "The item could not be removed.", + "reachedMaximumQuantity": "Your order exceeds the maximum order quantity of this item." }, "contact": { "emailAlreadyExists": "The e-mail address already exists.", diff --git a/docs/changelog/changelog_en.md b/docs/changelog/changelog_en.md index df7290c8b..0a64272da 100644 --- a/docs/changelog/changelog_en.md +++ b/docs/changelog/changelog_en.md @@ -18,6 +18,7 @@ ### 🩹 Fixed +- Fixed an issue where increasing quantity over maximum stock will lead to cart being cleared. - Removed the "Add to Cart" notification from the item and category pages when the quick checkout modal is not present. - Fixed an accessibility issue where the font size was too small. - Fixed an issue where product path was not reactive when category was changed. diff --git a/package.json b/package.json index e32b845a8..ba5257e27 100644 --- a/package.json +++ b/package.json @@ -31,7 +31,7 @@ "lhci:mobile": "lhci autorun" }, "dependencies": { - "@plentymarkets/shop-api": "^0.69.1", + "@plentymarkets/shop-api": "^0.69.2", "@types/applepayjs": "^14.0.8", "@vee-validate/nuxt": "^4.13.2", "@vee-validate/yup": "^4.13.2", diff --git a/yarn.lock b/yarn.lock index f38cd2199..1d7304dbc 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4237,7 +4237,7 @@ __metadata: "@nuxt/test-utils": ^3.13.1 "@nuxtjs/turnstile": ^0.8.0 "@paypal/paypal-js": 8.1.0 - "@plentymarkets/shop-api": ^0.69.1 + "@plentymarkets/shop-api": ^0.69.2 "@types/applepayjs": ^14.0.8 "@types/uuid": ^9.0.8 "@vee-validate/nuxt": ^4.13.2 @@ -4270,14 +4270,14 @@ __metadata: languageName: unknown linkType: soft -"@plentymarkets/shop-api@npm:^0.69.1": - version: 0.69.1 - resolution: "@plentymarkets/shop-api@npm:0.69.1::__archiveUrl=https%3A%2F%2Fnpm.pkg.github.com%2Fdownload%2F%40plentymarkets%2Fshop-api%2F0.69.1%2F996fe4a1e43d601c0b1c75d9c1c103bd3e301a10" +"@plentymarkets/shop-api@npm:^0.69.2": + version: 0.69.2 + resolution: "@plentymarkets/shop-api@npm:0.69.2::__archiveUrl=https%3A%2F%2Fnpm.pkg.github.com%2Fdownload%2F%40plentymarkets%2Fshop-api%2F0.69.2%2F30647f7862c0944605b240584e041457ca19fba4" dependencies: "@vue-storefront/middleware": ^3.10.0 axios: ^1.7.7 consola: ^3.2.3 - checksum: f2a7b5c86aa676cd9e997e56b7a2ce472b41857c9bbb991923a6386c95cad93ddc87a80be39b108c35118effcb39771cb86c3ffb95de6199c3e7617539ffd6fb + checksum: fa5e4a0156d84365069dead6138c838cfca5fbcce9b4b0b45b627610883445653b645469352d4b8a8728062f525e543922d35dbfb1e90634e5e59584179cf8ec languageName: node linkType: hard From 36eaf8c307810edd61f110d61554094900d573af Mon Sep 17 00:00:00 2001 From: oivan-plenty <114394643+oivan-plenty@users.noreply.github.com> Date: Wed, 30 Oct 2024 16:13:30 +0200 Subject: [PATCH 2/3] feat: refactor-index (#769) * refactored index * refactored index * lint * changes following comments --------- Co-authored-by: oivan-plenty --- apps/server/tsconfig.json | 1 + .../components/HeroContent/HeroContent.vue | 37 ---- .../ui/HeroCarousel/HeroCarousel.vue | 4 +- apps/web/components/ui/HeroCarousel/types.ts | 7 +- .../components/ui/HeroContent/HeroContent.vue | 4 +- apps/web/components/ui/MediaCard/types.ts | 2 +- .../useHomepageData/homepageTemplateData.json | 74 +++++++ .../useHomepageData/useHomepageData.ts | 81 ++++++++ apps/web/pages/index.vue | 186 +----------------- docs/changelog/changelog_en.md | 1 + 10 files changed, 167 insertions(+), 230 deletions(-) delete mode 100644 apps/web/components/HeroContent/HeroContent.vue create mode 100644 apps/web/composables/useHomepageData/homepageTemplateData.json create mode 100644 apps/web/composables/useHomepageData/useHomepageData.ts diff --git a/apps/server/tsconfig.json b/apps/server/tsconfig.json index 0f8043133..bcdbcb713 100644 --- a/apps/server/tsconfig.json +++ b/apps/server/tsconfig.json @@ -1,6 +1,7 @@ { "display": "Server", "compilerOptions": { + "resolveJsonModule": true, "lib": ["DOM", "DOM.Iterable", "ES2019"], "composite": false, "declaration": false, diff --git a/apps/web/components/HeroContent/HeroContent.vue b/apps/web/components/HeroContent/HeroContent.vue deleted file mode 100644 index 8ec7ed75a..000000000 --- a/apps/web/components/HeroContent/HeroContent.vue +++ /dev/null @@ -1,37 +0,0 @@ - - - diff --git a/apps/web/components/ui/HeroCarousel/HeroCarousel.vue b/apps/web/components/ui/HeroCarousel/HeroCarousel.vue index 23cf241e0..830e40104 100644 --- a/apps/web/components/ui/HeroCarousel/HeroCarousel.vue +++ b/apps/web/components/ui/HeroCarousel/HeroCarousel.vue @@ -16,7 +16,7 @@ diff --git a/apps/web/components/ui/MediaCard/types.ts b/apps/web/components/ui/MediaCard/types.ts index 25d2ecd3a..3eaf5cd1a 100644 --- a/apps/web/components/ui/MediaCard/types.ts +++ b/apps/web/components/ui/MediaCard/types.ts @@ -1,4 +1,4 @@ -export type MediaItem = { +export type MediaItemProps = { text: string; image: string; alt: string; diff --git a/apps/web/composables/useHomepageData/homepageTemplateData.json b/apps/web/composables/useHomepageData/homepageTemplateData.json new file mode 100644 index 000000000..9b9c73c53 --- /dev/null +++ b/apps/web/composables/useHomepageData/homepageTemplateData.json @@ -0,0 +1,74 @@ +{ + "id": 100, + "hero": [ + { + "image": { + "lg": "https://cdn02.plentymarkets.com/mevofvd5omld/frontend/homepage-hero-headphones.avif", + "md": "https://cdn02.plentymarkets.com/mevofvd5omld/frontend/headphones-md.avif", + "sm": "https://cdn02.plentymarkets.com/mevofvd5omld/frontend/headphones-sm.avif", + "xs": "https://cdn02.plentymarkets.com/mevofvd5omld/frontend/headphones-xs.avif", + "alt": "Headphones" + }, + "tagline": "Feel the music", + "taglineColor": "#000000", + "heading": "Your Sound, Elevated", + "headingColor": "#000000", + "description": "Immerse yourself in rich, crystal-clear audio with our cutting-edge headphones. Designed for the ultimate listening experience, whether you're a casual listener or an audiophile. Discover the perfect blend of style, comfort, and sound quality that elevates your music to new heights.\n\n", + "descriptionColor": "#000000", + "callToAction": "Order Now", + "link": "" + }, + { + "image": { + "lg": "https://cdn02.plentymarkets.com/mevofvd5omld/frontend/homepage-hero-headphones.avif", + "md": "https://cdn02.plentymarkets.com/mevofvd5omld/frontend/headphones-md.avif", + "sm": "https://cdn02.plentymarkets.com/mevofvd5omld/frontend/headphones-sm.avif", + "xs": "https://cdn02.plentymarkets.com/mevofvd5omld/frontend/headphones-xs.avif", + "alt": "Headphones" + }, + "tagline": "Experience Sound Freedom", + "taglineColor": "#000000", + "heading": "Wireless. Effortless. Seamless.", + "headingColor": "#000000", + "description": "Unleash your audio with our state-of-the-art wireless earbuds. Designed for all-day comfort and uncompromised sound quality, these earbuds deliver crisp highs and deep bass, letting you enjoy your music without any distractions. Discover freedom with a perfect fit, long battery life, and intuitive controls.", + "descriptionColor": "#000000", + "callToAction": "Shop Earbuds", + "link": "" + }, + { + "image": { + "lg": "https://cdn02.plentymarkets.com/mevofvd5omld/frontend/homepage-hero-headphones.avif", + "md": "https://cdn02.plentymarkets.com/mevofvd5omld/frontend/headphones-md.avif", + "sm": "https://cdn02.plentymarkets.com/mevofvd5omld/frontend/headphones-sm.avif", + "xs": "https://cdn02.plentymarkets.com/mevofvd5omld/frontend/headphones-xs.avif", + "alt": "Headphones" + }, + "tagline": "Amplify Your Space", + "taglineColor": "#000000", + "heading": "Big Sound, Compact Design", + "headingColor": "#000000", + "description": "Transform your space with our portable speakers that pack a punch. Crafted for superior sound performance, these speakers are perfect for home or on the go. With easy connectivity and a sleek design, elevate your listening experience whether you're indoors or outdoors.", + "descriptionColor": "#000000", + "callToAction": "Browse Speakers", + "link": "" + } + ], + "valueProposition": [ + { + "text": "
Experience the Future of Sound

Redefine Your Listening Experience

Our latest collection of headphones is designed to deliver unparalleled audio precision, with deep bass, clear highs, and an immersive experience for every genre of music. Combining sleek design, comfort, and cutting-edge technology, these headphones are made for those who refuse to compromise on sound quality.

  • Premium, studio-quality sound
  • Comfortable fit for extended listening
  • Long-lasting battery life
  • Seamless wireless connectivity
", + "image": "https://cdn02.plentymarkets.com/mevofvd5omld/frontend/headphones-mediacard.avif", + "alt": "Headphones", + "alignment": "left" + } + ], + "featured": [ + { + "headline": "", + "categoryId": 1 + }, + { + "headline": "", + "categoryId": 2 + } + ] +} diff --git a/apps/web/composables/useHomepageData/useHomepageData.ts b/apps/web/composables/useHomepageData/useHomepageData.ts new file mode 100644 index 000000000..0b28a02df --- /dev/null +++ b/apps/web/composables/useHomepageData/useHomepageData.ts @@ -0,0 +1,81 @@ +import { ref, watch, computed } from 'vue'; +import { HeroContentProps, SizeKey } from '~/components/ui/HeroCarousel/types'; +import { MediaItemProps } from '~/components/ui/MediaCard/types'; +import { useCategoryTree, useCategoryTemplate } from '~/composables'; +import homepageTemplateData from './homepageTemplateData.json'; + +const resolveImage = (imageSizes: Record, sizeKey: SizeKey): string => { + return imageSizes[sizeKey] || ''; +}; + +export default async function useHomepageData() { + const viewport = useViewport(); + const { data: categoryTree } = useCategoryTree(); + const recommendedProductsCategoryId = ref(''); + + const runtimeConfig = useRuntimeConfig(); + const homepageTemplate = ref(homepageTemplateData); + + const homepageCategoryId = runtimeConfig.public.homepageCategoryId; + const { fetchCategoryTemplate } = useCategoryTemplate(); + + if (typeof homepageCategoryId === 'number') { + const { data } = await fetchCategoryTemplate(homepageCategoryId); + const parsedData = JSON.parse(data); + if (parsedData) { + homepageTemplate.value = { + id: parsedData.id, + hero: parsedData.hero || [], + valueProposition: parsedData.valueProposition, + featured: parsedData.featured, + }; + } + } + + const getCurrentSizeKey = (): SizeKey => viewport.breakpoint.value as SizeKey; + + const mediaData = computed(() => + homepageTemplate.value.valueProposition.map((media: MediaItemProps) => ({ + image: media.image, + text: media.text, + alignment: media.alignment, + alt: media.alt, + })), + ); + + const formattedHeroItems = computed(() => { + const currentSizeKey = getCurrentSizeKey(); + return homepageTemplate.value.hero.map((item) => { + return { + image: resolveImage(item.image, currentSizeKey), + tagline: item.tagline || '', + heading: item.heading || '', + description: item.description || '', + callToAction: item.callToAction || '', + link: item.link || '', + backgroundSizes: { + lg: { width: '4000', height: '600' }, + md: { width: '1024', height: '600' }, + sm: { width: '640', height: '752' }, + xs: { width: '250', height: '250' }, + }, + actualBackgroundSize: currentSizeKey, + } as HeroContentProps; + }); + }); + + watch( + () => categoryTree.value, + () => { + const firstCategoryId = categoryTree.value?.[0]?.id; + if (firstCategoryId) recommendedProductsCategoryId.value = firstCategoryId.toString(); + }, + { immediate: true }, + ); + + return { + formattedHeroItems, + mediaData, + recommendedProductsCategoryId, + }; +} diff --git a/apps/web/pages/index.vue b/apps/web/pages/index.vue index e22ec45a2..2f14b5e7c 100644 --- a/apps/web/pages/index.vue +++ b/apps/web/pages/index.vue @@ -29,188 +29,10 @@ - diff --git a/docs/changelog/changelog_en.md b/docs/changelog/changelog_en.md index 0a64272da..a88753ef9 100644 --- a/docs/changelog/changelog_en.md +++ b/docs/changelog/changelog_en.md @@ -359,6 +359,7 @@ - Fixed the issue where a hard load after language switch leads to a 404 error. - Added the missing wishlist navigation button on mobile devices. - Fixed the link to the contribution guidelines in the documentation. +- Refactored index.vue ## New Contributors From eb6666d3bc0180f6a4bb98030df29ba54259b19a Mon Sep 17 00:00:00 2001 From: Cristi Sandru <149154151+csandru-plenty@users.noreply.github.com> Date: Wed, 30 Oct 2024 17:30:03 +0200 Subject: [PATCH 3/3] feat: notify on unsaved address (#762) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: notify on unsaved address * Update de.json * Update en.json --------- Co-authored-by: Franz Mutschler <36404400+fmutschler@users.noreply.github.com> Co-authored-by: Maximilian Röll <30629157+maxiroellplenty@users.noreply.github.com> --- apps/web/lang/de.json | 1 + apps/web/lang/en.json | 1 + apps/web/pages/checkout.vue | 18 ++++++++++++------ docs/changelog/changelog_en.md | 1 + 4 files changed, 15 insertions(+), 6 deletions(-) diff --git a/apps/web/lang/de.json b/apps/web/lang/de.json index b65c0ea93..aae9889aa 100644 --- a/apps/web/lang/de.json +++ b/apps/web/lang/de.json @@ -837,6 +837,7 @@ "saveAddress": "Adresse speichern", "savedBillingAddress": "Gespeicherte Rechnungsadressen", "savedShippingAddress": "Gespeicherte Lieferadressen", + "unsavedAddress": "Die eingegebene Adresse wurde noch nicht gespeichert.", "scrollTop": "Nach oben scrollen", "search": "Suche", "shipping": { diff --git a/apps/web/lang/en.json b/apps/web/lang/en.json index 35deb1028..3e9eb1500 100644 --- a/apps/web/lang/en.json +++ b/apps/web/lang/en.json @@ -837,6 +837,7 @@ "saveAddress": "Save address", "savedBillingAddress": "Saved billing adresses", "savedShippingAddress": "Saved shipping adresses", + "unsavedAddress": "The address you entered has not yet been saved. Click Save address to proceed with the checkout.", "scrollTop": "Scroll to top", "search": "Search", "shipping": { diff --git a/apps/web/pages/checkout.vue b/apps/web/pages/checkout.vue index d01ab3de7..f2c2b67b2 100644 --- a/apps/web/pages/checkout.vue +++ b/apps/web/pages/checkout.vue @@ -1,9 +1,9 @@