LangSelector.tsx176 lines · main
1import { PermissionAction } from '@supabase/shared-types/out/constants'
2import { useParams } from 'common'
3import { EyeOff, Key } from 'lucide-react'
4import { useMemo } from 'react'
5import {
6 Button,
7 DropdownMenu,
8 DropdownMenuContent,
9 DropdownMenuGroup,
10 DropdownMenuLabel,
11 DropdownMenuRadioGroup,
12 DropdownMenuRadioItem,
13 DropdownMenuSeparator,
14 DropdownMenuTrigger,
15 ToggleGroup,
16 ToggleGroupItem,
17} from 'ui'
18
19import type { ShowApiKey } from '@/components/interfaces/Docs/Docs.types'
20import { useAPIKeysQuery } from '@/data/api-keys/api-keys-query'
21import { useAsyncCheckPermissions } from '@/hooks/misc/useCheckPermissions'
22
23const DEFAULT_KEY = { name: 'hide', key: 'BRIVEN_KEY' }
24
25interface LangSelectorProps {
26 selectedLang: 'js' | 'bash'
27 selectedApiKey: ShowApiKey
28 setSelectedLang: (selectedLang: 'js' | 'bash') => void
29 setSelectedApiKey: (key: ShowApiKey) => void
30}
31
32export const LangSelector = ({
33 selectedLang,
34 selectedApiKey,
35 setSelectedLang,
36 setSelectedApiKey,
37}: LangSelectorProps) => {
38 const { ref: projectRef } = useParams()
39
40 const { can: canReadAPIKeys } = useAsyncCheckPermissions(PermissionAction.SECRETS_READ, '*')
41 const { data: apiKeys = [], isPending: isLoadingAPIKeys } = useAPIKeysQuery(
42 {
43 projectRef,
44 reveal: false,
45 },
46 { enabled: canReadAPIKeys }
47 )
48
49 const legacyKeys = useMemo(() => apiKeys.filter(({ type }) => type === 'legacy'), [apiKeys])
50 const publishableKeys = useMemo(
51 () => apiKeys.filter(({ type }) => type === 'publishable'),
52 [apiKeys]
53 )
54 const secretKeys = useMemo(() => apiKeys.filter(({ type }) => type === 'secret'), [apiKeys])
55
56 return (
57 <div className="flex items-center gap-x-1">
58 <ToggleGroup
59 type="single"
60 value={selectedLang}
61 variant="outline"
62 onValueChange={(value) => {
63 if (value) setSelectedLang(value as 'js' | 'bash')
64 }}
65 size="sm"
66 className="flex-1 flex"
67 >
68 <ToggleGroupItem value="js" className="flex-1 px-2 py-1 h-7 text-xs">
69 JavaScript
70 </ToggleGroupItem>
71 <ToggleGroupItem value="bash" className="flex-1 px-2 py-1 h-7 text-xs">
72 Bash
73 </ToggleGroupItem>
74 </ToggleGroup>
75 {selectedLang == 'bash' ? (
76 canReadAPIKeys &&
77 !isLoadingAPIKeys &&
78 apiKeys &&
79 apiKeys.length > 0 && (
80 <DropdownMenu>
81 <DropdownMenuTrigger asChild>
82 <Button type="default" size="tiny" className="p-0 h-7 w-7">
83 {selectedApiKey.key === DEFAULT_KEY.key ? (
84 <EyeOff size={12} strokeWidth={1.5} />
85 ) : (
86 <Key size={12} strokeWidth={1.5} />
87 )}
88 </Button>
89 </DropdownMenuTrigger>
90 <DropdownMenuContent align="end" side="bottom" className="w-48">
91 <DropdownMenuRadioGroup value={selectedApiKey.key}>
92 <DropdownMenuRadioItem
93 key="hide"
94 value={DEFAULT_KEY.key}
95 onClick={() => setSelectedApiKey(DEFAULT_KEY)}
96 >
97 Hide keys
98 </DropdownMenuRadioItem>
99
100 {publishableKeys.length > 0 && (
101 <>
102 <DropdownMenuSeparator />
103 <DropdownMenuLabel>Publishable keys</DropdownMenuLabel>
104 {publishableKeys.map((key) => {
105 const value = key.api_key
106 return (
107 <DropdownMenuRadioItem
108 key={key.id}
109 value={value}
110 onClick={() =>
111 setSelectedApiKey({
112 name: `Publishable key: ${key.name}`,
113 key: value,
114 })
115 }
116 >
117 {key.name}
118 </DropdownMenuRadioItem>
119 )
120 })}
121 </>
122 )}
123
124 {secretKeys.length > 0 && (
125 <>
126 <DropdownMenuSeparator />
127 <DropdownMenuLabel>Secret keys</DropdownMenuLabel>
128 {secretKeys.map((key) => {
129 const value = key.prefix + '...'
130 return (
131 <DropdownMenuRadioItem
132 key={key.id}
133 value={value}
134 onClick={() =>
135 setSelectedApiKey({ name: `Secret key: ${key.name}`, key: value })
136 }
137 >
138 {key.name}
139 </DropdownMenuRadioItem>
140 )
141 })}
142 </>
143 )}
144
145 {legacyKeys.length > 0 && (
146 <>
147 <DropdownMenuSeparator />
148 <DropdownMenuGroup>
149 <DropdownMenuLabel>JWT-based legacy keys</DropdownMenuLabel>
150 {legacyKeys.map((key) => {
151 const value = key.api_key
152 return (
153 <DropdownMenuRadioItem
154 key={key.id}
155 value={value}
156 onClick={() =>
157 setSelectedApiKey({ name: `Legacy key: ${key.name}`, key: value })
158 }
159 >
160 {key.name}
161 </DropdownMenuRadioItem>
162 )
163 })}
164 </DropdownMenuGroup>
165 </>
166 )}
167 </DropdownMenuRadioGroup>
168 </DropdownMenuContent>
169 </DropdownMenu>
170 )
171 ) : (
172 <div className="w-7 h-7" />
173 )}
174 </div>
175 )
176}