[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[taler-merchant-backoffice] 01/02: format
From: |
gnunet |
Subject: |
[taler-merchant-backoffice] 01/02: format |
Date: |
Tue, 30 Nov 2021 20:21:48 +0100 |
This is an automated email from the git hooks/post-receive script.
sebasjm pushed a commit to branch master
in repository merchant-backoffice.
commit b56c499a55de0b54b3cf407bdc955abd51c36d16
Author: Sebastian <sebasjm@gmail.com>
AuthorDate: Tue Nov 30 16:12:20 2021 -0300
format
---
.../src/components/form/InputPaytoForm.tsx | 282 +++++++----
.../instance/DefaultInstanceFormFields.tsx | 135 ++++--
packages/merchant-backoffice/src/i18n/index.tsx | 54 ++-
.../src/paths/admin/create/CreatePage.tsx | 257 ++++++----
.../src/paths/admin/create/index.tsx | 56 ++-
.../paths/instance/orders/create/CreatePage.tsx | 539 +++++++++++++--------
.../src/paths/instance/update/UpdatePage.tsx | 288 ++++++-----
.../src/paths/instance/update/index.tsx | 85 ++--
8 files changed, 1059 insertions(+), 637 deletions(-)
diff --git
a/packages/merchant-backoffice/src/components/form/InputPaytoForm.tsx
b/packages/merchant-backoffice/src/components/form/InputPaytoForm.tsx
index c52dc33..02436a3 100644
--- a/packages/merchant-backoffice/src/components/form/InputPaytoForm.tsx
+++ b/packages/merchant-backoffice/src/components/form/InputPaytoForm.tsx
@@ -15,9 +15,9 @@
*/
/**
-*
-* @author Sebastian Javier Marchano (sebasjm)
-*/
+ *
+ * @author Sebastian Javier Marchano (sebasjm)
+ */
import { h, VNode, Fragment } from "preact";
import { useCallback, useState } from "preact/hooks";
import { Translate, useTranslator } from "../../i18n";
@@ -33,135 +33,231 @@ export interface Props<T> extends InputProps<T> {
// https://datatracker.ietf.org/doc/html/rfc8905
type Entity = {
- target: string,
- path: string,
- path1: string,
- path2: string,
- host: string,
- account: string,
+ target: string;
+ path: string;
+ path1: string;
+ path2: string;
+ host: string;
+ account: string;
options: {
- 'receiver-name'?: string,
- sender?: string,
- message?: string,
- amount?: string,
- instruction?: string,
- [name: string]: string | undefined,
- },
-}
+ "receiver-name"?: string;
+ sender?: string;
+ message?: string;
+ amount?: string;
+ instruction?: string;
+ [name: string]: string | undefined;
+ };
+};
// const targets = ['ach', 'bic', 'iban', 'upi', 'bitcoin', 'ilp', 'void',
'x-taler-bank']
-const targets = ['iban', 'x-taler-bank']
-const defaultTarget = { target: 'iban', options: {} }
+const targets = ["iban", "x-taler-bank"];
+const defaultTarget = { target: "iban", options: {} };
function undefinedIfEmpty<T>(obj: T): T | undefined {
- return Object.keys(obj).some(k => (obj as any)[k] !== undefined) ? obj :
undefined
+ return Object.keys(obj).some((k) => (obj as any)[k] !== undefined)
+ ? obj
+ : undefined;
}
-export function InputPaytoForm<T>({ name, readonly, label, tooltip }:
Props<keyof T>): VNode {
- const { value: paytos, onChange, } = useField<T>(name);
+export function InputPaytoForm<T>({
+ name,
+ readonly,
+ label,
+ tooltip,
+}: Props<keyof T>): VNode {
+ const { value: paytos, onChange } = useField<T>(name);
- const [value, valueHandler] = useState<Partial<Entity>>(defaultTarget)
+ const [value, valueHandler] = useState<Partial<Entity>>(defaultTarget);
if (value.path1) {
if (value.path2) {
- value.path = `/${value.path1}/${value.path2}`
+ value.path = `/${value.path1}/${value.path2}`;
} else {
- value.path = `/${value.path1}`
+ value.path = `/${value.path1}`;
}
}
- const i18n = useTranslator()
+ const i18n = useTranslator();
- const url = new URL(`payto://${value.target}${value.path}`)
- const ops = value.options!
- Object.keys(ops).forEach(opt_key => {
- const opt_value = ops[opt_key]
- if (opt_value) url.searchParams.set(opt_key, opt_value)
- })
- const paytoURL = url.toString()
+ const url = new URL(`payto://${value.target}${value.path}`);
+ const ops = value.options!;
+ Object.keys(ops).forEach((opt_key) => {
+ const opt_value = ops[opt_key];
+ if (opt_value) url.searchParams.set(opt_key, opt_value);
+ });
+ const paytoURL = url.toString();
const errors: FormErrors<Entity> = {
target: !value.target ? i18n`required` : undefined,
- path1: !value.path1 ? i18n`required` : (
- value.target === 'iban' ? (
- value.path1.length < 4 ? i18n`IBAN numbers usually have more that 4
digits` : (
- value.path1.length > 34 ? i18n`IBAN numbers usually have less that
34 digits` :
- undefined
- )
- ): undefined
- ),
- path2: value.target === 'x-taler-bank' ? (!value.path2 ? i18n`required` :
undefined) : undefined,
+ path1: !value.path1
+ ? i18n`required`
+ : value.target === "iban"
+ ? value.path1.length < 4
+ ? i18n`IBAN numbers usually have more that 4 digits`
+ : value.path1.length > 34
+ ? i18n`IBAN numbers usually have less that 34 digits`
+ : undefined
+ : undefined,
+ path2:
+ value.target === "x-taler-bank"
+ ? !value.path2
+ ? i18n`required`
+ : undefined
+ : undefined,
options: undefinedIfEmpty({
- 'receiver-name': !value.options?.["receiver-name"] ? i18n`required` :
undefined,
- })
- }
+ "receiver-name": !value.options?.["receiver-name"]
+ ? i18n`required`
+ : undefined,
+ }),
+ };
- const hasErrors = Object.keys(errors).some(k => (errors as any)[k] !==
undefined)
+ const hasErrors = Object.keys(errors).some(
+ (k) => (errors as any)[k] !== undefined
+ );
const submit = useCallback((): void => {
- const alreadyExists = paytos.findIndex((x:string) => x === paytoURL) !==
-1;
+ const alreadyExists =
+ paytos.findIndex((x: string) => x === paytoURL) !== -1;
if (!alreadyExists) {
- onChange([paytoURL, ...paytos] as any)
+ onChange([paytoURL, ...paytos] as any);
}
- valueHandler(defaultTarget)
- }, [value])
-
+ valueHandler(defaultTarget);
+ }, [value]);
//FIXME: translating plural singular
return (
<InputGroup name="payto" label={label} fixed tooltip={tooltip}>
- <FormProvider<Entity> name="tax" errors={errors} object={value}
valueHandler={valueHandler} >
-
- <InputSelector<Entity> name="target" label={i18n`Target type`}
tooltip={i18n`Method to use for wire transfer`} values={targets} />
-
- {value.target === 'ach' && <Fragment>
- <Input<Entity> name="path1" label={i18n`Routing`}
tooltip={i18n`Routing number.`} />
- <Input<Entity> name="path2" label={i18n`Account`}
tooltip={i18n`Account number.`} />
- </Fragment>}
- {value.target === 'bic' && <Fragment>
- <Input<Entity> name="path1" label={i18n`Code`}
tooltip={i18n`Business Identifier Code.`} />
- </Fragment>}
- {value.target === 'iban' && <Fragment>
- <Input<Entity> name="path1" label={i18n`Account`} tooltip={i18n`Bank
Account Number.`} />
- </Fragment>}
- {value.target === 'upi' && <Fragment>
- <Input<Entity> name="path1" label={i18n`Account`}
tooltip={i18n`Unified Payment Interface.`} />
- </Fragment>}
- {value.target === 'bitcoin' && <Fragment>
- <Input<Entity> name="path1" label={i18n`Address`}
tooltip={i18n`Bitcoin protocol.`} />
- </Fragment>}
- {value.target === 'ilp' && <Fragment>
- <Input<Entity> name="path1" label={i18n`Address`}
tooltip={i18n`Interledger protocol.`} />
- </Fragment>}
- {value.target === 'void' && <Fragment>
- </Fragment>}
- {value.target === 'x-taler-bank' && <Fragment>
- <Input<Entity> name="path1" label={i18n`Host`} tooltip={i18n`Bank
host.`} />
- <Input<Entity> name="path2" label={i18n`Account`} tooltip={i18n`Bank
account.`} />
- </Fragment>}
-
- <Input name="options.receiver-name" label={i18n`Name`}
tooltip={i18n`Bank account owner's name.`} />
+ <FormProvider<Entity>
+ name="tax"
+ errors={errors}
+ object={value}
+ valueHandler={valueHandler}
+ >
+ <InputSelector<Entity>
+ name="target"
+ label={i18n`Target type`}
+ tooltip={i18n`Method to use for wire transfer`}
+ values={targets}
+ />
+
+ {value.target === "ach" && (
+ <Fragment>
+ <Input<Entity>
+ name="path1"
+ label={i18n`Routing`}
+ tooltip={i18n`Routing number.`}
+ />
+ <Input<Entity>
+ name="path2"
+ label={i18n`Account`}
+ tooltip={i18n`Account number.`}
+ />
+ </Fragment>
+ )}
+ {value.target === "bic" && (
+ <Fragment>
+ <Input<Entity>
+ name="path1"
+ label={i18n`Code`}
+ tooltip={i18n`Business Identifier Code.`}
+ />
+ </Fragment>
+ )}
+ {value.target === "iban" && (
+ <Fragment>
+ <Input<Entity>
+ name="path1"
+ label={i18n`Account`}
+ tooltip={i18n`Bank Account Number.`}
+ />
+ </Fragment>
+ )}
+ {value.target === "upi" && (
+ <Fragment>
+ <Input<Entity>
+ name="path1"
+ label={i18n`Account`}
+ tooltip={i18n`Unified Payment Interface.`}
+ />
+ </Fragment>
+ )}
+ {value.target === "bitcoin" && (
+ <Fragment>
+ <Input<Entity>
+ name="path1"
+ label={i18n`Address`}
+ tooltip={i18n`Bitcoin protocol.`}
+ />
+ </Fragment>
+ )}
+ {value.target === "ilp" && (
+ <Fragment>
+ <Input<Entity>
+ name="path1"
+ label={i18n`Address`}
+ tooltip={i18n`Interledger protocol.`}
+ />
+ </Fragment>
+ )}
+ {value.target === "void" && <Fragment></Fragment>}
+ {value.target === "x-taler-bank" && (
+ <Fragment>
+ <Input<Entity>
+ name="path1"
+ label={i18n`Host`}
+ tooltip={i18n`Bank host.`}
+ />
+ <Input<Entity>
+ name="path2"
+ label={i18n`Account`}
+ tooltip={i18n`Bank account.`}
+ />
+ </Fragment>
+ )}
+
+ <Input
+ name="options.receiver-name"
+ label={i18n`Name`}
+ tooltip={i18n`Bank account owner's name.`}
+ />
<div class="field is-horizontal">
<div class="field-label is-normal" />
- <div class="field-body" style={{ display: 'block' }}>
- {paytos.map((v: any, i: number) => <div key={i} class="tags
has-addons mt-3 mb-0 mr-3" style={{ flexWrap: 'nowrap' }}>
- <span class="tag is-medium is-info mb-0" style={{ maxWidth:
'90%' }}>{v}</span>
- <a class="tag is-medium is-danger is-delete mb-0" onClick={() =>
{
- onChange(paytos.filter((f: any) => f !== v) as any);
- }} />
- </div>
- )}
+ <div class="field-body" style={{ display: "block" }}>
+ {paytos.map((v: any, i: number) => (
+ <div
+ key={i}
+ class="tags has-addons mt-3 mb-0 mr-3"
+ style={{ flexWrap: "nowrap" }}
+ >
+ <span
+ class="tag is-medium is-info mb-0"
+ style={{ maxWidth: "90%" }}
+ >
+ {v}
+ </span>
+ <a
+ class="tag is-medium is-danger is-delete mb-0"
+ onClick={() => {
+ onChange(paytos.filter((f: any) => f !== v) as any);
+ }}
+ />
+ </div>
+ ))}
{!paytos.length && i18n`No accounts yet.`}
</div>
</div>
<div class="buttons is-right mt-5">
- <button class="button is-info"
+ <button
+ class="button is-info"
data-tooltip={i18n`add tax to the tax list`}
disabled={hasErrors}
- onClick={submit}><Translate>Add</Translate></button>
+ onClick={submit}
+ >
+ <Translate>Add</Translate>
+ </button>
</div>
</FormProvider>
</InputGroup>
- )
+ );
}
diff --git
a/packages/merchant-backoffice/src/components/instance/DefaultInstanceFormFields.tsx
b/packages/merchant-backoffice/src/components/instance/DefaultInstanceFormFields.tsx
index fae8a35..5a8e1d1 100644
---
a/packages/merchant-backoffice/src/components/instance/DefaultInstanceFormFields.tsx
+++
b/packages/merchant-backoffice/src/components/instance/DefaultInstanceFormFields.tsx
@@ -15,9 +15,9 @@
*/
/**
-*
-* @author Sebastian Javier Marchano (sebasjm)
-*/
+ *
+ * @author Sebastian Javier Marchano (sebasjm)
+ */
import { Fragment, h } from "preact";
import { useBackendContext } from "../../context/backend";
@@ -31,56 +31,85 @@ import { InputLocation } from "../form/InputLocation";
import { InputPaytoForm } from "../form/InputPaytoForm";
import { InputWithAddon } from "../form/InputWithAddon";
-export function DefaultInstanceFormFields({ readonlyId, showId }: {
readonlyId?: boolean; showId: boolean }) {
+export function DefaultInstanceFormFields({
+ readonlyId,
+ showId,
+}: {
+ readonlyId?: boolean;
+ showId: boolean;
+}) {
const i18n = useTranslator();
const backend = useBackendContext();
- return <Fragment>
- {showId && <InputWithAddon<Entity> name="id"
- addonBefore={`${backend.url}/instances/`} readonly={readonlyId}
- label={i18n`Identifier`}
- tooltip={i18n`Name of the instance in URLs. The 'default' instance is
special in that it is used to administer other instances.`} />
- }
-
- <Input<Entity> name="name"
- label={i18n`Business name`}
- tooltip={i18n`Legal name of the business represented by this instance.`}
/>
-
- <InputPaytoForm<Entity> name="payto_uris"
- label={i18n`Bank account`}
- tooltip={i18n`URI specifying bank account for crediting revenue.`} />
-
- <InputCurrency<Entity> name="default_max_deposit_fee"
- label={i18n`Default max deposit fee`}
- tooltip={i18n`Maximum deposit fees this merchant is willing to pay per
order by default.`} />
-
- <InputCurrency<Entity> name="default_max_wire_fee"
- label={i18n`Default max wire fee`}
- tooltip={i18n`Maximum wire fees this merchant is willing to pay per wire
transfer by default.`} />
-
- <Input<Entity> name="default_wire_fee_amortization"
- label={i18n`Default wire fee amortization`}
- tooltip={i18n`Number of orders excess wire transfer fees will be divided
by to compute per order surcharge.`} />
-
- <InputGroup name="address"
- label={i18n`Address`}
- tooltip={i18n`Physical location of the merchant.`}>
- <InputLocation name="address" />
- </InputGroup>
-
- <InputGroup name="jurisdiction"
- label={i18n`Jurisdiction`}
- tooltip={i18n`Jurisdiction for legal disputes with the merchant.`}>
- <InputLocation name="jurisdiction" />
- </InputGroup>
-
- <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`}
- 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>;
+ return (
+ <Fragment>
+ {showId && (
+ <InputWithAddon<Entity>
+ name="id"
+ addonBefore={`${backend.url}/instances/`}
+ readonly={readonlyId}
+ label={i18n`Identifier`}
+ tooltip={i18n`Name of the instance in URLs. The 'default' instance
is special in that it is used to administer other instances.`}
+ />
+ )}
+
+ <Input<Entity>
+ name="name"
+ label={i18n`Business name`}
+ tooltip={i18n`Legal name of the business represented by this
instance.`}
+ />
+
+ <InputPaytoForm<Entity>
+ name="payto_uris"
+ label={i18n`Bank account`}
+ tooltip={i18n`URI specifying bank account for crediting revenue.`}
+ />
+
+ <InputCurrency<Entity>
+ name="default_max_deposit_fee"
+ label={i18n`Default max deposit fee`}
+ tooltip={i18n`Maximum deposit fees this merchant is willing to pay per
order by default.`}
+ />
+
+ <InputCurrency<Entity>
+ name="default_max_wire_fee"
+ label={i18n`Default max wire fee`}
+ tooltip={i18n`Maximum wire fees this merchant is willing to pay per
wire transfer by default.`}
+ />
+
+ <Input<Entity>
+ name="default_wire_fee_amortization"
+ label={i18n`Default wire fee amortization`}
+ tooltip={i18n`Number of orders excess wire transfer fees will be
divided by to compute per order surcharge.`}
+ />
+
+ <InputGroup
+ name="address"
+ label={i18n`Address`}
+ tooltip={i18n`Physical location of the merchant.`}
+ >
+ <InputLocation name="address" />
+ </InputGroup>
+
+ <InputGroup
+ name="jurisdiction"
+ label={i18n`Jurisdiction`}
+ tooltip={i18n`Jurisdiction for legal disputes with the merchant.`}
+ >
+ <InputLocation name="jurisdiction" />
+ </InputGroup>
+
+ <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`}
+ 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/merchant-backoffice/src/i18n/index.tsx
b/packages/merchant-backoffice/src/i18n/index.tsx
index 63c8e19..f89ab31 100644
--- a/packages/merchant-backoffice/src/i18n/index.tsx
+++ b/packages/merchant-backoffice/src/i18n/index.tsx
@@ -27,23 +27,25 @@ import { useTranslationContext } from
"../context/translation";
export function useTranslator() {
const ctx = useTranslationContext();
- const jed = ctx.handler
- return function str(stringSeq: TemplateStringsArray, ...values: any[]):
string {
+ const jed = ctx.handler;
+ return function str(
+ stringSeq: TemplateStringsArray,
+ ...values: any[]
+ ): string {
const s = toI18nString(stringSeq);
- if (!s) return s
+ if (!s) return s;
const tr = jed
.translate(s)
.ifPlural(1, s)
.fetch(...values);
return tr;
- }
+ };
}
-
/**
* Convert template strings to a msgid
*/
- function toI18nString(stringSeq: ReadonlyArray<string>): string {
+function toI18nString(stringSeq: ReadonlyArray<string>): string {
let s = "";
for (let i = 0; i < stringSeq.length; i++) {
s += stringSeq[i];
@@ -54,7 +56,6 @@ export function useTranslator() {
return s;
}
-
interface TranslateSwitchProps {
target: number;
children: ComponentChildren;
@@ -88,7 +89,7 @@ interface TranslateProps {
function getTranslatedChildren(
translation: string,
- children: ComponentChildren,
+ children: ComponentChildren
): ComponentChild[] {
const tr = translation.split(/%(\d+)\$s/);
const childArray = children instanceof Array ? children : [children];
@@ -110,7 +111,7 @@ function getTranslatedChildren(
// Text
result.push(tr[i]);
} else {
- const childIdx = Number.parseInt(tr[i],10) - 1;
+ const childIdx = Number.parseInt(tr[i], 10) - 1;
result.push(placeholderChildren[childIdx]);
}
}
@@ -131,9 +132,9 @@ function getTranslatedChildren(
*/
export function Translate({ children }: TranslateProps): VNode {
const s = stringifyChildren(children);
- const ctx = useTranslationContext()
+ const ctx = useTranslationContext();
const translation: string = ctx.handler.ngettext(s, s, 1);
- const result = getTranslatedChildren(translation, children)
+ const result = getTranslatedChildren(translation, children);
return <Fragment>{result}</Fragment>;
}
@@ -154,14 +155,16 @@ export function TranslateSwitch({ children, target }:
TranslateSwitchProps) {
let plural: VNode<TranslationPluralProps> | undefined;
// const children = this.props.children;
if (children) {
- (children instanceof Array ? children : [children]).forEach((child: any)
=> {
- if (child.type === TranslatePlural) {
- plural = child;
+ (children instanceof Array ? children : [children]).forEach(
+ (child: any) => {
+ if (child.type === TranslatePlural) {
+ plural = child;
+ }
+ if (child.type === TranslateSingular) {
+ singular = child;
+ }
}
- if (child.type === TranslateSingular) {
- singular = child;
- }
- });
+ );
}
if (!singular || !plural) {
console.error("translation not found");
@@ -182,9 +185,12 @@ interface TranslationPluralProps {
/**
* See [[TranslateSwitch]].
*/
-export function TranslatePlural({ children, target }: TranslationPluralProps):
VNode {
+export function TranslatePlural({
+ children,
+ target,
+}: TranslationPluralProps): VNode {
const s = stringifyChildren(children);
- const ctx = useTranslationContext()
+ const ctx = useTranslationContext();
const translation = ctx.handler.ngettext(s, s, 1);
const result = getTranslatedChildren(translation, children);
return <Fragment>{result}</Fragment>;
@@ -193,11 +199,13 @@ export function TranslatePlural({ children, target }:
TranslationPluralProps): V
/**
* See [[TranslateSwitch]].
*/
-export function TranslateSingular({ children, target }:
TranslationPluralProps): VNode {
+export function TranslateSingular({
+ children,
+ target,
+}: TranslationPluralProps): VNode {
const s = stringifyChildren(children);
- const ctx = useTranslationContext()
+ const ctx = useTranslationContext();
const translation = ctx.handler.ngettext(s, s, target);
const result = getTranslatedChildren(translation, children);
return <Fragment>{result}</Fragment>;
-
}
diff --git a/packages/merchant-backoffice/src/paths/admin/create/CreatePage.tsx
b/packages/merchant-backoffice/src/paths/admin/create/CreatePage.tsx
index f5fa7c9..f1214c9 100644
--- a/packages/merchant-backoffice/src/paths/admin/create/CreatePage.tsx
+++ b/packages/merchant-backoffice/src/paths/admin/create/CreatePage.tsx
@@ -15,15 +15,18 @@
*/
/**
-*
-* @author Sebastian Javier Marchano (sebasjm)
-*/
+ *
+ * @author Sebastian Javier Marchano (sebasjm)
+ */
import { h, VNode } from "preact";
import { useState } from "preact/hooks";
-import * as yup from 'yup';
+import * as yup from "yup";
import { AsyncButton } from "../../../components/exception/AsyncButton";
-import { FormErrors, FormProvider } from
"../../../components/form/FormProvider";
+import {
+ FormErrors,
+ FormProvider,
+} from "../../../components/form/FormProvider";
import { SetTokenNewInstanceModal } from "../../../components/modal";
import { MerchantBackend } from "../../../declaration";
import { Translate, useTranslator } from "../../../i18n";
@@ -31,10 +34,9 @@ import { DefaultInstanceFormFields } from
"../../../components/instance/DefaultI
import { INSTANCE_ID_REGEX, PAYTO_REGEX } from "../../../utils/constants";
import { Amounts } from "@gnu-taler/taler-util";
-export type Entity = MerchantBackend.Instances.InstanceConfigurationMessage &
{
- auth_token?: string
-}
-
+export type Entity = MerchantBackend.Instances.InstanceConfigurationMessage & {
+ auth_token?: string;
+};
interface Props {
onCreate: (d: Entity) => Promise<void>;
@@ -53,137 +55,180 @@ function with_defaults(id?: string): Partial<Entity> {
}
function undefinedIfEmpty<T>(obj: T): T | undefined {
- return Object.keys(obj).some(k => (obj as any)[k] !== undefined) ? obj :
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 [value, valueHandler] = useState(with_defaults(forceId));
const [isTokenSet, updateIsTokenSet] = useState<boolean>(false);
- const [isTokenDialogActive, updateIsTokenDialogActive] =
useState<boolean>(false);
+ const [isTokenDialogActive, updateIsTokenDialogActive] =
+ useState<boolean>(false);
- const i18n = useTranslator()
+ const i18n = useTranslator();
const errors: FormErrors<Entity> = {
- id: !value.id ? i18n`required` : (!INSTANCE_ID_REGEX.test(value.id) ?
i18n`is not valid` : undefined),
+ id: !value.id
+ ? i18n`required`
+ : !INSTANCE_ID_REGEX.test(value.id)
+ ? i18n`is not valid`
+ : undefined,
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
- ),
+ !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,
+ 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
+ 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
+ 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 hasErrors = Object.keys(errors).some(
+ (k) => (errors as any)[k] !== undefined
+ );
const submit = (): Promise<void> => {
// use conversion instead of this
const newToken = value.auth_token;
value.auth_token = undefined;
- value.auth = newToken === null || newToken === undefined ? { method:
"external" } : { method: "token", token: `secret-token:${newToken}` };
- if (!value.address) value.address = {}
- if (!value.jurisdiction) value.jurisdiction = {}
+ value.auth =
+ newToken === null || newToken === undefined
+ ? { method: "external" }
+ : { method: "token", token: `secret-token:${newToken}` };
+ if (!value.address) value.address = {};
+ if (!value.jurisdiction) value.jurisdiction = {};
// remove above use conversion
// schema.validateSync(value, { abortEarly: false })
return onCreate(value as Entity);
- }
+ };
function updateToken(token: string | null) {
- valueHandler(old => ({ ...old, auth_token: token === null ? undefined :
token }))
+ valueHandler((old) => ({
+ ...old,
+ auth_token: token === null ? undefined : token,
+ }));
}
- return <div>
- <div class="columns">
- <div class="column" />
- <div class="column is-four-fifths">
- {isTokenDialogActive && <SetTokenNewInstanceModal
- onCancel={() => {
- updateIsTokenDialogActive(false);
- updateIsTokenSet(false);
- }}
- onClear={() => {
- updateToken(null);
- updateIsTokenDialogActive(false);
- updateIsTokenSet(true);
- }}
- onConfirm={(newToken) => {
- updateToken(newToken); updateIsTokenDialogActive(false);
- updateIsTokenSet(true);
- }}
- />}
- </div>
- <div class="column" />
- </div>
-
- <section class="hero is-hero-bar">
- <div class="hero-body">
- <div class="level">
- <div class="level-item has-text-centered">
- <h1 class="title">
- <button class="button is-danger has-tooltip-bottom"
- data-tooltip={i18n`change authorization configuration`}
- onClick={() => updateIsTokenDialogActive(true)} >
- <div class="icon is-centered"><i class="mdi mdi-lock-reset"
/></div>
- <span><Translate>Set access token</Translate></span>
- </button>
- </h1>
- </div>
- </div>
- </div></section>
-
-
- <section class="section is-main-section">
+ return (
+ <div>
<div class="columns">
<div class="column" />
<div class="column is-four-fifths">
-
- <FormProvider<Entity> errors={errors} object={value}
valueHandler={valueHandler} >
-
- <DefaultInstanceFormFields readonlyId={!!forceId} showId={true} />
-
- </FormProvider>
-
- <div class="buttons is-right mt-5">
- {onBack && <button class="button"
onClick={onBack}><Translate>Cancel</Translate></button>}
- <AsyncButton onClick={submit} disabled={!isTokenSet || hasErrors}
data-tooltip={
- hasErrors ? i18n`Need to complete marked fields and choose
authorization method` : 'confirm operation'
- }><Translate>Confirm</Translate></AsyncButton>
- </div>
-
+ {isTokenDialogActive && (
+ <SetTokenNewInstanceModal
+ onCancel={() => {
+ updateIsTokenDialogActive(false);
+ updateIsTokenSet(false);
+ }}
+ onClear={() => {
+ updateToken(null);
+ updateIsTokenDialogActive(false);
+ updateIsTokenSet(true);
+ }}
+ onConfirm={(newToken) => {
+ updateToken(newToken);
+ updateIsTokenDialogActive(false);
+ updateIsTokenSet(true);
+ }}
+ />
+ )}
</div>
<div class="column" />
</div>
- </section>
- </div>
+ <section class="hero is-hero-bar">
+ <div class="hero-body">
+ <div class="level">
+ <div class="level-item has-text-centered">
+ <h1 class="title">
+ <button
+ class="button is-danger has-tooltip-bottom"
+ data-tooltip={i18n`change authorization configuration`}
+ onClick={() => updateIsTokenDialogActive(true)}
+ >
+ <div class="icon is-centered">
+ <i class="mdi mdi-lock-reset" />
+ </div>
+ <span>
+ <Translate>Set access token</Translate>
+ </span>
+ </button>
+ </h1>
+ </div>
+ </div>
+ </div>
+ </section>
+
+ <section class="section is-main-section">
+ <div class="columns">
+ <div class="column" />
+ <div class="column is-four-fifths">
+ <FormProvider<Entity>
+ errors={errors}
+ object={value}
+ valueHandler={valueHandler}
+ >
+ <DefaultInstanceFormFields readonlyId={!!forceId} showId={true}
/>
+ </FormProvider>
+
+ <div class="buttons is-right mt-5">
+ {onBack && (
+ <button class="button" onClick={onBack}>
+ <Translate>Cancel</Translate>
+ </button>
+ )}
+ <AsyncButton
+ onClick={submit}
+ disabled={!isTokenSet || hasErrors}
+ data-tooltip={
+ hasErrors
+ ? i18n`Need to complete marked fields and choose
authorization method`
+ : "confirm operation"
+ }
+ >
+ <Translate>Confirm</Translate>
+ </AsyncButton>
+ </div>
+ </div>
+ <div class="column" />
+ </div>
+ </section>
+ </div>
+ );
}
diff --git a/packages/merchant-backoffice/src/paths/admin/create/index.tsx
b/packages/merchant-backoffice/src/paths/admin/create/index.tsx
index e240bb6..3f31b3d 100644
--- a/packages/merchant-backoffice/src/paths/admin/create/index.tsx
+++ b/packages/merchant-backoffice/src/paths/admin/create/index.tsx
@@ -14,9 +14,9 @@
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
/**
-*
-* @author Sebastian Javier Marchano (sebasjm)
-*/
+ *
+ * @author Sebastian Javier Marchano (sebasjm)
+ */
import { Fragment, h, VNode } from "preact";
import { useState } from "preact/hooks";
import { NotificationCard } from "../../../components/menu";
@@ -36,31 +36,39 @@ export type Entity =
MerchantBackend.Instances.InstanceConfigurationMessage;
export default function Create({ onBack, onConfirm, forceId }: Props): VNode {
const { createInstance } = useAdminAPI();
- const [notif, setNotif] = useState<Notification | undefined>(undefined)
+ const [notif, setNotif] = useState<Notification | undefined>(undefined);
const [createdOk, setCreatedOk] = useState<Entity | undefined>(undefined);
- const i18n = useTranslator()
+ const i18n = useTranslator();
if (createdOk) {
- return <InstanceCreatedSuccessfully entity={createdOk}
onConfirm={onConfirm} />
+ return (
+ <InstanceCreatedSuccessfully entity={createdOk} onConfirm={onConfirm} />
+ );
}
- return <Fragment>
- <NotificationCard notification={notif} />
-
- <CreatePage
- onBack={onBack}
- forceId={forceId}
- onCreate={(d: MerchantBackend.Instances.InstanceConfigurationMessage) =>
{
- return createInstance(d).then(() => {
- setCreatedOk(d)
- }).catch((error) => {
- setNotif({
- message: i18n`Failed to create instance`,
- type: "ERROR",
- description: error.message
- })
- })
- }} />
- </Fragment>
+ return (
+ <Fragment>
+ <NotificationCard notification={notif} />
+ <CreatePage
+ onBack={onBack}
+ forceId={forceId}
+ onCreate={(
+ d: MerchantBackend.Instances.InstanceConfigurationMessage
+ ) => {
+ return createInstance(d)
+ .then(() => {
+ setCreatedOk(d);
+ })
+ .catch((error) => {
+ setNotif({
+ message: i18n`Failed to create instance`,
+ type: "ERROR",
+ description: error.message,
+ });
+ });
+ }}
+ />
+ </Fragment>
+ );
}
diff --git
a/packages/merchant-backoffice/src/paths/instance/orders/create/CreatePage.tsx
b/packages/merchant-backoffice/src/paths/instance/orders/create/CreatePage.tsx
index e0e9970..4a5af6b 100644
---
a/packages/merchant-backoffice/src/paths/instance/orders/create/CreatePage.tsx
+++
b/packages/merchant-backoffice/src/paths/instance/orders/create/CreatePage.tsx
@@ -15,15 +15,18 @@
*/
/**
-*
-* @author Sebastian Javier Marchano (sebasjm)
-*/
+ *
+ * @author Sebastian Javier Marchano (sebasjm)
+ */
import { add, isAfter, isBefore, isFuture } from "date-fns";
import { Amounts } from "@gnu-taler/taler-util";
import { Fragment, h, VNode } from "preact";
import { useEffect, useState } from "preact/hooks";
-import { FormProvider, FormErrors } from
"../../../../components/form/FormProvider";
+import {
+ FormProvider,
+ FormErrors,
+} from "../../../../components/form/FormProvider";
import { Input } from "../../../../components/form/Input";
import { InputCurrency } from "../../../../components/form/InputCurrency";
import { InputDate } from "../../../../components/form/InputDate";
@@ -33,7 +36,7 @@ import { ProductList } from
"../../../../components/product/ProductList";
import { useConfigContext } from "../../../../context/config";
import { Duration, MerchantBackend, WithId } from "../../../../declaration";
import { Translate, useTranslator } from "../../../../i18n";
-import { OrderCreateSchema as schema } from '../../../../schemas/index';
+import { OrderCreateSchema as schema } from "../../../../schemas/index";
import { rate } from "../../../../utils/amount";
import { InventoryProductForm } from
"../../../../components/product/InventoryProductForm";
import { NonInventoryProductFrom } from
"../../../../components/product/NonInventoryProductForm";
@@ -43,7 +46,7 @@ interface Props {
onCreate: (d: MerchantBackend.Orders.PostOrderRequest) => void;
onBack?: () => void;
instanceConfig: InstanceConfig;
- instanceInventory: (MerchantBackend.Products.ProductDetail & WithId)[],
+ instanceInventory: (MerchantBackend.Products.ProductDetail & WithId)[];
}
interface InstanceConfig {
default_max_wire_fee: string;
@@ -53,9 +56,10 @@ interface InstanceConfig {
}
function with_defaults(config: InstanceConfig): Partial<Entity> {
- const defaultPayDeadline = !config.default_pay_delay ||
config.default_pay_delay.d_ms === "forever" ?
- undefined :
- add(new Date(), { seconds: config.default_pay_delay.d_ms / 1000 })
+ const defaultPayDeadline =
+ !config.default_pay_delay || config.default_pay_delay.d_ms === "forever"
+ ? undefined
+ : add(new Date(), { seconds: config.default_pay_delay.d_ms / 1000 });
return {
inventoryProducts: {},
@@ -69,12 +73,12 @@ function with_defaults(config: InstanceConfig):
Partial<Entity> {
refund_deadline: defaultPayDeadline,
},
shipping: {},
- extra: ''
+ extra: "",
};
}
interface ProductAndQuantity {
- product: MerchantBackend.Products.ProductDetail & WithId,
+ product: MerchantBackend.Products.ProductDetail & WithId;
quantity: number;
}
export interface ProductMap {
@@ -100,8 +104,8 @@ interface Payments {
wire_fee_amortization?: number;
}
interface Entity {
- inventoryProducts: ProductMap,
- products: MerchantBackend.Product[],
+ inventoryProducts: ProductMap;
+ products: MerchantBackend.Product[];
pricing: Partial<Pricing>;
payments: Partial<Payments>;
shipping: Partial<Shipping>;
@@ -110,65 +114,88 @@ interface Entity {
const stringIsValidJSON = (value: string) => {
try {
- JSON.parse(value.trim())
- return true
+ JSON.parse(value.trim());
+ return true;
} catch {
- return false
+ return false;
}
-}
+};
function undefinedIfEmpty<T>(obj: T): T | undefined {
- return Object.keys(obj).some(k => (obj as any)[k] !== undefined) ? obj :
undefined
+ return Object.keys(obj).some((k) => (obj as any)[k] !== undefined)
+ ? obj
+ : undefined;
}
-export function CreatePage({ onCreate, onBack, instanceConfig,
instanceInventory }: Props): VNode {
- const [value, valueHandler] = useState(with_defaults(instanceConfig))
- const config = useConfigContext()
- const zero = Amounts.getZero(config.currency)
+export function CreatePage({
+ onCreate,
+ onBack,
+ instanceConfig,
+ instanceInventory,
+}: Props): VNode {
+ const [value, valueHandler] = useState(with_defaults(instanceConfig));
+ const config = useConfigContext();
+ const zero = Amounts.getZero(config.currency);
const inventoryList = Object.values(value.inventoryProducts || {});
const productList = Object.values(value.products || {});
const i18n = useTranslator();
-
+
const errors: FormErrors<Entity> = {
pricing: undefinedIfEmpty({
summary: !value.pricing?.summary ? i18n`required` : undefined,
- order_price: !value.pricing?.order_price ? i18n`required` : (
- (Amounts.parse(value.pricing.order_price)?.value || 0) <= 0 ?
- i18n`must be greater than 0` : undefined
- )
+ order_price: !value.pricing?.order_price
+ ? i18n`required`
+ : (Amounts.parse(value.pricing.order_price)?.value || 0) <= 0
+ ? i18n`must be greater than 0`
+ : undefined,
}),
- extra: value.extra && !stringIsValidJSON(value.extra) ? i18n`not a valid
json` : undefined,
+ extra:
+ value.extra && !stringIsValidJSON(value.extra)
+ ? i18n`not a valid json`
+ : undefined,
payments: undefinedIfEmpty({
- refund_deadline: !value.payments?.refund_deadline ? i18n`required` : (
- !isFuture(value.payments.refund_deadline) ? i18n`should be in the
future` : (
- value.payments.pay_deadline &&
isBefore(value.payments.refund_deadline, value.payments.pay_deadline) ?
- i18n`pay deadline cannot be before refund deadline` : undefined
- )
- ),
- pay_deadline: !value.payments?.pay_deadline ? i18n`required` : (
- !isFuture(value.payments.pay_deadline) ? i18n`should be in the future`
: undefined
- ),
- auto_refund_deadline: !value.payments?.auto_refund_deadline ? undefined
: (
- !isFuture(value.payments.auto_refund_deadline) ? i18n`should be in the
future` : (
- !value.payments?.refund_deadline ? i18n`should have a refund
deadline` : (
- !isAfter(value.payments.refund_deadline,
value.payments.auto_refund_deadline) ?
- i18n`auto refund cannot be after refund deadline` : undefined
+ refund_deadline: !value.payments?.refund_deadline
+ ? i18n`required`
+ : !isFuture(value.payments.refund_deadline)
+ ? i18n`should be in the future`
+ : value.payments.pay_deadline &&
+ isBefore(value.payments.refund_deadline, value.payments.pay_deadline)
+ ? i18n`pay deadline cannot be before refund deadline`
+ : undefined,
+ pay_deadline: !value.payments?.pay_deadline
+ ? i18n`required`
+ : !isFuture(value.payments.pay_deadline)
+ ? i18n`should be in the future`
+ : undefined,
+ auto_refund_deadline: !value.payments?.auto_refund_deadline
+ ? undefined
+ : !isFuture(value.payments.auto_refund_deadline)
+ ? i18n`should be in the future`
+ : !value.payments?.refund_deadline
+ ? i18n`should have a refund deadline`
+ : !isAfter(
+ value.payments.refund_deadline,
+ value.payments.auto_refund_deadline
)
- )
- ),
+ ? i18n`auto refund cannot be after refund deadline`
+ : undefined,
}),
shipping: undefinedIfEmpty({
- delivery_date: !value.shipping?.delivery_date ? undefined : (
- !isFuture(value.shipping.delivery_date) ? i18n`should be in the
future` : undefined
- ),
+ delivery_date: !value.shipping?.delivery_date
+ ? undefined
+ : !isFuture(value.shipping.delivery_date)
+ ? i18n`should be in the future`
+ : undefined,
}),
- }
- const hasErrors = Object.keys(errors).some(k => (errors as any)[k] !==
undefined)
+ };
+ const hasErrors = Object.keys(errors).some(
+ (k) => (errors as any)[k] !== undefined
+ );
const submit = (): void => {
- const order = schema.cast(value)
+ const order = schema.cast(value);
if (!value.payments) return;
if (!value.shipping) return;
@@ -178,198 +205,322 @@ export function CreatePage({ onCreate, onBack,
instanceConfig, instanceInventory
summary: order.pricing.summary,
products: productList,
extra: value.extra,
- pay_deadline: value.payments.pay_deadline ? { t_ms:
Math.floor(value.payments.pay_deadline.getTime() / 1000) * 1000 } : undefined,
- wire_transfer_deadline: value.payments.pay_deadline ? { t_ms:
Math.floor(value.payments.pay_deadline.getTime() / 1000) * 1000 } : undefined,
- refund_deadline: value.payments.refund_deadline ? { t_ms:
Math.floor(value.payments.refund_deadline.getTime() / 1000) * 1000 } :
undefined,
+ pay_deadline: value.payments.pay_deadline
+ ? {
+ t_ms:
+ Math.floor(value.payments.pay_deadline.getTime() / 1000) *
1000,
+ }
+ : undefined,
+ wire_transfer_deadline: value.payments.pay_deadline
+ ? {
+ t_ms:
+ Math.floor(value.payments.pay_deadline.getTime() / 1000) *
1000,
+ }
+ : undefined,
+ refund_deadline: value.payments.refund_deadline
+ ? {
+ t_ms:
+ Math.floor(value.payments.refund_deadline.getTime() / 1000) *
+ 1000,
+ }
+ : undefined,
wire_fee_amortization: value.payments.wire_fee_amortization,
max_fee: value.payments.max_fee,
max_wire_fee: value.payments.max_wire_fee,
- delivery_date: value.shipping.delivery_date ? { t_ms:
value.shipping.delivery_date.getTime() } : undefined,
+ delivery_date: value.shipping.delivery_date
+ ? { t_ms: value.shipping.delivery_date.getTime() }
+ : undefined,
delivery_location: value.shipping.delivery_location,
fulfillment_url: value.shipping.fullfilment_url,
},
- inventory_products: inventoryList.map(p => ({
+ inventory_products: inventoryList.map((p) => ({
product_id: p.product.id,
- quantity: p.quantity
+ quantity: p.quantity,
})),
- }
+ };
onCreate(request);
- }
+ };
- const addProductToTheInventoryList = (product:
MerchantBackend.Products.ProductDetail & WithId, quantity: number) => {
- valueHandler(v => {
- const inventoryProducts = { ...v.inventoryProducts }
- inventoryProducts[product.id] = { product, quantity }
- return ({ ...v, inventoryProducts })
- })
- }
+ const addProductToTheInventoryList = (
+ product: MerchantBackend.Products.ProductDetail & WithId,
+ quantity: number
+ ) => {
+ valueHandler((v) => {
+ const inventoryProducts = { ...v.inventoryProducts };
+ inventoryProducts[product.id] = { product, quantity };
+ return { ...v, inventoryProducts };
+ });
+ };
const removeProductFromTheInventoryList = (id: string) => {
- valueHandler(v => {
- const inventoryProducts = { ...v.inventoryProducts }
- delete inventoryProducts[id]
- return ({ ...v, inventoryProducts })
- })
- }
+ valueHandler((v) => {
+ const inventoryProducts = { ...v.inventoryProducts };
+ delete inventoryProducts[id];
+ return { ...v, inventoryProducts };
+ });
+ };
const addNewProduct = async (product: MerchantBackend.Product) => {
- return valueHandler(v => {
- const products = v.products ? [...v.products, product] : []
- return ({ ...v, products })
- })
- }
+ return valueHandler((v) => {
+ const products = v.products ? [...v.products, product] : [];
+ return { ...v, products };
+ });
+ };
const removeFromNewProduct = (index: number) => {
- valueHandler(v => {
- const products = v.products ? [...v.products] : []
- products.splice(index, 1)
- return ({ ...v, products })
- })
- }
+ valueHandler((v) => {
+ const products = v.products ? [...v.products] : [];
+ products.splice(index, 1);
+ return { ...v, products };
+ });
+ };
- const [editingProduct, setEditingProduct] = useState<MerchantBackend.Product
| undefined>(undefined)
+ const [editingProduct, setEditingProduct] = useState<
+ MerchantBackend.Product | undefined
+ >(undefined);
const totalPriceInventory = inventoryList.reduce((prev, cur) => {
- const p = Amounts.parseOrThrow(cur.product.price)
- return Amounts.add(prev, Amounts.mult(p, cur.quantity).amount).amount
- }, zero)
+ const p = Amounts.parseOrThrow(cur.product.price);
+ return Amounts.add(prev, Amounts.mult(p, cur.quantity).amount).amount;
+ }, zero);
const totalPriceProducts = productList.reduce((prev, cur) => {
- if (!cur.price) return zero
- const p = Amounts.parseOrThrow(cur.price)
- return Amounts.add(prev, Amounts.mult(p, cur.quantity).amount).amount
- }, zero)
+ if (!cur.price) return zero;
+ const p = Amounts.parseOrThrow(cur.price);
+ return Amounts.add(prev, Amounts.mult(p, cur.quantity).amount).amount;
+ }, zero);
- const hasProducts = inventoryList.length > 0 || productList.length > 0
- const totalPrice = Amounts.add(totalPriceInventory, totalPriceProducts)
+ const hasProducts = inventoryList.length > 0 || productList.length > 0;
+ const totalPrice = Amounts.add(totalPriceInventory, totalPriceProducts);
const totalAsString = Amounts.stringify(totalPrice.amount);
- const allProducts = productList.concat(inventoryList.map(asProduct))
+ const allProducts = productList.concat(inventoryList.map(asProduct));
useEffect(() => {
- valueHandler(v => {
- return ({
- ...v, pricing: {
+ valueHandler((v) => {
+ return {
+ ...v,
+ pricing: {
...v.pricing,
products_price: hasProducts ? totalAsString : undefined,
order_price: hasProducts ? totalAsString : undefined,
- }
- })
- })
- }, [hasProducts, totalAsString])
-
- const discountOrRise = rate(value.pricing?.order_price ||
`${config.currency}:0`, totalAsString)
-
- return <div>
-
- <section class="section is-main-section">
- <div class="columns">
- <div class="column" />
- <div class="column is-four-fifths">
-
- {/* // FIXME: translating plural singular */}
- <InputGroup name="inventory_products" label={i18n`Manage products in
order`} alternative={
- allProducts.length > 0 && <p>
- {allProducts.length} products
- with a total price of {totalAsString}.
- </p>
- } tooltip={i18n`Manage list of products in the order.`}>
-
- <InventoryProductForm
- currentProducts={value.inventoryProducts || {}}
- onAddProduct={addProductToTheInventoryList}
- inventory={instanceInventory}
- />
-
- <NonInventoryProductFrom productToEdit={editingProduct}
onAddProduct={(p) => {
- setEditingProduct(undefined)
- return addNewProduct(p)
- }} />
-
- {allProducts.length > 0 &&
- <ProductList list={allProducts}
- actions={[{
- name: i18n`Remove`,
- tooltip: i18n`Remove this product from the order.`,
- handler: (e, index) => {
- if (e.product_id) {
- removeProductFromTheInventoryList(e.product_id)
- } else {
- removeFromNewProduct(index);
- setEditingProduct(e);
- }
- }
- }]}
+ },
+ };
+ });
+ }, [hasProducts, totalAsString]);
+
+ const discountOrRise = rate(
+ value.pricing?.order_price || `${config.currency}:0`,
+ totalAsString
+ );
+
+ return (
+ <div>
+ <section class="section is-main-section">
+ <div class="columns">
+ <div class="column" />
+ <div class="column is-four-fifths">
+ {/* // FIXME: translating plural singular */}
+ <InputGroup
+ name="inventory_products"
+ label={i18n`Manage products in order`}
+ alternative={
+ allProducts.length > 0 && (
+ <p>
+ {allProducts.length} products with a total price of{" "}
+ {totalAsString}.
+ </p>
+ )
+ }
+ tooltip={i18n`Manage list of products in the order.`}
+ >
+ <InventoryProductForm
+ currentProducts={value.inventoryProducts || {}}
+ onAddProduct={addProductToTheInventoryList}
+ inventory={instanceInventory}
/>
- }
- </InputGroup>
-
- <FormProvider<Entity> errors={errors} object={value}
valueHandler={valueHandler as any}>
- {hasProducts ?
- <Fragment>
- <InputCurrency name="pricing.products_price" label={i18n`Total
price`} readonly tooltip={i18n`total product price added up`} />
- <InputCurrency name="pricing.order_price"
- label={i18n`Total price`}
- addonAfter={discountOrRise > 0 && (discountOrRise < 1 ?
- `discount of %${Math.round((1 - discountOrRise) * 100)}` :
- `rise of %${Math.round((discountOrRise - 1) * 100)}`)
- }
- tooltip={i18n`Amount to be paid by the customer`}
- />
- </Fragment> :
- <InputCurrency name="pricing.order_price" label={i18n`Order
price`} tooltip={i18n`final order price`} />
- }
- <Input name="pricing.summary" inputType="multiline"
label={i18n`Summary`} tooltip={i18n`Title of the order to be shown to the
customer`} />
+ <NonInventoryProductFrom
+ productToEdit={editingProduct}
+ onAddProduct={(p) => {
+ setEditingProduct(undefined);
+ return addNewProduct(p);
+ }}
+ />
- <InputGroup name="shipping" label={i18n`Shipping and Fulfillment`}
initialActive >
- <InputDate name="shipping.delivery_date" label={i18n`Delivery
date`} tooltip={i18n`Deadline for physical delivery assured by the merchant.`}
/>
- {value.shipping?.delivery_date &&
- <InputGroup name="shipping.delivery_location"
label={i18n`Location`} tooltip={i18n`address where the products will be
delivered`} >
- <InputLocation name="shipping.delivery_location" />
- </InputGroup>
- }
- <Input name="shipping.fullfilment_url" label={i18n`Fulfillment
URL`} tooltip={i18n`URL to which the user will be redirected after successful
payment.`} />
+ {allProducts.length > 0 && (
+ <ProductList
+ list={allProducts}
+ actions={[
+ {
+ name: i18n`Remove`,
+ tooltip: i18n`Remove this product from the order.`,
+ handler: (e, index) => {
+ if (e.product_id) {
+ removeProductFromTheInventoryList(e.product_id);
+ } else {
+ removeFromNewProduct(index);
+ setEditingProduct(e);
+ }
+ },
+ },
+ ]}
+ />
+ )}
</InputGroup>
- <InputGroup name="payments" label={i18n`Taler payment options`}
tooltip={i18n`Override default Taler payment settings for this order`}>
- <InputDate name="payments.pay_deadline" label={i18n`Payment
deadline`} tooltip={i18n`Deadline for the customer to pay for the offer before
it expires. Inventory products will be reserved until this deadline.`} />
- <InputDate name="payments.refund_deadline" label={i18n`Refund
deadline`} tooltip={i18n`Time until which the order can be refunded by the
merchant.`} />
- <InputDate name="payments.auto_refund_deadline"
label={i18n`Auto-refund deadline`} tooltip={i18n`Time until which the wallet
will automatically check for refunds without user interaction.`} />
+ <FormProvider<Entity>
+ errors={errors}
+ object={value}
+ valueHandler={valueHandler as any}
+ >
+ {hasProducts ? (
+ <Fragment>
+ <InputCurrency
+ name="pricing.products_price"
+ label={i18n`Total price`}
+ readonly
+ tooltip={i18n`total product price added up`}
+ />
+ <InputCurrency
+ name="pricing.order_price"
+ label={i18n`Total price`}
+ addonAfter={
+ discountOrRise > 0 &&
+ (discountOrRise < 1
+ ? `discount of %${Math.round(
+ (1 - discountOrRise) * 100
+ )}`
+ : `rise of %${Math.round((discountOrRise - 1) * 100)}`)
+ }
+ tooltip={i18n`Amount to be paid by the customer`}
+ />
+ </Fragment>
+ ) : (
+ <InputCurrency
+ name="pricing.order_price"
+ label={i18n`Order price`}
+ tooltip={i18n`final order price`}
+ />
+ )}
- <InputCurrency name="payments.max_fee" label={i18n`Maximum
deposit fee`} tooltip={i18n`Maximum deposit fees the merchant is willing to
cover for this order. Higher deposit fees must be covered in full by the
consumer.`} />
- <InputCurrency name="payments.max_wire_fee" label={i18n`Maximum
wire fee`} tooltip={i18n`Maximum aggregate wire fees the merchant is willing to
cover for this order. Wire fees exceeding this amount are to be covered by the
customers.`} />
- <InputNumber name="payments.wire_fee_amortization"
label={i18n`Wire fee amortization`} tooltip={i18n`Factor by which wire fees
exceeding the above threshold are divided to determine the share of excess wire
fees to be paid explicitly by the consumer.`} />
- </InputGroup>
+ <Input
+ name="pricing.summary"
+ inputType="multiline"
+ label={i18n`Summary`}
+ tooltip={i18n`Title of the order to be shown to the customer`}
+ />
- <InputGroup name="extra" label={i18n`Additional information`}
tooltip={i18n`Custom information to be included in the contract for this
order.`}>
- <Input name="extra" inputType="multiline" label={`Value`}
tooltip={i18n`You must enter a value in JavaScript Object Notation (JSON).`} />
- </InputGroup>
- </FormProvider>
+ <InputGroup
+ name="shipping"
+ label={i18n`Shipping and Fulfillment`}
+ initialActive
+ >
+ <InputDate
+ name="shipping.delivery_date"
+ label={i18n`Delivery date`}
+ tooltip={i18n`Deadline for physical delivery assured by the
merchant.`}
+ />
+ {value.shipping?.delivery_date && (
+ <InputGroup
+ name="shipping.delivery_location"
+ label={i18n`Location`}
+ tooltip={i18n`address where the products will be
delivered`}
+ >
+ <InputLocation name="shipping.delivery_location" />
+ </InputGroup>
+ )}
+ <Input
+ name="shipping.fullfilment_url"
+ label={i18n`Fulfillment URL`}
+ tooltip={i18n`URL to which the user will be redirected after
successful payment.`}
+ />
+ </InputGroup>
+
+ <InputGroup
+ name="payments"
+ label={i18n`Taler payment options`}
+ tooltip={i18n`Override default Taler payment settings for this
order`}
+ >
+ <InputDate
+ name="payments.pay_deadline"
+ label={i18n`Payment deadline`}
+ tooltip={i18n`Deadline for the customer to pay for the offer
before it expires. Inventory products will be reserved until this deadline.`}
+ />
+ <InputDate
+ name="payments.refund_deadline"
+ label={i18n`Refund deadline`}
+ tooltip={i18n`Time until which the order can be refunded by
the merchant.`}
+ />
+ <InputDate
+ name="payments.auto_refund_deadline"
+ label={i18n`Auto-refund deadline`}
+ tooltip={i18n`Time until which the wallet will automatically
check for refunds without user interaction.`}
+ />
- <div class="buttons is-right mt-5">
- {onBack && <button class="button" onClick={onBack}
><Translate>Cancel</Translate></button>}
- <button class="button is-success" onClick={submit}
disabled={hasErrors} ><Translate>Confirm</Translate></button>
+ <InputCurrency
+ name="payments.max_fee"
+ label={i18n`Maximum deposit fee`}
+ tooltip={i18n`Maximum deposit fees the merchant is willing
to cover for this order. Higher deposit fees must be covered in full by the
consumer.`}
+ />
+ <InputCurrency
+ name="payments.max_wire_fee"
+ label={i18n`Maximum wire fee`}
+ tooltip={i18n`Maximum aggregate wire fees the merchant is
willing to cover for this order. Wire fees exceeding this amount are to be
covered by the customers.`}
+ />
+ <InputNumber
+ name="payments.wire_fee_amortization"
+ label={i18n`Wire fee amortization`}
+ tooltip={i18n`Factor by which wire fees exceeding the above
threshold are divided to determine the share of excess wire fees to be paid
explicitly by the consumer.`}
+ />
+ </InputGroup>
+
+ <InputGroup
+ name="extra"
+ label={i18n`Additional information`}
+ tooltip={i18n`Custom information to be included in the
contract for this order.`}
+ >
+ <Input
+ name="extra"
+ inputType="multiline"
+ label={`Value`}
+ tooltip={i18n`You must enter a value in JavaScript Object
Notation (JSON).`}
+ />
+ </InputGroup>
+ </FormProvider>
+
+ <div class="buttons is-right mt-5">
+ {onBack && (
+ <button class="button" onClick={onBack}>
+ <Translate>Cancel</Translate>
+ </button>
+ )}
+ <button
+ class="button is-success"
+ onClick={submit}
+ disabled={hasErrors}
+ >
+ <Translate>Confirm</Translate>
+ </button>
+ </div>
</div>
-
+ <div class="column" />
</div>
- <div class="column" />
- </div>
- </section>
-
- </div>
+ </section>
+ </div>
+ );
}
function asProduct(p: ProductAndQuantity) {
- return ({
+ return {
product_id: p.product.id,
image: p.product.image,
price: p.product.price,
unit: p.product.unit,
quantity: p.quantity,
description: p.product.description,
- taxes: p.product.taxes
- })
+ taxes: p.product.taxes,
+ };
}
diff --git
a/packages/merchant-backoffice/src/paths/instance/update/UpdatePage.tsx
b/packages/merchant-backoffice/src/paths/instance/update/UpdatePage.tsx
index 0fa96ed..863f977 100644
--- a/packages/merchant-backoffice/src/paths/instance/update/UpdatePage.tsx
+++ b/packages/merchant-backoffice/src/paths/instance/update/UpdatePage.tsx
@@ -15,191 +15,247 @@
*/
/**
-*
-* @author Sebastian Javier Marchano (sebasjm)
-*/
+ *
+ * @author Sebastian Javier Marchano (sebasjm)
+ */
import { h, VNode } from "preact";
import { useState } from "preact/hooks";
-import * as yup from 'yup';
+import * as yup from "yup";
import { AsyncButton } from "../../../components/exception/AsyncButton";
-import { FormProvider, FormErrors } from
"../../../components/form/FormProvider";
+import {
+ FormProvider,
+ FormErrors,
+} from "../../../components/form/FormProvider";
import { UpdateTokenModal } from "../../../components/modal";
import { useInstanceContext } from "../../../context/instance";
import { MerchantBackend } from "../../../declaration";
import { Translate, useTranslator } from "../../../i18n";
-import { InstanceUpdateSchema as schema } from '../../../schemas';
+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
-}
+type Entity = MerchantBackend.Instances.InstanceReconfigurationMessage & {
+ auth_token?: string;
+};
//MerchantBackend.Instances.InstanceAuthConfigurationMessage
interface Props {
onUpdate: (d: Entity) => void;
- onChangeAuth: (d:
MerchantBackend.Instances.InstanceAuthConfigurationMessage) => Promise<void>;
+ onChangeAuth: (
+ d: MerchantBackend.Instances.InstanceAuthConfigurationMessage
+ ) => Promise<void>;
selected: MerchantBackend.Instances.QueryInstancesResponse;
isLoading: boolean;
onBack: () => void;
}
-function convert(from: MerchantBackend.Instances.QueryInstancesResponse):
Entity {
- const { accounts, ...rest } = from
- const payto_uris = accounts.filter(a => a.active).map(a => a.payto_uri)
+function convert(
+ from: MerchantBackend.Instances.QueryInstancesResponse
+): Entity {
+ const { accounts, ...rest } = from;
+ const payto_uris = accounts.filter((a) => a.active).map((a) => a.payto_uri);
const defaults = {
default_wire_fee_amortization: 1,
default_pay_delay: { d_ms: 1000 * 60 * 60 }, //one hour
default_wire_transfer_delay: { d_ms: 1000 * 60 * 60 * 2 }, //two hours
- }
+ };
return { ...defaults, ...rest, payto_uris };
}
function getTokenValuePart(t?: string): string | undefined {
- if (!t) return t
+ if (!t) return t;
const match = /secret-token:(.*)/.exec(t);
if (!match || !match[1]) return undefined;
- return match[1]
+ return match[1];
}
function undefinedIfEmpty<T>(obj: T): T | undefined {
- return Object.keys(obj).some(k => (obj as any)[k] !== undefined) ? obj :
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()
- const currentTokenValue = getTokenValuePart(token)
+export function UpdatePage({
+ onUpdate,
+ onChangeAuth,
+ selected,
+ onBack,
+}: Props): VNode {
+ const { id, token } = useInstanceContext();
+ const currentTokenValue = getTokenValuePart(token);
function updateToken(token: string | undefined | null) {
- const value = token && token.startsWith('secret-token:') ?
- token.substring('secret-token:'.length) : token
+ const value =
+ token && token.startsWith("secret-token:")
+ ? token.substring("secret-token:".length)
+ : token;
if (!token) {
- onChangeAuth({ method: 'external' })
+ onChangeAuth({ method: "external" });
} else {
- onChangeAuth({ method: 'token', token: `secret-token:${value}` })
+ onChangeAuth({ method: "token", token: `secret-token:${value}` });
}
}
- const [value, valueHandler] = useState<Partial<Entity>>(convert(selected))
+ const [value, valueHandler] = useState<Partial<Entity>>(convert(selected));
- const i18n = useTranslator()
+ 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
- ),
+ !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,
+ 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
+ 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
+ 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 hasErrors = Object.keys(errors).some(
+ (k) => (errors as any)[k] !== undefined
+ );
const submit = async (): Promise<void> => {
await onUpdate(schema.cast(value));
- await onBack()
- return Promise.resolve()
- }
+ await onBack();
+ return Promise.resolve();
+ };
const [active, setActive] = useState(false);
- return <div>
- <section class="section">
-
- <section class="hero is-hero-bar">
- <div class="hero-body">
-
- <div class="level">
- <div class="level-left">
- <div class="level-item">
- <span class="is-size-4"><Translate>Instance id</Translate>:
<b>{id}</b></span>
+ return (
+ <div>
+ <section class="section">
+ <section class="hero is-hero-bar">
+ <div class="hero-body">
+ <div class="level">
+ <div class="level-left">
+ <div class="level-item">
+ <span class="is-size-4">
+ <Translate>Instance id</Translate>: <b>{id}</b>
+ </span>
+ </div>
</div>
- </div>
- <div class="level-right">
- <div class="level-item">
- <h1 class="title">
- <button class="button is-danger"
- data-tooltip={i18n`Change the authorization method use for
this instance.`}
- onClick={(): void => { setActive(!active); }} >
- <div class="icon is-left"><i class="mdi mdi-lock-reset"
/></div>
- <span><Translate>Manage access token</Translate></span>
- </button>
- </h1>
+ <div class="level-right">
+ <div class="level-item">
+ <h1 class="title">
+ <button
+ class="button is-danger"
+ data-tooltip={i18n`Change the authorization method use
for this instance.`}
+ onClick={(): void => {
+ setActive(!active);
+ }}
+ >
+ <div class="icon is-left">
+ <i class="mdi mdi-lock-reset" />
+ </div>
+ <span>
+ <Translate>Manage access token</Translate>
+ </span>
+ </button>
+ </h1>
+ </div>
</div>
</div>
</div>
- </div></section>
-
- <div class="columns">
- <div class="column" />
- <div class="column is-four-fifths">
- {active && <UpdateTokenModal oldToken={currentTokenValue}
- onCancel={() => { setActive(false); }}
- onClear={() => { updateToken(null); setActive(false); }}
- onConfirm={(newToken) => {
- updateToken(newToken); setActive(false)
- }}
- />}
- </div>
- <div class="column" />
- </div>
- <hr />
-
- <div class="columns">
- <div class="column" />
- <div class="column is-four-fifths">
- <FormProvider<Entity> errors={errors} object={value}
valueHandler={valueHandler} >
-
- <DefaultInstanceFormFields showId={false} />
-
- </FormProvider>
+ </section>
- <div class="buttons is-right mt-4">
- <button class="button" onClick={onBack} data-tooltip="cancel
operation"><Translate>Cancel</Translate></button>
-
- <AsyncButton onClick={submit} data-tooltip={
- hasErrors ? i18n`Need to complete marked fields` : 'confirm
operation'
- } disabled={hasErrors}
><Translate>Confirm</Translate></AsyncButton>
+ <div class="columns">
+ <div class="column" />
+ <div class="column is-four-fifths">
+ {active && (
+ <UpdateTokenModal
+ oldToken={currentTokenValue}
+ onCancel={() => {
+ setActive(false);
+ }}
+ onClear={() => {
+ updateToken(null);
+ setActive(false);
+ }}
+ onConfirm={(newToken) => {
+ updateToken(newToken);
+ setActive(false);
+ }}
+ />
+ )}
</div>
+ <div class="column" />
</div>
- <div class="column" />
- </div>
+ <hr />
- </section>
+ <div class="columns">
+ <div class="column" />
+ <div class="column is-four-fifths">
+ <FormProvider<Entity>
+ errors={errors}
+ object={value}
+ valueHandler={valueHandler}
+ >
+ <DefaultInstanceFormFields showId={false} />
+ </FormProvider>
- </div >
+ <div class="buttons is-right mt-4">
+ <button
+ class="button"
+ onClick={onBack}
+ data-tooltip="cancel operation"
+ >
+ <Translate>Cancel</Translate>
+ </button>
+ <AsyncButton
+ onClick={submit}
+ data-tooltip={
+ hasErrors
+ ? i18n`Need to complete marked fields`
+ : "confirm operation"
+ }
+ disabled={hasErrors}
+ >
+ <Translate>Confirm</Translate>
+ </AsyncButton>
+ </div>
+ </div>
+ <div class="column" />
+ </div>
+ </section>
+ </div>
+ );
}
diff --git a/packages/merchant-backoffice/src/paths/instance/update/index.tsx
b/packages/merchant-backoffice/src/paths/instance/update/index.tsx
index 33dc476..38a652d 100644
--- a/packages/merchant-backoffice/src/paths/instance/update/index.tsx
+++ b/packages/merchant-backoffice/src/paths/instance/update/index.tsx
@@ -18,7 +18,12 @@ import { Loading } from
"../../../components/exception/loading";
import { useInstanceContext } from "../../../context/instance";
import { MerchantBackend } from "../../../declaration";
import { HttpError, HttpResponse } from "../../../hooks/backend";
-import { useInstanceAPI, useInstanceDetails, useManagedInstanceDetails,
useManagementAPI } from "../../../hooks/instance";
+import {
+ useInstanceAPI,
+ useInstanceDetails,
+ useManagedInstanceDetails,
+ useManagementAPI,
+} from "../../../hooks/instance";
import { UpdatePage } from "./UpdatePage";
export interface Props {
@@ -29,41 +34,65 @@ export interface Props {
onNotFound: () => VNode;
onLoadError: (e: HttpError) => VNode;
onUpdateError: (e: HttpError) => void;
-
}
export default function Update(props: Props): VNode {
const { updateInstance, clearToken, setNewToken } = useInstanceAPI();
- const result = useInstanceDetails()
- return CommonUpdate(props, result, updateInstance, clearToken, setNewToken)
+ const result = useInstanceDetails();
+ return CommonUpdate(props, result, updateInstance, clearToken, setNewToken);
}
-export function AdminUpdate(props:Props & {instanceId:string}): VNode {
- const { updateInstance, clearToken, setNewToken } =
useManagementAPI(props.instanceId);
- const result = useManagedInstanceDetails(props.instanceId)
- return CommonUpdate(props, result, updateInstance, clearToken, setNewToken)
+export function AdminUpdate(props: Props & { instanceId: string }): VNode {
+ const { updateInstance, clearToken, setNewToken } = useManagementAPI(
+ props.instanceId
+ );
+ const result = useManagedInstanceDetails(props.instanceId);
+ return CommonUpdate(props, result, updateInstance, clearToken, setNewToken);
}
-function CommonUpdate({ onBack, onConfirm, onLoadError, onNotFound,
onUpdateError, onUnauthorized }: Props, result:
HttpResponse<MerchantBackend.Instances.QueryInstancesResponse>, updateInstance:
any, clearToken: any, setNewToken: any): VNode {
- const { changeToken } = useInstanceContext()
+function CommonUpdate(
+ {
+ onBack,
+ onConfirm,
+ onLoadError,
+ onNotFound,
+ onUpdateError,
+ onUnauthorized,
+ }: Props,
+ result: HttpResponse<MerchantBackend.Instances.QueryInstancesResponse>,
+ updateInstance: any,
+ clearToken: any,
+ setNewToken: any
+): VNode {
+ const { changeToken } = useInstanceContext();
- if (result.clientError && result.isUnauthorized) return onUnauthorized()
- if (result.clientError && result.isNotfound) return onNotFound()
- if (result.loading) return <Loading />
- if (!result.ok) return onLoadError(result)
+ if (result.clientError && result.isUnauthorized) return onUnauthorized();
+ if (result.clientError && result.isNotfound) return onNotFound();
+ if (result.loading) return <Loading />;
+ if (!result.ok) return onLoadError(result);
- return <Fragment>
- <UpdatePage
- onBack={onBack}
- isLoading={false}
- selected={result.data}
- onUpdate={(d: MerchantBackend.Instances.InstanceReconfigurationMessage):
Promise<void> => {
- return updateInstance(d).then(onConfirm).catch(onUpdateError)
- }}
- onChangeAuth={(d:
MerchantBackend.Instances.InstanceAuthConfigurationMessage): Promise<void> => {
- const apiCall = d.method === 'external' ? clearToken() :
setNewToken(d.token!);
- return apiCall.then(() =>
changeToken(d.token)).then(onConfirm).catch(onUpdateError)
- }}
+ return (
+ <Fragment>
+ <UpdatePage
+ onBack={onBack}
+ isLoading={false}
+ selected={result.data}
+ onUpdate={(
+ d: MerchantBackend.Instances.InstanceReconfigurationMessage
+ ): Promise<void> => {
+ return updateInstance(d).then(onConfirm).catch(onUpdateError);
+ }}
+ onChangeAuth={(
+ d: MerchantBackend.Instances.InstanceAuthConfigurationMessage
+ ): Promise<void> => {
+ const apiCall =
+ d.method === "external" ? clearToken() : setNewToken(d.token!);
+ return apiCall
+ .then(() => changeToken(d.token))
+ .then(onConfirm)
+ .catch(onUpdateError);
+ }}
/>
- </Fragment>
-}
\ No newline at end of file
+ </Fragment>
+ );
+}
--
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.