import { useState } from "react"; import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query"; import { useDataProvider, useNotify, useTranslate } from "ra-core"; import { Button } from "@/components/ds/ui/button"; import { Card, CardContent, CardHeader, CardTitle, } from "@/components/ds/ui/card"; import { Plus, Trash2, Copy } from "lucide-react"; import { CreateApiKeyDialog } from "./CreateApiKeyDialog"; import { Badge } from "@/components/ds/ui/badge"; import { format } from "date-fns"; import { decryptValue } from "@/lib/encryption-utils"; import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, } from "@/components/ds/ui/alert-dialog"; import { isDemoMode } from "@/lib/demo-utils"; import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, } from "@/components/ds/ui/tooltip"; export const ApiKeysTab = () => { const [showCreateDialog, setShowCreateDialog] = useState(false); const [keyToDelete, setKeyToDelete] = useState(null); const dataProvider = useDataProvider(); const notify = useNotify(); const queryClient = useQueryClient(); const translate = useTranslate(); const { data: apiKeys, isLoading } = useQuery({ queryKey: ["api_keys"], queryFn: async () => { const { data } = await dataProvider.getList("api_keys", { pagination: { page: 1, perPage: 100 }, sort: { field: "created_at", order: "DESC" }, filter: {}, }); return data; }, }); const deleteMutation = useMutation({ mutationFn: async (id: number) => { await dataProvider.delete("api_keys", { id, previousData: { id } as any, }); }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ["api_keys"] }); notify(translate("crm.integrations.api_keys.notification.deleted")); setKeyToDelete(null); }, onError: () => { notify( translate("crm.integrations.api_keys.notification.error_deleting"), { type: "error", }, ); }, }); return (

{translate("crm.integrations.api_keys.description")}

{isDemoMode() && (

Creating API keys is disabled in demo mode

)}
{isLoading ? ( {translate("crm.integrations.api_keys.loading")} ) : apiKeys && apiKeys.length > 0 ? (
{apiKeys.map((key: any) => ( setKeyToDelete(key.id)} /> ))}
) : (

{translate("crm.integrations.api_keys.empty")}

)} setShowCreateDialog(false)} /> setKeyToDelete(null)} > {translate("crm.integrations.api_keys.dialog.delete_title")} {translate("crm.integrations.api_keys.dialog.delete_description")} {translate("crm.activity.cancel")} keyToDelete && deleteMutation.mutate(keyToDelete)} className="bg-destructive hover:bg-destructive/90" > {translate("crm.integrations.webhooks.action.delete")}
); }; const ApiKeyCard = ({ apiKey, onDelete, }: { apiKey: any; onDelete: () => void; }) => { const notify = useNotify(); const translate = useTranslate(); const copyFullKey = async () => { try { if (apiKey.encrypted_key) { const fullKey = await decryptValue(apiKey.encrypted_key); await navigator.clipboard.writeText(fullKey); notify(translate("crm.integrations.api_keys.action.copied")); } else { notify(translate("crm.integrations.api_keys.fields.not_available"), { type: "warning", }); } } catch { notify( translate("crm.integrations.api_keys.notification.error_copying"), { type: "error" }, ); } }; return (
{apiKey.name}
{apiKey.is_active ? ( {translate("crm.integrations.webhooks.status.active")} ) : ( {translate("crm.integrations.webhooks.status.inactive")} )} {apiKey.scopes && apiKey.scopes.length > 0 && ( {apiKey.scopes.join(", ")} )}
{isDemoMode() && (

Deleting API keys is disabled in demo mode

)}
{apiKey.key_prefix}••••••••••••••••••••

{translate("crm.integrations.api_keys.fields.key_hint")}

{translate("crm.integrations.api_keys.fields.created", { date: format(new Date(apiKey.created_at), "PPP"), })}

{apiKey.last_used_at && (

{translate("crm.integrations.api_keys.fields.last_used", { date: format(new Date(apiKey.last_used_at), "PPp"), })}

)} {apiKey.expires_at && (

{translate("crm.integrations.api_keys.fields.expires", { date: format(new Date(apiKey.expires_at), "PPP"), })}

)}
); };