aboutsummaryrefslogtreecommitdiff
path: root/packages/merchant-backoffice-ui/src/paths
diff options
context:
space:
mode:
authorSebastian <sebasjm@gmail.com>2023-03-13 11:12:46 -0300
committerSebastian <sebasjm@gmail.com>2023-03-13 11:27:52 -0300
commit96d110379e9bfbffedfeebf44c1c972b12fffff4 (patch)
treee7065608cd1561d000f9d2eef79a4ff3e4611e16 /packages/merchant-backoffice-ui/src/paths
parent5f681813cf1bb7bb5c0baa41f29011d0029d003d (diff)
downloadwallet-core-96d110379e9bfbffedfeebf44c1c972b12fffff4.tar.xz
some fixes and validations
Diffstat (limited to 'packages/merchant-backoffice-ui/src/paths')
-rw-r--r--packages/merchant-backoffice-ui/src/paths/instance/orders/create/CreatePage.tsx12
-rw-r--r--packages/merchant-backoffice-ui/src/paths/instance/templates/create/CreatePage.tsx78
-rw-r--r--packages/merchant-backoffice-ui/src/paths/instance/templates/qr/QrPage.tsx21
-rw-r--r--packages/merchant-backoffice-ui/src/paths/instance/templates/update/UpdatePage.tsx48
4 files changed, 129 insertions, 30 deletions
diff --git a/packages/merchant-backoffice-ui/src/paths/instance/orders/create/CreatePage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/orders/create/CreatePage.tsx
index f4a82f377..d5c888f1c 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/orders/create/CreatePage.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/instance/orders/create/CreatePage.tsx
@@ -144,12 +144,18 @@ export function CreatePage({
const { i18n } = useTranslationContext();
+ const parsedPrice = !value.pricing?.order_price
+ ? undefined
+ : Amounts.parse(value.pricing.order_price);
+
const errors: FormErrors<Entity> = {
pricing: undefinedIfEmpty({
summary: !value.pricing?.summary ? i18n.str`required` : undefined,
order_price: !value.pricing?.order_price
? i18n.str`required`
- : Amounts.isZero(value.pricing.order_price)
+ : !parsedPrice
+ ? i18n.str`not valid`
+ : Amounts.isZero(parsedPrice)
? i18n.str`must be greater than 0`
: undefined,
}),
@@ -333,8 +339,8 @@ export function CreatePage({
}, [hasProducts, totalAsString]);
const discountOrRise = rate(
- value.pricing?.order_price || `${config.currency}:0`,
- totalAsString,
+ parsedPrice ?? Amounts.zeroOfCurrency(config.currency),
+ totalPrice.amount,
);
const minAgeByProducts = allProducts.reduce(
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>
}
/>
diff --git a/packages/merchant-backoffice-ui/src/paths/instance/templates/qr/QrPage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/templates/qr/QrPage.tsx
index a6b616907..64e9a86fe 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/templates/qr/QrPage.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/instance/templates/qr/QrPage.tsx
@@ -127,6 +127,15 @@ export function QrPage({ template, id: templateId, onBack }: Props): VNode {
<div class="columns">
<div class="column" />
<div class="column is-four-fifths">
+ <p class="is-size-5 mt-5 mb-5">
+ <i18n.Translate>
+ Here you can specify a default value for fields that are not
+ fixed. Default values can be edited by the customer before the
+ payment.
+ </i18n.Translate>
+ </p>
+
+ <p></p>
<FormProvider
object={state}
valueHandler={setState}
@@ -134,7 +143,11 @@ export function QrPage({ template, id: templateId, onBack }: Props): VNode {
>
<InputCurrency<Entity>
name="amount"
- label={i18n.str`Amount`}
+ label={
+ fixedAmount
+ ? i18n.str`Fixed amount`
+ : i18n.str`Default amount`
+ }
readonly={fixedAmount}
tooltip={i18n.str`Amount of the order`}
/>
@@ -142,7 +155,11 @@ export function QrPage({ template, id: templateId, onBack }: Props): VNode {
name="summary"
inputType="multiline"
readonly={fixedSummary}
- label={i18n.str`Order summary`}
+ label={
+ fixedSummary
+ ? i18n.str`Fixed summary`
+ : i18n.str`Default summary`
+ }
tooltip={i18n.str`Title of the order to be shown to the customer`}
/>
</FormProvider>
diff --git a/packages/merchant-backoffice-ui/src/paths/instance/templates/update/UpdatePage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/templates/update/UpdatePage.tsx
index 9fcfcc4bf..d12d1d2d3 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/templates/update/UpdatePage.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/instance/templates/update/UpdatePage.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, WithId } 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.TemplatePatchDetails & WithId;
@@ -46,12 +53,8 @@ interface Props {
template: Entity;
}
-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 UpdatePage({ template, onUpdate, onBack }: Props): VNode {
const { i18n } = useTranslationContext();
@@ -60,6 +63,10 @@ export function UpdatePage({ template, onUpdate, onBack }: Props): VNode {
const [showKey, setShowKey] = useState(false);
const [state, setState] = useState<Partial<Entity>>(template);
+ const parsedPrice = !state.template_contract?.amount
+ ? undefined
+ : Amounts.parse(state.template_contract?.amount);
+
const errors: FormErrors<Entity> = {
template_description: !state.template_description
? i18n.str`should not be empty`
@@ -67,6 +74,13 @@ export function UpdatePage({ template, onUpdate, 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`
@@ -78,7 +92,16 @@ export function UpdatePage({ template, onUpdate, 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(
@@ -155,20 +178,21 @@ export function UpdatePage({ template, onUpdate, 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 ? (
<InputWithAddon<Entity>
name="pos_key"
label={i18n.str`Point-of-sale key`}
inputType={showKey ? "text" : "password"}
- help=""
+ help="Be sure to be very hard to guess or use the random generator"
expand
tooltip={i18n.str`Useful to validate the purchase`}
+ fromStr={(v) => v.toUpperCase()}
addonAfter={
<span class="icon">
{showKey ? (
@@ -179,7 +203,7 @@ export function UpdatePage({ template, onUpdate, onBack }: Props): VNode {
</span>
}
side={
- <span>
+ <span style={{ display: "flex" }}>
<button
data-tooltip={i18n.str`generate random secret key`}
class="button is-info mr-3"