institution-fields.tsx 6.1 KB

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