index.tsx 5.1 KB

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