Add MUI theming and an axios interceptor

This commit is contained in:
lumijiez
2025-06-23 20:39:46 +03:00
parent d2afe2df98
commit d4f01c3f73
13 changed files with 949 additions and 154 deletions

View File

@@ -0,0 +1,143 @@
'use client';
import { useState } from 'react';
import { loadStripe } from '@stripe/stripe-js';
import { Elements } from '@stripe/react-stripe-js';
import PaymentForm from './components/PaymentForm';
import './globals.css';
const stripePromise = loadStripe('');
const products = [
{ id: '1', name: 'Premium Widget', price: 2999, description: 'High-quality widget for professionals' },
{ id: '2', name: 'Standard Widget', price: 1999, description: 'Reliable widget for everyday use' },
{ id: '3', name: 'Basic Widget', price: 999, description: 'Entry-level widget for beginners' }
];
export default function Home() {
const [selectedProduct, setSelectedProduct] = useState(null);
const [clientSecret, setClientSecret] = useState('');
const [orderId, setOrderId] = useState('');
const [loading, setLoading] = useState(false);
const handleProductSelect = async (product) => {
setLoading(true);
setSelectedProduct(product);
const newOrderId = Math.floor(Math.random() * 10000).toString();
setOrderId(newOrderId);
try {
const response = await fetch('https://impr.ink/api/stripe/create-payment-intent', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
amount: product.price,
orderId: newOrderId
}),
});
const data = await response.json();
if (data.clientSecret) {
setClientSecret(data.clientSecret);
} else {
console.error('Error creating payment intent:', data.error);
}
} catch (error) {
console.error('Error:', error);
} finally {
setLoading(false);
}
};
const handlePaymentSuccess = () => {
setSelectedProduct(null);
setClientSecret('');
setOrderId('');
};
const handleBackToProducts = () => {
setSelectedProduct(null);
setClientSecret('');
setOrderId('');
};
const appearance = {
theme: 'stripe',
variables: {
colorPrimary: '#0570de',
colorBackground: '#ffffff',
colorText: '#30313d',
colorDanger: '#df1b41',
fontFamily: 'Ideal Sans, system-ui, sans-serif',
spacingUnit: '2px',
borderRadius: '4px',
},
};
const options = {
clientSecret,
appearance,
};
return (
<div className="container">
<header>
<h1>🛍 Stripe Payment Demo</h1>
<p>Select a product to purchase</p>
</header>
{!selectedProduct ? (
<div className="products">
<h2>Products</h2>
<div className="product-grid">
{products.map((product) => (
<div key={product.id} className="product-card">
<h3>{product.name}</h3>
<p className="description">{product.description}</p>
<p className="price">${(product.price / 100).toFixed(2)}</p>
<button
onClick={() => handleProductSelect(product)}
disabled={loading}
className="select-btn"
>
{loading ? 'Loading...' : 'Select'}
</button>
</div>
))}
</div>
</div>
) : (
<div className="checkout">
<div className="order-summary">
<h2>Order Summary</h2>
<div className="order-details">
<p><strong>Product:</strong> {selectedProduct.name}</p>
<p><strong>Order ID:</strong> {orderId}</p>
<p><strong>Amount:</strong> ${(selectedProduct.price / 100).toFixed(2)}</p>
</div>
</div>
{clientSecret && (
<Elements options={options} stripe={stripePromise}>
<PaymentForm
onSuccess={handlePaymentSuccess}
orderId={orderId}
/>
</Elements>
)}
<button
onClick={handleBackToProducts}
className="back-btn"
>
Back to Products
</button>
</div>
)}
</div>
);
}