feat(notifications): switch messenger connect flow to bot links
This commit is contained in:
@@ -523,6 +523,11 @@ export type ConsumeLoginTokenMutationVariables = Exact<{
|
|||||||
|
|
||||||
export type ConsumeLoginTokenMutation = { __typename?: 'Mutation', consumeLoginToken: { __typename?: 'AuthSession', accessToken: string, expiresAt: any, user: { __typename?: 'User', id: string, email: string, fullName: string, role: UserRole, company?: { __typename?: 'Company', id: string } | null } } };
|
export type ConsumeLoginTokenMutation = { __typename?: 'Mutation', consumeLoginToken: { __typename?: 'AuthSession', accessToken: string, expiresAt: any, user: { __typename?: 'User', id: string, email: string, fullName: string, role: UserRole, company?: { __typename?: 'Company', id: string } | null } } };
|
||||||
|
|
||||||
|
export type MeQueryVariables = Exact<{ [key: string]: never; }>;
|
||||||
|
|
||||||
|
|
||||||
|
export type MeQuery = { __typename?: 'Query', me?: { __typename?: 'User', id: string, email: string } | null };
|
||||||
|
|
||||||
export type RegisterSelfMutationVariables = Exact<{
|
export type RegisterSelfMutationVariables = Exact<{
|
||||||
input: RegisterSelfInput;
|
input: RegisterSelfInput;
|
||||||
}>;
|
}>;
|
||||||
@@ -650,6 +655,34 @@ export function useConsumeLoginTokenMutation(options: VueApolloComposable.UseMut
|
|||||||
return VueApolloComposable.useMutation<ConsumeLoginTokenMutation, ConsumeLoginTokenMutationVariables>(ConsumeLoginTokenDocument, options);
|
return VueApolloComposable.useMutation<ConsumeLoginTokenMutation, ConsumeLoginTokenMutationVariables>(ConsumeLoginTokenDocument, options);
|
||||||
}
|
}
|
||||||
export type ConsumeLoginTokenMutationCompositionFunctionResult = VueApolloComposable.UseMutationReturn<ConsumeLoginTokenMutation, ConsumeLoginTokenMutationVariables>;
|
export type ConsumeLoginTokenMutationCompositionFunctionResult = VueApolloComposable.UseMutationReturn<ConsumeLoginTokenMutation, ConsumeLoginTokenMutationVariables>;
|
||||||
|
export const MeDocument = gql`
|
||||||
|
query Me {
|
||||||
|
me {
|
||||||
|
id
|
||||||
|
email
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* __useMeQuery__
|
||||||
|
*
|
||||||
|
* To run a query within a Vue component, call `useMeQuery` and pass it any options that fit your needs.
|
||||||
|
* When your component renders, `useMeQuery` returns an object from Apollo Client that contains result, loading and error properties
|
||||||
|
* you can use to render your UI.
|
||||||
|
*
|
||||||
|
* @param options that will be passed into the query, supported options are listed on: https://v4.apollo.vuejs.org/guide-composable/query.html#options;
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* const { result, loading, error } = useMeQuery();
|
||||||
|
*/
|
||||||
|
export function useMeQuery(options: VueApolloComposable.UseQueryOptions<MeQuery, MeQueryVariables> | VueCompositionApi.Ref<VueApolloComposable.UseQueryOptions<MeQuery, MeQueryVariables>> | ReactiveFunction<VueApolloComposable.UseQueryOptions<MeQuery, MeQueryVariables>> = {}) {
|
||||||
|
return VueApolloComposable.useQuery<MeQuery, MeQueryVariables>(MeDocument, {}, options);
|
||||||
|
}
|
||||||
|
export function useMeLazyQuery(options: VueApolloComposable.UseQueryOptions<MeQuery, MeQueryVariables> | VueCompositionApi.Ref<VueApolloComposable.UseQueryOptions<MeQuery, MeQueryVariables>> | ReactiveFunction<VueApolloComposable.UseQueryOptions<MeQuery, MeQueryVariables>> = {}) {
|
||||||
|
return VueApolloComposable.useLazyQuery<MeQuery, MeQueryVariables>(MeDocument, {}, options);
|
||||||
|
}
|
||||||
|
export type MeQueryCompositionFunctionResult = VueApolloComposable.UseQueryReturn<MeQuery, MeQueryVariables>;
|
||||||
export const RegisterSelfDocument = gql`
|
export const RegisterSelfDocument = gql`
|
||||||
mutation RegisterSelf($input: RegisterSelfInput!) {
|
mutation RegisterSelf($input: RegisterSelfInput!) {
|
||||||
registerSelf(input: $input) {
|
registerSelf(input: $input) {
|
||||||
|
|||||||
@@ -1,16 +1,18 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useMutation, useQuery } from '@vue/apollo-composable';
|
import { useMutation, useQuery } from '@vue/apollo-composable';
|
||||||
import {
|
import {
|
||||||
|
MeDocument,
|
||||||
MyMessengerConnectionsDocument,
|
MyMessengerConnectionsDocument,
|
||||||
MyNotificationHistoryDocument,
|
MyNotificationHistoryDocument,
|
||||||
SendTestMessengerMessageDocument,
|
SendTestMessengerMessageDocument,
|
||||||
} from '~/composables/graphql/generated';
|
} from '~/composables/graphql/generated';
|
||||||
|
|
||||||
const selectedChannel = ref<'TELEGRAM' | 'MAX'>('TELEGRAM');
|
const selectedChannel = ref<'TELEGRAM' | 'MAX'>('TELEGRAM');
|
||||||
const channelId = ref('');
|
|
||||||
const customMessage = ref('Тест канала уведомлений Fregat');
|
const customMessage = ref('Тест канала уведомлений Fregat');
|
||||||
const feedback = ref('');
|
const feedback = ref('');
|
||||||
|
const config = useRuntimeConfig();
|
||||||
|
|
||||||
|
const meQuery = useQuery(MeDocument);
|
||||||
const connectionsQuery = useQuery(MyMessengerConnectionsDocument);
|
const connectionsQuery = useQuery(MyMessengerConnectionsDocument);
|
||||||
const historyQuery = useQuery(MyNotificationHistoryDocument, () => ({
|
const historyQuery = useQuery(MyNotificationHistoryDocument, () => ({
|
||||||
channel: selectedChannel.value,
|
channel: selectedChannel.value,
|
||||||
@@ -33,11 +35,43 @@ const activeConnection = computed(() =>
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
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 '';
|
||||||
|
}
|
||||||
|
|
||||||
|
const payload = encodeURIComponent(`login:${email}`);
|
||||||
|
const separator = baseUrl.includes('?') ? '&' : '?';
|
||||||
|
return `${baseUrl}${separator}start=${payload}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
const telegramConnectUrl = computed(() => buildBotConnectUrl(config.public.telegramBotUrl || ''));
|
||||||
|
const maxConnectUrl = computed(() => buildBotConnectUrl(config.public.maxBotUrl || ''));
|
||||||
|
|
||||||
async function sendTest() {
|
async function sendTest() {
|
||||||
feedback.value = '';
|
feedback.value = '';
|
||||||
|
if (!activeConnection.value) {
|
||||||
|
feedback.value = `Канал ${selectedChannel.value} ещё не подключен. Сначала подключите его через бота.`;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const result = await sendTestMutation.mutate({
|
const result = await sendTestMutation.mutate({
|
||||||
type: selectedChannel.value,
|
type: selectedChannel.value,
|
||||||
channelId: channelId.value || undefined,
|
|
||||||
message: customMessage.value || undefined,
|
message: customMessage.value || undefined,
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -67,6 +101,62 @@ async function sendTest() {
|
|||||||
|
|
||||||
<div class="surface-card rounded-3xl p-5">
|
<div class="surface-card rounded-3xl p-5">
|
||||||
<div class="space-y-4">
|
<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>
|
||||||
|
|
||||||
|
<a
|
||||||
|
:href="telegramConnectUrl || undefined"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
class="btn btn-secondary"
|
||||||
|
:class="{ 'btn-disabled pointer-events-none': !telegramConnectUrl }"
|
||||||
|
>
|
||||||
|
{{ telegramConnection ? 'Переподключить Telegram' : 'Подключить Telegram' }}
|
||||||
|
</a>
|
||||||
|
</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>
|
||||||
|
|
||||||
|
<a
|
||||||
|
:href="maxConnectUrl || undefined"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
class="btn btn-accent"
|
||||||
|
:class="{ 'btn-disabled pointer-events-none': !maxConnectUrl }"
|
||||||
|
>
|
||||||
|
{{ maxConnection ? 'Переподключить Max' : 'Подключить Max' }}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="surface-card rounded-3xl p-5">
|
||||||
|
<div class="space-y-3">
|
||||||
<div class="tabs tabs-boxed w-fit">
|
<div class="tabs tabs-boxed w-fit">
|
||||||
<button
|
<button
|
||||||
class="tab"
|
class="tab"
|
||||||
@@ -91,15 +181,6 @@ async function sendTest() {
|
|||||||
</span>
|
</span>
|
||||||
</p>
|
</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">
|
<label class="form-control">
|
||||||
<span class="label-text">Тестовое сообщение</span>
|
<span class="label-text">Тестовое сообщение</span>
|
||||||
<textarea v-model="customMessage" class="textarea textarea-bordered border-[#d0e8d8] bg-white/80" rows="3" />
|
<textarea v-model="customMessage" class="textarea textarea-bordered border-[#d0e8d8] bg-white/80" rows="3" />
|
||||||
@@ -107,7 +188,7 @@ async function sendTest() {
|
|||||||
|
|
||||||
<button
|
<button
|
||||||
class="btn w-fit border-0 bg-[#139957] text-white hover:bg-[#0d854a]"
|
class="btn w-fit border-0 bg-[#139957] text-white hover:bg-[#0d854a]"
|
||||||
:disabled="sendTestMutation.loading.value"
|
:disabled="sendTestMutation.loading.value || !activeConnection"
|
||||||
@click="sendTest"
|
@click="sendTest"
|
||||||
>
|
>
|
||||||
Отправить тест
|
Отправить тест
|
||||||
@@ -116,11 +197,7 @@ async function sendTest() {
|
|||||||
<div v-if="feedback" class="alert" :class="feedback.startsWith('Ошибка') ? 'alert-error' : 'alert-success'">
|
<div v-if="feedback" class="alert" :class="feedback.startsWith('Ошибка') ? 'alert-error' : 'alert-success'">
|
||||||
{{ feedback }}
|
{{ feedback }}
|
||||||
</div>
|
</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>
|
<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-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 v-else-if="(historyQuery.result.value?.myNotificationHistory?.length ?? 0) === 0" class="alert">
|
||||||
|
|||||||
6
graphql/operations/auth/me.graphql
Normal file
6
graphql/operations/auth/me.graphql
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
query Me {
|
||||||
|
me {
|
||||||
|
id
|
||||||
|
email
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
query MyMessengerConnections {
|
||||||
|
myMessengerConnections {
|
||||||
|
id
|
||||||
|
type
|
||||||
|
channelId
|
||||||
|
isActive
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
query MyNotificationHistory($channel: MessengerType!, $limit: Int) {
|
||||||
|
myNotificationHistory(channel: $channel, limit: $limit) {
|
||||||
|
id
|
||||||
|
channel
|
||||||
|
title
|
||||||
|
message
|
||||||
|
createdAt
|
||||||
|
orderId
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
mutation SendTestMessengerMessage($type: MessengerType!, $channelId: String, $message: String) {
|
||||||
|
sendTestMessengerMessage(type: $type, channelId: $channelId, message: $message) {
|
||||||
|
type
|
||||||
|
channelId
|
||||||
|
success
|
||||||
|
detail
|
||||||
|
sentAt
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user