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