appoint team manager in UI
This commit is contained in:
@@ -19,12 +19,24 @@ P = Player
|
||||
player_router = APIRouter(prefix="/player", tags=["player"])
|
||||
|
||||
|
||||
def update_team_manager(scopes_str: str, team_id: int, state: bool = True):
|
||||
scopes = set(scopes_str.split())
|
||||
if state:
|
||||
scopes.add(f"team:{team_id}")
|
||||
else:
|
||||
scopes.remove(f"team:{team_id}")
|
||||
|
||||
print("new scopestr", " ".join(scopes))
|
||||
return " ".join(sorted(scopes))
|
||||
|
||||
|
||||
class PlayerRequest(BaseModel):
|
||||
display_name: str
|
||||
username: str
|
||||
gender: str | None
|
||||
number: str
|
||||
email: str | None
|
||||
is_manager: bool | None
|
||||
|
||||
|
||||
class AddPlayerRequest(PlayerRequest): ...
|
||||
@@ -96,6 +108,10 @@ def modify_player(
|
||||
player.number = r.number.strip()
|
||||
player.gender = r.gender.strip() if r.gender else None
|
||||
player.email = r.email.strip() if r.email else None
|
||||
if r.is_manager is not None:
|
||||
player.scopes = update_team_manager(
|
||||
player.scopes, request.team_id, r.is_manager
|
||||
)
|
||||
session.add(player)
|
||||
session.commit()
|
||||
return PlainTextResponse("modification successful")
|
||||
@@ -212,6 +228,8 @@ async def list_players(
|
||||
] + demo_players
|
||||
|
||||
allowed_scopes = set(user.scopes.split())
|
||||
team_manager_scope = f"team:{team_id}"
|
||||
is_team_manager = team_manager_scope in allowed_scopes
|
||||
|
||||
with Session(engine) as session:
|
||||
current_user = session.exec(
|
||||
@@ -220,7 +238,7 @@ async def list_players(
|
||||
.join(Team)
|
||||
.where(Team.id == team_id, P.disabled == False, P.id == user.id)
|
||||
).one_or_none()
|
||||
if not current_user and f"team:{team_id}" not in allowed_scopes:
|
||||
if not current_user and not is_team_manager:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail="you're not in this team",
|
||||
@@ -233,21 +251,26 @@ async def list_players(
|
||||
.where(Team.id == team_id, P.disabled == False)
|
||||
.order_by(P.display_name)
|
||||
).all()
|
||||
|
||||
if players:
|
||||
return [
|
||||
player.model_dump(
|
||||
include={
|
||||
"id",
|
||||
"display_name",
|
||||
"username",
|
||||
"gender",
|
||||
"number",
|
||||
"email",
|
||||
}
|
||||
)
|
||||
for player in players
|
||||
if not player.disabled
|
||||
]
|
||||
players_dump = []
|
||||
for player in players:
|
||||
if not player.disabled:
|
||||
player_dump = player.model_dump(
|
||||
include={
|
||||
"id",
|
||||
"display_name",
|
||||
"username",
|
||||
"gender",
|
||||
"number",
|
||||
}
|
||||
)
|
||||
if is_team_manager:
|
||||
player_dump["email"] = player.email
|
||||
if team_manager_scope in player.scopes:
|
||||
player_dump["is_manager"] = True
|
||||
players_dump.append(player_dump)
|
||||
return players_dump
|
||||
|
||||
|
||||
def read_teams_me(user: Annotated[P, Depends(get_current_active_user)]):
|
||||
|
||||
@@ -4,6 +4,7 @@ import { useSession } from "./Session";
|
||||
import { ErrorState } from "./types";
|
||||
import { useNavigate } from "react-router";
|
||||
import Calendar from "./Calendar";
|
||||
import { Info, Star, StarHalf, StarOff, UserPen } from "lucide-react";
|
||||
import Loading from "./Loading";
|
||||
|
||||
const TeamPanel = () => {
|
||||
@@ -21,6 +22,7 @@ const TeamPanel = () => {
|
||||
gender: undefined,
|
||||
number: "",
|
||||
email: "",
|
||||
is_manager: false,
|
||||
} as User;
|
||||
const [error, setError] = useState<ErrorState>();
|
||||
const [player, setPlayer] = useState(newPlayerTemplate);
|
||||
@@ -96,7 +98,12 @@ const TeamPanel = () => {
|
||||
setError({ ok: true, message: "" });
|
||||
}}
|
||||
>
|
||||
{p.display_name}
|
||||
<span>{p.display_name}</span>
|
||||
{p.is_manager && (
|
||||
<span className="icon">
|
||||
<Star size={16} fill="gold" />
|
||||
</span>
|
||||
)}
|
||||
</button>
|
||||
))}
|
||||
<button
|
||||
@@ -197,7 +204,7 @@ const TeamPanel = () => {
|
||||
</div>
|
||||
<div className="field">
|
||||
<label className="label">
|
||||
email <small>(optional)</small>
|
||||
email <small>(optional, but helpful)</small>
|
||||
</label>
|
||||
<div className="control">
|
||||
<input
|
||||
@@ -210,18 +217,52 @@ const TeamPanel = () => {
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
{error?.message && (
|
||||
<p
|
||||
className={
|
||||
"help" + (error.ok ? " is-success" : " is-danger")
|
||||
}
|
||||
</div>
|
||||
<div className="field">
|
||||
<div className="control">
|
||||
<label className="label">
|
||||
manager{" "}
|
||||
<span className="tooltip">
|
||||
<Info size={16} />
|
||||
<span className="tooltiptext notification is-primary is-light has-text-centered">
|
||||
managers are able to see the analyses and modify
|
||||
the players
|
||||
</span>
|
||||
</span>
|
||||
</label>
|
||||
<button
|
||||
className={"button"}
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
setPlayer({
|
||||
...player,
|
||||
is_manager: !player.is_manager,
|
||||
});
|
||||
setError({ ok: true, message: "" });
|
||||
}}
|
||||
>
|
||||
{error.message}
|
||||
</p>
|
||||
)}
|
||||
<span className="icon">
|
||||
{player.is_manager ? (
|
||||
<Star fill="gold" />
|
||||
) : (
|
||||
<StarOff />
|
||||
)}
|
||||
</span>
|
||||
<span>{player.is_manager ? "yes" : "no"}</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="field is-grouped is-grouped-centered">
|
||||
{error?.message && (
|
||||
<p
|
||||
className={
|
||||
"help" + (error.ok ? " is-success" : " is-danger")
|
||||
}
|
||||
>
|
||||
{error.message}
|
||||
</p>
|
||||
)}
|
||||
<div className="field is-grouped is-grouped-multiline is-grouped-centered">
|
||||
<button
|
||||
className={
|
||||
"button is-light" +
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { JwtPayload } from "jwt-decode";
|
||||
import { useSession } from "./Session";
|
||||
|
||||
export const baseUrl = "";
|
||||
@@ -53,6 +54,7 @@ export type User = {
|
||||
number: string;
|
||||
gender: Gender;
|
||||
scopes: string;
|
||||
is_manager: boolean;
|
||||
};
|
||||
|
||||
export async function currentUser(): Promise<User> {
|
||||
@@ -124,3 +126,9 @@ export const logout = async () => {
|
||||
console.error(e);
|
||||
}
|
||||
};
|
||||
|
||||
export interface PassToken extends JwtPayload {
|
||||
username: string;
|
||||
name: string;
|
||||
team_id: number;
|
||||
}
|
||||
|
||||
@@ -20,3 +20,23 @@
|
||||
position: absolute;
|
||||
margin: 1rem;
|
||||
}
|
||||
|
||||
.tooltip {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* Tooltip text */
|
||||
.tooltiptext {
|
||||
visibility: hidden;
|
||||
width: 10rem;
|
||||
padding: 0.25rem;
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
/* Show the tooltip text on hover */
|
||||
.tooltip:hover .tooltiptext {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user