mirror of
https://github.com/TheRealOwenRees/echecsfrance.git
synced 2026-07-22 20:16:57 +00:00
Display a message when there are no results
This commit is contained in:
@@ -10,7 +10,9 @@ import { usePathname, useRouter, useSearchParams } from "next/navigation";
|
|||||||
import { FormProvider, useForm } from "react-hook-form";
|
import { FormProvider, useForm } from "react-hook-form";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
|
import { ErrorBox } from "@/components/ErrorBox";
|
||||||
import { Spinner } from "@/components/Spinner";
|
import { Spinner } from "@/components/Spinner";
|
||||||
|
import { TranslatedError } from "@/components/TranslatedError";
|
||||||
import { SelectField } from "@/components/form/SelectField";
|
import { SelectField } from "@/components/form/SelectField";
|
||||||
import { TournamentSelectField } from "@/components/form/TournamentSelectField";
|
import { TournamentSelectField } from "@/components/form/TournamentSelectField";
|
||||||
import { fetchTournamentResultsSchema } from "@/schemas";
|
import { fetchTournamentResultsSchema } from "@/schemas";
|
||||||
@@ -170,10 +172,6 @@ export default function Elo() {
|
|||||||
}
|
}
|
||||||
}, [searchParams, form, player, kFactor, tournamentId]);
|
}, [searchParams, form, player, kFactor, tournamentId]);
|
||||||
|
|
||||||
if (error) {
|
|
||||||
console.error(error);
|
|
||||||
}
|
|
||||||
|
|
||||||
const playerResults = allResults?.data?.find((p) => p.id === player);
|
const playerResults = allResults?.data?.find((p) => p.id === player);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -278,6 +276,12 @@ export default function Elo() {
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{(!!error || allResults?.serverError) && (
|
||||||
|
<ErrorBox
|
||||||
|
error={<TranslatedError err={error ?? allResults?.serverError} />}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
{((!hasTournamentId && !isFetching) || !!error) && (
|
{((!hasTournamentId && !isFetching) || !!error) && (
|
||||||
<>
|
<>
|
||||||
<div className="relative my-8">
|
<div className="relative my-8">
|
||||||
|
|||||||
@@ -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>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -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>;
|
||||||
|
};
|
||||||
@@ -16,6 +16,13 @@
|
|||||||
"timeControlEnum": "{tc, select, Classic {Classic} Rapid {Rapid} Blitz {Blitz} Other {Other} other {{tc}}}"
|
"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": {
|
"Nav": {
|
||||||
"title": "Échecs France",
|
"title": "Échecs France",
|
||||||
"tournaments": "Tournaments",
|
"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.",
|
"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.",
|
"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.",
|
"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"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,13 @@
|
|||||||
"timeControlEnum": "{tc, select, Classic {Cadence Lente} Rapid {Rapide} Blitz {Blitz} Other {Autres} other {{tc}}}"
|
"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": {
|
"Nav": {
|
||||||
"title": "Échecs France",
|
"title": "Échecs France",
|
||||||
"tournaments": "Tournois",
|
"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.",
|
"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.",
|
"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.",
|
"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"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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();
|
const results = await rawResults.json();
|
||||||
|
|
||||||
if ("detail" in results && results.detail === "Not found") {
|
if ("detail" in results && results.detail === "Not found") {
|
||||||
@@ -131,6 +135,7 @@ export const fetchTournamentResults = action(
|
|||||||
}
|
}
|
||||||
|
|
||||||
const parsedResults = dbSchema.parse(results);
|
const parsedResults = dbSchema.parse(results);
|
||||||
|
|
||||||
return outputSchema.parse(
|
return outputSchema.parse(
|
||||||
parsedResults.map<z.infer<typeof outputSchema>[number]>((player) => ({
|
parsedResults.map<z.infer<typeof outputSchema>[number]>((player) => ({
|
||||||
id: player.id,
|
id: player.id,
|
||||||
|
|||||||
@@ -1,3 +1,7 @@
|
|||||||
import { createSafeActionClient } from "next-safe-action";
|
import { createSafeActionClient } from "next-safe-action";
|
||||||
|
|
||||||
export const action = createSafeActionClient();
|
export const action = createSafeActionClient({
|
||||||
|
handleReturnedServerError(error) {
|
||||||
|
return error.message;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|||||||
@@ -93,5 +93,11 @@ export type Prettify<T> = {
|
|||||||
[K in keyof T]: T[K];
|
[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> =
|
export type ExtractSafeActionResult<T> =
|
||||||
T extends SafeAction<infer Schema, infer Result> ? Result : never;
|
T extends SafeAction<infer Schema, infer Result> ? Result : never;
|
||||||
|
|||||||
Reference in New Issue
Block a user