Files
plunk/apps/web/src/components/ApiKeyDisplay.tsx
T

100 lines
2.8 KiB
TypeScript

import {useState} from 'react';
import {Button} from '@plunk/ui';
import {Check, Copy, Eye, EyeOff, RefreshCw} from 'lucide-react';
interface ApiKeyDisplayProps {
label: string;
value: string;
description?: string;
isSecret?: boolean;
onRegenerate?: () => Promise<void>;
showRegenerate?: boolean;
}
export function ApiKeyDisplay({
label,
value,
description,
isSecret = false,
onRegenerate,
showRegenerate = false,
}: ApiKeyDisplayProps) {
const [showKey, setShowKey] = useState(!isSecret);
const [copied, setCopied] = useState(false);
const [isRegenerating, setIsRegenerating] = useState(false);
const handleCopy = async () => {
try {
await navigator.clipboard.writeText(value);
setCopied(true);
setTimeout(() => setCopied(false), 2000);
} catch (error) {
console.error('Failed to copy:', error);
}
};
const handleRegenerate = async () => {
if (!onRegenerate) return;
try {
setIsRegenerating(true);
await onRegenerate();
} catch (error) {
console.error('Failed to regenerate:', error);
} finally {
setIsRegenerating(false);
}
};
const displayValue = showKey ? value : '•'.repeat(48);
return (
<div>
<label className="text-sm font-medium text-neutral-700 block mb-2">{label}</label>
<div className="flex items-center gap-2">
<code className="flex-1 px-3 py-2 bg-neutral-50 rounded-lg text-xs font-mono text-neutral-900 border border-neutral-200 truncate">
{displayValue}
</code>
<div className="flex items-center gap-1">
{isSecret && (
<Button
type="button"
variant="outline"
size="icon"
onClick={() => setShowKey(!showKey)}
title={showKey ? 'Hide key' : 'Show key'}
className="h-9 w-9"
>
{showKey ? <EyeOff className="h-4 w-4" /> : <Eye className="h-4 w-4" />}
</Button>
)}
<Button
type="button"
variant="outline"
size="icon"
onClick={handleCopy}
title="Copy to clipboard"
className="h-9 w-9"
>
{copied ? <Check className="h-4 w-4 text-green-600" /> : <Copy className="h-4 w-4" />}
</Button>
{showRegenerate && onRegenerate && (
<Button
type="button"
variant="outline"
size="icon"
onClick={handleRegenerate}
disabled={isRegenerating}
title="Regenerate key"
className="h-9 w-9"
>
<RefreshCw className={`h-4 w-4 ${isRegenerating ? 'animate-spin' : ''}`} />
</Button>
)}
</div>
</div>
{description && <p className="text-xs text-neutral-500 mt-1">{description}</p>}
</div>
);
}