account-info-section.tsx 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  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 OLFormText from '@/features/ui/components/ol/ol-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. maxLength={255}
  84. handleChange={handleFirstNameChange}
  85. canEdit={canUpdateNames}
  86. required={false}
  87. />
  88. <ReadOrWriteFormGroup
  89. id="last-name-input"
  90. type="text"
  91. label={t('last_name')}
  92. maxLength={255}
  93. value={lastName}
  94. handleChange={handleLastNameChange}
  95. canEdit={canUpdateNames}
  96. required={false}
  97. />
  98. {isSuccess ? (
  99. <OLFormGroup>
  100. <OLNotification
  101. type="success"
  102. content={t('thanks_settings_updated')}
  103. />
  104. </OLFormGroup>
  105. ) : null}
  106. {isError ? (
  107. <OLFormGroup>
  108. <OLNotification
  109. type="error"
  110. content={getUserFacingMessage(error) ?? ''}
  111. />
  112. </OLFormGroup>
  113. ) : null}
  114. {canUpdateEmail || canUpdateNames ? (
  115. <OLFormGroup>
  116. <OLButton
  117. type="submit"
  118. variant="primary"
  119. form="account-info-form"
  120. disabled={!isFormValid}
  121. isLoading={isLoading}
  122. loadingLabel={t('saving') + '…'}
  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. maxLength?: number
  140. required: boolean
  141. }
  142. function ReadOrWriteFormGroup({
  143. id,
  144. type,
  145. label,
  146. value,
  147. handleChange,
  148. canEdit,
  149. maxLength,
  150. required,
  151. }: ReadOrWriteFormGroupProps) {
  152. const [validationMessage, setValidationMessage] = useState('')
  153. const handleInvalid = (event: React.InvalidEvent<HTMLInputElement>) => {
  154. event.preventDefault()
  155. }
  156. const handleChangeAndValidity = (
  157. event: React.ChangeEvent<HTMLInputElement>
  158. ) => {
  159. handleChange(event)
  160. setValidationMessage(event.target.validationMessage)
  161. }
  162. if (!canEdit) {
  163. return (
  164. <OLFormGroup controlId={id}>
  165. <OLFormLabel>{label}</OLFormLabel>
  166. <OLFormControl type="text" readOnly value={value} />
  167. </OLFormGroup>
  168. )
  169. }
  170. return (
  171. <OLFormGroup controlId={id}>
  172. <OLFormLabel>{label}</OLFormLabel>
  173. <OLFormControl
  174. type={type}
  175. required={required}
  176. value={value}
  177. maxLength={maxLength}
  178. data-ol-dirty={!!validationMessage}
  179. onChange={handleChangeAndValidity}
  180. onInvalid={handleInvalid}
  181. />
  182. {validationMessage && (
  183. <OLFormText type="error">{validationMessage}</OLFormText>
  184. )}
  185. </OLFormGroup>
  186. )
  187. }
  188. export default AccountInfoSection