Reflex/src/views/admin/AdminReportDetail.vue
Professional 52e8199ba5 фикс
2025-05-26 20:54:52 +07:00

1586 lines
35 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 class="admin-report-detail">
<div class="header-controls">
<button @click="goBack" class="back-btn">
<i class="bi-arrow-left"></i>
<span class="btn-text">Назад к списку жалоб</span>
</button>
</div>
<div v-if="loading" class="loading-indicator">
<div class="loading-spinner"></div>
<span>Загрузка информации о жалобе...</span>
</div>
<div v-if="error" class="error-message">
<i class="bi-exclamation-triangle"></i>
{{ error }}
</div>
<div v-if="report && !loading" class="report-container">
<div class="page-header">
<h2>Детали жалобы</h2>
<div class="status-display">
<span class="status-badge" :class="getStatusClass(report.status)">
{{ getStatusText(report.status) }}
</span>
</div>
</div>
<div class="report-card">
<div class="report-header">
<div class="report-info">
<div class="report-id-section">
<i class="bi-file-text report-icon"></i>
<div class="id-info">
<h3 class="report-title">Жалоба #{{ report._id.substring(0, 8) }}</h3>
<div class="full-id-container">
<span class="id-label">ID:</span>
<span class="full-id">{{ report._id }}</span>
</div>
</div>
</div>
</div>
<div class="report-date">
<i class="bi-calendar3 date-icon"></i>
<div class="date-info">
<span class="date-label">Подана</span>
<span class="date-value">{{ formatDate(report.createdAt) }}</span>
</div>
</div>
</div>
<div class="report-content">
<div class="info-grid">
<div class="info-section">
<div class="section-header">
<i class="bi-info-circle"></i>
<h4>Информация о жалобе</h4>
</div>
<div class="info-item">
<label>Причина жалобы:</label>
<span class="reason-badge" :class="getReasonClass(report.reason)">
{{ getReasonText(report.reason) }}
</span>
</div>
<div class="info-item" v-if="report.description">
<label>Описание:</label>
<div class="description-container">
<p class="description">{{ report.description }}</p>
</div>
</div>
</div>
<div class="info-section">
<div class="section-header">
<i class="bi-people"></i>
<h4>Участники</h4>
</div>
<div class="users-grid">
<div class="user-card reporter">
<div class="user-header">
<i class="bi-person-exclamation"></i>
<span class="user-role">Жалующийся</span>
</div>
<div class="user-details">
<div class="user-name">{{ report.reporter?.name || 'Пользователь удален' }}</div>
<div class="user-email">{{ report.reporter?.email || '' }}</div>
</div>
</div>
<!-- Индикатор стрелки адаптирован для мобильных устройств -->
<div class="arrow-divider">
<i class="bi-arrow-right arrow-icon"></i>
<span class="vs-text">vs</span>
</div>
<div class="user-card reported">
<div class="user-header">
<i class="bi-person-x"></i>
<span class="user-role">Обжалуемый</span>
</div>
<div class="user-details">
<div class="user-name">{{ report.reportedUser?.name || 'Пользователь удален' }}</div>
<div class="user-email">{{ report.reportedUser?.email || '' }}</div>
<button
v-if="report.reportedUser?._id"
@click="viewUser(report.reportedUser._id)"
class="btn view-user-btn"
>
<i class="bi-eye"></i>
<span>Просмотр профиля</span>
</button>
</div>
</div>
</div>
</div>
<div class="info-section" v-if="report.adminNotes || report.resolvedAt">
<div class="section-header">
<i class="bi-shield-check"></i>
<h4>Административная информация</h4>
</div>
<div class="info-item" v-if="report.adminNotes">
<label>Заметки администратора:</label>
<div class="admin-notes-container">
<p class="admin-notes">{{ report.adminNotes }}</p>
</div>
</div>
<div class="info-item" v-if="report.resolvedAt">
<label>Дата рассмотрения:</label>
<div class="resolve-date">
<i class="bi-check-circle"></i>
<span>{{ formatDate(report.resolvedAt) }}</span>
</div>
</div>
</div>
</div>
</div>
<div class="report-actions" v-if="report.status === 'pending'">
<div class="actions-header">
<i class="bi-gear"></i>
<h4>Действия с жалобой</h4>
</div>
<div class="action-buttons">
<button @click="openResolveModal" class="btn resolve-btn">
<i class="bi-check-circle"></i>
<span>Рассмотреть</span>
</button>
<button @click="openDismissModal" class="btn dismiss-btn">
<i class="bi-x-circle"></i>
<span>Отклонить</span>
</button>
</div>
</div>
</div>
</div>
<!-- Модальное окно для рассмотрения жалобы -->
<div v-if="showResolveModal" class="modal-overlay" @click="showResolveModal = false">
<div class="modal" @click.stop>
<div class="modal-header">
<h3>Рассмотрение жалобы</h3>
<button @click="showResolveModal = false" class="modal-close">
<i class="bi-x"></i>
</button>
</div>
<div class="modal-content">
<p>Вы собираетесь отметить эту жалобу как рассмотренную.</p>
<div class="form-group">
<label>Заметки администратора (необязательно):</label>
<textarea
v-model="adminNotes"
placeholder="Укажите принятые меры или комментарии..."
rows="4"
></textarea>
</div>
</div>
<div class="modal-actions">
<button @click="updateReportStatus('resolved')" class="btn resolve-btn" :disabled="updating">
<i class="bi-check-circle"></i>
<span>{{ updating ? 'Обработка...' : 'Рассмотреть' }}</span>
</button>
<button @click="showResolveModal = false" class="btn cancel-btn">
<i class="bi-x"></i>
<span>Отмена</span>
</button>
</div>
</div>
</div>
<!-- Модальное окно для отклонения жалобы -->
<div v-if="showDismissModal" class="modal-overlay" @click="showDismissModal = false">
<div class="modal" @click.stop>
<div class="modal-header">
<h3>Отклонение жалобы</h3>
<button @click="showDismissModal = false" class="modal-close">
<i class="bi-x"></i>
</button>
</div>
<div class="modal-content">
<p>Вы собираетесь отклонить эту жалобу.</p>
<div class="form-group">
<label>Заметки администратора (необязательно):</label>
<textarea
v-model="adminNotes"
placeholder="Укажите причину отклонения..."
rows="4"
></textarea>
</div>
</div>
<div class="modal-actions">
<button @click="updateReportStatus('dismissed')" class="btn dismiss-btn" :disabled="updating">
<i class="bi-x-circle"></i>
<span>{{ updating ? 'Обработка...' : 'Отклонить' }}</span>
</button>
<button @click="showDismissModal = false" class="btn cancel-btn">
<i class="bi-arrow-left"></i>
<span>Отмена</span>
</button>
</div>
</div>
</div>
</div>
</template>
<script>
import { ref, onMounted } from 'vue';
import { useRouter, useRoute } from 'vue-router';
import axios from 'axios';
export default {
name: 'AdminReportDetail',
props: {
id: {
type: String,
required: true
}
},
setup(props) {
const router = useRouter();
const route = useRoute();
const report = ref(null);
const loading = ref(false);
const error = ref(null);
const updating = ref(false);
const showResolveModal = ref(false);
const showDismissModal = ref(false);
const adminNotes = ref('');
// Загрузка информации о жалобе
const loadReport = async () => {
loading.value = true;
error.value = null;
try {
const token = localStorage.getItem('userToken');
const response = await axios.get(`/api/admin/reports/${props.id}`, {
headers: {
Authorization: `Bearer ${token}`
}
});
report.value = response.data;
} catch (err) {
console.error('Ошибка при загрузке жалобы:', err);
error.value = 'Ошибка при загрузке информации о жалобе.';
} finally {
loading.value = false;
}
};
// Обновление статуса жалобы
const updateReportStatus = async (status) => {
console.log('updateReportStatus called with status:', status);
updating.value = true;
try {
const token = localStorage.getItem('userToken');
const response = await axios.put(`/api/admin/reports/${props.id}`, {
status,
adminNotes: adminNotes.value
}, {
headers: {
Authorization: `Bearer ${token}`
}
});
// Обновляем локальные данные
report.value = response.data;
// Закрываем модальные окна
showResolveModal.value = false;
showDismissModal.value = false;
adminNotes.value = '';
alert(`Жалоба успешно ${status === 'resolved' ? 'рассмотрена' : 'отклонена'}`);
} catch (err) {
console.error('Ошибка при обновлении статуса жалобы:', err);
alert('Ошибка при обновлении статуса жалобы');
} finally {
updating.value = false;
}
};
// Функция для открытия модального окна рассмотрения
const openResolveModal = () => {
console.log('openResolveModal called, current showResolveModal:', showResolveModal.value);
showResolveModal.value = true;
console.log('showResolveModal set to:', showResolveModal.value);
};
// Функция для открытия модального окна отклонения
const openDismissModal = () => {
console.log('openDismissModal called, current showDismissModal:', showDismissModal.value);
showDismissModal.value = true;
console.log('showDismissModal set to:', showDismissModal.value);
};
// Возврат к списку жалоб
const goBack = () => {
router.push({ name: 'AdminReports' });
};
// Просмотр профиля пользователя
const viewUser = (userId) => {
router.push({ name: 'AdminUserDetail', params: { id: userId } });
};
// Форматирование даты
const formatDate = (dateString) => {
if (!dateString) return '';
const date = new Date(dateString);
return date.toLocaleString('ru-RU', {
day: '2-digit',
month: '2-digit',
year: 'numeric',
hour: '2-digit',
minute: '2-digit'
});
};
// Получение текста причины жалобы
const getReasonText = (reason) => {
const reasons = {
inappropriate_content: 'Неподходящий контент',
fake_profile: 'Фальшивый профиль',
harassment: 'Домогательства',
spam: 'Спам',
offensive_behavior: 'Оскорбительное поведение',
underage: 'Несовершеннолетний',
other: 'Другое'
};
return reasons[reason] || reason;
};
// Получение CSS класса для причины
const getReasonClass = (reason) => {
const classes = {
inappropriate_content: 'reason-inappropriate',
fake_profile: 'reason-fake',
harassment: 'reason-harassment',
spam: 'reason-spam',
offensive_behavior: 'reason-offensive',
underage: 'reason-underage',
other: 'reason-other'
};
return classes[reason] || 'reason-other';
};
// Получение текста статуса
const getStatusText = (status) => {
const statuses = {
pending: 'На рассмотрении',
resolved: 'Рассмотрена',
dismissed: 'Отклонена'
};
return statuses[status] || status;
};
// Получение CSS класса для статуса
const getStatusClass = (status) => {
const classes = {
pending: 'status-pending',
resolved: 'status-resolved',
dismissed: 'status-dismissed'
};
return classes[status] || 'status-pending';
};
onMounted(() => {
loadReport();
// Отладочная информация о состоянии модальных окон
console.log('Component mounted, initial modal states:');
console.log('showResolveModal:', showResolveModal.value);
console.log('showDismissModal:', showDismissModal.value);
});
return {
report,
loading,
error,
updating,
showResolveModal,
showDismissModal,
adminNotes,
loadReport,
updateReportStatus,
openResolveModal,
openDismissModal,
goBack,
viewUser,
formatDate,
getReasonText,
getReasonClass,
getStatusText,
getStatusClass
};
}
}
</script>
<style scoped>
.admin-report-detail {
/* Base padding с поддержкой safe-area для устройств с вырезом */
padding: 1.5rem 1rem calc(60px + env(safe-area-inset-bottom, 0px)) 1rem;
max-width: 100%;
overflow-x: hidden;
box-sizing: border-box;
min-height: calc(100vh - 80px);
}
.header-controls {
margin-bottom: 1.5rem;
}
.back-btn {
background: #6c757d;
color: white;
border: none;
padding: 0.6rem 1rem;
border-radius: 0.375rem;
cursor: pointer;
font-weight: 500;
transition: all 0.2s ease;
text-decoration: none;
display: inline-flex;
align-items: center;
gap: 0.5rem;
min-height: 40px;
}
.back-btn:hover {
background: #545b62;
transform: translateY(-1px);
}
.back-btn .btn-text {
display: inline;
}
.loading-indicator {
text-align: center;
padding: 2rem;
color: #666;
font-style: italic;
}
.loading-spinner {
border: 4px solid rgba(255, 255, 255, 0.3);
border-top: 4px solid #007bff;
border-radius: 50%;
width: 2rem;
height: 2rem;
animation: spin 0.8s linear infinite;
margin: 0 auto 1rem auto;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
.error-message {
background-color: #f8d7da;
color: #721c24;
padding: 0.75rem 1rem;
border-radius: 0.375rem;
border: 1px solid #f5c6cb;
margin-bottom: 1rem;
display: flex;
align-items: center;
gap: 0.5rem;
}
h2 {
margin-top: 0;
margin-bottom: 1.5rem;
color: #333;
font-weight: 600;
font-size: 1.75rem;
}
.page-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 1.5rem;
flex-wrap: wrap;
gap: 1rem;
}
.status-display {
display: flex;
align-items: center;
gap: 0.5rem;
}
.report-container {
width: 100%;
overflow-x: hidden;
}
.report-card {
background: white;
border-radius: 0.75rem;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
overflow: hidden;
width: 100%;
box-sizing: border-box;
}
.report-header {
padding: 1.5rem;
background: linear-gradient(to right, #f8f9fa, #e9ecef);
border-bottom: 1px solid #dee2e6;
display: flex;
justify-content: space-between;
align-items: flex-start;
flex-wrap: wrap;
gap: 1rem;
min-height: 80px;
}
.report-info {
display: flex;
align-items: flex-start;
gap: 0.75rem;
flex: 1;
min-width: 0;
}
.report-id-section {
display: flex;
align-items: flex-start;
gap: 0.75rem;
min-width: 0;
flex: 1;
}
.report-icon {
color: #007bff;
font-size: 1.5rem;
margin-top: 0.25rem;
flex-shrink: 0;
}
.id-info {
min-width: 0;
flex: 1;
}
.report-title {
margin: 0 0 0.5rem 0;
color: #333;
font-size: 1.25rem;
font-weight: 600;
line-height: 1.2;
}
.full-id-container {
display: flex;
flex-wrap: wrap;
gap: 0.25rem;
align-items: baseline;
}
.id-label {
color: #6c757d;
font-size: 0.85rem;
font-weight: 500;
flex-shrink: 0;
}
.full-id {
color: #6c757d;
font-size: 0.85rem;
word-break: break-all;
font-family: 'Courier New', monospace;
background: rgba(108, 117, 125, 0.1);
padding: 0.125rem 0.25rem;
border-radius: 0.25rem;
flex: 1;
min-width: 0;
}
.report-date {
color: #6c757d;
font-size: 0.9rem;
display: flex;
align-items: flex-start;
gap: 0.5rem;
flex-shrink: 0;
text-align: right;
}
.date-icon {
color: #28a745;
font-size: 1.1rem;
margin-top: 0.125rem;
flex-shrink: 0;
}
.date-info {
display: flex;
flex-direction: column;
gap: 0.125rem;
text-align: right;
}
.date-label {
font-size: 0.8rem;
color: #6c757d;
font-weight: 500;
text-transform: uppercase;
letter-spacing: 0.5px;
}
.date-value {
font-size: 0.9rem;
color: #495057;
font-weight: 500;
white-space: nowrap;
}
.report-content {
padding: 1.5rem;
width: 100%;
box-sizing: border-box;
}
.info-grid {
display: grid;
gap: 1.5rem;
width: 100%;
}
.info-section {
background: #f8f9fa;
padding: 1.5rem;
border-radius: 0.375rem;
border: 1px solid #e9ecef;
width: 100%;
box-sizing: border-box;
overflow-wrap: break-word;
word-wrap: break-word;
}
.section-header {
display: flex;
align-items: center;
gap: 0.5rem;
margin-bottom: 1rem;
}
.section-header h4 {
margin: 0;
color: #495057;
font-weight: 500;
font-size: 1.1rem;
}
.info-item {
margin-bottom: 1rem;
}
.info-item:last-child {
margin-bottom: 0;
}
.info-item label {
display: block;
font-weight: 600;
color: #495057;
margin-bottom: 0.5rem;
}
.description,
.admin-notes {
background: #f8f9fa;
padding: 1rem;
border-radius: 0.375rem;
border: 1px solid #e9ecef;
margin: 0;
line-height: 1.6;
overflow-wrap: break-word;
word-wrap: break-word;
white-space: pre-line;
max-width: 100%;
}
.user-info {
display: flex;
flex-direction: column;
gap: 0.25rem;
}
.email {
color: #6c757d;
font-size: 0.9rem;
}
.view-user-btn {
background: #007bff;
color: white;
border: none;
padding: 0.4rem 0.75rem;
border-radius: 0.375rem;
cursor: pointer;
font-size: 0.85rem;
font-weight: 500;
margin-top: 0.5rem;
align-self: flex-start;
transition: all 0.2s ease;
display: inline-flex;
align-items: center;
gap: 0.5rem;
min-height: 32px;
}
.view-user-btn:hover {
background: #0056b3;
transform: translateY(-1px);
}
.reason-badge,
.status-badge {
padding: 0.25rem 0.75rem;
border-radius: 1rem;
font-size: 0.85rem;
font-weight: 500;
display: inline-block;
}
.reason-inappropriate { background-color: #f8d7da; color: #721c24; }
.reason-fake { background-color: #fff3cd; color: #856404; }
.reason-harassment { background-color: #d1ecf1; color: #0c5460; }
.reason-spam { background-color: #d4edda; color: #155724; }
.reason-offensive { background-color: #f8d7da; color: #721c24; }
.reason-underage { background-color: #f8d7da; color: #721c24; }
.reason-other { background-color: #e2e3e5; color: #383d41; }
.status-pending { background-color: #fff3cd; color: #856404; }
.status-resolved { background-color: #d4edda; color: #155724; }
.status-dismissed { background-color: #e2e3e5; color: #383d41; }
.report-actions {
padding: 1.5rem;
border-top: 1px solid #dee2e6;
background: #f8f9fa;
}
.actions-header {
display: flex;
align-items: center;
gap: 0.5rem;
margin-bottom: 1rem;
}
.action-buttons {
display: flex;
gap: 1rem;
flex-wrap: wrap;
}
.btn {
display: inline-flex;
align-items: center;
gap: 0.5rem;
padding: 0.6rem 1rem;
border: none;
border-radius: 0.375rem;
cursor: pointer;
font-weight: 500;
transition: all 0.2s ease;
text-decoration: none;
min-height: 40px;
}
.resolve-btn {
background: #28a745;
color: white;
}
.resolve-btn:hover:not(:disabled) {
background: #218838;
transform: translateY(-1px);
}
.dismiss-btn {
background: #dc3545;
color: white;
}
.dismiss-btn:hover:not(:disabled) {
background: #c82333;
transform: translateY(-1px);
}
.cancel-btn {
background: #6c757d;
color: white;
}
.cancel-btn:hover {
background: #545b62;
transform: translateY(-1px);
}
.btn:disabled {
opacity: 0.6;
cursor: not-allowed;
transform: none;
}
/* Модальные окна */
.modal-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.5);
display: flex;
align-items: center;
justify-content: center;
z-index: 1000;
padding: 1rem;
}
.modal {
background: white;
border-radius: 0.75rem;
padding: 1.5rem;
max-width: 500px;
width: 100%;
max-height: 90vh;
overflow-y: auto;
box-sizing: border-box;
}
.modal-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 1rem;
}
.modal-header h3 {
margin: 0;
color: #333;
font-size: 1.2rem;
}
.modal-close {
background: transparent;
border: none;
cursor: pointer;
font-size: 1.25rem;
color: #6c757d;
padding: 0.25rem;
min-width: 32px;
min-height: 32px;
display: flex;
align-items: center;
justify-content: center;
}
.modal-close:hover {
color: #333;
background: #f8f9fa;
border-radius: 0.25rem;
}
.form-group {
margin: 1rem 0;
}
.form-group label {
display: block;
font-weight: 600;
color: #495057;
margin-bottom: 0.5rem;
}
.form-group textarea {
width: 100%;
min-height: 100px;
padding: 0.75rem;
border: 1px solid #ced4da;
border-radius: 0.375rem;
font-family: inherit;
resize: vertical;
box-sizing: border-box;
}
.modal-actions {
display: flex;
gap: 1rem;
justify-content: flex-end;
margin-top: 1.5rem;
flex-wrap: wrap;
}
/* Пользовательские карточки в секции участников */
.users-grid {
display: grid;
grid-template-columns: 1fr auto 1fr;
gap: 1rem;
align-items: center;
width: 100%;
box-sizing: border-box;
}
.user-card {
background: white;
border: 1px solid #e9ecef;
border-radius: 0.5rem;
padding: 1rem;
transition: all 0.2s ease;
width: 100%;
box-sizing: border-box;
height: 100%;
display: flex;
flex-direction: column;
min-height: 120px;
}
.user-card:hover {
border-color: #007bff;
box-shadow: 0 2px 8px rgba(0, 123, 255, 0.1);
}
.user-header {
display: flex;
align-items: center;
gap: 0.5rem;
margin-bottom: 0.75rem;
flex-wrap: wrap;
}
.user-role {
font-weight: 600;
font-size: 0.85rem;
color: #495057;
white-space: nowrap;
}
.user-details {
display: flex;
flex-direction: column;
gap: 0.25rem;
overflow-wrap: break-word;
word-wrap: break-word;
flex: 1;
}
.user-name {
font-weight: 600;
color: #333;
margin-bottom: 0.25rem;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
max-width: 100%;
padding-right: 5px;
}
.user-email {
color: #666;
font-size: 0.9rem;
margin-bottom: 0.75rem;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.arrow-divider {
display: flex;
justify-content: center;
align-items: center;
color: #007bff;
font-size: 1.5rem;
position: relative;
}
.arrow-icon {
display: block;
}
.vs-text {
display: none;
font-weight: 600;
font-size: 0.9rem;
color: #495057;
text-transform: uppercase;
}
.resolve-date {
display: flex;
align-items: center;
gap: 0.5rem;
color: #28a745;
font-weight: 500;
}
/* Адаптивные стили для разных экранов */
/* Очень маленькие экраны (до 360px) */
@media (max-width: 360px) {
.admin-report-detail {
padding: 1rem 0.5rem calc(60px + env(safe-area-inset-bottom, 0px)) 0.5rem;
}
h2 {
font-size: 1.3rem;
}
.page-header {
flex-direction: column;
align-items: flex-start;
gap: 0.75rem;
}
.report-header {
padding: 0.75rem;
flex-direction: column;
align-items: flex-start;
gap: 0.75rem;
min-height: auto;
}
.report-info {
width: 100%;
gap: 0.5rem;
}
.report-id-section {
gap: 0.5rem;
}
.report-icon {
font-size: 1.25rem;
}
.report-title {
font-size: 1.1rem;
margin-bottom: 0.25rem;
}
.full-id-container {
flex-direction: column;
gap: 0.125rem;
align-items: flex-start;
}
.id-label {
font-size: 0.75rem;
}
.full-id {
font-size: 0.75rem;
padding: 0.0625rem 0.125rem;
width: 100%;
word-break: break-all;
}
.report-date {
width: 100%;
gap: 0.375rem;
text-align: left;
align-self: flex-start;
}
.date-icon {
font-size: 1rem;
}
.date-info {
text-align: left;
gap: 0.0625rem;
}
.date-label {
font-size: 0.7rem;
}
.date-value {
font-size: 0.8rem;
white-space: normal;
}
}
/* Маленькие экраны (361px - 480px) */
@media (min-width: 361px) and (max-width: 480px) {
.admin-report-detail {
padding: 1rem 0.75rem calc(60px + env(safe-area-inset-bottom, 0px)) 0.75rem;
}
.report-header {
padding: 1rem;
flex-direction: column;
align-items: flex-start;
gap: 1rem;
min-height: auto;
}
.report-info {
width: 100%;
}
.report-date {
width: 100%;
text-align: left;
align-self: flex-start;
}
.date-info {
text-align: left;
}
.full-id {
font-size: 0.8rem;
}
.date-value {
white-space: normal;
}
}
/* Средние экраны (481px - 767px) */
@media (min-width: 481px) and (max-width: 767px) {
.admin-report-detail {
padding: 1.25rem calc(60px + env(safe-area-inset-bottom, 0px)) 1.25rem;
}
.report-header {
padding: 1.25rem;
flex-direction: row;
align-items: flex-start;
flex-wrap: wrap;
gap: 1rem;
}
.report-info {
flex: 1;
min-width: 250px;
}
.report-date {
flex-shrink: 0;
min-width: 150px;
}
.date-value {
white-space: nowrap;
}
}
/* Большие экраны (768px - 991px) */
@media (min-width: 768px) and (max-width: 991px) {
.admin-report-detail {
padding: 1.5rem 1.25rem calc(60px + env(safe-area-inset-bottom, 0px)) 1.25rem;
}
.report-header {
padding: 1.5rem;
flex-direction: row;
align-items: flex-start;
gap: 1.25rem;
}
.report-info {
flex: 1;
max-width: calc(100% - 200px);
}
.report-date {
flex-shrink: 0;
min-width: 180px;
}
}
/* Очень большие экраны (992px - 1199px) */
@media (min-width: 992px) and (max-width: 1199px) {
.admin-report-detail {
padding: 2rem 1.5rem calc(60px + env(safe-area-inset-bottom, 0px)) 1.5rem;
}
.report-header {
padding: 1.75rem;
gap: 1.5rem;
}
.report-info {
max-width: calc(100% - 220px);
}
.report-date {
min-width: 200px;
}
}
/* Десктопные экраны (1200px+) */
@media (min-width: 1200px) {
.admin-report-detail {
padding: 2rem calc(60px + env(safe-area-inset-bottom, 0px)) 2rem;
max-width: 1200px;
margin: 0 auto;
}
.report-header {
padding: 2rem;
gap: 2rem;
}
.report-info {
max-width: calc(100% - 250px);
}
.report-date {
min-width: 230px;
}
.report-title {
font-size: 1.4rem;
}
.full-id {
font-size: 0.9rem;
}
.date-value {
font-size: 1rem;
}
}
/* Ландшафтная ориентация на мобильных */
@media (max-height: 450px) and (orientation: landscape) {
.admin-report-detail {
padding: 1rem calc(50px + env(safe-area-inset-bottom, 0px)) 1rem;
}
.header-controls {
margin-bottom: 1rem;
}
.page-header {
margin-bottom: 1rem;
}
.report-content,
.info-section,
.report-actions {
padding: 1rem;
}
.info-grid {
gap: 1rem;
}
.modal {
max-height: 85vh;
}
}
/* Высокие экраны */
@media (min-height: 800px) {
.admin-report-detail {
min-height: calc(100vh - 120px);
}
}
/* Устройства с вырезом (notch) */
@supports (padding: env(safe-area-inset-bottom)) {
.admin-report-detail {
padding-bottom: calc(60px + env(safe-area-inset-bottom, 0px));
}
}
/* Ретина экраны с высокой плотностью пикселей */
@media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi) {
.loading-spinner {
border-width: 2px;
}
.user-card {
border-width: 0.5px;
}
}
/* Темная тема (если поддерживается) */
@media (prefers-color-scheme: dark) {
/* Оставляем светлую тему для админ панели */
}
/* Уменьшенная анимация для пользователей с проблемами вестибулярного аппарата */
@media (prefers-reduced-motion: reduce) {
.loading-spinner {
animation: none;
}
.btn,
.back-btn,
.view-user-btn,
.user-card {
transition: none;
}
.btn:hover,
.back-btn:hover,
.view-user-btn:hover {
transform: none;
}
}
/* Адаптивные стили для блока header жалобы на разных экранах */
/* Очень маленькие экраны (до 360px) - адаптация header */
@media (max-width: 360px) {
.report-header {
padding: 0.75rem;
flex-direction: column;
align-items: flex-start;
gap: 0.75rem;
min-height: auto;
}
.report-info {
width: 100%;
gap: 0.5rem;
}
.report-id-section {
gap: 0.5rem;
}
.report-icon {
font-size: 1.25rem;
}
.report-title {
font-size: 1.1rem;
margin-bottom: 0.25rem;
}
.full-id-container {
flex-direction: column;
gap: 0.125rem;
align-items: flex-start;
}
.id-label {
font-size: 0.75rem;
}
.full-id {
font-size: 0.75rem;
padding: 0.0625rem 0.125rem;
width: 100%;
word-break: break-all;
}
.report-date {
width: 100%;
gap: 0.375rem;
text-align: left;
align-self: flex-start;
}
.date-icon {
font-size: 1rem;
}
.date-info {
text-align: left;
gap: 0.0625rem;
}
.date-label {
font-size: 0.7rem;
}
.date-value {
font-size: 0.8rem;
white-space: normal;
}
}
/* Маленькие экраны (361px - 480px) - адаптация header */
@media (min-width: 361px) and (max-width: 480px) {
.report-header {
padding: 1rem;
flex-direction: column;
align-items: flex-start;
gap: 1rem;
min-height: auto;
}
.report-info {
width: 100%;
}
.report-date {
width: 100%;
text-align: left;
align-self: flex-start;
}
.date-info {
text-align: left;
}
.full-id {
font-size: 0.8rem;
}
.date-value {
white-space: normal;
}
}
/* Средние экраны (481px - 767px) - адаптация header */
@media (min-width: 481px) and (max-width: 767px) {
.report-header {
padding: 1.25rem;
flex-direction: row;
align-items: flex-start;
flex-wrap: wrap;
gap: 1rem;
}
.report-info {
flex: 1;
min-width: 250px;
}
.report-date {
flex-shrink: 0;
min-width: 150px;
}
.date-value {
white-space: nowrap;
}
}
/* Большие экраны (768px - 991px) - адаптация header */
@media (min-width: 768px) and (max-width: 991px) {
.report-header {
padding: 1.5rem;
flex-direction: row;
align-items: flex-start;
gap: 1.25rem;
}
.report-info {
flex: 1;
max-width: calc(100% - 200px);
}
.report-date {
flex-shrink: 0;
min-width: 180px;
}
}
/* Очень большие экраны (992px - 1199px) - адаптация header */
@media (min-width: 992px) and (max-width: 1199px) {
.report-header {
padding: 1.75rem;
gap: 1.5rem;
}
.report-info {
max-width: calc(100% - 220px);
}
.report-date {
min-width: 200px;
}
}
/* Десктопные экраны (1200px+) - адаптация header */
@media (min-width: 1200px) {
.report-header {
padding: 2rem;
gap: 2rem;
}
.report-info {
max-width: calc(100% - 250px);
}
.report-date {
min-width: 230px;
}
.report-title {
font-size: 1.4rem;
}
.full-id {
font-size: 0.9rem;
}
.date-value {
font-size: 1rem;
}
}
/* Ландшафтная ориентация на мобильных - адаптация header */
@media (max-height: 450px) and (orientation: landscape) {
.report-header {
padding: 0.75rem 1rem;
flex-direction: row;
align-items: center;
gap: 1rem;
min-height: auto;
}
.report-info {
flex: 1;
}
.report-title {
font-size: 1.1rem;
margin-bottom: 0.25rem;
}
.full-id-container {
flex-direction: row;
flex-wrap: wrap;
gap: 0.25rem;
}
.full-id {
font-size: 0.75rem;
max-width: 200px;
}
.report-date {
flex-shrink: 0;
gap: 0.375rem;
}
.date-info {
gap: 0.0625rem;
}
.date-label {
font-size: 0.7rem;
}
.date-value {
font-size: 0.8rem;
}
}
</style>