Could anyone help me out here please, all I'm trying to do here is to show popup modal confirmation for delete action, but every time I clicked on **Yes **btn to confirm my delete action the last product on the list always get deleted instead. I need help from anyone please?
Here is my code for handling the delete popup
```
//OPEN DELETE MODALS
const [openDeleteModal, isOpenDeleteModal] = useState(false);
const closeDeleteModal = () => {
isOpenDeleteModal(false);
document.body.style.overflow = "unset";
};
const showDeleteModal = () => {
isOpenDeleteModal(true);
};
```
and here is the api
```
//DELETE PRODUCT
const deleteHandler = async (product) => {
try {
await axios.delete(`/api/products/${product._id}`, {
headers: { Authorization: `Bearer ${userInfo.token}` },
});
toast.success("product deleted successfully", {
position: "bottom-center",
});
dispatch({ type: "DELETE_SUCCESS" });
} catch (err) {
toast.error(getError(err), { position: "bottom-center" });
dispatch({ type: "DELETE_FAIL" });
}
};
```
down here is my modal for confirmation
```
{/* MODAL */}
{openDeleteModal && (
<div className="delete-modal">
<div className="delete-modal-box">
<div className="delete-modal-content">
<p className="delete-modal-content-p">
Are you sure to delete this product?
</p>
<div className="delete-modal-btn">
<button
onClick={closeDeleteModal}
className="delete-modal-btn-close"
>
Close
</button>
<button
onClick={() => {
deleteHandler(product);
closeDeleteModal();
}}
className="delete-modal-btn-yes"
>
{" "}
Yes
</button>
</div>
</div>
</div>
</div>
)}
```
All I'm trying to do is to be able to delete any product from the list not the last product every time.
here is the entirety of my productList map looks like
{products?.map((product, index) => (
<tr className="product-item-list" key={index}>
<tr>
<td className="product-item-id">{product._id}</td>
<td className="product-item-name">
{product.name}
</td>
<td className="product-item-price">
£{product.price}
</td>
<td className="product-item-category">
{product.category?.map((cat, index) => (
<span key={index}>{cat}</span>
))}
</td>
<td className="product-item-size">
{product.size?.map((s, index) => (
<span key={index}>{s} </span>
))}
</td>
<td className="product-btn-view">
<button
className="product-btn"
onClick={() =>
navigate(`/admin/productedit/${product._id}`)
}
>
Edit
</button>
<DeleteOutline
className="product-delete"
onClick={showDeleteModal}
/>
{/* MODAL */}
{openDeleteModal && (
<div className="delete-modal">
<div className="delete-modal-box">
<div className="delete-modal-content">
<p className="delete-modal-content-p">
Are you sure to delete this product?
</p>
<div className="delete-modal-btn">
<button
onClick={closeDeleteModal}
className="delete-modal-btn-close"
>
Close
</button>
<button
onClick={() => {
deleteHandler(product);
closeDeleteModal();
}}
className="delete-modal-btn-yes"
>
{" "}
Yes
</button>
</div>
</div>
</div>
</div>
)}
</td>
</tr>
<tr></tr>
</tr>
))}
I guess it happens because all of your modals open when you call showDeleteModal
And the last one is on the top, so when you click to delete the last closure works. Maybe its nessesary to pass id of product into the openDeleteModal. And than check if product.id equals to openDeleteModal.
Can you print to console the product.id when you click on the "Delete" button and check is it the correct id of the clicked product?
Related
The infinite scroll component is in a table and its inside a scrollable page. i tried giving
id="scrollableDiv" to every div in the page and also <html> in index.html still no use. when i remove the scrollableTarget="scrollableDiv" the fetchdata works untill the parent scroll bar in the bottom. after that fetchData function not working. when i forcefully scroll the parent scroll fetchdata works.
But i want it to work on scrolling the table. Not when scrolling the parent(i mean the whole page), anyone tell me where should i assingn id="scrollableDiv". There is no div with height specified
Here is the code,
const fetchMoreData = () => {
console.log("new more data");
const temp = [...ingestStats];
setTimeout(() => {
setIngestStats((prev) => prev.concat(temp));
}, 1500);};
<div className="row">
<div className="col-xl-12">
<div className="card dashboard-table">
{/* /.card-header */}
<div className="card-body p-0" id="collapse1">
<InfiniteScroll
dataLength={ingestStats.length}
next={fetchMoreData}
hasMore={ingestStats.length < 40 ? true : false}
loader={
<p style={{ textAlign: "center" }}>
<b>Loading...</b>
</p>
}
endMessage={
<p style={{ textAlign: "center" }}>
<b>Yay! You have seen it all</b>
</p>
}
scrollableTarget="scrollableDiv"
>
<table className="table table-hover table-borderless text-center table-sm table-responsive">
<thead>
<tr>
<th>Activity</th>
<th>
Time Stamp{" "}
<span href="#0">
<i className="fas fa-angle-down" />
</span>
</th>
<th>
Status{" "}
<span href="#0">
<i className="fas fa-angle-down" />
</span>
</th>
</tr>
</thead>
<tbody>
{ingestStats &&
ingestStats.map((item, index) => (
<tr key={`${item.time}-${index}`}>
<td>{item.activity}</td>
<td>{item.time}</td>
<td>
{item.isActive ? (
<span className="status-success">
Success
</span>
) : (
<span className="status-failed">
Success
</span>
)}
</td>
</tr>
))}
</tbody>
</table>
</InfiniteScroll>
</div>
{/* /.card-body */}
</div>
</div>
</div>
I was encountering a similar problem where the infinitescroll element would take the whole window in order to scroll... However, there is a small fix for this. You actually just need to add 'height' to the InfiniteScroll element and all your problems will be gone. That is the reason why it won't trigger your fetch data. See down for an example:
const [fetchIsLoading, setFetchIsLoading] = useState(false);
const [contacts, setContacts] = useState([]);
const loadMoreData = () => {
if (fetchIsLoading) {
return;
}
setFetchIsLoading(true);
fetch('https://randomuser.me/api/?results=10&inc=name,gender,email,nat,picture&noinfo')
.then((res) => res.json())
.then((body) => {
setContacts([...contacts, ...body.results]);
setFetchIsLoading(false);
})
.catch(() => {
setFetchIsLoading(false);
});
};
useEffect(() => {
loadMoreData();
}, []);
<div // < ------------- top level div with id 'scrollableDiv' is not even needed... but it works (if you use it or not)
// id="scrollableDiv"
// style={{height: 400}}
>
<InfiniteScroll style={{
// height: 400 <-------- this does not work!
}}
dataLength={contacts.length}
next={loadMoreData}
hasMore={true}//contacts.length < 15
loader={
<Skeleton
avatar
paragraph={{
rows: 1,
}}
active
/>
}
height={400} // <--------- however, this works through the way this infinite scroll is set up.
endMessage={<Divider plain>It is all, nothing more 🤐</Divider>}
//scrollableTarget="scrollableDiv" <-------- this is not even needed the way this infinite scroll is set up. Even though you point it to the top level div it works either way...
>
<List
dataSource={contacts}
renderItem={(item) => (
<List.Item key={item.email}>
<List.Item.Meta
avatar={<Avatar src={item.picture.large} />}
title={<div style={{ color: '#54BEC6', cursor: 'pointer' }}>{item.name.last}</div>}
description={item.email}
onClick={(event) => onClickContactsTab(event, item.name.last)}
/>
{
// Use this section to put extra information about the user
//<div>Extra info?</div>
}
</List.Item>
)}
/>
</InfiniteScroll>
</div>
TableItem component added without any data in UI. Could somebody help on this. On refereshing the UI, added data is shown with details in TableItem component.
Table Component Code
import TableItem from "./TableItem";
function Table({ searchWord }) {
const dispatch = useDispatch();
const dictData = useSelector((state) => state.dictionary);
useEffect(() => {
dispatch(getDictionaryAsync());
}, [dispatch]);
return (
<table className="table table-striped">
<thead>
<tr>
<th scope="col">Word</th>
<th scope="col">Description</th>
<th scope="col"></th>
</tr>
</thead>
<tbody>
{dictData &&
dictData
.filter((e) =>
searchWord === ""
? e
: e.word &&
e.word.toLowerCase().includes(searchWord.toLowerCase())
)
.map((item) => (
<TableItem item={item} key={item.id} searchWord={searchWord} />
))}
</tbody>
</table>
);
}
export default Table;
Below is the TableItem Component Code which i am trying to update,
When i add a word to dictionary it will fetch the details from the server and display it in the React app.
function TableItem({ item }) {
const [modal, setModal] = useState(false);
const openModal = () => {
setModal(true);
};
return (
<>
<tr key={item.id}>
<td style={{ textTransform: "capitalize" }}>{item.word}</td>
<td>
<b style={{ textTransform: "capitalize" }}>
{item.items && item.items[0].category} -{" "}
</b>
{item.items && truncate(item.items[0].definitions[0])}
</td>
<td>
<button className="btn btn-danger btn-sm " onClick={openModal}>
View
</button>
</td>
</tr>
<Modal isOpen={modal} ariaHideApp={true}>
<div className="modal-header">
<h3 className="modal-word-header">
{item.word && item.word.toUpperCase()}
</h3>
<button
className="btn btn-danger btn-sm"
onClick={() => setModal(false)}
>
<i class="fa fa-times" aria-hidden="true"></i>
</button>
</div>
<div className="model-content">
<p>
{item.items &&
item.items.map((e) => {
return (
<>
<i>{e.category}</i>
<ul>
{e.definitions.map((def) => {
return <li>{def}</li>;
})}
</ul>
</>
);
})}
</p>
</div>
</Modal>
</>
);
}
Better add your TableItem component code!
Below code works fine and updated the UI on change in the Data in TableItem,
useEffect(() => {
dispatch(getDictionaryAsync());
}, [dispatch, dictData]); *<--updated code*
I got list of users and next to each user there is block button.
After clicking on a block button, a modal pops up asking for confirmation. When i confirm, specific user is being block.
Now i got displaying modal but after clicking on confirm button nothing happens.
I think I need to assign a user to the button?
Unlock button works, but it's not in a modal.
My code:
import MyModal from '#/views/MyModal'
function UsersList({ users, userId }) {
function locking(pk, action) {
axios.get(`/user/${pk}/${action}/`).then(() => {
update()
})
}
return (
...{users != null && users.length > 0 ? (
users.map((profile) => {
return (
<tr key={profile.id} id={profile.id} className={userId === profile.id ? 'table-active' : ''}>
{showEntities && <td className='align-middle'>{profile.entity_name}</td>}
<td className='align-middle'>
{profile.first_name} {profile.last_name}
{!profile.active && (
<span className='ml-xl-2 badge badge-pill badge-secondary'>
<i className='fa fa-lock' /> Blocked
</span>
)}
</td>...
{profile.id !== userId && //to be sure to not block myself
(profile.active ? (
<button
type='button'
className='btn d-block btn-danger w-5rem mb-2 badge'
data-toggle='modal'
data-target='#MyModal'
>
Block
</button>
) : (
<a
className='btn d-block btn-warning w-5rem mb-2 badge'
href='#'
onClick={() => {
locking(profile.id, 'unlock')
}}
>
Unblock
</a>
))}
</td>
</tr>
)
})
) : (
)}
</tbody>
</table>
<MyModal locking={locking()} />
</div>
)
}
export default UsersList
MyModal
export default function MyModal({locking}) {
return (
<div className='modal fade' id='MyModal' tabIndex='-1' aria-labelledby='MyModal' aria-hidden='true'>
...
<h5 className='modal-title' id='MyModal'>
Are you sure to block this user?
</h5>
<button type='button' className='close' data-dismiss='modal' aria-label='Close'>
<span aria-hidden='true'>×</span>
</button>
</div>
<div className='modal-footer'>
<button type='button' className='btn btn-default' data-dismiss='modal'>
<i className='fas fa-times mr-2' />
Exit
</button>
<button
type='button'
className='btn btn-success'
onClick={() => {
locking
}}
>
<i className='fas fa-check-circle mr-2' />
Confirm
</button>
</div>
</div>
</div>
</div>
)
}
Issues
There is no click handler to block a profile
When passing locking to MyModal it is invoked immediately (locking={locking()})
In the MyModal block button onClick callback locking isn't invoked (() => { locking }
Solution
Add some state to UserList to store a user/profile id to block/unblock
Create a block callback to pass to the modal
Pass block callback to MyModal
UsersList
function UsersList({ users, userId }) {
const [profileId, setProfileId] = useState(null); // <-- create id state
function locking(pk, action) {
axios.get(`/user/${pk}/${action}/`)
.then(() => {
update();
})
.finally(() => {
setProfileId(null); // <-- clear state when done
});
}
const blockId = () => locking(profileId, 'lock'); // <-- callback to block/lock
return (
...
{users != null && users.length > 0 ? (
users.map((profile) => {
return (
<tr key={profile.id} id={profile.id} className={userId === profile.id ? 'table-active' : ''}>
...
</td>
...
{profile.id !== userId && //to be sure to not block myself
(profile.active ? (
<button
type='button'
className='btn d-block btn-danger w-5rem mb-2 badge'
data-toggle='modal'
data-target='#MyModal'
onClick={() => setProfileId(profile.id)} // <-- set id to block
>
Block
</button>
) : (
<a
className='btn d-block btn-warning w-5rem mb-2 badge'
href='#'
onClick={() => {
locking(profile.id, 'unlock')
}}
>
Unblock
</a>
))}
</td>
</tr>
)
})
) : (
)}
</tbody>
</table>
<MyModal locking={blockId} /> // <-- pass blockId callback
</div>
)
}
MyModal
export default function MyModal({locking}) {
return (
<div className='modal fade' id='MyModal' tabIndex='-1' aria-labelledby='MyModal' aria-hidden='true'>
...
<button
type='button'
className='btn btn-success'
onClick={locking} // <-- attach callback
>
<i className='fas fa-check-circle mr-2' />
Confirm
</button>
...
</div>
)
}
There is a 'Print' button on SaleInvoice.js which shows the Modal containing Report.js.
Report.js is created only to describe how and what should be displayed in the printed page.
Report.js has a button 'Print' which opens the Print Preview in the browser.
How do I make the 'Print' button on SaleInvoice.js directly open the Print Preview in the browser?
SaleInvoice.js
...
<button
style={{marginRight: '200px'}}
onClick={() => this.setState({printView: true})}>
Print
</button>
{this.state.printView ? (
<Modal
type="Print"
{...this.props}
show={this.state.printView}
modalClosed={() => this.setState({printView: false})}
/>
) : null}
...
Report.js
import React from 'react';
import styles from './SaleInvoice.module.css';
const SaleInvoice = props => {
const {customerName} = props;
const rows = props.lineItems;
const alignRightMarginRight = [styles.alignRight, styles.marginRight].join(
' ',
);
const {amountBeforeFreight, freight, amountAfterFreight} = props;
const reverseDateString = date => {
let [yyyy, mm, dd] = date.split('-');
return dd + '-' + mm + '-' + yyyy;
};
const renderContent = () => {
let i = 0;
let contentRows = (i, d) => {
return (
<React.Fragment>
<td className={styles.serialNumber}>{i}</td>
<td className={styles.productName}>{d.product_name}</td>
<td className={alignRightMarginRight}>{d.product_qty}</td>
<td className={alignRightMarginRight}>{d.product_unit}</td>
<td className={alignRightMarginRight}>{d.product_rate}</td>
<td className={alignRightMarginRight}>{d.product_disc}</td>
<td className={alignRightMarginRight}>{d.sub_total}</td>
</React.Fragment>
);
};
return rows.map(d => {
i++;
if (i === rows.length) {
return null;
}
if (i % 10 === 0) {
return (
<tr key={i} className={styles.pagebreak}>
{contentRows(i, d)}
</tr>
);
} else {
return <tr key={i}>{contentRows(i, d)}</tr>;
}
});
};
return (
<React.Fragment>
<div className={styles.all}>
<div className={[styles.header].join(' ')}>
<button className={styles.hideInPrint} onClick={window.print}>
Print
</button>
<button className={styles.hideInPrint} onClick={props.modalClosed}>
Close
</button>
<h5 className={styles.docTitle}>Estimate</h5>
<h6 className={styles.customerName}>
{customerName}
<span className={styles.date}>
{reverseDateString(props.date.split('T')[0])}
</span>
</h6>
</div>
<table className={styles.content}>
<thead>
<tr>
<td>No</td>
<td>Name</td>
<td className={alignRightMarginRight}>Qty</td>
<td className={alignRightMarginRight}>Unit</td>
<td className={alignRightMarginRight}>Rate</td>
<td className={alignRightMarginRight}>Disc</td>
<td className={alignRightMarginRight}>Total</td>
</tr>
</thead>
<tbody className={styles.content}>{renderContent()}</tbody>
</table>
<div className={styles.footer}>
<div className={styles.foot}>
<label>Amount:</label>
<label className={styles.amount}>
{amountBeforeFreight.toFixed(2)}
</label>
</div>
<div className={styles.foot}>
<label>Freight:</label>
<label className={styles.amount}>
{freight ? freight.toFixed(2) : 0}
</label>
</div>
<div className={styles.foot}>
<label>Final Amount:</label>
<label className={styles.amount}>
{amountAfterFreight.toFixed(2)}
</label>
</div>
</div>
</div>
</React.Fragment>
);
};
export default SaleInvoice;
in your file SalesInvoice.js, we would be changing the event onClick, instead of what action does(setting modal view to true in your state) we want it to open the print window in the browser.
as follows
<button
style={{marginRight: '200px'}}
onClick={window.print} >
Print
</button>
you will not need this part I guess?
{this.state.printView ? (
<Modal
type="Print"
{...this.props}
show={this.state.printView}
modalClosed={() => this.setState({printView: false})}
/>
) : null}
I use - React Redux and Redux form
I have a user list form which has table of contents with
edit/delete buttons on the right side of each row. When I click
on edit button it should pass the current row values to the user main form.
At the bottom of the form I have a addnew button which redirects to user main form.
My requirement is when I click addnew it should open user main form with empty fields,
when I click edit button on the table row it should open user main form with pre-populated fields with the
current selected row values.
How can I achieve this. Please help. Thanks in advance.
For Ex:
My UserList.js form
renderUsers() {
return this.props.users.reverse().map(user => {
return (
<tbody key={user._id}>
<tr key={user._id}>
<td>{user.email}</td>
<td>{user.firstname}</td>
<td><Link id={user._id} to="#" onClick={() => this.onDeleteUser(user._id)}>
<i className="material-icons">delete</i></Link>
<Link id={user._id} to="user/new"
onClick={ () => here I have to call usermain form sending the current row values}>
<i className="material-icons">edit</i></Link></td>
</tr>
</tbody>
);
});
}
render() {
return (
<div>
<div><h4>User List</h4></div><hr />
<table className="bordered highlight responsive">
<thead>
<tr>
<td>Email</td>
<td>First Name</td>
Action
{this.renderUsers()}
</table>
);
}
}
function mapStateToProps({ users}) {
return { users };
}
export default connect(mapStateToProps, { fetchUsers })(UserList);
My User.js form
const User = () => {
return(
<UserList />
<div className="fixed-action-btn">
<Link to="user/new" className="btn-floating btn-large waves-effect waves-light red">
<i className="material-icons">add</i>
</Link>
</div>
</div>
);
};
export default User;
My UserMain.js form
class UserMainForm extends Component {
renderFields() {
return _.map(userFields, ({ label, name }) => {
return (
<Field
key={name}
component={UserField}
label={label}
name={name}
/>
);
});
}
render() {
return (
<div className="container" style={{marginBottom:'5px'}}>
<div><h4>User Main { mode Edit/New}</h4></div><hr />
<form className="container" onSubmit={this.props.handleSubmit(() =>
this.props.submitUser(this.props.usermainForm.values,this.props.history))}>
<div className="row">
{this.renderFields()}
</div>
<Link to="/register/user" className="red btn-flat white-text">
Cancel
</Link>
<button type="submit" className="waves-effect waves-teal teal btn-flat right white-text">
Save
<i className="material-icons right">done</i>
</button>
</form>
</div>
);
}
}
export default reduxForm({
validate,
form: 'usermainForm',
destroyOnUnmount: false
})(UserMainForm);