Skip to content

Commit

Permalink
Pick grid to battle with, using search criteria
Browse files Browse the repository at this point in the history
  • Loading branch information
JustAman62 committed Aug 28, 2024
1 parent 3995566 commit 7f6f456
Show file tree
Hide file tree
Showing 5 changed files with 92 additions and 30 deletions.
4 changes: 2 additions & 2 deletions server/GridBattle.Api/Api/GridApi.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,8 @@ [FromServices] GridDbContext dbContext
(source == null || x.Source == source)
&& (
search == null
|| x.Name.Contains(search, StringComparison.OrdinalIgnoreCase)
|| x.Id.Contains(search, StringComparison.OrdinalIgnoreCase)
|| EF.Functions.ILike(x.Name, $"%{search}%")
|| EF.Functions.ILike(x.Id, $"%{search}%")
)
)
.OrderByDescending(x => x.CreatedDateTime)
Expand Down
9 changes: 8 additions & 1 deletion ui/src/Home.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Link, useNavigate } from "react-router-dom";
import ListGrids from "./grid/ListGrids";
import { LuPlus, LuSwords } from "react-icons/lu";
import { BiShuffle } from "react-icons/bi";
import SearchGrids from "./grid/SearchGrids";

export default function Home() {
const navigate = useNavigate();
Expand All @@ -19,7 +20,7 @@ export default function Home() {
};

return (
<div className="flex flex-col grow">
<div className="flex flex-col grow mb-8">
<div className="flex justify-center p-2">
<div className="flex flex-col grow max-w-screen-sm gap-4 px-4 pt-4 text-2xl">
<NavButton to="/battle" color="red">
Expand Down Expand Up @@ -48,6 +49,12 @@ export default function Home() {
<ListGrids pageSize={5} source="NYT" onGridChosen={(gridId) => navigate(`/grid/${gridId}`)} />
</div>
</div>
<div className="flex grow justify-center p-2">
<div className="grow max-w-screen-md">
<h1 className="font-semibold text-2xl text-center my-2">Find a Grid</h1>
<SearchGrids pageSize={5} onGridChosen={(gridId) => navigate(`/grid/${gridId}`)} />
</div>
</div>
</div>
);
}
14 changes: 8 additions & 6 deletions ui/src/grid/ListGrids.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ export default function ListGrids({ pageSize, source, query, onGridChosen }: Pro
.then((res) => {
setLoading(false);
if (res.status > 200) {
setResults([]);
throw Error("Unexpected error when fetching grids");
}
return res.json();
Expand All @@ -37,6 +38,7 @@ export default function ListGrids({ pageSize, source, query, onGridChosen }: Pro

return (
<ListGroup>
{!loading && results.length === 0 && <ListGroup.Item>No Grids Found</ListGroup.Item>}
{results.map((grid, i) => {
return (
<ListGroup.Item key={i} onClick={() => onGridChosen(grid.id)}>
Expand All @@ -53,23 +55,23 @@ export default function ListGrids({ pageSize, source, query, onGridChosen }: Pro
</ListGroup.Item>
);
})}
<div className="flex flex-row justify-between p-4">
<div className="flex flex-row justify-between items-center p-4">
{offset > 0 ? (
<Button outline pill size="sm" onClick={() => setOffset((x) => x - pageSize)}>
<Button className="w-10" outline pill size="sm" onClick={() => setOffset((x) => x - pageSize)}>
<BiCaretLeft />
</Button>
) : (
<div></div>
<div className="w-10">&nbsp;</div>
)}

{loading && <Spinner size="lg" />}
{loading ? <Spinner size="lg" /> : <span>Page {offset / pageSize + 1}</span>}

{results.length === pageSize ? (
<Button outline pill size="sm" onClick={() => setOffset((x) => x + pageSize)}>
<Button className="w-10" outline pill size="sm" onClick={() => setOffset((x) => x + pageSize)}>
<BiCaretRight />
</Button>
) : (
<div></div>
<div className="w-10">&nbsp;</div>
)}
</div>
</ListGroup>
Expand Down
29 changes: 29 additions & 0 deletions ui/src/grid/SearchGrids.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { useState } from "react";
import ListGrids from "./ListGrids";
import { GridSource } from "../Models";
import { FloatingLabel, Select } from "flowbite-react";

type Props = {
pageSize: number;
onGridChosen: (gridId: string) => void;
};

export default function SearchGrids({ pageSize, onGridChosen }: Props) {
const [query, setQuery] = useState("");
const [source, setSource] = useState<GridSource>("NYT");

return (
<div>
<form onSubmit={(e) => e.preventDefault()} className="flex gap-2">
<div className="grow">
<FloatingLabel sizing="sm" variant="outlined" label="Search by Name or ID" value={query} onChange={(e) => setQuery(e.target.value)} />
</div>
<Select value={source} onChange={(e) => setSource(e.target.value as GridSource)}>
<option value="NYT">NYT</option>
<option value="Custom">User Submitted</option>
</Select>
</form>
<ListGrids onGridChosen={onGridChosen} pageSize={pageSize} query={query} source={source} />
</div>
);
}
66 changes: 45 additions & 21 deletions ui/src/timer-battle/TimerBattleScreen.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import { useContext } from "react";
import { TimerBattleContext } from "./TimerBattleContext";
import { Button, Popover, Table } from "flowbite-react";
import { Badge, Button, Popover, Table } from "flowbite-react";
import _ from "underscore";
import TimedGrid from "../grid/TimedGrid";
import { Category } from "../Models";
import { useNavigate } from "react-router-dom";
import { TimerBattlePlayer } from "./Models";
import { HiOutlineEllipsisHorizontal } from "react-icons/hi2";
import SearchGrids from "../grid/SearchGrids";
import { TbPlugConnectedX } from "react-icons/tb";

export default function TimerBattleScreen() {
const { battle, username, sendMessage, setBattle, setRoomId } = useContext(TimerBattleContext);
Expand Down Expand Up @@ -102,12 +104,26 @@ export default function TimerBattleScreen() {

// Battle is not in progress, need to pick a grid
return (
<div className="flex flex-col gap-4 max-w-screen-md p-4 mx-auto">
<TimerBattleScores />
<Button onClick={selectRandomGrid}>Start with Random Grid</Button>
<Button onClick={leaveGame} role="destructive" color="red">
Leave Game
</Button>
<div className="flex flex-col gap-4 m-4">
<div className="flex grow justify-center p-2">
<div className="flex flex-col gap-2 overflow-x-auto">
<TimerBattleScores />
</div>
</div>
<div className="flex grow justify-center p-2">
<div className="flex flex-col gap-2 grow max-w-screen-md">
<Button onClick={selectRandomGrid}>Start with Random Grid</Button>
<Button onClick={leaveGame} role="destructive" color="red">
Leave Game
</Button>
</div>
</div>
<div className="flex grow justify-center p-2">
<div className="grow max-w-screen-md">
<h1 className="font-semibold text-2xl text-center my-2">Recent NYT Grids</h1>
<SearchGrids pageSize={5} onGridChosen={(gridId) => selectGrid(`/grid/${gridId}`)} />
</div>
</div>
</div>
);
}
Expand All @@ -134,15 +150,15 @@ function TimerBattleScores() {
};

return (
<Table className="overflow-x-auto" hoverable>
<Table hoverable>
<Table.Head>
<Table.HeadCell>Pos</Table.HeadCell>
<Table.HeadCell>Player</Table.HeadCell>
<Table.HeadCell className="p-2">Pos</Table.HeadCell>
<Table.HeadCell className="p-2">Player</Table.HeadCell>
{_.range(battle!.roundNumber).map((round) => (
<Table.HeadCell key={round}>Round {round}</Table.HeadCell>
<Table.HeadCell className="p-2 min-w-20" key={round}>Round {round + 1}</Table.HeadCell>
))}
<Table.HeadCell>Total</Table.HeadCell>
{isHost && <Table.HeadCell></Table.HeadCell>}
<Table.HeadCell className="p-2">Total</Table.HeadCell>
{isHost && <Table.HeadCell className="p-2"></Table.HeadCell>}
</Table.Head>
<Table.Body>
{battle!.players
Expand All @@ -151,28 +167,36 @@ function TimerBattleScores() {
const totalTime = new Date(calculateTotalTime(player));
const totalPenalties = calculatePenalties(player);
return (
<Table.Row key={i} className={player.name === username ? "bg-gray-200 dark:bg-gray-500" : ""}>
<Table.Cell>{i + 1}</Table.Cell>
<Table.Cell>{player.name}</Table.Cell>
<Table.Row key={i}>
<Table.Cell className="p-2 text-right">{i + 1}</Table.Cell>
<Table.Cell className="inline-flex gap-2 p-2">
<span>{player.name}</span>
{player.isHost && <Badge>Host</Badge>}
{!player.isActive && (
<Badge color="warning" icon={TbPlugConnectedX}>
Disconnected
</Badge>
)}
</Table.Cell>
{player.scores.map((score, j) => {
const time = new Date(score.time);
return (
<Table.Cell key={j} className="inline-flex gap-1 font-mono">
<Table.Cell key={j} className="font-mono p-2">
<span>
{time.getUTCMinutes()}:{time.getUTCSeconds().toString(10).padStart(2, "0")}
</span>
{score.penalties > 0 && <span className="text-red-500">+{score.penalties * 10}s</span>}
{score.penalties > 0 && <span className="ml-1 font-semibold text-red-500">+{score.penalties * 10}s</span>}
</Table.Cell>
);
})}
<Table.Cell className="inline-flex gap-1 font-mono">
<Table.Cell className="font-mono p-2">
<span>
{totalTime.getUTCMinutes()}:{totalTime.getUTCSeconds().toString(10).padStart(2, "0")}
</span>
{totalPenalties > 0 && <span className="text-red-500">+{totalPenalties * 10}s</span>}
{totalPenalties > 0 && <span className="ml-1 font-semibold text-red-500">+{totalPenalties * 10}s</span>}
</Table.Cell>
{isHost && (
<Table.Cell>
<Table.Cell className="p-2">
<Popover
content={
<div className="w-56 flex flex-col p-2">
Expand Down

0 comments on commit 7f6f456

Please sign in to comment.