"use client"; import React, { useState, useRef } from "react"; import { Box, Button, Heading, Stack, Text, Input, Table, Flex, IconButton, Code, Dialog, } from "@chakra-ui/react"; import { LuTrash2, LuCopy, LuPlus } from "react-icons/lu"; import { useQueryClient } from "@tanstack/react-query"; import { $api } from "../../../lib/apiClient"; import { toaster } from "../../../components/ui/toaster"; interface CreateApiKeyResponse { id: string; user_id: string; name: string | null; created_at: string; key: string; } export default function ApiKeysPage() { const [newKeyName, setNewKeyName] = useState(""); const [isCreating, setIsCreating] = useState(false); const [createdKey, setCreatedKey] = useState( null, ); const [keyToDelete, setKeyToDelete] = useState(null); const queryClient = useQueryClient(); const cancelRef = useRef(null); const { data: apiKeys, isLoading } = $api.useQuery( "get", "/v1/user/api-keys", ); const createKeyMutation = $api.useMutation("post", "/v1/user/api-keys", { onSuccess: (data) => { setCreatedKey(data); setNewKeyName(""); setIsCreating(false); queryClient.invalidateQueries({ queryKey: ["get", "/v1/user/api-keys"] }); toaster.create({ duration: 5000, render: () => ( API key created Make sure to copy it now - you won't see it again! ), }); setTimeout(() => { const keyElement = document.querySelector(".api-key-code"); if (keyElement) { const range = document.createRange(); range.selectNodeContents(keyElement); const selection = window.getSelection(); selection?.removeAllRanges(); selection?.addRange(range); } }, 100); }, onError: () => { toaster.create({ duration: 3000, render: () => ( Error Failed to create API key ), }); }, }); const deleteKeyMutation = $api.useMutation( "delete", "/v1/user/api-keys/{key_id}", { onSuccess: () => { queryClient.invalidateQueries({ queryKey: ["get", "/v1/user/api-keys"], }); toaster.create({ duration: 3000, render: () => ( API key deleted ), }); }, onError: () => { toaster.create({ duration: 3000, render: () => ( Error Failed to delete API key ), }); }, }, ); const handleCreateKey = () => { createKeyMutation.mutate({ body: { name: newKeyName || null }, }); }; const handleCopyKey = (key: string) => { navigator.clipboard.writeText(key); toaster.create({ duration: 2000, render: () => ( Copied to clipboard ), }); }; const handleDeleteRequest = (keyId: string) => { setKeyToDelete(keyId); }; const confirmDelete = () => { if (keyToDelete) { deleteKeyMutation.mutate({ params: { path: { key_id: keyToDelete } }, }); setKeyToDelete(null); } }; const formatDate = (dateString: string) => { return new Date(dateString).toLocaleDateString("en-US", { year: "numeric", month: "short", day: "numeric", hour: "2-digit", minute: "2-digit", }); }; return ( API Keys Manage your API keys for programmatic access to Reflector {/* Show newly created key */} {createdKey && ( API Key Created Make sure to copy your API key now. You won't be able to see it again! {createdKey.key} handleCopyKey(createdKey.key)} > )} {/* Create new key */} Create New API Key {!isCreating ? ( ) : ( Name (optional) setNewKeyName(e.target.value)} /> )} {/* List of API keys */} Your API Keys {isLoading ? ( Loading... ) : !apiKeys || apiKeys.length === 0 ? ( No API keys yet. Create one to get started. ) : ( Name Created Actions {apiKeys.map((key) => ( {key.name || Unnamed} {formatDate(key.created_at)} handleDeleteRequest(key.id)} loading={ deleteKeyMutation.isPending && deleteKeyMutation.variables?.params?.path?.key_id === key.id } > ))} )} {/* Delete confirmation dialog */} { if (!e.open) setKeyToDelete(null); }} initialFocusEl={() => cancelRef.current} > Delete API Key Are you sure you want to delete this API key? This action cannot be undone. ); }