feat: add and delete new api key

This commit is contained in:
Krishav Raj Singh 2025-07-18 23:25:27 +05:30
parent 74fc265187
commit ea659c7a1e
1 changed files with 95 additions and 5 deletions

View File

@ -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<Record<string, boolean>>({});
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() {
</div>
))}
<div className="flex justify-end">
<div className="space-y-4">
{newApiKeys.map(entry => {
const keyIsDuplicate = checkKeyIsDuplicate(entry.key);
return (
<div key={entry.id} className="space-y-2">
<Label htmlFor={entry.id}>{entry.key || "New API Key"}</Label>
<div className="space-x-2 flex">
<Input
id={`${entry.id}-key`}
type="text"
placeholder="KEY"
value={entry.key}
onChange={(e) => handleNewKeyChange(entry.id, 'key', e.target.value)}
/>
<Input
id={`${entry.id}-value`}
type="text"
placeholder="VALUE"
value={entry.value}
onChange={(e) => handleNewKeyChange(entry.id, 'value', e.target.value)}
/>
<Button
type="button"
variant="outline"
size="icon"
onClick={() => handleDeleteKey(entry.id)}
>
<Trash className="h-4 w-4" />
</Button>
</div>
{keyIsDuplicate && <p className="text-red-400 font-light">Key already exists</p>}
</div>
)})}
</div>
<div className="flex justify-between">
<Button
type="button"
variant="outline"
onClick={handleAddNewKey}
>
<Plus className="h-4 w-4" />
Add New Key
</Button>
<Button
type="submit"
variant="default"
disabled={!isDirty}
disabled={(!isDirty && newApiKeys.length === 0) || hasEmptyKeyValues || hasDuplicateKeys()}
>Save</Button>
</div>
</form>