Add bonus relation creation entrypoint
This commit is contained in:
@@ -308,7 +308,17 @@ function compactProductTitle(product: ProductCard) {
|
|||||||
: activeTab === 'withdrawals'
|
: activeTab === 'withdrawals'
|
||||||
? 'Номер выплаты, клиент или сумма'
|
? 'Номер выплаты, клиент или сумма'
|
||||||
: 'Название или номинал'"
|
: 'Название или номинал'"
|
||||||
/>
|
>
|
||||||
|
<template #controls>
|
||||||
|
<NuxtLink
|
||||||
|
v-if="activeTab === 'balances'"
|
||||||
|
to="/admin/bonuses/referrals/new"
|
||||||
|
class="btn btn-primary border-0"
|
||||||
|
>
|
||||||
|
Добавить
|
||||||
|
</NuxtLink>
|
||||||
|
</template>
|
||||||
|
</UiSectionSearchHero>
|
||||||
|
|
||||||
<template v-if="activeTab === 'balances'">
|
<template v-if="activeTab === 'balances'">
|
||||||
<div v-if="balancesQuery.loading.value || referralLinksQuery.loading.value || usersQuery.loading.value" class="manager-empty-state">
|
<div v-if="balancesQuery.loading.value || referralLinksQuery.loading.value || usersQuery.loading.value" class="manager-empty-state">
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useMutation, useQuery } from '@vue/apollo-composable';
|
import { useMutation, useQuery } from '@vue/apollo-composable';
|
||||||
import {
|
import {
|
||||||
|
CreateBonusProgramLinkDocument,
|
||||||
CreateReferralDocument,
|
CreateReferralDocument,
|
||||||
ManagerReferralLinksDocument,
|
ManagerReferralLinksDocument,
|
||||||
ManagerUsersDocument,
|
ManagerUsersDocument,
|
||||||
|
type CreateBonusProgramLinkMutation,
|
||||||
type ManagerReferralLinksQuery,
|
type ManagerReferralLinksQuery,
|
||||||
type ManagerUsersQuery,
|
type ManagerUsersQuery,
|
||||||
} from '~/composables/graphql/generated';
|
} from '~/composables/graphql/generated';
|
||||||
@@ -22,9 +24,12 @@ const refereeUserId = ref('');
|
|||||||
const bonusPercent = ref(5);
|
const bonusPercent = ref(5);
|
||||||
const createdReferralId = ref('');
|
const createdReferralId = ref('');
|
||||||
const errorMessage = ref('');
|
const errorMessage = ref('');
|
||||||
|
const bonusProgramLink = ref('');
|
||||||
|
const bonusProgramLinkExpiresAt = ref('');
|
||||||
const usersQuery = useQuery(ManagerUsersDocument);
|
const usersQuery = useQuery(ManagerUsersDocument);
|
||||||
const linksQuery = useQuery(ManagerReferralLinksDocument);
|
const linksQuery = useQuery(ManagerReferralLinksDocument);
|
||||||
const createReferralMutation = useMutation(CreateReferralDocument);
|
const createReferralMutation = useMutation(CreateReferralDocument, { throws: 'never' });
|
||||||
|
const createBonusProgramLinkMutation = useMutation(CreateBonusProgramLinkDocument, { throws: 'never' });
|
||||||
|
|
||||||
const clientOptions = computed<ManagerUserItem[]>(() => (
|
const clientOptions = computed<ManagerUserItem[]>(() => (
|
||||||
(usersQuery.result.value?.managerUsers ?? [])
|
(usersQuery.result.value?.managerUsers ?? [])
|
||||||
@@ -64,6 +69,8 @@ function userOptionLabel(user: ManagerUserItem) {
|
|||||||
async function createReferral() {
|
async function createReferral() {
|
||||||
createdReferralId.value = '';
|
createdReferralId.value = '';
|
||||||
errorMessage.value = '';
|
errorMessage.value = '';
|
||||||
|
bonusProgramLink.value = '';
|
||||||
|
bonusProgramLinkExpiresAt.value = '';
|
||||||
|
|
||||||
if (!referrerUserId.value || !refereeUserId.value) {
|
if (!referrerUserId.value || !refereeUserId.value) {
|
||||||
errorMessage.value = 'Выберите обоих клиентов для связки.';
|
errorMessage.value = 'Выберите обоих клиентов для связки.';
|
||||||
@@ -81,23 +88,47 @@ async function createReferral() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
const response = await createReferralMutation.mutate({
|
||||||
const response = await createReferralMutation.mutate({
|
input: {
|
||||||
input: {
|
referrerUserId: referrerUserId.value,
|
||||||
referrerUserId: referrerUserId.value,
|
refereeUserId: refereeUserId.value,
|
||||||
refereeUserId: refereeUserId.value,
|
bonusPercent: normalizedBonusPercent,
|
||||||
bonusPercent: normalizedBonusPercent,
|
},
|
||||||
},
|
});
|
||||||
});
|
|
||||||
|
|
||||||
createdReferralId.value = response?.data?.createReferral.id ?? '';
|
if (!response?.data?.createReferral.id) {
|
||||||
refereeUserId.value = '';
|
errorMessage.value = createReferralMutation.error.value?.message || 'Не удалось создать бонусную связку.';
|
||||||
await linksQuery.refetch();
|
return;
|
||||||
} catch (error: unknown) {
|
|
||||||
errorMessage.value = error instanceof Error
|
|
||||||
? error.message
|
|
||||||
: 'Не удалось создать бонусную связку.';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
createdReferralId.value = response.data.createReferral.id;
|
||||||
|
|
||||||
|
const bonusLinkResponse = await createBonusProgramLinkMutation.mutate({
|
||||||
|
userId: referrerUserId.value,
|
||||||
|
});
|
||||||
|
|
||||||
|
const bonusLinkPayload: CreateBonusProgramLinkMutation['createBonusProgramLink'] | undefined = bonusLinkResponse?.data?.createBonusProgramLink;
|
||||||
|
if (!bonusLinkPayload?.url) {
|
||||||
|
errorMessage.value = createBonusProgramLinkMutation.error.value?.message || 'Связка создана, но не удалось сгенерировать ссылку.';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bonusProgramLink.value = bonusLinkPayload.url;
|
||||||
|
bonusProgramLinkExpiresAt.value = bonusLinkPayload.expiresAt;
|
||||||
|
refereeUserId.value = '';
|
||||||
|
await linksQuery.refetch();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function copyBonusProgramLink() {
|
||||||
|
if (!bonusProgramLink.value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await navigator.clipboard.writeText(bonusProgramLink.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatDateTime(value: string) {
|
||||||
|
return new Date(value).toLocaleString('ru-RU');
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -106,8 +137,8 @@ async function createReferral() {
|
|||||||
<UiBackHeader
|
<UiBackHeader
|
||||||
to="/admin/bonuses/balances"
|
to="/admin/bonuses/balances"
|
||||||
back-label="Назад к бонусным счетам"
|
back-label="Назад к бонусным счетам"
|
||||||
title="Создать бонусную связку клиентов"
|
title="Создать бонусную связку"
|
||||||
subtitle="Первый клиент получает процент бонуса, когда заказ второго клиента переходит в статус доставленного."
|
subtitle="После создания менеджер сразу получает ссылку, которую можно переслать клиенту."
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div class="surface-card rounded-3xl p-5 space-y-4">
|
<div class="surface-card rounded-3xl p-5 space-y-4">
|
||||||
@@ -150,7 +181,7 @@ async function createReferral() {
|
|||||||
:disabled="createReferralMutation.loading.value || usersQuery.loading.value"
|
:disabled="createReferralMutation.loading.value || usersQuery.loading.value"
|
||||||
@click="createReferral"
|
@click="createReferral"
|
||||||
>
|
>
|
||||||
{{ createReferralMutation.loading.value ? 'Сохраняем...' : 'Создать связь' }}
|
{{ createReferralMutation.loading.value || createBonusProgramLinkMutation.loading.value ? 'Сохраняем...' : 'Создать связь' }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -163,6 +194,38 @@ async function createReferral() {
|
|||||||
Создана связь: <span class="font-semibold">{{ createdReferralId }}</span>
|
Создана связь: <span class="font-semibold">{{ createdReferralId }}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<article v-if="bonusProgramLink" class="surface-card rounded-3xl p-5 space-y-4">
|
||||||
|
<div class="space-y-2">
|
||||||
|
<p class="text-sm font-semibold text-[#123824]">Ссылка в бонусный кабинет</p>
|
||||||
|
<p class="text-sm text-[#466653]">
|
||||||
|
Эту ссылку менеджер может сразу отправить клиенту.
|
||||||
|
</p>
|
||||||
|
<div class="rounded-[20px] bg-[#f8fbf9] px-4 py-3 text-sm font-semibold text-[#123824] break-all">
|
||||||
|
{{ bonusProgramLink }}
|
||||||
|
</div>
|
||||||
|
<p v-if="bonusProgramLinkExpiresAt" class="text-xs text-[#5c7b69]">
|
||||||
|
Действует до {{ formatDateTime(bonusProgramLinkExpiresAt) }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex flex-wrap gap-3">
|
||||||
|
<a
|
||||||
|
:href="bonusProgramLink"
|
||||||
|
target="_blank"
|
||||||
|
rel="noreferrer"
|
||||||
|
class="btn rounded-full border border-[#d7e9de] bg-white px-5 text-[#123824] hover:bg-[#f3f8f5]"
|
||||||
|
>
|
||||||
|
Открыть
|
||||||
|
</a>
|
||||||
|
<button
|
||||||
|
class="btn rounded-full border-0 bg-[#139957] px-5 text-white hover:bg-[#0d854a]"
|
||||||
|
@click="copyBonusProgramLink"
|
||||||
|
>
|
||||||
|
Скопировать
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
|
||||||
<div class="space-y-4">
|
<div class="space-y-4">
|
||||||
<div class="flex items-center justify-between gap-3">
|
<div class="flex items-center justify-between gap-3">
|
||||||
<h2 class="text-lg font-bold text-[#123824]">Текущие бонусные связки</h2>
|
<h2 class="text-lg font-bold text-[#123824]">Текущие бонусные связки</h2>
|
||||||
|
|||||||
Reference in New Issue
Block a user