diff --git a/frontend/public/locales/en/common.json b/frontend/public/locales/en/common.json index 611406464b289565a84927754557afb94dcabe13..acc8e5609834491a6cc818257eea919dc658f7fc 100644 --- a/frontend/public/locales/en/common.json +++ b/frontend/public/locales/en/common.json @@ -157,5 +157,14 @@ "genericServerErrorBody": "The server reported:<1>{{errorBodyText}}</1>", "contactHelp": "Contact help through the link in the footer if the problem persists.", "unknown": "An unknown error has occurred. If the problem persists, contact support." + "contactHelp": "Contact help through the link in the footer if the problem persists.", + "invitationDataFetchFailed": "Failed to fetch invitation data", + "guestRegistrationFailed": "Failed to register your data", + "codes": { + "invalid_invite": "Invalid invite", + "invite_expired": "Invite has expired", + "cannot_update_fields": "Failed to update data", + "update_national_id_not_allowed": "Not allowed to update verified national ID" + } } } diff --git a/frontend/public/locales/nb/common.json b/frontend/public/locales/nb/common.json index b9f2d0a7e70dd9bc4e59fd1f66e55eb4857b644a..b776716aa1d77af9ef3b8f731af98b3dc18c4005 100644 --- a/frontend/public/locales/nb/common.json +++ b/frontend/public/locales/nb/common.json @@ -157,5 +157,14 @@ "genericServerErrorBody": "Respons fra server:<1>{{errorBodyText}}</1>", "contactHelp": "Kontakt hjelp via link i footer om problemet vedvarer.", "unknown": "En ukjent feil har oppstått. Om problemet vedvarer, kontakt brukerstøtte." + "contactHelp": "Kontakt hjelp via link i footer om problemet vedvarer.", + "invitationDataFetchFailed": "Klarte ikke å hente invitasjonsdata", + "guestRegistrationFailed": "Klarte ikke å registrere dataene dine", + "codes": { + "invalid_invite": "Ugyldig invitasjon", + "invite_expired": "Invitasjonen har utløpt", + "cannot_update_fields": "Klarte ikke å oppdatere dataene", + "update_national_id_not_allowed": "Ikke tillatt å endre verifisert fødselsnummer/D-nummer" + } } } diff --git a/frontend/public/locales/nn/common.json b/frontend/public/locales/nn/common.json index 60fc0cc01df242b2171d6885213c7bfe3bc8c53c..0940062bf04fa91ed59ced5066f4b4c110a8f299 100644 --- a/frontend/public/locales/nn/common.json +++ b/frontend/public/locales/nn/common.json @@ -158,5 +158,14 @@ "genericServerErrorBody": "Respons frå server:<1>{{errorBodyText}}</1>", "contactHelp": "Kontakt hjelp via link i footer om problemet vedvarer.", "unknown": "Ein uventa feil oppstod. Om problemet varer ved, kontakt brukarstøtte." + "contactHelp": "Kontakt hjelp via link i footer om problemet vedvarer.", + "invitationDataFetchFailed": "Klarte ikkje å hente invitasjonsdata", + "guestRegistrationFailed": "Klarte ikkje å registrere dataene dine", + "codes": { + "invalid_invite": "Ugyldig invitasjon", + "invite_expired": "Invitasjonen har utløpe", + "cannot_update_fields": "Klarte ikkje å oppdatere dataene", + "update_national_id_not_allowed": "Ikkje tillete å endre verifisert fødselsnummer/D-nummer" + } } } diff --git a/frontend/src/components/errorReport/index.tsx b/frontend/src/components/errorReport/index.tsx index 4aace3fff5168fe2edd63667db6240d5fc2420dc..32d0f67fe1f4aadb1f5c03b69cd87000a2361753 100644 --- a/frontend/src/components/errorReport/index.tsx +++ b/frontend/src/components/errorReport/index.tsx @@ -2,7 +2,7 @@ import { Alert, AlertTitle } from '@mui/material' import { Trans, useTranslation } from 'react-i18next' import React from 'react' -interface ErrorReportProps { +export interface ErrorReportProps { errorHeading: string statusCode?: number statusText?: string diff --git a/frontend/src/routes/guest/register/index.tsx b/frontend/src/routes/guest/register/index.tsx index c595bac7361717600f2670694d96eec2852f3782..7cf5a5033a4861a9c9a7dad0d09f758e0297b87d 100644 --- a/frontend/src/routes/guest/register/index.tsx +++ b/frontend/src/routes/guest/register/index.tsx @@ -21,6 +21,9 @@ import AuthenticationMethod from './authenticationMethod' import GuestRegisterStep from './steps/register' import GuestConsentStep from './steps/consent' import GuestSuccessStep from './steps/success' +import ServerErrorReport, { + ErrorReportProps, +} from '../../../components/errorReport' enum SubmitState { NotSubmitted, @@ -87,12 +90,37 @@ export default function GuestRegister() { // eslint-disable-next-line @typescript-eslint/no-unused-vars const [guestConsentData, setGuestConsentData] = useState<GuestConsentData | null>(null) + const [fetchInvitationDataError, setFetchInvitationDataError] = + useState<ErrorReportProps | null>(null) const fetchInvitationData = async () => { const response = await fetch('/api/ui/v1/invited/', fetchJsonOpts()) if (!response.ok) { + try { + // Expect that the error will contain some JSON-data in the body + const errorData = await response.json() + setFetchInvitationDataError({ + errorHeading: t('error.invitationDataFetchFailed'), + statusCode: response.status, + statusText: response.statusText, + errorBodyText: t(`error.codes.${errorData?.code}`), + }) + } catch (e: unknown) { + console.error(e) + + // Probably some unknown data in the body, create an error message + // using the rest of the information in the response + setFetchInvitationDataError({ + errorHeading: t('error.invitationDataFetchFailed'), + statusCode: response.status, + statusText: response.statusText, + errorBodyText: undefined, + }) + } + return } + setFetchInvitationDataError(null) const data: InvitationData = await response.json() const authenticationMethod = @@ -260,8 +288,32 @@ export default function GuestRegister() { setSubmitState(SubmitState.Submitted) setActiveStep(Step.SuccessStep) } else { - setSubmitState(SubmitState.SubmittedError) - console.error(`Server responded with status: ${response.status}`) + // Expect that the error will contain some JSON-data in the body + response + .json() + .then((errorData) => { + setFetchInvitationDataError({ + errorHeading: t('error.guestRegistrationFailed'), + statusCode: response.status, + statusText: response.statusText, + errorBodyText: t(`error.codes.${errorData?.code}`), + }) + }) + .catch((error) => { + console.error(error) + + // Probably some unknown data in the body, create an error message + // using the rest of the information in the response + setFetchInvitationDataError({ + errorHeading: t('error.guestRegistrationFailed'), + statusCode: response.status, + statusText: response.statusText, + errorBodyText: undefined, + }) + }) + .finally(() => { + setSubmitState(SubmitState.SubmittedError) + }) } }) .catch((error) => { @@ -372,6 +424,15 @@ export default function GuestRegister() { </Button> )} </Box> + + {fetchInvitationDataError !== null && ( + <ServerErrorReport + errorHeading={fetchInvitationDataError?.errorHeading} + statusCode={fetchInvitationDataError?.statusCode} + statusText={fetchInvitationDataError?.statusText} + errorBodyText={fetchInvitationDataError?.errorBodyText} + /> + )} </Page> ) } diff --git a/gregui/api/views/invitation.py b/gregui/api/views/invitation.py index 34b6d4fa79bc078e488b1f6e5b70f176761b6da6..61c77af66dd9eb22016b00cef539eaa94988631e 100644 --- a/gregui/api/views/invitation.py +++ b/gregui/api/views/invitation.py @@ -196,9 +196,15 @@ class InvitedGuestView(GenericAPIView): try: invite_link = InvitationLink.objects.get(uuid=invite_id) except (InvitationLink.DoesNotExist, exceptions.ValidationError): - return Response(status=status.HTTP_403_FORBIDDEN) + return Response( + status=status.HTTP_403_FORBIDDEN, + data={"code": "invalid_invite", "message": "Invalid invite"}, + ) if invite_link.expire <= timezone.now(): - return Response(status=status.HTTP_403_FORBIDDEN) + return Response( + status=status.HTTP_403_FORBIDDEN, + data={"code": "invite_expired", "message": "Invite expired"}, + ) # if invite_id: invite_link = InvitationLink.objects.get(uuid=invite_id) @@ -275,12 +281,21 @@ class InvitedGuestView(GenericAPIView): if illegal_fields: return Response( status=status.HTTP_400_BAD_REQUEST, - data={"error": {"cannot_update_fields": illegal_fields}}, + data={ + "code": "cannot_update_fields", + "message": f"cannot_update_fields: {illegal_fields}", + }, ) if self._verified_fnr_already_exists(person) and fnr: # The user should not be allowed to change a verified fnr - return Response(status=status.HTTP_400_BAD_REQUEST) + return Response( + status=status.HTTP_400_BAD_REQUEST, + data={ + "code": "update_national_id_not_allowed", + "message": "Not allowed to update verified national ID", + }, + ) with transaction.atomic(): # Note this only serializes data for the person, it does not look at other sections