481 lines
19 KiB
HTML
481 lines
19 KiB
HTML
<!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> |