From ea659c7a1e4f09f2d33bf7c9f05522750c2f70ea Mon Sep 17 00:00:00 2001 From: Krishav Raj Singh Date: Fri, 18 Jul 2025 23:25:27 +0530 Subject: [PATCH] feat: add and delete new api key --- .../env-manager/local-env-manager.tsx | 100 +++++++++++++++++- 1 file changed, 95 insertions(+), 5 deletions(-) diff --git a/frontend/src/components/env-manager/local-env-manager.tsx b/frontend/src/components/env-manager/local-env-manager.tsx index 9a274c32..26fe1a22 100644 --- a/frontend/src/components/env-manager/local-env-manager.tsx +++ b/frontend/src/components/env-manager/local-env-manager.tsx @@ -1,6 +1,6 @@ "use client"; -import { Eye, EyeOff } from "lucide-react"; +import { Eye, EyeOff, Plus, Trash } from "lucide-react"; import { Button } from "../ui/button"; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "../ui/card"; import { isLocalMode } from "@/lib/config"; @@ -19,6 +19,8 @@ interface APIKeyForm { export function LocalEnvManager() { const queryClient = useQueryClient(); const [visibleKeys, setVisibleKeys] = useState>({}); + const [newApiKeys, setNewApiKeys] = useState<{key: string, value: string, id: string}[]>([]); + const {data: apiKeys, isLoading} = useQuery({ queryKey: ['api-keys'], queryFn: async() => { @@ -33,8 +35,51 @@ export function LocalEnvManager() { }); const handleSave = async (data: APIKeyForm) => { - updateApiKeys.mutate(data); + const duplicate_key = newApiKeys.find(entry => data[entry.key.trim()]); + if (duplicate_key) { + toast.error(`Key ${duplicate_key.key} already exists`); + return; + } + const submitData = { + ...data, + ...Object.fromEntries(newApiKeys.map(entry => [entry.key, entry.value])) + } + + updateApiKeys.mutate(submitData); } + + const handleAddNewKey = () => { + setNewApiKeys([...newApiKeys, {key: "", value: "", id: crypto.randomUUID()}]); + } + + const checkKeyIsDuplicate = (key: string) => { + const trimmedKey = key.trim(); + const keyIsDuplicate = + trimmedKey && + ( + (apiKeys && Object.keys(apiKeys).includes(trimmedKey)) || + newApiKeys.filter(e => e.key.trim() === trimmedKey).length > 1 + ); + return keyIsDuplicate; + } + + const handleNewKeyChange = (id: string, field: string, value: string) => { + setNewApiKeys(prev => + prev.map(entry => entry.id === id ? {...entry, [field]: value} : entry) + ); + } + + const handleDeleteKey = (id: string) => { + setNewApiKeys(prev => prev.filter(entry => entry.id !== id)); + } + + const hasEmptyKeyValues = newApiKeys.some(entry => entry.key.trim() === "" || entry.value.trim() === ""); + const hasDuplicateKeys = (): boolean => { + const allKeys = [...Object.keys(apiKeys || {}), ...newApiKeys.map(entry => entry.key.trim())]; + const uniqueKeys = new Set(allKeys); + return uniqueKeys.size !== allKeys.length; + } + const updateApiKeys = useMutation({ mutationFn: async (data: APIKeyForm) => { const response = await backendApi.post('/env-vars', data); @@ -43,6 +88,7 @@ export function LocalEnvManager() { }, onSuccess: (data) => { toast.success(data.message); + setNewApiKeys([]); }, onError: () => { toast.error('Failed to update API keys'); @@ -119,12 +165,56 @@ export function LocalEnvManager() { ))} - -
+ +
+ {newApiKeys.map(entry => { + const keyIsDuplicate = checkKeyIsDuplicate(entry.key); + return ( + +
+ +
+ handleNewKeyChange(entry.id, 'key', e.target.value)} + /> + handleNewKeyChange(entry.id, 'value', e.target.value)} + /> + +
+ {keyIsDuplicate &&

Key already exists

} +
+ )})} +
+ +
+