375 lines
14 KiB
JavaScript
375 lines
14 KiB
JavaScript
let currentTab = 'biot';
|
||
let currentSubtab = null;
|
||
let events = { biot: [], ctpsr_main: [], gloves: [] };
|
||
let currentFilter = 'все';
|
||
|
||
async function loadEvents() {
|
||
try {
|
||
const [biotRes, ctpsrRes, glovesRes] = await Promise.all([
|
||
fetch('/api/events?tab=biot'),
|
||
fetch('/api/events?tab=ctpsr_main'),
|
||
fetch('/api/events?tab=gloves')
|
||
]);
|
||
events.biot = await biotRes.json();
|
||
events.ctpsr_main = await ctpsrRes.json();
|
||
events.gloves = await glovesRes.json();
|
||
console.log('Loaded events:', { biot: events.biot.length, ctpsr_main: events.ctpsr_main.length, gloves: events.gloves.length });
|
||
} catch (e) {
|
||
console.error('Ошибка загрузки:', e);
|
||
}
|
||
}
|
||
|
||
function switchTab(tab) {
|
||
currentTab = tab;
|
||
document.querySelectorAll('.tab').forEach(function(t) { t.classList.remove('active'); });
|
||
var activeBtn = document.querySelector('.tab[onclick="switchTab(\'' + tab + '\')"]');
|
||
if (activeBtn) activeBtn.classList.add('active');
|
||
|
||
var subtabsContainer = document.getElementById('subtabs');
|
||
if (tab === 'ctpsr') {
|
||
subtabsContainer.style.display = 'flex';
|
||
currentSubtab = 'ctpsr_main';
|
||
document.getElementById('page-title').textContent = 'Информация по ЦТПСР';
|
||
} else {
|
||
subtabsContainer.style.display = 'none';
|
||
currentSubtab = null;
|
||
document.getElementById('page-title').textContent = tab === 'biot' ? 'Информация по БиОТ' : 'Мероприятия';
|
||
}
|
||
console.log('switchTab:', tab, 'currentSubtab:', currentSubtab);
|
||
render();
|
||
}
|
||
|
||
function switchSubtab(subtab) {
|
||
currentSubtab = subtab;
|
||
document.querySelectorAll('.subtab').forEach(function(t) { t.classList.remove('active'); });
|
||
var activeBtn = document.querySelector('.subtab[onclick="switchSubtab(\'' + subtab + '\')"]');
|
||
if (activeBtn) activeBtn.classList.add('active');
|
||
console.log('switchSubtab:', subtab);
|
||
render();
|
||
}
|
||
|
||
function escapeHtml(text) {
|
||
if (!text) return '';
|
||
var div = document.createElement('div');
|
||
div.textContent = text;
|
||
return div.innerHTML;
|
||
}
|
||
|
||
function formatDate(dateStr) {
|
||
if (!dateStr) return '';
|
||
var d = new Date(dateStr);
|
||
return d.toLocaleDateString('ru-RU', { day: 'numeric', month: 'long', year: 'numeric' });
|
||
}
|
||
|
||
function setFilter(status) {
|
||
currentFilter = status;
|
||
render();
|
||
}
|
||
|
||
function render() {
|
||
var search = document.getElementById('search').value.toLowerCase();
|
||
var key = currentSubtab || currentTab;
|
||
var currentEvents = events[key] || [];
|
||
var isGloves = key === 'gloves';
|
||
|
||
console.log('render key:', key, 'events count:', currentEvents.length);
|
||
|
||
var filtered;
|
||
if (isGloves) {
|
||
filtered = currentEvents.filter(function(e) {
|
||
return (e.quantity && e.quantity.toLowerCase().includes(search)) ||
|
||
(e.location && e.location.toLowerCase().includes(search)) ||
|
||
(e.responsible && e.responsible.toLowerCase().includes(search));
|
||
});
|
||
} else {
|
||
filtered = currentEvents.filter(function(e) {
|
||
var matchSearch = e.title.toLowerCase().includes(search) ||
|
||
(e.description && e.description.toLowerCase().includes(search)) ||
|
||
(e.organizer && e.organizer.toLowerCase().includes(search));
|
||
var matchFilter = currentFilter === 'все' || e.status === currentFilter;
|
||
return matchSearch && matchFilter;
|
||
});
|
||
}
|
||
|
||
var statsEl = document.getElementById('stats');
|
||
var chipsEl = document.getElementById('chips');
|
||
|
||
if (!isGloves) {
|
||
var statuses = ['запланировано', 'в процессе', 'завершено', 'отменено'];
|
||
var counts = { все: currentEvents.length };
|
||
statuses.forEach(function(s) { counts[s] = currentEvents.filter(function(ev) { return ev.status === s; }).length; });
|
||
|
||
statsEl.innerHTML = `
|
||
<div class="stat-card">
|
||
<div class="number">${counts.все}</div>
|
||
<div class="label">Всего</div>
|
||
</div>
|
||
<div class="stat-card">
|
||
<div class="number">${counts['запланировано']}</div>
|
||
<div class="label">Запланировано</div>
|
||
</div>
|
||
<div class="stat-card">
|
||
<div class="number">${counts['в процессе']}</div>
|
||
<div class="label">В процессе</div>
|
||
</div>
|
||
<div class="stat-card">
|
||
<div class="number">${counts['завершено']}</div>
|
||
<div class="label">Завершено</div>
|
||
</div>
|
||
`;
|
||
|
||
chipsEl.innerHTML = ['все', ...statuses].map(function(s) {
|
||
return `<div class="chip ${currentFilter === s ? 'active' : ''}" onclick="setFilter('${s}')">
|
||
${s.charAt(0).toUpperCase() + s.slice(1)} (${counts[s] || 0})
|
||
</div>`;
|
||
}).join('');
|
||
} else {
|
||
statsEl.innerHTML = '';
|
||
chipsEl.innerHTML = '';
|
||
}
|
||
|
||
var container = document.getElementById('events');
|
||
if (filtered.length === 0) {
|
||
container.innerHTML = `
|
||
<div class="empty-state">
|
||
<h3>Нет записей</h3>
|
||
<p>Нажмите «Добавить», чтобы создать первую запись</p>
|
||
</div>
|
||
`;
|
||
return;
|
||
}
|
||
|
||
if (isGloves) {
|
||
container.innerHTML = `
|
||
<div style="overflow-x: auto;">
|
||
<table class="gloves-table">
|
||
<thead>
|
||
<tr>
|
||
<th>Количество</th>
|
||
<th>Место эксплуатации</th>
|
||
<th>Ответственное лицо</th>
|
||
<th>Последнее испытание</th>
|
||
<th>Следующее испытание</th>
|
||
<th>Срок (мес)</th>
|
||
<th>Действия</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
${filtered.map(function(e) {
|
||
return `<tr>
|
||
<td>${escapeHtml(e.quantity || '')}</td>
|
||
<td>${escapeHtml(e.location || '')}</td>
|
||
<td>${escapeHtml(e.responsible || '')}</td>
|
||
<td>${e.lastTestDate ? formatDate(e.lastTestDate) : ''}</td>
|
||
<td>${e.nextTestDate ? formatDate(e.nextTestDate) : ''}</td>
|
||
<td>${e.expiryMonths ? e.expiryMonths + ' мес.' : ''}</td>
|
||
<td>
|
||
<button class="btn btn-edit" onclick="editEvent(${e.id})">Изменить</button>
|
||
<button class="btn btn-delete" onclick="deleteEvent(${e.id})">Удалить</button>
|
||
</td>
|
||
</tr>`;
|
||
}).join('')}
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
`;
|
||
} else {
|
||
container.innerHTML = filtered.map(function(e) {
|
||
return `<div class="event-card">
|
||
<div class="event-header">
|
||
<div class="event-title">${escapeHtml(e.title)}</div>
|
||
<span class="event-status status-${e.status.replace(/ /g, '_')}">${e.status}</span>
|
||
</div>
|
||
<div class="event-meta">
|
||
${e.date ? `<span>📅 ${formatDate(e.date)}</span>` : ''}
|
||
${e.time ? `<span>⏰ ${e.time}</span>` : ''}
|
||
${e.location ? `<span>📍 ${escapeHtml(e.location)}</span>` : ''}
|
||
${e.organizer ? `<span>👤 ${escapeHtml(e.organizer)}</span>` : ''}
|
||
</div>
|
||
${e.description ? `<div class="event-description">${escapeHtml(e.description)}</div>` : ''}
|
||
<div class="event-actions">
|
||
<button class="btn btn-edit" onclick="editEvent(${e.id})">Изменить</button>
|
||
<button class="btn btn-delete" onclick="deleteEvent(${e.id})">Удалить</button>
|
||
</div>
|
||
</div>`;
|
||
}).join('');
|
||
}
|
||
}
|
||
|
||
function openModal(editId = null) {
|
||
var modal = document.getElementById('modal');
|
||
var form = document.getElementById('form');
|
||
var title = document.getElementById('modal-title');
|
||
|
||
console.log('=== OPEN MODAL ===');
|
||
console.log('currentTab:', currentTab, 'currentSubtab:', currentSubtab);
|
||
|
||
form.reset();
|
||
document.getElementById('eventId').value = '';
|
||
|
||
var key = currentSubtab || currentTab;
|
||
var isGloves = key === 'gloves';
|
||
|
||
console.log('key:', key, 'isGloves:', isGloves);
|
||
|
||
var glovesFields = document.getElementById('gloves-fields');
|
||
var eventFields = document.getElementById('event-fields');
|
||
|
||
glovesFields.style.display = isGloves ? 'block' : 'none';
|
||
eventFields.style.display = isGloves ? 'none' : 'block';
|
||
|
||
// Убираем required у скрытых полей
|
||
var glovesInputs = glovesFields.querySelectorAll('input, textarea');
|
||
var eventInputs = eventFields.querySelectorAll('input, textarea, select');
|
||
|
||
glovesInputs.forEach(function(input) {
|
||
input.required = isGloves && input.id !== 'notes';
|
||
});
|
||
|
||
eventInputs.forEach(function(input) {
|
||
input.required = !isGloves;
|
||
});
|
||
|
||
if (editId) {
|
||
var item = events[key].find(function(e) { return e.id === editId; });
|
||
if (item) {
|
||
title.textContent = isGloves ? 'Изменить запись' : 'Изменить мероприятие';
|
||
document.getElementById('eventId').value = item.id;
|
||
|
||
if (isGloves) {
|
||
document.getElementById('quantity').value = item.quantity || '';
|
||
document.getElementById('location').value = item.location || '';
|
||
document.getElementById('responsible').value = item.responsible || '';
|
||
document.getElementById('lastTestDate').value = item.lastTestDate || '';
|
||
document.getElementById('nextTestDate').value = item.nextTestDate || '';
|
||
document.getElementById('expiryMonths').value = item.expiryMonths || '';
|
||
document.getElementById('notes').value = item.notes || '';
|
||
} else {
|
||
document.getElementById('title').value = item.title;
|
||
document.getElementById('date').value = item.date;
|
||
document.getElementById('time').value = item.time || '';
|
||
document.getElementById('eventLocation').value = item.location || '';
|
||
document.getElementById('organizer').value = item.organizer || '';
|
||
document.getElementById('status').value = item.status;
|
||
document.getElementById('description').value = item.description || '';
|
||
}
|
||
}
|
||
} else {
|
||
title.textContent = isGloves ? 'Добавить перчатки' : 'Добавить мероприятие';
|
||
}
|
||
|
||
modal.classList.add('on');
|
||
}
|
||
|
||
function closeModal() {
|
||
document.getElementById('modal').classList.remove('on');
|
||
}
|
||
|
||
function editEvent(id) {
|
||
openModal(id);
|
||
}
|
||
|
||
function addNew() {
|
||
openModal(null);
|
||
}
|
||
|
||
async function deleteEvent(id) {
|
||
if (!confirm('Вы уверены, что хотите удалить эту запись?')) return;
|
||
var key = currentSubtab || currentTab;
|
||
await fetch('/api/events/' + id + '?tab=' + key, { method: 'DELETE' });
|
||
await loadEvents();
|
||
render();
|
||
}
|
||
|
||
document.getElementById('form').addEventListener('submit', async function(e) {
|
||
e.preventDefault();
|
||
|
||
var id = document.getElementById('eventId').value;
|
||
var key = currentSubtab || currentTab;
|
||
var isGloves = key === 'gloves';
|
||
var btn = e.target.querySelector('button[type="submit"]');
|
||
var originalText = btn.textContent;
|
||
|
||
console.log('=== FORM SUBMIT ===');
|
||
console.log('key:', key, 'isGloves:', isGloves, 'currentSubtab:', currentSubtab, 'currentTab:', currentTab);
|
||
|
||
// Временно убираем required у всех полей, чтобы браузер не блокировал
|
||
var allInputs = e.target.querySelectorAll('input, textarea, select');
|
||
var requiredBackup = [];
|
||
allInputs.forEach(function(input, i) {
|
||
requiredBackup[i] = input.required;
|
||
input.required = false;
|
||
});
|
||
|
||
btn.textContent = 'Сохранение...';
|
||
btn.disabled = true;
|
||
|
||
var data = isGloves ? {
|
||
quantity: document.getElementById('quantity').value,
|
||
location: document.getElementById('location').value,
|
||
responsible: document.getElementById('responsible').value,
|
||
lastTestDate: document.getElementById('lastTestDate').value,
|
||
nextTestDate: document.getElementById('nextTestDate').value,
|
||
expiryMonths: document.getElementById('expiryMonths').value,
|
||
notes: document.getElementById('notes').value
|
||
} : {
|
||
title: document.getElementById('title').value,
|
||
date: document.getElementById('date').value,
|
||
time: document.getElementById('time').value,
|
||
location: document.getElementById('eventLocation').value,
|
||
organizer: document.getElementById('organizer').value,
|
||
status: document.getElementById('status').value,
|
||
description: document.getElementById('description').value
|
||
};
|
||
|
||
console.log('Data to save:', data);
|
||
|
||
var url = id ? '/api/events/' + id + '?tab=' + key : '/api/events?tab=' + key;
|
||
console.log('POST to:', url);
|
||
|
||
try {
|
||
var response = await fetch(url, {
|
||
method: id ? 'PUT' : 'POST',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify(data)
|
||
});
|
||
|
||
var responseText = await response.text();
|
||
console.log('Response status:', response.status, 'body:', responseText);
|
||
|
||
if (!response.ok) {
|
||
throw new Error('Сервер вернул ' + response.status + ': ' + responseText);
|
||
}
|
||
|
||
var result = JSON.parse(responseText);
|
||
console.log('Server response:', result);
|
||
|
||
// Возвращаем required обратно
|
||
allInputs.forEach(function(input, i) {
|
||
input.required = requiredBackup[i];
|
||
});
|
||
|
||
closeModal();
|
||
await loadEvents();
|
||
render();
|
||
alert('✓ Запись сохранена!');
|
||
} catch (error) {
|
||
// Возвращаем required обратно
|
||
allInputs.forEach(function(input, i) {
|
||
input.required = requiredBackup[i];
|
||
});
|
||
|
||
console.error('Error saving:', error);
|
||
alert('Ошибка сохранения: ' + error.message);
|
||
} finally {
|
||
btn.textContent = originalText;
|
||
btn.disabled = false;
|
||
}
|
||
});
|
||
|
||
document.getElementById('modal').addEventListener('click', function(e) {
|
||
if (e.target === e.currentTarget) closeModal();
|
||
});
|
||
|
||
loadEvents();
|