diff --git a/client/src/core/hooks/usePlayerHeader.ts b/client/src/core/hooks/usePlayerHeader.ts new file mode 100644 index 0000000..a604331 --- /dev/null +++ b/client/src/core/hooks/usePlayerHeader.ts @@ -0,0 +1,40 @@ +import { useEffect, useState } from "react"; +import { useNavigate, useLocation } from "react-router"; +import { useCurrentUser } from "./useCurrentUser"; + +export function usePlayerHeader() { + const navigate = useNavigate(); + const location = useLocation(); + const [menuOpen, setMenuOpen] = useState(false); + + const { user, loading, clearUser } = useCurrentUser(); + + // Close mobile menu on route change + useEffect(() => { + setMenuOpen(false); + }, [location.pathname]); + + function toggleMenu() { + setMenuOpen(v => !v); + } + + function closeMenu() { + setMenuOpen(false); + } + + function handleLogout() { + localStorage.clear(); + clearUser(); + navigate("/"); + } + + return { + menuOpen, + toggleMenu, + closeMenu, + handleLogout, + userName: user?.fullName || "Player", + balance: user?.balance ?? 0, + loading + }; +} diff --git a/client/src/index.css b/client/src/index.css index 1925e6d..246b16f 100644 --- a/client/src/index.css +++ b/client/src/index.css @@ -4,4 +4,25 @@ --color-jerneNavy: #2E4658; --color-lightBG: #F2F2F2; --color-greyBorder: #D9D9D9; -} \ No newline at end of file +} + +/* ===== LOGO RESPONSIVENESS ===== */ +.logo { + height: 72px; + width: auto; + object-fit: contain; +} + +/* Tablet */ +@media (max-width: 900px) { + .logo { + height: 48px; + } +} + +/* Mobile */ +@media (max-width: 600px) { + .logo { + height: 36px; + } +} diff --git a/client/src/ui/components/Logo.tsx b/client/src/ui/components/Logo.tsx index 8cfcf12..1b5c9e5 100644 --- a/client/src/ui/components/Logo.tsx +++ b/client/src/ui/components/Logo.tsx @@ -1,23 +1,19 @@ interface LogoProps { - size?: number; className?: string; alt?: string; } export const Logo = ({ - size = 48, className = "", - alt = "Jer ne IF Logo", + alt = "Jerne IF Logo", }: LogoProps) => { return ( {alt} ); }; diff --git a/client/src/ui/components/PlayerPageHeader.tsx b/client/src/ui/components/PlayerPageHeader.tsx index fb52323..45e4c21 100644 --- a/client/src/ui/components/PlayerPageHeader.tsx +++ b/client/src/ui/components/PlayerPageHeader.tsx @@ -1,118 +1,27 @@ -import React, { useEffect, useState } from "react"; -import { NavLink, useNavigate, useLocation } from "react-router"; -import "../css/PlayerPageHeader.css"; -import { User, LogOut, Menu, X } from "lucide-react"; -import { useCurrentUser } from "../../core/hooks/useCurrentUser"; - -const navItems = [ - { label: "Board", path: "/player/board" }, - { label: "History", path: "/player/history" }, - { label: "Results", path: "/player/results" }, - { label: "My Transactions", path: "/player/transactions" }, -]; +import React from "react"; +import { usePlayerHeader } from "../../core/hooks/usePlayerHeader"; +import { PlayerPageHeaderView } from "./PlayerPageHeaderView"; export const PlayerPageHeader: React.FC = () => { - const navigate = useNavigate(); - const location = useLocation(); - const [menuOpen, setMenuOpen] = useState(false); - - const { user, loading, clearUser } = useCurrentUser(); - - // close mobile menu on route change - useEffect(() => { - setMenuOpen(false); - }, [location.pathname]); - - function handleLogout() { - localStorage.clear(); - clearUser(); // clears global cache + subscribers - navigate("/"); - } - - const userName = user?.fullName || "Player"; - const balance = user?.balance ?? 0; + const { + menuOpen, + toggleMenu, + closeMenu, + handleLogout, + userName, + balance, + loading + } = usePlayerHeader(); return ( -
- {/* LEFT */} -
-
- Jerne IF -
- - -
- - {/* DESKTOP NAV */} - - - {/* USER */} -
- -
-
- {loading ? "…" : userName} -
-
- Balance: {loading ? "…" : balance} DKK -
-
-
- - - - {/* MOBILE MENU */} -
- {navItems.map(item => ( - - {item.label} - - ))} -
- - {menuOpen && ( -
setMenuOpen(false)} - /> - )} -
+ ); }; diff --git a/client/src/ui/components/PlayerPageHeaderView.tsx b/client/src/ui/components/PlayerPageHeaderView.tsx new file mode 100644 index 0000000..7dc3002 --- /dev/null +++ b/client/src/ui/components/PlayerPageHeaderView.tsx @@ -0,0 +1,120 @@ +import { NavLink } from "react-router"; +import { User, LogOut, Menu, X } from "lucide-react"; +import "../css/PlayerPageHeader.css"; +import {Logo} from "./Logo.tsx"; + +const navItems = [ + { label: "Board", path: "/player/board" }, + { label: "History", path: "/player/history" }, + { label: "Results", path: "/player/results" }, + { label: "My Transactions", path: "/player/transactions" }, +]; + +interface Props { + menuOpen: boolean; + loading: boolean; + userName: string; + balance: number; + onToggleMenu: () => void; + onCloseMenu: () => void; + onLogout: () => void; +} + +export const PlayerPageHeaderView = ({ + menuOpen, + loading, + userName, + balance, + onToggleMenu, + onCloseMenu, + onLogout + }: Props) => { + return ( +
+ {/* LEFT */} +
+ + + +
+ + {/* DESKTOP NAV */} + + + {/* USER */} +
+ +
+
+ {loading ? "…" : userName} +
+
+ Balance: {loading ? "…" : balance} DKK +
+
+
+ + + + {/* MOBILE MENU */} +
+ {navItems.map(item => ( + + {item.label} + + ))} + +
+ + {menuOpen && ( +
+ )} +
+ ); +}; diff --git a/client/src/ui/css/PlayerBoardPage.css b/client/src/ui/css/PlayerBoardPage.css index 6fb723a..361a7a4 100644 --- a/client/src/ui/css/PlayerBoardPage.css +++ b/client/src/ui/css/PlayerBoardPage.css @@ -51,6 +51,7 @@ width: 100%; max-width: none; box-sizing: border-box; + text-align: center; } /* grid */ @@ -90,11 +91,15 @@ /* fields tabs */ .player-board-fields-tabs { display: inline-flex; + gap: 8px; + padding: 4px; + margin: 0 auto 16px auto; border-radius: 999px; background: #e9e5de; - padding: 4px; - gap: 4px; - margin-bottom: 16px; + + max-width: 100%; + overflow-x: auto; + white-space: nowrap; } .player-board-fields-tab { @@ -225,8 +230,15 @@ } .player-board-bets-card h2 { - margin: 0 0 12px 0; font-size: 1.1rem; + margin-bottom: 12px; + text-align: left; +} + +.player-board-bets-card p { + font-size: 0.9rem; + margin-bottom: 12px; + text-align: left; } .player-board-bets-empty { @@ -383,7 +395,22 @@ font-weight: 600; } -@media (max-width: 900px) { +@media (max-width: 600px) { + .player-board-fields-text { + font-size: 0.7rem; + letter-spacing: 0.05em; + } + + .player-board-fields-tab { + flex: 0 0 auto; + padding: 4px 5px; + } + + .player-board-fields-tabs { + width: 100%; + justify-content: center; + } + .player-board-layout { flex-direction: column; gap: 16px; diff --git a/client/src/ui/css/PlayerHistoryPage.css b/client/src/ui/css/PlayerHistoryPage.css index 0b7de6c..4e6aec1 100644 --- a/client/src/ui/css/PlayerHistoryPage.css +++ b/client/src/ui/css/PlayerHistoryPage.css @@ -40,9 +40,12 @@ .history-table-wrapper { margin-top: 8px; border-radius: 8px; - overflow: hidden; border: 1px solid #ddd; background: #f9f6f2; + + max-height: 600px; + overflow-y: auto; + overflow-x: hidden; } .history-table { @@ -59,6 +62,10 @@ } .history-table th { + position: sticky; + top: 0; + z-index: 2; + text-align: left; background: #ebe6de; font-weight: 600; diff --git a/client/src/ui/css/PlayerMyTransactionPage.css b/client/src/ui/css/PlayerMyTransactionPage.css index df8c7d4..50b7ba1 100644 --- a/client/src/ui/css/PlayerMyTransactionPage.css +++ b/client/src/ui/css/PlayerMyTransactionPage.css @@ -45,6 +45,9 @@ /* cards */ .balance-card { + display: flex; + flex-direction: column; + max-height: 850px; background: #f9f6f2; border-radius: 12px; border: 1px solid #d4cec7; @@ -154,6 +157,7 @@ /* pending deposits list */ .balance-pending-card { margin-top: 4px; + max-height: 456px; } .balance-pending-list { @@ -163,6 +167,7 @@ display: flex; flex-direction: column; gap: 6px; + overflow-y: auto; } .balance-pending-item { @@ -186,10 +191,12 @@ } /* history table */ -.balance-table-wrapper { +.balance-table-scrollable-wrapper { + display: flex; + overflow-y: auto; + flex: 1; margin-top: 8px; border-radius: 8px; - overflow: hidden; border: 1px solid #ddd; background: #f9f6f2; } @@ -207,8 +214,11 @@ } .balance-table th { - text-align: left; + position: sticky; + top: 0; background: #ebe6de; + z-index: 1; + text-align: left; font-weight: 600; } @@ -252,4 +262,16 @@ .balance-layout { flex-direction: column; } + + .balance-card{ + max-height: 500px; + } + + .balance-pending-card { + max-height: 300px; + } + + .balance-table-scrollable-wrapper { + overflow-x: auto; + } } diff --git a/client/src/ui/css/PlayerPageHeader.css b/client/src/ui/css/PlayerPageHeader.css index 240da7e..71be77b 100644 --- a/client/src/ui/css/PlayerPageHeader.css +++ b/client/src/ui/css/PlayerPageHeader.css @@ -2,7 +2,8 @@ display: flex; align-items: center; justify-content: space-between; - padding: 20px clamp(16px, 5vw, 80px) 0 clamp(16px, 5vw, 80px); + padding: 20px clamp(16px, 5vw, 80px); + position: relative; font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; background-color: #2E4658; } @@ -65,7 +66,8 @@ align-items: center; gap: 0.75rem; - /* glassmorphism */ + color: #fff; + background: rgba(255, 255, 255, 0.12); border-radius: 999px; padding: 0.35rem 0.8rem; @@ -127,6 +129,10 @@ gap: 12px; } + .player-header_logout-btn { + display: none; + } + .player-header_nav { justify-content: flex-start; width: 100%; @@ -149,7 +155,7 @@ .player-header_burger { display: none; border: none; - background: rgba(255, 255, 255, 0.12); + background: transparent; border-radius: 10px; padding: 8px; cursor: pointer; @@ -163,9 +169,14 @@ /* backdrop */ .player-header_backdrop { - display: none; + display: block; + position: fixed; + inset: 0; + background: rgba(0, 0, 0, 0.25); + z-index: 90; /* 👈 below menu, above page */ } + /* ===== MOBILE ===== */ @media (max-width: 900px) { /* show burger */ @@ -186,18 +197,22 @@ top: 100%; left: 0; right: 0; + background: #2E4658; display: flex; flex-direction: column; padding: 12px 16px; gap: 8px; - transform: translateY(-10px); + + transform: translateY(-8px); opacity: 0; pointer-events: none; + transition: opacity 0.15s ease, transform 0.15s ease; - z-index: 50; + z-index: 100; /* 👈 higher than content */ } + .player-header_mobile-menu--open { transform: translateY(0); opacity: 1; @@ -219,32 +234,10 @@ background: rgba(255, 255, 255, 0.18); transform: translateY(-1px); } -} - - /* backdrop */ - .player-header_backdrop { - display: block; - position: fixed; - inset: 0; - background: rgba(0, 0, 0, 0.25); - z-index: 40; - } - - .player-header { - position: relative; - } - - .player-header_logo img { - height: 80px; - width: auto; - transition: height 0.2s ease; - } - @media (max-width: 900px) { - .player-header_logo img { - height: 40px; - } + .player-header_mobile-logout{ + display: flex; + justify-content: flex-start; + text-align: left; } - - - +} diff --git a/client/src/ui/layout/DefaultLayout.tsx b/client/src/ui/layout/DefaultLayout.tsx index 99e3b84..1a29256 100644 --- a/client/src/ui/layout/DefaultLayout.tsx +++ b/client/src/ui/layout/DefaultLayout.tsx @@ -16,7 +16,6 @@ export const DefaultLayout = ({ children }: { children: React.ReactNode }) => { " > {children} - + ); }; - diff --git a/client/src/ui/pages/player/PlayerMyTransactionPage.tsx b/client/src/ui/pages/player/PlayerMyTransactionPage.tsx index f0dc0d9..28621b9 100644 --- a/client/src/ui/pages/player/PlayerMyTransactionPage.tsx +++ b/client/src/ui/pages/player/PlayerMyTransactionPage.tsx @@ -270,7 +270,7 @@ export const PlayerMyTransactionPage: React.FC = () => { Balance history -
+