[html]<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Список ролей</title>
<link rel="stylesheet" type="text/css" href="https://forumstatic.ru/files/001c/b1/c6/77845.css?v=1">
<style>
.admin-header {
display: flex;
justify-content: flex-end;
margin-bottom: 15px;
}
.admin-btn {
background: #C0C0B9;
color: #22281D;
border: none;
padding: 6px 12px;
border-radius: 20px;
cursor: pointer;
font-weight: 500;
font-family: Golos Text;
}
.edit-btn {
background: none;
border: none;
cursor: pointer;
font-size: 12px;
margin-left: 6px;
color: #C0C0B9;
display: inline-block;
}
.modal {
display: none;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0,0,0,0.8);
justify-content: center;
align-items: center;
z-index: 1000;
}
.modal-content {
background: #22281D;
padding: 20px;
border-radius: 12px;
min-width: 320px;
border: 1px solid #C0C0B9;
}
.modal-content h3 {
margin-top: 0;
color: #C0C0B9;
}
.modal-content input, .modal-content select {
width: 100%;
padding: 6px;
margin-bottom: 12px;
background: #2a3323;
color: #E8E8E8;
border: 1px solid #C0C0B9;
border-radius: 4px;
box-sizing: border-box;
}
.modal-content label {
display: block;
margin-bottom: 10px;
color: #E8E8E8;
}
.modal-buttons {
display: flex;
gap: 10px;
justify-content: flex-end;
margin-top: 10px;
}
.delete-btn {
background: #8B0000;
color: white;
}
.roli {
height: 200px;
overflow: auto;
}
.roli::-webkit-scrollbar {
width: 3px;
height: 3px;
}
</style>
</head>
<body>
<div class="admin-header" id="adminHeader" style="display: none;">
<button id="addCastBtn" class="admin-btn">➕ Добавить каст</button>
</div>
<table class="spisok_rol" id="rolesTable">
<tr>
<td style="vertical-align: top; width: 50%;" id="colLeft"></td>
<td style="vertical-align: top; width: 50%;" id="colRight"></td>
</tr>
</table>
<!-- Модалка для каста -->
<div id="castModal" class="modal">
<div class="modal-content">
<h3 id="castModalTitle">Редактировать каст</h3>
<input type="text" id="castNameInput" placeholder="Название каста">
<div class="modal-buttons">
<button id="castSaveBtn" class="admin-btn">Сохранить</button>
<button id="castDeleteBtn" class="admin-btn delete-btn" style="display:none;">🗑 Удалить каст</button>
<button id="castCancelBtn" class="admin-btn">Отмена</button>
</div>
</div>
</div>
<!-- Модалка для персонажа -->
<div id="roleModal" class="modal">
<div class="modal-content">
<h3 id="roleModalTitle">Добавить персонажа</h3>
<input type="text" id="roleNameEn" placeholder="Английское имя (будет ссылкой)">
<input type="text" id="roleNameRu" placeholder="Русское имя (будет текстом)">
<input type="number" id="roleId" placeholder="ID профиля">
<select id="roleCastSelect"></select>
<label><input type="checkbox" id="roleIsMain" checked> Основной персонаж</label>
<div id="mainIdField" style="display:none;">
<input type="number" id="roleMainId" placeholder="ID основного персонажа">
</div>
<div class="modal-buttons">
<button id="roleSaveBtn" class="admin-btn">Сохранить</button>
<button id="roleDeleteBtn" class="admin-btn delete-btn" style="display:none;">🗑 Удалить</button>
<button id="roleCancelBtn" class="admin-btn">Отмена</button>
</div>
</div>
</div>
<script>
let rolesData = {};
let editCastName = null;
let editRoleData = null;
let isAdmin = false;
// Проверка прав администратора через GroupID
if (typeof GroupID !== 'undefined' && GroupID === 1) {
isAdmin = true;
document.getElementById('adminHeader').style.display = 'flex';
}
const letterGroups = [
{ title: 'A B C D E', letters: ['A','B','C','D','E'] },
{ title: 'F G H I J', letters: ['F','G','H','I','J'] },
{ title: 'K L M N O', letters: ['K','L','M','N','O'] },
{ title: 'P Q R S T', letters: ['P','Q','R','S','T'] },
{ title: 'U V W X', letters: ['U','V','W','X'] },
{ title: 'Y Z 1-9', letters: ['Y','Z','#'] }
];
function getLetterGroup(firstLetter) {
firstLetter = firstLetter.toUpperCase();
for (let group of letterGroups) {
if (group.letters.includes(firstLetter)) {
return group.title;
}
}
return 'Y Z 1-9';
}
async function loadData() {
try {
const resp = await fetch('https://rvnln.ru/my_api.php');
const json = await resp.json();
if (json.success) {
rolesData = json.data;
renderTable();
fillCastSelect();
}
} catch(e) { console.error(e); }
}
function renderTable() {
let castsWithLetter = [];
for (let letter in rolesData) {
for (let cast in rolesData[letter]) {
let firstChar = cast.charAt(0).toUpperCase();
if (/[0-9]/.test(firstChar)) firstChar = '#';
castsWithLetter.push({
name: cast,
firstLetter: firstChar,
data: rolesData[letter][cast]
});
}
}
castsWithLetter.sort((a, b) => a.name.localeCompare(b.name, 'en'));
let grouped = {};
for (let group of letterGroups) {
grouped[group.title] = [];
}
for (let cast of castsWithLetter) {
let groupTitle = getLetterGroup(cast.firstLetter);
if (grouped[groupTitle]) {
grouped[groupTitle].push(cast);
} else {
grouped['Y Z 1-9'].push(cast);
}
}
let leftHtml = '';
let rightHtml = '';
let isLeft = true;
for (let group of letterGroups) {
let castsInGroup = grouped[group.title];
let groupHtml = renderGroup(group.title, castsInGroup);
if (isLeft) {
leftHtml += groupHtml;
} else {
rightHtml += groupHtml;
}
isLeft = !isLeft;
}
document.getElementById('colLeft').innerHTML = leftHtml;
document.getElementById('colRight').innerHTML = rightHtml;
}
function renderGroup(groupTitle, casts) {
if (casts.length === 0) {
return `<div class="zag">${escapeHtml(groupTitle)}</div><br><div class="roli"></div>`;
}
let castsHtml = '';
for (let cast of casts) {
castsHtml += `<div class="nazfandom">${escapeHtml(cast.name)}
${isAdmin ? `<button class="edit-btn" onclick="openCastModal('${escapeHtml(cast.name).replace(/'/g, "\\'")}')">⚙️</button>` : ''}
</div><br>`;
for (let role of (cast.data.roles || [])) {
castsHtml += `<a href="https://durka.f-rpg.me/profile.php?id=${role.id}" target="_blank">${escapeHtml(role.name_en)}</a> - ${escapeHtml(role.name_ru)}`;
if (isAdmin) {
castsHtml += ` <button class="edit-btn" onclick="openRoleModal(${role.id}, '${escapeHtml(role.name_en).replace(/'/g, "\\'")}', '${escapeHtml(role.name_ru).replace(/'/g, "\\'")}', '${escapeHtml(cast.name).replace(/'/g, "\\'")}')">✏️</button>`;
}
castsHtml += `<br>`;
}
if (isAdmin) {
castsHtml += `<button class="edit-btn" onclick="openRoleModal(null, null, null, '${escapeHtml(cast.name).replace(/'/g, "\\'")}')">+ Добавить персонажа</button><br>`;
}
}
return `<div class="zag">${escapeHtml(groupTitle)}</div><br><div class="roli">${castsHtml}</div>`;
}
function escapeHtml(str) {
if (!str) return '';
return str.replace(/[&<>]/g, function(m) {
if (m === '&') return '&';
if (m === '<') return '<';
if (m === '>') return '>';
return m;
});
}
function fillCastSelect() {
let select = document.getElementById('roleCastSelect');
if (!select) return;
select.innerHTML = '';
let allCasts = [];
for (let letter in rolesData) {
for (let cast in rolesData[letter]) {
allCasts.push(cast);
}
}
allCasts.sort((a, b) => a.localeCompare(b, 'en'));
for (let cast of allCasts) {
let opt = document.createElement('option');
opt.value = cast;
opt.textContent = cast;
select.appendChild(opt);
}
}
function openCastModal(castName) {
editCastName = castName;
document.getElementById('castModalTitle').textContent = castName ? 'Редактировать каст' : 'Добавить каст';
document.getElementById('castNameInput').value = castName || '';
document.getElementById('castDeleteBtn').style.display = castName ? 'inline-block' : 'none';
document.getElementById('castModal').style.display = 'flex';
}
function openRoleModal(id, nameEn, nameRu, castName) {
editRoleData = id ? { id } : null;
document.getElementById('roleModalTitle').textContent = id ? 'Редактировать персонажа' : 'Добавить персонажа';
document.getElementById('roleNameEn').value = nameEn || '';
document.getElementById('roleNameRu').value = nameRu || '';
document.getElementById('roleId').value = id || '';
document.getElementById('roleCastSelect').value = castName;
document.getElementById('roleIsMain').checked = true;
document.getElementById('mainIdField').style.display = 'none';
document.getElementById('roleDeleteBtn').style.display = id ? 'inline-block' : 'none';
document.getElementById('roleModal').style.display = 'flex';
}
function closeModals() {
document.getElementById('castModal').style.display = 'none';
document.getElementById('roleModal').style.display = 'none';
}
async function apiCall(action, data) {
let formData = new URLSearchParams({ action, ...data });
let resp = await fetch('https://rvnln.ru/my_api.php', { method: 'POST', body: formData });
let json = await resp.json();
if (!json.success) alert('Ошибка: ' + json.message);
return json.success;
}
document.getElementById('castSaveBtn').onclick = async () => {
let newName = document.getElementById('castNameInput').value.trim();
if (!newName) return alert('Введите название каста');
let ok;
if (editCastName) {
ok = await apiCall('edit_cast', { old_name: editCastName, new_name: newName });
} else {
ok = await apiCall('add_cast', { name: newName });
}
if (ok) {
closeModals();
loadData();
}
};
document.getElementById('castDeleteBtn').onclick = async () => {
if (!confirm(`Удалить каст "${editCastName}" и ВСЕХ персонажей в нём?`)) return;
let ok = await apiCall('delete_cast', { name: editCastName });
if (ok) {
closeModals();
loadData();
}
};
document.getElementById('castCancelBtn').onclick = closeModals;
document.getElementById('roleSaveBtn').onclick = async () => {
let nameEn = document.getElementById('roleNameEn').value.trim();
let nameRu = document.getElementById('roleNameRu').value.trim();
let userId = parseInt(document.getElementById('roleId').value);
let castName = document.getElementById('roleCastSelect').value;
let isMain = document.getElementById('roleIsMain').checked ? 1 : 0;
let mainId = document.getElementById('roleMainId').value || null;
if (!nameEn || !userId || !castName) return alert('Заполните английское имя и ID');
let ok;
if (editRoleData) {
ok = await apiCall('edit_character', { user_id: userId, name_en: nameEn, name_ru: nameRu, cast_name: castName });
} else {
ok = await apiCall('add_character', { user_id: userId, name_en: nameEn, name_ru: nameRu, cast_name: castName, is_main: isMain, main_id: mainId });
}
if (ok) {
closeModals();
loadData();
}
};
document.getElementById('roleDeleteBtn').onclick = async () => {
if (!confirm('Удалить персонажа?')) return;
if (await apiCall('delete_character', { user_id: editRoleData.id })) {
closeModals();
loadData();
}
};
document.getElementById('roleCancelBtn').onclick = closeModals;
document.getElementById('addCastBtn').onclick = () => openCastModal(null);
document.getElementById('roleIsMain').onchange = (e) => {
document.getElementById('mainIdField').style.display = e.target.checked ? 'none' : 'block';
};
loadData();
</script>
</body>
</html>[/html]





























