Fix mutate, added 404 & puzzle double sort

This commit is contained in:
Théo 2023-04-23 15:40:57 +02:00
parent f654936e45
commit e962fab69b
6 changed files with 155 additions and 52 deletions

View file

@ -3,10 +3,20 @@ import Image from 'next/image';
import error404 from '@/public/assets/404.png';
export default function NotFound() {
const random = Math.floor(Math.random() * 100);
if (random > 50) {
return (
<div className="m-auto flex h-screen flex-col items-center justify-center space-y-6">
<h2 className="text-6xl">Oh non! Un François 404</h2>
<Image priority src={error404} alt="François 404" width={1000} height={1000} />
<h1 className="text-4xl">Oh non! Un François 404</h1>
<Image priority src={error404} alt="François 404" width={800} height={800} />
</div>
);
} else {
return (
<div className="m-auto flex h-screen flex-col items-center justify-center space-y-6">
<h1 className="text-4xl">Oh non! Ce puzzle est introuvable</h1>
</div>
);
}
}

View file

@ -17,7 +17,15 @@ export async function generateMetadata({ params }: { params: { id: number } }):
const puzzle = await getPuzzle({ token, id });
if (!puzzle) {
notFound();
return {
title: 'Puzzle introuvable',
openGraph: {
title: 'Puzzle introuvable',
type: 'website',
url: getURL(`/dashboard/puzzles/${id}`)
// IMAGES WITH OG IMAGE
}
};
}
return {

View file

@ -5,12 +5,12 @@ export const getChapters = async ({ token }: { token: string }): Promise<Chapter
}
});
const chapters = (await res.json()) as Chapter[];
if (!res.ok) {
throw new Error('Failed to fetch puzzles');
return [];
}
const chapters = (await res.json()) as Chapter[];
if (!chapters) {
return [];
}
@ -31,11 +31,10 @@ export const getChapter = async ({
}
});
const chapter = (await res.json()) as Chapter;
if (!res.ok) {
throw new Error('Failed to fetch puzzles');
return null;
}
const chapter = (await res.json()) as Chapter;
if (!chapter) {
return null;
@ -74,12 +73,12 @@ export const getPuzzle = async ({
}
});
const puzzle = (await res.json()) as Puzzle;
if (!res.ok) {
throw new Error('Failed to fetch puzzle');
return null;
}
const puzzle = (await res.json()) as Puzzle;
if (!puzzle) {
return null;
}

View file

@ -62,8 +62,9 @@ export default function Puzzle({ token, id }: { token: string; id: number }) {
if (res.ok || res.status === 406 || res.status === 423) {
const data = res.ok || res.status === 406 ? ((await res.json()) as Granted) : null;
if (data && data.score) mutate(`puzzles/${puzzle?.id}`);
else if (data && data.tries) setGranted(data);
if (data && data.score) {
mutate([`puzzles/${puzzle?.id}`, 'me']);
} else if (data && data.tries) setGranted(data);
else if (res.ok && data?.success)
setGranted({ tries: null, score: null, message: 'Réponse correcte' });
else if (res.status === 423)

View file

@ -19,11 +19,20 @@ import type { Chapter, Puzzle } from '@/lib/puzzles';
import { cn } from '@/lib/utils';
import useLocalStorage from '@/lib/hooks/use-local-storage';
const difficulty = [
{ value: 'easy', label: 'Facile' },
{ value: 'medium', label: 'Moyen' },
{ value: 'hard', label: 'Difficile' }
];
// TODO: REFACTOR FILTER TO AVOID WARNINGS
export default function Puzzles({ token }: { token: string }) {
const { data: me } = useContext(UserContext);
const { data, isLoading } = usePuzzles({ token });
const [isOpen, setIsOpen] = useState<boolean[]>([]);
const [filter, setFilter] = useState<string>('');
const [filterTags, setFilterTags] = useState<string>('');
const [filterDifficulty, setFilterDifficulty] = useState<string>('');
const [filterChapter, setFilterChapter] = useState<number>();
function handleClick(index: number) {
@ -52,7 +61,7 @@ export default function Puzzles({ token }: { token: string }) {
if (now > startDate) {
const minutes = 10 * 60 * 1000;
if (startDate.getTime() + minutes < now.getTime()) {
if (now.getTime() - startDate.getTime() < minutes) {
return true;
}
}
@ -61,25 +70,39 @@ export default function Puzzles({ token }: { token: string }) {
}
const filteredData = useMemo(() => {
if (filter && filterChapter) {
if (filter === 'completed' || filter === 'no-completed') {
if ((filterTags || filterDifficulty) && filterChapter) {
return data
?.find((chapter) => chapter.id === filterChapter)
?.puzzles.filter((puzzle) => {
if (filter === 'completed') {
return puzzle!.score;
if (!puzzle?.tags) return false;
if (filterDifficulty && filterTags) {
if (filterTags === 'completed') {
return puzzle!.tags!.some((tag) => tag.name === filterDifficulty) && puzzle!.score;
} else if (filterTags === 'not-completed') {
return puzzle!.tags!.some((tag) => tag.name === filterDifficulty) && !puzzle!.score;
}
return (
puzzle!.tags!.some((tag) => tag.name === filterTags) &&
puzzle!.tags!.some((tag) => tag.name === filterDifficulty)
);
}
if (filterDifficulty) {
return puzzle!.tags!.some((tag) => tag.name === filterDifficulty);
}
if (filterTags) {
if (filterTags === 'completed') {
return puzzle!.score;
} else if (filterTags === 'not-completed') {
return !puzzle!.score;
}
return puzzle!.tags!.some((tag) => tag.name === filterTags);
}
return puzzle;
})
.map((puzzle) => puzzle);
}
return data
?.find((chapter) => chapter.id === filterChapter)
?.puzzles.filter((puzzle) => puzzle!.tags!.some((tag) => tag.name === filter))
.map((puzzle) => puzzle);
}
return data?.find((chapter) => chapter.id === filterChapter)?.puzzles;
}, [data, filter, filterChapter]);
}, [data, filterTags, filterDifficulty, filterChapter]);
return (
<>
@ -109,7 +132,7 @@ export default function Puzzles({ token }: { token: string }) {
</div>
<div className="flex flex-col">
{chapter.startDate && chapter.endDate ? (
<div className="flex items-center gap-x-2">
<div className="flex items-center justify-start gap-x-2 md:justify-end">
<Icon name="calendar-line" className="text-sm text-muted" />
<span className="text-sm text-muted">
{new Date(chapter.startDate).toLocaleDateString('fr-FR', {
@ -134,15 +157,24 @@ export default function Puzzles({ token }: { token: string }) {
<div className="h-1 w-1/2 rounded-lg bg-gradient-to-tl from-brand to-brand-accent" />
</div>
)}
<div className="mt-1 flex justify-end">
<div className="mt-1 flex justify-start gap-x-2 md:justify-end">
{isInEventGroup(chapter) && (
<FilterChapter
<>
<FilterDifficulty
chapters={data}
chapter={chapter}
filter={filter}
setFilter={setFilter}
filter={filterDifficulty}
setFilter={setFilterDifficulty}
setFilterChapter={setFilterChapter}
/>
<FilterTags
chapters={data}
chapter={chapter}
filter={filterTags}
setFilter={setFilterTags}
setFilterChapter={setFilterChapter}
/>
</>
)}
</div>
</div>
@ -202,11 +234,8 @@ function PuzzleProp({ puzzle, chapter }: { puzzle: Puzzle; chapter: Chapter }) {
const now = new Date();
if (now > startDate) {
const minutes = 10 * 60 * 1000;
if (startDate.getTime() + minutes < now.getTime()) {
return true;
}
}
return false;
}
@ -257,7 +286,10 @@ function PuzzleProp({ puzzle, chapter }: { puzzle: Puzzle; chapter: Chapter }) {
<div className="flex h-full w-full items-center justify-between p-4 opacity-50">
<div className="flex gap-x-2">
<span className="text-base font-semibold">
{puzzle.name} ({puzzle.scoreMax})
{puzzle.name}{' '}
<span className="text-sm text-highlight-secondary">
({puzzle.score ? `${puzzle.score}` : '?'}/{puzzle.scoreMax} points)
</span>
</span>
{puzzle.tags?.length && (
<div className="flex gap-x-2 text-sm text-muted">
@ -280,7 +312,7 @@ function PuzzleProp({ puzzle, chapter }: { puzzle: Puzzle; chapter: Chapter }) {
);
}
function FilterChapter({
function FilterDifficulty({
chapters,
chapter,
filter,
@ -294,7 +326,7 @@ function FilterChapter({
setFilterChapter: (chapter: number) => void;
}) {
const [stored, setStored] = useLocalStorage({
key: 'puzzles-filter',
key: 'filter-difficulty',
initialValue: ''
});
@ -304,12 +336,65 @@ function FilterChapter({
.find((c) => c.id === chapter.id)
?.puzzles?.map((p) => p.tags)
.flat()
.filter((tag) => difficulty.some((d) => tag?.name === d.value))
.filter((tag, index, self) => self.findIndex((t) => t!.name === tag!.name) === index)
.map((t) => {
return { title: t!.name, value: t!.name };
}) as { title: string; value: string }[];
options?.unshift({ title: 'Pas encore terminé(s)', value: 'no-completed' });
options?.unshift({ title: 'Toutes les difficultés', value: '' });
setFilterChapter(chapter.id);
function handleChange(event: ChangeEvent<HTMLSelectElement>) {
setFilter(event.target.value);
// TODO OPTI
// @ts-ignore
setStored(event.target.value);
}
useEffect(() => {
if (stored) {
// TODO OPTI
// @ts-ignore
setFilter(stored);
}
}, [stored]);
return <Select className="w-44" options={options} value={filter} onChange={handleChange} />;
}
function FilterTags({
chapters,
chapter,
filter,
setFilter,
setFilterChapter
}: {
chapters: Chapter[];
chapter: Chapter;
filter: string;
setFilter: (filter: string) => void;
setFilterChapter: (chapter: number) => void;
}) {
const [stored, setStored] = useLocalStorage({
key: 'filter-tags',
initialValue: ''
});
let options = [] as { title: string; value: string }[];
options = chapters
.find((c) => c.id === chapter.id)
?.puzzles?.map((p) => p.tags)
.flat()
.filter((tag) => !difficulty.some((d) => tag?.name === d.value))
.filter((tag, index, self) => self.findIndex((t) => t!.name === tag!.name) === index)
.map((t) => {
return { title: t!.name, value: t!.name };
}) as { title: string; value: string }[];
options?.unshift({ title: 'Pas encore terminé(s)', value: 'not-completed' });
options?.unshift({ title: 'Terminé(s)', value: 'completed' });
options?.unshift({ title: 'Tout les puzzles', value: '' });

View file

@ -22,7 +22,7 @@ const Select = forwardRef<
ref={ref}
>
{options.map((option) => (
<option key={option.value} value={option.value} selected={option.value === props.value}>
<option key={option.value} value={option.value}>
{option.title}
</option>
))}