diff --git a/bin/goose-ultra-final/src/components/LayoutComponents.tsx b/bin/goose-ultra-final/src/components/LayoutComponents.tsx index 56b22df..f5075ec 100644 --- a/bin/goose-ultra-final/src/components/LayoutComponents.tsx +++ b/bin/goose-ultra-final/src/components/LayoutComponents.tsx @@ -4,6 +4,8 @@ import { Icons } from '../constants'; import { GlobalMode, OrchestratorState, TabId } from '../types'; import { applyPlanToExistingHtml, deleteProjectFromDisk, generateMockFiles, compilePlanToCode, MODERN_TEMPLATE_PROMPT, FRAMEWORK_TEMPLATE_PROMPT, loadProjectFilesFromDisk, writeLastActiveProjectId, ensureProjectOnDisk, loadProjectMemories, deleteMemory, updateMemory, MemoryRecord, formatMemoriesForPrompt, retrieveRelevantMemories } from '../services/automationService'; import { classifyIntent, enhancePromptWithContext, loadProjectManifest, initializeProjectContext, IntentAnalysis, ProjectManifest, CLIE_VERSION } from '../services/ContextEngine'; +import { useUser, LogoutDialog } from './UserAuth'; +import QwenAuthDialog from './QwenAuthDialog'; // --- Shared Constants --- const FRAMEWORK_KEYWORDS = ['react', 'vue', 'svelte', 'bootstrap', 'jquery', 'three.js', 'p5.js', 'angular', 'alpine']; @@ -463,10 +465,44 @@ const StatusDot = ({ active, label }: { active: boolean, label: string }) => ( export const Sidebar = () => { const { state, dispatch } = useOrchestrator(); + const { session, logout } = useUser(); if (!state.sidebarOpen) return null; const [query, setQuery] = useState(''); const [renamingId, setRenamingId] = useState(null); const [renameValue, setRenameValue] = useState(''); + const [showLogoutDialog, setShowLogoutDialog] = useState(false); + const [showQwenAuthDialog, setShowQwenAuthDialog] = useState(false); + const [qwenAuthStatus, setQwenAuthStatus] = useState<{ isAuthenticated: boolean; isValid: boolean }>({ isAuthenticated: false, isValid: false }); + const [userStats, setUserStats] = useState({ projectCount: 0, chatCount: 0, totalSizeBytes: 0 }); + + // Check Qwen auth status on mount + useEffect(() => { + const checkQwenAuth = async () => { + const electron = (window as any).electron; + if (electron?.qwenAuth?.getStatus) { + const status = await electron.qwenAuth.getStatus(); + setQwenAuthStatus(status); + } + }; + checkQwenAuth(); + }, []); + + // Load user stats when logout dialog opens + useEffect(() => { + if (showLogoutDialog && session?.userId) { + const electron = (window as any).electron; + if (electron?.user?.getStats) { + electron.user.getStats(session.userId).then(setUserStats); + } + } + }, [showLogoutDialog, session?.userId]); + + const handleLogout = async (cleanData: boolean) => { + setShowLogoutDialog(false); + await logout(cleanData); + // Force page reload to show login screen + window.location.reload(); + }; const handleSelectProject = async (projectId: string) => { dispatch({ type: 'SELECT_PROJECT', projectId }); @@ -716,24 +752,27 @@ export const Sidebar = () => { {/* Qwen OAuth Status */}
{ - // Trigger Qwen OAuth flow - const electron = (window as any).electron; - if (electron?.openQwenAuth) { - electron.openQwenAuth(); - } - }} - title="Click to authenticate with Qwen" + className={`flex items-center gap-2 text-xs cursor-pointer transition-colors p-2 hover:bg-white/5 rounded-lg mx-2 group ${qwenAuthStatus.isAuthenticated ? '' : 'opacity-70'}`} + onClick={() => setShowQwenAuthDialog(true)} + title={qwenAuthStatus.isAuthenticated ? "Qwen Cloud connected • Click to refresh" : "Click to authenticate with Qwen"} > -
- Q +
+ Q
Qwen Cloud
-
Connected • Free Tier
+
+ {qwenAuthStatus.isAuthenticated ? (qwenAuthStatus.isValid ? 'Connected • Ready' : 'Token expired') : 'Click to authenticate'} +
- + {qwenAuthStatus.isAuthenticated ? ( + + ) : ( + + )}
{/* Ollama Cloud Status */} @@ -789,8 +828,54 @@ export const Sidebar = () => {
)} + + {/* User Session Section */} + {session && ( +
+
+ Account +
+
+
+
{session.displayName}
+
+ Logged in since {new Date(session.loginAt).toLocaleDateString()} +
+
+
+
setShowLogoutDialog(true)} + className="flex items-center gap-2 text-xs cursor-pointer transition-colors p-2 hover:bg-rose-500/10 rounded-lg mx-2 group text-zinc-400 hover:text-rose-400" + title="Logout" + > + + Logout +
+
+ )} - + + {/* Dialogs */} + setShowLogoutDialog(false)} + onLogout={handleLogout} + userName={session?.displayName || 'User'} + stats={userStats} + /> + setShowQwenAuthDialog(false)} + onSuccess={async () => { + // Refresh auth status after successful auth + const electron = (window as any).electron; + if (electron?.qwenAuth?.getStatus) { + const status = await electron.qwenAuth.getStatus(); + setQwenAuthStatus(status); + } + }} + /> + ); };