labs-experiments-widget.tsx 4.2 KB

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