From 5d5b63416bbbf8bfefe5ee8d5a5adfdc8c361e31 Mon Sep 17 00:00:00 2001 From: Sebastian Date: Wed, 7 Dec 2022 11:22:21 -0300 Subject: [PATCH] no-fix: moving out public histories page --- packages/demobank-ui/src/pages/Routing.tsx | 7 +- .../demobank-ui/src/pages/home/BankFrame.tsx | 201 +++++++++ .../src/pages/home/PublicHistoriesPage.tsx | 187 ++++++++ .../src/pages/home/Transactions.tsx | 82 ++++ packages/demobank-ui/src/pages/home/index.tsx | 421 +----------------- 5 files changed, 475 insertions(+), 423 deletions(-) create mode 100644 packages/demobank-ui/src/pages/home/BankFrame.tsx create mode 100644 packages/demobank-ui/src/pages/home/PublicHistoriesPage.tsx create mode 100644 packages/demobank-ui/src/pages/home/Transactions.tsx diff --git a/packages/demobank-ui/src/pages/Routing.tsx b/packages/demobank-ui/src/pages/Routing.tsx index cfc6a4bd8..ed9bb2cb3 100644 --- a/packages/demobank-ui/src/pages/Routing.tsx +++ b/packages/demobank-ui/src/pages/Routing.tsx @@ -2,11 +2,8 @@ import { createHashHistory } from "history"; import { h, VNode } from "preact"; import Router, { route, Route } from "preact-router"; import { useEffect } from "preact/hooks"; -import { - AccountPage, - PublicHistoriesPage, - RegistrationPage, -} from "./home/index.js"; +import { AccountPage, RegistrationPage } from "./home/index.js"; +import { PublicHistoriesPage } from "./home/PublicHistoriesPage.js"; export function Routing(): VNode { const history = createHashHistory(); diff --git a/packages/demobank-ui/src/pages/home/BankFrame.tsx b/packages/demobank-ui/src/pages/home/BankFrame.tsx new file mode 100644 index 000000000..9b4bc4567 --- /dev/null +++ b/packages/demobank-ui/src/pages/home/BankFrame.tsx @@ -0,0 +1,201 @@ +/* + This file is part of GNU Taler + (C) 2022 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 + */ + +import { Fragment, h, VNode } from "preact"; +import { PageStateType, usePageContext } from "../../context/pageState.js"; +import { useTranslationContext } from "../../context/translation.js"; +import { bankUiSettings } from "../../settings.js"; +import { LangSelectorLikePy as LangSelector } from "../../components/menu/LangSelector.js"; +import { StateUpdater } from "preact/hooks"; +import talerLogo from "../../assets/logo-white.svg"; + +export function BankFrame(Props: any): VNode { + const { i18n } = useTranslationContext(); + const { pageState, pageStateSetter } = usePageContext(); + console.log("BankFrame state", pageState); + const logOut = ( + + ); + + const demo_sites = []; + for (const i in bankUiSettings.demoSites) + demo_sites.push( + + {bankUiSettings.demoSites[i][0]} + , + ); + + return ( + +
+ +
+

+ + {bankUiSettings.bankName} + +

+ {maybeDemoContent( +

+ + This part of the demo shows how a bank that supports Taler + directly would work. In addition to using your own bank account, + you can also see the transaction history of some{" "} + + Public Accounts + + . + +

, + )} +
+ + {i18n.str`Taler + +
+ +
+ + + {pageState.isLoggedIn ? logOut : null} + {Props.children} +
+ +
+ ); +} + +function maybeDemoContent(content: VNode): VNode { + if (bankUiSettings.showDemoNav) { + return content; + } + return ; +} + +/** + * Bring the state to show the public accounts page. + */ +function goPublicAccounts(pageStateSetter: StateUpdater) { + return () => + pageStateSetter((prevState) => ({ + ...prevState, + showPublicHistories: true, + })); +} + +function ErrorBanner(Props: any): VNode | null { + const [pageState, pageStateSetter] = Props.pageState; + // const { i18n } = useTranslationContext(); + if (!pageState.error) return null; + + const rval = ( +
+
+

+ {pageState.error.title} +

+
+ { + pageStateSetter((prev: any) => ({ ...prev, error: undefined })); + }} + /> +
+
+

{pageState.error.description}

+
+ ); + delete pageState.error; + return rval; +} + +function StatusBanner(Props: any): VNode | null { + const [pageState, pageStateSetter] = Props.pageState; + if (!pageState.info) return null; + + const rval = ( +
+
+

+ {pageState.info} +

+
+ { + pageStateSetter((prev: any) => ({ ...prev, info: undefined })); + }} + /> +
+
+
+ ); + return rval; +} diff --git a/packages/demobank-ui/src/pages/home/PublicHistoriesPage.tsx b/packages/demobank-ui/src/pages/home/PublicHistoriesPage.tsx new file mode 100644 index 000000000..216808e5c --- /dev/null +++ b/packages/demobank-ui/src/pages/home/PublicHistoriesPage.tsx @@ -0,0 +1,187 @@ +/* + This file is part of GNU Taler + (C) 2022 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 + */ + +import { hooks } from "@gnu-taler/web-util/lib/index.browser"; +import { Fragment, h, VNode } from "preact"; +import { StateUpdater } from "preact/hooks"; +import useSWR, { SWRConfig } from "swr"; +import { PageStateType, usePageContext } from "../../context/pageState.js"; +import { useTranslationContext } from "../../context/translation.js"; +import { getBankBackendBaseUrl } from "../../utils.js"; +import { BankFrame } from "./BankFrame.js"; +import { Transactions } from "./Transactions.js"; + +export function PublicHistoriesPage(): VNode { + const { pageState, pageStateSetter } = usePageContext(); + // const { i18n } = useTranslationContext(); + return ( + + + +
+ { + pageStateSetter((prevState: PageStateType) => ({ + ...prevState, + showPublicHistories: false, + })); + }} + > + Go back + +
+
+
+ ); +} + +function SWRWithoutCredentials(Props: any): VNode { + const { baseUrl } = Props; + console.log("Base URL", baseUrl); + return ( + + fetch(baseUrl + url || "").then((r) => { + if (!r.ok) throw { status: r.status, json: r.json() }; + + return r.json(); + }), + }} + > + {Props.children} + + ); +} + +/** + * Show histories of public accounts. + */ +function PublicHistories(Props: any): VNode { + const [showAccount, setShowAccount] = useShowPublicAccount(); + const { data, error } = useSWR("access-api/public-accounts"); + const { i18n } = useTranslationContext(); + + if (typeof error !== "undefined") { + console.log("account error", error); + switch (error.status) { + case 404: + console.log("public accounts: 404", error); + Props.pageStateSetter((prevState: PageStateType) => ({ + ...prevState, + + showPublicHistories: false, + error: { + title: i18n.str`List of public accounts was not found.`, + debug: JSON.stringify(error), + }, + })); + break; + default: + console.log("public accounts: non-404 error", error); + Props.pageStateSetter((prevState: PageStateType) => ({ + ...prevState, + + showPublicHistories: false, + error: { + title: i18n.str`List of public accounts could not be retrieved.`, + debug: JSON.stringify(error), + }, + })); + break; + } + } + if (!data) return

Waiting public accounts list...

; + const txs: any = {}; + const accountsBar = []; + + /** + * Show the account specified in the props, or just one + * from the list if that's not given. + */ + if (typeof showAccount === "undefined" && data.publicAccounts.length > 0) + setShowAccount(data.publicAccounts[1].accountLabel); + console.log(`Public history tab: ${showAccount}`); + + // Ask story of all the public accounts. + for (const account of data.publicAccounts) { + console.log("Asking transactions for", account.accountLabel); + const isSelected = account.accountLabel == showAccount; + accountsBar.push( +
  • + setShowAccount(account.accountLabel)} + > + {account.accountLabel} + +
  • , + ); + txs[account.accountLabel] = ( + + ); + } + + return ( + +

    {i18n.str`History of public accounts`}

    +
    +
    +
    +
      {accountsBar}
    + {typeof showAccount !== "undefined" ? ( + txs[showAccount] + ) : ( +

    No public transactions found.

    + )} + {Props.children} +
    +
    +
    +
    + ); +} + +/** + * Stores in the state a object containing a 'username' + * and 'password' field, in order to avoid losing the + * handle of the data entered by the user in fields. + */ +function useShowPublicAccount( + state?: string, +): [string | undefined, StateUpdater] { + const ret = hooks.useLocalStorage( + "show-public-account", + JSON.stringify(state), + ); + const retObj: string | undefined = ret[0] ? JSON.parse(ret[0]) : ret[0]; + const retSetter: StateUpdater = function (val) { + const newVal = + val instanceof Function + ? JSON.stringify(val(retObj)) + : JSON.stringify(val); + ret[1](newVal); + }; + return [retObj, retSetter]; +} diff --git a/packages/demobank-ui/src/pages/home/Transactions.tsx b/packages/demobank-ui/src/pages/home/Transactions.tsx new file mode 100644 index 000000000..eb344403f --- /dev/null +++ b/packages/demobank-ui/src/pages/home/Transactions.tsx @@ -0,0 +1,82 @@ +import { h, VNode } from "preact"; +import { useEffect } from "preact/hooks"; +import useSWR from "swr"; +import { useTranslationContext } from "../../context/translation.js"; + +/** + * Show one page of transactions. + */ +export function Transactions({ + pageNumber, + accountLabel, + balanceValue, +}: any): VNode { + const { i18n } = useTranslationContext(); + const { data, error, mutate } = useSWR( + `access-api/accounts/${accountLabel}/transactions?page=${pageNumber}`, + ); + useEffect(() => { + mutate(); + }, [balanceValue]); + if (typeof error !== "undefined") { + console.log("transactions not found error", error); + switch (error.status) { + case 404: { + return

    Transactions page {pageNumber} was not found.

    ; + } + case 401: { + return

    Wrong credentials given.

    ; + } + default: { + return

    Transaction page {pageNumber} could not be retrieved.

    ; + } + } + } + if (!data) { + console.log(`History data of ${accountLabel} not arrived`); + return

    Transactions page loading...

    ; + } + console.log(`History data of ${accountLabel}`, data); + return ( +
    + + + + + + + + + + + {data.transactions.map((item: any, idx: number) => { + const sign = item.direction == "DBIT" ? "-" : ""; + const counterpart = + item.direction == "DBIT" ? item.creditorIban : item.debtorIban; + // Pattern: + // + // DD/MM YYYY subject -5 EUR + // DD/MM YYYY subject 5 EUR + const dateRegex = /^([0-9]{4})-([0-9]{2})-([0-9]{1,2})/; + const dateParse = dateRegex.exec(item.date); + const date = + dateParse !== null + ? `${dateParse[3]}/${dateParse[2]} ${dateParse[1]}` + : "date not found"; + return ( + + + + + + + ); + })} + +
    {i18n.str`Date`}{i18n.str`Amount`}{i18n.str`Counterpart`}{i18n.str`Subject`}
    {date} + {sign} + {item.amount} {item.currency} + {counterpart}{item.subject}
    +
    + ); +} diff --git a/packages/demobank-ui/src/pages/home/index.tsx b/packages/demobank-ui/src/pages/home/index.tsx index 64ceedf66..e05ace2e0 100644 --- a/packages/demobank-ui/src/pages/home/index.tsx +++ b/packages/demobank-ui/src/pages/home/index.tsx @@ -20,11 +20,8 @@ import useSWR, { SWRConfig, useSWRConfig } from "swr"; import { Amounts, HttpStatusCode, parsePaytoUri } from "@gnu-taler/taler-util"; import { hooks } from "@gnu-taler/web-util/lib/index.browser"; -import { createHashHistory } from "history"; -import Router, { Route, route } from "preact-router"; +import { route } from "preact-router"; import { StateUpdater, useEffect, useRef, useState } from "preact/hooks"; -import talerLogo from "../../assets/logo-white.svg"; -import { LangSelectorLikePy as LangSelector } from "../../components/menu/LangSelector.js"; import { PageStateType, usePageContext } from "../../context/pageState.js"; import { useTranslationContext } from "../../context/translation.js"; import { BackendStateType, useBackendState } from "../../hooks/backend.js"; @@ -35,6 +32,8 @@ import { getIbanFromPayto, validateAmount, } from "../../utils.js"; +import { BankFrame } from "./BankFrame.js"; +import { Transactions } from "./Transactions.js"; /** * FIXME: @@ -57,24 +56,6 @@ import { * Helpers. * ***********/ -function maybeDemoContent(content: VNode): VNode { - if (bankUiSettings.showDemoNav) { - return content; - } - return ; -} - -/** - * Bring the state to show the public accounts page. - */ -function goPublicAccounts(pageStateSetter: StateUpdater) { - return () => - pageStateSetter((prevState) => ({ - ...prevState, - showPublicHistories: true, - })); -} - /** * Get username from the backend state, and throw * exception if not found. @@ -145,29 +126,6 @@ function prepareHeaders(username?: string, password?: string): Headers { * State managers. * ******************/ -/** - * Stores in the state a object containing a 'username' - * and 'password' field, in order to avoid losing the - * handle of the data entered by the user in fields. - */ -function useShowPublicAccount( - state?: string, -): [string | undefined, StateUpdater] { - const ret = hooks.useLocalStorage( - "show-public-account", - JSON.stringify(state), - ); - const retObj: string | undefined = ret[0] ? JSON.parse(ret[0]) : ret[0]; - const retSetter: StateUpdater = function (val) { - const newVal = - val instanceof Function - ? JSON.stringify(val(retObj)) - : JSON.stringify(val); - ret[1](newVal); - }; - return [retObj, retSetter]; -} - /** * Stores the raw Payto value entered by the user in the state. */ @@ -689,165 +647,6 @@ async function registrationCall( * Functional components. * *************************/ -function ErrorBanner(Props: any): VNode | null { - const [pageState, pageStateSetter] = Props.pageState; - // const { i18n } = useTranslationContext(); - if (!pageState.error) return null; - - const rval = ( -
    -
    -

    - {pageState.error.title} -

    -
    - { - pageStateSetter((prev: any) => ({ ...prev, error: undefined })); - }} - /> -
    -
    -

    {pageState.error.description}

    -
    - ); - delete pageState.error; - return rval; -} - -function StatusBanner(Props: any): VNode | null { - const [pageState, pageStateSetter] = Props.pageState; - if (!pageState.info) return null; - - const rval = ( -
    -
    -

    - {pageState.info} -

    -
    - { - pageStateSetter((prev: any) => ({ ...prev, info: undefined })); - }} - /> -
    -
    -
    - ); - return rval; -} - -function BankFrame(Props: any): VNode { - const { i18n } = useTranslationContext(); - const { pageState, pageStateSetter } = usePageContext(); - console.log("BankFrame state", pageState); - const logOut = ( - - ); - - const demo_sites = []; - for (const i in bankUiSettings.demoSites) - demo_sites.push( - - {bankUiSettings.demoSites[i][0]} - , - ); - - return ( - -
    - -
    -

    - - {bankUiSettings.bankName} - -

    - {maybeDemoContent( -

    - - This part of the demo shows how a bank that supports Taler - directly would work. In addition to using your own bank account, - you can also see the transaction history of some{" "} - - Public Accounts - - . - -

    , - )} -
    - - {i18n.str`Taler - -
    - -
    - - - {pageState.isLoggedIn ? logOut : null} - {Props.children} -
    - -
    - ); -} function ShowInputErrorLabel({ isDirty, message, @@ -1685,81 +1484,6 @@ function RegistrationForm(): VNode { ); } -/** - * Show one page of transactions. - */ -function Transactions(Props: any): VNode { - const { pageNumber, accountLabel, balanceValue } = Props; - const { i18n } = useTranslationContext(); - const { data, error, mutate } = useSWR( - `access-api/accounts/${accountLabel}/transactions?page=${pageNumber}`, - ); - useEffect(() => { - mutate(); - }, [balanceValue]); - if (typeof error !== "undefined") { - console.log("transactions not found error", error); - switch (error.status) { - case 404: { - return

    Transactions page {pageNumber} was not found.

    ; - } - case 401: { - return

    Wrong credentials given.

    ; - } - default: { - return

    Transaction page {pageNumber} could not be retrieved.

    ; - } - } - } - if (!data) { - console.log(`History data of ${accountLabel} not arrived`); - return

    Transactions page loading...

    ; - } - console.log(`History data of ${accountLabel}`, data); - return ( -
    - - - - - - - - - - - {data.transactions.map((item: any, idx: number) => { - const sign = item.direction == "DBIT" ? "-" : ""; - const counterpart = - item.direction == "DBIT" ? item.creditorIban : item.debtorIban; - // Pattern: - // - // DD/MM YYYY subject -5 EUR - // DD/MM YYYY subject 5 EUR - const dateRegex = /^([0-9]{4})-([0-9]{2})-([0-9]{1,2})/; - const dateParse = dateRegex.exec(item.date); - const date = - dateParse !== null - ? `${dateParse[3]}/${dateParse[2]} ${dateParse[1]}` - : "date not found"; - return ( - - - - - - - ); - })} - -
    {i18n.str`Date`}{i18n.str`Amount`}{i18n.str`Counterpart`}{i18n.str`Subject`}
    {date} - {sign} - {item.amount} {item.currency} - {counterpart}{item.subject}
    -
    - ); -} - /** * Show only the account's balance. NOTE: the backend state * is mostly needed to provide the user's credentials to POST @@ -1965,145 +1689,6 @@ function SWRWithCredentials(props: any): VNode { ); } -function SWRWithoutCredentials(Props: any): VNode { - const { baseUrl } = Props; - console.log("Base URL", baseUrl); - return ( - - fetch(baseUrl + url || "").then((r) => { - if (!r.ok) throw { status: r.status, json: r.json() }; - - return r.json(); - }), - }} - > - {Props.children} - - ); -} - -/** - * Show histories of public accounts. - */ -function PublicHistories(Props: any): VNode { - const [showAccount, setShowAccount] = useShowPublicAccount(); - const { data, error } = useSWR("access-api/public-accounts"); - const { i18n } = useTranslationContext(); - - if (typeof error !== "undefined") { - console.log("account error", error); - switch (error.status) { - case 404: - console.log("public accounts: 404", error); - Props.pageStateSetter((prevState: PageStateType) => ({ - ...prevState, - - showPublicHistories: false, - error: { - title: i18n.str`List of public accounts was not found.`, - debug: JSON.stringify(error), - }, - })); - break; - default: - console.log("public accounts: non-404 error", error); - Props.pageStateSetter((prevState: PageStateType) => ({ - ...prevState, - - showPublicHistories: false, - error: { - title: i18n.str`List of public accounts could not be retrieved.`, - debug: JSON.stringify(error), - }, - })); - break; - } - } - if (!data) return

    Waiting public accounts list...

    ; - const txs: any = {}; - const accountsBar = []; - - /** - * Show the account specified in the props, or just one - * from the list if that's not given. - */ - if (typeof showAccount === "undefined" && data.publicAccounts.length > 0) - setShowAccount(data.publicAccounts[1].accountLabel); - console.log(`Public history tab: ${showAccount}`); - - // Ask story of all the public accounts. - for (const account of data.publicAccounts) { - console.log("Asking transactions for", account.accountLabel); - const isSelected = account.accountLabel == showAccount; - accountsBar.push( -
  • - setShowAccount(account.accountLabel)} - > - {account.accountLabel} - -
  • , - ); - txs[account.accountLabel] = ( - - ); - } - - return ( - -

    {i18n.str`History of public accounts`}

    -
    -
    -
    -
      {accountsBar}
    - {typeof showAccount !== "undefined" ? ( - txs[showAccount] - ) : ( -

    No public transactions found.

    - )} - {Props.children} -
    -
    -
    -
    - ); -} - -export function PublicHistoriesPage(): VNode { - const { pageState, pageStateSetter } = usePageContext(); - // const { i18n } = useTranslationContext(); - return ( - - - -
    - { - pageStateSetter((prevState: PageStateType) => ({ - ...prevState, - showPublicHistories: false, - })); - }} - > - Go back - -
    -
    -
    - ); -} - export function RegistrationPage(): VNode { const { i18n } = useTranslationContext(); if (!bankUiSettings.allowRegistrations) {