From 3e8e71fc02452149539ee3397ec0c826a627e22c Mon Sep 17 00:00:00 2001 From: Nils Pukropp Date: Mon, 16 Feb 2026 15:26:22 +0100 Subject: [PATCH] updated ui for user specific content --- backend/api/views.py | 3 +- frontend/src/App.tsx | 124 ++++++++++---------- frontend/src/pages/Dashboard.tsx | 189 ++++++++++++++++++++----------- frontend/src/pages/Settings.tsx | 48 +++++++- 4 files changed, 236 insertions(+), 128 deletions(-) diff --git a/backend/api/views.py b/backend/api/views.py index 23e1809..fe7b865 100644 --- a/backend/api/views.py +++ b/backend/api/views.py @@ -153,7 +153,8 @@ class UserViewSet(viewsets.ModelViewSet): return Response({ 'username': user.username, 'theme': user.theme, - 'ratings': RatingSerializer(ratings, many=True).data + 'avatar': request.build_absolute_uri(user.avatar.url) if user.avatar else None, + 'ratings': RatingSerializer(ratings, many=True, context={'request': request}).data }) @action(detail=False, methods=['get']) diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 2e9dada..dfb55ae 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -248,60 +248,69 @@ const App: React.FC = () => { - - {!isMobile && !loadingUser && ( - - {navItems.map((item) => ( - - ))} - {user && ( - - )} - - )} - - setAnchorEl(e.currentTarget)} sx={{ ml: 1 }}> - - {user?.avatar ? ( - - - - ) : ( - - - - )} - - - - - setAnchorEl(null)} - sx={{ maxHeight: 500 }} - > - Light Themes - handleThemeChange('latte')}>Latte - handleThemeChange('pink')}>Pastel Pink - handleThemeChange('solarized_light')}>Solarized Light - handleThemeChange('one_light')}>One Light - handleThemeChange('paper')}>Paper White - - - - Dark Themes - handleThemeChange('mocha')}>Mocha (Default) - handleThemeChange('frappe')}>Frappé - handleThemeChange('macchiato')}>Macchiato - handleThemeChange('atom')}>Atom One Dark - handleThemeChange('dracula')}>Dracula - handleThemeChange('gruvbox')}>Gruvbox Dark - handleThemeChange('nord')}>Nord - handleThemeChange('cyberpunk')}>Cyberpunk - handleThemeChange('forest')}>Forest Dark - - + + + {!isMobile && !loadingUser && ( + + + + + + + + )} + + + + {!loadingUser && ( + + user ? ( + + <> + + setAnchorEl(e.currentTarget)} sx={{ ml: 1 }}> + + + + {!user.avatar && user.username.charAt(0).toUpperCase()} + + + + + + setAnchorEl(null)} + + > + + setAnchorEl(null)}>Dashboard + + setAnchorEl(null)}>Settings + + + + Logout + + + + + + ) : ( + + + + ) + + )} + + + + @@ -312,9 +321,10 @@ const App: React.FC = () => { } /> } /> } /> - } /> - } /> - } /> + } /> + } /> + } /> + diff --git a/frontend/src/pages/Dashboard.tsx b/frontend/src/pages/Dashboard.tsx index 1c96d2e..6fa2160 100644 --- a/frontend/src/pages/Dashboard.tsx +++ b/frontend/src/pages/Dashboard.tsx @@ -12,10 +12,33 @@ interface DashboardData { my_ratings: any[]; } +import { + Typography, + Card, + CardContent, + Box, + Rating as MuiRating, + Button, + TextField, + Container, + CircularProgress, + Avatar, + Tabs, + Tab, + Accordion, + AccordionSummary, + AccordionDetails, + Divider +} from '@mui/material'; +import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; +import ContentCopyIcon from '@mui/icons-material/ContentCopy'; +import { Link } from 'react-router-dom'; +// ... const Dashboard: React.FC = () => { useTitle('Dashboard'); const [data, setData] = useState(null); const [loading, setLoading] = useState(true); + const [activeTab, setActiveTab] = useState(0); const shareUrl = data ? `${window.location.origin}/profile/${data.user.username}` : ''; @@ -41,6 +64,18 @@ const Dashboard: React.FC = () => { if (loading) return ; if (!data) return Please login to view dashboard.; + // Helper to group by category + const groupBy = (array: any[], key: string) => { + return array.reduce((result, currentValue) => { + const groupKey = currentValue.flavor?.category_name || currentValue.category_name; + (result[groupKey] = result[groupKey] || []).push(currentValue); + return result; + }, {}); + }; + + const ratedGrouped = groupBy(data.my_ratings, 'category_name'); + const missingGrouped = groupBy(data.missing_flavors, 'category_name'); + return ( @@ -48,7 +83,7 @@ const Dashboard: React.FC = () => { {!data.user.avatar && data.user.username.charAt(0).toUpperCase()} - Personal Dashboard + Dashboard { sx={{ width: { xs: '100%', sm: 300 } }} /> - - - My Ratings ({data.rated_count}) - - {data.my_ratings.map((rating: any) => ( - - - {rating.flavor_image && ( - - )} - - {rating.flavor_name} - - - {rating.score}/10 - - {rating.comment && ( - - "{rating.comment}" - - )} - - - - ))} - - - - Missing Flavors ({data.missing_count}) - - {data.missing_flavors.map((flavor: any) => ( - - - {flavor.image_url && ( - - )} - - {flavor.name} - {flavor.category_name} - {flavor.description.substring(0, 100)}... - {flavor.shop_url && ( - - )} - - - - ))} - - + + setActiveTab(v)}> + + + + + {activeTab === 0 ? ( + + {Object.keys(ratedGrouped).length === 0 ? ( + You haven't rated any flavors yet. + ) : ( + Object.entries(ratedGrouped).map(([category, ratings]: [string, any]) => ( + + {category} + + {ratings.map((rating: any) => ( + + + {rating.flavor_image && ( + + )} + + + + {rating.flavor_name} + + + + + {rating.score}/10 + + {rating.comment && ( + + "{rating.comment}" + + )} + + + + ))} + + + )) + )} + + ) : ( + + {Object.keys(missingGrouped).length === 0 ? ( + Amazing! You've rated everything! + ) : ( + Object.entries(missingGrouped).map(([category, flavors]: [string, any]) => ( + + {category} + + {flavors.map((flavor: any) => ( + + + {flavor.image_url && ( + + )} + + + + {flavor.name} + + + + {flavor.description.substring(0, 60)}... + + {flavor.shop_url && ( + + )} + + + + ))} + + + )) + )} + + )} ); }; diff --git a/frontend/src/pages/Settings.tsx b/frontend/src/pages/Settings.tsx index fae5553..fa980b6 100644 --- a/frontend/src/pages/Settings.tsx +++ b/frontend/src/pages/Settings.tsx @@ -7,12 +7,24 @@ import { Paper, Alert, Container, - Avatar + Avatar, + FormControl, + InputLabel, + Select, + MenuItem, + ListSubheader, + Divider } from '@mui/material'; import api from '../api'; import { useTitle } from '../hooks/useTitle'; +import { CatppuccinTheme } from '../theme'; -const Settings: React.FC = () => { +interface SettingsProps { + themeName: CatppuccinTheme; + onThemeChange: (newTheme: CatppuccinTheme) => void; +} + +const Settings: React.FC = ({ themeName, onThemeChange }) => { useTitle('Account Settings'); const [username, setUsername] = useState(''); const [email, setEmail] = useState(''); @@ -113,6 +125,38 @@ const Settings: React.FC = () => { )} + + Appearance + + Theme + + + + Profile Picture