- Created complete documentation in docs/ directory - Added PROJECT_OVERVIEW.md with feature highlights and getting started guide - Added ARCHITECTURE.md with system design and technical details - Added SECURITY.md with comprehensive security implementation guide - Added DEVELOPMENT.md with development workflows and best practices - Added DEPLOYMENT.md with production deployment instructions - Added API.md with complete REST API documentation - Added CONTRIBUTING.md with contribution guidelines - Added CHANGELOG.md with version history and migration notes - Reorganized all documentation files into docs/ directory for better organization - Updated README.md with proper documentation links and quick navigation - Enhanced project structure with professional documentation standards
225 lines
7.7 KiB
PHP
225 lines
7.7 KiB
PHP
<?php
|
|
/**
|
|
* Search and Filter JavaScript for Admin Settings
|
|
* This file provides client-side search and filtering for settings
|
|
*/
|
|
?>
|
|
<script>
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
// Add search box HTML if not already present
|
|
if (!document.getElementById('settings-search-container')) {
|
|
const searchHTML = `
|
|
<div id="settings-search-container" class="row mb-3">
|
|
<div class="col-md-8">
|
|
<div class="input-group">
|
|
<span class="input-group-text"><i class="bi bi-search"></i></span>
|
|
<input type="text" id="settings-search" class="form-control"
|
|
placeholder="Search settings... (Ctrl+K or Cmd+K)">
|
|
<button class="btn btn-outline-secondary" type="button" id="clear-search">
|
|
<i class="bi bi-x"></i>
|
|
</button>
|
|
</div>
|
|
<div id="search-results-count" class="text-muted small mt-1"></div>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<select id="settings-category-filter" class="form-select">
|
|
<option value="">All Categories</option>
|
|
<option value="general">General</option>
|
|
<option value="modules">Modules</option>
|
|
<option value="branding">Branding</option>
|
|
<option value="payment">Payments</option>
|
|
<option value="email">Email</option>
|
|
<option value="payout">Payouts</option>
|
|
<option value="seo">SEO</option>
|
|
<option value="security">Security</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
`;
|
|
|
|
// Insert before the tab content
|
|
const tabContent = document.querySelector('.tab-content');
|
|
if (tabContent) {
|
|
tabContent.insertAdjacentHTML('beforebegin', searchHTML);
|
|
}
|
|
}
|
|
|
|
const searchInput = document.getElementById('settings-search');
|
|
const clearBtn = document.getElementById('clear-search');
|
|
const categoryFilter = document.getElementById('settings-category-filter');
|
|
const resultsCount = document.getElementById('search-results-count');
|
|
|
|
if (!searchInput || !categoryFilter) return;
|
|
|
|
// Add data attributes to all form groups for searchability
|
|
function tagSettingFields() {
|
|
const currentTab = new URLSearchParams(window.location.search).get('tab') || 'general';
|
|
|
|
document.querySelectorAll('.mb-3, .form-group, .module-item').forEach(formGroup => {
|
|
const label = formGroup.querySelector('label, strong');
|
|
const input = formGroup.querySelector('input, select, textarea');
|
|
const helpText = formGroup.querySelector('.form-text, small');
|
|
|
|
if (label) {
|
|
const fieldName = input ? (input.name || input.id) : '';
|
|
const labelText = label.textContent.trim();
|
|
const helpContent = helpText ? helpText.textContent.trim() : '';
|
|
|
|
formGroup.setAttribute('data-setting-name', fieldName.toLowerCase());
|
|
formGroup.setAttribute('data-setting-label', labelText.toLowerCase());
|
|
formGroup.setAttribute('data-setting-info', helpContent.toLowerCase());
|
|
formGroup.setAttribute('data-setting-category', currentTab);
|
|
}
|
|
});
|
|
}
|
|
|
|
// Filter settings based on search and category
|
|
function filterSettings() {
|
|
const searchTerm = searchInput.value.toLowerCase().trim();
|
|
const category = categoryFilter.value;
|
|
let visibleCount = 0;
|
|
let totalCount = 0;
|
|
|
|
document.querySelectorAll('[data-setting-name]').forEach(element => {
|
|
totalCount++;
|
|
const name = element.getAttribute('data-setting-name') || '';
|
|
const label = element.getAttribute('data-setting-label') || '';
|
|
const info = element.getAttribute('data-setting-info') || '';
|
|
const settingCategory = element.getAttribute('data-setting-category') || '';
|
|
|
|
const matchesSearch = !searchTerm ||
|
|
name.includes(searchTerm) ||
|
|
label.includes(searchTerm) ||
|
|
info.includes(searchTerm);
|
|
|
|
const matchesCategory = !category || settingCategory === category;
|
|
|
|
const isVisible = matchesSearch && matchesCategory;
|
|
element.style.display = isVisible ? '' : 'none';
|
|
|
|
if (isVisible) visibleCount++;
|
|
});
|
|
|
|
updateResultCount(visibleCount, totalCount, searchTerm);
|
|
|
|
// Highlight search terms
|
|
if (searchTerm) {
|
|
highlightSearchTerms(searchTerm);
|
|
} else {
|
|
removeHighlights();
|
|
}
|
|
}
|
|
|
|
function updateResultCount(visible, total, searchTerm) {
|
|
if (!resultsCount) return;
|
|
|
|
if (searchTerm || categoryFilter.value) {
|
|
resultsCount.textContent = `Showing ${visible} of ${total} settings`;
|
|
resultsCount.style.display = 'block';
|
|
} else {
|
|
resultsCount.textContent = '';
|
|
resultsCount.style.display = 'none';
|
|
}
|
|
}
|
|
|
|
function highlightSearchTerms(term) {
|
|
removeHighlights();
|
|
|
|
document.querySelectorAll('[data-setting-name]:not([style*="display: none"])').forEach(element => {
|
|
const labels = element.querySelectorAll('label, .form-text, small');
|
|
labels.forEach(label => {
|
|
const text = label.textContent;
|
|
const regex = new RegExp(`(${term})`, 'gi');
|
|
if (regex.test(text)) {
|
|
label.innerHTML = text.replace(regex, '<mark>$1</mark>');
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
function removeHighlights() {
|
|
document.querySelectorAll('mark').forEach(mark => {
|
|
mark.replaceWith(mark.textContent);
|
|
});
|
|
}
|
|
|
|
function clearSearch() {
|
|
searchInput.value = '';
|
|
categoryFilter.value = '';
|
|
filterSettings();
|
|
searchInput.focus();
|
|
}
|
|
|
|
// Event listeners
|
|
searchInput.addEventListener('input', filterSettings);
|
|
categoryFilter.addEventListener('change', filterSettings);
|
|
clearBtn.addEventListener('click', clearSearch);
|
|
|
|
// Keyboard shortcuts
|
|
document.addEventListener('keydown', function(e) {
|
|
// Ctrl+K or Cmd+K to focus search
|
|
if ((e.ctrlKey || e.metaKey) && e.key === 'k') {
|
|
e.preventDefault();
|
|
searchInput.focus();
|
|
searchInput.select();
|
|
}
|
|
|
|
// Escape to clear search
|
|
if (e.key === 'Escape' && document.activeElement === searchInput) {
|
|
clearSearch();
|
|
}
|
|
});
|
|
|
|
// Initialize
|
|
tagSettingFields();
|
|
|
|
// Re-tag when tab changes
|
|
const tabLinks = document.querySelectorAll('.nav-tabs a');
|
|
tabLinks.forEach(link => {
|
|
link.addEventListener('click', function() {
|
|
setTimeout(tagSettingFields, 100);
|
|
});
|
|
});
|
|
|
|
// Show keyboard shortcut hint
|
|
searchInput.setAttribute('title', 'Press Ctrl+K (or Cmd+K) to focus, Esc to clear');
|
|
});
|
|
</script>
|
|
|
|
<style>
|
|
#settings-search-container {
|
|
background: #f8f9fa;
|
|
padding: 15px;
|
|
border-radius: 8px;
|
|
margin-bottom: 20px;
|
|
}
|
|
|
|
mark {
|
|
background-color: #ffeb3b;
|
|
padding: 2px 4px;
|
|
border-radius: 3px;
|
|
font-weight: 600;
|
|
}
|
|
|
|
#search-results-count {
|
|
margin-top: 5px;
|
|
font-size: 0.875rem;
|
|
}
|
|
|
|
#settings-search:focus {
|
|
border-color: #1a73e8;
|
|
box-shadow: 0 0 0 0.2rem rgba(26, 115, 232, 0.25);
|
|
}
|
|
|
|
.input-group .btn-outline-secondary {
|
|
border-color: #ced4da;
|
|
}
|
|
|
|
.input-group .btn-outline-secondary:hover {
|
|
background-color: #dc3545;
|
|
border-color: #dc3545;
|
|
color: white;
|
|
}
|
|
</style>
|
|
<?php
|