138 lines
4 KiB
TypeScript
138 lines
4 KiB
TypeScript
'use client';
|
|
|
|
import cookies from 'js-cookie';
|
|
import { notFound } from 'next/navigation';
|
|
import { useState } from 'react';
|
|
import { useForm } from 'react-hook-form';
|
|
import { useSWRConfig } from 'swr';
|
|
|
|
import { usePuzzle } from '@/lib/hooks/use-puzzles';
|
|
|
|
import AppLink from './AppLink';
|
|
import Button from './Button';
|
|
import Input from './Input';
|
|
import ToHTML from './ToHTML';
|
|
|
|
type PuzzleData = {
|
|
answer: string;
|
|
// filename: string;
|
|
// code_file: File[];
|
|
};
|
|
|
|
type Granted = {
|
|
tries: number;
|
|
score?: number;
|
|
};
|
|
|
|
export default function Puzzle({ token, id }: { token: string; id: number }) {
|
|
const [granted, setGranted] = useState<Granted | null>(null);
|
|
|
|
const { data: puzzle, isLoading } = usePuzzle({ token, id });
|
|
const { mutate } = useSWRConfig();
|
|
|
|
const { register, handleSubmit } = useForm<PuzzleData>({
|
|
defaultValues: {
|
|
answer: ''
|
|
// filename: '',
|
|
// code_file: []
|
|
}
|
|
});
|
|
|
|
async function onSubmit(data: PuzzleData) {
|
|
const formData = new FormData();
|
|
|
|
// if (data.code_file[0].size > 16 * 1024 * 1024) {
|
|
// alert('Fichier trop volumineux');
|
|
// return;
|
|
// }
|
|
|
|
formData.append('answer', data.answer);
|
|
// formData.append('filename', 'placeholder');
|
|
// formData.append('code_file', new Blob(), 'placeholder');
|
|
|
|
const res = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/puzzleResponse/${puzzle!.id}`, {
|
|
method: 'POST',
|
|
body: formData,
|
|
headers: {
|
|
Authorization: `Bearer ${cookies.get('token')}}`
|
|
}
|
|
});
|
|
|
|
if (res.ok || res.status === 406) {
|
|
const data = (await res.json()) as { tries: number; score?: number };
|
|
if (data.score) mutate(`puzzles/${puzzle?.id}`);
|
|
else setGranted(data);
|
|
}
|
|
}
|
|
|
|
if (!puzzle && isLoading) {
|
|
return <></>;
|
|
}
|
|
|
|
if (!puzzle) {
|
|
notFound();
|
|
}
|
|
|
|
return (
|
|
<div className="flex h-full w-full flex-col justify-between space-y-4">
|
|
<h1 className="text-2xl font-bold sm:text-3xl md:text-4xl">
|
|
{puzzle.name}{' '}
|
|
<span className="text-xl text-highlight-secondary">({puzzle.scoreMax} points)</span>
|
|
</h1>
|
|
<div className="flex h-screen w-full overflow-y-auto">
|
|
<ToHTML className="font-code text-xs sm:text-base" data={puzzle.content} />
|
|
</div>
|
|
{!puzzle.score ? (
|
|
<form
|
|
className="flex w-full flex-col justify-between sm:flex-row"
|
|
onSubmit={handleSubmit(onSubmit)}
|
|
encType="multipart/form-data"
|
|
>
|
|
<div className="flex flex-col items-center justify-center space-x-0 sm:flex-row sm:space-x-6">
|
|
<Input
|
|
className="w-full"
|
|
label="Réponse"
|
|
type="text"
|
|
placeholder="CAPTAIN LOOK !"
|
|
required
|
|
{...register('answer')}
|
|
/>
|
|
{granted && (
|
|
<div className="flex flex-col">
|
|
<p className="text-sm text-muted">Tentatives actuelles : {granted.tries}</p>
|
|
{granted.score && <p className="text-sm text-muted">Score : {granted.score}</p>}
|
|
</div>
|
|
)}
|
|
{/* <Input
|
|
className="h-16 w-full sm:w-1/3"
|
|
label="Code"
|
|
type="file"
|
|
required
|
|
accept=".py,.js,.ts,.java,.rs,.c"
|
|
{...register('code_file')}
|
|
/> */}
|
|
</div>
|
|
<Button kind="brand" className="mt-6" type="submit">
|
|
Envoyer
|
|
</Button>
|
|
</form>
|
|
) : (
|
|
<div className="flex items-center justify-between">
|
|
<div className=" items-center gap-x-2">
|
|
<p>
|
|
Tentatives : <span className="text-brand-accent">{puzzle.tries}</span>
|
|
</p>
|
|
<p>
|
|
Score : <span className="text-brand-accent">{puzzle.score}</span>
|
|
</p>
|
|
</div>
|
|
<AppLink href="/dashboard/puzzles">
|
|
<Button kind="brand" type="button">
|
|
Retour aux puzzles
|
|
</Button>
|
|
</AppLink>
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|