From 82cc8abd297a78c2c84b4f81492e85b2f9037c25 Mon Sep 17 00:00:00 2001 From: Kacper Date: Thu, 11 Dec 2025 01:00:42 +0100 Subject: [PATCH] refactor(settings): enhance project handling in SettingsView - Introduce type conversion for ElectronProject to SettingsProject - Update effective theme calculation to use converted settingsProject - Refactor currentProject references to use settingsProject in Appearance and DangerZone sections - Improve type safety and maintainability in settings-view.tsx --- app/src/components/views/settings-view.tsx | 21 ++- .../shared/api-provider-config.ts | 148 ++++++++++++++++++ 2 files changed, 166 insertions(+), 3 deletions(-) create mode 100644 app/src/components/views/settings-view/shared/api-provider-config.ts diff --git a/app/src/components/views/settings-view.tsx b/app/src/components/views/settings-view.tsx index 2576c88a..8f6806ec 100644 --- a/app/src/components/views/settings-view.tsx +++ b/app/src/components/views/settings-view.tsx @@ -18,6 +18,8 @@ import { KanbanDisplaySection } from "./settings-view/kanban-display/kanban-disp import { KeyboardShortcutsSection } from "./settings-view/keyboard-shortcuts/keyboard-shortcuts-section"; import { FeatureDefaultsSection } from "./settings-view/feature-defaults/feature-defaults-section"; import { DangerZoneSection } from "./settings-view/danger-zone/danger-zone-section"; +import type { Project as SettingsProject, Theme } from "./settings-view/shared/types"; +import type { Project as ElectronProject } from "@/lib/electron"; export function SettingsView() { const { @@ -37,8 +39,21 @@ export function SettingsView() { moveProjectToTrash, } = useAppStore(); + // Convert electron Project to settings-view Project type + const convertProject = (project: ElectronProject | null): SettingsProject | null => { + if (!project) return null; + return { + id: project.id, + name: project.name, + path: project.path, + theme: project.theme as Theme | undefined, + }; + }; + + const settingsProject = convertProject(currentProject); + // Compute the effective theme for the current project - const effectiveTheme = currentProject?.theme || theme; + const effectiveTheme = (settingsProject?.theme || theme) as Theme; // Handler to set theme - saves to project if one is selected, otherwise to global const handleSetTheme = (newTheme: typeof theme) => { @@ -111,7 +126,7 @@ export function SettingsView() { {/* Appearance Section */} @@ -138,7 +153,7 @@ export function SettingsView() { {/* Danger Zone Section - Only show when a project is selected */} setShowDeleteDialog(true)} /> diff --git a/app/src/components/views/settings-view/shared/api-provider-config.ts b/app/src/components/views/settings-view/shared/api-provider-config.ts new file mode 100644 index 00000000..79f5b63e --- /dev/null +++ b/app/src/components/views/settings-view/shared/api-provider-config.ts @@ -0,0 +1,148 @@ +import type { Dispatch, SetStateAction } from "react"; +import type { ApiKeys } from "@/store/app-store"; + +export type ProviderKey = "anthropic" | "google" | "openai"; + +export interface ProviderConfig { + key: ProviderKey; + label: string; + inputId: string; + placeholder: string; + value: string; + setValue: Dispatch>; + showValue: boolean; + setShowValue: Dispatch>; + hasStoredKey: string | null | undefined; + inputTestId: string; + toggleTestId: string; + testButton: { + onClick: () => Promise | void; + disabled: boolean; + loading: boolean; + testId: string; + }; + result: { success: boolean; message: string } | null; + resultTestId: string; + resultMessageTestId: string; + descriptionPrefix: string; + descriptionLinkHref: string; + descriptionLinkText: string; + descriptionSuffix?: string; +} + +export interface ProviderConfigParams { + apiKeys: ApiKeys; + anthropic: { + value: string; + setValue: Dispatch>; + show: boolean; + setShow: Dispatch>; + testing: boolean; + onTest: () => Promise; + result: { success: boolean; message: string } | null; + }; + google: { + value: string; + setValue: Dispatch>; + show: boolean; + setShow: Dispatch>; + testing: boolean; + onTest: () => Promise; + result: { success: boolean; message: string } | null; + }; + openai: { + value: string; + setValue: Dispatch>; + show: boolean; + setShow: Dispatch>; + testing: boolean; + onTest: () => Promise; + result: { success: boolean; message: string } | null; + }; +} + +export const buildProviderConfigs = ({ + apiKeys, + anthropic, + google, + openai, +}: ProviderConfigParams): ProviderConfig[] => [ + { + key: "anthropic", + label: "Anthropic API Key (Claude)", + inputId: "anthropic-key", + placeholder: "sk-ant-...", + value: anthropic.value, + setValue: anthropic.setValue, + showValue: anthropic.show, + setShowValue: anthropic.setShow, + hasStoredKey: apiKeys.anthropic, + inputTestId: "anthropic-api-key-input", + toggleTestId: "toggle-anthropic-visibility", + testButton: { + onClick: anthropic.onTest, + disabled: !anthropic.value || anthropic.testing, + loading: anthropic.testing, + testId: "test-claude-connection", + }, + result: anthropic.result, + resultTestId: "test-connection-result", + resultMessageTestId: "test-connection-message", + descriptionPrefix: "Used for Claude AI features. Get your key at", + descriptionLinkHref: "https://console.anthropic.com/account/keys", + descriptionLinkText: "console.anthropic.com", + descriptionSuffix: + ". Alternatively, the CLAUDE_CODE_OAUTH_TOKEN environment variable can be used.", + }, + { + key: "google", + label: "Google API Key (Gemini)", + inputId: "google-key", + placeholder: "AIza...", + value: google.value, + setValue: google.setValue, + showValue: google.show, + setShowValue: google.setShow, + hasStoredKey: apiKeys.google, + inputTestId: "google-api-key-input", + toggleTestId: "toggle-google-visibility", + testButton: { + onClick: google.onTest, + disabled: !google.value || google.testing, + loading: google.testing, + testId: "test-gemini-connection", + }, + result: google.result, + resultTestId: "gemini-test-connection-result", + resultMessageTestId: "gemini-test-connection-message", + descriptionPrefix: + "Used for Gemini AI features (including image/design prompts). Get your key at", + descriptionLinkHref: "https://makersuite.google.com/app/apikey", + descriptionLinkText: "makersuite.google.com", + }, + { + key: "openai", + label: "OpenAI API Key (Codex/GPT)", + inputId: "openai-key", + placeholder: "sk-...", + value: openai.value, + setValue: openai.setValue, + showValue: openai.show, + setShowValue: openai.setShow, + hasStoredKey: apiKeys.openai, + inputTestId: "openai-api-key-input", + toggleTestId: "toggle-openai-visibility", + testButton: { + onClick: openai.onTest, + disabled: !openai.value || openai.testing, + loading: openai.testing, + testId: "test-openai-connection", + }, + result: openai.result, + resultTestId: "openai-test-connection-result", + resultMessageTestId: "openai-test-connection-message", + descriptionPrefix: "Used for OpenAI Codex CLI and GPT models. Get your key at", + descriptionLinkHref: "https://platform.openai.com/api-keys", + descriptionLinkText: "platform.openai.com", + }, +];