mirror of
https://github.com/hubHarmony/servii-frontend.git
synced 2024-11-17 21:40:30 +00:00
+stripe integration
This commit is contained in:
parent
f13473c2f8
commit
a7044a9dc7
@ -2,7 +2,7 @@
|
|||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
<link rel="icon" type="image/svg+xml" href="src/assets/logo.png" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>Servii - Hebergement facile</title>
|
<title>Servii - Hebergement facile</title>
|
||||||
<!-- charger les polices -->
|
<!-- charger les polices -->
|
||||||
|
@ -5,7 +5,9 @@ import 'react-toastify/dist/ReactToastify.css';
|
|||||||
import { auth } from './service/firebase';
|
import { auth } from './service/firebase';
|
||||||
import styles from './App.module.scss';
|
import styles from './App.module.scss';
|
||||||
import Loading from './pages/Loading/loading';
|
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 LoginPage = lazy(() => import('./pages/LoginPage/LoginPage'));
|
||||||
const ServerDetails = lazy(() => import('./pages/ServerDetails/ServerDetails'));
|
const ServerDetails = lazy(() => import('./pages/ServerDetails/ServerDetails'));
|
||||||
@ -57,7 +59,8 @@ const App = () => {
|
|||||||
<Route path="/createServer/modpack" element={user ? <Modpack user={user} /> : <Navigate to="/login" />} />
|
<Route path="/createServer/modpack" element={user ? <Modpack user={user} /> : <Navigate to="/login" />} />
|
||||||
<Route path="/server/:serverName/*" element={user ? <ServerDetails user={user} /> : <Navigate to="/login" />} />
|
<Route path="/server/:serverName/*" element={user ? <ServerDetails user={user} /> : <Navigate to="/login" />} />
|
||||||
<Route path="/pricing" element={user ? <Pricing user={user} /> : <Navigate to="/login" />} />
|
<Route path="/pricing" element={user ? <Pricing user={user} /> : <Navigate to="/login" />} />
|
||||||
|
<Route path="/payement/*" element={user ? <PaymentForm user={user} /> : <Navigate to="/login" />} />
|
||||||
|
<Route path="/return/*" element={user ? <Checkout user={user} /> : <Navigate to="/login" />} />
|
||||||
<Route path="/" element={<Navigate to={user ? "/dashboard" : "/login"} />} />
|
<Route path="/" element={<Navigate to={user ? "/dashboard" : "/login"} />} />
|
||||||
<Route path="*" element={user ? <DashboardPage user={user} /> : <Navigate to="/login" />} />
|
<Route path="*" element={user ? <DashboardPage user={user} /> : <Navigate to="/login" />} />
|
||||||
</Routes>
|
</Routes>
|
||||||
|
BIN
src/assets/logo.png
Normal file
BIN
src/assets/logo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 42 KiB |
@ -52,7 +52,7 @@ const Navbar = ({
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{Subsribebtn && ( // Updated: Conditional rendering for the subscribe button
|
{Subsribebtn && (
|
||||||
<div className={styles.subscribeSection}>
|
<div className={styles.subscribeSection}>
|
||||||
<div className={styles.subscribeBtn} onClick={() => navigate('/Pricing')}>
|
<div className={styles.subscribeBtn} onClick={() => navigate('/Pricing')}>
|
||||||
S'abonner
|
S'abonner
|
||||||
@ -101,7 +101,7 @@ Navbar.propTypes = {
|
|||||||
showBackButton: PropTypes.bool,
|
showBackButton: PropTypes.bool,
|
||||||
onBackClick: PropTypes.func,
|
onBackClick: PropTypes.func,
|
||||||
backButtonText: PropTypes.string,
|
backButtonText: PropTypes.string,
|
||||||
Subsribebtn: PropTypes.bool // Updated: Prop type corrected
|
Subsribebtn: PropTypes.bool
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Navbar;
|
export default Navbar;
|
||||||
|
@ -3,7 +3,7 @@ import { useNavigate } from 'react-router-dom';
|
|||||||
import ServerCard from '../../components/serverCards/DefaultServerCard';
|
import ServerCard from '../../components/serverCards/DefaultServerCard';
|
||||||
import Navbar from '../../components/navbar/Navbar';
|
import Navbar from '../../components/navbar/Navbar';
|
||||||
import Loading from '../Loading/loading';
|
import Loading from '../Loading/loading';
|
||||||
import { getUserSubdomain } from "../../service/firebase";
|
import { getUserSubdomain, getUserSubscription } from "../../service/firebase";
|
||||||
import serviiApi from "../../service/api.tsx";
|
import serviiApi from "../../service/api.tsx";
|
||||||
import PropTypes from "prop-types";
|
import PropTypes from "prop-types";
|
||||||
import styles from './DashboardPage.module.scss';
|
import styles from './DashboardPage.module.scss';
|
||||||
@ -21,6 +21,7 @@ const DashboardPage = ({ user }) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const [subdomain, setSubdomain] = useState(' ');
|
const [subdomain, setSubdomain] = useState(' ');
|
||||||
|
const [subscription, setSubscription] = useState(0);
|
||||||
const [loading, setLoading] = useState(servers.length === 0);
|
const [loading, setLoading] = useState(servers.length === 0);
|
||||||
const [searchTerm, setSearchTerm] = useState('');
|
const [searchTerm, setSearchTerm] = useState('');
|
||||||
const [newSubdomain, setNewSubdomain] = useState(' ');
|
const [newSubdomain, setNewSubdomain] = useState(' ');
|
||||||
@ -33,6 +34,8 @@ const DashboardPage = ({ user }) => {
|
|||||||
if (user?.uid) {
|
if (user?.uid) {
|
||||||
const userSubdomain = await getUserSubdomain(user.uid);
|
const userSubdomain = await getUserSubdomain(user.uid);
|
||||||
setSubdomain(userSubdomain || null);
|
setSubdomain(userSubdomain || null);
|
||||||
|
const userSubscription = await getUserSubscription(user.uid);
|
||||||
|
setSubscription(userSubscription || 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
const ApiResponse = await serviiApi.fetchServers();
|
const ApiResponse = await serviiApi.fetchServers();
|
||||||
@ -55,14 +58,44 @@ const DashboardPage = ({ user }) => {
|
|||||||
|
|
||||||
const handleCreateServer = () => navigate('/CreateServer');
|
const handleCreateServer = () => navigate('/CreateServer');
|
||||||
|
|
||||||
const handleRunServer = async (serverName) => {
|
const handleRunServer = async (serverName, framework) => {
|
||||||
try {
|
try {
|
||||||
await serviiApi.serverRun(serverName);
|
if (subscription === 0) {
|
||||||
updateServersFromApi();
|
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) {
|
} catch (error) {
|
||||||
console.error('Error starting server:', error);
|
console.error('Error starting server:', error);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const handleStopServer = async (serverName) => {
|
const handleStopServer = async (serverName) => {
|
||||||
try {
|
try {
|
||||||
@ -193,7 +226,7 @@ const DashboardPage = ({ user }) => {
|
|||||||
framework={favoriteServer.framework}
|
framework={favoriteServer.framework}
|
||||||
maxPlayers={favoriteServer.maxPlayers}
|
maxPlayers={favoriteServer.maxPlayers}
|
||||||
countPlayers={favoriteServer.onlinePlayers}
|
countPlayers={favoriteServer.onlinePlayers}
|
||||||
onRunClick={() => handleRunServer(favoriteServer.name)}
|
onRunClick={() => handleRunServer(favoriteServer.name , favoriteServer.framework)}
|
||||||
onStopClick={() => handleStopServer(favoriteServer.name)}
|
onStopClick={() => handleStopServer(favoriteServer.name)}
|
||||||
onDeleteClick={() => {
|
onDeleteClick={() => {
|
||||||
setServerToDelete(favoriteServer.name);
|
setServerToDelete(favoriteServer.name);
|
||||||
@ -219,7 +252,7 @@ const DashboardPage = ({ user }) => {
|
|||||||
framework={server.framework}
|
framework={server.framework}
|
||||||
maxPlayers={server.maxPlayers}
|
maxPlayers={server.maxPlayers}
|
||||||
countPlayers={server.onlinePlayers}
|
countPlayers={server.onlinePlayers}
|
||||||
onRunClick={() => handleRunServer(server.name)}
|
onRunClick={() => handleRunServer(server.name , server.framework)}
|
||||||
onStopClick={() => handleStopServer(server.name)}
|
onStopClick={() => handleStopServer(server.name)}
|
||||||
onDeleteClick={() => {
|
onDeleteClick={() => {
|
||||||
setServerToDelete(server.name);
|
setServerToDelete(server.name);
|
||||||
|
51
src/pages/Payement/Checkout.jsx
Normal file
51
src/pages/Payement/Checkout.jsx
Normal file
@ -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 <div>Loading...</div>;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{sessionStatus.status === 'open' && (
|
||||||
|
<div>
|
||||||
|
<h1>Checkout is still open</h1>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{sessionStatus.status === 'complete' && (
|
||||||
|
<div>
|
||||||
|
<h1>Success!</h1>
|
||||||
|
<p>Payment Status: {sessionStatus.payment_status}</p>
|
||||||
|
<p>Customer Email: {sessionStatus.customer_email}</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Checkout;
|
@ -1,49 +1,97 @@
|
|||||||
import React, { useState } from 'react';
|
import { useCallback, useState, useEffect } from 'react';
|
||||||
import { CardElement, useStripe, useElements } from '@stripe/react-stripe-js';
|
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';
|
import styles from './PaymentForm.module.scss';
|
||||||
|
|
||||||
const PaymentForm = ({ groups, closeModal }) => {
|
const stripePromise = loadStripe("pk_test_51PyIYTP3VLLeb9GlXCKgD4ylbemZPx72I3HkEAu0bRtcsfK31nqb3WtUbXKXUcKmyfrxKLfuJzZCPyp7Ymtlq9zy00c7VmkL6G");
|
||||||
const stripe = useStripe();
|
|
||||||
const elements = useElements();
|
|
||||||
const [errorMessage, setErrorMessage] = useState(null);
|
|
||||||
const [successMessage, setSuccessMessage] = useState(null);
|
|
||||||
|
|
||||||
const handleSubmit = async (event) => {
|
const PackageNumber = (selectedPackage) => {
|
||||||
event.preventDefault();
|
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 (
|
return (
|
||||||
<form onSubmit={handleSubmit} className={styles.paymentForm}>
|
<div className={styles.container}>
|
||||||
<CardElement />
|
<Navbar
|
||||||
{errorMessage && <p className={styles.error}>{errorMessage}</p>}
|
user={user}
|
||||||
{successMessage && <p className={styles.success}>{successMessage}</p>}
|
hasShadow={true}
|
||||||
<button type="submit" disabled={!stripe} className={styles.submitButton}>
|
showBackButton={true}
|
||||||
Payer {groups.price}
|
onBackClick={() => navigate('/Pricing')}
|
||||||
</button>
|
backButtonText="Retour"
|
||||||
<button type="button" onClick={closeModal} className={styles.closeButton}>
|
/>
|
||||||
Fermer
|
<div id="checkout">
|
||||||
</button>
|
<EmbeddedCheckoutProvider stripe={stripePromise} options={options} >
|
||||||
</form>
|
<EmbeddedCheckout />
|
||||||
|
</EmbeddedCheckoutProvider>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default PaymentForm;
|
PaymentForm.propTypes = {
|
||||||
|
user: PropTypes.shape({
|
||||||
|
uid: PropTypes.string.isRequired,
|
||||||
|
email: PropTypes.string,
|
||||||
|
photoURL: PropTypes.string,
|
||||||
|
}).isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default PaymentForm;
|
@ -1,19 +1,7 @@
|
|||||||
.modal {
|
.container{
|
||||||
position: fixed;
|
margin-top: 5.5rem;
|
||||||
top: 0;
|
}
|
||||||
left: 0;
|
|
||||||
right: 0;
|
.Footer-PoweredBy-Text{
|
||||||
bottom: 0;
|
display: none;
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
@ -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 (
|
|
||||||
<div className={styles.pricingContainer}>
|
|
||||||
<Navbar
|
|
||||||
user={user}
|
|
||||||
hasShadow={true}
|
|
||||||
showBackButton={true}
|
|
||||||
onBackClick={() => navigate('/Dashboard')}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{groups.map((pkg, index) => (
|
|
||||||
<div key={index} className={styles.packageCard}>
|
|
||||||
<h2 className={styles.title}>{pkg.title}</h2>
|
|
||||||
<p className={styles.price}>{pkg.price}</p>
|
|
||||||
<ul className={styles.features}>
|
|
||||||
{pkg.features.map((feature, idx) => (
|
|
||||||
<li key={idx}>{feature}</li>
|
|
||||||
))}
|
|
||||||
</ul>
|
|
||||||
<button className={styles.button} onClick={() => handleSubscribe(pkg)}>Sinscrire</button>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
|
|
||||||
{isModalOpen && (
|
|
||||||
<div className={styles.modal}>
|
|
||||||
<div className={styles.modalContent}>
|
|
||||||
<h2>Paiement pour {selectedPackage.title}</h2>
|
|
||||||
<Elements stripe={stripePromise}>
|
|
||||||
<PaymentForm groups={selectedPackage} closeModal={() => setIsModalOpen(false)} />
|
|
||||||
</Elements>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
Pricing.propTypes = {
|
|
||||||
user: PropTypes.object.isRequired,
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Pricing;
|
|
52
src/pages/Payement/Pricing/Pricing.jsx
Normal file
52
src/pages/Payement/Pricing/Pricing.jsx
Normal file
@ -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 (
|
||||||
|
<div className={styles.pricingContainer}>
|
||||||
|
<Navbar
|
||||||
|
user={user}
|
||||||
|
hasShadow={true}
|
||||||
|
showBackButton={true}
|
||||||
|
onBackClick={() => navigate('/Dashboard')}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div className={styles.packageList}>
|
||||||
|
{groups.map((pkg, index) => (
|
||||||
|
<div key={index} className={styles.packageCard}>
|
||||||
|
<h2 className={styles.title}>{pkg.title}</h2>
|
||||||
|
<p className={styles.price}>{pkg.price}</p>
|
||||||
|
<ul className={styles.features}>
|
||||||
|
{pkg.features.map((feature, idx) => (
|
||||||
|
<li key={idx}>{feature}</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
<button className={styles.button} onClick={() => handleSubscribe(pkg)}>
|
||||||
|
S'inscrire
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
Pricing.propTypes = {
|
||||||
|
user: PropTypes.object.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Pricing;
|
@ -2,35 +2,43 @@ $primary-color: #000;
|
|||||||
$secondary-color: #fff;
|
$secondary-color: #fff;
|
||||||
|
|
||||||
.pricingContainer {
|
.pricingContainer {
|
||||||
|
margin-top: 5rem;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
gap: 20px;
|
flex-direction: row;
|
||||||
padding: 40px;
|
gap: 2rem;
|
||||||
|
padding: 4rem;
|
||||||
background-color: $secondary-color;
|
background-color: $secondary-color;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.packageList{
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
gap: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
.packageCard {
|
.packageCard {
|
||||||
background-color: $primary-color;
|
background-color: $primary-color;
|
||||||
color: $secondary-color;
|
color: $secondary-color;
|
||||||
border-radius: 8px;
|
border-radius: .8rem;
|
||||||
padding: 20px;
|
padding: 5rem;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
|
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
|
||||||
transition: transform 0.3s;
|
transition: transform 0.3s;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
transform: scale(1.05);
|
transform: scale(1.01);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.title {
|
.title {
|
||||||
font-size: 24px;
|
font-size: 2rem;
|
||||||
margin-bottom: 10px;
|
margin-bottom: .6rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.price {
|
.price {
|
||||||
font-size: 32px;
|
font-size: 2.5rem;
|
||||||
margin: 10px 0;
|
margin: 1rem 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.features {
|
.features {
|
||||||
@ -52,9 +60,4 @@ $secondary-color: #fff;
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
transition: background-color 0.3s;
|
transition: background-color 0.3s;
|
||||||
|
}
|
||||||
&:hover {
|
|
||||||
background-color: $primary-color;
|
|
||||||
color: $secondary-color;
|
|
||||||
}
|
|
||||||
}
|
|
@ -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 };
|
||||||
|
Loading…
Reference in New Issue
Block a user