integration-widget.tsx 3.9 KB

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