gnunet-svn
[Top][All Lists]
Advanced

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

[taler-merchant-backoffice] 02/02: transactions list pagination (WIP)


From: gnunet
Subject: [taler-merchant-backoffice] 02/02: transactions list pagination (WIP)
Date: Mon, 10 Jan 2022 19:30:00 +0100

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

ms pushed a commit to branch master
in repository merchant-backoffice.

commit 4d9b35bb4c153b9bf98cd3d8eeccda828d3aea29
Author: ms <ms@taler.net>
AuthorDate: Mon Jan 10 19:29:50 2022 +0100

    transactions list pagination (WIP)
---
 packages/bank/src/hooks/index.ts          |   1 +
 packages/bank/src/pages/home/index.tsx    | 143 +++++++++++++++++++++++-------
 packages/bank/tests/__tests__/homepage.js |  55 ++++++++++--
 3 files changed, 161 insertions(+), 38 deletions(-)

diff --git a/packages/bank/src/hooks/index.ts b/packages/bank/src/hooks/index.ts
index 9a1b50a..17a9d1d 100644
--- a/packages/bank/src/hooks/index.ts
+++ b/packages/bank/src/hooks/index.ts
@@ -115,6 +115,7 @@ export function useNotNullLocalStorage(
   key: string,
   initialValue: string,
 ): [string, StateUpdater<string>] {
+
   const [storedValue, setStoredValue] = useState<string>((): string => {
     return typeof window !== "undefined"
       ? window.localStorage.getItem(key) || initialValue
diff --git a/packages/bank/src/pages/home/index.tsx 
b/packages/bank/src/pages/home/index.tsx
index 61cf781..5401987 100644
--- a/packages/bank/src/pages/home/index.tsx
+++ b/packages/bank/src/pages/home/index.tsx
@@ -57,6 +57,17 @@ interface AccountStateType {
  * Helpers. *
  ***********/
 
+function useTransactionPageNumber(): [number, StateUpdater<number>] {
+
+  const ret = useNotNullLocalStorage("transaction-page", "0");
+  const retObj = JSON.parse(ret[0]);
+  const retSetter: StateUpdater<number> = function(val) {
+    const newVal = val instanceof Function ? JSON.stringify(val(retObj)) : 
JSON.stringify(val)
+    ret[1](newVal)
+  }
+  return [retObj, retSetter];
+}
+
 /**
  * Craft headers with Authorization and Content-Type.
  */
@@ -134,6 +145,7 @@ function usePageState(
   const retObj: PageStateType = JSON.parse(ret[0]);
   const retSetter: StateUpdater<PageStateType> = function(val) {
     const newVal = val instanceof Function ? JSON.stringify(val(retObj)) : 
JSON.stringify(val)
+    console.log("setting new page state", newVal)
     ret[1](newVal)
   }
   return [retObj, retSetter];
@@ -296,7 +308,6 @@ async function confirmWithdrawalCall(
   } else {
     console.log("Withdrawal operation confirmed!");
     pageStateSetter((prevState) => {
-      delete prevState.talerWithdrawUri;
       const { talerWithdrawUri, ...rest } = prevState;
       return {
         ...rest,
@@ -476,15 +487,52 @@ async function registrationCall(
  * Functional components. *
  *************************/
 
+/**
+ * Show list of transactions.
+ */
+function Transactions(Props: any): VNode {
+
+  const { pageNumber, accountLabel } = Props;
+  const { data, error } = useSWR(
+    `access-api/accounts/${accountLabel}/transactions?page=${pageNumber}`
+  );
+  if (typeof error !== "undefined") {
+    console.log("balance error", error);
+    switch(error.status) {
+      case 404: {
+        return <p>Transactions page {pageNumber} was not found.</p>
+      }
+      case 401: {
+        return <p>Wrong credentials given.</p>
+      }
+      default: {
+        return <p>Transaction page {pageNumber} could not be retrieved.</p>
+      }
+    }
+  }
+  var txsPages = <p>"loading..."</p>
+
+  if (data) {
+    txsPages = data.map((item: any) => <div>Result</div>)
+  }
+  return txsPages;
+}
+
 /**
  * Show only the account's balance.
  */
-function Account(props: any) {
-  const { withdrawalOutcome, talerWithdrawUri, accountLabel } = props;
-  const { data, error } = useSWR(`access-api/accounts/${props.accountLabel}`);
-  console.log("account data", data);
-  console.log("account error", error);
+function Account(Props: any): VNode {
+  const {
+    withdrawalOutcome,
+    talerWithdrawUri,
+    accountLabel } = Props;
+  const i18n = useTranslator();
+  /**
+   * Getting the bank account balance.
+   */
+  const { data, error } = useSWR(`access-api/accounts/${accountLabel}`);
   if (typeof error !== "undefined") {
+    console.log("account error", error);
     switch(error.status) {
       case 404: {
         return <p>Username was not found</p>
@@ -497,18 +545,25 @@ function Account(props: any) {
       }
     }
   }
-
   if (!data) return <p>Retrieving the profile page...</p>;
+
+  /**
+   * Withdrawal reached a final state: show it.
+   */
   if (withdrawalOutcome) {
     return <Fragment>
       <p>{withdrawalOutcome}</p>
-      {props.children}
+      {Props.children}
     </Fragment>
   }
+
   /**
-   * A Taler withdrawal replaces everything in the page and
-   * starts polling the backend until either the wallet selected
-   * a exchange and reserve public key, or a error / abort happened.
+   * This block shows the withdrawal QR code.
+   *
+   * A withdrawal operation replaces everything in the page and
+   * (ToDo:) starts polling the backend until either the wallet
+   * selected a exchange and reserve public key, or a error / abort
+   * happened.
    *
    * After reaching one of the above states, the user should be
    * brought to this ("Account") page where they get informed about
@@ -520,12 +575,28 @@ function Account(props: any) {
       <div>{QR({text: talerWithdrawUri})}</div>
       <a href={talerWithdrawUri}></a>
       <p>Withdraw address: <pre>{talerWithdrawUri}</pre></p>
-      {props.children}
+      {Props.children}
     </Fragment>);
   }
+
+  /**
+   * This part shows a list of transactions: with 5 elements by
+   * default and offers a "load more" button.
+   */
+  var [txPageNumber, setTxPageNumber] = useTransactionPageNumber() // Buggy.
+  var txsPages = []
+  for (let i = 0; i <= txPageNumber; i++) {
+    txsPages.push(<Transactions accountLabel={Props.accountLabel} 
pageNumber={txPageNumber} />)
+  }
+  
   return (<Fragment>
     <p>Your balance is {data.balance.amount}.</p>
-    {props.children}
+    <div>
+      <span>{i18n`Last transactions:`}</span> { txsPages }
+      <button onClick={() => setTxPageNumber(txPageNumber + 1)}>{i18n`Load 
more`}</button>
+      <button onClick={() => setTxPageNumber(0)}>{i18n`Reset`}</button>
+    </div>
+    {Props.children}
   </Fragment>);
 }
 
@@ -596,7 +667,8 @@ export function BankHome(): VNode {
          withdrawalOutcome={pageState.withdrawalOutcome}
          talerWithdrawUri={pageState.talerWithdrawUri}
          accountLabel={backendState.username}>
-
+          
+         { /* The user is logged in: offer to log out.  */ }
          <button onClick={() => {
             pageStateSetter((prevState) => {
               const {
@@ -607,30 +679,41 @@ export function BankHome(): VNode {
            })
          }}>Sign out</button>
 
-          {!pageState.withdrawalInProgress && <button onClick={() => {
-            createWithdrawalCall(
-              "EUR:5",
-              backendState,
-              pageStateSetter
-            )}}>{i18n`Charge Taler wallet`}</button>
+          { /**
+            * No withdrawal is happening: offer to start one.
+            */
+           !pageState.withdrawalInProgress && <button onClick={() => {
+              createWithdrawalCall(
+                "EUR:5",
+                backendState,
+                pageStateSetter
+              )}}>{i18n`Charge Taler wallet`}</button>
          }
 
-         {pageState.withdrawalOutcome && <button onClick={() => {
+         { /**
+            * Withdrawal reached a persisten state: offer to
+            * return back to the pristine profile page.
+            */
+           pageState.withdrawalOutcome && <button onClick={() => {
               pageStateSetter((prevState) => {
-               const { withdrawalOutcome, ...rest } = prevState;
+               const { withdrawalOutcome, withdrawalId, ...rest } = prevState;
                return {...rest, withdrawalInProgress: 
false};})}}>{i18n`Close`}</button>
          }
 
-         {pageState.talerWithdrawUri && <div><button onClick={() => {
-            confirmWithdrawalCall(
-              backendState,
-             pageState.withdrawalId,
-             pageStateSetter);}}>{i18n`Confirm withdrawal`}</button>
-            <button onClick={() => {
-              abortWithdrawalCall(
+         { /**
+            * The withdrawal QR code is rendered: offer to confirm
+            * or abort the operation.
+            */
+           pageState.talerWithdrawUri && <div><button onClick={() => {
+              confirmWithdrawalCall(
                 backendState,
                pageState.withdrawalId,
-               pageStateSetter);}}>{i18n`Abort withdrawal`}</button>
+               pageStateSetter);}}>{i18n`Confirm withdrawal`}</button>
+              <button onClick={() => {
+                abortWithdrawalCall(
+                  backendState,
+                 pageState.withdrawalId,
+                 pageStateSetter);}}>{i18n`Abort withdrawal`}</button>
          </div>}
        </Account>
       </SWRWithCredentials>
diff --git a/packages/bank/tests/__tests__/homepage.js 
b/packages/bank/tests/__tests__/homepage.js
index 0145522..f1045f1 100644
--- a/packages/bank/tests/__tests__/homepage.js
+++ b/packages/bank/tests/__tests__/homepage.js
@@ -16,6 +16,34 @@ jest.mock("../../src/i18n")
 const i18n = require("../../src/i18n")
 i18n.useTranslator.mockImplementation(() => function(arg) {return arg})
 
+/**
+ * Mocking local storage, see:
+ * 
https://stackoverflow.com/questions/32911630/how-do-i-deal-with-localstorage-in-jest-tests
+ */
+class LocalStorageMock {
+  constructor() {
+    this.store = {};
+  }
+
+  clear() {
+    this.store = {};
+  }
+
+  getItem(key) {
+    return this.store[key] || null;
+  }
+
+  setItem(key, value) {
+    this.store[key] = String(value);
+  }
+
+  removeItem(key) {
+    delete this.store[key];
+  }
+}
+
+global.localStotage = new LocalStorageMock();
+
 beforeAll(() => {
   Object.defineProperty(window, 'location', {
     value: {
@@ -23,10 +51,9 @@ beforeAll(() => {
       pathname: "/demobanks/default"
     }
   })
-  // Invalidating local storage: makes it more difficult
-  // to isolate the individual tests, and it doesn't really
-  // participate in the SPA logic.
-  global.Storage.prototype.setItem = jest.fn((key, value) => {})
+})
+afterAll(() => {
+  global.localStorage.clear()
 })
 
 /**
@@ -135,9 +162,13 @@ describe("withdraw", () => {
     )
     // assume wallet POSTed the payment details.
     const confirmButton = await screen.findByText("confirm withdrawal", 
{exact: false})
+    /**
+     * Not expecting a new withdrawal possibility while one is being processed.
+     */
     await waitFor(() => expect(
       screen.queryByText("charge taler wallet", {exact: 
false})).not.toBeInTheDocument());
     fetch.once("{}")
+    // Confirm currently processed withdrawal.
     fireEvent.click(confirmButton);
     /**
      * After having confirmed above, wait that the
@@ -161,15 +192,23 @@ describe("withdraw", () => {
     await screen.findByText("withdrawal confirmed", {exact: false})
 
     /**
-     * Click on a "return to homepage" button, and check that
-     * the withdrawal confirmation is gone, and the option to
-     * withdraw again reappeared.
+     * Click on a "return to homepage / close" button, and
+     * check that the withdrawal confirmation is gone, and
+     * the option to withdraw again reappeared.
      */
     const closeButton = await screen.findByText("close", {exact: false})
     fireEvent.click(closeButton);
+
+    /**
+     * After closing the operation, the confirmation message is not expected.
+     */
     await waitFor(() => expect(
       screen.queryByText("withdrawal confirmed", {exact: 
false})).not.toBeInTheDocument()
     );
+
+    /**
+     * After closing the operation, the possibility to withdraw again should 
be offered.
+     */
     await waitFor(() => expect(
       screen.queryByText(
         "charge taler wallet",
@@ -275,7 +314,7 @@ describe("home page", () => {
       "http://localhost/demobanks/default/access-api/testing/register";,
       expect.anything() // no need to match auth headers.
     )
-    expect(fetch).toHaveBeenLastCalledWith(
+    expect(fetch).toHaveBeenCalledWith(
       `http://localhost/demobanks/default/access-api/accounts/${username}`,
       expect.anything() // no need to match auth headers.
     )

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