feat: remember me and many more imporves

This commit is contained in:
Bereket Engida
2024-08-29 00:10:35 +03:00
parent ed3579d19e
commit f1e363bbff
36 changed files with 401 additions and 178 deletions

Binary file not shown.

View File

@@ -15,15 +15,20 @@ import { authClient } from "@/lib/auth-client";
import { useState } from "react";
import { Key } from "lucide-react";
import { PasswordInput } from "@/components/ui/password-input";
import { Checkbox } from "@/components/ui/checkbox";
import { toast } from "sonner";
import { useRouter } from "next/navigation";
export default function Page() {
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [rememberMe, setRememberMe] = useState(false);
const router = useRouter()
return (
<div className="h-[50rem] w-full dark:bg-black bg-white dark:bg-grid-white/[0.2] bg-grid-black/[0.2] relative flex items-center justify-center">
{/* Radial gradient for the container to give a faded look */}
<div className="absolute pointer-events-none inset-0 flex items-center justify-center dark:bg-black bg-white [mask-image:radial-gradient(ellipse_at_center,transparent_20%,black)]"></div>
<Card className="mx-auto max-w-sm">
<Card className="mx-auto max-w-sm z-50">
<CardHeader>
<CardTitle className="text-2xl">Login</CardTitle>
<CardDescription>
@@ -63,12 +68,22 @@ export default function Page() {
placeholder="Password"
/>
</div>
<div className="flex items-center gap-2">
<Checkbox onClick={() => {
setRememberMe(!rememberMe)
}} />
<Label>Remember me</Label>
</div>
<Button type="submit" className="w-full" onClick={async () => {
await authClient.signIn.credential({
const res = await authClient.signIn.credential({
email,
password,
callbackURL: "/"
callbackURL: "/",
dontRememberMe: !rememberMe
})
if (res.error) {
toast.error(res.error.message)
}
}}>
Login
</Button>
@@ -85,9 +100,14 @@ export default function Page() {
Login with Github
</Button>
<Button variant="secondary" className="gap-2" onClick={async () => {
await authClient.passkey.signIn({
const res = await authClient.passkey.signIn({
callbackURL: "/"
})
if (res?.error) {
toast.error(res.error.message)
} else {
router.push("/")
}
}}>
<Key size={16} />
Login with Passkey

View File

@@ -2,6 +2,7 @@ import type { Metadata } from "next";
import { Inter } from "next/font/google";
import "./globals.css";
import { ThemeWrapper } from "@/components/theme-provider";
import { Toaster } from "@/components/ui/sonner";
const inter = Inter({ subsets: ["latin"] });
@@ -16,10 +17,13 @@ export default function RootLayout({
children: React.ReactNode;
}>) {
return (
<html lang="en">
<ThemeWrapper forcedTheme="dark" attribute="class">
<body className={inter.className}>{children}</body>
</ThemeWrapper>
<html lang="en" suppressHydrationWarning>
<body className={inter.className}>
<ThemeWrapper forcedTheme="dark" attribute="class">
{children}
</ThemeWrapper>
<Toaster />
</body>
</html>
);
}

View File

@@ -11,7 +11,7 @@ export default async function TypewriterEffectSmoothDemo() {
{/* Radial gradient for the container to give a faded look */}
<div className="absolute pointer-events-none inset-0 flex items-center justify-center dark:bg-black bg-white [mask-image:radial-gradient(ellipse_at_center,transparent_20%,black)]"></div>
{
session ? <UserCard user={session.user} /> : null
session ? <UserCard session={session} /> : null
}
</div>

View File

@@ -8,9 +8,7 @@ export const SignOut = () => {
<Button
onClick={async () => {
await authClient.signOut({
body: {
callbackURL: "/"
}
})
}}
>

View File

@@ -12,6 +12,8 @@ const Toaster = ({ ...props }: ToasterProps) => {
<Sonner
theme={theme as ToasterProps["theme"]}
className="toaster group"
richColors
closeButton
toastOptions={{
classNames: {
toast:

View File

@@ -3,21 +3,21 @@
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
import { Card, CardContent, CardFooter, CardHeader, CardTitle } from "@/components/ui/card";
import { Button } from "./ui/button";
import { LogOut } from "lucide-react";
import { Check, LogOut } from "lucide-react";
import { authClient } from "@/lib/auth-client";
import { useRouter } from "next/navigation";
import AddPasskey from "./add-passkey";
import { Session, User } from "@/lib/types";
import { toast } from "sonner";
export default function UserCard({
user,
}: {
user: {
name: string;
email: string;
image?: string;
};
export default function UserCard(props: {
session: {
user: User;
session: Session
} | null
}) {
const router = useRouter();
const session = authClient.useSession(props.session)
return (
<Card>
<CardHeader>
@@ -26,23 +26,42 @@ export default function UserCard({
<CardContent className="grid gap-8">
<div className="flex items-center gap-4">
<Avatar className="hidden h-9 w-9 sm:flex">
<AvatarImage src={user.image || "#"} alt="Avatar" />
<AvatarFallback>{user.name.charAt(0)}</AvatarFallback>
<AvatarImage src={session?.user.image || "#"} alt="Avatar" />
<AvatarFallback>{session?.user.name.charAt(0)}</AvatarFallback>
</Avatar>
<div className="grid gap-1">
<p className="text-sm font-medium leading-none">{user.name}</p>
<p className="text-sm text-muted-foreground">{user.email}</p>
<p className="text-sm font-medium leading-none">{session?.user.name}</p>
<p className="text-sm text-muted-foreground">{session?.user.email}</p>
</div>
</div>
<div className="border-y py-4 flex items-center justify-between">
<div className="border-y py-4 flex items-center justify-between gap-2">
<AddPasskey />
{
session?.user.twoFactorEnabled ? <Button variant="secondary" className="gap-2" onClick={async () => {
const res = await authClient.twoFactor.disable()
if (res.error) {
toast.error(res.error.message)
}
}}>
Disable 2FA
</Button> : <Button variant="outline" className="gap-2" onClick={async () => {
const res = await authClient.twoFactor.enable()
if (res.error) {
toast.error(res.error.message)
}
}}>
<p>
Enable 2FA
</p>
</Button>
}
</div>
</CardContent>
<CardFooter>
<Button className="gap-2 z-10" variant="secondary">
<LogOut size={16} />
<span className="text-sm" onClick={async () => {
const res = await authClient.signOut()
await authClient.signOut()
router.refresh()
}}>
Sign Out

View File

@@ -0,0 +1,5 @@
import { InferSession, InferUser } from "better-auth/types";
import type { auth } from "./auth";
export type User = InferUser<typeof auth>;
export type Session = InferSession<typeof auth>;

View File

@@ -15,7 +15,11 @@ export async function middleware(request: NextRequest) {
permission: {
invitation: ["create"],
},
options: {
headers: request.headers,
},
});
console.log({ canInvite });
return NextResponse.next();
}