feat: loads of improvements (see comments)
1) check whether submitting user is submitting for himself 2) some refactoring of the tabs in `Ranking` 3) get chemistry and mvps from DB 4) restore previous 5) start over 6) (hopefully) improve logout
This commit is contained in:
@@ -235,15 +235,17 @@ h3 {
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
button,
|
||||
.button {
|
||||
button {
|
||||
margin: 4px;
|
||||
font-weight: bold;
|
||||
font-size: large;
|
||||
color: aliceblue;
|
||||
background-color: black;
|
||||
border-radius: 1.2em;
|
||||
z-index: 1;
|
||||
|
||||
&:hover {
|
||||
opacity: 75%;
|
||||
}
|
||||
}
|
||||
|
||||
#control-panel {
|
||||
|
@@ -1,7 +1,8 @@
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import { ButtonHTMLAttributes, useEffect, useRef, useState } from "react";
|
||||
import { ReactSortable, ReactSortableProps } from "react-sortablejs";
|
||||
import { apiAuth, User } from "./api";
|
||||
import { useSession } from "./Session";
|
||||
import { Chemistry, MVPRanking } from "./types";
|
||||
|
||||
type PlayerListProps = Partial<ReactSortableProps<any>> & {
|
||||
orderedList?: boolean;
|
||||
@@ -21,15 +22,37 @@ function PlayerList(props: PlayerListProps) {
|
||||
);
|
||||
}
|
||||
|
||||
const LoadButton = (props: ButtonHTMLAttributes<HTMLButtonElement>) => {
|
||||
return (
|
||||
<button {...props} style={{ padding: "4px 16px" }}>
|
||||
🗃️ restore previous
|
||||
</button>
|
||||
);
|
||||
};
|
||||
|
||||
const ClearButton = (props: ButtonHTMLAttributes<HTMLButtonElement>) => {
|
||||
return (
|
||||
<button {...props} style={{ padding: "4px 16px" }}>
|
||||
🗑️ start over
|
||||
</button>
|
||||
);
|
||||
};
|
||||
|
||||
function filterSort(list: User[], ids: number[]): User[] {
|
||||
const objectMap = new Map(list.map((obj) => [obj.id, obj]));
|
||||
const filteredAndSortedObjects = ids
|
||||
.map((id) => objectMap.get(id))
|
||||
.filter((obj) => obj !== undefined);
|
||||
return filteredAndSortedObjects;
|
||||
}
|
||||
|
||||
interface PlayerInfoProps {
|
||||
user: User;
|
||||
players: User[];
|
||||
}
|
||||
|
||||
export function Chemistry({ user, players }: PlayerInfoProps) {
|
||||
const index = players.indexOf(user);
|
||||
var otherPlayers = players.slice();
|
||||
otherPlayers.splice(index, 1);
|
||||
function ChemistryDnD({ user, players }: PlayerInfoProps) {
|
||||
var otherPlayers = players.filter((player) => player.id !== user.id);
|
||||
const [playersLeft, setPlayersLeft] = useState<User[]>([]);
|
||||
const [playersMiddle, setPlayersMiddle] = useState<User[]>(otherPlayers);
|
||||
const [playersRight, setPlayersRight] = useState<User[]>([]);
|
||||
@@ -48,12 +71,27 @@ export function Chemistry({ user, players }: PlayerInfoProps) {
|
||||
let middle = playersMiddle.map(({ id }) => id);
|
||||
let right = playersRight.map(({ id }) => id);
|
||||
const data = { user: user.id, hate: left, undecided: middle, love: right };
|
||||
const response = await apiAuth("chemistry", data, "POST");
|
||||
response ? setDialog(response) : setDialog("try sending again");
|
||||
const response = await apiAuth("chemistry", data, "PUT");
|
||||
setDialog(response || "try sending again");
|
||||
}
|
||||
|
||||
async function handleGet() {
|
||||
const chemistry = (await apiAuth("chemistry", null, "GET")) as Chemistry;
|
||||
setPlayersLeft(filterSort(otherPlayers, chemistry.hate));
|
||||
setPlayersMiddle(filterSort(otherPlayers, chemistry.undecided));
|
||||
setPlayersRight(filterSort(otherPlayers, chemistry.love));
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<HeaderControl
|
||||
onLoad={handleGet}
|
||||
onClear={() => {
|
||||
setPlayersRight([]);
|
||||
setPlayersMiddle(otherPlayers);
|
||||
setPlayersLeft([]);
|
||||
}}
|
||||
/>
|
||||
<div className="container">
|
||||
<div className="box three">
|
||||
<h2>😬</h2>
|
||||
@@ -111,7 +149,7 @@ export function Chemistry({ user, players }: PlayerInfoProps) {
|
||||
);
|
||||
}
|
||||
|
||||
export function MVP({ user, players }: PlayerInfoProps) {
|
||||
function MVPDnD({ user, players }: PlayerInfoProps) {
|
||||
const [availablePlayers, setAvailablePlayers] = useState<User[]>(players);
|
||||
const [rankedPlayers, setRankedPlayers] = useState<User[]>([]);
|
||||
|
||||
@@ -127,12 +165,25 @@ export function MVP({ user, players }: PlayerInfoProps) {
|
||||
setDialog("sending...");
|
||||
let mvps = rankedPlayers.map(({ id }) => id);
|
||||
const data = { user: user.id, mvps: mvps };
|
||||
const response = await apiAuth("mvps", data, "POST");
|
||||
const response = await apiAuth("mvps", data, "PUT");
|
||||
response ? setDialog(response) : setDialog("try sending again");
|
||||
}
|
||||
|
||||
async function handleGet() {
|
||||
const mvps = (await apiAuth("mvps", null, "GET")) as MVPRanking;
|
||||
setRankedPlayers(filterSort(players, mvps.mvps));
|
||||
setAvailablePlayers(players.filter((user) => !mvps.mvps.includes(user.id)));
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<HeaderControl
|
||||
onLoad={handleGet}
|
||||
onClear={() => {
|
||||
setAvailablePlayers(players);
|
||||
setRankedPlayers([]);
|
||||
}}
|
||||
/>
|
||||
<div className="container">
|
||||
<div className="box two">
|
||||
<h2>🥏🏃</h2>
|
||||
@@ -190,6 +241,27 @@ export function MVP({ user, players }: PlayerInfoProps) {
|
||||
);
|
||||
}
|
||||
|
||||
interface HeaderControlProps {
|
||||
onLoad: () => void;
|
||||
onClear: () => void;
|
||||
}
|
||||
function HeaderControl({ onLoad, onClear }: HeaderControlProps) {
|
||||
return (
|
||||
<>
|
||||
<div>
|
||||
<LoadButton onClick={onLoad} />
|
||||
<ClearButton onClick={onClear} />
|
||||
</div>
|
||||
<div>
|
||||
<span className="grey">
|
||||
assign as many or as few players as you want and don't forget to{" "}
|
||||
<b>submit</b> 💾 when you're done :)
|
||||
</span>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default function Rankings() {
|
||||
const { user } = useSession();
|
||||
const [players, setPlayers] = useState<User[] | null>(null);
|
||||
@@ -230,18 +302,13 @@ export default function Rankings() {
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
<span className="grey">
|
||||
assign as many or as few players as you want
|
||||
<br />
|
||||
and don't forget to <b>submit</b> 💾 when you're done :)
|
||||
</span>
|
||||
{tabs.map((tab) => {
|
||||
if (openTab !== tab.id) return null;
|
||||
switch (tab.id) {
|
||||
case "Chemistry":
|
||||
return <Chemistry key={tab.id} {...{ user, players }} />;
|
||||
return <ChemistryDnD key={tab.id} {...{ user, players }} />;
|
||||
case "MVP":
|
||||
return <MVP key={tab.id} {...{ user, players }} />;
|
||||
return <MVPDnD key={tab.id} {...{ user, players }} />;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
@@ -1,3 +1,5 @@
|
||||
import { useSession } from "./Session";
|
||||
|
||||
export const baseUrl = import.meta.env.VITE_BASE_URL as string;
|
||||
|
||||
export async function apiAuth(
|
||||
@@ -22,7 +24,8 @@ export async function apiAuth(
|
||||
|
||||
if (!resp.ok) {
|
||||
if (resp.status === 401) {
|
||||
logout();
|
||||
const { onLogout } = useSession();
|
||||
onLogout();
|
||||
throw new Error("Unauthorized");
|
||||
}
|
||||
}
|
||||
|
14
src/types.ts
14
src/types.ts
@@ -18,3 +18,17 @@ export interface PlayerRanking {
|
||||
std: number;
|
||||
n: number;
|
||||
}
|
||||
|
||||
export interface Chemistry {
|
||||
id: number;
|
||||
user: number;
|
||||
hate: number[];
|
||||
undecided: number[];
|
||||
love: number[];
|
||||
}
|
||||
|
||||
export interface MVPRanking {
|
||||
id: number;
|
||||
user: number;
|
||||
mvps: number[];
|
||||
}
|
||||
|
Reference in New Issue
Block a user