فهرست منبع

Merge pull request #7786 from overleaf/ii-departments-override

Override default departments

GitOrigin-RevId: 23061bc8c083bb8099ca62bd0cdb3c796e49979d
ilkin-overleaf 4 سال پیش
والد
کامیت
85f731110c

+ 16 - 3
services/web/frontend/js/features/settings/components/emails/add-email.tsx

@@ -9,8 +9,8 @@ import { AddEmailInput, InstitutionInfo } from './add-email-input'
 import useAsync from '../../../../shared/hooks/use-async'
 import { useUserEmailsContext } from '../../context/user-email-context'
 import { getJSON, postJSON } from '../../../../infrastructure/fetch-json'
-import { defaults as roles } from '../../roles'
-import { defaults as departments } from '../../departments'
+import { defaults as defaultRoles } from '../../roles'
+import { defaults as defaultDepartments } from '../../departments'
 import { University } from '../../../../../../types/university'
 import { CountryCode } from '../../../../../../types/country'
 import { ExposedSettings } from '../../../../../../types/exposed-settings'
@@ -49,6 +49,7 @@ function AddEmail() {
   const [university, setUniversity] = useState('')
   const [role, setRole] = useState('')
   const [department, setDepartment] = useState('')
+  const [departments, setDepartments] = useState(defaultDepartments)
   const [isInstitutionFieldsVisible, setIsInstitutionFieldsVisible] =
     useState(false)
   const [isUniversityDirty, setIsUniversityDirty] = useState(false)
@@ -70,6 +71,18 @@ function AddEmail() {
     }
   }, [setIsUniversityDirty, university])
 
+  useEffect(() => {
+    const selectedKnownUniversity = countryCode
+      ? universities[countryCode]?.find(({ name }) => name === university)
+      : undefined
+
+    if (selectedKnownUniversity && selectedKnownUniversity.departments.length) {
+      setDepartments(selectedKnownUniversity.departments)
+    } else {
+      setDepartments(defaultDepartments)
+    }
+  }, [countryCode, universities, university])
+
   // Fetch country institution
   useEffect(() => {
     // Skip if country not selected or universities for
@@ -222,7 +235,7 @@ function AddEmail() {
                           <>
                             <div className="form-group mb-2">
                               <DownshiftInput
-                                items={roles}
+                                items={defaultRoles}
                                 inputValue={role}
                                 placeholder={t('role')}
                                 label={t('role')}

+ 24 - 4
services/web/frontend/js/features/settings/components/emails/institution-and-role.tsx

@@ -7,9 +7,10 @@ import { useUserEmailsContext } from '../../context/user-email-context'
 import DownshiftInput from './downshift-input'
 import Icon from '../../../../shared/components/icon'
 import useAsync from '../../../../shared/hooks/use-async'
-import { postJSON } from '../../../../infrastructure/fetch-json'
-import { defaults as roles } from '../../roles'
-import { defaults as departments } from '../../departments'
+import { getJSON, postJSON } from '../../../../infrastructure/fetch-json'
+import { defaults as defaultRoles } from '../../roles'
+import { defaults as defaultDepartments } from '../../departments'
+import { University } from '../../../../../../types/university'
 
 type InstitutionAndRoleProps = {
   userEmailData: UserEmailData
@@ -18,6 +19,7 @@ type InstitutionAndRoleProps = {
 function InstitutionAndRole({ userEmailData }: InstitutionAndRoleProps) {
   const { t } = useTranslation()
   const { isLoading, isError, runAsync } = useAsync()
+  const changeAffiliationAsync = useAsync()
   const { affiliation } = userEmailData
   const {
     state,
@@ -27,6 +29,7 @@ function InstitutionAndRole({ userEmailData }: InstitutionAndRoleProps) {
   } = useUserEmailsContext()
   const [role, setRole] = useState(affiliation?.role || '')
   const [department, setDepartment] = useState(affiliation?.department || '')
+  const [departments, setDepartments] = useState(defaultDepartments)
 
   useEffect(() => {
     setUserEmailsContextLoading(isLoading)
@@ -34,6 +37,23 @@ function InstitutionAndRole({ userEmailData }: InstitutionAndRoleProps) {
 
   const handleChangeAffiliation = () => {
     setEmailAffiliationBeingEdited(userEmailData.email)
+
+    if (!affiliation?.institution.id) {
+      return
+    }
+
+    changeAffiliationAsync
+      .runAsync<University>(
+        getJSON(`/institutions/list/${affiliation.institution.id}`)
+      )
+      .then(data => {
+        if (data.departments.length) {
+          setDepartments(data.departments)
+        }
+      })
+      .catch(() => {
+        setDepartments(defaultDepartments)
+      })
   }
 
   const handleCancelAffiliationChange = () => {
@@ -87,7 +107,7 @@ function InstitutionAndRole({ userEmailData }: InstitutionAndRoleProps) {
         <div className="affiliation-change-container small">
           <form onSubmit={handleSubmit}>
             <DownshiftInput
-              items={roles}
+              items={defaultRoles}
               inputValue={role}
               placeholder={t('role')}
               label={t('role')}

+ 27 - 2
services/web/frontend/stories/settings/helpers/emails.js

@@ -5,6 +5,7 @@ const fakeUsersData = [
     affiliation: {
       institution: {
         confirmed: true,
+        id: 1,
         name: 'Overleaf',
       },
       licence: 'pro_plus',
@@ -22,6 +23,7 @@ const fakeUsersData = [
     affiliation: {
       institution: {
         confirmed: true,
+        id: 2,
         name: 'Overleaf',
       },
       licence: 'pro_plus',
@@ -38,8 +40,27 @@ const fakeUsersData = [
 ]
 
 const fakeInstitutions = [
-  { id: 9326, name: 'Unknown', country_code: 'al', departments: [] },
+  {
+    id: 9326,
+    name: 'Unknown',
+    country_code: 'al',
+    departments: ['New department'],
+  },
 ]
+const fakeInstitution = {
+  id: 123,
+  name: 'test',
+  country_code: 'de',
+  departments: [],
+  team_id: null,
+}
+const bazFakeInstitution = {
+  id: 2,
+  name: 'Baz',
+  country_code: 'de',
+  departments: ['Custom department 1', 'Custom department 2'],
+  team_id: null,
+}
 
 const fakeInstitutionDomain = [
   {
@@ -56,7 +77,11 @@ const fakeInstitutionDomain = [
 export function defaultSetupMocks(fetchMock) {
   fetchMock
     .get(/\/user\/emails/, fakeUsersData, { delay: MOCK_DELAY })
-    .get(/\/institutions\/list/, fakeInstitutions, { delay: MOCK_DELAY })
+    .get(/\/institutions\/list\/2/, bazFakeInstitution, { delay: MOCK_DELAY })
+    .get(/\/institutions\/list\/\d+/, fakeInstitution, { delay: MOCK_DELAY })
+    .get(/\/institutions\/list\?country_code=.*/, fakeInstitutions, {
+      delay: MOCK_DELAY,
+    })
     .get(/\/institutions\/domains/, fakeInstitutionDomain)
     .post(/\/user\/emails\/*/, 200, {
       delay: MOCK_DELAY,

+ 16 - 6
services/web/test/frontend/features/settings/components/emails/emails-section-add-new-email.test.tsx

@@ -196,8 +196,9 @@ describe('<EmailsSection />', function () {
     await screen.findByRole('link', { name: 'Link Accounts and Add Email' })
   })
 
-  it('adds new email address with existing institution', async function () {
+  it('adds new email address with existing institution and custom departments', async function () {
     const country = 'Germany'
+    const customDepartment = 'Custom department'
     fetchMock.get('/user/emails?ensureAffiliation=true', [])
     render(<EmailsSection />)
 
@@ -225,7 +226,7 @@ describe('<EmailsSection />', function () {
         id: userEmailData.affiliation.institution.id,
         name: userEmailData.affiliation.institution.name,
         country_code: 'de',
-        departments: [],
+        departments: [customDepartment],
       },
     ])
 
@@ -252,10 +253,19 @@ describe('<EmailsSection />', function () {
     const roleInput = screen.getByRole('textbox', { name: /role/i })
     await userEvent.type(roleInput, userEmailData.affiliation.role)
     const departmentInput = screen.getByRole('textbox', { name: /department/i })
-    await userEvent.type(departmentInput, userEmailData.affiliation.department)
+    await userEvent.click(departmentInput)
+    await userEvent.click(screen.getByText(customDepartment))
+
+    const userEmailDataCopy = {
+      ...userEmailData,
+      affiliation: {
+        ...userEmailData.affiliation,
+        department: customDepartment,
+      },
+    }
 
     fetchMock
-      .get('/user/emails?ensureAffiliation=true', [userEmailData])
+      .get('/user/emails?ensureAffiliation=true', [userEmailDataCopy])
       .post(/\/user\/emails/, 200)
 
     await userEvent.click(
@@ -272,13 +282,13 @@ describe('<EmailsSection />', function () {
         id: userEmailData.affiliation?.institution.id,
       },
       role: userEmailData.affiliation?.role,
-      department: userEmailData.affiliation?.department,
+      department: customDepartment,
     })
 
     screen.getByText(userEmailData.email)
     screen.getByText(userEmailData.affiliation.institution.name)
     screen.getByText(userEmailData.affiliation.role, { exact: false })
-    screen.getByText(userEmailData.affiliation.department, { exact: false })
+    screen.getByText(customDepartment, { exact: false })
   })
 
   it('adds new email address without existing institution', async function () {

+ 30 - 0
services/web/test/frontend/features/settings/components/emails/emails-section-institution-and-role.test.tsx

@@ -111,9 +111,39 @@ describe('user role and institution', function () {
       .to.not.exist
   })
 
+  it('fetches institution data and replaces departments dropdown on add/change', async function () {
+    const userEmailData = userData1
+    fetchMock.get('/user/emails?ensureAffiliation=true', [userEmailData])
+    render(<EmailsSection />)
+
+    await fetchMock.flush(true)
+    fetchMock.reset()
+
+    const fakeDepartment = 'Fake department'
+    const institution = userEmailData.affiliation.institution
+    fetchMock.get(`/institutions/list/${institution.id}`, {
+      id: institution.id,
+      name: institution.name,
+      country_code: 'de',
+      departments: [fakeDepartment],
+    })
+
+    fireEvent.click(
+      screen.getByRole('button', { name: /add role and department/i })
+    )
+
+    await fetchMock.flush(true)
+    fetchMock.reset()
+
+    fireEvent.click(screen.getByRole('textbox', { name: /department/i }))
+
+    screen.getByText(fakeDepartment)
+  })
+
   it('adds new role and department', async function () {
     fetchMock
       .get('/user/emails?ensureAffiliation=true', [userData1])
+      .get(/\/institutions\/list/, { departments: [] })
       .post('/user/emails/endorse', 200)
     render(<EmailsSection />)
 

+ 1 - 1
services/web/types/university.ts

@@ -4,5 +4,5 @@ export type University = {
   id: number
   name: string
   country_code: CountryCode
-  departments: unknown[]
+  departments: string[]
 }