REACT JS: Crud table delete - reactjs

I have a parent component that sends props to a CRUD Table, i have a problem with deleting the items i want to delete the item with the selected id and i want to send both the email and id in the body of the request. i create the Info just to send it to the CRUD table to be shown because i don't want the entire pendingList to be shown. The id sent to the request when i click on deleteIcon={<IoTrashOutline style={{fontSize: '18px', color: 'red', cursor: 'pointer'}} onClick={() => handleDeleteProduct(info.id)} />} is undefined and i get it cause info is an array the problem is i'm not sure how to get the specific id of the item i want to delete and send it to the request. Any suggestions?
const UserNewDevice = () => {
const [pendingList, setPendingList] = useState([]);
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState('');
const email = localStorage.getItem("email")
useEffect(() => {
const fetchPendingList = async () => {
setIsLoading(true);
try {
const res = await fetch('http://localhost:5000/prods/user_product', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${localStorage.getItem("token")}`
},
body: JSON.stringify(email),
});
if (!res.ok) {
const data = await res.json();
throw new Error(data.msg);
}
const data = await res.json();
setPendingList(data);
} catch (err) {
setError(err.message);
} finally {
setIsLoading(false);
}
};
fetchPendingList();
}, []);
const info = pendingList.map(item => {
return {
id: item._id,
produit: item.produits,
marque: item.marque,
référence: item.référence,
installation: item.annéeInstallation,
entretenu: item.entretenu,
année: item.selectedYearEntretenu,
}
});
const handleDeleteProduct = async (id) => {
try {
const email = localStorage.getItem("email");
console.log('id', id)
const res = await fetch('http://localhost:5000/prods/remove_product', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${localStorage.getItem('token')}`
},
body: JSON.stringify({ id, email })
});
if (!res.ok) {
const data = await res.json();
throw new Error(data.msg);
}
const data = await res.json();
setPendingList(pendingList.filter(item => item.id !== id));
} catch (err) {
console.error(err);
setError(err.message);
}
};
return (
<>
<div className="mt-10 sm:mt-0 w-[92%] ml-12">
<div className="md:grid md:grid-cols-2 md:gap-6">
<div className="mt-5 md:col-span-2 md:mt-0">
<CRUDTable
data={info}
link={`/user-projets-details/`}
pencilIcon={<IoPencil style={{fontSize: '18px', cursor: 'pointer', color: 'green'}}/>}
eyeIcon={<IoEyeOutline style={{fontSize: '18px', cursor: 'pointer', color: '#f8c408'}}/>}
deleteIcon={<IoTrashOutline style={{fontSize: '18px', color: 'red', cursor: 'pointer'}} onClick={() => handleDeleteProduct(info.id)} />}
/>
</div>
</div>
</div>
</>
)
}
export default UserNewDevice

The issue is that info is an array, not an object, and info.id is not a valid reference. To get the specific id of the item you want to delete, you need to pass it as a parameter when calling the handleDeleteProduct function from within the deleteIcon prop. You can modify the code as follows:
deleteIcon={(id) => <IoTrashOutline style={{fontSize: '18px', color: 'red', cursor: 'pointer'}} onClick={() => handleDeleteProduct(id)} />}
And when you call it in the map function:
const info = pendingList.map(item => {
return {
id: item._id,
produit: item.produits,
marque: item.marque,
référence: item.référence,
installation: item.annéeInstallation,
entretenu: item.entretenu,
année: item.selectedYearEntretenu,
}
});
Becomes:
const info = pendingList.map(item => {
return {
id: item._id,
produit: item.produits,
marque: item.marque,
référence: item.référence,
installation: item.annéeInstallation,
entretenu: item.entretenu,
année: item.selectedYearEntretenu,
deleteIcon: item._id,
}
});

Related

Square CreditCard payment in Reactjs

I am trying to integrate react-square-web-payments-sdk , Need to add creditcard holder name field in the paymentform and how can i mention the amount to be deducted from card to do that please check the below code and let me know still i need to add anything more.
I have installed react-square-web-payments-sdk and added creditCard in paymentform and getting the fields like cardnumber, mm/yy, cvv. and zipcode, But i need Cardholder name and no need of zip code, How can i do that and from my below code how can i show how much money to be deducted. Below is my component.
const Payment = () => {
const [errorMessage, setErrorMessage] = useState(null);
const onNonceGenerated = async (error, nonce) => {
if (error) {
setErrorMessage(error.message);
return;
}
// Your code to process the payment
try {
// Call your server to process the payment
const response = await fetch("/process-payment", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ nonce }),
});
console.log(response, "resp");
if (!response.ok) {
throw new Error("Failed to process payment");
}
// Payment was successful
alert("Payment successful!");
} catch (error) {
setErrorMessage(error.message);
}
};
return (
<div className="container">
{errorMessage && <p>{errorMessage}</p>}
<PaymentForm
applicationId="sandbox-Id"
locationId="locationId"
cardTokenizeResponseReceived={async (token, verifiedBuyer) => {
console.log(token,"token");
console.log(verifiedBuyer,"verifiedBuyer");
const response = await fetch("/api/pay", {
method: "POST",
headers: {
"Content-type": "application/json",
},
body: JSON.stringify({
sourceId: token.token,
}),
});
console.log(await response.json());
}}
>
<CreditCard
buttonProps={{
css: {
backgroundColor: "#E56B6F",
fontSize: "14px",
color: "#fff",
"&:hover": {
backgroundColor: "#E56B6F",
},
},
}}
/>
</PaymentForm>
</div>
);
};

Access token giving promise

So I have jwt authentication with access and refresh tokens. But the problem is when the access token expires and i generate a new access token on the frontend (react). It gives me a promise
backend/routes/auth.js (Route to refresh token and where im generating the refresh token and storing it (login route))
router.post("/login", async (req, res) => {
try {
// Validating user
const { error } = loginValidation(req.body);
if (error) return res.status(400).json(error.details[0].message);
// Making sure email is correct
const user = await User.findOne({ email: req.body.email });
if (!user) return res.status(400).json("Invalid email or password");
// Making sure the password is correct
const validPass = await bcrypt.compare(req.body.password, user.password);
if (!validPass) return res.status(400).json("Invalid email or password");
const userPayload = {
_id: user._id,
displayname: user.displayname,
username: user.username,
email: user.email,
bookmarkedTweets: user.bookmarkedTweets,
likedTweets: user.likedTweets,
followers: user.followers,
following: user.following,
date: user.date,
month: user.month,
day: user.day,
year: user.year,
profilePic: user.profilePic,
};
// Generating access token
const accessToken = generateAccessToken(userPayload);
// Generating refresh token
const refreshToken = jwt.sign(
user.toJSON(),
process.env.REFRESH_TOKEN_SECRET
);
// Getting info for refresh token
const newRefreshToken = new RefreshToken({
refreshToken: refreshToken,
});
// Saving refresh token into db
await newRefreshToken.save();
res.json({
_id: user._id,
displayname: user.displayname,
username: user.username,
email: user.email,
password: user.password,
bookmarkedTweets: user.bookmarkedTweets,
likedTweets: user.likedTweets,
followers: user.followers,
following: user.following,
date: user.date,
month: user.month,
day: user.day,
year: user.year,
profilePic: user.profilePic,
accessToken: accessToken,
refreshToken: refreshToken,
});
} catch (error) {
res.sendStatus(500);
console.log(error);
}
});
router.post("/refresh/token", async (req, res) => {
try {
// Getting refresh token
const refreshToken = req.body.token;
// Finding refresh token
const _refreshToken = await RefreshToken.findOne({ refreshToken: refreshToken });
// Making sure there is a refresh token and that refresh token exists in db
if (refreshToken == null) return res.sendStatus(401);
if (!_refreshToken) return res.sendStatus(403);
// Vaifying refresh token
jwt.verify(refreshToken, process.env.REFRESH_TOKEN_SECRET, (err, user) => {
if (err) return res.sendStatus(403);
const userPayload = {
_id: user._id,
displayname: user.displayname,
username: user.username,
email: user.email,
bookmarkedTweets: user.bookmarkedTweets,
likedTweets: user.likedTweets,
followers: user.followers,
following: user.following,
month: user.month,
day: user.day,
year: user.year,
date: user.date,
profilePic: user.profilePic,
};
// Generating access token
const accessToken = generateAccessToken(userPayload);
res.json({ accessToken });
});
} catch (error) {
res.sendStatus(500);
}
});
backend/middlewares/authenticateToken.js
const jwt = require("jsonwebtoken");
require("dotenv").config();
module.exports = function authenticateToken(req, res, next) {
const authHeader = req.headers["authorization"]; // Getting auth header
const token = authHeader && authHeader.split(" ")[1]; // Getting access token from auth header
if (token == null) return res.sendStatus(401);
jwt.verify(token, process.env.ACCESS_TOKEN_SECRET, (err, user) => {
if (err) return res.sendStatus(403); // Making sure the token is valid
req.user = user;
next();
});
};
backend/utils/generateAccessToken.js
const jwt = require("jsonwebtoken");
require("dotenv").config();
module.exports = function (user) {
return jwt.sign(user, process.env.ACCESS_TOKEN_SECRET, { expiresIn: "15s" });
};
frontend/src/api/axios.js
import axios from "axios";
const BASE_URL = "http://localhost:5000";
const accessToken = sessionStorage.getItem("accessToken");
const refreshToken = localStorage.getItem("refreshToken");
export default axios.create({
baseURL: BASE_URL,
headers: { "Content-Type": "application/json" },
});
const client = axios.create({ baseURL: BASE_URL });
export const axiosAuth = async ({ ...options }) => {
// Sending access token with request
client.defaults.headers.common.Authorization = `Bearer ${accessToken}`;
const onSuccess = (response) => response;
const onError = (error) => {
if (error.response.status === 403) {
// Making function to generate new access token
const refresh = async () => {
const { data } = await axios.post(`${BASE_URL}/auth/refresh/token`, {
token: refreshToken,
});
return data;
};
// Generating access token
const newAccessToken = refresh();
sessionStorage.setItem("accessToken", newAccessToken);
return client({ ...options });
}
throw error;
};
return client(options).then(onSuccess).catch(onError);
};
frontend/src/modals/Tweet.modal.js (Where im using axiosAuth)
import React, { useState } from "react";
import AccountCircleOutlinedIcon from "#mui/icons-material/AccountCircleOutlined";
import ArrowBackIcon from "#mui/icons-material/ArrowBack";
import PublicOutlinedIcon from "#mui/icons-material/PublicOutlined";
import CollectionsOutlinedIcon from "#mui/icons-material/CollectionsOutlined";
import GifBoxOutlinedIcon from "#mui/icons-material/GifBoxOutlined";
import BarChartOutlinedIcon from "#mui/icons-material/BarChartOutlined";
import SentimentSatisfiedAltOutlinedIcon from "#mui/icons-material/SentimentSatisfiedAltOutlined";
import ScheduleOutlinedIcon from "#mui/icons-material/ScheduleOutlined";
import LocationOnOutlinedIcon from "#mui/icons-material/LocationOnOutlined";
import Loader from "../components/Loader/Loader.comp";
import { useDispatch, useSelector } from "react-redux";
import {
setTweetModal,
setTweetPending,
tweetErrorClear,
tweetFail,
} from "../features/tweet.slice";
import { axiosAuth } from "../api/axios";
function Tweet() {
const [textfield, setTextfield] = useState("");
const { user } = useSelector((state) => state.user);
const { tweetModalIsOpen, error, isLoading } = useSelector(
(state) => state.tweet
);
const { profilePic } = user;
const dispatch = useDispatch();
if (error !== "") {
setTimeout(() => dispatch(tweetErrorClear()), 3000);
}
const handleOnClick = async () => {
dispatch(setTweetPending(true));
try {
await axiosAuth({
url: "/posts/create",
method: "POST",
body: { textfield },
});
} catch (error) {
dispatch(tweetFail(error.response.data));
}
};
return (
<div
className={
tweetModalIsOpen
? `h-screen w-screen absolute inset-0 bg-white py-2 px-4`
: `hidden`
}
>
<div className="flex-items justify-between">
<div
className="p-1 cursor-pointer rounded-full hover:bg-gray-200 transition-color"
onClick={() => dispatch(setTweetModal(false))}
>
<ArrowBackIcon style={{ fontSize: "1.5rem" }} />
</div>
<button
className="py-1 px-4 transition-color rounded-full bg-blue-500 text-white font-bold hover:bg-blue-600"
onClick={() => handleOnClick()}
>
{isLoading ? <Loader forPage={false} /> : <h1>Tweet</h1>}
</button>
</div>
<div className="flex mt-6 space-x-2">
{profilePic === "" ? (
<AccountCircleOutlinedIcon style={{ fontSize: "2rem" }} />
) : (
<img src={profilePic} alt="profile_pic" />
)}
<div>
<textarea
name="tweet"
id="tweet"
rows="4"
value={textfield}
onChange={(e) => setTextfield(e.target.value)}
className="w-screen outline-none text-xl text-gray-700 placeholder:text-gray-600"
placeholder="What's happening?"
></textarea>
<div className="flex-items space-x-2 text-blue-400 font-bold py-0.5 px-2 rounded-full hover:bg-blue-50 transition-color cursor-pointer w-max">
<PublicOutlinedIcon style={{ fontSize: "1.5rem" }} />
<h1>Everyone can reply</h1>
</div>
<div className="ring-1 ring-gray-100 my-2 w-[75%]" />
<div className="flex-items space-x-2 text-blue-500">
<div className="tweet-icon">
<CollectionsOutlinedIcon style={{ fontSize: "1.5rem" }} />
</div>
<div className="tweet-icon">
<GifBoxOutlinedIcon style={{ fontSize: "1.5rem" }} />
</div>
<div className="tweet-icon">
<BarChartOutlinedIcon style={{ fontSize: "1.5rem" }} />
</div>
<div className="tweet-icon">
<SentimentSatisfiedAltOutlinedIcon
style={{ fontSize: "1.5rem" }}
/>
</div>
<div className="tweet-icon">
<ScheduleOutlinedIcon style={{ fontSize: "1.5rem" }} />
</div>
<div className="tweet-icon">
<LocationOnOutlinedIcon style={{ fontSize: "1.5rem" }} />
</div>
</div>
</div>
</div>
{error && <h1 className="error err-animation">{error}!</h1>}
</div>
);
}
export default Tweet;
Thanks in advance!
I assume that _refreshToken is the promise you refer to. In order to obtain the eventual value of that promise (that is, the refresh token itself), add async to your middleware function and await to the assignment statement:
router.post("/refresh/token", async (req, res) => {
try {
const refreshToken = req.body.token;
const _refreshToken = await RefreshToken.findOne({ refreshToken: refreshToken });
Same in frontend/src/api/axios.js:
const onError = async (error) => {
...
const newAccessToken = await refresh();

Stop react redirecting before API call has finsished

Im writing an application using react and django rest. I am trying to update a post and then redirect back to the home screen, but sometimes the redirect happens before the put request.
As there is a Get request on the home page, that then gets called first and i do not see the updated values unless i refresh the page? Any suggestions?
Here is the page with the put request (updateNote())
import React, { useState, useEffect } from "react";
import { Link } from "react-router-dom";
import { ReactComponent as ArrowLeft } from "../assets/arrow-left.svg";
const NotePage = ({ match, history }) => {
let noteId = match.params.id;
let [note, setNote] = useState(null);
useEffect(() => {
getNote();
}, [noteId]);
let getNote = async () => {
let response = await fetch(`/api/get-note/${noteId}/`);
let data = await response.json();
setNote(data);
};
let updateNote = async () => {
fetch(`/api/get-note/${noteId}/update/`, {
method: "PUT",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(note),
});
};
let deleteNote = async () => {
fetch(`/api/get-note/${noteId}/delete/`, {
method: "DELETE",
headers: {
"Content-Type": "application/json",
},
});
history.push("/");
};
let handleSubmit = () => {
updateNote().then(history.push("/"));
};
let handleChange = (value) => {
setNote((note) => ({ ...note, body: value }));
console.log("Handle Change:", note);
};
return (
<div className="note">
<div className="note-header">
<h3>
<ArrowLeft onClick={handleSubmit} />
</h3>
<button onClick={deleteNote}>Delete</button>
</div>
<textarea
onChange={(e) => {
handleChange(e.target.value);
}}
value={note?.body}
></textarea>
</div>
);
};
export default NotePage;
Then here is the page it redirects to
import React, { useState, useEffect } from "react";
import ListItem from "../components/ListItem";
const NotesListPage = () => {
let [notes, setNotes] = useState([]);
useEffect(() => {
getNotes();
}, []);
let getNotes = async () => {
let response = await fetch("/api/get-notes/");
let data = await response.json();
setNotes(data);
};
return (
<div className="notes">
<div className="notes-header">
<h2 className="notes-title">☶ Notes</h2>
<p className="notes-count">{notes.length}</p>
</div>
<div className="notes-list">
{notes.map((note, index) => (
<ListItem key={index} note={note} />
))}
</div>
</div>
);
};
export default NotesListPage;
I want to make sure that history.push("/") doesnt get executed unitll the fetch request has returned a response
I suggest using the promise method and using '.then' or await just like that :
let updateNote = async () => {
let temp =await fetch(`/api/get-note/${noteId}/update/`, {
method: "PUT",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(note),
});
if(temp)
history.push("/")
};
If you want to navigate after the fetch request has resolved then the code needs to wait for them to settle. Don't forget to catch and/or handle any errors and rejected Promises appropriately.
Example:
const updateNote = async () => {
// return Promise to chain from
return fetch(`/api/get-note/${noteId}/update/`, {
method: "PUT",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(note),
});
};
const deleteNote = async () => {
try {
// wait for Promise to resolve
await fetch(`/api/get-note/${noteId}/delete/`, {
method: "DELETE",
headers: {
"Content-Type": "application/json",
},
});
history.push("/");
} catch(error) {
// log error, etc...
}
};
const handleSubmit = () => {
// pass a callback in .then
updateNote()
.then(() => history.push("/"))
.catch(error => {
// log error, etc...
});
};

Image upload in mern stack using multer not working

I'm trying to upload image in MongoDB using multer and react but I'm unable to post it. I have three inputs in by form i.e title, content and image. If I try to post title and content only it is successfully being posted. I have also added "proxy": "http://localhost:8000", in frontend package.json file
Here is my form
function PostCreate() {
const [title, setTile] = useState("");
const [content, setContent] = useState("");
const [image, setImage] = useState({});
const dispatch = useDispatch();
const post = useSelector((state) => state.postReducer);
const fileOnChange = (e) => {
setImage(e.target.files[0]);
};
const submitPost = (e) => {
e.preventDefault();
const formData = new FormData();
formData.append("image", image);
dispatch(createPost(title, content, image));
};
return (
<div className="postCreate">
<h3 className="postCreate__heading">Create New Post</h3>
<div className="formBody">
<form onSubmit={submitPost}>
<div className="formInputs">
<label className="label">Title</label>
<input
className="titleInput"
type="text"
value={title}
onChange={(e) => setTile(e.target.value)}
placeholder="Enter the Title of the Post"
/>
</div>
<div className="formInputs">
<input type="file" onChange={fileOnChange} />
</div>
<div className="formInputs">
<label className="label">Content</label>
<textarea
className="titleInput"
type="text"
style={{
width: "1500px",
height: "500px",
color: "black",
outline: "none",
border: "none",
borderRadius: "6px",
}}
value={content}
onChange={(e) => setContent(e.target.value)}
/>
</div>
<div className="button">
<Button type="submit" variant="contained" color="primary">
Post
</Button>
</div>
{/* <button type="submit">Post</button> */}
</form>
Here is my action
export const createPost = (title, content, image) => async (dispatch) => {
try {
dispatch({ type: POST_POST_LOADING });
const config = { headers: { "Content-Type": "application/json" } };
const { data } = await axios.post(
"/api/newpost",
{ title, content },
config
);
dispatch({
type: POST_POST_SUCCESS,
payload: data,
});
} catch (error) {
dispatch({
type: POST_POST_FAIL,
payload: error,
});
}
};
Here is my postController
const createPost = async (req, res) => {
const { title, content, writer, comment, image } = req.body;
const fileType = req.file.mimetype.split("/")[1];
const newFileName = req.file.filename + "." + fileType;
fs.rename(
`uploads/images/${req.file.filename}`,
`uploads/images/${newFileName}`,
(req, res) => {
console.log("Renamed");
}
);
// const imageUrl = req.file.filename;
const newPost = await Post.create({
title,
content,
writer,
comment,
image,
// imageUrl,
});
if (newPost) {
res.send("Post created");
console.log("Post created");
} else {
res.status(201).send("Post not created");
console.log("Post not created");
}
};
Here is my routes
router.post("/newpost", upload.single("image"), createPost);
You're creating a form, which is a good start, but not sending it with axios.
To send a file from frontend to backend, you need to construct a form using FormData API and append the file to it. You can also append additional data to it.
Here is how I would change your code to work. In your form file:
const formData = new FormData();
formData.append('image', image);
formData.append('title', title);
formData.append('content', content);
dispatch(createPost(formData));
Then change your action to:
export const createPost = (formData) => async (dispatch) => {
try {
dispatch({ type: POST_POST_LOADING });
const config = { headers: { "Content-Type": "multipart/form-data" } };
const { data } = await axios.post(
"/api/newpost",
formData,
config
);
dispatch({
type: POST_POST_SUCCESS,
payload: data,
});
} catch (error) {
dispatch({
type: POST_POST_FAIL,
payload: error,
});
}
};

Update the likes array in a post in the frontend

I have a PUT route in the backend for liking posts, it adds the users id to the likes array in the post. This works fine when tested on Postman (by providing the post in the body) and the likes array is updated. However, when the icon is clicked in the frontend, I want the likes array to update but I'm not sure how to update the state for the post. result is showing the response in the frontend with a 200 status code but that's as far as I'm getting.
How can I update the likes array in the frontend?
Post.js
const Post = (props) => {
const [post, setPost] = useState({});
const [error, setError] = useState(false);
const id = props.match.params.id;
const loadSinglePost = (id) => {
read(id).then((data) => {
if (error) {
console.log(data.error);
setError(data.error);
} else {
setPost(data);
console.log(data);
}
});
};
useEffect(() => {
loadSinglePost(id);
}, [props]);
const like = (id) => {
const {user: { _id }, token} = isAuthenticated();
fetch(`${API}/like/${_id}`, {
method: "PUT",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${token}`,
},
body: JSON.stringify({
id: id,
}),
})
.then(result => { console.log(result)})
.catch((err) => {
console.log(err);
});
};
return (
<div>
<Navbar />
<div>
<h3>{post && post.title}</h3>
<p>
{post && post.author ? post.author.name : ""}
</p>
<p>{post && post.body}</p>
<h5>{post && post.likes && post.likes.length} likes</h5>
<img
onClick={() => {
like(id);
}}
alt="..."
/>
</div>
</div>
);
};
export default Post;
controllers/post.js
exports.like = (req, res) => {
Post.findByIdAndUpdate(req.body._id, {
$push: {likes: req.profile._id}
}, {new: true}).exec((err, result) => {
if (err) {
return res.status(422).json({error: err})
} else {
return res.json(result)
}
})
}
exports.readById = (req, res) => {
const id = req.params.id
Post.findById(id)
.then(post => res.json(post))
.catch(err => res.status(400).json('Error: ' + err));
}
You can update likes in post in then callback like this:
const like = (id) => {
const {user: { _id }, token} = isAuthenticated();
fetch(`${API}/like/${_id}`, {
method: "PUT",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${token}`,
},
body: JSON.stringify({
id: id,
}),
})
.then(result => {
// here update post likes
let updatedPost = {...post}; //to make copy of post
updatedPost.likes = [...updatedPost.likes, id]; //add new id to updatedPost' likes array
setPost(updatedPost); //update post
console.log(result)
})
.catch((err) => {
console.log(err);
});
};
Also from front-end you're sending id key in body:
body: JSON.stringify({
id: id, // here
})
And at back end you're expecting _id
Post.findByIdAndUpdate(req.body._id, { // here
$push: {likes: req.profile._id}
}

Resources