feat: enhance GrowTentKonfigurator with lamp filtering and improved UI
- Implemented filtering logic for lamps based on selected tent shape and availability. - Updated rendering of lamp selection to dynamically display available options based on user input. - Enhanced user interface with improved styling and selection indicators for better user experience. - Added console logging for debugging purposes to track filtering and selection processes.
This commit is contained in:
@@ -16,7 +16,7 @@ import {
|
|||||||
} from '@mui/material';
|
} from '@mui/material';
|
||||||
import ShoppingCartIcon from '@mui/icons-material/ShoppingCart';
|
import ShoppingCartIcon from '@mui/icons-material/ShoppingCart';
|
||||||
import { TentShapeSelector, ProductSelector, ExtrasSelector } from '../components/configurator/index.js';
|
import { TentShapeSelector, ProductSelector, ExtrasSelector } from '../components/configurator/index.js';
|
||||||
import { tentShapes, lightTypes, ventilationTypes, extras } from '../data/configuratorData.js';
|
import { tentShapes, ventilationTypes, extras } from '../data/configuratorData.js';
|
||||||
|
|
||||||
function setCachedCategoryData(categoryId, data) {
|
function setCachedCategoryData(categoryId, data) {
|
||||||
if (!window.productCache) {
|
if (!window.productCache) {
|
||||||
@@ -244,7 +244,13 @@ class GrowTentKonfigurator extends Component {
|
|||||||
const productDepth = parseInt(depthMatch[1]);
|
const productDepth = parseInt(depthMatch[1]);
|
||||||
|
|
||||||
// Check if dimensions match
|
// Check if dimensions match
|
||||||
return productWidth === targetWidth && productDepth === targetDepth;
|
const sizeMatch = productWidth === targetWidth && productDepth === targetDepth;
|
||||||
|
|
||||||
|
// Check availability - only show products that are available for delivery
|
||||||
|
// Following same logic as Content.js: available > 0 OR availableSupplier == 1
|
||||||
|
const isAvailable = (product.available > 0) || (product.availableSupplier === 1);
|
||||||
|
|
||||||
|
return sizeMatch && isAvailable;
|
||||||
});
|
});
|
||||||
|
|
||||||
return matchingProducts.map(product => {
|
return matchingProducts.map(product => {
|
||||||
@@ -254,6 +260,58 @@ class GrowTentKonfigurator extends Component {
|
|||||||
}); // No sorting needed
|
}); // No sorting needed
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Filter lamps by tent size using "passend für Zelt" attribute
|
||||||
|
filterLampsByTentSize(tentShape, products, attributes) {
|
||||||
|
if (!products || !attributes || !tentShape) return [];
|
||||||
|
|
||||||
|
console.log('Filtering lamps for tent shape:', tentShape);
|
||||||
|
console.log('Available lamp products:', products.length);
|
||||||
|
console.log('Available lamp attributes:', attributes.length);
|
||||||
|
|
||||||
|
// Group attributes by product ID and attribute name
|
||||||
|
const attributesByProduct = {};
|
||||||
|
attributes.forEach(attr => {
|
||||||
|
if (!attributesByProduct[attr.kArtikel]) {
|
||||||
|
attributesByProduct[attr.kArtikel] = {};
|
||||||
|
}
|
||||||
|
attributesByProduct[attr.kArtikel][attr.cName] = attr.cWert;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Filter products by matching tent size in "passend für Zelt" attribute
|
||||||
|
const matchingProducts = products.filter(product => {
|
||||||
|
const attrs = attributesByProduct[product.id];
|
||||||
|
if (!attrs) return false;
|
||||||
|
|
||||||
|
// Check "passend für Zelt" attribute
|
||||||
|
const tentSizeAttr = attrs['passend für Zelt'];
|
||||||
|
if (!tentSizeAttr) return false;
|
||||||
|
|
||||||
|
// Check if tent size matches
|
||||||
|
const sizeMatch = tentSizeAttr === tentShape;
|
||||||
|
|
||||||
|
// Check availability - only show products that are available for delivery
|
||||||
|
const isAvailable = (product.available > 0) || (product.availableSupplier === 1);
|
||||||
|
|
||||||
|
console.log(`Product ${product.id}: tentSize=${tentSizeAttr}, match=${sizeMatch}, available=${isAvailable}`);
|
||||||
|
|
||||||
|
return sizeMatch && isAvailable;
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('Filtered lamps:', matchingProducts.length);
|
||||||
|
return matchingProducts;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get lamps for selected tent shape
|
||||||
|
getLampsForTentShape(shapeId) {
|
||||||
|
const cachedData = getCachedCategoryData('Lampen');
|
||||||
|
if (!cachedData || !cachedData.products || !cachedData.attributes) {
|
||||||
|
console.log('No cached lamp data available');
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.filterLampsByTentSize(shapeId, cachedData.products, cachedData.attributes);
|
||||||
|
}
|
||||||
|
|
||||||
// Helper function to generate coverage descriptions
|
// Helper function to generate coverage descriptions
|
||||||
getCoverageDescription(width, depth) {
|
getCoverageDescription(width, depth) {
|
||||||
const area = width * depth;
|
const area = width * depth;
|
||||||
@@ -355,8 +413,9 @@ class GrowTentKonfigurator extends Component {
|
|||||||
|
|
||||||
// Add light price
|
// Add light price
|
||||||
if (selectedLightType) {
|
if (selectedLightType) {
|
||||||
const light = lightTypes.find(l => l.id === selectedLightType);
|
const availableLamps = this.getLampsForTentShape(this.state.selectedTentShape);
|
||||||
if (light) {
|
const light = availableLamps.find(l => l.id === selectedLightType);
|
||||||
|
if (light && light.price) {
|
||||||
total += light.price;
|
total += light.price;
|
||||||
itemCount++;
|
itemCount++;
|
||||||
}
|
}
|
||||||
@@ -427,8 +486,9 @@ class GrowTentKonfigurator extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (selectedLightType) {
|
if (selectedLightType) {
|
||||||
const light = lightTypes.find(l => l.id === selectedLightType);
|
const availableLamps = this.getLampsForTentShape(this.state.selectedTentShape);
|
||||||
if (light) {
|
const light = availableLamps.find(l => l.id === selectedLightType);
|
||||||
|
if (light && light.price) {
|
||||||
originalTotal += light.price;
|
originalTotal += light.price;
|
||||||
itemCount++;
|
itemCount++;
|
||||||
}
|
}
|
||||||
@@ -543,15 +603,16 @@ class GrowTentKonfigurator extends Component {
|
|||||||
display: 'flex',
|
display: 'flex',
|
||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
borderRadius: '8px',
|
borderRadius: '8px',
|
||||||
boxShadow: '0px 2px 8px rgba(0,0,0,0.1)',
|
|
||||||
overflow: 'hidden',
|
overflow: 'hidden',
|
||||||
backgroundColor: '#ffffff',
|
|
||||||
cursor: 'pointer',
|
cursor: 'pointer',
|
||||||
border: '2px solid',
|
border: '2px solid',
|
||||||
borderColor: this.state.selectedTentSize === product.id ? '#2e7d32' : '#e0e0e0',
|
borderColor: this.state.selectedTentSize === product.id ? '#2e7d32' : '#e0e0e0',
|
||||||
|
backgroundColor: this.state.selectedTentSize === product.id ? '#f1f8e9' : '#ffffff',
|
||||||
'&:hover': {
|
'&:hover': {
|
||||||
borderColor: '#90caf9'
|
boxShadow: 6,
|
||||||
}
|
borderColor: this.state.selectedTentSize === product.id ? '#2e7d32' : '#90caf9'
|
||||||
|
},
|
||||||
|
transition: 'all 0.3s ease'
|
||||||
}}
|
}}
|
||||||
onClick={() => this.handleTentSizeSelect(product.id)}>
|
onClick={() => this.handleTentSizeSelect(product.id)}>
|
||||||
{/* Image */}
|
{/* Image */}
|
||||||
@@ -568,48 +629,41 @@ class GrowTentKonfigurator extends Component {
|
|||||||
{/* Content */}
|
{/* Content */}
|
||||||
<Box sx={{ p: 2, flexGrow: 1, display: 'flex', flexDirection: 'column' }}>
|
<Box sx={{ p: 2, flexGrow: 1, display: 'flex', flexDirection: 'column' }}>
|
||||||
{/* Name */}
|
{/* Name */}
|
||||||
<Typography
|
<Typography variant="h6" gutterBottom sx={{ fontWeight: 'bold' }}>
|
||||||
variant="h6"
|
|
||||||
sx={{
|
|
||||||
fontWeight: 600,
|
|
||||||
mb: 1,
|
|
||||||
display: '-webkit-box',
|
|
||||||
WebkitLineClamp: 2,
|
|
||||||
WebkitBoxOrient: 'vertical',
|
|
||||||
overflow: 'hidden',
|
|
||||||
textOverflow: 'ellipsis'
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{product.name}
|
{product.name}
|
||||||
</Typography>
|
</Typography>
|
||||||
|
|
||||||
{/* Price */}
|
{/* Price with VAT - Same as Product.js */}
|
||||||
<Typography
|
<Typography variant="h6" sx={{
|
||||||
variant="h6"
|
color: '#2e7d32',
|
||||||
sx={{
|
fontWeight: 'bold',
|
||||||
color: '#2e7d32',
|
mt: 2,
|
||||||
fontWeight: 'bold',
|
display: 'flex',
|
||||||
mt: 'auto'
|
alignItems: 'center',
|
||||||
}}
|
gap: 1,
|
||||||
>
|
flexWrap: 'wrap'
|
||||||
{product.price ? new Intl.NumberFormat('de-DE', {
|
}}>
|
||||||
|
<span>{product.price ? new Intl.NumberFormat('de-DE', {
|
||||||
style: 'currency',
|
style: 'currency',
|
||||||
currency: 'EUR'
|
currency: 'EUR'
|
||||||
}).format(product.price) : 'Kein Preis'}
|
}).format(product.price) : 'Kein Preis'}</span>
|
||||||
</Typography>
|
{product.vat && (
|
||||||
|
<small style={{ color: '#77aa77', fontSize: '0.6em' }}>
|
||||||
{/* VAT */}
|
(incl. {product.vat}% MwSt.,*)
|
||||||
<Typography variant="body2" sx={{ color: '#77aa77', fontSize: '0.75em' }}>
|
</small>
|
||||||
(incl. 19% MwSt.,*)
|
)}
|
||||||
</Typography>
|
</Typography>
|
||||||
|
|
||||||
{/* Selection Indicator */}
|
{/* Selection Indicator - Separate line */}
|
||||||
{this.state.selectedTentSize === product.id && (
|
{this.state.selectedTentSize === product.id && (
|
||||||
<Box sx={{ mt: 1, textAlign: 'center' }}>
|
<Typography variant="body2" sx={{
|
||||||
<Typography variant="body2" sx={{ color: '#2e7d32', fontWeight: 'bold' }}>
|
color: '#2e7d32',
|
||||||
✓ Ausgewählt
|
fontWeight: 'bold',
|
||||||
</Typography>
|
mt: 1,
|
||||||
</Box>
|
textAlign: 'center'
|
||||||
|
}}>
|
||||||
|
✓ Ausgewählt
|
||||||
|
</Typography>
|
||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
@@ -621,17 +675,128 @@ class GrowTentKonfigurator extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
renderLightSection() {
|
renderLightSection() {
|
||||||
const { selectedLightType } = this.state;
|
const { selectedLightType, selectedTentShape } = this.state;
|
||||||
|
|
||||||
|
if (!selectedTentShape) {
|
||||||
|
return (
|
||||||
|
<Box sx={{ mt: 4 }}>
|
||||||
|
<Typography variant="h2" component="h2" gutterBottom sx={{ color: '#2e7d32', fontWeight: 'bold' }}>
|
||||||
|
3. Beleuchtung wählen
|
||||||
|
</Typography>
|
||||||
|
<Typography variant="body2" color="text.secondary">
|
||||||
|
Bitte wählen Sie zuerst eine Zeltgröße aus.
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get real lamps for selected tent shape
|
||||||
|
const availableLamps = this.getLampsForTentShape(selectedTentShape);
|
||||||
|
const cachedData = getCachedCategoryData('Lampen');
|
||||||
|
|
||||||
|
if (!cachedData) {
|
||||||
|
return (
|
||||||
|
<Box sx={{ mt: 4 }}>
|
||||||
|
<Typography variant="h2" component="h2" gutterBottom sx={{ color: '#2e7d32', fontWeight: 'bold' }}>
|
||||||
|
3. Beleuchtung wählen - {selectedTentShape}
|
||||||
|
</Typography>
|
||||||
|
<Typography variant="body2" color="text.secondary">
|
||||||
|
Lade Beleuchtungs-Produkte...
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (availableLamps.length === 0) {
|
||||||
|
return (
|
||||||
|
<Box sx={{ mt: 4 }}>
|
||||||
|
<Typography variant="h2" component="h2" gutterBottom sx={{ color: '#2e7d32', fontWeight: 'bold' }}>
|
||||||
|
3. Beleuchtung wählen - {selectedTentShape}
|
||||||
|
</Typography>
|
||||||
|
<Typography variant="body2" color="text.secondary">
|
||||||
|
Keine passenden Lampen für Zeltgröße {selectedTentShape} verfügbar.
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ProductSelector
|
<Box sx={{ mt: 4 }}>
|
||||||
products={lightTypes}
|
<Typography variant="h2" component="h2" gutterBottom sx={{ color: '#2e7d32', fontWeight: 'bold' }}>
|
||||||
selectedValue={selectedLightType}
|
3. Beleuchtung wählen - {selectedTentShape}
|
||||||
onSelect={this.handleLightTypeSelect}
|
</Typography>
|
||||||
productType="light"
|
<Grid container spacing={2}>
|
||||||
title="3. Beleuchtung wählen"
|
{availableLamps.map((lamp) => (
|
||||||
gridSize={{ xs: 12, sm: 6 }}
|
<Grid item xs={12} sm={6} md={4} key={lamp.id}>
|
||||||
/>
|
<Box sx={{
|
||||||
|
width: { xs: '100%', sm: '250px' },
|
||||||
|
height: '100%',
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
borderRadius: '8px',
|
||||||
|
overflow: 'hidden',
|
||||||
|
cursor: 'pointer',
|
||||||
|
border: '2px solid',
|
||||||
|
borderColor: selectedLightType === lamp.id ? '#2e7d32' : '#e0e0e0',
|
||||||
|
backgroundColor: selectedLightType === lamp.id ? '#f1f8e9' : '#ffffff',
|
||||||
|
'&:hover': {
|
||||||
|
boxShadow: 6,
|
||||||
|
borderColor: '#2e7d32'
|
||||||
|
},
|
||||||
|
transition: 'all 0.3s ease'
|
||||||
|
}}
|
||||||
|
onClick={() => this.handleLightTypeSelect(lamp.id)}
|
||||||
|
>
|
||||||
|
{/* Image */}
|
||||||
|
<Box sx={{ height: 200, display: 'flex', alignItems: 'center', justifyContent: 'center', overflow: 'hidden' }}>
|
||||||
|
{this.renderTentImage(lamp)}
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
{/* Content */}
|
||||||
|
<Box sx={{ p: 2, flexGrow: 1, display: 'flex', flexDirection: 'column' }}>
|
||||||
|
{/* Name */}
|
||||||
|
<Typography variant="h6" gutterBottom sx={{ fontWeight: 'bold' }}>
|
||||||
|
{lamp.name}
|
||||||
|
</Typography>
|
||||||
|
|
||||||
|
{/* Price with VAT */}
|
||||||
|
<Typography variant="h6" sx={{
|
||||||
|
color: '#2e7d32',
|
||||||
|
fontWeight: 'bold',
|
||||||
|
mt: 2,
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
gap: 1,
|
||||||
|
flexWrap: 'wrap'
|
||||||
|
}}>
|
||||||
|
<span>{lamp.price ? new Intl.NumberFormat('de-DE', {
|
||||||
|
style: 'currency',
|
||||||
|
currency: 'EUR'
|
||||||
|
}).format(lamp.price) : 'Kein Preis'}</span>
|
||||||
|
{lamp.vat && (
|
||||||
|
<small style={{ color: '#77aa77', fontSize: '0.6em' }}>
|
||||||
|
(incl. {lamp.vat}% MwSt.,*)
|
||||||
|
</small>
|
||||||
|
)}
|
||||||
|
</Typography>
|
||||||
|
|
||||||
|
{/* Selection Indicator */}
|
||||||
|
{selectedLightType === lamp.id && (
|
||||||
|
<Typography variant="body2" sx={{
|
||||||
|
color: '#2e7d32',
|
||||||
|
fontWeight: 'bold',
|
||||||
|
mt: 1,
|
||||||
|
textAlign: 'center'
|
||||||
|
}}>
|
||||||
|
✓ Ausgewählt
|
||||||
|
</Typography>
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
</Grid>
|
||||||
|
))}
|
||||||
|
</Grid>
|
||||||
|
</Box>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -676,7 +841,8 @@ class GrowTentKonfigurator extends Component {
|
|||||||
selectedTent = tents.find(t => t.id === selectedTentSize);
|
selectedTent = tents.find(t => t.id === selectedTentSize);
|
||||||
if (selectedTent) break;
|
if (selectedTent) break;
|
||||||
}
|
}
|
||||||
const selectedLight = lightTypes.find(l => l.id === selectedLightType);
|
const availableLamps = this.state.selectedTentShape ? this.getLampsForTentShape(this.state.selectedTentShape) : [];
|
||||||
|
const selectedLight = availableLamps.find(l => l.id === selectedLightType);
|
||||||
const selectedVentilation = ventilationTypes.find(v => v.id === selectedVentilationType);
|
const selectedVentilation = ventilationTypes.find(v => v.id === selectedVentilationType);
|
||||||
const selectedExtrasItems = extras.filter(e => selectedExtras.includes(e.id));
|
const selectedExtrasItems = extras.filter(e => selectedExtras.includes(e.id));
|
||||||
const savingsInfo = this.calculateSavings();
|
const savingsInfo = this.calculateSavings();
|
||||||
|
|||||||
Reference in New Issue
Block a user