Harden auto-sync history row rendering

Normalize sparse playlist pipeline history rows before rendering and add a visible fallback for empty entries so the Run History tab cannot collapse into unreadable divider lines.
This commit is contained in:
Broque Thomas 2026-05-25 07:59:27 -07:00
parent 06c76bbcaf
commit 119e8f1599
2 changed files with 53 additions and 2 deletions

View file

@ -438,13 +438,14 @@ function renderAutoSyncHistoryPanel(history, total) {
<button onclick="refreshAutoSyncScheduleModal()">Refresh</button>
</div>
<div class="auto-sync-history-list">
${history.map(autoSyncHistoryEntryHtml).join('')}
${history.map((entry, index) => autoSyncHistoryEntryHtml(entry, index)).join('')}
${total > history.length ? `<div class="auto-sync-history-total">Showing ${history.length} of ${total} runs</div>` : ''}
</div>
`;
}
function autoSyncHistoryEntryHtml(entry) {
function autoSyncHistoryEntryHtml(entry, index = 0) {
entry = autoSyncNormalizeHistoryEntry(entry, index);
const status = entry.status || 'completed';
const before = entry.before_json || {};
const after = entry.after_json || {};
@ -499,6 +500,40 @@ function autoSyncHistoryEntryHtml(entry) {
`;
}
function autoSyncNormalizeHistoryEntry(entry, index) {
if (!entry || typeof entry !== 'object') {
return {
id: `unknown-${index}`,
status: 'completed',
playlist_name: 'Playlist pipeline run',
trigger_source: 'pipeline',
summary: 'Run history entry did not include detailed metadata.',
before_json: {},
after_json: {},
result_json: {},
};
}
return {
...entry,
id: entry.id ?? `history-${index}`,
before_json: autoSyncParseHistoryObject(entry.before_json),
after_json: autoSyncParseHistoryObject(entry.after_json),
result_json: autoSyncParseHistoryObject(entry.result_json),
};
}
function autoSyncParseHistoryObject(value) {
if (!value) return {};
if (typeof value === 'object') return value;
if (typeof value !== 'string') return {};
try {
const parsed = JSON.parse(value);
return parsed && typeof parsed === 'object' ? parsed : {};
} catch (_err) {
return {};
}
}
function autoSyncHistoryFallbackSummary(before, after, status) {
const beforeTracks = parseInt(before.track_count, 10) || 0;
const afterTracks = parseInt(after.track_count, 10) || 0;

View file

@ -12023,6 +12023,8 @@ body.helper-mode-active #dashboard-activity-feed:hover {
}
.auto-sync-history-entry {
display: block;
min-height: 84px;
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 10px;
background:
@ -12032,6 +12034,19 @@ body.helper-mode-active #dashboard-activity-feed:hover {
box-shadow: 0 10px 26px rgba(0, 0, 0, 0.14);
}
.auto-sync-history-entry:empty {
display: flex;
align-items: center;
padding: 16px;
}
.auto-sync-history-entry:empty::before {
content: 'Run history entry unavailable. Refresh after the next playlist pipeline run.';
color: rgba(255, 255, 255, 0.58);
font-size: 12px;
font-weight: 700;
}
.auto-sync-history-entry:hover {
border-color: rgba(var(--accent-rgb), 0.3);
background:
@ -12047,6 +12062,7 @@ body.helper-mode-active #dashboard-activity-feed:hover {
min-height: 84px;
padding: 15px 16px;
cursor: pointer;
background: rgba(255, 255, 255, 0.018);
}
.auto-sync-history-status {