can edit builds

This commit is contained in:
Kalle (Sendou)
2020-11-26 17:55:14 +02:00
parent 61cf6e72b6
commit c1805cf259
6 changed files with 110 additions and 43 deletions

View File

@@ -1,7 +1,7 @@
### Before production
- [x] Can log in
- [ ] Can update builds
- [x] Can update builds
- [ ] Build Analyzer
- [ ] Import /analyzer
- [ ] Builds link to /analyzer

View File

@@ -18,7 +18,7 @@ import { useMyTheme } from "lib/useMyTheme";
import Image from "next/image";
import Link from "next/link";
import { useState } from "react";
import { FiBarChart2, FiInfo, FiTarget } from "react-icons/fi";
import { FiBarChart2, FiEdit, FiInfo, FiTarget } from "react-icons/fi";
import Gears from "./Gears";
import ViewAP from "./ViewAP";
import ViewSlots from "./ViewSlots";
@@ -26,17 +26,15 @@ import ViewSlots from "./ViewSlots";
interface BuildCardProps {
// TODO: don't select unnecessary fields
build: PartialBy<Prisma.BuildGetPayload<{ include: { user: true } }>, "user">;
canModify?: boolean;
//setBuildBeingEdited?: (build: Build) => void;
otherBuildCount?: number;
onShowAllByUser?: () => void;
onEdit?: (build: BuildCardProps["build"]) => void;
showWeapon?: boolean;
}
const BuildCard: React.FC<BuildCardProps & BoxProps> = ({
build,
canModify,
//setBuildBeingEdited,
onEdit,
otherBuildCount,
onShowAllByUser,
showWeapon,
@@ -159,20 +157,17 @@ const BuildCard: React.FC<BuildCardProps & BoxProps> = ({
</PopoverContent>
</Popover>
)}
{/* {canModify && (
{onEdit && (
<IconButton
variant="ghost"
isRound
colorScheme={themeColor}
onClick={
setBuildBeingEdited && (() => setBuildBeingEdited(build))
}
aria-label="Show description"
onClick={() => onEdit(build)}
aria-label="Edit build"
fontSize="20px"
icon={<FiEdit />}
ml="0.5em"
/>
)} */}
)}
</Flex>
<Box mt="1em">
<Gears build={build} />

View File

@@ -93,7 +93,11 @@ const BuildModal: React.FC<Props> = ({ onClose, build }) => {
}
}
const success = await sendData("POST", "/api/builds", mutationData);
const success = await sendData(
build ? "PUT" : "POST",
"/api/builds",
build ? { ...mutationData, id: build.id } : mutationData
);
setSending(false);
if (!success) return;

View File

@@ -73,6 +73,7 @@ export const buildSchema = z.object({
.refine((val) => !val || shoesGear.includes(val as any))
.optional()
.nullable(),
id: z.number().optional(),
});
function isAbilityArray(arr: any[], type: "HEAD" | "CLOTHING" | "SHOES") {

View File

@@ -8,11 +8,7 @@ import DBClient from "prisma/client";
const prisma = DBClient.getInstance().prisma;
const buildHandler = async (req: NextApiRequest, res: NextApiResponse) => {
if (req.method !== "POST") {
return res.status(405).end();
}
const postHandler = async (req: NextApiRequest, res: NextApiResponse) => {
const user = await getMySession(req);
if (!user) return res.status(401).end();
@@ -33,23 +29,6 @@ const buildHandler = async (req: NextApiRequest, res: NextApiResponse) => {
.json({ message: "You have too many builds posted already" });
}
const getAbilityPoints = () => {
const result: Partial<Record<Ability, number>> = {};
const getPointsFromAbilityArray = (ability: Ability, index: number) => {
const existingAmount = result[ability] ?? 0;
const apsToAdd = index === 0 ? 10 : 3;
result[ability] = existingAmount + apsToAdd;
};
parsed.data.headAbilities.forEach(getPointsFromAbilityArray);
parsed.data.clothingAbilities.forEach(getPointsFromAbilityArray);
parsed.data.shoesAbilities.forEach(getPointsFromAbilityArray);
return result;
};
const hasTop500WithTheWeapon = async () => {
const playerData = await prisma.player.findFirst({
where: { userId: user.id },
@@ -70,7 +49,11 @@ const buildHandler = async (req: NextApiRequest, res: NextApiResponse) => {
await prisma.build.create({
data: {
...parsed.data,
abilityPoints: getAbilityPoints(),
abilityPoints: getAbilityPoints(
parsed.data.headAbilities,
parsed.data.clothingAbilities,
parsed.data.shoesAbilities
),
jpn: postsJpBuilds,
top500: await hasTop500WithTheWeapon(),
user: {
@@ -84,4 +67,79 @@ const buildHandler = async (req: NextApiRequest, res: NextApiResponse) => {
res.status(200).end();
};
const updateHandler = async (req: NextApiRequest, res: NextApiResponse) => {
const user = await getMySession(req);
if (!user) return res.status(401).end();
const parsed = buildSchema.safeParse(req.body);
if (!parsed.success) {
return res.status(400).end();
}
const id = parsed.data.id;
const existingBuild = await prisma.build.findUnique({
where: { id },
});
if (!existingBuild || existingBuild.weapon !== parsed.data.weapon) {
return res.status(400).end();
}
if (existingBuild.userId !== user.id) {
return res.status(403).end();
}
delete parsed.data.id;
await prisma.build.update({
where: { id },
data: {
...parsed.data,
abilityPoints: getAbilityPoints(
parsed.data.headAbilities,
parsed.data.clothingAbilities,
parsed.data.shoesAbilities
),
},
});
res.status(200).end();
};
const buildHandler = async (req: NextApiRequest, res: NextApiResponse) => {
switch (req.method) {
case "POST":
await postHandler(req, res);
break;
case "PUT":
await updateHandler(req, res);
break;
default:
return res.status(405).end();
}
};
function getAbilityPoints(
headAbilities: Ability[],
clothingAbilities: Ability[],
shoesAbilities: Ability[]
) {
const result: Partial<Record<Ability, number>> = {};
const getPointsFromAbilityArray = (ability: Ability, index: number) => {
const existingAmount = result[ability] ?? 0;
const apsToAdd = index === 0 ? 10 : 3;
result[ability] = existingAmount + apsToAdd;
};
headAbilities.forEach(getPointsFromAbilityArray);
clothingAbilities.forEach(getPointsFromAbilityArray);
shoesAbilities.forEach(getPointsFromAbilityArray);
return result;
}
export default buildHandler;

View File

@@ -1,7 +1,7 @@
import { Button, Divider, Select } from "@chakra-ui/react";
import { t, Trans } from "@lingui/macro";
import { useLingui } from "@lingui/react";
import { RankedMode } from "@prisma/client";
import { Build, RankedMode } from "@prisma/client";
import BuildCard from "components/builds/BuildCard";
import Breadcrumbs from "components/common/Breadcrumbs";
import Markdown from "components/common/Markdown";
@@ -33,7 +33,7 @@ interface Props {
const ProfilePage = (props: Props) => {
const [showProfileModal, setShowProfileModal] = useState(false);
const [showBuildModal, setShowBuildModal] = useState(false);
const [buildToEdit, setBuildToEdit] = useState<boolean | Build>(false);
const [loggedInUser] = useUser();
const { data: user } = useSWR<GetUserByIdentifierData>(
@@ -64,8 +64,11 @@ const ProfilePage = (props: Props) => {
{showProfileModal && (
<ProfileModal onClose={() => setShowProfileModal(false)} user={user} />
)}
{showBuildModal && (
<BuildModal onClose={() => setShowBuildModal(false)} />
{buildToEdit && (
<BuildModal
onClose={() => setBuildToEdit(false)}
build={typeof buildToEdit === "boolean" ? undefined : buildToEdit}
/>
)}
<Breadcrumbs
@@ -109,13 +112,19 @@ const ProfilePage = (props: Props) => {
</Select>
)}
{canPostBuilds() && (
<Button mt={5} onClick={() => setShowBuildModal(true)}>
<Button mt={5} onClick={() => setBuildToEdit(true)}>
<Trans>Add build</Trans>
</Button>
)}
<MyInfiniteScroller>
{builds.map((build) => (
<BuildCard key={build.id} build={build} m={2} showWeapon />
<BuildCard
key={build.id}
build={build}
m={2}
showWeapon
onEdit={(build) => setBuildToEdit(build)}
/>
))}
</MyInfiniteScroller>
</>