I'm working on a project and i want to add some cool feature on shopping basket/cart. Like after adding something in shopping basket i want to add something more in shopping basket itself by clicking on checkbox button and it will expand more items and by click on add button the item will add directly on the same shopping basket. And the cart value will update dynamically.
I've added some screenshots for what i'm actually want shopping basket/cart - 1 and then updated shopping basket by adding some dropdown items - 2 and after all the payment section updated payment page - 3
That's all. I know for this there may be a lot of things to do. So if any one can give me some solutions or suggestions or references like any existing projects or works - all these things will help me a lot. Kindly guide me.
I'm adding my shopping basket and payment code what i've done yet.
Booking.js
import React from 'react';
import "./Booking.css";
import BookingProduct from '../bookingproduct/BookingProduct';
import Subtotal from '../subtotal/Subtotal';
import { useStateValue } from '../../StateProvider';
import { offer } from '../../assets';
const Booking = () => {
const [{ basket, user }, dispatch] = useStateValue();
return (
<div className='booking'>
<div className="booking__left">
<img
className="booking__ad"
src={ offer }
alt=""
/>
<div>
<h4 style={{marginTop: 5, padding: 10}}>
{/* {!user ? 'Hello,' : 'Welcome,'} */}
Hello,
{!user ? <span style={{color: 'red', fontSize: 18}}> You've not Signed In yet !</span> : <span style={{color: 'green', fontSize: 18}}> {user?.email}</span>}
</h4>
<h3 className='booking__title'>
Your Shopping Basket
</h3>
{/* To show selected products on Booking page */}
{basket.map(item => (
<BookingProduct
id={item.id}
title={item.title}
image={item.image}
info={item.info}
price={item.price}
stock={item.stock}
nostock={item.nostock}
rating={item.rating}
/>
))}
</div>
</div>
<div className="booking__right">
<Subtotal/>
</div>
</div>
)
}
export default Booking
Subtotal.js
import React from 'react';
import './Subtotal.css';
import CurrencyFormat from 'react-currency-format';
import { useStateValue } from '../../StateProvider';
import { getBasketTotal } from '../../reducer';
import { useNavigate } from 'react-router-dom';
const Subtotal = () => {
const navigate = useNavigate();
const [{ basket }, dispatch] = useStateValue();
return (
<div className='subtotal__title'>
<h1>BOX OFFICE</h1>
<div className='subtotal'>
<CurrencyFormat
renderText={(value) => (
<>
<p>
Subtotal ({ basket?.length } items): <strong>{value}</strong>
</p>
<small className='subtotal__gift'>
<input type="checkbox"/>
Add Food and Beverage
</small>
<p className='subtotal__food'>
▶︎ Order for Food and Beverage will be deliver on same booked theatre.
</p>
</>
)}
decimalScale={2}
value={getBasketTotal(basket)}
displayType={"text"}
thousandSeparator={true}
prefix={"₹ "}
/>
</div>
{/* Checkout Button */}
<button onClick={e => navigate('/payment')}>
Book Now
</button>
</div>
)
}
export default Subtotal
BookingProduct.js
import React from 'react';
import './BookingProduct.css';
import { useStateValue } from '../../StateProvider';
const BookingProduct = ({ id, image, title, price, rating, hideButton, info, stock, nostock }) => {
const [{ basket }, dispatch] = useStateValue();
const removeFromBasket = () => {
// Remove the item from basket
dispatch({
type: 'REMOVE_FROM_BASKET',
id: id,
})
}
return (
<div className='bookingProduct'>
<img
className='bookingProduct__image'
src={image}
alt=""
/>
<div className="bookingProduct__info">
<p className="bookingProduct__title">
{title}
</p>
<p style={{fontSize: 10}}>
{info}
</p>
<p className="bookingProduct__price">
<small style={{fontWeight: 'bold', fontSize: '15px'}}>₹ </small>
<strong style={{fontSize: 14 ,fontWeight: 100}}>{price}</strong>
<p className="bookingProduct__stock">
{/* {stock} */}
<small style={{color: "green"}}> {stock}</small>
<small style={{color: "darkred"}}>{nostock} </small>
</p>
</p>
<div className="bookingProduct__rating">
{Array(rating).fill().map((_, i) => (
<p></p>
))}
</div>
{!hideButton && (
<button onClick={removeFromBasket}>
Remove from Box Office
</button>
)}
</div>
</div>
)
}
export default BookingProduct
Payment.js
import React, { useEffect, useState } from 'react';
import { CardElement, useElements, useStripe } from '@stripe/react-stripe-js';
import { Link, useNavigate } from 'react-router-dom';
import BookingProduct from '../bookingproduct/BookingProduct';
import './Payment.css';
import { useStateValue } from '../../StateProvider';
import { getBasketTotal } from '../../reducer';
import CurrencyFormat from 'react-currency-format';
import axios from '../../axios';
import { db } from '../../firebase';
const Payment = () => {
const [{ basket, user }, dispatch] = useStateValue();
const navigate = useNavigate();
// const stripe = loadStripe('secret_key');
const stripe = useStripe();
const elements = useElements();
const [succeeded, setSucceeded] = useState(false);
const [processing, setProcessing] = useState("");
const [error, setError] = useState(null);
const [disabled, setDisabled] = useState(true);
const [clientSecret, setClientSecret] = useState(true);
useEffect(() => {
// generate the special stripe secret which allows us to charge a customer
const getClientSecret = async () => {
const response = await axios({
method: 'post',
// Stripe expects the total in a currencies subunits => Rupees to Paisa
url: `/payments/create?total=${getBasketTotal(basket) * 100}`
});
setClientSecret(response.data.clientSecret)
}
getClientSecret();
}, [basket])
console.log('THE SECRET IS >>>', clientSecret)
console.log('', user)
const handleSubmit = async (event) => {
// Do all stripe stuff...
event.preventDefault();
setProcessing(true);
const payload = await stripe.confirmCardPayment(clientSecret, {
payment_method: {
card: elements.getElement(CardElement)
}
}).then(({ paymentIntent }) => {
// paymentIntent = payment confirmation
console.log({ user, basket, paymentIntent })
// adding orders on order page with user details
db
.collection('users')
.doc(user?.uid)
.collection('orders')
.doc(paymentIntent.id)
.set({
basket: basket,
amount: paymentIntent.amount,
created: paymentIntent.created
})
setSucceeded(true);
setError(null);
setProcessing(false);
dispatch({
type: 'EMPTY_BASKET'
})
// redirect the page and never back to payment page again since the payment is done
navigate('/orders', { replace: true })
})
}
const handleChange = event => {
// card element work
// listen for changes in the CardElement
// and display any errors as the customer types their card details
setDisabled(event.empty);
setError(event.error ? event.error.message : "");
}
return (
<div className='payment'>
<div className="payment__container">
{/* Number of items in cart / basket */}
<h1>
Box Office Booking (
<Link to='/booking' style={{fontSize: 25, verticalAlign: 'middle'}}>
{basket?.length} tickets
</Link>
)
</h1>
<div className="payment__section">
<div className="payment__title">
<h4>User</h4>
</div>
<div className="payment__address">
<p>{!user ? <span style={{color: 'red'}}>Please Sign In before making a booking !</span> : <span style={{color: 'green'}}> {user?.email}</span>}</p>
<p></p>
</div>
</div>
{/* Payment Section => Delivery Address */}
<div className="payment__section">
<div className="payment__title">
<h4>Nearest Theatre</h4>
</div>
<div className="payment__address">
<p>2023 JavaScript Road</p>
<p>Developers Colony, Stack World</p>
<p></p>
</div>
</div>
{/* Payment Section => Review Items */}
<div className="payment__section">
<div className="payment__title">
<h4>Review Tickets</h4>
</div>
<div className="payment__items">
{/* all the selected products */}
{basket.map(item => (
<BookingProduct
id={item.id}
title={item.title}
image={item.image}
info={item.info}
price={item.price}
stock={item.stock}
nostock={item.nostock}
rating={item.rating}
/>
))}
</div>
</div>
{/* Payment Section => Payment Method */}
<div className="payment__section">
<div className="payment__title">
<h4>Payment Method</h4>
</div>
<div className="payment__details">
<p className='payment__card'>Card Details</p>
{/* Stripe Work for Payment */}
<form onSubmit={handleSubmit}>
<CardElement onChange={handleChange}/>
<div className="payment__priceContainer">
<CurrencyFormat
renderText={(value) => (
<h4>
Booking Total: {value}
</h4>
)}
decimalScale={2}
value={getBasketTotal(basket)}
displayType={"text"}
thousandSeparator={true}
prefix={"₹ "}
/>
<button disabled={processing || disabled || succeeded}>
<span>
{processing ? <p style={{color: 'green'}}>Processing</p> : <span style={{color: ''}}>Book Now</span>}
</span>
</button>
</div>
{/* Errors */}
{error && <div>{error}</div>}
</form>
</div>
</div>
</div>
</div>
)
}
export default Payment
That's all.... if anything need kindly let me know. Thank You.