refactor: przeniesienie konfiguracji kanału logów do dedykowanego modalu Ustawienia z wybierakiem per-serwer

This commit is contained in:
mmacierzanka committed 2026-07-04 15:53:03 +02:00
1 parent 89137f7d1c
commit dcbd7859d7
5 files changed
+269 -51

No files matched your search

+75 -34
View File
@@ -16,8 +16,10 @@ let wsConnectedOnce = false;
// DOM Elements
const statusDot = document.getElementById('status-dot');
const statusText = document.getElementById('status-text');
const voiceSelect = document.getElementById('voice-channel-select');
const textChannelSelect = document.getElementById('text-channel-select');
const btnOpenSettings = document.getElementById('btn-open-settings');
const btnCloseSettings = document.getElementById('btn-close-settings');
const settingsModal = document.getElementById('settings-modal');
const settingsGuildsList = document.getElementById('settings-guilds-list');
const btnConnect = document.getElementById('btn-connect');
const trackTitle = document.getElementById('track-title');
const trackArtist = document.getElementById('track-artist');
@@ -162,8 +164,14 @@ function bindEvents() {
// Add Song Form
addForm.addEventListener('submit', addTrack);
// Text Channel Select
textChannelSelect.addEventListener('change', changeTextChannel);
// Settings Modal Events
if (btnOpenSettings) btnOpenSettings.addEventListener('click', openSettingsModal);
if (btnCloseSettings) btnCloseSettings.addEventListener('click', closeSettingsModal);
if (settingsModal) {
settingsModal.addEventListener('click', (e) => {
if (e.target === settingsModal) closeSettingsModal();
});
}
// Upload Events
if (btnSelectFile && fileInput) {
@@ -296,30 +304,9 @@ function updateUI(status) {
}
}
// Text selector population (only if select doesn't have focus/interaction)
if (document.activeElement !== textChannelSelect) {
const currentSelectedText = textChannelSelect.value;
textChannelSelect.innerHTML = '<option value="" disabled>Wybierz kanał...</option>';
if (status.text_channels && status.text_channels.length > 0) {
status.text_channels.forEach(channel => {
const opt = document.createElement('option');
opt.value = channel.channel_id;
opt.textContent = `${channel.guild_name} - #${channel.channel_name}`;
textChannelSelect.appendChild(opt);
});
if (status.active_text_channel_id) {
textChannelSelect.value = status.active_text_channel_id;
} else if (currentSelectedText && status.text_channels.some(c => c.channel_id === currentSelectedText)) {
textChannelSelect.value = currentSelectedText;
}
} else {
const opt = document.createElement('option');
opt.value = "";
opt.textContent = "Brak dostępnych kanałów";
textChannelSelect.appendChild(opt);
}
// Settings Guilds Population
if (status.guilds) {
renderSettingsGuilds(status.guilds);
}
// 2. Playback State
@@ -753,19 +740,73 @@ async function setVolume(vol) {
}
}
// Change Text Notification Channel
async function changeTextChannel() {
const channelId = textChannelSelect.value;
if (!channelId) return;
// Settings Modal Functions
function openSettingsModal() {
if (settingsModal) settingsModal.style.display = 'flex';
}
function closeSettingsModal() {
if (settingsModal) settingsModal.style.display = 'none';
}
function renderSettingsGuilds(guilds) {
if (!settingsGuildsList) return;
// Don't re-render if user is currently interacting with a select inside settings
if (settingsModal && settingsModal.style.display !== 'none' && document.activeElement && document.activeElement.classList.contains('guild-log-select')) {
return;
}
if (!guilds || guilds.length === 0) {
settingsGuildsList.innerHTML = '<div class="empty-queue-message"><i data-lucide="server"></i><p>Brak serwerów Discord w pamięci bota.</p></div>';
lucide.createIcons();
return;
}
let html = '';
guilds.forEach(guild => {
let optionsHtml = '';
if (guild.text_channels && guild.text_channels.length > 0) {
guild.text_channels.forEach(tc => {
const isSelected = guild.active_notification_channel_id === tc.channel_id ? 'selected' : '';
optionsHtml += `<option value="${tc.channel_id}" ${isSelected}>#${tc.channel_name}</option>`;
});
} else {
optionsHtml = '<option value="" disabled>Brak kanałów tekstowych</option>';
}
html += `
<div class="guild-setting-row glass-card-subtle">
<div class="guild-info">
<div class="guild-icon"><i data-lucide="shield"></i></div>
<div>
<div class="guild-name">${guild.guild_name}</div>
<div class="guild-id">ID: ${guild.guild_id}</div>
</div>
</div>
<div class="guild-control">
<select class="premium-select guild-log-select" data-guild-id="${guild.guild_id}" onchange="saveGuildLogChannel('${guild.guild_id}', this.value)">
<option value="">-- Brak / Domyślny --</option>
${optionsHtml}
</select>
</div>
</div>
`;
});
settingsGuildsList.innerHTML = html;
lucide.createIcons();
}
async function saveGuildLogChannel(guildId, channelId) {
try {
const res = await fetch('/api/settings/text_channel', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ channel_id: channelId })
body: JSON.stringify({ guild_id: guildId, channel_id: channelId || "" })
});
if (!res.ok) throw new Error('Settings change failed');
showToast('Zapisano kanał powiadomień na Discordzie!', 'success');
showToast('Zapisano kanał powiadomień dla serwera!', 'success');
} catch (err) {
showToast('Nie udało się zapisać kanału powiadomień', 'error');
}
+32 -12
View File
@@ -27,10 +27,14 @@
<h1>ŻyrarBOT</h1>
<span class="subtitle">Kontroler Web</span>
</div>
</div>
<div class="bot-status-badge" id="bot-status-container">
<span class="status-indicator offline" id="status-dot"></span>
<span id="status-text">Rozłączony</span>
<div class="header-actions" style="display: flex; align-items: center; gap: 16px;">
<button id="btn-open-settings" class="btn btn-secondary" style="display: flex; align-items: center; gap: 8px;">
<i data-lucide="settings"></i> Ustawienia
</button>
<div class="bot-status-badge" id="bot-status-container">
<span class="status-indicator offline" id="status-dot"></span>
<span id="status-text">Rozłączony</span>
</div>
</div>
</header>
@@ -41,7 +45,7 @@
<!-- Connection & Settings Panel -->
<div class="glass-card connection-panel">
<h2 class="card-title"><i data-lucide="radio"></i> Kanał Głosowy</h2>
<div class="control-row" style="margin-bottom: 20px;">
<div class="control-row">
<select id="voice-channel-select" class="premium-select">
<option value="" disabled selected>Wybierz kanał...</option>
</select>
@@ -49,13 +53,6 @@
<i data-lucide="log-in"></i> Połącz
</button>
</div>
<h2 class="card-title" style="margin-top: 10px;"><i data-lucide="message-square"></i> Kanał Tekstowy Logów</h2>
<div class="control-row">
<select id="text-channel-select" class="premium-select">
<option value="" disabled selected>Wybierz kanał...</option>
</select>
</div>
</div>
<!-- Player Panel (Now Playing) -->
@@ -217,6 +214,29 @@
</main>
</div>
<!-- Settings Modal -->
<div class="modal-overlay" id="settings-modal" style="display: none;">
<div class="modal-card glass-card">
<div class="modal-header">
<h2 class="card-title"><i data-lucide="settings"></i> Ustawienia ŻyrarBOT</h2>
<button class="btn-icon" id="btn-close-settings" title="Zamknij"><i data-lucide="x"></i></button>
</div>
<div class="modal-body">
<div class="settings-section">
<h3><i data-lucide="server"></i> Konfiguracja Serwerów Discord (Kanały Logów)</h3>
<p class="settings-desc">Wybierz kanał tekstowy na każdym serwerze Discord, na który bot ma wysyłać powiadomienia o odtwarzanych utworach i zmianach w kolejce.</p>
<div class="guilds-list" id="settings-guilds-list">
<!-- Populated via JS -->
<div class="empty-queue-message">
<i data-lucide="loader"></i>
<p>Ładowanie listy serwerów...</p>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Notification Toast -->
<div class="toast-container" id="toast-container"></div>
+118
View File
@@ -923,3 +923,121 @@ body {
.toast.info i {
color: var(--secondary);
}
/* Settings Modal */
.modal-overlay {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
background: rgba(0, 0, 0, 0.6);
backdrop-filter: blur(8px);
z-index: 1000;
display: flex;
align-items: center;
justify-content: center;
padding: 20px;
animation: fadeIn 0.2s ease-out;
}
.modal-card {
width: 100%;
max-width: 650px;
max-height: 85vh;
display: flex;
flex-direction: column;
background: var(--bg-card);
border: 1px solid var(--border-glass);
border-radius: 24px;
box-shadow: var(--shadow-premium);
overflow: hidden;
}
.modal-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 20px 24px;
border-bottom: 1px solid var(--border-glass);
background: rgba(255, 255, 255, 0.02);
}
.modal-body {
padding: 24px;
overflow-y: auto;
flex: 1;
}
.settings-section {
margin-bottom: 24px;
}
.settings-section h3 {
font-size: 1.1rem;
font-weight: 600;
display: flex;
align-items: center;
gap: 10px;
color: var(--text-main);
margin-bottom: 8px;
}
.settings-desc {
font-size: 0.9rem;
color: var(--text-muted);
margin-bottom: 16px;
line-height: 1.5;
}
.guild-setting-row {
display: flex;
align-items: center;
justify-content: space-between;
padding: 16px;
border-radius: 16px;
background: rgba(255, 255, 255, 0.03);
border: 1px solid var(--border-glass);
margin-bottom: 12px;
gap: 16px;
}
.guild-info {
display: flex;
align-items: center;
gap: 12px;
flex: 1;
min-width: 0;
}
.guild-icon {
width: 42px;
height: 42px;
border-radius: 12px;
background: rgba(139, 92, 246, 0.15);
color: var(--primary);
display: flex;
align-items: center;
justify-content: center;
font-size: 1.2rem;
flex-shrink: 0;
}
.guild-name {
font-weight: 600;
color: var(--text-main);
font-size: 0.95rem;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.guild-id {
font-size: 0.75rem;
color: var(--text-muted);
}
.guild-control {
width: 240px;
flex-shrink: 0;
}