File tree

15 files changed

+192
-30
lines changed

15 files changed

+192
-30
lines changed
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ const stripe = require("stripe")('sk_test_51HeTtQImEgmfO9dxsYTv9vHitaqlfq2bW9QkR
1515
const app = express();
1616

1717
// - Middlrwares
18-
app.use(cors({ origin: true }))
18+
app.use(cors())
1919
app.use(express.json());
2020

2121
// - API Routes
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
"firebase": "^7.23.0",
1616
"formik": "^2.2.0",
1717
"history": "^5.0.0",
18+
"moment": "^2.29.1",
1819
"node-sass": "^4.14.1",
1920
"react": "^16.13.1",
2021
"react-bootstrap": "^1.3.0",
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,6 @@
1919
@import './components/card/CheckoutCard.scss';
2020
@import './views/product/Product.scss';
2121
@import './views/payment/Payment.scss';
22+
@import './components/order/Order.scss';
23+
@import './views/orders/Orders.scss';
24+
@import './views/payment/Payment.scss';
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ const CheckoutCard = ({
1111
active,
1212
title,
1313
quantity,
14+
hiddenAction
1415
}) => {
1516
const dis = useDis();
1617
const formatedPrice = price.toLocaleString("en-US", {
@@ -47,7 +48,8 @@ const CheckoutCard = ({
4748
<div className="checkout-card__delivery">
4849
<span>Enable for FREE Delivery </span>
4950
</div>
50-
<div className="checkout-card__action">
51+
{
52+
!hiddenAction && <div className="checkout-card__action">
5153
<div className="checkout-card__action__qty">
5254
<Form>
5355
<Form.Group>
@@ -65,6 +67,7 @@ const CheckoutCard = ({
6567
<span onClick={removeCartItem}> Delete</span>
6668
</div>
6769
</div>
70+
}
6871
</div>
6972
<div className="checkout-card__price">
7073
<span>{formatedPrice}</span>
Original file line numberDiff line numberDiff line change
@@ -59,11 +59,12 @@ const Header = () => {
5959
<span>{user ? "Sign Out" : "Sign In"}</span>
6060
</div>
6161
</RouterLink>
62-
63-
<div className="header__nav__option">
64-
<span>returns</span>
65-
<span>&orders</span>
66-
</div>
62+
<RouterLink to='/orders'>
63+
<div className="header__nav__option">
64+
<span>returns</span>
65+
<span>&orders</span>
66+
</div>
67+
</RouterLink>
6768
<RouterLink to="/checkout">
6869
<div className="header__nav__cart">
6970
<span>{cartCount}</span>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import React from "react";
2+
import moment from "moment";
3+
import CheckoutCard from "./../card/CheckoutCard";
4+
import CurrencyFormat from "react-currency-format";
5+
6+
const Order = ({ order }) => {
7+
console.log(order);
8+
return (
9+
<div className="order">
10+
<h2>Order</h2>
11+
<p>{moment.unix(order.data.created).format("MMMM Do YYYY,h:mma")}</p>
12+
<p className="order__id">
13+
<small>{order.id}</small>
14+
</p>
15+
{order.data.cartItems &&
16+
order.data.cartItems.map(({ productId, ...props }) => (
17+
<div key={productId}>
18+
<CheckoutCard productId={productId} hiddenAction {...props} />
19+
<div className="checkout__cart__card" />
20+
</div>
21+
))}
22+
<CurrencyFormat
23+
value={order.data.amount / 100}
24+
displayType={"text"}
25+
thousandSeparator={true}
26+
decimalScale={2}
27+
prefix={"$"}
28+
renderText={(value) => (
29+
<div className='order__total'>
30+
<h3>Order Total : {value}</h3>
31+
</div>
32+
)}
33+
/>
34+
</div>
35+
);
36+
};
37+
38+
export default Order;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
.order {
2+
padding: 40px;
3+
margin: 20px 0;
4+
border: 1px solid lightgray;
5+
position: relative;
6+
background-color: white;
7+
8+
&__id {
9+
position: absolute;
10+
top: 40px;
11+
right: 20px;
12+
}
13+
&__total {
14+
font-weight: 500;
15+
text-align: right;
16+
}
17+
}
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,12 @@ export const cartSlice = createSlice({
3939
removeItem: (state, action) => {
4040
state.cart = state.cart.filter((item) => item.productId !== action.payload);
4141
},
42+
emptyCart: (state) => {
43+
state.cart=[]
44+
}
4245
},
4346
});
4447

45-
export const { addItem, removeItem , updateItemQuantity} = cartSlice.actions;
48+
export const { addItem, removeItem , updateItemQuantity, emptyCart} = cartSlice.actions;
4649

4750
export default cartSlice.reducer;
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ export const fetchUser = (uid) => {
2222
.get()
2323
.then((doc) => {
2424
if (doc.exists) {
25-
dis(setUser(doc.data()));
25+
dis(setUser({...doc.data(), uid: doc.id}));
2626
} else {
2727
console.log("No user found !");
2828
}
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import Product from "./views/product/Product";
1212
import Payment from "./views/payment/Payment";
1313
import { loadStripe } from "@stripe/stripe-js";
1414
import { Elements } from "@stripe/react-stripe-js";
15+
import Orders from "./views/orders/Orders";
1516

1617
const promise = loadStripe(
1718
"pk_test_51HeTtQImEgmfO9dx3rV5DTtE8sIiZOUG8KZbdXdfOEAzOG54ej7Xivl2kXmgMrHGV8dLRSlLioJ56v4KbB0bkMnP00UWd0uuAL"
@@ -35,6 +36,10 @@ const routes = [
3536
path: 'payment',
3637
element: <Elements stripe={promise}><Payment/></Elements>
3738
},
39+
{
40+
path: 'orders',
41+
element:<Orders/>
42+
},
3843
{
3944
path: 'product/:productId',
4045
element: <Product/>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import React, { useState, useEffect } from "react";
2+
import { db } from "../../firebase.utils";
3+
import { selectUser } from "../../redux/userSlice";
4+
import { useSelector } from "react-redux";
5+
import Order from './../../components/order/Order';
6+
7+
const Orders = () => {
8+
const [orders, setOrders] = useState([]);
9+
const user = useSelector(selectUser);
10+
11+
useEffect(() => {
12+
if (user) {
13+
db.collection("users")
14+
.doc(user?.uid)
15+
.collection("orders")
16+
.orderBy("created", "desc")
17+
.onSnapshot((snapshot) =>
18+
setOrders(
19+
snapshot.docs.map((doc) => ({
20+
id: doc.id,
21+
data: doc.data(),
22+
}))
23+
)
24+
);
25+
} else {
26+
setOrders([])
27+
}
28+
}, [user]);
29+
30+
31+
return (
32+
<div className="orders">
33+
<h1>Your Orders</h1>
34+
<div className="orders__order">
35+
{
36+
orders?.map(order => (
37+
<Order order={order} key={order.id}/>
38+
))
39+
}
40+
</div>
41+
</div>
42+
);
43+
};
44+
45+
export default Orders;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
.orders{
2+
padding: 20px 80px;
3+
4+
& > h1 {
5+
padding: 30px 0;
6+
}
7+
}
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,23 @@
11
import React, { useState, useEffect } from "react";
2-
import {useNavigate} from 'react-router-dom';
3-
import { useSelector } from "react-redux";
2+
import { useNavigate } from "react-router-dom";
3+
import { useSelector, useDis } from "react-redux";
44
import { selectUser } from "../../redux/userSlice";
5+
import { emptyCart } from "../../redux/cartSlice";
56
import CheckoutCard from "../../components/card/CheckoutCard";
6-
import { selectCartItems } from "../../redux/cartSelector";
7-
import { selectCartItemsCount, selectSubtotal } from "../../redux/cartSelector";
7+
import {
8+
selectCartItemsCount,
9+
selectSubtotal,
10+
selectCartItems,
11+
} from "../../redux/cartSelector";
812
import { Link } from "react-router-dom";
913
import { useStripe, useElements, CardElement } from "@stripe/react-stripe-js";
1014
import CurrencyFormat from "react-currency-format";
1115
import axios from "../../axios";
16+
import { db } from "../../firebase.utils";
1217

1318
const Payment = () => {
14-
1519
const navigate = useNavigate();
20+
const dis = useDis();
1621

1722
const user = useSelector(selectUser);
1823
const cartItems = useSelector((state) => selectCartItems(state));
@@ -35,29 +40,39 @@ const Payment = () => {
3540
// Stripe expects the total in a currencies subunits
3641
url: `/payments/create?total=${subtotal * 100}`,
3742
});
38-
43+
3944
setClientSecret(response.data.clientSecret);
4045
};
4146
getClientSecret();
4247
}, [subtotal]);
43-
44-
45-
48+
console.log(clientSecret)
4649
const handleSubmit = async (event) => {
4750
event.preventDefault();
4851
setProcessing(true);
4952

50-
const payload = await stripe.confirmCardPayment(clientSecret, {
51-
payment_method: {
52-
card: elements.getElement(CardElement)
53-
}
54-
}).then(({ paymentIntent }) => {
55-
// paymentIntent = payment confirmation
56-
setSucceeded(true)
57-
setError(null)
58-
setProcessing(false)
59-
navigate('/orders')
60-
})
53+
await stripe
54+
.confirmCardPayment(clientSecret, {
55+
payment_method: {
56+
card: elements.getElement(CardElement),
57+
},
58+
})
59+
.then(({ paymentIntent }) => {
60+
// paymentIntent = payment confirmation
61+
db.collection("users")
62+
.doc(user?.uid)
63+
.collection("orders")
64+
.doc(paymentIntent.id)
65+
.set({
66+
cartItems,
67+
amount: paymentIntent.amount,
68+
created: paymentIntent.created,
69+
});
70+
setSucceeded(true);
71+
setError(null);
72+
setProcessing(false);
73+
dis(emptyCart());
74+
navigate("/orders");
75+
});
6176
};
6277

6378
const handleChange = (event) => {
@@ -92,7 +107,7 @@ const Payment = () => {
92107
{cartItems.length > 0 &&
93108
cartItems.map(({ productId, ...props }) => (
94109
<div key={productId}>
95-
<CheckoutCard productId={productId} {...props} />
110+
<CheckoutCard productId={productId} hiddenAction {...props} />
96111
<div className="checkout__cart__card" />
97112
</div>
98113
))}
Original file line numberDiff line numberDiff line change
@@ -28,4 +28,23 @@
2828
&__address, &__items,&__details {
2929
flex: 0.8;
3030
}
31+
32+
&__details > form {
33+
max-width: 400px;
34+
}
35+
36+
&__details > h3 {
37+
padding-bottom: 20px;
38+
}
39+
&__details > form > div > button{
40+
background: #f0c14b;
41+
border-radius: 2px;
42+
width: 100%;
43+
height: 30px;
44+
border: 1px solid;
45+
font-weight: bold;
46+
margin-top: 10px;
47+
border-color: #a88724 #9c7e31 #846a29;
48+
color: #111;
49+
}
3150
}
Original file line numberDiff line numberDiff line change
@@ -7815,6 +7815,11 @@ mixin-object@^2.0.1:
78157815
dependencies:
78167816
minimist "^1.2.5"
78177817

7818+
moment@^2.29.1:
7819+
version "2.29.1"
7820+
resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.1.tgz#b2be769fa31940be9eeea6469c075e35006fa3d3"
7821+
integrity sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==
7822+
78187823
move-concurrently@^1.0.1:
78197824
version "1.0.1"
78207825
resolved "https://registry.yarnpkg.com/move-concurrently/-/move-concurrently-1.0.1.tgz#be2c005fda32e0b29af1f05d7c4b33214c701f92"

0 commit comments

Comments
 (0)