integration-widget.tsx 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. import { useCallback, useState, ReactNode } from 'react'
  2. import { useTranslation } from 'react-i18next'
  3. import AccessibleModal from '../../../../shared/components/accessible-modal'
  4. import { Button, Modal } from 'react-bootstrap'
  5. import getMeta from '../../../../utils/meta'
  6. import { sendMB } from '../../../../infrastructure/event-tracking'
  7. function trackUpgradeClick() {
  8. sendMB('settings-upgrade-click')
  9. }
  10. type IntegrationLinkingWidgetProps = {
  11. logo: ReactNode
  12. title: string
  13. description: string
  14. helpPath: string
  15. hasFeature?: boolean
  16. statusIndicator?: ReactNode
  17. linked?: boolean
  18. linkPath: string
  19. unlinkPath: string
  20. unlinkConfirmationTitle: string
  21. unlinkConfirmationText: string
  22. disabled?: boolean
  23. }
  24. export function IntegrationLinkingWidget({
  25. logo,
  26. title,
  27. description,
  28. helpPath,
  29. hasFeature,
  30. statusIndicator,
  31. linked,
  32. linkPath,
  33. unlinkPath,
  34. unlinkConfirmationTitle,
  35. unlinkConfirmationText,
  36. disabled,
  37. }: IntegrationLinkingWidgetProps) {
  38. const { t } = useTranslation()
  39. const [showModal, setShowModal] = useState(false)
  40. const handleUnlinkClick = useCallback(() => {
  41. setShowModal(true)
  42. }, [])
  43. const handleModalHide = useCallback(() => {
  44. setShowModal(false)
  45. }, [])
  46. return (
  47. <div className="settings-widget-container">
  48. <div>{logo}</div>
  49. <div className="description-container">
  50. <div className="title-row">
  51. <h4>{title}</h4>
  52. {!hasFeature && (
  53. <span className="label label-info">{t('premium_feature')}</span>
  54. )}
  55. </div>
  56. <p className="small">
  57. {description}{' '}
  58. <a href={helpPath} target="_blank" rel="noreferrer">
  59. {t('learn_more')}
  60. </a>
  61. </p>
  62. {hasFeature && statusIndicator}
  63. </div>
  64. <div>
  65. <ActionButton
  66. hasFeature={hasFeature}
  67. linked={linked}
  68. handleUnlinkClick={handleUnlinkClick}
  69. linkPath={linkPath}
  70. disabled={disabled}
  71. />
  72. </div>
  73. <UnlinkConfirmationModal
  74. show={showModal}
  75. title={unlinkConfirmationTitle}
  76. content={unlinkConfirmationText}
  77. unlinkPath={unlinkPath}
  78. handleHide={handleModalHide}
  79. />
  80. </div>
  81. )
  82. }
  83. type ActionButtonProps = {
  84. hasFeature?: boolean
  85. linked?: boolean
  86. handleUnlinkClick: () => void
  87. linkPath: string
  88. disabled?: boolean
  89. }
  90. function ActionButton({
  91. hasFeature,
  92. linked,
  93. handleUnlinkClick,
  94. linkPath,
  95. disabled,
  96. }: ActionButtonProps) {
  97. const { t } = useTranslation()
  98. if (!hasFeature) {
  99. return (
  100. <Button
  101. bsStyle="info"
  102. href="/user/subscription/plans"
  103. onClick={trackUpgradeClick}
  104. >
  105. <span className="text-capitalize">{t('upgrade')}</span>
  106. </Button>
  107. )
  108. } else if (linked) {
  109. return (
  110. <Button bsStyle="danger" onClick={handleUnlinkClick} disabled={disabled}>
  111. {t('unlink')}
  112. </Button>
  113. )
  114. } else {
  115. return (
  116. <Button bsStyle="info" href={linkPath} disabled={disabled}>
  117. <span className="text-capitalize">{t('link')}</span>
  118. </Button>
  119. )
  120. }
  121. }
  122. type UnlinkConfirmModalProps = {
  123. show: boolean
  124. title: string
  125. content: string
  126. unlinkPath: string
  127. handleHide: () => void
  128. }
  129. function UnlinkConfirmationModal({
  130. show,
  131. title,
  132. content,
  133. unlinkPath,
  134. handleHide,
  135. }: UnlinkConfirmModalProps) {
  136. const { t } = useTranslation()
  137. const handleCancel = (
  138. event: React.MouseEvent<HTMLButtonElement & Button>
  139. ) => {
  140. event.preventDefault()
  141. handleHide()
  142. }
  143. return (
  144. <AccessibleModal show={show} onHide={handleHide}>
  145. <Modal.Header closeButton>
  146. <Modal.Title>{title}</Modal.Title>
  147. </Modal.Header>
  148. <Modal.Body className="modal-body-share">
  149. <p>{content}</p>
  150. </Modal.Body>
  151. <Modal.Footer>
  152. <form action={unlinkPath} method="POST" className="form-inline">
  153. <input type="hidden" name="_csrf" value={getMeta('ol-csrfToken')} />
  154. <Button onClick={handleCancel}>{t('cancel')}</Button>
  155. <Button type="submit" bsStyle="danger">
  156. {t('unlink')}
  157. </Button>
  158. </form>
  159. </Modal.Footer>
  160. </AccessibleModal>
  161. )
  162. }