"use client"; import { zodResolver } from "@hookform/resolvers/zod"; import { last } from "lodash"; import { useTranslations } from "next-intl"; import { FormProvider, useFieldArray, useForm } from "react-hook-form"; import { IoAdd, IoCloseOutline } from "react-icons/io5"; import { twMerge } from "tailwind-merge"; import { z } from "zod"; import { RadioGroupField } from "@/components/form/RadioGroupField"; import { SelectField } from "@/components/form/SelectField"; import { TextField } from "@/components/form/TextField"; import { getNewRating } from "@/utils/eloCalculator"; import { KFactor } from "./KFactor"; const resultsSchema = z.object({ currentElo: z.number().int().positive(), kFactor: z.enum(["40", "30", "20", "15", "10"]), games: z.array( z.object({ opponentElo: z.number().int().positive(), result: z.enum(["win", "draw", "loss"]), }), ), }); type DeepPartial = T extends object ? { [P in keyof T]?: DeepPartial; } : T; type EloFormValues = DeepPartial>; export const ManualEloForm = () => { const t = useTranslations("Elo"); const form = useForm({ resolver: zodResolver(resultsSchema), defaultValues: { kFactor: "20", games: [{}], }, }); const { fields: gameFields, append: appendGame, remove: removeGame, } = useFieldArray({ control: form.control, name: "games", }); const onSubmit = async (data: EloFormValues) => {}; const [currentElo, kFactor, games] = form.watch([ "currentElo", "kFactor", "games", ]); type Deltas = { rating: number; deltas: { rating: number; delta: number | undefined }[]; }; const calculations = !Number.isNaN(currentElo) ? (games ?? []).reduce( (acc, game) => { if (!Number.isNaN(game?.opponentElo) && game?.result) { const result = game?.result === "win" ? 1 : game?.result === "loss" ? 0 : 0.5; const { delta } = getNewRating( currentElo!, game.opponentElo!, result, parseInt(kFactor!, 10), ); return { rating: acc.rating + delta, deltas: [...acc.deltas, { rating: acc.rating + delta, delta }], }; } return { rating: acc.rating, deltas: [...acc.deltas, { rating: acc.rating, delta: undefined }], }; }, { rating: currentElo!, deltas: [] }, ) : { rating: currentElo!, deltas: [] }; const deltas = calculations.deltas; const hasDeltas = deltas.some((d) => d.delta !== undefined); const totalDelta = Math.round( deltas.reduce((acc, delta) => acc + (delta.delta ?? 0), 0), ); return (
({ value: k, label: k, }))} isClearable={false} required />

{t("resultsTitle")}

{gameFields.map((game, i) => { return (
({ value: result, label: t("gameResult", { result }), }))} required /> {gameFields.length > 1 && ( )}
{gameFields.length > 1 && ( )}
{deltas[i]?.delta !== undefined && (
1 && "mr-10", i === deltas.length - 1 && "font-bold", )} > 0 && "text-success", deltas[i].delta! < 0 && "text-error", )} > {deltas[i]!.delta! >= 0 ? "+" : ""} {deltas[i].delta!}
)}
); })}
{hasDeltas && (
{t.rich("finalRating", { rating: Math.round(last(deltas)!.rating), delta: () => ( 0 && "text-success", totalDelta! < 0 && "text-error", )} > {totalDelta >= 0 ? "+" : ""} {totalDelta} ), })}
)}
); };