From 2b895bea57ae486004d8173354ccd5af52cf042e Mon Sep 17 00:00:00 2001 From: Sebastian Date: Fri, 14 Jun 2024 12:33:17 -0300 Subject: new payto form --- packages/merchant-backoffice-ui/src/Routing.tsx | 4 - .../src/components/form/InputPaytoForm.tsx | 4 +- .../src/components/form/InputToggle.tsx | 28 +++- .../src/components/menu/index.tsx | 16 +- .../src/components/modal/index.tsx | 103 ++++++++++++ .../src/components/tokenfamily/TokenFamilyForm.tsx | 2 +- .../src/hooks/tokenfamily.ts | 2 +- .../paths/instance/accounts/create/CreatePage.tsx | 183 ++++++++++++++------- .../src/paths/instance/accounts/create/index.tsx | 22 ++- .../src/paths/instance/accounts/list/index.tsx | 2 +- .../tokenfamilies/create/Create.stories.tsx | 2 +- .../instance/tokenfamilies/create/CreatePage.tsx | 2 +- .../paths/instance/tokenfamilies/create/index.tsx | 2 +- .../paths/instance/tokenfamilies/list/Table.tsx | 2 +- .../paths/instance/tokenfamilies/list/index.tsx | 2 +- .../src/paths/instance/transfers/list/ListPage.tsx | 4 +- .../merchant-backoffice-ui/src/scss/toggle.scss | 16 ++ 17 files changed, 302 insertions(+), 94 deletions(-) (limited to 'packages/merchant-backoffice-ui') diff --git a/packages/merchant-backoffice-ui/src/Routing.tsx b/packages/merchant-backoffice-ui/src/Routing.tsx index c290cd67f..b3fd44e74 100644 --- a/packages/merchant-backoffice-ui/src/Routing.tsx +++ b/packages/merchant-backoffice-ui/src/Routing.tsx @@ -94,10 +94,6 @@ export enum InstancePaths { order_new = "/order/new", order_details = "/order/:oid/details", - reserves_list = "/reserves", - reserves_details = "/reserves/:rid/details", - reserves_new = "/reserves/new", - kyc = "/kyc", transfers_list = "/transfers", diff --git a/packages/merchant-backoffice-ui/src/components/form/InputPaytoForm.tsx b/packages/merchant-backoffice-ui/src/components/form/InputPaytoForm.tsx index 9cec72c01..585894863 100644 --- a/packages/merchant-backoffice-ui/src/components/form/InputPaytoForm.tsx +++ b/packages/merchant-backoffice-ui/src/components/form/InputPaytoForm.tsx @@ -296,7 +296,7 @@ export function InputPaytoForm({ > name="target" - label={i18n.str`Account type`} + label={i18n.str`Type`} tooltip={i18n.str`Method to use for wire transfer`} values={targets} readonly={readonly} @@ -430,7 +430,9 @@ export function InputPaytoForm({ name="params.receiver-name" readonly={readonly} label={i18n.str`Owner's name`} + placeholder="John Doe" tooltip={i18n.str`Legal name of the person holding the account.`} + help={i18n.str`It should match the bank account name.`} /> )} diff --git a/packages/merchant-backoffice-ui/src/components/form/InputToggle.tsx b/packages/merchant-backoffice-ui/src/components/form/InputToggle.tsx index 8c935f33b..89a4dc48c 100644 --- a/packages/merchant-backoffice-ui/src/components/form/InputToggle.tsx +++ b/packages/merchant-backoffice-ui/src/components/form/InputToggle.tsx @@ -18,7 +18,7 @@ * * @author Sebastian Javier Marchano (sebasjm) */ -import { h, VNode } from "preact"; +import { ComponentChildren, h, VNode } from "preact"; import { InputProps, useField } from "./useField.js"; interface Props extends InputProps { @@ -26,11 +26,12 @@ interface Props extends InputProps { readonly?: boolean; expand?: boolean; threeState?: boolean; + side?: ComponentChildren; toBoolean?: (v?: any) => boolean | undefined; fromBoolean?: (s: boolean | undefined) => any; } -const defaultToBoolean = (f?: any): boolean | undefined => f || ""; +const defaultToBoolean = (f?: any): boolean | undefined => f; const defaultFromBoolean = (v: boolean | undefined): any => v as any; export function InputToggle({ @@ -41,6 +42,7 @@ export function InputToggle({ label, help, threeState, + side, expand, fromBoolean = defaultFromBoolean, toBoolean = defaultToBoolean, @@ -56,7 +58,7 @@ export function InputToggle({ return (
-
); diff --git a/packages/merchant-backoffice-ui/src/components/menu/index.tsx b/packages/merchant-backoffice-ui/src/components/menu/index.tsx index baab9584c..c13839d2d 100644 --- a/packages/merchant-backoffice-ui/src/components/menu/index.tsx +++ b/packages/merchant-backoffice-ui/src/components/menu/index.tsx @@ -29,11 +29,11 @@ function getInstanceTitle(path: string, id: string): string { case InstancePaths.settings: return `${id}: Settings`; case InstancePaths.bank_new: - return `${id}: Account`; + return `${id}: New bank account`; case InstancePaths.bank_list: - return `${id}: Account`; + return `${id}: Bank accounts`; case InstancePaths.bank_update: - return `${id}: Account`; + return `${id}: Update bank Account`; case InstancePaths.order_list: return `${id}: Orders`; case InstancePaths.order_new: @@ -44,10 +44,6 @@ function getInstanceTitle(path: string, id: string): string { return `${id}: New product`; case InstancePaths.inventory_update: return `${id}: Update product`; - case InstancePaths.reserves_new: - return `${id}: New reserve`; - case InstancePaths.reserves_list: - return `${id}: Reserves`; case InstancePaths.transfers_list: return `${id}: Transfers`; case InstancePaths.transfers_new: @@ -59,11 +55,11 @@ function getInstanceTitle(path: string, id: string): string { case InstancePaths.webhooks_update: return `${id}: Update webhook`; case InstancePaths.otp_devices_list: - return `${id}: otp devices`; + return `${id}: OTP devices`; case InstancePaths.otp_devices_new: - return `${id}: New otp devices`; + return `${id}: New OTP device`; case InstancePaths.otp_devices_update: - return `${id}: Update otp devices`; + return `${id}: Update OTP device`; case InstancePaths.templates_new: return `${id}: New template`; case InstancePaths.templates_update: diff --git a/packages/merchant-backoffice-ui/src/components/modal/index.tsx b/packages/merchant-backoffice-ui/src/components/modal/index.tsx index 43062d13e..518583a9c 100644 --- a/packages/merchant-backoffice-ui/src/components/modal/index.tsx +++ b/packages/merchant-backoffice-ui/src/components/modal/index.tsx @@ -31,6 +31,8 @@ import { AccountLetter, codecForAccountLetter, PaytoString, + PaytoUri, + stringifyPaytoUri, } from "@gnu-taler/taler-util"; interface Props { @@ -288,6 +290,107 @@ export function ImportingAccountModal({ ); } +interface CompareAccountsModalProps { + onCancel: () => void; + onConfirm: (account: PaytoString) => void; + formPayto: PaytoUri | undefined; + testPayto: PaytoUri; +} + +export function CompareAccountsModal({ + onCancel, + onConfirm, + formPayto, + testPayto, +}: CompareAccountsModalProps): VNode { + const { i18n } = useTranslationContext(); + return ( + onConfirm(stringifyPaytoUri(testPayto))} + > +

+ + Testing agaisnt the account info URL succedded but the account + information reported is different with the account details form. + +

+
+ + + + + + + + + + + + + + + {testPayto.targetType === "iban" && ( + + + + + + )} + {testPayto.targetType === "bitcoin" && ( + + + + + + )} + {testPayto.targetType === "x-taler-bank" && ( + + + + + + + + + + + + + )} + + + + + + +
+ Field + + In the form + + Reported +
+ Type + {formPayto?.targetType ?? "--"}{testPayto.targetType}
+ IBAN + {formPayto?.targetPath ?? "--"}{testPayto.targetPath}
+ Address + {formPayto?.targetPath ?? "--"}{testPayto.targetPath}
+ Host + {formPayto?.targetPath ?? "--"}{testPayto.targetPath}
+ Account id + {formPayto?.targetPath ?? "--"}{testPayto.targetPath}
+ Owner's name + {formPayto?.params["receiver-name"] ?? "--"}{testPayto.params["receiver-name"]}
+
+
+ ); +} + interface DeleteModalProps { element: { id: string; name: string }; onCancel: () => void; diff --git a/packages/merchant-backoffice-ui/src/components/tokenfamily/TokenFamilyForm.tsx b/packages/merchant-backoffice-ui/src/components/tokenfamily/TokenFamilyForm.tsx index 1492beb48..b4c49502d 100644 --- a/packages/merchant-backoffice-ui/src/components/tokenfamily/TokenFamilyForm.tsx +++ b/packages/merchant-backoffice-ui/src/components/tokenfamily/TokenFamilyForm.tsx @@ -1,6 +1,6 @@ /* This file is part of GNU Taler - (C) 2021-2023 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 diff --git a/packages/merchant-backoffice-ui/src/hooks/tokenfamily.ts b/packages/merchant-backoffice-ui/src/hooks/tokenfamily.ts index 62f364972..221babf30 100644 --- a/packages/merchant-backoffice-ui/src/hooks/tokenfamily.ts +++ b/packages/merchant-backoffice-ui/src/hooks/tokenfamily.ts @@ -1,6 +1,6 @@ /* This file is part of GNU Taler - (C) 2021-2023 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 diff --git a/packages/merchant-backoffice-ui/src/paths/instance/accounts/create/CreatePage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/accounts/create/CreatePage.tsx index 2f19d0c91..bec32bac3 100644 --- a/packages/merchant-backoffice-ui/src/paths/instance/accounts/create/CreatePage.tsx +++ b/packages/merchant-backoffice-ui/src/paths/instance/accounts/create/CreatePage.tsx @@ -21,10 +21,12 @@ import { HttpStatusCode, + PaytoUri, TalerError, TalerMerchantApi, TranslatedString, assertUnreachable, + parsePaytoUri, } from "@gnu-taler/taler-util"; import { useTranslationContext } from "@gnu-taler/web-util/browser"; import { Fragment, h, VNode } from "preact"; @@ -37,12 +39,16 @@ import { import { Input } from "../../../../components/form/Input.js"; import { InputPaytoForm } from "../../../../components/form/InputPaytoForm.js"; import { InputSelector } from "../../../../components/form/InputSelector.js"; -import { ImportingAccountModal } from "../../../../components/modal/index.js"; +import { + CompareAccountsModal, + ImportingAccountModal, +} from "../../../../components/modal/index.js"; import { undefinedIfEmpty } from "../../../../utils/table.js"; import { safeConvertURL } from "../update/UpdatePage.js"; import { testRevenueAPI } from "./index.js"; +import { InputToggle } from "../../../../components/form/InputToggle.js"; -type Entity = TalerMerchantApi.AccountAddDetails; +type Entity = TalerMerchantApi.AccountAddDetails & { verified?: boolean }; interface Props { onCreate: (d: TalerMerchantApi.AccountAddDetails) => Promise; @@ -55,8 +61,12 @@ export function CreatePage({ onCreate, onBack }: Props): VNode { const { i18n } = useTranslationContext(); const [importing, setImporting] = useState(false); + const [revenuePayto, setRevenuePayto] = useState( + undefined, + ); const [state, setState] = useState>({}); const facadeURL = safeConvertURL(state.credit_facade_url); + const [testError, setTestError] = useState( undefined, ); @@ -133,70 +143,60 @@ export function CreatePage({ onCreate, onBack }: Props): VNode { ); if (resp instanceof TalerError) { setTestError(i18n.str`The request to check the revenue API failed.`); + setState({ + ...state, + verified: undefined, + }); return; - } - if (resp.type === "fail") { + } else if (resp.type === "fail") { switch (resp.case) { case HttpStatusCode.BadRequest: { setTestError(i18n.str`Server replied with "bad request".`); + setState({ + ...state, + verified: undefined, + }); return; } case HttpStatusCode.Unauthorized: { - setTestError(i18n.str`Unauthorized, try with another credentials.`); - + setTestError(i18n.str`Unauthorized, check credentials.`); + setState({ + ...state, + verified: undefined, + }); return; } case HttpStatusCode.NotFound: { setTestError( i18n.str`The endpoint doesn't seems to be a Taler Revenue API.`, ); + setState({ + ...state, + verified: undefined, + }); return; } default: { assertUnreachable(resp); } } + } else { + const found = resp.body; + const match = state.payto_uri === found; + setState({ + ...state, + verified: match, + }); + if (!match) { + setRevenuePayto(parsePaytoUri(resp.body)); + } + setTestError(undefined); } } } return ( -
- {importing && ( - { - setImporting(false); - }} - onConfirm={(ac) => { - const u = new URL(ac.infoURL); - const user = u.username; - const pwd = u.password; - u.password = ""; - u.username = ""; - const credit_facade_url = u.href; - setState({ - payto_uri: ac.accountURI, - credit_facade_credentials: - user || pwd - ? { - type: "basic", - password: pwd, - username: user, - } - : undefined, - credit_facade_url, - }); - // if (u.username && ac.accesToken) { - // state.credit_facade_credentials = { - // type: "bearer", - // token: ac.accesToken, - // username: u.username, - // }; - // } else - setImporting(false); - }} - /> - )} +
@@ -208,11 +208,19 @@ export function CreatePage({ onCreate, onBack }: Props): VNode { > name="payto_uri" - label={i18n.str`Account`} + label={i18n.str`Account details`} /> +
+

+ + If the bank supports Taler Revenue API then you can add the + endpoint URL below to keep the revenue information in sync. + +

+
name="credit_facade_url" - label={i18n.str`Account info URL`} + label={i18n.str`Endpoint URL`} help="https://bank.demo.taler.net/accounts/_username_/taler-revenue/" expand tooltip={i18n.str`From where the merchant can download information about incoming wire transfers to this account`} @@ -242,22 +250,39 @@ export function CreatePage({ onCreate, onBack }: Props): VNode { /> ) : undefined} + + label={i18n.str`Match`} + tooltip={i18n.str`Check where the information match agaisnt the server info.`} + name="verified" + readonly + threeState + help={ + testError !== undefined + ? testError + : state.verified === undefined + ? i18n.str`Not verified` + : state.verified + ? i18n.str`Last test was ok` + : i18n.str`Last test failed` + } + side={ + + } + />
- -
-
+ {!importing ? undefined : ( + { + setImporting(false); + }} + onConfirm={(ac) => { + const u = new URL(ac.infoURL); + const user = u.username; + const pwd = u.password; + u.password = ""; + u.username = ""; + const credit_facade_url = u.href; + setState({ + payto_uri: ac.accountURI, + credit_facade_credentials: + user || pwd + ? { + type: "basic", + password: pwd, + username: user, + } + : undefined, + credit_facade_url, + }); + setImporting(false); + }} + /> + )} + {!revenuePayto ? undefined : ( + { + setRevenuePayto(undefined); + }} + onConfirm={(d) => { + setState({ + ...state, + payto_uri: d, + }); + setRevenuePayto(undefined); + }} + formPayto={ + !state.payto_uri ? undefined : parsePaytoUri(state.payto_uri) + } + testPayto={revenuePayto} + /> + )} + ); } diff --git a/packages/merchant-backoffice-ui/src/paths/instance/accounts/create/index.tsx b/packages/merchant-backoffice-ui/src/paths/instance/accounts/create/index.tsx index 9e0830e6e..2c1a13e27 100644 --- a/packages/merchant-backoffice-ui/src/paths/instance/accounts/create/index.tsx +++ b/packages/merchant-backoffice-ui/src/paths/instance/accounts/create/index.tsx @@ -33,7 +33,7 @@ import { } from "@gnu-taler/taler-util"; import { BrowserFetchHttpLib, - useTranslationContext + useTranslationContext, } from "@gnu-taler/web-util/browser"; import { Fragment, VNode, h } from "preact"; import { useState } from "preact/hooks"; @@ -86,10 +86,13 @@ export enum TestRevenueErrorType { export async function testRevenueAPI( revenueAPI: URL, creds: FacadeCredentials | undefined, -): Promise | OperationFail -| OperationFail -| OperationFail -| TalerError> { +): Promise< + | OperationOk + | OperationFail + | OperationFail + | OperationFail + | TalerError +> { const api = new TalerRevenueHttpClient( revenueAPI.href, new BrowserFetchHttpLib(), @@ -113,6 +116,12 @@ export async function testRevenueAPI( return config; } + const resp = await api.getHistory(auth); + if (resp.type === "fail") { + return resp; + } + + return opFixedSuccess(resp.body.credit_account as PaytoString); } catch (err) { if (err instanceof TalerError) { return err; @@ -122,7 +131,6 @@ export async function testRevenueAPI( // detail: err.errorDetail, // }; } + throw err } - - return opFixedSuccess(undefined); } diff --git a/packages/merchant-backoffice-ui/src/paths/instance/accounts/list/index.tsx b/packages/merchant-backoffice-ui/src/paths/instance/accounts/list/index.tsx index 1eda7382d..c0ddab475 100644 --- a/packages/merchant-backoffice-ui/src/paths/instance/accounts/list/index.tsx +++ b/packages/merchant-backoffice-ui/src/paths/instance/accounts/list/index.tsx @@ -75,7 +75,7 @@ export default function ListOtpDevices({ } { const idx = accounts.indexOf(d) if (idx === -1) return undefined; return d }} - placeholder={i18n.str`Select one account`} + placeholder={i18n.str`All accounts`} tooltip={i18n.str`filter by account address`} /> diff --git a/packages/merchant-backoffice-ui/src/scss/toggle.scss b/packages/merchant-backoffice-ui/src/scss/toggle.scss index 6c7346eb3..d3ff22997 100644 --- a/packages/merchant-backoffice-ui/src/scss/toggle.scss +++ b/packages/merchant-backoffice-ui/src/scss/toggle.scss @@ -49,10 +49,26 @@ $green: #56c080; .toggle-checkbox:checked+& { background: $green; + &:before { + left: 4px; + } + } + .toggle-checkbox:not(checked)+& { + background: $red; + &:before { left: 30px; } } + .toggle-checkbox:indeterminate+& { + background: rgba(0, 0, 0, 0.301); + + &:before { + left: 16px; + background: rgba(0, 0, 0, 0.301); + } + } + } .toggle-checkbox { -- cgit v1.2.3