mirror of
https://github.com/narrrl/holy-flavor-rater.git
synced 2026-11-03 16:41:27 +01:00
renamed dashboard to my collection, added limited status badges, and improved navigation discoverability
This commit is contained in:
1 parent
dc4b5e4512
commit
469ca3eb47
6 files changed
+126
-85
No files matched your search
@@ -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
@@ -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",
|
||||||
|
|||||||
@@ -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 }}>
|
||||||
|
|||||||
@@ -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={{
|
||||||
|
|||||||
@@ -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={{
|
||||||
|
|||||||
Reference in new issue
Block a user