mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-03-25 00:33:08 +00:00
- Extended SetupAPI interface with 20+ missing methods for Cursor, Codex, OpenCode, Gemini, and Copilot CLI integrations - Fixed WorktreeInfo type to include isCurrent and hasWorktree fields - Added null checks for optional API properties across all hooks - Fixed Feature type conflicts between @automaker/types and local definitions - Added missing CLI status hooks for all providers - Fixed type mismatches in mutation callbacks and event handlers - Removed dead code referencing non-existent GlobalSettings properties - Updated mock implementations in electron.ts for all new API methods Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
189 lines
7.5 KiB
TypeScript
189 lines
7.5 KiB
TypeScript
import { useState } from 'react';
|
|
import { useNavigate } from '@tanstack/react-router';
|
|
import { Button } from '@/components/ui/button';
|
|
import {
|
|
Select,
|
|
SelectContent,
|
|
SelectItem,
|
|
SelectTrigger,
|
|
SelectValue,
|
|
} from '@/components/ui/select';
|
|
import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip';
|
|
import { toast } from 'sonner';
|
|
import { LogOut, User, Code2, RefreshCw } from 'lucide-react';
|
|
import { Spinner } from '@/components/ui/spinner';
|
|
import { cn } from '@/lib/utils';
|
|
import { logout } from '@/lib/http-api-client';
|
|
import { useAuthStore } from '@/store/auth-store';
|
|
import { useAppStore } from '@/store/app-store';
|
|
import {
|
|
useAvailableEditors,
|
|
useEffectiveDefaultEditor,
|
|
type EditorInfo,
|
|
} from '@/components/views/board-view/worktree-panel/hooks/use-available-editors';
|
|
import { getEditorIcon } from '@/components/icons/editor-icons';
|
|
|
|
export function AccountSection() {
|
|
const navigate = useNavigate();
|
|
const [isLoggingOut, setIsLoggingOut] = useState(false);
|
|
|
|
// Editor settings
|
|
const { editors, isLoading: isLoadingEditors, isRefreshing, refresh } = useAvailableEditors();
|
|
const defaultEditorCommand = useAppStore((s) => s.defaultEditorCommand);
|
|
const setDefaultEditorCommand = useAppStore((s) => s.setDefaultEditorCommand);
|
|
|
|
// Use shared hook for effective default editor
|
|
const effectiveEditor = useEffectiveDefaultEditor(editors);
|
|
|
|
// Normalize Select value: if saved editor isn't found, show 'auto'
|
|
const hasSavedEditor =
|
|
!!defaultEditorCommand && editors.some((e: EditorInfo) => e.command === defaultEditorCommand);
|
|
const selectValue = hasSavedEditor ? defaultEditorCommand : 'auto';
|
|
|
|
// Get icon component for the effective editor
|
|
const EffectiveEditorIcon = effectiveEditor ? getEditorIcon(effectiveEditor.command) : null;
|
|
|
|
const handleRefreshEditors = async () => {
|
|
await refresh();
|
|
toast.success('Editor list refreshed');
|
|
};
|
|
|
|
const handleLogout = async () => {
|
|
setIsLoggingOut(true);
|
|
try {
|
|
await logout();
|
|
// Reset auth state
|
|
useAuthStore.getState().resetAuth();
|
|
// Navigate to logged out page
|
|
navigate({ to: '/logged-out' });
|
|
} catch (error) {
|
|
console.error('Logout failed:', error);
|
|
setIsLoggingOut(false);
|
|
}
|
|
};
|
|
|
|
return (
|
|
<div
|
|
className={cn(
|
|
'rounded-2xl overflow-hidden',
|
|
'border border-border/50',
|
|
'bg-gradient-to-br from-card/80 via-card/70 to-card/80 backdrop-blur-xl',
|
|
'shadow-sm'
|
|
)}
|
|
>
|
|
<div className="p-6 border-b border-border/30 bg-gradient-to-r from-primary/5 via-transparent to-transparent">
|
|
<div className="flex items-center gap-3 mb-2">
|
|
<div className="w-9 h-9 rounded-xl bg-gradient-to-br from-primary/20 to-primary/10 flex items-center justify-center border border-primary/20">
|
|
<User className="w-5 h-5 text-primary" />
|
|
</div>
|
|
<h2 className="text-lg font-semibold text-foreground tracking-tight">Account</h2>
|
|
</div>
|
|
<p className="text-sm text-muted-foreground/80 ml-12">Manage your session and account.</p>
|
|
</div>
|
|
<div className="p-6 space-y-4">
|
|
{/* Default IDE */}
|
|
<div className="flex items-center justify-between gap-4 p-4 rounded-xl bg-muted/30 border border-border/30">
|
|
<div className="flex items-center gap-3.5 min-w-0">
|
|
<div className="w-11 h-11 rounded-xl bg-gradient-to-br from-muted/50 to-muted/30 border border-border/30 flex items-center justify-center shrink-0">
|
|
<Code2 className="w-5 h-5 text-muted-foreground" />
|
|
</div>
|
|
<div className="min-w-0">
|
|
<p className="font-medium text-foreground">Default IDE</p>
|
|
<p className="text-xs text-muted-foreground/70 mt-0.5">
|
|
Default IDE to use when opening branches or worktrees
|
|
</p>
|
|
</div>
|
|
</div>
|
|
<div className="flex items-center gap-2">
|
|
<Select
|
|
value={selectValue}
|
|
onValueChange={(value) => setDefaultEditorCommand(value === 'auto' ? null : value)}
|
|
disabled={isLoadingEditors || isRefreshing || editors.length === 0}
|
|
>
|
|
<SelectTrigger className="w-[180px] shrink-0">
|
|
<SelectValue placeholder="Select editor">
|
|
{effectiveEditor ? (
|
|
<span className="flex items-center gap-2">
|
|
{EffectiveEditorIcon && <EffectiveEditorIcon className="w-4 h-4" />}
|
|
{effectiveEditor.name}
|
|
{selectValue === 'auto' && (
|
|
<span className="text-muted-foreground text-xs">(Auto)</span>
|
|
)}
|
|
</span>
|
|
) : (
|
|
'Select editor'
|
|
)}
|
|
</SelectValue>
|
|
</SelectTrigger>
|
|
<SelectContent>
|
|
<SelectItem value="auto">
|
|
<span className="flex items-center gap-2">
|
|
<Code2 className="w-4 h-4" />
|
|
Auto-detect
|
|
</span>
|
|
</SelectItem>
|
|
{editors.map((editor: EditorInfo) => {
|
|
const Icon = getEditorIcon(editor.command);
|
|
return (
|
|
<SelectItem key={editor.command} value={editor.command}>
|
|
<span className="flex items-center gap-2">
|
|
<Icon className="w-4 h-4" />
|
|
{editor.name}
|
|
</span>
|
|
</SelectItem>
|
|
);
|
|
})}
|
|
</SelectContent>
|
|
</Select>
|
|
<Tooltip>
|
|
<TooltipTrigger asChild>
|
|
<Button
|
|
variant="ghost"
|
|
size="icon"
|
|
onClick={handleRefreshEditors}
|
|
disabled={isRefreshing || isLoadingEditors}
|
|
className="shrink-0 h-9 w-9"
|
|
>
|
|
{isRefreshing ? <Spinner size="sm" /> : <RefreshCw className="w-4 h-4" />}
|
|
</Button>
|
|
</TooltipTrigger>
|
|
<TooltipContent>
|
|
<p>Refresh available editors</p>
|
|
</TooltipContent>
|
|
</Tooltip>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Logout */}
|
|
<div className="flex items-center justify-between gap-4 p-4 rounded-xl bg-muted/30 border border-border/30">
|
|
<div className="flex items-center gap-3.5 min-w-0">
|
|
<div className="w-11 h-11 rounded-xl bg-gradient-to-br from-muted/50 to-muted/30 border border-border/30 flex items-center justify-center shrink-0">
|
|
<LogOut className="w-5 h-5 text-muted-foreground" />
|
|
</div>
|
|
<div className="min-w-0">
|
|
<p className="font-medium text-foreground">Log Out</p>
|
|
<p className="text-xs text-muted-foreground/70 mt-0.5">
|
|
End your current session and return to the login screen
|
|
</p>
|
|
</div>
|
|
</div>
|
|
<Button
|
|
variant="outline"
|
|
onClick={handleLogout}
|
|
disabled={isLoggingOut}
|
|
data-testid="logout-button"
|
|
className={cn(
|
|
'shrink-0 gap-2',
|
|
'transition-all duration-200 ease-out',
|
|
'hover:scale-[1.02] active:scale-[0.98]'
|
|
)}
|
|
>
|
|
<LogOut className="w-4 h-4" />
|
|
{isLoggingOut ? 'Logging out...' : 'Log Out'}
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|