index.tsx99 lines · main
| 1 | import { useParams } from 'common' |
| 2 | import { PropsWithChildren } from 'react' |
| 3 | import { cn } from 'ui' |
| 4 | |
| 5 | import { useAvailableIntegrations } from '../../Landing/useAvailableIntegrations' |
| 6 | import { FilesViewer } from './FilesViewer' |
| 7 | import { MarkdownContent } from './MarkdownContent' |
| 8 | import { InlineLinkClassName } from '@/components/ui/InlineLink' |
| 9 | |
| 10 | const getSiteUrlLabel = (url: string | undefined | null) => { |
| 11 | if (!url) return undefined |
| 12 | |
| 13 | try { |
| 14 | return new URL(url).origin |
| 15 | } catch (error) { |
| 16 | return undefined |
| 17 | } |
| 18 | } |
| 19 | |
| 20 | const isGithubHost = (url: string | undefined | null) => { |
| 21 | if (!url) return false |
| 22 | try { |
| 23 | const hostname = new URL(url).hostname.toLowerCase() |
| 24 | return hostname === 'github.com' || hostname.endsWith('.github.com') |
| 25 | } catch (error) { |
| 26 | return false |
| 27 | } |
| 28 | } |
| 29 | |
| 30 | /** |
| 31 | * [Joshen] This will serve as the overview tab for remotely fetched integrations |
| 32 | */ |
| 33 | export const IntegrationOverviewTabV2 = ({ children }: PropsWithChildren) => { |
| 34 | const { id } = useParams() |
| 35 | |
| 36 | const { data: allIntegrations } = useAvailableIntegrations() |
| 37 | const integration = allIntegrations.find((i) => i.id === id) |
| 38 | |
| 39 | if (!integration) { |
| 40 | return <div>Unsupported integration type</div> |
| 41 | } |
| 42 | |
| 43 | const { type, content, docsUrl, siteUrl, files = [] } = integration |
| 44 | |
| 45 | const docsUrlLabel = docsUrl?.includes('supabase.com/docs') |
| 46 | ? 'Briven Docs' |
| 47 | : isGithubHost(docsUrl) |
| 48 | ? 'GitHub Docs' |
| 49 | : 'Documentation' |
| 50 | const siteUrlLabel = getSiteUrlLabel(siteUrl) |
| 51 | |
| 52 | return ( |
| 53 | <div className="grid grid-cols-3 gap-x-8 px-10 py-8"> |
| 54 | <div className="col-span-2 flex flex-col gap-y-8"> |
| 55 | {files.length > 0 && <FilesViewer files={files} />} |
| 56 | <MarkdownContent integrationId={id} content={content} /> |
| 57 | {children} |
| 58 | </div> |
| 59 | |
| 60 | <div className="text-sm col-span-1 flex flex-col gap-y-8"> |
| 61 | <div className="flex flex-col gap-y-4"> |
| 62 | <p>Details</p> |
| 63 | |
| 64 | <div className="flex flex-col gap-y-1"> |
| 65 | <p className="font-mono uppercase text-foreground-light">Type</p> |
| 66 | <p className="capitalize">{type === 'oauth' ? 'OAuth' : type.replaceAll('_', ' ')}</p> |
| 67 | </div> |
| 68 | |
| 69 | <div className="flex flex-col gap-y-1"> |
| 70 | <p className="font-mono uppercase text-foreground-light">Built by</p> |
| 71 | <p className={cn(!integration.author.name && 'text-foreground-lighter')}> |
| 72 | {integration.author.name || 'Unknown Author'} |
| 73 | </p> |
| 74 | </div> |
| 75 | |
| 76 | {docsUrl && ( |
| 77 | <div className="flex flex-col gap-y-1"> |
| 78 | <p className="font-mono uppercase text-foreground-light">Docs</p> |
| 79 | <a target="_blank" rel="noreferrer" href={docsUrl} className={InlineLinkClassName}> |
| 80 | {docsUrlLabel} |
| 81 | </a> |
| 82 | </div> |
| 83 | )} |
| 84 | |
| 85 | {siteUrl && ( |
| 86 | <div className="flex flex-col gap-y-1"> |
| 87 | <p className="font-mono uppercase text-foreground-light">Website</p> |
| 88 | <a target="_blank" rel="noreferrer" href={siteUrl} className={InlineLinkClassName}> |
| 89 | {siteUrlLabel} |
| 90 | </a> |
| 91 | </div> |
| 92 | )} |
| 93 | </div> |
| 94 | |
| 95 | {/* Subsequent other actions here */} |
| 96 | </div> |
| 97 | </div> |
| 98 | ) |
| 99 | } |