diff --git a/services/web/frontend/js/features/settings/components/emails/email.tsx b/services/web/frontend/js/features/settings/components/emails/email.tsx
index e895e484f7..63e2d69cea 100644
--- a/services/web/frontend/js/features/settings/components/emails/email.tsx
+++ b/services/web/frontend/js/features/settings/components/emails/email.tsx
@@ -29,7 +29,7 @@ function Email({ userEmailData }: EmailProps) {
)}
{userEmailData.confirmedAt &&
userEmailData.affiliation?.institution.confirmed &&
- userEmailData.affiliation?.licence !== 'free' && (
+ userEmailData.affiliation.licence !== 'free' && (
{t('professional')}
diff --git a/services/web/frontend/js/features/settings/components/emails/institution-and-role.tsx b/services/web/frontend/js/features/settings/components/emails/institution-and-role.tsx
new file mode 100644
index 0000000000..4ff2672a6d
--- /dev/null
+++ b/services/web/frontend/js/features/settings/components/emails/institution-and-role.tsx
@@ -0,0 +1,50 @@
+import { useTranslation } from 'react-i18next'
+import { UserEmailData } from '../../../../../../types/user-email'
+import { Button } from 'react-bootstrap'
+
+type InstitutionAndRoleProps = {
+ userEmailData: UserEmailData
+}
+
+function InstitutionAndRole({ userEmailData }: InstitutionAndRoleProps) {
+ const { t } = useTranslation()
+ const { affiliation } = userEmailData
+
+ const handleAddRoleDepartment = () => {
+ console.log('TODO: add role department')
+ }
+
+ const handleChangeAffiliation = () => {
+ console.log('TODO: change affiliation')
+ }
+
+ if (!affiliation?.institution) {
+ return null
+ }
+
+ return (
+ <>
+ {affiliation.institution.name}
+ {!affiliation.department && !affiliation.role && (
+
+
+
+ )}
+ {(affiliation.role || affiliation.department) && (
+
+ {[affiliation.role, affiliation.department]
+ .filter(Boolean)
+ .join(', ')}
+
+
+
+ )}
+ >
+ )
+}
+
+export default InstitutionAndRole
diff --git a/services/web/frontend/js/features/settings/components/emails/resend-confirmation-email-button.tsx b/services/web/frontend/js/features/settings/components/emails/resend-confirmation-email-button.tsx
index 9cde7bb097..b5a1808f5a 100644
--- a/services/web/frontend/js/features/settings/components/emails/resend-confirmation-email-button.tsx
+++ b/services/web/frontend/js/features/settings/components/emails/resend-confirmation-email-button.tsx
@@ -1,6 +1,7 @@
import { useEffect } from 'react'
import { useTranslation } from 'react-i18next'
import Icon from '../../../../shared/components/icon'
+import { Button } from 'react-bootstrap'
import useAsync from '../../../../shared/hooks/use-async'
import { postJSON } from '../../../../infrastructure/fetch-json'
import { UserEmailData } from '../../../../../../types/user-email'
@@ -42,12 +43,12 @@ function ResendConfirmationEmailButton({
return (
<>
-
+
{isError && (
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 791c9bc8f0..e092dbd100 100644
--- a/services/web/frontend/js/features/settings/components/emails/row.tsx
+++ b/services/web/frontend/js/features/settings/components/emails/row.tsx
@@ -1,6 +1,7 @@
import { UserEmailData } from '../../../../../../types/user-email'
import { Row, Col } from 'react-bootstrap'
import Email from './email'
+import InstitutionAndRole from './institution-and-role'
import EmailCell from './cell'
type EmailsRowProps = {
@@ -16,7 +17,11 @@ function EmailsRow({ userEmailData }: EmailsRowProps) {
- todo
+ {userEmailData.affiliation?.institution && (
+
+
+
+ )}
todo
diff --git a/services/web/frontend/stories/settings.stories.js b/services/web/frontend/stories/settings.stories.js
index 23e6117a34..ede3090288 100644
--- a/services/web/frontend/stories/settings.stories.js
+++ b/services/web/frontend/stories/settings.stories.js
@@ -21,6 +21,7 @@ const fakeUsersData = [
affiliation: {
institution: {
confirmed: true,
+ name: 'Overleaf',
},
licence: 'pro_plus',
},
@@ -34,6 +35,15 @@ const fakeUsersData = [
default: false,
},
{
+ affiliation: {
+ institution: {
+ confirmed: true,
+ name: 'Overleaf',
+ },
+ licence: 'pro_plus',
+ department: 'Art & Art History',
+ role: 'Reader',
+ },
email: 'baz@overleaf.com',
default: false,
},
diff --git a/services/web/test/frontend/features/settings/components/emails/emails-section-institution-and-role.test.tsx b/services/web/test/frontend/features/settings/components/emails/emails-section-institution-and-role.test.tsx
new file mode 100644
index 0000000000..ecfbe25652
--- /dev/null
+++ b/services/web/test/frontend/features/settings/components/emails/emails-section-institution-and-role.test.tsx
@@ -0,0 +1,84 @@
+import { render, screen } from '@testing-library/react'
+import { expect } from 'chai'
+import { UserEmailData } from '../../../../../../types/user-email'
+import InstitutionAndRole from '../../../../../../frontend/js/features/settings/components/emails/institution-and-role'
+
+const userData1: UserEmailData = {
+ affiliation: {
+ cachedConfirmedAt: null,
+ cachedPastReconfirmDate: false,
+ cachedReconfirmedAt: null,
+ department: null,
+ institution: {
+ commonsAccount: false,
+ confirmed: true,
+ id: 1,
+ isUniversity: false,
+ name: 'Overleaf',
+ ssoEnabled: false,
+ ssoBeta: false,
+ },
+ inReconfirmNotificationPeriod: false,
+ inferred: false,
+ licence: 'pro_plus',
+ pastReconfirmDate: false,
+ portal: { slug: '', templates_count: 1 },
+ role: null,
+ },
+ confirmedAt: '2022-03-09T10:59:44.139Z',
+ email: 'foo@overleaf.com',
+ default: true,
+}
+
+const userData2: UserEmailData = {
+ affiliation: {
+ cachedConfirmedAt: null,
+ cachedPastReconfirmDate: false,
+ cachedReconfirmedAt: null,
+ department: 'Art History',
+ institution: {
+ commonsAccount: false,
+ confirmed: true,
+ id: 1,
+ isUniversity: false,
+ name: 'Overleaf',
+ ssoEnabled: false,
+ ssoBeta: false,
+ },
+ inReconfirmNotificationPeriod: false,
+ inferred: false,
+ licence: 'pro_plus',
+ pastReconfirmDate: false,
+ portal: { slug: '', templates_count: 1 },
+ role: 'Reader',
+ },
+ email: 'baz@overleaf.com',
+ default: false,
+}
+
+describe('user role and institution', function () {
+ it('renders affiliation name with add role/department button', function () {
+ const userEmailData = userData1
+ render()
+
+ screen.getByText(userEmailData.affiliation.institution.name, {
+ exact: false,
+ })
+ screen.getByRole('button', { name: /add role and department/i })
+ expect(screen.queryByRole('button', { name: /change/i })).to.not.exist
+ })
+
+ it('renders affiliation name, role and department with change button', function () {
+ const userEmailData = userData2
+ render()
+
+ screen.getByText(userEmailData.affiliation.institution.name, {
+ exact: false,
+ })
+ screen.getByText(userEmailData.affiliation.department, { exact: false })
+ screen.getByText(userEmailData.affiliation.role, { exact: false })
+ screen.getByRole('button', { name: /change/i })
+ expect(screen.queryByRole('button', { name: /add role and department/i }))
+ .to.not.exist
+ })
+})
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 c62455ddb5..c96828797e 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
@@ -8,24 +8,40 @@ import {
import EmailsSection from '../../../../../../frontend/js/features/settings/components/emails-section'
import { expect } from 'chai'
import fetchMock from 'fetch-mock'
+import { UserEmailData } from '../../../../../../types/user-email'
-const confirmedUserData = {
+const confirmedUserData: UserEmailData = {
confirmedAt: '2022-03-10T10:59:44.139Z',
email: 'bar@overleaf.com',
default: false,
}
-const unconfirmedUserData = {
+const unconfirmedUserData: UserEmailData = {
email: 'baz@overleaf.com',
default: false,
}
-const professionalUserData = {
+const professionalUserData: UserEmailData = {
affiliation: {
+ cachedConfirmedAt: null,
+ cachedPastReconfirmDate: false,
+ cachedReconfirmedAt: null,
+ department: 'Art History',
institution: {
+ commonsAccount: false,
confirmed: true,
+ id: 1,
+ isUniversity: false,
+ name: 'Overleaf',
+ ssoEnabled: false,
+ ssoBeta: false,
},
+ inReconfirmNotificationPeriod: false,
+ inferred: false,
licence: 'pro_plus',
+ pastReconfirmDate: false,
+ portal: { slug: '', templates_count: 1 },
+ role: 'Reader',
},
confirmedAt: '2022-03-09T10:59:44.139Z',
email: 'foo@overleaf.com',
@@ -69,12 +85,10 @@ describe('', function () {
})
it('renders primary status', function () {
- window.metaAttributesCache.set('ol-userEmails', fakeUsersData)
+ window.metaAttributesCache.set('ol-userEmails', [professionalUserData])
render()
- const primary = fakeUsersData.find(userData => userData.default)
-
- screen.getByText(`${primary.email} (primary)`)
+ screen.getByText(`${professionalUserData.email} (primary)`)
})
it('shows confirmation status for unconfirmed users', function () {
diff --git a/services/web/types/affiliation.ts b/services/web/types/affiliation.ts
index 5b76d77494..d60aa25ba1 100644
--- a/services/web/types/affiliation.ts
+++ b/services/web/types/affiliation.ts
@@ -1,6 +1,19 @@
import { Institution } from './institution'
+import { Portal } from './portal'
+import { Nullable } from './utils'
export type Affiliation = {
+ cachedConfirmedAt: Nullable
+ cachedEntitlement: Nullable
+ cachedLastDayToReconfirm: Nullable
+ cachedPastReconfirmDate: boolean
+ cachedReconfirmedAt: Nullable
+ department: Nullable
+ inReconfirmNotificationPeriod: boolean
+ inferred: boolean
institution: Institution
- licence?: 'free' | 'pro_plus'
+ licence: 'free' | 'pro_plus'
+ pastReconfirmDate: boolean
+ portal: Portal
+ role: Nullable
}
diff --git a/services/web/types/institution.ts b/services/web/types/institution.ts
index f857032b17..4edba50eb9 100644
--- a/services/web/types/institution.ts
+++ b/services/web/types/institution.ts
@@ -1 +1,12 @@
-export type Institution = Record
+import { Nullable } from './utils'
+
+export type Institution = {
+ commonsAccount: boolean
+ confirmed: boolean
+ id: number
+ isUniversity: boolean
+ maxConfirmationMonths: Nullable
+ name: string
+ ssoBeta: boolean
+ ssoEnabled: boolean
+}
diff --git a/services/web/types/portal.ts b/services/web/types/portal.ts
new file mode 100644
index 0000000000..ef8d2eca4d
--- /dev/null
+++ b/services/web/types/portal.ts
@@ -0,0 +1,4 @@
+export type Portal = {
+ slug: string
+ templates_count: number
+}
diff --git a/services/web/types/user-email.ts b/services/web/types/user-email.ts
index b6ae8b8d42..2e95af337e 100644
--- a/services/web/types/user-email.ts
+++ b/services/web/types/user-email.ts
@@ -2,7 +2,7 @@ import { Affiliation } from './affiliation'
export type UserEmailData = {
affiliation?: Affiliation
- confirmedAt: string
+ confirmedAt?: string
email: string
default: boolean
ssoAvailable?: boolean