E-Commerce-Module/frontend/src/components/CouponInput.jsx

138 lines
No EOL
3.7 KiB
JavaScript

import React, { useState } from 'react';
import {
TextField,
Button,
Box,
Typography,
CircularProgress,
Alert,
Paper,
Divider,
Chip
} from '@mui/material';
import { useMutation, useQueryClient } from '@tanstack/react-query';
import couponService from '../services/couponService';
import { useAuth } from '../hooks/reduxHooks';
/**
* Component for inputting and applying coupon codes to the cart
*/
const CouponInput = () => {
const [couponCode, setCouponCode] = useState('');
const [error, setError] = useState(null);
const { user } = useAuth();
const queryClient = useQueryClient();
// Get current cart data from cache
const cartData = queryClient.getQueryData(['cart', user]);
// Apply coupon mutation
const applyCoupon = useMutation({
mutationFn: ({ userId, code }) => couponService.applyCoupon(userId, code),
onSuccess: (data) => {
// Update React Query cache directly
queryClient.setQueryData(['cart', user], data);
setError(null);
},
onError: (error) => {
setError(error.message || 'Failed to apply coupon');
},
});
// Remove coupon mutation
const removeCoupon = useMutation({
mutationFn: (userId) => couponService.removeCoupon(userId),
onSuccess: (data) => {
// Update React Query cache directly
queryClient.setQueryData(['cart', user], data);
setError(null);
},
onError: (error) => {
setError(error.message || 'Failed to remove coupon');
},
});
// Handle coupon code input change
const handleCouponChange = (e) => {
setCouponCode(e.target.value.toUpperCase());
setError(null);
};
// Handle applying coupon
const handleApplyCoupon = () => {
if (!couponCode) {
setError('Please enter a coupon code');
return;
}
applyCoupon.mutate({ userId: user, code: couponCode });
};
// Handle removing coupon
const handleRemoveCoupon = () => {
removeCoupon.mutate(user);
setCouponCode('');
};
// Check if a coupon is already applied
const hasCoupon = cartData?.couponCode;
return (
<Paper variant="outlined" sx={{ p: 2, mb: 3 }}>
<Typography variant="h6" gutterBottom>
Discount Code
</Typography>
{error && (
<Alert severity="error" sx={{ mb: 2 }}>
{error}
</Alert>
)}
{hasCoupon ? (
<Box>
<Box sx={{ display: 'flex', alignItems: 'center', mb: 2 }}>
<Chip
label={cartData.couponCode}
color="success"
sx={{ mr: 2 }}
/>
<Typography variant="body2" color="success.main">
Discount applied: -${cartData.couponDiscount?.toFixed(2)}
</Typography>
</Box>
<Button
variant="outlined"
color="error"
size="small"
onClick={handleRemoveCoupon}
disabled={removeCoupon.isLoading}
>
{removeCoupon.isLoading ? <CircularProgress size={24} /> : 'Remove Coupon'}
</Button>
</Box>
) : (
<Box sx={{ display: 'flex' }}>
<TextField
fullWidth
placeholder="Enter coupon code"
value={couponCode}
onChange={handleCouponChange}
disabled={applyCoupon.isLoading}
inputProps={{ style: { textTransform: 'uppercase' } }}
sx={{ mr: 2 }}
/>
<Button
variant="contained"
onClick={handleApplyCoupon}
disabled={!couponCode || applyCoupon.isLoading}
>
{applyCoupon.isLoading ? <CircularProgress size={24} /> : 'Apply'}
</Button>
</Box>
)}
</Paper>
);
};
export default CouponInput;