multi_image / src /components /settings-panel.tsx
mrbeniwal's picture
Initial commit
b24a0b1
'use client';
import * as React from 'react';
import { Settings, Eye, EyeOff, Check, Trash2, Key } from 'lucide-react';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
import {
Sheet,
SheetContent,
SheetDescription,
SheetHeader,
SheetTitle,
SheetTrigger,
} from '@/components/ui/sheet';
import { useApiKeys } from '@/contexts/api-keys-context';
import { PROVIDER_CONFIGS, type ImageProvider } from '@/lib/api-config';
import { Icons } from './icons';
import { cn } from '@/lib/utils';
interface ApiKeyInputProps {
provider: ImageProvider;
label: string;
placeholder: string;
icon: React.ComponentType<{ className?: string }>;
}
function ApiKeyInput({ provider, label, placeholder, icon: Icon }: ApiKeyInputProps) {
const { getApiKey, setApiKey, clearApiKey, hasKey } = useApiKeys();
const [showKey, setShowKey] = React.useState(false);
const [localValue, setLocalValue] = React.useState('');
const [isSaved, setIsSaved] = React.useState(false);
// Sync local value with context on mount and when context changes
React.useEffect(() => {
const contextValue = getApiKey(provider);
setLocalValue(contextValue);
setIsSaved(contextValue.length > 0);
}, [getApiKey, provider]);
const handleSave = () => {
setApiKey(provider, localValue);
setIsSaved(true);
setTimeout(() => setIsSaved(false), 2000);
};
const handleClear = () => {
setLocalValue('');
clearApiKey(provider);
};
const handleKeyDown = (e: React.KeyboardEvent) => {
if (e.key === 'Enter') {
handleSave();
}
};
const isConfigured = hasKey(provider);
return (
<div className="space-y-2">
<div className="flex items-center gap-2">
<Icon className="h-4 w-4 text-muted-foreground" />
<Label htmlFor={`api-key-${provider}`} className="text-sm font-medium">
{label}
</Label>
{isConfigured && (
<span className="ml-auto flex items-center gap-1 text-xs text-green-600 dark:text-green-400">
<Check className="h-3 w-3" />
Configured
</span>
)}
</div>
<div className="flex gap-2">
<div className="relative flex-1">
<Input
id={`api-key-${provider}`}
type={showKey ? 'text' : 'password'}
value={localValue}
onChange={(e) => setLocalValue(e.target.value)}
onKeyDown={handleKeyDown}
placeholder={placeholder}
className="pr-10 font-mono text-sm"
/>
<Button
type="button"
variant="ghost"
size="icon"
className="absolute right-1 top-1/2 h-7 w-7 -translate-y-1/2"
onClick={() => setShowKey(!showKey)}
>
{showKey ? (
<EyeOff className="h-4 w-4 text-muted-foreground" />
) : (
<Eye className="h-4 w-4 text-muted-foreground" />
)}
</Button>
</div>
<Button
type="button"
variant="outline"
size="icon"
onClick={handleSave}
disabled={localValue === getApiKey(provider)}
className={cn(isSaved && 'bg-green-100 dark:bg-green-900')}
>
<Check className={cn('h-4 w-4', isSaved && 'text-green-600')} />
</Button>
<Button
type="button"
variant="outline"
size="icon"
onClick={handleClear}
disabled={!localValue}
>
<Trash2 className="h-4 w-4 text-muted-foreground" />
</Button>
</div>
</div>
);
}
export function SettingsPanel() {
const { isOpen, setIsOpen, hasKey } = useApiKeys();
const configuredCount = [
hasKey('openai'),
hasKey('google'),
hasKey('qwen'),
].filter(Boolean).length;
return (
<Sheet open={isOpen} onOpenChange={setIsOpen}>
<SheetTrigger asChild>
<Button variant="ghost" size="icon" className="relative">
<Settings className="h-5 w-5" />
{configuredCount > 0 && (
<span className="absolute -right-1 -top-1 flex h-4 w-4 items-center justify-center rounded-full bg-primary text-[10px] font-medium text-primary-foreground">
{configuredCount}
</span>
)}
<span className="sr-only">Settings</span>
</Button>
</SheetTrigger>
<SheetContent side="left" className="w-[400px] sm:w-[450px]">
<SheetHeader>
<SheetTitle className="flex items-center gap-2">
<Key className="h-5 w-5" />
API Keys
</SheetTitle>
<SheetDescription>
Configure your API keys for image generation. Keys are stored locally in your browser and never sent to our servers.
</SheetDescription>
</SheetHeader>
<div className="mt-6 space-y-6">
<ApiKeyInput
provider="openai"
label="OpenAI API Key"
placeholder="sk-..."
icon={Icons.openai}
/>
<ApiKeyInput
provider="google"
label="Google Gemini API Key"
placeholder="AIza..."
icon={Icons.google}
/>
<ApiKeyInput
provider="qwen"
label="Qwen (DashScope) API Key"
placeholder="sk-..."
icon={Icons.qwen}
/>
<div className="rounded-lg border bg-muted/50 p-4">
<h4 className="text-sm font-medium">How to get API keys:</h4>
<ul className="mt-2 space-y-1 text-xs text-muted-foreground">
<li>
<strong>OpenAI:</strong>{' '}
<a
href="https://platform.openai.com/api-keys"
target="_blank"
rel="noopener noreferrer"
className="text-primary hover:underline"
>
platform.openai.com/api-keys
</a>
</li>
<li>
<strong>Google:</strong>{' '}
<a
href="https://aistudio.google.com/apikey"
target="_blank"
rel="noopener noreferrer"
className="text-primary hover:underline"
>
aistudio.google.com/apikey
</a>
</li>
<li>
<strong>Qwen:</strong>{' '}
<a
href="https://dashscope.console.aliyun.com/"
target="_blank"
rel="noopener noreferrer"
className="text-primary hover:underline"
>
dashscope.console.aliyun.com
</a>
</li>
</ul>
</div>
</div>
</SheetContent>
</Sheet>
);
}