account-info-section.tsx 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  1. import { useState } from 'react'
  2. import { useTranslation } from 'react-i18next'
  3. import {
  4. getUserFacingMessage,
  5. postJSON,
  6. } from '../../../infrastructure/fetch-json'
  7. import getMeta from '../../../utils/meta'
  8. import useAsync from '../../../shared/hooks/use-async'
  9. import { useUserContext } from '../../../shared/context/user-context'
  10. import OLButton from '@/features/ui/components/ol/ol-button'
  11. import OLNotification from '@/features/ui/components/ol/ol-notification'
  12. import OLFormGroup from '@/features/ui/components/ol/ol-form-group'
  13. import OLFormLabel from '@/features/ui/components/ol/ol-form-label'
  14. import OLFormControl from '@/features/ui/components/ol/ol-form-control'
  15. import FormText from '@/features/ui/components/bootstrap-5/form/form-text'
  16. function AccountInfoSection() {
  17. const { t } = useTranslation()
  18. const { hasAffiliationsFeature } = getMeta('ol-ExposedSettings')
  19. const isExternalAuthenticationSystemUsed = getMeta(
  20. 'ol-isExternalAuthenticationSystemUsed'
  21. )
  22. const shouldAllowEditingDetails = getMeta('ol-shouldAllowEditingDetails')
  23. const {
  24. first_name: initialFirstName,
  25. last_name: initialLastName,
  26. email: initialEmail,
  27. } = useUserContext()
  28. const [email, setEmail] = useState(initialEmail)
  29. const [firstName, setFirstName] = useState(initialFirstName)
  30. const [lastName, setLastName] = useState(initialLastName)
  31. const { isLoading, isSuccess, isError, error, runAsync } = useAsync()
  32. const [isFormValid, setIsFormValid] = useState(true)
  33. const handleEmailChange = (event: React.ChangeEvent<HTMLInputElement>) => {
  34. setEmail(event.target.value)
  35. setIsFormValid(event.target.validity.valid)
  36. }
  37. const handleFirstNameChange = (
  38. event: React.ChangeEvent<HTMLInputElement>
  39. ) => {
  40. setFirstName(event.target.value)
  41. }
  42. const handleLastNameChange = (event: React.ChangeEvent<HTMLInputElement>) => {
  43. setLastName(event.target.value)
  44. }
  45. const canUpdateEmail =
  46. !hasAffiliationsFeature && !isExternalAuthenticationSystemUsed
  47. const canUpdateNames = shouldAllowEditingDetails
  48. const handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {
  49. event.preventDefault()
  50. if (!isFormValid) {
  51. return
  52. }
  53. runAsync(
  54. postJSON('/user/settings', {
  55. body: {
  56. email: canUpdateEmail ? email : undefined,
  57. first_name: canUpdateNames ? firstName : undefined,
  58. last_name: canUpdateNames ? lastName : undefined,
  59. },
  60. })
  61. ).catch(() => {})
  62. }
  63. return (
  64. <>
  65. <h3>{t('update_account_info')}</h3>
  66. <form id="account-info-form" onSubmit={handleSubmit}>
  67. {hasAffiliationsFeature ? null : (
  68. <ReadOrWriteFormGroup
  69. id="email-input"
  70. type="email"
  71. label={t('email')}
  72. value={email}
  73. handleChange={handleEmailChange}
  74. canEdit={canUpdateEmail}
  75. required
  76. />
  77. )}
  78. <ReadOrWriteFormGroup
  79. id="first-name-input"
  80. type="text"
  81. label={t('first_name')}
  82. value={firstName}
  83. handleChange={handleFirstNameChange}
  84. canEdit={canUpdateNames}
  85. required={false}
  86. />
  87. <ReadOrWriteFormGroup
  88. id="last-name-input"
  89. type="text"
  90. label={t('last_name')}
  91. value={lastName}
  92. handleChange={handleLastNameChange}
  93. canEdit={canUpdateNames}
  94. required={false}
  95. />
  96. {isSuccess ? (
  97. <OLFormGroup>
  98. <OLNotification
  99. type="success"
  100. content={t('thanks_settings_updated')}
  101. />
  102. </OLFormGroup>
  103. ) : null}
  104. {isError ? (
  105. <OLFormGroup>
  106. <OLNotification
  107. type="error"
  108. content={getUserFacingMessage(error) ?? ''}
  109. />
  110. </OLFormGroup>
  111. ) : null}
  112. {canUpdateEmail || canUpdateNames ? (
  113. <OLFormGroup>
  114. <OLButton
  115. type="submit"
  116. variant="primary"
  117. form="account-info-form"
  118. disabled={!isFormValid}
  119. isLoading={isLoading}
  120. bs3Props={{
  121. loading: isLoading ? `${t('saving')}…` : t('update'),
  122. }}
  123. >
  124. {t('update')}
  125. </OLButton>
  126. </OLFormGroup>
  127. ) : null}
  128. </form>
  129. </>
  130. )
  131. }
  132. type ReadOrWriteFormGroupProps = {
  133. id: string
  134. type: string
  135. label: string
  136. value?: string
  137. handleChange: (event: any) => void
  138. canEdit: boolean
  139. required: boolean
  140. }
  141. function ReadOrWriteFormGroup({
  142. id,
  143. type,
  144. label,
  145. value,
  146. handleChange,
  147. canEdit,
  148. required,
  149. }: ReadOrWriteFormGroupProps) {
  150. const [validationMessage, setValidationMessage] = useState('')
  151. const handleInvalid = (event: React.InvalidEvent<HTMLInputElement>) => {
  152. event.preventDefault()
  153. }
  154. const handleChangeAndValidity = (
  155. event: React.ChangeEvent<HTMLInputElement>
  156. ) => {
  157. handleChange(event)
  158. setValidationMessage(event.target.validationMessage)
  159. }
  160. if (!canEdit) {
  161. return (
  162. <OLFormGroup controlId={id}>
  163. <OLFormLabel>{label}</OLFormLabel>
  164. <OLFormControl type="text" readOnly value={value} />
  165. </OLFormGroup>
  166. )
  167. }
  168. return (
  169. <OLFormGroup controlId={id}>
  170. <OLFormLabel>{label}</OLFormLabel>
  171. <OLFormControl
  172. type={type}
  173. required={required}
  174. value={value}
  175. data-ol-dirty={!!validationMessage}
  176. onChange={handleChangeAndValidity}
  177. onInvalid={handleInvalid}
  178. />
  179. {validationMessage && (
  180. <FormText type="error">{validationMessage}</FormText>
  181. )}
  182. </OLFormGroup>
  183. )
  184. }
  185. export default AccountInfoSection