feat: Implement custom bot configuration dashboard

This commit introduces a new custom bot configuration dashboard, providing a streamlined interface for managing bot settings.

Key changes include:
- **UI/UX Enhancements:**
    - Added new CSS for a modern, responsive dashboard layout, including header, navigation, and form styling.
    - Implemented a dynamic bot status indicator (online/offline).
    - Improved form validation and user feedback mechanisms.
- **Frontend Logic:**
    - Refactored `custom-bot.js` to handle loading, saving, and validating bot settings.
    - Integrated `showFeedback` utility for consistent user notifications.
    - Updated API calls to use `fetch` for better control and error handling.
- **HTML Structure:**
    - Modified `index.html` to incorporate the new dashboard layout and elements.
- **Utility Functions:**
    - Added `showFeedback` function in `utils.js` for displaying success/error messages.

This feature significantly improves the user experience for configuring custom bots.
This commit is contained in:
Slipstream 2025-05-26 15:19:26 -06:00
parent d09a0897c6
commit 0677b7e168
Signed by: slipstream
GPG Key ID: 13E498CE010AC6FD
4 changed files with 526 additions and 624 deletions

View File

@ -468,3 +468,148 @@ input[type="radio"] {
.text-danger {
color: var(--danger-color);
}
/* Custom Bot Configuration Dashboard Styles */
/* Header for the streamlined dashboard */
.header {
background-color: var(--card-bg);
border-bottom: 1px solid var(--border-color);
padding: var(--spacing-4) var(--spacing-6);
display: flex;
justify-content: space-between;
align-items: center;
position: fixed;
top: 0;
left: 0;
right: 0;
z-index: 1000;
box-shadow: var(--shadow-sm);
}
.header-title {
font-size: 1.5rem;
font-weight: 600;
color: var(--primary-color);
margin: 0;
}
.user-info {
display: flex;
align-items: center;
gap: var(--spacing-3);
}
.user-avatar {
width: 40px;
height: 40px;
border-radius: 50%;
background-color: var(--primary-color);
color: white;
display: flex;
align-items: center;
justify-content: center;
font-weight: 600;
font-size: 1.125rem;
}
.user-name {
font-weight: 500;
color: var(--text-primary);
}
/* Bot status indicator */
.w-4 {
width: 1rem;
}
.h-4 {
height: 1rem;
}
.rounded-full {
border-radius: 50%;
}
.bg-gray-400 {
background-color: #9CA3AF;
}
.bg-green-500 {
background-color: var(--success-color);
}
.bg-red-500 {
background-color: var(--danger-color);
}
.bg-yellow-500 {
background-color: var(--warning-color);
}
/* Text colors */
.text-red-500 {
color: var(--danger-color);
}
.text-green-500 {
color: var(--success-color);
}
.text-gray-600 {
color: var(--text-secondary);
}
.text-blue-500 {
color: var(--primary-color);
}
.hover\:underline:hover {
text-decoration: underline;
}
/* List styles for setup guide */
.list-decimal {
list-style-type: decimal;
}
.list-disc {
list-style-type: disc;
}
.pl-5 {
padding-left: 1.25rem;
}
.space-y-4 > * + * {
margin-top: 1rem;
}
/* Responsive design */
@media (max-width: 768px) {
.header {
padding: var(--spacing-3) var(--spacing-4);
}
.header-title {
font-size: 1.25rem;
}
.user-info {
gap: var(--spacing-2);
}
.user-avatar {
width: 32px;
height: 32px;
font-size: 1rem;
}
.content-container {
padding: var(--spacing-4) !important;
}
.btn-group {
flex-direction: column;
}
}

View File

@ -3,7 +3,7 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Bot Dashboard</title>
<title>Custom Bot Configuration Dashboard</title>
<!-- Preload fonts -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
@ -12,16 +12,13 @@
<link rel="stylesheet" href="css/main.css">
<link rel="stylesheet" href="css/components.css">
<link rel="stylesheet" href="css/layout.css">
<link rel="stylesheet" href="css/theme-settings.css">
<link rel="stylesheet" href="css/command-customization.css">
<link rel="stylesheet" href="css/cog-management.css">
</head>
<body>
<!-- Auth Section -->
<div id="auth-section" class="container">
<div class="card mt-6" style="max-width: 500px; margin: 100px auto; text-align: center;">
<h1>Discord Bot Dashboard</h1>
<p class="mb-4">Manage your Discord bot settings and configurations</p>
<h1>Custom Bot Configuration</h1>
<p class="mb-4">Set up and manage your personalized Discord bot</p>
<button id="login-button" class="btn btn-primary btn-lg">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="mr-2"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"></path></svg>
Login with Discord
@ -29,602 +26,161 @@
</div>
</div>
<!-- Server Selection Section (New) -->
<div id="server-select-section" class="container dashboard-section" style="display: none; margin-top: 80px;">
<!-- Bot Configuration Section -->
<div id="bot-config-section" class="container dashboard-section" style="display: none; margin-top: 80px;">
<div class="card">
<div class="card-header">
<h2 class="card-title">Select a Server</h2>
<p class="text-muted">Choose the server you want to manage.</p>
</div>
<div id="server-list-container" class="server-list-grid p-4">
<!-- Server items will be populated by JS -->
<div class="loading-spinner-container">
<div class="loading-spinner"></div>
</div>
<h2 class="card-title">Custom Bot Setup</h2>
<p class="text-muted">Configure your personalized Discord bot with your own token and settings.</p>
</div>
</div>
</div>
<!-- Dashboard Section (Initially hidden until server selected) -->
<!-- Main Dashboard Section -->
<div id="dashboard-container" class="dashboard-container" style="display: none;">
<!-- Sidebar -->
<div id="sidebar" class="sidebar">
<div class="sidebar-header">
<a href="#" class="sidebar-logo">Bot Dashboard</a>
</div>
<div class="sidebar-nav">
<a href="#server-settings" class="nav-item active" data-section="server-settings-section">
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="nav-icon"><rect x="2" y="3" width="20" height="14" rx="2" ry="2"></rect><line x1="8" y1="21" x2="16" y2="21"></line><line x1="12" y1="17" x2="12" y2="21"></line></svg>
Server Settings
</a>
<a href="#welcome-module" class="nav-item" data-section="welcome-module-section">
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="nav-icon"><path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"></path><polyline points="9 22 9 12 15 12 15 22"></polyline></svg>
Welcome/Leave
</a>
<a href="#modules-settings" class="nav-item" data-section="modules-settings-section">
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="nav-icon"><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"></path><rect x="8" y="2" width="8" height="4" rx="1" ry="1"></rect></svg>
Modules
</a>
<a href="#cog-management" class="nav-item" data-section="cog-management-section" id="nav-cog-management">
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="nav-icon"><circle cx="12" cy="12" r="3"></circle><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"></path></svg>
Cog Management
</a>
<a href="#permissions-settings" class="nav-item" data-section="permissions-settings-section">
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="nav-icon"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"></path></svg>
Permissions
</a>
<a href="#theme-settings" class="nav-item" data-section="theme-settings-section" id="nav-theme-settings">
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="nav-icon"><circle cx="12" cy="12" r="3"></circle><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"></path></svg>
Theme Settings
</a>
<a href="#custom-bot" class="nav-item" data-section="custom-bot-section" id="nav-custom-bot">
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="nav-icon"><rect x="3" y="11" width="18" height="11" rx="2" ry="2"></rect><path d="M7 11V7a5 5 0 0 1 10 0v4"></path></svg>
Custom Bot
</a>
<a href="#command-customization" class="nav-item" data-section="command-customization-section">
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="nav-icon"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"></path><polyline points="15 3 21 3 21 9"></polyline><line x1="10" y1="14" x2="21" y2="3"></line></svg>
Command Customization
</a>
</div>
<div class="sidebar-footer">
<button id="logout-button" class="btn btn-danger w-full">
<!-- Header -->
<header class="header">
<h1 class="header-title">Custom Bot Configuration</h1>
<div class="user-info">
<div id="user-avatar" class="user-avatar">U</div>
<span id="username" class="user-name">User</span>
<button id="logout-button" class="btn btn-danger ml-4">
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="mr-2"><path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"></path><polyline points="16 17 21 12 16 7"></polyline><line x1="21" y1="12" x2="9" y2="12"></line></svg>
Logout
</button>
</div>
</div>
</header>
<!-- Main Content -->
<div class="main-content">
<!-- Header -->
<header class="header">
<button id="sidebar-toggle" class="sidebar-toggle">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="3" y1="12" x2="21" y2="12"></line><line x1="3" y1="6" x2="21" y2="6"></line><line x1="3" y1="18" x2="21" y2="18"></line></svg>
</button>
<h1 class="header-title">Discord Bot Dashboard</h1>
<div class="user-info">
<div id="user-avatar" class="user-avatar">U</div>
<span id="username" class="user-name">User</span>
</div>
</header>
<!-- Content Container -->
<div class="content-container" style="margin-top: 80px; padding: 20px;">
<!-- Content Container -->
<div class="content-container" style="margin-top: 80px;">
<!-- Server Settings Section -->
<div id="server-settings-section" class="dashboard-section">
<!-- Bot Status Section -->
<div class="card">
<div class="card-header">
<h2 class="card-title">Manage Server Settings</h2>
<p class="text-muted">Settings for the selected server.</p> <!-- Added description -->
<h2 class="card-title">Bot Status</h2>
<p class="text-muted">Current status of your custom Discord bot</p>
</div>
<!-- Removed guild-select dropdown -->
</div>
<div id="settings-form"> <!-- Display block by default now, JS will load content -->
<div class="card">
<div class="card-header">
<h3 class="card-title">Prefix Settings</h3>
<div class="card-body">
<div id="bot-status-indicator" class="flex items-center mb-4">
<div id="status-dot" class="w-4 h-4 rounded-full bg-gray-400 mr-2"></div>
<span id="status-text">Checking status...</span>
</div>
<div class="form-group">
<label for="prefix-input">Command Prefix:</label>
<div class="flex gap-2">
<input type="text" id="prefix-input" name="prefix" maxlength="10" class="w-full">
<button id="save-prefix-button" class="btn btn-primary">
Save Prefix
</button>
</div>
<p id="prefix-feedback" class="mt-2"></p>
<div id="bot-controls" class="flex gap-2">
<button id="start-bot-button" class="btn btn-primary" disabled>Start Bot</button>
<button id="stop-bot-button" class="btn btn-danger" disabled>Stop Bot</button>
</div>
<div id="bot-error" class="mt-4 text-red-500 hidden"></div>
</div>
</div>
</div>
<!-- Welcome/Leave Module Section -->
<div id="welcome-module-section" class="dashboard-section" style="display: none;">
<!-- Bot Configuration Section -->
<div class="card">
<div class="card-header">
<h2 class="card-title">Welcome & Leave Messages</h2>
<h2 class="card-title">Bot Configuration</h2>
<p class="text-muted">Configure your custom Discord bot settings</p>
</div>
</div>
<div id="welcome-settings-form" style="display: none;">
<div class="card">
<div class="card-header">
<h3 class="card-title">Welcome Messages</h3>
<div class="card-body">
<div class="form-group">
<label for="bot-token-input">Bot Token:</label>
<input type="password" id="bot-token-input" class="w-full" placeholder="Enter your Discord bot token">
<p class="text-sm text-gray-600 mt-1">Your bot token is stored securely and never shared.</p>
</div>
<div class="form-group">
<label for="welcome-channel">Welcome Channel:</label>
<div class="flex gap-2">
<input type="text" id="welcome-channel" name="welcome_channel_id" placeholder="Enter Channel ID" class="w-full">
<select id="welcome-channel-select" class="w-full">
<option value="">-- Select Channel --</option>
<!-- Will be populated by JS -->
</select>
</div>
<label for="bot-prefix-input">Command Prefix:</label>
<input type="text" id="bot-prefix-input" class="w-full" placeholder="!" maxlength="5">
<p class="text-sm text-gray-600 mt-1">The prefix users will type before commands (e.g., !help).</p>
</div>
<div class="form-group">
<label for="welcome-message">Welcome Message Template:</label>
<textarea id="welcome-message" name="welcome_message" rows="4" placeholder="Use {user} for mention, {username} for name, {server} for server name." class="w-full"></textarea>
</div>
<div class="btn-group">
<button id="save-welcome-button" class="btn btn-primary">Save Welcome Settings</button>
<button id="disable-welcome-button" class="btn btn-warning">Disable Welcome</button>
<button id="test-welcome-button" class="btn btn-secondary">Test Welcome Message</button>
</div>
<p id="welcome-feedback" class="mt-2"></p>
</div>
<div class="card">
<div class="card-header">
<h3 class="card-title">Goodbye Messages</h3>
</div>
<div class="form-group">
<label for="goodbye-channel">Goodbye Channel:</label>
<div class="flex gap-2">
<input type="text" id="goodbye-channel" name="goodbye_channel_id" placeholder="Enter Channel ID" class="w-full">
<select id="goodbye-channel-select" class="w-full">
<option value="">-- Select Channel --</option>
<!-- Will be populated by JS -->
</select>
</div>
</div>
<div class="form-group">
<label for="goodbye-message">Goodbye Message Template:</label>
<textarea id="goodbye-message" name="goodbye_message" rows="4" placeholder="Use {username} for name, {server} for server name." class="w-full"></textarea>
</div>
<div class="btn-group">
<button id="save-goodbye-button" class="btn btn-primary">Save Goodbye Settings</button>
<button id="disable-goodbye-button" class="btn btn-warning">Disable Goodbye</button>
<button id="test-goodbye-button" class="btn btn-secondary">Test Goodbye Message</button>
</div>
<p id="goodbye-feedback" class="mt-2"></p>
</div>
</div>
</div>
<!-- Modules Settings Section -->
<div id="modules-settings-section" class="dashboard-section" style="display: none;">
<div class="card">
<div class="card-header">
<h2 class="card-title">Manage Bot Modules</h2>
</div>
</div>
<div id="modules-settings-form" style="display: none;">
<div class="card">
<div class="card-header">
<h3 class="card-title">Enabled Modules (Cogs)</h3>
</div>
<div class="cogs-container p-4 border rounded mb-4">
<div id="cogs-list">
<!-- Cog checkboxes will be populated by JS -->
<div class="loading-spinner-container">
<div class="loading-spinner"></div>
</div>
</div>
</div>
<div class="btn-group">
<button id="save-cogs-button" class="btn btn-primary">Save Module Settings</button>
</div>
<p id="cogs-feedback" class="mt-2"></p>
</div>
</div>
</div>
<!-- Permissions Settings Section -->
<div id="permissions-settings-section" class="dashboard-section" style="display: none;">
<div class="card">
<div class="card-header">
<h2 class="card-title">Command Permissions</h2>
</div>
</div>
<div id="permissions-settings-form" style="display: none;">
<div class="card">
<div class="card-header">
<h3 class="card-title">Command Permissions</h3>
</div>
<div class="form-group">
<label for="command-select">Command:</label>
<select id="command-select" class="w-full">
<!-- Will be populated by JS -->
<option value="">-- Select Command --</option>
<label for="bot-status-type-select">Status Type:</label>
<select id="bot-status-type-select" class="w-full">
<option value="playing">Playing</option>
<option value="listening">Listening to</option>
<option value="watching">Watching</option>
<option value="competing">Competing in</option>
</select>
</div>
<div class="form-group">
<label for="role-select">Role:</label>
<select id="role-select" class="w-full">
<!-- Will be populated by JS -->
<option value="">-- Select Role --</option>
</select>
<label for="bot-status-text-input">Status Text:</label>
<input type="text" id="bot-status-text-input" class="w-full" placeholder="!help" maxlength="128">
<p class="text-sm text-gray-600 mt-1">The text that will appear in your bot's status.</p>
</div>
<div class="btn-group">
<button id="add-perm-button" class="btn btn-success">Allow Role</button>
<button id="remove-perm-button" class="btn btn-danger">Disallow Role</button>
<button id="save-bot-config-button" class="btn btn-primary">Save Configuration</button>
</div>
<div id="current-perms" class="mt-4 p-4 border rounded">
<!-- Current permissions will be listed here -->
<div class="text-gray">No permissions set yet.</div>
</div>
<p id="perms-feedback" class="mt-2"></p>
<p id="bot-config-feedback" class="mt-2"></p>
</div>
</div>
</div>
<!-- AI Settings Section Removed -->
<!-- Include Theme Settings Section -->
<div id="theme-settings-template" style="display: none;">
<!-- This template will be used to restore the form after loading -->
<div class="card">
<div class="card-header">
<h3 class="card-title">Theme Mode</h3>
</div>
<div class="form-group">
<div class="radio-group">
<input type="radio" id="theme-mode-light" name="theme_mode" value="light" checked>
<label for="theme-mode-light">Light Mode</label>
</div>
<div class="radio-group">
<input type="radio" id="theme-mode-dark" name="theme_mode" value="dark">
<label for="theme-mode-dark">Dark Mode</label>
</div>
<div class="radio-group">
<input type="radio" id="theme-mode-custom" name="theme_mode" value="custom">
<label for="theme-mode-custom">Custom Mode</label>
</div>
</div>
</div>
<div id="custom-theme-settings" class="card" style="display: none;">
<div class="card-header">
<h3 class="card-title">Custom Colors</h3>
</div>
<div class="form-group">
<label for="primary-color">Primary Color:</label>
<div class="color-picker-container">
<input type="color" id="primary-color" value="#5865F2">
<input type="text" id="primary-color-text" value="#5865F2" class="color-text-input">
</div>
</div>
<div class="form-group">
<label for="secondary-color">Secondary Color:</label>
<div class="color-picker-container">
<input type="color" id="secondary-color" value="#2D3748">
<input type="text" id="secondary-color-text" value="#2D3748" class="color-text-input">
</div>
</div>
<div class="form-group">
<label for="accent-color">Accent Color:</label>
<div class="color-picker-container">
<input type="color" id="accent-color" value="#7289DA">
<input type="text" id="accent-color-text" value="#7289DA" class="color-text-input">
</div>
</div>
<div class="form-group">
<label for="font-family">Font Family:</label>
<select id="font-family" class="w-full">
<option value="Inter, sans-serif">Inter</option>
<option value="'Roboto', sans-serif">Roboto</option>
<option value="'Open Sans', sans-serif">Open Sans</option>
<option value="'Montserrat', sans-serif">Montserrat</option>
<option value="'Poppins', sans-serif">Poppins</option>
</select>
</div>
<div class="form-group">
<label for="custom-css">Custom CSS (Advanced):</label>
<textarea id="custom-css" rows="6" class="w-full" placeholder="Enter custom CSS here..."></textarea>
<small class="text-muted">Custom CSS will be applied to the dashboard. Use with caution.</small>
</div>
</div>
<div class="card">
<div class="card-header">
<h3 class="card-title">Theme Preview</h3>
</div>
<div id="theme-preview" class="theme-preview">
<div class="preview-header">
<div class="preview-title">Header</div>
<div class="preview-button">Button</div>
</div>
<div class="preview-content">
<div class="preview-card">
<div class="preview-card-header">Card Title</div>
<div class="preview-card-body">
<p>This is a preview of how your theme will look.</p>
<div class="preview-form-control"></div>
<div class="preview-button-primary">Primary Button</div>
<div class="preview-button-secondary">Secondary Button</div>
</div>
</div>
</div>
</div>
</div>
<div class="card">
<div class="btn-group">
<button id="save-theme-settings-button" class="btn btn-primary">Save Theme Settings</button>
<button id="reset-theme-settings-button" class="btn btn-warning">Reset to Defaults</button>
</div>
<p id="theme-settings-feedback" class="mt-2"></p>
</div>
<!-- Bot Setup Guide Section -->
<div class="card">
<div class="card-header">
<h2 class="card-title">How to Create Your Discord Bot</h2>
<p class="text-muted">Follow these steps to set up your custom Discord bot</p>
</div>
<!-- Theme Settings Section -->
<div id="theme-settings-section" class="dashboard-section" style="display: none;">
<div class="card">
<div class="card-header">
<h2 class="card-title">Theme Settings</h2>
</div>
</div>
<div id="theme-settings-form">
<!-- Will be populated by JS -->
<div class="loading-spinner-container">
<div class="loading-spinner"></div>
</div>
</div>
</div>
<!-- Include Cog Management Section -->
<!-- Cog Management Section -->
<div id="cog-management-section" class="dashboard-section" style="display: none;">
<div class="card">
<div class="card-header">
<h2 class="card-title">Manage Cogs & Commands</h2>
<p class="text-muted">Enable/disable modules and commands for the selected server.</p> <!-- Added description -->
</div>
<!-- Removed cog-guild-select dropdown -->
</div>
<div id="cog-management-loading" class="loading-container">
<div class="loading-spinner"></div>
<p>Loading cogs and commands...</p>
</div>
<div id="cog-management-content" style="display: none;">
<div class="card">
<div class="card-header">
<h3 class="card-title">Cogs (Modules)</h3>
<p class="text-sm text-muted">Enable or disable entire modules of functionality</p>
</div>
<div class="cogs-list-container p-4">
<div id="cogs-list" class="grid grid-cols-1 md:grid-cols-2 gap-4">
<!-- Cogs will be populated here -->
</div>
</div>
<div class="btn-group mt-4">
<button id="save-cogs-button" class="btn btn-primary">Save Cog Settings</button>
</div>
<p id="cogs-feedback" class="mt-2"></p>
</div>
<div class="card mt-6">
<div class="card-header">
<h3 class="card-title">Commands</h3>
<p class="text-sm text-muted">Enable or disable individual commands</p>
</div>
<div class="form-group">
<label for="cog-filter">Filter by Cog:</label>
<select id="cog-filter" class="w-full">
<option value="all">All Cogs</option>
<!-- Cog options will be populated here -->
</select>
</div>
<div class="commands-list-container p-4">
<div id="commands-list" class="grid grid-cols-1 md:grid-cols-2 gap-4">
<!-- Commands will be populated here -->
</div>
</div>
<div class="btn-group mt-4">
<button id="save-commands-button" class="btn btn-primary">Save Command Settings</button>
</div>
<p id="commands-feedback" class="mt-2"></p>
</div>
</div>
</div>
<!-- Include Command Customization Section -->
<div id="command-customization-section" class="dashboard-section" style="display: none;">
<div class="card">
<div class="card-header">
<h2 class="card-title">Command Customization</h2>
</div>
</div>
<div id="command-customization-form">
<!-- Command Customization Card -->
<div class="card">
<div class="card-header">
<h3 class="card-title">Customize Commands</h3>
<p class="text-muted">Customize the names and descriptions of commands for your server.</p>
</div>
<div class="form-group">
<div class="search-container">
<input type="text" id="command-search" placeholder="Search commands..." class="w-full">
</div>
</div>
<div id="command-list" class="command-list">
<div class="loading-spinner-container">
<div class="loading-spinner"></div>
</div>
</div>
</div>
<!-- Command Group Customization Card -->
<div class="card">
<div class="card-header">
<h3 class="card-title">Customize Command Groups</h3>
<p class="text-muted">Customize the names of command groups for your server.</p>
</div>
<div id="group-list" class="command-list">
<div class="loading-spinner-container">
<div class="loading-spinner"></div>
</div>
</div>
</div>
<!-- Command Aliases Card -->
<div class="card">
<div class="card-header">
<h3 class="card-title">Command Aliases</h3>
<p class="text-muted">Add alternative names for commands.</p>
</div>
<div id="alias-list" class="command-list">
<div class="loading-spinner-container">
<div class="loading-spinner"></div>
</div>
</div>
<div class="form-group mt-4">
<h4>Add New Alias</h4>
<div class="flex-row">
<div class="flex-col mr-2">
<label for="alias-command-select">Command:</label>
<select id="alias-command-select" class="w-full">
<option value="">Select a command</option>
</select>
</div>
<div class="flex-col">
<label for="alias-name-input">Alias:</label>
<input type="text" id="alias-name-input" placeholder="Enter alias name" class="w-full">
</div>
</div>
<button id="add-alias-button" class="btn btn-primary mt-2">Add Alias</button>
<p id="alias-feedback" class="mt-2"></p>
</div>
</div>
<!-- Sync Commands Card -->
<div class="card">
<div class="card-header">
<h3 class="card-title">Sync Commands</h3>
<p class="text-muted">Sync command customizations to Discord.</p>
</div>
<div class="form-group">
<p>After making changes to command names, descriptions, or aliases, you need to sync the changes to Discord.</p>
<button id="sync-commands-button" class="btn btn-primary">Sync Commands</button>
<p id="sync-feedback" class="mt-2"></p>
</div>
</div>
</div>
</div>
<!-- Include Custom Bot Section -->
<div id="custom-bot-section" class="dashboard-section" style="display: none;">
<!-- This will be loaded from custom-bot.html -->
<div class="loading-spinner-container">
<div class="loading-spinner"></div>
</div>
</div>
<!-- Command Customization Templates -->
<template id="command-item-template">
<div class="command-item">
<div class="command-header">
<h4 class="command-name"></h4>
<div class="command-actions">
<button class="btn btn-sm btn-primary edit-command-btn">Edit</button>
<button class="btn btn-sm btn-warning reset-command-btn">Reset</button>
</div>
</div>
<div class="command-details">
<p class="command-description"></p>
<div class="command-customization" style="display: none;">
<div class="form-group">
<label>Custom Name:</label>
<input type="text" class="custom-command-name w-full" placeholder="Enter custom name">
</div>
<div class="form-group">
<label>Custom Description:</label>
<input type="text" class="custom-command-description w-full" placeholder="Enter custom description">
</div>
<div class="btn-group">
<button class="btn btn-sm btn-primary save-command-btn">Save</button>
<button class="btn btn-sm btn-secondary cancel-command-btn">Cancel</button>
</div>
</div>
</div>
</div>
</template>
<template id="group-item-template">
<div class="command-item">
<div class="command-header">
<h4 class="group-name"></h4>
<div class="command-actions">
<button class="btn btn-sm btn-primary edit-group-btn">Edit</button>
<button class="btn btn-sm btn-warning reset-group-btn">Reset</button>
</div>
</div>
<div class="group-details">
<div class="group-customization" style="display: none;">
<div class="form-group">
<label>Custom Name:</label>
<input type="text" class="custom-group-name w-full" placeholder="Enter custom name">
</div>
<div class="btn-group">
<button class="btn btn-sm btn-primary save-group-btn">Save</button>
<button class="btn btn-sm btn-secondary cancel-group-btn">Cancel</button>
</div>
</div>
</div>
</div>
</template>
<template id="alias-item-template">
<div class="alias-item">
<div class="alias-header">
<h4 class="command-name"></h4>
</div>
<div class="alias-list">
<ul class="alias-tags">
<!-- Alias tags will be added here -->
<div class="card-body">
<ol class="list-decimal pl-5 space-y-4">
<li>
<strong>Create a Discord Application:</strong>
<p>Go to the <a href="https://discord.com/developers/applications" target="_blank" class="text-blue-500 hover:underline">Discord Developer Portal</a> and click "New Application".</p>
</li>
<li>
<strong>Set Up Your Bot:</strong>
<p>In your application, go to the "Bot" tab and click "Add Bot".</p>
</li>
<li>
<strong>Customize Your Bot:</strong>
<p>Upload a profile picture and set a username for your bot.</p>
</li>
<li>
<strong>Get Your Bot Token:</strong>
<p>Click "Reset Token" to generate a new token, then copy it.</p>
<p class="text-red-500">IMPORTANT: Never share your bot token with anyone!</p>
</li>
<li>
<strong>Set Bot Permissions:</strong>
<p>In the "Bot" tab, under "Privileged Gateway Intents", enable:</p>
<ul class="list-disc pl-5">
<li>Presence Intent</li>
<li>Server Members Intent</li>
<li>Message Content Intent</li>
</ul>
</div>
</div>
</template>
<template id="alias-tag-template">
<li class="alias-tag">
<span class="alias-name"></span>
<button class="remove-alias-btn">×</button>
</li>
</template>
</li>
<li>
<strong>Invite Your Bot:</strong>
<p>Go to the "OAuth2" tab, then "URL Generator". Select the following scopes:</p>
<ul class="list-disc pl-5">
<li>bot</li>
<li>applications.commands</li>
</ul>
<p>Then select the following bot permissions:</p>
<ul class="list-disc pl-5">
<li>Send Messages</li>
<li>Embed Links</li>
<li>Attach Files</li>
<li>Read Message History</li>
<li>Use Slash Commands</li>
<li>Add Reactions</li>
</ul>
<p>Copy the generated URL and open it in your browser to invite the bot to your server.</p>
</li>
<li>
<strong>Configure Your Bot:</strong>
<p>Paste your bot token in the configuration form above and save it.</p>
</li>
<li>
<strong>Start Your Bot:</strong>
<p>Click the "Start Bot" button to bring your bot online!</p>
</li>
</ol>
</div>
</div>
</div>
</div>
</div>
<!-- JavaScript files -->
<script src="js/utils.js"></script>
<script src="js/main.js"></script>
<!-- <script src="js/ai-settings.js"></script> --> <!-- Removed AI settings script -->
<script src="js/theme-settings.js"></script>
<script src="js/custom-bot.js"></script>
<script src="js/command-customization.js"></script>
<script src="js/cog-management.js"></script>
<!-- JavaScript files -->
<script src="js/utils.js"></script>
<script src="js/custom-bot.js"></script>
</body>
</html>

View File

@ -1,6 +1,6 @@
/**
* Custom Bot Management
* This module handles the custom bot functionality in the dashboard.
* Custom Bot Configuration Dashboard
* Streamlined interface for bot setup and management
*/
// Status constants
@ -22,11 +22,14 @@ let stopBotButton;
let statusDot;
let statusText;
let botError;
let botConfigFeedback;
/**
* Initialize the custom bot functionality
*/
function initCustomBot() {
console.log('Initializing custom bot dashboard...');
// Get DOM elements
botTokenInput = document.getElementById('bot-token-input');
botPrefixInput = document.getElementById('bot-prefix-input');
@ -38,6 +41,7 @@ function initCustomBot() {
statusDot = document.getElementById('status-dot');
statusText = document.getElementById('status-text');
botError = document.getElementById('bot-error');
botConfigFeedback = document.getElementById('bot-config-feedback');
// Add event listeners
if (saveBotConfigButton) {
@ -65,27 +69,43 @@ function initCustomBot() {
*/
async function loadCustomBotSettings() {
try {
const response = await API.get('/dashboard/api/settings');
console.log('Loading custom bot settings...');
const response = await fetch('/dashboard/api/settings', {
method: 'GET',
credentials: 'include',
headers: {
'Content-Type': 'application/json'
}
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
console.log('Loaded bot settings:', data);
// Fill in the form fields
if (botTokenInput && response.custom_bot_token) {
botTokenInput.value = response.custom_bot_token;
if (botTokenInput && data.custom_bot_token) {
botTokenInput.value = data.custom_bot_token;
}
if (botPrefixInput) {
botPrefixInput.value = response.custom_bot_prefix || '!';
botPrefixInput.value = data.custom_bot_prefix || '!';
}
if (botStatusTypeSelect) {
botStatusTypeSelect.value = response.custom_bot_status_type || 'listening';
botStatusTypeSelect.value = data.custom_bot_status_type || 'listening';
}
if (botStatusTextInput) {
botStatusTextInput.value = response.custom_bot_status_text || '!help';
botStatusTextInput.value = data.custom_bot_status_text || '!help';
}
showFeedback('Bot settings loaded successfully', false);
} catch (error) {
console.error('Error loading custom bot settings:', error);
Toast.error('Failed to load custom bot settings');
showFeedback('Failed to load custom bot settings: ' + error.message, true);
}
}
@ -94,44 +114,58 @@ async function loadCustomBotSettings() {
*/
async function saveCustomBotConfig() {
try {
console.log('Saving custom bot configuration...');
// Validate inputs
if (!botTokenInput.value) {
Toast.error('Bot token is required');
if (!botTokenInput.value.trim()) {
showFeedback('Bot token is required', true);
return;
}
if (!botPrefixInput.value) {
Toast.error('Command prefix is required');
if (!botPrefixInput.value.trim()) {
showFeedback('Command prefix is required', true);
return;
}
if (!botStatusTextInput.value) {
Toast.error('Status text is required');
if (!botStatusTextInput.value.trim()) {
showFeedback('Status text is required', true);
return;
}
// Get current settings first
const currentSettings = await API.get('/dashboard/api/settings');
// Prepare the settings object with updated bot settings
// Prepare the settings object
const settings = {
...currentSettings,
custom_bot_token: botTokenInput.value,
custom_bot_prefix: botPrefixInput.value,
custom_bot_token: botTokenInput.value.trim(),
custom_bot_prefix: botPrefixInput.value.trim(),
custom_bot_status_type: botStatusTypeSelect.value,
custom_bot_status_text: botStatusTextInput.value
custom_bot_status_text: botStatusTextInput.value.trim()
};
// Save the settings
await API.put('/dashboard/api/settings', settings);
console.log('Sending bot configuration:', settings);
Toast.success('Custom bot configuration saved successfully');
// Save the settings
const response = await fetch('/dashboard/api/settings', {
method: 'PUT',
credentials: 'include',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(settings)
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const result = await response.json();
console.log('Bot configuration saved:', result);
showFeedback('Custom bot configuration saved successfully', false);
// Check bot status after saving
checkBotStatus();
setTimeout(checkBotStatus, 1000);
} catch (error) {
console.error('Error saving custom bot configuration:', error);
Toast.error('Failed to save custom bot configuration');
showFeedback('Failed to save custom bot configuration: ' + error.message, true);
}
}
@ -140,18 +174,32 @@ async function saveCustomBotConfig() {
*/
async function startCustomBot() {
try {
console.log('Starting custom bot...');
startBotButton.disabled = true;
startBotButton.textContent = 'Starting...';
await API.post('/dashboard/api/custom-bot/start');
const response = await fetch('/dashboard/api/custom-bot/start', {
method: 'POST',
credentials: 'include',
headers: {
'Content-Type': 'application/json'
}
});
Toast.success('Custom bot started successfully');
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const result = await response.json();
console.log('Bot start result:', result);
showFeedback('Custom bot started successfully', false);
// Check bot status after starting
checkBotStatus();
setTimeout(checkBotStatus, 2000);
} catch (error) {
console.error('Error starting custom bot:', error);
Toast.error('Failed to start custom bot: ' + (error.response?.data?.detail || error.message));
showFeedback('Failed to start custom bot: ' + error.message, true);
// Re-enable the button
startBotButton.disabled = false;
@ -164,18 +212,32 @@ async function startCustomBot() {
*/
async function stopCustomBot() {
try {
console.log('Stopping custom bot...');
stopBotButton.disabled = true;
stopBotButton.textContent = 'Stopping...';
await API.post('/dashboard/api/custom-bot/stop');
const response = await fetch('/dashboard/api/custom-bot/stop', {
method: 'POST',
credentials: 'include',
headers: {
'Content-Type': 'application/json'
}
});
Toast.success('Custom bot stopped successfully');
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const result = await response.json();
console.log('Bot stop result:', result);
showFeedback('Custom bot stopped successfully', false);
// Check bot status after stopping
checkBotStatus();
setTimeout(checkBotStatus, 2000);
} catch (error) {
console.error('Error stopping custom bot:', error);
Toast.error('Failed to stop custom bot: ' + (error.response?.data?.detail || error.message));
showFeedback('Failed to stop custom bot: ' + error.message, true);
// Re-enable the button
stopBotButton.disabled = false;
@ -188,17 +250,30 @@ async function stopCustomBot() {
*/
async function checkBotStatus() {
try {
const response = await API.get('/dashboard/api/custom-bot/status');
const response = await fetch('/dashboard/api/custom-bot/status', {
method: 'GET',
credentials: 'include',
headers: {
'Content-Type': 'application/json'
}
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const status = await response.json();
console.log('Bot status:', status);
// Update the status indicator
updateStatusIndicator(response);
updateStatusIndicator(status);
// Update button states
updateButtonStates(response);
updateButtonStates(status);
// Show error if any
if (response.error && botError) {
botError.textContent = response.error;
if (status.error && botError) {
botError.textContent = status.error;
botError.classList.remove('hidden');
} else if (botError) {
botError.classList.add('hidden');
@ -271,5 +346,29 @@ function updateButtonStates(status) {
}
}
/**
* Show feedback message to user
*/
function showFeedback(message, isError = false) {
if (botConfigFeedback) {
botConfigFeedback.textContent = message;
botConfigFeedback.className = isError ? 'mt-2 text-red-500' : 'mt-2 text-green-500';
// Clear feedback after 5 seconds
setTimeout(() => {
if (botConfigFeedback) {
botConfigFeedback.textContent = '';
botConfigFeedback.className = 'mt-2';
}
}, 5000);
}
}
// Initialize when DOM is loaded
document.addEventListener('DOMContentLoaded', function() {
console.log('DOM loaded, initializing custom bot dashboard...');
initCustomBot();
});
// Export the initialization function
window.initCustomBot = initCustomBot;

View File

@ -661,9 +661,111 @@ const DOM = {
}
};
// Authentication utilities
const Auth = {
async checkAuth() {
try {
const response = await fetch('/dashboard/api/user', {
method: 'GET',
credentials: 'include'
});
if (response.ok) {
const user = await response.json();
console.log('User authenticated:', user);
return user;
} else {
console.log('User not authenticated');
return null;
}
} catch (error) {
console.error('Auth check failed:', error);
return null;
}
},
redirectToLogin() {
window.location.href = '/dashboard/api/auth/discord';
},
async logout() {
try {
await fetch('/dashboard/api/auth/logout', {
method: 'POST',
credentials: 'include'
});
window.location.reload();
} catch (error) {
console.error('Logout failed:', error);
window.location.reload();
}
}
};
// Main dashboard initialization
async function initDashboard() {
console.log('Initializing dashboard...');
const authSection = document.getElementById('auth-section');
const botConfigSection = document.getElementById('bot-config-section');
const dashboardContainer = document.getElementById('dashboard-container');
const loginButton = document.getElementById('login-button');
const logoutButton = document.getElementById('logout-button');
const usernameSpan = document.getElementById('username');
const userAvatar = document.getElementById('user-avatar');
// Check authentication
const user = await Auth.checkAuth();
if (user) {
// User is authenticated, show dashboard
console.log('User authenticated, showing dashboard');
DOM.hide(authSection);
DOM.hide(botConfigSection);
DOM.show(dashboardContainer);
// Update user info
if (usernameSpan) {
usernameSpan.textContent = user.username || 'User';
}
if (userAvatar) {
userAvatar.textContent = (user.username || 'U').charAt(0).toUpperCase();
}
} else {
// User not authenticated, show login
console.log('User not authenticated, showing login');
DOM.show(authSection);
DOM.hide(botConfigSection);
DOM.hide(dashboardContainer);
}
// Add event listeners
if (loginButton) {
loginButton.addEventListener('click', () => {
console.log('Login button clicked');
Auth.redirectToLogin();
});
}
if (logoutButton) {
logoutButton.addEventListener('click', () => {
console.log('Logout button clicked');
Auth.logout();
});
}
}
// Initialize when DOM is loaded
document.addEventListener('DOMContentLoaded', function() {
console.log('DOM loaded, initializing dashboard...');
initDashboard();
});
// Export utilities
window.Toast = Toast;
window.API = API;
window.Modal = Modal;
window.Form = Form;
window.DOM = DOM;
window.Auth = Auth;
window.initDashboard = initDashboard;