Add batch remove to watchlist modal
This commit is contained in:
parent
139b8530f4
commit
0c40a922e6
3 changed files with 197 additions and 3 deletions
|
|
@ -19144,6 +19144,32 @@ def remove_from_watchlist():
|
|||
print(f"Error removing from watchlist: {e}")
|
||||
return jsonify({"success": False, "error": str(e)}), 500
|
||||
|
||||
@app.route('/api/watchlist/remove-batch', methods=['POST'])
|
||||
def remove_batch_from_watchlist():
|
||||
"""Remove multiple artists from the watchlist"""
|
||||
try:
|
||||
data = request.get_json()
|
||||
artist_ids = data.get('artist_ids', [])
|
||||
|
||||
if not artist_ids or not isinstance(artist_ids, list):
|
||||
return jsonify({"success": False, "error": "Missing or invalid artist_ids"}), 400
|
||||
|
||||
database = get_database()
|
||||
removed = 0
|
||||
for artist_id in artist_ids:
|
||||
if database.remove_artist_from_watchlist(artist_id):
|
||||
removed += 1
|
||||
|
||||
return jsonify({
|
||||
"success": True,
|
||||
"removed": removed,
|
||||
"message": f"Removed {removed} artist{'s' if removed != 1 else ''} from watchlist"
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error batch removing from watchlist: {e}")
|
||||
return jsonify({"success": False, "error": str(e)}), 500
|
||||
|
||||
@app.route('/api/watchlist/check', methods=['POST'])
|
||||
def check_watchlist_status():
|
||||
"""Check if an artist is in the watchlist"""
|
||||
|
|
|
|||
|
|
@ -24095,12 +24095,29 @@ async function showWatchlistModal() {
|
|||
oninput="filterWatchlistArtists()">
|
||||
</div>
|
||||
|
||||
<!-- Batch action bar (hidden until selection) -->
|
||||
<div class="watchlist-batch-bar" id="watchlist-batch-bar" style="display: none;">
|
||||
<span class="watchlist-batch-count" id="watchlist-batch-count">0 selected</span>
|
||||
<button class="playlist-modal-btn playlist-modal-btn-secondary watchlist-batch-remove-btn"
|
||||
id="watchlist-batch-remove-btn"
|
||||
onclick="batchRemoveFromWatchlist()">
|
||||
Remove Selected
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="watchlist-artists-list" id="watchlist-artists-list">
|
||||
${artistsData.artists.map(artist => `
|
||||
<div class="watchlist-artist-item"
|
||||
data-artist-name="${artist.artist_name.toLowerCase().replace(/"/g, '"')}"
|
||||
data-artist-id="${artist.spotify_artist_id || artist.itunes_artist_id}"
|
||||
style="cursor: pointer;">
|
||||
<label class="watchlist-checkbox-wrapper" onclick="event.stopPropagation();">
|
||||
<input type="checkbox" class="watchlist-select-cb"
|
||||
data-artist-id="${artist.spotify_artist_id || artist.itunes_artist_id}"
|
||||
data-artist-name="${escapeHtml(artist.artist_name)}"
|
||||
onchange="updateWatchlistBatchBar()">
|
||||
<span class="watchlist-checkbox-custom"></span>
|
||||
</label>
|
||||
${artist.image_url ? `
|
||||
<img src="${artist.image_url}"
|
||||
alt="${escapeHtml(artist.artist_name)}"
|
||||
|
|
@ -24147,11 +24164,11 @@ async function showWatchlistModal() {
|
|||
});
|
||||
});
|
||||
|
||||
// Add click handlers to artist items (except for remove button)
|
||||
// Add click handlers to artist items (except for remove button or checkbox)
|
||||
modal.querySelectorAll('.watchlist-artist-item').forEach(item => {
|
||||
item.addEventListener('click', (e) => {
|
||||
// Don't trigger if clicking the remove button
|
||||
if (e.target.closest('.watchlist-remove-btn')) {
|
||||
// Don't trigger if clicking the remove button or checkbox
|
||||
if (e.target.closest('.watchlist-remove-btn') || e.target.closest('.watchlist-checkbox-wrapper')) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -24444,6 +24461,9 @@ function filterWatchlistArtists() {
|
|||
item.style.display = 'none';
|
||||
}
|
||||
});
|
||||
|
||||
// Refresh batch bar in case visible selection changed
|
||||
updateWatchlistBatchBar();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -24759,6 +24779,73 @@ async function removeFromWatchlistModal(artistId, artistName) {
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get visible checked checkboxes (not hidden by search filter)
|
||||
*/
|
||||
function getVisibleCheckedWatchlist() {
|
||||
return Array.from(document.querySelectorAll('.watchlist-select-cb:checked')).filter(cb => {
|
||||
const item = cb.closest('.watchlist-artist-item');
|
||||
return item && item.style.display !== 'none';
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the batch action bar based on checkbox selection
|
||||
*/
|
||||
function updateWatchlistBatchBar() {
|
||||
const checked = getVisibleCheckedWatchlist();
|
||||
const bar = document.getElementById('watchlist-batch-bar');
|
||||
const countEl = document.getElementById('watchlist-batch-count');
|
||||
|
||||
if (checked.length > 0) {
|
||||
bar.style.display = 'flex';
|
||||
countEl.textContent = `${checked.length} selected`;
|
||||
} else {
|
||||
bar.style.display = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Batch remove selected artists from watchlist
|
||||
*/
|
||||
async function batchRemoveFromWatchlist() {
|
||||
const checked = getVisibleCheckedWatchlist();
|
||||
if (checked.length === 0) return;
|
||||
|
||||
const count = checked.length;
|
||||
if (!confirm(`Remove ${count} artist${count !== 1 ? 's' : ''} from your watchlist?`)) return;
|
||||
|
||||
const artistIds = checked.map(cb => cb.getAttribute('data-artist-id'));
|
||||
|
||||
try {
|
||||
const response = await fetch('/api/watchlist/remove-batch', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ artist_ids: artistIds })
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
if (!data.success) {
|
||||
throw new Error(data.error || 'Failed to remove artists');
|
||||
}
|
||||
|
||||
console.log(`❌ Batch removed ${data.removed} artists from watchlist`);
|
||||
|
||||
// Refresh the modal
|
||||
showWatchlistModal();
|
||||
|
||||
// Update button count
|
||||
updateWatchlistButtonCount();
|
||||
|
||||
// Update any visible artist cards
|
||||
updateArtistCardWatchlistStatus();
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error batch removing from watchlist:', error);
|
||||
alert(`Error removing artists: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
// --- Metadata Updater Functions ---
|
||||
|
||||
// Global state for metadata update polling
|
||||
|
|
|
|||
|
|
@ -8221,6 +8221,87 @@ body {
|
|||
transform: scale(1.05);
|
||||
}
|
||||
|
||||
/* Watchlist Batch Action Bar */
|
||||
.watchlist-batch-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 10px 20px;
|
||||
margin: 0 30px 12px;
|
||||
background: rgba(255, 59, 48, 0.1);
|
||||
border: 1px solid rgba(255, 59, 48, 0.25);
|
||||
border-radius: 10px;
|
||||
animation: batchBarSlideIn 0.2s ease-out;
|
||||
}
|
||||
|
||||
@keyframes batchBarSlideIn {
|
||||
from { opacity: 0; transform: translateY(-8px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
|
||||
.watchlist-batch-count {
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
color: rgba(255, 255, 255, 0.8);
|
||||
}
|
||||
|
||||
.watchlist-batch-remove-btn {
|
||||
border-color: rgba(255, 59, 48, 0.4) !important;
|
||||
color: #ff6b6b !important;
|
||||
}
|
||||
|
||||
.watchlist-batch-remove-btn:hover {
|
||||
background: rgba(255, 59, 48, 0.2) !important;
|
||||
border-color: rgba(255, 59, 48, 0.6) !important;
|
||||
}
|
||||
|
||||
/* Watchlist Checkbox */
|
||||
.watchlist-checkbox-wrapper {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-right: 12px;
|
||||
flex-shrink: 0;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.watchlist-checkbox-wrapper input[type="checkbox"] {
|
||||
position: absolute;
|
||||
opacity: 0;
|
||||
width: 0;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
.watchlist-checkbox-custom {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border: 2px solid rgba(255, 255, 255, 0.2);
|
||||
border-radius: 4px;
|
||||
background: rgba(255, 255, 255, 0.03);
|
||||
transition: all 0.15s ease;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.watchlist-checkbox-wrapper:hover .watchlist-checkbox-custom {
|
||||
border-color: rgba(255, 255, 255, 0.4);
|
||||
background: rgba(255, 255, 255, 0.06);
|
||||
}
|
||||
|
||||
.watchlist-checkbox-wrapper input:checked + .watchlist-checkbox-custom {
|
||||
background: rgba(29, 185, 84, 0.3);
|
||||
border-color: #1db954;
|
||||
}
|
||||
|
||||
.watchlist-checkbox-wrapper input:checked + .watchlist-checkbox-custom::after {
|
||||
content: '✓';
|
||||
color: #1db954;
|
||||
font-size: 13px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
/* ===== WISHLIST OVERVIEW MODAL STYLES ===== */
|
||||
|
||||
/* Category Grid */
|
||||
|
|
|
|||
Loading…
Reference in a new issue