gnunet-svn
[Top][All Lists]
Advanced

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

[taler-wallet-core] branch master updated: admin ui


From: gnunet
Subject: [taler-wallet-core] branch master updated: admin ui
Date: Mon, 23 Oct 2023 12:11:24 +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 66455014e admin ui
66455014e is described below

commit 66455014e08b229ff4d3ee9071fd7c82225cfcd6
Author: Sebastian <sebasjm@gmail.com>
AuthorDate: Mon Oct 23 07:11:18 2023 -0300

    admin ui
---
 packages/demobank-ui/src/components/Routing.tsx    |  11 +-
 packages/demobank-ui/src/hooks/circuit.ts          |  99 +++++++++-
 packages/demobank-ui/src/hooks/settings.ts         |   7 +-
 packages/demobank-ui/src/pages.ts                  |   2 +-
 .../demobank-ui/src/pages/AccountPage/index.ts     |   7 +-
 .../demobank-ui/src/pages/AccountPage/state.ts     |   5 +-
 .../demobank-ui/src/pages/AccountPage/views.tsx    |   2 +-
 packages/demobank-ui/src/pages/LoginForm.tsx       |   2 +-
 .../demobank-ui/src/pages/OperationState/state.ts  |   2 +-
 .../src/pages/PaytoWireTransferForm.tsx            |  12 +-
 .../demobank-ui/src/pages/ProfileNavigation.tsx    |   2 +-
 packages/demobank-ui/src/pages/QrCodeSection.tsx   |   2 +-
 .../demobank-ui/src/pages/ShowAccountDetails.tsx   |   2 +-
 .../src/pages/UpdateAccountPassword.tsx            |   2 +-
 .../demobank-ui/src/pages/WalletWithdrawForm.tsx   |   4 +-
 .../src/pages/WithdrawalConfirmationQuestion.tsx   |   2 +-
 .../{HomePage.tsx => WithdrawalOperationPage.tsx}  |  43 +----
 .../demobank-ui/src/pages/WithdrawalQRCode.tsx     |   2 +-
 packages/demobank-ui/src/pages/admin/Account.tsx   |   2 +-
 .../demobank-ui/src/pages/admin/AccountForm.tsx    |   2 +-
 .../demobank-ui/src/pages/admin/AccountList.tsx    | 209 +++++++++++----------
 packages/demobank-ui/src/pages/admin/AdminHome.tsx | 176 ++++++++++++++++-
 .../src/pages/admin/CreateNewAccount.tsx           |   2 +-
 .../demobank-ui/src/pages/admin/RemoveAccount.tsx  |   2 +-
 .../src/pages/business/CreateCashout.tsx           |   2 +-
 .../src/pages/business/ShowCashoutDetails.tsx      |   2 +-
 26 files changed, 406 insertions(+), 199 deletions(-)

diff --git a/packages/demobank-ui/src/components/Routing.tsx 
b/packages/demobank-ui/src/components/Routing.tsx
index 1d587fe32..c94e74201 100644
--- a/packages/demobank-ui/src/components/Routing.tsx
+++ b/packages/demobank-ui/src/components/Routing.tsx
@@ -21,7 +21,7 @@ import { Route, Router, route } from "preact-router";
 import { useEffect } from "preact/hooks";
 import { useBackendState } from "../hooks/backend.js";
 import { BankFrame } from "../pages/BankFrame.js";
-import { HomePage, WithdrawalOperationPage } from "../pages/HomePage.js";
+import { WithdrawalOperationPage } from "../pages/WithdrawalOperationPage.js";
 import { LoginForm } from "../pages/LoginForm.js";
 import { PublicHistoriesPage } from "../pages/PublicHistoriesPage.js";
 import { RegistrationPage } from "../pages/RegistrationPage.js";
@@ -35,6 +35,7 @@ import { CreateNewAccount } from 
"../pages/admin/CreateNewAccount.js";
 import { CashoutListForAccount } from 
"../pages/admin/CashoutListForAccount.js";
 import { ShowCashoutDetails } from "../pages/business/ShowCashoutDetails.js";
 import { WireTransfer } from "../pages/admin/Account.js";
+import { AccountPage } from "../pages/AccountPage/index.js";
 
 export function Routing(): VNode {
   const history = createHashHistory();
@@ -301,17 +302,11 @@ export function Routing(): VNode {
                 }}
               />;
             } else {
-              return <HomePage
+              return <AccountPage
                 account={username}
                 goToConfirmOperation={(wopid) => {
                   route(`/operation/${wopid}`);
                 }}
-                goToBusinessAccount={() => {
-                  route("/business");
-                }}
-                onRegister={() => {
-                  route("/register");
-                }}
               />
             }
           }}
diff --git a/packages/demobank-ui/src/hooks/circuit.ts 
b/packages/demobank-ui/src/hooks/circuit.ts
index 06e068d6d..fc17c0184 100644
--- a/packages/demobank-ui/src/hooks/circuit.ts
+++ b/packages/demobank-ui/src/hooks/circuit.ts
@@ -18,10 +18,11 @@ import { useState } from "preact/hooks";
 import { MAX_RESULT_SIZE, PAGE_SIZE } from "../utils.js";
 import { useBackendState } from "./backend.js";
 
-import { AccessToken, AmountJson, Amounts, OperationOk, 
TalerCoreBankResultByMethod, TalerCorebankApi, TalerError, TalerHttpError } 
from "@gnu-taler/taler-util";
+import { AccessToken, AmountJson, AmountString, Amounts, OperationOk, 
TalerCoreBankResultByMethod, TalerCorebankApi, TalerError, TalerHttpError } 
from "@gnu-taler/taler-util";
 import _useSWR, { SWRHook } from "swr";
 import { useBankCoreApiContext } from "../context/config.js";
-import { assertUnreachable } from "../pages/HomePage.js";
+import { assertUnreachable } from "../pages/WithdrawalOperationPage.js";
+import { format, getDate, getDay, getHours, getMonth, getYear, set, sub } from 
"date-fns";
 
 // FIX default import https://github.com/microsoft/TypeScript/issues/49189
 const useSWR = _useSWR as unknown as SWRHook;
@@ -121,11 +122,6 @@ export function useRatiosAndFeeConfig() {
   return undefined;
 }
 
-interface PaginationFilter {
-  account?: string;
-  page?: number;
-}
-
 export function useBusinessAccounts() {
   const { state: credentials } = useBackendState();
   const token = credentials.status !== "loggedIn" ? undefined : 
credentials.token
@@ -250,3 +246,92 @@ export function useCashoutDetails(cashoutId: string) {
   if (error) return error;
   return undefined;
 }
+export type MonitorMetrics = {
+  lastHour: TalerCoreBankResultByMethod<"getMonitor">,
+  lastDay: TalerCoreBankResultByMethod<"getMonitor">,
+  lastMonth: TalerCoreBankResultByMethod<"getMonitor">,
+}
+
+function getTimeframesForDate(time: Date, timeframe: 
TalerCorebankApi.MonitorTimeframeParam): { current: number, previous: number } {
+  switch (timeframe) {
+    case TalerCorebankApi.MonitorTimeframeParam.hour: return {
+      current: getHours(sub(time, { hours: 1 })),
+      previous: getHours(sub(time, { hours: 2 }))
+    }
+    case TalerCorebankApi.MonitorTimeframeParam.day: return {
+      current: getDate(sub(time, { days: 1 })),
+      previous: getDate(sub(time, { days: 2 }))
+    }
+    case TalerCorebankApi.MonitorTimeframeParam.month: return {
+      current: getMonth(sub(time, { months: 1 })),
+      previous: getMonth(sub(time, { months: 2 }))
+    }
+    case TalerCorebankApi.MonitorTimeframeParam.year: return {
+      current: getYear(sub(time, { years: 1 })),
+      previous: getYear(sub(time, { years: 2 }))
+    }
+    case TalerCorebankApi.MonitorTimeframeParam.decade: return {
+      current: getYear(sub(time, { years: 10 })),
+      previous: getYear(sub(time, { years: 20 }))
+    }
+    default: assertUnreachable(timeframe)
+  }
+}
+
+export type LastMonitor = { current: 
TalerCoreBankResultByMethod<"getMonitor">, previous: 
TalerCoreBankResultByMethod<"getMonitor"> }
+export function useLastMonitorInfo(time: Date, timeframe: 
TalerCorebankApi.MonitorTimeframeParam) {
+  const { api, config } = useBankCoreApiContext();
+
+  async function fetcher() {
+    const params = getTimeframesForDate(time, timeframe)
+    // const resp = await Promise.all([
+    //   api.getMonitor({ timeframe, which: params.current }),
+    //   api.getMonitor({ timeframe, which: params.previous }),
+    // ])
+    const current: TalerCoreBankResultByMethod<"getMonitor"> = {
+      type: "ok" as const,
+      body: {
+        cashinCount: 1,
+        cashinExternalVolume: "LOCAL:1234" as AmountString,
+        cashoutCount: 2,
+        cashoutExternalVolume: "LOCAL:2345" as AmountString,
+        talerPayoutCount: 3,
+        talerPayoutInternalVolume: "LOCAL:3456" as AmountString,
+      }
+    }
+
+    const previous = {
+      type: "ok" as const,
+      body: {
+        cashinCount: 1,
+        cashinExternalVolume: "LOCAL:2345" as AmountString,
+        cashoutCount: 2,
+        cashoutExternalVolume: "LOCAL:2345" as AmountString,
+        talerPayoutCount: 3,
+        talerPayoutInternalVolume: "LOCAL:3456" as AmountString,
+      }
+
+    }
+    return {
+      current,
+      previous,
+    }
+  }
+
+  const { data, error } = useSWR<LastMonitor, TalerHttpError>(
+    config.have_cashout || true ? ["useLastMonitorInfo"] : false, fetcher, {
+    refreshInterval: 0,
+    refreshWhenHidden: false,
+    revalidateOnFocus: false,
+    revalidateOnReconnect: false,
+    refreshWhenOffline: false,
+    errorRetryCount: 0,
+    errorRetryInterval: 1,
+    shouldRetryOnError: false,
+    keepPreviousData: true,
+  });
+
+  if (data) return data;
+  if (error) return error;
+  return undefined;
+}
diff --git a/packages/demobank-ui/src/hooks/settings.ts 
b/packages/demobank-ui/src/hooks/settings.ts
index cfc3b6a5b..ca2d131f2 100644
--- a/packages/demobank-ui/src/hooks/settings.ts
+++ b/packages/demobank-ui/src/hooks/settings.ts
@@ -15,18 +15,15 @@
  */
 
 import {
-  AmountString,
   Codec,
   TranslatedString,
   buildCodecForObject,
-  codecForAmountString,
   codecForBoolean,
   codecForNumber,
   codecForString,
-  codecOptional,
+  codecOptional
 } from "@gnu-taler/taler-util";
-import { buildStorageKey, useLocalStorage } from "@gnu-taler/web-util/browser";
-import { useTranslationContext } from "@gnu-taler/web-util/browser";
+import { buildStorageKey, useLocalStorage, useTranslationContext } from 
"@gnu-taler/web-util/browser";
 
 interface Settings {
   currentWithdrawalOperationId: string | undefined;
diff --git a/packages/demobank-ui/src/pages.ts 
b/packages/demobank-ui/src/pages.ts
index c78240a02..cf003fde5 100644
--- a/packages/demobank-ui/src/pages.ts
+++ b/packages/demobank-ui/src/pages.ts
@@ -1,4 +1,4 @@
-import { WithdrawalOperationPage } from "./pages/HomePage.js";
+import { WithdrawalOperationPage } from "./pages/WithdrawalOperationPage.js";
 import { PageEntry, pageDefinition } from "./route.js";
 
 // const operationById: PageEntry<{ operationId: string }> = {
diff --git a/packages/demobank-ui/src/pages/AccountPage/index.ts 
b/packages/demobank-ui/src/pages/AccountPage/index.ts
index ef6b4fede..87ed878b0 100644
--- a/packages/demobank-ui/src/pages/AccountPage/index.ts
+++ b/packages/demobank-ui/src/pages/AccountPage/index.ts
@@ -14,9 +14,8 @@
  GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 
-import { AbsoluteTime, AmountJson, TalerCorebankApi, TalerError, 
TalerErrorDetail } from "@gnu-taler/taler-util";
-import { HttpResponsePaginated, utils } from "@gnu-taler/web-util/browser";
-import { VNode } from "preact";
+import { AbsoluteTime, AmountJson, TalerCorebankApi, TalerError } from 
"@gnu-taler/taler-util";
+import { utils } from "@gnu-taler/web-util/browser";
 import { ErrorLoading } from "../../components/ErrorLoading.js";
 import { Loading } from "../../components/Loading.js";
 import { LoginForm } from "../LoginForm.js";
@@ -25,7 +24,6 @@ import { InvalidIbanView, ReadyView } from "./views.js";
 
 export interface Props {
   account: string;
-  goToBusinessAccount: () => void;
   goToConfirmOperation: (id: string) => void;
 }
 
@@ -51,7 +49,6 @@ export namespace State {
     error: undefined;
     account: string,
     limit: AmountJson,
-    goToBusinessAccount: () => void;
     goToConfirmOperation: (id: string) => void;
   }
 
diff --git a/packages/demobank-ui/src/pages/AccountPage/state.ts 
b/packages/demobank-ui/src/pages/AccountPage/state.ts
index 96d45b7bd..793593f0d 100644
--- a/packages/demobank-ui/src/pages/AccountPage/state.ts
+++ b/packages/demobank-ui/src/pages/AccountPage/state.ts
@@ -18,9 +18,9 @@ import { Amounts, HttpStatusCode, TalerError, TalerErrorCode, 
parsePaytoUri } fr
 import { ErrorType, notifyError, useTranslationContext } from 
"@gnu-taler/web-util/browser";
 import { useAccountDetails } from "../../hooks/access.js";
 import { Props, State } from "./index.js";
-import { assertUnreachable } from "../HomePage.js";
+import { assertUnreachable } from "../WithdrawalOperationPage.js";
 
-export function useComponentState({ account, goToBusinessAccount, 
goToConfirmOperation }: Props): State {
+export function useComponentState({ account, goToConfirmOperation }: Props): 
State {
   const result = useAccountDetails(account);
   const { i18n } = useTranslationContext();
 
@@ -76,7 +76,6 @@ export function useComponentState({ account, 
goToBusinessAccount, goToConfirmOpe
 
   return {
     status: "ready",
-    goToBusinessAccount,
     goToConfirmOperation,
     error: undefined,
     account,
diff --git a/packages/demobank-ui/src/pages/AccountPage/views.tsx 
b/packages/demobank-ui/src/pages/AccountPage/views.tsx
index 00643ec3e..8fff37624 100644
--- a/packages/demobank-ui/src/pages/AccountPage/views.tsx
+++ b/packages/demobank-ui/src/pages/AccountPage/views.tsx
@@ -53,7 +53,7 @@ function ShowDemoInfo(): VNode {
   </Attention>
 }
 
-export function ReadyView({ account, limit, goToBusinessAccount, 
goToConfirmOperation }: State.Ready): VNode<{}> {
+export function ReadyView({ account, limit, goToConfirmOperation }: 
State.Ready): VNode<{}> {
 
   return <Fragment>
     <ShowDemoInfo />
diff --git a/packages/demobank-ui/src/pages/LoginForm.tsx 
b/packages/demobank-ui/src/pages/LoginForm.tsx
index 6d1d35288..b18f29d86 100644
--- a/packages/demobank-ui/src/pages/LoginForm.tsx
+++ b/packages/demobank-ui/src/pages/LoginForm.tsx
@@ -23,7 +23,7 @@ import { useBankCoreApiContext } from "../context/config.js";
 import { useBackendState } from "../hooks/backend.js";
 import { bankUiSettings } from "../settings.js";
 import { undefinedIfEmpty, withRuntimeErrorHandling } from "../utils.js";
-import { assertUnreachable } from "./HomePage.js";
+import { assertUnreachable } from "./WithdrawalOperationPage.js";
 import { doAutoFocus } from "./PaytoWireTransferForm.js";
 
 
diff --git a/packages/demobank-ui/src/pages/OperationState/state.ts 
b/packages/demobank-ui/src/pages/OperationState/state.ts
index 9e34a846b..136a2b505 100644
--- a/packages/demobank-ui/src/pages/OperationState/state.ts
+++ b/packages/demobank-ui/src/pages/OperationState/state.ts
@@ -23,7 +23,7 @@ import { useBackendState } from "../../hooks/backend.js";
 import { useSettings } from "../../hooks/settings.js";
 import { buildRequestErrorMessage, withRuntimeErrorHandling } from 
"../../utils.js";
 import { Props, State } from "./index.js";
-import { assertUnreachable } from "../HomePage.js";
+import { assertUnreachable } from "../WithdrawalOperationPage.js";
 import { mutate } from "swr";
 
 export function useComponentState({ currency, onClose }: Props): 
utils.RecursiveState<State> {
diff --git a/packages/demobank-ui/src/pages/PaytoWireTransferForm.tsx 
b/packages/demobank-ui/src/pages/PaytoWireTransferForm.tsx
index d859c10d7..63cb3e865 100644
--- a/packages/demobank-ui/src/pages/PaytoWireTransferForm.tsx
+++ b/packages/demobank-ui/src/pages/PaytoWireTransferForm.tsx
@@ -43,7 +43,7 @@ import {
 } from "../utils.js";
 import { useBankCoreApiContext } from "../context/config.js";
 import { useBackendState } from "../hooks/backend.js";
-import { assertUnreachable } from "./HomePage.js";
+import { assertUnreachable } from "./WithdrawalOperationPage.js";
 import { mutate } from "swr";
 
 const logger = new Logger("PaytoWireTransferForm");
@@ -128,7 +128,7 @@ export function PaytoWireTransferForm({
     if (rawPaytoInput) {
       const p = parsePaytoUri(rawPaytoInput)
       if (!p) return;
-      sendingAmount = p.params.amount
+      sendingAmount = p.params.amount as AmountString
       delete p.params.amount
       //if this payto is valid then it already have message
       payto_uri = stringifyPaytoUri(p)
@@ -137,7 +137,7 @@ export function PaytoWireTransferForm({
       const ibanPayto = buildPayto("iban", iban, undefined);
       ibanPayto.params.message = encodeURIComponent(subject);
       payto_uri = stringifyPaytoUri(ibanPayto);
-      sendingAmount = `${limit.currency}:${trimmedAmountStr}`
+      sendingAmount = `${limit.currency}:${trimmedAmountStr}` as AmountString
     }
     const puri = payto_uri;
 
@@ -446,7 +446,7 @@ export function InputAmount(
   );
 }
 
-export function RenderAmount({ value, negative }: { value: AmountJson, 
negative?: boolean }): VNode {
+export function RenderAmount({ value, negative, noCurrency }: { value: 
AmountJson, negative?: boolean, noCurrency?: boolean }): VNode {
   const { config } = useBankCoreApiContext()
   const str = Amounts.stringifyValue(value)
   const sep_pos = str.indexOf(FRAC_SEPARATOR)
@@ -456,11 +456,11 @@ export function RenderAmount({ value, negative }: { 
value: AmountJson, negative?
     const small = str.substring(limit)
     return <span class="whitespace-nowrap">
       {negative ? "-" : undefined}
-      {value.currency} {normal} <sup class="-ml-2">{small}</sup>
+      {noCurrency ? undefined : value.currency} {normal} <sup 
class="-ml-2">{small}</sup>
     </span>
   }
   return <span class="whitespace-nowrap">
     {negative ? "-" : undefined}
-    {value.currency} {str}
+    {noCurrency ? undefined : value.currency} {str}
   </span>
 }
\ No newline at end of file
diff --git a/packages/demobank-ui/src/pages/ProfileNavigation.tsx 
b/packages/demobank-ui/src/pages/ProfileNavigation.tsx
index c061c9742..20a1ececd 100644
--- a/packages/demobank-ui/src/pages/ProfileNavigation.tsx
+++ b/packages/demobank-ui/src/pages/ProfileNavigation.tsx
@@ -1,7 +1,7 @@
 import { useTranslationContext } from "@gnu-taler/web-util/browser";
 import { Fragment, VNode, h } from "preact";
 import { useBankCoreApiContext } from "../context/config.js";
-import { assertUnreachable } from "./HomePage.js";
+import { assertUnreachable } from "./WithdrawalOperationPage.js";
 
 export function ProfileNavigation({ current }: { current: "details" | 
"credentials" | "cashouts" }): VNode {
   const { i18n } = useTranslationContext()
diff --git a/packages/demobank-ui/src/pages/QrCodeSection.tsx 
b/packages/demobank-ui/src/pages/QrCodeSection.tsx
index 64f9ec5ab..8948827aa 100644
--- a/packages/demobank-ui/src/pages/QrCodeSection.tsx
+++ b/packages/demobank-ui/src/pages/QrCodeSection.tsx
@@ -32,7 +32,7 @@ import { useEffect } from "preact/hooks";
 import { QR } from "../components/QR.js";
 import { buildRequestErrorMessage, withRuntimeErrorHandling } from 
"../utils.js";
 import { useBankCoreApiContext } from "../context/config.js";
-import { assertUnreachable } from "./HomePage.js";
+import { assertUnreachable } from "./WithdrawalOperationPage.js";
 
 export function QrCodeSection({
   withdrawUri,
diff --git a/packages/demobank-ui/src/pages/ShowAccountDetails.tsx 
b/packages/demobank-ui/src/pages/ShowAccountDetails.tsx
index 21724474a..74346985a 100644
--- a/packages/demobank-ui/src/pages/ShowAccountDetails.tsx
+++ b/packages/demobank-ui/src/pages/ShowAccountDetails.tsx
@@ -8,7 +8,7 @@ import { useBankCoreApiContext } from "../context/config.js";
 import { useAccountDetails } from "../hooks/access.js";
 import { useBackendState } from "../hooks/backend.js";
 import { undefinedIfEmpty, withRuntimeErrorHandling } from "../utils.js";
-import { assertUnreachable } from "./HomePage.js";
+import { assertUnreachable } from "./WithdrawalOperationPage.js";
 import { LoginForm } from "./LoginForm.js";
 import { AccountForm } from "./admin/AccountForm.js";
 import { ProfileNavigation } from "./ProfileNavigation.js";
diff --git a/packages/demobank-ui/src/pages/UpdateAccountPassword.tsx 
b/packages/demobank-ui/src/pages/UpdateAccountPassword.tsx
index e3f0de8cc..ef3737e81 100644
--- a/packages/demobank-ui/src/pages/UpdateAccountPassword.tsx
+++ b/packages/demobank-ui/src/pages/UpdateAccountPassword.tsx
@@ -5,7 +5,7 @@ import { ShowInputErrorLabel } from 
"../components/ShowInputErrorLabel.js";
 import { useBankCoreApiContext } from "../context/config.js";
 import { useBackendState } from "../hooks/backend.js";
 import { undefinedIfEmpty, withRuntimeErrorHandling } from "../utils.js";
-import { assertUnreachable } from "./HomePage.js";
+import { assertUnreachable } from "./WithdrawalOperationPage.js";
 import { doAutoFocus } from "./PaytoWireTransferForm.js";
 import { ProfileNavigation } from "./ProfileNavigation.js";
 
diff --git a/packages/demobank-ui/src/pages/WalletWithdrawForm.tsx 
b/packages/demobank-ui/src/pages/WalletWithdrawForm.tsx
index 28d5d7749..f1ff49068 100644
--- a/packages/demobank-ui/src/pages/WalletWithdrawForm.tsx
+++ b/packages/demobank-ui/src/pages/WalletWithdrawForm.tsx
@@ -37,7 +37,7 @@ import { useBankCoreApiContext } from "../context/config.js";
 import { useBackendState } from "../hooks/backend.js";
 import { useSettings } from "../hooks/settings.js";
 import { buildRequestErrorMessage, undefinedIfEmpty, withRuntimeErrorHandling 
} from "../utils.js";
-import { assertUnreachable } from "./HomePage.js";
+import { assertUnreachable } from "./WithdrawalOperationPage.js";
 import { OperationState } from "./OperationState/index.js";
 import { InputAmount, doAutoFocus } from "./PaytoWireTransferForm.js";
 
@@ -62,7 +62,7 @@ function OldWithdrawalForm({ goToConfirmOperation, limit, 
onCancel, focus }: {
 
   if (!!settings.currentWithdrawalOperationId) {
     return <Attention type="warning" title={i18n.str`There is an operation 
already`}>
-      <span ref={focus ? doAutoFocus : undefined}/>
+      <span ref={focus ? doAutoFocus : undefined} />
       <i18n.Translate>
         To complete or cancel the operation click <a class="font-semibold 
text-yellow-700 hover:text-yellow-600" 
href={`#/operation/${settings.currentWithdrawalOperationId}`}>here</a>
       </i18n.Translate>
diff --git a/packages/demobank-ui/src/pages/WithdrawalConfirmationQuestion.tsx 
b/packages/demobank-ui/src/pages/WithdrawalConfirmationQuestion.tsx
index 87637f7ef..895094c28 100644
--- a/packages/demobank-ui/src/pages/WithdrawalConfirmationQuestion.tsx
+++ b/packages/demobank-ui/src/pages/WithdrawalConfirmationQuestion.tsx
@@ -40,7 +40,7 @@ import { buildRequestErrorMessage, undefinedIfEmpty, 
withRuntimeErrorHandling }
 import { useSettings } from "../hooks/settings.js";
 import { RenderAmount } from "./PaytoWireTransferForm.js";
 import { useBankCoreApiContext } from "../context/config.js";
-import { assertUnreachable } from "./HomePage.js";
+import { assertUnreachable } from "./WithdrawalOperationPage.js";
 import { mutate } from "swr";
 
 const logger = new Logger("WithdrawalConfirmationQuestion");
diff --git a/packages/demobank-ui/src/pages/HomePage.tsx 
b/packages/demobank-ui/src/pages/WithdrawalOperationPage.tsx
similarity index 70%
rename from packages/demobank-ui/src/pages/HomePage.tsx
rename to packages/demobank-ui/src/pages/WithdrawalOperationPage.tsx
index bd85cea1e..7ef2a6c39 100644
--- a/packages/demobank-ui/src/pages/HomePage.tsx
+++ b/packages/demobank-ui/src/pages/WithdrawalOperationPage.tsx
@@ -15,62 +15,23 @@
  */
 
 import {
-  HttpStatusCode,
   Logger,
   TranslatedString,
   parseWithdrawUri,
-  stringifyWithdrawUri,
+  stringifyWithdrawUri
 } from "@gnu-taler/taler-util";
 import {
-  ErrorType,
-  HttpResponse,
-  HttpResponsePaginated,
-  notify,
   notifyError,
-  useTranslationContext,
+  useTranslationContext
 } from "@gnu-taler/web-util/browser";
 import { Fragment, VNode, h } from "preact";
 import { Loading } from "../components/Loading.js";
 import { useBankCoreApiContext } from "../context/config.js";
 import { useSettings } from "../hooks/settings.js";
-import { AccountPage } from "./AccountPage/index.js";
-import { LoginForm } from "./LoginForm.js";
 import { WithdrawalQRCode } from "./WithdrawalQRCode.js";
 
 const logger = new Logger("AccountPage");
 
-/**
- * show content based on state:
- * - LoginForm if the user is not logged in
- * - qr code if withdrawal in progress
- * - else account information
- * Use the handler to catch error cases
- *
- * @param param0
- * @returns
- */
-export function HomePage({
-  onRegister,
-  account,
-  goToConfirmOperation,
-  goToBusinessAccount,
-}: {
-  account: string,
-  onRegister: () => void;
-  goToBusinessAccount: () => void;
-  goToConfirmOperation: (id: string) => void;
-}): VNode {
-  const { i18n } = useTranslationContext();
-
-  return (
-    <AccountPage
-      account={account}
-      goToConfirmOperation={goToConfirmOperation}
-      goToBusinessAccount={goToBusinessAccount}
-    />
-  );
-}
-
 export function WithdrawalOperationPage({
   operationId,
   onContinue,
diff --git a/packages/demobank-ui/src/pages/WithdrawalQRCode.tsx 
b/packages/demobank-ui/src/pages/WithdrawalQRCode.tsx
index 51edbc95f..5c300d0ab 100644
--- a/packages/demobank-ui/src/pages/WithdrawalQRCode.tsx
+++ b/packages/demobank-ui/src/pages/WithdrawalQRCode.tsx
@@ -26,7 +26,7 @@ import { Fragment, VNode, h } from "preact";
 import { ErrorLoading } from "../components/ErrorLoading.js";
 import { Loading } from "../components/Loading.js";
 import { useWithdrawalDetails } from "../hooks/access.js";
-import { assertUnreachable } from "./HomePage.js";
+import { assertUnreachable } from "./WithdrawalOperationPage.js";
 import { QrCodeSection } from "./QrCodeSection.js";
 import { WithdrawalConfirmationQuestion } from 
"./WithdrawalConfirmationQuestion.js";
 import { Attention } from "../components/Attention.js";
diff --git a/packages/demobank-ui/src/pages/admin/Account.tsx 
b/packages/demobank-ui/src/pages/admin/Account.tsx
index 103747414..a1e80ccb9 100644
--- a/packages/demobank-ui/src/pages/admin/Account.tsx
+++ b/packages/demobank-ui/src/pages/admin/Account.tsx
@@ -4,7 +4,7 @@ import { Fragment, VNode, h } from "preact";
 import { ErrorLoading } from "../../components/ErrorLoading.js";
 import { Loading } from "../../components/Loading.js";
 import { useAccountDetails } from "../../hooks/access.js";
-import { assertUnreachable } from "../HomePage.js";
+import { assertUnreachable } from "../WithdrawalOperationPage.js";
 import { LoginForm } from "../LoginForm.js";
 import { PaytoWireTransferForm } from "../PaytoWireTransferForm.js";
 import { useBackendState } from "../../hooks/backend.js";
diff --git a/packages/demobank-ui/src/pages/admin/AccountForm.tsx 
b/packages/demobank-ui/src/pages/admin/AccountForm.tsx
index bce089560..410683dcb 100644
--- a/packages/demobank-ui/src/pages/admin/AccountForm.tsx
+++ b/packages/demobank-ui/src/pages/admin/AccountForm.tsx
@@ -6,7 +6,7 @@ import { useTranslationContext } from 
"@gnu-taler/web-util/browser";
 import { TalerCorebankApi, buildPayto, parsePaytoUri } from 
"@gnu-taler/taler-util";
 import { doAutoFocus } from "../PaytoWireTransferForm.js";
 import { CopyButton } from "../../components/CopyButton.js";
-import { assertUnreachable } from "../HomePage.js";
+import { assertUnreachable } from "../WithdrawalOperationPage.js";
 
 const IBAN_REGEX = /^[A-Z][A-Z0-9]*$/;
 const EMAIL_REGEX =
diff --git a/packages/demobank-ui/src/pages/admin/AccountList.tsx 
b/packages/demobank-ui/src/pages/admin/AccountList.tsx
index 39b43b9b1..7d3dd5595 100644
--- a/packages/demobank-ui/src/pages/admin/AccountList.tsx
+++ b/packages/demobank-ui/src/pages/admin/AccountList.tsx
@@ -3,10 +3,10 @@ import { useTranslationContext } from 
"@gnu-taler/web-util/browser";
 import { Fragment, VNode, h } from "preact";
 import { ErrorLoading } from "../../components/ErrorLoading.js";
 import { Loading } from "../../components/Loading.js";
+import { useBankCoreApiContext } from "../../context/config.js";
 import { useBusinessAccounts } from "../../hooks/circuit.js";
-import { assertUnreachable } from "../HomePage.js";
 import { RenderAmount } from "../PaytoWireTransferForm.js";
-import { useBankCoreApiContext } from "../../context/config.js";
+import { assertUnreachable } from "../WithdrawalOperationPage.js";
 
 interface Props {
   onCreateAccount: () => void;
@@ -36,119 +36,122 @@ export function AccountList({ onRemoveAccount, 
onShowAccountDetails, onUpdateAcc
   }
 
   const { accounts } = result.data.body;
-  return <div class="px-4 sm:px-6 lg:px-8">
-    <div class="sm:flex sm:items-center">
-      <div class="sm:flex-auto">
-        <h1 class="text-base font-semibold leading-6 text-gray-900">
-          <i18n.Translate>Accounts</i18n.Translate>
-        </h1>
-        <p class="mt-2 text-sm text-gray-700">
-          <i18n.Translate>A list of all business account in the 
bank.</i18n.Translate>
-        </p>
-      </div>
-      <div class="mt-4 sm:ml-16 sm:mt-0 sm:flex-none">
-        <button type="button" class="block rounded-md bg-indigo-600 px-3 py-2 
text-center text-sm font-semibold text-white shadow-sm hover:bg-indigo-500 
focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 
focus-visible:outline-indigo-600"
-          onClick={(e) => {
-            e.preventDefault()
-            onCreateAccount()
-          }}>
-          <i18n.Translate>Create account</i18n.Translate>
-        </button>
+  return <Fragment>
+    <div class="px-4 sm:px-6 lg:px-8 mt-4">
+      <div class="sm:flex sm:items-center">
+        <div class="sm:flex-auto">
+          <h1 class="text-base font-semibold leading-6 text-gray-900">
+            <i18n.Translate>Accounts</i18n.Translate>
+          </h1>
+          <p class="mt-2 text-sm text-gray-700">
+            <i18n.Translate>A list of all business account in the 
bank.</i18n.Translate>
+          </p>
+        </div>
+        <div class="mt-4 sm:ml-16 sm:mt-0 sm:flex-none">
+          <button type="button" class="block rounded-md bg-indigo-600 px-3 
py-2 text-center text-sm font-semibold text-white shadow-sm hover:bg-indigo-500 
focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 
focus-visible:outline-indigo-600"
+            onClick={(e) => {
+              e.preventDefault()
+              onCreateAccount()
+            }}>
+            <i18n.Translate>Create account</i18n.Translate>
+          </button>
+        </div>
       </div>
-    </div>
-    <div class="mt-8 flow-root">
-      <div class="-mx-4 -my-2 overflow-x-auto sm:-mx-6 lg:-mx-8">
-        <div class="inline-block min-w-full py-2 align-middle sm:px-6 lg:px-8">
-          {!accounts.length ? (
-            <div></div>
-          ) : (
-            <table class="min-w-full divide-y divide-gray-300">
-              <thead>
-                <tr>
-                  <th scope="col" class="py-3.5 pl-4 pr-3 text-left text-sm 
font-semibold text-gray-900 sm:pl-0">{i18n.str`Username`}</th>
-                  <th scope="col" class="px-3 py-3.5 text-left text-sm 
font-semibold text-gray-900">{i18n.str`Name`}</th>
-                  <th scope="col" class="px-3 py-3.5 text-left text-sm 
font-semibold text-gray-900">{i18n.str`Balance`}</th>
-                  <th scope="col" class="relative py-3.5 pl-3 pr-4 sm:pr-0">
-                    <span class="sr-only">{i18n.str`Actions`}</span>
-                  </th>
-                </tr>
-              </thead>
-              <tbody class="divide-y divide-gray-200">
-                {accounts.map((item, idx) => {
-                  const balance = !item.balance
-                    ? undefined
-                    : Amounts.parse(item.balance.amount);
-                  const noBalance = Amounts.isZero(item.balance.amount)
-                  const balanceIsDebit =
-                    item.balance &&
-                    item.balance.credit_debit_indicator == "debit";
+      <div class="mt-8 flow-root">
+        <div class="-mx-4 -my-2 overflow-x-auto sm:-mx-6 lg:-mx-8">
+          <div class="inline-block min-w-full py-2 align-middle sm:px-6 
lg:px-8">
+            {!accounts.length ? (
+              <div></div>
+            ) : (
+              <table class="min-w-full divide-y divide-gray-300">
+                <thead>
+                  <tr>
+                    <th scope="col" class="py-3.5 pl-4 pr-3 text-left text-sm 
font-semibold text-gray-900 sm:pl-0">{i18n.str`Username`}</th>
+                    <th scope="col" class="px-3 py-3.5 text-left text-sm 
font-semibold text-gray-900">{i18n.str`Name`}</th>
+                    <th scope="col" class="px-3 py-3.5 text-left text-sm 
font-semibold text-gray-900">{i18n.str`Balance`}</th>
+                    <th scope="col" class="relative py-3.5 pl-3 pr-4 sm:pr-0">
+                      <span class="sr-only">{i18n.str`Actions`}</span>
+                    </th>
+                  </tr>
+                </thead>
+                <tbody class="divide-y divide-gray-200">
+                  {accounts.map((item, idx) => {
+                    const balance = !item.balance
+                      ? undefined
+                      : Amounts.parse(item.balance.amount);
+                    const noBalance = Amounts.isZero(item.balance.amount)
+                    const balanceIsDebit =
+                      item.balance &&
+                      item.balance.credit_debit_indicator == "debit";
 
-                  return <tr key={idx}>
-                    <td class="whitespace-nowrap py-4 pl-4 pr-3 text-sm 
font-medium text-gray-900 sm:pl-0">
-                      <a href="#" class="text-indigo-600 hover:text-indigo-900"
-                        onClick={(e) => {
-                          e.preventDefault();
-                          onShowAccountDetails(item.username)
-                        }}
-                      >
-                        {item.username}
-                      </a>
+                    return <tr key={idx}>
+                      <td class="whitespace-nowrap py-4 pl-4 pr-3 text-sm 
font-medium text-gray-900 sm:pl-0">
+                        <a href="#" class="text-indigo-600 
hover:text-indigo-900"
+                          onClick={(e) => {
+                            e.preventDefault();
+                            onShowAccountDetails(item.username)
+                          }}
+                        >
+                          {item.username}
+                        </a>
 
 
-                    </td>
-                    <td class="whitespace-nowrap px-3 py-4 text-sm 
text-gray-500">
-                      {item.name}
-                    </td>
-                    <td data-negative={noBalance ? undefined : balanceIsDebit 
? "true" : "false"} class="whitespace-nowrap px-3 py-4 text-sm text-gray-500 
data-[negative=false]:text-green-600 data-[negative=true]:text-red-600 ">
-                      {!balance ? (
-                        i18n.str`unknown`
-                      ) : (
-                        <span class="amount">
-                          <RenderAmount value={balance} 
negative={balanceIsDebit} />
-                        </span>
-                      )}
-                    </td>
-                    <td class="relative whitespace-nowrap py-4 pl-3 pr-4 
text-right text-sm font-medium sm:pr-0">
-                      <a href="#" class="text-indigo-600 hover:text-indigo-900"
-                        onClick={(e) => {
-                          e.preventDefault();
-                          onUpdateAccountPassword(item.username)
-                        }}
-                      >
-                        change password
-                      </a>
-                      <br />
-                      {config.have_cashout ?
-                        <Fragment>
+                      </td>
+                      <td class="whitespace-nowrap px-3 py-4 text-sm 
text-gray-500">
+                        {item.name}
+                      </td>
+                      <td data-negative={noBalance ? undefined : 
balanceIsDebit ? "true" : "false"} class="whitespace-nowrap px-3 py-4 text-sm 
text-gray-500 data-[negative=false]:text-green-600 
data-[negative=true]:text-red-600 ">
+                        {!balance ? (
+                          i18n.str`unknown`
+                        ) : (
+                          <span class="amount">
+                            <RenderAmount value={balance} 
negative={balanceIsDebit} />
+                          </span>
+                        )}
+                      </td>
+                      <td class="relative whitespace-nowrap py-4 pl-3 pr-4 
text-right text-sm font-medium sm:pr-0">
+                        <a href="#" class="text-indigo-600 
hover:text-indigo-900"
+                          onClick={(e) => {
+                            e.preventDefault();
+                            onUpdateAccountPassword(item.username)
+                          }}
+                        >
+                          change password
+                        </a>
+                        <br />
+                        {config.have_cashout ?
+                          <Fragment>
 
+                            <a href="#" class="text-indigo-600 
hover:text-indigo-900" onClick={(e) => {
+                              e.preventDefault();
+                              onShowCashoutForAccount(item.username)
+                            }}
+                            >
+                              cashouts
+                            </a>
+                            <br />
+                          </Fragment>
+                          : undefined}
+                        {noBalance ?
                           <a href="#" class="text-indigo-600 
hover:text-indigo-900" onClick={(e) => {
                             e.preventDefault();
-                            onShowCashoutForAccount(item.username)
+                            onRemoveAccount(item.username)
                           }}
                           >
-                            cashouts
+                            remove
                           </a>
-                          <br />
-                        </Fragment>
-                        : undefined}
-                      {noBalance ?
-                        <a href="#" class="text-indigo-600 
hover:text-indigo-900" onClick={(e) => {
-                          e.preventDefault();
-                          onRemoveAccount(item.username)
-                        }}
-                        >
-                          remove
-                        </a>
-                        : undefined}
-                    </td>
-                  </tr>
-                })}
+                          : undefined}
+                      </td>
+                    </tr>
+                  })}
 
-              </tbody>
-            </table>
-          )}
+                </tbody>
+              </table>
+            )}
+          </div>
         </div>
       </div>
     </div>
-  </div>
+  </Fragment>
+
 }
\ No newline at end of file
diff --git a/packages/demobank-ui/src/pages/admin/AdminHome.tsx 
b/packages/demobank-ui/src/pages/admin/AdminHome.tsx
index 01f9f6dbd..78827d3a2 100644
--- a/packages/demobank-ui/src/pages/admin/AdminHome.tsx
+++ b/packages/demobank-ui/src/pages/admin/AdminHome.tsx
@@ -1,7 +1,14 @@
+import { AmountJson, AmountString, Amounts, TalerCorebankApi, TalerError } 
from "@gnu-taler/taler-util";
+import { useTranslationContext } from "@gnu-taler/web-util/browser";
 import { Fragment, VNode, h } from "preact";
+import { useState } from "preact/hooks";
 import { Transactions } from "../../components/Transactions/index.js";
+import { MonitorMetrics, useLastMonitorInfo } from "../../hooks/circuit.js";
+import { RenderAmount } from "../PaytoWireTransferForm.js";
 import { WireTransfer } from "./Account.js";
 import { AccountList } from "./AccountList.js";
+import { assertUnreachable } from "../WithdrawalOperationPage.js";
+import { ErrorLoading } from "../../components/ErrorLoading.js";
 
 /**
  * Query account information and show QR code if there is pending withdrawal
@@ -17,6 +24,10 @@ interface Props {
 }
 export function AdminHome({ onCreateAccount, onRegister, onRemoveAccount, 
onShowAccountDetails, onShowCashoutForAccount, onUpdateAccountPassword }: 
Props): VNode {
   return <Fragment>
+    <Metrics />
+    <WireTransfer onRegister={onRegister} />
+
+    <Transactions account="admin" />
     <AccountList
       onCreateAccount={onCreateAccount}
       onRemoveAccount={onRemoveAccount}
@@ -25,8 +36,167 @@ export function AdminHome({ onCreateAccount, onRegister, 
onRemoveAccount, onShow
       onUpdateAccountPassword={onUpdateAccountPassword}
     />
 
-    <WireTransfer onRegister={onRegister} />
+  </Fragment>
+}
 
-    <Transactions account="admin" />
+function Metrics(): VNode {
+  const { i18n } = useTranslationContext()
+  const [metricType, setMetricType] = 
useState<TalerCorebankApi.MonitorTimeframeParam>(TalerCorebankApi.MonitorTimeframeParam.day);
+
+  const resp = useLastMonitorInfo(new Date(), metricType);
+  console.log(resp)
+  if (!resp) return <Fragment />;
+  if (resp instanceof TalerError) {
+    return <ErrorLoading error={resp} />
+  }
+  if (resp.current.type !== "ok" || resp.current.type !== "ok" || 
resp.current.type !== "ok") {
+    return <Fragment />
+  }
+  if (resp.previous.type !== "ok" || resp.previous.type !== "ok" || 
resp.previous.type !== "ok") {
+    return <Fragment />
+  }
+
+  // const metric = getMetricInfo(metricType, current, previous);
+
+  return <Fragment>
+    <div class="sm:hidden">
+      <label for="tabs" class="sr-only"><i18n.Translate>Select a 
section</i18n.Translate></label>
+      <select id="tabs" name="tabs" class="block w-full rounded-md 
border-gray-300 focus:border-indigo-500 focus:ring-indigo-500" onChange={(e) => 
{
+        // const op = e.currentTarget.value as typeof metricType
+        setMetricType(e.currentTarget.value as any)
+      }}>
+        <option value={TalerCorebankApi.MonitorTimeframeParam.hour} 
selected={metricType == 
TalerCorebankApi.MonitorTimeframeParam.hour}><i18n.Translate>Last 
hour</i18n.Translate></option>
+        <option value={TalerCorebankApi.MonitorTimeframeParam.day} 
selected={metricType == 
TalerCorebankApi.MonitorTimeframeParam.day}><i18n.Translate>Last 
day</i18n.Translate></option>
+        <option value={TalerCorebankApi.MonitorTimeframeParam.month} 
selected={metricType == 
TalerCorebankApi.MonitorTimeframeParam.month}><i18n.Translate>Last 
month</i18n.Translate></option>
+        <option value={TalerCorebankApi.MonitorTimeframeParam.year} 
selected={metricType == 
TalerCorebankApi.MonitorTimeframeParam.year}><i18n.Translate>Last 
year</i18n.Translate></option>
+      </select>
+    </div>
+    <div class="hidden sm:block">
+      <nav class="isolate flex divide-x divide-gray-200 rounded-lg shadow" 
aria-label="Tabs">
+        <a href="#" onClick={(e) => { e.preventDefault(); 
setMetricType(TalerCorebankApi.MonitorTimeframeParam.hour) }} 
data-selected={metricType == TalerCorebankApi.MonitorTimeframeParam.hour} 
class="rounded-l-lg text-gray-500 hover:text-gray-700 
data-[selected=true]:text-gray-900 group relative min-w-0 flex-1 
overflow-hidden bg-white py-4 px-4 text-center text-sm font-medium 
hover:bg-gray-50 focus:z-10" >
+          <span><i18n.Translate>Last hour</i18n.Translate></span>
+          <span aria-hidden="true" data-selected={metricType == 
TalerCorebankApi.MonitorTimeframeParam.hour} class="bg-transparent 
data-[selected=true]:bg-indigo-500 absolute inset-x-0 bottom-0 h-0.5"></span>
+        </a>
+        <a href="#" onClick={(e) => { e.preventDefault(); 
setMetricType(TalerCorebankApi.MonitorTimeframeParam.day) }} 
data-selected={metricType == TalerCorebankApi.MonitorTimeframeParam.day} 
aria-current="page" class="             text-gray-500 hover:text-gray-700 
data-[selected=true]:text-gray-900 group relative min-w-0 flex-1 
overflow-hidden bg-white py-4 px-4 text-center text-sm font-medium 
hover:bg-gray-50 focus:z-10">
+          <span><i18n.Translate>Last day</i18n.Translate></span>
+          <span aria-hidden="true" data-selected={metricType == 
TalerCorebankApi.MonitorTimeframeParam.day} class="bg-transparent 
data-[selected=true]:bg-indigo-500 absolute inset-x-0 bottom-0 h-0.5"></span>
+        </a>
+        <a href="#" onClick={(e) => { e.preventDefault(); 
setMetricType(TalerCorebankApi.MonitorTimeframeParam.month) }} 
data-selected={metricType == TalerCorebankApi.MonitorTimeframeParam.month} 
class="rounded-r-lg text-gray-500 hover:text-gray-700 
data-[selected=true]:text-gray-900 group relative min-w-0 flex-1 
overflow-hidden bg-white py-4 px-4 text-center text-sm font-medium 
hover:bg-gray-50 focus:z-10">
+          <span><i18n.Translate>Last month</i18n.Translate></span>
+          <span aria-hidden="true" data-selected={metricType == 
TalerCorebankApi.MonitorTimeframeParam.month} class="bg-transparent 
data-[selected=true]:bg-indigo-500 absolute inset-x-0 bottom-0 h-0.5"></span>
+        </a>
+        <a href="#" onClick={(e) => { e.preventDefault(); 
setMetricType(TalerCorebankApi.MonitorTimeframeParam.year) }} 
data-selected={metricType == TalerCorebankApi.MonitorTimeframeParam.year} 
class="rounded-r-lg text-gray-500 hover:text-gray-700 
data-[selected=true]:text-gray-900 group relative min-w-0 flex-1 
overflow-hidden bg-white py-4 px-4 text-center text-sm font-medium 
hover:bg-gray-50 focus:z-10">
+          <span><i18n.Translate>Last Year</i18n.Translate></span>
+          <span aria-hidden="true" data-selected={metricType == 
TalerCorebankApi.MonitorTimeframeParam.year} class="bg-transparent 
data-[selected=true]:bg-indigo-500 absolute inset-x-0 bottom-0 h-0.5"></span>
+        </a>
+      </nav>
+    </div>
+
+    <div class="w-full flex justify-between">
+
+      <h1 class="text-base font-semibold leading-7 text-gray-900 mt-5">
+
+        Trading volume on Thursday
+      </h1>
+      <div class="flex items-center justify-between">
+        {/* <span class="flex flex-grow flex-col">
+          <span class="text-sm text-black font-medium leading-6 " 
id="availability-label">
+            ASD
+          </span>
+        </span> */}
+        <button type="button" class="bg-gray-200 relative inline-flex h-6 w-48 
flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent 
transition-colors duration-200 ease-in-out focus:outline-none focus:ring-2 
focus:ring-indigo-600 focus:ring-offset-2" role="switch" aria-checked="false">
+          <span class="sr-only">Use setting</span>
+          {/* <!-- Enabled: "translate-x-5", Not Enabled: "translate-x-0" --> 
*/}
+          <span class="translate-x-0 pointer-events-none relative inline-block 
h-5 w-32 transform rounded-full bg-white shadow ring-0 transition duration-200 
ease-in-out">
+            {/* <!-- Enabled: "opacity-0 duration-100 ease-out", Not Enabled: 
"opacity-100 duration-200 ease-in" --> */}
+            <span class="opacity-100 duration-200 ease-in absolute inset-0 
flex h-full w-full items-center justify-center transition-opacity" 
aria-hidden="true">
+              Transaction
+            </span>
+            {/* <!-- Enabled: "opacity-100 duration-200 ease-in", Not Enabled: 
"opacity-0 duration-100 ease-out" --> */}
+            <span class="opacity-0 duration-100 ease-out absolute inset-0 flex 
h-full w-full items-center justify-center transition-opacity" 
aria-hidden="true">
+              <svg class="h-3 w-3 text-indigo-600" fill="currentColor" 
viewBox="0 0 12 12">
+                <path d="M3.707 5.293a1 1 0 00-1.414 1.414l1.414-1.414zM5 
8l-.707.707a1 1 0 001.414 0L5 8zm4.707-3.293a1 1 0 00-1.414-1.414l1.414 
1.414zm-7.414 2l2 2 1.414-1.414-2-2-1.414 1.414zm3.414 2l4-4-1.414-1.414-4 4 
1.414 1.414z" />
+              </svg>
+            </span>
+          </span>
+        </button>
+      </div>
+
+    </div>
+    <dl class="mt-5 grid grid-cols-1 divide-y divide-gray-200 overflow-hidden 
rounded-lg bg-white shadow-lg md:grid-cols-3 md:divide-x md:divide-y-0">
+
+      <div class="px-4 py-5 sm:p-6">
+        <dt class="text-base font-normal text-gray-900">
+          <i18n.Translate>Cashin</i18n.Translate>
+        </dt>
+        <MetricValue
+          current={resp.current.body.cashinExternalVolume}
+          previous={resp.previous.body.cashinExternalVolume}
+        />
+      </div>
+
+      <div class="px-4 py-5 sm:p-6">
+        <dt class="text-base font-normal text-gray-900">
+          <i18n.Translate>Cashout</i18n.Translate>
+        </dt>
+        <MetricValue
+          current={resp.current.body.cashoutExternalVolume}
+          previous={resp.previous.body.cashoutExternalVolume}
+        />
+      </div>
+      <div class="px-4 py-5 sm:p-6">
+        <dt class="text-base font-normal text-gray-900">
+          <i18n.Translate>Payout</i18n.Translate>
+        </dt>
+        <MetricValue
+          current={resp.current.body.talerPayoutInternalVolume}
+          previous={resp.previous.body.talerPayoutInternalVolume}
+        />
+      </div>
+    </dl>
   </Fragment>
-}
\ No newline at end of file
+
+}
+
+
+function MetricValue({ current, previous }: { current: AmountString | 
undefined, previous: AmountString | undefined }): VNode {
+  const { i18n } = useTranslationContext()
+  const cmp = current && previous ? Amounts.cmp(current, previous) : 0;
+  const currAmount = !current ? undefined : 
Number.parseFloat(Amounts.stringifyValue(current))
+  const prevAmount = !previous ? undefined : 
Number.parseFloat(Amounts.stringifyValue(previous))
+
+  const rate = !currAmount || Number.isNaN(currAmount) || !prevAmount || 
Number.isNaN(prevAmount) ? 0 :
+    cmp === -1 ? 1 - Math.round(currAmount) / Math.round(prevAmount) :
+      cmp === 1 ? (Math.round(currAmount) / Math.round(prevAmount)) - 1 : 0;
+
+  const rateStr = `${(Math.abs(rate) * 100).toFixed(2)}%`
+  return <dd class="mt-1 flex justify-between md:block lg:flex">
+    <div class="flex justify-start  items-baseline text-2xl font-semibold 
text-indigo-600">
+      {!current ? "-" : <RenderAmount value={Amounts.parseOrThrow(current)} />}
+    </div>
+    <div class="flex justify-end items-baseline text-2xl font-semibold 
text-indigo-600">
+      <small class="ml-2 text-sm font-medium text-gray-500">
+        <i18n.Translate>from</i18n.Translate> {!previous ? "-" : <RenderAmount 
value={Amounts.parseOrThrow(previous)} />}
+      </small>
+    </div>
+
+    {cmp == 1 &&
+      <div class="inline-flex items-baseline rounded-full px-2.5 py-0.5 
text-sm font-medium bg-green-100 text-green-800 md:mt-2 lg:mt-0">
+        <svg class="-ml-1 mr-0.5 h-5 w-5 flex-shrink-0 self-center 
text-green-500" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
+          <path fill-rule="evenodd" d="M10 17a.75.75 0 01-.75-.75V5.612L5.29 
9.77a.75.75 0 01-1.08-1.04l5.25-5.5a.75.75 0 011.08 0l5.25 5.5a.75.75 0 11-1.08 
1.04l-3.96-4.158V16.25A.75.75 0 0110 17z" clip-rule="evenodd" />
+        </svg>
+        <span class="sr-only"><i18n.Translate>Increased 
by</i18n.Translate></span>
+        {rateStr}
+      </div>
+    }
+    {cmp == -1 &&
+      <div class="inline-flex items-baseline rounded-full px-2.5 py-0.5 
text-sm font-medium bg-red-100 text-red-800 md:mt-2 lg:mt-0">
+        <svg class="-ml-1 mr-0.5 h-5 w-5 flex-shrink-0 self-center 
text-red-500" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
+          <path fill-rule="evenodd" d="M10 3a.75.75 0 
01.75.75v10.638l3.96-4.158a.75.75 0 111.08 1.04l-5.25 5.5a.75.75 0 01-1.08 
0l-5.25-5.5a.75.75 0 111.08-1.04l3.96 4.158V3.75A.75.75 0 0110 3z" 
clip-rule="evenodd" />
+        </svg>
+        <span class="sr-only"><i18n.Translate>Descreased 
by</i18n.Translate></span>
+        {rateStr}
+      </div>
+    }
+  </dd>
+}
diff --git a/packages/demobank-ui/src/pages/admin/CreateNewAccount.tsx 
b/packages/demobank-ui/src/pages/admin/CreateNewAccount.tsx
index 772ea6e84..ea40001c0 100644
--- a/packages/demobank-ui/src/pages/admin/CreateNewAccount.tsx
+++ b/packages/demobank-ui/src/pages/admin/CreateNewAccount.tsx
@@ -7,7 +7,7 @@ import { getRandomPassword } from "../rnd.js";
 import { AccountForm, AccountFormData } from "./AccountForm.js";
 import { useBackendState } from "../../hooks/backend.js";
 import { useBankCoreApiContext } from "../../context/config.js";
-import { assertUnreachable } from "../HomePage.js";
+import { assertUnreachable } from "../WithdrawalOperationPage.js";
 import { mutate } from "swr";
 import { Attention } from "../../components/Attention.js";
 
diff --git a/packages/demobank-ui/src/pages/admin/RemoveAccount.tsx 
b/packages/demobank-ui/src/pages/admin/RemoveAccount.tsx
index 88961c2cb..4aa17e302 100644
--- a/packages/demobank-ui/src/pages/admin/RemoveAccount.tsx
+++ b/packages/demobank-ui/src/pages/admin/RemoveAccount.tsx
@@ -8,7 +8,7 @@ import { Loading } from "../../components/Loading.js";
 import { ShowInputErrorLabel } from "../../components/ShowInputErrorLabel.js";
 import { useAccountDetails } from "../../hooks/access.js";
 import { buildRequestErrorMessage, undefinedIfEmpty, withRuntimeErrorHandling 
} from "../../utils.js";
-import { assertUnreachable } from "../HomePage.js";
+import { assertUnreachable } from "../WithdrawalOperationPage.js";
 import { LoginForm } from "../LoginForm.js";
 import { doAutoFocus } from "../PaytoWireTransferForm.js";
 import { useBankCoreApiContext } from "../../context/config.js";
diff --git a/packages/demobank-ui/src/pages/business/CreateCashout.tsx 
b/packages/demobank-ui/src/pages/business/CreateCashout.tsx
index 4696c899e..5595d3b51 100644
--- a/packages/demobank-ui/src/pages/business/CreateCashout.tsx
+++ b/packages/demobank-ui/src/pages/business/CreateCashout.tsx
@@ -45,7 +45,7 @@ import {
 } from "../../utils.js";
 import { LoginForm } from "../LoginForm.js";
 import { InputAmount } from "../PaytoWireTransferForm.js";
-import { assertUnreachable } from "../HomePage.js";
+import { assertUnreachable } from "../WithdrawalOperationPage.js";
 import { Attention } from "../../components/Attention.js";
 
 interface Props {
diff --git a/packages/demobank-ui/src/pages/business/ShowCashoutDetails.tsx 
b/packages/demobank-ui/src/pages/business/ShowCashoutDetails.tsx
index a8e34e4b9..5c09e2001 100644
--- a/packages/demobank-ui/src/pages/business/ShowCashoutDetails.tsx
+++ b/packages/demobank-ui/src/pages/business/ShowCashoutDetails.tsx
@@ -37,7 +37,7 @@ import {
   undefinedIfEmpty,
   withRuntimeErrorHandling
 } from "../../utils.js";
-import { assertUnreachable } from "../HomePage.js";
+import { assertUnreachable } from "../WithdrawalOperationPage.js";
 
 interface Props {
   id: string;

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