diff --git a/static/css/room.css b/static/css/room.css index dd9a641..214ef15 100644 --- a/static/css/room.css +++ b/static/css/room.css @@ -304,7 +304,7 @@ } .progress-bar { - background-color: #16767b !important; - color: #fff !important; + background-color: var(--primary-color) !important; + color: white !important; transition: background-color 0.2s; } \ No newline at end of file diff --git a/static/js/rooms/fileManager.js b/static/js/rooms/fileManager.js index 95fc7ce..3aedd8a 100644 --- a/static/js/rooms/fileManager.js +++ b/static/js/rooms/fileManager.js @@ -227,6 +227,16 @@ export class FileManager { return; } + // Filter out folders and get only file IDs + const fileIds = selectedItems + .filter(item => item.type !== 'folder') + .map(item => item.id); + + if (fileIds.length === 0) { + console.log('[FileManager] No files to download (only folders selected)'); + return; + } + try { const response = await fetch(`/api/rooms/${this.roomManager.roomId}/files/download`, { method: 'POST', @@ -234,7 +244,7 @@ export class FileManager { 'Content-Type': 'application/json', 'X-CSRF-Token': document.querySelector('meta[name="csrf-token"]').getAttribute('content') }, - body: JSON.stringify({ file_ids: selectedItems.map(item => item.id) }) + body: JSON.stringify({ file_ids: fileIds }) }); console.log('[FileManager] Download response status:', response.status); @@ -256,6 +266,7 @@ export class FileManager { console.log('[FileManager] Download initiated'); } catch (error) { console.error('[FileManager] Error downloading files:', error); + document.getElementById('fileError').textContent = 'Failed to download files. Please try again.'; throw error; } } @@ -322,14 +333,63 @@ export class FileManager { } } + updateSelection(index, event) { + console.log('[FileManager] Updating selection:', { index, event }); + + // Prevent selection if clicking on a checkbox or action button + if (event.target.classList.contains('select-item-checkbox') || event.target.closest('.file-action-btn')) { + return; + } + + const checkboxes = document.querySelectorAll('.select-item-checkbox'); + const checkbox = checkboxes[index]; + if (!checkbox) return; + + if (event.ctrlKey) { + // CTRL + Click: Toggle individual selection + checkbox.checked = !checkbox.checked; + if (checkbox.checked) { + this.selectedItems.add(index); + } else { + this.selectedItems.delete(index); + } + } else if (event.shiftKey && this.lastSelectedIndex !== -1) { + // SHIFT + Click: Select range + const start = Math.min(this.lastSelectedIndex, index); + const end = Math.max(this.lastSelectedIndex, index); + for (let i = start; i <= end; i++) { + checkboxes[i].checked = true; + this.selectedItems.add(i); + } + } else { + // Normal click: Select single item + const wasChecked = checkbox.checked; + checkboxes.forEach(cb => { + cb.checked = false; + this.selectedItems.delete(parseInt(cb.dataset.index)); + }); + checkbox.checked = !wasChecked; + if (!wasChecked) { + this.selectedItems.add(index); + } + } + + this.lastSelectedIndex = index; + this.roomManager.viewManager.updateMultiSelectUI(); + } + getSelectedItems() { console.log('[FileManager] Getting selected items'); return Array.from(this.selectedItems).map(index => this.currentFiles[index]); } - updateSelection(index, event) { - console.log('[FileManager] Updating selection:', { index, event }); - // Implementation of selection logic + clearSelection() { + console.log('[FileManager] Clearing selection'); + this.selectedItems.clear(); + this.lastSelectedIndex = -1; + const checkboxes = document.querySelectorAll('.select-item-checkbox'); + checkboxes.forEach(cb => cb.checked = false); + this.roomManager.viewManager.updateMultiSelectUI(); } navigateToParent() { diff --git a/static/js/rooms/modalManager.js b/static/js/rooms/modalManager.js index 3902aad..428a640 100644 --- a/static/js/rooms/modalManager.js +++ b/static/js/rooms/modalManager.js @@ -65,6 +65,53 @@ export class ModalManager { this.deleteModal.show(); } + showBatchDeleteModal() { + const selectedCheckboxes = document.querySelectorAll('.select-item-checkbox:checked'); + if (selectedCheckboxes.length === 0) return; + + const selectedItems = Array.from(selectedCheckboxes).map(cb => { + const idx = parseInt(cb.dataset.index); + console.log('[ModalManager] Processing checkbox with idx:', idx); + console.log('[ModalManager] Current files:', this.roomManager.fileManager.currentFiles); + + if (isNaN(idx) || idx < 0 || idx >= this.roomManager.fileManager.currentFiles.length) { + console.error('[ModalManager] Invalid index:', idx); + return null; + } + + const file = this.roomManager.fileManager.currentFiles[idx]; + if (!file) { + console.error('[ModalManager] No file found at index:', idx); + return null; + } + + console.log('[ModalManager] Found file:', file); + return { + name: file.name, + path: file.path || '', + type: file.type + }; + }).filter(item => item !== null); // Remove any null items + + if (selectedItems.length === 0) { + console.error('[ModalManager] No valid items selected for deletion'); + return; + } + + // Update modal content + const fileNameEl = document.getElementById('deleteFileName'); + const labelEl = document.getElementById('deleteConfirmLabel'); + + if (fileNameEl) fileNameEl.textContent = `${selectedItems.length} item${selectedItems.length > 1 ? 's' : ''}`; + if (labelEl) labelEl.textContent = 'Move to Trash'; + + // Store the items to delete in the FileManager + this.roomManager.fileManager.batchDeleteItems = selectedItems; + + // Show the modal + this.deleteModal.show(); + } + showRenameModal(filename) { document.getElementById('renameError').textContent = ''; const ext = filename.includes('.') ? filename.substring(filename.lastIndexOf('.')) : ''; diff --git a/static/js/rooms/room.js b/static/js/rooms/room.js index 27410e7..b92263c 100644 --- a/static/js/rooms/room.js +++ b/static/js/rooms/room.js @@ -74,7 +74,46 @@ class RoomManager { initializeEventListeners() { console.log('[RoomManager] Setting up event listeners'); - // Add any global event listeners here + + // Add event listener for select all checkbox + const selectAllCheckbox = document.querySelector('.select-all-checkbox'); + if (selectAllCheckbox) { + selectAllCheckbox.addEventListener('change', (event) => { + const checkboxes = document.querySelectorAll('.select-item-checkbox'); + checkboxes.forEach((cb, index) => { + cb.checked = event.target.checked; + if (event.target.checked) { + this.fileManager.selectedItems.add(index); + } else { + this.fileManager.selectedItems.delete(index); + } + }); + this.viewManager.updateMultiSelectUI(); + }); + } + + // Add event listener for download selected button + const downloadSelectedBtn = document.getElementById('downloadSelectedBtn'); + if (downloadSelectedBtn && this.canDownload) { + downloadSelectedBtn.addEventListener('click', () => { + this.fileManager.downloadSelected(); + }); + } + + // Add event listener for delete selected button + const deleteSelectedBtn = document.getElementById('deleteSelectedBtn'); + if (deleteSelectedBtn && this.canDelete) { + deleteSelectedBtn.addEventListener('click', () => { + this.modalManager.showBatchDeleteModal(); + }); + } + + // Add event listener for clicking outside to clear selection + document.addEventListener('click', (event) => { + if (!event.target.closest('.file-card') && !event.target.closest('.file-row') && !event.target.closest('.file-action-btn')) { + this.fileManager.clearSelection(); + } + }); } } diff --git a/static/js/rooms/uploadManager.js b/static/js/rooms/uploadManager.js index 1b42cbf..6d79db6 100644 --- a/static/js/rooms/uploadManager.js +++ b/static/js/rooms/uploadManager.js @@ -74,8 +74,7 @@ export class UploadManager { async startUpload(files) { this.uploadProgressContainer.style.display = 'block'; this.uploadProgressBar.style.width = '0%'; - this.uploadProgressBar.classList.remove('bg-success'); - this.uploadProgressBar.classList.add('bg-info'); + this.uploadProgressBar.className = 'progress-bar bg-primary-opacity-15 text-primary'; this.uploadProgressText.textContent = ''; this.pendingUploads = files; @@ -109,8 +108,7 @@ export class UploadManager { // All files processed this.uploadProgressBar.style.width = '100%'; this.uploadProgressBar.textContent = '100%'; - this.uploadProgressBar.classList.remove('bg-info'); - this.uploadProgressBar.classList.add('bg-success'); + this.uploadProgressBar.className = 'progress-bar bg-success-opacity-15 text-success'; this.uploadProgressText.textContent = 'Upload complete!'; // Reset state @@ -123,8 +121,7 @@ export class UploadManager { setTimeout(() => { this.uploadProgressContainer.style.display = 'none'; this.uploadProgressText.textContent = ''; - this.uploadProgressBar.style.backgroundColor = '#16767b'; - this.uploadProgressBar.style.color = '#fff'; + this.uploadProgressBar.className = 'progress-bar bg-primary-opacity-15 text-primary'; }, 3000); // Refresh file list @@ -175,8 +172,7 @@ export class UploadManager { } catch (error) { console.error('Upload error:', error); this.uploadProgressText.textContent = `Error uploading ${file.name}`; - this.uploadProgressBar.style.backgroundColor = '#ef4444'; - this.uploadProgressBar.style.color = '#fff'; + this.uploadProgressBar.className = 'progress-bar bg-danger-opacity-15 text-danger'; currentFileIndex++; updateProgress(); await processNextFile(); @@ -226,8 +222,7 @@ export class UploadManager { } uploadError.style.display = 'block'; - this.uploadProgressBar.style.backgroundColor = '#ef4444'; - this.uploadProgressBar.style.color = '#fff'; + this.uploadProgressBar.className = 'progress-bar bg-danger-opacity-15 text-danger'; } async handleFileExists(file, formData) { diff --git a/static/js/rooms/viewManager.js b/static/js/rooms/viewManager.js index b25f204..ae41aae 100644 --- a/static/js/rooms/viewManager.js +++ b/static/js/rooms/viewManager.js @@ -127,7 +127,9 @@ export class ViewManager { - + @@ -151,7 +153,14 @@ export class ViewManager { async renderGridView(files) { console.log('[ViewManager] Rendering grid view'); const fileGrid = document.getElementById('fileGrid'); - let html = ''; + let html = ` +
+
+ + Select All +
+
+ `; files.forEach((file, index) => { console.log('[ViewManager] Rendering grid item:', file); @@ -170,10 +179,11 @@ export class ViewManager { const modified = new Date(file.modified).toLocaleString(); return ` - +
+ + Name Size
+ data-index="${index}" style="margin: 0;" + onclick="event.stopPropagation(); window.roomManager.fileManager.updateSelection(${index}, event)"> @@ -201,8 +211,13 @@ export class ViewManager { return `
-
+
+
+ +