mirror of
https://github.com/Sendouc/sendou.ink.git
synced 2026-09-30 15:17:43 -05:00
rotation selector
This commit is contained in:
@@ -1,17 +1,27 @@
|
||||
import { Select, Tag, TagCloseButton, TagLabel } from "@chakra-ui/react";
|
||||
import { t } from "@lingui/macro";
|
||||
import { useLingui } from "@lingui/react";
|
||||
import { weaponsWithHero } from "lib/lists/weaponsWithHero";
|
||||
import { salmonRunWeapons, weaponsWithHero } from "lib/lists/weaponsWithHero";
|
||||
import WeaponImage from "./WeaponImage";
|
||||
|
||||
interface Props {
|
||||
name: string;
|
||||
name?: string;
|
||||
value: string[];
|
||||
onChange: (value: string[]) => void;
|
||||
isSalmonRun?: boolean;
|
||||
}
|
||||
|
||||
const MultiWeaponSelector: React.FC<Props> = ({ name, value, onChange }) => {
|
||||
const MultiWeaponSelector: React.FC<Props> = ({
|
||||
name,
|
||||
value,
|
||||
onChange,
|
||||
isSalmonRun,
|
||||
}) => {
|
||||
const { i18n } = useLingui();
|
||||
|
||||
const weaponsArray: readonly string[] = isSalmonRun
|
||||
? salmonRunWeapons
|
||||
: weaponsWithHero;
|
||||
return (
|
||||
<>
|
||||
<Select
|
||||
@@ -26,7 +36,13 @@ const MultiWeaponSelector: React.FC<Props> = ({ name, value, onChange }) => {
|
||||
<option hidden value="">
|
||||
{t`Select weapon`}
|
||||
</option>
|
||||
{weaponsWithHero.map((wpn) => (
|
||||
{isSalmonRun && (
|
||||
<>
|
||||
<option value="RANDOM">{t`Random`}</option>
|
||||
<option value="RANDOM_GRIZZCO">{t`Random (Grizzco)`}</option>
|
||||
</>
|
||||
)}
|
||||
{weaponsArray.map((wpn) => (
|
||||
<option key={wpn} value={wpn}>
|
||||
{i18n._(wpn)}
|
||||
</option>
|
||||
|
||||
48
components/sr/AddRecordModal.tsx
Normal file
48
components/sr/AddRecordModal.tsx
Normal file
@@ -0,0 +1,48 @@
|
||||
import {
|
||||
Button,
|
||||
Modal,
|
||||
ModalBody,
|
||||
ModalCloseButton,
|
||||
ModalContent,
|
||||
ModalFooter,
|
||||
ModalHeader,
|
||||
ModalOverlay,
|
||||
} from "@chakra-ui/react";
|
||||
import { Trans } from "@lingui/macro";
|
||||
import { useState } from "react";
|
||||
import RotationSelector from "./RotationSelector";
|
||||
|
||||
interface Props {
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
const AddRecordModal: React.FC<Props> = ({ onClose }) => {
|
||||
const [sending, setSending] = useState(false);
|
||||
return (
|
||||
<Modal isOpen onClose={onClose} size="xl" closeOnOverlayClick={false}>
|
||||
<ModalOverlay>
|
||||
<ModalContent>
|
||||
<ModalHeader>
|
||||
<Trans>Adding a new Salmon Run result</Trans>
|
||||
</ModalHeader>
|
||||
<ModalCloseButton borderRadius="50%" />
|
||||
<form /*onSubmit={handleSubmit(onSubmit)}*/>
|
||||
<ModalBody pb={6}>
|
||||
<RotationSelector />
|
||||
</ModalBody>
|
||||
<ModalFooter>
|
||||
<Button mr={3} /*type="submit"*/ isLoading={sending}>
|
||||
<Trans>Save</Trans>
|
||||
</Button>
|
||||
<Button onClick={onClose} variant="outline">
|
||||
<Trans>Cancel</Trans>
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</form>
|
||||
</ModalContent>
|
||||
</ModalOverlay>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
export default AddRecordModal;
|
||||
128
components/sr/RotationSelector.tsx
Normal file
128
components/sr/RotationSelector.tsx
Normal file
@@ -0,0 +1,128 @@
|
||||
import { Box, Button, FormLabel, Select } from "@chakra-ui/react";
|
||||
import { t, Trans } from "@lingui/macro";
|
||||
import { useLingui } from "@lingui/react";
|
||||
import MultiWeaponSelector from "components/common/MultiWeaponSelector";
|
||||
import SubText from "components/common/SubText";
|
||||
import WeaponImage from "components/common/WeaponImage";
|
||||
import { salmonRunStages } from "lib/lists/stages";
|
||||
import { useMyTheme } from "lib/useMyTheme";
|
||||
import { GetAllSalmonRunRotationsData } from "prisma/queries/getAllSalmonRunRotations";
|
||||
import { useState } from "react";
|
||||
import useSWR from "swr";
|
||||
|
||||
const randomToNaturalName = {
|
||||
RANDOM: t`Random`,
|
||||
RANDOM_GRIZZCO: t`Random (Grizzco)`,
|
||||
} as const;
|
||||
|
||||
interface Props {}
|
||||
|
||||
const RotationSelector: React.FC<Props> = ({}) => {
|
||||
const { gray } = useMyTheme();
|
||||
const { i18n } = useLingui();
|
||||
const { data } = useSWR<GetAllSalmonRunRotationsData>("/api/sr/rotations");
|
||||
|
||||
const [stage, setStage] = useState("Spawning Grounds");
|
||||
const [weapons, setWeapons] = useState<string[]>([]);
|
||||
const [idSelected, setIdSelected] = useState<number | null>(null);
|
||||
|
||||
const filteredRotations = getFilteredRotations();
|
||||
|
||||
if (idSelected && data) {
|
||||
const rotation = data.find((rotation) => rotation.id === idSelected)!;
|
||||
return (
|
||||
<Box>
|
||||
<SubText>
|
||||
<Trans>Selected rotation</Trans>
|
||||
</SubText>
|
||||
<Box my={2} fontWeight="bold">
|
||||
#{rotation.id}
|
||||
</Box>
|
||||
<Box my={2} color={gray}>
|
||||
{new Date(rotation.startTime).toLocaleDateString()}
|
||||
</Box>
|
||||
{rotation.weapons.map((wpn, i) => (
|
||||
<Box as="span" mx={1} key={i}>
|
||||
<WeaponImage name={wpn} size={32} />
|
||||
</Box>
|
||||
))}
|
||||
<Box my={2}>
|
||||
<Button
|
||||
size="sm"
|
||||
variant="outline"
|
||||
onClick={() => setIdSelected(null)}
|
||||
>
|
||||
<Trans>Change selection</Trans>
|
||||
</Button>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<FormLabel htmlFor="stage">
|
||||
<Trans>Stage</Trans>
|
||||
</FormLabel>
|
||||
<Select
|
||||
name="stage"
|
||||
value={stage}
|
||||
onChange={(e) => setStage(e.target.value)}
|
||||
>
|
||||
{salmonRunStages.map((stage) => (
|
||||
<option key={stage} value={stage}>
|
||||
{i18n._(stage)}
|
||||
</option>
|
||||
))}
|
||||
</Select>
|
||||
<Box mt={4}>
|
||||
<FormLabel htmlFor="weapons">
|
||||
<Trans>Weapons</Trans>
|
||||
</FormLabel>
|
||||
<MultiWeaponSelector
|
||||
name="weapons"
|
||||
value={weapons}
|
||||
onChange={setWeapons}
|
||||
isSalmonRun
|
||||
/>
|
||||
</Box>
|
||||
{filteredRotations && (
|
||||
<Box mt={4}>
|
||||
<FormLabel htmlFor="rotation">
|
||||
<Trans>Rotation</Trans>
|
||||
</FormLabel>
|
||||
<Select
|
||||
name="rotation"
|
||||
onChange={(e) => setIdSelected(parseInt(e.target.value))}
|
||||
>
|
||||
{filteredRotations.map((rotation) => (
|
||||
<option key={rotation.id} value={rotation.id}>
|
||||
{new Date(rotation.startTime).toLocaleDateString()} -{" "}
|
||||
{rotation.weapons
|
||||
.map((wpn) =>
|
||||
wpn === "RANDOM" || wpn === "RANDOM_GRIZZCO"
|
||||
? randomToNaturalName[wpn]
|
||||
: wpn
|
||||
)
|
||||
.join(", ")}
|
||||
</option>
|
||||
))}
|
||||
</Select>
|
||||
</Box>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
|
||||
function getFilteredRotations() {
|
||||
if (!data) return null;
|
||||
if (!stage || weapons.length < 1) return null;
|
||||
return data.filter((rotation) => {
|
||||
return (
|
||||
rotation.stage === stage &&
|
||||
weapons.every((wpn) => rotation.weapons.includes(wpn))
|
||||
);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export default RotationSelector;
|
||||
@@ -142,6 +142,59 @@ export const weaponsWithHero = [
|
||||
"Kensa Undercover Brella",
|
||||
] as const;
|
||||
|
||||
export const salmonRunWeapons = [
|
||||
"Sploosh-o-matic",
|
||||
"Splattershot Jr.",
|
||||
"Splash-o-matic",
|
||||
"Aerospray MG",
|
||||
"Splattershot",
|
||||
".52 Gal",
|
||||
"N-ZAP '85",
|
||||
"Splattershot Pro",
|
||||
".96 Gal",
|
||||
"Jet Squelcher",
|
||||
"L-3 Nozzlenose",
|
||||
"H-3 Nozzlenose",
|
||||
"Squeezer",
|
||||
"Luna Blaster",
|
||||
"Blaster",
|
||||
"Range Blaster",
|
||||
"Rapid Blaster",
|
||||
"Rapid Blaster Pro",
|
||||
"Clash Blaster",
|
||||
"Carbon Roller",
|
||||
"Splat Roller",
|
||||
"Dynamo Roller",
|
||||
"Flingza Roller",
|
||||
"Inkbrush",
|
||||
"Octobrush",
|
||||
"Classic Squiffer",
|
||||
"Splat Charger",
|
||||
"Splatterscope",
|
||||
"E-liter 4K",
|
||||
"E-liter 4K Scope",
|
||||
"Bamboozler 14 Mk I",
|
||||
"Goo Tuber",
|
||||
"Slosher",
|
||||
"Tri-Slosher",
|
||||
"Sloshing Machine",
|
||||
"Bloblobber",
|
||||
"Explosher",
|
||||
"Mini Splatling",
|
||||
"Heavy Splatling",
|
||||
"Hydra Splatling",
|
||||
"Ballpoint Splatling",
|
||||
"Nautilus 47",
|
||||
"Dapple Dualies",
|
||||
"Splat Dualies",
|
||||
"Glooga Dualies",
|
||||
"Dualie Squelchers",
|
||||
"Dark Tetra Dualies",
|
||||
"Splat Brella",
|
||||
"Tenta Brella",
|
||||
"Undercover Brella",
|
||||
] as const;
|
||||
|
||||
export const weaponsWithHeroCategorized: readonly {
|
||||
name: string;
|
||||
weapons: readonly string[];
|
||||
|
||||
@@ -1,18 +1,12 @@
|
||||
import { NextApiRequest, NextApiResponse } from "next";
|
||||
import prisma from "prisma/client";
|
||||
import { getAllSalmonRunRotations } from "prisma/queries/getAllSalmonRunRotations";
|
||||
|
||||
const rotationsHandler = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||
if (req.method !== "GET") {
|
||||
return res.status(405).end();
|
||||
}
|
||||
|
||||
const rotations = await prisma.salmonRunRotation.findMany({
|
||||
where: { startTime: { lt: new Date() } },
|
||||
orderBy: { id: "desc" },
|
||||
select: { id: true, startTime: true, weapons: true, stage: true },
|
||||
});
|
||||
|
||||
res.status(200).json(rotations);
|
||||
res.status(200).json(await getAllSalmonRunRotations());
|
||||
};
|
||||
|
||||
export default rotationsHandler;
|
||||
|
||||
23
pages/sr/leaderboards.tsx
Normal file
23
pages/sr/leaderboards.tsx
Normal file
@@ -0,0 +1,23 @@
|
||||
import { Button } from "@chakra-ui/react";
|
||||
import { t, Trans } from "@lingui/macro";
|
||||
import Breadcrumbs from "components/common/Breadcrumbs";
|
||||
import AddRecordModal from "components/sr/AddRecordModal";
|
||||
import { useState } from "react";
|
||||
|
||||
const SalmonRunLeaderboardsPage = ({}) => {
|
||||
const [showModal, setShowModal] = useState(true);
|
||||
|
||||
return (
|
||||
<>
|
||||
{showModal && <AddRecordModal onClose={() => setShowModal(false)} />}
|
||||
<Breadcrumbs
|
||||
pages={[{ name: t`Salmon Run` }, { name: t`Leaderboards` }]}
|
||||
/>
|
||||
<Button variant="outline" onClick={() => setShowModal(true)}>
|
||||
<Trans>Submit result</Trans>
|
||||
</Button>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default SalmonRunLeaderboardsPage;
|
||||
13
prisma/queries/getAllSalmonRunRotations.ts
Normal file
13
prisma/queries/getAllSalmonRunRotations.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import { Prisma } from "@prisma/client";
|
||||
import prisma from "prisma/client";
|
||||
|
||||
export type GetAllSalmonRunRotationsData = Prisma.PromiseReturnType<
|
||||
typeof getAllSalmonRunRotations
|
||||
>;
|
||||
|
||||
export const getAllSalmonRunRotations = async () =>
|
||||
prisma.salmonRunRotation.findMany({
|
||||
where: { startTime: { lt: new Date() } },
|
||||
orderBy: { id: "desc" },
|
||||
select: { id: true, startTime: true, weapons: true, stage: true },
|
||||
});
|
||||
BIN
public/weapons/RANDOM.png
Normal file
BIN
public/weapons/RANDOM.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.4 KiB |
BIN
public/weapons/RANDOM_GRIZZCO.png
Normal file
BIN
public/weapons/RANDOM_GRIZZCO.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.2 KiB |
Reference in New Issue
Block a user