beatport progress

This commit is contained in:
Broque Thomas 2025-09-28 18:16:28 -07:00
parent f7119b7319
commit 5981bd772f
3 changed files with 260 additions and 226 deletions

View file

@ -409,61 +409,104 @@
</div>
<div class="beatport-navigation">
<!-- Main Category Cards View -->
<!-- New Homepage Main View -->
<div class="beatport-main-view active" id="beatport-main-view">
<div class="beatport-category-cards">
<div class="beatport-category-card" data-category="top-charts">
<div class="category-icon top-charts-icon"></div>
<h3>🔥 Top Charts</h3>
<p>Beatport Top 100, Top 100 New Releases, Hype Top 100</p>
<span class="category-count">3 Charts</span>
</div>
<div class="beatport-category-card" data-category="genres">
<div class="category-icon genres-icon"></div>
<h3>🎵 Genre Explorer</h3>
<p>House, Techno, Trance, and 36 more genres</p>
<span class="category-count">39 Genres</span>
</div>
<div class="beatport-category-card" data-category="staff-picks">
<div class="category-icon staff-picks-icon"></div>
<h3>📊 Staff Picks</h3>
<p>Curated selections and secret weapons</p>
<span class="category-count">5 Collections</span>
</div>
</div>
</div>
<!-- Top Charts Sub-View -->
<div class="beatport-sub-view" id="beatport-top-charts-view">
<div class="beatport-breadcrumb">
<button class="breadcrumb-back">← Back to Categories</button>
<span class="breadcrumb-path">Browse Charts > Top Charts</span>
</div>
<div class="beatport-chart-list">
<div class="beatport-chart-item" data-chart-type="top-100" data-chart-id="top-100" data-chart-name="Beatport Top 100" data-chart-endpoint="/api/beatport/top-100">
<div class="chart-icon">🏆</div>
<div class="chart-info">
<h3>Beatport Top 100</h3>
<p>The hottest electronic tracks right now</p>
<span class="track-count">100 tracks</span>
<!-- Genre Explorer Section -->
<div class="homepage-genre-section">
<h3 class="section-title">🎵 Genre Explorer</h3>
<div class="genre-chart-types-grid">
<div class="genre-chart-type-card" data-action="show-genres">
<div class="chart-type-icon">🎵</div>
<div class="chart-type-info">
<h3>Browse All Genres</h3>
<p>House, Techno, Trance, and 36 more genres</p>
<span class="track-count">39 Genres</span>
</div>
</div>
</div>
<div class="beatport-chart-item" data-chart-type="top-100-releases" data-chart-id="top-100-releases" data-chart-name="Top 100 New Releases" data-chart-endpoint="/api/beatport/top-100-releases">
<div class="chart-icon">📀</div>
<div class="chart-info">
<h3>Top 100 New Releases</h3>
<p>Newest and most popular releases on Beatport</p>
<span class="track-count">100 tracks</span>
</div>
<!-- Main Charts Section -->
<div class="homepage-main-charts-section">
<h3 class="section-title">📊 Main Charts</h3>
<div class="genre-chart-types-grid">
<div class="genre-chart-type-card" data-chart-type="top-10" data-chart-endpoint="/api/beatport/top-100">
<div class="chart-type-icon">🔥</div>
<div class="chart-type-info">
<h3>Beatport Top 10</h3>
<p>Current hottest tracks</p>
<span class="track-count">10 tracks</span>
</div>
</div>
<div class="genre-chart-type-card" data-chart-type="top-100" data-chart-endpoint="/api/beatport/top-100">
<div class="chart-type-icon">💯</div>
<div class="chart-type-info">
<h3>Beatport Top 100</h3>
<p>Complete chart rankings</p>
<span class="track-count">100 tracks</span>
</div>
</div>
</div>
<div class="beatport-chart-item" data-chart-type="hype" data-chart-id="hype-10" data-chart-name="Hype Top 100" data-chart-endpoint="/api/beatport/hype-top-100">
<div class="chart-icon">🚀</div>
<div class="chart-info">
<h3>Hype Top 100</h3>
<p>Rising stars and breakthrough tracks</p>
<span class="track-count">100 tracks</span>
</div>
<!-- Releases Section -->
<div class="homepage-releases-section">
<h3 class="section-title">🎵 Releases</h3>
<div class="genre-chart-types-grid">
<div class="genre-chart-type-card" data-chart-type="releases-top-10" data-chart-endpoint="/api/beatport/homepage/releases-top-10">
<div class="chart-type-icon">🆕</div>
<div class="chart-type-info">
<h3>Top 10 Releases</h3>
<p>Newest releases trending</p>
<span class="track-count">10 releases</span>
</div>
</div>
<div class="genre-chart-type-card" data-chart-type="releases-top-100" data-chart-endpoint="/api/beatport/top-100-releases">
<div class="chart-type-icon">📊</div>
<div class="chart-type-info">
<h3>Top 100 Releases</h3>
<p>All trending releases</p>
<span class="track-count">100 releases</span>
</div>
</div>
<div class="genre-chart-type-card" data-chart-type="latest-releases" data-chart-endpoint="/api/beatport/homepage/latest-releases">
<div class="chart-type-icon">🕒</div>
<div class="chart-type-info">
<h3>Latest Releases</h3>
<p>Recently published</p>
<span class="track-count">50 releases</span>
</div>
</div>
</div>
</div>
<!-- Hype Section -->
<div class="homepage-hype-section">
<h3 class="section-title">🔥 Hype</h3>
<div class="genre-chart-types-grid">
<div class="genre-chart-type-card" data-chart-type="hype-top-10" data-chart-endpoint="/api/beatport/homepage/hype-top-10">
<div class="chart-type-icon">🚀</div>
<div class="chart-type-info">
<h3>Hype Top 10</h3>
<p>Hottest trending tracks</p>
<span class="track-count">10 tracks</span>
</div>
</div>
<div class="genre-chart-type-card" data-chart-type="hype-top-100" data-chart-endpoint="/api/beatport/hype-top-100">
<div class="chart-type-icon">🔥</div>
<div class="chart-type-info">
<h3>Hype Top 100</h3>
<p>Complete hype chart rankings</p>
<span class="track-count">100 tracks</span>
</div>
</div>
<div class="genre-chart-type-card" data-chart-type="hype-picks" data-chart-endpoint="/api/beatport/homepage/hype-picks">
<div class="chart-type-icon"></div>
<div class="chart-type-info">
<h3>Hype Picks</h3>
<p>Editor selected hype tracks</p>
<span class="track-count">50 tracks</span>
</div>
</div>
</div>
</div>
@ -692,55 +735,6 @@
</div>
</div>
<!-- Staff Picks Sub-View -->
<div class="beatport-sub-view" id="beatport-staff-picks-view">
<div class="beatport-breadcrumb">
<button class="breadcrumb-back">← Back to Categories</button>
<span class="breadcrumb-path">Browse Charts > Staff Picks</span>
</div>
<div class="beatport-chart-list">
<div class="beatport-chart-item" data-chart-type="staff-pick" data-chart-id="best-new-tracks">
<div class="chart-icon"></div>
<div class="chart-info">
<h3>Best New Tracks</h3>
<p>Editor's picks of the week</p>
<span class="track-count">20 tracks</span>
</div>
</div>
<div class="beatport-chart-item" data-chart-type="staff-pick" data-chart-id="secret-weapons">
<div class="chart-icon">🔫</div>
<div class="chart-info">
<h3>Secret Weapons</h3>
<p>Underground gems for DJs</p>
<span class="track-count">15 tracks</span>
</div>
</div>
<div class="beatport-chart-item" data-chart-type="staff-pick" data-chart-id="closing-essentials">
<div class="chart-icon">🌅</div>
<div class="chart-info">
<h3>Closing Essentials</h3>
<p>Perfect tracks to end the night</p>
<span class="track-count">12 tracks</span>
</div>
</div>
<div class="beatport-chart-item" data-chart-type="staff-pick" data-chart-id="peak-time-driving">
<div class="chart-icon">🔥</div>
<div class="chart-info">
<h3>Peak Time Driving</h3>
<p>High-energy dancefloor destroyers</p>
<span class="track-count">25 tracks</span>
</div>
</div>
<div class="beatport-chart-item" data-chart-type="staff-pick" data-chart-id="underground-gems">
<div class="chart-icon">💎</div>
<div class="chart-info">
<h3>Underground Gems</h3>
<p>Hidden treasures from emerging artists</p>
<span class="track-count">18 tracks</span>
</div>
</div>
</div>
</div>
</div>
</div>

View file

@ -9733,14 +9733,18 @@ function initializeSyncPage() {
});
});
// Logic for Beatport category cards
const beatportCategoryCards = document.querySelectorAll('.beatport-category-card');
beatportCategoryCards.forEach(card => {
card.addEventListener('click', () => {
const category = card.dataset.category;
handleBeatportCategoryClick(category);
// Logic for Homepage Genre Explorer card
const genreExplorerCard = document.querySelector('[data-action="show-genres"]');
if (genreExplorerCard) {
genreExplorerCard.addEventListener('click', () => {
console.log('🎵 Genre Explorer card clicked');
showBeatportSubView('genres');
loadBeatportGenres();
});
});
}
// Setup homepage chart handlers (following genre page pattern to prevent duplicates)
setupHomepageChartTypeHandlers();
// Logic for Beatport breadcrumb back buttons
const beatportBackButtons = document.querySelectorAll('.breadcrumb-back');
@ -10188,19 +10192,12 @@ async function clearBeatportPlaylists() {
function handleBeatportCategoryClick(category) {
console.log(`🎵 Beatport category clicked: ${category}`);
// Show the appropriate sub-view based on category
// Only handle genres category now - homepage has direct chart buttons
switch(category) {
case 'top-charts':
showBeatportSubView('top-charts');
loadBeatportTopCharts(); // Load top charts dynamically
break;
case 'genres':
showBeatportSubView('genres');
loadBeatportGenres(); // Load genres dynamically
break;
case 'staff-picks':
showBeatportSubView('staff-picks');
break;
default:
showToast(`Unknown category: ${category}`, 'error');
}
@ -10369,159 +10366,148 @@ async function loadGenreImagesProgressively(genres) {
console.log(`✅ Progressive image loading complete: ${imagesLoaded}/${genres.length} images loaded`);
}
async function loadBeatportTopCharts() {
console.log('🔥 Loading Beatport top charts dynamically...');
function setupHomepageChartTypeHandlers() {
console.log('🔧 Setting up homepage chart type handlers...');
const chartList = document.querySelector('#beatport-top-charts-view .beatport-chart-list');
if (!chartList) {
console.error('❌ Could not find chart list element');
// Select all homepage chart type cards (following genre page pattern)
const chartTypeCards = document.querySelectorAll('.homepage-main-charts-section .genre-chart-type-card[data-chart-type], .homepage-releases-section .genre-chart-type-card[data-chart-type], .homepage-hype-section .genre-chart-type-card[data-chart-type]');
chartTypeCards.forEach(card => {
// Remove existing listeners by cloning (following genre page pattern)
card.replaceWith(card.cloneNode(true));
});
// Re-select after cloning to ensure clean event listeners (following genre page pattern)
const newChartTypeCards = document.querySelectorAll('.homepage-main-charts-section .genre-chart-type-card[data-chart-type], .homepage-releases-section .genre-chart-type-card[data-chart-type], .homepage-hype-section .genre-chart-type-card[data-chart-type]');
newChartTypeCards.forEach(card => {
card.addEventListener('click', () => {
const chartType = card.dataset.chartType;
const chartEndpoint = card.dataset.chartEndpoint;
const chartName = card.querySelector('.chart-type-info h3').textContent;
console.log(`🔥 Homepage chart clicked: ${chartName} (${chartType})`);
handleHomepageChartTypeClick(chartType, chartEndpoint, chartName);
});
});
console.log(`✅ Setup ${newChartTypeCards.length} homepage chart handlers`);
}
async function handleHomepageChartTypeClick(chartType, chartEndpoint, chartName) {
console.log(`🔥 Homepage chart type clicked: ${chartType} (${chartName})`);
// Map chart types to API endpoints and create descriptive names (following genre page pattern)
const chartTypeMap = {
'top-10': {
endpoint: `/api/beatport/top-100`, // Use top-100 endpoint and limit to 10
name: `Beatport Top 10`,
limit: 10
},
'top-100': {
endpoint: `/api/beatport/top-100`,
name: `Beatport Top 100`,
limit: 100
},
'releases-top-10': {
endpoint: `/api/beatport/homepage/releases-top-10`, // Placeholder for future
name: `Top 10 Releases`,
limit: 10
},
'releases-top-100': {
endpoint: `/api/beatport/top-100-releases`,
name: `Top 100 Releases`,
limit: 100
},
'latest-releases': {
endpoint: `/api/beatport/homepage/latest-releases`, // Placeholder for future
name: `Latest Releases`,
limit: 50
},
'hype-top-10': {
endpoint: `/api/beatport/hype-top-100`, // Use hype-100 endpoint and limit to 10
name: `Hype Top 10`,
limit: 10
},
'hype-top-100': {
endpoint: `/api/beatport/hype-top-100`,
name: `Hype Top 100`,
limit: 100
},
'hype-picks': {
endpoint: `/api/beatport/homepage/hype-picks`, // Placeholder for future
name: `Hype Picks`,
limit: 50
}
};
const chartConfig = chartTypeMap[chartType];
if (!chartConfig) {
console.error(`❌ Unknown homepage chart type: ${chartType}`);
showToast(`Unknown chart type: ${chartType}`, 'error');
return;
}
// Show loading state
chartList.innerHTML = `
<div class="chart-loading-placeholder">
<div class="loading-spinner"></div>
<p>🔥 Discovering current Beatport top charts...</p>
</div>
`;
try {
// Define the top charts we want to show (based on our working endpoints)
const topCharts = [
{
id: 'beatport-top-100',
name: 'Beatport Top 100',
description: 'The hottest electronic tracks right now',
icon: '🏆',
endpoint: '/api/beatport/top-100',
trackCount: '100 tracks'
},
{
id: 'top-100-releases',
name: 'Top 100 New Releases',
description: 'Newest and most popular releases on Beatport',
icon: '📀',
endpoint: '/api/beatport/top-100-releases',
trackCount: '100 tracks'
},
{
id: 'hype-top-100',
name: 'Hype Top 100',
description: 'Trending tracks gaining momentum',
icon: '🚀',
endpoint: '/api/beatport/hype-top-100',
trackCount: '100 tracks'
}
];
// Generate chart cards dynamically
const chartCardsHTML = topCharts.map(chart => `
<div class="beatport-chart-item"
data-chart-type="${chart.id}"
data-chart-id="${chart.id}"
data-chart-name="${chart.name}"
data-chart-endpoint="${chart.endpoint}">
<div class="chart-icon">${chart.icon}</div>
<div class="chart-info">
<h3>${chart.name}</h3>
<p>${chart.description}</p>
<span class="track-count">${chart.trackCount}</span>
</div>
</div>
`).join('');
chartList.innerHTML = chartCardsHTML;
// Add click handlers to dynamically created chart items
const chartItems = chartList.querySelectorAll('.beatport-chart-item');
chartItems.forEach(item => {
item.addEventListener('click', () => {
const chartType = item.dataset.chartType;
const chartId = item.dataset.chartId;
const chartName = item.dataset.chartName;
const chartEndpoint = item.dataset.chartEndpoint;
handleBeatportTopChartClick(chartType, chartId, chartName, chartEndpoint);
});
});
console.log(`✅ Loaded ${topCharts.length} Beatport top charts dynamically`);
showToast(`Loaded ${topCharts.length} top charts`, 'success');
} catch (error) {
console.error('❌ Error loading Beatport top charts:', error);
chartList.innerHTML = `
<div class="chart-error-placeholder">
<p> Failed to load top charts: ${error.message}</p>
<button onclick="loadBeatportTopCharts()" class="refresh-charts-btn">🔄 Retry</button>
</div>
`;
showToast(`Error loading Beatport top charts: ${error.message}`, 'error');
}
}
async function handleBeatportTopChartClick(chartType, chartId, chartName, chartEndpoint) {
console.log(`🔥 Beatport top chart clicked: ${chartName} - CREATING PLAYLIST CARD`);
try {
// Check if we already have a card for this chart
// Check if we already have a card for this specific chart type (following genre page pattern)
const existingState = Object.values(beatportChartStates).find(state =>
state.chart && state.chart.name === chartName && state.chart.chart_type === chartType
state.chart && state.chart.name === chartConfig.name && state.chart.chart_type === `homepage_${chartType}`
);
if (existingState) {
console.log(`🔄 Found existing Beatport card for ${chartName}, opening existing modal`);
console.log(`🔄 Found existing Beatport card for ${chartConfig.name}, opening existing modal`);
handleBeatportCardClick(existingState.chart.hash);
return;
}
// First, create a chart hash for state management
const chartHash = `${chartType}_${chartId}_${Date.now()}`;
// Create a chart hash for state management (following genre page pattern)
const chartHash = `homepage_${chartType}_${Date.now()}`;
showToast(`Loading ${chartName}...`, 'info');
showToast(`Loading ${chartConfig.name}...`, 'info');
// Fetch tracks from the chart endpoint
const response = await fetch(`${chartEndpoint}?limit=100`);
// Fetch tracks from the specific endpoint (following genre page pattern)
const response = await fetch(`${chartConfig.endpoint}?limit=${chartConfig.limit}`);
if (!response.ok) {
throw new Error(`Failed to fetch ${chartName}: ${response.status}`);
throw new Error(`Failed to fetch ${chartConfig.name}: ${response.status}`);
}
const data = await response.json();
if (!data.success || !data.tracks || data.tracks.length === 0) {
throw new Error(`No tracks found in ${chartName}`);
throw new Error(`No tracks found in ${chartConfig.name}`);
}
// Create chart data object for playlist card
// Create chart data object for playlist card (following genre page pattern)
const chartData = {
hash: chartHash,
name: chartName,
chart_type: chartType,
name: chartConfig.name,
chart_type: `homepage_${chartType}`,
track_count: data.tracks.length,
tracks: data.tracks.map(track => ({
name: track.title || 'Unknown Title',
artists: [track.artist || 'Unknown Artist'],
album: chartName,
album: chartConfig.name,
duration_ms: 0,
external_urls: { beatport: track.url || '' },
source: 'beatport'
}))
};
// Add card to container (in background, like YouTube does)
console.log(`🃏 Creating Beatport playlist card for: ${chartName}`);
// Add card to container (in background, like YouTube does - following genre page pattern)
console.log(`🃏 Creating Beatport playlist card for: ${chartConfig.name}`);
addBeatportCardToContainer(chartData);
// Automatically open discovery modal (like when you click a YouTube or Tidal card in fresh state)
// Automatically open discovery modal (like when you click a YouTube or Tidal card in fresh state - following genre page pattern)
handleBeatportCardClick(chartHash);
console.log(`✅ Created Beatport card and opened discovery modal for ${chartName}`);
console.log(`✅ Created Beatport card and opened discovery modal for ${chartConfig.name}`);
} catch (error) {
console.error(`❌ Error loading ${chartName}:`, error);
showToast(`Error loading ${chartName}: ${error.message}`, 'error');
console.error(`❌ Error loading ${chartConfig.name}:`, error);
showToast(`Error loading ${chartConfig.name}: ${error.message}`, 'error');
}
}
async function openBeatportDiscoveryModal(chartHash, chartData) {
console.log(`🎵 Opening Beatport discovery modal (reusing YouTube modal): ${chartData.name}`);

View file

@ -4893,7 +4893,11 @@ body {
.genre-main-charts-section,
.genre-releases-section,
.genre-editorial-section,
.genre-new-charts-section {
.genre-new-charts-section,
.homepage-genre-section,
.homepage-main-charts-section,
.homepage-releases-section,
.homepage-hype-section {
margin-bottom: 40px;
padding: 20px;
background: linear-gradient(135deg,
@ -5065,6 +5069,52 @@ body {
letter-spacing: 0.5px;
}
/* Empty Section Placeholder */
.empty-section-placeholder {
display: flex;
align-items: center;
gap: 20px;
padding: 40px 20px;
background: linear-gradient(135deg,
rgba(30, 30, 30, 0.5) 0%,
rgba(20, 20, 20, 0.7) 100%);
border-radius: 12px;
border: 2px dashed rgba(255, 255, 255, 0.15);
text-align: center;
opacity: 0.8;
transition: all 0.3s ease;
min-height: 120px;
justify-content: center;
}
.empty-section-placeholder:hover {
opacity: 1;
border-color: rgba(255, 255, 255, 0.25);
background: linear-gradient(135deg,
rgba(35, 35, 35, 0.6) 0%,
rgba(25, 25, 25, 0.8) 100%);
}
.empty-section-icon {
font-size: 48px;
opacity: 0.6;
filter: grayscale(1);
}
.empty-section-text h4 {
font-size: 18px;
font-weight: 600;
color: rgba(255, 255, 255, 0.8);
margin: 0 0 8px 0;
}
.empty-section-text p {
font-size: 14px;
color: rgba(255, 255, 255, 0.5);
margin: 0;
font-style: italic;
}
.expand-indicator {
position: absolute;
top: 50%;
@ -5520,7 +5570,11 @@ body {
.genre-main-charts-section,
.genre-releases-section,
.genre-editorial-section,
.genre-new-charts-section {
.genre-new-charts-section,
.homepage-genre-section,
.homepage-main-charts-section,
.homepage-releases-section,
.homepage-hype-section {
margin-bottom: 24px;
padding: 16px;
}