diff --git a/compass/app/auth/actions.ts b/compass/app/auth/actions.ts index fffd782..e940050 100644 --- a/compass/app/auth/actions.ts +++ b/compass/app/auth/actions.ts @@ -5,22 +5,34 @@ import { redirect } from "next/navigation"; import { createClient } from "@/utils/supabase/server"; -export async function login(username: string, password: string) { - const supabase = createClient(); +const supabase = createClient(); +export async function login(email: string, password: string) { // type-casting here for convenience // in practice, you should validate your inputs const data = { - email: username, - password: password, + email, + password, }; const { error } = await supabase.auth.signInWithPassword(data); if (error) { - redirect("/auth/error"); + return "Incorrect email/password"; } revalidatePath("/resource", "layout"); redirect("/resource"); } + +export async function signOut() { + const { data, error } = await supabase.auth.getUser(); + if (error || !data?.user) { + redirect("auth/login"); + } + + supabase.auth.signOut(); + + revalidatePath("/resource", "layout"); + redirect("/auth/login"); +} diff --git a/compass/app/auth/login/page.tsx b/compass/app/auth/login/page.tsx index 5360e32..8033b62 100644 --- a/compass/app/auth/login/page.tsx +++ b/compass/app/auth/login/page.tsx @@ -5,18 +5,39 @@ import Button from "@/components/Button"; import Input from "@/components/Input"; import InlineLink from "@/components/InlineLink"; import Image from "next/image"; -import { useState } from "react"; +import { useEffect, useState } from "react"; import PasswordInput from "@/components/auth/PasswordInput"; import ErrorBanner from "@/components/auth/ErrorBanner"; +import { login } from "../actions"; +import { createClient } from "@/utils/supabase/client"; +import { useRouter } from "next/navigation"; export default function Page() { + const router = useRouter(); + + useEffect(() => { + const supabase = createClient(); + + async function checkUser() { + const { data } = await supabase.auth.getUser(); + + if (data.user) { + router.push("/resource"); + } + } + + checkUser(); + }, [router]); + const [email, setEmail] = useState(""); const [password, setPassword] = useState(""); const [emailError, setEmailError] = useState(""); const [passwordError, setPasswordError] = useState(""); + const [loginError, setLoginError] = useState(""); const handleEmailChange = (event: React.ChangeEvent) => { setEmail(event.currentTarget.value); + setEmail; }; const handlePasswordChange = ( @@ -25,23 +46,33 @@ export default function Page() { setPassword(event.currentTarget.value); }; - const handleClick = (event: React.MouseEvent) => { - // Priority: Incorrect combo > Missing email > Missing password + const handleClick = async (event: React.MouseEvent) => { + event.preventDefault(); + const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; + + if (email.trim().length === 0) { + console.log(email); + setEmailError("Please enter your email."); + return; + } + + if (!emailRegex.test(email)) { + setEmailError("Please enter a valid email address."); + return; + } + + setEmailError(""); if (password.trim().length === 0) { - setEmailError("Please enter your password."); - event.preventDefault(); - } - // This shouldn't happen, already provides validation, but just in case. - if (email.trim().length === 0) { - setPasswordError("Please enter your email."); - event.preventDefault(); - } - // Placeholder for incorrect email + password combo. - if (email === "incorrect@gmail.com" && password) { - setPasswordError("Incorrect password."); - event.preventDefault(); + console.log(password); + setPasswordError("Please enter your password."); + return; } + + setPasswordError(""); + + const error = await login(email, password); + setLoginError(error); }; return ( @@ -60,7 +91,7 @@ export default function Page() { type="email" valid={emailError == ""} title="Email" - placeholder="janedoe@gmail.com" + placeholder="Enter Email" onChange={handleEmailChange} required /> @@ -70,6 +101,7 @@ export default function Page() {
@@ -82,6 +114,8 @@ export default function Page() {
+ + {loginError && } ); } diff --git a/compass/app/page.tsx b/compass/app/page.tsx index 994f96a..fc8d76a 100644 --- a/compass/app/page.tsx +++ b/compass/app/page.tsx @@ -7,7 +7,7 @@ import InlineLink from "@/components/InlineLink"; import Paper from "@/components/auth/Paper"; // import { Metadata } from 'next' import Image from "next/image"; -import { ChangeEvent, useState } from "react"; +import { useState } from "react"; // export const metadata: Metadata = { // title: 'Login', diff --git a/compass/app/resource/layout.tsx b/compass/app/resource/layout.tsx index e9b670d..84c0866 100644 --- a/compass/app/resource/layout.tsx +++ b/compass/app/resource/layout.tsx @@ -3,6 +3,10 @@ import Sidebar from "@/components/resource/Sidebar"; import React, { useState } from "react"; import { ChevronDoubleRightIcon } from "@heroicons/react/24/outline"; +import { createClient } from "@/utils/supabase/client"; +import { useEffect } from "react"; +import { User } from "@supabase/supabase-js"; +import { useRouter } from "next/navigation"; export default function RootLayout({ children, @@ -10,6 +14,26 @@ export default function RootLayout({ children: React.ReactNode; }) { const [isSidebarOpen, setIsSidebarOpen] = useState(false); + const [user, setUser] = useState(); + const router = useRouter(); + + useEffect(() => { + const supabase = createClient(); + + async function getUser() { + const { data, error } = await supabase.auth.getUser(); + + if (error || data.user === null) { + router.push("auth/login"); + return; + } + + setUser(data.user); + console.log(data.user); + } + + getUser(); + }, [router]); return (
@@ -27,13 +51,21 @@ export default function RootLayout({ {/* sidebar */}
- +
{/* page ui */}
{children}
diff --git a/compass/components/resource/Sidebar.tsx b/compass/components/resource/Sidebar.tsx index 781da49..882302c 100644 --- a/compass/components/resource/Sidebar.tsx +++ b/compass/components/resource/Sidebar.tsx @@ -11,9 +11,11 @@ import { UserProfile } from "./UserProfile"; interface SidebarProps { setIsSidebarOpen: React.Dispatch>; + name: string; + email: string; } -const Sidebar: React.FC = ({ setIsSidebarOpen }) => { +const Sidebar: React.FC = ({ setIsSidebarOpen, name, email }) => { return (
{/* button to close sidebar */} @@ -29,7 +31,7 @@ const Sidebar: React.FC = ({ setIsSidebarOpen }) => {
{/* user + logout button */}
- +
{/* navigation menu */}
diff --git a/compass/components/resource/UserProfile.tsx b/compass/components/resource/UserProfile.tsx index 6921e82..e0c342c 100644 --- a/compass/components/resource/UserProfile.tsx +++ b/compass/components/resource/UserProfile.tsx @@ -1,13 +1,23 @@ -export const UserProfile = () => { +import { signOut } from "@/app/auth/actions"; + +interface UserProfileProps { + name: string; + email: string; +} + +export const UserProfile = ({ name, email }: UserProfileProps) => { return (
- Compass Center + {name} - cssgunc@gmail.com + {email}
-
diff --git a/compass/next.config.js b/compass/next.config.js index 3173a01..34bcd12 100644 --- a/compass/next.config.js +++ b/compass/next.config.js @@ -3,6 +3,9 @@ const nextConfig = { images: { domains: ["notioly.com"], }, + experimental: { + serverActions: true + } }; module.exports = nextConfig;