Compare commits
23 Commits
lightemera
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| b0900be760 | |||
| 98290b41a1 | |||
| 3660e1cd7c | |||
| 8c184944f3 | |||
| 49bc4f64fb | |||
| 36f5e7dfa6 | |||
| 5669bb6be3 | |||
| c49faca15b | |||
| 08a8819d25 | |||
| d2a8189a6b | |||
| 9ae801fea9 | |||
| 9bcded7f9a | |||
| 80f786ff61 | |||
| ddfdcd613f | |||
| 95eec4f93e | |||
| 590b6773f8 | |||
| bca2ca59e6 | |||
| 9b6403f9a5 | |||
| a441c3e78b | |||
| 6a91e0739a | |||
| a9ed1691f2 | |||
| c4ce652ab3 | |||
| 5a5600c375 |
23
.gitignore
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
/.pnp
|
||||
.pnp.js
|
||||
|
||||
# testing
|
||||
/coverage
|
||||
|
||||
# production
|
||||
/build
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
.env.local
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
11
api/package.json
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"name": "nuit-info-2023-api",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC"
|
||||
}
|
||||
23
webapp/.gitignore
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
/.pnp
|
||||
.pnp.js
|
||||
|
||||
# testing
|
||||
/coverage
|
||||
|
||||
# production
|
||||
/build
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
.env.local
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
70
webapp/README.md
Normal file
@@ -0,0 +1,70 @@
|
||||
# Getting Started with Create React App
|
||||
|
||||
This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
|
||||
|
||||
## Available Scripts
|
||||
|
||||
In the project directory, you can run:
|
||||
|
||||
### `npm start`
|
||||
|
||||
Runs the app in the development mode.\
|
||||
Open [http://localhost:3000](http://localhost:3000) to view it in your browser.
|
||||
|
||||
The page will reload when you make changes.\
|
||||
You may also see any lint errors in the console.
|
||||
|
||||
### `npm test`
|
||||
|
||||
Launches the test runner in the interactive watch mode.\
|
||||
See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
|
||||
|
||||
### `npm run build`
|
||||
|
||||
Builds the app for production to the `build` folder.\
|
||||
It correctly bundles React in production mode and optimizes the build for the best performance.
|
||||
|
||||
The build is minified and the filenames include the hashes.\
|
||||
Your app is ready to be deployed!
|
||||
|
||||
See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
|
||||
|
||||
### `npm run eject`
|
||||
|
||||
**Note: this is a one-way operation. Once you `eject`, you can't go back!**
|
||||
|
||||
If you aren't satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
|
||||
|
||||
Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you're on your own.
|
||||
|
||||
You don't have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn't feel obligated to use this feature. However we understand that this tool wouldn't be useful if you couldn't customize it when you are ready for it.
|
||||
|
||||
## Learn More
|
||||
|
||||
You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
|
||||
|
||||
To learn React, check out the [React documentation](https://reactjs.org/).
|
||||
|
||||
### Code Splitting
|
||||
|
||||
This section has moved here: [https://facebook.github.io/create-react-app/docs/code-splitting](https://facebook.github.io/create-react-app/docs/code-splitting)
|
||||
|
||||
### Analyzing the Bundle Size
|
||||
|
||||
This section has moved here: [https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size](https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size)
|
||||
|
||||
### Making a Progressive Web App
|
||||
|
||||
This section has moved here: [https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app](https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app)
|
||||
|
||||
### Advanced Configuration
|
||||
|
||||
This section has moved here: [https://facebook.github.io/create-react-app/docs/advanced-configuration](https://facebook.github.io/create-react-app/docs/advanced-configuration)
|
||||
|
||||
### Deployment
|
||||
|
||||
This section has moved here: [https://facebook.github.io/create-react-app/docs/deployment](https://facebook.github.io/create-react-app/docs/deployment)
|
||||
|
||||
### `npm run build` fails to minify
|
||||
|
||||
This section has moved here: [https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify](https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify)
|
||||
18063
webapp/package-lock.json
generated
Normal file
43
webapp/package.json
Normal file
@@ -0,0 +1,43 @@
|
||||
{
|
||||
"name": "nuit-info-2023-webapp",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@testing-library/jest-dom": "^5.17.0",
|
||||
"@testing-library/react": "^13.4.0",
|
||||
"@testing-library/user-event": "^13.5.0",
|
||||
"flowbite": "^2.2.0",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-i18next": "^13.5.0",
|
||||
"react-router-dom": "^6.20.1",
|
||||
"react-scripts": "5.0.1",
|
||||
"tailwindcss": "^3.3.6",
|
||||
"translate": "^2.0.2",
|
||||
"web-vitals": "^2.1.4"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "react-scripts start",
|
||||
"build": "react-scripts build",
|
||||
"test": "react-scripts test",
|
||||
"eject": "react-scripts eject"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"extends": [
|
||||
"react-app",
|
||||
"react-app/jest"
|
||||
]
|
||||
},
|
||||
"browserslist": {
|
||||
"production": [
|
||||
">0.2%",
|
||||
"not dead",
|
||||
"not op_mini all"
|
||||
],
|
||||
"development": [
|
||||
"last 1 chrome version",
|
||||
"last 1 firefox version",
|
||||
"last 1 safari version"
|
||||
]
|
||||
}
|
||||
}
|
||||
BIN
webapp/public/favicon.ico
Normal file
|
After Width: | Height: | Size: 3.8 KiB |
44
webapp/public/index.html
Normal file
@@ -0,0 +1,44 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="theme-color" content="#000000" />
|
||||
<meta
|
||||
name="description"
|
||||
content="Web site created using create-react-app"
|
||||
/>
|
||||
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
|
||||
<!--
|
||||
manifest.json provides metadata used when your web app is installed on a
|
||||
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
|
||||
-->
|
||||
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
|
||||
<!--
|
||||
Notice the use of %PUBLIC_URL% in the tags above.
|
||||
It will be replaced with the URL of the `public` folder during the build.
|
||||
Only files inside the `public` folder can be referenced from the HTML.
|
||||
|
||||
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
|
||||
work correctly both with client-side routing and a non-root public URL.
|
||||
Learn how to configure a non-root public URL by running `npm run build`.
|
||||
-->
|
||||
<title>React App</title>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
<div id="root"></div>
|
||||
<!--
|
||||
This HTML file is a template.
|
||||
If you open it directly in the browser, you will see an empty page.
|
||||
|
||||
You can add webfonts, meta tags, or analytics to this file.
|
||||
The build step will place the bundled scripts into the <body> tag.
|
||||
|
||||
To begin the development, run `npm start` or `yarn start`.
|
||||
To create a production bundle, use `npm run build` or `yarn build`.
|
||||
-->
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/flowbite/2.2.0/flowbite.min.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
BIN
webapp/public/logo192.png
Normal file
|
After Width: | Height: | Size: 5.2 KiB |
BIN
webapp/public/logo512.png
Normal file
|
After Width: | Height: | Size: 9.4 KiB |
25
webapp/public/manifest.json
Normal file
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"short_name": "React App",
|
||||
"name": "Create React App Sample",
|
||||
"icons": [
|
||||
{
|
||||
"src": "favicon.ico",
|
||||
"sizes": "64x64 32x32 24x24 16x16",
|
||||
"type": "image/x-icon"
|
||||
},
|
||||
{
|
||||
"src": "logo192.png",
|
||||
"type": "image/png",
|
||||
"sizes": "192x192"
|
||||
},
|
||||
{
|
||||
"src": "logo512.png",
|
||||
"type": "image/png",
|
||||
"sizes": "512x512"
|
||||
}
|
||||
],
|
||||
"start_url": ".",
|
||||
"display": "standalone",
|
||||
"theme_color": "#000000",
|
||||
"background_color": "#ffffff"
|
||||
}
|
||||
3
webapp/public/robots.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
# https://www.robotstxt.org/robotstxt.html
|
||||
User-agent: *
|
||||
Disallow:
|
||||
BIN
webapp/src/assets/perso/perso-1/pose-1.png
Normal file
|
After Width: | Height: | Size: 26 KiB |
BIN
webapp/src/assets/perso/perso-1/pose-2.png
Normal file
|
After Width: | Height: | Size: 156 KiB |
BIN
webapp/src/assets/perso/perso-2/pose-1.png
Normal file
|
After Width: | Height: | Size: 66 KiB |
BIN
webapp/src/assets/perso/perso-3/pose-1.png
Normal file
|
After Width: | Height: | Size: 93 KiB |
BIN
webapp/src/assets/perso/perso-4/pose-1.png
Normal file
|
After Width: | Height: | Size: 38 KiB |
BIN
webapp/src/assets/perso/perso-4/pose-2.png
Normal file
|
After Width: | Height: | Size: 66 KiB |
0
webapp/src/assets/txt
Normal file
12
webapp/src/components/content/game/ammo/index.jsx
Normal file
@@ -0,0 +1,12 @@
|
||||
function Ammo({arg}) {
|
||||
return(
|
||||
<div className="relative h-48 w-32 bottom-0 bg-white overflow-hidden transition-all duration-200 hover:bottom-full rounded-md">
|
||||
<p>{arg}</p>
|
||||
<div className="absolute left-d24 bottom-0 h-4 w-full rotate-45 bg-darkGreen">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default Ammo;
|
||||
16
webapp/src/components/content/game/ammobox/index.jsx
Normal file
@@ -0,0 +1,16 @@
|
||||
import Ammo from "../ammo";
|
||||
import { get } from "../../../../modules/fetcher";
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
function AmmoBox({game}) {
|
||||
return(
|
||||
<div className="fixed bottom-0 left-28 right-0 h-24 flex flex-row justify-between items-start">
|
||||
<Ammo arg={"test"}/>
|
||||
<Ammo arg={"test2"}/>
|
||||
<Ammo arg={"test3"}/>
|
||||
<Ammo arg={"test4"}/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default AmmoBox;
|
||||
50
webapp/src/components/content/game/cardmission/index.jsx
Normal file
@@ -0,0 +1,50 @@
|
||||
import {useState} from "react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import img from "../poseimg";
|
||||
|
||||
|
||||
function CardMission({theme}) {
|
||||
|
||||
const id = theme.id;
|
||||
|
||||
const navigate = useNavigate();
|
||||
|
||||
const [validate,setValidate] = useState(false);
|
||||
let transi = validate ? "right-0" : "right-dfull";
|
||||
let group1 = validate ? "left-0" : "left-1/4";
|
||||
let group2 = validate ? "left-1/4" : "left-1/2";
|
||||
let group3 = validate ? "left-1/2" : "left-3/4";
|
||||
let group4 = validate ? "left-3/4" : "left-full";
|
||||
|
||||
function changeTransi(id) {
|
||||
setValidate(!validate);
|
||||
setTimeout(() => {
|
||||
navigate('/game/'+id);
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
return(
|
||||
<div className="h-60 w-72 bg-slate-200 flex flex-col justify-start items-center gap-5 rounded-md">
|
||||
<h2 className="font-bold mt-1">{theme.theme}</h2>
|
||||
<div>
|
||||
<img src={img[id]} alt="perso" className="h-28 w-28 rounded-full object-cover object-left-top"/>
|
||||
</div>
|
||||
<button className="px-1 py-2 w-32 rounded-lg text-white bg-darkBlue mt-2" onClick={() => changeTransi(id)}>Play</button>
|
||||
<div id={"transi-"+id} className={"fixed w-full h-1/2 top-1/4 py-4 bg-red-700 transition-all flex justify-center items-center "+transi}>
|
||||
<img src={img[id]} alt="perso" className="h-full"/>
|
||||
{/* <div className={"h-2 w-24 rounded-full bg-white blur-sm shadow shadow-white fixed top-1/3 transition duration-1000 "+group1}></div>
|
||||
<div className={"h-2 w-24 rounded-full bg-white blur-sm shadow shadow-white fixed top-2/3 transition duration-1000 "+group1}></div>
|
||||
<div className={"h-2 w-24 rounded-full bg-white blur-sm shadow shadow-white fixed top-1/4 transition duration-1000 "+group2}></div>
|
||||
<div className={"h-2 w-24 rounded-full bg-white blur-sm shadow shadow-white fixed top-1/2 transition duration-1000 "+group2}></div>
|
||||
<div className={"h-2 w-24 rounded-full bg-white blur-sm shadow shadow-white fixed top-3/4 transition duration-1000 "+group2}></div>
|
||||
<div className={"h-2 w-24 rounded-full bg-white blur-sm shadow shadow-white fixed top-1/3 transition duration-1000 "+group3}></div>
|
||||
<div className={"h-2 w-24 rounded-full bg-white blur-sm shadow shadow-white fixed top-2/3 transition duration-1000 "+group3}></div>
|
||||
<div className={"h-2 w-24 rounded-full bg-white blur-sm shadow shadow-white fixed top-1/4 transition duration-1000 "+group4}></div>
|
||||
<div className={"h-2 w-24 rounded-full bg-white blur-sm shadow shadow-white fixed top-1/2 transition duration-1000 "+group4}></div>
|
||||
<div className={"h-2 w-24 rounded-full bg-white blur-sm shadow shadow-white fixed top-3/4 transition duration-1000 "+group4}></div> */}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default CardMission;
|
||||
30
webapp/src/components/content/game/gamescene/index.jsx
Normal file
@@ -0,0 +1,30 @@
|
||||
import AmmoBox from "../ammobox";
|
||||
import { post } from "../../../../modules/fetcher";
|
||||
import { useEffect, useState } from "react";
|
||||
import img from "../poseimg";
|
||||
import QuestionBox from "../questionbox";
|
||||
|
||||
function GameScene({theme,id}) {
|
||||
|
||||
const [data,setData] = useState([]);
|
||||
const token = localStorage.getItem('token');
|
||||
|
||||
useEffect(() => {
|
||||
post('https://saucisson.justw.tf/api/games/create/'+id,{},token)
|
||||
.then(res => {
|
||||
console.log(res.JSON);
|
||||
setData(res.JSON);
|
||||
})
|
||||
.catch(err => console.log(err));
|
||||
},[]);
|
||||
|
||||
return(
|
||||
<main className="min-h-screen w-screen pl-44 bg-red-900 flex flex-col justify-center items-center gap-12">
|
||||
<QuestionBox question={"test"}/>
|
||||
<img src={img[id]} alt="oponent" className="mt-12 w-48"/>
|
||||
<AmmoBox game={data}/>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
|
||||
export default GameScene;
|
||||
31
webapp/src/components/content/game/mainmenu/index.jsx
Normal file
@@ -0,0 +1,31 @@
|
||||
import CardMission from "../cardmission";
|
||||
import { useEffect, useState } from "react";
|
||||
import { get } from '../../../../modules/fetcher';
|
||||
|
||||
function MainMenu() {
|
||||
|
||||
const [themes,setThemes] = useState([]);
|
||||
const token = localStorage.getItem('token');
|
||||
|
||||
useEffect(() => {
|
||||
get('https://saucisson.justw.tf/api/themes',token)
|
||||
.then(res => {
|
||||
console.log(res.JSON.themes);
|
||||
setThemes(res.JSON.themes);
|
||||
console.log(themes[0]);
|
||||
})
|
||||
.catch(err => console.log(err));
|
||||
},[]);
|
||||
|
||||
return(
|
||||
<main className="min-h-screen w-screen pl-44 bg-mainBlue flex flex-row flex-wrap gap-12 p-8">
|
||||
{themes.map((theme) => {
|
||||
return(
|
||||
<CardMission theme={theme} />
|
||||
)
|
||||
})}
|
||||
</main>
|
||||
);
|
||||
}
|
||||
|
||||
export default MainMenu;
|
||||
8
webapp/src/components/content/game/poseimg/index.jsx
Normal file
@@ -0,0 +1,8 @@
|
||||
import Pose1 from "../../../../assets/perso/perso-1/pose-1.png";
|
||||
import Pose2 from "../../../../assets/perso/perso-2/pose-1.png";
|
||||
import Pose3 from "../../../../assets/perso/perso-3/pose-1.png";
|
||||
import Pose4 from "../../../../assets/perso/perso-4/pose-1.png";
|
||||
|
||||
const img = ["",Pose1,Pose2,Pose3,Pose4];
|
||||
|
||||
export default img;
|
||||
9
webapp/src/components/content/game/questionbox/index.jsx
Normal file
@@ -0,0 +1,9 @@
|
||||
function QuestionBox({question}) {
|
||||
return(
|
||||
<div className="fixed top-4 left-32 right-4 h-42 bg-slate-300 rounded-md">
|
||||
{question}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default QuestionBox;
|
||||
31
webapp/src/components/content/home/footer/index.jsx
Normal file
@@ -0,0 +1,31 @@
|
||||
function Footer() {
|
||||
return(
|
||||
<footer className="bg-darkBlue rounded-lg shadow">
|
||||
<div className="w-full max-w-screen-xl mx-auto p-4 md:py-8">
|
||||
<div className="sm:flex sm:items-center sm:justify-between">
|
||||
<a href="https://flowbite.com/"
|
||||
className="flex items-center mb-4 sm:mb-0 space-x-3 rtl:space-x-reverse">
|
||||
<span
|
||||
className="self-center text-2xl font-semibold whitespace-nowrap text-white">Debate</span>
|
||||
</a>
|
||||
<ul className="flex flex-wrap items-center mb-6 text-sm font-medium text-white sm:mb-0">
|
||||
<li>
|
||||
<a href="#" className="hover:underline me-4 md:me-6">Home</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#" className="hover:underline me-4 md:me-6">Game</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#" className="hover:underline me-4 md:me-6">Profile</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<hr className="my-6 border-gray-200 sm:mx-auto dark:border-gray-700 lg:my-8"/>
|
||||
<span className="block text-sm text-white sm:text-center">© 2023 <a
|
||||
href="https://flowbite.com/" className="hover:underline">Debate™</a>. All Rights Reserved.</span>
|
||||
</div>
|
||||
</footer>
|
||||
);
|
||||
}
|
||||
|
||||
export default Footer;
|
||||
26
webapp/src/components/content/home/hero/index.jsx
Normal file
@@ -0,0 +1,26 @@
|
||||
// import Pose1 from "../../../../assets/home/baneer.jpg";
|
||||
import {Link} from "react-router-dom";
|
||||
function HomeHero() {
|
||||
return (
|
||||
<section class="bg-darkBlue md:mt-16 w-full">
|
||||
<div class="grid max-w-screen-xl px-4 py-8 mx-auto lg:gap-8 xl:gap-0 lg:py-16 lg:grid-cols-12">
|
||||
<div class="mr-auto place-self-center lg:col-span-7">
|
||||
<h1 class="max-w-2xl mb-4 text-4xl font-extrabold tracking-tight leading-none md:text-5xl xl:text-6xl text-white">Debate</h1>
|
||||
<p class="max-w-2xl mb-6 font-light lg:mb-8 md:text-lg lg:text-xl text-gray-400">Platform for immersive climate-themed mystery games, inspired by the thrilling narrative style of Danganronpa.</p>
|
||||
<Link to='/game' class="inline-flex items-center justify-center px-5 py-3 mr-3 text-base font-medium text-center text-white rounded-lg bg-mainBlue focus:ring-4 dark:focus:ring-blue-900">
|
||||
Get started
|
||||
<svg class="w-5 h-5 ml-2 -mr-1" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M10.293 3.293a1 1 0 011.414 0l6 6a1 1 0 010 1.414l-6 6a1 1 0 01-1.414-1.414L14.586 11H3a1 1 0 110-2h11.586l-4.293-4.293a1 1 0 010-1.414z" clip-rule="evenodd"></path></svg>
|
||||
</Link>
|
||||
<a href="#" class="inline-flex items-center justify-center px-5 py-3 text-base font-medium text-center border border-gray-300 rounded-lg hover:bg-white hover:text-black focus:ring-4 focus:ring-gray-100 text-white">
|
||||
Check my profile
|
||||
</a>
|
||||
</div>
|
||||
<div class="hidden lg:mt-0 lg:col-span-5 lg:flex">
|
||||
<img src="https://www.cci.fr/sites/g/files/mwbcuj1451/files/2023-03/energie-ecologique.png" alt="mockup" />
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
export default HomeHero;
|
||||
18
webapp/src/components/content/home/secondSection/index.jsx
Normal file
@@ -0,0 +1,18 @@
|
||||
function SecondSection() {
|
||||
return (
|
||||
<section className="bg-white dark:bg-gray-900">
|
||||
<div className="gap-16 items-center py-8 px-4 mx-auto max-w-screen-xl lg:grid lg:grid-cols-2 lg:py-16 lg:px-6">
|
||||
<div className="font-light text-gray-500 sm:text-lg dark:text-gray-400">
|
||||
<h2 className="mb-4 text-4xl tracking-tight font-extrabold text-gray-900 dark:text-white">Let us introduce you to the game</h2>
|
||||
<p className="mb-4">Welcome to Debate. On this site, you'll find a Dangaronpa-type game. The aim is simple: to find the right answer to refute your opponent's statement. Our different themes and levels on global warming and its consequences will put your knowledge to the test! Good luck</p>
|
||||
</div>
|
||||
<div className="grid grid-cols-2 gap-4 mt-8">
|
||||
<img className="w-full rounded-lg h-96 object-cover" src="https://i0.wp.com/ecolosport.fr/wp-content/uploads/2023/02/matthew-smith-Rfflri94rs8-unsplash-scaled-e1676906506139.jpeg?resize=1440%2C720&ssl=1" alt="office content 1" />
|
||||
<img className="mt-4 w-full lg:mt-10 rounded-lg h-96 object-cover" src="https://img.freepik.com/photos-premium/environnement-jour-terre-entre-mains-arbres-qui-poussent_34998-113.jpg" alt="office content 2" />
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
export default SecondSection
|
||||
8
webapp/src/components/logo/index.jsx
Normal file
@@ -0,0 +1,8 @@
|
||||
function Logo() {
|
||||
return(
|
||||
<div className="h-12 w-12 bg-red-500">
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Logo;
|
||||
15
webapp/src/components/navbarre/game/index.jsx
Normal file
@@ -0,0 +1,15 @@
|
||||
import Logo from "../../logo";
|
||||
import NavListGame from "./navlist";
|
||||
import list from "../list";
|
||||
|
||||
function NavbarreGame() {
|
||||
return (
|
||||
<nav className="h-full w-28 fixed top-0 left-0 bg-darkBlue flex flex-col justify-between items-center py-4 text-white">
|
||||
<Logo />
|
||||
<NavListGame list={list}/>
|
||||
<div>by GPO</div>
|
||||
</nav>
|
||||
);
|
||||
}
|
||||
|
||||
export default NavbarreGame;
|
||||
20
webapp/src/components/navbarre/game/navlist/index.jsx
Normal file
@@ -0,0 +1,20 @@
|
||||
// import NavLinks from "../navlinks";
|
||||
// import Link from "react";
|
||||
|
||||
function NavListGame({list}) {
|
||||
list.map((item, index) => {
|
||||
console.log("Item: " + item + " Index: " + index);
|
||||
});
|
||||
|
||||
return(
|
||||
<ul className="flex flex-col justify-between items-center gap-3">
|
||||
{list.map((item, index) => (
|
||||
<li key={index} className="h-full flex justify-center items-center">
|
||||
<a href={item[1]} className="text-align">{item[0]}</a>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
);
|
||||
}
|
||||
|
||||
export default NavListGame;
|
||||
41
webapp/src/components/navbarre/home/index.jsx
Normal file
@@ -0,0 +1,41 @@
|
||||
import NavListHome from "./navlist";
|
||||
import list from "../list";
|
||||
import LoginButton from "../login-button";
|
||||
import logout from "../../../lib/logout";
|
||||
import {useEffect} from "react";
|
||||
|
||||
function NavbarreHome() {
|
||||
return(
|
||||
<nav className="fixed top-0 left-0 h-16 w-full px-4 flex flex-row justify-between items-center text-center bg-white shadow-2xl">
|
||||
<img className="h-12 w-12 rounded-full" src="https://media.discordapp.net/attachments/1182365035700428971/1182533295850926090/image_2.png?ex=65850af4&is=657295f4&hm=eca346750009e926b9f0c76c9e6ca4e19c76f3157ad1fc19b8e7624dd8ef97b8&=&format=webp&quality=lossless&width=662&height=662"/>
|
||||
<NavListHome list={list}/>
|
||||
{localStorage.getItem('token') !== null ? (
|
||||
<div>
|
||||
<button id="dropdownDefaultButton" data-dropdown-toggle="dropdown"
|
||||
className="text-white bg-darkGreen focus:ring-4 focus:outline-none font-medium rounded-lg text-sm px-5 py-2.5 text-center inline-flex items-center"
|
||||
type="button">{localStorage.getItem('username')}
|
||||
<svg className="w-2.5 h-2.5 ms-3" aria-hidden="true"
|
||||
xmlns="http://www.w3.org/2000/svg" fill="none"
|
||||
viewBox="0 0 10 6">
|
||||
<path stroke="currentColor"
|
||||
d="m1 1 4 4 4-4"/>
|
||||
</svg>
|
||||
</button>
|
||||
<div id="dropdown"
|
||||
className="z-10 hidden bg-white divide-y divide-gray-100 rounded-lg shadow w-44">
|
||||
<ul className="py-2 text-sm text-gray-700"
|
||||
aria-labelledby="dropdownDefaultButton">
|
||||
<li>
|
||||
<a onClick={() => logout()} className="block px-4 py-2 hover:bg-gray-100 dark:hover:bg-gray-600 dark:hover:text-white">Logout</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<LoginButton/>
|
||||
)}
|
||||
</nav>
|
||||
)
|
||||
}
|
||||
|
||||
export default NavbarreHome;
|
||||
9
webapp/src/components/navbarre/home/navlinks/index.jsx
Normal file
@@ -0,0 +1,9 @@
|
||||
// import Link from 'react';
|
||||
|
||||
// function NavLinks() {
|
||||
// return(
|
||||
// <Link to="test" className="">test</Link>
|
||||
// );
|
||||
// }
|
||||
|
||||
// export default NavLinks;
|
||||
20
webapp/src/components/navbarre/home/navlist/index.jsx
Normal file
@@ -0,0 +1,20 @@
|
||||
// import NavLinks from "../navlinks";
|
||||
import Link from "react";
|
||||
|
||||
function NavListHome({list}) {
|
||||
list.map((item, index) => {
|
||||
console.log("Item: " + item + " Index: " + index);
|
||||
});
|
||||
|
||||
return(
|
||||
<ul className="flex flex-row justify-between items-center h-full gap-5">
|
||||
{list.map((item, index) => (
|
||||
<li key={index} className="h-full flex justify-center items-center">
|
||||
<a href={item[1]} className="text-align">{item[0]}</a>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
);
|
||||
}
|
||||
|
||||
export default NavListHome;
|
||||
7
webapp/src/components/navbarre/list/index.js
Normal file
@@ -0,0 +1,7 @@
|
||||
const list = [
|
||||
['Home', '/'],
|
||||
['Game', '/game'],
|
||||
['Profile', '/profile'],
|
||||
];
|
||||
|
||||
export default list;
|
||||
9
webapp/src/components/navbarre/login-button/index.jsx
Normal file
@@ -0,0 +1,9 @@
|
||||
function LoginButton() {
|
||||
return (
|
||||
<a href="/login">
|
||||
<button className="py-3 px-6 bg-darkBlue text-white rounded-md">Login</button>
|
||||
</a>
|
||||
)
|
||||
}
|
||||
|
||||
export default LoginButton;
|
||||
15
webapp/src/components/navbarre/navlist/index.jsx
Normal file
@@ -0,0 +1,15 @@
|
||||
import NavLinks from "../navlinks";
|
||||
|
||||
function NavListHome({list}) {
|
||||
return(
|
||||
<ul className="flex flex-row justify-between items-center h-full">
|
||||
{list.map((item, index) => (
|
||||
<li key={index} className="h-full">
|
||||
<NavLinks text={item[0]} link={item[1]} />
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
);
|
||||
}
|
||||
|
||||
export default NavListHome;
|
||||
17
webapp/src/index.css
Normal file
@@ -0,0 +1,17 @@
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
|
||||
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
|
||||
sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
code {
|
||||
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
|
||||
monospace;
|
||||
}
|
||||
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
50
webapp/src/index.js
Normal file
@@ -0,0 +1,50 @@
|
||||
import React, {useCallback, useEffect, useState} from 'react';
|
||||
import ReactDOM from 'react-dom/client';
|
||||
import './index.css';
|
||||
import Home from "./pages/home";
|
||||
import Login from "./pages/login";
|
||||
import Game from "./pages/game";
|
||||
import GameTheme from "./pages/gametheme";
|
||||
import {BrowserRouter, Navigate, Route, Routes} from "react-router-dom";
|
||||
import Register from "./pages/register";
|
||||
import {post} from "./modules/fetcher";
|
||||
|
||||
const root = ReactDOM.createRoot(document.getElementById('root'));
|
||||
function App() {
|
||||
const [isConnected, setIsConnected] = useState(null)
|
||||
const checkUserConnection = useCallback(async () => {
|
||||
try {
|
||||
const res = await post('https://saucisson.justw.tf/api/users/verify', {'token': localStorage.getItem('token')}, '');
|
||||
|
||||
if (res.status === 200) {
|
||||
setIsConnected(true);
|
||||
} else {
|
||||
setIsConnected(false);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Erreur lors de la vérification de la connexion:', error);
|
||||
setIsConnected(false);
|
||||
}
|
||||
}, []); // [] en dépendances pour que la fonction soit mémorisée
|
||||
|
||||
useEffect(() => {
|
||||
checkUserConnection();
|
||||
}, [checkUserConnection, window.location.href]);
|
||||
return (
|
||||
<React.StrictMode>
|
||||
<BrowserRouter>
|
||||
<Routes>
|
||||
<Route path="/" element={<Home/>}/>
|
||||
<Route path="/login" element={isConnected ? <Navigate to="/"/> : <Login />}/>
|
||||
<Route path="/register" element={<Register />}/>
|
||||
<Route path="/game" element={<Game />}/>
|
||||
<Route path="/game/:id" element={isConnected ? <GameTheme /> : <Navigate to="/game" />}/>
|
||||
</Routes>
|
||||
</BrowserRouter>
|
||||
</React.StrictMode>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
|
||||
root.render(<App />)
|
||||
7
webapp/src/lib/logout.js
Normal file
@@ -0,0 +1,7 @@
|
||||
function logout() {
|
||||
localStorage.removeItem('token');
|
||||
localStorage.removeItem('username');
|
||||
window.location.reload();
|
||||
}
|
||||
|
||||
export default logout
|
||||
0
webapp/src/lib/txt
Normal file
1
webapp/src/logo.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 841.9 595.3"><g fill="#61DAFB"><path d="M666.3 296.5c0-32.5-40.7-63.3-103.1-82.4 14.4-63.6 8-114.2-20.2-130.4-6.5-3.8-14.1-5.6-22.4-5.6v22.3c4.6 0 8.3.9 11.4 2.6 13.6 7.8 19.5 37.5 14.9 75.7-1.1 9.4-2.9 19.3-5.1 29.4-19.6-4.8-41-8.5-63.5-10.9-13.5-18.5-27.5-35.3-41.6-50 32.6-30.3 63.2-46.9 84-46.9V78c-27.5 0-63.5 19.6-99.9 53.6-36.4-33.8-72.4-53.2-99.9-53.2v22.3c20.7 0 51.4 16.5 84 46.6-14 14.7-28 31.4-41.3 49.9-22.6 2.4-44 6.1-63.6 11-2.3-10-4-19.7-5.2-29-4.7-38.2 1.1-67.9 14.6-75.8 3-1.8 6.9-2.6 11.5-2.6V78.5c-8.4 0-16 1.8-22.6 5.6-28.1 16.2-34.4 66.7-19.9 130.1-62.2 19.2-102.7 49.9-102.7 82.3 0 32.5 40.7 63.3 103.1 82.4-14.4 63.6-8 114.2 20.2 130.4 6.5 3.8 14.1 5.6 22.5 5.6 27.5 0 63.5-19.6 99.9-53.6 36.4 33.8 72.4 53.2 99.9 53.2 8.4 0 16-1.8 22.6-5.6 28.1-16.2 34.4-66.7 19.9-130.1 62-19.1 102.5-49.9 102.5-82.3zm-130.2-66.7c-3.7 12.9-8.3 26.2-13.5 39.5-4.1-8-8.4-16-13.1-24-4.6-8-9.5-15.8-14.4-23.4 14.2 2.1 27.9 4.7 41 7.9zm-45.8 106.5c-7.8 13.5-15.8 26.3-24.1 38.2-14.9 1.3-30 2-45.2 2-15.1 0-30.2-.7-45-1.9-8.3-11.9-16.4-24.6-24.2-38-7.6-13.1-14.5-26.4-20.8-39.8 6.2-13.4 13.2-26.8 20.7-39.9 7.8-13.5 15.8-26.3 24.1-38.2 14.9-1.3 30-2 45.2-2 15.1 0 30.2.7 45 1.9 8.3 11.9 16.4 24.6 24.2 38 7.6 13.1 14.5 26.4 20.8 39.8-6.3 13.4-13.2 26.8-20.7 39.9zm32.3-13c5.4 13.4 10 26.8 13.8 39.8-13.1 3.2-26.9 5.9-41.2 8 4.9-7.7 9.8-15.6 14.4-23.7 4.6-8 8.9-16.1 13-24.1zM421.2 430c-9.3-9.6-18.6-20.3-27.8-32 9 .4 18.2.7 27.5.7 9.4 0 18.7-.2 27.8-.7-9 11.7-18.3 22.4-27.5 32zm-74.4-58.9c-14.2-2.1-27.9-4.7-41-7.9 3.7-12.9 8.3-26.2 13.5-39.5 4.1 8 8.4 16 13.1 24 4.7 8 9.5 15.8 14.4 23.4zM420.7 163c9.3 9.6 18.6 20.3 27.8 32-9-.4-18.2-.7-27.5-.7-9.4 0-18.7.2-27.8.7 9-11.7 18.3-22.4 27.5-32zm-74 58.9c-4.9 7.7-9.8 15.6-14.4 23.7-4.6 8-8.9 16-13 24-5.4-13.4-10-26.8-13.8-39.8 13.1-3.1 26.9-5.8 41.2-7.9zm-90.5 125.2c-35.4-15.1-58.3-34.9-58.3-50.6 0-15.7 22.9-35.6 58.3-50.6 8.6-3.7 18-7 27.7-10.1 5.7 19.6 13.2 40 22.5 60.9-9.2 20.8-16.6 41.1-22.2 60.6-9.9-3.1-19.3-6.5-28-10.2zM310 490c-13.6-7.8-19.5-37.5-14.9-75.7 1.1-9.4 2.9-19.3 5.1-29.4 19.6 4.8 41 8.5 63.5 10.9 13.5 18.5 27.5 35.3 41.6 50-32.6 30.3-63.2 46.9-84 46.9-4.5-.1-8.3-1-11.3-2.7zm237.2-76.2c4.7 38.2-1.1 67.9-14.6 75.8-3 1.8-6.9 2.6-11.5 2.6-20.7 0-51.4-16.5-84-46.6 14-14.7 28-31.4 41.3-49.9 22.6-2.4 44-6.1 63.6-11 2.3 10.1 4.1 19.8 5.2 29.1zm38.5-66.7c-8.6 3.7-18 7-27.7 10.1-5.7-19.6-13.2-40-22.5-60.9 9.2-20.8 16.6-41.1 22.2-60.6 9.9 3.1 19.3 6.5 28.1 10.2 35.4 15.1 58.3 34.9 58.3 50.6-.1 15.7-23 35.6-58.4 50.6zM320.8 78.4z"/><circle cx="420.9" cy="296.5" r="45.7"/><path d="M520.5 78.1z"/></g></svg>
|
||||
|
After Width: | Height: | Size: 2.6 KiB |
69
webapp/src/modules/fetcher.js
Normal file
@@ -0,0 +1,69 @@
|
||||
|
||||
async function get(url, token) {
|
||||
const options = {
|
||||
method: 'GET',
|
||||
headers: { 'Content-Type': 'application/json', authorization: `${token}` },
|
||||
};
|
||||
|
||||
return await fetch(url, options)
|
||||
.then(res => res.json())
|
||||
.then(json => {
|
||||
return json;
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
async function post(url, body, token) {
|
||||
const options = {
|
||||
method: 'POST',
|
||||
mode: 'cors',
|
||||
headers: { 'Content-Type': 'application/json', authorization: `${token}` },
|
||||
body: JSON.stringify(body),
|
||||
};
|
||||
|
||||
return await fetch(url, options)
|
||||
.then(res => res.json())
|
||||
.then(json => {
|
||||
return json;
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
async function patch(url, body, token) {
|
||||
const options = {
|
||||
method: 'PATCH',
|
||||
mode: 'cors',
|
||||
headers: { 'Content-Type': 'application/json', authorization: `${token}` },
|
||||
body: JSON.stringify(body),
|
||||
};
|
||||
|
||||
return await fetch(url, options)
|
||||
.then(res => res.json())
|
||||
.then(json => {
|
||||
return json;
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
async function put(url, body, token) {
|
||||
const options = {
|
||||
method: 'PUT',
|
||||
mode: 'cors',
|
||||
headers: { 'Content-Type': 'application/json', authorization: `${token}` },
|
||||
body: JSON.stringify(body),
|
||||
};
|
||||
|
||||
return await fetch(url, options)
|
||||
.then(res => res.json())
|
||||
.then(json => {
|
||||
return json;
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
export {
|
||||
get,
|
||||
post,
|
||||
patch,
|
||||
put,
|
||||
};
|
||||
13
webapp/src/pages/game/index.jsx
Normal file
@@ -0,0 +1,13 @@
|
||||
import NavbarreGame from "../../components/navbarre/game";
|
||||
import MainMenu from "../../components/content/game/mainmenu";
|
||||
|
||||
function Game () {
|
||||
return(
|
||||
<div className="w-full h-full">
|
||||
<NavbarreGame />
|
||||
<MainMenu />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default Game;
|
||||
29
webapp/src/pages/gametheme/index.jsx
Normal file
@@ -0,0 +1,29 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { useParams } from "react-router-dom";
|
||||
import { get } from "../../modules/fetcher";
|
||||
import NavbarreGame from "../../components/navbarre/game";
|
||||
import GameScene from "../../components/content/game/gamescene";
|
||||
|
||||
function GameTheme() {
|
||||
|
||||
const {id} = useParams();
|
||||
const [theme,setTheme] = useState([]);
|
||||
const token = localStorage.getItem('token');
|
||||
|
||||
useEffect(() => {
|
||||
get('https://saucisson.justw.tf/api/themes/'+id, token)
|
||||
.then(res => {
|
||||
setTheme(res.JSON.themes);
|
||||
})
|
||||
.catch(err => console.log(err));
|
||||
},[]);
|
||||
|
||||
return(
|
||||
<div className="h-full w-full">
|
||||
<NavbarreGame />
|
||||
<GameScene theme={theme} id={id}/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default GameTheme;
|
||||
17
webapp/src/pages/home/index.jsx
Normal file
@@ -0,0 +1,17 @@
|
||||
import NavbarreHome from "../../components/navbarre/home";
|
||||
import HomeHero from "../../components/content/home/hero";
|
||||
import Footer from "../../components/content/home/footer";
|
||||
import SecondSection from "../../components/content/home/secondSection";
|
||||
|
||||
function Home() {
|
||||
return (
|
||||
<div>
|
||||
<NavbarreHome/>
|
||||
<HomeHero />
|
||||
<SecondSection />
|
||||
<Footer />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Home;
|
||||
67
webapp/src/pages/login/index.jsx
Normal file
@@ -0,0 +1,67 @@
|
||||
import {Link, redirect, useNavigate} from "react-router-dom";
|
||||
import {useState} from "react";
|
||||
import {get, post} from '../../modules/fetcher';
|
||||
function Login() {
|
||||
const navigate = useNavigate();
|
||||
const loginUser = async (e) => {
|
||||
e.preventDefault()
|
||||
post('https://saucisson.justw.tf/api/users/login', {username, password}, '')
|
||||
.then(async res => {
|
||||
if (res.status === 200) {
|
||||
localStorage.setItem('username', username)
|
||||
localStorage.setItem('token', res.JSON.token)
|
||||
navigate('/')
|
||||
} else {
|
||||
setError(res.message)
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
const [error, setError] = useState(null)
|
||||
const [username, setUsername] = useState(null)
|
||||
const [password, setPassword] = useState(null)
|
||||
return (
|
||||
<section className="bg-gradient-to-r from-mainBlue to-darkGreen">
|
||||
<div className="flex flex-col items-center justify-center px-6 py-8 mx-auto md:h-screen lg:py-0">
|
||||
<div
|
||||
className="w-full bg-white rounded-lg shadow md:mt-0 sm:max-w-md xl:p-0">
|
||||
<div className="p-6 space-y-4 md:space-y-6 sm:p-8">
|
||||
<h1 className="text-xl font-bold leading-tight tracking-tight text-gray-900 md:text-2xl">
|
||||
Login to your account
|
||||
</h1>
|
||||
<form onSubmit={loginUser} className="space-y-4 md:space-y-6">
|
||||
{error && (
|
||||
<div>
|
||||
<p className="text-red-500 text-md">{error}</p>
|
||||
</div>
|
||||
)}
|
||||
<div>
|
||||
<label htmlFor="email"
|
||||
className="block mb-2 text-sm font-medium text-gray-900">Your
|
||||
username</label>
|
||||
<input type="text" name="email" id="email"
|
||||
className="bg-gray-50 border border-gray-300 text-gray-900 sm:text-sm rounded-lg focus:ring-blue-600 focus:border-blue-600 block w-full p-2.5"
|
||||
placeholder="John Doe" required="" onChange={e => {setUsername(e.target.value)}}/>
|
||||
</div>
|
||||
<div>
|
||||
<label htmlFor="password"
|
||||
className="block mb-2 text-sm font-medium text-gray-900">Password</label>
|
||||
<input type="password" name="password" id="password" placeholder="••••••••"
|
||||
className="bg-gray-50 border border-gray-300 text-gray-900 sm:text-sm rounded-lg focus:ring-blue-600 focus:border-blue-600 block w-full p-2.5"
|
||||
required onChange={e => {setPassword(e.target.value)}}/>
|
||||
</div>
|
||||
<button className="w-full text-white bg-darkGreen focus:ring-4 focus:outline-none font-medium rounded-lg text-sm px-5 py-2.5 text-center">Login</button>
|
||||
<p className="text-sm font-light text-gray-500">
|
||||
Don’t have an account yet? <Link to={'/register'}
|
||||
className="font-medium text-darkGreen hover:underline">Sign
|
||||
up</Link>
|
||||
</p>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
export default Login
|
||||
81
webapp/src/pages/register/index.jsx
Normal file
@@ -0,0 +1,81 @@
|
||||
import {Link, redirect, useNavigate} from "react-router-dom";
|
||||
import {post} from "../../modules/fetcher";
|
||||
import {useState} from "react";
|
||||
|
||||
|
||||
function Register() {
|
||||
const navigate = useNavigate();
|
||||
const registerUser = async (e) => {
|
||||
e.preventDefault()
|
||||
if(password === confirmPassword) {
|
||||
post('https://saucisson.justw.tf/api/users/register', {username, password}, '')
|
||||
.then(async res => {
|
||||
if (res.status === 200) {
|
||||
localStorage.setItem('username', username)
|
||||
localStorage.setItem('token', res.JSON.token)
|
||||
navigate('/')
|
||||
} else {
|
||||
setError(res.message)
|
||||
}
|
||||
})
|
||||
} else {
|
||||
setError("Password doesn't match")
|
||||
}
|
||||
|
||||
}
|
||||
const [error, setError] = useState(null)
|
||||
const [username, setUsername] = useState(null)
|
||||
const [password, setPassword] = useState(null)
|
||||
const [confirmPassword, setconfirmPassword] = useState(null)
|
||||
return (
|
||||
<section className="bg-gradient-to-r from-mainBlue to-darkGreen">
|
||||
<div className="flex flex-col items-center justify-center px-6 py-8 mx-auto md:h-screen lg:py-0">
|
||||
<div
|
||||
className="w-full bg-white rounded-lg shadow md:mt-0 sm:max-w-md xl:p-0">
|
||||
<div className="p-6 space-y-4 md:space-y-6 sm:p-8">
|
||||
<h1 className="text-xl font-bold leading-tight tracking-tight text-gray-900 md:text-2xl">
|
||||
Create an account
|
||||
</h1>
|
||||
<form className="space-y-4 md:space-y-6" onSubmit={registerUser}>
|
||||
{error && (
|
||||
<div>
|
||||
<p className="text-red-500 text-md">{error}</p>
|
||||
</div>
|
||||
)}
|
||||
<div>
|
||||
<label htmlFor="email"
|
||||
className="block mb-2 text-sm font-medium text-gray-900">Your
|
||||
username</label>
|
||||
<input type="text" name="email" id="email" className="bg-gray-50 border border-gray-300 text-gray-900 sm:text-sm rounded-lg focus:ring-blue-600 focus:border-blue-600 block w-full p-2.5" placeholder="John Doe" required="" onChange={e => {setUsername(e.target.value)}}/>
|
||||
</div>
|
||||
<div>
|
||||
<label htmlFor="password"
|
||||
className="block mb-2 text-sm font-medium text-gray-900">Password</label>
|
||||
<input type="password" name="password" id="password" placeholder="••••••••"
|
||||
className="bg-gray-50 border border-gray-300 text-gray-900 sm:text-sm rounded-lg focus:ring-blue-600 focus:border-blue-600 block w-full p-2.5"
|
||||
required="" onChange={e => {setPassword(e.target.value)}}/>
|
||||
</div>
|
||||
<div>
|
||||
<label htmlFor="password"
|
||||
className="block mb-2 text-sm font-medium text-gray-900">Password confirmation</label>
|
||||
<input type="password" name="confirm_password" id="confirm_password" placeholder="••••••••"
|
||||
className="bg-gray-50 border border-gray-300 text-gray-900 sm:text-sm rounded-lg focus:ring-blue-600 focus:border-blue-600 block w-full p-2.5"
|
||||
required="" onChange={e => {setconfirmPassword(e.target.value)}}/>
|
||||
</div>
|
||||
<button type="submit"
|
||||
className="w-full text-white bg-darkGreen focus:ring-4 focus:outline-none font-medium rounded-lg text-sm px-5 py-2.5 text-center">Register
|
||||
</button>
|
||||
<p className="text-sm font-light text-gray-500">
|
||||
Don’t have an account yet? <Link to={'/login'}
|
||||
className="font-medium text-darkGreen hover:underline">Sign
|
||||
up</Link>
|
||||
</p>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
export default Register
|
||||
0
webapp/src/pages/txt
Normal file
26
webapp/tailwind.config.js
Normal file
@@ -0,0 +1,26 @@
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
module.exports = {
|
||||
content: [
|
||||
"./src/**/*.{js,jsx,ts,tsx}",
|
||||
"./node_modules/flowbite/**/*.js"
|
||||
],
|
||||
theme: {
|
||||
extend: {
|
||||
spacing: {
|
||||
'dfull' : '-100vw',
|
||||
'd24': '-24px',
|
||||
},
|
||||
colors: {
|
||||
'darkGreen': '#4dab7f',
|
||||
'mainBlue': '#047385',
|
||||
'mainOrange': '#d76d1e',
|
||||
'darkBlue': '#043743',
|
||||
|
||||
}
|
||||
},
|
||||
},
|
||||
plugins: [
|
||||
require('flowbite/plugin')
|
||||
],
|
||||
}
|
||||
|
||||