22 Commits

Author SHA1 Message Date
98290b41a1 Merge branch 'GringoElPepito' of https://git.justw.tf/Lightemerald/nuitdelinfo2023 into GringoElPepito 2023-12-08 07:21:10 +01:00
3660e1cd7c Following 2023-12-08 07:21:06 +01:00
8c184944f3 styling game page 2023-12-08 07:20:06 +01:00
49bc4f64fb Starting Quizz 2023-12-08 05:43:14 +01:00
36f5e7dfa6 Merge remote-tracking branch 'origin/GringoElPepito' into GringoElPepito 2023-12-08 05:23:06 +01:00
5669bb6be3 styling the home page 2023-12-08 05:22:33 +01:00
c49faca15b Following game 2023-12-08 03:52:15 +01:00
08a8819d25 Merge branch 'GringoElPepito' of https://git.justw.tf/Lightemerald/nuitdelinfo2023 into GringoElPepito 2023-12-08 02:37:22 +01:00
d2a8189a6b Animation transition game 2023-12-08 02:37:19 +01:00
9ae801fea9 fix username problem 2023-12-08 01:57:36 +01:00
9bcded7f9a adding logout and show the username 2023-12-08 00:50:36 +01:00
80f786ff61 adding register and login pages 2023-12-08 00:08:21 +01:00
ddfdcd613f Adding button redirection
Following game
2023-12-08 00:03:39 +01:00
95eec4f93e Starting Game MainMenu 2023-12-07 22:57:26 +01:00
590b6773f8 Starting home page 2023-12-07 21:40:34 +01:00
bca2ca59e6 Merge branch 'GringoElPepito' of https://git.justw.tf/Lightemerald/nuitdelinfo2023 into GringoElPepito 2023-12-07 20:09:54 +01:00
9b6403f9a5 Splitting navbarre component 2023-12-07 20:09:48 +01:00
a441c3e78b adding Router and starting the login page 2023-12-07 20:09:33 +01:00
6a91e0739a Starting navabarre 2023-12-07 19:42:51 +01:00
a9ed1691f2 adding tailwind & flowbite integration 2023-12-07 19:04:53 +01:00
c4ce652ab3 Structure react app 2023-12-07 18:45:39 +01:00
5a5600c375 Start Webapp 2023-12-07 18:34:57 +01:00
51 changed files with 19034 additions and 0 deletions

11
api/package.json Normal file
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

43
webapp/package.json Normal file
View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

44
webapp/public/index.html Normal file
View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

BIN
webapp/public/logo512.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

View 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
View File

@@ -0,0 +1,3 @@
# https://www.robotstxt.org/robotstxt.html
User-agent: *
Disallow:

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 156 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 93 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

0
webapp/src/assets/txt Normal file
View File

View 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;

View 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;

View 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;

View 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;

View 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;

View 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;

View 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;

View 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;

View 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;

View 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

View File

@@ -0,0 +1,8 @@
function Logo() {
return(
<div className="h-12 w-12 bg-red-500">
</div>
)
}
export default Logo;

View 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;

View 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;

View 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;

View File

@@ -0,0 +1,9 @@
// import Link from 'react';
// function NavLinks() {
// return(
// <Link to="test" className="">test</Link>
// );
// }
// export default NavLinks;

View 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;

View File

@@ -0,0 +1,7 @@
const list = [
['Home', '/'],
['Game', '/game'],
['Profile', '/profile'],
];
export default list;

View 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;

View 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
View 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
View 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
View 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
View File

1
webapp/src/logo.svg Normal file
View 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

View 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,
};

View 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;

View 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;

View 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;

View 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">
Dont 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

View 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">
Dont 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
View File

26
webapp/tailwind.config.js Normal file
View 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')
],
}