feat: begin to add support for multiple teams
This commit is contained in:
parent
c246a0b264
commit
ded2b79db7
2
db.py
2
db.py
@ -51,7 +51,7 @@ class Player(SQLModel, table=True):
|
|||||||
disabled: bool | None = None
|
disabled: bool | None = None
|
||||||
hashed_password: str | None = None
|
hashed_password: str | None = None
|
||||||
number: str | None = None
|
number: str | None = None
|
||||||
teams: list[Team] | None = Relationship(
|
teams: list[Team] = Relationship(
|
||||||
back_populates="players", link_model=PlayerTeamLink
|
back_populates="players", link_model=PlayerTeamLink
|
||||||
)
|
)
|
||||||
scopes: str = ""
|
scopes: str = ""
|
||||||
|
7
main.py
7
main.py
@ -59,10 +59,11 @@ def add_players(players: list[Player]):
|
|||||||
session.commit()
|
session.commit()
|
||||||
|
|
||||||
|
|
||||||
async def list_players():
|
async def list_players(team_id: int):
|
||||||
with Session(engine) as session:
|
with Session(engine) as session:
|
||||||
statement = select(Player).order_by(Player.display_name)
|
statement = select(Team).where(Team.id == team_id)
|
||||||
players = session.exec(statement).fetchall()
|
players = [t.players for t in session.exec(statement)][0]
|
||||||
|
if players:
|
||||||
return [
|
return [
|
||||||
player.model_dump(include={"id", "display_name", "number"})
|
player.model_dump(include={"id", "display_name", "number"})
|
||||||
for player in players
|
for player in players
|
||||||
|
16
src/App.css
16
src/App.css
@ -408,6 +408,10 @@ button {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.avatars {
|
||||||
|
margin: 16px auto;
|
||||||
|
}
|
||||||
|
|
||||||
.avatar {
|
.avatar {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
font-size: 110%;
|
font-size: 110%;
|
||||||
@ -415,8 +419,18 @@ button {
|
|||||||
width: fit-content;
|
width: fit-content;
|
||||||
border: 3px solid;
|
border: 3px solid;
|
||||||
border-radius: 1em;
|
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 {
|
.user-info {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { createRef, MouseEventHandler, useEffect, useState } from "react";
|
import { createRef, MouseEventHandler, useEffect, useState } from "react";
|
||||||
import { useSession } from "./Session";
|
import { TeamState, useSession } from "./Session";
|
||||||
import { User } from "./api";
|
import { User } from "./api";
|
||||||
import { useTheme } from "./ThemeProvider";
|
import { useTheme } from "./ThemeProvider";
|
||||||
import { colourTheme, darkTheme, normalTheme, rainbowTheme } from "./themes";
|
import { colourTheme, darkTheme, normalTheme, rainbowTheme } from "./themes";
|
||||||
@ -11,7 +11,7 @@ interface ContextMenuItem {
|
|||||||
onClick: () => void;
|
onClick: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const UserInfo = (user: User, teams: Team[] | undefined) => {
|
const UserInfo = (user: User, teams: TeamState | undefined) => {
|
||||||
return (
|
return (
|
||||||
<div className="user-info">
|
<div className="user-info">
|
||||||
<div>
|
<div>
|
||||||
@ -42,9 +42,9 @@ const UserInfo = (user: User, teams: Team[] | undefined) => {
|
|||||||
textAlign: "left",
|
textAlign: "left",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{teams.map((team) => (
|
{teams.teams.map((team, index) => (
|
||||||
<li>
|
<li>
|
||||||
{team.name} (
|
{teams.activeTeam === index ? <b>{team.name}</b> : team.name} (
|
||||||
{team.location || team.country || "location unknown"})
|
{team.location || team.country || "location unknown"})
|
||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
@ -56,7 +56,7 @@ const UserInfo = (user: User, teams: Team[] | undefined) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export default function Avatar() {
|
export default function Avatar() {
|
||||||
const { user, teams, onLogout } = useSession();
|
const { user, teams, setTeams, onLogout } = useSession();
|
||||||
const { theme, setTheme } = useTheme();
|
const { theme, setTheme } = useTheme();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const [contextMenu, setContextMenu] = useState<{
|
const [contextMenu, setContextMenu] = useState<{
|
||||||
@ -147,6 +147,8 @@ export default function Avatar() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<>
|
||||||
|
<div className="avatars">
|
||||||
<div
|
<div
|
||||||
className="avatar"
|
className="avatar"
|
||||||
onContextMenu={handleMenuClick}
|
onContextMenu={handleMenuClick}
|
||||||
@ -161,6 +163,24 @@ export default function Avatar() {
|
|||||||
ref={avatarRef}
|
ref={avatarRef}
|
||||||
>
|
>
|
||||||
👤 {user?.username}
|
👤 {user?.username}
|
||||||
|
</div>
|
||||||
|
{teams && teams?.teams.length > 1 && (
|
||||||
|
<select
|
||||||
|
className="group-avatar"
|
||||||
|
value={teams.activeTeam}
|
||||||
|
onChange={(e) =>
|
||||||
|
setTeams({ ...teams, activeTeam: Number(e.target.value) })
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{teams.teams.map((team) => (
|
||||||
|
<option key={team.id} value={team.id}>
|
||||||
|
👥 {team.name}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</select>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
{contextMenu.open && (
|
{contextMenu.open && (
|
||||||
<ul
|
<ul
|
||||||
className="context-menu"
|
className="context-menu"
|
||||||
@ -193,6 +213,6 @@ export default function Avatar() {
|
|||||||
>
|
>
|
||||||
{dialog}
|
{dialog}
|
||||||
</dialog>
|
</dialog>
|
||||||
</div>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -277,21 +277,27 @@ function HeaderControl({ onLoad, onClear }: HeaderControlProps) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default function Rankings() {
|
export default function Rankings() {
|
||||||
const { user } = useSession();
|
const { user, teams } = useSession();
|
||||||
const [players, setPlayers] = useState<User[] | null>(null);
|
const [players, setPlayers] = useState<User[] | null>(null);
|
||||||
|
|
||||||
async function loadPlayers() {
|
async function loadPlayers() {
|
||||||
|
if (teams) {
|
||||||
try {
|
try {
|
||||||
const data = await apiAuth("player/list", null, "GET");
|
const data = await apiAuth(
|
||||||
|
`player/list?team_id=${teams?.activeTeam}`,
|
||||||
|
null,
|
||||||
|
"GET"
|
||||||
|
);
|
||||||
setPlayers(data as User[]);
|
setPlayers(data as User[]);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
loadPlayers();
|
loadPlayers();
|
||||||
}, [user]);
|
}, [user, teams]);
|
||||||
|
|
||||||
const tabs = [
|
const tabs = [
|
||||||
{ id: "Chemistry", label: "🧪 Chemistry" },
|
{ id: "Chemistry", label: "🧪 Chemistry" },
|
||||||
@ -300,7 +306,7 @@ export default function Rankings() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{user && players ? (
|
{user && teams && players ? (
|
||||||
<TabController tabs={tabs}>
|
<TabController tabs={tabs}>
|
||||||
<ChemistryDnD {...{ user, players }} />
|
<ChemistryDnD {...{ user, players }} />
|
||||||
<MVPDnD {...{ user, players }} />
|
<MVPDnD {...{ user, players }} />
|
||||||
|
@ -14,15 +14,22 @@ export interface SessionProviderProps {
|
|||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface TeamState {
|
||||||
|
teams: Team[];
|
||||||
|
activeTeam: number;
|
||||||
|
}
|
||||||
|
|
||||||
export interface Session {
|
export interface Session {
|
||||||
user: User | null;
|
user: User | null;
|
||||||
teams: Team[] | null;
|
teams: TeamState | null;
|
||||||
|
setTeams: (teams: TeamState) => void;
|
||||||
onLogout: () => void;
|
onLogout: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const sessionContext = createContext<Session>({
|
const sessionContext = createContext<Session>({
|
||||||
user: null,
|
user: null,
|
||||||
teams: null,
|
teams: null,
|
||||||
|
setTeams: () => {},
|
||||||
onLogout: () => {},
|
onLogout: () => {},
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -30,7 +37,7 @@ export function SessionProvider(props: SessionProviderProps) {
|
|||||||
const { children } = props;
|
const { children } = props;
|
||||||
|
|
||||||
const [user, setUser] = useState<User | null>(null);
|
const [user, setUser] = useState<User | null>(null);
|
||||||
const [teams, setTeams] = useState<Team[] | null>(null);
|
const [teams, setTeams] = useState<TeamState | null>(null);
|
||||||
const [err, setErr] = useState<unknown>(null);
|
const [err, setErr] = useState<unknown>(null);
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
|
|
||||||
@ -50,12 +57,12 @@ export function SessionProvider(props: SessionProviderProps) {
|
|||||||
|
|
||||||
async function loadTeam() {
|
async function loadTeam() {
|
||||||
const teams: Team[] = await apiAuth("player/me/teams", null, "GET");
|
const teams: Team[] = await apiAuth("player/me/teams", null, "GET");
|
||||||
if (teams) setTeams(teams);
|
if (teams) setTeams({ teams: teams, activeTeam: teams[0].id });
|
||||||
}
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
loadUser();
|
loadUser();
|
||||||
setTimeout(() => loadTeam(), 1500);
|
setTimeout(() => loadTeam(), 500);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
function onLogin(user: User) {
|
function onLogin(user: User) {
|
||||||
@ -87,7 +94,7 @@ export function SessionProvider(props: SessionProviderProps) {
|
|||||||
content = <Login onLogin={onLogin} />;
|
content = <Login onLogin={onLogin} />;
|
||||||
} else
|
} else
|
||||||
content = (
|
content = (
|
||||||
<sessionContext.Provider value={{ user, teams, onLogout }}>
|
<sessionContext.Provider value={{ user, teams, setTeams, onLogout }}>
|
||||||
{children}
|
{children}
|
||||||
</sessionContext.Provider>
|
</sessionContext.Provider>
|
||||||
);
|
);
|
||||||
|
@ -34,6 +34,7 @@ export interface MVPRanking {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface Team {
|
export interface Team {
|
||||||
|
id: number;
|
||||||
name: string;
|
name: string;
|
||||||
location: string;
|
location: string;
|
||||||
country: string;
|
country: string;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user