Files
web-frontend/app/pages/bonus-system/index.vue
2026-04-04 09:29:16 +07:00

163 lines
5.1 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 { useQuery } from '@vue/apollo-composable';
import {
ManagerBonusBalancesDocument,
ReferralStatsDocument,
type ManagerBonusBalancesQuery,
type ReferralStatsQuery,
} from '~/composables/graphql/generated';
definePageMeta({
middleware: ['manager-only'],
});
type BalanceItem = ManagerBonusBalancesQuery['managerBonusBalances'][number];
type WithdrawalItem = ReferralStatsQuery['referralStats']['pendingWithdrawals'][number];
const route = useRoute();
const router = useRouter();
const search = ref('');
const balancesQuery = useQuery(ManagerBonusBalancesDocument);
const bonusQuery = useQuery(ReferralStatsDocument);
const activeTab = computed<'balances' | 'withdrawals'>(() => (
route.query.tab === 'withdrawals' ? 'withdrawals' : 'balances'
));
function setTab(tab: 'balances' | 'withdrawals') {
void router.replace({
query: {
...route.query,
tab,
},
});
}
const balances = computed<BalanceItem[]>(() => balancesQuery.result.value?.managerBonusBalances ?? []);
const withdrawals = computed<WithdrawalItem[]>(() => bonusQuery.result.value?.referralStats.pendingWithdrawals ?? []);
const filteredBalances = computed(() => {
const query = search.value.trim().toLowerCase();
return balances.value.filter((item) => {
if (!query) {
return true;
}
return [
item.fullName,
item.email,
item.companyName || '',
String(item.balance),
String(item.pendingWithdrawalAmount),
]
.join(' ')
.toLowerCase()
.includes(query);
});
});
const filteredWithdrawals = computed(() => {
const query = search.value.trim().toLowerCase();
return withdrawals.value.filter((item) => {
if (!query) {
return true;
}
return [
item.requesterId,
String(item.amount),
item.status,
item.reviewComment || '',
]
.join(' ')
.toLowerCase()
.includes(query);
});
});
</script>
<template>
<section class="space-y-6">
<UiSectionSearchHero
v-model="search"
title="Бонусы"
:search-placeholder="activeTab === 'balances' ? 'Пользователь, email, компания или сумма' : 'Пользователь, сумма или статус'"
/>
<div class="tabs tabs-boxed w-fit bg-white">
<button
class="tab"
:class="{ 'tab-active': activeTab === 'balances' }"
@click="setTab('balances')"
>
Балансы
</button>
<button
class="tab"
:class="{ 'tab-active': activeTab === 'withdrawals' }"
@click="setTab('withdrawals')"
>
Заявки на выплату
</button>
</div>
<template v-if="activeTab === 'balances'">
<div v-if="balancesQuery.loading.value" class="manager-empty-state">
Загружаем балансы...
</div>
<div v-else-if="filteredBalances.length === 0" class="manager-empty-state">
Балансы по текущему запросу не найдены.
</div>
<div v-else class="grid gap-4 lg:grid-cols-2 xl:grid-cols-3">
<article
v-for="item in filteredBalances"
:key="item.userId"
class="surface-card rounded-3xl p-5"
>
<div class="space-y-1">
<h2 class="text-lg font-bold text-[#123824]">{{ item.fullName }}</h2>
<p class="text-sm text-[#466653]">{{ item.email }}</p>
<p v-if="item.companyName" class="text-sm text-[#466653]">{{ item.companyName }}</p>
</div>
<div class="mt-4 space-y-2 text-sm text-[#355947]">
<p>Баланс: <span class="font-semibold text-[#123824]">{{ item.balance }}</span></p>
<p>В ожидании выплаты: {{ item.pendingWithdrawalAmount }}</p>
<p>Транзакций: {{ item.transactionsCount }}</p>
</div>
</article>
</div>
</template>
<template v-else>
<div v-if="bonusQuery.loading.value" class="manager-empty-state">
Загружаем заявки...
</div>
<div v-else-if="filteredWithdrawals.length === 0" class="manager-empty-state">
Активных заявок на выплату сейчас нет.
</div>
<div v-else class="space-y-4">
<article
v-for="withdrawal in filteredWithdrawals"
:key="withdrawal.id"
class="surface-card rounded-3xl px-5 py-5"
>
<div class="flex flex-col gap-3 md:flex-row md:items-start md:justify-between">
<div class="space-y-1">
<p class="text-sm font-semibold text-[#123824]">Пользователь: {{ withdrawal.requesterId }}</p>
<p class="text-sm text-[#355947]">Сумма: {{ withdrawal.amount }}</p>
<p class="text-xs text-[#5c7b69]">{{ new Date(withdrawal.createdAt).toLocaleString() }}</p>
</div>
<NuxtLink :to="`/bonus-system/withdrawals/${withdrawal.id}`" class="btn btn-accent btn-sm border-0">
Проверить выплату
</NuxtLink>
</div>
</article>
</div>
</template>
</section>
</template>