Make auto-sync run history readable
Render playlist pipeline history as visible run cards with fallback summaries, preview chips, metadata, Details controls, and an explicit empty result message for sparse payloads.
This commit is contained in:
parent
9eb7a49c24
commit
06c76bbcaf
2 changed files with 91 additions and 22 deletions
|
|
@ -456,18 +456,32 @@ function autoSyncHistoryEntryHtml(entry) {
|
|||
const wishlistDelta = autoSyncDelta(after.wishlisted_count, before.wishlisted_count);
|
||||
const libraryDelta = autoSyncDelta(after.in_library_count, before.in_library_count);
|
||||
const entryId = `auto-sync-history-${entry.id}`;
|
||||
const playlistName = entry.playlist_name || after.name || before.name || `Playlist #${entry.playlist_id || 'unknown'}`;
|
||||
const summary = entry.summary || autoSyncHistoryFallbackSummary(before, after, status);
|
||||
const resultHtml = [
|
||||
autoSyncHistoryResultPill('Refreshed', result.playlists_refreshed),
|
||||
autoSyncHistoryResultPill('Synced', result.tracks_synced),
|
||||
autoSyncHistoryResultPill('Skipped', result.sync_skipped),
|
||||
autoSyncHistoryResultPill('Queued', result.wishlist_queued),
|
||||
result.error ? `<span class="error">${_esc(result.error)}</span>` : '',
|
||||
].filter(Boolean).join('');
|
||||
return `
|
||||
<article class="auto-sync-history-entry">
|
||||
<div class="auto-sync-history-row" onclick="autoSyncToggleHistoryEntry('${entryId}')">
|
||||
<span class="auto-sync-history-status ${_escAttr(status)}">${_esc(autoSyncHistoryStatusLabel(status))}</span>
|
||||
<div class="auto-sync-history-main">
|
||||
<strong>${_esc(entry.playlist_name || 'Mirrored playlist')}</strong>
|
||||
<small>${_esc(entry.summary || '')}</small>
|
||||
<strong>${_esc(playlistName)}</strong>
|
||||
<small>${_esc(summary)}</small>
|
||||
<div class="auto-sync-history-preview">
|
||||
${autoSyncHistoryPreviewPill('Tracks', before.track_count, after.track_count, trackDelta)}
|
||||
${autoSyncHistoryPreviewPill('Discovered', before.discovered_count, after.discovered_count, discoveredDelta)}
|
||||
</div>
|
||||
</div>
|
||||
<div class="auto-sync-history-meta">
|
||||
${started ? `<span>${_esc(started)}</span>` : ''}
|
||||
${duration ? `<span>${_esc(duration)}</span>` : ''}
|
||||
<span>${_esc(entry.trigger_source || 'pipeline')}</span>
|
||||
<button type="button" onclick="event.stopPropagation(); autoSyncToggleHistoryEntry('${entryId}')">Details</button>
|
||||
</div>
|
||||
</div>
|
||||
<div id="${entryId}" class="auto-sync-history-detail">
|
||||
|
|
@ -478,17 +492,19 @@ function autoSyncHistoryEntryHtml(entry) {
|
|||
${autoSyncHistoryStatHtml('In library', before.in_library_count, after.in_library_count, libraryDelta)}
|
||||
</div>
|
||||
<div class="auto-sync-history-result">
|
||||
${autoSyncHistoryResultPill('Refreshed', result.playlists_refreshed)}
|
||||
${autoSyncHistoryResultPill('Synced', result.tracks_synced)}
|
||||
${autoSyncHistoryResultPill('Skipped', result.sync_skipped)}
|
||||
${autoSyncHistoryResultPill('Queued', result.wishlist_queued)}
|
||||
${result.error ? `<span class="error">${_esc(result.error)}</span>` : ''}
|
||||
${resultHtml || '<span class="muted">No detailed result payload recorded for this run.</span>'}
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
`;
|
||||
}
|
||||
|
||||
function autoSyncHistoryFallbackSummary(before, after, status) {
|
||||
const beforeTracks = parseInt(before.track_count, 10) || 0;
|
||||
const afterTracks = parseInt(after.track_count, 10) || 0;
|
||||
return `${autoSyncHistoryStatusLabel(status)} | ${beforeTracks} -> ${afterTracks} tracks`;
|
||||
}
|
||||
|
||||
function autoSyncToggleHistoryEntry(entryId) {
|
||||
const el = document.getElementById(entryId);
|
||||
if (el) el.classList.toggle('expanded');
|
||||
|
|
@ -527,6 +543,13 @@ function autoSyncHistoryStatHtml(label, before, after, delta) {
|
|||
`;
|
||||
}
|
||||
|
||||
function autoSyncHistoryPreviewPill(label, before, after, delta) {
|
||||
const beforeValue = parseInt(before, 10) || 0;
|
||||
const afterValue = parseInt(after, 10) || 0;
|
||||
const deltaText = delta ? ` ${delta > 0 ? '+' : ''}${delta}` : '';
|
||||
return `<span>${_esc(label)} ${beforeValue}->${afterValue}${_esc(deltaText)}</span>`;
|
||||
}
|
||||
|
||||
function autoSyncHistoryResultPill(label, value) {
|
||||
if (value === undefined || value === null || value === '') return '';
|
||||
return `<span>${_esc(label)}: ${_esc(String(value))}</span>`;
|
||||
|
|
|
|||
|
|
@ -12016,29 +12016,36 @@ body.helper-mode-active #dashboard-activity-feed:hover {
|
|||
.auto-sync-history-list {
|
||||
min-height: 0;
|
||||
overflow-y: auto;
|
||||
padding: 18px;
|
||||
padding: 20px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.auto-sync-history-entry {
|
||||
border: 1px solid rgba(255, 255, 255, 0.08);
|
||||
border-radius: 8px;
|
||||
background: rgba(255, 255, 255, 0.035);
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
border-radius: 10px;
|
||||
background:
|
||||
linear-gradient(135deg, rgba(255, 255, 255, 0.055), rgba(255, 255, 255, 0.025)),
|
||||
rgba(255, 255, 255, 0.035);
|
||||
overflow: hidden;
|
||||
box-shadow: 0 10px 26px rgba(0, 0, 0, 0.14);
|
||||
}
|
||||
|
||||
.auto-sync-history-entry:hover {
|
||||
border-color: rgba(var(--accent-rgb), 0.22);
|
||||
border-color: rgba(var(--accent-rgb), 0.3);
|
||||
background:
|
||||
linear-gradient(135deg, rgba(var(--accent-rgb), 0.075), rgba(255, 255, 255, 0.028)),
|
||||
rgba(255, 255, 255, 0.04);
|
||||
}
|
||||
|
||||
.auto-sync-history-row {
|
||||
display: grid;
|
||||
grid-template-columns: auto minmax(0, 1fr) auto;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
padding: 13px 14px;
|
||||
gap: 14px;
|
||||
min-height: 84px;
|
||||
padding: 15px 16px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
|
|
@ -12077,18 +12084,38 @@ body.helper-mode-active #dashboard-activity-feed:hover {
|
|||
display: block;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.auto-sync-history-main strong {
|
||||
color: rgba(255, 255, 255, 0.88);
|
||||
font-size: 13px;
|
||||
font-size: 14px;
|
||||
line-height: 1.25;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.auto-sync-history-main small {
|
||||
margin-top: 3px;
|
||||
color: rgba(255, 255, 255, 0.42);
|
||||
font-size: 11px;
|
||||
color: rgba(255, 255, 255, 0.5);
|
||||
font-size: 12px;
|
||||
line-height: 1.3;
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
.auto-sync-history-preview {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 6px;
|
||||
margin-top: 9px;
|
||||
}
|
||||
|
||||
.auto-sync-history-preview span {
|
||||
padding: 4px 7px;
|
||||
border-radius: 6px;
|
||||
background: rgba(var(--accent-rgb), 0.1);
|
||||
border: 1px solid rgba(var(--accent-rgb), 0.16);
|
||||
color: rgb(var(--accent-light-rgb));
|
||||
font-size: 10px;
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
.auto-sync-history-meta {
|
||||
|
|
@ -12099,7 +12126,8 @@ body.helper-mode-active #dashboard-activity-feed:hover {
|
|||
}
|
||||
|
||||
.auto-sync-history-meta span,
|
||||
.auto-sync-history-result span {
|
||||
.auto-sync-history-result span,
|
||||
.auto-sync-history-meta button {
|
||||
padding: 4px 7px;
|
||||
border-radius: 6px;
|
||||
background: rgba(255, 255, 255, 0.055);
|
||||
|
|
@ -12108,9 +12136,21 @@ body.helper-mode-active #dashboard-activity-feed:hover {
|
|||
font-weight: 700;
|
||||
}
|
||||
|
||||
.auto-sync-history-meta button {
|
||||
border: 1px solid rgba(var(--accent-rgb), 0.18);
|
||||
color: rgb(var(--accent-light-rgb));
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.auto-sync-history-meta button:hover {
|
||||
background: rgba(var(--accent-rgb), 0.14);
|
||||
border-color: rgba(var(--accent-rgb), 0.35);
|
||||
}
|
||||
|
||||
.auto-sync-history-detail {
|
||||
display: none;
|
||||
padding: 0 14px 14px;
|
||||
padding: 0 16px 16px;
|
||||
border-top: 1px solid rgba(255, 255, 255, 0.07);
|
||||
}
|
||||
|
||||
.auto-sync-history-detail.expanded {
|
||||
|
|
@ -12121,7 +12161,7 @@ body.helper-mode-active #dashboard-activity-feed:hover {
|
|||
display: grid;
|
||||
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||
gap: 8px;
|
||||
padding-top: 2px;
|
||||
padding-top: 14px;
|
||||
}
|
||||
|
||||
.auto-sync-history-stats div {
|
||||
|
|
@ -12167,6 +12207,12 @@ body.helper-mode-active #dashboard-activity-feed:hover {
|
|||
border-color: rgba(239, 68, 68, 0.18);
|
||||
}
|
||||
|
||||
.auto-sync-history-result span.muted {
|
||||
color: rgba(255, 255, 255, 0.42);
|
||||
background: rgba(255, 255, 255, 0.045);
|
||||
border-color: rgba(255, 255, 255, 0.08);
|
||||
}
|
||||
|
||||
.auto-sync-history-empty {
|
||||
margin: 24px;
|
||||
padding: 44px;
|
||||
|
|
|
|||
Loading…
Reference in a new issue