institution-fields.tsx 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205
  1. import { useEffect, useState, useRef } from 'react'
  2. import { useTranslation } from 'react-i18next'
  3. import CountryInput from './country-input'
  4. import DownshiftInput from '../downshift-input'
  5. import EmailAffiliatedWithInstitution from './email-affiliated-with-institution'
  6. import defaultRoles from '../../../data/roles'
  7. import defaultDepartments from '../../../data/departments'
  8. import { CountryCode } from '../../../data/countries-list'
  9. import { University } from '../../../../../../../types/university'
  10. import { DomainInfo } from './input'
  11. import { getJSON } from '../../../../../infrastructure/fetch-json'
  12. import useAsync from '../../../../../shared/hooks/use-async'
  13. import UniversityName from './university-name'
  14. import OLFormGroup from '@/shared/components/ol/ol-form-group'
  15. type InstitutionFieldsProps = {
  16. countryCode: CountryCode | null
  17. setCountryCode: React.Dispatch<React.SetStateAction<CountryCode | null>>
  18. universities: Partial<Record<CountryCode, University[]>>
  19. setUniversities: React.Dispatch<
  20. React.SetStateAction<Partial<Record<CountryCode, University[]>>>
  21. >
  22. universityName: string
  23. setUniversityName: React.Dispatch<React.SetStateAction<string>>
  24. role: string
  25. setRole: React.Dispatch<React.SetStateAction<string>>
  26. department: string
  27. setDepartment: React.Dispatch<React.SetStateAction<string>>
  28. newEmailMatchedDomain: DomainInfo | null
  29. }
  30. function InstitutionFields({
  31. countryCode,
  32. setCountryCode,
  33. universities,
  34. setUniversities,
  35. universityName,
  36. setUniversityName,
  37. role,
  38. setRole,
  39. department,
  40. setDepartment,
  41. newEmailMatchedDomain,
  42. }: InstitutionFieldsProps) {
  43. const { t } = useTranslation()
  44. const countryRef = useRef<HTMLInputElement | null>(null)
  45. const [departments, setDepartments] = useState<string[]>([
  46. ...defaultDepartments,
  47. ])
  48. const [isInstitutionFieldsVisible, setIsInstitutionFieldsVisible] =
  49. useState(false)
  50. const [isUniversityDirty, setIsUniversityDirty] = useState(false)
  51. const { runAsync: institutionRunAsync } = useAsync<University[]>()
  52. useEffect(() => {
  53. if (isInstitutionFieldsVisible && countryRef.current) {
  54. countryRef.current?.focus()
  55. }
  56. }, [countryRef, isInstitutionFieldsVisible])
  57. useEffect(() => {
  58. if (universityName) {
  59. setIsUniversityDirty(true)
  60. }
  61. }, [setIsUniversityDirty, universityName])
  62. // If the institution selected by autocompletion has changed
  63. // hide the fields visibility and reset values
  64. useEffect(() => {
  65. if (!newEmailMatchedDomain) {
  66. setIsInstitutionFieldsVisible(false)
  67. setRole('')
  68. setDepartment('')
  69. }
  70. }, [newEmailMatchedDomain, setRole, setDepartment])
  71. useEffect(() => {
  72. if (newEmailMatchedDomain?.university?.departments?.length) {
  73. setDepartments(newEmailMatchedDomain.university.departments)
  74. return
  75. }
  76. // fallback if not matched on domain
  77. const selectedKnownUniversity = countryCode
  78. ? universities[countryCode]?.find(({ name }) => name === universityName)
  79. : undefined
  80. if (selectedKnownUniversity && selectedKnownUniversity.departments.length) {
  81. setDepartments(selectedKnownUniversity.departments)
  82. } else {
  83. setDepartments([...defaultDepartments])
  84. }
  85. }, [countryCode, universities, universityName, newEmailMatchedDomain])
  86. // Fetch country institution
  87. useEffect(() => {
  88. // Skip if country not selected or universities for
  89. // that country are already fetched
  90. if (!countryCode || universities[countryCode]) {
  91. return
  92. }
  93. institutionRunAsync(
  94. getJSON(`/institutions/list?country_code=${countryCode}`)
  95. )
  96. .then(data => {
  97. setUniversities(state => ({ ...state, [countryCode]: data }))
  98. })
  99. .catch(() => {})
  100. }, [countryCode, universities, setUniversities, institutionRunAsync])
  101. const getUniversityItems = () => {
  102. if (!countryCode) {
  103. return []
  104. }
  105. return (
  106. universities[countryCode]
  107. ?.map(({ name }) => name)
  108. .filter(name =>
  109. name.trim().toLowerCase().includes(universityName.toLowerCase())
  110. ) ?? []
  111. )
  112. }
  113. const handleShowInstitutionFields = () => {
  114. setIsInstitutionFieldsVisible(true)
  115. }
  116. const handleSelectUniversityManually = () => {
  117. setRole('')
  118. setDepartment('')
  119. handleShowInstitutionFields()
  120. }
  121. const isLetUsKnowVisible =
  122. !newEmailMatchedDomain && !isInstitutionFieldsVisible
  123. const isAutocompletedInstitutionVisible =
  124. newEmailMatchedDomain && !isInstitutionFieldsVisible
  125. const isRoleAndDepartmentVisible =
  126. isAutocompletedInstitutionVisible || isUniversityDirty
  127. // Is the email affiliated with an institution?
  128. if (isLetUsKnowVisible) {
  129. return (
  130. <EmailAffiliatedWithInstitution onClick={handleShowInstitutionFields} />
  131. )
  132. }
  133. return (
  134. <>
  135. {isAutocompletedInstitutionVisible ? (
  136. // Display the institution name after autocompletion
  137. <UniversityName
  138. name={newEmailMatchedDomain.university.name}
  139. onClick={handleSelectUniversityManually}
  140. />
  141. ) : (
  142. // Display the country and university fields
  143. <>
  144. <OLFormGroup className="mb-2">
  145. <CountryInput
  146. id="new-email-country-input"
  147. setValue={setCountryCode}
  148. ref={countryRef}
  149. />
  150. </OLFormGroup>
  151. <OLFormGroup className={isRoleAndDepartmentVisible ? 'mb-2' : 'mb-0'}>
  152. <DownshiftInput
  153. items={getUniversityItems()}
  154. inputValue={universityName}
  155. label={t('university')}
  156. showLabel
  157. setValue={setUniversityName}
  158. disabled={!countryCode}
  159. />
  160. </OLFormGroup>
  161. </>
  162. )}
  163. {isRoleAndDepartmentVisible && (
  164. <>
  165. <OLFormGroup className="mb-2">
  166. <DownshiftInput
  167. items={[...defaultRoles]}
  168. inputValue={role}
  169. label={t('role')}
  170. setValue={setRole}
  171. showLabel
  172. />
  173. </OLFormGroup>
  174. <OLFormGroup className="mb-0">
  175. <DownshiftInput
  176. items={departments}
  177. inputValue={department}
  178. label={t('department')}
  179. setValue={setDepartment}
  180. showLabel
  181. />
  182. </OLFormGroup>
  183. </>
  184. )}
  185. </>
  186. )
  187. }
  188. export default InstitutionFields