Files
web-frontend/app/pages/clients/[id].vue
2026-04-04 10:36:05 +07:00

202 lines
7.4 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 {
ManagerOrdersDocument,
ManagerUsersDetailDocument,
RegistrationRequestsDocument,
ReviewRegistrationRequestDocument,
type ManagerOrdersQuery,
type ManagerUsersDetailQuery,
type RegistrationRequestsQuery,
} from '~/composables/graphql/generated';
import { messengerConnectionAvatarSrc } from '~/composables/useMessengerConnectionPresentation';
definePageMeta({
middleware: ['manager-only'],
});
type ManagerUserItem = ManagerUsersDetailQuery['managerUsers'][number];
type ManagerOrderItem = ManagerOrdersQuery['managerOrders'][number];
type RequestItem = RegistrationRequestsQuery['registrationRequests'][number];
const route = useRoute();
const entityId = computed(() => String(route.params.id || ''));
const isRequestMode = computed(() => route.query.tab === 'requests');
const backTarget = computed(() => (
isRequestMode.value ? '/clients?tab=requests' : '/clients'
));
const usersQuery = useQuery(ManagerUsersDetailDocument);
const requestsQuery = useQuery(RegistrationRequestsDocument, {
status: null,
});
const userOrdersQuery = useQuery(ManagerOrdersDocument, () => ({
status: null,
customerId: isRequestMode.value ? null : entityId.value,
}));
const reviewMutation = useMutation(ReviewRegistrationRequestDocument);
const currentUser = computed<ManagerUserItem | null>(() =>
(usersQuery.result.value?.managerUsers ?? []).find((item: ManagerUserItem) => item.id === entityId.value) ?? null,
);
const currentRequest = computed(() =>
(requestsQuery.result.value?.registrationRequests ?? []).find((item: RequestItem) => item.id === entityId.value),
);
const currentUserOrders = computed<ManagerOrderItem[]>(() => userOrdersQuery.result.value?.managerOrders ?? []);
function userInitials(fullName: string) {
const parts = fullName
.trim()
.split(/\s+/)
.filter(Boolean)
.slice(0, 2);
if (!parts.length) {
return 'FR';
}
return parts.map((part) => part[0]?.toUpperCase() ?? '').join('');
}
async function approveRequest() {
if (!currentRequest.value) {
return;
}
await reviewMutation.mutate({
input: {
requestId: currentRequest.value.id,
decision: 'APPROVE',
},
});
await requestsQuery.refetch({ status: null });
}
async function rejectRequest() {
if (!currentRequest.value) {
return;
}
await reviewMutation.mutate({
input: {
requestId: currentRequest.value.id,
decision: 'REJECT',
rejectionReason: 'Не хватает данных для регистрации.',
},
});
await requestsQuery.refetch({ status: null });
}
</script>
<template>
<section class="space-y-6">
<NuxtLink :to="backTarget" class="text-sm font-semibold text-[#0d854a]"> Назад к пользователям</NuxtLink>
<template v-if="isRequestMode">
<div v-if="requestsQuery.loading.value" class="manager-empty-state">
Загружаем карточку клиента...
</div>
<div v-else-if="!currentRequest" class="manager-empty-state">
Карточка клиента не найдена.
</div>
<template v-else>
<div class="flex flex-col gap-3 md:flex-row md:items-start md:justify-between">
<div class="manager-hero">
<p class="manager-eyebrow">Заявка</p>
<h1 class="manager-title">{{ currentRequest.companyName }}</h1>
<p class="manager-copy">Контакт: {{ currentRequest.contactName }} · {{ currentRequest.email }}</p>
</div>
<div v-if="currentRequest.status === 'PENDING'" class="flex flex-wrap gap-2">
<button class="btn btn-success border-0" @click="approveRequest">Одобрить</button>
<button class="btn btn-error border-0" @click="rejectRequest">Отклонить</button>
</div>
</div>
<div class="grid gap-4 lg:grid-cols-3">
<div class="manager-stat-card">
<p class="manager-stat-label">Статус</p>
<p class="manager-stat-value text-lg">
{{ currentRequest.status === 'APPROVED' ? 'Активен' : currentRequest.status === 'REJECTED' ? 'Отклонен' : 'На проверке' }}
</p>
</div>
<div class="manager-stat-card">
<p class="manager-stat-label">Дата заявки</p>
<p class="manager-stat-value text-lg">{{ new Date(currentRequest.createdAt).toLocaleDateString() }}</p>
</div>
<div class="manager-stat-card">
<p class="manager-stat-label">ИНН</p>
<p class="manager-stat-value text-lg">{{ currentRequest.inn || 'Не указан' }}</p>
</div>
</div>
</template>
</template>
<template v-else>
<div v-if="usersQuery.loading.value || userOrdersQuery.loading.value" class="manager-empty-state">
Загружаем пользователя...
</div>
<div v-else-if="!currentUser" class="manager-empty-state">
Пользователь не найден.
</div>
<template v-else>
<div class="surface-card flex flex-col gap-6 rounded-[36px] p-6 md:flex-row md:items-center">
<div class="shrink-0">
<img
v-if="messengerConnectionAvatarSrc(currentUser.telegramConnection)"
:src="messengerConnectionAvatarSrc(currentUser.telegramConnection)"
:alt="currentUser.fullName"
class="h-28 w-28 rounded-[36px] object-cover shadow-[0_12px_30px_rgba(18,56,36,0.14)]"
>
<div
v-else
class="flex h-28 w-28 items-center justify-center rounded-[36px] bg-[linear-gradient(135deg,#dff7e9_0%,#c2ead3_100%)] text-4xl font-black text-[#123824]"
>
{{ userInitials(currentUser.fullName) }}
</div>
</div>
<div class="space-y-2">
<p class="manager-eyebrow">Пользователь</p>
<h1 class="manager-title">{{ currentUser.fullName }}</h1>
<p class="text-sm text-[#466653]">{{ currentUser.email }}</p>
<p v-if="currentUser.companyName" class="text-sm text-[#466653]">{{ currentUser.companyName }}</p>
</div>
</div>
<div class="surface-card rounded-3xl p-5">
<h2 class="text-xl font-bold text-[#123824]">Заказы пользователя</h2>
<div v-if="currentUserOrders.length === 0" class="manager-empty-state mt-4">
У пользователя пока нет заказов.
</div>
<div v-else class="mt-4 space-y-3">
<NuxtLink
v-for="order in currentUserOrders"
:key="order.id"
:to="`/client-orders/${order.id}`"
class="manager-mini-card block"
>
<div class="flex flex-wrap items-start justify-between gap-3">
<div class="space-y-1">
<p class="text-sm font-semibold text-[#123824]">{{ order.code }}</p>
<p class="text-sm text-[#355947]">{{ new Date(order.createdAt).toLocaleString() }}</p>
<p v-if="order.deliveryAddress" class="text-sm text-[#355947]">{{ order.deliveryAddress }}</p>
</div>
<p class="text-sm font-semibold text-[#466653]">{{ order.status }}</p>
</div>
</NuxtLink>
</div>
</div>
</template>
</template>
</section>
</template>