ApiKeysIllustrations.tsx 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. import { SupportCategories } from '@supabase/shared-types/out/constants'
  2. import { LOCAL_STORAGE_KEYS } from 'common'
  3. import { ExternalLink } from 'lucide-react'
  4. import {
  5. Card,
  6. CardContent,
  7. Separator,
  8. Table,
  9. TableBody,
  10. TableCell,
  11. TableHead,
  12. TableHeader,
  13. TableRow,
  14. } from 'ui'
  15. import { SupportLink } from '../Support/SupportLink'
  16. import { ApiKeyPill } from './ApiKeyPill'
  17. import { CreateNewAPIKeysButton } from './CreateNewAPIKeysButton'
  18. import { FeatureBanner } from '@/components/ui/FeatureBanner'
  19. import { InlineLink, InlineLinkClassName } from '@/components/ui/InlineLink'
  20. import { APIKeysData } from '@/data/api-keys/api-keys-query'
  21. // Mock API Keys for demo
  22. const mockApiKeys = [
  23. {
  24. id: 'mock-id-2',
  25. type: 'publishable',
  26. api_key: 'sb_publishable_ltaNA7nnVozoSCOcZIjg',
  27. name: 'web',
  28. },
  29. {
  30. id: 'mock-id-3',
  31. type: 'publishable',
  32. api_key: 'sb_publishable_YpotEpinEWsC2dI7FIKI',
  33. name: 'mobile',
  34. },
  35. {
  36. id: 'mock-id-1',
  37. type: 'secret',
  38. api_key: 'sb_secret_8I4Se•••••••••••••',
  39. name: 'backend_api',
  40. },
  41. ] as Extract<APIKeysData[number], { type: 'secret' | 'publishable' }>[]
  42. /**
  43. * Reusable table illustration component
  44. */
  45. const ApiKeysTableIllustration = () => {
  46. return (
  47. <Card className="w-full overflow-hidden opacity-60 pointer-events-none bg-surface-100">
  48. <CardContent className="p-0">
  49. <Table className="p-5">
  50. <TableHeader>
  51. <TableRow className="bg-200">
  52. <TableHead
  53. key=""
  54. className="text-left font-mono uppercase text-xs text-foreground-lighter h-auto py-2 overflow-hidden w-[180px]"
  55. >
  56. Name
  57. </TableHead>
  58. <TableHead className="text-left font-mono uppercase text-xs text-foreground-lighter h-auto py-2 pr-0">
  59. API Key
  60. </TableHead>
  61. <TableHead
  62. className="text-right font-mono uppercase text-xs text-foreground-lighter h-auto py-2"
  63. key="actions"
  64. />
  65. </TableRow>
  66. </TableHeader>
  67. <TableBody>
  68. {mockApiKeys.map((apiKey) => (
  69. <TableRow key={apiKey.id}>
  70. <TableCell className="py-2 w-[180px]">{apiKey.name}</TableCell>
  71. <TableCell className="py-2">
  72. <div className="flex flex-row gap-2">
  73. <ApiKeyPill apiKey={apiKey} />
  74. </div>
  75. </TableCell>
  76. <TableCell />
  77. </TableRow>
  78. ))}
  79. </TableBody>
  80. </Table>
  81. </CardContent>
  82. </Card>
  83. )
  84. }
  85. /**
  86. * Reusable illustration with gradient overlay component
  87. */
  88. const ApiKeysIllustrationWithOverlay = () => {
  89. return (
  90. <>
  91. {/* Gradient overlay - horizontal on desktop, vertical on mobile */}
  92. <div
  93. className="absolute inset-x-0 bottom-0 h-1/2 xl:h-full xl:inset-x-auto xl:-right-16 xl:top-3 w-full xl:w-2/3
  94. bg-linear-to-t xl:bg-linear-to-l
  95. from-background-alternative via-background-alternative/90 via-5% to-transparent
  96. z-3 pointer-events-none xl:max-w-[500px]"
  97. />
  98. <div className="absolute scale-100 left-10 -bottom-14 w-[720px] xl:w-[500px] xl:left-auto xl:right-[-200px] 2xl:-right-16 xl:top-[21px] xl:scale-75">
  99. <ApiKeysTableIllustration />
  100. </div>
  101. </>
  102. )
  103. }
  104. export const ApiKeysCreateCallout = () => {
  105. return (
  106. <FeatureBanner illustration={<ApiKeysIllustrationWithOverlay />} bgAlt>
  107. <div className="flex flex-col gap-0 z-2">
  108. <p className="text-sm text-foreground">Create API keys</p>
  109. <p className="text-sm text-foreground-lighter lg:max-w-sm 2xl:max-w-none">
  110. Use keys to authenticate requests to your app
  111. </p>
  112. <div className="mt-4">
  113. <CreateNewAPIKeysButton />
  114. </div>
  115. </div>
  116. </FeatureBanner>
  117. )
  118. }
  119. export const ApiKeysFeedbackBanner = () => {
  120. return (
  121. <FeatureBanner
  122. storageKey={LOCAL_STORAGE_KEYS.API_KEYS_FEEDBACK_DISMISSED}
  123. className="p-0! flex flex-col gap-0"
  124. dismissable
  125. >
  126. <div className="p-5">
  127. <p className="text-sm text-foreground">Your new API keys are here</p>
  128. <p className="text-sm text-foreground-lighter">
  129. We've updated our API keys to better support your application needs.{' '}
  130. <InlineLink
  131. href="https://github.com/orgs/briven/discussions/29260"
  132. className="inline-flex items-center gap-1"
  133. >
  134. Join the discussion on GitHub <ExternalLink aria-hidden size={14} strokeWidth={1.5} />
  135. </InlineLink>
  136. </p>
  137. </div>
  138. <Separator className="w-full" />
  139. <div className="px-5 py-2 bg-surface-200/30">
  140. <p className="text-sm text-foreground-lighter">
  141. Having trouble with the new API keys?{' '}
  142. <SupportLink
  143. className={InlineLinkClassName}
  144. queryParams={{
  145. category: SupportCategories.PROBLEM,
  146. subject: 'Help with API keys',
  147. }}
  148. >
  149. Contact support
  150. </SupportLink>
  151. </p>
  152. </div>
  153. </FeatureBanner>
  154. )
  155. }