diff --git a/app/blueprints/products.py b/app/blueprints/products.py index 318483b..bff17b5 100644 --- a/app/blueprints/products.py +++ b/app/blueprints/products.py @@ -17,6 +17,18 @@ def product_finder(): """Serve the product finder quiz page""" return render_template('product_finder.html') +@products_bp.route('/index') +@login_required +def quiz_index(): + """Alternative route for product finder""" + return render_template('product_finder.html') + +@products_bp.route('/advanced-search') +@login_required +def advanced_search(): + """Serve the advanced search page""" + return render_template('advanced_search.html') + @products_bp.route('/css/') def serve_css(filename): """Serve CSS files""" diff --git a/app/static/css/styles.css b/app/static/css/styles.css index e430104..7048405 100644 --- a/app/static/css/styles.css +++ b/app/static/css/styles.css @@ -732,3 +732,166 @@ body { margin: 5px 0; color: #666; } + +/* Search Bar Component */ +.search-container { + padding: 20px 30px; + background-color: #ffffff; + border-bottom: 2px solid #e0e0e0; +} + +.search-wrapper { + max-width: 800px; + margin: 0 auto; +} + +.search-input-wrapper { + position: relative; + margin-bottom: 15px; +} + +.search-input { + width: 100%; + padding: 12px 16px; + font-size: 16px; + border: 2px solid #333; + border-radius: 8px; + outline: none; + transition: all 0.3s ease; + background-color: white; +} + +.search-input:focus { + border-color: #6a4c93; + box-shadow: 0 0 0 3px rgba(106, 76, 147, 0.1); +} + +.search-dropdown { + position: absolute; + top: 100%; + left: 0; + right: 0; + background-color: white; + border: 2px solid #333; + border-top: none; + border-radius: 0 0 8px 8px; + max-height: 300px; + overflow-y: auto; + display: none; + z-index: 1000; + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); +} + +.search-dropdown-item { + padding: 12px 16px; + cursor: pointer; + border-bottom: 1px solid #e0e0e0; + transition: background-color 0.2s ease; +} + +.search-dropdown-item:last-child { + border-bottom: none; +} + +.search-dropdown-item:hover { + background-color: #f5f5f5; +} + +.search-dropdown-item.selected { + background-color: #e8e0f0; + font-weight: bold; +} + +.search-dropdown-item-title { + font-weight: bold; + color: #333; + margin-bottom: 4px; +} + +.search-dropdown-item-code { + font-size: 12px; + color: #666; +} + +.search-buttons { + display: flex; + gap: 10px; + justify-content: center; +} + +.search-btn { + padding: 10px 20px; + font-size: 14px; + font-weight: bold; + border: 2px solid #333; + border-radius: 8px; + cursor: pointer; + transition: all 0.3s ease; + background-color: white; + color: #333; +} + +.search-btn:hover:not(:disabled) { + transform: translateY(-2px); + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15); +} + +.search-btn:active:not(:disabled) { + transform: translateY(0); +} + +.search-btn:disabled { + opacity: 0.5; + cursor: not-allowed; +} + +.view-btn { + background-color: #6a4c93; + color: white; + border-color: #6a4c93; +} + +.view-btn:hover:not(:disabled) { + background-color: #563d7a; + border-color: #563d7a; +} + +.search-btn-action { + background-color: #4CAF50; + color: white; + border-color: #4CAF50; +} + +.search-btn-action:hover:not(:disabled) { + background-color: #45a049; + border-color: #45a049; +} + +.advanced-btn { + background-color: #2196F3; + color: white; + border-color: #2196F3; +} + +.advanced-btn:hover:not(:disabled) { + background-color: #0b7dda; + border-color: #0b7dda; +} + +/* Dropdown scrollbar */ +.search-dropdown::-webkit-scrollbar { + width: 8px; +} + +.search-dropdown::-webkit-scrollbar-track { + background: #f1f1f1; +} + +.search-dropdown::-webkit-scrollbar-thumb { + background: #888; + border-radius: 4px; +} + +.search-dropdown::-webkit-scrollbar-thumb:hover { + background: #555; +} diff --git a/app/static/js/script.js b/app/static/js/script.js index 83eefb1..9c68169 100644 --- a/app/static/js/script.js +++ b/app/static/js/script.js @@ -1445,3 +1445,321 @@ function navigateToHistory(index) { // Start the quiz when page loads window.addEventListener('DOMContentLoaded', init); + +// ===== SEARCH BAR FUNCTIONALITY ===== + +// Search state variables +let searchMatches = []; +let selectedSearchIndex = -1; + +// Toggle search bar visibility based on current screen +function toggleSearchBar() { + const searchContainer = document.getElementById('searchContainer'); + const currentKey = history.length > 0 ? history[history.length - 1] : 'start'; + + if (currentKey === 'start') { + searchContainer.style.display = 'block'; + } else { + searchContainer.style.display = 'none'; + // Clear search when hiding + clearSearch(); + } +} + +// Clear search state +function clearSearch() { + const searchInput = document.getElementById('searchInput'); + const searchDropdown = document.getElementById('searchDropdown'); + const viewBtn = document.getElementById('viewBtn'); + + if (searchInput) searchInput.value = ''; + if (searchDropdown) { + searchDropdown.style.display = 'none'; + searchDropdown.innerHTML = ''; + } + if (viewBtn) viewBtn.disabled = true; + + searchMatches = []; + selectedSearchIndex = -1; +} + +// Filter products by search term +function filterProductsBySearch(searchTerm) { + if (!searchTerm || searchTerm.trim().length < 2) { + return []; + } + + const term = searchTerm.toLowerCase().trim(); + + return productData.filter(product => { + const description = (product.description || '').toLowerCase(); + const productCode = (product.productCode || '').toLowerCase(); + const category = (product.category || '').toLowerCase(); + const baseType = (product.baseType || '').toLowerCase(); + const subTypeWindow = (product.subType?.window || '').toLowerCase(); + const subTypeDoor = (product.subType?.door || '').toLowerCase(); + + return description.includes(term) || + productCode.includes(term) || + category.includes(term) || + baseType.includes(term) || + subTypeWindow.includes(term) || + subTypeDoor.includes(term); + }); +} + +// Update search dropdown with matches +function updateSearchDropdown(matches) { + const dropdown = document.getElementById('searchDropdown'); + const viewBtn = document.getElementById('viewBtn'); + + if (!matches || matches.length === 0) { + dropdown.style.display = 'none'; + dropdown.innerHTML = ''; + viewBtn.disabled = true; + selectedSearchIndex = -1; + return; + } + + // Show only first 10 matches + const displayMatches = matches.slice(0, 10); + + dropdown.innerHTML = displayMatches.map((product, index) => { + const prodCode = product.productCode || product.id; + const desc = product.description || 'No description'; + const isSelected = index === selectedSearchIndex ? 'selected' : ''; + + return ` +
+
${desc}
+
${prodCode}
+
+ `; + }).join(''); + + if (matches.length > 10) { + dropdown.innerHTML += ` +
+ + ${matches.length - 10} more results... (click to view all) +
+ `; + } + + dropdown.style.display = 'block'; +} + +// Select a search item +function selectSearchItem(index, closeDropdown = true) { + selectedSearchIndex = index; + const viewBtn = document.getElementById('viewBtn'); + const searchInput = document.getElementById('searchInput'); + const searchDropdown = document.getElementById('searchDropdown'); + + viewBtn.disabled = false; + + // Update search input with selected product description + if (searchMatches[index]) { + const selectedProduct = searchMatches[index]; + const desc = selectedProduct.description || selectedProduct.DESCRIPTION || 'No description'; + searchInput.value = desc; + } + + // Hide dropdown after selection (unless using keyboard navigation) + if (closeDropdown) { + searchDropdown.style.display = 'none'; + } + + // Update visual selection + const items = document.querySelectorAll('.search-dropdown-item'); + items.forEach((item, i) => { + if (i === index) { + item.classList.add('selected'); + } else { + item.classList.remove('selected'); + } + }); +} + +// Handle View button click +function handleViewProduct() { + if (selectedSearchIndex >= 0 && selectedSearchIndex < searchMatches.length) { + const product = searchMatches[selectedSearchIndex]; + const prodCode = product.productCode || product.id; + + // Clear search and hide dropdown + clearSearch(); + + // Navigate to product + showProductByCode(prodCode); + } +} + +// Handle Search button click (show results grid) +function handleSearchResults() { + const searchInput = document.getElementById('searchInput'); + const searchTerm = searchInput.value.trim(); + + if (!searchTerm || searchTerm.length < 2) { + alert('Please enter at least 2 characters to search.'); + return; + } + + const matches = filterProductsBySearch(searchTerm); + + if (matches.length === 0) { + alert('No products found matching "' + searchTerm + '".'); + return; + } + + // Store search results temporarily + searchMatches = matches; + + // Clear search UI + clearSearch(); + + // Display results in grid format (similar to showFilteredProducts) + showSearchResultsGrid(matches, searchTerm); +} + +// Show search results in a grid +function showSearchResultsGrid(matches, searchTerm) { + const contentDiv = document.getElementById('content'); + + let html = ` +
+
Found ${matches.length} Product${matches.length > 1 ? 's' : ''}
+
+

Search results for: "${searchTerm}"

+
+
+ `; + + matches.forEach(product => { + const prodCode = product.productCode || product.id; + const desc = product.description || product.DESCRIPTION || 'No description'; + const materials = product.materials ? product.materials.join(', ') : 'N/A'; + const colors = product.colors ? product.colors.join(', ') : 'N/A'; + const baseType = product.baseType || 'N/A'; + const subType = product.subType?.window || product.subType?.door || 'N/A'; + + html += ` +
+

${desc}

+

${prodCode}

+
+ Type: ${baseType}${subType !== 'N/A' ? ' - ' + subType : ''}
+ Materials: ${materials}
+ Colors: ${colors} +
+
+ `; + }); + + html += ` +
+
+ + +
+
+ `; + + contentDiv.innerHTML = html; + + // Update history to track search results + history.push('search-results'); + updateBreadcrumb(); +} + +// Handle Advanced Search button click +function handleAdvancedSearch() { + // Navigate to advanced search page + window.location.href = '/quiz/advanced-search'; +} + +// Set up search input event listeners +function initSearchBar() { + const searchInput = document.getElementById('searchInput'); + const searchDropdown = document.getElementById('searchDropdown'); + + if (!searchInput) return; + + // Input event for real-time filtering + searchInput.addEventListener('input', function(e) { + const searchTerm = e.target.value.trim(); + + if (searchTerm.length < 2) { + searchMatches = []; + updateSearchDropdown([]); + return; + } + + searchMatches = filterProductsBySearch(searchTerm); + updateSearchDropdown(searchMatches); + + // Reset selection + selectedSearchIndex = -1; + document.getElementById('viewBtn').disabled = true; + }); + + // Keyboard navigation (arrow keys and enter) + searchInput.addEventListener('keydown', function(e) { + if (searchMatches.length === 0) return; + + if (e.key === 'ArrowDown') { + e.preventDefault(); + selectedSearchIndex = Math.min(selectedSearchIndex + 1, searchMatches.length - 1); + updateSearchDropdown(searchMatches); + selectSearchItem(selectedSearchIndex, false); // Keep dropdown open + } else if (e.key === 'ArrowUp') { + e.preventDefault(); + selectedSearchIndex = Math.max(selectedSearchIndex - 1, -1); + if (selectedSearchIndex >= 0) { + updateSearchDropdown(searchMatches); + selectSearchItem(selectedSearchIndex, false); // Keep dropdown open + } else { + document.getElementById('viewBtn').disabled = true; + } + } else if (e.key === 'Enter') { + e.preventDefault(); + if (selectedSearchIndex >= 0) { + handleViewProduct(); + } else { + handleSearchResults(); + } + } else if (e.key === 'Escape') { + clearSearch(); + } + }); + + // Click outside to close dropdown + document.addEventListener('click', function(e) { + if (!searchInput.contains(e.target) && !searchDropdown.contains(e.target)) { + searchDropdown.style.display = 'none'; + } + }); +} + +// Override loadContent to toggle search bar +const originalLoadContent = loadContent; +loadContent = function(key) { + originalLoadContent(key); + toggleSearchBar(); +}; + +// Override startOver to show search bar +const originalStartOver = startOver; +startOver = function() { + originalStartOver(); + toggleSearchBar(); +}; + +// Initialize search bar when page loads +window.addEventListener('DOMContentLoaded', function() { + initSearchBar(); + toggleSearchBar(); +}); diff --git a/app/templates/advanced_search.html b/app/templates/advanced_search.html new file mode 100644 index 0000000..d172455 --- /dev/null +++ b/app/templates/advanced_search.html @@ -0,0 +1,153 @@ + + + + + + + + + Advanced Search - Product Finder + + + + +
+
+

Advanced Search

+
+ + + + + +
+
+
🚧
+
Work in Progress
+
Advanced Search Features Coming Soon
+ +
+

Planned Features:

+
    +
  • Multi-criteria filtering (type, material, color, size)
  • +
  • Price range filters
  • +
  • Availability by location
  • +
  • Advanced product specifications search
  • +
  • Compatibility matching
  • +
  • Save and share custom searches
  • +
+
+ +
+ + +
+
+
+
+ + + + diff --git a/app/templates/product_finder.html b/app/templates/product_finder.html index b3b2423..9a24599 100644 --- a/app/templates/product_finder.html +++ b/app/templates/product_finder.html @@ -26,6 +26,25 @@ Start + + +