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