diff --git a/app/layout.tsx b/app/layout.tsx index 23c919a..8a1ee64 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -13,6 +13,8 @@ export const metadata: Metadata = { viewport: "width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no", }; +import LocaleProvider from "@/components/LocaleProvider"; + export default function RootLayout({ children, }: Readonly<{ @@ -20,7 +22,11 @@ export default function RootLayout({ }>) { return ( - {children} + + + {children} + + ); } diff --git a/components/ActionPlanGenerator.tsx b/components/ActionPlanGenerator.tsx index b2b5b91..97237d3 100644 --- a/components/ActionPlanGenerator.tsx +++ b/components/ActionPlanGenerator.tsx @@ -8,17 +8,10 @@ import useStore from "@/lib/store"; import modelAdapter from "@/lib/services/adapter-instance"; import { ListTodo, Copy, Loader2, CheckCircle2, Clock, AlertTriangle, Settings } from "lucide-react"; import { cn } from "@/lib/utils"; +import { translations } from "@/lib/i18n/translations"; export default function ActionPlanGenerator() { - const { - currentPrompt, - actionPlan, - selectedProvider, - selectedModels, - availableModels, - apiKeys, - isProcessing, - error, + language, setCurrentPrompt, setSelectedProvider, setActionPlan, @@ -28,238 +21,241 @@ export default function ActionPlanGenerator() { setSelectedModel, } = useStore(); - const [copied, setCopied] = useState(false); +const t = translations[language].actionPlan; +const common = translations[language].common; - const selectedModel = selectedModels[selectedProvider]; - const models = availableModels[selectedProvider] || modelAdapter.getAvailableModels(selectedProvider); +const [copied, setCopied] = useState(false); - useEffect(() => { - if (typeof window !== "undefined") { - loadAvailableModels(); - const saved = localStorage.getItem("promptarch-api-keys"); - if (saved) { - try { - const keys = JSON.parse(saved); - if (keys.qwen) modelAdapter.updateQwenApiKey(keys.qwen); - if (keys.ollama) modelAdapter.updateOllamaApiKey(keys.ollama); - if (keys.zai) modelAdapter.updateZaiApiKey(keys.zai); - } catch (e) { - console.error("Failed to load API keys:", e); - } +const selectedModel = selectedModels[selectedProvider]; +const models = availableModels[selectedProvider] || modelAdapter.getAvailableModels(selectedProvider); + +useEffect(() => { + if (typeof window !== "undefined") { + loadAvailableModels(); + const saved = localStorage.getItem("promptarch-api-keys"); + if (saved) { + try { + const keys = JSON.parse(saved); + if (keys.qwen) modelAdapter.updateQwenApiKey(keys.qwen); + if (keys.ollama) modelAdapter.updateOllamaApiKey(keys.ollama); + if (keys.zai) modelAdapter.updateZaiApiKey(keys.zai); + } catch (e) { + console.error("Failed to load API keys:", e); } } - }, [selectedProvider]); + } +}, [selectedProvider]); - const loadAvailableModels = async () => { - const fallbackModels = modelAdapter.getAvailableModels(selectedProvider); - setAvailableModels(selectedProvider, fallbackModels); +const loadAvailableModels = async () => { + const fallbackModels = modelAdapter.getAvailableModels(selectedProvider); + setAvailableModels(selectedProvider, fallbackModels); - try { - const result = await modelAdapter.listModels(selectedProvider); - if (result.success && result.data) { - setAvailableModels(selectedProvider, result.data[selectedProvider] || fallbackModels); - } - } catch (error) { - console.error("Failed to load models:", error); + try { + const result = await modelAdapter.listModels(selectedProvider); + if (result.success && result.data) { + setAvailableModels(selectedProvider, result.data[selectedProvider] || fallbackModels); } - }; + } catch (error) { + console.error("Failed to load models:", error); + } +}; - const handleGenerate = async () => { - if (!currentPrompt.trim()) { - setError("Please enter PRD or project requirements"); - return; +const handleGenerate = async () => { + if (!currentPrompt.trim()) { + setError("Please enter PRD or project requirements"); + return; + } + + const apiKey = apiKeys[selectedProvider]; + const isQwenOAuth = selectedProvider === "qwen" && modelAdapter.hasQwenAuth(); + + if (!isQwenOAuth && (!apiKey || !apiKey.trim())) { + setError(`Please configure your ${selectedProvider.toUpperCase()} API key in Settings`); + return; + } + + setProcessing(true); + setError(null); + + console.log("[ActionPlanGenerator] Starting action plan generation...", { selectedProvider, selectedModel, hasQwenAuth: modelAdapter.hasQwenAuth() }); + + try { + const result = await modelAdapter.generateActionPlan(currentPrompt, selectedProvider, selectedModel); + + console.log("[ActionPlanGenerator] Generation result:", result); + + if (result.success && result.data) { + const newPlan = { + id: Math.random().toString(36).substr(2, 9), + prdId: "", + tasks: [], + frameworks: [], + architecture: { + pattern: "", + structure: "", + technologies: [], + bestPractices: [], + }, + estimatedDuration: "", + createdAt: new Date(), + rawContent: result.data, + }; + setActionPlan(newPlan); + } else { + console.error("[ActionPlanGenerator] Generation failed:", result.error); + setError(result.error || "Failed to generate action plan"); } + } catch (err) { + console.error("[ActionPlanGenerator] Generation error:", err); + setError(err instanceof Error ? err.message : "An error occurred"); + } finally { + setProcessing(false); + } +}; - const apiKey = apiKeys[selectedProvider]; - const isQwenOAuth = selectedProvider === "qwen" && modelAdapter.hasQwenAuth(); +const handleCopy = async () => { + if (actionPlan?.rawContent) { + await navigator.clipboard.writeText(actionPlan.rawContent); + setCopied(true); + setTimeout(() => setCopied(false), 2000); + } +}; - if (!isQwenOAuth && (!apiKey || !apiKey.trim())) { - setError(`Please configure your ${selectedProvider.toUpperCase()} API key in Settings`); - return; - } - - setProcessing(true); - setError(null); - - console.log("[ActionPlanGenerator] Starting action plan generation...", { selectedProvider, selectedModel, hasQwenAuth: modelAdapter.hasQwenAuth() }); - - try { - const result = await modelAdapter.generateActionPlan(currentPrompt, selectedProvider, selectedModel); - - console.log("[ActionPlanGenerator] Generation result:", result); - - if (result.success && result.data) { - const newPlan = { - id: Math.random().toString(36).substr(2, 9), - prdId: "", - tasks: [], - frameworks: [], - architecture: { - pattern: "", - structure: "", - technologies: [], - bestPractices: [], - }, - estimatedDuration: "", - createdAt: new Date(), - rawContent: result.data, - }; - setActionPlan(newPlan); - } else { - console.error("[ActionPlanGenerator] Generation failed:", result.error); - setError(result.error || "Failed to generate action plan"); - } - } catch (err) { - console.error("[ActionPlanGenerator] Generation error:", err); - setError(err instanceof Error ? err.message : "An error occurred"); - } finally { - setProcessing(false); - } - }; - - const handleCopy = async () => { - if (actionPlan?.rawContent) { - await navigator.clipboard.writeText(actionPlan.rawContent); - setCopied(true); - setTimeout(() => setCopied(false), 2000); - } - }; - - return ( -
- - - - - Action Plan Generator - - - Convert PRD into actionable implementation plan - - - -
- -
- {(["qwen", "ollama", "zai"] as const).map((provider) => ( - - ))} -
-
- -
- - -
- -
- -