diff --git a/frontend/public/locales/en/common.json b/frontend/public/locales/en/common.json index e54b8d161f9a432fe105913e7a7a7e8ef4a23560..ec71fe0fe7be1cd7f191a75f605637404dd87a1a 100644 --- a/frontend/public/locales/en/common.json +++ b/frontend/public/locales/en/common.json @@ -168,11 +168,13 @@ "unknown": "An unknown error has occurred. If the problem persists, contact support.", "invitationDataFetchFailed": "Failed to fetch invitation data", "guestRegistrationFailed": "Failed to register your data", + "partialSubmitSuccess": "Invite creation partial successful", "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" + "update_national_id_not_allowed": "Not allowed to update verified national ID", + "invite_email_failed": "There was a problem sending the invite e-mail. Contact support." } } } diff --git a/frontend/public/locales/nb/common.json b/frontend/public/locales/nb/common.json index 11be84cd4d97ac445fa925a06f52f5aebfb9be8e..1444a73b11e60f1e42865f38b6bc645161b430cf 100644 --- a/frontend/public/locales/nb/common.json +++ b/frontend/public/locales/nb/common.json @@ -168,11 +168,13 @@ "errorLoadOusRoleType": "Kunne ikke laste organisasjons og/eller rolletype data fra server", "invitationDataFetchFailed": "Klarte ikke å hente invitasjonsdata", "guestRegistrationFailed": "Klarte ikke å registrere dataene dine", + "partialSubmitSuccess": "", "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" + "update_national_id_not_allowed": "Ikke tillatt å endre verifisert fødselsnummer/D-nummer", + "invite_email_failed": "Klarte ikke å sende e-post med invitasjon. Kontakt support." } } } diff --git a/frontend/public/locales/nn/common.json b/frontend/public/locales/nn/common.json index 5e721f5c1b7c48f66b867f1d2432f763c397225e..6ba68cb561c26ad6f9a8d57d2346481286a366db 100644 --- a/frontend/public/locales/nn/common.json +++ b/frontend/public/locales/nn/common.json @@ -173,7 +173,8 @@ "invalid_invite": "Ugyldig invitasjon", "invite_expired": "Invitasjonen har gått ut", "cannot_update_fields": "Klarte ikkje å oppdatere informasjonen din", - "update_national_id_not_allowed": "Ikkje tillate å endre verifisert fødselsnummer/D-nummer" + "update_national_id_not_allowed": "Ikkje tillate å endre verifisert fødselsnummer/D-nummer", + "invite_email_failed": "Klarte ikkje å sende e-post med invitasjon. Kontakt support." } } } diff --git a/frontend/src/routes/sponsor/register/stepRegistration.tsx b/frontend/src/routes/sponsor/register/stepRegistration.tsx index 7e965b8e8532c0a53c36ff8d30ba153093156d37..e23b8f94b400b65c17dd48b57cd41c0454a4b139 100644 --- a/frontend/src/routes/sponsor/register/stepRegistration.tsx +++ b/frontend/src/routes/sponsor/register/stepRegistration.tsx @@ -54,6 +54,8 @@ export default function StepRegistration() { useState<ServerErrorReportData>() const [formDataErrorReport, setFormDataErrorReport] = useState<ServerErrorReportData>() + const [submitPartialSuccess, setSubmitPartialSuccess] = + useState<ServerErrorReportData>() // The organizational unit and role types are not used by this component, but // loading them here anyways to make sure that they can be loaded using the @@ -71,6 +73,62 @@ export default function StepRegistration() { } } + function handleSubmitErrorResponse(res: Response) { + // Try to extract data from body of error message + res + .text() + .then((text) => { + setSubmitState(SubmitState.SubmitFailure) + setSubmitErrorReport({ + errorHeading: t('error.invitationCreationFailedHeader'), + statusCode: res.status, + statusText: res.statusText, + errorBodyText: text, + }) + }) + .catch((error) => { + // Extracting data from body failed, just show an error message with no body text + console.error('error', error) + setSubmitErrorReport({ + errorHeading: t('error.invitationCreationFailedHeader'), + statusCode: res.status, + statusText: res.statusText, + errorBodyText: undefined, + }) + setSubmitState(SubmitState.SubmitFailure) + }) + } + + function handleSubmitOkResponse(res: Response) { + // The invite was created, but there may still be some warning + res + .json() + .then((jsonResponse) => { + // If there is a body then it should have some json-content + console.log('result', jsonResponse) + + if ('code' in jsonResponse) { + // There is something in the body indicating a warning + setSubmitPartialSuccess({ + errorHeading: t('error.partialSubmitSuccess'), + statusCode: undefined, + statusText: undefined, + errorBodyText: t(`error.codes.${jsonResponse.code}`), + }) + } else { + console.log(JSON.stringify(jsonResponse)) + } + }) + .catch((exception) => { + // There was a problem when extracting the body + console.error(exception) + }) + .finally(() => { + setSubmitState(SubmitState.SubmitSuccess) + setActiveStep(Steps.SuccessStep) + }) + } + const registerGuest = () => { const payload = { first_name: formData.first_name, @@ -96,41 +154,12 @@ export default function StepRegistration() { fetch('/api/ui/v1/invite/', submitJsonOpts('POST', payload)) .then((res) => { if (!res.ok) { - // Try to extract data from body of error message - res - .text() - .then((text) => { - setSubmitState(SubmitState.SubmitFailure) - setSubmitErrorReport({ - errorHeading: t('error.invitationCreationFailedHeader'), - statusCode: res.status, - statusText: res.statusText, - errorBodyText: text, - }) - }) - .catch((error) => { - // Extracting data from body failed, just show an error message with no body text - console.error('error', error) - setSubmitErrorReport({ - errorHeading: t('error.invitationCreationFailedHeader'), - statusCode: res.status, - statusText: res.statusText, - errorBodyText: undefined, - }) - setSubmitState(SubmitState.SubmitFailure) - }) + handleSubmitErrorResponse(res) } else { - return res.text() + handleSubmitOkResponse(res) } return null }) - .then((result) => { - if (result !== null) { - console.log('result', result) - setSubmitState(SubmitState.SubmitSuccess) - setActiveStep(Steps.SuccessStep) - } - }) .catch((error) => { console.error('error', error) setSubmitState(SubmitState.SubmitFailure) @@ -259,6 +288,15 @@ export default function StepRegistration() { errorBodyText={formDataErrorReport.errorBodyText} /> )} + + {submitPartialSuccess !== undefined && ( + <ServerErrorReport + errorHeading={submitPartialSuccess.errorHeading} + statusCode={undefined} + statusText={undefined} + errorBodyText={submitPartialSuccess.errorBodyText} + /> + )} </Page> ) } diff --git a/gregui/api/views/invitation.py b/gregui/api/views/invitation.py index 1df8e74ba20219de72d713bdb8483723cc49fdf1..0ef0add688b93277383328a6f6dc792a4bf03d29 100644 --- a/gregui/api/views/invitation.py +++ b/gregui/api/views/invitation.py @@ -75,9 +75,22 @@ class InvitationView(CreateAPIView, DestroyAPIView): invitation__role__person_id=person.id, invitation__role__sponsor_id=sponsor_user.sponsor_id, ): - send_invite_mail(invitationlink) + try: + send_invite_mail(invitationlink) + except Exception as exception: + logger.error("send_invite_mail", exception=exception) + # The invite has been created, so send 201, but include some data saying the + # e-mail was not sent properly + return Response( + status=status.HTTP_201_CREATED, + data={ + "code": "invite_email_failed", + "message": "Failed to send invite e-mail", + }, + ) - return Response(status=status.HTTP_201_CREATED) + # Empty json-body to avoid client complaining about non-json data in response + return Response(status=status.HTTP_201_CREATED, data={}) def delete(self, request, *args, **kwargs) -> Response: try: