summaryrefslogtreecommitdiff
path: root/packages/merchant-backoffice-ui/src/paths/admin/create/CreatePage.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'packages/merchant-backoffice-ui/src/paths/admin/create/CreatePage.tsx')
-rw-r--r--packages/merchant-backoffice-ui/src/paths/admin/create/CreatePage.tsx208
1 files changed, 126 insertions, 82 deletions
diff --git a/packages/merchant-backoffice-ui/src/paths/admin/create/CreatePage.tsx b/packages/merchant-backoffice-ui/src/paths/admin/create/CreatePage.tsx
index 0d7681e1d..4a5ab440b 100644
--- a/packages/merchant-backoffice-ui/src/paths/admin/create/CreatePage.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/admin/create/CreatePage.tsx
@@ -1,6 +1,6 @@
/*
This file is part of GNU Taler
- (C) 2021 Taler Systems S.A.
+ (C) 2021-2024 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
@@ -19,28 +19,35 @@
* @author Sebastian Javier Marchano (sebasjm)
*/
+import {
+ Duration,
+ TalerMerchantApi,
+ createAccessToken,
+} from "@gnu-taler/taler-util";
+import { useTranslationContext } from "@gnu-taler/web-util/browser";
import { h, VNode } from "preact";
import { useState } from "preact/hooks";
-import * as yup from "yup";
import { AsyncButton } from "../../../components/exception/AsyncButton.js";
import {
FormErrors,
FormProvider,
} from "../../../components/form/FormProvider.js";
-import { SetTokenNewInstanceModal } from "../../../components/modal/index.js";
-import { MerchantBackend } from "../../../declaration.js";
-import { Translate, useTranslator } from "../../../i18n/index.js";
import { DefaultInstanceFormFields } from "../../../components/instance/DefaultInstanceFormFields.js";
-import { INSTANCE_ID_REGEX, PAYTO_REGEX } from "../../../utils/constants.js";
-import { Amounts } from "@gnu-taler/taler-util";
+import { SetTokenNewInstanceModal } from "../../../components/modal/index.js";
+import { INSTANCE_ID_REGEX } from "../../../utils/constants.js";
import { undefinedIfEmpty } from "../../../utils/table.js";
-export type Entity = MerchantBackend.Instances.InstanceConfigurationMessage & {
+export type Entity = Omit<
+ Omit<TalerMerchantApi.InstanceConfigurationMessage, "default_pay_delay">,
+ "default_wire_transfer_delay"
+> & {
auth_token?: string;
+ default_pay_delay: Duration;
+ default_wire_transfer_delay: Duration;
};
interface Props {
- onCreate: (d: Entity) => Promise<void>;
+ onCreate: (d: TalerMerchantApi.InstanceConfigurationMessage) => Promise<void>;
onBack?: () => void;
forceId?: string;
}
@@ -48,10 +55,11 @@ interface Props {
function with_defaults(id?: string): Partial<Entity> {
return {
id,
- payto_uris: [],
- default_pay_delay: { d_us: 2 * 1000 * 60 * 60 * 1000 }, // two hours
- default_wire_fee_amortization: 1,
- default_wire_transfer_delay: { d_us: 1000 * 2 * 60 * 60 * 24 * 1000 }, // two days
+ // accounts: [],
+ user_type: "business",
+ use_stefan: true,
+ default_pay_delay: { d_ms: 2 * 60 * 60 * 1000 }, // two hours
+ default_wire_transfer_delay: { d_ms: 2 * 60 * 60 * 24 * 1000 }, // two days
};
}
@@ -61,76 +69,86 @@ export function CreatePage({ onCreate, onBack, forceId }: Props): VNode {
const [isTokenDialogActive, updateIsTokenDialogActive] =
useState<boolean>(false);
- const i18n = useTranslator();
+ const { i18n } = useTranslationContext();
const errors: FormErrors<Entity> = {
id: !value.id
- ? i18n`required`
+ ? i18n.str`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,
- 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`
+ ? i18n.str`is not valid`
+ : undefined,
+ name: !value.name ? i18n.str`required` : undefined,
+
+ user_type: !value.user_type
+ ? i18n.str`required`
+ : value.user_type !== "business" && value.user_type !== "individual"
+ ? i18n.str`should be business or individual`
+ : undefined,
+ // accounts:
+ // !value.accounts || !value.accounts.length
+ // ? i18n.str`required`
+ // : undefinedIfEmpty(
+ // value.accounts.map((p) => {
+ // return !PAYTO_REGEX.test(p.payto_uri)
+ // ? i18n.str`is not valid`
+ // : undefined;
+ // }),
+ // ),
+ default_pay_delay: !value.default_pay_delay
+ ? i18n.str`required`
+ : !!value.default_wire_transfer_delay &&
+ value.default_wire_transfer_delay.d_ms !== "forever" &&
+ value.default_pay_delay.d_ms !== "forever" &&
+ value.default_pay_delay.d_ms > value.default_wire_transfer_delay.d_ms
+ ? i18n.str`pay delay can't be greater than wire transfer delay`
: undefined,
- default_pay_delay: !value.default_pay_delay ? i18n`required` : undefined,
default_wire_transfer_delay: !value.default_wire_transfer_delay
- ? i18n`required`
+ ? i18n.str`required`
: undefined,
address: undefinedIfEmpty({
address_lines:
value.address?.address_lines && value.address?.address_lines.length > 7
- ? i18n`max 7 lines`
+ ? i18n.str`max 7 lines`
: undefined,
}),
jurisdiction: undefinedIfEmpty({
address_lines:
value.address?.address_lines && value.address?.address_lines.length > 7
- ? i18n`max 7 lines`
+ ? i18n.str`max 7 lines`
: undefined,
}),
};
const hasErrors = Object.keys(errors).some(
- (k) => (errors as any)[k] !== undefined
+ (k) => (errors as Record<string, unknown>)[k] !== undefined,
);
const submit = (): Promise<void> => {
// use conversion instead of this
- const newToken = value.auth_token;
- value.auth_token = undefined;
- value.auth =
+ const newValue = structuredClone(value);
+
+ const newToken = newValue.auth_token;
+ newValue.auth_token = undefined;
+ newValue.auth =
newToken === null || newToken === undefined
? { method: "external" }
- : { method: "token", token: `secret-token:${newToken}` };
- if (!value.address) value.address = {};
- if (!value.jurisdiction) value.jurisdiction = {};
+ : { method: "token", token: createAccessToken(newToken) };
+ if (!newValue.address) newValue.address = {};
+ if (!newValue.jurisdiction) newValue.jurisdiction = {};
// remove above use conversion
// schema.validateSync(value, { abortEarly: false })
- return onCreate(value as Entity);
+ newValue.default_pay_delay = Duration.toTalerProtocolDuration(
+ newValue.default_pay_delay!,
+ ) as any;
+ newValue.default_wire_transfer_delay = Duration.toTalerProtocolDuration(
+ newValue.default_wire_transfer_delay!,
+ ) as any;
+ // delete value.default_pay_delay;
+ // delete value.default_wire_transfer_delay;
+
+ return onCreate(
+ newValue as any as TalerMerchantApi.InstanceConfigurationMessage,
+ );
};
function updateToken(token: string | null) {
@@ -167,29 +185,6 @@ export function CreatePage({ onCreate, onBack, forceId }: Props): VNode {
<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">
<div class="columns">
<div class="column" />
@@ -202,22 +197,71 @@ export function CreatePage({ onCreate, onBack, forceId }: Props): VNode {
<DefaultInstanceFormFields readonlyId={!!forceId} showId={true} />
</FormProvider>
+ <div class="level">
+ <div class="level-item has-text-centered">
+ <h1 class="title">
+ <button
+ class={
+ !isTokenSet
+ ? "button is-danger has-tooltip-bottom"
+ : !value.auth_token
+ ? "button has-tooltip-bottom"
+ : "button is-info has-tooltip-bottom"
+ }
+ data-tooltip={i18n.str`change authorization configuration`}
+ onClick={() => updateIsTokenDialogActive(true)}
+ >
+ <div class="icon is-centered">
+ <i class="mdi mdi-lock-reset" />
+ </div>
+ <span>
+ <i18n.Translate>Set access token</i18n.Translate>
+ </span>
+ </button>
+ </h1>
+ </div>
+ </div>
+ <div class="level">
+ <div class="level-item has-text-centered">
+ {!isTokenSet ? (
+ <p class="is-size-6">
+ <i18n.Translate>
+ Access token is not yet configured. This instance can't be
+ created.
+ </i18n.Translate>
+ </p>
+ ) : value.auth_token === undefined ? (
+ <p class="is-size-6">
+ <i18n.Translate>
+ No access token. Authorization must be handled externally.
+ </i18n.Translate>
+ </p>
+ ) : (
+ <p class="is-size-6">
+ <i18n.Translate>
+ Access token is set. Authorization is handled by the
+ merchant backend.
+ </i18n.Translate>
+ </p>
+ )}
+ </div>
+ </div>
<div class="buttons is-right mt-5">
{onBack && (
<button class="button" onClick={onBack}>
- <Translate>Cancel</Translate>
+ <i18n.Translate>Cancel</i18n.Translate>
</button>
)}
<AsyncButton
onClick={submit}
- disabled={!isTokenSet || hasErrors}
+ disabled={hasErrors || !isTokenSet}
data-tooltip={
hasErrors
- ? i18n`Need to complete marked fields and choose authorization method`
+ ? i18n.str`Need to complete marked fields and choose authorization method`
: "confirm operation"
}
>
- <Translate>Confirm</Translate>
+ <i18n.Translate>Confirm</i18n.Translate>
</AsyncButton>
</div>
</div>