fix #6363, breaking with merchant backend that accounts implemented
This commit is contained in:
parent
982fc51a97
commit
03d3cce827
@ -82,6 +82,12 @@ export interface FormType<T> {
|
|||||||
|
|
||||||
const FormContext = createContext<FormType<unknown>>(null!);
|
const FormContext = createContext<FormType<unknown>>(null!);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* FIXME:
|
||||||
|
* USE MEMORY EVENTS INSTEAD OF CONTEXT
|
||||||
|
* @deprecated
|
||||||
|
*/
|
||||||
|
|
||||||
export function useFormContext<T>() {
|
export function useFormContext<T>() {
|
||||||
return useContext<FormType<T>>(FormContext);
|
return useContext<FormType<T>>(FormContext);
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,47 @@
|
|||||||
|
/*
|
||||||
|
This file is part of GNU Taler
|
||||||
|
(C) 2021-2023 Taler Systems S.A.
|
||||||
|
|
||||||
|
GNU Taler is free software; you can redistribute it and/or modify it under the
|
||||||
|
terms of the GNU General Public License as published by the Free Software
|
||||||
|
Foundation; either version 3, or (at your option) any later version.
|
||||||
|
|
||||||
|
GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||||
|
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along with
|
||||||
|
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author Sebastian Javier Marchano (sebasjm)
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { h } from "preact";
|
||||||
|
import { tests } from "@gnu-taler/web-util/lib/index.browser";
|
||||||
|
import { InputPaytoForm } from "./InputPaytoForm.js";
|
||||||
|
import { FormProvider } from "./FormProvider.js";
|
||||||
|
import { useState } from "preact/hooks";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
title: "Components/Form/PayTo",
|
||||||
|
component: InputPaytoForm,
|
||||||
|
argTypes: {
|
||||||
|
onUpdate: { action: "onUpdate" },
|
||||||
|
onBack: { action: "onBack" },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Example = tests.createExample(() => {
|
||||||
|
const initial = {
|
||||||
|
accounts: [],
|
||||||
|
};
|
||||||
|
const [form, updateForm] = useState<Partial<typeof initial>>(initial);
|
||||||
|
return (
|
||||||
|
<FormProvider valueHandler={updateForm} object={form}>
|
||||||
|
<InputPaytoForm name="accounts" label="Accounts:" />
|
||||||
|
</FormProvider>
|
||||||
|
);
|
||||||
|
}, {});
|
@ -28,6 +28,8 @@ import { Input } from "./Input.js";
|
|||||||
import { InputGroup } from "./InputGroup.js";
|
import { InputGroup } from "./InputGroup.js";
|
||||||
import { InputSelector } from "./InputSelector.js";
|
import { InputSelector } from "./InputSelector.js";
|
||||||
import { InputProps, useField } from "./useField.js";
|
import { InputProps, useField } from "./useField.js";
|
||||||
|
import { InputWithAddon } from "./InputWithAddon.js";
|
||||||
|
import { MerchantBackend } from "../../declaration.js";
|
||||||
|
|
||||||
export interface Props<T> extends InputProps<T> {
|
export interface Props<T> extends InputProps<T> {
|
||||||
isValid?: (e: any) => boolean;
|
isValid?: (e: any) => boolean;
|
||||||
@ -50,6 +52,13 @@ type Entity = {
|
|||||||
instruction?: string;
|
instruction?: string;
|
||||||
[name: string]: string | undefined;
|
[name: string]: string | undefined;
|
||||||
};
|
};
|
||||||
|
auth: {
|
||||||
|
type: "unset" | "basic" | "none";
|
||||||
|
url?: string;
|
||||||
|
username?: string;
|
||||||
|
password?: string;
|
||||||
|
repeat?: string;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
function isEthereumAddress(address: string) {
|
function isEthereumAddress(address: string) {
|
||||||
@ -162,8 +171,15 @@ const targets = [
|
|||||||
"bitcoin",
|
"bitcoin",
|
||||||
"ethereum",
|
"ethereum",
|
||||||
];
|
];
|
||||||
|
const accountAuthType = ["none", "basic"];
|
||||||
const noTargetValue = targets[0];
|
const noTargetValue = targets[0];
|
||||||
const defaultTarget = { target: noTargetValue, options: {} };
|
const defaultTarget: Partial<Entity> = {
|
||||||
|
target: noTargetValue,
|
||||||
|
options: {},
|
||||||
|
auth: {
|
||||||
|
type: "unset" as const,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
export function InputPaytoForm<T>({
|
export function InputPaytoForm<T>({
|
||||||
name,
|
name,
|
||||||
@ -187,7 +203,7 @@ export function InputPaytoForm<T>({
|
|||||||
}
|
}
|
||||||
const { i18n } = useTranslationContext();
|
const { i18n } = useTranslationContext();
|
||||||
|
|
||||||
const ops = value.options!;
|
const ops = value.options ?? {};
|
||||||
const url = tryUrl(`payto://${value.target}${payToPath}`);
|
const url = tryUrl(`payto://${value.target}${payToPath}`);
|
||||||
if (url) {
|
if (url) {
|
||||||
Object.keys(ops).forEach((opt_key) => {
|
Object.keys(ops).forEach((opt_key) => {
|
||||||
@ -222,6 +238,24 @@ export function InputPaytoForm<T>({
|
|||||||
? i18n.str`required`
|
? i18n.str`required`
|
||||||
: undefined,
|
: undefined,
|
||||||
}),
|
}),
|
||||||
|
auth: !value.auth
|
||||||
|
? undefined
|
||||||
|
: undefinedIfEmpty({
|
||||||
|
username:
|
||||||
|
value.auth.type === "basic" && !value.auth.username
|
||||||
|
? i18n.str`required`
|
||||||
|
: undefined,
|
||||||
|
password:
|
||||||
|
value.auth.type === "basic" && !value.auth.password
|
||||||
|
? i18n.str`required`
|
||||||
|
: undefined,
|
||||||
|
repeat:
|
||||||
|
value.auth.type === "basic" && !value.auth.repeat
|
||||||
|
? i18n.str`required`
|
||||||
|
: value.auth.repeat !== value.auth.password
|
||||||
|
? i18n.str`is not the same`
|
||||||
|
: undefined,
|
||||||
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
const hasErrors = Object.keys(errors).some(
|
const hasErrors = Object.keys(errors).some(
|
||||||
@ -229,10 +263,31 @@ export function InputPaytoForm<T>({
|
|||||||
);
|
);
|
||||||
|
|
||||||
const submit = useCallback((): void => {
|
const submit = useCallback((): void => {
|
||||||
|
const accounts: MerchantBackend.Instances.MerchantBankAccount[] = paytos;
|
||||||
const alreadyExists =
|
const alreadyExists =
|
||||||
paytos.findIndex((x: string) => x === paytoURL) !== -1;
|
accounts.findIndex((x) => x.payto_uri === paytoURL) !== -1;
|
||||||
if (!alreadyExists) {
|
if (!alreadyExists) {
|
||||||
onChange([paytoURL, ...paytos] as any);
|
const newValue: MerchantBackend.Instances.MerchantBankAccount = {
|
||||||
|
payto_uri: paytoURL,
|
||||||
|
};
|
||||||
|
if (value.auth) {
|
||||||
|
if (value.auth.url) {
|
||||||
|
newValue.credit_facade_url = value.auth.url;
|
||||||
|
}
|
||||||
|
if (value.auth.type === "none") {
|
||||||
|
newValue.credit_facade_credentials = {
|
||||||
|
type: "none",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (value.auth.type === "basic") {
|
||||||
|
newValue.credit_facade_credentials = {
|
||||||
|
type: "basic",
|
||||||
|
username: value.auth.username ?? "",
|
||||||
|
password: value.auth.password ?? "",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onChange([newValue, ...accounts] as any);
|
||||||
}
|
}
|
||||||
valueHandler(defaultTarget);
|
valueHandler(defaultTarget);
|
||||||
}, [value]);
|
}, [value]);
|
||||||
@ -339,18 +394,106 @@ export function InputPaytoForm<T>({
|
|||||||
</Fragment>
|
</Fragment>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{/**
|
||||||
|
* Show additional fields apart from the payto
|
||||||
|
*/}
|
||||||
{value.target !== noTargetValue && (
|
{value.target !== noTargetValue && (
|
||||||
|
<Fragment>
|
||||||
<Input
|
<Input
|
||||||
name="options.receiver-name"
|
name="options.receiver-name"
|
||||||
label={i18n.str`Name`}
|
label={i18n.str`Name`}
|
||||||
tooltip={i18n.str`Bank account owner's name.`}
|
tooltip={i18n.str`Bank account owner's name.`}
|
||||||
/>
|
/>
|
||||||
)}
|
<InputWithAddon
|
||||||
|
name="auth.url"
|
||||||
|
label={i18n.str`Account info URL`}
|
||||||
|
help="https://bank.com"
|
||||||
|
expand
|
||||||
|
tooltip={i18n.str`From where the merchant can download information about incoming wire transfers to this account`}
|
||||||
|
/>
|
||||||
|
<InputSelector
|
||||||
|
name="auth.type"
|
||||||
|
label={i18n.str`Auth type`}
|
||||||
|
tooltip={i18n.str`Choose the authentication type for the account info URL`}
|
||||||
|
values={accountAuthType}
|
||||||
|
toStr={(str) => {
|
||||||
|
// if (str === "unset") {
|
||||||
|
// return "Without change";
|
||||||
|
// }
|
||||||
|
if (str === "none") return "Without authentication";
|
||||||
|
return "Username and password";
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
{value.auth?.type === "basic" ? (
|
||||||
|
<Fragment>
|
||||||
|
<Input
|
||||||
|
name="auth.username"
|
||||||
|
label={i18n.str`Username`}
|
||||||
|
tooltip={i18n.str`Username to access the account information.`}
|
||||||
|
/>
|
||||||
|
<Input
|
||||||
|
name="auth.password"
|
||||||
|
inputType="password"
|
||||||
|
label={i18n.str`Password`}
|
||||||
|
tooltip={i18n.str`Password to access the account information.`}
|
||||||
|
/>
|
||||||
|
<Input
|
||||||
|
name="auth.repeat"
|
||||||
|
inputType="password"
|
||||||
|
label={i18n.str`Repeat password`}
|
||||||
|
/>
|
||||||
|
</Fragment>
|
||||||
|
) : undefined}
|
||||||
|
|
||||||
|
{/* <InputWithAddon
|
||||||
|
name="options.credit_credentials"
|
||||||
|
label={i18n.str`Account info`}
|
||||||
|
inputType={showKey ? "text" : "password"}
|
||||||
|
help="From where the merchant can download information about incoming wire transfers to this account"
|
||||||
|
expand
|
||||||
|
tooltip={i18n.str`Useful to validate the purchase`}
|
||||||
|
fromStr={(v) => v.toUpperCase()}
|
||||||
|
addonAfter={
|
||||||
|
<span class="icon">
|
||||||
|
{showKey ? (
|
||||||
|
<i class="mdi mdi-eye" />
|
||||||
|
) : (
|
||||||
|
<i class="mdi mdi-eye-off" />
|
||||||
|
)}
|
||||||
|
</span>
|
||||||
|
}
|
||||||
|
side={
|
||||||
|
<span style={{ display: "flex" }}>
|
||||||
|
<button
|
||||||
|
data-tooltip={
|
||||||
|
showKey
|
||||||
|
? i18n.str`show secret key`
|
||||||
|
: i18n.str`hide secret key`
|
||||||
|
}
|
||||||
|
class="button is-info mr-3"
|
||||||
|
onClick={(e) => {
|
||||||
|
setShowKey(!showKey);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{showKey ? (
|
||||||
|
<i18n.Translate>hide</i18n.Translate>
|
||||||
|
) : (
|
||||||
|
<i18n.Translate>show</i18n.Translate>
|
||||||
|
)}
|
||||||
|
</button>
|
||||||
|
</span>
|
||||||
|
}
|
||||||
|
/> */}
|
||||||
|
</Fragment>
|
||||||
|
)}
|
||||||
|
{/**
|
||||||
|
* Show the values in the list
|
||||||
|
*/}
|
||||||
<div class="field is-horizontal">
|
<div class="field is-horizontal">
|
||||||
<div class="field-label is-normal" />
|
<div class="field-label is-normal" />
|
||||||
<div class="field-body" style={{ display: "block" }}>
|
<div class="field-body" style={{ display: "block" }}>
|
||||||
{paytos.map((v: any, i: number) => (
|
{paytos.map(
|
||||||
|
(v: MerchantBackend.Instances.MerchantBankAccount, i: number) => (
|
||||||
<div
|
<div
|
||||||
key={i}
|
key={i}
|
||||||
class="tags has-addons mt-3 mb-0 mr-3"
|
class="tags has-addons mt-3 mb-0 mr-3"
|
||||||
@ -360,7 +503,7 @@ export function InputPaytoForm<T>({
|
|||||||
class="tag is-medium is-info mb-0"
|
class="tag is-medium is-info mb-0"
|
||||||
style={{ maxWidth: "90%" }}
|
style={{ maxWidth: "90%" }}
|
||||||
>
|
>
|
||||||
{v}
|
{v.payto_uri}
|
||||||
</span>
|
</span>
|
||||||
<a
|
<a
|
||||||
class="tag is-medium is-danger is-delete mb-0"
|
class="tag is-medium is-danger is-delete mb-0"
|
||||||
@ -369,7 +512,8 @@ export function InputPaytoForm<T>({
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
))}
|
),
|
||||||
|
)}
|
||||||
{!paytos.length && i18n.str`No accounts yet.`}
|
{!paytos.length && i18n.str`No accounts yet.`}
|
||||||
{required && (
|
{required && (
|
||||||
<span class="icon has-text-danger is-right">
|
<span class="icon has-text-danger is-right">
|
||||||
|
@ -0,0 +1,17 @@
|
|||||||
|
/*
|
||||||
|
This file is part of GNU Taler
|
||||||
|
(C) 2021-2023 Taler Systems S.A.
|
||||||
|
|
||||||
|
GNU Taler is free software; you can redistribute it and/or modify it under the
|
||||||
|
terms of the GNU General Public License as published by the Free Software
|
||||||
|
Foundation; either version 3, or (at your option) any later version.
|
||||||
|
|
||||||
|
GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||||
|
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along with
|
||||||
|
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
|
||||||
|
*/
|
||||||
|
|
||||||
|
export * as payto from "./form/InputPaytoForm.stories.js";
|
@ -86,7 +86,7 @@ export function DefaultInstanceFormFields({
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<InputPaytoForm<Entity>
|
<InputPaytoForm<Entity>
|
||||||
name="payto_uris"
|
name="accounts"
|
||||||
label={i18n.str`Bank account`}
|
label={i18n.str`Bank account`}
|
||||||
tooltip={i18n.str`URI specifying bank account for crediting revenue.`}
|
tooltip={i18n.str`URI specifying bank account for crediting revenue.`}
|
||||||
/>
|
/>
|
||||||
|
@ -262,15 +262,45 @@ export namespace MerchantBackend {
|
|||||||
// header.
|
// header.
|
||||||
token?: string;
|
token?: string;
|
||||||
}
|
}
|
||||||
|
type FacadeCredentials = NoFacadeCredentials | BasicAuthFacadeCredentials;
|
||||||
|
|
||||||
|
interface NoFacadeCredentials {
|
||||||
|
type: "none";
|
||||||
|
}
|
||||||
|
|
||||||
|
interface BasicAuthFacadeCredentials {
|
||||||
|
type: "basic";
|
||||||
|
|
||||||
|
// Username to use to authenticate
|
||||||
|
username: string;
|
||||||
|
|
||||||
|
// Password to use to authenticate
|
||||||
|
password: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface MerchantBankAccount {
|
||||||
|
// The payto:// URI where the wallet will send coins.
|
||||||
|
payto_uri: string;
|
||||||
|
|
||||||
|
// Optional base URL for a facade where the
|
||||||
|
// merchant backend can see incoming wire
|
||||||
|
// transfers to reconcile its accounting
|
||||||
|
// with that of the exchange. Used by
|
||||||
|
// taler-merchant-wirewatch.
|
||||||
|
credit_facade_url?: string;
|
||||||
|
|
||||||
|
// Credentials for accessing the credit facade.
|
||||||
|
credit_facade_credentials?: FacadeCredentials;
|
||||||
|
}
|
||||||
//POST /private/instances
|
//POST /private/instances
|
||||||
interface InstanceConfigurationMessage {
|
interface InstanceConfigurationMessage {
|
||||||
// The URI where the wallet will send coins. A merchant may have
|
// Bank accounts of the merchant. A merchant may have
|
||||||
// multiple accounts, thus this is an array. Note that by
|
// multiple accounts, thus this is an array. Note that by
|
||||||
// removing URIs from this list the respective account is set to
|
// removing accounts from this list the respective account is set to
|
||||||
// inactive and thus unavailable for new contracts, but preserved
|
// inactive and thus unavailable for new contracts, but preserved
|
||||||
// in the database as existing offers and contracts may still refer
|
// in the database as existing offers and contracts may still refer
|
||||||
// to it.
|
// to it.
|
||||||
payto_uris: string[];
|
accounts: MerchantBankAccount[];
|
||||||
|
|
||||||
// Name of the merchant instance to create (will become $INSTANCE).
|
// Name of the merchant instance to create (will become $INSTANCE).
|
||||||
id: string;
|
id: string;
|
||||||
@ -326,10 +356,11 @@ export namespace MerchantBackend {
|
|||||||
|
|
||||||
// PATCH /private/instances/$INSTANCE
|
// PATCH /private/instances/$INSTANCE
|
||||||
interface InstanceReconfigurationMessage {
|
interface InstanceReconfigurationMessage {
|
||||||
// The URI where the wallet will send coins. A merchant may have
|
// Bank accounts of the merchant. A merchant may have
|
||||||
// multiple accounts, thus this is an array. Note that by
|
// multiple accounts, thus this is an array. Note that removing
|
||||||
// removing URIs from this list
|
// URIs from this list deactivates the specified accounts
|
||||||
payto_uris: string[];
|
// (they will no longer be used for future contracts).
|
||||||
|
accounts: MerchantBankAccount[];
|
||||||
|
|
||||||
// Merchant name corresponding to this instance.
|
// Merchant name corresponding to this instance.
|
||||||
name: string;
|
name: string;
|
||||||
@ -491,6 +522,16 @@ export namespace MerchantBackend {
|
|||||||
// salt used to compute h_wire
|
// salt used to compute h_wire
|
||||||
salt: HashCode;
|
salt: HashCode;
|
||||||
|
|
||||||
|
// URL from where the merchant can download information
|
||||||
|
// about incoming wire transfers to this account.
|
||||||
|
credit_facade_url?: string;
|
||||||
|
|
||||||
|
// Credentials to use when accessing the credit facade.
|
||||||
|
// Never returned on a GET (as this may be somewhat
|
||||||
|
// sensitive data). Can be set in POST
|
||||||
|
// or PATCH requests to update (or delete) credentials.
|
||||||
|
credit_facade_credentials?: FacadeCredentials;
|
||||||
|
|
||||||
// true if this account is active,
|
// true if this account is active,
|
||||||
// false if it is historic.
|
// false if it is historic.
|
||||||
active: boolean;
|
active: boolean;
|
||||||
|
@ -47,7 +47,7 @@ interface Props {
|
|||||||
function with_defaults(id?: string): Partial<Entity> {
|
function with_defaults(id?: string): Partial<Entity> {
|
||||||
return {
|
return {
|
||||||
id,
|
id,
|
||||||
payto_uris: [],
|
accounts: [],
|
||||||
user_type: "business",
|
user_type: "business",
|
||||||
default_pay_delay: { d_us: 2 * 1000 * 60 * 60 * 1000 }, // two hours
|
default_pay_delay: { d_us: 2 * 1000 * 60 * 60 * 1000 }, // two hours
|
||||||
default_wire_fee_amortization: 1,
|
default_wire_fee_amortization: 1,
|
||||||
@ -75,12 +75,14 @@ export function CreatePage({ onCreate, onBack, forceId }: Props): VNode {
|
|||||||
: value.user_type !== "business" && value.user_type !== "individual"
|
: value.user_type !== "business" && value.user_type !== "individual"
|
||||||
? i18n.str`should be business or individual`
|
? i18n.str`should be business or individual`
|
||||||
: undefined,
|
: undefined,
|
||||||
payto_uris:
|
accounts:
|
||||||
!value.payto_uris || !value.payto_uris.length
|
!value.accounts || !value.accounts.length
|
||||||
? i18n.str`required`
|
? i18n.str`required`
|
||||||
: undefinedIfEmpty(
|
: undefinedIfEmpty(
|
||||||
value.payto_uris.map((p) => {
|
value.accounts.map((p) => {
|
||||||
return !PAYTO_REGEX.test(p) ? i18n.str`is not valid` : undefined;
|
return !PAYTO_REGEX.test(p.payto_uri)
|
||||||
|
? i18n.str`is not valid`
|
||||||
|
: undefined;
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
default_max_deposit_fee: !value.default_max_deposit_fee
|
default_max_deposit_fee: !value.default_max_deposit_fee
|
||||||
|
@ -53,14 +53,23 @@ interface Props {
|
|||||||
function convert(
|
function convert(
|
||||||
from: MerchantBackend.Instances.QueryInstancesResponse,
|
from: MerchantBackend.Instances.QueryInstancesResponse,
|
||||||
): Entity {
|
): Entity {
|
||||||
const { accounts, ...rest } = from;
|
const { accounts: qAccounts, ...rest } = from;
|
||||||
const payto_uris = accounts.filter((a) => a.active).map((a) => a.payto_uri);
|
const accounts = qAccounts
|
||||||
|
.filter((a) => a.active)
|
||||||
|
.map(
|
||||||
|
(a) =>
|
||||||
|
({
|
||||||
|
payto_uri: a.payto_uri,
|
||||||
|
credit_facade_url: a.credit_facade_url,
|
||||||
|
credit_facade_credentials: a.credit_facade_credentials,
|
||||||
|
} as MerchantBackend.Instances.MerchantBankAccount),
|
||||||
|
);
|
||||||
const defaults = {
|
const defaults = {
|
||||||
default_wire_fee_amortization: 1,
|
default_wire_fee_amortization: 1,
|
||||||
default_pay_delay: { d_us: 2 * 1000 * 1000 * 60 * 60 }, //two hours
|
default_pay_delay: { d_us: 2 * 1000 * 1000 * 60 * 60 }, //two hours
|
||||||
default_wire_transfer_delay: { d_us: 2 * 1000 * 1000 * 60 * 60 * 2 }, //two hours
|
default_wire_transfer_delay: { d_us: 2 * 1000 * 1000 * 60 * 60 * 2 }, //two hours
|
||||||
};
|
};
|
||||||
return { ...defaults, ...rest, payto_uris };
|
return { ...defaults, ...rest, accounts };
|
||||||
}
|
}
|
||||||
|
|
||||||
function getTokenValuePart(t?: string): string | undefined {
|
function getTokenValuePart(t?: string): string | undefined {
|
||||||
@ -103,12 +112,14 @@ export function UpdatePage({
|
|||||||
: value.user_type !== "business" && value.user_type !== "individual"
|
: value.user_type !== "business" && value.user_type !== "individual"
|
||||||
? i18n.str`should be business or individual`
|
? i18n.str`should be business or individual`
|
||||||
: undefined,
|
: undefined,
|
||||||
payto_uris:
|
accounts:
|
||||||
!value.payto_uris || !value.payto_uris.length
|
!value.accounts || !value.accounts.length
|
||||||
? i18n.str`required`
|
? i18n.str`required`
|
||||||
: undefinedIfEmpty(
|
: undefinedIfEmpty(
|
||||||
value.payto_uris.map((p) => {
|
value.accounts.map((p) => {
|
||||||
return !PAYTO_REGEX.test(p) ? i18n.str`is not valid` : undefined;
|
return !PAYTO_REGEX.test(p.payto_uri)
|
||||||
|
? i18n.str`is not valid`
|
||||||
|
: undefined;
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
default_max_deposit_fee: !value.default_max_deposit_fee
|
default_max_deposit_fee: !value.default_max_deposit_fee
|
||||||
|
@ -22,6 +22,7 @@ import { strings } from "./i18n/strings.js";
|
|||||||
|
|
||||||
import * as admin from "./paths/admin/index.stories.js";
|
import * as admin from "./paths/admin/index.stories.js";
|
||||||
import * as instance from "./paths/instance/index.stories.js";
|
import * as instance from "./paths/instance/index.stories.js";
|
||||||
|
import * as components from "./components/index.stories.js";
|
||||||
|
|
||||||
import { renderStories } from "@gnu-taler/web-util/lib/index.browser";
|
import { renderStories } from "@gnu-taler/web-util/lib/index.browser";
|
||||||
|
|
||||||
@ -33,7 +34,7 @@ function SortStories(a: any, b: any): number {
|
|||||||
|
|
||||||
function main(): void {
|
function main(): void {
|
||||||
renderStories(
|
renderStories(
|
||||||
{ admin, instance },
|
{ admin, instance, components },
|
||||||
{
|
{
|
||||||
strings,
|
strings,
|
||||||
},
|
},
|
||||||
|
Loading…
Reference in New Issue
Block a user