diff --git a/actions/grades.ts b/actions/grades.ts new file mode 100644 index 0000000..dee4386 --- /dev/null +++ b/actions/grades.ts @@ -0,0 +1,34 @@ +"use server"; + +import prisma from "@/lib/prisma"; + +export async function getGrade(testId: number, userId: number) { + const grade = await prisma.grade.findFirst({ + select: { + grade: true, + id: true, + testId: true, + note: true, + createdAt: true, + }, + where: { + testId: testId, + userId: userId + } + }); + + return grade; +} + +export async function addGrade(testId: number, userId: number, grade: number, note: string | null) { + const newGrade = await prisma.grade.create({ + data: { + grade: grade, + note: note, + testId: testId, + userId: userId, + } + }); + + return newGrade; +} \ No newline at end of file diff --git a/actions/mangeTest.ts b/actions/mangeTest.ts index 74822da..02a569c 100644 --- a/actions/mangeTest.ts +++ b/actions/mangeTest.ts @@ -9,4 +9,66 @@ export async function setTestActive(id: number, active: boolean) { return true; +} + +export async function getActiveTest(date: Date) { + return await prisma.test.findFirst({ + select: { + id: true, + isActive: true, + isPassed: true, + testOf: { + select: { + id: true, + firstName: true, + lastName: true, + isTeacher: true, + }, + }, + createdAt: true, + testOn: true, + }, + where: { + isActive: true, + isPassed: false, + testOn: new Date(date.getFullYear() + '-' + (date.getMonth() + 1) + '-' + (date.getDate().toString().length === 1 ? '0' + date.getDate() : date.getDate())), + }, + }); +} + +export async function getActiveTestWithGrade(date: Date, userId: number) { + return await prisma.test.findFirst({ + select: { + id: true, + isActive: true, + isPassed: true, + testOf: { + select: { + id: true, + firstName: true, + lastName: true, + isTeacher: true, + }, + }, + grades: { + select: { + id: true, + grade: true, + note: true, + createdAt: true, + }, + take: 1, + where: { + userId: userId + } + }, + createdAt: true, + testOn: true, + }, + where: { + isActive: true, + isPassed: false, + testOn: new Date(date.getFullYear() + '-' + (date.getMonth() + 1) + '-' + (date.getDate().toString().length === 1 ? '0' + date.getDate() : date.getDate())), + }, + }); } \ No newline at end of file diff --git a/app/page.tsx b/app/page.tsx index 3949590..05ac1f5 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -1,49 +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 { signIn } from 'next-auth/react'; +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]); - - async function handleSubmit(event: React.FormEvent) { - event.preventDefault(); - - // localStorage.setItem('@password', password); - // router.push('/play'); - - console.log('Trying to sign in'); - const result = await signIn('credentials', { - key: password, - callbackUrl: '/play', - }); + if (session != null) { + redirect('/play'); } return (
{'Logo'} -
- setPassword(e.target.value)}> - - -
+
); } diff --git a/app/play/TodayTest.tsx b/app/play/TodayTest.tsx new file mode 100644 index 0000000..af33660 --- /dev/null +++ b/app/play/TodayTest.tsx @@ -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 ( + <> + + {activeTest.data && + (activeTest.data.grades[0] ? ( +
+
+ {'Your + {activeTest.data.grades[0].grade} +
+
+ ) : ( + + ))} +
+ +
+ + ); +} diff --git a/app/play/layout.tsx b/app/play/layout.tsx new file mode 100644 index 0000000..e71b472 --- /dev/null +++ b/app/play/layout.tsx @@ -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}; +} diff --git a/app/play/page.tsx b/app/play/page.tsx index 2c602aa..00cabc9 100644 --- a/app/play/page.tsx +++ b/app/play/page.tsx @@ -1,43 +1,35 @@ -'use client'; - -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'; +import { getAuthServerSession } from '@/lib/authenticate'; +import { redirect } from 'next/navigation'; +import { getActiveTest } from '@/actions/mangeTest'; +import { TodayTest } from './TodayTest'; -export default function Play() { - const router = useRouter(); - const [password, setPassword] = useState(''); +export default async function Play() { + const session = await getAuthServerSession(); - const { data, error, isLoading } = useSWR('/api/test?key=' + password, fetcher); + if (session == null) { + redirect('/'); + } - // useEffect(() => { - // const pass = localStorage.getItem('@password'); - // setPassword(pass); - // if (!pass) { - // router.push('/'); - // } - // }, [router]); + const now = new Date(); + const todayTest = await getActiveTest(now); return (
{'Logo'} - - {data && data.status == 200 && password && } -
+ + {/* */} + {/* {data && data.status == 200 && password && } */} + {/*
-
+
*/}
); } diff --git a/components/custom/gradingForm.tsx b/components/custom/gradingForm.tsx deleted file mode 100644 index d0ef52a..0000000 --- a/components/custom/gradingForm.tsx +++ /dev/null @@ -1,64 +0,0 @@ -'use client'; - -import { useRouter } from 'next/navigation'; -import { useState } from 'react'; -import Image from 'next/image'; -import YourGrade from '@images/your-grade.svg'; -import {Check} from "lucide-react"; - -export function GradingForm({ password, data }: { password: string; data: any }) { - const [grade, setGrade] = useState(0); - const [hasVoted, setHasVoted] = useState(false); - const router = useRouter(); - - async function handleSubmit(event: React.FormEvent) { - event.preventDefault(); - - const response = await fetch('/api/grade', { - method: 'POST', - body: JSON.stringify({ grade, key: password, testId: data.id }), - }); - const json = await response.json(); - if (response.status == 200) { - setHasVoted(true); - } else { - alert(json.message); - } - } - - if (data.vote.hasVoted || hasVoted) { - return ( -
-
- {YourGrade} - {data.vote.hasVoted ? data.vote.grade : grade} -
-
- ); - } - - return ( -
-
-
- setGrade(Number(e.target.value) / 2)} - /> -
-
- {YourGrade} - {grade} - - -
-
-
- ); -} diff --git a/components/custom/index.ts b/components/custom/index.ts index 415a7d9..07b5a02 100644 --- a/components/custom/index.ts +++ b/components/custom/index.ts @@ -1,4 +1,3 @@ export { DayCard as DayCard } from './dayCard'; export { TestCard as TestCard } from './testCard'; -export { GradingForm } from "./gradingForm"; export { Santa } from "./santa"; diff --git a/components/custom/testCard.tsx b/components/custom/testCard.tsx index 754d420..351cb22 100644 --- a/components/custom/testCard.tsx +++ b/components/custom/testCard.tsx @@ -6,7 +6,6 @@ import Image from 'next/image'; import { Ban, Snowflake, CircleOff } from 'lucide-react'; export function TestCard({ data, error, isLoading }: { data: any; error: any; isLoading: boolean }) { - //console.log(data); if (isLoading) return (
@@ -27,7 +26,7 @@ export function TestCard({ data, error, isLoading }: { data: any; error: any; is
); - if (data.status == 404) + if (!data) return (
{'Calendrier diff --git a/components/forms/GradingForm.tsx b/components/forms/GradingForm.tsx new file mode 100644 index 0000000..5d1be4c --- /dev/null +++ b/components/forms/GradingForm.tsx @@ -0,0 +1,55 @@ +'use client'; + +import { useState } from 'react'; +import Image from 'next/image'; +import YourGrade from '@images/your-grade.svg'; +import { Check } from 'lucide-react'; +import { Session } from 'next-auth'; +import { addGrade } from '@/actions/grades'; + +export function GradingForm({ session, testId }: { session: Session; testId: number }) { + const [grade, setGrade] = useState(0); + const [hasVoted, setHasVoted] = useState(false); + + async function handleSubmit(event: React.FormEvent) { + event.preventDefault(); + + const result = await addGrade(testId, session.user.id, grade, null); + if (result.id) { + console.log('ok'); + setHasVoted(true); + setGrade(result.grade); + } else { + console.log('error'); + } + } + + if (hasVoted && grade != 0) { + return ( +
+
+ {YourGrade} + {grade} +
+
+ ); + } + + return ( +
+
+
+ setGrade(Number(e.target.value) / 2)} /> +
+
+ {YourGrade} + {grade} + + +
+
+
+ ); +} diff --git a/components/forms/LoginForm.tsx b/components/forms/LoginForm.tsx new file mode 100644 index 0000000..1c9f826 --- /dev/null +++ b/components/forms/LoginForm.tsx @@ -0,0 +1,30 @@ +'use client'; + +import { signIn } from 'next-auth/react'; +import { useState } from 'react'; +import { Input } from '../ui/input'; +import { Check } from 'lucide-react'; + +export function LoginForm() { + const [password, setPassword] = useState(''); + + async function handleSubmit(event: React.FormEvent) { + event.preventDefault(); + + console.log('Trying to sign in'); + await signIn('credentials', { + key: password, + callbackUrl: '/play', + }); + } + + return ( +
+ setPassword(e.target.value)}> + + +
+ ); +} diff --git a/lib/authenticate.ts b/lib/authenticate.ts index e7c56b1..407e937 100644 --- a/lib/authenticate.ts +++ b/lib/authenticate.ts @@ -1,6 +1,7 @@ import Credentials from "next-auth/providers/credentials"; import prisma from "./prisma"; import { getServerSession, RequestInternal, type NextAuthOptions, User } from "next-auth"; +import type { GetServerSidePropsContext, NextApiRequest, NextApiResponse } from "next" export async function authenticate(key: string) { console.log("Running authenticate function with key: " + key); @@ -72,4 +73,9 @@ export const authOptions: NextAuthOptions = { ], }; +// Use it in server contexts +export function auth(...args: [GetServerSidePropsContext["req"], GetServerSidePropsContext["res"]] | [NextApiRequest, NextApiResponse] | []) { + return getServerSession(...args, authOptions) +} + export const getAuthServerSession = () => getServerSession(authOptions); \ No newline at end of file