1862 lines
67 KiB
JavaScript
1862 lines
67 KiB
JavaScript
/**
|
||
* Main JavaScript file for the Discord Bot Dashboard
|
||
*/
|
||
|
||
// Initialize components when DOM is loaded
|
||
document.addEventListener('DOMContentLoaded', () => {
|
||
console.log('DOM loaded, initializing dashboard...');
|
||
|
||
// Fix for white screen issue - ensure containers are properly initialized
|
||
// This runs before any other initialization to make sure the DOM is in a good state
|
||
const fixWhiteScreenIssue = () => {
|
||
console.log('Running white screen fix...');
|
||
|
||
// Check if auth section exists and is visible
|
||
const authSection = document.getElementById('auth-section');
|
||
const dashboardContainer = document.getElementById('dashboard-container');
|
||
const serverSelectSection = document.getElementById('server-select-section');
|
||
|
||
console.log('Auth section:', authSection ? 'found' : 'not found');
|
||
console.log('Dashboard container:', dashboardContainer ? 'found' : 'not found');
|
||
console.log('Server select section:', serverSelectSection ? 'found' : 'not found');
|
||
|
||
// If we're logged in (check for session cookie) but dashboard is hidden, show it
|
||
const hasDashboardCookie = document.cookie.includes('dashboard_session');
|
||
console.log('Dashboard session cookie present:', hasDashboardCookie);
|
||
|
||
if (hasDashboardCookie) {
|
||
console.log('Session cookie found, ensuring dashboard is visible');
|
||
if (authSection) authSection.style.display = 'none';
|
||
if (dashboardContainer) dashboardContainer.style.display = 'block';
|
||
if (serverSelectSection) serverSelectSection.style.display = 'block';
|
||
} else {
|
||
console.log('No session cookie found, showing auth section');
|
||
if (authSection) authSection.style.display = 'block';
|
||
if (dashboardContainer) dashboardContainer.style.display = 'none';
|
||
}
|
||
};
|
||
|
||
// Run the fix immediately
|
||
fixWhiteScreenIssue();
|
||
|
||
// Initialize modals
|
||
Modal.init();
|
||
|
||
// Initialize sidebar toggle
|
||
initSidebar();
|
||
|
||
// Initialize authentication
|
||
initAuth();
|
||
|
||
// Initialize tabs
|
||
initTabs();
|
||
|
||
// Initialize dropdowns
|
||
initDropdowns();
|
||
|
||
// Store selected guild ID globally (using localStorage)
|
||
window.selectedGuildId = localStorage.getItem('selectedGuildId');
|
||
window.currentSettingsGuildId = null; // Track which guild's settings are loaded
|
||
|
||
// If we have a selected guild ID but the dashboard is still not visible, force it
|
||
if (window.selectedGuildId && document.getElementById('dashboard-container').style.display === 'none') {
|
||
console.log('Selected guild ID found but dashboard not visible, forcing visibility');
|
||
document.getElementById('dashboard-container').style.display = 'block';
|
||
// Try to show the appropriate section
|
||
showSection('server-settings');
|
||
}
|
||
});
|
||
|
||
/**
|
||
* Initialize sidebar functionality
|
||
*/
|
||
function initSidebar() {
|
||
const sidebarToggle = document.getElementById('sidebar-toggle');
|
||
const sidebar = document.getElementById('sidebar');
|
||
|
||
if (sidebarToggle && sidebar) {
|
||
sidebarToggle.addEventListener('click', () => {
|
||
sidebar.classList.toggle('show');
|
||
});
|
||
|
||
// Close sidebar when clicking outside on mobile
|
||
document.addEventListener('click', (event) => {
|
||
if (window.innerWidth <= 768 &&
|
||
sidebar.classList.contains('show') &&
|
||
!sidebar.contains(event.target) &&
|
||
event.target !== sidebarToggle) {
|
||
sidebar.classList.remove('show');
|
||
}
|
||
});
|
||
}
|
||
|
||
// Set active nav item based on current page
|
||
const currentPath = window.location.pathname;
|
||
document.querySelectorAll('.nav-item').forEach(item => {
|
||
const href = item.getAttribute('href');
|
||
if (href && currentPath.includes(href)) {
|
||
item.classList.add('active');
|
||
}
|
||
|
||
// Add click event to nav items
|
||
item.addEventListener('click', (event) => {
|
||
// Prevent default only if it's a section link
|
||
if (href && href.startsWith('#')) {
|
||
event.preventDefault();
|
||
|
||
// Get the section ID from the href (remove the # symbol)
|
||
const sectionId = href.substring(1);
|
||
|
||
// Show the section
|
||
showSection(sectionId);
|
||
|
||
// Close sidebar on mobile
|
||
if (window.innerWidth <= 768 && sidebar) {
|
||
sidebar.classList.remove('show');
|
||
}
|
||
}
|
||
});
|
||
});
|
||
}
|
||
|
||
/**
|
||
* Initialize authentication
|
||
*/
|
||
function initAuth() {
|
||
const loginButton = document.getElementById('login-button');
|
||
const logoutButton = document.getElementById('logout-button');
|
||
const authSection = document.getElementById('auth-section');
|
||
const dashboardSection = document.getElementById('dashboard-container');
|
||
const serverSelectSection = document.getElementById('server-select-section');
|
||
|
||
console.log('Initializing authentication...');
|
||
console.log('Auth section:', authSection ? 'found' : 'not found');
|
||
console.log('Dashboard section:', dashboardSection ? 'found' : 'not found');
|
||
console.log('Server select section:', serverSelectSection ? 'found' : 'not found');
|
||
|
||
// Check authentication status
|
||
checkAuthStatus();
|
||
|
||
// Login button event
|
||
if (loginButton) {
|
||
loginButton.addEventListener('click', () => {
|
||
// Show loading state
|
||
loginButton.disabled = true;
|
||
loginButton.classList.add('btn-loading');
|
||
|
||
// Redirect to login page
|
||
window.location.href = '/dashboard/api/auth/login';
|
||
});
|
||
}
|
||
|
||
// Logout button event
|
||
if (logoutButton) {
|
||
logoutButton.addEventListener('click', () => {
|
||
// Show loading state
|
||
logoutButton.disabled = true;
|
||
logoutButton.classList.add('btn-loading');
|
||
|
||
// Clear session
|
||
fetch('/dashboard/api/auth/logout', {
|
||
method: 'POST',
|
||
credentials: 'same-origin' // Important for cookies
|
||
})
|
||
.then(() => {
|
||
// Redirect to login page
|
||
Toast.success('Logged out successfully');
|
||
setTimeout(() => {
|
||
window.location.reload();
|
||
}, 1000);
|
||
})
|
||
.catch(error => {
|
||
console.error('Logout error:', error);
|
||
Toast.error('Failed to logout. Please try again.');
|
||
logoutButton.disabled = false;
|
||
logoutButton.classList.remove('btn-loading');
|
||
});
|
||
});
|
||
}
|
||
|
||
/**
|
||
* Check if user is authenticated
|
||
*/
|
||
function checkAuthStatus() {
|
||
console.log('Checking authentication status...');
|
||
|
||
// Show loading indicator
|
||
const loadingContainer = document.createElement('div');
|
||
loadingContainer.className = 'loading-container';
|
||
loadingContainer.innerHTML = '<div class="loading-spinner"></div>';
|
||
loadingContainer.style.position = 'fixed';
|
||
loadingContainer.style.top = '50%';
|
||
loadingContainer.style.left = '50%';
|
||
loadingContainer.style.transform = 'translate(-50%, -50%)';
|
||
document.body.appendChild(loadingContainer);
|
||
|
||
fetch('/dashboard/api/auth/status', {
|
||
credentials: 'same-origin' // Important for cookies
|
||
})
|
||
.then(response => {
|
||
console.log('Auth status response:', response.status);
|
||
if (!response.ok) {
|
||
throw new Error(`Status check failed: ${response.status}`);
|
||
}
|
||
return response.json();
|
||
})
|
||
.then(data => {
|
||
console.log('Auth status data:', data);
|
||
|
||
if (data.authenticated) {
|
||
console.log('User is authenticated, showing dashboard');
|
||
|
||
// User is authenticated, show dashboard
|
||
if (authSection) {
|
||
console.log('Hiding auth section');
|
||
authSection.style.display = 'none';
|
||
} else {
|
||
console.warn('Auth section not found');
|
||
}
|
||
|
||
if (dashboardSection) {
|
||
console.log('Showing dashboard section');
|
||
dashboardSection.style.display = 'block';
|
||
} else {
|
||
console.warn('Dashboard section not found');
|
||
}
|
||
|
||
// If user data is included in the response, use it
|
||
if (data.user) {
|
||
console.log('User data found in response, updating display');
|
||
updateUserDisplay(data.user);
|
||
} else {
|
||
console.log('No user data in response, loading separately');
|
||
// Otherwise load user info separately
|
||
loadUserInfo();
|
||
}
|
||
|
||
// Show server selection screen first
|
||
console.log('Showing server selection screen');
|
||
showServerSelection();
|
||
|
||
// Force visibility of server selection section
|
||
const serverSelectSection = document.getElementById('server-select-section');
|
||
if (serverSelectSection) {
|
||
console.log('Forcing server select section to be visible');
|
||
serverSelectSection.style.display = 'block';
|
||
} else {
|
||
console.warn('Server select section not found');
|
||
}
|
||
} else {
|
||
console.log('User is not authenticated, showing login');
|
||
|
||
// User is not authenticated, show login
|
||
if (authSection) {
|
||
authSection.style.display = 'block';
|
||
} else {
|
||
console.warn('Auth section not found');
|
||
}
|
||
|
||
if (dashboardSection) {
|
||
dashboardSection.style.display = 'none';
|
||
} else {
|
||
console.warn('Dashboard section not found');
|
||
}
|
||
|
||
// Show message if provided
|
||
if (data.message) {
|
||
console.log('Auth message:', data.message);
|
||
}
|
||
}
|
||
})
|
||
.catch(error => {
|
||
console.error('Auth check error:', error);
|
||
// Assume not authenticated on error
|
||
if (authSection) authSection.style.display = 'block';
|
||
if (dashboardSection) dashboardSection.style.display = 'none';
|
||
|
||
// Show error toast
|
||
Toast.error('Failed to check authentication status. Please try again.');
|
||
})
|
||
.finally(() => {
|
||
// Remove loading indicator
|
||
document.body.removeChild(loadingContainer);
|
||
|
||
// Fallback mechanism - if we have a dashboard cookie but nothing is visible, force the dashboard to show
|
||
const hasDashboardCookie = document.cookie.includes('dashboard_session');
|
||
const authSection = document.getElementById('auth-section');
|
||
const dashboardContainer = document.getElementById('dashboard-container');
|
||
const serverSelectSection = document.getElementById('server-select-section');
|
||
|
||
if (hasDashboardCookie) {
|
||
console.log('Fallback check: Session cookie found, ensuring dashboard is visible');
|
||
|
||
// Check if all sections are hidden, which would result in a white screen
|
||
const allHidden =
|
||
(!authSection || authSection.style.display === 'none') &&
|
||
(!dashboardContainer || dashboardContainer.style.display === 'none') &&
|
||
(!serverSelectSection || serverSelectSection.style.display === 'none');
|
||
|
||
if (allHidden) {
|
||
console.log('CRITICAL: All sections are hidden, forcing dashboard and server selection to be visible');
|
||
if (dashboardContainer) dashboardContainer.style.display = 'block';
|
||
if (serverSelectSection) serverSelectSection.style.display = 'block';
|
||
|
||
// Force load user guilds
|
||
loadUserGuilds();
|
||
}
|
||
}
|
||
});
|
||
}
|
||
|
||
/**
|
||
* Load user information
|
||
*/
|
||
function loadUserInfo() {
|
||
fetch('/dashboard/api/auth/user', {
|
||
credentials: 'same-origin' // Important for cookies
|
||
})
|
||
.then(response => {
|
||
if (!response.ok) {
|
||
if (response.status === 401) {
|
||
// User is not authenticated, show login
|
||
if (authSection) authSection.style.display = 'block';
|
||
if (dashboardSection) dashboardSection.style.display = 'none';
|
||
throw new Error('Not authenticated');
|
||
}
|
||
throw new Error(`Failed to load user info: ${response.status}`);
|
||
}
|
||
return response.json();
|
||
})
|
||
.then(user => {
|
||
updateUserDisplay(user);
|
||
})
|
||
.catch(error => {
|
||
console.error('Error loading user info:', error);
|
||
if (error.message !== 'Not authenticated') {
|
||
Toast.error('Failed to load user information');
|
||
}
|
||
});
|
||
}
|
||
|
||
/**
|
||
* Update user display with user data
|
||
* @param {Object} user - User data object
|
||
*/
|
||
function updateUserDisplay(user) {
|
||
// Update username display
|
||
const usernameSpan = document.getElementById('username');
|
||
if (usernameSpan) {
|
||
usernameSpan.textContent = user.username;
|
||
}
|
||
|
||
// Update avatar if available
|
||
const userAvatar = document.getElementById('user-avatar');
|
||
if (userAvatar) {
|
||
if (user.avatar) {
|
||
userAvatar.style.backgroundImage = `url(https://cdn.discordapp.com/avatars/${user.id}/${user.avatar}.png)`;
|
||
userAvatar.textContent = '';
|
||
} else {
|
||
// Set initials as fallback
|
||
userAvatar.textContent = user.username.substring(0, 1).toUpperCase();
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Initialize tab functionality
|
||
*/
|
||
function initTabs() {
|
||
document.querySelectorAll('.tabs').forEach(tabContainer => {
|
||
const tabs = tabContainer.querySelectorAll('.tab');
|
||
|
||
tabs.forEach(tab => {
|
||
tab.addEventListener('click', () => {
|
||
// Get target content ID
|
||
const target = tab.getAttribute('data-target');
|
||
if (!target) return;
|
||
|
||
// Remove active class from all tabs
|
||
tabs.forEach(t => t.classList.remove('active'));
|
||
|
||
// Add active class to clicked tab
|
||
tab.classList.add('active');
|
||
|
||
// Hide all tab content
|
||
const tabContents = document.querySelectorAll('.tab-content');
|
||
tabContents.forEach(content => {
|
||
content.classList.remove('active');
|
||
});
|
||
|
||
// Show target content
|
||
const targetContent = document.getElementById(target);
|
||
if (targetContent) {
|
||
targetContent.classList.add('active');
|
||
}
|
||
});
|
||
});
|
||
});
|
||
}
|
||
|
||
/**
|
||
* Show a specific section of the dashboard
|
||
* @param {string} sectionId - The ID of the section to show (e.g., 'server-settings')
|
||
*/
|
||
function showSection(sectionId) {
|
||
console.log(`Attempting to show section: ${sectionId}`);
|
||
|
||
// Check if a server is selected before showing any section other than server-select
|
||
if (!window.selectedGuildId && sectionId !== 'server-select') {
|
||
console.log('No server selected, redirecting to server selection.');
|
||
showServerSelection(); // Redirect to server selection
|
||
return; // Stop further execution
|
||
}
|
||
|
||
// Hide all specific dashboard sections first
|
||
document.querySelectorAll('.dashboard-section').forEach(section => {
|
||
section.style.display = 'none';
|
||
});
|
||
// Also hide the server selection section if it exists and we are showing another section
|
||
const serverSelectSection = document.getElementById('server-select-section');
|
||
if (serverSelectSection && sectionId !== 'server-select') {
|
||
serverSelectSection.style.display = 'none';
|
||
}
|
||
|
||
// Remove active class from all nav items
|
||
document.querySelectorAll('.nav-item').forEach(item => {
|
||
item.classList.remove('active');
|
||
});
|
||
|
||
// Show the selected section
|
||
const sectionElement = document.getElementById(`${sectionId}-section`);
|
||
if (sectionElement) {
|
||
sectionElement.style.display = 'block';
|
||
console.log(`Successfully displayed section: ${sectionId}-section`);
|
||
|
||
// Add active class to the corresponding nav item
|
||
const navItem = document.querySelector(`.nav-item[data-section="${sectionId}-section"]`);
|
||
if (navItem) {
|
||
navItem.classList.add('active');
|
||
}
|
||
|
||
// Load data for the specific section if needed and a guild is selected
|
||
if (window.selectedGuildId) {
|
||
// Load AI settings if needed (assuming it's guild-specific now)
|
||
if (sectionId === 'ai-settings' && typeof loadAiSettings === 'function') {
|
||
// Check if already loaded for this guild to prevent redundant calls
|
||
// This requires loadAiSettings to track its loaded state per guild or be idempotent
|
||
console.log(`Loading AI settings for guild ${window.selectedGuildId}`);
|
||
loadAiSettings(window.selectedGuildId); // Pass guildId
|
||
}
|
||
|
||
// Load theme settings if needed (assuming global/user-specific)
|
||
if (sectionId === 'theme-settings' && typeof loadThemeSettings === 'function' && typeof themeSettingsLoaded !== 'undefined' && !themeSettingsLoaded) {
|
||
console.log("Loading theme settings");
|
||
loadThemeSettings();
|
||
// themeSettingsLoaded = true; // Assuming loadThemeSettings handles this
|
||
}
|
||
|
||
// Load custom bot section if needed
|
||
if (sectionId === 'custom-bot') {
|
||
console.log("Loading custom bot section");
|
||
loadCustomBotSection();
|
||
}
|
||
|
||
// Load cog management if needed
|
||
if (sectionId === 'cog-management' && typeof loadCogManagementData === 'function') {
|
||
// Check if already loaded for this guild
|
||
if (!window.cogManagementLoadedGuild || window.cogManagementLoadedGuild !== window.selectedGuildId) {
|
||
console.log(`Loading Cog Management data for guild ${window.selectedGuildId}`);
|
||
loadCogManagementData(window.selectedGuildId); // Pass guildId
|
||
window.cogManagementLoadedGuild = window.selectedGuildId; // Track loaded guild
|
||
} else {
|
||
console.log(`Cog Management data for guild ${window.selectedGuildId} already loaded.`);
|
||
}
|
||
}
|
||
|
||
// Load command customization if needed
|
||
if (sectionId === 'command-customization' && typeof loadCommandCustomizationData === 'function') {
|
||
// Check if already loaded for this guild
|
||
if (!window.commandCustomizationLoadedGuild || window.commandCustomizationLoadedGuild !== window.selectedGuildId) {
|
||
console.log(`Loading Command Customization data for guild ${window.selectedGuildId}`);
|
||
loadCommandCustomizationData(window.selectedGuildId); // Pass guildId
|
||
window.commandCustomizationLoadedGuild = window.selectedGuildId; // Track loaded guild
|
||
} else {
|
||
console.log(`Command Customization data for guild ${window.selectedGuildId} already loaded.`);
|
||
}
|
||
}
|
||
|
||
// Load general server settings (prefix, welcome/leave, modules, permissions) if viewing relevant sections
|
||
if (['server-settings', 'welcome-module', 'modules-settings', 'permissions-settings'].includes(sectionId)) {
|
||
// loadGuildSettings already prevents redundant loads using window.currentSettingsGuildId
|
||
console.log(`Loading general guild settings for guild ${window.selectedGuildId}`);
|
||
loadGuildSettings(window.selectedGuildId);
|
||
}
|
||
}
|
||
} else {
|
||
console.warn(`Section with ID ${sectionId}-section not found.`);
|
||
// Optionally show a default section or an error message
|
||
// If no section found and a guild is selected, maybe default to server-settings?
|
||
if (window.selectedGuildId) {
|
||
console.log("Defaulting to server-settings section.");
|
||
showSection('server-settings');
|
||
} else {
|
||
// If no guild selected either, go back to server selection
|
||
showServerSelection();
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Initialize dropdown functionality
|
||
*/
|
||
function initDropdowns() {
|
||
document.querySelectorAll('.dropdown-toggle').forEach(toggle => {
|
||
toggle.addEventListener('click', (event) => {
|
||
event.preventDefault();
|
||
event.stopPropagation();
|
||
|
||
const dropdown = toggle.closest('.dropdown');
|
||
const menu = dropdown.querySelector('.dropdown-menu');
|
||
|
||
// Close all other dropdowns
|
||
document.querySelectorAll('.dropdown-menu.show').forEach(openMenu => {
|
||
if (openMenu !== menu) {
|
||
openMenu.classList.remove('show');
|
||
}
|
||
});
|
||
|
||
// Toggle this dropdown
|
||
menu.classList.toggle('show');
|
||
});
|
||
});
|
||
|
||
// Close dropdowns when clicking outside
|
||
document.addEventListener('click', (event) => {
|
||
if (!event.target.closest('.dropdown')) {
|
||
document.querySelectorAll('.dropdown-menu.show').forEach(menu => {
|
||
menu.classList.remove('show');
|
||
});
|
||
}
|
||
});
|
||
}
|
||
|
||
/**
|
||
* Show the server selection screen
|
||
*/
|
||
function showServerSelection() {
|
||
console.log('Showing server selection screen...');
|
||
const dashboardContainer = document.getElementById('dashboard-container');
|
||
const serverSelectSection = document.getElementById('server-select-section'); // Assuming this ID exists in index.html
|
||
|
||
if (!serverSelectSection) {
|
||
console.error('Server selection section not found!');
|
||
// Create a visible error message for the user
|
||
const errorDiv = document.createElement('div');
|
||
errorDiv.style.position = 'fixed';
|
||
errorDiv.style.top = '50%';
|
||
errorDiv.style.left = '50%';
|
||
errorDiv.style.transform = 'translate(-50%, -50%)';
|
||
errorDiv.style.backgroundColor = '#f8d7da';
|
||
errorDiv.style.color = '#721c24';
|
||
errorDiv.style.padding = '20px';
|
||
errorDiv.style.borderRadius = '5px';
|
||
errorDiv.style.boxShadow = '0 0 10px rgba(0,0,0,0.1)';
|
||
errorDiv.style.zIndex = '9999';
|
||
errorDiv.innerHTML = `
|
||
<h3>Dashboard Error</h3>
|
||
<p>The server selection section could not be found. This is likely a bug in the dashboard.</p>
|
||
<p>Please try refreshing the page. If the problem persists, contact the administrator.</p>
|
||
`;
|
||
document.body.appendChild(errorDiv);
|
||
|
||
// Try to show the dashboard anyway as a fallback
|
||
if (dashboardContainer) dashboardContainer.style.display = 'block';
|
||
return;
|
||
}
|
||
|
||
console.log('Server selection section found, displaying it');
|
||
|
||
// Make sure dashboard container is visible first
|
||
if (dashboardContainer) {
|
||
console.log('Making dashboard container visible');
|
||
dashboardContainer.style.display = 'block';
|
||
} else {
|
||
console.error('Dashboard container not found!');
|
||
}
|
||
|
||
// Show server selection section
|
||
console.log('Making server selection section visible');
|
||
serverSelectSection.style.display = 'block';
|
||
|
||
// Hide all other specific dashboard sections just in case
|
||
document.querySelectorAll('.dashboard-section').forEach(section => {
|
||
if (section.id !== 'server-select-section') {
|
||
section.style.display = 'none';
|
||
}
|
||
});
|
||
|
||
// Load the list of guilds for the user
|
||
console.log('Loading user guilds...');
|
||
loadUserGuilds();
|
||
}
|
||
|
||
/**
|
||
* Load guilds the user has admin access to for the selection screen
|
||
*/
|
||
function loadUserGuilds() {
|
||
console.log('Loading user guilds...');
|
||
const serverListContainer = document.getElementById('server-list-container'); // Assuming this ID exists within server-select-section
|
||
if (!serverListContainer) {
|
||
console.error('Server list container not found!');
|
||
|
||
// Try to find the server-select-section and add the container
|
||
const serverSelectSection = document.getElementById('server-select-section');
|
||
if (serverSelectSection) {
|
||
console.log('Server select section found, creating server list container');
|
||
const newContainer = document.createElement('div');
|
||
newContainer.id = 'server-list-container';
|
||
newContainer.className = 'server-list-container';
|
||
serverSelectSection.appendChild(newContainer);
|
||
|
||
// Now try again with the new container
|
||
loadUserGuilds();
|
||
return;
|
||
} else {
|
||
console.error('Server select section also not found, cannot proceed');
|
||
return;
|
||
}
|
||
}
|
||
|
||
console.log('Server list container found, showing loading state');
|
||
serverListContainer.innerHTML = '<div class="loading-spinner"></div><p>Loading your servers...</p>'; // Show loading state
|
||
|
||
console.log('Fetching user guilds from API...');
|
||
API.get('/dashboard/api/user-guilds')
|
||
.then(guilds => {
|
||
console.log('User guilds received:', guilds);
|
||
serverListContainer.innerHTML = ''; // Clear loading state
|
||
|
||
if (!guilds || guilds.length === 0) {
|
||
console.log('No guilds found');
|
||
serverListContainer.innerHTML = '<div class="alert alert-info">No servers found where you have admin permissions.</div>';
|
||
return;
|
||
}
|
||
|
||
console.log(`Found ${guilds.length} guilds, rendering them`);
|
||
guilds.forEach(guild => {
|
||
const guildElement = document.createElement('div');
|
||
guildElement.className = 'server-select-item card'; // Add card class for styling
|
||
guildElement.style.cursor = 'pointer';
|
||
guildElement.dataset.guildId = guild.id;
|
||
|
||
// Check if we have an icon URL
|
||
if (guild.icon_url) {
|
||
// Create an image element for the server icon
|
||
const iconElement = document.createElement('img');
|
||
iconElement.className = 'server-icon';
|
||
iconElement.src = guild.icon_url;
|
||
iconElement.alt = `${guild.name} icon`;
|
||
iconElement.width = 50;
|
||
iconElement.height = 50;
|
||
|
||
// Add error handling for icon loading
|
||
iconElement.onerror = function() {
|
||
console.log(`Failed to load icon for server ${guild.name}, using text fallback`);
|
||
// Replace the img with a text-based fallback
|
||
const fallbackIcon = createTextFallbackIcon(guild.name);
|
||
this.parentNode.replaceChild(fallbackIcon, this);
|
||
};
|
||
|
||
guildElement.appendChild(iconElement);
|
||
} else {
|
||
// No icon URL provided, use text-based fallback immediately
|
||
const fallbackIcon = createTextFallbackIcon(guild.name);
|
||
guildElement.appendChild(fallbackIcon);
|
||
}
|
||
|
||
// Helper function to create a text-based fallback icon
|
||
function createTextFallbackIcon(serverName) {
|
||
const fallbackDiv = document.createElement('div');
|
||
fallbackDiv.className = 'server-icon-fallback';
|
||
fallbackDiv.style.width = '50px';
|
||
fallbackDiv.style.height = '50px';
|
||
fallbackDiv.style.backgroundColor = '#5865F2'; // Discord blue
|
||
fallbackDiv.style.borderRadius = '50%';
|
||
fallbackDiv.style.display = 'flex';
|
||
fallbackDiv.style.alignItems = 'center';
|
||
fallbackDiv.style.justifyContent = 'center';
|
||
fallbackDiv.style.color = 'white';
|
||
fallbackDiv.style.fontWeight = 'bold';
|
||
fallbackDiv.style.fontSize = '18px';
|
||
|
||
// Get the first letter of each word in the server name
|
||
const initials = serverName
|
||
.split(' ')
|
||
.map(word => word.charAt(0))
|
||
.join('')
|
||
.substring(0, 2)
|
||
.toUpperCase();
|
||
|
||
fallbackDiv.textContent = initials;
|
||
|
||
return fallbackDiv;
|
||
}
|
||
|
||
const nameElement = document.createElement('span');
|
||
nameElement.className = 'server-name';
|
||
nameElement.textContent = guild.name;
|
||
|
||
// Icon is already appended in the icon creation logic above
|
||
guildElement.appendChild(nameElement);
|
||
|
||
guildElement.addEventListener('click', () => {
|
||
console.log(`Server selected: ${guild.name} (${guild.id})`);
|
||
// Store selected guild ID
|
||
localStorage.setItem('selectedGuildId', guild.id);
|
||
window.selectedGuildId = guild.id;
|
||
window.currentSettingsGuildId = null; // Reset loaded settings tracker
|
||
|
||
// Hide server selection and show dashboard
|
||
const serverSelectSection = document.getElementById('server-select-section');
|
||
const dashboardContainer = document.getElementById('dashboard-container');
|
||
if (serverSelectSection) serverSelectSection.style.display = 'none';
|
||
if (dashboardContainer) dashboardContainer.style.display = 'block';
|
||
|
||
// Load data for the selected guild and show the default section
|
||
loadDashboardData(); // Now loads data for the selected guild
|
||
showSection('server-settings'); // Show the server settings section by default
|
||
});
|
||
|
||
serverListContainer.appendChild(guildElement);
|
||
});
|
||
})
|
||
.catch(error => {
|
||
console.error('Error loading user guilds:', error);
|
||
|
||
// Create a more detailed error message with troubleshooting steps
|
||
let errorMessage = '<div class="alert alert-danger">';
|
||
errorMessage += '<h4>Error Loading Servers</h4>';
|
||
errorMessage += '<p>We encountered a problem while trying to load your servers. This could be due to:</p>';
|
||
errorMessage += '<ul>';
|
||
errorMessage += '<li>Database connection issues</li>';
|
||
errorMessage += '<li>Discord API rate limiting</li>';
|
||
errorMessage += '<li>Missing bot configuration</li>';
|
||
errorMessage += '</ul>';
|
||
errorMessage += '<p>Troubleshooting steps:</p>';
|
||
errorMessage += '<ol>';
|
||
errorMessage += '<li>Refresh the page and try again</li>';
|
||
errorMessage += '<li>Check that the bot is properly configured with database access</li>';
|
||
errorMessage += '<li>Ensure the Discord bot token is properly set</li>';
|
||
errorMessage += '</ol>';
|
||
|
||
// Add debug information to help troubleshoot
|
||
errorMessage += '<div class="debug-info" style="margin-top: 15px; padding: 10px; background: #f8f9fa; border-radius: 5px;">';
|
||
errorMessage += '<h5>Debug Information</h5>';
|
||
errorMessage += `<p>Error: ${error.message || 'Unknown error'}</p>`;
|
||
errorMessage += `<p>Status: ${error.status || 'N/A'}</p>`;
|
||
errorMessage += `<p>Time: ${new Date().toISOString()}</p>`;
|
||
errorMessage += '</div>';
|
||
|
||
errorMessage += '<button id="retry-load-guilds" class="btn btn-primary">Retry</button>';
|
||
errorMessage += '</div>';
|
||
|
||
serverListContainer.innerHTML = errorMessage;
|
||
|
||
// Add event listener to the retry button
|
||
document.getElementById('retry-load-guilds').addEventListener('click', () => {
|
||
console.log('Retrying guild load...');
|
||
loadUserGuilds(); // Retry loading guilds
|
||
});
|
||
|
||
// Try to make the server selection section visible again in case it was hidden
|
||
const serverSelectSection = document.getElementById('server-select-section');
|
||
if (serverSelectSection) {
|
||
console.log('Ensuring server select section is visible');
|
||
serverSelectSection.style.display = 'block';
|
||
}
|
||
|
||
// Try to make the dashboard container visible in case it was hidden
|
||
const dashboardContainer = document.getElementById('dashboard-container');
|
||
if (dashboardContainer) {
|
||
console.log('Ensuring dashboard container is visible');
|
||
dashboardContainer.style.display = 'block';
|
||
}
|
||
|
||
Toast.error('Failed to load your servers. See details on screen.');
|
||
});
|
||
}
|
||
|
||
|
||
/**
|
||
* Load initial dashboard data *after* a server has been selected
|
||
*/
|
||
function loadDashboardData() {
|
||
if (!window.selectedGuildId) {
|
||
console.warn('loadDashboardData called without a selected guild ID.');
|
||
showServerSelection(); // Redirect back to selection if no guild is selected
|
||
return;
|
||
}
|
||
console.log(`Loading dashboard data for guild: ${window.selectedGuildId}`);
|
||
// No longer need to load the general guild list here.
|
||
// Specific sections will load their data via showSection or dedicated functions.
|
||
// We might load some initial settings common to multiple sections here if needed.
|
||
// For now, let's ensure the basic settings are loaded if the user lands on a relevant page.
|
||
loadGuildSettings(window.selectedGuildId);
|
||
}
|
||
|
||
/**
|
||
* Show a global error message about missing bot token
|
||
*/
|
||
function showBotTokenMissingError() {
|
||
// Create error banner
|
||
const errorBanner = document.createElement('div');
|
||
errorBanner.className = 'error-message';
|
||
errorBanner.style.margin = '0';
|
||
errorBanner.style.borderRadius = '0';
|
||
errorBanner.style.position = 'sticky';
|
||
errorBanner.style.top = '0';
|
||
errorBanner.style.zIndex = '1000';
|
||
|
||
errorBanner.innerHTML = `
|
||
<div style="display: flex; align-items: center; justify-content: space-between;">
|
||
<div>
|
||
<strong>Configuration Error:</strong> Discord Bot Token is not configured.
|
||
<p>Some features like channel selection, role management, and command permissions will not work.</p>
|
||
<p>Please set the <code>DISCORD_BOT_TOKEN</code> environment variable in your .env file.</p>
|
||
</div>
|
||
<button class="btn btn-sm" id="close-error-banner" style="background: transparent; border: none;">×</button>
|
||
</div>
|
||
`;
|
||
|
||
// Add to the top of the page
|
||
document.body.insertBefore(errorBanner, document.body.firstChild);
|
||
|
||
// Add close button functionality
|
||
document.getElementById('close-error-banner').addEventListener('click', () => {
|
||
errorBanner.remove();
|
||
});
|
||
}
|
||
|
||
/**
|
||
* Load guilds for the *original* server select dropdown (now potentially redundant)
|
||
* Kept for reference or potential future use, but not called by default flow anymore.
|
||
*/
|
||
function loadGuilds() {
|
||
console.warn("loadGuilds function called - this might be redundant now.");
|
||
const guildSelect = document.getElementById('guild-select');
|
||
if (!guildSelect) return;
|
||
|
||
// Show loading state
|
||
guildSelect.disabled = true;
|
||
guildSelect.innerHTML = '<option value="">Loading servers...</option>';
|
||
|
||
// Fetch guilds from API
|
||
API.get('/dashboard/api/guilds')
|
||
.then(guilds => {
|
||
// Clear loading state
|
||
guildSelect.innerHTML = '<option value="">--Please choose a server--</option>';
|
||
|
||
// Add guilds to select
|
||
guilds.forEach(guild => {
|
||
const option = document.createElement('option');
|
||
option.value = guild.id;
|
||
option.textContent = guild.name;
|
||
guildSelect.appendChild(option);
|
||
});
|
||
|
||
// Enable select
|
||
guildSelect.disabled = false;
|
||
|
||
// Add change event
|
||
guildSelect.addEventListener('change', () => {
|
||
const guildId = guildSelect.value;
|
||
if (guildId) {
|
||
loadGuildSettings(guildId);
|
||
} else {
|
||
// Hide settings form if no guild selected
|
||
const settingsForm = document.getElementById('settings-form');
|
||
if (settingsForm) {
|
||
settingsForm.style.display = 'none';
|
||
}
|
||
}
|
||
});
|
||
})
|
||
.catch(error => {
|
||
console.error('Error loading guilds:', error);
|
||
guildSelect.innerHTML = '<option value="">Error loading servers</option>';
|
||
guildSelect.disabled = false;
|
||
Toast.error('Failed to load servers. Please try again.');
|
||
});
|
||
}
|
||
|
||
/**
|
||
* Load settings for the currently selected guild (window.selectedGuildId)
|
||
* @param {string} guildId - The guild ID to load settings for
|
||
*/
|
||
function loadGuildSettings(guildId) {
|
||
// Prevent reloading if settings for this guild are already loaded
|
||
if (window.currentSettingsGuildId === guildId) {
|
||
console.log(`Settings for guild ${guildId} already loaded.`);
|
||
// Ensure the forms are visible if navigating back
|
||
const settingsForm = document.getElementById('settings-form');
|
||
const welcomeSettingsForm = document.getElementById('welcome-settings-form');
|
||
const modulesSettingsForm = document.getElementById('modules-settings-form');
|
||
const permissionsSettingsForm = document.getElementById('permissions-settings-form');
|
||
if (settingsForm) settingsForm.style.display = 'block';
|
||
if (welcomeSettingsForm) welcomeSettingsForm.style.display = 'block';
|
||
if (modulesSettingsForm) modulesSettingsForm.style.display = 'block';
|
||
if (permissionsSettingsForm) permissionsSettingsForm.style.display = 'block';
|
||
return;
|
||
}
|
||
console.log(`Loading settings for guild: ${guildId}`);
|
||
|
||
const settingsForm = document.getElementById('settings-form');
|
||
const welcomeSettingsForm = document.getElementById('welcome-settings-form');
|
||
const modulesSettingsForm = document.getElementById('modules-settings-form');
|
||
const permissionsSettingsForm = document.getElementById('permissions-settings-form');
|
||
|
||
if (!settingsForm) return;
|
||
|
||
// Show loading state
|
||
const loadingContainer = document.createElement('div');
|
||
loadingContainer.className = 'loading-container';
|
||
loadingContainer.innerHTML = '<div class="loading-spinner"></div><p>Loading server settings...</p>';
|
||
loadingContainer.style.textAlign = 'center';
|
||
loadingContainer.style.padding = '2rem';
|
||
|
||
// Hide all forms
|
||
settingsForm.style.display = 'none';
|
||
if (welcomeSettingsForm) welcomeSettingsForm.style.display = 'none';
|
||
if (modulesSettingsForm) modulesSettingsForm.style.display = 'none';
|
||
if (permissionsSettingsForm) permissionsSettingsForm.style.display = 'none';
|
||
|
||
// Add loading indicator to server settings section
|
||
settingsForm.parentNode.insertBefore(loadingContainer, settingsForm);
|
||
|
||
// Fetch guild settings from API
|
||
API.get(`/dashboard/api/guilds/${guildId}/settings`)
|
||
.then(settings => {
|
||
// Remove loading container
|
||
loadingContainer.remove();
|
||
|
||
// Show all settings forms
|
||
settingsForm.style.display = 'block';
|
||
if (welcomeSettingsForm) welcomeSettingsForm.style.display = 'block';
|
||
if (modulesSettingsForm) modulesSettingsForm.style.display = 'block';
|
||
if (permissionsSettingsForm) permissionsSettingsForm.style.display = 'block';
|
||
|
||
// Populate forms with settings
|
||
populateGuildSettings(settings);
|
||
|
||
// Load additional data
|
||
loadGuildChannels(guildId);
|
||
loadGuildRoles(guildId);
|
||
loadGuildCommands(guildId);
|
||
|
||
// Mark settings as loaded for this guild
|
||
window.currentSettingsGuildId = guildId;
|
||
|
||
// Set up event listeners for buttons
|
||
setupSaveSettingsButtons(guildId);
|
||
setupWelcomeLeaveTestButtons(guildId);
|
||
})
|
||
.catch(error => {
|
||
console.error('Error loading guild settings:', error);
|
||
loadingContainer.innerHTML = '<p class="text-danger">Error loading server settings. Please try again.</p>';
|
||
Toast.error('Failed to load server settings. Please try again.');
|
||
});
|
||
}
|
||
|
||
/**
|
||
* Populate guild settings form with data
|
||
* @param {Object} settings - The guild settings
|
||
*/
|
||
function populateGuildSettings(settings) {
|
||
// Prefix
|
||
const prefixInput = document.getElementById('prefix-input');
|
||
if (prefixInput) {
|
||
prefixInput.value = settings.prefix || '!';
|
||
}
|
||
|
||
// Welcome settings
|
||
const welcomeChannel = document.getElementById('welcome-channel');
|
||
const welcomeMessage = document.getElementById('welcome-message');
|
||
|
||
if (welcomeChannel && welcomeMessage) {
|
||
welcomeChannel.value = settings.welcome_channel_id || '';
|
||
welcomeMessage.value = settings.welcome_message || '';
|
||
}
|
||
|
||
// Goodbye settings
|
||
const goodbyeChannel = document.getElementById('goodbye-channel');
|
||
const goodbyeMessage = document.getElementById('goodbye-message');
|
||
|
||
if (goodbyeChannel && goodbyeMessage) {
|
||
goodbyeChannel.value = settings.goodbye_channel_id || '';
|
||
goodbyeMessage.value = settings.goodbye_message || '';
|
||
}
|
||
|
||
// Cogs (modules)
|
||
const cogsList = document.getElementById('cogs-list');
|
||
if (cogsList && settings.enabled_cogs) {
|
||
cogsList.innerHTML = '';
|
||
|
||
Object.entries(settings.enabled_cogs).forEach(([cogName, enabled]) => {
|
||
const cogDiv = document.createElement('div');
|
||
|
||
const checkbox = document.createElement('input');
|
||
checkbox.type = 'checkbox';
|
||
checkbox.id = `cog-${cogName}`;
|
||
checkbox.name = `cog-${cogName}`;
|
||
checkbox.checked = enabled;
|
||
|
||
const label = document.createElement('label');
|
||
label.htmlFor = `cog-${cogName}`;
|
||
label.textContent = cogName;
|
||
|
||
cogDiv.appendChild(checkbox);
|
||
cogDiv.appendChild(label);
|
||
cogsList.appendChild(cogDiv);
|
||
});
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Load channels for a guild
|
||
* @param {string} guildId - The guild ID
|
||
*/
|
||
function loadGuildChannels(guildId) {
|
||
const welcomeChannelSelect = document.getElementById('welcome-channel-select');
|
||
const goodbyeChannelSelect = document.getElementById('goodbye-channel-select');
|
||
|
||
if (!welcomeChannelSelect && !goodbyeChannelSelect) return;
|
||
|
||
// Fetch channels from API
|
||
API.get(`/dashboard/api/guilds/${guildId}/channels`)
|
||
.then(channels => {
|
||
// Filter text channels
|
||
const textChannels = channels.filter(channel => channel.type === 0);
|
||
|
||
// Populate welcome channel select
|
||
if (welcomeChannelSelect) {
|
||
welcomeChannelSelect.innerHTML = '<option value="">-- Select Channel --</option>';
|
||
|
||
textChannels.forEach(channel => {
|
||
const option = document.createElement('option');
|
||
option.value = channel.id;
|
||
option.textContent = `#${channel.name}`;
|
||
welcomeChannelSelect.appendChild(option);
|
||
});
|
||
|
||
// Set current value if available
|
||
const welcomeChannelInput = document.getElementById('welcome-channel');
|
||
if (welcomeChannelInput && welcomeChannelInput.value) {
|
||
welcomeChannelSelect.value = welcomeChannelInput.value;
|
||
}
|
||
|
||
// Add change event
|
||
welcomeChannelSelect.addEventListener('change', () => {
|
||
if (welcomeChannelInput) {
|
||
welcomeChannelInput.value = welcomeChannelSelect.value;
|
||
}
|
||
});
|
||
}
|
||
|
||
// Populate goodbye channel select
|
||
if (goodbyeChannelSelect) {
|
||
goodbyeChannelSelect.innerHTML = '<option value="">-- Select Channel --</option>';
|
||
|
||
textChannels.forEach(channel => {
|
||
const option = document.createElement('option');
|
||
option.value = channel.id;
|
||
option.textContent = `#${channel.name}`;
|
||
goodbyeChannelSelect.appendChild(option);
|
||
});
|
||
|
||
// Set current value if available
|
||
const goodbyeChannelInput = document.getElementById('goodbye-channel');
|
||
if (goodbyeChannelInput && goodbyeChannelInput.value) {
|
||
goodbyeChannelSelect.value = goodbyeChannelInput.value;
|
||
}
|
||
|
||
// Add change event
|
||
goodbyeChannelSelect.addEventListener('change', () => {
|
||
if (goodbyeChannelInput) {
|
||
goodbyeChannelInput.value = goodbyeChannelSelect.value;
|
||
}
|
||
});
|
||
}
|
||
})
|
||
.catch(error => {
|
||
console.error('Error loading channels:', error);
|
||
|
||
// Check for specific error about missing bot token
|
||
if (error.status === 503 && error.message && error.message.includes('Bot token not configured')) {
|
||
// Show global error banner
|
||
showBotTokenMissingError();
|
||
|
||
// Show a more helpful message in the channel selects
|
||
if (welcomeChannelSelect) {
|
||
welcomeChannelSelect.innerHTML = '<option value="">Bot token not configured</option>';
|
||
welcomeChannelSelect.disabled = true;
|
||
}
|
||
if (goodbyeChannelSelect) {
|
||
goodbyeChannelSelect.innerHTML = '<option value="">Bot token not configured</option>';
|
||
goodbyeChannelSelect.disabled = true;
|
||
}
|
||
|
||
// Add a visible error message
|
||
const errorDiv = document.createElement('div');
|
||
errorDiv.className = 'error-message';
|
||
errorDiv.innerHTML = `
|
||
<p>The Discord bot token is not configured. Channel selection is unavailable.</p>
|
||
<p>Please set the <code>DISCORD_BOT_TOKEN</code> environment variable in your .env file.</p>
|
||
`;
|
||
|
||
// Add the error message near the channel selects
|
||
if (welcomeChannelSelect && welcomeChannelSelect.parentNode) {
|
||
welcomeChannelSelect.parentNode.appendChild(errorDiv);
|
||
}
|
||
} else {
|
||
Toast.error('Failed to load channels. Please try again.');
|
||
}
|
||
});
|
||
}
|
||
|
||
/**
|
||
* Load roles for a guild
|
||
* @param {string} guildId - The guild ID
|
||
*/
|
||
function loadGuildRoles(guildId) {
|
||
const roleSelect = document.getElementById('role-select');
|
||
if (!roleSelect) return;
|
||
|
||
// Fetch roles from API
|
||
API.get(`/dashboard/api/guilds/${guildId}/roles`)
|
||
.then(roles => {
|
||
roleSelect.innerHTML = '<option value="">-- Select Role --</option>';
|
||
|
||
roles.forEach(role => {
|
||
const option = document.createElement('option');
|
||
option.value = role.id;
|
||
option.textContent = role.name;
|
||
|
||
// Set color if available
|
||
if (role.color) {
|
||
option.style.color = `#${role.color.toString(16).padStart(6, '0')}`;
|
||
}
|
||
|
||
roleSelect.appendChild(option);
|
||
});
|
||
})
|
||
.catch(error => {
|
||
console.error('Error loading roles:', error);
|
||
|
||
// Check for specific error about missing bot token
|
||
if (error.status === 503 && error.message && error.message.includes('Bot token not configured')) {
|
||
// Show global error banner (if not already shown)
|
||
showBotTokenMissingError();
|
||
|
||
// Show a more helpful message in the role select
|
||
roleSelect.innerHTML = '<option value="">Bot token not configured</option>';
|
||
roleSelect.disabled = true;
|
||
|
||
// Add a visible error message
|
||
const errorDiv = document.createElement('div');
|
||
errorDiv.className = 'error-message';
|
||
errorDiv.innerHTML = `
|
||
<p>The Discord bot token is not configured. Role selection is unavailable.</p>
|
||
<p>Please set the <code>DISCORD_BOT_TOKEN</code> environment variable in your .env file.</p>
|
||
`;
|
||
|
||
// Add the error message near the role select
|
||
if (roleSelect.parentNode) {
|
||
roleSelect.parentNode.appendChild(errorDiv);
|
||
}
|
||
} else {
|
||
Toast.error('Failed to load roles. Please try again.');
|
||
}
|
||
});
|
||
}
|
||
|
||
/**
|
||
* Load commands for a guild
|
||
* @param {string} guildId - The guild ID
|
||
*/
|
||
function loadGuildCommands(guildId) {
|
||
const commandSelect = document.getElementById('command-select');
|
||
if (!commandSelect) return;
|
||
|
||
// Fetch commands from API
|
||
API.get(`/dashboard/api/guilds/${guildId}/commands`)
|
||
.then(commands => {
|
||
commandSelect.innerHTML = '<option value="">-- Select Command --</option>';
|
||
|
||
commands.forEach(command => {
|
||
const option = document.createElement('option');
|
||
option.value = command.name;
|
||
option.textContent = command.name;
|
||
|
||
// Add description as title attribute
|
||
if (command.description) {
|
||
option.title = command.description;
|
||
}
|
||
|
||
commandSelect.appendChild(option);
|
||
});
|
||
|
||
// Load command permissions
|
||
loadCommandPermissions(guildId);
|
||
})
|
||
.catch(error => {
|
||
console.error('Error loading commands:', error);
|
||
|
||
// Check for specific error about missing bot token
|
||
if (error.status === 503 && error.message && error.message.includes('Bot token not configured')) {
|
||
// Show global error banner (if not already shown)
|
||
showBotTokenMissingError();
|
||
|
||
// Show a more helpful message in the command select
|
||
commandSelect.innerHTML = '<option value="">Bot token not configured</option>';
|
||
commandSelect.disabled = true;
|
||
|
||
// Add a visible error message
|
||
const errorDiv = document.createElement('div');
|
||
errorDiv.className = 'error-message';
|
||
errorDiv.innerHTML = `
|
||
<p>The Discord bot token is not configured. Command selection is unavailable.</p>
|
||
<p>Please set the <code>DISCORD_BOT_TOKEN</code> environment variable in your .env file.</p>
|
||
`;
|
||
|
||
// Add the error message near the command select
|
||
if (commandSelect.parentNode) {
|
||
commandSelect.parentNode.appendChild(errorDiv);
|
||
}
|
||
} else {
|
||
Toast.error('Failed to load commands. Please try again.');
|
||
}
|
||
});
|
||
}
|
||
|
||
/**
|
||
* Load command permissions for a guild
|
||
* @param {string} guildId - The guild ID
|
||
*/
|
||
function loadCommandPermissions(guildId) {
|
||
const currentPerms = document.getElementById('current-perms');
|
||
if (!currentPerms) return;
|
||
|
||
// Show loading state
|
||
currentPerms.innerHTML = '<div class="loading-spinner-container"><div class="loading-spinner loading-spinner-sm"></div></div>';
|
||
|
||
// Fetch command permissions from API
|
||
API.get(`/dashboard/api/guilds/${guildId}/command-permissions`)
|
||
.then(permissions => {
|
||
// Clear loading state
|
||
currentPerms.innerHTML = '';
|
||
|
||
if (permissions.length === 0) {
|
||
currentPerms.innerHTML = '<div class="text-gray">No custom permissions set.</div>';
|
||
return;
|
||
}
|
||
|
||
// Group permissions by command
|
||
const permsByCommand = {};
|
||
|
||
permissions.forEach(perm => {
|
||
if (!permsByCommand[perm.command]) {
|
||
permsByCommand[perm.command] = [];
|
||
}
|
||
|
||
permsByCommand[perm.command].push(perm);
|
||
});
|
||
|
||
// Create permission elements
|
||
Object.entries(permsByCommand).forEach(([command, perms]) => {
|
||
const commandDiv = document.createElement('div');
|
||
commandDiv.className = 'command-perms';
|
||
commandDiv.innerHTML = `<div class="command-name">${command}</div>`;
|
||
|
||
const rolesList = document.createElement('div');
|
||
rolesList.className = 'roles-list';
|
||
|
||
perms.forEach(perm => {
|
||
const roleSpan = document.createElement('span');
|
||
roleSpan.className = 'role-badge';
|
||
roleSpan.textContent = perm.role_name;
|
||
roleSpan.dataset.roleId = perm.role_id;
|
||
roleSpan.dataset.command = command;
|
||
|
||
// Add remove button
|
||
const removeBtn = document.createElement('button');
|
||
removeBtn.className = 'role-remove';
|
||
removeBtn.innerHTML = '×';
|
||
removeBtn.addEventListener('click', () => {
|
||
removeCommandPermission(guildId, command, perm.role_id);
|
||
});
|
||
|
||
roleSpan.appendChild(removeBtn);
|
||
rolesList.appendChild(roleSpan);
|
||
});
|
||
|
||
commandDiv.appendChild(rolesList);
|
||
currentPerms.appendChild(commandDiv);
|
||
});
|
||
})
|
||
.catch(error => {
|
||
console.error('Error loading command permissions:', error);
|
||
currentPerms.innerHTML = '<div class="text-danger">Error loading permissions.</div>';
|
||
});
|
||
}
|
||
|
||
/**
|
||
* Add a command permission
|
||
* @param {string} guildId - The guild ID
|
||
* @param {string} command - The command name
|
||
* @param {string} roleId - The role ID
|
||
*/
|
||
function addCommandPermission(guildId, command, roleId) {
|
||
const addPermButton = document.getElementById('add-perm-button');
|
||
const permsFeedback = document.getElementById('perms-feedback');
|
||
|
||
if (!command || !roleId) {
|
||
permsFeedback.textContent = 'Please select both a command and a role.';
|
||
permsFeedback.className = 'error';
|
||
return;
|
||
}
|
||
|
||
// Show loading state
|
||
addPermButton.disabled = true;
|
||
addPermButton.classList.add('btn-loading');
|
||
|
||
// Send request to API
|
||
API.post(`/dashboard/api/guilds/${guildId}/command-permissions`, {
|
||
command,
|
||
role_id: roleId
|
||
})
|
||
.then(() => {
|
||
// Show success message
|
||
permsFeedback.textContent = 'Permission added successfully.';
|
||
permsFeedback.className = '';
|
||
|
||
// Reload permissions
|
||
loadCommandPermissions(guildId);
|
||
|
||
// Reset form
|
||
document.getElementById('command-select').value = '';
|
||
document.getElementById('role-select').value = '';
|
||
})
|
||
.catch(error => {
|
||
console.error('Error adding permission:', error);
|
||
permsFeedback.textContent = 'Error adding permission. Please try again.';
|
||
permsFeedback.className = 'error';
|
||
})
|
||
.finally(() => {
|
||
// Remove loading state
|
||
addPermButton.disabled = false;
|
||
addPermButton.classList.remove('btn-loading');
|
||
});
|
||
}
|
||
|
||
/**
|
||
* Remove a command permission
|
||
* @param {string} guildId - The guild ID
|
||
* @param {string} command - The command name
|
||
* @param {string} roleId - The role ID
|
||
*/
|
||
function removeCommandPermission(guildId, command, roleId) {
|
||
// Send request to API
|
||
API.delete(`/dashboard/api/guilds/${guildId}/command-permissions?command=${command}&role_id=${roleId}`)
|
||
.then(() => {
|
||
// Show success message
|
||
Toast.success('Permission removed successfully.');
|
||
|
||
// Reload permissions
|
||
loadCommandPermissions(guildId);
|
||
})
|
||
.catch(error => {
|
||
console.error('Error removing permission:', error);
|
||
Toast.error('Error removing permission. Please try again.');
|
||
});
|
||
}
|
||
|
||
/**
|
||
* Set up welcome/leave message test buttons
|
||
* @param {string} guildId - The guild ID
|
||
*/
|
||
/**
|
||
* Set up event listeners for save settings buttons
|
||
* @param {string} guildId - The guild ID
|
||
*/
|
||
function setupSaveSettingsButtons(guildId) {
|
||
// Save prefix button
|
||
const savePrefixButton = document.getElementById('save-prefix-button');
|
||
if (savePrefixButton) {
|
||
savePrefixButton.addEventListener('click', () => {
|
||
// Get prefix value
|
||
const prefix = document.getElementById('prefix-input').value;
|
||
if (!prefix) {
|
||
Toast.error('Please enter a prefix');
|
||
return;
|
||
}
|
||
|
||
// Show loading state
|
||
savePrefixButton.disabled = true;
|
||
savePrefixButton.classList.add('btn-loading');
|
||
|
||
// Send request to API
|
||
API.patch(`/dashboard/api/guilds/${guildId}/settings`, {
|
||
prefix: prefix
|
||
})
|
||
.then(() => {
|
||
// Show success message
|
||
Toast.success('Prefix saved successfully');
|
||
|
||
// Show feedback
|
||
const prefixFeedback = document.getElementById('prefix-feedback');
|
||
if (prefixFeedback) {
|
||
prefixFeedback.textContent = 'Prefix saved successfully';
|
||
prefixFeedback.className = 'success';
|
||
|
||
// Clear feedback after a few seconds
|
||
setTimeout(() => {
|
||
prefixFeedback.textContent = '';
|
||
prefixFeedback.className = '';
|
||
}, 3000);
|
||
}
|
||
})
|
||
.catch(error => {
|
||
console.error('Error saving prefix:', error);
|
||
Toast.error('Failed to save prefix. Please try again.');
|
||
|
||
// Show error feedback
|
||
const prefixFeedback = document.getElementById('prefix-feedback');
|
||
if (prefixFeedback) {
|
||
prefixFeedback.textContent = 'Error saving prefix. Please try again.';
|
||
prefixFeedback.className = 'error';
|
||
}
|
||
})
|
||
.finally(() => {
|
||
// Remove loading state
|
||
savePrefixButton.disabled = false;
|
||
savePrefixButton.classList.remove('btn-loading');
|
||
});
|
||
});
|
||
}
|
||
|
||
// Save welcome settings button
|
||
const saveWelcomeButton = document.getElementById('save-welcome-button');
|
||
if (saveWelcomeButton) {
|
||
saveWelcomeButton.addEventListener('click', () => {
|
||
// Get welcome settings
|
||
const welcomeChannelId = document.getElementById('welcome-channel').value;
|
||
const welcomeMessage = document.getElementById('welcome-message').value;
|
||
|
||
// Show loading state
|
||
saveWelcomeButton.disabled = true;
|
||
saveWelcomeButton.classList.add('btn-loading');
|
||
|
||
// Send request to API
|
||
API.patch(`/dashboard/api/guilds/${guildId}/settings`, {
|
||
welcome_channel_id: welcomeChannelId,
|
||
welcome_message: welcomeMessage
|
||
})
|
||
.then(() => {
|
||
// Show success message
|
||
Toast.success('Welcome settings saved successfully');
|
||
|
||
// Show feedback
|
||
const welcomeFeedback = document.getElementById('welcome-feedback');
|
||
if (welcomeFeedback) {
|
||
welcomeFeedback.textContent = 'Welcome settings saved successfully';
|
||
welcomeFeedback.className = 'success';
|
||
|
||
// Clear feedback after a few seconds
|
||
setTimeout(() => {
|
||
welcomeFeedback.textContent = '';
|
||
welcomeFeedback.className = '';
|
||
}, 3000);
|
||
}
|
||
})
|
||
.catch(error => {
|
||
console.error('Error saving welcome settings:', error);
|
||
Toast.error('Failed to save welcome settings. Please try again.');
|
||
|
||
// Show error feedback
|
||
const welcomeFeedback = document.getElementById('welcome-feedback');
|
||
if (welcomeFeedback) {
|
||
welcomeFeedback.textContent = 'Error saving welcome settings. Please try again.';
|
||
welcomeFeedback.className = 'error';
|
||
}
|
||
})
|
||
.finally(() => {
|
||
// Remove loading state
|
||
saveWelcomeButton.disabled = false;
|
||
saveWelcomeButton.classList.remove('btn-loading');
|
||
});
|
||
});
|
||
}
|
||
|
||
// Disable welcome button
|
||
const disableWelcomeButton = document.getElementById('disable-welcome-button');
|
||
if (disableWelcomeButton) {
|
||
disableWelcomeButton.addEventListener('click', () => {
|
||
// Confirm disable
|
||
if (!confirm('Are you sure you want to disable welcome messages?')) {
|
||
return;
|
||
}
|
||
|
||
// Show loading state
|
||
disableWelcomeButton.disabled = true;
|
||
disableWelcomeButton.classList.add('btn-loading');
|
||
|
||
// Send request to API
|
||
API.patch(`/dashboard/api/guilds/${guildId}/settings`, {
|
||
welcome_channel_id: null,
|
||
welcome_message: null
|
||
})
|
||
.then(() => {
|
||
// Show success message
|
||
Toast.success('Welcome messages disabled');
|
||
|
||
// Clear welcome settings inputs
|
||
const welcomeChannel = document.getElementById('welcome-channel');
|
||
const welcomeMessage = document.getElementById('welcome-message');
|
||
const welcomeChannelSelect = document.getElementById('welcome-channel-select');
|
||
|
||
if (welcomeChannel) welcomeChannel.value = '';
|
||
if (welcomeMessage) welcomeMessage.value = '';
|
||
if (welcomeChannelSelect) welcomeChannelSelect.value = '';
|
||
|
||
// Show feedback
|
||
const welcomeFeedback = document.getElementById('welcome-feedback');
|
||
if (welcomeFeedback) {
|
||
welcomeFeedback.textContent = 'Welcome messages disabled';
|
||
welcomeFeedback.className = 'success';
|
||
|
||
// Clear feedback after a few seconds
|
||
setTimeout(() => {
|
||
welcomeFeedback.textContent = '';
|
||
welcomeFeedback.className = '';
|
||
}, 3000);
|
||
}
|
||
})
|
||
.catch(error => {
|
||
console.error('Error disabling welcome messages:', error);
|
||
Toast.error('Failed to disable welcome messages. Please try again.');
|
||
|
||
// Show error feedback
|
||
const welcomeFeedback = document.getElementById('welcome-feedback');
|
||
if (welcomeFeedback) {
|
||
welcomeFeedback.textContent = 'Error disabling welcome messages. Please try again.';
|
||
welcomeFeedback.className = 'error';
|
||
}
|
||
})
|
||
.finally(() => {
|
||
// Remove loading state
|
||
disableWelcomeButton.disabled = false;
|
||
disableWelcomeButton.classList.remove('btn-loading');
|
||
});
|
||
});
|
||
}
|
||
|
||
// Save goodbye settings button
|
||
const saveGoodbyeButton = document.getElementById('save-goodbye-button');
|
||
if (saveGoodbyeButton) {
|
||
saveGoodbyeButton.addEventListener('click', () => {
|
||
// Get goodbye settings
|
||
const goodbyeChannelId = document.getElementById('goodbye-channel').value;
|
||
const goodbyeMessage = document.getElementById('goodbye-message').value;
|
||
|
||
// Show loading state
|
||
saveGoodbyeButton.disabled = true;
|
||
saveGoodbyeButton.classList.add('btn-loading');
|
||
|
||
// Send request to API
|
||
API.patch(`/dashboard/api/guilds/${guildId}/settings`, {
|
||
goodbye_channel_id: goodbyeChannelId,
|
||
goodbye_message: goodbyeMessage
|
||
})
|
||
.then(() => {
|
||
// Show success message
|
||
Toast.success('Goodbye settings saved successfully');
|
||
|
||
// Show feedback
|
||
const goodbyeFeedback = document.getElementById('goodbye-feedback');
|
||
if (goodbyeFeedback) {
|
||
goodbyeFeedback.textContent = 'Goodbye settings saved successfully';
|
||
goodbyeFeedback.className = 'success';
|
||
|
||
// Clear feedback after a few seconds
|
||
setTimeout(() => {
|
||
goodbyeFeedback.textContent = '';
|
||
goodbyeFeedback.className = '';
|
||
}, 3000);
|
||
}
|
||
})
|
||
.catch(error => {
|
||
console.error('Error saving goodbye settings:', error);
|
||
Toast.error('Failed to save goodbye settings. Please try again.');
|
||
|
||
// Show error feedback
|
||
const goodbyeFeedback = document.getElementById('goodbye-feedback');
|
||
if (goodbyeFeedback) {
|
||
goodbyeFeedback.textContent = 'Error saving goodbye settings. Please try again.';
|
||
goodbyeFeedback.className = 'error';
|
||
}
|
||
})
|
||
.finally(() => {
|
||
// Remove loading state
|
||
saveGoodbyeButton.disabled = false;
|
||
saveGoodbyeButton.classList.remove('btn-loading');
|
||
});
|
||
});
|
||
}
|
||
|
||
// Disable goodbye button
|
||
const disableGoodbyeButton = document.getElementById('disable-goodbye-button');
|
||
if (disableGoodbyeButton) {
|
||
disableGoodbyeButton.addEventListener('click', () => {
|
||
// Confirm disable
|
||
if (!confirm('Are you sure you want to disable goodbye messages?')) {
|
||
return;
|
||
}
|
||
|
||
// Show loading state
|
||
disableGoodbyeButton.disabled = true;
|
||
disableGoodbyeButton.classList.add('btn-loading');
|
||
|
||
// Send request to API
|
||
API.patch(`/dashboard/api/guilds/${guildId}/settings`, {
|
||
goodbye_channel_id: null,
|
||
goodbye_message: null
|
||
})
|
||
.then(() => {
|
||
// Show success message
|
||
Toast.success('Goodbye messages disabled');
|
||
|
||
// Clear goodbye settings inputs
|
||
const goodbyeChannel = document.getElementById('goodbye-channel');
|
||
const goodbyeMessage = document.getElementById('goodbye-message');
|
||
const goodbyeChannelSelect = document.getElementById('goodbye-channel-select');
|
||
|
||
if (goodbyeChannel) goodbyeChannel.value = '';
|
||
if (goodbyeMessage) goodbyeMessage.value = '';
|
||
if (goodbyeChannelSelect) goodbyeChannelSelect.value = '';
|
||
|
||
// Show feedback
|
||
const goodbyeFeedback = document.getElementById('goodbye-feedback');
|
||
if (goodbyeFeedback) {
|
||
goodbyeFeedback.textContent = 'Goodbye messages disabled';
|
||
goodbyeFeedback.className = 'success';
|
||
|
||
// Clear feedback after a few seconds
|
||
setTimeout(() => {
|
||
goodbyeFeedback.textContent = '';
|
||
goodbyeFeedback.className = '';
|
||
}, 3000);
|
||
}
|
||
})
|
||
.catch(error => {
|
||
console.error('Error disabling goodbye messages:', error);
|
||
Toast.error('Failed to disable goodbye messages. Please try again.');
|
||
|
||
// Show error feedback
|
||
const goodbyeFeedback = document.getElementById('goodbye-feedback');
|
||
if (goodbyeFeedback) {
|
||
goodbyeFeedback.textContent = 'Error disabling goodbye messages. Please try again.';
|
||
goodbyeFeedback.className = 'error';
|
||
}
|
||
})
|
||
.finally(() => {
|
||
// Remove loading state
|
||
disableGoodbyeButton.disabled = false;
|
||
disableGoodbyeButton.classList.remove('btn-loading');
|
||
});
|
||
});
|
||
}
|
||
|
||
// Save cogs button
|
||
const saveCogsButton = document.getElementById('save-cogs-button');
|
||
if (saveCogsButton) {
|
||
saveCogsButton.addEventListener('click', () => {
|
||
// Get cog settings
|
||
const cogsPayload = {};
|
||
const cogCheckboxes = document.querySelectorAll('#cogs-list input[type="checkbox"]');
|
||
|
||
cogCheckboxes.forEach(checkbox => {
|
||
// Extract cog name from checkbox ID (format: cog-{name})
|
||
const cogName = checkbox.id.replace('cog-', '');
|
||
cogsPayload[cogName] = checkbox.checked;
|
||
});
|
||
|
||
// Show loading state
|
||
saveCogsButton.disabled = true;
|
||
saveCogsButton.classList.add('btn-loading');
|
||
|
||
// Send request to API
|
||
API.patch(`/dashboard/api/guilds/${guildId}/settings`, {
|
||
cogs: cogsPayload
|
||
})
|
||
.then(() => {
|
||
// Show success message
|
||
Toast.success('Module settings saved successfully');
|
||
|
||
// Show feedback
|
||
const cogsFeedback = document.getElementById('cogs-feedback');
|
||
if (cogsFeedback) {
|
||
cogsFeedback.textContent = 'Module settings saved successfully';
|
||
cogsFeedback.className = 'success';
|
||
|
||
// Clear feedback after a few seconds
|
||
setTimeout(() => {
|
||
cogsFeedback.textContent = '';
|
||
cogsFeedback.className = '';
|
||
}, 3000);
|
||
}
|
||
})
|
||
.catch(error => {
|
||
console.error('Error saving module settings:', error);
|
||
Toast.error('Failed to save module settings. Please try again.');
|
||
|
||
// Show error feedback
|
||
const cogsFeedback = document.getElementById('cogs-feedback');
|
||
if (cogsFeedback) {
|
||
cogsFeedback.textContent = 'Error saving module settings. Please try again.';
|
||
cogsFeedback.className = 'error';
|
||
}
|
||
})
|
||
.finally(() => {
|
||
// Remove loading state
|
||
saveCogsButton.disabled = false;
|
||
saveCogsButton.classList.remove('btn-loading');
|
||
});
|
||
});
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Load the custom bot section
|
||
*/
|
||
function loadCustomBotSection() {
|
||
const customBotSection = document.getElementById('custom-bot-section');
|
||
if (!customBotSection) return;
|
||
|
||
// Show loading state
|
||
customBotSection.innerHTML = '<div class="loading-spinner-container"><div class="loading-spinner"></div></div>';
|
||
|
||
// Load the custom bot HTML
|
||
fetch('/dashboard/custom-bot.html')
|
||
.then(response => response.text())
|
||
.then(html => {
|
||
// Insert the HTML
|
||
customBotSection.innerHTML = html;
|
||
|
||
// Initialize the custom bot functionality
|
||
if (typeof initCustomBot === 'function') {
|
||
initCustomBot();
|
||
} else {
|
||
console.error('initCustomBot function not found. Make sure custom-bot.js is loaded.');
|
||
}
|
||
})
|
||
.catch(error => {
|
||
console.error('Error loading custom bot section:', error);
|
||
customBotSection.innerHTML = '<div class="alert alert-danger">Error loading custom bot section. Please try again.</div>';
|
||
});
|
||
}
|
||
|
||
function setupWelcomeLeaveTestButtons(guildId) {
|
||
// Welcome message test button
|
||
const testWelcomeButton = document.getElementById('test-welcome-button');
|
||
if (testWelcomeButton) {
|
||
testWelcomeButton.addEventListener('click', () => {
|
||
// Show loading state
|
||
testWelcomeButton.disabled = true;
|
||
testWelcomeButton.classList.add('btn-loading');
|
||
|
||
// Send test request to API
|
||
API.post(`/dashboard/api/guilds/${guildId}/test-welcome`)
|
||
.then(response => {
|
||
console.log('Test welcome message response:', response);
|
||
|
||
// Show success message with formatted message
|
||
Toast.success('Test welcome message sent!');
|
||
|
||
// Show formatted message in feedback area
|
||
const welcomeFeedback = document.getElementById('welcome-feedback');
|
||
if (welcomeFeedback) {
|
||
welcomeFeedback.innerHTML = `
|
||
<div class="mt-4 p-3 border rounded bg-light">
|
||
<strong>Test Message:</strong>
|
||
<p class="mb-0">${response.formatted_message}</p>
|
||
<small class="text-muted">Sent to channel ID: ${response.channel_id}</small>
|
||
</div>
|
||
`;
|
||
}
|
||
})
|
||
.catch(error => {
|
||
console.error('Error testing welcome message:', error);
|
||
|
||
// Show error message
|
||
if (error.status === 400) {
|
||
Toast.error('Welcome channel not configured. Please set a welcome channel first.');
|
||
} else {
|
||
Toast.error('Failed to test welcome message. Please try again.');
|
||
}
|
||
})
|
||
.finally(() => {
|
||
// Remove loading state
|
||
testWelcomeButton.disabled = false;
|
||
testWelcomeButton.classList.remove('btn-loading');
|
||
});
|
||
});
|
||
}
|
||
|
||
// Goodbye message test button
|
||
const testGoodbyeButton = document.getElementById('test-goodbye-button');
|
||
if (testGoodbyeButton) {
|
||
testGoodbyeButton.addEventListener('click', () => {
|
||
// Show loading state
|
||
testGoodbyeButton.disabled = true;
|
||
testGoodbyeButton.classList.add('btn-loading');
|
||
|
||
// Send test request to API
|
||
API.post(`/dashboard/api/guilds/${guildId}/test-goodbye`)
|
||
.then(response => {
|
||
console.log('Test goodbye message response:', response);
|
||
|
||
// Show success message with formatted message
|
||
Toast.success('Test goodbye message sent!');
|
||
|
||
// Show formatted message in feedback area
|
||
const goodbyeFeedback = document.getElementById('goodbye-feedback');
|
||
if (goodbyeFeedback) {
|
||
goodbyeFeedback.innerHTML = `
|
||
<div class="mt-4 p-3 border rounded bg-light">
|
||
<strong>Test Message:</strong>
|
||
<p class="mb-0">${response.formatted_message}</p>
|
||
<small class="text-muted">Sent to channel ID: ${response.channel_id}</small>
|
||
</div>
|
||
`;
|
||
}
|
||
})
|
||
.catch(error => {
|
||
console.error('Error testing goodbye message:', error);
|
||
|
||
// Show error message
|
||
if (error.status === 400) {
|
||
Toast.error('Goodbye channel not configured. Please set a goodbye channel first.');
|
||
} else {
|
||
Toast.error('Failed to test goodbye message. Please try again.');
|
||
}
|
||
})
|
||
.finally(() => {
|
||
// Remove loading state
|
||
testGoodbyeButton.disabled = false;
|
||
testGoodbyeButton.classList.remove('btn-loading');
|
||
});
|
||
});
|
||
}
|
||
}
|