index.tsx 3.2 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697
  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 '@/hooks/use-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. export type Props = {
  19. className?: string
  20. payload: Plugin
  21. titleLeft?: React.ReactNode
  22. installed?: boolean
  23. installFailed?: boolean
  24. hideCornerMark?: boolean
  25. descriptionLineRows?: number
  26. footer?: React.ReactNode
  27. isLoading?: boolean
  28. loadingFileName?: string
  29. locale?: string
  30. }
  31. const Card = ({
  32. className,
  33. payload,
  34. titleLeft,
  35. installed,
  36. installFailed,
  37. hideCornerMark,
  38. descriptionLineRows = 2,
  39. footer,
  40. isLoading = false,
  41. loadingFileName,
  42. locale: localeFromProps,
  43. }: Props) => {
  44. const defaultLocale = useGetLanguage()
  45. const locale = localeFromProps ? getLanguage(localeFromProps) : defaultLocale
  46. const { t } = useMixedTranslation(localeFromProps)
  47. const { categoriesMap } = useSingleCategories(t)
  48. const { category, type, name, org, label, brief, icon, verified, badges = [] } = payload
  49. const isBundle = !['plugin', 'model', 'tool', 'extension', 'agent-strategy'].includes(type)
  50. const cornerMark = isBundle ? categoriesMap.bundle?.label : categoriesMap[category]?.label
  51. const getLocalizedText = (obj: Record<string, string> | undefined) =>
  52. obj ? renderI18nObject(obj, locale) : ''
  53. const isPartner = badges.includes('partner')
  54. const wrapClassName = cn('hover-bg-components-panel-on-panel-item-bg relative rounded-xl border-[0.5px] border-components-panel-border bg-components-panel-on-panel-item-bg p-4 pb-3 shadow-xs', className)
  55. if (isLoading) {
  56. return (
  57. <Placeholder
  58. wrapClassName={wrapClassName}
  59. loadingFileName={loadingFileName!}
  60. />
  61. )
  62. }
  63. return (
  64. <div className={wrapClassName}>
  65. {!hideCornerMark && <CornerMark text={cornerMark} />}
  66. {/* Header */}
  67. <div className="flex">
  68. <Icon src={icon} installed={installed} installFailed={installFailed} />
  69. <div className="ml-3 w-0 grow">
  70. <div className="flex h-5 items-center">
  71. <Title title={getLocalizedText(label)} />
  72. {isPartner && <Partner className='ml-0.5 h-4 w-4' text={t('plugin.marketplace.partnerTip')} />}
  73. {verified && <Verified className='ml-0.5 h-4 w-4' text={t('plugin.marketplace.verifiedTip')} />}
  74. {titleLeft} {/* This can be version badge */}
  75. </div>
  76. <OrgInfo
  77. className="mt-0.5"
  78. orgName={org}
  79. packageName={name}
  80. />
  81. </div>
  82. </div>
  83. <Description
  84. className="mt-3"
  85. text={getLocalizedText(brief)}
  86. descriptionLineRows={descriptionLineRows}
  87. />
  88. {footer && <div>{footer}</div>}
  89. </div>
  90. )
  91. }
  92. export default React.memo(Card)