MarketplaceDetailRail.tsx 2.6 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091
  1. import { ArrowUpRight } from 'lucide-react'
  2. import type { ReactNode } from 'react'
  3. import { cn } from 'ui'
  4. import { getMarketplaceType, getMarketplaceTypeLabel } from './Marketplace.constants'
  5. import type { IntegrationDefinition } from '@/components/interfaces/Integrations/Landing/Integrations.constants'
  6. interface RailRowProps {
  7. label: string
  8. value: ReactNode
  9. href?: string
  10. mono?: boolean
  11. }
  12. const RailRow = ({ label, value, href, mono }: RailRowProps) => {
  13. const valueCls = cn(
  14. 'flex items-center gap-1 text-sm',
  15. href ? 'text-brand-link' : 'text-foreground',
  16. mono && 'font-mono'
  17. )
  18. const content = (
  19. <>
  20. {value}
  21. {href && <ArrowUpRight size={11} />}
  22. </>
  23. )
  24. return (
  25. <div className="flex flex-col gap-1">
  26. <div className="text-xs text-foreground-lighter">{label}</div>
  27. {href ? (
  28. <a href={href} target="_blank" rel="noreferrer" className={valueCls}>
  29. {content}
  30. </a>
  31. ) : (
  32. <div className={valueCls}>{content}</div>
  33. )}
  34. </div>
  35. )
  36. }
  37. interface RailGroupProps {
  38. title: string
  39. children: ReactNode
  40. }
  41. const RailGroup = ({ title, children }: RailGroupProps) => (
  42. <div className="flex flex-col gap-3 border-b pb-4 last:border-b-0">
  43. <div className="font-mono text-[10px] uppercase tracking-widest text-foreground-lighter">
  44. {title}
  45. </div>
  46. <div className="flex flex-col gap-3">{children}</div>
  47. </div>
  48. )
  49. interface MarketplaceDetailRailProps {
  50. integration: IntegrationDefinition
  51. isInstalled: boolean
  52. }
  53. const tryHostname = (url: string | null | undefined) => {
  54. if (!url) return undefined
  55. try {
  56. return new URL(url).hostname
  57. } catch {
  58. return undefined
  59. }
  60. }
  61. export const MarketplaceDetailRail = ({ integration, isInstalled }: MarketplaceDetailRailProps) => {
  62. const typeLabel = getMarketplaceTypeLabel(getMarketplaceType(integration))
  63. const docsUrl = integration.docsUrl ?? undefined
  64. const siteUrl = integration.siteUrl ?? undefined
  65. const siteHost = tryHostname(siteUrl)
  66. return (
  67. <aside className="sticky top-6 flex flex-col gap-4 self-start text-sm">
  68. <RailGroup title="About">
  69. <RailRow label="Type" value={typeLabel} />
  70. <RailRow label="Built by" value={integration.author?.name || 'Briven'} />
  71. {isInstalled && <RailRow label="Status" value="Installed" />}
  72. </RailGroup>
  73. {(docsUrl || siteUrl) && (
  74. <RailGroup title="Links">
  75. {docsUrl && <RailRow label="Documentation" value="Read the docs" href={docsUrl} />}
  76. {siteUrl && siteHost && <RailRow label="Website" value={siteHost} href={siteUrl} />}
  77. </RailGroup>
  78. )}
  79. </aside>
  80. )
  81. }