/**
* 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 {
// Use proper folder ID (null for root, or the folder ID)
const folderId = this.currentFolder ? this.currentFolder : 'null';
// Add cache busting to prevent browser caching
let url = `index.php?page=marketing&action=marketing_files&folder_id=${folderId}&limit=50&_t=${Date.now()}`;
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) {
emptyState.style.display = 'block';
} else {
this.renderFiles(files);
}
} 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);
});
}
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 = `
${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);
});
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 `
`;
}
// 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)}
`;
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 = `
`;
} 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();
}
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();
});