index.tsx 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127
  1. 'use client'
  2. import type { FC } from 'react'
  3. import React, { useState } from 'react'
  4. import { useTranslation } from 'react-i18next'
  5. import cn from 'classnames'
  6. import useSWR from 'swr'
  7. import Progress from './progress'
  8. import Button from '@/app/components/base/button'
  9. import { LinkExternal02, XClose } from '@/app/components/base/icons/src/vender/line/general'
  10. import AccountSetting from '@/app/components/header/account-setting'
  11. import { fetchTenantInfo } from '@/service/common'
  12. import { IS_CE_EDITION } from '@/config'
  13. import { useProviderContext } from '@/context/provider-context'
  14. const APIKeyInfoPanel: FC = () => {
  15. const isCloud = !IS_CE_EDITION
  16. const { providers }: any = useProviderContext()
  17. const { t } = useTranslation()
  18. const [showSetAPIKeyModal, setShowSetAPIKeyModal] = useState(false)
  19. const [isShow, setIsShow] = useState(true)
  20. const { data: userInfo } = useSWR({ url: '/info' }, fetchTenantInfo)
  21. if (!userInfo)
  22. return null
  23. const hasBindAPI = userInfo?.providers?.find(({ token_is_set }) => token_is_set)
  24. if (hasBindAPI)
  25. return null
  26. // first show in trail and not used exhausted, else find the exhausted
  27. const [used, total, providerName] = (() => {
  28. if (!providers || !isCloud)
  29. return [0, 0, '']
  30. let used = 0
  31. let total = 0
  32. let trailProviderName = ''
  33. let hasFoundNotExhausted = false
  34. Object.keys(providers).forEach((providerName) => {
  35. if (hasFoundNotExhausted)
  36. return
  37. providers[providerName].providers.forEach(({ quota_type, quota_limit, quota_used }: any) => {
  38. if (quota_type === 'trial') {
  39. if (quota_limit !== quota_used)
  40. hasFoundNotExhausted = true
  41. used = quota_used
  42. total = quota_limit
  43. trailProviderName = providerName
  44. }
  45. })
  46. })
  47. return [used, total, trailProviderName]
  48. })()
  49. const usedPercent = Math.round(used / total * 100)
  50. const exhausted = isCloud && usedPercent === 100
  51. if (!(isShow))
  52. return null
  53. return (
  54. <div className={cn(exhausted ? 'bg-[#FEF3F2] border-[#FEE4E2]' : 'bg-[#EFF4FF] border-[#D1E0FF]', 'mb-6 relative rounded-2xl shadow-md border p-8 ')}>
  55. <div className={cn('text-[24px] text-gray-800 font-semibold', isCloud ? 'flex items-center h-8 space-x-1' : 'leading-8 mb-6')}>
  56. {isCloud && <em-emoji id={exhausted ? '🤔' : '😀'} />}
  57. {isCloud
  58. ? (
  59. <div>{t(`appOverview.apiKeyInfo.cloud.${exhausted ? 'exhausted' : 'trial'}.title`, { providerName })}</div>
  60. )
  61. : (
  62. <div>
  63. <div>{t('appOverview.apiKeyInfo.selfHost.title.row1')}</div>
  64. <div>{t('appOverview.apiKeyInfo.selfHost.title.row2')}</div>
  65. </div>
  66. )}
  67. </div>
  68. {isCloud && (
  69. <div className='mt-1 text-sm text-gray-600 font-normal'>{t(`appOverview.apiKeyInfo.cloud.${exhausted ? 'exhausted' : 'trial'}.description`)}</div>
  70. )}
  71. {/* Call times info */}
  72. {isCloud && (
  73. <div className='my-5'>
  74. <div className='flex items-center h-5 space-x-2 text-sm text-gray-700 font-medium'>
  75. <div>{t('appOverview.apiKeyInfo.callTimes')}</div>
  76. <div>·</div>
  77. <div className={cn('font-semibold', exhausted && 'text-[#D92D20]')}>{used}/{total}</div>
  78. </div>
  79. <Progress className='mt-2' value={usedPercent} />
  80. </div>
  81. )}
  82. <Button
  83. type='primary'
  84. className='space-x-2'
  85. onClick={() => {
  86. setShowSetAPIKeyModal(true)
  87. }}
  88. >
  89. <div className='text-sm font-medium'>{t('appOverview.apiKeyInfo.setAPIBtn')}</div>
  90. <LinkExternal02 className='w-4 h-4' />
  91. </Button>
  92. {!isCloud && (
  93. <a
  94. className='mt-2 flex items-center h-[26px] text-xs font-medium text-[#155EEF] p-1 space-x-1'
  95. href='https://cloud.dify.ai/apps'
  96. target='_blank'
  97. >
  98. <div>{t('appOverview.apiKeyInfo.tryCloud')}</div>
  99. <LinkExternal02 className='w-3 h-3' />
  100. </a>
  101. )}
  102. <div
  103. onClick={() => setIsShow(false)}
  104. className='absolute right-4 top-4 flex items-center justify-center w-8 h-8 cursor-pointer '>
  105. <XClose className='w-4 h-4 text-gray-500' />
  106. </div>
  107. {
  108. showSetAPIKeyModal && (
  109. <AccountSetting activeTab="provider" onCancel={async () => {
  110. setShowSetAPIKeyModal(false)
  111. }} />
  112. )
  113. }
  114. </div>
  115. )
  116. }
  117. export default React.memo(APIKeyInfoPanel)