// Dashboard JavaScript - Version corrigée document.addEventListener('DOMContentLoaded', function() { // Initialisation générale initializeDashboard(); initializeContextMenu(); initializeDropdowns(); initializeGridView(); initializeCollaboration(); initializeFileHandlers(); }); // =================== INITIALISATION =================== function initializeDashboard() { // Formatage des tailles de fichiers document.querySelectorAll('.file-size').forEach(function(element) { const size = parseInt(element.getAttribute('data-size')); element.textContent = formatFileSize(size); }); // Événements de base document.getElementById('searchButton')?.addEventListener('click', searchFiles); document.getElementById('newFolderBtn')?.addEventListener('click', showNewFolderModal); document.getElementById('themeSwitcher')?.addEventListener('click', toggleDarkMode); // Thème initTheme(); // Version loadVersion(); // Double-clic sur les dossiers document.querySelectorAll('tr[data-type="folder"] td:first-child, tr[data-type="shared-folder"] td:first-child').forEach(cell => { cell.style.userSelect = 'none'; cell.addEventListener('dblclick', (e) => { e.preventDefault(); const row = cell.closest('tr'); const url = row.dataset.url; if (url) window.location.href = url; }); }); } // =================== MENU CONTEXTUEL =================== function initializeContextMenu() { const contextMenu = document.querySelector('.context-menu'); let selectedItem = null; // Gestionnaire du clic droit document.addEventListener('contextmenu', function(e) { const row = e.target.closest('tr[data-type]'); if (!row) return; e.preventDefault(); selectedItem = { type: row.dataset.type, name: row.dataset.name, url: row.dataset.url, element: row }; adjustMenuOptions(selectedItem); showContextMenu(e.pageX, e.pageY); }); // Fermer le menu au clic extérieur document.addEventListener('click', function(e) { const contextMenu = document.getElementById('contextMenu'); if (!contextMenu?.contains(e.target)) { hideContextMenu(); } });// Actions du menu contextuel document.addEventListener('click', function(e) { if (!e.target.closest('#contextMenu')) return; e.preventDefault(); const target = e.target.closest('button, a'); if (!target || !selectedItem) return; let action = ''; if (target.classList.contains('context-item-open')) action = 'open'; else if (target.classList.contains('context-item-rename')) action = 'rename'; else if (target.classList.contains('context-item-collaborate')) action = 'collaborate'; else if (target.classList.contains('context-item-share')) action = 'copy-link'; else if (target.classList.contains('context-item-move')) action = 'move'; else if (target.classList.contains('context-item-leave')) action = 'leave'; else if (target.classList.contains('context-item-delete')) action = 'delete'; if (action) { handleMenuAction(action, selectedItem); hideContextMenu(); } }); function adjustMenuOptions(item) { const contextMenu = document.getElementById('contextMenu'); if (!contextMenu) return; const isFile = item.type === 'file'; const isFolder = item.type === 'folder'; const isSharedFolder = item.type === 'shared-folder'; // DEBUG console.log('🔍 Context Menu Debug:', { itemType: item.type, itemName: item.name, isFile, isFolder, isSharedFolder }); // Vérifier si l'utilisateur est propriétaire (pour dossiers partagés) let isOwner = false; if (isSharedFolder) { const ownerCell = item.element.querySelector('td:nth-child(3) .text-muted'); isOwner = ownerCell && ownerCell.textContent.trim() === 'moi'; console.log('🔍 Shared Folder - isOwner:', isOwner); } // Récupérer tous les éléments du menu const openBtn = contextMenu.querySelector('.context-item-open'); const renameBtn = contextMenu.querySelector('.context-item-rename'); const collaborateBtn = contextMenu.querySelector('.context-item-collaborate'); const shareBtn = contextMenu.querySelector('.context-item-share'); const moveBtn = contextMenu.querySelector('.context-item-move'); const leaveBtn = contextMenu.querySelector('.context-item-leave'); const separator = contextMenu.querySelector('.menu-separator'); const deleteBtn = contextMenu.querySelector('.context-item-delete'); // MASQUER TOUT PAR DÉFAUT if (openBtn) openBtn.style.display = 'none'; if (renameBtn) renameBtn.style.display = 'none'; if (collaborateBtn) collaborateBtn.style.display = 'none'; if (shareBtn) shareBtn.style.display = 'none'; if (moveBtn) moveBtn.style.display = 'none'; if (leaveBtn) leaveBtn.style.display = 'none'; if (separator) separator.style.display = 'none'; if (deleteBtn) deleteBtn.style.display = 'none'; // AFFICHER SELON LE TYPE if (isFile) { // FICHIERS : Renommer, Copier le lien, Déplacer, Supprimer console.log('✅ Affichage menu FICHIER'); if (renameBtn) renameBtn.style.display = 'flex'; if (shareBtn) shareBtn.style.display = 'flex'; if (moveBtn) moveBtn.style.display = 'flex'; if (separator) separator.style.display = 'block'; if (deleteBtn) deleteBtn.style.display = 'flex'; } else if (isFolder) { // DOSSIERS PERSONNELS : Ouvrir, Renommer, Collaborer, Supprimer console.log('✅ Affichage menu DOSSIER'); if (openBtn) openBtn.style.display = 'flex'; if (renameBtn) renameBtn.style.display = 'flex'; if (collaborateBtn) { collaborateBtn.style.display = 'flex'; // Vérifier si déjà collaboratif const collabBadge = item.element.querySelector('.collaboration-badge'); const isCollaborative = collabBadge !== null; const span = collaborateBtn.querySelector('span'); if (span) { span.textContent = isCollaborative ? 'Gérer la collaboration' : 'Activer la collaboration'; } } if (separator) separator.style.display = 'block'; if (deleteBtn) deleteBtn.style.display = 'flex'; } else if (isSharedFolder) { // DOSSIERS PARTAGÉS console.log('✅ Affichage menu DOSSIER PARTAGÉ (owner:', isOwner, ')'); if (openBtn) openBtn.style.display = 'flex'; if (isOwner) { // PROPRIÉTAIRE : Renommer, Collaborer, Supprimer if (renameBtn) renameBtn.style.display = 'flex'; if (collaborateBtn) { collaborateBtn.style.display = 'flex'; const collabBadge = item.element.querySelector('.collaboration-badge'); const isCollaborative = collabBadge !== null; const span = collaborateBtn.querySelector('span'); if (span) { span.textContent = isCollaborative ? 'Gérer la collaboration' : 'Activer la collaboration'; } } if (separator) separator.style.display = 'block'; if (deleteBtn) deleteBtn.style.display = 'flex'; } else { // INVITÉ : Quitter seulement if (leaveBtn) leaveBtn.style.display = 'flex'; } } // DEBUG FINAL : afficher l'état de tous les boutons console.log('📋 État final des boutons:', { open: openBtn?.style.display, rename: renameBtn?.style.display, collaborate: collaborateBtn?.style.display, share: shareBtn?.style.display, move: moveBtn?.style.display, leave: leaveBtn?.style.display, delete: deleteBtn?.style.display }); } function showContextMenu(x, y) { const contextMenu = document.getElementById('contextMenu'); if (!contextMenu) return; contextMenu.style.display = 'block'; // Ajuster la position const menuRect = contextMenu.getBoundingClientRect(); const windowWidth = window.innerWidth; const windowHeight = window.innerHeight; if (x + menuRect.width > windowWidth) { x = windowWidth - menuRect.width - 10; } if (y + menuRect.height > windowHeight) { y = windowHeight - menuRect.height - 10; } contextMenu.style.left = `${x}px`; contextMenu.style.top = `${y}px`; } function hideContextMenu() { const contextMenu = document.getElementById('contextMenu'); if (contextMenu) { contextMenu.style.display = 'none'; } selectedItem = null; } function handleMenuAction(action, item) { switch(action) { case 'open': if (item.type === 'folder') { window.location.href = `/dpanel/dashboard/folder/${encodeURIComponent(item.name)}`; } else if (item.type === 'shared-folder') { window.location.href = item.url; } break; case 'rename': if (item.type === 'folder') { renameFolder(item.name); } else if (item.type === 'file') { renameFile(item.name); } break; case 'collaborate': // Détecter si le dossier est déjà collaboratif en cherchant le badge const collabBadge = item.element.querySelector('.collaboration-badge'); const isCollaborative = collabBadge !== null; if (isCollaborative) { // Si déjà collaboratif, afficher les détails showCollaborationDetails(item.name, item.type); } else { // Sinon, activer la collaboration toggleCollaboration(item.name, item.type, true); } break; case 'copy-link': if (item.url) { navigator.clipboard.writeText(item.url).then(() => { showToast('success', 'Lien copié'); }); } break; case 'move': if (item.type === 'file') { // Utiliser la modal Bootstrap pour le déplacement showMoveFileBootstrapModal(item.name); } break; case 'leave': if (item.type === 'shared-folder') { leaveSharedFolder(item.name, item.element); } break; case 'delete': if (item.type === 'folder') { confirmDeleteFolder(item.name); } else if (item.type === 'file') { confirmDeleteFile(item.name); } break; } } } // =================== DROPDOWNS =================== function initializeDropdowns() { // Gestionnaire pour les dropdowns Bootstrap document.querySelectorAll('[data-bs-toggle="dropdown"]').forEach(toggle => { toggle.addEventListener('click', function(e) { e.preventDefault(); e.stopPropagation(); // Fermer les autres dropdowns document.querySelectorAll('.dropdown-menu.show').forEach(menu => { if (menu !== this.nextElementSibling) { menu.classList.remove('show'); } }); // Toggle le dropdown actuel const dropdown = this.nextElementSibling; if (dropdown && dropdown.classList.contains('dropdown-menu')) { // Correction du positionnement pour éviter le débordement en bas dropdown.classList.toggle('show'); if (dropdown.classList.contains('show')) { // Reset dropdown.style.top = ''; dropdown.style.bottom = ''; dropdown.style.transform = ''; const rect = dropdown.getBoundingClientRect(); const windowHeight = window.innerHeight; if (rect.bottom > windowHeight) { // Afficher au-dessus si déborde dropdown.style.top = 'auto'; dropdown.style.bottom = '100%'; dropdown.style.transform = 'translateY(-8px)'; } else { dropdown.style.top = ''; dropdown.style.bottom = ''; dropdown.style.transform = ''; } } } // Gestion dynamique des options selon le type/propriétaire const tr = this.closest('tr[data-type]'); if (tr && dropdown) { const type = tr.getAttribute('data-type'); const owner = tr.querySelector('td:nth-child(3) .text-muted')?.textContent?.trim(); // Pour les dossiers partagés, vérifier si on est propriétaire const isOwner = owner === 'moi'; dropdown.querySelectorAll('.dropdown-item').forEach(item => { const action = item.textContent.trim(); // Déplacer : seulement pour les fichiers if (action.includes('Déplacer')) { item.style.display = (type === 'file') ? '' : 'none'; } // Renommer/Supprimer : pas pour dossier partagé non propriétaire if ((action.includes('Renommer') || action.includes('Supprimer')) && type === 'shared-folder' && !isOwner) { item.style.display = 'none'; } // Quitter : seulement pour dossier partagé non propriétaire if (action.includes('Quitter ce dossier')) { item.style.display = (type === 'shared-folder' && !isOwner) ? '' : 'none'; } }); } }); }); // Fermer les dropdowns au clic extérieur document.addEventListener('click', function(e) { if (!e.target.closest('.dropdown')) { document.querySelectorAll('.dropdown-menu.show').forEach(menu => { menu.classList.remove('show'); }); } }); // Empêcher la fermeture du dropdown quand on clique à l'intérieur document.querySelectorAll('.dropdown-menu').forEach(menu => { menu.addEventListener('click', function(e) { e.stopPropagation(); }); }); // Action pour quitter un dossier partagé document.querySelectorAll('.leave-shared-folder-btn').forEach(btn => { btn.addEventListener('click', function(e) { e.preventDefault(); const folderName = this.getAttribute('data-folder-name'); const folderOwner = this.getAttribute('data-folder-owner'); Swal.fire({ title: 'Quitter ce dossier partagé ?', text: 'Vous ne verrez plus ce dossier dans votre dashboard.', icon: 'warning', showCancelButton: true, confirmButtonText: 'Quitter', cancelButtonText: 'Annuler' }).then((result) => { if (result.isConfirmed) { fetch(`/api/dpanel/sharedfolders/leave`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ folderName, folderOwner }) }) .then(res => res.json()) .then(data => { if (data.success) { showToast('success', 'Dossier quitté'); setTimeout(() => location.reload(), 800); } else { showToast('error', data.error || 'Erreur'); } }) .catch(() => showToast('error', 'Erreur réseau')); } }); }); }); } // =================== VUE GRILLE =================== function initializeGridView() { const tableBody = document.querySelector('#fileTable tbody'); const searchContainer = document.querySelector('.flex.justify-between.items-center'); if (!tableBody || !searchContainer) return; // Créer le bouton de basculement const viewToggleBtn = document.createElement('button'); viewToggleBtn.className = 'btn btn-secondary ml-2 view-toggle-btn'; viewToggleBtn.innerHTML = ''; viewToggleBtn.title = 'Basculer vers la vue grille'; searchContainer.appendChild(viewToggleBtn); let isGridView = localStorage.getItem('isGridView') === 'true'; function buildGridLayout(tr) { if (!tr) return; const firstCell = tr.querySelector('td:first-child'); if (!firstCell) return; const nameDiv = firstCell.querySelector('div'); if (!nameDiv) return; const name = nameDiv.textContent.trim(); const icon = firstCell.querySelector('i')?.cloneNode(true); const type = tr.querySelector('td:nth-child(2)')?.textContent.trim() || ''; const size = tr.querySelector('td:nth-child(4)')?.textContent.trim() || ''; const collab = firstCell.querySelector('.collaboration-badge'); if (!icon) return; // Créer le conteneur principal const content = document.createElement('div'); content.className = 'icon-container'; // Créer l'icône avec arrière-plan stylé const iconWrapper = document.createElement('div'); iconWrapper.className = 'icon'; iconWrapper.appendChild(icon); // Créer le label const label = document.createElement('div'); label.className = 'label'; label.textContent = name; // Créer les détails const details = document.createElement('div'); details.className = 'details'; details.textContent = size !== '-' ? `${type} • ${size}` : type; // Assembler le conteneur content.appendChild(iconWrapper); content.appendChild(label); content.appendChild(details); // Nettoyer et remplacer le contenu firstCell.innerHTML = ''; firstCell.appendChild(content); // Réajouter le badge de collaboration s'il existe if (collab) { const clonedCollab = collab.cloneNode(true); firstCell.appendChild(clonedCollab); } // Ajouter le dropdown pour la vue grille const dropdown = tr.querySelector('.dropdown'); if (dropdown) { const gridDropdown = dropdown.cloneNode(true); gridDropdown.className = 'grid-dropdown dropdown'; firstCell.appendChild(gridDropdown); } } function toggleView() { const currentIsGrid = tableBody.classList.contains('grid-view'); isGridView = !currentIsGrid; if (isGridView) { // Passer à la vue grille tableBody.classList.add('grid-view'); // Appliquer le layout grille avec un délai pour l'animation setTimeout(() => { document.querySelectorAll('#fileTable tbody tr').forEach((tr, index) => { tr.style.animationDelay = `${index * 0.05}s`; buildGridLayout(tr); }); }, 50); viewToggleBtn.innerHTML = ''; viewToggleBtn.title = 'Basculer vers la vue liste'; } else { // Retourner à la vue liste tableBody.classList.remove('grid-view'); // Rafraîchir pour restaurer la vue liste originale setTimeout(() => location.reload(), 100); } localStorage.setItem('isGridView', isGridView); } viewToggleBtn.addEventListener('click', toggleView); // Initialiser la vue si nécessaire if (isGridView) { tableBody.classList.add('grid-view'); // Appliquer le layout avec animation délayée setTimeout(() => { document.querySelectorAll('#fileTable tbody tr').forEach((tr, index) => { tr.style.animationDelay = `${index * 0.05}s`; buildGridLayout(tr); }); }, 100); viewToggleBtn.innerHTML = ''; viewToggleBtn.title = 'Basculer vers la vue liste'; } } // =================== COLLABORATION =================== function initializeCollaboration() { // WebSocket pour la collaboration en temps réel try { const ws = new WebSocket(`ws://${window.location.host}`); ws.onmessage = function(event) { const data = JSON.parse(event.data); if (data.type === 'collaborationStatus') { updateCollaborationUI(data); } }; ws.onerror = function(error) { console.warn('WebSocket error:', error); }; } catch (error) { console.warn('WebSocket not available:', error); } // Charger le statut initial loadCollaborationStatus(); // Gestionnaires d'événements document.querySelectorAll('.toggle-collaboration-btn').forEach(button => { button.addEventListener('click', function(e) { e.preventDefault(); const itemName = this.getAttribute('data-item-name'); const itemType = this.getAttribute('data-item-type'); const isCollaborative = this.getAttribute('data-is-collaborative') === 'true'; if (isCollaborative) { showCollaborationDetails(itemName, itemType); } else { toggleCollaboration(itemName, itemType, true); } }); }); } async function loadCollaborationStatus() { try { const response = await fetch('/api/dpanel/collaboration/status'); const data = await response.json(); if (data.items) { Object.entries(data.items).forEach(([itemId, status]) => { const [type, name] = itemId.split('-'); updateCollaborationUI({ itemName: name, itemType: type, isCollaborative: status.isCollaborative, activeUsers: status.activeUsers }); }); } } catch (error) { console.error('Error loading collaboration status:', error); } } function toggleCollaboration(itemName, itemType, enable) { const itemLabel = itemType === 'folder' ? 'dossier' : 'fichier'; const performToggle = () => { fetch('/api/dpanel/collaboration/toggle', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ itemName, itemType, enable }) }) .then(response => response.json()) .then(data => { if (data.success) { updateCollaborationUI({ itemName, itemType, isCollaborative: enable, activeUsers: data.activeUsers || [] }); showToast('success', `Collaboration ${enable ? 'activée' : 'désactivée'}`); } else { throw new Error(data.error || 'Erreur inconnue'); } }) .catch(error => { console.error('Error:', error); showToast('error', 'Erreur lors de la modification de la collaboration'); }); }; if (!enable) { performToggle(); } else { Swal.fire({ title: 'Activer la collaboration ?', text: `D'autres utilisateurs pourront accéder à ce ${itemLabel} en temps réel.`, icon: 'question', showCancelButton: true, confirmButtonText: 'Activer', cancelButtonText: 'Annuler' }).then((result) => { if (result.isConfirmed) { performToggle(); } }); } } function showCollaborationDetails(itemName, itemType) { fetch(`/api/dpanel/collaboration/details/${itemType}/${itemName}`) .then(response => response.json()) .then(data => { // Supprimer toute modal existante document.querySelectorAll('.collaboration-modal').forEach(modal => modal.remove()); const modal = createCollaborationModal(itemName, itemType, data); document.body.appendChild(modal); // Afficher la modal avec les classes CSS modernes modal.classList.add('show'); modal.style.display = 'flex'; }) .catch(error => { console.error('Error:', error); showToast('error', 'Impossible de charger les détails de la collaboration'); }); } function createCollaborationModal(itemName, itemType, data) { const modal = document.createElement('div'); modal.className = 'collaboration-modal'; modal.style.display = 'flex'; modal.innerHTML = ` `; // Gestionnaire pour fermer en cliquant à l'extérieur modal.addEventListener('click', (e) => { if (e.target === modal) { modal.remove(); } }); // Ajouter les gestionnaires d'événements const searchInput = modal.querySelector('.search-user-input'); const searchBtn = modal.querySelector('.search-user-btn'); const search = () => searchCollabUser(searchInput.value.trim(), itemName, itemType, modal); searchBtn.addEventListener('click', search); searchInput.addEventListener('keypress', (e) => { if (e.key === 'Enter') search(); }); return modal; } function searchCollabUser(username, itemName, itemType, modal) { if (!username) return; const resultsDiv = modal.querySelector('.search-results'); resultsDiv.innerHTML = `
Recherche en cours...
`; fetch(`/api/dpanel/collaboration/searchuser?username=${encodeURIComponent(username)}`) .then(response => response.json()) .then(result => { if (result.found) { resultsDiv.innerHTML = `
${result.user.name}
${result.user.name}
Utilisateur trouvé
`; } else { resultsDiv.innerHTML = `
Utilisateur non trouvé
`; } }) .catch(error => { console.error('Error:', error); resultsDiv.innerHTML = `
Erreur lors de la recherche
`; }); } function addCollaborator(itemName, itemType, userId) { fetch('/api/dpanel/collaboration/add', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ itemName, itemType, userId }) }) .then(response => response.json()) .then(data => { if (data.success) { showToast('success', 'Collaborateur ajouté'); // Rafraîchir la modal document.querySelector('.collaboration-modal')?.remove(); showCollaborationDetails(itemName, itemType); } else { throw new Error(data.error || 'Erreur lors de l\'ajout'); } }) .catch(error => { console.error('Error:', error); showToast('error', 'Erreur lors de l\'ajout du collaborateur'); }); } function removeCollaborator(itemName, itemType, userId) { fetch('/api/dpanel/collaboration/remove', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ itemName, itemType, userId }) }) .then(response => response.json()) .then(data => { if (data.success) { showToast('success', 'Collaborateur retiré'); // Rafraîchir la modal document.querySelector('.collaboration-modal')?.remove(); showCollaborationDetails(itemName, itemType); } else { throw new Error(data.error || 'Erreur lors du retrait'); } }) .catch(error => { console.error('Error:', error); showToast('error', 'Erreur lors du retrait du collaborateur'); }); } function updateCollaborationUI(data) { const row = document.querySelector(`tr[data-name="${data.itemName}"]`); if (!row) return; const button = row.querySelector('.toggle-collaboration-btn'); const nameCell = row.querySelector('td:first-child'); if (button) { button.setAttribute('data-is-collaborative', data.isCollaborative); button.title = data.isCollaborative ? 'Voir les collaborateurs' : 'Activer la collaboration'; } let badge = nameCell.querySelector('.collaboration-badge'); if (data.isCollaborative) { if (!badge) { badge = document.createElement('span'); badge.className = 'ml-2 badge badge-info collaboration-badge'; badge.innerHTML = ' '; nameCell.querySelector('div').appendChild(badge); } const countSpan = badge.querySelector('.active-users-count'); if (data.activeUsers && data.activeUsers.length > 0) { badge.classList.remove('badge-secondary'); badge.classList.add('badge-info'); countSpan.textContent = ` ${data.activeUsers.length}`; badge.title = `Collaborateurs actifs : ${data.activeUsers.map(u => u.name).join(', ')}`; } else { badge.classList.remove('badge-info'); badge.classList.add('badge-secondary'); countSpan.textContent = ''; badge.title = 'Aucun collaborateur actif'; } } else if (badge) { badge.remove(); } } // =================== GESTIONNAIRES DE FICHIERS =================== function initializeFileHandlers() { // Boutons de suppression document.querySelectorAll('.delete-file-button, .delete-folder-btn').forEach(button => { button.addEventListener('click', function(e) { e.preventDefault(); const fileName = this.dataset.fileName; const folderName = this.dataset.folderName; if (fileName) { confirmDeleteFile(fileName); } else if (folderName) { confirmDeleteFolder(folderName); } }); }); // Boutons de copie document.querySelectorAll('.copy-button').forEach(button => { button.addEventListener('click', function(e) { e.preventDefault(); const fileUrl = this.dataset.fileUrl; copyFileLink(fileUrl); }); }); // Boutons de renommage document.querySelectorAll('.rename-file-btn, .rename-folder-btn').forEach(button => { button.addEventListener('click', function(e) { e.preventDefault(); const fileName = this.dataset.fileName; const folderName = this.dataset.folderName; if (fileName) { renameFile(fileName); } else if (folderName) { renameFolder(folderName); } }); }); // Boutons de déplacement document.querySelectorAll('.move-file-btn').forEach(button => { button.addEventListener('click', function(e) { e.preventDefault(); const fileName = this.dataset.fileName; // Utiliser la modal Bootstrap pour le déplacement showMoveFileBootstrapModal(fileName); }); }); // Gestionnaire pour le bouton confirmMoveFile dans la modal Bootstrap const confirmMoveFileBtn = document.getElementById('confirmMoveFile'); if (confirmMoveFileBtn) { confirmMoveFileBtn.addEventListener('click', function(e) { e.preventDefault(); const fileNameElement = document.getElementById('moveFileName'); const folderSelectElement = document.getElementById('moveFolderSelect'); console.log('Elements found:', { fileNameElement: !!fileNameElement, folderSelectElement: !!folderSelectElement, fileNameElementType: fileNameElement?.tagName, folderSelectElementType: folderSelectElement?.tagName }); if (!fileNameElement) { showToast('error', 'Élément fileName non trouvé'); return; } if (!folderSelectElement) { showToast('error', 'Élément folderSelect non trouvé'); return; } // Récupérer les valeurs brutes const rawFileName = fileNameElement.value; const rawFolderName = folderSelectElement.value; console.log('Raw values:', { rawFileName: rawFileName, rawFolderName: rawFolderName, rawFileNameType: typeof rawFileName, rawFolderNameType: typeof rawFolderName, fileNameIsString: typeof rawFileName === 'string', folderNameIsString: typeof rawFolderName === 'string' }); // Convertir explicitement en string et nettoyer // Utiliser une méthode plus robuste de conversion let fileName, folderName; try { fileName = (rawFileName + '').trim(); // Force conversion to string folderName = (rawFolderName + '').trim(); } catch (e) { console.error('Error converting values:', e); fileName = String(rawFileName || '').trim(); folderName = String(rawFolderName || '').trim(); } console.log('Processed values:', { fileName: fileName, folderName: folderName, fileNameType: typeof fileName, folderNameType: typeof folderName, fileNameLength: fileName.length, folderNameLength: folderName.length, fileNameConstructor: fileName.constructor.name, folderNameConstructor: folderName.constructor.name }); if (!fileName || fileName === '') { showToast('error', 'Aucun fichier sélectionné'); return; } if (!folderName || folderName === '') { showToast('error', 'Veuillez sélectionner un dossier'); return; } // Fermer la modal $('#moveFileModal').modal('hide'); // Appeler la fonction moveFile console.log('Calling moveFile with:', { fileName, folderName }); moveFile(fileName, folderName); }); } } // =================== FONCTIONS UTILITAIRES =================== function formatFileSize(fileSizeInBytes) { if (!fileSizeInBytes || fileSizeInBytes === 0) return '0 octets'; if (fileSizeInBytes < 1024) return fileSizeInBytes + ' octets'; else if (fileSizeInBytes < 1048576) return (fileSizeInBytes / 1024).toFixed(2) + ' Ko'; else if (fileSizeInBytes < 1073741824) return (fileSizeInBytes / 1048576).toFixed(2) + ' Mo'; else return (fileSizeInBytes / 1073741824).toFixed(2) + ' Go'; } function getDefaultAvatar(username) { const encodedName = encodeURIComponent(username); return `https://api.dicebear.com/7.x/initials/svg?seed=${encodedName}&background=%234e54c8&radius=50`; } function showToast(type, message) { Swal.fire({ position: 'top-end', icon: type, title: message, showConfirmButton: false, timer: 1500, toast: true }); } function searchFiles() { const input = document.getElementById('searchInput'); const filter = input.value.toUpperCase(); const table = document.getElementById('fileTable'); const tr = table.getElementsByTagName('tr'); for (let i = 1; i < tr.length; i++) { const td = tr[i].getElementsByTagName('td')[0]; if (td) { const txtValue = td.textContent || td.innerText; if (txtValue.toUpperCase().indexOf(filter) > -1) { tr[i].style.display = ""; } else { tr[i].style.display = "none"; } } } } // =================== MODALES ET ACTIONS =================== function showNewFolderModal() { Swal.fire({ title: 'Nouveau dossier', input: 'text', inputPlaceholder: 'Entrer le nom du nouveau dossier', confirmButtonText: 'Créer', showCancelButton: true, cancelButtonText: 'Annuler', preConfirm: (folderName) => { if (!folderName) { Swal.showValidationMessage('Le nom du dossier ne peut pas être vide.'); } return folderName; } }).then(result => { if (result.isConfirmed) { createNewFolder(result.value); } }); } function createNewFolder(folderName) { fetch('/api/dpanel/dashboard/newfolder', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ folderName }) }) .then(async response => { const result = await response.json(); if (response.ok && (result.success || result.message === 'Folder created successfully.')) { showToast('success', 'Dossier créé avec succès'); setTimeout(() => location.reload(), 1500); } else { throw new Error(result.message || 'Erreur lors de la création'); } }) .catch(error => { console.error('Error:', error); showToast('error', error.message || 'Erreur lors de la création du dossier'); }); } function confirmDeleteFolder(folderName) { Swal.fire({ title: 'Êtes-vous sûr?', text: `La suppression du dossier "${folderName}" est irréversible!`, icon: 'warning', showCancelButton: true, confirmButtonColor: '#d33', cancelButtonColor: '#3085d6', confirmButtonText: 'Supprimer', cancelButtonText: 'Annuler', }).then((result) => { if (result.isConfirmed) { deleteFolder(folderName); } }); } function deleteFolder(folderName) { fetch(`/api/dpanel/dashboard/deletefolder/${folderName}`, { method: 'DELETE' }) .then(response => { if (response.ok) { showToast('success', 'Dossier supprimé avec succès'); setTimeout(() => location.reload(), 1500); } else { throw new Error('La suppression du dossier a échoué'); } }) .catch(error => { console.error('Error:', error); showToast('error', 'Erreur lors de la suppression du dossier'); }); } function confirmDeleteFile(filename) { Swal.fire({ title: 'Êtes-vous sûr de vouloir supprimer ce fichier?', text: 'Cette action est irréversible!', icon: 'warning', showCancelButton: true, confirmButtonColor: '#d33', cancelButtonColor: '#3085d6', confirmButtonText: 'Supprimer' }).then((result) => { if (result.isConfirmed) { deleteFile(filename); } }); } function deleteFile(filename) { fetch('/api/dpanel/dashboard/delete', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ filename: filename }) }) .then(response => { if (response.ok) { showToast('success', 'Fichier supprimé avec succès'); setTimeout(() => location.reload(), 1500); } else { throw new Error('La suppression du fichier a échoué'); } }) .catch(error => { console.error('Error:', error); showToast('error', 'Erreur lors de la suppression du fichier'); }); } function copyFileLink(fileUrl) { navigator.clipboard.writeText(fileUrl).then(() => { showToast('success', 'Lien copié !'); }, (err) => { console.error('Erreur lors de la copie: ', err); showToast('error', 'Erreur lors de la copie'); }); } function renameFile(currentName) { Swal.fire({ title: 'Renommer le fichier', input: 'text', inputValue: currentName, inputPlaceholder: 'Nouveau nom', showCancelButton: true, confirmButtonText: 'Renommer', cancelButtonText: 'Annuler', inputValidator: (value) => { if (!value) { return 'Vous devez entrer un nom de fichier'; } } }).then((result) => { if (result.isConfirmed) { const newName = result.value; fetch(`/api/dpanel/dashboard/rename/`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ currentName: currentName, newName: newName }) }) .then(response => response.json()) .then(data => { if (data.success || response.ok) { showToast('success', 'Fichier renommé avec succès'); setTimeout(() => location.reload(), 1500); } else { throw new Error(data.message || 'Erreur lors du renommage'); } }) .catch(error => { console.error('Error:', error); showToast('error', 'Erreur lors du renommage du fichier'); }); } }); } function renameFolder(currentName) { Swal.fire({ title: 'Renommer le dossier', input: 'text', inputValue: currentName, showCancelButton: true, confirmButtonText: 'Renommer', cancelButtonText: 'Annuler', inputValidator: (value) => { if (!value) { return 'Veuillez entrer un nom de dossier'; } } }).then((result) => { if (result.isConfirmed) { const newName = result.value; fetch('/api/dpanel/folders/rename', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ oldName: currentName, newName: newName }) }) .then(response => response.json()) .then(data => { if (data.success) { showToast('success', 'Dossier renommé avec succès'); setTimeout(() => location.reload(), 1500); } else { throw new Error(data.message || 'Erreur lors du renommage'); } }) .catch(error => { console.error('Error:', error); showToast('error', 'Erreur lors du renommage du dossier'); }); } }); } function showMoveFileModal(fileName) { const folders = Array.from(document.querySelectorAll('tr[data-type="folder"]')) .map(folderRow => ({ name: folderRow.dataset.name, value: folderRow.dataset.name })); Swal.fire({ title: 'Déplacer le fichier', html: ` `, showCancelButton: true, confirmButtonText: 'Déplacer', cancelButtonText: 'Annuler', preConfirm: () => { const select = document.getElementById('moveFolderSelect'); const folderName = select.value; if (!folderName) { Swal.showValidationMessage('Veuillez sélectionner un dossier'); return false; } return folderName; } }).then((result) => { if (result.isConfirmed && result.value) { moveFile(fileName, result.value); } }); } // Fonction alternative utilisant la modal Bootstrap function showMoveFileBootstrapModal(fileName) { // S'assurer que fileName est une chaîne de caractères const fileNameStr = String(fileName).trim(); console.log('showMoveFileBootstrapModal called with:', { original: fileName, converted: fileNameStr, type: typeof fileName }); if (!fileNameStr) { showToast('error', 'Nom de fichier invalide'); return; } // Définir le nom du fichier dans le champ caché const moveFileNameElement = document.getElementById('moveFileName'); if (moveFileNameElement) { moveFileNameElement.value = fileNameStr; console.log('Set moveFileName value to:', moveFileNameElement.value); } else { console.error('Element moveFileName not found'); showToast('error', 'Erreur: Élément de formulaire manquant'); return; } // Afficher la modal Bootstrap $('#moveFileModal').modal('show'); } function moveFile(fileName, folderName) { // S'assurer que les paramètres sont des chaînes de caractères const fileNameStr = String(fileName).trim(); const folderNameStr = String(folderName).trim(); console.log('moveFile called with:', { original: { fileName, folderName }, converted: { fileNameStr, folderNameStr }, types: { originalFileName: typeof fileName, originalFolderName: typeof folderName, convertedFileName: typeof fileNameStr, convertedFolderName: typeof folderNameStr } }); if (!fileNameStr) { showToast('error', 'Nom de fichier invalide'); return; } if (!folderNameStr) { showToast('error', 'Nom de dossier invalide'); return; } // Créer explicitement l'objet à envoyer const requestBody = {}; requestBody.fileName = fileNameStr; requestBody.folderName = folderNameStr; // Vérification finale console.log('Request body before sending:', { requestBody: requestBody, stringify: JSON.stringify(requestBody), fileNameInBody: requestBody.fileName, folderNameInBody: requestBody.folderName, fileNameType: typeof requestBody.fileName, folderNameType: typeof requestBody.folderName, fileNameConstructor: requestBody.fileName.constructor.name, folderNameConstructor: requestBody.folderName.constructor.name }); const jsonPayload = JSON.stringify(requestBody); console.log('JSON payload:', jsonPayload); fetch('/api/dpanel/dashboard/movefile', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: jsonPayload }) .then(response => response.json()) .then(data => { if (data.message === "File moved successfully" || data.success) { showToast('success', 'Fichier déplacé avec succès'); setTimeout(() => location.reload(), 1500); } else { throw new Error(data.error || 'Une erreur est survenue'); } }) .catch(error => { console.error('Error:', error); showToast('error', 'Erreur lors du déplacement du fichier'); }); } // =================== THÈME =================== function initTheme() { const body = document.body; const savedTheme = localStorage.getItem('theme'); if (savedTheme) { setTheme(savedTheme); } else if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) { setTheme('dark'); } } function setTheme(theme) { const body = document.body; if (theme === 'dark') { body.classList.add('dark'); } else { body.classList.remove('dark'); } localStorage.setItem('theme', theme); } function toggleDarkMode() { const body = document.body; if (body.classList.contains('dark')) { setTheme('light'); } else { setTheme('dark'); } } // =================== VERSION =================== function loadVersion() { fetch('/build-metadata') .then(response => response.json()) .then(data => { const versionElement = document.getElementById('version-number'); if (versionElement) { versionElement.textContent = data.build_version; } }) .catch(error => { console.error('Error fetching version:', error); const versionElement = document.getElementById('version-number'); if (versionElement) { versionElement.textContent = 'Version indisponible'; } }); } // =================== METADATA =================== function displayMetadata() { fetch('/build-metadata') .then(response => response.json()) .then(metadata => { document.getElementById('buildVersion').textContent = metadata.build_version; document.getElementById('nodeVersion').textContent = metadata.node_version; document.getElementById('expressVersion').textContent = metadata.express_version; document.getElementById('buildSha').textContent = metadata.build_sha; document.getElementById('osType').textContent = metadata.os_type; document.getElementById('osRelease').textContent = metadata.os_release; // Utiliser Bootstrap modal si disponible, sinon créer une modal simple const modalElement = document.getElementById('metadataModal'); if (modalElement && typeof $ !== 'undefined' && $.fn.modal) { $('#metadataModal').modal('show'); } }) .catch(error => { console.error('Failed to fetch metadata:', error); showToast('error', 'Impossible de récupérer les métadonnées'); }); } // Expose les fonctions globalement si nécessaire window.displayMetadata = displayMetadata; window.addCollaborator = addCollaborator; window.removeCollaborator = removeCollaborator; window.toggleCollaboration = toggleCollaboration; // =================== FONCTIONS COLLABORATIVES =================== function leaveSharedFolder(folderName, element) { Swal.fire({ title: 'Quitter le dossier partagé', text: `Êtes-vous sûr de vouloir quitter le dossier "${folderName}" ? Vous perdrez l'accès à ce dossier et ses fichiers.`, icon: 'warning', showCancelButton: true, confirmButtonColor: '#f59e0b', cancelButtonColor: '#6b7280', confirmButtonText: 'Oui, quitter', cancelButtonText: 'Annuler' }).then((result) => { if (result.isConfirmed) { // Extraire le propriétaire depuis l'URL du dossier partagé const url = element.dataset.url; const urlParts = url.split('/'); const owner = urlParts[urlParts.indexOf('shared') + 1]; fetch('/api/dpanel/sharedfolders/leave', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ folderName: folderName, folderOwner: owner }) }) .then(response => response.json()) .then(data => { if (data.success) { showToast('success', 'Vous avez quitté le dossier partagé'); // Supprimer la ligne du tableau element.remove(); } else { showToast('error', data.error || 'Erreur lors de la sortie du dossier'); } }) .catch(error => { console.error('Error:', error); showToast('error', 'Erreur de connexion'); }); } }); }