Files
web-frontend/app/pages/cart.vue
2026-04-02 18:28:57 +07:00

164 lines
5.9 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<script setup lang="ts">
import { useMutation } from '@vue/apollo-composable';
import { SubmitCalculationOrderDocument } from '~/composables/graphql/generated';
import { useClientCart } from '~/composables/useClientCart';
import { useCounterpartyProfile } from '~/composables/useCounterpartyProfile';
const { isComplete: isCounterpartyComplete, loading: counterpartyLoading } = useCounterpartyProfile();
const submitMutation = useMutation(SubmitCalculationOrderDocument, { throws: 'never' });
const cart = useClientCart();
const sending = ref(false);
const success = ref('');
const errorMessage = ref('');
function lineVolume(productId: string) {
const item = cart.items.value.find((entry) => entry.productId === productId);
if (!item) {
return 0;
}
return Number(item.quantity) * Number(item.parameters.width) * Number(item.parameters.thickness);
}
function increment(productId: string) {
success.value = '';
errorMessage.value = '';
cart.incrementQuantity(productId);
}
function decrement(productId: string) {
success.value = '';
errorMessage.value = '';
cart.decrementQuantity(productId);
}
function removeFromCart(productId: string) {
success.value = '';
errorMessage.value = '';
cart.removeProduct(productId);
}
async function submitCart() {
success.value = '';
errorMessage.value = '';
if (!isCounterpartyComplete.value) {
errorMessage.value = 'Сначала заполните карточку контрагента в профиле.';
return;
}
if (cart.items.value.length < 1) {
errorMessage.value = 'Добавьте хотя бы одну позицию в корзину.';
return;
}
sending.value = true;
const createdCodes: string[] = [];
for (const item of cart.items.value) {
const result = await submitMutation.mutate({
input: {
productName: item.productName,
quantity: Number(item.quantity),
parameters: {
width: Number(item.parameters.width),
thickness: Number(item.parameters.thickness),
color: item.parameters.color,
},
},
});
const code = result?.data?.submitCalculationOrder?.code;
if (!code) {
errorMessage.value = submitMutation.error.value?.message || 'Не удалось отправить одну из позиций.';
sending.value = false;
return;
}
createdCodes.push(code);
}
sending.value = false;
cart.clearCart();
success.value = `Отправлено заявок: ${createdCodes.length}. Статус: уточнение цены.`;
}
</script>
<template>
<section class="space-y-6">
<h1 class="text-3xl font-extrabold text-[#0f2f20]">Корзина</h1>
<div class="surface-card space-y-4 rounded-3xl p-5 md:p-6">
<div v-if="counterpartyLoading.value" class="alert">
Проверяем карточку контрагента...
</div>
<div v-else-if="!isCounterpartyComplete" class="alert alert-warning">
Для оформления заявки заполните карточку контрагента в
<NuxtLink to="/profile" class="link link-hover font-semibold">профиле</NuxtLink>.
</div>
<div class="rounded-2xl border border-base-300 bg-base-100 p-4">
<h2 class="text-lg font-bold text-[#123824]">Список позиций</h2>
<div v-if="cart.items.length === 0" class="alert mt-3">
Корзина пока пустая. Добавьте товар из каталога.
</div>
<ul v-else class="mt-3 space-y-2">
<li
v-for="item in cart.items"
:key="item.productId"
class="flex flex-col gap-3 rounded-xl border border-[#d6ebde] bg-white/75 px-3 py-3 md:flex-row md:items-center md:justify-between"
>
<div>
<p class="font-semibold text-[#123824]">{{ item.productName }}</p>
<p class="text-xs opacity-70">SKU: {{ item.sku }}</p>
<p class="text-sm opacity-80">
Объем: {{ lineVolume(item.productId) }}
</p>
</div>
<div class="flex items-center gap-2">
<button class="btn btn-square btn-sm" @click="decrement(item.productId)">-</button>
<span class="min-w-8 text-center font-semibold">{{ item.quantity }}</span>
<button class="btn btn-square btn-sm" @click="increment(item.productId)">+</button>
<button class="btn btn-ghost btn-sm text-error" @click="removeFromCart(item.productId)">
Удалить
</button>
</div>
</li>
</ul>
</div>
<div class="rounded-2xl border border-base-300 bg-base-100 p-4">
<h2 class="text-lg font-bold text-[#123824]">Итого</h2>
<ul class="mt-3 space-y-2 text-sm text-[#214735]">
<li class="flex items-center justify-between">
<span>Позиций</span>
<span class="font-semibold">{{ cart.totalPositions }}</span>
</li>
<li class="flex items-center justify-between">
<span>Количество, шт.</span>
<span class="font-semibold">{{ cart.totalItems }}</span>
</li>
<li class="flex items-center justify-between">
<span>Суммарный объем</span>
<span class="font-semibold">{{ cart.totalVolume }}</span>
</li>
</ul>
</div>
<button
class="btn w-full border-0 bg-[#139957] text-white hover:bg-[#0d854a]"
:disabled="sending || counterpartyLoading.value || !isCounterpartyComplete || cart.items.length === 0"
@click="submitCart"
>
{{ sending ? 'Отправляем…' : 'Оформить заявку' }}
</button>
<div v-if="success" class="alert alert-success">{{ success }}</div>
<div v-if="errorMessage" class="alert alert-error">{{ errorMessage }}</div>
</div>
</section>
</template>