MarketplaceDetail.tsx130 lines · main
1import { ArrowUpRight, BookOpen, Settings } from 'lucide-react'
2import { Button, cn } from 'ui'
3import { GenericSkeletonLoader, ShimmeringLoader } from 'ui-patterns'
4import { Admonition } from 'ui-patterns/admonition'
5
6import { MarketplaceDetailHero } from './MarketplaceDetailHero'
7import { MarketplaceDetailTopBar } from './MarketplaceDetailTopBar'
8import { OverviewTab } from './OverviewTab'
9import { InstallIntegrationSheet } from '@/components/interfaces/Integrations/Integration/IntegrationOverviewTabV2/InstallIntegrationSheet/InstallIntegrationSheet'
10import { InstallOAuthIntegrationButton } from '@/components/interfaces/Integrations/Integration/IntegrationOverviewTabV2/InstallIntegrationSheet/InstallOAuthIntegrationButton'
11import { useIntegrationDetail } from '@/components/interfaces/Integrations/Landing/useIntegrationDetail'
12import { UnknownInterface } from '@/components/ui/UnknownInterface'
13
14export const centeredContentClass = 'mx-auto w-full max-w-6xl px-6 xl:px-10'
15
16export const MarketplaceDetail = () => {
17 const {
18 ref,
19 activeRoute,
20 isKnownRoute,
21 layout,
22 tabs,
23 isReady,
24 isWrapperBlocked,
25 pageTitle,
26 pageSubTitle,
27 integration,
28 isInstalled,
29 isAvailableLoading,
30 isInstalledLoading,
31 Component,
32 } = useIntegrationDetail()
33
34 if (!isReady) return null
35 if (isWrapperBlocked) return <UnknownInterface urlBack={`/project/${ref}/integrations`} />
36
37 if (isAvailableLoading || isInstalledLoading) {
38 return (
39 <>
40 <MarketplaceDetailTopBar title="" />
41 <div className={cn(centeredContentClass, 'border-b bg-surface-75 pt-10')}>
42 <div className="mx-auto flex w-full max-w-6xl flex-col gap-3 pb-6">
43 <ShimmeringLoader className="h-9 w-64" />
44 <ShimmeringLoader className="h-4 w-96" />
45 </div>
46 </div>
47 <div className={cn(centeredContentClass, 'py-8')}>
48 <GenericSkeletonLoader />
49 </div>
50 </>
51 )
52 }
53
54 if (!integration) {
55 return (
56 <>
57 <MarketplaceDetailTopBar title="Integration not found" />
58 <div className={cn(centeredContentClass, 'py-8')}>
59 <Admonition type="warning" title="This integration is not currently available">
60 Please try again later or contact support if the problem persists.
61 </Admonition>
62 </div>
63 </>
64 )
65 }
66
67 const renderInstallAction = () => {
68 if (integration.type === 'oauth') {
69 return <InstallOAuthIntegrationButton integration={integration} />
70 }
71 if (isInstalled) {
72 return (
73 <Button type="outline" disabled icon={<Settings size={13} />}>
74 Installed
75 </Button>
76 )
77 }
78 return <InstallIntegrationSheet integration={integration} />
79 }
80
81 const CustomPageComponent = activeRoute !== 'overview' && isKnownRoute ? Component : null
82
83 return (
84 <>
85 <MarketplaceDetailTopBar
86 title={integration.name}
87 isInstalled={isInstalled}
88 actions={
89 <>
90 {integration.docsUrl && (
91 <Button
92 type="text"
93 size="tiny"
94 icon={<BookOpen size={13} />}
95 iconRight={<ArrowUpRight size={13} />}
96 asChild
97 >
98 <a href={integration.docsUrl} target="_blank" rel="noreferrer">
99 Docs
100 </a>
101 </Button>
102 )}
103 {renderInstallAction()}
104 </>
105 }
106 />
107
108 <MarketplaceDetailHero
109 integration={integration}
110 title={pageTitle}
111 subtitle={pageSubTitle}
112 tabs={tabs}
113 />
114
115 {activeRoute === 'overview' ? (
116 <div className={centeredContentClass}>
117 <OverviewTab integration={integration} isInstalled={isInstalled} />
118 </div>
119 ) : CustomPageComponent ? (
120 layout === 'constrained' ? (
121 <div className={centeredContentClass}>
122 <CustomPageComponent />
123 </div>
124 ) : (
125 <CustomPageComponent />
126 )
127 ) : null}
128 </>
129 )
130}