diff --git a/cutt/player.py b/cutt/player.py index ce91d8c..86f45bb 100644 --- a/cutt/player.py +++ b/cutt/player.py @@ -18,13 +18,16 @@ P = Player player_router = APIRouter(prefix="/player", tags=["player"]) -class AddPlayerRequest(BaseModel): +class PlayerRequest(BaseModel): display_name: str username: str number: str email: str +class AddPlayerRequest(PlayerRequest): ... + + def add_player( r: AddPlayerRequest, request: Annotated[TeamScopedRequest, Depends(verify_team_scope)], @@ -57,6 +60,63 @@ def add_player( ) session.add(new_player) session.commit() + return PlainTextResponse(f"added {new_player.display_name}") + + +class ModifyPlayerRequest(PlayerRequest): + id: int + + +def modify_player( + r: ModifyPlayerRequest, + request: Annotated[TeamScopedRequest, Depends(verify_team_scope)], +): + with Session(engine) as session: + player = session.exec( + select(P) + .join(PlayerTeamLink) + .join(Team) + .where(Team.id == request.team_id, P.id == r.id, P.username == r.username) + ).one_or_none() + if player: + player.display_name = r.display_name.strip() + player.number = r.number.strip() + player.email = r.email.strip() + session.add(player) + session.commit() + return PlainTextResponse("modification successful") + else: + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, + detail="no such player found in your team", + ) + + +class DisablePlayerRequest(BaseModel): + player_id: int + + +def disable_player( + r: DisablePlayerRequest, + request: Annotated[TeamScopedRequest, Depends(verify_team_scope)], +): + with Session(engine) as session: + player = session.exec( + select(P) + .join(PlayerTeamLink) + .join(Team) + .where(Team.id == request.team_id, P.id == r.player_id) + ).one_or_none() + if player: + player.disabled = True + session.add(player) + session.commit() + return PlainTextResponse(f"disabled {player.display_name}") + else: + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, + detail="no such player found in your team", + ) def add_player_to_team(player_id: int, team_id: int): @@ -86,11 +146,17 @@ async def list_all_players(): async def list_players(team_id: int): with Session(engine) as session: - statement = select(Team).where(Team.id == team_id) - players = [t.players for t in session.exec(statement)][0] + players = session.exec( + select(P) + .join(PlayerTeamLink) + .join(Team) + .where(Team.id == team_id, P.disabled == False) + ).all() if players: return [ - player.model_dump(include={"id", "display_name", "username", "number"}) + player.model_dump( + include={"id", "display_name", "username", "number", "email"} + ) for player in players if not player.disabled ] @@ -102,12 +168,22 @@ def read_teams_me(user: Annotated[P, Depends(get_current_active_user)]): player_router.add_api_route( - "/add/{team_id}", + "/{team_id}", endpoint=add_player, methods=["POST"], ) player_router.add_api_route( - "/list/{team_id}", + "/{team_id}", + endpoint=modify_player, + methods=["PUT"], +) +player_router.add_api_route( + "/{team_id}", + endpoint=disable_player, + methods=["DELETE"], +) +player_router.add_api_route( + "/{team_id}/list", endpoint=list_players, methods=["GET"], dependencies=[Depends(get_current_active_user)], @@ -119,7 +195,7 @@ player_router.add_api_route( dependencies=[Security(get_current_active_user, scopes=["admin"])], ) player_router.add_api_route( - "/add/{player_id}/{team_id}", + "/add/{team_id}/{player_id}", endpoint=add_player_to_team, methods=["GET"], dependencies=[Security(get_current_active_user, scopes=["admin"])], diff --git a/src/App.css b/src/App.css index f744301..852af31 100644 --- a/src/App.css +++ b/src/App.css @@ -507,9 +507,15 @@ button { &.new-player { background-color: #3838; } + + &.disable-player { + background-color: #e338; + } } .new-player-inputs { + display: flex; + flex-direction: column; margin: auto; div { diff --git a/src/TeamPanel.tsx b/src/TeamPanel.tsx index 8074e79..b4c1b5c 100644 --- a/src/TeamPanel.tsx +++ b/src/TeamPanel.tsx @@ -1,6 +1,7 @@ import { FormEvent, useEffect, useState } from "react"; import { apiAuth, loadPlayers, User } from "./api"; import { useSession } from "./Session"; +import { ErrorState } from "./types"; export const TeamPanel = () => { const { teams } = useSession(); @@ -11,12 +12,13 @@ export const TeamPanel = () => { number: "", email: "", } as User; - const [error, setError] = useState(""); + const [error, setError] = useState(); const [players, setPlayers] = useState(null); const [player, setPlayer] = useState(newPlayerTemplate); useEffect(() => { if (teams) { + setError({ ok: true, message: "" }); loadPlayers(teams.activeTeam).then((data) => setPlayers(data)); } }, [teams]); @@ -24,16 +26,45 @@ export const TeamPanel = () => { async function handleSubmit(e: FormEvent) { e.preventDefault(); if (teams) { - const r = await apiAuth( - `player/add/${teams?.activeTeam}`, - player, - "POST" - ); - if (r.detail) setError(r.detail); + if (player.id === 0) { + const r = await apiAuth(`player/${teams?.activeTeam}`, player, "POST"); + if (r.detail) setError({ ok: false, message: r.detail }); + else { + setError({ ok: true, message: r }); + loadPlayers(teams.activeTeam).then((data) => setPlayers(data)); + } + } else { + const r = await apiAuth(`player/${teams?.activeTeam}`, player, "PUT"); + if (r.detail) setError({ ok: false, message: r.detail }); + else { + setError({ ok: true, message: r }); + loadPlayers(teams.activeTeam).then((data) => setPlayers(data)); + } + } } } - if (teams) { + async function handleDisable(e: FormEvent) { + e.preventDefault(); + if (teams && player.id !== 0) { + var confirmation = confirm("are you sure?"); + if (confirmation) { + const r = await apiAuth( + `player/${teams?.activeTeam}`, + { player_id: player.id }, + "DELETE" + ); + if (r.detail) setError({ ok: false, message: r.detail }); + else { + setError({ ok: true, message: r }); + setPlayer(newPlayerTemplate); + loadPlayers(teams.activeTeam).then((data) => setPlayers(data)); + } + } + } + } + + if (teams && players) { const activeTeam = teams.teams.filter( (team) => team.id == teams?.activeTeam )[0]; @@ -60,7 +91,10 @@ export const TeamPanel = () => { @@ -68,7 +102,10 @@ export const TeamPanel = () => { @@ -85,13 +122,14 @@ export const TeamPanel = () => { type="text" required value={player.display_name} - onChange={(e) => + onChange={(e) => { setPlayer({ ...player, display_name: e.target.value, username: e.target.value.toLowerCase().replace(/\W/g, ""), - }) - } + }); + setError({ ok: true, message: "" }); + }} />
@@ -99,30 +137,62 @@ export const TeamPanel = () => { - setPlayer({ ...player, username: e.target.value }) - } + onChange={(e) => { + setPlayer({ ...player, username: e.target.value }); + setError({ ok: true, message: "" }); + }} />
- + - setPlayer({ ...player, number: e.target.value }) - } + value={player.number || ""} + onChange={(e) => { + setPlayer({ ...player, number: e.target.value }); + setError({ ok: true, message: "" }); + }} />
- {error && ( - {error} + + { + setPlayer({ ...player, email: e.target.value }); + setError({ ok: true, message: "" }); + }} + /> +
+
+ {error?.message && ( + + {error.message} + )}
- +
+ +
+ {player.id !== 0 && ( +
+ +
+ )} diff --git a/src/api.ts b/src/api.ts index 6649c9c..c5709a3 100644 --- a/src/api.ts +++ b/src/api.ts @@ -78,7 +78,7 @@ export async function currentUser(): Promise { export async function loadPlayers(teamId: number) { try { - const data = await apiAuth(`player/list/${teamId}`, null, "GET"); + const data = await apiAuth(`player/${teamId}/list`, null, "GET"); return data as User[]; } catch (error) { console.error(error); diff --git a/src/types.ts b/src/types.ts index e3151e8..cd46efa 100644 --- a/src/types.ts +++ b/src/types.ts @@ -39,3 +39,8 @@ export interface Team { location: string; country: string; } + +export type ErrorState = { + ok: boolean; + message: string; +};