'use client'; import { useState, useEffect } from 'react'; import { Zap, Plus, AlertCircle, Clock } from 'lucide-react'; import { useUser } from '@/app/dashboard/useUser'; import { useApiKeys } from './useApiKeys'; import { MAX_API_KEYS, type ApiKey } from './types'; import { ProUpgradePrompt } from './ProUpgradePrompt'; import { ApiKeyTable } from './ApiKeyTable'; import { GenerateKeyDialog } from './GenerateKeyDialog'; import { RevokeKeyDialog } from './RevokeKeyDialog'; import { WebhookSnippet } from './WebhookSnippet'; import { useToast } from '@/components/ui/toast'; import { useI18n } from '@/lib/i18n'; import { cn } from '@/lib/utils'; export default function ApiKeysPage() { const { t } = useI18n(); const { data: user, isLoading: isLoadingUser } = useUser(); const { keys, total, isLoading: isLoadingKeys, isGenerating, isRevoking, generateKey, revokeKey, errorDetails, parseGenerateError, parseRevokeError, } = useApiKeys(); const { toast } = useToast(); const [generateDialogOpen, setGenerateDialogOpen] = useState(false); const [revokeDialogOpen, setRevokeDialogOpen] = useState(false); const [keyToRevoke, setKeyToRevoke] = useState<{ id: string; name: string } | null>(null); const [apiError, setApiError] = useState(null); const isPro = user?.tier === 'pro'; const maxKeysReached = total >= MAX_API_KEYS; const isLoading = isLoadingUser || isLoadingKeys; // Handle API errors with specific error codes useEffect(() => { if (errorDetails?.code === 'PRO_FEATURE_REQUIRED') { setApiError(null); } else if (errorDetails?.code === 'API_KEY_LIMIT_REACHED') { setApiError(t('apiKeys.limitReachedDesc')); } else if (errorDetails) { setApiError(errorDetails.message); } else { setApiError(null); } }, [errorDetails]); // eslint-disable-line react-hooks/exhaustive-deps const handleRevokeClick = (key: ApiKey) => { setKeyToRevoke({ id: key.id, name: key.name }); setRevokeDialogOpen(true); }; const handleRevokeConfirm = async () => { if (!keyToRevoke) return; try { await revokeKey(keyToRevoke.id); setRevokeDialogOpen(false); setKeyToRevoke(null); toast({ title: t('apiKeys.keyRevoked'), description: t('apiKeys.keyRevokedDesc'), }); } catch (error) { const revokeError = parseRevokeError(); if (revokeError?.code === 'API_KEY_NOT_FOUND') { toast({ variant: 'destructive', title: t('apiKeys.keyNotFound'), description: t('apiKeys.keyNotFoundDesc'), }); } else { toast({ variant: 'destructive', title: t('apiKeys.error'), description: revokeError?.message || t('apiKeys.revokeError'), }); } } }; const handleGenerateKey = async (name?: string) => { try { const result = await generateKey(name); return result; } catch (error) { const genError = parseGenerateError(); if (genError?.code === 'API_KEY_LIMIT_REACHED') { toast({ variant: 'destructive', title: t('apiKeys.limitReached'), description: t('apiKeys.limitReachedDesc'), }); } else if (genError?.code === 'PRO_FEATURE_REQUIRED') { toast({ variant: 'destructive', title: t('apiKeys.proRequired'), description: t('apiKeys.proRequiredDesc'), }); } else { toast({ variant: 'destructive', title: t('apiKeys.error'), description: genError?.message || t('apiKeys.generateError'), }); } throw error; } }; if (isLoading) { return (

{t('apiKeys.loading')}

); } if (!isPro) { return ; } return (
{/* ── Editorial Header ───────────────────────────────────── */}
{t('apiKeys.sectionTitle')}

{t('apiKeys.title')}

{t('apiKeys.subtitle')}

{/* API Error */} {apiError && (
{apiError}
)} {/* ── Main Card ──────────────────────────────────────────── */}
{/* Section header */}

{t('apiKeys.sectionTitle')}

{t('apiKeys.sectionDesc')}

{/* Key count + generate button */}

{t('apiKeys.keysUsed', { total, max: MAX_API_KEYS })}

{maxKeysReached ? ( {t('apiKeys.maxReached')} ) : ( t(MAX_API_KEYS - total !== 1 ? 'apiKeys.canGeneratePlural' : 'apiKeys.canGenerate', { count: MAX_API_KEYS - total }) )}

{/* Production key display area */}

{t('apiKeys.sectionTitle')}

{keys.length > 0 ? `${keys[0].key_prefix}************************************` : t('apiKeys.noKeysGenerated')}

{/* Key table */}
{/* Webhook snippet */} {/* Dialogs */}
); }