index.tsx 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. import { useState } from 'react'
  2. import cn from 'classnames'
  3. import { useContext } from 'use-context-selector'
  4. import { useTranslation } from 'react-i18next'
  5. import Indicator from '../../../indicator'
  6. import OpenaiProvider from '../openai-provider'
  7. import AzureProvider from '../azure-provider'
  8. import type { ValidatedStatusState } from '../provider-input/useValidateToken'
  9. import { ValidatedStatus } from '../provider-input/useValidateToken'
  10. import s from './index.module.css'
  11. import type { Provider, ProviderAzureToken } from '@/models/common'
  12. import { ProviderName } from '@/models/common'
  13. import { updateProviderAIKey } from '@/service/common'
  14. import { ToastContext } from '@/app/components/base/toast'
  15. type IProviderItemProps = {
  16. icon: string
  17. name: string
  18. provider: Provider
  19. activeId: string
  20. onActive: (v: string) => void
  21. onSave: () => void
  22. }
  23. const ProviderItem = ({
  24. activeId,
  25. icon,
  26. name,
  27. provider,
  28. onActive,
  29. onSave,
  30. }: IProviderItemProps) => {
  31. const { t } = useTranslation()
  32. const [validatedStatus, setValidatedStatus] = useState<ValidatedStatusState>()
  33. const [loading, setLoading] = useState(false)
  34. const { notify } = useContext(ToastContext)
  35. const [token, setToken] = useState<ProviderAzureToken | string>(
  36. provider.provider_name === 'azure_openai'
  37. ? { openai_api_base: '', openai_api_key: '' }
  38. : '',
  39. )
  40. const id = `${provider.provider_name}-${provider.provider_type}`
  41. const isOpen = id === activeId
  42. const comingSoon = false
  43. const isValid = provider.is_valid
  44. const providerTokenHasSetted = () => {
  45. if (provider.provider_name === ProviderName.AZURE_OPENAI) {
  46. return (provider.token && provider.token.openai_api_base && provider.token.openai_api_key)
  47. ? {
  48. openai_api_base: provider.token.openai_api_base,
  49. openai_api_key: provider.token.openai_api_key,
  50. }
  51. : undefined
  52. }
  53. if (provider.provider_name === ProviderName.OPENAI)
  54. return provider.token
  55. }
  56. const handleUpdateToken = async () => {
  57. if (loading)
  58. return
  59. if (validatedStatus?.status === ValidatedStatus.Success) {
  60. try {
  61. setLoading(true)
  62. await updateProviderAIKey({ url: `/workspaces/current/providers/${provider.provider_name}/token`, body: { token } })
  63. notify({ type: 'success', message: t('common.actionMsg.modifiedSuccessfully') })
  64. onActive('')
  65. }
  66. catch (e) {
  67. notify({ type: 'error', message: t('common.provider.saveFailed') })
  68. }
  69. finally {
  70. setLoading(false)
  71. onSave()
  72. }
  73. }
  74. }
  75. return (
  76. <div className='mb-2 border-[0.5px] border-gray-200 bg-gray-50 rounded-md'>
  77. <div className='flex items-center px-4 h-[52px] cursor-pointer border-b-[0.5px] border-b-gray-200'>
  78. <div className={cn(s[`icon-${icon}`], 'mr-3 w-6 h-6 rounded-md')} />
  79. <div className='grow text-sm font-medium text-gray-800'>{name}</div>
  80. {
  81. providerTokenHasSetted() && !comingSoon && !isOpen && (
  82. <div className='flex items-center mr-4'>
  83. {!isValid && <div className='text-xs text-[#D92D20]'>{t('common.provider.invalidApiKey')}</div>}
  84. <Indicator color={!isValid ? 'red' : 'green'} className='ml-2' />
  85. </div>
  86. )
  87. }
  88. {
  89. !comingSoon && !isOpen && (
  90. <div className='
  91. px-3 h-[28px] bg-white border border-gray-200 rounded-md cursor-pointer
  92. text-xs font-medium text-gray-700 flex items-center
  93. ' onClick={() => onActive(id)}>
  94. {providerTokenHasSetted() ? t('common.provider.editKey') : t('common.provider.addKey')}
  95. </div>
  96. )
  97. }
  98. {
  99. comingSoon && !isOpen && (
  100. <div className='
  101. flex items-center px-2 h-[22px] border border-[#444CE7] rounded-md
  102. text-xs font-medium text-[#444CE7]
  103. '>
  104. {t('common.provider.comingSoon')}
  105. </div>
  106. )
  107. }
  108. {
  109. isOpen && (
  110. <div className='flex items-center'>
  111. <div className='
  112. flex items-center
  113. mr-[5px] px-3 h-7 rounded-md cursor-pointer
  114. text-xs font-medium text-gray-700
  115. ' onClick={() => onActive('')} >
  116. {t('common.operation.cancel')}
  117. </div>
  118. <div className='
  119. flex items-center
  120. px-3 h-7 rounded-md cursor-pointer bg-primary-700
  121. text-xs font-medium text-white
  122. ' onClick={handleUpdateToken}>
  123. {t('common.operation.save')}
  124. </div>
  125. </div>
  126. )
  127. }
  128. </div>
  129. {
  130. provider.provider_name === ProviderName.OPENAI && isOpen && (
  131. <OpenaiProvider
  132. provider={provider}
  133. onValidatedStatus={v => setValidatedStatus(v)}
  134. onTokenChange={v => setToken(v)}
  135. />
  136. )
  137. }
  138. {
  139. provider.provider_name === ProviderName.AZURE_OPENAI && isOpen && (
  140. <AzureProvider
  141. provider={provider}
  142. onValidatedStatus={v => setValidatedStatus(v)}
  143. onTokenChange={v => setToken(v)}
  144. />
  145. )
  146. }
  147. </div>
  148. )
  149. }
  150. export default ProviderItem