From a8c5a9696c1735a178158cbc9ac4f9bb4b6f013d Mon Sep 17 00:00:00 2001
From: Sebastian <sebasjm@gmail.com>
Date: Wed, 8 Feb 2023 17:41:19 -0300
Subject: impl accout management and refactor

---
 .../demobank-ui/src/pages/WalletWithdrawForm.tsx   | 259 ++++++++++++---------
 1 file changed, 145 insertions(+), 114 deletions(-)

(limited to 'packages/demobank-ui/src/pages/WalletWithdrawForm.tsx')

diff --git a/packages/demobank-ui/src/pages/WalletWithdrawForm.tsx b/packages/demobank-ui/src/pages/WalletWithdrawForm.tsx
index a1b616657..2b2df3baa 100644
--- a/packages/demobank-ui/src/pages/WalletWithdrawForm.tsx
+++ b/packages/demobank-ui/src/pages/WalletWithdrawForm.tsx
@@ -14,36 +14,54 @@
  GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 
-import { Logger } from "@gnu-taler/taler-util";
-import { h, VNode } from "preact";
-import { StateUpdater, useEffect, useRef } from "preact/hooks";
-import { useBackendContext } from "../context/backend.js";
-import { PageStateType, usePageContext } from "../context/pageState.js";
+import { Amounts, Logger } from "@gnu-taler/taler-util";
 import {
-  InternationalizationAPI,
+  RequestError,
   useTranslationContext,
 } from "@gnu-taler/web-util/lib/index.browser";
-import { BackendState } from "../hooks/backend.js";
-import { prepareHeaders, validateAmount } from "../utils.js";
+import { h, VNode } from "preact";
+import { useEffect, useRef, useState } from "preact/hooks";
+import { PageStateType, usePageContext } from "../context/pageState.js";
+import { useAccessAPI } from "../hooks/access.js";
+import { undefinedIfEmpty } from "../utils.js";
+import { ShowInputErrorLabel } from "./ShowInputErrorLabel.js";
 
 const logger = new Logger("WalletWithdrawForm");
 
 export function WalletWithdrawForm({
   focus,
   currency,
+  onError,
+  onSuccess,
 }: {
-  currency?: string;
+  currency: string;
   focus?: boolean;
+  onError: (e: PageStateType["error"]) => void;
+  onSuccess: (
+    data: SandboxBackend.Access.BankAccountCreateWithdrawalResponse,
+  ) => void;
 }): VNode {
-  const backend = useBackendContext();
-  const { pageState, pageStateSetter } = usePageContext();
+  // const backend = useBackendContext();
+  // const { pageState, pageStateSetter } = usePageContext();
   const { i18n } = useTranslationContext();
-  let submitAmount: string | undefined = "5.00";
+  const { createWithdrawal } = useAccessAPI();
 
+  const [amount, setAmount] = useState<string | undefined>("5.00");
   const ref = useRef<HTMLInputElement>(null);
   useEffect(() => {
     if (focus) ref.current?.focus();
   }, [focus]);
+
+  const amountFloat = amount ? parseFloat(amount) : undefined;
+  const errors = undefinedIfEmpty({
+    amount: !amountFloat
+      ? i18n.str`required`
+      : Number.isNaN(amountFloat)
+      ? i18n.str`should be a number`
+      : amountFloat < 0
+      ? i18n.str`should be positive`
+      : undefined,
+  });
   return (
     <form
       id="reserve-form"
@@ -63,8 +81,8 @@ export function WalletWithdrawForm({
             type="text"
             readonly
             class="currency-indicator"
-            size={currency?.length ?? 5}
-            maxLength={currency?.length}
+            size={currency.length}
+            maxLength={currency.length}
             tabIndex={-1}
             value={currency}
           />
@@ -74,14 +92,15 @@ export function WalletWithdrawForm({
             ref={ref}
             id="withdraw-amount"
             name="withdraw-amount"
-            value={submitAmount}
+            value={amount ?? ""}
             onChange={(e): void => {
-              // FIXME: validate using 'parseAmount()',
-              // deactivate submit button as long as
-              // amount is not valid
-              submitAmount = e.currentTarget.value;
+              setAmount(e.currentTarget.value);
             }}
           />
+          <ShowInputErrorLabel
+            message={errors?.amount}
+            isDirty={amount !== undefined}
+          />
         </div>
       </p>
       <p>
@@ -90,22 +109,34 @@ export function WalletWithdrawForm({
             id="select-exchange"
             class="pure-button pure-button-primary"
             type="submit"
+            disabled={!!errors}
             value={i18n.str`Withdraw`}
-            onClick={(e) => {
+            onClick={async (e) => {
               e.preventDefault();
-              submitAmount = validateAmount(submitAmount);
-              /**
-               * By invalid amounts, the validator prints error messages
-               * on the console, and the browser colourizes the amount input
-               * box to indicate a error.
-               */
-              if (!submitAmount && currency) return;
-              createWithdrawalCall(
-                `${currency}:${submitAmount}`,
-                backend.state,
-                pageStateSetter,
-                i18n,
-              );
+              if (!amountFloat) return;
+              try {
+                const result = await createWithdrawal({
+                  amount: Amounts.stringify(
+                    Amounts.fromFloat(amountFloat, currency),
+                  ),
+                });
+
+                onSuccess(result.data);
+              } catch (error) {
+                if (error instanceof RequestError) {
+                  onError({
+                    title: i18n.str`Could not create withdrawal operation`,
+                    description: (error as any).error.description,
+                    debug: JSON.stringify(error),
+                  });
+                }
+                if (error instanceof Error) {
+                  onError({
+                    title: i18n.str`Something when wrong trying to start the withdrawal`,
+                    description: error.message,
+                  });
+                }
+              }
             }}
           />
         </div>
@@ -114,84 +145,84 @@ export function WalletWithdrawForm({
   );
 }
 
-/**
- * This function creates a withdrawal operation via the Access API.
- *
- * After having successfully created the withdrawal operation, the
- * user should receive a QR code of the "taler://withdraw/" type and
- * supposed to scan it with their phone.
- *
- * TODO: (1) after the scan, the page should refresh itself and inform
- * the user about the operation's outcome.  (2) use POST helper.  */
-async function createWithdrawalCall(
-  amount: string,
-  backendState: BackendState,
-  pageStateSetter: StateUpdater<PageStateType>,
-  i18n: InternationalizationAPI,
-): Promise<void> {
-  if (backendState?.status === "loggedOut") {
-    logger.error("Page has a problem: no credentials found in the state.");
-    pageStateSetter((prevState) => ({
-      ...prevState,
-
-      error: {
-        title: i18n.str`No credentials given.`,
-      },
-    }));
-    return;
-  }
-
-  let res: Response;
-  try {
-    const { username, password } = backendState;
-    const headers = prepareHeaders(username, password);
-
-    // Let bank generate withdraw URI:
-    const url = new URL(
-      `access-api/accounts/${backendState.username}/withdrawals`,
-      backendState.url,
-    );
-    res = await fetch(url.href, {
-      method: "POST",
-      headers,
-      body: JSON.stringify({ amount }),
-    });
-  } catch (error) {
-    logger.trace("Could not POST withdrawal request to the bank", error);
-    pageStateSetter((prevState) => ({
-      ...prevState,
-
-      error: {
-        title: i18n.str`Could not create withdrawal operation`,
-        description: (error as any).error.description,
-        debug: JSON.stringify(error),
-      },
-    }));
-    return;
-  }
-  if (!res.ok) {
-    const response = await res.json();
-    logger.error(
-      `Withdrawal creation gave response error: ${response} (${res.status})`,
-    );
-    pageStateSetter((prevState) => ({
-      ...prevState,
-
-      error: {
-        title: i18n.str`Withdrawal creation gave response error`,
-        description: response.error.description,
-        debug: JSON.stringify(response),
-      },
-    }));
-    return;
-  }
-
-  logger.trace("Withdrawal operation created!");
-  const resp = await res.json();
-  pageStateSetter((prevState: PageStateType) => ({
-    ...prevState,
-    withdrawalInProgress: true,
-    talerWithdrawUri: resp.taler_withdraw_uri,
-    withdrawalId: resp.withdrawal_id,
-  }));
-}
+// /**
+//  * This function creates a withdrawal operation via the Access API.
+//  *
+//  * After having successfully created the withdrawal operation, the
+//  * user should receive a QR code of the "taler://withdraw/" type and
+//  * supposed to scan it with their phone.
+//  *
+//  * TODO: (1) after the scan, the page should refresh itself and inform
+//  * the user about the operation's outcome.  (2) use POST helper.  */
+// async function createWithdrawalCall(
+//   amount: string,
+//   backendState: BackendState,
+//   pageStateSetter: StateUpdater<PageStateType>,
+//   i18n: InternationalizationAPI,
+// ): Promise<void> {
+//   if (backendState?.status === "loggedOut") {
+//     logger.error("Page has a problem: no credentials found in the state.");
+//     pageStateSetter((prevState) => ({
+//       ...prevState,
+
+//       error: {
+//         title: i18n.str`No credentials given.`,
+//       },
+//     }));
+//     return;
+//   }
+
+//   let res: Response;
+//   try {
+//     const { username, password } = backendState;
+//     const headers = prepareHeaders(username, password);
+
+//     // Let bank generate withdraw URI:
+//     const url = new URL(
+//       `access-api/accounts/${backendState.username}/withdrawals`,
+//       backendState.url,
+//     );
+//     res = await fetch(url.href, {
+//       method: "POST",
+//       headers,
+//       body: JSON.stringify({ amount }),
+//     });
+//   } catch (error) {
+//     logger.trace("Could not POST withdrawal request to the bank", error);
+//     pageStateSetter((prevState) => ({
+//       ...prevState,
+
+//       error: {
+//         title: i18n.str`Could not create withdrawal operation`,
+//         description: (error as any).error.description,
+//         debug: JSON.stringify(error),
+//       },
+//     }));
+//     return;
+//   }
+//   if (!res.ok) {
+//     const response = await res.json();
+//     logger.error(
+//       `Withdrawal creation gave response error: ${response} (${res.status})`,
+//     );
+//     pageStateSetter((prevState) => ({
+//       ...prevState,
+
+//       error: {
+//         title: i18n.str`Withdrawal creation gave response error`,
+//         description: response.error.description,
+//         debug: JSON.stringify(response),
+//       },
+//     }));
+//     return;
+//   }
+
+//   logger.trace("Withdrawal operation created!");
+//   const resp = await res.json();
+//   pageStateSetter((prevState: PageStateType) => ({
+//     ...prevState,
+//     withdrawalInProgress: true,
+//     talerWithdrawUri: resp.taler_withdraw_uri,
+//     withdrawalId: resp.withdrawal_id,
+//   }));
+// }
-- 
cgit v1.2.3

