Advanced search work started

Co-authored-by: Copilot <copilot@github.com>
This commit is contained in:
Jason
2026-04-28 16:50:41 -05:00
parent b8c477a59c
commit a6056f69ca
2 changed files with 298 additions and 55 deletions
+9 -1
View File
@@ -1677,8 +1677,16 @@ function showSearchResultsGrid(matches, searchTerm) {
// Handle Advanced Search button click // Handle Advanced Search button click
function handleAdvancedSearch() { function handleAdvancedSearch() {
// Navigate to advanced search page // Get search text from input field
const searchInput = document.getElementById('searchInput');
const searchText = searchInput ? searchInput.value.trim() : '';
// Navigate to advanced search page with search query parameter
if (searchText) {
window.location.href = '/quiz/advanced-search?q=' + encodeURIComponent(searchText);
} else {
window.location.href = '/quiz/advanced-search'; window.location.href = '/quiz/advanced-search';
}
} }
// Set up search input event listeners // Set up search input event listeners
+288 -53
View File
@@ -9,52 +9,62 @@
<title>Advanced Search - Product Finder</title> <title>Advanced Search - Product Finder</title>
<link rel="stylesheet" href="{{ url_for('products.serve_css', filename='styles.css') }}"> <link rel="stylesheet" href="{{ url_for('products.serve_css', filename='styles.css') }}">
<style> <style>
.wip-container { .search-container {
padding: 30px 40px;
}
.search-header {
text-align: center; text-align: center;
padding: 60px 40px;
}
.wip-icon {
font-size: 80px;
margin-bottom: 20px;
}
.wip-title {
font-size: 32px;
font-weight: bold;
color: #333;
margin-bottom: 15px;
}
.wip-subtitle {
font-size: 18px;
color: #666;
margin-bottom: 30px; margin-bottom: 30px;
} }
.wip-details { .search-input-container {
max-width: 600px; max-width: 800px;
margin: 0 auto 40px; margin: 0 auto;
padding: 20px; text-align: center;
background-color: #f9f9f9; }
border: 1px solid #ddd;
.products-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
gap: 20px;
margin-top: 20px;
}
.product-card {
background: white;
border: 2px solid #e0e0e0;
border-radius: 8px; border-radius: 8px;
text-align: left; padding: 20px;
cursor: pointer;
transition: all 0.2s;
} }
.wip-details h3 { .product-card:hover {
border-color: #6a4c93;
box-shadow: 0 4px 12px rgba(106, 76, 147, 0.15);
transform: translateY(-2px);
}
.product-card h3 {
color: #333; color: #333;
margin-bottom: 15px; font-size: 1.1em;
margin-bottom: 10px;
} }
.wip-details ul { .no-results {
list-style-position: inside; text-align: center;
padding: 40px;
color: #666; color: #666;
line-height: 1.8;
} }
.wip-details ul li { .search-stats {
margin-bottom: 8px; text-align: center;
padding: 15px;
background: #f5f5f5;
border-radius: 8px;
margin-bottom: 20px;
color: #666;
} }
</style> </style>
</head> </head>
@@ -76,32 +86,235 @@
</div> </div>
<div id="content"> <div id="content">
<div class="wip-container"> <div class="search-container">
<div class="wip-icon">🚧</div> <div class="search-header">
<div class="wip-title">Work in Progress</div> <h2 style="margin-bottom: 15px;">🔍 Search Products</h2>
<div class="wip-subtitle">Advanced Search Features Coming Soon</div> <p style="color: #666; margin-bottom: 20px;">
Search by product code, description, color, material, or any combination.
<div class="wip-details"> Try: "c1800", "storm door white", "bronze aluminum"
<h3>Planned Features:</h3> </p>
<ul>
<li>Multi-criteria filtering (type, material, color, size)</li>
<li>Price range filters</li>
<li>Availability by location</li>
<li>Advanced product specifications search</li>
<li>Compatibility matching</li>
<li>Save and share custom searches</li>
</ul>
</div> </div>
<div class="result-actions"> <div class="search-input-container">
<button class="back-button" onclick="goBack()">← Go Back</button> <input
<button class="back-button" onclick="goToFinder()" style="margin-left: 10px;">🏠 Back to Product Finder</button> type="text"
id="searchInput"
placeholder="Enter search terms..."
style="width: 100%; padding: 15px 20px; font-size: 16px; border: 2px solid #ddd; border-radius: 8px; box-sizing: border-box;"
onkeyup="handleSearch(event)"
>
<button
onclick="performSearch()"
style="margin-top: 15px; padding: 15px 30px; font-size: 16px; background-color: #6a4c93; color: white; border: none; border-radius: 8px; cursor: pointer; font-weight: 600;"
>
Search
</button>
<button
onclick="clearSearch()"
style="margin-top: 15px; margin-left: 10px; padding: 15px 30px; font-size: 16px; background-color: #ccc; color: #333; border: none; border-radius: 8px; cursor: pointer; font-weight: 600;"
>
Clear
</button>
</div>
<div id="searchResults" style="margin-top: 30px;"></div>
<div class="result-actions" style="margin-top: 30px;">
<button class="back-button" onclick="goToFinder()">🏠 Back to Product Finder</button>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<script> <script>
// Product data
let productData = [];
// Load product data on page load
async function loadProductData() {
try {
const response = await fetch('/quiz/data/products.json');
productData = await response.json();
console.log('Loaded', productData.length, 'products');
} catch (error) {
console.error('Error loading product data:', error);
document.getElementById('searchResults').innerHTML = `
<div class="no-results">
<p style="color: #d32f2f;">⚠️ Error loading product data. Please refresh the page.</p>
</div>
`;
}
}
// Normalize text for matching (remove spaces, hyphens, special chars, lowercase)
function normalizeText(text) {
if (!text) return '';
return text.toString().toLowerCase()
.replace(/[-\s_]/g, '') // Remove hyphens, spaces, underscores
.replace(/[^a-z0-9]/g, ''); // Remove other special characters
}
// Check if a value matches a search term (partial match)
function matchesTerm(value, term) {
if (!value) return false;
// For arrays (like colors, materials)
if (Array.isArray(value)) {
return value.some(item => normalizeText(item).includes(term));
}
// For objects (like subType)
if (typeof value === 'object' && value !== null) {
return Object.values(value).some(v =>
v && normalizeText(v).includes(term)
);
}
// For strings and numbers
return normalizeText(value).includes(term);
}
// Search products with partial matching
function searchProducts(searchQuery) {
if (!searchQuery || searchQuery.trim() === '') {
return [];
}
// Split search query into individual terms
const searchTerms = searchQuery.toLowerCase()
.split(/\s+/)
.filter(term => term.length > 0)
.map(term => normalizeText(term));
console.log('Search terms:', searchTerms);
// Filter products that match ALL search terms
const results = productData.filter(product => {
// Each product must match ALL search terms
return searchTerms.every(term => {
// Check if the term matches any of these fields
return (
matchesTerm(product.productCode, term) ||
matchesTerm(product.id, term) ||
matchesTerm(product.description, term) ||
matchesTerm(product.category, term) ||
matchesTerm(product.baseType, term) ||
matchesTerm(product.subType, term) ||
matchesTerm(product.materials, term) ||
matchesTerm(product.colors, term) ||
matchesTerm(product.location, term)
);
});
});
return results;
}
// Display search results
function displayResults(results, searchQuery) {
const resultsDiv = document.getElementById('searchResults');
if (results.length === 0) {
resultsDiv.innerHTML = `
<div class="no-results">
<h3 style="color: #333; margin-bottom: 10px;">No products found</h3>
<p>No products match "${searchQuery}"</p>
<p style="margin-top: 15px; font-size: 0.9em;">Try:</p>
<ul style="text-align: left; max-width: 400px; margin: 10px auto;">
<li>Fewer or more general terms</li>
<li>Different spellings (c1800, c-1800, c 1800)</li>
<li>Product categories (storm door, window, slider)</li>
<li>Materials (aluminum, vinyl) or colors (white, bronze)</li>
</ul>
</div>
`;
return;
}
let html = `
<div class="search-stats">
Found <strong>${results.length}</strong> product${results.length !== 1 ? 's' : ''} matching "<strong>${searchQuery}</strong>"
</div>
<div class="products-grid">
`;
results.forEach(product => {
const prodCode = product.productCode || product.id;
const desc = product.description || 'No description';
const materials = product.materials && product.materials.length > 0
? product.materials.join(', ')
: 'N/A';
const colors = product.colors && product.colors.length > 0
? product.colors.join(', ')
: 'N/A';
const location = product.location || 'N/A';
const discontinued = product.discontinued ? ' <span style="color: #d32f2f;">(Discontinued)</span>' : '';
// Get subtype string
let subtypeStr = '';
if (product.subType) {
const doorType = product.subType.door;
const windowType = product.subType.window;
if (doorType) subtypeStr = doorType;
else if (windowType) subtypeStr = windowType;
}
html += `
<div class="product-card" onclick="viewProductDetail('${prodCode}')">
<h3>${desc}${discontinued}</h3>
<p style="color: #999; font-size: 0.95em; margin-top: 5px;"><strong>${prodCode}</strong></p>
<div style="font-size: 0.9em; color: #666; margin-top: 10px;">
${product.baseType ? `<strong>Type:</strong> ${product.baseType}<br>` : ''}
${subtypeStr ? `<strong>Subtype:</strong> ${subtypeStr}<br>` : ''}
<strong>Materials:</strong> ${materials}<br>
<strong>Colors:</strong> ${colors}<br>
<strong>Location:</strong> ${location}
</div>
</div>
`;
});
html += '</div>';
resultsDiv.innerHTML = html;
}
// Perform search
function performSearch() {
const searchInput = document.getElementById('searchInput');
const searchQuery = searchInput.value.trim();
if (searchQuery === '') {
document.getElementById('searchResults').innerHTML = `
<div class="no-results">
<p>Please enter a search term</p>
</div>
`;
return;
}
const results = searchProducts(searchQuery);
displayResults(results, searchQuery);
}
// Handle Enter key in search input
function handleSearch(event) {
if (event.key === 'Enter') {
performSearch();
}
}
// Clear search
function clearSearch() {
document.getElementById('searchInput').value = '';
document.getElementById('searchResults').innerHTML = '';
document.getElementById('searchInput').focus();
}
// View product detail (redirect to product finder with product code)
function viewProductDetail(productCode) {
window.location.href = `/quiz/?p=${productCode}`;
}
// Load user session info // Load user session info
async function loadUserInfo() { async function loadUserInfo() {
try { try {
@@ -146,8 +359,30 @@
window.location.href = '{{ url_for("products.quiz_index") }}'; window.location.href = '{{ url_for("products.quiz_index") }}';
} }
// Load user info when page loads // Initialize page
loadUserInfo(); async function init() {
await loadUserInfo();
await loadProductData();
// Check if there's a search query in URL
const urlParams = new URLSearchParams(window.location.search);
const queryParam = urlParams.get('q');
if (queryParam) {
// Populate search input with the query
const searchInput = document.getElementById('searchInput');
searchInput.value = queryParam;
// Automatically perform the search
performSearch();
}
// Focus on search input
document.getElementById('searchInput').focus();
}
// Load when page loads
init();
</script> </script>
</body> </body>
</html> </html>