mirror of
https://github.com/narrrl/holy-flavor-rater.git
synced 2026-11-03 16:41:27 +01:00
Merge branch 'main' into narl.io
This commit is contained in:
commit
e9132cd074
11 files changed
+161
-100
No files matched your search
+17
-13
@@ -317,11 +317,13 @@ const App: React.FC = () => {
|
||||
</ListItemButton>
|
||||
</ListItem>
|
||||
|
||||
<ListItem disablePadding>
|
||||
<ListItemButton component={Link} to="/community" onClick={() => setDrawerOpen(false)}>
|
||||
<ListItemText primary={t('nav.community')} />
|
||||
</ListItemButton>
|
||||
</ListItem>
|
||||
{user && (
|
||||
<ListItem disablePadding>
|
||||
<ListItemButton component={Link} to="/community" onClick={() => setDrawerOpen(false)}>
|
||||
<ListItemText primary={t('nav.community')} />
|
||||
</ListItemButton>
|
||||
</ListItem>
|
||||
)}
|
||||
|
||||
<ListItemButton onClick={() => setCatOpen(!catOpen)}>
|
||||
<ListItemText primary={t('nav.categories')} />
|
||||
@@ -444,14 +446,16 @@ const App: React.FC = () => {
|
||||
</Typography>
|
||||
|
||||
<Box sx={{ display: { xs: 'none', md: 'flex' }, ml: 4 }}>
|
||||
<Button
|
||||
color="inherit"
|
||||
component={Link}
|
||||
to="/community"
|
||||
sx={{ fontWeight: 'bold', textTransform: 'none', fontSize: '1rem', mr: 2 }}
|
||||
>
|
||||
{t('nav.community')}
|
||||
</Button>
|
||||
{user && (
|
||||
<Button
|
||||
color="inherit"
|
||||
component={Link}
|
||||
to="/community"
|
||||
sx={{ fontWeight: 'bold', textTransform: 'none', fontSize: '1rem', mr: 2 }}
|
||||
>
|
||||
{t('nav.community')}
|
||||
</Button>
|
||||
)}
|
||||
<Button
|
||||
color="inherit"
|
||||
endIcon={<ArrowDropDownIcon />}
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
import React from 'react';
|
||||
import { Box, Typography, alpha, useTheme } from '@mui/material';
|
||||
|
||||
interface RatingBadgeProps {
|
||||
score: number;
|
||||
size?: 'small' | 'medium' | 'large';
|
||||
sx?: any;
|
||||
}
|
||||
|
||||
const RatingBadge: React.FC<RatingBadgeProps> = ({ score, size = 'medium', sx }) => {
|
||||
const theme = useTheme();
|
||||
const isLarge = size === 'large';
|
||||
const isSmall = size === 'small';
|
||||
|
||||
return (
|
||||
<Box sx={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
bgcolor: alpha(theme.palette.primary.main, 0.1),
|
||||
color: 'primary.main',
|
||||
px: isLarge ? 2.5 : (isSmall ? 1 : 1.5),
|
||||
py: isLarge ? 0.8 : (isSmall ? 0.2 : 0.5),
|
||||
borderRadius: isLarge ? 2.5 : (isSmall ? 1 : 2),
|
||||
minWidth: isLarge ? 70 : (isSmall ? 40 : 50),
|
||||
border: '1px solid',
|
||||
borderColor: alpha(theme.palette.primary.main, 0.2),
|
||||
lineHeight: 1,
|
||||
...sx
|
||||
}}>
|
||||
<Typography
|
||||
variant={isLarge ? "h5" : (isSmall ? "caption" : "subtitle1")}
|
||||
sx={{ fontWeight: '900', lineHeight: 1 }}
|
||||
>
|
||||
{typeof score === 'number' ? score.toFixed(isLarge ? 1 : 0) : score}
|
||||
</Typography>
|
||||
<Typography
|
||||
variant="caption"
|
||||
sx={{
|
||||
fontSize: isLarge ? '0.7rem' : (isSmall ? '0.5rem' : '0.6rem'),
|
||||
fontWeight: 'bold',
|
||||
opacity: 0.8,
|
||||
mt: 0.2
|
||||
}}
|
||||
>
|
||||
/ 10
|
||||
</Typography>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default RatingBadge;
|
||||
@@ -88,9 +88,10 @@ const resources = {
|
||||
},
|
||||
"common": {
|
||||
"search": "Search...",
|
||||
"back": "Back",
|
||||
"backTo": "Back to {{page}}",
|
||||
"backToArchive": "Back to Archive",
|
||||
"backToHome": "Back to Home",
|
||||
"backTo": "Back to",
|
||||
"buyNow": "Buy Now",
|
||||
"reviews": "reviews",
|
||||
"rank": "Rank",
|
||||
@@ -218,9 +219,10 @@ const resources = {
|
||||
},
|
||||
"common": {
|
||||
"search": "Suchen...",
|
||||
"back": "Zurück",
|
||||
"backTo": "Zurück zu {{page}}",
|
||||
"backToArchive": "Zurück zum Archiv",
|
||||
"backToHome": "Zurück zur Startseite",
|
||||
"backTo": "Zurück zu",
|
||||
"buyNow": "Jetzt kaufen",
|
||||
"reviews": "Bewertungen",
|
||||
"rank": "Rang",
|
||||
|
||||
@@ -27,6 +27,7 @@ import SendIcon from '@mui/icons-material/Send';
|
||||
import WhatshotIcon from '@mui/icons-material/Whatshot';
|
||||
import NotificationsIcon from '@mui/icons-material/Notifications';
|
||||
import PeopleIcon from '@mui/icons-material/People';
|
||||
import ArrowBackIcon from '@mui/icons-material/ArrowBack';
|
||||
import { Link, useNavigate } from 'react-router-dom';
|
||||
import api from '../api';
|
||||
import { useTitle } from '../hooks/useTitle';
|
||||
@@ -34,6 +35,7 @@ import RichText from '../components/RichText';
|
||||
import { formatDate } from '../utils/date';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import MentionTextField from '../components/MentionTextField';
|
||||
import RatingBadge from '../components/RatingBadge';
|
||||
|
||||
interface Reply {
|
||||
id: number;
|
||||
@@ -100,6 +102,14 @@ const CommunityFeed: React.FC = () => {
|
||||
user.username.toLowerCase().includes(followingSearch.toLowerCase())
|
||||
);
|
||||
|
||||
const handleGoBack = () => {
|
||||
if (window.history.length > 1) {
|
||||
navigate(-1);
|
||||
} else {
|
||||
navigate('/');
|
||||
}
|
||||
};
|
||||
|
||||
const fetchFeedData = async (pageNum: number) => {
|
||||
setLoading(true);
|
||||
try {
|
||||
@@ -177,6 +187,28 @@ const CommunityFeed: React.FC = () => {
|
||||
|
||||
return (
|
||||
<Container maxWidth="lg" sx={{ py: 4 }}>
|
||||
<Box sx={{ mb: 4 }}>
|
||||
<Button
|
||||
variant="outlined"
|
||||
onClick={handleGoBack}
|
||||
startIcon={<ArrowBackIcon />}
|
||||
sx={{
|
||||
borderRadius: 2,
|
||||
textTransform: 'none',
|
||||
fontWeight: 'bold',
|
||||
color: 'text.secondary',
|
||||
borderColor: 'divider',
|
||||
'&:hover': {
|
||||
borderColor: 'primary.main',
|
||||
color: 'primary.main',
|
||||
bgcolor: 'transparent'
|
||||
}
|
||||
}}
|
||||
>
|
||||
{window.history.length > 1 ? t('common.back') : t('common.backToHome')}
|
||||
</Button>
|
||||
</Box>
|
||||
|
||||
<Box sx={{ mb: 6 }}>
|
||||
<Typography variant="h3" sx={{ fontWeight: '800', mb: 1 }}>{t('community.title')}</Typography>
|
||||
<Typography variant="h6" color="text.secondary">
|
||||
@@ -239,18 +271,7 @@ const CommunityFeed: React.FC = () => {
|
||||
</Typography>
|
||||
</Box>
|
||||
</Box>
|
||||
<Box sx={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
bgcolor: (theme) => alpha(theme.palette.primary.main, 0.1),
|
||||
color: 'primary.main',
|
||||
px: 2, py: 0.5,
|
||||
borderRadius: 2
|
||||
}}>
|
||||
<Typography variant="h5" sx={{ fontWeight: '900', lineHeight: 1 }}>{rating.score}</Typography>
|
||||
<Typography variant="caption" sx={{ fontSize: '0.6rem', fontWeight: 'bold' }}>/ 10</Typography>
|
||||
</Box>
|
||||
<RatingBadge score={rating.score} />
|
||||
</Box>
|
||||
|
||||
{/* Flavor and Comment */}
|
||||
@@ -397,10 +418,9 @@ const CommunityFeed: React.FC = () => {
|
||||
<Avatar src={flavor.image_url} variant="rounded" sx={{ width: 32, height: 32, mr: 2, bgcolor: 'transparent' }} imgProps={{ sx: { objectFit: 'contain' } }} />
|
||||
<ListItemText
|
||||
primary={flavor.name}
|
||||
secondary={`${(flavor.average_rating || 0).toFixed(1)} / 10`}
|
||||
primaryTypographyProps={{ variant: 'body2', fontWeight: 'bold', noWrap: true }}
|
||||
secondaryTypographyProps={{ variant: 'caption', color: 'primary.main', fontWeight: 'bold' }}
|
||||
/>
|
||||
<RatingBadge score={flavor.average_rating || 0} size="small" />
|
||||
</ListItemButton>
|
||||
))}
|
||||
</List>
|
||||
|
||||
@@ -107,7 +107,7 @@ const Dashboard: React.FC = () => {
|
||||
}
|
||||
}}
|
||||
>
|
||||
{t('common.backToHome').replace('Home', 'Back')}
|
||||
{window.history.length > 1 ? t('common.back') : t('common.backToHome')}
|
||||
</Button>
|
||||
|
||||
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', mb: 6, flexWrap: 'wrap', gap: 2 }}>
|
||||
|
||||
@@ -4,15 +4,16 @@ import {
|
||||
Card,
|
||||
CardContent,
|
||||
Box,
|
||||
Rating as MuiRating,
|
||||
TextField,
|
||||
Button,
|
||||
Accordion,
|
||||
AccordionSummary,
|
||||
AccordionDetails
|
||||
AccordionDetails,
|
||||
Rating as MuiRating
|
||||
} from '@mui/material';
|
||||
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
|
||||
import api from '../api';
|
||||
import RatingBadge from '../components/RatingBadge';
|
||||
|
||||
interface Rating {
|
||||
id: number;
|
||||
@@ -143,8 +144,7 @@ const FlavorList: React.FC = () => {
|
||||
)}
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', mb: 1 }}>
|
||||
<Typography variant="body2" sx={{ mr: 1 }}>Avg:</Typography>
|
||||
<MuiRating value={flavor.average_rating || 0} readOnly precision={0.5} max={10} size="small" />
|
||||
<Typography variant="body2" sx={{ ml: 1 }}>({(flavor.average_rating || 0).toFixed(1)})</Typography>
|
||||
<RatingBadge score={flavor.average_rating || 0} size="small" />
|
||||
</Box>
|
||||
|
||||
{flavor.ratings && flavor.ratings.length > 0 && (
|
||||
@@ -175,7 +175,7 @@ const FlavorList: React.FC = () => {
|
||||
<MuiRating
|
||||
max={10}
|
||||
value={ratingInput[flavor.id]?.score || 0}
|
||||
onChange={(_, val) => setRatingInput({...ratingInput, [flavor.id]: {...(ratingInput[flavor.id] || {comment: ''}), score: val || 0}})}
|
||||
onChange={(_: any, val: number | null) => setRatingInput({...ratingInput, [flavor.id]: {...(ratingInput[flavor.id] || {comment: ''}), score: val || 0}})}
|
||||
/>
|
||||
<TextField
|
||||
label="Comment (Optional)"
|
||||
|
||||
@@ -7,22 +7,21 @@ import {
|
||||
CircularProgress,
|
||||
Button,
|
||||
Avatar,
|
||||
Rating as MuiRating,
|
||||
Paper,
|
||||
Chip,
|
||||
Container,
|
||||
IconButton,
|
||||
useMediaQuery,
|
||||
alpha
|
||||
} from '@mui/material';
|
||||
import { Link, useLocation } from 'react-router-dom';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import StarIcon from '@mui/icons-material/Star';
|
||||
import ChevronLeftIcon from '@mui/icons-material/ChevronLeft';
|
||||
import ChevronRightIcon from '@mui/icons-material/ChevronRight';
|
||||
import EmojiEventsIcon from '@mui/icons-material/EmojiEvents';
|
||||
import api from '../api';
|
||||
import { useTitle } from '../hooks/useTitle';
|
||||
import { formatDate } from '../utils/date';
|
||||
import RatingBadge from '../components/RatingBadge';
|
||||
|
||||
interface Flavor {
|
||||
id: number;
|
||||
@@ -50,7 +49,6 @@ interface Review {
|
||||
|
||||
const MainPage: React.FC = () => {
|
||||
const { t } = useTranslation();
|
||||
const isMobileSize = useMediaQuery('(max-width:600px)');
|
||||
const [topFlavors, setTopFlavors] = useState<Flavor[]>([]);
|
||||
const [newestFlavors, setNewestFlavors] = useState<Flavor[]>([]);
|
||||
const [recentReviews, setRecentReviews] = useState<Review[]>([]);
|
||||
@@ -202,8 +200,7 @@ const MainPage: React.FC = () => {
|
||||
<Typography variant="h6" sx={{ fontSize: '1rem' }}>{flavor.name}</Typography>
|
||||
<Typography variant="caption" color="text.secondary" sx={{ display: 'block', mb: 1 }}>{flavor.category_name}</Typography>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center' }}>
|
||||
<MuiRating value={flavor.average_rating || 0} readOnly precision={0.5} max={10} size="small" />
|
||||
<Typography variant="caption" sx={{ ml: 1 }}>({(flavor.average_rating || 0).toFixed(1)})</Typography>
|
||||
<RatingBadge score={flavor.average_rating || 0} size="small" />
|
||||
</Box>
|
||||
</CardContent>
|
||||
</Box>
|
||||
@@ -362,7 +359,7 @@ const MainPage: React.FC = () => {
|
||||
<Box sx={{ flex: { xs: '1 1 100%', xl: '1 1 65%' }, minWidth: 0 }}>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', mb: 4, justifyContent: 'space-between' }}>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center' }}>
|
||||
<StarIcon color="primary" sx={{ mr: 1, fontSize: '2rem' }} />
|
||||
<EmojiEventsIcon color="primary" sx={{ mr: 1, fontSize: '2rem' }} />
|
||||
<Typography variant="h4" sx={{ fontWeight: 'bold' }}>{t('home.hallOfFame')}</Typography>
|
||||
</Box>
|
||||
<Box sx={{ display: 'flex', gap: 1 }}>
|
||||
@@ -409,10 +406,10 @@ const MainPage: React.FC = () => {
|
||||
{currentTop.name}
|
||||
</Typography>
|
||||
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', mb: { xs: 2, md: 3 }, flexWrap: 'wrap' }}>
|
||||
<MuiRating value={currentTop.average_rating || 0} readOnly precision={0.5} max={10} size={isMobileSize ? "medium" : "large"} />
|
||||
<Typography variant="h5" sx={{ ml: { xs: 0, sm: 2 }, mt: { xs: 1, sm: 0 }, fontWeight: 'bold', fontSize: { xs: '1.25rem', md: '1.5rem' } }}>
|
||||
{(currentTop.average_rating || 0).toFixed(1)} / 10
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', mb: { xs: 2, md: 3 }, flexWrap: 'wrap', gap: 2 }}>
|
||||
<RatingBadge score={currentTop.average_rating || 0} size="large" />
|
||||
<Typography variant="body2" color="text.secondary" sx={{ fontWeight: 'bold' }}>
|
||||
{currentTop.ratings.length} {t('common.reviews')}
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
@@ -497,14 +494,7 @@ const MainPage: React.FC = () => {
|
||||
<Typography variant="subtitle2" sx={{ fontWeight: 'bold', fontSize: '0.8rem', '&:hover': { color: 'primary.main' } }}>{review.user}</Typography>
|
||||
</Link>
|
||||
<Box sx={{ flexGrow: 1 }} />
|
||||
{/* Compact rating for sidebar and tablets */}
|
||||
<Box sx={{ display: { xs: 'flex', xl: 'none' }, alignItems: 'center', gap: 0.5 }}>
|
||||
<StarIcon sx={{ fontSize: '1rem', color: 'primary.main' }} />
|
||||
<Typography variant="caption" sx={{ fontWeight: 'bold' }}>{review.score}/10</Typography>
|
||||
</Box>
|
||||
<Box sx={{ display: { xs: 'none', xl: 'flex' }, alignItems: 'center' }}>
|
||||
<MuiRating value={review.score} readOnly size="small" max={10} />
|
||||
</Box>
|
||||
<RatingBadge score={review.score} size="small" />
|
||||
</Box>
|
||||
<Typography variant="caption" color="text.secondary" display="block" sx={{ mb: 1 }}>
|
||||
{formatDate(review.created_at)} • on <Link to={`/flavor/${review.flavor}`} style={{ color: 'inherit', fontWeight: 'bold' }}>{review.flavor_name}</Link>
|
||||
|
||||
@@ -19,6 +19,7 @@ import ArrowBackIcon from '@mui/icons-material/ArrowBack';
|
||||
import { Link } from 'react-router-dom';
|
||||
import api from '../api';
|
||||
import { useTitle } from '../hooks/useTitle';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
interface Rating {
|
||||
id: number;
|
||||
@@ -43,6 +44,7 @@ interface ProfileData {
|
||||
}
|
||||
|
||||
const PublicProfile: React.FC = () => {
|
||||
const { t } = useTranslation();
|
||||
const { username } = useParams<{ username: string }>();
|
||||
const navigate = useNavigate();
|
||||
const [data, setData] = useState<ProfileData | null>(null);
|
||||
@@ -144,7 +146,7 @@ const PublicProfile: React.FC = () => {
|
||||
}
|
||||
}}
|
||||
>
|
||||
Back
|
||||
{window.history.length > 1 ? t('common.back') : t('common.backToHome')}
|
||||
</Button>
|
||||
|
||||
{/* Profile Header */}
|
||||
|
||||
@@ -17,7 +17,7 @@ import {
|
||||
Grid
|
||||
} from '@mui/material';
|
||||
import ArrowBackIcon from '@mui/icons-material/ArrowBack';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import api from '../api';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useTitle } from '../hooks/useTitle';
|
||||
@@ -30,6 +30,7 @@ interface SettingsProps {
|
||||
|
||||
const Settings: React.FC<SettingsProps> = ({ themeName, onThemeChange }) => {
|
||||
const { t, i18n } = useTranslation();
|
||||
const navigate = useNavigate();
|
||||
useTitle(t('nav.settings'));
|
||||
const [username, setUsername] = useState('');
|
||||
const [email, setEmail] = useState('');
|
||||
@@ -44,6 +45,14 @@ const Settings: React.FC<SettingsProps> = ({ themeName, onThemeChange }) => {
|
||||
const [isDeleting, setIsDeleting] = useState(false);
|
||||
const [message, setMessage] = useState<{ type: 'success' | 'error', text: string } | null>(null);
|
||||
|
||||
const handleGoBack = () => {
|
||||
if (window.history.length > 1) {
|
||||
navigate(-1);
|
||||
} else {
|
||||
navigate('/');
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const fetchUser = async () => {
|
||||
try {
|
||||
@@ -162,12 +171,23 @@ const Settings: React.FC<SettingsProps> = ({ themeName, onThemeChange }) => {
|
||||
<Container maxWidth="lg" sx={{ py: 4 }}>
|
||||
<Button
|
||||
variant="outlined"
|
||||
component={Link}
|
||||
to="/"
|
||||
onClick={handleGoBack}
|
||||
startIcon={<ArrowBackIcon />}
|
||||
sx={{ mb: 4, textTransform: 'none', borderRadius: 2 }}
|
||||
sx={{
|
||||
mb: 4,
|
||||
borderRadius: 2,
|
||||
textTransform: 'none',
|
||||
fontWeight: 'bold',
|
||||
color: 'text.secondary',
|
||||
borderColor: 'divider',
|
||||
'&:hover': {
|
||||
borderColor: 'primary.main',
|
||||
color: 'primary.main',
|
||||
bgcolor: 'transparent'
|
||||
}
|
||||
}}
|
||||
>
|
||||
{t('common.backToHome')}
|
||||
{window.history.length > 1 ? t('common.back') : t('common.backToHome')}
|
||||
</Button>
|
||||
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 3, mb: 6 }}>
|
||||
|
||||
@@ -5,7 +5,6 @@ import {
|
||||
Box,
|
||||
Card,
|
||||
CardContent,
|
||||
Rating as MuiRating,
|
||||
CircularProgress,
|
||||
Container,
|
||||
Button
|
||||
@@ -13,6 +12,8 @@ import {
|
||||
import ArrowBackIcon from '@mui/icons-material/ArrowBack';
|
||||
import api from '../../api';
|
||||
import { useTitle } from '../../hooks/useTitle';
|
||||
import RatingBadge from '../../components/RatingBadge';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
interface Rating {
|
||||
id: number;
|
||||
@@ -37,6 +38,7 @@ interface Flavor {
|
||||
}
|
||||
|
||||
const CategoryFlavors: React.FC = () => {
|
||||
const { t } = useTranslation();
|
||||
const { slug } = useParams<{ slug: string }>();
|
||||
const navigate = useNavigate();
|
||||
const [flavors, setFlavors] = useState<Flavor[]>([]);
|
||||
@@ -95,7 +97,7 @@ const CategoryFlavors: React.FC = () => {
|
||||
}
|
||||
}}
|
||||
>
|
||||
Back
|
||||
{window.history.length > 1 ? t('common.back') : t('common.backToHome')}
|
||||
</Button>
|
||||
</Box>
|
||||
|
||||
@@ -152,8 +154,7 @@ const CategoryFlavors: React.FC = () => {
|
||||
<Typography variant="h6" sx={{ fontSize: '1.1rem', mb: 1 }}>{flavor.name}</Typography>
|
||||
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', mb: 1 }}>
|
||||
<MuiRating value={flavor.average_rating || 0} readOnly precision={0.5} max={10} size="small" />
|
||||
<Typography variant="caption" sx={{ ml: 1 }}>({(flavor.average_rating || 0).toFixed(1)})</Typography>
|
||||
<RatingBadge score={flavor.average_rating || 0} size="small" />
|
||||
</Box>
|
||||
<Typography variant="caption" color="text.secondary">{flavor.ratings.length} reviews</Typography>
|
||||
</CardContent>
|
||||
|
||||
@@ -27,6 +27,7 @@ import CommentIcon from '@mui/icons-material/Comment';
|
||||
import { formatDate } from '../../utils/date';
|
||||
import MentionTextField from '../../components/MentionTextField';
|
||||
import RichText from '../../components/RichText';
|
||||
import RatingBadge from '../../components/RatingBadge';
|
||||
|
||||
interface Reply {
|
||||
id: number;
|
||||
@@ -193,34 +194,6 @@ const FlavorDetail: React.FC = () => {
|
||||
}
|
||||
};
|
||||
|
||||
const RatingBadge = ({ score, size = 'medium' }: { score: number, size?: 'small' | 'medium' | 'large' }) => {
|
||||
const isLarge = size === 'large';
|
||||
const isSmall = size === 'small';
|
||||
|
||||
return (
|
||||
<Box sx={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
bgcolor: (theme) => alpha(theme.palette.primary.main, 0.1),
|
||||
color: 'primary.main',
|
||||
px: isLarge ? 3 : (isSmall ? 1 : 2),
|
||||
py: isLarge ? 1 : 0.5,
|
||||
borderRadius: isLarge ? 3 : 2,
|
||||
minWidth: isLarge ? 80 : 50,
|
||||
border: '1px solid',
|
||||
borderColor: (theme) => alpha(theme.palette.primary.main, 0.2)
|
||||
}}>
|
||||
<Typography variant={isLarge ? "h4" : (isSmall ? "subtitle2" : "h6")} sx={{ fontWeight: '900', lineHeight: 1 }}>
|
||||
{score}
|
||||
</Typography>
|
||||
<Typography variant="caption" sx={{ fontSize: isLarge ? '0.75rem' : '0.6rem', fontWeight: 'bold', opacity: 0.8 }}>
|
||||
/ 10
|
||||
</Typography>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
if (loading) return <Box sx={{ display: 'flex', justifyContent: 'center', mt: 8 }}><CircularProgress /></Box>;
|
||||
if (!flavor) return <Typography>Flavor not found</Typography>;
|
||||
|
||||
@@ -245,7 +218,7 @@ const FlavorDetail: React.FC = () => {
|
||||
}
|
||||
}}
|
||||
>
|
||||
{t('common.backToHome').replace('Home', 'Back')}
|
||||
{window.history.length > 1 ? t('common.back') : t('common.backToHome')}
|
||||
</Button>
|
||||
</Box>
|
||||
|
||||
@@ -311,13 +284,10 @@ const FlavorDetail: React.FC = () => {
|
||||
</Typography>
|
||||
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 2, mb: 3 }}>
|
||||
<RatingBadge score={Number((flavor.average_rating || 0).toFixed(1))} size="large" />
|
||||
<Box>
|
||||
<MuiRating value={flavor.average_rating || 0} readOnly precision={0.5} size="small" />
|
||||
<Typography variant="body2" color="text.secondary" sx={{ fontWeight: 'bold' }}>
|
||||
{flavor.ratings.length} {t('common.reviews')}
|
||||
</Typography>
|
||||
</Box>
|
||||
<RatingBadge score={flavor.average_rating || 0} size="large" />
|
||||
<Typography variant="body2" color="text.secondary" sx={{ fontWeight: 'bold' }}>
|
||||
{flavor.ratings.length} {t('common.reviews')}
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
<Typography variant="body2" sx={{ lineHeight: 1.7, color: 'text.secondary', mb: 4 }}>
|
||||
|
||||
Reference in new issue
Block a user