diff --git a/services/web/frontend/js/features/settings/components/emails-section.tsx b/services/web/frontend/js/features/settings/components/emails-section.tsx
index a8bc54b017..e58dcd213a 100644
--- a/services/web/frontend/js/features/settings/components/emails-section.tsx
+++ b/services/web/frontend/js/features/settings/components/emails-section.tsx
@@ -26,6 +26,20 @@ function EmailsSectionContent() {
// Only show the "add email" button if the user has permission to add a secondary email
const hideAddSecondaryEmail = getMeta('ol-cannot-add-secondary-email')
+ // Sort emails: primary first, then confirmed secondary emails, then unconfirmed secondary emails
+ const sortedUserEmails = [...userEmails].sort((a, b) => {
+ // Primary email comes first
+ if (a.default) return -1
+ if (b.default) return 1
+
+ // Then sort by confirmation status
+ if (a.confirmedAt && !b.confirmedAt) return -1
+ if (!a.confirmedAt && b.confirmedAt) return 1
+
+ // If both have the same status, sort by email string
+ return a.email.localeCompare(b.email)
+ })
+
return (
<>
{t('emails_and_affiliations_title')}
@@ -54,7 +68,7 @@ function EmailsSectionContent() {
) : (
<>
- {userEmails?.map(userEmail => (
+ {sortedUserEmails.map(userEmail => (
diff --git a/services/web/frontend/js/features/settings/components/emails/row.tsx b/services/web/frontend/js/features/settings/components/emails/row.tsx
index fd74281ad8..3cbb84ef9b 100644
--- a/services/web/frontend/js/features/settings/components/emails/row.tsx
+++ b/services/web/frontend/js/features/settings/components/emails/row.tsx
@@ -28,7 +28,7 @@ function EmailsRow({ userEmailData, primary }: EmailsRowProps) {
return (
<>
-
+
diff --git a/services/web/test/frontend/features/settings/components/emails/emails-section.test.tsx b/services/web/test/frontend/features/settings/components/emails/emails-section.test.tsx
index 243d85533a..e784f6aaac 100644
--- a/services/web/test/frontend/features/settings/components/emails/emails-section.test.tsx
+++ b/services/web/test/frontend/features/settings/components/emails/emails-section.test.tsx
@@ -163,4 +163,56 @@ describe('', function () {
screen.getByText(/sorry, something went wrong/i)
screen.getByRole('button', { name: /resend confirmation code/i })
})
+
+ it('sorts emails with primary first, then confirmed, then unconfirmed', async function () {
+ const unconfirmedEmail = { ...unconfirmedUserData, email: 'b@example.com' }
+ const unconfirmedEmailTwo = {
+ ...unconfirmedUserData,
+ email: 'd@example.com',
+ }
+ const confirmedEmail = {
+ ...confirmedUserData,
+ email: 'a@example.com',
+ confirmedAt: new Date().toISOString(),
+ }
+ const confirmedEmailTwo = {
+ ...confirmedUserData,
+ email: 'e@example.com',
+ confirmedAt: new Date().toISOString(),
+ }
+ const primaryEmail = {
+ ...professionalUserData,
+ email: 'c@example.com',
+ default: true,
+ }
+
+ const emails = [
+ confirmedEmailTwo,
+ unconfirmedEmailTwo,
+ unconfirmedEmail,
+ confirmedEmail,
+ primaryEmail,
+ ]
+
+ fetchMock.get('/user/emails?ensureAffiliation=true', emails)
+ render()
+
+ await waitForElementToBeRemoved(() => screen.getByText(/loading/i))
+
+ const emailElements = screen.getAllByTestId(/email-row/i)
+
+ // Primary should be first regardless of alphabetical order
+ expect(within(emailElements[0]).getByText('c@example.com')).to.exist
+ expect(within(emailElements[0]).getByText('Primary')).to.exist
+
+ // Confirmed should be second in alphabetical order
+ expect(within(emailElements[1]).getByText('a@example.com')).to.exist
+ expect(within(emailElements[2]).getByText('e@example.com')).to.exist
+
+ // Unconfirmed should be last in alphabetical order
+ expect(within(emailElements[3]).getByText('b@example.com')).to.exist
+ expect(within(emailElements[3]).getByText(/unconfirmed/i)).to.exist
+ expect(within(emailElements[4]).getByText('d@example.com')).to.exist
+ expect(within(emailElements[4]).getByText(/unconfirmed/i)).to.exist
+ })
})