I am trying to implement stripe payment and create order in my database. Even though my stripe payment is successful I'm unable to create order in database. My implementation -
Shopping Cart:
import './ShoppingCart.css';
import {useSelector} from 'react-redux';
import { useState, useEffect } from 'react';
import {useNavigate }from 'react-router-dom';
import { useDispatch } from 'react-redux';
import StripeCheckout from 'react-stripe-checkout';
import logo from '../images/BritonStore.png';
import {userRequest} from '../requestMethods';
import { Link } from 'react-router-dom';
// const KEY = process.env.REACT_APP_STRIPE;
const ShoppingCart = ({id}) => {
const cart = useSelector(state=>state.cart);
const [stripeToken, setStripeToken] = useState(null);
const history = useNavigate();
const user = JSON.parse(localStorage.getItem("persist:root"))?.user;
const currentUser = user && JSON.parse(user).currentUser;
const TOKEN = currentUser?.accessToken;
// console.log(TOKEN)
const width = window.innerWidth;
const onToken = (token) => {
setStripeToken(token);
};
useEffect(()=>{
const makeRequest = async ()=>{
try{
const res = await userRequest.post("/checkout/pay", {
tokenId:stripeToken.id,
amount: cart.total,
});
console.log(res)
history('/success', {stripeData: res.data, products: cart});
}catch(err){
console.log(err)
}
};
stripeToken && makeRequest();
}, [stripeToken, cart.total, history])
return (
<div>
{cart.products.length === 0 && width < 451 ?
<div className='emt-cart'>
<div className="emt-wrapper">
<h2 className="emt-header">Your Bag is Empty.</h2>
<div id="sc-empty-cart-animated-image" type="image/svg+xml" data="https://m.media-amazon.com/images/G/02/cart/empty/animated/rolling-cart-desaturated._CB405717979_.svg">
<img alt="" src="https://m.media-amazon.com/images/G/02/cart/empty/animated/cart-fallback-desaturated._CB405717979_.svg"/>
</div>
</div>
</div>
:
cart.products.length === 0 && width > 451 ?
<div className="emt-pc-wrapper">
<div className='emt-pc'>
<img src="https://m.media-amazon.com/images/G/02/cart/empty/kettle-desaturated._CB424695504_.svg" alt="" />
<div className="emt-pc-text">
<h2>Your Bag is Empty</h2>
<Link to='/store'>Continue Shopping</Link>
</div>
</div>
</div>
:
<div className='cartscreen'>
<div className='cartscreen-title'>
<h2>Review your bag.</h2>
<p>Free delivery and free returns</p>
</div>
{cart.products.map(product=>(
<div className='cart' key={cart.products.id}>
<div className='image'>
<img src={product.image} key={product._id}/>
</div>
<div className='details'>
<div className='title'>
<h3>{product.title}</h3>
<p>Product ID: {product._id}</p>
</div>
<div className='quantity'>
<p>Quantity: {product.quantity}</p>
</div>
<div className='total-price'>
<p>{product.price*product.quantity}$</p>
<p>Remove</p>
</div>
</div>
</div>
))}
<div className='total'>
<div className='left'>
<p>Subtotal</p>
<p>Shipping</p>
</div>
<div className='right'>
<p> {cart.total} $</p>
<p>Free</p>
</div>
</div>
<div className='f-total'>
<h4>Total</h4>
<h4> {cart.total} $</h4>
</div>
<div className='checkout'>
<div className='method'>
<h3>How would you like to checkout ?</h3>
</div>
</div>
<div className='method-cards'>
<div className='card'>
<h2>Proceed To Checkout</h2>
{TOKEN ? <StripeCheckout
name='Store'
image={logo}
billingAddress
shippingAddress
description={`Your total is $${cart.total}`}
amount={cart.total*100}
token={onToken}
stripeKey={"pk_test_51KLN11ITl57zrxjuYSYut39BpN1qKSqcUWCksXqFXJU95FxSIei4O4Lfb4BrH4Rc7hWN7rZRy7sbz2J7QCC08QSf00AwpDVnBB"}
>
<button className='btn-payment'>Pay Now!</button>
</StripeCheckout> : <p className='login-wrng'>Please <Link to='/login'>Login</Link> to continue payment</p>}
{TOKEN ? <p className='payment-methods'>We accept payments from PayPal, Visa, MasterCard</p> : null}
</div>
</div>
</div>}
</div>
)
}
export default ShoppingCart
Success Screen/ Create Order Screen: I believe the problem is in line [ const data = location.state.stripeData;
const cart = location.state.cart;]. the screen goes blank and console says "cannot read properties of undefined stripeData." location = useLocation() returns state null.
import React, {useState, useEffect} from 'react';
import { useLocation } from 'react-router';
import {useSelector} from 'react-redux';
import { userRequest } from '../requestMethods';
const Success = () => {
const location = useLocation();
const data = location.state.stripeData;
const cart = location.state.cart;
const currentUser = useSelector((state) => state.user.currentUser);
const [orderId, setOrderId] = useState(null);
useEffect(() => {
const createOrder = async () => {
try {
const res = await userRequest.post("/orders", {
userId: currentUser._id,
products: cart.products.map((item) => ({
productId: item._id,
quantity: item._quantity,
})),
amount: cart.total,
address: data.billing_details.address,
});
setOrderId(res.data._id);
} catch(err) {
console.log(err)
}
};
data && createOrder();
}, [cart, data, currentUser]);
return (
<div>
{orderId
? `Order has been created successfully. Your order number is ${orderId}`
: `Failed...`}
</div>
)
}
export default Success
order model -
const mongoose = require('mongoose');
const orderSchema = new mongoose.Schema(
{
userId: {type: String, required: true, unique:true},
products: [
{
productId: {type: String},
quantity: {type: Number, default: 1},
},
],
amount: {type:Number, required:true},
address: { type: Object, required:true },
status: {type: String, default: 'pending'},
}, {timestamps: true}
);
module.exports = mongoose.model('Order', orderSchema);
order route -
const Order = require("../models/Order");
const {
verifyToken,
verifyTokenAndAuthorization,
verifyTokenAndAdmin,
} = require("./verifyToken");
const router = require("express").Router();
//CREATE
router.post("/", verifyToken, async (req, res) => {
const newOrder = new Order(req.body);
try {
const savedOrder = await newOrder.save();
res.status(200).json(savedOrder);
} catch (err) {
res.status(500).json(err);
}
});
i don't know if you solved it but i am certain that someone else will find my answer worthwhile. since react-router-dom v6, useHistory() hook was replaced with useNavigate(). you will need to use useNavigate() instead of useHistory(). so your code will be like:
import { useNavigate } from "react-router-dom"
const navigate = useNavigate()
...
navigate("/success", {state: {data: res.data})
this is the new way, i hope that you will understand my solution and implement it in your code.
Related
I've got a React App, that scans products via a Scanner(using serialport.io + socket.io) and adds each scanned product into my frontend cart component.
Right now I got it working, but my solution creates a new row in my cart per product scanned as you can see here and I need it to display a new row only the first time a prod is scanned and then if the same product is detected it only updates the quantity and total per product and also the cart total, something like this...
From what I've searched the best way to do this would be by using react useContext and useReducer but I can't get it working.
This is my code on server side index.js:
io.on("connection", (socket) => {
console.log("Socket Connected");
const port = new SerialPort({
path: "COM6",
baudRate: 9600,
autoOpen: false,
});
socket.on("start_scanner", () => {
port.open(function (err) {
if (err) {
console.log("Error opening port: ", err.message);
}
else{
console.log("Scanner Connected");
}
});
port.on("open", function () {
setInterval(function () {
const portReader = port.read();
if (portReader != null) {
const sensorVal = Buffer.from(portReader).toString();
const soap = require("soap");
const url = "http://example.com?wsdl";
soap.createClient(url, function (err, client) {
client.GetProductById(
{
UserId: "1",
ProductId: sensorVal,
},
function (err, result) {
if (err) return console.log(err);
let productScanned = result.GetProductByIdResult;
socket.broadcast.emit("add_product_to_list", productScanned);
}
);
});
}
}, 700);
});
});
This is my Cart component code:
import { useState, useEffect } from "react";
import io from "socket.io-client";
import ProductRow from "./ProductRow";
import "./ProductsList.css";
const socket = io.connect("http://localhost:5000");
const ProductsList = (props) => {
const [scannedData, setScannedData] = useState([]);
useEffect(() => {
socket.on("add_product_to_list", (productScanned) => {
setScannedData((prevProducts) => [...prevProducts, productScanned]);
});
}, [socket]);
return (
<div className="w-9/12 h-full px-20 py-20 flex flex-col ">
<div className="w-full h-auto my-2 px-3 py-3 font-semibold grid grid-cols-3 bg-blue-600 rounded-xl">
<div className="w-[60%] text-left">Product</div>
<div className="w-[20%] text-left">Quant.</div>
<div className="w-[20%] text-left">Price</div>
</div>
<div
id="products-wrapper"
className="w-full h-[95%] flex flex-col overflow-x-hidden overflow-y-scroll"
>
{scannedData.map((productScanned) => (
<ProductRow data={productScanned} />
))}
</div>
<div className="w-full h-[15%] flex flex-row justify-end">
<div className="w-[20%] h-auto px-3 py-3 font-semibold flex flex-col justify-center bg-blue-600 rounded-xl ">
<div className="w-full text-left">
Total: {/* Total price amount */}{" "}
</div>
<div className="w-full text-left">
Qty: {0}
</div>
</div>
</div>
</div>
);
};
export default ProductsList;
This is my Cart Row component:
import "./ProductsList.css";
const ProductRow = ({ data }) => {
return (
<div
className="product-row w-full h-auto my-2 px-3 py-3 text-black text-center grid grid-cols-3 rounded-xl "
key={data._x003C_Id_x003E_k__BackingField}
>
<div className="w-[60%] text-left">
{data._x003C_Name_x003E_k__BackingField}
</div>
<div className="w-[20%] text-left">{1}</div>
<div className="w-[20%] text-left">
{parseFloat(data._x003C_Price_x003E_k__BackingField).toFixed(2)}€
</div>
</div>
);
};
export default ProductRow;
I've also got a cart-context.js and a CartProvider.js files which I was using to achieve my goal but can't get it to work.
/*cart-context.js*/
import React from "react";
const CartContext = React.createContext({
items: [],
totalAmount: 0,
addItem: (item) => {},
removeItem: (id) => {},
});
export default CartContext;
/*CartProvider.js*/
import { useContext, useReducer } from "react";
import CartContext from "./cart-context";
const defaultCartState = {
items: [],
totalAmount: 0,
};
const cartReducer = (state, action) => {
if (action.type === "ADD_ITEM") {
const updatedTotalAmount = state.totalAmount + action.item.price * action.item.amount;
const existingCartItemIndex = state.items.findIndex(
(item) => item.id === action.item.id
)
const existingCartItem = state.items[existingCartItemIndex];
let updatedItems;
if(existingCartItem){
const updatedItem = {
...existingCartItem,
amount: existingCartItem.amount + action.item.amount
}
updatedItems = [...state.items];
updatedItems[existingCartItemIndex] = updatedItem;
} else{
updatedItems = state.items.concat(action.item);
}
return {
items: updatedItems,
totalAmonut: updatedTotalAmount,
};
}
return defaultCartState;
};
const CartProvider = (props) => {
const [cartState, dispatchCartAction] = useReducer(
cartReducer,
defaultCartState
);
const addItemToCartHandler = (item) => {
dispatchCartAction({ type: "ADD_ITEM", item: item });
};
const removeItemFromCartHandler = (id) => {
dispatchCartAction({ type: "REMOVE_ITEM", id: id });
};
const cartContext = {
items: cartState.items,
totalAmonut: cartState.totalAmonut,
addItem: addItemToCartHandler,
removeItem: removeItemFromCartHandler,
};
return (
<CartContext.Provider value={cartContext}>
{props.children}
</CartContext.Provider>
);
};
export default CartProvider;
Could anyone help me out and help me understand my mistakes?
Thanks in advance.
My app crash when I like on submit button on my react app but when I comment my image code then no problem comes and its enter the other info int mongodb so there is issue in my handlesubmit code there is something missing for image because my backend node.js code work and its enter the image in the databae .I used multer for sending the image.
Here is my react code
Productstate.js
import React, { useState } from 'react'
import Productcontext from './Productcontext'
const Prductstate = (props) => {
const host = "http://localhost:5000"
const productInitial = []
const [product, setProduct] = useState(productInitial);
//Add Note
const addProduct = async (title, description, price, category, image) => {
//TODO API CALL
let url = `${host}/api/product/upload`
//Api Call
const response = await fetch(url, {
method: 'POST', // *GET, POST, PUT, DELETE, etc.
headers: {
'Content-Type': 'application/json',
'auth-token': "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjp7ImlkIjoiNjJlZThmMGI4NmU1YzQ5NDZkM2I3NWQ1In0sImlhdCI6MTY1OTgwMTM1NX0.NdR38S1oby9mSzl1zTH-LYYavsJGjwq6OWsPP_q3YvI"
},
body: JSON.stringify({ title, description, price, category, image }) // body data type must match "Content-Type" header
});
const json = response.json(); // parses JSON response into native JavaScript objects
console.log("adding a new note");
const newpro = {
"_id": "63304097cc95e936a974bd6e",
"user": "62ee8f0b86e5c4946d3b75d5",
"title": title,
"description": description,
"price": price,
"category": category,
"image":{
"data":"BinData(0, 'bG9nc2lnbi5wbmc=')",
"contentType":"image/png "
},
"date": "2022-08-25T10:26:57.324Z",
"__v": 0
}
setProduct(product.concat(newpro));
}
//logic to add a note
return (
<Productcontext.Provider value={{ product, addProduct }}>
{props.children}
</Productcontext.Provider>
)
}
export default Prductstate
Adminform.js
import React,{useContext,useState} from 'react'
import Productcontext from '../context/Productcontext';
const Adminform = () => {
const context=useContext(Productcontext);
const {addProduct}=context;
const handleClick=(e)=>{
e.preventDefault();
addProduct(product.title,product.description,product.price,product.category,product.image);
}
const onChange=(e)=>{
setProduct({...product,[e.target.name]:e.target.value})
}
const [product, setProduct] = useState({title:"",description:"",price:"",category:"",image:""})
return (
<div className="container">
<div className="screen">
<div className="screen__content">
<form className="login" encType="multipart/form-data" onSubmit={handleClick}>
<div className="login__field">
<i className="login__icon fas fa-user"></i>
<input type="text" className="login__input" placeholder="Name of Product" name='title' onChange={onChange}/>
</div>
<div className="login__field">
<i className="login__icon fas fa-lock"></i>
<input type="text" className="login__input" placeholder="Description" name='description' onChange={onChange}/>
</div>
<div className="login__field">
<i className="login__icon fas fa-lock"></i>
<input type="text" className="login__input" placeholder="Price" name='price' onChange={onChange}/>
</div>
<div className="login__field">
<i className="login__icon fas fa-lock"></i>
<input type="text" className="login__input" placeholder="Category" name='category' onChange={onChange}/>
</div>
<div className="login__field">
<i className="login__icon fas fa-lock"></i>
<input type="file" className="login__input" name="uploaded_file" onChange={onChange}/>
</div>
<button className="button login__submit">
<span className="button__text">Add Product</span>
<i className="button__icon fas fa-chevron-right"></i>
</button>
</form>
<div className="social-login">
<h3></h3>
<div className="social-icons">
</div>
</div>
</div>
<div className="screen__background">
<span className="screen__background__shape screen__background__shape4"></span>
<span className="screen__background__shape screen__background__shape3"></span>
<span className="screen__background__shape screen__background__shape2"></span>
<span className="screen__background__shape screen__background__shape1"></span>
</div>
</div>
</div>
)
}
export default Adminform
index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);
My backend code
ProductModel.js
const mongoose = require('mongoose');
const { Schema } = mongoose;
const ProductSchema = new Schema({
user:{
type:mongoose.Schema.Types.ObjectId,
ref:'user'
},
title:{
type:String,
required:true
},
description: {
type: String,
required: [true, "Please write description of product"]
},
price: {
type: Number,
required: [true, "Please Enter the Price"],
maxLength: [15, "Price cannot exceed 8 figure amount"]
},
category: {
type: String,
required: [true, "Category is must"]
},
image:{
data:Buffer,
contentType:String
}
});
const product=mongoose.model('product',ProductSchema);
module.exports=product;
Product.js
const express = require('express');
const { body, validationResult } = require('express-validator');
const router = express.Router();
const multer=require('multer');
const fetchuser = require('../middleware/fetchuser');
const productModel=require('../models/productModel');
const app = express()
//storage
const storage = multer.diskStorage({
destination: 'uploads',
filename: function (req, file, cb) {
cb(null,file.originalname)
}
})
const upload = multer({ storage: storage }).single('testImage');
router.post('/upload',fetchuser,async(req,res)=>{
upload(req,res,(err)=>{
if(err){
console.log(err);
}
else{
const newproduct= new productModel({
title:req.body.title,
description:req.body.description,
price:req.body.price,
category:req.body.category,
user: req.user.id,
image:{
data:req.file.filename,
contentType:'image/png '
}
})
newproduct.save()
.then(()=>res.send("succesfully uploaded")).catch((err)=>console.log(err))
}
})
});
module.exports=router;
index.js
const connectToMongo=require('./db');
var cors = require('cors');
const express = require('express')
connectToMongo();
const app = express()
app.use(cors())
const port = 5000
app.use(express.json());
app.use('/api/authent',require('./routes/authent'));
//app.use('/api/Categories',require('./routes/Categories'));
//app.use('/api/category',require('./routes/category'));
app.use('/api/product',require('./routes/product'));
app.listen(port, () => {
console.log(`ecart Backend listening on port ${port}`)
})
This is my customer schema.
const mongoose = require('mongoose');
const { isEmail } = require('validator');
const uniqueValidator = require('mongoose-unique-validator');
const bcrypt = require('bcrypt');
const requiredString = {
type: String,
required: true
}
const customerSchema = new mongoose.Schema({
firstname: {
type: String,
required: [true, 'Please enter your first name'],
},
lastname: {
type: String,
required:[true, 'Please enter your last name']
},
username: {
type: String,
unique: true,
required: true,
lowercase:true,
},
email: {
type: String,
unique: true,
validate: [isEmail, 'Please enter a valid email'],
lowercase: true
},
address: requiredString,
mobile: {
type:String,
required:true,
unique:true,
maxlength: [11, 'Minimum length is 11 number']
},
barangay: requiredString,
city: requiredString,
province: requiredString,
postalCode:requiredString,
password: {
type: String,
required: [true, 'Please enter a password'],
minlength: [8, 'Minimum password length is 8 characters']
},
verified: {
type: Boolean
},
status: requiredString,
code: requiredString,
profilePicture: {
type: String
}
});
// validate for uniqueness
customerSchema.plugin(uniqueValidator);
// fire a function before saving to database
customerSchema.pre('save', async function(next) {
const salt = await bcrypt.genSalt();
this.password = await bcrypt.hash(this.password,salt);
next();
})
// create static login method for user
customerSchema.statics.login = async function(username,password) {
const customer = await this.findOne({ username });
if(customer) {
const auth = await bcrypt.compare(password,customer.password);
if(auth) {
return customer;
}
throw Error('Incorrect password');
}
throw Error('This username doesn\'t exist');
}
customerSchema.statics.resetPassword = async function(id,password) {
const customer = await this.findById(id);
if(customer) {
const salt = await bcrypt.genSalt();
const newPassword = await bcrypt.hash(password,salt);
return newPassword;
}
}
const CustomerModel = mongoose.model('customer', customerSchema);
module.exports = CustomerModel;
This is my code in signing up in my controller.js
module.exports.customer_signup_post = async (req, res) => {
const { firstname,lastname,username,email,mobile,address,barangay,city,province,postalCode,password } = req.body;
const verified = false;
const code = Math.floor(Math.random() * 100000);
const status = 'active';
const htmlContent = `
<h1>Hi ${firstname}!</h1>
<h2>${code}</h2>
<p>It seems like you registered with this account. Please use this code to verify your account</p>
<p>Thank you for using Tulin Bicycle Shop! Enjoy Shopping!</p>
`
try {
const newCustomer = await Customer.create({ firstname,lastname,username,email,mobile,address,barangay,city,province,postalCode,password,verified,status,code });
const info = await transporter.sendMail({
from: `'Tulin Bicycle Shop' <${process.env.MAIL_ACCOUNT}>`,
to: `${newCustomer.email}`,
subject: 'Account verification',
html: htmlContent
});
console.log("Message was sent: " + info.response);
res.status(201).json({ mssg: `${newCustomer.firstname} has been created, please check your email for verification`, customerId: newCustomer._id,redirect:`/verify/${newCustomer._id}` });
}
catch(err) {
const errors = handleErrors(err);
res.status(400).json({ errors });
}
}
This is the error being shown when I try to signup new user
E11000 duplicate key error collection: capstone.customers index: username_1 dup key: { username: "neknek" }
This is my code in React.js
import { AiOutlineLoading3Quarters } from 'react-icons/ai'
import { Helmet } from 'react-helmet';
import { useState } from 'react';
import { useNavigate,Link } from 'react-router-dom';
import axios from 'axios';
import StepOne from '../../../components/shop/signup.jsx/StepOne';
import StepTwo from '../../../components/shop/signup.jsx/StepTwo';
import StepThree from '../../../components/shop/signup.jsx/StepThree';
const Signup = () => {
const [firstname,setFirstname] = useState('');
const [lastname,setLastname] = useState('')
const [username,setUsername] = useState('');
const [email,setEmail] = useState('');
const [mobile,setMobile] = useState('');
const [address,setAddress] = useState('');
const [barangay,setBarangay] = useState('');
const [city,setCity] = useState('');
const [province,setProvince] = useState('');
const [postalCode,setPostalCode] = useState('');
const [password,setPassword] = useState('');
const [confirmPassword,setConfirmPassword] = useState('');
const [success,setSuccess] = useState('');
const [isLoading,setIsLoading] = useState(false);
const [passErr,setPassErr] = useState('');
const [usernameErr,setUsernameErr] = useState('');
const [emailErr,setEmailErr] = useState('');
const [mobileErr,setMobileErr] = useState('');
const [passLimitErr,setPassLimitErr] = useState('');
const [activeStep,setActiveStep] = useState('step one');
const navigate = useNavigate();
const onSignup = (e) => {
e.preventDefault();
setIsLoading(true);
if(password !== confirmPassword) {
setPassErr('Password doesn\'t match, please check your password');
setTimeout(() => {
setPassErr('');
},2000);
} else {
axios.post('/customer',{ firstname,lastname,username,email,mobile,address,barangay,city,province,postalCode,password })
.then((data) => {
setSuccess(data.data.mssg);
setTimeout(() => {
navigate(data.data.redirect);
},2000)
setIsLoading(false);
}).catch((err) => {
setEmailErr(err.response.data.errors.email);
setPassLimitErr(err.response.data.errors.password);
setUsernameErr(err.response.data.errors.username);
setMobileErr(err.response.data.errors.mobile);
setTimeout(() => {
setEmailErr('');
setUsernameErr('');
setPassLimitErr('');
setMobileErr('');
},2000)
setIsLoading(false);
})
}
}
return (
<>
<Helmet><title>Tulin Bicycle Shop | Signup</title></Helmet>
<div className="content signup-bg h-full overflow-hidden">
<div className="max-content flex items-center justify-center w-1/2">
<img className="object-cover h-4/5" src="/image/bike-bg.png" alt="Bike background" />
<div className="bg-white h-4/5 w-2/5 relative" onSubmit={onSignup}>
<div className="absolute flex items-center gap-2 top-0 right-0 p-4">
<h2>Already have an account?</h2>
<Link to='/login' className="rounded-full border border-gray-700 p-2 shadow-xl">Sign in</Link>
</div>
<div className="px-8 py-24">
<h1 className="text-gray-800 text-5xl">Sign up</h1>
<span>Create your account</span>
{ isLoading && <h2 className="text-sm text-green-500 flex items-center gap-2"><AiOutlineLoading3Quarters className="animate-spin" />Please wait...</h2> }
{ emailErr && <h2 className="text-sm text-red-500">{ emailErr }</h2> }
{ usernameErr && <h2 className="text-sm text-red-500">{ usernameErr }</h2> }
{ mobileErr && <h2 className="text-sm text-red-500">{ mobileErr }</h2> }
<p className="text-sm text-green-500">{ success }</p>
<div className="flex items-center justify-center gap-2">
{/* Pages button */}
<button onClick={() => setActiveStep('step one')} className={`rounded-full w-5 h-5 border border-gray-700 flex justify-center items-center p-1 text-xs ${activeStep === 'step one' && 'bg-gray-300'}`}>1</button>
<button onClick={() => setActiveStep('step two')} className={`rounded-full w-5 h-5 border border-gray-700 flex justify-center items-center p-1 text-xs ${activeStep === 'step two' && 'bg-gray-300'}`}>2</button>
<button onClick={() => setActiveStep('step three')} className={`rounded-full w-5 h-5 border border-gray-700 flex justify-center items-center p-1 text-xs ${activeStep === 'step three' && 'bg-gray-300'}`}>3</button>
</div>
<form className="flex flex-col gap-2 mt-1" onSubmit={onSignup}>
{/* First Step */}
{ activeStep === 'step one' &&
<StepOne
firstname={firstname}
setFirstname={setFirstname}
lastname={lastname}
setLastname={setLastname}
username={username}
setUsername={setUsername}
email={email}
setEmail={setEmail}
usernameErr={usernameErr}
emailErr={emailErr}
setActiveStep={setActiveStep}
/> }
{/* Second Step */}
{ activeStep === 'step two' &&
<StepTwo
mobile={mobile}
address={address}
barangay={barangay}
city={city}
province={province}
postalCode={postalCode}
setMobile={setMobile}
setAddress={setAddress}
setBarangay={setBarangay}
setCity={setCity}
setProvince={setProvince}
setPostalCode={setPostalCode}
setActiveStep={setActiveStep}
/> }
{/* Step Three */}
{ activeStep === 'step three' &&
<StepThree
password={password}
setPassword={setPassword}
confirmPassword={confirmPassword}
setConfirmPassword={setConfirmPassword}
passLimitErr={passLimitErr}
passErr={passErr}
setActiveStep={setActiveStep}
/> }
</form>
</div>
</div>
</div>
</div>
</>
);
};
export default Signup;
The main problem is it always shows that error when I try to signup new users, it will display that error but will be inserted the data anyway. I want to fix this problem and I tried to drop indexes but still not working.
I finally figured it out, the problem is in the frontend part. The form tag sends a data when you're using a button inside it, what I did was created an onClick event in the signup button then call the function onSignup in my StepThree component.
const StepThree = ({ password,setPassword,confirmPassword,setConfirmPassword,passLimitErr,passErr,setActiveStep,onSignup }) => {
return (
<div className="w-full">
<div className="flex flex-col gap-2">
<label htmlFor="password">Password:</label>
<input className="user-auth" type="password" placeholder="Password"
onChange={(e) => setPassword(e.target.value)}
value={password}
required
/>
<span className="pass-error">{ passLimitErr }</span>
</div>
<div className="flex flex-col gap-2">
<label htmlFor="confirm password">Confirm Password:</label>
<input className="user-auth" type="password" placeholder="Confirm password"
onChange={(e) => setConfirmPassword(e.target.value)}
value={confirmPassword}
required
/>
<span className="pass-error">{ passErr }</span>
</div>
<div className="flex items-center gap-2">
<div onClick={() => setActiveStep('step two')} className="flex-col flex gap-2 text-center cursor-pointer">
<span className="bg-gray-900 text-gray-100 p-2 rounded">Previous</span>
</div>
<div className="flex-col flex gap-2">
<button onClick={onSignup} className="signup-btn-user">Signup</button>
</div>
</div>
</div>
)
}
export default StepThree
Trying to pass in an _id to allow my comments to post to my front end React app. When I test express route in postman I don't get the comment . See screen shot below. When I try to post the comment I get the reference error _id id not defined.
Update:
The _id is fixed with id: data._id but I am not able to see my comments when I test in browser:
Here is backend express/node GalleryController.js
const mongoose = require('mongoose');
const Posts = mongoose.model('posts');
exports.PostGalleryInput = async (req, res) => {
console.log(req.body);
await new Posts({
body: req.body,
_id: req.params.id
}).save(async (err, data) => {
if (err) {
console.log('err:', err);
res.status(500).json({
message: 'Something went wrong, please try again later.'
});
} else {
res.status(200).json({
message: 'Post Created',
data,
id: _id
});
}
});
};
Here is Data Schema:
const mongoose = require('mongoose');
const PostSchema = new mongoose.Schema(
{
id: String,
title: String,
body: String,
name: String,
url: String,
comment: String
},
{
collection: 'posts'
}
);
module.exports = mongoose.model('posts', PostSchema);
Here is front end React component
import React, { useState, useEffect } from 'react';
import Container from 'react-bootstrap/Container';
import Card from 'react-bootstrap/Card';
import Button from 'react-bootstrap/Button';
import axios from 'axios';
import './css/sharewall.css';
const Cards = () => {
const [posts, setPosts] = useState([]);
const [comment, setComment] = useState('');
const loadPosts = async () => {
try {
let res = await axios.get(`http://localhost:5000/getall`);
setPosts(res.data.reverse());
} catch (error) {
console.log(error);
}
};
function saveComment(e, _id) {
e.preventDefault();
axios({
method: 'POST',
url: 'http://localhost:5000/postinput',
data: {
_id: _id,
comment: comment
}
})
.then((res) => {
loadPosts();
})
.catch((err) => {
console.log(err);
});
}
const loadComment = async () => {
try {
let res = await axios.post('http://localhost:5000/postinput');
setComment(res.data.comment._id);
console.log(res.data.comment._id);
} catch (error) {
console.log(error);
}
};
useEffect(() => {
loadPosts();
loadComment();
}, []);
return (
<div className="compoentclass">
<Container className="mt-5 ml-auto mr-auto">
<div className="text-center">
{posts.map(
(post) =>
post.url && (
<div key={post._id}>
<Card className="">
<Card.Img alt="" src={post.url} />
<Card.ImgOverlay className="overlay">
<Card.Title className="d-flex justify-content-center align-items-center">
<Card.Text className="cardStyle text-light">{post.body}</Card.Text>
</Card.Title>
</Card.ImgOverlay>
</Card>
<div>
<Card.Text>{post.comment}</Card.Text>
</div>
<textarea
className="comment text-center mt-3 mb-3"
onChange={(e) => setComment(e.target.value)}
name="comment"
type="text"
/>
<div className="d-flex justify-content-start mt-n3 mb-4">
<Button
className="shareButton"
variant="secondary"
onClick={(e) => saveComment(e, post._id)}
>
Comment
</Button>
</div>
</div>
)
)}
</div>
</Container>
</div>
);
};
export default Cards;
I am creating an 'edit profile' page for a dashboard the technologies that I use for the same are Next.js, Node.js & MongoDB.
Note: skip to the backend part if you just wanted to know the issue.
Frontend
Firstly,let me explain the Frontend part.
I am using useRef() inorder to reference data(name,bio) in the inputfields. which are working nicely.
Everything is fine the issue is in the handlesbumit() event_handler.
I am using FormData to send my form data to the backend API
If you're thinking why I'm not using a usual body object to send data the reason is that I have to add the profile picture updation later for which I have to send files , which as far I know we can't do that with an Object and yeah just to inform you it works fine if I would have used that Object part but can't use it with profilepicture updation.
The value that I have consoled out for the references are all good, and the rest of the handler is just as it is written can't find anything odd in that.
import { useUser } from '../../../lib/hooks';
import React, { useState, useEffect, useRef } from 'react';
import Head from 'next/head';
import { ImBook, ImListNumbered } from 'react-icons/im';
import { AiFillGithub, AiOutlineTwitter, AiFillFacebook, AiFillInstagram, AiFillLinkedin } from 'react-icons/ai'
import { FaFacebook, FaStackOverflow } from 'react-icons/fa';
const ProfileSection = () => {
const [user, { mutate }] = useUser();
const [isUpdating, setIsUpdating] = useState(false);
const nameRef = useRef();
const profilePictureRef = useRef();
const bioRef = useRef();
const [msg, setMsg] = useState({ message: '', isError: false });
useEffect(() => {
nameRef.current.value = user.name;
bioRef.current.value = user.Bio;
}, [user]);
const handleSubmit = async (event) => {
event.preventDefault();
if (isUpdating) return;
setIsUpdating(true);
console.log(nameRef.current.value); //Testing
console.log(bioRef.current.value); //Testing
const formData = new FormData();
formData.append('name', nameRef.current.value);
formData.append('Bio', bioRef.current.value);
console.log(formData.get('name'));
const res = await fetch('/api/user', {
method: 'PATCH',
body: formData,
});
if (res.status === 200) {
const userData = await res.json();
mutate({
user: {
...user,
...userData.user,
},
});
setMsg({ message: 'Profile updated' });
} else {
setMsg({ message: await res.text(), isError: true });
}
};
return (
<>
<Head>
<title>Settings</title>
</Head>
<main>
<div class="row">
<div class="col s12 m12">
<div className="card-panel br-10">
{msg.message ? <p style={{ color: msg.isError ? 'red' : '#0070f3', textAlign: 'center' }}>{msg.message}</p> : null}
<form onSubmit={handleSubmit}>
<div className="row">
<div className="col s12 m6 l6">
<label htmlFor="name">
Name
<input
required
id="name"
name="name"
type="text"
ref={nameRef}
/>
</label>
</div>
<div className="col s12 m6 l6">
<label htmlFor="bio">
Bio
<textarea
id="bio"
name="bio"
type="text"
ref={bioRef}
/>
</label>
</div>
</div>
<div className="center-align">
<button disabled={isUpdating} className="btn" type="submit" >Save</button>
</div>
</form>
</div>
</div>
</div>
</main>
</>
);
};
const SettingPage = () => {
const [user] = useUser();
if (!user) {
return (
<>
<p>Please sign in</p>
</>
);
}
return (
<>
<ProfileSection />
</>
);
};
export default SettingPage;
<!-- begin snippet: js hide: false console: true babel: false -->
<!-- language: lang-js -->
import { useUser } from '../../../lib/hooks';
import React, { useState, useEffect, useRef } from 'react';
import Head from 'next/head';
import { ImBook, ImListNumbered } from 'react-icons/im';
import { AiFillGithub, AiOutlineTwitter, AiFillFacebook, AiFillInstagram, AiFillLinkedin } from 'react-icons/ai'
import { FaFacebook, FaStackOverflow } from 'react-icons/fa';
const ProfileSection = () => {
const [user, { mutate }] = useUser();
const [isUpdating, setIsUpdating] = useState(false);
const nameRef = useRef();
const profilePictureRef = useRef();
const bioRef = useRef();
const [msg, setMsg] = useState({ message: '', isError: false });
useEffect(() => {
nameRef.current.value = user.name;
bioRef.current.value = user.Bio;
}, [user]);
const handleSubmit = async (event) => {
event.preventDefault();
if (isUpdating) return;
setIsUpdating(true);
console.log(nameRef.current.value);
console.log(bioRef.current.value);
const formData = new FormData();
formData.append('name', nameRef.current.value);
formData.append('Bio', bioRef.current.value);
console.log(formData.get('name'));
const res = await fetch('/api/user', {
method: 'PATCH',
body: formData,
});
if (res.status === 200) {
const userData = await res.json();
mutate({
user: {
...user,
...userData.user,
},
});
setMsg({ message: 'Profile updated' });
} else {
setMsg({ message: await res.text(), isError: true });
}
};
return (
<>
<Head>
<title>Settings</title>
</Head>
<main>
<div class="row">
<div class="col s12 m12">
<div className="card-panel br-10">
{msg.message ? <p style={{ color: msg.isError ? 'red' : '#0070f3', textAlign: 'center' }}>{msg.message}</p> : null}
<form onSubmit={handleSubmit}>
<div className="row">
<div className="col s12 m6 l6">
<label htmlFor="name">
Name
<input
required
id="name"
name="name"
type="text"
ref={nameRef}
/>
</label>
</div>
<div className="col s12 m6 l6">
<label htmlFor="bio">
Bio
<textarea
id="bio"
name="bio"
type="text"
ref={bioRef}
/>
</label>
</div>
</div>
<div className="center-align">
<button disabled={isUpdating} className="btn" type="submit" >Save</button>
</div>
</form>
</div>
</div>
</div>
</main>
</>
);
};
const SettingPage = () => {
const [user] = useUser();
if (!user) {
return (
<>
<p>Please sign in</p>
</>
);
}
return (
<>
<ProfileSection />
</>
);
};
export default SettingPage;
Backend
Now, the backend API for the same handlesubmit() event_handler i.e. 'api/user'
Please ignore the handler, it's just a predefined middleware npm next-connect which itself checks what type of request is coming if its 'PATCH' it will run handler.patch.
The Issue is the value of name & Bio is undefined,which means its not getting values from req.body;
And to check I also consoled out req.body which give out this
The data is correct but req.body is not a Object its a String now and I get it, its because I'm using formdata so how to get the values of name & Bio from this req.body ?
import nextConnect from 'next-connect';
import middleware from '../../../middlewares/middleware';
import { extractUser } from '../../../lib/api-helpers';
const handler = nextConnect();
handler.use(middleware);
handler.get(async (req, res) => res.json({ user: extractUser(req) }));
handler.patch(async (req, res) => {
if (!req.user) {
req.status(401).end();
return;
}
const { name, Bio } = req.body;
await req.db.collection('users').updateOne(
{ _id: req.user._id },
{
$set: {
name:name,
Bio: Bio,
},
},
);
res.json({ user: { name, Bio } });
});
export default handler;
I have encountered a this issue.
I was resolve it by use 2 form, a form use to get user's info as email, password and the other for send user's picture.
Maybe has best practice for this case.