labs-experiments-widget.tsx 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  1. import { ReactNode, useCallback, useState } from 'react'
  2. import { useTranslation } from 'react-i18next'
  3. import Badge from '@/shared/components/badge'
  4. import { postJSON } from '@/infrastructure/fetch-json'
  5. import { Button } from 'react-bootstrap'
  6. import getMeta from '@/utils/meta'
  7. export type UserFeatures = {
  8. [key: string]: boolean
  9. }
  10. type IntegrationLinkingWidgetProps = {
  11. logo: ReactNode
  12. title: string
  13. description: string
  14. helpPath?: string
  15. labsEnabled?: boolean
  16. experimentName: string
  17. setErrorMessage: (message: string) => void
  18. }
  19. export function LabsExperimentWidget({
  20. logo,
  21. title,
  22. description,
  23. helpPath,
  24. labsEnabled,
  25. experimentName,
  26. setErrorMessage,
  27. }: IntegrationLinkingWidgetProps) {
  28. const { t } = useTranslation()
  29. const userFeatures = getMeta('ol-features') as UserFeatures
  30. const [enabled, setEnabled] = useState(() => {
  31. return userFeatures[experimentName] === true
  32. })
  33. const experimentsErrorMessage = t(
  34. 'we_are_unable_to_opt_you_into_this_experiment'
  35. )
  36. const handleEnable = useCallback(async () => {
  37. try {
  38. const enablePath = `/labs/participate/experiments/${experimentName}/opt-in`
  39. await postJSON(enablePath)
  40. setEnabled(true)
  41. } catch (err) {
  42. setErrorMessage(experimentsErrorMessage)
  43. }
  44. }, [experimentName, setErrorMessage, experimentsErrorMessage])
  45. const handleDisable = useCallback(async () => {
  46. try {
  47. const disablePath = `/labs/participate/experiments/${experimentName}/opt-out`
  48. await postJSON(disablePath)
  49. setEnabled(false)
  50. } catch (err) {
  51. setErrorMessage(experimentsErrorMessage)
  52. }
  53. }, [experimentName, setErrorMessage, experimentsErrorMessage])
  54. return (
  55. <div className="labs-experiment-widget-container">
  56. <div className="p-2">{logo}</div>
  57. <div className="description-container">
  58. <div className="title-row">
  59. <h3 className="h4">{title}</h3>
  60. {enabled && <Badge bsStyle="info">{t('enabled')}</Badge>}
  61. </div>
  62. <p className="small">
  63. {description}{' '}
  64. {helpPath && (
  65. <a href={helpPath} target="_blank" rel="noreferrer">
  66. {t('learn_more')}
  67. </a>
  68. )}
  69. </p>
  70. </div>
  71. <div>
  72. {labsEnabled && (
  73. <ActionButton
  74. enabled={enabled}
  75. handleDisable={handleDisable}
  76. handleEnable={handleEnable}
  77. />
  78. )}
  79. </div>
  80. </div>
  81. )
  82. }
  83. type ActionButtonProps = {
  84. enabled?: boolean
  85. handleEnable: () => void
  86. handleDisable: () => void
  87. }
  88. function ActionButton({
  89. enabled,
  90. handleEnable,
  91. handleDisable,
  92. }: ActionButtonProps) {
  93. const { t } = useTranslation()
  94. if (enabled) {
  95. return (
  96. <Button
  97. bsStyle="secondary"
  98. onClick={handleDisable}
  99. className="btn btn-secondary"
  100. >
  101. {t('turn_off')}
  102. </Button>
  103. )
  104. } else {
  105. return (
  106. <Button
  107. bsStyle="primary"
  108. onClick={handleEnable}
  109. className="btn btn-primary"
  110. >
  111. {t('turn_on')}
  112. </Button>
  113. )
  114. }
  115. }
  116. export default LabsExperimentWidget