Add delivery addresses to profile, cart, and orders

This commit is contained in:
Ruslan Bakiev
2026-04-03 10:26:47 +07:00
parent 0aad9177f8
commit 4c82c2437a
11 changed files with 757 additions and 41 deletions

View File

@@ -1,11 +1,19 @@
<script setup lang="ts">
import { useMutation } from '@vue/apollo-composable';
import { SubmitCalculationOrderDocument } from '~/composables/graphql/generated';
import { useMutation, useQuery } from '@vue/apollo-composable';
import {
MyDeliveryAddressesDocument,
SubmitCalculationOrderDocument,
type MyDeliveryAddressesQuery,
} from '~/composables/graphql/generated';
import { useClientCart } from '~/composables/useClientCart';
import { useCounterpartyProfile } from '~/composables/useCounterpartyProfile';
type DeliveryAddressItem = MyDeliveryAddressesQuery['myDeliveryAddresses'][number];
const { isComplete: isCounterpartyComplete, loading: counterpartyLoading } = useCounterpartyProfile();
const submitMutation = useMutation(SubmitCalculationOrderDocument, { throws: 'never' });
const deliveryAddressesQuery = useQuery(MyDeliveryAddressesDocument);
const {
items: cartItems,
totalPositions,
@@ -16,10 +24,34 @@ const {
removeProduct,
clearCart,
} = useClientCart();
const selectedDeliveryAddressId = ref('');
const sending = ref(false);
const success = ref('');
const errorMessage = ref('');
const deliveryAddresses = computed<DeliveryAddressItem[]>(() => deliveryAddressesQuery.result.value?.myDeliveryAddresses ?? []);
const hasDeliveryAddresses = computed(() => deliveryAddresses.value.length > 0);
watch(
deliveryAddresses,
(addresses) => {
if (addresses.length < 1) {
selectedDeliveryAddressId.value = '';
return;
}
const hasCurrentSelection = addresses.some((address) => address.id === selectedDeliveryAddressId.value);
if (hasCurrentSelection) {
return;
}
const defaultAddress = addresses.find((address) => address.isDefault);
selectedDeliveryAddressId.value = defaultAddress?.id || addresses[0]?.id || '';
},
{ immediate: true },
);
function lineVolume(productId: string) {
const item = cartItems.value.find((entry) => entry.productId === productId);
if (!item) {
@@ -56,6 +88,11 @@ async function submitCart() {
return;
}
if (!selectedDeliveryAddressId.value) {
errorMessage.value = 'Выберите адрес доставки в профиле.';
return;
}
if (cartItems.value.length < 1) {
errorMessage.value = 'Добавьте хотя бы одну позицию в корзину.';
return;
@@ -74,6 +111,7 @@ async function submitCart() {
thickness: Number(item.parameters.thickness),
color: item.parameters.color,
},
deliveryAddressId: selectedDeliveryAddressId.value,
},
});
@@ -97,7 +135,7 @@ async function submitCart() {
<section class="space-y-6">
<h1 class="text-3xl font-extrabold text-[#0f2f20]">Корзина</h1>
<div v-if="counterpartyLoading.value" class="alert surface-card border-0">
<div v-if="counterpartyLoading" class="alert surface-card border-0">
Проверяем карточку контрагента...
</div>
<div v-else-if="!isCounterpartyComplete" class="alert alert-warning">
@@ -105,6 +143,40 @@ async function submitCart() {
<NuxtLink to="/profile" class="link link-hover font-semibold">профиле</NuxtLink>.
</div>
<div class="surface-card rounded-3xl p-4 md:p-5">
<h2 class="text-lg font-bold text-[#123824]">Адрес доставки</h2>
<div v-if="deliveryAddressesQuery.loading" class="alert mt-3 surface-card border-0">
Загружаем адреса...
</div>
<div v-else-if="!hasDeliveryAddresses" class="alert alert-warning mt-3">
Адреса не добавлены.
<NuxtLink to="/profile" class="link link-hover font-semibold">Добавить адрес в профиле</NuxtLink>
</div>
<div v-else class="mt-3 space-y-2">
<label
v-for="address in deliveryAddresses"
:key="address.id"
class="flex cursor-pointer items-start gap-3 rounded-2xl border border-[#d6ebde] bg-white/75 p-3"
>
<input
v-model="selectedDeliveryAddressId"
type="radio"
name="delivery-address"
class="radio radio-success mt-1"
:value="address.id"
>
<span>
<span class="block font-semibold text-[#123824]">
{{ address.label || 'Адрес доставки' }}
<span v-if="address.isDefault" class="badge badge-success ml-2">Основной</span>
</span>
<span class="block text-sm text-[#355947]">{{ address.unrestrictedValue || address.address }}</span>
</span>
</label>
</div>
</div>
<h2 class="text-xl font-bold text-[#123824]">Позиции</h2>
<div v-if="cartItems.length === 0" class="alert surface-card border-0">
@@ -155,7 +227,7 @@ async function submitCart() {
<button
class="btn w-full border-0 bg-[#139957] text-white hover:bg-[#0d854a]"
:disabled="sending || counterpartyLoading.value || !isCounterpartyComplete || cartItems.length === 0"
:disabled="sending || counterpartyLoading || !isCounterpartyComplete || !selectedDeliveryAddressId || cartItems.length === 0"
@click="submitCart"
>
{{ sending ? 'Отправляем…' : 'Оформить заявку' }}