Compare commits

...

2 Commits

Author SHA1 Message Date
62ba89c599
feat: require gender when registering 2025-05-23 22:09:49 +02:00
05bdc5c44c
feat: support mixed teams in MVP ranking 2025-05-23 22:01:08 +02:00
6 changed files with 61 additions and 22 deletions

View File

@ -38,6 +38,7 @@ class Team(SQLModel, table=True):
name: str
location: str | None
country: str | None
mixed: bool = False
players: list["Player"] | None = Relationship(
back_populates="teams", link_model=PlayerTeamLink
)

View File

@ -515,12 +515,6 @@ button {
&.disable-player {
background-color: #e338;
}
&.mmp {
background-color: lightskyblue;
}
&.fmp {
background-color: salmon;
}
}
.new-player-inputs {
@ -551,6 +545,14 @@ button {
}
}
.mmp {
background-color: lightskyblue;
}
.fmp {
background-color: salmon;
}
@keyframes blink {
0% {
background-color: #8888;

View File

@ -7,9 +7,11 @@ import TabController from "./TabController";
type PlayerListProps = Partial<ReactSortableProps<any>> & {
orderedList?: boolean;
gender?: boolean;
};
function PlayerList(props: PlayerListProps) {
const fmps = props.list?.filter((item) => item.gender === "fmp").length;
return (
<ReactSortable
{...props}
@ -17,13 +19,23 @@ function PlayerList(props: PlayerListProps) {
swapThreshold={0.2}
style={{ minHeight: props.list && props.list?.length < 1 ? 64 : 32 }}
>
{props.list?.map((item, index) => (
<div key={item.id} className="item">
{props.orderedList
? index + 1 + ". " + item.display_name
: item.display_name}
</div>
))}
{props.list &&
props.list.map((item, index) => (
<div
key={item.id}
className={"item " + (props.gender ? item.gender : "")}
>
{props.orderedList
? props.gender
? index +
1 -
(item.gender !== "fmp" ? fmps! : 0) +
". " +
item.display_name
: index + 1 + ". " + item.display_name
: item.display_name}
</div>
))}
</ReactSortable>
);
}
@ -325,8 +337,11 @@ function MVPDnD({ user, teams, players }: PlayerInfoProps) {
const [availablePlayers, setAvailablePlayers] = useState<User[]>(players);
const [rankedPlayers, setRankedPlayers] = useState<User[]>([]);
const [loading, setLoading] = useState(false);
const [mixed, setMixed] = useState(false);
useEffect(() => {
const activeTeam = teams.teams.find((team) => team.id == teams.activeTeam);
activeTeam && setMixed(activeTeam.mixed);
handleGet();
}, [players]);
@ -382,11 +397,9 @@ function MVPDnD({ user, teams, players }: PlayerInfoProps) {
setList={setAvailablePlayers}
group={{
name: "mvp-shared",
pull: function (to) {
return to.el.classList.contains("putclone") ? "clone" : true;
},
}}
className="dragbox"
gender={mixed}
/>
</div>
<div className="box two">
@ -399,15 +412,23 @@ function MVPDnD({ user, teams, players }: PlayerInfoProps) {
)}
<PlayerList
list={rankedPlayers}
setList={setRankedPlayers}
setList={(newList) =>
mixed
? setRankedPlayers(
newList.sort((a, b) =>
a.gender && b.gender
? a.gender.localeCompare(b.gender)
: -1
)
)
: setRankedPlayers(newList)
}
group={{
name: "mvp-shared",
pull: function (to) {
return to.el.classList.contains("putclone") ? "clone" : true;
},
}}
className="dragbox"
orderedList
gender={mixed}
/>
</div>
</div>

View File

@ -1,6 +1,6 @@
import { jwtDecode, JwtPayload } from "jwt-decode";
import { ReactNode, useEffect, useState } from "react";
import { apiAuth, baseUrl, User } from "./api";
import { apiAuth, baseUrl, Gender, User } from "./api";
import { useNavigate } from "react-router";
import { Eye, EyeSlash } from "./Icons";
import { useSession } from "./Session";
@ -237,6 +237,21 @@ export const SetPassword = () => {
}}
/>
</div>
<div>
<label>gender</label>
<select
name="gender"
required
value={player.gender}
onChange={(e) => {
setPlayer({ ...player, gender: e.target.value as Gender });
}}
>
<option value={undefined}></option>
<option value="fmp">FMP</option>
<option value="mmp">MMP</option>
</select>
</div>
<div>
<label>number (optional)</label>
<input

View File

@ -155,7 +155,6 @@ const TeamPanel = () => {
<label>gender</label>
<select
name="gender"
required
value={player.gender}
onChange={(e) => {
setPlayer({ ...player, gender: e.target.value as Gender });

View File

@ -46,6 +46,7 @@ export interface Team {
name: string;
location: string;
country: string;
mixed: boolean;
}
export type ErrorState = {