Actualiser js/admin.js

This commit is contained in:
2026-06-22 11:40:07 +02:00
parent 840c0914c2
commit a9c32c1716
+59 -75
View File
@@ -446,95 +446,84 @@ async function saveFilmForm(e) {
} catch (err) { console.error('Erreur sauvegarde :', err); } } catch (err) { console.error('Erreur sauvegarde :', err); }
} }
// ── IMPORT CSV : INTELLIGENCE ET FUSION ── // --- FONCTION D'IMPORT INTELLIGENTE ---
async function handleCsvUpload(input) { async function handleCsvUpload(input) {
if (!input.files || input.files.length === 0) return; if (!input.files || input.files.length === 0) return;
closeConfigModal();
let allData = []; let allData = [];
// 1. Lecture de TOUS les fichiers sélectionnés // 1. Lecture de tous les fichiers (supporte le multi-upload)
for (const file of input.files) { for (const file of input.files) {
try { try {
const text = await file.text(); const text = await file.text();
const data = parseCSV(text); const data = parseCSV(text);
allData = allData.concat(data); allData = allData.concat(data);
} catch(e) { console.error('Erreur fichier', e); } } catch(e) {
console.error('Erreur lecture fichier', file.name, e);
}
} }
input.value = ''; // Reset de l'input input.value = ''; // Reset l'input file
if (allData.length === 0) { if (allData.length === 0) {
alert('❌ Fichiers vides ou mal formatés.'); alert('❌ Fichiers vides ou mal formatés.');
return; return;
} }
// 2. Dédoublonnage et fusion intelligente des CSV en mémoire // 2. Fusion et Dédoublonnage en mémoire
// Utilise une Map pour regrouper les données par "Titre|Année"
const mergedMap = new Map(); const mergedMap = new Map();
allData.forEach(row => { allData.forEach(row => {
const title = row['title'] || row['Name'] || 'Sans titre'; const title = (row['Title'] || row['Name'] || '').trim();
const dateStr = row['publish_date'] || row['Year'] || row['year'] || ''; const dateStr = (row['Date'] || row['Year'] || row['year'] || '').toString();
const m = String(dateStr).match(/(\d{4})/); const year = dateStr.match(/(\d{4})/)?.[1] || '';
const year = m ? m[1] : ''; const key = `${title.toLowerCase()}|${year}`;
const key = `${title.toLowerCase().trim()}|${year}`;
if (!mergedMap.has(key)) { if (!mergedMap.has(key)) {
mergedMap.set(key, { ...row }); mergedMap.set(key, { ...row });
} else { } else {
const existing = mergedMap.get(key); const existing = mergedMap.get(key);
// Si la ligne actuelle a une note/critique et que l'existante n'en a pas, on complète // Fusion des notes et critiques si présentes
if ((row['Rating'] || row['rating']) && !(existing['Rating'] || existing['rating'])) { if ((row['Rating'] || row['rating']) && !existing['Rating']) existing['Rating'] = row['Rating'];
existing['Rating'] = row['Rating'] || row['rating']; if ((row['Review'] || row['review']) && !existing['Review']) existing['Review'] = row['Review'];
}
if ((row['Review'] || row['review']) && !(existing['Review'] || existing['review'])) {
existing['Review'] = row['Review'] || row['review'];
}
} }
}); });
const finalData = Array.from(mergedMap.values()); let finalData = Array.from(mergedMap.values());
// 3. Protection de la Base de Données (Ne pas écraser l'existant) // 3. Protection : Ne pas écraser les données BDD existantes
finalData.forEach(row => { finalData = finalData.map(row => {
const title = row['title'] || row['Name'] || 'Sans titre'; const title = (row['Title'] || row['Name'] || '').trim();
const dateStr = row['publish_date'] || row['Year'] || row['year'] || ''; const dateStr = (row['Date'] || row['Year'] || row['year'] || '').toString();
const m = String(dateStr).match(/(\d{4})/); const year = dateStr.match(/(\d{4})/)?.[1] || '';
const year = m ? m[1] : '';
// Cherche le film dans le dashboard déjà chargé
const existingDb = allItems.find(f => const existingDb = allItems.find(f =>
f.title.toLowerCase().trim() === title.toLowerCase().trim() && f.title.toLowerCase().trim() === title.toLowerCase() &&
String(f.year) === String(year) && String(f.year) === String(year) &&
f.type === currentAdminTab f.type === currentAdminTab
); );
if (existingDb) { if (existingDb) {
const csvRating = row['Rating'] || row['rating']; return {
const csvReview = row['Review'] || row['review']; ...row,
'Rating': (row['Rating'] || row['rating']) || existingDb.rating,
// Récupère l'ancienne note si le fichier CSV n'en contient pas 'Review': (row['Review'] || row['review']) || existingDb.review
if (!csvRating && existingDb.rating) { };
row['Rating'] = existingDb.rating;
}
// Récupère l'ancienne critique si le fichier CSV n'en contient pas
if (!csvReview && existingDb.review) {
row['Review'] = existingDb.review;
}
} }
return row;
}); });
showProgressModal(finalData.length); // 4. Lancement de la modale de progression stylisée
showImportModal(finalData.length, currentAdminTab);
// 4. Envoi par lots de 10 // 5. Envoi par lots de 10
const batchSize = 10; const batchSize = 10;
let processed = 0; let processed = 0;
let totalStats = { upc_hits: 0, tmdb_hits: 0, no_image: 0 };
for (let i = 0; i < finalData.length; i += batchSize) { for (let i = 0; i < finalData.length; i += batchSize) {
const batch = finalData.slice(i, i + batchSize); const batch = finalData.slice(i, i + batchSize);
try { try {
const response = await fetch(`${API_URL}?action=import_batch`, { await fetch(`${API_URL}?action=import_batch`, {
method: 'POST', method: 'POST',
headers: { headers: {
'Authorization': localStorage.getItem('token'), 'Authorization': localStorage.getItem('token'),
@@ -542,33 +531,16 @@ async function handleCsvUpload(input) {
}, },
body: JSON.stringify({ items: batch, type: currentAdminTab }) body: JSON.stringify({ items: batch, type: currentAdminTab })
}); });
const resData = await response.json();
if (resData.stats) {
totalStats.upc_hits += resData.stats.upc_hits || 0;
totalStats.tmdb_hits += resData.stats.tmdb_hits || 0;
totalStats.no_image += resData.stats.no_image || 0;
}
} catch (err) { } catch (err) {
console.error('Erreur sur un lot:', err); console.error('Erreur lot import:', err);
} }
processed += batch.length; processed += batch.length;
updateProgressModal(processed, finalData.length); updateImportModal(processed, finalData.length);
} }
closeProgressModal(); // 6. Finalisation
closeImportModal();
// 5. Affichage du bilan alert(`✅ Import terminé ! ${finalData.length} élément(s) traité(s).`);
let statsMessage = `✅ Import terminé avec succès !\n\n🎬 Films traités : ${finalData.length}\n`;
if (totalStats.upc_hits > 0 || totalStats.tmdb_hits > 0 || totalStats.no_image > 0) {
statsMessage += `\n📊 Bilan des recherches d'images :\n`;
if (currentAdminTab === 'videotheque') {
statsMessage += `• 📦 Jaquettes via Code EAN (UPC) : ${totalStats.upc_hits}\n`;
}
statsMessage += `• 🍿 Affiches via le titre (TMDB) : ${totalStats.tmdb_hits}\n`;
statsMessage += `• ❌ Aucune image trouvée : ${totalStats.no_image}\n`;
}
alert(statsMessage);
loadDashboardData(); loadDashboardData();
} }
@@ -627,21 +599,33 @@ async function saveNewPassword() {
} catch (err) { console.error('Erreur mise à jour mot de passe :', err); } } catch (err) { console.error('Erreur mise à jour mot de passe :', err); }
} }
function showProgressModal(total) { // --- IMPORT PROGRESSIVE DYNAMIQUE ---
document.getElementById('progress-text').textContent = 'Traitement des films...'; function showImportModal(total, type) {
document.getElementById('progress-bar').style.width = '0%'; const modal = document.getElementById('import-progress-modal');
document.getElementById('progress-count').textContent = `0 / ${total}`; const title = document.getElementById('import-modal-title');
document.getElementById('progress-overlay').classList.add('open'); const desc = document.getElementById('import-modal-desc');
// Adaptation selon le contexte
title.innerHTML = type === 'critique'
? '<i class="ti ti-star"></i> Import des Critiques'
: '<i class="ti ti-video"></i> Import Vidéothèque';
desc.textContent = `Traitement de ${total} élément(s)...`;
document.getElementById('import-progress-bar').style.width = '0%';
document.getElementById('import-modal-counter').textContent = '0%';
modal.classList.add('open');
} }
function updateProgressModal(current, total) { function updateImportModal(current, total) {
const pct = Math.round((current / total) * 100); const pct = Math.round((current / total) * 100);
document.getElementById('progress-bar').style.width = pct + '%'; document.getElementById('import-progress-bar').style.width = pct + '%';
document.getElementById('progress-count').textContent = `${current} / ${total}`; document.getElementById('import-modal-counter').textContent = `${pct}%`;
} }
function closeProgressModal() { function closeImportModal() {
document.getElementById('progress-overlay').classList.remove('open'); document.getElementById('import-progress-modal').classList.remove('open');
} }
async function fetchInfoFromEAN() { async function fetchInfoFromEAN() {