veza/apps/web/src/components/developer/modals/CreateAPIKeyModal.tsx

157 lines
5.6 KiB
TypeScript
Raw Normal View History

import React, { useState } from 'react';
import { Button } from '../../ui/button';
import { Input } from '../../ui/input';
import { X, Key, Copy, Check } from 'lucide-react';
import { useToast } from '../../../context/ToastContext';
interface CreateAPIKeyModalProps {
onClose: () => void;
onCreate: (keyData: { name: string; scopes: string[] }) => void;
}
const SCOPES = [
{ id: 'user.read', label: 'Read User Data' },
{ id: 'user.write', label: 'Update User Profile' },
{ id: 'tracks.read', label: 'Read Tracks' },
{ id: 'tracks.upload', label: 'Upload Tracks' },
{ id: 'sales.read', label: 'Read Sales Data' },
];
export const CreateAPIKeyModal: React.FC<CreateAPIKeyModalProps> = ({
onClose,
onCreate,
}) => {
const { addToast } = useToast();
const [step, setStep] = useState(1);
const [name, setName] = useState('');
const [selectedScopes, setSelectedScopes] = useState<string[]>(['user.read']);
const [generatedKey, setGeneratedKey] = useState('');
const toggleScope = (id: string) => {
setSelectedScopes((prev) =>
prev.includes(id) ? prev.filter((s) => s !== id) : [...prev, id],
);
};
const handleGenerate = () => {
if (!name) {
addToast('Please name your key', 'error');
return;
}
// Mock Key Generation
const mockKey = `vz_${Math.random().toString(36).substr(2, 8)}_${Math.random().toString(36).substr(2, 16)}`;
setGeneratedKey(mockKey);
onCreate({ name, scopes: selectedScopes }); // Notify parent
setStep(2);
};
const copyKey = () => {
navigator.clipboard.writeText(generatedKey);
addToast('API Key copied to clipboard', 'success');
};
return (
<div className="fixed inset-0 z-[100] flex items-center justify-center p-4">
<div
className="absolute inset-0 bg-kodo-void/90 backdrop-blur-sm"
onClick={onClose}
></div>
<div className="relative w-full max-w-lg bg-kodo-graphite border border-kodo-steel rounded-xl shadow-2xl animate-scaleIn overflow-hidden">
<div className="p-4 border-b border-kodo-steel bg-kodo-ink flex justify-between items-center">
<h3 className="font-bold text-white flex items-center gap-2">
<Key className="w-4 h-4 text-kodo-gold" />{' '}
{step === 1 ? 'Create API Key' : 'API Key Generated'}
</h3>
<button onClick={onClose}>
<X className="w-5 h-5 text-gray-400 hover:text-white" />
</button>
</div>
<div className="p-6">
{step === 1 ? (
<div className="space-y-6">
<Input
label="Key Name"
placeholder="e.g. Production Server, Mobile App"
value={name}
onChange={(e) => setName(e.target.value)}
autoFocus
/>
<div>
<label className="block text-xs font-bold text-gray-400 uppercase mb-3">
Permissions (Scopes)
</label>
<div className="grid grid-cols-1 md:grid-cols-2 gap-3">
{SCOPES.map((scope) => (
<label
key={scope.id}
className="flex items-center gap-3 p-3 bg-kodo-ink rounded border border-kodo-steel cursor-pointer hover:border-gray-500 transition-colors"
>
<input
type="checkbox"
className="rounded border-gray-600 bg-transparent text-kodo-gold focus:ring-0"
checked={selectedScopes.includes(scope.id)}
onChange={() => toggleScope(scope.id)}
/>
<span className="text-sm text-gray-300">
{scope.label}
</span>
</label>
))}
</div>
</div>
</div>
) : (
<div className="text-center space-y-6">
<div className="w-16 h-16 bg-kodo-lime/20 rounded-full flex items-center justify-center mx-auto text-kodo-lime">
<Check className="w-8 h-8" />
</div>
<div>
<h4 className="text-xl font-bold text-white mb-2">
Key Created Successfully
</h4>
<p className="text-sm text-gray-400 max-w-xs mx-auto">
Please copy your API key now. For security reasons, you won't
be able to see it again.
</p>
</div>
<div className="bg-black border border-kodo-steel rounded-lg p-4 flex items-center gap-2 relative group">
<code className="text-kodo-gold font-mono text-sm flex-1 break-all">
{generatedKey}
</code>
<Button
variant="ghost"
size="icon"
onClick={copyKey}
className="hover:text-white"
>
<Copy className="w-4 h-4" />
</Button>
</div>
</div>
)}
</div>
<div className="p-4 border-t border-kodo-steel bg-kodo-ink flex justify-end gap-3">
{step === 1 ? (
<>
<Button variant="ghost" onClick={onClose}>
Cancel
</Button>
<Button variant="primary" onClick={handleGenerate}>
Generate Key
</Button>
</>
) : (
<Button variant="primary" onClick={onClose}>
Done
</Button>
)}
</div>
</div>
</div>
);
};