feat: error handling for the tables and wizards

This commit is contained in:
Taranto 2020-10-30 17:51:57 +00:00 committed by Josh Harvey
parent 7d5d963685
commit 96b8e3d9a2
10 changed files with 55 additions and 44 deletions

View file

@ -51,7 +51,6 @@ function checkHasLightning (settings) {
function poll (req, res, next) { function poll (req, res, next) {
const machineVersion = req.query.version const machineVersion = req.query.version
// TODO new-admin: pass this field from the machines
const machineModel = req.query.model const machineModel = req.query.model
const deviceId = req.deviceId const deviceId = req.deviceId
const deviceTime = req.deviceTime const deviceTime = req.deviceTime

View file

@ -13,7 +13,7 @@ const NamespacedTable = ({
...props ...props
}) => { }) => {
const innerSave = (...[, it]) => { const innerSave = (...[, it]) => {
save(toNamespace(it.id)(R.omit(['id2'], it))) return save(toNamespace(it.id)(R.omit(['id2'], it)))
} }
const innerData = R.map(it => ({ const innerData = R.map(it => ({

View file

@ -34,10 +34,15 @@ const ActionCol = ({ disabled, editing }) => {
onToggle, onToggle,
toggleWidth, toggleWidth,
forceAdd, forceAdd,
clearError,
actionColSize actionColSize
} = useContext(TableCtx) } = useContext(TableCtx)
const disableEdit = disabled || (disableRowEdit && disableRowEdit(values)) const disableEdit = disabled || (disableRowEdit && disableRowEdit(values))
const cancel = () => {
clearError()
resetForm()
}
return ( return (
<> <>
@ -51,7 +56,7 @@ const ActionCol = ({ disabled, editing }) => {
Save Save
</Link> </Link>
{!forceAdd && ( {!forceAdd && (
<Link color="secondary" onClick={resetForm}> <Link color="secondary" onClick={cancel}>
Cancel Cancel
</Link> </Link>
)} )}
@ -170,6 +175,7 @@ const ERow = ({ editing, disabled, lastOfGroup }) => {
elements, elements,
enableEdit, enableEdit,
enableDelete, enableDelete,
error,
enableToggle, enableToggle,
rowSize, rowSize,
stripeWhen stripeWhen
@ -199,13 +205,18 @@ const ERow = ({ editing, disabled, lastOfGroup }) => {
} }
const touchedErrors = R.pick(R.keys(touched), errors) const touchedErrors = R.pick(R.keys(touched), errors)
const hasTouchedErrors = touchedErrors && R.keys(touchedErrors).length > 0
const hasErrors = hasTouchedErrors || !!error
const errorMessage =
error || (touchedErrors && R.values(touchedErrors).join(', '))
return ( return (
<Tr <Tr
className={classnames(classNames)} className={classnames(classNames)}
size={rowSize} size={rowSize}
error={touchedErrors && R.keys(touchedErrors).length > 0} error={editing && hasErrors}
errorMessage={touchedErrors && R.values(touchedErrors).join(', ')}> errorMessage={errorMessage}>
{innerElements.map((it, idx) => { {innerElements.map((it, idx) => {
return ( return (
<ECol <ECol

View file

@ -32,6 +32,7 @@ const ETable = ({
elements = [], elements = [],
data = [], data = [],
save, save,
error: externalError,
rowSize = 'md', rowSize = 'md',
validationSchema, validationSchema,
enableCreate, enableCreate,
@ -58,8 +59,14 @@ const ETable = ({
const [editingId, setEditingId] = useState(null) const [editingId, setEditingId] = useState(null)
const [adding, setAdding] = useState(false) const [adding, setAdding] = useState(false)
const [saving, setSaving] = useState(false) const [saving, setSaving] = useState(false)
const [error, setError] = useState(null)
useEffect(() => setError(externalError), [externalError])
useEffect(() => {
setError(null)
setAdding(forceAdd)
}, [forceAdd])
useEffect(() => setAdding(forceAdd), [forceAdd])
const innerSave = async value => { const innerSave = async value => {
if (saving) return if (saving) return
@ -70,9 +77,9 @@ const ETable = ({
const list = index !== -1 ? R.update(index, it, data) : R.prepend(it, data) const list = index !== -1 ? R.update(index, it, data) : R.prepend(it, data)
if (!R.equals(data[index], it)) { if (!R.equals(data[index], it)) {
// no response means the save failed try {
const response = await save({ [name]: list }, it) await save({ [name]: list }, it)
if (!response) { } catch (err) {
setSaving(false) setSaving(false)
return return
} }
@ -98,11 +105,13 @@ const ETable = ({
const onEdit = it => { const onEdit = it => {
if (shouldOverrideEdit && shouldOverrideEdit(it)) return editOverride(it) if (shouldOverrideEdit && shouldOverrideEdit(it)) return editOverride(it)
setEditingId(it) setEditingId(it)
setError(null)
setEditing && setEditing(it, true) setEditing && setEditing(it, true)
} }
const addField = () => { const addField = () => {
setAdding(true) setAdding(true)
setError(null)
setEditing && setEditing(true, true) setEditing && setEditing(true, true)
} }
@ -129,6 +138,8 @@ const ETable = ({
elements, elements,
enableEdit, enableEdit,
onEdit, onEdit,
clearError: () => setError(null),
error: error,
disableRowEdit, disableRowEdit,
editWidth, editWidth,
enableDelete, enableDelete,

View file

@ -48,18 +48,15 @@ const GET_INFO = gql`
const CashOut = ({ name: SCREEN_KEY }) => { const CashOut = ({ name: SCREEN_KEY }) => {
const classes = useStyles() const classes = useStyles()
const [wizard, setWizard] = useState(false) const [wizard, setWizard] = useState(false)
const [error, setError] = useState(false)
const { data } = useQuery(GET_INFO) const { data } = useQuery(GET_INFO)
const [saveConfig] = useMutation(SAVE_CONFIG, { const [saveConfig, { error }] = useMutation(SAVE_CONFIG, {
onCompleted: () => setWizard(false), onCompleted: () => setWizard(false),
onError: () => setError(true),
refetchQueries: () => ['getData'] refetchQueries: () => ['getData']
}) })
const save = (rawConfig, accounts) => { const save = (rawConfig, accounts) => {
const config = toNamespace(SCREEN_KEY)(rawConfig) const config = toNamespace(SCREEN_KEY)(rawConfig)
setError(false)
return saveConfig({ variables: { config, accounts } }) return saveConfig({ variables: { config, accounts } })
} }
@ -76,7 +73,7 @@ const CashOut = ({ name: SCREEN_KEY }) => {
return ( return (
<> <>
<TitleSection title="Cash-out" error={error}> <TitleSection title="Cash-out">
<div className={classes.fudgeFactor}> <div className={classes.fudgeFactor}>
<P>Transaction fudge factor</P> <P>Transaction fudge factor</P>
<Switch <Switch
@ -102,7 +99,6 @@ const CashOut = ({ name: SCREEN_KEY }) => {
</div> </div>
</TitleSection> </TitleSection>
<EditableTable <EditableTable
name="test"
namespaces={R.map(R.path(['deviceId']))(machines)} namespaces={R.map(R.path(['deviceId']))(machines)}
data={config} data={config}
stripeWhen={it => !DenominationsSchema.isValidSync(it)} stripeWhen={it => !DenominationsSchema.isValidSync(it)}
@ -112,6 +108,7 @@ const CashOut = ({ name: SCREEN_KEY }) => {
toggleWidth={109} toggleWidth={109}
onToggle={onToggle} onToggle={onToggle}
save={save} save={save}
error={error?.message}
validationSchema={DenominationsSchema} validationSchema={DenominationsSchema}
disableRowEdit={R.compose(R.not, R.path(['active']))} disableRowEdit={R.compose(R.not, R.path(['active']))}
elements={getElements(machines, locale)} elements={getElements(machines, locale)}
@ -121,7 +118,7 @@ const CashOut = ({ name: SCREEN_KEY }) => {
machine={R.find(R.propEq('deviceId', wizard))(machines)} machine={R.find(R.propEq('deviceId', wizard))(machines)}
onClose={() => setWizard(false)} onClose={() => setWizard(false)}
save={save} save={save}
error={error} error={error?.message}
locale={locale} locale={locale}
/> />
)} )}

View file

@ -41,7 +41,7 @@ const Commissions = ({ name: SCREEN_KEY }) => {
const [isEditingDefault, setEditingDefault] = useState(false) const [isEditingDefault, setEditingDefault] = useState(false)
const [isEditingOverrides, setEditingOverrides] = useState(false) const [isEditingOverrides, setEditingOverrides] = useState(false)
const { data } = useQuery(GET_DATA) const { data } = useQuery(GET_DATA)
const [saveConfig] = useMutation(SAVE_CONFIG, { const [saveConfig, { error }] = useMutation(SAVE_CONFIG, {
refetchQueries: () => ['getData'] refetchQueries: () => ['getData']
}) })
@ -76,6 +76,7 @@ const Commissions = ({ name: SCREEN_KEY }) => {
<TitleSection title="Commissions" /> <TitleSection title="Commissions" />
<Section> <Section>
<EditableTable <EditableTable
error={error?.message}
title="Default setup" title="Default setup"
rowSize="lg" rowSize="lg"
titleLg titleLg
@ -92,6 +93,7 @@ const Commissions = ({ name: SCREEN_KEY }) => {
</Section> </Section>
<Section> <Section>
<EditableTable <EditableTable
error={error?.message}
title="Overrides" title="Overrides"
titleLg titleLg
name="overrides" name="overrides"

View file

@ -102,13 +102,11 @@ const Locales = ({ name: SCREEN_KEY }) => {
const [cancelCryptoConfiguration, setCancelCryptoConfiguration] = useState( const [cancelCryptoConfiguration, setCancelCryptoConfiguration] = useState(
null null
) )
const [error, setError] = useState(false)
const [isEditingDefault, setEditingDefault] = useState(false) const [isEditingDefault, setEditingDefault] = useState(false)
const [isEditingOverrides, setEditingOverrides] = useState(false) const [isEditingOverrides, setEditingOverrides] = useState(false)
const { data } = useQuery(GET_DATA) const { data } = useQuery(GET_DATA)
const [saveConfig] = useMutation(SAVE_CONFIG, { const [saveConfig, { error }] = useMutation(SAVE_CONFIG, {
onCompleted: () => setWizard(false), onCompleted: () => setWizard(false),
onError: () => setError(true),
refetchQueries: () => ['getData'] refetchQueries: () => ['getData']
}) })
@ -130,14 +128,14 @@ const Locales = ({ name: SCREEN_KEY }) => {
config.fiatCurrency && config.fiatCurrency &&
newConfig.locale_fiatCurrency !== config.fiatCurrency newConfig.locale_fiatCurrency !== config.fiatCurrency
) )
setDataToSave(newConfig) return setDataToSave(newConfig)
else save(newConfig)
return save(newConfig)
} }
const save = config => { const save = config => {
setDataToSave(null) setDataToSave(null)
setError(false) return saveConfig({ variables: { config } })
saveConfig({ variables: { config } })
} }
const saveOverrides = it => { const saveOverrides = it => {
@ -169,6 +167,7 @@ const Locales = ({ name: SCREEN_KEY }) => {
<Section> <Section>
<EditableTable <EditableTable
title="Default settings" title="Default settings"
error={error?.message}
titleLg titleLg
name="locale" name="locale"
enableEdit enableEdit
@ -183,6 +182,7 @@ const Locales = ({ name: SCREEN_KEY }) => {
</Section> </Section>
<Section> <Section>
<EditableTable <EditableTable
error={error?.message}
title="Overrides" title="Overrides"
titleLg titleLg
name="overrides" name="overrides"
@ -213,7 +213,7 @@ const Locales = ({ name: SCREEN_KEY }) => {
save(toNamespace(namespaces.WALLETS)(rawConfig)) save(toNamespace(namespaces.WALLETS)(rawConfig))
setCancelCryptoConfiguration(null) setCancelCryptoConfiguration(null)
}} }}
error={error} error={error?.message}
cryptoCurrencies={cryptoCurrencies} cryptoCurrencies={cryptoCurrencies}
userAccounts={data?.config?.accounts} userAccounts={data?.config?.accounts}
accounts={accounts} accounts={accounts}

View file

@ -67,13 +67,8 @@ const CashCassettes = () => {
const { data } = useQuery(GET_MACHINES_AND_CONFIG) const { data } = useQuery(GET_MACHINES_AND_CONFIG)
const [resetCashOut] = useMutation(RESET_CASHOUT_BILLS, { const [resetCashOut, { error }] = useMutation(RESET_CASHOUT_BILLS, {
refetchQueries: () => ['getData'], refetchQueries: () => ['getData']
onError: ({ graphQLErrors, message }) => {
const errorMessage = graphQLErrors[0] ? graphQLErrors[0].message : message
// TODO new-admin : this should not be final
alert(JSON.stringify(errorMessage))
}
}) })
const cashout = data?.config && fromNamespace('cashOut')(data.config) const cashout = data?.config && fromNamespace('cashOut')(data.config)
@ -145,6 +140,7 @@ const CashCassettes = () => {
<TitleSection title="Cash Cassettes" /> <TitleSection title="Cash Cassettes" />
<EditableTable <EditableTable
error={error?.message}
name="cashboxes" name="cashboxes"
enableEdit enableEdit
stripeWhen={isCashOutDisabled} stripeWhen={isCashOutDisabled}

View file

@ -34,7 +34,6 @@ const GET_INFO = gql`
const Triggers = () => { const Triggers = () => {
const classes = useStyles() const classes = useStyles()
const [wizard, setWizard] = useState(false) const [wizard, setWizard] = useState(false)
const [error, setError] = useState(false)
const { data, loading } = useQuery(GET_INFO) const { data, loading } = useQuery(GET_INFO)
const triggers = fromServer(data?.config?.triggers ?? []) const triggers = fromServer(data?.config?.triggers ?? [])
@ -43,9 +42,8 @@ const Triggers = () => {
data?.config && fromNamespace('compliance')(data.config) data?.config && fromNamespace('compliance')(data.config)
const rejectAddressReuse = complianceConfig?.rejectAddressReuse ?? false const rejectAddressReuse = complianceConfig?.rejectAddressReuse ?? false
const [saveConfig] = useMutation(SAVE_CONFIG, { const [saveConfig, { error }] = useMutation(SAVE_CONFIG, {
onCompleted: () => setWizard(false), onCompleted: () => setWizard(false),
onError: () => setError(true),
refetchQueries: () => ['getData'] refetchQueries: () => ['getData']
}) })
@ -53,7 +51,6 @@ const Triggers = () => {
const toSave = R.concat([{ id: v4(), direction: 'both', ...rawConfig }])( const toSave = R.concat([{ id: v4(), direction: 'both', ...rawConfig }])(
triggers triggers
) )
setError(false)
return saveConfig({ variables: { config: { triggers: toServer(toSave) } } }) return saveConfig({ variables: { config: { triggers: toServer(toSave) } } })
} }
@ -63,7 +60,6 @@ const Triggers = () => {
} }
const save = config => { const save = config => {
setError(false)
return saveConfig({ return saveConfig({
variables: { config: { triggers: toServer(config.triggers) } } variables: { config: { triggers: toServer(config.triggers) } }
}) })
@ -121,6 +117,7 @@ const Triggers = () => {
sortBy={sortBy} sortBy={sortBy}
groupBy="triggerType" groupBy="triggerType"
enableDelete enableDelete
error={error?.message}
save={save} save={save}
validationSchema={Schema} validationSchema={Schema}
elements={getElements(currency, classes)} elements={getElements(currency, classes)}
@ -128,7 +125,7 @@ const Triggers = () => {
{wizard && ( {wizard && (
<Wizard <Wizard
currency={currency} currency={currency}
error={error} error={error?.message}
save={add} save={add}
onClose={() => setWizard(null)} onClose={() => setWizard(null)}
/> />

View file

@ -49,12 +49,10 @@ const Wallet = ({ name: SCREEN_KEY }) => {
null null
) )
const [wizard, setWizard] = useState(false) const [wizard, setWizard] = useState(false)
const [error, setError] = useState(false)
const { data } = useQuery(GET_INFO) const { data } = useQuery(GET_INFO)
const [saveConfig] = useMutation(SAVE_CONFIG, { const [saveConfig, { error }] = useMutation(SAVE_CONFIG, {
onCompleted: () => setWizard(false), onCompleted: () => setWizard(false),
onError: () => setError(true),
refetchQueries: () => ['getData'] refetchQueries: () => ['getData']
}) })
@ -65,7 +63,6 @@ const Wallet = ({ name: SCREEN_KEY }) => {
const save = (rawConfig, accounts) => { const save = (rawConfig, accounts) => {
const config = toNamespace(SCREEN_KEY)(rawConfig) const config = toNamespace(SCREEN_KEY)(rawConfig)
setError(false)
return saveConfig({ variables: { config, accounts } }) return saveConfig({ variables: { config, accounts } })
} }
@ -90,11 +87,12 @@ const Wallet = ({ name: SCREEN_KEY }) => {
return ( return (
<> <>
<TitleSection title="Wallet Settings" error={error} /> <TitleSection title="Wallet Settings" />
<EditableTable <EditableTable
name="test" name="test"
namespaces={R.map(R.path(['code']))(cryptoCurrencies)} namespaces={R.map(R.path(['code']))(cryptoCurrencies)}
data={config} data={config}
error={error?.message}
stripeWhen={it => !WalletSchema.isValidSync(it)} stripeWhen={it => !WalletSchema.isValidSync(it)}
enableEdit enableEdit
shouldOverrideEdit={shouldOverrideEdit} shouldOverrideEdit={shouldOverrideEdit}
@ -113,7 +111,7 @@ const Wallet = ({ name: SCREEN_KEY }) => {
coin={R.find(R.propEq('code', wizard))(cryptoCurrencies)} coin={R.find(R.propEq('code', wizard))(cryptoCurrencies)}
onClose={() => setWizard(false)} onClose={() => setWizard(false)}
save={save} save={save}
error={error} error={error?.message}
cryptoCurrencies={cryptoCurrencies} cryptoCurrencies={cryptoCurrencies}
userAccounts={data?.config?.accounts} userAccounts={data?.config?.accounts}
accounts={accounts} accounts={accounts}