From 0388d31d364139d0a3999126b06d8ac850117ab9 Mon Sep 17 00:00:00 2001
From: Sebastian <sebasjm@gmail.com>
Date: Tue, 19 Sep 2023 00:39:00 -0300
Subject: account page

---
 .../demobank-ui/src/components/ErrorLoading.tsx    | 29 ++++++++++++++++++++++
 1 file changed, 29 insertions(+)
 create mode 100644 packages/demobank-ui/src/components/ErrorLoading.tsx

(limited to 'packages/demobank-ui/src/components/ErrorLoading.tsx')

diff --git a/packages/demobank-ui/src/components/ErrorLoading.tsx b/packages/demobank-ui/src/components/ErrorLoading.tsx
new file mode 100644
index 000000000..fbc4ffceb
--- /dev/null
+++ b/packages/demobank-ui/src/components/ErrorLoading.tsx
@@ -0,0 +1,29 @@
+/*
+/*
+ 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 <http://www.gnu.org/licenses/>
+ */
+
+import { useTranslationContext } from "@gnu-taler/web-util/browser";
+import { h, VNode } from "preact";
+
+export function ErrorLoading(): VNode {
+  const { i18n } = useTranslationContext()
+  return (
+    <div><i18n.Translate>
+      Could not complete the request
+    </i18n.Translate>
+    </div>
+  );
+}
-- 
cgit v1.2.3


From ae49194d4271f1108ec9b8318ea3b7977314cb85 Mon Sep 17 00:00:00 2001
From: Sebastian <sebasjm@gmail.com>
Date: Mon, 25 Sep 2023 08:40:18 -0300
Subject: more ui

---
 .../demobank-ui/src/components/ErrorLoading.tsx    | 19 +++++--
 packages/demobank-ui/src/components/Routing.tsx    |  4 +-
 .../src/components/Transactions/views.tsx          |  1 +
 packages/demobank-ui/src/hooks/access.ts           |  2 +-
 .../demobank-ui/src/pages/AccountPage/index.ts     | 10 ++--
 .../demobank-ui/src/pages/AccountPage/state.ts     |  7 +++
 packages/demobank-ui/src/pages/BankFrame.tsx       |  5 +-
 packages/demobank-ui/src/pages/HomePage.tsx        |  8 +--
 packages/demobank-ui/src/pages/LoginForm.tsx       |  4 +-
 .../demobank-ui/src/pages/OperationState/index.ts  | 11 ++--
 .../demobank-ui/src/pages/OperationState/state.ts  | 36 ++++++++++----
 .../demobank-ui/src/pages/OperationState/views.tsx | 44 ++++++----------
 packages/demobank-ui/src/pages/PaymentOptions.tsx  |  4 +-
 .../demobank-ui/src/pages/RegistrationPage.tsx     | 58 +++++++++++++++++++---
 packages/demobank-ui/src/pages/Test.tsx            |  5 --
 .../demobank-ui/src/pages/WalletWithdrawForm.tsx   |  1 -
 packages/taler-util/src/errors.ts                  |  2 +-
 .../taler-wallet-core/src/operations/withdraw.ts   | 17 +++----
 packages/taler-wallet-core/src/versions.ts         |  2 +-
 19 files changed, 145 insertions(+), 95 deletions(-)
 delete mode 100644 packages/demobank-ui/src/pages/Test.tsx

(limited to 'packages/demobank-ui/src/components/ErrorLoading.tsx')

diff --git a/packages/demobank-ui/src/components/ErrorLoading.tsx b/packages/demobank-ui/src/components/ErrorLoading.tsx
index fbc4ffceb..a4faa4d5d 100644
--- a/packages/demobank-ui/src/components/ErrorLoading.tsx
+++ b/packages/demobank-ui/src/components/ErrorLoading.tsx
@@ -15,15 +15,24 @@
  GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 
-import { useTranslationContext } from "@gnu-taler/web-util/browser";
+import { HttpError, useTranslationContext } from "@gnu-taler/web-util/browser";
 import { h, VNode } from "preact";
 
-export function ErrorLoading(): VNode {
+export function ErrorLoading({ error }: { error: HttpError<SandboxBackend.SandboxError> }): VNode {
   const { i18n } = useTranslationContext()
   return (
-    <div><i18n.Translate>
-      Could not complete the request
-    </i18n.Translate>
+    <div><div class="rounded-md bg-red-50 p-4">
+      <div class="flex">
+        <div class="flex-shrink-0">
+          <svg class="h-5 w-5 text-red-400" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
+            <path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.28 7.22a.75.75 0 00-1.06 1.06L8.94 10l-1.72 1.72a.75.75 0 101.06 1.06L10 11.06l1.72 1.72a.75.75 0 101.06-1.06L11.06 10l1.72-1.72a.75.75 0 00-1.06-1.06L10 8.94 8.28 7.22z" clip-rule="evenodd" />
+          </svg>
+        </div>
+        <div class="ml-3 flex-1 md:flex md:justify-between">
+          <p class="text-sm font-medium text-red-800">{error.message}</p>
+        </div>
+      </div>
+    </div>
     </div>
   );
 }
diff --git a/packages/demobank-ui/src/components/Routing.tsx b/packages/demobank-ui/src/components/Routing.tsx
index d51bd01eb..2710069c2 100644
--- a/packages/demobank-ui/src/components/Routing.tsx
+++ b/packages/demobank-ui/src/components/Routing.tsx
@@ -23,7 +23,6 @@ import { BusinessAccount } from "../pages/business/Home.js";
 import { HomePage, WithdrawalOperationPage } from "../pages/HomePage.js";
 import { PublicHistoriesPage } from "../pages/PublicHistoriesPage.js";
 import { RegistrationPage } from "../pages/RegistrationPage.js";
-import { Test } from "../pages/Test.js";
 import { useBackendContext } from "../context/backend.js";
 import { LoginForm } from "../pages/LoginForm.js";
 import { AdminHome } from "../pages/admin/Home.js";
@@ -69,6 +68,9 @@ export function Routing(): VNode {
                 onComplete={() => {
                   route("/account");
                 }}
+                onCancel={() => {
+                  route("/account");
+                }}
               />
             )}
           />
diff --git a/packages/demobank-ui/src/components/Transactions/views.tsx b/packages/demobank-ui/src/components/Transactions/views.tsx
index b11888320..e34120e34 100644
--- a/packages/demobank-ui/src/components/Transactions/views.tsx
+++ b/packages/demobank-ui/src/components/Transactions/views.tsx
@@ -32,6 +32,7 @@ export function LoadingUriView({ error }: State.LoadingUriError): VNode {
 
 export function ReadyView({ transactions }: State.Ready): VNode {
   const { i18n } = useTranslationContext();
+  if (!transactions.length) return <div />
   const txByDate = transactions.reduce((prev, cur) => {
     const d = cur.when.t_ms === "never"
       ? ""
diff --git a/packages/demobank-ui/src/hooks/access.ts b/packages/demobank-ui/src/hooks/access.ts
index 13fee71f0..2f6848af8 100644
--- a/packages/demobank-ui/src/hooks/access.ts
+++ b/packages/demobank-ui/src/hooks/access.ts
@@ -94,7 +94,7 @@ export function useAccessAnonAPI(): AccessAnonAPI {
   const { request } = useAuthenticatedBackend();
 
   const abortWithdrawal = async (id: string): Promise<HttpResponseOk<void>> => {
-    const res = await request<void>(`accounts/withdrawals/${id}/abort`, {
+    const res = await request<void>(`withdrawals/${id}/abort`, {
       method: "POST",
       contentType: "json",
     });
diff --git a/packages/demobank-ui/src/pages/AccountPage/index.ts b/packages/demobank-ui/src/pages/AccountPage/index.ts
index ba9b85710..9230fb6b1 100644
--- a/packages/demobank-ui/src/pages/AccountPage/index.ts
+++ b/packages/demobank-ui/src/pages/AccountPage/index.ts
@@ -18,7 +18,7 @@ import { HttpError, HttpResponseOk, HttpResponsePaginated, utils } from "@gnu-ta
 import { AbsoluteTime, AmountJson, PaytoUriIBAN, PaytoUriTalerBank } from "@gnu-taler/taler-util";
 import { Loading } from "../../components/Loading.js";
 import { useComponentState } from "./state.js";
-import { ReadyView, InvalidIbanView} from "./views.js";
+import { ReadyView, InvalidIbanView } from "./views.js";
 import { VNode } from "preact";
 import { LoginForm } from "../LoginForm.js";
 import { ErrorLoading } from "../../components/ErrorLoading.js";
@@ -29,7 +29,7 @@ export interface Props {
     error: HttpResponsePaginated<T, SandboxBackend.SandboxError>,
   ) => VNode;
   goToBusinessAccount: () => void;
-  goToConfirmOperation: (id:string) => void;
+  goToConfirmOperation: (id: string) => void;
 }
 
 export type State = State.Loading | State.LoadingError | State.Ready | State.InvalidIban | State.UserNotFound;
@@ -48,14 +48,14 @@ export namespace State {
   export interface BaseInfo {
     error: undefined;
   }
-  
+
   export interface Ready extends BaseInfo {
     status: "ready";
     error: undefined;
-    account: string, 
+    account: string,
     limit: AmountJson,
     goToBusinessAccount: () => void;
-    goToConfirmOperation: (id:string) => void;
+    goToConfirmOperation: (id: string) => void;
   }
 
   export interface InvalidIban {
diff --git a/packages/demobank-ui/src/pages/AccountPage/state.ts b/packages/demobank-ui/src/pages/AccountPage/state.ts
index b12afbf9e..3df463f4e 100644
--- a/packages/demobank-ui/src/pages/AccountPage/state.ts
+++ b/packages/demobank-ui/src/pages/AccountPage/state.ts
@@ -48,6 +48,13 @@ export function useComponentState({ account, goToBusinessAccount, goToConfirmOpe
         error: result,
       };
     }
+    if (result.status === HttpStatusCode.Unauthorized) {
+      notifyError(i18n.str`Require login`, undefined);
+      return {
+        status: "error-user-not-found",
+        error: result,
+      };
+    }
     return {
       status: "loading-error",
       error: result,
diff --git a/packages/demobank-ui/src/pages/BankFrame.tsx b/packages/demobank-ui/src/pages/BankFrame.tsx
index a1414544e..214aac063 100644
--- a/packages/demobank-ui/src/pages/BankFrame.tsx
+++ b/packages/demobank-ui/src/pages/BankFrame.tsx
@@ -344,8 +344,6 @@ function StatusBanner(): VNode {
     class="fixed top-10 z-20 ml-4 mr-4"
   > {
       notifs.map(n => {
-        const info = n.message.type === "info" ? n.message : undefined
-        const error = n.message.type === "error" ? n.message : undefined
         switch (n.message.type) {
           case "error":
             return <div class="rounded-md bg-red-50 p-4">
@@ -478,8 +476,7 @@ function AccountBalance({ account }: { account: string }): VNode {
 
   // FIXME: balance
   return <div>
-    {Amounts.currencyOf(result.data.balance)}
-    {Amounts.stringifyValue(result.data.balance)}
+    {Amounts.currencyOf(result.data.balance)} {Amounts.stringifyValue(result.data.balance)}
     {/* {Amounts.currencyOf(result.data.balance.amount)}
     &nbsp;{result.data.balance.credit_debit_indicator === "debit" ? "-" : ""}
     {Amounts.stringifyValue(result.data.balance.amount)} */}
diff --git a/packages/demobank-ui/src/pages/HomePage.tsx b/packages/demobank-ui/src/pages/HomePage.tsx
index d80a57d21..fb30c1218 100644
--- a/packages/demobank-ui/src/pages/HomePage.tsx
+++ b/packages/demobank-ui/src/pages/HomePage.tsx
@@ -59,7 +59,7 @@ export function HomePage({
   account: string,
   onRegister: () => void;
   goToBusinessAccount: () => void;
-  goToConfirmOperation: (id:string) => void;
+  goToConfirmOperation: (id: string) => void;
 }): VNode {
   const { i18n } = useTranslationContext();
 
@@ -84,7 +84,7 @@ export function WithdrawalOperationPage({
   //or return withdrawal uri from response
   const baseUrl = getInitialBackendBaseURL()
   const uri = stringifyWithdrawUri({
-    bankIntegrationApiBaseUrl: `${baseUrl}/integration-api`,
+    bankIntegrationApiBaseUrl: `${baseUrl}/taler-integration`,
     withdrawalOperationId: operationId,
   });
   const parsedUri = parseWithdrawUri(uri);
@@ -98,7 +98,7 @@ export function WithdrawalOperationPage({
     );
     return <Loading />;
   }
-  
+
   return (
     <WithdrawalQRCode
       withdrawUri={parsedUri}
@@ -121,7 +121,7 @@ export function handleNotOkResult(
   return function handleNotOkResult2<T>(
     result:
       | HttpResponsePaginated<T, SandboxBackend.SandboxError | undefined>
-      | HttpResponse<T, SandboxBackend.SandboxError| undefined>,
+      | HttpResponse<T, SandboxBackend.SandboxError | undefined>,
   ): VNode {
     if (result.loading) return <Loading />;
     if (!result.ok) {
diff --git a/packages/demobank-ui/src/pages/LoginForm.tsx b/packages/demobank-ui/src/pages/LoginForm.tsx
index 0d4bd1261..cfef71b9a 100644
--- a/packages/demobank-ui/src/pages/LoginForm.tsx
+++ b/packages/demobank-ui/src/pages/LoginForm.tsx
@@ -49,7 +49,7 @@ export function LoginForm({ onRegister }: { onRegister?: () => void }): VNode {
       ? i18n.str`Missing username`
       // : !USERNAME_REGEX.test(username)
       //   ? i18n.str`Use letters and numbers only, and start with a lowercase letter`
-        : undefined,
+      : undefined,
     password: !password ? i18n.str`Missing password` : undefined,
   }) ?? busy;
 
@@ -213,7 +213,7 @@ export function LoginForm({ onRegister }: { onRegister?: () => void }): VNode {
           {bankUiSettings.allowRegistrations && onRegister &&
             <p class="mt-10 text-center text-sm text-gray-500 border-t">
               <button type="submit"
-                class="flex mt-4 w-full justify-center rounded-md bg-green-600 px-3 py-1.5 text-sm font-semibold leading-6 text-white shadow-sm hover:bg-green-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-green-600"
+                class="flex mt-4 rounded-md bg-blue-600 px-3 py-1.5 text-sm font-semibold leading-6 text-white shadow-sm hover:bg-blue-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-600"
                 onClick={(e) => {
                   e.preventDefault()
                   onRegister()
diff --git a/packages/demobank-ui/src/pages/OperationState/index.ts b/packages/demobank-ui/src/pages/OperationState/index.ts
index 32302f272..b347fd942 100644
--- a/packages/demobank-ui/src/pages/OperationState/index.ts
+++ b/packages/demobank-ui/src/pages/OperationState/index.ts
@@ -14,19 +14,16 @@
  GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 
-import { HttpError, HttpResponseOk, HttpResponsePaginated, utils } from "@gnu-taler/web-util/browser";
-import { AbsoluteTime, AmountJson, PaytoUriIBAN, PaytoUriTalerBank, WithdrawUriResult } from "@gnu-taler/taler-util";
+import { AbsoluteTime, AmountJson, WithdrawUriResult } from "@gnu-taler/taler-util";
+import { HttpError, utils } from "@gnu-taler/web-util/browser";
+import { ErrorLoading } from "../../components/ErrorLoading.js";
 import { Loading } from "../../components/Loading.js";
 import { useComponentState } from "./state.js";
-import { ReadyView, AbortedView, ConfirmedView, InvalidPaytoView, InvalidReserveView, InvalidWithdrawalView, NeedConfirmationView } from "./views.js";
-import { VNode } from "preact";
-import { LoginForm } from "../LoginForm.js";
-import { ErrorLoading } from "../../components/ErrorLoading.js";
+import { AbortedView, ConfirmedView, InvalidPaytoView, InvalidReserveView, InvalidWithdrawalView, NeedConfirmationView, ReadyView } from "./views.js";
 
 export interface Props {
   currency: string;
   onClose: () => void;
-  goToConfirmOperation: (id: string) => void;
 }
 
 export type State = State.Loading |
diff --git a/packages/demobank-ui/src/pages/OperationState/state.ts b/packages/demobank-ui/src/pages/OperationState/state.ts
index ae03ed529..56e79f9ab 100644
--- a/packages/demobank-ui/src/pages/OperationState/state.ts
+++ b/packages/demobank-ui/src/pages/OperationState/state.ts
@@ -15,16 +15,15 @@
  */
 
 import { Amounts, HttpStatusCode, TranslatedString, parsePaytoUri, parseWithdrawUri, stringifyWithdrawUri } from "@gnu-taler/taler-util";
-import { ErrorType, RequestError, notify, notifyError, notifyInfo, useTranslationContext, utils } from "@gnu-taler/web-util/browser";
-import { useBackendContext } from "../../context/backend.js";
-import { useAccessAPI, useAccessAnonAPI, useAccountDetails, useWithdrawalDetails } from "../../hooks/access.js";
-import { Props, State } from "./index.js";
-import { useSettings } from "../../hooks/settings.js";
-import { buildRequestErrorMessage, undefinedIfEmpty } from "../../utils.js";
-import { useEffect, useMemo, useState } from "preact/hooks";
+import { RequestError, notify, notifyError, notifyInfo, useTranslationContext, utils } from "@gnu-taler/web-util/browser";
+import { useEffect, useState } from "preact/hooks";
+import { useAccessAPI, useAccessAnonAPI, useWithdrawalDetails } from "../../hooks/access.js";
 import { getInitialBackendBaseURL } from "../../hooks/backend.js";
+import { useSettings } from "../../hooks/settings.js";
+import { buildRequestErrorMessage } from "../../utils.js";
+import { Props, State } from "./index.js";
 
-export function useComponentState({ currency, onClose,goToConfirmOperation }: Props): utils.RecursiveState<State> {
+export function useComponentState({ currency, onClose }: Props): utils.RecursiveState<State> {
   const { i18n } = useTranslationContext();
   const [settings, updateSettings] = useSettings()
   const { createWithdrawal } = useAccessAPI();
@@ -142,8 +141,8 @@ export function useComponentState({ currency, onClose,goToConfirmOperation }: Pr
       }
     }
     setBusy(undefined)
-  }  
-  const bankIntegrationApiBaseUrl = `${baseUrl}/integration-api`
+  }
+  const bankIntegrationApiBaseUrl = `${baseUrl}/taler-integration`
   const uri = stringifyWithdrawUri({
     bankIntegrationApiBaseUrl,
     withdrawalOperationId,
@@ -160,7 +159,13 @@ export function useComponentState({ currency, onClose,goToConfirmOperation }: Pr
 
   return (): utils.RecursiveState<State> => {
     const result = useWithdrawalDetails(withdrawalOperationId);
+    const shouldCreateNewOperation = !result.ok && !result.loading && result.info.status === HttpStatusCode.NotFound
 
+    useEffect(() => {
+      if (shouldCreateNewOperation) {
+        doSilentStart()
+      }
+    }, [])
     if (!result.ok) {
       if (result.loading) {
         return {
@@ -168,6 +173,12 @@ export function useComponentState({ currency, onClose,goToConfirmOperation }: Pr
           error: undefined
         }
       }
+      if (result.info.status === HttpStatusCode.NotFound) {
+        return {
+          status: "loading",
+          error: undefined,
+        }
+      }
       return {
         status: "loading-error",
         error: result
@@ -178,7 +189,10 @@ export function useComponentState({ currency, onClose,goToConfirmOperation }: Pr
       return {
         status: "aborted",
         error: undefined,
-        onClose,
+        onClose: async () => {
+          updateSettings("currentWithdrawalOperationId", undefined)
+          onClose()
+        },
       }
     }
 
diff --git a/packages/demobank-ui/src/pages/OperationState/views.tsx b/packages/demobank-ui/src/pages/OperationState/views.tsx
index 17f1d8457..681a3b94d 100644
--- a/packages/demobank-ui/src/pages/OperationState/views.tsx
+++ b/packages/demobank-ui/src/pages/OperationState/views.tsx
@@ -256,6 +256,7 @@ export function AbortedView({ error, onClose }: State.Aborted) {
     <div>aborted</div>
   );
 }
+
 export function ConfirmedView({ error, onClose }: State.Confirmed) {
   const { i18n } = useTranslationContext();
   const [settings, updateSettings] = useSettings()
@@ -325,8 +326,17 @@ export function ReadyView({ uri, onClose }: State.Ready): VNode<{}> {
     document.title = `${document.title} ${uri.withdrawalOperationId}`;
   }, []);
   const talerWithdrawUri = stringifyWithdrawUri(uri);
-  const [show, setShow] = useState(false)
   return <Fragment>
+    <div class="flex justify-end mt-4">
+      <button type="button"
+        class="inline-flex items-center rounded-md bg-red-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-red-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-red-500"
+        onClick={() => {
+          onClose()
+        }}
+      >
+        Cancel
+      </button>
+    </div>
 
     <div class="bg-white shadow sm:rounded-lg mt-4">
       <div class="p-4">
@@ -360,39 +370,13 @@ export function ReadyView({ uri, onClose }: State.Ready): VNode<{}> {
               <i18n.Translate>Scan the QR code with your mobile device.</i18n.Translate>
             </p>
           </div>
-          <div class="mt-5 sm:ml-6 sm:mt-0 sm:flex sm:flex-shrink-0 sm:items-center">
-            <button type="button"
-              class="inline-flex items-center rounded-md bg-indigo-600 px-3 py-2 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-500"
-              onClick={() => {
-                setShow(!show)
-              }}
-            >
-              {!show ?
-                <i18n.Translate>Show QR</i18n.Translate>
-                :
-                <i18n.Translate>Hide QR</i18n.Translate>
-              }
-            </button>
-          </div>
         </div>
-        {show &&
-          <div class="mt-2 max-w-md ml-auto mr-auto">
-            <QR text={talerWithdrawUri} />
-          </div>
-        }
+        <div class="mt-2 max-w-md ml-auto mr-auto">
+          <QR text={talerWithdrawUri} />
+        </div>
       </div>
     </div>
 
-    <div class="flex justify-end mt-4">
-      <button type="button"
-        class="inline-flex items-center rounded-md bg-red-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-red-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-red-500"
-        onClick={() => {
-          onClose()
-        }}
-      >
-        Cancel
-      </button>
-    </div>
   </Fragment>
 
 }
diff --git a/packages/demobank-ui/src/pages/PaymentOptions.tsx b/packages/demobank-ui/src/pages/PaymentOptions.tsx
index 2830f5c1e..49419d0dc 100644
--- a/packages/demobank-ui/src/pages/PaymentOptions.tsx
+++ b/packages/demobank-ui/src/pages/PaymentOptions.tsx
@@ -56,11 +56,11 @@ export function PaymentOptions({ limit, goToConfirmOperation }: { limit: AmountJ
                   <i18n.Translate>Withdraw digital money into your mobile wallet or browser extension</i18n.Translate>
                 </span>
                 {!!settings.currentWithdrawalOperationId &&
-                  <span class="inline-flex items-center gap-x-1.5 rounded-full bg-green-100 px-1.5 py-0.5 text-xs font-medium text-green-700">
+                  <span class="inline-flex items-center gap-x-1.5 w-fit rounded-full bg-green-100 px-2 py-1 text-xs font-medium text-green-700">
                     <svg class="h-1.5 w-1.5 fill-green-500" viewBox="0 0 6 6" aria-hidden="true">
                       <circle cx="3" cy="3" r="3" />
                     </svg>
-                    <i18n.Translate>Operation in progress</i18n.Translate>
+                    <i18n.Translate>operation ready</i18n.Translate>
                   </span>
                 }
               </span>
diff --git a/packages/demobank-ui/src/pages/RegistrationPage.tsx b/packages/demobank-ui/src/pages/RegistrationPage.tsx
index 8221457bf..5325f43ab 100644
--- a/packages/demobank-ui/src/pages/RegistrationPage.tsx
+++ b/packages/demobank-ui/src/pages/RegistrationPage.tsx
@@ -33,8 +33,10 @@ const logger = new Logger("RegistrationPage");
 
 export function RegistrationPage({
   onComplete,
+  onCancel
 }: {
   onComplete: () => void;
+  onCancel: () => void;
 }): VNode {
   const { i18n } = useTranslationContext();
   if (!bankUiSettings.allowRegistrations) {
@@ -42,7 +44,7 @@ export function RegistrationPage({
       <p>{i18n.str`Currently, the bank is not accepting new registrations!`}</p>
     );
   }
-  return <RegistrationForm onComplete={onComplete} />;
+  return <RegistrationForm onComplete={onComplete} onCancel={onCancel} />;
 }
 
 export const USERNAME_REGEX = /^[a-z][a-zA-Z0-9-]*$/;
@@ -50,7 +52,7 @@ export const USERNAME_REGEX = /^[a-z][a-zA-Z0-9-]*$/;
 /**
  * Collect and submit registration data.
  */
-function RegistrationForm({ onComplete }: { onComplete: () => void }): VNode {
+function RegistrationForm({ onComplete, onCancel }: { onComplete: () => void, onCancel: () => void }): VNode {
   const backend = useBackendContext();
   const [username, setUsername] = useState<string | undefined>();
   const [name, setName] = useState<string | undefined>();
@@ -168,9 +170,38 @@ function RegistrationForm({ onComplete }: { onComplete: () => void }): VNode {
             autoCapitalize="none"
             autoCorrect="off"
           >
+            <div>
+              <label for="name" class="block text-sm font-medium leading-6 text-gray-900">
+                <i18n.Translate>Name</i18n.Translate>
+                <b style={{ color: "red" }}> *</b>
+              </label>
+              <div class="mt-2">
+                <input
+                  autoFocus
+                  type="text"
+                  name="name"
+                  id="name"
+                  class="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
+                  value={name ?? ""}
+                  enterkeyhint="next"
+                  placeholder="your name"
+                  autocomplete="name"
+                  required
+                  onInput={(e): void => {
+                    setName(e.currentTarget.value);
+                  }}
+                />
+                <ShowInputErrorLabel
+                  message={errors?.name}
+                  isDirty={name !== undefined}
+                />
+              </div>
+            </div>
+
             <div>
               <label for="username" class="block text-sm font-medium leading-6 text-gray-900">
                 <i18n.Translate>Username</i18n.Translate>
+                <b style={{ color: "red" }}> *</b>
               </label>
               <div class="mt-2">
                 <input
@@ -197,7 +228,10 @@ function RegistrationForm({ onComplete }: { onComplete: () => void }): VNode {
 
             <div>
               <div class="flex items-center justify-between">
-                <label for="password" class="block text-sm font-medium leading-6 text-gray-900">Password</label>
+                <label for="password" class="block text-sm font-medium leading-6 text-gray-900">
+                  Password
+                  <b style={{ color: "red" }}> *</b>
+                </label>
               </div>
               <div class="mt-2">
                 <input
@@ -223,7 +257,10 @@ function RegistrationForm({ onComplete }: { onComplete: () => void }): VNode {
 
             <div>
               <div class="flex items-center justify-between">
-                <label for="register-repeat" class="block text-sm font-medium leading-6 text-gray-900">Repeat assword</label>
+                <label for="register-repeat" class="block text-sm font-medium leading-6 text-gray-900">
+                  Repeat assword
+                  <b style={{ color: "red" }}> *</b>
+                </label>
               </div>
               <div class="mt-2">
                 <input
@@ -247,9 +284,18 @@ function RegistrationForm({ onComplete }: { onComplete: () => void }): VNode {
               </div>
             </div>
 
-            <div>
+            <div class="flex w-full justify-between">
+              <button type="submit"
+                class="ring-1 ring-gray-600 rounded-md bg-white disabled:bg-gray-300 px-3 py-1.5 text-sm font-semibold leading-6 text-black shadow-sm hover:bg-white-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2"
+                onClick={(e) => {
+                  e.preventDefault()
+                  onCancel()
+                }}
+              >
+                <i18n.Translate>Cancel</i18n.Translate>
+              </button>
               <button type="submit"
-                class="flex w-full justify-center rounded-md bg-indigo-600 disabled:bg-gray-300 px-3 py-1.5 text-sm font-semibold leading-6 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"
+                class=" rounded-md bg-indigo-600 disabled:bg-gray-300 px-3 py-1.5 text-sm font-semibold leading-6 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"
                 disabled={!!errors}
                 onClick={(e) => {
                   e.preventDefault()
diff --git a/packages/demobank-ui/src/pages/Test.tsx b/packages/demobank-ui/src/pages/Test.tsx
deleted file mode 100644
index 874f7fe68..000000000
--- a/packages/demobank-ui/src/pages/Test.tsx
+++ /dev/null
@@ -1,5 +0,0 @@
-import { VNode, h } from "preact";
-
-export function Test(): VNode {
-    return <div >hola</div>
-}
\ No newline at end of file
diff --git a/packages/demobank-ui/src/pages/WalletWithdrawForm.tsx b/packages/demobank-ui/src/pages/WalletWithdrawForm.tsx
index e16825527..3c5ee34fd 100644
--- a/packages/demobank-ui/src/pages/WalletWithdrawForm.tsx
+++ b/packages/demobank-ui/src/pages/WalletWithdrawForm.tsx
@@ -307,7 +307,6 @@ export function WalletWithdrawForm({
         <OperationState
           currency={limit.currency}
           onClose={onCancel}
-          goToConfirmOperation={goToConfirmOperation}
         />
       }
     </div>
diff --git a/packages/taler-util/src/errors.ts b/packages/taler-util/src/errors.ts
index 4399dbcf2..07a402413 100644
--- a/packages/taler-util/src/errors.ts
+++ b/packages/taler-util/src/errors.ts
@@ -78,7 +78,7 @@ export interface DetailsMap {
     stack?: string;
   };
   [TalerErrorCode.WALLET_BANK_INTEGRATION_PROTOCOL_VERSION_INCOMPATIBLE]: {
-    exchangeProtocolVersion: string;
+    bankProtocolVersion: string;
     walletProtocolVersion: string;
   };
   [TalerErrorCode.WALLET_CORE_API_OPERATION_UNKNOWN]: {
diff --git a/packages/taler-wallet-core/src/operations/withdraw.ts b/packages/taler-wallet-core/src/operations/withdraw.ts
index fb503d75f..2c9c95d4c 100644
--- a/packages/taler-wallet-core/src/operations/withdraw.ts
+++ b/packages/taler-wallet-core/src/operations/withdraw.ts
@@ -570,7 +570,7 @@ export async function getBankWithdrawalInfo(
     throw TalerError.fromDetail(
       TalerErrorCode.WALLET_BANK_INTEGRATION_PROTOCOL_VERSION_INCOMPATIBLE,
       {
-        exchangeProtocolVersion: config.version,
+        bankProtocolVersion: config.version,
         walletProtocolVersion: WALLET_BANK_INTEGRATION_PROTOCOL_VERSION,
       },
       "bank integration protocol version not compatible with wallet",
@@ -813,10 +813,10 @@ async function handleKycRequired(
             amlStatus === AmlStatus.normal || amlStatus === undefined
               ? WithdrawalGroupStatus.PendingKyc
               : amlStatus === AmlStatus.pending
-              ? WithdrawalGroupStatus.PendingAml
-              : amlStatus === AmlStatus.fronzen
-              ? WithdrawalGroupStatus.SuspendedAml
-              : assertUnreachable(amlStatus);
+                ? WithdrawalGroupStatus.PendingAml
+                : amlStatus === AmlStatus.fronzen
+                  ? WithdrawalGroupStatus.SuspendedAml
+                  : assertUnreachable(amlStatus);
 
           await tx.withdrawalGroups.put(wg2);
           const newTxState = computeWithdrawalTransactionStatus(wg2);
@@ -1145,8 +1145,7 @@ export async function updateWithdrawalDenoms(
         denom.verificationStatus === DenominationVerificationStatus.Unverified
       ) {
         logger.trace(
-          `Validating denomination (${current + 1}/${
-            denominations.length
+          `Validating denomination (${current + 1}/${denominations.length
           }) signature of ${denom.denomPubHash}`,
         );
         let valid = false;
@@ -1240,7 +1239,7 @@ async function queryReserve(
     if (
       resp.status === 404 &&
       result.talerErrorResponse.code ===
-        TalerErrorCode.EXCHANGE_RESERVES_STATUS_UNKNOWN
+      TalerErrorCode.EXCHANGE_RESERVES_STATUS_UNKNOWN
     ) {
       return { ready: false };
     } else {
@@ -1775,7 +1774,7 @@ export async function getExchangeWithdrawalInfo(
     ) {
       logger.warn(
         `wallet's support for exchange protocol version ${WALLET_EXCHANGE_PROTOCOL_VERSION} might be outdated ` +
-          `(exchange has ${exchangeDetails.protocolVersionRange}), checking for updates`,
+        `(exchange has ${exchangeDetails.protocolVersionRange}), checking for updates`,
       );
     }
   }
diff --git a/packages/taler-wallet-core/src/versions.ts b/packages/taler-wallet-core/src/versions.ts
index 8b9177bc3..022f4900d 100644
--- a/packages/taler-wallet-core/src/versions.ts
+++ b/packages/taler-wallet-core/src/versions.ts
@@ -29,7 +29,7 @@ export const WALLET_EXCHANGE_PROTOCOL_VERSION = "17:0:0";
 export const WALLET_MERCHANT_PROTOCOL_VERSION = "2:0:1";
 
 /**
- * Protocol version spoken with the merchant.
+ * Protocol version spoken with the bank.
  *
  * Uses libtool's current:revision:age versioning.
  */
-- 
cgit v1.2.3


From ea0738ccd585445d7e2080d9009025dde9cf22c5 Mon Sep 17 00:00:00 2001
From: Sebastian <sebasjm@gmail.com>
Date: Mon, 25 Sep 2023 14:49:47 -0300
Subject: better /config error

---
 .../demobank-ui/src/components/ErrorLoading.tsx    |  3 ++
 .../src/components/Transactions/state.ts           |  2 +-
 packages/demobank-ui/src/components/app.tsx        | 18 +++++++---
 packages/demobank-ui/src/declaration.d.ts          |  3 +-
 packages/demobank-ui/src/hooks/config.ts           | 40 +++++++++-------------
 .../demobank-ui/src/pages/AccountPage/state.ts     |  4 +--
 packages/demobank-ui/src/pages/BankFrame.tsx       | 14 ++++++--
 .../demobank-ui/src/pages/WithdrawalQRCode.tsx     |  1 -
 packages/web-util/src/hooks/index.ts               |  1 +
 packages/web-util/src/hooks/useNotifications.ts    | 11 ++++++
 10 files changed, 60 insertions(+), 37 deletions(-)

(limited to 'packages/demobank-ui/src/components/ErrorLoading.tsx')

diff --git a/packages/demobank-ui/src/components/ErrorLoading.tsx b/packages/demobank-ui/src/components/ErrorLoading.tsx
index a4faa4d5d..f83b61234 100644
--- a/packages/demobank-ui/src/components/ErrorLoading.tsx
+++ b/packages/demobank-ui/src/components/ErrorLoading.tsx
@@ -32,6 +32,9 @@ export function ErrorLoading({ error }: { error: HttpError<SandboxBackend.Sandbo
           <p class="text-sm font-medium text-red-800">{error.message}</p>
         </div>
       </div>
+        <div class="ml-3 flex-1 md:flex md:justify-between">
+          <p class="text-sm font-medium text-red-800">Got status "{error.info.status}" on {error.info.url}</p>
+        </div>
     </div>
     </div>
   );
diff --git a/packages/demobank-ui/src/components/Transactions/state.ts b/packages/demobank-ui/src/components/Transactions/state.ts
index 30c48aa45..4b62b005e 100644
--- a/packages/demobank-ui/src/components/Transactions/state.ts
+++ b/packages/demobank-ui/src/components/Transactions/state.ts
@@ -44,7 +44,7 @@ export function useComponentState({ account }: Props): State {
         cp.targetType === "bitcoin" ? `${cp.targetPath.substring(0, 6)}...` : undefined) ??
         "unkown";
 
-      const when = AbsoluteTime.fromMilliseconds(tx.date / 1000);
+      const when = AbsoluteTime.fromProtocolTimestamp(tx.date);
       const amount = Amounts.parse(tx.amount);
       const subject = tx.subject;
       return {
diff --git a/packages/demobank-ui/src/components/app.tsx b/packages/demobank-ui/src/components/app.tsx
index ebda31035..a587c6f1e 100644
--- a/packages/demobank-ui/src/components/app.tsx
+++ b/packages/demobank-ui/src/components/app.tsx
@@ -29,6 +29,8 @@ import { useEffect, useState } from "preact/hooks";
 import { Loading } from "./Loading.js";
 import { getInitialBackendBaseURL } from "../hooks/backend.js";
 import { BANK_INTEGRATION_PROTOCOL_VERSION, useConfigState } from "../hooks/config.js";
+import { ErrorLoading } from "./ErrorLoading.js";
+import { BankFrame } from "../pages/BankFrame.js";
 const WITH_LOCAL_STORAGE_CACHE = false;
 
 /**
@@ -76,12 +78,18 @@ function VersionCheck({ children }: { children: ComponentChildren }): VNode {
   if (checked === undefined) {
     return <Loading />
   }
-  if (checked === false) {
-    return <div>
-      the bank backend is not supported. supported version "{BANK_INTEGRATION_PROTOCOL_VERSION}"
-    </div>
+  if (typeof checked ===  "string") {
+    return <BankFrame>
+      the bank backend is not supported. supported version "{BANK_INTEGRATION_PROTOCOL_VERSION}", server version "{checked}"
+    </BankFrame>
   }
-  return <Fragment>{children}</Fragment>
+  if (checked === true) {
+    return <Fragment>{children}</Fragment>
+  }
+  
+  return <BankFrame>
+    <ErrorLoading error={checked}/>
+  </BankFrame>
 }
 
 function localStorageProvider(): Map<unknown, unknown> {
diff --git a/packages/demobank-ui/src/declaration.d.ts b/packages/demobank-ui/src/declaration.d.ts
index 8d729c1f7..d3d9e02ef 100644
--- a/packages/demobank-ui/src/declaration.d.ts
+++ b/packages/demobank-ui/src/declaration.d.ts
@@ -205,8 +205,7 @@ namespace SandboxBackend {
       // Transaction unique ID.  Matches
       // $transaction_id from the URI.
       row_id: number;
-      date: number;
-      // date: Timestamp;
+      date: Timestamp;
     }
 
     interface CreateBankAccountTransactionCreate {
diff --git a/packages/demobank-ui/src/hooks/config.ts b/packages/demobank-ui/src/hooks/config.ts
index 4b22e8ad3..4cf677d35 100644
--- a/packages/demobank-ui/src/hooks/config.ts
+++ b/packages/demobank-ui/src/hooks/config.ts
@@ -1,5 +1,5 @@
 import { LibtoolVersion } from "@gnu-taler/taler-util";
-import { useApiContext } from "@gnu-taler/web-util/browser";
+import { ErrorType, HttpError, HttpResponseServerError, RequestError, useApiContext } from "@gnu-taler/web-util/browser";
 import { useEffect, useState } from "preact/hooks";
 import { getInitialBackendBaseURL } from "./backend.js";
 
@@ -12,38 +12,32 @@ export const BANK_INTEGRATION_PROTOCOL_VERSION = "0:0:0";
 
 async function getConfigState(
   request: ReturnType<typeof useApiContext>["request"],
-): Promise<SandboxBackend.Config | undefined> {
-  try {
-    const url = getInitialBackendBaseURL();
-    const result = await request<SandboxBackend.Config>(
-      url,
-      `config`,
-    );
-    return result.data;
-  } catch (error) {
-    return undefined;
-  }
+): Promise<SandboxBackend.Config> {
+  const url = getInitialBackendBaseURL();
+  const result = await request<SandboxBackend.Config>(url, `config`);
+  return result.data;
 }
 
-export function useConfigState(): boolean | undefined {
-  const [checked, setChecked] = useState<boolean>()
+export function useConfigState(): undefined | true | string | HttpError<SandboxBackend.SandboxError> {
+  const [checked, setChecked] = useState<true | string | HttpError<SandboxBackend.SandboxError>>()
   const { request } = useApiContext();
 
   useEffect(() => {
-    
     getConfigState(request)
-      .then((result) => {
-        if (!result) {
-          setChecked(false)
+      .then((s) => {
+        const r = LibtoolVersion.compare(BANK_INTEGRATION_PROTOCOL_VERSION, s.version)
+        if (r?.compatible) {
+          setChecked(true);
         } else {
-          const r = LibtoolVersion.compare(BANK_INTEGRATION_PROTOCOL_VERSION, result.version)
-          setChecked(r?.compatible);
+          setChecked(s.version)
         }
       })
-      .catch((error) => {
-        setChecked(false);
+      .catch((error: unknown) => {
+        if (error instanceof RequestError) {
+          setChecked(error.cause);
+        }
       });
-  });
+  }, []);
 
   return checked;
 }
diff --git a/packages/demobank-ui/src/pages/AccountPage/state.ts b/packages/demobank-ui/src/pages/AccountPage/state.ts
index c212e7484..ca7e1d447 100644
--- a/packages/demobank-ui/src/pages/AccountPage/state.ts
+++ b/packages/demobank-ui/src/pages/AccountPage/state.ts
@@ -75,9 +75,7 @@ export function useComponentState({ account, goToBusinessAccount, goToConfirmOpe
     };
   }
 
-  // FIXME: balance
-  const balanceIsDebit = true;
-  // data.balance.credit_debit_indicator == "debit";
+  const balanceIsDebit = data.balance.credit_debit_indicator == "debit";
   const limit = balanceIsDebit
     ? Amounts.sub(debitThreshold, balance).amount
     : Amounts.add(balance, debitThreshold).amount;
diff --git a/packages/demobank-ui/src/pages/BankFrame.tsx b/packages/demobank-ui/src/pages/BankFrame.tsx
index c4f872679..5c43d2c3e 100644
--- a/packages/demobank-ui/src/pages/BankFrame.tsx
+++ b/packages/demobank-ui/src/pages/BankFrame.tsx
@@ -15,7 +15,7 @@
  */
 
 import { Amounts, Logger, PaytoUriIBAN, TranslatedString, parsePaytoUri, stringifyPaytoUri } from "@gnu-taler/taler-util";
-import { notifyError, useNotifications, useTranslationContext } from "@gnu-taler/web-util/browser";
+import { notifyError, notifyException, useNotifications, useTranslationContext } from "@gnu-taler/web-util/browser";
 import { ComponentChildren, Fragment, h, VNode } from "preact";
 import { StateUpdater, useEffect, useErrorBoundary, useState } from "preact/hooks";
 import { LangSelectorLikePy as LangSelector } from "../components/LangSelector.js";
@@ -54,7 +54,12 @@ export function BankFrame({
 
   useEffect(() => {
     if (error) {
-      notifyError(i18n.str`Internal error, please report.`, (error instanceof Error ? error.message : String(error)) as TranslatedString)
+      const desc = (error instanceof Error ? error.stack : String(error)) as TranslatedString
+      if (error instanceof Error) {
+        notifyException(i18n.str`Internal error, please report.`, error)
+      } else {
+        notifyError(i18n.str`Internal error, please report.`, String(error) as TranslatedString)
+      }
       resetError()
     }
   }, [error])
@@ -386,6 +391,11 @@ function StatusBanner(): VNode {
                   {n.message.description}
                 </div>
               }
+              {n.message.debug &&
+                <div class="mt-2 text-sm text-red-700 font-mono break-all">
+                    {n.message.debug}
+                </div>
+              }
             </div>
           case "info":
             return <div class="rounded-md bg-green-50 border-4 border-green-600 p-6">
diff --git a/packages/demobank-ui/src/pages/WithdrawalQRCode.tsx b/packages/demobank-ui/src/pages/WithdrawalQRCode.tsx
index 25c571e28..8f4e175f6 100644
--- a/packages/demobank-ui/src/pages/WithdrawalQRCode.tsx
+++ b/packages/demobank-ui/src/pages/WithdrawalQRCode.tsx
@@ -45,7 +45,6 @@ export function WithdrawalQRCode({
   withdrawUri,
   onClose,
 }: Props): VNode {
-  const [settings, updateSettings] = useSettings();
   const { i18n } = useTranslationContext();
   const result = useWithdrawalDetails(withdrawUri.withdrawalOperationId);
 
diff --git a/packages/web-util/src/hooks/index.ts b/packages/web-util/src/hooks/index.ts
index c29de9023..cc3267dbd 100644
--- a/packages/web-util/src/hooks/index.ts
+++ b/packages/web-util/src/hooks/index.ts
@@ -4,6 +4,7 @@ export { useMemoryStorage } from "./useMemoryStorage.js";
 export {
   useNotifications,
   notifyError,
+  notifyException,
   notifyInfo,
   notify,
   ErrorNotification,
diff --git a/packages/web-util/src/hooks/useNotifications.ts b/packages/web-util/src/hooks/useNotifications.ts
index 2f9df24f9..792095b06 100644
--- a/packages/web-util/src/hooks/useNotifications.ts
+++ b/packages/web-util/src/hooks/useNotifications.ts
@@ -36,6 +36,17 @@ export function notifyError(
     debug,
   });
 }
+export function notifyException(
+  title: TranslatedString,
+  ex: Error,
+) {
+  notify({
+    type: "error" as const,
+    title,
+    description: ex.message as TranslatedString,
+    debug: ex.stack,
+  });
+}
 export function notifyInfo(title: TranslatedString) {
   notify({
     type: "info" as const,
-- 
cgit v1.2.3


From 1708d49a2d5da1f325173a030695223e5a24e75c Mon Sep 17 00:00:00 2001
From: Sebastian <sebasjm@gmail.com>
Date: Fri, 29 Sep 2023 16:02:15 -0300
Subject: more ui

---
 packages/demobank-ui/dev.mjs                       |   2 +-
 packages/demobank-ui/src/components/Attention.tsx  |  59 +++++
 .../demobank-ui/src/components/ErrorLoading.tsx    |  22 +-
 .../src/components/Transactions/views.tsx          |  51 ++--
 packages/demobank-ui/src/demobank-ui-settings.js   |  21 ++
 packages/demobank-ui/src/hooks/access.ts           |   3 +-
 packages/demobank-ui/src/hooks/circuit.ts          |   2 +-
 packages/demobank-ui/src/hooks/settings.ts         |   3 +
 .../demobank-ui/src/pages/AccountPage/views.tsx    |  62 ++---
 packages/demobank-ui/src/pages/BankFrame.tsx       | 104 ++++----
 packages/demobank-ui/src/pages/HomePage.tsx        |   6 +-
 packages/demobank-ui/src/pages/LoginForm.tsx       |   8 +-
 .../demobank-ui/src/pages/OperationState/state.ts  |   4 +-
 .../demobank-ui/src/pages/OperationState/views.tsx |   5 +-
 packages/demobank-ui/src/pages/PaymentOptions.tsx  |   5 +-
 .../src/pages/PaytoWireTransferForm.tsx            | 268 ++++++++++++---------
 packages/demobank-ui/src/pages/QrCodeSection.tsx   |   1 -
 .../src/pages/UpdateAccountPassword.tsx            |   8 +-
 .../demobank-ui/src/pages/WalletWithdrawForm.tsx   | 183 +++++---------
 .../src/pages/WithdrawalConfirmationQuestion.tsx   |  14 +-
 .../demobank-ui/src/pages/WithdrawalQRCode.tsx     |  12 +-
 .../demobank-ui/src/pages/admin/AccountForm.tsx    |   8 +-
 packages/demobank-ui/src/pages/admin/Home.tsx      |   2 +
 .../demobank-ui/src/pages/admin/RemoveAccount.tsx  |  65 +----
 24 files changed, 448 insertions(+), 470 deletions(-)
 create mode 100644 packages/demobank-ui/src/components/Attention.tsx
 create mode 100644 packages/demobank-ui/src/demobank-ui-settings.js

(limited to 'packages/demobank-ui/src/components/ErrorLoading.tsx')

diff --git a/packages/demobank-ui/dev.mjs b/packages/demobank-ui/dev.mjs
index 9c09e5716..f29a05e49 100755
--- a/packages/demobank-ui/dev.mjs
+++ b/packages/demobank-ui/dev.mjs
@@ -18,7 +18,7 @@
 import { serve } from "@gnu-taler/web-util/node";
 import { initializeDev } from "@gnu-taler/web-util/build";
 
-const devEntryPoints = ["src/stories.tsx", "src/index.tsx"];
+const devEntryPoints = ["src/stories.tsx", "src/index.tsx", "src/demobank-ui-settings.js"];
 
 const build = initializeDev({
   type: "development",
diff --git a/packages/demobank-ui/src/components/Attention.tsx b/packages/demobank-ui/src/components/Attention.tsx
new file mode 100644
index 000000000..3313e5796
--- /dev/null
+++ b/packages/demobank-ui/src/components/Attention.tsx
@@ -0,0 +1,59 @@
+import { TranslatedString } from "@gnu-taler/taler-util";
+import { ComponentChildren, Fragment, VNode, h } from "preact";
+import { assertUnreachable } from "./Routing.js";
+
+interface Props { 
+  type?: "info" | "success" | "warning" | "danger", 
+  onClose?: () => void, 
+  title: TranslatedString, 
+  children?: ComponentChildren ,
+}
+export function Attention({ type = "info", title, children, onClose }: Props): VNode {
+  return <div class={`group attention-${type} mt-2`}>
+    <div class="rounded-md group-[.attention-info]:bg-blue-50 group-[.attention-warning]:bg-yellow-50 group-[.attention-danger]:bg-red-50 group-[.attention-success]:bg-green-50 p-4 shadow">
+      <div class="flex">
+        <div >
+          <svg xmlns="http://www.w3.org/2000/svg" stroke="none" viewBox="0 0 24 24" fill="currentColor" class="w-8 h-8 group-[.attention-info]:text-blue-400 group-[.attention-warning]:text-yellow-400 group-[.attention-danger]:text-red-400 group-[.attention-success]:text-green-400">
+            {(() => {
+              switch (type) {
+                case "info":
+                  return <path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a.75.75 0 000 1.5h.253a.25.25 0 01.244.304l-.459 2.066A1.75 1.75 0 0010.747 15H11a.75.75 0 000-1.5h-.253a.25.25 0 01-.244-.304l.459-2.066A1.75 1.75 0 009.253 9H9z" />
+                case "warning":
+                  return <path fill-rule="evenodd" d="M9.401 3.003c1.155-2 4.043-2 5.197 0l7.355 12.748c1.154 2-.29 4.5-2.599 4.5H4.645c-2.309 0-3.752-2.5-2.598-4.5L9.4 3.003zM12 8.25a.75.75 0 01.75.75v3.75a.75.75 0 01-1.5 0V9a.75.75 0 01.75-.75zm0 8.25a.75.75 0 100-1.5.75.75 0 000 1.5z" />
+                case "danger":
+                  return <path fill-rule="evenodd" d="M2.25 12c0-5.385 4.365-9.75 9.75-9.75s9.75 4.365 9.75 9.75-4.365 9.75-9.75 9.75S2.25 17.385 2.25 12zM12 8.25a.75.75 0 01.75.75v3.75a.75.75 0 01-1.5 0V9a.75.75 0 01.75-.75zm0 8.25a.75.75 0 100-1.5.75.75 0 000 1.5z" />
+                case "success":
+                  return <path fill-rule="evenodd" d="M7.493 18.75c-.425 0-.82-.236-.975-.632A7.48 7.48 0 016 15.375c0-1.75.599-3.358 1.602-4.634.151-.192.373-.309.6-.397.473-.183.89-.514 1.212-.924a9.042 9.042 0 012.861-2.4c.723-.384 1.35-.956 1.653-1.715a4.498 4.498 0 00.322-1.672V3a.75.75 0 01.75-.75 2.25 2.25 0 012.25 2.25c0 1.152-.26 2.243-.723 3.218-.266.558.107 1.282.725 1.282h3.126c1.026 0 1.945.694 2.054 1.715.045.422.068.85.068 1.285a11.95 11.95 0 01-2.649 7.521c-.388.482-.987.729-1.605.729H14.23c-.483 0-.964-.078-1.423-.23l-3.114-1.04a4.501 4.501 0 00-1.423-.23h-.777zM2.331 10.977a11.969 11.969 0 00-.831 4.398 12 12 0 00.52 3.507c.26.85 1.084 1.368 1.973 1.368H4.9c.445 0 .72-.498.523-.898a8.963 8.963 0 01-.924-3.977c0-1.708.476-3.305 1.302-4.666.245-.403-.028-.959-.5-.959H4.25c-.832 0-1.612.453-1.918 1.227z" />
+                default:
+                  assertUnreachable(type)
+              }
+            })()}
+          </svg>
+        </div>
+        <div class="ml-3 w-full">
+          <h3 class="text-sm group-hover:text-white font-bold group-[.attention-info]:text-blue-800 group-[.attention-success]:text-green-800 group-[.attention-warning]:text-yellow-800 group-[.attention-danger]:text-red-800">
+            {title}
+          </h3>
+          <div class="mt-2 text-sm group-[.attention-info]:text-blue-700 group-[.attention-warning]:text-yellow-700 group-[.attention-danger]:text-red-700 group-[.attention-success]:text-green-700">
+            {children}
+          </div>
+        </div>
+        {onClose &&
+          <div>
+            <button type="button" class="font-semibold items-center rounded bg-transparent px-2 py-1 text-xs text-gray-900  hover:bg-gray-50"
+              onClick={(e) => {
+                e.preventDefault();
+                onClose();
+              }}
+            >
+              <svg class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
+                <path d="M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0 101.06-1.06L11.06 10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.22z" />
+              </svg>
+            </button>
+          </div>
+        }
+      </div>
+    </div>
+
+  </div>
+}
diff --git a/packages/demobank-ui/src/components/ErrorLoading.tsx b/packages/demobank-ui/src/components/ErrorLoading.tsx
index f83b61234..ee62671ce 100644
--- a/packages/demobank-ui/src/components/ErrorLoading.tsx
+++ b/packages/demobank-ui/src/components/ErrorLoading.tsx
@@ -17,25 +17,13 @@
 
 import { HttpError, useTranslationContext } from "@gnu-taler/web-util/browser";
 import { h, VNode } from "preact";
+import { Attention } from "./Attention.js";
+import { TranslatedString } from "@gnu-taler/taler-util";
 
 export function ErrorLoading({ error }: { error: HttpError<SandboxBackend.SandboxError> }): VNode {
   const { i18n } = useTranslationContext()
-  return (
-    <div><div class="rounded-md bg-red-50 p-4">
-      <div class="flex">
-        <div class="flex-shrink-0">
-          <svg class="h-5 w-5 text-red-400" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
-            <path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.28 7.22a.75.75 0 00-1.06 1.06L8.94 10l-1.72 1.72a.75.75 0 101.06 1.06L10 11.06l1.72 1.72a.75.75 0 101.06-1.06L11.06 10l1.72-1.72a.75.75 0 00-1.06-1.06L10 8.94 8.28 7.22z" clip-rule="evenodd" />
-          </svg>
-        </div>
-        <div class="ml-3 flex-1 md:flex md:justify-between">
-          <p class="text-sm font-medium text-red-800">{error.message}</p>
-        </div>
-      </div>
-        <div class="ml-3 flex-1 md:flex md:justify-between">
-          <p class="text-sm font-medium text-red-800">Got status "{error.info.status}" on {error.info.url}</p>
-        </div>
-    </div>
-    </div>
+  return (<Attention type="danger" title={error.message as TranslatedString}>
+    <p class="text-sm font-medium text-red-800">Got status "{error.info.status}" on {error.info.url}</p>
+  </Attention>
   );
 }
diff --git a/packages/demobank-ui/src/components/Transactions/views.tsx b/packages/demobank-ui/src/components/Transactions/views.tsx
index f8b2e3113..f92c874f3 100644
--- a/packages/demobank-ui/src/components/Transactions/views.tsx
+++ b/packages/demobank-ui/src/components/Transactions/views.tsx
@@ -19,6 +19,7 @@ import { useTranslationContext } from "@gnu-taler/web-util/browser";
 import { State } from "./index.js";
 import { format, isToday } from "date-fns";
 import { Amounts } from "@gnu-taler/taler-util";
+import { useEffect, useRef } from "preact/hooks";
 
 export function LoadingUriView({ error }: State.LoadingUriError): VNode {
   const { i18n } = useTranslationContext();
@@ -55,9 +56,9 @@ export function ReadyView({ transactions, onNext, onPrev }: State.Ready): VNode
           <thead>
             <tr>
               <th scope="col" class="pl-2 py-3.5 text-left text-sm font-semibold text-gray-900 ">{i18n.str`Date`}</th>
-              <th scope="col" class="pl-2 py-3.5 text-left text-sm font-semibold text-gray-900 lg:table-cell">{i18n.str`Amount`}</th>
-              <th scope="col" class="pl-2 py-3.5 text-left text-sm font-semibold text-gray-900 lg:table-cell">{i18n.str`Counterpart`}</th>
-              <th scope="col" class="pl-2 py-3.5 text-left text-sm font-semibold text-gray-900 lg:table-cell">{i18n.str`Subject`}</th>
+              <th scope="col" class="hidden sm:table-cell pl-2 py-3.5 text-left text-sm font-semibold text-gray-900 ">{i18n.str`Amount`}</th>
+              <th scope="col" class="hidden sm:table-cell pl-2 py-3.5 text-left text-sm font-semibold text-gray-900 ">{i18n.str`Counterpart`}</th>
+              <th scope="col" class="pl-2 py-3.5 text-left text-sm font-semibold text-gray-900 ">{i18n.str`Subject`}</th>
             </tr>
           </thead>
           <tbody>
@@ -69,22 +70,38 @@ export function ReadyView({ transactions, onNext, onPrev }: State.Ready): VNode
                   </th>
                 </tr>
                 {txs.map(item => {
+                  const time = item.when.t_ms === "never" ? "" : format(item.when.t_ms, "HH:mm:ss")
+                  const amount = <Fragment>
+                    {item.negative ? "-" : ""}
+                    {item.amount ? (
+                      `${Amounts.stringifyValue(item.amount)} ${item.amount.currency
+                      }`
+                    ) : (
+                      <span style={{ color: "grey" }}>&lt;{i18n.str`invalid value`}&gt;</span>
+                    )}
+                  </Fragment>
                   return (<tr key={idx}>
                     <td class="relative py-2 pl-2 pr-2 text-sm ">
-                      <div class="font-medium text-gray-900">{item.when.t_ms === "never"
-                        ? ""
-                        : format(item.when.t_ms, "HH:mm:ss")}</div>
+                      <div class="font-medium text-gray-900">{time}</div>
+                      <dl class="font-normal sm:hidden">
+                        <dt class="sr-only sm:hidden"><i18n.Translate>Amount</i18n.Translate></dt>
+                        <dd class="mt-1 truncate text-gray-700">
+                          {item.negative ? i18n.str`sent` : i18n.str`received`} {item.amount ? (
+                            `${Amounts.stringifyValue(item.amount)}`
+                          ) : (
+                            <span style={{ color: "grey" }}>&lt;{i18n.str`invalid value`}&gt;</span>
+                          )}</dd>
+                        <dt class="sr-only sm:hidden"><i18n.Translate>Counterpart</i18n.Translate></dt>
+                        <dd class="mt-1 truncate text-gray-500 sm:hidden">
+                        {item.negative ? i18n.str`to` : i18n.str`from`} {item.counterpart}
+                        </dd>
+                      </dl>
                     </td>
                     <td data-negative={item.negative ? "true" : "false"}
-                      class="px-3 py-3.5 text-sm text-gray-500 data-[negative=false]:text-green-600 data-[negative=true]:text-red-600">
-                      {item.negative ? "-" : ""}
-                      {item.amount ? (
-                        `${Amounts.stringifyValue(item.amount)} ${item.amount.currency
-                        }`
-                      ) : (
-                        <span style={{ color: "grey" }}>&lt;{i18n.str`invalid value`}&gt;</span>
-                      )}</td>
-                    <td class="px-3 py-3.5 text-sm text-gray-500">{item.counterpart}</td>
+                      class="hidden sm:table-cell px-3 py-3.5 text-sm text-gray-500 data-[negative=false]:text-green-600 data-[negative=true]:text-red-600">
+                      {amount}
+                    </td>
+                    <td class="hidden sm:table-cell px-3 py-3.5 text-sm text-gray-500">{item.counterpart}</td>
                     <td class="px-3 py-3.5 text-sm text-gray-500 break-all min-w-md">{item.subject}</td>
                   </tr>)
                 })}
@@ -94,8 +111,8 @@ export function ReadyView({ transactions, onNext, onPrev }: State.Ready): VNode
           </tbody>
 
         </table>
-       
-       <nav class="flex items-center justify-between border-t border-gray-200 bg-white px-4 py-3 sm:px-6 rounded-lg" aria-label="Pagination">
+
+        <nav class="flex items-center justify-between border-t border-gray-200 bg-white px-4 py-3 sm:px-6 rounded-lg" aria-label="Pagination">
           <div class="flex flex-1 justify-between sm:justify-end">
             <button
               class="relative disabled:bg-gray-100 disabled:text-gray-500 inline-flex items-center rounded-md bg-white px-3 py-2 text-sm font-semibold text-gray-900 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus-visible:outline-offset-0"
diff --git a/packages/demobank-ui/src/demobank-ui-settings.js b/packages/demobank-ui/src/demobank-ui-settings.js
new file mode 100644
index 000000000..8a0961831
--- /dev/null
+++ b/packages/demobank-ui/src/demobank-ui-settings.js
@@ -0,0 +1,21 @@
+// Values for development environment
+
+/**
+ * Global settings for the demobank UI.
+ */
+localStorage.setItem("bank-base-url", "http://bank.taler.test/");
+
+globalThis.talerDemobankSettings = {
+  backendBaseURL: "http://bank.taler.test/",
+  allowRegistrations: true,
+  showDemoNav: true,
+  simplePasswordForRandomAccounts: true,
+  allowRandomAccountCreation: true,
+  bankName: "Taler DEVELOPMENT Bank",
+  // Names and links for other demo sites to show in the navbar
+  demoSites: [
+    ["Exchange", "https://Exchnage.taler.test/"],
+    ["Bank", "https://bank-ui.taler.test/"],
+    ["Merchant", "https://merchant.taler.test/"],
+  ],
+};
diff --git a/packages/demobank-ui/src/hooks/access.ts b/packages/demobank-ui/src/hooks/access.ts
index 20fd64bfa..154c43ae6 100644
--- a/packages/demobank-ui/src/hooks/access.ts
+++ b/packages/demobank-ui/src/hooks/access.ts
@@ -70,7 +70,7 @@ export function useAccessAPI(): AccessAPI {
         contentType: "json",
       },
     );
-    await mutateAll(/.*accounts\/.*\/transactions.*/);
+    await mutateAll(/.*accounts\/.*/);
     return res;
   };
   const deleteAccount = async (): Promise<HttpResponseOk<void>> => {
@@ -382,7 +382,6 @@ export function useTransactions(
     loadMore: () => {
       if (!afterData || isReachingEnd) return;
       // if (afterData.data.transactions.length < MAX_RESULT_SIZE) {
-      // console.log("load more", page)
       const l = afterData.data.transactions[afterData.data.transactions.length-1]
       setStart(String(l.row_id));
       // }
diff --git a/packages/demobank-ui/src/hooks/circuit.ts b/packages/demobank-ui/src/hooks/circuit.ts
index 82caafdf2..5dba60951 100644
--- a/packages/demobank-ui/src/hooks/circuit.ts
+++ b/packages/demobank-ui/src/hooks/circuit.ts
@@ -435,7 +435,7 @@ export function useBusinessAccounts(
     HttpResponseOk<SandboxBackend.Circuit.CircuitAccounts>,
     RequestError<SandboxBackend.SandboxError>
   >(
-    [`circuit-api/accounts`, args?.page, PAGE_SIZE, args?.account],
+    [`accounts`, args?.page, PAGE_SIZE, args?.account],
     sandboxAccountsFetcher,
     {
       refreshInterval: 0,
diff --git a/packages/demobank-ui/src/hooks/settings.ts b/packages/demobank-ui/src/hooks/settings.ts
index 5f004c6d4..ad853f9d7 100644
--- a/packages/demobank-ui/src/hooks/settings.ts
+++ b/packages/demobank-ui/src/hooks/settings.ts
@@ -33,6 +33,7 @@ interface Settings {
   showInstallWallet: boolean;
   maxWithdrawalAmount: number;
   fastWithdrawal: boolean;
+  showDebugInfo: boolean;
 }
 
 export const codecForSettings = (): Codec<Settings> =>
@@ -42,6 +43,7 @@ export const codecForSettings = (): Codec<Settings> =>
     .property("showDemoDescription", (codecForBoolean()))
     .property("showInstallWallet", (codecForBoolean()))
     .property("fastWithdrawal", (codecForBoolean()))
+    .property("showDebugInfo", (codecForBoolean()))
     .property("maxWithdrawalAmount", codecForNumber())
     .build("Settings");
 
@@ -52,6 +54,7 @@ const defaultSettings: Settings = {
   showInstallWallet: true,
   maxWithdrawalAmount: 25,
   fastWithdrawal: false,
+  showDebugInfo: false,
 };
 
 const DEMOBANK_SETTINGS_KEY = buildStorageKey(
diff --git a/packages/demobank-ui/src/pages/AccountPage/views.tsx b/packages/demobank-ui/src/pages/AccountPage/views.tsx
index 83846be90..483cb579a 100644
--- a/packages/demobank-ui/src/pages/AccountPage/views.tsx
+++ b/packages/demobank-ui/src/pages/AccountPage/views.tsx
@@ -16,10 +16,10 @@
 
 import { useTranslationContext } from "@gnu-taler/web-util/browser";
 import { Fragment, VNode, h } from "preact";
+import { Attention } from "../../components/Attention.js";
 import { Transactions } from "../../components/Transactions/index.js";
 import { useBusinessAccountDetails } from "../../hooks/circuit.js";
 import { useSettings } from "../../hooks/settings.js";
-import { bankUiSettings } from "../../settings.js";
 import { PaymentOptions } from "../PaymentOptions.js";
 import { State } from "./index.js";
 
@@ -31,53 +31,27 @@ export function InvalidIbanView({ error }: State.InvalidIban) {
 
 const IS_PUBLIC_ACCOUNT_ENABLED = false
 
-
 function ShowDemoInfo(): VNode {
   const { i18n } = useTranslationContext();
   const [settings, updateSettings] = useSettings();
   if (!settings.showDemoDescription) return <Fragment />
-  return <div class="rounded-md bg-blue-50 p-4">
-    <div class="flex">
-      <div class="flex-shrink-0">
-        <svg class="h-5 w-5 text-blue-400" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
-          <path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a.75.75 0 000 1.5h.253a.25.25 0 01.244.304l-.459 2.066A1.75 1.75 0 0010.747 15H11a.75.75 0 000-1.5h-.253a.25.25 0 01-.244-.304l.459-2.066A1.75 1.75 0 009.253 9H9z" clip-rule="evenodd" />
-        </svg>
-      </div>
-      <div class="ml-3">
-        <h3 class="text-sm font-bold text-blue-800">
-          <i18n.Translate>This is a demo bank!</i18n.Translate>
-        </h3>
-        <div class="mt-2 text-sm text-blue-700">
-          {IS_PUBLIC_ACCOUNT_ENABLED ? (
-            <i18n.Translate>
-              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{" "}
-              <a href="/public-accounts">Public Accounts</a>.
-            </i18n.Translate>
-          ) : (
-            <i18n.Translate>
-              This part of the demo shows how a bank that supports Taler
-              directly would work.
-            </i18n.Translate>
-          )}
-          <p class="mt-3 text-sm flex justify-end">
-            <button type="button" class="inline-flex font-semibold items-center rounded bg-white px-2 py-1 text-xs text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 hover:bg-gray-50"
-              onClick={(e) => {
-                e.preventDefault();
-                updateSettings("showDemoDescription", false);
-              }}
-            >
-              <svg class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
-                <path d="M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0 101.06-1.06L11.06 10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.22z" />
-              </svg>
-            </button>
-          </p>
-
-        </div>
-      </div>
-    </div>
-  </div>
+  return <Attention title={i18n.str`This is a demo bank`} onClose={() => {
+    updateSettings("showDemoDescription", false);
+  }}>
+    {IS_PUBLIC_ACCOUNT_ENABLED ? (
+      <i18n.Translate>
+        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{" "}
+        <a href="/public-accounts">Public Accounts</a>.
+      </i18n.Translate>
+    ) : (
+      <i18n.Translate>
+        This part of the demo shows how a bank that supports Taler
+        directly would work.
+      </i18n.Translate>
+    )}
+  </Attention>
 }
 
 export function ReadyView({ account, limit, goToBusinessAccount, goToConfirmOperation }: State.Ready): VNode<{}> {
diff --git a/packages/demobank-ui/src/pages/BankFrame.tsx b/packages/demobank-ui/src/pages/BankFrame.tsx
index 15ef8a036..29334cae4 100644
--- a/packages/demobank-ui/src/pages/BankFrame.tsx
+++ b/packages/demobank-ui/src/pages/BankFrame.tsx
@@ -15,7 +15,7 @@
  */
 
 import { Amounts, Logger, PaytoUriIBAN, TranslatedString, parsePaytoUri, stringifyPaytoUri } from "@gnu-taler/taler-util";
-import { notifyError, notifyException, useNotifications, useTranslationContext } from "@gnu-taler/web-util/browser";
+import { NotificationMessage, notifyError, notifyException, useNotifications, useTranslationContext } from "@gnu-taler/web-util/browser";
 import { ComponentChildren, Fragment, h, VNode } from "preact";
 import { StateUpdater, useEffect, useErrorBoundary, useState } from "preact/hooks";
 import { LangSelector } from "../components/LangSelector.js";
@@ -26,6 +26,7 @@ import { useSettings } from "../hooks/settings.js";
 import { CopyButton, CopyIcon } from "../components/CopyButton.js";
 import logo from "../assets/logo-2021.svg";
 import { useAccountDetails } from "../hooks/access.js";
+import { Attention } from "../components/Attention.js";
 
 const GIT_HASH = typeof __GIT_HASH__ !== "undefined" ? __GIT_HASH__ : undefined;
 const VERSION = typeof __VERSION__ !== "undefined" ? __VERSION__ : undefined;
@@ -108,7 +109,7 @@ export function BankFrame({
                   setOpen(!open)
                 }}>
                 <span class="absolute -inset-0.5"></span>
-                <span class="sr-only">Open main menu</span>
+                <span class="sr-only">Open settings</span>
                 <svg class="block h-10 w-10" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true">
                   <path stroke-linecap="round" stroke-linejoin="round" d="M3.75 6.75h16.5M3.75 12h16.5m-16.5 5.25h16.5" />
                 </svg>
@@ -227,6 +228,22 @@ export function BankFrame({
                                     </button>
                                   </div>
                                 </li>
+                                <li class="mt-2">
+                                  <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">
+                                        <i18n.Translate>Show debug info</i18n.Translate>
+                                      </span>
+                                    </span>
+                                    <button type="button" data-enabled={settings.showDebugInfo} class="bg-indigo-600 data-[enabled=false]:bg-gray-200 relative inline-flex h-6 w-11 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" aria-labelledby="availability-label" aria-describedby="availability-description"
+
+                                      onClick={() => {
+                                        updateSettings("showDebugInfo", !settings.showDebugInfo);
+                                      }}>
+                                      <span aria-hidden="true" data-enabled={settings.showDebugInfo} class="translate-x-5 data-[enabled=false]:translate-x-0 pointer-events-none inline-block h-5 w-5 transform rounded-full bg-white shadow ring-0 transition duration-200 ease-in-out"></span>
+                                    </button>
+                                  </div>
+                                </li>
                                 <li class="mt-2">
                                   <div class="flex items-center justify-between">
                                     <span class="flex flex-grow flex-col">
@@ -286,10 +303,10 @@ export function BankFrame({
       }
     </div >
 
+    <StatusBanner />
     <main class="-mt-32 flex-1">
       <div class="mx-auto max-w-7xl px-4 pb-12 sm:px-6 lg:px-8">
         <div class="rounded-lg bg-white px-5 py-6 shadow sm:px-6">
-          <StatusBanner />
           {children}
         </div>
       </div>
@@ -301,79 +318,46 @@ export function BankFrame({
   );
 }
 
+function MaybeShowDebugInfo({ info }: { info: any }): VNode {
+  const [settings] = useSettings()
+  if (settings.showDebugInfo) {
+    return <pre class="whitespace-break-spaces ">
+    {info}
+  </pre>
+  }
+  return <Fragment /> 
+}
+
 
 function StatusBanner(): VNode {
   const notifs = useNotifications()
-  return <div
-    class="fixed top-10 z-20 ml-4 mr-4"
-  > {
+  if (notifs.length === 0) return <Fragment />
+  return <div class="fixed z-20 w-full p-4"> {
       notifs.map(n => {
         switch (n.message.type) {
           case "error":
-            return <div class="rounded-md bg-red-50 p-4">
-              <div class="flex">
-                <div class="flex-shrink-0">
-                  <svg class="h-5 w-5 text-red-400" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
-                    <path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.28 7.22a.75.75 0 00-1.06 1.06L8.94 10l-1.72 1.72a.75.75 0 101.06 1.06L10 11.06l1.72 1.72a.75.75 0 101.06-1.06L11.06 10l1.72-1.72a.75.75 0 00-1.06-1.06L10 8.94 8.28 7.22z" clip-rule="evenodd" />
-                  </svg>
-                </div>
-                <div class="ml-3 flex-1 md:flex md:justify-between">
-                  <p class="text-sm font-medium text-red-800">{n.message.title}</p>
-                </div>
-                <div>
-                  <p class="text-sm">
-                    <button type="button" class="inline-flex font-semibold items-center rounded bg-white px-2 py-1 text-xs text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 hover:bg-gray-50"
-                      onClick={(e) => {
-                        e.preventDefault();
-                        n.remove()
-                      }}
-                    >
-                      Close
-                      <svg class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
-                        <path d="M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0 101.06-1.06L11.06 10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.22z" />
-                      </svg>
-                    </button>
-                  </p>
-                </div>
-              </div>
+            return <Attention type="danger" title={n.message.title} onClose={() => {
+              n.remove()
+            }}>
               {n.message.description &&
                 <div class="mt-2 text-sm text-red-700">
                   {n.message.description}
                 </div>
               }
+              <MaybeShowDebugInfo info={n.message.debug} />
+              {/* <a href="#" class="text-gray-500">
+                show debug info
+              </a>
               {n.message.debug &&
                 <div class="mt-2 text-sm text-red-700 font-mono break-all">
                   {n.message.debug}
                 </div>
-              }
-            </div>
+              } */}
+            </Attention>
           case "info":
-            return <div class="rounded-md bg-green-50 border-4 border-green-600 p-6">
-              <div class="flex">
-                <div class="flex-shrink-0">
-                  <svg class="h-8 w-8 text-green-400" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
-                    <path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.857-9.809a.75.75 0 00-1.214-.882l-3.483 4.79-1.88-1.88a.75.75 0 10-1.06 1.061l2.5 2.5a.75.75 0 001.137-.089l4-5.5z" clip-rule="evenodd" />
-                  </svg>
-                </div>
-                <div class="ml-3 flex-1 md:flex md:justify-between">
-                  <h3 class="text-lg font-medium text-green-800">{n.message.title}</h3>
-
-                  <p class="mt-3 text-sm md:ml-6 md:mt-0">
-                    <button type="button" class="inline-flex font-semibold items-center rounded bg-white px-2 py-1 text-md text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 hover:bg-gray-50"
-                      onClick={(e) => {
-                        e.preventDefault();
-                        n.remove();
-                      }}
-                    >
-                      <svg class="h-8 w-8" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
-                        <path d="M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0 101.06-1.06L11.06 10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.22z" />
-                      </svg>
-                    </button>
-                  </p>
-                </div>
-
-              </div>
-            </div>
+            return <Attention type="success" title={n.message.title} onClose={() => {
+              n.remove();
+            }} />
         }
       })}
   </div>
diff --git a/packages/demobank-ui/src/pages/HomePage.tsx b/packages/demobank-ui/src/pages/HomePage.tsx
index d945d80d1..95144f086 100644
--- a/packages/demobank-ui/src/pages/HomePage.tsx
+++ b/packages/demobank-ui/src/pages/HomePage.tsx
@@ -137,8 +137,8 @@ export function handleNotOkResult(
           const errorData = result.payload;
           notify({
             type: "error",
-            title: i18n.str`Could not load due to a client error`,
-            description: errorData?.error?.description as TranslatedString,
+            title: i18n.str`Could not load due to a request error`,
+            description: i18n.str`Request to url "${result.info.url}" returned ${result.info.status}`,
             debug: JSON.stringify(result),
           });
           break;
@@ -174,7 +174,7 @@ export function handleNotOkResult(
           assertUnreachable(result);
         }
       }
-      route("/")
+      // route("/")
       return <div>error</div>;
     }
     return <div />;
diff --git a/packages/demobank-ui/src/pages/LoginForm.tsx b/packages/demobank-ui/src/pages/LoginForm.tsx
index 14d261622..3ea94b899 100644
--- a/packages/demobank-ui/src/pages/LoginForm.tsx
+++ b/packages/demobank-ui/src/pages/LoginForm.tsx
@@ -23,6 +23,7 @@ import { useBackendContext } from "../context/backend.js";
 import { useCredentialsChecker } from "../hooks/useCredentialsChecker.js";
 import { bankUiSettings } from "../settings.js";
 import { undefinedIfEmpty } from "../utils.js";
+import { doAutoFocus } from "./PaytoWireTransferForm.js";
 
 
 /**
@@ -98,8 +99,8 @@ export function LoginForm({ onRegister }: { onRegister?: () => void }): VNode {
               });
             } else {
               saveError({
-                title: i18n.str`Could not load due to a client error`,
-                // description: cause.payload.error.description,
+                title: i18n.str`Could not load due to a request error`,
+                description: i18n.str`Request to url "${cause.info.url}" returned ${cause.info.status}`,
                 debug: JSON.stringify(cause.payload),
               });
             }
@@ -159,8 +160,7 @@ export function LoginForm({ onRegister }: { onRegister?: () => void }): VNode {
             </label>
             <div class="mt-2">
               <input
-                ref={ref}
-                autoFocus
+                ref={doAutoFocus}
                 type="text"
                 name="username"
                 id="username"
diff --git a/packages/demobank-ui/src/pages/OperationState/state.ts b/packages/demobank-ui/src/pages/OperationState/state.ts
index 56e79f9ab..4be680377 100644
--- a/packages/demobank-ui/src/pages/OperationState/state.ts
+++ b/packages/demobank-ui/src/pages/OperationState/state.ts
@@ -118,7 +118,9 @@ export function useComponentState({ currency, onClose }: Props): utils.Recursive
     try {
       setBusy({})
       await confirmWithdrawal(wid);
-      notifyInfo(i18n.str`Wire transfer completed!`)
+      if (!settings.showWithdrawalSuccess) {
+        notifyInfo(i18n.str`Wire transfer completed!`)
+      }
     } catch (error) {
       if (error instanceof RequestError) {
         notify(
diff --git a/packages/demobank-ui/src/pages/OperationState/views.tsx b/packages/demobank-ui/src/pages/OperationState/views.tsx
index 93b3694d7..2cb7385db 100644
--- a/packages/demobank-ui/src/pages/OperationState/views.tsx
+++ b/packages/demobank-ui/src/pages/OperationState/views.tsx
@@ -267,13 +267,12 @@ export function ConfirmedView({ error, onClose }: State.Confirmed) {
         </div>
         <div class="mt-3 text-center sm:mt-5">
           <h3 class="text-base font-semibold leading-6 text-gray-900" id="modal-title">
-            <i18n.Translate>Withdrawal OK</i18n.Translate>
+            <i18n.Translate>Withdrawal confirmed</i18n.Translate>
           </h3>
           <div class="mt-2">
             <p class="text-sm text-gray-500">
               <i18n.Translate>
-                The wire transfer to the Taler exchange bank's account is completed, now the
-                exchange will send the requested amount into your GNU Taler wallet.
+                The wire transfer to the Taler operator has been initiated. You will soon receive the requested amount in your Taler wallet.
               </i18n.Translate>
             </p>
           </div>
diff --git a/packages/demobank-ui/src/pages/PaymentOptions.tsx b/packages/demobank-ui/src/pages/PaymentOptions.tsx
index 49419d0dc..fef272831 100644
--- a/packages/demobank-ui/src/pages/PaymentOptions.tsx
+++ b/packages/demobank-ui/src/pages/PaymentOptions.tsx
@@ -30,7 +30,7 @@ export function PaymentOptions({ limit, goToConfirmOperation }: { limit: AmountJ
   const { i18n } = useTranslationContext();
   const [settings] = useSettings();
 
-  const [tab, setTab] = useState<"charge-wallet" | "wire-transfer" | undefined>();
+  const [tab, setTab] = useState<"charge-wallet" | "wire-transfer" | undefined>("wire-transfer");
 
   return (
     <div class="mt-2">
@@ -82,7 +82,7 @@ export function PaymentOptions({ limit, goToConfirmOperation }: { limit: AmountJ
                   <i18n.Translate>another bank account</i18n.Translate>
                 </span>
                 <span id="project-type-1-description-0" class="mt-1 flex items-center text-sm text-gray-500">
-                  <i18n.Translate>Make a wire transfer to an account which you know the address.</i18n.Translate>
+                  <i18n.Translate>Make a wire transfer to an account which you know the bank account number</i18n.Translate>
                 </span>
               </span>
             </span>
@@ -108,6 +108,7 @@ export function PaymentOptions({ limit, goToConfirmOperation }: { limit: AmountJ
             limit={limit}
             onSuccess={() => {
               notifyInfo(i18n.str`Wire transfer created!`);
+              setTab(undefined)
             }}
             onCancel={() => {
               setTab(undefined)
diff --git a/packages/demobank-ui/src/pages/PaytoWireTransferForm.tsx b/packages/demobank-ui/src/pages/PaytoWireTransferForm.tsx
index 5f5a6ce3b..785dc4264 100644
--- a/packages/demobank-ui/src/pages/PaytoWireTransferForm.tsx
+++ b/packages/demobank-ui/src/pages/PaytoWireTransferForm.tsx
@@ -55,10 +55,11 @@ export function PaytoWireTransferForm({
   onCancel: (() => void) | undefined;
   limit: AmountJson;
 }): VNode {
-  const [isRawPayto, setIsRawPayto] = useState(false);
-  const [iban, setIban] = useState<string | undefined>(undefined);
-  const [subject, setSubject] = useState<string | undefined>(undefined);
-  const [amount, setAmount] = useState<string | undefined>(undefined);
+  const [isRawPayto, setIsRawPayto] = useState(true);
+  // FIXME: remove this
+  const [iban, setIban] = useState<string | undefined>("DE4745461198061");
+  const [subject, setSubject] = useState<string | undefined>("ASD");
+  const [amount, setAmount] = useState<string | undefined>("1.00001");
 
   const [rawPaytoInput, rawPaytoInputSetter] = useState<string | undefined>(
     undefined,
@@ -76,17 +77,17 @@ export function PaytoWireTransferForm({
 
   const errorsWire = undefinedIfEmpty({
     iban: !iban
-      ? i18n.str`Missing IBAN`
+      ? i18n.str`required`
       : !IBAN_REGEX.test(iban)
         ? i18n.str`IBAN should have just uppercased letters and numbers`
         : validateIBAN(iban, i18n),
-    subject: !subject ? i18n.str`Missing subject` : undefined,
+    subject: !subject ? i18n.str`required` : undefined,
     amount: !trimmedAmountStr
-      ? i18n.str`Missing amount`
+      ? i18n.str`required`
       : !parsedAmount
-        ? i18n.str`Amount is not valid`
+        ? i18n.str`not valid`
         : Amounts.isZero(parsedAmount)
-          ? i18n.str`Should be greater than 0`
+          ? i18n.str`should be greater than 0`
           : Amounts.cmp(limit, parsedAmount) === -1
             ? i18n.str`balance is not enough`
             : undefined,
@@ -101,14 +102,14 @@ export function PaytoWireTransferForm({
       ? i18n.str`required`
       : !parsed
         ? i18n.str`does not follow the pattern`
-        : !parsed.params.amount
-          ? i18n.str`use the "amount" parameter to specify the amount to be transferred`
-          : Amounts.parse(parsed.params.amount) === undefined
-            ? i18n.str`the amount is not valid`
-            : !parsed.params.message
-              ? i18n.str`use the "message" parameter to specify a reference text for the transfer`
-              : !parsed.isKnown || parsed.targetType !== "iban"
-                ? i18n.str`only "IBAN" target are supported`
+        : !parsed.isKnown || parsed.targetType !== "iban"
+          ? i18n.str`only "IBAN" target are supported`
+          : !parsed.params.amount
+            ? i18n.str`use the "amount" parameter to specify the amount to be transferred`
+            : Amounts.parse(parsed.params.amount) === undefined
+              ? i18n.str`the amount is not valid`
+              : !parsed.params.message
+                ? i18n.str`use the "message" parameter to specify a reference text for the transfer`
                 : !IBAN_REGEX.test(parsed.iban)
                   ? i18n.str`IBAN should have just uppercased letters and numbers`
                   : validateIBAN(parsed.iban, i18n),
@@ -159,6 +160,9 @@ export function PaytoWireTransferForm({
   }
 
   return (<div class="grid grid-cols-1 gap-x-8 gap-y-8 pt-10 md:grid-cols-3 bg-gray-100 my-4 px-4 pb-4 rounded-lg">
+    {/**
+     * FIXME: Scan a qr code
+     */}
     <div class="px-4 sm:px-0">
       <h2 class="text-base font-semibold leading-7 text-gray-900">
         {title}
@@ -167,6 +171,17 @@ export function PaytoWireTransferForm({
         <div class="px-4 mt-4 grid grid-cols-1 gap-y-6 sm:grid-cols-1 sm:gap-x-4">
           <label class={"relative flex cursor-pointer rounded-lg border bg-white p-4 shadow-sm focus:outline-none" + (!isRawPayto ? "border-indigo-600 ring-2 ring-indigo-600" : "border-gray-300")}>
             <input type="radio" name="project-type" value="Newsletter" class="sr-only" aria-labelledby="project-type-0-label" aria-describedby="project-type-0-description-0 project-type-0-description-1" onChange={() => {
+              if (parsed && parsed.isKnown && parsed.targetType === "iban") {
+                setIban(parsed.iban)
+                const amount = Amounts.parse(parsed.params["amount"])
+                if (amount) {
+                  setAmount(Amounts.stringifyValue(amount))
+                }
+                const subject = parsed.params["subject"]
+                if (subject) {
+                  setSubject(subject)
+                }
+              }
               setIsRawPayto(false)
             }} />
             <span class="flex flex-1">
@@ -180,12 +195,22 @@ export function PaytoWireTransferForm({
 
           <label class={"relative flex cursor-pointer rounded-lg border bg-white p-4 shadow-sm focus:outline-none" + (isRawPayto ? "border-indigo-600 ring-2 ring-indigo-600" : "border-gray-300")}>
             <input type="radio" name="project-type" value="Existing Customers" class="sr-only" aria-labelledby="project-type-1-label" aria-describedby="project-type-1-description-0 project-type-1-description-1" onChange={() => {
+              if (iban) {
+                const payto = buildPayto("iban", iban, undefined)
+                if (parsedAmount) {
+                  payto.params["amount"] = Amounts.stringify(parsedAmount)
+                }
+                if (subject) {
+                  payto.params["message"] = subject
+                }
+                rawPaytoInputSetter(stringifyPaytoUri(payto))
+              }
               setIsRawPayto(true)
             }} />
             <span class="flex flex-1">
               <span class="flex flex-col">
                 <span class="block text-sm  font-medium text-gray-900">
-                  <i18n.Translate>using the payto:// format</i18n.Translate>
+                  <i18n.Translate>Import payto:// URI</i18n.Translate>
                 </span>
               </span>
             </span>
@@ -195,7 +220,7 @@ export function PaytoWireTransferForm({
     </div>
 
     <form
-      class="bg-white shadow-sm ring-1 ring-gray-900/5 sm:rounded-xl md:col-span-2"
+      class="bg-white shadow-sm ring-1 ring-gray-900/5 sm:rounded-xl md:col-span-2 w-fit mx-auto"
       autoCapitalize="none"
       autoCorrect="off"
       onSubmit={e => {
@@ -203,105 +228,106 @@ export function PaytoWireTransferForm({
       }}
     >
       <div class="px-4 py-6 sm:p-8">
-        <div class="grid max-w-2xl grid-cols-1 gap-x-6 gap-y-8 sm:grid-cols-6">
-          {!isRawPayto ?
-            <Fragment>
-
-              <div class="sm:col-span-5">
-                <label for="iban" class="block text-sm font-medium leading-6 text-gray-900">{i18n.str`Account number`}</label>
-                <div class="mt-2">
-                  <input
-                    ref={ref}
-                    type="text"
-                    class="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
-                    name="iban"
-                    id="iban"
-                    value={iban ?? ""}
-                    placeholder="CC0123456789"
-                    autocomplete="off"
-                    required
-                    pattern={ibanRegex}
-                    onInput={(e): void => {
-                      setIban(e.currentTarget.value);
-                    }}
-                  />
-                  <ShowInputErrorLabel
-                    message={errorsWire?.iban}
-                    isDirty={iban !== undefined}
-                  />
-                </div>
-                <p class="mt-2 text-sm text-gray-500" >the receiver of the money</p>
-              </div>
+        {!isRawPayto ?
+          <div class="grid max-w-xs grid-cols-1 gap-x-6 gap-y-8 sm:grid-cols-6">
 
-              <div class="sm:col-span-5">
-                <label for="subject" class="block text-sm font-medium leading-6 text-gray-900">{i18n.str`Transfer subject`}</label>
-                <div class="mt-2">
-                  <input
-                    type="text"
-                    class="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
-                    name="subject"
-                    id="subject"
-                    autocomplete="off"
-                    placeholder="subject"
-                    value={subject ?? ""}
-                    required
-                    onInput={(e): void => {
-                      setSubject(e.currentTarget.value);
-                    }}
-                  />
-                  <ShowInputErrorLabel
-                    message={errorsWire?.subject}
-                    isDirty={subject !== undefined}
-                  />
-                </div>
-                <p class="mt-2 text-sm text-gray-500" >some text to identify the transfer</p>
+            <div class="sm:col-span-5">
+              <label for="iban" class="block text-sm font-medium leading-6 text-gray-900">{i18n.str`Recipient`}</label>
+              <div class="mt-2">
+                <input
+                  ref={focus ? doAutoFocus : undefined}
+                  type="text"
+                  class="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
+                  name="iban"
+                  id="iban"
+                  value={iban ?? ""}
+                  placeholder="CC0123456789"
+                  autocomplete="off"
+                  required
+                  pattern={ibanRegex}
+                  onInput={(e): void => {
+                    setIban(e.currentTarget.value.toUpperCase());
+                  }}
+                />
+                <ShowInputErrorLabel
+                  message={errorsWire?.iban}
+                  isDirty={iban !== undefined}
+                />
               </div>
+              <p class="mt-2 text-sm text-gray-500" >
+                <i18n.Translate>IBAN of the recipient's account</i18n.Translate>
+              </p>
+            </div>
 
-              <div class="sm:col-span-5">
-                <label for="amount" class="block text-sm font-medium leading-6 text-gray-900">{i18n.str`Amount`}</label>
-                <Amount
-                  name="amount"
-                  currency={limit.currency}
-                  value={trimmedAmountStr}
-                  onChange={(d) => {
-                    setAmount(d)
+            <div class="sm:col-span-5">
+              <label for="subject" class="block text-sm font-medium leading-6 text-gray-900">{i18n.str`Transfer subject`}</label>
+              <div class="mt-2">
+                <input
+                  type="text"
+                  class="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
+                  name="subject"
+                  id="subject"
+                  autocomplete="off"
+                  placeholder="subject"
+                  value={subject ?? ""}
+                  required
+                  onInput={(e): void => {
+                    setSubject(e.currentTarget.value);
                   }}
                 />
                 <ShowInputErrorLabel
                   message={errorsWire?.subject}
                   isDirty={subject !== undefined}
                 />
-                <p class="mt-2 text-sm text-gray-500" >amount to transfer</p>
               </div>
+              <p class="mt-2 text-sm text-gray-500" >some text to identify the transfer</p>
+            </div>
 
-            </Fragment> :
-            <Fragment>
-              <div class="sm:col-span-6">
-                <label for="address" class="block text-sm font-medium leading-6 text-gray-900">{i18n.str`payto URI:`}</label>
-                <div class="mt-2">
-                  <input
-                    name="address"
-                    id="address"
-                    type="text"
-                    size={50}
-                    class="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6" ref={ref}
-                    value={rawPaytoInput ?? ""}
-                    required
-                    placeholder={i18n.str`payto://iban/[receiver-iban]?message=[subject]&amount=[${limit.currency}:X.Y]`}
-                    onInput={(e): void => {
-                      rawPaytoInputSetter(e.currentTarget.value);
-                    }}
-                  />
-                  <ShowInputErrorLabel
-                    message={errorsPayto?.rawPaytoInput}
-                    isDirty={rawPaytoInput !== undefined}
-                  />
-                </div>
-              </div>
+            <div class="sm:col-span-5">
+              <label for="amount" class="block text-sm font-medium leading-6 text-gray-900">{i18n.str`Amount`}</label>
+              <Amount
+                name="amount"
+                left
+                currency={limit.currency}
+                value={trimmedAmountStr}
+                onChange={(d) => {
+                  setAmount(d)
+                }}
+              />
+              <ShowInputErrorLabel
+                message={errorsWire?.amount}
+                isDirty={subject !== undefined}
+              />
+              <p class="mt-2 text-sm text-gray-500" >amount to transfer</p>
+            </div>
 
-            </Fragment>
-          }
-        </div>
+          </div> :
+          <div class="grid max-w-2xl grid-cols-1 gap-x-6 gap-y-8 sm:grid-cols-6 w-full">
+            <div class="sm:col-span-6">
+              <label for="address" class="block text-sm font-medium leading-6 text-gray-900">{i18n.str`payto URI:`}</label>
+              <div class="mt-2">
+                <textarea
+                  ref={focus ? doAutoFocus : undefined}
+                  name="address"
+                  id="address"
+                  type="textarea"
+                  rows={3}
+                  class="block overflow-hidden w-64 rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
+                  value={rawPaytoInput ?? ""}
+                  required
+                  placeholder={i18n.str`payto://iban/[receiver-iban]?message=[subject]&amount=[${limit.currency}:X.Y]`}
+                  onInput={(e): void => {
+                    rawPaytoInputSetter(e.currentTarget.value);
+                  }}
+                />
+                <ShowInputErrorLabel
+                  message={errorsPayto?.rawPaytoInput}
+                  isDirty={rawPaytoInput !== undefined}
+                />
+              </div>
+            </div>
+          </div>
+        }
       </div>
       <div class="flex items-center justify-between gap-x-6 border-t border-gray-900/10 px-4 py-4 sm:px-8">
         {onCancel ?
@@ -328,17 +354,37 @@ export function PaytoWireTransferForm({
   )
 
 }
+
+/**
+ * Show the element when the load ended
+ * @param element 
+ */
+export function doAutoFocus(element: HTMLElement | null) {
+  if (element) {
+    window.requestIdleCallback(() => {
+      element.focus()
+      element.scrollIntoView({
+        behavior: "smooth",
+        block: "center",
+        inline: "center"
+      })
+    })
+  }
+}
+
 export function Amount(
   {
     currency,
     name,
     value,
     error,
+    left,
     onChange,
   }: {
     error?: string;
     currency: string;
     name: string;
+    left?: boolean | undefined,
     value: string | undefined;
     onChange?: (s: string) => void;
   },
@@ -346,13 +392,16 @@ export function Amount(
 ): VNode {
   return (
     <div class="mt-2">
-      <div class="relative rounded-md shadow-sm">
-        <div class="pointer-events-none absolute inset-y-0 flex items-center pl-3">
+      <div class="flex rounded-md shadow-sm border-0 ring-1 ring-inset ring-gray-300 focus:ring-2 focus:ring-inset focus:ring-indigo-600">
+        <div
+          class="pointer-events-none inset-y-0 flex items-center px-3"
+        >
           <span class="text-gray-500 sm:text-sm">{currency}</span>
         </div>
         <input
           type="number"
-          class="text-right block w-full rounded-md border-0 py-1.5 pl-16 text-gray-900 ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
+          data-left={left}
+          class="text-right rounded-md rounded-l-none data-[left=true]:text-left w-full py-1.5 pl-3 text-gray-900  placeholder:text-gray-400  sm:text-sm sm:leading-6"
           placeholder="0.00" aria-describedby="price-currency"
           ref={ref}
           name={name}
@@ -371,3 +420,4 @@ export function Amount(
     </div>
   );
 }
+
diff --git a/packages/demobank-ui/src/pages/QrCodeSection.tsx b/packages/demobank-ui/src/pages/QrCodeSection.tsx
index 0a5a386ae..6a50d4ef3 100644
--- a/packages/demobank-ui/src/pages/QrCodeSection.tsx
+++ b/packages/demobank-ui/src/pages/QrCodeSection.tsx
@@ -86,7 +86,6 @@ export function QrCodeSection({
           </h3>
           <div class="mt-4">
             <a href={talerWithdrawUri}
-              // class="text-sm font-semibold leading-6 text-gray-900 btn "
               class="inline-flex items-center  disabled:opacity-50 disabled:cursor-default cursor-pointer rounded-md bg-indigo-600 px-3 py-2 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"
             >
               <i18n.Translate>Click here to start</i18n.Translate>
diff --git a/packages/demobank-ui/src/pages/UpdateAccountPassword.tsx b/packages/demobank-ui/src/pages/UpdateAccountPassword.tsx
index d19c411f3..46f4fe0ef 100644
--- a/packages/demobank-ui/src/pages/UpdateAccountPassword.tsx
+++ b/packages/demobank-ui/src/pages/UpdateAccountPassword.tsx
@@ -5,6 +5,7 @@ import { useEffect, useRef, useState } from "preact/hooks";
 import { ShowInputErrorLabel } from "../components/ShowInputErrorLabel.js";
 import { useAdminAccountAPI, useBusinessAccountDetails } from "../hooks/circuit.js";
 import { buildRequestErrorMessage, undefinedIfEmpty } from "../utils.js";
+import { doAutoFocus } from "./PaytoWireTransferForm.js";
 
 export function UpdateAccountPassword({
   account,
@@ -27,11 +28,6 @@ export function UpdateAccountPassword({
   const [password, setPassword] = useState<string | undefined>();
   const [repeat, setRepeat] = useState<string | undefined>();
 
-  const ref = useRef<HTMLInputElement>(null);
-  useEffect(() => {
-    if (focus) ref.current?.focus();
-  }, [focus]);
-
   if (!result.ok) {
     if (result.loading || result.type === ErrorType.TIMEOUT) {
       return onLoadNotOk(result);
@@ -96,7 +92,7 @@ export function UpdateAccountPassword({
               </label>
               <div class="mt-2">
                 <input
-                  ref={ref}
+                  ref={focus ? doAutoFocus : undefined}
                   type="password"
                   class="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 data-[error=true]:ring-red-500 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
                   name="password"
diff --git a/packages/demobank-ui/src/pages/WalletWithdrawForm.tsx b/packages/demobank-ui/src/pages/WalletWithdrawForm.tsx
index 3c5ee34fd..7357223b7 100644
--- a/packages/demobank-ui/src/pages/WalletWithdrawForm.tsx
+++ b/packages/demobank-ui/src/pages/WalletWithdrawForm.tsx
@@ -29,14 +29,15 @@ import {
   notifyError,
   useTranslationContext,
 } from "@gnu-taler/web-util/browser";
-import { VNode, h } from "preact";
+import { Fragment, VNode, h } from "preact";
 import { forwardRef } from "preact/compat";
 import { useEffect, useRef, useState } from "preact/hooks";
 import { useAccessAPI } from "../hooks/access.js";
 import { buildRequestErrorMessage, undefinedIfEmpty } from "../utils.js";
-import { Amount } from "./PaytoWireTransferForm.js";
+import { Amount, doAutoFocus } from "./PaytoWireTransferForm.js";
 import { useSettings } from "../hooks/settings.js";
 import { OperationState } from "./OperationState/index.js";
+import { Attention } from "../components/Attention.js";
 
 const logger = new Logger("WalletWithdrawForm");
 const RefAmount = forwardRef(Amount);
@@ -53,47 +54,13 @@ function OldWithdrawalForm({ goToConfirmOperation, limit, onCancel, focus }: {
 
   const { createWithdrawal } = useAccessAPI();
   const [amountStr, setAmountStr] = useState<string | undefined>(`${settings.maxWithdrawalAmount}`);
-  const ref = useRef<HTMLInputElement>(null);
-  useEffect(() => {
-    if (focus) ref.current?.focus();
-  }, [focus]);
 
   if (!!settings.currentWithdrawalOperationId) {
-    return <div>
-
-      <div class="rounded-md bg-yellow-50 ring-yellow-2 p-4">
-        <div class="flex">
-          <div class="flex-shrink-0">
-            <svg class="h-5 w-5 text-yellow-300" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
-              <path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a.75.75 0 000 1.5h.253a.25.25 0 01.244.304l-.459 2.066A1.75 1.75 0 0010.747 15H11a.75.75 0 000-1.5h-.253a.25.25 0 01-.244-.304l.459-2.066A1.75 1.75 0 009.253 9H9z" clip-rule="evenodd" />
-            </svg>
-          </div>
-          <div class="ml-3">
-            <h3 class="text-sm font-bold text-yellow-800">
-              <i18n.Translate>There is an operation already</i18n.Translate>
-            </h3>
-            <div class="mt-2 text-sm text-yellow-700">
-              <p>
-                <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>
-              </p>
-            </div>
-
-          </div>
-        </div>
-      </div >
-      <div class="flex justify-end gap-x-6 border-t border-gray-900/10 px-4 py-4 sm:px-8 " >
-        <button type="button" class="text-sm font-semibold leading-6 text-gray-900 bg-white p-2 rounded-sm"
-          onClick={() => {
-            updateSettings("currentWithdrawalOperationId", undefined)
-            onCancel()
-          }}
-        >
-          <i18n.Translate>Cancel</i18n.Translate>
-        </button>
-      </div>
-    </div>
+    return <Attention type="warning" title={i18n.str`There is an operation already`}>
+      <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>
+    </Attention>
   }
 
   const trimmedAmountStr = amountStr?.trim();
@@ -157,8 +124,8 @@ function OldWithdrawalForm({ goToConfirmOperation, limit, onCancel, focus }: {
       e.preventDefault()
     }}
   >
-    <div class="px-4 py-6 sm:p-8">
-      <div class="grid max-w-2xl grid-cols-1 gap-x-6 gap-y-8 sm:grid-cols-6">
+    <div class="px-4 py-6 ">
+      <div class="grid max-w-xs grid-cols-1 gap-x-6 gap-y-8 sm:grid-cols-6">
         <div class="sm:col-span-5">
           <label for="withdraw-amount">{i18n.str`Amount`}</label>
           <RefAmount
@@ -169,51 +136,53 @@ function OldWithdrawalForm({ goToConfirmOperation, limit, onCancel, focus }: {
               setAmountStr(v);
             }}
             error={errors?.amount}
-            ref={ref}
+            ref={focus ? doAutoFocus : undefined}
           />
         </div>
-        <div class="sm:col-span-5">
-          <span class="isolate inline-flex rounded-md shadow-sm">
-            <button type="button"
-              class="relative               inline-flex px-6 py-4 text-sm items-center rounded-l-md bg-white text-gray-900 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus:z-10"
-              onClick={(e) => {
-                e.preventDefault();
-                setAmountStr("50.00")
-              }}
-            >
-              50.00
-            </button>
-            <button type="button"
-              class="relative -ml-px -mr-px inline-flex px-6 py-4 text-sm items-center              bg-white text-gray-900 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus:z-10"
-              onClick={(e) => {
-                e.preventDefault();
-                setAmountStr("25.00")
-              }}
-            >
+      </div>
+      <div class="mt-4">
+        <div class="sm:inline">
 
-              25.00
-            </button>
-            <button type="button"
-              class="relative -ml-px -mr-px inline-flex px-6 py-4 text-sm items-center              bg-white text-gray-900 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus:z-10"
-              onClick={(e) => {
-                e.preventDefault();
-                setAmountStr("10.00")
-              }}
-            >
-              10.00
-            </button>
-            <button type="button"
-              class="relative               inline-flex px-6 py-4 text-sm items-center rounded-r-md bg-white  text-gray-900 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus:z-10"
-              onClick={(e) => {
-                e.preventDefault();
-                setAmountStr("5.00")
-              }}
-            >
-              5.00
-            </button>
-          </span>
-        </div>
+          <button type="button"
+            class="               inline-flex px-6 py-4 text-sm items-center rounded-l-md bg-white text-gray-900 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus:z-10"
+            onClick={(e) => {
+              e.preventDefault();
+              setAmountStr("50.00")
+            }}
+          >
+            50.00
+          </button>
+          <button type="button"
+            class=" -ml-px -mr-px inline-flex px-6 py-4 text-sm items-center rounded-r-md sm:rounded-none             bg-white text-gray-900 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus:z-10"
+            onClick={(e) => {
+              e.preventDefault();
+              setAmountStr("25.00")
+            }}
+          >
 
+            25.00
+          </button>
+        </div>
+        <div class="mt-4 sm:inline">
+          <button type="button"
+            class=" -ml-px -mr-px inline-flex px-6 py-4 text-sm items-center rounded-l-md sm:rounded-none             bg-white text-gray-900 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus:z-10"
+            onClick={(e) => {
+              e.preventDefault();
+              setAmountStr("10.00")
+            }}
+          >
+            10.00
+          </button>
+          <button type="button"
+            class="               inline-flex px-6 py-4 text-sm items-center rounded-r-md bg-white  text-gray-900 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus:z-10"
+            onClick={(e) => {
+              e.preventDefault();
+              setAmountStr("5.00")
+            }}
+          >
+            5.00
+          </button>
+        </div>
       </div>
     </div>
     <div class="flex items-center justify-between gap-x-6 border-t border-gray-900/10 px-4 py-4 sm:px-8">
@@ -255,46 +224,20 @@ export function WalletWithdrawForm({
     <div class="px-4 sm:px-0">
       <h2 class="text-base font-semibold leading-7 text-gray-900"><i18n.Translate>Prepare your wallet</i18n.Translate></h2>
       <p class="mt-1 text-sm text-gray-500">
-        <i18n.Translate>After using your wallet you will confirm or cancel the operation.</i18n.Translate>
+        <i18n.Translate>After using your wallet you will need to confirm or cancel the operation on this site.</i18n.Translate>
       </p>
     </div>
 
     <div class="col-span-2">
-      {settings.showInstallWallet && <div class="rounded-md bg-blue-50 ring-blue-2 ring-2 p-4">
-        <div class="flex">
-          <div class="flex-shrink-0">
-            <svg class="h-5 w-5 text-blue-300" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
-              <path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a.75.75 0 000 1.5h.253a.25.25 0 01.244.304l-.459 2.066A1.75 1.75 0 0010.747 15H11a.75.75 0 000-1.5h-.253a.25.25 0 01-.244-.304l.459-2.066A1.75 1.75 0 009.253 9H9z" clip-rule="evenodd" />
-            </svg>
-          </div>
-          <div class="ml-3">
-            <h3 class="text-sm font-bold text-blue-800">
-              <i18n.Translate>You need a GNU Taler Wallet</i18n.Translate>
-            </h3>
-            <div class="mt-2 text-sm text-blue-700">
-              <p>
-                <i18n.Translate>
-                  If you dont have one yet you can follow the instruction <a target="_blank" rel="noreferrer noopener" class="font-semibold text-blue-700 hover:text-blue-600" href="https://taler.net/en/wallet.html">here</a>
-                </i18n.Translate>
-              </p>
-              <p class="mt-3 text-sm flex justify-end">
-                <button type="button" class="inline-flex font-semibold items-center rounded bg-white px-2 py-1 text-xs text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 hover:bg-gray-50"
-                  onClick={(e) => {
-                    e.preventDefault();
-                    updateSettings("showInstallWallet", false);
-                  }}
-                >
-                  I know
-                  <svg class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
-                    <path d="M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0 101.06-1.06L11.06 10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.22z" />
-                  </svg>
-                </button>
-              </p>
-
-            </div>
-          </div>
-        </div>
-      </div>}
+      {settings.showInstallWallet &&
+        <Attention title={i18n.str`You need a GNU Taler Wallet`} onClose={() => {
+          updateSettings("showInstallWallet", false);
+        }}>
+          <i18n.Translate>
+            If you don't have one yet you can follow the instruction <a target="_blank" rel="noreferrer noopener" class="font-semibold text-blue-700 hover:text-blue-600" href="https://taler.net/en/wallet.html">here</a>
+          </i18n.Translate>
+        </Attention>
+      }
 
       {!settings.fastWithdrawal ?
         <OldWithdrawalForm
diff --git a/packages/demobank-ui/src/pages/WithdrawalConfirmationQuestion.tsx b/packages/demobank-ui/src/pages/WithdrawalConfirmationQuestion.tsx
index d160a88b3..208d4b859 100644
--- a/packages/demobank-ui/src/pages/WithdrawalConfirmationQuestion.tsx
+++ b/packages/demobank-ui/src/pages/WithdrawalConfirmationQuestion.tsx
@@ -37,6 +37,7 @@ import { useMemo, useState } from "preact/hooks";
 import { ShowInputErrorLabel } from "../components/ShowInputErrorLabel.js";
 import { useAccessAnonAPI } from "../hooks/access.js";
 import { buildRequestErrorMessage, undefinedIfEmpty } from "../utils.js";
+import { useSettings } from "../hooks/settings.js";
 
 const logger = new Logger("WithdrawalConfirmationQuestion");
 
@@ -59,6 +60,7 @@ export function WithdrawalConfirmationQuestion({
   withdrawUri,
 }: Props): VNode {
   const { i18n } = useTranslationContext();
+  const [settings, updateSettings] = useSettings()
 
   const captchaNumbers = useMemo(() => {
     return {
@@ -87,7 +89,9 @@ export function WithdrawalConfirmationQuestion({
       await confirmWithdrawal(
         withdrawUri.withdrawalOperationId,
       );
-      notifyInfo(i18n.str`Wire transfer completed!`)
+      if (!settings.showWithdrawalSuccess) {
+        notifyInfo(i18n.str`Wire transfer completed!`)
+      }
     } catch (error) {
       if (error instanceof RequestError) {
         notify(
@@ -203,7 +207,7 @@ export function WithdrawalConfirmationQuestion({
 
             <div class="grid grid-cols-1 gap-x-8 gap-y-8 pt-10 md:grid-cols-3 bg-gray-100 my-4 px-4 pb-4 rounded-lg">
               <div class="px-4 sm:px-0">
-                <h2 class="text-base font-semibold text-gray-900"><i18n.Translate>Answer the next question to authorize the wire transfer</i18n.Translate></h2>
+                <h2 class="text-base font-semibold text-gray-900"><i18n.Translate>Answer the next question to authorize the wire transfer.</i18n.Translate></h2>
               </div>
               <form
                 class="bg-white shadow-sm ring-1 ring-gray-900/5 sm:rounded-xl md:col-span-2"
@@ -311,14 +315,10 @@ export function WithdrawalConfirmationQuestion({
 
                     }
                   })()}
-                  <div class="px-4 py-2 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-0">
-                    <dt class="text-sm font-medium leading-6 text-gray-900">Withdrawal identification</dt>
-                    <dd class="mt-1 text-sm leading-6 text-gray-700 sm:col-span-2 sm:mt-0 break-words">{details.reserve}</dd>
-                  </div>
                   <div class="px-4 py-2 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-0">
                     <dt class="text-sm font-medium leading-6 text-gray-900">Amount</dt>
                     <dd class="mt-1 text-sm leading-6 text-gray-700 sm:col-span-2 sm:mt-0">
-                      {Amounts.stringifyValue(details.amount)}
+                      {Amounts.currencyOf(details.amount)} {Amounts.stringifyValue(details.amount)}
                     </dd>
                   </div>
                 </dl>
diff --git a/packages/demobank-ui/src/pages/WithdrawalQRCode.tsx b/packages/demobank-ui/src/pages/WithdrawalQRCode.tsx
index 8f4e175f6..c8efc033b 100644
--- a/packages/demobank-ui/src/pages/WithdrawalQRCode.tsx
+++ b/packages/demobank-ui/src/pages/WithdrawalQRCode.tsx
@@ -94,20 +94,12 @@ export function WithdrawalQRCode({
         </div>
         <div class="mt-3 text-center sm:mt-5">
           <h3 class="text-base font-semibold leading-6 text-gray-900" id="modal-title">
-            <i18n.Translate>Withdrawal OK</i18n.Translate>
+            <i18n.Translate>Withdrawal confirmed</i18n.Translate>
           </h3>
           <div class="mt-2">
             <p class="text-sm text-gray-500">
               <i18n.Translate>
-                The wire transfer to the Taler exchange bank's account is completed, now the
-                exchange will send the requested amount into your GNU Taler wallet.
-              </i18n.Translate>
-            </p>
-          </div>
-          <div class="mt-2">
-            <p >
-              <i18n.Translate>
-                You can close this page now or continue to the account page.
+                The wire transfer to the Taler operator has been initiated. You will soon receive the requested amount in your Taler wallet.
               </i18n.Translate>
             </p>
           </div>
diff --git a/packages/demobank-ui/src/pages/admin/AccountForm.tsx b/packages/demobank-ui/src/pages/admin/AccountForm.tsx
index 02df824a2..ed8bf610d 100644
--- a/packages/demobank-ui/src/pages/admin/AccountForm.tsx
+++ b/packages/demobank-ui/src/pages/admin/AccountForm.tsx
@@ -4,6 +4,7 @@ import { PartialButDefined, RecursivePartial, WithIntermediate, undefinedIfEmpty
 import { useEffect, useRef, useState } from "preact/hooks";
 import { useTranslationContext } from "@gnu-taler/web-util/browser";
 import { buildPayto, parsePaytoUri } from "@gnu-taler/taler-util";
+import { doAutoFocus } from "../PaytoWireTransferForm.js";
 
 const IBAN_REGEX = /^[A-Z][A-Z0-9]*$/;
 const EMAIL_REGEX =
@@ -37,10 +38,6 @@ export function AccountForm({
     RecursivePartial<typeof initial> | undefined
   >(undefined);
   const { i18n } = useTranslationContext();
-  const ref = useRef<HTMLInputElement>(null);
-  useEffect(() => {
-    if (focus) ref.current?.focus();
-  }, [focus]);
 
   function updateForm(newForm: typeof initial): void {
 
@@ -97,7 +94,6 @@ export function AccountForm({
       <div class="px-4 py-6 sm:p-8">
         <div class="grid max-w-2xl grid-cols-1 gap-x-6 gap-y-8 sm:grid-cols-6">
 
-
           <div class="sm:col-span-5">
             <label
               class="block text-sm font-medium leading-6 text-gray-900"
@@ -108,7 +104,7 @@ export function AccountForm({
             </label>
             <div class="mt-2">
               <input
-                ref={ref}
+                ref={focus ? doAutoFocus : undefined}
                 type="text"
                 class="block w-full disabled:bg-gray-100 rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 data-[error=true]:ring-red-500 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
                 name="username"
diff --git a/packages/demobank-ui/src/pages/admin/Home.tsx b/packages/demobank-ui/src/pages/admin/Home.tsx
index ffa559097..d50ff14b4 100644
--- a/packages/demobank-ui/src/pages/admin/Home.tsx
+++ b/packages/demobank-ui/src/pages/admin/Home.tsx
@@ -10,6 +10,7 @@ import { AdminAccount } from "./Account.js";
 import { AccountList } from "./AccountList.js";
 import { CreateNewAccount } from "./CreateNewAccount.js";
 import { RemoveAccount } from "./RemoveAccount.js";
+import { Transactions } from "../../components/Transactions/index.js";
 
 /**
  * Query account information and show QR code if there is pending withdrawal
@@ -141,6 +142,7 @@ export function AdminHome({ onRegister }: Props): VNode {
 
       <AdminAccount onRegister={onRegister} />
 
+      <Transactions account="admin"/>
     </Fragment>
   );
 }
\ No newline at end of file
diff --git a/packages/demobank-ui/src/pages/admin/RemoveAccount.tsx b/packages/demobank-ui/src/pages/admin/RemoveAccount.tsx
index 1e5370afc..b323b0d01 100644
--- a/packages/demobank-ui/src/pages/admin/RemoveAccount.tsx
+++ b/packages/demobank-ui/src/pages/admin/RemoveAccount.tsx
@@ -6,6 +6,8 @@ import { Amounts, HttpStatusCode, TranslatedString } from "@gnu-taler/taler-util
 import { buildRequestErrorMessage, undefinedIfEmpty } from "../../utils.js";
 import { useEffect, useRef, useState } from "preact/hooks";
 import { ShowInputErrorLabel } from "../../components/ShowInputErrorLabel.js";
+import { Attention } from "../../components/Attention.js";
+import { doAutoFocus } from "../PaytoWireTransferForm.js";
 
 export function RemoveAccount({
   account,
@@ -36,47 +38,15 @@ export function RemoveAccount({
     }
     return onLoadNotOk(result);
   }
-  const ref = useRef<HTMLInputElement>(null);
-  useEffect(() => {
-    if (focus) ref.current?.focus();
-  }, [focus]);
-
   const balance = Amounts.parse(result.data.balance.amount);
   if (!balance) {
     return <div>there was an error reading the balance</div>;
   }
   const isBalanceEmpty = Amounts.isZero(balance);
   if (!isBalanceEmpty) {
-    return <div>
-      <div class="rounded-md bg-yellow-50 p-4">
-        <div class="flex">
-          <div class="flex-shrink-0">
-            <svg class="h-5 w-5 text-yellow-400" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
-              <path fill-rule="evenodd" d="M8.485 2.495c.673-1.167 2.357-1.167 3.03 0l6.28 10.875c.673 1.167-.17 2.625-1.516 2.625H3.72c-1.347 0-2.189-1.458-1.515-2.625L8.485 2.495zM10 5a.75.75 0 01.75.75v3.5a.75.75 0 01-1.5 0v-3.5A.75.75 0 0110 5zm0 9a1 1 0 100-2 1 1 0 000 2z" clip-rule="evenodd" />
-            </svg>
-          </div>
-          <div class="ml-3">
-            <h3 class="text-sm font-medium text-yellow-800">
-              <i18n.Translate>Can't delete the account</i18n.Translate>
-            </h3>
-            <div class="mt-2 text-sm text-yellow-700">
-              <p>
-                <i18n.Translate>The account can't be delete while still holding some balance. First make sure that the owner make a complete cashout.</i18n.Translate>
-              </p>
-            </div>
-          </div>
-
-        </div>
-      </div>
-      <div class="mt-2 flex justify-end">
-        <button type="button" class="rounded-md ring-1 ring-gray-400 bg-white px-3 py-2 text-sm font-semibold shadow-sm hover:bg-gray-100 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 "
-          onClick={() => {
-            onCancel()
-          }}>
-          <i18n.Translate>Go back</i18n.Translate>
-        </button>
-      </div>
-    </div>
+    return <Attention type="warning" title={i18n.str`Can't delete the account`} onClose={onCancel}>
+      <i18n.Translate>The account can't be delete while still holding some balance. First make sure that the owner make a complete cashout.</i18n.Translate>
+    </Attention>
   }
 
   async function doRemove() {
@@ -117,26 +87,9 @@ export function RemoveAccount({
 
   return (
     <div>
-      <div class="rounded-md bg-yellow-50 p-4">
-        <div class="flex">
-          <div class="flex-shrink-0">
-            <svg class="h-5 w-5 text-yellow-400" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
-              <path fill-rule="evenodd" d="M8.485 2.495c.673-1.167 2.357-1.167 3.03 0l6.28 10.875c.673 1.167-.17 2.625-1.516 2.625H3.72c-1.347 0-2.189-1.458-1.515-2.625L8.485 2.495zM10 5a.75.75 0 01.75.75v3.5a.75.75 0 01-1.5 0v-3.5A.75.75 0 0110 5zm0 9a1 1 0 100-2 1 1 0 000 2z" clip-rule="evenodd" />
-            </svg>
-          </div>
-          <div class="ml-3">
-            <h3 class="text-sm font-bold text-yellow-800">
-              <i18n.Translate>You are going to remove the account</i18n.Translate>
-            </h3>
-            <div class="mt-2 text-sm text-yellow-700">
-              <p>
-                <i18n.Translate>This step can't be undone.</i18n.Translate>
-              </p>
-            </div>
-          </div>
-
-        </div>
-      </div>
+      <Attention type="warning" title={i18n.str`You are going to remove the account`}>
+        <i18n.Translate>This step can't be undone.</i18n.Translate>
+      </Attention>
 
       <div class="grid grid-cols-1 gap-x-8 gap-y-8 pt-10 md:grid-cols-3 bg-gray-100 my-4 px-4 pb-4 rounded-lg">
         <div class="px-4 sm:px-0">
@@ -164,7 +117,7 @@ export function RemoveAccount({
                 </label>
                 <div class="mt-2">
                   <input
-                    ref={ref}
+                    ref={focus ? doAutoFocus : undefined}
                     type="text"
                     class="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 data-[error=true]:ring-red-500 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
                     name="password"
-- 
cgit v1.2.3

