feat: show import-pending red lozenge when Sonarr/Radarr has issues
All checks were successful
Build and Push Docker Image / build (push) Successful in 23s
All checks were successful
Build and Push Docker Image / build (push) Successful in 23s
- Detect trackedDownloadState=importPending or status=warning/error - Extract statusMessages and errorMessage from queue records - Display red 'Import Pending' badge on download card header - Hover reveals tooltip with the specific issue messages - Visible to all users (not admin-only)
This commit is contained in:
@@ -384,6 +384,14 @@ function createDownloadCard(download) {
|
|||||||
|
|
||||||
header.appendChild(type);
|
header.appendChild(type);
|
||||||
header.appendChild(status);
|
header.appendChild(status);
|
||||||
|
|
||||||
|
if (download.importIssues && download.importIssues.length > 0) {
|
||||||
|
const issueBadge = document.createElement('span');
|
||||||
|
issueBadge.className = 'import-issue-badge';
|
||||||
|
issueBadge.textContent = 'Import Pending';
|
||||||
|
issueBadge.setAttribute('data-tooltip', download.importIssues.join('\n'));
|
||||||
|
header.appendChild(issueBadge);
|
||||||
|
}
|
||||||
|
|
||||||
const title = document.createElement('h3');
|
const title = document.createElement('h3');
|
||||||
title.className = 'download-title';
|
title.className = 'download-title';
|
||||||
|
|||||||
@@ -688,6 +688,38 @@ body {
|
|||||||
color: var(--text-muted);
|
color: var(--text-muted);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ===== Import Issue Badge ===== */
|
||||||
|
.import-issue-badge {
|
||||||
|
padding: 2px 8px;
|
||||||
|
border-radius: 10px;
|
||||||
|
font-size: 0.65rem;
|
||||||
|
font-weight: 600;
|
||||||
|
background: #ffebee;
|
||||||
|
color: #c62828;
|
||||||
|
cursor: help;
|
||||||
|
position: relative;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.import-issue-badge:hover::after {
|
||||||
|
content: attr(data-tooltip);
|
||||||
|
position: absolute;
|
||||||
|
top: calc(100% + 6px);
|
||||||
|
left: 0;
|
||||||
|
background: #424242;
|
||||||
|
color: #fff;
|
||||||
|
padding: 8px 12px;
|
||||||
|
border-radius: 6px;
|
||||||
|
font-size: 0.7rem;
|
||||||
|
font-weight: 400;
|
||||||
|
white-space: pre-line;
|
||||||
|
max-width: 320px;
|
||||||
|
z-index: 100;
|
||||||
|
box-shadow: 0 2px 8px rgba(0,0,0,0.25);
|
||||||
|
line-height: 1.4;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
.download-user-badge {
|
.download-user-badge {
|
||||||
padding: 2px 8px;
|
padding: 2px 8px;
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
|
|||||||
@@ -59,6 +59,29 @@ function tagMatchesUser(tag, username) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Extract import issues from a Sonarr/Radarr queue record
|
||||||
|
function getImportIssues(queueRecord) {
|
||||||
|
if (!queueRecord) return null;
|
||||||
|
const state = queueRecord.trackedDownloadState;
|
||||||
|
const status = queueRecord.trackedDownloadStatus;
|
||||||
|
if (state !== 'importPending' && status !== 'warning' && status !== 'error') return null;
|
||||||
|
const messages = [];
|
||||||
|
if (queueRecord.statusMessages && queueRecord.statusMessages.length > 0) {
|
||||||
|
for (const sm of queueRecord.statusMessages) {
|
||||||
|
if (sm.messages && sm.messages.length > 0) {
|
||||||
|
messages.push(...sm.messages);
|
||||||
|
} else if (sm.title) {
|
||||||
|
messages.push(sm.title);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (queueRecord.errorMessage) {
|
||||||
|
messages.push(queueRecord.errorMessage);
|
||||||
|
}
|
||||||
|
if (messages.length === 0) return null;
|
||||||
|
return messages;
|
||||||
|
}
|
||||||
|
|
||||||
// Helper to build Sonarr web UI link for a series
|
// Helper to build Sonarr web UI link for a series
|
||||||
function getSonarrLink(series) {
|
function getSonarrLink(series) {
|
||||||
if (!series || !series._instanceUrl || !series.titleSlug) return null;
|
if (!series || !series._instanceUrl || !series.titleSlug) return null;
|
||||||
@@ -355,6 +378,8 @@ router.get('/user-downloads', async (req, res) => {
|
|||||||
episodeInfo: sonarrMatch,
|
episodeInfo: sonarrMatch,
|
||||||
userTag: userTag
|
userTag: userTag
|
||||||
};
|
};
|
||||||
|
const issues = getImportIssues(sonarrMatch);
|
||||||
|
if (issues) dlObj.importIssues = issues;
|
||||||
if (isAdmin) {
|
if (isAdmin) {
|
||||||
dlObj.downloadPath = slot.storage || null;
|
dlObj.downloadPath = slot.storage || null;
|
||||||
dlObj.targetPath = series.path || null;
|
dlObj.targetPath = series.path || null;
|
||||||
@@ -391,6 +416,8 @@ router.get('/user-downloads', async (req, res) => {
|
|||||||
movieInfo: radarrMatch,
|
movieInfo: radarrMatch,
|
||||||
userTag: userTag
|
userTag: userTag
|
||||||
};
|
};
|
||||||
|
const issues = getImportIssues(radarrMatch);
|
||||||
|
if (issues) dlObj.importIssues = issues;
|
||||||
if (isAdmin) {
|
if (isAdmin) {
|
||||||
dlObj.downloadPath = slot.storage || null;
|
dlObj.downloadPath = slot.storage || null;
|
||||||
dlObj.targetPath = movie.path || null;
|
dlObj.targetPath = movie.path || null;
|
||||||
@@ -536,6 +563,8 @@ router.get('/user-downloads', async (req, res) => {
|
|||||||
download.seriesName = series.title;
|
download.seriesName = series.title;
|
||||||
download.episodeInfo = sonarrMatch;
|
download.episodeInfo = sonarrMatch;
|
||||||
download.userTag = userTag;
|
download.userTag = userTag;
|
||||||
|
const sonarrIssues = getImportIssues(sonarrMatch);
|
||||||
|
if (sonarrIssues) download.importIssues = sonarrIssues;
|
||||||
if (isAdmin) {
|
if (isAdmin) {
|
||||||
download.downloadPath = download.savePath || null;
|
download.downloadPath = download.savePath || null;
|
||||||
download.targetPath = series.path || null;
|
download.targetPath = series.path || null;
|
||||||
@@ -565,6 +594,8 @@ router.get('/user-downloads', async (req, res) => {
|
|||||||
download.movieName = movie.title;
|
download.movieName = movie.title;
|
||||||
download.movieInfo = radarrMatch;
|
download.movieInfo = radarrMatch;
|
||||||
download.userTag = userTag;
|
download.userTag = userTag;
|
||||||
|
const radarrIssues = getImportIssues(radarrMatch);
|
||||||
|
if (radarrIssues) download.importIssues = radarrIssues;
|
||||||
if (isAdmin) {
|
if (isAdmin) {
|
||||||
download.downloadPath = download.savePath || null;
|
download.downloadPath = download.savePath || null;
|
||||||
download.targetPath = movie.path || null;
|
download.targetPath = movie.path || null;
|
||||||
|
|||||||
Reference in New Issue
Block a user