242 lines
8.0 KiB
Vue
242 lines
8.0 KiB
Vue
<script setup lang="ts">
|
||
import { useMutation, useQuery } from '@vue/apollo-composable';
|
||
import {
|
||
MeDocument,
|
||
MyMessengerConnectionsDocument,
|
||
MyNotificationHistoryDocument,
|
||
SendTestMessengerMessageDocument,
|
||
} from '~/composables/graphql/generated';
|
||
import { useMessengerStart } from '~/composables/useMessengerStart';
|
||
|
||
const selectedChannel = ref<'TELEGRAM' | 'MAX'>('TELEGRAM');
|
||
const customMessage = ref('Тест канала уведомлений Fregat');
|
||
const feedback = ref('');
|
||
const config = useRuntimeConfig();
|
||
const { openMessengerBot, pendingChannel } = useMessengerStart();
|
||
|
||
const meQuery = useQuery(MeDocument);
|
||
const connectionsQuery = useQuery(MyMessengerConnectionsDocument);
|
||
const historyQuery = useQuery(MyNotificationHistoryDocument, () => ({
|
||
channel: selectedChannel.value,
|
||
limit: 50,
|
||
}));
|
||
const sendTestMutation = useMutation(SendTestMessengerMessageDocument);
|
||
|
||
watch(selectedChannel, () => {
|
||
feedback.value = '';
|
||
historyQuery.refetch({
|
||
channel: selectedChannel.value,
|
||
limit: 50,
|
||
});
|
||
});
|
||
|
||
const activeConnection = computed(() =>
|
||
connectionsQuery.result.value?.myMessengerConnections?.find(
|
||
(item: { type: 'TELEGRAM' | 'MAX'; isActive: boolean; channelId: string }) =>
|
||
item.type === selectedChannel.value && item.isActive,
|
||
),
|
||
);
|
||
|
||
const telegramConnection = computed(() =>
|
||
connectionsQuery.result.value?.myMessengerConnections?.find(
|
||
(item: { type: 'TELEGRAM' | 'MAX'; isActive: boolean; channelId: string }) =>
|
||
item.type === 'TELEGRAM' && item.isActive,
|
||
),
|
||
);
|
||
|
||
const maxConnection = computed(() =>
|
||
connectionsQuery.result.value?.myMessengerConnections?.find(
|
||
(item: { type: 'TELEGRAM' | 'MAX'; isActive: boolean; channelId: string }) =>
|
||
item.type === 'MAX' && item.isActive,
|
||
),
|
||
);
|
||
|
||
function buildBotConnectUrl(baseUrl: string) {
|
||
const email = meQuery.result.value?.me?.email?.trim().toLowerCase();
|
||
if (!email || !baseUrl) {
|
||
return '';
|
||
}
|
||
|
||
return baseUrl;
|
||
}
|
||
|
||
const telegramConnectUrl = computed(() => buildBotConnectUrl(config.public.telegramBotUrl || ''));
|
||
const maxConnectUrl = computed(() => buildBotConnectUrl(config.public.maxBotUrl || ''));
|
||
|
||
async function connectMessenger(channel: 'TELEGRAM' | 'MAX') {
|
||
const baseUrl = channel === 'TELEGRAM' ? telegramConnectUrl.value : maxConnectUrl.value;
|
||
if (!baseUrl) {
|
||
return;
|
||
}
|
||
|
||
await openMessengerBot({
|
||
channel,
|
||
baseUrl,
|
||
});
|
||
}
|
||
|
||
async function sendTest() {
|
||
feedback.value = '';
|
||
if (!activeConnection.value) {
|
||
feedback.value = `Канал ${selectedChannel.value} ещё не подключен. Сначала подключите его через бота.`;
|
||
return;
|
||
}
|
||
|
||
const result = await sendTestMutation.mutate({
|
||
type: selectedChannel.value,
|
||
message: customMessage.value || undefined,
|
||
});
|
||
|
||
const payload = result?.data?.sendTestMessengerMessage;
|
||
if (!payload) {
|
||
feedback.value = 'Не удалось отправить тестовое сообщение.';
|
||
return;
|
||
}
|
||
|
||
feedback.value = payload.success
|
||
? `Отправлено в ${payload.type}: ${payload.detail}`
|
||
: `Ошибка отправки: ${payload.detail}`;
|
||
|
||
await historyQuery.refetch({
|
||
channel: selectedChannel.value,
|
||
limit: 50,
|
||
});
|
||
}
|
||
</script>
|
||
|
||
<template>
|
||
<section class="space-y-6">
|
||
<div>
|
||
<h1 class="text-3xl font-extrabold text-[#0f2f20]">Уведомления</h1>
|
||
</div>
|
||
|
||
<div class="surface-card rounded-3xl p-5">
|
||
<div class="space-y-4">
|
||
<h2 class="text-xl font-bold text-[#123824]">Подключение каналов</h2>
|
||
|
||
<div class="rounded-2xl border border-[#d6ebde] bg-white/75 p-4">
|
||
<div class="flex flex-col gap-3 md:flex-row md:items-center md:justify-between">
|
||
<div>
|
||
<p class="font-semibold">Telegram</p>
|
||
<p class="text-sm opacity-80">
|
||
{{
|
||
telegramConnection
|
||
? `Подключен: ${telegramConnection.channelId}`
|
||
: 'Не подключен'
|
||
}}
|
||
</p>
|
||
</div>
|
||
|
||
<button
|
||
class="btn btn-secondary"
|
||
:class="{ 'btn-disabled pointer-events-none': !telegramConnectUrl }"
|
||
:disabled="pendingChannel === 'TELEGRAM' || !telegramConnectUrl"
|
||
@click="connectMessenger('TELEGRAM')"
|
||
>
|
||
{{
|
||
pendingChannel === 'TELEGRAM'
|
||
? 'Открываем Telegram…'
|
||
: telegramConnection
|
||
? 'Переподключить Telegram'
|
||
: 'Подключить Telegram'
|
||
}}
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="rounded-2xl border border-[#d6ebde] bg-white/75 p-4">
|
||
<div class="flex flex-col gap-3 md:flex-row md:items-center md:justify-between">
|
||
<div>
|
||
<p class="font-semibold">Max</p>
|
||
<p class="text-sm opacity-80">
|
||
{{
|
||
maxConnection
|
||
? `Подключен: ${maxConnection.channelId}`
|
||
: 'Не подключен'
|
||
}}
|
||
</p>
|
||
</div>
|
||
|
||
<button
|
||
class="btn btn-accent"
|
||
:class="{ 'btn-disabled pointer-events-none': !maxConnectUrl }"
|
||
:disabled="pendingChannel === 'MAX' || !maxConnectUrl"
|
||
@click="connectMessenger('MAX')"
|
||
>
|
||
{{
|
||
pendingChannel === 'MAX'
|
||
? 'Открываем Max…'
|
||
: maxConnection
|
||
? 'Переподключить Max'
|
||
: 'Подключить Max'
|
||
}}
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="surface-card rounded-3xl p-5">
|
||
<div class="space-y-3">
|
||
<div class="tabs tabs-boxed w-fit">
|
||
<button
|
||
class="tab"
|
||
:class="{ 'tab-active': selectedChannel === 'TELEGRAM' }"
|
||
@click="selectedChannel = 'TELEGRAM'"
|
||
>
|
||
Telegram
|
||
</button>
|
||
<button
|
||
class="tab"
|
||
:class="{ 'tab-active': selectedChannel === 'MAX' }"
|
||
@click="selectedChannel = 'MAX'"
|
||
>
|
||
Max
|
||
</button>
|
||
</div>
|
||
|
||
<p class="text-sm opacity-80">
|
||
Активный канал:
|
||
<span class="font-semibold">
|
||
{{ activeConnection ? activeConnection.channelId : 'не подключен' }}
|
||
</span>
|
||
</p>
|
||
|
||
<label class="form-control">
|
||
<span class="label-text">Тестовое сообщение</span>
|
||
<textarea v-model="customMessage" class="textarea textarea-bordered" rows="3" />
|
||
</label>
|
||
|
||
<button
|
||
class="btn w-fit border-0 bg-[#139957] text-white hover:bg-[#0d854a]"
|
||
:disabled="sendTestMutation.loading.value || !activeConnection"
|
||
@click="sendTest"
|
||
>
|
||
Отправить тест
|
||
</button>
|
||
|
||
<div v-if="feedback" class="alert" :class="feedback.startsWith('Ошибка') ? 'alert-error' : 'alert-success'">
|
||
{{ feedback }}
|
||
</div>
|
||
|
||
<h2 class="text-xl font-bold text-[#123824]">История по каналу {{ selectedChannel }}</h2>
|
||
<div v-if="historyQuery.loading.value" class="alert border-0 bg-white/75">Загрузка истории...</div>
|
||
<div v-else-if="(historyQuery.result.value?.myNotificationHistory?.length ?? 0) === 0" class="alert">
|
||
История пока пустая.
|
||
</div>
|
||
<ul v-else class="space-y-3">
|
||
<li
|
||
v-for="item in historyQuery.result.value?.myNotificationHistory ?? []"
|
||
:key="item.id"
|
||
class="rounded-xl border border-[#d6ebde] bg-white/75 p-3"
|
||
>
|
||
<p class="font-semibold">{{ item.title }}</p>
|
||
<p class="text-sm opacity-80">{{ item.message }}</p>
|
||
<p class="text-xs opacity-60">{{ new Date(item.createdAt).toLocaleString() }}</p>
|
||
</li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
</template>
|