219 lines
8.4 KiB
TypeScript
219 lines
8.4 KiB
TypeScript
import { Label } from '@/components/ui/label';
|
|
import { Input } from '@/components/ui/input';
|
|
import { Switch } from '@/components/ui/switch';
|
|
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
|
|
import {
|
|
Select,
|
|
SelectContent,
|
|
SelectItem,
|
|
SelectTrigger,
|
|
SelectValue,
|
|
} from '@/components/ui/select';
|
|
import { Brain, Eye, EyeOff, Key } from 'lucide-react';
|
|
import { Button } from '@/components/ui/button';
|
|
|
|
interface AIProviderSettingsProps {
|
|
aiEnabled: boolean;
|
|
aiProvider: 'claude' | 'openai' | 'gemini';
|
|
claudeApiKey: string;
|
|
openaiApiKey: string;
|
|
geminiApiKey: string;
|
|
showApiKeys: Record<string, boolean>;
|
|
onAiEnabledChange: (enabled: boolean) => void;
|
|
onProviderChange: (provider: 'claude' | 'openai' | 'gemini') => void;
|
|
onClaudeApiKeyChange: (key: string) => void;
|
|
onOpenaiApiKeyChange: (key: string) => void;
|
|
onGeminiApiKeyChange: (key: string) => void;
|
|
onToggleApiKeyVisibility: (provider: 'claude' | 'openai' | 'gemini') => void;
|
|
maskApiKey: (key: string) => string;
|
|
}
|
|
|
|
const PROVIDERS = [
|
|
{ value: 'claude', label: 'Claude (Anthropic)', description: 'Advanced AI by Anthropic' },
|
|
{ value: 'openai', label: 'OpenAI (GPT-4)', description: 'GPT-4 by OpenAI' },
|
|
{ value: 'gemini', label: 'Gemini (Google)', description: 'Gemini by Google' }
|
|
];
|
|
|
|
export function AIProviderSettings({
|
|
aiEnabled,
|
|
aiProvider,
|
|
claudeApiKey,
|
|
openaiApiKey,
|
|
geminiApiKey,
|
|
showApiKeys,
|
|
onAiEnabledChange,
|
|
onProviderChange,
|
|
onClaudeApiKeyChange,
|
|
onOpenaiApiKeyChange,
|
|
onGeminiApiKeyChange,
|
|
onToggleApiKeyVisibility,
|
|
maskApiKey
|
|
}: AIProviderSettingsProps) {
|
|
return (
|
|
<Card className="border-0 shadow-sm">
|
|
<CardHeader className="pb-3">
|
|
<div className="flex items-center gap-2">
|
|
<Brain className="w-5 h-5 text-re-green" />
|
|
<CardTitle className="text-base font-semibold">AI Provider & API Keys</CardTitle>
|
|
</div>
|
|
<CardDescription className="text-sm">
|
|
Select your AI provider and configure API keys
|
|
</CardDescription>
|
|
</CardHeader>
|
|
<CardContent className="space-y-4">
|
|
{/* Master Toggle */}
|
|
<div className="flex items-center justify-between p-4 bg-gray-50 border border-gray-200 rounded-md">
|
|
<div>
|
|
<p className="font-medium text-sm">Enable AI Features</p>
|
|
<p className="text-xs text-muted-foreground">
|
|
Master toggle to enable/disable all AI-powered features
|
|
</p>
|
|
</div>
|
|
<Switch
|
|
checked={aiEnabled}
|
|
onCheckedChange={onAiEnabledChange}
|
|
/>
|
|
</div>
|
|
|
|
{aiEnabled && (
|
|
<>
|
|
{/* Provider Selection */}
|
|
<div className="space-y-2">
|
|
<Label htmlFor="ai-provider" className="text-sm font-medium">
|
|
AI Provider
|
|
</Label>
|
|
<Select value={aiProvider} onValueChange={(value: any) => onProviderChange(value)}>
|
|
<SelectTrigger
|
|
id="ai-provider"
|
|
className="border-gray-200 focus:border-re-green focus:ring-2 focus:ring-re-green/20"
|
|
>
|
|
<SelectValue placeholder="Select AI provider" />
|
|
</SelectTrigger>
|
|
<SelectContent>
|
|
{PROVIDERS.map((provider) => (
|
|
<SelectItem key={provider.value} value={provider.value}>
|
|
<div className="flex items-center gap-2">
|
|
<span className="font-medium">{provider.label}</span>
|
|
<span className="text-xs text-muted-foreground">-</span>
|
|
<span className="text-xs text-muted-foreground">{provider.description}</span>
|
|
</div>
|
|
</SelectItem>
|
|
))}
|
|
</SelectContent>
|
|
</Select>
|
|
</div>
|
|
|
|
{/* API Keys for each provider */}
|
|
<div className="space-y-4 pt-2">
|
|
<Label className="text-sm font-medium flex items-center gap-2">
|
|
<Key className="w-4 h-4" />
|
|
API Keys
|
|
</Label>
|
|
|
|
{/* Claude API Key */}
|
|
<div className="space-y-2">
|
|
<Label htmlFor="claude-key" className="text-xs text-muted-foreground">
|
|
Claude API Key
|
|
</Label>
|
|
<div className="flex gap-2">
|
|
<Input
|
|
id="claude-key"
|
|
type={showApiKeys.claude ? 'text' : 'password'}
|
|
value={claudeApiKey}
|
|
onChange={(e) => onClaudeApiKeyChange(e.target.value)}
|
|
placeholder={showApiKeys.claude ? "sk-ant-..." : maskApiKey(claudeApiKey) || "sk-ant-..."}
|
|
className="border-gray-200 focus:border-re-green focus:ring-2 focus:ring-re-green/20 font-mono text-sm"
|
|
/>
|
|
<Button
|
|
type="button"
|
|
variant="outline"
|
|
size="icon"
|
|
onClick={() => onToggleApiKeyVisibility('claude')}
|
|
className="border-gray-200"
|
|
>
|
|
{showApiKeys.claude ? (
|
|
<EyeOff className="w-4 h-4" />
|
|
) : (
|
|
<Eye className="w-4 h-4" />
|
|
)}
|
|
</Button>
|
|
</div>
|
|
<p className="text-xs text-muted-foreground">
|
|
Get your API key from console.anthropic.com
|
|
</p>
|
|
</div>
|
|
|
|
{/* OpenAI API Key */}
|
|
<div className="space-y-2">
|
|
<Label htmlFor="openai-key" className="text-xs text-muted-foreground">
|
|
OpenAI API Key
|
|
</Label>
|
|
<div className="flex gap-2">
|
|
<Input
|
|
id="openai-key"
|
|
type={showApiKeys.openai ? 'text' : 'password'}
|
|
value={openaiApiKey}
|
|
onChange={(e) => onOpenaiApiKeyChange(e.target.value)}
|
|
placeholder={showApiKeys.openai ? "sk-..." : maskApiKey(openaiApiKey) || "sk-..."}
|
|
className="border-gray-200 focus:border-re-green focus:ring-2 focus:ring-re-green/20 font-mono text-sm"
|
|
/>
|
|
<Button
|
|
type="button"
|
|
variant="outline"
|
|
size="icon"
|
|
onClick={() => onToggleApiKeyVisibility('openai')}
|
|
className="border-gray-200"
|
|
>
|
|
{showApiKeys.openai ? (
|
|
<EyeOff className="w-4 h-4" />
|
|
) : (
|
|
<Eye className="w-4 h-4" />
|
|
)}
|
|
</Button>
|
|
</div>
|
|
<p className="text-xs text-muted-foreground">
|
|
Get your API key from platform.openai.com
|
|
</p>
|
|
</div>
|
|
|
|
{/* Gemini API Key */}
|
|
<div className="space-y-2">
|
|
<Label htmlFor="gemini-key" className="text-xs text-muted-foreground">
|
|
Gemini API Key
|
|
</Label>
|
|
<div className="flex gap-2">
|
|
<Input
|
|
id="gemini-key"
|
|
type={showApiKeys.gemini ? 'text' : 'password'}
|
|
value={geminiApiKey}
|
|
onChange={(e) => onGeminiApiKeyChange(e.target.value)}
|
|
placeholder={showApiKeys.gemini ? "AIza..." : maskApiKey(geminiApiKey) || "AIza..."}
|
|
className="border-gray-200 focus:border-re-green focus:ring-2 focus:ring-re-green/20 font-mono text-sm"
|
|
/>
|
|
<Button
|
|
type="button"
|
|
variant="outline"
|
|
size="icon"
|
|
onClick={() => onToggleApiKeyVisibility('gemini')}
|
|
className="border-gray-200"
|
|
>
|
|
{showApiKeys.gemini ? (
|
|
<EyeOff className="w-4 h-4" />
|
|
) : (
|
|
<Eye className="w-4 h-4" />
|
|
)}
|
|
</Button>
|
|
</div>
|
|
<p className="text-xs text-muted-foreground">
|
|
Get your API key from ai.google.dev
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</>
|
|
)}
|
|
</CardContent>
|
|
</Card>
|
|
);
|
|
}
|
|
|