schon/storefront/app/components/forms/address.vue
Alexandr SaVBaD Waltz 52e559dae0 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.
2026-03-10 11:44:09 +03:00

181 lines
No EOL
4 KiB
Vue

<template>
<form class="form" @submit.prevent="handleCreate">
<div class="form__block">
<p>{{ t('fields.city') }}</p>
<el-select
v-model="city"
size="large"
:placeholder="t('fields.city')"
>
<el-option
v-for="item in options"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</div>
<div class="form__block">
<p>{{ t('fields.addressAuto') }}</p>
<el-select
size="large"
filterable
remote
reserve-keyword
:remote-method="handleSearch"
v-model="query"
:placeholder="t('fields.address')"
:loading="autocompleteLoading"
>
<el-option
v-for="(item, idx) in completeResults"
:key="idx"
:label="item.display_name"
:value="item.display_name"
/>
</el-select>
</div>
<ui-input
:type="'text'"
v-model="name"
:label="t('fields.addressName')"
:placeholder="t('fields.addressName')"
/>
<ui-checkbox v-model="isHouse">
{{ t('checkboxes.isHouse') }}
</ui-checkbox>
<div class="form__box">
<ui-input
:type="'text'"
v-model="entrance"
:placeholder="t('fields.entrance')"
:label="t('fields.entrance')"
:numberOnly="true"
:inputMode="'decimal'"
:isDisabled="isHouse"
/>
<ui-input
:type="'text'"
v-model="floor"
:placeholder="t('fields.floor')"
:label="t('fields.floor')"
:numberOnly="true"
:inputMode="'decimal'"
:isDisabled="isHouse"
/>
<ui-input
:type="'text'"
v-model="apartNumber"
:placeholder="t('fields.apartNumber')"
:label="t('fields.apartNumber')"
:numberOnly="true"
:inputMode="'decimal'"
:isDisabled="isHouse"
/>
</div>
<ui-button
:type="'submit'"
:isLoading="createLoading"
:isDisabled="!isFormValid"
class="form__button"
>
{{ t('buttons.add') }}
</ui-button>
</form>
</template>
<script setup lang="ts">
import {useAddressAutocomplete, useAddressCreate} from "@composables/adresses";
const { t } = useI18n();
const { createAddress, loading: createLoading } = useAddressCreate();
const {
query,
completeResults,
loading: autocompleteLoading,
handleSearch,
} = useAddressAutocomplete();
const name = ref<string>('');
const isHouse = ref<boolean>(false);
const entrance = ref<string>('');
const floor = ref<string>('');
const apartNumber = ref<string>('');
const city = ref<string>('');
const options = [
{
value: 'Moscow',
label: t('forms.address.moscow'),
}
];
const isFormValid = computed(() => {
const baseValid =
city.value !== '' &&
query.value !== '' &&
name.value !== '';
if (isHouse.value) {
return baseValid;
}
return (
baseValid &&
entrance.value !== '' &&
floor.value !== '' &&
apartNumber.value !== ''
);
});
watch(isHouse, (val) => {
if (val) {
entrance.value = '';
floor.value = '';
apartNumber.value = '';
}
});
async function handleCreate() {
await createAddress({
rawData: query.value,
addressLine1: isHouse.value
? `name=${name.value} is_house=true`
: `entrance=${entrance.value} floor=${floor.value} apartment_number=${apartNumber.value} name=${name.value} is_house=false`
});
}
</script>
<style lang="scss" scoped>
.form {
display: flex;
flex-direction: column;
gap: 20px;
&__block {
display: flex;
flex-direction: column;
gap: 10px;
& p {
color: $secondary;
font-size: 14px;
font-weight: 500;
letter-spacing: -0.5px;
}
}
&__box {
display: flex;
align-items: center;
gap: 20px;
}
&__button {
margin-top: 10px;
width: fit-content;
padding-inline: 30px;
}
}
</style>