mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-01-31 20:03:37 +00:00
feat: enhance ESLint configuration and improve component error handling
- Updated ESLint configuration to include support for `.mjs` and `.cjs` file types, adding necessary global variables for Node.js and browser environments. - Introduced a new `vite-env.d.ts` file to define environment variables for Vite, improving type safety. - Refactored error handling in `file-browser-dialog.tsx`, `description-image-dropzone.tsx`, and `feature-image-upload.tsx` to omit error parameters, simplifying the catch blocks. - Removed unused bug report button functionality from the sidebar, streamlining the component structure. - Adjusted various components to improve code readability and maintainability, including updates to type imports and component props. These changes aim to enhance the development experience by improving linting support and simplifying error handling across components.
This commit is contained in:
@@ -1,24 +1,17 @@
|
||||
|
||||
import { useState, useEffect, useCallback } from "react";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
CardDescription,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} from "@/components/ui/card";
|
||||
import { useState, useEffect, useCallback } from 'react';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { Label } from '@/components/ui/label';
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
import {
|
||||
Accordion,
|
||||
AccordionContent,
|
||||
AccordionItem,
|
||||
AccordionTrigger,
|
||||
} from "@/components/ui/accordion";
|
||||
import { useSetupStore } from "@/store/setup-store";
|
||||
import { useAppStore } from "@/store/app-store";
|
||||
import { getElectronAPI } from "@/lib/electron";
|
||||
} from '@/components/ui/accordion';
|
||||
import { useSetupStore } from '@/store/setup-store';
|
||||
import { useAppStore } from '@/store/app-store';
|
||||
import { getElectronAPI } from '@/lib/electron';
|
||||
import {
|
||||
CheckCircle2,
|
||||
Loader2,
|
||||
@@ -31,14 +24,13 @@ import {
|
||||
RefreshCw,
|
||||
Download,
|
||||
Info,
|
||||
AlertTriangle,
|
||||
ShieldCheck,
|
||||
XCircle,
|
||||
Trash2,
|
||||
} from "lucide-react";
|
||||
import { toast } from "sonner";
|
||||
import { StatusBadge, TerminalOutput } from "../components";
|
||||
import { useCliStatus, useCliInstallation, useTokenSave } from "../hooks";
|
||||
} from 'lucide-react';
|
||||
import { toast } from 'sonner';
|
||||
import { StatusBadge, TerminalOutput } from '../components';
|
||||
import { useCliStatus, useCliInstallation, useTokenSave } from '../hooks';
|
||||
|
||||
interface ClaudeSetupStepProps {
|
||||
onNext: () => void;
|
||||
@@ -46,17 +38,13 @@ interface ClaudeSetupStepProps {
|
||||
onSkip: () => void;
|
||||
}
|
||||
|
||||
type VerificationStatus = "idle" | "verifying" | "verified" | "error";
|
||||
type VerificationStatus = 'idle' | 'verifying' | 'verified' | 'error';
|
||||
|
||||
// Claude Setup Step
|
||||
// Users can either:
|
||||
// 1. Have Claude CLI installed and authenticated (verified by running a test query)
|
||||
// 2. Provide an Anthropic API key manually
|
||||
export function ClaudeSetupStep({
|
||||
onNext,
|
||||
onBack,
|
||||
onSkip,
|
||||
}: ClaudeSetupStepProps) {
|
||||
export function ClaudeSetupStep({ onNext, onBack, onSkip }: ClaudeSetupStepProps) {
|
||||
const {
|
||||
claudeCliStatus,
|
||||
claudeAuthStatus,
|
||||
@@ -66,21 +54,16 @@ export function ClaudeSetupStep({
|
||||
} = useSetupStore();
|
||||
const { setApiKeys, apiKeys } = useAppStore();
|
||||
|
||||
const [apiKey, setApiKey] = useState("");
|
||||
const [apiKey, setApiKey] = useState('');
|
||||
|
||||
// CLI Verification state
|
||||
const [cliVerificationStatus, setCliVerificationStatus] =
|
||||
useState<VerificationStatus>("idle");
|
||||
const [cliVerificationError, setCliVerificationError] = useState<
|
||||
string | null
|
||||
>(null);
|
||||
const [cliVerificationStatus, setCliVerificationStatus] = useState<VerificationStatus>('idle');
|
||||
const [cliVerificationError, setCliVerificationError] = useState<string | null>(null);
|
||||
|
||||
// API Key Verification state
|
||||
const [apiKeyVerificationStatus, setApiKeyVerificationStatus] =
|
||||
useState<VerificationStatus>("idle");
|
||||
const [apiKeyVerificationError, setApiKeyVerificationError] = useState<
|
||||
string | null
|
||||
>(null);
|
||||
useState<VerificationStatus>('idle');
|
||||
const [apiKeyVerificationError, setApiKeyVerificationError] = useState<string | null>(null);
|
||||
|
||||
// Delete API Key state
|
||||
const [isDeletingApiKey, setIsDeletingApiKey] = useState(false);
|
||||
@@ -96,14 +79,11 @@ export function ClaudeSetupStep({
|
||||
[]
|
||||
);
|
||||
|
||||
const getStoreState = useCallback(
|
||||
() => useSetupStore.getState().claudeCliStatus,
|
||||
[]
|
||||
);
|
||||
const getStoreState = useCallback(() => useSetupStore.getState().claudeCliStatus, []);
|
||||
|
||||
// Use custom hooks
|
||||
const { isChecking, checkStatus } = useCliStatus({
|
||||
cliType: "claude",
|
||||
cliType: 'claude',
|
||||
statusApi,
|
||||
setCliStatus: setClaudeCliStatus,
|
||||
setAuthStatus: setClaudeAuthStatus,
|
||||
@@ -114,120 +94,114 @@ export function ClaudeSetupStep({
|
||||
}, [checkStatus]);
|
||||
|
||||
const { isInstalling, installProgress, install } = useCliInstallation({
|
||||
cliType: "claude",
|
||||
cliType: 'claude',
|
||||
installApi,
|
||||
onProgressEvent: getElectronAPI().setup?.onInstallProgress,
|
||||
onSuccess: onInstallSuccess,
|
||||
getStoreState,
|
||||
});
|
||||
|
||||
const { isSaving: isSavingApiKey, saveToken: saveApiKeyToken } = useTokenSave(
|
||||
{
|
||||
provider: "anthropic",
|
||||
onSuccess: () => {
|
||||
setClaudeAuthStatus({
|
||||
authenticated: true,
|
||||
method: "api_key",
|
||||
hasCredentialsFile: false,
|
||||
apiKeyValid: true,
|
||||
});
|
||||
setApiKeys({ ...apiKeys, anthropic: apiKey });
|
||||
toast.success("API key saved successfully!");
|
||||
},
|
||||
}
|
||||
);
|
||||
const { isSaving: isSavingApiKey, saveToken: saveApiKeyToken } = useTokenSave({
|
||||
provider: 'anthropic',
|
||||
onSuccess: () => {
|
||||
setClaudeAuthStatus({
|
||||
authenticated: true,
|
||||
method: 'api_key',
|
||||
hasCredentialsFile: false,
|
||||
apiKeyValid: true,
|
||||
});
|
||||
setApiKeys({ ...apiKeys, anthropic: apiKey });
|
||||
toast.success('API key saved successfully!');
|
||||
},
|
||||
});
|
||||
|
||||
// Verify CLI authentication by running a test query (uses CLI credentials only, not API key)
|
||||
const verifyCliAuth = useCallback(async () => {
|
||||
setCliVerificationStatus("verifying");
|
||||
setCliVerificationStatus('verifying');
|
||||
setCliVerificationError(null);
|
||||
|
||||
try {
|
||||
const api = getElectronAPI();
|
||||
if (!api.setup?.verifyClaudeAuth) {
|
||||
setCliVerificationStatus("error");
|
||||
setCliVerificationError("Verification API not available");
|
||||
setCliVerificationStatus('error');
|
||||
setCliVerificationError('Verification API not available');
|
||||
return;
|
||||
}
|
||||
|
||||
// Pass "cli" to verify CLI authentication only (ignores any API key)
|
||||
const result = await api.setup.verifyClaudeAuth("cli");
|
||||
const result = await api.setup.verifyClaudeAuth('cli');
|
||||
|
||||
// Check for "Limit reached" error - treat as unverified
|
||||
const hasLimitReachedError =
|
||||
result.error?.toLowerCase().includes("limit reached") ||
|
||||
result.error?.toLowerCase().includes("rate limit");
|
||||
result.error?.toLowerCase().includes('limit reached') ||
|
||||
result.error?.toLowerCase().includes('rate limit');
|
||||
|
||||
if (result.authenticated && !hasLimitReachedError) {
|
||||
setCliVerificationStatus("verified");
|
||||
setCliVerificationStatus('verified');
|
||||
setClaudeAuthStatus({
|
||||
authenticated: true,
|
||||
method: "cli_authenticated",
|
||||
method: 'cli_authenticated',
|
||||
hasCredentialsFile: claudeAuthStatus?.hasCredentialsFile || false,
|
||||
});
|
||||
toast.success("Claude CLI authentication verified!");
|
||||
toast.success('Claude CLI authentication verified!');
|
||||
} else {
|
||||
setCliVerificationStatus("error");
|
||||
setCliVerificationStatus('error');
|
||||
setCliVerificationError(
|
||||
hasLimitReachedError
|
||||
? "Rate limit reached. Please try again later."
|
||||
: result.error || "Authentication failed"
|
||||
? 'Rate limit reached. Please try again later.'
|
||||
: result.error || 'Authentication failed'
|
||||
);
|
||||
setClaudeAuthStatus({
|
||||
authenticated: false,
|
||||
method: "none",
|
||||
method: 'none',
|
||||
hasCredentialsFile: claudeAuthStatus?.hasCredentialsFile || false,
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
const errorMessage =
|
||||
error instanceof Error ? error.message : "Verification failed";
|
||||
const errorMessage = error instanceof Error ? error.message : 'Verification failed';
|
||||
// Also check for limit reached in caught errors
|
||||
const isLimitError =
|
||||
errorMessage.toLowerCase().includes("limit reached") ||
|
||||
errorMessage.toLowerCase().includes("rate limit");
|
||||
setCliVerificationStatus("error");
|
||||
errorMessage.toLowerCase().includes('limit reached') ||
|
||||
errorMessage.toLowerCase().includes('rate limit');
|
||||
setCliVerificationStatus('error');
|
||||
setCliVerificationError(
|
||||
isLimitError
|
||||
? "Rate limit reached. Please try again later."
|
||||
: errorMessage
|
||||
isLimitError ? 'Rate limit reached. Please try again later.' : errorMessage
|
||||
);
|
||||
}
|
||||
}, [claudeAuthStatus, setClaudeAuthStatus]);
|
||||
|
||||
// Verify API Key authentication (uses API key only)
|
||||
const verifyApiKeyAuth = useCallback(async () => {
|
||||
setApiKeyVerificationStatus("verifying");
|
||||
setApiKeyVerificationStatus('verifying');
|
||||
setApiKeyVerificationError(null);
|
||||
|
||||
try {
|
||||
const api = getElectronAPI();
|
||||
if (!api.setup?.verifyClaudeAuth) {
|
||||
setApiKeyVerificationStatus("error");
|
||||
setApiKeyVerificationError("Verification API not available");
|
||||
setApiKeyVerificationStatus('error');
|
||||
setApiKeyVerificationError('Verification API not available');
|
||||
return;
|
||||
}
|
||||
|
||||
// Pass "api_key" to verify API key authentication only
|
||||
const result = await api.setup.verifyClaudeAuth("api_key");
|
||||
const result = await api.setup.verifyClaudeAuth('api_key');
|
||||
|
||||
if (result.authenticated) {
|
||||
setApiKeyVerificationStatus("verified");
|
||||
setApiKeyVerificationStatus('verified');
|
||||
setClaudeAuthStatus({
|
||||
authenticated: true,
|
||||
method: "api_key",
|
||||
method: 'api_key',
|
||||
hasCredentialsFile: false,
|
||||
apiKeyValid: true,
|
||||
});
|
||||
toast.success("API key authentication verified!");
|
||||
toast.success('API key authentication verified!');
|
||||
} else {
|
||||
setApiKeyVerificationStatus("error");
|
||||
setApiKeyVerificationError(result.error || "Authentication failed");
|
||||
setApiKeyVerificationStatus('error');
|
||||
setApiKeyVerificationError(result.error || 'Authentication failed');
|
||||
}
|
||||
} catch (error) {
|
||||
const errorMessage =
|
||||
error instanceof Error ? error.message : "Verification failed";
|
||||
setApiKeyVerificationStatus("error");
|
||||
const errorMessage = error instanceof Error ? error.message : 'Verification failed';
|
||||
setApiKeyVerificationStatus('error');
|
||||
setApiKeyVerificationError(errorMessage);
|
||||
}
|
||||
}, [setClaudeAuthStatus]);
|
||||
@@ -238,29 +212,28 @@ export function ClaudeSetupStep({
|
||||
try {
|
||||
const api = getElectronAPI();
|
||||
if (!api.setup?.deleteApiKey) {
|
||||
toast.error("Delete API not available");
|
||||
toast.error('Delete API not available');
|
||||
return;
|
||||
}
|
||||
|
||||
const result = await api.setup.deleteApiKey("anthropic");
|
||||
const result = await api.setup.deleteApiKey('anthropic');
|
||||
if (result.success) {
|
||||
// Clear local state
|
||||
setApiKey("");
|
||||
setApiKeys({ ...apiKeys, anthropic: "" });
|
||||
setApiKeyVerificationStatus("idle");
|
||||
setApiKey('');
|
||||
setApiKeys({ ...apiKeys, anthropic: '' });
|
||||
setApiKeyVerificationStatus('idle');
|
||||
setApiKeyVerificationError(null);
|
||||
setClaudeAuthStatus({
|
||||
authenticated: false,
|
||||
method: "none",
|
||||
method: 'none',
|
||||
hasCredentialsFile: claudeAuthStatus?.hasCredentialsFile || false,
|
||||
});
|
||||
toast.success("API key deleted successfully");
|
||||
toast.success('API key deleted successfully');
|
||||
} else {
|
||||
toast.error(result.error || "Failed to delete API key");
|
||||
toast.error(result.error || 'Failed to delete API key');
|
||||
}
|
||||
} catch (error) {
|
||||
const errorMessage =
|
||||
error instanceof Error ? error.message : "Failed to delete API key";
|
||||
const errorMessage = error instanceof Error ? error.message : 'Failed to delete API key';
|
||||
toast.error(errorMessage);
|
||||
} finally {
|
||||
setIsDeletingApiKey(false);
|
||||
@@ -282,30 +255,30 @@ export function ClaudeSetupStep({
|
||||
|
||||
const copyCommand = (command: string) => {
|
||||
navigator.clipboard.writeText(command);
|
||||
toast.success("Command copied to clipboard");
|
||||
toast.success('Command copied to clipboard');
|
||||
};
|
||||
|
||||
// User is ready if either method is verified
|
||||
const hasApiKey =
|
||||
!!apiKeys.anthropic ||
|
||||
claudeAuthStatus?.method === "api_key" ||
|
||||
claudeAuthStatus?.method === "api_key_env";
|
||||
const isCliVerified = cliVerificationStatus === "verified";
|
||||
const isApiKeyVerified = apiKeyVerificationStatus === "verified";
|
||||
claudeAuthStatus?.method === 'api_key' ||
|
||||
claudeAuthStatus?.method === 'api_key_env';
|
||||
const isCliVerified = cliVerificationStatus === 'verified';
|
||||
const isApiKeyVerified = apiKeyVerificationStatus === 'verified';
|
||||
const isReady = isCliVerified || isApiKeyVerified;
|
||||
|
||||
const getAuthMethodLabel = () => {
|
||||
if (isApiKeyVerified) return "API Key";
|
||||
if (isCliVerified) return "Claude CLI";
|
||||
if (isApiKeyVerified) return 'API Key';
|
||||
if (isCliVerified) return 'Claude CLI';
|
||||
return null;
|
||||
};
|
||||
|
||||
// Helper to get status badge for CLI
|
||||
const getCliStatusBadge = () => {
|
||||
if (cliVerificationStatus === "verified") {
|
||||
if (cliVerificationStatus === 'verified') {
|
||||
return <StatusBadge status="authenticated" label="Verified" />;
|
||||
}
|
||||
if (cliVerificationStatus === "error") {
|
||||
if (cliVerificationStatus === 'error') {
|
||||
return <StatusBadge status="error" label="Error" />;
|
||||
}
|
||||
if (isChecking) {
|
||||
@@ -320,10 +293,10 @@ export function ClaudeSetupStep({
|
||||
|
||||
// Helper to get status badge for API Key
|
||||
const getApiKeyStatusBadge = () => {
|
||||
if (apiKeyVerificationStatus === "verified") {
|
||||
if (apiKeyVerificationStatus === 'verified') {
|
||||
return <StatusBadge status="authenticated" label="Verified" />;
|
||||
}
|
||||
if (apiKeyVerificationStatus === "error") {
|
||||
if (apiKeyVerificationStatus === 'error') {
|
||||
return <StatusBadge status="error" label="Error" />;
|
||||
}
|
||||
if (hasApiKey) {
|
||||
@@ -339,9 +312,7 @@ export function ClaudeSetupStep({
|
||||
<div className="w-16 h-16 rounded-xl bg-brand-500/10 flex items-center justify-center mx-auto mb-4">
|
||||
<Terminal className="w-8 h-8 text-brand-500" />
|
||||
</div>
|
||||
<h2 className="text-2xl font-bold text-foreground mb-2">
|
||||
API Key Setup
|
||||
</h2>
|
||||
<h2 className="text-2xl font-bold text-foreground mb-2">API Key Setup</h2>
|
||||
<p className="text-muted-foreground">Configure for code generation</p>
|
||||
</div>
|
||||
|
||||
@@ -353,15 +324,8 @@ export function ClaudeSetupStep({
|
||||
<Info className="w-5 h-5" />
|
||||
Authentication Methods
|
||||
</CardTitle>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={checkStatus}
|
||||
disabled={isChecking}
|
||||
>
|
||||
<RefreshCw
|
||||
className={`w-4 h-4 ${isChecking ? "animate-spin" : ""}`}
|
||||
/>
|
||||
<Button variant="ghost" size="sm" onClick={checkStatus} disabled={isChecking}>
|
||||
<RefreshCw className={`w-4 h-4 ${isChecking ? 'animate-spin' : ''}`} />
|
||||
</Button>
|
||||
</div>
|
||||
<CardDescription>
|
||||
@@ -377,16 +341,14 @@ export function ClaudeSetupStep({
|
||||
<div className="flex items-center gap-3">
|
||||
<Terminal
|
||||
className={`w-5 h-5 ${
|
||||
cliVerificationStatus === "verified"
|
||||
? "text-green-500"
|
||||
: "text-muted-foreground"
|
||||
cliVerificationStatus === 'verified'
|
||||
? 'text-green-500'
|
||||
: 'text-muted-foreground'
|
||||
}`}
|
||||
/>
|
||||
<div className="text-left">
|
||||
<p className="font-medium text-foreground">Claude CLI</p>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Use Claude Code subscription
|
||||
</p>
|
||||
<p className="text-sm text-muted-foreground">Use Claude Code subscription</p>
|
||||
</div>
|
||||
</div>
|
||||
{getCliStatusBadge()}
|
||||
@@ -398,15 +360,11 @@ export function ClaudeSetupStep({
|
||||
<div className="space-y-4 p-4 rounded-lg bg-muted/30 border border-border">
|
||||
<div className="flex items-center gap-2">
|
||||
<Download className="w-4 h-4 text-muted-foreground" />
|
||||
<p className="font-medium text-foreground">
|
||||
Install Claude CLI
|
||||
</p>
|
||||
<p className="font-medium text-foreground">Install Claude CLI</p>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label className="text-sm text-muted-foreground">
|
||||
macOS / Linux
|
||||
</Label>
|
||||
<Label className="text-sm text-muted-foreground">macOS / Linux</Label>
|
||||
<div className="flex items-center gap-2">
|
||||
<code className="flex-1 bg-muted px-3 py-2 rounded text-sm font-mono text-foreground">
|
||||
curl -fsSL https://claude.ai/install.sh | bash
|
||||
@@ -415,9 +373,7 @@ export function ClaudeSetupStep({
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
onClick={() =>
|
||||
copyCommand(
|
||||
"curl -fsSL https://claude.ai/install.sh | bash"
|
||||
)
|
||||
copyCommand('curl -fsSL https://claude.ai/install.sh | bash')
|
||||
}
|
||||
>
|
||||
<Copy className="w-4 h-4" />
|
||||
@@ -426,9 +382,7 @@ export function ClaudeSetupStep({
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label className="text-sm text-muted-foreground">
|
||||
Windows
|
||||
</Label>
|
||||
<Label className="text-sm text-muted-foreground">Windows</Label>
|
||||
<div className="flex items-center gap-2">
|
||||
<code className="flex-1 bg-muted px-3 py-2 rounded text-sm font-mono text-foreground">
|
||||
irm https://claude.ai/install.ps1 | iex
|
||||
@@ -436,20 +390,14 @@ export function ClaudeSetupStep({
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
onClick={() =>
|
||||
copyCommand(
|
||||
"irm https://claude.ai/install.ps1 | iex"
|
||||
)
|
||||
}
|
||||
onClick={() => copyCommand('irm https://claude.ai/install.ps1 | iex')}
|
||||
>
|
||||
<Copy className="w-4 h-4" />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{isInstalling && (
|
||||
<TerminalOutput lines={installProgress.output} />
|
||||
)}
|
||||
{isInstalling && <TerminalOutput lines={installProgress.output} />}
|
||||
|
||||
<Button
|
||||
onClick={install}
|
||||
@@ -480,27 +428,21 @@ export function ClaudeSetupStep({
|
||||
)}
|
||||
|
||||
{/* CLI Verification Status */}
|
||||
{cliVerificationStatus === "verifying" && (
|
||||
{cliVerificationStatus === 'verifying' && (
|
||||
<div className="flex items-center gap-3 p-4 rounded-lg bg-blue-500/10 border border-blue-500/20">
|
||||
<Loader2 className="w-5 h-5 text-blue-500 animate-spin" />
|
||||
<div>
|
||||
<p className="font-medium text-foreground">
|
||||
Verifying CLI authentication...
|
||||
</p>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Running a test query
|
||||
</p>
|
||||
<p className="font-medium text-foreground">Verifying CLI authentication...</p>
|
||||
<p className="text-sm text-muted-foreground">Running a test query</p>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{cliVerificationStatus === "verified" && (
|
||||
{cliVerificationStatus === 'verified' && (
|
||||
<div className="flex items-center gap-3 p-4 rounded-lg bg-green-500/10 border border-green-500/20">
|
||||
<CheckCircle2 className="w-5 h-5 text-green-500" />
|
||||
<div>
|
||||
<p className="font-medium text-foreground">
|
||||
CLI Authentication verified!
|
||||
</p>
|
||||
<p className="font-medium text-foreground">CLI Authentication verified!</p>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Your Claude CLI is working correctly.
|
||||
</p>
|
||||
@@ -508,17 +450,13 @@ export function ClaudeSetupStep({
|
||||
</div>
|
||||
)}
|
||||
|
||||
{cliVerificationStatus === "error" && cliVerificationError && (
|
||||
{cliVerificationStatus === 'error' && cliVerificationError && (
|
||||
<div className="flex items-start gap-3 p-4 rounded-lg bg-red-500/10 border border-red-500/20">
|
||||
<XCircle className="w-5 h-5 text-red-500 shrink-0" />
|
||||
<div className="flex-1">
|
||||
<p className="font-medium text-foreground">
|
||||
Verification failed
|
||||
</p>
|
||||
<p className="text-sm text-red-400 mt-1">
|
||||
{cliVerificationError}
|
||||
</p>
|
||||
{cliVerificationError.includes("login") && (
|
||||
<p className="font-medium text-foreground">Verification failed</p>
|
||||
<p className="text-sm text-red-400 mt-1">{cliVerificationError}</p>
|
||||
{cliVerificationError.includes('login') && (
|
||||
<div className="mt-3 p-3 rounded bg-muted/50">
|
||||
<p className="text-sm text-muted-foreground mb-2">
|
||||
Run this command in your terminal:
|
||||
@@ -530,7 +468,7 @@ export function ClaudeSetupStep({
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
onClick={() => copyCommand("claude login")}
|
||||
onClick={() => copyCommand('claude login')}
|
||||
>
|
||||
<Copy className="w-4 h-4" />
|
||||
</Button>
|
||||
@@ -542,22 +480,19 @@ export function ClaudeSetupStep({
|
||||
)}
|
||||
|
||||
{/* CLI Verify Button - Hide if CLI is verified */}
|
||||
{cliVerificationStatus !== "verified" && (
|
||||
{cliVerificationStatus !== 'verified' && (
|
||||
<Button
|
||||
onClick={verifyCliAuth}
|
||||
disabled={
|
||||
cliVerificationStatus === "verifying" ||
|
||||
!claudeCliStatus?.installed
|
||||
}
|
||||
disabled={cliVerificationStatus === 'verifying' || !claudeCliStatus?.installed}
|
||||
className="w-full bg-brand-500 hover:bg-brand-600 text-white"
|
||||
data-testid="verify-cli-button"
|
||||
>
|
||||
{cliVerificationStatus === "verifying" ? (
|
||||
{cliVerificationStatus === 'verifying' ? (
|
||||
<>
|
||||
<Loader2 className="w-4 h-4 mr-2 animate-spin" />
|
||||
Verifying...
|
||||
</>
|
||||
) : cliVerificationStatus === "error" ? (
|
||||
) : cliVerificationStatus === 'error' ? (
|
||||
<>
|
||||
<RefreshCw className="w-4 h-4 mr-2" />
|
||||
Retry Verification
|
||||
@@ -580,15 +515,13 @@ export function ClaudeSetupStep({
|
||||
<div className="flex items-center gap-3">
|
||||
<Key
|
||||
className={`w-5 h-5 ${
|
||||
apiKeyVerificationStatus === "verified"
|
||||
? "text-green-500"
|
||||
: "text-muted-foreground"
|
||||
apiKeyVerificationStatus === 'verified'
|
||||
? 'text-green-500'
|
||||
: 'text-muted-foreground'
|
||||
}`}
|
||||
/>
|
||||
<div className="text-left">
|
||||
<p className="font-medium text-foreground">
|
||||
Anthropic API Key
|
||||
</p>
|
||||
<p className="font-medium text-foreground">Anthropic API Key</p>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Pay-per-use with your own API key
|
||||
</p>
|
||||
@@ -614,7 +547,7 @@ export function ClaudeSetupStep({
|
||||
data-testid="anthropic-api-key-input"
|
||||
/>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
Don't have an API key?{" "}
|
||||
Don't have an API key?{' '}
|
||||
<a
|
||||
href="https://console.anthropic.com/settings/keys"
|
||||
target="_blank"
|
||||
@@ -640,7 +573,7 @@ export function ClaudeSetupStep({
|
||||
Saving...
|
||||
</>
|
||||
) : (
|
||||
"Save API Key"
|
||||
'Save API Key'
|
||||
)}
|
||||
</Button>
|
||||
{hasApiKey && (
|
||||
@@ -662,27 +595,21 @@ export function ClaudeSetupStep({
|
||||
</div>
|
||||
|
||||
{/* API Key Verification Status */}
|
||||
{apiKeyVerificationStatus === "verifying" && (
|
||||
{apiKeyVerificationStatus === 'verifying' && (
|
||||
<div className="flex items-center gap-3 p-4 rounded-lg bg-blue-500/10 border border-blue-500/20">
|
||||
<Loader2 className="w-5 h-5 text-blue-500 animate-spin" />
|
||||
<div>
|
||||
<p className="font-medium text-foreground">
|
||||
Verifying API key...
|
||||
</p>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Running a test query
|
||||
</p>
|
||||
<p className="font-medium text-foreground">Verifying API key...</p>
|
||||
<p className="text-sm text-muted-foreground">Running a test query</p>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{apiKeyVerificationStatus === "verified" && (
|
||||
{apiKeyVerificationStatus === 'verified' && (
|
||||
<div className="flex items-center gap-3 p-4 rounded-lg bg-green-500/10 border border-green-500/20">
|
||||
<CheckCircle2 className="w-5 h-5 text-green-500" />
|
||||
<div>
|
||||
<p className="font-medium text-foreground">
|
||||
API Key verified!
|
||||
</p>
|
||||
<p className="font-medium text-foreground">API Key verified!</p>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Your API key is working correctly.
|
||||
</p>
|
||||
@@ -690,37 +617,30 @@ export function ClaudeSetupStep({
|
||||
</div>
|
||||
)}
|
||||
|
||||
{apiKeyVerificationStatus === "error" &&
|
||||
apiKeyVerificationError && (
|
||||
<div className="flex items-start gap-3 p-4 rounded-lg bg-red-500/10 border border-red-500/20">
|
||||
<XCircle className="w-5 h-5 text-red-500 shrink-0" />
|
||||
<div className="flex-1">
|
||||
<p className="font-medium text-foreground">
|
||||
Verification failed
|
||||
</p>
|
||||
<p className="text-sm text-red-400 mt-1">
|
||||
{apiKeyVerificationError}
|
||||
</p>
|
||||
</div>
|
||||
{apiKeyVerificationStatus === 'error' && apiKeyVerificationError && (
|
||||
<div className="flex items-start gap-3 p-4 rounded-lg bg-red-500/10 border border-red-500/20">
|
||||
<XCircle className="w-5 h-5 text-red-500 shrink-0" />
|
||||
<div className="flex-1">
|
||||
<p className="font-medium text-foreground">Verification failed</p>
|
||||
<p className="text-sm text-red-400 mt-1">{apiKeyVerificationError}</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* API Key Verify Button - Hide if API key is verified */}
|
||||
{apiKeyVerificationStatus !== "verified" && (
|
||||
{apiKeyVerificationStatus !== 'verified' && (
|
||||
<Button
|
||||
onClick={verifyApiKeyAuth}
|
||||
disabled={
|
||||
apiKeyVerificationStatus === "verifying" || !hasApiKey
|
||||
}
|
||||
disabled={apiKeyVerificationStatus === 'verifying' || !hasApiKey}
|
||||
className="w-full bg-brand-500 hover:bg-brand-600 text-white"
|
||||
data-testid="verify-api-key-button"
|
||||
>
|
||||
{apiKeyVerificationStatus === "verifying" ? (
|
||||
{apiKeyVerificationStatus === 'verifying' ? (
|
||||
<>
|
||||
<Loader2 className="w-4 h-4 mr-2 animate-spin" />
|
||||
Verifying...
|
||||
</>
|
||||
) : apiKeyVerificationStatus === "error" ? (
|
||||
) : apiKeyVerificationStatus === 'error' ? (
|
||||
<>
|
||||
<RefreshCw className="w-4 h-4 mr-2" />
|
||||
Retry Verification
|
||||
@@ -741,20 +661,12 @@ export function ClaudeSetupStep({
|
||||
|
||||
{/* Navigation */}
|
||||
<div className="flex justify-between pt-4">
|
||||
<Button
|
||||
variant="ghost"
|
||||
onClick={onBack}
|
||||
className="text-muted-foreground"
|
||||
>
|
||||
<Button variant="ghost" onClick={onBack} className="text-muted-foreground">
|
||||
<ArrowLeft className="w-4 h-4 mr-2" />
|
||||
Back
|
||||
</Button>
|
||||
<div className="flex gap-2">
|
||||
<Button
|
||||
variant="ghost"
|
||||
onClick={onSkip}
|
||||
className="text-muted-foreground"
|
||||
>
|
||||
<Button variant="ghost" onClick={onSkip} className="text-muted-foreground">
|
||||
Skip for now
|
||||
</Button>
|
||||
<Button
|
||||
|
||||
@@ -1,40 +1,28 @@
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
import { CheckCircle2, AlertCircle, Shield, Sparkles } from "lucide-react";
|
||||
import { useSetupStore } from "@/store/setup-store";
|
||||
import { useAppStore } from "@/store/app-store";
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { CheckCircle2, Sparkles } from 'lucide-react';
|
||||
|
||||
interface CompleteStepProps {
|
||||
onFinish: () => void;
|
||||
}
|
||||
|
||||
export function CompleteStep({ onFinish }: CompleteStepProps) {
|
||||
const { claudeCliStatus, claudeAuthStatus } = useSetupStore();
|
||||
const { apiKeys } = useAppStore();
|
||||
|
||||
const claudeReady =
|
||||
(claudeCliStatus?.installed && claudeAuthStatus?.authenticated) ||
|
||||
apiKeys.anthropic;
|
||||
|
||||
return (
|
||||
<div className="text-center space-y-6">
|
||||
<div className="w-20 h-20 rounded-full bg-gradient-to-br from-green-500 to-emerald-600 shadow-lg shadow-green-500/30 flex items-center justify-center mx-auto">
|
||||
<div className="w-20 h-20 rounded-full bg-linear-to-br from-green-500 to-emerald-600 shadow-lg shadow-green-500/30 flex items-center justify-center mx-auto">
|
||||
<CheckCircle2 className="w-10 h-10 text-white" />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h2 className="text-3xl font-bold text-foreground mb-3">
|
||||
Setup Complete!
|
||||
</h2>
|
||||
<h2 className="text-3xl font-bold text-foreground mb-3">Setup Complete!</h2>
|
||||
<p className="text-muted-foreground max-w-md mx-auto">
|
||||
Your development environment is configured. You're ready to start
|
||||
building with AI-powered assistance.
|
||||
Your development environment is configured. You're ready to start building with
|
||||
AI-powered assistance.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<Button
|
||||
size="lg"
|
||||
className="bg-gradient-to-r from-brand-500 to-brand-600 hover:from-brand-600 hover:to-brand-700 text-white"
|
||||
className="bg-linear-to-r from-brand-500 to-brand-600 hover:from-brand-600 hover:to-brand-700 text-white"
|
||||
onClick={onFinish}
|
||||
data-testid="setup-finish-button"
|
||||
>
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import { Terminal, ArrowRight } from "lucide-react";
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { ArrowRight } from 'lucide-react';
|
||||
|
||||
interface WelcomeStepProps {
|
||||
onNext: () => void;
|
||||
@@ -10,17 +9,14 @@ export function WelcomeStep({ onNext }: WelcomeStepProps) {
|
||||
return (
|
||||
<div className="text-center space-y-6">
|
||||
<div className="flex items-center justify-center mx-auto">
|
||||
{/* eslint-disable-next-line @next/next/no-img-element */}
|
||||
<img src="/logo.png" alt="Automaker Logo" className="w-24 h-24" />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h2 className="text-3xl font-bold text-foreground mb-3">
|
||||
Welcome to Automaker
|
||||
</h2>
|
||||
<h2 className="text-3xl font-bold text-foreground mb-3">Welcome to Automaker</h2>
|
||||
<p className="text-muted-foreground max-w-md mx-auto">
|
||||
To get started, we'll need to verify either claude code cli is
|
||||
installed or you have Anthropic api keys
|
||||
To get started, we'll need to verify either claude code cli is installed or you have
|
||||
Anthropic api keys
|
||||
</p>
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user