Getting order history to show and be linked to the user
I have created an E-commerce website using the MERN stack and GraphQL. I am having a problem where when the Signed In user purchases products from the payment page, the order is not linked to the user in the database and the order history shows up blank. In my MongoDB compass I'm able to see the order collection with an array of products purchased in that order but the User collection's orders array remains blank.
These lines of code are what I am working with currently:-
Payment Page:
import React, { useEffect, useState } from "react";
import CurrencyFormat from "react-currency-format";
import CheckoutProduct from "../Checkout/CheckoutProduct";
import { useQuery } from "@apollo/client";
import { QUERY_USER } from "../../utils/queries";
import { useMutation } from "@apollo/client";
import { ADD_ORDER } from "../../utils/mutations";
import { getCartTotal } from "../../utils/reducer";
import { useStateValue } from "../../StateProvider";
import { CardElement, useElements, useStripe } from "@stripe/react-stripe-js";
import HeaderTwo from "../../components/HeaderTwo";
import "./Payment.css";
import { Link, useNavigate } from "react-router-dom";
const Payment = () => {
const [{ cart, address, }, dispatch] = useStateValue();
const {user} = useQuery(QUERY_USER);
const {addOrder} = useMutation(ADD_ORDER);
const elements = useElements();
const stripe = useStripe();
const navigate = useNavigate();
const handleSubmit = async (event) => {
event.preventDefault();
if (!stripe || !elements) {
return;
}
const { clientSecret } = await fetch(
`/payment/create?total=${Math.floor(getCartTotal(cart))}`,
{
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
paymentMethodType: "card",
currency: "usd",
}),
}
).then((response) => response.json());
const { paymentIntent } = await stripe
.confirmCardPayment(clientSecret, {
payment_method: {
card: elements.getElement(CardElement),
},
})
.then((result) => {
alert("Payment Successful");
fetch("/orders/add", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
cart: cart.map(product => (product.id)),
price: getCartTotal(cart),
email: user?.email,
address: address,
}),
})
.then((response) => {
if (response.ok) {
dispatch({
type: "EMPTY_CART",
});
navigate("/orders");
}
})
.catch((error) => console.log(error));
})
.catch((error) => console.log(error));
};
return (
<div>
<HeaderTwo />
<div className="payment">
<div className="payment_container">
<h1>
Checkout (<Link to="/checkout">{cart?.length} items</Link>)
</h1>
<div className="payment_section">
<div className="payment_title">
<h3>Delivery Address</h3>
</div>
<div className="payment_address">
<p>{address.fullName}</p>
<p>{address.userEmail}</p>
<p>{address.userAddress}</p>
<p>{address.userCity}</p>
<p>{address.userState}</p>
<p>{address.userZip}</p>
<p>{address.userPhone}</p>
</div>
</div>
<div className="payment_section">
<div className="payment_title">
<h3>Review items and delivery</h3>
</div>
<div className="payment_items">
{cart.map((item) => (
<CheckoutProduct
id={item.id}
title={item.title}
image={item.image}
price={item.price}
rating={item.rating}
/>
))}
</div>
</div>
<div className="payment_section">
<div className="payment_title">
<h3>Payment Method</h3>
</div>
<div className="payment_details">
<form>
<CardElement />
<div className="payment_priceContainer">
<CurrencyFormat
renderText={(value) => <h3>Order Total: {value}</h3>}
decimalScale={2}
value={getCartTotal(cart)}
displayType={"text"}
thousandSeparator={true}
prefix={"$"}
/>
</div>
</form>
</div>
</div>
</div>
<button onClick={handleSubmit}>
<span>
<strong>Buy Now</strong>
</span>
</button>
</div>
</div>
);
};
export default Payment;
Order History
import React, { useState, useEffect } from "react";
import { useQuery } from "@apollo/client";
import { QUERY_USER } from "../../utils/queries";
import HeaderTwo from "../../components/HeaderTwo";
import CheckoutProduct from "../../pages/Checkout/CheckoutProduct";
import "./OrderHistory.css"
const OrderHistory = () => {
const { loading, error, data } = useQuery(QUERY_USER);
const [orders, setOrders] = useState([]);
useEffect(() => {
if (data) {
fetch(`/orders/get?email=${data.user.email}`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
// query: JSON.stringify({ email: data.user.email }),
})
.then((response) => response.json())
.then((data) => setOrders(data));
}
}, [data]);
if (loading) return <div>Loading...</div>;
if (error) return <div>Error: {error.message}</div>;
const { user } = data;
return (
<div>
<HeaderTwo />
<div className="order_history">
<h2>Viewing {user ? `${user.email}'s` : "your"} orders.</h2>
{user?.orders ? (
user.orders.map((order) => (
<div key={order._id} className="order_container">
<h3>
{new Date(parseInt(order.purchaseDate)).toLocaleDateString()}
</h3>
<div className="order_info">
{order.products.map((product) => (
<div key={product._id} className="product_info">
<CheckoutProduct
id={product._id}
title={product.name}
price={product.price}
image={product.image}
rating={product.rating}
/>
</div>
))}
</div>
</div>
))
) : (
<div>No orders for this user.</div>
)}
</div>
</div>
);
};
export default OrderHistory;
Server.js
const express = require("express");
const { ApolloServer } = require("apollo-server-express");
const path = require("path");
const { authMiddleware } = require("./utils/auth");
const { typeDefs, resolvers } = require("./schemas");
const Orders = require("./models/Order");
const cors = require("cors");
const db = require("./config/connection");
const stripeInit = require("stripe");
const stripe = stripeInit(
"Stripe API Key"
);
const PORT = process.env.PORT || 3001;
const app = express();
const server = new ApolloServer({
typeDefs,
resolvers,
context: authMiddleware,
});
app.use(express.urlencoded({ extended: false }));
app.use(express.json());
app.use(cors());
if (process.env.NODE_ENV === "production") {
app.use(express.static(path.join(__dirname, "../client/build")));
}
app.get("/", (req, res) => {
res.sendFile(path.join(__dirname, "../client/build/index.html"));
});
//Stripe API
app.post("/payment/create", async (req, res) => {
const total = req.query.total;
console.log("Payment Request recieved for $", total);
const payment = await stripe.paymentIntents.create({
amount: total * 100,
currency: "usd",
payment_method_types: ["card"],
});
res.json({
clientSecret: payment.client_secret,
});
});
app.post("/orders/add", (req, res) => {
const products = req.body.cart;
const price = req.body.price;
const email = req.body.email;
const address = req.body.address;
const orderDetail = {
products: products,
price: price,
address: address,
email: email,
};
Orders.create(orderDetail, (err, result) => {
if (err) {
console.log(err);
} else {
console.log("order added to database >> ", result);
}
});
});
app.post("/orders/get", (req, res) => {
const email = req.query.email;
Orders.find((err, result) => {
if (err) {
console.log(err);
} else {
const userOrders = result.filter((order) => order.email === email);
res.send(userOrders);
}
});
});
// Create a new instance of an Apollo server with the GraphQL schema
const startApolloServer = async (typeDefs, resolvers) => {
await server.start();
server.applyMiddleware({ app });
db.once("open", () => {
app.listen(PORT, () => {
console.log(`API server running on port ${PORT}!`);
console.log(
`Use GraphQL at http://localhost:${PORT}${server.graphqlPath}`
);
});
});
};
// Call the async function to start the server
startApolloServer(typeDefs, resolvers);
User Model
const mongoose = require('mongoose');
const { Schema } = mongoose;
const bcrypt = require('bcrypt');
const Order = require('./Order');
const userSchema = new Schema({
firstName: {
type: String,
required: true,
trim: true
},
lastName: {
type: String,
required: true,
trim: true
},
email: {
type: String,
required: true,
unique: true
},
password: {
type: String,
required: true,
minlength: 5
},
orders: [Order.schema]
});
// set up pre-save middleware to create password
userSchema.pre('save', async function(next) {
if (this.isNew || this.isModified('password')) {
const saltRounds = 10;
this.password = await bcrypt.hash(this.password, saltRounds);
}
next();
});
// compare the incoming password with the hashed password
userSchema.methods.isCorrectPassword = async function(password) {
return await bcrypt.compare(password, this.password);
};
const User = mongoose.model('User', userSchema);
module.exports = User;
Order Model
const mongoose = require('mongoose');
const { Schema } = mongoose;
const orderSchema = new Schema({
purchaseDate: {
type: Date,
default: Date.now
},
email:{
type: String
},
products: [
{
type: Schema.Types.ObjectId,
ref: 'Product'
}
]
});
const Order = mongoose.model('Order', orderSchema);
module.exports = Order;
Comments
Post a Comment