<!DOCTYPE html><html lang="fr"> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0">Gestionnaire de Fichiers <title>Gestionnaire de Fichiers</title> <style> :root { --primary: #4361ee; --secondary: #3a0ca3; --success: #4cc9f0; --danger: #f72585; --light: #f8f9fa; --dark: #212529; --gray: #6c757d; } * { margin: 0; padding: 0; box-sizing: border-box; font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; } body { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: var(--dark); line-height: 1.6; min-height: 100vh; padding: 20px; } .container { max-width: 1200px; margin: 0 auto; background: white; border-radius: 15px; box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2); overflow: hidden; } header { background: linear-gradient(135deg, var(--primary) 0%, var(--secondary) 100%); color: white; padding: 30px; text-align: center; } h1 { font-size: 2.5rem; margin-bottom: 10px; } .subtitle { font-size: 1.1rem; opacity: 0.9; } .main-content { display: grid; grid-template-columns: 1fr 1fr; gap: 30px; padding: 30px; } @media (max-width: 768px) { .main-content { grid-template-columns: 1fr; } } .card { background: white; border-radius: 10px; padding: 25px; box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1); transition: transform 0.3s ease; border: 1px solid #eaeaea; } .card:hover { transform: translateY(-5px); } .card h2 { color: var(--primary); margin-bottom: 20px; padding-bottom: 10px; border-bottom: 2px solid #f0f0f0; display: flex; align-items: center; gap: 10px; } .upload-area { border: 2px dashed var(--primary); border-radius: 8px; padding: 30px; text-align: center; margin-bottom: 20px; transition: all 0.3s ease; cursor: pointer; } .upload-area:hover { background-color: #f0f7ff; border-color: var(--secondary); } .upload-area i { font-size: 48px; color: var(--primary); margin-bottom: 15px; } .upload-area p { margin-bottom: 15px; color: var(--gray); } .btn { display: inline-block; background: linear-gradient(135deg, var(--primary) 0%, var(--secondary) 100%); color: white; padding: 12px 25px; border: none; border-radius: 5px; cursor: pointer; font-size: 1rem; font-weight: 600; transition: all 0.3s ease; text-decoration: none; } .btn:hover { transform: translateY(-2px); box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); } .btn-secondary { background: #f0f0f0; color: var(--dark); } .btn-secondary:hover { background: #e0e0e0; } .btn-danger { background: var(--danger); } .file-list { list-style: none; max-height: 400px; overflow-y: auto; border: 1px solid #eaeaea; border-radius: 8px; } .file-item { display: flex; justify-content: space-between; align-items: center; padding: 12px 15px; border-bottom: 1px solid #f0f0f0; transition: background-color 0.2s; } .file-item:hover { background-color: #f9f9f9; } .file-info { display: flex; align-items: center; flex: 1; } .file-icon { margin-right: 10px; color: var(--primary); font-size: 1.2rem; } .file-name { font-weight: 500; word-break: break-all; } .file-details { display: flex; align-items: center; gap: 15px; } .file-size { color: var(--gray); font-size: 0.9rem; min-width: 70px; text-align: right; } .file-actions { display: flex; gap: 10px; } .action-btn { background: none; border: none; cursor: pointer; color: var(--gray); font-size: 1.1rem; transition: color 0.2s; padding: 5px; border-radius: 3px; } .action-btn:hover { color: var(--primary); background: #f0f0f0; } .path-info { background-color: #f0f7ff; padding: 15px; border-radius: 8px; margin-bottom: 20px; font-family: monospace; word-break: break-all; display: flex; align-items: center; gap: 10px; } .alert { padding: 15px; border-radius: 5px; margin-bottom: 20px; display: none; } .alert-success { background-color: #d4edda; color: #155724; border: 1px solid #c3e6cb; } .alert-error { background-color: #f8d7da; color: #721c24; border: 1px solid #f5c6cb; } .progress-bar { height: 6px; background-color: #e0e0e0; border-radius: 3px; margin-top: 10px; overflow: hidden; display: none; } .progress { height: 100%; background: linear-gradient(90deg, var(--primary) 0%, var(--secondary) 100%); width: 0%; transition: width 0.3s ease; } footer { text-align: center; padding: 20px; color: var(--gray); font-size: 0.9rem; background: #f8f9fa; border-top: 1px solid #eaeaea; } .storage-info { background-color: #f0f7ff; padding: 15px; border-radius: 8px; margin-top: 20px; text-align: center; } .storage-bar { height: 10px; background-color: #e0e0e0; border-radius: 5px; margin-top: 10px; overflow: hidden; } .storage-used { height: 100%; background: linear-gradient(90deg, var(--primary) 0%, var(--secondary) 100%); width: 0%; transition: width 0.5s ease; } .empty-state { text-align: center; padding: 30px; color: var(--gray); } .empty-state i { font-size: 48px; margin-bottom: 15px; opacity: 0.5; } .search-box { margin-bottom: 20px; position: relative; } .search-box input { width: 100%; padding: 12px 15px; border: 1px solid #eaeaea; border-radius: 5px; font-size: 1rem; } .search-box i { position: absolute; right: 15px; top: 50%; transform: translateY(-50%); color: var(--gray); } .file-type-filter { display: flex; gap: 10px; margin-bottom: 20px; flex-wrap: wrap; } .filter-btn { padding: 8px 15px; background: #f0f0f0; border: none; border-radius: 20px; cursor: pointer; transition: all 0.2s; } .filter-btn.active { background: var(--primary); color: white; } .file-preview { margin-top: 20px; padding: 15px; background: #f9f9f9; border-radius: 8px; display: none; } .preview-content { max-height: 200px; overflow: auto; margin-top: 10px; padding: 10px; background: white; border-radius: 5px; font-family: monospace; font-size: 0.9rem; } </style> <div class="container"> <header>

📁 Gestionnaire de Fichiers

<p class="subtitle">Visualisez et gérez les fichiers de votre site web</p> </header> <div class="alert alert-success" id="successAlert"> Fichier téléchargé avec succès! </div> <div class="alert alert-error" id="errorAlert"> Erreur lors du téléchargement du fichier. </div> <div class="main-content"> <div class="card">

<i>📤</i> Télécharger un fichier

<div class="upload-area" id="dropArea"> <i>📁</i> <p>Glissez-déposez vos fichiers ici ou cliquez pour sélectionner</p> <button class="btn" id="browseBtn">Parcourir les fichiers</button> </div> <div class="progress-bar" id="progressBar"> <div class="progress" id="progress"></div> </div> <div style="margin-top: 20px; display: flex; gap: 10px;"> <button class="btn" id="uploadBtn" disabled>Télécharger</button> <button class="btn btn-secondary" id="clearBtn">Effacer</button> </div> <div class="storage-info"> <p>Espace de stockage utilisé: <span id="storageUsed">0 KB</span> / <span id="storageTotal">5 MB</span></p> <div class="storage-bar"> <div class="storage-used" id="storageBar"></div> </div> </div> </div> <div class="card">

<i>📂</i> Fichiers du site

<div class="path-info"> <i>📁</i> <span id="currentPath">/racine/</span> </div> <div class="search-box"> <i>🔍</i> </div> <div class="file-type-filter"> <button class="filter-btn active" data-filter="all">Tous</button> <button class="filter-btn" data-filter="image">Images</button> <button class="filter-btn" data-filter="document">Documents</button> <button class="filter-btn" data-filter="code">Code</button> <button class="filter-btn" data-filter="other">Autres</button> </div> <ul class="file-list" id="fileList"> <!-- Les fichiers seront affichés ici par JavaScript --> </ul> <div class="file-preview" id="filePreview">

Aperçu du fichier

<div class="preview-content" id="previewContent"></div> </div> <div style="margin-top: 20px; text-align: center; display: flex; gap: 10px; justify-content: center;"> <button class="btn btn-secondary" id="refreshBtn">Actualiser</button> <button class="btn btn-danger" id="clearStorageBtn">Tout supprimer</button> </div> </div> </div> <footer> <p>Gestionnaire de fichiers &copy; 2023 - Fonctionne entièrement côté client</p> </footer> </div> <script> // Éléments DOM const fileInput = document.getElementById('fileInput'); const browseBtn = document.getElementById('browseBtn'); const dropArea = document.getElementById('dropArea'); const uploadBtn = document.getElementById('uploadBtn'); const clearBtn = document.getElementById('clearBtn'); const refreshBtn = document.getElementById('refreshBtn'); const clearStorageBtn = document.getElementById('clearStorageBtn'); const progressBar = document.getElementById('progressBar'); const progress = document.getElementById('progress'); const successAlert = document.getElementById('successAlert'); const errorAlert = document.getElementById('errorAlert'); const fileList = document.getElementById('fileList'); const currentPath = document.getElementById('currentPath'); const storageUsed = document.getElementById('storageUsed'); const storageTotal = document.getElementById('storageTotal'); const storageBar = document.getElementById('storageBar'); const searchInput = document.getElementById('searchInput'); const filterButtons = document.querySelectorAll('.filter-btn'); const filePreview = document.getElementById('filePreview'); const previewContent = document.getElementById('previewContent'); let selectedFile = null; const MAX_STORAGE = 5 * 1024 * 1024; // 5 MB en bytes let currentFilter = 'all'; let files = []; // Initialisation document.addEventListener('DOMContentLoaded', function() { loadFiles(); updateStorageInfo(); // Ajouter des fichiers d'exemple si le stockage est vide if (files.length === 0) { addSampleFiles(); } }); // Événements browseBtn.addEventListener('click', () => fileInput.click()); fileInput.addEventListener('change', (e) => { selectedFile = e.target.files[0]; updateUI(); }); dropArea.addEventListener('dragover', (e) => { e.preventDefault(); dropArea.style.borderColor = 'var(--secondary)'; dropArea.style.backgroundColor = '#f0f7ff'; }); dropArea.addEventListener('dragleave', () => { dropArea.style.borderColor = 'var(--primary)'; dropArea.style.backgroundColor = ''; }); dropArea.addEventListener('drop', (e) => { e.preventDefault(); dropArea.style.borderColor = 'var(--primary)'; dropArea.style.backgroundColor = ''; if (e.dataTransfer.files.length) { selectedFile = e.dataTransfer.files[0]; fileInput.files = e.dataTransfer.files; updateUI(); } }); uploadBtn.addEventListener('click', uploadFile); clearBtn.addEventListener('click', clearSelection); refreshBtn.addEventListener('click', loadFiles); clearStorageBtn.addEventListener('click', clearStorage); searchInput.addEventListener('input', filterFiles); filterButtons.forEach(btn => { btn.addEventListener('click', function() { filterButtons.forEach(b => b.classList.remove('active')); this.classList.add('active'); currentFilter = this.getAttribute('data-filter'); filterFiles(); }); }); // Fonctions function updateUI() { if (selectedFile) { uploadBtn.disabled = false; dropArea.innerHTML = ` <i>📄</i> <p><strong>${selectedFile.name}</strong></p> <p>Taille: ${formatFileSize(selectedFile.size)}</p> `; } else { uploadBtn.disabled = true; dropArea.innerHTML = ` <i>📁</i> <p>Glissez-déposez vos fichiers ici ou cliquez pour sélectionner</p> <button class="btn" id="browseBtn">Parcourir les fichiers</button> `; document.getElementById('browseBtn').addEventListener('click', () => fileInput.click()); } } function formatFileSize(bytes) { if (bytes === 0) return '0 Bytes'; const k = 1024; const sizes = ['Bytes', 'KB', 'MB', 'GB']; const i = Math.floor(Math.log(bytes) / Math.log(k)); return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]; } function uploadFile() { if (!selectedFile) return; // Vérifier la taille du fichier if (selectedFile.size > MAX_STORAGE) { errorAlert.textContent = "Le fichier est trop volumineux (max 5 MB)"; showAlert(errorAlert); return; } // Vérifier l'espace disponible const currentStorage = getUsedStorage(); if (currentStorage + selectedFile.size > MAX_STORAGE) { errorAlert.textContent = "Espace de stockage insuffisant"; showAlert(errorAlert); return; } progressBar.style.display = 'block'; progress.style.width = '0%'; // Simuler l'upload avec une progression let progressValue = 0; const progressInterval = setInterval(() => { progressValue += 5; progress.style.width = progressValue + '%'; if (progressValue >= 100) { clearInterval(progressInterval); saveFile(selectedFile); progressBar.style.display = 'none'; showAlert(successAlert); clearSelection(); loadFiles(); updateStorageInfo(); } }, 50); } function saveFile(file) { // Récupérer les fichiers existants files = JSON.parse(localStorage.getItem('siteFiles') || '[]'); // Vérifier si le fichier existe déjà const existingFileIndex = files.findIndex(f => f.name === file.name); // Lire le fichier comme Data URL const reader = new FileReader(); reader.onload = function(e) { if (existingFileIndex !== -1) { // Remplacer le fichier existant files[existingFileIndex] = { name: file.name, size: file.size, type: file.type, data: e.target.result, lastModified: new Date().toISOString() }; } else { // Ajouter le nouveau fichier files.push({ name: file.name, size: file.size, type: file.type, data: e.target.result, lastModified: new Date().toISOString() }); } // Sauvegarder dans le localStorage localStorage.setItem('siteFiles', JSON.stringify(files)); }; reader.readAsDataURL(file); } function loadFiles() { fileList.innerHTML = '<li class="file-item">Chargement...</li>'; setTimeout(() => { files = JSON.parse(localStorage.getItem('siteFiles') || '[]'); displayFiles(files); }, 500); } function displayFiles(filesToDisplay) { fileList.innerHTML = ''; filePreview.style.display = 'none'; if (filesToDisplay.length === 0) { fileList.innerHTML = ` <div class="empty-state"> <i>📁</i> <p>Aucun fichier trouvé</p> </div> `; return; } filesToDisplay.forEach((file, index) => { const li = document.createElement('li'); li.className = 'file-item'; const icon = getFileIcon(file.type); const fileType = getFileType(file.type); li.innerHTML = ` <div class="file-info"> <span class="file-icon">${icon}</span> <span class="file-name">${file.name}</span> </div> <div class="file-details"> <span class="file-size">${formatFileSize(file.size)}</span> <div class="file-actions"> <button class="action-btn view-btn" title="Aperçu" data-index="${index}">👁️</button> <button class="action-btn download-btn" title="Télécharger" data-index="${index}">⬇️</button> <button class="action-btn delete-btn" title="Supprimer" data-index="${index}">🗑️</button> </div> </div> `; // Ajouter un attribut de type pour le filtrage li.setAttribute('data-type', fileType); fileList.appendChild(li); }); // Ajouter les événements aux boutons document.querySelectorAll('.view-btn').forEach(btn => { btn.addEventListener('click', function() { viewFile(parseInt(this.getAttribute('data-index'))); }); }); document.querySelectorAll('.download-btn').forEach(btn => { btn.addEventListener('click', function() { downloadFile(parseInt(this.getAttribute('data-index'))); }); }); document.querySelectorAll('.delete-btn').forEach(btn => { btn.addEventListener('click', function() { deleteFile(parseInt(this.getAttribute('data-index'))); }); }); // Appliquer le filtre actuel filterFiles(); } function viewFile(index) { if (index >= 0 && index < files.length) { const file = files[index]; filePreview.style.display = 'block'; // Afficher un aperçu selon le type de fichier if (file.type.startsWith('image/')) { previewContent.innerHTML = `<img src="${file.data}" style="max-width: 100%;" alt="${file.name}">`; } else if (file.type === 'text/plain' || file.type === 'application/javascript' || file.type === 'text/css' || file.type === 'text/html') { // Pour les fichiers texte, décoder le contenu const base64Data = file.data.split(',')[1]; const textContent = atob(base64Data); previewContent.textContent = textContent.substring(0, 1000) + (textContent.length > 1000 ? '...' : ''); } else { previewContent.innerHTML = `<p>Aperçu non disponible pour ce type de fichier.</p>`; } // Faire défiler jusqu'à l'aperçu filePreview.scrollIntoView({ behavior: 'smooth' }); } } function downloadFile(index) { if (index >= 0 && index < files.length) { const file = files[index]; // Créer un lien de téléchargement const a = document.createElement('a'); a.href = file.data; a.download = file.name; document.body.appendChild(a); a.click(); document.body.removeChild(a); showAlert(successAlert, `Fichier "${file.name}" téléchargé avec succès`); } } function deleteFile(index) { if (index >= 0 && index < files.length) { const fileName = files[index].name; if (confirm(`Êtes-vous sûr de vouloir supprimer "${fileName}" ?`)) { files.splice(index, 1); localStorage.setItem('siteFiles', JSON.stringify(files)); loadFiles(); updateStorageInfo(); showAlert(successAlert, `Fichier "${fileName}" supprimé avec succès`); } } } function getFileIcon(type) { const icons = { 'text/html': '🌐', 'text/css': '🎨', 'application/javascript': '⚙️', 'image/jpeg': '🖼️', 'image/png': '🖼️', 'image/gif': '🖼️', 'application/pdf': '📄', 'text/plain': '📝', 'default': '📁' }; return icons[type] || icons.default; } function getFileType(mimeType) { if (mimeType.startsWith('image/')) return 'image'; if (mimeType === 'application/pdf' || mimeType === 'text/plain') return 'document'; if (mimeType === 'text/html' || mimeType === 'text/css' || mimeType === 'application/javascript') return 'code'; return 'other'; } function filterFiles() { const searchTerm = searchInput.value.toLowerCase(); const fileItems = document.querySelectorAll('.file-item'); fileItems.forEach(item => { const fileName = item.querySelector('.file-name').textContent.toLowerCase(); const fileType = item.getAttribute('data-type'); const matchesSearch = fileName.includes(searchTerm); const matchesFilter = currentFilter === 'all' || fileType === currentFilter; item.style.display = matchesSearch && matchesFilter ? 'flex' : 'none'; }); } function clearSelection() { selectedFile = null; fileInput.value = ''; updateUI(); } function showAlert(alertElement, message = null) { if (message) { alertElement.textContent = message; } alertElement.style.display = 'block'; setTimeout(() => { alertElement.style.display = 'none'; }, 5000); } function getUsedStorage() { return files.reduce((total, file) => total + file.size, 0); } function updateStorageInfo() { const used = getUsedStorage(); storageUsed.textContent = formatFileSize(used); storageTotal.textContent = formatFileSize(MAX_STORAGE); const percentage = (used / MAX_STORAGE) * 100; storageBar.style.width = percentage + '%'; } function clearStorage() { if (confirm("Êtes-vous sûr de vouloir supprimer tous les fichiers ? Cette action est irréversible.")) { localStorage.removeItem('siteFiles'); files = []; loadFiles(); updateStorageInfo(); showAlert(successAlert, "Tous les fichiers ont été supprimés"); } } function addSampleFiles() { // Ajouter quelques fichiers d'exemple const sampleFiles = [ { name: 'index.html', size: 1024, type: 'text/html', data: 'data:text/html;base64,PGh0bWw+PGhlYWQ+PC9oZWFkPjxib2R5PjxoMT5Cb25qb3VyIPCfmoA8L2gxPjwvYm9keT48L2h0bWw+', lastModified: new Date().toISOString() }, { name: 'style.css', size: 2048, type: 'text/css', data: 'data:text/css;base64,Ym9keSB7IGZvbnQtZmFtaWx5OiBBcmlhbCwgc2Fucy1zZXJpZjsgbWFyZ2luOiAwOyBwYWRkaW5nOiAyMHB4OyB9', lastModified: new Date().toISOString() }, { name: 'script.js', size: 3072, type: 'application/javascript', data: 'data:application/javascript;base64,Y29uc29sZS5sb2coIkJvbmpvdXIg4p2kIPCfmoAiKTs=', lastModified: new Date().toISOString() } ]; localStorage.setItem('siteFiles', JSON.stringify(sampleFiles)); files = sampleFiles; displayFiles(files); updateStorageInfo(); } </script>