Files
web-frontend/app/pages/bonus-system/referrals/new.vue
2026-04-07 14:46:13 +07:00

140 lines
4.8 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 {
CreateBonusProgramLinkDocument,
ManagerUsersDocument,
type CreateBonusProgramLinkMutation,
type ManagerUsersQuery,
} from '~/composables/graphql/generated';
definePageMeta({
middleware: ['manager-only'],
path: '/admin/bonuses/links/new',
alias: ['/bonus-system/links/new', '/bonus-system/referrals/new', '/admin/bonuses/referrals/new'],
});
type ManagerUserItem = ManagerUsersQuery['managerUsers'][number];
const userId = ref('');
const errorMessage = ref('');
const bonusProgramLink = ref('');
const bonusProgramLinkExpiresAt = ref('');
const usersQuery = useQuery(ManagerUsersDocument);
const createBonusProgramLinkMutation = useMutation(CreateBonusProgramLinkDocument, { throws: 'never' });
const clientOptions = computed<ManagerUserItem[]>(() => (
(usersQuery.result.value?.managerUsers ?? [])
.filter((user) => user.role === 'CLIENT')
));
function userOptionLabel(user: ManagerUserItem) {
return [user.fullName, user.companyName || user.email]
.filter(Boolean)
.join(' • ');
}
async function createBonusAccountLink() {
errorMessage.value = '';
bonusProgramLink.value = '';
bonusProgramLinkExpiresAt.value = '';
if (!userId.value) {
errorMessage.value = 'Выберите клиента.';
return;
}
const bonusLinkResponse = await createBonusProgramLinkMutation.mutate({
userId: userId.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;
}
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>
<template>
<section class="space-y-6 max-w-3xl">
<UiBackHeader
to="/admin/bonuses/balances"
back-label="Назад к бонусным счетам"
title="Создать бонусный счет"
subtitle="Менеджер выбирает клиента и сразу получает ссылку, которую можно переслать ему."
/>
<div class="surface-card rounded-3xl p-5 space-y-4">
<label class="form-control">
<span class="label-text">Клиент</span>
<select v-model="userId" class="select manager-field w-full">
<option value="">Выберите клиента</option>
<option v-for="user in clientOptions" :key="user.id" :value="user.id">
{{ userOptionLabel(user) }}
</option>
</select>
</label>
<div class="mt-4">
<button
class="btn btn-primary border-0"
:disabled="createBonusProgramLinkMutation.loading.value || usersQuery.loading.value"
@click="createBonusAccountLink"
>
{{ createBonusProgramLinkMutation.loading.value ? 'Генерируем...' : 'Создать ссылку' }}
</button>
</div>
</div>
<div v-if="errorMessage" class="surface-card rounded-3xl border border-[#d27d7d] bg-[#fff4f4] p-5 text-sm text-[#8b2a2a]">
{{ errorMessage }}
</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>
</section>
</template>