I have the current web page below and want to delete a user when I click on the red 'x' button at the top of the card.
Currently, after I click the 'x' delete button, nothing happens. After I refresh the page, the user will be removed from my webpage. But I want this to happen without needing a refresh at all.
Sample Web Page Render:
Back-end Route:
To achieve this, I tried the following:
Setup back-end route:
const router = require('express').Router()
const {User} = require('../db/models')
module.exports = router
const {isUser, isAdmin} = require('../checks')
// 8080/api/users/:userId
router.delete('/:userId', isAdmin, async (req, res, next) => {
let userId = req.params.userId
try {
await User.destroy({
where: {
id: userId
}
})
res.status(204)
} catch (err) {
next(err)
}
})
Setup redux store and thunk:
import axios from 'axios'
// ACTION TYPES
const SET_USERS = 'SET_USERS'
const DELETE_USER = 'DELETE_USER'
// ACTION CREATORS
export const setUsers = users => ({
type: SET_USERS,
users: users
})
export const deleteUser = delUserId => ({
type: DELETE_USER,
delUserId: delUserId
})
// DOLLAR HELPER FOR CENTS FIELD
// export const toDollars = cents => {
// return `$${(cents / 100).toFixed(2)}`
// }
// THUNK CREATORS
export const getUsers = () => async dispatch => {
try {
const {data} = await axios.get('/api/users')
dispatch(setUsers(data))
console.log('getUsersThunk DATA ARRAY', data)
} catch (err) {
console.error(err)
}
}
export const deleteUserThunk = delUserId => async dispatch => {
try {
const response = await axios.delete(`/api/users/${delUserId}`)
const deleteUserId = response.data
dispatch(deleteUser(deleteUserId))
console.log('getUsersThunk DELETE', deleteUserId)
} catch (err) {
console.error(err)
}
}
// REDUCER
// INITIAL STATE
const allUsers = []
export default function(state = allUsers, action) {
switch (action.type) {
case SET_USERS:
return action.users
case DELETE_USER: {
let userRemovalArray = state.filter(user => user.id !== action.delUserId)
return userRemovalArray
}
default:
return state
}
}
Build front-end component that calls 'deleteUserThunk'
import React from 'react'
import {connect} from 'react-redux'
import {getUsers, deleteUserThunk} from '../store/allUsers'
import {updateUserThunk, fetchSingleUser} from '../store/singleUser'
// Status Filter import BeerFilter from './BeerFilter'
import Card from 'react-bootstrap/Card'
import Button from 'react-bootstrap/Button'
import {UncontrolledCollapse} from 'reactstrap'
export class AllUsers extends React.Component {
constructor(props) {
super(props)
this.state = {
showForm: false,
stat: ''
}
this.clickHandlerOne = this.clickHandlerOne.bind(this)
this.handleChange = this.handleChange.bind(this)
this.handleSubmit = this.handleSubmit.bind(this)
}
componentDidMount() {
try {
this.props.fetchInitialUsers()
} catch (error) {
console.error(error)
}
}
clickHandlerOne() {
let hidden = this.state.showForm
this.setState({
showForm: !hidden
})
}
handleChange(event) {
//console.log('event.target', event.target)
this.setState({
[event.target.name]: event.target.value
})
}
async handleSubmit(userId) {
event.preventDefault()
const updatedUser = {
id: userId,
isAdmin: this.state.stat
}
// console.log('UPDATE USER', updatedUser)
await this.props.updateUserThunk(updatedUser)
this.props.fetchInitialUsers()
}
render() {
const users = this.props.users
// console.log('PROPS', this.props)
console.log('USERS', this.props.users)
return (
<div>
{/* <div className="options">
<select onChange={this.handleChange}>
<option value="">Sort By...</option>
<option value="priceHighToLow">Price (high to low)</option>
<option value="priceLowToHigh">Price (low to high)</option>
<option value="name">Name</option>
</select>
<BeerFilter />
</div> */}
<div className="flex-cards">
{users.map(user => (
<Card style={{width: '18rem'}} key={user.id}>
{/* delete thunk */}
<div>
<Button
id={`delete${user.id}`}
variant="danger"
onClick={() => this.props.deleteUserThunk(user.id)}
>
X
</Button>
</div>
<Card.Body>
<Card.Title>User Id: {user.id}</Card.Title>
<Card.Text>
<div>
<ul>
<li>
<div className="highlight">
<img src={user.imageUrl} />
</div>
<div className="details">
<p>Username: {user.username}</p>
<p>User Email: {user.email}</p>
<p>Admin Status: {user.isAdmin ? 'true' : 'false'}</p>
<p>
Created Date:{' '}
{new Intl.DateTimeFormat('en-GB', {
month: 'short',
day: '2-digit',
year: 'numeric'
}).format(new Date(user.createdAt))}
</p>
<p />
<Button
id={`user${user.id}`}
onClick={() => {
this.clickHandlerOne()
}}
variant="outline-info"
>
Admin Status Toggle
</Button>
<UncontrolledCollapse toggler={`#user${user.id}`}>
<form onSubmit={() => this.handleSubmit(user.id)}>
<div>
<span>
<select
name="stat"
value={this.state.isAdmin}
onChange={this.handleChange}
>
<option value="">user isAdmin?</option>
<option value="true">true</option>
<option value="false">false</option>
</select>
</span>
<div>
{/* */}
<button type="submit">Submit</button>
</div>
</div>
</form>
</UncontrolledCollapse>
</div>
</li>
</ul>
</div>
</Card.Text>
</Card.Body>
</Card>
))}
</div>
</div>
)
}
}
const mapStateToProps = state => {
return {
users: state.allUsers
}
}
const mapDispatchToProps = dispatch => {
return {
loadSingleUser: id => dispatch(fetchSingleUser(id)),
updateUserThunk: updatedUser => dispatch(updateUserThunk(updatedUser)),
//getSortedBeers: (sortBy, beers) => dispatch(sortBeers(sortBy, beers)),
fetchInitialUsers: () => dispatch(getUsers()),
deleteUserThunk: userId => dispatch(deleteUserThunk(userId))
}
}
export default connect(mapStateToProps, mapDispatchToProps)(AllUsers)
With my code above, when I click on the red 'x' button nothing happens. I have to hit the refresh button for my now deleted user to be removed from my webpage.
How can I have the user removed from my current view without having to hit refresh?
This isn't a complete answer, but there's definitely a problem here:
const response = await axios.delete(`/api/users/${delUserId}`)
If you look at the screenshot you provided of the error in the web console, it's showing undefined where delUserId should be. So somewhere along the line between the click on the 'X' and the line above, you aren't passing the user ID correctly.
Here, as you are using mapDispatchToProps in your component "AllUsers"
your deleteUserThunk(in THUNK CREATORS) is assigned to deleteUserThunk(in your component "AllUsers")
hence, you need to call your THUNK CREATEORS function by calling the component function which is assigned in mapDispatchToProps
You have to call it in the following way
onClick={() => this.props.deleteUserThunk(user.id)}
This will pass your user.id to deleteUserThunk(in your component "AllUsers") to deleteUserThunk(in THUNK CREATORS)
As per your comments..
Firstly, you have to remove it from componentDidMount() because, you should not run your delete function when your component is mounted.
Secondly, if your reducer is updated, then your browser will update without any refresh. Try checking the parameters which are passed into DELETE_USER
my suggestion would be:
in your function deleteUserThunk (in THUNK CREATORS)
replace dispatch(deleteUser(deleteUserId)) with
dispatch(deleteUser(delUserId))
Related
I know, I know, this question gets asked a hundred times a day, but none of the solutions have been working for me. I'm updating a variable using useState and it is not re-rendering any of my components.
I'm POSTing some data using the NextJS API Routing which updates a document in a MongoDB and returns the updated document. Once it's returned, it updates a state with the updated list. I've got a button that just console logs the list variable and it's being updated correctly, but it's not re-rendering anything when it gets updated.
// /components/AddNew.jsx
export default function AddNew({ user, list, setList }) {
// ...
fetch('/api/lists', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify(data),
})
.then(response => response.json())
.then((response) => {
if (response.status === 200) {
console.log(response.list);
setList([...response.list]);
setMessage(<div><strong>{name}</strong> has been added</div>);
} else {
setMessage(<div>Something went wrong, sorry</div>);
}
})
// ...
}
// /api/lists.js
import clientPromise from "../../lib/mongodb";
export default async function handler(req, res) {
const client = await clientPromise;
const db = client.db("MY_DATABASE");
switch (req.method) {
case "POST":
let userListExists = await db.collection("lists").findOne({user_id: req.body.user_id});
if (userListExists) {
const updateResponse = await db.collection("lists").findOneAndUpdate(
{ user_id: req.body.user_id },
{ $push: { list: req.body.list[0] } },
{ returnOriginal: false }
);
const updatedList = updateResponse.value.list;
res.json({status: 200, list: updatedList});
} else {
let newList = await db.collection("lists").insertOne(req.body);
res.json({status: 200, list: newList});
}
break;
case "GET":
const userID = req.query.user_id;
const userList = await db.collection("lists").findOne({user_id: userID});
res.json({ status: 200, data: userList });
break;
}
}
The document is being updated correctly, the response.list that gets console logged in AddNew.jsx is showing the full, updated list, but it's just not re-rendering. I'm certain I'm just missing something glaringly obvious but I just can't see what I'm doing wrong.
const [list, setList] = useState(false); are set in a parent component and passed down;
Edit: In the AddNew component I'm returning this:
return (
<div className="add-new">
<h2>Add new item</h2>
{message}
<form onSubmit={handleSubmit}>
<label>
<span>Name of activity</span>
<input className="name" type="text" name="name" value={name} onChange={(e) => {
setName(e.target.value);
}} />
</label>
<label>
<span>Categories (comma separated)</span>
<input className="categories" type="text" name="categories" value={rawCategories} onChange={(e) => {
setRawCategories(e.target.value.split(','));
}} />
</label>
<button type="submit">Submit</button>
</form>
</div>
);
and a parent that's being passed the list and setList from it's component looks like this:
import Result from "#/components/Result";
import RandomiseButton from "#/components/RandomiseButton";
import CategorySelector from "#/components/CategorySelector";
import { useState } from "react";
import AddNew from "./AddNew";
export default function Home({ user, list, setList }) {
const { _id, username } = user;
const [category, setCategory] = useState('all');
const [selectedItem, setSelectedItem] = useState(false);
let categories = [
'all',
];
if (list) {
console.log('has list');
list.forEach(element => {
element.categories.forEach(cat => {
if (!categories.includes(cat)) {
categories.push(cat);
}
});
console.log(element);
});
} else {
console.log('no list');
}
return (
<>
<div className="controls">
<CategorySelector
category={category}
categories={categories}
setCategory={setCategory}
list={list}
/>
<RandomiseButton
category={category}
setSelectedItem={setSelectedItem}
list={list}
/>
</div>
<Result selectedItem={selectedItem} list={list} />
<AddNew user={user} list={list} setList={setList} />
</>
)
}
Embarrassingly, the answer was as simple as a typo - apologies to anyone with this same issue finding this now...
I have 2 components, the Favorites component, makes a request to the api and maps the data to Card.
I also have a BtnFav button, which receives an individual item, and renders a full or empty heart according to a boolean.
Clicking on the BtnFav render removes a certain item from the favorites database.
What I need is that in the Favorites component, when I click on the BtnFavs component, the useEffect of Favorites is triggered again to bring the updated favorites.
How can i solve this? I have partially solved it with a global context(favoritesUser), but is there any other neater alternative?
The data flow for now would be something like this:
Favorites component fetches all the complete data and passes it to the Card component, the Card component passes individual data to the BtnFavs component.
Favorites Component:
const fetchWines = async () => {
try {
const vinos = await axios.get(`/api/favoritos/${id}`);
const arrVinos = vinos.data.map((vino) => {
return vino.product;
});
setVinosFavs(arrVinos);
} catch (err) {
console.error(err);
}
};
useEffect(() => {
fetchWines();
}, [favoritesUser]);
return (
<div>
<h1>Mis favoritos</h1>
<Card listWines={vinosFavs} />
</div>
);
BtnFavs:
const handleClickFav = (e) => {
if (!boton) {
axios.post("/api/favoritos/add", { userId, productId }).then((data) => {
setBoton(true);
return;
});
}
axios.put("/api/favoritos/delete ", { userId, productId }).then((data) => {
setBoton(false);
setFavoritesUser(data);
});
};
What I need is that in the Favorites component, when I click on the BtnFavs component, the useEffect of Favorites is triggered again to bring the updated favorites.
How can i solve this? I have partially solved it with a global context(favoritesUser), but is there any other neater alternative?
The pattern you want is called a callback function, just like the onClick of a button. You pass a function to your components that get executed given a condition. If you want fetchWines to be called again, then just pass the function in as a prop.
Favorites Component:
<Card listWines={vinosFavs} refresh={fetchWines} />
Card Component
<BtnFavs onDelete={refresh} ... />
BtnFavs Component
onDelete();
You can name it whatever you want, but generally callbacks will be named like on<condition>.
If you really wanted useEffect to be triggered then you would pass a setState function that set one of the dependencies, but I don't see a point in this case.
I will share code, because this problem its normal for me, i really want to learn and improve that.
const Favorites = () => {
const { favoritesUser } = useFavoritesContext();
const user = useSelector((state) => state.user);
const id = user.id;
const [vinosFavs, setVinosFavs] = useState([]);
const fetchWines = async () => {
try {
const vinos = await axios.get(`/api/favoritos/${id}`);
const arrVinos = vinos.data.map((vino) => {
return vino.product;
});
setVinosFavs(arrVinos);
} catch (err) {
console.error(err);
}
};
useEffect(() => {
fetchWines();
}, [favoritesUser]);
return (
<div>
<h1>My favorits</h1>
<Grid listVinos={vinosFavs} />
</div>
);
};
export default Favorites
Grid
export default function Grid({ listVinos }) {
return (
<div>
<ul className={styles.layoutDeVinos}>
{listVinos?.map((element) => {
return <WineCard key={element.id} vino={element} />;
})}
</ul>
</div>
);
}
Card
export default function WineCard({ vino }) {
return (
<>
<div>
<Link to={`/products/${vino.id}`}>
<li>
<div className={styles.card}>
<div
className={styles.img1}
style={{
backgroundImage: `url(${vino.images})`,
}}
></div>
<div className={styles.text}>{vino.descripcion}</div>
<div className={styles.catagory}>
{vino.nombre}
<i className="fas fa-film"></i>
</div>
<div className={styles.views}>
{vino.bodega}
<i className="far fa-eye"></i>{" "}
</div>
</div>
</li>
</Link>
<div className="botonesUsuario">
<BtnFavs vino={vino} />
</div>
</div>
</>
);
}
BTN FAVS
export default function BtnFavs({ vino }) {
const { setFavoritesUser } = useFavoritesContext();
const [boton, setBoton] = useState(false);
const user = useSelector((state) => state.user);
const userId = user.id;
const productId = vino.id;
useEffect(() => {
axios
.post("/api/favoritos/verify", { userId, productId })
.then((bool) => setBoton(bool.data));
}, []);
const handleClickFav = (e) => {
if (!boton) {
axios.post("/api/favoritos/add", { userId, productId }).then((data) => {
setBoton(true);
return;
});
}
axios.put("/api/favoritos/delete ", { userId, productId }).then((data) => {
setBoton(false);
setFavoritesUser(data);
});
};
return (
<>
{!user.id ? (
<div></div>
) : boton ? (
<span
class="favIcons material-symbols-rounded"
onClick={handleClickFav}
>
favorite
</span>
) : (
<span className="material-symbols-rounded" onClick={handleClickFav}>
favorite
</span>
)}
</>
);
}
I'm trying to send a parameter to the api to give me an exact json response, but when i'm trying to submit my form via select onChange={onAddSubmit}, it is still passing the default value 146846 but I already changed my select to 146847 so i'm getting the invalid response.
Home.js
import React, { useEffect, useState } from "react";
import Api from "../Api";
import AppContainer from "../tmp/AppContainer";
import HomeContainer from "./HomeContainer";
const Home = () => {
const [posts, setPosts] = useState();
const [loading, setLoading] = useState(false);
const [allValues, setAllValues] = useState({
contractId: "146846",
planId: "1028",
dateStart: "2021-01-30",
dateEnd: "2021-01-31",
numberOfAdults: 1,
numberOfChildren: 0,
planOption: "Individual",
unit: "day",
});
const changeHandler = (e) => {
setAllValues({ ...allValues, [e.target.name]: e.target.value });
};
const onAddSubmit = async (e) => {
e.preventDefault();
setLoading(true);
try {
await Api.getCurlPost({
...allValues,
}).then((response) => {
const result = response.data;
setPosts(result.data);
});
} catch {
alert("Failed to add post!");
} finally {
setLoading(false);
}
};
useEffect(() => {
onAddSubmit;
}, []);
return (
<AppContainer>
<HomeContainer
onAddSubmit={onAddSubmit}
changeHandler={changeHandler}
loading={loading}
posts={posts}
/>
</AppContainer>
);
};
export default Home;
Here is my HomeContainer.js that contains my html and bootstrap design
import React, { Component } from "react";
import { FontAwesomeIcon } from "#fortawesome/react-fontawesome";
import { faPlane } from "#fortawesome/free-solid-svg-icons";
import { faCircleNotch } from "#fortawesome/free-solid-svg-icons";
class HomeContainer extends Component {
render() {
const { onAddSubmit, changeHandler, loading, posts } = this.props;
return (
<div>
<div className="row mb-auto">
<div className="col-md-6 p-2">
<img
width="100%"
src="../resources/img/starr/7437.jpg"
/>
<div className="mt-3 shadow-sm p-3 mb-5 bg-white rounded">
<h3 className="text-center">
<FontAwesomeIcon
icon={faPlane}
rotation={0}
style={{ marginRight: 5 }}
/>
Travel Details
</h3>
<form onChange={onAddSubmit}>
<div className="form-group">
<select
className="form-control"
name="contractId"
onChange={changeHandler}
>
<option value="146846">
TravelLead Domestic Travel Insurance
</option>
<option value="146847">
TravelLead International Travel
Insurance
</option>
</select>
</div>
<div className="form-group">
<select
className="form-control"
name="planId"
onChange={changeHandler}
>
<option value="1028">
Economy (Single Trip)
</option>
<option value="1029">
Elite (Single Trip)
</option>
</select>
</div>
</form>
</div>
</div>
</div>
</div>
);
}
}
export default HomeContainer;
I figured it already, I just added allValues as a parameter when calling onAddSubmit on my useEffect hook, and if there is something changes happen to allValues list it will automatically submit a request to the API and throw a response.
useEffect(() => {
onAddSubmit(allValues);
}, [allValues]);
And onAddSubmit function, just change the ...allValues to ...data on api parameter that sends a request to axios.
const onAddSubmit = async (data) => {
setLoading(true);
try {
await Api.getCurlPost({
...data,
}).then((response) => {
const result = response.data;
setPosts(result.data);
});
} catch {
alert("Failed to add post!");
} finally {
setLoading(false);
}
};
I am very new to react and currently using Context API, I am trying to get the country name from user input in the form then send back to the context, and in context I am using Componentdidmount to call API and show data, when user input, its saves data alert it but then suddenly shows up with that error.
This is my form file..
import React, { useState } from 'react'
import { Consumer } from '../../context';
import Spinner from '../spinner';
function Country() {
const [name, setName] = useState('');
//
function Submit (e, dispatch){
e.preventDefault();
alert(`this form is submited ${name}`)
dispatch({type: 'SELECT_COUNTRY', payload: name});
setName('');
}
return (
<Consumer>
{ value =>{
if (!value.chart.length){
return <Spinner/>
}
else{
// setCountry(value.countries)
const { dispatch } = value;
console.log('coming from dispatch',dispatch)
return (
<div className='columns'>
<div className='column'>
<form onSubmit={Submit.bind(dispatch)}>
<div className="field">
<label className="label">Pick your Country</label>
<div className="control has-icons-left has-icons-right">
<input className="input" type="text" placeholder="Enter Country name..." value={name} onChange={e => setName(e.target.value)}/>
<span className="icon is-small is-left">
<i className="fas fa-globe-asia"></i>
</span>
<span className="icon is-small is-right">
<i className="fas fa-check"></i>
</span>
</div>
</div>
</form>
</div>
</div>
);**strong text**
}
}}
</Consumer>
)
}
export default Country;
This is my Context file..
import React, { Component } from 'react';
import axios from "axios";
import Country from './components/country/country';
const Context = React.createContext();
const reducer = (state, action) => {
switch(action.type) {
case 'SELECT_COUNTRY':
return {
...state,
cont:action.payload
};
default:
return state;
}
}
export class Provider extends Component {
state = {
data : {},
chart : {},
countries : {},
cont: '',
dispatch : action => this.setState(state =>
reducer(state,action))
}
componentDidMount(){
axios.get('https://covid19.mathdro.id/api')
.then(res => {
// console.log(res.data)
this.setState({ data : res.data});
})
.catch(err => console.log(err))
axios.get('https://covid19.mathdro.id/api/daily')
.then(res => {
const result = res.data.map(({ confirmed, deaths, reportDate: date }) => ({ confirmed: confirmed.total, deaths: deaths.total, date }));
this.setState({ chart : result});
})
.catch(err => console.log(err))
axios.get('https://covid19.mathdro.id/api/countries')
.then(res => {
console.log('yeh country ka res h', res.data.countries)
const { countries } = res.data;
// console.log('yesh country ka destructuring h',countries)
this.setState({ countries : countries.map( country => country.name)});
})
.catch(err => console.log(err))
}
render() {
return (
<Context.Provider value= {this.state}>
{this.props.children}
</Context.Provider>
)
}
}
export const Consumer = Context.Consumer;
You were not calling your Submit function correctly, try this:
form onSubmit={(e)=>Submit(e,dispatch)}
Im trying to get access a variable called isSuperAdmin, It basically tells me if the logged in user is a super admin or not allowing me to disable some features.
I currently have no access to the variable in the current page however my redux action is showing it as being there, I think I may have configured something incorrectly, as of now my code doesn't change from the initial state value of null to the bool value isSuperUser. Here is the page that I am trying to use this variable.
import React, { PropTypes } from 'react';
import withStyles from 'isomorphic-style-loader/lib/withStyles';
import { connect } from 'react-redux';
import Modal from '../Modal';
import Summary from '../Summary';
import s from './BookingDetailsModal.scss';
import AmendConsumerDetails from './AmendConsumerDetails';
import ChangeBookingSession from './ChangeBookingSession';
import payReservationCashActionCreator from '../../actions/payReservationCash';
import payReservationCardActionCreator from '../../actions/payReservationCard';
import payRestActionCreator from '../../actions/payRest';
import refundCashActionCreator from '../../actions/refundCash';
import cancelReservationActionCreator from '../../actions/cancelReservation';
import formatPrice from '../../../../../core/formatPrice';
import {
BOXOFFICE_HIDE_BOOKING_DETAILS,
BOXOFFICE_SET_BOOKING_DETAILS_ACTION_TYPE,
resendConfirmationEmail as resendConfirmationEmailActionCreator,
} from '../../actions';
function renderActionButtons({
isSuperAdmin,
setActionType,
resendConfirmationEmail,
order: {
type: orderType,
paid: orderPaid,
amount: orderAmount,
refundedAt: orderRefundedAt,
canceledAt: orderCanceledAt,
sessionId,
},
isCreatingPayment,
payReservationCard,
payReservationCash,
payRest,
refundCash,
cancelReservation,
}) {
debugger;
return (
<div className={s.buttonsContainer}>
<div className={s.buttonsContainer}>
<div className={s.buttonContainer}>
<button
onClick={(e) => {
e.preventDefault();
setActionType('AMEND_CONSUMER_DETAILS');
}}
>Amend consumer details</button>
</div>
{ sessionId ?
<div className={s.buttonContainer}>
<button
onClick={(e) => {
e.preventDefault();
setActionType('CHANGE_SESSION');
}}
>Move to another session</button>
</div> : null
}
<div className={s.buttonContainer}>
<button disabled>Amend tickets or products</button>
</div>
{ orderType === 'reservation' && isCreatingPayment && !orderPaid ?
<div>
<div className={s.buttonContainer}>
<button
onClick={(e) => {
e.preventDefault();
payReservationCash();
}}
>Pay Reservation CASH</button>
</div>
<div className={s.buttonContainer}>
<button
onClick={(e) => {
e.preventDefault();
payReservationCard();
}}
>Pay Reservation CARD</button>
</div>
</div> :
null
}
{ orderType === 'deposit' && isCreatingPayment && !orderPaid ?
<div>
<div className={s.buttonContainer}>
<button
onClick={(e) => {
e.preventDefault();
payRest('CASH');
}}
>Pay Rest CASH</button>
</div>
<div className={s.buttonContainer}>
<button
onClick={(e) => {
e.preventDefault();
payRest('CARD');
}}
>Pay Rest CARD</button>
</div>
</div> :
null
}
{ !orderRefundedAt && orderPaid ?
<div className={s.buttonContainer}>
<button
disabled={isSuperAdmin}
onClick={(e) => {
e.preventDefault();
refundCash(orderAmount);
}}
>Refund CASH, {formatPrice(orderAmount)}</button>
</div> : null
}
{ orderCanceledAt === null && orderType === 'reservation' ?
<div className={s.buttonContainer}>
<button
onClick={(e) => {
e.preventDefault();
cancelReservation();
}}
>Cancel Reservation</button>
</div> : null
}
<div className={s.buttonContainer}>
<button
onClick={(e) => {
e.preventDefault();
resendConfirmationEmail();
}}
>Resend confirmation email</button>
</div>
</div>
</div>
);
}
renderActionButtons.propTypes = {
isSuperAdmin: PropTypes.bool.isRequired,
setActionType: PropTypes.func.isRequired,
resendConfirmationEmail: PropTypes.func.isRequired,
order: PropTypes.shape({
type: PropTypes.string.isRequired,
paid: PropTypes.bool.isRequired,
sessionId: PropTypes.string.isRequired,
amount: PropTypes.number.isRequired,
// reservationPaidCashAt: PropTypes.string.isRequired,
// reservationPaidCardAt: PropTypes.string.isRequired,
}).isRequired,
payReservationCard: PropTypes.func.isRequired,
payReservationCash: PropTypes.func.isRequired,
payRest: PropTypes.func.isRequired,
isCreatingPayment: PropTypes.bool.isRequired,
refundCash: PropTypes.func.isRequired,
cancelReservation: PropTypes.func.isRequired,
};
const components = {
AMEND_CONSUMER_DETAILS: AmendConsumerDetails,
CHANGE_SESSION: ChangeBookingSession,
};
function renderAction(actionType, props) {
const Component = components[actionType];
return <Component {...props} />;
}
function BookingDetailsModal(props) {
const { hideOrderDetails, orderId, bookingDetailsActionType } = props;
return (
<Modal onClose={hideOrderDetails}>
<div className={s.container}>
<div className={s.summaryContainer}>
<Summary orderId={orderId} withEdits={false} />
</div>
<div className={s.actionsContainer}>
{bookingDetailsActionType ?
renderAction(bookingDetailsActionType, props) :
renderActionButtons(props)
}
</div>
</div>
</Modal>
);
}
BookingDetailsModal.propTypes = {
orderId: PropTypes.string.isRequired,
hideOrderDetails: PropTypes.func.isRequired,
bookingDetailsActionType: PropTypes.oneOf([
'AMEND_CONSUMER_DETAILS',
]),
};
const mapStateToProps = (state, { orderId }) => (
{
ui: { bookingDetailsActionType },
ui: { isSuperAdmin },
orders: {
data: { [orderId]: order },
edits: { [orderId]: orderEdits },
},
}
) => ({
bookingDetailsActionType,
isSuperAdmin,
order,
isCreatingPayment: orderEdits.isCreatingPayment,
});
const mapDispatchToProps = (dispatch, { orderId }) => ({
hideOrderDetails: () => dispatch({ type: BOXOFFICE_HIDE_BOOKING_DETAILS }),
setActionType: actionType =>
dispatch({ type: BOXOFFICE_SET_BOOKING_DETAILS_ACTION_TYPE, actionType }),
resendConfirmationEmail: () => dispatch(resendConfirmationEmailActionCreator(orderId)),
payReservationCard: () => dispatch(payReservationCardActionCreator(orderId)),
payReservationCash: () => dispatch(payReservationCashActionCreator(orderId)),
payRest: type => dispatch(payRestActionCreator(orderId, type)),
refundCash: amount => dispatch(refundCashActionCreator(orderId, amount)),
cancelReservation: () => dispatch(cancelReservationActionCreator(orderId)),
});
export default connect(mapStateToProps, mapDispatchToProps)(withStyles(s)(BookingDetailsModal));
My Redux tab on page load shows the following:
type(pin): "BOXOFFICE_IS_SUPER_USER"
isSuperAdmin(pin): true
This is how I have used createStore to access the variable:
const isSuperAdmin = createStore(null, {
[BOXOFFICE_IS_SUPER_USER]: isSuperAdmin => isSuperAdmin,
});
I then proceeded to add it to the reducer at the bottom.
edit I have changed the variable isSuperAdmin in the createStore to true and this can be read perfectly fine, it must now be an issue with the variable passed to the action in the first place.
Here is the code where I get the value of the variable and pass it on:
Export default ({ knex }) => authenticateAdmin(knex)(
async (req, res) => {
try {
const { eventId } = req;
const event = await fetchEvent(knex, eventId);
const isSuperAdmin = await res.isSuperAdmin;
res.send({ event, isSuperAdmin});
} catch (err) {
res.send(err.stack);
console.error(err.stack); // eslint-disable-line no-console
throw err;
}
}
);
And the dispatch:
export const fetchEvent = () => async (dispatch, getState) => {
try {
const state = getState();
const { auth: { password } } = state;
const response = await fetch('/api/event', {
headers: {
Accept: 'application-json',
'X-Password': password,
},
});
if (response.status === 200) {
const { event, isSuperAdmin } = await response.json();
dispatch({ type: BOXOFFICE_SET_EVENT, event });
dispatch({ type: BOXOFFICE_IS_SUPER_USER, isSuperAdmin });
} else {
localStorage.removeItem('password');
dispatch({ type: BOXOFFICE_UNAUTHENTICATE });
}
} catch (err) {
console.log(err); // eslint-disable-line no-console
throw err;
}
};
EDIT
Here is the reducer:
export default combineReducers({
isSuperAdmin, ------- My variable
isProcessingPayment,
isSelectDateCollapsed,
isLoadingBookings,
shouldShowBookings,
shouldShowDepositModal,
shouldShowReservationModal,
shouldShowConsumerDetailsModal,
shouldShowDiscountModal,
shouldShowOrderConfirmationModal,
bookingFilter,
selectedOrderId,
sendConfirmationEmail,
bookingIds,
orderDetailsId,
bookingDetailsActionType,
});
I guess the way you defined your mapStateToProps is incorrect.
Updated the code
try following:
const mapStateToProps = ({
ui: {
bookingDetailsActionType,
isSuperAdmin
},
orders: {
data,
edits
}
}, {
orderId
}) => {
const order = data[orderId],
orderEdits = edits[orderId];
return {
bookingDetailsActionType,
isSuperAdmin,
order,
isCreatingPayment: orderEdits.isCreatingPayment
};
};
I finally have a solution! Turns out my issue was not setting a property type for my isSuperUser variable. Despite my colleague telling me that it will work without any property type (which still makes sense to me and confuses me as to why it wont work?!).
A simple change in the index.js file from:
[BOXOFFICE_IS_SUPER_USER]: isSuperAdmin => isSuperAdmin,
to
[BOXOFFICE_IS_SUPER_USER]: (state, { isSuperAdmin }) => isSuperAdmin,
and adding a property type to the show.js file where I used res.send()
res.send({ event, isSuperAdmin: isSuperAdmin});
Im still at a loss as to why it won't work with no property type but oh well...!