feat: enhance ProductDetailPage to support partial data loading and improve user feedback with loading descriptions
This commit is contained in:
47
.vscode/launch.json
vendored
47
.vscode/launch.json
vendored
@@ -16,7 +16,8 @@
|
||||
"skipFiles": [
|
||||
"<node_internals>/**"
|
||||
]
|
||||
},{
|
||||
},
|
||||
{
|
||||
"name": "Start",
|
||||
"type": "node-terminal",
|
||||
"request": "launch",
|
||||
@@ -28,6 +29,50 @@
|
||||
"skipFiles": [
|
||||
"<node_internals>/**"
|
||||
]
|
||||
},
|
||||
{
|
||||
"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": [
|
||||
"<node_internals>/**",
|
||||
"${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": [
|
||||
"<node_internals>/**",
|
||||
"${workspaceFolder}/node_modules/**",
|
||||
"${workspaceFolder}/dist/**"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
</Box>
|
||||
|
||||
{/* Product full description */}
|
||||
{product.description && (
|
||||
{(product.description || upgrading) && (
|
||||
<Box
|
||||
sx={{
|
||||
mt: 4,
|
||||
@@ -995,7 +1062,15 @@ class ProductDetailPage extends Component {
|
||||
"& strong": { fontWeight: 600 },
|
||||
}}
|
||||
>
|
||||
{parse(product.description)}
|
||||
{product.description ? (
|
||||
parse(product.description)
|
||||
) : upgrading ? (
|
||||
<Box sx={{ textAlign: "center", py: 2 }}>
|
||||
<Typography variant="body1" color="text.secondary">
|
||||
{this.props.t ? this.props.t('product.loadingDescription') : 'Produktbeschreibung wird geladen...'}
|
||||
</Typography>
|
||||
</Box>
|
||||
) : null}
|
||||
</Box>
|
||||
</Box>
|
||||
)}
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user