window.onload = async function () { const token = localStorage.getItem('auth_token'); if (!token) { window.location.href = '/'; return; } let decoded; try { decoded = JSON.parse(atob(token.split('.')[1])); } catch (e) { localStorage.removeItem('auth_token'); window.location.href = '/'; return; } const currentTime = Math.floor(Date.now() / 1000); if (decoded.exp && decoded.exp < currentTime) { localStorage.removeItem('auth_token'); window.location.href = '/'; return; } if (decoded.mustChangePassword) { window.location.href = '/change-password.html'; return; } document.getElementById('user-name').textContent = decoded.name || ''; // Admin-only page: if token doesn't contain admin flag, bounce. if (!decoded.admin) { window.location.href = '/movies.html'; return; } const responseEl = document.getElementById('response'); const form = document.getElementById('add-movie-form'); const yearInput = document.getElementById('oscar_year'); const activeYearInput = document.getElementById('active_oscar_year'); const saveActiveYearBtn = document.getElementById('save-active-year'); // Modal 100% elements const completionModalTitleEl = document.getElementById('completion_modal_title'); const completionModalBodyTextEl = document.getElementById('completion_modal_body_text'); const completionModalBodyHtmlEl = document.getElementById('completion_modal_body_html'); const completionModalVideoSrcEl = document.getElementById('completion_modal_video_src'); const completionModalReloadBtn = document.getElementById('completion_modal_reload'); const completionModalSaveBtn = document.getElementById('completion_modal_save'); const completionModalPreviewTitleEl = document.getElementById('completion_modal_preview_title'); const completionModalPreviewTextEl = document.getElementById('completion_modal_preview_text'); const completionModalPreviewCustomEl = document.getElementById('completion_modal_preview_custom'); const completionModalPreviewVideoEl = document.getElementById('completion_modal_preview_video'); const completionModalPreviewVideoSourceEl = document.getElementById('completion_modal_preview_video_source'); const manageYearSelect = document.getElementById('manage_year'); const adminMoviesBody = document.getElementById('admin-movies-body'); const refreshMoviesBtn = document.getElementById('refresh-movies'); const deleteSelectedBtn = document.getElementById('delete-selected'); const selectAllBox = document.getElementById('select-all-movies'); const selectedCountEl = document.getElementById('selected-count'); const editMovieModalEl = document.getElementById('editMovieModal'); const editMovieModal = editMovieModalEl ? new bootstrap.Modal(editMovieModalEl) : null; const editMovieModalTitleEl = document.getElementById('editMovieModalTitle'); const saveMovieChangesBtn = document.getElementById('save-movie-changes'); const editMovieIdEl = document.getElementById('edit_movie_id'); const editImdbIdEl = document.getElementById('edit_imdb_id'); const editYearEl = document.getElementById('edit_year'); const editCategoryEl = document.getElementById('edit_category'); const editVodLinkEl = document.getElementById('edit_vod_link'); const editPlayerModeEl = document.getElementById('edit_player_mode'); const editVideoSrcEl = document.getElementById('edit_video_src'); const editEmbedSrcEl = document.getElementById('edit_embed_src'); const editRefreshOmdbEl = document.getElementById('edit_refresh_omdb'); const editTitleEl = document.getElementById('edit_title'); const editRatingEl = document.getElementById('edit_rating'); const editPosterEl = document.getElementById('edit_poster'); const editDescriptionEl = document.getElementById('edit_description'); let moviesById = new Map(); let activeYear = null; const DEFAULT_COMPLETION_MODAL = Object.freeze({ title: 'Félicitations! very nice 🎉🎉🎉', bodyText: '', videoSrc: 'video/reward.mp4', bodyHtml: '', }); function showResponse(kind, message) { responseEl.classList.remove('d-none', 'alert-success', 'alert-danger', 'alert-warning'); responseEl.classList.add(kind === 'success' ? 'alert-success' : kind === 'warning' ? 'alert-warning' : 'alert-danger'); responseEl.textContent = message; } function sanitizeHtml(raw) { const html = String(raw || ''); const withoutScripts = html.replace(//gi, ''); const withoutOnAttrs = withoutScripts .replace(/\son\w+="[^"]*"/gi, '') .replace(/\son\w+='[^']*'/gi, '') .replace(/\son\w+=\S+/gi, ''); return withoutOnAttrs.trim(); } function normalizeCompletionModal(value) { const v = value && typeof value === 'object' && !Array.isArray(value) ? value : {}; const title = typeof v.title === 'string' ? v.title : DEFAULT_COMPLETION_MODAL.title; const bodyText = typeof v.bodyText === 'string' ? v.bodyText : DEFAULT_COMPLETION_MODAL.bodyText; const videoSrc = typeof v.videoSrc === 'string' ? v.videoSrc : DEFAULT_COMPLETION_MODAL.videoSrc; const bodyHtml = typeof v.bodyHtml === 'string' ? v.bodyHtml : DEFAULT_COMPLETION_MODAL.bodyHtml; return { title: String(title || '').trim().slice(0, 200) || DEFAULT_COMPLETION_MODAL.title, bodyText: String(bodyText || '').slice(0, 8000), videoSrc: String(videoSrc || '').trim().slice(0, 2048), bodyHtml: String(bodyHtml || '').slice(0, 20000), }; } async function fetchCompletionModal() { const res = await fetch('/api/settings/completion-modal', { method: 'GET' }); if (!res.ok) throw new Error(`Erreur (${res.status})`); const data = await res.json().catch(() => ({})); return normalizeCompletionModal(data); } async function saveCompletionModal(payload) { const res = await fetch('/api/settings/completion-modal', { method: 'PUT', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` }, body: JSON.stringify(payload) }); const data = await res.json().catch(() => ({})); if (!res.ok) { const msg = data.message || data.error || `Erreur (${res.status})`; throw new Error(msg); } return normalizeCompletionModal(data); } function setCompletionModalForm(value) { if (!completionModalTitleEl || !completionModalBodyTextEl || !completionModalBodyHtmlEl || !completionModalVideoSrcEl) return; completionModalTitleEl.value = value.title || ''; completionModalBodyTextEl.value = value.bodyText || ''; completionModalBodyHtmlEl.value = value.bodyHtml || ''; completionModalVideoSrcEl.value = value.videoSrc || ''; } function getCompletionModalFormValue() { return normalizeCompletionModal({ title: completionModalTitleEl ? completionModalTitleEl.value : '', bodyText: completionModalBodyTextEl ? completionModalBodyTextEl.value : '', bodyHtml: completionModalBodyHtmlEl ? completionModalBodyHtmlEl.value : '', videoSrc: completionModalVideoSrcEl ? completionModalVideoSrcEl.value : '', }); } function updateCompletionModalPreview(value) { if (completionModalPreviewTitleEl) { completionModalPreviewTitleEl.textContent = value.title || DEFAULT_COMPLETION_MODAL.title; } const text = String(value.bodyText || ''); if (completionModalPreviewTextEl) { completionModalPreviewTextEl.textContent = text; completionModalPreviewTextEl.classList.toggle('d-none', !text); } const safeHtml = sanitizeHtml(value.bodyHtml || ''); if (completionModalPreviewCustomEl) { if (safeHtml) { completionModalPreviewCustomEl.innerHTML = safeHtml; completionModalPreviewCustomEl.classList.remove('d-none'); } else { completionModalPreviewCustomEl.innerHTML = ''; completionModalPreviewCustomEl.classList.add('d-none'); } } const videoSrc = String(value.videoSrc || '').trim(); if (completionModalPreviewVideoEl && completionModalPreviewVideoSourceEl) { if (videoSrc) { completionModalPreviewVideoSourceEl.setAttribute('src', videoSrc); try { completionModalPreviewVideoEl.load(); } catch (_) {} completionModalPreviewVideoEl.classList.remove('d-none'); } else { try { completionModalPreviewVideoEl.pause(); } catch (_) {} completionModalPreviewVideoEl.classList.add('d-none'); } } } async function loadCompletionModalIntoUi() { // If the tab isn't in DOM (older HTML), just skip gracefully. if (!completionModalTitleEl) return; try { const value = await fetchCompletionModal(); setCompletionModalForm(value); updateCompletionModalPreview(getCompletionModalFormValue()); } catch (err) { // Still show defaults so admin can edit even if network hiccups. setCompletionModalForm(DEFAULT_COMPLETION_MODAL); updateCompletionModalPreview(getCompletionModalFormValue()); showResponse('warning', err.message || 'Impossible de charger le contenu du modal.'); } } const userResetResponseEl = document.getElementById('user-reset-response'); const userResetForm = document.getElementById('reset-user-password-form'); const userResetEmailEl = document.getElementById('reset_user_email'); // Admin users list elements const adminUsersBody = document.getElementById('admin-users-body'); const refreshUsersBtn = document.getElementById('refresh-users'); const adminUsersCountEl = document.getElementById('admin-users-count'); const adminUsersTabBtn = document.getElementById('admin-users-tab'); let usersLoadedOnce = false; function showUserResetResponse(kind, message) { if (!userResetResponseEl) return; userResetResponseEl.classList.remove('d-none', 'alert-success', 'alert-danger', 'alert-warning'); userResetResponseEl.classList.add(kind === 'success' ? 'alert-success' : kind === 'warning' ? 'alert-warning' : 'alert-danger'); userResetResponseEl.textContent = message; } function setUsersCount(count) { if (!adminUsersCountEl) return; adminUsersCountEl.textContent = Number.isInteger(count) ? String(count) : '—'; } function renderAdminUsers(users) { if (!adminUsersBody) return; const list = Array.isArray(users) ? users : []; setUsersCount(list.length); if (!list.length) { adminUsersBody.innerHTML = 'Aucun utilisateur.'; return; } adminUsersBody.innerHTML = ''; list.forEach((u) => { const tr = document.createElement('tr'); const tdName = document.createElement('td'); tdName.textContent = u?.name || '(sans nom)'; const tdEmail = document.createElement('td'); tdEmail.textContent = u?.email || ''; const tdStatus = document.createElement('td'); tdStatus.className = 'text-nowrap'; const badges = []; if (u?.admin) badges.push({ text: 'Admin', cls: 'bg-warning text-dark' }); if (u?.mustChangePassword) badges.push({ text: 'Reset MDP', cls: 'bg-danger' }); if (!badges.length) { const span = document.createElement('span'); span.className = 'badge bg-secondary'; span.textContent = 'OK'; tdStatus.appendChild(span); } else { badges.forEach((b, idx) => { const span = document.createElement('span'); span.className = `badge ${b.cls}`; span.textContent = b.text; tdStatus.appendChild(span); if (idx < badges.length - 1) { tdStatus.appendChild(document.createTextNode(' ')); } }); } tr.appendChild(tdName); tr.appendChild(tdEmail); tr.appendChild(tdStatus); adminUsersBody.appendChild(tr); }); } async function fetchAdminUsers() { const res = await fetch('/api/users/admin/list', { method: 'GET', headers: { 'Authorization': `Bearer ${token}`, } }); const data = await res.json().catch(() => ([])); if (!res.ok) { const msg = data?.message || data?.error || `Erreur (${res.status})`; throw new Error(msg); } return Array.isArray(data) ? data : []; } async function loadAdminUsers(options = {}) { const force = !!options.force; if (!adminUsersBody) return; if (usersLoadedOnce && !force) return; adminUsersBody.innerHTML = 'Chargement…'; setUsersCount(null); try { const users = await fetchAdminUsers(); usersLoadedOnce = true; users.sort((a, b) => (a?.name || '').localeCompare((b?.name || ''), 'fr', { sensitivity: 'base' })); renderAdminUsers(users); } catch (err) { adminUsersBody.innerHTML = `${err.message || 'Erreur réseau'}`; setUsersCount(null); } } function parseYear(value) { const n = Number(value); if (!Number.isInteger(n)) return null; if (n < 1900 || n > 3000) return null; return n; } async function fetchActiveYear() { try { const res = await fetch('/api/settings/year', { method: 'GET' }); if (!res.ok) throw new Error('Failed to fetch active year'); const data = await res.json(); const year = Number(data?.year); return Number.isInteger(year) ? year : null; } catch (_) { return null; } } async function setActiveYear(newYear) { const res = await fetch('/api/settings/year', { method: 'PUT', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` }, body: JSON.stringify({ year: newYear }) }); if (!res.ok) { let msg = `Erreur (${res.status})`; try { const data = await res.json(); msg = data.message || data.error || msg; } catch (_) {} throw new Error(msg); } const data = await res.json(); const year = Number(data?.year); return Number.isInteger(year) ? year : newYear; } function isValidImdbId(value) { return /^tt\d{5,}$/.test(String(value || '').trim()); } document.getElementById('reset-form').addEventListener('click', function () { form.reset(); responseEl.classList.add('d-none'); }); document.getElementById('log-off').addEventListener('click', function () { localStorage.removeItem('auth_token'); window.location.href = '/'; }); // Modal 100% live preview wiring function hookModal100Inputs() { if (!completionModalTitleEl) return; const inputs = [completionModalTitleEl, completionModalBodyTextEl, completionModalBodyHtmlEl, completionModalVideoSrcEl].filter(Boolean); inputs.forEach((el) => { el.addEventListener('input', () => { updateCompletionModalPreview(getCompletionModalFormValue()); }); }); if (completionModalReloadBtn) { completionModalReloadBtn.addEventListener('click', async () => { completionModalReloadBtn.disabled = true; const oldText = completionModalReloadBtn.textContent; completionModalReloadBtn.textContent = 'Chargement...'; try { await loadCompletionModalIntoUi(); showResponse('success', 'Contenu rechargé.'); } catch (_) { // loadCompletionModalIntoUi already handles messaging } finally { completionModalReloadBtn.disabled = false; completionModalReloadBtn.textContent = oldText; } }); } if (completionModalSaveBtn) { completionModalSaveBtn.addEventListener('click', async () => { completionModalSaveBtn.disabled = true; const oldText = completionModalSaveBtn.textContent; completionModalSaveBtn.textContent = 'Enregistrement...'; try { const value = getCompletionModalFormValue(); // send raw HTML; server sanitizes/caps it const saved = await saveCompletionModal(value); setCompletionModalForm(saved); updateCompletionModalPreview(getCompletionModalFormValue()); showResponse('success', 'Modal 100% enregistré.'); } catch (err) { showResponse('danger', err.message || 'Erreur réseau'); } finally { completionModalSaveBtn.disabled = false; completionModalSaveBtn.textContent = oldText; } }); } } if (userResetForm && userResetEmailEl) { userResetForm.addEventListener('submit', async function (e) { e.preventDefault(); const email = (userResetEmailEl.value || '').trim(); if (!email) { showUserResetResponse('warning', 'Email invalide.'); return; } try { showUserResetResponse('warning', 'Génération du mot de passe temporaire...'); const res = await fetch('/api/users/admin/reset-temp-password', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}`, }, body: JSON.stringify({ email }), }); const data = await res.json().catch(() => ({})); if (!res.ok) { showUserResetResponse('danger', data.message || data.error || `Erreur (${res.status})`); return; } const label = data?.user?.name ? `${data.user.name} (${data.user.email})` : email; const expiresAt = data?.expiresAt ? ` Expire: ${data.expiresAt}` : ''; showUserResetResponse('success', `Temp password pour ${label}: ${data.tempPassword}.${expiresAt}`); } catch (err) { showUserResetResponse('danger', err.message || 'Erreur réseau'); } }); } if (refreshUsersBtn) { refreshUsersBtn.addEventListener('click', async () => { refreshUsersBtn.disabled = true; const oldText = refreshUsersBtn.textContent; refreshUsersBtn.textContent = 'Chargement...'; try { await loadAdminUsers({ force: true }); } finally { refreshUsersBtn.disabled = false; refreshUsersBtn.textContent = oldText; } }); } if (adminUsersTabBtn) { // Only load when the tab is shown (and then cache), to keep initial load snappy. adminUsersTabBtn.addEventListener('shown.bs.tab', () => loadAdminUsers({ force: false })); } form.addEventListener('submit', async function (e) { e.preventDefault(); const year = parseYear(yearInput.value); const imdb_id = document.getElementById('imdb_id').value.trim(); const category = document.getElementById('category').value.trim(); const vod_link = document.getElementById('vod_link').value.trim(); const player_mode = (document.getElementById('player_mode')?.value || 'auto').trim(); const video_src = document.getElementById('video_src')?.value?.trim() || ''; const embed_src = document.getElementById('embed_src')?.value?.trim() || ''; if (!year) { showResponse('warning', 'Année invalide. Exemple attendu: 2026'); return; } if (!/^tt\d{5,}$/.test(imdb_id)) { showResponse('warning', 'IMDB ID invalide. Exemple attendu: tt1234567'); return; } if (!vod_link && !video_src && !embed_src) { showResponse('warning', 'Ajoute au moins une source (VOD / video_src / embed_src).'); return; } try { const res = await fetch('/api/movies/add', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` }, body: JSON.stringify({ year, imdb_id, category, vod_link, player_mode, video_src, embed_src }) }); if (res.ok) { showResponse('success', 'Film ajouté avec succès.'); form.reset(); await refreshYears(); await loadMoviesForManagement(); return; } let errMessage = `Erreur (${res.status})`; try { const data = await res.json(); errMessage = data.message || data.error || errMessage; } catch (_) { // ignore JSON parse errors } showResponse('danger', errMessage); } catch (err) { showResponse('danger', err.message || 'Erreur réseau'); } }); async function refreshYears() { try { const yearsRes = await fetch('/api/movies/years'); const years = yearsRes.ok ? await yearsRes.json() : []; // Rebuild manage dropdown from scratch (so removed years disappear) const previousSelection = manageYearSelect.value; manageYearSelect.innerHTML = ''; const allOpt = document.createElement('option'); allOpt.value = ''; allOpt.textContent = 'Toutes'; manageYearSelect.appendChild(allOpt); years.forEach((y) => { const opt = document.createElement('option'); opt.value = String(y); opt.textContent = String(y); manageYearSelect.appendChild(opt); }); const stillExists = years.map(String).includes(previousSelection); manageYearSelect.value = stillExists ? previousSelection : ''; // Prefer defaulting UI to active year (if selection is empty/invalid). const hasSelection = !!manageYearSelect.value; if (!hasSelection && activeYear && years.map(String).includes(String(activeYear))) { manageYearSelect.value = String(activeYear); } // Default add-year to active year (or latest year) if empty if (!yearInput.value) { if (activeYear) { yearInput.value = String(activeYear); } else if (years.length > 0) { yearInput.value = String(years[0]); } } } catch (_) { // ignore } } function getSelectedMovieIds() { return Array.from(adminMoviesBody.querySelectorAll('input[type="checkbox"][data-movie-id]:checked')) .map((el) => el.getAttribute('data-movie-id')) .filter(Boolean); } function updateSelectionUI() { const selectedIds = getSelectedMovieIds(); selectedCountEl.textContent = String(selectedIds.length); deleteSelectedBtn.disabled = selectedIds.length === 0; const allBoxes = Array.from(adminMoviesBody.querySelectorAll('input[type="checkbox"][data-movie-id]')); const checked = allBoxes.filter((b) => b.checked).length; selectAllBox.indeterminate = checked > 0 && checked < allBoxes.length; selectAllBox.checked = allBoxes.length > 0 && checked === allBoxes.length; } function openEditModal(movieId) { if (!editMovieModal) return; const movie = moviesById.get(movieId); if (!movie) return; editMovieIdEl.value = movie._id || ''; editImdbIdEl.value = movie.imdb_id || ''; editYearEl.value = movie.year ? String(movie.year) : ''; editCategoryEl.value = movie.category || ''; editVodLinkEl.value = movie.vod_link || ''; if (editPlayerModeEl) editPlayerModeEl.value = movie.player_mode || 'auto'; if (editVideoSrcEl) editVideoSrcEl.value = movie.video_src || ''; if (editEmbedSrcEl) editEmbedSrcEl.value = movie.embed_src || ''; editRefreshOmdbEl.checked = false; editTitleEl.value = movie.title || ''; editRatingEl.value = movie.rating || ''; editPosterEl.value = movie.poster || ''; editDescriptionEl.value = movie.description || ''; if (editMovieModalTitleEl) { const label = movie.title ? `Modifier: ${movie.title}` : 'Modifier le film'; editMovieModalTitleEl.textContent = label; } editMovieModal.show(); } async function saveMovieChanges() { const movieId = (editMovieIdEl.value || '').trim(); if (!movieId) return; const imdb_id = (editImdbIdEl.value || '').trim(); const yearRaw = (editYearEl.value || '').trim(); const category = (editCategoryEl.value || '').trim(); const vod_link = (editVodLinkEl.value || '').trim(); const player_mode = (editPlayerModeEl?.value || 'auto').trim(); const video_src = (editVideoSrcEl?.value || '').trim(); const embed_src = (editEmbedSrcEl?.value || '').trim(); const refreshOmdb = !!editRefreshOmdbEl.checked; const title = editTitleEl.value; const rating = editRatingEl.value; const poster = editPosterEl.value; const description = editDescriptionEl.value; if (!isValidImdbId(imdb_id)) { showResponse('warning', 'IMDB ID invalide. Exemple attendu: tt1234567'); return; } let year = null; if (yearRaw !== '') { year = parseYear(yearRaw); if (!year) { showResponse('warning', 'Année invalide. Exemple attendu: 2026'); return; } } if (!category) { showResponse('warning', 'Catégorie invalide.'); return; } if (!vod_link && !video_src && !embed_src) { showResponse('warning', 'Ajoute au moins une source (VOD / video_src / embed_src).'); return; } const body = { imdb_id, year: yearRaw === '' ? null : year, category, vod_link, player_mode, video_src, embed_src, refreshOmdb }; // Only send manual fields if we're not refreshing from OMDb if (!refreshOmdb) { body.title = title; body.description = description; body.rating = rating; body.poster = poster; } try { saveMovieChangesBtn.disabled = true; saveMovieChangesBtn.textContent = 'Enregistrement...'; const res = await fetch(`/api/movies/${encodeURIComponent(movieId)}`, { method: 'PUT', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` }, body: JSON.stringify(body) }); if (!res.ok) { let msg = `Erreur (${res.status})`; try { const data = await res.json(); msg = data.message || data.error || msg; } catch (_) {} showResponse('danger', msg); return; } const updated = await res.json(); moviesById.set(updated._id, updated); showResponse('success', 'Film mis à jour avec succès.'); editMovieModal.hide(); await refreshYears(); await loadMoviesForManagement(); } catch (err) { showResponse('danger', err.message || 'Erreur réseau'); } finally { saveMovieChangesBtn.disabled = false; saveMovieChangesBtn.textContent = 'Enregistrer'; } } async function loadMoviesForManagement() { const year = manageYearSelect.value; const url = year ? `/api/movies?year=${encodeURIComponent(year)}` : '/api/movies'; adminMoviesBody.innerHTML = `Chargement…`; try { const res = await fetch(url, { method: 'GET' }); if (!res.ok) { adminMoviesBody.innerHTML = `Erreur lors du chargement (${res.status})`; return; } const movies = await res.json(); movies.sort((a, b) => (a.title || '').localeCompare((b.title || ''), 'fr', { sensitivity: 'base' })); moviesById = new Map(movies.map((m) => [m._id, m])); if (!movies.length) { adminMoviesBody.innerHTML = `Aucun film.`; updateSelectionUI(); return; } adminMoviesBody.innerHTML = ''; movies.forEach((m) => { const tr = document.createElement('tr'); tr.innerHTML = `
${m.title || '(sans titre)'}
${m.imdb_id || ''}
${m.category || ''} ${m.year || ''} `; adminMoviesBody.appendChild(tr); }); adminMoviesBody.querySelectorAll('input[type="checkbox"][data-movie-id]').forEach((cb) => { cb.addEventListener('change', updateSelectionUI); }); adminMoviesBody.querySelectorAll('button[data-edit-movie-id]').forEach((btn) => { btn.addEventListener('click', () => openEditModal(btn.getAttribute('data-edit-movie-id'))); }); updateSelectionUI(); } catch (err) { adminMoviesBody.innerHTML = `${err.message || 'Erreur réseau'}`; } } refreshMoviesBtn.addEventListener('click', loadMoviesForManagement); manageYearSelect.addEventListener('change', loadMoviesForManagement); selectAllBox.addEventListener('change', () => { const checked = selectAllBox.checked; adminMoviesBody.querySelectorAll('input[type="checkbox"][data-movie-id]').forEach((cb) => { cb.checked = checked; }); updateSelectionUI(); }); deleteSelectedBtn.addEventListener('click', async () => { const selectedIds = getSelectedMovieIds(); if (!selectedIds.length) return; const yearLabel = manageYearSelect.value ? ` (${manageYearSelect.value})` : ''; const ok = window.confirm(`Supprimer ${selectedIds.length} film(s)${yearLabel} ? Cette action est irréversible.`); if (!ok) return; try { const res = await fetch('/api/movies/delete', { method: 'DELETE', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` }, body: JSON.stringify({ ids: selectedIds }) }); if (!res.ok) { let msg = `Erreur (${res.status})`; try { const data = await res.json(); msg = data.message || data.error || msg; } catch (_) {} showResponse('danger', msg); return; } showResponse('success', 'Film(s) supprimé(s) avec succès.'); await refreshYears(); await loadMoviesForManagement(); } catch (err) { showResponse('danger', err.message || 'Erreur réseau'); } }); if (saveMovieChangesBtn) { saveMovieChangesBtn.addEventListener('click', saveMovieChanges); } // Initial load activeYear = await fetchActiveYear(); if (activeYearInput && activeYear) { activeYearInput.value = String(activeYear); document.title = `Pool Oscars ${activeYear} - Admin`; } hookModal100Inputs(); await loadCompletionModalIntoUi(); if (saveActiveYearBtn && activeYearInput) { saveActiveYearBtn.addEventListener('click', async () => { const y = parseYear(activeYearInput.value); if (!y) { showResponse('warning', 'Année invalide. Exemple attendu: 2026'); return; } saveActiveYearBtn.disabled = true; const oldText = saveActiveYearBtn.textContent; saveActiveYearBtn.textContent = 'Application...'; try { activeYear = await setActiveYear(y); activeYearInput.value = String(activeYear); yearInput.value = String(activeYear); document.title = `Pool Oscars ${activeYear} - Admin`; showResponse('success', `Année active appliquée: ${activeYear}`); await refreshYears(); await loadMoviesForManagement(); } catch (err) { showResponse('danger', err.message || 'Erreur réseau'); } finally { saveActiveYearBtn.disabled = false; saveActiveYearBtn.textContent = oldText; } }); } await refreshYears(); await loadMoviesForManagement(); };