Add article interaction forms: Implemented ArticleAvailabilityForm, ArticleQuestionForm, and ArticleRatingForm components for user inquiries, ratings, and availability requests. Integrated photo upload functionality and enhanced user experience with collapsible sections in ProductDetailPage for better interaction with product details.
This commit is contained in:
@@ -1,10 +1,13 @@
|
||||
import React, { Component } from "react";
|
||||
import { Box, Typography, CardMedia, Stack, Chip } from "@mui/material";
|
||||
import { Box, Typography, CardMedia, Stack, Chip, Button, Collapse } from "@mui/material";
|
||||
import { Link } from "react-router-dom";
|
||||
import parse from "html-react-parser";
|
||||
import AddToCartButton from "./AddToCartButton.js";
|
||||
import Images from "./Images.js";
|
||||
import { withI18n } from "../i18n/withTranslation.js";
|
||||
import ArticleQuestionForm from "./ArticleQuestionForm.js";
|
||||
import ArticleRatingForm from "./ArticleRatingForm.js";
|
||||
import ArticleAvailabilityForm from "./ArticleAvailabilityForm.js";
|
||||
|
||||
// Utility function to clean product names by removing trailing number in parentheses
|
||||
const cleanProductName = (name) => {
|
||||
@@ -35,7 +38,11 @@ class ProductDetailPage extends Component {
|
||||
komponentenData: {}, // Store individual komponent data with loading states
|
||||
komponentenImages: {}, // Store tiny pictures for komponenten
|
||||
totalKomponentenPrice: 0,
|
||||
totalSavings: 0
|
||||
totalSavings: 0,
|
||||
// Collapsible sections state
|
||||
showQuestionForm: false,
|
||||
showRatingForm: false,
|
||||
showAvailabilityForm: false
|
||||
};
|
||||
} else {
|
||||
this.state = {
|
||||
@@ -51,7 +58,11 @@ class ProductDetailPage extends Component {
|
||||
komponentenData: {}, // Store individual komponent data with loading states
|
||||
komponentenImages: {}, // Store tiny pictures for komponenten
|
||||
totalKomponentenPrice: 0,
|
||||
totalSavings: 0
|
||||
totalSavings: 0,
|
||||
// Collapsible sections state
|
||||
showQuestionForm: false,
|
||||
showRatingForm: false,
|
||||
showAvailabilityForm: false
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -459,6 +470,52 @@ class ProductDetailPage extends Component {
|
||||
this.setState({ imageDialogOpen: false });
|
||||
};
|
||||
|
||||
toggleQuestionForm = () => {
|
||||
this.setState(prevState => ({
|
||||
showQuestionForm: !prevState.showQuestionForm,
|
||||
showRatingForm: false,
|
||||
showAvailabilityForm: false
|
||||
}), () => {
|
||||
if (this.state.showQuestionForm) {
|
||||
setTimeout(() => this.scrollToSection('question-form'), 100);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
toggleRatingForm = () => {
|
||||
this.setState(prevState => ({
|
||||
showRatingForm: !prevState.showRatingForm,
|
||||
showQuestionForm: false,
|
||||
showAvailabilityForm: false
|
||||
}), () => {
|
||||
if (this.state.showRatingForm) {
|
||||
setTimeout(() => this.scrollToSection('rating-form'), 100);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
toggleAvailabilityForm = () => {
|
||||
this.setState(prevState => ({
|
||||
showAvailabilityForm: !prevState.showAvailabilityForm,
|
||||
showQuestionForm: false,
|
||||
showRatingForm: false
|
||||
}), () => {
|
||||
if (this.state.showAvailabilityForm) {
|
||||
setTimeout(() => this.scrollToSection('availability-form'), 100);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
scrollToSection = (sectionId) => {
|
||||
const element = document.getElementById(sectionId);
|
||||
if (element) {
|
||||
element.scrollIntoView({
|
||||
behavior: 'smooth',
|
||||
block: 'start'
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const { product, loading, error, attributeImages, isSteckling, attributes, komponentenLoaded, komponentenData, komponentenImages, totalKomponentenPrice, totalSavings } =
|
||||
this.state;
|
||||
@@ -658,35 +715,91 @@ class ProductDetailPage extends Component {
|
||||
</Box>
|
||||
)}
|
||||
|
||||
{/* Attribute images and chips */}
|
||||
{/* Attribute images and chips with action buttons */}
|
||||
{(attributes.some(attr => attributeImages[attr.kMerkmalWert]) || attributes.some(attr => !attributeImages[attr.kMerkmalWert])) && (
|
||||
<Stack direction="row" spacing={2} sx={{ flexWrap: "wrap", gap: 1, mb: 2 }}>
|
||||
{attributes
|
||||
.filter(attribute => attributeImages[attribute.kMerkmalWert])
|
||||
.map((attribute) => {
|
||||
const key = attribute.kMerkmalWert;
|
||||
return (
|
||||
<Box key={key} sx={{ mb: 1,border: "1px solid #e0e0e0", borderRadius: 1 }}>
|
||||
<CardMedia
|
||||
component="img"
|
||||
style={{ width: "72px", height: "98px" }}
|
||||
image={attributeImages[key]}
|
||||
alt={`Attribute ${key}`}
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
})}
|
||||
{attributes
|
||||
.filter(attribute => !attributeImages[attribute.kMerkmalWert])
|
||||
.map((attribute) => (
|
||||
<Chip
|
||||
key={attribute.kMerkmalWert}
|
||||
label={attribute.cWert}
|
||||
disabled
|
||||
sx={{ mb: 1 }}
|
||||
/>
|
||||
))}
|
||||
</Stack>
|
||||
<Box sx={{ display: "flex", justifyContent: "space-between", alignItems: "flex-start", mb: 2, gap: 2 }}>
|
||||
<Stack direction="row" spacing={2} sx={{ flexWrap: "wrap", gap: 1, flex: 1 }}>
|
||||
{attributes
|
||||
.filter(attribute => attributeImages[attribute.kMerkmalWert])
|
||||
.map((attribute) => {
|
||||
const key = attribute.kMerkmalWert;
|
||||
return (
|
||||
<Box key={key} sx={{ mb: 1,border: "1px solid #e0e0e0", borderRadius: 1 }}>
|
||||
<CardMedia
|
||||
component="img"
|
||||
style={{ width: "72px", height: "98px" }}
|
||||
image={attributeImages[key]}
|
||||
alt={`Attribute ${key}`}
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
})}
|
||||
{attributes
|
||||
.filter(attribute => !attributeImages[attribute.kMerkmalWert])
|
||||
.map((attribute) => (
|
||||
<Chip
|
||||
key={attribute.kMerkmalWert}
|
||||
label={attribute.cWert}
|
||||
disabled
|
||||
sx={{ mb: 1 }}
|
||||
/>
|
||||
))}
|
||||
</Stack>
|
||||
|
||||
{/* Right-aligned action buttons */}
|
||||
<Stack direction="column" spacing={1} sx={{ flexShrink: 0 }}>
|
||||
<Button
|
||||
variant="outlined"
|
||||
size="small"
|
||||
onClick={this.toggleQuestionForm}
|
||||
sx={{
|
||||
fontSize: "0.75rem",
|
||||
px: 1.5,
|
||||
py: 0.5,
|
||||
minWidth: "auto",
|
||||
whiteSpace: "nowrap"
|
||||
}}
|
||||
>
|
||||
Frage zum Artikel
|
||||
</Button>
|
||||
<Button
|
||||
variant="outlined"
|
||||
size="small"
|
||||
onClick={this.toggleRatingForm}
|
||||
sx={{
|
||||
fontSize: "0.75rem",
|
||||
px: 1.5,
|
||||
py: 0.5,
|
||||
minWidth: "auto",
|
||||
whiteSpace: "nowrap"
|
||||
}}
|
||||
>
|
||||
Artikel Bewerten
|
||||
</Button>
|
||||
{(product.available !== 1 && product.availableSupplier !== 1) && (
|
||||
<Button
|
||||
variant="outlined"
|
||||
size="small"
|
||||
onClick={this.toggleAvailabilityForm}
|
||||
sx={{
|
||||
fontSize: "0.75rem",
|
||||
px: 1.5,
|
||||
py: 0.5,
|
||||
minWidth: "auto",
|
||||
whiteSpace: "nowrap",
|
||||
borderColor: "warning.main",
|
||||
color: "warning.main",
|
||||
"&:hover": {
|
||||
borderColor: "warning.dark",
|
||||
backgroundColor: "warning.light"
|
||||
}
|
||||
}}
|
||||
>
|
||||
Verfügbarkeit anfragen
|
||||
</Button>
|
||||
)}
|
||||
</Stack>
|
||||
</Box>
|
||||
)}
|
||||
|
||||
{/* Weight */}
|
||||
@@ -888,6 +1001,39 @@ class ProductDetailPage extends Component {
|
||||
</Box>
|
||||
)}
|
||||
|
||||
{/* Article Question Form */}
|
||||
<Collapse in={this.state.showQuestionForm}>
|
||||
<div id="question-form">
|
||||
<ArticleQuestionForm
|
||||
productId={product.id}
|
||||
productName={cleanProductName(product.name)}
|
||||
socket={this.props.socket}
|
||||
/>
|
||||
</div>
|
||||
</Collapse>
|
||||
|
||||
{/* Article Rating Form */}
|
||||
<Collapse in={this.state.showRatingForm}>
|
||||
<div id="rating-form">
|
||||
<ArticleRatingForm
|
||||
productId={product.id}
|
||||
productName={cleanProductName(product.name)}
|
||||
socket={this.props.socket}
|
||||
/>
|
||||
</div>
|
||||
</Collapse>
|
||||
|
||||
{/* Article Availability Form - only show for out of stock items */}
|
||||
{(product.available !== 1 && product.availableSupplier !== 1) && (
|
||||
<Collapse in={this.state.showAvailabilityForm}>
|
||||
<ArticleAvailabilityForm
|
||||
productId={product.id}
|
||||
productName={cleanProductName(product.name)}
|
||||
socket={this.props.socket}
|
||||
/>
|
||||
</Collapse>
|
||||
)}
|
||||
|
||||
{product.komponenten && product.komponenten.split(",").length > 0 && (
|
||||
<Box sx={{ mt: 4, p: 4, background: "#fff", borderRadius: 2, boxShadow: "0 2px 8px rgba(0,0,0,0.08)" }}>
|
||||
<Typography variant="h4" gutterBottom>{this.props.t ? this.props.t('product.consistsOf') : 'Bestehend aus:'}</Typography>
|
||||
|
||||
Reference in New Issue
Block a user