generated from chingu-voyages/voyage-template
-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #71 from chingu-voyages/feature/issue-45-admin-aut…
…hentication Feature/issue 45 admin authentication
- Loading branch information
Showing
22 changed files
with
8,530 additions
and
3,478 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
{ | ||
"singleQuote": true, | ||
"trailingComma": "all", | ||
"printWidth": 100, | ||
"tabWidth": 2, | ||
"semi": true | ||
} |
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
import { authOptions } from "@/lib/auth"; | ||
import { getServerSession } from "next-auth"; | ||
import { redirect } from "next/navigation"; | ||
|
||
export const dynamic = "force-dynamic"; | ||
|
||
export default async function AdminsPage() { | ||
const session = await getServerSession(authOptions); | ||
if (!session || !session.user) { | ||
redirect("/admin/signin"); | ||
} | ||
return ( | ||
<div> | ||
<h1>Admins Dashboard</h1> | ||
<h2>Welcome back {session.user.username}</h2> | ||
</div> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
import SignUpForm from "@/app/components/AdminForm/SignUpForm"; | ||
import Footer from "@/app/components/Footer"; | ||
// import getAdmins from "./action"; | ||
|
||
export const dynamic = "force-dynamic"; | ||
|
||
export default async function AdminsRegisterPage() { | ||
// const admins = await getAdmins(); | ||
return ( | ||
<> | ||
<div className=" flex flex-col justify-center items-center mt-6"> | ||
<h1 className=" text-2xl font-semibold">Admin Registration</h1> | ||
<SignUpForm /> | ||
</div> | ||
<Footer /> | ||
</> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
import SignInForm from "@/app/components/AdminForm/SignInForm"; | ||
|
||
export default function AdminsSignInPage() { | ||
return ( | ||
<div className="flex flex-col justify-center items-center mt-6"> | ||
<h1 className="text-2xl font-semibold">Admin Sign In</h1> | ||
<SignInForm /> | ||
</div> | ||
) | ||
} |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
import { authOptions } from "@/lib/auth" | ||
import NextAuth from "next-auth" | ||
|
||
const handler = NextAuth(authOptions) | ||
|
||
export { handler as GET, handler as POST } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
import prisma from "@/prisma/prismaClient"; | ||
import { NextResponse } from "next/server"; | ||
import {hash} from "bcrypt"; | ||
|
||
export async function POST(req: Request){ | ||
try { | ||
const {username,password} = await req.json(); | ||
// verify if the user already exists | ||
const alreadyExists = await prisma.admin.findUnique({ | ||
where: { | ||
username : username | ||
} | ||
}); | ||
if(alreadyExists){ | ||
return NextResponse.json({admin:null,message: "User already exists"},{status:400}); | ||
} | ||
|
||
// hash the password | ||
const hashedPassword = await hash(password,10); | ||
// create a new admin | ||
const admin = await prisma.admin.create({ | ||
data: { | ||
username, | ||
password : hashedPassword | ||
} | ||
}); | ||
|
||
// return the admin without the password | ||
// eslint-disable-next-line @typescript-eslint/no-unused-vars | ||
const {password: adminPassword,...adminWithoutPassword} = admin; | ||
return NextResponse.json({admin: adminWithoutPassword,message: "Admin created successfully"},{status:201}); | ||
|
||
} catch (error) { | ||
return NextResponse.json({admin:null,message: error},{status:500}); | ||
} | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
"use client" | ||
import { useRouter } from "next/navigation" | ||
import { useForm } from "react-hook-form" | ||
import { zodResolver } from "@hookform/resolvers/zod" | ||
import * as z from "zod"; | ||
import { Input } from "@/components/ui/input"; | ||
import { Button } from "@/components/ui/button"; | ||
import { signIn } from "next-auth/react" | ||
import { useState } from "react"; | ||
|
||
//validation schema with Zod | ||
const AdminSchema = z | ||
.object({ | ||
username: z.string().min(1, "Username is required"), | ||
password: z | ||
.string() | ||
.min(1, "Password is required") | ||
.min(6, "Password must be at least 6 characters long"), | ||
}); | ||
|
||
type AdminFormData = z.infer<typeof AdminSchema>; | ||
|
||
export default function SignInForm() { | ||
const router = useRouter() | ||
const [authError, setAuthError] = useState<string | null>(null); | ||
|
||
const { register, handleSubmit, formState : {errors} } = useForm<AdminFormData>({ | ||
resolver: zodResolver(AdminSchema), | ||
defaultValues: { | ||
username: "", | ||
password: "", | ||
} | ||
}) | ||
|
||
const onSubmit = async (data: AdminFormData) => { | ||
setAuthError(null) | ||
const signInData = await signIn("credentials", { | ||
username: data.username, | ||
password: data.password, | ||
redirect: false, | ||
}) | ||
|
||
if(signInData?.error === "CredentialsSignin") { | ||
setAuthError("Invalid username or password") | ||
}else{ | ||
router.push('/admin') | ||
router.refresh() //refresh the page after successful login | ||
} | ||
} | ||
|
||
return ( | ||
<form onSubmit={handleSubmit(onSubmit)} className="regForm"> | ||
<div> | ||
<label htmlFor="username">Username</label> | ||
<Input | ||
{...register("username")} | ||
id="username" | ||
placeholder="Enter your username" | ||
className="mt-1 bg-white" | ||
/> | ||
{errors.username && <p className="text-red-600">{errors.username.message}</p>} | ||
</div> | ||
<div> | ||
<label htmlFor="password">Password</label> | ||
<Input | ||
{...register("password")} | ||
id="password" | ||
type="password" | ||
placeholder="Enter your password" | ||
className="mt-1 bg-white" | ||
/> | ||
{errors.password && <p className="text-red-600">{errors.password.message}</p>} | ||
{authError && <p className="text-red-600">{authError}</p>} | ||
</div> | ||
<Button type="submit" variant="default">Sign In</Button> | ||
</form> | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
'use client'; | ||
import { useRouter } from 'next/navigation'; | ||
import { useForm } from 'react-hook-form'; | ||
import { zodResolver } from '@hookform/resolvers/zod'; | ||
import * as z from 'zod'; | ||
import { Input } from '@/components/ui/input'; | ||
import { Button } from '@/components/ui/button'; | ||
import Link from 'next/link'; | ||
|
||
// Define validation schema with Zod | ||
const AdminRegistrationSchema = z | ||
.object({ | ||
username: z.string().min(1, 'Username is required'), | ||
password: z | ||
.string() | ||
.min(1, 'Password is required') | ||
.min(6, 'Password must be at least 6 characters long'), | ||
confirmPassword: z.string().min(1, 'Confirm Password is required'), | ||
}) | ||
.refine((data) => data.password === data.confirmPassword, { | ||
path: ['confirmPassword'], | ||
message: 'Password do not match', | ||
}); | ||
|
||
type AdminRegistrationFormData = z.infer<typeof AdminRegistrationSchema>; | ||
|
||
export default function SignUpForm() { | ||
const { | ||
register, | ||
handleSubmit, | ||
formState: { errors }, | ||
} = useForm<AdminRegistrationFormData>({ | ||
resolver: zodResolver(AdminRegistrationSchema), | ||
defaultValues: { | ||
username: '', | ||
password: '', | ||
confirmPassword: '', | ||
}, | ||
}); | ||
const router = useRouter(); | ||
|
||
const onSubmit = async (data: AdminRegistrationFormData) => { | ||
const response = await fetch('/api/auth', { | ||
method: 'POST', | ||
headers: { | ||
'Content-Type': 'application/json', | ||
}, | ||
body: JSON.stringify(data), | ||
}); | ||
if (response.ok) { | ||
router.push('/admin/signin'); | ||
} else { | ||
alert('An error occurred while registering'); | ||
} | ||
}; | ||
|
||
return ( | ||
<form onSubmit={handleSubmit(onSubmit)} className="regForm"> | ||
<div> | ||
<label htmlFor="username">Username</label> | ||
<Input | ||
{...register('username')} | ||
id="username" | ||
placeholder="Enter your username" | ||
className="mt-1 bg-white" | ||
/> | ||
{errors.username && <p className="text-red-500">{errors.username.message}</p>} | ||
</div> | ||
<div> | ||
<label htmlFor="password">Password</label> | ||
<Input | ||
{...register('password')} | ||
id="password" | ||
type="password" | ||
placeholder="Enter your password" | ||
className="mt-1 bg-white" | ||
/> | ||
{errors.password && <p className="text-red-600">{errors.password.message}</p>} | ||
</div> | ||
<div> | ||
<label htmlFor="confirmPassword">Confirm Password</label> | ||
<Input | ||
{...register('confirmPassword')} | ||
id="confirmPassword" | ||
type="password" | ||
placeholder="Confirm your password" | ||
className="mt-1 bg-white" | ||
/> | ||
{errors.confirmPassword && <p className="text-red-600">{errors.confirmPassword.message}</p>} | ||
</div> | ||
<Button type="submit" variant="default"> | ||
Register | ||
</Button> | ||
<div> | ||
<h2 className="text-center"> | ||
Do you already have an account?{' '} | ||
<Link href="/admin/signin" className=" font-semibold text-white"> | ||
{' '} | ||
please Login | ||
</Link> | ||
</h2> | ||
</div> | ||
</form> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,24 +1,9 @@ | ||
import { Button } from "@/components/ui/button"; | ||
import Link from "next/link"; | ||
import { getServerSession } from "next-auth"; | ||
import { authOptions } from "@/lib/auth"; | ||
import NavbarToggle from "./NavbarToggle"; | ||
|
||
export default function Navbar() { | ||
return ( | ||
<nav className="flex flex-row justify-between items-center mx-9 py-9"> | ||
<div> | ||
<Link href="/" className="text-4xl font-semibold"> | ||
SunCityLA | ||
</Link> | ||
</div> | ||
<div className="flex flex-row justify-end items-center gap-6 font-semibold"> | ||
<Link href="/">Home</Link> | ||
<Link href="/about">About</Link> | ||
<Link href="/bookings">Schedule an Evaluation</Link> | ||
<Link href="/admins">Admin Login</Link> | ||
<Link href="/Support">Support</Link> | ||
<Button> | ||
<Link href="/bookings/new">New booking</Link> | ||
</Button> | ||
</div> | ||
</nav> | ||
); | ||
export default async function Navbar() { | ||
const session = await getServerSession(authOptions); | ||
|
||
return <NavbarToggle session={session} />; | ||
} |
Oops, something went wrong.