From ead44afb69fafdf10aa12baed18cdac2ee7317a2 Mon Sep 17 00:00:00 2001 From: sebseb7 Date: Wed, 3 Sep 2025 11:31:24 +0200 Subject: [PATCH] feat: enhance GrowTentKonfigurator with tent filtering and improved rendering - Added helper functions to filter tent products by shape and generate coverage descriptions based on dimensions. - Implemented logic to handle product image rendering with caching and loading states. - Updated tent selection process to dynamically find and display products based on selected tent shape. - Enhanced user interface with loading indicators and improved layout for product selection. --- src/pages/GrowTentKonfigurator.js | 336 +++++++++++++++++++++++++++--- 1 file changed, 310 insertions(+), 26 deletions(-) diff --git a/src/pages/GrowTentKonfigurator.js b/src/pages/GrowTentKonfigurator.js index 744f3fe..d06e61b 100644 --- a/src/pages/GrowTentKonfigurator.js +++ b/src/pages/GrowTentKonfigurator.js @@ -10,10 +10,13 @@ import { ListItem, ListItemText, ListItemSecondaryAction, + Grid, + CardMedia, + CircularProgress, } from '@mui/material'; import ShoppingCartIcon from '@mui/icons-material/ShoppingCart'; import { TentShapeSelector, ProductSelector, ExtrasSelector } from '../components/configurator/index.js'; -import { tentShapes, tentSizes, lightTypes, ventilationTypes, extras } from '../data/configuratorData.js'; +import { tentShapes, lightTypes, ventilationTypes, extras } from '../data/configuratorData.js'; function setCachedCategoryData(categoryId, data) { if (!window.productCache) { @@ -143,12 +146,19 @@ class GrowTentKonfigurator extends Component { return; } - console.log(`productList:${categoryId}`); window.socketManager.off(`productList:${categoryId}`); + // Track if we've received the full response to ignore stub response if needed + let receivedFullResponse = false; + window.socketManager.on(`productList:${categoryId}`,(response) => { - console.log("getCategoryProducts full response", response); + receivedFullResponse = true; setCachedCategoryData(categoryId, response); + + // Force re-render when data arrives + if (categoryId === 'Zelte') { + this.forceUpdate(); + } }); const currentLanguage = this.props.languageContext?.currentLanguage || this.props.i18n?.language || 'de'; @@ -156,7 +166,13 @@ class GrowTentKonfigurator extends Component { "getCategoryProducts", { categoryId: categoryId, language: currentLanguage, requestTranslation: currentLanguage === 'de' ? false : true }, (response) => { - console.log("getCategoryProducts stub response", response); + // Only process stub response if we haven't received the full response yet + if (!receivedFullResponse) { + setCachedCategoryData(categoryId, response); + if (categoryId === 'Zelte') { + this.forceUpdate(); + } + } } ); } @@ -188,6 +204,133 @@ class GrowTentKonfigurator extends Component { + // Helper function to filter real tent products by shape + filterTentsByShape(tentShape, products, attributes) { + if (!products || !attributes || !tentShape) { + return []; + } + + // Parse the shape dimensions (e.g., '80x80' -> width: 80, depth: 80) + const [widthStr, depthStr] = tentShape.split('x'); + const targetWidth = parseInt(widthStr); + const targetDepth = parseInt(depthStr); + + // Group attributes by product for efficient lookup + const productAttributes = {}; + attributes.forEach(attr => { + if (!productAttributes[attr.kArtikel]) { + productAttributes[attr.kArtikel] = {}; + } + productAttributes[attr.kArtikel][attr.cName] = attr.cWert; + }); + + // Filter products that match the target dimensions + const matchingProducts = products.filter(product => { + const attrs = productAttributes[product.id]; + if (!attrs) return false; + + // Check width (Breite) + const widthAttr = attrs['Breite']; + if (!widthAttr) return false; + const widthMatch = widthAttr.match(/(\d+)cm breit/i); + if (!widthMatch) return false; + const productWidth = parseInt(widthMatch[1]); + + // Check depth (Tiefe) + const depthAttr = attrs['Tiefe']; + if (!depthAttr) return false; + const depthMatch = depthAttr.match(/(\d+)cm tief/i); + if (!depthMatch) return false; + const productDepth = parseInt(depthMatch[1]); + + // Check if dimensions match + return productWidth === targetWidth && productDepth === targetDepth; + }); + + return matchingProducts.map(product => { + // Convert to the format expected by the configurator + console.log('Raw product from backend:', product); + return product; + }); // No sorting needed + } + + // Helper function to generate coverage descriptions + getCoverageDescription(width, depth) { + const area = width * depth; + if (area <= 3600) return '1-2 Pflanzen'; // 60x60 + if (area <= 6400) return '2-4 Pflanzen'; // 80x80 + if (area <= 10000) return '4-6 Pflanzen'; // 100x100 + return '3-6 Pflanzen'; // 120x60 and larger + } + + // Render tent image using working code from Product component + renderTentImage(product) { + if (!window.smallPicCache) { + window.smallPicCache = {}; + } + + const pictureList = product.pictureList; + + if (!pictureList || pictureList.length === 0 || !pictureList.split(',').length) { + return ( + + ); + } + + const bildId = pictureList.split(',')[0]; + + if (window.smallPicCache[bildId]) { + return ( + + ); + } + + // Load image if not cached + if (!this.loadingImages) this.loadingImages = new Set(); + if (!this.loadingImages.has(bildId)) { + this.loadingImages.add(bildId); + window.socketManager.emit('getPic', { bildId, size:'small' }, (res) => { + if (res.success) { + window.smallPicCache[bildId] = URL.createObjectURL(new Blob([res.imageBuffer], { type: 'image/jpeg' })); + this.forceUpdate(); + } + this.loadingImages.delete(bildId); + }); + } + + return ( + + ); + } + + // Get real tent products for the selected shape + getTentsForShape(shapeId) { + const cachedData = getCachedCategoryData('Zelte'); + if (!cachedData || !cachedData.products || !cachedData.attributes) { + return []; + } + + return this.filterTentsByShape(shapeId, cachedData.products, cachedData.attributes); + } + calculateTotalPrice() { let total = 0; const { selectedTentSize, selectedLightType, selectedVentilationType, selectedExtras } = this.state; @@ -195,9 +338,17 @@ class GrowTentKonfigurator extends Component { // Add tent price if (selectedTentSize) { - const tent = tentSizes.find(t => t.id === selectedTentSize); - if (tent) { - total += tent.price; + // Find the selected tent from all available shapes + let selectedTent = null; + const allShapes = ['60x60', '80x80', '100x100', '120x60']; + for (const shape of allShapes) { + const tents = this.getTentsForShape(shape); + selectedTent = tents.find(t => t.id === selectedTentSize); + if (selectedTent) break; + } + + if (selectedTent) { + total += selectedTent.price; itemCount++; } } @@ -260,9 +411,17 @@ class GrowTentKonfigurator extends Component { // Calculate original total without discount if (selectedTentSize) { - const tent = tentSizes.find(t => t.id === selectedTentSize); - if (tent) { - originalTotal += tent.price; + // Find the selected tent from all available shapes + let selectedTent = null; + const allShapes = ['60x60', '80x80', '100x100', '120x60']; + for (const shape of allShapes) { + const tents = this.getTentsForShape(shape); + selectedTent = tents.find(t => t.id === selectedTentSize); + if (selectedTent) break; + } + + if (selectedTent) { + originalTotal += selectedTent.price; itemCount++; } } @@ -321,25 +480,143 @@ class GrowTentKonfigurator extends Component { } renderTentSizeSection() { - const { selectedTentSize, selectedTentShape } = this.state; - - // Filter tents by selected shape - const filteredTents = tentSizes.filter(tent => tent.shapeId === selectedTentShape); - + const { selectedTentShape } = this.state; + if (!selectedTentShape) { return null; // Don't show tent sizes until shape is selected } - + + // Get real filtered tent products for the selected shape + const filteredTents = this.getTentsForShape(selectedTentShape); + + // Show loading state if data is not yet available + if (filteredTents.length === 0) { + const cachedData = getCachedCategoryData('Zelte'); + if (!cachedData) { + return ( + + + Lade Growbox-Produkte... + + + ); + } + // If we have cached data but no filtered tents, show empty state + return ( + + + Keine Produkte für diese Größe verfügbar + + + ); + } + + console.log('Product display:', { + productsCount: filteredTents.length, + firstProduct: filteredTents[0] || null + }); + + if (filteredTents.length === 0) { + return ( + + + Keine Produkte verfügbar + + + ); + } + return ( - + + + 2. Growbox Produkt auswählen + + + Wähle das passende Produkt für deine {selectedTentShape} Growbox + + + {filteredTents.map((product, _index) => ( + + this.handleTentSizeSelect(product.id)}> + {/* Image */} + + {this.renderTentImage(product)} + + + {/* Content */} + + {/* Name */} + +{product.name} + + + {/* Price */} + +{product.price ? new Intl.NumberFormat('de-DE', { + style: 'currency', + currency: 'EUR' + }).format(product.price) : 'Kein Preis'} + + + {/* VAT */} + + (incl. 19% MwSt.,*) + + + {/* Selection Indicator */} + {this.state.selectedTentSize === product.id && ( + + + ✓ Ausgewählt + + + )} + + + + ))} + + ); } @@ -391,7 +668,14 @@ class GrowTentKonfigurator extends Component { renderInlineSummary() { const { selectedTentSize, selectedLightType, selectedVentilationType, selectedExtras, totalPrice } = this.state; - const selectedTent = tentSizes.find(t => t.id === selectedTentSize); + // Find the selected tent from all available shapes + let selectedTent = null; + const allShapes = ['60x60', '80x80', '100x100', '120x60']; + for (const shape of allShapes) { + const tents = this.getTentsForShape(shape); + selectedTent = tents.find(t => t.id === selectedTentSize); + if (selectedTent) break; + } const selectedLight = lightTypes.find(l => l.id === selectedLightType); const selectedVentilation = ventilationTypes.find(v => v.id === selectedVentilationType); const selectedExtrasItems = extras.filter(e => selectedExtras.includes(e.id));