index.tsx 3.1 KB

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