diff --git a/app/dashboard/logout/page.tsx b/app/dashboard/logout/page.tsx
new file mode 100644
index 0000000..7a93d87
--- /dev/null
+++ b/app/dashboard/logout/page.tsx
@@ -0,0 +1,7 @@
+import { useRouter } from 'next/navigation';
+
+export default function Page() {
+ const router = useRouter();
+ router.push('/');
+ return <>>;
+}
diff --git a/app/dashboard/settings/page.tsx b/app/dashboard/settings/page.tsx
index 83d4c17..811b200 100644
--- a/app/dashboard/settings/page.tsx
+++ b/app/dashboard/settings/page.tsx
@@ -61,13 +61,15 @@ export default function Page() {
return (
- {me && me?.groups?.length > 0 && (
-
+ ) : (
+ Vous n' êtes dans aucun groupe
)}
);
diff --git a/middleware.ts b/middleware.ts
index 5335a3f..48f542c 100644
--- a/middleware.ts
+++ b/middleware.ts
@@ -26,6 +26,13 @@ export async function middleware(req: NextRequest) {
return res;
}
+ if (isAuth && req.nextUrl.pathname.includes('logout')) {
+ res.cookies.set('token', '', {
+ path: '/',
+ expires: new Date(0)
+ });
+ }
+
if (isAuth && req.nextUrl.pathname.includes('sign')) {
return NextResponse.redirect(getURL('/dashboard/puzzles'));
}
@@ -55,9 +62,9 @@ async function validateToken(token: string | undefined) {
headers: {
Authorization: `Bearer ${token}`
},
- cache: 'no-cache',
+ cache: 'force-cache',
next: {
- revalidate: 60000
+ revalidate: 30
}
});
diff --git a/package.json b/package.json
index 2dddc30..8fa298f 100644
--- a/package.json
+++ b/package.json
@@ -25,13 +25,15 @@
"axios": "^1.3.4",
"boring-avatars": "^1.7.0",
"clsx": "^1.2.1",
- "framer-motion": "^10.11.2",
+ "framer-motion": "^10.12.4",
"js-cookie": "^3.0.1",
"next": "13.2.3",
"react": "18.2.0",
+ "react-confetti": "^6.1.0",
"react-dom": "18.2.0",
"react-hook-form": "^7.43.1",
"react-markdown": "^8.0.5",
+ "react-use": "^17.4.0",
"remark-breaks": "^3.0.2",
"remark-gfm": "^3.0.1",
"remixicon": "^2.5.0",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 41b4695..19c672a 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -17,8 +17,8 @@ dependencies:
specifier: ^1.2.1
version: 1.2.1
framer-motion:
- specifier: ^10.11.2
- version: 10.11.2(react-dom@18.2.0)(react@18.2.0)
+ specifier: ^10.12.4
+ version: 10.12.4(react-dom@18.2.0)(react@18.2.0)
js-cookie:
specifier: ^3.0.1
version: 3.0.1
@@ -28,6 +28,9 @@ dependencies:
react:
specifier: 18.2.0
version: 18.2.0
+ react-confetti:
+ specifier: ^6.1.0
+ version: 6.1.0(react@18.2.0)
react-dom:
specifier: 18.2.0
version: 18.2.0(react@18.2.0)
@@ -37,6 +40,9 @@ dependencies:
react-markdown:
specifier: ^8.0.5
version: 8.0.6(@types/react@18.0.27)(react@18.2.0)
+ react-use:
+ specifier: ^17.4.0
+ version: 17.4.0(react-dom@18.2.0)(react@18.2.0)
remark-breaks:
specifier: ^3.0.2
version: 3.0.2
@@ -671,6 +677,10 @@ packages:
'@types/unist': 2.0.6
dev: false
+ /@types/js-cookie@2.2.7:
+ resolution: {integrity: sha512-aLkWa0C0vO5b4Sr798E26QgOkss68Un0bLjs7u9qxzPT5CG+8DuNTffWES58YzJs3hrVAOs1wonycqEBqNJubA==}
+ dev: false
+
/@types/js-cookie@3.0.3:
resolution: {integrity: sha512-Xe7IImK09HP1sv2M/aI+48a20VX+TdRJucfq4vfRVy6nWN8PYPOEnlMRSgxJAgYQIXJVL8dZ4/ilAM7dWNaOww==}
dev: true
@@ -854,6 +864,10 @@ packages:
eslint-visitor-keys: 3.4.0
dev: true
+ /@xobotyi/scrollbar-width@1.9.5:
+ resolution: {integrity: sha512-N8tkAACJx2ww8vFMneJmaAgmjAG1tnVBZJRLRcx061tmsLRZHSEZSLuGWnwPtunsSLvSqXQ2wfp7Mgqg1I+2dQ==}
+ dev: false
+
/acorn-jsx@5.3.2(acorn@8.8.2):
resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==}
peerDependencies:
@@ -1158,6 +1172,12 @@ packages:
resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
dev: true
+ /copy-to-clipboard@3.3.3:
+ resolution: {integrity: sha512-2KV8NhB5JqC3ky0r9PMCAZKbUHSwtEo4CwCs0KXgruG43gX5PMqDEBbVU4OUzw2MuAWUfsuFmWvEKG5QRfSnJA==}
+ dependencies:
+ toggle-selection: 1.0.6
+ dev: false
+
/cross-spawn@7.0.3:
resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==}
engines: {node: '>= 8'}
@@ -1167,6 +1187,20 @@ packages:
which: 2.0.2
dev: true
+ /css-in-js-utils@3.1.0:
+ resolution: {integrity: sha512-fJAcud6B3rRu+KHYk+Bwf+WFL2MDCJJ1XG9x137tJQ0xYxor7XziQtuGFbWNdqrvF4Tk26O3H73nfVqXt/fW1A==}
+ dependencies:
+ hyphenate-style-name: 1.0.4
+ dev: false
+
+ /css-tree@1.1.3:
+ resolution: {integrity: sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==}
+ engines: {node: '>=8.0.0'}
+ dependencies:
+ mdn-data: 2.0.14
+ source-map: 0.6.1
+ dev: false
+
/cssesc@3.0.0:
resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==}
engines: {node: '>=4'}
@@ -1311,6 +1345,12 @@ packages:
tapable: 2.2.1
dev: true
+ /error-stack-parser@2.1.4:
+ resolution: {integrity: sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ==}
+ dependencies:
+ stackframe: 1.3.4
+ dev: false
+
/es-abstract@1.21.2:
resolution: {integrity: sha512-y/B5POM2iBnIxCiernH1G7rC9qQoM77lLIMQLuob0zhp8C56Po81+2Nj0WFKnd0pNReDTnkYryc+zhOzpEIROg==}
engines: {node: '>= 0.4'}
@@ -1738,7 +1778,6 @@ packages:
/fast-deep-equal@3.1.3:
resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
- dev: true
/fast-diff@1.2.0:
resolution: {integrity: sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==}
@@ -1763,6 +1802,18 @@ packages:
resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==}
dev: true
+ /fast-loops@1.1.3:
+ resolution: {integrity: sha512-8EZzEP0eKkEEVX+drtd9mtuQ+/QrlfW/5MlwcwK5Nds6EkZ/tRzEexkzUY2mIssnAyVLT+TKHuRXmFNNXYUd6g==}
+ dev: false
+
+ /fast-shallow-equal@1.0.0:
+ resolution: {integrity: sha512-HPtaa38cPgWvaCFmRNhlc6NG7pv6NUHqjPgVAkWGoB9mQMwYB27/K0CvOM5Czy+qpT3e8XJ6Q4aPAnzpNpzNaw==}
+ dev: false
+
+ /fastest-stable-stringify@2.0.2:
+ resolution: {integrity: sha512-bijHueCGd0LqqNK9b5oCMHc0MluJAx0cwqASgbWMvkO01lCYgIhacVRLcaDz3QnyYIRNJRDwMb41VuT6pHJ91Q==}
+ dev: false
+
/fastq@1.15.0:
resolution: {integrity: sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==}
dependencies:
@@ -1832,8 +1883,8 @@ packages:
resolution: {integrity: sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA==}
dev: true
- /framer-motion@10.11.2(react-dom@18.2.0)(react@18.2.0):
- resolution: {integrity: sha512-IrwuC9regNOU99JoM/Z62CAMA3awGV6AcF7e3bcgXk/ZoNlGSt5aVq0J7UAwtLmCkwVlRvBkiMnvv2mZ1GW2pg==}
+ /framer-motion@10.12.4(react-dom@18.2.0)(react@18.2.0):
+ resolution: {integrity: sha512-9gLtv8T6dui0tujHROR+VM3kdJyKiFCFiD94IQE+0OuX6LaIyXtdVpviokVdrHSb1giWhmmX4yzoucALMx6mtw==}
peerDependencies:
react: ^18.0.0
react-dom: ^18.0.0
@@ -2054,6 +2105,10 @@ packages:
resolution: {integrity: sha512-nAxA0v8+vXSBDt3AnRUNjyRIQ0rD+ntpbAp4LnPkumc5M9yUbSMa4XDU9Q6etY4f1Wp4bNgvc1yjiZtsTTrSng==}
dev: false
+ /hyphenate-style-name@1.0.4:
+ resolution: {integrity: sha512-ygGZLjmXfPHj+ZWh6LwbC37l43MhfztxetbFCoYTM2VjkIUpeHgSNn7QIyVFj7YQ1Wl9Cbw5sholVJPzWvC2MQ==}
+ dev: false
+
/ignore@5.2.4:
resolution: {integrity: sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==}
engines: {node: '>= 4'}
@@ -2087,6 +2142,13 @@ packages:
resolution: {integrity: sha512-7NXolsK4CAS5+xvdj5OMMbI962hU/wvwoxk+LWR9Ek9bVtyuuYScDN6eS0rUm6TxApFpw7CX1o4uJzcd4AyD3Q==}
dev: false
+ /inline-style-prefixer@6.0.4:
+ resolution: {integrity: sha512-FwXmZC2zbeeS7NzGjJ6pAiqRhXR0ugUShSNb6GApMl6da0/XGc4MOJsoWAywia52EEWbXNSy0pzkwz/+Y+swSg==}
+ dependencies:
+ css-in-js-utils: 3.1.0
+ fast-loops: 1.1.3
+ dev: false
+
/internal-slot@1.0.5:
resolution: {integrity: sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ==}
engines: {node: '>= 0.4'}
@@ -2291,6 +2353,10 @@ packages:
hasBin: true
dev: true
+ /js-cookie@2.2.1:
+ resolution: {integrity: sha512-HvdH2LzI/EAZcUwA8+0nKNtWHqS+ZmijLA30RwZA0bo7ToCckjK5MkGhjED9KoRcXO6BaGI3I9UIzSA1FKFPOQ==}
+ dev: false
+
/js-cookie@3.0.1:
resolution: {integrity: sha512-+0rgsUXZu4ncpPxRL+lNEptWMOWl9etvPHc/koSRp6MPwpRYAhmk0dUG00J4bxVV3r9uUzfo24wW0knS07SKSw==}
engines: {node: '>=12'}
@@ -2528,6 +2594,10 @@ packages:
'@types/mdast': 3.0.11
dev: false
+ /mdn-data@2.0.14:
+ resolution: {integrity: sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==}
+ dev: false
+
/merge2@1.4.1:
resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==}
engines: {node: '>= 8'}
@@ -2837,6 +2907,24 @@ packages:
thenify-all: 1.6.0
dev: true
+ /nano-css@5.3.5(react-dom@18.2.0)(react@18.2.0):
+ resolution: {integrity: sha512-vSB9X12bbNu4ALBu7nigJgRViZ6ja3OU7CeuiV1zMIbXOdmkLahgtPmh3GBOlDxbKY0CitqlPdOReGlBLSp+yg==}
+ peerDependencies:
+ react: '*'
+ react-dom: '*'
+ dependencies:
+ css-tree: 1.1.3
+ csstype: 3.1.2
+ fastest-stable-stringify: 2.0.2
+ inline-style-prefixer: 6.0.4
+ react: 18.2.0
+ react-dom: 18.2.0(react@18.2.0)
+ rtl-css-js: 1.16.1
+ sourcemap-codec: 1.4.8
+ stacktrace-js: 2.0.2
+ stylis: 4.1.3
+ dev: false
+
/nanoid@3.3.6:
resolution: {integrity: sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==}
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
@@ -3252,6 +3340,16 @@ packages:
engines: {node: '>=10'}
dev: true
+ /react-confetti@6.1.0(react@18.2.0):
+ resolution: {integrity: sha512-7Ypx4vz0+g8ECVxr88W9zhcQpbeujJAVqL14ZnXJ3I23mOI9/oBVTQ3dkJhUmB0D6XOtCZEM6N0Gm9PMngkORw==}
+ engines: {node: '>=10.18'}
+ peerDependencies:
+ react: ^16.3.0 || ^17.0.1 || ^18.0.0
+ dependencies:
+ react: 18.2.0
+ tween-functions: 1.2.0
+ dev: false
+
/react-dom@18.2.0(react@18.2.0):
resolution: {integrity: sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==}
peerDependencies:
@@ -3357,6 +3455,40 @@ packages:
tslib: 2.5.0
dev: false
+ /react-universal-interface@0.6.2(react@18.2.0)(tslib@2.5.0):
+ resolution: {integrity: sha512-dg8yXdcQmvgR13RIlZbTRQOoUrDciFVoSBZILwjE2LFISxZZ8loVJKAkuzswl5js8BHda79bIb2b84ehU8IjXw==}
+ peerDependencies:
+ react: '*'
+ tslib: '*'
+ dependencies:
+ react: 18.2.0
+ tslib: 2.5.0
+ dev: false
+
+ /react-use@17.4.0(react-dom@18.2.0)(react@18.2.0):
+ resolution: {integrity: sha512-TgbNTCA33Wl7xzIJegn1HndB4qTS9u03QUwyNycUnXaweZkE4Kq2SB+Yoxx8qbshkZGYBDvUXbXWRUmQDcZZ/Q==}
+ peerDependencies:
+ react: ^16.8.0 || ^17.0.0 || ^18.0.0
+ react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
+ dependencies:
+ '@types/js-cookie': 2.2.7
+ '@xobotyi/scrollbar-width': 1.9.5
+ copy-to-clipboard: 3.3.3
+ fast-deep-equal: 3.1.3
+ fast-shallow-equal: 1.0.0
+ js-cookie: 2.2.1
+ nano-css: 5.3.5(react-dom@18.2.0)(react@18.2.0)
+ react: 18.2.0
+ react-dom: 18.2.0(react@18.2.0)
+ react-universal-interface: 0.6.2(react@18.2.0)(tslib@2.5.0)
+ resize-observer-polyfill: 1.5.1
+ screenfull: 5.2.0
+ set-harmonic-interval: 1.0.1
+ throttle-debounce: 3.0.1
+ ts-easing: 0.2.0
+ tslib: 2.5.0
+ dev: false
+
/react@18.2.0:
resolution: {integrity: sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==}
engines: {node: '>=0.10.0'}
@@ -3436,6 +3568,10 @@ packages:
resolution: {integrity: sha512-q54ra2QutYDZpuSnFjmeagmEiN9IMo56/zz5dDNitzKD23oFRw77cWo4TsrAdmdkPiEn8mxlrTqxnkujDbEGww==}
dev: false
+ /resize-observer-polyfill@1.5.1:
+ resolution: {integrity: sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==}
+ dev: false
+
/resolve-from@4.0.0:
resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==}
engines: {node: '>=4'}
@@ -3471,6 +3607,12 @@ packages:
glob: 7.2.3
dev: true
+ /rtl-css-js@1.16.1:
+ resolution: {integrity: sha512-lRQgou1mu19e+Ya0LsTvKrVJ5TYUbqCVPAiImX3UfLTenarvPUl1QFdvu5Z3PYmHT9RCcwIfbjRQBntExyj3Zg==}
+ dependencies:
+ '@babel/runtime': 7.21.0
+ dev: false
+
/run-parallel@1.2.0:
resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==}
dependencies:
@@ -3498,6 +3640,11 @@ packages:
loose-envify: 1.4.0
dev: false
+ /screenfull@5.2.0:
+ resolution: {integrity: sha512-9BakfsO2aUQN2K9Fdbj87RJIEZ82Q9IGim7FqM5OsebfoFC6ZHXgDq/KvniuLTPdeM8wY2o6Dj3WQ7KeQCj3cA==}
+ engines: {node: '>=0.10.0'}
+ dev: false
+
/semver@6.3.0:
resolution: {integrity: sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==}
hasBin: true
@@ -3511,6 +3658,11 @@ packages:
lru-cache: 6.0.0
dev: true
+ /set-harmonic-interval@1.0.1:
+ resolution: {integrity: sha512-AhICkFV84tBP1aWqPwLZqFvAwqEoVA9kxNMniGEUvzOlm4vLmOFLiTT3UZ6bziJTy4bOVpzWGTfSCbmaayGx8g==}
+ engines: {node: '>=6.9'}
+ dev: false
+
/shebang-command@2.0.0:
resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
engines: {node: '>=8'}
@@ -3545,10 +3697,50 @@ packages:
resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==}
engines: {node: '>=0.10.0'}
+ /source-map@0.5.6:
+ resolution: {integrity: sha512-MjZkVp0NHr5+TPihLcadqnlVoGIoWo4IBHptutGh9wI3ttUYvCG26HkSuDi+K6lsZ25syXJXcctwgyVCt//xqA==}
+ engines: {node: '>=0.10.0'}
+ dev: false
+
+ /source-map@0.6.1:
+ resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==}
+ engines: {node: '>=0.10.0'}
+ dev: false
+
+ /sourcemap-codec@1.4.8:
+ resolution: {integrity: sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==}
+ deprecated: Please use @jridgewell/sourcemap-codec instead
+ dev: false
+
/space-separated-tokens@2.0.2:
resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==}
dev: false
+ /stack-generator@2.0.10:
+ resolution: {integrity: sha512-mwnua/hkqM6pF4k8SnmZ2zfETsRUpWXREfA/goT8SLCV4iOFa4bzOX2nDipWAZFPTjLvQB82f5yaodMVhK0yJQ==}
+ dependencies:
+ stackframe: 1.3.4
+ dev: false
+
+ /stackframe@1.3.4:
+ resolution: {integrity: sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==}
+ dev: false
+
+ /stacktrace-gps@3.1.2:
+ resolution: {integrity: sha512-GcUgbO4Jsqqg6RxfyTHFiPxdPqF+3LFmQhm7MgCuYQOYuWyqxo5pwRPz5d/u6/WYJdEnWfK4r+jGbyD8TSggXQ==}
+ dependencies:
+ source-map: 0.5.6
+ stackframe: 1.3.4
+ dev: false
+
+ /stacktrace-js@2.0.2:
+ resolution: {integrity: sha512-Je5vBeY4S1r/RnLydLl0TBTi3F2qdfWmYsGvtfZgEI+SCprPppaIhQf5nGcal4gI4cGpCV/duLcAzT1np6sQqg==}
+ dependencies:
+ error-stack-parser: 2.1.4
+ stack-generator: 2.0.10
+ stacktrace-gps: 3.1.2
+ dev: false
+
/stop-iteration-iterator@1.0.0:
resolution: {integrity: sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ==}
engines: {node: '>= 0.4'}
@@ -3634,6 +3826,10 @@ packages:
react: 18.2.0
dev: false
+ /stylis@4.1.3:
+ resolution: {integrity: sha512-GP6WDNWf+o403jrEp9c5jibKavrtLW+/qYGhFxFrG8maXhwTBI7gLLhiBb0o7uFccWN+EOS9aMO6cGHWAO07OA==}
+ dev: false
+
/sucrase@3.31.0:
resolution: {integrity: sha512-6QsHnkqyVEzYcaiHsOKkzOtOgdJcb8i54x6AV2hDwyZcY9ZyykGZVw6L/YN98xC0evwTP6utsWWrKRaa8QlfEQ==}
engines: {node: '>=8'}
@@ -3737,6 +3933,11 @@ packages:
any-promise: 1.3.0
dev: true
+ /throttle-debounce@3.0.1:
+ resolution: {integrity: sha512-dTEWWNu6JmeVXY0ZYoPuH5cRIwc0MeGbJwah9KUNYSJwommQpCzTySTpEe8Gs1J23aeWEuAobe4Ag7EHVt/LOg==}
+ engines: {node: '>=10'}
+ dev: false
+
/tiny-glob@0.2.9:
resolution: {integrity: sha512-g/55ssRPUjShh+xkfx9UPDXqhckHEsHr4Vd9zX55oSdGZc/MD0m3sferOkwWtp98bv+kcVfEHtRJgBVJzelrzg==}
dependencies:
@@ -3751,6 +3952,10 @@ packages:
is-number: 7.0.0
dev: true
+ /toggle-selection@1.0.6:
+ resolution: {integrity: sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ==}
+ dev: false
+
/trim-lines@3.0.1:
resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==}
dev: false
@@ -3759,6 +3964,10 @@ packages:
resolution: {integrity: sha512-AqTiAOLcj85xS7vQ8QkAV41hPDIJ71XJB4RCUrzo/1GM2CQwhkJGaf9Hgr7BOugMRpgGUrqRg/DrBDl4H40+8g==}
dev: false
+ /ts-easing@0.2.0:
+ resolution: {integrity: sha512-Z86EW+fFFh/IFB1fqQ3/+7Zpf9t2ebOAxNI/V6Wo7r5gqiqtxmgTlQ1qbqQcjLKYeSHPTsEmvlJUDg/EuL0uHQ==}
+ dev: false
+
/ts-interface-checker@0.1.13:
resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==}
dev: true
@@ -3789,6 +3998,10 @@ packages:
typescript: 4.9.5
dev: true
+ /tween-functions@1.2.0:
+ resolution: {integrity: sha512-PZBtLYcCLtEcjL14Fzb1gSxPBeL7nWvGhO5ZFPGqziCcr8uvHp0NDmdjBchp6KHL+tExcg0m3NISmKxhU394dA==}
+ dev: false
+
/type-check@0.4.0:
resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==}
engines: {node: '>= 0.8.0'}
diff --git a/ui/Leaderboard.tsx b/ui/Leaderboard.tsx
index ac74431..f49cf1b 100644
--- a/ui/Leaderboard.tsx
+++ b/ui/Leaderboard.tsx
@@ -1,5 +1,6 @@
'use client';
+import { AnimatePresence, motion } from 'framer-motion';
import useSWRSubscription, { type SWRSubscription } from 'swr/subscription';
import { type ScoreEvent } from '@/lib/leaderboard';
@@ -53,61 +54,70 @@ export default function Leaderboard() {
{data && data.end_date && (
)}
-
- {data?.groups.map((group, key) => {
- const tries = group.players.reduce((a, b) => a + b.tries, 0);
- return (
- -
-
-
- {group.rank}
-
-
-
-
{group.name}
-
- {group.players && group.players.length > 1
- ? group.players
- .map((player) => player.pseudo || 'Anonyme')
- .sort((a, b) => a.localeCompare(b))
- .join(', ')
- : group.players[0].pseudo}
+
+
+ {data?.groups.map((group, key) => {
+ const tries = group.players.reduce((a, b) => a + b.tries, 0) || 0;
+ const puzzles = group.players.reduce((a, b) => a + b.completion, 0) || 0;
+ return (
+
+
+
+ {group.rank}
+
+
+
+ {group.name}
+
+ {group.players && group.players.length > 1
+ ? group.players
+ .map((player) => player.pseudo || 'Anonyme')
+ .sort((a, b) => a.localeCompare(b))
+ .join(', ')
+ : group.players[0].pseudo}
+
+
+
+
+
+
+ Puzzle{puzzles > 1 ? 's' : ''}
+ {puzzles}
+
+
+ Essai{tries > 1 ? 's' : ''}
+ {tries}
+
+
+ Score
+
+ {group.players.reduce((a, b) => a + b.score, 0)}
-
-
-
- Essai{tries > 1 ? 's' : ''}
- {tries}
-
- {/*
- Puzzles
-
- {group.players.reduce((a, b) => a + b.completion, 0)}
-
-
*/}
-
- Score
-
- {group.players.reduce((a, b) => a + b.score, 0)}
-
-
-
-
- );
- }) ||
- [...Array(20).keys()].map((i) => (
-
- ))}
-
+
+ );
+ }) ||
+ [...Array(20).keys()].map((i) => (
+
+ ))}
+
+
);
diff --git a/ui/Puzzle.tsx b/ui/Puzzle.tsx
index 745a4b2..53632ca 100644
--- a/ui/Puzzle.tsx
+++ b/ui/Puzzle.tsx
@@ -2,16 +2,17 @@
import cookies from 'js-cookie';
import { notFound, useRouter } from 'next/navigation';
-import { useState } from 'react';
+import { useContext, useState } from 'react';
import { useForm } from 'react-hook-form';
import { useSWRConfig } from 'swr';
import { usePuzzle } from '@/lib/hooks/use-puzzles';
+import { UserContext } from '@/context/user';
+import { getURL } from '@/lib/utils';
import Button from './Button';
import Input from './Input';
import ToHTML from './ToHTML';
-import { getURL } from '@/lib/utils';
type PuzzleData = {
answer: string;
@@ -27,6 +28,7 @@ type Granted = {
};
export default function Puzzle({ token, id }: { token: string; id: number }) {
+ const { data: me } = useContext(UserContext);
const [granted, setGranted] = useState
(null);
const { data: puzzle, isLoading } = usePuzzle({ token, id });
const { mutate } = useSWRConfig();
@@ -81,6 +83,11 @@ export default function Puzzle({ token, id }: { token: string; id: number }) {
notFound();
}
+ // TODO : add a check to see if the user is in the group of the puzzle
+ if (me?.groups.length === 0) {
+ router.push('/dashboard/puzzles');
+ }
+
return (
diff --git a/ui/Timer.tsx b/ui/Timer.tsx
index fb0a181..8e35b47 100644
--- a/ui/Timer.tsx
+++ b/ui/Timer.tsx
@@ -1,5 +1,7 @@
import clsx from 'clsx';
import { useEffect, useReducer } from 'react';
+import { useWindowSize } from 'react-use';
+import Confetti from 'react-confetti';
type State = {
hours: number;
@@ -22,6 +24,8 @@ function reducer(state: State, action: Action): State {
}
export function Timer({ targetDate, className }: { targetDate: Date; className?: string }) {
+ const { width, height } = useWindowSize();
+
const [timeRemaining, dispatch] = useReducer(reducer, {
hours: 0,
minutes: 0,
@@ -46,7 +50,12 @@ export function Timer({ targetDate, className }: { targetDate: Date; className?:
}, [targetDate]);
if (timeRemaining.hours < 0) {
- return Terminé;
+ return (
+ <>
+
+ Terminé
+ >
+ );
}
return (
diff --git a/ui/dashboard/Usernav.tsx b/ui/dashboard/Usernav.tsx
index 5252420..d4554d3 100644
--- a/ui/dashboard/Usernav.tsx
+++ b/ui/dashboard/Usernav.tsx
@@ -2,7 +2,6 @@
import { UserContext } from '@/context/user';
import { titleCase } from '@/lib/utils';
-import cookies from 'js-cookie';
import { useRouter, useSelectedLayoutSegment } from 'next/navigation';
import { useContext, useEffect, useState } from 'react';
import AvatarComponent from '../Avatar';
@@ -22,11 +21,6 @@ export default function Usernav({ isOpen, toggle }: { isOpen: boolean; toggle: (
}
}, [isOpen]);
- async function handleLogout() {
- cookies.remove('token');
- router.replace('/');
- }
-
return (
@@ -56,7 +50,7 @@ export default function Usernav({ isOpen, toggle }: { isOpen: boolean; toggle: (