Files
CGW-Quote-Builder/app/templates/advanced_search.html
T
Jason a6056f69ca Advanced search work started
Co-authored-by: Copilot <copilot@github.com>
2026-04-28 16:50:41 -05:00

389 lines
16 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">
<meta http-equiv="Pragma" content="no-cache">
<meta http-equiv="Expires" content="0">
<title>Advanced Search - Product Finder</title>
<link rel="stylesheet" href="{{ url_for('products.serve_css', filename='styles.css') }}">
<style>
.search-container {
padding: 30px 40px;
}
.search-header {
text-align: center;
margin-bottom: 30px;
}
.search-input-container {
max-width: 800px;
margin: 0 auto;
text-align: center;
}
.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;
padding: 20px;
cursor: pointer;
transition: all 0.2s;
}
.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;
font-size: 1.1em;
margin-bottom: 10px;
}
.no-results {
text-align: center;
padding: 40px;
color: #666;
}
.search-stats {
text-align: center;
padding: 15px;
background: #f5f5f5;
border-radius: 8px;
margin-bottom: 20px;
color: #666;
}
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>Advanced Search</h1>
</div>
<div class="user-info-bar" id="userInfoBar">
<span id="userLocationInfo"></span>
<a href="{{ url_for('auth.logout') }}" class="logout-btn" title="Logout"
onclick="return confirm('Are you sure you want to log out?');"
style="background: none; border: none; cursor: pointer; font-size: 16px; text-decoration: none; display: inline-block; padding: 8px 12px;">🚪</a>
</div>
<div class="breadcrumb">
Start Advanced Search
</div>
<div id="content">
<div class="search-container">
<div class="search-header">
<h2 style="margin-bottom: 15px;">🔍 Search Products</h2>
<p style="color: #666; margin-bottom: 20px;">
Search by product code, description, color, material, or any combination.
Try: "c1800", "storm door white", "bronze aluminum"
</p>
</div>
<div class="search-input-container">
<input
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>
<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
async function loadUserInfo() {
try {
const response = await fetch('/api/session');
const data = await response.json();
if (data.status === 'success') {
const locationNames = {
'LINDS': 'Lindsborg',
'IOLA': 'Iola',
'KC': 'Kansas City',
'BMD': 'BMD'
};
const currentLoc = locationNames[data.currentLocation] || data.currentLocation;
const canChangeLocation = data.accessibleLocations && data.accessibleLocations.length > 1;
let locationDisplay;
if (canChangeLocation) {
locationDisplay = `<a href="{{ url_for('auth.select_location_page') }}" style="color: #6a4c93; text-decoration: none; font-weight: 500; cursor: pointer;"
onmouseover="this.style.textDecoration='underline'"
onmouseout="this.style.textDecoration='none'"
title="Click to change location">📍 ${currentLoc}</a>`;
} else {
locationDisplay = `📍 ${currentLoc}`;
}
document.getElementById('userLocationInfo').innerHTML =
`👤 ${data.username} | ${locationDisplay}`;
}
} catch (error) {
console.error('Error loading user info:', error);
}
}
// Navigation functions
function goBack() {
window.history.back();
}
function goToFinder() {
window.location.href = '{{ url_for("products.quiz_index") }}';
}
// Initialize page
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>
</body>
</html>