fix: google login redirect

This commit is contained in:
mehedi-hasan 2024-04-20 13:53:18 +06:00
parent d26c214047
commit 8a6a99a81b
13 changed files with 412 additions and 88 deletions

20
.env
View File

@ -1,20 +0,0 @@
DATABASE_URL=mongodb+srv://freshbitemehedi:freshbitemehedi2810@project1.lispgny.mongodb.net/Skilld
UPLOADTHING_SECRET=sk_live_938397c2e99554ab0bc541ba01114334d2ed523a0dbb9988870bcfc05d6ad9df
UPLOADTHING_APP_ID=h07pj3xjyv
NEXTAUTH_URL=http://localhost:3000
NEXTAUTH_SECRET=2aqrEwXp57xY5dA6WN5akp3ceFDVg64Qu0Ufm/Dew8g=
# Go to github and setup the oauth configuration
# https://next-auth.js.org/providers/github#configuration
# https://github.com/settings/developers
GITHUB_ID=674c632636000fe44d40
GITHUB_SECRET=0ec0b70e902ae00021514e21427108275ef4244b
NEXT_PUBLIC_SUPABASE_URL=https://ckngsmtjumbnwksjutdo.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImNrbmdzbXRqdW1ibndrc2p1dGRvIiwicm9sZSI6ImFub24iLCJpYXQiOjE3MTAzNDIwNjMsImV4cCI6MjAyNTkxODA2M30.ZkykkQeBh8-Ej8_2KMdFPNelEF6jzAgb1Nqd64vwAvs
NEXT_PUBLIC_SITE_URL=http://localhost:3000

View File

@ -1,29 +1,49 @@
import { Metadata } from "next";
import Link from "next/link";
import UserAuthForm from "@/components/forms/user-auth-form";
import { buttonVariants } from "@/components/ui/button";
import { cn } from "@/lib/utils";
import Image from "next/image";
import SignUpForm from "@/components/forms/signup-form";
export const metadata: Metadata = {
title: "Sign In | Skilld",
description: "Sign In to start your journey with Skilld.",
title: "Sign Up | Skilld",
description: "Sign Up to start your journey with Skilld.",
};
export default function AuthenticationPage() {
return (
<div className="relative h-screen flex-col items-center justify-center md:grid lg:max-w-none lg:grid-cols-2 lg:px-0">
<Link
href="/examples/authentication"
href="/signin"
className={cn(
buttonVariants({ variant: "ghost" }),
"absolute right-4 hidden top-4 md:right-8 md:top-8",
"absolute right-4 top-4 md:right-8 md:top-8 hidden",
)}
>
Login
Sign In
</Link>
<div className="relative hidden h-full flex-col bg-secondary p-10 dark:border-r lg:flex">
<div className="absolute inset-0 bg-secondary" />
<div className="relative z-20 flex items-center text-lg font-medium">
<Link
href="/dashboard"
className="relative z-20 flex items-center text-lg font-medium"
>
<Image
src="/Skilld AI Logos/Skilld Logo-colour on black.png"
alt="Skilld Logo"
width={100}
height={30}
className="hidden dark:block h-[25px] w-[80px] md:w-[100px] md:h-[30px] object-contain"
/>
<Image
src="/Skilld AI Logos/Skilld Logo-colour.png"
alt="Skilld Logo"
width={100}
height={30}
className="dark:hidden h-[25px] w-[80px] md:w-[100px] md:h-[30px] object-contain"
/>
</Link>
{/* <div className="relative z-20 flex items-center text-lg font-medium">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
@ -37,7 +57,7 @@ export default function AuthenticationPage() {
<path d="M15 6v12a3 3 0 1 0 3-3H6a3 3 0 1 0 3 3V6a3 3 0 1 0-3 3h12a3 3 0 1 0-3-3" />
</svg>
Skilld
</div>
</div> */}
<div className="relative z-20 mt-auto">
<blockquote className="space-y-2">
<p className="text-lg text-muted-foreground">
@ -57,28 +77,36 @@ export default function AuthenticationPage() {
<h1 className="text-2xl font-semibold tracking-tight">
Create an account
</h1>
<p className="text-sm text-muted-foreground">
Enter your email below to create your account
{/* <p className="text-sm text-muted-foreground">
Enter your email and password below to create your account
</p> */}
</div>
<SignUpForm />
<div>
<p className="text-center text-sm text-muted-foreground">
Already have an account?{" "}
<Link href="/signin" className="underline hover:text-primary">
Sign In
</Link>
</p>
<p className="mt-2 text-center text-sm text-muted-foreground">
By clicking continue, you agree to our{" "}
<Link
href="/terms"
className="underline underline-offset-4 hover:text-primary"
>
Terms of Service
</Link>{" "}
and{" "}
<Link
href="/privacy"
className="underline underline-offset-4 hover:text-primary"
>
Privacy Policy
</Link>
.
</p>
</div>
<UserAuthForm />
<p className="px-8 text-center text-sm text-muted-foreground">
By clicking continue, you agree to our{" "}
<Link
href="/terms"
className="underline underline-offset-4 hover:text-primary"
>
Terms of Service
</Link>{" "}
and{" "}
<Link
href="/privacy"
className="underline underline-offset-4 hover:text-primary"
>
Privacy Policy
</Link>
.
</p>
</div>
</div>
</div>

View File

@ -0,0 +1,114 @@
import { Metadata } from "next";
import Link from "next/link";
import { buttonVariants } from "@/components/ui/button";
import { cn } from "@/lib/utils";
import Image from "next/image";
import SignInForm from "@/components/forms/signin-form";
export const metadata: Metadata = {
title: "Sign In | Skilld",
description: "Sign In to restart your journey with Skilld.",
};
export default function AuthenticationPage() {
return (
<div className="relative h-screen flex-col items-center justify-center md:grid lg:max-w-none lg:grid-cols-2 lg:px-0">
<Link
href="/"
className={cn(
buttonVariants({ variant: "ghost" }),
"absolute right-4 top-4 md:right-8 md:top-8 hidden",
)}
>
Sign Up
</Link>
<div className="relative hidden h-full flex-col bg-secondary p-10 dark:border-r lg:flex">
<div className="absolute inset-0 bg-secondary" />
<Link
href="/dashboard"
className="relative z-20 flex items-center text-lg font-medium"
>
<Image
src="/Skilld AI Logos/Skilld Logo-colour on black.png"
alt="Skilld Logo"
width={100}
height={30}
className="hidden dark:block h-[25px] w-[80px] md:w-[100px] md:h-[30px] object-contain"
/>
<Image
src="/Skilld AI Logos/Skilld Logo-colour.png"
alt="Skilld Logo"
width={100}
height={30}
className="dark:hidden h-[25px] w-[80px] md:w-[100px] md:h-[30px] object-contain"
/>
</Link>
{/* <div className="relative z-20 flex items-center text-lg font-medium">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
className="mr-2 h-6 w-6"
>
<path d="M15 6v12a3 3 0 1 0 3-3H6a3 3 0 1 0 3 3V6a3 3 0 1 0-3 3h12a3 3 0 1 0-3-3" />
</svg>
Skilld
</div> */}
<div className="relative z-20 mt-auto">
<blockquote className="space-y-2">
<p className="text-lg text-muted-foreground">
&ldquo;Empower your coding journey at Skilld. Access tutorials,
challenges, and expert-led courses to master programming languages
and tools. Join a supportive community for discussions and code
reviews. Elevate your skills and stay ahead in the tech world with
us!.&rdquo;
</p>
{/* <footer className="text-sm">Ak</footer> */}
</blockquote>
</div>
</div>
<div className="p-4 lg:p-8 h-full flex items-center">
<div className="mx-auto flex w-full flex-col justify-center space-y-6 sm:w-[350px]">
<div className="flex flex-col space-y-2 text-center">
<h1 className="text-2xl font-semibold tracking-tight">
Sign In To Your Account
</h1>
{/* <p className="text-sm text-muted-foreground">
Enter your email and password below to create your account
</p> */}
</div>
<SignInForm />
<div>
<p className="text-center text-sm text-muted-foreground">
Don&#39;t have an account?{" "}
<Link href="/" className="underline hover:text-primary">
Sign Up
</Link>
</p>
{/* <p className="mt-2 text-center text-sm text-muted-foreground">
By clicking continue, you agree to our{" "}
<Link
href="/terms"
className="underline underline-offset-4 hover:text-primary"
>
Terms of Service
</Link>{" "}
and{" "}
<Link
href="/privacy"
className="underline underline-offset-4 hover:text-primary"
>
Privacy Policy
</Link>
.
</p> */}
</div>
</div>
</div>
</div>
);
}

View File

@ -1,7 +1,9 @@
import Header from "@/components/layout/header";
import Sidebar from "@/components/layout/sidebar";
import StoreProvider from "@/lib/store-provider";
import { createClient } from "@/utils/supabase/server";
import type { Metadata } from "next";
import { redirect } from "next/navigation";
export const metadata: Metadata = {
title: "Dashboard | Skilld",
@ -9,11 +11,22 @@ export const metadata: Metadata = {
"Empower your coding journey at Skilld. Access tutorials, challenges, and expert-led courses to master programming languages and tools. Join a supportive community for discussions and code reviews. Elevate your skills and stay ahead in the tech world with us!",
};
export default function DashboardLayout({
export default async function DashboardLayout({
children,
}: {
children: React.ReactNode;
}) {
const supabase = createClient();
const {
data: { user },
} = await supabase.auth.getUser();
if (!user) {
return redirect("/signin");
}
return (
<>
<StoreProvider lastUpdate={new Date().getTime()}>

View File

@ -1,13 +1,25 @@
import { Metadata } from "next";
import React from "react";
import Header from "@/components/task/header";
import { createClient } from "@/utils/supabase/server";
import { redirect } from "next/navigation";
export const metadata: Metadata = {
title: "Task | Skilld",
description: "Skilld",
};
const Layout = ({ children }: { children: React.ReactNode }) => {
const Layout = async ({ children }: { children: React.ReactNode }) => {
const supabase = createClient();
const {
data: { user },
} = await supabase.auth.getUser();
if (!user) {
return redirect("/signin");
}
return (
<div className="grid grid-rows-[53px_1fr] min-h-screen">
<Header />

View File

@ -0,0 +1,136 @@
"use client";
import { Button } from "@/components/ui/button";
import {
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import { zodResolver } from "@hookform/resolvers/zod";
import { useRouter, useSearchParams } from "next/navigation";
import { useState } from "react";
import { useForm } from "react-hook-form";
import * as z from "zod";
import GoogleSignInButton from "../google-auth-button";
import { createClient } from "@/utils/supabase/client";
import { useToast } from "../ui/use-toast";
const formSchema = z.object({
email: z.string().email({ message: "Enter a valid email address" }),
password: z
.string()
.min(6, { message: "Password should be minimum 6 characters" }),
});
type UserFormValue = z.infer<typeof formSchema>;
export default function SignInForm() {
const router = useRouter();
const searchParams = useSearchParams();
const callbackUrl = searchParams.get("callbackUrl");
const [loading, setLoading] = useState(false);
const { toast } = useToast();
// const defaultValues = {
// email: "demo@gmail.com",
// };
const form = useForm<UserFormValue>({
resolver: zodResolver(formSchema),
// defaultValues,
});
const onSubmit = async ({ email, password }: UserFormValue) => {
console.log(email, password);
const supabase = createClient();
setLoading(true);
const { data, error } = await supabase.auth.signInWithPassword({
email,
password,
// options: {
// emailRedirectTo: `${process.env.NEXT_PUBLIC_SITE_URL}/auth/callback`,
// },
});
console.log(data);
console.log(error);
// signIn("credentials", {
// email: data.email,
// callbackUrl: callbackUrl ?? "/dashboard",
// });
setLoading(false);
if (error) {
toast({ variant: "destructive", title: error.message });
return;
}
router.push("/dashboard");
};
return (
<>
<Form {...form}>
<form
onSubmit={form.handleSubmit(onSubmit)}
className="space-y-2 w-full"
>
<FormField
control={form.control}
name="email"
render={({ field }) => (
<FormItem>
<FormLabel>Email</FormLabel>
<FormControl>
<Input
type="email"
placeholder="Enter your email..."
disabled={loading}
{...field}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="password"
render={({ field }) => (
<FormItem>
<FormLabel>Password</FormLabel>
<FormControl>
<Input
type="password"
placeholder="Enter your password..."
disabled={loading}
{...field}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<Button disabled={loading} className="ml-auto w-full" type="submit">
{loading ? "Signing In..." : "Sign In"}
</Button>
</form>
</Form>
<div className="relative">
<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 className="bg-background px-2 text-muted-foreground">
Or continue with
</span>
</div>
</div>
<GoogleSignInButton />
</>
);
}

View File

@ -16,6 +16,7 @@ import { useForm } from "react-hook-form";
import * as z from "zod";
import GoogleSignInButton from "../google-auth-button";
import { createClient } from "@/utils/supabase/client";
import { useToast } from "../ui/use-toast";
const formSchema = z.object({
email: z.string().email({ message: "Enter a valid email address" }),
@ -26,11 +27,14 @@ const formSchema = z.object({
type UserFormValue = z.infer<typeof formSchema>;
export default function UserAuthForm() {
export default function SignUpForm() {
const router = useRouter();
const searchParams = useSearchParams();
const callbackUrl = searchParams.get("callbackUrl");
const [loading, setLoading] = useState(false);
const { toast } = useToast();
// const defaultValues = {
// email: "demo@gmail.com",
// };
@ -42,6 +46,8 @@ export default function UserAuthForm() {
const onSubmit = async ({ email, password }: UserFormValue) => {
console.log(email, password);
const supabase = createClient();
setLoading(true);
const { data, error } = await supabase.auth.signUp({
email,
password,
@ -56,9 +62,15 @@ export default function UserAuthForm() {
// callbackUrl: callbackUrl ?? "/dashboard",
// });
if (!error) {
router.push("/dashboard");
setLoading(false);
if (error) {
toast({ variant: "destructive", title: error.message });
return;
}
router.push("/dashboard");
};
return (
@ -106,7 +118,7 @@ export default function UserAuthForm() {
/>
<Button disabled={loading} className="ml-auto w-full" type="submit">
Continue With Email
{loading ? "Signing Up..." : "Sign Up"}
</Button>
</form>
</Form>

View File

@ -17,6 +17,8 @@ export default function GoogleSignInButton() {
redirectTo: `${process.env.NEXT_PUBLIC_SITE_URL}/auth/callback`,
},
});
console.log(error);
};
return (

View File

@ -30,6 +30,8 @@ import {
User2Icon,
UserX2Icon,
X,
Upload,
BookCheck,
} from "lucide-react";
export type Icon = LucideIcon;
@ -61,7 +63,9 @@ export const Icons = {
sun: SunMedium,
moon: Moon,
laptop: Laptop,
upload: Upload,
shieldQuestion: ShieldQuestion,
bookCheck: BookCheck,
gitHub: ({ ...props }: LucideProps) => (
<svg
aria-hidden="true"

View File

@ -4,35 +4,51 @@ import { MobileSidebar } from "./mobile-sidebar";
import { UserNav } from "./user-nav";
import Link from "next/link";
import Image from "next/image";
import { createClient } from "@/utils/supabase/server";
import { redirect } from "next/navigation";
export default async function Header() {
const supabase = createClient();
const {
data: { user },
} = await supabase.auth.getUser();
if (!user) {
return redirect("/signin");
}
export default function Header() {
return (
<div className="fixed top-0 left-0 right-0 supports-backdrop-blur:bg-background/60 border-b bg-background/95 backdrop-blur z-20">
<nav className="h-14 flex items-center justify-between px-4">
<div className="hidden lg:block">
<Link href="/dashboard" className="inline-block w-fit">
<Image
src="/Skilld AI Logos/Skilld Logo-colour on black.png"
alt="Skilld Logo"
width={100}
height={30}
className="hidden dark:block h-[25px] w-[80px] md:w-[100px] md:h-[30px] object-contain"
/>
<Image
src="/Skilld AI Logos/Skilld Logo-colour.png"
alt="Skilld Logo"
width={100}
height={30}
className="dark:hidden h-[25px] w-[80px] md:w-[100px] md:h-[30px] object-contain"
/>
</Link>
<Image
src="/Skilld AI Logos/Skilld Logo-colour on black.png"
alt="Skilld Logo"
width={100}
height={30}
className="hidden dark:block h-[25px] w-[80px] md:w-[100px] md:h-[30px] object-contain"
/>
<Image
src="/Skilld AI Logos/Skilld Logo-colour.png"
alt="Skilld Logo"
width={100}
height={30}
className="dark:hidden h-[25px] w-[80px] md:w-[100px] md:h-[30px] object-contain"
/>
</Link>
</div>
<div className={cn("block lg:!hidden")}>
<MobileSidebar />
</div>
<div className="flex items-center gap-2">
<UserNav />
<div className="flex items-center gap-4">
<UserNav
email={user?.email || "example@gmail.com"}
avatarUrl={user?.user_metadata?.avatar_url || ""}
/>
<ThemeToggle />
</div>
</nav>

View File

@ -10,9 +10,9 @@ export default function Sidebar() {
<div className="space-y-4 py-4">
<div className="px-3 py-2">
<div className="space-y-1">
<h2 className="mb-2 px-4 text-xl font-semibold tracking-tight">
{/* <h2 className="mb-2 px-4 text-xl font-semibold tracking-tight">
Overview
</h2>
</h2> */}
<DashboardNav items={navItems} />
</div>
</div>

View File

@ -12,15 +12,22 @@ import {
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { createClient } from "@/utils/supabase/client";
import Link from "next/link";
import { useRouter } from "next/navigation";
export function UserNav() {
export function UserNav({
email,
avatarUrl,
}: {
email: string;
avatarUrl: string;
}) {
const router = useRouter();
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="ghost" className="relative h-8 w-8 rounded-full">
<Avatar className="h-8 w-8">
<AvatarImage src={""} alt={""} />
<AvatarImage src={avatarUrl} alt={"User avatar"} />
<AvatarFallback>CN</AvatarFallback>
</Avatar>
</Button>
@ -30,29 +37,29 @@ export function UserNav() {
<div className="flex flex-col space-y-1">
<p className="text-sm font-medium leading-none">John Doe</p>
<p className="text-xs leading-none text-muted-foreground">
john@doe.com
{email}
</p>
</div>
</DropdownMenuLabel>
<DropdownMenuSeparator />
<DropdownMenuGroup>
<DropdownMenuItem>
Profile
<Link href="/dashboard/profile">Profile</Link>
<DropdownMenuShortcut>P</DropdownMenuShortcut>
</DropdownMenuItem>
<DropdownMenuItem>
{/* <DropdownMenuItem>
Billing
<DropdownMenuShortcut>B</DropdownMenuShortcut>
</DropdownMenuItem>
<DropdownMenuItem>
</DropdownMenuItem> */}
{/* <DropdownMenuItem>
Settings
<DropdownMenuShortcut>S</DropdownMenuShortcut>
</DropdownMenuItem>
<DropdownMenuItem>New Team</DropdownMenuItem>
</DropdownMenuItem> */}
{/* <DropdownMenuItem>New Team</DropdownMenuItem> */}
</DropdownMenuGroup>
<DropdownMenuSeparator />
<DropdownMenuItem
className="cursor-pointer"
className="cursor-pointer"
onClick={async () => {
const supabase = createClient();
const { error } = await supabase.auth.signOut();

View File

@ -23,9 +23,9 @@ export const HEADING_LINK_ANCHOR = `before:content-['#'] before:absolute before:
export const navItems: NavItem[] = [
{
title: "Dashboard",
title: "Upload CV",
href: "/dashboard",
icon: "dashboard",
icon: "upload",
label: "Dashboard",
},
{
@ -37,7 +37,7 @@ export const navItems: NavItem[] = [
{
title: "Your Task",
href: "/dashboard/task/react/1",
icon: "shieldQuestion",
icon: "bookCheck",
label: "Your Task",
},