labs-experiments-widget.tsx 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. import { ReactNode, useCallback } from 'react'
  2. import { useTranslation } from 'react-i18next'
  3. import OLBadge from '@/features/ui/components/ol/ol-badge'
  4. import OLTooltip from '@/features/ui/components/ol/ol-tooltip'
  5. import { postJSON } from '@/infrastructure/fetch-json'
  6. import OLButton from '@/features/ui/components/ol/ol-button'
  7. import getMeta from '@/utils/meta'
  8. type IntegrationLinkingWidgetProps = {
  9. logo: ReactNode
  10. title: string
  11. description: string
  12. helpPath?: string
  13. labsEnabled?: boolean
  14. experimentName: string
  15. setErrorMessage: (message: string) => void
  16. optedIn: boolean
  17. setOptedIn: (optedIn: boolean) => void
  18. }
  19. export function LabsExperimentWidget({
  20. logo,
  21. title,
  22. description,
  23. helpPath,
  24. labsEnabled,
  25. experimentName,
  26. setErrorMessage,
  27. optedIn,
  28. setOptedIn,
  29. }: IntegrationLinkingWidgetProps) {
  30. const { t } = useTranslation()
  31. const experimentsErrorMessage = t(
  32. 'we_are_unable_to_opt_you_into_this_experiment'
  33. )
  34. const allowedExperiments = getMeta('ol-allowedExperiments')
  35. const disabled = !allowedExperiments.includes(experimentName) && !optedIn
  36. const handleEnable = useCallback(async () => {
  37. try {
  38. const enablePath = `/labs/participate/experiments/${experimentName}/opt-in`
  39. await postJSON(enablePath)
  40. setOptedIn(true)
  41. } catch (err) {
  42. setErrorMessage(experimentsErrorMessage)
  43. }
  44. }, [experimentName, setErrorMessage, experimentsErrorMessage, setOptedIn])
  45. const handleDisable = useCallback(async () => {
  46. try {
  47. const disablePath = `/labs/participate/experiments/${experimentName}/opt-out`
  48. await postJSON(disablePath)
  49. setOptedIn(false)
  50. } catch (err) {
  51. setErrorMessage(experimentsErrorMessage)
  52. }
  53. }, [experimentName, setErrorMessage, experimentsErrorMessage, setOptedIn])
  54. return (
  55. <div
  56. className={`labs-experiment-widget-container ${disabled ? 'disabled-experiment' : ''}`}
  57. >
  58. <div className="experiment-logo-container">{logo}</div>
  59. <div className="description-container">
  60. <div className="title-row">
  61. <h3 className="h4">{title}</h3>
  62. {optedIn && <OLBadge bg="info">{t('enabled')}</OLBadge>}
  63. </div>
  64. <p className="small">
  65. {description}{' '}
  66. {helpPath && (
  67. <a href={helpPath} target="_blank" rel="noreferrer">
  68. {t('learn_more')}
  69. </a>
  70. )}
  71. </p>
  72. </div>
  73. {disabled && (
  74. <div className="disabled-explanation">{t('experiment_full')}</div>
  75. )}
  76. <div>
  77. {labsEnabled && (
  78. <ActionButton
  79. optedIn={optedIn}
  80. handleDisable={handleDisable}
  81. handleEnable={handleEnable}
  82. disabled={disabled}
  83. />
  84. )}
  85. </div>
  86. </div>
  87. )
  88. }
  89. type ActionButtonProps = {
  90. optedIn?: boolean
  91. disabled?: boolean
  92. handleEnable: () => void
  93. handleDisable: () => void
  94. }
  95. function ActionButton({
  96. optedIn,
  97. disabled,
  98. handleEnable,
  99. handleDisable,
  100. }: ActionButtonProps) {
  101. const { t } = useTranslation()
  102. if (optedIn) {
  103. return (
  104. <OLButton variant="secondary" onClick={handleDisable}>
  105. {t('turn_off')}
  106. </OLButton>
  107. )
  108. } else if (disabled) {
  109. const tooltipableButton = (
  110. <div className="d-inline-block">
  111. <OLButton variant="primary" disabled>
  112. {t('turn_on')}
  113. </OLButton>
  114. </div>
  115. )
  116. return (
  117. <OLTooltip
  118. id="experiment-disabled"
  119. description={t('this_experiment_isnt_accepting_new_participants')}
  120. overlayProps={{ delay: 0 }}
  121. >
  122. {tooltipableButton}
  123. </OLTooltip>
  124. )
  125. } else {
  126. return (
  127. <OLButton variant="primary" onClick={handleEnable}>
  128. {t('turn_on')}
  129. </OLButton>
  130. )
  131. }
  132. }
  133. export default LabsExperimentWidget