From c7ab8b3a6738dcbc3d76500d6e00ce23d5eabea5 Mon Sep 17 00:00:00 2001 From: Nils Pukropp Date: Mon, 16 Feb 2026 01:15:42 +0100 Subject: [PATCH] fixed wrong redirects --- backend/api/serializers.py | 3 +- frontend/src/pages/flavors/FlavorDetail.tsx | 204 +++++++++----------- 2 files changed, 88 insertions(+), 119 deletions(-) diff --git a/backend/api/serializers.py b/backend/api/serializers.py index b75a4e6..99ae14a 100644 --- a/backend/api/serializers.py +++ b/backend/api/serializers.py @@ -31,13 +31,14 @@ class RatingSerializer(serializers.ModelSerializer): class FlavorSerializer(serializers.ModelSerializer): category_name = serializers.CharField(source='category.name', read_only=True) + category_slug = serializers.CharField(source='category.slug', read_only=True) average_rating = serializers.FloatField(read_only=True) user_rating = serializers.SerializerMethodField() ratings = RatingSerializer(many=True, read_only=True) class Meta: model = Flavor - fields = ['id', 'name', 'category', 'category_name', 'description', 'average_rating', 'user_rating', 'ratings', 'image_url', 'is_available', 'shop_url'] + fields = ['id', 'name', 'category', 'category_name', 'category_slug', 'description', 'average_rating', 'user_rating', 'ratings', 'image_url', 'is_available', 'shop_url'] def get_user_rating(self, obj): request = self.context.get('request') diff --git a/frontend/src/pages/flavors/FlavorDetail.tsx b/frontend/src/pages/flavors/FlavorDetail.tsx index 687c0d4..cf4ef37 100644 --- a/frontend/src/pages/flavors/FlavorDetail.tsx +++ b/frontend/src/pages/flavors/FlavorDetail.tsx @@ -8,7 +8,9 @@ import { Rating as MuiRating, Button, TextField, - Avatar + Avatar, + Alert, + Paper } from '@mui/material'; import api from '../../api'; import { useTitle } from '../../hooks/useTitle'; @@ -49,20 +51,16 @@ const FlavorDetail: React.FC = () => { const [loading, setLoading] = useState(true); const [replyInputs, setReplyInputs] = useState<{[key: number]: string}>({}); const [currentUser, setCurrentUser] = useState(null); + + // Rating form state + const [newScore, setNewScore] = useState(null); + const [newComment, setNewComment] = useState(''); + + // Edit state const [editMode, setEditMode] = useState(null); const [editScore, setEditScore] = useState(0); const [editComment, setEditComment] = useState(''); - useEffect(() => { - const getUser = async () => { - try { - const res = await api.get('users/me/'); - setCurrentUser(res.data.username); - } catch (e) { /* ignore */ } - }; - getUser(); - }, []); - const fetchFlavor = async () => { try { const res = await api.get(`flavors/${id}/`); @@ -75,26 +73,48 @@ const FlavorDetail: React.FC = () => { }; useEffect(() => { + const getUser = async () => { + try { + const res = await api.get('users/me/'); + setCurrentUser(res.data.username); + } catch (e) { /* ignore */ } + }; + getUser(); fetchFlavor(); }, [id]); useTitle(flavor?.name || 'Flavor Detail'); + const handleRatingSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + if (!newScore) { + alert('Please select a score'); + return; + } + try { + await api.post('ratings/', { flavor: flavor?.id, score: newScore, comment: newComment }); + setNewScore(null); + setNewComment(''); + fetchFlavor(); + } catch (err: any) { + alert(err.response?.data?.error || 'Failed to submit rating'); + } + }; + const handleReplySubmit = async (ratingId: number) => { const text = replyInputs[ratingId]; if (!text) return; - try { await api.post(`ratings/${ratingId}/reply/`, { text }); setReplyInputs({ ...replyInputs, [ratingId]: '' }); - fetchFlavor(); // Refresh to show new reply + fetchFlavor(); } catch (err) { alert('Failed to submit reply'); } }; const handleDeleteRating = async (ratingId: number) => { - if (!confirm('Are you sure you want to delete your review?')) return; + if (!confirm('Are you sure?')) return; try { await api.delete(`ratings/${ratingId}/`); fetchFlavor(); @@ -128,95 +148,69 @@ const FlavorDetail: React.FC = () => { ← Back to {flavor.category_name} - + {flavor.image_url && ( - - + + )} {!flavor.is_available && ( - - Out of Stock - + Out of Stock )} {flavor.name} {flavor.category_name} - + {(flavor.average_rating || 0).toFixed(1)} / 10 - ({flavor.ratings.length} ratings) - - {flavor.description} - + {flavor.description} {flavor.shop_url && ( - )} + {/* New Rating Form */} + {currentUser && flavor.user_rating === null && ( + + Rate this flavor +
+ + Score (1-10) + setNewScore(val)} size="large" /> + + setNewComment(e.target.value)} + sx={{ mb: 2 }} + /> + + +
+ )} + Community Reviews - {flavor.ratings.length === 0 ? ( - No reviews yet. Be the first to rate this flavor! + No reviews yet. ) : ( flavor.ratings.map((rating: Rating) => ( {editMode === rating.id ? ( - Edit Review setEditScore(val || 0)} /> - setEditComment(e.target.value)} - /> + setEditComment(e.target.value)} /> @@ -225,23 +219,15 @@ const FlavorDetail: React.FC = () => { ) : ( <> - - {rating.user.charAt(0).toUpperCase()} - + {rating.user.charAt(0).toUpperCase()} {rating.user} - - {rating.score}/10 + + {rating.score}/10 - - {rating.comment && ( - {rating.comment} - )} - - - - {new Date(rating.created_at).toLocaleDateString()} - + {rating.comment && {rating.comment}} + + {new Date(rating.created_at).toLocaleDateString()} {currentUser === rating.user && ( @@ -252,39 +238,21 @@ const FlavorDetail: React.FC = () => { )} - {/* Replies Section */} - {(rating.replies.length > 0 || true) && ( // Always show reply section essentially - - {rating.replies.map((reply: Reply) => ( - - - {reply.user} - - {reply.text} - - {new Date(reply.created_at).toLocaleDateString()} - - - ))} - - - setReplyInputs({ ...replyInputs, [rating.id]: e.target.value })} - /> - + + {rating.replies.map((reply: Reply) => ( + + {reply.user} + {reply.text} + {new Date(reply.created_at).toLocaleDateString()} - - )} + ))} + {currentUser && ( + + setReplyInputs({ ...replyInputs, [rating.id]: e.target.value })} /> + + + )} + ))