Convert existing Formik form into multistep - reactjs

I have a basic form opening in a modal, but need to convert this form to a multi-step inside the modal. I just cannot get my head around how this would work. Any help would be greatly appreciated!
Form:
import {FC, useState} from 'react'
import * as Yup from 'yup'
import {useFormik} from 'formik'
import {isNotEmpty, /*toAbsoluteUrl*/} from '../../../../../../_metronic/helpers'
import {initialUser, User} from '../core/_models'
import clsx from 'clsx'
import {useListView} from '../core/ListViewProvider'
import {UsersListLoading} from '../components/loading/UsersListLoading'
import {createUser, updateUser} from '../core/_requests'
import {useQueryResponse} from '../core/QueryResponseProvider'
type Props = {
isUserLoading: boolean
user: User
}
const editUserSchema = Yup.object().shape({
email: Yup.string()
.email('Wrong email format')
.min(3, 'Minimum 3 symbols')
.max(50, 'Maximum 50 symbols')
.required('Email is required'),
name: Yup.string()
.min(3, 'Minimum 3 symbols')
.max(50, 'Maximum 50 symbols')
.required('Name is required'),
})
const AddPropForm: FC<Props> = ({user, isUserLoading}) => {
const {setItemIdForUpdate} = useListView()
const {refetch} = useQueryResponse()
const [userForEdit] = useState<User>({
...user,
avatar: user.avatar || initialUser.avatar,
role: user.role || initialUser.role,
position: user.position || initialUser.position,
name: user.name || initialUser.name,
email: user.email || initialUser.email,
})
const cancel = (withRefresh?: boolean) => {
if (withRefresh) {
refetch()
}
setItemIdForUpdate(undefined)
}
// const blankImg = toAbsoluteUrl('/media/svg/avatars/blank.svg')
// const userAvatarImg = toAbsoluteUrl(`/media/${userForEdit.avatar}`)
const formik = useFormik({
initialValues: userForEdit,
validationSchema: editUserSchema,
onSubmit: async (values, {setSubmitting}) => {
setSubmitting(true)
try {
if (isNotEmpty(values.id)) {
await updateUser(values)
} else {
await createUser(values)
}
} catch (ex) {
console.error(ex)
} finally {
setSubmitting(true)
cancel(true)
}
},
})
return (
<>
<form id='kt_modal_add_user_form' className='form' onSubmit={formik.handleSubmit} noValidate>
{/* begin::Scroll */}
<div
className='d-flex flex-column scroll-y me-n7 pe-7'
id='kt_modal_add_user_scroll'
data-kt-scroll='true'
data-kt-scroll-activate='{default: false, lg: true}'
data-kt-scroll-max-height='auto'
data-kt-scroll-dependencies='#kt_modal_add_user_header'
data-kt-scroll-wrappers='#kt_modal_add_user_scroll'
data-kt-scroll-offset='300px'
>
{/* begin::Input group */}
{/* <div className='fv-row mb-7'> */}
{/* begin::Label */}
{/* <label className='d-block fw-bold fs-6 mb-5'>Avatar</label> */}
{/* end::Label */}
{/* begin::Image input */}
{/* <div
className='image-input image-input-outline'
data-kt-image-input='true'
style={{backgroundImage: `url('${blankImg}')`}}
> */}
{/* begin::Preview existing avatar */}
{/* <div
className='image-input-wrapper w-125px h-125px'
style={{backgroundImage: `url('${userAvatarImg}')`}}
></div> */}
{/* end::Preview existing avatar */}
{/* begin::Label */}
{/* <label
className='btn btn-icon btn-circle btn-active-color-primary w-25px h-25px bg-body shadow'
data-kt-image-input-action='change'
data-bs-toggle='tooltip'
title='Change avatar'
>
<i className='bi bi-pencil-fill fs-7'></i>
<input type='file' name='avatar' accept='.png, .jpg, .jpeg' />
<input type='hidden' name='avatar_remove' />
</label> */}
{/* end::Label */}
{/* begin::Cancel */}
{/* <span
className='btn btn-icon btn-circle btn-active-color-primary w-25px h-25px bg-body shadow'
data-kt-image-input-action='cancel'
data-bs-toggle='tooltip'
title='Cancel avatar'
>
<i className='bi bi-x fs-2'></i>
</span> */}
{/* end::Cancel */}
{/* begin::Remove */}
{/* <span
className='btn btn-icon btn-circle btn-active-color-primary w-25px h-25px bg-body shadow'
data-kt-image-input-action='remove'
data-bs-toggle='tooltip'
title='Remove avatar'
>
<i className='bi bi-x fs-2'></i>
</span> */}
{/* end::Remove */}
{/* </div> */}
{/* end::Image input */}
{/* begin::Hint */}
{/* <div className='form-text'>Allowed file types: png, jpg, jpeg.</div> */}
{/* end::Hint */}
{/* </div> */}
{/* end::Input group */}
{/* begin::Input group */}
<div className='fv-row mb-7'>
{/* begin::Label */}
<label className='required fw-bold fs-6 mb-2'>Full Name</label>
{/* end::Label */}
{/* begin::Input */}
<input
placeholder='Full name'
{...formik.getFieldProps('name')}
type='text'
name='name'
className={clsx(
'form-control form-control-solid mb-3 mb-lg-0',
{'is-invalid': formik.touched.name && formik.errors.name},
{
'is-valid': formik.touched.name && !formik.errors.name,
}
)}
autoComplete='off'
disabled={formik.isSubmitting || isUserLoading}
/>
{formik.touched.name && formik.errors.name && (
<div className='fv-plugins-message-container'>
<div className='fv-help-block'>
<span role='alert'>{formik.errors.name}</span>
</div>
</div>
)}
{/* end::Input */}
</div>
{/* end::Input group */}
{/* begin::Input group */}
<div className='fv-row mb-7'>
{/* begin::Label */}
<label className='required fw-bold fs-6 mb-2'>Email</label>
{/* end::Label */}
{/* begin::Input */}
<input
placeholder='Email'
{...formik.getFieldProps('email')}
className={clsx(
'form-control form-control-solid mb-3 mb-lg-0',
{'is-invalid': formik.touched.email && formik.errors.email},
{
'is-valid': formik.touched.email && !formik.errors.email,
}
)}
type='email'
name='email'
autoComplete='off'
disabled={formik.isSubmitting || isUserLoading}
/>
{/* end::Input */}
{formik.touched.email && formik.errors.email && (
<div className='fv-plugins-message-container'>
<span role='alert'>{formik.errors.email}</span>
</div>
)}
</div>
{/* end::Input group */}
{/* begin::Input group */}
<div className='mb-7'>
{/* begin::Label */}
<label className='required fw-bold fs-6 mb-5'>Role</label>
{/* end::Label */}
{/* begin::Roles */}
{/* begin::Input row */}
<div className='d-flex fv-row'>
{/* begin::Radio */}
<div className='form-check form-check-custom form-check-solid'>
{/* begin::Input */}
<input
className='form-check-input me-3'
{...formik.getFieldProps('role')}
name='role'
type='radio'
value='Tenant'
id='kt_modal_update_role_option_0'
checked={true}
disabled={true}
/>
{/* end::Input */}
{/* begin::Label */}
<label className='form-check-label' htmlFor='kt_modal_update_role_option_0'>
<div className='fw-bolder text-gray-800'>Tenant</div>
<div className='text-gray-600'>
Create an invite for a tenant to register with Landlord Direct
</div>
</label>
{/* end::Label */}
</div>
{/* end::Radio */}
</div>
{/* end::Input row */}
{/* <div className='separator separator-dashed my-5'></div> */}
{/* begin::Input row */}
{/* <div className='d-flex fv-row'> */}
{/* begin::Radio */}
{/* <div className='form-check form-check-custom form-check-solid'> */}
{/* begin::Input */}
{/* <input
className='form-check-input me-3'
{...formik.getFieldProps('role')}
name='role'
type='radio'
value='Developer'
id='kt_modal_update_role_option_1'
checked={formik.values.role === 'Developer'}
disabled={formik.isSubmitting || isUserLoading}
/> */}
{/* end::Input */}
{/* begin::Label */}
{/* <label className='form-check-label' htmlFor='kt_modal_update_role_option_1'>
<div className='fw-bolder text-gray-800'>Developer</div>
<div className='text-gray-600'>
Best for developers or people primarily using the API
</div>
</label> */}
{/* end::Label */}
{/* </div> */}
{/* end::Radio */}
{/* </div> */}
{/* end::Input row */}
{/* <div className='separator separator-dashed my-5'></div> */}
{/* begin::Input row */}
{/* <div className='d-flex fv-row'> */}
{/* begin::Radio */}
{/* <div className='form-check form-check-custom form-check-solid'> */}
{/* begin::Input */}
{/* <input
className='form-check-input me-3'
{...formik.getFieldProps('role')}
name='role'
type='radio'
value='Analyst'
id='kt_modal_update_role_option_2'
checked={formik.values.role === 'Analyst'}
disabled={formik.isSubmitting || isUserLoading}
/> */}
{/* end::Input */}
{/* begin::Label */}
{/* <label className='form-check-label' htmlFor='kt_modal_update_role_option_2'>
<div className='fw-bolder text-gray-800'>Analyst</div>
<div className='text-gray-600'>
Best for people who need full access to analytics data, but don't need to update
business settings
</div>
</label> */}
{/* end::Label */}
{/* </div> */}
{/* end::Radio */}
{/* </div> */}
{/* end::Input row */}
{/* <div className='separator separator-dashed my-5'></div> */}
{/* begin::Input row */}
{/* <div className='d-flex fv-row'> */}
{/* begin::Radio */}
{/* <div className='form-check form-check-custom form-check-solid'> */}
{/* begin::Input */}
{/* <input
className='form-check-input me-3'
{...formik.getFieldProps('role')}
name='role'
type='radio'
value='Support'
id='kt_modal_update_role_option_3'
checked={formik.values.role === 'Support'}
disabled={formik.isSubmitting || isUserLoading}
/> */}
{/* end::Input */}
{/* begin::Label */}
{/* <label className='form-check-label' htmlFor='kt_modal_update_role_option_3'>
<div className='fw-bolder text-gray-800'>Support</div>
<div className='text-gray-600'>
Best for employees who regularly refund payments and respond to disputes
</div>
</label> */}
{/* end::Label */}
{/* </div> */}
{/* end::Radio */}
{/* </div> */}
{/* end::Input row */}
{/* <div className='separator separator-dashed my-5'></div> */}
{/* begin::Input row */}
{/* <div className='d-flex fv-row'> */}
{/* begin::Radio */}
{/* <div className='form-check form-check-custom form-check-solid'> */}
{/* begin::Input */}
{/* <input
className='form-check-input me-3'
{...formik.getFieldProps('role')}
name='role'
type='radio'
id='kt_modal_update_role_option_4'
value='Trial'
checked={formik.values.role === 'Trial'}
disabled={formik.isSubmitting || isUserLoading}
/> */}
{/* end::Input */}
{/* begin::Label */}
{/* <label className='form-check-label' htmlFor='kt_modal_update_role_option_4'>
<div className='fw-bolder text-gray-800'>Trial</div>
<div className='text-gray-600'>
Best for people who need to preview content data, but don't need to make any
updates
</div>
</label> */}
{/* end::Label */}
{/* </div> */}
{/* end::Radio */}
{/* </div> */}
{/* end::Input row */}
{/* end::Roles */}
</div>
{/* end::Input group */}
</div>
{/* end::Scroll */}
{/* begin::Actions */}
<div className='text-center pt-15'>
<button
type='reset'
onClick={() => cancel()}
className='btn btn-light me-3'
data-kt-users-modal-action='cancel'
disabled={formik.isSubmitting || isUserLoading}
>
Discard
</button>
<button
type='submit'
className='btn btn-primary'
data-kt-users-modal-action='submit'
disabled={isUserLoading || formik.isSubmitting || !formik.isValid || !formik.touched}
>
<span className='indicator-label'>Submit</span>
{(formik.isSubmitting || isUserLoading) && (
<span className='indicator-progress'>
Please wait...{' '}
<span className='spinner-border spinner-border-sm align-middle ms-2'></span>
</span>
)}
</button>
</div>
{/* end::Actions */}
</form>
{(formik.isSubmitting || isUserLoading) && <UsersListLoading />}
</>
)
}
export {AddPropForm}
The form wrapper:
import {useQuery} from 'react-query'
import {AddPropForm} from './AddPropForm'
import {isNotEmpty, QUERIES} from '../../../../../../_metronic/helpers'
import {useListView} from '../core/ListViewProvider'
import {getUserById} from '../core/_requests'
const AddPropModalFormWrapper = () => {
const {itemIdForUpdate, setItemIdForUpdate} = useListView()
const enabledQuery: boolean = isNotEmpty(itemIdForUpdate)
const {
isLoading,
data: user,
error,
} = useQuery(
`${QUERIES.USERS_LIST}-user-${itemIdForUpdate}`,
() => {
return getUserById(itemIdForUpdate)
},
{
cacheTime: 0,
enabled: enabledQuery,
onError: (err) => {
setItemIdForUpdate(undefined)
console.error(err)
},
}
)
if (!itemIdForUpdate) {
return <AddPropForm isUserLoading={isLoading} user={{id: undefined}} />
}
if (!isLoading && !error && user) {
return <AddPropForm isUserLoading={isLoading} user={user} />
}
return null
}
export {AddPropModalFormWrapper}
The form I am trying replace the current form with:
mport React, {FC, useEffect, useRef, useState} from 'react'
import {Step1} from './steps/Step1'
import {Step2} from './steps/Step2'
import {Step3} from './steps/Step3'
import {Step4} from './steps/Step4'
import {Step5} from './steps/Step5'
import {KTSVG} from '../../../../_metronic/helpers'
import {StepperComponent} from '../../../../_metronic/assets/ts/components'
import {Formik, Form, FormikValues} from 'formik'
import {createAccountSchemas, ICreateAccount, inits} from './CreateAccountWizardHelper'
const Horizontal: FC = () => {
const stepperRef = useRef<HTMLDivElement | null>(null)
const stepper = useRef<StepperComponent | null>(null)
const [currentSchema, setCurrentSchema] = useState(createAccountSchemas[0])
const [initValues] = useState<ICreateAccount>(inits)
const [isSubmitButton, setSubmitButton] = useState(false)
const loadStepper = () => {
stepper.current = StepperComponent.createInsance(stepperRef.current as HTMLDivElement)
}
const prevStep = () => {
if (!stepper.current) {
return
}
setSubmitButton(stepper.current.currentStepIndex === stepper.current.totatStepsNumber! - 1)
stepper.current.goPrev()
setCurrentSchema(createAccountSchemas[stepper.current.currentStepIndex - 1])
}
const submitStep = (values: ICreateAccount, actions: FormikValues) => {
if (!stepper.current) {
return
}
setSubmitButton(stepper.current.currentStepIndex === stepper.current.totatStepsNumber! - 1)
setCurrentSchema(createAccountSchemas[stepper.current.currentStepIndex])
if (stepper.current.currentStepIndex !== stepper.current.totatStepsNumber) {
stepper.current.goNext()
} else {
stepper.current.goto(1)
actions.resetForm()
}
}
useEffect(() => {
if (!stepperRef.current) {
return
}
loadStepper()
}, [stepperRef])
return (
<div className='card'>
<div className='card-body'>
<div
ref={stepperRef}
className='stepper stepper-links d-flex flex-column pt-15'
id='kt_create_account_stepper'
>
<div className='stepper-nav mb-5'>
<div className='stepper-item current' data-kt-stepper-element='nav'>
<h3 className='stepper-title'>Account Type</h3>
</div>
<div className='stepper-item' data-kt-stepper-element='nav'>
<h3 className='stepper-title'>Account Info</h3>
</div>
<div className='stepper-item' data-kt-stepper-element='nav'>
<h3 className='stepper-title'>Business Info</h3>
</div>
<div className='stepper-item' data-kt-stepper-element='nav'>
<h3 className='stepper-title'>Billing Details</h3>
</div>
<div className='stepper-item' data-kt-stepper-element='nav'>
<h3 className='stepper-title'>Completed</h3>
</div>
</div>
<Formik validationSchema={currentSchema} initialValues={initValues} onSubmit={submitStep}>
{() => (
<Form className='mx-auto mw-600px w-100 pt-15 pb-10' id='kt_create_account_form'>
<div className='current' data-kt-stepper-element='content'>
<Step1 />
</div>
<div data-kt-stepper-element='content'>
<Step2 />
</div>
<div data-kt-stepper-element='content'>
<Step3 />
</div>
<div data-kt-stepper-element='content'>
<Step4 />
</div>
<div data-kt-stepper-element='content'>
<Step5 />
</div>
<div className='d-flex flex-stack pt-15'>
<div className='mr-2'>
<button
onClick={prevStep}
type='button'
className='btn btn-lg btn-light-primary me-3'
data-kt-stepper-action='previous'
>
<KTSVG
path='/media/icons/duotune/arrows/arr063.svg'
className='svg-icon-4 me-1'
/>
Back
</button>
</div>
<div>
<button type='submit' className='btn btn-lg btn-primary me-3'>
<span className='indicator-label'>
{!isSubmitButton && 'Continue'}
{isSubmitButton && 'Submit'}
<KTSVG
path='/media/icons/duotune/arrows/arr064.svg'
className='svg-icon-3 ms-2 me-0'
/>
</span>
</button>
</div>
</div>
</Form>
)}
</Formik>
</div>
</div>
</div>
)
}
export {Horizontal}
What would be the correct way of bringing in the multi step functionality of the new form whilst still having it open inside the modal?

Related

How to Redirect a Register Page to another Page using React & Formik

The page is not responding after clicking the sign up button, I wonder what went wrong with the code. I want it to redirect to "dashboards/elearning" page. The NavLink points not to the Sign Up button, and if I add a NavLink around sign up button, the validation would be pointless.
Below is the register.js file.
import React from 'react';
import { NavLink } from 'react-router-dom';
import { Button, Form } from 'react-bootstrap';
import * as Yup from 'yup';
import { useFormik } from 'formik';
import LayoutFullpage from 'layout/LayoutFullpage';
import CsLineIcons from 'cs-line-icons/CsLineIcons';
import HtmlHead from 'components/html-head/HtmlHead';
const Register = () => {
const title = 'Register';
const description = 'Register Page';
const validationSchema = Yup.object().shape({
name: Yup.string().required('Name is required'),
email: Yup.string().email().required('Email is required'),
password: Yup.string().min(6, 'Must be at least 6 chars!').required('Password is required'),
terms: Yup.bool().required().oneOf([true], 'Terms must be accepted'),
});
const initialValues = { name: '', email: '', password: '', terms: false };
const onSubmit = (values) => console.log('submit form', values);
const formik = useFormik({ initialValues, validationSchema, onSubmit });
const { handleSubmit, handleChange, values, touched, errors } = formik;
const leftSide = (
<div className="min-h-100 d-flex align-items-center">
<div className="w-100 w-lg-75 w-xxl-50">
<div>
<div className="mb-5">
<h1 className="display-3 text-white">Multiple Niches</h1>
<h1 className="display-3 text-white">Ready for Your Project</h1>
</div>
<p className="h6 text-white lh-1-5 mb-5">
Dynamically target high-payoff intellectual capital for customized technologies. Objectively integrate emerging core competencies before
process-centric communities...
</p>
<div className="mb-5">
<Button size="lg" variant="outline-white" href="/">
Learn More
</Button>
</div>
</div>
</div>
</div>
);
// if <NavLink to="/dashboards/elearning"> Sign up </NavLink>, all other validation mathods invalid
const rightSide = (
<div className="sw-lg-70 min-h-100 bg-foreground d-flex justify-content-center align-items-center shadow-deep py-5 full-page-content-right-border">
<div className="sw-lg-50 px-5">
<div className="sh-11">
<NavLink to="/dashboards/elearning">
<div className="logo-default" />
</NavLink>
</div>
<div className="mb-5">
<h2 className="cta-1 mb-0 text-primary">Welcome,</h2>
<h2 className="cta-1 text-primary">let's get the ball rolling!</h2>
</div>
<div className="mb-5">
<p className="h6">Please use the form to register.</p>
<p className="h6">
If you are a member, please <NavLink to="/login">login</NavLink>.
</p>
</div>
<div>
<form id="registerForm" className="tooltip-end-bottom" onSubmit={handleSubmit}>
<div className="mb-3 filled form-group tooltip-end-top">
<CsLineIcons icon="user" />
<Form.Control type="text" name="name" placeholder="Name" value={values.name} onChange={handleChange} />
{errors.name && touched.name && <div className="d-block invalid-tooltip">{errors.name}</div>}
</div>
<div className="mb-3 filled form-group tooltip-end-top">
<CsLineIcons icon="email" />
<Form.Control type="text" name="email" placeholder="Email" value={values.email} onChange={handleChange} />
{errors.email && touched.email && <div className="d-block invalid-tooltip">{errors.email}</div>}
</div>
<div className="mb-3 filled form-group tooltip-end-top">
<CsLineIcons icon="lock-off" />
<Form.Control type="password" name="password" onChange={handleChange} value={values.password} placeholder="Password" />
{errors.password && touched.password && <div className="d-block invalid-tooltip">{errors.password}</div>}
</div>
<div className="mb-3 position-relative form-group">
<div className="form-check">
<input type="checkbox" className="form-check-input" name="terms" onChange={handleChange} value={values.terms} />
<label className="form-check-label">
I have read and accept the{' '}
<NavLink to="/dashboards/elearning" target="_blank">
terms and conditions.
</NavLink>
</label>
{errors.terms && touched.terms && <div className="d-block invalid-tooltip">{errors.terms}</div>}
</div>
</div>
<Button size="lg" type="submit">
Signup
</Button>
</form>
</div>
</div>
</div>
);
return (
<>
<HtmlHead title={title} description={description} />
<LayoutFullpage left={leftSide} right={rightSide} />
</>
);
};
export default Register;
You can use the useNavigate hook from react-router-dom
Please find the necessary snippet below.
import { useNavigate } from 'react-router-dom';
...
const navigate = useNavigate();
const onSubmit = (values) => {
console.log('submit form', values);
navigate('dashboards/elearning');
};
...

How to call a modal from an onClick event

Trying to trigger a modal popup from an onClick event as below:
import {KTSVG} from '../../../../../../../_metronic/helpers'
import {useListView} from '../../core/ListViewProvider'
import { CreateAppModal } from '../../manage-properties-modal/add-property-modal/AddPropertyModal'
// import {UsersListFilter} from './UsersListFilter'
const PropertyListToolbar = () => {
const {setItemIdForUpdate} = useListView()
const openAddUserModal = () => {
setItemIdForUpdate(null)
}
return (
<div className='d-flex justify-content-end' data-kt-user-table-toolbar='base'>
{/* <UsersListFilter /> */}
{/* begin::Export */}
{/* <button type='button' className='btn btn-light-primary me-3' onClick={openAddUserModal}>
<KTSVG path='/media/icons/duotune/arrows/arr078.svg' className='svg-icon-2' />
Export Details
</button> */}
{/* end::Export */}
{/* begin::Add user */}
<button type='button' className='btn btn-primary' onClick={CreateAppModal}>
<KTSVG path='/media/icons/duotune/arrows/arr075.svg' className='svg-icon-2' />
New Property
</button>
{/* end::Add user */}
</div>
)
}
export {PropertyListToolbar}
The setItemIdForUpdate code, I would like to just disable for now and just get the button to launch the below modal, which I can then customize before I look at saving its state.
import {useState, useRef} from 'react'
import {createPortal} from 'react-dom'
import {Modal} from 'react-bootstrap'
import {defaultCreateAppData, ICreateAppData} from './IAppModels'
import {StepperComponent} from '../../../assets/ts/components'
import {KTSVG} from '../../../helpers'
import {Step1} from './steps/Step1'
import {Step2} from './steps/Step2'
import {Step3} from './steps/Step3'
import {Step4} from './steps/Step4'
import {Step5} from './steps/Step5'
type Props = {
show: boolean
handleClose: () => void
}
const modalsRoot = document.getElementById('root-modals') || document.body
const CreateAppModal = ({show, handleClose}: Props) => {
const stepperRef = useRef<HTMLDivElement | null>(null)
const stepper = useRef<StepperComponent | null>(null)
const [data, setData] = useState<ICreateAppData>(defaultCreateAppData)
const [hasError, setHasError] = useState(false)
const loadStepper = () => {
stepper.current = StepperComponent.createInsance(stepperRef.current as HTMLDivElement)
}
const updateData = (fieldsToUpdate: Partial<ICreateAppData>) => {
const updatedData = {...data, ...fieldsToUpdate}
setData(updatedData)
}
const checkAppBasic = (): boolean => {
if (!data.appBasic.appName || !data.appBasic.appType) {
return false
}
return true
}
const checkAppDataBase = (): boolean => {
if (!data.appDatabase.databaseName || !data.appDatabase.databaseSolution) {
return false
}
return true
}
const prevStep = () => {
if (!stepper.current) {
return
}
stepper.current.goPrev()
}
const nextStep = () => {
setHasError(false)
if (!stepper.current) {
return
}
if (stepper.current.getCurrentStepIndex() === 1) {
if (!checkAppBasic()) {
setHasError(true)
return
}
}
if (stepper.current.getCurrentStepIndex() === 3) {
if (!checkAppDataBase()) {
setHasError(true)
return
}
}
stepper.current.goNext()
}
const submit = () => {
window.location.reload()
}
return createPortal(
<Modal
id='kt_modal_create_app'
tabIndex={-1}
aria-hidden='true'
dialogClassName='modal-dialog modal-dialog-centered mw-900px'
show={show}
onHide={handleClose}
onEntered={loadStepper}
>
<div className='modal-header'>
<h2>Create App</h2>
{/* begin::Close */}
<div className='btn btn-sm btn-icon btn-active-color-primary' onClick={handleClose}>
<KTSVG className='svg-icon-1' path='/media/icons/duotune/arrows/arr061.svg' />
</div>
{/* end::Close */}
</div>
<div className='modal-body py-lg-10 px-lg-10'>
{/*begin::Stepper */}
<div
ref={stepperRef}
className='stepper stepper-pills stepper-column d-flex flex-column flex-xl-row flex-row-fluid'
id='kt_modal_create_app_stepper'
>
{/* begin::Aside*/}
<div className='d-flex justify-content-center justify-content-xl-start flex-row-auto w-100 w-xl-300px'>
{/* begin::Nav*/}
<div className='stepper-nav ps-lg-10'>
{/* begin::Step 1*/}
<div className='stepper-item current' data-kt-stepper-element='nav'>
{/* begin::Wrapper*/}
<div className='stepper-wrapper'>
{/* begin::Icon*/}
<div className='stepper-icon w-40px h-40px'>
<i className='stepper-check fas fa-check'></i>
<span className='stepper-number'>1</span>
</div>
{/* end::Icon*/}
{/* begin::Label*/}
<div className='stepper-label'>
<h3 className='stepper-title'>Details</h3>
<div className='stepper-desc'>Name your App</div>
</div>
{/* end::Label*/}
</div>
{/* end::Wrapper*/}
{/* begin::Line*/}
<div className='stepper-line h-40px'></div>
{/* end::Line*/}
</div>
{/* end::Step 1*/}
{/* begin::Step 2*/}
<div className='stepper-item' data-kt-stepper-element='nav'>
{/* begin::Wrapper*/}
<div className='stepper-wrapper'>
{/* begin::Icon*/}
<div className='stepper-icon w-40px h-40px'>
<i className='stepper-check fas fa-check'></i>
<span className='stepper-number'>2</span>
</div>
{/* begin::Icon*/}
{/* begin::Label*/}
<div className='stepper-label'>
<h3 className='stepper-title'>Frameworks</h3>
<div className='stepper-desc'>Define your app framework</div>
</div>
{/* begin::Label*/}
</div>
{/* end::Wrapper*/}
{/* begin::Line*/}
<div className='stepper-line h-40px'></div>
{/* end::Line*/}
</div>
{/* end::Step 2*/}
{/* begin::Step 3*/}
<div className='stepper-item' data-kt-stepper-element='nav'>
{/* begin::Wrapper*/}
<div className='stepper-wrapper'>
{/* begin::Icon*/}
<div className='stepper-icon w-40px h-40px'>
<i className='stepper-check fas fa-check'></i>
<span className='stepper-number'>3</span>
</div>
{/* end::Icon*/}
{/* begin::Label*/}
<div className='stepper-label'>
<h3 className='stepper-title'>Database</h3>
<div className='stepper-desc'>Select the app database type</div>
</div>
{/* end::Label*/}
</div>
{/* end::Wrapper*/}
{/* begin::Line*/}
<div className='stepper-line h-40px'></div>
{/* end::Line*/}
</div>
{/* end::Step 3*/}
{/* begin::Step 4*/}
<div className='stepper-item' data-kt-stepper-element='nav'>
{/* begin::Wrapper*/}
<div className='stepper-wrapper'>
{/* begin::Icon*/}
<div className='stepper-icon w-40px h-40px'>
<i className='stepper-check fas fa-check'></i>
<span className='stepper-number'>4</span>
</div>
{/* end::Icon*/}
{/* begin::Label*/}
<div className='stepper-label'>
<h3 className='stepper-title'>Storage</h3>
<div className='stepper-desc'>Provide storage details</div>
</div>
{/* end::Label*/}
</div>
{/* end::Wrapper*/}
{/* begin::Line*/}
<div className='stepper-line h-40px'></div>
{/* end::Line*/}
</div>
{/* end::Step 4*/}
{/* begin::Step 5*/}
<div className='stepper-item' data-kt-stepper-element='nav'>
{/* begin::Wrapper*/}
<div className='stepper-wrapper'>
{/* begin::Icon*/}
<div className='stepper-icon w-40px h-40px'>
<i className='stepper-check fas fa-check'></i>
<span className='stepper-number'>5</span>
</div>
{/* end::Icon*/}
{/* begin::Label*/}
<div className='stepper-label'>
<h3 className='stepper-title'>Completed</h3>
<div className='stepper-desc'>Review and Submit</div>
</div>
{/* end::Label*/}
</div>
{/* end::Wrapper*/}
</div>
{/* end::Step 5*/}
</div>
{/* end::Nav*/}
</div>
{/* begin::Aside*/}
{/*begin::Content */}
<div className='flex-row-fluid py-lg-5 px-lg-15'>
{/*begin::Form */}
<form noValidate id='kt_modal_create_app_form'>
<Step1 data={data} updateData={updateData} hasError={hasError} />
<Step2 data={data} updateData={updateData} hasError={hasError} />
<Step3 data={data} updateData={updateData} hasError={hasError} />
<Step4 data={data} updateData={updateData} hasError={hasError} />
<Step5 />
{/*begin::Actions */}
<div className='d-flex flex-stack pt-10'>
<div className='me-2'>
<button
type='button'
className='btn btn-lg btn-light-primary me-3'
data-kt-stepper-action='previous'
onClick={prevStep}
>
<KTSVG
path='/media/icons/duotune/arrows/arr063.svg'
className='svg-icon-3 me-1'
/>{' '}
Previous
</button>
</div>
<div>
<button
type='button'
className='btn btn-lg btn-primary'
data-kt-stepper-action='submit'
onClick={submit}
>
Submit{' '}
<KTSVG
path='/media/icons/duotune/arrows/arr064.svg'
className='svg-icon-3 ms-2 me-0'
/>
</button>
<button
type='button'
className='btn btn-lg btn-primary'
data-kt-stepper-action='next'
onClick={nextStep}
>
Next Step{' '}
<KTSVG
path='/media/icons/duotune/arrows/arr064.svg'
className='svg-icon-3 ms-1 me-0'
/>
</button>
</div>
</div>
{/*end::Actions */}
</form>
{/*end::Form */}
</div>
{/*end::Content */}
</div>
{/* end::Stepper */}
</div>
</Modal>,
modalsRoot
)
}
export {CreateAppModal}
I would like to trigger CreateAppModal but cannot for the life of me get my head around it. Any help would be greatly appreciated!
You need to share the source code from CreateAppModal so we can know what it does and whats the relation with setItemIdForUpdate.

Main checkbox selecting all cards when checked

I am building a component where I am grouping cards by regions. I have a checkbox by the region name and when its checked I want to be able to select all the cards in the region.
This is how my cards are displayed by region:
<div className="pl-14 text-black font-nunito mt-8 ml-3 text-2xl">
{_.map(_.keysIn(groups), (region) => {
return (
<>
<div className="flex justify-between">
<div className="flex">
<Checkbox
className="bg-transparent shadow-none text-black hover:bg-transparent"
color="primary"
/>
<p className="mt-1">{_.capitalize(region)}</p>
</div>
<div className="pr-16">
<Button variant="text" className="text-lightBlue">
View More
</Button>
</div>
</div>
<UserCards groupedUser={groups[region]} />
</>
);
})}
</div>
And my cards look like this:
const UserCards = ({ groupedUser }) => {
return groupedUser.map((user) => (
<div className="cardContainer">
<div className="card shadow-customShadow">
<Checkbox
key={user.email}
className="bg-transparent shadow-none text-black mb-14 hover:bg-transparent"
color="primary"
onChange={(event) => {
if (event.target.checked) {
const data = [...userEmails];
data.push(user.email);
setUserEmails(data);
} else {
const data = [...userEmails];
const index = data.indexOf(user.email);
data.splice(index, 1);
setUserEmails(data);
}
}}
checked={_.includes(userEmails, user.email)}
/>
<div>
<div className="mt-2 text-lg">
{information stored here}
</div>
<div className="mt-2 text-lg">
{information stored here}
</div>
</div>
</div>
</div>
));
};
How can I tell the region check box to check all the cards?

How my button can move under each new added sections? For now it's placed above all sections

How can I modify my code in order to get a dynamic +Add Invitee button which will move down the last section each time you add fields? For now, it appears above all blocks.
export default function App() {
const [menu, setMenu] = useState("");
return (
<>
<button type="button" onClick={addInvitee}>
+Add Invitee
</button>
<form onSubmit={handleSubmit}>
{invited.map(({ age, email, id, location, name }, index) => (
<div key={id}>
<div className="grid grid-cols-3 gap-5">
<label className="mr-3 h-6 text-md font-bold">
Names:
<input
type="text"
value={name}
placeholder="Names"
name="name"
onChange={updateInvitee(id)}
/>
</label>
...
{!!index && (
<button type="button" onClick={() => removeInvitee(id)}>
Remove
</button>
)}
</div>
</div>
))}
</form>
</>
);
}
Here is my:
code
To achieve that you just need to take the button down under the form.
export default function App() {
const [menu, setMenu] = useState("");
return (
<>
<form onSubmit={handleSubmit}>
{invited.map(({ age, email, id, location, name }, index) => (
<div key={id}>
<div className="grid grid-cols-3 gap-5">
<label className="mr-3 h-6 text-md font-bold">
Names:
<input
type="text"
value={name}
placeholder="Names"
name="name"
onChange={updateInvitee(id)}
/>
</label>
...
{!!index && (
<button type="button" onClick={() => removeInvitee(id)}>
Remove
</button>
)}
</div>
</div>
))}
</form>
<button type="button" onClick={addInvitee}>
+Add Invitee
</button>
</>
);
}
also you can style the button or wrap it with a div for more complex styling to achieve the example from the photo you shared.
here is alink for a working example: codesandbox

Display different values in a popup in React

I am trying to have a popup for editing profile information in react, and I want there to be a button by each value that will allow a user to edit that specific value. The popup works, except it will only display the last value that I have put in. I think it is because I need to reset the state each time the button is clicked, but I am still fairly new to react so I am not sure how to do it.
I will add my code, however it is pulling some information from a local database, so let me know if you need me to remove things.
Here is the component for the popup
import React from 'react'
const Popup = props => {
console.log(props)
return(
<div className='popup-box'>
<div className='box'>
<span className='close-icon' onClick={props.handleClose}>x</span>
{props.content}
</div>
</div>
)
}
export default Popup
And here is the component page where the popup appears
import React, { useState, useEffect } from 'react'
import { Link } from 'react-router-dom'
import { getUser } from '../apiClient'
import Nav from './Nav'
import EditPopup from './EditPopup'
export default function UserProfile (props) {
const [isOpen, setIsOpen] = useState(false)
const togglePopup = () => {
setIsOpen(!isOpen)
}
const [user, setUser] = useState({
user: []
})
useEffect(() => {
getUser(props.match.params.id)
.then((res) => {
setUser(res)
})
.catch((error) => {
console.log('error', error.message)
})
}, [])
return (
<div className='globalBackground'>
<Nav />
<div className='UserInfoForm'>
<div className='profile-heading' >
<h1>General User Information</h1>
</div>
<div className='profile-Pic' >
<div className='profile-pic-heading'>
<h2>Profile Picture</h2>
</div>
<div className='profile-pic-display'>
<img src={user.profilePic} style={{ width: '150px', height: '150px' }}
alt=''
/>
</div>
</div>
<div className='UsernameEdit'>
<h2>Username</h2>
<p>{user.username}</p>
<input type='button' value='Edit' onClick={togglePopup} />
{isOpen && <EditPopup
content={<>
<b>Edit Your Username</b>
<div className='userNameEditInput'>
<input className='usernameEdit' placeholder={user.username} />
</div>
<button>Save Changes</button>
</>}
handleClose={togglePopup}
/>}
<div />
<div className='EmailEdit'>
<h2>Email</h2>
<p>{user.email}</p>
<input type='button' value='Edit' onClick={togglePopup} />
{isOpen && <EditPopup
content={<>
<b>Edit Your Email</b>
<div className='emailEditInput'>
<input className='emailEdit' placeholder={user.email} />
</div>
<button>Save Changes</button>
</>}
handleClose={togglePopup}
/>}
</div>
<div className='CountryEdit'>
<h2>Country</h2>
<p>{user.country}</p>
<input type='button' value='Edit' onClick={togglePopup} />
{isOpen && <EditPopup
content={<>
<b>Edit Your Country</b>
<div className='countryEditInput'>
<input className='countryEdit' placeholder={user.country} />
</div>
<button>Save Changes</button>
</>}
handleClose={togglePopup}
/>}
</div>
<div className='RegionEdit'>
<h2>Region</h2>
<p>{user.region}</p>
<input type='button' value='Edit' onClick={togglePopup} />
{isOpen && <EditPopup
content={<>
<b>Edit Your Region</b>
<div className='regionEditInput'>
<input className='regionEdit' placeholder={user.email} />
</div>
<button>Save Changes</button>
</>}
handleClose={togglePopup}
/>}
</div>
<div className='HandicapEdit'>
<h2>Handicap</h2>
<p>{user.handicap}</p>
<input type='button' value='Edit' onClick={togglePopup} />
{isOpen && <EditPopup
content={<>
<b>Edit Your Handicap</b>
<div className='handicapEditInput'>
<input className='handicapEdit' placeholder={user.handicap} />
</div>
<button>Save Changes</button>
</>}
handleClose={togglePopup}
/>}
</div>
</div>
</div>
</div>
)
}
Let me know if there's any other code needed, any help would be greatly appreciated :--)
You can remove the state from the UserProfile and move it inside the Popup Component.
import React from 'react';
const Popup = (props) => {
const [toggle, setToggle] = useState(false);
const togglePopup = () => setToggle((prevToggle) => !prevToggle);
return (
<>
<input type="button" value="Edit" onClick={togglePopup} />
{toggle && (
<div className="popup-box">
<div className="box">
<span className="close-icon" onClick={togglePopup }>
x
</span>
{props.content}
</div>
</div>
)}
</>
);
};
export default Popup;
You can then use EditPopup as below in the UserProfile.
{
<div className='UsernameEdit'>
<h2>Username</h2>
<p>{user.username}</p>
<EditPopup
content={<>
<b>Edit Your Username</b>
<div className='userNameEditInput'>
<input className='usernameEdit' placeholder={user.username} />
</div>
<button>Save Changes</button>
</>
}
/>
<div />
}

Resources