/** * Advanced Search Functionality * Handles complex search queries with date ranges, user filters, and multiple criteria */ // Open advanced search modal function openAdvancedSearch() { const modal = document.getElementById('advancedSearchModal'); if (modal) { modal.style.display = 'flex'; document.body.classList.add('modal-open'); loadUsersForSearch(); populateCurrentFilters(); loadSavedFilters(); } } // Close advanced search modal function closeAdvancedSearch() { const modal = document.getElementById('advancedSearchModal'); if (modal) { modal.style.display = 'none'; document.body.classList.remove('modal-open'); } } // Close modal when clicking on backdrop function closeOnAdvancedSearchBackdropClick(event) { const modal = document.getElementById('advancedSearchModal'); if (event.target === modal) { closeAdvancedSearch(); } } // Load users for dropdown async function loadUsersForSearch() { try { const response = await fetch('/api/get_users.php', { credentials: 'same-origin' }); const data = await response.json(); if (data.success && data.users) { const createdBySelect = document.getElementById('adv-created-by'); const assignedToSelect = document.getElementById('adv-assigned-to'); // Clear existing options (except first default option) while (createdBySelect.options.length > 1) { createdBySelect.remove(1); } while (assignedToSelect.options.length > 2) { // Keep "Any" and "Unassigned" assignedToSelect.remove(2); } // Add users to both dropdowns data.users.forEach(user => { const displayName = user.display_name || user.username; const option1 = document.createElement('option'); option1.value = user.user_id; option1.textContent = displayName; createdBySelect.appendChild(option1); const option2 = document.createElement('option'); option2.value = user.user_id; option2.textContent = displayName; assignedToSelect.appendChild(option2); }); } } catch (error) { console.error('Error loading users:', error); } } // Populate form with current URL parameters function populateCurrentFilters() { const urlParams = new URLSearchParams(window.location.search); // Search text if (urlParams.has('search')) { document.getElementById('adv-search-text').value = urlParams.get('search'); } // Status if (urlParams.has('status')) { const statuses = urlParams.get('status').split(','); const statusSelect = document.getElementById('adv-status'); Array.from(statusSelect.options).forEach(option => { option.selected = statuses.includes(option.value); }); } } // Perform advanced search function performAdvancedSearch(event) { event.preventDefault(); const params = new URLSearchParams(); // Search text const searchText = document.getElementById('adv-search-text').value.trim(); if (searchText) { params.set('search', searchText); } // Date ranges const createdFrom = document.getElementById('adv-created-from').value; const createdTo = document.getElementById('adv-created-to').value; const updatedFrom = document.getElementById('adv-updated-from').value; const updatedTo = document.getElementById('adv-updated-to').value; if (createdFrom) params.set('created_from', createdFrom); if (createdTo) params.set('created_to', createdTo); if (updatedFrom) params.set('updated_from', updatedFrom); if (updatedTo) params.set('updated_to', updatedTo); // Status (multi-select) const statusSelect = document.getElementById('adv-status'); const selectedStatuses = Array.from(statusSelect.selectedOptions).map(opt => opt.value); if (selectedStatuses.length > 0) { params.set('status', selectedStatuses.join(',')); } // Priority range const priorityMin = document.getElementById('adv-priority-min').value; const priorityMax = document.getElementById('adv-priority-max').value; if (priorityMin) params.set('priority_min', priorityMin); if (priorityMax) params.set('priority_max', priorityMax); // Users const createdBy = document.getElementById('adv-created-by').value; const assignedTo = document.getElementById('adv-assigned-to').value; if (createdBy) params.set('created_by', createdBy); if (assignedTo) params.set('assigned_to', assignedTo); // Redirect to dashboard with params window.location.href = '/?' + params.toString(); } // Reset advanced search form function resetAdvancedSearch() { document.getElementById('advancedSearchForm').reset(); // Unselect all multi-select options const statusSelect = document.getElementById('adv-status'); Array.from(statusSelect.options).forEach(option => { option.selected = false; }); } // Save current search as a filter async function saveCurrentFilter() { showInputModal( 'Save Search Filter', 'Enter a name for this filter:', 'My Filter', async (filterName) => { if (!filterName || filterName.trim() === '') { toast.warning('Filter name cannot be empty', 2000); return; } const filterCriteria = getCurrentFilterCriteria(); try { const response = await fetch('/api/saved_filters.php', { method: 'POST', credentials: 'same-origin', headers: { 'Content-Type': 'application/json', 'X-CSRF-Token': window.CSRF_TOKEN }, body: JSON.stringify({ filter_name: filterName.trim(), filter_criteria: filterCriteria }) }); const result = await response.json(); if (result.success) { toast.success(`Filter "${filterName}" saved successfully!`, 3000); loadSavedFilters(); } else { toast.error('Failed to save filter: ' + (result.error || 'Unknown error'), 4000); } } catch (error) { console.error('Error saving filter:', error); toast.error('Error saving filter', 4000); } } ); } // Get current filter criteria from form function getCurrentFilterCriteria() { const criteria = {}; const searchText = document.getElementById('adv-search-text').value.trim(); if (searchText) criteria.search = searchText; const createdFrom = document.getElementById('adv-created-from').value; if (createdFrom) criteria.created_from = createdFrom; const createdTo = document.getElementById('adv-created-to').value; if (createdTo) criteria.created_to = createdTo; const updatedFrom = document.getElementById('adv-updated-from').value; if (updatedFrom) criteria.updated_from = updatedFrom; const updatedTo = document.getElementById('adv-updated-to').value; if (updatedTo) criteria.updated_to = updatedTo; const statusSelect = document.getElementById('adv-status'); const selectedStatuses = Array.from(statusSelect.selectedOptions).map(opt => opt.value); if (selectedStatuses.length > 0) criteria.status = selectedStatuses.join(','); const priorityMin = document.getElementById('adv-priority-min').value; if (priorityMin) criteria.priority_min = priorityMin; const priorityMax = document.getElementById('adv-priority-max').value; if (priorityMax) criteria.priority_max = priorityMax; const createdBy = document.getElementById('adv-created-by').value; if (createdBy) criteria.created_by = createdBy; const assignedTo = document.getElementById('adv-assigned-to').value; if (assignedTo) criteria.assigned_to = assignedTo; return criteria; } // Load saved filters async function loadSavedFilters() { try { const response = await fetch('/api/saved_filters.php', { credentials: 'same-origin' }); const data = await response.json(); if (data.success && data.filters) { populateSavedFiltersDropdown(data.filters); } } catch (error) { console.error('Error loading saved filters:', error); } } // Populate saved filters dropdown function populateSavedFiltersDropdown(filters) { const dropdown = document.getElementById('saved-filters-select'); if (!dropdown) return; // Clear existing options except the first (placeholder) while (dropdown.options.length > 1) { dropdown.remove(1); } // Add saved filters filters.forEach(filter => { const option = document.createElement('option'); option.value = filter.filter_id; option.textContent = filter.filter_name + (filter.is_default ? ' ⭐' : ''); option.dataset.criteria = JSON.stringify(filter.filter_criteria); dropdown.appendChild(option); }); } // Load a saved filter function loadSavedFilter() { const dropdown = document.getElementById('saved-filters-select'); const selectedOption = dropdown.options[dropdown.selectedIndex]; if (!selectedOption || !selectedOption.dataset.criteria) return; try { const criteria = JSON.parse(selectedOption.dataset.criteria); applySavedFilterCriteria(criteria); } catch (error) { console.error('Error loading filter:', error); } } // Apply saved filter criteria to form function applySavedFilterCriteria(criteria) { // Search text document.getElementById('adv-search-text').value = criteria.search || ''; // Date ranges document.getElementById('adv-created-from').value = criteria.created_from || ''; document.getElementById('adv-created-to').value = criteria.created_to || ''; document.getElementById('adv-updated-from').value = criteria.updated_from || ''; document.getElementById('adv-updated-to').value = criteria.updated_to || ''; // Status const statusSelect = document.getElementById('adv-status'); const statuses = criteria.status ? criteria.status.split(',') : []; Array.from(statusSelect.options).forEach(option => { option.selected = statuses.includes(option.value); }); // Priority document.getElementById('adv-priority-min').value = criteria.priority_min || ''; document.getElementById('adv-priority-max').value = criteria.priority_max || ''; // Users document.getElementById('adv-created-by').value = criteria.created_by || ''; document.getElementById('adv-assigned-to').value = criteria.assigned_to || ''; } // Delete saved filter async function deleteSavedFilter() { const dropdown = document.getElementById('saved-filters-select'); const selectedOption = dropdown.options[dropdown.selectedIndex]; if (!selectedOption || selectedOption.value === '') { if (typeof toast !== 'undefined') { toast.error('Please select a filter to delete'); } return; } const filterId = selectedOption.value; const filterName = selectedOption.textContent; showConfirmModal( `Delete Filter "${filterName}"?`, 'This action cannot be undone.', 'error', async () => { try { const response = await fetch('/api/saved_filters.php', { method: 'DELETE', credentials: 'same-origin', headers: { 'Content-Type': 'application/json', 'X-CSRF-Token': window.CSRF_TOKEN }, body: JSON.stringify({ filter_id: filterId }) }); const result = await response.json(); if (result.success) { toast.success('Filter deleted successfully', 3000); loadSavedFilters(); resetAdvancedSearch(); } else { toast.error('Failed to delete filter', 4000); } } catch (error) { console.error('Error deleting filter:', error); toast.error('Error deleting filter', 4000); } } ); } // Keyboard shortcut (Ctrl+Shift+F) document.addEventListener('keydown', (e) => { if (e.ctrlKey && e.shiftKey && e.key === 'F') { e.preventDefault(); openAdvancedSearch(); } // ESC to close if (e.key === 'Escape') { const modal = document.getElementById('advancedSearchModal'); if (modal && modal.style.display === 'flex') { closeAdvancedSearch(); } } });