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:
sebseb7
2025-07-18 11:56:37 +02:00
parent e43b894bfc
commit 47a882b667
5 changed files with 1170 additions and 31 deletions

View File

@@ -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>