overleaf/services/web/frontend/js/features/settings/components/account-info-section.tsx
Jessica Lawshe 50aad92eb9 Merge pull request #20692 from overleaf/ar-limit-length-of-user-editable-fields
[web] limit length of user editable fields

GitOrigin-RevId: 239398dd05dcde7fea0ac8415e41396ef01c2b74
2024-10-14 10:57:56 +00:00

205 lines
5.7 KiB
TypeScript

import { useState } from 'react'
import { useTranslation } from 'react-i18next'
import {
getUserFacingMessage,
postJSON,
} from '../../../infrastructure/fetch-json'
import getMeta from '../../../utils/meta'
import useAsync from '../../../shared/hooks/use-async'
import { useUserContext } from '../../../shared/context/user-context'
import OLButton from '@/features/ui/components/ol/ol-button'
import OLNotification from '@/features/ui/components/ol/ol-notification'
import OLFormGroup from '@/features/ui/components/ol/ol-form-group'
import OLFormLabel from '@/features/ui/components/ol/ol-form-label'
import OLFormControl from '@/features/ui/components/ol/ol-form-control'
import FormText from '@/features/ui/components/bootstrap-5/form/form-text'
function AccountInfoSection() {
const { t } = useTranslation()
const { hasAffiliationsFeature } = getMeta('ol-ExposedSettings')
const isExternalAuthenticationSystemUsed = getMeta(
'ol-isExternalAuthenticationSystemUsed'
)
const shouldAllowEditingDetails = getMeta('ol-shouldAllowEditingDetails')
const {
first_name: initialFirstName,
last_name: initialLastName,
email: initialEmail,
} = useUserContext()
const [email, setEmail] = useState(initialEmail)
const [firstName, setFirstName] = useState(initialFirstName)
const [lastName, setLastName] = useState(initialLastName)
const { isLoading, isSuccess, isError, error, runAsync } = useAsync()
const [isFormValid, setIsFormValid] = useState(true)
const handleEmailChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setEmail(event.target.value)
setIsFormValid(event.target.validity.valid)
}
const handleFirstNameChange = (
event: React.ChangeEvent<HTMLInputElement>
) => {
setFirstName(event.target.value)
}
const handleLastNameChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setLastName(event.target.value)
}
const canUpdateEmail =
!hasAffiliationsFeature && !isExternalAuthenticationSystemUsed
const canUpdateNames = shouldAllowEditingDetails
const handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault()
if (!isFormValid) {
return
}
runAsync(
postJSON('/user/settings', {
body: {
email: canUpdateEmail ? email : undefined,
first_name: canUpdateNames ? firstName : undefined,
last_name: canUpdateNames ? lastName : undefined,
},
})
).catch(() => {})
}
return (
<>
<h3>{t('update_account_info')}</h3>
<form id="account-info-form" onSubmit={handleSubmit}>
{hasAffiliationsFeature ? null : (
<ReadOrWriteFormGroup
id="email-input"
type="email"
label={t('email')}
value={email}
handleChange={handleEmailChange}
canEdit={canUpdateEmail}
required
/>
)}
<ReadOrWriteFormGroup
id="first-name-input"
type="text"
label={t('first_name')}
value={firstName}
maxLength={255}
handleChange={handleFirstNameChange}
canEdit={canUpdateNames}
required={false}
/>
<ReadOrWriteFormGroup
id="last-name-input"
type="text"
label={t('last_name')}
maxLength={255}
value={lastName}
handleChange={handleLastNameChange}
canEdit={canUpdateNames}
required={false}
/>
{isSuccess ? (
<OLFormGroup>
<OLNotification
type="success"
content={t('thanks_settings_updated')}
/>
</OLFormGroup>
) : null}
{isError ? (
<OLFormGroup>
<OLNotification
type="error"
content={getUserFacingMessage(error) ?? ''}
/>
</OLFormGroup>
) : null}
{canUpdateEmail || canUpdateNames ? (
<OLFormGroup>
<OLButton
type="submit"
variant="primary"
form="account-info-form"
disabled={!isFormValid}
isLoading={isLoading}
bs3Props={{
loading: isLoading ? `${t('saving')}` : t('update'),
}}
>
{t('update')}
</OLButton>
</OLFormGroup>
) : null}
</form>
</>
)
}
type ReadOrWriteFormGroupProps = {
id: string
type: string
label: string
value?: string
handleChange: (event: any) => void
canEdit: boolean
maxLength?: number
required: boolean
}
function ReadOrWriteFormGroup({
id,
type,
label,
value,
handleChange,
canEdit,
maxLength,
required,
}: ReadOrWriteFormGroupProps) {
const [validationMessage, setValidationMessage] = useState('')
const handleInvalid = (event: React.InvalidEvent<HTMLInputElement>) => {
event.preventDefault()
}
const handleChangeAndValidity = (
event: React.ChangeEvent<HTMLInputElement>
) => {
handleChange(event)
setValidationMessage(event.target.validationMessage)
}
if (!canEdit) {
return (
<OLFormGroup controlId={id}>
<OLFormLabel>{label}</OLFormLabel>
<OLFormControl type="text" readOnly value={value} />
</OLFormGroup>
)
}
return (
<OLFormGroup controlId={id}>
<OLFormLabel>{label}</OLFormLabel>
<OLFormControl
type={type}
required={required}
value={value}
maxLength={maxLength}
data-ol-dirty={!!validationMessage}
onChange={handleChangeAndValidity}
onInvalid={handleInvalid}
/>
{validationMessage && (
<FormText type="error">{validationMessage}</FormText>
)}
</OLFormGroup>
)
}
export default AccountInfoSection