feat: add gender
This commit is contained in:
parent
b739246129
commit
4e2e0dd2a5
@ -1,6 +1,7 @@
|
|||||||
from datetime import datetime, timezone
|
from datetime import datetime, timezone
|
||||||
from sqlmodel import (
|
from sqlmodel import (
|
||||||
ARRAY,
|
ARRAY,
|
||||||
|
CHAR,
|
||||||
Column,
|
Column,
|
||||||
Integer,
|
Integer,
|
||||||
Relationship,
|
Relationship,
|
||||||
@ -48,6 +49,7 @@ class Player(SQLModel, table=True):
|
|||||||
display_name: str
|
display_name: str
|
||||||
email: str | None = None
|
email: str | None = None
|
||||||
full_name: str | None = None
|
full_name: str | None = None
|
||||||
|
gender: str | None = Field(default=None, sa_column=Column(CHAR(3)))
|
||||||
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
|
||||||
|
@ -21,8 +21,9 @@ player_router = APIRouter(prefix="/player", tags=["player"])
|
|||||||
class PlayerRequest(BaseModel):
|
class PlayerRequest(BaseModel):
|
||||||
display_name: str
|
display_name: str
|
||||||
username: str
|
username: str
|
||||||
|
gender: str | None
|
||||||
number: str
|
number: str
|
||||||
email: str
|
email: str | None
|
||||||
|
|
||||||
|
|
||||||
class AddPlayerRequest(PlayerRequest): ...
|
class AddPlayerRequest(PlayerRequest): ...
|
||||||
@ -54,6 +55,7 @@ def add_player(
|
|||||||
new_player = Player(
|
new_player = Player(
|
||||||
username=r.username,
|
username=r.username,
|
||||||
display_name=r.display_name,
|
display_name=r.display_name,
|
||||||
|
gender=r.gender if r.gender else None,
|
||||||
email=r.email if r.email else None,
|
email=r.email if r.email else None,
|
||||||
number=r.number,
|
number=r.number,
|
||||||
disabled=False,
|
disabled=False,
|
||||||
@ -80,9 +82,11 @@ def modify_player(
|
|||||||
.where(Team.id == request.team_id, P.id == r.id, P.username == r.username)
|
.where(Team.id == request.team_id, P.id == r.id, P.username == r.username)
|
||||||
).one_or_none()
|
).one_or_none()
|
||||||
if player:
|
if player:
|
||||||
|
print(r)
|
||||||
player.display_name = r.display_name.strip()
|
player.display_name = r.display_name.strip()
|
||||||
player.number = r.number.strip()
|
player.number = r.number.strip()
|
||||||
player.email = r.email.strip()
|
player.gender = r.gender.strip() if r.gender else None
|
||||||
|
player.email = r.email.strip() if r.email else None
|
||||||
session.add(player)
|
session.add(player)
|
||||||
session.commit()
|
session.commit()
|
||||||
return PlainTextResponse("modification successful")
|
return PlainTextResponse("modification successful")
|
||||||
@ -170,7 +174,14 @@ async def list_players(
|
|||||||
if players:
|
if players:
|
||||||
return [
|
return [
|
||||||
player.model_dump(
|
player.model_dump(
|
||||||
include={"id", "display_name", "username", "number", "email"}
|
include={
|
||||||
|
"id",
|
||||||
|
"display_name",
|
||||||
|
"username",
|
||||||
|
"gender",
|
||||||
|
"number",
|
||||||
|
"email",
|
||||||
|
}
|
||||||
)
|
)
|
||||||
for player in players
|
for player in players
|
||||||
if not player.disabled
|
if not player.disabled
|
||||||
|
60
src/App.css
60
src/App.css
@ -29,7 +29,6 @@ dialog {
|
|||||||
border-radius: 1em;
|
border-radius: 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*=========Network Controls=========*/
|
/*=========Network Controls=========*/
|
||||||
|
|
||||||
.infobutton {
|
.infobutton {
|
||||||
@ -61,7 +60,7 @@ dialog {
|
|||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
max-width: 240px;
|
max-width: 240px;
|
||||||
margin: 0px;
|
margin: 0px;
|
||||||
background-color: #F0F8FFdd;
|
background-color: #f0f8ffdd;
|
||||||
|
|
||||||
.slider,
|
.slider,
|
||||||
span {
|
span {
|
||||||
@ -103,8 +102,8 @@ dialog {
|
|||||||
bottom: 0;
|
bottom: 0;
|
||||||
background-color: #ccc;
|
background-color: #ccc;
|
||||||
border-radius: 34px;
|
border-radius: 34px;
|
||||||
-webkit-transition: .4s;
|
-webkit-transition: 0.4s;
|
||||||
transition: .4s;
|
transition: 0.4s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.slider:before {
|
.slider:before {
|
||||||
@ -116,19 +115,19 @@ dialog {
|
|||||||
bottom: 3px;
|
bottom: 3px;
|
||||||
background-color: white;
|
background-color: white;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
-webkit-transition: .4s;
|
-webkit-transition: 0.4s;
|
||||||
transition: .4s;
|
transition: 0.4s;
|
||||||
}
|
}
|
||||||
|
|
||||||
input:checked+.slider {
|
input:checked + .slider {
|
||||||
background-color: #2196F3;
|
background-color: #2196f3;
|
||||||
}
|
}
|
||||||
|
|
||||||
input:focus+.slider {
|
input:focus + .slider {
|
||||||
box-shadow: 0 0 1px #2196F3;
|
box-shadow: 0 0 1px #2196f3;
|
||||||
}
|
}
|
||||||
|
|
||||||
input:checked+.slider:before {
|
input:checked + .slider:before {
|
||||||
-webkit-transform: translateX(24px);
|
-webkit-transform: translateX(24px);
|
||||||
-ms-transform: translateX(24px);
|
-ms-transform: translateX(24px);
|
||||||
transform: translateX(24px);
|
transform: translateX(24px);
|
||||||
@ -138,8 +137,6 @@ input:checked+.slider:before {
|
|||||||
opacity: 66%;
|
opacity: 66%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
.hint {
|
.hint {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
font-size: 80%;
|
font-size: 80%;
|
||||||
@ -151,7 +148,8 @@ input:checked+.slider:before {
|
|||||||
z-index: -1;
|
z-index: -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
input {
|
input,
|
||||||
|
select {
|
||||||
padding: 0.2em 16px;
|
padding: 0.2em 16px;
|
||||||
margin-top: 0.25em;
|
margin-top: 0.25em;
|
||||||
margin-bottom: 0.25em;
|
margin-bottom: 0.25em;
|
||||||
@ -180,7 +178,6 @@ h3 {
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.container {
|
.container {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: nowrap;
|
flex-wrap: nowrap;
|
||||||
@ -281,10 +278,8 @@ button {
|
|||||||
font-size: 80%;
|
font-size: 80%;
|
||||||
margin: 0px;
|
margin: 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@media only screen and (max-width: 768px) {
|
@media only screen and (max-width: 768px) {
|
||||||
#control-panel {
|
#control-panel {
|
||||||
grid-template-columns: 1fr;
|
grid-template-columns: 1fr;
|
||||||
@ -308,7 +303,6 @@ button {
|
|||||||
font-size: xx-large;
|
font-size: xx-large;
|
||||||
margin-bottom: 16px;
|
margin-bottom: 16px;
|
||||||
margin-right: 16px;
|
margin-right: 16px;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.wavering {
|
.wavering {
|
||||||
@ -317,11 +311,13 @@ button {
|
|||||||
}
|
}
|
||||||
|
|
||||||
::backdrop {
|
::backdrop {
|
||||||
background-image: linear-gradient(45deg,
|
background-image: linear-gradient(
|
||||||
magenta,
|
45deg,
|
||||||
rebeccapurple,
|
magenta,
|
||||||
dodgerblue,
|
rebeccapurple,
|
||||||
green);
|
dodgerblue,
|
||||||
|
green
|
||||||
|
);
|
||||||
opacity: 0.75;
|
opacity: 0.75;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -488,7 +484,10 @@ button {
|
|||||||
|
|
||||||
input {
|
input {
|
||||||
max-width: 300px;
|
max-width: 300px;
|
||||||
margin: 0.2em auto;
|
}
|
||||||
|
select {
|
||||||
|
max-width: 335px;
|
||||||
|
background-color: white;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -511,6 +510,12 @@ button {
|
|||||||
&.disable-player {
|
&.disable-player {
|
||||||
background-color: #e338;
|
background-color: #e338;
|
||||||
}
|
}
|
||||||
|
&.mmp {
|
||||||
|
background-color: lightskyblue;
|
||||||
|
}
|
||||||
|
&.fmp {
|
||||||
|
background-color: salmon;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.new-player-inputs {
|
.new-player-inputs {
|
||||||
@ -533,21 +538,19 @@ button {
|
|||||||
margin: auto 1em;
|
margin: auto 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
input {
|
input,
|
||||||
|
select {
|
||||||
width: 90%;
|
width: 90%;
|
||||||
margin: 4px 0;
|
margin: 4px 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@keyframes blink {
|
@keyframes blink {
|
||||||
|
|
||||||
0% {
|
0% {
|
||||||
background-color: #8888;
|
background-color: #8888;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
13% {
|
13% {
|
||||||
background-color: #8888;
|
background-color: #8888;
|
||||||
}
|
}
|
||||||
@ -560,7 +563,6 @@ button {
|
|||||||
background-color: #8888;
|
background-color: #8888;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
38% {
|
38% {
|
||||||
background-color: #8888;
|
background-color: #8888;
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,10 @@ const UserInfo = (user: User, teams: TeamState | undefined) => {
|
|||||||
<b>display name: </b>
|
<b>display name: </b>
|
||||||
</div>
|
</div>
|
||||||
<div>{user?.display_name}</div>
|
<div>{user?.display_name}</div>
|
||||||
|
<div>
|
||||||
|
<b>gender: </b>
|
||||||
|
</div>
|
||||||
|
<div>{user?.gender?.toUpperCase() || "-"}</div>
|
||||||
<div>
|
<div>
|
||||||
<b>number: </b>
|
<b>number: </b>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { FormEvent, useEffect, useState } from "react";
|
import { FormEvent, useEffect, useState } from "react";
|
||||||
import { apiAuth, loadPlayers, User } from "./api";
|
import { apiAuth, Gender, loadPlayers, User } from "./api";
|
||||||
import { useSession } from "./Session";
|
import { useSession } from "./Session";
|
||||||
import { ErrorState } from "./types";
|
import { ErrorState } from "./types";
|
||||||
import { useNavigate } from "react-router";
|
import { useNavigate } from "react-router";
|
||||||
@ -15,6 +15,7 @@ const TeamPanel = () => {
|
|||||||
id: 0,
|
id: 0,
|
||||||
username: "",
|
username: "",
|
||||||
display_name: "",
|
display_name: "",
|
||||||
|
gender: undefined,
|
||||||
number: "",
|
number: "",
|
||||||
email: "",
|
email: "",
|
||||||
} as User;
|
} as User;
|
||||||
@ -95,7 +96,7 @@ const TeamPanel = () => {
|
|||||||
{players &&
|
{players &&
|
||||||
players.map((p) => (
|
players.map((p) => (
|
||||||
<button
|
<button
|
||||||
className="team-player"
|
className={"team-player " + p.gender}
|
||||||
key={p.id}
|
key={p.id}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setPlayer(p);
|
setPlayer(p);
|
||||||
@ -151,6 +152,22 @@ const TeamPanel = () => {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<div>
|
||||||
|
<label>gender</label>
|
||||||
|
<select
|
||||||
|
name="gender"
|
||||||
|
required
|
||||||
|
value={player.gender}
|
||||||
|
onChange={(e) => {
|
||||||
|
setPlayer({ ...player, gender: e.target.value as Gender });
|
||||||
|
setError({ ok: true, message: "" });
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<option value={undefined}></option>
|
||||||
|
<option value="fmp">FMP</option>
|
||||||
|
<option value="mmp">MMP</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label>number (optional)</label>
|
<label>number (optional)</label>
|
||||||
<input
|
<input
|
||||||
|
@ -43,12 +43,15 @@ export async function apiAuth(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type Gender = "fmp" | "mmp" | undefined;
|
||||||
|
|
||||||
export type User = {
|
export type User = {
|
||||||
id: number;
|
id: number;
|
||||||
username: string;
|
username: string;
|
||||||
display_name: string;
|
display_name: string;
|
||||||
email: string;
|
email: string;
|
||||||
number: string;
|
number: string;
|
||||||
|
gender: Gender;
|
||||||
scopes: string;
|
scopes: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user