fixed wrong redirects

This commit is contained in:
nvrl committed 2026-02-16 01:15:42 +01:00
1 parent 8a5402059c
commit c7ab8b3a67
2 files changed
+88 -119

No files matched your search

+86 -118
View File
@@ -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<string | null>(null);
// Rating form state
const [newScore, setNewScore] = useState<number | null>(null);
const [newComment, setNewComment] = useState('');
// Edit state
const [editMode, setEditMode] = useState<number | null>(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 = () => {
&larr; Back to {flavor.category_name}
</Button>
<Card sx={{ display: 'flex', flexDirection: { xs: 'column', md: 'row' }, mb: 4, overflow: 'visible' }}>
<Card sx={{ display: 'flex', flexDirection: { xs: 'column', md: 'row' }, mb: 4 }}>
{flavor.image_url && (
<Box
sx={{
width: { xs: '100%', md: 400 },
height: 400,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
bgcolor: 'rgba(255,255,255,0.05)',
p: 4
}}
>
<Box
component="img"
src={flavor.image_url}
sx={{
maxWidth: '100%',
maxHeight: '100%',
objectFit: 'contain',
filter: 'drop-shadow(0px 10px 20px rgba(0,0,0,0.3))'
}}
/>
<Box sx={{ width: { xs: '100%', md: 400 }, height: 400, display: 'flex', alignItems: 'center', justifyContent: 'center', bgcolor: 'rgba(255,255,255,0.05)', p: 4 }}>
<Box component="img" src={flavor.image_url} sx={{ maxWidth: '100%', maxHeight: '100%', objectFit: 'contain' }} />
</Box>
)}
<CardContent sx={{ flex: 1, p: 4 }}>
{!flavor.is_available && (
<Box sx={{
display: 'inline-block',
bgcolor: 'error.main',
color: 'white',
px: 1,
py: 0.5,
borderRadius: 1,
mb: 2,
fontSize: '0.875rem',
fontWeight: 'bold'
}}>
Out of Stock
</Box>
<Alert severity="error" sx={{ mb: 2 }}>Out of Stock</Alert>
)}
<Typography variant="h3" gutterBottom sx={{ fontWeight: 'bold' }}>{flavor.name}</Typography>
<Typography variant="h6" color="text.secondary" gutterBottom>{flavor.category_name}</Typography>
<Box sx={{ display: 'flex', alignItems: 'center', mb: 3 }}>
<MuiRating value={flavor.average_rating || 0} readOnly precision={0.5} size="large" />
<MuiRating value={flavor.average_rating || 0} readOnly precision={0.5} size="large" max={10} />
<Typography variant="h5" sx={{ ml: 2 }}>{(flavor.average_rating || 0).toFixed(1)} / 10</Typography>
<Typography variant="body2" color="text.secondary" sx={{ ml: 1 }}>({flavor.ratings.length} ratings)</Typography>
</Box>
<Typography variant="body1" sx={{ mb: 4, lineHeight: 1.8 }}>
{flavor.description}
</Typography>
<Typography variant="body1" sx={{ mb: 4, lineHeight: 1.8 }}>{flavor.description}</Typography>
{flavor.shop_url && (
<Button
variant="contained"
size="large"
component="a"
href={flavor.shop_url}
target="_blank"
sx={{ px: 4, py: 1.5 }}
>
<Button variant="contained" size="large" component="a" href={flavor.shop_url} target="_blank">
Buy Now
</Button>
)}
</CardContent>
</Card>
{/* New Rating Form */}
{currentUser && flavor.user_rating === null && (
<Paper sx={{ p: 3, mb: 4 }}>
<Typography variant="h5" gutterBottom>Rate this flavor</Typography>
<form onSubmit={handleRatingSubmit}>
<Box sx={{ mb: 2 }}>
<Typography component="legend">Score (1-10)</Typography>
<MuiRating max={10} value={newScore} onChange={(_, val) => setNewScore(val)} size="large" />
</Box>
<TextField
fullWidth
multiline
rows={3}
label="Your Comment (Optional)"
value={newComment}
onChange={(e) => setNewComment(e.target.value)}
sx={{ mb: 2 }}
/>
<Button variant="contained" type="submit" disabled={!newScore}>Submit Rating</Button>
</form>
</Paper>
)}
<Typography variant="h4" gutterBottom sx={{ mt: 6, mb: 3 }}>Community Reviews</Typography>
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 3 }}>
{flavor.ratings.length === 0 ? (
<Typography color="text.secondary">No reviews yet. Be the first to rate this flavor!</Typography>
<Typography color="text.secondary">No reviews yet.</Typography>
) : (
flavor.ratings.map((rating: Rating) => (
<Card key={rating.id} variant="outlined">
<CardContent>
{editMode === rating.id ? (
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 2 }}>
<Typography variant="h6">Edit Review</Typography>
<MuiRating max={10} value={editScore} onChange={(_, val) => setEditScore(val || 0)} />
<TextField
multiline
fullWidth
label="Comment"
value={editComment}
onChange={(e) => setEditComment(e.target.value)}
/>
<TextField multiline fullWidth value={editComment} onChange={(e) => setEditComment(e.target.value)} />
<Box sx={{ display: 'flex', gap: 1 }}>
<Button variant="contained" onClick={() => handleUpdateRating(rating.id)}>Save</Button>
<Button onClick={() => setEditMode(null)}>Cancel</Button>
@@ -225,23 +219,15 @@ const FlavorDetail: React.FC = () => {
) : (
<>
<Box sx={{ display: 'flex', alignItems: 'center', mb: 2 }}>
<Avatar sx={{ width: 32, height: 32, mr: 1.5, fontSize: '0.875rem' }}>
{rating.user.charAt(0).toUpperCase()}
</Avatar>
<Avatar sx={{ width: 32, height: 32, mr: 1.5 }}>{rating.user.charAt(0).toUpperCase()}</Avatar>
<Typography variant="subtitle1" sx={{ fontWeight: 'bold' }}>{rating.user}</Typography>
<Box sx={{ flexGrow: 1 }} />
<MuiRating value={rating.score} readOnly size="small" />
<Typography variant="body2" sx={{ ml: 1, fontWeight: 'bold' }}>{rating.score}/10</Typography>
<MuiRating value={rating.score} readOnly size="small" max={10} />
<Typography variant="body2" sx={{ ml: 1 }}>{rating.score}/10</Typography>
</Box>
{rating.comment && (
<Typography variant="body1" sx={{ mb: 2 }}>{rating.comment}</Typography>
)}
<Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
<Typography variant="caption" color="text.secondary">
{new Date(rating.created_at).toLocaleDateString()}
</Typography>
{rating.comment && <Typography variant="body1" sx={{ mb: 2 }}>{rating.comment}</Typography>}
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<Typography variant="caption" color="text.secondary">{new Date(rating.created_at).toLocaleDateString()}</Typography>
{currentUser === rating.user && (
<Box>
<Button size="small" onClick={() => startEdit(rating)}>Edit</Button>
@@ -252,39 +238,21 @@ const FlavorDetail: React.FC = () => {
</>
)}
{/* Replies Section */}
{(rating.replies.length > 0 || true) && ( // Always show reply section essentially
<Box sx={{ mt: 2, ml: 4, pl: 2, borderLeft: '2px solid rgba(0,0,0,0.1)' }}>
{rating.replies.map((reply: Reply) => (
<Box key={reply.id} sx={{ mb: 2 }}>
<Typography variant="subtitle2" sx={{ fontWeight: 'bold', fontSize: '0.85rem' }}>
{reply.user}
</Typography>
<Typography variant="body2">{reply.text}</Typography>
<Typography variant="caption" color="text.secondary">
{new Date(reply.created_at).toLocaleDateString()}
</Typography>
</Box>
))}
<Box sx={{ mt: 2, display: 'flex', gap: 1 }}>
<TextField
size="small"
placeholder="Write a reply..."
fullWidth
value={replyInputs[rating.id] || ''}
onChange={(e) => setReplyInputs({ ...replyInputs, [rating.id]: e.target.value })}
/>
<Button
variant="text"
disabled={!replyInputs[rating.id]}
onClick={() => handleReplySubmit(rating.id)}
>
Reply
</Button>
<Box sx={{ mt: 2, ml: { xs: 2, md: 4 }, pl: 2, borderLeft: '2px solid rgba(0,0,0,0.1)' }}>
{rating.replies.map((reply: Reply) => (
<Box key={reply.id} sx={{ mb: 2 }}>
<Typography variant="subtitle2" sx={{ fontWeight: 'bold', fontSize: '0.85rem' }}>{reply.user}</Typography>
<Typography variant="body2">{reply.text}</Typography>
<Typography variant="caption" color="text.secondary">{new Date(reply.created_at).toLocaleDateString()}</Typography>
</Box>
</Box>
)}
))}
{currentUser && (
<Box sx={{ mt: 2, display: 'flex', gap: 1 }}>
<TextField size="small" placeholder="Write a reply..." fullWidth value={replyInputs[rating.id] || ''} onChange={(e) => setReplyInputs({ ...replyInputs, [rating.id]: e.target.value })} />
<Button size="small" disabled={!replyInputs[rating.id]} onClick={() => handleReplySubmit(rating.id)}>Reply</Button>
</Box>
)}
</Box>
</CardContent>
</Card>
))