From eef5862ad850f1d9bb367c75b9874ec4bcdebec2 Mon Sep 17 00:00:00 2001 From: Matthew Slight Date: Fri, 20 Jun 2025 11:48:18 +0400 Subject: [PATCH] feat: add player profile page --- src/components/Sidebar.jsx | 30 ++-- .../components/ActionBar/BottomSheet.tsx | 1 - src/pages/profile/index.tsx | 151 ++++++++++++++++++ src/routes/index.tsx | 9 ++ 4 files changed, 175 insertions(+), 16 deletions(-) create mode 100644 src/pages/profile/index.tsx diff --git a/src/components/Sidebar.jsx b/src/components/Sidebar.jsx index 496ca6c..63b3f17 100644 --- a/src/components/Sidebar.jsx +++ b/src/components/Sidebar.jsx @@ -78,21 +78,21 @@ export default function Sidebar({ isSidebarOpen, closeSidebar }) { ), }, - // { - // to: '/profile', - // label: 'Profile', - // Icon: ({ className }) => ( - // - // ), - // }, + { + to: '/profile', + label: 'Profile', + Icon: ({ className }) => ( + + ), + }, ]; // const endgamesNav = { diff --git a/src/pages/drills/components/ActionBar/BottomSheet.tsx b/src/pages/drills/components/ActionBar/BottomSheet.tsx index 29aeaea..9972121 100644 --- a/src/pages/drills/components/ActionBar/BottomSheet.tsx +++ b/src/pages/drills/components/ActionBar/BottomSheet.tsx @@ -1,4 +1,3 @@ -import React from 'react'; import { AnimatePresence, motion } from 'framer-motion'; import { Archive, Clipboard, ExternalLink, X } from 'lucide-react'; diff --git a/src/pages/profile/index.tsx b/src/pages/profile/index.tsx new file mode 100644 index 0000000..4331546 --- /dev/null +++ b/src/pages/profile/index.tsx @@ -0,0 +1,151 @@ +import { useMemo } from 'react'; +import { formatDistanceToNow } from 'date-fns'; + +import { useChessComRatings } from '@/hooks/useChessComRatings'; +import { useProfile } from '@/hooks/useProfile'; +import { useRecentGames } from '@/pages/games/hooks/useRecentGames'; +import WinRateDial from '@/pages/insights/components/WinRateDial'; + +export default function PlayerProfile() { + const { profile } = useProfile(); + const { rating, timeClass } = useChessComRatings(); + const { games } = useRecentGames(profile.username, 5); + + const form = useMemo(() => { + if (!games.length) return ''; + return games + .slice(0, 5) + .map((g) => { + const white = + g.meta.players.white.player.username.toLowerCase() === + profile.username.toLowerCase(); + const result = g.meta.pgnTags?.Result; + const won = result === (white ? '1-0' : '0-1'); + const lost = result === (white ? '0-1' : '1-0'); + return won ? 'W' : lost ? 'L' : 'D'; + }) + .join(' '); + }, [games, profile.username]); + + const strengths = [ + 'Tactical awareness', + 'Aggressive attacker', + 'Strong with open positions', + ]; + const weaknesses = ['Endgame technique', 'Time management', 'Closed games']; + const bestOpenings = [ + { name: 'Sicilian Defense', win: 68 }, + { name: 'Ruy Lopez', win: 65 }, + { name: 'Italian Game', win: 62 }, + ]; + const worstOpenings = [ + { name: 'French Defense', win: 32 }, + { name: 'Caro-Kann', win: 35 }, + { name: 'Pirc Defense', win: 40 }, + ]; + + const countryCode = profile.country?.split('/').pop()?.toUpperCase() || ''; + const flagEmoji = countryCode + ? countryCode + .split('') + .map((c) => String.fromCodePoint(c.charCodeAt(0) + 127397)) + .join('') + : ''; + + return ( +
+
+
+ {profile.avatar ? ( + {`${profile.username} + ) : ( +
+ )} +
+

+ {profile.name || profile.username}{' '} + {flagEmoji && {flagEmoji}} +

+

@{profile.username}

+ {profile.last_online && ( +

+ Last online{' '} + {formatDistanceToNow(new Date(profile.last_online * 1000), { + addSuffix: true, + })} +

+ )} +
+
+ +
+
+ +
+
+

Current Form

+

+ {form || 'N/A'} +

+
+
+ +
+
+

Strengths

+
    + {strengths.map((s) => ( +
  • {s}
  • + ))} +
+
+
+

Weaknesses

+
    + {weaknesses.map((w) => ( +
  • {w}
  • + ))} +
+
+
+ +
+
+

+ Best Openings +

+
    + {bestOpenings.map((o) => ( +
  • + {o.name} + {o.win}% +
  • + ))} +
+
+
+

+ Worst Openings +

+
    + {worstOpenings.map((o) => ( +
  • + {o.name} + {o.win}% +
  • + ))} +
+
+
+
+
+ ); +} diff --git a/src/routes/index.tsx b/src/routes/index.tsx index 15781bd..aaebe43 100644 --- a/src/routes/index.tsx +++ b/src/routes/index.tsx @@ -14,6 +14,7 @@ import Help from '@/pages/help'; import PreSignupHome from '@/pages/home/PreSignupHome'; import Insights from '@/pages/insights'; import NotFound from '@/pages/NotFound'; +import PlayerProfile from '@/pages/profile'; import ReportPage from '@/pages/report'; import Settings from '@/pages/settings'; @@ -101,6 +102,14 @@ export default function AppRoutes() { } /> + + + + } + />