integration-widget.tsx 3.8 KB

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