Files
mon-petit-cinema/js/public.js
T
2026-06-19 20:17:32 +02:00

355 lines
14 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
let films = [];
const API_URL = '../api.php';
let currentPubTab = 'critique';
let activeRatingFilter = 0;
let activeStreamingFilter = '';
let searchQuery = '';
let currentPage = 1;
const itemsPerPage = 12;
async function loadPublicData() {
try {
const response = await fetch(`${API_URL}?action=get_films`);
films = await response.json();
generateStreamingSelect(); // Remplit le menu déroulant
renderPublicGrid();
} catch (error) {
console.error("Erreur de récupération :", error);
}
}
// ── GÉNÉRATION DU MENU DÉROULANT STREAMING ──
function generateStreamingSelect() {
const select = document.getElementById('pub-streaming-select');
if (!select) return;
// Extraire toutes les plateformes uniques
const platforms = new Set();
films.forEach(f => {
if (f.type === 'critique' && f.streaming &&
f.streaming !== 'Disponible en support physique ou Cinéma' &&
f.streaming.trim() !== '') {
// Séparer par virgule si plusieurs plateformes
f.streaming.split(',').forEach(p => {
const platform = p.trim();
if (platform.length > 0) platforms.add(platform);
});
}
});
// Trier alphabétiquement
const sortedPlatforms = Array.from(platforms).sort();
// Conserver l'option "Toutes" et ajouter les plateformes
select.innerHTML = '<option value="">Toutes les plateformes</option>';
sortedPlatforms.forEach(platform => {
const option = document.createElement('option');
option.value = platform;
option.textContent = platform;
select.appendChild(option);
});
// Écouteur d'événement
select.addEventListener('change', (e) => {
activeStreamingFilter = e.target.value;
currentPage = 1;
renderPublicGrid();
});
}
function switchPubTab(tabName) {
currentPubTab = tabName;
activeRatingFilter = 0;
activeStreamingFilter = '';
currentPage = 1;
// Reset le select
const select = document.getElementById('pub-streaming-select');
if (select) select.value = '';
const ratingBar = document.getElementById('rating-filter-bar');
if(ratingBar) ratingBar.style.display = (tabName === 'critique') ? 'flex' : 'none';
// Cacher le select sur l'onglet vidéothèque
if (select) select.style.display = (tabName === 'critique') ? 'block' : 'none';
document.querySelectorAll('.rating-filter-btn').forEach(btn => {
btn.classList.remove('active');
btn.querySelectorAll('.rf-star').forEach(s => s.classList.remove('filled'));
});
document.querySelectorAll('.tab-btn').forEach(btn => btn.classList.remove('active'));
const activeBtn = document.getElementById(`tab-pub-${tabName}s`);
if(activeBtn) activeBtn.classList.add('active');
renderPublicGrid();
}
function filterByRating(stars) {
if (currentPubTab !== 'critique') return;
activeRatingFilter = (activeRatingFilter === stars) ? 0 : stars;
document.querySelectorAll('.rating-filter-btn').forEach(btn => {
const n = parseInt(btn.dataset.stars);
btn.classList.toggle('active', n === activeRatingFilter);
btn.querySelectorAll('.rf-star').forEach((s, i) => {
s.classList.toggle('filled', i < activeRatingFilter);
});
});
renderPublicGrid();
}
function renderPublicGrid() {
const grid = document.getElementById('grid');
const emptyState = document.getElementById('empty-state');
const countLabel = document.getElementById('count-label');
if (!grid) return;
grid.innerHTML = '';
// 1. Filtrage par type
let filtered = films.filter(f => f.type === currentPubTab);
// 2. Filtre par note
if (currentPubTab === 'critique' && activeRatingFilter > 0) {
filtered = filtered.filter(f => Math.round(parseFloat(f.rating)) === activeRatingFilter);
}
// 3. Filtre par plateforme (avec normalisation)
if (currentPubTab === 'critique' && activeStreamingFilter) {
const filterLower = activeStreamingFilter.toLowerCase();
filtered = filtered.filter(f => {
if (!f.streaming || f.streaming === 'Disponible en support physique ou Cinéma') return false;
// On vérifie si la plateforme sélectionnée est présente dans le champ streaming
const platforms = f.streaming.split(',').map(p => p.trim().toLowerCase());
return platforms.includes(filterLower);
});
}
// 4. Recherche textuelle
if (searchQuery) {
const q = searchQuery.toLowerCase();
filtered = filtered.filter(f =>
f.title.toLowerCase().includes(q) ||
(f.director && f.director.toLowerCase().includes(q))
);
}
if (countLabel) {
countLabel.textContent = `${filtered.length} ${currentPubTab === 'critique' ? 'critique' : 'œuvre'}${filtered.length > 1 ? 's' : ''}`;
}
if (filtered.length === 0) {
if (emptyState) emptyState.style.display = 'block';
renderPagination(0);
return;
}
if (emptyState) emptyState.style.display = 'none';
// 5. Pagination
const totalPages = Math.ceil(filtered.length / itemsPerPage) || 1;
if (currentPage > totalPages) currentPage = totalPages;
const startIdx = (currentPage - 1) * itemsPerPage;
const pageItems = filtered.slice(startIdx, startIdx + itemsPerPage);
// 6. Rendu
pageItems.forEach(f => {
const card = document.createElement('div');
card.className = 'card';
card.onclick = () => openDetail(f.id);
let posterHTML = f.poster
? `<div class="card-poster-wrap"><img class="card-poster" src="${f.poster}" alt="${f.title}" loading="lazy"></div>`
: `<div class="card-poster-wrap"><div class="card-poster-placeholder"><i class="ti ti-movie"></i><span>Pas d'affiche</span></div></div>`;
let streamingBadge = '';
if (f.type === 'critique') {
if (f.streaming && f.streaming !== 'Disponible en support physique ou Cinéma') {
streamingBadge = `<div class="card-streaming-badge" title="${f.streaming}">📺 ${f.streaming.split(',')[0]}</div>`;
} else {
streamingBadge = `<div class="card-physical-badge"> Physique / Cinéma</div>`;
}
}
if (f.type === 'critique') {
const rating = parseFloat(f.rating) || 0;
const fullStars = Math.floor(rating);
const emptyStars = 5 - Math.ceil(rating);
const starsHTML = '★'.repeat(fullStars) + `<span class="stars-muted">${'☆'.repeat(emptyStars)}</span>`;
const ratingNum = `<span class="card-rating-badge">${rating.toFixed(1)}</span>`;
card.innerHTML = `
${posterHTML}
${streamingBadge}
<div class="card-body">
<h3 class="card-title" title="${f.title}">${f.title}</h3>
<p class="card-director">${f.director || 'Réalisateur inconnu'}</p>
<p class="card-meta">${f.year || '-'}</p>
<div class="card-stars">${starsHTML}${ratingNum}</div>
</div>`;
} else {
card.innerHTML = `
${posterHTML}
<div class="card-body">
<h3 class="card-title" title="${f.title}">${f.title}</h3>
<p class="card-director">${f.director || 'Réalisateur inconnu'}</p>
<p class="card-meta">${f.year || '-'}</p>
<div class="card-video-footer">
<span class="badge-format">${f.format || 'Physique'}</span>
${f.length ? `<span class="video-length"><i class="ti ti-clock"></i> ${f.length} min</span>` : ''}
</div>
</div>`;
}
grid.appendChild(card);
});
renderPagination(totalPages);
}
function renderPagination(totalPages) {
const container = document.getElementById('pub-pagination');
if (!container) return;
container.innerHTML = '';
if (totalPages <= 1) return;
const info = document.createElement('span');
info.className = 'pub-pagination-info';
info.textContent = `Page ${currentPage} / ${totalPages}`;
container.appendChild(info);
const prevBtn = document.createElement('button');
prevBtn.innerHTML = '<i class="ti ti-chevron-left"></i>';
prevBtn.disabled = currentPage === 1;
prevBtn.onclick = () => { currentPage--; renderPublicGrid(); window.scrollTo({ top: 0, behavior: 'smooth' }); };
container.appendChild(prevBtn);
const maxButtons = 5;
let startPage = Math.max(1, currentPage - Math.floor(maxButtons / 2));
let endPage = Math.min(totalPages, startPage + maxButtons - 1);
if (endPage - startPage + 1 < maxButtons) {
startPage = Math.max(1, endPage - maxButtons + 1);
}
if (startPage > 1) {
container.appendChild(createPubPageBtn(1));
if (startPage > 2) container.appendChild(createPubEllipsis());
}
for (let i = startPage; i <= endPage; i++) {
container.appendChild(createPubPageBtn(i));
}
if (endPage < totalPages) {
if (endPage < totalPages - 1) container.appendChild(createPubEllipsis());
container.appendChild(createPubPageBtn(totalPages));
}
const nextBtn = document.createElement('button');
nextBtn.innerHTML = '<i class="ti ti-chevron-right"></i>';
nextBtn.disabled = currentPage === totalPages;
nextBtn.onclick = () => { currentPage++; renderPublicGrid(); window.scrollTo({ top: 0, behavior: 'smooth' }); };
container.appendChild(nextBtn);
}
function createPubPageBtn(num) {
const btn = document.createElement('button');
btn.textContent = num;
if (num === currentPage) btn.classList.add('active');
btn.onclick = () => { currentPage = num; renderPublicGrid(); window.scrollTo({ top: 0, behavior: 'smooth' }); };
return btn;
}
function createPubEllipsis() {
const span = document.createElement('span');
span.textContent = '...';
span.style.color = 'var(--muted)';
span.style.padding = '0 0.5rem';
return span;
}
function openDetail(id) {
const f = films.find(item => item.id == id);
if (!f) return;
const dPoster = document.getElementById('d-poster');
const dPosterWrap = document.getElementById('d-poster-wrap');
const dTitle = document.getElementById('d-title');
const dMeta = document.getElementById('d-meta');
const dBody = document.getElementById('d-body');
const detailModalLayout = document.getElementById('detail-modal-layout');
const detailOverlay = document.getElementById('detail-overlay');
if (f.poster) {
if (dPoster) dPoster.src = f.poster;
if (dPosterWrap) {
dPosterWrap.style.display = 'block';
dPosterWrap.className = `detail-poster poster-${f.type}`;
}
if (detailModalLayout) detailModalLayout.classList.remove('no-poster');
} else {
if (dPosterWrap) dPosterWrap.style.display = 'none';
if (detailModalLayout) detailModalLayout.classList.add('no-poster');
}
if (dTitle) dTitle.textContent = f.title;
if (dMeta) dMeta.textContent = `${f.year ? f.year + ' | ' : ''}${f.director || 'Réalisateur inconnu'}`;
if (dBody) {
if (f.type === 'critique') {
const rating = parseFloat(f.rating) || 0;
const fullStars = Math.floor(rating);
const emptyStars = 5 - Math.ceil(rating);
const stars = '★'.repeat(fullStars) + `<span class="stars-muted">${'☆'.repeat(emptyStars)}</span>`;
dBody.innerHTML = `
<div class="detail-stars">${stars} <span style="font-size:1rem; color:var(--muted); margin-left:0.5rem;">${rating.toFixed(1)}/5</span></div>
<div class="pub-review-card">
<div class="pub-review-quote-icon"><i class="ti ti-quote"></i></div>
<p class="pub-review-text">${f.review ? f.review : 'Aucun texte rédigé pour le moment.'}</p>
</div>
${f.streaming ? `<div class="tech-pill" style="margin-top:1.5rem; width:fit-content;"><i class="ti ti-device-tv"></i> Visionnage : ${f.streaming}</div>` : ''}
`;
} else {
dBody.innerHTML = `
<div class="pub-tech-badges">
${f.format ? `<span class="tech-pill format-gold"><i class="ti ti-disc"></i> ${f.format}</span>` : ''}
${f.length ? `<span class="tech-pill"><i class="ti ti-clock"></i> ${f.length} Min</span>` : ''}
${f.number_of_discs ? `<span class="tech-pill"><i class="ti ti-layers-intersect"></i> ${f.number_of_discs} Disque(s)</span>` : ''}
</div>
<div class="pub-tech-grid">
<div class="tech-meta-item">
<span class="tech-meta-label">Éditeur</span>
<span class="tech-meta-value">${f.publisher || '&mdash;'}</span>
</div>
<div class="tech-meta-item">
<span class="tech-meta-label">Format Image</span>
<span class="tech-meta-value">${f.aspect_ratio || '&mdash;'}</span>
</div>
<div class="tech-meta-item" style="grid-column: span 2;">
<span class="tech-meta-label">Code Barre (EAN)</span>
<span class="tech-meta-value">${f.ean_isbn13 || '&mdash;'}</span>
</div>
</div>
<div class="pub-synopsis-box">
<h4>Synopsis / Notes</h4>
<p>${f.description ? f.description : 'Aucune description fournie.'}</p>
</div>
`;
}
}
if (detailOverlay) detailOverlay.classList.add('open');
}
function closeDetail() {
const detailOverlay = document.getElementById('detail-overlay');
if (detailOverlay) detailOverlay.classList.remove('open');
}
document.addEventListener('DOMContentLoaded', () => {
loadPublicData();
const searchInput = document.getElementById('pub-search-input');
if (searchInput) {
searchInput.addEventListener('input', (e) => {
searchQuery = e.target.value;
currentPage = 1;
renderPublicGrid();
});
}
});