renamed dashboard to my collection, added limited status badges, and improved navigation discoverability

This commit is contained in:
nvrl committed 2026-02-19 16:56:59 +01:00
1 parent dc4b5e4512
commit 469ca3eb47
6 files changed
+126 -85

No files matched your search

+56
View File
@@ -0,0 +1,56 @@
import React from 'react';
import { Box, Typography, alpha, useTheme } from '@mui/material';
import HistoryIcon from '@mui/icons-material/History';
import BlockIcon from '@mui/icons-material/Block';
interface StatusBadgeProps {
isLegacy?: boolean;
isAvailable?: boolean;
size?: 'small' | 'medium';
sx?: any;
}
const StatusBadge: React.FC<StatusBadgeProps> = ({ isLegacy, isAvailable, size = 'medium', sx }) => {
const theme = useTheme();
const isSmall = size === 'small';
if (isAvailable && !isLegacy) return null;
const label = isLegacy ? 'Limited' : 'Out of Stock';
const color = isLegacy ? theme.palette.warning.main : theme.palette.error.main;
const Icon = isLegacy ? HistoryIcon : BlockIcon;
return (
<Box sx={{
display: 'inline-flex',
alignItems: 'center',
gap: 0.5,
bgcolor: alpha(color, 0.9),
color: '#fff',
px: isSmall ? 0.8 : 1.2,
py: isSmall ? 0.2 : 0.4,
borderRadius: 1.5,
boxShadow: '0 4px 12px rgba(0,0,0,0.2)',
backdropFilter: 'blur(4px)',
border: '1px solid',
borderColor: alpha('#fff', 0.2),
...sx
}}>
<Icon sx={{ fontSize: isSmall ? '0.7rem' : '0.9rem' }} />
<Typography
variant="caption"
sx={{
fontWeight: '900',
textTransform: 'uppercase',
letterSpacing: 0.5,
fontSize: isSmall ? '0.55rem' : '0.65rem',
lineHeight: 1
}}
>
{label}
</Typography>
</Box>
);
};
export default StatusBadge;
+10 -8
View File
@@ -9,7 +9,7 @@ const resources = {
"home": "Home", "home": "Home",
"community": "Community", "community": "Community",
"about": "About", "about": "About",
"dashboard": "Dashboard", "dashboard": "My Collection",
"profile": "My Profile", "profile": "My Profile",
"settings": "Settings", "settings": "Settings",
"logout": "Logout", "logout": "Logout",
@@ -25,6 +25,7 @@ const resources = {
"packs-and-other": "Packs & Other" "packs-and-other": "Packs & Other"
}, },
"dashboard": { "dashboard": {
"title": "My Collection",
"welcome": "Welcome back, {{username}}!", "welcome": "Welcome back, {{username}}!",
"shareProfile": "Share Profile", "shareProfile": "Share Profile",
"copySuccess": "Link copied to clipboard!", "copySuccess": "Link copied to clipboard!",
@@ -32,14 +33,14 @@ const resources = {
"missing": "Missing", "missing": "Missing",
"noRatings": "You haven't rated any flavors yet.", "noRatings": "You haven't rated any flavors yet.",
"noComment": "No comment provided.", "noComment": "No comment provided.",
"exploreFlavors": "Go explore flavors", "exploreFlavors": "Explore New Flavors",
"allRated": "Amazing! You've rated everything! 🏆", "allRated": "Amazing! You've rated everything! 🏆",
"myReviews": "My Reviews", "myReviews": "My Rated Flavors",
"exploreNew": "Explore New", "exploreNew": "Explore New",
"editingReview": "Editing review for {{flavor}}", "editingReview": "Editing review for {{flavor}}",
"newScore": "New Score:", "newScore": "New Score:",
"searchMissing": "Search flavors you haven't rated...", "searchMissing": "Search flavors you haven't rated...",
"searchRated": "Search your reviews...", "searchRated": "Search your collection...",
"circleAvg": "Average rating from your friends", "circleAvg": "Average rating from your friends",
"sortBy": "Sort by", "sortBy": "Sort by",
"date": "Date", "date": "Date",
@@ -250,7 +251,7 @@ const resources = {
"home": "Startseite", "home": "Startseite",
"community": "Community", "community": "Community",
"about": "Über uns", "about": "Über uns",
"dashboard": "Dashboard", "dashboard": "Mein Archiv",
"profile": "Mein Profil", "profile": "Mein Profil",
"settings": "Einstellungen", "settings": "Einstellungen",
"logout": "Abmelden", "logout": "Abmelden",
@@ -266,6 +267,7 @@ const resources = {
"packs-and-other": "Bundles & Mehr" "packs-and-other": "Bundles & Mehr"
}, },
"dashboard": { "dashboard": {
"title": "Mein Archiv",
"welcome": "Willkommen zurück, {{username}}!", "welcome": "Willkommen zurück, {{username}}!",
"shareProfile": "Profil teilen", "shareProfile": "Profil teilen",
"copySuccess": "Link in die Zwischenablage kopiert!", "copySuccess": "Link in die Zwischenablage kopiert!",
@@ -273,14 +275,14 @@ const resources = {
"missing": "Fehlend", "missing": "Fehlend",
"noRatings": "Du hast noch keine Sorten bewertet.", "noRatings": "Du hast noch keine Sorten bewertet.",
"noComment": "Kein Kommentar abgegeben.", "noComment": "Kein Kommentar abgegeben.",
"exploreFlavors": "Sorten entdecken", "exploreFlavors": "Neue Sorten entdecken",
"allRated": "Wahnsinn! Du hast alles bewertet! 🏆", "allRated": "Wahnsinn! Du hast alles bewertet! 🏆",
"myReviews": "Meine Rezensionen", "myReviews": "Meine Sammlungen",
"exploreNew": "Neu entdecken", "exploreNew": "Neu entdecken",
"editingReview": "Rezension bearbeiten für {{flavor}}", "editingReview": "Rezension bearbeiten für {{flavor}}",
"newScore": "Neue Bewertung:", "newScore": "Neue Bewertung:",
"searchMissing": "Suche nach Sorten, die du noch nicht bewertet hast...", "searchMissing": "Suche nach Sorten, die du noch nicht bewertet hast...",
"searchRated": "Suche in deinen Rezensionen...", "searchRated": "Suche in deinem Archiv...",
"circleAvg": "Durchschnittsbewertung deiner Freunde", "circleAvg": "Durchschnittsbewertung deiner Freunde",
"sortBy": "Sortieren nach", "sortBy": "Sortieren nach",
"date": "Datum", "date": "Datum",
+31 -33
View File
@@ -19,6 +19,7 @@ import {
Chip, Chip,
Collapse, Collapse,
TextField, TextField,
Rating as MuiRating,
InputAdornment, InputAdornment,
Tooltip, Tooltip,
useMediaQuery, useMediaQuery,
@@ -36,9 +37,9 @@ import { useTranslation } from 'react-i18next';
import api from '../api'; import api from '../api';
import { useTitle } from '../hooks/useTitle'; import { useTitle } from '../hooks/useTitle';
import RatingBadge from '../components/RatingBadge'; import RatingBadge from '../components/RatingBadge';
import StatusBadge from '../components/StatusBadge';
import MentionTextField from '../components/MentionTextField'; import MentionTextField from '../components/MentionTextField';
import RichText from '../components/RichText'; import RichText from '../components/RichText';
import DynamicBanner from '../components/DynamicBanner';
import { formatDate } from '../utils/date'; import { formatDate } from '../utils/date';
interface DashboardData { interface DashboardData {
@@ -54,7 +55,7 @@ const Dashboard: React.FC = () => {
const navigate = useNavigate(); const navigate = useNavigate();
const theme = useTheme(); const theme = useTheme();
const isMobile = useMediaQuery(theme.breakpoints.down('lg')); const isMobile = useMediaQuery(theme.breakpoints.down('lg'));
useTitle(t('nav.dashboard')); useTitle(t('dashboard.title'));
const [data, setData] = useState<DashboardData | null>(null); const [data, setData] = useState<DashboardData | null>(null);
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true);
const [activeTab, setActiveTab] = useState(0); const [activeTab, setActiveTab] = useState(0);
@@ -232,14 +233,17 @@ const Dashboard: React.FC = () => {
<Container maxWidth="lg" sx={{ py: 4 }}> <Container maxWidth="lg" sx={{ py: 4 }}>
{/* Action Bar */} {/* Action Bar */}
<Box sx={{ mb: 4, display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}> <Box sx={{ mb: 4, display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<Button <Box sx={{ display: 'flex', alignItems: 'center', gap: 2 }}>
variant="outlined" <Button
onClick={handleGoBack} variant="outlined"
startIcon={<ArrowBackIcon />} onClick={handleGoBack}
sx={{ borderRadius: 2, textTransform: 'none', fontWeight: 'bold', color: 'text.secondary', borderColor: 'divider' }} startIcon={<ArrowBackIcon />}
> sx={{ borderRadius: 2, textTransform: 'none', fontWeight: 'bold', color: 'text.secondary', borderColor: 'divider' }}
{window.history.length > 1 ? t('common.back') : t('common.backToHome')} >
</Button> {t('common.back')}
</Button>
<Typography variant="h5" sx={{ fontWeight: '900', display: { xs: 'none', md: 'block' } }}>{t('dashboard.title')}</Typography>
</Box>
<Button <Button
variant="contained" variant="contained"
@@ -263,7 +267,7 @@ const Dashboard: React.FC = () => {
position: 'relative' position: 'relative'
}}> }}>
<Box sx={{ <Box sx={{
height: { xs: 100, sm: 140 }, height: { xs: 80, sm: 100 },
position: 'relative', position: 'relative',
overflow: 'hidden' overflow: 'hidden'
}}> }}>
@@ -279,21 +283,13 @@ const Dashboard: React.FC = () => {
`, `,
zIndex: 0 zIndex: 0
}} /> }} />
{/* Layer 2: Interactive Generative Art */}
<DynamicBanner
username={data.user.username}
palette={palette}
ratingsCount={data.rated_count}
followersCount={0}
/>
</Box> </Box>
<CardContent sx={{ pt: 0, px: { xs: 2, sm: 4 }, pb: 4, position: 'relative', zIndex: 2 }}> <CardContent sx={{ pt: 0, px: { xs: 2, sm: 4 }, pb: 4, position: 'relative', zIndex: 2 }}>
<Box sx={{ display: 'flex', flexDirection: { xs: 'column', sm: 'row' }, alignItems: { xs: 'center', sm: 'flex-end' }, gap: { xs: 2, sm: 4 } }}> <Box sx={{ display: 'flex', flexDirection: { xs: 'column', sm: 'row' }, alignItems: { xs: 'center', sm: 'flex-end' }, gap: { xs: 2, sm: 4 } }}>
<Box sx={{ <Box sx={{
position: 'relative', position: 'relative',
mt: { xs: -6, sm: -8 }, mt: { xs: -5, sm: -6 },
p: 0.5, p: 0.5,
borderRadius: '50%', borderRadius: '50%',
background: `linear-gradient(135deg, ${palette[0] || theme.palette.primary.main}, ${theme.palette.secondary.main})`, background: `linear-gradient(135deg, ${palette[0] || theme.palette.primary.main}, ${theme.palette.secondary.main})`,
@@ -303,8 +299,8 @@ const Dashboard: React.FC = () => {
<Avatar <Avatar
src={data.user.avatar || undefined} src={data.user.avatar || undefined}
sx={{ sx={{
width: { xs: 100, sm: 120 }, width: { xs: 90, sm: 110 },
height: { xs: 100, sm: 120 }, height: { xs: 90, sm: 110 },
border: '4px solid', border: '4px solid',
borderColor: (theme) => theme.palette.background.paper, borderColor: (theme) => theme.palette.background.paper,
fontSize: '3rem', fontSize: '3rem',
@@ -356,7 +352,7 @@ const Dashboard: React.FC = () => {
{/* Tabs Section */} {/* Tabs Section */}
<Box sx={{ borderBottom: 1, borderColor: 'divider', mb: 4 }}> <Box sx={{ borderBottom: 1, borderColor: 'divider', mb: 4 }}>
<Tabs value={activeTab} onChange={(_, v) => setActiveTab(v)} textColor="primary" indicatorColor="primary"> <Tabs value={activeTab} onChange={(_, v) => setActiveTab(v)} textColor="primary" indicatorColor="primary" variant="fullWidth">
<Tab label={t('dashboard.myReviews')} sx={{ fontWeight: '900', textTransform: 'none', fontSize: '1.1rem' }} /> <Tab label={t('dashboard.myReviews')} sx={{ fontWeight: '900', textTransform: 'none', fontSize: '1.1rem' }} />
<Tab label={t('dashboard.exploreNew')} sx={{ fontWeight: '900', textTransform: 'none', fontSize: '1.1rem' }} /> <Tab label={t('dashboard.exploreNew')} sx={{ fontWeight: '900', textTransform: 'none', fontSize: '1.1rem' }} />
</Tabs> </Tabs>
@@ -422,7 +418,7 @@ const Dashboard: React.FC = () => {
{filteredAndSortedRated.length === 0 ? ( {filteredAndSortedRated.length === 0 ? (
<Box sx={{ py: 10, textAlign: 'center', bgcolor: 'action.hover', borderRadius: 4, border: '1px dashed', borderColor: 'divider' }}> <Box sx={{ py: 10, textAlign: 'center', bgcolor: 'action.hover', borderRadius: 4, border: '1px dashed', borderColor: 'divider' }}>
<Typography variant="h6" color="text.secondary">{t('dashboard.noRatings')}</Typography> <Typography variant="h6" color="text.secondary">{t('dashboard.noRatings')}</Typography>
<Button onClick={() => setActiveTab(1)} sx={{ mt: 2 }}>{t('dashboard.exploreFlavors')}</Button> <Button onClick={() => setActiveTab(1)} variant="contained" sx={{ mt: 2, borderRadius: 2 }}>{t('dashboard.exploreFlavors')}</Button>
</Box> </Box>
) : ( ) : (
filteredAndSortedRated.map(rating => ( filteredAndSortedRated.map(rating => (
@@ -440,11 +436,9 @@ const Dashboard: React.FC = () => {
<Typography variant="subtitle2" sx={{ fontWeight: 'bold' }}>{t('dashboard.editingReview', { flavor: rating.flavor_name })}</Typography> <Typography variant="subtitle2" sx={{ fontWeight: 'bold' }}>{t('dashboard.editingReview', { flavor: rating.flavor_name })}</Typography>
<Stack direction="row" spacing={2} alignItems="center"> <Stack direction="row" spacing={2} alignItems="center">
<Typography variant="body2">{t('dashboard.newScore')}</Typography> <Typography variant="body2">{t('dashboard.newScore')}</Typography>
<TextField <MuiRating
type="number" size="small" max={10}
slotProps={{ input: { inputProps: { min: 1, max: 10 } } }} value={editScore} onChange={(_: any, val: number | null) => setEditScore(val || 0)}
value={editScore} onChange={(e) => setEditScore(Number(e.target.value))}
sx={{ width: 80 }}
/> />
</Stack> </Stack>
<MentionTextField <MentionTextField
@@ -467,9 +461,12 @@ const Dashboard: React.FC = () => {
<Typography variant="h6" sx={{ fontWeight: 'bold', lineHeight: 1.2 }}> <Typography variant="h6" sx={{ fontWeight: 'bold', lineHeight: 1.2 }}>
<Link to={`/flavor/${rating.flavor}`} style={{ color: 'inherit', textDecoration: 'none' }}>{rating.flavor_name}</Link> <Link to={`/flavor/${rating.flavor}`} style={{ color: 'inherit', textDecoration: 'none' }}>{rating.flavor_name}</Link>
</Typography> </Typography>
<Typography variant="caption" color="text.secondary" display="block"> <Stack direction="row" spacing={1} alignItems="center" sx={{ mt: 0.5 }}>
{t(`categories.${rating.category_slug}`, { defaultValue: rating.category_name })} • {formatDate(rating.created_at)} <Typography variant="caption" color="text.secondary">
</Typography> {t(`categories.${rating.category_slug}`, { defaultValue: rating.category_name })} • {formatDate(rating.created_at)}
</Typography>
<StatusBadge isLegacy={rating.is_legacy} isAvailable={rating.is_available} size="small" />
</Stack>
</Box> </Box>
</Box> </Box>
<RatingBadge score={rating.score} size={isMobile ? "medium" : "large"} /> <RatingBadge score={rating.score} size={isMobile ? "medium" : "large"} />
@@ -630,8 +627,9 @@ const Dashboard: React.FC = () => {
'&:hover': { transform: 'scale(1.1)' } '&:hover': { transform: 'scale(1.1)' }
}} }}
/> />
<Box sx={{ position: 'absolute', top: 10, left: 10, zIndex: 2 }}> <Box sx={{ position: 'absolute', top: 10, left: 10, zIndex: 2, display: 'flex', flexDirection: 'column', gap: 1 }}>
<RatingBadge score={flavor.average_rating || 0} size="small" sx={{ height: 24 }} /> <RatingBadge score={flavor.average_rating || 0} size="small" sx={{ height: 24 }} />
<StatusBadge isLegacy={flavor.is_legacy} isAvailable={flavor.is_available} size="small" />
</Box> </Box>
{flavor.followed_average_rating && ( {flavor.followed_average_rating && (
<Box sx={{ position: 'absolute', top: 10, right: 10, zIndex: 2 }}> <Box sx={{ position: 'absolute', top: 10, right: 10, zIndex: 2 }}>
+11 -10
View File
@@ -22,6 +22,7 @@ import api from '../api';
import { useTitle } from '../hooks/useTitle'; import { useTitle } from '../hooks/useTitle';
import { formatDate } from '../utils/date'; import { formatDate } from '../utils/date';
import RatingBadge from '../components/RatingBadge'; import RatingBadge from '../components/RatingBadge';
import StatusBadge from '../components/StatusBadge';
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
interface Flavor { interface Flavor {
@@ -165,15 +166,9 @@ const MainPage: React.FC<MainPageProps> = ({ adminMode }) => {
<Box key={flavor.id}> <Box key={flavor.id}>
<Card sx={{ height: '100%', display: 'flex', flexDirection: 'column', position: 'relative', transition: 'transform 0.2s', '&:hover': { transform: 'scale(1.02)' } }}> <Card sx={{ height: '100%', display: 'flex', flexDirection: 'column', position: 'relative', transition: 'transform 0.2s', '&:hover': { transform: 'scale(1.02)' } }}>
<Box component={Link} to={`/flavor/${flavor.id}`} sx={{ textDecoration: 'none', color: 'inherit', flexGrow: 1, display: 'flex', flexDirection: 'column' }}> <Box component={Link} to={`/flavor/${flavor.id}`} sx={{ textDecoration: 'none', color: 'inherit', flexGrow: 1, display: 'flex', flexDirection: 'column' }}>
{!flavor.is_available && ( <Box sx={{ position: 'absolute', top: 10, right: 10, zIndex: 1 }}>
<Box sx={{ <StatusBadge isLegacy={flavor.is_legacy} isAvailable={flavor.is_available} size="small" />
position: 'absolute', top: 10, right: 10, </Box>
bgcolor: flavor.is_legacy ? 'warning.main' : 'error.main',
color: 'white', px: 1, borderRadius: 1, fontSize: '0.75rem', zIndex: 1
}}>
{flavor.is_legacy ? 'Unavailable' : 'Out of Stock'}
</Box>
)}
{flavor.image_url && ( {flavor.image_url && (
<Box <Box
sx={{ sx={{
@@ -407,6 +402,9 @@ const MainPage: React.FC<MainPageProps> = ({ adminMode }) => {
textDecoration: 'none' textDecoration: 'none'
}} }}
> >
<Box sx={{ position: 'absolute', top: 16, right: 16, zIndex: 1 }}>
<StatusBadge isLegacy={currentTop.is_legacy} isAvailable={currentTop.is_available} />
</Box>
<Box <Box
component="img" component="img"
@@ -553,8 +551,11 @@ const MainPage: React.FC<MainPageProps> = ({ adminMode }) => {
gap: 2 gap: 2
}}> }}>
{newestFlavors.map(flavor => ( {newestFlavors.map(flavor => (
<Card key={flavor.id} sx={{ transition: 'transform 0.2s', '&:hover': { transform: 'translateY(-4px)' }, borderRadius: 3 }}> <Card key={flavor.id} sx={{ transition: 'transform 0.2s', '&:hover': { transform: 'translateY(-4px)' }, borderRadius: 3, position: 'relative' }}>
<Link to={`/flavor/${flavor.id}`} style={{ textDecoration: 'none', color: 'inherit' }}> <Link to={`/flavor/${flavor.id}`} style={{ textDecoration: 'none', color: 'inherit' }}>
<Box sx={{ position: 'absolute', top: 8, right: 8, zIndex: 1 }}>
<StatusBadge isLegacy={flavor.is_legacy} isAvailable={flavor.is_available} size="small" />
</Box>
<Box sx={{ aspectRatio: '1/1', overflow: 'hidden', bgcolor: 'action.hover' }}> <Box sx={{ aspectRatio: '1/1', overflow: 'hidden', bgcolor: 'action.hover' }}>
<Box <Box
component="img" component="img"
@@ -13,6 +13,7 @@ import ArrowBackIcon from '@mui/icons-material/ArrowBack';
import api from '../../api'; import api from '../../api';
import { useTitle } from '../../hooks/useTitle'; import { useTitle } from '../../hooks/useTitle';
import RatingBadge from '../../components/RatingBadge'; import RatingBadge from '../../components/RatingBadge';
import StatusBadge from '../../components/StatusBadge';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
interface Rating { interface Rating {
@@ -114,15 +115,9 @@ const CategoryFlavors: React.FC = () => {
<Box key={flavor.id}> <Box key={flavor.id}>
<Card sx={{ height: '100%', display: 'flex', flexDirection: 'column', position: 'relative', transition: 'transform 0.2s', '&:hover': { transform: 'scale(1.02)' } }}> <Card sx={{ height: '100%', display: 'flex', flexDirection: 'column', position: 'relative', transition: 'transform 0.2s', '&:hover': { transform: 'scale(1.02)' } }}>
<Box component={Link} to={`/flavor/${flavor.id}`} sx={{ textDecoration: 'none', color: 'inherit', flexGrow: 1, display: 'flex', flexDirection: 'column' }}> <Box component={Link} to={`/flavor/${flavor.id}`} sx={{ textDecoration: 'none', color: 'inherit', flexGrow: 1, display: 'flex', flexDirection: 'column' }}>
{!flavor.is_available && ( <Box sx={{ position: 'absolute', top: 10, right: 10, zIndex: 1 }}>
<Box sx={{ <StatusBadge isLegacy={flavor.is_legacy} isAvailable={flavor.is_available} size="small" />
position: 'absolute', top: 10, right: 10, </Box>
bgcolor: flavor.is_legacy ? 'warning.main' : 'error.main',
color: 'white', px: 1, borderRadius: 1, fontSize: '0.75rem', zIndex: 1
}}>
{flavor.is_legacy ? 'Unavailable' : 'Out of Stock'}
</Box>
)}
{flavor.image_url && ( {flavor.image_url && (
<Box <Box
sx={{ sx={{
+14 -25
View File
@@ -28,6 +28,7 @@ import { formatDate } from '../../utils/date';
import MentionTextField from '../../components/MentionTextField'; import MentionTextField from '../../components/MentionTextField';
import RichText from '../../components/RichText'; import RichText from '../../components/RichText';
import RatingBadge from '../../components/RatingBadge'; import RatingBadge from '../../components/RatingBadge';
import StatusBadge from '../../components/StatusBadge';
interface Reply { interface Reply {
id: number; id: number;
@@ -239,31 +240,19 @@ const FlavorDetail: React.FC<FlavorDetailProps> = ({ adminMode }) => {
backdropFilter: 'blur(12px)', backdropFilter: 'blur(12px)',
overflow: 'hidden' overflow: 'hidden'
}}> }}>
<Box sx={{ <Box sx={{
p: 4, p: 4,
display: 'flex', display: 'flex',
alignItems: 'center', alignItems: 'center',
justifyContent: 'center', justifyContent: 'center',
bgcolor: 'action.hover', bgcolor: 'action.hover',
position: 'relative', position: 'relative',
aspectRatio: '1/1' aspectRatio: '1/1'
}}> }}>
{!flavor.is_available && ( <Box sx={{ position: 'absolute', top: 16, right: 16, zIndex: 1 }}>
<Chip <StatusBadge isLegacy={flavor.is_legacy} isAvailable={flavor.is_available} />
label={flavor.is_legacy ? "Legacy" : "Out of Stock"} </Box>
color={flavor.is_legacy ? "warning" : "error"} {flavor.image_url ? ( <Box
sx={{
position: 'absolute',
top: 16,
right: 16,
fontWeight: '900',
borderRadius: 2,
boxShadow: '0 4px 12px rgba(0,0,0,0.1)'
}}
/>
)}
{flavor.image_url ? (
<Box
component="img" component="img"
src={flavor.image_url} src={flavor.image_url}
sx={{ sx={{