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
|
||||
}
|
||||
},
|
||||
// Back button (breadcrumbs section)
|
||||
React.createElement(
|
||||
Box,
|
||||
{
|
||||
sx: {
|
||||
mb: 2,
|
||||
position: ["-webkit-sticky", "sticky"],
|
||||
top: {
|
||||
xs: "80px",
|
||||
sm: "80px",
|
||||
md: "80px",
|
||||
lg: "80px",
|
||||
},
|
||||
left: 0,
|
||||
width: "100%",
|
||||
display: "flex",
|
||||
flexDirection: { xs: "column", md: "row" },
|
||||
gap: 4,
|
||||
background: "#fff",
|
||||
borderRadius: 2,
|
||||
boxShadow: "0 2px 8px rgba(0,0,0,0.08)",
|
||||
zIndex: 999, // Just below the AppBar
|
||||
py: 0,
|
||||
px: 2,
|
||||
}
|
||||
},
|
||||
// Product Image Section
|
||||
React.createElement(
|
||||
Box,
|
||||
{
|
||||
sx: {
|
||||
width: { xs: "100%", sm: "555px" },
|
||||
maxWidth: "100%",
|
||||
minHeight: "400px",
|
||||
background: "#f8f8f8",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
ml: { xs: 0, md: 0 },
|
||||
display: "inline-flex",
|
||||
px: 0,
|
||||
py: 1,
|
||||
backgroundColor: "#2e7d32", // primary dark green
|
||||
borderRadius: 1,
|
||||
}
|
||||
},
|
||||
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(
|
||||
Typography,
|
||||
{
|
||||
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 } },
|
||||
{ variant: "body2", color: "text.secondary" },
|
||||
React.createElement(
|
||||
Typography,
|
||||
{ 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,
|
||||
'a',
|
||||
{
|
||||
sx: {
|
||||
display: "flex",
|
||||
flexDirection: { xs: "column", sm: "row" },
|
||||
justifyContent: "space-between",
|
||||
alignItems: { xs: "flex-start", sm: "flex-start" },
|
||||
gap: 2,
|
||||
href: "#",
|
||||
onClick: (e) => {
|
||||
e.preventDefault();
|
||||
if (window.history.length > 1) {
|
||||
window.history.back();
|
||||
} else {
|
||||
window.location.href = '/';
|
||||
}
|
||||
},
|
||||
style: {
|
||||
paddingLeft: 16,
|
||||
paddingRight: 16,
|
||||
paddingTop: 8,
|
||||
paddingBottom: 8,
|
||||
textDecoration: "none",
|
||||
color: "#fff",
|
||||
fontWeight: "bold",
|
||||
cursor: "pointer"
|
||||
}
|
||||
},
|
||||
React.createElement(
|
||||
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'
|
||||
)
|
||||
)
|
||||
this.props.t ? this.props.t('common.back') : 'Zurück'
|
||||
)
|
||||
)
|
||||
)
|
||||
),
|
||||
// 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(
|
||||
Box,
|
||||
{
|
||||
@@ -271,6 +415,7 @@ class PrerenderProduct extends React.Component {
|
||||
background: "#fff",
|
||||
borderRadius: 2,
|
||||
boxShadow: "0 2px 8px rgba(0,0,0,0.08)",
|
||||
minHeight: "100px" // Minimum height to prevent shifts
|
||||
}
|
||||
},
|
||||
React.createElement(
|
||||
|
||||
Reference in New Issue
Block a user