diff --git a/frontend/js/api.js b/frontend/js/api.js index 7d21838..e7f9772 100644 --- a/frontend/js/api.js +++ b/frontend/js/api.js @@ -115,6 +115,8 @@ export const api = { deletePlaylistItem: (id, itemId) => request(`/playlists/${id}/items/${itemId}`, { method: 'DELETE' }), reorderPlaylistItems: (id, order) => request(`/playlists/${id}/items/reorder`, { method: 'POST', body: JSON.stringify({ order }) }), assignPlaylistToDevice: (playlistId, device_id) => request(`/playlists/${playlistId}/assign`, { method: 'POST', body: JSON.stringify({ device_id }) }), + publishPlaylist: (id) => request(`/playlists/${id}/publish`, { method: 'POST' }), + discardPlaylistDraft: (id) => request(`/playlists/${id}/discard`, { method: 'POST' }), // Device Groups - Playlist groupAssignPlaylist: (groupId, playlist_id) => request(`/groups/${groupId}/assign-playlist`, { method: 'POST', body: JSON.stringify({ playlist_id }) }), diff --git a/frontend/js/views/device-detail.js b/frontend/js/views/device-detail.js index 20d36b7..4629a08 100644 --- a/frontend/js/views/device-detail.js +++ b/frontend/js/views/device-detail.js @@ -159,6 +159,21 @@ async function loadDevice(deviceId, activeTab = null) {
+ ${device.playlist_status === 'draft' ? ` +
+
+ +
+
Unpublished changes
+
${device.playlist_has_published ? 'Devices are still showing the last published version.' : 'This playlist has never been published. Devices will show nothing until you publish.'}
+
+
+
+ ${device.playlist_has_published ? '' : ''} + +
+
+ ` : ''}
@@ -568,6 +583,37 @@ async function setupActions(device) { } }); + // Publish / Discard from device detail + const devicePublishBtn = document.getElementById('devicePublishBtn'); + if (devicePublishBtn && device.playlist_id) { + devicePublishBtn.addEventListener('click', async () => { + try { + devicePublishBtn.disabled = true; + devicePublishBtn.textContent = 'Publishing...'; + await api.publishPlaylist(device.playlist_id); + showToast('Playlist published — devices updated'); + loadDevice(device.id, 'playlist'); + } catch (err) { + devicePublishBtn.disabled = false; + devicePublishBtn.textContent = 'Publish'; + showToast(err.message, 'error'); + } + }); + } + const deviceDiscardBtn = document.getElementById('deviceDiscardDraftBtn'); + if (deviceDiscardBtn && device.playlist_id) { + deviceDiscardBtn.addEventListener('click', async () => { + if (!confirm('Discard all unpublished changes and revert to the last published version?')) return; + try { + await api.discardPlaylistDraft(device.playlist_id); + showToast('Draft changes discarded'); + loadDevice(device.id, 'playlist'); + } catch (err) { + showToast(err.message, 'error'); + } + }); + } + // Populate playlist picker const playlistPicker = document.getElementById('playlistPicker'); if (playlistPicker) { diff --git a/frontend/js/views/playlists.js b/frontend/js/views/playlists.js index 92badb6..332e7b8 100644 --- a/frontend/js/views/playlists.js +++ b/frontend/js/views/playlists.js @@ -99,6 +99,7 @@ async function loadPlaylists() {
${esc(p.name)}
${p.is_auto_generated ? 'auto' : ''} + ${p.status === 'draft' ? 'draft' : ''}
${p.item_count} item${p.item_count !== 1 ? 's' : ''}
@@ -175,7 +176,26 @@ async function renderDetail(container, playlistId) { } function renderDetailContent(container, playlist) { + const isDraft = playlist.status === 'draft'; + const hasPublished = !!playlist.published_snapshot; + container.innerHTML = ` + ${isDraft ? ` +
+
+ +
+
Unpublished changes
+
${hasPublished ? 'Devices are still showing the last published version.' : 'This playlist has never been published. Devices will show nothing until you publish.'}
+
+
+
+ ${hasPublished ? '' : ''} + +
+
+ ` : ''} +