feat: Add comprehensive documentation suite and reorganize project structure
- 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
This commit is contained in:
224
admin/includes/settings_search.php
Normal file
224
admin/includes/settings_search.php
Normal file
@@ -0,0 +1,224 @@
|
||||
<?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
|
||||
Reference in New Issue
Block a user