From 42ad65a7813d72eec6600839f9a522a4a92ba514 Mon Sep 17 00:00:00 2001 From: Cedric Date: Thu, 18 Jun 2026 10:41:22 +0200 Subject: [PATCH] Actualiser js/admin.js --- js/admin.js | 735 +++++++--------------------------------------------- 1 file changed, 96 insertions(+), 639 deletions(-) diff --git a/js/admin.js b/js/admin.js index 6047b05..839162d 100644 --- a/js/admin.js +++ b/js/admin.js @@ -1,674 +1,131 @@ -<<<<<<< HEAD /** * ========================================================================= * Mon Cinéma - Module d'Administration (admin.js) - * Gestion de l'état, de la persistance, des onglets et des imports/exports CSV * ========================================================================= */ -// ── PARSEUR CSV UNIVERSEL (Gestion des guillemets et séparateurs) ── -function parseFlexibleCSV(text) { - const rows = []; - let row = [""]; - let inQuotes = false; - - // Détection dynamique du séparateur principal (, ou ;) basé sur la première ligne - const firstLine = text.split(/\r?\n/)[0] || ""; - const semiColonCount = (firstLine.match(/;/g) || []).length; - const commaCount = (firstLine.match(/,/g) || []).length; - const separator = semiColonCount > commaCount ? ';' : ','; - - for (let i = 0; i < text.length; i++) { - let c = text[i]; - let next = text[i + 1]; - - if (c === '"') { - if (inQuotes && next === '"') { - row[row.length - 1] += '"'; - i++; - } else { - inQuotes = !inQuotes; - } - } else if (c === separator && !inQuotes) { - row.push(''); - } else if ((c === '\r' || c === '\n') && !inQuotes) { - if (c === '\r' && next === '\n') { i++; } - rows.push(row); - row = ['']; - } else { - row[row.length - 1] += c; - } - } - if (row.length > 1 || row[0] !== '') rows.push(row); - return rows; -} - -// ── FONCTION D'IMPORTATION SÉCURISÉE (Latin-1 / Multi-formats) ── -function importFromCSV(input) { - const file = input.files[0]; - if (!file) return; - - const reader = new FileReader(); - reader.onload = async function (e) { - const text = e.target.result; - const rows = parseFlexibleCSV(text); - - if (rows.length <= 1) { - alert("Le fichier semble vide ou illisible."); - input.value = ''; - return; - } - - // Nettoyage et normalisation des en-têtes (ignore les accents et la casse) - const cleanHeader = (h) => h.trim().toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, ""); - const headers = rows[0].map(cleanHeader); - - // Mappage des colonnes (gère 'importcinetest.csv' et votre catalogue global) - const idxTitle = headers.findIndex(h => h === 'titre' || h === 'title' || h === 'name'); - const idxYear = headers.findIndex(h => h === 'annee' || h === 'year' || h === 'publish_date'); - const idxDirector = headers.findIndex(h => h === 'realisateur' || h === 'director' || h === 'creators'); - const idxRating = headers.findIndex(h => h === 'note' || h === 'rating'); - const idxReview = headers.findIndex(h => h === 'critique' || h === 'review' || h === 'description'); - const idxPoster = headers.findIndex(h => h === 'url_affiche' || h === 'url'); - - if (idxTitle === -1) { - alert("Erreur : Impossible de trouver la colonne des titres."); - input.value = ''; - return; - } - - const toImport = []; - let duplicateCount = 0; - - for (let i = 1; i < rows.length; i++) { - const cols = rows[i]; - if (!cols || cols.length === 0 || (cols.length === 1 && cols[0] === '')) continue; - - const titleRaw = cols[idxTitle]?.trim(); - if (!titleRaw) continue; - - // Nettoyage des suffixes d'éditions - let title = titleRaw.replace(/\s*\[.*?\]/g, "").replace(/\s*- Édition.*/gi, ""); - - // Extraction propre de l'année (4 chiffres) - let year = ''; - if (idxYear >= 0 && cols[idxYear]) { - const match = cols[idxYear].trim().match(/\d{4}/); - year = match ? match[0] : cols[idxYear].trim().substring(0, 4); - } - - // Réalisateur - let director = ''; - if (idxDirector >= 0 && cols[idxDirector]) { - director = cols[idxDirector].split(',')[0].trim(); - } - - // Note (Normalisée entre 1 et 5) - let rating = 0; - if (idxRating >= 0 && cols[idxRating]) { - const ratingRaw = parseFloat(cols[idxRating]); - if (!isNaN(ratingRaw) && ratingRaw > 0) { - rating = Math.round(Math.min(5, Math.max(1, ratingRaw))); - } - } - if (rating === 0) rating = 3; - - // Critique ou description de l'œuvre - let review = idxReview >= 0 && cols[idxReview] ? cols[idxReview].trim() : ''; - - // URL de l'affiche - let filePoster = idxPoster >= 0 && cols[idxPoster] ? cols[idxPoster].trim() : ''; - - // Déduplication (titre + année) - const alreadyExists = films.some( - f => f.title.toLowerCase() === title.toLowerCase() && (year === '' || f.year === year) - ); - - if (alreadyExists) { - duplicateCount++; - continue; - } - - toImport.push({ title, year, director, rating, review, filePoster }); - } - - if (toImport.length === 0) { - alert(`Aucun nouveau film inséré.\n⚠️ ${duplicateCount} doublon(s) filtré(s).`); - input.value = ''; - return; - } - - const hasTmdb = !!localStorage.getItem('tmdb-api-key'); - showImportProgress(toImport.length); - - let importedCount = 0; - - for (let i = 0; i < toImport.length; i++) { - const { title, year, director, rating, review, filePoster } = toImport[i]; - updateImportProgress(i, toImport.length, `🎬 Intégration : ${title}`); - - let poster = filePoster; - let streaming = "Disponible sur vos étagères ou au cinéma"; - - if (!poster && hasTmdb) { - const details = await fetchTmdbDetails(title, year); - poster = details.poster; - streaming = details.streaming; - await new Promise(r => setTimeout(r, 150)); - } else if (hasTmdb && poster) { - const details = await fetchTmdbDetails(title, year); - if (details.streaming) streaming = details.streaming; - } - - // Injection forcée en type 'critique' pour correspondre à votre vue principale - films.push({ - id: Date.now() + Math.floor(Math.random() * 1000) + i, - title, - type: 'critique', - year, - director, - rating, - review, - poster, - streaming, - format: '', length: '', publisher: '', ean_isbn13: '', number_of_discs: '1', aspect_ratio: '', description: '' - }); - importedCount++; - } - - updateImportProgress(toImport.length, toImport.length, '✅ Terminé !'); - await new Promise(r => setTimeout(r, 300)); - hideImportProgress(); - - films.sort((a, b) => b.id - a.id); - persist(); - switchTab('critique'); - - alert(`Succès ! ${importedCount} films ajoutés.\n(${duplicateCount} doublons ignorés)`); - input.value = ''; - }; - - // Utilisation obligatoire de ISO-8859-1 (Latin-1) pour préserver les accents sous Excel / Windows - reader.readAsText(file, 'ISO-8859-1'); -} - -// ── COMPOSANTS INTERFACES DE PROGRESSION ── -function showImportProgress(total) { - let overlay = document.getElementById('import-overlay'); - if (!overlay) { - overlay = document.createElement('div'); - overlay.id = 'import-overlay'; - overlay.innerHTML = ` -
-

Importation en cours...

-
-
-
-

Initialisation...

-
- `; - document.body.appendChild(overlay); - } - overlay.style.display = 'block'; -} - -function updateImportProgress(current, total, message) { - const fill = document.getElementById('import-progress-fill'); - const text = document.getElementById('import-progress-text'); - const percent = Math.round((current / total) * 100); - if (fill) fill.style.width = `${percent}%`; - if (text) text.innerText = `${message} (${percent}%)`; -} - -function hideImportProgress() { - const overlay = document.getElementById('import-overlay'); - if (overlay) overlay.style.display = 'none'; -} - -// ── FONCTION D'EXPORTATION DU CATALOGUE (CSV) ── -function exportToCSV() { - if (!films || films.length === 0) { - alert("Aucune donnée disponible pour l'exportation."); - return; - } - - const headers = ['ID', 'Titre', 'Annee', 'Realisateur', 'Note', 'Critique', 'URL_Affiche']; - const csvRows = [headers.join(';')]; - - films.forEach(f => { - const row = [ - f.id, - `"${(f.title || '').replace(/"/g, '""')}"`, - f.year || '', - `"${(f.director || '').replace(/"/g, '""')}"`, - f.rating || 0, - `"${(f.review || '').replace(/"/g, '""')}"`, - `"${(f.poster || '').replace(/"/g, '""')}"` - ]; - csvRows.push(row.join(';')); - }); - - const csvContent = "\uFEFF" + csvRows.join("\n"); - const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' }); - const url = URL.createObjectURL(blob); - - const link = document.createElement("a"); - link.setAttribute("href", url); - link.setAttribute("download", `mon_cinema_export_${new Date().toISOString().slice(0,10)}.csv`); - document.body.appendChild(link); - link.click(); - document.body.removeChild(link); -} - -// ── ENREGISTREMENT LOCAL STORAGE ── -function persist() { - localStorage.setItem('mon-cinema-films', JSON.stringify(films)); - if (typeof renderFilms === 'function') { - renderFilms(); - } -} - -// ── NAVIGATION ONGLETS ── -function switchTab(tabName) { - document.querySelectorAll('.tab-content').forEach(el => el.classList.remove('active')); - document.querySelectorAll('.tab-btn').forEach(el => el.classList.remove('active')); - - const targetTab = document.getElementById(`tab-${tabName}`); - const targetBtn = document.querySelector(`[onclick="switchTab('${tabName}')"]`); - - if (targetTab) targetTab.classList.add('active'); - if (targetBtn) targetBtn.classList.add('active'); - - localStorage.setItem('mon-cinema-active-tab', tabName); -} -======= -// ══════════════════════════════════════════════════════════════════ -// admin.js — Backoffice Mon Cinéma -// Chargé UNIQUEMENT par dashboard.html -// ══════════════════════════════════════════════════════════════════ - const API_URL = '../api.php'; let allItems = []; let currentAdminTab = 'critique'; -// ── Garde de session (dashboard uniquement) ────────────────────── +// ── 1. GARDE DE SESSION ── (function guardSession() { - if (!localStorage.getItem('token')) { - window.location.href = 'login.html'; - } + if (window.location.pathname.includes('dashboard.html') && !localStorage.getItem('token')) { + window.location.href = 'login.html'; + } })(); // ══════════════════════════════════════════════════════════════════ -// 1. CHARGEMENT & RENDU +// CHARGEMENT & RENDU (DASHBOARD) // ══════════════════════════════════════════════════════════════════ async function loadDashboardData() { - try { - const res = await fetch(`${API_URL}?action=get_films`); - allItems = await res.json(); + try { + const res = await fetch(`${API_URL}?action=get_films`); + allItems = await res.json(); + + const secRes = await fetch(`${API_URL}?action=check_security_status`); + const secData = await secRes.json(); + const banner = document.getElementById('security-banner'); + if (banner) banner.style.display = secData.is_blank ? 'flex' : 'none'; - // Vérifier si compte sécurisé - const secRes = await fetch(`${API_URL}?action=check_security_status`); - const secData = await secRes.json(); - const banner = document.getElementById('security-banner'); - if (banner) banner.style.display = secData.is_blank ? 'flex' : 'none'; - - renderAdminTable(); - } catch (err) { - console.error('Erreur chargement :', err); - } -} - -function switchAdminTab(tabName) { - currentAdminTab = tabName; - - document.getElementById('btn-tab-critique').classList.toggle('active', tabName === 'critique'); - document.getElementById('btn-tab-videotheque').classList.toggle('active', tabName === 'videotheque'); - - const importSection = document.getElementById('import-section'); - if (importSection) importSection.style.display = tabName === 'critique' ? 'block' : 'none'; - - document.getElementById('admin-subtitle').textContent = - tabName === 'critique' - ? 'Gestion de vos critiques de films' - : 'Gestion de votre stock physique de films'; - - document.getElementById('th-dynamic').textContent = tabName === 'critique' ? 'Note' : 'Format'; - - const selectAll = document.getElementById('select-all-checkbox'); - if (selectAll) selectAll.checked = false; - - renderAdminTable(); + renderAdminTable(); + } catch (err) { + console.error('Erreur chargement :', err); + } } function renderAdminTable() { - const tbody = document.getElementById('admin-table-body'); - const countLabel = document.getElementById('admin-count-label'); - if (!tbody) return; + const tbody = document.getElementById('admin-table-body'); + const countLabel = document.getElementById('admin-count-label'); + if (!tbody) return; - tbody.innerHTML = ''; - const filtered = allItems.filter(item => item.type === currentAdminTab); + tbody.innerHTML = ''; + const filtered = allItems.filter(item => item.type === currentAdminTab); - if (countLabel) { - countLabel.textContent = `${filtered.length} élément(s) dans cette catégorie`; - } + if (countLabel) countLabel.textContent = `${filtered.length} élément(s) dans cette catégorie`; - if (filtered.length === 0) { - tbody.innerHTML = ` - - Aucun film trouvé dans cette liste.`; - updateBulkBarVisibility(); - return; - } - - filtered.forEach(f => { - const tr = document.createElement('tr'); - - const imgHTML = f.poster - ? `${f.title}` - : `
`; - - const dynamicCell = currentAdminTab === 'critique' - ? `${'★'.repeat(f.rating)}${'☆'.repeat(5 - f.rating)}` - : `${f.format || 'Physique'}`; - - tr.innerHTML = ` - - - - ${imgHTML} - ${f.title} - ${f.year || '—'} - ${f.director || 'Inconnu'} - ${dynamicCell} - -
- - -
- `; - tbody.appendChild(tr); - }); - - updateBulkBarVisibility(); -} - -// ══════════════════════════════════════════════════════════════════ -// 2. SÉLECTION EN MASSE -// ══════════════════════════════════════════════════════════════════ -function toggleSelectAll(master) { - document.querySelectorAll('.film-checkbox').forEach(cb => cb.checked = master.checked); - updateBulkBarVisibility(); -} - -function updateBulkBarVisibility() { - const checked = document.querySelectorAll('.film-checkbox:checked'); - const bulkBar = document.getElementById('bulk-actions-bar'); - const bulkCount = document.getElementById('bulk-count'); - const selectAll = document.getElementById('select-all-checkbox'); - - if (!bulkBar || !bulkCount) return; - - if (checked.length > 0) { - bulkBar.style.display = 'flex'; - bulkCount.textContent = checked.length; - } else { - bulkBar.style.display = 'none'; - if (selectAll) selectAll.checked = false; - } -} - -async function executeBulkDelete() { - const checked = document.querySelectorAll('.film-checkbox:checked'); - const idsToDelete = Array.from(checked).map(cb => cb.value); - if (!idsToDelete.length) return; - - if (!confirm(`Supprimer définitivement ces ${idsToDelete.length} élément(s) ?`)) return; - - try { - const res = await fetch(`${API_URL}?action=delete_multiple_films&type=${currentAdminTab}`, { - method: 'DELETE', - headers: { 'Content-Type': 'application/json', 'Authorization': localStorage.getItem('token') }, - body: JSON.stringify({ ids: idsToDelete }) - }); - const data = await res.json(); - if (data.success) { - const selectAll = document.getElementById('select-all-checkbox'); - if (selectAll) selectAll.checked = false; - loadDashboardData(); - } else { - alert('Erreur suppression groupée : ' + data.error); + if (filtered.length === 0) { + tbody.innerHTML = `Aucun film trouvé.`; + return; } - } catch (err) { - console.error('Bulk delete :', err); - } + + filtered.forEach(f => { + const tr = document.createElement('tr'); + const imgHTML = f.poster ? `${f.title}` : `
`; + const dynamicCell = currentAdminTab === 'critique' + ? `${'★'.repeat(f.rating)}` + : `${f.format || 'Physique'}`; + + tr.innerHTML = ` + + ${imgHTML} + ${f.title} + ${f.year || ''} + ${f.director || ''} + ${dynamicCell} + + + + `; + tbody.appendChild(tr); + }); } // ══════════════════════════════════════════════════════════════════ -// 3. SUPPRESSION UNITAIRE +// IMPORT / EXPORT CSV +// ══════════════════════════════════════════════════════════════════ +function exportToCSV() { + if (!allItems.length) return alert("Aucune donnée."); + const headers = ['ID', 'Titre', 'Annee', 'Realisateur', 'Note', 'Critique', 'URL_Affiche']; + const csvRows = [headers.join(';')]; + allItems.forEach(f => { + csvRows.push([f.id, `"${f.title}"`, f.year, `"${f.director}"`, f.rating, `"${f.review}"`, `"${f.poster}"`].join(';')); + }); + const blob = new Blob(["\uFEFF" + csvRows.join("\n")], { type: 'text/csv;charset=utf-8;' }); + const link = document.createElement("a"); + link.href = URL.createObjectURL(blob); + link.download = "export_cinema.csv"; + link.click(); +} + +async function handleCsvUpload(input) { + if (!input.files[0]) return; + const formData = new FormData(); + formData.append('csv_file', input.files[0]); + try { + const res = await fetch(`${API_URL}?action=import_csv`, { + method: 'POST', + headers: { 'Authorization': localStorage.getItem('token') }, + body: formData + }); + const data = await res.json(); + if (data.success) { + alert(`Succès : ${data.imported} films ajoutés.`); + loadDashboardData(); + } + } catch (e) { console.error(e); } +} + +// ══════════════════════════════════════════════════════════════════ +// GESTION ACTIONS UNITAIRES // ══════════════════════════════════════════════════════════════════ async function deleteSingleFilm(id) { - if (!confirm('Supprimer définitivement cette œuvre ?')) return; - try { - const res = await fetch(`${API_URL}?action=delete_film&id=${id}&type=${currentAdminTab}`, { - method: 'DELETE', - headers: { 'Authorization': localStorage.getItem('token') } + if (!confirm('Supprimer cette œuvre ?')) return; + await fetch(`${API_URL}?action=delete_film&id=${id}&type=${currentAdminTab}`, { + method: 'DELETE', + headers: { 'Authorization': localStorage.getItem('token') } }); - const data = await res.json(); - if (data.success) loadDashboardData(); - } catch (err) { - console.error(err); - } + loadDashboardData(); } -// ══════════════════════════════════════════════════════════════════ -// 4. MODALES FILM (ouvrir / fermer / remplir) -// ══════════════════════════════════════════════════════════════════ -function openAddModal() { - document.getElementById('film-form').reset(); - document.getElementById('f-id').value = ''; - document.getElementById('modal-form-title').textContent = - currentAdminTab === 'critique' ? 'Rédiger une Critique' : 'Ajouter un film physique'; - - document.getElementById('form-critique-fields').style.display = currentAdminTab === 'critique' ? 'block' : 'none'; - document.getElementById('form-videotheque-fields').style.display = currentAdminTab === 'videotheque' ? 'block' : 'none'; - - document.getElementById('admin-modal').classList.add('open'); +function switchAdminTab(tabName) { + currentAdminTab = tabName; + renderAdminTable(); } -function openEditModal(id) { - const item = allItems.find(x => String(x.id) === String(id)); - if (!item) return; - - openAddModal(); - document.getElementById('modal-form-title').textContent = "Modifier l'œuvre"; - - 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 || ''; - - 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 || ''; - } -} - -function closeAdminModal() { - document.getElementById('admin-modal').classList.remove('open'); -} - -// ══════════════════════════════════════════════════════════════════ -// 5. SAUVEGARDE FORMULAIRE FILM -// ══════════════════════════════════════════════════════════════════ -async function saveFilmForm(e) { - e.preventDefault(); - - const payload = { - type: currentAdminTab, - id: document.getElementById('f-id').value || null, - title: document.getElementById('f-title').value, - year: document.getElementById('f-year').value, - director: document.getElementById('f-director').value, - poster: document.getElementById('f-poster').value - }; - - if (currentAdminTab === 'critique') { - payload.rating = parseInt(document.getElementById('f-rating').value); - payload.review = document.getElementById('f-review').value; - payload.streaming = document.getElementById('f-streaming').value; - } else { - payload.format = document.getElementById('f-format').value; - payload.length = document.getElementById('f-length').value; - payload.publisher = document.getElementById('f-publisher').value; - payload.aspect_ratio = document.getElementById('f-aspect').value; - payload.ean_isbn13 = document.getElementById('f-ean').value; - payload.number_of_discs = document.getElementById('f-discs').value; - payload.description = document.getElementById('f-description').value; - } - - try { - const res = await fetch(`${API_URL}?action=save_film`, { - method: 'POST', - headers: { 'Content-Type': 'application/json', 'Authorization': localStorage.getItem('token') }, - body: JSON.stringify(payload) - }); - const data = await res.json(); - if (data.success) { - closeAdminModal(); - loadDashboardData(); - } else { - alert('Erreur lors de l\'enregistrement.'); +// ── INITIALISATION ── +document.addEventListener('DOMContentLoaded', () => { + if (document.getElementById('admin-table-body')) { + loadDashboardData(); } - } catch (err) { - console.error('Erreur save :', err); - } -} - -// ══════════════════════════════════════════════════════════════════ -// 6. IMPORT CSV (Letterboxd) -// ══════════════════════════════════════════════════════════════════ -async function handleCsvUpload(input) { - if (!input.files || input.files.length === 0) return; - - const formData = new FormData(); - formData.append('csv_file', input.files[0]); - - try { - const res = await fetch(`${API_URL}?action=import_csv`, { - method: 'POST', - headers: { 'Authorization': localStorage.getItem('token') }, - body: formData - }); - const data = await res.json(); - - if (data.success) { - alert(`✅ Import réussi ! ${data.imported} films ajoutés ou mis à jour.`); - loadDashboardData(); - } else { - alert('❌ Erreur import : ' + (data.error || 'Erreur inconnue')); - } - } catch (err) { - console.error('Erreur Import CSV :', err); - alert('Impossible d\'importer le fichier.'); - } finally { - input.value = ''; - } -} - -// ══════════════════════════════════════════════════════════════════ -// 7. MODALE TMDB -// ══════════════════════════════════════════════════════════════════ -function openConfigModal() { document.getElementById('config-modal').classList.add('open'); } -function closeConfigModal() { document.getElementById('config-modal').classList.remove('open'); } - -async function saveTmdbKey() { - const key = document.getElementById('tmdb-key-input').value.trim(); - if (!key) { alert('Veuillez saisir une clé.'); return; } - - try { - const res = await fetch(`${API_URL}?action=save_tmdb_key`, { - method: 'POST', - headers: { 'Content-Type': 'application/json', 'Authorization': localStorage.getItem('token') }, - body: JSON.stringify({ tmdb_key: key }) - }); - const data = await res.json(); - if (data.success) { - alert('✅ Clé TMDB chiffrée et sauvegardée.'); - document.getElementById('tmdb-key-input').value = ''; - closeConfigModal(); - } - } catch (err) { - console.error(err); - } -} - -// ══════════════════════════════════════════════════════════════════ -// 8. MODALE MOT DE PASSE -// ══════════════════════════════════════════════════════════════════ -function openPasswordModal() { document.getElementById('password-modal').classList.add('open'); } -function closePasswordModal() { document.getElementById('password-modal').classList.remove('open'); } - -async function saveNewPassword() { - const pwd1 = document.getElementById('new-password-input').value; - const pwd2 = document.getElementById('new-password-confirm').value; - const errEl = document.getElementById('pwd-error'); - - errEl.style.display = 'none'; - - if (pwd1.length < 4) { - errEl.textContent = 'Le mot de passe doit faire au moins 4 caractères.'; - errEl.style.display = 'block'; - return; - } - if (pwd1 !== pwd2) { - errEl.textContent = 'Les mots de passe ne correspondent pas.'; - errEl.style.display = 'block'; - return; - } - - try { - // Premier usage (compte vide) → setup_admin, sinon update_password - const secRes = await fetch(`${API_URL}?action=check_security_status`); - const secData = await secRes.json(); - const action = secData.is_blank ? 'setup_admin' : 'update_password'; - - const res = await fetch(`${API_URL}?action=${action}`, { - method: 'POST', - headers: { 'Content-Type': 'application/json', 'Authorization': localStorage.getItem('token') }, - body: JSON.stringify({ password: pwd1, new_password: pwd1 }) - }); - const data = await res.json(); - - if (data.success || data.success === '') { - alert('✅ Mot de passe mis à jour. Vous allez être redirigé vers la connexion.'); - localStorage.removeItem('token'); - window.location.href = 'login.html'; - } - } catch (err) { - console.error(err); - } -} - -// ══════════════════════════════════════════════════════════════════ -// 9. DÉCONNEXION -// ══════════════════════════════════════════════════════════════════ -function logout() { - localStorage.removeItem('token'); - window.location.href = 'login.html'; -} - -// ══════════════════════════════════════════════════════════════════ -// INIT -// ══════════════════════════════════════════════════════════════════ -document.addEventListener('DOMContentLoaded', loadDashboardData); ->>>>>>> 5a22e3f (all) +}); \ No newline at end of file