/** * Marketing File Management System * Professional drag-and-drop upload with folder management and tagging */ class MarketingFileManager { constructor() { this.currentFolder = ''; this.selectedFiles = []; this.uploadQueue = []; this.viewMode = 'grid'; this.filters = { search: '', tag: '', fileTypes: [] }; this.folders = []; // Store folders data this.loadRequestId = 0; // Track the latest load request // Get permissions from PHP this.permissions = window.marketingPermissions || { canCreate: 0, canUpdate: 0, canDelete: 0 }; this.init(); } init() { this.bindEvents(); this.loadFolders(); this.loadTags(); this.loadFiles(); this.setupDragAndDrop(); } bindEvents() { // Upload modal document.getElementById('uploadBtn')?.addEventListener('click', () => { this.showUploadModal(); }); // Create folder modal document.getElementById('createFolderBtn')?.addEventListener('click', () => { this.showFolderModal(); }); // View mode toggle document.getElementById('gridViewBtn')?.addEventListener('click', () => { this.setViewMode('grid'); }); document.getElementById('listViewBtn')?.addEventListener('click', () => { this.setViewMode('list'); }); // Search document.getElementById('searchInput')?.addEventListener('input', (e) => { this.filters.search = e.target.value; this.debounce(this.loadFiles.bind(this), 300)(); }); // Tag filter document.getElementById('tagFilter')?.addEventListener('change', (e) => { this.filters.tag = e.target.value; this.loadFiles(); }); // File type filters document.querySelectorAll('.file-type-filters input[type="checkbox"]').forEach(checkbox => { checkbox.addEventListener('change', () => { this.updateFileTypeFilters(); }); }); // Modal events this.bindModalEvents(); // Upload events this.bindUploadEvents(); } bindModalEvents() { // Close modals document.querySelectorAll('.modal-close, .modal-cancel').forEach(btn => { btn.addEventListener('click', (e) => { this.closeModal(e.target.closest('.modal')); }); }); // Create folder document.getElementById('createFolder')?.addEventListener('click', () => { this.createFolder(); }); // Download file document.getElementById('downloadFile')?.addEventListener('click', () => { if (this.selectedFile) { this.downloadFile(this.selectedFile); } }); // Delete file document.getElementById('deleteFile')?.addEventListener('click', () => { if (this.selectedFile) { this.deleteFile(this.selectedFile); } }); // Save edit document.getElementById('saveEdit')?.addEventListener('click', () => { this.saveEdit(); }); // Edit folder document.getElementById('saveEditFolder')?.addEventListener('click', () => { this.saveEditFolder(); }); // Delete folder document.getElementById('deleteFolder')?.addEventListener('click', () => { if (this.selectedFolder) { this.deleteFolder(this.selectedFolder); } }); } bindUploadEvents() { const fileInput = document.getElementById('fileInput'); const browseBtn = document.getElementById('browseBtn'); const startUpload = document.getElementById('startUpload'); browseBtn?.addEventListener('click', () => { fileInput.click(); }); fileInput?.addEventListener('change', (e) => { this.handleFileSelect(e.target.files); }); startUpload?.addEventListener('click', () => { this.startUpload(); }); } setupDragAndDrop() { const uploadArea = document.getElementById('uploadArea'); const filesContainer = document.getElementById('filesContainer'); if (uploadArea) { uploadArea.addEventListener('dragover', this.handleDragOver); uploadArea.addEventListener('drop', (e) => this.handleDrop(e)); } if (filesContainer) { filesContainer.addEventListener('dragover', this.handleDragOver); filesContainer.addEventListener('drop', (e) => this.handleDrop(e)); } } handleDragOver(e) { e.preventDefault(); e.stopPropagation(); e.currentTarget.classList.add('drag-over'); } handleDrop(e) { e.preventDefault(); e.stopPropagation(); e.currentTarget.classList.remove('drag-over'); const files = e.dataTransfer.files; if (files.length > 0) { this.showUploadModal(); this.handleFileSelect(files); } } async loadFolders() { try { const response = await fetch('index.php?page=marketing&action=marketing_folders&tree=true', { cache: 'no-store' }); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } const text = await response.text(); if (!text || text.trim() === '') { console.warn('Empty response from folders API'); this.folders = []; this.renderFolderTree([]); this.populateFolderSelects([]); return; } const data = JSON.parse(text); this.folders = data || []; // Store the folders data // Always render the folder tree (at minimum shows Root) this.renderFolderTree(this.folders); this.populateFolderSelects(this.folders); } catch (error) { console.error('Error loading folders:', error); this.folders = []; // Show at least root folder on error this.renderFolderTree([]); this.populateFolderSelects([]); } } async loadTags() { try { const response = await fetch('index.php?page=marketing&action=marketing_tags&used_only=true', { cache: 'no-store' }); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } const text = await response.text(); if (!text || text.trim() === '') { console.warn('Empty response from tags API'); this.populateTagFilter([]); return; } const data = JSON.parse(text); // Always populate tag filter (at minimum shows "All Tags") this.populateTagFilter(data || []); } catch (error) { console.error('Error loading tags:', error); // Show empty tag filter on error this.populateTagFilter([]); } } async loadFiles() { const container = document.getElementById('filesContainer'); const loading = document.getElementById('loadingIndicator'); const emptyState = document.getElementById('emptyState'); // Increment request ID to invalidate previous requests const requestId = ++this.loadRequestId; // Clear container FIRST to prevent showing old files container.innerHTML = ''; loading.style.display = 'block'; emptyState.style.display = 'none'; try { // Add cache busting to prevent browser caching let url = `index.php?page=marketing&action=marketing_files&limit=50&_t=${Date.now()}`; // Only filter by folder if no tag filter is active (tag search is across all folders) if (!this.filters.tag) { const folderId = this.currentFolder ? this.currentFolder : 'null'; url += `&folder_id=${folderId}`; } if (this.filters.search) { url += `&search=${encodeURIComponent(this.filters.search)}`; } if (this.filters.tag) { url += `&tag=${encodeURIComponent(this.filters.tag)}`; } if (this.filters.fileTypes.length > 0) { // API expects individual file_type parameter, so we'll filter client-side for now } const response = await fetch(url, { cache: 'no-store' }); // Ignore response if a newer request was made if (requestId !== this.loadRequestId) { return; } if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } const text = await response.text(); if (!text || text.trim() === '') { console.warn('Empty response from files API'); emptyState.style.display = 'block'; return; } const data = JSON.parse(text); if (data && data.length > 0) { let files = data; // Client-side file type filtering if (this.filters.fileTypes.length > 0) { files = files.filter(file => this.filters.fileTypes.includes(file.file_type.toLowerCase()) ); } if (files.length === 0) { // No files after filtering, check for subfolders const subfolders = this.getSubfolders(this.currentFolder); if (subfolders.length > 0) { this.renderFolderTiles(subfolders); } else { emptyState.style.display = 'block'; } } else { this.renderFiles(files); } } else { // No files, check for subfolders const subfolders = this.getSubfolders(this.currentFolder); if (subfolders.length > 0) { this.renderFolderTiles(subfolders); } else { emptyState.style.display = 'block'; } } } catch (error) { console.error('Error loading files:', error); this.showToast('Error loading files', 'error'); } finally { loading.style.display = 'none'; } } renderFolderTree(folders, container = null, level = 0) { if (!container) { container = document.getElementById('folderTree'); container.innerHTML = '
Root
'; // Add click listener to root folder const rootFolder = container.querySelector('.folder-item.root'); if (rootFolder) { rootFolder.addEventListener('click', () => { this.selectFolder(''); }); } } folders.forEach(folder => { const folderItem = document.createElement('div'); folderItem.className = 'folder-item'; folderItem.setAttribute('data-folder', folder.id); folderItem.style.marginLeft = `${level * 20}px`; const hasChildren = folder.children && folder.children.length > 0; const expandIcon = hasChildren ? '' : ''; // Only show edit button if user has update permission const editButton = this.permissions.canUpdate === 1 ? `` : ''; folderItem.innerHTML = ` ${expandIcon} ${this.escapeHtml(folder.folder_name)} (${folder.file_count}) ${editButton} `; folderItem.addEventListener('click', (e) => { // Don't select folder if edit button was clicked if (e.target.closest('.folder-edit-btn')) { e.stopPropagation(); this.editFolder(folder); } else { this.selectFolder(folder.id); } }); container.appendChild(folderItem); if (hasChildren) { this.renderFolderTree(folder.children, container, level + 1); } }); } renderFiles(files) { const container = document.getElementById('filesContainer'); container.innerHTML = ''; files.forEach(file => { const fileElement = this.createFileElement(file); container.appendChild(fileElement); }); } getSubfolders(folderId) { // Find immediate children of the specified folder if (!folderId || folderId === '') { // Root folder - return top-level folders return this.folders; } // Recursively search for the folder and return its children const findFolder = (folders, targetId) => { for (const folder of folders) { if (folder.id === targetId) { return folder.children || []; } if (folder.children && folder.children.length > 0) { const found = findFolder(folder.children, targetId); if (found) return found; } } return []; }; return findFolder(this.folders, folderId); } renderFolderTiles(subfolders) { const container = document.getElementById('filesContainer'); container.innerHTML = ''; subfolders.forEach(folder => { const folderElement = this.createFolderTileElement(folder); container.appendChild(folderElement); }); } createFolderTileElement(folder) { const folderElement = document.createElement('div'); folderElement.className = `folder-tile ${this.viewMode}-item`; folderElement.setAttribute('data-folder-id', folder.id); folderElement.innerHTML = `
${this.escapeHtml(folder.folder_name)}
${folder.file_count || 0} files
`; // Click to navigate to folder folderElement.addEventListener('click', () => { this.selectFolder(folder.id); }); return folderElement; } createFileElement(file) { const fileElement = document.createElement('div'); fileElement.className = `file-item ${this.viewMode}-item`; fileElement.setAttribute('data-file-id', file.id); const thumbnail = this.getThumbnail(file); const tags = file.tags.map(tag => `${this.escapeHtml(tag)}`).join(''); fileElement.innerHTML = `
${thumbnail}
${this.escapeHtml(file.title || file.original_filename)}
${file.file_size_formatted} .${file.file_type.toUpperCase()} ${this.formatDate(file.created)}
${tags}
`; // Bind events fileElement.querySelector('.preview-btn').addEventListener('click', () => { this.previewFile(file); }); fileElement.querySelector('.download-btn').addEventListener('click', () => { this.downloadFile(file); }); fileElement.querySelector('.edit-btn').addEventListener('click', () => { this.editFile(file); }); // Make tags clickable to filter by tag fileElement.querySelectorAll('.tag.clickable').forEach(tagElement => { tagElement.addEventListener('click', (e) => { e.stopPropagation(); const tagName = tagElement.getAttribute('data-tag'); this.filterByTag(tagName); }); }); fileElement.addEventListener('dblclick', () => { this.previewFile(file); }); return fileElement; } getThumbnail(file) { const isImage = ['jpg', 'jpeg', 'png', 'gif', 'webp'].includes(file.file_type.toLowerCase()); if (isImage && file.thumbnail_path) { return `${this.escapeHtml(file.title)}`; } // File type icons const iconMap = { pdf: 'fa-file-pdf', doc: 'fa-file-word', docx: 'fa-file-word', xls: 'fa-file-excel', xlsx: 'fa-file-excel', mp4: 'fa-file-video', mov: 'fa-file-video', avi: 'fa-file-video' }; const iconClass = iconMap[file.file_type.toLowerCase()] || 'fa-file'; return `
`; } showUploadModal() { const modal = document.getElementById('uploadModal'); this.showModal(modal); this.populateUploadFolders(this.folders); // Use stored folders data } showFolderModal() { const modal = document.getElementById('folderModal'); this.showModal(modal); this.populateParentFolders(this.folders); // Use stored folders data } showModal(modal) { modal.style.display = 'flex'; modal.classList.add('show'); document.body.classList.add('modal-open'); } closeModal(modal) { modal.classList.remove('show'); setTimeout(() => { modal.style.display = 'none'; document.body.classList.remove('modal-open'); }, 300); } handleFileSelect(files) { this.uploadQueue = []; Array.from(files).forEach(file => { this.uploadQueue.push({ file: file, progress: 0, status: 'pending' }); }); this.renderUploadQueue(); document.getElementById('startUpload').disabled = this.uploadQueue.length === 0; } renderUploadQueue() { const container = document.getElementById('uploadQueue'); container.innerHTML = ''; this.uploadQueue.forEach((item, index) => { const queueItem = document.createElement('div'); queueItem.className = 'upload-item'; queueItem.innerHTML = `
${this.escapeHtml(item.file.name)}
${this.formatFileSize(item.file.size)}
${item.status}
`; queueItem.querySelector('.remove-btn').addEventListener('click', () => { this.removeFromQueue(index); }); container.appendChild(queueItem); }); } async startUpload() { const folder = document.getElementById('uploadFolder').value; const tags = document.getElementById('uploadTags').value .split(',') .map(tag => tag.trim()) .filter(tag => tag.length > 0); for (let i = 0; i < this.uploadQueue.length; i++) { const item = this.uploadQueue[i]; await this.uploadFile(item, folder, tags, i); } // Switch to the uploaded folder if different from current if (folder && folder !== this.currentFolder) { this.currentFolder = folder; } this.loadFiles(); this.closeModal(document.getElementById('uploadModal')); this.showToast('Files uploaded successfully!', 'success'); } async uploadFile(item, folderId, tags, index) { const formData = new FormData(); formData.append('file', item.file); formData.append('folder_id', folderId); formData.append('tags', JSON.stringify(tags)); formData.append('title', item.file.name.replace(/\.[^/.]+$/, "")); item.status = 'uploading'; this.updateQueueItem(index, item); try { const response = await fetch('index.php?page=marketing&action=marketing_upload', { method: 'POST', body: formData, onUploadProgress: (progressEvent) => { item.progress = Math.round((progressEvent.loaded * 100) / progressEvent.total); this.updateQueueItem(index, item); } }); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } const text = await response.text(); if (!text || text.trim() === '') { throw new Error('Empty response from upload server'); } const result = JSON.parse(text); if (result.success) { item.status = 'completed'; item.progress = 100; } else { throw new Error(result.error || 'Upload failed'); } } catch (error) { item.status = 'error'; item.error = error.message; this.showToast(error.message, 'error'); } this.updateQueueItem(index, item); } async createFolder() { const folderName = document.getElementById('folderName').value.trim(); const parentId = document.getElementById('parentFolder').value; const description = document.getElementById('folderDescription').value.trim(); if (!folderName) { this.showToast('Folder name is required', 'error'); return; } try { const formData = new FormData(); formData.append('folder_name', folderName); formData.append('parent_id', parentId || ''); formData.append('description', description); const response = await fetch('index.php?page=marketing&action=marketing_folders', { method: 'POST', body: formData }); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } const text = await response.text(); if (!text || text.trim() === '') { throw new Error('Empty response from server'); } const data = JSON.parse(text); if (data && (data.success || data.rowID)) { this.closeModal(document.getElementById('folderModal')); this.loadFolders(); this.showToast('Folder created successfully!', 'success'); } else if (data.error) { throw new Error(data.error); } else { throw new Error('Unexpected response format'); } } catch (error) { console.error('Create folder error:', error); this.showToast(error.message || 'Error creating folder', 'error'); } } async deleteFile(file) { if (!confirm(`Are you sure you want to delete "${file.title || file.original_filename}"?`)) { return; } try { const formData = new FormData(); formData.append('file_id', file.id); const response = await fetch('index.php?page=marketing&action=marketing_delete', { method: 'POST', body: formData }); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } const text = await response.text(); if (!text || text.trim() === '') { throw new Error('Empty response from delete server'); } const data = JSON.parse(text); if (data && (data.success || !data.error)) { this.closeModal(document.getElementById('previewModal')); this.loadFiles(); this.showToast('File deleted successfully!', 'success'); } else if (data.error) { throw new Error(data.error); } else { throw new Error('Unexpected response format'); } } catch (error) { this.showToast(error.message || 'Error deleting file', 'error'); } } previewFile(file) { this.selectedFile = file; const modal = document.getElementById('previewModal'); const title = document.getElementById('previewTitle'); const content = document.getElementById('previewContent'); title.textContent = file.title || file.original_filename; // Generate preview content based on file type if (['jpg', 'jpeg', 'png', 'gif', 'webp'].includes(file.file_type.toLowerCase())) { content.innerHTML = `${this.escapeHtml(file.title)}`; } else if (file.file_type.toLowerCase() === 'mp4') { content.innerHTML = ``; } else { content.innerHTML = `

${this.escapeHtml(file.title || file.original_filename)}

File Type: ${file.file_type.toUpperCase()}

Size: ${file.file_size_formatted}

Created: ${this.formatDate(file.created)}

`; } this.showModal(modal); } downloadFile(file) { const link = document.createElement('a'); link.href = file.file_path; link.download = file.original_filename; document.body.appendChild(link); link.click(); document.body.removeChild(link); } // Utility methods async apiCall(endpoint, params = {}, method = 'GET') { const url = new URL(`/api.php${endpoint}`, window.location.origin); let options = { method: method, headers: { 'Content-Type': 'application/json', } }; if (method === 'GET') { Object.keys(params).forEach(key => url.searchParams.append(key, params[key])); } else { options.body = JSON.stringify(params); } const response = await fetch(url, options); return await response.json(); } escapeHtml(text) { const map = { '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }; return text ? text.replace(/[&<>"']/g, m => map[m]) : ''; } formatDate(dateString) { return new Date(dateString).toLocaleDateString(); } formatFileSize(bytes) { const sizes = ['B', 'KB', 'MB', 'GB']; const i = Math.floor(Math.log(bytes) / Math.log(1024)); return Math.round(bytes / Math.pow(1024, i) * 100) / 100 + ' ' + sizes[i]; } debounce(func, wait) { let timeout; return function executedFunction(...args) { const later = () => { clearTimeout(timeout); func(...args); }; clearTimeout(timeout); timeout = setTimeout(later, wait); }; } showToast(message, type = 'info') { // Simple toast implementation const toast = document.createElement('div'); toast.className = `toast toast-${type}`; toast.textContent = message; document.body.appendChild(toast); setTimeout(() => { toast.classList.add('show'); }, 100); setTimeout(() => { toast.classList.remove('show'); setTimeout(() => document.body.removeChild(toast), 300); }, 3000); } setViewMode(mode) { this.viewMode = mode; const container = document.getElementById('filesContainer'); // Update view mode classes container.className = `files-container ${mode}-view`; // Update button states document.querySelectorAll('.view-btn').forEach(btn => btn.classList.remove('active')); document.getElementById(`${mode}ViewBtn`).classList.add('active'); // Re-render files with new view mode this.loadFiles(); } selectFolder(folderId) { // Clear current folder selection and files BEFORE setting new folder const container = document.getElementById('filesContainer'); container.innerHTML = ''; // Set new current folder this.currentFolder = folderId; // Update UI this.updateBreadcrumb(); // Update active folder in tree document.querySelectorAll('.folder-item').forEach(item => { item.classList.remove('active'); }); const selectedFolder = document.querySelector(`[data-folder="${folderId}"]`); if (selectedFolder) { selectedFolder.classList.add('active'); } // Load files for the new folder this.loadFiles(); } updateBreadcrumb() { // Implement breadcrumb navigation const nav = document.getElementById('breadcrumbNav'); // This would build breadcrumb based on current folder path } updateFileTypeFilters() { this.filters.fileTypes = []; document.querySelectorAll('.file-type-filters input[type="checkbox"]:checked').forEach(checkbox => { const types = checkbox.value.split(','); this.filters.fileTypes.push(...types); }); this.loadFiles(); } filterByTag(tagName) { // Set the tag filter this.filters.tag = tagName; // Update the dropdown to show the selected tag const tagSelect = document.getElementById('tagFilter'); if (tagSelect) { tagSelect.value = tagName; } // Clear folder selection to search across all folders this.currentFolder = ''; // Update folder tree UI to show root as active document.querySelectorAll('.folder-item').forEach(item => { item.classList.remove('active'); }); const rootFolder = document.querySelector('.folder-item.root'); if (rootFolder) { rootFolder.classList.add('active'); } // Reload files with the tag filter this.loadFiles(); } populateTagFilter(tags) { const select = document.getElementById('tagFilter'); select.innerHTML = ''; tags.forEach(tag => { const option = document.createElement('option'); option.value = tag.tag_name; option.textContent = `${tag.tag_name} (${tag.usage_count})`; select.appendChild(option); }); } populateFolderSelects(folders) { this.populateUploadFolders(folders); this.populateParentFolders(folders); } addFolderOptions(select, folders, level = 0) { folders.forEach(folder => { const option = document.createElement('option'); option.value = folder.id; option.textContent = ' '.repeat(level) + folder.folder_name; select.appendChild(option); if (folder.children && folder.children.length > 0) { this.addFolderOptions(select, folder.children, level + 1); } }); } populateUploadFolders(folders = []) { // Populate upload folder select const select = document.getElementById('uploadFolder'); if (select) { select.innerHTML = ''; this.addFolderOptions(select, folders); } } populateParentFolders(folders = []) { // Populate parent folder select const select = document.getElementById('parentFolder'); if (select) { select.innerHTML = ''; this.addFolderOptions(select, folders); } } getFileIcon(fileType) { const iconMap = { pdf: 'fa-file-pdf', doc: 'fa-file-word', docx: 'fa-file-word', xls: 'fa-file-excel', xlsx: 'fa-file-excel', mp4: 'fa-file-video', mov: 'fa-file-video', avi: 'fa-file-video' }; return iconMap[fileType.toLowerCase()] || 'fa-file'; } // Edit file functionality editFile(file) { this.selectedFile = file; this.showEditModal(); this.populateEditModal(file); } showEditModal() { const modal = document.getElementById('editModal'); if (modal) { this.showModal(modal); } } populateEditModal(file) { // Populate title const titleInput = document.getElementById('editTitle'); if (titleInput) { titleInput.value = file.title || ''; } // Populate folder select const folderSelect = document.getElementById('editFolder'); if (folderSelect) { folderSelect.innerHTML = ''; this.addFolderOptions(folderSelect, this.folders); // Select current folder if (file.folder_id) { folderSelect.value = file.folder_id; } } // Populate tags const tagsInput = document.getElementById('editTags'); if (tagsInput) { tagsInput.value = file.tags ? file.tags.join(', ') : ''; } } saveEdit() { if (!this.selectedFile) return; const title = document.getElementById('editTitle').value.trim(); const folderId = document.getElementById('editFolder').value; const tags = document.getElementById('editTags').value.trim(); // Compare with original values to detect changes const originalTitle = this.selectedFile.title || ''; const originalFolderId = this.selectedFile.folder_id || ''; const originalTags = (this.selectedFile.tags || []).join(', '); const hasChanges = title !== originalTitle || folderId !== originalFolderId || tags !== originalTags; if (!hasChanges) { this.showToast('No changes detected', 'info'); this.closeModal(document.getElementById('editModal')); return; } // Show loading state const saveBtn = document.getElementById('saveEdit'); const originalText = saveBtn.innerHTML; saveBtn.innerHTML = ' Saving...'; saveBtn.disabled = true; // Prepare FormData with only changed fields const formData = new FormData(); formData.append('file_id', this.selectedFile.id); if (title !== originalTitle) { formData.append('title', title); } if (folderId !== originalFolderId) { formData.append('folder_id', folderId); } if (tags !== originalTags) { formData.append('tags', tags); } // Send update request fetch('index.php?page=marketing&action=marketing_update', { method: 'POST', body: formData }) .then(response => response.json()) .then(data => { if (data.success) { this.showToast('File updated successfully!', 'success'); // Reload window to reflect changes setTimeout(() => { window.location.reload(); }, 500); } else { throw new Error(data.error || data.message || 'Failed to update file'); } }) .catch(error => { console.error('Update error:', error); this.showToast('Error updating file: ' + error.message, 'error'); }) .finally(() => { // Restore button state saveBtn.innerHTML = originalText; saveBtn.disabled = false; }); } updateQueueItem(index, item) { const queueItems = document.querySelectorAll('.upload-item'); if (queueItems[index]) { const progressFill = queueItems[index].querySelector('.progress-fill'); const status = queueItems[index].querySelector('.upload-status'); progressFill.style.width = `${item.progress}%`; status.textContent = item.status; if (item.status === 'error') { queueItems[index].classList.add('error'); } else if (item.status === 'completed') { queueItems[index].classList.add('completed'); } } } removeFromQueue(index) { this.uploadQueue.splice(index, 1); this.renderUploadQueue(); document.getElementById('startUpload').disabled = this.uploadQueue.length === 0; } // Edit folder functionality editFolder(folder) { this.selectedFolder = folder; this.showEditFolderModal(); this.populateEditFolderModal(folder); } showEditFolderModal() { const modal = document.getElementById('editFolderModal'); if (modal) { this.showModal(modal); } } populateEditFolderModal(folder) { // Populate folder name const nameInput = document.getElementById('editFolderName'); if (nameInput) { nameInput.value = folder.folder_name || ''; } // Populate parent folder select const parentSelect = document.getElementById('editParentFolder'); if (parentSelect) { parentSelect.innerHTML = ''; this.addFolderOptionsExcluding(parentSelect, this.folders, folder.id); // Select current parent if (folder.parent_id) { parentSelect.value = folder.parent_id; } } // Populate description const descInput = document.getElementById('editFolderDescription'); if (descInput) { descInput.value = folder.description || ''; } } addFolderOptionsExcluding(select, folders, excludeId, level = 0) { // Add folders but exclude the current folder and its children folders.forEach(folder => { if (folder.id !== excludeId && !this.isFolderDescendant(folder, excludeId)) { const option = document.createElement('option'); option.value = folder.id; option.textContent = ' '.repeat(level) + folder.folder_name; select.appendChild(option); if (folder.children && folder.children.length > 0) { this.addFolderOptionsExcluding(select, folder.children, excludeId, level + 1); } } }); } isFolderDescendant(folder, ancestorId) { // Check if folder is a descendant of ancestorId if (folder.id === ancestorId) return true; if (folder.children) { for (let child of folder.children) { if (this.isFolderDescendant(child, ancestorId)) { return true; } } } return false; } saveEditFolder() { if (!this.selectedFolder) return; const folderName = document.getElementById('editFolderName').value.trim(); const parentId = document.getElementById('editParentFolder').value; const description = document.getElementById('editFolderDescription').value.trim(); // Compare with original values const originalName = this.selectedFolder.folder_name || ''; const originalParentId = this.selectedFolder.parent_id || ''; const originalDescription = this.selectedFolder.description || ''; const hasChanges = folderName !== originalName || parentId !== originalParentId || description !== originalDescription; if (!hasChanges) { this.showToast('No changes detected', 'info'); this.closeModal(document.getElementById('editFolderModal')); return; } if (!folderName) { this.showToast('Folder name is required', 'error'); return; } // Show loading state const saveBtn = document.getElementById('saveEditFolder'); const originalText = saveBtn.innerHTML; saveBtn.innerHTML = ' Saving...'; saveBtn.disabled = true; // Prepare FormData const formData = new FormData(); formData.append('id', this.selectedFolder.id); if (folderName !== originalName) { formData.append('folder_name', folderName); } if (parentId !== originalParentId) { formData.append('parent_id', parentId); } if (description !== originalDescription) { formData.append('description', description); } // Send update request fetch('index.php?page=marketing&action=marketing_folders', { method: 'POST', body: formData }) .then(response => response.json()) .then(data => { if (data.success) { this.showToast('Folder updated successfully!', 'success'); // Reload window to reflect changes setTimeout(() => { window.location.reload(); }, 500); } else { throw new Error(data.error || data.message || 'Failed to update folder'); } }) .catch(error => { console.error('Update folder error:', error); this.showToast('Error updating folder: ' + error.message, 'error'); }) .finally(() => { // Restore button state saveBtn.innerHTML = originalText; saveBtn.disabled = false; }); } async deleteFolder(folder) { if (!confirm(`Are you sure you want to delete the folder "${folder.folder_name}"? This action cannot be undone.`)) { return; } try { const formData = new FormData(); formData.append('id', folder.id); formData.append('delete', 'true'); const response = await fetch('index.php?page=marketing&action=marketing_folders', { method: 'POST', body: formData }); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } const text = await response.text(); if (!text || text.trim() === '') { throw new Error('Empty response from server'); } const data = JSON.parse(text); if (data && (data.success || !data.error)) { this.closeModal(document.getElementById('editFolderModal')); this.showToast('Folder deleted successfully!', 'success'); // Reload window to reflect changes setTimeout(() => { window.location.reload(); }, 500); } else if (data.error) { throw new Error(data.error); } else { throw new Error('Unexpected response format'); } } catch (error) { this.showToast(error.message || 'Error deleting folder', 'error'); } } } // Initialize when DOM is ready document.addEventListener('DOMContentLoaded', () => { window.marketingManager = new MarketingFileManager(); });