mirror of
https://github.com/Fayorg/calendrier-avant.git
synced 2026-05-27 17:18:38 +02:00
Merge pull request #7 from Fayorg/authentication
Using NextAuth for authentication & Server actions
This commit is contained in:
6
app/api/auth/[...nextauth]/route.ts
Normal file
6
app/api/auth/[...nextauth]/route.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import NextAuth from "next-auth";
|
||||
|
||||
import { authOptions } from "@lib/authenticate";
|
||||
|
||||
const handler = NextAuth(authOptions);
|
||||
export { handler as GET, handler as POST };
|
||||
@@ -1,93 +0,0 @@
|
||||
import {NextRequest, NextResponse} from "next/server";
|
||||
|
||||
import prisma from "@/lib/prisma";
|
||||
interface IBody {
|
||||
key: string
|
||||
grade: number
|
||||
testId: number
|
||||
}
|
||||
|
||||
export async function POST(req: Request){
|
||||
const body: IBody = await req.json();
|
||||
|
||||
const test = await prisma.test.findFirst({
|
||||
select: {
|
||||
id: true,
|
||||
testOn: true,
|
||||
testOf: {
|
||||
select: {
|
||||
id: true,
|
||||
firstName: true,
|
||||
lastName: true,
|
||||
isTeacher: true
|
||||
}
|
||||
},
|
||||
grades: {
|
||||
select: {
|
||||
note: true,
|
||||
grade: true,
|
||||
createdAt: true,
|
||||
},
|
||||
where: {
|
||||
user: {
|
||||
key: body.key
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
where: {
|
||||
id: body.testId
|
||||
}
|
||||
});
|
||||
|
||||
if(!test){
|
||||
return NextResponse.json({error: "Test not found"}, {status: 404});
|
||||
}
|
||||
|
||||
if(test.grades.length > 0){
|
||||
return NextResponse.json({error: "You have already voted"}, {status: 403});
|
||||
}
|
||||
|
||||
const grade = await prisma.grade.create({
|
||||
data: {
|
||||
note: "",
|
||||
grade: body.grade,
|
||||
user: {
|
||||
connect: {
|
||||
key: body.key
|
||||
}
|
||||
},
|
||||
test: {
|
||||
connect: {
|
||||
id: test.id
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return NextResponse.json({
|
||||
id: test.id,
|
||||
testOn: test.testOn,
|
||||
testOf: {
|
||||
firstName: test.testOf.firstName,
|
||||
lastName: test.testOf.lastName,
|
||||
isTeacher: test.testOf.isTeacher
|
||||
},
|
||||
vote: {
|
||||
hasVoted: test.grades?.length > 0,
|
||||
grade: grade.grade,
|
||||
note: grade.note,
|
||||
createdAt: grade.createdAt
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export async function GET(req: NextRequest){
|
||||
const key = req.nextUrl.searchParams.get("key");
|
||||
|
||||
if(!key) return NextResponse.json({error: "No key provided"}, {status: 400});
|
||||
|
||||
const grades = await prisma.grade.findMany({})
|
||||
|
||||
return NextResponse.json(grades, {status: 200});
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
import {NextRequest, NextResponse} from "next/server";
|
||||
import prisma from "@/lib/prisma";
|
||||
|
||||
export async function GET(req: NextRequest){
|
||||
const key = req.nextUrl.searchParams.get("key");
|
||||
|
||||
if(!key) return NextResponse.json({error: "No key provided"}, {status: 400});
|
||||
|
||||
const user = await prisma.users.findUnique({
|
||||
select: {
|
||||
id: true,
|
||||
firstName: true,
|
||||
lastName: true,
|
||||
isTeacher: true,
|
||||
test: {
|
||||
select: {
|
||||
testOn: true
|
||||
}
|
||||
}
|
||||
},
|
||||
where: {
|
||||
key
|
||||
}
|
||||
});
|
||||
|
||||
if(!user) return NextResponse.json({error: "Key not found"}, {status: 404});
|
||||
|
||||
return NextResponse.json({
|
||||
id: user.id,
|
||||
firstName: user.firstName,
|
||||
lastName: user.lastName,
|
||||
isTeacher: user.isTeacher,
|
||||
testOn: user.test?.testOn
|
||||
});
|
||||
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
import {NextRequest, NextResponse} from "next/server";
|
||||
import prisma from "@/lib/prisma";
|
||||
|
||||
export async function GET(req: NextRequest){
|
||||
const date = req.nextUrl.searchParams.get("date");
|
||||
const key = req.nextUrl.searchParams.get("key");
|
||||
|
||||
const usableDate = new Date(date || new Date());
|
||||
|
||||
const test = await prisma.test.findFirst({
|
||||
select: {
|
||||
id: true,
|
||||
testOn: true,
|
||||
testOf: {
|
||||
select: {
|
||||
id: true,
|
||||
firstName: true,
|
||||
lastName: true,
|
||||
isTeacher: true
|
||||
}
|
||||
},
|
||||
grades: {
|
||||
select: {
|
||||
note: true,
|
||||
grade: true,
|
||||
createdAt: true,
|
||||
},
|
||||
where: {
|
||||
user: {
|
||||
key: key || ""
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
where: {
|
||||
testOn: new Date(usableDate.getFullYear() + "-" + (usableDate.getMonth() + 1) + "-" + (usableDate.getDate().toString().length === 1 ? "0" + usableDate.getDate() : usableDate.getDate()))
|
||||
}
|
||||
});
|
||||
|
||||
if(!test){
|
||||
return NextResponse.json({error: "Test not found"}, {status: 404});
|
||||
}
|
||||
|
||||
return NextResponse.json({
|
||||
id: test.id,
|
||||
testOn: test.testOn,
|
||||
testOf: {
|
||||
firstName: test.testOf.firstName,
|
||||
lastName: test.testOf.lastName,
|
||||
isTeacher: test.testOf.isTeacher
|
||||
},
|
||||
vote: {
|
||||
hasVoted: test.grades?.length > 0,
|
||||
grade: test.grades[0]?.grade,
|
||||
note: test.grades[0]?.note,
|
||||
createdAt: test.grades[0]?.createdAt
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -1,7 +1,14 @@
|
||||
import prisma from '@/lib/prisma';
|
||||
import ActiveCard from './ActiveCard';
|
||||
import { getAuthServerSession } from '@/lib/authenticate';
|
||||
import { red } from 'next/dist/lib/picocolors';
|
||||
import { redirect } from 'next/navigation';
|
||||
|
||||
export default async function Dashboard() {
|
||||
const authSession = await getAuthServerSession();
|
||||
console.log(authSession);
|
||||
if (!authSession || !authSession.user.isTeacher) return redirect('/');
|
||||
|
||||
const tests = await prisma.test.findMany({ select: { isActive: true, isPassed: true, id: true, testOf: { select: { id: true, firstName: true, lastName: true, isTeacher: true } } } });
|
||||
|
||||
const activeTests = tests.filter((test) => test.isActive);
|
||||
|
||||
44
app/page.tsx
44
app/page.tsx
@@ -1,42 +1,20 @@
|
||||
'use client';
|
||||
|
||||
import { Poppins } from 'next/font/google'
|
||||
import { Check } from 'lucide-react'
|
||||
import { useState, useEffect } from 'react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
|
||||
import logo from '../images/logo.svg';
|
||||
import Image from "next/image";
|
||||
import {Input} from "@components/ui/input";
|
||||
import {white} from "next/dist/lib/picocolors";
|
||||
import Image from 'next/image';
|
||||
import { LoginForm } from '@/components/forms/LoginForm';
|
||||
import { getAuthServerSession } from '@/lib/authenticate';
|
||||
import { redirect } from 'next/navigation';
|
||||
|
||||
export default function Home() {
|
||||
const [password, setPassword] = useState('');
|
||||
const router = useRouter();
|
||||
export default async function Home() {
|
||||
const session = await getAuthServerSession();
|
||||
|
||||
useEffect(() => {
|
||||
if (localStorage.getItem('@password')) {
|
||||
router.push('/play');
|
||||
}
|
||||
}, [router]);
|
||||
|
||||
function handleSubmit(event: React.FormEvent<HTMLFormElement>) {
|
||||
event.preventDefault();
|
||||
|
||||
localStorage.setItem('@password', password);
|
||||
router.push('/play');
|
||||
if (session != null) {
|
||||
redirect('/play');
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={"w-full h-screen text-[#F0F0F0] bg-black p-12 flex flex-col items-center justify-center gap-y-28"}>
|
||||
<Image src={logo} alt={"Logo"} width={100} height={200} className={"mx-auto w-full md:w-[400px]"}/>
|
||||
<form onSubmit={handleSubmit} className={"flex flex-row gap-4 w-full md:w-[400px]"}>
|
||||
<Input type="password" placeholder="Mot de passe" id="password" name="password" value={password} onChange={(e) => setPassword(e.target.value)}></Input>
|
||||
<input type="submit" value="Submit" id="submit" className={"hidden"} />
|
||||
<label htmlFor="submit" className={"w-[54px] h-[54px] bg-secondary rounded-[20px] contents-none grid place-content-center"}>
|
||||
<Check width={24} height={24} color={"white"}/>
|
||||
</label>
|
||||
</form>
|
||||
<div className={'w-full h-screen text-[#F0F0F0] bg-black p-12 flex flex-col items-center justify-center gap-y-28'}>
|
||||
<Image src={logo} alt={'Logo'} width={100} height={200} className={'mx-auto w-full md:w-[400px]'} />
|
||||
<LoginForm />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
47
app/play/TodayTest.tsx
Normal file
47
app/play/TodayTest.tsx
Normal file
@@ -0,0 +1,47 @@
|
||||
'use client';
|
||||
|
||||
import { getActiveTestWithGrade } from '@/actions/mangeTest';
|
||||
import { TestCard } from '@/components/custom';
|
||||
import { Session } from 'next-auth';
|
||||
import { signOut } from 'next-auth/react';
|
||||
import { useEffect, useState } from 'react';
|
||||
import Image from 'next/image';
|
||||
import LogOut from '@images/logout.svg';
|
||||
import { GradingForm } from '@/components/forms/GradingForm';
|
||||
import YourGrade from '@images/your-grade.svg';
|
||||
|
||||
export function TodayTest({ session }: { session: Session }) {
|
||||
const [activeTest, setActiveTest] = useState<{ data: any | null; error: Error | null; isLoading: boolean }>({ isLoading: true, data: null, error: null });
|
||||
|
||||
useEffect(() => {
|
||||
getActiveTestWithGrade(new Date(), session.user.id)
|
||||
.catch((err) => setActiveTest({ data: null, error: err, isLoading: false }))
|
||||
.then((data) => setActiveTest({ data, error: null, isLoading: false }));
|
||||
}, [session.user.id]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<TestCard data={activeTest.data} error={activeTest.error} isLoading={activeTest.isLoading} />
|
||||
{activeTest.data &&
|
||||
(activeTest.data.grades[0] ? (
|
||||
<div className={'w-full md:w-[400px]'}>
|
||||
<div className={'flex flex-row justify-evenly'}>
|
||||
<Image src={YourGrade} alt={'Your Grade'} width={100} />
|
||||
<span className={'w-[54px] h-[54px] bg-accent rounded-[20px] contents-none grid place-content-center'}>{activeTest.data.grades[0].grade}</span>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<GradingForm session={session} testId={activeTest.data.id} />
|
||||
))}
|
||||
<div>
|
||||
<button
|
||||
onClick={async () => {
|
||||
await signOut();
|
||||
}}
|
||||
>
|
||||
<Image src={LogOut} alt={'Logo'} width={50} height={50} className={'mx-auto w-full md:w-[400px]'} />
|
||||
</button>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
12
app/play/layout.tsx
Normal file
12
app/play/layout.tsx
Normal file
@@ -0,0 +1,12 @@
|
||||
import { getAuthServerSession } from '@/lib/authenticate';
|
||||
import { redirect } from 'next/navigation';
|
||||
|
||||
export default async function Layout({ children }: { children: React.ReactNode }) {
|
||||
const session = await getAuthServerSession();
|
||||
|
||||
if (session == null) {
|
||||
redirect('/');
|
||||
}
|
||||
|
||||
return <>{children}</>;
|
||||
}
|
||||
@@ -1,43 +1,35 @@
|
||||
'use client';
|
||||
import Image from 'next/image';
|
||||
import Logo from '/images/logo.svg';
|
||||
import { getAuthServerSession } from '@/lib/authenticate';
|
||||
import { redirect } from 'next/navigation';
|
||||
import { getActiveTest } from '@/actions/mangeTest';
|
||||
import { TodayTest } from './TodayTest';
|
||||
|
||||
import { GradingForm, TestCard } from '@/components/custom';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { useState, useEffect } from 'react';
|
||||
import useSWR from 'swr';
|
||||
import { fetcher } from '@/lib/fetcher';
|
||||
import Image from "next/image";
|
||||
import Logo from "/images/logo.svg";
|
||||
import LogOut from "@images/logout.svg";
|
||||
export default async function Play() {
|
||||
const session = await getAuthServerSession();
|
||||
|
||||
export default function Play() {
|
||||
const router = useRouter();
|
||||
const [password, setPassword] = useState<string | null>('');
|
||||
if (session == null) {
|
||||
redirect('/');
|
||||
}
|
||||
|
||||
const { data, error, isLoading } = useSWR('/api/test?key=' + password, fetcher);
|
||||
|
||||
useEffect(() => {
|
||||
const pass = localStorage.getItem('@password');
|
||||
setPassword(pass);
|
||||
if (!pass) {
|
||||
router.push('/');
|
||||
}
|
||||
}, [router]);
|
||||
const now = new Date();
|
||||
const todayTest = await getActiveTest(now);
|
||||
|
||||
return (
|
||||
<div className={"w-full h-[100vh] text-[#F0F0F0] bg-black p-12 flex flex-col items-center justify-center gap-y-28"}>
|
||||
<Image src={Logo} alt={"Logo"} width={100} height={200} className={"mx-auto w-full md:w-[400px]"}/>
|
||||
<TestCard data={data} error={error} isLoading={isLoading} />
|
||||
{data && data.status == 200 && password && <GradingForm password={password} data={data} />}
|
||||
<div>
|
||||
<div className={'w-full h-[100vh] text-[#F0F0F0] bg-black p-12 flex flex-col items-center justify-center gap-y-28'}>
|
||||
<Image src={Logo} alt={'Logo'} width={100} height={200} className={'mx-auto w-full md:w-[400px]'} />
|
||||
<TodayTest session={session} />
|
||||
{/* <TestCard data={data} error={error} isLoading={isLoading} /> */}
|
||||
{/* {data && data.status == 200 && password && <GradingForm password={password} data={data} />} */}
|
||||
{/* <div>
|
||||
<button
|
||||
onClick={() => {
|
||||
localStorage.clear();
|
||||
router.push('/');
|
||||
onClick={async () => {
|
||||
await signOut();
|
||||
}}
|
||||
>
|
||||
<Image src={LogOut} alt={"Logo"} width={50} height={50} className={"mx-auto w-full md:w-[400px]"}/>
|
||||
<Image src={LogOut} alt={'Logo'} width={50} height={50} className={'mx-auto w-full md:w-[400px]'} />
|
||||
</button>
|
||||
</div>
|
||||
</div> */}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user