Refactor PrerenderProduct layout: Introduced a back button for navigation, improved styling with Box components, and ensured consistent heights for product details and price sections. Enhanced product display with fixed height placeholders and updated availability messaging for better user experience.
This commit is contained in:
@@ -90,178 +90,322 @@ class PrerenderProduct extends React.Component {
|
|||||||
flexGrow: 1
|
flexGrow: 1
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
// Back button (breadcrumbs section)
|
||||||
React.createElement(
|
React.createElement(
|
||||||
Box,
|
Box,
|
||||||
{
|
{
|
||||||
sx: {
|
sx: {
|
||||||
|
mb: 2,
|
||||||
|
position: ["-webkit-sticky", "sticky"],
|
||||||
|
top: {
|
||||||
|
xs: "80px",
|
||||||
|
sm: "80px",
|
||||||
|
md: "80px",
|
||||||
|
lg: "80px",
|
||||||
|
},
|
||||||
|
left: 0,
|
||||||
|
width: "100%",
|
||||||
display: "flex",
|
display: "flex",
|
||||||
flexDirection: { xs: "column", md: "row" },
|
zIndex: 999, // Just below the AppBar
|
||||||
gap: 4,
|
py: 0,
|
||||||
background: "#fff",
|
px: 2,
|
||||||
borderRadius: 2,
|
|
||||||
boxShadow: "0 2px 8px rgba(0,0,0,0.08)",
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// Product Image Section
|
|
||||||
React.createElement(
|
React.createElement(
|
||||||
Box,
|
Box,
|
||||||
{
|
{
|
||||||
sx: {
|
sx: {
|
||||||
width: { xs: "100%", sm: "555px" },
|
ml: { xs: 0, md: 0 },
|
||||||
maxWidth: "100%",
|
display: "inline-flex",
|
||||||
minHeight: "400px",
|
px: 0,
|
||||||
background: "#f8f8f8",
|
py: 1,
|
||||||
display: "flex",
|
backgroundColor: "#2e7d32", // primary dark green
|
||||||
flexDirection: "column",
|
borderRadius: 1,
|
||||||
alignItems: "center",
|
|
||||||
justifyContent: "center",
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
React.createElement(
|
|
||||||
CardMedia,
|
|
||||||
{
|
|
||||||
component: 'img',
|
|
||||||
height: '400',
|
|
||||||
image: mainImage,
|
|
||||||
alt: product.name,
|
|
||||||
sx: { objectFit: 'contain', p: 2 }
|
|
||||||
}
|
|
||||||
)
|
|
||||||
),
|
|
||||||
// Product Details Section
|
|
||||||
React.createElement(
|
|
||||||
Box,
|
|
||||||
{
|
|
||||||
sx: {
|
|
||||||
flex: "1 1 60%",
|
|
||||||
p: { xs: 2, md: 4 },
|
|
||||||
display: "flex",
|
|
||||||
flexDirection: "column",
|
|
||||||
}
|
|
||||||
},
|
|
||||||
// Product identifiers
|
|
||||||
React.createElement(
|
|
||||||
Box,
|
|
||||||
{ sx: { mb: 1 } },
|
|
||||||
React.createElement(
|
|
||||||
Typography,
|
|
||||||
{ variant: 'body2', color: 'text.secondary' },
|
|
||||||
(this.props.t ? this.props.t('product.articleNumber') : 'Artikelnummer')+': '+product.articleNumber+' '+(product.gtin ? ` | GTIN: ${product.gtin}` : "")
|
|
||||||
)
|
|
||||||
),
|
|
||||||
// Product title
|
|
||||||
React.createElement(
|
React.createElement(
|
||||||
Typography,
|
Typography,
|
||||||
{
|
{ variant: "body2", color: "text.secondary" },
|
||||||
variant: 'h4',
|
|
||||||
component: 'h1',
|
|
||||||
gutterBottom: true,
|
|
||||||
sx: { fontWeight: 600, color: "#333" }
|
|
||||||
},
|
|
||||||
cleanProductName(product.name)
|
|
||||||
),
|
|
||||||
// Manufacturer if available
|
|
||||||
product.manufacturer && React.createElement(
|
|
||||||
Box,
|
|
||||||
{ sx: { display: "flex", alignItems: "center", mb: 2 } },
|
|
||||||
React.createElement(
|
React.createElement(
|
||||||
Typography,
|
'a',
|
||||||
{ variant: 'body2', sx: { fontStyle: "italic" } },
|
|
||||||
(this.props.t ? this.props.t('product.manufacturer') : 'Hersteller')+': '+product.manufacturer
|
|
||||||
)
|
|
||||||
),
|
|
||||||
// Product specifications (attributes)
|
|
||||||
attributes.length > 0 && React.createElement(
|
|
||||||
Box,
|
|
||||||
{ sx: { mb: 2 } },
|
|
||||||
React.createElement(
|
|
||||||
Stack,
|
|
||||||
{ direction: 'row', spacing: 1, flexWrap: 'wrap', gap: 1 },
|
|
||||||
...attributes.map((attr, index) =>
|
|
||||||
React.createElement(
|
|
||||||
Chip,
|
|
||||||
{
|
|
||||||
key: index,
|
|
||||||
label: `${attr.cName}: ${attr.cWert}`,
|
|
||||||
disabled: true,
|
|
||||||
sx: { mb: 1 }
|
|
||||||
}
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
),
|
|
||||||
// Weight
|
|
||||||
product.weight && product.weight > 0 && React.createElement(
|
|
||||||
Box,
|
|
||||||
{ sx: { mb: 2 } },
|
|
||||||
React.createElement(
|
|
||||||
Typography,
|
|
||||||
{ variant: 'body2', color: 'text.secondary' },
|
|
||||||
(this.props.t ? this.props.t('product.weight', { weight: product.weight.toFixed(1).replace(".", ",") }) : `Gewicht: ${product.weight.toFixed(1).replace(".", ",")} kg`)
|
|
||||||
)
|
|
||||||
),
|
|
||||||
// Price and availability section
|
|
||||||
React.createElement(
|
|
||||||
Box,
|
|
||||||
{
|
|
||||||
sx: {
|
|
||||||
mt: "auto",
|
|
||||||
p: 3,
|
|
||||||
background: "#f9f9f9",
|
|
||||||
borderRadius: 2,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
React.createElement(
|
|
||||||
Box,
|
|
||||||
{
|
{
|
||||||
sx: {
|
href: "#",
|
||||||
display: "flex",
|
onClick: (e) => {
|
||||||
flexDirection: { xs: "column", sm: "row" },
|
e.preventDefault();
|
||||||
justifyContent: "space-between",
|
if (window.history.length > 1) {
|
||||||
alignItems: { xs: "flex-start", sm: "flex-start" },
|
window.history.back();
|
||||||
gap: 2,
|
} else {
|
||||||
|
window.location.href = '/';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
style: {
|
||||||
|
paddingLeft: 16,
|
||||||
|
paddingRight: 16,
|
||||||
|
paddingTop: 8,
|
||||||
|
paddingBottom: 8,
|
||||||
|
textDecoration: "none",
|
||||||
|
color: "#fff",
|
||||||
|
fontWeight: "bold",
|
||||||
|
cursor: "pointer"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
React.createElement(
|
this.props.t ? this.props.t('common.back') : 'Zurück'
|
||||||
Box,
|
|
||||||
null,
|
|
||||||
React.createElement(
|
|
||||||
Typography,
|
|
||||||
{
|
|
||||||
variant: "h4",
|
|
||||||
color: "primary",
|
|
||||||
sx: { fontWeight: "bold" }
|
|
||||||
},
|
|
||||||
priceWithTax
|
|
||||||
),
|
|
||||||
product.vat && React.createElement(
|
|
||||||
Typography,
|
|
||||||
{ variant: 'body2', color: 'text.secondary' },
|
|
||||||
(this.props.t ? this.props.t('product.inclVat', { vat: product.vat }) : `inkl. ${product.vat}% MwSt.`)
|
|
||||||
),
|
|
||||||
product.versandklasse &&
|
|
||||||
product.versandklasse != "standard" &&
|
|
||||||
product.versandklasse != "kostenlos" && React.createElement(
|
|
||||||
Typography,
|
|
||||||
{ variant: 'body2', color: 'text.secondary' },
|
|
||||||
product.versandklasse
|
|
||||||
),
|
|
||||||
React.createElement(
|
|
||||||
Typography,
|
|
||||||
{
|
|
||||||
variant: 'body1',
|
|
||||||
color: product.available ? 'success.main' : 'error.main',
|
|
||||||
fontWeight: 'medium',
|
|
||||||
sx: { mt: 1 }
|
|
||||||
},
|
|
||||||
product.available ? '✅ Verfügbar' : '❌ Nicht verfügbar'
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
// Product full description
|
React.createElement(
|
||||||
|
Box,
|
||||||
|
{
|
||||||
|
sx: {
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: { xs: "column", md: "row" },
|
||||||
|
gap: 4,
|
||||||
|
background: "#fff",
|
||||||
|
borderRadius: 2,
|
||||||
|
boxShadow: "0 2px 8px rgba(0,0,0,0.08)",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// Product Image Section
|
||||||
|
React.createElement(
|
||||||
|
Box,
|
||||||
|
{
|
||||||
|
sx: {
|
||||||
|
width: { xs: "100%", sm: "555px" },
|
||||||
|
maxWidth: "100%",
|
||||||
|
minHeight: "400px",
|
||||||
|
height: "400px", // Fixed height to prevent shifts
|
||||||
|
background: "#f8f8f8",
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
alignItems: "center",
|
||||||
|
justifyContent: "center",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
React.createElement(
|
||||||
|
CardMedia,
|
||||||
|
{
|
||||||
|
component: 'img',
|
||||||
|
height: '400',
|
||||||
|
image: mainImage,
|
||||||
|
alt: product.name,
|
||||||
|
sx: {
|
||||||
|
objectFit: 'contain',
|
||||||
|
p: 2,
|
||||||
|
width: '100%',
|
||||||
|
maxWidth: '100%'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
),
|
||||||
|
// Product Details Section
|
||||||
|
React.createElement(
|
||||||
|
Box,
|
||||||
|
{
|
||||||
|
sx: {
|
||||||
|
flex: "1 1 60%",
|
||||||
|
p: { xs: 2, md: 4 },
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
minHeight: "400px", // Ensure consistent minimum height
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// Product identifiers - fixed height to prevent shifts
|
||||||
|
React.createElement(
|
||||||
|
Box,
|
||||||
|
{ sx: { mb: 1, minHeight: "24px" } },
|
||||||
|
React.createElement(
|
||||||
|
Typography,
|
||||||
|
{ variant: 'body2', color: 'text.secondary' },
|
||||||
|
(this.props.t ? this.props.t('product.articleNumber') : 'Artikelnummer')+': '+product.articleNumber+' '+(product.gtin ? ` | GTIN: ${product.gtin}` : "")
|
||||||
|
)
|
||||||
|
),
|
||||||
|
// Product title - reserve space for 2 lines
|
||||||
|
React.createElement(
|
||||||
|
Box,
|
||||||
|
{ sx: { mb: 2, minHeight: "72px" } },
|
||||||
|
React.createElement(
|
||||||
|
Typography,
|
||||||
|
{
|
||||||
|
variant: 'h4',
|
||||||
|
component: 'h1',
|
||||||
|
sx: {
|
||||||
|
fontWeight: 600,
|
||||||
|
color: "#333",
|
||||||
|
display: '-webkit-box',
|
||||||
|
WebkitLineClamp: 2,
|
||||||
|
WebkitBoxOrient: 'vertical',
|
||||||
|
overflow: 'hidden',
|
||||||
|
lineHeight: 1.2
|
||||||
|
}
|
||||||
|
},
|
||||||
|
cleanProductName(product.name)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
// Manufacturer if available - fixed height placeholder
|
||||||
|
React.createElement(
|
||||||
|
Box,
|
||||||
|
{ sx: { display: "flex", alignItems: "center", mb: 2, minHeight: "28px" } },
|
||||||
|
product.manufacturer && React.createElement(
|
||||||
|
Typography,
|
||||||
|
{ variant: 'body2', sx: { fontStyle: "italic" } },
|
||||||
|
(this.props.t ? this.props.t('product.manufacturer') : 'Hersteller')+': '+product.manufacturer
|
||||||
|
)
|
||||||
|
),
|
||||||
|
// Product specifications (attributes) - fixed height container
|
||||||
|
React.createElement(
|
||||||
|
Box,
|
||||||
|
{ sx: { mb: 2, minHeight: attributes.length > 0 ? "auto" : "0px" } },
|
||||||
|
attributes.length > 0 && React.createElement(
|
||||||
|
Stack,
|
||||||
|
{ direction: 'row', spacing: 1, flexWrap: 'wrap', gap: 1 },
|
||||||
|
...attributes.map((attr, index) =>
|
||||||
|
React.createElement(
|
||||||
|
Chip,
|
||||||
|
{
|
||||||
|
key: index,
|
||||||
|
label: `${attr.cName}: ${attr.cWert}`,
|
||||||
|
disabled: true,
|
||||||
|
size: "small",
|
||||||
|
sx: { mb: 1 }
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
// Weight - fixed height placeholder
|
||||||
|
React.createElement(
|
||||||
|
Box,
|
||||||
|
{ sx: { mb: 2, minHeight: "28px" } },
|
||||||
|
product.weight && product.weight > 0 && React.createElement(
|
||||||
|
Typography,
|
||||||
|
{ variant: 'body2', color: 'text.secondary' },
|
||||||
|
(this.props.t ? this.props.t('product.weight', { weight: product.weight.toFixed(1).replace(".", ",") }) : `Gewicht: ${product.weight.toFixed(1).replace(".", ",")} kg`)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
// Price and availability section - positioned at bottom
|
||||||
|
React.createElement(
|
||||||
|
Box,
|
||||||
|
{
|
||||||
|
sx: {
|
||||||
|
mt: "auto",
|
||||||
|
p: 3,
|
||||||
|
background: "#f9f9f9",
|
||||||
|
borderRadius: 2,
|
||||||
|
minHeight: "120px", // Fixed minimum height for price section
|
||||||
|
}
|
||||||
|
},
|
||||||
|
React.createElement(
|
||||||
|
Box,
|
||||||
|
{
|
||||||
|
sx: {
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: { xs: "column", sm: "row" },
|
||||||
|
justifyContent: "space-between",
|
||||||
|
alignItems: { xs: "flex-start", sm: "flex-start" },
|
||||||
|
gap: 2,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// Left side - Price information
|
||||||
|
React.createElement(
|
||||||
|
Box,
|
||||||
|
{ sx: { flex: 1 } },
|
||||||
|
React.createElement(
|
||||||
|
Typography,
|
||||||
|
{
|
||||||
|
variant: "h4",
|
||||||
|
color: "primary",
|
||||||
|
sx: { fontWeight: "bold", mb: 1 }
|
||||||
|
},
|
||||||
|
priceWithTax
|
||||||
|
),
|
||||||
|
// VAT info with fixed height
|
||||||
|
React.createElement(
|
||||||
|
Box,
|
||||||
|
{ sx: { minHeight: "20px", mb: 1 } },
|
||||||
|
product.vat && React.createElement(
|
||||||
|
Typography,
|
||||||
|
{ variant: 'body2', color: 'text.secondary' },
|
||||||
|
(this.props.t ? this.props.t('product.inclVat', { vat: product.vat }) : `inkl. ${product.vat}% MwSt.`)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
// Shipping class with fixed height
|
||||||
|
React.createElement(
|
||||||
|
Box,
|
||||||
|
{ sx: { minHeight: "20px", mb: 1 } },
|
||||||
|
product.versandklasse &&
|
||||||
|
product.versandklasse != "standard" &&
|
||||||
|
product.versandklasse != "kostenlos" && React.createElement(
|
||||||
|
Typography,
|
||||||
|
{ variant: 'body2', color: 'text.secondary' },
|
||||||
|
product.versandklasse
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
// Right side - Cart button area with availability info
|
||||||
|
React.createElement(
|
||||||
|
Box,
|
||||||
|
{
|
||||||
|
sx: {
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
minWidth: { xs: "100%", sm: "200px" }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// Placeholder for AddToCartButton area
|
||||||
|
React.createElement(
|
||||||
|
Box,
|
||||||
|
{
|
||||||
|
sx: {
|
||||||
|
minHeight: "48px",
|
||||||
|
mb: 1,
|
||||||
|
display: "flex",
|
||||||
|
alignItems: "center",
|
||||||
|
justifyContent: "center",
|
||||||
|
backgroundColor: "#f0f0f0",
|
||||||
|
borderRadius: 2,
|
||||||
|
border: "1px dashed #ccc"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
React.createElement(
|
||||||
|
Typography,
|
||||||
|
{
|
||||||
|
variant: 'body2',
|
||||||
|
color: 'text.secondary',
|
||||||
|
sx: { fontStyle: 'italic' }
|
||||||
|
},
|
||||||
|
'Add to Cart Button'
|
||||||
|
)
|
||||||
|
),
|
||||||
|
// Availability and delivery time info (matching ProductDetailPage)
|
||||||
|
React.createElement(
|
||||||
|
Typography,
|
||||||
|
{
|
||||||
|
variant: 'caption',
|
||||||
|
sx: {
|
||||||
|
fontStyle: "italic",
|
||||||
|
color: "text.secondary",
|
||||||
|
textAlign: "center",
|
||||||
|
minHeight: "28px",
|
||||||
|
display: "flex",
|
||||||
|
alignItems: "center",
|
||||||
|
justifyContent: "center"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
product.id && product.id.toString().endsWith("steckling") ?
|
||||||
|
(this.props.t ? this.props.t('delivery.times.cutting14Days') : "Lieferzeit: 14 Tage") :
|
||||||
|
product.available == 1 ?
|
||||||
|
(this.props.t ? this.props.t('delivery.times.standard2to3Days') : "Lieferzeit: 2-3 Tage") :
|
||||||
|
product.availableSupplier == 1 ?
|
||||||
|
(this.props.t ? this.props.t('delivery.times.supplier7to9Days') : "Lieferzeit: 7-9 Tage") :
|
||||||
|
(product.available ? '✅ Verfügbar' : '❌ Nicht verfügbar')
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
// Product full description - separate card
|
||||||
product.description && React.createElement(
|
product.description && React.createElement(
|
||||||
Box,
|
Box,
|
||||||
{
|
{
|
||||||
@@ -271,6 +415,7 @@ class PrerenderProduct extends React.Component {
|
|||||||
background: "#fff",
|
background: "#fff",
|
||||||
borderRadius: 2,
|
borderRadius: 2,
|
||||||
boxShadow: "0 2px 8px rgba(0,0,0,0.08)",
|
boxShadow: "0 2px 8px rgba(0,0,0,0.08)",
|
||||||
|
minHeight: "100px" // Minimum height to prevent shifts
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
React.createElement(
|
React.createElement(
|
||||||
|
|||||||
Reference in New Issue
Block a user