"use client"; import { useCallback, useEffect, useMemo, useState } from "react"; import { zodResolver } from "@hookform/resolvers/zod"; import { useQuery } from "@tanstack/react-query"; import { isEmpty, sortBy } from "lodash"; import { useTranslations } from "next-intl"; import { usePathname, useRouter, useSearchParams } from "next/navigation"; import { FormProvider, useForm } from "react-hook-form"; import { z } from "zod"; import { ErrorBox } from "@/components/ErrorBox"; import { Spinner } from "@/components/Spinner"; import { TranslatedError } from "@/components/TranslatedError"; import { SelectField } from "@/components/form/SelectField"; import { TournamentSelectField } from "@/components/form/TournamentSelectField"; import { fetchTournamentResultsSchema } from "@/schemas"; import { fetchTournamentResults } from "@/server/fetchTournamentResults"; import { SearchedTournament } from "@/server/searchTournaments"; import { TimeControl } from "@/types"; import { Link } from "@/utils/navigation"; import { KFactor } from "./KFactor"; import { ManualEloForm } from "./ManualEloForm"; import { TournamentResults } from "./TournamentResults"; const kFactors = ["40", "20", "10"]; const formSchema = fetchTournamentResultsSchema.extend({ tournamentId: z.string().optional(), player: z.string().optional(), kFactor: z.string(), }); type FetchResultsFormValues = z.infer; export default function Elo() { const t = useTranslations("Elo"); type TranslationKey = Parameters[0]; const router = useRouter(); const pathname = usePathname(); const searchParams = useSearchParams(); const current = useMemo( () => new URLSearchParams(Array.from(searchParams.entries())), [searchParams], ); const [tournament, setTournament] = useState(null); const tournamentId = searchParams.get("tId") ?? ""; const kFactorParam = searchParams.get("k"); const player = searchParams.get("player") ?? ""; const kFactor = kFactorParam && kFactors.includes(kFactorParam) ? kFactorParam : "20"; const hasTournamentId = !isEmpty(tournamentId.trim()); const { data: allResults, isFetching, error, } = useQuery({ queryKey: ["fetchTournamentResults", { id: tournamentId?.trim() }], queryFn: async () => fetchTournamentResults({ id: tournamentId?.trim() }), refetchOnWindowFocus: false, staleTime: Infinity, gcTime: 10 * 60 * 1000, enabled: hasTournamentId, retry: false, }); const playerOptions = sortBy( (allResults?.data ?? []).map((player) => ({ value: player.id, label: player.name, })), "label", ); const form = useForm({ resolver: zodResolver(fetchTournamentResultsSchema), defaultValues: { tournamentId: tournamentId ?? "", kFactor: kFactor ?? "20", player: player ?? "", }, }); const clearForm = useCallback(() => { current.delete("tId"); current.delete("k"); current.delete("player"); form.setValue("tournamentId", ""); form.setValue("kFactor", "20"); form.setValue("player", ""); const search = current.toString(); const query = search ? `?${search}` : ""; router.push(`${pathname}${query}`); }, [current, form, pathname, router]); useEffect(() => { // We subscribe to form changes and update the URL parameters const subscription = form.watch((value, { name, type }) => { let update = false; if (type === "change") { if ( name === "tournamentId" && value.tournamentId !== tournamentId && value.tournamentId ) { current.set("tId", value.tournamentId); update = true; } else if ( name === "tournamentId" && value.tournamentId !== tournamentId && value.tournamentId === null ) { clearForm(); } else if ( name === "player" && value.player !== player && value.player ) { current.set("player", value.player); update = true; } else if ( name === "kFactor" && value.kFactor !== kFactor && value.kFactor ) { current.set("k", value.kFactor); update = true; } if (update) { const search = current.toString(); const query = search ? `?${search}` : ""; router.replace(`${pathname}${query}`); } } }); return () => subscription.unsubscribe(); }, [ clearForm, current, form, form.watch, kFactor, pathname, player, router, tournamentId, ]); useEffect(() => { // When the URL changes, we update the form values if (tournamentId !== form.getValues("tournamentId")) { form.setValue("tournamentId", tournamentId); } if (player !== form.getValues("player")) { form.setValue("player", player); } if (kFactor !== form.getValues("kFactor")) { form.setValue("kFactor", form.getValues("kFactor")); } }, [searchParams, form, player, kFactor, tournamentId]); const playerResults = allResults?.data?.find((p) => p.id === player); return (

{t("title")}

{t("info")}

{hasTournamentId && !isFetching && !error && (
)}
t("noTournamentsFound")} onInformChange={(tournaments) => { setTournament(tournaments?.[0]?.data ?? null); }} /> {isFetching && (
)} {!isFetching && playerOptions.length > 0 && (
({ value: k, label: k, }))} isClearable={false} required />
)}
{hasTournamentId && !isFetching && player && playerResults && !error && ( )} {(!!error || allResults?.serverError) && ( } /> )} {((!hasTournamentId && !isFetching) || !!error) && ( <>
{t("useManualLabel")}
)}
); }