feat: add advanced address blacklisting
This commit is contained in:
parent
473bb15c24
commit
8af7c97c16
7 changed files with 367 additions and 66 deletions
|
|
@ -1,16 +1,16 @@
|
||||||
|
const _ = require('lodash/fp')
|
||||||
|
|
||||||
const db = require('./db')
|
const db = require('./db')
|
||||||
const notifierQueries = require('./notifier/queries')
|
const notifierQueries = require('./notifier/queries')
|
||||||
|
|
||||||
// Get all blacklist rows from the DB "blacklist" table that were manually inserted by the operator
|
|
||||||
const getBlacklist = () => {
|
const getBlacklist = () => {
|
||||||
return db.any(`SELECT * FROM blacklist`).then(res =>
|
const blacklistSql = `SELECT * FROM blacklist`
|
||||||
res.map(item => ({
|
const messagesSql = `SELECT * FROM blacklist_messages`
|
||||||
address: item.address
|
return Promise.all([db.any(blacklistSql), db.any(messagesSql)])
|
||||||
}))
|
.then(([blacklist, messages]) => Promise.all([_.map(_.mapKeys(_.camelCase), blacklist), _.map(_.mapKeys(_.camelCase), messages)]))
|
||||||
)
|
.then(([blacklist, messages]) => _.map(it => ({ ...it, blacklistMessage: _.find(ite => it.blacklistMessageId === ite.id, messages) }), blacklist))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete row from blacklist table by crypto code and address
|
|
||||||
const deleteFromBlacklist = address => {
|
const deleteFromBlacklist = address => {
|
||||||
const sql = `DELETE FROM blacklist WHERE address = $1`
|
const sql = `DELETE FROM blacklist WHERE address = $1`
|
||||||
notifierQueries.clearBlacklistNotification(address)
|
notifierQueries.clearBlacklistNotification(address)
|
||||||
|
|
@ -38,10 +38,22 @@ function addToUsedAddresses (address) {
|
||||||
return db.oneOrNone(sql, [address])
|
return db.oneOrNone(sql, [address])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getMessages () {
|
||||||
|
const sql = `SELECT * FROM blacklist_messages`
|
||||||
|
return db.any(sql)
|
||||||
|
}
|
||||||
|
|
||||||
|
function editBlacklistMessage (id, content) {
|
||||||
|
const sql = `UPDATE blacklist_messages SET content = $1 WHERE id = $2 RETURNING id`
|
||||||
|
return db.oneOrNone(sql, [content, id])
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
blocked,
|
blocked,
|
||||||
addToUsedAddresses,
|
addToUsedAddresses,
|
||||||
getBlacklist,
|
getBlacklist,
|
||||||
deleteFromBlacklist,
|
deleteFromBlacklist,
|
||||||
insertIntoBlacklist
|
insertIntoBlacklist,
|
||||||
|
getMessages,
|
||||||
|
editBlacklistMessage
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,13 +2,16 @@ const blacklist = require('../../../blacklist')
|
||||||
|
|
||||||
const resolvers = {
|
const resolvers = {
|
||||||
Query: {
|
Query: {
|
||||||
blacklist: () => blacklist.getBlacklist()
|
blacklist: () => blacklist.getBlacklist(),
|
||||||
|
blacklistMessages: () => blacklist.getMessages()
|
||||||
},
|
},
|
||||||
Mutation: {
|
Mutation: {
|
||||||
deleteBlacklistRow: (...[, { address }]) =>
|
deleteBlacklistRow: (...[, { address }]) =>
|
||||||
blacklist.deleteFromBlacklist(address),
|
blacklist.deleteFromBlacklist(address),
|
||||||
insertBlacklistRow: (...[, { address }]) =>
|
insertBlacklistRow: (...[, { address }]) =>
|
||||||
blacklist.insertIntoBlacklist(address)
|
blacklist.insertIntoBlacklist(address),
|
||||||
|
editBlacklistMessage: (...[, { id, content }]) =>
|
||||||
|
blacklist.editBlacklistMessage(id, content)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,15 +3,25 @@ const { gql } = require('apollo-server-express')
|
||||||
const typeDef = gql`
|
const typeDef = gql`
|
||||||
type Blacklist {
|
type Blacklist {
|
||||||
address: String!
|
address: String!
|
||||||
|
blacklistMessage: BlacklistMessage!
|
||||||
|
}
|
||||||
|
|
||||||
|
type BlacklistMessage {
|
||||||
|
id: ID
|
||||||
|
label: String
|
||||||
|
content: String
|
||||||
|
allowToggle: Boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
type Query {
|
type Query {
|
||||||
blacklist: [Blacklist] @auth
|
blacklist: [Blacklist] @auth
|
||||||
|
blacklistMessages: [BlacklistMessage] @auth
|
||||||
}
|
}
|
||||||
|
|
||||||
type Mutation {
|
type Mutation {
|
||||||
deleteBlacklistRow(address: String!): Blacklist @auth
|
deleteBlacklistRow(address: String!): Blacklist @auth
|
||||||
insertBlacklistRow(address: String!): Blacklist @auth
|
insertBlacklistRow(address: String!): Blacklist @auth
|
||||||
|
editBlacklistMessage(id: ID, content: String): BlacklistMessage @auth
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
|
|
|
||||||
26
migrations/1664916753772-advanced-blacklisting.js
Normal file
26
migrations/1664916753772-advanced-blacklisting.js
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
const uuid = require('uuid')
|
||||||
|
|
||||||
|
var db = require('./db')
|
||||||
|
|
||||||
|
exports.up = function (next) {
|
||||||
|
const defaultMessageId = uuid.v4()
|
||||||
|
|
||||||
|
var sql = [
|
||||||
|
`CREATE TABLE blacklist_messages (
|
||||||
|
id UUID PRIMARY KEY,
|
||||||
|
label TEXT NOT NULL,
|
||||||
|
content TEXT NOT NULL,
|
||||||
|
allow_toggle BOOLEAN NOT NULL DEFAULT true
|
||||||
|
)`,
|
||||||
|
`INSERT INTO blacklist_messages (id, label, content, allow_toggle) VALUES ('${defaultMessageId}', 'Suspicious address', 'This address may be associated with a deceptive offer or a prohibited group. Please make sure you''re using an address from your own wallet.', false)`,
|
||||||
|
`ALTER TABLE blacklist ADD COLUMN blacklist_message_id UUID REFERENCES blacklist_messages(id)`,
|
||||||
|
`UPDATE blacklist SET blacklist_message_id = '${defaultMessageId}'`,
|
||||||
|
`ALTER TABLE blacklist ALTER COLUMN blacklist_message_id SET NOT NULL`
|
||||||
|
]
|
||||||
|
|
||||||
|
db.multi(sql, next)
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.down = function (next) {
|
||||||
|
next()
|
||||||
|
}
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
import { useQuery, useMutation } from '@apollo/react-hooks'
|
import { useQuery, useMutation } from '@apollo/react-hooks'
|
||||||
import { addressDetector } from '@lamassu/coins'
|
import { addressDetector } from '@lamassu/coins'
|
||||||
import { Box, Dialog, DialogContent, DialogActions } from '@material-ui/core'
|
import { Box, Dialog, DialogContent, DialogActions } from '@material-ui/core'
|
||||||
import Grid from '@material-ui/core/Grid'
|
|
||||||
import { makeStyles } from '@material-ui/core/styles'
|
import { makeStyles } from '@material-ui/core/styles'
|
||||||
import gql from 'graphql-tag'
|
import gql from 'graphql-tag'
|
||||||
import * as R from 'ramda'
|
import * as R from 'ramda'
|
||||||
|
|
@ -13,9 +12,12 @@ import { Switch } from 'src/components/inputs'
|
||||||
import TitleSection from 'src/components/layout/TitleSection'
|
import TitleSection from 'src/components/layout/TitleSection'
|
||||||
import { H2, Label2, P, Info3, Info2 } from 'src/components/typography'
|
import { H2, Label2, P, Info3, Info2 } from 'src/components/typography'
|
||||||
import { ReactComponent as CloseIcon } from 'src/styling/icons/action/close/zodiac.svg'
|
import { ReactComponent as CloseIcon } from 'src/styling/icons/action/close/zodiac.svg'
|
||||||
|
import { ReactComponent as ReverseSettingsIcon } from 'src/styling/icons/circle buttons/settings/white.svg'
|
||||||
|
import { ReactComponent as SettingsIcon } from 'src/styling/icons/circle buttons/settings/zodiac.svg'
|
||||||
import { fromNamespace, toNamespace } from 'src/utils/config'
|
import { fromNamespace, toNamespace } from 'src/utils/config'
|
||||||
|
|
||||||
import styles from './Blacklist.styles'
|
import styles from './Blacklist.styles'
|
||||||
|
import BlackListAdvanced from './BlacklistAdvanced'
|
||||||
import BlackListModal from './BlacklistModal'
|
import BlackListModal from './BlacklistModal'
|
||||||
import BlacklistTable from './BlacklistTable'
|
import BlacklistTable from './BlacklistTable'
|
||||||
|
|
||||||
|
|
@ -33,6 +35,11 @@ const GET_BLACKLIST = gql`
|
||||||
query getBlacklistData {
|
query getBlacklistData {
|
||||||
blacklist {
|
blacklist {
|
||||||
address
|
address
|
||||||
|
blacklistMessage {
|
||||||
|
id
|
||||||
|
label
|
||||||
|
content
|
||||||
|
}
|
||||||
}
|
}
|
||||||
cryptoCurrencies {
|
cryptoCurrencies {
|
||||||
display
|
display
|
||||||
|
|
@ -61,6 +68,25 @@ const ADD_ROW = gql`
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
|
const GET_BLACKLIST_MESSAGES = gql`
|
||||||
|
query getBlacklistMessages {
|
||||||
|
blacklistMessages {
|
||||||
|
id
|
||||||
|
label
|
||||||
|
content
|
||||||
|
allowToggle
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
const EDIT_BLACKLIST_MESSAGE = gql`
|
||||||
|
mutation editBlacklistMessage($id: ID, $content: String) {
|
||||||
|
editBlacklistMessage(id: $id, content: $content) {
|
||||||
|
id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
const PaperWalletDialog = ({ onConfirmed, onDissmised, open, props }) => {
|
const PaperWalletDialog = ({ onConfirmed, onDissmised, open, props }) => {
|
||||||
const classes = useStyles()
|
const classes = useStyles()
|
||||||
|
|
||||||
|
|
@ -106,10 +132,13 @@ const PaperWalletDialog = ({ onConfirmed, onDissmised, open, props }) => {
|
||||||
const Blacklist = () => {
|
const Blacklist = () => {
|
||||||
const { data: blacklistResponse } = useQuery(GET_BLACKLIST)
|
const { data: blacklistResponse } = useQuery(GET_BLACKLIST)
|
||||||
const { data: configData } = useQuery(GET_INFO)
|
const { data: configData } = useQuery(GET_INFO)
|
||||||
|
const { data: messagesResponse, refetch } = useQuery(GET_BLACKLIST_MESSAGES)
|
||||||
const [showModal, setShowModal] = useState(false)
|
const [showModal, setShowModal] = useState(false)
|
||||||
const [errorMsg, setErrorMsg] = useState(null)
|
const [errorMsg, setErrorMsg] = useState(null)
|
||||||
|
const [editMessageError, setEditMessageError] = useState(null)
|
||||||
const [deleteDialog, setDeleteDialog] = useState(false)
|
const [deleteDialog, setDeleteDialog] = useState(false)
|
||||||
const [confirmDialog, setConfirmDialog] = useState(false)
|
const [confirmDialog, setConfirmDialog] = useState(false)
|
||||||
|
const [advancedSettings, setAdvancedSettings] = useState(false)
|
||||||
|
|
||||||
const [deleteEntry] = useMutation(DELETE_ROW, {
|
const [deleteEntry] = useMutation(DELETE_ROW, {
|
||||||
onError: ({ message }) => {
|
onError: ({ message }) => {
|
||||||
|
|
@ -129,6 +158,11 @@ const Blacklist = () => {
|
||||||
refetchQueries: () => ['getData']
|
refetchQueries: () => ['getData']
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const [editMessage] = useMutation(EDIT_BLACKLIST_MESSAGE, {
|
||||||
|
onError: e => setEditMessageError(e),
|
||||||
|
refetchQueries: () => ['getBlacklistData']
|
||||||
|
})
|
||||||
|
|
||||||
const classes = useStyles()
|
const classes = useStyles()
|
||||||
|
|
||||||
const blacklistData = R.path(['blacklist'])(blacklistResponse) ?? []
|
const blacklistData = R.path(['blacklist'])(blacklistResponse) ?? []
|
||||||
|
|
@ -184,6 +218,15 @@ const Blacklist = () => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const editBlacklistMessage = r => {
|
||||||
|
editMessage({
|
||||||
|
variables: {
|
||||||
|
id: r.id,
|
||||||
|
content: r.content
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<PaperWalletDialog
|
<PaperWalletDialog
|
||||||
|
|
@ -193,7 +236,17 @@ const Blacklist = () => {
|
||||||
setConfirmDialog(false)
|
setConfirmDialog(false)
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<TitleSection title="Blacklisted addresses">
|
<TitleSection
|
||||||
|
title="Blacklisted addresses"
|
||||||
|
buttons={[
|
||||||
|
{
|
||||||
|
text: 'Advanced settings',
|
||||||
|
icon: SettingsIcon,
|
||||||
|
inverseIcon: ReverseSettingsIcon,
|
||||||
|
toggle: setAdvancedSettings
|
||||||
|
}
|
||||||
|
]}>
|
||||||
|
{!advancedSettings && (
|
||||||
<Box display="flex" alignItems="center" justifyContent="flex-end">
|
<Box display="flex" alignItems="center" justifyContent="flex-end">
|
||||||
<Box
|
<Box
|
||||||
display="flex"
|
display="flex"
|
||||||
|
|
@ -238,8 +291,8 @@ const Blacklist = () => {
|
||||||
<HelpTooltip width={304}>
|
<HelpTooltip width={304}>
|
||||||
<P>
|
<P>
|
||||||
The "Reject reused addresses" option means that all addresses
|
The "Reject reused addresses" option means that all addresses
|
||||||
that are used once will be automatically rejected if there's an
|
that are used once will be automatically rejected if there's
|
||||||
attempt to use them again on a new transaction.
|
an attempt to use them again on a new transaction.
|
||||||
</P>
|
</P>
|
||||||
</HelpTooltip>
|
</HelpTooltip>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
@ -247,8 +300,9 @@ const Blacklist = () => {
|
||||||
Blacklist new addresses
|
Blacklist new addresses
|
||||||
</Link>
|
</Link>
|
||||||
</Box>
|
</Box>
|
||||||
|
)}
|
||||||
</TitleSection>
|
</TitleSection>
|
||||||
<Grid container className={classes.grid}>
|
{!advancedSettings && (
|
||||||
<div className={classes.content}>
|
<div className={classes.content}>
|
||||||
<BlacklistTable
|
<BlacklistTable
|
||||||
data={blacklistData}
|
data={blacklistData}
|
||||||
|
|
@ -259,7 +313,15 @@ const Blacklist = () => {
|
||||||
setDeleteDialog={setDeleteDialog}
|
setDeleteDialog={setDeleteDialog}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</Grid>
|
)}
|
||||||
|
{advancedSettings && (
|
||||||
|
<BlackListAdvanced
|
||||||
|
data={messagesResponse}
|
||||||
|
editBlacklistMessage={editBlacklistMessage}
|
||||||
|
mutationError={editMessageError}
|
||||||
|
onClose={() => refetch()}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
{showModal && (
|
{showModal && (
|
||||||
<BlackListModal
|
<BlackListModal
|
||||||
onClose={() => {
|
onClose={() => {
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,14 @@ const styles = {
|
||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
flex: 1
|
flex: 1
|
||||||
},
|
},
|
||||||
|
advancedForm: {
|
||||||
|
'& > *': {
|
||||||
|
marginTop: 20
|
||||||
|
},
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
height: '100%'
|
||||||
|
},
|
||||||
footer: {
|
footer: {
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
|
|
@ -58,6 +66,9 @@ const styles = {
|
||||||
cancelButton: {
|
cancelButton: {
|
||||||
marginRight: 8,
|
marginRight: 8,
|
||||||
padding: 0
|
padding: 0
|
||||||
|
},
|
||||||
|
resetToDefault: {
|
||||||
|
width: 145
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
177
new-lamassu-admin/src/pages/Blacklist/BlacklistAdvanced.js
Normal file
177
new-lamassu-admin/src/pages/Blacklist/BlacklistAdvanced.js
Normal file
|
|
@ -0,0 +1,177 @@
|
||||||
|
import { makeStyles } from '@material-ui/core/styles'
|
||||||
|
import { Form, Formik, Field } from 'formik'
|
||||||
|
import * as R from 'ramda'
|
||||||
|
import React, { useState } from 'react'
|
||||||
|
import * as Yup from 'yup'
|
||||||
|
|
||||||
|
import ErrorMessage from 'src/components/ErrorMessage'
|
||||||
|
import Modal from 'src/components/Modal'
|
||||||
|
import { ActionButton, IconButton, Button } from 'src/components/buttons'
|
||||||
|
import { TextInput } from 'src/components/inputs/formik'
|
||||||
|
import DataTable from 'src/components/tables/DataTable'
|
||||||
|
import { ReactComponent as DisabledDeleteIcon } from 'src/styling/icons/action/delete/disabled.svg'
|
||||||
|
import { ReactComponent as DeleteIcon } from 'src/styling/icons/action/delete/enabled.svg'
|
||||||
|
import { ReactComponent as EditIcon } from 'src/styling/icons/action/edit/enabled.svg'
|
||||||
|
import { ReactComponent as DefaultIconReverse } from 'src/styling/icons/button/retry/white.svg'
|
||||||
|
import { ReactComponent as DefaultIcon } from 'src/styling/icons/button/retry/zodiac.svg'
|
||||||
|
|
||||||
|
import styles from './Blacklist.styles'
|
||||||
|
|
||||||
|
const useStyles = makeStyles(styles)
|
||||||
|
|
||||||
|
const DEFAULT_MESSAGE = `This address may be associated with a deceptive offer or a prohibited group. Please make sure you're using an address from your own wallet.`
|
||||||
|
|
||||||
|
const getErrorMsg = (formikErrors, formikTouched, mutationError) => {
|
||||||
|
if (!formikErrors || !formikTouched) return null
|
||||||
|
if (mutationError) return 'Internal server error'
|
||||||
|
if (formikErrors.event && formikTouched.event) return formikErrors.event
|
||||||
|
if (formikErrors.message && formikTouched.message) return formikErrors.message
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
const BlacklistAdvanced = ({
|
||||||
|
data,
|
||||||
|
editBlacklistMessage,
|
||||||
|
onClose,
|
||||||
|
mutationError
|
||||||
|
}) => {
|
||||||
|
const classes = useStyles()
|
||||||
|
const [selectedMessage, setSelectedMessage] = useState(null)
|
||||||
|
|
||||||
|
const elements = [
|
||||||
|
{
|
||||||
|
name: 'label',
|
||||||
|
header: 'Label',
|
||||||
|
width: 250,
|
||||||
|
textAlign: 'left',
|
||||||
|
size: 'sm',
|
||||||
|
view: it => R.path(['label'], it)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'content',
|
||||||
|
header: 'Content',
|
||||||
|
width: 690,
|
||||||
|
textAlign: 'left',
|
||||||
|
size: 'sm',
|
||||||
|
view: it => R.path(['content'], it)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'edit',
|
||||||
|
header: 'Edit',
|
||||||
|
width: 130,
|
||||||
|
textAlign: 'center',
|
||||||
|
size: 'sm',
|
||||||
|
view: it => (
|
||||||
|
<IconButton
|
||||||
|
className={classes.deleteButton}
|
||||||
|
onClick={() => setSelectedMessage(it)}>
|
||||||
|
<EditIcon />
|
||||||
|
</IconButton>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'deleteButton',
|
||||||
|
header: 'Delete',
|
||||||
|
width: 130,
|
||||||
|
textAlign: 'center',
|
||||||
|
size: 'sm',
|
||||||
|
view: it => (
|
||||||
|
<IconButton
|
||||||
|
className={classes.deleteButton}
|
||||||
|
disabled={
|
||||||
|
!R.isNil(R.path(['allowToggle']) && !R.path(['allowToggle'], it))
|
||||||
|
}>
|
||||||
|
{R.path(['allowToggle'], it) ? (
|
||||||
|
<DeleteIcon />
|
||||||
|
) : (
|
||||||
|
<DisabledDeleteIcon />
|
||||||
|
)}
|
||||||
|
</IconButton>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
const handleModalClose = () => {
|
||||||
|
setSelectedMessage(null)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleSubmit = values => {
|
||||||
|
editBlacklistMessage(values)
|
||||||
|
handleModalClose()
|
||||||
|
!R.isNil(onClose) && onClose()
|
||||||
|
}
|
||||||
|
|
||||||
|
const initialValues = {
|
||||||
|
label: !R.isNil(selectedMessage) ? selectedMessage.label : '',
|
||||||
|
content: !R.isNil(selectedMessage) ? selectedMessage.content : ''
|
||||||
|
}
|
||||||
|
|
||||||
|
const validationSchema = Yup.object().shape({
|
||||||
|
label: Yup.string().required('A label is required!'),
|
||||||
|
content: Yup.string()
|
||||||
|
.required('The message content is required!')
|
||||||
|
.trim()
|
||||||
|
})
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<DataTable
|
||||||
|
data={R.path(['blacklistMessages'], data)}
|
||||||
|
elements={elements}
|
||||||
|
emptyText="No blacklisted addresses so far"
|
||||||
|
name="blacklistTable"
|
||||||
|
/>
|
||||||
|
{selectedMessage && (
|
||||||
|
<Modal
|
||||||
|
title={`Blacklist message - ${selectedMessage?.label}`}
|
||||||
|
open={true}
|
||||||
|
width={676}
|
||||||
|
height={400}
|
||||||
|
handleClose={handleModalClose}>
|
||||||
|
<Formik
|
||||||
|
validateOnBlur={false}
|
||||||
|
validateOnChange={false}
|
||||||
|
initialValues={initialValues}
|
||||||
|
validationSchema={validationSchema}
|
||||||
|
onSubmit={values =>
|
||||||
|
handleSubmit({ id: selectedMessage.id, ...values })
|
||||||
|
}>
|
||||||
|
{({ errors, touched, setFieldValue }) => (
|
||||||
|
<Form className={classes.advancedForm}>
|
||||||
|
<ActionButton
|
||||||
|
color="primary"
|
||||||
|
Icon={DefaultIcon}
|
||||||
|
InverseIcon={DefaultIconReverse}
|
||||||
|
className={classes.resetToDefault}
|
||||||
|
type="button"
|
||||||
|
onClick={() => setFieldValue('content', DEFAULT_MESSAGE)}>
|
||||||
|
Reset to default
|
||||||
|
</ActionButton>
|
||||||
|
<Field
|
||||||
|
name="content"
|
||||||
|
label="Message content"
|
||||||
|
fullWidth
|
||||||
|
multiline={true}
|
||||||
|
rows={6}
|
||||||
|
component={TextInput}
|
||||||
|
/>
|
||||||
|
<div className={classes.footer}>
|
||||||
|
{getErrorMsg(errors, touched, mutationError) && (
|
||||||
|
<ErrorMessage>
|
||||||
|
{getErrorMsg(errors, touched, mutationError)}
|
||||||
|
</ErrorMessage>
|
||||||
|
)}
|
||||||
|
<Button type="submit" className={classes.submit}>
|
||||||
|
Confirm
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</Form>
|
||||||
|
)}
|
||||||
|
</Formik>
|
||||||
|
</Modal>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default BlacklistAdvanced
|
||||||
Loading…
Add table
Add a link
Reference in a new issue