Display a message when there are no results

This commit is contained in:
Timothy Armes
2024-09-27 10:53:15 +02:00
parent 93e3e1242a
commit f8cd2612b5
8 changed files with 92 additions and 11 deletions
+8 -4
View File
@@ -10,7 +10,9 @@ 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";
@@ -170,10 +172,6 @@ export default function Elo() {
}
}, [searchParams, form, player, kFactor, tournamentId]);
if (error) {
console.error(error);
}
const playerResults = allResults?.data?.find((p) => p.id === player);
return (
@@ -278,6 +276,12 @@ export default function Elo() {
/>
)}
{(!!error || allResults?.serverError) && (
<ErrorBox
error={<TranslatedError err={error ?? allResults?.serverError} />}
/>
)}
{((!hasTournamentId && !isFetching) || !!error) && (
<>
<div className="relative my-8">
+16
View File
@@ -0,0 +1,16 @@
type ErrorBoxProps = { title?: React.ReactNode; error?: React.ReactNode };
export const ErrorBox = ({ title, error }: ErrorBoxProps) => {
if (!error) return null;
return (
<div className="mt-8 rounded bg-orange-700/20 p-4 text-orange-700">
<div className="flex flex-col gap-4">
{title && <div className="font-bold">{title}</div>}
<div>
<p className="font-medium text-sm">{error}</p>
</div>
</div>
</div>
);
};
+36
View File
@@ -0,0 +1,36 @@
import isNil from "lodash/isNil";
import { useTranslations } from "next-intl";
import { Path } from "@/types";
type TranslatedErrorProps = {
err: unknown;
};
export const TranslatedError = ({ err }: TranslatedErrorProps) => {
const t = useTranslations("Errors");
if (isNil(err)) {
return null;
}
let error: string | undefined;
if (typeof err === "string") {
error = err;
}
if (isNil(error) && err instanceof Error) {
error = err.message;
}
if (isNil(error)) {
error = "ERR_UNEXPECTED";
}
if (error?.startsWith("ERR_")) {
return t(error as Path<IntlMessages["Errors"]>);
}
return <span>{error}</span>;
};
+8 -3
View File
@@ -16,6 +16,13 @@
"timeControlEnum": "{tc, select, Classic {Classic} Rapid {Rapid} Blitz {Blitz} Other {Other} other {{tc}}}"
},
"Errors": {
"ERR_UNEXPECTED": "An unexpected error occurred",
"ERR_TOURNAMENT_NOT_FOUND": "Tournament not found",
"ERR_TOURNAMENT_RESULTS_NOT_AVAILABLE": "Results are not yet available for this tournament",
"ERR_NO_TOURNAMENT_ID": "The link you provided is incorrect"
},
"Nav": {
"title": "Échecs France",
"tournaments": "Tournaments",
@@ -191,8 +198,6 @@
"kFactorInfo5": "10 once a player's published rating has reached 2400 and remains at that level subsequently, even if the rating drops below 2400.",
"kFactorInfo6": "40 for all players until their 18th birthday, as long as their rating remains under 2300.",
"unknownError": "We were unable to fetch results from this URL. Please <contact>contact us</contact> if the problem persists.",
"ERR_TOURNAMENT_NOT_FOUND": "Tournament not found",
"ERR_NO_TOURNAMENT_ID": "The link you provided is incorrect"
"unknownError": "We were unable to fetch results from this URL. Please <contact>contact us</contact> if the problem persists."
}
}
+8 -3
View File
@@ -16,6 +16,13 @@
"timeControlEnum": "{tc, select, Classic {Cadence Lente} Rapid {Rapide} Blitz {Blitz} Other {Autres} other {{tc}}}"
},
"Errors": {
"ERR_UNEXPECTED": "Une erreur inattendue est survenue",
"ERR_TOURNAMENT_NOT_FOUND": "Tournoi introuvable",
"ERR_TOURNAMENT_RESULTS_NOT_AVAILABLE": "Les résultats ne sont pas encore disponibles pour ce tournoi.",
"ERR_NO_TOURNAMENT_ID": "L'identifiant du tournoi fourni est incorrect"
},
"Nav": {
"title": "Échecs France",
"tournaments": "Tournois",
@@ -192,8 +199,6 @@
"kFactorInfo5": "<b>10</b> une fois que le classement publié d'un joueur a atteint 2400 et reste à ce niveau par la suite, même s'il redescend en dessous de 2400.",
"kFactorInfo6": "<b>40</b> pour tous les joueurs jusqu'à leur 18e anniversaire, tant que leur classement reste inférieur à 2300.",
"unknownError": "Nous n'avons pas pu récupérer les résultats à partir de cette URL. Veuillez <contact>nous contacter</contact> si le problème persiste.",
"ERR_TOURNAMENT_NOT_FOUND": "Tournoi introuvable",
"ERR_NO_TOURNAMENT_ID": "L'identifiant du tournoi fourni est incorrect"
"unknownError": "Nous n'avons pas pu récupérer les résultats à partir de cette URL. Veuillez <contact>nous contacter</contact> si le problème persiste."
}
}
+5
View File
@@ -124,6 +124,10 @@ export const fetchTournamentResults = action(
},
);
if (rawResults.status >= 500) {
throw new Error("ERR_TOURNAMENT_RESULTS_NOT_AVAILABLE");
}
const results = await rawResults.json();
if ("detail" in results && results.detail === "Not found") {
@@ -131,6 +135,7 @@ export const fetchTournamentResults = action(
}
const parsedResults = dbSchema.parse(results);
return outputSchema.parse(
parsedResults.map<z.infer<typeof outputSchema>[number]>((player) => ({
id: player.id,
+5 -1
View File
@@ -1,3 +1,7 @@
import { createSafeActionClient } from "next-safe-action";
export const action = createSafeActionClient();
export const action = createSafeActionClient({
handleReturnedServerError(error) {
return error.message;
},
});
+6
View File
@@ -93,5 +93,11 @@ export type Prettify<T> = {
[K in keyof T]: T[K];
} & {};
export type Path<T, K extends keyof T = keyof T> = K extends string
? T[K] extends Record<string, unknown>
? `${K}.${Path<T[K], keyof T[K]>}`
: K
: never;
export type ExtractSafeActionResult<T> =
T extends SafeAction<infer Schema, infer Result> ? Result : never;