Reflex/src/views/DeviceBlockedPage.vue

666 lines
16 KiB
Vue
Raw Normal View History

<template>
<div class="device-blocked-page">
<div class="blocked-container">
<div class="blocked-icon">
<svg viewBox="0 0 24 24" width="80" height="80" fill="currentColor">
<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zM13 17h-2v-6h2v6zm0-8h-2V7h2v2z"/>
</svg>
</div>
<div class="blocked-content">
<h1>Устройство заблокировано</h1>
<div class="block-info">
<p class="main-message">
Ваше устройство было заблокировано администрацией сервиса.
</p>
<div v-if="blockInfo" class="block-details">
<div class="detail-item">
<strong>Причина блокировки:</strong>
<span>{{ blockInfo.reason || 'Нарушение правил сервиса' }}</span>
</div>
<div v-if="blockInfo.blockedAt" class="detail-item">
<strong>Дата блокировки:</strong>
<span>{{ formatDate(blockInfo.blockedAt) }}</span>
</div>
<div v-if="blockInfo.deviceId" class="detail-item">
<strong>ID устройства:</strong>
<code>{{ blockInfo.deviceId.slice(-8) }}</code>
</div>
</div>
</div>
<div class="help-section">
<h3>Что это означает?</h3>
<ul>
<li>Ваше устройство заблокировано для использования сервиса</li>
<li>Доступ к функциям приложения ограничен</li>
<li>Блокировка может быть связана с нарушением правил</li>
</ul>
</div>
<div class="contact-section">
<h3>Как обжаловать блокировку?</h3>
<p>
Если вы считаете, что блокировка была применена ошибочно,
вы можете обратиться в службу поддержки:
</p>
<div class="contact-methods">
<div class="contact-item">
<strong>Email:</strong>
<a href="mailto:support@datingapp.com">support@datingapp.com</a>
</div>
<div class="contact-item">
<strong>Telegram:</strong>
<a href="https://t.me/datingapp_support" target="_blank">@datingapp_support</a>
</div>
</div>
<div class="appeal-form">
<h4>Отправить обращение</h4>
<form @submit.prevent="submitAppeal" class="appeal-form-content">
<div class="form-group">
<label for="email">Ваш email:</label>
<input
type="email"
id="email"
v-model="appealForm.email"
required
placeholder="your@email.com"
>
</div>
<div class="form-group">
<label for="message">Сообщение:</label>
<textarea
id="message"
v-model="appealForm.message"
required
placeholder="Опишите ситуацию и почему считаете блокировку ошибочной..."
rows="4"
></textarea>
</div>
<button
type="submit"
:disabled="appealLoading"
class="btn btn-primary"
>
{{ appealLoading ? 'Отправка...' : 'Отправить обращение' }}
</button>
</form>
<div v-if="appealSent" class="appeal-success">
<p> Ваше обращение отправлено! Мы рассмотрим его в ближайшее время.</p>
</div>
</div>
</div>
<div class="additional-info">
<h3>Дополнительная информация</h3>
<div class="info-grid">
<div class="info-card">
<h4>Правила сервиса</h4>
<p>Ознакомьтесь с правилами использования нашего сервиса</p>
<a href="/terms" class="link">Читать правила</a>
</div>
<div class="info-card">
<h4>Безопасность</h4>
<p>Узнайте больше о мерах безопасности</p>
<a href="/security" class="link">О безопасности</a>
</div>
</div>
</div>
<div class="bypass-warning">
<div class="warning-icon"></div>
<div class="warning-text">
<strong>Внимание!</strong>
Попытки обойти блокировку устройства могут привести к
дополнительным ограничениям и продлению срока блокировки.
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import { ref, reactive, onMounted } from 'vue';
import deviceSecurityService from '@/services/deviceSecurityService';
export default {
name: 'DeviceBlockedPage',
setup() {
const blockInfo = ref(null);
const appealSent = ref(false);
const appealLoading = ref(false);
const appealForm = reactive({
email: '',
message: ''
});
// Загрузка информации о блокировке
const loadBlockInfo = () => {
try {
// Получаем информацию о блокировке из deviceSecurityService
if (deviceSecurityService && typeof deviceSecurityService.getBlockInfo === 'function') {
const info = deviceSecurityService.getBlockInfo();
blockInfo.value = info;
console.log('[DeviceBlockedPage] Информация о блокировке:', info);
}
// Также проверяем localStorage
const storedBlockInfo = localStorage.getItem('deviceBlockInfo');
if (storedBlockInfo) {
try {
const parsedInfo = JSON.parse(storedBlockInfo);
if (!blockInfo.value) {
blockInfo.value = parsedInfo;
}
} catch (e) {
console.error('[DeviceBlockedPage] Ошибка парсинга данных блокировки:', e);
}
}
// Если нет информации, создаем базовую
if (!blockInfo.value) {
blockInfo.value = {
reason: 'Нарушение правил сервиса',
blockedAt: new Date().toISOString(),
deviceId: 'unknown'
};
}
} catch (error) {
console.error('[DeviceBlockedPage] Ошибка загрузки информации о блокировке:', error);
}
};
// Отправка обращения
const submitAppeal = async () => {
if (!appealForm.email || !appealForm.message) {
alert('Пожалуйста, заполните все поля');
return;
}
appealLoading.value = true;
try {
// Здесь можно отправить обращение на email или в систему
// Пока что симулируем отправку
console.log('[DeviceBlockedPage] Отправка обращения:', appealForm);
// Симуляция отправки
await new Promise(resolve => setTimeout(resolve, 2000));
// Сохраняем информацию о том, что обращение отправлено
localStorage.setItem('appealSent', JSON.stringify({
email: appealForm.email,
message: appealForm.message,
sentAt: new Date().toISOString(),
deviceId: blockInfo.value?.deviceId
}));
appealSent.value = true;
// Очищаем форму
appealForm.email = '';
appealForm.message = '';
} catch (error) {
console.error('[DeviceBlockedPage] Ошибка отправки обращения:', error);
alert('Ошибка при отправке обращения. Попробуйте позже.');
} finally {
appealLoading.value = false;
}
};
// Форматирование даты
const formatDate = (dateString) => {
if (!dateString) return 'Неизвестно';
try {
return new Date(dateString).toLocaleString('ru-RU', {
year: 'numeric',
month: 'long',
day: 'numeric',
hour: '2-digit',
minute: '2-digit'
});
} catch (err) {
return 'Неверная дата';
}
};
// Проверка, было ли уже отправлено обращение
const checkAppealStatus = () => {
try {
const storedAppeal = localStorage.getItem('appealSent');
if (storedAppeal) {
const appealData = JSON.parse(storedAppeal);
// Проверяем, было ли обращение отправлено в последние 24 часа
const sentAt = new Date(appealData.sentAt);
const now = new Date();
const hoursDiff = (now - sentAt) / (1000 * 60 * 60);
if (hoursDiff < 24) {
appealSent.value = true;
appealForm.email = appealData.email;
}
}
} catch (error) {
console.error('[DeviceBlockedPage] Ошибка проверки статуса обращения:', error);
}
};
onMounted(() => {
loadBlockInfo();
checkAppealStatus();
// Отправляем событие аналитики
if (typeof gtag !== 'undefined') {
gtag('event', 'device_blocked_page_view', {
'event_category': 'security',
'event_label': 'device_blocked'
});
}
});
return {
blockInfo,
appealForm,
appealSent,
appealLoading,
submitAppeal,
formatDate
};
}
};
</script>
<style scoped>
.device-blocked-page {
min-height: 100vh;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
display: flex;
justify-content: center;
align-items: center;
padding: 20px;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
}
.blocked-container {
background: white;
border-radius: 16px;
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1);
max-width: 700px;
width: 100%;
overflow: hidden;
}
.blocked-icon {
text-align: center;
padding: 40px 20px 20px;
background: linear-gradient(135deg, #ff6b6b, #ee5a52);
color: white;
}
.blocked-content {
padding: 30px;
}
.blocked-content h1 {
text-align: center;
color: #333;
margin: 0 0 25px 0;
font-size: 28px;
font-weight: 700;
}
.block-info {
background: #f8f9fa;
border-radius: 12px;
padding: 20px;
margin-bottom: 30px;
border-left: 4px solid #dc3545;
}
.main-message {
font-size: 16px;
color: #555;
margin: 0 0 20px 0;
line-height: 1.6;
}
.block-details {
border-top: 1px solid #dee2e6;
padding-top: 15px;
}
.detail-item {
display: flex;
margin-bottom: 10px;
flex-wrap: wrap;
gap: 10px;
}
.detail-item strong {
color: #333;
min-width: 140px;
}
.detail-item code {
background: #e9ecef;
padding: 2px 6px;
border-radius: 4px;
font-family: monospace;
font-size: 12px;
}
.help-section {
margin-bottom: 30px;
}
.help-section h3 {
color: #333;
margin: 0 0 15px 0;
font-size: 20px;
}
.help-section ul {
list-style: none;
padding: 0;
margin: 0;
}
.help-section li {
padding: 8px 0;
color: #555;
position: relative;
padding-left: 20px;
}
.help-section li::before {
content: '•';
color: #dc3545;
font-size: 18px;
position: absolute;
left: 0;
}
.contact-section {
background: #f8f9fa;
border-radius: 12px;
padding: 25px;
margin-bottom: 30px;
}
.contact-section h3 {
color: #333;
margin: 0 0 15px 0;
font-size: 20px;
}
.contact-methods {
margin: 20px 0;
}
.contact-item {
display: flex;
align-items: center;
margin-bottom: 10px;
gap: 10px;
flex-wrap: wrap;
}
.contact-item strong {
color: #333;
min-width: 80px;
}
.contact-item a {
color: #007bff;
text-decoration: none;
}
.contact-item a:hover {
text-decoration: underline;
}
.appeal-form {
margin-top: 25px;
border-top: 1px solid #dee2e6;
padding-top: 20px;
}
.appeal-form h4 {
color: #333;
margin: 0 0 20px 0;
font-size: 18px;
}
.appeal-form-content {
max-width: 100%;
}
.form-group {
margin-bottom: 20px;
}
.form-group label {
display: block;
margin-bottom: 8px;
font-weight: 600;
color: #333;
}
.form-group input,
.form-group textarea {
width: 100%;
padding: 12px;
border: 1px solid #ddd;
border-radius: 8px;
font-size: 14px;
transition: border-color 0.3s;
box-sizing: border-box;
}
.form-group input:focus,
.form-group textarea:focus {
outline: none;
border-color: #007bff;
box-shadow: 0 0 0 3px rgba(0, 123, 255, 0.1);
}
.form-group textarea {
resize: vertical;
min-height: 100px;
}
.appeal-success {
background: #d4edda;
color: #155724;
padding: 15px;
border-radius: 8px;
margin-top: 20px;
border: 1px solid #c3e6cb;
}
.additional-info {
margin-bottom: 30px;
}
.additional-info h3 {
color: #333;
margin: 0 0 20px 0;
font-size: 20px;
}
.info-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 20px;
}
.info-card {
background: #f8f9fa;
border-radius: 12px;
padding: 20px;
text-align: center;
}
.info-card h4 {
color: #333;
margin: 0 0 10px 0;
font-size: 16px;
}
.info-card p {
color: #666;
margin: 0 0 15px 0;
line-height: 1.5;
}
.link {
color: #007bff;
text-decoration: none;
font-weight: 500;
}
.link:hover {
text-decoration: underline;
}
.bypass-warning {
background: #fff3cd;
border: 1px solid #ffeaa7;
border-radius: 12px;
padding: 20px;
display: flex;
align-items: flex-start;
gap: 15px;
}
.warning-icon {
font-size: 24px;
flex-shrink: 0;
}
.warning-text {
color: #856404;
line-height: 1.5;
}
.warning-text strong {
color: #721c24;
}
.btn {
background: #007bff;
color: white;
border: none;
padding: 12px 24px;
border-radius: 8px;
cursor: pointer;
font-size: 14px;
font-weight: 500;
transition: all 0.3s;
display: inline-block;
text-decoration: none;
}
.btn:hover:not(:disabled) {
background: #0056b3;
transform: translateY(-1px);
}
.btn:disabled {
opacity: 0.6;
cursor: not-allowed;
transform: none;
}
.btn-primary {
background: #007bff;
}
.btn-primary:hover:not(:disabled) {
background: #0056b3;
}
/* Responsive design */
@media (max-width: 768px) {
.device-blocked-page {
padding: 10px;
}
.blocked-content {
padding: 20px;
}
.blocked-content h1 {
font-size: 24px;
}
.detail-item {
flex-direction: column;
gap: 5px;
}
.detail-item strong {
min-width: auto;
}
.contact-item {
flex-direction: column;
align-items: flex-start;
gap: 5px;
}
.contact-item strong {
min-width: auto;
}
.info-grid {
grid-template-columns: 1fr;
}
.bypass-warning {
flex-direction: column;
gap: 10px;
}
}
@media (max-width: 480px) {
.blocked-container {
border-radius: 8px;
}
.blocked-icon {
padding: 30px 20px 15px;
}
.blocked-content {
padding: 15px;
}
.block-info,
.contact-section {
padding: 15px;
}
.form-group input,
.form-group textarea {
padding: 10px;
}
}
</style>