index.tsx 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  1. import React, { useEffect, useState } from 'react'
  2. import { useTranslation } from 'react-i18next'
  3. import cn from 'classnames'
  4. import style from './style.module.css'
  5. import Modal from '@/app/components/base/modal'
  6. import useCopyToClipboard from '@/hooks/use-copy-to-clipboard'
  7. import copyStyle from '@/app/components/app/chat/copy-btn/style.module.css'
  8. import Tooltip from '@/app/components/base/tooltip'
  9. import { useAppContext } from '@/context/app-context'
  10. // const isDevelopment = process.env.NODE_ENV === 'development'
  11. type Props = {
  12. isShow: boolean
  13. onClose: () => void
  14. accessToken: string
  15. appBaseUrl: string
  16. }
  17. const OPTION_MAP = {
  18. iframe: {
  19. getContent: (url: string, token: string) =>
  20. `<iframe
  21. src="${url}/chatbot/${token}"
  22. style="width: 100%; height: 100%; min-height: 700px"
  23. frameborder="0"
  24. allow="microphone">
  25. </iframe>`,
  26. },
  27. scripts: {
  28. getContent: (url: string, token: string, isTestEnv?: boolean) =>
  29. `<script>
  30. window.difyChatbotConfig = { token: '${token}'${isTestEnv ? ', isDev: true' : ''} }
  31. </script>
  32. <script
  33. src="${url}/embed.min.js"
  34. id="${token}"
  35. defer>
  36. </script>`,
  37. },
  38. }
  39. const prefixEmbedded = 'appOverview.overview.appInfo.embedded'
  40. type Option = keyof typeof OPTION_MAP
  41. type OptionStatus = {
  42. iframe: boolean
  43. scripts: boolean
  44. }
  45. const Embedded = ({ isShow, onClose, appBaseUrl, accessToken }: Props) => {
  46. const { t } = useTranslation()
  47. const [option, setOption] = useState<Option>('iframe')
  48. const [isCopied, setIsCopied] = useState<OptionStatus>({ iframe: false, scripts: false })
  49. const [_, copy] = useCopyToClipboard()
  50. const { langeniusVersionInfo } = useAppContext()
  51. const isTestEnv = langeniusVersionInfo.current_env === 'TESTING' || langeniusVersionInfo.current_env === 'DEVELOPMENT'
  52. const onClickCopy = () => {
  53. copy(OPTION_MAP[option].getContent(appBaseUrl, accessToken, isTestEnv))
  54. setIsCopied({ ...isCopied, [option]: true })
  55. }
  56. // when toggle option, reset then copy status
  57. const resetCopyStatus = () => {
  58. const cache = { ...isCopied }
  59. Object.keys(cache).forEach((key) => {
  60. cache[key as keyof OptionStatus] = false
  61. })
  62. setIsCopied(cache)
  63. }
  64. useEffect(() => {
  65. resetCopyStatus()
  66. }, [isShow])
  67. return (
  68. <Modal
  69. title={t(`${prefixEmbedded}.title`)}
  70. isShow={isShow}
  71. onClose={onClose}
  72. className="!max-w-2xl w-[640px]"
  73. closable={true}
  74. >
  75. <div className="mb-4 mt-8 text-gray-900 text-[14px] font-medium leading-tight">
  76. {t(`${prefixEmbedded}.explanation`)}
  77. </div>
  78. <div className="flex gap-4 items-center">
  79. {Object.keys(OPTION_MAP).map((v, index) => {
  80. return (
  81. <div
  82. key={index}
  83. className={cn(
  84. style.option,
  85. style[`${v}Icon`],
  86. option === v && style.active,
  87. )}
  88. onClick={() => {
  89. setOption(v as Option)
  90. resetCopyStatus()
  91. }}
  92. ></div>
  93. )
  94. })}
  95. </div>
  96. <div className="mt-6 w-full bg-gray-100 rounded-lg flex-col justify-start items-start inline-flex">
  97. <div className="self-stretch pl-3 pr-1 py-1 bg-gray-50 rounded-tl-lg rounded-tr-lg border border-black border-opacity-5 justify-start items-center gap-2 inline-flex">
  98. <div className="grow shrink basis-0 text-slate-700 text-[13px] font-medium leading-none">
  99. {t(`${prefixEmbedded}.${option}`)}
  100. </div>
  101. <div className="p-2 rounded-lg justify-center items-center gap-1 flex">
  102. <Tooltip
  103. selector={'code-copy-feedback'}
  104. content={(isCopied[option] ? t(`${prefixEmbedded}.copied`) : t(`${prefixEmbedded}.copy`)) || ''}
  105. >
  106. <div className="w-8 h-8 cursor-pointer hover:bg-gray-100 rounded-lg">
  107. <div onClick={onClickCopy} className={`w-full h-full ${copyStyle.copyIcon} ${isCopied[option] ? copyStyle.copied : ''}`}></div>
  108. </div>
  109. </Tooltip>
  110. </div>
  111. </div>
  112. <div className="self-stretch p-3 justify-start items-start gap-2 inline-flex">
  113. <div className="grow shrink basis-0 text-slate-700 text-[13px] leading-tight font-mono">
  114. <pre className='select-text'>{OPTION_MAP[option].getContent(appBaseUrl, accessToken, isTestEnv)}</pre>
  115. </div>
  116. </div>
  117. </div>
  118. </Modal>
  119. )
  120. }
  121. export default Embedded