Skip to content

Commit 426f1b8

Browse files
feat(web): add login form (#49)
Co-authored-by: Chenxin Yan <yanchenxin2004@gmail.com>
1 parent fff22eb commit 426f1b8

5 files changed

Lines changed: 174 additions & 3 deletions

File tree

apps/browser/src/components/SignIn.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
const SignIn = () => {
22
const handleSignIn = () => {
33
chrome.tabs.create({
4-
url: `${process.env.PLASMO_PUBLIC_CLERK_SYNC_HOST}/signin`,
4+
url: `${process.env.PLASMO_PUBLIC_CLERK_SYNC_HOST}/sign-in`,
55
});
66
};
77
return (

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

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
import { SignedIn, SignedOut, SignInButton, UserButton } from "@clerk/nextjs";
1+
import { SignedIn, SignedOut, UserButton } from "@clerk/nextjs";
2+
import Link from "next/link";
3+
import { Button } from "@/components/ui/button";
24

35
export default function HomeLayout({
46
children,
@@ -9,7 +11,13 @@ export default function HomeLayout({
911
<>
1012
<header className="flex justify-end items-center p-4 gap-4 h-16">
1113
<SignedOut>
12-
<SignInButton />
14+
<Button
15+
asChild
16+
variant="outline"
17+
className="bg-white hover:bg-gray-50"
18+
>
19+
<Link href="/sign-in">Sign In</Link>
20+
</Button>
1321
</SignedOut>
1422
<SignedIn>
1523
<UserButton />
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
"use client";
2+
3+
import { useSignIn } from "@clerk/nextjs";
4+
import { Button } from "@/components/ui/button";
5+
import {
6+
Card,
7+
CardContent,
8+
CardDescription,
9+
CardHeader,
10+
CardTitle,
11+
} from "@/components/ui/card";
12+
import { Field, FieldGroup } from "@/components/ui/field";
13+
import { cn } from "@/lib/utils";
14+
15+
export function LoginForm({
16+
className,
17+
...props
18+
}: React.ComponentProps<"div">) {
19+
const { signIn, isLoaded } = useSignIn();
20+
21+
const handleGoogleSignIn = async () => {
22+
if (!isLoaded) return;
23+
24+
try {
25+
await signIn.authenticateWithRedirect({
26+
strategy: "oauth_google",
27+
redirectUrlComplete: "/dashboard",
28+
redirectUrl: "/dashboard",
29+
});
30+
} catch (error) {
31+
console.error("Sign in failed:", error);
32+
}
33+
};
34+
35+
return (
36+
<div className={cn("flex flex-col gap-6", className)} {...props}>
37+
<Card>
38+
<CardHeader className="text-center">
39+
<CardTitle className="text-xl">Welcome back</CardTitle>
40+
<CardDescription>Login with your Google account</CardDescription>
41+
</CardHeader>
42+
<CardContent>
43+
<form>
44+
<FieldGroup>
45+
<Field>
46+
<Button
47+
variant="outline"
48+
type="button"
49+
onClick={handleGoogleSignIn}
50+
disabled={!isLoaded}
51+
>
52+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
53+
<title>Google Logo</title>
54+
<path
55+
d="M12.48 10.92v3.28h7.84c-.24 1.84-.853 3.187-1.787 4.133-1.147 1.147-2.933 2.4-6.053 2.4-4.827 0-8.6-3.893-8.6-8.72s3.773-8.72 8.6-8.72c2.6 0 4.507 1.027 5.907 2.347l2.307-2.307C18.747 1.44 16.133 0 12.48 0 5.867 0 .307 5.387.307 12s5.56 12 12.173 12c3.573 0 6.267-1.173 8.373-3.36 2.16-2.16 2.84-5.213 2.84-7.667 0-.76-.053-1.467-.173-2.053H12.48z"
56+
fill="currentColor"
57+
/>
58+
</svg>
59+
Login with Google
60+
</Button>
61+
</Field>
62+
</FieldGroup>
63+
</form>
64+
</CardContent>
65+
</Card>
66+
</div>
67+
);
68+
}

apps/web/src/app/sign-in/page.tsx

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { GalleryVerticalEnd } from "lucide-react";
2+
import { LoginForm } from "./components/login-form";
3+
4+
export default function LoginPage() {
5+
return (
6+
<div className="bg-muted flex min-h-svh flex-col items-center justify-center gap-6 p-6 md:p-10">
7+
<div className="flex w-full max-w-sm flex-col gap-6">
8+
<a href="/" className="flex items-center gap-2 self-center font-medium">
9+
<div className="bg-primary text-primary-foreground flex size-6 items-center justify-center rounded-md">
10+
<GalleryVerticalEnd className="size-4" />
11+
</div>
12+
CourseHelper
13+
</a>
14+
<LoginForm />
15+
</div>
16+
</div>
17+
);
18+
}
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
"use client";
2+
3+
import * as React from "react";
4+
import { cn } from "@/lib/utils";
5+
6+
function Field({ className, ...props }: React.ComponentProps<"div">) {
7+
return (
8+
<div
9+
data-slot="field"
10+
className={cn("flex flex-col gap-2", className)}
11+
{...props}
12+
/>
13+
);
14+
}
15+
16+
function FieldLabel({
17+
className,
18+
...props
19+
}: React.ComponentProps<"label">) {
20+
return (
21+
<label
22+
data-slot="field-label"
23+
className={cn(
24+
"text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70",
25+
className
26+
)}
27+
{...props}
28+
/>
29+
);
30+
}
31+
32+
function FieldDescription({
33+
className,
34+
...props
35+
}: React.ComponentProps<"p">) {
36+
return (
37+
<p
38+
data-slot="field-description"
39+
className={cn("text-muted-foreground text-sm", className)}
40+
{...props}
41+
/>
42+
);
43+
}
44+
45+
function FieldGroup({ className, ...props }: React.ComponentProps<"div">) {
46+
return (
47+
<div
48+
data-slot="field-group"
49+
className={cn("flex flex-col gap-4", className)}
50+
{...props}
51+
/>
52+
);
53+
}
54+
55+
function FieldSeparator({ className, ...props }: React.ComponentProps<"div">) {
56+
return (
57+
<div
58+
data-slot="field-separator"
59+
className={cn("relative", className)}
60+
{...props}
61+
>
62+
<div className="absolute inset-0 flex items-center">
63+
<span className="w-full border-t" />
64+
</div>
65+
<div className="relative flex justify-center text-xs uppercase">
66+
<span
67+
data-slot="field-separator-content"
68+
className="bg-background px-2 text-muted-foreground"
69+
>
70+
{props.children}
71+
</span>
72+
</div>
73+
</div>
74+
);
75+
}
76+
77+
export { Field, FieldLabel, FieldDescription, FieldGroup, FieldSeparator };

0 commit comments

Comments
 (0)