mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-01-31 20:03:37 +00:00
style: fix formatting with Prettier
🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { ReactNode } from "react";
|
||||
import { ReactNode } from 'react';
|
||||
|
||||
interface AuthMethodOption {
|
||||
id: string;
|
||||
@@ -17,39 +17,38 @@ interface AuthMethodSelectorProps {
|
||||
// Map badge colors to complete Tailwind class names
|
||||
const getBadgeClasses = (badgeColor: string) => {
|
||||
const colorMap: Record<string, { border: string; bg: string; text: string }> = {
|
||||
"brand-500": {
|
||||
border: "hover:border-brand-500/50",
|
||||
bg: "hover:bg-brand-500/5",
|
||||
text: "text-brand-500",
|
||||
'brand-500': {
|
||||
border: 'hover:border-brand-500/50',
|
||||
bg: 'hover:bg-brand-500/5',
|
||||
text: 'text-brand-500',
|
||||
},
|
||||
"green-500": {
|
||||
border: "hover:border-green-500/50",
|
||||
bg: "hover:bg-green-500/5",
|
||||
text: "text-green-500",
|
||||
'green-500': {
|
||||
border: 'hover:border-green-500/50',
|
||||
bg: 'hover:bg-green-500/5',
|
||||
text: 'text-green-500',
|
||||
},
|
||||
"blue-500": {
|
||||
border: "hover:border-blue-500/50",
|
||||
bg: "hover:bg-blue-500/5",
|
||||
text: "text-blue-500",
|
||||
'blue-500': {
|
||||
border: 'hover:border-blue-500/50',
|
||||
bg: 'hover:bg-blue-500/5',
|
||||
text: 'text-blue-500',
|
||||
},
|
||||
"purple-500": {
|
||||
border: "hover:border-purple-500/50",
|
||||
bg: "hover:bg-purple-500/5",
|
||||
text: "text-purple-500",
|
||||
'purple-500': {
|
||||
border: 'hover:border-purple-500/50',
|
||||
bg: 'hover:bg-purple-500/5',
|
||||
text: 'text-purple-500',
|
||||
},
|
||||
};
|
||||
|
||||
return colorMap[badgeColor] || {
|
||||
border: "hover:border-brand-500/50",
|
||||
bg: "hover:bg-brand-500/5",
|
||||
text: "text-brand-500",
|
||||
};
|
||||
return (
|
||||
colorMap[badgeColor] || {
|
||||
border: 'hover:border-brand-500/50',
|
||||
bg: 'hover:bg-brand-500/5',
|
||||
text: 'text-brand-500',
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
export function AuthMethodSelector({
|
||||
options,
|
||||
onSelect,
|
||||
}: AuthMethodSelectorProps) {
|
||||
export function AuthMethodSelector({ options, onSelect }: AuthMethodSelectorProps) {
|
||||
return (
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
{options.map((option) => {
|
||||
@@ -65,12 +64,8 @@ export function AuthMethodSelector({
|
||||
{option.icon}
|
||||
<div>
|
||||
<p className="font-medium text-foreground">{option.title}</p>
|
||||
<p className="text-sm text-muted-foreground mt-1">
|
||||
{option.description}
|
||||
</p>
|
||||
<p className={`text-xs ${badgeClasses.text} mt-2`}>
|
||||
{option.badge}
|
||||
</p>
|
||||
<p className="text-sm text-muted-foreground mt-1">{option.description}</p>
|
||||
<p className={`text-xs ${badgeClasses.text} mt-2`}>{option.badge}</p>
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
|
||||
@@ -1,14 +1,8 @@
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
CardDescription,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} from "@/components/ui/card";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Download, Loader2, AlertCircle } from "lucide-react";
|
||||
import { CopyableCommandField } from "./copyable-command-field";
|
||||
import { TerminalOutput } from "./terminal-output";
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Download, Loader2, AlertCircle } from 'lucide-react';
|
||||
import { CopyableCommandField } from './copyable-command-field';
|
||||
import { TerminalOutput } from './terminal-output';
|
||||
|
||||
interface CommandInfo {
|
||||
label: string; // e.g., "macOS / Linux"
|
||||
@@ -23,7 +17,7 @@ interface CliInstallationCardProps {
|
||||
installProgress: { output: string[] };
|
||||
onInstall: () => void;
|
||||
warningMessage?: string;
|
||||
color?: "brand" | "green"; // For different CLI themes
|
||||
color?: 'brand' | 'green'; // For different CLI themes
|
||||
}
|
||||
|
||||
export function CliInstallationCard({
|
||||
@@ -34,11 +28,11 @@ export function CliInstallationCard({
|
||||
installProgress,
|
||||
onInstall,
|
||||
warningMessage,
|
||||
color = "brand",
|
||||
color = 'brand',
|
||||
}: CliInstallationCardProps) {
|
||||
const colorClasses = {
|
||||
brand: "bg-brand-500 hover:bg-brand-600",
|
||||
green: "bg-green-500 hover:bg-green-600",
|
||||
brand: 'bg-brand-500 hover:bg-brand-600',
|
||||
green: 'bg-green-500 hover:bg-green-600',
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -52,16 +46,10 @@ export function CliInstallationCard({
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-4">
|
||||
{commands.map((cmd, index) => (
|
||||
<CopyableCommandField
|
||||
key={index}
|
||||
label={cmd.label}
|
||||
command={cmd.command}
|
||||
/>
|
||||
<CopyableCommandField key={index} label={cmd.label} command={cmd.command} />
|
||||
))}
|
||||
|
||||
{isInstalling && (
|
||||
<TerminalOutput lines={installProgress.output} />
|
||||
)}
|
||||
{isInstalling && <TerminalOutput lines={installProgress.output} />}
|
||||
|
||||
<Button
|
||||
onClick={onInstall}
|
||||
@@ -86,9 +74,7 @@ export function CliInstallationCard({
|
||||
<div className="p-3 rounded-lg bg-yellow-500/10 border border-yellow-500/20">
|
||||
<div className="flex items-start gap-2">
|
||||
<AlertCircle className="w-4 h-4 text-yellow-500 mt-0.5" />
|
||||
<p className="text-xs text-yellow-600 dark:text-yellow-400">
|
||||
{warningMessage}
|
||||
</p>
|
||||
<p className="text-xs text-yellow-600 dark:text-yellow-400">{warningMessage}</p>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -1,26 +1,21 @@
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Copy } from "lucide-react";
|
||||
import { toast } from "sonner";
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Copy } from 'lucide-react';
|
||||
import { toast } from 'sonner';
|
||||
|
||||
interface CopyableCommandFieldProps {
|
||||
command: string;
|
||||
label?: string;
|
||||
}
|
||||
|
||||
export function CopyableCommandField({
|
||||
command,
|
||||
label,
|
||||
}: CopyableCommandFieldProps) {
|
||||
export function CopyableCommandField({ command, label }: CopyableCommandFieldProps) {
|
||||
const copyToClipboard = () => {
|
||||
navigator.clipboard.writeText(command);
|
||||
toast.success("Command copied to clipboard");
|
||||
toast.success('Command copied to clipboard');
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="space-y-2">
|
||||
{label && (
|
||||
<span className="text-sm text-muted-foreground">{label}</span>
|
||||
)}
|
||||
{label && <span className="text-sm text-muted-foreground">{label}</span>}
|
||||
<div className="flex items-center gap-2">
|
||||
<code className="flex-1 bg-muted px-3 py-2 rounded text-sm font-mono text-foreground">
|
||||
{command}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
// Re-export all setup-view components for easier imports
|
||||
export { StepIndicator } from "./step-indicator";
|
||||
export { StatusBadge } from "./status-badge";
|
||||
export { StatusRow } from "./status-row";
|
||||
export { TerminalOutput } from "./terminal-output";
|
||||
export { CopyableCommandField } from "./copyable-command-field";
|
||||
export { CliInstallationCard } from "./cli-installation-card";
|
||||
export { ReadyStateCard } from "./ready-state-card";
|
||||
export { AuthMethodSelector } from "./auth-method-selector";
|
||||
export { StepIndicator } from './step-indicator';
|
||||
export { StatusBadge } from './status-badge';
|
||||
export { StatusRow } from './status-row';
|
||||
export { TerminalOutput } from './terminal-output';
|
||||
export { CopyableCommandField } from './copyable-command-field';
|
||||
export { CliInstallationCard } from './cli-installation-card';
|
||||
export { ReadyStateCard } from './ready-state-card';
|
||||
export { AuthMethodSelector } from './auth-method-selector';
|
||||
|
||||
@@ -1,25 +1,21 @@
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
import { CheckCircle2 } from "lucide-react";
|
||||
import { Card, CardContent } from '@/components/ui/card';
|
||||
import { CheckCircle2 } from 'lucide-react';
|
||||
|
||||
interface ReadyStateCardProps {
|
||||
title: string;
|
||||
description: string;
|
||||
variant?: "success" | "info";
|
||||
variant?: 'success' | 'info';
|
||||
}
|
||||
|
||||
export function ReadyStateCard({
|
||||
title,
|
||||
description,
|
||||
variant = "success",
|
||||
}: ReadyStateCardProps) {
|
||||
export function ReadyStateCard({ title, description, variant = 'success' }: ReadyStateCardProps) {
|
||||
const variantClasses = {
|
||||
success: "bg-green-500/5 border-green-500/20",
|
||||
info: "bg-blue-500/5 border-blue-500/20",
|
||||
success: 'bg-green-500/5 border-green-500/20',
|
||||
info: 'bg-blue-500/5 border-blue-500/20',
|
||||
};
|
||||
|
||||
const iconColorClasses = {
|
||||
success: "bg-green-500/10 text-green-500",
|
||||
info: "bg-blue-500/10 text-blue-500",
|
||||
success: 'bg-green-500/10 text-green-500',
|
||||
info: 'bg-blue-500/10 text-blue-500',
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@@ -1,46 +1,46 @@
|
||||
import { CheckCircle2, XCircle, Loader2, AlertCircle } from "lucide-react";
|
||||
import { CheckCircle2, XCircle, Loader2, AlertCircle } from 'lucide-react';
|
||||
|
||||
interface StatusBadgeProps {
|
||||
status:
|
||||
| "installed"
|
||||
| "not_installed"
|
||||
| "checking"
|
||||
| "authenticated"
|
||||
| "not_authenticated"
|
||||
| "error"
|
||||
| "unverified";
|
||||
| 'installed'
|
||||
| 'not_installed'
|
||||
| 'checking'
|
||||
| 'authenticated'
|
||||
| 'not_authenticated'
|
||||
| 'error'
|
||||
| 'unverified';
|
||||
label: string;
|
||||
}
|
||||
|
||||
export function StatusBadge({ status, label }: StatusBadgeProps) {
|
||||
const getStatusConfig = () => {
|
||||
switch (status) {
|
||||
case "installed":
|
||||
case "authenticated":
|
||||
case 'installed':
|
||||
case 'authenticated':
|
||||
return {
|
||||
icon: <CheckCircle2 className="w-4 h-4" />,
|
||||
className: "bg-green-500/10 text-green-500 border-green-500/20",
|
||||
className: 'bg-green-500/10 text-green-500 border-green-500/20',
|
||||
};
|
||||
case "not_installed":
|
||||
case "not_authenticated":
|
||||
case 'not_installed':
|
||||
case 'not_authenticated':
|
||||
return {
|
||||
icon: <XCircle className="w-4 h-4" />,
|
||||
className: "bg-red-500/10 text-red-500 border-red-500/20",
|
||||
className: 'bg-red-500/10 text-red-500 border-red-500/20',
|
||||
};
|
||||
case "error":
|
||||
case 'error':
|
||||
return {
|
||||
icon: <XCircle className="w-4 h-4" />,
|
||||
className: "bg-red-500/10 text-red-500 border-red-500/20",
|
||||
className: 'bg-red-500/10 text-red-500 border-red-500/20',
|
||||
};
|
||||
case "checking":
|
||||
case 'checking':
|
||||
return {
|
||||
icon: <Loader2 className="w-4 h-4 animate-spin" />,
|
||||
className: "bg-yellow-500/10 text-yellow-500 border-yellow-500/20",
|
||||
className: 'bg-yellow-500/10 text-yellow-500 border-yellow-500/20',
|
||||
};
|
||||
case "unverified":
|
||||
case 'unverified':
|
||||
return {
|
||||
icon: <AlertCircle className="w-4 h-4" />,
|
||||
className: "bg-yellow-500/10 text-yellow-500 border-yellow-500/20",
|
||||
className: 'bg-yellow-500/10 text-yellow-500 border-yellow-500/20',
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,31 +1,19 @@
|
||||
import { StatusBadge } from "./status-badge";
|
||||
import { StatusBadge } from './status-badge';
|
||||
|
||||
interface StatusRowProps {
|
||||
label: string;
|
||||
status:
|
||||
| "checking"
|
||||
| "installed"
|
||||
| "not_installed"
|
||||
| "authenticated"
|
||||
| "not_authenticated";
|
||||
status: 'checking' | 'installed' | 'not_installed' | 'authenticated' | 'not_authenticated';
|
||||
statusLabel: string;
|
||||
metadata?: string; // e.g., "(Subscription Token)"
|
||||
}
|
||||
|
||||
export function StatusRow({
|
||||
label,
|
||||
status,
|
||||
statusLabel,
|
||||
metadata,
|
||||
}: StatusRowProps) {
|
||||
export function StatusRow({ label, status, statusLabel, metadata }: StatusRowProps) {
|
||||
return (
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-sm text-foreground">{label}</span>
|
||||
<div className="flex items-center gap-2">
|
||||
<StatusBadge status={status} label={statusLabel} />
|
||||
{metadata && (
|
||||
<span className="text-xs text-muted-foreground">{metadata}</span>
|
||||
)}
|
||||
{metadata && <span className="text-xs text-muted-foreground">{metadata}</span>}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -3,19 +3,14 @@ interface StepIndicatorProps {
|
||||
totalSteps: number;
|
||||
}
|
||||
|
||||
export function StepIndicator({
|
||||
currentStep,
|
||||
totalSteps,
|
||||
}: StepIndicatorProps) {
|
||||
export function StepIndicator({ currentStep, totalSteps }: StepIndicatorProps) {
|
||||
return (
|
||||
<div className="flex items-center justify-center gap-2 mb-8">
|
||||
{Array.from({ length: totalSteps }).map((_, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className={`h-2 rounded-full transition-all duration-300 ${
|
||||
index <= currentStep
|
||||
? "w-8 bg-brand-500"
|
||||
: "w-2 bg-muted-foreground/30"
|
||||
index <= currentStep ? 'w-8 bg-brand-500' : 'w-2 bg-muted-foreground/30'
|
||||
}`}
|
||||
/>
|
||||
))}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Re-export all hooks for easier imports
|
||||
export { useCliStatus } from "./use-cli-status";
|
||||
export { useCliInstallation } from "./use-cli-installation";
|
||||
export { useTokenSave } from "./use-token-save";
|
||||
export { useCliStatus } from './use-cli-status';
|
||||
export { useCliInstallation } from './use-cli-installation';
|
||||
export { useTokenSave } from './use-token-save';
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { useState, useCallback } from "react";
|
||||
import { toast } from "sonner";
|
||||
import { useState, useCallback } from 'react';
|
||||
import { toast } from 'sonner';
|
||||
|
||||
interface UseCliInstallationOptions {
|
||||
cliType: "claude";
|
||||
cliType: 'claude';
|
||||
installApi: () => Promise<any>;
|
||||
onProgressEvent?: (callback: (progress: any) => void) => (() => void) | undefined;
|
||||
onSuccess?: () => void;
|
||||
@@ -29,20 +29,22 @@ export function useCliInstallation({
|
||||
let unsubscribe: (() => void) | undefined;
|
||||
|
||||
if (onProgressEvent) {
|
||||
unsubscribe = onProgressEvent((progress: { cli?: string; data?: string; type?: string }) => {
|
||||
if (progress.cli === cliType) {
|
||||
setInstallProgress((prev) => ({
|
||||
output: [...prev.output, progress.data || progress.type || ""],
|
||||
}));
|
||||
unsubscribe = onProgressEvent(
|
||||
(progress: { cli?: string; data?: string; type?: string }) => {
|
||||
if (progress.cli === cliType) {
|
||||
setInstallProgress((prev) => ({
|
||||
output: [...prev.output, progress.data || progress.type || ''],
|
||||
}));
|
||||
}
|
||||
}
|
||||
});
|
||||
);
|
||||
}
|
||||
|
||||
const result = await installApi();
|
||||
unsubscribe?.();
|
||||
|
||||
if (result.success) {
|
||||
if (cliType === "claude" && onSuccess && getStoreState) {
|
||||
if (cliType === 'claude' && onSuccess && getStoreState) {
|
||||
// Claude-specific: retry logic to detect installation
|
||||
let retries = 5;
|
||||
let detected = false;
|
||||
@@ -68,7 +70,7 @@ export function useCliInstallation({
|
||||
if (!detected) {
|
||||
toast.success(`${cliType} CLI installation completed`, {
|
||||
description:
|
||||
"The CLI was installed but may need a terminal restart to be detected. You can continue with authentication if you have a token.",
|
||||
'The CLI was installed but may need a terminal restart to be detected. You can continue with authentication if you have a token.',
|
||||
duration: 7000,
|
||||
});
|
||||
}
|
||||
@@ -77,11 +79,11 @@ export function useCliInstallation({
|
||||
onSuccess?.();
|
||||
}
|
||||
} else {
|
||||
toast.error("Installation failed", { description: result.error });
|
||||
toast.error('Installation failed', { description: result.error });
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`Failed to install ${cliType}:`, error);
|
||||
toast.error("Installation failed");
|
||||
toast.error('Installation failed');
|
||||
} finally {
|
||||
setIsInstalling(false);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useState, useCallback } from "react";
|
||||
import { useState, useCallback } from 'react';
|
||||
|
||||
interface UseCliStatusOptions {
|
||||
cliType: "claude";
|
||||
cliType: 'claude';
|
||||
statusApi: () => Promise<any>;
|
||||
setCliStatus: (status: any) => void;
|
||||
setAuthStatus: (status: any) => void;
|
||||
@@ -24,10 +24,10 @@ export function useCliStatus({
|
||||
|
||||
if (result.success) {
|
||||
const cliStatus = {
|
||||
installed: result.status === "installed",
|
||||
installed: result.status === 'installed',
|
||||
path: result.path || null,
|
||||
version: result.version || null,
|
||||
method: result.method || "none",
|
||||
method: result.method || 'none',
|
||||
};
|
||||
console.log(`[${cliType} Setup] CLI Status:`, cliStatus);
|
||||
setCliStatus(cliStatus);
|
||||
@@ -35,29 +35,24 @@ export function useCliStatus({
|
||||
if (result.auth) {
|
||||
// Validate method is one of the expected values, default to "none"
|
||||
const validMethods = [
|
||||
"oauth_token_env",
|
||||
"oauth_token",
|
||||
"api_key",
|
||||
"api_key_env",
|
||||
"credentials_file",
|
||||
"cli_authenticated",
|
||||
"none",
|
||||
'oauth_token_env',
|
||||
'oauth_token',
|
||||
'api_key',
|
||||
'api_key_env',
|
||||
'credentials_file',
|
||||
'cli_authenticated',
|
||||
'none',
|
||||
] as const;
|
||||
type AuthMethod = (typeof validMethods)[number];
|
||||
const method: AuthMethod = validMethods.includes(
|
||||
result.auth.method as AuthMethod
|
||||
)
|
||||
const method: AuthMethod = validMethods.includes(result.auth.method as AuthMethod)
|
||||
? (result.auth.method as AuthMethod)
|
||||
: "none";
|
||||
: 'none';
|
||||
const authStatus = {
|
||||
authenticated: result.auth.authenticated,
|
||||
method,
|
||||
hasCredentialsFile: false,
|
||||
oauthTokenValid:
|
||||
result.auth.hasStoredOAuthToken ||
|
||||
result.auth.hasEnvOAuthToken,
|
||||
apiKeyValid:
|
||||
result.auth.hasStoredApiKey || result.auth.hasEnvApiKey,
|
||||
oauthTokenValid: result.auth.hasStoredOAuthToken || result.auth.hasEnvOAuthToken,
|
||||
apiKeyValid: result.auth.hasStoredApiKey || result.auth.hasEnvApiKey,
|
||||
hasEnvOAuthToken: result.auth.hasEnvOAuthToken,
|
||||
hasEnvApiKey: result.auth.hasEnvApiKey,
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useState, useCallback } from "react";
|
||||
import { toast } from "sonner";
|
||||
import { getElectronAPI } from "@/lib/electron";
|
||||
import { useState, useCallback } from 'react';
|
||||
import { toast } from 'sonner';
|
||||
import { getElectronAPI } from '@/lib/electron';
|
||||
|
||||
interface UseTokenSaveOptions {
|
||||
provider: string; // e.g., "anthropic_oauth_token", "anthropic", "openai"
|
||||
@@ -13,7 +13,7 @@ export function useTokenSave({ provider, onSuccess }: UseTokenSaveOptions) {
|
||||
const saveToken = useCallback(
|
||||
async (tokenValue: string) => {
|
||||
if (!tokenValue.trim()) {
|
||||
toast.error("Please enter a valid token");
|
||||
toast.error('Please enter a valid token');
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -27,25 +27,23 @@ export function useTokenSave({ provider, onSuccess }: UseTokenSaveOptions) {
|
||||
console.log(`[Token Save] Store result for ${provider}:`, result);
|
||||
|
||||
if (result.success) {
|
||||
const tokenType = provider.includes("oauth")
|
||||
? "subscription token"
|
||||
: "API key";
|
||||
const tokenType = provider.includes('oauth') ? 'subscription token' : 'API key';
|
||||
toast.success(`${tokenType} saved successfully`);
|
||||
onSuccess?.();
|
||||
return true;
|
||||
} else {
|
||||
toast.error("Failed to save token", { description: result.error });
|
||||
toast.error('Failed to save token', { description: result.error });
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
// Web mode fallback - just show success
|
||||
toast.success("Token saved");
|
||||
toast.success('Token saved');
|
||||
onSuccess?.();
|
||||
return true;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`[Token Save] Failed to save ${provider}:`, error);
|
||||
toast.error("Failed to save token");
|
||||
toast.error('Failed to save token');
|
||||
return false;
|
||||
} finally {
|
||||
setIsSaving(false);
|
||||
|
||||
@@ -1,15 +1,8 @@
|
||||
|
||||
import { useState, useEffect, useCallback } from "react";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
CardDescription,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} from "@/components/ui/card";
|
||||
import { useSetupStore } from "@/store/setup-store";
|
||||
import { getElectronAPI } from "@/lib/electron";
|
||||
import { useState, useEffect, useCallback } from 'react';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
import { useSetupStore } from '@/store/setup-store';
|
||||
import { getElectronAPI } from '@/lib/electron';
|
||||
import {
|
||||
CheckCircle2,
|
||||
Loader2,
|
||||
@@ -21,9 +14,9 @@ import {
|
||||
AlertTriangle,
|
||||
Github,
|
||||
XCircle,
|
||||
} from "lucide-react";
|
||||
import { toast } from "sonner";
|
||||
import { StatusBadge } from "../components";
|
||||
} from 'lucide-react';
|
||||
import { toast } from 'sonner';
|
||||
import { StatusBadge } from '../components';
|
||||
|
||||
interface GitHubSetupStepProps {
|
||||
onNext: () => void;
|
||||
@@ -31,11 +24,7 @@ interface GitHubSetupStepProps {
|
||||
onSkip: () => void;
|
||||
}
|
||||
|
||||
export function GitHubSetupStep({
|
||||
onNext,
|
||||
onBack,
|
||||
onSkip,
|
||||
}: GitHubSetupStepProps) {
|
||||
export function GitHubSetupStep({ onNext, onBack, onSkip }: GitHubSetupStepProps) {
|
||||
const { ghCliStatus, setGhCliStatus } = useSetupStore();
|
||||
const [isChecking, setIsChecking] = useState(false);
|
||||
|
||||
@@ -57,7 +46,7 @@ export function GitHubSetupStep({
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Failed to check gh status:", error);
|
||||
console.error('Failed to check gh status:', error);
|
||||
} finally {
|
||||
setIsChecking(false);
|
||||
}
|
||||
@@ -69,7 +58,7 @@ export function GitHubSetupStep({
|
||||
|
||||
const copyCommand = (command: string) => {
|
||||
navigator.clipboard.writeText(command);
|
||||
toast.success("Command copied to clipboard");
|
||||
toast.success('Command copied to clipboard');
|
||||
};
|
||||
|
||||
const isReady = ghCliStatus?.installed && ghCliStatus?.authenticated;
|
||||
@@ -93,12 +82,8 @@ export function GitHubSetupStep({
|
||||
<div className="w-16 h-16 rounded-xl bg-zinc-800 flex items-center justify-center mx-auto mb-4">
|
||||
<Github className="w-8 h-8 text-white" />
|
||||
</div>
|
||||
<h2 className="text-2xl font-bold text-foreground mb-2">
|
||||
GitHub CLI Setup
|
||||
</h2>
|
||||
<p className="text-muted-foreground">
|
||||
Optional - Used for creating pull requests
|
||||
</p>
|
||||
<h2 className="text-2xl font-bold text-foreground mb-2">GitHub CLI Setup</h2>
|
||||
<p className="text-muted-foreground">Optional - Used for creating pull requests</p>
|
||||
</div>
|
||||
|
||||
{/* Info Banner */}
|
||||
@@ -107,13 +92,10 @@ export function GitHubSetupStep({
|
||||
<div className="flex items-start gap-3">
|
||||
<AlertTriangle className="w-5 h-5 text-amber-500 shrink-0 mt-0.5" />
|
||||
<div>
|
||||
<p className="font-medium text-foreground">
|
||||
This step is optional
|
||||
</p>
|
||||
<p className="font-medium text-foreground">This step is optional</p>
|
||||
<p className="text-sm text-muted-foreground mt-1">
|
||||
The GitHub CLI allows you to create pull requests directly from
|
||||
the app. Without it, you can still create PRs manually in your
|
||||
browser.
|
||||
The GitHub CLI allows you to create pull requests directly from the app. Without it,
|
||||
you can still create PRs manually in your browser.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -130,24 +112,17 @@ export function GitHubSetupStep({
|
||||
</CardTitle>
|
||||
<div className="flex items-center gap-2">
|
||||
{getStatusBadge()}
|
||||
<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>
|
||||
</div>
|
||||
<CardDescription>
|
||||
{ghCliStatus?.installed
|
||||
? ghCliStatus.authenticated
|
||||
? `Logged in${ghCliStatus.user ? ` as ${ghCliStatus.user}` : ""}`
|
||||
: "Installed but not logged in"
|
||||
: "Not installed on your system"}
|
||||
? `Logged in${ghCliStatus.user ? ` as ${ghCliStatus.user}` : ''}`
|
||||
: 'Installed but not logged in'
|
||||
: 'Not installed on your system'}
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-4">
|
||||
@@ -156,9 +131,7 @@ export function GitHubSetupStep({
|
||||
<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">
|
||||
GitHub CLI is ready!
|
||||
</p>
|
||||
<p className="font-medium text-foreground">GitHub CLI is ready!</p>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
You can create pull requests directly from the app.
|
||||
{ghCliStatus?.version && (
|
||||
@@ -175,9 +148,7 @@ export function GitHubSetupStep({
|
||||
<div className="flex items-start gap-3 p-4 rounded-lg bg-muted/30 border border-border">
|
||||
<XCircle className="w-5 h-5 text-muted-foreground shrink-0 mt-0.5" />
|
||||
<div className="flex-1">
|
||||
<p className="font-medium text-foreground">
|
||||
GitHub CLI not found
|
||||
</p>
|
||||
<p className="font-medium text-foreground">GitHub CLI not found</p>
|
||||
<p className="text-sm text-muted-foreground mt-1">
|
||||
Install the GitHub CLI to enable PR creation from the app.
|
||||
</p>
|
||||
@@ -185,9 +156,7 @@ export function GitHubSetupStep({
|
||||
</div>
|
||||
|
||||
<div className="space-y-3 p-4 rounded-lg bg-muted/30 border border-border">
|
||||
<p className="font-medium text-foreground text-sm">
|
||||
Installation Commands:
|
||||
</p>
|
||||
<p className="font-medium text-foreground text-sm">Installation Commands:</p>
|
||||
|
||||
<div className="space-y-2">
|
||||
<p className="text-xs text-muted-foreground">macOS (Homebrew)</p>
|
||||
@@ -198,7 +167,7 @@ export function GitHubSetupStep({
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
onClick={() => copyCommand("brew install gh")}
|
||||
onClick={() => copyCommand('brew install gh')}
|
||||
>
|
||||
<Copy className="w-4 h-4" />
|
||||
</Button>
|
||||
@@ -214,7 +183,7 @@ export function GitHubSetupStep({
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
onClick={() => copyCommand("winget install GitHub.cli")}
|
||||
onClick={() => copyCommand('winget install GitHub.cli')}
|
||||
>
|
||||
<Copy className="w-4 h-4" />
|
||||
</Button>
|
||||
@@ -230,7 +199,7 @@ export function GitHubSetupStep({
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
onClick={() => copyCommand("sudo apt install gh")}
|
||||
onClick={() => copyCommand('sudo apt install gh')}
|
||||
>
|
||||
<Copy className="w-4 h-4" />
|
||||
</Button>
|
||||
@@ -256,9 +225,7 @@ export function GitHubSetupStep({
|
||||
<div className="flex items-start gap-3 p-4 rounded-lg bg-amber-500/10 border border-amber-500/20">
|
||||
<AlertTriangle className="w-5 h-5 text-amber-500 shrink-0 mt-0.5" />
|
||||
<div className="flex-1">
|
||||
<p className="font-medium text-foreground">
|
||||
GitHub CLI not logged in
|
||||
</p>
|
||||
<p className="font-medium text-foreground">GitHub CLI not logged in</p>
|
||||
<p className="text-sm text-muted-foreground mt-1">
|
||||
Run the login command to authenticate with GitHub.
|
||||
</p>
|
||||
@@ -266,18 +233,12 @@ export function GitHubSetupStep({
|
||||
</div>
|
||||
|
||||
<div className="space-y-2 p-4 rounded-lg bg-muted/30 border border-border">
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Run this command in your terminal:
|
||||
</p>
|
||||
<p className="text-sm text-muted-foreground">Run this command in your terminal:</p>
|
||||
<div className="flex items-center gap-2">
|
||||
<code className="flex-1 bg-muted px-3 py-2 rounded text-sm font-mono text-foreground">
|
||||
gh auth login
|
||||
</code>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
onClick={() => copyCommand("gh auth login")}
|
||||
>
|
||||
<Button variant="ghost" size="icon" onClick={() => copyCommand('gh auth login')}>
|
||||
<Copy className="w-4 h-4" />
|
||||
</Button>
|
||||
</div>
|
||||
@@ -290,9 +251,7 @@ export function GitHubSetupStep({
|
||||
<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">
|
||||
Checking GitHub CLI status...
|
||||
</p>
|
||||
<p className="font-medium text-foreground">Checking GitHub CLI status...</p>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
@@ -301,28 +260,20 @@ export function GitHubSetupStep({
|
||||
|
||||
{/* 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"
|
||||
>
|
||||
{isReady ? "Skip" : "Skip for now"}
|
||||
<Button variant="ghost" onClick={onSkip} className="text-muted-foreground">
|
||||
{isReady ? 'Skip' : 'Skip for now'}
|
||||
</Button>
|
||||
<Button
|
||||
onClick={onNext}
|
||||
className="bg-brand-500 hover:bg-brand-600 text-white"
|
||||
data-testid="github-next-button"
|
||||
>
|
||||
{isReady ? "Continue" : "Continue without GitHub CLI"}
|
||||
{isReady ? 'Continue' : 'Continue without GitHub CLI'}
|
||||
<ArrowRight className="w-4 h-4 ml-2" />
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user