Files
web-frontend/app/pages/client-orders/[id].vue
2026-04-04 10:42:40 +07:00

192 lines
6.3 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, useQuery } from '@vue/apollo-composable';
import {
BlockOrderDocument,
CompleteOrderDocument,
ManagerFinalizeOrderDocument,
ManagerOrdersDocument,
ManagerSetOrderOfferDocument,
StartOrderWorkDocument,
} from '~/composables/graphql/generated';
definePageMeta({
middleware: ['manager-only'],
});
const route = useRoute();
const orderId = computed(() => String(route.params.id || ''));
const ordersQuery = useQuery(ManagerOrdersDocument, { status: null });
const setOfferMutation = useMutation(ManagerSetOrderOfferDocument);
const finalizeMutation = useMutation(ManagerFinalizeOrderDocument);
const blockMutation = useMutation(BlockOrderDocument);
const startWorkMutation = useMutation(StartOrderWorkDocument);
const completeWorkMutation = useMutation(CompleteOrderDocument);
const currentOrder = computed(() =>
(ordersQuery.result.value?.managerOrders ?? []).find((item) => item.id === orderId.value),
);
const offerForm = reactive({
deliveryTerms: '',
deliveryFee: 0,
totalPrice: 0,
});
watchEffect(() => {
if (!currentOrder.value) {
return;
}
offerForm.deliveryTerms = currentOrder.value.deliveryTerms || 'Доставка 3-5 дней';
offerForm.deliveryFee = Number(currentOrder.value.deliveryFee ?? 1000);
offerForm.totalPrice = Number(currentOrder.value.totalPrice ?? 12500);
});
async function refetchOrder() {
await ordersQuery.refetch({ status: null });
}
async function publishOffer() {
if (!currentOrder.value) {
return;
}
await setOfferMutation.mutate({
input: {
orderId: currentOrder.value.id,
deliveryTerms: offerForm.deliveryTerms,
deliveryFee: Number(offerForm.deliveryFee),
totalPrice: Number(offerForm.totalPrice),
},
});
await refetchOrder();
}
async function approveOrder() {
if (!currentOrder.value) {
return;
}
await finalizeMutation.mutate({ orderId: currentOrder.value.id, decision: 'APPROVE' });
await refetchOrder();
}
async function rejectOrder() {
if (!currentOrder.value) {
return;
}
await finalizeMutation.mutate({ orderId: currentOrder.value.id, decision: 'REJECT' });
await refetchOrder();
}
async function blockOrder() {
if (!currentOrder.value) {
return;
}
await blockMutation.mutate({
input: {
orderId: currentOrder.value.id,
reason: 'Нужно уточнение параметров заказа.',
},
});
await refetchOrder();
}
async function startOrder() {
if (!currentOrder.value) {
return;
}
await startWorkMutation.mutate({ orderId: currentOrder.value.id });
await refetchOrder();
}
async function completeOrder() {
if (!currentOrder.value) {
return;
}
await completeWorkMutation.mutate({ orderId: currentOrder.value.id });
await refetchOrder();
}
</script>
<template>
<section class="space-y-6">
<NuxtLink to="/client-orders" class="text-sm font-semibold text-[#0d854a]"> Назад к заказам клиентов</NuxtLink>
<div v-if="ordersQuery.loading.value" class="manager-empty-state">
Загружаем заказ...
</div>
<div v-else-if="!currentOrder" class="manager-empty-state">
Заказ не найден.
</div>
<template v-else>
<div class="manager-hero">
<p class="manager-eyebrow">Заказ</p>
<h1 class="manager-title">{{ currentOrder.code }}</h1>
</div>
<div class="grid gap-4 lg:grid-cols-[1.1fr_0.9fr]">
<div class="space-y-4">
<OrdersOrderStatusTimelineCard
:status="currentOrder.status"
:created-at="currentOrder.createdAt"
/>
<div class="surface-card rounded-3xl p-5">
<h2 class="text-xl font-bold text-[#123824]">Состав заказа</h2>
<ul class="mt-4 space-y-3">
<li v-for="item in currentOrder.items" :key="item.id" class="manager-mini-card text-sm text-[#123824]">
{{ item.productName }} × {{ item.quantity }}
</li>
</ul>
</div>
<div class="surface-card rounded-3xl p-5">
<h2 class="text-xl font-bold text-[#123824]">Доставка</h2>
<div class="mt-4 grid gap-3 md:grid-cols-2">
<div class="manager-mini-card text-sm text-[#123824]">
Адрес: {{ currentOrder.deliveryAddress || 'не выбран' }}
</div>
<div class="manager-mini-card text-sm text-[#123824]">
Условия: {{ currentOrder.deliveryTerms || 'еще не указаны' }}
</div>
</div>
</div>
</div>
<div class="space-y-4">
<div class="surface-card rounded-3xl p-5">
<h2 class="text-xl font-bold text-[#123824]">Оффер</h2>
<div class="mt-4 space-y-3">
<input v-model="offerForm.deliveryTerms" class="input manager-field w-full" placeholder="Условия доставки">
<input v-model="offerForm.deliveryFee" type="number" class="input manager-field w-full" placeholder="Стоимость доставки">
<input v-model="offerForm.totalPrice" type="number" class="input manager-field w-full" placeholder="Итоговая стоимость">
<button class="btn btn-primary border-0" @click="publishOffer">Публиковать оффер</button>
</div>
</div>
<div class="surface-card rounded-3xl p-5">
<h2 class="text-xl font-bold text-[#123824]">Действия</h2>
<div class="mt-4 flex flex-wrap gap-2">
<button class="btn btn-success btn-sm border-0" @click="approveOrder">Подтвердить</button>
<button class="btn btn-error btn-sm border-0" @click="rejectOrder">Отклонить</button>
<button class="btn btn-warning btn-sm border-0" @click="blockOrder">Заблокировать</button>
<button class="btn btn-accent btn-sm border-0" :disabled="currentOrder.status !== 'CONFIRMED'" @click="startOrder">В работу</button>
<button class="btn btn-neutral btn-sm border-0" :disabled="currentOrder.status !== 'IN_PROGRESS'" @click="completeOrder">Завершить</button>
</div>
</div>
</div>
</div>
</template>
</section>
</template>