Replit-Commit-Author: Agent Replit-Commit-Session-Id: e931b5ab-041b-42e7-baf1-50017869cef6 Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/e19c6a51-7e4c-4bb8-a6a6-46dc00f0ec99/396f4320-2dbc-4ff6-85bf-f132094a1754.jpg
187 lines
6.1 KiB
TypeScript
187 lines
6.1 KiB
TypeScript
import { useState, useEffect } from "react";
|
|
import { useMutation, useQuery } from "@tanstack/react-query";
|
|
import { queryClient } from "@/lib/queryClient";
|
|
import { apiClient } from "@/lib/api";
|
|
import { useToast } from "@/hooks/use-toast";
|
|
import { Card, CardContent } from "@/components/ui/card";
|
|
import { Button } from "@/components/ui/button";
|
|
import { Input } from "@/components/ui/input";
|
|
import { Label } from "@/components/ui/label";
|
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
|
|
|
interface AuthenticationPanelProps {
|
|
token: string;
|
|
onSave: () => void;
|
|
}
|
|
|
|
export default function AuthenticationPanel({ token, onSave }: AuthenticationPanelProps) {
|
|
const { toast } = useToast();
|
|
const [citesToken, setCitesToken] = useState(token || "");
|
|
const [iucnToken, setIucnToken] = useState("");
|
|
const [activeTab, setActiveTab] = useState("cites");
|
|
|
|
// Fetch both tokens from the server
|
|
const { data: tokenData } = useQuery({
|
|
queryKey: ['/api/token'],
|
|
queryFn: async () => {
|
|
return await apiClient.getTokens();
|
|
},
|
|
enabled: true, // Always fetch the tokens
|
|
});
|
|
|
|
// Update local state when token data is fetched
|
|
useEffect(() => {
|
|
if (tokenData) {
|
|
setCitesToken(tokenData.token || "");
|
|
setIucnToken(tokenData.iucnToken || "");
|
|
}
|
|
}, [tokenData]);
|
|
|
|
const saveTokenMutation = useMutation({
|
|
mutationFn: async (params: { citesToken: string; iucnToken?: string }) => {
|
|
return await apiClient.saveToken(params.citesToken, params.iucnToken);
|
|
},
|
|
onSuccess: (response) => {
|
|
if (response.success) {
|
|
queryClient.invalidateQueries({ queryKey: ['/api/token'] });
|
|
toast({
|
|
title: "Success",
|
|
description: "API tokens have been saved successfully",
|
|
});
|
|
onSave();
|
|
} else {
|
|
toast({
|
|
title: "Error",
|
|
description: response.message || "Failed to save API tokens",
|
|
variant: "destructive",
|
|
});
|
|
}
|
|
},
|
|
onError: () => {
|
|
toast({
|
|
title: "Error",
|
|
description: "Failed to save API tokens",
|
|
variant: "destructive",
|
|
});
|
|
}
|
|
});
|
|
|
|
const handleSaveTokens = () => {
|
|
if (!citesToken.trim()) {
|
|
toast({
|
|
title: "Validation Error",
|
|
description: "Please enter a CITES API token",
|
|
variant: "destructive",
|
|
});
|
|
return;
|
|
}
|
|
|
|
saveTokenMutation.mutate({
|
|
citesToken,
|
|
iucnToken: iucnToken.trim() ? iucnToken : undefined
|
|
});
|
|
};
|
|
|
|
const handleGetCitesToken = () => {
|
|
window.open("https://api.speciesplus.net/", "_blank");
|
|
};
|
|
|
|
const handleGetIucnToken = () => {
|
|
window.open("https://apiv4.iucnredlist.org/api/v4/docs", "_blank");
|
|
};
|
|
|
|
return (
|
|
<Card>
|
|
<CardContent className="pt-4">
|
|
<h2 className="text-lg font-semibold mb-3 flex items-center">
|
|
<svg
|
|
className="h-5 w-5 text-primary mr-2"
|
|
xmlns="http://www.w3.org/2000/svg"
|
|
viewBox="0 0 24 24"
|
|
fill="none"
|
|
stroke="currentColor"
|
|
strokeWidth="2"
|
|
strokeLinecap="round"
|
|
strokeLinejoin="round"
|
|
>
|
|
<path d="M21 2l-2 2m-7.61 7.61a5.5 5.5 0 1 1-7.778 7.778 5.5 5.5 0 0 1 7.777-7.777zm0 0L15.5 7.5m0 0l3 3L22 7l-3-3m-3.5 3.5L19 4"/>
|
|
</svg>
|
|
API Authentication
|
|
</h2>
|
|
|
|
<Tabs value={activeTab} onValueChange={setActiveTab} className="mb-4">
|
|
<TabsList className="grid grid-cols-2">
|
|
<TabsTrigger value="cites">CITES+ API</TabsTrigger>
|
|
<TabsTrigger value="iucn">IUCN Red List API</TabsTrigger>
|
|
</TabsList>
|
|
|
|
<TabsContent value="cites" className="pt-4">
|
|
<div className="mb-4">
|
|
<Label htmlFor="citesToken" className="block text-sm font-medium text-gray-700 mb-1">
|
|
CITES+ API Token (Required)
|
|
</Label>
|
|
<Input
|
|
type="password"
|
|
id="citesToken"
|
|
className="w-full"
|
|
placeholder="Enter your CITES API token"
|
|
value={citesToken}
|
|
onChange={(e) => setCitesToken(e.target.value)}
|
|
/>
|
|
<div className="mt-2 text-xs text-gray-500">
|
|
The CITES+ API provides species listings, distributions, and legislation information.
|
|
</div>
|
|
<Button
|
|
variant="link"
|
|
className="text-primary text-sm px-0 py-1"
|
|
onClick={handleGetCitesToken}
|
|
>
|
|
Get a CITES+ API token
|
|
</Button>
|
|
</div>
|
|
</TabsContent>
|
|
|
|
<TabsContent value="iucn" className="pt-4">
|
|
<div className="mb-4">
|
|
<Label htmlFor="iucnToken" className="block text-sm font-medium text-gray-700 mb-1">
|
|
IUCN Red List API v4 Token (Optional)
|
|
</Label>
|
|
<Input
|
|
type="password"
|
|
id="iucnToken"
|
|
className="w-full"
|
|
placeholder="Enter your IUCN API token"
|
|
value={iucnToken}
|
|
onChange={(e) => setIucnToken(e.target.value)}
|
|
/>
|
|
<div className="mt-2 text-xs text-gray-500">
|
|
The IUCN Red List API provides conservation status, threats, habitats, and conservation measures.
|
|
</div>
|
|
<Button
|
|
variant="link"
|
|
className="text-primary text-sm px-0 py-1"
|
|
onClick={handleGetIucnToken}
|
|
>
|
|
Get an IUCN Red List API token
|
|
</Button>
|
|
</div>
|
|
</TabsContent>
|
|
</Tabs>
|
|
|
|
<div className="flex justify-between items-center">
|
|
<Button
|
|
className="bg-primary text-white"
|
|
onClick={handleSaveTokens}
|
|
disabled={saveTokenMutation.isPending}
|
|
>
|
|
{saveTokenMutation.isPending ? "Saving..." : "Save Tokens"}
|
|
</Button>
|
|
<div className="text-xs text-gray-500">
|
|
Both tokens are stored securely.
|
|
</div>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
);
|
|
}
|