Redesign client cabinet UI with capsule nav and card layouts
This commit is contained in:
143
app/pages/notifications.vue
Normal file
143
app/pages/notifications.vue
Normal file
@@ -0,0 +1,143 @@
|
||||
<script setup lang="ts">
|
||||
import { useMutation, useQuery } from '@vue/apollo-composable';
|
||||
import {
|
||||
MyMessengerConnectionsDocument,
|
||||
MyNotificationHistoryDocument,
|
||||
SendTestMessengerMessageDocument,
|
||||
} from '~/composables/graphql/generated';
|
||||
|
||||
const selectedChannel = ref<'TELEGRAM' | 'MAX'>('TELEGRAM');
|
||||
const channelId = ref('');
|
||||
const customMessage = ref('Тест канала уведомлений Fregat');
|
||||
const feedback = ref('');
|
||||
|
||||
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,
|
||||
),
|
||||
);
|
||||
|
||||
async function sendTest() {
|
||||
feedback.value = '';
|
||||
const result = await sendTestMutation.mutate({
|
||||
type: selectedChannel.value,
|
||||
channelId: channelId.value || undefined,
|
||||
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>
|
||||
<p class="mt-1 text-sm text-[#28543f]/80">Управление Telegram и Max в едином стиле кабинета.</p>
|
||||
</div>
|
||||
|
||||
<div class="surface-card rounded-3xl p-5">
|
||||
<div class="space-y-4">
|
||||
<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>
|
||||
<input
|
||||
v-model="channelId"
|
||||
class="input input-bordered border-[#d0e8d8] bg-white/80"
|
||||
placeholder="если пусто, берется активный подключенный канал"
|
||||
>
|
||||
</label>
|
||||
|
||||
<label class="form-control">
|
||||
<span class="label-text">Тестовое сообщение</span>
|
||||
<textarea v-model="customMessage" class="textarea textarea-bordered border-[#d0e8d8] bg-white/80" rows="3" />
|
||||
</label>
|
||||
|
||||
<button
|
||||
class="btn w-fit border-0 bg-[#139957] text-white hover:bg-[#0d854a]"
|
||||
:disabled="sendTestMutation.loading.value"
|
||||
@click="sendTest"
|
||||
>
|
||||
Отправить тест
|
||||
</button>
|
||||
|
||||
<div v-if="feedback" class="alert" :class="feedback.startsWith('Ошибка') ? 'alert-error' : 'alert-success'">
|
||||
{{ feedback }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="surface-card rounded-3xl p-5">
|
||||
<div class="space-y-3">
|
||||
<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>
|
||||
Reference in New Issue
Block a user