149 lines
5.6 KiB
TypeScript
149 lines
5.6 KiB
TypeScript
|
|
"use client";
|
||
|
|
|
||
|
|
import { useState } from "react";
|
||
|
|
import Link from "next/link";
|
||
|
|
import { Loader2, Mail, Sparkles, User, Lock } from "lucide-react";
|
||
|
|
import { cn } from "@/lib/utils";
|
||
|
|
|
||
|
|
export default function SignUpPage() {
|
||
|
|
const [name, setName] = useState("");
|
||
|
|
const [email, setEmail] = useState("");
|
||
|
|
const [password, setPassword] = useState("");
|
||
|
|
const [loading, setLoading] = useState(false);
|
||
|
|
const [message, setMessage] = useState<string | null>(null);
|
||
|
|
|
||
|
|
async function onSubmit(e: React.FormEvent) {
|
||
|
|
e.preventDefault();
|
||
|
|
setMessage(null);
|
||
|
|
setLoading(true);
|
||
|
|
try {
|
||
|
|
await new Promise((r) => setTimeout(r, 600));
|
||
|
|
setMessage("Thanks — account creation will connect to your API soon. For now, use dev sign-in.");
|
||
|
|
} finally {
|
||
|
|
setLoading(false);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return (
|
||
|
|
<div
|
||
|
|
className={cn(
|
||
|
|
"rounded-2xl border border-border/60 bg-card/80 p-8 shadow-xl shadow-[hsl(var(--teal)/0.06)] backdrop-blur-md",
|
||
|
|
"ring-1 ring-[hsl(var(--teal)/0.15)]",
|
||
|
|
)}
|
||
|
|
>
|
||
|
|
<div className="mb-8 text-center">
|
||
|
|
<div className="mx-auto mb-4 flex h-12 w-12 items-center justify-center rounded-xl bg-gradient-to-br from-[hsl(var(--teal))] to-[hsl(var(--primary))] text-primary-foreground shadow-lg">
|
||
|
|
<Sparkles className="h-6 w-6" aria-hidden />
|
||
|
|
</div>
|
||
|
|
<h1 className="text-2xl font-semibold tracking-tight text-foreground">Join Tasks</h1>
|
||
|
|
<p className="mt-2 text-sm text-muted-foreground">Create your workspace — registration is a preview for now.</p>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<form onSubmit={onSubmit} className="space-y-5">
|
||
|
|
{message ? (
|
||
|
|
<div
|
||
|
|
role="status"
|
||
|
|
className="rounded-lg border border-[hsl(var(--teal)/0.35)] bg-[hsl(var(--teal)/0.08)] px-3 py-2 text-sm text-foreground"
|
||
|
|
>
|
||
|
|
{message}
|
||
|
|
</div>
|
||
|
|
) : null}
|
||
|
|
|
||
|
|
<div className="space-y-2">
|
||
|
|
<label htmlFor="name" className="text-sm font-medium text-foreground">
|
||
|
|
Name
|
||
|
|
</label>
|
||
|
|
<div className="relative">
|
||
|
|
<User className="pointer-events-none absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground" />
|
||
|
|
<input
|
||
|
|
id="name"
|
||
|
|
name="name"
|
||
|
|
type="text"
|
||
|
|
autoComplete="name"
|
||
|
|
required
|
||
|
|
value={name}
|
||
|
|
onChange={(e) => setName(e.target.value)}
|
||
|
|
className={cn(
|
||
|
|
"w-full rounded-lg border border-input bg-background py-2.5 pl-10 pr-3 text-sm outline-none transition",
|
||
|
|
"placeholder:text-muted-foreground focus:border-[hsl(var(--teal))] focus:ring-2 focus:ring-[hsl(var(--teal)/0.25)]",
|
||
|
|
)}
|
||
|
|
placeholder="Alex Doe"
|
||
|
|
/>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div className="space-y-2">
|
||
|
|
<label htmlFor="email" className="text-sm font-medium text-foreground">
|
||
|
|
Email
|
||
|
|
</label>
|
||
|
|
<div className="relative">
|
||
|
|
<Mail className="pointer-events-none absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground" />
|
||
|
|
<input
|
||
|
|
id="email"
|
||
|
|
name="email"
|
||
|
|
type="email"
|
||
|
|
autoComplete="email"
|
||
|
|
required
|
||
|
|
value={email}
|
||
|
|
onChange={(e) => setEmail(e.target.value)}
|
||
|
|
className={cn(
|
||
|
|
"w-full rounded-lg border border-input bg-background py-2.5 pl-10 pr-3 text-sm outline-none transition",
|
||
|
|
"placeholder:text-muted-foreground focus:border-[hsl(var(--teal))] focus:ring-2 focus:ring-[hsl(var(--teal)/0.25)]",
|
||
|
|
)}
|
||
|
|
placeholder="you@example.com"
|
||
|
|
/>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div className="space-y-2">
|
||
|
|
<label htmlFor="password" className="text-sm font-medium text-foreground">
|
||
|
|
Password
|
||
|
|
</label>
|
||
|
|
<div className="relative">
|
||
|
|
<Lock className="pointer-events-none absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground" />
|
||
|
|
<input
|
||
|
|
id="password"
|
||
|
|
name="password"
|
||
|
|
type="password"
|
||
|
|
autoComplete="new-password"
|
||
|
|
required
|
||
|
|
minLength={8}
|
||
|
|
value={password}
|
||
|
|
onChange={(e) => setPassword(e.target.value)}
|
||
|
|
className={cn(
|
||
|
|
"w-full rounded-lg border border-input bg-background py-2.5 pl-10 pr-3 text-sm outline-none transition",
|
||
|
|
"placeholder:text-muted-foreground focus:border-[hsl(var(--teal))] focus:ring-2 focus:ring-[hsl(var(--teal)/0.25)]",
|
||
|
|
)}
|
||
|
|
placeholder="At least 8 characters"
|
||
|
|
/>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<button
|
||
|
|
type="submit"
|
||
|
|
disabled={loading}
|
||
|
|
className={cn(
|
||
|
|
"flex w-full items-center justify-center gap-2 rounded-lg py-2.5 text-sm font-medium text-primary-foreground transition",
|
||
|
|
"bg-gradient-to-r from-[hsl(var(--primary))] to-[hsl(var(--teal))] hover:opacity-[0.96]",
|
||
|
|
"focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[hsl(var(--primary))]",
|
||
|
|
"disabled:pointer-events-none disabled:opacity-60",
|
||
|
|
)}
|
||
|
|
>
|
||
|
|
{loading ? <Loader2 className="h-4 w-4 animate-spin" /> : null}
|
||
|
|
Create account
|
||
|
|
</button>
|
||
|
|
</form>
|
||
|
|
|
||
|
|
<p className="mt-8 text-center text-sm text-muted-foreground">
|
||
|
|
Already have an account?{" "}
|
||
|
|
<Link
|
||
|
|
href="/sign-in"
|
||
|
|
className="font-medium text-[hsl(var(--primary))] underline-offset-4 hover:underline"
|
||
|
|
>
|
||
|
|
Sign in
|
||
|
|
</Link>
|
||
|
|
</p>
|
||
|
|
</div>
|
||
|
|
);
|
||
|
|
}
|