gnunet-svn
[Top][All Lists]
Advanced

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

[taler-wallet-core] branch master updated: better template UI


From: gnunet
Subject: [taler-wallet-core] branch master updated: better template UI
Date: Tue, 24 Oct 2023 14:42:00 +0200

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 21fda5751 better template UI
21fda5751 is described below

commit 21fda5751d61594ee94d995232f0ce66647533b2
Author: Sebastian <sebasjm@gmail.com>
AuthorDate: Tue Oct 24 09:41:53 2023 -0300

    better template UI
---
 packages/demobank-ui/src/hooks/backend.ts          |   2 +-
 packages/demobank-ui/src/hooks/settings.ts         |   2 +-
 .../src/components/form/InputTab.tsx               |  90 +++++++++++++++++++
 packages/merchant-backoffice-ui/src/hooks/index.ts |   4 +-
 .../paths/instance/templates/create/CreatePage.tsx |  92 +++++++++++++++----
 .../paths/instance/templates/update/UpdatePage.tsx | 100 +++++++++++++++++----
 6 files changed, 252 insertions(+), 38 deletions(-)

diff --git a/packages/demobank-ui/src/hooks/backend.ts 
b/packages/demobank-ui/src/hooks/backend.ts
index 589d7fab0..93d647e73 100644
--- a/packages/demobank-ui/src/hooks/backend.ts
+++ b/packages/demobank-ui/src/hooks/backend.ts
@@ -92,7 +92,7 @@ export interface BackendStateHandler {
 }
 
 const BACKEND_STATE_KEY = buildStorageKey(
-  "backend-state",
+  "bank-state",
   codecForBackendState(),
 );
 
diff --git a/packages/demobank-ui/src/hooks/settings.ts 
b/packages/demobank-ui/src/hooks/settings.ts
index ca2d131f2..1e656b3ba 100644
--- a/packages/demobank-ui/src/hooks/settings.ts
+++ b/packages/demobank-ui/src/hooks/settings.ts
@@ -74,7 +74,7 @@ const defaultSettings: Settings = {
 };
 
 const DEMOBANK_SETTINGS_KEY = buildStorageKey(
-  "demobank-settings",
+  "bank-settings",
   codecForSettings(),
 );
 
diff --git a/packages/merchant-backoffice-ui/src/components/form/InputTab.tsx 
b/packages/merchant-backoffice-ui/src/components/form/InputTab.tsx
new file mode 100644
index 000000000..2701768aa
--- /dev/null
+++ b/packages/merchant-backoffice-ui/src/components/form/InputTab.tsx
@@ -0,0 +1,90 @@
+/*
+ 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 <http://www.gnu.org/licenses/>
+ */
+
+/**
+ *
+ * @author Sebastian Javier Marchano (sebasjm)
+ */
+import { h, VNode } from "preact";
+import { InputProps, useField } from "./useField.js";
+
+interface Props<T> extends InputProps<T> {
+  readonly?: boolean;
+  expand?: boolean;
+  values: any[];
+  toStr?: (v?: any) => string;
+  fromStr?: (s: string) => any;
+}
+
+const defaultToString = (f?: any): string => f || "";
+const defaultFromString = (v: string): any => v as any;
+
+export function InputTab<T>({
+  name,
+  readonly,
+  expand,
+  placeholder,
+  tooltip,
+  label,
+  help,
+  values,
+  fromStr = defaultFromString,
+  toStr = defaultToString,
+}: Props<keyof T>): VNode {
+  const { error, value, onChange, required } = useField<T>(name);
+  return (
+    <div class="field is-horizontal">
+      <div class="field-label is-normal">
+        <label class="label">
+          {label}
+          {tooltip && (
+            <span class="icon has-tooltip-right" data-tooltip={tooltip}>
+              <i class="mdi mdi-information" />
+            </span>
+          )}
+        </label>
+      </div>
+      <div class="field-body is-flex-grow-3">
+        <div class="field has-icons-right">
+          <p class={expand ? "control is-expanded " : "control  "}>
+            <div class="tabs is-toggle is-fullwidth is-small">
+              <ul>
+                {values.map((v, i) => {
+                  return (
+                    <li key={i} class={value === v ? "is-active" : ""}
+                      onClick={(e) => { onChange(v) }}
+                    >
+                      <a style={{ cursor: "initial" }}>
+                        <span>{toStr(v)}</span>
+                      </a>
+                    </li>
+                  );
+                })}
+              </ul>
+            </div>
+            {help}
+          </p>
+          {required && (
+            <span class="icon has-text-danger is-right" style={{ height: 
"2.5em" }}>
+              <i class="mdi mdi-alert" />
+            </span>
+          )}
+          {error && <p class="help is-danger">{error}</p>}
+        </div>
+      </div>
+    </div>
+  );
+}
diff --git a/packages/merchant-backoffice-ui/src/hooks/index.ts 
b/packages/merchant-backoffice-ui/src/hooks/index.ts
index 3c8ef15ed..f0cd1bfb9 100644
--- a/packages/merchant-backoffice-ui/src/hooks/index.ts
+++ b/packages/merchant-backoffice-ui/src/hooks/index.ts
@@ -45,14 +45,14 @@ const loginTokenCodec = buildCodecForObject<LoginToken>()
   .property("token", codecForString())
   .property("expiration", codecForTimestamp)
   .build("loginToken")
-const TOKENS_KEY = buildStorageKey("backend-token", 
codecForMap(loginTokenCodec));
+const TOKENS_KEY = buildStorageKey("merchant-token", 
codecForMap(loginTokenCodec));
 
 
 export function useBackendURL(
   url?: string,
 ): [string, StateUpdater<string>] {
   const [value, setter] = useSimpleLocalStorage(
-    "backend-url",
+    "merchant-base-url",
     url || calculateRootPath(),
   );
 
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 78ea07477..947f3572c 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
@@ -41,8 +41,16 @@ import { useBackendContext } from 
"../../../../context/backend.js";
 import { MerchantBackend } from "../../../../declaration.js";
 import { useInstanceOtpDevices } from "../../../../hooks/otp.js";
 import { undefinedIfEmpty } from "../../../../utils/table.js";
+import { InputTab } from "../../../../components/form/InputTab.js";
 
-type Entity = MerchantBackend.Template.TemplateAddDetails;
+enum Steps {
+  BOTH_FIXED,
+  FIXED_PRICE,
+  FIXED_SUMMARY,
+  NON_FIXED,
+}
+
+type Entity = MerchantBackend.Template.TemplateAddDetails & { type: Steps };
 
 interface Props {
   onCreate: (d: Entity) => Promise<void>;
@@ -61,6 +69,7 @@ export function CreatePage({ onCreate, onBack }: Props): 
VNode {
         d_us: 1000 * 1000 * 60 * 30, //30 min
       },
     },
+    type: Steps.NON_FIXED,
   });
 
   const parsedPrice = !state.template_contract?.amount
@@ -79,13 +88,20 @@ export function CreatePage({ onCreate, onBack }: Props): 
VNode {
     template_contract: !state.template_contract
       ? undefined
       : undefinedIfEmpty({
-        amount: !state.template_contract?.amount
+        amount: !(state.type === Steps.FIXED_PRICE || state.type === 
Steps.BOTH_FIXED)
           ? undefined
-          : !parsedPrice
-            ? i18n.str`not valid`
-            : Amounts.isZero(parsedPrice)
-              ? i18n.str`must be greater than 0`
-              : undefined,
+          : !state.template_contract?.amount
+            ? i18n.str`required`
+            : !parsedPrice
+              ? i18n.str`not valid`
+              : Amounts.isZero(parsedPrice)
+                ? i18n.str`must be greater than 0`
+                : undefined,
+        summary: !(state.type === Steps.FIXED_SUMMARY || state.type === 
Steps.BOTH_FIXED)
+          ? undefined
+          : !state.template_contract?.summary
+            ? i18n.str`required`
+            : undefined,
         minimum_age:
           state.template_contract.minimum_age < 0
             ? i18n.str`should be greater that 0`
@@ -106,6 +122,17 @@ export function CreatePage({ onCreate, onBack }: Props): 
VNode {
 
   const submitForm = () => {
     if (hasErrors) return Promise.reject();
+    if (state.template_contract) {
+      if (state.type === Steps.NON_FIXED) {
+        delete state.template_contract.amount;
+        delete state.template_contract.summary;
+      } else if (state.type === Steps.FIXED_SUMMARY) {
+        delete state.template_contract.amount;
+      } else if (state.type === Steps.FIXED_PRICE) {
+        delete state.template_contract.summary;
+      }
+    }
+    delete state.type
     return onCreate(state as any);
   };
 
@@ -134,17 +161,48 @@ export function CreatePage({ onCreate, onBack }: Props): 
VNode {
                 help=""
                 tooltip={i18n.str`Describe what this template stands for`}
               />
-              <Input
-                name="template_contract.summary"
-                inputType="multiline"
-                label={i18n.str`Fixed summary`}
-                tooltip={i18n.str`If specified, this template will create 
order with the same summary`}
-              />
-              <InputCurrency
-                name="template_contract.amount"
-                label={i18n.str`Fixed price`}
-                tooltip={i18n.str`If specified, this template will create 
order with the same price`}
+              <InputTab
+                name="type"
+                label={i18n.str`Type`}
+                help={(() => {
+                  switch (state.type) {
+                    case Steps.NON_FIXED: return i18n.str`User will be able to 
input price and summary before payment.`
+                    case Steps.FIXED_PRICE: return i18n.str`User will be able 
to add a summary before payment.`
+                    case Steps.FIXED_SUMMARY: return i18n.str`User will be 
able to set the price before payment.`
+                    case Steps.BOTH_FIXED: return i18n.str`User will not be 
able to change the price or the summary.`
+                  }
+                })()}
+                tooltip={i18n.str`Define what the user be allowed to modify`}
+                values={[
+                  Steps.NON_FIXED,
+                  Steps.FIXED_PRICE,
+                  Steps.FIXED_SUMMARY,
+                  Steps.BOTH_FIXED,
+                ]}
+                toStr={(v: Steps): string => {
+                  switch (v) {
+                    case Steps.NON_FIXED: return i18n.str`Simple`
+                    case Steps.FIXED_PRICE: return i18n.str`With price`
+                    case Steps.FIXED_SUMMARY: return i18n.str`With summary`
+                    case Steps.BOTH_FIXED: return i18n.str`With price and 
summary`
+                  }
+                }}
               />
+              {state.type === Steps.BOTH_FIXED || state.type === 
Steps.FIXED_SUMMARY ?
+                <Input
+                  name="template_contract.summary"
+                  inputType="multiline"
+                  label={i18n.str`Fixed summary`}
+                  tooltip={i18n.str`If specified, this template will create 
order with the same summary`}
+                />
+                : undefined}
+              {state.type === Steps.BOTH_FIXED || state.type === 
Steps.FIXED_PRICE ?
+                <InputCurrency
+                  name="template_contract.amount"
+                  label={i18n.str`Fixed price`}
+                  tooltip={i18n.str`If specified, this template will create 
order with the same price`}
+                />
+                : undefined}
               <InputNumber
                 name="template_contract.minimum_age"
                 label={i18n.str`Minimum age`}
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 82b74e1fa..b578d4664 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
@@ -39,6 +39,14 @@ import { InputWithAddon } from 
"../../../../components/form/InputWithAddon.js";
 import { useBackendContext } from "../../../../context/backend.js";
 import { MerchantBackend, WithId } from "../../../../declaration.js";
 import { undefinedIfEmpty } from "../../../../utils/table.js";
+import { InputTab } from "../../../../components/form/InputTab.js";
+
+enum Steps {
+  BOTH_FIXED,
+  FIXED_PRICE,
+  FIXED_SUMMARY,
+  NON_FIXED,
+}
 
 type Entity = MerchantBackend.Template.TemplatePatchDetails & WithId;
 
@@ -52,7 +60,16 @@ export function UpdatePage({ template, onUpdate, onBack }: 
Props): VNode {
   const { i18n } = useTranslationContext();
   const { url: backendURL } = useBackendContext()
 
-  const [state, setState] = useState<Partial<Entity>>(template);
+  const intialStep =
+    template.template_contract?.amount === undefined && 
template.template_contract?.summary === undefined
+      ? Steps.NON_FIXED
+      : template.template_contract?.summary === undefined
+        ? Steps.FIXED_PRICE
+        : template.template_contract?.amount === undefined
+          ? Steps.FIXED_SUMMARY
+          : Steps.BOTH_FIXED;
+
+  const [state, setState] = useState<Partial<Entity & { type: Steps }>>({ 
...template, type: intialStep });
 
   const parsedPrice = !state.template_contract?.amount
     ? undefined
@@ -65,13 +82,20 @@ export function UpdatePage({ template, onUpdate, onBack }: 
Props): VNode {
     template_contract: !state.template_contract
       ? undefined
       : undefinedIfEmpty({
-        amount: !state.template_contract?.amount
+        amount: !(state.type === Steps.FIXED_PRICE || state.type === 
Steps.BOTH_FIXED)
           ? undefined
-          : !parsedPrice
-            ? i18n.str`not valid`
-            : Amounts.isZero(parsedPrice)
-              ? i18n.str`must be greater than 0`
-              : undefined,
+          : !state.template_contract?.amount
+            ? i18n.str`required`
+            : !parsedPrice
+              ? i18n.str`not valid`
+              : Amounts.isZero(parsedPrice)
+                ? i18n.str`must be greater than 0`
+                : undefined,
+        summary: !(state.type === Steps.FIXED_SUMMARY || state.type === 
Steps.BOTH_FIXED)
+          ? undefined
+          : !state.template_contract?.summary
+            ? i18n.str`required`
+            : undefined,
         minimum_age:
           state.template_contract.minimum_age < 0
             ? i18n.str`should be greater that 0`
@@ -92,6 +116,17 @@ export function UpdatePage({ template, onUpdate, onBack }: 
Props): VNode {
 
   const submitForm = () => {
     if (hasErrors) return Promise.reject();
+    if (state.template_contract) {
+      if (state.type === Steps.NON_FIXED) {
+        delete state.template_contract.amount;
+        delete state.template_contract.summary;
+      } else if (state.type === Steps.FIXED_SUMMARY) {
+        delete state.template_contract.amount;
+      } else if (state.type === Steps.FIXED_PRICE) {
+        delete state.template_contract.summary;
+      }
+    }
+    delete state.type
     return onUpdate(state as any);
   };
 
@@ -136,17 +171,48 @@ export function UpdatePage({ template, onUpdate, onBack 
}: Props): VNode {
                   help=""
                   tooltip={i18n.str`Describe what this template stands for`}
                 />
-                <Input
-                  name="template_contract.summary"
-                  inputType="multiline"
-                  label={i18n.str`Fixed summary`}
-                  tooltip={i18n.str`If specified, this template will create 
order with the same summary`}
-                />
-                <InputCurrency
-                  name="template_contract.amount"
-                  label={i18n.str`Fixed price`}
-                  tooltip={i18n.str`If specified, this template will create 
order with the same price`}
+                <InputTab
+                  name="type"
+                  label={i18n.str`Type`}
+                  help={(() => {
+                    switch (state.type) {
+                      case Steps.NON_FIXED: return i18n.str`User will be able 
to input price and summary before payment.`
+                      case Steps.FIXED_PRICE: return i18n.str`User will be 
able to add a summary before payment.`
+                      case Steps.FIXED_SUMMARY: return i18n.str`User will be 
able to set the price before payment.`
+                      case Steps.BOTH_FIXED: return i18n.str`User will not be 
able to change the price or the summary.`
+                    }
+                  })()}
+                  tooltip={i18n.str`Define what the user be allowed to modify`}
+                  values={[
+                    Steps.NON_FIXED,
+                    Steps.FIXED_PRICE,
+                    Steps.FIXED_SUMMARY,
+                    Steps.BOTH_FIXED,
+                  ]}
+                  toStr={(v: Steps): string => {
+                    switch (v) {
+                      case Steps.NON_FIXED: return i18n.str`Simple`
+                      case Steps.FIXED_PRICE: return i18n.str`With price`
+                      case Steps.FIXED_SUMMARY: return i18n.str`With summary`
+                      case Steps.BOTH_FIXED: return i18n.str`With price and 
summary`
+                    }
+                  }}
                 />
+                {state.type === Steps.BOTH_FIXED || state.type === 
Steps.FIXED_SUMMARY ?
+                  <Input
+                    name="template_contract.summary"
+                    inputType="multiline"
+                    label={i18n.str`Fixed summary`}
+                    tooltip={i18n.str`If specified, this template will create 
order with the same summary`}
+                  />
+                  : undefined}
+                {state.type === Steps.BOTH_FIXED || state.type === 
Steps.FIXED_PRICE ?
+                  <InputCurrency
+                    name="template_contract.amount"
+                    label={i18n.str`Fixed price`}
+                    tooltip={i18n.str`If specified, this template will create 
order with the same price`}
+                  />
+                  : undefined}
                 <InputNumber
                   name="template_contract.minimum_age"
                   label={i18n.str`Minimum age`}

-- 
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]