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.

index.tsx 3.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109
  1. 'use client'
  2. import React from 'react'
  3. import type { Plugin } from '../types'
  4. import Icon from '../card/base/card-icon'
  5. import CornerMark from './base/corner-mark'
  6. import Title from './base/title'
  7. import OrgInfo from './base/org-info'
  8. import Description from './base/description'
  9. import Placeholder from './base/placeholder'
  10. import cn from '@/utils/classnames'
  11. import { useGetLanguage } from '@/context/i18n'
  12. import { getLanguage } from '@/i18n/language'
  13. import { useSingleCategories } from '../hooks'
  14. import { renderI18nObject } from '@/i18n'
  15. import { useMixedTranslation } from '@/app/components/plugins/marketplace/hooks'
  16. import Partner from '../base/badges/partner'
  17. import Verified from '../base/badges/verified'
  18. import { RiAlertFill } from '@remixicon/react'
  19. export type Props = {
  20. className?: string
  21. payload: Plugin
  22. titleLeft?: React.ReactNode
  23. installed?: boolean
  24. installFailed?: boolean
  25. hideCornerMark?: boolean
  26. descriptionLineRows?: number
  27. footer?: React.ReactNode
  28. isLoading?: boolean
  29. loadingFileName?: string
  30. locale?: string
  31. limitedInstall?: boolean
  32. }
  33. const Card = ({
  34. className,
  35. payload,
  36. titleLeft,
  37. installed,
  38. installFailed,
  39. hideCornerMark,
  40. descriptionLineRows = 2,
  41. footer,
  42. isLoading = false,
  43. loadingFileName,
  44. locale: localeFromProps,
  45. limitedInstall = false,
  46. }: Props) => {
  47. const defaultLocale = useGetLanguage()
  48. const locale = localeFromProps ? getLanguage(localeFromProps) : defaultLocale
  49. const { t } = useMixedTranslation(localeFromProps)
  50. const { categoriesMap } = useSingleCategories(t)
  51. const { category, type, name, org, label, brief, icon, verified, badges = [] } = payload
  52. const isBundle = !['plugin', 'model', 'tool', 'extension', 'agent-strategy'].includes(type)
  53. const cornerMark = isBundle ? categoriesMap.bundle?.label : categoriesMap[category]?.label
  54. const getLocalizedText = (obj: Record<string, string> | undefined) =>
  55. obj ? renderI18nObject(obj, locale) : ''
  56. const isPartner = badges.includes('partner')
  57. const wrapClassName = cn('hover-bg-components-panel-on-panel-item-bg relative overflow-hidden rounded-xl border-[0.5px] border-components-panel-border bg-components-panel-on-panel-item-bg shadow-xs', className)
  58. if (isLoading) {
  59. return (
  60. <Placeholder
  61. wrapClassName={wrapClassName}
  62. loadingFileName={loadingFileName!}
  63. />
  64. )
  65. }
  66. return (
  67. <div className={wrapClassName}>
  68. <div className={cn('p-4 pb-3', limitedInstall && 'pb-1')}>
  69. {!hideCornerMark && <CornerMark text={cornerMark} />}
  70. {/* Header */}
  71. <div className="flex">
  72. <Icon src={icon} installed={installed} installFailed={installFailed} />
  73. <div className="ml-3 w-0 grow">
  74. <div className="flex h-5 items-center">
  75. <Title title={getLocalizedText(label)} />
  76. {isPartner && <Partner className='ml-0.5 h-4 w-4' text={t('plugin.marketplace.partnerTip')} />}
  77. {verified && <Verified className='ml-0.5 h-4 w-4' text={t('plugin.marketplace.verifiedTip')} />}
  78. {titleLeft} {/* This can be version badge */}
  79. </div>
  80. <OrgInfo
  81. className="mt-0.5"
  82. orgName={org}
  83. packageName={name}
  84. />
  85. </div>
  86. </div>
  87. <Description
  88. className="mt-3"
  89. text={getLocalizedText(brief)}
  90. descriptionLineRows={descriptionLineRows}
  91. />
  92. {footer && <div>{footer}</div>}
  93. </div>
  94. {limitedInstall
  95. && <div className='relative flex h-8 items-center gap-x-2 px-3 after:absolute after:bottom-0 after:left-0 after:right-0 after:top-0 after:bg-toast-warning-bg after:opacity-40'>
  96. <RiAlertFill className='h-3 w-3 shrink-0 text-text-warning-secondary' />
  97. <p className='system-xs-regular z-10 grow text-text-secondary'>
  98. {t('plugin.installModal.installWarning')}
  99. </p>
  100. </div>}
  101. </div>
  102. )
  103. }
  104. export default React.memo(Card)