Files
mylder-frontend/src/app/(auth)/signup/page.tsx
christiankrag 53dbb0ed97 Add Mylder brand design system with warm amber accent
- Implement comprehensive design system with OKLCH colors
- Add warm stone neutrals replacing cold zinc grays
- Add amber brand color for CTAs and accents
- Update typography to Plus Jakarta Sans + JetBrains Mono
- Add brand variants to Button and Badge components
- Add utility classes: glass, shadow-brand, text-gradient-brand
- Update all pages to use semantic design tokens
- Add design system documentation

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-13 16:19:06 +01:00

147 lines
4.6 KiB
TypeScript

'use client'
import { useState } from 'react'
import Link from 'next/link'
import { useRouter } from 'next/navigation'
import { createClient } from '@/lib/supabase/client'
import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'
import { Label } from '@/components/ui/label'
import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from '@/components/ui/card'
import { Bot, Loader2 } from 'lucide-react'
export default function SignupPage() {
const router = useRouter()
const [email, setEmail] = useState('')
const [fullName, setFullName] = useState('')
const [password, setPassword] = useState('')
const [confirmPassword, setConfirmPassword] = useState('')
const [isLoading, setIsLoading] = useState(false)
const [error, setError] = useState<string | null>(null)
const handleSignup = async (e: React.FormEvent) => {
e.preventDefault()
setError(null)
if (password !== confirmPassword) {
setError('Passwords do not match')
return
}
if (password.length < 6) {
setError('Password must be at least 6 characters')
return
}
setIsLoading(true)
const supabase = createClient()
const { error } = await supabase.auth.signUp({
email,
password,
options: {
data: {
full_name: fullName,
},
},
})
if (error) {
setError(error.message)
setIsLoading(false)
return
}
router.push('/dashboard')
}
return (
<div className="min-h-screen flex items-center justify-center bg-background px-4">
<Card className="w-full max-w-md">
<CardHeader className="text-center">
<Link href="/" className="inline-flex items-center justify-center gap-2 mb-4">
<Bot className="h-8 w-8 text-brand" />
<span className="text-xl font-bold">Mylder</span>
</Link>
<CardTitle>Create your account</CardTitle>
<CardDescription>Start building with AI in minutes</CardDescription>
</CardHeader>
<form onSubmit={handleSignup}>
<CardContent className="space-y-4">
<div className="space-y-2">
<Label htmlFor="name">Full Name</Label>
<Input
id="name"
type="text"
placeholder="John Doe"
value={fullName}
onChange={(e) => setFullName(e.target.value)}
required
disabled={isLoading}
/>
</div>
<div className="space-y-2">
<Label htmlFor="email">Email</Label>
<Input
id="email"
type="email"
placeholder="you@example.com"
value={email}
onChange={(e) => setEmail(e.target.value)}
required
disabled={isLoading}
/>
</div>
<div className="space-y-2">
<Label htmlFor="password">Password</Label>
<Input
id="password"
type="password"
placeholder="••••••••"
value={password}
onChange={(e) => setPassword(e.target.value)}
required
disabled={isLoading}
/>
</div>
<div className="space-y-2">
<Label htmlFor="confirmPassword">Confirm Password</Label>
<Input
id="confirmPassword"
type="password"
placeholder="••••••••"
value={confirmPassword}
onChange={(e) => setConfirmPassword(e.target.value)}
required
disabled={isLoading}
/>
</div>
{error && (
<p className="text-sm text-destructive">{error}</p>
)}
</CardContent>
<CardFooter className="flex flex-col gap-4">
<Button type="submit" variant="brand" className="w-full" disabled={isLoading}>
{isLoading ? (
<>
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
Creating account...
</>
) : (
'Create Account'
)}
</Button>
<p className="text-sm text-center text-muted-foreground">
Already have an account?{' '}
<Link href="/login" className="text-brand hover:underline">
Log in
</Link>
</p>
</CardFooter>
</form>
</Card>
</div>
)
}