diff --git a/jsapp/js/account/security/email/emailSection.api.ts b/jsapp/js/account/security/email/emailSection.api.ts index eff4a78abe..99f91a4728 100644 --- a/jsapp/js/account/security/email/emailSection.api.ts +++ b/jsapp/js/account/security/email/emailSection.api.ts @@ -1,5 +1,5 @@ import { fetchDelete, fetchGet, fetchPost } from '#/api' -import type { PaginatedResponse } from '#/dataInterface' +import type { FailResponse, PaginatedResponse } from '#/dataInterface' export interface EmailResponse { primary: boolean @@ -14,7 +14,7 @@ export async function getUserEmails() { } export async function setUserEmail(newEmail: string) { - return fetchPost(LIST_URL, { email: newEmail }) + return fetchPost(LIST_URL, { email: newEmail }) } /** Removes all unverified/non-primary emails (there should only be one anyway)*/ diff --git a/jsapp/js/account/security/email/emailSection.component.tsx b/jsapp/js/account/security/email/emailSection.component.tsx index 18ea8bb26d..229080814d 100644 --- a/jsapp/js/account/security/email/emailSection.component.tsx +++ b/jsapp/js/account/security/email/emailSection.component.tsx @@ -1,5 +1,6 @@ import React, { useEffect, useState } from 'react' +import { Group, Text } from '@mantine/core' import cx from 'classnames' import securityStyles from '#/account/security/securityRoute.module.scss' import { MemberRoleEnum } from '#/api/models/memberRoleEnum' @@ -7,6 +8,7 @@ import { useOrganizationAssumed } from '#/api/useOrganizationAssumed' import Button from '#/components/common/button' import Icon from '#/components/common/icon' import TextBox from '#/components/common/textBox' +import type { FailResponse } from '#/dataInterface' import sessionStore from '#/stores/session' import { formatTime, notify } from '#/utils' import { deleteUnverifiedUserEmails, getUserEmails, setUserEmail } from './emailSection.api' @@ -18,6 +20,7 @@ interface EmailState { newEmail: string refreshedEmail: boolean refreshedEmailDate: string + fieldErrors: string[] } export default function EmailSection() { @@ -35,6 +38,7 @@ export default function EmailSection() { newEmail: initialEmail, refreshedEmail: false, refreshedEmailDate: '', + fieldErrors: [], }) useEffect(() => { @@ -47,18 +51,42 @@ export default function EmailSection() { }, []) function setNewUserEmail(newEmail: string) { + // Clear errors before making an API call + setEmail({ + ...email, + fieldErrors: [], + }) + setUserEmail(newEmail).then( - () => { - getUserEmails().then((data) => { + (response) => { + if ('primary' in response) { + getUserEmails().then((data) => { + setEmail({ + ...email, + emails: data.results, + newEmail: '', + }) + }) + } else { + // If there is no primary email in response, we display an error. This can happen for example when user has + // been created through Django admin without email address. setEmail({ ...email, - emails: data.results, - newEmail: '', + fieldErrors: ['Primary email missing'], }) - }) + } }, - () => { - /* Avoid crashing app when 500 error happens */ + (err: FailResponse) => { + let errMessages: string[] = [] + if (err.responseJSON?.email && typeof err.responseJSON.email === 'string') { + errMessages.push(err.responseJSON.email) + } else if (Array.isArray(err.responseJSON?.email)) { + errMessages = err.responseJSON.email + } + setEmail({ + ...email, + fieldErrors: errMessages, + }) }, ) } @@ -181,7 +209,11 @@ export default function EmailSection() { handleSubmit() }} > -