Skip to content

Commit 2536eb5

Browse files
committed
feat(web): create authentication pages and onboarding (SSC-9)
- Build split-screen auth layout with branding - Create login page with OAuth options - Add registration with password strength indicator - Implement forgot password flow - Create multi-step onboarding wizard - Include subject selection and goal setting
1 parent 0fb9c12 commit 2536eb5

5 files changed

Lines changed: 1176 additions & 0 deletions

File tree

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
"use client";
2+
3+
import * as React from "react";
4+
import Link from "next/link";
5+
import { ArrowLeft, BookOpen, Loader2, Mail } from "lucide-react";
6+
import { Button } from "@/components/ui/button";
7+
import {
8+
Card,
9+
CardContent,
10+
CardDescription,
11+
CardHeader,
12+
CardTitle,
13+
} from "@/components/ui/card";
14+
import { Input } from "@/components/ui/input";
15+
import { Label } from "@/components/ui/label";
16+
17+
export default function ForgotPasswordPage() {
18+
const [isLoading, setIsLoading] = React.useState(false);
19+
const [isSubmitted, setIsSubmitted] = React.useState(false);
20+
const [email, setEmail] = React.useState("");
21+
const [error, setError] = React.useState("");
22+
23+
const handleSubmit = async (e: React.FormEvent) => {
24+
e.preventDefault();
25+
setError("");
26+
27+
if (!email) {
28+
setError("Email is required");
29+
return;
30+
}
31+
if (!/\S+@\S+\.\S+/.test(email)) {
32+
setError("Please enter a valid email");
33+
return;
34+
}
35+
36+
setIsLoading(true);
37+
38+
// Simulate API call
39+
await new Promise((resolve) => setTimeout(resolve, 1500));
40+
41+
setIsLoading(false);
42+
setIsSubmitted(true);
43+
};
44+
45+
if (isSubmitted) {
46+
return (
47+
<>
48+
{/* Mobile Logo */}
49+
<div className="lg:hidden flex items-center justify-center mb-8">
50+
<Link href="/" className="flex items-center gap-2">
51+
<div className="flex h-10 w-10 items-center justify-center rounded-lg bg-primary">
52+
<BookOpen className="h-5 w-5 text-primary-foreground" />
53+
</div>
54+
<span className="font-bold text-xl">StudySync</span>
55+
</Link>
56+
</div>
57+
58+
<Card className="border-0 shadow-none lg:border lg:shadow-sm">
59+
<CardHeader className="text-center">
60+
<div className="flex justify-center mb-4">
61+
<div className="flex h-16 w-16 items-center justify-center rounded-full bg-primary/10">
62+
<Mail className="h-8 w-8 text-primary" />
63+
</div>
64+
</div>
65+
<CardTitle className="text-2xl">Check your email</CardTitle>
66+
<CardDescription>
67+
We sent a password reset link to{" "}
68+
<span className="font-medium text-foreground">{email}</span>
69+
</CardDescription>
70+
</CardHeader>
71+
<CardContent className="space-y-4">
72+
<p className="text-sm text-muted-foreground text-center">
73+
Didn&apos;t receive the email? Check your spam folder or try again.
74+
</p>
75+
76+
<Button
77+
variant="outline"
78+
className="w-full"
79+
onClick={() => setIsSubmitted(false)}
80+
>
81+
Try another email
82+
</Button>
83+
84+
<Button variant="ghost" className="w-full" asChild>
85+
<Link href="/login">
86+
<ArrowLeft className="mr-2 h-4 w-4" />
87+
Back to login
88+
</Link>
89+
</Button>
90+
</CardContent>
91+
</Card>
92+
</>
93+
);
94+
}
95+
96+
return (
97+
<>
98+
{/* Mobile Logo */}
99+
<div className="lg:hidden flex items-center justify-center mb-8">
100+
<Link href="/" className="flex items-center gap-2">
101+
<div className="flex h-10 w-10 items-center justify-center rounded-lg bg-primary">
102+
<BookOpen className="h-5 w-5 text-primary-foreground" />
103+
</div>
104+
<span className="font-bold text-xl">StudySync</span>
105+
</Link>
106+
</div>
107+
108+
<Card className="border-0 shadow-none lg:border lg:shadow-sm">
109+
<CardHeader className="text-center">
110+
<CardTitle className="text-2xl">Forgot your password?</CardTitle>
111+
<CardDescription>
112+
No worries, we&apos;ll send you reset instructions.
113+
</CardDescription>
114+
</CardHeader>
115+
<CardContent className="space-y-4">
116+
<form onSubmit={handleSubmit} className="space-y-4">
117+
<div className="space-y-2">
118+
<Label htmlFor="email">Email</Label>
119+
<Input
120+
id="email"
121+
type="email"
122+
placeholder="you@example.com"
123+
value={email}
124+
onChange={(e) => setEmail(e.target.value)}
125+
disabled={isLoading}
126+
/>
127+
{error && <p className="text-sm text-destructive">{error}</p>}
128+
</div>
129+
130+
<Button type="submit" className="w-full" disabled={isLoading}>
131+
{isLoading && <Loader2 className="mr-2 h-4 w-4 animate-spin" />}
132+
Send Reset Link
133+
</Button>
134+
</form>
135+
136+
<Button variant="ghost" className="w-full" asChild>
137+
<Link href="/login">
138+
<ArrowLeft className="mr-2 h-4 w-4" />
139+
Back to login
140+
</Link>
141+
</Button>
142+
</CardContent>
143+
</Card>
144+
</>
145+
);
146+
}

apps/web/src/app/(auth)/layout.tsx

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import { BookOpen } from "lucide-react";
2+
import Link from "next/link";
3+
4+
export default function AuthLayout({
5+
children,
6+
}: {
7+
children: React.ReactNode;
8+
}) {
9+
return (
10+
<div className="min-h-screen grid lg:grid-cols-2">
11+
{/* Left Panel - Branding */}
12+
<div className="hidden lg:flex flex-col justify-between bg-primary p-10 text-primary-foreground">
13+
<Link href="/" className="flex items-center gap-2">
14+
<div className="flex h-10 w-10 items-center justify-center rounded-lg bg-primary-foreground/20">
15+
<BookOpen className="h-5 w-5" />
16+
</div>
17+
<span className="font-bold text-xl">StudySync</span>
18+
</Link>
19+
20+
<div className="space-y-6">
21+
<blockquote className="space-y-2">
22+
<p className="text-lg">
23+
&ldquo;StudySync transformed how I study. The AI-generated flashcards
24+
save me hours of preparation time, and I&apos;ve seen my grades improve
25+
significantly.&rdquo;
26+
</p>
27+
<footer className="text-sm opacity-80">
28+
— Sarah M., Medical Student
29+
</footer>
30+
</blockquote>
31+
</div>
32+
33+
<div className="space-y-4">
34+
<div className="flex items-center gap-8">
35+
<div>
36+
<p className="text-3xl font-bold">50K+</p>
37+
<p className="text-sm opacity-80">Active Students</p>
38+
</div>
39+
<div>
40+
<p className="text-3xl font-bold">1M+</p>
41+
<p className="text-sm opacity-80">Flashcards Created</p>
42+
</div>
43+
<div>
44+
<p className="text-3xl font-bold">95%</p>
45+
<p className="text-sm opacity-80">Satisfaction Rate</p>
46+
</div>
47+
</div>
48+
</div>
49+
</div>
50+
51+
{/* Right Panel - Form */}
52+
<div className="flex items-center justify-center p-8">
53+
<div className="w-full max-w-md">
54+
{children}
55+
</div>
56+
</div>
57+
</div>
58+
);
59+
}

0 commit comments

Comments
 (0)