From 13890af125e431c92d4dabfd5261230db67d651e Mon Sep 17 00:00:00 2001 From: Christian Vidal Wolf Date: Fri, 24 Apr 2026 13:45:38 +0200 Subject: [PATCH] feat: add AI Settings modal to manage API keys from the UI --- src/components/SettingsModal.tsx | 125 +++++++++++++++++++++++++++++++ src/components/TopBar.tsx | 18 ++++- src/services/anthropic.ts | 5 +- 3 files changed, 146 insertions(+), 2 deletions(-) create mode 100644 src/components/SettingsModal.tsx diff --git a/src/components/SettingsModal.tsx b/src/components/SettingsModal.tsx new file mode 100644 index 0000000..c1cac7a --- /dev/null +++ b/src/components/SettingsModal.tsx @@ -0,0 +1,125 @@ +import React, { useState, useEffect } from 'react'; +import { X, Settings, Key, Save, Trash2, CheckCircle2 } from 'lucide-react'; +import { cn } from '../lib/utils'; + +interface SettingsModalProps { + isOpen: boolean; + onClose: () => void; +} + +export function SettingsModal({ isOpen, onClose }: SettingsModalProps) { + const [apiKey, setApiKey] = useState(''); + const [saved, setSaved] = useState(false); + + useEffect(() => { + if (isOpen) { + const stored = localStorage.getItem('GEMINI_API_KEY') || ''; + setApiKey(stored); + setSaved(false); + } + }, [isOpen]); + + const handleSave = () => { + if (apiKey.trim()) { + localStorage.setItem('GEMINI_API_KEY', apiKey.trim()); + } else { + localStorage.removeItem('GEMINI_API_KEY'); + } + setSaved(true); + setTimeout(() => { + setSaved(false); + onClose(); + }, 1500); + }; + + const handleClear = () => { + localStorage.removeItem('GEMINI_API_KEY'); + setApiKey(''); + setSaved(true); + setTimeout(() => setSaved(false), 1500); + }; + + if (!isOpen) return null; + + return ( +
+
+
+
+
+ +
+

AI Settings

+
+ +
+ +
+
+ +
+ setApiKey(e.target.value)} + placeholder="Enter your API key..." + className="w-full bg-slate-800 border border-slate-700 rounded-lg px-4 py-3 text-sm text-white focus:outline-none focus:border-blue-500 focus:ring-1 focus:ring-blue-500 transition-all placeholder:text-slate-600" + /> + {saved && ( +
+ +
+ )} +
+

+ This key is stored locally in your browser and will override the system default. + Useful if you want to use your own billing account. +

+
+ +
+

Instructions

+
    +
  • Go to Google AI Studio
  • +
  • Enable billing in Settings if you want to use the paid tier
  • +
  • Generate a new API key and paste it here
  • +
+
+
+ +
+ +
+ + +
+
+
+
+ ); +} diff --git a/src/components/TopBar.tsx b/src/components/TopBar.tsx index d5d7e47..6fca2a4 100644 --- a/src/components/TopBar.tsx +++ b/src/components/TopBar.tsx @@ -1,6 +1,7 @@ import React, { useState, useRef, useEffect } from 'react'; -import { Download, LogOut, Undo2, CloudUpload, Loader2, ChevronDown, RotateCcw, Maximize2, X } from 'lucide-react'; +import { Download, LogOut, Undo2, CloudUpload, Loader2, ChevronDown, RotateCcw, Maximize2, X, Settings } from 'lucide-react'; import { cn } from '../lib/utils'; +import { SettingsModal } from './SettingsModal'; interface TopBarProps { stats: any; @@ -26,6 +27,7 @@ interface TopBarProps { export function TopBar({ stats, activeModule, onExport, onRefresh, hasData, hasUnsavedChanges, userEmail, onSignOut, canUndo, onUndo, undoMessage, undoSteps, pendingCount, pendingChanges, onSaveAll, onRevertRow, isSavingAll, isMaximized, onToggleMaximize }: TopBarProps) { const [showPending, setShowPending] = useState(false); + const [showSettings, setShowSettings] = useState(false); const dropdownRef = useRef(null); useEffect(() => { @@ -190,6 +192,15 @@ export function TopBar({ stats, activeModule, onExport, onRefresh, hasData, hasU MAXIMIZE + +