mirror of
https://github.com/hubHarmony/servii-frontend.git
synced 2024-11-18 05:40:31 +00:00
+payement handling v0.5
This commit is contained in:
parent
0d6b741fd4
commit
da3a161f30
23
package-lock.json
generated
23
package-lock.json
generated
@ -10,6 +10,8 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@fortawesome/free-solid-svg-icons": "^6.5.2",
|
"@fortawesome/free-solid-svg-icons": "^6.5.2",
|
||||||
"@fortawesome/react-fontawesome": "^0.2.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/jest-dom": "^6.4.6",
|
||||||
"@testing-library/react": "^16.0.0",
|
"@testing-library/react": "^16.0.0",
|
||||||
"@types/jest": "^29.5.12",
|
"@types/jest": "^29.5.12",
|
||||||
@ -3184,6 +3186,27 @@
|
|||||||
"@sinonjs/commons": "^3.0.0"
|
"@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": {
|
"node_modules/@testing-library/dom": {
|
||||||
"version": "10.3.0",
|
"version": "10.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.3.0.tgz",
|
||||||
|
@ -13,6 +13,8 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@fortawesome/free-solid-svg-icons": "^6.5.2",
|
"@fortawesome/free-solid-svg-icons": "^6.5.2",
|
||||||
"@fortawesome/react-fontawesome": "^0.2.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/jest-dom": "^6.4.6",
|
||||||
"@testing-library/react": "^16.0.0",
|
"@testing-library/react": "^16.0.0",
|
||||||
"@types/jest": "^29.5.12",
|
"@types/jest": "^29.5.12",
|
||||||
|
@ -5,6 +5,7 @@ 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';
|
||||||
|
|
||||||
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'));
|
||||||
@ -55,6 +56,8 @@ const App = () => {
|
|||||||
<Route path="/createServer/bedrock" element={user ? <Bedrock user={user} /> : <Navigate to="/login" />} />
|
<Route path="/createServer/bedrock" element={user ? <Bedrock user={user} /> : <Navigate to="/login" />} />
|
||||||
<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="/" 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>
|
||||||
|
49
src/pages/Payement/PaymentForm/PaymentForm.jsx
Normal file
49
src/pages/Payement/PaymentForm/PaymentForm.jsx
Normal file
@ -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 (
|
||||||
|
<form onSubmit={handleSubmit} className={styles.paymentForm}>
|
||||||
|
<CardElement />
|
||||||
|
{errorMessage && <p className={styles.error}>{errorMessage}</p>}
|
||||||
|
{successMessage && <p className={styles.success}>{successMessage}</p>}
|
||||||
|
<button type="submit" disabled={!stripe} className={styles.submitButton}>
|
||||||
|
Payer {groups.price}
|
||||||
|
</button>
|
||||||
|
<button type="button" onClick={closeModal} className={styles.closeButton}>
|
||||||
|
Fermer
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default PaymentForm;
|
19
src/pages/Payement/PaymentForm/PaymentForm.module.scss
Normal file
19
src/pages/Payement/PaymentForm/PaymentForm.module.scss
Normal file
@ -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;
|
||||||
|
}
|
||||||
|
|
68
src/pages/Payement/Pricing.jsx
Normal file
68
src/pages/Payement/Pricing.jsx
Normal file
@ -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 (
|
||||||
|
<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;
|
60
src/pages/Payement/Pricing.module.scss
Normal file
60
src/pages/Payement/Pricing.module.scss
Normal file
@ -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;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user