mirror of
https://github.com/overleaf/overleaf.git
synced 2025-04-14 17:33:34 +00:00
Merge pull request #7509 from overleaf/ii-7155-display-institution
Display the institution and role if applicable GitOrigin-RevId: e00b07f0118e7f3ab8ec0b0f01b2b3e52fcc1cd2
This commit is contained in:
parent
dc706b4942
commit
e0d5cf4b42
11 changed files with 207 additions and 15 deletions
services/web
frontend
js/features/settings/components/emails
stories
test/frontend/features/settings/components/emails
types
|
@ -29,7 +29,7 @@ function Email({ userEmailData }: EmailProps) {
|
|||
)}
|
||||
{userEmailData.confirmedAt &&
|
||||
userEmailData.affiliation?.institution.confirmed &&
|
||||
userEmailData.affiliation?.licence !== 'free' && (
|
||||
userEmailData.affiliation.licence !== 'free' && (
|
||||
<div className="small">
|
||||
<span className="label label-primary">{t('professional')}</span>
|
||||
</div>
|
||||
|
|
|
@ -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 (
|
||||
<>
|
||||
<div>{affiliation.institution.name}</div>
|
||||
{!affiliation.department && !affiliation.role && (
|
||||
<div className="small">
|
||||
<Button className="btn-inline-link" onClick={handleAddRoleDepartment}>
|
||||
{t('add_role_and_department')}
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
{(affiliation.role || affiliation.department) && (
|
||||
<div className="small">
|
||||
{[affiliation.role, affiliation.department]
|
||||
.filter(Boolean)
|
||||
.join(', ')}
|
||||
<br />
|
||||
<Button className="btn-inline-link" onClick={handleChangeAffiliation}>
|
||||
{t('change')}
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default InstitutionAndRole
|
|
@ -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 (
|
||||
<>
|
||||
<button
|
||||
className="btn btn-inline-link"
|
||||
<Button
|
||||
className="btn-inline-link"
|
||||
onClick={handleResendConfirmationEmail}
|
||||
>
|
||||
{t('resend_confirmation_email')}
|
||||
</button>
|
||||
</Button>
|
||||
<br />
|
||||
{isError && (
|
||||
<span className="text-danger">
|
||||
|
|
|
@ -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) {
|
|||
</EmailCell>
|
||||
</Col>
|
||||
<Col sm={5}>
|
||||
<EmailCell>todo</EmailCell>
|
||||
{userEmailData.affiliation?.institution && (
|
||||
<EmailCell>
|
||||
<InstitutionAndRole userEmailData={userEmailData} />
|
||||
</EmailCell>
|
||||
)}
|
||||
</Col>
|
||||
<Col sm={2}>
|
||||
<EmailCell>todo</EmailCell>
|
||||
|
|
|
@ -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,
|
||||
},
|
||||
|
|
|
@ -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(<InstitutionAndRole userEmailData={userEmailData} />)
|
||||
|
||||
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(<InstitutionAndRole userEmailData={userEmailData} />)
|
||||
|
||||
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
|
||||
})
|
||||
})
|
|
@ -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('<EmailsSection />', function () {
|
|||
})
|
||||
|
||||
it('renders primary status', function () {
|
||||
window.metaAttributesCache.set('ol-userEmails', fakeUsersData)
|
||||
window.metaAttributesCache.set('ol-userEmails', [professionalUserData])
|
||||
render(<EmailsSection />)
|
||||
|
||||
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 () {
|
||||
|
|
|
@ -1,6 +1,19 @@
|
|||
import { Institution } from './institution'
|
||||
import { Portal } from './portal'
|
||||
import { Nullable } from './utils'
|
||||
|
||||
export type Affiliation = {
|
||||
cachedConfirmedAt: Nullable<string>
|
||||
cachedEntitlement: Nullable<boolean>
|
||||
cachedLastDayToReconfirm: Nullable<string>
|
||||
cachedPastReconfirmDate: boolean
|
||||
cachedReconfirmedAt: Nullable<string>
|
||||
department: Nullable<string>
|
||||
inReconfirmNotificationPeriod: boolean
|
||||
inferred: boolean
|
||||
institution: Institution
|
||||
licence?: 'free' | 'pro_plus'
|
||||
licence: 'free' | 'pro_plus'
|
||||
pastReconfirmDate: boolean
|
||||
portal: Portal
|
||||
role: Nullable<string>
|
||||
}
|
||||
|
|
|
@ -1 +1,12 @@
|
|||
export type Institution = Record<string, unknown>
|
||||
import { Nullable } from './utils'
|
||||
|
||||
export type Institution = {
|
||||
commonsAccount: boolean
|
||||
confirmed: boolean
|
||||
id: number
|
||||
isUniversity: boolean
|
||||
maxConfirmationMonths: Nullable<number>
|
||||
name: string
|
||||
ssoBeta: boolean
|
||||
ssoEnabled: boolean
|
||||
}
|
||||
|
|
4
services/web/types/portal.ts
Normal file
4
services/web/types/portal.ts
Normal file
|
@ -0,0 +1,4 @@
|
|||
export type Portal = {
|
||||
slug: string
|
||||
templates_count: number
|
||||
}
|
|
@ -2,7 +2,7 @@ import { Affiliation } from './affiliation'
|
|||
|
||||
export type UserEmailData = {
|
||||
affiliation?: Affiliation
|
||||
confirmedAt: string
|
||||
confirmedAt?: string
|
||||
email: string
|
||||
default: boolean
|
||||
ssoAvailable?: boolean
|
||||
|
|
Loading…
Add table
Reference in a new issue