<div id="personnelRoot">
<div class="prs-top-bar">
<div class="prs-search">
<label>🔍 Поиск по персонажам</label>
<input type="text" id="prsSearchInput" placeholder="Введите имя или внешность...">
<button id="prsResetBtn">Сбросить</button>
</div>
<div class="prs-filters">
<label>📋 Фильтр:</label>
<select id="prsStatusFilter">
<option value="all">Все персонажи</option>
<option value="alive">Живые</option>
<option value="deceased">Мёртвые</option>
</select>
</div>
<div class="prs-admin-panel" style="display:none;">
<button id="prsAddBtn" class="prs-admin-btn">➕ Добавить персонажа</button>
</div>
</div>
<div id="prsEditForm" style="display:none;">
<h3 id="prsFormTitle">Добавление персонажа</h3>
<input type="hidden" id="prsKey">
<div class="prs-form-row">
<label>Номер:</label>
<input type="text" id="prsNumber" placeholder="1">
</div>
<div class="prs-form-row">
<label>Фото (URL):</label>
<input type="text" id="prsPhoto" placeholder="https://...">
</div>
<div class="prs-form-row">
<label>Имя Фамилия / Прозвище:</label>
<input type="text" id="prsName" placeholder="Иван Иванов">
</div>
<div class="prs-form-row">
<label>Ссылка на анкету:</label>
<input type="text" id="prsLink" placeholder="https://... или /viewtopic.php?id=123">
</div>
<div class="prs-form-row">
<label>Возраст:</label>
<input type="text" id="prsAge" placeholder="1990 г.р.">
</div>
<div class="prs-form-row">
<label>Дата смерти (оставить пустым если жив):</label>
<input type="text" id="prsDeathDate" placeholder="01.01.2024">
</div>
<div class="prs-form-row">
<label>Вид (раса/тип):</label>
<input type="text" id="prsType" placeholder="человек">
</div>
<div class="prs-form-row">
<label>Местоположение:</label>
<input type="text" id="prsLocation" placeholder="Нью-Йорк">
</div>
<div class="prs-form-row">
<label>Род деятельности:</label>
<input type="text" id="prsOccupation" placeholder="детектив">
</div>
<div class="prs-form-row">
<label>Внешность:</label>
<input type="text" id="prsAppearance" placeholder="актер/актриса">
</div>
<div class="prs-form-buttons">
<button id="prsSaveBtn" class="prs-admin-btn">💾 Сохранить</button>
<button id="prsCancelBtn" class="prs-admin-btn">❌ Отмена</button>
</div>
</div>
<div class="prs-table-wrapper">
<table class="prs-table">
<thead>
<tr>
<th class="col-num">№</th>
<th class="col-photo">Фото</th>
<th class="col-name">Имя Фамилия / Прозвище</th>
<th class="col-age">Возраст</th>
<th class="col-type">Вид</th>
<th class="col-info">Местоположение и род деятельности</th>
<th class="col-appearance">Внешность</th>
<th class="col-actions" style="display:none;">⚙️</th>
</tr>
</thead>
<tbody id="prsTableBody">
<tr><td colspan="8" style="text-align:center">Загрузка персонажей...</td></tr>
</tbody>
</table>
</div>
</div>
<style>
#personnelRoot {
margin: 0px 10px 60px 10px;
font-family: 'Raleway Medium', sans-serif;
font-size: 14px;
background: var(--forcol, #f5f0eb) !important;
padding: 20px;
border-radius: 8px;
}
#personnelRoot .prs-top-bar {
display: flex;
align-items: center;
justify-content: space-between;
flex-wrap: wrap;
gap: 10px;
margin-bottom: 15px;
}
#personnelRoot .prs-search {
display: flex;
align-items: center;
gap: 10px;
flex-wrap: wrap;
flex: 2;
}
#personnelRoot .prs-search label {
font: 14px 'Raleway Medium' !important;
margin: 0 !important;
color: #4a3c35 !important;
}
#personnelRoot .prs-search input {
padding: 6px 12px;
width: 250px;
background: var(--forcol, #f5f0eb) !important;
border: 3px solid #8b847a !important;
border-radius: 3px;
font-family: 'Raleway Medium';
font-size: 14px;
color: #4a3c35;
}
#personnelRoot .prs-search input::placeholder {
color: #4a3c35;
opacity: 0.7;
}
#personnelRoot .prs-search button,
.prs-admin-btn {
background: var(--forcol, #f5f0eb) !important;
border-radius: 7px !important;
border: 1px solid #46464638 !important;
color: #322a26 !important;
padding: 9px 10px 7px 10px !important;
cursor: pointer !important;
font-size: 11px !important;
width: auto !important;
font-weight: bolder !important;
font-family: 'Raleway Medium';
}
#personnelRoot .prs-filters {
display: flex;
align-items: center;
gap: 10px;
flex: 1;
justify-content: center;
}
#personnelRoot .prs-filters label {
font: 14px 'Raleway Medium' !important;
color: #4a3c35 !important;
}
#personnelRoot .prs-filters select {
padding: 6px 12px;
background: var(--forcol, #f5f0eb) !important;
border: 3px solid #8b847a !important;
border-radius: 3px;
font-family: 'Raleway Medium';
font-size: 14px;
color: #4a3c35;
}
.prs-admin-panel {
display: flex;
gap: 10px;
}
#prsEditForm {
background: #e8e2da;
padding: 15px;
border-radius: 8px;
margin-bottom: 15px;
border: 2px solid #8b847a;
}
#prsEditForm h3 {
margin-top: 0;
color: #4a3c35;
font-family: 'Raleway Medium';
}
.prs-form-row {
display: flex;
align-items: center;
gap: 10px;
margin-bottom: 10px;
flex-wrap: wrap;
}
.prs-form-row label {
width: 250px;
color: #4a3c35;
font-weight: bold;
}
.prs-form-row input,
.prs-form-row select {
flex: 1;
min-width: 200px;
padding: 6px 12px;
background: var(--forcol, #f5f0eb) !important;
border: 2px solid #8b847a !important;
border-radius: 3px;
font-family: 'Raleway Medium';
font-size: 14px;
color: #4a3c35;
}
.prs-form-buttons {
display: flex;
gap: 10px;
margin-top: 15px;
}
#personnelRoot .prs-table-wrapper {
max-height: 500px;
overflow-y: auto;
overflow-x: auto;
border: none;
margin-top: 5px;
margin-bottom: 20px;
position: relative;
}
#personnelRoot .prs-table {
width: 100%;
min-width: 800px;
border-collapse: collapse;
font-family: 'Raleway Medium', sans-serif;
font-size: 14px;
background-color: transparent;
border: 3px solid #8b847a;
table-layout: fixed;
}
#personnelRoot .prs-table th,
#personnelRoot .prs-table td {
border: none;
padding: 10px 8px;
vertical-align: middle;
word-wrap: break-word;
overflow-wrap: break-word;
}
#personnelRoot .prs-table th:not(:nth-child(3)):not(:nth-child(6)),
#personnelRoot .prs-table td:not(:nth-child(3)):not(:nth-child(6)) {
text-align: center;
}
#personnelRoot .prs-table th:nth-child(3),
#personnelRoot .prs-table td:nth-child(3) {
text-align: left;
}
#personnelRoot .prs-table th:nth-child(6),
#personnelRoot .prs-table td:nth-child(6) {
text-align: left;
}
#personnelRoot .prs-table tbody tr td {
border-top: 3px solid #8b847a;
border-bottom: 3px solid #8b847a;
}
#personnelRoot .prs-table tbody tr:first-child td {
border-top: none;
}
#personnelRoot .prs-table tbody tr:last-child td {
border-bottom: none;
}
#personnelRoot .prs-table thead tr {
position: sticky;
top: 0;
z-index: 20;
}
#personnelRoot .prs-table thead th {
background-color: #8c7f72 !important;
border-bottom: 3px solid #8b847a;
text-align: center !important;
font-weight: bold !important;
font-size: 14px !important;
color: #4a3c35 !important;
font-family: 'Raleway Medium', sans-serif;
vertical-align: middle;
}
/* Ширина колонок */
.col-num { width: 40px; }
.col-photo { width: 90px; }
.col-name { width: 120px; }
.col-age { width: 100px; }
.col-type { width: 120px; }
.col-info { width: 140px; }
.col-appearance { width: 90px; }
.col-actions { width: 60px; }
/* Первая буква жирная и подчёркнутая */
.first-letter {
font-weight: bold;
text-decoration: underline;
}
.character-photo {
width: 65px;
height: 85px;
object-fit: cover;
border-radius: 4px;
border: 2px solid #D6CFC4;
box-shadow: 0 4px 12px rgba(0,0,0,0.2);
background: #D6CFC4;
padding: 4px 4px 6px 4px;
transition: all 0.4s ease;
display: block;
margin: 0 auto;
filter: sepia(0.5) hue-rotate(-5deg) brightness(0.96) contrast(1.08) saturate(1.2);
clip-path: polygon(
0% 3%, 2% 0%, 98% 0%, 100% 2%, 100% 5%, 99% 8%, 100% 12%, 98% 15%,
99% 20%, 100% 25%, 98% 28%, 99% 32%, 100% 35%, 98% 40%, 99% 44%, 100% 48%,
98% 52%, 99% 56%, 100% 60%, 98% 65%, 99% 70%, 100% 75%, 98% 80%, 99% 85%,
100% 90%, 98% 94%, 99% 97%, 100% 100%, 97% 100%, 94% 98%, 90% 100%, 85% 99%,
80% 100%, 75% 98%, 70% 99%, 65% 100%, 60% 98%, 55% 99%, 50% 100%, 45% 98%,
40% 99%, 35% 100%, 30% 98%, 25% 99%, 20% 100%, 15% 98%, 10% 99%, 8% 100%,
5% 98%, 3% 100%, 0% 97%
);
}
.character-photo-left {
transform: rotate(-3deg);
}
.character-photo-right {
transform: rotate(3deg);
}
.character-photo-left:hover {
filter: sepia(0) hue-rotate(0) brightness(1) contrast(1) saturate(1);
transform: rotate(-5deg) scale(1.05);
}
.character-photo-right:hover {
filter: sepia(0) hue-rotate(0) brightness(1) contrast(1) saturate(1);
transform: rotate(5deg) scale(1.05);
}
.character-name-link {
color: #4a3c35;
text-decoration: none;
font-weight: bold;
transition: all 0.2s ease;
cursor: pointer;
}
.character-name-link:hover {
color: #8c7f72;
text-decoration: underline;
}
.death-date {
color: #4a3c35;
font-style: italic;
font-weight: bold;
}
.character-age {
white-space: normal;
word-break: keep-all;
}
.prs-action-btn {
background: none;
border: none;
cursor: pointer;
font-size: 16px;
padding: 2px 5px;
}
.prs-action-btn:hover {
opacity: 0.7;
}
.prs-table-wrapper::-webkit-scrollbar {
width: 8px;
height: 8px;
}
.prs-table-wrapper::-webkit-scrollbar-track {
background: #e0dcd5;
border-radius: 4px;
}
.prs-table-wrapper::-webkit-scrollbar-thumb {
background: #8b847a;
border-radius: 4px;
}
</style>
<script>
// ==================== КОНФИГУРАЦИЯ ====================
var PRS_CONFIG = {
apiUrl: location.origin + '/api.php',
appId: 16777215,
userId: 1,
adminGroups: [1],
allowedGroups: [1, 2, 3, 4, 5, 6, 7], // ВСЕ группы могут видеть
keyPrefix: 'chr_prs_',
debug: false
};
// ==================== ГЛОБАЛЬНЫЕ ДАННЫЕ ====================
var charactersData = [];
var isAdmin = false;
var apiToken = '';
// ==================== ТОКЕН ====================
function getToken() {
if (typeof window !== 'undefined' && window.ForumAPITicket) return window.ForumAPITicket;
if (typeof ForumAPITicket !== 'undefined') return ForumAPITicket;
if (typeof guestTicket !== 'undefined') return guestTicket;
if (typeof FORUM !== 'undefined' && FORUM.getSessionId) return FORUM.getSessionId();
var cookies = document.cookie.split(';');
for (var i = 0; i < cookies.length; i++) {
var c = cookies[i].trim();
if (c.indexOf('token=') === 0) return c.substring(6);
if (c.indexOf('session=') === 0) return c.substring(8);
if (c.indexOf('sid=') === 0) return c.substring(4);
}
if (typeof Storage !== 'undefined') {
var ls = localStorage.getItem('token') || localStorage.getItem('session');
if (ls) return ls;
}
return '';
}
function logDebug(msg) {
if (!PRS_CONFIG.debug) return;
console.log(msg);
}
// ==================== ПРОВЕРКА ПРАВ ====================
function checkPermissions() {
var groupId = 3;
if (typeof GroupID !== 'undefined') groupId = GroupID;
else if (typeof UserGroup !== 'undefined') groupId = UserGroup;
// ВСЕ пользователи (включая гостей) могут видеть картотеку
// Гости обычно GroupID = 3 или undefined, но мы разрешаем всем
var hasAccess = true; // Доступ открыт для всех
// Админы могут редактировать
isAdmin = PRS_CONFIG.adminGroups.indexOf(groupId) !== -1;
return true; // Всегда разрешаем доступ
}
// ==================== API ====================
function apiCall(method, params, callback, errorCallback) {
var data = {
method: method,
app_id: PRS_CONFIG.appId
};
apiToken = getToken();
if (apiToken) {
data.token = apiToken;
}
for (var key in params) {
if (params.hasOwnProperty(key)) {
data[key] = params[key];
}
}
var queryString = serializeParams(data);
var url = PRS_CONFIG.apiUrl + '?' + queryString;
logDebug('API Request: ' + method + ' | token=' + (apiToken ? 'YES' : 'NO'));
var xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
xhr.onreadystatechange = function() {
if (xhr.readyState === 4) {
logDebug('API Response ' + method + ': ' + xhr.status);
if (xhr.status === 200) {
try {
var response = JSON.parse(xhr.responseText);
logDebug('Response: ' + JSON.stringify(response).substring(0, 200));
callback(response);
} catch (e) {
logDebug('Parse error: ' + e.message);
if (errorCallback) errorCallback('Parse error: ' + e.message);
}
} else {
logDebug('HTTP error: ' + xhr.status);
if (errorCallback) errorCallback('HTTP ' + xhr.status);
}
}
};
xhr.onerror = function() {
logDebug('Network error');
if (errorCallback) errorCallback('Network error');
};
xhr.send();
}
function serializeParams(obj) {
var str = [];
for (var p in obj) {
if (obj.hasOwnProperty(p)) {
str.push(encodeURIComponent(p) + "=" + encodeURIComponent(obj[p]));
}
}
return str.join("&");
}
function isSuccess(response) {
return response.response === "1" ||
(response.response && response.response.storage && response.response.storage.success);
}
// ==================== ЗАГРУЗКА ДАННЫХ ====================
function loadData() {
logDebug('=== ЗАГРУЗКА ДАННЫХ ===');
apiCall('storage.keys', {}, function(response) {
if (response.response && response.response.storage && response.response.storage.keys) {
var allKeys = response.response.storage.keys;
logDebug('Всего ключей: ' + allKeys.length);
var ourKeys = [];
for (var i = 0; i < allKeys.length; i++) {
if (allKeys[i].indexOf(PRS_CONFIG.keyPrefix) === 0) {
ourKeys.push(allKeys[i]);
}
}
logDebug('Наши ключи: ' + ourKeys.length);
if (ourKeys.length === 0) {
charactersData = [];
renderTable();
return;
}
loadKeysData(ourKeys);
} else {
charactersData = [];
renderTable();
}
}, function(err) {
document.getElementById('prsTableBody').innerHTML =
'<tr><td colspan="8" style="text-align:center">Ошибка: ' + err + '</td></tr>';
});
}
function loadKeysData(keys) {
var params = {};
for (var i = 0; i < keys.length; i++) {
params['key[' + i + ']'] = keys[i];
}
apiCall('storage.get', params, function(response) {
if (response.response && response.response.storage && response.response.storage.data) {
var data = response.response.storage.data;
charactersData = [];
for (var key in data) {
if (data.hasOwnProperty(key)) {
try {
var rawData = data[key];
var char = JSON.parse(rawData);
char._key = key;
charactersData.push(char);
} catch(e) {
logDebug('Ошибка парсинга: ' + e.message);
}
}
}
logDebug('Загружено персонажей: ' + charactersData.length);
renderTable();
} else {
charactersData = [];
renderTable();
}
}, function(err) {
logDebug('Ошибка загрузки: ' + err);
});
}
// ==================== СОХРАНЕНИЕ ====================
function saveCharacter(char, callback) {
var key = char._key;
if (!key) {
key = PRS_CONFIG.keyPrefix + Date.now() + '_' + Math.random().toString(36).substr(2, 6);
char._key = key;
}
var data = {
number: parseInt(char.number) || 0,
photo: char.photo || '',
name: char.name || '',
link: char.link || '',
age: char.age || '',
deathDate: char.deathDate || '',
type: char.type || '',
location: char.location || '',
occupation: char.occupation || '',
appearance: char.appearance || ''
};
var jsonData = JSON.stringify(data);
logDebug('Сохранение: ' + key);
apiToken = getToken();
if (!apiToken) {
alert('Ошибка: не найден токен авторизации.');
if (callback) callback(false);
return;
}
apiCall('storage.set', {
key: key,
value: jsonData
}, function(response) {
if (isSuccess(response)) {
logDebug('Успешно сохранено!');
if (callback) callback(true, key);
} else {
logDebug('Ошибка сохранения');
if (callback) callback(false);
}
}, function(err) {
if (callback) callback(false);
});
}
function deleteCharacter(key, callback) {
logDebug('Удаление: ' + key);
apiToken = getToken();
if (!apiToken) {
alert('Ошибка: не найден токен авторизации.');
if (callback) callback(false);
return;
}
apiCall('storage.delete', {key: key}, function(response) {
if (isSuccess(response)) {
if (callback) callback(true);
} else {
if (callback) callback(false);
}
}, function(err) {
if (callback) callback(false);
});
}
// ==================== ВСПОМОГАТЕЛЬНЫЕ ====================
function getCharacterLink(char) {
if (char.link && char.link.trim() !== "") {
var link = char.link;
if (!link.startsWith('http') && !link.startsWith('/')) link = '/' + link;
var isExternal = link.startsWith('http://') || link.startsWith('https://');
var targetAttr = isExternal ? ' target="_blank" rel="noopener noreferrer"' : '';
return '<a href="' + link + '" class="character-name-link"' + targetAttr + '>' + char.name + '</a>';
}
return '<span class="character-name-link" style="cursor: default;">' + char.name + '</span>';
}
function getAgeDisplay(char) {
if (char.deathDate && char.deathDate.trim() !== "") {
return '<span class="death-date">⚰️ ' + char.deathDate + '</span>';
}
return char.age || '—';
}
// ==================== РЕНДЕРИНГ ====================
function renderTable() {
var tbody = document.getElementById('prsTableBody');
if (!tbody) return;
if (!charactersData || charactersData.length === 0) {
tbody.innerHTML = '<tr><td colspan="8" style="text-align:center">Нет персонажей</td></tr>';
return;
}
// Сортируем по введённому номеру (number)
var sorted = charactersData.slice().sort(function(a, b) {
return (parseInt(a.number) || 0) - (parseInt(b.number) || 0);
});
var filterValue = document.getElementById('prsStatusFilter').value;
var filtered = sorted;
if (filterValue === 'deceased') {
filtered = sorted.filter(function(char) {
return char.deathDate && char.deathDate.trim() !== "";
});
} else if (filterValue === 'alive') {
filtered = sorted.filter(function(char) {
return !char.deathDate || char.deathDate.trim() === "";
});
}
var html = '';
for (var i = 0; i < filtered.length; i++) {
var char = filtered[i];
// ПЕРВАЯ БУКВА ЖИРНАЯ И ПОДЧЁРКНУТАЯ
var infoText = [];
if (char.location) {
var locFirst = char.location.charAt(0);
var locRest = char.location.slice(1);
infoText.push('📍 <span class="first-letter">' + locFirst + '</span>' + locRest);
}
if (char.occupation) {
var occFirst = char.occupation.charAt(0);
var occRest = char.occupation.slice(1);
infoText.push('💼 <span class="first-letter">' + occFirst + '</span>' + occRest);
}
var infoHtml = infoText.length > 0 ? infoText.join('<br>') : '—';
var rotateClass = (i % 2 === 0) ? 'character-photo-left' : 'character-photo-right';
var ageDisplay = getAgeDisplay(char);
var actions = '';
if (isAdmin) {
actions = '<td class="center">' +
'<button class="prs-action-btn" onclick="editCharacter(\'' + char._key + '\')" title="Редактировать">✏️</button>' +
'<button class="prs-action-btn" onclick="deleteCharacterPrompt(\'' + char._key + '\')" title="Удалить">🗑️</button>' +
'</td>';
}
html += '<tr>';
// Показываем ВВЕДЁННЫЙ номер, а не порядковый
html += '<td class="center">' + (char.number || '—') + '</td>';
html += '<td class="center">';
if (char.photo && char.photo.trim() !== "") {
html += '<img src="' + char.photo + '" class="character-photo ' + rotateClass + '" alt="' + char.name + '">';
} else {
html += '<span style="color: #8b847a;">—</span>';
}
html += '</td>';
html += '<td>' + getCharacterLink(char) + '</td>';
html += '<td class="character-age">' + ageDisplay + '</td>';
html += '<td>' + (char.type || '—') + '</td>';
html += '<td>' + infoHtml + '</td>';
html += '<td>' + (char.appearance || '—') + '</td>';
html += actions;
html += '</tr>';
}
tbody.innerHTML = html;
}
// ==================== ФИЛЬТРАЦИЯ И ПОИСК (ОБНОВЛЁННАЯ ВЕРСИЯ) ====================
function filterTable() {
var search = document.getElementById('prsSearchInput').value.toLowerCase().trim();
var status = document.getElementById('prsStatusFilter').value;
var rows = document.querySelectorAll('#prsTableBody tr');
for (var i = 0; i < rows.length; i++) {
var row = rows[i];
// Проверяем, есть ли в строке данные (не "Нет персонажей" и не "Загрузка...")
if (row.cells.length < 2) {
row.style.display = '';
continue;
}
// Получаем данные из колонок (индексы с 0)
var nameCell = row.cells[2] ? row.cells[2].textContent.toLowerCase() : ''; // Имя (колонка 3)
var appearanceCell = row.cells[6] ? row.cells[6].textContent.toLowerCase() : ''; // Внешность (колонка 7)
var statusCell = row.cells[3] ? row.cells[3].textContent.toLowerCase() : ''; // Возраст/Статус (колонка 4)
var show = true;
// ПОИСК: проверяем Имя ИЛИ Внешность
if (search) {
var nameMatch = nameCell.indexOf(search) !== -1;
var appearanceMatch = appearanceCell.indexOf(search) !== -1;
// Показываем, если совпадает хотя бы одно поле
show = nameMatch || appearanceMatch;
}
// ФИЛЬТР ПО СТАТУСУ (жив/мёртв)
if (show && status !== 'all') {
var hasDeathDate = statusCell.indexOf('⚰️') !== -1 || statusCell.indexOf('дата смерти') !== -1;
if (status === 'deceased') {
show = hasDeathDate;
} else if (status === 'alive') {
show = !hasDeathDate;
}
}
row.style.display = show ? '' : 'none';
}
}
// ==================== ФОРМА ====================
function showEditForm(character) {
var isNew = !character;
document.getElementById('prsFormTitle').textContent = isNew ? 'Добавление персонажа' : 'Редактирование персонажа';
document.getElementById('prsKey').value = character ? character._key : '';
document.getElementById('prsNumber').value = character ? (character.number || '') : '';
document.getElementById('prsPhoto').value = character ? (character.photo || '') : '';
document.getElementById('prsName').value = character ? (character.name || '') : '';
document.getElementById('prsLink').value = character ? (character.link || '') : '';
document.getElementById('prsAge').value = character ? (character.age || '') : '';
document.getElementById('prsDeathDate').value = character ? (character.deathDate || '') : '';
document.getElementById('prsType').value = character ? (character.type || '') : '';
document.getElementById('prsLocation').value = character ? (character.location || '') : '';
document.getElementById('prsOccupation').value = character ? (character.occupation || '') : '';
document.getElementById('prsAppearance').value = character ? (character.appearance || '') : '';
document.getElementById('prsEditForm').style.display = 'block';
document.getElementById('prsEditForm').scrollIntoView({behavior: 'smooth'});
}
function hideEditForm() {
document.getElementById('prsEditForm').style.display = 'none';
}
function collectFormData() {
return {
_key: document.getElementById('prsKey').value,
number: document.getElementById('prsNumber').value,
photo: document.getElementById('prsPhoto').value,
name: document.getElementById('prsName').value,
link: document.getElementById('prsLink').value,
age: document.getElementById('prsAge').value,
deathDate: document.getElementById('prsDeathDate').value,
type: document.getElementById('prsType').value,
location: document.getElementById('prsLocation').value,
occupation: document.getElementById('prsOccupation').value,
appearance: document.getElementById('prsAppearance').value
};
}
function validateForm() {
if (!document.getElementById('prsName').value.trim()) {
alert('Введите имя персонажа');
return false;
}
if (!document.getElementById('prsNumber').value.trim()) {
alert('Введите номер');
return false;
}
return true;
}
function saveForm() {
if (!validateForm()) return;
var char = collectFormData();
var isNew = !char._key;
logDebug('=== СОХРАНЕНИЕ === ' + (isNew ? 'новый' : 'обновление'));
saveCharacter(char, function(success, key) {
if (success) {
if (isNew) {
charactersData.push(char);
} else {
for (var i = 0; i < charactersData.length; i++) {
if (charactersData[i]._key === char._key) {
charactersData[i] = char;
break;
}
}
}
renderTable();
hideEditForm();
alert('Персонаж сохранен!');
} else {
alert('Ошибка сохранения!');
}
});
}
function deleteCharacterPrompt(key) {
if (!confirm('Удалить этого персонажа?')) return;
deleteCharacter(key, function(success) {
if (success) {
charactersData = charactersData.filter(function(char) { return char._key !== key; });
renderTable();
alert('Персонаж удален!');
} else {
alert('Ошибка удаления');
}
});
}
function editCharacter(key) {
var char = null;
for (var i = 0; i < charactersData.length; i++) {
if (charactersData[i]._key === key) {
char = charactersData[i];
break;
}
}
if (char) showEditForm(char);
}
// ==================== ИНИЦИАЛИЗАЦИЯ ====================
function init() {
if (!checkPermissions()) return;
apiToken = getToken();
logDebug('Token: ' + (apiToken ? 'найден' : 'НЕТ'));
if (isAdmin) {
document.querySelector('.prs-admin-panel').style.display = 'flex';
var actionsHeader = document.querySelector('.col-actions');
if (actionsHeader) actionsHeader.style.display = 'table-cell';
}
document.getElementById('prsAddBtn').addEventListener('click', function() { showEditForm(null); });
document.getElementById('prsSaveBtn').addEventListener('click', saveForm);
document.getElementById('prsCancelBtn').addEventListener('click', hideEditForm);
var searchInput = document.getElementById('prsSearchInput');
var resetBtn = document.getElementById('prsResetBtn');
var statusFilter = document.getElementById('prsStatusFilter');
if (searchInput) searchInput.addEventListener('keyup', filterTable);
if (resetBtn) resetBtn.addEventListener('click', function() {
if (searchInput) searchInput.value = '';
if (statusFilter) statusFilter.value = 'all';
filterTable();
});
if (statusFilter) statusFilter.addEventListener('change', filterTable);
loadData();
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}
</script>