I am trying to submit a form with a ibanElement, locally it works perfectly but on my test environment : Failed to execute 'postMessage' on 'DOMWindow': The target origin provided ('https://js.stripe.com') does not match the recipient window's origin (https://my.test.com)
any suggestion?
Thanks for the help
const ELEMENT_TYPE = 'card';
const PAYMENT_METHODS_CREDIT_CARD = "CREDIT_CARD";
const PAYMENT_METHODS_SEPA = "SEPA";
Vue.use(localeService);
const localeFromCookies = Vue.prototype.$getLocale(localeCookieName, null);
Vue.use(VeeValidate, { locale: localeFromCookies });
Vue.use(VueMask.VueMaskPlugin);
var paymentMethodsApp = new Vue({
el: '#app-payment-methods',
data() {
return {
wizardStepSize : 'md',
paymentMethod : PAYMENT_METHODS_CREDIT_CARD,
billingAddressStreet: null,
billingAddressLine2: null,
billingAddressCity: null,
billingAddressPostalCode : null,
billingAddressCountry: null,
billingEmail: null,
stripe: null,
elements: null,
cardElement: null,
isCardElementEmptyOrInvalid : true,
ibanElement : null,
isIbanElementEmptyOrInvalid : true,
paidCustomerId: null,
paidSubscriptionId : null,
stripeClientSecret : null,
stripePaymentConfirmation: false,
paymentMethodsSubmitting : false,
billingDataChecking : true,
alerts: []
}
},
methods: {
onCreditCardPaymentSelected() {
Vue.nextTick(function () {
paymentMethodsApp.mountStripeCardElement();
paymentMethodsApp.unmountStripeSepaIbanElement();
});
},
onInvoiceEBankingPaymentSelected() {
Vue.nextTick(function () {
paymentMethodsApp.unmountStripeCardElement();
paymentMethodsApp.unmountStripeSepaIbanElement();
});
},
onInvoicePhysicalPaymentSlipSelected() {
Vue.nextTick(function () {
paymentMethodsApp.unmountStripeCardElement();
paymentMethodsApp.unmountStripeSepaIbanElement();
});
},
onSEPAPaymentSelected(){
Vue.nextTick(function () {
paymentMethodsApp.unmountStripeCardElement();
paymentMethodsApp.mountSepaElement();
});
},
saveCustomer() {
const customerData = {
"paymentMethod" : this.paymentMethod,
"billingAddressStreet" : this.billingAddressStreet,
"billingAddressLine2" : this.billingAddressLine2,
"billingAddressCity" : this.billingAddressCity,
"billingAddressPostalCode" : this.billingAddressPostalCode,
"billingAddressCountry" : this.billingAddressCountry,
"billingEmail" : this.billingEmail,
}
return axios.post("/billings/customers", customerData)
.then(response => {
this.paidCustomerId = response.data.paidCustomerId;
})
.catch(error => {
if (error.response.status === 401 || error.response.status === 403) {
location.reload();
}
this.createAlert(saveBillingAddressErrorMsg);
});
},
saveSubscription() {
return axios.post("/billings/subscriptions")
.then(response => {
this.paidSubscriptionId = response.data.paidSubscriptionId;
this.stripeClientSecret = response.data.clientSecret;
this.stripePaymentConfirmation = response.data.paymentConfirmation;
})
.catch(error => {
if (error.response.status === 401 || error.response.status === 403) {
location.reload();
}
this.createAlert(saveBillingAddressErrorMsg);
});
},
loadBillingDetails() {
axios.get("/billings")
.then(response => {
const responseData = response.data;
if(responseData) {
this.paidCustomerId = responseData.paidCustomerId;
this.paidSubscriptionId = responseData.paidSubscriptionId;
this.billingAddressStreet = responseData.billingAddressStreet;
this.billingAddressLine2 = responseData.billingAddressLine2;
this.billingAddressCity = responseData.billingAddressCity;
this.billingAddressPostalCode = responseData.billingAddressPostalCode;
this.billingAddressCountry = responseData.billingAddressCountry;
this.billingEmail = responseData.billingEmail;
if(responseData.paymentMethod) {
this.paymentMethod = responseData.paymentMethod;
}
}
})
.catch(error => {
if(error.response) {
if(error.response.status === 401 || error.response.status === 403) {
location.replace("/");
}
}
})
.then(()=> {
this.billingDataChecking = false;
})
},
onProceedWithPaymentClicked() {
this.$validator.validateAll()
.then(isFormValid => {
if (isFormValid) {
this.doProceedWithPayment();
}
});
},
async doProceedWithPayment() {
this.paymentMethodsSubmitting = true;
this.disableStripeCardElement(true);
//this.disableStripeIbanElement(true);
await this.saveCustomer();
if(this.paidCustomerId) {
await this.saveSubscription();
if(this.paymentMethod === PAYMENT_METHODS_CREDIT_CARD) {
if (this.stripeClientSecret) {
if (this.stripePaymentConfirmation) {
await this.confirmCardPaymentWithStripe(this.stripeClientSecret);
} else {
await this.confirmCardSetupWithStripe(this.stripeClientSecret);
}
}
} else {
location.replace("/watchers");
}
if(this.paymentMethod === PAYMENT_METHODS_SEPA) {
if (this.stripeClientSecret) {
if (this.stripePaymentConfirmation) {
await this.confirmSepaDebitPayment(this.stripeClientSecret);
} else {
await this.confirmSepaDebitSetup(this.stripeClientSecret);
}
}
} else {
location.replace("/watchers");
}
}
this.paymentMethodsSubmitting = false;
this.disableStripeCardElement(false);
this.disableStripeIbanElement(false);
},
confirmCardPaymentWithStripe(stripeCustomerClientSecret) {
return this.stripe
.confirmCardPayment(stripeCustomerClientSecret, {
payment_method: {
card: this.cardElement,
// billing_details: {
// name: nameInput.value,
// },
}
})
.then((result) => {
if(result.error) {
this.$validator.errors.add({
field: 'creditCard',
msg: result.error.message
});
} else {
// Successful subscription payment
location.replace("/watchers"); //reload page and next step of activation wizard
}
});
},
confirmCardSetupWithStripe(stripeCustomerClientSecret) {
return this.stripe
.confirmCardSetup(stripeCustomerClientSecret, {
payment_method: {
card: this.cardElement,
// billing_details: {
// name: nameInput.value,
// },
}
})
.then((result) => {
if(result.error) {
this.$validator.errors.add({
field: 'creditCard',
msg: result.error.message
});
} else {
// Successful subscription payment
location.replace("/watchers"); //reload page and next step of activation wizard
}
})
},
confirmSepaDebitSetup(stripeCustomerClientSecret) {
return this.stripe
.confirmSepaDebitSetup(stripeCustomerClientSecret, {
payment_method: {
sepa_debit: this.ibanElement,
billing_details: {
name: 'test',
email: this.billingEmail,
},
},
})
.then(function (result) {
// Handle result.error or result.setupIntent
location.replace("/watchers"); //reload page and next step of activation wizard
})
},
confirmSepaDebitPayment(stripeCustomerClientSecret) {
return this.stripe
.confirmSepaDebitPayment(stripeCustomerClientSecret, {
payment_method: {
sepa_debit: this.ibanElement,
billing_details: {
name: 'test',
email: this.billingEmail,
},
},
})
.then(function (result) {
// Handle result.error or result.paymentIntent
location.replace("/watchers"); //reload page and next step of activation wizard
})
},
initStripe() {
this.stripe = Stripe(stripePublishableToken);
},
mountStripeCardElement() {
this.paymentMethod = PAYMENT_METHODS_CREDIT_CARD
const stripeOptions = {
// stripeAccount: this.stripeAccount,
// apiVersion: this.apiVersion,
locale: localeFromCookies ? localeFromCookies : "auto"
};
const style = {
invalid: {
color: "#a94442",
iconColor: "#a94442"
}
};
const createOptions = {
// classes: this.classes,
style: style,
// value: this.value,
hidePostalCode: true
// iconStyle: this.iconStyle,
// hideIcon: this.hideIcon,
// disabled: this.disabled,
};
this.elements = this.stripe.elements(stripeOptions);
this.cardElement = this.elements.create(ELEMENT_TYPE, createOptions);
this.cardElement.mount('#stripe-element-mount-point');
this.cardElement.on('change', this.onStripeCardElementChange);
},
mountSepaElement() {
this.paymentMethod = PAYMENT_METHODS_SEPA
const stripeOptions = {
// stripeAccount: this.stripeAccount,
// apiVersion: this.apiVersion,
locale: localeFromCookies ? localeFromCookies : "auto"
};
const style = {
invalid: {
color: "#a94442",
iconColor: "#a94442"
}
};
const createOptions = {
// classes: this.classes,
style: style,
// value: this.value,
supportedCountries : ['SEPA']
//hidePostalCode: true
// iconStyle: this.iconStyle,
// hideIcon: this.hideIcon,
// disabled: this.disabled
};
this.elements = this.stripe.elements(stripeOptions);
this.ibanElement= this.elements.create('iban', createOptions);
this.ibanElement.mount('#stripe-element-mount-point2');
this.ibanElement.on('change', this.onStripeSepaElementChange);
},
unmountStripeCardElement() {
this.$validator.errors.remove("creditCard");
this.cardElement.unmount();
},
unmountStripeSepaIbanElement() {
this.$validator.errors.remove("ibanSepa");
this.ibanElement.unmount();
},
onStripeCardElementChange(event) {
this.isCardElementEmptyOrInvalid = event.empty;
if (event.error) {
this.isCardElementEmptyOrInvalid = true;
this.$validator.errors.add({
field: 'creditCard',
msg: event.error.message
});
} else {
this.$validator.errors.remove("creditCard");
}
},
onStripeSepaElementChange(event) {
this.isIbanElementEmptyOrInvalid = event.empty;
if (event.error) {
this.isIbanElementEmptyOrInvalid = true;
this.$validator.errors.add({
field: 'ibanSepa',
msg: event.error.message
});
} else {
this.$validator.errors.remove("ibanSepa");
}
},
disableStripeCardElement(disabled) {
this.cardElement.update({disabled: disabled});
},
disableStripeIbanElement(disabled) {
this.ibanElement.update({disabled: disabled});
},
setActiveTab () {
const activeTab = this.$formWizard.attr("active-tab");
if(activeTab) {
const wizard = this.$refs.wizard;
if(wizard) {
const currentActiveTabIndex = wizard.activeTabIndex;
wizard.changeTab(currentActiveTabIndex,activeTab);
wizard.tabs.forEach((tab, index)=>{
if(index < activeTab)
tab.checked = true
});
}
}
},
createAlert(message) {
const alertItem = {
key: new Date().getTime(),
message: message,
type: "danger"
};
this.alerts.push(alertItem);
},
onPageResize() {
if (window.innerWidth > 992) { //media query value
this.wizardStepSize = 'md'
} else {
this.wizardStepSize = 'xs'
}
}
},
computed: {
$formWizard() {
return $("#payment-methods-form-wizard");
},
disabledForm() {
return this.paymentMethodsSubmitting
|| this.billingDataChecking
|| (this.paidCustomerId && this.paidSubscriptionId);
},
disabledPaymentMethod() {
return this.paymentMethodsSubmitting
|| this.billingDataChecking;
},
disableSubmitButton() {
return this.paymentMethodsSubmitting
|| this.billingDataChecking
|| (this.paymentMethod === PAYMENT_METHODS_CREDIT_CARD && this.isCardElementEmptyOrInvalid)
|| (this.paymentMethod === PAYMENT_METHODS_SEPA && this.isIbanElementEmptyOrInvalid)
|| this.errors.any();
},
showInvoiceEBankingPaymentMethod() {
return wearerCountry !== 'CH';
}
},
mounted() {
this.setActiveTab();
this.initStripe();
this.mountStripeCardElement();
this.loadBillingDetails();
},
created() {
window.addEventListener('resize', this.onPageResize)
},
beforeDestroy() {
window.removeEventListener('resize', this.onPageResize)
}
});
<tab-content>
<div>
<div class="row" v-if="paymentMethod == PAYMENT_METHODS_CREDIT_CARD">
<div class="form-group col-xs-12 col-md-6 required" :class="{'has-error': errors.has('creditCard') }">
<label class="control-label">[[#{registration.payment.creditCard}]]</label>
<div class="form-control">
<form id="stripe-element-form" >
<div id="stripe-element-mount-point"></div>
<slot name="stripe-element-errors">
<div id="stripe-element-errors" role="alert"></div>
</slot>
<button ref="submitButtonRef" type="submit" class="hide" ></button>
</form>
</div>
<span v-if="errors.has('creditCard')" class="help-block">
{{ errors.first('creditCard') }}
</span>
</div>
</div>
<div class="row" v-if="paymentMethod == PAYMENT_METHODS_SEPA">
<div class="form-group col-xs-12 col-md-6 required" :class="{'has-error': errors.has('ibanSepa') }">
<label class="control-label">[[#{registration.payment.sepa}]]</label>
<div class="form-control">
<form id="stripe-element-form" >
<div id="stripe-element-mount-point2"></div>
<slot name="stripe-element-errors">
<div id="stripe-element-errors" role="alert"></div>
</slot>
<button ref="submitButtonRef" type="submit" class="hide" ></button>
</form>
</div>
<div id="mandate-acceptance">
By providing your payment information and confirming this payment, you
authorise (A) Smartwatcher Technologies AG and Stripe, our payment service provider
and/or PPRO, its local service provider, to send instructions to your
bank to debit your account and (B) your bank to debit your account in
accordance with those instructions. As part of your rights, you are
entitled to a refund from your bank under the terms and conditions of
your agreement with your bank. A refund must be claimed within 8 weeks
starting from the date on which your account was debited. Your rights
are explained in a statement that you can obtain from your bank. You
agree to receive notifications for future debits up to 2 days before
they occur.
</div>
<span v-if="errors.has('ibanSepa')" class="help-block">
{{ errors.first('ibanSepa') }}
</span>
</div>
</div>
</div>
</tab-content>
Related
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed yesterday.
Improve this question
Creating a react application with nodejs backend power with redux for state management. After i run my application for few renders all the dispatches are running as it supposed to be but after a point...loading is set true forever. I need to re-run the application from commandline and again same issue. would like to know what's the problem.
My UserActions:
import axios from 'axios'
import { BOT_DETAILS_RESET, BOT_LIST_RESET } from '../constants/botConstants'
import { USER_CREATE_FAILURE, USER_CREATE_REQUEST, USER_CREATE_SUCCESS, USER_DELETE_FAILURE, USER_DELETE_REQUEST, USER_DELETE_SUCCESS, USER_DETAILS_FAILURE, USER_DETAILS_REQUEST, USER_DETAILS_RESET, USER_DETAILS_SUCCESS, USER_LIST_FAILURE, USER_LIST_REQUEST, USER_LIST_RESET, USER_LIST_SUCCESS, USER_LOGIN_FAILURE, USER_LOGIN_REQUEST, USER_LOGIN_SUCCESS, USER_LOGOUT, USER_UPDATE_FAILURE, USER_UPDATE_REQUEST, USER_UPDATE_SUCCESS } from "../constants/userConstants"
export const login = (username, password) => async (dispatch) => {
try {
dispatch({
type: USER_LOGIN_REQUEST
})
const config = {
header: {
'Content-Type': 'application/json',
}
}
const { data } = await axios.post('/api/users/login', { username, password }, config)
dispatch({
type: USER_LOGIN_SUCCESS,
payload: data
})
localStorage.setItem('userInfo', JSON.stringify(data))
} catch (error) {
dispatch({
type: USER_LOGIN_FAILURE,
payload: error.response && error.response.data.message ? error.response.data.message : error.message
})
}
}
export const listUsers = () => async (dispatch, getState) => {
try {
dispatch({
type: USER_LIST_REQUEST
})
const { userLogin: { userInfo } } = getState()
const config = {
headers: {
Authorization: `Bearer ${userInfo.token}`
}
}
const { data } = await axios.get(`/api/users`, config)
console.log(data)
dispatch({
type: USER_LIST_SUCCESS,
payload: data.users_list
})
} catch (error) {
const message =
error.response && error.response.data.message
? error.response.data.message
: error.message
if (message === 'Not authorized, token failed') {
dispatch(logout())
}
dispatch({ type: USER_LIST_FAILURE, payload: message })
}
}
export const deleteUser = (username) => async (dispatch, getState) => {
try {
dispatch({
type: USER_DELETE_REQUEST
})
const { userLogin: { userInfo } } = getState()
const config = {
headers: {
Authorization: `Bearer ${userInfo.token}`
}
}
await axios.delete(`/api/users/${username}`, config)
dispatch({
type: USER_DELETE_SUCCESS,
})
} catch (error) {
const message = error.response && error.response.data.message ? error.response.data.message : error.message
if (message === 'Not authorized, token failed') {
dispatch(logout())
}
dispatch({ type: USER_DELETE_FAILURE, payload: message })
}
}
export const createUser = (first_name, last_name, username, password, role) => async (dispatch, getState) => {
const newRole = role.toUpperCase()
console.log(newRole)
try {
dispatch({
type: USER_CREATE_REQUEST
})
const { userLogin: { userInfo } } = getState()
const config = {
headers: {
Authorization: `Bearer ${userInfo.token}`
}
}
const { data } = await axios.post(`/api/users/`, { first_name, last_name, username, password, role: newRole }, config)
console.log(data)
dispatch({
type: USER_CREATE_SUCCESS,
payload: data
})
} catch (error) {
dispatch({ type: USER_CREATE_FAILURE, payload: error.response && error.response.data.message ? error.response.data.message : error.message })
}
}
export const getUserDetails = (username) => async (dispatch, getState) => {
try {
dispatch({
type: USER_DETAILS_REQUEST
})
const { userLogin: { userInfo } } = getState()
const config = {
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${userInfo.token}`
}
}
const { data } = await axios.get(`/api/users/${username}`, config)
dispatch({
type: USER_DETAILS_SUCCESS,
payload: data
})
} catch (error) {
const message =
error.response && error.response.data.message
? error.response.data.message
: error.message
if (message === 'Not authorized, token failed') {
dispatch(logout())
}
dispatch({ type: USER_DETAILS_FAILURE, payload: message })
}
}
export const updateUser = (user, username) => async (dispatch, getState) => {
try {
dispatch({
type: USER_UPDATE_REQUEST
})
const { userLogin: { userInfo } } = getState()
const config = {
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${userInfo.token}`
}
}
const { data } = await axios.put(`/api/users/${username}`, user, config)
dispatch({
type: USER_UPDATE_SUCCESS,
})
dispatch({
type: USER_DETAILS_SUCCESS,
payload: data
})
} catch (error) {
const message =
error.response && error.response.data.message
? error.response.data.message
: error.message
if (message === 'Not authorized, token failed') {
dispatch(logout())
}
dispatch({ type: USER_UPDATE_FAILURE, payload: message })
}
}
export const logout = () => (dispatch) => {
localStorage.removeItem('userInfo')
dispatch({ type: USER_DETAILS_RESET })
dispatch({ type: BOT_DETAILS_RESET })
dispatch({ type: USER_LIST_RESET })
dispatch({ type: BOT_LIST_RESET })
dispatch({ type: USER_LOGOUT })
document.location.href = '/'
}
My user Reducer:
export const userLoginReducer = (state = {}, action) => {
switch (action.type) {
case USER_LOGIN_REQUEST:
return { loading: true }
case USER_LOGIN_SUCCESS:
return { loading: false, userInfo: action.payload }
case USER_LOGIN_FAILURE:
return { loading: false, error: action.payload }
case USER_LOGOUT:
return {}
default:
return state
}
}
export const userListReducer = (state = { users: [] }, action) => {
switch (action.type) {
case USER_LIST_REQUEST:
return { loading: true, }
case USER_LIST_SUCCESS:
return { loading: false, users: action.payload }
case USER_LIST_FAILURE:
return { loading: false, error: action.payload }
case USER_LIST_RESET:
return { users: [] }
default:
return state
}
}
export const userDeleteReducer = (state = {}, action) => {
switch (action.type) {
case USER_DELETE_REQUEST:
return { loading: true, }
case USER_DELETE_SUCCESS:
return { loading: false, success: true }
case USER_DELETE_FAILURE:
return { loading: false, error: action.payload }
default:
return state
}
}
export const userCreateReducer = (state = {}, action) => {
switch (action.type) {
case USER_CREATE_REQUEST:
return { loading: true, }
case USER_CREATE_SUCCESS:
return { loading: false, success: true, user: action.payload }
case USER_CREATE_FAILURE:
return { loading: false, error: action.payload }
case USER_CREATE_RESET:
return {}
default:
return state
}
}
export const userDetailsReducer = (state = { user: {} }, action) => {
switch (action.type) {
case USER_DETAILS_REQUEST:
return { ...state, loading: true, }
case USER_DETAILS_SUCCESS:
return { loading: false, user: action.payload }
case USER_DETAILS_FAILURE:
return { loading: false, error: action.payload }
case USER_DETAILS_RESET:
return { user: {} }
default:
return state
}
}
export const userUpdateReducer = (state = { user: {} }, action) => {
switch (action.type) {
case USER_UPDATE_REQUEST:
return { loading: true, }
case USER_UPDATE_SUCCESS:
return { loading: false, success: true }
case USER_UPDATE_FAILURE:
return { loading: false, error: action.payload }
case USER_UPDATE_RESET:
return { user: {} }
default:
return state
}
}
My Login Component:
import React, { useEffect, useState } from 'react'
import COLOR from '../const/colors'
import { Row, Col, Space, Typography, Form, Button, Input } from 'antd'
import { Link, useNavigate } from 'react-router-dom'
import { useDispatch, useSelector } from 'react-redux'
import { login } from '../redux/actions/userActions'
const { Title } = Typography
const LoginPage = () => {
const dispatch = useDispatch()
const navigate = useNavigate()
const [input, setInput] = useState({
username: '',
password: ''
})
const userLogin = useSelector(state => state.userLogin)
const { loading, userInfo, error } = userLogin
const handleChange = (e) => {
const { name, value } = e.target
setInput({
...input,
[name]: value
})
}
const onFinishFailed = (errorInfo) => {
console.log('Failed:', errorInfo);
};
const handleSubmit = () => {
dispatch(login(input.username, input.password))
console.log('login request dispatched')
}
useEffect(() => {
if (userInfo) {
navigate('/auth')
}
}, [userInfo, dispatch, navigate])
return (
<div>
{loading && <p>Loading...</p>}
{error && <p>{error}</p>}
<Row gutter={[0, { xs: 8, sm: 16, md: 24, lg: 32 }]}>
<Col className="gutter-row"
xs={{ span: 20, offset: 2 }}
sm={{ span: 16, offset: 4 }}
md={{ span: 14, offset: 6 }}
lg={{ span: 14, offset: 6 }}
xl={{ span: 6, offset: 9 }}>
<Row justify="center" align="middle">
<Col span={24}>
<Space direction="vertical" className="container-login">
<Title level={4}
style={{
color: COLOR.PRIMARY,
textAlign: "center",
fontWeight: "bold",
marginTop: 20,
}}>
Sign in to Damex CWC
</Title>
<br />
<Form
name="normal-login"
className='login-form'
onFinish={handleSubmit}
onFinishFailed={onFinishFailed}
autoComplete="off"
>
{/* <Spin
spinning={loading}
size="large"
indicator={
<LoadingOutlined style={{ color: "#00ff6a" }} />
}
> */}
<Form.Item
rules={[
{
required: true,
message: "Please input your Username"
},
{
type: "username",
message: "Please enter a valid email address",
},
]}
>
<Input
name='username'
size="large"
placeholder={"Username"}
value={input.username}
onChange={handleChange}
/>
</Form.Item>
<Form.Item
rules={[
{
required: true,
message: "Please input your Password",
},
]}
>
<Input.Password
name='password'
size="large"
placeholder={"Password"}
value={input.password}
onChange={handleChange}
/>
</Form.Item>
{/* <Form.Item
name="remember"
valuePropName="checked"
>
<Checkbox>Remember me</Checkbox>
</Form.Item> */}
<Form.Item
>
<Button
block
type="primary"
htmlType="submit"
className="login-form-button"
>
Sign In
</Button>
</Form.Item>
{/* </Spin> */}
</Form>
</Space>
</Col>
</Row>
<br />
<Row
justify="center" align="middle"
>
<Button
className="link-login"
type="link"
>
<Link className='link-login' to={"/forgotpassword"}>
Forgot password?
</Link>
</Button>
<span>{"‧"}</span>
<Button
className="link-login"
type="link"
>
<Link className='link-login' to={"#"}
onClick={(e) => {
window.location = "mailto:info#damex.io";
e.preventDefault();
}}
>
Don't have an account?
</Link>
</Button>
<span>{"‧"}</span>
<Button type="link" className="link-login" >
<Link
className="link-login"
to="https://www.damex.io/privacy"
target="_blank"
rel="noopener noreferrer"
>
Privacy Policy
</Link>
</Button>
</Row>
<Row justify="center" align="middle">
<Link to="mailto:support#damex.io?subject=Issue with 2FA (Damex Business)">
<Button type="link" className="link-login">
Have an issue with 2-factor authentication?
</Button>
</Link>
</Row>
</Col>
</Row>
</div>
)
}
export default LoginPage
My userList page:
import { Button, Col, Row } from 'antd'
import React, { useEffect, useState, } from 'react'
import COLOR from '../const/colors'
import { useDispatch, useSelector } from 'react-redux'
import { useNavigate } from 'react-router-dom'
import { deleteUser, listUsers } from '../redux/actions/userActions'
import Table from '../components/Table'
import ModalDelete from '../components/ModalDelete'
const UserListPage = () => {
const dispatch = useDispatch()
const navigate = useNavigate()
const userLogin = useSelector(state => state.userLogin)
const { userInfo } = userLogin
console.log(userInfo.role)
const userList = useSelector(state => state.userList)
const { loading, error, users } = userList
const userDelete = useSelector(state => state.userDelete)
const { success: successDelete } = userDelete
const [selectedUser, setSelectedUser] = useState(null);
const [modalVisible, setModalVisible] = useState(false);
const handleCreate = () => {
navigate(`/auth/users/create`)
};
useEffect(() => {
if ((userInfo && (userInfo.role === 'ADMIN' || userInfo.role === 'TRADING'))) {
dispatch(listUsers())
} else {
navigate('/')
}
}, [dispatch, userInfo, successDelete, navigate])
// handle delete button click
const handleDelete = (user) => {
setSelectedUser(user);
setModalVisible(true);
};
// handle modal confirm delete
const handleConfirmDelete = () => {
// dispatch delete user by username action
dispatch(deleteUser(selectedUser.username));
setModalVisible(false);
};
// handle modal cancel delete
const handleCancelDelete = () => {
setSelectedUser(null);
setModalVisible(false);
};
return (
<div style={{ padding: '0 1rem' }}>
{userInfo && (userInfo.role === 'ADMIN' || userInfo.role === 'TRADING') ? (
<>
<div style={{ color: COLOR.SECONDARY_TEXT, marginBottom: '32px', marginTop: '24px' }}>
Users
</div>
<Row style={{ margin: '24px' }}>
<Col offset={20}>
<Button type='primary' onClick={handleCreate} className='create-btn'>Create User</Button>
</Col>
</Row>
<Table users={users} handleDelete={handleDelete} />
{loading && <p>Loading...{loading}</p>}
{error && <p>{error}</p>}
</>
) :
<p>Not Authorized to view this</p>
}
<ModalDelete
user={selectedUser}
isModalOpen={modalVisible}
onConfirm={handleConfirmDelete}
onCancel={handleCancelDelete}
/>
</div>
)
}
export default UserListPage
Table component:
import React from 'react'
import { Table as AntdTable, Button, Space } from 'antd'
import { useNavigate } from 'react-router-dom';
const Table = ({ users, bots, handleDelete }) => {
console.log(users && users)
console.log(bots && bots)
const navigate = useNavigate()
const handleEdit = (record) => {
users && navigate(`/auth/users/${record.username}`);
bots && navigate(`/auth/bots/${record.bot_id}`);
};
let columns = [];
if (users) {
columns = [{ key: 'username', title: 'Username', dataIndex: 'username' }, { key: 'role', title: 'Role', dataIndex: 'role' }]
} else if (bots) {
columns = [
{ key: 'bot_id', title: 'BotID', dataIndex: 'bot_id' },
{ key: 'exchange', title: 'Exchange', dataIndex: 'exchange' },
{ key: 'strategy', title: 'Strategy', dataIndex: 'strategy' },
{ key: 'trading_pair', title: 'Trading Pair', dataIndex: 'trading_pair' },
]
}
const actionColumn = {
title: 'Action',
dataIndex: 'id',
key: 'action',
render: (id, record) => (
<Space size={'large'}>
<Button type="primary" onClick={() => handleEdit(record)}>Edit</Button>
<Button type="primary" className='danger-btn' onClick={() => handleDelete(record)}>Delete</Button>
</Space>
),
};
const tableColumns = [
...columns,
actionColumn,
];
return (
<AntdTable rowKey={record => users ? record.username : record.bot_id} dataSource={users ? users : bots} columns={tableColumns} />
)
}
export default Table
I couldn't figure out what's wrong...if it's problem with front end it shouldn't be working with starting few renders of application right. In case backend controller here it is:
import asyncHandler from 'express-async-handler'
import generateToken from '../utils/generateToken.js'
import pool from '../config/db.js'
import bcrypt from 'bcryptjs'
//Auth user & get token
// Post /api/users/login
// Public route
export const authUser = asyncHandler(async (req, res) => {
console.log('logging...')
const { username, password } = req.body
try {
const query = `SELECT * FROM users WHERE username = '${username}';`
const client = await pool.connect()
const user = await client.query(query);
client.release();
if (user.rows.length !== 0) {
const user_info = user.rows[0];
const matchedPassword = await bcrypt.compare(password, user_info.password);
if (matchedPassword) {
res.status(200).json({
username: user_info.username,
role: user_info.role,
token: generateToken(user_info.username)
});
} else {
res.status(401).json({ message: 'Invalid email or password! Please, try again' });
}
} else {
res.status(401).json({ message: 'Invalid email or password! Please, try again' });
}
} catch (error) {
res.status(500).json({ message: 'Server error has occurred', error: error })
}
})
// //get user profile
// // GET /api/users/profile
// // Private route
export const getUserProfile = asyncHandler(async (req, res) => {
try {
const user_profile_query = `SELECT username, first_name, last_name, role FROM users WHERE username = '${req.username}';`
const client = await pool.connect();
const results = await client.query(user_profile_query);
client.release();
if (results.rows.length !== 0) {
const user_profile = results.rows[0]
res.status(200).json({ user_profile });
}
else {
res.status(404).json({ message: `User Not Found for ${req.username}` });
}
} catch (error) {
res.status(500).json({ message: `Internal Server Error while fetching user - ${req.username}.`, error: error })
}
})
// //put update user password
// // PUT /api/users/profile
// // Private route
export const updateUserPassword = asyncHandler(async (req, res) => {
try {
const updatedHashedPassword = await bcrypt.hash(req.body.password, 10);
const user_password_update_query = `UPDATE users SET password = '${updatedHashedPassword}' WHERE username = '${req.username}' RETURNING username, first_name, last_name, role;`
const client = await pool.connect();
const results = await client.query(user_password_update_query);
client.release();
if (results.rows.length !== 0) {
const user_password_update = results.rows[0]
res.status(200).json({ user_password_update });
}
else {
res.status(404).json({ message: `User Not Found for ${req.username}` });
}
} catch (error) {
res.status(500).json({ message: `Internal Server Error while updating password for user - ${req.username}.`, error: error })
}
})
//Register a new user
// Post /api/users
// Private/Admin route
export const registerUser = asyncHandler(async (req, res) => {
console.log('registering...')
try {
const { username, first_name, last_name, password, role } = req.body
const user_exist_query = `SELECT * FROM users WHERE username = '${username}';`
const client = await pool.connect();
const user_exists = await client.query(user_exist_query);
if (user_exists.rows.length !== 0) {
return res.status(400).json({ message: `User - ${username} already exists.` })
}
const hashedPassword = await bcrypt.hash(password, 10);
const user_register_query = 'INSERT INTO users (username, first_name, last_name, password, role) VALUES ($1, $2, $3, $4, $5) RETURNING username, first_name, last_name, role;';
const values = [username, first_name, last_name, hashedPassword, role]
const results = await client.query(user_register_query, values);
client.release();
if (results.rows.length !== 0) {
const user_info = results.rows[0];
res.status(201).json({
username: user_info.username,
role: user_info.role,
})
} else {
res.status(400).json({ message: `Invalid user data for registering user - ${username}` })
}
} catch (error) {
res.status(500).json({ message: `Internal Server Error while registering user - ${username}.`, error: error })
}
})
//get all users
// GET /api/users
// Private/admin route
export const getUsers = asyncHandler(async (req, res) => {
try {
const users_list_query = `SELECT username, first_name, last_name, role FROM users;`
const client = await pool.connect();
const results = await client.query(users_list_query);
client.release();
if (results.rows.length !== 0) {
const users_list = results.rows
res.status(200).json({ users_list });
}
else {
res.status(404).json({ message: `No users found.` });
}
} catch (error) {
res.status(500).json({ message: `Internal Server Error while fetching users list}.`, error: error })
}
})
//delete a user by id
// delete /api/users/:id
// Private/admin route
export const deleteUser = asyncHandler(async (req, res) => {
try {
const user_delete_query = `DELETE FROM users WHERE username = '${req.params.username}';`
const client = await pool.connect();
const user_delete = await client.query(user_delete_query);
client.release();
if (user_delete.rowCount !== 0) {
res.status(200).json({ message: 'User removed from Database successfully.' });
}
else {
res.status(404).json({ message: `User Not Found for ${req.params.username}` });
}
} catch (error) {
res.status(500).json({ message: `Internal Server Error while deleting user - ${req.params.username}.`, error: error })
}
})
Wondering how to debug (Or where to get started) on my current Axios issue. I have a Next.js project (12.3) and have an axios interceptor hook that handles all of my internal requests. The interceptor works on every action /on every page however this certain page /me/settings/password&security/email/verify.
This specific action is being used on several other pages with no issue. Evenb copying the entire page from a working one freezes the tab. It hangs in preflight for a few minutes then I get an iteration of [Violation] 'submit' handler took <N>ms.
Here is my entire page:
export default function VerifyEmailForm() {
const router = useRouter();
const user: UserState = useSelector((root: RootState) => root.user);
const [apiError, setApiError] = useState("");
const [schema, setSchema] = useState<any>();
const [resubmit, setResubmit] = useState(false);
const [updateEmail, setUpdateEmail] = useState(false);
const [submitSuccsess, setSubmitSuccess] = useState(false);
const [dispatchAxios, { success, loading, error }] = useAxios();
const initialValues = {
email: user?.email ? user?.email : "",
};
const handleSubmit = async (values: any) => {
if (!user.email || updateEmail) {
await dispatchAxios(
updateUser({
action: [
{
key: "email",
value: values?.email,
},
],
})
);
}
await dispatchAxios(requestEmailVerification(values?.email)).then((res) => {
if (res?.data?.error) {
setApiError(res?.data?.error);
} else {
setResubmit(true);
setSubmitSuccess(true);
}
});
};
useEffect(() => {
const yup = import("yup").then((yup) =>
setSchema(
yup.object().shape({
email: yup
.string()
.email("Must be a valid email")
.required("Email is required"),
})
)
);
}, []);
useEffect(() => {
if (error && !loading) {
setApiError(error?.message);
}
}, [loading, success, error]);
return (
<Formik
validationSchema={schema}
initialValues={initialValues}
onSubmit={handleSubmit}
validateOnBlur={true}
>
{({ handleSubmit, setFieldValue, errors, values, touched, isValid }) => {
return (
<Form
onSubmit={handleSubmit}
onBlur={() =>
setTimeout(() => {
setApiError("");
}, 200)
}
>
<div className="flex flex-wrap gap-9 pb-2.5 ">
{!user.email || updateEmail ? (
<div className="w-full relative">
<div className="relative w-full">
<Field
styles="bg-transparent text-center"
name="email"
type="email"
placeholder="Email address"
component={InputField}
error={errors.email}
/>
{values.email && (
<button
type="button"
onClick={() => setFieldValue("email", "")}
className={`flex items-center justify-center bg-background-secondary p-0.5 rounded-full w-4 h-4 absolute bottom-2 ${
errors.email ? "right-8 " : "right-2.5 "
}`}
>
<span className="sr-only">Clear Field</span>
<XMarkIcon
className="text-deactive w-2.5 "
aria-hidden="true"
/>
</button>
)}
</div>
<InputError
error={errors.email as string}
touched={touched?.email as boolean}
styles={"flex justify-center"}
/>
</div>
) : (
<></>
)}
<div className="w-full flex flex-col justify-center items-center">
<Button
type="submit"
disabled={!isValid}
classes={`w-full text-white p-3 rounded-2xl mx-8 ${
errors.email || !values.email.length
? "border-transparent text-deactive"
: "border-2 border-primary"
}`}
label={resubmit ? "Resend Verification" : "Verify"}
/>
{user.email && (
<Button
type="button"
classes={`w-full text-white p-3 rounded-2xl mx-8 border-transparent`}
label="Update Email"
onClick={() => setUpdateEmail(true)}
/>
)}
<Button
type="button"
classes={`w-full text-white p-3 rounded-2xl mx-8 border-transparent`}
label="Cancel"
onClick={() => router.back()}
/>
</div>
</div>
<ErrorAlertModal errors={apiError ? [apiError] : undefined} />
<SuccessAlertModal success={submitSuccsess} />
</Form>
);
}}
</Formik>
);
}
And Here is my Axios hook:
export function useAxios(
internal: boolean = true
): [DispatchAxios, IAxiosState] {
const { data: session, status } = useSession();
const dispatch = useDispatch();
const isMounted = useRef(true);
const user: UserState = useSelector((root: RootState) => root.user);
const env = process.env.NODE_ENV;
const { logoutUser } = useLogout();
const client = axios.create({
// baseURL: process.env.API_URL,
});
client.interceptors.request.use((config: AxiosRequestConfig) => {
config.headers = config.headers || {};
if (!config.headers["Content-Type"]) {
config.headers["Content-Type"] = "application/json";
}
if (!config.headers["Ocp-Apim-Subscription-Key"] && env === "production") {
config.headers[
"Ocp-Apim-Subscription-Key"
] = `${process.env.NEXT_PUBLIC_API_GATEWAY_KEY}`;
}
// Internal requests need tokens and other parameters added in.
if (internal) {
if (session?.accessToken) {
config.headers.Authorization = `Bearer ${session.accessToken}`;
}
}
return config;
});
const [state, setState] = useState<IAxiosState>({
loading: false,
error: null,
response: null,
success: false,
});
const dispatchAxios: DispatchAxios = useCallback(
(params: IAxiosAction) => {
const action: IAxiosAction = {
type: params.type,
config: params.config,
batchOrder: params.batchOrder || [],
};
if (params.data) {
action.data = params.data;
}
dispatch({
...action,
loading: true,
});
setState({
...state,
error: null,
response: null,
loading: true,
success: false,
});
return client(params.config)
.then((response: AxiosResponse) => {
// dispatch must come before setState
if (response.data.err === "INVALID TOKEN") {
throw action;
}
dispatch({
...action,
response,
loading: false,
});
if (isMounted.current) {
setState({
...state,
loading: false,
success: true,
error: null,
response,
});
}
return {
success: true,
error: null,
response,
};
})
.catch((error: AxiosError) => {
const originalReq = error.config;
// Token refresh failed, log user out.
if (
originalReq?.url ===
process.env.NEXT_PUBLIC_AUTH_REFRESH_API_ENDPOINT
) {
// logoutUser();
}
// dispatch must come before setState
dispatch({
...action,
error,
loading: false,
success: false,
});
if (isMounted.current) {
setState({
...state,
loading: false,
success: false,
response: null,
error,
});
}
return {
success: false,
response: null,
error,
};
});
},
[isMounted, dispatch, state, client]
);
useEffect(() => {
isMounted.current = true;
return () => {
isMounted.current = false;
};
}, [state]);
return [dispatchAxios, state];
}
The updateUser action is being used in several other parts of the app as the user is able to change and update parts of their profile. As you can see the email is being targeted in this instance. We use this same request in another page /me/settings/password&security/email and it resolves just fine no freezing no hanging. Im convin
It was my own import of moment.js. Dynamically importing it in a wrapping component.
Somehow it cased the issue not sure why. :)
On my application i'm using Reduxjs/toolkit for state management and TypeScript for type safety.
My backend were wrote in Node.js with MongoDB.
I have implemented pagination for one slice/component, and i know that is not the best solution and i want to improve it and make reusable for other slices.
Could you help me with that? Give me some hints?
Below is my current pagination implementation:
// CategorySlice
interface InitialState {
categories: ICategory[];
isFetching: boolean;
errorMessage: string | null;
// to refactor
currentPage: number;
itemsPerPage: number;
totalResults: number;
}
const initialState: InitialState = {
categories: [],
isFetching: false,
errorMessage: '',
// to refactor
currentPage: 1,
itemsPerPage: 9,
totalResults: 0,
};
export const fetchCategories = createAsyncThunk<
{ data: ICategory[]; totalResults: number },
number
>('category/fetchCategories', async (currentPage, { rejectWithValue }) => {
try {
const accessToken = getToken();
if (!accessToken) rejectWithValue('Invalid token');
const config = {
headers: { Authorization: `Bearer ${accessToken}` },
};
const response: IApiResponse<ICategoryToConvert[]> = await api.get(
`/categories?page=${currentPage}&limit=9&isPrivate[ne]=true`,
config
);
const data = response.data.data;
const convertedData = data.map(e => {
return {
id: e._id,
name: e.name,
image: e.image,
};
});
return {
totalResults: response.data.totalResults,
data: convertedData,
};
} catch (error) {
removeToken();
return rejectWithValue(error);
}
});
export const categorySlice = createSlice({
name: 'category',
initialState,
reducers: {
setNextPage(state, { payload }) {
state.currentPage = payload;
},
},
extraReducers: builder => {
builder.addCase(fetchCategories.pending, state => {
state.isFetching = true;
state.errorMessage = null;
});
builder.addCase(fetchCategories.fulfilled, (state, action) => {
state.categories = action.payload.data;
state.isFetching = false;
state.totalResults = action.payload.totalResults;
});
builder.addCase(fetchCategories.rejected, state => {
state.isFetching = false;
state.errorMessage = 'Problem with fetching categories 🐱👤';
});
},
});
// Category Page
const CategoryPage = () => {
const dispatch = useAppDispatch();
const { currentPage } = useAppSelector(state => state.category);
useEffect(() => {
dispatch(fetchCategories(currentPage));
}, [dispatch, currentPage]);
return (
<ContainerWrapper>
<CategoryList />
</ContainerWrapper>
);
};
export default CategoryPage;
Inside CategoryPage
I'm passing those properties from state selector.
<Pagination
currentPage={currentPage}
itemsPerPage={itemsPerPage}
paginate={(n: number) => dispatch(categoryActions.setNextPage(n))}
totalItems={totalResults}
/>
And finally PaginationComponent
interface IProps {
itemsPerPage: number;
totalItems: number;
paginate: (numb: number) => void;
currentPage: number;
}
const Pagination = ({ itemsPerPage, totalItems, paginate, currentPage }: IProps) => {
const numberOfPages = [];
for (let i = 1; i <= Math.ceil(totalItems / itemsPerPage); i++) {
numberOfPages.push(i);
}
return (
<nav className={styles['pagination']}>
<ul className={styles['pagination__list']}>
{numberOfPages.map(number => {
return (
<li
key={number}
className={`${styles['pagination__item']} ${
currentPage === number && styles['pagination__item--active']
}`}
onClick={() => paginate(number)}
>
<div className={styles['pagination__link']}>{number}</div>
</li>
);
})}
</ul>
</nav>
);
};
export default Pagination;
I am trying to upload an image to my nodejs api/service using FileReader, the file is uploaded successfully but the page gets reloaded.
// html part
import React, { useState, useEffect, useCallback } from "react";
import { withRouter, useParams, useHistory } from "react-router-dom";
import { Card, Button } from "react-bootstrap";
import Page from "./Page";
import TitleBar from "./TitleBar";
import SingleGalleryRow from "./SingleGalleryRow";
import CenteredPopup from "../common/CenteredPopup";
import { MdDelete,MdOutlineCrop,MdOutlineCircle } from "react-icons/md";
import Cropper from "react-easy-crop";
import getCroppedImg from "../shared/util/cropImage";
function EditImage{breadcrumb,pageName,setLoader,showToast}) {
const [list, setList] = useState([]);
const history = useHistory();
//const [files, setFiles] = useState([]);
const [umodalShow, setUModalShow] = useState(false);
const [row, setRow] = useState({
ImageName: "",
ImageServerPath: "",
ImageFullUrl: "",
ImageUrl: "",
SiteId: 1,
CreatedBy: 1,
CreatedOn: "",
Id: 0,
Name: "",
IsDeleted: 0,
IsVisible: 1,
ModifiedBy: 1,
ModifiedOn: "",
});
const [isSubmitClicked, setIsSubmitClicked] = useState(false);
const [IsDisabled, setIsDisabled] = useState(false);
const [nameError, setNameError] = useState("");
const [validated, setValidated] = useState(0);
const { id } = useParams();
let content = "";
const [detail, setDetail] = useState({
Content: "",
CreatedBy: 1,
CreatedOn: "",
Id: 0,
IsDeleted: 0,
IsVisible: 1,
ModifiedBy: 1,
ModifiedOn: "",
Name: "",
SiteId: 1,
});
useEffect(() => {
let abortController = new AbortController();
//let isComponentMounted = true;
const fetchData = async () => {
// debugger;
try {
setLoader(true);
let apiUrl = process.env.REACT_APP_IMAGEGALLERYAPI + "/getbyId/" + id;
const response = await fetch(apiUrl, {
signal: abortController.signal,
});
const json = await response.json();
const resultPages = json ? json.list : "";
if (resultPages && resultPages.length > 0) {
//debugger;
let firstRow = resultPages[0];
let isEnabled = false;
if (firstRow.IsVisible === 1 || firstRow.IsVisible === true) {
isEnabled = true;
}
let newDetailPage = {
Id: firstRow.Id,
Name: firstRow.Name,
Content: firstRow.Content,
IsVisible: isEnabled,
IsDeleted: firstRow.IsDeleted,
SiteId: firstRow.SiteId,
CreatedBy: firstRow.CreatedBy,
CreatedOn: firstRow.CreatedOn,
ModifiedBy: firstRow.ModifiedBy,
ModifiedOn: firstRow.ModifiedOn,
};
setDetail(newDetailPage);
let itemRows = [];
for (let item of resultPages) {
let row = {
Id: item.DetailId,
ImageSliderId: item.ImageSliderId,
ImageName: item.ImageName,
ImageServerPath: item.ImageServerPath,
ImageFullUrl: item.ImageFullUrl,
ImageUrl: item.ImageUrl,
ImageType: item.ImageType,
ImageSize: item.ImageSize,
IsVisible: item.DetIsVisible,
IsDeleted: item.DetIsDeleted,
SiteId: item.DetSiteId,
CreatedBy: item.DetCreatedBy,
CreatedOn: item.DetCreatedOn,
ModifiedBy: item.DetModifiedBy,
ModifiedOn: item.DetModifiedOn,
};
if (item.ImageFullUrl) {
itemRows.push(row);
}
}
if (itemRows && itemRows.length > 0) {
setList(itemRows);
}
}
setLoader(false);
//abortController = null;
} catch (err) {
setLoader(false);
if (err.name === "AbortError") {
// Handling error thrown by aborting request
}
}
};
fetchData();
// Side-effect logic...
return () => {
// Side-effect cleanup
abortController.abort();
console.log("unmounting...");
};
}, []);
const [imageCropWidth, setImageCropWidth] = useState(100);
const [imageCropHeight, setImageCropHeight] = useState(100);
const [crop, setCrop] = useState({ x: 0, y: 0 });
const [zoom, setZoom] = useState(1);
const [aspect, setAspect] = useState(1);
const [croppedAreaPixels, setCroppedAreaPixels] = useState(null);
const [croppedImage, setCroppedImage] = useState(null);
const [cropSize, setCropSize] = useState({ width: 100, height: 100 });
const [Image, setImage] = useState("");
const [rotation, setRotation] = useState(0);
const [cropShape,setCropShape] = useState('rect');
const onSaveImage = async (e) => {
//debugger;
if (validate() < 0) {
e.preventDefault();
return -1;
}
try {
const croppedImage = await getCroppedImg(Image, croppedAreaPixels, rotation);
setCroppedImage(croppedImage);
setImage(croppedImage);
if (row) {
row.ImageFullUrl = croppedImage;
}
} catch (err) {
setLoader(false);
showToast("Error!", "Password couldn't be saved");
} finally {
setUModalShow(false);
}
};
const showImageForCrop = (e) => {
//debugger;
let localSrc = e.ImageFullUrl; //e.target.src;
setImage(localSrc);
setUModalShow(true);
setCropSize({width:(imageCropWidth ? parseInt(imageCropWidth):0),
height:(imageCropHeight ? parseInt(imageCropHeight):0)});
if (list && list.length > 0) {
const selectedIndex = list.findIndex((item) => item.Id === e.Id);
if (selectedIndex > -1) {
setRow(list[selectedIndex]);
}
}
};
const singlefileSelectedHandler = (e) => {
debugger;
e.preventDefault();
setIsDisabled(true);
content = "";
if (detail && detail.Content) {
content = detail.Content;
}
//let imageList = "";
const selectedImage = e.target.files[0];
let imageSizeInMBs = selectedImage.size ? selectedImage.size / 1024 / 1024 : 0;
if (selectedImage && imageSizeInMBs > 8) {
setIsDisabled(false);
setRow({
...row,
["ImageName"]: "",
["ImageServerPath"]: "",
["ImageFullUrl"]: "",
["ImageUrl"]: "",
});
showToast("Information!", "Image size can't be greater than 8MB.");
return -1;
}
//setFiles(...e.target.files);
const filename =
selectedImage && selectedImage.name
? new Date().valueOf() +
"_" +
selectedImage.name.replace(/\\/g, "").replace(/ /g, "").replace(/'/g, "").replace(/"/g, "").replace(/`/g, "")
: "";
debugger;
setUModalShow(false);
setCropSize({width:(imageCropWidth ? parseInt(imageCropWidth):0),
height:(imageCropHeight ? parseInt(imageCropHeight):0)});
const reader = new FileReader();
reader.onload = (ev) => {
//debugger;
ev.preventDefault();
let base64 = ev.target.result;
base64 = base64.replace(/^data:image\/(png|jpeg);base64,/, "");
setImage(reader.result);
//imgRef.current = e.target.result;
try {
//debugger;
const settings = {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
image: base64,
filename: filename,
}),
//signal: abortController.signal,
};
//setUModalShow(true);
fetch(process.env.REACT_APP_SERVER_DOMAIN + `/uploaddetail`, settings)
.then((response) => response.json())
.then((response) => {
debugger;
//const uploadedImageUrl = response.data.url; // get saved image url
const uploadedImageUrl = response.fullurl; // get saved image url
const uploadedImageName = response.imagename;
const uploadedServerPath = response.serverpath;
const uploadurl = response.url;
setRow({
...row,
["ImageName"]: uploadedImageName,
["ImageServerPath"]: uploadedServerPath,
["ImageFullUrl"]: uploadedImageUrl,
["ImageUrl"]: uploadurl,
});
if (uploadedImageUrl) {
setIsDisabled(false);
}
content +=
"<div class='slide'><div class='slider-image embed-responsive-16by9' style='background-image: url(" +
'http://localhost:3001/' + uploadedImageUrl +
");'></div></div>";
setDetail({ ...detail, ["Content"]: content });
})
.catch((e) => {
debugger;
console.log(e);
setIsDisabled(false);
});
} catch (error) {
debugger;
setIsDisabled(false);
}
};
reader.readAsDataURL(selectedImage);
};
const validate = () => {
//debugger;
if (detail && !detail.Name) {
setNameError("Name is mandatory.");
return -1;
}
setNameError("");
return 0;
};
const HandleSaveMenu = async (e) => {
//debugger;
e.preventDefault();
const form = e.currentTarget;
if (validate() < 0) {
// e.preventDefault();
return -1;
}
if (form.checkValidity() === false) {
e.stopPropagation();
const formData = new FormData(form);
const validationMessages = Array.from(formData.keys()).reduce((acc, key) => {
acc[key] = form.elements[key].validationMessage;
return acc;
}, {});
alert(validationMessages);
//console.log(validationMessages)
} else if (form.checkValidity() === true) {
//setIsSubmitClicked(true);
//debugger;
content = `<div class="grid"><div class="grid-sizer"></div>`;
if (list && list.length > 0) {
//debugger;
let listcounter = 0;
for (let item of list) {
content += `<div class="grid-item"><img src="${item.ImageFullUrl}" alt="${item.ImageName}" /></div>`;
}
content += `</div>`;
setDetail({ ...detail, ["Content"]: content });
}
//debugger;
const settings = {
method: "PATCH",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
name: detail.Name,
content: content /*detail.Content*/,
imageDetail: list,
siteId: 1,
isVisible: detail.IsVisible === true ? 1 : 0,
isDeleted: false,
CreatedBy: 1,
ModifiedBy: 1,
}),
};
try {
setLoader(true);
let submitUrl = process.env.REACT_APP_IMAGEGALLERYAPI + "/" + id;
const fetchResponse = await fetch(submitUrl, settings);
if (!fetchResponse.ok) {
// => false
//const statusCode = fetchResponse.status; // => 404
const text = await fetchResponse.text();
//console.log(text);
}
const retdata = await fetchResponse.json();
let resultSet = retdata ? retdata.list : "";
setLoader(false);
if (resultSet && resultSet.length > 0 && resultSet[0].affectedRows > 0) {
history.push("/backend/imagegallerylist");
showToast("Information!", "Gallery has been saved successfully.");
}
//return resultSet;
} catch (e) {
setLoader(false);
}
}
setValidated(1);
};
const onAddSide = (e) => {
debugger;
let newImageList = [...list];
if (row.Id && row.Id > 0) {
if (newImageList && newImageList.length > 0) {
const selectedIndex = newImageList.findIndex((item) => item.Id === row.Id);
if (selectedIndex > -1) {
newImageList[selectedIndex] = row;
}
}
} else {
row.Id = Math.max.apply(
Math,
newImageList.map(function (o) {
return o.Id + 1;
})
);
if (!row.Id || (row.Id && row.Id === 0) || row.Id === -Infinity) {
row.Id = 1;
}
newImageList.push(row);
}
setList(newImageList);
setImage("");
setRow({
ImageName: "",
Name: "",
ImageServerPath: "",
ImageFullUrl: "",
ImageUrl: "",
SiteId: 1,
CreatedBy: 1,
CreatedOn: "",
Id: 0,
IsDeleted: 0,
IsVisible: 1,
ModifiedBy: 1,
ModifiedOn: "",
});
};
const onDeleteSlide = (e) => {
//debugger;
const dataToShow = list ? list.filter((item) => item.Id.toString() !== e.target.id) : list;
setList(dataToShow);
};
const onEditSlide = (e) => {
// debugger;
if (list && list.length > 0) {
const selectedIndex = list.findIndex((item) => item.Id.toString() === e.target.id);
if (selectedIndex > -1) {
setRow(list[selectedIndex]);
}
}
};
const onCropperZoomChange =(e) => {
if(e && e.currentTarget && e.currentTarget.value)
{
let lZoomValue = e.currentTarget.value;
if(lZoomValue && lZoomValue < 0.4)
{
//setZoom(0.28);
setZoom(0.4);
}
else if(lZoomValue && lZoomValue > 1)
{
setZoom(1);
}
else{
setZoom(lZoomValue);
}
}
};
return (
<Page breadcrumb={breadcrumb} pageName={pageName} fluid>
<form noValidate validated={validated} onSubmit={HandleSaveMenu} className="user-form">
<Card>
<Card.Header>
<TitleBar title="Edit Gallery" />
</Card.Header>
<Card.Body>
<div className="form-row">
<div className="form-group col-md-4">
<label className="form-label" htmlFor="Name">
Name
</label>
<input
name="Name"
required
id="Name"
className={`form-control ${nameError ? "is-invalid" : ""}`}
value={detail.Name}
onChange={handleFormChanges}
/>
{nameError ? <div className="invalid-feedback">{nameError}</div> : ""}
{!nameError && <div className="valid-feedback">Looks good!</div>}
</div>
<div className="form-group col-md-2">
<label className="form-label" htmlFor="ImageCropWidth">
Width
</label>
<input
name="ImageCropWidth"
id="ImageCropWidth"
className={`form-control`}
value={imageCropWidth}
onChange={(e) => setImageCropWidth(e.target.value)}
/>
</div>
<div className="form-group col-md-2">
<label className="form-label" htmlFor="ImageCropWidth">
Height
</label>
<input
name="ImageCropWidth"
id="ImageCropWidth"
className={`form-control`}
value={imageCropHeight}
onChange={(e) => setImageCropHeight(e.target.value)}
/>
</div>
<div className="form-group col-md-4">
<label className="form-label"> </label>
<div className="custom-control custom-switch">
<input
id="IsVisible"
name="IsVisible"
checked={detail.IsVisible}
onChange={handleFormChanges}
type="checkbox"
className="custom-control-input"
/>
<label title="" htmlFor="IsVisible" className="custom-control-label">
Active
</label>
</div>
</div>
</div>
<SingleGalleryRow
data={row}
setRow={setRow}
onUpload={singlefileSelectedHandler}
onAddSide={onAddSide}
IsDisabled={IsDisabled}
/>
<ul className="slide-list">
{list &&
list.map(function (item, i) {
//debugger;
return (
<li key={i}>
<div className="slide-row">
<div className="img-holder">
<img
className="thumbnail"
src={item.ImageFullUrl}
alt={item.ImageName}
onClick={() => showImageForCrop(item)}
/>
</div>
<div className="btns-holder">
<button
type="button"
id={item.Id}
onClick={onDeleteSlide}
className="btn btn-circle btn-sm btn-danger"
>
<MdDelete />
</button>
</div>
</div>
</li>
);
})}
</ul>
</Card.Body>
<Card.Footer>
<div className="btn-holder text-right">
<Button type="submit" variant="primary" disabled={isSubmitClicked}>
Save
</Button>
</div>
</Card.Footer>
</Card>
</form>
<CenteredPopup
show={umodalShow}
onHide={() => setUModalShow(false)}
title="Crop Image"
content=""
closetext="Close"
savetext="Apply"
onSaveChanges={onSaveImage}
>
<div className="cropimage-container">
<div className="cropimage-tools">
<span className="cropimage-icons"
onClick={(e) => setCropShape('rect')}
>
<MdOutlineCrop />
<span className="cropimage-text">
Crop
</span>
</span>
<span className="cropimage-icons"
onClick={(e) => setCropShape('round')} >
<MdOutlineCircle />
<span className="cropimage-text">
Circle
</span>
</span>
</div>
<div className="crop-container">
<Cropper
image={Image}
crop={crop}
cropSize={cropSize}
cropShape={cropShape}
zoom={zoom || 1}
showGrid={true}
zoomWithScroll={false}
objectFit="horizontal-cover"
onCropChange={setCrop}
onCropComplete={onCropComplete}
onZoomChange={onCropperZoomChange}
/>
</div>
</div>
<div className="form-row ic_rangeslider">
<div className="form-group col-md-12">
<label className="form-label" htmlFor="ZoomBtn">
{zoom || 1}
</label>
<span className="range-slider__wrap">
<input
id="zoomrangeid"
type="range"
className="slider range-slider range-slider--light"
value={zoom || 1}
min={0.4}
max={1}
step={0.1}
aria-labelledby="Zoom"
onChange={onCropperZoomChange}
/>
</span>
</div>
</div>
</CenteredPopup>
</Page>
);
}
export default withRouter(EditImage);
I am trying to fix it from four hours by reading through the articles from google.
I don't want the page to be reloaded/refreshed. My image files are being uploaded successfully and my node js service is returning the result in json.
If you need anything, please ask.
Why my browser is being reloaded/refreshed when my fileUpload is complete?
My client package.json
{
"name": "test1",
"version": "0.1.0",
"private": true,
"dependencies": {
"bootstrap": "^4.6.0",
"faker": "^5.5.3",
"node-sass": "^4.14.1",
"react": "^17.0.2",
"react-bootstrap": "^1.6.1",
"react-dom": "^17.0.2",
"react-easy-crop": "^4.0.0",
"react-icons": "^4.2.0",
"react-router-dom": "^5.2.0",
"react-scripts": "^4.0.3",
"styled-components": "^5.3.0",
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
]
},
},
}
I have app with devices-array which have objects
with device_name and device_id. I'd like to remove specific device depending which device user want's to remove from devices array.
I've already tryied to findIndex and indexOf -methods, but it return's undefined for request. How to access device depending which device user want's to remove?
deviceController.js
'use strict'
import Users from '../models/userModel.js'
deleteDevice: async (req, res) => {
try {
const { device } = req.body.device.device_id
const dev = await Users.findOne({ _id: req.user.id }).populate('device')
const devs = dev.device //devices
const index = devs.findIndex(req.body.device)
const devic = devs.slice(index, 1)
await Users.findOneAndUpdate({ _id: req.user.id }, { device: devic })
res.json({ msg: 'Deleted success' })
} catch (err) {
return res.status(500).json({ msg: err.message })
}
}
export { deviceControl }
import React, { useState, useEffect } from 'react'
import { Link } from 'react-router-dom'
import axios from 'axios'
import { useSelector, useDispatch } from 'react-redux'
import { FontAwesomeIcon } from '#fortawesome/react-fontawesome'
import { faEdit, faTrash } from '#fortawesome/free-solid-svg-icons'
import { Container, Table } from 'react-bootstrap'
import { showErr, showSuccess } from '../utils/notifications/Notification'
import {
fetchAllUsers,
dispatchGetAllUsers
} from '../../redux/actions/usersAction'
import '../../index.css'
//initialize device
const initialState = {
device: { device_name: '', _id: '', device_id: '' },
err: '',
success: ''
}
function AllDevices() {
//authentication
const auth = useSelector((state) => state.auth)
//token
const token = useSelector((state) => state.token)
//set data
const [data, setData] = useState(initialState)
//users
const users = useSelector((state) => state.users)
const { device_name, device_id, err, success } = data
//loading
const [loading, setLoading] = useState(false)
//authentication
const { user, isAdmin } = auth
const dispatch = useDispatch()
const handleChange = (e) => {
const { name, value } = e.target
setData({ ...data, [name]: value, err: '', success: '' })
}
useEffect(() => {
if (isAdmin) {
fetchAllUsers(token).then((res) => {
dispatch(dispatchGetAllUsers(res))
})
}
if (isAdmin || user) {
setData(initialState)
}
}, [token, user, isAdmin, dispatch, loading])
const updateDeviceName = async () => {
try {
if (window.confirm('Are you sure you want to rename this device?')) {
await axios.patch(
`/device/edit/${data.device._id}/${data.device.device_name}`,
{
device: { device_name, device_id }
},
{
headers: { Authorization: token }
}
)
}
setData({ ...data, err: '', success: 'Updated Success!' })
} catch (err) {
setData({ ...data, err: err.response.data.msg, success: '' })
}
}
const handleUpdate = () => {
if (device_name) updateDeviceName(device_name)
}
const handleDelete = async () => {
try {
if (window.confirm('Are you sure you want to delete this device?')) {
setLoading(true)
await axios.patch(
`/device/delete/${data.device}`,
{ device: data.device },
{
headers: { Authorization: token }
}
)
}
setData({ ...data, err: '', success: 'Updated Success!' })
} catch (err) {
setData({ ...data, err: err.response.data.msg, success: '' })
}
}
return (
<>
<h5 className='m-5'>
{' '}
<Link to='/'>
<i className='fas fa-undo-alt'></i>Takaisin
</Link>
</h5>
<Container fluid='sm'>
<div>
{err && showErr(err)}
{success && showSuccess(success)}
{loading && <h3>Loading.....</h3>}
</div>
<Table bordered hover variant='light' responsive>
<tbody>
<tr>
<th>Nimi</th>
<th>Laite</th>
</tr>
{/* Loop through user details */}
{users.map((p) => (
<tr>
<td>
{p.name}
<br />
{p.email}
</td>
<td>
{p.device.map((d) => (
<div>
<div
className='d-flex-inline'
style={{
position: 'relative',
width: '170px'
}}
>
{' '}
<input
type='text'
style={{
fontSize: '16px',
width: '100px'
}}
defaultValue={d.device_name}
name='device_name'
onChange={handleChange}
/>{' '}
<FontAwesomeIcon
style={{
position: 'absolute',
top: '3px',
right: '70px',
zIndex: '2'
}}
icon={faEdit}
title='Edit'
onClick={() => handleUpdate(d.device_name)}
/>{' '}
</div>
<div
className='d-flex'
style={{
position: 'relative',
width: '100px'
}}
>
<input
type='text'
style={{
fontSize: '14px',
width: '200px',
color: '#333'
}}
defaultValue={d.device_id}
disabled
name='device_id'
/>{' '}
<FontAwesomeIcon
style={{
position: 'absolute',
top: '3px',
right: '3px',
zIndex: '2'
}}
icon={faTrash}
title='Trash'
onClick={() => handleDelete(d.device)}
/>{' '}
</div>
</div>
))}
</td>
</tr>
))}
</tbody>
</Table>
</Container>
</>
)
}
export default AllDevices
const deviceSchema = mongoose.Schema({
device_id: { type: Number, required: true },
device_name: { type: String, required: true }
})
const userSchema = new mongoose.Schema(
{
name: {
type: String,
required: [true, 'Please enter your name!']
},
email: {
type: String,
required: [true, 'Please enter your email!']
},
device: [deviceSchema, { type: mongoose.Schema.Types.ObjectId }],
password: {
type: String,
required: [true, 'Please enter your password!']
},
days: { type: Date },
role: {
type: Number,
default: 0 // 0 = user 1= admin 3=visitor
}
},
{
timestamps: true
}
)
const Users = mongoose.model('User', userSchema)
export default Users
import { deviceControl } from '../controllers/deviceController.js'
import express from 'express'
import auth from '../middleware/auth.js'
import authAdmin from '../middleware/authAdmin.js'
const router = express.Router()
router.get('/device', auth, authAdmin, deviceControl.getUsersAllDevices)
router.post('/device', auth, authAdmin, deviceControl.getUsersAllDevices)
router.patch('/device/delete/:id', auth, authAdmin, deviceControl.deleteDevice)
router.put('/add_device', auth, deviceControl.addDevice)
router.patch('/device/edit', auth, deviceControl.updateDeviceName)
export default router
try using JS filter function.
const removeDevice(devices, id) => {
return devices.filter(device => device.id !== id)
}