feat: Prevent leaving the page without saving (#431)
* fix: make all fields required on the Terms & Conditions page if Show on screen is enabled fix: enable/disable the Terms & Conditions form based on the Show on screen toggle fix: replaced deactivated field with plain text when not editing fix: make de non editable text content field scrollable style: make it follow the same style as the other screens, with the edit button and links to save and cancel feat: created Prompt component to avoid leaving pages without saving feat: applied component to the editable table feat: applied component to the Cashout, Commissions, Locales, Cashboxes, Notifications, CryptoBalanceOverrides and Wallet pages feat: applied component to the ContactInfo and ReceiptPrinting pages refactor: export the default prompt message to be used in other contexts fix: applied prompt component to the Operator Info pages fix: create routes for the operator info components feat: applied the Prompt component to the Contact Info and Receipt pages feat: applied the Prompt component to the Terms & Conditions page * refactor: move prompt to components * feat: use formik on the boolean properties table * chore: removed console.logs chore: removed comments refactor: moved BooleanCell to the BooleanPropertiesTable file and make it not a formik field
This commit is contained in:
parent
dbfb37a756
commit
3c0f4ec194
10 changed files with 173 additions and 129 deletions
16
new-lamassu-admin/src/components/PromptWhenDirty.js
Normal file
16
new-lamassu-admin/src/components/PromptWhenDirty.js
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
import { useFormikContext } from 'formik'
|
||||||
|
import React from 'react'
|
||||||
|
import { Prompt } from 'react-router-dom'
|
||||||
|
|
||||||
|
const PROMPT_DEFAULT_MESSAGE =
|
||||||
|
'You have unsaved changes on this page. Are you sure you want to leave?'
|
||||||
|
|
||||||
|
const PromptWhenDirty = ({ message = PROMPT_DEFAULT_MESSAGE }) => {
|
||||||
|
const formik = useFormikContext()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Prompt when={formik.dirty && formik.submitCount === 0} message={message} />
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default PromptWhenDirty
|
||||||
|
|
@ -1,117 +1,110 @@
|
||||||
import { makeStyles } from '@material-ui/core/styles'
|
import { makeStyles } from '@material-ui/core/styles'
|
||||||
import classnames from 'classnames'
|
import classnames from 'classnames'
|
||||||
|
import { useFormikContext, Form, Formik, Field as FormikField } from 'formik'
|
||||||
|
import _ from 'lodash'
|
||||||
import React, { useState, memo } from 'react'
|
import React, { useState, memo } from 'react'
|
||||||
|
import * as Yup from 'yup'
|
||||||
|
|
||||||
|
import PromptWhenDirty from 'src/components/PromptWhenDirty'
|
||||||
import { Link } from 'src/components/buttons'
|
import { Link } from 'src/components/buttons'
|
||||||
import { RadioGroup } from 'src/components/inputs'
|
import { RadioGroup } from 'src/components/inputs/formik'
|
||||||
import { Table, TableBody, TableRow, TableCell } from 'src/components/table'
|
import { Table, TableBody, TableRow, TableCell } from 'src/components/table'
|
||||||
import BooleanCell from 'src/components/tables/BooleanCell'
|
|
||||||
import { H4 } from 'src/components/typography'
|
import { H4 } from 'src/components/typography'
|
||||||
import { ReactComponent as EditIconDisabled } from 'src/styling/icons/action/edit/disabled.svg'
|
import { ReactComponent as EditIconDisabled } from 'src/styling/icons/action/edit/disabled.svg'
|
||||||
import { ReactComponent as EditIcon } from 'src/styling/icons/action/edit/enabled.svg'
|
import { ReactComponent as EditIcon } from 'src/styling/icons/action/edit/enabled.svg'
|
||||||
|
import { ReactComponent as FalseIcon } from 'src/styling/icons/table/false.svg'
|
||||||
|
import { ReactComponent as TrueIcon } from 'src/styling/icons/table/true.svg'
|
||||||
|
|
||||||
import { booleanPropertiesTableStyles } from './BooleanPropertiesTable.styles'
|
import { booleanPropertiesTableStyles } from './BooleanPropertiesTable.styles'
|
||||||
|
|
||||||
const useStyles = makeStyles(booleanPropertiesTableStyles)
|
const useStyles = makeStyles(booleanPropertiesTableStyles)
|
||||||
|
|
||||||
|
const BooleanCell = ({ name }) => {
|
||||||
|
const { values } = useFormikContext()
|
||||||
|
return values[name] === 'true' ? <TrueIcon /> : <FalseIcon />
|
||||||
|
}
|
||||||
|
|
||||||
const BooleanPropertiesTable = memo(
|
const BooleanPropertiesTable = memo(
|
||||||
({ title, disabled, data, elements, save }) => {
|
({ title, disabled, data, elements, save }) => {
|
||||||
|
const initialValues = _.fromPairs(elements.map(it => [it.name, '']))
|
||||||
|
const schemaValidation = _.fromPairs(
|
||||||
|
elements.map(it => [it.name, Yup.boolean().required()])
|
||||||
|
)
|
||||||
|
|
||||||
const [editing, setEditing] = useState(false)
|
const [editing, setEditing] = useState(false)
|
||||||
const [radioGroupValues, setRadioGroupValues] = useState(elements)
|
|
||||||
|
|
||||||
const classes = useStyles()
|
const classes = useStyles()
|
||||||
|
|
||||||
const innerSave = () => {
|
const innerSave = async value => {
|
||||||
radioGroupValues.forEach(element => {
|
save(value)
|
||||||
data[element.name] = element.value
|
|
||||||
})
|
|
||||||
|
|
||||||
save(data)
|
|
||||||
setEditing(false)
|
setEditing(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
const innerCancel = () => {
|
const innerCancel = () => setEditing(false)
|
||||||
setRadioGroupValues(elements)
|
|
||||||
setEditing(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleRadioButtons = (elementName, newValue) => {
|
|
||||||
setRadioGroupValues(
|
|
||||||
radioGroupValues.map(element =>
|
|
||||||
element.name === elementName
|
|
||||||
? { ...element, value: newValue }
|
|
||||||
: element
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const radioButtonOptions = [
|
const radioButtonOptions = [
|
||||||
{ display: 'Yes', code: true },
|
{ display: 'Yes', code: 'true' },
|
||||||
{ display: 'No', code: false }
|
{ display: 'No', code: 'false' }
|
||||||
]
|
]
|
||||||
|
|
||||||
if (!elements || radioGroupValues?.length === 0) return null
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={classes.booleanPropertiesTableWrapper}>
|
<div className={classes.booleanPropertiesTableWrapper}>
|
||||||
<div className={classes.rowWrapper}>
|
<Formik
|
||||||
<H4>{title}</H4>
|
enableReinitialize
|
||||||
{editing ? (
|
onSubmit={innerSave}
|
||||||
<div className={classes.rightAligned}>
|
initialValues={data || initialValues}
|
||||||
<Link onClick={innerCancel} color="secondary">
|
schemaValidation={schemaValidation}>
|
||||||
Cancel
|
<Form>
|
||||||
</Link>
|
<div className={classes.rowWrapper}>
|
||||||
<Link
|
<H4>{title}</H4>
|
||||||
className={classes.rightLink}
|
{editing ? (
|
||||||
onClick={innerSave}
|
<div className={classes.rightAligned}>
|
||||||
color="primary">
|
<Link onClick={innerCancel} color="secondary">
|
||||||
Save
|
Cancel
|
||||||
</Link>
|
</Link>
|
||||||
|
<Link
|
||||||
|
className={classes.rightLink}
|
||||||
|
type="submit"
|
||||||
|
color="primary">
|
||||||
|
Save
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className={classes.transparentButton}>
|
||||||
|
<button disabled={disabled} onClick={() => setEditing(true)}>
|
||||||
|
{disabled ? <EditIconDisabled /> : <EditIcon />}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
) : (
|
<PromptWhenDirty />
|
||||||
<div className={classes.transparentButton}>
|
<Table className={classes.fillColumn}>
|
||||||
<button disabled={disabled} onClick={() => setEditing(true)}>
|
<TableBody className={classes.fillColumn}>
|
||||||
{disabled ? <EditIconDisabled /> : <EditIcon />}
|
{elements.map((it, idx) => (
|
||||||
</button>
|
<TableRow key={idx} size="sm" className={classes.tableRow}>
|
||||||
</div>
|
<TableCell className={classes.leftTableCell}>
|
||||||
)}
|
{it.display}
|
||||||
</div>
|
</TableCell>
|
||||||
<Table className={classes.fillColumn}>
|
<TableCell className={classes.rightTableCell}>
|
||||||
<TableBody className={classes.fillColumn}>
|
{editing && (
|
||||||
{radioGroupValues &&
|
<FormikField
|
||||||
radioGroupValues.map((element, idx) => (
|
component={RadioGroup}
|
||||||
<TableRow key={idx} size="sm" className={classes.tableRow}>
|
name={it.name}
|
||||||
<TableCell className={classes.leftTableCell}>
|
options={radioButtonOptions}
|
||||||
{element.display}
|
className={classnames(
|
||||||
</TableCell>
|
classes.radioButtons,
|
||||||
<TableCell className={classes.rightTableCell}>
|
classes.rightTableCell
|
||||||
{editing && (
|
)}
|
||||||
<RadioGroup
|
/>
|
||||||
options={radioButtonOptions}
|
)}
|
||||||
value={element.value}
|
{!editing && <BooleanCell name={it.name} />}
|
||||||
onChange={event =>
|
</TableCell>
|
||||||
handleRadioButtons(
|
</TableRow>
|
||||||
element.name,
|
))}
|
||||||
event.target.value === 'true'
|
</TableBody>
|
||||||
)
|
</Table>
|
||||||
}
|
</Form>
|
||||||
className={classnames(
|
</Formik>
|
||||||
classes.radioButtons,
|
|
||||||
classes.rightTableCell
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{!editing && (
|
|
||||||
<BooleanCell
|
|
||||||
className={classes.rightTableCell}
|
|
||||||
value={element.value}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</TableCell>
|
|
||||||
</TableRow>
|
|
||||||
))}
|
|
||||||
</TableBody>
|
|
||||||
</Table>
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import * as R from 'ramda'
|
||||||
import React, { useState } from 'react'
|
import React, { useState } from 'react'
|
||||||
import { v4 } from 'uuid'
|
import { v4 } from 'uuid'
|
||||||
|
|
||||||
|
import PromptWhenDirty from 'src/components/PromptWhenDirty'
|
||||||
import Link from 'src/components/buttons/Link.js'
|
import Link from 'src/components/buttons/Link.js'
|
||||||
import { AddButton } from 'src/components/buttons/index.js'
|
import { AddButton } from 'src/components/buttons/index.js'
|
||||||
import { TBody, Table } from 'src/components/fake-table/Table'
|
import { TBody, Table } from 'src/components/fake-table/Table'
|
||||||
|
|
@ -159,6 +160,7 @@ const ETable = ({
|
||||||
validationSchema={validationSchema}
|
validationSchema={validationSchema}
|
||||||
onSubmit={innerSave}>
|
onSubmit={innerSave}>
|
||||||
<Form>
|
<Form>
|
||||||
|
<PromptWhenDirty />
|
||||||
<ERow editing={true} disabled={forceDisable} />
|
<ERow editing={true} disabled={forceDisable} />
|
||||||
</Form>
|
</Form>
|
||||||
</Formik>
|
</Formik>
|
||||||
|
|
|
||||||
|
|
@ -1,8 +0,0 @@
|
||||||
import React from 'react'
|
|
||||||
|
|
||||||
import { ReactComponent as FalseIcon } from 'src/styling/icons/table/false.svg'
|
|
||||||
import { ReactComponent as TrueIcon } from 'src/styling/icons/table/true.svg'
|
|
||||||
|
|
||||||
const BooleanCell = ({ value }) => (value ? <TrueIcon /> : <FalseIcon />)
|
|
||||||
|
|
||||||
export default BooleanCell
|
|
||||||
|
|
@ -2,6 +2,8 @@ import { Form, Formik } from 'formik'
|
||||||
import React, { useContext } from 'react'
|
import React, { useContext } from 'react'
|
||||||
import * as Yup from 'yup'
|
import * as Yup from 'yup'
|
||||||
|
|
||||||
|
import PromptWhenDirty from 'src/components/PromptWhenDirty'
|
||||||
|
|
||||||
import NotificationsCtx from '../NotificationsContext'
|
import NotificationsCtx from '../NotificationsContext'
|
||||||
|
|
||||||
import Header from './EditHeader'
|
import Header from './EditHeader'
|
||||||
|
|
@ -41,6 +43,7 @@ const SingleFieldEditableNumber = ({
|
||||||
setEditing(name, false)
|
setEditing(name, false)
|
||||||
}}>
|
}}>
|
||||||
<Form className={className}>
|
<Form className={className}>
|
||||||
|
<PromptWhenDirty />
|
||||||
<Header
|
<Header
|
||||||
title={title}
|
title={title}
|
||||||
editing={isEditing(name)}
|
editing={isEditing(name)}
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ import { Form, Formik } from 'formik'
|
||||||
import React, { useContext } from 'react'
|
import React, { useContext } from 'react'
|
||||||
import * as Yup from 'yup'
|
import * as Yup from 'yup'
|
||||||
|
|
||||||
|
import PromptWhenDirty from 'src/components/PromptWhenDirty'
|
||||||
import { TL2 } from 'src/components/typography'
|
import { TL2 } from 'src/components/typography'
|
||||||
|
|
||||||
import { Cashbox } from '../../../components/inputs/cashbox/Cashbox'
|
import { Cashbox } from '../../../components/inputs/cashbox/Cashbox'
|
||||||
|
|
@ -59,6 +60,7 @@ const FiatBalance = ({
|
||||||
setEditing(NAME, false)
|
setEditing(NAME, false)
|
||||||
}}>
|
}}>
|
||||||
<Form className={classes.form}>
|
<Form className={classes.form}>
|
||||||
|
<PromptWhenDirty />
|
||||||
<Header
|
<Header
|
||||||
title="Cash out (Empty)"
|
title="Cash out (Empty)"
|
||||||
editing={editing}
|
editing={editing}
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ import React, { useState } from 'react'
|
||||||
import * as Yup from 'yup'
|
import * as Yup from 'yup'
|
||||||
|
|
||||||
import ErrorMessage from 'src/components/ErrorMessage'
|
import ErrorMessage from 'src/components/ErrorMessage'
|
||||||
|
import PromptWhenDirty from 'src/components/PromptWhenDirty'
|
||||||
import { Link } from 'src/components/buttons'
|
import { Link } from 'src/components/buttons'
|
||||||
import Switch from 'src/components/inputs/base/Switch'
|
import Switch from 'src/components/inputs/base/Switch'
|
||||||
import { TextInput, NumberInput } from 'src/components/inputs/formik'
|
import { TextInput, NumberInput } from 'src/components/inputs/formik'
|
||||||
|
|
@ -167,12 +168,13 @@ const ContactInfo = () => {
|
||||||
name: 'phone',
|
name: 'phone',
|
||||||
label: 'Phone number',
|
label: 'Phone number',
|
||||||
value:
|
value:
|
||||||
info.phone &&
|
info.phone && locale.country
|
||||||
parsePhoneNumberFromString(
|
? parsePhoneNumberFromString(
|
||||||
info.phone,
|
info.phone,
|
||||||
locale.country
|
locale.country
|
||||||
).formatInternational(),
|
).formatInternational()
|
||||||
component: NumberInput
|
: '',
|
||||||
|
component: TextInput
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'email',
|
name: 'email',
|
||||||
|
|
@ -250,6 +252,7 @@ const ContactInfo = () => {
|
||||||
setError(null)
|
setError(null)
|
||||||
}}>
|
}}>
|
||||||
<Form>
|
<Form>
|
||||||
|
<PromptWhenDirty />
|
||||||
<div className={classes.row}>
|
<div className={classes.row}>
|
||||||
<Field
|
<Field
|
||||||
field={findField('name')}
|
field={findField('name')}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,13 @@
|
||||||
import { makeStyles } from '@material-ui/core'
|
import { makeStyles } from '@material-ui/core'
|
||||||
import Grid from '@material-ui/core/Grid'
|
import Grid from '@material-ui/core/Grid'
|
||||||
import React, { useState } from 'react'
|
import React from 'react'
|
||||||
|
import {
|
||||||
|
Route,
|
||||||
|
Switch,
|
||||||
|
Redirect,
|
||||||
|
useLocation,
|
||||||
|
useHistory
|
||||||
|
} from 'react-router-dom'
|
||||||
|
|
||||||
import Sidebar from 'src/components/layout/Sidebar'
|
import Sidebar from 'src/components/layout/Sidebar'
|
||||||
import TitleSection from 'src/components/layout/TitleSection'
|
import TitleSection from 'src/components/layout/TitleSection'
|
||||||
|
|
@ -24,34 +31,66 @@ const styles = {
|
||||||
|
|
||||||
const useStyles = makeStyles(styles)
|
const useStyles = makeStyles(styles)
|
||||||
|
|
||||||
const CONTACT_INFORMATION = 'Contact information'
|
const innerRoutes = [
|
||||||
const RECEIPT = 'Receipt'
|
{
|
||||||
const COIN_ATM_RADAR = 'Coin ATM Radar'
|
label: 'Contact information',
|
||||||
const TERMS_CONDITIONS = 'Terms & Conditions'
|
route: '/settings/operator-info/contact-info',
|
||||||
|
component: ContactInfo
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Receipt',
|
||||||
|
route: '/settings/operator-info/receipt-printing',
|
||||||
|
component: ReceiptPrinting
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Coin ATM Radar',
|
||||||
|
route: '/settings/operator-info/coin-atm-radar',
|
||||||
|
component: CoinAtmRadar
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Terms & Conditions',
|
||||||
|
route: '/settings/operator-info/terms-conditions',
|
||||||
|
component: TermsConditions
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
const pages = [CONTACT_INFORMATION, RECEIPT, COIN_ATM_RADAR, TERMS_CONDITIONS]
|
const Routes = () => (
|
||||||
|
<Switch>
|
||||||
|
<Redirect
|
||||||
|
exact
|
||||||
|
from="/settings/operator-info"
|
||||||
|
to="/settings/operator-info/contact-info"
|
||||||
|
/>
|
||||||
|
<Route exact path="/" />
|
||||||
|
{innerRoutes.map(({ route, component: Page, key }) => (
|
||||||
|
<Route path={route} key={key}>
|
||||||
|
<Page name={key} />
|
||||||
|
</Route>
|
||||||
|
))}
|
||||||
|
</Switch>
|
||||||
|
)
|
||||||
|
|
||||||
const OperatorInfo = () => {
|
const OperatorInfo = () => {
|
||||||
const [selected, setSelected] = useState(CONTACT_INFORMATION)
|
|
||||||
const classes = useStyles()
|
const classes = useStyles()
|
||||||
|
const history = useHistory()
|
||||||
|
const location = useLocation()
|
||||||
|
|
||||||
const isSelected = it => selected === it
|
const isSelected = it => location.pathname === it.route
|
||||||
|
|
||||||
|
const onClick = it => history.push(it.route)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<TitleSection title="Operator information"></TitleSection>
|
<TitleSection title="Operator information"></TitleSection>
|
||||||
<Grid container className={classes.grid}>
|
<Grid container className={classes.grid}>
|
||||||
<Sidebar
|
<Sidebar
|
||||||
data={pages}
|
data={innerRoutes}
|
||||||
isSelected={isSelected}
|
isSelected={isSelected}
|
||||||
displayName={it => it}
|
displayName={it => it.label}
|
||||||
onClick={it => setSelected(it)}
|
onClick={onClick}
|
||||||
/>
|
/>
|
||||||
<div className={classes.content}>
|
<div className={classes.content}>
|
||||||
{isSelected(CONTACT_INFORMATION) && <ContactInfo />}
|
<Routes />
|
||||||
{isSelected(RECEIPT) && <ReceiptPrinting />}
|
|
||||||
{isSelected(TERMS_CONDITIONS) && <TermsConditions />}
|
|
||||||
{isSelected(COIN_ATM_RADAR) && <CoinAtmRadar />}
|
|
||||||
</div>
|
</div>
|
||||||
</Grid>
|
</Grid>
|
||||||
</>
|
</>
|
||||||
|
|
|
||||||
|
|
@ -99,43 +99,35 @@ const ReceiptPrinting = memo(() => {
|
||||||
elements={[
|
elements={[
|
||||||
{
|
{
|
||||||
name: 'operatorWebsite',
|
name: 'operatorWebsite',
|
||||||
display: 'Operator website',
|
display: 'Operator website'
|
||||||
value: receiptPrintingConfig.operatorWebsite
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'operatorEmail',
|
name: 'operatorEmail',
|
||||||
display: 'Operator email',
|
display: 'Operator email'
|
||||||
value: receiptPrintingConfig.operatorEmail
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'operatorPhone',
|
name: 'operatorPhone',
|
||||||
display: 'Operator phone',
|
display: 'Operator phone'
|
||||||
value: receiptPrintingConfig.operatorPhone
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'companyNumber',
|
name: 'companyNumber',
|
||||||
display: 'Company number',
|
display: 'Company number'
|
||||||
value: receiptPrintingConfig.companyNumber
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'machineLocation',
|
name: 'machineLocation',
|
||||||
display: 'Machine location',
|
display: 'Machine location'
|
||||||
value: receiptPrintingConfig.machineLocation
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'customerNameOrPhoneNumber',
|
name: 'customerNameOrPhoneNumber',
|
||||||
display: 'Customer name or phone number (if known)',
|
display: 'Customer name or phone number (if known)'
|
||||||
value: receiptPrintingConfig.customerNameOrPhoneNumber
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'exchangeRate',
|
name: 'exchangeRate',
|
||||||
display: 'Exchange rate',
|
display: 'Exchange rate'
|
||||||
value: receiptPrintingConfig.exchangeRate
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'addressQRCode',
|
name: 'addressQRCode',
|
||||||
display: 'Address QR code',
|
display: 'Address QR code'
|
||||||
value: receiptPrintingConfig.addressQRCode
|
|
||||||
}
|
}
|
||||||
]}
|
]}
|
||||||
save={save}
|
save={save}
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ import React, { useState } from 'react'
|
||||||
import * as Yup from 'yup'
|
import * as Yup from 'yup'
|
||||||
|
|
||||||
import ErrorMessage from 'src/components/ErrorMessage'
|
import ErrorMessage from 'src/components/ErrorMessage'
|
||||||
|
import PromptWhenDirty from 'src/components/PromptWhenDirty'
|
||||||
import { Link } from 'src/components/buttons'
|
import { Link } from 'src/components/buttons'
|
||||||
import { Switch } from 'src/components/inputs'
|
import { Switch } from 'src/components/inputs'
|
||||||
import { TextInput } from 'src/components/inputs/formik'
|
import { TextInput } from 'src/components/inputs/formik'
|
||||||
|
|
@ -218,6 +219,7 @@ const TermsConditions = () => {
|
||||||
setError(null)
|
setError(null)
|
||||||
}}>
|
}}>
|
||||||
<Form>
|
<Form>
|
||||||
|
<PromptWhenDirty />
|
||||||
{fields.map((f, idx) => (
|
{fields.map((f, idx) => (
|
||||||
<div className={classes.row} key={idx}>
|
<div className={classes.row} key={idx}>
|
||||||
<Field
|
<Field
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue