v1.1
This commit is contained in:
+481
@@ -0,0 +1,481 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ru">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes">
|
||||
<title>Комитеты бизнес-объединений ДНР</title>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:opsz,wght@14..32,300;400;500;600;700&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css">
|
||||
<style>
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
body { font-family: 'Inter', sans-serif; background: #f8fafc; color: #0f172a; line-height: 1.5; padding: 2rem 1rem; }
|
||||
.container { max-width: 1400px; margin: 0 auto; }
|
||||
|
||||
.header { text-align: center; margin-bottom: 2rem; }
|
||||
.header h1 { font-size: 2rem; font-weight: 700; background: linear-gradient(135deg, #1e3a5f, #0f2b40); -webkit-background-clip: text; background-clip: text; color: transparent; }
|
||||
.header p { color: #475569; margin-top: 0.5rem; }
|
||||
|
||||
.back-link {
|
||||
display: inline-block;
|
||||
margin-bottom: 1.5rem;
|
||||
color: #1e3a5f;
|
||||
text-decoration: none;
|
||||
font-weight: 500;
|
||||
}
|
||||
.back-link:hover { text-decoration: underline; }
|
||||
|
||||
/* Фильтры */
|
||||
.filters {
|
||||
background: white;
|
||||
border-radius: 20px;
|
||||
padding: 1.5rem;
|
||||
margin-bottom: 2rem;
|
||||
box-shadow: 0 1px 3px rgba(0,0,0,0.05);
|
||||
border: 1px solid #eef2f6;
|
||||
}
|
||||
.filter-group {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
}
|
||||
.filter-group label {
|
||||
font-weight: 600;
|
||||
color: #475569;
|
||||
}
|
||||
.org-filter {
|
||||
flex: 1;
|
||||
min-width: 250px;
|
||||
padding: 10px 14px;
|
||||
border-radius: 12px;
|
||||
border: 1px solid #cbd5e1;
|
||||
font-family: 'Inter', sans-serif;
|
||||
font-size: 0.9rem;
|
||||
background: white;
|
||||
}
|
||||
.status-filter {
|
||||
padding: 10px 14px;
|
||||
border-radius: 12px;
|
||||
border: 1px solid #cbd5e1;
|
||||
font-family: 'Inter', sans-serif;
|
||||
font-size: 0.9rem;
|
||||
background: white;
|
||||
}
|
||||
.search-input {
|
||||
flex: 2;
|
||||
min-width: 250px;
|
||||
padding: 10px 14px;
|
||||
border-radius: 12px;
|
||||
border: 1px solid #cbd5e1;
|
||||
font-family: 'Inter', sans-serif;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
.stats {
|
||||
margin-top: 1rem;
|
||||
padding-top: 1rem;
|
||||
border-top: 1px solid #e2e8f0;
|
||||
font-size: 0.85rem;
|
||||
color: #64748b;
|
||||
}
|
||||
|
||||
/* Сетка комитетов */
|
||||
.committees-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(380px, 1fr));
|
||||
gap: 1.5rem;
|
||||
}
|
||||
.committee-card {
|
||||
background: white;
|
||||
border-radius: 20px;
|
||||
border: 1px solid #eef2f6;
|
||||
overflow: hidden;
|
||||
transition: all 0.2s;
|
||||
box-shadow: 0 1px 3px rgba(0,0,0,0.03);
|
||||
}
|
||||
.committee-card:hover {
|
||||
border-color: #cbd5e1;
|
||||
box-shadow: 0 8px 20px rgba(0,0,0,0.05);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
.card-header {
|
||||
padding: 1.2rem;
|
||||
background: linear-gradient(135deg, #1e3a5f, #0f2b40);
|
||||
color: white;
|
||||
}
|
||||
.org-badge {
|
||||
display: inline-block;
|
||||
background: rgba(255,255,255,0.2);
|
||||
padding: 4px 10px;
|
||||
border-radius: 20px;
|
||||
font-size: 0.7rem;
|
||||
font-weight: 500;
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
.committee-name {
|
||||
font-size: 1.1rem;
|
||||
font-weight: 700;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
.committee-status {
|
||||
display: inline-block;
|
||||
font-size: 0.7rem;
|
||||
padding: 3px 10px;
|
||||
border-radius: 20px;
|
||||
background: rgba(255,255,255,0.15);
|
||||
}
|
||||
.card-body {
|
||||
padding: 1.2rem;
|
||||
}
|
||||
.leader-section {
|
||||
margin-bottom: 1rem;
|
||||
padding-bottom: 1rem;
|
||||
border-bottom: 1px solid #eef2f6;
|
||||
}
|
||||
.leader-title {
|
||||
font-size: 0.7rem;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
color: #6c86a3;
|
||||
font-weight: 600;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
.leader-name {
|
||||
font-weight: 700;
|
||||
color: #0f172a;
|
||||
margin-bottom: 0.25rem;
|
||||
}
|
||||
.leader-contacts {
|
||||
font-size: 0.75rem;
|
||||
color: #475569;
|
||||
}
|
||||
.leader-contacts a {
|
||||
color: #1e3a5f;
|
||||
text-decoration: none;
|
||||
}
|
||||
.leader-contacts a:hover { text-decoration: underline; }
|
||||
.leader-tags {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 6px;
|
||||
margin-top: 8px;
|
||||
}
|
||||
.tag {
|
||||
background: #e2e8f0;
|
||||
padding: 2px 8px;
|
||||
border-radius: 16px;
|
||||
font-size: 0.7rem;
|
||||
color: #334155;
|
||||
}
|
||||
.committee-description {
|
||||
font-size: 0.85rem;
|
||||
color: #475569;
|
||||
line-height: 1.5;
|
||||
margin-top: 0.75rem;
|
||||
padding-top: 0.75rem;
|
||||
border-top: 1px solid #eef2f6;
|
||||
}
|
||||
.expand-btn {
|
||||
background: none;
|
||||
border: none;
|
||||
color: #1e3a5f;
|
||||
cursor: pointer;
|
||||
font-size: 0.8rem;
|
||||
margin-top: 0.5rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
.full-description {
|
||||
display: none;
|
||||
margin-top: 0.75rem;
|
||||
padding-top: 0.75rem;
|
||||
border-top: 1px solid #e2e8f0;
|
||||
font-size: 0.85rem;
|
||||
color: #334155;
|
||||
line-height: 1.5;
|
||||
}
|
||||
.full-description.open { display: block; }
|
||||
|
||||
.no-results {
|
||||
text-align: center;
|
||||
padding: 3rem;
|
||||
color: #64748b;
|
||||
background: white;
|
||||
border-radius: 20px;
|
||||
}
|
||||
|
||||
.loading {
|
||||
text-align: center;
|
||||
padding: 3rem;
|
||||
color: #64748b;
|
||||
}
|
||||
|
||||
.footer-note {
|
||||
margin-top: 2rem;
|
||||
text-align: center;
|
||||
font-size: 0.75rem;
|
||||
color: #6c86a3;
|
||||
border-top: 1px solid #e2edf7;
|
||||
padding-top: 2rem;
|
||||
}
|
||||
|
||||
@media (max-width: 700px) {
|
||||
.committees-grid { grid-template-columns: 1fr; }
|
||||
.filter-group { flex-direction: column; align-items: stretch; }
|
||||
.org-filter, .status-filter, .search-input { width: 100%; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<a href="/" class="back-link"><i class="fas fa-arrow-left"></i> Назад к списку организаций</a>
|
||||
|
||||
<div class="header">
|
||||
<h1><i class="fas fa-users"></i> Комитеты бизнес-объединений</h1>
|
||||
<p>Детальная информация о комитетах, их руководителях и деятельности</p>
|
||||
</div>
|
||||
|
||||
<div class="filters">
|
||||
<div class="filter-group">
|
||||
<label><i class="fas fa-building"></i> Организация:</label>
|
||||
<select id="orgFilter" class="org-filter">
|
||||
<option value="all">Все организации</option>
|
||||
</select>
|
||||
|
||||
<label><i class="fas fa-flag-checkered"></i> Статус:</label>
|
||||
<select id="statusFilter" class="status-filter">
|
||||
<option value="all">Все статусы</option>
|
||||
<option value="active">Активные</option>
|
||||
<option value="medium">Средняя активность</option>
|
||||
<option value="inactive">Неактивные</option>
|
||||
<option value="candidate">С кандидатом</option>
|
||||
</select>
|
||||
|
||||
<label><i class="fas fa-search"></i> Поиск:</label>
|
||||
<input type="text" id="searchInput" class="search-input" placeholder="Название комитета, руководитель...">
|
||||
</div>
|
||||
<div class="stats" id="stats">
|
||||
Загрузка...
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="committeesContainer" class="committees-grid">
|
||||
<div class="loading"><i class="fas fa-spinner fa-spin"></i> Загрузка комитетов...</div>
|
||||
</div>
|
||||
|
||||
<div class="footer-note">
|
||||
<i class="fas fa-info-circle"></i> Данные о комитетах загружаются из папки committees/.
|
||||
Статус активности определяется на основе информации о руководителе.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
let allCommittees = [];
|
||||
let organizationsList = [];
|
||||
|
||||
async function loadOrganizations() {
|
||||
try {
|
||||
const response = await fetch('/data.json');
|
||||
if (response.ok) {
|
||||
const data = await response.json();
|
||||
organizationsList = data.organizations || [];
|
||||
// Добавляем также дополнительные организации, если есть комитеты
|
||||
const additional = data.additionalOrganizations || [];
|
||||
organizationsList = [...organizationsList, ...additional];
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Ошибка загрузки организаций:', error);
|
||||
}
|
||||
}
|
||||
|
||||
async function loadCommittees() {
|
||||
const committeeFiles = [
|
||||
{ file: '/committees/opora_committees.json', orgName: 'ОПОРА РОССИИ' },
|
||||
// Добавьте другие файлы комитетов по мере появления:
|
||||
// { file: '/committees/deloros_committees.json', orgName: 'Деловая Россия' },
|
||||
// { file: '/committees/spp_committees.json', orgName: 'Союз промышленников и предпринимателей ДНР' },
|
||||
];
|
||||
|
||||
allCommittees = [];
|
||||
|
||||
for (const source of committeeFiles) {
|
||||
try {
|
||||
const response = await fetch(source.file);
|
||||
if (response.ok) {
|
||||
const committees = await response.json();
|
||||
committees.forEach(committee => {
|
||||
allCommittees.push({
|
||||
...committee,
|
||||
organization: source.orgName,
|
||||
organizationId: source.orgName.toLowerCase().replace(/[^a-z0-9]/g, '_')
|
||||
});
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(`Файл ${source.file} не найден`);
|
||||
}
|
||||
}
|
||||
|
||||
renderFilters();
|
||||
renderCommittees();
|
||||
}
|
||||
|
||||
function renderFilters() {
|
||||
const orgSelect = document.getElementById('orgFilter');
|
||||
const uniqueOrgs = [...new Set(allCommittees.map(c => c.organization))];
|
||||
|
||||
orgSelect.innerHTML = '<option value="all">Все организации</option>';
|
||||
uniqueOrgs.forEach(org => {
|
||||
const option = document.createElement('option');
|
||||
option.value = org;
|
||||
option.textContent = org;
|
||||
orgSelect.appendChild(option);
|
||||
});
|
||||
}
|
||||
|
||||
function getLeaderStatus(leader) {
|
||||
if (!leader || !leader.status) return 'unknown';
|
||||
return leader.status;
|
||||
}
|
||||
|
||||
function filterCommittees() {
|
||||
const orgFilter = document.getElementById('orgFilter').value;
|
||||
const statusFilter = document.getElementById('statusFilter').value;
|
||||
const searchTerm = document.getElementById('searchInput').value.toLowerCase();
|
||||
|
||||
return allCommittees.filter(committee => {
|
||||
// Фильтр по организации
|
||||
if (orgFilter !== 'all' && committee.organization !== orgFilter) return false;
|
||||
|
||||
// Фильтр по статусу
|
||||
if (statusFilter !== 'all') {
|
||||
const leaderStatus = getLeaderStatus(committee.current_leader);
|
||||
if (statusFilter === 'candidate') {
|
||||
if (!committee.candidate_for_leader?.name) return false;
|
||||
} else if (leaderStatus !== statusFilter) return false;
|
||||
}
|
||||
|
||||
// Поиск
|
||||
if (searchTerm) {
|
||||
const searchFields = [
|
||||
committee.name,
|
||||
committee.current_leader?.name,
|
||||
committee.current_leader?.title,
|
||||
committee.candidate_for_leader?.name,
|
||||
committee.description
|
||||
].filter(Boolean).join(' ').toLowerCase();
|
||||
if (!searchFields.includes(searchTerm)) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
function renderCommittees() {
|
||||
const container = document.getElementById('committeesContainer');
|
||||
const filtered = filterCommittees();
|
||||
const stats = document.getElementById('stats');
|
||||
|
||||
stats.innerHTML = `Найдено: ${filtered.length} комитетов из ${allCommittees.length}`;
|
||||
|
||||
if (filtered.length === 0) {
|
||||
container.innerHTML = '<div class="no-results"><i class="fas fa-inbox"></i><br>Комитеты не найдены. Попробуйте изменить параметры фильтрации.</div>';
|
||||
return;
|
||||
}
|
||||
|
||||
container.innerHTML = '';
|
||||
filtered.forEach((committee, idx) => {
|
||||
const leader = committee.current_leader;
|
||||
const candidate = committee.candidate_for_leader;
|
||||
const statusClass = leader?.status === 'active' ? 'Активен' : (leader?.status === 'medium' ? 'Средняя активность' : 'Статус не определён');
|
||||
|
||||
const card = document.createElement('div');
|
||||
card.className = 'committee-card';
|
||||
card.innerHTML = `
|
||||
<div class="card-header">
|
||||
<div class="org-badge"><i class="fas fa-building"></i> ${escapeHtml(committee.organization)}</div>
|
||||
<div class="committee-name">${escapeHtml(committee.name)}</div>
|
||||
<div class="committee-status">${statusClass}</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="leader-section">
|
||||
<div class="leader-title"><i class="fas fa-user-tie"></i> Текущий руководитель</div>
|
||||
<div class="leader-name">${leader?.name ? escapeHtml(leader.name) : '—'}</div>
|
||||
<div class="leader-contacts">
|
||||
${leader?.contacts?.email ? `<i class="fas fa-envelope"></i> <a href="mailto:${escapeHtml(leader.contacts.email)}">${escapeHtml(leader.contacts.email)}</a><br>` : ''}
|
||||
${leader?.contacts?.phone ? `<i class="fas fa-phone"></i> <a href="tel:${escapeHtml(leader.contacts.phone)}">${escapeHtml(leader.contacts.phone)}</a><br>` : ''}
|
||||
${leader?.contacts?.telegram ? `<i class="fab fa-telegram"></i> <a href="https://t.me/${escapeHtml(leader.contacts.telegram.replace('@', ''))}" target="_blank">${escapeHtml(leader.contacts.telegram)}</a><br>` : ''}
|
||||
</div>
|
||||
<div class="leader-tags">
|
||||
${leader?.title ? `<span class="tag">${escapeHtml(leader.title)}</span>` : ''}
|
||||
${leader?.additional_info ? `<span class="tag">${escapeHtml(leader.additional_info.substring(0, 50))}${leader.additional_info.length > 50 ? '...' : ''}</span>` : ''}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
${candidate?.name ? `
|
||||
<div class="leader-section">
|
||||
<div class="leader-title"><i class="fas fa-user-plus"></i> Кандидат на должность</div>
|
||||
<div class="leader-name">${escapeHtml(candidate.name)}</div>
|
||||
<div class="leader-contacts">
|
||||
${candidate.contacts?.email ? `<i class="fas fa-envelope"></i> <a href="mailto:${escapeHtml(candidate.contacts.email)}">${escapeHtml(candidate.contacts.email)}</a><br>` : ''}
|
||||
${candidate.contacts?.phone ? `<i class="fas fa-phone"></i> <a href="tel:${escapeHtml(candidate.contacts.phone)}">${escapeHtml(candidate.contacts.phone)}</a><br>` : ''}
|
||||
${candidate.contacts?.telegram ? `<i class="fab fa-telegram"></i> <a href="https://t.me/${escapeHtml(candidate.contacts.telegram.replace('@', ''))}" target="_blank">${escapeHtml(candidate.contacts.telegram)}</a><br>` : ''}
|
||||
</div>
|
||||
<div class="leader-tags">
|
||||
${candidate.title ? `<span class="tag">${escapeHtml(candidate.title)}</span>` : ''}
|
||||
${candidate.additional_info ? `<span class="tag">${escapeHtml(candidate.additional_info.substring(0, 50))}${candidate.additional_info.length > 50 ? '...' : ''}</span>` : ''}
|
||||
</div>
|
||||
</div>
|
||||
` : ''}
|
||||
|
||||
<div class="committee-description">
|
||||
<strong>О комитете:</strong><br>
|
||||
${escapeHtml(committee.description.substring(0, 200))}${committee.description.length > 200 ? '...' : ''}
|
||||
</div>
|
||||
|
||||
${committee.description.length > 200 ? `
|
||||
<button class="expand-btn" onclick="toggleDescription(this)">
|
||||
<i class="fas fa-chevron-down"></i> Читать полностью
|
||||
</button>
|
||||
<div class="full-description">
|
||||
${escapeHtml(committee.description)}
|
||||
</div>
|
||||
` : ''}
|
||||
</div>
|
||||
`;
|
||||
container.appendChild(card);
|
||||
});
|
||||
}
|
||||
|
||||
function toggleDescription(btn) {
|
||||
const fullDesc = btn.nextElementSibling;
|
||||
const icon = btn.querySelector('i');
|
||||
if (fullDesc.classList.contains('open')) {
|
||||
fullDesc.classList.remove('open');
|
||||
icon.className = 'fas fa-chevron-down';
|
||||
btn.innerHTML = '<i class="fas fa-chevron-down"></i> Читать полностью';
|
||||
} else {
|
||||
fullDesc.classList.add('open');
|
||||
icon.className = 'fas fa-chevron-up';
|
||||
btn.innerHTML = '<i class="fas fa-chevron-up"></i> Свернуть';
|
||||
}
|
||||
}
|
||||
|
||||
function escapeHtml(str) {
|
||||
if (!str) return '';
|
||||
return str.replace(/[&<>]/g, m => ({ '&': '&', '<': '<', '>': '>' }[m]));
|
||||
}
|
||||
|
||||
// Добавляем обработчики фильтров
|
||||
document.getElementById('orgFilter').addEventListener('change', () => renderCommittees());
|
||||
document.getElementById('statusFilter').addEventListener('change', () => renderCommittees());
|
||||
document.getElementById('searchInput').addEventListener('input', () => renderCommittees());
|
||||
|
||||
// Загрузка данных
|
||||
async function init() {
|
||||
await loadOrganizations();
|
||||
await loadCommittees();
|
||||
}
|
||||
|
||||
init();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user