diff --git a/services/web/frontend/js/features/settings/components/account-info-section.tsx b/services/web/frontend/js/features/settings/components/account-info-section.tsx index 11a027cc0f..c947f14e04 100644 --- a/services/web/frontend/js/features/settings/components/account-info-section.tsx +++ b/services/web/frontend/js/features/settings/components/account-info-section.tsx @@ -39,16 +39,18 @@ function AccountInfoSection() { const { isLoading, isSuccess, isError, error, runAsync } = useAsync() const [isFormValid, setIsFormValid] = useState(true) - const handleEmailChange = event => { + const handleEmailChange = (event: React.ChangeEvent) => { setEmail(event.target.value) setIsFormValid(event.target.validity.valid) } - const handleFirstNameChange = event => { + const handleFirstNameChange = ( + event: React.ChangeEvent + ) => { setFirstName(event.target.value) } - const handleLastNameChange = event => { + const handleLastNameChange = (event: React.ChangeEvent) => { setLastName(event.target.value) } @@ -56,7 +58,7 @@ function AccountInfoSection() { !hasAffiliationsFeature && !isExternalAuthenticationSystemUsed const canUpdateNames = shouldAllowEditingDetails - const handleSubmit = event => { + const handleSubmit = (event: React.FormEvent) => { event.preventDefault() if (!isFormValid) { return @@ -151,11 +153,15 @@ function ReadOrWriteFormGroup({ }: ReadOrWriteFormGroupProps) { const [validationMessage, setValidationMessage] = useState('') - const handleInvalid = event => { + const handleInvalid = ( + event: React.InvalidEvent + ) => { event.preventDefault() } - const handleChangeAndValidity = event => { + const handleChangeAndValidity = ( + event: React.ChangeEvent + ) => { handleChange(event) setValidationMessage(event.target.validationMessage) } diff --git a/services/web/frontend/js/features/settings/components/emails/add-email/input.tsx b/services/web/frontend/js/features/settings/components/emails/add-email/input.tsx index d478b49ba0..1de8743a04 100644 --- a/services/web/frontend/js/features/settings/components/emails/add-email/input.tsx +++ b/services/web/frontend/js/features/settings/components/emails/add-email/input.tsx @@ -70,20 +70,20 @@ function Input({ onChange, handleAddNewEmail }: InputProps) { (event: ChangeEvent) => { const hint = event.target.value setInputValue(hint) - const match = matchLocalAndDomain(hint) - if (!matchedDomain?.hostname.startsWith(match.domain)) { + const { local, domain } = matchLocalAndDomain(hint) + if (domain && !matchedDomain?.hostname.startsWith(domain)) { setSuggestion(null) } - if (!match.domain) { + if (!domain) { return } - if (domainCache.has(match.domain)) { - const cachedDomain = domainCache.get(match.domain) - setSuggestion(`${match.local}@${cachedDomain.hostname}`) + if (domainCache.has(domain)) { + const cachedDomain = domainCache.get(domain) as DomainInfo + setSuggestion(`${local}@${cachedDomain.hostname}`) setMatchedDomain(cachedDomain) return } - const query = `?hostname=${match.domain}&limit=1` + const query = `?hostname=${domain}&limit=1` getJSON>(`/institutions/domains${query}`, { signal, }) @@ -96,8 +96,8 @@ function Input({ onChange, handleAddNewEmail }: InputProps) { } const hostname = data[0]?.hostname if (hostname) { - domainCache.set(match.domain, data[0]) - setSuggestion(`${match.local}@${hostname}`) + domainCache.set(domain, data[0]) + setSuggestion(`${local}@${hostname}`) setMatchedDomain(data[0]) } else { setSuggestion(null) @@ -142,7 +142,7 @@ function Input({ onChange, handleAddNewEmail }: InputProps) { ) useEffect(() => { - if (suggestion && !suggestion.startsWith(inputValue)) { + if (suggestion && inputValue && !suggestion.startsWith(inputValue)) { setSuggestion(null) } }, [suggestion, inputValue]) diff --git a/services/web/frontend/js/features/settings/components/emails/sso-alert.tsx b/services/web/frontend/js/features/settings/components/emails/sso-alert.tsx index e90bab4f2d..3d75d8de6f 100644 --- a/services/web/frontend/js/features/settings/components/emails/sso-alert.tsx +++ b/services/web/frontend/js/features/settings/components/emails/sso-alert.tsx @@ -35,24 +35,20 @@ export function SSOAlert() { const handleErrorClosed = () => setErrorClosed(true) if (samlError) { - return ( - !errorClosed && ( - -

- {' '} - {samlError.translatedMessage - ? samlError.translatedMessage - : samlError.message} -

- {samlError.tryAgain && ( -

{t('try_again')}

- )} -
- ) - ) + return !errorClosed ? ( + +

+ {' '} + {samlError.translatedMessage + ? samlError.translatedMessage + : samlError.message} +

+ {samlError.tryAgain &&

{t('try_again')}

} +
+ ) : null } if (!institutionLinked) { diff --git a/services/web/frontend/js/features/settings/components/leave/modal-form.tsx b/services/web/frontend/js/features/settings/components/leave/modal-form.tsx index 8a75c6560a..1c3d85dfd3 100644 --- a/services/web/frontend/js/features/settings/components/leave/modal-form.tsx +++ b/services/web/frontend/js/features/settings/components/leave/modal-form.tsx @@ -24,11 +24,15 @@ function LeaveModalForm({ const [confirmation, setConfirmation] = useState(false) const [error, setError] = useState(null) - const handleEmailChange = event => { + const handleEmailChange = ( + event: React.ChangeEvent + ) => { setEmail(event.target.value) } - const handlePasswordChange = event => { + const handlePasswordChange = ( + event: React.ChangeEvent + ) => { setPassword(event.target.value) } @@ -36,7 +40,7 @@ function LeaveModalForm({ setConfirmation(prev => !prev) } - const handleSubmit = event => { + const handleSubmit = (event: React.FormEvent) => { event.preventDefault() if (!isFormValid) { return diff --git a/services/web/frontend/js/features/settings/components/linking-section.tsx b/services/web/frontend/js/features/settings/components/linking-section.tsx index 3485de4583..9e01ea4c29 100644 --- a/services/web/frontend/js/features/settings/components/linking-section.tsx +++ b/services/web/frontend/js/features/settings/components/linking-section.tsx @@ -13,12 +13,12 @@ function LinkingSection() { const projectSyncSuccessMessage = getMeta( 'ol-projectSyncSuccessMessage' ) as string - const [integrationLinkingWidgets] = useState( + const [integrationLinkingWidgets] = useState( () => getMeta('integrationLinkingWidgets') || importOverleafModules('integrationLinkingWidgets') ) - const [referenceLinkingWidgets] = useState( + const [referenceLinkingWidgets] = useState( () => getMeta('referenceLinkingWidgets') || importOverleafModules('referenceLinkingWidgets') @@ -139,7 +139,7 @@ function SSOLinkingWidgetContainer({ const { t } = useTranslation() const { unlink } = useSSOContext() - let description = null + let description = '' switch (subscription.providerId) { case 'collabratec': description = t('linked_collabratec_description') diff --git a/services/web/frontend/js/features/settings/components/linking/integration-widget.tsx b/services/web/frontend/js/features/settings/components/linking/integration-widget.tsx index b66c2d1b13..d2958a515d 100644 --- a/services/web/frontend/js/features/settings/components/linking/integration-widget.tsx +++ b/services/web/frontend/js/features/settings/components/linking/integration-widget.tsx @@ -141,7 +141,9 @@ function UnlinkConfirmationModal({ }: UnlinkConfirmModalProps) { const { t } = useTranslation() - const handleCancel = event => { + const handleCancel = ( + event: React.MouseEvent + ) => { event.preventDefault() handleHide() } diff --git a/services/web/frontend/js/features/settings/components/linking/sso-widget.tsx b/services/web/frontend/js/features/settings/components/linking/sso-widget.tsx index 0424fe36df..2e1868dcf2 100644 --- a/services/web/frontend/js/features/settings/components/linking/sso-widget.tsx +++ b/services/web/frontend/js/features/settings/components/linking/sso-widget.tsx @@ -8,7 +8,7 @@ import GoogleLogo from '../../../../shared/svgs/google-logo' import OrcidLogo from '../../../../shared/svgs/orcid-logo' import LinkingStatus from './status' -const providerLogos = { +const providerLogos: { readonly [p: string]: JSX.Element } = { collabratec: , google: , orcid: , @@ -95,6 +95,7 @@ export function SSOLinkingWidget({ ) } + type ActionButtonProps = { unlinkRequestInflight: boolean accountIsLinked?: boolean diff --git a/services/web/frontend/js/features/settings/components/linking/status.tsx b/services/web/frontend/js/features/settings/components/linking/status.tsx index 4e3ef1ab2b..c2c95c68b2 100644 --- a/services/web/frontend/js/features/settings/components/linking/status.tsx +++ b/services/web/frontend/js/features/settings/components/linking/status.tsx @@ -4,7 +4,7 @@ import Icon from '../../../../shared/components/icon' type Status = 'pending' | 'success' | 'error' type LinkingStatusProps = { - status?: Status + status: Status description: string | ReactNode } diff --git a/services/web/frontend/js/features/settings/components/password-section.tsx b/services/web/frontend/js/features/settings/components/password-section.tsx index 367a90732a..2e68f5f3c6 100644 --- a/services/web/frontend/js/features/settings/components/password-section.tsx +++ b/services/web/frontend/js/features/settings/components/password-section.tsx @@ -72,16 +72,22 @@ function PasswordForm() { const [isNewPasswordValid, setIsNewPasswordValid] = useState(false) const [isFormValid, setIsFormValid] = useState(false) - const handleCurrentPasswordChange = event => { + const handleCurrentPasswordChange = ( + event: React.ChangeEvent + ) => { setCurrentPassword(event.target.value) } - const handleNewPassword1Change = event => { + const handleNewPassword1Change = ( + event: React.ChangeEvent + ) => { setNewPassword1(event.target.value) setIsNewPasswordValid(event.target.validity.valid) } - const handleNewPassword2Change = event => { + const handleNewPassword2Change = ( + event: React.ChangeEvent + ) => { setNewPassword2(event.target.value) } @@ -91,7 +97,7 @@ function PasswordForm() { ) }, [currentPassword, newPassword1, newPassword2, isNewPasswordValid]) - const handleSubmit = event => { + const handleSubmit = (event: React.FormEvent) => { event.preventDefault() if (!isFormValid) { return @@ -157,7 +163,7 @@ type PasswordFormGroupProps = { id: string label: string value: string - handleChange: (event: any) => void + handleChange: (event: React.ChangeEvent) => void minLength?: number validationMessage?: string } @@ -173,11 +179,15 @@ function PasswordFormGroup({ const [validationMessage, setValidationMessage] = useState('') const [hadInteraction, setHadInteraction] = useState(false) - const handleInvalid = event => { + const handleInvalid = ( + event: React.InvalidEvent + ) => { event.preventDefault() } - const handleChangeAndValidity = event => { + const handleChangeAndValidity = ( + event: React.ChangeEvent + ) => { handleChange(event) setHadInteraction(true) setValidationMessage(event.target.validationMessage) diff --git a/services/web/frontend/js/infrastructure/fetch-json.ts b/services/web/frontend/js/infrastructure/fetch-json.ts index 7eac477df6..0017091e91 100644 --- a/services/web/frontend/js/infrastructure/fetch-json.ts +++ b/services/web/frontend/js/infrastructure/fetch-json.ts @@ -79,7 +79,9 @@ export class FetchError extends OError { getUserFacingMessage() { const statusCode = this.response?.status const defaultMessage = getErrorMessageForStatusCode(statusCode) - const message = this.data?.message?.text || this.data?.message + const message = (this.data?.message?.text || this.data?.message) as + | string + | undefined if (message && message !== defaultMessage) return message const statusCodes: { readonly [K: number]: string } = { @@ -208,11 +210,14 @@ async function parseResponseBody(response: Response) { return {} } -export function getUserFacingMessage(error: Error): string { +export function getUserFacingMessage(error: Error | null) { + if (!error) { + return undefined + } + if (error instanceof FetchError) { return error.getUserFacingMessage() - } else { - // checking existence of `error` to prevent errors when called from Javascript - return error?.message } + + return error.message } diff --git a/services/web/frontend/js/shared/hooks/use-persisted-state.ts b/services/web/frontend/js/shared/hooks/use-persisted-state.ts index 5d7eb968b5..cc4e4491f8 100644 --- a/services/web/frontend/js/shared/hooks/use-persisted-state.ts +++ b/services/web/frontend/js/shared/hooks/use-persisted-state.ts @@ -38,7 +38,7 @@ function usePersistedState( useEffect(() => { if (listen) { - const listener = event => { + const listener = (event: StorageEvent) => { if (event.key === key) { // note: this value is read via getItem rather than from event.newValue // because getItem handles deserializing the JSON that's stored in localStorage.