From 52e559dae04639d719a174f9dcc23fe9cf63e4d8 Mon Sep 17 00:00:00 2001 From: Alexandr SaVBaD Waltz Date: Tue, 10 Mar 2026 11:44:09 +0300 Subject: [PATCH] feat(cart): add address and promocode selection to checkout Implemented billing and shipping address selection in the cart UI, along with improved promocode dropdown. Updated GraphQL mutation to accept new address fields for more comprehensive order handling. - Replaced the old promocode selection implementation with `el-select` components. - Introduced billing and shipping address fields with selectable options. - Enhanced form validation to ensure all required fields are populated before checkout. - Updated translations (`en-gb.json`, `ru-ru.json`) with new field labels. - Adjusted SCSS for consistent styling of dropdowns. Improves user experience by streamlining and enhancing the checkout process. No breaking changes introduced. --- storefront/app/assets/styles/ui/select.scss | 9 +- storefront/app/components/forms/address.vue | 2 +- .../app/composables/orders/useOrderBuy.ts | 12 +- storefront/app/pages/cart.vue | 123 ++++++++++++++---- storefront/i18n/locales/en-gb.json | 7 +- storefront/i18n/locales/ru-ru.json | 7 +- 6 files changed, 126 insertions(+), 34 deletions(-) diff --git a/storefront/app/assets/styles/ui/select.scss b/storefront/app/assets/styles/ui/select.scss index 526c9851..11238565 100644 --- a/storefront/app/assets/styles/ui/select.scss +++ b/storefront/app/assets/styles/ui/select.scss @@ -2,7 +2,6 @@ .el-select__wrapper { height: 36px !important; - min-height: 50px !important; background-color: transparent !important; } .el-select--large .el-select__wrapper { @@ -24,4 +23,12 @@ .el-select-dropdown__item.is-hovering { color: $primary !important; background-color: $main !important; +} + + +.el-select--small .el-select__wrapper { + height: 30px !important; +} +.el-select--small .el-select-dropdown__item { + height: 28px !important; } \ No newline at end of file diff --git a/storefront/app/components/forms/address.vue b/storefront/app/components/forms/address.vue index 2e21e8f2..672ebde6 100644 --- a/storefront/app/components/forms/address.vue +++ b/storefront/app/components/forms/address.vue @@ -16,7 +16,7 @@
-

{{ t('fields.address') }}

+

{{ t('fields.addressAuto') }}

(BUY_CART); - async function buyOrder(promocodeUuid?: string) { + async function buyOrder(args: IBuyOrderArguments) { const result = await mutate({ orderUuid: orderUuid.value, forcePayment: true, forceBalance: false, - promocodeUuid: promocodeUuid + promocodeUuid: args.promocodeUuid, + billingAddress: args.billingAddress, + shippingAddress: args.shippingAddress, }); if (result?.data?.buyOrder?.transaction?.process?.url) { diff --git a/storefront/app/pages/cart.vue b/storefront/app/pages/cart.vue index 14112de3..a925b01a 100644 --- a/storefront/app/pages/cart.vue +++ b/storefront/app/pages/cart.vue @@ -23,35 +23,83 @@

{{ t('cart.items', productsInCartQuantity, { count: productsInCartQuantity }) }}

{{ t('cart.totalPrice') }}: {{ totalPrice }}$
-
- - - - -
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {{ item.node.code }} + + {{ + item.node.discountType === 'amount' + ? item.node.discount + '$' + : item.node.discount + '%' + }} + + + + {{ t('buttons.checkout') }} @@ -66,15 +114,18 @@ import {usePageTitle} from "@composables/utils"; import {useOrderBuy} from "~/composables/orders"; import {useExactProducts} from "@composables/products/useExactProducts"; +import {useValidators} from "@composables/rules"; const {t} = useI18n(); const cartStore = useCartStore(); const userStore = useUserStore(); const promocodeStore = usePromocodeStore(); +const addressesStore = useAddressesStore(); const { $appHelpers } = useNuxtApp(); const isAuthenticated = computed(() => userStore.isAuthenticated); +const addresses = computed(() => addressesStore.addresses); const promocodes = computed(() => promocodeStore.promocodes); const cookieCart = useCookie($appHelpers.COOKIES_CART_KEY, { @@ -82,6 +133,7 @@ const cookieCart = useCookie($appHelpers.COOKIES_CART_KEY, { path: '/', }); +const { required, isEmail } = useValidators(); const { buyOrder } = useOrderBuy(); const { products, getExactProducts } = useExactProducts(); @@ -99,6 +151,10 @@ const selectPromo = (promo) => { selectedPromo.value = promo; }; +const promo = ref(''); +const billingAddress = ref(''); +const shippingAddress = ref(''); + const cartUuids = computed(() => { return cookieCart.value.map(item => item.productUuid); }); @@ -154,6 +210,21 @@ const productsInCartQuantity = computed(() => { const { setPageTitle } = usePageTitle(); +const isFormValid = computed(() => { + return ( + required(billingAddress.value) === true && + required(shippingAddress.value) === true + ); +}); + +const handleBuy = async () => { + await buyOrder({ + promocodeUuid: promo.value, + billingAddress: billingAddress.value, + shippingAddress: shippingAddress.value + }) +} + setPageTitle(t('breadcrumbs.cart')); diff --git a/storefront/i18n/locales/en-gb.json b/storefront/i18n/locales/en-gb.json index a7227804..74dcf2bc 100644 --- a/storefront/i18n/locales/en-gb.json +++ b/storefront/i18n/locales/en-gb.json @@ -55,8 +55,11 @@ "confirmPassword": "Confirm password", "confirmNewPassword": "Confirm new password", "brandsSearch": "Search brands by name...", - "promocode": "Enter promocode", - "address": "Start typing the address", + "promocode": "Promocode", + "billingAddress": "Billing address", + "shippingAddress": "Shipping Address", + "address": "Address", + "addressAuto": "Start typing the address", "addressName": "Name", "country": "Country", "region": "Region", diff --git a/storefront/i18n/locales/ru-ru.json b/storefront/i18n/locales/ru-ru.json index fce0b4a2..a075445e 100644 --- a/storefront/i18n/locales/ru-ru.json +++ b/storefront/i18n/locales/ru-ru.json @@ -55,8 +55,11 @@ "confirmPassword": "Подтвердите пароль", "confirmNewPassword": "Подтвердите новый пароль", "brandsSearch": "Поиск брендов по названию...", - "promocode": "Введите промокод", - "address": "Начните вводить адрес", + "promocode": "Промокод", + "billingAddress": "Адрес для выставления счетов", + "shippingAddress": "Адрес доставки", + "address": "Адрес", + "addressAuto": "Начните вводить адрес", "addressName": "Название", "country": "Страна", "region": "Регион",