let currentTab = 'biot'; let currentSubtab = null; let events = { biot: [], ctpsr_main: [], gloves: [] }; let currentFilter = 'все'; let currentUser = null; // API_URL пустой — используем относительные пути const API_URL = ''; // Проверка авторизации при загрузке async function checkAuth() { const token = localStorage.getItem('token'); const name = localStorage.getItem('userName'); const id = localStorage.getItem('userId'); if (token && name && id) { currentUser = { token, name, id }; showApp(); await loadEvents(); render(); } else { showLogin(); } } function showLogin() { document.getElementById('login-screen').classList.add('on'); document.getElementById('app-content').classList.remove('on'); } function showApp() { document.getElementById('login-screen').classList.remove('on'); document.getElementById('app-content').classList.add('on'); document.getElementById('user-name').textContent = currentUser.name; // Показываем кнопку админки только для admin if (currentUser.id === 'admin' || currentUser.name === 'Администратор') { document.getElementById('admin-btn').style.display = 'inline-block'; } } async function login(username, password) { try { const res = await fetch(API_URL + '/service/login?username=' + encodeURIComponent(username) + '&password=' + encodeURIComponent(password), { method: 'GET' }); const data = await res.json(); if (res.ok) { currentUser = { token: data.token, name: data.name, id: username }; localStorage.setItem('token', data.token); localStorage.setItem('userName', data.name); localStorage.setItem('userId', username); showApp(); await loadEvents(); render(); } else { alert(data.error || 'Ошибка входа'); } } catch (e) { alert('Ошибка соединения: ' + e.message); } } } catch (e) { alert('Ошибка соединения: ' + e.message); } } function logout() { localStorage.removeItem('token'); localStorage.removeItem('userName'); localStorage.removeItem('userId'); currentUser = null; showLogin(); } document.getElementById('login-form').addEventListener('submit', function(e) { e.preventDefault(); const username = document.getElementById('username').value; const password = document.getElementById('password').value; login(username, password); }); checkAuth(); // Админ панель async function openAdmin() { if (!currentUser || currentUser.id !== 'admin') { alert('Только для администратора'); return; } document.getElementById('admin-modal').classList.add('on'); await loadUsers(); } function closeAdmin() { document.getElementById('admin-modal').classList.remove('on'); } async function loadUsers() { try { const res = await fetch(API_URL + '/service/users', { headers: { 'Authorization': 'Bearer ' + currentUser.token } }); if (!res.ok) { throw new Error('Ошибка загрузки: ' + res.status); } const users = await res.json(); const list = document.getElementById('users-list'); list.innerHTML = users.map(u => `
${u.name}
ID: ${u.id}
${u.id !== 'admin' ? ` ` : 'Администратор'}
`).join(''); document.getElementById('admin-stats').innerHTML = 'Всего пользователей: ' + users.length; } catch (e) { console.error('Error loading users:', e); document.getElementById('users-list').innerHTML = '

Ошибка: ' + e.message + '

'; } } document.getElementById('add-user-form').addEventListener('submit', async function(e) { e.preventDefault(); const id = document.getElementById('new-user-id').value.trim(); const name = document.getElementById('new-user-name').value.trim(); const password = document.getElementById('new-user-password').value.trim(); try { const res = await fetch(API_URL + '/service/users', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + currentUser.token }, body: JSON.stringify({ id, name, password }) }); const data = await res.json(); if (res.ok) { alert('Пользователь добавлен!\n\nID: ' + data.user.id + '\nФИО: ' + data.user.name + '\nПароль: ' + data.user.password + '\n\nЗапишите данные!'); document.getElementById('add-user-form').reset(); await loadUsers(); } else { alert('Ошибка: ' + data.error); } } catch (e) { alert('Ошибка: ' + e.message); } }); async function deleteUser(id) { if (!confirm('Удалить пользователя ' + id + '?')) return; try { const res = await fetch(API_URL + '/service/users/' + id, { method: 'DELETE', headers: { 'Authorization': 'Bearer ' + currentUser.token } }); const data = await res.json(); if (res.ok) { await loadUsers(); alert('Пользователь удалён'); } else { alert('Ошибка: ' + data.error); } } catch (e) { alert('Ошибка: ' + e.message); } } async function loadEvents() { if (!currentUser) return; try { const headers = { 'Authorization': 'Bearer ' + currentUser.token }; const [biotRes, ctpsrRes, glovesRes] = await Promise.all([ fetch(API_URL + '/service/events?tab=biot', { headers }), fetch(API_URL + '/service/events?tab=ctpsr_main', { headers }), fetch(API_URL + '/service/events?tab=gloves', { headers }) ]); 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 = `
${counts.все}
Всего
${counts['запланировано']}
Запланировано
${counts['в процессе']}
В процессе
${counts['завершено']}
Завершено
`; chipsEl.innerHTML = ['все', ...statuses].map(function(s) { return `
${s.charAt(0).toUpperCase() + s.slice(1)} (${counts[s] || 0})
`; }).join(''); } else { statsEl.innerHTML = ''; chipsEl.innerHTML = ''; } var container = document.getElementById('events'); if (filtered.length === 0) { container.innerHTML = `

Нет записей

Нажмите «Добавить», чтобы создать первую запись

`; return; } if (isGloves) { container.innerHTML = `
${filtered.map(function(e) { return ``; }).join('')}
Количество Место эксплуатации Ответственное лицо Последнее испытание Следующее испытание Срок (мес) Действия
${escapeHtml(e.quantity || '')} ${escapeHtml(e.location || '')} ${escapeHtml(e.responsible || '')} ${e.lastTestDate ? formatDate(e.lastTestDate) : ''} ${e.nextTestDate ? formatDate(e.nextTestDate) : ''} ${e.expiryMonths ? e.expiryMonths + ' мес.' : ''}
`; } else { container.innerHTML = filtered.map(function(e) { return `
${escapeHtml(e.title)}
${e.status}
${e.date ? `📅 ${formatDate(e.date)}` : ''} ${e.time ? `⏰ ${e.time}` : ''} ${e.location ? `📍 ${escapeHtml(e.location)}` : ''} ${e.organizer ? `👤 ${escapeHtml(e.organizer)}` : ''}
${e.description ? `
${escapeHtml(e.description)}
` : ''}
`; }).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_URL + '/service/events/' + id + '?tab=' + key, { method: 'DELETE', headers: { 'Authorization': 'Bearer ' + currentUser.token } }); 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_URL + '/service/events/' + id + '?tab=' + key : API_URL + '/service/events?tab=' + key; console.log('POST to:', url); try { var response = await fetch(url, { method: id ? 'PUT' : 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + currentUser.token }, body: JSON.stringify(data) }); console.log('Response headers:', response.headers.get('content-type')); 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(); }); async function testApi() { if (!currentUser) { alert('Сначала войдите в систему'); return; } try { const res = await fetch(API_URL + '/service/events?tab=biot', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + currentUser.token }, body: JSON.stringify({ title: 'Тест API', date: '2026-09-10', status: 'запланировано' }) }); const text = await res.text(); console.log('API Response:', res.status, text); if (res.ok) { const data = JSON.parse(text); alert('API работает! Ответ: ' + JSON.stringify(data)); await loadEvents(); render(); } else { alert('API вернул ошибку ' + res.status + ': ' + text); } } catch (e) { console.error('Test failed:', e); alert('Ошибка теста API: ' + e.message); } } loadEvents();