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

---
 .../src/pages/WithdrawalConfirmationQuestion.tsx   | 466 ++++++++++++---------
 1 file changed, 258 insertions(+), 208 deletions(-)

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

diff --git a/packages/demobank-ui/src/pages/WithdrawalConfirmationQuestion.tsx b/packages/demobank-ui/src/pages/WithdrawalConfirmationQuestion.tsx
index b87b77c83..4e5c621e2 100644
--- a/packages/demobank-ui/src/pages/WithdrawalConfirmationQuestion.tsx
+++ b/packages/demobank-ui/src/pages/WithdrawalConfirmationQuestion.tsx
@@ -15,24 +15,29 @@
  */
 
 import { Logger } from "@gnu-taler/taler-util";
+import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
 import { Fragment, h, VNode } from "preact";
-import { StateUpdater, useMemo, useState } from "preact/hooks";
+import { useMemo, useState } from "preact/hooks";
 import { useBackendContext } from "../context/backend.js";
-import { PageStateType, usePageContext } from "../context/pageState.js";
-import {
-  InternationalizationAPI,
-  useTranslationContext,
-} from "@gnu-taler/web-util/lib/index.browser";
-import { BackendState } from "../hooks/backend.js";
-import { prepareHeaders } from "../utils.js";
+import { 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("WithdrawalConfirmationQuestion");
 
+interface Props {
+  account: string;
+  withdrawalId: string;
+}
 /**
  * Additional authentication required to complete the operation.
  * Not providing a back button, only abort.
  */
-export function WithdrawalConfirmationQuestion(): VNode {
+export function WithdrawalConfirmationQuestion({
+  account,
+  withdrawalId,
+}: Props): VNode {
   const { pageState, pageStateSetter } = usePageContext();
   const backend = useBackendContext();
   const { i18n } = useTranslationContext();
@@ -42,10 +47,20 @@ export function WithdrawalConfirmationQuestion(): VNode {
       a: Math.floor(Math.random() * 10),
       b: Math.floor(Math.random() * 10),
     };
-  }, [pageState.withdrawalId]);
+  }, []);
 
+  const { confirmWithdrawal, abortWithdrawal } = useAccessAPI();
   const [captchaAnswer, setCaptchaAnswer] = useState<string | undefined>();
-
+  const answer = parseInt(captchaAnswer ?? "", 10);
+  const errors = undefinedIfEmpty({
+    answer: !captchaAnswer
+      ? i18n.str`Answer the question before continue`
+      : Number.isNaN(answer)
+      ? i18n.str`The answer should be a number`
+      : answer !== captchaNumbers.a + captchaNumbers.b
+      ? i18n.str`The answer "${answer}" to "${captchaNumbers.a} + ${captchaNumbers.b}" is wrong.`
+      : undefined,
+  });
   return (
     <Fragment>
       <h1 class="nav">{i18n.str`Confirm Withdrawal`}</h1>
@@ -82,33 +97,49 @@ export function WithdrawalConfirmationQuestion(): VNode {
                     setCaptchaAnswer(e.currentTarget.value);
                   }}
                 />
+                <ShowInputErrorLabel
+                  message={errors?.answer}
+                  isDirty={captchaAnswer !== undefined}
+                />
               </p>
               <p>
                 <button
                   type="submit"
                   class="pure-button pure-button-primary btn-confirm"
+                  disabled={!!errors}
                   onClick={async (e) => {
                     e.preventDefault();
-                    if (
-                      captchaAnswer ==
-                      (captchaNumbers.a + captchaNumbers.b).toString()
-                    ) {
-                      await confirmWithdrawalCall(
-                        backend.state,
-                        pageState.withdrawalId,
-                        pageStateSetter,
-                        i18n,
-                      );
-                      return;
+                    try {
+                      await confirmWithdrawal(withdrawalId);
+                      pageStateSetter((prevState) => {
+                        const { talerWithdrawUri, ...rest } = prevState;
+                        return {
+                          ...rest,
+                          info: i18n.str`Withdrawal confirmed!`,
+                        };
+                      });
+                    } catch (error) {
+                      pageStateSetter((prevState) => ({
+                        ...prevState,
+                        error: {
+                          title: i18n.str`Could not confirm the withdrawal`,
+                          description: (error as any).error.description,
+                          debug: JSON.stringify(error),
+                        },
+                      }));
                     }
-                    pageStateSetter((prevState: PageStateType) => ({
-                      ...prevState,
-
-                      error: {
-                        title: i18n.str`The answer "${captchaAnswer}" to "${captchaNumbers.a} + ${captchaNumbers.b}" is wrong.`,
-                      },
-                    }));
-                    setCaptchaAnswer(undefined);
+                    // if (
+                    //   captchaAnswer ==
+                    //   (captchaNumbers.a + captchaNumbers.b).toString()
+                    // ) {
+                    //   await confirmWithdrawalCall(
+                    //     backend.state,
+                    //     pageState.withdrawalId,
+                    //     pageStateSetter,
+                    //     i18n,
+                    //   );
+                    //   return;
+                    // }
                   }}
                 >
                   {i18n.str`Confirm`}
@@ -118,12 +149,31 @@ export function WithdrawalConfirmationQuestion(): VNode {
                   class="pure-button pure-button-secondary btn-cancel"
                   onClick={async (e) => {
                     e.preventDefault();
-                    await abortWithdrawalCall(
-                      backend.state,
-                      pageState.withdrawalId,
-                      pageStateSetter,
-                      i18n,
-                    );
+                    try {
+                      await abortWithdrawal(withdrawalId);
+                      pageStateSetter((prevState) => {
+                        const { talerWithdrawUri, ...rest } = prevState;
+                        return {
+                          ...rest,
+                          info: i18n.str`Withdrawal confirmed!`,
+                        };
+                      });
+                    } catch (error) {
+                      pageStateSetter((prevState) => ({
+                        ...prevState,
+                        error: {
+                          title: i18n.str`Could not confirm the withdrawal`,
+                          description: (error as any).error.description,
+                          debug: JSON.stringify(error),
+                        },
+                      }));
+                    }
+                    // await abortWithdrawalCall(
+                    //   backend.state,
+                    //   pageState.withdrawalId,
+                    //   pageStateSetter,
+                    //   i18n,
+                    // );
                   }}
                 >
                   {i18n.str`Cancel`}
@@ -156,188 +206,188 @@ export function WithdrawalConfirmationQuestion(): VNode {
  * This function will set the confirmation status in the
  * 'page state' and let the related components refresh.
  */
-async function confirmWithdrawalCall(
-  backendState: BackendState,
-  withdrawalId: string | undefined,
-  pageStateSetter: StateUpdater<PageStateType>,
-  i18n: InternationalizationAPI,
-): Promise<void> {
-  if (backendState.status === "loggedOut") {
-    logger.error("No credentials found.");
-    pageStateSetter((prevState) => ({
-      ...prevState,
+// async function confirmWithdrawalCall(
+//   backendState: BackendState,
+//   withdrawalId: string | undefined,
+//   pageStateSetter: StateUpdater<PageStateType>,
+//   i18n: InternationalizationAPI,
+// ): Promise<void> {
+//   if (backendState.status === "loggedOut") {
+//     logger.error("No credentials found.");
+//     pageStateSetter((prevState) => ({
+//       ...prevState,
 
-      error: {
-        title: i18n.str`No credentials found.`,
-      },
-    }));
-    return;
-  }
-  if (typeof withdrawalId === "undefined") {
-    logger.error("No withdrawal ID found.");
-    pageStateSetter((prevState) => ({
-      ...prevState,
+//       error: {
+//         title: i18n.str`No credentials found.`,
+//       },
+//     }));
+//     return;
+//   }
+//   if (typeof withdrawalId === "undefined") {
+//     logger.error("No withdrawal ID found.");
+//     pageStateSetter((prevState) => ({
+//       ...prevState,
 
-      error: {
-        title: i18n.str`No withdrawal ID found.`,
-      },
-    }));
-    return;
-  }
-  let res: Response;
-  try {
-    const { username, password } = backendState;
-    const headers = prepareHeaders(username, password);
-    /**
-     * NOTE: tests show that when a same object is being
-     * POSTed, caching might prevent same requests from being
-     * made.  Hence, trying to POST twice the same amount might
-     * get silently ignored.
-     *
-     * headers.append("cache-control", "no-store");
-     * headers.append("cache-control", "no-cache");
-     * headers.append("pragma", "no-cache");
-     * */
+//       error: {
+//         title: i18n.str`No withdrawal ID found.`,
+//       },
+//     }));
+//     return;
+//   }
+//   let res: Response;
+//   try {
+//     const { username, password } = backendState;
+//     const headers = prepareHeaders(username, password);
+//     /**
+//      * NOTE: tests show that when a same object is being
+//      * POSTed, caching might prevent same requests from being
+//      * made.  Hence, trying to POST twice the same amount might
+//      * get silently ignored.
+//      *
+//      * headers.append("cache-control", "no-store");
+//      * headers.append("cache-control", "no-cache");
+//      * headers.append("pragma", "no-cache");
+//      * */
 
-    // Backend URL must have been stored _with_ a final slash.
-    const url = new URL(
-      `access-api/accounts/${backendState.username}/withdrawals/${withdrawalId}/confirm`,
-      backendState.url,
-    );
-    res = await fetch(url.href, {
-      method: "POST",
-      headers,
-    });
-  } catch (error) {
-    logger.error("Could not POST withdrawal confirmation to the bank", error);
-    pageStateSetter((prevState) => ({
-      ...prevState,
+//     // Backend URL must have been stored _with_ a final slash.
+//     const url = new URL(
+//       `access-api/accounts/${backendState.username}/withdrawals/${withdrawalId}/confirm`,
+//       backendState.url,
+//     );
+//     res = await fetch(url.href, {
+//       method: "POST",
+//       headers,
+//     });
+//   } catch (error) {
+//     logger.error("Could not POST withdrawal confirmation to the bank", error);
+//     pageStateSetter((prevState) => ({
+//       ...prevState,
 
-      error: {
-        title: i18n.str`Could not confirm the withdrawal`,
-        description: (error as any).error.description,
-        debug: JSON.stringify(error),
-      },
-    }));
-    return;
-  }
-  if (!res || !res.ok) {
-    const response = await res.json();
-    // assume not ok if res is null
-    logger.error(
-      `Withdrawal confirmation gave response error (${res.status})`,
-      res.statusText,
-    );
-    pageStateSetter((prevState) => ({
-      ...prevState,
+//       error: {
+//         title: i18n.str`Could not confirm the withdrawal`,
+//         description: (error as any).error.description,
+//         debug: JSON.stringify(error),
+//       },
+//     }));
+//     return;
+//   }
+//   if (!res || !res.ok) {
+//     const response = await res.json();
+//     // assume not ok if res is null
+//     logger.error(
+//       `Withdrawal confirmation gave response error (${res.status})`,
+//       res.statusText,
+//     );
+//     pageStateSetter((prevState) => ({
+//       ...prevState,
 
-      error: {
-        title: i18n.str`Withdrawal confirmation gave response error`,
-        debug: JSON.stringify(response),
-      },
-    }));
-    return;
-  }
-  logger.trace("Withdrawal operation confirmed!");
-  pageStateSetter((prevState) => {
-    const { talerWithdrawUri, ...rest } = prevState;
-    return {
-      ...rest,
+//       error: {
+//         title: i18n.str`Withdrawal confirmation gave response error`,
+//         debug: JSON.stringify(response),
+//       },
+//     }));
+//     return;
+//   }
+//   logger.trace("Withdrawal operation confirmed!");
+//   pageStateSetter((prevState) => {
+//     const { talerWithdrawUri, ...rest } = prevState;
+//     return {
+//       ...rest,
 
-      info: i18n.str`Withdrawal confirmed!`,
-    };
-  });
-}
+//       info: i18n.str`Withdrawal confirmed!`,
+//     };
+//   });
+// }
 
-/**
- * Abort a withdrawal operation via the Access API's /abort.
- */
-async function abortWithdrawalCall(
-  backendState: BackendState,
-  withdrawalId: string | undefined,
-  pageStateSetter: StateUpdater<PageStateType>,
-  i18n: InternationalizationAPI,
-): Promise<void> {
-  if (backendState.status === "loggedOut") {
-    logger.error("No credentials found.");
-    pageStateSetter((prevState) => ({
-      ...prevState,
+// /**
+//  * Abort a withdrawal operation via the Access API's /abort.
+//  */
+// async function abortWithdrawalCall(
+//   backendState: BackendState,
+//   withdrawalId: string | undefined,
+//   pageStateSetter: StateUpdater<PageStateType>,
+//   i18n: InternationalizationAPI,
+// ): Promise<void> {
+//   if (backendState.status === "loggedOut") {
+//     logger.error("No credentials found.");
+//     pageStateSetter((prevState) => ({
+//       ...prevState,
 
-      error: {
-        title: i18n.str`No credentials found.`,
-      },
-    }));
-    return;
-  }
-  if (typeof withdrawalId === "undefined") {
-    logger.error("No withdrawal ID found.");
-    pageStateSetter((prevState) => ({
-      ...prevState,
+//       error: {
+//         title: i18n.str`No credentials found.`,
+//       },
+//     }));
+//     return;
+//   }
+//   if (typeof withdrawalId === "undefined") {
+//     logger.error("No withdrawal ID found.");
+//     pageStateSetter((prevState) => ({
+//       ...prevState,
 
-      error: {
-        title: i18n.str`No withdrawal ID found.`,
-      },
-    }));
-    return;
-  }
-  let res: Response;
-  try {
-    const { username, password } = backendState;
-    const headers = prepareHeaders(username, password);
-    /**
-     * NOTE: tests show that when a same object is being
-     * POSTed, caching might prevent same requests from being
-     * made.  Hence, trying to POST twice the same amount might
-     * get silently ignored.  Needs more observation!
-     *
-     * headers.append("cache-control", "no-store");
-     * headers.append("cache-control", "no-cache");
-     * headers.append("pragma", "no-cache");
-     * */
+//       error: {
+//         title: i18n.str`No withdrawal ID found.`,
+//       },
+//     }));
+//     return;
+//   }
+//   let res: Response;
+//   try {
+//     const { username, password } = backendState;
+//     const headers = prepareHeaders(username, password);
+//     /**
+//      * NOTE: tests show that when a same object is being
+//      * POSTed, caching might prevent same requests from being
+//      * made.  Hence, trying to POST twice the same amount might
+//      * get silently ignored.  Needs more observation!
+//      *
+//      * headers.append("cache-control", "no-store");
+//      * headers.append("cache-control", "no-cache");
+//      * headers.append("pragma", "no-cache");
+//      * */
 
-    // Backend URL must have been stored _with_ a final slash.
-    const url = new URL(
-      `access-api/accounts/${backendState.username}/withdrawals/${withdrawalId}/abort`,
-      backendState.url,
-    );
-    res = await fetch(url.href, { method: "POST", headers });
-  } catch (error) {
-    logger.error("Could not abort the withdrawal", error);
-    pageStateSetter((prevState) => ({
-      ...prevState,
+//     // Backend URL must have been stored _with_ a final slash.
+//     const url = new URL(
+//       `access-api/accounts/${backendState.username}/withdrawals/${withdrawalId}/abort`,
+//       backendState.url,
+//     );
+//     res = await fetch(url.href, { method: "POST", headers });
+//   } catch (error) {
+//     logger.error("Could not abort the withdrawal", error);
+//     pageStateSetter((prevState) => ({
+//       ...prevState,
 
-      error: {
-        title: i18n.str`Could not abort the withdrawal.`,
-        description: (error as any).error.description,
-        debug: JSON.stringify(error),
-      },
-    }));
-    return;
-  }
-  if (!res.ok) {
-    const response = await res.json();
-    logger.error(
-      `Withdrawal abort gave response error (${res.status})`,
-      res.statusText,
-    );
-    pageStateSetter((prevState) => ({
-      ...prevState,
+//       error: {
+//         title: i18n.str`Could not abort the withdrawal.`,
+//         description: (error as any).error.description,
+//         debug: JSON.stringify(error),
+//       },
+//     }));
+//     return;
+//   }
+//   if (!res.ok) {
+//     const response = await res.json();
+//     logger.error(
+//       `Withdrawal abort gave response error (${res.status})`,
+//       res.statusText,
+//     );
+//     pageStateSetter((prevState) => ({
+//       ...prevState,
 
-      error: {
-        title: i18n.str`Withdrawal abortion failed.`,
-        description: response.error.description,
-        debug: JSON.stringify(response),
-      },
-    }));
-    return;
-  }
-  logger.trace("Withdrawal operation aborted!");
-  pageStateSetter((prevState) => {
-    const { ...rest } = prevState;
-    return {
-      ...rest,
+//       error: {
+//         title: i18n.str`Withdrawal abortion failed.`,
+//         description: response.error.description,
+//         debug: JSON.stringify(response),
+//       },
+//     }));
+//     return;
+//   }
+//   logger.trace("Withdrawal operation aborted!");
+//   pageStateSetter((prevState) => {
+//     const { ...rest } = prevState;
+//     return {
+//       ...rest,
 
-      info: i18n.str`Withdrawal aborted!`,
-    };
-  });
-}
+//       info: i18n.str`Withdrawal aborted!`,
+//     };
+//   });
+// }
-- 
cgit v1.2.3

