LangSelector.tsx 5.9 KB

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