release website style for panel
This commit is contained in:
572
public/js/app.js
572
public/js/app.js
@@ -28,6 +28,61 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
'enable-status'
|
||||
];
|
||||
|
||||
const sections = document.querySelectorAll('.section-bg');
|
||||
const observer = new IntersectionObserver((entries) => {
|
||||
entries.forEach((entry, index) => {
|
||||
if (entry.isIntersecting) {
|
||||
entry.target.style.transform = 'translateY(0)';
|
||||
entry.target.style.opacity = '1';
|
||||
entry.target.style.transitionDelay = `${index * 0.1}s`;
|
||||
}
|
||||
});
|
||||
}, { threshold: 0.1 });
|
||||
|
||||
|
||||
function throttle(fn, wait) {
|
||||
let lastTime = 0;
|
||||
return function (...args) {
|
||||
const now = Date.now();
|
||||
if (now - lastTime >= wait) {
|
||||
fn.apply(this, args);
|
||||
lastTime = now;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Hamburger Menu Toggle
|
||||
const hamburger = document.querySelector('.hamburger');
|
||||
const mobileNav = document.querySelector('[data-mobile-nav]');
|
||||
const navLinks = mobileNav.querySelectorAll('a');
|
||||
|
||||
// Debounce function to prevent rapid clicks
|
||||
function debounce(fn, wait) {
|
||||
let timeout;
|
||||
return function (...args) {
|
||||
clearTimeout(timeout);
|
||||
timeout = setTimeout(() => fn.apply(this, args), wait);
|
||||
};
|
||||
}
|
||||
|
||||
hamburger.addEventListener('click', debounce(() => {
|
||||
mobileNav.classList.toggle('active');
|
||||
hamburger.classList.toggle('active');
|
||||
}, 100));
|
||||
|
||||
navLinks.forEach(link => {
|
||||
link.addEventListener('click', () => {
|
||||
mobileNav.classList.remove('active');
|
||||
hamburger.classList.remove('active');
|
||||
});
|
||||
});
|
||||
|
||||
document.addEventListener('click', (e) => {
|
||||
if (!mobileNav.contains(e.target) && !hamburger.contains(e.target) && mobileNav.classList.contains('active')) {
|
||||
mobileNav.classList.remove('active');
|
||||
hamburger.classList.remove('active');
|
||||
}
|
||||
});
|
||||
const elements = {
|
||||
loginPage: document.getElementById('loginPage'),
|
||||
loginApiKey: document.getElementById('loginApiKey'),
|
||||
@@ -321,7 +376,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
terminal.clear();
|
||||
} else {
|
||||
terminal = new Terminal({
|
||||
rows: 8,
|
||||
rows: 22,
|
||||
fontSize: 14,
|
||||
fontFamily: 'monospace',
|
||||
theme: {
|
||||
@@ -497,91 +552,178 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
}
|
||||
}
|
||||
|
||||
function toggleSections(status) {
|
||||
const sections = document.querySelectorAll('.bg-gray-800.p-6.rounded-lg.shadow-lg.mb-6');
|
||||
const serverStatusSection = document.querySelector('.bg-gray-800.p-6.rounded-lg.shadow-lg.mb-6[data-section="server-status"]');
|
||||
const editPropertiesBtn = elements.editPropertiesBtn;
|
||||
const updateModsBtn = elements.updateModsBtn;
|
||||
const backupBtn = elements.backupBtn;
|
||||
const sftpBtn = elements.sftpBtn;
|
||||
const startBtn = document.getElementById('startBtn');
|
||||
const stopBtn = elements.stopBtn;
|
||||
const restartBtn = elements.restartBtn;
|
||||
const sftpBrowserSection = elements.sftpBrowserSection;
|
||||
|
||||
if (startBtn) {
|
||||
if (status.toLowerCase() === 'running') {
|
||||
startBtn.disabled = true;
|
||||
startBtn.classList.add('disabled-btn');
|
||||
} else {
|
||||
startBtn.disabled = false;
|
||||
startBtn.classList.remove('disabled-btn');
|
||||
}
|
||||
function updateDockerUI(message) {
|
||||
if (message.error) {
|
||||
console.log('Docker error detected, setting status to Not Running');
|
||||
if (elements.serverStatus) elements.serverStatus.textContent = 'Not Running';
|
||||
toggleSections('Not Running');
|
||||
return;
|
||||
}
|
||||
|
||||
if (status.toLowerCase() !== 'running') {
|
||||
const memoryPercent = parseFloat(message.data?.memory?.percent) || 0;
|
||||
if (state.memoryPercent !== memoryPercent && elements.memoryPercent && memoryMeter) {
|
||||
memoryMeter.data.datasets[0].data = [memoryPercent, 100 - memoryPercent];
|
||||
memoryMeter.update();
|
||||
elements.memoryPercent.textContent = `${memoryPercent.toFixed(1)}%`;
|
||||
state.memoryPercent = memoryPercent;
|
||||
}
|
||||
|
||||
const cpuPercent = parseFloat(message.data?.cpu) || 0;
|
||||
if (state.cpuPercent !== cpuPercent && elements.cpuPercent && cpuMeter) {
|
||||
const scaledCpuPercent = Math.min((cpuPercent / 600) * 100, 100);
|
||||
cpuMeter.data.datasets[0].data = [scaledCpuPercent, 100 - scaledCpuPercent];
|
||||
cpuMeter.update();
|
||||
elements.cpuPercent.textContent = `${cpuPercent.toFixed(1)}%`;
|
||||
state.cpuPercent = cpuPercent;
|
||||
}
|
||||
|
||||
const status = message.data?.status || 'Unknown';
|
||||
if (elements.serverStatus) {
|
||||
elements.serverStatus.textContent = status;
|
||||
state.serverStatus = status;
|
||||
toggleSections(status);
|
||||
}
|
||||
}
|
||||
|
||||
function toggleSections(status) {
|
||||
const sections = document.querySelectorAll('.section-bg');
|
||||
const serverStatusSection = document.querySelector('.section-bg[data-section="server-status"]');
|
||||
const editPropertiesBtn = elements.editPropertiesBtn;
|
||||
const updateModsBtn = elements.updateModsBtn;
|
||||
const sftpBtn = elements.sftpBtn;
|
||||
const startBtn = document.getElementById('startBtn');
|
||||
const stopBtn = elements.stopBtn;
|
||||
const restartBtn = elements.restartBtn;
|
||||
const sftpBrowserSection = elements.sftpBrowserSection;
|
||||
// Menu items in desktop and mobile nav
|
||||
const refreshBtn = elements.refresh || document.getElementById('refresh');
|
||||
const mobileRefreshBtn = elements.mobileRefresh || document.getElementById('mobileRefresh');
|
||||
const backupBtn = elements.backupBtn;
|
||||
const mobileBackupBtn = elements.mobileBackupBtn;
|
||||
const logoutBtn = elements.logoutBtn || document.getElementById('logoutBtn');
|
||||
const mobileLogoutBtn = elements.mobileLogoutBtn || document.getElementById('mobileLogoutBtn');
|
||||
|
||||
if (startBtn) {
|
||||
if (status.toLowerCase() === 'running') {
|
||||
startBtn.disabled = true;
|
||||
startBtn.classList.add('disabled-btn');
|
||||
} else {
|
||||
startBtn.disabled = false;
|
||||
startBtn.classList.remove('disabled-btn');
|
||||
}
|
||||
}
|
||||
|
||||
if (status.toLowerCase() !== 'running') {
|
||||
// Hide all sections except Server Status
|
||||
sections.forEach(section => {
|
||||
if (section !== serverStatusSection) {
|
||||
section.classList.add('hidden');
|
||||
}
|
||||
if (section !== serverStatusSection) {
|
||||
section.classList.add('hidden');
|
||||
}
|
||||
});
|
||||
// Hide control buttons
|
||||
if (editPropertiesBtn) {
|
||||
editPropertiesBtn.classList.add('hidden');
|
||||
editPropertiesBtn.classList.add('hidden');
|
||||
editPropertiesBtn.style.display = 'none';
|
||||
}
|
||||
if (updateModsBtn) {
|
||||
updateModsBtn.classList.add('hidden');
|
||||
}
|
||||
if (backupBtn) {
|
||||
backupBtn.classList.add('hidden');
|
||||
updateModsBtn.classList.add('hidden');
|
||||
updateModsBtn.style.display = 'none';
|
||||
}
|
||||
if (sftpBtn) {
|
||||
sftpBtn.classList.add('hidden');
|
||||
sftpBtn.classList.add('hidden');
|
||||
}
|
||||
if (stopBtn) {
|
||||
stopBtn.disabled = true;
|
||||
stopBtn.classList.add('disabled-btn');
|
||||
stopBtn.disabled = true;
|
||||
stopBtn.classList.add('disabled-btn');
|
||||
}
|
||||
if (restartBtn) {
|
||||
restartBtn.disabled = true;
|
||||
restartBtn.classList.add('disabled-btn');
|
||||
restartBtn.disabled = true;
|
||||
restartBtn.classList.add('disabled-btn');
|
||||
}
|
||||
if (sftpBrowserSection) {
|
||||
sftpBrowserSection.style.display = 'none';
|
||||
sftpBrowserSection.style.display = 'none';
|
||||
}
|
||||
// Hide Refresh, Backup, and Logout buttons in desktop nav
|
||||
if (refreshBtn) {
|
||||
refreshBtn.classList.add('hidden');
|
||||
refreshBtn.style.display = 'none';
|
||||
}
|
||||
if (backupBtn) {
|
||||
backupBtn.classList.add('hidden');
|
||||
backupBtn.style.display = 'none';
|
||||
}
|
||||
if (logoutBtn) {
|
||||
logoutBtn.classList.add('hidden');
|
||||
logoutBtn.style.display = 'none';
|
||||
}
|
||||
// Hide Refresh, Backup, and Logout menu items in mobile nav
|
||||
if (mobileRefreshBtn && mobileRefreshBtn.parentElement) {
|
||||
mobileRefreshBtn.parentElement.classList.add('hidden');
|
||||
}
|
||||
if (mobileBackupBtn && mobileBackupBtn.parentElement) {
|
||||
mobileBackupBtn.parentElement.classList.add('hidden');
|
||||
}
|
||||
if (mobileLogoutBtn && mobileLogoutBtn.parentElement) {
|
||||
mobileLogoutBtn.parentElement.classList.add('hidden');
|
||||
}
|
||||
if (!state.hasShownStartNotification) {
|
||||
showNotification('Server is stopped. Click "Start" to enable all features.', 'error', 'server-stopped');
|
||||
state.hasShownStartNotification = true;
|
||||
showNotification('Server is stopped. Click "Start" to enable all features.', 'error', 'server-stopped');
|
||||
state.hasShownStartNotification = true;
|
||||
}
|
||||
} else {
|
||||
} else {
|
||||
// Show all sections
|
||||
sections.forEach(section => {
|
||||
section.classList.remove('hidden');
|
||||
section.classList.remove('hidden');
|
||||
});
|
||||
// Show control buttons
|
||||
if (editPropertiesBtn) {
|
||||
editPropertiesBtn.classList.remove('hidden');
|
||||
editPropertiesBtn.classList.remove('hidden');
|
||||
editPropertiesBtn.style.display = '';
|
||||
}
|
||||
if (updateModsBtn) {
|
||||
updateModsBtn.classList.remove('hidden');
|
||||
}
|
||||
if (backupBtn) {
|
||||
backupBtn.classList.remove('hidden');
|
||||
updateModsBtn.classList.remove('hidden');
|
||||
updateModsBtn.style.display = '';
|
||||
}
|
||||
if (sftpBtn) {
|
||||
sftpBtn.classList.remove('hidden');
|
||||
sftpBtn.classList.remove('hidden');
|
||||
}
|
||||
if (stopBtn) {
|
||||
stopBtn.disabled = false;
|
||||
stopBtn.classList.remove('disabled-btn');
|
||||
stopBtn.disabled = false;
|
||||
stopBtn.classList.remove('disabled-btn');
|
||||
}
|
||||
if (restartBtn) {
|
||||
restartBtn.disabled = false;
|
||||
restartBtn.classList.remove('disabled-btn');
|
||||
restartBtn.disabled = false;
|
||||
stopBtn.classList.remove('disabled-btn');
|
||||
}
|
||||
if (sftpBrowserSection) {
|
||||
sftpBrowserSection.style.display = 'block';
|
||||
sftpBrowserSection.style.display = 'block';
|
||||
}
|
||||
// Show Refresh, Backup, and Logout buttons in desktop nav
|
||||
if (refreshBtn) {
|
||||
refreshBtn.classList.remove('hidden');
|
||||
refreshBtn.style.display = '';
|
||||
}
|
||||
if (backupBtn) {
|
||||
backupBtn.classList.remove('hidden');
|
||||
refreshBtn.style.display = '';
|
||||
}
|
||||
if (logoutBtn) {
|
||||
logoutBtn.classList.remove('hidden');
|
||||
logoutBtn.style.display = '';
|
||||
}
|
||||
// Show Refresh, Backup, and Logout menu items in mobile nav
|
||||
if (mobileRefreshBtn && mobileRefreshBtn.parentElement) {
|
||||
mobileRefreshBtn.parentElement.classList.remove('hidden');
|
||||
}
|
||||
if (mobileBackupBtn && mobileBackupBtn.parentElement) {
|
||||
mobileBackupBtn.parentElement.classList.remove('hidden');
|
||||
}
|
||||
if (mobileLogoutBtn && mobileLogoutBtn.parentElement) {
|
||||
mobileLogoutBtn.parentElement.classList.remove('hidden');
|
||||
}
|
||||
state.hasShownStartNotification = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function updateDockerLogsUI(message) {
|
||||
if (message.error) {
|
||||
@@ -751,23 +893,52 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
? players.map(player => `
|
||||
<div class="flex items-center space-x-4 mb-2">
|
||||
<span class="w-32">${player}</span>
|
||||
<div class="flex flex-wrap gap-2">
|
||||
<button class="tell-player bg-blue-600 hover:bg-blue-700 px-2 py-1 rounded text-sm" data-player="${player}">Tell</button>
|
||||
<button class="give-player bg-green-600 hover:bg-green-700 px-2 py-1 rounded text-sm" data-player="${player}">Give</button>
|
||||
<button class="teleport-player bg-cyan-600 hover:bg-cyan-700 px-2 py-1 rounded text-sm ${isSinglePlayer ? 'opacity-50 cursor-not-allowed' : ''}" data-player="${player}" ${isSinglePlayer ? 'disabled' : ''}>Teleport</button>
|
||||
<button class="effect-player bg-teal-600 hover:bg-teal-700 px-2 py-1 rounded text-sm" data-player="${player}">Effect</button>
|
||||
<button class="op-player bg-purple-600 hover:bg-purple-700 px-2 py-1 rounded text-sm" data-player="${player}">Op</button>
|
||||
<button class="deop-player bg-purple-600 hover:bg-purple-700 px-2 py-1 rounded text-sm" data-player="${player}">Deop</button>
|
||||
<button class="kick-player bg-red-600 hover:bg-red-700 px-2 py-1 rounded text-sm" data-player="${player}">Kick</button>
|
||||
<button class="ban-player bg-red-600 hover:bg-red-700 px-2 py-1 rounded text-sm" data-player="${player}">Ban</button>
|
||||
<div class="flex flex-col gap-2">
|
||||
<button class="toggle-actions bg-gray-600 hover:bg-gray-700 px-2 py-1 rounded text-sm" data-player="${player}">Actions</button>
|
||||
<div class="action-buttons hidden flex-wrap gap-2" data-player="${player}">
|
||||
<button class="tell-player bg-blue-600 hover:bg-blue-700 px-2 py-1 rounded text-sm" data-player="${player}">Tell</button>
|
||||
<button class="give-player bg-green-600 hover:bg-green-700 px-2 py-1 rounded text-sm" data-player="${player}">Give</button>
|
||||
<button class="teleport-player bg-cyan-600 hover:bg-cyan-700 px-2 py-1 rounded text-sm ${isSinglePlayer ? 'opacity-50 cursor-not-allowed' : ''}" data-player="${player}" ${isSinglePlayer ? 'disabled' : ''}>Teleport</button>
|
||||
<button class="effect-player bg-teal-600 hover:bg-teal-700 px-2 py-1 rounded text-sm" data-player="${player}">Effect</button>
|
||||
<button class="op-player bg-purple-600 hover:bg-purple-700 px-2 py-1 rounded text-sm" data-player="${player}">Op</button>
|
||||
<button class="deop-player bg-purple-600 hover:bg-purple-700 px-2 py-1 rounded text-sm" data-player="${player}">Deop</button>
|
||||
<button class="kick-player bg-red-600 hover:bg-red-700 px-2 py-1 rounded text-sm" data-player="${player}">Kick</button>
|
||||
<button class="ban-player bg-red-600 hover:bg-red-700 px-2 py-1 rounded text-sm" data-player="${player}">Ban</button>
|
||||
<button class="close-actions bg-gray-600 hover:bg-gray-700 px-2 py-1 rounded text-sm" data-player="${player}">Close</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`).join('')
|
||||
`).join('')
|
||||
: 'None';
|
||||
if (state.playerList !== playerListHtml && elements.playerList) {
|
||||
elements.playerList.innerHTML = playerListHtml;
|
||||
state.playerList = playerListHtml;
|
||||
|
||||
|
||||
// Toggle action buttons
|
||||
document.querySelectorAll('.toggle-actions').forEach(button => {
|
||||
button.addEventListener('click', () => {
|
||||
const player = button.getAttribute('data-player').trim();
|
||||
const actionDiv = document.querySelector(`.action-buttons[data-player="${player}"]`);
|
||||
if (actionDiv) {
|
||||
actionDiv.classList.toggle('hidden');
|
||||
button.textContent = actionDiv.classList.contains('hidden') ? 'Actions' : 'Hide';
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Close action buttons
|
||||
document.querySelectorAll('.close-actions').forEach(button => {
|
||||
button.addEventListener('click', () => {
|
||||
const player = button.getAttribute('data-player').trim();
|
||||
const actionDiv = document.querySelector(`.action-buttons[data-player="${player}"]`);
|
||||
const toggleButton = document.querySelector(`.toggle-actions[data-player="${player}"]`);
|
||||
if (actionDiv && toggleButton) {
|
||||
actionDiv.classList.add('hidden');
|
||||
toggleButton.textContent = 'Actions';
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
document.querySelectorAll('.tell-player').forEach(button => {
|
||||
button.addEventListener('click', () => {
|
||||
const player = button.getAttribute('data-player').trim();
|
||||
@@ -780,7 +951,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
elements.tellModal.classList.remove('hidden');
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
document.querySelectorAll('.give-player').forEach(button => {
|
||||
button.addEventListener('click', () => {
|
||||
const player = button.getAttribute('data-player').trim();
|
||||
@@ -795,7 +966,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
elements.giveModal.classList.remove('hidden');
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
document.querySelectorAll('.teleport-player').forEach(button => {
|
||||
if (!button.disabled) {
|
||||
button.addEventListener('click', () => {
|
||||
@@ -813,7 +984,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
document.querySelectorAll('.effect-player').forEach(button => {
|
||||
button.addEventListener('click', () => {
|
||||
const player = button.getAttribute('data-player').trim();
|
||||
@@ -826,7 +997,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
elements.effectModal.classList.remove('hidden');
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
document.querySelectorAll('.kick-player').forEach(button => {
|
||||
button.addEventListener('click', () => {
|
||||
const player = button.getAttribute('data-player').trim();
|
||||
@@ -858,7 +1029,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
document.querySelectorAll('.ban-player').forEach(button => {
|
||||
button.addEventListener('click', () => {
|
||||
const player = button.getAttribute('data-player').trim();
|
||||
@@ -890,7 +1061,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
document.querySelectorAll('.op-player').forEach(button => {
|
||||
button.addEventListener('click', () => {
|
||||
const player = button.getAttribute('data-player').trim();
|
||||
@@ -922,7 +1093,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
document.querySelectorAll('.deop-player').forEach(button => {
|
||||
button.addEventListener('click', () => {
|
||||
const player = button.getAttribute('data-player').trim();
|
||||
@@ -1038,9 +1209,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
}
|
||||
elements.loginPage.classList.remove('hidden');
|
||||
elements.mainContent.classList.add('hidden');
|
||||
elements.authControls.innerHTML = '<input id="apiKey" type="text" placeholder="Enter API Key" class="bg-gray-700 px-4 py-2 rounded text-white">';
|
||||
elements.apiKeyInput = document.getElementById('apiKey');
|
||||
elements.apiKeyInput.addEventListener('change', handleApiKeyChange);
|
||||
if (ws) {
|
||||
ws.close();
|
||||
ws = null;
|
||||
@@ -1069,13 +1238,35 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
}
|
||||
elements.loginPage.classList.add('hidden');
|
||||
elements.mainContent.classList.remove('hidden');
|
||||
elements.authControls.innerHTML = '<button id="logoutBtn" class="bg-red-600 hover:bg-red-700 px-4 py-2 rounded">Logout</button>';
|
||||
const logoutBtn = document.getElementById('logoutBtn');
|
||||
if (logoutBtn) {
|
||||
logoutBtn.addEventListener('click', handleLogout);
|
||||
} else {
|
||||
console.error('Logout button not found after insertion');
|
||||
}
|
||||
// Verify buttons exist
|
||||
const logoutBtn = document.getElementById('logoutBtn');
|
||||
const mobileLogoutBtn = document.getElementById('mobileLogoutBtn');
|
||||
|
||||
if (logoutBtn) {
|
||||
console.log('Desktop logout button found');
|
||||
} else {
|
||||
console.error('Desktop logout button (#logoutBtn) not found');
|
||||
}
|
||||
|
||||
if (mobileLogoutBtn) {
|
||||
console.log('Mobile logout button found');
|
||||
} else {
|
||||
console.error('Mobile logout button (#mobileLogoutBtn) not found');
|
||||
}
|
||||
|
||||
// Remove any existing logout listeners to prevent duplicates
|
||||
document.removeEventListener('click', handleLogoutClick);
|
||||
|
||||
// Event delegation for logout buttons
|
||||
function handleLogoutClick(event) {
|
||||
if (event.target.id === 'logoutBtn' || event.target.id === 'mobileLogoutBtn') {
|
||||
console.log(`Logout button clicked: ${event.target.id}`);
|
||||
handleLogout();
|
||||
}
|
||||
}
|
||||
|
||||
document.addEventListener('click', handleLogoutClick);
|
||||
|
||||
initializeCharts();
|
||||
initializeTerminal();
|
||||
}
|
||||
@@ -1095,72 +1286,84 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
showLoginPage();
|
||||
}
|
||||
|
||||
async function handleRefresh() {
|
||||
if (ws && ws.readyState === WebSocket.OPEN) {
|
||||
const key = `action-refresh`;
|
||||
const notification = showNotification('Refreshing server data...', 'loading', key);
|
||||
ws.send(JSON.stringify({ type: 'refresh' }));
|
||||
initializeTerminal();
|
||||
setTimeout(() => updateNotification(notification, 'Server data refreshed successfully', 'success', key), 1000);
|
||||
} else {
|
||||
showNotification('Not connected to server. Please log in.', 'error', 'ws-disconnected');
|
||||
}
|
||||
}
|
||||
|
||||
function updatePagination() {
|
||||
const totalPages = Math.max(1, Math.ceil(totalResults / resultsPerPage));
|
||||
elements.pagination.innerHTML = '';
|
||||
|
||||
const createPageButton = (page, text, disabled = false) => {
|
||||
const button = document.createElement('button');
|
||||
button.textContent = text;
|
||||
button.className = `px-3 py-1 rounded ${disabled || page === currentPage
|
||||
? 'bg-gray-600 cursor-not-allowed'
|
||||
: 'bg-blue-600 hover:bg-blue-700'
|
||||
const button = document.createElement('button');
|
||||
button.textContent = text;
|
||||
button.className = `nav-btn ${disabled || page === currentPage
|
||||
? 'disabled'
|
||||
: 'active'
|
||||
}`;
|
||||
if (!disabled && page !== currentPage) {
|
||||
button.addEventListener('click', () => {
|
||||
currentPage = page;
|
||||
searchMods();
|
||||
});
|
||||
}
|
||||
elements.pagination.appendChild(button);
|
||||
if (!disabled && page !== currentPage) {
|
||||
button.addEventListener('click', () => {
|
||||
currentPage = page;
|
||||
searchMods();
|
||||
});
|
||||
}
|
||||
elements.pagination.appendChild(button);
|
||||
};
|
||||
|
||||
if (totalResults > 0) {
|
||||
createPageButton(currentPage - 1, 'Previous', currentPage === 1);
|
||||
const maxButtons = 5;
|
||||
const startPage = Math.max(1, currentPage - Math.floor(maxButtons / 2));
|
||||
const endPage = Math.min(totalPages, startPage + maxButtons - 1);
|
||||
for (let i = startPage; i <= endPage; i++) {
|
||||
createPageButton(i, i.toString());
|
||||
}
|
||||
createPageButton(currentPage + 1, 'Next', currentPage === totalPages);
|
||||
createPageButton(currentPage - 1, 'Previous', currentPage === 1);
|
||||
const maxButtons = 5;
|
||||
const startPage = Math.max(1, currentPage - Math.floor(maxButtons / 2));
|
||||
const endPage = Math.min(totalPages, startPage + maxButtons - 1);
|
||||
for (let i = startPage; i <= endPage; i++) {
|
||||
createPageButton(i, i.toString());
|
||||
}
|
||||
createPageButton(currentPage + 1, 'Next', currentPage === totalPages);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function updateModListPagination() {
|
||||
function updateModListPagination() {
|
||||
const filteredMods = state.allMods.filter(mod =>
|
||||
mod.name.toLowerCase().includes(modListSearchQuery.toLowerCase())
|
||||
mod.name.toLowerCase().includes(modListSearchQuery.toLowerCase())
|
||||
);
|
||||
const totalModPages = Math.max(1, Math.ceil(filteredMods.length / resultsPerPage));
|
||||
elements.modListPagination.innerHTML = '';
|
||||
|
||||
const createPageButton = (page, text, disabled = false) => {
|
||||
const button = document.createElement('button');
|
||||
button.textContent = text;
|
||||
button.className = `px-3 py-1 rounded ${disabled || page === modListCurrentPage
|
||||
? 'bg-gray-600 cursor-not-allowed'
|
||||
: 'bg-blue-600 hover:bg-blue-700'
|
||||
const button = document.createElement('button');
|
||||
button.textContent = text;
|
||||
button.className = `nav-btn ${disabled || page === modListCurrentPage
|
||||
? 'disabled'
|
||||
: 'active'
|
||||
}`;
|
||||
if (!disabled && page !== modListCurrentPage) {
|
||||
button.addEventListener('click', () => {
|
||||
modListCurrentPage = page;
|
||||
renderModList();
|
||||
});
|
||||
}
|
||||
elements.modListPagination.appendChild(button);
|
||||
if (!disabled && page !== modListCurrentPage) {
|
||||
button.addEventListener('click', () => {
|
||||
modListCurrentPage = page;
|
||||
renderModList();
|
||||
});
|
||||
}
|
||||
elements.modListPagination.appendChild(button);
|
||||
};
|
||||
|
||||
if (filteredMods.length > 0) {
|
||||
createPageButton(modListCurrentPage - 1, 'Previous', modListCurrentPage === 1);
|
||||
const maxButtons = 5;
|
||||
const startPage = Math.max(1, modListCurrentPage - Math.floor(maxButtons / 2));
|
||||
const endPage = Math.min(totalModPages, startPage + maxButtons - 1);
|
||||
for (let i = startPage; i <= endPage; i++) {
|
||||
createPageButton(i, i.toString());
|
||||
}
|
||||
createPageButton(modListCurrentPage + 1, 'Next', modListCurrentPage === totalModPages);
|
||||
createPageButton(modListCurrentPage - 1, 'Previous', modListCurrentPage === 1);
|
||||
const maxButtons = 5;
|
||||
const startPage = Math.max(1, modListCurrentPage - Math.floor(maxButtons / 2));
|
||||
const endPage = Math.min(totalModPages, startPage + maxButtons - 1);
|
||||
for (let i = startPage; i <= endPage; i++) {
|
||||
createPageButton(i, i.toString());
|
||||
}
|
||||
createPageButton(modListCurrentPage + 1, 'Next', modListCurrentPage === totalModPages);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function closeSearch() {
|
||||
elements.modSearch.value = '';
|
||||
@@ -1648,9 +1851,6 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
if (filteredSettings.includes(key)) {
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(`Rendering field for ${key}: ${value}`);
|
||||
|
||||
const fieldDiv = document.createElement('div');
|
||||
fieldDiv.className = 'flex items-center space-x-2';
|
||||
fieldDiv.style.display = 'flex';
|
||||
@@ -1687,7 +1887,6 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
switchContainer.className = 'relative inline-block';
|
||||
switchContainer.setAttribute('role', 'switch');
|
||||
switchContainer.setAttribute('aria-checked', value.toLowerCase());
|
||||
switchContainer.setAttribute('tabindex', '0');
|
||||
switchContainer.dataset.name = key;
|
||||
switchContainer.style.width = '40px';
|
||||
switchContainer.style.height = '24px';
|
||||
@@ -1918,7 +2117,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
}
|
||||
});
|
||||
|
||||
elements.apiKeyInput.addEventListener('change', handleApiKeyChange);
|
||||
// elements.apiKeyInput.addEventListener('change', handleApiKeyChange);
|
||||
|
||||
elements.generateMyLinkBtn.addEventListener('click', async () => {
|
||||
try {
|
||||
@@ -1969,46 +2168,47 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
|
||||
document.getElementById('startBtn').addEventListener('click', async () => {
|
||||
try {
|
||||
const key = `action-start`;
|
||||
const notification = showNotification('Starting server...', 'loading', key);
|
||||
await wsRequest('/start');
|
||||
initializeTerminal();
|
||||
if (ws && ws.readyState === WebSocket.OPEN) {
|
||||
ws.send(JSON.stringify({ type: 'subscribe', endpoints: ['docker', 'docker-logs'] }));
|
||||
const messageHandler = (event) => {
|
||||
try {
|
||||
const message = JSON.parse(event.data);
|
||||
if (message.type === 'docker') {
|
||||
state.serverStatus = message.data?.status || 'Unknown';
|
||||
elements.serverStatus.textContent = state.serverStatus;
|
||||
toggleSections(state.serverStatus);
|
||||
if (message.data?.status === 'running') {
|
||||
updateNotification(notification, 'Server started successfully', 'success', key);
|
||||
ws.removeEventListener('message', messageHandler);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error parsing WebSocket message:', error);
|
||||
}
|
||||
};
|
||||
ws.addEventListener('message', messageHandler);
|
||||
setTimeout(() => {
|
||||
if (ws && ws.readyState === WebSocket.OPEN) {
|
||||
ws.removeEventListener('message', messageHandler);
|
||||
if (state.serverStatus !== 'running') {
|
||||
updateNotification(notification, 'Failed to start server. Please try again.', 'error', key);
|
||||
toggleSections(state.serverStatus);
|
||||
}
|
||||
}
|
||||
}, 30000);
|
||||
} else {
|
||||
updateNotification(notification, 'Not connected to server. Please log in.', 'error', key);
|
||||
}
|
||||
const key = `action-start`;
|
||||
const notification = showNotification('Starting server...', 'loading', key);
|
||||
await wsRequest('/start');
|
||||
initializeTerminal();
|
||||
if (ws && ws.readyState === WebSocket.OPEN) {
|
||||
ws.send(JSON.stringify({ type: 'subscribe', endpoints: ['docker', 'docker-logs'] }));
|
||||
const messageHandler = (event) => {
|
||||
try {
|
||||
const message = JSON.parse(event.data);
|
||||
if (message.type === 'docker') {
|
||||
state.serverStatus = message.data?.status || 'Unknown';
|
||||
elements.serverStatus.textContent = state.serverStatus;
|
||||
toggleSections(state.serverStatus);
|
||||
if (message.data?.status === 'running') {
|
||||
updateNotification(notification, 'Server started successfully', 'success', key);
|
||||
ws.removeEventListener('message', messageHandler);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error parsing WebSocket message:', error);
|
||||
}
|
||||
};
|
||||
ws.addEventListener('message', messageHandler);
|
||||
setTimeout(() => {
|
||||
if (ws && ws.readyState === WebSocket.OPEN) {
|
||||
ws.removeEventListener('message', messageHandler);
|
||||
if (state.serverStatus !== 'running') {
|
||||
updateNotification(notification, 'Failed to start server. Please try again.', 'error', key);
|
||||
toggleSections(state.serverStatus);
|
||||
}
|
||||
}
|
||||
}, 30000);
|
||||
} else {
|
||||
updateNotification(notification, 'Not connected to server. Please log in.', 'error', key);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Start server error:', error);
|
||||
showNotification(`Failed to start server: ${error.message}`, 'error', 'start-error');
|
||||
console.error('Start server error:', error);
|
||||
showNotification(`Failed to start server: ${error.message}`, 'error', 'start-error');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
document.getElementById('stopBtn').addEventListener('click', async () => {
|
||||
try {
|
||||
@@ -2065,6 +2265,8 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
elements.updateModsBtn.addEventListener('click', updateMods);
|
||||
|
||||
elements.closeUpdateModsBtn.addEventListener('click', () => {
|
||||
@@ -2177,4 +2379,42 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
showLoginPage();
|
||||
}
|
||||
window.showNotification = showNotification;
|
||||
|
||||
// Add this at the end of the document.addEventListener('DOMContentLoaded', ...) block
|
||||
function applyResponsiveStyles() {
|
||||
const sections = document.querySelectorAll('.section-bg');
|
||||
sections.forEach(section => {
|
||||
section.style.padding = window.innerWidth <= 640 ? '1rem' : window.innerWidth <= 768 ? '1.5rem' : '2rem';
|
||||
section.style.maxWidth = '100%';
|
||||
section.style.overflowX = 'hidden';
|
||||
});
|
||||
|
||||
const iframes = document.querySelectorAll('#sftpIframe');
|
||||
iframes.forEach(iframe => {
|
||||
iframe.style.height = window.innerWidth <= 640 ? '300px' : window.innerWidth <= 768 ? '400px' : '650px';
|
||||
iframe.style.minHeight = iframe.style.height;
|
||||
iframe.style.width = '100%';
|
||||
iframe.style.maxWidth = '100%';
|
||||
});
|
||||
|
||||
const canvases = document.querySelectorAll('#memoryMeter, #cpuMeter');
|
||||
canvases.forEach(canvas => {
|
||||
canvas.style.width = window.innerWidth <= 640 ? '80px' : window.innerWidth <= 768 ? '100px' : '150px';
|
||||
canvas.style.height = canvas.style.width;
|
||||
});
|
||||
|
||||
const terminals = document.querySelectorAll('#dockerLogsTerminal');
|
||||
terminals.forEach(terminal => {
|
||||
terminal.style.maxHeight = window.innerWidth <= 640 ? '7rem' : window.innerWidth <= 768 ? '8rem' : '12rem';
|
||||
});
|
||||
|
||||
const consoles = document.querySelectorAll('#consoleOutput');
|
||||
consoles.forEach(console => {
|
||||
console.style.height = window.innerWidth <= 640 ? '7rem' : window.innerWidth <= 768 ? '8rem' : '12rem';
|
||||
});
|
||||
}
|
||||
|
||||
window.addEventListener('resize', applyResponsiveStyles);
|
||||
|
||||
|
||||
});
|
Reference in New Issue
Block a user