Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion apps/browser/src/components/SignIn.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
const SignIn = () => {
const handleSignIn = () => {
chrome.tabs.create({
url: `${process.env.PLASMO_PUBLIC_CLERK_SYNC_HOST}/signin`,
url: `${process.env.PLASMO_PUBLIC_CLERK_SYNC_HOST}/sign-in`,
});
};
return (
Expand Down
12 changes: 10 additions & 2 deletions apps/web/src/app/(Landing)/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { SignedIn, SignedOut, SignInButton, UserButton } from "@clerk/nextjs";
import { SignedIn, SignedOut, UserButton } from "@clerk/nextjs";
import Link from "next/link";
import { Button } from "@/components/ui/button";

export default function HomeLayout({
children,
Expand All @@ -9,7 +11,13 @@ export default function HomeLayout({
<>
<header className="flex justify-end items-center p-4 gap-4 h-16">
<SignedOut>
<SignInButton />
<Button
asChild
variant="outline"
className="bg-white hover:bg-gray-50"
>
<Link href="/sign-in">Sign In</Link>
</Button>
</SignedOut>
<SignedIn>
<UserButton />
Expand Down
68 changes: 68 additions & 0 deletions apps/web/src/app/sign-in/components/login-form.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
"use client";

import { useSignIn } from "@clerk/nextjs";
import { Button } from "@/components/ui/button";
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from "@/components/ui/card";
import { Field, FieldGroup } from "@/components/ui/field";
import { cn } from "@/lib/utils";

export function LoginForm({
className,
...props
}: React.ComponentProps<"div">) {
const { signIn, isLoaded } = useSignIn();

const handleGoogleSignIn = async () => {
if (!isLoaded) return;

try {
await signIn.authenticateWithRedirect({
strategy: "oauth_google",
redirectUrlComplete: "/dashboard",
redirectUrl: "/dashboard",
});
} catch (error) {
console.error("Sign in failed:", error);
}
};

return (
<div className={cn("flex flex-col gap-6", className)} {...props}>
<Card>
<CardHeader className="text-center">
<CardTitle className="text-xl">Welcome back</CardTitle>
<CardDescription>Login with your Google account</CardDescription>
</CardHeader>
<CardContent>
<form>
<FieldGroup>
<Field>
<Button
variant="outline"
type="button"
onClick={handleGoogleSignIn}
disabled={!isLoaded}
>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<title>Google Logo</title>
<path
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"
fill="currentColor"
/>
</svg>
Login with Google
</Button>
</Field>
</FieldGroup>
</form>
</CardContent>
</Card>
</div>
);
}
18 changes: 18 additions & 0 deletions apps/web/src/app/sign-in/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { GalleryVerticalEnd } from "lucide-react";
import { LoginForm } from "./components/login-form";

export default function LoginPage() {
return (
<div className="bg-muted flex min-h-svh flex-col items-center justify-center gap-6 p-6 md:p-10">
<div className="flex w-full max-w-sm flex-col gap-6">
<a href="/" className="flex items-center gap-2 self-center font-medium">
<div className="bg-primary text-primary-foreground flex size-6 items-center justify-center rounded-md">
<GalleryVerticalEnd className="size-4" />
</div>
CourseHelper
</a>
<LoginForm />
</div>
</div>
);
}
77 changes: 77 additions & 0 deletions apps/web/src/components/ui/field.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
"use client";

import * as React from "react";
import { cn } from "@/lib/utils";

function Field({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="field"
className={cn("flex flex-col gap-2", className)}
{...props}
/>
);
}

function FieldLabel({
className,
...props
}: React.ComponentProps<"label">) {
return (
<label
data-slot="field-label"
className={cn(
"text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70",
className
)}
{...props}
/>
);
}

function FieldDescription({
className,
...props
}: React.ComponentProps<"p">) {
return (
<p
data-slot="field-description"
className={cn("text-muted-foreground text-sm", className)}
{...props}
/>
);
}

function FieldGroup({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="field-group"
className={cn("flex flex-col gap-4", className)}
{...props}
/>
);
}

function FieldSeparator({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="field-separator"
className={cn("relative", className)}
{...props}
>
<div className="absolute inset-0 flex items-center">
<span className="w-full border-t" />
</div>
<div className="relative flex justify-center text-xs uppercase">
<span
data-slot="field-separator-content"
className="bg-background px-2 text-muted-foreground"
>
{props.children}
</span>
</div>
</div>
);
}

export { Field, FieldLabel, FieldDescription, FieldGroup, FieldSeparator };