Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add technical error page #71

Merged
merged 6 commits into from
Oct 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions src/components/EmailForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,13 @@ export const EmailForm = ({ surveyId }: { surveyId: string }) => {
<div className="fr-grid-row fr-grid-row--center fr-py-md-7w fr-py-3w">
<div className="fr-col-11 fr-col-md-9 ">
{unknownEmail ? (
<UnknownEmailForm questioningUrl={questioningUrl} />
<UnknownEmailForm questioningUrl={questioningUrl} surveyId={surveyId} />
) : (
<KnownEmailForm questioningUrl={questioningUrl} email={emailData.mail!} />
<KnownEmailForm
questioningUrl={questioningUrl}
surveyId={surveyId}
email={emailData.mail!}
/>
)}
<Divider orientation="horizontal" variant="fullWidth" className="fr-p-0 fr-my-3w" />
<p>{t("contactDetailsInformation")}</p>
Expand Down
9 changes: 8 additions & 1 deletion src/components/KnownEmailForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,15 @@ import { useTranslation } from "i18n/i18n";
import { useEffect } from "react";
import { knownEmailForm } from "types/schemas";
import { EmailInput } from "./UnknownEmailForm";
import { TechnicalError } from "./errorPages/TechnicalError";

export const KnownEmailForm = ({
questioningUrl,
surveyId,
email,
}: {
questioningUrl?: string;
surveyId: string;
email: string;
}) => {
const { t } = useTranslation("EmailForm");
Expand All @@ -20,7 +23,7 @@ export const KnownEmailForm = ({
});
const navigate = useNavigate();

const { mutateAsync, isSuccess } = useFetchMutationPortail("/repondant/mail", "put");
const { mutateAsync, isSuccess, isError } = useFetchMutationPortail("/repondant/mail", "put");

useEffect(() => {
if (isSuccess && questioningUrl) {
Expand All @@ -41,6 +44,10 @@ export const KnownEmailForm = ({
}
});

if (isError) {
return <TechnicalError surveyId={surveyId} />;
}

return (
<div>
<h4>{t("knownEmailFormtitle")}</h4>
Expand Down
30 changes: 25 additions & 5 deletions src/components/Login.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,19 @@ import { ContentSurvey } from "types/ContentSurvey";
import { Navigate } from "@tanstack/react-router";
import { useEffect } from "react";
import { Loading } from "./surveyHomepage/Loading";
import { TechnicalError } from "./errorPages/TechnicalError";
import { Ineligible, Unauthorized } from "./errorPages/ErrorPages";

export const Login = ({ surveyData }: { surveyData: ContentSurvey }) => {
const { data: questioningUrlData, isLoading } = useFetchQueryPortail("/questionnaires-url");
const { data: questioningUrlData, isLoading, error } = useFetchQueryPortail("/questionnaires-url");

useEffect(() => {
if (questioningUrlData && questioningUrlData[0].url && surveyData.verifmail === false) {
if (
questioningUrlData &&
questioningUrlData.length > 0 &&
questioningUrlData[0].url &&
surveyData.verifmail === false
) {
window.location.href = questioningUrlData[0].url;
}
}, [questioningUrlData, surveyData]);
Expand All @@ -17,9 +24,22 @@ export const Login = ({ surveyData }: { surveyData: ContentSurvey }) => {
return <Loading />;
}

if (surveyData.verifmail || surveyData.verifmail === undefined) {
return <Navigate to={"/$survey/repondant/mail"} params={{ survey: surveyData.id }} />;
if (error) {
if (error.status === 401 || error.status === 403 || error.status === 404) {
return <Unauthorized surveyId={surveyData.id} />;
} else {
return <TechnicalError surveyId={surveyData.id} />;
}
}

return <Loading />;
if (
questioningUrlData &&
questioningUrlData.length > 0 &&
questioningUrlData[0].url &&
(surveyData.verifmail || surveyData.verifmail === undefined)
) {
return <Navigate to={"/$survey/repondant/mail"} params={{ survey: surveyData.id }} />;
} else {
return <Ineligible surveyId={surveyData.id} />;
}
};
15 changes: 13 additions & 2 deletions src/components/UnknownEmailForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,21 @@ import { useEffect } from "react";
import { FieldErrors, UseFormRegister } from "react-hook-form";
import { unknownEmailForm } from "types/schemas";
import { Schema, z } from "zod";
import { TechnicalError } from "./errorPages/TechnicalError";

export const UnknownEmailForm = ({ questioningUrl }: { questioningUrl?: string }) => {
export const UnknownEmailForm = ({
questioningUrl,
surveyId,
}: {
questioningUrl?: string;
surveyId: string;
}) => {
const { t } = useTranslation("EmailForm");
const navigate = useNavigate();
const { t: supportFormTranslation } = useTranslation("SupportForm");
const { register, errors, handleSubmit } = useForm(unknownEmailForm);

const { mutateAsync, isSuccess } = useFetchMutationPortail("/repondant/mail", "put");
const { mutateAsync, isSuccess, isError } = useFetchMutationPortail("/repondant/mail", "put");

useEffect(() => {
if (isSuccess && questioningUrl) {
Expand All @@ -32,6 +39,10 @@ export const UnknownEmailForm = ({ questioningUrl }: { questioningUrl?: string }
});
});

if (isError) {
return <TechnicalError surveyId={surveyId} />;
}

return (
<div>
<h4>{t("unknownEmailFormtitle")}</h4>
Expand Down
105 changes: 105 additions & 0 deletions src/components/errorPages/ErrorPages.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import { fr } from "@codegouvfr/react-dsfr";
import { Alert } from "@codegouvfr/react-dsfr/Alert";
import { Breadcrumb } from "@codegouvfr/react-dsfr/Breadcrumb";
import Button from "@codegouvfr/react-dsfr/Button";
import { Loading } from "components/surveyHomepage/Loading";
import { declareComponentKeys, useTranslation } from "i18n/i18n";
import content from "resources/content.json";
import { ContentSurvey } from "types/ContentSurvey";

type Props = {
data: ContentSurvey;
message: string;
};

export const ErrorPage = ({ data, message }: Props) => {
const { t } = useTranslation("ErrorPages");

return (
<div className={"fr-container"}>
<Breadcrumb
currentPageLabel={t("connexion")}
className="fr-mb-0"
homeLinkProps={{
to: "/",
}}
segments={[
{
label: data.titleShort,
linkProps: {
to: "/$survey/introduction",
params: { survey: data.id },
},
},
]}
/>
<div id="content" className="fr-grid-row fr-grid-row--center ">
<div
className="fr-col-md-10 fr-col-lg-6 fr-col-12 fr-mt-3w fr-mb-3w "
style={{
backgroundColor: fr.colors.decisions.background.default.grey.hover,
}}
>
<div className="fr-grid-row fr-grid-row--center fr-py-md-7w fr-py-3w">
<Alert
className="fr-col-11 fr-col-md-8"
description={
<p className="fr-text--sm">
{message}
<br />
{t("alertText")}
<Button
className="fr-pl-1v fr-text--sm"
style={{
padding: 0,
display: "inline",
textDecoration: "underline",
fontWeight: "400",
}}
priority="tertiary no outline"
linkProps={{
to: "/$survey/contacter-assistance",
params: {
survey: data.id,
},
}}
>
{t("contactSupport")}
</Button>
</p>
}
severity="error"
title={t("alertTitle")}
/>
</div>
</div>
</div>
</div>
);
};

export const Ineligible = ({ surveyId }: { surveyId: string }) => {
const data = content.specifique.find(s => s.id === surveyId);
const message = content.generique.content.ineligible.body;
if (!data) {
return <Loading />;
}

return <ErrorPage data={data} message={message} />;
};

export const Unauthorized = ({ surveyId }: { surveyId: string }) => {
const data = content.specifique.find(s => s.id === surveyId);
const message = content.generique.content.unauthorized.body;
if (!data) {
return <Loading />;
}

return <ErrorPage data={data} message={message} />;
};

const { i18n } = declareComponentKeys<"connexion" | "alertTitle" | "contactSupport" | "alertText">()(
"ErrorPages",
);

export type I18n = typeof i18n;
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { fr } from "@codegouvfr/react-dsfr";
import { Button } from "@codegouvfr/react-dsfr/Button";
import { ReactNode } from "@tanstack/react-router";
import { NotFoundIcon } from "assets/NotFound";
import { declareComponentKeys, useTranslation } from "i18n";
import { Helmet } from "react-helmet-async";
Expand All @@ -8,24 +9,45 @@ export const NotFound = () => {
const { t } = useTranslation("NotFound");
const { t: headerTranslation } = useTranslation("Header");

return (
<ErrorPageDescription
titlepage={`${t("title")} - ${headerTranslation("service tagline")}`}
title={t("title")}
errorText={t("error")}
text={t("notFoundText")}
>
<p className="fr-text--sm">{t("verifyUrl")}</p>
<p className="fr-text--sm">{t("goHomeInformation")}</p>
<Button linkProps={{ to: "/" }}>{t("buttonLabel")}</Button>
</ErrorPageDescription>
);
};

type Props = {
children: ReactNode;
titlepage: string;
title: string;
errorText: string;
text: string;
};

export const ErrorPageDescription = ({ children, titlepage, title, errorText, text }: Props) => {
return (
<>
<Helmet>
<title>{`${t("title")} - ${headerTranslation("service tagline")}`}</title>
<title>{titlepage}</title>
</Helmet>
<section className="fr-container">
<div
className={"fr-grid-row fr-grid-row--center fr-grid-row--middle fr-col-12 fr-px-2w fr-py-6w"}
>
<div className={"fr-col-md-6 fr-col-12"}>
<h1>{t("title")}</h1>
<h1>{title}</h1>
<p className="fr-text--sm" style={{ color: fr.colors.decisions.text.mention.grey.default }}>
{t("error")}
{errorText}
</p>
<p className={"fr-text--lead"}>{t("notFoundText")}</p>
<p className="fr-text--sm">{t("verifyUrl")}</p>
<p className="fr-text--sm">{t("goHomeInformation")}</p>
<Button linkProps={{ to: "/" }}>{t("buttonLabel")}</Button>
<p className={"fr-text--lead"}>{text}</p>
{children}
</div>
<div className={"fr-col-3 fr-hidden fr-unhidden-md fr-col-offset-1"}>
<NotFoundIcon />
Expand Down
32 changes: 32 additions & 0 deletions src/components/errorPages/TechnicalError.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { Button } from "@codegouvfr/react-dsfr/Button";
import { declareComponentKeys, useTranslation } from "i18n";
import { ErrorPageDescription } from "./NotFound";

export const TechnicalError = ({ surveyId }: { surveyId: string }) => {
const { t } = useTranslation("TechnicalError");
const { t: headerTranslation } = useTranslation("Header");

return (
<ErrorPageDescription
titlepage={`${t("title")} - ${headerTranslation("service tagline")}`}
title={t("title")}
errorText={t("error")}
text={t("technicalErrorText")}
>
<p className="fr-text--sm fr-mb-0">{t("reloadPageTips")}</p>
<p className="fr-text--sm">{t("supportInformation")}</p>
<Button
priority="secondary"
linkProps={{ to: "/$survey/contacter-assistance", params: { survey: surveyId } }}
>
{t("buttonLabel")}
</Button>
</ErrorPageDescription>
);
};

const { i18n } = declareComponentKeys<
"title" | "error" | "technicalErrorText" | "reloadPageTips" | "supportInformation" | "buttonLabel"
>()("TechnicalError");

export type I18n = typeof i18n;
2 changes: 1 addition & 1 deletion src/hooks/useFetchQuery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ export function useFetchMutationPortail<
});
}

// use only for support and reinit password
// use only for support and reinit password
export function useFetchMutationWithoutAuth<
Path extends APIPathsPortail,
Method extends APIMethodsPortail<Path>,
Expand Down
15 changes: 15 additions & 0 deletions src/i18n/resources/en.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,21 @@ export const translations: Translations<"en"> = {
"The page may no longer be available. In that case, to continue your visit, you can consult our homepage.",
"buttonLabel": "Homepage",
},
TechnicalError: {
"title": "Unexpected Error",
"error": "Error 500",
"technicalErrorText":
"Sorry, the service is experiencing an issue. We are working to resolve it as quickly as possible.",
"reloadPageTips": "Try refreshing the page or try again later.",
"supportInformation": "If you need immediate assistance, please contact us.",
"buttonLabel": "Contact Us",
},
ErrorPages: {
"connexion": "Login",
"alertTitle": "Survey Access Unavailable",
"contactSupport": "contact support",
"alertText": "If you believe this is an error, please",
},
EmailForm: {
"connexion": "Log in",
"contactDetailsInformation":
Expand Down
15 changes: 15 additions & 0 deletions src/i18n/resources/fr.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,21 @@ export const translations: Translations<"fr"> = {
"La page n'est peut-être plus disponible. Dans ce cas, pour continuer votre visite vous pouvez consulter notre page d'accueil.",
"buttonLabel": "Page d'accueil",
},
TechnicalError: {
"title": "Erreur inattendue",
"error": "Erreur 500",
"technicalErrorText":
"Désolé, le service rencontre un problème, nous travaillons pour le résoudre le plus rapidement possible.",
"reloadPageTips": "Essayez de rafraîchir la page ou bien ressayez plus tard.",
"supportInformation": "Si vous avez besoin d’une aide immédiate, merci de nous contacter.",
"buttonLabel": "Contactez-nous",
},
ErrorPages: {
"connexion": "Connexion",
"alertTitle": "Accès à l'enquête impossible",
"contactSupport": "contacter l'assistance",
"alertText": " Si vous pensez qu'il s'agit d'une erreur, veuillez",
},
EmailForm: {
"connexion": "Connexion",
"contactDetailsInformation":
Expand Down
4 changes: 3 additions & 1 deletion src/i18n/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@ export type ComponentKey =
| import("../routes/$survey/documents").I18n
| import("components/surveyHomepage/SupportForm").I18n
| import("components/LegalInformation").I18n
| import("components/NotFound").I18n
| import("components/errorPages/NotFound").I18n
| import("components/errorPages/TechnicalError").I18n
| import("components/errorPages/ErrorPages").I18n
| import("components/EmailForm").I18n
| import("components/Accessibility").I18n;

Expand Down
Loading
Loading