/** * API Settings Dialog Component */ "use client"; import { useState, useEffect } from "react"; import { Settings, Cloud, CloudOff } from "lucide-react"; import { useForm } from "react-hook-form"; import { zodResolver } from "@hookform/resolvers/zod"; import * as z from "zod"; import { useLanguage } from "@/contexts/LanguageContext"; import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, DialogTrigger, } from "@/components/ui/dialog"; import { Button } from "@/components/ui/button"; import { Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage, } from "@/components/ui/form"; import { Input } from "@/components/ui/input"; import { getApiSettingsAsync, saveApiSettingsAsync, clearApiSettings, migrateToEncrypted, type ApiSettings, DEFAULT_API_SETTINGS, } from "@/lib/storage"; import { useAuth } from "@/contexts/auth-context"; import { getCloudSettings, saveCloudSettings, isCloudSyncEnabled } from "@/lib/user-api"; const formSchema = z.object({ // All API keys are optional - users only need the ones for their selected models openai_api_key: z.string().optional().or(z.literal("")), // Stock market data APIs alpha_vantage_api_key: z.string().optional().or(z.literal("")), // 美股基本面資料 finmind_api_key: z.string().optional().or(z.literal("")), // 台灣股市資料 // LLM Providers anthropic_api_key: z.string().optional().or(z.literal("")), google_api_key: z.string().optional().or(z.literal("")), grok_api_key: z.string().optional().or(z.literal("")), deepseek_api_key: z.string().optional().or(z.literal("")), qwen_api_key: z.string().optional().or(z.literal("")), // Custom endpoint custom_base_url: z.string().optional().or(z.literal("")), custom_api_key: z.string().optional().or(z.literal("")), }); type FormValues = z.infer; export function ApiSettingsDialog() { const [open, setOpen] = useState(false); const [saveSuccess, setSaveSuccess] = useState(false); const [loading, setLoading] = useState(false); const [syncStatus, setSyncStatus] = useState<"local" | "cloud" | "syncing">("local"); const { isAuthenticated } = useAuth(); const { t } = useLanguage(); const form = useForm({ resolver: zodResolver(formSchema), defaultValues: DEFAULT_API_SETTINGS, }); // Load and decrypt settings when dialog opens useEffect(() => { if (open) { setLoading(true); setSaveSuccess(false); const loadSettings = async () => { try { // First try to migrate legacy settings await migrateToEncrypted(); // If authenticated, try to load from cloud first if (isAuthenticated && isCloudSyncEnabled()) { setSyncStatus("syncing"); const cloudSettings = await getCloudSettings(); if (cloudSettings) { form.reset(cloudSettings); setSyncStatus("cloud"); return; } } // Fall back to local storage const localSettings = await getApiSettingsAsync(); form.reset(localSettings); setSyncStatus(isAuthenticated ? "cloud" : "local"); } catch (error) { console.error("Failed to load settings:", error); setSyncStatus("local"); } finally { setLoading(false); } }; loadSettings(); } }, [open, form, isAuthenticated]); const onSubmit = async (values: FormValues) => { setLoading(true); try { // Encrypt and save settings locally await saveApiSettingsAsync(values as ApiSettings); // If authenticated, also save to cloud if (isAuthenticated && isCloudSyncEnabled()) { setSyncStatus("syncing"); const cloudSaved = await saveCloudSettings(values as ApiSettings); setSyncStatus(cloudSaved ? "cloud" : "local"); } setSaveSuccess(true); setTimeout(() => { setSaveSuccess(false); setOpen(false); }, 1500); } catch (error) { console.error("Failed to save settings:", error); } finally { setLoading(false); } }; const handleClear = () => { clearApiSettings(); form.reset(DEFAULT_API_SETTINGS); }; return ( {/* @ts-ignore - React 19 type compatibility issue with Radix UI */} {t.settings.apiConfiguration} {t.settings.description} {t.settings.encryptionEnabled}
{t.settings.onlyFillNeeded}
{/* Stock Market Data APIs Section */}

{t.settings.stockMarketApis}

{/* FinMind API Key - Taiwan Stocks */} ( {t.settings.finmindToken} {t.settings.finmindDesc} )} /> {/* Alpha Vantage API Key - US Stocks */} ( {t.settings.alphaVantageKey} {t.settings.alphaVantageDesc} )} />
{/* LLM Providers Section */}

{t.settings.llmProviders}

{/* OpenAI API Key */} ( OpenAI API Key {t.settings.openaiDesc} )} /> ( Anthropic API Key {t.settings.anthropicDesc} )} /> {/* Google API Key */} ( Google API Key {t.settings.googleDesc} )} /> {/* Grok API Key */} ( Grok (xAI) API Key {t.settings.grokDesc} )} /> {/* DeepSeek API Key */} ( DeepSeek API Key {t.settings.deepseekDesc} )} /> {/* Qwen API Key */} ( Qwen (Alibaba) API Key {t.settings.qwenDesc} )} />
{/* Custom Endpoint Section */}

{t.settings.customEndpoint}

{/* Custom Base URL */} ( {t.settings.customBaseUrl} {t.settings.customBaseUrlDesc} )} /> {/* Custom API Key */} ( {t.settings.customApiKey} {t.settings.customApiKeyDesc} )} />
{saveSuccess && (
{t.settings.settingsSaved}
)}
); }