summaryrefslogtreecommitdiff
path: root/packages/frontend
diff options
context:
space:
mode:
authorSebastian <sebasjm@gmail.com>2021-06-25 11:28:13 -0300
committerSebastian <sebasjm@gmail.com>2021-06-25 11:28:22 -0300
commit1ce7ccc04487406250cee603638950b80d6a779a (patch)
tree7ce159c27b7e896f31e893d18bb42502b0589610 /packages/frontend
parent81bda791096b23fbd677d885a04886a8d4290038 (diff)
downloadmerchant-backoffice-1ce7ccc04487406250cee603638950b80d6a779a.tar.gz
merchant-backoffice-1ce7ccc04487406250cee603638950b80d6a779a.tar.bz2
merchant-backoffice-1ce7ccc04487406250cee603638950b80d6a779a.zip
fix validation messages
Diffstat (limited to 'packages/frontend')
-rw-r--r--packages/frontend/src/components/form/InputArray.tsx11
-rw-r--r--packages/frontend/src/components/form/InputDuration.tsx8
-rw-r--r--packages/frontend/src/components/instance/DefaultInstanceFormFields.tsx2
-rw-r--r--packages/frontend/src/components/picker/DurationPicker.tsx4
-rw-r--r--packages/frontend/src/paths/admin/create/CreatePage.tsx67
-rw-r--r--packages/frontend/src/paths/instance/update/UpdatePage.tsx56
6 files changed, 118 insertions, 30 deletions
diff --git a/packages/frontend/src/components/form/InputArray.tsx b/packages/frontend/src/components/form/InputArray.tsx
index 211d995..9f16cbf 100644
--- a/packages/frontend/src/components/form/InputArray.tsx
+++ b/packages/frontend/src/components/form/InputArray.tsx
@@ -34,10 +34,12 @@ const defaultToString = (f?: any): string => f || ''
const defaultFromString = (v: string): any => v as any
export function InputArray<T>({ name, readonly, placeholder, tooltip, label, help, addonBefore, isValid = () => true, fromStr = defaultFromString, toStr = defaultToString }: Props<keyof T>): VNode {
- const { error: formError, value, onChange } = useField<T>(name);
+ const { error: formError, value, onChange, required } = useField<T>(name);
const [localError, setLocalError] = useState<string | null>(null)
- const error = formError || localError
+ const error = localError || formError
+
+ console.log(formError, localError)
const array: any[] = (value ? value! : []) as any;
const [currentValue, setCurrentValue] = useState('');
@@ -58,11 +60,14 @@ export function InputArray<T>({ name, readonly, placeholder, tooltip, label, hel
{addonBefore && <div class="control">
<a class="button is-static">{addonBefore}</a>
</div>}
- <p class="control is-expanded">
+ <p class="control is-expanded has-icons-right">
<input class={error ? "input is-danger" : "input"} type="text"
placeholder={placeholder} readonly={readonly} disabled={readonly}
name={String(name)} value={currentValue}
onChange={(e): void => setCurrentValue(e.currentTarget.value)} />
+ {required && <span class="icon has-text-danger is-right">
+ <i class="mdi mdi-alert" />
+ </span>}
</p>
<p class="control">
<button class="button is-info has-tooltip-left" disabled={!currentValue} onClick={(): void => {
diff --git a/packages/frontend/src/components/form/InputDuration.tsx b/packages/frontend/src/components/form/InputDuration.tsx
index 258858b..e5849b4 100644
--- a/packages/frontend/src/components/form/InputDuration.tsx
+++ b/packages/frontend/src/components/form/InputDuration.tsx
@@ -99,11 +99,11 @@ export function InputDuration<T>({ name, expand, placeholder, tooltip, label, he
</div>
{error && <p class="help is-danger">{error}</p>}
</div>
- {!readonly && <span data-tooltip={i18n`change value to empty`}>
- <button class="button is-info mr-3" onClick={() => onChange(undefined as any)} ><Translate>clear</Translate></button>
- </span>}
{withForever && <span data-tooltip={i18n`change value to never`}>
- <button class="button is-info" onClick={() => onChange({ d_ms: 'forever' } as any)}><Translate>forever</Translate></button>
+ <button class="button is-info mr-3" onClick={() => onChange({ d_ms: 'forever' } as any)}><Translate>forever</Translate></button>
+ </span>}
+ {!readonly && <span data-tooltip={i18n`change value to empty`}>
+ <button class="button is-info " onClick={() => onChange(undefined as any)} ><Translate>clear</Translate></button>
</span>}
</div>
{opened && <SimpleModal onCancel={() => setOpened(false)}>
diff --git a/packages/frontend/src/components/instance/DefaultInstanceFormFields.tsx b/packages/frontend/src/components/instance/DefaultInstanceFormFields.tsx
index 2d7f93f..4187d77 100644
--- a/packages/frontend/src/components/instance/DefaultInstanceFormFields.tsx
+++ b/packages/frontend/src/components/instance/DefaultInstanceFormFields.tsx
@@ -75,11 +75,11 @@ export function DefaultInstanceFormFields({ readonlyId, showId }: { readonlyId?:
<InputDuration<Entity> name="default_pay_delay"
label={i18n`Default payment delay`}
+ withForever
tooltip={i18n`Time customers have to pay an order before the offer expires by default.`} />
<InputDuration<Entity> name="default_wire_transfer_delay"
label={i18n`Default wire transfer delay`}
- withForever
tooltip={i18n`Maximum time an exchange is allowed to delay wiring funds to the merchant, enabling it to aggregate smaller payments into larger wire transfers and reducing wire fees.`} />
</Fragment>;
diff --git a/packages/frontend/src/components/picker/DurationPicker.tsx b/packages/frontend/src/components/picker/DurationPicker.tsx
index b452561..235a63e 100644
--- a/packages/frontend/src/components/picker/DurationPicker.tsx
+++ b/packages/frontend/src/components/picker/DurationPicker.tsx
@@ -107,7 +107,7 @@ function DurationColumn({ unit, min = 0, max, value, onIncrease, onDecrease, onC
<div class="rdp-column" style={{ top: 0 }}>
- <div class="rdp-cell" key={value - 1}>
+ <div class="rdp-cell" key={value - 2}>
{onDecrease && <button style={{ width: '100%', textAlign: 'center', margin: 5 }}
onClick={onDecrease}>
<span class="icon">
@@ -130,7 +130,7 @@ function DurationColumn({ unit, min = 0, max, value, onIncrease, onDecrease, onC
{value < max ? toTwoDigitString(value + 1) : ''}
</div>
- <div class="rdp-cell" key={value - 1}>
+ <div class="rdp-cell" key={value + 2}>
{onIncrease && <button style={{ width: '100%', textAlign: 'center', margin: 5 }}
onClick={onIncrease}>
<span class="icon">
diff --git a/packages/frontend/src/paths/admin/create/CreatePage.tsx b/packages/frontend/src/paths/admin/create/CreatePage.tsx
index c9276d7..ff7e6d6 100644
--- a/packages/frontend/src/paths/admin/create/CreatePage.tsx
+++ b/packages/frontend/src/paths/admin/create/CreatePage.tsx
@@ -27,8 +27,9 @@ import { FormErrors, FormProvider } from "../../../components/form/FormProvider"
import { SetTokenNewInstanceModal } from "../../../components/modal";
import { MerchantBackend } from "../../../declaration";
import { Translate, useTranslator } from "../../../i18n";
-import { InstanceCreateSchema as schema } from '../../../schemas';
import { DefaultInstanceFormFields } from "../../../components/instance/DefaultInstanceFormFields";
+import { PAYTO_REGEX } from "../../../utils/constants";
+import { Amounts } from "@gnu-taler/taler-util";
export type Entity = MerchantBackend.Instances.InstanceConfigurationMessage & { auth_token?: string }
@@ -46,18 +47,64 @@ function with_defaults(id?: string): Partial<Entity> {
default_wire_transfer_delay: { d_ms: 1000 * 2 * 60 * 60 * 24 }, // one day
};
}
+
+function undefinedIfEmpty<T>(obj: T): T | undefined {
+ return Object.keys(obj).some(k => (obj as any)[k] !== undefined) ? obj : undefined
+}
+
export function CreatePage({ onCreate, onBack, forceId }: Props): VNode {
const [value, valueHandler] = useState(with_defaults(forceId))
const [isTokenSet, updateIsTokenSet] = useState<boolean>(false);
const [isTokenDialogActive, updateIsTokenDialogActive] = useState<boolean>(false);
- let errors: FormErrors<Entity> = {}
- try {
- schema.validateSync(value, { abortEarly: false })
- } catch (err) {
- const yupErrors = err.inner as yup.ValidationError[]
- errors = yupErrors.reduce((prev, cur) => !cur.path ? prev : ({ ...prev, [cur.path]: cur.message }), {})
- }
+ const i18n = useTranslator()
+
+ const errors: FormErrors<Entity> = {
+ id: !value.id ? i18n`required` : undefined,
+ name: !value.name ? i18n`required` : undefined,
+ auth: {
+ method: value.auth?.method === 'token' && !value.auth?.token ? i18n`token can't be empty` : (
+ value.auth?.method !== 'external' ? i18n`access token is not defined` : undefined
+ )
+ },
+ payto_uris:
+ !value.payto_uris || !value.payto_uris.length ? i18n`required` : (
+ undefinedIfEmpty(value.payto_uris.map(p => {
+ return !PAYTO_REGEX.test(p) ? i18n`is not valid` : undefined
+ }))
+ ),
+ default_max_deposit_fee:
+ !value.default_max_deposit_fee ? i18n`required` : (
+ !Amounts.parse(value.default_max_deposit_fee) ? i18n`invalid format` :
+ undefined
+ ),
+ default_max_wire_fee:
+ !value.default_max_wire_fee ? i18n`required` : (
+ !Amounts.parse(value.default_max_wire_fee) ? i18n`invalid format` :
+ undefined
+ ),
+ default_wire_fee_amortization:
+ value.default_wire_fee_amortization === undefined ? i18n`required` : (
+ isNaN(value.default_wire_fee_amortization) ? i18n`is not a number` : (
+ value.default_wire_fee_amortization < 1 ? i18n`must be 1 or greater` :
+ undefined
+ )
+ ),
+ default_pay_delay:
+ !value.default_pay_delay ? i18n`required` : undefined,
+ default_wire_transfer_delay:
+ !value.default_wire_transfer_delay ? i18n`required` : undefined,
+ address: undefinedIfEmpty({
+ address_lines:
+ value.address?.address_lines && value.address?.address_lines.length > 7 ? i18n`max 7 lines` :
+ undefined
+ }),
+ jurisdiction: undefinedIfEmpty({
+ address_lines: value.address?.address_lines && value.address?.address_lines.length > 7 ? i18n`max 7 lines` :
+ undefined
+ }),
+ };
+
const hasErrors = Object.keys(errors).some(k => (errors as any)[k] !== undefined)
const submit = (): Promise<void> => {
@@ -67,15 +114,13 @@ export function CreatePage({ onCreate, onBack, forceId }: Props): VNode {
value.auth = newToken === null || newToken === undefined ? { method: "external" } : { method: "token", token: `secret-token:${newToken}` };
// remove above use conversion
// schema.validateSync(value, { abortEarly: false })
- return onCreate(schema.cast(value) as Entity);
+ return onCreate(value as Entity);
}
function updateToken(token: string | null) {
valueHandler(old => ({ ...old, auth_token: token === null ? undefined : token }))
}
- const i18n = useTranslator()
-
return <div>
<div class="columns">
<div class="column" />
diff --git a/packages/frontend/src/paths/instance/update/UpdatePage.tsx b/packages/frontend/src/paths/instance/update/UpdatePage.tsx
index c900192..6e7b9eb 100644
--- a/packages/frontend/src/paths/instance/update/UpdatePage.tsx
+++ b/packages/frontend/src/paths/instance/update/UpdatePage.tsx
@@ -30,6 +30,8 @@ import { MerchantBackend } from "../../../declaration";
import { Translate, useTranslator } from "../../../i18n";
import { InstanceUpdateSchema as schema } from '../../../schemas';
import { DefaultInstanceFormFields } from "../../../components/instance/DefaultInstanceFormFields";
+import { PAYTO_REGEX } from "../../../utils/constants";
+import { Amounts } from "@gnu-taler/taler-util";
type Entity = MerchantBackend.Instances.InstanceReconfigurationMessage & { auth_token?: string }
@@ -60,7 +62,9 @@ function getTokenValuePart(t?: string): string | undefined {
return match[1]
}
-
+function undefinedIfEmpty<T>(obj: T): T | undefined {
+ return Object.keys(obj).some(k => (obj as any)[k] !== undefined) ? obj : undefined
+}
export function UpdatePage({ onUpdate, onChangeAuth, selected, onBack }: Props): VNode {
const { id, token } = useInstanceContext()
@@ -79,13 +83,48 @@ export function UpdatePage({ onUpdate, onChangeAuth, selected, onBack }: Props):
const [value, valueHandler] = useState<Partial<Entity>>(convert(selected))
- let errors: FormErrors<Entity> = {}
- try {
- schema.validateSync(value, { abortEarly: false })
- } catch (err) {
- const yupErrors = err.inner as yup.ValidationError[]
- errors = yupErrors.reduce((prev, cur) => !cur.path ? prev : ({ ...prev, [cur.path]: cur.message }), {})
- }
+ const i18n = useTranslator()
+
+ const errors: FormErrors<Entity> = {
+ name: !value.name ? i18n`required` : undefined,
+ payto_uris:
+ !value.payto_uris || !value.payto_uris.length ? i18n`required` : (
+ undefinedIfEmpty(value.payto_uris.map(p => {
+ return !PAYTO_REGEX.test(p) ? i18n`is not valid` : undefined
+ }))
+ ),
+ default_max_deposit_fee:
+ !value.default_max_deposit_fee ? i18n`required` : (
+ !Amounts.parse(value.default_max_deposit_fee) ? i18n`invalid format` :
+ undefined
+ ),
+ default_max_wire_fee:
+ !value.default_max_wire_fee ? i18n`required` : (
+ !Amounts.parse(value.default_max_wire_fee) ? i18n`invalid format` :
+ undefined
+ ),
+ default_wire_fee_amortization:
+ value.default_wire_fee_amortization === undefined ? i18n`required` : (
+ isNaN(value.default_wire_fee_amortization) ? i18n`is not a number` : (
+ value.default_wire_fee_amortization < 1 ? i18n`must be 1 or greater` :
+ undefined
+ )
+ ),
+ default_pay_delay:
+ !value.default_pay_delay ? i18n`required` : undefined,
+ default_wire_transfer_delay:
+ !value.default_wire_transfer_delay ? i18n`required` : undefined,
+ address: undefinedIfEmpty({
+ address_lines:
+ value.address?.address_lines && value.address?.address_lines.length > 7 ? i18n`max 7 lines` :
+ undefined
+ }),
+ jurisdiction: undefinedIfEmpty({
+ address_lines: value.address?.address_lines && value.address?.address_lines.length > 7 ? i18n`max 7 lines` :
+ undefined
+ }),
+ };
+
const hasErrors = Object.keys(errors).some(k => (errors as any)[k] !== undefined)
const submit = async (): Promise<void> => {
await onUpdate(schema.cast(value));
@@ -93,7 +132,6 @@ export function UpdatePage({ onUpdate, onChangeAuth, selected, onBack }: Props):
return Promise.resolve()
}
const [active, setActive] = useState(false);
- const i18n = useTranslator()
return <div>
<section class="section">