From ded2b79db714655ef7fd42a7c9e62e89fa237624 Mon Sep 17 00:00:00 2001 From: julius Date: Wed, 19 Mar 2025 15:08:18 +0100 Subject: [PATCH] feat: begin to add support for multiple teams --- db.py | 2 +- main.py | 15 ++++++------ src/App.css | 16 ++++++++++++- src/Avatar.tsx | 60 ++++++++++++++++++++++++++++++++---------------- src/Rankings.tsx | 22 +++++++++++------- src/Session.tsx | 17 ++++++++++---- src/types.ts | 1 + 7 files changed, 91 insertions(+), 42 deletions(-) diff --git a/db.py b/db.py index b6e9f57..3807943 100644 --- a/db.py +++ b/db.py @@ -51,7 +51,7 @@ class Player(SQLModel, table=True): disabled: bool | None = None hashed_password: str | None = None number: str | None = None - teams: list[Team] | None = Relationship( + teams: list[Team] = Relationship( back_populates="players", link_model=PlayerTeamLink ) scopes: str = "" diff --git a/main.py b/main.py index 7ce4c5a..a3a8dce 100644 --- a/main.py +++ b/main.py @@ -59,14 +59,15 @@ def add_players(players: list[Player]): session.commit() -async def list_players(): +async def list_players(team_id: int): with Session(engine) as session: - statement = select(Player).order_by(Player.display_name) - players = session.exec(statement).fetchall() - return [ - player.model_dump(include={"id", "display_name", "number"}) - for player in players - ] + statement = select(Team).where(Team.id == team_id) + players = [t.players for t in session.exec(statement)][0] + if players: + return [ + player.model_dump(include={"id", "display_name", "number"}) + for player in players + ] async def read_teams_me(user: Annotated[Player, Depends(get_current_active_user)]): diff --git a/src/App.css b/src/App.css index 435b940..e906d71 100644 --- a/src/App.css +++ b/src/App.css @@ -408,6 +408,10 @@ button { } } +.avatars { + margin: 16px auto; +} + .avatar { font-weight: bold; font-size: 110%; @@ -415,8 +419,18 @@ button { width: fit-content; border: 3px solid; border-radius: 1em; - margin: 0 auto 16px auto; + margin: 4px auto; +} +.group-avatar { + background-color: aliceblue; + font-weight: bold; + font-size: 90%; + padding: 3px 1em; + width: fit-content; + border: 3px solid; + border-radius: 1em; + margin: 4px auto; } .user-info { diff --git a/src/Avatar.tsx b/src/Avatar.tsx index 58d9488..a1fb2b9 100644 --- a/src/Avatar.tsx +++ b/src/Avatar.tsx @@ -1,5 +1,5 @@ import { createRef, MouseEventHandler, useEffect, useState } from "react"; -import { useSession } from "./Session"; +import { TeamState, useSession } from "./Session"; import { User } from "./api"; import { useTheme } from "./ThemeProvider"; import { colourTheme, darkTheme, normalTheme, rainbowTheme } from "./themes"; @@ -11,7 +11,7 @@ interface ContextMenuItem { onClick: () => void; } -const UserInfo = (user: User, teams: Team[] | undefined) => { +const UserInfo = (user: User, teams: TeamState | undefined) => { return (
@@ -42,9 +42,9 @@ const UserInfo = (user: User, teams: Team[] | undefined) => { textAlign: "left", }} > - {teams.map((team) => ( + {teams.teams.map((team, index) => (
  • - {team.name} ( + {teams.activeTeam === index ? {team.name} : team.name} ( {team.location || team.country || "location unknown"})
  • ))} @@ -56,7 +56,7 @@ const UserInfo = (user: User, teams: Team[] | undefined) => { }; export default function Avatar() { - const { user, teams, onLogout } = useSession(); + const { user, teams, setTeams, onLogout } = useSession(); const { theme, setTheme } = useTheme(); const navigate = useNavigate(); const [contextMenu, setContextMenu] = useState<{ @@ -147,20 +147,40 @@ export default function Avatar() { } return ( -
    { - if (contextMenu.open && event.target === avatarRef.current) { - handleMenuClose(); - } else { - handleMenuClick(event); - } - }} - ref={avatarRef} - > - ๐Ÿ‘ค {user?.username} + <> +
    +
    { + if (contextMenu.open && event.target === avatarRef.current) { + handleMenuClose(); + } else { + handleMenuClick(event); + } + }} + ref={avatarRef} + > + ๐Ÿ‘ค {user?.username} +
    + {teams && teams?.teams.length > 1 && ( + + )} +
    + {contextMenu.open && (
      {dialog} -
    + ); } diff --git a/src/Rankings.tsx b/src/Rankings.tsx index 51a2bc9..88b1872 100644 --- a/src/Rankings.tsx +++ b/src/Rankings.tsx @@ -277,21 +277,27 @@ function HeaderControl({ onLoad, onClear }: HeaderControlProps) { } export default function Rankings() { - const { user } = useSession(); + const { user, teams } = useSession(); const [players, setPlayers] = useState(null); async function loadPlayers() { - try { - const data = await apiAuth("player/list", null, "GET"); - setPlayers(data as User[]); - } catch (error) { - console.error(error); + if (teams) { + try { + const data = await apiAuth( + `player/list?team_id=${teams?.activeTeam}`, + null, + "GET" + ); + setPlayers(data as User[]); + } catch (error) { + console.error(error); + } } } useEffect(() => { loadPlayers(); - }, [user]); + }, [user, teams]); const tabs = [ { id: "Chemistry", label: "๐Ÿงช Chemistry" }, @@ -300,7 +306,7 @@ export default function Rankings() { return ( <> - {user && players ? ( + {user && teams && players ? ( diff --git a/src/Session.tsx b/src/Session.tsx index fd0f0b4..7b14c1c 100644 --- a/src/Session.tsx +++ b/src/Session.tsx @@ -14,15 +14,22 @@ export interface SessionProviderProps { children: ReactNode; } +export interface TeamState { + teams: Team[]; + activeTeam: number; +} + export interface Session { user: User | null; - teams: Team[] | null; + teams: TeamState | null; + setTeams: (teams: TeamState) => void; onLogout: () => void; } const sessionContext = createContext({ user: null, teams: null, + setTeams: () => {}, onLogout: () => {}, }); @@ -30,7 +37,7 @@ export function SessionProvider(props: SessionProviderProps) { const { children } = props; const [user, setUser] = useState(null); - const [teams, setTeams] = useState(null); + const [teams, setTeams] = useState(null); const [err, setErr] = useState(null); const [loading, setLoading] = useState(false); @@ -50,12 +57,12 @@ export function SessionProvider(props: SessionProviderProps) { async function loadTeam() { const teams: Team[] = await apiAuth("player/me/teams", null, "GET"); - if (teams) setTeams(teams); + if (teams) setTeams({ teams: teams, activeTeam: teams[0].id }); } useEffect(() => { loadUser(); - setTimeout(() => loadTeam(), 1500); + setTimeout(() => loadTeam(), 500); }, []); function onLogin(user: User) { @@ -87,7 +94,7 @@ export function SessionProvider(props: SessionProviderProps) { content = ; } else content = ( - + {children} ); diff --git a/src/types.ts b/src/types.ts index 5633dd4..e3151e8 100644 --- a/src/types.ts +++ b/src/types.ts @@ -34,6 +34,7 @@ export interface MVPRanking { } export interface Team { + id: number; name: string; location: string; country: string;