MarketplaceSidebar.tsx 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119
  1. import { useParams } from 'common'
  2. import { ArrowUpRight, BookOpen, LayoutGrid, PlusSquare } from 'lucide-react'
  3. import Link from 'next/link'
  4. import { useRouter } from 'next/router'
  5. import { cn } from 'ui'
  6. import { useInstalledIntegrations } from '@/components/interfaces/Integrations/Landing/useInstalledIntegrations'
  7. import { DOCS_URL } from '@/lib/constants'
  8. const sectionLabelCls =
  9. 'px-2 pt-4 pb-1.5 font-mono text-xs uppercase tracking-wider text-foreground-lighter'
  10. interface SidebarLinkProps {
  11. href: string
  12. active?: boolean
  13. icon?: React.ReactNode
  14. label: React.ReactNode
  15. count?: number | string
  16. className?: string
  17. }
  18. const SidebarLink = ({ href, active, icon, label, count, className }: SidebarLinkProps) => (
  19. <Link
  20. href={href}
  21. className={cn(
  22. 'flex items-center justify-between rounded px-2 py-1 text-sm',
  23. 'text-foreground-light hover:bg-surface-200 hover:text-foreground',
  24. active && 'bg-surface-200 text-foreground',
  25. className
  26. )}
  27. >
  28. <span className="flex min-w-0 items-center gap-2">
  29. <span className="text-foreground-lighter">{icon}</span>
  30. {label}
  31. </span>
  32. {count !== undefined && (
  33. <span className="font-mono text-xs text-foreground-lighter">{count}</span>
  34. )}
  35. </Link>
  36. )
  37. const HELP_LINKS: Array<{ icon: React.ReactNode; label: string; href: string }> = [
  38. {
  39. icon: <BookOpen className="text-foreground-lighter" size={13} />,
  40. label: 'Integrations docs',
  41. href: `${DOCS_URL}/guides/integrations`,
  42. },
  43. {
  44. icon: <PlusSquare className="text-foreground-lighter" size={13} />,
  45. label: 'Build an integration',
  46. href: `${DOCS_URL}/guides/integrations/build-a-briven-oauth-integration`,
  47. },
  48. ]
  49. export const MarketplaceSidebar = () => {
  50. const router = useRouter()
  51. const { ref } = useParams()
  52. const { installedIntegrations } = useInstalledIntegrations()
  53. const baseHref = `/project/${ref}/integrations`
  54. const isDiscoverActive =
  55. router.pathname === '/project/[ref]/integrations' && !router.query.type && !router.query.source
  56. const activeIntegrationId = typeof router.query.id === 'string' ? router.query.id : undefined
  57. return (
  58. <aside className="grow flex h-full w-full flex-col gap-y-0.5 overflow-y-auto p-4 text-sm">
  59. <SidebarLink
  60. href={baseHref}
  61. active={isDiscoverActive}
  62. icon={<LayoutGrid size={13} />}
  63. label="Marketplace"
  64. />
  65. {installedIntegrations.length > 0 && (
  66. <>
  67. <div className={sectionLabelCls}>Installed · {installedIntegrations.length}</div>
  68. {installedIntegrations.map((integration) => {
  69. const isActive = activeIntegrationId === integration.id
  70. return (
  71. <Link
  72. key={integration.id}
  73. href={`${baseHref}/${integration.id}/overview`}
  74. aria-current={isActive ? 'page' : undefined}
  75. className={cn(
  76. 'flex items-center gap-2 rounded px-2 py-1 text-sm',
  77. 'text-foreground-light hover:bg-surface-200 hover:text-foreground',
  78. isActive && 'bg-surface-200 text-foreground'
  79. )}
  80. >
  81. <span className="relative flex h-[18px] w-[18px] shrink-0 items-center justify-center overflow-hidden rounded-[4px] border bg-white">
  82. {integration.icon({ className: 'p-0.5' })}
  83. </span>
  84. <span className="truncate">{integration.name}</span>
  85. </Link>
  86. )
  87. })}
  88. </>
  89. )}
  90. <div className={sectionLabelCls}>Resources</div>
  91. {HELP_LINKS.map(({ icon, label, href }) => (
  92. <a
  93. key={label}
  94. href={href}
  95. target="_blank"
  96. rel="noreferrer"
  97. className="flex items-center justify-between rounded px-2 py-1 text-sm text-foreground-light hover:bg-surface-200 hover:text-foreground"
  98. >
  99. <span className="flex items-center gap-2">
  100. {icon}
  101. {label}
  102. </span>
  103. <ArrowUpRight size={11} className="opacity-50" />
  104. </a>
  105. ))}
  106. </aside>
  107. )
  108. }