gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[taler-wallet-core] branch master updated: some fixes and validations


From: gnunet
Subject: [taler-wallet-core] branch master updated: some fixes and validations
Date: Mon, 13 Mar 2023 15:29:08 +0100

This is an automated email from the git hooks/post-receive script.

sebasjm pushed a commit to branch master
in repository wallet-core.

The following commit(s) were added to refs/heads/master by this push:
     new 96d110379 some fixes and validations
96d110379 is described below

commit 96d110379e9bfbffedfeebf44c1c972b12fffff4
Author: Sebastian <sebasjm@gmail.com>
AuthorDate: Mon Mar 13 11:12:46 2023 -0300

    some fixes and validations
---
 .../src/components/form/InputSelector.tsx          | 24 +++----
 .../src/components/form/InputWithAddon.tsx         |  2 +-
 .../src/components/form/useField.tsx               |  5 +-
 .../paths/instance/orders/create/CreatePage.tsx    | 12 +++-
 .../paths/instance/templates/create/CreatePage.tsx | 78 ++++++++++++++++++----
 .../src/paths/instance/templates/qr/QrPage.tsx     | 21 +++++-
 .../paths/instance/templates/update/UpdatePage.tsx | 48 +++++++++----
 .../merchant-backoffice-ui/src/utils/amount.ts     |  6 +-
 .../merchant-backoffice-ui/src/utils/crypto.ts     |  8 +++
 9 files changed, 154 insertions(+), 50 deletions(-)

diff --git 
a/packages/merchant-backoffice-ui/src/components/form/InputSelector.tsx 
b/packages/merchant-backoffice-ui/src/components/form/InputSelector.tsx
index 021977dfe..495c93897 100644
--- a/packages/merchant-backoffice-ui/src/components/form/InputSelector.tsx
+++ b/packages/merchant-backoffice-ui/src/components/form/InputSelector.tsx
@@ -24,8 +24,7 @@ import { InputProps, useField } from "./useField.js";
 interface Props<T> extends InputProps<T> {
   readonly?: boolean;
   expand?: boolean;
-  values: string[];
-  convert?: (v: string) => any;
+  values: any[];
   toStr?: (v?: any) => string;
   fromStr?: (s: string) => any;
 }
@@ -42,11 +41,11 @@ export function InputSelector<T>({
   label,
   help,
   values,
-  convert,
+  fromStr = defaultFromString,
   toStr = defaultToString,
 }: Props<keyof T>): VNode {
   const { error, value, onChange } = useField<T>(name);
-
+  console.log(error);
   return (
     <div class="field is-horizontal">
       <div class="field-label is-normal">
@@ -68,18 +67,17 @@ export function InputSelector<T>({
               disabled={readonly}
               readonly={readonly}
               onChange={(e) => {
-                const v = convert
-                  ? convert(e.currentTarget.value)
-                  : e.currentTarget.value;
-                onChange(v);
+                onChange(fromStr(e.currentTarget.value));
               }}
             >
               {placeholder && <option>{placeholder}</option>}
-              {values.map((v, i) => (
-                <option key={i} value={v} selected={value === v}>
-                  {toStr(v)}
-                </option>
-              ))}
+              {values.map((v, i) => {
+                return (
+                  <option key={i} value={v} selected={value === v}>
+                    {toStr(v)}
+                  </option>
+                );
+              })}
             </select>
             {help}
           </p>
diff --git 
a/packages/merchant-backoffice-ui/src/components/form/InputWithAddon.tsx 
b/packages/merchant-backoffice-ui/src/components/form/InputWithAddon.tsx
index dbf4e2409..34feec202 100644
--- a/packages/merchant-backoffice-ui/src/components/form/InputWithAddon.tsx
+++ b/packages/merchant-backoffice-ui/src/components/form/InputWithAddon.tsx
@@ -96,7 +96,6 @@ export function InputWithAddon<T>({
                   <i class="mdi mdi-alert" />
                 </span>
               )}
-              {help}
               {children}
             </p>
             {addonAfter && (
@@ -106,6 +105,7 @@ export function InputWithAddon<T>({
             )}
           </div>
           {error && <p class="help is-danger">{error}</p>}
+          <span class="has-text-grey">{help}</span>
         </div>
         {side}
       </div>
diff --git a/packages/merchant-backoffice-ui/src/components/form/useField.tsx 
b/packages/merchant-backoffice-ui/src/components/form/useField.tsx
index dffb0cc66..c7559faae 100644
--- a/packages/merchant-backoffice-ui/src/components/form/useField.tsx
+++ b/packages/merchant-backoffice-ui/src/components/form/useField.tsx
@@ -20,6 +20,7 @@
  */
 
 import { ComponentChildren, VNode } from "preact";
+import { useState } from "preact/hooks";
 import { useFormContext } from "./FormProvider.js";
 
 interface Use<V> {
@@ -37,10 +38,11 @@ export function useField<T>(name: keyof T): Use<T[typeof 
name]> {
     useFormContext<T>();
   type P = typeof name;
   type V = T[P];
-
+  const [isDirty, setDirty] = useState(false);
   const updateField =
     (field: P) =>
     (value: V): void => {
+      setDirty(true);
       return valueHandler((prev) => {
         return setValueDeeper(prev, String(field).split("."), value);
       });
@@ -50,7 +52,6 @@ export function useField<T>(name: keyof T): Use<T[typeof 
name]> {
   const defaultFromString = (v: string): V => v as any;
   const value = readField(object, String(name));
   const initial = readField(initialObject, String(name));
-  const isDirty = value !== initial;
   const hasError = readField(errors, String(name));
   return {
     error: isDirty ? hasError : undefined,
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"
diff --git a/packages/merchant-backoffice-ui/src/utils/amount.ts 
b/packages/merchant-backoffice-ui/src/utils/amount.ts
index 93d6a3a4a..475489d3e 100644
--- a/packages/merchant-backoffice-ui/src/utils/amount.ts
+++ b/packages/merchant-backoffice-ui/src/utils/amount.ts
@@ -59,14 +59,12 @@ export function mergeRefunds(
   return prev;
 }
 
-export const rate = (one: string, two: string): number => {
-  const a = Amounts.parseOrThrow(one);
-  const b = Amounts.parseOrThrow(two);
+export function rate(a: AmountJson, b: AmountJson): number {
   const af = toFloat(a);
   const bf = toFloat(b);
   if (bf === 0) return 0;
   return af / bf;
-};
+}
 
 function toFloat(amount: AmountJson): number {
   return amount.value + amount.fraction / amountFractionalBase;
diff --git a/packages/merchant-backoffice-ui/src/utils/crypto.ts 
b/packages/merchant-backoffice-ui/src/utils/crypto.ts
index 7bab8abf1..27e6ade02 100644
--- a/packages/merchant-backoffice-ui/src/utils/crypto.ts
+++ b/packages/merchant-backoffice-ui/src/utils/crypto.ts
@@ -46,6 +46,14 @@ function encodeBase32(data: ArrayBuffer) {
   return sb;
 }
 
+export function isBase32RFC3548Charset(s: string): boolean {
+  for (let idx = 0; idx < s.length; idx++) {
+    const c = s.charAt(idx);
+    if (encTable.indexOf(c) === -1) return false;
+  }
+  return true;
+}
+
 export function randomBase32Key(): string {
   var buf = new Uint8Array(20);
   window.crypto.getRandomValues(buf);

-- 
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]