/** * Command Customization JavaScript * Handles command customization functionality for the dashboard */ // Track loaded state let commandCustomizationLoadedGuild = null; // Initialize command customization when DOM is loaded document.addEventListener('DOMContentLoaded', () => { initCommandCustomization(); }); /** * Initialize command customization */ function initCommandCustomization() { // Add event listener to command search input const commandSearch = document.getElementById('command-search'); if (commandSearch) { commandSearch.addEventListener('input', filterCommands); } // Add event listener to sync commands button const syncCommandsButton = document.getElementById('sync-commands-button'); if (syncCommandsButton) { syncCommandsButton.addEventListener('click', syncCommands); } // Add event listener to add alias button const addAliasButton = document.getElementById('add-alias-button'); if (addAliasButton) { addAliasButton.addEventListener('click', addAlias); } // Loading is now handled by showSection in main.js // const navItem = document.querySelector('a[data-section="command-customization-section"]'); // if (navItem) { // navItem.addEventListener('click', () => { // loadCommandCustomizationData(window.selectedGuildId); // Pass guildId // }); // } } /** * Load command customizations from API * @param {string} guildId - The guild ID (passed from main.js) */ async function loadCommandCustomizationData(guildId) { // Renamed function // Track loaded state for this module if (commandCustomizationLoadedGuild === guildId) { console.log(`Command customization data for guild ${guildId} already loaded.`); // Ensure content is visible if navigating back document.getElementById('command-list').style.display = 'block'; document.getElementById('group-list').style.display = 'block'; document.getElementById('alias-list').style.display = 'block'; return; } console.log(`Loading command customization data for guild: ${guildId}`); try { // Show loading spinners document.getElementById('command-list').innerHTML = '
'; document.getElementById('group-list').innerHTML = '
'; document.getElementById('alias-list').innerHTML = '
'; if (!guildId) { Toast.error('No guild selected for command customization.'); return; } // Fetch command customizations from API using the API helper const data = await API.get(`/dashboard/api/guilds/${guildId}/command-customizations`); // Updated endpoint // Render command customizations renderCommandCustomizations(data.command_customizations || {}); // Render group customizations renderGroupCustomizations(data.group_customizations || {}); // Render command aliases renderCommandAliases(data.command_aliases || {}); // Populate command select for aliases populateCommandSelect(Object.keys(data.command_customizations || {})); // Mark as loaded for this guild commandCustomizationLoadedGuild = guildId; } catch (error) { console.error('Error loading command customizations:', error); Toast.error('Failed to load command customizations'); // Show error message in lists document.getElementById('command-list').innerHTML = '
Failed to load command customizations
'; document.getElementById('group-list').innerHTML = '
Failed to load group customizations
'; document.getElementById('alias-list').innerHTML = '
Failed to load command aliases
'; } } /** * Render command customizations * @param {Object} commandCustomizations - Command customizations object */ function renderCommandCustomizations(commandCustomizations) { const commandList = document.getElementById('command-list'); commandList.innerHTML = ''; if (Object.keys(commandCustomizations).length === 0) { commandList.innerHTML = '
No commands found or available for customization.
'; return; } // Sort commands alphabetically const sortedCommands = Object.keys(commandCustomizations).sort(); // Create command items sortedCommands.forEach(commandName => { const customization = commandCustomizations[commandName]; const commandItem = createCommandItem(commandName, customization); commandList.appendChild(commandItem); }); } /** * Create a command item element * @param {string} commandName - Original command name * @param {Object} customization - Command customization object * @returns {HTMLElement} Command item element */ function createCommandItem(commandName, customization) { // Clone the template const template = document.getElementById('command-item-template'); const commandItem = template.content.cloneNode(true).querySelector('.command-item'); // Set command name const nameElement = commandItem.querySelector('.command-name'); nameElement.textContent = commandName; if (customization.name && customization.name !== commandName) { nameElement.textContent = `${customization.name} (${commandName})`; } // Set command description const descriptionElement = commandItem.querySelector('.command-description'); descriptionElement.textContent = customization.description || 'No description available'; // Set custom name input value const customNameInput = commandItem.querySelector('.custom-command-name'); customNameInput.value = customization.name || ''; customNameInput.placeholder = commandName; // Set custom description input value const customDescriptionInput = commandItem.querySelector('.custom-command-description'); customDescriptionInput.value = customization.description || ''; // Add event listeners to buttons const editButton = commandItem.querySelector('.edit-command-btn'); const resetButton = commandItem.querySelector('.reset-command-btn'); const saveButton = commandItem.querySelector('.save-command-btn'); const cancelButton = commandItem.querySelector('.cancel-command-btn'); const customizationDiv = commandItem.querySelector('.command-customization'); editButton.addEventListener('click', () => { customizationDiv.style.display = 'block'; editButton.style.display = 'none'; }); resetButton.addEventListener('click', () => { resetCommandCustomization(commandName); }); saveButton.addEventListener('click', () => { saveCommandCustomization( commandName, customNameInput.value, customDescriptionInput.value, customizationDiv, editButton, nameElement, descriptionElement ); }); cancelButton.addEventListener('click', () => { customizationDiv.style.display = 'none'; editButton.style.display = 'inline-block'; // Reset input values customNameInput.value = customization.name || ''; customDescriptionInput.value = customization.description || ''; }); // Add data attribute for filtering commandItem.dataset.commandName = commandName.toLowerCase(); return commandItem; } /** * Save command customization * @param {string} commandName - Original command name * @param {string} customName - Custom command name * @param {string} customDescription - Custom command description * @param {HTMLElement} customizationDiv - Command customization div * @param {HTMLElement} editButton - Edit button * @param {HTMLElement} nameElement - Command name element * @param {HTMLElement} descriptionElement - Command description element */ async function saveCommandCustomization( commandName, customName, customDescription, customizationDiv, editButton, nameElement, descriptionElement ) { try { // Validate custom name format if provided if (customName && (!/^[a-z][a-z0-9_]*$/.test(customName) || customName.length > 32)) { Toast.error('Custom command names must be lowercase, start with a letter, and contain only letters, numbers, and underscores (max 32 characters)'); return; } // Validate custom description if provided if (customDescription && customDescription.length > 100) { Toast.error('Custom command descriptions must be 100 characters or less'); return; } // Get the current guild ID const guildId = window.selectedGuildId; // Use global guild ID if (!guildId) { Toast.error('No guild selected'); return; } // Prepare request data const requestData = { command_name: commandName, custom_name: customName || null, custom_description: customDescription || null }; // Send request to API await API.post(`/dashboard/api/guilds/${guildId}/command-customizations/commands`, requestData); // Updated endpoint // Update UI customizationDiv.style.display = 'none'; editButton.style.display = 'inline-block'; if (customName) { nameElement.textContent = `${customName} (${commandName})`; } else { nameElement.textContent = commandName; } if (customDescription) { descriptionElement.textContent = customDescription; } else { // Fetch the original description if resetting // This might require an extra API call or storing original data descriptionElement.textContent = 'No description available'; // Placeholder } Toast.success('Command customization saved successfully'); } catch (error) { console.error('Error saving command customization:', error); Toast.error(`Failed to save command customization: ${error.message || error}`); } } /** * Reset command customization * @param {string} commandName - Original command name */ async function resetCommandCustomization(commandName) { try { // Get the current guild ID const guildId = window.selectedGuildId; // Use global guild ID if (!guildId) { Toast.error('No guild selected'); return; } // Prepare request data const requestData = { command_name: commandName, custom_name: null, custom_description: null }; // Send request to API await API.post(`/dashboard/api/guilds/${guildId}/command-customizations/commands`, requestData); // Updated endpoint // Reload command customizations loadCommandCustomizationData(guildId); // Reload data for the current guild Toast.success('Command customization reset successfully'); } catch (error) { console.error('Error resetting command customization:', error); Toast.error(`Failed to reset command customization: ${error.message || error}`); } } /** * Render group customizations * @param {Object} groupCustomizations - Group customizations object */ function renderGroupCustomizations(groupCustomizations) { const groupList = document.getElementById('group-list'); groupList.innerHTML = ''; if (Object.keys(groupCustomizations).length === 0) { groupList.innerHTML = '
No command groups found or available for customization.
'; return; } // Sort groups alphabetically const sortedGroups = Object.keys(groupCustomizations).sort(); // Create group items sortedGroups.forEach(groupName => { const customization = groupCustomizations[groupName]; const customName = customization.name || customization; // Support both old and new format const groupItem = createGroupItem(groupName, customName); groupList.appendChild(groupItem); }); } /** * Create a group item element * @param {string} groupName - Original group name * @param {string} customName - Custom group name * @returns {HTMLElement} Group item element */ function createGroupItem(groupName, customName) { // Clone the template const template = document.getElementById('group-item-template'); const groupItem = template.content.cloneNode(true).querySelector('.command-item'); // Set group name const nameElement = groupItem.querySelector('.group-name'); nameElement.textContent = groupName; if (customName && customName !== groupName) { nameElement.textContent = `${customName} (${groupName})`; } // Set custom name input value const customNameInput = groupItem.querySelector('.custom-group-name'); customNameInput.value = customName || ''; customNameInput.placeholder = groupName; // Add event listeners to buttons const editButton = groupItem.querySelector('.edit-group-btn'); const resetButton = groupItem.querySelector('.reset-group-btn'); const saveButton = groupItem.querySelector('.save-group-btn'); const cancelButton = groupItem.querySelector('.cancel-group-btn'); const customizationDiv = groupItem.querySelector('.group-customization'); editButton.addEventListener('click', () => { customizationDiv.style.display = 'block'; editButton.style.display = 'none'; }); resetButton.addEventListener('click', () => { resetGroupCustomization(groupName); }); saveButton.addEventListener('click', () => { saveGroupCustomization( groupName, customNameInput.value, customizationDiv, editButton, nameElement ); }); cancelButton.addEventListener('click', () => { customizationDiv.style.display = 'none'; editButton.style.display = 'inline-block'; // Reset input value customNameInput.value = customName || ''; }); return groupItem; } /** * Save group customization * @param {string} groupName - Original group name * @param {string} customName - Custom group name * @param {HTMLElement} customizationDiv - Group customization div * @param {HTMLElement} editButton - Edit button * @param {HTMLElement} nameElement - Group name element */ async function saveGroupCustomization( groupName, customName, customizationDiv, editButton, nameElement ) { try { // Validate custom name format if provided if (customName && (!/^[a-z][a-z0-9_]*$/.test(customName) || customName.length > 32)) { Toast.error('Custom group names must be lowercase, start with a letter, and contain only letters, numbers, and underscores (max 32 characters)'); return; } // Get the current guild ID const guildId = window.selectedGuildId; // Use global guild ID if (!guildId) { Toast.error('No guild selected'); return; } // Prepare request data const requestData = { group_name: groupName, custom_name: customName || null }; // Send request to API await API.post(`/dashboard/api/guilds/${guildId}/command-customizations/groups`, requestData); // Updated endpoint // Update UI customizationDiv.style.display = 'none'; editButton.style.display = 'inline-block'; if (customName) { nameElement.textContent = `${customName} (${groupName})`; } else { nameElement.textContent = groupName; } Toast.success('Group customization saved successfully'); } catch (error) { console.error('Error saving group customization:', error); Toast.error(`Failed to save group customization: ${error.message || error}`); } } /** * Reset group customization * @param {string} groupName - Original group name */ async function resetGroupCustomization(groupName) { try { // Get the current guild ID const guildId = window.selectedGuildId; // Use global guild ID if (!guildId) { Toast.error('No guild selected'); return; } // Prepare request data const requestData = { group_name: groupName, custom_name: null }; // Send request to API await API.post(`/dashboard/api/guilds/${guildId}/command-customizations/groups`, requestData); // Updated endpoint // Reload command customizations loadCommandCustomizationData(guildId); // Reload data for the current guild Toast.success('Group customization reset successfully'); } catch (error) { console.error('Error resetting group customization:', error); Toast.error(`Failed to reset group customization: ${error.message || error}`); } } /** * Render command aliases * @param {Object} commandAliases - Command aliases object */ function renderCommandAliases(commandAliases) { const aliasList = document.getElementById('alias-list'); aliasList.innerHTML = ''; if (Object.keys(commandAliases).length === 0) { aliasList.innerHTML = '
No command aliases found.
'; return; } // Sort commands alphabetically const sortedCommands = Object.keys(commandAliases).sort(); // Create alias items sortedCommands.forEach(commandName => { const aliases = commandAliases[commandName]; if (aliases && aliases.length > 0) { const aliasItem = createAliasItem(commandName, aliases); aliasList.appendChild(aliasItem); } }); } /** * Create an alias item element * @param {string} commandName - Original command name * @param {Array} aliases - Command aliases * @returns {HTMLElement} Alias item element */ function createAliasItem(commandName, aliases) { // Clone the template const template = document.getElementById('alias-item-template'); const aliasItem = template.content.cloneNode(true).querySelector('.alias-item'); // Set command name const nameElement = aliasItem.querySelector('.command-name'); nameElement.textContent = commandName; // Add alias tags const aliasTagsList = aliasItem.querySelector('.alias-tags'); aliases.forEach(alias => { const aliasTag = createAliasTag(commandName, alias); aliasTagsList.appendChild(aliasTag); }); return aliasItem; } /** * Create an alias tag element * @param {string} commandName - Original command name * @param {string} alias - Command alias * @returns {HTMLElement} Alias tag element */ function createAliasTag(commandName, alias) { // Clone the template const template = document.getElementById('alias-tag-template'); const aliasTag = template.content.cloneNode(true).querySelector('.alias-tag'); // Set alias name const nameElement = aliasTag.querySelector('.alias-name'); nameElement.textContent = alias; // Add event listener to remove button const removeButton = aliasTag.querySelector('.remove-alias-btn'); removeButton.addEventListener('click', () => { removeAlias(commandName, alias); }); return aliasTag; } /** * Populate command select for aliases * @param {Array} commands - Command names */ function populateCommandSelect(commands) { const commandSelect = document.getElementById('alias-command-select'); commandSelect.innerHTML = ''; // Sort commands alphabetically const sortedCommands = commands.sort(); // Add command options sortedCommands.forEach(commandName => { const option = document.createElement('option'); option.value = commandName; option.textContent = commandName; commandSelect.appendChild(option); }); } /** * Add a command alias */ async function addAlias() { try { // Get input values const commandSelect = document.getElementById('alias-command-select'); const aliasInput = document.getElementById('alias-name-input'); const feedbackElement = document.getElementById('alias-feedback'); const commandName = commandSelect.value; const aliasName = aliasInput.value.trim(); // Validate inputs if (!commandName) { feedbackElement.textContent = 'Please select a command'; feedbackElement.className = 'mt-2 text-danger'; return; } if (!aliasName) { feedbackElement.textContent = 'Please enter an alias name'; feedbackElement.className = 'mt-2 text-danger'; return; } // Validate alias format if (!/^[a-z][a-z0-9_]*$/.test(aliasName) || aliasName.length > 32) { feedbackElement.textContent = 'Alias names must be lowercase, start with a letter, and contain only letters, numbers, and underscores (max 32 characters)'; feedbackElement.className = 'mt-2 text-danger'; return; } // Get the current guild ID const guildId = window.selectedGuildId; // Use global guild ID if (!guildId) { feedbackElement.textContent = 'No guild selected'; feedbackElement.className = 'mt-2 text-danger'; return; } // Prepare request data const requestData = { command_name: commandName, alias_name: aliasName }; // Send request to API await API.post(`/dashboard/api/guilds/${guildId}/command-customizations/aliases`, requestData); // Updated endpoint // Clear input aliasInput.value = ''; // Show success message feedbackElement.textContent = 'Alias added successfully'; feedbackElement.className = 'mt-2 text-success'; // Reload command customizations loadCommandCustomizationData(guildId); // Reload data for the current guild Toast.success('Command alias added successfully'); } catch (error) { console.error('Error adding command alias:', error); const feedbackElement = document.getElementById('alias-feedback'); feedbackElement.textContent = `Failed to add command alias: ${error.message || error}`; feedbackElement.className = 'mt-2 text-danger'; Toast.error('Failed to add command alias'); } } /** * Remove a command alias * @param {string} commandName - Original command name * @param {string} aliasName - Command alias */ async function removeAlias(commandName, aliasName) { try { // Get the current guild ID const guildId = window.selectedGuildId; // Use global guild ID if (!guildId) { Toast.error('No guild selected'); return; } // Prepare request data const requestData = { command_name: commandName, alias_name: aliasName }; // Send request to API await API.delete(`/dashboard/api/guilds/${guildId}/command-customizations/aliases`, requestData); // Updated endpoint // Reload command customizations loadCommandCustomizationData(guildId); // Reload data for the current guild Toast.success('Command alias removed successfully'); } catch (error) { console.error('Error removing command alias:', error); Toast.error(`Failed to remove command alias: ${error.message || error}`); } } /** * Sync commands to Discord */ async function syncCommands() { try { // Get the current guild ID const guildId = window.selectedGuildId; // Use global guild ID if (!guildId) { Toast.error('No guild selected'); return; } // Show feedback const feedbackElement = document.getElementById('sync-feedback'); feedbackElement.textContent = 'Syncing commands...'; feedbackElement.className = 'mt-2 text-info'; // Send request to API await API.post(`/dashboard/api/guilds/${guildId}/sync-commands`); // Updated endpoint // Show success message feedbackElement.textContent = 'Commands synced successfully'; feedbackElement.className = 'mt-2 text-success'; Toast.success('Commands synced successfully'); } catch (error) { console.error('Error syncing commands:', error); // Show error message const feedbackElement = document.getElementById('sync-feedback'); feedbackElement.textContent = `Failed to sync commands: ${error.message || error}`; feedbackElement.className = 'mt-2 text-danger'; Toast.error('Failed to sync commands'); } } /** * Filter commands by search query */ function filterCommands() { const searchQuery = document.getElementById('command-search').value.toLowerCase(); const commandItems = document.querySelectorAll('#command-list .command-item'); commandItems.forEach(item => { const commandName = item.dataset.commandName; if (commandName.includes(searchQuery)) { item.style.display = 'block'; } else { item.style.display = 'none'; } }); }