recently i have been working on my react project. my code is working fine most of the time.
import React, { useState, useEffect } from "react";
import Axios from "axios";
import { Navigate, Link } from "react-router-dom";
import API from "../backend";
import { isAutheticated } from "../auth";
import ReactPaginate from "react-paginate";
import { ToastContainer, toast } from "react-toastify";
import "react-toastify/dist/ReactToastify.css";
import Spinner from "react-bootstrap/Spinner";
const Body = () => {
// set values (got from api)
const [values, setValues] = useState({
bookingId: "",
email: "",
status: "",
error: "",
success: "",
});
// set api response as array, so that i can loop through using map
const [data, setData] = useState([]);
// if some click submit
const [didsubmit, setDidSumbit] = useState(false);
// set and get default booking status
const [myValue, setMyValue] = useState("");
// if something changes
const [didChange, setDidChange] = useState(false);
// for pagination
const [pageChange, setPageChange] = useState(false);
// total pages in pagination
const [allTotalPages, setAllTotalPages] = useState("");
// default page number (used in api get response)
const [num, setNum] = useState(1);
// used for searching
const [details, setDetails] = useState({
email: false,
});
// search state
const [search, setSearch] = useState(false);
// if site is loading
const [loading, setLoading] = useState(true);
// destructuring token from localstorage
const { token } = isAutheticated();
// dataObject for api call
const tokenKey = {
headers: {
authorization: `Bearer ${token}`,
},
};
// api call
const axiosGetCall = async () => {
try {
let res;
if (search === true) {
res = await Axios.get(
`${API}/allorders?page=${num}&useremail=${details.email}`,
tokenKey
);
setSearch(false);
} else {
res = await Axios.get(`${API}/allorders?page=${num}`, tokenKey);
}
setValues({ ...values, success: "", error: "" });
const data = await res.data.bookings;
const totalItems = await res.data.Total;
setData(data);
const totalPages = Math.ceil(totalItems / 5);
setAllTotalPages(totalPages);
setPageChange(false);
setLoading(false);
} catch (error) {
setSearch(false);
setLoading(false);
setValues({ ...values, success: "", error: "" });
}
};
//GET API NORMAL FOR FIRST LOAD
useEffect(() => {
axiosGetCall();
// eslint-disable-next-line
}, []);
//GET API IF DID CHANGE
useEffect(() => {
if (didChange === true) {
// if there is something in details do this
if (details.email === false) {
axiosGetCall();
} else {
setSearch(true);
axiosGetCall();
}
}
// eslint-disable-next-line
}, [didChange]);
//GET API IF DID Search
useEffect(() => {
if (search === true) {
axiosGetCall();
}
// eslint-disable-next-line
}, [search]);
//GET API WHEN PAGE CHANGES
useEffect(() => {
if (pageChange === true) {
setValues({ ...values, success: "", error: "" });
// if there is something in details do this
if (details.email === false) {
axiosGetCall();
} else {
setSearch(true);
axiosGetCall();
}
}
// eslint-disable-next-line
}, [pageChange]);
const onSubmit = (event, id, mail, val) => {
event.preventDefault();
setValues({
...values,
bookingId: id,
email: mail,
status: val,
success: "",
error: "",
});
setMyValue(val);
setDidSumbit(true);
};
const newStatus = {
status: myValue,
id: values.bookingId,
};
// POST API
useEffect(() => {
const axiosUpdateCall = async () => {
try {
const res = await Axios.post(`${API}/changeorder`, newStatus, tokenKey);
setDidSumbit(false);
setDidChange(false);
setValues({ ...values, success: "Changed Successfully", error: "" });
} catch (error) {
setValues({
...values,
error: error.response.data.error[0],
success: "",
});
setDidChange(false);
}
};
if (didChange) {
axiosUpdateCall();
}
}, [didChange]);
// Error Toast
const errorToast = () => {
toast.error(`${values.error}`, {
position: "top-right",
autoClose: 8000,
hideProgressBar: false,
closeOnClick: true,
pauseOnHover: true,
draggable: true,
progress: undefined,
theme: "dark",
});
};
// Sucess Toast
const successToast = () => {
toast.success(`${values.success}`, {
position: "top-right",
autoClose: true,
hideProgressBar: false,
closeOnClick: true,
pauseOnHover: true,
draggable: true,
progress: undefined,
theme: "dark",
});
};
// pagination stuff
const handlePageClick = async (data) => {
let currentPagex = data.selected + 1;
setNum(currentPagex);
setPageChange(true);
setLoading(true);
setValues({ ...values, success: "", error: "" });
};
// search stuff
const onSearch = (event) => {
event.preventDefault();
setSearch(true);
setLoading(true);
setValues({ ...values, success: "", error: "" });
};
// handling changes on pages
const onHandleChange = (val) => (event) => {
setDetails({ ...details, [val]: event.target.value });
setValues({ ...values, success: "", error: "" });
};
return (
<>
<ToastContainer
position="top-right"
autoClose={5000}
hideProgressBar={false}
newestOnTop={false}
closeOnClick={false}
rtl={false}
pauseOnFocusLoss
draggable
pauseOnHover
theme="dark"
/>
{values.error && errorToast()}
{values.success && successToast()}
<div style={{ display: didsubmit ? "none" : "block" }}>
<section className="section dashboard">
<div className="row justify-content-md-center">
<div className="col-lg-6">
<div className="input-group">
<input
type="search"
className="form-control rounded"
placeholder="example#gmail.com"
aria-label="Search"
aria-describedby="search-addon"
onChange={onHandleChange("email")}
/>
<button
type="button"
className="btn btn-outline-primary"
onClick={onSearch}
disabled={search}
>
{search === true ? "Loading.." : "Search"}
</button>
<a href="/main" className="btn btn-outline-primary">
Reload
</a>
</div>
</div>
</div>
</section>
</div>
<main id="main" className="main">
<section className="section dashboard ">
<div className="row justify-content-md-center">
<div className={didsubmit ? "col-lg-6" : "col-lg-12"}>
<div className="row">
{/* */}
<div className="col-12">
<div className="card recent-sales overflow-auto">
<div className="card-body">
<h3 className="card-title">
{didsubmit === true
? "Change Booking Status"
: "Recent Bookings "}
<span></span>
</h3>
{loading === true ? (
<>
{" "}
<Spinner animation="border" role="status">
<span className="visually-hidden">Loading...</span>
</Spinner>
</>
) : (
<>
<table className="table table-borderless datatable">
{didsubmit === true ? (
<></>
) : (
<thead>
<tr>
<th scope="col">Name</th>
<th scope="col">User Email</th>
<th scope="col">Customer Phone</th>
<th scope="col">Payment Recept</th>
<th scope="col">Order id</th>
<th scope="col">Package Name</th>
<th scope="col">Total Kids</th>
<th scope="col">Total Adults</th>
<th scope="col">Total Price</th>
<th scope="col">BookedFor</th>
<th scope="col">created On</th>
<th scope="col">Status</th>
</tr>
</thead>
)}
{/* */}
<tbody>
{didsubmit === true ? (
<>
<tr>
<td>
{" "}
<p>Email: {values.email}</p>
<p>Current Status: {values.status}</p>
<div className="col-3">
<select
onChange={(e) =>
setMyValue(e.target.value)
}
defaultValue={values.status}
>
<option>Pending</option>
<option>Unpaid</option>
<option>Bank</option>
<option>Bank-Paid</option>
<option>Success</option>
</select>
</div>
<div className="float-end">
<button
className="btnx btn-primary"
disabled={didChange}
onClick={() => {
setDidSumbit(false);
}}
>
Back
</button>
<button
className="btnx btn-primary"
disabled={didChange}
onClick={() => {
setDidChange(true);
}}
>
{didChange
? "Loading.."
: "Change Status"}
</button>
</div>
</td>
</tr>
</>
) : (
<>
{data.map((e, i) => (
<tr key={e._id}>
<td scope="row">
{e.tourDetails[0].username}
</td>
<td scope="row">
<p>{e.tourDetails[0].email}</p>
</td>
<td scope="row">
<p>{e.tourDetails[0].userphone}</p>
</td>
<td scope="row">
<p>{e.paymentid}</p>
</td>
<td scope="row">
<p>{e.orderId}</p>
</td>
<td>{e.tourDetails[0].name}</td>
<td>{e.Childquantity}</td>
<td>{e.Adultquantity}</td>
<td>₹{e.overAllTotal}</td>
<td>
<span className="text-primary">
{e.bookedFor}
</span>
</td>
<td>{e.createdAt.slice(0, 10)}</td>
<td>
{e.orderStatus === "Success" && (
<span className="badge bg-success">
{e.orderStatus}
</span>
)}
{e.orderStatus === "Bank" && (
<span className="badge bg-dark">
{e.orderStatus}
</span>
)}
{e.orderStatus === "Bank-Paid" && (
<span className="badge bg-secondary">
{e.orderStatus}
</span>
)}
{e.orderStatus === "Unpaid" && (
<span className="badge bg-danger">
{e.orderStatus}
</span>
)}
{e.orderStatus === "Pending" && (
<span className="badge bg-warning">
{e.orderStatus}
</span>
)}
</td>
<td>
<button
className="btnx btn-primary"
disabled={didsubmit}
onClick={(event) => {
onSubmit(
event,
e._id,
e.tourDetails[0].email,
e.orderStatus
);
}}
>
{didsubmit
? "loading.."
: "Change Status"}
</button>
</td>
</tr>
))}
</>
)}
</tbody>
{/* */}
</table>
</>
)}
</div>
</div>
</div>
</div>
</div>
{/* */}
</div>
</section>
</main>
{/* */}
<div style={{ display: didsubmit ? "none" : "block" }}>
<ReactPaginate
previousLabel={"previous"}
nextLabel={"next"}
breakLabel={"..."}
pageCount={allTotalPages}
marginPagesDisplayed={2}
pageRangeDisplayed={3}
onPageChange={handlePageClick}
containerClassName={"pagination justify-content-center"}
pageClassName={"page-item"}
pageLinkClassName={"page-link"}
previousClassName={"page-item"}
previousLinkClassName={"page-link"}
nextClassName={"page-item"}
nextLinkClassName={"page-link"}
breakClassName={"page-item"}
breakLinkClassName={"page-link"}
activeClassName={"active"}
/>
</div>
</>
);
};
export default Body;
this is the whole code, here most of the time my get response works fine. sometimes it doesn't hit the route.
sometimes Get request doesn't hit after post request
POST "http://127.0.0.1:4000/api/changeorder".
GET "http://127.0.0.1:4000/api/allorders?page=1".
I guess my Get request is loading before post request so the virtual dom isn't updating.
Related
I have a table that can add and delete rows. However, am not able to validate the table. if the table cells are empty or filled am not able to post the values to the backend. The error message still shows.What is the best way to validate the table. I would like to have the error message show if the fields are empty on submit.
payment.js
import React, {Component } from "react";
import "bootstrap/dist/css/bootstrap.min.css"
import axios from "axios"
import SimpleReactValidator from "simple-react-validator"
import Box from '#mui/material/Box';
import Button from "#mui/material/Button";
import BillTable from "./billTable"
import $ from 'jquery'
class Payment extends Component {
constructor(){
super()
this.state = {
Bill:"",
billTable: [{
index: Math.random(),
serialNumber: "",
item:"",
cost:""}],
errorMessage: '',
}
this.validator = new SimpleReactValidator({autoForceUpdate: this,messages: {
default: 'Please enter this field!'
},element: message => <div style={{color: "red"}}>{message}</div>})
this.handleSubmit = this.handleSubmit.bind(this)
this.handleChange = this.handleChange.bind(this)
}
handleChange = (e) => {
if (["serialNumber", "item", "cost"].includes(e.target.name)) {
let billTable = [...this.state.billTable]
billTable[e.target.dataset.id][e.target.name] = e.target.value;
}
else {
this.setState({ [e.target.name]: e.target.value })
}
};
addNewRowPlan = () => {
this.setState((prevState) => ({
billTable: [...prevState.billTable, { index: Math.random(), serialNumber: "", item: "", cost:""}],
}));
}
deleteRow = (index) => {
this.setState({
billTable: this.state.billTable.filter((s, sindex) => index !== sindex),
});
}
clickOnDelete(record) {
this.setState({
billTable: this.state.billTable.filter(r => r !== record),
});
}
handleSubmit(event){
event.preventDefault()
if(this.validator.allValid()){
this.validator.hideMessages()
const billed = {
Bill:this.state.billTable
};
axios.post(`http://localhost:4000/bill`, billed,{
})
.then(response => {
console.log(response.data)
return $('.alert-success').show();
})
this.setState({
Bill:"",
})
}
else{
this.validator.showMessages()
this.forceUpdate()
return $('.alert-danger').show();
}
}
render() {
let {billTable} = this.state
return (
<div>
<div className="container">
<div className="form-div">
<h1>Billing</h1>
<Box component="form" onSubmit={this.handleSubmit} noValidate sx={{ mt: 1}} onChange={this.handleChange}>
<div className="row" style={{ marginTop: 20 }}>
<div className="col-sm-3"></div>
<div className="col-sm-12">
<div className="card">
<div className="card-header text-center">Bill</div>
<div className="card-body">
<div className="row">
<div className="col-sm-4">
</div>
</div>
<table>
<thead>
<tr>
<th>Serial #</th>
<th>Item</th>
<th>Cost</th>
</tr>
</thead>
<tbody>
<BillTable id="bill" add={this.addNewRowPlan.bind(this)} delete={this.clickOnDelete.bind(this)} billTable={billTable} />
{this.validator.message('bill',this.state.Bill,'required')}
</tbody>
</table>
</div>
</div>
</div>
<div className="col-sm-1"></div>
</div>
<Button
type="submit"
fullWidth
sx={{ mt: 3, mb: 2}}
>
<span>Submit</span>
</Button>
</Box>
</div>
</div>
</div>
);
}
}
export default Payment;
billTable.js
import React from "react"
const billTable = (props) => {
return (props.billTable.map((val, idx) => {
let serialNumber = `serialNumber-$${idx}`, item = `item-$${idx}`, cost = `cost-$${idx}`
return (
<tr key={val.index}>
<td>
<input type="text" name="serialNumber" data-id={idx} id={serialNumber} className="form-control" />
</td>
<td>
<input type="text" name="item" data-id={idx} id={item} className="form-control " />
</td>
<td>
<input type="text" name="cost" data-id={idx} id={cost} className="form-control"/>
</td>
<td>
{
idx===0?<button onClick={()=>props.add(idx)} type="button" className="btn btn-primary">Add Row</button>
: <button className="btn btn-danger" onClick={(() => props.delete(val))} >Delete Row</button>
}
</td>
</tr>
)
})
)
}
export default billTable
`````````````````````
How can i correct this code. Thank you in advance
I'm trying to build a CURD operation component that GET the data from API. When a user clicks on the EDIT, it renders the component showing the details of the line item to be edited. But when the user hits the back button in the browser, the previous component with the post list re-renders and it loses the previous state and scroll position. Is there a way that I can bring the selected row to the first position if I scroll up it displays the previous data and if I scroll down it shows the next coming data?
import React, { useState, useEffect } from "react";
import axios from "axios";
import { Link } from "react-router-dom";
import "bootstrap/dist/css/bootstrap.min.css";
import { CSVLink, CSVDownload } from "react-csv";
import "./Home.css";
const Home = () => {
const [loading, setLoading] = useState(false);
const [users, setUsers] = useState([]);
const [searchTitle, setSearchTitle] = useState("");
const [prime, setPrime] = useState([]);
const [primeid, setPrimeid] = useState("");
const [category, setCategory] = useState([]);
const [categoryid, setCategoryid] = useState("");
const [subcategory, setSubcategory] = useState([]);
const [subcategoryid, setSubcategoryid] = useState("");
const usersList = users.length;
useEffect(() => {
loadUsers();
}, []);
// the below command is used to send the load data from the database
const loadUsers = async () => {
const result = await axios.get(`http://localhost:4000/api/email/`);
// setUsers(result.data);
setUsers(result.data);
// console.log(result)
};
useEffect(() => {
const getprime = async () => {
const resprime = await axios.get(
`http://localhost:4000/api/primewise${primeid}`
);
setPrime(resprime.data.reverse());
};
getprime();
}, []);
const handleprime = (event) => {
const getprimeid = event.target.value;
setPrimeid(getprimeid);
};
useEffect(() => {
const getcategory = async () => {
const rescategory = await axios.get(
"http://localhost:4000/api/categorywise"
);
setCategory(rescategory.data.reverse());
};
getcategory();
}, []);
const handlecategory = (event) => {
const getcategoryid = event.target.value;
setCategoryid(getcategoryid);
};
useEffect(() => {
const getsubcategory = async () => {
const ressubcategory = await axios.get(
"http://localhost:4000/api/subcategorywise"
);
setSubcategory(ressubcategory.data.reverse());
};
getsubcategory();
}, []);
const handleSubcategory = (event) => {
const getSubcategoryid = event.target.value;
setSubcategoryid(getSubcategoryid);
};
useEffect(() => {
if (users.length) {
const scrollPosition = sessionStorage.getItem("scrollPosition");
if (scrollPosition) {
window.scrollTo(0, parseInt(scrollPosition, 10));
sessionStorage.removeItem("scrollPosition");
}
}
}, [users]);
useEffect(() => {
if (users.length) {
const scrollPosition = sessionStorage.getItem("scrollPosition");
if (scrollPosition) {
window.scrollTo(0, parseInt(scrollPosition, 10));
sessionStorage.removeItem("scrollPosition");
}
}
}, [users]);
return (
<div className="App">
<div className="header-1">
<nav className="navbar">
<div className="container-fluid">
<div className="navbar-brand">Supplier Contact Information</div>
{/* <form className="d-flex">
<Link className="btn btn-outline-light" to="/users/statics">
Statics
</Link>
</form> */}
</div>
</nav>
<hr class="solid"></hr>
</div>
<div className="header">
<h4>CATEGORYWISE SUPPLIER INFORMATION</h4>
<div className="col-md-0">
<div class="row">
<div class="col-sm-3">
<div class="card">
<div class="card-body text-center ">
<input
type="text"
placeholder="Power Search..."
className="form-control"
onChange={(e) => setSearchTitle(e.target.value)}
/>
<br />
<select
name="prime"
className="form-control"
onChange={(e) => handleprime(e)}
>
<option value="">--Select Prime--</option>
{prime.map((resprime, index) => (
<option key={index} value={resprime.Prime_Descr}>
{resprime.Prime_Descr}{" "}
</option>
))}
</select>{" "}
</div>
</div>
</div>
<div class="col-sm-3">
<div class="card">
<div class="card-body">
<select
name="category"
className="form-control"
onChange={(e) => handlecategory(e)}
>
<option value="">--Select Category--</option>
{category.map((rescategory, index) => (
<option key={index} value={rescategory.CATEGORY}>
{rescategory.CATEGORY}{" "}
</option>
))}
</select>{" "}
<br />
<select
name="country"
className="form-control"
onChange={(e) => handlecategory(e)}
>
<option value="">--Select Category--</option>
{subcategory.map((ressubcategory, index) => (
<option key={index} value={ressubcategory.SUB_CATEGORY}>
{ressubcategory.SUB_CATEGORY}{" "}
</option>
))}
</select>{" "}
</div>
</div>
</div>
<div class="col-sm-6">
<div class="card-1">
<div class="card-body text-center ">
<CSVLink
filename="supplier contact info."
data={users}
class="btn btn-outline-light"
>
Export to csv
</CSVLink>
<br />
<br />
<div className="container-fluid">
<Link className="btn btn-outline-light" to="/users/add">
Add Supplier
</Link>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div className="outer-wrapper">
<div className="tabel-wrapper">
<table>
<thead>
<th scope="col">S.No</th>
<th scope="col">Prime</th>
<th scope="col">Category</th>
<th scope="col">Sub-Category</th>
<th scope="col">Supplier</th>
<th scope="col">Country</th>
<th scope="col">Region</th>
<th scope="col">Title/Designation</th>
<th scope="col">Representative</th>
<th scope="col">Department</th>
<th scope="col">Primary Contact No</th>
<th scope="col">Secondary Contact No</th>
<th scope="col">EmailId</th>
<th scope="col">Address</th>
<th>Action</th>
</thead>
<tbody>
{/* {usersList ? (
<h4>Loading ...</h4> */}
{loading ? (
<h4>Loading ...</h4>
) : (
users
.filter((value) => {
if (searchTitle === "") {
return value;
} else if (
// (value.PRIME &&
// value.PRIME.toLowerCase().includes(
// searchTitle.toLowerCase()
// )) ||
(value.CATEGORY &&
value.CATEGORY.toLowerCase().includes(
searchTitle.toLowerCase()
)) ||
(value.SUB_CATEGORY &&
value.SUB_CATEGORY.toLowerCase().includes(
searchTitle.toLowerCase()
)) ||
(value.SUPPLIER &&
value.SUPPLIER.toLowerCase().includes(
searchTitle.toLowerCase()
)) ||
(value.COUNTRY &&
value.COUNTRY.toLowerCase().includes(
searchTitle.toLowerCase()
)) ||
(value.REGION &&
value.REGION.toLowerCase().includes(
searchTitle.toLowerCase()
)) ||
(value.DESIGNATION &&
value.DESIGNATION.toLowerCase().includes(
searchTitle.toLowerCase()
)) ||
(value.REPRESENTATIVE_NAME &&
value.REPRESENTATIVE_NAME.toLowerCase().includes(
searchTitle.toLowerCase()
)) ||
(value.DIVISION &&
value.DIVISION.toLowerCase().includes(
searchTitle.toLowerCase()
)) ||
(value.CONTACT_NO &&
value.CONTACT_NO.toLowerCase().includes(
searchTitle.toLowerCase()
)) ||
(value.ALTERNATE_CONTACT_NUMBER &&
value.ALTERNATE_CONTACT_NUMBER.toLowerCase().includes(
searchTitle.toLowerCase()
)) ||
(value.EMAIL_ID &&
value.EMAIL_ID.toLowerCase().includes(
searchTitle.toLowerCase()
)) ||
(value.ADDRESS &&
value.ADDRESS.toLowerCase().includes(
searchTitle.toLowerCase()
))
) {
return value;
}
})
.map((user, index) => (
<tr>
<td scope="row">{index + 1}</td>
<td>{user.PRIME}</td>
<td>{user.CATEGORY}</td>
<td>{user.SUB_CATEGORY}</td>
<td>{user.SUPPLIER}</td>
<td>{user.COUNTRY}</td>
<td>{user.REGION}</td>
<td>{user.DESIGNATION}</td>
<td>{user.REPRESENTATIVE_NAME}</td>
<td>{user.DIVISION}</td>
<td>{user.CONTACT_NO}</td>
<td>{user.ALTERNATE_CONTACT_NUMBER}</td>
<td>{user.EMAIL_ID}</td>
<td>{user.ADDRESS}</td>
<td>
<Link
class="btn btn-outline-primary mr-2"
to={`/users/edit/${user.ID}`}
>
Edit
</Link>
</td>
</tr>
))
)}
</tbody>
</table>
</div>
</div>
</div>
);
};
export default Home;
I have a table full of text inputs. All the inputs are stored in a React state array (newUserPledges). If I have values in all the text inputs and add a new row, the new row is duplicating the previous row input values. I can't see why it would be doing that.
Here is my component with all the irrelevant code dotted out.
import React, { useEffect, useState } from 'react';
...
const initialNewPledgeState = {
campaign: '',
date: dateFormatted(new Date()),
pledges: 0,
howPaid: '',
confirmation: '',
sponsor: '',
notes: ''
};
const buttonRef = React.createRef();
const AdminControlPanel = () => {
...
const [addPledgeOptions, setAddPledgeOptions] = useState([]);
const [selectedDeleteUser, setSelectedDeleteUser] = useState('');
const [selectedEditUser, setSelectedEditUser] = useState('');
const [selectedPledgeUser, setSelectedPledgeUser] = useState('');
const [selectedAddPledgeUser, setSelectedAddPledgeUser] =
useState('');
const [allUsers, setAllUsers] = useState([]);
const [usersWithPledges, setUsersWithPledges] = useState([]);
const [usersWithPledgesCount, setUsersWithPledgesCount] =
useState(0);
const [userPledges, setUserPledges] = useState([]);
const [newUserPledges, setNewUserPledges] = useState([
initialNewPledgeState
]);
const alert = useAlert();
const setUpUsersArrays = () => {
getUsers('user').then((r) => {
setAllUsers(r.data.users);
const firstAddPledgeOption = [
{ value: '', label: 'Select a user to add pledges for...' }
];
const usersOptions = r.data?.users?.map((u) => {
return {
value: u.email,
label: `${u.email} - ${u.last_name}, ${u.first_name}`
};
});
if (usersOptions?.length) {
setAddPledgeOptions(
firstAddPledgeOption.concat(usersOptions)
);
} else {
setOptions([]);
setEditOptions([]);
}
});
getUsersWithPledges().then((r) => {
const firstPledgeOption = [
{ value: '', label: 'Select a user to view pledges for...' }
];
const usersOptions = r.data?.users?.map((u) => {
return {
value: u.email,
label: `${u.email} - ${u.last_name}, ${u.first_name}`
};
});
if (usersOptions) {
setUsersWithPledgesCount(usersOptions.length);
setUsersWithPledges(firstPledgeOption.concat(usersOptions));
}
});
};
useEffect(() => {
getDatabaseStats().then((r) => {
setDbStats(r.data.stats);
});
setUpUsersArrays();
}, []);
useEffect(() => {
console.log(selectedDeleteUser.value);
}, [selectedDeleteUser]);
...
const handlePledgeChange = (e, i) => {
// console.log(e.target);
const value = e.target.value;
const pledge = newUserPledges[i];
console.log({ pledge });
pledge[e.target.name] = value;
const tempPledges = [...newUserPledges];
tempPledges[i][e.target.name] = value;
// tempPledges.splice(i, 1, pledge);
setNewUserPledges(tempPledges);
// const value = e.target.value;
// setInputState({
// ...inputState,
// [e.target.name]: value
// });
};
const addNewPledgeRow = (e) => {
e.preventDefault();
const tempPledges = [...newUserPledges].concat(
initialNewPledgeState
);
console.log({ tempPledges });
setNewUserPledges(tempPledges);
};
const deletePledgeRow = (e) => {
e.preventDefault();
const tempPledges = [...newUserPledges];
tempPledges.pop();
setNewUserPledges(tempPledges);
};
const customStyles = {
menuList: (base) => ({
...base,
// kill the white space on first and last option
padding: 0,
minHeight: '300px'
})
};
return (
<div className={'container admin'}>
<h2>Admin Control Panel</h2>
...
<div
className={
'col-12 col-md-9 mx-auto my-4 p-1 border border-secondary bg-light accordion'
}
id={'admin-accordion-add-pledges'}
>
<div className={'accordion-item'}>
<h2 className={'accordion-header'} id={'headingAddPledges'}>
<button
className={'accordion-button collapsed'}
type={'button'}
data-bs-toggle={'collapse'}
data-bs-target={'#collapseAddPledges'}
aria-expanded={'true'}
aria-controls={'collapseAddPledges'}
>
<h4 className={'text-center'}>Add Pledges For User</h4>
</button>
</h2>
<div
id={'collapseAddPledges'}
className={'accordion-collapse collapse mt-1 px-2'}
aria-labelledby={'headingAddPledges'}
data-bs-parent={'#admin-accordion-add-pledges'}
>
{!!addPledgeOptions.length ? (
<Select
className={'mb-2'}
value={selectedAddPledgeUser}
onChange={(e) => setSelectedAddPledgeUser(e)}
options={addPledgeOptions}
styles={customStyles}
/>
) : (
<h4 className={'text-center'}>
No users to add pledges for
</h4>
)}
{selectedAddPledgeUser?.value?.length > 0 && (
<form>
<div className={'my-2 user-pledges'}>
<table className={'table table-bordered'}>
<thead>
<tr>
<th width={100}>Campaign</th>
<th width={120}>Date</th>
<th width={80}>Pledges</th>
<th>How Paid</th>
<th>Confirmation</th>
<th>Sponsor</th>
<th>Notes</th>
</tr>
</thead>
<tbody>
{!!newUserPledges.length &&
newUserPledges.map((p, i) => (
<tr key={i}>
<td>
<input
type={'text'}
className={'form-control'}
value={p.campaign}
name={'campaign'}
id={`campaign-${i}`}
onChange={(e) =>
handlePledgeChange(e, i)
}
/>
</td>
<td>
<input
type={'date'}
max={dateFormatted(new Date())}
className={'form-control'}
value={p.date}
name={'date'}
id={`date-${i}`}
onChange={(e) =>
handlePledgeChange(e, i)
}
/>
</td>
<td>
<input
type={'text'}
className={'form-control'}
value={p.pledges}
name={'pledges'}
onChange={(e) =>
handlePledgeChange(e, i)
}
/>
</td>
<td>
<input
type={'text'}
className={'form-control'}
value={p.howPaid}
name={'howPaid'}
onChange={(e) =>
handlePledgeChange(e, i)
}
/>
</td>
<td>
<input
type={'text'}
className={'form-control'}
value={p.confirmation}
name={'confirmation'}
onChange={(e) =>
handlePledgeChange(e, i)
}
/>
</td>
<td>
<input
type={'text'}
className={'form-control'}
value={p.sponsor}
name={'sponsor'}
onChange={(e) =>
handlePledgeChange(e, i)
}
/>
</td>
<td>
<input
type={'text'}
className={'form-control'}
value={p.notes}
name={'notes'}
onChange={(e) =>
handlePledgeChange(e, i)
}
/>
</td>
</tr>
))}
</tbody>
</table>
<div className={'d-flex justify-content-evenly'}>
<button
className={'btn btn-primary'}
onClick={addNewPledgeRow}
disabled={newUserPledges.length > 9}
>
<FontAwesomeIcon icon={faPlusSquare} />
Add New Row
</button>
<button
className={'btn btn-primary'}
onClick={deletePledgeRow}
disabled={newUserPledges.length === 1}
>
<FontAwesomeIcon icon={faMinusSquare} />
Delete Row
</button>
<button className={'btn btn-success'}>
Submit Pledges
</button>
<button className={'btn btn-danger'}>
Reset Table
</button>
</div>
</div>
</form>
)}
</div>
</div>
</div>
<br />
</div>
);
};
export default AdminControlPanel;
Here is an example of what is happening. I fill in values in the first row, and then hit add row and it adds a second row with duplicated values.
Asked my question in the Reactiflux Discord and got an answer right away. I was mutating my rows state in the handlePledgeChange function.
I have changed that function to this and it works great now.
const handlePledgeChange = (e, i) => {
const value = e.target.value;
const tempPledges = newUserPledges.map((item, idx) => {
if (i === idx) return { ...item, [e.target.name]: value };
return item;
});
setNewUserPledges(tempPledges);
};
I'm trying to confirm a user delete using Sweetalert2 in react, but when I use the 'Cancel' button, it still sends the form to delete the user, as if I've pressed 'Confirm'. Here's my code
export default function Users() {
const [users, setUsers] = useState([])
const [isModalOpen, setModal] = useState(false)
const [hover, setHover] = useState('')
const [userModal, setuserModal] = useState('')
const [show, setShow] = useState(false)
const [userDelete, setUserDelete] = useState('')
useEffect(() => {
fetch(`${OC_API}/users`)
.then(res => res.json())
.then(res => {
setUsers(res)
console.log(res)
})
.catch(e => console.log(`Hubo un error Cod. 200 ${e}`))
},[])
function toggleModal(user) {
setuserModal(user)
setModal(!isModalOpen)
}
function handleDeleteUser(user) {
fetch(`${OC_API}/userdelete`, {
method: 'POST',
headers: {'Content-Type':'application/json'},
body: JSON.stringify({"user_id": user._id})
}).then(res => {
if (res.ok){
alert('¡Usuario eliminado!')
} else {
alert('¡Hubo un error! Cod. 201')
}
}).then(() => {
fetch(`${OC_API}/users`)
.then(res => res.json(),
error => {alert(`Error de conexión ${error.message}`); throw error;})
.then(data => {
console.log(data)
setUsers(data)
})
.catch(e => console.log(`Hubo un error Cod. 202 ${e}`))
})
}
if (users == undefined || users == [] || users == null ){
return (
<Loading />
)
} else {
return(
<div className="container">
<motion.div className="row justify-content-center"
initial={{ opacity: 0 }}
animate={{ opacity: 1}}
transition={{ duration: 1}}
style={{'paddingTop':'3em'}}
>
<div className="col col-md-12 text-right" >
<AddUserButton />
</div>
<div className="col col-md-12 text-center" >
<ParagraphStyle>
<div className="card" >
<div className="card-header text-left">
Administrar Usuarios
</div>
<div className="card-body">
<div className="table-responsive">
<AdminTableStyle>
<table className="table">
<thead >
<tr>
<th scope="col">Nombre</th>
<th scope="col">RUT</th>
<th scope="col">Correo</th>
<th scope="col">Rol</th>
<th scope="col">Acciones</th>
</tr>
</thead>
<tbody>
{users.map(user => (
<Fragment key={user.id}>
<SweetAlert
show={show}
icon={'warning'}
title="Eliminar Usuario"
text={`¿Desea borrar a usuario ${userDelete.name}?`}
showCancelButton={true}
confirmButtonColor={'#3085d6'}
cancelButtonColor={'#d33'}
cancelButtonText={'Cancelar'}
confirmButtonText={'Confirmar'}
onCancel={ () => {setShow(false)
return }}
onConfirm={() => {
handleDeleteUser(userDelete)
setShow(false)
}}
/>
<tr onMouseOver={() => setHover(user._id)} onMouseLeave={() => setHover('')}>
<td style={{'textAlign': 'left'}}>{user.name}</td>
<td>{user.rut}</td>
<td>{user.email}</td>
<td>{user.role}</td>
<td>
<span style={{'marginRight': '5px'}}>
{
hover==user._id?
<a href="#" onClick={() => toggleModal(user)}>
<FontAwesomeIcon
icon='edit'
/>
</a>
:
''
}
</span>
<span>
{
hover==user._id?
<a href="#" onClick={() => {
setUserDelete(user)
setShow(true)
}}
>
<FontAwesomeIcon
icon='trash'
/>
</a>
:
''
}
</span>
</td>
</tr>
</Fragment>
))}
</tbody>
</table>
</AdminTableStyle>
</div>
</div>
</div>
</ParagraphStyle>
</div>
</motion.div>
<UserEdit
setuserModal={setuserModal}
isModalOpen={isModalOpen}
userModal={userModal}
toggleModal={toggleModal}
/>
</div>
)
}
}
I tried adding an onCancel option in the Sweetalert component and set the Show state to false, but it continues executing the handleDeleteUser function
Any suggestions?
When I hit the Edit button I trigger a flag for enabling editing. The desired behaviour would be to enable editing only on one item at the time. However, all the item get enabled for editing by the flag.
<li key={recipe._id} className="list__item recipe" id={randomString()}>
<Card body id={`card-${recipe._id}`} >
<CardTitle name={randomString()}><input type="text" name="name" defaultValue={recipe.name} readOnly={disabled} className="recipe__name" onChange={(e) => {changeItem(e, recipe)}} /></CardTitle>
<CardImg top width="100%" src={require('../assets/318x180.svg')} alt="Card image cap" />
<input id={String(recipe._id)} className="toggle" type="checkbox" onChange={(e) => {handleChange(e)}} />
<label htmlFor={String(recipe._id)} className="lbl-toggle bold">Ingredients</label>
<ul key={recipe._id} className="ingredients expand-content" id='ingredientList'>
{iselect()}
</ul>
<table>
<tbody>
<tr>
<td className={'center'}><span className={'bold'}>Actual Cost:</span></td>
<td className={'center'}><span className={'bold'}>Proposed Price:</span></td>
</tr>
<tr>
<td className={'center'}><span className="recipe__cost">£{cost(Math.floor(Math.random()*10))}</span></td>
<td className={'center'}><span className="recipe__proposed">£{proposed(cost(Math.floor(Math.random()*10)))}</span></td>
</tr>
</tbody>
</table>
<Button color="primary" onClick={() => { saveEditRecipe(recipe); }} className={`bold save-button ${disabled ? "hidden" : "show"}`}>Save</Button>
<Button color="primary" onClick={(e) => { editRecipe(e, recipe); }} id={`card-${recipe._id}`} className={'edit-button'}>Edit</Button>
<Button color="danger" onClick={() => { deleteRecipe(recipe); }} className={'delete'}>X</Button>
</Card>
</li>
// /client/src/components/Recipe.js
import React, { useState, useEffect} from "react";
import { Card, Button, CardTitle, CardText, CardImg } from 'reactstrap';
import { FaPen} from "react-icons/fa";
import './Recipe.css';
// SERVICES
import recipeService from '../services/recipeService';
import ingredientService from '../services/ingredientService';
function Recipe() {
const initialEditState = { id: null, name: '', ingredients: null }
const [recipes, setrecipes] = useState(null);
const [newRecipe, setNewRecipe] = useState({});
const [ingredients, setingredients] = useState(null);
const [fields, setFields] = useState([{name:"", quantity: null}]);
const [editFields, setEditFields] = useState([{ name: "", quantity: null }]);
const [disabled, setDisabled] = useState(true);
const [currentRecipe, setCurrentRecipe] = useState(initialEditState)
// const inputRef = useRef();
// function handleGamClick() {
// setDisabled(!disabled);
// }
useEffect(() => {
if(!recipes) {
getRecipes();
}
if(!ingredients) {
getIngredients();
}
if(currentRecipe._id !== undefined) {
setDisabled(false);
}
// console.log(disabled);
}, [recipes, ingredients, currentRecipe])
const getRecipes = async () => {
let res = await recipeService.getAll();
setrecipes(res);
}
const getIngredients = async () => {
let res = await ingredientService.getAll();
setingredients(res);
}
// const loadIngredients = (recipe) => {
// let initial = recipe.ingredients;
// let ingredients;
// const values = [...editFields];
// for (var i in initial){
// ingredients = initial[i].ingredients;
// }
// initial.map((ingredient) => {
// setEditFields(ingredient);
// }
// )
// setEditFields(values);
// console.log(values);
// }
// Method for calculating recipe cost
const cost = (nameKey, ingredient) => {
return 20;
}
// Method for calculating recipe proposed price
const proposed = (recipe) => {
return recipe * 2.5;
}
// Method for creating recipe
const createRecipe = async (recipe) => {
const ingredients = fields;
const object = {...newRecipe, ingredients }
recipeService.create(object);
window.location.reload();
}
// Method for
const changeItem = (e, recipe) => {
recipe[e.target.name] = e.target.value;
return recipe;
}
// Method for editing recipe
const editRecipe = async (e, recipe) => {
setCurrentRecipe(recipe);
// if (e.target) {
// setDisabled(false);
// }
// setEditFields(recipe.ingredients);
// const ingredients = editFields;
// const object = { recipe, ingredients }
// if (recipe._id === currentRecipe._id) {
// setDisabled(false);
// }
// console.log(editFields);
// recipeService.edit(object);
// window.location.reload();
}
const saveEditRecipe = async (recipe) => {
setCurrentRecipe(initialEditState);
setDisabled(true);
}
// Method for deleting recipe
const deleteRecipe = async (recipe) => {
if (window.confirm('Are you sure you wish to delete this item?')) {
recipeService.delete(recipe);
window.location.reload();
}
}
// Method for
const handleChange = (e) => setNewRecipe({
...newRecipe,
// [e.target.name]:e.target.name === "name" ? e.target.value : Number(e.target.value ),
[e.target.name]:e.target.value
});
// Method for
function handleChanger(idx, event) {
// console.log(recipes)
// console.log(event.target.name);
const values = event.target.className.includes("edit") ? [...editFields] : [...fields];
if (event.target.name === "name") {
values[idx].name = event.target.value;
} else {
values[idx].quantity = event.target.value;
}
event.target.className.includes("edit") ? setEditFields(values) : setFields(values);
}
// Method for
function handleAdd(e, idx) {
console.log(idx);
const values = e.target.className.includes("edit") ? [...editFields] : [...fields];
values.push({name:"", quantity: null});
e.target.className.includes("edit") ? setEditFields(values) : setFields(values);
}
// Method for
function handleRemove(e, i) {
const values = e.target.className.includes("edit") ? [...editFields] : [...fields];
values.splice(i, 1);
e.target.className.includes("edit") ? setEditFields(values) : setFields(values);
}
// rendering ingredients dropdown
const renderIngredientName = (ingredient) => {
let initial =[];
for (ingredient in ingredients) {
initial.push(ingredients[ingredient].name);
}
let createOption = (option) => {
return <option key={option.toLowerCase()} defaultValue={option.toLowerCase()} >{option.toLowerCase()}</option>;
}
return initial.map(createOption);
}
// Method for
const randomString = (recipe) => {
return String("expand"+Math.floor(Math.random()*100))
}
// Rendering recipes
const renderRecipe = recipe => {
const iselect = () => {
let initial = recipe.ingredients;
function splice(idx){
return initial.splice(idx, 1)
}
return initial.map((ingredient, idx) =>
<li key={`${ingredient}-${idx}`}>
<select name="name" className="edit recipe__ingredient__name" value={ingredient.name || ""} disabled={disabled} onChange={(e) => {handleChanger(idx, e)}}>
{renderIngredientName()}
</select>
<input type="number" name="quantity" id="quantity" className="recipe__ingredient__quantity" disabled={disabled} defaultValue={ingredient.quantity} />
{/* <span className={"float"}><Button color="danger" onClick={() => splice(idx)} className={'edit delete-ingredient'}>x</Button></span> */}
</li>
);
}
// eslint-disable-next-line
const edit = () => {
let edit = [{name:"", quantity: null}];
return edit.map((ingredient, idx) =>
<li key={`${ingredient}-${idx}-${recipe._id}`}>
<select name="name" className="edit recipe__ingredient__name" value={ingredient.name || ""} onChange={(e) => {handleChanger(idx, e)}}>
<option value=""></option>
{renderIngredientName()}
</select>
<input type="number" name="quantity" className="edit recipe__ingredient__quantity" value={ingredient.quantity || ""} placeholder="grams" onChange={e => handleChanger(idx, e)} />
<span className={'float'}>
{ idx === edit.length-1 ?
(<Button color="primary" className={'edit create-ingredient'} onClick={(e) => handleAdd(e, idx)}>+</Button>)
:
(<Button color="danger" onClick={(e) => handleRemove(e, idx)} className={'edit delete-ingredient'}>x</Button>)
}
</span>
</li>
)
}
return (
<li key={recipe._id} className="list__item recipe" id={randomString()}>
<Card body id={`card-${recipe._id}`} >
<CardTitle name={randomString()}><input type="text" name="name" defaultValue={recipe.name} readOnly={disabled} className="recipe__name" onChange={(e) => {changeItem(e, recipe)}} /></CardTitle>
<CardImg top width="100%" src={require('../assets/318x180.svg')} alt="Card image cap" />
<input id={String(recipe._id)} className="toggle" type="checkbox" onChange={(e) => {handleChange(e)}} />
<label htmlFor={String(recipe._id)} className="lbl-toggle bold">Ingredients</label>
{/* <span className="bold">Ingredients:</span> */}
<ul key={recipe._id} className="ingredients expand-content" id='ingredientList'>
{iselect()}
{/* {editFields.map((ingredient, idx) => {
return (
<li key={`${ingredient}-${idx}-${recipe._id}`}>
<select name="name" className="edit recipe__ingredient__name" value={ingredient.name || ""} onChange={(e) => {handleChanger(idx, e)}}>
<option value=""></option>
{renderIngredientName()}
</select>
<input type="number" name="quantity" className="edit recipe__ingredient__quantity" value={ingredient.quantity || ""} placeholder="grams" onChange={e => handleChanger(idx, e)} />
<span className={'float'}>
{ idx === editFields.length-1 ?
(<Button color="primary" className={'edit create-ingredient'} onClick={(e) => handleAdd(e, idx)}>+</Button>)
:
(<Button color="danger" onClick={(e) => handleRemove(e, idx)} className={'edit delete-ingredient'}>x</Button>)
}
</span>
</li>
);
})} */}
</ul>
<table>
<tbody>
<tr>
<td className={'center'}><span className={'bold'}>Actual Cost:</span></td>
<td className={'center'}><span className={'bold'}>Proposed Price:</span></td>
</tr>
<tr>
<td className={'center'}><span className="recipe__cost">£{cost(Math.floor(Math.random()*10))}</span></td>
<td className={'center'}><span className="recipe__proposed">£{proposed(cost(Math.floor(Math.random()*10)))}</span></td>
</tr>
</tbody>
</table>
<Button color="primary" onClick={() => { saveEditRecipe(recipe); }} className={`bold save-button ${disabled ? "hidden" : "show"}`}>Save</Button>
<Button color="primary" onClick={(e) => { editRecipe(e, recipe); }} id={`card-${recipe._id}`} className={'edit-button'}>Edit</Button>
<Button color="danger" onClick={() => { deleteRecipe(recipe); }} className={'delete'}>X</Button>
</Card>
</li>
);
};
return (
<div className="recipe">
<ul className="list">
{(recipes && recipes.length > 0) ? (
recipes.map(recipe => renderRecipe(recipe))
) : (
<p>No recipes found</p>
)}
<li key='new' className="add__item list__item recipe">
<Card body>
<CardTitle><input type="text" name="name" id="name" placeholder="New Recipe" className="recipe__name" onChange={(e) => {handleChange(e)}} /></CardTitle>
<CardImg top width="100%" src={require('../assets/318x180.svg')} alt="Card image cap" />
<span className={'bold'}>ingredients
{/* <span className={'float'}><Button color="primary" className={'create-ingredient'} onClick={(e) => handleAdd(e)}>+</Button></span> */}
</span>
<ul className="ingredients" id='ingredientList'>
{fields.map((field, idx) => {
return (
<li key={`${field}-${idx}`}>
<select name="name" className="recipe__ingredient__name" value={field.name || ""} onChange={(e) => {handleChanger(idx, e)}}>
<option value=""></option>
{renderIngredientName()}
</select>
<input type="number" name="quantity" className="recipe__ingredient__quantity" value={field.quantity || ""} placeholder="grams" onChange={e => handleChanger(idx, e)} />
{/* <span className={'float'}><Button color="danger" onClick={(e) => handleRemove(e, idx)} className={'delete-ingredient'}>x</Button></span> */}
<span className={'float'}>
{idx === fields.length - 1 ?
(<Button color = "primary" className = { 'create-ingredient' } onClick = { (e) => handleAdd(e) }>+</Button>)
:
(<Button color="danger" onClick={(e) => handleRemove(e, idx)} className={'delete-ingredient'}>x</Button>)
}
</span>
</li>
);
})}
</ul>
<CardText><span className={'bold'}>Calculated Cost:</span></CardText>
<CardText><span className={'bold'}>Calculated Price:</span></CardText>
<Button color="success" className={'new-recipe, bold'} onClick={() => { createRecipe(newRecipe); }}>Create new recipe</Button>
</Card>
</li>
</ul>
</div>
);
}
export default Recipe;
You didn't share your full code, but it should look something like this:
state = {
editableId:null
}
editRecipe = (event,recipe) => {
this.setState(({editableId:recipe._id}))
}
<Button disabled={!this.state.editableId == recipe._id} color="primary" onClick={(e) => { editRecipe(e, recipe); }} id={`card-${recipe._id}`} className={'edit-button'}>Edit</Button>
When you want no items to be editable, reset the state.editableId to null