From c9ad1a372a5e61c0f4c07cb5851044887ca8bcbe Mon Sep 17 00:00:00 2001 From: AntoninoP Date: Sun, 22 Sep 2024 18:50:07 +0200 Subject: [PATCH 01/17] modpack api calls --- src/pages/CreateServer/modpack/modpack.jsx | 42 +++++++++++++++++++--- src/service/api.tsx | 16 +++++++++ 2 files changed, 53 insertions(+), 5 deletions(-) diff --git a/src/pages/CreateServer/modpack/modpack.jsx b/src/pages/CreateServer/modpack/modpack.jsx index 57e8345..485816b 100644 --- a/src/pages/CreateServer/modpack/modpack.jsx +++ b/src/pages/CreateServer/modpack/modpack.jsx @@ -1,13 +1,39 @@ +import React, { useEffect, useState } from 'react'; import styles from './modpack.module.scss'; import PropTypes from "prop-types"; import Navbar from '../../../components/navbar/Navbar'; import { useNavigate } from 'react-router-dom'; - +import serviiApi from '../../../service/api'; const Modpack = ({ user }) => { const navigate = useNavigate(); + const [modpacks, setModpacks] = useState([]); + const [error, setError] = useState(''); + + useEffect(() => { + const fetchModpacks = async () => { + if (!user) { + setError("Vous devez être connecté pour voir les modpacks."); + return; + } + + try { + const response = await serviiApi.fetchModpacks(); + console.log(response); + if (response.return_code === 200) { + setModpacks(response.message); + } else { + setError(response.message); + } + } catch (error) { + console.error(error); + setError("Erreur lors du chargement des modpacks."); + } + }; + + fetchModpacks(); + }, [user]); - return (
{ backButtonText="Retour" onBackClick={() => navigate('/CreateServer')} /> -
-

Prochainement disponible

-
+
+ {error ?

{error}

: ( +
    + {modpacks.map((modpack, index) => ( +
  • {modpack.name}
  • + ))} +
+ )} +
); }; diff --git a/src/service/api.tsx b/src/service/api.tsx index 810def7..0786875 100644 --- a/src/service/api.tsx +++ b/src/service/api.tsx @@ -88,7 +88,23 @@ function toast_status(status: number, message: string) { }); } + + class serviiApi { + + public static async fetchModpacks(): Promise { + try { + const response = await fetch(`${apiUrl}/modpacks`, { + method: 'GET', + }); + const json = await response.json(); + return { return_code: response.status, message: json }; + } catch (error) { + toast_status(666, "Couldn't fetch modpacks"); + console.error(error); + return { return_code: 503, message: "Couldn't fetch modpacks" }; + } + } private static async call(endpoint: serviiRequest, body: T, token: string): Promise { const unreachable: string = "Couldn't find an available API"; try { From 788409120a1e48f70fc6c1ee2af6bcb1c367b0b5 Mon Sep 17 00:00:00 2001 From: AntoninoP Date: Sun, 22 Sep 2024 23:27:44 +0200 Subject: [PATCH 02/17] + styles pour la pages des modpacks et fix css --- src/App.jsx | 2 +- src/components/CreateServer/CreateServer.jsx | 163 ---------- .../CreateServer/CreateServer.module.scss | 296 ------------------ .../serverCards/DefaultServerCard.module.scss | 1 + src/main.css | 2 +- .../CreateServer/{Javapick => java}/java.jsx | 2 +- .../{Javapick => java}/java.module.scss | 0 src/pages/CreateServer/modpack/modpack.jsx | 77 ++++- .../CreateServer/modpack/modpack.module.scss | 158 +++++++++- src/service/api.test.js | 0 10 files changed, 220 insertions(+), 481 deletions(-) delete mode 100644 src/components/CreateServer/CreateServer.jsx delete mode 100644 src/components/CreateServer/CreateServer.module.scss rename src/pages/CreateServer/{Javapick => java}/java.jsx (98%) rename src/pages/CreateServer/{Javapick => java}/java.module.scss (100%) delete mode 100644 src/service/api.test.js diff --git a/src/App.jsx b/src/App.jsx index 5d92b87..57d30ee 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -9,7 +9,7 @@ import Loading from './pages/Loading/loading'; const LoginPage = lazy(() => import('./pages/LoginPage/LoginPage')); const ServerDetails = lazy(() => import('./pages/ServerDetails/ServerDetails')); const CreatePage = lazy(() => import('./pages/CreateServer/CreateServer')); -const Javapick = lazy(() => import('./pages/CreateServer/Javapick/java')); +const Javapick = lazy(() => import('./pages/CreateServer/java/java')); const Modpack = lazy(() => import('./pages/CreateServer/modpack/modpack')); const Bedrock = lazy(() => import('./pages/CreateServer/bedrock/bedrock')); const DashboardPage = lazy(() => import('./pages/DashboardPage/DashboardPage')); diff --git a/src/components/CreateServer/CreateServer.jsx b/src/components/CreateServer/CreateServer.jsx deleted file mode 100644 index 8bfe9c5..0000000 --- a/src/components/CreateServer/CreateServer.jsx +++ /dev/null @@ -1,163 +0,0 @@ -import { useState, useEffect } from 'react'; -import styles from './CreateServer.module.scss'; -import { getUserSubdomain } from "../../service/firebase"; -import serviiApi from "../../service/api.tsx"; -import PropTypes from "prop-types"; -import { GoTag, GoCheck } from "react-icons/go"; - -const versions = { - paper: [ - "1.16.2", - "1.14.4", - "1.13.2", - "1.16.1", - "1.18.2", - "1.19", - "1.20.4", - "1.19.3", - "1.19.4", - "1.19.2", - "1.20.1", - "1.21.1", - "1.20.5", - "1.20.6", - "1.21", - "1.20.2", - "1.20", - "1.9.4", - "1.10.2", - "1.11.2", - "1.12.2", - "1.13.1", - "1.13.2", - "1.14.1", - "1.14.2", - "1.14.3", - "1.14.4", - "1.15.1", - "1.15.2", - "1.16.1", - "1.16.2", - "1.16.3", - "1.16.4", - "1.16.5", - "1.17.1", - "1.18.1" - ].sort((a, b) => b.localeCompare(a, undefined, { numeric: true })) -}; - -const CreateServer = ({ user, onCreateServer, onSubdomainUpdate, onCancel, noServers }) => { - const [subdomain, setSubdomain] = useState(null); - const [subdomainInput, setSubdomainInput] = useState(''); - const [serverName, setServerName] = useState(''); - const [serverVersion, setServerVersion] = useState('1.21.1'); - - const loadSubdomain = async () => { - try { - if (user && user.uid) { - const userSubdomain = await getUserSubdomain(user.uid); - setSubdomain(userSubdomain || ''); - onSubdomainUpdate(userSubdomain || ''); - } else { - console.error('User or user.uid is undefined'); - } - } catch (error) { - console.error('Error fetching subdomain:', error); - } - }; - - useEffect(() => { - loadSubdomain().then(r => r); - }, - [user]); - - const handleSaveSubdomain = async () => { - try { - await serviiApi.setSubdomain(subdomainInput); - setSubdomain(subdomainInput); - onSubdomainUpdate(subdomainInput); - } catch (error) { - console.error('Error setting subdomain:', error); - } - }; - - const handleCreateServer = async () => { - try { - const frameworkToSend = 'paper'; - await onCreateServer(serverName, serverVersion, frameworkToSend); - } catch (error) { - console.error('Error creating server:', error); - } - }; - - const VersionChoice = (version) => { - return () => { - setServerVersion(version); - }; - }; - - const validateInput = (input) => { - return input.replace(/[^a-zA-Z]/g, ''); - }; - - return ( -
- {noServers ? ( -
-
Bonjour
-
Aucun serveur
- -
- ) : ( - subdomain === null ? ( -
Loading...
- ) : ( - subdomain === '' ? ( -
-
Ecrivez votre sous domaine
-
- Le sous-domaine est le nom sous lequel vos amis et vous rejoignez le serveur, un peu comme une adresse. Choisissez-le bien, car il nest pas facilement modifiable ! -
- setSubdomainInput(validateInput(e.target.value))} - placeholder='Nom du sous domaine' - /> - - -
- ) : ( -
-
Création du serveur
-
Comment voulez vous jouer ?
-
dd
-
- ) - ) - )} -
- ); -}; - -CreateServer.propTypes = { - user: PropTypes.oneOfType([ - PropTypes.shape({ - uid: PropTypes.string.isRequired, - displayName: PropTypes.string, - email: PropTypes.string, - photoURL: PropTypes.string, - }), - ]), - onCreateServer: PropTypes.func.isRequired, - onSubdomainUpdate: PropTypes.func.isRequired, - onCancel: PropTypes.func, - noServers: PropTypes.any -}; - -export default CreateServer; diff --git a/src/components/CreateServer/CreateServer.module.scss b/src/components/CreateServer/CreateServer.module.scss deleted file mode 100644 index 067dce3..0000000 --- a/src/components/CreateServer/CreateServer.module.scss +++ /dev/null @@ -1,296 +0,0 @@ -.mainCardCommon { - display: flex; - justify-content: start; - align-items: start; - flex-direction: column; - background-color: #1D1836; - border-radius: 1.5rem; -} - -.title { - font-size: 2.5rem; - font-weight: 700; - margin-bottom: 1.5rem; -} - -.subtitle { - font-size: 1.2rem; - font-weight: 300; - text-align: center; - margin-bottom: 2rem; -} - -.input, .select { - width: 60rem; - padding: 1rem; - background-color: #090325; - border: none; - border-radius: 0.5rem; - color: white; - font-size: 1rem; -} - -.btnSubCreate, .btnServCreate { - margin-top: 2rem; - padding: 1rem 2rem; - background-color: #090325; - color: white; - border: none; - border-radius: 0.5rem; - cursor: pointer; - font-size: 1rem; - transition: background-color 0.3s, transform 0.3s; - margin-right: 1rem; -} - -.containerNoserveur { - display: flex; - justify-content: center; - align-items: center; -} - -.mainCardNoserveur { - background-color: #1D1836; - padding: 3rem; - margin-top: 5rem; - border-radius: 1rem; -} - -.nsTitle { - font-size: 3rem; - color: #F2F2F2; - font-weight: 900; -} - -.nsSubTitle { - font-size: 1.8rem; - font-weight: 300; -} - -.btnnoServCreate { - width: 40rem; - height: 5rem; - margin-top: 5rem; - font-size: 2rem; - font-weight: 900; - background-color: #090325; - border-radius: 1rem; - color: white; - border: none; - cursor: pointer; -} - -.btnServCreate { - width: 15rem; - height: 3.5rem; - margin-top: 2.5rem; - font-size: 1.5rem; - font-weight: 900; - border-radius: 1rem; - background-color: #090325; -} - -.GamesChoice { - margin-top: var(--navbar-height); - width: 50rem; - height: 10rem; - background-color: red; - display: flex; - justify-content: center; - align-items: center; - flex-direction: column; -} - -.mainCardSubdomain { - background-color: #1D1836; - padding: 3rem; - margin-top: 5rem; - border-radius: 1rem; - width: 55rem; -} - -.subtitle { - font-size: 1.2rem; - font-weight: 600; - text-align: start; - margin-bottom: 1.5rem; - margin-top: 2rem; - -} - -.inputsubdomain { - width: 50rem; - padding: 1rem; - margin-top: 1rem; - background-color: #090325; - border: none; - border-radius: 0.5rem; - color: white; - font-size: 1rem; -} - - -.carreContainer { - display: flex; - justify-content: space-around; - align-items: center; - width: 100%; - height: 6rem; -} - -.carreWrapper { - display: flex; - flex-direction: column; - align-items: center; -} - -.carre { - width: 5rem; - height: 5rem; - object-fit: cover; - display: block; - cursor: pointer; - transition: transform 0.2s; -} - -.carre:hover { - transform: scale(1.1); -} - -.selected { - border: 2px solid #fff; - transform: scale(1.2); -} - -.adviced { - border: 2px solid violet; -} - -.recommendationLabel { - margin-top: 0.5rem; - font-size: 1rem; - color: violet; - font-weight: 700; - text-align: center; -} - -.VersionContainer { - display: grid; - grid-template-columns: repeat(6, 1fr); - justify-items: center; - align-items: center; - width: 100%; -} - -.VersionCard{ - height: 4rem; - width: 8rem; - background-color: #090325; - display: flex; - align-items: center; - justify-content: center ; - font-size: 1.5rem; - margin-bottom: 1rem; - border-radius: .3rem; -} - -.VersionCard svg { - margin-right: 0.5rem; -} - -.VersionCard:hover{ - background-color: #09032579; - cursor: pointer; -} - -.selectedVersion { - border: .15rem solid #fff; - color: #fff; -} - - - - -@media (max-width: 800px) { - - .VersionContainer { - grid-template-columns: repeat(3, 1fr); - } - - .buttonContainer{ - display: flex; - justify-content: center; - align-items: center; - flex-direction: row; - } - - .title { - font-size: 1.8rem; - margin-bottom: 1rem; - } - - .subtitle { - font-size: 1rem; - margin-bottom: 1.5rem; - } - - .input, .select { - width: 100%; - padding: 0.75rem; - font-size: 0.875rem; - } - - .btnSubCreate, .btnServCreate { - margin-top: 2rem; - padding: 1rem 2.5rem; - font-size: 1rem; - margin-right: 2rem; - display: flex; - justify-content: center; - align-items: center; - } - - .mainCardNoserveur { - padding: 2rem; - margin-top: 3rem; - } - - .nsTitle { - font-size: 2rem; - } - - .nsSubTitle { - font-size: 1.2rem; - } - - .btnnoServCreate { - width: 100%; - height: 3.5rem; - font-size: 1.5rem; - margin-top: 3rem; - } - - .btnServCreate { - width: 100%; - height: 2.5rem; - font-size: 1.25rem; - } - - .mainCardCreateServ { - margin-top: 3rem; - padding: 1.5rem; - width: 35rem; - } - - .mainCardSubdomain { - padding: 2rem; - margin-top: 3rem; - width: 100%; - } - - .inputsubdomain { - width: 100%; - padding: 0.75rem; - font-size: 0.875rem; - } -} diff --git a/src/components/serverCards/DefaultServerCard.module.scss b/src/components/serverCards/DefaultServerCard.module.scss index 8f6ef1b..1f45f2e 100644 --- a/src/components/serverCards/DefaultServerCard.module.scss +++ b/src/components/serverCards/DefaultServerCard.module.scss @@ -105,6 +105,7 @@ .startStopButton { padding: 1rem 1.5rem; border-radius: 0.5rem; + font-size: 1rem; border: none; color: white; cursor: pointer; diff --git a/src/main.css b/src/main.css index d1b4cd9..86da27f 100644 --- a/src/main.css +++ b/src/main.css @@ -54,7 +54,7 @@ body { @media (min-width: 2000px) { html { - font-size: 15px; + font-size: 20px; } } diff --git a/src/pages/CreateServer/Javapick/java.jsx b/src/pages/CreateServer/java/java.jsx similarity index 98% rename from src/pages/CreateServer/Javapick/java.jsx rename to src/pages/CreateServer/java/java.jsx index 52dbc07..4b8e1fe 100644 --- a/src/pages/CreateServer/Javapick/java.jsx +++ b/src/pages/CreateServer/java/java.jsx @@ -3,7 +3,7 @@ import { GoTag, GoCheck } from "react-icons/go"; import { useNavigate } from 'react-router-dom'; import styles from './java.module.scss'; import PropTypes from "prop-types"; -import Navbar from '../../../components/navbar/Navbar'; +import Navbar from '../../../components/navbar/Navbar.jsx'; import serviiApi from "../../../service/api.tsx"; const Javapick = ({ user }) => { diff --git a/src/pages/CreateServer/Javapick/java.module.scss b/src/pages/CreateServer/java/java.module.scss similarity index 100% rename from src/pages/CreateServer/Javapick/java.module.scss rename to src/pages/CreateServer/java/java.module.scss diff --git a/src/pages/CreateServer/modpack/modpack.jsx b/src/pages/CreateServer/modpack/modpack.jsx index 485816b..cbdea89 100644 --- a/src/pages/CreateServer/modpack/modpack.jsx +++ b/src/pages/CreateServer/modpack/modpack.jsx @@ -1,27 +1,25 @@ -import React, { useEffect, useState } from 'react'; +import { useEffect, useState } from 'react'; import styles from './modpack.module.scss'; -import PropTypes from "prop-types"; +import PropTypes from 'prop-types'; import Navbar from '../../../components/navbar/Navbar'; -import { useNavigate } from 'react-router-dom'; +import { useNavigate } from 'react-router-dom'; import serviiApi from '../../../service/api'; const Modpack = ({ user }) => { const navigate = useNavigate(); const [modpacks, setModpacks] = useState([]); + const [selectedModpackIndex, setSelectedModpackIndex] = useState(null); const [error, setError] = useState(''); + const [serverShortName, setServerShortName] = useState(''); useEffect(() => { const fetchModpacks = async () => { - if (!user) { - setError("Vous devez être connecté pour voir les modpacks."); - return; - } - try { const response = await serviiApi.fetchModpacks(); - console.log(response); if (response.return_code === 200) { - setModpacks(response.message); + const fetchedModpacks = response.message; + + setModpacks(fetchedModpacks); } else { setError(response.message); } @@ -34,6 +32,28 @@ const Modpack = ({ user }) => { fetchModpacks(); }, [user]); + const handleModpackClick = (index) => { + setSelectedModpackIndex(index); + }; + + + const handleCancel = () => { + setSelectedModpackIndex(null); + console.log("dffdff"); + }; + + + const handleCreate = async () => { + try { + const framework = selectedModpackIndex.framework; + await serviiApi.serverCreate(selectedModpackIndex.short_name, selectedModpackIndex.short_name, framework); + console.log(selectedModpackIndex.short_name, selectedModpackIndex.short_name, framework); + navigate('/Dashboard'); + } catch (error) { + console.error('Error creating server:', error); + } + }; + return (
{ backButtonText="Retour" onBackClick={() => navigate('/CreateServer')} /> -
+
{error ?

{error}

: ( -
    - {modpacks.map((modpack, index) => ( -
  • {modpack.name}
  • - ))} -
+ modpacks.map((modpack, index) => ( +
handleModpackClick(index)} + > + {modpack.name} +

{modpack.name}

+

{modpack.description}

+
+ Aventure + Combat + PvE + {modpack.framework} +
+
+ Version MC: {modpack.mcVersion} + Version Modpack: {modpack.version} +
+ + {selectedModpackIndex === index && ( +
+
Créer le serveur {modpack.short_name} ?
+ + + +
+ )} +
+ )) )}
diff --git a/src/pages/CreateServer/modpack/modpack.module.scss b/src/pages/CreateServer/modpack/modpack.module.scss index 5ec5e9c..690f5f2 100644 --- a/src/pages/CreateServer/modpack/modpack.module.scss +++ b/src/pages/CreateServer/modpack/modpack.module.scss @@ -1,3 +1,155 @@ -.hey{ - margin-top: 8em; -} \ No newline at end of file +.Container { + display: flex; + flex-direction: column; + align-items: center; + padding: 6rem; + } + + .modpackGrid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(30rem, 1fr)); + gap: 2rem; + width: 100%; + padding: 1.5rem; + } + + .modpackCard { + background-color: #f9f9f9; + border: 1px solid #ddd; + border-radius: .7rem; + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); + padding: 2rem; + width: 30rem; + text-align: center; + transition: transform 0.2s ease-in-out, filter 0.2s ease-in-out; + cursor: pointer; + position: relative; + + &:hover { + transform: translateY(-.2rem); + } + + .content { + transition: filter 0.2s ease-in-out; + } + + &.selected .content { + filter: blur(.8rem); + } + + + h3 { + margin: .8rem 0; + font-size: 1.5em; + color: #333; + } + + p { + font-size: 1em; + color: #666; + } + + .modpackImage { + width: 100%; + height: auto; + margin-bottom: 1rem; + } + + .tags { + display: flex; + flex-wrap: wrap; + justify-content: center; + gap: .5rem; + margin: 2.5rem 0rem; + + .tag { + display: flex; + align-items: center; + padding: .4rem .8rem; + border-radius: 1.5rem; + font-size: 0.9em; + font-weight: 500; + color: #fff; + + &.adventure { + background-color: #4CAF50; + } + + &.combat { + background-color: #E91E63; + } + + &.pve { + background-color: #2196F3; + } + + &.forge { + background-color: #FFC107; + } + } + } + + .meta { + display: flex; + justify-content: space-between; + font-size: .9rem; + color: #999; + } + + .overlay { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: rgba(255, 255, 255, 0.9); + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + gap: 1rem; + z-index: 10; + + h4 { + font-size: 1.8rem; + color: #333; + margin-bottom: 1rem; + } + + .createButton { + background-color: black; + color: white; + padding: 0.5rem 1.5rem; + border: none; + border-radius: 5px; + cursor: pointer; + font-size: 1.25rem; + } + + .createButton:hover { + background-color: #252525bb; + } + + .cancelButton { + background-color: gray; + color: white; + padding: 0.5rem 1.5rem; + border: none; + border-radius: 5px; + cursor: pointer; + font-size: 1.25rem; + } + + .cancelButton:hover { + background-color: rgba(95, 95, 95, 0.685); + } + + .shortNamtitle { + font-weight: 400; + font-size: 1.5rem; + color: #333; + margin-bottom: 1rem; + } + } + } + \ No newline at end of file diff --git a/src/service/api.test.js b/src/service/api.test.js deleted file mode 100644 index e69de29..0000000 From 1212375d651cde09444ec5987351d96bc9418b44 Mon Sep 17 00:00:00 2001 From: AntoninoP Date: Sun, 22 Sep 2024 23:57:46 +0200 Subject: [PATCH 03/17] +ajout modal confirmation suppresion --- .../DeleteConfirmationModal.jsx | 20 +++ .../DeleteConfirmationModal.module.scss | 62 +++++++ src/pages/DashboardPage/DashboardPage.jsx | 160 ++++++++++-------- 3 files changed, 172 insertions(+), 70 deletions(-) create mode 100644 src/components/DeleteConfirmationModal/DeleteConfirmationModal.jsx create mode 100644 src/components/DeleteConfirmationModal/DeleteConfirmationModal.module.scss diff --git a/src/components/DeleteConfirmationModal/DeleteConfirmationModal.jsx b/src/components/DeleteConfirmationModal/DeleteConfirmationModal.jsx new file mode 100644 index 0000000..1d02f39 --- /dev/null +++ b/src/components/DeleteConfirmationModal/DeleteConfirmationModal.jsx @@ -0,0 +1,20 @@ +import styles from './DeleteConfirmationModal.module.scss'; + +const DeleteConfirmationModal = ({ isOpen, onClose, onDelete }) => { + if (!isOpen) return null; + + return ( +
+
+

Êtes-vous sûr de vouloir supprimer ?

+

Cette action est irréversible.

+
+ + +
+
+
+ ); +}; + +export default DeleteConfirmationModal; diff --git a/src/components/DeleteConfirmationModal/DeleteConfirmationModal.module.scss b/src/components/DeleteConfirmationModal/DeleteConfirmationModal.module.scss new file mode 100644 index 0000000..70ed127 --- /dev/null +++ b/src/components/DeleteConfirmationModal/DeleteConfirmationModal.module.scss @@ -0,0 +1,62 @@ +.modalOverlay { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: rgba(0, 0, 0, 0.5); + display: flex; + align-items: center; + justify-content: center; +} + +.modal { + background-color: white; + border-radius: 0.67rem; + padding: 1.67rem; + box-shadow: 0 0.33rem 2.5rem rgba(0, 0, 0, 0.1); + max-width: 33.33rem; + width: 100%; + text-align: center; +} + +h2 { + margin: 0 0 0.83rem; +} + +p { + margin: 0 0 1.67rem; +} + +.modalButtons { + display: flex; + justify-content: space-between; +} + +.cancelButton { + background-color: #f0f0f0; + color: #333; + border: none; + padding: 0.83rem 1.25rem; + border-radius: 0.42rem; + font-size: 1rem; + cursor: pointer; + + &:hover { + background-color: #e0e0e0; + } +} + +.deleteButton { + background-color: #be3939; + color: white; + border: none; + padding: 0.83rem 1.25rem; + border-radius: 0.42rem; + font-size: 1rem; + cursor: pointer; + + &:hover { + background-color: rgba(255, 59, 59, 0.76); + } +} diff --git a/src/pages/DashboardPage/DashboardPage.jsx b/src/pages/DashboardPage/DashboardPage.jsx index 459ae41..bec1a02 100644 --- a/src/pages/DashboardPage/DashboardPage.jsx +++ b/src/pages/DashboardPage/DashboardPage.jsx @@ -7,6 +7,7 @@ import { getUserSubdomain } from "../../service/firebase"; import serviiApi from "../../service/api.tsx"; import PropTypes from "prop-types"; import styles from './DashboardPage.module.scss'; +import DeleteConfirmationModal from '../../components/DeleteConfirmationModal/DeleteConfirmationModal'; const CACHE_KEY_SERVERS = 'cachedServers'; const CACHE_KEY_TIMESTAMP = 'cacheTimestamp'; @@ -23,6 +24,9 @@ const DashboardPage = ({ user }) => { const [loading, setLoading] = useState(servers.length === 0); const [searchTerm, setSearchTerm] = useState(''); const [newSubdomain, setNewSubdomain] = useState(' '); + + const [isModalOpen, setModalOpen] = useState(false); + const [serverToDelete, setServerToDelete] = useState(null); const updateServersFromApi = useCallback(async () => { try { @@ -69,12 +73,16 @@ const DashboardPage = ({ user }) => { } }; - const handleDeleteServer = async (serverName) => { - try { - await serviiApi.serverDelete(serverName); - updateServersFromApi(); - } catch (error) { - console.error('Error deleting server:', error); + const handleDeleteServer = async () => { + if (serverToDelete) { + try { + await serviiApi.serverDelete(serverToDelete); + setModalOpen(false); + setServerToDelete(null); + updateServersFromApi(); + } catch (error) { + console.error('Error deleting server:', error); + } } }; @@ -152,71 +160,83 @@ const DashboardPage = ({ user }) => { return (
-
-
- Adresse de connexion à vos serveurs : - - {" " + subdomain}.servii.fr - -
- -
- setSearchTerm(e.target.value)} - className={styles.searchInput} - /> - -
- -
- {favoriteServer ? ( -
- handleRunServer(favoriteServer.name)} - onStopClick={() => handleStopServer(favoriteServer.name)} - onDeleteClick={() => handleDeleteServer(favoriteServer.name)} - subdomain={subdomain} - favoriteServer={true} - /> -
- ) : null} -
- -
- {serversWithoutFavorite.length > 0 ? ( - serversWithoutFavorite.filter(server => - server.name.toLowerCase().includes(searchTerm.toLowerCase()) - ).map((server) => ( - handleRunServer(server.name)} - onStopClick={() => handleStopServer(server.name)} - onDeleteClick={() => handleDeleteServer(server.name)} - subdomain={subdomain} - /> - )) - ) : null} -
+
+
+ Adresse de connexion à vos serveurs : + + {" " + subdomain}.servii.fr +
+ +
+ setSearchTerm(e.target.value)} + className={styles.searchInput} + /> + +
+ +
+ {favoriteServer ? ( +
+ handleRunServer(favoriteServer.name)} + onStopClick={() => handleStopServer(favoriteServer.name)} + onDeleteClick={() => { + setServerToDelete(favoriteServer.name); + setModalOpen(true); + }} + subdomain={subdomain} + favoriteServer={true} + /> +
+ ) : null} +
+ +
+ {serversWithoutFavorite.length > 0 ? ( + serversWithoutFavorite.filter(server => + server.name.toLowerCase().includes(searchTerm.toLowerCase()) + ).map((server) => ( + handleRunServer(server.name)} + onStopClick={() => handleStopServer(server.name)} + onDeleteClick={() => { + setServerToDelete(server.name); + setModalOpen(true); + }} + subdomain={subdomain} + /> + )) + ) : null} +
+
+ + setModalOpen(false)} + onDelete={handleDeleteServer} + />
); }; From 0d6b741fd4da337360780fe3537d1b2e6d6b3bdc Mon Sep 17 00:00:00 2001 From: AntoninoP Date: Mon, 23 Sep 2024 02:15:57 +0200 Subject: [PATCH 04/17] fix responsive --- src/main.css | 6 +- .../CreateServer/CreateServer.module.scss | 18 +- src/pages/CreateServer/modpack/modpack.jsx | 36 +-- .../CreateServer/modpack/modpack.module.scss | 287 +++++++++--------- 4 files changed, 172 insertions(+), 175 deletions(-) diff --git a/src/main.css b/src/main.css index 86da27f..886f248 100644 --- a/src/main.css +++ b/src/main.css @@ -42,19 +42,19 @@ body { @media (min-width: 1600px) { html { - font-size: 17px; + font-size: 14px; } } @media (min-width: 1800px) { html { - font-size: 19px; + font-size: 15px; } } @media (min-width: 2000px) { html { - font-size: 20px; + font-size: 17px; } } diff --git a/src/pages/CreateServer/CreateServer.module.scss b/src/pages/CreateServer/CreateServer.module.scss index 915ca10..bc72c7b 100644 --- a/src/pages/CreateServer/CreateServer.module.scss +++ b/src/pages/CreateServer/CreateServer.module.scss @@ -26,7 +26,6 @@ } .GamesContainer { - margin-top: 2.5rem; display: flex; justify-content: center; align-items: center; @@ -90,20 +89,23 @@ img { .GamesContainer { flex-direction: column; flex-wrap: row; - margin-top: 40rem; height: 100%; - + margin-top: 45rem; } .GameCard { - margin: 1rem; + margin: 0rem 0rem .5rem 0rem; width: 20rem; padding: 2.5rem; } -} -@media (max-width: 750px) { - .GamesContainer { - margin-top: 50rem; + .GamesChoice { + margin-top: 35rem; } + + .title { + margin-top: 2rem; + } + + } diff --git a/src/pages/CreateServer/modpack/modpack.jsx b/src/pages/CreateServer/modpack/modpack.jsx index cbdea89..879fdb3 100644 --- a/src/pages/CreateServer/modpack/modpack.jsx +++ b/src/pages/CreateServer/modpack/modpack.jsx @@ -10,16 +10,13 @@ const Modpack = ({ user }) => { const [modpacks, setModpacks] = useState([]); const [selectedModpackIndex, setSelectedModpackIndex] = useState(null); const [error, setError] = useState(''); - const [serverShortName, setServerShortName] = useState(''); useEffect(() => { const fetchModpacks = async () => { try { const response = await serviiApi.fetchModpacks(); if (response.return_code === 200) { - const fetchedModpacks = response.message; - - setModpacks(fetchedModpacks); + setModpacks(response.message); } else { setError(response.message); } @@ -36,22 +33,23 @@ const Modpack = ({ user }) => { setSelectedModpackIndex(index); }; - const handleCancel = () => { - setSelectedModpackIndex(null); - console.log("dffdff"); + console.log("Annuler cliqué, index sélectionné avant :", selectedModpackIndex); + setSelectedModpackIndex(null); // Remise à zéro de l'index sélectionné }; - const handleCreate = async () => { - try { - const framework = selectedModpackIndex.framework; - await serviiApi.serverCreate(selectedModpackIndex.short_name, selectedModpackIndex.short_name, framework); - console.log(selectedModpackIndex.short_name, selectedModpackIndex.short_name, framework); - navigate('/Dashboard'); - } catch (error) { - console.error('Error creating server:', error); - } + if (selectedModpackIndex !== null) { + const selectedModpack = modpacks[selectedModpackIndex]; + const { short_name, framework } = selectedModpack; + + try { + await serviiApi.serverCreate(short_name, short_name, framework); + navigate('/Dashboard'); + } catch (error) { + console.error('Error creating server:', error); + } + } }; return ( @@ -67,7 +65,7 @@ const Modpack = ({ user }) => { {error ?

{error}

: ( modpacks.map((modpack, index) => (
handleModpackClick(index)} > @@ -84,13 +82,11 @@ const Modpack = ({ user }) => { Version MC: {modpack.mcVersion} Version Modpack: {modpack.version}
- - {selectedModpackIndex === index && ( + {selectedModpackIndex === index && ( // Condition modifiée
Créer le serveur {modpack.short_name} ?
-
)}
diff --git a/src/pages/CreateServer/modpack/modpack.module.scss b/src/pages/CreateServer/modpack/modpack.module.scss index 690f5f2..9272390 100644 --- a/src/pages/CreateServer/modpack/modpack.module.scss +++ b/src/pages/CreateServer/modpack/modpack.module.scss @@ -1,155 +1,154 @@ .Container { + display: flex; + flex-direction: column; + align-items: center; + padding: 6rem; +} + +.modpackGrid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(30rem, 1fr)); + gap: 2rem; + width: 100%; + padding: 1.5rem; +} + +.modpackCard { + background-color: #f9f9f9; + border: 1px solid #ddd; + border-radius: .7rem; + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); + padding: 2rem; + width: 30rem; + text-align: center; + transition: all 0.3s ease; /* Ajout de la transition pour l'état sélectionné/non-sélectionné */ + cursor: pointer; + position: relative; + + &:hover { + transform: translateY(-.2rem); + } + + .content { + transition: filter 0.3s ease; /* Transition pour le flou */ + filter: none; /* Par défaut, pas de flou */ + } + + &.selected .content { + filter: blur(0.8rem); /* Appliquer le flou si sélectionné */ + } + + h3 { + margin: .8rem 0; + font-size: 1.5em; + color: #333; + } + + p { + font-size: 1em; + color: #666; + } + + .modpackImage { + width: 100%; + height: auto; + margin-bottom: 1rem; + } + + .tags { + display: flex; + flex-wrap: wrap; + justify-content: center; + gap: .5rem; + margin: 2.5rem 0rem; + + .tag { + display: flex; + align-items: center; + padding: .4rem .8rem; + border-radius: 1.5rem; + font-size: 0.9em; + font-weight: 500; + color: #fff; + + &.adventure { + background-color: #4CAF50; + } + + &.combat { + background-color: #E91E63; + } + + &.pve { + background-color: #2196F3; + } + + &.forge { + background-color: #FFC107; + } + } + } + + .meta { + display: flex; + justify-content: space-between; + font-size: .9rem; + color: #999; + } + + .overlay { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: rgba(255, 255, 255, 0.9); display: flex; flex-direction: column; + justify-content: center; align-items: center; - padding: 6rem; - } - - .modpackGrid { - display: grid; - grid-template-columns: repeat(auto-fill, minmax(30rem, 1fr)); - gap: 2rem; - width: 100%; - padding: 1.5rem; - } - - .modpackCard { - background-color: #f9f9f9; - border: 1px solid #ddd; - border-radius: .7rem; - box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); - padding: 2rem; - width: 30rem; - text-align: center; - transition: transform 0.2s ease-in-out, filter 0.2s ease-in-out; - cursor: pointer; - position: relative; - - &:hover { - transform: translateY(-.2rem); - } - - .content { - transition: filter 0.2s ease-in-out; - } - - &.selected .content { - filter: blur(.8rem); - } - - - h3 { - margin: .8rem 0; - font-size: 1.5em; + gap: 1rem; + z-index: 10; + + h4 { + font-size: 1.8rem; color: #333; - } - - p { - font-size: 1em; - color: #666; - } - - .modpackImage { - width: 100%; - height: auto; margin-bottom: 1rem; } - - .tags { - display: flex; - flex-wrap: wrap; - justify-content: center; - gap: .5rem; - margin: 2.5rem 0rem; - - .tag { - display: flex; - align-items: center; - padding: .4rem .8rem; - border-radius: 1.5rem; - font-size: 0.9em; - font-weight: 500; - color: #fff; - - &.adventure { - background-color: #4CAF50; - } - - &.combat { - background-color: #E91E63; - } - - &.pve { - background-color: #2196F3; - } - - &.forge { - background-color: #FFC107; - } - } + + .createButton { + background-color: black; + color: white; + padding: 0.5rem 1.5rem; + border: none; + border-radius: 5px; + cursor: pointer; + font-size: 1.25rem; } - - .meta { - display: flex; - justify-content: space-between; - font-size: .9rem; - color: #999; + + .createButton:hover { + background-color: #252525bb; } - - .overlay { - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - background: rgba(255, 255, 255, 0.9); - display: flex; - flex-direction: column; - justify-content: center; - align-items: center; - gap: 1rem; - z-index: 10; - - h4 { - font-size: 1.8rem; - color: #333; - margin-bottom: 1rem; - } - - .createButton { - background-color: black; - color: white; - padding: 0.5rem 1.5rem; - border: none; - border-radius: 5px; - cursor: pointer; - font-size: 1.25rem; - } - - .createButton:hover { - background-color: #252525bb; - } - - .cancelButton { - background-color: gray; - color: white; - padding: 0.5rem 1.5rem; - border: none; - border-radius: 5px; - cursor: pointer; - font-size: 1.25rem; - } - - .cancelButton:hover { - background-color: rgba(95, 95, 95, 0.685); - } - - .shortNamtitle { - font-weight: 400; - font-size: 1.5rem; - color: #333; - margin-bottom: 1rem; - } + + .cancelButton { + background-color: gray; + color: white; + padding: 0.5rem 1.5rem; + border: none; + border-radius: 5px; + cursor: pointer; + font-size: 1.25rem; + } + + .cancelButton:hover { + background-color: rgba(95, 95, 95, 0.685); + } + + .shortNamtitle { + font-weight: 400; + font-size: 1.5rem; + color: #333; + margin-bottom: 1rem; } } - \ No newline at end of file +} From da3a161f300485ec9421882a1560fab66dabf208 Mon Sep 17 00:00:00 2001 From: AntoninoP Date: Mon, 23 Sep 2024 03:02:29 +0200 Subject: [PATCH 05/17] +payement handling v0.5 --- package-lock.json | 23 +++++++ package.json | 2 + src/App.jsx | 3 + .../Payement/PaymentForm/PaymentForm.jsx | 49 +++++++++++++ .../PaymentForm/PaymentForm.module.scss | 19 ++++++ src/pages/Payement/Pricing.jsx | 68 +++++++++++++++++++ src/pages/Payement/Pricing.module.scss | 60 ++++++++++++++++ 7 files changed, 224 insertions(+) create mode 100644 src/pages/Payement/PaymentForm/PaymentForm.jsx create mode 100644 src/pages/Payement/PaymentForm/PaymentForm.module.scss create mode 100644 src/pages/Payement/Pricing.jsx create mode 100644 src/pages/Payement/Pricing.module.scss diff --git a/package-lock.json b/package-lock.json index 6921446..237c5c5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,8 @@ "dependencies": { "@fortawesome/free-solid-svg-icons": "^6.5.2", "@fortawesome/react-fontawesome": "^0.2.2", + "@stripe/react-stripe-js": "^2.8.0", + "@stripe/stripe-js": "^4.5.0", "@testing-library/jest-dom": "^6.4.6", "@testing-library/react": "^16.0.0", "@types/jest": "^29.5.12", @@ -3184,6 +3186,27 @@ "@sinonjs/commons": "^3.0.0" } }, + "node_modules/@stripe/react-stripe-js": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@stripe/react-stripe-js/-/react-stripe-js-2.8.0.tgz", + "integrity": "sha512-Vf1gNEuBxA9EtxiLghm2ZWmgbADNMJw4HW6eolUu0DON/6mZvWZgk0KHolN0sozNJwYp0i/8hBsDBcBUWcvnbw==", + "dependencies": { + "prop-types": "^15.7.2" + }, + "peerDependencies": { + "@stripe/stripe-js": "^1.44.1 || ^2.0.0 || ^3.0.0 || ^4.0.0", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/@stripe/stripe-js": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/@stripe/stripe-js/-/stripe-js-4.5.0.tgz", + "integrity": "sha512-dMOzc58AOlsF20nYM/avzV8RFhO/vgYTY7ajLMH6mjlnZysnOHZxsECQvjEmL8Q/ukPwHkOnxSPW/QGCCnp7XA==", + "engines": { + "node": ">=12.16" + } + }, "node_modules/@testing-library/dom": { "version": "10.3.0", "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.3.0.tgz", diff --git a/package.json b/package.json index 70d12b1..2ed23ab 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,8 @@ "dependencies": { "@fortawesome/free-solid-svg-icons": "^6.5.2", "@fortawesome/react-fontawesome": "^0.2.2", + "@stripe/react-stripe-js": "^2.8.0", + "@stripe/stripe-js": "^4.5.0", "@testing-library/jest-dom": "^6.4.6", "@testing-library/react": "^16.0.0", "@types/jest": "^29.5.12", diff --git a/src/App.jsx b/src/App.jsx index 57d30ee..824d4fb 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -5,6 +5,7 @@ import 'react-toastify/dist/ReactToastify.css'; import { auth } from './service/firebase'; import styles from './App.module.scss'; import Loading from './pages/Loading/loading'; +import Pricing from './pages/Payement/Pricing'; const LoginPage = lazy(() => import('./pages/LoginPage/LoginPage')); const ServerDetails = lazy(() => import('./pages/ServerDetails/ServerDetails')); @@ -55,6 +56,8 @@ const App = () => { : } /> : } /> : } /> + : } /> + } /> : } /> diff --git a/src/pages/Payement/PaymentForm/PaymentForm.jsx b/src/pages/Payement/PaymentForm/PaymentForm.jsx new file mode 100644 index 0000000..59231c4 --- /dev/null +++ b/src/pages/Payement/PaymentForm/PaymentForm.jsx @@ -0,0 +1,49 @@ +import React, { useState } from 'react'; +import { CardElement, useStripe, useElements } from '@stripe/react-stripe-js'; +import styles from './PaymentForm.module.scss'; + +const PaymentForm = ({ groups, closeModal }) => { + const stripe = useStripe(); + const elements = useElements(); + const [errorMessage, setErrorMessage] = useState(null); + const [successMessage, setSuccessMessage] = useState(null); + + const handleSubmit = async (event) => { + event.preventDefault(); + + if (!stripe || !elements) return; + + const cardElement = elements.getElement(CardElement); + const { error, paymentMethod } = await stripe.createPaymentMethod({ + type: 'card', + card: cardElement, + }); + + if (error) { + setErrorMessage(error.message); + setSuccessMessage(null); + } else { + setSuccessMessage(`Paiement réussi! Méthode de paiement ID: ${paymentMethod.id}`); + setErrorMessage(null); + // Ici, vous devriez également envoyer paymentMethod.id à votre serveur + // Fermer la modal après un court délai ou après l'envoi à votre serveur + setTimeout(() => closeModal(), 2000); + } + }; + + return ( +
+ + {errorMessage &&

{errorMessage}

} + {successMessage &&

{successMessage}

} + + + + ); +}; + +export default PaymentForm; diff --git a/src/pages/Payement/PaymentForm/PaymentForm.module.scss b/src/pages/Payement/PaymentForm/PaymentForm.module.scss new file mode 100644 index 0000000..0bbf541 --- /dev/null +++ b/src/pages/Payement/PaymentForm/PaymentForm.module.scss @@ -0,0 +1,19 @@ +.modal { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: rgba(0, 0, 0, 0.5); + display: flex; + justify-content: center; + align-items: center; + } + + .modalContent { + background: #fff; + padding: 20px; + border-radius: 8px; + width: 300px; + } + \ No newline at end of file diff --git a/src/pages/Payement/Pricing.jsx b/src/pages/Payement/Pricing.jsx new file mode 100644 index 0000000..f82b4df --- /dev/null +++ b/src/pages/Payement/Pricing.jsx @@ -0,0 +1,68 @@ +import React, { useState } from 'react'; +import styles from './Pricing.module.scss'; +import Navbar from '../../components/navbar/Navbar'; +import { useNavigate } from 'react-router-dom'; +import PropTypes from 'prop-types'; +import { loadStripe } from '@stripe/stripe-js'; +import { Elements } from '@stripe/react-stripe-js'; +import PaymentForm from './PaymentForm/PaymentForm'; + +const stripePromise = loadStripe('pk_test_51PyIYTP3VLLeb9GlXCKgD4ylbemZPx72I3HkEAu0bRtcsfK31nqb3WtUbXKXUcKmyfrxKLfuJzZCPyp7Ymtlq9zy00c7VmkL6G'); + +const Pricing = ({ user }) => { + const navigate = useNavigate(); + const [selectedPackage, setSelectedPackage] = useState(null); + const [isModalOpen, setIsModalOpen] = useState(false); + + const groups = [ + { title: 'Gratuit', price: '0 €', features: ['Accès limité', 'Support par e-mail'] }, + { title: 'Standard', price: '2 €', features: ['Accès complet', 'Support prioritaire'] }, + { title: 'Premium', price: '9 €', features: ['Accès illimité', 'Support 24/7'] }, + ]; + + const handleSubscribe = (pkg) => { + setSelectedPackage(pkg); + setIsModalOpen(true); + }; + + return ( +
+ navigate('/Dashboard')} + /> + + {groups.map((pkg, index) => ( +
+

{pkg.title}

+

{pkg.price}

+
    + {pkg.features.map((feature, idx) => ( +
  • {feature}
  • + ))} +
+ +
+ ))} + + {isModalOpen && ( +
+
+

Paiement pour {selectedPackage.title}

+ + setIsModalOpen(false)} /> + +
+
+ )} +
+ ); +}; + +Pricing.propTypes = { + user: PropTypes.object.isRequired, +}; + +export default Pricing; diff --git a/src/pages/Payement/Pricing.module.scss b/src/pages/Payement/Pricing.module.scss new file mode 100644 index 0000000..b9d81a9 --- /dev/null +++ b/src/pages/Payement/Pricing.module.scss @@ -0,0 +1,60 @@ +$primary-color: #000; +$secondary-color: #fff; + +.pricingContainer { + display: flex; + justify-content: center; + gap: 20px; + padding: 40px; + background-color: $secondary-color; +} + +.packageCard { + background-color: $primary-color; + color: $secondary-color; + border-radius: 8px; + padding: 20px; + text-align: center; + box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1); + transition: transform 0.3s; + + &:hover { + transform: scale(1.05); + } +} + +.title { + font-size: 24px; + margin-bottom: 10px; +} + +.price { + font-size: 32px; + margin: 10px 0; +} + +.features { + list-style-type: none; + padding: 0; + margin: 20px 0; + + li { + margin: 5px 0; + } +} + +.button { + background-color: $secondary-color; + color: $primary-color; + border: none; + border-radius: 4px; + padding: 10px 20px; + cursor: pointer; + font-size: 16px; + transition: background-color 0.3s; + + &:hover { + background-color: $primary-color; + color: $secondary-color; + } +} From f13473c2f85ede80ced42bb9995e26ac6ce7688c Mon Sep 17 00:00:00 2001 From: AntoninoP Date: Mon, 23 Sep 2024 13:25:52 +0200 Subject: [PATCH 06/17] + btn s'abonner dans la navbar --- src/components/navbar/Navbar.jsx | 16 ++++++++++++++-- src/components/navbar/Navbar.module.scss | 12 ++++++++++++ src/pages/CreateServer/modpack/modpack.jsx | 2 +- .../CreateServer/modpack/modpack.module.scss | 2 +- 4 files changed, 28 insertions(+), 4 deletions(-) diff --git a/src/components/navbar/Navbar.jsx b/src/components/navbar/Navbar.jsx index f7f9ee6..a301ae5 100644 --- a/src/components/navbar/Navbar.jsx +++ b/src/components/navbar/Navbar.jsx @@ -4,16 +4,19 @@ import styles from './Navbar.module.scss'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { faUser, faCog, faSignOutAlt, faArrowLeft } from '@fortawesome/free-solid-svg-icons'; import PropTypes from "prop-types"; +import { useNavigate } from 'react-router-dom'; const Navbar = ({ user, hasShadow = true, showBackButton = false, onBackClick, - backButtonText = "Retour au dashboard" + backButtonText = "Retour au dashboard", + Subsribebtn = true }) => { const [dropdownOpen, setDropdownOpen] = useState(false); const dropdownRef = useRef(null); + const navigate = useNavigate(); const handleLogout = () => { auth.signOut(); @@ -45,7 +48,15 @@ const Navbar = ({ {showBackButton && (
- {backButtonText} {} + {backButtonText} +
+ )} + + {Subsribebtn && ( // Updated: Conditional rendering for the subscribe button +
+
navigate('/Pricing')}> + S'abonner +
)} @@ -90,6 +101,7 @@ Navbar.propTypes = { showBackButton: PropTypes.bool, onBackClick: PropTypes.func, backButtonText: PropTypes.string, + Subsribebtn: PropTypes.bool // Updated: Prop type corrected }; export default Navbar; diff --git a/src/components/navbar/Navbar.module.scss b/src/components/navbar/Navbar.module.scss index 8c1347f..46492f6 100644 --- a/src/components/navbar/Navbar.module.scss +++ b/src/components/navbar/Navbar.module.scss @@ -89,6 +89,18 @@ font-size: 1.5rem; } + +.subscribeBtn{ + padding: .5rem 1rem; + border-radius: .4rem; + background-color: #2f2f2f; + color: white; + cursor: pointer; + border: none; + margin-right: 2rem; +} + + @media (max-width: 650px) { .backButton{ font-size: 1.25rem; diff --git a/src/pages/CreateServer/modpack/modpack.jsx b/src/pages/CreateServer/modpack/modpack.jsx index 879fdb3..81b7afd 100644 --- a/src/pages/CreateServer/modpack/modpack.jsx +++ b/src/pages/CreateServer/modpack/modpack.jsx @@ -35,7 +35,7 @@ const Modpack = ({ user }) => { const handleCancel = () => { console.log("Annuler cliqué, index sélectionné avant :", selectedModpackIndex); - setSelectedModpackIndex(null); // Remise à zéro de l'index sélectionné + setSelectedModpackIndex(null); }; const handleCreate = async () => { diff --git a/src/pages/CreateServer/modpack/modpack.module.scss b/src/pages/CreateServer/modpack/modpack.module.scss index 9272390..792658d 100644 --- a/src/pages/CreateServer/modpack/modpack.module.scss +++ b/src/pages/CreateServer/modpack/modpack.module.scss @@ -21,7 +21,7 @@ padding: 2rem; width: 30rem; text-align: center; - transition: all 0.3s ease; /* Ajout de la transition pour l'état sélectionné/non-sélectionné */ + transition: all 0.3s ease; cursor: pointer; position: relative; From a7044a9dc7ec4186d3a61dc5d08ed9330107c229 Mon Sep 17 00:00:00 2001 From: AntoninoP Date: Thu, 26 Sep 2024 16:18:48 +0200 Subject: [PATCH 07/17] +stripe integration --- index.html | 2 +- src/App.jsx | 7 +- src/assets/logo.png | Bin 0 -> 43491 bytes src/components/navbar/Navbar.jsx | 4 +- src/pages/DashboardPage/DashboardPage.jsx | 45 ++++++- src/pages/Payement/Checkout.jsx | 51 +++++++ .../Payement/PaymentForm/PaymentForm.jsx | 124 ++++++++++++------ .../PaymentForm/PaymentForm.module.scss | 26 +--- src/pages/Payement/Pricing.jsx | 68 ---------- src/pages/Payement/Pricing/Pricing.jsx | 52 ++++++++ .../{ => Pricing}/Pricing.module.scss | 33 ++--- src/service/firebase.jsx | 14 +- 12 files changed, 274 insertions(+), 152 deletions(-) create mode 100644 src/assets/logo.png create mode 100644 src/pages/Payement/Checkout.jsx delete mode 100644 src/pages/Payement/Pricing.jsx create mode 100644 src/pages/Payement/Pricing/Pricing.jsx rename src/pages/Payement/{ => Pricing}/Pricing.module.scss (70%) diff --git a/index.html b/index.html index e1f3365..6878b75 100644 --- a/index.html +++ b/index.html @@ -2,7 +2,7 @@ - + Servii - Hebergement facile diff --git a/src/App.jsx b/src/App.jsx index 824d4fb..a6212b3 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -5,7 +5,9 @@ import 'react-toastify/dist/ReactToastify.css'; import { auth } from './service/firebase'; import styles from './App.module.scss'; import Loading from './pages/Loading/loading'; -import Pricing from './pages/Payement/Pricing'; +import Pricing from './pages/Payement/Pricing/Pricing'; +import PaymentForm from './pages/Payement/PaymentForm/PaymentForm'; +import Checkout from './pages/Payement/Checkout'; const LoginPage = lazy(() => import('./pages/LoginPage/LoginPage')); const ServerDetails = lazy(() => import('./pages/ServerDetails/ServerDetails')); @@ -57,7 +59,8 @@ const App = () => { : } /> : } /> : } /> - + : } /> + : } /> } /> : } /> diff --git a/src/assets/logo.png b/src/assets/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..b6ffd49c4803355f3e1cbbee99db36fda2850a9d GIT binary patch literal 43491 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 literal 0 HcmV?d00001 diff --git a/src/components/navbar/Navbar.jsx b/src/components/navbar/Navbar.jsx index a301ae5..e5f27de 100644 --- a/src/components/navbar/Navbar.jsx +++ b/src/components/navbar/Navbar.jsx @@ -52,7 +52,7 @@ const Navbar = ({
)} - {Subsribebtn && ( // Updated: Conditional rendering for the subscribe button + {Subsribebtn && (
navigate('/Pricing')}> S'abonner @@ -101,7 +101,7 @@ Navbar.propTypes = { showBackButton: PropTypes.bool, onBackClick: PropTypes.func, backButtonText: PropTypes.string, - Subsribebtn: PropTypes.bool // Updated: Prop type corrected + Subsribebtn: PropTypes.bool }; export default Navbar; diff --git a/src/pages/DashboardPage/DashboardPage.jsx b/src/pages/DashboardPage/DashboardPage.jsx index bec1a02..2b030cc 100644 --- a/src/pages/DashboardPage/DashboardPage.jsx +++ b/src/pages/DashboardPage/DashboardPage.jsx @@ -3,7 +3,7 @@ import { useNavigate } from 'react-router-dom'; import ServerCard from '../../components/serverCards/DefaultServerCard'; import Navbar from '../../components/navbar/Navbar'; import Loading from '../Loading/loading'; -import { getUserSubdomain } from "../../service/firebase"; +import { getUserSubdomain, getUserSubscription } from "../../service/firebase"; import serviiApi from "../../service/api.tsx"; import PropTypes from "prop-types"; import styles from './DashboardPage.module.scss'; @@ -21,6 +21,7 @@ const DashboardPage = ({ user }) => { }); const [subdomain, setSubdomain] = useState(' '); + const [subscription, setSubscription] = useState(0); const [loading, setLoading] = useState(servers.length === 0); const [searchTerm, setSearchTerm] = useState(''); const [newSubdomain, setNewSubdomain] = useState(' '); @@ -33,6 +34,8 @@ const DashboardPage = ({ user }) => { if (user?.uid) { const userSubdomain = await getUserSubdomain(user.uid); setSubdomain(userSubdomain || null); + const userSubscription = await getUserSubscription(user.uid); + setSubscription(userSubscription || 0); } const ApiResponse = await serviiApi.fetchServers(); @@ -55,14 +58,44 @@ const DashboardPage = ({ user }) => { const handleCreateServer = () => navigate('/CreateServer'); - const handleRunServer = async (serverName) => { + const handleRunServer = async (serverName, framework) => { try { - await serviiApi.serverRun(serverName); - updateServersFromApi(); + if (subscription === 0) { + if (framework === "paper") { + navigate('/payement?package=Gratuit'); + } if (framework === "Bedrock") { + navigate('/payement?package=Standard'); + } + else { + navigate('/payement?package=Standard'); + } + } + + else if (subscription === 1) { + if (framework === "paper") { + await serviiApi.serverRun(serverName); + updateServersFromApi(); + } else { + navigate('/payement?package=Standard'); + } + } + else if (subscription === 2) { + if (framework === "paper" || framework === "Bedrock") { + await serviiApi.serverRun(serverName); + updateServersFromApi(); + } else { + navigate('/payement?package=Premium'); + } + } + else if (subscription === 3) { + await serviiApi.serverRun(serverName); + updateServersFromApi(); + } } catch (error) { console.error('Error starting server:', error); } }; + const handleStopServer = async (serverName) => { try { @@ -193,7 +226,7 @@ const DashboardPage = ({ user }) => { framework={favoriteServer.framework} maxPlayers={favoriteServer.maxPlayers} countPlayers={favoriteServer.onlinePlayers} - onRunClick={() => handleRunServer(favoriteServer.name)} + onRunClick={() => handleRunServer(favoriteServer.name , favoriteServer.framework)} onStopClick={() => handleStopServer(favoriteServer.name)} onDeleteClick={() => { setServerToDelete(favoriteServer.name); @@ -219,7 +252,7 @@ const DashboardPage = ({ user }) => { framework={server.framework} maxPlayers={server.maxPlayers} countPlayers={server.onlinePlayers} - onRunClick={() => handleRunServer(server.name)} + onRunClick={() => handleRunServer(server.name , server.framework)} onStopClick={() => handleStopServer(server.name)} onDeleteClick={() => { setServerToDelete(server.name); diff --git a/src/pages/Payement/Checkout.jsx b/src/pages/Payement/Checkout.jsx new file mode 100644 index 0000000..120c0a6 --- /dev/null +++ b/src/pages/Payement/Checkout.jsx @@ -0,0 +1,51 @@ +import { useState, useEffect } from 'react'; + +const Checkout = () => { + const [sessionStatus, setSessionStatus] = useState(null); + + useEffect(() => { + const params = new URLSearchParams(window.location.search); + const session_id = params.get('session_id'); + + console.log('session_id:', session_id); + + if (session_id) { + const fetchSessionStatus = async () => { + try { + const response = await fetch(`http://192.168.68.114:3000/get-session-status?session_id=${session_id}`); + const data = await response.json(); + setSessionStatus(data); + } catch (error) { + console.error('Error fetching session status:', error); + } + }; + + fetchSessionStatus(); + } else { + console.error('No session_id found in URL'); + } + }, []); + + if (!sessionStatus) { + return
Loading...
; + } + + return ( +
+ {sessionStatus.status === 'open' && ( +
+

Checkout is still open

+
+ )} + {sessionStatus.status === 'complete' && ( +
+

Success!

+

Payment Status: {sessionStatus.payment_status}

+

Customer Email: {sessionStatus.customer_email}

+
+ )} +
+ ); +}; + +export default Checkout; diff --git a/src/pages/Payement/PaymentForm/PaymentForm.jsx b/src/pages/Payement/PaymentForm/PaymentForm.jsx index 59231c4..628ba22 100644 --- a/src/pages/Payement/PaymentForm/PaymentForm.jsx +++ b/src/pages/Payement/PaymentForm/PaymentForm.jsx @@ -1,49 +1,97 @@ -import React, { useState } from 'react'; -import { CardElement, useStripe, useElements } from '@stripe/react-stripe-js'; +import { useCallback, useState, useEffect } from 'react'; +import { useLocation, useNavigate } from 'react-router-dom'; +import Navbar from '../../../components/navbar/Navbar'; +import PropTypes from 'prop-types'; +import { loadStripe } from '@stripe/stripe-js'; +import { EmbeddedCheckoutProvider, EmbeddedCheckout } from '@stripe/react-stripe-js'; import styles from './PaymentForm.module.scss'; -const PaymentForm = ({ groups, closeModal }) => { - const stripe = useStripe(); - const elements = useElements(); - const [errorMessage, setErrorMessage] = useState(null); - const [successMessage, setSuccessMessage] = useState(null); +const stripePromise = loadStripe("pk_test_51PyIYTP3VLLeb9GlXCKgD4ylbemZPx72I3HkEAu0bRtcsfK31nqb3WtUbXKXUcKmyfrxKLfuJzZCPyp7Ymtlq9zy00c7VmkL6G"); - const handleSubmit = async (event) => { - event.preventDefault(); +const PackageNumber = (selectedPackage) => { + switch (selectedPackage) { + case 'Gratuit': + return 1; + case 'Standard': + return 2; + case 'Premium': + return 3; + default: + return 1; + } +}; - if (!stripe || !elements) return; +const CheckoutForm = ({ email }) => { + const location = useLocation(); + const [clientSecret, setClientSecret] = useState(''); + const queryParams = new URLSearchParams(location.search); + const selectedPackage = queryParams.get('package'); + + const fetchClientSecret = useCallback(() => { + return fetch('http://192.168.68.114:3000/get-checkout-session', { + method: 'GET', + headers: { + 'Content-Type': 'application/json', + 'permission': PackageNumber(selectedPackage), + 'email': email, + }, + }) + .then((res) => res.json()) + .then((data) => { + if (data && data.clientSecret) { + setClientSecret(data.clientSecret); + } + }) + .catch((error) => { + console.error('Error fetching client secret:', error); + }); + }, [selectedPackage, email]); + + useEffect(() => { + fetchClientSecret(); + + + }, [fetchClientSecret]); + + + + return { clientSecret }; +}; + + + +const PaymentForm = ({ user }) => { + const navigate = useNavigate(); + const { clientSecret } = CheckoutForm({ email: user.email }); + + const options = { clientSecret }; - const cardElement = elements.getElement(CardElement); - const { error, paymentMethod } = await stripe.createPaymentMethod({ - type: 'card', - card: cardElement, - }); - if (error) { - setErrorMessage(error.message); - setSuccessMessage(null); - } else { - setSuccessMessage(`Paiement réussi! Méthode de paiement ID: ${paymentMethod.id}`); - setErrorMessage(null); - // Ici, vous devriez également envoyer paymentMethod.id à votre serveur - // Fermer la modal après un court délai ou après l'envoi à votre serveur - setTimeout(() => closeModal(), 2000); - } - }; return ( -
- - {errorMessage &&

{errorMessage}

} - {successMessage &&

{successMessage}

} - - - +
+ navigate('/Pricing')} + backButtonText="Retour" + /> +
+ + + +
+
); }; -export default PaymentForm; +PaymentForm.propTypes = { + user: PropTypes.shape({ + uid: PropTypes.string.isRequired, + email: PropTypes.string, + photoURL: PropTypes.string, + }).isRequired, +}; + +export default PaymentForm; \ No newline at end of file diff --git a/src/pages/Payement/PaymentForm/PaymentForm.module.scss b/src/pages/Payement/PaymentForm/PaymentForm.module.scss index 0bbf541..95748db 100644 --- a/src/pages/Payement/PaymentForm/PaymentForm.module.scss +++ b/src/pages/Payement/PaymentForm/PaymentForm.module.scss @@ -1,19 +1,7 @@ -.modal { - position: fixed; - top: 0; - left: 0; - right: 0; - bottom: 0; - background: rgba(0, 0, 0, 0.5); - display: flex; - justify-content: center; - align-items: center; - } - - .modalContent { - background: #fff; - padding: 20px; - border-radius: 8px; - width: 300px; - } - \ No newline at end of file +.container{ + margin-top: 5.5rem; +} + +.Footer-PoweredBy-Text{ + display: none; +} \ No newline at end of file diff --git a/src/pages/Payement/Pricing.jsx b/src/pages/Payement/Pricing.jsx deleted file mode 100644 index f82b4df..0000000 --- a/src/pages/Payement/Pricing.jsx +++ /dev/null @@ -1,68 +0,0 @@ -import React, { useState } from 'react'; -import styles from './Pricing.module.scss'; -import Navbar from '../../components/navbar/Navbar'; -import { useNavigate } from 'react-router-dom'; -import PropTypes from 'prop-types'; -import { loadStripe } from '@stripe/stripe-js'; -import { Elements } from '@stripe/react-stripe-js'; -import PaymentForm from './PaymentForm/PaymentForm'; - -const stripePromise = loadStripe('pk_test_51PyIYTP3VLLeb9GlXCKgD4ylbemZPx72I3HkEAu0bRtcsfK31nqb3WtUbXKXUcKmyfrxKLfuJzZCPyp7Ymtlq9zy00c7VmkL6G'); - -const Pricing = ({ user }) => { - const navigate = useNavigate(); - const [selectedPackage, setSelectedPackage] = useState(null); - const [isModalOpen, setIsModalOpen] = useState(false); - - const groups = [ - { title: 'Gratuit', price: '0 €', features: ['Accès limité', 'Support par e-mail'] }, - { title: 'Standard', price: '2 €', features: ['Accès complet', 'Support prioritaire'] }, - { title: 'Premium', price: '9 €', features: ['Accès illimité', 'Support 24/7'] }, - ]; - - const handleSubscribe = (pkg) => { - setSelectedPackage(pkg); - setIsModalOpen(true); - }; - - return ( -
- navigate('/Dashboard')} - /> - - {groups.map((pkg, index) => ( -
-

{pkg.title}

-

{pkg.price}

-
    - {pkg.features.map((feature, idx) => ( -
  • {feature}
  • - ))} -
- -
- ))} - - {isModalOpen && ( -
-
-

Paiement pour {selectedPackage.title}

- - setIsModalOpen(false)} /> - -
-
- )} -
- ); -}; - -Pricing.propTypes = { - user: PropTypes.object.isRequired, -}; - -export default Pricing; diff --git a/src/pages/Payement/Pricing/Pricing.jsx b/src/pages/Payement/Pricing/Pricing.jsx new file mode 100644 index 0000000..7f025c3 --- /dev/null +++ b/src/pages/Payement/Pricing/Pricing.jsx @@ -0,0 +1,52 @@ +import styles from './Pricing.module.scss'; +import Navbar from '../../../components/navbar/Navbar'; +import { useNavigate } from 'react-router-dom'; +import PropTypes from 'prop-types'; + +const Pricing = ({ user }) => { + const navigate = useNavigate(); + + const groups = [ + { title: 'Gratuit', price: '0 €', features: ['Accès limité', 'Support par e-mail'] }, + { title: 'Standard', price: '2 €', features: ['Accès complet', 'Support prioritaire'] }, + { title: 'Premium', price: '9 €', features: ['Accès illimité', 'Support 24/7'] }, + ]; + + const handleSubscribe = (pkg) => { + navigate(`/payement?package=${pkg.title}`); + }; + + return ( +
+ navigate('/Dashboard')} + /> + +
+ {groups.map((pkg, index) => ( +
+

{pkg.title}

+

{pkg.price}

+
    + {pkg.features.map((feature, idx) => ( +
  • {feature}
  • + ))} +
+ +
+ ))} +
+
+ ); +}; + +Pricing.propTypes = { + user: PropTypes.object.isRequired, +}; + +export default Pricing; diff --git a/src/pages/Payement/Pricing.module.scss b/src/pages/Payement/Pricing/Pricing.module.scss similarity index 70% rename from src/pages/Payement/Pricing.module.scss rename to src/pages/Payement/Pricing/Pricing.module.scss index b9d81a9..259d960 100644 --- a/src/pages/Payement/Pricing.module.scss +++ b/src/pages/Payement/Pricing/Pricing.module.scss @@ -2,35 +2,43 @@ $primary-color: #000; $secondary-color: #fff; .pricingContainer { + margin-top: 5rem; display: flex; justify-content: center; - gap: 20px; - padding: 40px; + flex-direction: row; + gap: 2rem; + padding: 4rem; background-color: $secondary-color; } +.packageList{ + display: flex; + flex-direction: row; + gap: 2rem; +} + .packageCard { background-color: $primary-color; color: $secondary-color; - border-radius: 8px; - padding: 20px; + border-radius: .8rem; + padding: 5rem; text-align: center; box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1); transition: transform 0.3s; &:hover { - transform: scale(1.05); + transform: scale(1.01); } } .title { - font-size: 24px; - margin-bottom: 10px; + font-size: 2rem; + margin-bottom: .6rem; } .price { - font-size: 32px; - margin: 10px 0; + font-size: 2.5rem; + margin: 1rem 0; } .features { @@ -52,9 +60,4 @@ $secondary-color: #fff; cursor: pointer; font-size: 16px; transition: background-color 0.3s; - - &:hover { - background-color: $primary-color; - color: $secondary-color; - } -} +} \ No newline at end of file diff --git a/src/service/firebase.jsx b/src/service/firebase.jsx index 5f84bdd..d8245a1 100644 --- a/src/service/firebase.jsx +++ b/src/service/firebase.jsx @@ -37,4 +37,16 @@ const getUserSubdomain = async (userId) => { } }; -export { auth, googleProvider, signInWithPopup, getUserSubdomain, app }; + +const getUserSubscription = async (userId) => { + const userDocRef = doc(db, 'users', userId); + const userDocSnap = await getDoc(userDocRef); + if (userDocSnap.exists()) { + const userData = userDocSnap.data(); + return userData.subscription; + } else { + throw new Error("No such document!"); + } +}; + +export { auth, googleProvider, signInWithPopup, getUserSubdomain, getUserSubscription, app }; From 0e85a6acf30eb217c765415406147dcde8edf39c Mon Sep 17 00:00:00 2001 From: AntoninoP Date: Thu, 26 Sep 2024 16:57:34 +0200 Subject: [PATCH 08/17] check server creation --- src/pages/CreateServer/CreateServer.jsx | 93 ++++++++++++++----------- 1 file changed, 54 insertions(+), 39 deletions(-) diff --git a/src/pages/CreateServer/CreateServer.jsx b/src/pages/CreateServer/CreateServer.jsx index ecbf540..98b5e54 100644 --- a/src/pages/CreateServer/CreateServer.jsx +++ b/src/pages/CreateServer/CreateServer.jsx @@ -2,60 +2,75 @@ import styles from './CreateServer.module.scss'; import PropTypes from "prop-types"; import Navbar from '../../components/navbar/Navbar'; import { useNavigate } from 'react-router-dom'; -import bedrockimg from '../../assets/bedrock.png'; +import bedrockimg from '../../assets/bedrock.png'; import modedimg from '../../assets/moded.png'; -import javaimg from '../../assets/java.png' +import javaimg from '../../assets/java.png'; +import { getUserSubscription } from "../../service/firebase"; +import { useState, useEffect } from 'react'; const CreateServer = ({ user }) => { const navigate = useNavigate(); + const [subscription, setSubscription] = useState(0); + + useEffect(() => { + const fetchSubscription = async () => { + try { + const userSubscription = await getUserSubscription(user.uid); + setSubscription(userSubscription || 0); + } catch (error) { + console.error('Error fetching subscription:', error); + } + }; + + fetchSubscription(); + }, [user.uid]); - return (
navigate('/dashboard')} - /> + user={user} + hasShadow={false} + showBackButton={true} + onBackClick={() => navigate('/dashboard')} + /> -
-
Création du serveur
-
De quelle façon voulez-vous jouer ?
-
-
navigate('/CreateServer/java')}> - -
Java Edition
-
Découvrez la version classique de Minecraft sur PC, avec un large éventail de mises à jour et de fonctionnalités, couvrant plus de vingt versions !
- -
-
navigate('/CreateServer/bedrock')}> - -
Mini-jeu
-
Plongez dans Minecraft avec des cartes personnalisées et des règles uniques, en solo ou avec vos amis.
- -
-
navigate('/CreateServer/modpack')}> - -
Minecraft Modé
-
Explorez la version modifiée de Minecraft sur PC, avec des modpacks riches et variés, contenant plus de 200 mods pour une expérience de jeu personnalisée.
- -
-
+
+
Création du serveur
+
De quelle façon voulez-vous jouer ?
+
+
(subscription > 0) ? navigate('/CreateServer/java') : navigate('/payement?package=Gratuit')}> + Java Edition +
Java Edition
+
Découvrez la version classique de Minecraft sur PC, avec un large éventail de mises à jour et de fonctionnalités, couvrant plus de vingt versions !
+ +
+ +
(subscription > 1) ? navigate('/CreateServer/bedrock') : navigate('/payement?package=Standard')}> + Mini-jeu +
Mini-jeu
+
Plongez dans Minecraft avec des cartes personnalisées et des règles uniques, en solo ou avec vos amis.
+ +
+ +
(subscription > 2) ? navigate('/CreateServer/bedrock') : navigate('/payement?package=Premium')}> + Minecraft Modé +
Minecraft Modé
+
Explorez la version modifiée de Minecraft sur PC, avec des modpacks riches et variés, contenant plus de 200 mods pour une expérience de jeu personnalisée.
+ +
+
); }; CreateServer.propTypes = { - user: PropTypes.oneOfType([ - PropTypes.shape({ - uid: PropTypes.string.isRequired, - displayName: PropTypes.string, - email: PropTypes.string, - photoURL: PropTypes.string, - }), - ]), + user: PropTypes.shape({ + uid: PropTypes.string.isRequired, + displayName: PropTypes.string, + email: PropTypes.string, + photoURL: PropTypes.string, + }).isRequired, }; export default CreateServer; From 8e5ffcd6e47d41986ba02a9922c46897eb3e72f2 Mon Sep 17 00:00:00 2001 From: AntoninoP Date: Thu, 26 Sep 2024 17:31:06 +0200 Subject: [PATCH 09/17] protected roads --- src/App.jsx | 40 +++++++++++++++++++++++++++++++++++----- 1 file changed, 35 insertions(+), 5 deletions(-) diff --git a/src/App.jsx b/src/App.jsx index a6212b3..5e654be 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -2,7 +2,7 @@ import { useEffect, useState, Suspense, lazy } from 'react'; import { BrowserRouter as Router, Route, Routes, Navigate } from 'react-router-dom'; import { ToastContainer } from 'react-toastify'; import 'react-toastify/dist/ReactToastify.css'; -import { auth } from './service/firebase'; +import { auth , getUserSubscription } from './service/firebase'; import styles from './App.module.scss'; import Loading from './pages/Loading/loading'; import Pricing from './pages/Payement/Pricing/Pricing'; @@ -21,6 +21,7 @@ const App = () => { const [user, setUser] = useState(() => JSON.parse(localStorage.getItem('user')) || null); const [loading, setLoading] = useState(true); const [showLoading, setShowLoading] = useState(false); + const [subscription, setSubscription] = useState(0); useEffect(() => { const timeoutId = setTimeout(() => setShowLoading(true), 6000); @@ -28,10 +29,11 @@ const App = () => { const unsubscribe = auth.onAuthStateChanged((user) => { if (user) { localStorage.setItem('user', JSON.stringify(user)); + setUser(user); } else { localStorage.removeItem('user'); + setUser(null); } - setUser(user); setLoading(false); clearTimeout(timeoutId); }); @@ -42,6 +44,20 @@ const App = () => { }; }, []); + useEffect(() => { + if (user) { + const fetchSubscription = async () => { + try { + const userSubscription = await getUserSubscription(user.uid); + setSubscription(userSubscription || 0); + } catch (error) { + console.error('Error fetching subscription:', error); + } + }; + fetchSubscription(); + } + }, [user]); + if (loading && showLoading) { return ; } @@ -51,20 +67,34 @@ const App = () => {
}> + {/* Public Route */} : } /> + + {/* Protected Routes (Requires Authentication) */} : } /> : } /> - : } /> - : } /> - : } /> + + {/* Routes with Subscription Levels */} + 0 ? : ) : } /> + 1 ? : ) : } /> + 2 ? : ) : } /> + + {/* Server Details Route */} : } /> + + {/* Pricing and Payment */} : } /> : } /> : } /> + + {/* Default Route */} } /> + + {/* Catch-all route */} : } /> + Date: Thu, 26 Sep 2024 17:41:40 +0200 Subject: [PATCH 10/17] final commit --- src/pages/Payement/Checkout.jsx | 2 +- src/pages/Payement/PaymentForm/PaymentForm.jsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/Payement/Checkout.jsx b/src/pages/Payement/Checkout.jsx index 120c0a6..625c9e3 100644 --- a/src/pages/Payement/Checkout.jsx +++ b/src/pages/Payement/Checkout.jsx @@ -12,7 +12,7 @@ const Checkout = () => { if (session_id) { const fetchSessionStatus = async () => { try { - const response = await fetch(`http://192.168.68.114:3000/get-session-status?session_id=${session_id}`); + const response = await fetch(`https://www.servii.fr/api/get-session-status?session_id=${session_id}`); const data = await response.json(); setSessionStatus(data); } catch (error) { diff --git a/src/pages/Payement/PaymentForm/PaymentForm.jsx b/src/pages/Payement/PaymentForm/PaymentForm.jsx index 628ba22..d2ecc07 100644 --- a/src/pages/Payement/PaymentForm/PaymentForm.jsx +++ b/src/pages/Payement/PaymentForm/PaymentForm.jsx @@ -28,7 +28,7 @@ const CheckoutForm = ({ email }) => { const selectedPackage = queryParams.get('package'); const fetchClientSecret = useCallback(() => { - return fetch('http://192.168.68.114:3000/get-checkout-session', { + return fetch('https://www.servii.fr/api/get-checkout-session', { method: 'GET', headers: { 'Content-Type': 'application/json', From 94c3655a5ceec7ca1e1cabc49e2bb7b5261f1381 Mon Sep 17 00:00:00 2001 From: AntoninoP Date: Fri, 27 Sep 2024 11:15:11 +0200 Subject: [PATCH 11/17] + pricing menu --- src/assets/tier/basique.png | Bin 0 -> 4601 bytes src/assets/tier/premium.png | Bin 0 -> 5199 bytes src/assets/tier/standard.png | Bin 0 -> 6634 bytes src/pages/Payement/Pricing/Pricing.jsx | 58 +++++++++++++--- .../Payement/Pricing/Pricing.module.scss | 63 +++++++++++++----- 5 files changed, 95 insertions(+), 26 deletions(-) create mode 100644 src/assets/tier/basique.png create mode 100644 src/assets/tier/premium.png create mode 100644 src/assets/tier/standard.png diff --git a/src/assets/tier/basique.png b/src/assets/tier/basique.png new file mode 100644 index 0000000000000000000000000000000000000000..6843c9f783b2c561378ba9638149a5fdb05c39a2 GIT binary patch literal 4601 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 TcTM=`ib_aOL?CwuDgXZfnmDcL literal 0 HcmV?d00001 diff --git a/src/assets/tier/premium.png b/src/assets/tier/premium.png new file mode 100644 index 0000000000000000000000000000000000000000..2d01e3398d836aa16813cbddcefafdfec13e8510 GIT binary patch literal 5199 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 LQm|ixFO>VgU-gm? literal 0 HcmV?d00001 diff --git a/src/assets/tier/standard.png b/src/assets/tier/standard.png new file mode 100644 index 0000000000000000000000000000000000000000..42b15308ebca31c61fea9005d46f3f7035b38e6a GIT binary patch literal 6634 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 y@0f7<;vV8a3I7AO$9L-h literal 0 HcmV?d00001 diff --git a/src/pages/Payement/Pricing/Pricing.jsx b/src/pages/Payement/Pricing/Pricing.jsx index 7f025c3..cae1703 100644 --- a/src/pages/Payement/Pricing/Pricing.jsx +++ b/src/pages/Payement/Pricing/Pricing.jsx @@ -3,13 +3,50 @@ import Navbar from '../../../components/navbar/Navbar'; import { useNavigate } from 'react-router-dom'; import PropTypes from 'prop-types'; +import basique from '../../../assets/tier/basique.png'; +import standard from '../../../assets/tier/standard.png'; +import premium from '../../../assets/tier/premium.png'; + const Pricing = ({ user }) => { const navigate = useNavigate(); const groups = [ - { title: 'Gratuit', price: '0 €', features: ['Accès limité', 'Support par e-mail'] }, - { title: 'Standard', price: '2 €', features: ['Accès complet', 'Support prioritaire'] }, - { title: 'Premium', price: '9 €', features: ['Accès illimité', 'Support 24/7'] }, + { + title: 'Basique', + price: '0 €', + features: [ + { name: 'Accès au serveurs vanilla', isAvailable: true }, + { name: 'Personalisation complète de vos serveurs', isAvailable: true }, + { name: 'Support par e-mail', isAvailable: true }, + { name: 'Accès au serveurs Mini-jeux', isAvailable: false }, + { name: 'Accès au serveurs de modpacks', isAvailable: false } + ], + image: basique + }, + { + title: 'Standard', + price: '2 €', + features: [ + { name: 'Accès au serveurs vanilla', isAvailable: true }, + { name: 'Personalisation complète de vos serveurs', isAvailable: true }, + { name: 'Support par e-mail', isAvailable: true }, + { name: 'Accès au serveurs Mini-jeux', isAvailable: true }, + { name: 'Accès au serveurs de modpacks', isAvailable: false } + ], + image: standard + }, + { + title: 'Premium', + price: '9 €', + features: [ + { name: 'Accès au serveurs vanilla', isAvailable: true }, + { name: 'Personalisation complète de vos serveurs', isAvailable: true }, + { name: 'Support par e-mail', isAvailable: true }, + { name: 'Accès au serveurs Mini-jeux', isAvailable: true }, + { name: 'Accès au serveurs de modpacks', isAvailable: true } + ], + image: premium + }, ]; const handleSubscribe = (pkg) => { @@ -28,16 +65,21 @@ const Pricing = ({ user }) => {
{groups.map((pkg, index) => (
+
+ {pkg.title} +

{pkg.title}

{pkg.price}

-
    - {pkg.features.map((feature, idx) => ( -
  • {feature}
  • - ))} -
+
    + {pkg.features.map((feature, idx) => ( +
  • + {feature.isAvailable ? '✔️' : '❌'} {feature.name} +
  • + ))} +
))}
diff --git a/src/pages/Payement/Pricing/Pricing.module.scss b/src/pages/Payement/Pricing/Pricing.module.scss index 259d960..9454bb7 100644 --- a/src/pages/Payement/Pricing/Pricing.module.scss +++ b/src/pages/Payement/Pricing/Pricing.module.scss @@ -8,56 +8,83 @@ $secondary-color: #fff; flex-direction: row; gap: 2rem; padding: 4rem; - background-color: $secondary-color; } -.packageList{ +.packageList { display: flex; flex-direction: row; - gap: 2rem; + gap: 3rem; + } .packageCard { - background-color: $primary-color; - color: $secondary-color; - border-radius: .8rem; - padding: 5rem; + position: relative; + background-color: white; + color: black; + border-radius: 0.5rem; + padding: 2rem 1rem; text-align: center; - box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1); - transition: transform 0.3s; - + transition: transform 0.3s, box-shadow 0.3s; + border: 0.15rem solid #dfdcd5; + &:hover { transform: scale(1.01); + box-shadow: 0 3px 10px rgba(0, 0, 0, 0.1); + } + + &:hover .button { + visibility: visible; + opacity: 1; + } + + img { + position: center; + width: 4.5rem; + height: auto; } } .title { font-size: 2rem; - margin-bottom: .6rem; + margin-bottom: 2rem; + font-weight: 500; } .price { - font-size: 2.5rem; + font-size: 2rem; margin: 1rem 0; } .features { list-style-type: none; padding: 0; - margin: 20px 0; - + margin: 1.5rem 0; + text-align: left; li { - margin: 5px 0; + margin: .8rem 0; } } .button { - background-color: $secondary-color; + visibility: hidden; + opacity: 0; color: $primary-color; border: none; border-radius: 4px; padding: 10px 20px; cursor: pointer; font-size: 16px; - transition: background-color 0.3s; -} \ No newline at end of file + margin-top: 1rem; + background-color: #2F2F2F; + color: white; + + &:hover { + background-color: darken(#2F2F2F, 10%); + } +} + +@media (max-width: 700px) { + .packageList { + flex-direction: column; + } +} From d4d718892c08b6d71484cbd35bd8aabc27ede4e6 Mon Sep 17 00:00:00 2001 From: AntoninoP Date: Fri, 27 Sep 2024 12:45:07 +0200 Subject: [PATCH 12/17] new buy menu --- src/App.jsx | 2 +- .../Payement/PaymentForm/PaymentForm.jsx | 2 +- src/pages/Payement/Pricing/Pricing.jsx | 50 ++++-- .../Payement/Pricing/Pricing.module.scss | 166 +++++++++++------- 4 files changed, 141 insertions(+), 79 deletions(-) diff --git a/src/App.jsx b/src/App.jsx index 5e654be..65d7a33 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -75,7 +75,7 @@ const App = () => { : } /> {/* Routes with Subscription Levels */} - 0 ? : ) : } /> + 0 ? : ) : } /> 1 ? : ) : } /> 2 ? : ) : } /> diff --git a/src/pages/Payement/PaymentForm/PaymentForm.jsx b/src/pages/Payement/PaymentForm/PaymentForm.jsx index d2ecc07..6a417bc 100644 --- a/src/pages/Payement/PaymentForm/PaymentForm.jsx +++ b/src/pages/Payement/PaymentForm/PaymentForm.jsx @@ -6,7 +6,7 @@ import { loadStripe } from '@stripe/stripe-js'; import { EmbeddedCheckoutProvider, EmbeddedCheckout } from '@stripe/react-stripe-js'; import styles from './PaymentForm.module.scss'; -const stripePromise = loadStripe("pk_test_51PyIYTP3VLLeb9GlXCKgD4ylbemZPx72I3HkEAu0bRtcsfK31nqb3WtUbXKXUcKmyfrxKLfuJzZCPyp7Ymtlq9zy00c7VmkL6G"); +const stripePromise = loadStripe("pk_live_51PyIYTP3VLLeb9GlHpiK8p5lVC3kGPvAAb0Nn8m5qVAGzespGfGlDoP8Wmw4HbZJJgqxxHEIG7MJwP4IAWCpRBi100dYMpV1gv"); const PackageNumber = (selectedPackage) => { switch (selectedPackage) { diff --git a/src/pages/Payement/Pricing/Pricing.jsx b/src/pages/Payement/Pricing/Pricing.jsx index cae1703..bb5b95f 100644 --- a/src/pages/Payement/Pricing/Pricing.jsx +++ b/src/pages/Payement/Pricing/Pricing.jsx @@ -7,28 +7,33 @@ import basique from '../../../assets/tier/basique.png'; import standard from '../../../assets/tier/standard.png'; import premium from '../../../assets/tier/premium.png'; +// Import des icônes +import { FaCheckCircle, FaTimesCircle } from 'react-icons/fa'; + const Pricing = ({ user }) => { const navigate = useNavigate(); const groups = [ { title: 'Basique', - price: '0 €', + price: '2€', + description: 'Fait pour les joueurs vanilla sur un petit serveur. 2gb de Ram', features: [ { name: 'Accès au serveurs vanilla', isAvailable: true }, - { name: 'Personalisation complète de vos serveurs', isAvailable: true }, + { name: 'Personalisation complète', isAvailable: true }, { name: 'Support par e-mail', isAvailable: true }, { name: 'Accès au serveurs Mini-jeux', isAvailable: false }, - { name: 'Accès au serveurs de modpacks', isAvailable: false } + { name: 'Accès au serveurs de modpacks', isAvailable: false }, ], image: basique }, { title: 'Standard', - price: '2 €', + price: '5€', + description: 'Fait pour les joueurs de mini-jeux et vanilla ! 5gb de Ram', features: [ { name: 'Accès au serveurs vanilla', isAvailable: true }, - { name: 'Personalisation complète de vos serveurs', isAvailable: true }, + { name: 'Personalisation complète', isAvailable: true }, { name: 'Support par e-mail', isAvailable: true }, { name: 'Accès au serveurs Mini-jeux', isAvailable: true }, { name: 'Accès au serveurs de modpacks', isAvailable: false } @@ -37,10 +42,11 @@ const Pricing = ({ user }) => { }, { title: 'Premium', - price: '9 €', + price: '9€', + description: 'Conçu pour les joueurs de modpacks robustes. 10gb de Ram', features: [ { name: 'Accès au serveurs vanilla', isAvailable: true }, - { name: 'Personalisation complète de vos serveurs', isAvailable: true }, + { name: 'Personalisation complète', isAvailable: true }, { name: 'Support par e-mail', isAvailable: true }, { name: 'Accès au serveurs Mini-jeux', isAvailable: true }, { name: 'Accès au serveurs de modpacks', isAvailable: true } @@ -50,7 +56,7 @@ const Pricing = ({ user }) => { ]; const handleSubscribe = (pkg) => { - navigate(`/payement?package=${pkg.title}`); + navigate(`/payment?package=${pkg.title}`); }; return ( @@ -65,21 +71,35 @@ const Pricing = ({ user }) => {
{groups.map((pkg, index) => (
-
+
+

{pkg.title}

{pkg.title}
-

{pkg.title}

-

{pkg.price}

- + +
+

{pkg.price}

+ /mois +
+ +
{pkg.description}
+
    +
    CE QUI EST INCLUS
    + {pkg.features.map((feature, idx) => (
  • - {feature.isAvailable ? '✔️' : '❌'} {feature.name} + {feature.isAvailable ? ( + + ) : ( + + )} + {feature.name}
  • ))}
+
))}
diff --git a/src/pages/Payement/Pricing/Pricing.module.scss b/src/pages/Payement/Pricing/Pricing.module.scss index 9454bb7..7634cb4 100644 --- a/src/pages/Payement/Pricing/Pricing.module.scss +++ b/src/pages/Payement/Pricing/Pricing.module.scss @@ -2,7 +2,7 @@ $primary-color: #000; $secondary-color: #fff; .pricingContainer { - margin-top: 5rem; + margin-top: 4rem; display: flex; justify-content: center; flex-direction: row; @@ -14,77 +14,119 @@ $secondary-color: #fff; display: flex; flex-direction: row; gap: 3rem; - } .packageCard { - position: relative; - background-color: white; - color: black; - border-radius: 0.5rem; - padding: 2rem 1rem; - text-align: center; - transition: transform 0.3s, box-shadow 0.3s; - border: 0.15rem solid #dfdcd5; - - &:hover { - transform: scale(1.01); - box-shadow: 0 3px 10px rgba(0, 0, 0, 0.1); - } - - &:hover .button { - visibility: visible; - opacity: 1; - } - - img { - position: center; - width: 4.5rem; - height: auto; - } -} - -.title { - font-size: 2rem; - margin-bottom: 2rem; - font-weight: 500; -} - -.price { - font-size: 2rem; - margin: 1rem 0; -} - -.features { - list-style-type: none; - padding: 0; - margin: 1.5rem 0; - text-align: left; - li { - margin: .8rem 0; - } -} - -.button { - visibility: hidden; - opacity: 0; + background-color: $secondary-color; color: $primary-color; - border: none; - border-radius: 4px; - padding: 10px 20px; - cursor: pointer; - font-size: 16px; - margin-top: 1rem; - background-color: #2F2F2F; - color: white; + border-radius: .5rem; + padding: 0rem 2rem 2rem 2rem; + text-align: start; + transition: transform 0.3s, box-shadow 0.3s; + border: 0.15rem solid #dfdcd5; + box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1); + + &.recommended { + border-color: #76e4c8; + box-shadow: 0 6px 20px rgba(0, 228, 184, 0.5); + } &:hover { - background-color: darken(#2F2F2F, 10%); + transform: scale(1.02); + box-shadow: 0 6px 20px rgba(0, 0, 0, 0.15); + } + + .packageCardheader { + display: flex; + justify-content: space-between; + align-items: center; + + img { + width: 3rem; + height: auto; + margin-left: 0.5rem; + } + } + + .title { + font-size: 1.8rem; + font-weight: 600; + } + + .priceContainer { + display: flex; + justify-content: start; + align-items: baseline; + margin-bottom:-1rem; + margin-top: -4rem; + + .price { + font-size: 2.5rem; + font-weight: bold; + color: #2F2F2F; + } + + .mensuel { + font-size: 1rem; + margin-left: 0.25rem; + color: #7c7c7c; + } + } + + .description { + width: 16.5rem; + height: 4rem; + font-size: 1rem; + margin-top: -0.25rem; + color: #3c3c3c; + } + + .features { + list-style-type: none; + padding: 0; + margin: 1rem 0; + + li { + margin: 0.5rem 0; + font-size: 1rem; + color: #555; + } + } + + .inclut { + font-size: 1.25rem; + margin-bottom: 1rem; + font-weight: 500; + } + + .button { + color: $primary-color; + border: none; + border-radius: 2px; + padding: 12px 24px; + cursor: pointer; + font-size: 1rem; + margin-top: 1rem; + background-color: #2F2F2F; + color: $secondary-color; + transition: background-color 0.3s, transform 0.2s; + width: 100%; + + &:hover { + background-color: darken(#2F2F2F, 10%); + transform: translateY(-2px); + } + } + + hr { + border: 0.1rem solid #dfdcd5; + margin: 1.5rem -2rem; } } + @media (max-width: 700px) { .packageList { flex-direction: column; - } + } } From 9925a5444552fab7ca70801ff56825e406675494 Mon Sep 17 00:00:00 2001 From: AntoninoP Date: Fri, 27 Sep 2024 13:51:41 +0200 Subject: [PATCH 13/17] fix server card link --- src/pages/CreateServer/CreateServer.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/CreateServer/CreateServer.jsx b/src/pages/CreateServer/CreateServer.jsx index 98b5e54..4b4ef32 100644 --- a/src/pages/CreateServer/CreateServer.jsx +++ b/src/pages/CreateServer/CreateServer.jsx @@ -52,7 +52,7 @@ const CreateServer = ({ user }) => {
-
(subscription > 2) ? navigate('/CreateServer/bedrock') : navigate('/payement?package=Premium')}> +
(subscription > 2) ? navigate('/CreateServer/modpack') : navigate('/payement?package=Premium')}> Minecraft Modé
Minecraft Modé
Explorez la version modifiée de Minecraft sur PC, avec des modpacks riches et variés, contenant plus de 200 mods pour une expérience de jeu personnalisée.
From 8518f5bb0aa1893782c7fbb211899efa202ffa7a Mon Sep 17 00:00:00 2001 From: Charles Le Maux Date: Fri, 27 Sep 2024 14:53:16 +0200 Subject: [PATCH 14/17] [~] frabric -> fabric --- src/components/serverCards/DefaultServerCard.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/serverCards/DefaultServerCard.jsx b/src/components/serverCards/DefaultServerCard.jsx index efb00e4..224d5f5 100644 --- a/src/components/serverCards/DefaultServerCard.jsx +++ b/src/components/serverCards/DefaultServerCard.jsx @@ -10,7 +10,7 @@ const ServerCard = ({ status, version, name, framework, onRunClick, onStopClick, const getFrameworkSource = () => { switch (framework) { - case 'frabric': + case 'fabric': return fabric; case 'forge': return forge; From 67f1a50fa5664be0205d767fcc67361874ce5093 Mon Sep 17 00:00:00 2001 From: AntoninoP Date: Fri, 27 Sep 2024 14:56:27 +0200 Subject: [PATCH 15/17] fix road --- src/App.jsx | 13 ++++++++++--- src/service/api.tsx | 2 +- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/App.jsx b/src/App.jsx index 65d7a33..899b0a6 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -2,7 +2,7 @@ import { useEffect, useState, Suspense, lazy } from 'react'; import { BrowserRouter as Router, Route, Routes, Navigate } from 'react-router-dom'; import { ToastContainer } from 'react-toastify'; import 'react-toastify/dist/ReactToastify.css'; -import { auth , getUserSubscription } from './service/firebase'; +import { auth, getUserSubscription } from './service/firebase'; import styles from './App.module.scss'; import Loading from './pages/Loading/loading'; import Pricing from './pages/Payement/Pricing/Pricing'; @@ -22,6 +22,7 @@ const App = () => { const [loading, setLoading] = useState(true); const [showLoading, setShowLoading] = useState(false); const [subscription, setSubscription] = useState(0); + const [loadingSubscription, setLoadingSubscription] = useState(true); useEffect(() => { const timeoutId = setTimeout(() => setShowLoading(true), 6000); @@ -48,17 +49,23 @@ const App = () => { if (user) { const fetchSubscription = async () => { try { + setLoadingSubscription(true); const userSubscription = await getUserSubscription(user.uid); + console.log('User Subscription:', userSubscription); setSubscription(userSubscription || 0); } catch (error) { console.error('Error fetching subscription:', error); + } finally { + setLoadingSubscription(false); } }; fetchSubscription(); + } else { + setLoadingSubscription(false); } }, [user]); - - if (loading && showLoading) { + + if (loading || loadingSubscription) { return ; } diff --git a/src/service/api.tsx b/src/service/api.tsx index 0786875..2464f96 100644 --- a/src/service/api.tsx +++ b/src/service/api.tsx @@ -94,7 +94,7 @@ class serviiApi { public static async fetchModpacks(): Promise { try { - const response = await fetch(`${apiUrl}/modpacks`, { + const response = await fetch(`https://www.servii.fr/api/modpacks/image/a-metadata.txt`, { method: 'GET', }); const json = await response.json(); From d80a36557646756c55c8a3b186f5b5b20df02658 Mon Sep 17 00:00:00 2001 From: AntoninoP Date: Fri, 27 Sep 2024 15:14:36 +0200 Subject: [PATCH 16/17] fix prix et fix bug routes --- src/App.jsx | 2 +- src/pages/Payement/PaymentForm/PaymentForm.jsx | 2 +- src/pages/Payement/Pricing/Pricing.jsx | 15 +++++++++------ 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/src/App.jsx b/src/App.jsx index 899b0a6..433a668 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -91,7 +91,7 @@ const App = () => { {/* Pricing and Payment */} : } /> - : } /> + : } /> : } /> {/* Default Route */} diff --git a/src/pages/Payement/PaymentForm/PaymentForm.jsx b/src/pages/Payement/PaymentForm/PaymentForm.jsx index 6a417bc..1ef9164 100644 --- a/src/pages/Payement/PaymentForm/PaymentForm.jsx +++ b/src/pages/Payement/PaymentForm/PaymentForm.jsx @@ -10,7 +10,7 @@ const stripePromise = loadStripe("pk_live_51PyIYTP3VLLeb9GlHpiK8p5lVC3kGPvAAb0Nn const PackageNumber = (selectedPackage) => { switch (selectedPackage) { - case 'Gratuit': + case 'Basique': return 1; case 'Standard': return 2; diff --git a/src/pages/Payement/Pricing/Pricing.jsx b/src/pages/Payement/Pricing/Pricing.jsx index bb5b95f..8c6cdbf 100644 --- a/src/pages/Payement/Pricing/Pricing.jsx +++ b/src/pages/Payement/Pricing/Pricing.jsx @@ -16,9 +16,10 @@ const Pricing = ({ user }) => { const groups = [ { title: 'Basique', - price: '2€', - description: 'Fait pour les joueurs vanilla sur un petit serveur. 2gb de Ram', + price: '2.99€', + description: 'Fait pour les joueurs vanilla sur un petit serveur.', features: [ + { name: '2gb de Ram', isAvailable: true }, { name: 'Accès au serveurs vanilla', isAvailable: true }, { name: 'Personalisation complète', isAvailable: true }, { name: 'Support par e-mail', isAvailable: true }, @@ -29,9 +30,10 @@ const Pricing = ({ user }) => { }, { title: 'Standard', - price: '5€', - description: 'Fait pour les joueurs de mini-jeux et vanilla ! 5gb de Ram', + price: '4.99€', + description: 'Fait pour les joueurs de mini-jeux et vanilla !', features: [ + { name: '5gb de Ram', isAvailable: true }, { name: 'Accès au serveurs vanilla', isAvailable: true }, { name: 'Personalisation complète', isAvailable: true }, { name: 'Support par e-mail', isAvailable: true }, @@ -42,9 +44,10 @@ const Pricing = ({ user }) => { }, { title: 'Premium', - price: '9€', - description: 'Conçu pour les joueurs de modpacks robustes. 10gb de Ram', + price: '9.99€', + description: 'Conçu pour les joueurs de modpacks robustes.', features: [ + { name: '10gb de Ram', isAvailable: true }, { name: 'Accès au serveurs vanilla', isAvailable: true }, { name: 'Personalisation complète', isAvailable: true }, { name: 'Support par e-mail', isAvailable: true }, From 960794fcc26903f5460c8d8f3d2377f1c95674e8 Mon Sep 17 00:00:00 2001 From: AntoninoP Date: Fri, 27 Sep 2024 15:39:33 +0200 Subject: [PATCH 17/17] fix css on tier cards last commit --- src/pages/Payement/Pricing/Pricing.jsx | 4 ++-- .../Payement/Pricing/Pricing.module.scss | 20 +++++-------------- 2 files changed, 7 insertions(+), 17 deletions(-) diff --git a/src/pages/Payement/Pricing/Pricing.jsx b/src/pages/Payement/Pricing/Pricing.jsx index 8c6cdbf..4963637 100644 --- a/src/pages/Payement/Pricing/Pricing.jsx +++ b/src/pages/Payement/Pricing/Pricing.jsx @@ -2,7 +2,6 @@ import styles from './Pricing.module.scss'; import Navbar from '../../../components/navbar/Navbar'; import { useNavigate } from 'react-router-dom'; import PropTypes from 'prop-types'; - import basique from '../../../assets/tier/basique.png'; import standard from '../../../assets/tier/standard.png'; import premium from '../../../assets/tier/premium.png'; @@ -13,6 +12,7 @@ import { FaCheckCircle, FaTimesCircle } from 'react-icons/fa'; const Pricing = ({ user }) => { const navigate = useNavigate(); + const groups = [ { title: 'Basique', @@ -80,7 +80,7 @@ const Pricing = ({ user }) => {
-

{pkg.price}

+
{pkg.price}
/mois
diff --git a/src/pages/Payement/Pricing/Pricing.module.scss b/src/pages/Payement/Pricing/Pricing.module.scss index 7634cb4..cee635f 100644 --- a/src/pages/Payement/Pricing/Pricing.module.scss +++ b/src/pages/Payement/Pricing/Pricing.module.scss @@ -20,22 +20,12 @@ $secondary-color: #fff; background-color: $secondary-color; color: $primary-color; border-radius: .5rem; - padding: 0rem 2rem 2rem 2rem; + padding: .5rem 2rem 2rem 2rem; text-align: start; transition: transform 0.3s, box-shadow 0.3s; border: 0.15rem solid #dfdcd5; box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1); - - &.recommended { - border-color: #76e4c8; - box-shadow: 0 6px 20px rgba(0, 228, 184, 0.5); - } - - &:hover { - transform: scale(1.02); - box-shadow: 0 6px 20px rgba(0, 0, 0, 0.15); - } - + .packageCardheader { display: flex; justify-content: space-between; @@ -57,8 +47,7 @@ $secondary-color: #fff; display: flex; justify-content: start; align-items: baseline; - margin-bottom:-1rem; - margin-top: -4rem; + margin-bottom: 1.5rem; .price { font-size: 2.5rem; @@ -77,7 +66,7 @@ $secondary-color: #fff; width: 16.5rem; height: 4rem; font-size: 1rem; - margin-top: -0.25rem; + margin-top: 0.25rem; color: #3c3c3c; } @@ -125,6 +114,7 @@ $secondary-color: #fff; } + @media (max-width: 700px) { .packageList { flex-direction: column;