Format & fix chapter & localStorage hook
This commit is contained in:
parent
065f517d54
commit
d09b1d5bc0
9 changed files with 90 additions and 27 deletions
35
lib/hooks/use-local-storage.ts
Normal file
35
lib/hooks/use-local-storage.ts
Normal file
|
@ -0,0 +1,35 @@
|
|||
import { useEffect, useState } from 'react';
|
||||
|
||||
export default function useLocalStorage<T>({
|
||||
key,
|
||||
initialValue,
|
||||
serialize = JSON.stringify,
|
||||
deserialize = JSON.parse
|
||||
}: {
|
||||
key: string;
|
||||
initialValue: T;
|
||||
serialize?: (value: T) => string;
|
||||
deserialize?: (value: string) => T;
|
||||
}) {
|
||||
const [stored, setStored] = useState(() => {
|
||||
try {
|
||||
const localStorageValue = window.localStorage.getItem(key);
|
||||
if (localStorageValue !== null) {
|
||||
return deserialize(localStorageValue);
|
||||
} else {
|
||||
return initialValue;
|
||||
}
|
||||
} catch {
|
||||
return initialValue;
|
||||
}
|
||||
});
|
||||
useEffect(() => {
|
||||
try {
|
||||
const serializedState = serialize(stored);
|
||||
window.localStorage.setItem(key, serializedState);
|
||||
} catch {
|
||||
// Ignore
|
||||
}
|
||||
}, [key, serialize, stored]);
|
||||
return [stored, setStored];
|
||||
}
|
|
@ -2,8 +2,7 @@ export const getChapters = async ({ token }: { token: string }): Promise<Chapter
|
|||
const res = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/chapters`, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`
|
||||
},
|
||||
cache: 'force-cache'
|
||||
}
|
||||
});
|
||||
|
||||
const chapters = (await res.json()) as Chapter[];
|
||||
|
|
|
@ -3,7 +3,8 @@ const nextConfig = {
|
|||
reactStrictMode: true,
|
||||
swcMinify: true,
|
||||
experimental: {
|
||||
appDir: true
|
||||
appDir: true,
|
||||
scrollRestoration: true
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@ const SCORE_COLORS = ['text-yellow-400', 'text-gray-400', 'text-orange-400'];
|
|||
|
||||
export default function Leaderboard() {
|
||||
// TODO CHANGER CECI
|
||||
const CHAPITRE_EVENT = 3;
|
||||
const CHAPITRE_EVENT = 1;
|
||||
|
||||
const subscription: SWRSubscription<string, ScoreEvent, Error> = (key, { next }) => {
|
||||
const socket = new WebSocket(key);
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
'use client';
|
||||
|
||||
import cookies from 'js-cookie';
|
||||
import { notFound } from 'next/navigation';
|
||||
import { notFound, useRouter } 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';
|
||||
import { getURL } from '@/lib/utils';
|
||||
|
||||
type PuzzleData = {
|
||||
answer: string;
|
||||
|
@ -29,6 +29,7 @@ export default function Puzzle({ token, id }: { token: string; id: number }) {
|
|||
|
||||
const { data: puzzle, isLoading } = usePuzzle({ token, id });
|
||||
const { mutate } = useSWRConfig();
|
||||
const router = useRouter();
|
||||
|
||||
const { register, handleSubmit } = useForm<PuzzleData>({
|
||||
defaultValues: {
|
||||
|
@ -118,19 +119,21 @@ export default function Puzzle({ token, id }: { token: string; id: number }) {
|
|||
</form>
|
||||
) : (
|
||||
<div className="flex items-center justify-between">
|
||||
<div className=" items-center gap-x-2">
|
||||
<div className="items-center gap-x-2">
|
||||
<p>
|
||||
Tentatives : <span className="text-brand-accent">{puzzle.tries}</span>
|
||||
Tentative(s) : <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>
|
||||
<Button
|
||||
kind="brand"
|
||||
type="button"
|
||||
onClick={() => router.push(getURL(`/dashboard/puzzles`))}
|
||||
>
|
||||
Retour aux puzzles
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
'use client';
|
||||
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { useContext, useMemo, useState } from 'react';
|
||||
import { type ChangeEvent, useContext, useEffect, useMemo, useState } from 'react';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { useSWRConfig } from 'swr';
|
||||
|
||||
|
@ -17,6 +17,7 @@ import { useGroups } from '@/lib/hooks/use-groups';
|
|||
import { usePuzzles } from '@/lib/hooks/use-puzzles';
|
||||
import type { Chapter, Puzzle } from '@/lib/puzzles';
|
||||
import { cn } from '@/lib/utils';
|
||||
import useLocalStorage from '@/lib/hooks/use-local-storage';
|
||||
|
||||
export default function Puzzles({ token }: { token: string }) {
|
||||
const { data: me } = useContext(UserContext);
|
||||
|
@ -62,6 +63,16 @@ export default function Puzzles({ token }: { token: string }) {
|
|||
return data?.find((chapter) => chapter.id === filterChapter)?.puzzles;
|
||||
}, [data, filter, filterChapter]);
|
||||
|
||||
useEffect(() => {
|
||||
const { hash } = window.location;
|
||||
if (hash) {
|
||||
const element = document.querySelector(hash);
|
||||
if (element) {
|
||||
element.scrollIntoView({ behavior: 'smooth' });
|
||||
}
|
||||
}
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
{(!isLoading &&
|
||||
|
@ -283,6 +294,13 @@ function FilterChapter({
|
|||
setFilter: (filter: string) => void;
|
||||
setFilterChapter: (chapter: number) => void;
|
||||
}) {
|
||||
const [stored, setStored] = useLocalStorage({
|
||||
key: 'puzzles-filter',
|
||||
initialValue: ''
|
||||
});
|
||||
|
||||
console.log(stored);
|
||||
|
||||
let options = [] as { title: string; value: string }[];
|
||||
|
||||
options = chapters
|
||||
|
@ -294,21 +312,28 @@ function FilterChapter({
|
|||
return { title: t!.name, value: t!.name };
|
||||
}) as { title: string; value: string }[];
|
||||
|
||||
|
||||
options?.unshift({ title: 'Pas encore terminé', value: 'no-completed' });
|
||||
options?.unshift({ title: 'Pas encore terminé', value: 'no-completed' });
|
||||
options?.unshift({ title: 'Terminés', value: 'completed' });
|
||||
options?.unshift({ title: 'Tout les puzzles', value: '' });
|
||||
|
||||
setFilterChapter(chapter.id);
|
||||
|
||||
return (
|
||||
<Select
|
||||
className="w-44"
|
||||
options={options || []}
|
||||
value={filter}
|
||||
onChange={(event) => setFilter(event.target.value)}
|
||||
/>
|
||||
);
|
||||
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} />;
|
||||
}
|
||||
|
||||
type GroupData = {
|
||||
|
|
|
@ -22,7 +22,7 @@ const Select = forwardRef<
|
|||
ref={ref}
|
||||
>
|
||||
{options.map((option) => (
|
||||
<option key={option.value} value={option.value}>
|
||||
<option key={option.value} value={option.value} selected={option.value === props.value}>
|
||||
{option.title}
|
||||
</option>
|
||||
))}
|
||||
|
|
|
@ -17,7 +17,7 @@ export default function Tips({ className, kind = 'info', text }: TipsProps) {
|
|||
className
|
||||
})}
|
||||
>
|
||||
<p className="py-2 px-4">{text}</p>
|
||||
<p className="px-4 py-2">{text}</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue