diff options
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.tsx | 208 |
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> |