Files
talorr cda36918e8 init
2026-03-27 03:36:08 +03:00

135 lines
4.6 KiB
Vue
Raw Permalink 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 Card from "primevue/card";
import Message from "primevue/message";
import Skeleton from "primevue/skeleton";
import Tag from "primevue/tag";
import type { ActiveSignalCountByBot, Bot } from "~/types";
definePageMeta({
middleware: "auth"
});
type BotCard = {
bot: Bot;
totalSignals: number;
};
const { user } = useAuth();
const loading = ref(true);
const loadError = ref("");
const botCards = ref<BotCard[]>([]);
const availableBots = computed(() => user.value?.botAccesses?.map((access) => access.bot) ?? []);
const formatSignalCount = (count: number) => {
if (count === 0) return "Нет активных сигналов";
if (count === 1) return "1 активный сигнал";
if (count < 5) return `${count} активных сигнала`;
return `${count} активных сигналов`;
};
const loadBotCards = async () => {
loading.value = true;
try {
const response = await useApi<{ items: ActiveSignalCountByBot[] }>("/signals/active-counts");
const countsByBotKey = new Map(response.items.map((item) => [item.botKey, item.activeSignals]));
const cards = availableBots.value.map((bot) => ({
bot,
totalSignals: countsByBotKey.get(bot.key) ?? 0
}));
botCards.value = cards;
loadError.value = "";
} catch (error) {
loadError.value = error instanceof Error ? error.message : "Не удалось загрузить список ботов";
} finally {
loading.value = false;
}
};
onMounted(async () => {
await loadBotCards();
});
watch(
() => availableBots.value.map((bot) => bot.key).join("|"),
(nextKeys, previousKeys) => {
if (nextKeys === previousKeys) return;
void loadBotCards();
}
);
</script>
<template>
<section class="grid gap-4">
<div
class="flex flex-col gap-4 rounded-[24px] border p-5 md:flex-row md:items-start md:justify-between"
:style="{
borderColor: 'var(--border)',
background: 'radial-gradient(circle at top right, color-mix(in srgb, var(--accent) 10%, transparent), transparent 28%), var(--surface)',
boxShadow: '0 10px 30px color-mix(in srgb, var(--text) 4%, transparent)'
}"
>
<div>
<span class="mb-2 inline-block text-xs font-semibold uppercase tracking-[0.18em] text-(--muted)">Доступные боты</span>
<h2 class="m-0 text-3xl font-semibold">Выберите бота</h2>
<p class="m-0 text-sm leading-6 text-(--muted)">
Каждая карточка открывает отдельную ленту сигналов без общего шума и лишних фильтров на старте.
</p>
</div>
<Tag class="rounded" :value="`${availableBots.length} в доступе`" severity="contrast" />
</div>
<Message v-if="loadError" severity="error" :closable="false">{{ loadError }}</Message>
<Message v-else-if="!loading && botCards.length === 0" severity="info" :closable="false">
У пользователя пока нет доступа ни к одному боту.
</Message>
<div v-if="loading" class="bot-grid">
<Card v-for="index in 6" :key="index" class="overflow-hidden rounded-[24px] border shadow-sm">
<template #content>
<div class="grid gap-3 p-4">
<div class="flex items-center justify-between gap-3">
<Skeleton width="2.75rem" height="2.75rem" borderRadius="16px" />
<Skeleton width="2.25rem" height="1.75rem" borderRadius="999px" />
</div>
<Skeleton width="3rem" height="0.7rem" />
<Skeleton width="72%" height="1.45rem" />
<Skeleton width="58%" height="0.95rem" />
<Skeleton width="100%" height="2.5rem" borderRadius="16px" />
</div>
</template>
</Card>
</div>
<div v-else class="bot-grid">
<NuxtLink
v-for="card in botCards"
:key="card.bot.id"
:to="`/bots/${card.bot.key}`"
class="bot-tile"
>
<div class="bot-tile__top">
<div class="bot-tile__icon">
<AppLogo size="26px" />
</div>
<Tag class="bot-tile__count" :value="String(card.totalSignals)" severity="contrast" />
</div>
<div class="bot-tile__body">
<p class="bot-tile__eyebrow">Бот</p>
<h2>{{ card.bot.name }}</h2>
<p class="bot-tile__meta">{{ formatSignalCount(card.totalSignals) }}</p>
</div>
<div class="bot-tile__footer">
<span class="bot-tile__action">Открыть ленту</span>
<i class="pi pi-arrow-right bot-tile__arrow" />
</div>
</NuxtLink>
</div>
</section>
</template>