aboutsummaryrefslogtreecommitdiff
path: root/packages/merchant-backoffice-ui/src/paths/instance/templates/create/CreatePage.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'packages/merchant-backoffice-ui/src/paths/instance/templates/create/CreatePage.tsx')
-rw-r--r--packages/merchant-backoffice-ui/src/paths/instance/templates/create/CreatePage.tsx78
1 files changed, 65 insertions, 13 deletions
diff --git a/packages/merchant-backoffice-ui/src/paths/instance/templates/create/CreatePage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/templates/create/CreatePage.tsx
index 144e968c5..f6aa9a9ae 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/templates/create/CreatePage.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/instance/templates/create/CreatePage.tsx
@@ -19,6 +19,10 @@
* @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";
@@ -35,7 +39,10 @@ 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 { randomBase32Key } from "../../../../utils/crypto.js";
+import {
+ isBase32RFC3548Charset,
+ randomBase32Key,
+} from "../../../../utils/crypto.js";
import { undefinedIfEmpty } from "../../../../utils/table.js";
type Entity = MerchantBackend.Template.TemplateAddDetails;
@@ -45,17 +52,14 @@ interface Props {
onBack?: () => void;
}
-const algorithms = ["0", "1", "2"];
-const algorithmsNames = [
- "off",
- "30s 8d TOTP-SHA1 without amount",
- "30s 8d eTOTP-SHA1 with amount",
-];
+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<Partial<Entity>>({
template_contract: {
minimum_age: 0,
@@ -65,6 +69,10 @@ export function CreatePage({ onCreate, onBack }: Props): VNode {
},
});
+ const parsedPrice = !state.template_contract?.amount
+ ? undefined
+ : Amounts.parse(state.template_contract?.amount);
+
const errors: FormErrors<Entity> = {
template_id: !state.template_id ? i18n.str`should not be empty` : undefined,
template_description: !state.template_description
@@ -73,6 +81,13 @@ export function CreatePage({ onCreate, onBack }: Props): VNode {
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`
@@ -84,7 +99,16 @@ export function CreatePage({ onCreate, onBack }: Props): VNode {
: state.template_contract.pay_duration.d_us < 1000 * 1000 //less than one second
? i18n.str`to short`
: undefined,
- }),
+ } as Partial<MerchantTemplateContractDetails>),
+ 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(
@@ -144,21 +168,32 @@ export function CreatePage({ onCreate, onBack }: Props): VNode {
/>
<InputSelector<Entity>
name="pos_algorithm"
- label={i18n.str`Veritifaction algorithm`}
+ label={i18n.str`Verification algorithm`}
tooltip={i18n.str`Algorithm to use to verify transaction in offline mode`}
values={algorithms}
toStr={(v) => algorithmsNames[v]}
- convert={(v) => Number(v)}
+ fromStr={(v) => Number(v)}
/>
{state.pos_algorithm && state.pos_algorithm > 0 ? (
- <Input<Entity>
+ <InputWithAddon<Entity>
name="pos_key"
label={i18n.str`Point-of-sale key`}
- help=""
+ 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={
+ <span class="icon">
+ {showKey ? (
+ <i class="mdi mdi-eye" />
+ ) : (
+ <i class="mdi mdi-eye-off" />
+ )}
+ </span>
+ }
side={
- <span data-tooltip={i18n.str`generate random secret key`}>
+ <span style={{ display: "flex" }}>
<button
+ data-tooltip={i18n.str`generate random secret key`}
class="button is-info mr-3"
onClick={(e) => {
const pos_key = randomBase32Key();
@@ -167,6 +202,23 @@ export function CreatePage({ onCreate, onBack }: Props): VNode {
>
<i18n.Translate>random</i18n.Translate>
</button>
+ <button
+ data-tooltip={
+ showKey
+ ? i18n.str`show secret key`
+ : i18n.str`hide secret key`
+ }
+ class="button is-info mr-3"
+ onClick={(e) => {
+ setShowKey(!showKey);
+ }}
+ >
+ {showKey ? (
+ <i18n.Translate>hide</i18n.Translate>
+ ) : (
+ <i18n.Translate>show</i18n.Translate>
+ )}
+ </button>
</span>
}
/>