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:
parent
06c76bbcaf
commit
119e8f1599
2 changed files with 53 additions and 2 deletions
|
|
@ -438,13 +438,14 @@ function renderAutoSyncHistoryPanel(history, total) {
|
||||||
<button onclick="refreshAutoSyncScheduleModal()">Refresh</button>
|
<button onclick="refreshAutoSyncScheduleModal()">Refresh</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="auto-sync-history-list">
|
<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>` : ''}
|
${total > history.length ? `<div class="auto-sync-history-total">Showing ${history.length} of ${total} runs</div>` : ''}
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function autoSyncHistoryEntryHtml(entry) {
|
function autoSyncHistoryEntryHtml(entry, index = 0) {
|
||||||
|
entry = autoSyncNormalizeHistoryEntry(entry, index);
|
||||||
const status = entry.status || 'completed';
|
const status = entry.status || 'completed';
|
||||||
const before = entry.before_json || {};
|
const before = entry.before_json || {};
|
||||||
const after = entry.after_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) {
|
function autoSyncHistoryFallbackSummary(before, after, status) {
|
||||||
const beforeTracks = parseInt(before.track_count, 10) || 0;
|
const beforeTracks = parseInt(before.track_count, 10) || 0;
|
||||||
const afterTracks = parseInt(after.track_count, 10) || 0;
|
const afterTracks = parseInt(after.track_count, 10) || 0;
|
||||||
|
|
|
||||||
|
|
@ -12023,6 +12023,8 @@ body.helper-mode-active #dashboard-activity-feed:hover {
|
||||||
}
|
}
|
||||||
|
|
||||||
.auto-sync-history-entry {
|
.auto-sync-history-entry {
|
||||||
|
display: block;
|
||||||
|
min-height: 84px;
|
||||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
background:
|
background:
|
||||||
|
|
@ -12032,6 +12034,19 @@ body.helper-mode-active #dashboard-activity-feed:hover {
|
||||||
box-shadow: 0 10px 26px rgba(0, 0, 0, 0.14);
|
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 {
|
.auto-sync-history-entry:hover {
|
||||||
border-color: rgba(var(--accent-rgb), 0.3);
|
border-color: rgba(var(--accent-rgb), 0.3);
|
||||||
background:
|
background:
|
||||||
|
|
@ -12047,6 +12062,7 @@ body.helper-mode-active #dashboard-activity-feed:hover {
|
||||||
min-height: 84px;
|
min-height: 84px;
|
||||||
padding: 15px 16px;
|
padding: 15px 16px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
background: rgba(255, 255, 255, 0.018);
|
||||||
}
|
}
|
||||||
|
|
||||||
.auto-sync-history-status {
|
.auto-sync-history-status {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue