overleaf-cep/services/web/frontend/js/features/settings/components/linking/integration-widget.tsx
Antoine Clausse e0f3bea9ad [web] De-capitalize english translations (#24123)
* Create decapitalize.sh script

* Remove `text-capitalize` classes, rely on translations instead

* `Account Linking` -> `Account linking`

* `Account Settings` -> `Account settings`

* `Add Affiliation` -> `Add affiliation`

* `Add Email` -> `Add email`

* `Add Files` -> `Add files`

* `Add to Dictionary` -> `Add to dictionary`

* `All Projects` -> `All projects`

* `All Templates` -> `All templates`

* `Archive Projects` -> `Archive projects`

* `Archived Projects` -> `Archived projects`

* `Auto Compile` -> `Auto compile`

* `Back to Subscription` -> `Back to subscription`

* `Blank Project` -> `Blank project`

* `Change Password` -> `Change password`

* `Change Project Owner` -> `Change project owner`

* `Clear Sessions` -> `Clear sessions`

* `Company Name` -> `Company name`

* `Compile Error Handling` -> `Compile error handling`

* `Compile Mode` -> `Compile mode`

* `Compromised Password` -> `Compromised password`

* `Confirm Affiliation` -> `Confirm affiliation`

* `Confirm Email` -> `Confirm email`

* `Connected Users` -> `Connected users`

* `Contact Sales` -> `Contact sales`

* `Contact Support` -> `Contact support`

* `Contact Us` -> `Contact us`

* `Copy Project` -> `Copy project`

* `Delete Account` -> `Delete account`

* `Emails and Affiliations` -> `Emails and affiliations`

* `Git Integration` -> `Git integration`

* `Group Settings` -> `Group settings`

* `Link Accounts` -> `Link accounts`

* `Make Primary` -> `Make primary`

* `Mendeley Integration` -> `Mendeley integration`

* `Papers Integration` -> `Papers integration`

* `Project Synchronisation` -> `Project synchronisation`

* `Sessions Cleared` -> `Sessions cleared`

* `Stop Compilation` -> `Stop compilation`

* `Update Account Info` -> `Update account info`

* `the Sales team` -> `the sales team`

* `your Group settings` -> `your group settings`

* `Zotero Integration` -> `Zotero integration`

* Update decapitalize.sh

* Decapitalize some translations

* `Example Project` -> `Example project`

* `New Project` -> `New project`

* `New Tag` -> `New tag`

* `Trashed Projects` -> `Trashed projects`

* `Upload Project` -> `Upload project`

* `Your Projects` -> `Your projects`

* Revert "Create decapitalize.sh script"

This reverts commit 8c79f367096c206c704c7c01e3572a18f3961d5e.

* Revert changes to stories

* Fix tests

* `Contact us of` -> `Contact us if`

* Make `Contact us` bold in tex files

* `sales team` -> `Sales team`

* `Link accounts and Add email` -> `Link accounts and add email`

* `Make Private` -> `Make private`

* `contact support` -> `contact Support`

* Make `Make primary` tests case sensitive

* Use `add_email` translation string

* Revert changes to non-english locales

* Remove redundant `Account settings` translation

* `New project Name` -> `New project name`

GitOrigin-RevId: 675c46f96ddbf3d259a8d723fed62aa4a7ed40b7
2025-05-22 08:07:46 +00:00

225 lines
5.1 KiB
TypeScript

import { useCallback, useState, ReactNode } from 'react'
import { useTranslation } from 'react-i18next'
import OLBadge from '@/features/ui/components/ol/ol-badge'
import getMeta from '../../../../utils/meta'
import { sendMB } from '../../../../infrastructure/event-tracking'
import OLButton from '@/features/ui/components/ol/ol-button'
import OLModal, {
OLModalBody,
OLModalFooter,
OLModalHeader,
OLModalTitle,
} from '@/features/ui/components/ol/ol-modal'
function trackUpgradeClick(integration: string) {
sendMB('settings-upgrade-click', { integration })
}
function trackLinkingClick(integration: string) {
sendMB('link-integration-click', { integration, location: 'Settings' })
}
type IntegrationLinkingWidgetProps = {
id: string
logo: ReactNode
title: string
description: string
helpPath: string
hasFeature?: boolean
statusIndicator?: ReactNode
linked?: boolean
linkPath: string
unlinkPath: string
unlinkConfirmationTitle: string
unlinkConfirmationText: string
disabled?: boolean
}
export function IntegrationLinkingWidget({
id,
logo,
title,
description,
helpPath,
hasFeature,
statusIndicator,
linked,
linkPath,
unlinkPath,
unlinkConfirmationTitle,
unlinkConfirmationText,
disabled,
}: IntegrationLinkingWidgetProps) {
const { t } = useTranslation()
const [showModal, setShowModal] = useState(false)
const handleUnlinkClick = useCallback(() => {
setShowModal(true)
}, [])
const handleModalHide = useCallback(() => {
setShowModal(false)
}, [])
return (
<div className="settings-widget-container">
<div>{logo}</div>
<div className="description-container">
<div className="title-row">
<h4 id={id}>{title}</h4>
{!hasFeature && <OLBadge bg="info">{t('premium_feature')}</OLBadge>}
</div>
<p className="small">
{description}{' '}
<a href={helpPath} target="_blank" rel="noreferrer">
{t('learn_more_about', { appName: title })}
</a>
</p>
{hasFeature && statusIndicator}
</div>
<div>
<ActionButton
titleId={id}
integration={title}
hasFeature={hasFeature}
linked={linked}
handleUnlinkClick={handleUnlinkClick}
linkPath={linkPath}
disabled={disabled}
/>
</div>
<UnlinkConfirmationModal
integration={title}
show={showModal}
title={unlinkConfirmationTitle}
content={unlinkConfirmationText}
unlinkPath={unlinkPath}
handleHide={handleModalHide}
/>
</div>
)
}
type ActionButtonProps = {
integration: string
hasFeature?: boolean
linked?: boolean
handleUnlinkClick: () => void
linkPath: string
disabled?: boolean
titleId: string
}
function ActionButton({
hasFeature,
linked,
handleUnlinkClick,
linkPath,
disabled,
integration,
titleId,
}: ActionButtonProps) {
const { t } = useTranslation()
const linkTextId = `${titleId}-link`
if (!hasFeature) {
return (
<OLButton
variant="primary"
href="/user/subscription/plans"
onClick={() => trackUpgradeClick(integration)}
aria-labelledby={`${titleId} ${linkTextId}`}
>
<span id={linkTextId}>{t('upgrade')}</span>
</OLButton>
)
} else if (linked) {
return (
<OLButton
variant="danger-ghost"
onClick={handleUnlinkClick}
disabled={disabled}
>
{t('unlink')}
</OLButton>
)
} else {
return (
<>
{disabled ? (
<OLButton disabled variant="secondary">
{t('link')}
</OLButton>
) : (
<OLButton
variant="secondary"
href={linkPath}
onClick={() => trackLinkingClick(integration)}
>
{t('link')}
</OLButton>
)}
</>
)
}
}
type UnlinkConfirmModalProps = {
show: boolean
title: string
integration: string
content: string
unlinkPath: string
handleHide: () => void
}
function UnlinkConfirmationModal({
show,
title,
integration,
content,
unlinkPath,
handleHide,
}: UnlinkConfirmModalProps) {
const { t } = useTranslation()
const handleCancel = (event: React.MouseEvent<HTMLButtonElement>) => {
event.preventDefault()
handleHide()
}
const handleConfirm = () => {
sendMB('unlink-integration-click', {
integration,
})
}
return (
<OLModal show={show} onHide={handleHide}>
<OLModalHeader closeButton>
<OLModalTitle>{title}</OLModalTitle>
</OLModalHeader>
<OLModalBody>
<p>{content}</p>
</OLModalBody>
<OLModalFooter>
<form action={unlinkPath} method="POST" className="form-inline">
<input type="hidden" name="_csrf" value={getMeta('ol-csrfToken')} />
<OLButton variant="secondary" onClick={handleCancel}>
{t('cancel')}
</OLButton>
<OLButton
type="submit"
variant="danger-ghost"
onClick={handleConfirm}
>
{t('unlink')}
</OLButton>
</form>
</OLModalFooter>
</OLModal>
)
}