Advanced search work started
Co-authored-by: Copilot <copilot@github.com>
This commit is contained in:
@@ -1677,9 +1677,17 @@ 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
|
||||||
function initSearchBar() {
|
function initSearchBar() {
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
Reference in New Issue
Block a user