kak-ya-mogu-skopirovat-chuzh/script.js
2026-09-10 10:43:35 +00:00

573 lines
20 KiB
JavaScript
Raw Permalink 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.

let currentTab = 'biot';
let currentSubtab = null;
let events = { biot: [], ctpsr_main: [], gloves: [] };
let currentFilter = 'все';
let currentUser = null;
// Проверка авторизации при загрузке
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 {
// Используем GET с query параметрами — proxy пропускает GET
const res = await fetch('/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();
} else {
alert(data.error || 'Ошибка входа');
}
} 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('/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 => `
<div style="background: #252830; padding: 12px; border-radius: 8px; margin-bottom: 10px; display: flex; justify-content: space-between; align-items: center;">
<div>
<strong style="color: #f4f4f5;">${u.name}</strong>
<div style="color: #8a8a92; font-size: 13px;">ID: ${u.id}</div>
</div>
${u.id !== 'admin' ? `
<button class="btn btn-secondary" onclick="deleteUser('${u.id}')" style="padding: 6px 12px; font-size: 13px;">Удалить</button>
` : '<span style="color: #8a8a92; font-size: 13px;">Администратор</span>'}
</div>
`).join('');
document.getElementById('admin-stats').innerHTML = 'Всего пользователей: ' + users.length;
} catch (e) {
console.error('Error loading users:', e);
document.getElementById('users-list').innerHTML = '<p style="color: #f87171;">Ошибка: ' + e.message + '</p>';
}
}
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('/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('/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('/service/events?tab=biot', { headers }),
fetch('/service/events?tab=ctpsr_main', { headers }),
fetch('/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 = `
<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('/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 ? '/service/events/' + id + '?tab=' + key : '/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('/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();