Преглед изворни кода

Merge pull request #7509 from overleaf/ii-7155-display-institution

Display the institution and role if applicable

GitOrigin-RevId: e00b07f0118e7f3ab8ec0b0f01b2b3e52fcc1cd2
Tim Down пре 4 година
родитељ
комит
e0d5cf4b42

+ 1 - 1
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' && (
           <div className="small">
             <span className="label label-primary">{t('professional')}</span>
           </div>

+ 50 - 0
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 (
+    <>
+      <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

+ 4 - 3
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 (
     <>
-      <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">

+ 6 - 1
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) {
         </EmailCell>
       </Col>
       <Col sm={5}>
-        <EmailCell>todo</EmailCell>
+        {userEmailData.affiliation?.institution && (
+          <EmailCell>
+            <InstitutionAndRole userEmailData={userEmailData} />
+          </EmailCell>
+        )}
       </Col>
       <Col sm={2}>
         <EmailCell>todo</EmailCell>

+ 10 - 0
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,
   },

+ 84 - 0
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(<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
+  })
+})

+ 21 - 7
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('<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 () {

+ 14 - 1
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<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>
 }

+ 12 - 1
services/web/types/institution.ts

@@ -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 - 0
services/web/types/portal.ts

@@ -0,0 +1,4 @@
+export type Portal = {
+  slug: string
+  templates_count: number
+}

+ 1 - 1
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