row.tsx 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. import { useState } from 'react'
  2. import { Trans, useTranslation } from 'react-i18next'
  3. import { UserEmailData } from '../../../../../../types/user-email'
  4. import Email from './email'
  5. import InstitutionAndRole from './institution-and-role'
  6. import EmailCell from './cell'
  7. import Actions from './actions'
  8. import { institutionAlreadyLinked } from '../../utils/selectors'
  9. import { useUserEmailsContext } from '../../context/user-email-context'
  10. import getMeta from '../../../../utils/meta'
  11. import { ssoAvailableForInstitution } from '../../utils/sso'
  12. import ReconfirmationInfo from './reconfirmation-info'
  13. import { useLocation } from '../../../../shared/hooks/use-location'
  14. import OLRow from '@/shared/components/ol/ol-row'
  15. import OLCol from '@/shared/components/ol/ol-col'
  16. import OLButton from '@/shared/components/ol/ol-button'
  17. type EmailsRowProps = {
  18. userEmailData: UserEmailData
  19. primary?: UserEmailData
  20. }
  21. function EmailsRow({ userEmailData, primary }: EmailsRowProps) {
  22. const hasSSOAffiliation = Boolean(
  23. userEmailData.affiliation &&
  24. ssoAvailableForInstitution(userEmailData.affiliation.institution)
  25. )
  26. return (
  27. <>
  28. <OLRow data-testid="email-row">
  29. <OLCol lg={4}>
  30. <EmailCell>
  31. <Email userEmailData={userEmailData} />
  32. </EmailCell>
  33. </OLCol>
  34. <OLCol lg={5}>
  35. {userEmailData.affiliation?.institution && (
  36. <EmailCell>
  37. <InstitutionAndRole userEmailData={userEmailData} />
  38. </EmailCell>
  39. )}
  40. </OLCol>
  41. <OLCol lg={3}>
  42. <EmailCell className="text-lg-end">
  43. <Actions userEmailData={userEmailData} primary={primary} />
  44. </EmailCell>
  45. </OLCol>
  46. </OLRow>
  47. {hasSSOAffiliation && (
  48. <SSOAffiliationInfo userEmailData={userEmailData} />
  49. )}
  50. <ReconfirmationInfo userEmailData={userEmailData} />
  51. </>
  52. )
  53. }
  54. type SSOAffiliationInfoProps = {
  55. userEmailData: UserEmailData
  56. }
  57. function SSOAffiliationInfo({ userEmailData }: SSOAffiliationInfoProps) {
  58. const { samlInitPath } = getMeta('ol-ExposedSettings')
  59. const { t } = useTranslation()
  60. const { state } = useUserEmailsContext()
  61. const location = useLocation()
  62. const [linkAccountsButtonDisabled, setLinkAccountsButtonDisabled] =
  63. useState(false)
  64. function handleLinkAccountsButtonClick() {
  65. setLinkAccountsButtonDisabled(true)
  66. location.assign(
  67. `${samlInitPath}?university_id=${userEmailData.affiliation?.institution?.id}&auto=/user/settings&email=${userEmailData.email}`
  68. )
  69. }
  70. if (
  71. !userEmailData.samlProviderId &&
  72. institutionAlreadyLinked(state, userEmailData)
  73. ) {
  74. // if the email is not linked to the institution, but there's another email already linked to that institution
  75. // no SSO affiliation is displayed, since cannot have multiple emails linked to the same institution
  76. return null
  77. }
  78. if (userEmailData.samlProviderId) {
  79. return (
  80. <OLRow>
  81. <OLCol lg={{ span: 8, offset: 4 }}>
  82. <EmailCell>
  83. <p>
  84. <Trans
  85. i18nKey="acct_linked_to_institution_acct_2"
  86. components={
  87. /* eslint-disable-next-line jsx-a11y/anchor-has-content, react/jsx-key */
  88. [<strong />]
  89. }
  90. values={{
  91. institutionName: userEmailData.affiliation?.institution.name,
  92. }}
  93. shouldUnescape
  94. tOptions={{ interpolation: { escapeValue: true } }}
  95. />
  96. </p>
  97. </EmailCell>
  98. </OLCol>
  99. </OLRow>
  100. )
  101. }
  102. const domainAlsoForGroupWithDomainCapture =
  103. userEmailData?.affiliation?.group?.domainCaptureEnabled
  104. if (domainAlsoForGroupWithDomainCapture) {
  105. // user is not linked via Commons and should link via groups
  106. // do not show UI to link to Commons
  107. return null
  108. }
  109. return (
  110. <OLRow>
  111. <OLCol lg={{ span: 8, offset: 4 }}>
  112. <div className="horizontal-divider" />
  113. <OLRow>
  114. <OLCol lg={9}>
  115. <EmailCell>
  116. <p className="small">
  117. <Trans
  118. i18nKey="can_link_your_institution_acct_2"
  119. values={{
  120. institutionName:
  121. userEmailData.affiliation?.institution.name,
  122. }}
  123. shouldUnescape
  124. tOptions={{ interpolation: { escapeValue: true } }}
  125. components={
  126. /* eslint-disable-next-line jsx-a11y/anchor-has-content, react/jsx-key */
  127. [<strong />]
  128. }
  129. />
  130. </p>
  131. <p className="small">
  132. <Trans
  133. i18nKey="doing_this_allow_log_in_through_institution_2"
  134. components={
  135. /* eslint-disable-next-line jsx-a11y/anchor-has-content, react/jsx-key */
  136. [<strong />]
  137. }
  138. />{' '}
  139. <a href="/learn/how-to/Institutional_Login" target="_blank">
  140. {t('find_out_more_about_institution_login')}
  141. </a>
  142. </p>
  143. </EmailCell>
  144. </OLCol>
  145. <OLCol lg={3} className="text-lg-end">
  146. <EmailCell>
  147. <OLButton
  148. variant="primary"
  149. className="btn-link-accounts"
  150. disabled={linkAccountsButtonDisabled}
  151. onClick={handleLinkAccountsButtonClick}
  152. size="sm"
  153. >
  154. {t('link_accounts')}
  155. </OLButton>
  156. </EmailCell>
  157. </OLCol>
  158. </OLRow>
  159. </OLCol>
  160. </OLRow>
  161. )
  162. }
  163. export default EmailsRow