Files
interview-demo-code/frontend/spa/src/views/Account.vue
Nikita Kiselev f329bfa9d9
Some checks failed
Telegram Mini App Shop Builder / Compute version metadata (push) Has been cancelled
Telegram Mini App Shop Builder / Run Frontend tests (push) Has been cancelled
Telegram Mini App Shop Builder / Run Backend tests (push) Has been cancelled
Telegram Mini App Shop Builder / Run PHP_CodeSniffer (push) Has been cancelled
Telegram Mini App Shop Builder / Build module. (push) Has been cancelled
Telegram Mini App Shop Builder / release (push) Has been cancelled
Squashed commit message
2026-03-11 23:00:23 +03:00

239 lines
9.8 KiB
Vue
Raw 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.
<template>
<BaseViewWrapper>
<div class="account-page">
<!-- Профиль пользователя -->
<div class="card card-border bg-base-100 mb-4">
<div class="card-body">
<div class="flex items-center gap-4">
<div class="avatar">
<div v-if="tgData?.user?.photo_url" class="w-16 h-16 rounded-full">
<img :src="tgData?.user?.photo_url" alt="avatar"/>
</div>
<div v-else class="bg-primary text-primary-content w-16 h-16 rounded-full flex items-center justify-center">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-8">
<path stroke-linecap="round" stroke-linejoin="round" d="M15.75 6a3.75 3.75 0 1 1-7.5 0 3.75 3.75 0 0 1 7.5 0ZM4.501 20.118a7.5 7.5 0 0 1 14.998 0A17.933 17.933 0 0 1 12 21.75c-2.676 0-5.216-.584-7.499-1.632Z" />
</svg>
</div>
</div>
<div class="flex-1">
<h2 class="text-xl font-bold">
{{ username }}
</h2>
<p v-if="tgData?.user?.username" class="text-sm text-base-content/70">
@{{ tgData.user.username }}
</p>
<p v-if="daysWithUs !== null" class="text-sm text-base-content/40">
Вы с нами {{ daysWithUs }} {{ daysWord }}
</p>
</div>
</div>
</div>
</div>
<!-- Список пунктов меню -->
<div class="menu bg-base-100 rounded-box card card-border w-full mb-4">
<li class="w-full">
<a @click="openManagerChat" class="flex items-center gap-3 w-full">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-6">
<path stroke-linecap="round" stroke-linejoin="round" d="M20.25 8.511c.884.284 1.5 1.128 1.5 2.097v4.286c0 1.136-.847 2.1-1.98 2.193-.34.027-.68.052-1.02.072v3.091l-3-3c-1.354 0-2.694-.055-4.02-.163a2.115 2.115 0 0 1-.825-.242m9.345-8.334a2.126 2.126 0 0 0-.476-.095 8.07 8.07 0 0 0-1.603-.093C9.5 7.5 8.25 8.25 8.25 9.75v4.286c0 .597.237 1.17.659 1.591l3.682 3.682a2.25 2.25 0 0 0 1.591.659h4.286c.597 0 1.17-.237 1.591-.659l3.682-3.682a2.25 2.25 0 0 0 .659-1.591V10.608c0-1.136-.847-2.1-1.98-2.193a48.138 48.138 0 0 0-1.02-.072Z" />
</svg>
<span>Связаться с нами</span>
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-5 ml-auto">
<path stroke-linecap="round" stroke-linejoin="round" d="m8.25 4.5 7.5 7.5-7.5 7.5" />
</svg>
</a>
</li>
</div>
<!-- Кнопка добавления на главный экран -->
<div v-if="isHomeScreenSupported" class="card card-border bg-base-100">
<div class="card-body p-4">
<button
v-if="!isHomeScreenAdded"
@click="addToHomeScreen"
class="btn btn-outline w-full justify-center gap-3 mb-2"
>
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-6">
<path stroke-linecap="round" stroke-linejoin="round" d="M13.5 16.875h3.375m0 0h3.375m-3.375 0V13.5m0 3.375v3.375M6 10.5h2.25a2.25 2.25 0 0 0 2.25-2.25V6a2.25 2.25 0 0 0-2.25-2.25H6A2.25 2.25 0 0 0 3.75 6v2.25A2.25 2.25 0 0 0 6 10.5Zm0 9.75h2.25A2.25 2.25 0 0 0 10.5 18v-2.25a2.25 2.25 0 0 0-2.25-2.25H6a2.25 2.25 0 0 0-2.25 2.25V18A2.25 2.25 0 0 0 6 20.25Zm9.75-9.75H18a2.25 2.25 0 0 0 2.25-2.25V6A2.25 2.25 0 0 0 18 3.75h-2.25A2.25 2.25 0 0 0 13.5 6v2.25a2.25 2.25 0 0 0 2.25 2.25Z" />
</svg>
<span>Добавить на главный экран</span>
</button>
<div v-else class="flex items-center justify-center gap-3 mb-2 p-3 bg-success/10 rounded-lg">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-6 text-success">
<path stroke-linecap="round" stroke-linejoin="round" d="M9 12.75 11.25 15 15 9.75M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z" />
</svg>
<span class="text-success font-medium">Приложение уже добавлено на главный экран</span>
</div>
<p v-if="!isHomeScreenAdded" class="text-xs text-base-content/50 text-center">
Добавьте приложение на главный экран для быстрого доступа
</p>
</div>
</div>
</div>
</BaseViewWrapper>
</template>
<script setup>
import {computed, ref} from "vue";
import {useTgData} from "@/composables/useTgData.js";
import {useSettingsStore} from "@/stores/SettingsStore.js";
import BaseViewWrapper from "@/views/BaseViewWrapper.vue";
import {onMounted, onUnmounted} from "vue";
import {useRoute} from "vue-router";
import {useYaMetrikaStore} from "@/stores/yaMetrikaStore.js";
import {usePulseStore} from "@/stores/Pulse.js";
import {useHapticFeedback} from "@/composables/useHapticFeedback.js";
defineOptions({
name: 'Account'
});
const tgData = useTgData();
const settings = useSettingsStore();
const route = useRoute();
const yaMetrika = useYaMetrikaStore();
const pulse = usePulseStore();
const haptic = useHapticFeedback();
const username = computed(() => {
if (tgData?.user?.first_name || tgData?.user?.last_name) {
const parts = [tgData.user.first_name, tgData.user.last_name].filter(Boolean);
return parts.join(' ') || 'Пользователь';
}
return tgData?.user?.username ? `@${tgData.user.username}` : 'Пользователь';
});
const isHomeScreenSupported = ref(false);
const isHomeScreenAdded = ref(false);
const daysWithUs = ref(null);
// Функция для склонения слова "день" по правилам русского языка
function getDaysWord(days) {
const lastDigit = days % 10;
const lastTwoDigits = days % 100;
// Исключения для 11-14
if (lastTwoDigits >= 11 && lastTwoDigits <= 14) {
return 'дней';
}
// Склонение по последней цифре
if (lastDigit === 1) {
return 'день';
} else if (lastDigit >= 2 && lastDigit <= 4) {
return 'дня';
} else {
return 'дней';
}
}
const daysWord = computed(() => {
if (daysWithUs.value === null) return '';
return getDaysWord(daysWithUs.value);
});
function openManagerChat() {
if (!settings.manager_username) {
window.Telegram.WebApp.showAlert('Менеджер недоступен');
return;
}
haptic.selectionChanged();
// Формируем ссылку для открытия чата с менеджером
const managerUsername = String(settings.manager_username).trim();
const username = managerUsername.startsWith('@')
? managerUsername.substring(1)
: managerUsername;
const chatUrl = `https://t.me/${username}`;
window.Telegram.WebApp.openTelegramLink(chatUrl);
}
function handleHomeScreenAdded() {
haptic.notificationOccurred('success');
window.Telegram.WebApp.showAlert('Приложение успешно добавлено на главный экран!');
isHomeScreenAdded.value = true;
}
function checkHomeScreenSupport() {
if (window.Telegram?.WebApp?.checkHomeScreenStatus) {
window.Telegram.WebApp.checkHomeScreenStatus((status) => {
isHomeScreenSupported.value = status !== 'unsupported';
if (status === 'added') {
isHomeScreenAdded.value = true;
}
});
} else if (window.Telegram?.WebApp?.addToHomeScreen) {
// Если есть метод addToHomeScreen, значит функция поддерживается
isHomeScreenSupported.value = true;
}
}
function addToHomeScreen() {
haptic.selectionChanged();
// Используем Telegram Web App API для добавления на главный экран
if (window.Telegram?.WebApp?.addToHomeScreen) {
window.Telegram.WebApp.addToHomeScreen();
} else {
// Fallback для старых версий или если метод недоступен
window.Telegram.WebApp.showAlert('Функция добавления на главный экран недоступна в вашей версии Telegram.');
}
}
function calculateDaysWithUs() {
const createdAt = pulse.customer_created_at;
if (createdAt) {
const createdDate = new Date(createdAt);
const now = new Date();
const diffTime = now - createdDate;
const diffDays = Math.floor(diffTime / (1000 * 60 * 60 * 24));
// Если 0 дней, показываем 1 день
daysWithUs.value = diffDays === 0 ? 1 : diffDays;
}
}
onMounted(() => {
window.document.title = 'Профиль';
yaMetrika.pushHit(route.path, {
title: 'Профиль',
});
// Вычисляем количество дней с момента первого визита
calculateDaysWithUs();
// Проверяем поддержку функции добавления на главный экран
checkHomeScreenSupport();
// Регистрируем обработчик события успешного добавления на главный экран
if (window.Telegram?.WebApp?.onEvent) {
window.Telegram.WebApp.onEvent('homeScreenAdded', handleHomeScreenAdded);
}
});
onUnmounted(() => {
// Удаляем обработчик события при размонтировании компонента
if (window.Telegram?.WebApp?.offEvent) {
window.Telegram.WebApp.offEvent('homeScreenAdded', handleHomeScreenAdded);
}
});
</script>
<style scoped>
.account-page {
padding-bottom: 1rem;
}
.menu li > a {
padding: 1rem;
width: 100%;
}
.menu li > a:active {
background-color: hsl(var(--b2));
}
</style>