Fix CSP violations and ignoreAvailable reference error
- Add .hidden utility class to style.css for CSP compliance - Replace all inline style='display: none' with class='hidden' in HTML - Update all UI modules to use classList.add/remove instead of style.display - Fix ignoreAvailable reference error in history.js (use state.ignoreAvailable) - Rebuild client bundle with vite
This commit is contained in:
+22
-17
@@ -11,7 +11,7 @@ export function fadeOutLogin() {
|
||||
const login = document.getElementById('login-container');
|
||||
login.classList.add('fade-out');
|
||||
login.addEventListener('transitionend', () => {
|
||||
login.style.display = 'none';
|
||||
login.classList.add('hidden');
|
||||
login.classList.remove('fade-out');
|
||||
resolve();
|
||||
}, { once: true });
|
||||
@@ -20,7 +20,7 @@ export function fadeOutLogin() {
|
||||
|
||||
export function showSplash() {
|
||||
const splash = document.getElementById('splash-screen');
|
||||
splash.style.display = 'flex';
|
||||
splash.classList.remove('hidden');
|
||||
splash.style.opacity = '1';
|
||||
splash.classList.remove('fade-out');
|
||||
}
|
||||
@@ -36,12 +36,12 @@ export function dismissSplash(startTime) {
|
||||
// transitionend never fires (e.g. display was toggled in same frame)
|
||||
const TRANSITION_MS = 400;
|
||||
const fallback = setTimeout(() => {
|
||||
splash.style.display = 'none';
|
||||
splash.classList.add('hidden');
|
||||
resolve();
|
||||
}, TRANSITION_MS + 100);
|
||||
splash.addEventListener('transitionend', () => {
|
||||
clearTimeout(fallback);
|
||||
splash.style.display = 'none';
|
||||
splash.classList.add('hidden');
|
||||
resolve();
|
||||
}, { once: true });
|
||||
}, remaining);
|
||||
@@ -115,22 +115,27 @@ export async function handleLogoutClick() {
|
||||
}
|
||||
|
||||
export function showLogin() {
|
||||
document.getElementById('login-container').style.display = 'flex';
|
||||
document.getElementById('dashboard-container').style.display = 'none';
|
||||
document.getElementById('login-container').classList.remove('hidden');
|
||||
document.getElementById('dashboard-container').classList.add('hidden');
|
||||
hideLoginError();
|
||||
}
|
||||
|
||||
export function showDashboard() {
|
||||
document.getElementById('login-container').style.display = 'none';
|
||||
document.getElementById('dashboard-container').style.display = 'block';
|
||||
document.getElementById('login-container').classList.add('hidden');
|
||||
document.getElementById('dashboard-container').classList.remove('hidden');
|
||||
document.getElementById('currentUser').textContent = state.currentUser.name || '-';
|
||||
// Always start with status panel hidden (guards against stale display value on re-login)
|
||||
const sp = document.getElementById('status-panel');
|
||||
sp.style.display = 'none';
|
||||
sp.classList.add('hidden');
|
||||
// Also hide webhooks-section to keep them in sync (both show/hide together)
|
||||
const webhooksSection = document.getElementById('webhooks-section');
|
||||
if (webhooksSection) webhooksSection.style.display = 'none';
|
||||
document.getElementById('admin-controls').style.display = state.isAdmin ? 'flex' : 'none';
|
||||
if (webhooksSection) webhooksSection.classList.add('hidden');
|
||||
const adminControls = document.getElementById('admin-controls');
|
||||
if (state.isAdmin) {
|
||||
adminControls.classList.remove('hidden');
|
||||
} else {
|
||||
adminControls.classList.add('hidden');
|
||||
}
|
||||
// Note: webhooks-section visibility is controlled by toggleStatusPanel()
|
||||
// Initialise days input from saved value
|
||||
const daysInput = document.getElementById('history-days');
|
||||
@@ -141,31 +146,31 @@ export function showDashboard() {
|
||||
export function showLoginError(message) {
|
||||
const errorDiv = document.getElementById('login-error');
|
||||
errorDiv.textContent = message;
|
||||
errorDiv.style.display = 'block';
|
||||
errorDiv.classList.remove('hidden');
|
||||
}
|
||||
|
||||
export function hideLoginError() {
|
||||
const errorDiv = document.getElementById('login-error');
|
||||
errorDiv.style.display = 'none';
|
||||
errorDiv.classList.add('hidden');
|
||||
}
|
||||
|
||||
export function showError(message) {
|
||||
const errorDiv = document.getElementById('error-message');
|
||||
errorDiv.textContent = message;
|
||||
errorDiv.style.display = 'block';
|
||||
errorDiv.classList.remove('hidden');
|
||||
}
|
||||
|
||||
export function hideError() {
|
||||
const errorDiv = document.getElementById('error-message');
|
||||
errorDiv.style.display = 'none';
|
||||
errorDiv.classList.add('hidden');
|
||||
}
|
||||
|
||||
export function showLoading() {
|
||||
const loading = document.getElementById('loading');
|
||||
loading.style.display = 'block';
|
||||
loading.classList.remove('hidden');
|
||||
}
|
||||
|
||||
export function hideLoading() {
|
||||
const loading = document.getElementById('loading');
|
||||
loading.style.display = 'none';
|
||||
loading.classList.add('hidden');
|
||||
}
|
||||
|
||||
@@ -75,12 +75,12 @@ export function renderDownloads() {
|
||||
}
|
||||
|
||||
if (filteredDownloads.length === 0) {
|
||||
noDownloads.style.display = 'block';
|
||||
noDownloads.classList.remove('hidden');
|
||||
downloadsList.innerHTML = '';
|
||||
return;
|
||||
}
|
||||
|
||||
noDownloads.style.display = 'none';
|
||||
noDownloads.classList.add('hidden');
|
||||
|
||||
// Get existing cards
|
||||
const existingCards = new Map();
|
||||
|
||||
@@ -13,17 +13,17 @@ export function initDownloadClientFilter() {
|
||||
|
||||
filterBtn.addEventListener('click', (e) => {
|
||||
e.stopPropagation();
|
||||
filterDropdown.style.display = filterDropdown.style.display === 'block' ? 'none' : 'block';
|
||||
filterDropdown.classList.toggle('open');
|
||||
});
|
||||
|
||||
filterClose.addEventListener('click', () => {
|
||||
filterDropdown.style.display = 'none';
|
||||
filterDropdown.classList.remove('open');
|
||||
});
|
||||
|
||||
// Close dropdown when clicking outside
|
||||
document.addEventListener('click', (e) => {
|
||||
if (!filterDropdown.contains(e.target) && e.target !== filterBtn) {
|
||||
filterDropdown.style.display = 'none';
|
||||
filterDropdown.classList.remove('open');
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
+15
-15
@@ -24,11 +24,11 @@ export function initHistoryControls() {
|
||||
refreshBtn.addEventListener('click', () => loadHistory(true));
|
||||
}
|
||||
if (ignoreToggle) {
|
||||
ignoreToggle.checked = ignoreAvailable;
|
||||
ignoreToggle.checked = state.ignoreAvailable;
|
||||
ignoreToggle.addEventListener('change', () => {
|
||||
ignoreAvailable = ignoreToggle.checked;
|
||||
saveIgnoreAvailable(ignoreAvailable);
|
||||
renderHistory(lastHistoryItems);
|
||||
state.ignoreAvailable = ignoreToggle.checked;
|
||||
saveIgnoreAvailable(state.ignoreAvailable);
|
||||
renderHistory(state.lastHistoryItems);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -53,8 +53,8 @@ export function stopHistoryRefresh() {
|
||||
export function clearHistory() {
|
||||
state.lastHistoryItems = [];
|
||||
document.getElementById('history-list').innerHTML = '';
|
||||
document.getElementById('no-history').style.display = 'none';
|
||||
document.getElementById('history-error').style.display = 'none';
|
||||
document.getElementById('no-history').classList.add('hidden');
|
||||
document.getElementById('history-error').classList.add('hidden');
|
||||
}
|
||||
|
||||
export async function loadHistory(forceRefresh = false) {
|
||||
@@ -63,24 +63,24 @@ export async function loadHistory(forceRefresh = false) {
|
||||
const errorEl = document.getElementById('history-error');
|
||||
const noHistoryEl = document.getElementById('no-history');
|
||||
|
||||
loadingEl.style.display = 'block';
|
||||
errorEl.style.display = 'none';
|
||||
noHistoryEl.style.display = 'none';
|
||||
loadingEl.classList.remove('hidden');
|
||||
errorEl.classList.add('hidden');
|
||||
noHistoryEl.classList.add('hidden');
|
||||
|
||||
try {
|
||||
const result = await apiLoadHistory(forceRefresh);
|
||||
loadingEl.style.display = 'none';
|
||||
loadingEl.classList.add('hidden');
|
||||
if (result.success) {
|
||||
state.lastHistoryItems = result.history;
|
||||
renderHistory(state.lastHistoryItems);
|
||||
} else {
|
||||
errorEl.textContent = result.error || 'Failed to load history.';
|
||||
errorEl.style.display = 'block';
|
||||
errorEl.classList.remove('hidden');
|
||||
}
|
||||
} catch (err) {
|
||||
loadingEl.style.display = 'none';
|
||||
loadingEl.classList.add('hidden');
|
||||
errorEl.textContent = 'Failed to load history.';
|
||||
errorEl.style.display = 'block';
|
||||
errorEl.classList.remove('hidden');
|
||||
console.error('[History] Load error:', err);
|
||||
}
|
||||
}
|
||||
@@ -93,10 +93,10 @@ export function renderHistory(items) {
|
||||
? items.filter(item => !(item.outcome === 'failed' && item.availableForUpgrade))
|
||||
: items;
|
||||
if (!visible.length) {
|
||||
noHistoryEl.style.display = 'block';
|
||||
noHistoryEl.classList.remove('hidden');
|
||||
return;
|
||||
}
|
||||
noHistoryEl.style.display = 'none';
|
||||
noHistoryEl.classList.add('hidden');
|
||||
visible.forEach(item => listEl.appendChild(createHistoryCard(item)));
|
||||
}
|
||||
|
||||
|
||||
@@ -7,24 +7,24 @@ import { fetchWebhookStatus } from './webhooks.js';
|
||||
export async function toggleStatusPanel() {
|
||||
const panel = document.getElementById('status-panel');
|
||||
const webhooksSection = document.getElementById('webhooks-section');
|
||||
if (panel.style.display !== 'none') {
|
||||
if (!panel.classList.contains('hidden')) {
|
||||
// Close both panels (webhooks is a sibling, hide it too)
|
||||
panel.style.display = 'none';
|
||||
if (webhooksSection) webhooksSection.style.display = 'none';
|
||||
panel.classList.add('hidden');
|
||||
if (webhooksSection) webhooksSection.classList.add('hidden');
|
||||
if (state.statusRefreshHandle) { clearInterval(state.statusRefreshHandle); state.statusRefreshHandle = null; }
|
||||
return;
|
||||
}
|
||||
// Open status panel and webhooks section (siblings)
|
||||
panel.style.display = 'block';
|
||||
panel.classList.remove('hidden');
|
||||
// Show webhooks section for admin users (collapsed by default)
|
||||
if (webhooksSection && state.isAdmin) {
|
||||
webhooksSection.style.display = 'block';
|
||||
webhooksSection.classList.remove('hidden');
|
||||
state.webhookSectionExpanded = false;
|
||||
document.getElementById('webhooks-content').style.display = 'none';
|
||||
document.getElementById('webhooks-content').classList.add('hidden');
|
||||
document.getElementById('webhooks-toggle').classList.remove('expanded');
|
||||
await fetchWebhookStatus();
|
||||
} else if (webhooksSection) {
|
||||
webhooksSection.style.display = 'none';
|
||||
webhooksSection.classList.add('hidden');
|
||||
}
|
||||
refreshStatusPanel();
|
||||
if (state.statusRefreshHandle) clearInterval(state.statusRefreshHandle);
|
||||
@@ -32,9 +32,9 @@ export async function toggleStatusPanel() {
|
||||
}
|
||||
|
||||
export function closeStatusPanel() {
|
||||
document.getElementById('status-panel').style.display = 'none';
|
||||
document.getElementById('status-panel').classList.add('hidden');
|
||||
const webhooksSection = document.getElementById('webhooks-section');
|
||||
if (webhooksSection) webhooksSection.style.display = 'none';
|
||||
if (webhooksSection) webhooksSection.classList.add('hidden');
|
||||
if (state.statusRefreshHandle) { clearInterval(state.statusRefreshHandle); state.statusRefreshHandle = null; }
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ export async function refreshStatusPanel() {
|
||||
const panel = document.getElementById('status-panel');
|
||||
const contentDiv = document.getElementById('status-content');
|
||||
console.log('[Status] panel found:', !!panel, 'contentDiv found:', !!contentDiv, 'panel display:', panel?.style?.display);
|
||||
if (!panel || panel.style.display === 'none') return;
|
||||
if (!panel || panel.classList.contains('hidden')) return;
|
||||
console.log('[Status] Refreshing status panel...');
|
||||
try {
|
||||
const result = await apiRefreshStatusPanel();
|
||||
|
||||
@@ -26,20 +26,20 @@ export function initTabs() {
|
||||
export function activateTab(tab) {
|
||||
const downloadsTab = document.getElementById('downloads-tab');
|
||||
const historyTab = document.getElementById('history-tab');
|
||||
const downloadsSection = document.getElementById('downloads-section');
|
||||
const historySection = document.getElementById('history-section');
|
||||
const downloadsSection = document.getElementById('tab-downloads');
|
||||
const historySection = document.getElementById('tab-history');
|
||||
|
||||
if (tab === 'downloads') {
|
||||
downloadsTab.classList.add('active');
|
||||
historyTab.classList.remove('active');
|
||||
downloadsSection.style.display = 'block';
|
||||
historySection.style.display = 'none';
|
||||
downloadsSection.classList.remove('hidden');
|
||||
historySection.classList.add('hidden');
|
||||
saveActiveTab('downloads');
|
||||
} else if (tab === 'history') {
|
||||
historyTab.classList.add('active');
|
||||
downloadsTab.classList.remove('active');
|
||||
historySection.style.display = 'block';
|
||||
downloadsSection.style.display = 'none';
|
||||
historySection.classList.remove('hidden');
|
||||
downloadsSection.classList.add('hidden');
|
||||
saveActiveTab('history');
|
||||
loadHistory();
|
||||
}
|
||||
|
||||
+35
-14
@@ -22,7 +22,11 @@ export function toggleWebhookSection() {
|
||||
const content = document.getElementById('webhooks-content');
|
||||
const toggle = document.getElementById('webhooks-toggle');
|
||||
|
||||
content.style.display = state.webhookSectionExpanded ? '' : 'none';
|
||||
if (state.webhookSectionExpanded) {
|
||||
content.classList.remove('hidden');
|
||||
} else {
|
||||
content.classList.add('hidden');
|
||||
}
|
||||
toggle.classList.toggle('expanded', state.webhookSectionExpanded);
|
||||
|
||||
if (state.webhookSectionExpanded) {
|
||||
@@ -32,7 +36,7 @@ export function toggleWebhookSection() {
|
||||
|
||||
export async function fetchWebhookStatus() {
|
||||
const loadingEl = document.getElementById('webhook-loading');
|
||||
loadingEl.style.display = '';
|
||||
loadingEl.classList.remove('hidden');
|
||||
|
||||
try {
|
||||
const result = await apiFetchWebhookStatus();
|
||||
@@ -42,7 +46,7 @@ export async function fetchWebhookStatus() {
|
||||
} catch (err) {
|
||||
console.error('Failed to fetch webhook status:', err);
|
||||
} finally {
|
||||
loadingEl.style.display = 'none';
|
||||
loadingEl.classList.add('hidden');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,9 +60,15 @@ export function renderWebhookStatus() {
|
||||
|
||||
sonarrStatus.textContent = sonarrWebhook.enabled ? '● Enabled' : '○ Disabled';
|
||||
sonarrStatus.className = 'status-indicator ' + (sonarrWebhook.enabled ? 'enabled' : 'disabled');
|
||||
sonarrEnableBtn.style.display = sonarrWebhook.enabled ? 'none' : '';
|
||||
sonarrTestBtn.style.display = sonarrWebhook.enabled ? '' : 'none';
|
||||
sonarrTriggers.style.display = sonarrWebhook.enabled ? '' : 'none';
|
||||
if (sonarrWebhook.enabled) {
|
||||
sonarrEnableBtn.classList.add('hidden');
|
||||
sonarrTestBtn.classList.remove('hidden');
|
||||
sonarrTriggers.classList.remove('hidden');
|
||||
} else {
|
||||
sonarrEnableBtn.classList.remove('hidden');
|
||||
sonarrTestBtn.classList.add('hidden');
|
||||
sonarrTriggers.classList.add('hidden');
|
||||
}
|
||||
|
||||
if (sonarrWebhook.enabled) {
|
||||
document.getElementById('sonarr-onGrab').textContent = sonarrWebhook.triggers.onGrab ? '✓' : '✗';
|
||||
@@ -72,12 +82,12 @@ export function renderWebhookStatus() {
|
||||
}
|
||||
|
||||
if (sonarrWebhook.stats) {
|
||||
sonarrStats.style.display = '';
|
||||
sonarrStats.classList.remove('hidden');
|
||||
document.getElementById('sonarr-events').textContent = sonarrWebhook.stats.eventsReceived ?? 0;
|
||||
document.getElementById('sonarr-polls').textContent = sonarrWebhook.stats.pollsSkipped ?? 0;
|
||||
document.getElementById('sonarr-last').textContent = formatTimeAgo(sonarrWebhook.stats.lastWebhookTimestamp);
|
||||
} else {
|
||||
sonarrStats.style.display = 'none';
|
||||
sonarrStats.classList.add('hidden');
|
||||
}
|
||||
|
||||
// Radarr
|
||||
@@ -89,9 +99,15 @@ export function renderWebhookStatus() {
|
||||
|
||||
radarrStatus.textContent = radarrWebhook.enabled ? '● Enabled' : '○ Disabled';
|
||||
radarrStatus.className = 'status-indicator ' + (radarrWebhook.enabled ? 'enabled' : 'disabled');
|
||||
radarrEnableBtn.style.display = radarrWebhook.enabled ? 'none' : '';
|
||||
radarrTestBtn.style.display = radarrWebhook.enabled ? '' : 'none';
|
||||
radarrTriggers.style.display = radarrWebhook.enabled ? '' : 'none';
|
||||
if (radarrWebhook.enabled) {
|
||||
radarrEnableBtn.classList.add('hidden');
|
||||
radarrTestBtn.classList.remove('hidden');
|
||||
radarrTriggers.classList.remove('hidden');
|
||||
} else {
|
||||
radarrEnableBtn.classList.remove('hidden');
|
||||
radarrTestBtn.classList.add('hidden');
|
||||
radarrTriggers.classList.add('hidden');
|
||||
}
|
||||
|
||||
if (radarrWebhook.enabled) {
|
||||
document.getElementById('radarr-onGrab').textContent = radarrWebhook.triggers.onGrab ? '✓' : '✗';
|
||||
@@ -105,12 +121,12 @@ export function renderWebhookStatus() {
|
||||
}
|
||||
|
||||
if (radarrWebhook.stats) {
|
||||
radarrStats.style.display = '';
|
||||
radarrStats.classList.remove('hidden');
|
||||
document.getElementById('radarr-events').textContent = radarrWebhook.stats.eventsReceived ?? 0;
|
||||
document.getElementById('radarr-polls').textContent = radarrWebhook.stats.pollsSkipped ?? 0;
|
||||
document.getElementById('radarr-last').textContent = formatTimeAgo(radarrWebhook.stats.lastWebhookTimestamp);
|
||||
} else {
|
||||
radarrStats.style.display = 'none';
|
||||
radarrStats.classList.add('hidden');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -188,5 +204,10 @@ export function setWebhookLoading(loading) {
|
||||
document.getElementById('enable-radarr-webhook').disabled = loading;
|
||||
document.getElementById('test-sonarr-webhook').disabled = loading;
|
||||
document.getElementById('test-radarr-webhook').disabled = loading;
|
||||
document.getElementById('webhook-loading').style.display = loading ? '' : 'none';
|
||||
const loadingEl = document.getElementById('webhook-loading');
|
||||
if (loading) {
|
||||
loadingEl.classList.remove('hidden');
|
||||
} else {
|
||||
loadingEl.classList.add('hidden');
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user