diff --git a/app/components/BuyClassCard/index.tsx b/app/components/BuyClassCard/index.tsx index 7d975e0..6685b6d 100644 --- a/app/components/BuyClassCard/index.tsx +++ b/app/components/BuyClassCard/index.tsx @@ -51,32 +51,35 @@ const BuyClassCard = ({ cards, xp, name, label, owned, formApi }: Props) => { ))}
{buyable.map((card) => { - const disabled = !field.value().some((v: string) => +v === card.id) && card.cost > balance + const disabled = + !field.value().some((v: string) => +v === card.id) && + card.cost > balance return ( - - )})} + + ) + })}
{error && {error}}
diff --git a/app/components/BuyItemCard/index.tsx b/app/components/BuyItemCard/index.tsx index 81defb8..b016eae 100644 --- a/app/components/BuyItemCard/index.tsx +++ b/app/components/BuyItemCard/index.tsx @@ -70,7 +70,10 @@ const BuyItemCard = ({ const balance = credits - boughtItems.reduce((acc, cur) => acc + cur.cost, 0) + - soldItems.reduce((acc, cur) => acc + (cur.cost ? getSellPrice(cur.cost) : 50), 0) + soldItems.reduce( + (acc, cur) => acc + (cur.cost ? getSellPrice(cur.cost) : 50), + 0 + ) return (
diff --git a/app/components/EditButton/index.tsx b/app/components/EditButton/index.tsx index 6f4ffdb..bb2c085 100644 --- a/app/components/EditButton/index.tsx +++ b/app/components/EditButton/index.tsx @@ -1,9 +1,9 @@ -import type { ElementRef, MouseEventHandler } from 'react' +import type { ComponentRef, MouseEventHandler } from 'react' import clsx from 'clsx' import { PencilIcon, XMarkIcon } from '@heroicons/react/24/solid' type EditButtonProps = { - onClick: MouseEventHandler> + onClick: MouseEventHandler> active: boolean hideLabel?: boolean disabled?: boolean diff --git a/app/components/Modal/index.tsx b/app/components/Modal/index.tsx index dc60f81..ac01f17 100644 --- a/app/components/Modal/index.tsx +++ b/app/components/Modal/index.tsx @@ -1,22 +1,21 @@ import clsx from 'clsx' -import { - useRef, - type ElementRef, - type HTMLProps, - type PropsWithChildren, - type ReactEventHandler, - useEffect +import type { + PropsWithChildren, + HTMLProps, + ComponentRef, + ReactEventHandler } from 'react' +import { useRef, useEffect } from 'react' type Props = PropsWithChildren< { open?: boolean - onClose?: ReactEventHandler> - } & HTMLProps> + onClose?: ReactEventHandler> + } & HTMLProps> > const Modal = ({ children, open, onClose, ...props }: Props) => { - const ref = useRef>(null) + const ref = useRef>(null) useEffect(() => { if (open) { diff --git a/app/components/PlaceholderInput/index.tsx b/app/components/PlaceholderInput/index.tsx index 26b8cef..35fc217 100644 --- a/app/components/PlaceholderInput/index.tsx +++ b/app/components/PlaceholderInput/index.tsx @@ -1,7 +1,8 @@ import type { FormApi } from '@rvf/react-router' import RequiredIndicator from '~/components/RequiredIndicator' import clsx from 'clsx' -import { useState, type ChangeEventHandler, type ElementRef } from 'react' +import { useState } from 'react' +import type { ChangeEventHandler, ComponentRef } from 'react' import type { loader as resolveLoader } from '~/routes/_app.games.$game.resolve.$mission._index' import type { useLoaderData } from 'react-router' import type { JsonObject } from '@prisma/client/runtime/library' @@ -13,7 +14,7 @@ type Placeholder = ReturnType< type Props = { index: number placeholder: Placeholder - onChange?: ChangeEventHandler> + onChange?: ChangeEventHandler> formApi: FormApi } diff --git a/app/components/RebelRewardManager/index.tsx b/app/components/RebelRewardManager/index.tsx index a5fa3b5..0c8ad9a 100644 --- a/app/components/RebelRewardManager/index.tsx +++ b/app/components/RebelRewardManager/index.tsx @@ -157,11 +157,9 @@ const RebelRewardManager = ({ rebel, allRewards, formAction }: Props) => {
- {form - .value('rewards') - ?.map((_, i) => ( - - ))} + {form.value('rewards')?.map((_, i) => ( + + ))} ) : !rebel.rewards.length ? (

No Rewards

diff --git a/app/components/SelectInput/index.tsx b/app/components/SelectInput/index.tsx index ddb5905..a5603ea 100644 --- a/app/components/SelectInput/index.tsx +++ b/app/components/SelectInput/index.tsx @@ -53,10 +53,7 @@ const SelectInput = forwardRef( {labelRight} { schema, method: 'POST', defaultValues: { - slot: -1, - mission: -1 + slot: -1 as number, + mission: -1 as number } }) @@ -401,13 +401,19 @@ const Game = () => { -
+
Side Mission Deck + + Settings +
diff --git a/app/routes/_app.games.$game.rebels.rewards.ts b/app/routes/_app.games.$game.rebels.rewards.ts index e466fb6..caaa618 100644 --- a/app/routes/_app.games.$game.rebels.rewards.ts +++ b/app/routes/_app.games.$game.rebels.rewards.ts @@ -13,7 +13,7 @@ export const loader = async ({ params }: LoaderFunctionArgs) => { export const rewardSchema = z.object({ id: z.coerce.number().int().positive(), - rewards: z.array(z.coerce.number().int().positive()).default([]), + rewards: z.array(z.coerce.number().int().positive()).default([]) }) export const action = async (args: ActionFunctionArgs) => { diff --git a/app/routes/_app.games.$game.resolve.$mission._index.tsx b/app/routes/_app.games.$game.resolve.$mission._index.tsx index 2d7c498..b63307c 100644 --- a/app/routes/_app.games.$game.resolve.$mission._index.tsx +++ b/app/routes/_app.games.$game.resolve.$mission._index.tsx @@ -2,7 +2,7 @@ import { redirect, useLoaderData, useOutletContext } from 'react-router' import type { LoaderData } from './_app.games.$game' import type { ActionFunctionArgs, LoaderFunctionArgs } from 'react-router' import { prisma } from '~/services/db.server' -import type { ChangeEvent, ElementRef } from 'react' +import type { ChangeEvent, ComponentRef } from 'react' import { useEffect, useId, useReducer, useState } from 'react' import { MissionRewardType, @@ -698,7 +698,7 @@ const Resolve = () => { action: { name: string type: string - event: ChangeEvent> + event: ChangeEvent> } ) => ({ ...state, diff --git a/app/routes/_app.games.$game.settings.tsx b/app/routes/_app.games.$game.settings.tsx new file mode 100644 index 0000000..c62338b --- /dev/null +++ b/app/routes/_app.games.$game.settings.tsx @@ -0,0 +1,99 @@ +import { parseFormData, useForm } from '@rvf/react-router' +import { useState } from 'react' +import { ActionFunction, redirect, useFetcher } from 'react-router' +import z from 'zod' +import Modal from '~/components/Modal' +import { requireAuth } from '~/utils/requireAuth.server' +import { prisma } from '~/services/db.server' +import SubmitButton from '~/components/SubmitButton' + +const schema = z.object({ + action: z.enum(['delete']) +}) + +export const action: ActionFunction = async (args) => { + const { data } = await parseFormData(await args.request.formData(), schema) + + const { userId } = await requireAuth(args) + const gameId = parseInt(args.params.game!, 10) + + switch (data?.action) { + case 'delete': { + await prisma.game.delete({ + where: { + id: gameId, + userId: userId + } + }) + + return redirect('/games') + } + } + + throw new Response('Invalid action', { status: 400 }) +} + +const GameSettings = () => { + const [deleting, setDeleting] = useState(false) + + const deleteFetcher = useFetcher() + const deleteForm = useForm({ + schema, + defaultValues: { action: 'delete' }, + fetcher: deleteFetcher, + method: 'POST' + }) + + return ( + <> +
+

Settings

+
+
+
+

Delete Game

+

+ This will permanently delete the game and all associated data. + This action cannot be undone. +

+
+ +
+
+
+ setDeleting(false)}> +
+ +

Confirm Deletion

+

+ Are you sure you want to delete this game? This action cannot be + undone. +

+
+ + + Delete Game + +
+
+
+ + ) +} + +export default GameSettings diff --git a/app/routes/_app.games.new.tsx b/app/routes/_app.games.new.tsx index 573e093..07dcbd9 100644 --- a/app/routes/_app.games.new.tsx +++ b/app/routes/_app.games.new.tsx @@ -741,8 +741,9 @@ const NewGame = () => { label="Imperial Class" required hintRight={ - imperialClasses.find((c) => c.id === +form.value('imperialClass'))?.cards?.[0] - ?.name + imperialClasses.find( + (c) => c.id === +form.value('imperialClass') + )?.cards?.[0]?.name } >