/* 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, MerchantTemplateContractDetails, } 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 { Input } from "../../../../components/form/Input.js"; import { InputCurrency } from "../../../../components/form/InputCurrency.js"; import { InputDuration } from "../../../../components/form/InputDuration.js"; import { InputNumber } from "../../../../components/form/InputNumber.js"; import { InputSelector } from "../../../../components/form/InputSelector.js"; import { InputWithAddon } from "../../../../components/form/InputWithAddon.js"; import { useBackendContext } from "../../../../context/backend.js"; import { MerchantBackend } from "../../../../declaration.js"; import { isBase32RFC3548Charset, randomBase32Key, } from "../../../../utils/crypto.js"; import { undefinedIfEmpty } from "../../../../utils/table.js"; type Entity = MerchantBackend.Template.TemplateAddDetails; interface Props { onCreate: (d: Entity) => Promise; onBack?: () => void; } const algorithms = [0, 1, 2]; const algorithmsNames = ["off", "30s 8d TOTP-SHA1", "30s 8d eTOTP-SHA1"]; export function CreatePage({ onCreate, onBack }: Props): VNode { const { i18n } = useTranslationContext(); const backend = useBackendContext(); const [showKey, setShowKey] = useState(false); const [state, setState] = useState>({ template_contract: { minimum_age: 0, pay_duration: { d_us: 1000 * 1000 * 60 * 30, //30 min }, }, }); const parsedPrice = !state.template_contract?.amount ? undefined : Amounts.parse(state.template_contract?.amount); const errors: FormErrors = { template_id: !state.template_id ? i18n.str`should not be empty` : undefined, template_description: !state.template_description ? i18n.str`should not be empty` : undefined, template_contract: !state.template_contract ? undefined : undefinedIfEmpty({ amount: !state.template_contract?.amount ? undefined : !parsedPrice ? i18n.str`not valid` : Amounts.isZero(parsedPrice) ? i18n.str`must be greater than 0` : undefined, minimum_age: state.template_contract.minimum_age < 0 ? i18n.str`should be greater that 0` : undefined, pay_duration: !state.template_contract.pay_duration ? i18n.str`can't be empty` : state.template_contract.pay_duration.d_us === "forever" ? undefined : state.template_contract.pay_duration.d_us < 1000 * 1000 //less than one second ? i18n.str`to short` : undefined, } as Partial), pos_key: !state.pos_key ? !state.pos_algorithm ? undefined : i18n.str`required` : !isBase32RFC3548Charset(state.pos_key) ? i18n.str`just letters and numbers from 2 to 7` : state.pos_key.length !== 32 ? i18n.str`size of the key should be 32` : undefined, }; const hasErrors = Object.keys(errors).some( (k) => (errors as any)[k] !== undefined, ); const submitForm = () => { if (hasErrors) return Promise.reject(); return onCreate(state as any); }; return (
name="template_id" addonBefore={`${backend.url}/instances/templates/`} label={i18n.str`Identifier`} tooltip={i18n.str`Name of the template in URLs.`} /> name="template_description" label={i18n.str`Description`} help="" tooltip={i18n.str`Describe what this template stands for`} /> name="pos_algorithm" label={i18n.str`Verification algorithm`} tooltip={i18n.str`Algorithm to use to verify transaction in offline mode`} values={algorithms} toStr={(v) => algorithmsNames[v]} fromStr={(v) => Number(v)} /> {state.pos_algorithm && state.pos_algorithm > 0 ? ( name="pos_key" label={i18n.str`Point-of-sale key`} inputType={showKey ? "text" : "password"} help="Be sure to be very hard to guess or use the random generator" tooltip={i18n.str`Useful to validate the purchase`} fromStr={(v) => v.toUpperCase()} addonAfter={ {showKey ? ( ) : ( )} } side={ } /> ) : undefined}
{onBack && ( )} Confirm
); }