diff --git a/.vscode/launch.json b/.vscode/launch.json index 0fc92d9..187ab99 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -16,7 +16,8 @@ "skipFiles": [ "/**" ] - },{ + }, + { "name": "Start", "type": "node-terminal", "request": "launch", @@ -28,6 +29,50 @@ "skipFiles": [ "/**" ] + }, + { + "type": "chrome", + "request": "launch", + "name": "Launch Chrome - Debug React App", + "url": "https://dev.seedheads.de", + "webRoot": "${workspaceFolder}/src", + "sourceMapPathOverrides": { + "webpack://reactshop/./src/*": "${webRoot}/*", + "webpack://reactshop/src/*": "${webRoot}/*", + "webpack:///src/*": "${webRoot}/*", + "webpack:///./src/*": "${webRoot}/*", + "webpack:///./*": "${workspaceFolder}/*", + "webpack:///./~/*": "${workspaceFolder}/node_modules/*", + "webpack://*": "${workspaceFolder}/*" + }, + "smartStep": true, + "skipFiles": [ + "/**", + "${workspaceFolder}/node_modules/**", + "${workspaceFolder}/dist/**" + ] + }, + { + "type": "chrome", + "request": "attach", + "name": "Attach to Chrome - Debug React App", + "port": 9222, + "webRoot": "${workspaceFolder}/src", + "sourceMapPathOverrides": { + "webpack://reactshop/./src/*": "${webRoot}/*", + "webpack://reactshop/src/*": "${webRoot}/*", + "webpack:///src/*": "${webRoot}/*", + "webpack:///./src/*": "${webRoot}/*", + "webpack:///./*": "${workspaceFolder}/*", + "webpack:///./~/*": "${workspaceFolder}/node_modules/*", + "webpack://*": "${workspaceFolder}/*" + }, + "smartStep": true, + "skipFiles": [ + "/**", + "${workspaceFolder}/node_modules/**", + "${workspaceFolder}/dist/**" + ] } ] } diff --git a/src/components/ProductDetailPage.js b/src/components/ProductDetailPage.js index 8977452..524f352 100644 --- a/src/components/ProductDetailPage.js +++ b/src/components/ProductDetailPage.js @@ -21,12 +21,34 @@ class ProductDetailPage extends Component { constructor(props) { super(props); - if ( - window.productDetailCache && - window.productDetailCache[this.props.seoName] - ) { - const cachedData = window.productDetailCache[this.props.seoName]; - + // First try to find cached data by seoName (complete data) + let cachedData = null; + let partialProduct = null; + let isUpgrading = false; + + if (window.productDetailCache && window.productDetailCache[this.props.seoName]) { + cachedData = window.productDetailCache[this.props.seoName]; + } else if (window.productDetailCache) { + // If not found by seoName, search for partial data by checking all cached products + // Look for a product where the seoName matches this.props.seoName + for (const key in window.productDetailCache) { + const cached = window.productDetailCache[key]; + if (cached && cached.seoName === this.props.seoName) { + partialProduct = cached; + isUpgrading = true; + break; + } + // Also check if cached is a product object directly (from category cache) + if (cached && typeof cached === 'object' && cached.seoName === this.props.seoName) { + partialProduct = cached; + isUpgrading = true; + break; + } + } + } + + if (cachedData) { + // Complete cached data found // Clean up prerender fallback since we have cached data if (typeof window !== "undefined" && window.__PRERENDER_FALLBACK__) { delete window.__PRERENDER_FALLBACK__; @@ -46,6 +68,7 @@ class ProductDetailPage extends Component { this.state = { product: cachedData.product, loading: false, + upgrading: false, error: null, attributeImages: {}, attributes: cachedData.attributes || [], @@ -62,10 +85,52 @@ class ProductDetailPage extends Component { showRatingForm: false, showAvailabilityForm: false }; + } else if (partialProduct && isUpgrading) { + // Partial product data found - enter upgrading state + console.log("ProductDetailPage: Found partial product data, entering upgrading state"); + + // Clean up prerender fallback since we have some data + if (typeof window !== "undefined" && window.__PRERENDER_FALLBACK__) { + delete window.__PRERENDER_FALLBACK__; + console.log("ProductDetailPage: Cleaned up prerender fallback using partial product data"); + } + + // Initialize komponenten from partial product data if available + const komponenten = []; + if(partialProduct.komponenten) { + for(const komponent of partialProduct.komponenten.split(",")) { + // Handle both "x" and "×" as separators + const [id, count] = komponent.split(/[x×]/); + komponenten.push({id: id.trim(), count: count.trim()}); + } + } + + this.state = { + product: partialProduct, + loading: false, + upgrading: true, // This indicates we have partial data and are loading complete data + error: null, + attributeImages: {}, + attributes: [], // Will be loaded when upgrading + isSteckling: false, + imageDialogOpen: false, + komponenten: komponenten, + komponentenLoaded: komponenten.length === 0, // If no komponenten, mark as loaded + komponentenData: {}, // Store individual komponent data with loading states + komponentenImages: {}, // Store tiny pictures for komponenten + totalKomponentenPrice: 0, + totalSavings: 0, + // Collapsible sections state + showQuestionForm: false, + showRatingForm: false, + showAvailabilityForm: false + }; } else { + // No cached data found - full loading state this.state = { product: null, loading: true, + upgrading: false, error: null, attributeImages: {}, attributes: [], @@ -86,8 +151,8 @@ class ProductDetailPage extends Component { } componentDidMount() { - // Only load product data if not already cached - if (!this.state.product) { + // Load product data if we have no product or if we're in upgrading state + if (!this.state.product || this.state.upgrading) { this.loadProductData(); } else { // Product is cached, but we still need to load komponenten if they exist @@ -102,7 +167,7 @@ class ProductDetailPage extends Component { componentDidUpdate(prevProps) { if (prevProps.seoName !== this.props.seoName) this.setState( - { product: null, loading: true, error: null, imageDialogOpen: false }, + { product: null, loading: true, upgrading: false, error: null, imageDialogOpen: false }, this.loadProductData ); } @@ -373,6 +438,7 @@ class ProductDetailPage extends Component { this.setState({ product: res.product, loading: false, + upgrading: false, // Clear upgrading state since we now have complete data error: null, imageDialogOpen: false, attributes: res.attributes, @@ -526,14 +592,15 @@ class ProductDetailPage extends Component { }; render() { - const { product, loading, error, attributeImages, isSteckling, attributes, komponentenLoaded, komponentenData, komponentenImages, totalKomponentenPrice, totalSavings } = + const { product, loading, upgrading, error, attributeImages, isSteckling, attributes, komponentenLoaded, komponentenData, komponentenImages, totalKomponentenPrice, totalSavings } = this.state; // Debug alerts removed - if (loading) { + if (loading && !upgrading) { + // Only show full loading screen when we have no product data at all // Check if prerender fallback is available if (typeof window !== "undefined" && window.__PRERENDER_FALLBACK__) { return ( @@ -977,7 +1044,7 @@ class ProductDetailPage extends Component { {/* Product full description */} - {product.description && ( + {(product.description || upgrading) && ( - {parse(product.description)} + {product.description ? ( + parse(product.description) + ) : upgrading ? ( + + + {this.props.t ? this.props.t('product.loadingDescription') : 'Produktbeschreibung wird geladen...'} + + + ) : null} )} diff --git a/src/i18n/locales/de/product.js b/src/i18n/locales/de/product.js index 9c389eb..ae2a0d3 100644 --- a/src/i18n/locales/de/product.js +++ b/src/i18n/locales/de/product.js @@ -1,5 +1,6 @@ export default { "loading": "Produkt wird geladen...", + "loadingDescription": "Produktbeschreibung wird geladen...", "notFound": "Produkt nicht gefunden", "notFoundDescription": "Das gesuchte Produkt existiert nicht oder wurde entfernt.", "backToHome": "Zurück zur Startseite", diff --git a/src/i18n/locales/en/product.js b/src/i18n/locales/en/product.js index 4a38cf7..9c8476b 100644 --- a/src/i18n/locales/en/product.js +++ b/src/i18n/locales/en/product.js @@ -1,5 +1,6 @@ export default { "loading": "Loading product...", // Produkt wird geladen... + "loadingDescription": "Loading product description...", // Produktbeschreibung wird geladen... "notFound": "Product not found", // Produkt nicht gefunden "notFoundDescription": "The product you are looking for does not exist or has been removed.", // Das gesuchte Produkt existiert nicht oder wurde entfernt. "backToHome": "Back to homepage", // Zurück zur Startseite diff --git a/src/services/SocketManager.js b/src/services/SocketManager.js index 9b2fbe1..11624c4 100644 --- a/src/services/SocketManager.js +++ b/src/services/SocketManager.js @@ -4,8 +4,8 @@ import { io } from 'socket.io-client'; class SocketManager { constructor() { this.socket = io('', { - transports: ["websocket"], - autoConnect: false + transports: ["websocket", "polling"], + autoConnect: false }); this.emit = this.emit.bind(this);