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.
181 lines
No EOL
4 KiB
Vue
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> |