diff --git a/js/admin.js b/js/admin.js index c3c87e0..c7e5f70 100644 --- a/js/admin.js +++ b/js/admin.js @@ -292,4 +292,257 @@ function createEllipsis() { return span; } -// ... [Les autres fonctions (Modales, Suppressions, CSV, etc.) restent identiques mais assurez-vous d'utiliser le code nettoyé ci-dessous pour éviter les erreurs de syntaxe] ... \ No newline at end of file +// ── POP-UP CONFIRMATION ─ +let pendingDeleteAction = null; +function showConfirmModal(actionFn) { + pendingDeleteAction = actionFn; + const modal = document.getElementById('confirm-modal'); + if (modal) modal.classList.add('open'); +} +function closeConfirmModal() { + const modal = document.getElementById('confirm-modal'); + if (modal) modal.classList.remove('open'); + pendingDeleteAction = null; +} + +// ─ SUPPRESSIONS ── +async function executeBulkDelete() { + const ids = Array.from(selectedIds); + if (ids.length === 0) return; + showConfirmModal(async () => { + try { + const res = await fetch(`${API_URL}?action=bulk_delete`, { + method: 'POST', + headers: { 'Authorization': localStorage.getItem('token'), 'Content-Type': 'application/json' }, + body: JSON.stringify({ ids, type: currentAdminTab }) + }); + if (!res.ok) throw new Error("Erreur serveur."); + selectedIds.clear(); + document.getElementById('bulk-actions-bar').style.display = 'none'; + const selectAll = document.getElementById('select-all-checkbox'); + if (selectAll) selectAll.checked = false; + loadDashboardData(); + } catch (err) { + console.error('Erreur bulk delete :', err); + alert("Une erreur est survenue."); + } + }); +} + +async function deleteSingleFilm(id) { + showConfirmModal(async () => { + try { + const res = await fetch(`${API_URL}?action=delete_film&id=${id}&type=${currentAdminTab}`, { + method: 'DELETE', + headers: { 'Authorization': localStorage.getItem('token') } + }); + if (!res.ok) throw new Error("Erreur serveur."); + loadDashboardData(); + } catch (err) { + console.error('Erreur delete :', err); + alert("Une erreur est survenue."); + } + }); +} + +// ── MODALES & UI ── +function toggleFormFields() { + const critFields = document.getElementById('form-critique-fields'); + const vidFields = document.getElementById('form-videotheque-fields'); + if (critFields) critFields.style.display = currentAdminTab === 'critique' ? 'block' : 'none'; + if (vidFields) vidFields.style.display = currentAdminTab === 'videotheque' ? 'block' : 'none'; +} + +function switchAdminTab(tabName) { + currentAdminTab = tabName; + currentPage = 1; + searchQuery = ''; + selectedIds.clear(); + const searchInput = document.getElementById('search-input'); + if (searchInput) searchInput.value = ''; + document.querySelectorAll('.tab-btn').forEach(b => b.classList.remove('active')); + const btn = document.getElementById(`btn-tab-${tabName}`); + if (btn) btn.classList.add('active'); + toggleFormFields(); + renderAdminTable(); +} + +function openAddModal() { + document.getElementById('film-form').reset(); + document.getElementById('f-id').value = ''; + toggleFormFields(); + document.getElementById('admin-modal').classList.add('open'); +} + +function openEditModal(id) { + const item = allItems.find(x => String(x.id) === String(id)); + if (!item) return; + document.getElementById('f-id').value = item.id; + document.getElementById('f-title').value = item.title; + document.getElementById('f-year').value = item.year || ''; + document.getElementById('f-director').value = item.director || ''; + document.getElementById('f-poster').value = item.poster || ''; + toggleFormFields(); + if (currentAdminTab === 'critique') { + document.getElementById('f-rating').value = item.rating || 3; + document.getElementById('f-review').value = item.review || ''; + document.getElementById('f-streaming').value = item.streaming || ''; + } else { + document.getElementById('f-format').value = item.format || ''; + document.getElementById('f-length').value = item.length || ''; + document.getElementById('f-publisher').value = item.publisher || ''; + document.getElementById('f-aspect').value = item.aspect_ratio || ''; + document.getElementById('f-ean').value = item.ean_isbn13 || ''; + document.getElementById('f-discs').value = item.number_of_discs || 1; + document.getElementById('f-description').value = item.description || ''; + } + document.getElementById('admin-modal').classList.add('open'); +} + +function closeAdminModal() { document.getElementById('admin-modal').classList.remove('open'); } +function openConfigModal() { document.getElementById('config-modal').classList.add('open'); } +function closeConfigModal() { document.getElementById('config-modal').classList.remove('open'); } +function openPasswordModal() { + document.getElementById('pwd-error').style.display = 'none'; + document.getElementById('password-modal').classList.add('open'); +} +function closePasswordModal() { document.getElementById('password-modal').classList.remove('open'); } +function logout() { + localStorage.removeItem('token'); + window.location.href = 'login.html'; +} + +// ── SAUVEGARDE FILM ── +async function saveFilmForm(e) { + e.preventDefault(); + const payload = { + type: currentAdminTab, + id: document.getElementById('f-id').value, + title: document.getElementById('f-title').value, + year: document.getElementById('f-year').value, + director: document.getElementById('f-director').value, + poster: document.getElementById('f-poster').value, + rating: document.getElementById('f-rating') ? document.getElementById('f-rating').value : '', + review: document.getElementById('f-review') ? document.getElementById('f-review').value : '', + streaming: document.getElementById('f-streaming') ? document.getElementById('f-streaming').value : '', + format: document.getElementById('f-format') ? document.getElementById('f-format').value : '', + length: document.getElementById('f-length') ? document.getElementById('f-length').value : '', + publisher: document.getElementById('f-publisher') ? document.getElementById('f-publisher').value : '', + aspect_ratio: document.getElementById('f-aspect') ? document.getElementById('f-aspect').value : '', + ean_isbn13: document.getElementById('f-ean') ? document.getElementById('f-ean').value : '', + number_of_discs: document.getElementById('f-discs') ? document.getElementById('f-discs').value : 1, + description: document.getElementById('f-description') ? document.getElementById('f-description').value : '' + }; + try { + await fetch(`${API_URL}?action=save_film`, { + method: 'POST', + headers: { 'Authorization': localStorage.getItem('token'), 'Content-Type': 'application/json' }, + body: JSON.stringify(payload) + }); + closeAdminModal(); + loadDashboardData(); + } catch (err) { console.error('Erreur sauvegarde :', err); } +} + +// ── IMPORT CSV PAR LOTS ── +async function handleCsvUpload(input) { + if (!input.files || input.files.length === 0) return; + const file = input.files[0]; + input.value = ''; + + try { + const text = await file.text(); + const allData = parseCSV(text); + if (allData.length === 0) { + alert('❌ Le fichier CSV est vide ou mal formaté.'); + return; + } + closeConfigModal(); + showProgressModal(allData.length); + + const batchSize = 5; + let processed = 0; + for (let i = 0; i < allData.length; i += batchSize) { + const batch = allData.slice(i, i + batchSize); + try { + await fetch(`${API_URL}?action=import_batch`, { + method: 'POST', + headers: { + 'Authorization': localStorage.getItem('token'), + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ items: batch, type: currentAdminTab }) + }); + } catch (err) { + console.error('Erreur sur un lot:', err); + } + processed += batch.length; + updateProgressModal(processed, allData.length); + } + closeProgressModal(); + alert(`✅ Import terminé ! ${allData.length} élément(s) traité(s).`); + loadDashboardData(); + } catch (err) { + console.error('Erreur lecture CSV :', err); + closeProgressModal(); + alert(' Impossible de lire le fichier CSV.'); + } +} + +// ── SAUVEGARDE CLÉ TMDB ── +async function saveTmdbKey() { + const input = document.getElementById('tmdb-key-input'); + if (input && input.value) { + try { + const res = await fetch(`${API_URL}?action=save_config`, { + method: 'POST', + headers: { 'Authorization': localStorage.getItem('token'), 'Content-Type': 'application/json' }, + body: JSON.stringify({ key_name: 'tmdb_api_key', key_value: input.value }) + }); + const data = await res.json(); + if (data.success) { + alert('✅ Clé API sauvegardée et chiffrée en base de données !'); + closeConfigModal(); + } else { + alert('❌ Erreur : ' + (data.error || 'Impossible de sauvegarder.')); + } + } catch (err) { + console.error('Erreur sauvegarde clé :', err); + alert('Erreur de communication avec le serveur.'); + } + } +} + +// ── SAUVEGARDE MOT DE PASSE ── +async function saveNewPassword() { + const pwdInput = document.getElementById('new-password-input'); + const pwdConfirm = document.getElementById('new-password-confirm'); + const errorMsg = document.getElementById('pwd-error'); + if (!pwdInput || !pwdConfirm) return; + if (pwdInput.value !== pwdConfirm.value) { + errorMsg.textContent = "Les mots de passe ne correspondent pas."; + errorMsg.style.display = "block"; + return; + } + if (pwdInput.value.length < 4) { + errorMsg.textContent = "Le mot de passe doit contenir au moins 4 caractères."; + errorMsg.style.display = "block"; + return; + } + try { + const response = await fetch(`${API_URL}?action=update_password`, { + method: 'POST', + headers: { 'Authorization': localStorage.getItem('token'), 'Content-Type': 'application/json' }, + body: JSON.stringify({ new_password: pwdInput.value }) + }); + const data = await response.json(); + if (data.success) { + pwdInput.value = ''; + pwdConfirm.value = ''; + errorMsg.style.display = "none"; + closePasswordModal(); + alert('Mot de passe mis à jour.'); + loadDashboardData(); + } + } catch (err) { console.error('Erreur mise à jour mot de passe :', err); } +} \ No newline at end of file