import React, { Component } from 'react'; import Box from '@mui/material/Box'; import Typography from '@mui/material/Typography'; import Grid from '@mui/material/Grid'; import Pagination from '@mui/material/Pagination'; import FormControl from '@mui/material/FormControl'; import InputLabel from '@mui/material/InputLabel'; import Select from '@mui/material/Select'; import MenuItem from '@mui/material/MenuItem'; import Chip from '@mui/material/Chip'; import Stack from '@mui/material/Stack'; import Product from './Product.js'; import { removeSessionSetting } from '../utils/sessionStorage.js'; // Sort products by fuzzy similarity to their name/description function sortProductsByFuzzySimilarity(products, searchTerm) { console.log('sortProductsByFuzzySimilarity',products,searchTerm); // Create an array that preserves the product object and its searchable text const productsWithText = products.map(product => { const searchableText = `${product.name || ''} ${product.description || ''}`; return { product, searchableText }; }); // Sort products based on their searchable text similarity productsWithText.sort((a, b) => { const scoreA = getFuzzySimilarityScore(a.searchableText, searchTerm); const scoreB = getFuzzySimilarityScore(b.searchableText, searchTerm); return scoreB - scoreA; // Higher scores first }); // Return just the sorted product objects return productsWithText.map(item => item.product); } // Calculate a similarity score between text and search term function getFuzzySimilarityScore(text, searchTerm) { const searchWords = searchTerm.toLowerCase().split(/\W+/).filter(Boolean); const textWords = text.toLowerCase().split(/\W+/).filter(Boolean); let totalScore = 0; for (let searchWord of searchWords) { // Exact matches get highest priority if (textWords.includes(searchWord)) { totalScore += 2; continue; } // Partial matches get scored based on similarity let bestMatch = 0; for (let textWord of textWords) { if (textWord.includes(searchWord) || searchWord.includes(textWord)) { const similarity = Math.min(searchWord.length, textWord.length) / Math.max(searchWord.length, textWord.length); if (similarity > bestMatch) bestMatch = similarity; } } totalScore += bestMatch; } return totalScore; } class ProductList extends Component { constructor(props) { super(props); this.state = { viewMode: window.productListViewMode || 'grid', products:[], page: window.productListPage || 1, itemsPerPage: window.productListItemsPerPage || 20, sortBy: window.currentSearchQuery ? 'searchField' : 'name' }; } componentDidMount() { this.handleSearchQuery = () => { this.setState({ sortBy: window.currentSearchQuery ? 'searchField' : 'name' }); }; window.addEventListener('search-query-change', this.handleSearchQuery); } componentWillUnmount() { window.removeEventListener('search-query-change', this.handleSearchQuery); } handleViewModeChange = (viewMode) => { this.setState({ viewMode }); window.productListViewMode = viewMode; } handlePageChange = (event, value) => { this.setState({ page: value }); window.productListPage = value; } componentDidUpdate() { const currentPageCapacity = this.state.itemsPerPage === 'all' ? Infinity : this.state.itemsPerPage; if(this.props.products.length > 0 ) if (this.props.products.length < (currentPageCapacity * (this.state.page-1)) ) { if(this.state.page != 1) this.setState({ page: 1 }); window.productListPage = 1; } } handleProductsPerPageChange = (event) => { const newItemsPerPage = event.target.value; const newState = { itemsPerPage: newItemsPerPage }; window.productListItemsPerPage = newItemsPerPage; if(newItemsPerPage!=='all'){ const newTotalPages = Math.ceil(this.props.products.length / newItemsPerPage); if (this.state.page > newTotalPages) { newState.page = newTotalPages; window.productListPage = newTotalPages; } } this.setState(newState); } handleSortChange = (event) => { const sortBy = event.target.value; this.setState({ sortBy }); } renderPagination = (pages, page) => { return ( {(this.state.itemsPerPage==='all')?null: = 600} showLastButton={window.innerWidth >= 600} sx={{ '& .MuiPagination-ul': { flexWrap: 'nowrap', overflowX: 'auto', maxWidth: '100%' } }} /> } ); } render() { //console.log('products',this.props.activeAttributeFilters,this.props.activeManufacturerFilters,window.currentSearchQuery,this.state.sortBy); const filteredProducts = (this.state.sortBy==='searchField')&&(window.currentSearchQuery)?sortProductsByFuzzySimilarity(this.props.products, window.currentSearchQuery):this.state.sortBy==='name'?this.props.products:this.props.products.sort((a,b)=>{ if(this.state.sortBy==='price-low-high'){ return a.price-b.price; } if(this.state.sortBy==='price-high-low'){ return b.price-a.price; } }); const products = this.state.itemsPerPage==='all'?[...filteredProducts]:filteredProducts.slice((this.state.page - 1) * this.state.itemsPerPage , this.state.page * this.state.itemsPerPage); return ( {this.props.activeAttributeFilters.map((filter,index) => ( { removeSessionSetting(`filter_attribute_${filter.id}`); this.props.onFilterChange(); }} onDelete={() => { removeSessionSetting(`filter_attribute_${filter.id}`); this.props.onFilterChange(); }} clickable /> ))} {this.props.activeManufacturerFilters.map((filter,index) => ( { removeSessionSetting(`filter_manufacturer_${filter.value}`); this.props.onFilterChange(); }} onDelete={() => { removeSessionSetting(`filter_manufacturer_${filter.value}`); this.props.onFilterChange(); }} clickable /> ))} {/* Sort Dropdown */} Sortierung {/* Per Page Dropdown */} pro Seite {/* Product count info - mobile only */} { this.props.totalProductCount==this.props.products.length && this.props.totalProductCount>0 ? `${this.props.totalProductCount} Produkte` : `${this.props.products.length} von ${this.props.totalProductCount} Produkte` } { this.renderPagination(Math.ceil(filteredProducts.length / this.state.itemsPerPage), this.state.page) } {/*this.props.dataType == 'category' && (<>Kategorie: {this.props.dataParam})}*/} {this.props.dataType == 'search' && (<>Suchergebnisse für: "{this.props.dataParam}")} { this.props.totalProductCount==this.props.products.length && this.props.totalProductCount>0 ? `${this.props.totalProductCount} Produkte` : `${this.props.products.length} von ${this.props.totalProductCount} Produkte` } {products.map((product, index) => ( ))} {/* Bottom pagination */} {this.renderPagination(Math.ceil(filteredProducts.length / this.state.itemsPerPage), this.state.page)} ); } } export default ProductList;