/* This file is part of GNU Taler (C) 2021-2023 Taler Systems S.A. GNU Taler is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version. GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GNU Taler; see the file COPYING. If not, see */ /** * * @author Sebastian Javier Marchano (sebasjm) */ import { Amounts } from "@gnu-taler/taler-util"; import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser"; import { h, VNode } from "preact"; import { useState } from "preact/hooks"; import { AsyncButton } from "../../../components/exception/AsyncButton.js"; import { FormErrors, FormProvider, } from "../../../components/form/FormProvider.js"; import { DefaultInstanceFormFields } from "../../../components/instance/DefaultInstanceFormFields.js"; import { UpdateTokenModal } from "../../../components/modal/index.js"; import { useInstanceContext } from "../../../context/instance.js"; import { MerchantBackend } from "../../../declaration.js"; import { PAYTO_REGEX } from "../../../utils/constants.js"; import { undefinedIfEmpty } from "../../../utils/table.js"; type Entity = MerchantBackend.Instances.InstanceReconfigurationMessage & { auth_token?: string; }; //MerchantBackend.Instances.InstanceAuthConfigurationMessage interface Props { onUpdate: (d: Entity) => void; onChangeAuth: ( d: MerchantBackend.Instances.InstanceAuthConfigurationMessage, ) => Promise; selected: MerchantBackend.Instances.QueryInstancesResponse; isLoading: boolean; onBack: () => void; } function convert( from: MerchantBackend.Instances.QueryInstancesResponse, ): Entity { const { accounts: qAccounts, ...rest } = from; const accounts = qAccounts .filter((a) => a.active) .map( (a) => ({ payto_uri: a.payto_uri, credit_facade_url: a.credit_facade_url, credit_facade_credentials: a.credit_facade_credentials, } as MerchantBackend.Instances.MerchantBankAccount), ); const defaults = { default_wire_fee_amortization: 1, default_pay_delay: { d_us: 2 * 1000 * 1000 * 60 * 60 }, //two hours default_wire_transfer_delay: { d_us: 2 * 1000 * 1000 * 60 * 60 * 2 }, //two hours }; return { ...defaults, ...rest, accounts }; } function getTokenValuePart(t?: string): string | undefined { if (!t) return t; const match = /secret-token:(.*)/.exec(t); if (!match || !match[1]) return undefined; return match[1]; } 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; if (!token) { onChangeAuth({ method: "external" }); } else { onChangeAuth({ method: "token", token: `secret-token:${value}` }); } } const [value, valueHandler] = useState>(convert(selected)); const { i18n } = useTranslationContext(); const errors: FormErrors = { 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_max_deposit_fee: !value.default_max_deposit_fee ? i18n.str`required` : !Amounts.parse(value.default_max_deposit_fee) ? i18n.str`invalid format` : undefined, default_max_wire_fee: !value.default_max_wire_fee ? i18n.str`required` : !Amounts.parse(value.default_max_wire_fee) ? i18n.str`invalid format` : undefined, default_wire_fee_amortization: value.default_wire_fee_amortization === undefined ? i18n.str`required` : isNaN(value.default_wire_fee_amortization) ? i18n.str`is not a number` : value.default_wire_fee_amortization < 1 ? i18n.str`must be 1 or greater` : undefined, default_pay_delay: !value.default_pay_delay ? i18n.str`required` : undefined, default_wire_transfer_delay: !value.default_wire_transfer_delay ? i18n.str`required` : undefined, address: undefinedIfEmpty({ address_lines: value.address?.address_lines && value.address?.address_lines.length > 7 ? i18n.str`max 7 lines` : undefined, }), jurisdiction: undefinedIfEmpty({ address_lines: value.address?.address_lines && value.address?.address_lines.length > 7 ? i18n.str`max 7 lines` : undefined, }), }; const hasErrors = Object.keys(errors).some( (k) => (errors as any)[k] !== undefined, ); const submit = async (): Promise => { await onUpdate(value as Entity); }; const [active, setActive] = useState(false); return ( Instance id: {id} { setActive(!active); }} > Manage access token {active && ( { setActive(false); }} onClear={() => { updateToken(null); setActive(false); }} onConfirm={(newToken) => { updateToken(newToken); setActive(false); }} /> )} errors={errors} object={value} valueHandler={valueHandler} > Cancel Confirm ); }