Skip to content

Commit 317b07b

Browse files
committed
✨ added new collection handling and rendering logic
1 parent 13a7ab3 commit 317b07b

2 files changed

Lines changed: 131 additions & 26 deletions

File tree

js/display.js

Lines changed: 54 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -432,9 +432,10 @@ function renderEpisodes(episodes, seasonNum) {
432432
* Creates a media card element for a given media item.
433433
* @param {Object} item - The media item to create a card for.
434434
* @param {string} [extraClasses=""] - Additional CSS classes to apply to the card.
435+
* @param {boolean} [showStatusBadges=true] - Whether to display watch/resume badges.
435436
* @returns {HTMLElement} The created media card element.
436437
*/
437-
export function createMediaCard(item, extraClasses = "") {
438+
export function createMediaCard(item, extraClasses = "", showStatusBadges = true) {
438439
const card = document.createElement('div');
439440
card.className = `media-card group relative rounded-xl overflow-hidden cursor-pointer bg-[#1a1a1a] shadow-lg transition-all duration-300 ${extraClasses}`;
440441

@@ -472,7 +473,9 @@ export function createMediaCard(item, extraClasses = "") {
472473
// Generate status badges HTML (watched/resume)
473474
const watchedBadge = watched ? '<span class="watched-pill"><i class="fas fa-eye text-xs"></i> Vu</span>' : '';
474475
const resumeBadge = !watched && hasResume ? '<span class="resume-pill">Reprendre</span>' : '';
475-
const badgeStack = watchedBadge || resumeBadge ? `<div class="status-badges">${watchedBadge}${resumeBadge}</div>` : '';
476+
const badgeStack = showStatusBadges && (watchedBadge || resumeBadge)
477+
? `<div class="status-badges">${watchedBadge}${resumeBadge}</div>`
478+
: '';
476479

477480
// Build media card HTML structure
478481
card.innerHTML = `
@@ -493,11 +496,14 @@ export function createMediaCard(item, extraClasses = "") {
493496
/**
494497
* Renders a grid of media items.
495498
* @param {Array} items - The list of media items to render.
499+
* @param {Object} [options={}] - Optional rendering options.
500+
* @param {boolean} [options.showStatusBadges=true] - Whether to display watch/resume badges.
496501
*/
497-
export function renderGrid(items) {
502+
export function renderGrid(items, options = {}) {
498503
const grid = document.getElementById('contentGrid');
499504
grid.innerHTML = '';
500-
items.forEach(item => grid.appendChild(createMediaCard(item, "aspect-[2/3]")));
505+
const { showStatusBadges = true } = options;
506+
items.forEach(item => grid.appendChild(createMediaCard(item, "aspect-[2/3]", showStatusBadges)));
501507
}
502508

503509
/**
@@ -535,6 +541,18 @@ export function renderCollections(collectionsData, appData) {
535541
return a.name.localeCompare(b.name);
536542
});
537543

544+
const slugifyCollection = (text) => String(text || '')
545+
.normalize('NFD')
546+
.replace(/[\u0300-\u036f]/g, '')
547+
.toLowerCase()
548+
.replace(/[^a-z0-9]+/g, '-')
549+
.replace(/(^-|-$)/g, '');
550+
551+
const grid = document.createElement('div');
552+
grid.className = "grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-5 gap-6 gap-y-10";
553+
554+
let hasRenderableCollection = false;
555+
538556
for (const [key, collection] of sortedCollections) {
539557
const items = [];
540558

@@ -558,29 +576,44 @@ export function renderCollections(collectionsData, appData) {
558576
if (items.length > 0) {
559577
items.sort((a, b) => a.year - b.year);
560578

561-
const section = document.createElement('section');
562-
section.className = "animate-fade-in-up";
579+
const firstFilmTitle = Array.isArray(collection.films) && collection.films.length > 0
580+
? collection.films[0]
581+
: null;
582+
const firstFilmItem = firstFilmTitle
583+
? Object.values(appData.films).find(f => f.title === firstFilmTitle)
584+
: null;
563585

564-
const titleHTML = `
565-
<h2 class="text-2xl font-bold mb-6 flex items-center gap-3">
566-
<span class="w-1.5 h-8 bg-red-600 rounded-full shadow-[0_0_15px_#dc2626]"></span>
567-
<span class="tracking-tight">${collection.name}</span>
568-
</h2>
569-
`;
570-
section.innerHTML = titleHTML;
586+
const previewItem = firstFilmItem || items[0];
587+
const collectionSlug = slugifyCollection(collection.name || key);
571588

572-
const rowDiv = document.createElement('div');
573-
rowDiv.className = "scroll-row flex gap-6 overflow-x-auto pb-8 hide-scrollbar scroll-smooth snap-x pl-1";
589+
hasRenderableCollection = true;
574590

575-
items.forEach(item => {
576-
const card = createMediaCard(item, "min-w-[200px] md:min-w-[280px] aspect-[2/3] snap-start");
577-
rowDiv.appendChild(card);
578-
});
591+
const previewWrapper = document.createElement('div');
592+
previewWrapper.className = "space-y-3 animate-fade-in-up";
593+
594+
const previewCard = createMediaCard(previewItem, "w-[200px] md:w-[280px] aspect-[2/3]", false);
579595

580-
section.appendChild(rowDiv);
581-
container.appendChild(section);
596+
previewCard.onclick = () => {
597+
if (typeof window !== 'undefined' && typeof window.router === 'function') {
598+
window.router('collections', { detail: { type: 'collections', slug: collectionSlug }, pushState: true });
599+
}
600+
};
601+
602+
const collectionTitle = document.createElement('h3');
603+
collectionTitle.className = "font-bold text-white text-base md:text-lg tracking-tight line-clamp-1";
604+
collectionTitle.textContent = collection.name;
605+
606+
previewWrapper.appendChild(collectionTitle);
607+
previewWrapper.appendChild(previewCard);
608+
grid.appendChild(previewWrapper);
582609
}
583610
}
611+
612+
if (hasRenderableCollection) {
613+
container.appendChild(grid);
614+
} else {
615+
container.innerHTML = '<div class="text-center text-gray-500 py-10">Aucune collection disponible.</div>';
616+
}
584617
}
585618

586619
/**

js/main.js

Lines changed: 77 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,50 @@ function findActorBySlug(slug) {
186186
return actors.find(actor => slugify(actor.name) === slug) || null;
187187
}
188188

189+
/**
190+
* Finds a collection by slug.
191+
* @param {string} slug - Collection slug.
192+
* @returns {Object|null} Collection object or null.
193+
*/
194+
function findCollectionBySlug(slug) {
195+
if (!slug) return null;
196+
const entries = Object.entries(appData.collections || {});
197+
for (const [key, collection] of entries) {
198+
const keySlug = slugify(key);
199+
const nameSlug = slugify(collection?.name || '');
200+
if (slug === keySlug || slug === nameSlug) {
201+
return collection;
202+
}
203+
}
204+
return null;
205+
}
206+
207+
/**
208+
* Builds media items array for a collection.
209+
* @param {Object} collection - Collection object.
210+
* @returns {Array} Ordered media items.
211+
*/
212+
function getCollectionItems(collection) {
213+
const items = [];
214+
if (!collection) return items;
215+
216+
if (Array.isArray(collection.films)) {
217+
collection.films.forEach(title => {
218+
const foundFilm = Object.values(appData.films).find(f => f.title === title);
219+
if (foundFilm) items.push(foundFilm);
220+
});
221+
}
222+
223+
if (Array.isArray(collection.series)) {
224+
collection.series.forEach(title => {
225+
const foundSerie = Object.values(appData.series).find(s => s.title === title);
226+
if (foundSerie) items.push(foundSerie);
227+
});
228+
}
229+
230+
return items;
231+
}
232+
189233
/**
190234
* Updates the URL path for the current view and optional detail.
191235
* @param {string} view - Base view.
@@ -300,6 +344,10 @@ function router(view, options = {}) {
300344
const navFilms = document.getElementById('nav-films');
301345
const navCollections = document.getElementById('nav-collections');
302346
const navActors = document.getElementById('nav-actors');
347+
const sectionTitle = document.getElementById('sectionTitle');
348+
349+
const existingBackBtn = document.getElementById('collectionBackBtn');
350+
if (existingBackBtn) existingBackBtn.remove();
303351

304352
// Reset all navigation link styles
305353
textWhite(navHome, navSeries, navFilms, navCollections, navActors);
@@ -355,12 +403,31 @@ function router(view, options = {}) {
355403
applyFilters();
356404
}
357405
else if (safeView === 'collections') {
358-
// Show collections view
406+
// Show collections list or a selected collection detail view
359407
if (navCollections) navCollections.classList.add('text-white');
360-
collectionsContent.classList.remove('hidden');
361-
renderCollections(appData.collections, appData);
408+
const selectedCollection = detail && detail.type === 'collections'
409+
? (detail.collection || findCollectionBySlug(detail.slug))
410+
: null;
411+
412+
if (selectedCollection) {
413+
genericGrid.classList.remove('hidden');
414+
title.innerText = selectedCollection.name || 'Collection';
415+
416+
if (sectionTitle) {
417+
const backBtn = document.createElement('button');
418+
backBtn.id = 'collectionBackBtn';
419+
backBtn.className = 'ml-auto text-sm font-bold text-gray-300 hover:text-white transition-colors flex items-center gap-2';
420+
backBtn.innerHTML = '<i class="fas fa-arrow-left text-xs"></i><span>Retour aux collections</span>';
421+
backBtn.onclick = () => router('collections');
422+
sectionTitle.appendChild(backBtn);
423+
}
362424

363-
enableHorizontalWheelScroll();
425+
renderGrid(getCollectionItems(selectedCollection), { showStatusBadges: false });
426+
} else {
427+
collectionsContent.classList.remove('hidden');
428+
renderCollections(appData.collections, appData);
429+
enableHorizontalWheelScroll();
430+
}
364431
}
365432
else if (safeView === 'actors') {
366433
// Show actors view
@@ -525,7 +592,12 @@ function handleSearch(e) {
525592
if (currentView === 'home') {
526593
hero.classList.remove('hidden'); homeContent.classList.remove('hidden'); genericGrid.classList.add('hidden');
527594
} else if (currentView === 'collections') {
528-
collectionsContent.classList.remove('hidden'); genericGrid.classList.add('hidden');
595+
const { detail } = parseLocationRoute();
596+
if (detail && detail.type === 'collections') {
597+
router('collections', { pushState: false, detail });
598+
} else {
599+
collectionsContent.classList.remove('hidden'); genericGrid.classList.add('hidden');
600+
}
529601
} else if (currentView === 'actors') {
530602
actorsContent.classList.remove('hidden'); genericGrid.classList.add('hidden');
531603
renderActorsList(appData.actors, appData.films, appData.series);

0 commit comments

Comments
 (0)