index.tsx 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  1. import type { FC } from 'react'
  2. import { useTranslation } from 'react-i18next'
  3. import type {
  4. ModelProvider,
  5. TypeWithI18N,
  6. } from '../declarations'
  7. import { ConfigurateMethodEnum } from '../declarations'
  8. import {
  9. DEFAULT_BACKGROUND_COLOR,
  10. MODEL_PROVIDER_QUOTA_GET_FREE,
  11. modelTypeFormat,
  12. } from '../utils'
  13. import {
  14. useAnthropicBuyQuota,
  15. useFreeQuota,
  16. useLanguage,
  17. useUpdateModelProviders,
  18. } from '../hooks'
  19. import ModelBadge from '../model-badge'
  20. import ProviderIcon from '../provider-icon'
  21. import s from './index.module.css'
  22. import { Plus, Settings01 } from '@/app/components/base/icons/src/vender/line/general'
  23. import Button from '@/app/components/base/button'
  24. import { IS_CE_EDITION } from '@/config'
  25. type ProviderCardProps = {
  26. provider: ModelProvider
  27. onOpenModal: (configurateMethod: ConfigurateMethodEnum) => void
  28. }
  29. const TIP_MAP: { [k: string]: TypeWithI18N } = {
  30. minimax: {
  31. en_US: 'Earn 1 million tokens for free',
  32. zh_Hans: '免费获取 100 万个 token',
  33. },
  34. spark: {
  35. en_US: 'Earn 3 million tokens (v3.0) for free',
  36. zh_Hans: '免费获取 300 万个 token (v3.0)',
  37. },
  38. zhipuai: {
  39. en_US: 'Earn 10 million tokens for free',
  40. zh_Hans: '免费获取 1000 万个 token',
  41. },
  42. }
  43. const ProviderCard: FC<ProviderCardProps> = ({
  44. provider,
  45. onOpenModal,
  46. }) => {
  47. const { t } = useTranslation()
  48. const language = useLanguage()
  49. const updateModelProviders = useUpdateModelProviders()
  50. const handlePay = useAnthropicBuyQuota()
  51. const handleFreeQuotaSuccess = () => {
  52. updateModelProviders()
  53. }
  54. const handleFreeQuota = useFreeQuota(handleFreeQuotaSuccess)
  55. const configurateMethods = provider.configurate_methods.filter(method => method !== ConfigurateMethodEnum.fetchFromRemote)
  56. const canGetFreeQuota = MODEL_PROVIDER_QUOTA_GET_FREE.includes(provider.provider) && !IS_CE_EDITION && provider.system_configuration.enabled
  57. return (
  58. <div
  59. className='group relative flex flex-col justify-between px-4 py-3 h-[148px] border-[0.5px] border-black/5 rounded-xl shadow-xs hover:shadow-lg'
  60. style={{ background: provider.background || DEFAULT_BACKGROUND_COLOR }}
  61. >
  62. <div>
  63. <div className='py-0.5'>
  64. <ProviderIcon provider={provider} />
  65. </div>
  66. {
  67. provider.description && (
  68. <div className='mt-1 leading-4 text-xs text-black/[48]'>{provider.description[language] || provider.description.en_US}</div>
  69. )
  70. }
  71. </div>
  72. <div>
  73. <div className={`flex flex-wrap group-hover:hidden gap-0.5 ${canGetFreeQuota && 'pb-[18px]'}`}>
  74. {
  75. provider.supported_model_types.map(modelType => (
  76. <ModelBadge key={modelType}>
  77. {modelTypeFormat(modelType)}
  78. </ModelBadge>
  79. ))
  80. }
  81. {
  82. canGetFreeQuota && (
  83. <div className='absolute left-0 right-0 bottom-0 flex items-center h-[26px] px-4 bg-white/50 rounded-b-xl'>
  84. 📣&nbsp;
  85. <div
  86. className={`${s.vender} text-xs font-medium text-transparent truncate`}
  87. title={TIP_MAP[provider.provider][language]}
  88. >
  89. {TIP_MAP[provider.provider][language]}
  90. </div>
  91. </div>
  92. )
  93. }
  94. </div>
  95. {
  96. canGetFreeQuota && (
  97. <div className='hidden group-hover:block'>
  98. <Button
  99. className='mb-1 w-full h-7 text-xs'
  100. type='primary'
  101. onClick={() => handleFreeQuota(provider.provider)}
  102. >
  103. {t('common.modelProvider.getFreeTokens')}
  104. </Button>
  105. </div>
  106. )
  107. }
  108. <div className={`hidden group-hover:grid grid-cols-${configurateMethods.length} gap-1`}>
  109. {
  110. configurateMethods.map((method) => {
  111. if (method === ConfigurateMethodEnum.predefinedModel) {
  112. return (
  113. <Button
  114. key={method}
  115. className={'h-7 bg-white text-xs text-gray-700 shrink-0'}
  116. onClick={() => onOpenModal(method)}
  117. >
  118. <Settings01 className={`mr-[5px] w-3.5 h-3.5 ${s.icon}`} />
  119. <span className='text-xs inline-flex items-center justify-center overflow-ellipsis shrink-0'>{t('common.operation.setup')}</span>
  120. </Button>
  121. )
  122. }
  123. return (
  124. <Button
  125. key={method}
  126. className='px-0 h-7 bg-white text-xs text-gray-700'
  127. onClick={() => onOpenModal(method)}
  128. >
  129. <Plus className='mr-[5px] w-3.5 h-3.5' />
  130. {t('common.modelProvider.addModel')}
  131. </Button>
  132. )
  133. })
  134. }
  135. </div>
  136. </div>
  137. </div>
  138. )
  139. }
  140. export default ProviderCard