fixed cart desync
This commit is contained in:
parent
202e99b12f
commit
c913b09edf
6 changed files with 54 additions and 25 deletions
|
|
@ -52,7 +52,7 @@ const upload = multer({
|
||||||
storage,
|
storage,
|
||||||
fileFilter,
|
fileFilter,
|
||||||
limits: {
|
limits: {
|
||||||
fileSize: 5 * 1024 * 1024 // 5MB limit
|
fileSize: 10 * 1024 * 1024 // 10MB
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,6 @@ const createTransporter = () => {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// Mock email transporter for development
|
|
||||||
const transporter = createTransporter();
|
const transporter = createTransporter();
|
||||||
|
|
||||||
module.exports = (pool, query) => {
|
module.exports = (pool, query) => {
|
||||||
|
|
@ -94,7 +93,6 @@ module.exports = (pool, query) => {
|
||||||
[authId, user.id]
|
[authId, user.id]
|
||||||
);
|
);
|
||||||
|
|
||||||
// Send email with code (mock in development)
|
|
||||||
const loginLink = `${config.site.protocol}://${config.site.domain}/verify?code=${authCode}&email=${encodeURIComponent(email)}`;
|
const loginLink = `${config.site.protocol}://${config.site.domain}/verify?code=${authCode}&email=${encodeURIComponent(email)}`;
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -513,7 +513,6 @@ module.exports = (pool, query, authMiddleware) => {
|
||||||
? shippingService.parseAddressString(shippingAddress)
|
? shippingService.parseAddressString(shippingAddress)
|
||||||
: shippingAddress;
|
: shippingAddress;
|
||||||
|
|
||||||
console.log("Fetching rates for parsed address:", parsedAddress);
|
|
||||||
|
|
||||||
const shippingResponse = await shippingService.getShippingRates(
|
const shippingResponse = await shippingService.getShippingRates(
|
||||||
null, // Use default from config
|
null, // Use default from config
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ import { loadStripe } from '@stripe/stripe-js';
|
||||||
import { Elements } from '@stripe/react-stripe-js';
|
import { Elements } from '@stripe/react-stripe-js';
|
||||||
import config from '../config';
|
import config from '../config';
|
||||||
import apiClient from '@services/api';
|
import apiClient from '@services/api';
|
||||||
|
import { useAuth } from '@hooks/reduxHooks';
|
||||||
|
|
||||||
// Create the context
|
// Create the context
|
||||||
const StripeContext = createContext();
|
const StripeContext = createContext();
|
||||||
|
|
@ -12,13 +13,19 @@ export const StripeProvider = ({ children }) => {
|
||||||
const [clientSecret, setClientSecret] = useState('');
|
const [clientSecret, setClientSecret] = useState('');
|
||||||
const [isLoading, setIsLoading] = useState(true);
|
const [isLoading, setIsLoading] = useState(true);
|
||||||
const [error, setError] = useState(null);
|
const [error, setError] = useState(null);
|
||||||
|
const { isAuthenticated } = useAuth();
|
||||||
|
|
||||||
|
// Load or reload Stripe configuration when authentication state changes
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
// Reset state when auth changes
|
||||||
|
setIsLoading(true);
|
||||||
|
setError(null);
|
||||||
|
|
||||||
// Try to load Stripe public key from environment
|
// Try to load Stripe public key from environment
|
||||||
let publicKey = import.meta.env.VITE_STRIPE_PUBLIC_KEY;
|
let publicKey = import.meta.env.VITE_STRIPE_PUBLIC_KEY;
|
||||||
|
|
||||||
// If not found, fetch from API
|
// If not found, fetch from API
|
||||||
if (!publicKey) {
|
if (isAuthenticated) {
|
||||||
// Fetch Stripe public key from backend
|
// Fetch Stripe public key from backend
|
||||||
apiClient.get('/payment/config')
|
apiClient.get('/payment/config')
|
||||||
.then(response => {
|
.then(response => {
|
||||||
|
|
@ -34,10 +41,10 @@ export const StripeProvider = ({ children }) => {
|
||||||
setError('Failed to load payment configuration');
|
setError('Failed to load payment configuration');
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
});
|
});
|
||||||
} else {
|
} else if(publicKey){
|
||||||
loadStripeInstance(publicKey);
|
loadStripeInstance(publicKey);
|
||||||
}
|
}
|
||||||
}, []);
|
}, [isAuthenticated]); // Add isAuthenticated as a dependency to reload on auth changes
|
||||||
|
|
||||||
const loadStripeInstance = (publicKey) => {
|
const loadStripeInstance = (publicKey) => {
|
||||||
try {
|
try {
|
||||||
|
|
@ -102,7 +109,27 @@ export const StripeProvider = ({ children }) => {
|
||||||
error,
|
error,
|
||||||
createCheckoutSession,
|
createCheckoutSession,
|
||||||
checkSessionStatus,
|
checkSessionStatus,
|
||||||
completeOrder
|
completeOrder,
|
||||||
|
reloadConfig: () => {
|
||||||
|
setIsLoading(true);
|
||||||
|
setError(null);
|
||||||
|
if (isAuthenticated){
|
||||||
|
apiClient.get('/payment/config')
|
||||||
|
.then(response => {
|
||||||
|
if (response.data.stripePublicKey) {
|
||||||
|
loadStripeInstance(response.data.stripePublicKey);
|
||||||
|
} else {
|
||||||
|
setError('Stripe public key not found');
|
||||||
|
setIsLoading(false);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
console.error('Error fetching Stripe config:', err);
|
||||||
|
setError('Failed to load payment configuration');
|
||||||
|
setIsLoading(false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,6 @@ import apiClient from '../../services/api';
|
||||||
import { useAdminCategories } from '../../hooks/categoryAdminHooks';
|
import { useAdminCategories } from '../../hooks/categoryAdminHooks';
|
||||||
|
|
||||||
const AdminDashboardPage = () => {
|
const AdminDashboardPage = () => {
|
||||||
// Mock data - would be replaced with real API calls
|
|
||||||
const [stats, setStats] = useState({
|
const [stats, setStats] = useState({
|
||||||
totalProducts: 0,
|
totalProducts: 0,
|
||||||
totalUsers: 0,
|
totalUsers: 0,
|
||||||
|
|
@ -53,7 +52,6 @@ const AdminDashboardPage = () => {
|
||||||
// Fetch categories
|
// Fetch categories
|
||||||
const { data: categories, isLoading: categoriesLoading } = useAdminCategories();
|
const { data: categories, isLoading: categoriesLoading } = useAdminCategories();
|
||||||
|
|
||||||
// Mock recent orders - would be replaced with real API data
|
|
||||||
const recentOrders = [
|
const recentOrders = [
|
||||||
{ id: '4532', customer: 'John Doe', date: '2023-04-22', total: 49.99, status: 'Delivered' },
|
{ id: '4532', customer: 'John Doe', date: '2023-04-22', total: 49.99, status: 'Delivered' },
|
||||||
{ id: '4531', customer: 'Jane Smith', date: '2023-04-21', total: 89.95, status: 'Processing' },
|
{ id: '4531', customer: 'Jane Smith', date: '2023-04-21', total: 89.95, status: 'Processing' },
|
||||||
|
|
@ -67,10 +65,9 @@ const AdminDashboardPage = () => {
|
||||||
setStats(prev => ({
|
setStats(prev => ({
|
||||||
...prev,
|
...prev,
|
||||||
totalProducts: products.length,
|
totalProducts: products.length,
|
||||||
// Other stats would be updated from their respective API calls
|
totalUsers: 15,
|
||||||
totalUsers: 15, // Mock data
|
totalOrders: 42,
|
||||||
totalOrders: 42, // Mock data
|
revenue: 2459.99
|
||||||
revenue: 2459.99 // Mock data
|
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -23,8 +23,8 @@ import {
|
||||||
FormLabel
|
FormLabel
|
||||||
} from '@mui/material';
|
} from '@mui/material';
|
||||||
import { useNavigate, Link as RouterLink } from 'react-router-dom';
|
import { useNavigate, Link as RouterLink } from 'react-router-dom';
|
||||||
import { useAuth, useCart } from '../hooks/reduxHooks';
|
import { useAuth } from '../hooks/reduxHooks';
|
||||||
import { useCheckout } from '../hooks/apiHooks';
|
import { useCheckout, useGetCart } from '../hooks/apiHooks';
|
||||||
import { useStripe, StripeElementsProvider } from '../context/StripeContext';
|
import { useStripe, StripeElementsProvider } from '../context/StripeContext';
|
||||||
import apiClient from '../services/api';
|
import apiClient from '../services/api';
|
||||||
|
|
||||||
|
|
@ -34,7 +34,7 @@ const steps = ['Shipping Address', 'Shipping Method', 'Review Order', 'Payment',
|
||||||
const CheckoutPage = () => {
|
const CheckoutPage = () => {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const { user, userData } = useAuth();
|
const { user, userData } = useAuth();
|
||||||
const { items, total, itemCount } = useCart();
|
const { data: cart, isLoading: cartLoading } = useGetCart(user);
|
||||||
const checkout = useCheckout();
|
const checkout = useCheckout();
|
||||||
const { createCheckoutSession, isLoading: isStripeLoading } = useStripe();
|
const { createCheckoutSession, isLoading: isStripeLoading } = useStripe();
|
||||||
|
|
||||||
|
|
@ -54,8 +54,8 @@ const CheckoutPage = () => {
|
||||||
|
|
||||||
// State for form data
|
// State for form data
|
||||||
const [formData, setFormData] = useState({
|
const [formData, setFormData] = useState({
|
||||||
firstName: userData?.first_name || '',
|
firstName: userData?.firstName || '',
|
||||||
lastName: userData?.last_name || '',
|
lastName: userData?.lastName || '',
|
||||||
email: userData?.email || '',
|
email: userData?.email || '',
|
||||||
address: '',
|
address: '',
|
||||||
city: '',
|
city: '',
|
||||||
|
|
@ -125,7 +125,6 @@ const CheckoutPage = () => {
|
||||||
|
|
||||||
for (const field of requiredFields) {
|
for (const field of requiredFields) {
|
||||||
if (!formData[field]) {
|
if (!formData[field]) {
|
||||||
// In a real app, you'd set specific errors for each field
|
|
||||||
setError(`Please fill in all required fields`);
|
setError(`Please fill in all required fields`);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -210,7 +209,7 @@ const CheckoutPage = () => {
|
||||||
|
|
||||||
// Handle place order
|
// Handle place order
|
||||||
const handlePlaceOrder = async () => {
|
const handlePlaceOrder = async () => {
|
||||||
if (!user || !items || items.length === 0) {
|
if (!user || !cart || !cart.items || cart.items.length === 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -280,8 +279,17 @@ const CheckoutPage = () => {
|
||||||
}
|
}
|
||||||
}, [checkoutUrl]);
|
}, [checkoutUrl]);
|
||||||
|
|
||||||
|
// Loading state
|
||||||
|
if (cartLoading) {
|
||||||
|
return (
|
||||||
|
<Box sx={{ display: 'flex', justifyContent: 'center', my: 8 }}>
|
||||||
|
<CircularProgress />
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// If no items in cart, redirect to cart page
|
// If no items in cart, redirect to cart page
|
||||||
if (!items || items.length === 0) {
|
if (!cart || !cart.items || cart.items.length === 0) {
|
||||||
return (
|
return (
|
||||||
<Box sx={{ textAlign: 'center', py: 6 }}>
|
<Box sx={{ textAlign: 'center', py: 6 }}>
|
||||||
<Typography variant="h5" gutterBottom>
|
<Typography variant="h5" gutterBottom>
|
||||||
|
|
@ -489,7 +497,7 @@ const CheckoutPage = () => {
|
||||||
</Typography>
|
</Typography>
|
||||||
|
|
||||||
<List disablePadding>
|
<List disablePadding>
|
||||||
{items.map((item) => (
|
{cart.items.map((item) => (
|
||||||
<ListItem key={item.product_id} sx={{ py: 1, px: 0 }}>
|
<ListItem key={item.product_id} sx={{ py: 1, px: 0 }}>
|
||||||
<ListItemText
|
<ListItemText
|
||||||
primary={item.name}
|
primary={item.name}
|
||||||
|
|
@ -503,7 +511,7 @@ const CheckoutPage = () => {
|
||||||
|
|
||||||
<ListItem sx={{ py: 1, px: 0 }}>
|
<ListItem sx={{ py: 1, px: 0 }}>
|
||||||
<ListItemText primary="Subtotal" />
|
<ListItemText primary="Subtotal" />
|
||||||
<Typography variant="body2">${total.toFixed(2)}</Typography>
|
<Typography variant="body2">${cart.subtotal.toFixed(2)}</Typography>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
|
|
||||||
<ListItem sx={{ py: 1, px: 0 }}>
|
<ListItem sx={{ py: 1, px: 0 }}>
|
||||||
|
|
@ -519,7 +527,7 @@ const CheckoutPage = () => {
|
||||||
<ListItem sx={{ py: 1, px: 0 }}>
|
<ListItem sx={{ py: 1, px: 0 }}>
|
||||||
<ListItemText primary="Total" />
|
<ListItemText primary="Total" />
|
||||||
<Typography variant="subtitle1" sx={{ fontWeight: 700 }}>
|
<Typography variant="subtitle1" sx={{ fontWeight: 700 }}>
|
||||||
${(total + shippingCost).toFixed(2)}
|
${(cart.subtotal + shippingCost).toFixed(2)}
|
||||||
</Typography>
|
</Typography>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
</List>
|
</List>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue