-
-
- Adresse de connexion à vos serveurs :
-
- {" " + subdomain}.servii.fr
-
-
-
-
-
setSearchTerm(e.target.value)}
- className={styles.searchInput}
- />
-
- +
- Créer un nouveau serveur
-
-
-
-
- {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}
+ />
+
+ +
+ Créer un nouveau serveur
+
+
+
+
+ {favoriteServer ? (
+
+ handleRunServer(favoriteServer.name , favoriteServer.framework)}
+ 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 , server.framework)}
+ onStopClick={() => handleStopServer(server.name)}
+ onDeleteClick={() => {
+ setServerToDelete(server.name);
+ setModalOpen(true);
+ }}
+ subdomain={subdomain}
+ />
+ ))
+ ) : null}
+
+
+
+
setModalOpen(false)}
+ onDelete={handleDeleteServer}
+ />
);
};
diff --git a/src/pages/Payement/Checkout.jsx b/src/pages/Payement/Checkout.jsx
new file mode 100644
index 0000000..625c9e3
--- /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(`https://www.servii.fr/api/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
new file mode 100644
index 0000000..1ef9164
--- /dev/null
+++ b/src/pages/Payement/PaymentForm/PaymentForm.jsx
@@ -0,0 +1,97 @@
+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 stripePromise = loadStripe("pk_live_51PyIYTP3VLLeb9GlHpiK8p5lVC3kGPvAAb0Nn8m5qVAGzespGfGlDoP8Wmw4HbZJJgqxxHEIG7MJwP4IAWCpRBi100dYMpV1gv");
+
+const PackageNumber = (selectedPackage) => {
+ switch (selectedPackage) {
+ case 'Basique':
+ return 1;
+ case 'Standard':
+ return 2;
+ case 'Premium':
+ return 3;
+ default:
+ return 1;
+ }
+};
+
+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('https://www.servii.fr/api/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 };
+
+
+
+ return (
+
+
navigate('/Pricing')}
+ backButtonText="Retour"
+ />
+
+
+
+
+
+
+ );
+};
+
+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
new file mode 100644
index 0000000..95748db
--- /dev/null
+++ b/src/pages/Payement/PaymentForm/PaymentForm.module.scss
@@ -0,0 +1,7 @@
+.container{
+ margin-top: 5.5rem;
+}
+
+.Footer-PoweredBy-Text{
+ display: none;
+}
\ No newline at end of file
diff --git a/src/pages/Payement/Pricing/Pricing.jsx b/src/pages/Payement/Pricing/Pricing.jsx
new file mode 100644
index 0000000..4963637
--- /dev/null
+++ b/src/pages/Payement/Pricing/Pricing.jsx
@@ -0,0 +1,117 @@
+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';
+
+// Import des icônes
+import { FaCheckCircle, FaTimesCircle } from 'react-icons/fa';
+
+const Pricing = ({ user }) => {
+ const navigate = useNavigate();
+
+
+ const groups = [
+ {
+ title: 'Basique',
+ 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 },
+ { name: 'Accès au serveurs Mini-jeux', isAvailable: false },
+ { name: 'Accès au serveurs de modpacks', isAvailable: false },
+ ],
+ image: basique
+ },
+ {
+ title: 'Standard',
+ 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 },
+ { name: 'Accès au serveurs Mini-jeux', isAvailable: true },
+ { name: 'Accès au serveurs de modpacks', isAvailable: false }
+ ],
+ image: standard
+ },
+ {
+ title: 'Premium',
+ 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 },
+ { name: 'Accès au serveurs Mini-jeux', isAvailable: true },
+ { name: 'Accès au serveurs de modpacks', isAvailable: true }
+ ],
+ image: premium
+ },
+ ];
+
+ const handleSubscribe = (pkg) => {
+ navigate(`/payment?package=${pkg.title}`);
+ };
+
+ return (
+
+
navigate('/Dashboard')}
+ />
+
+
+ {groups.map((pkg, index) => (
+
+
+
{pkg.title}
+
+
+
+
+
+
{pkg.description}
+
+
+ CE QUI EST INCLUS
+
+ {pkg.features.map((feature, idx) => (
+
+ {feature.isAvailable ? (
+
+ ) : (
+
+ )}
+ {feature.name}
+
+ ))}
+
+
handleSubscribe(pkg)}>
+ Démarrez maintenant
+
+
+ ))}
+
+
+ );
+};
+
+Pricing.propTypes = {
+ user: PropTypes.object.isRequired,
+};
+
+export default Pricing;
diff --git a/src/pages/Payement/Pricing/Pricing.module.scss b/src/pages/Payement/Pricing/Pricing.module.scss
new file mode 100644
index 0000000..cee635f
--- /dev/null
+++ b/src/pages/Payement/Pricing/Pricing.module.scss
@@ -0,0 +1,122 @@
+$primary-color: #000;
+$secondary-color: #fff;
+
+.pricingContainer {
+ margin-top: 4rem;
+ display: flex;
+ justify-content: center;
+ flex-direction: row;
+ gap: 2rem;
+ padding: 4rem;
+}
+
+.packageList {
+ display: flex;
+ flex-direction: row;
+ gap: 3rem;
+}
+
+.packageCard {
+ background-color: $secondary-color;
+ color: $primary-color;
+ border-radius: .5rem;
+ 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);
+
+ .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: 1.5rem;
+
+ .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;
+ }
+}
diff --git a/src/service/api.test.js b/src/service/api.test.js
deleted file mode 100644
index e69de29..0000000
diff --git a/src/service/api.tsx b/src/service/api.tsx
index 810def7..2464f96 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(`https://www.servii.fr/api/modpacks/image/a-metadata.txt`, {
+ 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 {
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 };