Reflex/src/components/ReportModal.vue

352 lines
7.7 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>
<div v-if="isVisible" class="report-modal-overlay" @click="closeModal">
<div class="report-modal" @click.stop>
<div class="modal-header">
<h3>Пожаловаться на пользователя</h3>
<button class="close-btn" @click="closeModal">
<i class="bi-x"></i>
</button>
</div>
<div class="modal-content">
<div v-if="error" class="error-message">
{{ error }}
</div>
<div v-if="success" class="success-message">
{{ success }}
</div>
<form @submit.prevent="submitReport" v-if="!success">
<div class="form-group">
<label for="reason">Причина жалобы *</label>
<select id="reason" v-model="reportData.reason" required>
<option value="">Выберите причину</option>
<option value="inappropriate_content">Неподходящий контент</option>
<option value="fake_profile">Поддельный профиль</option>
<option value="harassment">Домогательства</option>
<option value="spam">Спам</option>
<option value="offensive_behavior">Оскорбительное поведение</option>
<option value="underage">Несовершеннолетний</option>
<option value="other">Другое</option>
</select>
</div>
<div class="form-group">
<label for="description">Дополнительная информация</label>
<textarea
id="description"
v-model="reportData.description"
placeholder="Опишите подробнее, что произошло (необязательно)"
rows="4"
maxlength="500"
></textarea>
<div class="char-count">
{{ reportData.description.length }}/500
</div>
</div>
<div class="form-actions">
<button type="button" class="btn btn-secondary" @click="closeModal" :disabled="loading">
Отмена
</button>
<button type="submit" class="btn btn-primary" :disabled="loading || !reportData.reason">
<span v-if="loading" class="spinner"></span>
{{ loading ? 'Отправка...' : 'Отправить жалобу' }}
</button>
</div>
</form>
<div v-if="success" class="success-actions">
<button class="btn btn-primary" @click="closeModal">
Закрыть
</button>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { ref, reactive, watch } from 'vue';
import api from '@/services/api';
const props = defineProps({
isVisible: {
type: Boolean,
default: false
},
reportedUserId: {
type: String,
required: true
},
reportedUserName: {
type: String,
default: ''
}
});
const emit = defineEmits(['close', 'reported']);
const loading = ref(false);
const error = ref('');
const success = ref('');
const reportData = reactive({
reason: '',
description: ''
});
// Сброс формы при открытии модального окна
watch(() => props.isVisible, (newValue) => {
if (newValue) {
reportData.reason = '';
reportData.description = '';
error.value = '';
success.value = '';
}
});
const closeModal = () => {
emit('close');
};
const submitReport = async () => {
if (!reportData.reason) {
error.value = 'Пожалуйста, выберите причину жалобы';
return;
}
loading.value = true;
error.value = '';
try {
const response = await api.createReport({
reportedUserId: props.reportedUserId,
reason: reportData.reason,
description: reportData.description.trim()
});
success.value = response.data.message;
emit('reported');
// Закрываем модальное окно через 2 секунды
setTimeout(() => {
closeModal();
}, 2000);
} catch (err) {
console.error('Ошибка при отправке жалобы:', err);
error.value = err.response?.data?.message || 'Произошла ошибка при отправке жалобы';
} finally {
loading.value = false;
}
};
</script>
<style scoped>
.report-modal-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
backdrop-filter: blur(4px);
display: flex;
align-items: center;
justify-content: center;
z-index: 1000;
padding: 1rem;
}
.report-modal {
background: white;
border-radius: 16px;
max-width: 500px;
width: 100%;
max-height: 90vh;
overflow-y: auto;
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.2);
}
.modal-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 1.5rem 1.5rem 0;
border-bottom: 1px solid #eee;
margin-bottom: 1.5rem;
}
.modal-header h3 {
margin: 0;
color: #333;
font-size: 1.2rem;
font-weight: 600;
}
.close-btn {
background: none;
border: none;
font-size: 1.5rem;
color: #666;
cursor: pointer;
padding: 0.25rem;
border-radius: 4px;
transition: all 0.2s ease;
}
.close-btn:hover {
background-color: #f5f5f5;
color: #333;
}
.modal-content {
padding: 0 1.5rem 1.5rem;
}
.error-message {
background-color: #ffebee;
border-left: 4px solid #f44336;
color: #d32f2f;
padding: 1rem;
border-radius: 4px;
margin-bottom: 1rem;
}
.success-message {
background-color: #e8f5e8;
border-left: 4px solid #4caf50;
color: #2e7d32;
padding: 1rem;
border-radius: 4px;
margin-bottom: 1rem;
}
.form-group {
margin-bottom: 1.5rem;
}
.form-group label {
display: block;
margin-bottom: 0.5rem;
color: #333;
font-weight: 500;
}
.form-group select,
.form-group textarea {
width: 100%;
padding: 0.75rem;
border: 2px solid #e1e1e1;
border-radius: 8px;
font-size: 1rem;
transition: border-color 0.2s ease;
font-family: inherit;
}
.form-group select:focus,
.form-group textarea:focus {
outline: none;
border-color: #667eea;
}
.form-group textarea {
resize: vertical;
min-height: 100px;
}
.char-count {
text-align: right;
font-size: 0.875rem;
color: #666;
margin-top: 0.25rem;
}
.form-actions,
.success-actions {
display: flex;
gap: 1rem;
justify-content: flex-end;
margin-top: 2rem;
}
.btn {
padding: 0.75rem 1.5rem;
border: none;
border-radius: 8px;
font-size: 1rem;
font-weight: 500;
cursor: pointer;
transition: all 0.2s ease;
display: flex;
align-items: center;
gap: 0.5rem;
}
.btn:disabled {
opacity: 0.6;
cursor: not-allowed;
}
.btn-secondary {
background-color: #f5f5f5;
color: #666;
}
.btn-secondary:hover:not(:disabled) {
background-color: #eeeeee;
}
.btn-primary {
background: linear-gradient(45deg, #667eea, #764ba2);
color: white;
}
.btn-primary:hover:not(:disabled) {
transform: translateY(-1px);
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.3);
}
.spinner {
width: 16px;
height: 16px;
border: 2px solid transparent;
border-top: 2px solid currentColor;
border-radius: 50%;
animation: spin 1s linear infinite;
}
@keyframes spin {
to {
transform: rotate(360deg);
}
}
/* Адаптивные стили */
@media (max-width: 576px) {
.report-modal {
margin: 1rem;
max-width: calc(100% - 2rem);
}
.modal-header {
padding: 1rem 1rem 0;
margin-bottom: 1rem;
}
.modal-content {
padding: 0 1rem 1rem;
}
.form-actions {
flex-direction: column;
}
.btn {
width: 100%;
justify-content: center;
}
}
</style>