Files
dps-webshop/01-frontend/src/helper/productpage/Ratings.tsx
2026-03-11 12:30:20 +01:00

171 lines
4.2 KiB
TypeScript

import {
Box,
Button,
Divider,
IconButton,
Rating,
Snackbar,
SnackbarCloseReason,
TextField,
Typography,
useTheme,
} from "@mui/material";
import { Close } from "@mui/icons-material";
import { useQuery } from "@tanstack/react-query";
import React, { useMemo, useState } from "react";
import { useTranslation } from "react-i18next";
import RatingType from "../../components/Rating";
import { fetchRatingList, submitRating } from "../query/Queries";
import RatingCard from "./RatingCard";
import RatingSubmitType from "../../components/RatingSubmit";
export default function Ratings({ itemId }: { itemId: string }) {
const { t } = useTranslation();
const theme = useTheme();
const [open, setOpen] = useState<boolean>(false);
const [ratingText, setRatingText] = useState<string>("");
const [ratingValue, setRatingValue] = useState<number | null>(2.5);
const ratingData: RatingSubmitType = {
rating: ratingValue || 0,
content: ratingText || "",
articleId: itemId,
};
const { refetch } = useQuery({
queryKey: ["submitRating", ratingData],
queryFn: () => submitRating(ratingData),
retry: 3,
retryDelay: 1000,
enabled: false,
});
const handleRatingSubmit = () => {
setOpen(true);
void refetch(); // bewusst ausgelöst, kein await notwendig
};
const handleClose = (
_: React.SyntheticEvent | Event,
reason?: SnackbarCloseReason,
) => {
if (reason === "clickaway") return;
setOpen(false);
};
const action = (
<React.Fragment>
<IconButton
size="small"
aria-label={t("close")}
color="inherit"
onClick={handleClose}
>
<Close fontSize="small" />
</IconButton>
</React.Fragment>
);
const { data = [] } = useQuery<RatingType[]>({
queryKey: ["fetchRatingList", itemId],
queryFn: () => fetchRatingList(itemId),
retry: 3,
retryDelay: 1000,
});
const ratings: RatingType[] = useMemo(() => data || [], [data]);
const getRatings = () => {
if (ratings.length === 0) {
return (
<Typography
variant="body1"
sx={{ color: theme.palette.text.secondary }}
>
{t("noRatingsYet")}
</Typography>
);
}
return ratings.map((ratingType: RatingType) => (
<RatingCard key={ratingType.timestamp} {...ratingType} />
));
};
return (
<>
<Divider sx={{ backgroundColor: theme.palette.divider, my: 3 }} />
<Box sx={{ mb: 4 }}>
<Typography
variant="h5"
sx={{ color: theme.palette.text.primary, mb: 2 }}
>
{t("rateThisProduct")}:
</Typography>
<Rating
name="half-rating"
value={ratingValue}
onChange={(_, value) => setRatingValue(value)}
precision={0.5}
/>
<TextField
label={t("review")}
multiline
minRows={4}
maxRows={16}
fullWidth
sx={{
mt: 2,
mb: 2,
backgroundColor: theme.palette.background.paper,
color: theme.palette.text.primary,
"& .MuiInputBase-input": {
color: theme.palette.text.primary,
},
"& label": {
color: theme.palette.text.secondary,
},
"& .MuiOutlinedInput-root": {
"& fieldset": {
borderColor: theme.palette.divider,
},
"&:hover fieldset": {
borderColor: theme.palette.text.primary,
},
"&.Mui-focused fieldset": {
borderColor: theme.palette.primary.main,
},
},
}}
value={ratingText}
onChange={(e) => setRatingText(e.target.value)}
/>
<Button
variant="contained"
color="primary"
onClick={handleRatingSubmit}
>
{t("submit")}
</Button>
</Box>
<Divider sx={{ backgroundColor: theme.palette.divider, my: 3 }} />
<Box>{getRatings()}</Box>
<Snackbar
open={open}
autoHideDuration={3000}
onClose={handleClose}
message={t("thanksForRating")}
action={action}
/>
</>
);
}