Redesign messenger connection settings
This commit is contained in:
@@ -282,6 +282,7 @@ export type Mutation = {
|
|||||||
createMyDeliveryAddress: DeliveryAddress;
|
createMyDeliveryAddress: DeliveryAddress;
|
||||||
createReferral: ReferralLink;
|
createReferral: ReferralLink;
|
||||||
deleteMyDeliveryAddress: Scalars['Boolean']['output'];
|
deleteMyDeliveryAddress: Scalars['Boolean']['output'];
|
||||||
|
deleteMyMessengerConnection: Scalars['Boolean']['output'];
|
||||||
managerSetOrderOffer: Order;
|
managerSetOrderOffer: Order;
|
||||||
managerSetOrderStatus: Order;
|
managerSetOrderStatus: Order;
|
||||||
registerSelf: RegistrationRequest;
|
registerSelf: RegistrationRequest;
|
||||||
@@ -352,6 +353,11 @@ export type MutationDeleteMyDeliveryAddressArgs = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
export type MutationDeleteMyMessengerConnectionArgs = {
|
||||||
|
connectionId: Scalars['ID']['input'];
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
export type MutationManagerSetOrderOfferArgs = {
|
export type MutationManagerSetOrderOfferArgs = {
|
||||||
input: SetOrderOfferInput;
|
input: SetOrderOfferInput;
|
||||||
};
|
};
|
||||||
@@ -1043,6 +1049,13 @@ export type DeleteMyDeliveryAddressMutationVariables = Exact<{
|
|||||||
|
|
||||||
export type DeleteMyDeliveryAddressMutation = { __typename?: 'Mutation', deleteMyDeliveryAddress: boolean };
|
export type DeleteMyDeliveryAddressMutation = { __typename?: 'Mutation', deleteMyDeliveryAddress: boolean };
|
||||||
|
|
||||||
|
export type DeleteMyMessengerConnectionMutationVariables = Exact<{
|
||||||
|
connectionId: Scalars['ID']['input'];
|
||||||
|
}>;
|
||||||
|
|
||||||
|
|
||||||
|
export type DeleteMyMessengerConnectionMutation = { __typename?: 'Mutation', deleteMyMessengerConnection: boolean };
|
||||||
|
|
||||||
export type MyCounterpartyProfileQueryVariables = Exact<{ [key: string]: never; }>;
|
export type MyCounterpartyProfileQueryVariables = Exact<{ [key: string]: never; }>;
|
||||||
|
|
||||||
|
|
||||||
@@ -2650,6 +2663,33 @@ export function useDeleteMyDeliveryAddressMutation(options: VueApolloComposable.
|
|||||||
return VueApolloComposable.useMutation<DeleteMyDeliveryAddressMutation, DeleteMyDeliveryAddressMutationVariables>(DeleteMyDeliveryAddressDocument, options);
|
return VueApolloComposable.useMutation<DeleteMyDeliveryAddressMutation, DeleteMyDeliveryAddressMutationVariables>(DeleteMyDeliveryAddressDocument, options);
|
||||||
}
|
}
|
||||||
export type DeleteMyDeliveryAddressMutationCompositionFunctionResult = VueApolloComposable.UseMutationReturn<DeleteMyDeliveryAddressMutation, DeleteMyDeliveryAddressMutationVariables>;
|
export type DeleteMyDeliveryAddressMutationCompositionFunctionResult = VueApolloComposable.UseMutationReturn<DeleteMyDeliveryAddressMutation, DeleteMyDeliveryAddressMutationVariables>;
|
||||||
|
export const DeleteMyMessengerConnectionDocument = gql`
|
||||||
|
mutation DeleteMyMessengerConnection($connectionId: ID!) {
|
||||||
|
deleteMyMessengerConnection(connectionId: $connectionId)
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* __useDeleteMyMessengerConnectionMutation__
|
||||||
|
*
|
||||||
|
* To run a mutation, you first call `useDeleteMyMessengerConnectionMutation` within a Vue component and pass it any options that fit your needs.
|
||||||
|
* When your component renders, `useDeleteMyMessengerConnectionMutation` returns an object that includes:
|
||||||
|
* - A mutate function that you can call at any time to execute the mutation
|
||||||
|
* - Several other properties: https://v4.apollo.vuejs.org/api/use-mutation.html#return
|
||||||
|
*
|
||||||
|
* @param options that will be passed into the mutation, supported options are listed on: https://v4.apollo.vuejs.org/guide-composable/mutation.html#options;
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* const { mutate, loading, error, onDone } = useDeleteMyMessengerConnectionMutation({
|
||||||
|
* variables: {
|
||||||
|
* connectionId: // value for 'connectionId'
|
||||||
|
* },
|
||||||
|
* });
|
||||||
|
*/
|
||||||
|
export function useDeleteMyMessengerConnectionMutation(options: VueApolloComposable.UseMutationOptions<DeleteMyMessengerConnectionMutation, DeleteMyMessengerConnectionMutationVariables> | ReactiveFunction<VueApolloComposable.UseMutationOptions<DeleteMyMessengerConnectionMutation, DeleteMyMessengerConnectionMutationVariables>> = {}) {
|
||||||
|
return VueApolloComposable.useMutation<DeleteMyMessengerConnectionMutation, DeleteMyMessengerConnectionMutationVariables>(DeleteMyMessengerConnectionDocument, options);
|
||||||
|
}
|
||||||
|
export type DeleteMyMessengerConnectionMutationCompositionFunctionResult = VueApolloComposable.UseMutationReturn<DeleteMyMessengerConnectionMutation, DeleteMyMessengerConnectionMutationVariables>;
|
||||||
export const MyCounterpartyProfileDocument = gql`
|
export const MyCounterpartyProfileDocument = gql`
|
||||||
query MyCounterpartyProfile {
|
query MyCounterpartyProfile {
|
||||||
myCounterpartyProfile {
|
myCounterpartyProfile {
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ type MessengerStartInput = {
|
|||||||
|
|
||||||
export function useMessengerStart() {
|
export function useMessengerStart() {
|
||||||
const pendingChannel = ref<MessengerChannel | null>(null);
|
const pendingChannel = ref<MessengerChannel | null>(null);
|
||||||
|
const maxMiniApp = useMaxMiniApp();
|
||||||
|
|
||||||
async function openMessengerBot({ channel, baseUrl, email, redirectPath }: MessengerStartInput) {
|
async function openMessengerBot({ channel, baseUrl, email, redirectPath }: MessengerStartInput) {
|
||||||
pendingChannel.value = channel;
|
pendingChannel.value = channel;
|
||||||
@@ -38,7 +39,17 @@ export function useMessengerStart() {
|
|||||||
|
|
||||||
const startUrl = buildMessengerBotStartUrl(baseUrl, payload.startToken);
|
const startUrl = buildMessengerBotStartUrl(baseUrl, payload.startToken);
|
||||||
if (import.meta.client) {
|
if (import.meta.client) {
|
||||||
window.open(startUrl, '_blank', 'noopener,noreferrer');
|
if (
|
||||||
|
channel === 'MAX'
|
||||||
|
&& maxMiniApp.isAvailable.value
|
||||||
|
&& startUrl.startsWith('https://max.ru/')
|
||||||
|
&& typeof maxMiniApp.webApp.value?.openMaxLink === 'function'
|
||||||
|
) {
|
||||||
|
maxMiniApp.webApp.value.openMaxLink(startUrl);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
window.open(startUrl, '_blank', 'noopener,noreferrer');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return payload;
|
return payload;
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useQuery } from '@vue/apollo-composable';
|
import { useMutation, useQuery } from '@vue/apollo-composable';
|
||||||
import {
|
import {
|
||||||
|
DeleteMyMessengerConnectionDocument,
|
||||||
MeDocument,
|
MeDocument,
|
||||||
MyMessengerConnectionsDocument,
|
MyMessengerConnectionsDocument,
|
||||||
} from '~/composables/graphql/generated';
|
} from '~/composables/graphql/generated';
|
||||||
@@ -12,9 +13,11 @@ import {
|
|||||||
} from '~/composables/useMessengerConnectionPresentation';
|
} from '~/composables/useMessengerConnectionPresentation';
|
||||||
import { useMessengerStart } from '~/composables/useMessengerStart';
|
import { useMessengerStart } from '~/composables/useMessengerStart';
|
||||||
|
|
||||||
|
type MessengerChannel = 'TELEGRAM' | 'MAX';
|
||||||
|
|
||||||
type MessengerItem = {
|
type MessengerItem = {
|
||||||
id: string;
|
id: string;
|
||||||
type: 'TELEGRAM' | 'MAX';
|
type: MessengerChannel;
|
||||||
isActive: boolean;
|
isActive: boolean;
|
||||||
channelId: string;
|
channelId: string;
|
||||||
displayName?: string | null;
|
displayName?: string | null;
|
||||||
@@ -22,23 +25,56 @@ type MessengerItem = {
|
|||||||
avatarAvailable?: boolean | null;
|
avatarAvailable?: boolean | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type MessengerOption = {
|
||||||
|
channel: MessengerChannel;
|
||||||
|
label: string;
|
||||||
|
title: string;
|
||||||
|
description: string;
|
||||||
|
buttonClass: string;
|
||||||
|
iconClass: string;
|
||||||
|
unavailableText: string;
|
||||||
|
};
|
||||||
|
|
||||||
const config = useRuntimeConfig();
|
const config = useRuntimeConfig();
|
||||||
|
const feedback = ref('');
|
||||||
const meQuery = useQuery(MeDocument);
|
const meQuery = useQuery(MeDocument);
|
||||||
const connectionsQuery = useQuery(MyMessengerConnectionsDocument);
|
const connectionsQuery = useQuery(MyMessengerConnectionsDocument);
|
||||||
|
const deleteConnectionMutation = useMutation(DeleteMyMessengerConnectionDocument);
|
||||||
const { openMessengerBot, pendingChannel } = useMessengerStart();
|
const { openMessengerBot, pendingChannel } = useMessengerStart();
|
||||||
|
|
||||||
const telegramConnection = computed(() =>
|
const telegramConnection = computed(() =>
|
||||||
connectionsQuery.result.value?.myMessengerConnections?.find(
|
connectionsQuery.result.value?.myMessengerConnections?.find(
|
||||||
(item: MessengerItem) => item.type === 'TELEGRAM' && item.isActive,
|
(item: MessengerItem) => item.type === 'TELEGRAM' && item.isActive,
|
||||||
),
|
) ?? null,
|
||||||
);
|
);
|
||||||
|
|
||||||
const maxConnection = computed(() =>
|
const maxConnection = computed(() =>
|
||||||
connectionsQuery.result.value?.myMessengerConnections?.find(
|
connectionsQuery.result.value?.myMessengerConnections?.find(
|
||||||
(item: MessengerItem) => item.type === 'MAX' && item.isActive,
|
(item: MessengerItem) => item.type === 'MAX' && item.isActive,
|
||||||
),
|
) ?? null,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const messengerOptions: MessengerOption[] = [
|
||||||
|
{
|
||||||
|
channel: 'TELEGRAM',
|
||||||
|
label: 'Telegram',
|
||||||
|
title: 'Подключить Telegram',
|
||||||
|
description: 'Получайте статусы заказов и сервисные уведомления в Telegram.',
|
||||||
|
buttonClass: 'bg-[#1a9c63] text-white hover:bg-[#148553]',
|
||||||
|
iconClass: 'bg-[#123824] text-white',
|
||||||
|
unavailableText: 'Telegram пока не настроен в окружении фронта.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
channel: 'MAX',
|
||||||
|
label: 'MAX',
|
||||||
|
title: 'Подключить MAX',
|
||||||
|
description: 'Открывает MAX-бота и привязывает аккаунт к личному кабинету.',
|
||||||
|
buttonClass: 'bg-[#2b7fff] text-white hover:bg-[#1d6df1]',
|
||||||
|
iconClass: 'bg-[#2b7fff] text-white',
|
||||||
|
unavailableText: 'MAX пока не настроен в окружении фронта.',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
function buildBotConnectUrl(baseUrl: string) {
|
function buildBotConnectUrl(baseUrl: string) {
|
||||||
const accountEmail = meQuery.result.value?.me?.email?.trim().toLowerCase();
|
const accountEmail = meQuery.result.value?.me?.email?.trim().toLowerCase();
|
||||||
if (!accountEmail || !baseUrl) {
|
if (!accountEmail || !baseUrl) {
|
||||||
@@ -51,9 +87,32 @@ function buildBotConnectUrl(baseUrl: string) {
|
|||||||
const telegramConnectUrl = computed(() => buildBotConnectUrl(config.public.telegramBotUrl || ''));
|
const telegramConnectUrl = computed(() => buildBotConnectUrl(config.public.telegramBotUrl || ''));
|
||||||
const maxConnectUrl = computed(() => buildBotConnectUrl(config.public.maxBotUrl || ''));
|
const maxConnectUrl = computed(() => buildBotConnectUrl(config.public.maxBotUrl || ''));
|
||||||
|
|
||||||
async function connectMessenger(channel: 'TELEGRAM' | 'MAX') {
|
function connectUrl(channel: MessengerChannel) {
|
||||||
const baseUrl = channel === 'TELEGRAM' ? telegramConnectUrl.value : maxConnectUrl.value;
|
return channel === 'TELEGRAM' ? telegramConnectUrl.value : maxConnectUrl.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
function connectionFor(channel: MessengerChannel) {
|
||||||
|
return channel === 'TELEGRAM' ? telegramConnection.value : maxConnection.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
const activeConnections = computed(() => messengerOptions
|
||||||
|
.map((option) => ({
|
||||||
|
option,
|
||||||
|
connection: connectionFor(option.channel),
|
||||||
|
}))
|
||||||
|
.filter((item) => Boolean(item.connection)));
|
||||||
|
|
||||||
|
const availableOptions = computed(() => messengerOptions
|
||||||
|
.filter((option) => !connectionFor(option.channel)));
|
||||||
|
|
||||||
|
async function connectMessenger(channel: MessengerChannel) {
|
||||||
|
feedback.value = '';
|
||||||
|
const baseUrl = connectUrl(channel);
|
||||||
|
|
||||||
if (!baseUrl) {
|
if (!baseUrl) {
|
||||||
|
feedback.value = channel === 'MAX'
|
||||||
|
? 'MAX не откроется, пока не задан NUXT_PUBLIC_MAX_BOT_URL.'
|
||||||
|
: 'Telegram не откроется, пока не задан NUXT_PUBLIC_TELEGRAM_BOT_URL.';
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -63,92 +122,189 @@ async function connectMessenger(channel: 'TELEGRAM' | 'MAX') {
|
|||||||
redirectPath: `/profile/notifications/success?connected=${channel.toLowerCase()}`,
|
redirectPath: `/profile/notifications/success?connected=${channel.toLowerCase()}`,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function removeConnection(connectionId: string) {
|
||||||
|
feedback.value = '';
|
||||||
|
const result = await deleteConnectionMutation.mutate({
|
||||||
|
connectionId,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!result?.data?.deleteMyMessengerConnection) {
|
||||||
|
feedback.value = 'Не удалось отключить аккаунт. Попробуйте еще раз.';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await connectionsQuery.refetch();
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<section class="space-y-6">
|
<section class="space-y-6">
|
||||||
<NuxtLink to="/profile" class="link link-hover text-sm">← Назад в профиль</NuxtLink>
|
<NuxtLink to="/profile" class="link link-hover text-sm">← Назад в профиль</NuxtLink>
|
||||||
<h1 class="text-3xl font-extrabold text-[#0f2f20]">Уведомления</h1>
|
|
||||||
|
|
||||||
<div class="surface-card rounded-3xl p-5">
|
<div class="space-y-2">
|
||||||
<p class="text-sm text-[#355947]">
|
<h1 class="text-3xl font-extrabold text-[#0f2f20]">Уведомления</h1>
|
||||||
Подключите Telegram и Max, чтобы получать статусы заказов и важные уведомления в удобном канале.
|
<p class="max-w-3xl text-sm leading-6 text-[#466653]">
|
||||||
|
Подключите удобные мессенджеры, чтобы получать статусы заказов и важные уведомления без лишних переходов в кабинет.
|
||||||
</p>
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="mt-4 space-y-3">
|
<div
|
||||||
<div class="rounded-2xl bg-[#f8fbf9] p-4 transition hover:shadow-md">
|
v-if="feedback"
|
||||||
<p class="font-semibold">Telegram</p>
|
class="rounded-[24px] border px-4 py-3 text-sm font-medium"
|
||||||
<div v-if="telegramConnection" class="mt-3 flex items-center gap-3 rounded-2xl bg-white px-3 py-2">
|
:class="feedback.includes('Не удалось') || feedback.includes('не откроется')
|
||||||
<div v-if="messengerConnectionAvatarSrc(telegramConnection)" class="avatar">
|
? 'border-[#f1d1c7] bg-[#fff3ef] text-[#9d4426]'
|
||||||
<div class="h-11 w-11 rounded-full">
|
: 'border-[#cbe9d6] bg-[#f1fbf5] text-[#1c6b45]'"
|
||||||
<img :src="messengerConnectionAvatarSrc(telegramConnection)" :alt="messengerConnectionName(telegramConnection)">
|
>
|
||||||
</div>
|
{{ feedback }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
v-if="activeConnections.length === 0"
|
||||||
|
class="rounded-[32px] bg-[#edf3ee] p-6 md:p-8"
|
||||||
|
>
|
||||||
|
<div class="space-y-3">
|
||||||
|
<h2 class="text-2xl font-black tracking-[-0.03em] text-[#123824]">Подключите мессенджеры</h2>
|
||||||
|
<p class="max-w-3xl text-sm leading-6 text-[#557562]">
|
||||||
|
Вы можете подключить любой из мессенджеров ниже. После подключения уведомления о заказах и важных действиях будут приходить прямо туда.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mt-6 grid gap-4 md:grid-cols-2">
|
||||||
|
<button
|
||||||
|
v-for="option in messengerOptions"
|
||||||
|
:key="option.channel"
|
||||||
|
class="flex min-h-[120px] flex-col items-start justify-between rounded-[28px] border-0 px-5 py-5 text-left shadow-[0_18px_38px_rgba(18,56,36,0.08)] transition"
|
||||||
|
:class="[option.buttonClass, { 'opacity-60': !connectUrl(option.channel) }]"
|
||||||
|
:disabled="pendingChannel === option.channel || !connectUrl(option.channel)"
|
||||||
|
@click="connectMessenger(option.channel)"
|
||||||
|
>
|
||||||
|
<div class="space-y-2">
|
||||||
|
<div class="inline-flex h-11 w-11 items-center justify-center rounded-2xl bg-white/18 text-sm font-black text-white">
|
||||||
|
{{ option.channel === 'TELEGRAM' ? 'TG' : 'MX' }}
|
||||||
</div>
|
</div>
|
||||||
<div v-else class="avatar placeholder">
|
<div>
|
||||||
<div class="h-11 w-11 rounded-full bg-[#123824] text-sm font-bold text-white">
|
<p class="text-lg font-black tracking-[-0.03em]">{{ option.title }}</p>
|
||||||
<span>{{ messengerConnectionInitials(telegramConnection, 'TG') }}</span>
|
<p class="mt-1 text-sm text-white/82">{{ option.description }}</p>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="min-w-0">
|
|
||||||
<p class="truncate text-sm font-semibold text-[#123824]">
|
|
||||||
{{ messengerConnectionName(telegramConnection) }}
|
|
||||||
</p>
|
|
||||||
<p class="truncate text-xs text-[#5c7b69]">
|
|
||||||
{{ messengerConnectionHandle(telegramConnection) || 'Подключен' }}
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<p v-else class="text-sm opacity-80">Не подключен</p>
|
<p class="text-sm font-semibold text-white/90">
|
||||||
<button
|
{{ pendingChannel === option.channel ? `Открываем ${option.label}...` : `Перейти в ${option.label}` }}
|
||||||
class="btn btn-secondary mt-3 w-full"
|
</p>
|
||||||
:class="{ 'btn-disabled pointer-events-none': !telegramConnectUrl }"
|
</button>
|
||||||
:disabled="pendingChannel === 'TELEGRAM' || !telegramConnectUrl"
|
</div>
|
||||||
@click="connectMessenger('TELEGRAM')"
|
|
||||||
>
|
|
||||||
{{
|
|
||||||
pendingChannel === 'TELEGRAM'
|
|
||||||
? 'Открываем Telegram…'
|
|
||||||
: telegramConnection
|
|
||||||
? 'Переподключить Telegram'
|
|
||||||
: 'Подключить Telegram'
|
|
||||||
}}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="rounded-2xl bg-[#f8fbf9] p-4 transition hover:shadow-md">
|
<div class="mt-4 space-y-2">
|
||||||
<p class="font-semibold">Max</p>
|
<p
|
||||||
<div v-if="maxConnection" class="mt-3 flex items-center gap-3 rounded-2xl bg-white px-3 py-2">
|
v-for="option in messengerOptions.filter((item) => !connectUrl(item.channel))"
|
||||||
<div class="avatar placeholder">
|
:key="`${option.channel}-hint`"
|
||||||
<div class="h-11 w-11 rounded-full bg-[#2b7fff] text-sm font-bold text-white">
|
class="text-sm text-[#8b5a49]"
|
||||||
<span>{{ messengerConnectionInitials(maxConnection, 'MX') }}</span>
|
>
|
||||||
</div>
|
{{ option.unavailableText }}
|
||||||
</div>
|
</p>
|
||||||
<div class="min-w-0">
|
|
||||||
<p class="truncate text-sm font-semibold text-[#123824]">
|
|
||||||
{{ messengerConnectionName(maxConnection) }}
|
|
||||||
</p>
|
|
||||||
<p class="truncate text-xs text-[#5c7b69]">
|
|
||||||
{{ messengerConnectionHandle(maxConnection) || 'Подключен' }}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<p v-else class="text-sm opacity-80">Не подключен</p>
|
|
||||||
<button
|
|
||||||
class="btn btn-accent mt-3 w-full"
|
|
||||||
: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>
|
||||||
|
|
||||||
|
<template v-else>
|
||||||
|
<div class="rounded-[32px] bg-[#edf3ee] p-6 md:p-8">
|
||||||
|
<div class="space-y-3">
|
||||||
|
<h2 class="text-2xl font-black tracking-[-0.03em] text-[#123824]">Подключенные аккаунты</h2>
|
||||||
|
<p class="text-sm leading-6 text-[#557562]">
|
||||||
|
Здесь показаны активные мессенджеры, привязанные к вашему кабинету.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mt-6 space-y-4">
|
||||||
|
<article
|
||||||
|
v-for="{ option, connection } in activeConnections"
|
||||||
|
:key="connection!.id"
|
||||||
|
class="rounded-[28px] bg-white px-5 py-4 shadow-[0_18px_38px_rgba(18,56,36,0.08)]"
|
||||||
|
>
|
||||||
|
<div class="flex flex-col gap-4 md:flex-row md:items-center md:justify-between">
|
||||||
|
<div class="flex min-w-0 items-center gap-4">
|
||||||
|
<div v-if="messengerConnectionAvatarSrc(connection)" class="avatar">
|
||||||
|
<div class="h-14 w-14 rounded-[20px]">
|
||||||
|
<img :src="messengerConnectionAvatarSrc(connection)" :alt="messengerConnectionName(connection)">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-else
|
||||||
|
class="flex h-14 w-14 items-center justify-center rounded-[20px] text-sm font-black"
|
||||||
|
:class="option.iconClass"
|
||||||
|
>
|
||||||
|
{{ messengerConnectionInitials(connection, option.channel === 'TELEGRAM' ? 'TG' : 'MX') }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="min-w-0">
|
||||||
|
<p class="text-[11px] font-semibold uppercase tracking-[0.18em] text-[#6a8a76]">{{ option.label }}</p>
|
||||||
|
<p class="truncate text-lg font-bold text-[#123824]">{{ messengerConnectionName(connection) }}</p>
|
||||||
|
<p class="truncate text-sm text-[#557562]">{{ messengerConnectionHandle(connection) || connection.channelId }}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button
|
||||||
|
class="btn rounded-full border border-[#e5cfc7] bg-[#fff5f1] px-5 text-[#a64d2d] hover:border-[#deb5a8] hover:bg-[#ffe8e0]"
|
||||||
|
:disabled="deleteConnectionMutation.loading.value"
|
||||||
|
@click="removeConnection(connection!.id)"
|
||||||
|
>
|
||||||
|
{{ deleteConnectionMutation.loading.value ? 'Удаляем...' : 'Удалить' }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
v-if="availableOptions.length > 0"
|
||||||
|
class="rounded-[32px] bg-[#edf3ee] p-6 md:p-8"
|
||||||
|
>
|
||||||
|
<div class="space-y-3">
|
||||||
|
<h2 class="text-2xl font-black tracking-[-0.03em] text-[#123824]">Можно подключить еще</h2>
|
||||||
|
<p class="text-sm leading-6 text-[#557562]">
|
||||||
|
Добавьте второй канал, чтобы не потерять уведомления, если один мессенджер недоступен.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mt-6 grid gap-4 md:grid-cols-2">
|
||||||
|
<button
|
||||||
|
v-for="option in availableOptions"
|
||||||
|
:key="option.channel"
|
||||||
|
class="flex min-h-[112px] flex-col items-start justify-between rounded-[28px] border-0 px-5 py-5 text-left shadow-[0_18px_38px_rgba(18,56,36,0.08)] transition"
|
||||||
|
:class="[option.buttonClass, { 'opacity-60': !connectUrl(option.channel) }]"
|
||||||
|
:disabled="pendingChannel === option.channel || !connectUrl(option.channel)"
|
||||||
|
@click="connectMessenger(option.channel)"
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
<p class="text-lg font-black tracking-[-0.03em]">{{ option.title }}</p>
|
||||||
|
<p class="mt-2 text-sm text-white/82">{{ option.description }}</p>
|
||||||
|
</div>
|
||||||
|
<p class="text-sm font-semibold text-white/90">
|
||||||
|
{{ pendingChannel === option.channel ? `Открываем ${option.label}...` : `Подключить ${option.label}` }}
|
||||||
|
</p>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mt-4 space-y-2">
|
||||||
|
<p
|
||||||
|
v-for="option in availableOptions.filter((item) => !connectUrl(item.channel))"
|
||||||
|
:key="`${option.channel}-connected-hint`"
|
||||||
|
class="text-sm text-[#8b5a49]"
|
||||||
|
>
|
||||||
|
{{ option.unavailableText }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
v-else
|
||||||
|
class="rounded-[32px] bg-[linear-gradient(135deg,#123824_0%,#1a5635_100%)] p-6 text-white md:p-8"
|
||||||
|
>
|
||||||
|
<p class="text-[11px] font-semibold uppercase tracking-[0.18em] text-white/65">Готово</p>
|
||||||
|
<h2 class="mt-3 text-2xl font-black tracking-[-0.03em]">Оба канала подключены</h2>
|
||||||
|
<p class="mt-2 max-w-3xl text-sm leading-6 text-white/78">
|
||||||
|
Теперь важные уведомления будут доступны и в Telegram, и в MAX. Если захотите сменить аккаунт, удалите текущий и подключите новый.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
</section>
|
</section>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -48,12 +48,12 @@ const successConnection = computed(() =>
|
|||||||
|
|
||||||
const profileName = computed(() => meQuery.result.value?.me?.fullName?.trim() || meQuery.result.value?.me?.email || 'Пользователь');
|
const profileName = computed(() => meQuery.result.value?.me?.fullName?.trim() || meQuery.result.value?.me?.email || 'Пользователь');
|
||||||
const successTitle = computed(() =>
|
const successTitle = computed(() =>
|
||||||
connectedChannel.value === 'telegram' ? 'Telegram успешно подключен' : 'Канал успешно подключен',
|
connectedChannel.value === 'telegram' ? 'Telegram успешно подключен' : 'MAX успешно подключен',
|
||||||
);
|
);
|
||||||
const successText = computed(() =>
|
const successText = computed(() =>
|
||||||
connectedChannel.value === 'telegram'
|
connectedChannel.value === 'telegram'
|
||||||
? 'Теперь этот Telegram привязан к вашему личному кабинету. Все важные уведомления и статусы заказов будут приходить сюда.'
|
? 'Теперь этот Telegram привязан к вашему личному кабинету. Все важные уведомления и статусы заказов будут приходить сюда.'
|
||||||
: 'Канал успешно привязан к вашему личному кабинету.',
|
: 'Теперь этот MAX привязан к вашему личному кабинету. Все важные уведомления и статусы заказов будут приходить сюда.',
|
||||||
);
|
);
|
||||||
const successAvatarSrc = computed(() => messengerConnectionAvatarSrc(successConnection.value));
|
const successAvatarSrc = computed(() => messengerConnectionAvatarSrc(successConnection.value));
|
||||||
const successAvatarInitials = computed(() =>
|
const successAvatarInitials = computed(() =>
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
mutation DeleteMyMessengerConnection($connectionId: ID!) {
|
||||||
|
deleteMyMessengerConnection(connectionId: $connectionId)
|
||||||
|
}
|
||||||
@@ -523,6 +523,7 @@ type Mutation {
|
|||||||
createInvitation(input: CreateInvitationInput!): Invitation!
|
createInvitation(input: CreateInvitationInput!): Invitation!
|
||||||
acceptInvitation(input: AcceptInvitationInput!): User!
|
acceptInvitation(input: AcceptInvitationInput!): User!
|
||||||
connectMessenger(input: ConnectMessengerInput!): MessengerConnection!
|
connectMessenger(input: ConnectMessengerInput!): MessengerConnection!
|
||||||
|
deleteMyMessengerConnection(connectionId: ID!): Boolean!
|
||||||
upsertMyCounterpartyProfile(input: UpsertMyCounterpartyProfileInput!): CounterpartyProfile!
|
upsertMyCounterpartyProfile(input: UpsertMyCounterpartyProfileInput!): CounterpartyProfile!
|
||||||
addProductToCart(productId: ID!): Cart!
|
addProductToCart(productId: ID!): Cart!
|
||||||
updateCartItemQuantity(input: UpdateCartItemQuantityInput!): Cart!
|
updateCartItemQuantity(input: UpdateCartItemQuantityInput!): Cart!
|
||||||
|
|||||||
Reference in New Issue
Block a user