You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
  1. import {
  2. memo,
  3. useMemo,
  4. useState,
  5. } from 'react'
  6. import { useTranslation } from 'react-i18next'
  7. import {
  8. RiCheckLine,
  9. RiDeleteBinLine,
  10. RiEditLine,
  11. RiEqualizer2Line,
  12. } from '@remixicon/react'
  13. import Indicator from '@/app/components/header/indicator'
  14. import Badge from '@/app/components/base/badge'
  15. import ActionButton from '@/app/components/base/action-button'
  16. import Tooltip from '@/app/components/base/tooltip'
  17. import Button from '@/app/components/base/button'
  18. import Input from '@/app/components/base/input'
  19. import cn from '@/utils/classnames'
  20. import type { Credential } from '../types'
  21. import { CredentialTypeEnum } from '../types'
  22. type ItemProps = {
  23. credential: Credential
  24. disabled?: boolean
  25. onDelete?: (id: string) => void
  26. onEdit?: (id: string, values: Record<string, any>) => void
  27. onSetDefault?: (id: string) => void
  28. onRename?: (payload: {
  29. credential_id: string
  30. name: string
  31. }) => void
  32. disableRename?: boolean
  33. disableEdit?: boolean
  34. disableDelete?: boolean
  35. disableSetDefault?: boolean
  36. onItemClick?: (id: string) => void
  37. showSelectedIcon?: boolean
  38. selectedCredentialId?: string
  39. }
  40. const Item = ({
  41. credential,
  42. disabled,
  43. onDelete,
  44. onEdit,
  45. onSetDefault,
  46. onRename,
  47. disableRename,
  48. disableEdit,
  49. disableDelete,
  50. disableSetDefault,
  51. onItemClick,
  52. showSelectedIcon,
  53. selectedCredentialId,
  54. }: ItemProps) => {
  55. const { t } = useTranslation()
  56. const [renaming, setRenaming] = useState(false)
  57. const [renameValue, setRenameValue] = useState(credential.name)
  58. const isOAuth = credential.credential_type === CredentialTypeEnum.OAUTH2
  59. const showAction = useMemo(() => {
  60. return !(disableRename && disableEdit && disableDelete && disableSetDefault)
  61. }, [disableRename, disableEdit, disableDelete, disableSetDefault])
  62. const CredentialItem = (
  63. <div
  64. key={credential.id}
  65. className={cn(
  66. 'group flex h-8 items-center rounded-lg p-1 hover:bg-state-base-hover',
  67. renaming && 'bg-state-base-hover',
  68. (disabled || credential.not_allowed_to_use) && 'cursor-not-allowed opacity-50',
  69. )}
  70. onClick={() => {
  71. if (credential.not_allowed_to_use || disabled)
  72. return
  73. onItemClick?.(credential.id === '__workspace_default__' ? '' : credential.id)
  74. }}
  75. >
  76. {
  77. renaming && (
  78. <div className='flex w-full items-center space-x-1'>
  79. <Input
  80. wrapperClassName='grow rounded-[6px]'
  81. className='h-6'
  82. value={renameValue}
  83. onChange={e => setRenameValue(e.target.value)}
  84. placeholder={t('common.placeholder.input')}
  85. onClick={e => e.stopPropagation()}
  86. />
  87. <Button
  88. size='small'
  89. variant='primary'
  90. onClick={(e) => {
  91. e.stopPropagation()
  92. onRename?.({
  93. credential_id: credential.id,
  94. name: renameValue,
  95. })
  96. setRenaming(false)
  97. }}
  98. >
  99. {t('common.operation.save')}
  100. </Button>
  101. <Button
  102. size='small'
  103. onClick={(e) => {
  104. e.stopPropagation()
  105. setRenaming(false)
  106. }}
  107. >
  108. {t('common.operation.cancel')}
  109. </Button>
  110. </div>
  111. )
  112. }
  113. {
  114. !renaming && (
  115. <div className='flex w-0 grow items-center space-x-1.5'>
  116. {
  117. showSelectedIcon && (
  118. <div className='h-4 w-4'>
  119. {
  120. selectedCredentialId === credential.id && (
  121. <RiCheckLine className='h-4 w-4 text-text-accent' />
  122. )
  123. }
  124. </div>
  125. )
  126. }
  127. <Indicator
  128. className='ml-2 mr-1.5 shrink-0'
  129. color={credential.not_allowed_to_use ? 'gray' : 'green'}
  130. />
  131. <div
  132. className='system-md-regular truncate text-text-secondary'
  133. title={credential.name}
  134. >
  135. {credential.name}
  136. </div>
  137. {
  138. credential.is_default && (
  139. <Badge className='shrink-0'>
  140. {t('plugin.auth.default')}
  141. </Badge>
  142. )
  143. }
  144. </div>
  145. )
  146. }
  147. {
  148. credential.from_enterprise && (
  149. <Badge className='shrink-0'>
  150. Enterprise
  151. </Badge>
  152. )
  153. }
  154. {
  155. showAction && !renaming && (
  156. <div className='ml-2 hidden shrink-0 items-center group-hover:flex'>
  157. {
  158. !credential.is_default && !disableSetDefault && !credential.not_allowed_to_use && (
  159. <Button
  160. size='small'
  161. disabled={disabled}
  162. onClick={(e) => {
  163. e.stopPropagation()
  164. onSetDefault?.(credential.id)
  165. }}
  166. >
  167. {t('plugin.auth.setDefault')}
  168. </Button>
  169. )
  170. }
  171. {
  172. !disableRename && !credential.from_enterprise && !credential.not_allowed_to_use && (
  173. <Tooltip popupContent={t('common.operation.rename')}>
  174. <ActionButton
  175. disabled={disabled}
  176. onClick={(e) => {
  177. e.stopPropagation()
  178. setRenaming(true)
  179. setRenameValue(credential.name)
  180. }}
  181. >
  182. <RiEditLine className='h-4 w-4 text-text-tertiary' />
  183. </ActionButton>
  184. </Tooltip>
  185. )
  186. }
  187. {
  188. !isOAuth && !disableEdit && !credential.from_enterprise && !credential.not_allowed_to_use && (
  189. <Tooltip popupContent={t('common.operation.edit')}>
  190. <ActionButton
  191. disabled={disabled}
  192. onClick={(e) => {
  193. e.stopPropagation()
  194. onEdit?.(
  195. credential.id,
  196. {
  197. ...credential.credentials,
  198. __name__: credential.name,
  199. __credential_id__: credential.id,
  200. },
  201. )
  202. }}
  203. >
  204. <RiEqualizer2Line className='h-4 w-4 text-text-tertiary' />
  205. </ActionButton>
  206. </Tooltip>
  207. )
  208. }
  209. {
  210. !disableDelete && !credential.from_enterprise && (
  211. <Tooltip popupContent={t('common.operation.delete')}>
  212. <ActionButton
  213. className='hover:bg-transparent'
  214. disabled={disabled}
  215. onClick={(e) => {
  216. e.stopPropagation()
  217. onDelete?.(credential.id)
  218. }}
  219. >
  220. <RiDeleteBinLine className='h-4 w-4 text-text-tertiary hover:text-text-destructive' />
  221. </ActionButton>
  222. </Tooltip>
  223. )
  224. }
  225. </div>
  226. )
  227. }
  228. </div>
  229. )
  230. if (credential.not_allowed_to_use) {
  231. return (
  232. <Tooltip popupContent={t('plugin.auth.customCredentialUnavailable')}>
  233. {CredentialItem}
  234. </Tooltip>
  235. )
  236. }
  237. return (
  238. CredentialItem
  239. )
  240. }
  241. export default memo(Item)