// =========================================== // REUSABLE DATATABLE COLUMN SEARCH UTILITY // =========================================== /** * Initialize column-specific search functionality for DataTable * @param {Object} config - Configuration object * @param {string} config.tableId - DataTable selector (e.g., '#PRItemTable') * @param {Object} config.dataTable - DataTable instance * @param {Array} config.searchableColumns - Array of column configurations * @param {string} config.containerId - Container ID for search inputs (optional) * @param {string} config.searchInputClass - CSS class for search inputs (optional) */ function initializeColumnSearch(config) { const { tableId, dataTable, searchableColumns, containerId = null, searchInputClass = 'column-search-input' } = config; // Create search container if not provided let searchContainer; if (containerId) { searchContainer = $('#' + containerId); } else { // Create default search container above the table searchContainer = $('
'); $(tableId).parent().prepend(searchContainer); } // Clear existing search inputs searchContainer.empty(); // Create search inputs for each specified column searchableColumns.forEach(function (columnConfig) { const columnIndex = columnConfig.columnIndex; const columnName = columnConfig.columnName; const placeholder = columnConfig.placeholder; const searchType = columnConfig.searchType || 'text'; const searchMode = columnConfig.searchMode || 'contains'; const selectOptions = columnConfig.selectOptions || null; const width = columnConfig.width || 'auto'; // Create input wrapper const inputWrapper = $(''); let searchInput; // Create different input types based on searchType switch (searchType) { case 'select': searchInput = $(''); // Add options if provided if (selectOptions && Array.isArray(selectOptions)) { selectOptions.forEach(function (option) { searchInput.append(''); }); } break; case 'date': searchInput = $(''); break; case 'number': searchInput = $(''); break; default: // text searchInput = $(''); } inputWrapper.append(searchInput); searchContainer.append(inputWrapper); }); // Add clear all button const clearButton = $(''); searchContainer.append(clearButton); // Bind search events bindColumnSearchEvents(dataTable, searchInputClass); } /** * Bind search events to column search inputs * @param {Object} dataTable - DataTable instance * @param {string} searchInputClass - CSS class for search inputs */ function bindColumnSearchEvents(dataTable, searchInputClass) { // Individual column search $('.' + searchInputClass).off('keyup change').on('keyup change', function () { const columnIndex = $(this).data('column'); const searchValue = $(this).val(); const searchMode = $(this).data('search-mode') || 'contains'; // Apply column-specific search with regex based on search mode let searchRegex = ''; if (searchValue) { switch (searchMode) { case 'exact': // Exact match - search for the exact value searchRegex = '^' + escapeRegex(searchValue) + '$'; break; case 'starts': // Starts with - search for values that start with the input searchRegex = '^' + escapeRegex(searchValue); break; case 'ends': // Ends with - search for values that end with the input searchRegex = escapeRegex(searchValue) + '$'; break; default: // 'contains' // Contains - default DataTable behavior (no regex needed) searchRegex = searchValue; } } // Apply search with regex if needed if (searchMode !== 'contains' && searchValue) { dataTable.column(columnIndex).search(searchRegex, true, false).draw(); } else { dataTable.column(columnIndex).search(searchValue).draw(); } // Update URL parameters (optional) updateSearchParams($(this).data('column-name'), searchValue); }); // Clear all searches $('.clear-column-search').off('click').on('click', function () { $('.' + searchInputClass).val(''); // Clear all column searches dataTable.columns().search('').draw(); // Clear URL parameters (optional) clearAllSearchParams(); }); } /** * Escape special regex characters * @param {string} string - String to escape * @returns {string} Escaped string */ function escapeRegex(string) { return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); } /** * Populate select options dynamically from table data * @param {Object} dataTable - DataTable instance * @param {number} columnIndex - Column index to extract unique values * @param {string} selectSelector - jQuery selector for the select element */ function populateSelectOptions(dataTable, columnIndex, selectSelector) { // Get unique values from the column const uniqueValues = dataTable.column(columnIndex).data().unique().sort(); const selectElement = $(selectSelector); // Clear existing options (except the first "All" option) selectElement.find('option:not(:first)').remove(); // Add unique values as options uniqueValues.each(function (value) { if (value && value.toString().trim() !== '') { selectElement.append(''); } }); } /** * Update URL search parameters (optional feature) * @param {string} paramName - Parameter name * @param {string} paramValue - Parameter value */ function updateSearchParams(paramName, paramValue) { if (typeof URLSearchParams === 'undefined') return; const url = new URL(window.location); if (paramValue && paramValue.trim() !== '') { url.searchParams.set('search_' + paramName, paramValue); } else { url.searchParams.delete('search_' + paramName); } window.history.replaceState({}, '', url); } /** * Clear all search parameters from URL */ function clearAllSearchParams() { if (typeof URLSearchParams === 'undefined') return; const url = new URL(window.location); const keysToDelete = []; for (const key of url.searchParams.keys()) { if (key.indexOf('search_') === 0) { keysToDelete.push(key); } } keysToDelete.forEach(function (key) { url.searchParams.delete(key); }); window.history.replaceState({}, '', url); } /** * Restore search values from URL parameters * @param {string} searchInputClass - CSS class for search inputs */ function restoreSearchFromURL(searchInputClass) { if (typeof URLSearchParams === 'undefined') return; const url = new URL(window.location); $('.' + searchInputClass).each(function () { const columnName = $(this).data('column-name'); const searchValue = url.searchParams.get('search_' + columnName); if (searchValue) { $(this).val(searchValue).trigger('change'); } }); }