mirror of
https://github.com/siteboon/claudecodeui.git
synced 2026-07-01 10:02:57 +08:00
style(auth): modernize login, setup, and onboarding screens
This commit is contained in:
@@ -1,3 +1,5 @@
|
||||
import { AlertCircle } from 'lucide-react';
|
||||
|
||||
type AuthErrorAlertProps = {
|
||||
errorMessage: string;
|
||||
};
|
||||
@@ -8,8 +10,9 @@ export default function AuthErrorAlert({ errorMessage }: AuthErrorAlertProps) {
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="rounded-md border border-red-300 bg-red-100 p-3 dark:border-red-800 dark:bg-red-900/20">
|
||||
<p className="text-sm text-red-700 dark:text-red-400">{errorMessage}</p>
|
||||
<div className="flex items-start gap-2.5 rounded-xl border border-destructive/30 bg-destructive/10 p-3 text-destructive">
|
||||
<AlertCircle className="mt-0.5 h-4 w-4 flex-shrink-0" />
|
||||
<p className="text-sm leading-relaxed">{errorMessage}</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
import { useState } from 'react';
|
||||
import type { ComponentType } from 'react';
|
||||
import { Eye, EyeOff } from 'lucide-react';
|
||||
|
||||
type AuthInputFieldProps = {
|
||||
id: string;
|
||||
label: string;
|
||||
@@ -8,13 +12,14 @@ type AuthInputFieldProps = {
|
||||
type?: 'text' | 'password' | 'email';
|
||||
name?: string;
|
||||
autoComplete?: string;
|
||||
icon?: ComponentType<{ className?: string }>;
|
||||
};
|
||||
|
||||
/**
|
||||
* A labelled input field for authentication forms.
|
||||
* Renders a `<label>` / `<input>` pair and forwards browser autofill hints
|
||||
* (`name`, `autoComplete`) so that password managers can identify and fill
|
||||
* the field correctly.
|
||||
* the field correctly. Password fields gain a show/hide visibility toggle.
|
||||
*/
|
||||
export default function AuthInputField({
|
||||
id,
|
||||
@@ -26,24 +31,49 @@ export default function AuthInputField({
|
||||
type = 'text',
|
||||
name,
|
||||
autoComplete,
|
||||
icon: Icon,
|
||||
}: AuthInputFieldProps) {
|
||||
const [isPasswordVisible, setIsPasswordVisible] = useState(false);
|
||||
|
||||
const isPasswordField = type === 'password';
|
||||
const resolvedType = isPasswordField && isPasswordVisible ? 'text' : type;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<label htmlFor={id} className="mb-1 block text-sm font-medium text-foreground">
|
||||
<label htmlFor={id} className="mb-1.5 block text-sm font-medium text-foreground">
|
||||
{label}
|
||||
</label>
|
||||
<input
|
||||
id={id}
|
||||
type={type}
|
||||
name={name ?? id}
|
||||
autoComplete={autoComplete}
|
||||
value={value}
|
||||
onChange={(event) => onChange(event.target.value)}
|
||||
className="w-full rounded-md border border-border bg-background px-3 py-2 text-foreground focus:border-transparent focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||
placeholder={placeholder}
|
||||
required
|
||||
disabled={isDisabled}
|
||||
/>
|
||||
<div className="group relative">
|
||||
{Icon && (
|
||||
<Icon className="pointer-events-none absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground transition-colors group-focus-within:text-primary" />
|
||||
)}
|
||||
<input
|
||||
id={id}
|
||||
type={resolvedType}
|
||||
name={name ?? id}
|
||||
autoComplete={autoComplete}
|
||||
value={value}
|
||||
onChange={(event) => onChange(event.target.value)}
|
||||
className={`w-full rounded-xl border border-border bg-background/60 py-2.5 text-foreground shadow-sm transition-colors placeholder:text-muted-foreground/60 hover:border-foreground/20 focus:border-primary focus:outline-none focus:ring-2 focus:ring-primary/30 disabled:cursor-not-allowed disabled:opacity-60 ${
|
||||
Icon ? 'pl-10' : 'pl-3.5'
|
||||
} ${isPasswordField ? 'pr-11' : 'pr-3.5'}`}
|
||||
placeholder={placeholder}
|
||||
required
|
||||
disabled={isDisabled}
|
||||
/>
|
||||
{isPasswordField && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setIsPasswordVisible((previous) => !previous)}
|
||||
disabled={isDisabled}
|
||||
tabIndex={-1}
|
||||
aria-label={isPasswordVisible ? 'Hide password' : 'Show password'}
|
||||
className="absolute right-2 top-1/2 flex h-7 w-7 -translate-y-1/2 items-center justify-center rounded-lg text-muted-foreground transition-colors hover:bg-muted hover:text-foreground disabled:opacity-60"
|
||||
>
|
||||
{isPasswordVisible ? <EyeOff className="h-4 w-4" /> : <Eye className="h-4 w-4" />}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,30 +1,30 @@
|
||||
import { MessageSquare } from 'lucide-react';
|
||||
|
||||
const loadingDotAnimationDelays = ['0s', '0.1s', '0.2s'];
|
||||
const loadingDotAnimationDelays = ['0s', '0.15s', '0.3s'];
|
||||
|
||||
export default function AuthLoadingScreen() {
|
||||
return (
|
||||
<div className="flex min-h-screen items-center justify-center bg-background p-4">
|
||||
<div className="text-center">
|
||||
<div className="mb-4 flex justify-center">
|
||||
<div className="flex h-16 w-16 items-center justify-center rounded-lg bg-primary shadow-sm">
|
||||
<MessageSquare className="h-8 w-8 text-primary-foreground" />
|
||||
<div className="relative flex min-h-screen items-center justify-center overflow-hidden bg-background p-4">
|
||||
<div aria-hidden className="pointer-events-none absolute inset-0">
|
||||
<div className="absolute -top-40 left-1/2 h-[36rem] w-[36rem] -translate-x-1/2 rounded-full bg-primary/10 blur-3xl" />
|
||||
</div>
|
||||
|
||||
<div className="relative text-center">
|
||||
<div className="mb-5 flex justify-center">
|
||||
<div className="flex h-16 w-16 items-center justify-center rounded-2xl bg-gradient-to-br from-primary to-primary/80 shadow-lg shadow-primary/25 ring-1 ring-inset ring-white/20">
|
||||
<img src="/logo.svg" alt="CloudCLI" className="h-9 w-9" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h1 className="mb-2 text-2xl font-bold text-foreground">CloudCLI</h1>
|
||||
<h1 className="mb-4 font-serif text-2xl font-bold tracking-tight text-foreground">CloudCLI</h1>
|
||||
|
||||
<div className="flex items-center justify-center space-x-2">
|
||||
<div className="flex items-center justify-center gap-2">
|
||||
{loadingDotAnimationDelays.map((delay) => (
|
||||
<div
|
||||
key={delay}
|
||||
className="h-2 w-2 animate-bounce rounded-full bg-blue-500"
|
||||
className="h-2 w-2 animate-bounce rounded-full bg-primary"
|
||||
style={{ animationDelay: delay }}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<p className="mt-2 text-muted-foreground">Loading...</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import type { ReactNode } from 'react';
|
||||
import { MessageSquare } from 'lucide-react';
|
||||
import { IS_PLATFORM } from '../../../constants/config';
|
||||
|
||||
type AuthScreenLayoutProps = {
|
||||
@@ -18,29 +17,38 @@ export default function AuthScreenLayout({
|
||||
logo,
|
||||
}: AuthScreenLayoutProps) {
|
||||
return (
|
||||
<div className="flex min-h-screen items-center justify-center bg-background p-4">
|
||||
<div className="w-full max-w-md">
|
||||
<div className="space-y-6 rounded-lg border border-border bg-card p-8 shadow-lg">
|
||||
<div className="relative h-screen overflow-y-auto bg-background">
|
||||
{/* Ambient, on-brand backdrop that gives the screen depth without
|
||||
competing with the card content. Fixed so it stays put while the
|
||||
form scrolls on short viewports. */}
|
||||
<div aria-hidden className="pointer-events-none fixed inset-0">
|
||||
<div className="absolute -top-40 left-1/2 h-[36rem] w-[36rem] -translate-x-1/2 rounded-full bg-primary/10 blur-3xl" />
|
||||
<div className="absolute -bottom-32 -left-24 h-[26rem] w-[26rem] rounded-full bg-primary/5 blur-3xl" />
|
||||
<div className="absolute inset-0 bg-[radial-gradient(hsl(var(--foreground)/0.04)_1px,transparent_1px)] [background-size:22px_22px] opacity-60" />
|
||||
</div>
|
||||
|
||||
<div className="relative mx-auto flex min-h-full w-full max-w-md items-center justify-center p-4 py-8">
|
||||
<div className="w-full rounded-2xl border border-border/70 bg-card/90 p-8 shadow-[0_24px_60px_-20px_hsl(var(--foreground)/0.18)] ring-1 ring-foreground/5 backdrop-blur-xl sm:p-10">
|
||||
<div className="text-center">
|
||||
<div className="mb-4 flex justify-center">
|
||||
<div className="mb-5 flex justify-center">
|
||||
{logo ?? (
|
||||
<div className="flex h-16 w-16 items-center justify-center rounded-lg bg-primary shadow-sm">
|
||||
<MessageSquare className="h-8 w-8 text-primary-foreground" />
|
||||
<div className="flex h-16 w-16 items-center justify-center rounded-2xl bg-gradient-to-br from-primary to-primary/80 shadow-lg shadow-primary/25 ring-1 ring-inset ring-white/20">
|
||||
<img src="/logo.svg" alt="CloudCLI" className="h-9 w-9" />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<h1 className="text-2xl font-bold text-foreground">{title}</h1>
|
||||
<p className="mt-2 text-muted-foreground">{description}</p>
|
||||
<h1 className="font-serif text-3xl font-bold tracking-tight text-foreground">{title}</h1>
|
||||
<p className="mx-auto mt-2 max-w-xs text-sm leading-relaxed text-muted-foreground">{description}</p>
|
||||
</div>
|
||||
|
||||
{children}
|
||||
<div className="mt-8">{children}</div>
|
||||
|
||||
<div className="text-center">
|
||||
<p className="text-sm text-muted-foreground">{footerText}</p>
|
||||
<div className="mt-6 border-t border-border/60 pt-5 text-center">
|
||||
<p className="text-xs leading-relaxed text-muted-foreground">{footerText}</p>
|
||||
</div>
|
||||
|
||||
{!IS_PLATFORM && (
|
||||
<div className="flex items-center justify-center gap-1.5 pt-2">
|
||||
<div className="mt-4 flex items-center justify-center gap-1.5">
|
||||
<svg className="h-3.5 w-3.5 text-muted-foreground/50" fill="currentColor" viewBox="0 0 24 24" aria-hidden="true">
|
||||
<path d="M12 2C6.477 2 2 6.484 2 12.017c0 4.425 2.865 8.18 6.839 9.504.5.092.682-.217.682-.483 0-.237-.008-.868-.013-1.703-2.782.605-3.369-1.343-3.369-1.343-.454-1.158-1.11-1.466-1.11-1.466-.908-.62.069-.608.069-.608 1.003.07 1.531 1.032 1.531 1.032.892 1.53 2.341 1.088 2.91.832.092-.647.35-1.088.636-1.338-2.22-.253-4.555-1.113-4.555-4.951 0-1.093.39-1.988 1.029-2.688-.103-.253-.446-1.272.098-2.65 0 0 .84-.27 2.75 1.026A9.564 9.564 0 0112 6.844c.85.004 1.705.115 2.504.337 1.909-1.296 2.747-1.027 2.747-1.027.546 1.379.202 2.398.1 2.651.64.7 1.028 1.595 1.028 2.688 0 3.848-2.339 4.695-4.566 4.943.359.309.678.92.678 1.855 0 1.338-.012 2.419-.012 2.747 0 .268.18.58.688.482A10.019 10.019 0 0022 12.017C22 6.484 17.522 2 12 2z" />
|
||||
</svg>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { useCallback, useState } from 'react';
|
||||
import type { FormEvent } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Loader2, Lock, User } from 'lucide-react';
|
||||
import { useAuth } from '../context/AuthContext';
|
||||
import AuthErrorAlert from './AuthErrorAlert';
|
||||
import AuthInputField from './AuthInputField';
|
||||
@@ -69,6 +70,7 @@ export default function LoginForm() {
|
||||
placeholder={t('login.placeholders.username')}
|
||||
isDisabled={isSubmitting}
|
||||
autoComplete="username"
|
||||
icon={User}
|
||||
/>
|
||||
|
||||
<AuthInputField
|
||||
@@ -80,6 +82,7 @@ export default function LoginForm() {
|
||||
isDisabled={isSubmitting}
|
||||
type="password"
|
||||
autoComplete="current-password"
|
||||
icon={Lock}
|
||||
/>
|
||||
|
||||
<AuthErrorAlert errorMessage={errorMessage} />
|
||||
@@ -87,9 +90,16 @@ export default function LoginForm() {
|
||||
<button
|
||||
type="submit"
|
||||
disabled={isSubmitting}
|
||||
className="w-full rounded-md bg-blue-600 px-4 py-2 font-medium text-white transition-colors duration-200 hover:bg-blue-700 disabled:bg-blue-400"
|
||||
className="flex w-full items-center justify-center gap-2 rounded-xl bg-primary px-4 py-2.5 font-medium text-primary-foreground shadow-lg shadow-primary/25 transition-all duration-200 hover:brightness-110 hover:shadow-primary/30 focus:outline-none focus:ring-2 focus:ring-primary/40 focus:ring-offset-2 focus:ring-offset-card active:scale-[0.99] disabled:cursor-not-allowed disabled:opacity-60"
|
||||
>
|
||||
{isSubmitting ? t('login.loading') : t('login.submit')}
|
||||
{isSubmitting ? (
|
||||
<>
|
||||
<Loader2 className="h-4 w-4 animate-spin" />
|
||||
{t('login.loading')}
|
||||
</>
|
||||
) : (
|
||||
t('login.submit')
|
||||
)}
|
||||
</button>
|
||||
</form>
|
||||
</AuthScreenLayout>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { useCallback, useState } from 'react';
|
||||
import type { FormEvent } from 'react';
|
||||
import { Loader2, Lock, ShieldCheck, User } from 'lucide-react';
|
||||
import { useAuth } from '../context/AuthContext';
|
||||
import AuthErrorAlert from './AuthErrorAlert';
|
||||
import AuthInputField from './AuthInputField';
|
||||
@@ -85,7 +86,6 @@ export default function SetupForm() {
|
||||
title="Welcome to CloudCLI"
|
||||
description="Set up your account to get started"
|
||||
footerText="This is a single-user system. Only one account can be created."
|
||||
logo={<img src="/logo.svg" alt="CloudCLI" className="h-16 w-16" />}
|
||||
>
|
||||
<form onSubmit={handleSubmit} className="space-y-4">
|
||||
<AuthInputField
|
||||
@@ -94,9 +94,10 @@ export default function SetupForm() {
|
||||
label="Username"
|
||||
value={formState.username}
|
||||
onChange={(value) => updateField('username', value)}
|
||||
placeholder="Enter your username"
|
||||
placeholder="Choose a username"
|
||||
isDisabled={isSubmitting}
|
||||
autoComplete="username"
|
||||
icon={User}
|
||||
/>
|
||||
|
||||
<AuthInputField
|
||||
@@ -105,10 +106,11 @@ export default function SetupForm() {
|
||||
label="Password"
|
||||
value={formState.password}
|
||||
onChange={(value) => updateField('password', value)}
|
||||
placeholder="Enter your password"
|
||||
placeholder="Create a password"
|
||||
isDisabled={isSubmitting}
|
||||
type="password"
|
||||
autoComplete="new-password"
|
||||
icon={Lock}
|
||||
/>
|
||||
|
||||
<AuthInputField
|
||||
@@ -117,20 +119,33 @@ export default function SetupForm() {
|
||||
label="Confirm Password"
|
||||
value={formState.confirmPassword}
|
||||
onChange={(value) => updateField('confirmPassword', value)}
|
||||
placeholder="Confirm your password"
|
||||
placeholder="Re-enter your password"
|
||||
isDisabled={isSubmitting}
|
||||
type="password"
|
||||
autoComplete="new-password"
|
||||
icon={ShieldCheck}
|
||||
/>
|
||||
|
||||
<p className="flex items-center gap-1.5 text-xs text-muted-foreground">
|
||||
<ShieldCheck className="h-3.5 w-3.5" />
|
||||
At least 3 characters for username, 6 for password.
|
||||
</p>
|
||||
|
||||
<AuthErrorAlert errorMessage={errorMessage} />
|
||||
|
||||
<button
|
||||
type="submit"
|
||||
disabled={isSubmitting}
|
||||
className="w-full rounded-md bg-blue-600 px-4 py-2 font-medium text-white transition-colors duration-200 hover:bg-blue-700 disabled:bg-blue-400"
|
||||
className="flex w-full items-center justify-center gap-2 rounded-xl bg-primary px-4 py-2.5 font-medium text-primary-foreground shadow-lg shadow-primary/25 transition-all duration-200 hover:brightness-110 hover:shadow-primary/30 focus:outline-none focus:ring-2 focus:ring-primary/40 focus:ring-offset-2 focus:ring-offset-card active:scale-[0.99] disabled:cursor-not-allowed disabled:opacity-60"
|
||||
>
|
||||
{isSubmitting ? 'Setting up...' : 'Create Account'}
|
||||
{isSubmitting ? (
|
||||
<>
|
||||
<Loader2 className="h-4 w-4 animate-spin" />
|
||||
Setting up...
|
||||
</>
|
||||
) : (
|
||||
'Create Account'
|
||||
)}
|
||||
</button>
|
||||
</form>
|
||||
</AuthScreenLayout>
|
||||
|
||||
@@ -148,11 +148,18 @@ export default function Onboarding({ onComplete }: OnboardingProps) {
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="flex min-h-screen items-center justify-center bg-background p-4">
|
||||
<div className="w-full max-w-2xl">
|
||||
<div className="relative h-screen overflow-y-auto bg-background">
|
||||
<div aria-hidden className="pointer-events-none fixed inset-0">
|
||||
<div className="absolute -top-40 left-1/2 h-[36rem] w-[36rem] -translate-x-1/2 rounded-full bg-primary/10 blur-3xl" />
|
||||
<div className="absolute -bottom-32 -left-24 h-[26rem] w-[26rem] rounded-full bg-primary/5 blur-3xl" />
|
||||
<div className="absolute inset-0 bg-[radial-gradient(hsl(var(--foreground)/0.04)_1px,transparent_1px)] [background-size:22px_22px] opacity-60" />
|
||||
</div>
|
||||
|
||||
<div className="relative mx-auto flex min-h-full w-full max-w-2xl items-center justify-center p-4">
|
||||
<div className="w-full py-6">
|
||||
<OnboardingStepProgress currentStep={currentStep} />
|
||||
|
||||
<div className="rounded-lg border border-border bg-card p-8 shadow-lg">
|
||||
<div className="rounded-2xl border border-border/70 bg-card/90 p-6 shadow-[0_24px_60px_-20px_hsl(var(--foreground)/0.18)] ring-1 ring-foreground/5 backdrop-blur-xl">
|
||||
{currentStep === 0 ? (
|
||||
<GitConfigurationStep
|
||||
gitName={gitName}
|
||||
@@ -169,12 +176,12 @@ export default function Onboarding({ onComplete }: OnboardingProps) {
|
||||
)}
|
||||
|
||||
{errorMessage && (
|
||||
<div className="mt-6 rounded-lg border border-red-300 bg-red-100 p-4 dark:border-red-800 dark:bg-red-900/20">
|
||||
<p className="text-sm text-red-700 dark:text-red-400">{errorMessage}</p>
|
||||
<div className="mt-5 rounded-xl border border-destructive/30 bg-destructive/10 p-3.5">
|
||||
<p className="text-sm text-destructive">{errorMessage}</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="mt-8 flex items-center justify-between border-t border-border pt-6">
|
||||
<div className="mt-6 flex items-center justify-between border-t border-border pt-5">
|
||||
<button
|
||||
onClick={handlePreviousStep}
|
||||
disabled={currentStep === 0 || isSubmitting}
|
||||
@@ -189,7 +196,7 @@ export default function Onboarding({ onComplete }: OnboardingProps) {
|
||||
<button
|
||||
onClick={handleNextStep}
|
||||
disabled={!isCurrentStepValid || isSubmitting}
|
||||
className="flex items-center gap-2 rounded-lg bg-blue-600 px-6 py-3 font-medium text-white transition-colors duration-200 hover:bg-blue-700 disabled:cursor-not-allowed disabled:bg-blue-400"
|
||||
className="flex items-center gap-2 rounded-xl bg-primary px-6 py-2.5 font-medium text-primary-foreground shadow-lg shadow-primary/25 transition-all duration-200 hover:brightness-110 active:scale-[0.99] disabled:cursor-not-allowed disabled:opacity-60 disabled:shadow-none"
|
||||
>
|
||||
{isSubmitting ? (
|
||||
<>
|
||||
@@ -207,7 +214,7 @@ export default function Onboarding({ onComplete }: OnboardingProps) {
|
||||
<button
|
||||
onClick={handleFinish}
|
||||
disabled={isSubmitting}
|
||||
className="flex items-center gap-2 rounded-lg bg-green-600 px-6 py-3 font-medium text-white transition-colors duration-200 hover:bg-green-700 disabled:cursor-not-allowed disabled:bg-green-400"
|
||||
className="flex items-center gap-2 rounded-xl bg-emerald-600 px-6 py-2.5 font-medium text-white shadow-lg shadow-emerald-600/25 transition-all duration-200 hover:bg-emerald-700 active:scale-[0.99] disabled:cursor-not-allowed disabled:opacity-60 disabled:shadow-none"
|
||||
>
|
||||
{isSubmitting ? (
|
||||
<>
|
||||
@@ -225,6 +232,7 @@ export default function Onboarding({ onComplete }: OnboardingProps) {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -31,26 +31,26 @@ export default function AgentConnectionCard({
|
||||
: status.error || 'Not connected';
|
||||
|
||||
return (
|
||||
<div className={`rounded-lg border p-4 transition-colors ${containerClassName}`}>
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className={`flex h-10 w-10 items-center justify-center rounded-full ${iconContainerClassName}`}>
|
||||
<div className={`rounded-xl border px-3 py-2.5 transition-colors ${containerClassName}`}>
|
||||
<div className="flex items-center justify-between gap-3">
|
||||
<div className="flex min-w-0 items-center gap-3">
|
||||
<div className={`flex h-9 w-9 flex-shrink-0 items-center justify-center rounded-full ${iconContainerClassName}`}>
|
||||
<SessionProviderLogo provider={provider} className="h-5 w-5" />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div className="flex items-center gap-2 font-medium text-foreground">
|
||||
<div className="min-w-0">
|
||||
<div className="flex items-center gap-1.5 text-sm font-medium text-foreground">
|
||||
{title}
|
||||
{status.authenticated && <Check className="h-4 w-4 text-green-500" />}
|
||||
{status.authenticated && <Check className="h-3.5 w-3.5 flex-shrink-0 text-emerald-500" />}
|
||||
</div>
|
||||
<div className="text-xs text-muted-foreground">{statusText}</div>
|
||||
<div className="truncate text-xs text-muted-foreground">{statusText}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{!status.authenticated && !status.loading && (
|
||||
<button
|
||||
onClick={onLogin}
|
||||
className={`${loginButtonClassName} rounded-lg px-4 py-2 text-sm font-medium text-white transition-colors`}
|
||||
className={`${loginButtonClassName} flex-shrink-0 rounded-lg px-4 py-1.5 text-sm font-medium text-white transition-colors`}
|
||||
>
|
||||
Login
|
||||
</button>
|
||||
|
||||
@@ -51,15 +51,15 @@ export default function AgentConnectionsStep({
|
||||
onOpenProviderLogin,
|
||||
}: AgentConnectionsStepProps) {
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<div className="mb-6 text-center">
|
||||
<h2 className="mb-2 text-2xl font-bold text-foreground">Connect Your AI Agents</h2>
|
||||
<p className="text-muted-foreground">
|
||||
<div className="space-y-4">
|
||||
<div className="text-center">
|
||||
<h2 className="font-serif text-xl font-bold tracking-tight text-foreground">Connect Your AI Agents</h2>
|
||||
<p className="mx-auto mt-1 max-w-sm text-sm leading-relaxed text-muted-foreground">
|
||||
Login to one or more AI coding assistants. All are optional.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="space-y-3">
|
||||
<div className="-mr-1 max-h-[38vh] space-y-2 overflow-y-auto pr-1">
|
||||
{providerCards.map((providerCard) => (
|
||||
<AgentConnectionCard
|
||||
key={providerCard.provider}
|
||||
@@ -74,9 +74,7 @@ export default function AgentConnectionsStep({
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="pt-2 text-center text-sm text-muted-foreground">
|
||||
<p>You can configure these later in Settings.</p>
|
||||
</div>
|
||||
<p className="text-center text-xs text-muted-foreground">You can configure these later in Settings.</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -16,13 +16,13 @@ export default function GitConfigurationStep({
|
||||
onGitEmailChange,
|
||||
}: GitConfigurationStepProps) {
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<div className="mb-8 text-center">
|
||||
<div className="mx-auto mb-4 flex h-16 w-16 items-center justify-center rounded-full bg-blue-100 dark:bg-blue-900/30">
|
||||
<GitBranch className="h-8 w-8 text-blue-600 dark:text-blue-400" />
|
||||
<div className="space-y-5">
|
||||
<div className="text-center">
|
||||
<div className="mx-auto mb-3 flex h-14 w-14 items-center justify-center rounded-2xl bg-primary/10 ring-1 ring-inset ring-primary/20">
|
||||
<GitBranch className="h-7 w-7 text-primary" />
|
||||
</div>
|
||||
<h2 className="mb-2 text-2xl font-bold text-foreground">Git Configuration</h2>
|
||||
<p className="text-muted-foreground">
|
||||
<h2 className="font-serif text-xl font-bold tracking-tight text-foreground">Git Configuration</h2>
|
||||
<p className="mx-auto mt-1 max-w-sm text-sm leading-relaxed text-muted-foreground">
|
||||
Configure your git identity to ensure proper attribution for commits.
|
||||
</p>
|
||||
</div>
|
||||
@@ -38,7 +38,7 @@ export default function GitConfigurationStep({
|
||||
id="gitName"
|
||||
value={gitName}
|
||||
onChange={(event) => onGitNameChange(event.target.value)}
|
||||
className="w-full rounded-lg border border-border bg-background px-4 py-3 text-foreground focus:border-transparent focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||
className="w-full rounded-xl border border-border bg-background/60 px-4 py-2.5 text-foreground shadow-sm transition-colors placeholder:text-muted-foreground/60 hover:border-foreground/20 focus:border-primary focus:outline-none focus:ring-2 focus:ring-primary/30"
|
||||
placeholder="John Doe"
|
||||
required
|
||||
disabled={isSubmitting}
|
||||
@@ -56,7 +56,7 @@ export default function GitConfigurationStep({
|
||||
id="gitEmail"
|
||||
value={gitEmail}
|
||||
onChange={(event) => onGitEmailChange(event.target.value)}
|
||||
className="w-full rounded-lg border border-border bg-background px-4 py-3 text-foreground focus:border-transparent focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||
className="w-full rounded-xl border border-border bg-background/60 px-4 py-2.5 text-foreground shadow-sm transition-colors placeholder:text-muted-foreground/60 hover:border-foreground/20 focus:border-primary focus:outline-none focus:ring-2 focus:ring-primary/30"
|
||||
placeholder="john@example.com"
|
||||
required
|
||||
disabled={isSubmitting}
|
||||
|
||||
@@ -11,7 +11,7 @@ const onboardingSteps = [
|
||||
|
||||
export default function OnboardingStepProgress({ currentStep }: OnboardingStepProgressProps) {
|
||||
return (
|
||||
<div className="mb-8">
|
||||
<div className="mb-5">
|
||||
<div className="flex items-center justify-between">
|
||||
{onboardingSteps.map((step, index) => {
|
||||
const isCompleted = index < currentStep;
|
||||
@@ -22,18 +22,18 @@ export default function OnboardingStepProgress({ currentStep }: OnboardingStepPr
|
||||
<div key={step.title} className="contents">
|
||||
<div className="flex flex-1 flex-col items-center">
|
||||
<div
|
||||
className={`flex h-12 w-12 items-center justify-center rounded-full border-2 transition-colors duration-200 ${
|
||||
className={`flex h-10 w-10 items-center justify-center rounded-full border-2 transition-all duration-200 ${
|
||||
isCompleted
|
||||
? 'border-green-500 bg-green-500 text-white'
|
||||
? 'border-emerald-500 bg-emerald-500 text-white shadow-lg shadow-emerald-500/25'
|
||||
: isActive
|
||||
? 'border-blue-600 bg-blue-600 text-white'
|
||||
: 'border-border bg-background text-muted-foreground'
|
||||
? 'border-primary bg-primary text-primary-foreground shadow-lg shadow-primary/25'
|
||||
: 'border-border bg-card text-muted-foreground'
|
||||
}`}
|
||||
>
|
||||
{isCompleted ? <Check className="h-6 w-6" /> : <Icon className="h-6 w-6" />}
|
||||
{isCompleted ? <Check className="h-5 w-5" /> : <Icon className="h-5 w-5" />}
|
||||
</div>
|
||||
|
||||
<div className="mt-2 text-center">
|
||||
<div className="mt-1.5 text-center">
|
||||
<p className={`text-sm font-medium ${isActive ? 'text-foreground' : 'text-muted-foreground'}`}>
|
||||
{step.title}
|
||||
</p>
|
||||
@@ -42,7 +42,7 @@ export default function OnboardingStepProgress({ currentStep }: OnboardingStepPr
|
||||
</div>
|
||||
|
||||
{index < onboardingSteps.length - 1 && (
|
||||
<div className={`mx-2 h-0.5 flex-1 transition-colors duration-200 ${isCompleted ? 'bg-green-500' : 'bg-border'}`} />
|
||||
<div className={`mx-2 h-0.5 flex-1 transition-colors duration-200 ${isCompleted ? 'bg-emerald-500' : 'bg-border'}`} />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user