import React, { Component } from 'react';
import {
Container,
Paper,
Box,
Typography,
Button,
Divider,
List,
ListItem,
ListItemText,
ListItemSecondaryAction,
Grid,
CardMedia,
CircularProgress,
Stack,
} from '@mui/material';
import ShoppingCartIcon from '@mui/icons-material/ShoppingCart';
import { TentShapeSelector, ExtrasSelector } from '../components/configurator/index.js';
import { tentShapes } from '../data/configuratorData.js';
import { Link } from 'react-router-dom';
import IconButton from '@mui/material/IconButton';
import ZoomInIcon from '@mui/icons-material/ZoomIn';
function setCachedCategoryData(categoryId, data, language = 'de') {
if (!window.productCache) {
window.productCache = {};
}
if (!window.productDetailCache) {
window.productDetailCache = {};
}
try {
const cacheKey = `categoryProducts_${categoryId}_${language}`;
if(data.products) for(const product of data.products) {
const productCacheKey = `product_${product.id}_${language}`;
window.productDetailCache[productCacheKey] = product;
}
window.productCache[cacheKey] = {
...data,
timestamp: Date.now()
};
} catch (err) {
console.error('Error writing to cache:', err);
}
}
function getCachedCategoryData(categoryId, language = 'de') {
if (!window.productCache) {
window.productCache = {};
}
try {
const cacheKey = `categoryProducts_${categoryId}_${language}`;
const cachedData = window.productCache[cacheKey];
if (cachedData) {
const { timestamp } = cachedData;
const cacheAge = Date.now() - timestamp;
const tenMinutes = 10 * 60 * 1000;
if (cacheAge < tenMinutes) {
return cachedData;
}
}
} catch (err) {
console.error('Error reading from cache:', err);
}
return null;
}
import { withI18n } from '../i18n/withTranslation.js';
class GrowTentKonfigurator extends Component {
constructor(props) {
super(props);
// Try to restore state from window object
const savedState = window.growTentKonfiguratorState;
this.state = {
selectedTentShape: savedState?.selectedTentShape || '80x80',
selectedTentSize: savedState?.selectedTentSize || 'tent_80x80x160',
selectedLightType: savedState?.selectedLightType || 'led_quantum_board',
selectedVentilationType: savedState?.selectedVentilationType || 'premium_ventilation',
selectedExtras: savedState?.selectedExtras || [],
totalPrice: savedState?.totalPrice || 0,
categoryLoadStatus: {
"Zelte": false,
"Lampen": false,
"Abluft-sets": false,
"Set-zubehoer": false
}
};
this.handleTentShapeSelect = this.handleTentShapeSelect.bind(this);
this.handleTentSizeSelect = this.handleTentSizeSelect.bind(this);
this.handleLightTypeSelect = this.handleLightTypeSelect.bind(this);
this.handleVentilationSelect = this.handleVentilationSelect.bind(this);
this.handleExtraToggle = this.handleExtraToggle.bind(this);
this.calculateTotalPrice = this.calculateTotalPrice.bind(this);
this.saveStateToWindow = this.saveStateToWindow.bind(this);
this.checkCacheValidity = this.checkCacheValidity.bind(this);
this.handleAddToCart = this.handleAddToCart.bind(this);
}
saveStateToWindow() {
// Save current state to window object for backup
window.growTentKonfiguratorState = {
selectedTentShape: this.state.selectedTentShape,
selectedTentSize: this.state.selectedTentSize,
selectedLightType: this.state.selectedLightType,
selectedVentilationType: this.state.selectedVentilationType,
selectedExtras: this.state.selectedExtras,
totalPrice: this.state.totalPrice,
timestamp: Date.now() // Add timestamp for debugging
};
}
componentDidMount() {
// @note Calculate initial total price with preselected products
//this.calculateTotalPrice();
this.fetchCategoryData("Zelte");
this.fetchCategoryData("Lampen");
this.fetchCategoryData("Abluft-sets");
this.fetchCategoryData("Set-zubehoer");
this.cacheCheckInterval = setInterval(this.checkCacheValidity, 60000);
}
componentDidUpdate(prevProps, prevState) {
// Reset tent size selection if shape changes
if (prevState.selectedTentShape !== this.state.selectedTentShape && this.state.selectedTentShape !== prevState.selectedTentShape) {
this.setState({ selectedTentSize: '' });
}
// Reset ventilation selection if current selection is not deliverable for new tent shape
if (prevState.selectedTentShape !== this.state.selectedTentShape && this.state.selectedVentilationType) {
const availableVentilation = this.getVentilationForTentShape(this.state.selectedTentShape);
const currentVentilation = availableVentilation.find(v => v.id === this.state.selectedVentilationType);
if (!currentVentilation || !currentVentilation.isDeliverable) {
this.setState({ selectedVentilationType: '' });
}
}
// Recalculate total price when selections change
if (
prevState.selectedTentSize !== this.state.selectedTentSize ||
prevState.selectedLightType !== this.state.selectedLightType ||
prevState.selectedVentilationType !== this.state.selectedVentilationType ||
prevState.selectedExtras !== this.state.selectedExtras
) {
this.calculateTotalPrice();
}
// Save state to window object whenever selections change
if (
prevState.selectedTentShape !== this.state.selectedTentShape ||
prevState.selectedTentSize !== this.state.selectedTentSize ||
prevState.selectedLightType !== this.state.selectedLightType ||
prevState.selectedVentilationType !== this.state.selectedVentilationType ||
prevState.selectedExtras !== this.state.selectedExtras ||
prevState.totalPrice !== this.state.totalPrice
) {
this.saveStateToWindow();
}
// Check if language changed and reload category data
const currentLanguage = this.props.languageContext?.currentLanguage || this.props.i18n?.language;
const prevLanguage = prevProps.languageContext?.currentLanguage || prevProps.i18n?.language;
if (currentLanguage && prevLanguage && currentLanguage !== prevLanguage) {
console.log(`Language changed from ${prevLanguage} to ${currentLanguage} - reloading configurator data`);
// Reset category load status to trigger loading state UI
this.setState({
categoryLoadStatus: {
"Zelte": false,
"Lampen": false,
"Abluft-sets": false,
"Set-zubehoer": false
}
}, () => {
// Fetch data for all categories with new language
this.fetchCategoryData("Zelte");
this.fetchCategoryData("Lampen");
this.fetchCategoryData("Abluft-sets");
this.fetchCategoryData("Set-zubehoer");
});
}
}
componentWillUnmount() {
if (this.cacheCheckInterval) {
clearInterval(this.cacheCheckInterval);
}
}
fetchCategoryData(categoryId) {
const currentLanguage = this.props.languageContext?.currentLanguage || this.props.i18n?.language || 'de';
const cachedData = getCachedCategoryData(categoryId, currentLanguage);
if (cachedData) {
this.setState(prevState => ({
categoryLoadStatus: {
...prevState.categoryLoadStatus,
[categoryId]: true
}
}));
return;
}
console.log(`Cache miss for category ${categoryId}, fetching...`);
window.socketManager.off(`productList:${categoryId}`);
window.socketManager.on(`productList:${categoryId}`,(response) => {
setCachedCategoryData(categoryId, response, currentLanguage);
this.setState(prevState => ({
categoryLoadStatus: {
...prevState.categoryLoadStatus,
[categoryId]: true
}
}));
});
window.socketManager.emit(
"getCategoryProducts",
{ categoryId: categoryId, language: currentLanguage, requestTranslation: currentLanguage === 'de' ? false : true },
() => {}
);
}
checkCacheValidity() {
const categories = ["Zelte", "Lampen", "Abluft-sets", "Set-zubehoer"];
const currentLanguage = this.props.languageContext?.currentLanguage || this.props.i18n?.language || 'de';
let needsUpdate = false;
const newStatus = { ...this.state.categoryLoadStatus };
for (const categoryId of categories) {
if (newStatus[categoryId] && !getCachedCategoryData(categoryId, currentLanguage)) {
console.log(`Refetching ${categoryId} due to cache miss/expiry`);
newStatus[categoryId] = false;
needsUpdate = true;
this.fetchCategoryData(categoryId);
}
}
if (needsUpdate) {
this.setState({ categoryLoadStatus: newStatus });
}
}
handleTentShapeSelect(shapeId) {
this.setState({ selectedTentShape: shapeId });
}
handleTentSizeSelect(tentId) {
this.setState({ selectedTentSize: tentId });
}
handleLightTypeSelect(lightId) {
this.setState({ selectedLightType: lightId });
}
handleVentilationSelect(ventilationId) {
this.setState({ selectedVentilationType: ventilationId });
}
handleExtraToggle(extraId) {
const { selectedExtras } = this.state;
const newSelectedExtras = selectedExtras.includes(extraId)
? selectedExtras.filter(id => id !== extraId)
: [...selectedExtras, extraId];
this.setState({ selectedExtras: newSelectedExtras });
}
handleAddToCart() {
if (!window.cart) window.cart = [];
// Collect components
const setComponents = [];
// Tent
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 === this.state.selectedTentSize);
if (selectedTent) {
setComponents.push(selectedTent);
break;
}
}
// Light
if (this.state.selectedLightType) {
const availableLamps = this.getLampsForTentShape(this.state.selectedTentShape);
const light = availableLamps.find(l => l.id === this.state.selectedLightType);
if (light) setComponents.push(light);
}
// Ventilation
if (this.state.selectedVentilationType) {
const availableVentilation = this.getVentilationForTentShape(this.state.selectedTentShape);
const ventilation = availableVentilation.find(v => v.id === this.state.selectedVentilationType);
if (ventilation) setComponents.push(ventilation);
}
// Extras
const currentLanguage = this.props.languageContext?.currentLanguage || this.props.i18n?.language || 'de';
const extrasData = getCachedCategoryData('Set-zubehoer', currentLanguage);
if (extrasData && Array.isArray(extrasData.products)) {
this.state.selectedExtras.forEach(extraId => {
const extra = extrasData.products.find(e => e.id === extraId);
if (extra) setComponents.push(extra);
});
}
// Now push to cart
const totalPrice = this.state.totalPrice;
const setName = this.props.t ? this.props.t("kitConfig.setName", { shape: this.state.selectedTentShape }) : `Growbox Set - ${this.state.selectedTentShape}`;
window.cart.push({
id: null,
name: setName,
seoName: null,
pictureList: null,
price: totalPrice,
fGrundPreis: null,
cGrundEinheit: null,
quantity: 1,
weight: null,
vat: 19,
versandklasse: null,
availableSupplier: 0,
komponenten: setComponents,
available: 1
});
window.dispatchEvent(new CustomEvent("cart"));
}
// 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
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 product;
}); // No sorting needed
}
// Filter lamps by tent size using "passend für Zelt" attribute
filterLampsByTentSize(tentShape, products, attributes) {
if (!products || !attributes || !tentShape) return [];
// 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);
return sizeMatch && isAvailable;
});
return matchingProducts;
}
// Get lamps for selected tent shape
getLampsForTentShape(shapeId) {
const currentLanguage = this.props.languageContext?.currentLanguage || this.props.i18n?.language || 'de';
const cachedData = getCachedCategoryData('Lampen', currentLanguage);
if (!cachedData || !cachedData.products || !cachedData.attributes) {
return [];
}
return this.filterLampsByTentSize(shapeId, cachedData.products, cachedData.attributes);
}
// Get ventilation products for selected tent shape
getVentilationForTentShape(shapeId) {
const currentLanguage = this.props.languageContext?.currentLanguage || this.props.i18n?.language || 'de';
const cachedData = getCachedCategoryData('Abluft-sets', currentLanguage);
if (!cachedData || !cachedData.products || !cachedData.attributes) {
return [];
}
return this.filterVentilationByTentSize(shapeId, cachedData.products, cachedData.attributes);
}
// Filter ventilation by tent size using "passend für Zelt" attribute
filterVentilationByTentSize(tentShape, products, attributes) {
if (!products || !attributes || !tentShape) return [];
// 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
// Include all size-matching products, but mark availability status
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;
// Only filter by tent size match, not by availability
const sizeMatch = tentSizeAttr === tentShape;
return sizeMatch;
}).map(product => {
// Add availability flag to each product for UI rendering
const isAvailable = (product.available > 0) || (product.availableSupplier === 1);
return {
...product,
isDeliverable: isAvailable
};
});
return matchingProducts;
}
// Helper function to generate coverage descriptions
getCoverageDescription(width, depth) {
const { t } = this.props;
const area = width * depth;
if (area <= 3600) return t ? t('kitConfig.plants1to2') : '1-2 Pflanzen'; // 60x60
if (area <= 6400) return t ? t('kitConfig.plants2to4') : '2-4 Pflanzen'; // 80x80
if (area <= 10000) return t ? t('kitConfig.plants4to6') : '4-6 Pflanzen'; // 100x100
return t ? t('kitConfig.plants3to6') : '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/avif' }));
this.forceUpdate();
}
this.loadingImages.delete(bildId);
});
}
return (
);
}
// Get real tent products for the selected shape
getTentsForShape(shapeId) {
const currentLanguage = this.props.languageContext?.currentLanguage || this.props.i18n?.language || 'de';
const cachedData = getCachedCategoryData('Zelte', currentLanguage);
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;
let itemCount = 0;
// Add tent price
if (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;
}
if (selectedTent) {
total += selectedTent.price;
itemCount++;
}
}
// Add light price
if (selectedLightType) {
const availableLamps = this.getLampsForTentShape(this.state.selectedTentShape);
const light = availableLamps.find(l => l.id === selectedLightType);
if (light && light.price) {
total += light.price;
itemCount++;
}
}
// Add ventilation price
if (selectedVentilationType) {
const availableVentilation = this.getVentilationForTentShape(this.state.selectedTentShape);
const ventilation = availableVentilation.find(v => v.id === selectedVentilationType);
if (ventilation && ventilation.price && ventilation.isDeliverable) {
total += ventilation.price;
itemCount++;
}
}
const currentLanguage = this.props.languageContext?.currentLanguage || this.props.i18n?.language || 'de';
const extrasData = getCachedCategoryData('Set-zubehoer', currentLanguage);
if (!extrasData || !Array.isArray(extrasData.products)) {
console.warn('Extras data not available; skipping extras price in total calculation');
} else {
// Add extras prices
selectedExtras.forEach(extraId => {
const extra = extrasData.products.find(e => e.id === extraId);
if (extra) {
total += extra.price;
itemCount++;
}
});
}
// Apply bundle discount
let discountPercentage = 0;
if (itemCount >= 3) discountPercentage = 15; // 15% for 3+ items
if (itemCount >= 5) discountPercentage = 22; // 22% for 5+ items
if (itemCount >= 7) discountPercentage = 28; // 28% for 7+ items
const discountedTotal = total * (1 - discountPercentage / 100);
this.setState({ totalPrice: discountedTotal });
}
formatPrice(price) {
return new Intl.NumberFormat('de-DE', {
style: 'currency',
currency: 'EUR'
}).format(price);
}
calculateSavings() {
// Bundle discount calculation
const { selectedTentSize, selectedLightType, selectedVentilationType, selectedExtras } = this.state;
let itemCount = 0;
let originalTotal = 0;
// Calculate original total without discount
if (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;
}
if (selectedTent) {
originalTotal += selectedTent.price;
itemCount++;
}
}
if (selectedLightType) {
const availableLamps = this.getLampsForTentShape(this.state.selectedTentShape);
const light = availableLamps.find(l => l.id === selectedLightType);
if (light && light.price) {
originalTotal += light.price;
itemCount++;
}
}
if (selectedVentilationType) {
const availableVentilation = this.getVentilationForTentShape(this.state.selectedTentShape);
const ventilation = availableVentilation.find(v => v.id === selectedVentilationType);
if (ventilation && ventilation.price && ventilation.isDeliverable) {
originalTotal += ventilation.price;
itemCount++;
}
}
const currentLanguage = this.props.languageContext?.currentLanguage || this.props.i18n?.language || 'de';
const extrasData = getCachedCategoryData('Set-zubehoer', currentLanguage);
if (!extrasData || !Array.isArray(extrasData.products)) {
console.warn('Extras data not available; skipping extras in savings calculation');
} else {
selectedExtras.forEach(extraId => {
const extra = extrasData.products.find(e => e.id === extraId);
if (extra) {
originalTotal += extra.price;
itemCount++;
}
});
}
// Progressive discount based on number of selected items
let discountPercentage = 0;
if (itemCount >= 3) discountPercentage = 15; // 15% for 3+ items
if (itemCount >= 5) discountPercentage = 22; // 22% for 5+ items
if (itemCount >= 7) discountPercentage = 28; // 28% for 7+ items
const savings = originalTotal * (discountPercentage / 100);
return {
savings: savings,
discountPercentage: discountPercentage,
hasDiscount: discountPercentage > 0
};
}
renderTentShapeSection() {
const { selectedTentShape } = this.state;
const { t } = this.props;
return (
);
}
renderTentSizeSection() {
const { selectedTentShape } = this.state;
const { t } = this.props;
if (!selectedTentShape) {
return null; // Don't show tent sizes until shape is selected
}
if (!this.state.categoryLoadStatus["Zelte"]) {
return (
{t ? t("kitConfig.loadingProducts") : "Lade Growbox-Produkte..."}
);
}
// Get real filtered tent products for the selected shape
const filteredTents = this.getTentsForShape(selectedTentShape);
if (filteredTents.length === 0) {
return (
{t ? t("kitConfig.noProductsAvailable") : "Keine Produkte für diese Größe verfügbar"}
);
}
return (
{t ? t("kitConfig.selectProductTitle") : "2. Growbox Produkt auswählen"}
{t ? t("kitConfig.selectProductSubtitle", { shape: selectedTentShape }) : `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}
{product.kurzBeschreibung}
{/* Price with VAT - Same as Product.js */}
{product.price ? new Intl.NumberFormat('de-DE', {
style: 'currency',
currency: 'EUR'
}).format(product.price) : (t ? t("kitConfig.noPrice") : 'Kein Preis')}
{product.vat && (
({t ? t("product.inclVat", { vat: product.vat }) : `incl. ${product.vat}% MwSt.,*`})
)}
{/* Selection Indicator - Separate line */}
{this.state.selectedTentSize === product.id && (
{t ? t("kitConfig.selected") : "✓ Ausgewählt"}
)}
event.stopPropagation()}
>
))}
);
}
renderLightSection() {
const { selectedLightType, selectedTentShape } = this.state;
const { t } = this.props;
if (!selectedTentShape) {
return (
{t ? t("kitConfig.selectLightingTitle") : "3. Beleuchtung wählen"}
{t ? t("kitConfig.selectLightingSubtitle") : "Bitte wählen Sie zuerst eine Zeltgröße aus."}
);
}
if (!this.state.categoryLoadStatus["Lampen"]) {
return (
{t ? t("kitConfig.selectLightingTitleShape", { shape: selectedTentShape }) : `3. Beleuchtung wählen - ${selectedTentShape}`}
{t ? t("kitConfig.loadingLighting") : "Lade Beleuchtungs-Produkte..."}
);
}
const availableLamps = this.getLampsForTentShape(selectedTentShape);
if (availableLamps.length === 0) {
return (
{t ? t("kitConfig.selectLightingTitleShape", { shape: selectedTentShape }) : `3. Beleuchtung wählen - ${selectedTentShape}`}
{t ? t("kitConfig.noLightingAvailable", { shape: selectedTentShape }) : `Keine passenden Lampen für Zeltgröße ${selectedTentShape} verfügbar.`}
);
}
return (
{t ? t("kitConfig.selectLightingTitleShape", { shape: selectedTentShape }) : `3. Beleuchtung wählen - ${selectedTentShape}`}
{availableLamps.map((lamp) => (
this.handleLightTypeSelect(lamp.id)}
>
{/* Image */}
{this.renderTentImage(lamp)}
{/* Content */}
{/* Name */}
{lamp.name}
{lamp.kurzBeschreibung}
{/* Price with VAT */}
{lamp.price ? new Intl.NumberFormat('de-DE', {
style: 'currency',
currency: 'EUR'
}).format(lamp.price) : (t ? t("kitConfig.noPrice") : 'Kein Preis')}
{lamp.vat && (
({t ? t("product.inclVat", { vat: lamp.vat }) : `incl. ${lamp.vat}% MwSt.,*`})
)}
{/* Selection Indicator */}
{selectedLightType === lamp.id && (
{t ? t("kitConfig.selected") : "✓ Ausgewählt"}
)}
event.stopPropagation()}
>
))}
);
}
renderVentilationSection() {
const { selectedVentilationType, selectedTentShape } = this.state;
const { t } = this.props;
if (!selectedTentShape) {
return (
{t ? t("kitConfig.selectVentilationTitle") : "4. Belüftung auswählen"}
{t ? t("kitConfig.selectVentilationSubtitle") : "Bitte wählen Sie zuerst eine Zeltgröße aus."}
);
}
if (!this.state.categoryLoadStatus["Abluft-sets"]) {
return (
{t ? t("kitConfig.selectVentilationTitleShape", { shape: selectedTentShape }) : `4. Belüftung auswählen - ${selectedTentShape}`}
{t ? t("kitConfig.loadingVentilation") : "Lade Belüftungs-Produkte..."}
);
}
const availableVentilation = this.getVentilationForTentShape(selectedTentShape);
if (availableVentilation.length === 0) {
return (
{t ? t("kitConfig.selectVentilationTitleShape", { shape: selectedTentShape }) : `4. Belüftung auswählen - ${selectedTentShape}`}
{t ? t("kitConfig.noVentilationAvailable", { shape: selectedTentShape }) : `Keine passenden Belüftung für Zeltgröße ${selectedTentShape} verfügbar.`}
);
}
return (
{t ? t("kitConfig.selectVentilationTitleShape", { shape: selectedTentShape }) : `4. Belüftung auswählen - ${selectedTentShape}`}
{availableVentilation.map((ventilation) => (
{
if (ventilation.isDeliverable) {
this.handleVentilationSelect(ventilation.id);
}
}}
>
{/* Non-deliverable overlay */}
{!ventilation.isDeliverable && (
{t ? t("kitConfig.notDeliverable") : "Nicht lieferbar"}
)}
{/* Image */}
{this.renderTentImage(ventilation)}
{/* Content */}
{/* Name */}
{ventilation.name}
{ventilation.kurzBeschreibung}
{/* Price with VAT */}
{ventilation.price ? new Intl.NumberFormat('de-DE', {
style: 'currency',
currency: 'EUR'
}).format(ventilation.price) : (t ? t("kitConfig.noPrice") : 'Kein Preis')}
{ventilation.vat && (
({t ? t("product.inclVat", { vat: ventilation.vat }) : `incl. ${ventilation.vat}% MwSt.,*`})
)}
{/* Selection Indicator */}
{selectedVentilationType === ventilation.id && ventilation.isDeliverable && (
{t ? t("kitConfig.selected") : "✓ Ausgewählt"}
)}
event.stopPropagation()}
>
))}
);
}
renderExtrasSection() {
const { selectedExtras } = this.state;
const { t } = this.props;
if (!this.state.categoryLoadStatus["Set-zubehoer"]) {
return (
{t ? t("kitConfig.selectExtrasTitle") : "5. Extras hinzufügen (optional)"}
{t ? t("kitConfig.loadingExtras") : "Lade Extras..."}
);
}
const currentLanguage = this.props.languageContext?.currentLanguage || this.props.i18n?.language || 'de';
const extrasData = getCachedCategoryData('Set-zubehoer', currentLanguage);
if (!extrasData) {
return (
{t ? t("kitConfig.selectExtrasTitle") : "5. Extras hinzufügen (optional)"}
{t ? t("kitConfig.noExtrasAvailable") : "Keine Extras verfügbar"}
);
}
const extras = Array.isArray(extrasData.products) ? extrasData.products : [];
if (extras.length === 0) {
return (
{t ? t("kitConfig.selectExtrasTitle") : "5. Extras hinzufügen (optional)"}
{t ? t("kitConfig.noExtrasAvailable") : "Keine Extras verfügbar"}
);
}
return (
);
}
renderInlineSummary() {
const { selectedTentSize, selectedLightType, selectedVentilationType, selectedExtras, totalPrice } = this.state;
const { t } = this.props;
// 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 availableLamps = this.state.selectedTentShape ? this.getLampsForTentShape(this.state.selectedTentShape) : [];
const selectedLight = availableLamps.find(l => l.id === selectedLightType);
const availableVentilation = this.state.selectedTentShape ? this.getVentilationForTentShape(this.state.selectedTentShape) : [];
const selectedVentilation = availableVentilation.find(v => v.id === selectedVentilationType && v.isDeliverable);
const currentLanguage = this.props.languageContext?.currentLanguage || this.props.i18n?.language || 'de';
const extrasData = getCachedCategoryData('Set-zubehoer', currentLanguage);
const selectedExtrasItems = Array.isArray(extrasData?.products)
? extrasData.products.filter(e => selectedExtras.includes(e.id))
: [];
const savingsInfo = this.calculateSavings();
return (
{t ? t("kitConfig.yourConfiguration") : "🎯 Ihre Konfiguration"}
{selectedTent && (
{this.formatPrice(selectedTent.price)}
{selectedTent.vat && (
({t ? t("product.inclVat", { vat: selectedTent.vat }) : `incl. ${selectedTent.vat}% MwSt.,*`})
)}
)}
{selectedLight && (
{this.formatPrice(selectedLight.price)}
{selectedLight.vat && (
({t ? t("product.inclVat", { vat: selectedLight.vat }) : `incl. ${selectedLight.vat}% MwSt.,*`})
)}
)}
{selectedVentilation && (
{this.formatPrice(selectedVentilation.price)}
{selectedVentilation.vat && (
({t ? t("product.inclVat", { vat: selectedVentilation.vat }) : `incl. ${selectedVentilation.vat}% MwSt.,*`})
)}
)}
{selectedExtrasItems.map(extra => (
{this.formatPrice(extra.price)}
{extra.vat && (
({t ? t("product.inclVat", { vat: extra.vat }) : `incl. ${extra.vat}% MwSt.,*`})
)}
))}
{savingsInfo.hasDiscount && (
{t ? t("product.youSave", { amount: this.formatPrice(savingsInfo.savings) }) : `Sie sparen: ${this.formatPrice(savingsInfo.savings)}`} ({savingsInfo.discountPercentage}% Bundle-Rabatt)
({t ? t("product.inclVat", { vat: 19 }) : "incl. 19% MwSt.,*"})
)}
{t ? t("kitConfig.totalPrice") : "Gesamtpreis:"}
{this.formatPrice(totalPrice)}
({t ? t("product.inclVat", { vat: 19 }) : "incl. 19% MwSt.,*"})
}
sx={{
bgcolor: '#2e7d32',
'&:hover': { bgcolor: '#1b5e20' },
minWidth: 250
}}
>
{t ? t("kitConfig.addToCart") : "In den Warenkorb"}
);
}
/*
window.cart.push({
id: null,
name: "Set 80x80",
seoName: nnull,
pictureList: null,
price: total,
fGrundPreis: null,
cGrundEinheit: null,
quantity: 1,
weight: null,
vat: vat,
versandklasse: null,
availableSupplier: null,
komponenten: setComponents,
available: null
});
*/
render() {
const { t } = this.props;
return (
{t ? t("kitConfig.pageTitle") : "🌱 Growbox Konfigurator"}
{t ? t("kitConfig.pageSubtitle") : "Stelle dein perfektes Indoor Grow Setup zusammen"}
{/* Bundle Discount Information */}
{t ? t("kitConfig.bundleDiscountTitle") : "🎯 Bundle-Rabatt sichern!"}
15%
{/* Note: Translation key would be: product.discount.from3Products */}
{t ? t("product.discount.from3Products") : "ab 3 Produkten"}
22%
{/* Note: Translation key would be: product.discount.from5Products */}
{t ? t("product.discount.from5Products") : "ab 5 Produkten"}
28%
{/* Note: Translation key would be: product.discount.from7Products */}
{t ? t("product.discount.from7Products") : "ab 7 Produkten"}
{/* Note: Translation key would be: product.discount.moreProductsMoreSavings */}
{t ? t("product.discount.moreProductsMoreSavings") : "Je mehr Produkte du auswählst, desto mehr sparst du!"}
{this.renderTentShapeSection()}
{this.renderTentSizeSection()}
{this.state.selectedTentShape && }
{this.renderLightSection()}
{this.renderVentilationSection()}
{this.renderExtrasSection()}
{/* Inline summary section - expands when scrolling to bottom */}
{this.renderInlineSummary()}
);
}
}
export default withI18n()(GrowTentKonfigurator);