Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Picklists save to the database now #171

Merged
merged 1 commit into from
May 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
169 changes: 113 additions & 56 deletions components/stats/Picklist.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import { Report } from "@/lib/Types";
import { DbPicklist, Report } from "@/lib/Types";

import { useDrag, useDrop } from "react-dnd";
import { ChangeEvent, useEffect, useState } from "react";
import { FaArrowDown, FaArrowUp, FaPlus } from "react-icons/fa";
import { getServerSideProps } from '../../pages/[teamSlug]/[seasonSlug]/[competitonSlug]/stats';
import ClientAPI from "@/lib/client/ClientAPI";

type CardData = {
number: number;
id: number;
picklist?: number;
picklistIndex?: number;
};

type Picklist = {
Expand All @@ -19,8 +20,8 @@ type Picklist = {

const Includes = (bucket: any[], item: CardData) => {
let result = false;
bucket.forEach((i: { id: number }) => {
if (i.id === item.id) {
bucket.forEach((i: { number: number }) => {
if (i.number === item.number) {
result = true;
}
});
Expand All @@ -29,17 +30,17 @@ const Includes = (bucket: any[], item: CardData) => {
};

function removeTeamFromPicklist(team: CardData, picklists: Picklist[]) {
if (team.picklist === undefined) return;
if (team.picklistIndex === undefined) return;

const picklist = picklists[team.picklist];
const picklist = picklists[team.picklistIndex];
if (!picklist) return;

picklist.teams = picklist.teams.filter((team) => team.id !== team.id);
picklist.teams = picklist.teams.filter((t) => t.number !== team.number);
picklist.update(picklist);
}

function TeamCard(props: { cardData: CardData, draggable: boolean, picklist?: Picklist, rank?: number, lastRank?: number, width?: string, height?: string}) {
const { number: teamNumber, id, picklist } = props.cardData;
const { number: teamNumber, picklistIndex: picklist } = props.cardData;

const [{ isDragging }, dragRef] = useDrag({
type: "team",
Expand Down Expand Up @@ -89,12 +90,12 @@ function PicklistCard(props: { picklist: Picklist, picklists: Picklist[] }) {
const [{ isOver }, dropRef] = useDrop({
accept: "team",
drop: (item: CardData) => {
if (item.picklist === picklist.index) return;
if (item.picklistIndex === picklist.index) return;

removeTeamFromPicklist(item, props.picklists);

if (!Includes(picklist.teams, item)) {
item.picklist = picklist.index;
item.picklistIndex = picklist.index;
picklist.teams.push(item);
picklist.update(picklist);
}
Expand All @@ -119,7 +120,7 @@ function PicklistCard(props: { picklist: Picklist, picklists: Picklist[] }) {
<TeamCard
cardData={team}
draggable={false}
key={team.id}
key={team.number}
picklist={picklist}
rank={index}
lastRank={picklist.teams.length - 1}
Expand All @@ -132,7 +133,7 @@ function PicklistCard(props: { picklist: Picklist, picklists: Picklist[] }) {
);
}

export function TeamList(props: { teams: CardData[], picklists: Picklist[] }) {
export function TeamList(props: { teams: CardData[], picklists: Picklist[], expectedTeamCount: number }) {
const [{ isOver}, dropRef] = useDrop({
accept: "team",
drop: (item: CardData) => {
Expand All @@ -145,70 +146,126 @@ export function TeamList(props: { teams: CardData[], picklists: Picklist[] }) {

return (
<div ref={dropRef} className="w-full h-fit flex flex-row bg-base-300 space-x-2 p-2 overflow-x-scroll">
{props.teams.length > 0 ? props.teams.map((team) => (
<TeamCard
draggable={true}
cardData={team}
key={team.id}
></TeamCard>
)) :
<progress className="progress w-full"></progress>}
{
props.teams.map((team) => (
<TeamCard
draggable={true}
cardData={team}
key={team.number}
></TeamCard>
))
}
{ props.teams.length !== props.expectedTeamCount &&
<div className="loading loading-spinner" />
}
</div>);
}

export default function PicklistScreen(props: { teams: number[], reports: Report[] }) {
const api = new ClientAPI("gearboxiscool");

export default function PicklistScreen(props: { teams: number[], reports: Report[], expectedTeamCount: number, picklistId: string }) {
const [picklists, setPicklists] = useState<Picklist[]>([]);
const [loadingPicklists, setLoadingPicklists] = useState(false);

const teams = props.teams.map((team) => ({ number: team, id: team }));
const teams = props.teams.map((team) => ({ number: team }));

function savePicklists(picklists: Picklist[]) {
const picklistDict = picklists.reduce<DbPicklist>((acc, picklist) => {
acc.picklists[picklist.name] = picklist.teams.map((team) => team.number);
return acc;
}, {
_id: props.picklistId,
picklists: {}
});

api.updatePicklist(picklistDict);
}

function updatePicklist(picklist: Picklist) {
setPicklists((old) => {
const newPicklists = old.map((p) => {
if (p.index === picklist.index) {
return picklist;
} else {
return p;
}
});

savePicklists(newPicklists);
return newPicklists;
});
}

useEffect(() => {
if (picklists.length > 0 || loadingPicklists) return;

setLoadingPicklists(true);
api.getPicklist(props.picklistId).then((picklistDict) => {
setPicklists(Object.entries(picklistDict.picklists).map((picklist, index) => {
const newPicklist: Picklist = {
index,
name: picklist[0],
teams: picklist[1].map((team: number) => ({ number: team })),
update: updatePicklist
};

for (const team of newPicklist.teams) {
team.picklistIndex = newPicklist.index;
}

return newPicklist;
}));

setLoadingPicklists(false);
});
});

const addPicklist = () => {
const newPicklist: Picklist = {
index: picklists.length,
name: `Picklist ${picklists.length + 1}`,
teams: [],
update: (picklist: Picklist) => {
setPicklists((old) => {
const newPicklists = old.map((p) => {
if (p.index === picklist.index) {
return picklist;
} else {
return p;
}
});

return newPicklists;
});
},
update: updatePicklist
};

setPicklists([...picklists, newPicklist]);
const newPicklists = [...picklists, newPicklist];
savePicklists(newPicklists);
setPicklists(newPicklists);
};

return (
<div className="w-full h-fit flex flex-col space-y-2">
<TeamList teams={teams} picklists={picklists}></TeamList>
<TeamList teams={teams} picklists={picklists} expectedTeamCount={props.expectedTeamCount}></TeamList>

<div className="w-full h-[30rem] px-4 py-2 flex flex-row space-x-3">
{picklists.length === 0
? (
<div className="w-full h-full flex items-center justify-center">
<h1 className="text-3xl text-accent animate-bounce font-semibold">
Create A Picklist
</h1>
</div>
)
: picklists.map((picklist) => (
<PicklistCard key={picklist.index} picklist={picklist} picklists={picklists}></PicklistCard>
{
loadingPicklists
? <div className="w-full h-full flex items-center justify-center">
<div className="loading loading-spinner" />
</div>
: picklists.length === 0
? (
<div className="w-full h-full flex items-center justify-center">
<h1 className="text-3xl text-accent animate-bounce font-semibold">
Create A Picklist
</h1>
</div>
)
: picklists.map((picklist) => (
<PicklistCard key={picklist.index} picklist={picklist} picklists={picklists}></PicklistCard>
)
)
)}
}
</div>

<button
className="btn btn-circle btn-lg btn-primary absolute right-10 bottom-[21rem] animate-pulse font-bold "
onClick={addPicklist}
>
<FaPlus></FaPlus>
</button>
{ !loadingPicklists &&
<button
className="btn btn-circle btn-lg btn-primary absolute right-10 bottom-[21rem] animate-pulse font-bold "
onClick={addPicklist}
>
<FaPlus></FaPlus>
</button>
}
</div>
);
}
}
18 changes: 18 additions & 0 deletions lib/API.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
User,
Report,
Pitreport,
DbPicklist,
} from "./Types";
import { GenerateSlug, removeDuplicates } from "./Utils";
import { ObjectId } from "mongodb";
Expand Down Expand Up @@ -429,6 +430,11 @@ export namespace API {

const pitReports = await generatePitReports(tba, db, data.tbaId);

const picklist = await db.addObject<DbPicklist>(Collections.Picklists, {
_id: new ObjectId(),
picklists: {},
});

var comp = await db.addObject<Competition>(
Collections.Competitions,
new Competition(
Expand All @@ -439,6 +445,7 @@ export namespace API {
data.end,
pitReports,
matches.map((match) => String(match._id)),
picklist._id.toString()
)
);

Expand Down Expand Up @@ -837,6 +844,17 @@ export namespace API {
await Promise.all(promises);

return res.status(200).send({ scouters, matches, reports });
},

getPicklist: async (req, res, { db, data }) => {
const picklist = await db.findObjectById<DbPicklist>(Collections.Picklists, new ObjectId(data.id));
return res.status(200).send(picklist);
},

updatePicklist: async (req, res, { db, data }) => {
const { _id, ...picklist } = data.picklist;
await db.updateObjectById<DbPicklist>(Collections.Picklists, new ObjectId(data.picklist._id), picklist);
return res.status(200).send({ result: "success" });
}
};
}
1 change: 1 addition & 0 deletions lib/MongoDB.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ export enum Collections {
Sessions = "sessions",
Forms = "Forms",
Pitreports = "Pitreports",
Picklists = "Picklists",
}

export async function GetDatabase(): Promise<MongoDBInterface> {
Expand Down
13 changes: 12 additions & 1 deletion lib/Types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -235,14 +235,17 @@ export class Competition {
pitReports: string[];
matches: string[];

picklist: string;

constructor(
name: string,
slug: string | undefined,
tbaId: string | undefined,
start: number,
end: number,
pitReports: string[] = [],
matches: string[] = []
matches: string[] = [],
picklist: string = ""
) {
this.name = name;
this.slug = slug;
Expand All @@ -251,6 +254,7 @@ export class Competition {
this.end = end;
this.pitReports = pitReports;
this.matches = matches;
this.picklist = picklist;
}
}

Expand Down Expand Up @@ -349,3 +353,10 @@ export interface EventData {
firstRanking: TheBlueAlliance.SimpleRank[];
oprRanking: TheBlueAlliance.OprRanking;
}

export type DbPicklist = {
_id: string;
picklists: {
[name: string]: number[];
};
}
9 changes: 9 additions & 0 deletions lib/client/ClientAPI.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
FormData,
EventData,
Pitreport,
DbPicklist,
} from "../Types";

export enum ClientRequestMethod {
Expand Down Expand Up @@ -392,4 +393,12 @@ export default class ClientAPI {
return await this.request("/findScouterManagementData", { compId, scouterIds });
}

async getPicklist(id: string): Promise<DbPicklist> {
return await this.request("/getPicklist", { id });
}

async updatePicklist(picklist: DbPicklist) {
return await this.request("/updatePicklist", { picklist });
}

}
3 changes: 2 additions & 1 deletion pages/[teamSlug]/[seasonSlug]/[competitonSlug]/stats.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,8 @@ export default function Stats(props: StatsPageProps) {
) : (
<></>
)}
{page === 1 ? <PicklistScreen teams={Array.from(teams)} reports={reports}></PicklistScreen> : <></>}
{page === 1 ? <PicklistScreen
teams={Array.from(teams)} reports={reports} expectedTeamCount={props.competition.pitReports.length} picklistId={props.competition.picklist}></PicklistScreen> : <></>}
</Container>
);
}
Expand Down
Loading