mirror of
https://github.com/AtHeartEngineering/bandada.git
synced 2026-01-09 20:28:06 -05:00
feat: add UI components to support admin apikey interactions
This commit is contained in:
@@ -10,6 +10,7 @@
|
||||
"dependencies": {
|
||||
"@bandada/credentials": "2.1.1",
|
||||
"@bandada/utils": "2.1.1",
|
||||
"@chakra-ui/icons": "^2.1.1",
|
||||
"@chakra-ui/react": "^2.5.1",
|
||||
"@chakra-ui/styled-system": "^2.0.0",
|
||||
"@chakra-ui/theme-tools": "^2.0.16",
|
||||
|
||||
188
apps/dashboard/src/components/api-key.tsx
Normal file
188
apps/dashboard/src/components/api-key.tsx
Normal file
@@ -0,0 +1,188 @@
|
||||
import { useEffect, useState } from "react"
|
||||
import {
|
||||
Box,
|
||||
Flex,
|
||||
Switch,
|
||||
useClipboard,
|
||||
useToast,
|
||||
IconButton,
|
||||
Tooltip,
|
||||
Text
|
||||
} from "@chakra-ui/react"
|
||||
import { ViewIcon, CopyIcon, RepeatIcon, CheckIcon } from "@chakra-ui/icons"
|
||||
import { ApiKeyActions } from "@bandada/utils"
|
||||
import { Admin } from "../types"
|
||||
import { getAdmin, updateApiKey } from "../api/bandadaAPI"
|
||||
|
||||
export default function ApiKeyComponent({
|
||||
adminId
|
||||
}: {
|
||||
adminId: string
|
||||
}): JSX.Element {
|
||||
const [admin, setAdmin] = useState<Admin>()
|
||||
const [apiKey, setApiKey] = useState("")
|
||||
const [isEnabled, setIsEnabled] = useState(false)
|
||||
const [isCopied, setIsCopied] = useState(false)
|
||||
const { onCopy } = useClipboard(apiKey)
|
||||
const toast = useToast()
|
||||
|
||||
useEffect(() => {
|
||||
getAdmin(adminId).then((admin) => {
|
||||
if (admin) {
|
||||
setAdmin(admin)
|
||||
setApiKey(!admin.apiKey ? "" : admin.apiKey)
|
||||
setIsEnabled(admin.apiEnabled)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
if (isCopied) {
|
||||
const timer = setTimeout(() => {
|
||||
setIsCopied(false)
|
||||
}, 2000)
|
||||
return () => clearTimeout(timer)
|
||||
}
|
||||
}, [isCopied])
|
||||
|
||||
const showToast = (
|
||||
title: string,
|
||||
description: string,
|
||||
status: "info" | "warning" | "success" | "error",
|
||||
duration = 2000,
|
||||
position: "top" | "bottom" = "top"
|
||||
) => {
|
||||
toast({
|
||||
title,
|
||||
description,
|
||||
status,
|
||||
duration,
|
||||
isClosable: true,
|
||||
position
|
||||
})
|
||||
}
|
||||
|
||||
const handleCopy = () => {
|
||||
onCopy()
|
||||
setIsCopied(true)
|
||||
}
|
||||
|
||||
const handleRefresh = async () => {
|
||||
if (admin) {
|
||||
const newApiKey = await updateApiKey(
|
||||
admin.id,
|
||||
ApiKeyActions.Generate
|
||||
)
|
||||
if (!newApiKey) {
|
||||
showToast(
|
||||
"Something went wrong",
|
||||
"API Key has not been refreshed",
|
||||
"error"
|
||||
)
|
||||
} else {
|
||||
showToast(
|
||||
"API Key refresh",
|
||||
"Successfully refreshed",
|
||||
"success"
|
||||
)
|
||||
setApiKey(newApiKey)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const showApiKey = () => {
|
||||
if (admin && admin.apiKey) {
|
||||
showToast(
|
||||
"API Key",
|
||||
`Your API key is: ${admin.apiKey}`,
|
||||
"info",
|
||||
2500,
|
||||
"top"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const toggleIsEnabled = async () => {
|
||||
if (admin) {
|
||||
let toastTitle = ""
|
||||
let toastDescription = ""
|
||||
let action = ApiKeyActions.Enable
|
||||
|
||||
if (!admin.apiKey) {
|
||||
await updateApiKey(admin.id, ApiKeyActions.Generate)
|
||||
toastTitle = "API Key Generated"
|
||||
toastDescription = "A new API key has been generated."
|
||||
} else {
|
||||
action = isEnabled
|
||||
? ApiKeyActions.Disable
|
||||
: ApiKeyActions.Enable
|
||||
await updateApiKey(admin.id, action)
|
||||
toastTitle =
|
||||
action === ApiKeyActions.Enable
|
||||
? "API Key Enabled"
|
||||
: "API Key Disabled"
|
||||
toastDescription =
|
||||
action === ApiKeyActions.Enable
|
||||
? "API key has been enabled."
|
||||
: "API key has been disabled."
|
||||
}
|
||||
|
||||
showToast(toastTitle, toastDescription, "success")
|
||||
setIsEnabled((prevState) => !prevState)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Flex
|
||||
align="center"
|
||||
justify="space-between"
|
||||
p={4}
|
||||
borderRadius="md"
|
||||
minWidth="300px"
|
||||
height="48px"
|
||||
>
|
||||
<Flex align="center" flexGrow={1} justify="center">
|
||||
<Box>
|
||||
<Text
|
||||
fontWeight="bold"
|
||||
colorScheme="primary"
|
||||
color={isEnabled ? "balticSea.900" : "balticSea.400"}
|
||||
>
|
||||
API Key
|
||||
</Text>
|
||||
</Box>
|
||||
<Switch
|
||||
isChecked={isEnabled}
|
||||
onChange={toggleIsEnabled}
|
||||
mx={4}
|
||||
/>
|
||||
|
||||
{isEnabled && (
|
||||
<Flex align="center">
|
||||
<Tooltip label="Show API Key">
|
||||
<IconButton
|
||||
icon={<ViewIcon />}
|
||||
onClick={showApiKey}
|
||||
aria-label="View API Key"
|
||||
/>
|
||||
</Tooltip>
|
||||
<IconButton
|
||||
icon={isCopied ? <CheckIcon /> : <CopyIcon />}
|
||||
onClick={handleCopy}
|
||||
ml={2}
|
||||
aria-label="Copy API Key"
|
||||
isDisabled={!isEnabled}
|
||||
/>
|
||||
<IconButton
|
||||
icon={<RepeatIcon />}
|
||||
onClick={handleRefresh}
|
||||
ml={2}
|
||||
aria-label="Refresh API Key"
|
||||
isDisabled={!isEnabled}
|
||||
/>
|
||||
</Flex>
|
||||
)}
|
||||
</Flex>
|
||||
</Flex>
|
||||
)
|
||||
}
|
||||
@@ -27,6 +27,7 @@ import GroupCard from "../components/group-card"
|
||||
import { AuthContext } from "../context/auth-context"
|
||||
import { Group } from "../types"
|
||||
import GoerliGroupCard from "../components/goerli-group"
|
||||
import ApiKeyComponent from "../components/api-key"
|
||||
|
||||
export default function GroupsPage(): JSX.Element {
|
||||
const { admin } = useContext(AuthContext)
|
||||
@@ -115,6 +116,8 @@ export default function GroupsPage(): JSX.Element {
|
||||
</Button>
|
||||
</HStack>
|
||||
|
||||
<ApiKeyComponent adminId={admin?.id!} />
|
||||
|
||||
<Button
|
||||
variant="solid"
|
||||
colorScheme="primary"
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
export type Admin = {
|
||||
address: string
|
||||
id: string
|
||||
address: string
|
||||
apiKey: string | null
|
||||
apiEnabled: boolean
|
||||
}
|
||||
|
||||
export default Admin
|
||||
|
||||
25
yarn.lock
25
yarn.lock
@@ -2685,6 +2685,30 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@chakra-ui/icon@npm:3.2.0":
|
||||
version: 3.2.0
|
||||
resolution: "@chakra-ui/icon@npm:3.2.0"
|
||||
dependencies:
|
||||
"@chakra-ui/shared-utils": "npm:2.0.5"
|
||||
peerDependencies:
|
||||
"@chakra-ui/system": ">=2.0.0"
|
||||
react: ">=18"
|
||||
checksum: 10/b63eec1cb00b3f92ab64927fef96b60563ae32ae5df4a29edfe8cda4ba1c5aeb980c608b5a12e2376eaeb117a12362a45dd5e974b87ef72937dd897de7dc272f
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@chakra-ui/icons@npm:^2.1.1":
|
||||
version: 2.1.1
|
||||
resolution: "@chakra-ui/icons@npm:2.1.1"
|
||||
dependencies:
|
||||
"@chakra-ui/icon": "npm:3.2.0"
|
||||
peerDependencies:
|
||||
"@chakra-ui/system": ">=2.0.0"
|
||||
react: ">=18"
|
||||
checksum: 10/30c8644e1e80a1d24ce5fa081673da726a2b26e7dc62fa90f318f478efdd4537ef31db4bf4f6d5afc76ad6d815fdb6a1b6a394a353fc4ba99af28cf9116a6186
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@chakra-ui/image@npm:2.0.15":
|
||||
version: 2.0.15
|
||||
resolution: "@chakra-ui/image@npm:2.0.15"
|
||||
@@ -13923,6 +13947,7 @@ __metadata:
|
||||
dependencies:
|
||||
"@bandada/credentials": "npm:2.1.1"
|
||||
"@bandada/utils": "npm:2.1.1"
|
||||
"@chakra-ui/icons": "npm:^2.1.1"
|
||||
"@chakra-ui/react": "npm:^2.5.1"
|
||||
"@chakra-ui/styled-system": "npm:^2.0.0"
|
||||
"@chakra-ui/theme-tools": "npm:^2.0.16"
|
||||
|
||||
Reference in New Issue
Block a user