From a5406c5a5dc437e036168eb068db11d88e05bb0f Mon Sep 17 00:00:00 2001
From: Sebastian <sebasjm@gmail.com>
Date: Tue, 19 Sep 2023 08:31:08 -0300
Subject: some ui

---
 .../src/components/Transactions/views.tsx          | 76 ++++++++++++----------
 1 file changed, 40 insertions(+), 36 deletions(-)

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

diff --git a/packages/demobank-ui/src/components/Transactions/views.tsx b/packages/demobank-ui/src/components/Transactions/views.tsx
index 34d078c16..f4a78e516 100644
--- a/packages/demobank-ui/src/components/Transactions/views.tsx
+++ b/packages/demobank-ui/src/components/Transactions/views.tsx
@@ -33,42 +33,46 @@ export function LoadingUriView({ error }: State.LoadingUriError): VNode {
 export function ReadyView({ transactions }: State.Ready): VNode {
   const { i18n } = useTranslationContext();
   return (
-    <div class="results">
-      <table class="pure-table pure-table-striped">
-        <thead>
-          <tr>
-            <th>{i18n.str`Date`}</th>
-            <th>{i18n.str`Amount`}</th>
-            <th>{i18n.str`Counterpart`}</th>
-            <th>{i18n.str`Subject`}</th>
-          </tr>
-        </thead>
-        <tbody>
-          {transactions.map((item, idx) => {
-            return (
-              <tr key={idx}>
-                <td>
-                  {item.when.t_ms === "never"
-                    ? ""
-                    : format(item.when.t_ms, "dd/MM/yyyy HH:mm:ss")}
-                </td>
-                <td>
-                  {item.negative ? "-" : ""}
-                  {item.amount ? (
-                    `${Amounts.stringifyValue(item.amount)} ${
-                      item.amount.currency
-                    }`
-                  ) : (
-                    <span style={{ color: "grey" }}>&lt;invalid value&gt;</span>
-                  )}
-                </td>
-                <td>{item.counterpart}</td>
-                <td>{item.subject}</td>
-              </tr>
-            );
-          })}
-        </tbody>
-      </table>
+    <div class="px-4 mt-4">
+      <div class="sm:flex sm:items-center">
+        <div class="sm:flex-auto">
+          <h1 class="text-base font-semibold leading-6 text-gray-900"><i18n.Translate>Latest transactions</i18n.Translate></h1>
+        </div>
+      </div>
+      <div class="-mx-4 mt-5 ring-1 ring-gray-300 sm:mx-0 sm:rounded-lg">
+        <table class="min-w-full divide-y divide-gray-300">
+          <thead>
+            <tr>
+              <th scope="col" class="pl-4 pr-3 py-3.5 text-left text-sm font-semibold text-gray-900 sm:pl-6">{i18n.str`Date`}</th>
+              <th scope="col" class="px-3      py-3.5 text-left text-sm font-semibold text-gray-900 lg:table-cell">{i18n.str`Amount`}</th>
+              <th scope="col" class="px-3      py-3.5 text-left text-sm font-semibold text-gray-900 lg:table-cell">{i18n.str`Counterpart`}</th>
+              <th scope="col" class="px-3      py-3.5 text-left text-sm font-semibold text-gray-900 lg:table-cell">{i18n.str`Subject`}</th>
+            </tr>
+          </thead>
+          <tbody>
+            {transactions.map((item, idx) => {
+              return (
+                <tr key={idx}>
+                  <td class="relative py-4 pl-4 pr-3 text-sm sm:pl-6">
+                    <div class="font-medium text-gray-900">{item.when.t_ms === "never"
+                      ? ""
+                      : format(item.when.t_ms, "dd/MM/yyyy HH:mm:ss")}</div>
+                  </td>
+                  <td class="px-3 py-3.5 text-sm text-gray-500">{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>
+                  <td class="px-3 py-3.5 text-sm text-gray-500">{item.subject}</td>
+                </tr>
+              );
+            })}
+          </tbody>
+        </table>
+      </div>
     </div>
   );
 }
-- 
cgit v1.2.3


From e39d5c488e2e425bd7febf694caadc17d5126401 Mon Sep 17 00:00:00 2001
From: Sebastian <sebasjm@gmail.com>
Date: Wed, 20 Sep 2023 15:18:36 -0300
Subject: more ui

---
 packages/demobank-ui/src/assets/logo-2021.svg      |    9 +
 packages/demobank-ui/src/components/QR.tsx         |    5 +-
 .../src/components/Transactions/views.tsx          |   14 +-
 packages/demobank-ui/src/forms/simplest.ts         |   66 +
 packages/demobank-ui/src/hooks/notification.ts     |   54 -
 .../demobank-ui/src/pages/AccountPage/index.ts     |    4 +-
 .../demobank-ui/src/pages/AccountPage/state.ts     |   10 +-
 packages/demobank-ui/src/pages/AdminPage.tsx       |  150 +-
 packages/demobank-ui/src/pages/BankFrame.tsx       |  409 +--
 packages/demobank-ui/src/pages/BusinessAccount.tsx |  106 +-
 packages/demobank-ui/src/pages/HomePage.tsx        |   40 +-
 packages/demobank-ui/src/pages/LoginForm.tsx       |  335 +--
 packages/demobank-ui/src/pages/PaymentOptions.tsx  |   48 +-
 .../src/pages/PaytoWireTransferForm.tsx            |  221 +-
 packages/demobank-ui/src/pages/QrCodeSection.tsx   |  126 +-
 .../demobank-ui/src/pages/RegistrationPage.tsx     |  311 ++-
 .../demobank-ui/src/pages/WalletWithdrawForm.tsx   |  319 ++-
 .../src/pages/WithdrawalConfirmationQuestion.tsx   |  383 ++-
 .../demobank-ui/src/pages/WithdrawalQRCode.tsx     |   23 +-
 packages/demobank-ui/src/pages/rnd.ts              | 2890 ++++++++++++++++++++
 packages/demobank-ui/src/utils.ts                  |   18 +-
 pnpm-lock.yaml                                     |   10 +-
 22 files changed, 4451 insertions(+), 1100 deletions(-)
 create mode 100644 packages/demobank-ui/src/assets/logo-2021.svg
 create mode 100644 packages/demobank-ui/src/forms/simplest.ts
 delete mode 100644 packages/demobank-ui/src/hooks/notification.ts
 create mode 100644 packages/demobank-ui/src/pages/rnd.ts

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

diff --git a/packages/demobank-ui/src/assets/logo-2021.svg b/packages/demobank-ui/src/assets/logo-2021.svg
new file mode 100644
index 000000000..8c5ff3e5b
--- /dev/null
+++ b/packages/demobank-ui/src/assets/logo-2021.svg
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 201 90">
+  <g fill="#0042b3" fill-rule="evenodd" stroke-width=".3">
+    <path d="M86.7 1.1c15.6 0 29 9.4 36 23.2h-5.9A35.1 35.1 0 0086.7 6.5C67 6.5 51 23.6 51 44.7c0 10.4 3.8 19.7 10 26.6a31.4 31.4 0 01-4.2 3A45.2 45.2 0 0146 44.7c0-24 18.2-43.6 40.7-43.6zm35.8 64.3a40.4 40.4 0 01-39 22.8c3-1.5 6-3.5 8.6-5.7a35.6 35.6 0 0024.6-17.1z" />
+    <path d="M64.2 1.1l3.1.1c-3 1.6-5.9 3.5-8.5 5.8a37.5 37.5 0 00-30.2 37.7c0 14.3 7.3 26.7 18 33.3a29.6 29.6 0 01-8.5.2c-9-8-14.6-20-14.6-33.5 0-24 18.2-43.6 40.7-43.6zm5.4 81.4a35.6 35.6 0 0024.6-17.1h5.9a40.4 40.4 0 01-39 22.8c3-1.5 5.9-3.5 8.5-5.7zm24.8-58.2a37 37 0 00-12.6-12.8 29.6 29.6 0 018.5-.2c4 3.6 7.4 8 9.9 13z" />
+    <path d="M41.8 1.1c1 0 2 0 3.1.2-3 1.5-5.9 3.4-8.5 5.6A37.5 37.5 0 006.1 44.7c0 21.1 16 38.3 35.7 38.3 12.6 0 23.6-7 30-17.6h5.8a40.4 40.4 0 01-35.8 23C19.3 88.4 1 68.8 1 44.7c0-24 18.2-43.6 40.7-43.6zm30.1 23.2a38.1 38.1 0 00-4.5-6.1c1.3-1.2 2.7-2.2 4.3-3 2.3 2.7 4.4 5.8 6 9.1z" />
+  </g>
+  <path d="M76.1 34.4h9.2v-5H61.9v5H71v26h5.1zM92.6 52.9h13.7l3 7.4h5.3l-12.7-31.2h-4.7L84.5 60.3h5.2zm11.8-4.9h-9.9l5-12.4zM123.8 29.4h-4.6v31h20.6v-5h-16zM166.5 29.4H145v31h21.6v-5H150v-8.3h14.5v-4.9h-14.5v-8h16.4zM191.2 39.5c0 1.6-.5 2.8-1.6 3.8s-2.6 1.4-4.4 1.4h-7.4V34.3h7.4c1.9 0 3.4.4 4.4 1.3 1 .9 1.6 2.2 1.6 3.9zm6 20.8l-7.7-11.7c1-.3 1.9-.7 2.7-1.3a8.8 8.8 0 003.6-4.6c.4-1 .5-2.2.5-3.5 0-1.5-.2-2.9-.7-4.1a8.4 8.4 0 00-2.1-3.1c-1-.8-2-1.5-3.4-2-1.3-.4-2.8-.6-4.5-.6h-12.9v31h5V49.4h6.5l7 10.8z" />
+</svg>
\ No newline at end of file
diff --git a/packages/demobank-ui/src/components/QR.tsx b/packages/demobank-ui/src/components/QR.tsx
index c1c159ef8..945a08867 100644
--- a/packages/demobank-ui/src/components/QR.tsx
+++ b/packages/demobank-ui/src/components/QR.tsx
@@ -33,7 +33,6 @@ export function QR({ text }: { text: string }): VNode {
   return (
     <div
       style={{
-        width: "100%",
         display: "flex",
         flexDirection: "column",
         alignItems: "left",
@@ -41,9 +40,7 @@ export function QR({ text }: { text: string }): VNode {
     >
       <div
         style={{
-          width: "50%",
-          minWidth: 200,
-          maxWidth: 300,
+          width: "100%",
           marginRight: "auto",
           marginLeft: "auto",
         }}
diff --git a/packages/demobank-ui/src/components/Transactions/views.tsx b/packages/demobank-ui/src/components/Transactions/views.tsx
index f4a78e516..6303037a1 100644
--- a/packages/demobank-ui/src/components/Transactions/views.tsx
+++ b/packages/demobank-ui/src/components/Transactions/views.tsx
@@ -39,21 +39,21 @@ export function ReadyView({ transactions }: State.Ready): VNode {
           <h1 class="text-base font-semibold leading-6 text-gray-900"><i18n.Translate>Latest transactions</i18n.Translate></h1>
         </div>
       </div>
-      <div class="-mx-4 mt-5 ring-1 ring-gray-300 sm:mx-0 sm:rounded-lg">
+      <div class="-mx-4 mt-5 ring-1 ring-gray-300 sm:mx-0 sm:rounded-lg min-w-fit bg-white">
         <table class="min-w-full divide-y divide-gray-300">
           <thead>
             <tr>
-              <th scope="col" class="pl-4 pr-3 py-3.5 text-left text-sm font-semibold text-gray-900 sm:pl-6">{i18n.str`Date`}</th>
-              <th scope="col" class="px-3      py-3.5 text-left text-sm font-semibold text-gray-900 lg:table-cell">{i18n.str`Amount`}</th>
-              <th scope="col" class="px-3      py-3.5 text-left text-sm font-semibold text-gray-900 lg:table-cell">{i18n.str`Counterpart`}</th>
-              <th scope="col" class="px-3      py-3.5 text-left text-sm font-semibold text-gray-900 lg:table-cell">{i18n.str`Subject`}</th>
+              <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>
             </tr>
           </thead>
           <tbody>
             {transactions.map((item, idx) => {
               return (
                 <tr key={idx}>
-                  <td class="relative py-4 pl-4 pr-3 text-sm sm:pl-6">
+                  <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, "dd/MM/yyyy HH:mm:ss")}</div>
@@ -66,7 +66,7 @@ export function ReadyView({ transactions }: State.Ready): VNode {
                       <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>
-                  <td class="px-3 py-3.5 text-sm text-gray-500">{item.subject}</td>
+                  <td class="px-3 py-3.5 text-sm text-gray-500 break-all min-w-md">{item.subject}</td>
                 </tr>
               );
             })}
diff --git a/packages/demobank-ui/src/forms/simplest.ts b/packages/demobank-ui/src/forms/simplest.ts
new file mode 100644
index 000000000..54b6b1c65
--- /dev/null
+++ b/packages/demobank-ui/src/forms/simplest.ts
@@ -0,0 +1,66 @@
+import {
+  AbsoluteTime,
+  AmountJson,
+  TranslatedString
+} from "@gnu-taler/taler-util";
+import { DoubleColumnForm, FormState } from "@gnu-taler/web-util/browser";
+
+export namespace Data {
+  export interface WithResolution {
+    when: AbsoluteTime;
+    threshold: AmountJson;
+    state: string;
+  }
+  export interface Form extends WithResolution {
+    comment: string;
+  }
+}
+
+const design: DoubleColumnForm = [
+  {
+    title: "Simple form" as TranslatedString,
+    fields: [
+      {
+        type: "textArea",
+        props: {
+          name: "comment",
+          label: "Comments" as TranslatedString,
+        },
+      },
+    ],
+  },
+  {
+    title: "Resolution" as TranslatedString,
+    description: `Current state is and threshold at ` as TranslatedString,
+    fields: [
+      {
+        type: "date",
+        props: {
+          name: "when",
+          label: "Decision Time" as TranslatedString,
+        },
+      },
+      {
+        type: "amount",
+        props: {
+          name: "threshold",
+          label: "New threshold" as TranslatedString,
+        },
+      },
+    ],
+  }
+  ,
+];
+
+function formBehavior(v: Partial<Data.Form>): FormState<Data.Form> {
+  return {
+    when: {
+      disabled: true,
+    },
+    threshold: {
+      // disabled: v.state === AmlExchangeBackend.AmlState.frozen,
+    },
+  };
+}
+
+
diff --git a/packages/demobank-ui/src/hooks/notification.ts b/packages/demobank-ui/src/hooks/notification.ts
deleted file mode 100644
index 9bf621b41..000000000
--- a/packages/demobank-ui/src/hooks/notification.ts
+++ /dev/null
@@ -1,54 +0,0 @@
-import { TranslatedString } from "@gnu-taler/taler-util";
-import { memoryMap } from "@gnu-taler/web-util/browser";
-import { StateUpdater, useEffect, useState } from "preact/hooks";
-
-export type NotificationMessage = ErrorNotification | InfoNotification;
-
-//FIXME: this should not be exported since every notification
-// goes throw notify function
-export interface ErrorMessage {
-  description?: string;
-  title: TranslatedString;
-  debug?: string;
-}
-
-interface ErrorNotification {
-  type: "error";
-  error: ErrorMessage;
-}
-interface InfoNotification {
-  type: "info";
-  info: TranslatedString;
-}
-
-const storage = memoryMap<NotificationMessage>();
-const NOTIFICATION_KEY = "notification";
-
-export function onNotificationUpdate(
-  handler: (newValue: NotificationMessage | undefined) => void,
-) {
-  return storage.onUpdate(NOTIFICATION_KEY, () => {
-    const newValue = storage.get(NOTIFICATION_KEY);
-    handler(newValue);
-  });
-}
-
-export function notifyError(error: ErrorMessage) {
-  storage.set(NOTIFICATION_KEY, { type: "error", error });
-}
-export function notifyInfo(info: TranslatedString) {
-  storage.set(NOTIFICATION_KEY, { type: "info", info });
-}
-
-export function useNotifications(): [
-  NotificationMessage | undefined,
-  StateUpdater<NotificationMessage | undefined>,
-] {
-  const [value, setter] = useState<NotificationMessage | undefined>();
-  useEffect(() => {
-    return storage.onUpdate(NOTIFICATION_KEY, () => {
-      setter(storage.get(NOTIFICATION_KEY));
-    });
-  });
-  return [value, setter];
-}
diff --git a/packages/demobank-ui/src/pages/AccountPage/index.ts b/packages/demobank-ui/src/pages/AccountPage/index.ts
index 28fb7cb0c..ed6945f84 100644
--- a/packages/demobank-ui/src/pages/AccountPage/index.ts
+++ b/packages/demobank-ui/src/pages/AccountPage/index.ts
@@ -15,7 +15,7 @@
  */
 
 import { HttpError, HttpResponseOk, HttpResponsePaginated, utils } from "@gnu-taler/web-util/browser";
-import { AbsoluteTime, AmountJson, PaytoUriIBAN } from "@gnu-taler/taler-util";
+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";
@@ -51,7 +51,7 @@ export namespace State {
     status: "ready";
     error: undefined;
     account: string, 
-    payto: PaytoUriIBAN, 
+    payto: PaytoUriIBAN | PaytoUriTalerBank, 
     balance: AmountJson, 
     balanceIsDebit: boolean, 
     limit: AmountJson,
diff --git a/packages/demobank-ui/src/pages/AccountPage/state.ts b/packages/demobank-ui/src/pages/AccountPage/state.ts
index bc59c9374..2249b743e 100644
--- a/packages/demobank-ui/src/pages/AccountPage/state.ts
+++ b/packages/demobank-ui/src/pages/AccountPage/state.ts
@@ -15,10 +15,9 @@
  */
 
 import { Amounts, HttpStatusCode, parsePaytoUri } from "@gnu-taler/taler-util";
-import { ErrorType, useTranslationContext } from "@gnu-taler/web-util/browser";
+import { ErrorType, notifyError, useTranslationContext } from "@gnu-taler/web-util/browser";
 import { useBackendContext } from "../../context/backend.js";
 import { useAccountDetails } from "../../hooks/access.js";
-import { notifyError } from "../../hooks/notification.js";
 import { Props, State } from "./index.js";
 
 export function useComponentState({ account, onLoadNotOk }: Props): State {
@@ -43,9 +42,7 @@ export function useComponentState({ account, onLoadNotOk }: Props): State {
     //logout if there is any error, not if loading
     backend.logOut();
     if (result.status === HttpStatusCode.NotFound) {
-      notifyError({
-        title: i18n.str`Username or account label "${account}" not found`,
-      });
+      notifyError(i18n.str`Username or account label "${account}" not found`, undefined);
       return {
         status: "error-user-not-found",
         error: result,
@@ -62,7 +59,8 @@ export function useComponentState({ account, onLoadNotOk }: Props): State {
   const debitThreshold = Amounts.parseOrThrow(data.debitThreshold);
   const payto = parsePaytoUri(data.paytoUri);
 
-  if (!payto || !payto.isKnown || payto.targetType !== "iban") {
+  if (!payto || !payto.isKnown || (payto.targetType !== "iban" && payto.targetType !== "x-taler-bank")) {
+    console.log(payto)
     return {
       status: "invalid-iban",
       error: result
diff --git a/packages/demobank-ui/src/pages/AdminPage.tsx b/packages/demobank-ui/src/pages/AdminPage.tsx
index 73a4f9ca3..18462bdc3 100644
--- a/packages/demobank-ui/src/pages/AdminPage.tsx
+++ b/packages/demobank-ui/src/pages/AdminPage.tsx
@@ -14,11 +14,14 @@
  GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 
-import { Amounts, HttpStatusCode, parsePaytoUri } from "@gnu-taler/taler-util";
+import { Amounts, HttpStatusCode, TranslatedString, parsePaytoUri } from "@gnu-taler/taler-util";
 import {
   ErrorType,
   HttpResponsePaginated,
   RequestError,
+  notify,
+  notifyError,
+  notifyInfo,
   useTranslationContext,
 } from "@gnu-taler/web-util/browser";
 import { Fragment, h, VNode } from "preact";
@@ -39,12 +42,10 @@ import {
   validateIBAN,
   WithIntermediate,
 } from "../utils.js";
-import { ErrorBannerFloat } from "./BankFrame.js";
 import { ShowCashoutDetails } from "./BusinessAccount.js";
 import { handleNotOkResult } from "./HomePage.js";
 import { PaytoWireTransferForm } from "./PaytoWireTransferForm.js";
 import { ShowInputErrorLabel } from "../components/ShowInputErrorLabel.js";
-import { ErrorMessage, notifyInfo } from "../hooks/notification.js";
 
 const charset =
   "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
@@ -362,6 +363,7 @@ function AdminAccount({ onRegister }: { onRegister: () => void }): VNode {
         onSuccess={() => {
           notifyInfo(i18n.str`Wire transfer created!`);
         }}
+        onCancel={undefined}
       />
     </Fragment>
   );
@@ -414,7 +416,6 @@ export function UpdateAccountPassword({
   const { changePassword } = useAdminAccountAPI();
   const [password, setPassword] = useState<string | undefined>();
   const [repeat, setRepeat] = useState<string | undefined>();
-  const [error, saveError] = useState<ErrorMessage | undefined>();
 
   if (!result.ok) {
     if (result.loading || result.type === ErrorType.TIMEOUT) {
@@ -431,8 +432,8 @@ export function UpdateAccountPassword({
     repeat: !repeat
       ? i18n.str`required`
       : password !== repeat
-      ? i18n.str`password doesn't match`
-      : undefined,
+        ? i18n.str`password doesn't match`
+        : undefined,
   });
 
   return (
@@ -442,9 +443,6 @@ export function UpdateAccountPassword({
           <i18n.Translate>Update password for {account}</i18n.Translate>
         </h1>
       </div>
-      {error && (
-        <ErrorBannerFloat error={error} onClear={() => saveError(undefined)} />
-      )}
 
       <div style={{ maxWidth: 600, overflowX: "hidden", margin: "auto" }}>
         <form class="pure-form">
@@ -507,15 +505,11 @@ export function UpdateAccountPassword({
                     onUpdateSuccess();
                   } catch (error) {
                     if (error instanceof RequestError) {
-                      saveError(buildRequestErrorMessage(i18n, error.cause));
+                      notify(buildRequestErrorMessage(i18n, error.cause));
                     } else {
-                      saveError({
-                        title: i18n.str`Operation failed, please report`,
-                        description:
-                          error instanceof Error
-                            ? error.message
-                            : JSON.stringify(error),
-                      });
+                      notifyError(i18n.str`Operation failed, please report`, (error instanceof Error
+                        ? error.message
+                        : JSON.stringify(error)) as TranslatedString)
                     }
                   }
                 }}
@@ -540,7 +534,6 @@ function CreateNewAccount({
   const [submitAccount, setSubmitAccount] = useState<
     SandboxBackend.Circuit.CircuitAccountData | undefined
   >();
-  const [error, saveError] = useState<ErrorMessage | undefined>();
   return (
     <div>
       <div>
@@ -548,9 +541,6 @@ function CreateNewAccount({
           <i18n.Translate>New account</i18n.Translate>
         </h1>
       </div>
-      {error && (
-        <ErrorBannerFloat error={error} onClear={() => saveError(undefined)} />
-      )}
 
       <div style={{ maxWidth: 600, overflowX: "hidden", margin: "auto" }}>
         <AccountForm
@@ -587,39 +577,38 @@ function CreateNewAccount({
                   if (!submitAccount) return;
                   try {
                     const account: SandboxBackend.Circuit.CircuitAccountRequest =
-                      {
-                        cashout_address: submitAccount.cashout_address,
-                        contact_data: submitAccount.contact_data,
-                        internal_iban: submitAccount.iban,
-                        name: submitAccount.name,
-                        username: submitAccount.username,
-                        password: randomPassword(),
-                      };
+                    {
+                      cashout_address: submitAccount.cashout_address,
+                      contact_data: submitAccount.contact_data,
+                      internal_iban: submitAccount.iban,
+                      name: submitAccount.name,
+                      username: submitAccount.username,
+                      password: randomPassword(),
+                    };
 
                     await createAccount(account);
                     onCreateSuccess(account.password);
                   } catch (error) {
                     if (error instanceof RequestError) {
-                      saveError(
+                      notify(
                         buildRequestErrorMessage(i18n, error.cause, {
                           onClientError: (status) =>
                             status === HttpStatusCode.Forbidden
                               ? i18n.str`The rights to perform the operation are not sufficient`
                               : status === HttpStatusCode.BadRequest
-                              ? i18n.str`Input data was invalid`
-                              : status === HttpStatusCode.Conflict
-                              ? i18n.str`At least one registration detail was not available`
-                              : undefined,
+                                ? i18n.str`Input data was invalid`
+                                : status === HttpStatusCode.Conflict
+                                  ? i18n.str`At least one registration detail was not available`
+                                  : undefined,
                         }),
                       );
                     } else {
-                      saveError({
-                        title: i18n.str`Operation failed, please report`,
-                        description:
-                          error instanceof Error
-                            ? error.message
-                            : JSON.stringify(error),
-                      });
+                      notifyError(
+                        i18n.str`Operation failed, please report`,
+                        (error instanceof Error
+                          ? error.message
+                          : JSON.stringify(error)) as TranslatedString
+                      )
                     }
                   }
                 }}
@@ -654,7 +643,6 @@ export function ShowAccountDetails({
   const [submitAccount, setSubmitAccount] = useState<
     SandboxBackend.Circuit.CircuitAccountData | undefined
   >();
-  const [error, saveError] = useState<ErrorMessage | undefined>();
 
   if (!result.ok) {
     if (result.loading || result.type === ErrorType.TIMEOUT) {
@@ -673,9 +661,6 @@ export function ShowAccountDetails({
           <i18n.Translate>Business account details</i18n.Translate>
         </h1>
       </div>
-      {error && (
-        <ErrorBannerFloat error={error} onClear={() => saveError(undefined)} />
-      )}
       <div style={{ maxWidth: 600, overflowX: "hidden", margin: "auto" }}>
         <AccountForm
           template={result.data}
@@ -740,24 +725,23 @@ export function ShowAccountDetails({
                         onUpdateSuccess();
                       } catch (error) {
                         if (error instanceof RequestError) {
-                          saveError(
+                          notify(
                             buildRequestErrorMessage(i18n, error.cause, {
                               onClientError: (status) =>
                                 status === HttpStatusCode.Forbidden
                                   ? i18n.str`The rights to change the account are not sufficient`
                                   : status === HttpStatusCode.NotFound
-                                  ? i18n.str`The username was not found`
-                                  : undefined,
+                                    ? i18n.str`The username was not found`
+                                    : undefined,
                             }),
                           );
                         } else {
-                          saveError({
-                            title: i18n.str`Operation failed, please report`,
-                            description:
-                              error instanceof Error
-                                ? error.message
-                                : JSON.stringify(error),
-                          });
+                          notifyError(
+                            i18n.str`Operation failed, please report`,
+                            (error instanceof Error
+                              ? error.message
+                              : JSON.stringify(error)) as TranslatedString
+                          )
                         }
                       }
                     }
@@ -788,7 +772,6 @@ function RemoveAccount({
   const { i18n } = useTranslationContext();
   const result = useAccountDetails(account);
   const { deleteAccount } = useAdminAccountAPI();
-  const [error, saveError] = useState<ErrorMessage | undefined>();
 
   if (!result.ok) {
     if (result.loading || result.type === ErrorType.TIMEOUT) {
@@ -812,7 +795,8 @@ function RemoveAccount({
           <i18n.Translate>Remove account: {account}</i18n.Translate>
         </h1>
       </div>
-      {!isBalanceEmpty && (
+      {/* {FXME: SHOW WARNING} */}
+      {/* {!isBalanceEmpty && (
         <ErrorBannerFloat
           error={{
             title: i18n.str`Can't delete the account`,
@@ -820,10 +804,7 @@ function RemoveAccount({
           }}
           onClear={() => saveError(undefined)}
         />
-      )}
-      {error && (
-        <ErrorBannerFloat error={error} onClear={() => saveError(undefined)} />
-      )}
+      )} */}
 
       <p>
         <div style={{ display: "flex", justifyContent: "space-between" }}>
@@ -852,26 +833,23 @@ function RemoveAccount({
                   onUpdateSuccess();
                 } catch (error) {
                   if (error instanceof RequestError) {
-                    saveError(
+                    notify(
                       buildRequestErrorMessage(i18n, error.cause, {
                         onClientError: (status) =>
                           status === HttpStatusCode.Forbidden
                             ? i18n.str`The administrator specified a institutional username`
                             : status === HttpStatusCode.NotFound
-                            ? i18n.str`The username was not found`
-                            : status === HttpStatusCode.PreconditionFailed
-                            ? i18n.str`Balance was not zero`
-                            : undefined,
+                              ? i18n.str`The username was not found`
+                              : status === HttpStatusCode.PreconditionFailed
+                                ? i18n.str`Balance was not zero`
+                                : undefined,
                       }),
                     );
                   } else {
-                    saveError({
-                      title: i18n.str`Operation failed, please report`,
-                      description:
-                        error instanceof Error
+                    notifyError(i18n.str`Operation failed, please report`,
+                      (error instanceof Error
                           ? error.message
-                          : JSON.stringify(error),
-                    });
+                          : JSON.stringify(error)) as TranslatedString);
                   }
                 }
               }}
@@ -915,31 +893,31 @@ function AccountForm({
       cashout_address: !newForm.cashout_address
         ? i18n.str`required`
         : !parsed
-        ? i18n.str`does not follow the pattern`
-        : !parsed.isKnown || parsed.targetType !== "iban"
-        ? i18n.str`only "IBAN" target are supported`
-        : !IBAN_REGEX.test(parsed.iban)
-        ? i18n.str`IBAN should have just uppercased letters and numbers`
-        : validateIBAN(parsed.iban, i18n),
+          ? i18n.str`does not follow the pattern`
+          : !parsed.isKnown || parsed.targetType !== "iban"
+            ? i18n.str`only "IBAN" target are supported`
+            : !IBAN_REGEX.test(parsed.iban)
+              ? i18n.str`IBAN should have just uppercased letters and numbers`
+              : validateIBAN(parsed.iban, i18n),
       contact_data: undefinedIfEmpty({
         email: !newForm.contact_data?.email
           ? i18n.str`required`
           : !EMAIL_REGEX.test(newForm.contact_data.email)
-          ? i18n.str`it should be an email`
-          : undefined,
+            ? i18n.str`it should be an email`
+            : undefined,
         phone: !newForm.contact_data?.phone
           ? i18n.str`required`
           : !newForm.contact_data.phone.startsWith("+")
-          ? i18n.str`should start with +`
-          : !REGEX_JUST_NUMBERS_REGEX.test(newForm.contact_data.phone)
-          ? i18n.str`phone number can't have other than numbers`
-          : undefined,
+            ? i18n.str`should start with +`
+            : !REGEX_JUST_NUMBERS_REGEX.test(newForm.contact_data.phone)
+              ? i18n.str`phone number can't have other than numbers`
+              : undefined,
       }),
       iban: !newForm.iban
         ? undefined //optional field
         : !IBAN_REGEX.test(newForm.iban)
-        ? i18n.str`IBAN should have just uppercased letters and numbers`
-        : validateIBAN(newForm.iban, i18n),
+          ? i18n.str`IBAN should have just uppercased letters and numbers`
+          : validateIBAN(newForm.iban, i18n),
       name: !newForm.name ? i18n.str`required` : undefined,
       username: !newForm.username ? i18n.str`required` : undefined,
     });
diff --git a/packages/demobank-ui/src/pages/BankFrame.tsx b/packages/demobank-ui/src/pages/BankFrame.tsx
index 5b6d95ade..d234845a0 100644
--- a/packages/demobank-ui/src/pages/BankFrame.tsx
+++ b/packages/demobank-ui/src/pages/BankFrame.tsx
@@ -15,17 +15,16 @@
  */
 
 import { Logger, PaytoUriIBAN, TranslatedString, parsePaytoUri, stringifyPaytoUri } from "@gnu-taler/taler-util";
-import { useTranslationContext } from "@gnu-taler/web-util/browser";
+import { useNotifications, useTranslationContext } from "@gnu-taler/web-util/browser";
 import { ComponentChildren, Fragment, h, VNode } from "preact";
 import { StateUpdater, useEffect, useState } from "preact/hooks";
-import talerLogo from "../assets/logo-white.svg";
 import { LangSelectorLikePy as LangSelector } from "../components/LangSelector.js";
 import { useBackendContext } from "../context/backend.js";
 import { useBusinessAccountDetails } from "../hooks/circuit.js";
 import { bankUiSettings } from "../settings.js";
 import { useSettings } from "../hooks/settings.js";
-import { ErrorMessage, onNotificationUpdate } from "../hooks/notification.js";
 import { CopyButton, CopyIcon } from "../components/CopyButton.js";
+import logo from "../assets/logo-2021.svg";
 
 const IS_PUBLIC_ACCOUNT_ENABLED = false;
 const GIT_HASH = typeof __GIT_HASH__ !== "undefined" ? __GIT_HASH__ : undefined;
@@ -81,16 +80,23 @@ export function BankFrame({
       </a>,
     );
 
-  return (<div class="min-h-full">
+  return (<div class="min-h-full flex flex-col m-0" style="min-height: 100vh;">
     <div class="bg-indigo-600 pb-32">
-      <nav class="border-b border-indigo-300 border-opacity-25 bg-indigo-600 lg:border-none">
+      <nav class="">
         <div class="mx-auto max-w-7xl px-2 sm:px-4 lg:px-8">
-          <div class="relative flex h-16 items-center justify-between lg:border-b lg:border-indigo-400 lg:border-opacity-25">
+          <div class="relative flex h-16 items-center justify-between ">
             <div class="flex items-center px-2 lg:px-0">
-              <div class="flex-shrink-0">
-                <img class="block h-8 w-8" src="https://tailwindui.com/img/logos/mark.svg?color=indigo&shade=300" alt="Your Company" />
+              <div class="flex-shrink-0 bg-white rounded-lg">
+                <a href="#/">
+                  <img
+                    class="h-8 w-auto"
+                    src={logo}
+                    alt="Taler"
+                    style={{ height: 35, margin: 10 }}
+                  />
+                </a>
               </div>
-              <div class="hidden lg:ml-10 lg:block">
+              <div class="hidden sm:block lg:ml-10 ">
                 <div class="flex space-x-4">
                   {/* <!-- Current: "bg-indigo-700 text-white", Default: "text-white hover:bg-indigo-500 hover:bg-opacity-75" --> */}
                   {bankUiSettings.demoSites.map(([name, url]) => {
@@ -100,62 +106,131 @@ export function BankFrame({
               </div>
             </div>
 
-            <div class="flex lg:hidden">
-              {/* <!-- Mobile menu button --> */}
-              <button type="button" class="relative inline-flex items-center justify-center rounded-md bg-indigo-600 p-2 text-indigo-200 hover:bg-indigo-500 hover:bg-opacity-75 hover:text-white focus:outline-none focus:ring-2 focus:ring-white focus:ring-offset-2 focus:ring-offset-indigo-600" aria-controls="mobile-menu" aria-expanded="false"
+            <div class="flex">
+              <button type="button" class="relative inline-flex items-center justify-center rounded-md bg-indigo-600 p-1 text-indigo-200 hover:bg-indigo-500 hover:bg-opacity-75 hover:text-white focus:outline-none focus:ring-2 focus:ring-white focus:ring-offset-2 focus:ring-offset-indigo-600" aria-controls="mobile-menu" aria-expanded="false"
                 onClick={(e) => {
                   setOpen(!open)
                 }}>
                 <span class="absolute -inset-0.5"></span>
                 <span class="sr-only">Open main menu</span>
-                {/* <!-- Menu open: "hidden", Menu closed: "block" --> */}
-                <svg class="block h-6 w-6" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" aria-hidden="true">
+                <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>
-                {/* <!-- Menu open: "block", Menu closed: "hidden" --> */}
-                <svg class="hidden h-6 w-6" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" aria-hidden="true">
-                  <path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" />
-                </svg>
               </button>
             </div>
           </div>
         </div>
 
-        {/* <!-- Mobile menu, show/hide based on menu state. --> */}
         {open &&
-          <div class="lg:hidden" id="mobile-menu">
-            <div class="space-y-1 px-2 pb-3 pt-2">
-              {/* <!-- Current: "bg-indigo-700 text-white", Default: "text-white hover:bg-indigo-500 hover:bg-opacity-75" --> */}
-              {bankUiSettings.demoSites.map(([name, url]) => {
-                return <a href={url} class="text-white hover:bg-indigo-500 hover:bg-opacity-75 block rounded-md py-2 px-3 text-base font-medium">{name}</a>
-              })}
+          <Fragment>
+            <div class="relative z-10" aria-labelledby="slide-over-title" role="dialog" aria-modal="true"
+              onClick={() => {
+                setOpen(false)
+              }}>
+              <div class="fixed inset-0"></div>
+
+              <div class="fixed inset-0 overflow-hidden">
+                <div class="absolute inset-0 overflow-hidden">
+                  <div class="pointer-events-none fixed inset-y-0 right-0 flex max-w-full pl-10">
+                    <div class="pointer-events-auto w-screen max-w-md" >
+                      <div class="flex h-full flex-col overflow-y-scroll bg-white py-6 shadow-xl" onClick={(e) => {
+                        //do not trigger close if clicking inside the sidebar
+                        e.stopPropagation();
+                      }}>
+                        <div class="px-4 sm:px-6" >
+                          <div class="flex items-start justify-between" >
+                            <h2 class="text-base font-semibold leading-6 text-gray-900" id="slide-over-title">
+                              <i18n.Translate>Settings</i18n.Translate>
+                            </h2>
+                            <div class="ml-3 flex h-7 items-center">
+                              <button type="button" class="relative rounded-md bg-white text-gray-400 hover:text-gray-500 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2"
+                                onClick={(e) => {
+                                  setOpen(false)
+                                }}
+
+                              >
+                                <span class="absolute -inset-2.5"></span>
+                                <span class="sr-only">
+                                  <i18n.Translate>Close panel</i18n.Translate>
+                                </span>
+                                <svg class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" aria-hidden="true">
+                                  <path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" />
+                                </svg>
+                              </button>
+                            </div>
+                          </div>
+                        </div>
+                        <div class="relative mt-6 flex-1 px-4 sm:px-6">
+                          {/* <!-- Your content --> */}
+
+                          <nav class="flex flex-1 flex-col" aria-label="Sidebar">
+                            <ul role="list" class="flex flex-1 flex-col gap-y-7">
+                              <li>
+                                <ul role="list" class="-mx-2 space-y-1">
+                                  <li>
+                                    <a href="#"
+                                      class="text-gray-700 hover:text-indigo-600 hover:bg-gray-100 group flex gap-x-3 rounded-md p-2 text-sm leading-6 font-semibold"
+                                      onClick={() => {
+                                        backend.logOut();
+                                        setOpen(false)
+                                        updateSettings("currentWithdrawalOperationId", undefined);
+                                      }}
+                                    >
+                                      <svg class="h-6 w-6 shrink-0 text-indigo-600" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" aria-hidden="true">
+                                        <path stroke-linecap="round" stroke-linejoin="round" d="M2.25 12l8.954-8.955c.44-.439 1.152-.439 1.591 0L21.75 12M4.5 9.75v10.125c0 .621.504 1.125 1.125 1.125H9.75v-4.875c0-.621.504-1.125 1.125-1.125h2.25c.621 0 1.125.504 1.125 1.125V21h4.125c.621 0 1.125-.504 1.125-1.125V9.75M8.25 21h8.25" />
+                                      </svg>
+                                      Log out
+                                      {/* <span class="ml-auto w-9 min-w-max whitespace-nowrap rounded-full bg-gray-50 px-2.5 py-0.5 text-center text-xs font-medium leading-5 text-gray-600 ring-1 ring-inset ring-gray-200" aria-hidden="true">5</span> */}
+                                    </a>
+                                  </li>
+                                </ul>
+                              </li>
+                              <li class="sm:hidden">
+                                <div class="text-xs font-semibold leading-6 text-gray-400">
+                                  <i18n.Translate>Sites</i18n.Translate>
+                                </div>
+                                <ul role="list" class="-mx-2 mt-2 space-y-1">
+                                  {bankUiSettings.demoSites.map(([name, url]) => {
+                                    return <a href={url} target="_blank" rel="noopener noreferrer" class="text-gray-700 hover:text-indigo-600 hover:bg-gray-100 group flex gap-x-3 rounded-md p-2 text-sm leading-6 font-semibold">
+                                      <span class="flex h-6 w-6 shrink-0 items-center justify-center rounded-lg border text-[0.625rem] font-medium bg-white text-gray-400 border-gray-200 group-hover:border-indigo-600 group-hover:text-indigo-600">&gt;</span>
+                                      <span class="truncate">{name}</span>
+                                    </a>
+                                  })}
+                                </ul>
+                              </li>
+                            </ul>
+                          </nav>
+                        </div>
+                      </div>
+                    </div>
+                  </div>
+                </div>
+              </div>
             </div>
 
-          </div>
+          </Fragment>
         }
       </nav >
-      <header class="py-10">
-
-        <div class="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
-          <div class=" flex flex-wrap items-center justify-between sm:flex-nowrap">
-            {/* <h1 class="text-base font-semibold leading-6 text-gray-900"></h1> */}
-            <h1 class="text-3xl font-bold tracking-tight text-white"><WelcomeAccount /></h1>
-            <div>
 
-              <h2 class="text-3xl font-bold tracking-tight text-white">KUDOS 100.00</h2>
+      {true &&
+        <header class="py-5 border-t border-indigo-300 border-opacity-25 bg-indigo-600 lg:border-t lg:border-indigo-400 lg:border-opacity-25">
+          <div class="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
+            <div class=" flex flex-wrap items-center justify-between sm:flex-nowrap">
+              <h3 class="text-2xl font-bold tracking-tight text-white"><WelcomeAccount /></h3>
+              <div>
+                <h3 class="text-2xl font-bold tracking-tight text-white"><AccountBalance /></h3>
+              </div>
             </div>
-            {/* <div class="ml-4 mt-2 flex-shrink-0">
-              <button type="button" class="relative 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-600">Create new job</button>
-            </div> */}
           </div>
-        </div>
-      </header>
+
+        </header>
+      }
     </div >
 
-    <main class="-mt-32">
+    <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">
-          {/* <!-- Your content --> */}
+          <StatusBanner />
           {children}
         </div>
       </div>
@@ -207,15 +282,15 @@ export function BankFrame({
     //             />
     //           ) : undefined}
 
-    //           <LangSelector />
+    // <LangSelector />
 
     //           <a
     //             href="#"
     //             class="pure-button logout-button"
-    //             onClick={() => {
-    //               backend.logOut();
-    //               updateSettings("currentWithdrawalOperationId", undefined);
-    //             }}
+    // onClick={() => {
+    //   backend.logOut();
+    //   updateSettings("currentWithdrawalOperationId", undefined);
+    // }}
     //           >{i18n.str`Logout`}</a>
     //         </Fragment>
     //       ) : undefined}
@@ -225,149 +300,110 @@ export function BankFrame({
     //     <StatusBanner />
     //     {children}
     //   </section>
-    //   <section id="footer" class="footer">
-    //     <hr />
-    //     <div>
-    //       <p>
-    //         You can learn more about GNU Taler on our{" "}
-    //         <a href="https://taler.net">main website</a>.
-    //       </p>
-    //     </div>
-    //     <div style="flex-grow:1" />
-    //     <p>
-    //       Copyright &copy; 2014&mdash;2022 Taler Systems SA. {versionText}{" "}
-    //       <TestingTag />
-    //     </p>
-    //   </section>
     // </Fragment>
   );
 }
 
-function maybeDemoContent(content: VNode): VNode {
-  if (bankUiSettings.showDemoNav) {
-    return content;
-  }
-  return <Fragment />;
-}
+// function maybeDemoContent(content: VNode): VNode {
+//   if (bankUiSettings.showDemoNav) {
+//     return content;
+//   }
+//   return <Fragment />;
+// }
 
-export function ErrorBannerFloat({
-  error,
-  onClear,
-}: {
-  error: ErrorMessage;
-  onClear?: () => void;
-}): VNode {
-  return (
-    <div
-      style={{
-        position: "fixed",
-        top: 10,
-        zIndex: 200,
-        width: "90%",
-      }}
-    >
-      <ErrorBanner error={error} onClear={onClear} />
-    </div>
-  );
-}
+// export function ErrorBannerFloat({
+//   error,
+//   onClear,
+// }: {
+//   error: ErrorMessage;
+//   onClear?: () => void;
+// }): VNode {
+//   return (
+//     <div
+//       style={{
+//         position: "fixed",
+//         top: 10,
+//         zIndex: 200,
+//         width: "90%",
+//       }}
+//     >
+//       <ErrorBanner error={error} onClear={onClear} />
+//     </div>
+//   );
+// }
 
-function ErrorBanner({
-  error,
-  onClear,
-}: {
-  error: ErrorMessage;
-  onClear?: () => void;
-}): VNode {
-  return (
-    <div
-      class="informational informational-fail"
-      style={{
-        marginTop: 8,
-        paddingLeft: 16,
-        paddingRight: 16,
-      }}
-    >
-      <div style={{ display: "flex", justifyContent: "space-between" }}>
-        <p>
-          <b>{error.title}</b>
-        </p>
-        <div style={{ marginTop: "auto", marginBottom: "auto" }}>
-          {onClear && (
-            <input
-              type="button"
-              class="pure-button"
-              value="Clear"
-              onClick={(e) => {
-                e.preventDefault();
-                onClear();
-              }}
-            />
-          )}
-        </div>
-      </div>
-      <p>{error.description}</p>
-    </div>
-  );
-}
-
-function StatusBanner(): VNode | null {
-  const [info, setInfo] = useState<TranslatedString>();
-  const [error, setError] = useState<ErrorMessage>();
-  useEffect(() => {
-    return onNotificationUpdate((newValue) => {
-      if (newValue === undefined) {
-        setInfo(undefined);
-        setError(undefined);
-      } else {
-        if (newValue.type === "error") {
-          setError(newValue.error);
-        } else {
-          setInfo(newValue.info);
-        }
-      }
-    });
-  }, []);
-  return (
-    <div
-      style={{
-        position: "fixed",
-        top: 10,
-        zIndex: 200,
-        width: "90%",
-      }}
-    >
-      {!info ? undefined : (
-        <div
-          class="informational informational-ok"
-          style={{ marginTop: 8, paddingLeft: 16, paddingRight: 16 }}
-        >
-          <div style={{ display: "flex", justifyContent: "space-between" }}>
-            <p>
-              <b>{info}</b>
-            </p>
-            <div>
-              <input
-                type="button"
-                class="pure-button"
-                value="Clear"
-                onClick={async () => {
-                  setInfo(undefined);
-                }}
-              />
+function StatusBanner(): VNode {
+  const notifs = useNotifications()
+  return <div
+    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">
+            <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>
+                  <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-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>
+            {n.message.description &&
+              <div class="mt-2 text-sm text-red-700">
+                {n.message.description}
+              </div>
+            }
           </div>
-        </div>
-      )}
-      {!error ? undefined : (
-        <ErrorBanner
-          error={error}
-          onClear={() => {
-            setError(undefined);
-          }}
-        />
-      )}
-    </div>
-  );
+          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();
+                      }}
+                    >
+                      Close
+                      <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>
+        }
+      })}
+  </div>
+
 }
 
 function TestingTag(): VNode {
@@ -392,12 +428,12 @@ function TestingTag(): VNode {
 
 function Footer() {
   return (
-    <footer class="absolute bottom-4">
+    <footer class="bottom-4 mb-4">
       <div class="mt-8 mx-8 md:order-1 md:mt-0">
         <div>
           <p class="text-xs leading-5 text-gray-400">
             You can learn more about GNU Taler on our{" "}
-            <a class="font-semibold text-gray-500 hover:text-gray-400" href="https://taler.net">main website</a>.
+            <a target="_blank" rel="noreferrer noopener" class="font-semibold text-gray-500 hover:text-gray-400" href="https://taler.net">main website</a>.
           </p>
         </div>
         <div style="flex-grow:1" />
@@ -418,4 +454,9 @@ function WelcomeAccount(): VNode {
     Welcome,  {account} (<a href={stringifyPaytoUri(payto)}>{payto.iban}</a>)! <CopyButton getContent={() => stringifyPaytoUri(payto)} />
   </i18n.Translate>
 
-}
\ No newline at end of file
+}
+
+function AccountBalance(): VNode {
+
+  return <div>KUDOS 100.00</div>
+}
diff --git a/packages/demobank-ui/src/pages/BusinessAccount.tsx b/packages/demobank-ui/src/pages/BusinessAccount.tsx
index 2faf83a1c..ec71ceca6 100644
--- a/packages/demobank-ui/src/pages/BusinessAccount.tsx
+++ b/packages/demobank-ui/src/pages/BusinessAccount.tsx
@@ -17,17 +17,21 @@ import {
   AmountJson,
   Amounts,
   HttpStatusCode,
-  TranslatedString,
+  TranslatedString
 } from "@gnu-taler/taler-util";
 import {
   HttpResponse,
   HttpResponsePaginated,
   RequestError,
+  notify,
+  notifyError,
+  notifyInfo,
   useTranslationContext,
 } from "@gnu-taler/web-util/browser";
 import { Fragment, VNode, h } from "preact";
-import { StateUpdater, useEffect, useState } from "preact/hooks";
+import { useEffect, useState } from "preact/hooks";
 import { Cashouts } from "../components/Cashouts/index.js";
+import { ShowInputErrorLabel } from "../components/ShowInputErrorLabel.js";
 import { useBackendContext } from "../context/backend.js";
 import { useAccountDetails } from "../hooks/access.js";
 import {
@@ -42,12 +46,9 @@ import {
   undefinedIfEmpty,
 } from "../utils.js";
 import { ShowAccountDetails, UpdateAccountPassword } from "./AdminPage.js";
-import { ErrorBannerFloat } from "./BankFrame.js";
-import { LoginForm } from "./LoginForm.js";
-import { ShowInputErrorLabel } from "../components/ShowInputErrorLabel.js";
 import { handleNotOkResult } from "./HomePage.js";
-import { ErrorMessage, notifyInfo } from "../hooks/notification.js";
-import { Amount } from "./WalletWithdrawForm.js";
+import { LoginForm } from "./LoginForm.js";
+import { Amount } from "./PaytoWireTransferForm.js";
 
 interface Props {
   onClose: () => void;
@@ -225,7 +226,6 @@ function CreateCashout({
   const { i18n } = useTranslationContext();
   const ratiosResult = useRatiosAndFeeConfig();
   const result = useAccountDetails(account);
-  const [error, saveError] = useState<ErrorMessage | undefined>();
   const {
     estimateByCredit: calculateFromCredit,
     estimateByDebit: calculateFromDebit,
@@ -268,15 +268,15 @@ function CreateCashout({
       calculateFromDebit(amount, sellFee, sellRate)
         .then((r) => {
           setCalc(r);
-          saveError(undefined);
         })
         .catch((error) => {
-          saveError(
+          notify(
             error instanceof RequestError
               ? buildRequestErrorMessage(i18n, error.cause)
               : {
+                  type: "error",
                   title: i18n.str`Could not estimate the cashout`,
-                  description: error.message,
+                  description: error.message as TranslatedString
                 },
           );
         });
@@ -284,13 +284,13 @@ function CreateCashout({
       calculateFromCredit(amount, sellFee, sellRate)
         .then((r) => {
           setCalc(r);
-          saveError(undefined);
         })
         .catch((error) => {
-          saveError(
+          notify(
             error instanceof RequestError
               ? buildRequestErrorMessage(i18n, error.cause)
               : {
+                  type: "error",
                   title: i18n.str`Could not estimate the cashout`,
                   description: error.message,
                 },
@@ -321,9 +321,6 @@ function CreateCashout({
 
   return (
     <div>
-      {error && (
-        <ErrorBannerFloat error={error} onClear={() => saveError(undefined)} />
-      )}
       <h1>New cashout</h1>
       <form class="pure-form">
         <fieldset>
@@ -341,13 +338,15 @@ function CreateCashout({
           />
         </fieldset>
         <fieldset>
-          <label>
+          <label for="amount">
             {form.isDebit
               ? i18n.str`Amount to send`
               : i18n.str`Amount to receive`}
+            
           </label>
           <div style={{ display: "flex" }}>
             <Amount
+              name="amount"
               currency={amount.currency}
               value={form.amount}
               onChange={(v) => {
@@ -376,24 +375,27 @@ function CreateCashout({
           <input value={sellRate} disabled />
         </fieldset>
         <fieldset>
-          <label>{i18n.str`Balance now`}</label>
+          <label for="balance-now">{i18n.str`Balance now`}</label>
           <Amount
+            name="banace-now"
             currency={balance.currency}
             value={Amounts.stringifyValue(balance)}
           />
         </fieldset>
         <fieldset>
-          <label
+          <label for="total-cost"
             style={{ fontWeight: "bold", color: "red" }}
           >{i18n.str`Total cost`}</label>
           <Amount
+            name="total-cost"
             currency={balance.currency}
             value={Amounts.stringifyValue(calc.debit)}
           />
         </fieldset>
         <fieldset>
-          <label>{i18n.str`Balance after`}</label>
+          <label for="balance-after">{i18n.str`Balance after`}</label>
           <Amount
+            name="balance-after"
             currency={balance.currency}
             value={balanceAfter ? Amounts.stringifyValue(balanceAfter) : ""}
           />
@@ -401,16 +403,18 @@ function CreateCashout({
         {Amounts.isZero(sellFee) ? undefined : (
           <Fragment>
             <fieldset>
-              <label>{i18n.str`Amount after conversion`}</label>
+              <label for="amount-conversiojn">{i18n.str`Amount after conversion`}</label>
               <Amount
+                name="amount-conversion"
                 currency={fiatCurrency}
                 value={Amounts.stringifyValue(calc.beforeFee)}
               />
             </fieldset>
 
             <fieldset>
-              <label>{i18n.str`Cashout fee`}</label>
+              <label form="cashout-fee">{i18n.str`Cashout fee`}</label>
               <Amount
+                name="cashout-fee"
                 currency={fiatCurrency}
                 value={Amounts.stringifyValue(sellFee)}
               />
@@ -418,10 +422,11 @@ function CreateCashout({
           </Fragment>
         )}
         <fieldset>
-          <label
+          <label for="total"
             style={{ fontWeight: "bold", color: "green" }}
           >{i18n.str`Total cashout transfer`}</label>
           <Amount
+            name="total"
             currency={fiatCurrency}
             value={Amounts.stringifyValue(calc.credit)}
           />
@@ -511,7 +516,7 @@ function CreateCashout({
                 onComplete(res.data.uuid);
               } catch (error) {
                 if (error instanceof RequestError) {
-                  saveError(
+                  notify(
                     buildRequestErrorMessage(i18n, error.cause, {
                       onClientError: (status) =>
                         status === HttpStatusCode.BadRequest
@@ -530,14 +535,13 @@ function CreateCashout({
                     }),
                   );
                 } else {
-                  saveError({
-                    title: i18n.str`Operation failed, please report`,
-                    description:
-                      error instanceof Error
-                        ? error.message
-                        : JSON.stringify(error),
-                  });
-                }
+                  notifyError(
+                    i18n.str`Operation failed, please report`,
+                    (error instanceof Error
+                      ? error.message
+                      : JSON.stringify(error)) as TranslatedString
+                  )
+            }
               }
             }}
           >
@@ -565,7 +569,6 @@ export function ShowCashoutDetails({
   const result = useCashoutDetails(id);
   const { abortCashout, confirmCashout } = useCircuitAccountAPI();
   const [code, setCode] = useState<string | undefined>(undefined);
-  const [error, saveError] = useState<ErrorMessage | undefined>();
   if (!result.ok) return onLoadNotOk(result);
   const errors = undefinedIfEmpty({
     code: !code ? i18n.str`required` : undefined,
@@ -574,9 +577,6 @@ export function ShowCashoutDetails({
   return (
     <div>
       <h1>Cashout details {id}</h1>
-      {error && (
-        <ErrorBannerFloat error={error} onClear={() => saveError(undefined)} />
-      )}
       <form class="pure-form">
         <fieldset>
           <label>
@@ -661,7 +661,7 @@ export function ShowCashoutDetails({
                   onCancel();
                 } catch (error) {
                   if (error instanceof RequestError) {
-                    saveError(
+                    notify(
                       buildRequestErrorMessage(i18n, error.cause, {
                         onClientError: (status) =>
                           status === HttpStatusCode.NotFound
@@ -672,14 +672,13 @@ export function ShowCashoutDetails({
                       }),
                     );
                   } else {
-                    saveError({
-                      title: i18n.str`Operation failed, please report`,
-                      description:
-                        error instanceof Error
-                          ? error.message
-                          : JSON.stringify(error),
-                    });
-                  }
+                    notifyError(
+                      i18n.str`Operation failed, please report`,
+                      (error instanceof Error
+                        ? error.message
+                        : JSON.stringify(error)) as TranslatedString
+                    )
+                }
                 }
               }}
             >
@@ -699,7 +698,7 @@ export function ShowCashoutDetails({
                   });
                 } catch (error) {
                   if (error instanceof RequestError) {
-                    saveError(
+                    notify(
                       buildRequestErrorMessage(i18n, error.cause, {
                         onClientError: (status) =>
                           status === HttpStatusCode.NotFound
@@ -714,14 +713,13 @@ export function ShowCashoutDetails({
                       }),
                     );
                   } else {
-                    saveError({
-                      title: i18n.str`Operation failed, please report`,
-                      description:
-                        error instanceof Error
-                          ? error.message
-                          : JSON.stringify(error),
-                    });
-                  }
+                    notifyError(
+                      i18n.str`Operation failed, please report`,
+                      (error instanceof Error
+                        ? error.message
+                        : JSON.stringify(error)) as TranslatedString
+                    )
+                }
                 }
               }}
             >
diff --git a/packages/demobank-ui/src/pages/HomePage.tsx b/packages/demobank-ui/src/pages/HomePage.tsx
index 86e511284..e00daf278 100644
--- a/packages/demobank-ui/src/pages/HomePage.tsx
+++ b/packages/demobank-ui/src/pages/HomePage.tsx
@@ -17,6 +17,7 @@
 import {
   HttpStatusCode,
   Logger,
+  TranslatedString,
   parseWithdrawUri,
   stringifyWithdrawUri,
 } from "@gnu-taler/taler-util";
@@ -24,18 +25,20 @@ import {
   ErrorType,
   HttpResponse,
   HttpResponsePaginated,
+  notify,
+  notifyError,
   useTranslationContext,
 } from "@gnu-taler/web-util/browser";
 import { Fragment, VNode, h } from "preact";
 import { Loading } from "../components/Loading.js";
 import { useBackendContext } from "../context/backend.js";
 import { getInitialBackendBaseURL } from "../hooks/backend.js";
-import { notifyError, notifyInfo } from "../hooks/notification.js";
 import { useSettings } from "../hooks/settings.js";
 import { AccountPage } from "./AccountPage/index.js";
 import { AdminPage } from "./AdminPage.js";
 import { LoginForm } from "./LoginForm.js";
 import { WithdrawalQRCode } from "./WithdrawalQRCode.js";
+import { error } from "console";
 
 const logger = new Logger("AccountPage");
 
@@ -100,9 +103,10 @@ export function WithdrawalOperationPage({
   const { i18n } = useTranslationContext();
 
   if (!parsedUri) {
-    notifyError({
-      title: i18n.str`The Withdrawal URI is not valid: "${uri}"`,
-    });
+    notifyError(
+      i18n.str`The Withdrawal URI is not valid: "${uri}"`,
+      undefined
+    );
     return <Loading />;
   }
 
@@ -132,46 +136,46 @@ export function handleNotOkResult(
     if (!result.ok) {
       switch (result.type) {
         case ErrorType.TIMEOUT: {
-          notifyError({
-            title: i18n.str`Request timeout, try again later.`,
-          });
+          notifyError(i18n.str`Request timeout, try again later.`, undefined);
           break;
         }
         case ErrorType.CLIENT: {
           if (result.status === HttpStatusCode.Unauthorized) {
-            notifyError({
-              title: i18n.str`Wrong credentials`,
-            });
+            notifyError(i18n.str`Wrong credentials`, undefined);
             return <LoginForm onRegister={onRegister} />;
           }
           const errorData = result.payload;
-          notifyError({
+          notify({
+            type: "error",
             title: i18n.str`Could not load due to a client error`,
-            description: errorData.error.description,
+            description: errorData.error.description as TranslatedString,
             debug: JSON.stringify(result),
           });
           break;
         }
         case ErrorType.SERVER: {
-          notifyError({
+          notify({
+            type: "error", 
             title: i18n.str`Server returned with error`,
-            description: result.payload.error.description,
+            description: result.payload.error.description as TranslatedString,
             debug: JSON.stringify(result.payload),
           });
           break;
         }
         case ErrorType.UNREADABLE: {
-          notifyError({
+          notify({
+            type:"error",
             title: i18n.str`Unexpected error.`,
-            description: `Response from ${result.info?.url} is unreadable, http status: ${result.status}`,
+            description: i18n.str`Response from ${result.info?.url} is unreadable, http status: ${result.status}`,
             debug: JSON.stringify(result),
           });
           break;
         }
         case ErrorType.UNEXPECTED: {
-          notifyError({
+          notify({
+            type:"error",
             title: i18n.str`Unexpected error.`,
-            description: `Diagnostic from ${result.info?.url} is "${result.message}"`,
+            description: i18n.str`Diagnostic from ${result.info?.url} is "${result.message}"`,
             debug: JSON.stringify(result),
           });
           break;
diff --git a/packages/demobank-ui/src/pages/LoginForm.tsx b/packages/demobank-ui/src/pages/LoginForm.tsx
index f0ae97d60..46039005a 100644
--- a/packages/demobank-ui/src/pages/LoginForm.tsx
+++ b/packages/demobank-ui/src/pages/LoginForm.tsx
@@ -14,16 +14,14 @@
  GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 
-import { HttpStatusCode } from "@gnu-taler/taler-util";
-import { ErrorType, useTranslationContext } from "@gnu-taler/web-util/browser";
+import { HttpStatusCode, TranslatedString } from "@gnu-taler/taler-util";
+import { ErrorType, notifyError, useTranslationContext } from "@gnu-taler/web-util/browser";
 import { Fragment, VNode, h } from "preact";
 import { useEffect, useRef, useState } from "preact/hooks";
 import { useBackendContext } from "../context/backend.js";
 import { useCredentialsChecker } from "../hooks/backend.js";
-import { ErrorMessage } from "../hooks/notification.js";
 import { bankUiSettings } from "../settings.js";
 import { undefinedIfEmpty } from "../utils.js";
-import { ErrorBannerFloat } from "./BankFrame.js";
 import { USERNAME_REGEX } from "./RegistrationPage.js";
 import { ShowInputErrorLabel } from "../components/ShowInputErrorLabel.js";
 
@@ -36,177 +34,202 @@ export function LoginForm({ onRegister }: { onRegister?: () => void }): VNode {
   const [password, setPassword] = useState<string | undefined>();
   const { i18n } = useTranslationContext();
   const testLogin = useCredentialsChecker();
-  const [error, saveError] = useState<ErrorMessage | undefined>();
   const ref = useRef<HTMLInputElement>(null);
   useEffect(function focusInput() {
     ref.current?.focus();
   }, []);
+  const [busy, setBusy] = useState<Record<string, undefined>>()
 
   const errors = undefinedIfEmpty({
     username: !username
       ? i18n.str`Missing username`
       : !USERNAME_REGEX.test(username)
-      ? i18n.str`Use letters and numbers only, and start with a lowercase letter`
-      : undefined,
+        ? i18n.str`Use letters and numbers only, and start with a lowercase letter`
+        : undefined,
     password: !password ? i18n.str`Missing password` : undefined,
-  });
+  }) ?? busy;
+
+  function saveError({ title, description, debug }: { title: TranslatedString, description?: TranslatedString, debug?: any }) {
+    notifyError(title, description, debug)
+  }
+
+  async function doLogin() {
+    if (!username || !password) return;
+    setBusy({})
+    const testResult = await testLogin(username, password);
+    if (testResult.valid) {
+      backend.logIn({ username, password });
+    } else {
+      if (testResult.requestError) {
+        const { cause } = testResult;
+        switch (cause.type) {
+          case ErrorType.CLIENT: {
+            if (cause.status === HttpStatusCode.Unauthorized) {
+              saveError({
+                title: i18n.str`Wrong credentials for "${username}"`,
+              });
+            } else
+            if (cause.status === HttpStatusCode.NotFound) {
+              saveError({
+                title: i18n.str`Account not found`,
+              });
+            } else {
+              saveError({
+                title: i18n.str`Could not load due to a client error`,
+                description: cause.payload.error.description,
+                debug: JSON.stringify(cause.payload),
+              });
+            }
+            break;
+          }
+          case ErrorType.SERVER: {
+            saveError({
+              title: i18n.str`Server had a problem, try again later or report.`,
+              description: cause.payload.error.description,
+              debug: JSON.stringify(cause.payload),
+            });
+            break;
+          }
+          case ErrorType.TIMEOUT: {
+            saveError({
+              title: i18n.str`Request timeout, try again later.`,
+            });
+            break;
+          }
+          case ErrorType.UNREADABLE: {
+            saveError({
+              title: i18n.str`Unexpected error.`,
+              description: `Response from ${cause.info?.url} is unreadable, http status: ${cause.status}` as TranslatedString,
+              debug: JSON.stringify(cause),
+            });
+            break;
+          }
+          default: {
+            saveError({
+              title: i18n.str`Unexpected error, please report.`,
+              description: `Diagnostic from ${cause.info?.url} is "${cause.message}"` as TranslatedString,
+              debug: JSON.stringify(cause),
+            });
+            break;
+          }
+        }
+      } else {
+        saveError({
+          title: i18n.str`Unexpected error, please report.`,
+          debug: JSON.stringify(testResult.error),
+        });
+      }
+      backend.logOut();
+    }
+    setPassword(undefined);
+    setBusy(undefined)
+  }
 
   return (
     <Fragment>
-      <h1 class="nav">{i18n.str`Welcome to ${bankUiSettings.bankName}!`}</h1>
-      {error && (
+      <h1 class="nav"></h1>
+      {/* {error && (
         <ErrorBannerFloat error={error} onClear={() => saveError(undefined)} />
-      )}
-      <div class="login-div">
-        <form
-          class="login-form"
-          noValidate
-          onSubmit={(e) => {
-            e.preventDefault();
-          }}
-          autoCapitalize="none"
-          autoCorrect="off"
-        >
-          <div class="pure-form">
-            <h2>{i18n.str`Please login!`}</h2>
-            <p class="unameFieldLabel loginFieldLabel formFieldLabel">
-              <label for="username">{i18n.str`Username:`}</label>
-            </p>
-            <input
-              ref={ref}
-              autoFocus
-              type="text"
-              name="username"
-              id="username"
-              value={username ?? ""}
-              enterkeyhint="next"
-              placeholder="Username"
-              autocomplete="username"
-              required
-              onInput={(e): void => {
-                setUsername(e.currentTarget.value);
-              }}
-            />
-            <ShowInputErrorLabel
-              message={errors?.username}
-              isDirty={username !== undefined}
-            />
-            <p class="passFieldLabel loginFieldLabel formFieldLabel">
-              <label for="password">{i18n.str`Password:`}</label>
-            </p>
-            <input
-              type="password"
-              name="password"
-              id="password"
-              autocomplete="current-password"
-              enterkeyhint="send"
-              value={password ?? ""}
-              placeholder="Password"
-              required
-              onInput={(e): void => {
-                setPassword(e.currentTarget.value);
-              }}
-            />
-            <ShowInputErrorLabel
-              message={errors?.password}
-              isDirty={password !== undefined}
-            />
-            <br />
-            <button
-              type="submit"
-              class="pure-button pure-button-primary"
-              disabled={!!errors}
-              onClick={async (e) => {
-                e.preventDefault();
-                if (!username || !password) return;
-                const testResult = await testLogin(username, password);
-                if (testResult.valid) {
-                  backend.logIn({ username, password });
-                } else {
-                  if (testResult.requestError) {
-                    const { cause } = testResult;
-                    switch (cause.type) {
-                      case ErrorType.CLIENT: {
-                        if (cause.status === HttpStatusCode.Unauthorized) {
-                          saveError({
-                            title: i18n.str`Wrong credentials for "${username}"`,
-                          });
-                        }
-                        if (cause.status === HttpStatusCode.NotFound) {
-                          saveError({
-                            title: i18n.str`Account not found`,
-                          });
-                        } else {
-                          saveError({
-                            title: i18n.str`Could not load due to a client error`,
-                            description: cause.payload.error.description,
-                            debug: JSON.stringify(cause.payload),
-                          });
-                        }
-                        break;
-                      }
-                      case ErrorType.SERVER: {
-                        saveError({
-                          title: i18n.str`Server had a problem, try again later or report.`,
-                          description: cause.payload.error.description,
-                          debug: JSON.stringify(cause.payload),
-                        });
-                        break;
-                      }
-                      case ErrorType.TIMEOUT: {
-                        saveError({
-                          title: i18n.str`Request timeout, try again later.`,
-                        });
-                        break;
-                      }
-                      case ErrorType.UNREADABLE: {
-                        saveError({
-                          title: i18n.str`Unexpected error.`,
-                          description: `Response from ${cause.info?.url} is unreadable, http status: ${cause.status}`,
-                          debug: JSON.stringify(cause),
-                        });
-                        break;
-                      }
-                      default: {
-                        saveError({
-                          title: i18n.str`Unexpected error, please report.`,
-                          description: `Diagnostic from ${cause.info?.url} is "${cause.message}"`,
-                          debug: JSON.stringify(cause),
-                        });
-                        break;
-                      }
-                    }
-                  } else {
-                    saveError({
-                      title: i18n.str`Unexpected error, please report.`,
-                      debug: JSON.stringify(testResult.error),
-                    });
-                  }
-                  backend.logOut();
-                }
-                setUsername(undefined);
-                setPassword(undefined);
-              }}
-            >
-              {i18n.str`Login`}
-            </button>
-
-            {bankUiSettings.allowRegistrations && onRegister ? (
-              <button
-                class="pure-button pure-button-secondary btn-cancel"
+      )} */}
+
+      <div class="flex min-h-full flex-col justify-center">
+        <div class="sm:mx-auto sm:w-full sm:max-w-sm">
+          <h2 class="text-center text-2xl font-bold leading-9 tracking-tight text-gray-900">{i18n.str`Welcome to ${bankUiSettings.bankName}!`}</h2>
+        </div>
+
+        <div class="mt-10 sm:mx-auto sm:w-full sm:max-w-sm">
+          <form class="space-y-6" noValidate
+            onSubmit={(e) => {
+              e.preventDefault();
+            }}
+            autoCapitalize="none"
+            autoCorrect="off"
+          >
+            <div>
+              <label for="username" class="block text-sm font-medium leading-6 text-gray-900">
+                <i18n.Translate>Username</i18n.Translate>
+              </label>
+              <div class="mt-2">
+                <input
+                  ref={ref}
+                  autoFocus
+                  type="text"
+                  name="username"
+                  id="username"
+                  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={username ?? ""}
+                  enterkeyhint="next"
+                  placeholder="identification"
+                  autocomplete="username"
+                  required
+                  onInput={(e): void => {
+                    setUsername(e.currentTarget.value);
+                  }}
+                />
+                <ShowInputErrorLabel
+                  message={errors?.username}
+                  isDirty={username !== undefined}
+                />
+              </div>
+            </div>
+
+            <div>
+              <div class="flex items-center justify-between">
+                <label for="password" class="block text-sm font-medium leading-6 text-gray-900">Password</label>
+              </div>
+              <div class="mt-2">
+                <input
+                  type="password"
+                  name="password"
+                  id="password"
+                  autocomplete="current-password"
+                  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"
+                  enterkeyhint="send"
+                  value={password ?? ""}
+                  placeholder="Password"
+                  required
+                  onInput={(e): void => {
+                    setPassword(e.currentTarget.value);
+                  }}
+                />
+                <ShowInputErrorLabel
+                  message={errors?.password}
+                  isDirty={password !== undefined}
+                />
+              </div>
+            </div>
+
+            <div>
+              <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"
+                disabled={!!errors}
                 onClick={(e) => {
-                  e.preventDefault();
-                  onRegister();
+                  e.preventDefault()
+                  doLogin()
                 }}
               >
-                {i18n.str`Register`}
+                <i18n.Translate>Log in</i18n.Translate>
               </button>
-            ) : (
-              <div />
-            )}
-          </div>
-        </form>
+            </div>
+          </form>
+
+          {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"
+                onClick={(e) => {
+                  e.preventDefault()
+                  onRegister()
+                }}
+              >
+                <i18n.Translate>Register</i18n.Translate>
+              </button>
+            </p>
+          }
+        </div>
       </div>
+
+
     </Fragment>
   );
 }
diff --git a/packages/demobank-ui/src/pages/PaymentOptions.tsx b/packages/demobank-ui/src/pages/PaymentOptions.tsx
index cf3f41deb..c82c1b28d 100644
--- a/packages/demobank-ui/src/pages/PaymentOptions.tsx
+++ b/packages/demobank-ui/src/pages/PaymentOptions.tsx
@@ -15,10 +15,9 @@
  */
 
 import { AmountJson } from "@gnu-taler/taler-util";
-import { useTranslationContext } from "@gnu-taler/web-util/browser";
+import { notifyInfo, useTranslationContext } from "@gnu-taler/web-util/browser";
 import { h, VNode } from "preact";
 import { useState } from "preact/hooks";
-import { notifyInfo } from "../hooks/notification.js";
 import { PaytoWireTransferForm } from "./PaytoWireTransferForm.js";
 import { WalletWithdrawForm } from "./WalletWithdrawForm.js";
 import { useSettings } from "../hooks/settings.js";
@@ -31,7 +30,8 @@ export function PaymentOptions({ limit }: { limit: AmountJson }): VNode {
   const { i18n } = useTranslationContext();
   const [settings, updateSettings] = useSettings();
 
-  const [tab, setTab] = useState<"charge-wallet" | "wire-transfer" | undefined>(undefined);
+  const [tab, setTab] = useState<"charge-wallet" | "wire-transfer" | undefined>();
+  // const [tab, setTab] = useState<"charge-wallet" | "wire-transfer" | undefined>(undefined);
 
   return (<fieldset>
     <legend class="px-4 text-base font-semibold leading-6 text-gray-900">
@@ -41,34 +41,32 @@ export function PaymentOptions({ limit }: { limit: AmountJson }): VNode {
     <div class="px-4 mt-4 grid grid-cols-1 gap-y-6 sm:grid-cols-2 sm:gap-x-4">
       {/* <!-- Active: "border-indigo-600 ring-2 ring-indigo-600", Not Active: "border-gray-300" --> */}
       <label class={"relative flex cursor-pointer rounded-lg border bg-white p-4 shadow-sm focus:outline-none" + (tab === "charge-wallet" ? "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={() => {
+        <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" onClick={() => {
           setTab("charge-wallet")
         }} />
         <span class="flex flex-1">
           <span class="flex flex-col">
-            <span id="project-type-0-label" class="block text-sm font-semibold font-medium text-gray-900">
-              <i18n.Translate>a Taler Wallet</i18n.Translate>
+            <span id="project-type-0-label" class="block text-sm font-medium text-gray-900">
+              <i18n.Translate>a <b>Taler</b> wallet</i18n.Translate>
             </span>
             <span id="project-type-0-description-0" class="mt-1 flex items-center text-sm text-gray-500">
               <i18n.Translate>Withdraw digital money into your mobile wallet or browser extension</i18n.Translate>
             </span>
           </span>
         </span>
-        {/* <!-- Not Checked: "invisible" --> */}
         <svg class="h-5 w-5 text-indigo-600" style={{ visibility: tab === "charge-wallet" ? "visible" : "hidden" }} 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>
       </label>
 
 
-      {/* <!-- Active: "border-indigo-600 ring-2 ring-indigo-600", Not Active: "border-gray-300" --> */}
       <label class={"relative flex cursor-pointer rounded-lg border bg-white p-4 shadow-sm focus:outline-none" + (tab === "wire-transfer" ? "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={() => {
+        <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" onClick={() => {
           setTab("wire-transfer")
         }} />
         <span class="flex flex-1">
           <span class="flex flex-col">
-            <span id="project-type-1-label" class="block text-sm font-semibold font-medium text-gray-900">
+            <span id="project-type-1-label" class="block text-sm font-medium text-gray-900">
               <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">
@@ -88,6 +86,9 @@ export function PaymentOptions({ limit }: { limit: AmountJson }): VNode {
         onSuccess={(id) => {
           updateSettings("currentWithdrawalOperationId", id);
         }}
+        onCancel={() => {
+          setTab(undefined)
+        }}
       />
     )}
     {tab === "wire-transfer" && (
@@ -97,32 +98,11 @@ export function PaymentOptions({ limit }: { limit: AmountJson }): VNode {
         onSuccess={() => {
           notifyInfo(i18n.str`Wire transfer created!`);
         }}
+        onCancel={() => {
+          setTab(undefined)
+        }}
       />
     )}
 
   </fieldset>)
-  {/* return (
-    <article>
-      <div class="payments">
-        <div class="tab">
-          <button
-            class={tab === "charge-wallet" ? "tablinks active" : "tablinks"}
-            onClick={(): void => {
-              setTab("charge-wallet");
-            }}
-          >
-            {i18n.str`Withdraw `}
-          </button>
-          <button
-            class={tab === "wire-transfer" ? "tablinks active" : "tablinks"}
-            onClick={(): void => {
-              setTab("wire-transfer");
-            }}
-          >
-            {i18n.str`Wire transfer`}
-          </button>
-        </div>
-      </div>
-    </article>
-  ); */}
 }
diff --git a/packages/demobank-ui/src/pages/PaytoWireTransferForm.tsx b/packages/demobank-ui/src/pages/PaytoWireTransferForm.tsx
index 1107360bd..5e0624cbf 100644
--- a/packages/demobank-ui/src/pages/PaytoWireTransferForm.tsx
+++ b/packages/demobank-ui/src/pages/PaytoWireTransferForm.tsx
@@ -19,17 +19,21 @@ import {
   Amounts,
   HttpStatusCode,
   Logger,
-  parsePaytoUri
+  TranslatedString,
+  buildPayto,
+  parsePaytoUri,
+  stringifyPaytoUri
 } from "@gnu-taler/taler-util";
 import {
   RequestError,
+  notify,
+  notifyError,
   useTranslationContext,
 } from "@gnu-taler/web-util/browser";
-import { h, VNode, Fragment } from "preact";
+import { h, VNode, Fragment, Ref } from "preact";
 import { useEffect, useRef, useState } from "preact/hooks";
 import { ShowInputErrorLabel } from "../components/ShowInputErrorLabel.js";
 import { useAccessAPI } from "../hooks/access.js";
-import { notifyError } from "../hooks/notification.js";
 import {
   buildRequestErrorMessage,
   undefinedIfEmpty,
@@ -41,10 +45,12 @@ const logger = new Logger("PaytoWireTransferForm");
 export function PaytoWireTransferForm({
   focus,
   onSuccess,
+  onCancel,
   limit,
 }: {
   focus?: boolean;
   onSuccess: () => void;
+  onCancel: (() => void) | undefined;
   limit: AmountJson;
 }): VNode {
   const [isRawPayto, setIsRawPayto] = useState(false);
@@ -105,7 +111,51 @@ export function PaytoWireTransferForm({
                   ? i18n.str`IBAN should have just uppercased letters and numbers`
                   : validateIBAN(parsed.iban, i18n),
   });
-  // if (!isRawPayto) {
+
+  async function doSend() {
+    let paytoUri: string | undefined;
+
+    if (rawPaytoInput) {
+      paytoUri = rawPaytoInput
+    } else {
+      if (!iban || !subject) return;
+      const ibanPayto = buildPayto("iban", iban, undefined);
+      ibanPayto.params.message = encodeURIComponent(subject);
+      paytoUri = stringifyPaytoUri(ibanPayto);
+    }
+
+    try {
+      await createTransaction({
+        paytoUri,
+        amount: `${limit.currency}:${amount}`,
+      });
+      onSuccess();
+      setAmount(undefined);
+      setIban(undefined);
+      setSubject(undefined);
+      rawPaytoInputSetter(undefined)
+    } catch (error) {
+      if (error instanceof RequestError) {
+        notify(
+          buildRequestErrorMessage(i18n, error.cause, {
+            onClientError: (status) =>
+              status === HttpStatusCode.BadRequest
+                ? i18n.str`The request was invalid or the payto://-URI used unacceptable features.`
+                : undefined,
+          }),
+        );
+      } else {
+        notifyError(
+          i18n.str`Operation failed, please report`,
+          (error instanceof Error
+            ? error.message
+            : JSON.stringify(error)) as TranslatedString
+        )
+}
+    }
+
+  }
+
   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">
     <div class="px-4 sm:px-0">
       <h2 class="text-base font-semibold leading-7 text-gray-900"><i18n.Translate>Transfer details</i18n.Translate></h2>
@@ -118,7 +168,7 @@ export function PaytoWireTransferForm({
             }} />
             <span class="flex flex-1">
               <span class="flex flex-col">
-                <span id="project-type-0-label" class="block text-sm  font-medium text-gray-900">
+                <span class="block text-sm  font-medium text-gray-900">
                   <i18n.Translate>form</i18n.Translate>
                 </span>
               </span>
@@ -133,7 +183,7 @@ export function PaytoWireTransferForm({
             }} />
             <span class="flex flex-1">
               <span class="flex flex-col">
-                <span id="project-type-1-label" class="block text-sm  font-medium text-gray-900">
+                <span class="block text-sm  font-medium text-gray-900">
                   <i18n.Translate>payto://</i18n.Translate>
                 </span>
               </span>
@@ -143,23 +193,31 @@ export function PaytoWireTransferForm({
       </div>
     </div>
 
-    <form class="bg-white shadow-sm ring-1 ring-gray-900/5 sm:rounded-xl md:col-span-2">
+    <form
+      class="bg-white shadow-sm ring-1 ring-gray-900/5 sm:rounded-xl md:col-span-2"
+      autoCapitalize="none"
+      autoCorrect="off"
+      onSubmit={e => {
+        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">
           {!isRawPayto ?
             <Fragment>
 
-              <div class="sm:col-span-3">
-                <label for="first-name" class="block text-sm font-medium leading-6 text-gray-900">{i18n.str`Account number`}</label>
+              <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"
-                    id="iban"
                     name="iban"
+                    id="iban"
                     value={iban ?? ""}
                     placeholder="CC0123456789"
+                    autocomplete="off"
                     required
                     pattern={ibanRegex}
                     onInput={(e): void => {
@@ -171,21 +229,18 @@ export function PaytoWireTransferForm({
                     isDirty={iban !== undefined}
                   />
                 </div>
-                <p class="mt-2 text-sm text-gray-500" id="email-description">the receiver of the money</p>
-              </div>
-
-              <div class="sm:col-span-3">
+                <p class="mt-2 text-sm text-gray-500" >the receiver of the money</p>
               </div>
 
-              <div class="sm:col-span-3">
-                <label for="first-name" class="block text-sm font-medium leading-6 text-gray-900">{i18n.str`Transfer subject`}</label>
+              <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
@@ -198,37 +253,40 @@ export function PaytoWireTransferForm({
                     isDirty={subject !== undefined}
                   />
                 </div>
-                <p class="mt-2 text-sm text-gray-500" id="email-description">some text to identify the transfer</p>
-
-              </div>
-
-              <div class="sm:col-span-3">
+                <p class="mt-2 text-sm text-gray-500" >some text to identify the transfer</p>
               </div>
 
-              <div class="sm:col-span-3">
-                <label for="first-name" class="block text-sm font-medium leading-6 text-gray-900">{i18n.str`Amount`}</label>
-                <div class="mt-2">
-                  <input type="text" name="first-name" id="first-name" autocomplete="given-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" />
-                </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)
+                  }}
+                />
+                <ShowInputErrorLabel
+                  message={errorsWire?.subject}
+                  isDirty={subject !== undefined}
+                />
+                <p class="mt-2 text-sm text-gray-500" >amount to transfer</p>
               </div>
 
-              <div class="sm:col-span-3">
-              </div>
             </Fragment> :
             <Fragment>
               <div class="sm:col-span-6">
-                <label for="first-name" class="block text-sm font-medium leading-6 text-gray-900">{i18n.str`payto URI:`}</label>
+                <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}
-                    id="address"
                     value={rawPaytoInput ?? ""}
                     required
                     placeholder={i18n.str`payto://iban/[receiver-iban]?message=[subject]&amount=[${limit.currency}:X.Y]`}
-                    // pattern={`payto://iban/[A-Z][A-Z][0-9]+?message=[a-zA-Z0-9 ]+&amount=${currency}:[0-9]+(.[0-9]+)?`}
                     onInput={(e): void => {
                       rawPaytoInputSetter(e.currentTarget.value);
                     }}
@@ -244,9 +302,23 @@ export function PaytoWireTransferForm({
           }
         </div>
       </div>
-      <div class="flex items-center 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">Cancel</button>
-        <button type="submit" class="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">
+      <div class="flex items-center justify-between gap-x-6 border-t border-gray-900/10 px-4 py-4 sm:px-8">
+        {onCancel ?
+          <button type="button" class="text-sm font-semibold leading-6 text-gray-900"
+            onClick={onCancel}
+          >
+            <i18n.Translate>Cancel</i18n.Translate>
+          </button>
+          : <div />
+        }
+        <button type="submit"
+          class="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"
+          disabled={isRawPayto ? !!errorsPayto : !!errorsWire}
+          onClick={(e) => {
+            e.preventDefault()
+            doSend()
+          }}
+        >
           <i18n.Translate>Send</i18n.Translate>
         </button>
       </div>
@@ -262,8 +334,6 @@ export function PaytoWireTransferForm({
   //       onSubmit={(e) => {
   //         e.preventDefault();
   //       }}
-  //       autoCapitalize="none"
-  //       autoCorrect="off"
   //     >
   //       <label for="iban">{i18n.str`Receiver IBAN:`}</label>&nbsp;
 
@@ -318,39 +388,7 @@ export function PaytoWireTransferForm({
   //             if (!(iban && subject && amount)) {
   //               return;
   //             }
-  //             const ibanPayto = buildPayto("iban", iban, undefined);
-  //             ibanPayto.params.message = encodeURIComponent(subject);
-  //             const paytoUri = stringifyPaytoUri(ibanPayto);
-
-  //             try {
-  //               await createTransaction({
-  //                 paytoUri,
-  //                 amount: `${limit.currency}:${amount}`,
-  //               });
-  //               onSuccess();
-  //               setAmount(undefined);
-  //               setIban(undefined);
-  //               setSubject(undefined);
-  //             } catch (error) {
-  //               if (error instanceof RequestError) {
-  //                 notifyError(
-  //                   buildRequestErrorMessage(i18n, error.cause, {
-  //                     onClientError: (status) =>
-  //                       status === HttpStatusCode.BadRequest
-  //                         ? i18n.str`The request was invalid or the payto://-URI used unacceptable features.`
-  //                         : undefined,
-  //                   }),
-  //                 );
-  //               } else {
-  //                 notifyError({
-  //                   title: i18n.str`Operation failed, please report`,
-  //                   description:
-  //                     error instanceof Error
-  //                       ? error.message
-  //                       : JSON.stringify(error),
-  //                 });
-  //               }
-  //             }
+
   //           }}
   //         />
   //         <input
@@ -389,3 +427,46 @@ export function PaytoWireTransferForm({
   //   </div>
   // );
 }
+export function Amount(
+  {
+    currency,
+    name,
+    value,
+    error,
+    onChange,
+  }: {
+    error?: string;
+    currency: string;
+    name: string;
+    value: string | undefined;
+    onChange?: (s: string) => void;
+  },
+  ref: Ref<HTMLInputElement>,
+): 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">
+          <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"
+          placeholder="0.00" aria-describedby="price-currency"
+          ref={ref}
+          name={name}
+          id={name}
+          autocomplete="off"
+          value={value ?? ""}
+          disabled={!onChange}
+          onInput={(e): void => {
+            if (onChange) {
+              onChange(e.currentTarget.value);
+            }
+          }}
+        />
+      </div>
+      <ShowInputErrorLabel message={error} isDirty={value !== undefined} />
+    </div>
+  );
+}
diff --git a/packages/demobank-ui/src/pages/QrCodeSection.tsx b/packages/demobank-ui/src/pages/QrCodeSection.tsx
index c27984569..7c1b3bdc5 100644
--- a/packages/demobank-ui/src/pages/QrCodeSection.tsx
+++ b/packages/demobank-ui/src/pages/QrCodeSection.tsx
@@ -17,17 +17,19 @@
 import {
   HttpStatusCode,
   stringifyWithdrawUri,
+  TranslatedString,
   WithdrawUriResult,
 } from "@gnu-taler/taler-util";
 import {
+  notify,
+  notifyError,
   RequestError,
   useTranslationContext,
 } from "@gnu-taler/web-util/browser";
-import { h, VNode } from "preact";
+import { Fragment, h, VNode } from "preact";
 import { useEffect } from "preact/hooks";
 import { QR } from "../components/QR.js";
 import { useAccessAnonAPI } from "../hooks/access.js";
-import { notifyError } from "../hooks/notification.js";
 import { buildRequestErrorMessage } from "../utils.js";
 
 export function QrCodeSection({
@@ -49,47 +51,87 @@ export function QrCodeSection({
   const talerWithdrawUri = stringifyWithdrawUri(withdrawUri);
 
   const { abortWithdrawal } = useAccessAnonAPI();
+
+  async function doAbort() {
+    try {
+      await abortWithdrawal(withdrawUri.withdrawalOperationId);
+      onAborted();
+    } catch (error) {
+      if (error instanceof RequestError) {
+        notify(
+          buildRequestErrorMessage(i18n, error.cause, {
+            onClientError: (status) =>
+              status === HttpStatusCode.Conflict
+                ? i18n.str`The reserve operation has been confirmed previously and can't be aborted`
+                : undefined,
+          }),
+        );
+      } else {
+        notifyError(
+          i18n.str`Operation failed, please report`,
+          (error instanceof Error
+            ? error.message
+            : JSON.stringify(error)) as TranslatedString
+        )
+      }
+    }
+  }
+
   return (
-    <section id="main" class="content">
-      <h1 class="nav">{i18n.str`Charge your GNU Taler wallet`}</h1>
-      <article>
-        <div class="qr-div ">
-          <a href={talerWithdrawUri} class="pure-button pure-button-primary">
-            <i18n.Translate>Continue with GNU Taler</i18n.Translate>
-          </a>
-          <p>{i18n.str`Or scan this QR code with your mobile to receive the coin in another device:`}</p>
-          <QR text={talerWithdrawUri} />
-          <a
-            class="pure-button btn-cancel"
-            onClick={async (e) => {
-              e.preventDefault();
-              try {
-                await abortWithdrawal(withdrawUri.withdrawalOperationId);
-                onAborted();
-              } catch (error) {
-                if (error instanceof RequestError) {
-                  notifyError(
-                    buildRequestErrorMessage(i18n, error.cause, {
-                      onClientError: (status) =>
-                        status === HttpStatusCode.Conflict
-                          ? i18n.str`The reserve operation has been confirmed previously and can't be aborted`
-                          : undefined,
-                    }),
-                  );
-                } else {
-                  notifyError({
-                    title: i18n.str`Operation failed, please report`,
-                    description:
-                      error instanceof Error
-                        ? error.message
-                        : JSON.stringify(error),
-                  });
-                }
-              }
-            }}
-          >{i18n.str`Cancel`}</a>
+    <Fragment>
+      <div class="bg-white shadow-xl sm:rounded-lg">
+        <div class="px-4 py-5 sm:p-6">
+          <h3 class="text-base font-semibold leading-6 text-gray-900">
+            <i18n.Translate>If you have a Taler wallet installed in this device</i18n.Translate>
+          </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>
+            </a>
+          </div>
+          <div class="mt-4 max-w-xl text-sm text-gray-500">
+            <p><i18n.Translate>
+              You will see the details of the operation in your wallet including the fees (if applies).
+              If you still one you can install it from <a class="font-semibold text-gray-500 hover:text-gray-400" href="https://taler.net/en/wallet.html">here</a>.
+            </i18n.Translate></p>
+          </div>
+          <div class="flex items-center justify-between gap-x-6 border-t border-gray-900/10 pt-2 mt-2 ">
+            <div />
+            <button type="button"
+              class="disabled:opacity-50 disabled:cursor-default cursor-pointer 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-600"
+              onClick={doAbort}
+            >
+              Cancel withdrawal
+            </button>
+          </div>
+        </div>
+      </div>
+
+      <div class="bg-white shadow-xl sm:rounded-lg mt-8">
+        <div class="px-4 py-5 sm:p-6">
+          <h3 class="text-base font-semibold leading-6 text-gray-900">
+            <i18n.Translate>Or if you have the wallet in another device</i18n.Translate>
+          </h3>
+          <div class="mt-4 max-w-xl text-sm text-gray-500">
+            <i18n.Translate>Scan the QR below to start the withdrawal</i18n.Translate>
+          </div>
+          <div class="mt-2 max-w-md ml-auto mr-auto">
+            <QR text={talerWithdrawUri} />
+          </div>
+        </div>
+        <div class="flex items-center justify-center gap-x-6 border-t border-gray-900/10 px-4 py-4 sm:px-8">
+          <button type="button"
+            class="disabled:opacity-50 disabled:cursor-default cursor-pointer 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-600"
+            onClick={doAbort}
+          >
+            Cancel withdrawal
+          </button>
         </div>
-      </article>
-    </section>
+      </div>
+
+    </Fragment>
   );
 }
diff --git a/packages/demobank-ui/src/pages/RegistrationPage.tsx b/packages/demobank-ui/src/pages/RegistrationPage.tsx
index e52a5b11b..b912b9060 100644
--- a/packages/demobank-ui/src/pages/RegistrationPage.tsx
+++ b/packages/demobank-ui/src/pages/RegistrationPage.tsx
@@ -13,19 +13,21 @@
  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 { HttpStatusCode, Logger } from "@gnu-taler/taler-util";
+import { HttpStatusCode, Logger, TranslatedString } from "@gnu-taler/taler-util";
 import {
   RequestError,
+  notify,
+  notifyError,
   useTranslationContext,
 } from "@gnu-taler/web-util/browser";
 import { Fragment, VNode, h } from "preact";
 import { useState } from "preact/hooks";
 import { useBackendContext } from "../context/backend.js";
 import { useTestingAPI } from "../hooks/access.js";
-import { notifyError } from "../hooks/notification.js";
 import { bankUiSettings } from "../settings.js";
 import { buildRequestErrorMessage, undefinedIfEmpty } from "../utils.js";
 import { ShowInputErrorLabel } from "../components/ShowInputErrorLabel.js";
+import { getRandomPassword, getRandomUsername } from "./rnd.js";
 
 const logger = new Logger("RegistrationPage");
 
@@ -61,147 +63,214 @@ function RegistrationForm({ onComplete }: { onComplete: () => void }): VNode {
     username: !username
       ? i18n.str`Missing username`
       : !USERNAME_REGEX.test(username)
-      ? i18n.str`Use letters and numbers only, and start with a lowercase letter`
-      : undefined,
+        ? i18n.str`Use letters and numbers only, and start with a lowercase letter`
+        : undefined,
     password: !password ? i18n.str`Missing password` : undefined,
     repeatPassword: !repeatPassword
       ? i18n.str`Missing password`
       : repeatPassword !== password
-      ? i18n.str`Passwords don't match`
-      : undefined,
+        ? i18n.str`Passwords don't match`
+        : undefined,
   });
 
+  async function doRegistrationStep() {
+    if (!username || !password) return;
+    try {
+      await register({ username, password });
+      setUsername(undefined);
+      backend.logIn({ username, password });
+      onComplete();
+    } catch (error) {
+      if (error instanceof RequestError) {
+        notify(
+          buildRequestErrorMessage(i18n, error.cause, {
+            onClientError: (status) =>
+              status === HttpStatusCode.Conflict
+                ? i18n.str`That username is already taken`
+                : undefined,
+          }),
+        );
+      } else {
+        notifyError(
+          i18n.str`Operation failed, please report`,
+          (error instanceof Error
+            ? error.message
+            : JSON.stringify(error)) as TranslatedString
+        )
+}
+    }
+    setPassword(undefined);
+    setRepeatPassword(undefined);
+}
+
+  async function delay(ms: number):Promise<void> {
+    return new Promise((resolve) => {
+      setTimeout(() => {
+        resolve(undefined);
+      }, ms)
+    })
+  }
+  async function doRandomRegistration(tries: number = 3) {
+    const user = getRandomUsername();
+    const pass = getRandomPassword();
+    try {
+      setUsername(undefined);
+      setPassword(undefined);
+      setRepeatPassword(undefined);
+      await register({ username: user, password: pass });
+      backend.logIn({ username: user, password: pass });
+      onComplete();
+    } catch (error) {
+      if (error instanceof RequestError) {
+        if (tries > 0) {
+          await delay(200)
+          await doRandomRegistration(tries-1)
+        } else {
+          notify(
+            buildRequestErrorMessage(i18n, error.cause, {
+              onClientError: (status) =>
+                status === HttpStatusCode.Conflict
+                  ? i18n.str`Could not create a random user`
+                  : undefined,
+            }),
+          );
+        }
+      } else {
+        notifyError(
+          i18n.str`Operation failed, please report`,
+          (error instanceof Error
+            ? error.message
+            : JSON.stringify(error)) as TranslatedString
+        )
+}
+    }
+  }
+
   return (
     <Fragment>
-      <h1 class="nav">{i18n.str`Welcome to ${bankUiSettings.bankName}!`}</h1>
-      <article>
-        <div class="register-div">
-          <form
-            class="register-form"
-            noValidate
+      <h1 class="nav"></h1>
+
+      <div class="flex min-h-full flex-col justify-center">
+        <div class="sm:mx-auto sm:w-full sm:max-w-sm">
+          <h2 class="text-center text-2xl font-bold leading-9 tracking-tight text-gray-900">{i18n.str`Sign up!`}</h2>
+        </div>
+
+        <div class="mt-10 sm:mx-auto sm:w-full sm:max-w-sm">
+          <form class="space-y-6" noValidate
             onSubmit={(e) => {
               e.preventDefault();
             }}
             autoCapitalize="none"
             autoCorrect="off"
           >
-            <div class="pure-form">
-              <h2>{i18n.str`Please register!`}</h2>
-              <p class="unameFieldLabel registerFieldLabel formFieldLabel">
-                <label for="register-un">{i18n.str`Username:`}</label>
-              </p>
-              <input
-                id="register-un"
-                name="register-un"
-                type="text"
-                placeholder="Username"
-                autocomplete="username"
-                value={username ?? ""}
-                onInput={(e): void => {
-                  setUsername(e.currentTarget.value);
-                }}
-              />
-              <ShowInputErrorLabel
-                message={errors?.username}
-                isDirty={username !== undefined}
-              />
-              <p class="unameFieldLabel registerFieldLabel formFieldLabel">
-                <label for="register-pw">{i18n.str`Password:`}</label>
-              </p>
-              <input
-                type="password"
-                name="register-pw"
-                id="register-pw"
-                placeholder="Password"
-                autocomplete="new-password"
-                value={password ?? ""}
-                required
-                onInput={(e): void => {
-                  setPassword(e.currentTarget.value);
-                }}
-              />
-              <ShowInputErrorLabel
-                message={errors?.password}
-                isDirty={password !== undefined}
-              />
-              <p class="unameFieldLabel registerFieldLabel formFieldLabel">
-                <label for="register-repeat">{i18n.str`Repeat Password:`}</label>
-              </p>
-              <input
-                type="password"
-                style={{ marginBottom: 8 }}
-                name="register-repeat"
-                id="register-repeat"
-                autocomplete="new-password"
-                placeholder="Same password"
-                value={repeatPassword ?? ""}
-                required
-                onInput={(e): void => {
-                  setRepeatPassword(e.currentTarget.value);
-                }}
-              />
-              <ShowInputErrorLabel
-                message={errors?.repeatPassword}
-                isDirty={repeatPassword !== undefined}
-              />
-              <br />
-              <button
-                class="pure-button pure-button-primary btn-register"
-                type="submit"
+            <div>
+              <label for="username" class="block text-sm font-medium leading-6 text-gray-900">
+                <i18n.Translate>Username</i18n.Translate>
+              </label>
+              <div class="mt-2">
+                <input
+                  autoFocus
+                  type="text"
+                  name="username"
+                  id="username"
+                  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={username ?? ""}
+                  enterkeyhint="next"
+                  placeholder="identification"
+                  autocomplete="username"
+                  required
+                  onInput={(e): void => {
+                    setUsername(e.currentTarget.value);
+                  }}
+                />
+                <ShowInputErrorLabel
+                  message={errors?.username}
+                  isDirty={username !== undefined}
+                />
+              </div>
+            </div>
+
+            <div>
+              <div class="flex items-center justify-between">
+                <label for="password" class="block text-sm font-medium leading-6 text-gray-900">Password</label>
+              </div>
+              <div class="mt-2">
+                <input
+                  type="password"
+                  name="password"
+                  id="password"
+                  autocomplete="current-password"
+                  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"
+                  enterkeyhint="send"
+                  value={password ?? ""}
+                  placeholder="Password"
+                  required
+                  onInput={(e): void => {
+                    setPassword(e.currentTarget.value);
+                  }}
+                />
+                <ShowInputErrorLabel
+                  message={errors?.password}
+                  isDirty={password !== undefined}
+                />
+              </div>
+            </div>
+
+            <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>
+              </div>
+              <div class="mt-2">
+                <input
+                  type="password"
+                  name="register-repeat"
+                  id="register-repeat"
+                  autocomplete="current-password"
+                  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"
+                  enterkeyhint="send"
+                  value={repeatPassword ?? ""}
+                  placeholder="Same password"
+                  required
+                  onInput={(e): void => {
+                    setRepeatPassword(e.currentTarget.value);
+                  }}
+                />
+                <ShowInputErrorLabel
+                  message={errors?.repeatPassword}
+                  isDirty={repeatPassword !== undefined}
+                />
+              </div>
+            </div>
+
+            <div>
+              <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"
                 disabled={!!errors}
-                onClick={async (e) => {
-                  e.preventDefault();
-
-                  if (!username || !password) return;
-                  try {
-                    const credentials = { username, password };
-                    await register(credentials);
-                    setUsername(undefined);
-                    setPassword(undefined);
-                    setRepeatPassword(undefined);
-                    backend.logIn(credentials);
-                    onComplete();
-                  } catch (error) {
-                    if (error instanceof RequestError) {
-                      notifyError(
-                        buildRequestErrorMessage(i18n, error.cause, {
-                          onClientError: (status) =>
-                            status === HttpStatusCode.Conflict
-                              ? i18n.str`That username is already taken`
-                              : undefined,
-                        }),
-                      );
-                    } else {
-                      notifyError({
-                        title: i18n.str`Operation failed, please report`,
-                        description:
-                          error instanceof Error
-                            ? error.message
-                            : JSON.stringify(error),
-                      });
-                    }
-                  }
-                }}
-              >
-                {i18n.str`Register`}
-              </button>
-              {/* FIXME: should use a different color */}
-              <button
-                class="pure-button pure-button-secondary btn-cancel"
                 onClick={(e) => {
-                  e.preventDefault();
-                  setUsername(undefined);
-                  setPassword(undefined);
-                  setRepeatPassword(undefined);
-                  onComplete();
+                  e.preventDefault()
+                  doRegistrationStep()
                 }}
               >
-                {i18n.str`Cancel`}
+                <i18n.Translate>Confirm</i18n.Translate>
               </button>
             </div>
+
           </form>
+
+          <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"
+              onClick={(e) => {
+                e.preventDefault()
+                doRandomRegistration()
+              }}
+            >
+              <i18n.Translate>Create a random user</i18n.Translate>
+            </button>
+          </p>
         </div>
-      </article>
+      </div>
+
     </Fragment>
   );
 }
diff --git a/packages/demobank-ui/src/pages/WalletWithdrawForm.tsx b/packages/demobank-ui/src/pages/WalletWithdrawForm.tsx
index da624f61b..6574ec934 100644
--- a/packages/demobank-ui/src/pages/WalletWithdrawForm.tsx
+++ b/packages/demobank-ui/src/pages/WalletWithdrawForm.tsx
@@ -19,19 +19,21 @@ import {
   Amounts,
   HttpStatusCode,
   Logger,
+  TranslatedString,
   parseWithdrawUri,
 } from "@gnu-taler/taler-util";
 import {
   RequestError,
+  notify,
+  notifyError,
   useTranslationContext,
 } from "@gnu-taler/web-util/browser";
-import { Ref, VNode, h } from "preact";
+import { VNode, h } from "preact";
+import { forwardRef } from "preact/compat";
 import { useEffect, useRef, useState } from "preact/hooks";
 import { useAccessAPI } from "../hooks/access.js";
-import { notifyError } from "../hooks/notification.js";
 import { buildRequestErrorMessage, undefinedIfEmpty } from "../utils.js";
-import { ShowInputErrorLabel } from "../components/ShowInputErrorLabel.js";
-import { forwardRef } from "preact/compat";
+import { Amount } from "./PaytoWireTransferForm.js";
 
 const logger = new Logger("WalletWithdrawForm");
 const RefAmount = forwardRef(Amount);
@@ -40,10 +42,12 @@ export function WalletWithdrawForm({
   focus,
   limit,
   onSuccess,
+  onCancel,
 }: {
   limit: AmountJson;
   focus?: boolean;
   onSuccess: (operationId: string) => void;
+  onCancel: () => void;
 }): VNode {
   const { i18n } = useTranslationContext();
   const { createWithdrawal } = useAccessAPI();
@@ -71,136 +75,195 @@ export function WalletWithdrawForm({
             : undefined,
   });
 
-  return (
+  async function doStart() {
+    if (!parsedAmount) return;
+    try {
+      const result = await createWithdrawal({
+        amount: Amounts.stringify(parsedAmount),
+      });
+      const uri = parseWithdrawUri(result.data.taler_withdraw_uri);
+      if (!uri) {
+        return notifyError(
+          i18n.str`Server responded with an invalid  withdraw URI`,
+          i18n.str`Withdraw URI: ${result.data.taler_withdraw_uri}`);
+      } else {
+        onSuccess(uri.withdrawalOperationId);
+      }
+    } catch (error) {
+      if (error instanceof RequestError) {
+        notify(
+          buildRequestErrorMessage(i18n, error.cause, {
+            onClientError: (status) =>
+              status === HttpStatusCode.Forbidden
+                ? i18n.str`The operation was rejected due to insufficient funds`
+                : undefined,
+          }),
+        );
+      } else {
+        notifyError(
+          i18n.str`Operation failed, please report`,
+          (error instanceof Error
+            ? error.message
+            : JSON.stringify(error)) as TranslatedString
+        )
+}
+    }
+
+  }
+
+  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">
+    <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>Upon starting you will receive the money in your digital wallet, if you don't have one please <a class="font-semibold text-gray-500 hover:text-gray-400" href="https://taler.net/en/wallet.html">install one from here</a></i18n.Translate>.
+      </p>
+      <p class="mt-1 text-sm text-gray-500">
+        <i18n.Translate>After using your wallet you will be redirected here to confirm or cancel the operation.</i18n.Translate>
+      </p>
+    </div>
     <form
-      id="reserve-form"
-      class="pure-form"
-      name="tform"
-      onSubmit={(e) => {
-        e.preventDefault();
-      }}
+      class="bg-white shadow-sm ring-1 ring-gray-900/5 sm:rounded-xl md:col-span-2"
       autoCapitalize="none"
       autoCorrect="off"
+      onSubmit={e => {
+        e.preventDefault()
+      }}
     >
-      <p>
-        <label for="withdraw-amount">{i18n.str`Amount to withdraw:`}</label>
-        &nbsp;
-        <RefAmount
-          currency={limit.currency}
-          value={amountStr}
-          onChange={(v) => {
-            setAmountStr(v);
-          }}
-          error={errors?.amount}
-          ref={ref}
-        />
-      </p>
-      <p>
-        <div>
-          <input
-            id="select-exchange"
-            class="pure-button pure-button-primary"
-            type="submit"
-            disabled={!!errors}
-            value={i18n.str`Withdraw`}
-            onClick={async (e) => {
-              e.preventDefault();
-              if (!parsedAmount) return;
-              try {
-                const result = await createWithdrawal({
-                  amount: Amounts.stringify(parsedAmount),
-                });
-                const uri = parseWithdrawUri(result.data.taler_withdraw_uri);
-                if (!uri) {
-                  return notifyError({
-                    title: i18n.str`Server responded with an invalid  withdraw URI`,
-                    description: i18n.str`Withdraw URI: ${result.data.taler_withdraw_uri}`,
-                  });
-                } else {
-                  onSuccess(uri.withdrawalOperationId);
-                }
-              } catch (error) {
-                if (error instanceof RequestError) {
-                  notifyError(
-                    buildRequestErrorMessage(i18n, error.cause, {
-                      onClientError: (status) =>
-                        status === HttpStatusCode.Forbidden
-                          ? i18n.str`The operation was rejected due to insufficient funds`
-                          : undefined,
-                    }),
-                  );
-                } else {
-                  notifyError({
-                    title: i18n.str`Operation failed, please report`,
-                    description:
-                      error instanceof Error
-                        ? error.message
-                        : JSON.stringify(error),
-                  });
-                }
-              }
-            }}
-          />
-        </div>
-      </p>
-    </form>
-  );
-}
+      <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 for="withdraw-amount">{i18n.str`Amount`}</label>
+            <RefAmount
+              currency={limit.currency}
+              value={amountStr}
+              name="withdraw-amount"
+              onChange={(v) => {
+                setAmountStr(v);
+              }}
+              error={errors?.amount}
+              ref={ref}
+            />
+          </div>
+          <div class="sm:col-span-5">
+            <span class="isolate inline-flex rounded-md shadow-sm">
+              <button type="button"
+                class="relative               inline-flex px-3 py-2 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-3 py-2 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")
+                }}
+              >
 
-export function Amount(
-  {
-    currency,
-    value,
-    error,
-    onChange,
-  }: {
-    error?: string;
-    currency: string;
-    value: string | undefined;
-    onChange?: (s: string) => void;
-  },
-  ref: Ref<HTMLInputElement>,
-): VNode {
-  return (
-    <div style={{ width: "max-content" }}>
-      <div>
-        <input
-          type="text"
-          readonly
-          class="currency-indicator"
-          size={currency.length}
-          maxLength={currency.length}
-          tabIndex={-1}
-          style={{
-            borderTopRightRadius: 0,
-            borderBottomRightRadius: 0,
-            borderRight: 0,
-          }}
-          value={currency}
-        />
-        <input
-          type="number"
-          ref={ref}
-          name="amount"
-          id="amount"
-          placeholder="0"
-          style={{
-            borderTopLeftRadius: 0,
-            borderBottomLeftRadius: 0,
-            borderLeft: 0,
-            width: 150,
-            color: "black",
-          }}
-          value={value ?? ""}
-          disabled={!onChange}
-          onInput={(e): void => {
-            if (onChange) {
-              onChange(e.currentTarget.value);
-            }
+                25.00
+              </button>
+              <button type="button"
+                class="relative -ml-px -mr-px inline-flex px-3 py-2 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-3 py-2 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>
+
+        </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">
+        <button type="button" class="text-sm font-semibold leading-6 text-gray-900"
+          onClick={onCancel}
+        >
+          <i18n.Translate>Cancel</i18n.Translate></button>
+        <button type="submit"
+          class="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"
+          // disabled={isRawPayto ? !!errorsPayto : !!errorsWire}
+          onClick={(e) => {
+            e.preventDefault()
+            doStart()
           }}
-        />
+        >
+          <i18n.Translate>Continue</i18n.Translate>
+        </button>
       </div>
-      <ShowInputErrorLabel message={error} isDirty={value !== undefined} />
-    </div>
+
+    </form>
+  </div>
   );
 }
+
+// export function Amount(
+//   {
+//     currency,
+//     value,
+//     error,
+//     onChange,
+//   }: {
+//     error?: string;
+//     currency: string;
+//     value: string | undefined;
+//     onChange?: (s: string) => void;
+//   },
+//   ref: Ref<HTMLInputElement>,
+// ): VNode {
+//   return (
+//     <div style={{ width: "max-content" }}>
+//       <div>
+//         <input
+//           type="text"
+//           readonly
+//           class="currency-indicator"
+//           size={currency.length}
+//           maxLength={currency.length}
+//           tabIndex={-1}
+//           style={{
+//             borderTopRightRadius: 0,
+//             borderBottomRightRadius: 0,
+//             borderRight: 0,
+//           }}
+//           value={currency}
+//         />
+//         <input
+//           type="number"
+//           ref={ref}
+//           name="amount"
+//           id="amount"
+//           placeholder="0"
+//           style={{
+//             borderTopLeftRadius: 0,
+//             borderBottomLeftRadius: 0,
+//             borderLeft: 0,
+//             width: 150,
+//             color: "black",
+//           }}
+//           value={value ?? ""}
+//           disabled={!onChange}
+//           onInput={(e): void => {
+//             if (onChange) {
+//               onChange(e.currentTarget.value);
+//             }
+//           }}
+//         />
+//       </div>
+//       <ShowInputErrorLabel message={error} isDirty={value !== undefined} />
+//     </div>
+//   );
+// }
diff --git a/packages/demobank-ui/src/pages/WithdrawalConfirmationQuestion.tsx b/packages/demobank-ui/src/pages/WithdrawalConfirmationQuestion.tsx
index 2fa8e51b5..28f00169d 100644
--- a/packages/demobank-ui/src/pages/WithdrawalConfirmationQuestion.tsx
+++ b/packages/demobank-ui/src/pages/WithdrawalConfirmationQuestion.tsx
@@ -15,26 +15,40 @@
  */
 
 import {
+  AmountJson,
+  Amounts,
   HttpStatusCode,
   Logger,
+  PaytoUri,
+  PaytoUriGeneric,
+  PaytoUriIBAN,
+  PaytoUriTalerBank,
+  TranslatedString,
   WithdrawUriResult,
 } from "@gnu-taler/taler-util";
 import {
   RequestError,
+  notify,
+  notifyError,
   useTranslationContext,
 } from "@gnu-taler/web-util/browser";
 import { Fragment, VNode, h } from "preact";
 import { useMemo, useState } from "preact/hooks";
 import { useAccessAnonAPI } from "../hooks/access.js";
-import { notifyError } from "../hooks/notification.js";
 import { buildRequestErrorMessage, undefinedIfEmpty } from "../utils.js";
 import { ShowInputErrorLabel } from "../components/ShowInputErrorLabel.js";
+import { Amount } from "./PaytoWireTransferForm.js";
 
 const logger = new Logger("WithdrawalConfirmationQuestion");
 
 interface Props {
   onAborted: () => void;
   withdrawUri: WithdrawUriResult;
+  details: {
+    account: PaytoUri,
+    reserve: string,
+    amount: AmountJson,
+  }
 }
 /**
  * Additional authentication required to complete the operation.
@@ -42,6 +56,7 @@ interface Props {
  */
 export function WithdrawalConfirmationQuestion({
   onAborted,
+  details,
   withdrawUri,
 }: Props): VNode {
   const { i18n } = useTranslationContext();
@@ -60,135 +75,257 @@ export function WithdrawalConfirmationQuestion({
     answer: !captchaAnswer
       ? i18n.str`Answer the question before continue`
       : Number.isNaN(answer)
-      ? i18n.str`The answer should be a number`
-      : answer !== captchaNumbers.a + captchaNumbers.b
-      ? i18n.str`The answer "${answer}" to "${captchaNumbers.a} + ${captchaNumbers.b}" is wrong.`
-      : undefined,
+        ? i18n.str`The answer should be a number`
+        : answer !== captchaNumbers.a + captchaNumbers.b
+          ? i18n.str`The answer "${answer}" to "${captchaNumbers.a} + ${captchaNumbers.b}" is wrong.`
+          : undefined,
   });
+
+  async function doTransfer() {
+    try {
+      await confirmWithdrawal(
+        withdrawUri.withdrawalOperationId,
+      );
+    } catch (error) {
+      if (error instanceof RequestError) {
+        notify(
+          buildRequestErrorMessage(i18n, error.cause, {
+            onClientError: (status) =>
+              status === HttpStatusCode.Conflict
+                ? i18n.str`The withdrawal has been aborted previously and can't be confirmed`
+                : status === HttpStatusCode.UnprocessableEntity
+                  ? i18n.str`The withdraw operation cannot be confirmed because no exchange and reserve public key selection happened before`
+                  : undefined,
+          }),
+        );
+      } else {
+        notifyError(
+          i18n.str`Operation failed, please report`,
+          (error instanceof Error
+            ? error.message
+            : JSON.stringify(error)) as TranslatedString
+        )
+      }
+    }
+  }
+
+  async function doCancel() {
+    try {
+      await abortWithdrawal(withdrawUri.withdrawalOperationId);
+      onAborted();
+    } catch (error) {
+      if (error instanceof RequestError) {
+        notify(
+          buildRequestErrorMessage(i18n, error.cause, {
+            onClientError: (status) =>
+              status === HttpStatusCode.Conflict
+                ? i18n.str`The reserve operation has been confirmed previously and can't be aborted`
+                : undefined,
+          }),
+        );
+      } else {
+        notifyError(
+          i18n.str`Operation failed, please report`,
+          (error instanceof Error
+            ? error.message
+            : JSON.stringify(error)) as TranslatedString
+        )
+      }
+    }
+  }
+
   return (
     <Fragment>
-      <h1 class="nav">{i18n.str`Confirm Withdrawal`}</h1>
-      <article>
-        <div class="challenge-div">
-          <form
-            class="challenge-form"
-            noValidate
-            onSubmit={(e) => {
-              e.preventDefault();
-            }}
-            autoCapitalize="none"
-            autoCorrect="off"
-          >
-            <div class="pure-form" id="captcha" name="capcha-form">
-              <h2>{i18n.str`Authorize withdrawal by solving challenge`}</h2>
-              <p>
-                <label for="answer">
-                  {i18n.str`What is`}&nbsp;
-                  <em>
-                    {captchaNumbers.a}&nbsp;+&nbsp;{captchaNumbers.b}
-                  </em>
-                  ?&nbsp;
-                </label>
-                &nbsp;
-                <input
-                  name="answer"
-                  id="answer"
-                  value={captchaAnswer ?? ""}
-                  type="text"
-                  autoFocus
-                  required
-                  onInput={(e): void => {
-                    setCaptchaAnswer(e.currentTarget.value);
-                  }}
-                />
-                <ShowInputErrorLabel
-                  message={errors?.answer}
-                  isDirty={captchaAnswer !== undefined}
-                />
-              </p>
-              <p>
-                <button
-                  type="submit"
-                  class="pure-button pure-button-primary btn-confirm"
-                  disabled={!!errors}
-                  onClick={async (e) => {
-                    e.preventDefault();
-                    try {
-                      await confirmWithdrawal(
-                        withdrawUri.withdrawalOperationId,
-                      );
-                    } catch (error) {
-                      if (error instanceof RequestError) {
-                        notifyError(
-                          buildRequestErrorMessage(i18n, error.cause, {
-                            onClientError: (status) =>
-                              status === HttpStatusCode.Conflict
-                                ? i18n.str`The withdrawal has been aborted previously and can't be confirmed`
-                                : status === HttpStatusCode.UnprocessableEntity
-                                ? i18n.str`The withdraw operation cannot be confirmed because no exchange and reserve public key selection happened before`
-                                : undefined,
-                          }),
-                        );
-                      } else {
-                        notifyError({
-                          title: i18n.str`Operation failed, please report`,
-                          description:
-                            error instanceof Error
-                              ? error.message
-                              : JSON.stringify(error),
-                        });
-                      }
-                    }
-                  }}
-                >
-                  {i18n.str`Confirm`}
-                </button>
-                &nbsp;
-                <button
-                  class="pure-button pure-button-secondary btn-cancel"
-                  onClick={async (e) => {
-                    e.preventDefault();
-                    try {
-                      await abortWithdrawal(withdrawUri.withdrawalOperationId);
-                      onAborted();
-                    } catch (error) {
-                      if (error instanceof RequestError) {
-                        notifyError(
-                          buildRequestErrorMessage(i18n, error.cause, {
-                            onClientError: (status) =>
-                              status === HttpStatusCode.Conflict
-                                ? i18n.str`The reserve operation has been confirmed previously and can't be aborted`
-                                : undefined,
-                          }),
-                        );
-                      } else {
-                        notifyError({
-                          title: i18n.str`Operation failed, please report`,
-                          description:
-                            error instanceof Error
-                              ? error.message
-                              : JSON.stringify(error),
-                        });
+      <div class="bg-white shadow sm:rounded-lg">
+        <div class="px-4 py-5 sm:p-6">
+          <h3 class="text-base font-semibold leading-6 text-gray-900">
+            <i18n.Translate>Confirm the withdrawal operation</i18n.Translate>
+          </h3>
+          <div class="mt-2 max-w-xl text-sm text-gray-500">
+            <div class="px-4 mt-4 ">
+              <div class="w-full">
+                <div class="px-4 sm:px-0">
+                  <p><i18n.Translate>Wire transfer details</i18n.Translate></p>
+                </div>
+                <div class="mt-6 border-t border-gray-100">
+                  <dl class="divide-y divide-gray-100">
+                    {((): VNode => {
+                      switch (details.account.targetType) {
+                        case "iban": {
+                          const p = details.account as PaytoUriIBAN
+                          const name = p.params["receiver-name"]
+                          return <Fragment>
+                            <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">Exchange account</dt>
+                              <dd class="mt-1 text-sm leading-6 text-gray-700 sm:col-span-2 sm:mt-0">{p.iban}</dd>
+                            </div>
+                            {name &&
+                              <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">Exchange name</dt>
+                                <dd class="mt-1 text-sm leading-6 text-gray-700 sm:col-span-2 sm:mt-0">{p.params["receiver-name"]}</dd>
+                              </div>
+                            }
+                          </Fragment>
+                        }
+                        case "x-taler-bank": {
+                          const p = details.account as PaytoUriTalerBank
+                          const name = p.params["receiver-name"]
+                          return <Fragment>
+                            <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">Exchange account</dt>
+                              <dd class="mt-1 text-sm leading-6 text-gray-700 sm:col-span-2 sm:mt-0">{p.account}</dd>
+                            </div>
+                            {name &&
+                              <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">Exchange name</dt>
+                                <dd class="mt-1 text-sm leading-6 text-gray-700 sm:col-span-2 sm:mt-0">{p.params["receiver-name"]}</dd>
+                              </div>
+                            }
+                          </Fragment>
+                        }
+                        default:
+                          return <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">Exchange account</dt>
+                            <dd class="mt-1 text-sm leading-6 text-gray-700 sm:col-span-2 sm:mt-0">{details.account.targetPath}</dd>
+                          </div>
+
                       }
-                    }
-                  }}
-                >
-                  {i18n.str`Cancel`}
-                </button>
-              </p>
+                    })()}
+                    <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">{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)}</dd>
+                    </div>
+                  </dl>
+                </div>
+              </div>
+
+            </div>
+            <div class="px-4 mt-4 grid grid-cols-1 gap-y-6 sm:grid-cols-3 sm:gap-x-3">
+
+              <label class={"relative flex cursor-pointer rounded-lg border bg-white p-4 shadow-sm focus:outline-noneborder-indigo-600 ring-2 ring-indigo-600"}>
+                <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" />
+                <span class="flex flex-1">
+                  <span class="flex flex-col">
+                    <span id="project-type-0-label" class="block text-sm font-medium text-gray-900 ">
+                      <i18n.Translate>challenge response test</i18n.Translate>
+                    </span>
+                  </span>
+                </span>
+                <svg class="h-5 w-5 text-indigo-600" 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>
+              </label>
+
+
+              <label class="relative flex cursor-pointer rounded-lg border bg-gray-100  p-4 shadow-sm focus:outline-none 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" />
+                <span class="flex flex-1">
+                  <span class="flex flex-col">
+                    <span id="project-type-1-label" class="block text-sm font-medium text-gray-900">
+                      <i18n.Translate>using SMS</i18n.Translate>
+                    </span>
+                    <span id="project-type-1-description-0" class="mt-1 flex items-center text-sm text-gray-500">
+                      <i18n.Translate>not available</i18n.Translate>
+                    </span>
+                  </span>
+                </span>
+                <svg class="h-5 w-5 text-indigo-600 hidden" 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>
+              </label>
+
+              <label class="relative flex cursor-pointer rounded-lg border bg-gray-100 p-4 shadow-sm focus:outline-none 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" />
+                <span class="flex flex-1">
+                  <span class="flex flex-col">
+                    <span id="project-type-1-label" class="block text-sm font-medium text-gray-900">
+                      <i18n.Translate>one time password</i18n.Translate>
+                    </span>
+                    <span id="project-type-1-description-0" class="mt-1 flex items-center text-sm text-gray-500">
+                      <i18n.Translate>not available</i18n.Translate>
+                    </span>
+                  </span>
+                </span>
+                <svg class="h-5 w-5 text-indigo-600 hidden" 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>
+              </label>
+            </div>
+          </div>
+          <div class="mt-3 text-sm leading-6">
+
+            <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 leading-7 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"
+                autoCapitalize="none"
+                autoCorrect="off"
+                onSubmit={e => {
+                  e.preventDefault()
+                }}
+              >
+                <div class="px-4 py-6 sm:p-8">
+                  <label for="withdraw-amount">{i18n.str`What is`}&nbsp;
+                    <em>
+                      {captchaNumbers.a}&nbsp;+&nbsp;{captchaNumbers.b}
+                    </em>
+                    ?
+                  </label>
+                  <div class="mt-2">
+                    <div class="relative rounded-md shadow-sm">
+                      <input
+                        type="text"
+                        // class="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"
+                        aria-describedby="answer"
+                        autoFocus
+                        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={username ?? ""}
+                        required
+
+                        name="answer"
+                        id="answer"
+                        autocomplete="off"
+                      // value={value ?? ""}
+                      // disabled={!onChange}
+                      // onInput={(e): void => {
+                      //   if (onChange) {
+                      //     onChange(e.currentTarget.value);
+                      //   }
+                      // }}
+                      />
+                    </div>
+                    <ShowInputErrorLabel message={errors?.answer} isDirty={false} />
+                  </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">
+                  <button type="button" class="text-sm font-semibold leading-6 text-gray-900"
+                  // onClick={onCancel}
+                  >
+                    <i18n.Translate>Cancel</i18n.Translate></button>
+                  <button type="submit"
+                    class="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"
+                  // disabled={isRawPayto ? !!errorsPayto : !!errorsWire}
+                  // onClick={(e) => {
+                  //   e.preventDefault()
+                  //   doStart()
+                  // }}
+                  >
+                    <i18n.Translate>Transfer</i18n.Translate>
+                  </button>
+                </div>
+
+              </form>
             </div>
-          </form>
-          <div class="hint">
-            <p>
-              <i18n.Translate>
-                A this point, a <b>real</b> bank would ask for an additional
-                authentication proof (PIN/TAN, one time password, ..), instead
-                of a simple calculation.
-              </i18n.Translate>
-            </p>
           </div>
         </div>
-      </article>
+      </div>
+
     </Fragment>
   );
 }
diff --git a/packages/demobank-ui/src/pages/WithdrawalQRCode.tsx b/packages/demobank-ui/src/pages/WithdrawalQRCode.tsx
index 80fdac3c8..3b983c2d4 100644
--- a/packages/demobank-ui/src/pages/WithdrawalQRCode.tsx
+++ b/packages/demobank-ui/src/pages/WithdrawalQRCode.tsx
@@ -15,15 +15,16 @@
  */
 
 import {
+  Amounts,
   HttpStatusCode,
   Logger,
   WithdrawUriResult,
+  parsePaytoUri,
 } from "@gnu-taler/taler-util";
-import { ErrorType, useTranslationContext } from "@gnu-taler/web-util/browser";
+import { ErrorType, notifyInfo, useTranslationContext } from "@gnu-taler/web-util/browser";
 import { Fragment, VNode, h } from "preact";
 import { Loading } from "../components/Loading.js";
 import { useWithdrawalDetails } from "../hooks/access.js";
-import { notifyInfo } from "../hooks/notification.js";
 import { useSettings } from "../hooks/settings.js";
 import { handleNotOkResult } from "./HomePage.js";
 import { QrCodeSection } from "./QrCodeSection.js";
@@ -127,6 +128,19 @@ export function WithdrawalQRCode({
       </section>
     </section>
   }
+  if (!data.selected_reserve_pub) {
+    return <div>
+      the exchange is selcted but no reserve pub
+    </div>
+  }
+
+  const account = !data.selected_exchange_account ? undefined : parsePaytoUri(data.selected_exchange_account)
+
+  if (!account) {
+    return <div>
+      the exchange is selcted but no account 
+    </div>
+  }
 
   if (!data.selection_done) {
     return (
@@ -144,6 +158,11 @@ export function WithdrawalQRCode({
   return (
     <WithdrawalConfirmationQuestion
       withdrawUri={withdrawUri}
+      details={{
+        account,
+        reserve: data.selected_reserve_pub,
+        amount: Amounts.parseOrThrow("usd:10.00")
+      }}
       onAborted={() => {
         notifyInfo(i18n.str`Operation canceled`);
         clearCurrentWithdrawal()
diff --git a/packages/demobank-ui/src/pages/rnd.ts b/packages/demobank-ui/src/pages/rnd.ts
new file mode 100644
index 000000000..8c9bae875
--- /dev/null
+++ b/packages/demobank-ui/src/pages/rnd.ts
@@ -0,0 +1,2890 @@
+import { createEddsaKeyPair, encodeCrock, getRandomBytes } from "@gnu-taler/taler-util"
+
+
+const noun = [
+    "people",
+    "history",
+    "way",
+    "art",
+    "world",
+    "information",
+    "map",
+    "two",
+    "family",
+    "government",
+    "health",
+    "system",
+    "computer",
+    "meat",
+    "year",
+    "thanks",
+    "music",
+    "person",
+    "reading",
+    "method",
+    "data",
+    "food",
+    "understanding",
+    "theory",
+    "law",
+    "bird",
+    "literature",
+    "problem",
+    "software",
+    "control",
+    "knowledge",
+    "power",
+    "ability",
+    "economics",
+    "love",
+    "internet",
+    "television",
+    "science",
+    "library",
+    "nature",
+    "fact",
+    "product",
+    "idea",
+    "temperature",
+    "investment",
+    "area",
+    "society",
+    "activity",
+    "story",
+    "industry",
+    "media",
+    "thing",
+    "oven",
+    "community",
+    "definition",
+    "safety",
+    "quality",
+    "development",
+    "language",
+    "management",
+    "player",
+    "variety",
+    "video",
+    "week",
+    "security",
+    "country",
+    "exam",
+    "movie",
+    "organization",
+    "equipment",
+    "physics",
+    "analysis",
+    "policy",
+    "series",
+    "thought",
+    "basis",
+    "boyfriend",
+    "direction",
+    "strategy",
+    "technology",
+    "army",
+    "camera",
+    "freedom",
+    "paper",
+    "environment",
+    "child",
+    "instance",
+    "month",
+    "truth",
+    "marketing",
+    "university",
+    "writing",
+    "article",
+    "department",
+    "difference",
+    "goal",
+    "news",
+    "audience",
+    "fishing",
+    "growth",
+    "income",
+    "marriage",
+    "user",
+    "combination",
+    "failure",
+    "meaning",
+    "medicine",
+    "philosophy",
+    "teacher",
+    "communication",
+    "night",
+    "chemistry",
+    "disease",
+    "disk",
+    "energy",
+    "nation",
+    "road",
+    "role",
+    "soup",
+    "advertising",
+    "location",
+    "success",
+    "addition",
+    "apartment",
+    "education",
+    "math",
+    "moment",
+    "painting",
+    "politics",
+    "attention",
+    "decision",
+    "event",
+    "property",
+    "shopping",
+    "student",
+    "wood",
+    "competition",
+    "distribution",
+    "entertainment",
+    "office",
+    "population",
+    "president",
+    "unit",
+    "category",
+    "cigarette",
+    "context",
+    "introduction",
+    "opportunity",
+    "performance",
+    "driver",
+    "flight",
+    "length",
+    "magazine",
+    "newspaper",
+    "relationship",
+    "teaching",
+    "cell",
+    "dealer",
+    "finding",
+    "lake",
+    "member",
+    "message",
+    "phone",
+    "scene",
+    "appearance",
+    "association",
+    "concept",
+    "customer",
+    "death",
+    "discussion",
+    "housing",
+    "inflation",
+    "insurance",
+    "mood",
+    "woman",
+    "advice",
+    "blood",
+    "effort",
+    "expression",
+    "importance",
+    "opinion",
+    "payment",
+    "reality",
+    "responsibility",
+    "situation",
+    "skill",
+    "statement",
+    "wealth",
+    "application",
+    "city",
+    "county",
+    "depth",
+    "estate",
+    "foundation",
+    "grandmother",
+    "heart",
+    "perspective",
+    "photo",
+    "recipe",
+    "studio",
+    "topic",
+    "collection",
+    "depression",
+    "imagination",
+    "passion",
+    "percentage",
+    "resource",
+    "setting",
+    "ad",
+    "agency",
+    "college",
+    "connection",
+    "criticism",
+    "debt",
+    "description",
+    "memory",
+    "patience",
+    "secretary",
+    "solution",
+    "administration",
+    "aspect",
+    "attitude",
+    "director",
+    "personality",
+    "psychology",
+    "recommendation",
+    "response",
+    "selection",
+    "storage",
+    "version",
+    "alcohol",
+    "argument",
+    "complaint",
+    "contract",
+    "emphasis",
+    "highway",
+    "loss",
+    "membership",
+    "possession",
+    "preparation",
+    "steak",
+    "union",
+    "agreement",
+    "cancer",
+    "currency",
+    "employment",
+    "engineering",
+    "entry",
+    "interaction",
+    "mixture",
+    "preference",
+    "region",
+    "republic",
+    "tradition",
+    "virus",
+    "actor",
+    "classroom",
+    "delivery",
+    "device",
+    "difficulty",
+    "drama",
+    "election",
+    "engine",
+    "football",
+    "guidance",
+    "hotel",
+    "owner",
+    "priority",
+    "protection",
+    "suggestion",
+    "tension",
+    "variation",
+    "anxiety",
+    "atmosphere",
+    "awareness",
+    "bath",
+    "bread",
+    "candidate",
+    "climate",
+    "comparison",
+    "confusion",
+    "construction",
+    "elevator",
+    "emotion",
+    "employee",
+    "employer",
+    "guest",
+    "height",
+    "leadership",
+    "mall",
+    "manager",
+    "operation",
+    "recording",
+    "sample",
+    "transportation",
+    "charity",
+    "cousin",
+    "disaster",
+    "editor",
+    "efficiency",
+    "excitement",
+    "extent",
+    "feedback",
+    "guitar",
+    "homework",
+    "leader",
+    "mom",
+    "outcome",
+    "permission",
+    "presentation",
+    "promotion",
+    "reflection",
+    "refrigerator",
+    "resolution",
+    "revenue",
+    "session",
+    "singer",
+    "tennis",
+    "basket",
+    "bonus",
+    "cabinet",
+    "childhood",
+    "church",
+    "clothes",
+    "coffee",
+    "dinner",
+    "drawing",
+    "hair",
+    "hearing",
+    "initiative",
+    "judgment",
+    "lab",
+    "measurement",
+    "mode",
+    "mud",
+    "orange",
+    "poetry",
+    "police",
+    "possibility",
+    "procedure",
+    "queen",
+    "ratio",
+    "relation",
+    "restaurant",
+    "satisfaction",
+    "sector",
+    "signature",
+    "significance",
+    "song",
+    "tooth",
+    "town",
+    "vehicle",
+    "volume",
+    "wife",
+    "accident",
+    "airport",
+    "appointment",
+    "arrival",
+    "assumption",
+    "baseball",
+    "chapter",
+    "committee",
+    "conversation",
+    "database",
+    "enthusiasm",
+    "error",
+    "explanation",
+    "farmer",
+    "gate",
+    "girl",
+    "hall",
+    "historian",
+    "hospital",
+    "injury",
+    "instruction",
+    "maintenance",
+    "manufacturer",
+    "meal",
+    "perception",
+    "pie",
+    "poem",
+    "presence",
+    "proposal",
+    "reception",
+    "replacement",
+    "revolution",
+    "river",
+    "son",
+    "speech",
+    "tea",
+    "village",
+    "warning",
+    "winner",
+    "worker",
+    "writer",
+    "assistance",
+    "breath",
+    "buyer",
+    "chest",
+    "chocolate",
+    "conclusion",
+    "contribution",
+    "cookie",
+    "courage",
+    "dad",
+    "desk",
+    "drawer",
+    "establishment",
+    "examination",
+    "garbage",
+    "grocery",
+    "honey",
+    "impression",
+    "improvement",
+    "independence",
+    "insect",
+    "inspection",
+    "inspector",
+    "king",
+    "ladder",
+    "menu",
+    "penalty",
+    "piano",
+    "potato",
+    "profession",
+    "professor",
+    "quantity",
+    "reaction",
+    "requirement",
+    "salad",
+    "sister",
+    "supermarket",
+    "tongue",
+    "weakness",
+    "wedding",
+    "affair",
+    "ambition",
+    "analyst",
+    "apple",
+    "assignment",
+    "assistant",
+    "bathroom",
+    "bedroom",
+    "beer",
+    "birthday",
+    "celebration",
+    "championship",
+    "cheek",
+    "client",
+    "consequence",
+    "departure",
+    "diamond",
+    "dirt",
+    "ear",
+    "fortune",
+    "friendship",
+    "funeral",
+    "gene",
+    "girlfriend",
+    "hat",
+    "indication",
+    "intention",
+    "lady",
+    "midnight",
+    "negotiation",
+    "obligation",
+    "passenger",
+    "pizza",
+    "platform",
+    "poet",
+    "pollution",
+    "recognition",
+    "reputation",
+    "shirt",
+    "sir",
+    "speaker",
+    "stranger",
+    "surgery",
+    "sympathy",
+    "tale",
+    "throat",
+    "trainer",
+    "uncle",
+    "youth",
+    "time",
+    "work",
+    "film",
+    "water",
+    "money",
+    "example",
+    "while",
+    "business",
+    "study",
+    "game",
+    "life",
+    "form",
+    "air",
+    "day",
+    "place",
+    "number",
+    "part",
+    "field",
+    "fish",
+    "back",
+    "process",
+    "heat",
+    "hand",
+    "experience",
+    "job",
+    "book",
+    "end",
+    "point",
+    "type",
+    "home",
+    "economy",
+    "value",
+    "body",
+    "market",
+    "guide",
+    "interest",
+    "state",
+    "radio",
+    "course",
+    "company",
+    "price",
+    "size",
+    "card",
+    "list",
+    "mind",
+    "trade",
+    "line",
+    "care",
+    "group",
+    "risk",
+    "word",
+    "fat",
+    "force",
+    "key",
+    "light",
+    "training",
+    "name",
+    "school",
+    "top",
+    "amount",
+    "level",
+    "order",
+    "practice",
+    "research",
+    "sense",
+    "service",
+    "piece",
+    "web",
+    "boss",
+    "sport",
+    "fun",
+    "house",
+    "page",
+    "term",
+    "test",
+    "answer",
+    "sound",
+    "focus",
+    "matter",
+    "kind",
+    "soil",
+    "board",
+    "oil",
+    "picture",
+    "access",
+    "garden",
+    "range",
+    "rate",
+    "reason",
+    "future",
+    "site",
+    "demand",
+    "exercise",
+    "image",
+    "case",
+    "cause",
+    "coast",
+    "action",
+    "age",
+    "bad",
+    "boat",
+    "record",
+    "result",
+    "section",
+    "building",
+    "mouse",
+    "cash",
+    "class",
+    "nothing",
+    "period",
+    "plan",
+    "store",
+    "tax",
+    "side",
+    "subject",
+    "space",
+    "rule",
+    "stock",
+    "weather",
+    "chance",
+    "figure",
+    "man",
+    "model",
+    "source",
+    "beginning",
+    "earth",
+    "program",
+    "chicken",
+    "design",
+    "feature",
+    "head",
+    "material",
+    "purpose",
+    "question",
+    "rock",
+    "salt",
+    "act",
+    "birth",
+    "car",
+    "dog",
+    "object",
+    "scale",
+    "sun",
+    "note",
+    "profit",
+    "rent",
+    "speed",
+    "style",
+    "war",
+    "bank",
+    "craft",
+    "half",
+    "inside",
+    "outside",
+    "standard",
+    "bus",
+    "exchange",
+    "eye",
+    "fire",
+    "position",
+    "pressure",
+    "stress",
+    "advantage",
+    "benefit",
+    "box",
+    "frame",
+    "issue",
+    "step",
+    "cycle",
+    "face",
+    "item",
+    "metal",
+    "paint",
+    "review",
+    "room",
+    "screen",
+    "structure",
+    "view",
+    "account",
+    "ball",
+    "discipline",
+    "medium",
+    "share",
+    "balance",
+    "bit",
+    "black",
+    "bottom",
+    "choice",
+    "gift",
+    "impact",
+    "machine",
+    "shape",
+    "tool",
+    "wind",
+    "address",
+    "average",
+    "career",
+    "culture",
+    "morning",
+    "pot",
+    "sign",
+    "table",
+    "task",
+    "condition",
+    "contact",
+    "credit",
+    "egg",
+    "hope",
+    "ice",
+    "network",
+    "north",
+    "square",
+    "attempt",
+    "date",
+    "effect",
+    "link",
+    "post",
+    "star",
+    "voice",
+    "capital",
+    "challenge",
+    "friend",
+    "self",
+    "shot",
+    "brush",
+    "couple",
+    "debate",
+    "exit",
+    "front",
+    "function",
+    "lack",
+    "living",
+    "plant",
+    "plastic",
+    "spot",
+    "summer",
+    "taste",
+    "theme",
+    "track",
+    "wing",
+    "brain",
+    "button",
+    "click",
+    "desire",
+    "foot",
+    "gas",
+    "influence",
+    "notice",
+    "rain",
+    "wall",
+    "base",
+    "damage",
+    "distance",
+    "feeling",
+    "pair",
+    "savings",
+    "staff",
+    "sugar",
+    "target",
+    "text",
+    "animal",
+    "author",
+    "budget",
+    "discount",
+    "file",
+    "ground",
+    "lesson",
+    "minute",
+    "officer",
+    "phase",
+    "reference",
+    "register",
+    "sky",
+    "stage",
+    "stick",
+    "title",
+    "trouble",
+    "bowl",
+    "bridge",
+    "campaign",
+    "character",
+    "club",
+    "edge",
+    "evidence",
+    "fan",
+    "letter",
+    "lock",
+    "maximum",
+    "novel",
+    "option",
+    "pack",
+    "park",
+    "plenty",
+    "quarter",
+    "skin",
+    "sort",
+    "weight",
+    "baby",
+    "background",
+    "carry",
+    "dish",
+    "factor",
+    "fruit",
+    "glass",
+    "joint",
+    "master",
+    "muscle",
+    "red",
+    "strength",
+    "traffic",
+    "trip",
+    "vegetable",
+    "appeal",
+    "chart",
+    "gear",
+    "ideal",
+    "kitchen",
+    "land",
+    "log",
+    "mother",
+    "net",
+    "party",
+    "principle",
+    "relative",
+    "sale",
+    "season",
+    "signal",
+    "spirit",
+    "street",
+    "tree",
+    "wave",
+    "belt",
+    "bench",
+    "commission",
+    "copy",
+    "drop",
+    "minimum",
+    "path",
+    "progress",
+    "project",
+    "sea",
+    "south",
+    "status",
+    "stuff",
+    "ticket",
+    "tour",
+    "angle",
+    "blue",
+    "breakfast",
+    "confidence",
+    "daughter",
+    "degree",
+    "doctor",
+    "dot",
+    "dream",
+    "duty",
+    "essay",
+    "father",
+    "fee",
+    "finance",
+    "hour",
+    "juice",
+    "limit",
+    "luck",
+    "milk",
+    "mouth",
+    "peace",
+    "pipe",
+    "seat",
+    "stable",
+    "storm",
+    "substance",
+    "team",
+    "trick",
+    "afternoon",
+    "bat",
+    "beach",
+    "blank",
+    "catch",
+    "chain",
+    "consideration",
+    "cream",
+    "crew",
+    "detail",
+    "gold",
+    "interview",
+    "kid",
+    "mark",
+    "match",
+    "mission",
+    "pain",
+    "pleasure",
+    "score",
+    "screw",
+    "sex",
+    "shop",
+    "shower",
+    "suit",
+    "tone",
+    "window",
+    "agent",
+    "band",
+    "block",
+    "bone",
+    "calendar",
+    "cap",
+    "coat",
+    "contest",
+    "corner",
+    "court",
+    "cup",
+    "district",
+    "door",
+    "east",
+    "finger",
+    "garage",
+    "guarantee",
+    "hole",
+    "hook",
+    "implement",
+    "layer",
+    "lecture",
+    "lie",
+    "manner",
+    "meeting",
+    "nose",
+    "parking",
+    "partner",
+    "profile",
+    "respect",
+    "rice",
+    "routine",
+    "schedule",
+    "swimming",
+    "telephone",
+    "tip",
+    "winter",
+    "airline",
+    "bag",
+    "battle",
+    "bed",
+    "bill",
+    "bother",
+    "cake",
+    "code",
+    "curve",
+    "designer",
+    "dimension",
+    "dress",
+    "ease",
+    "emergency",
+    "evening",
+    "extension",
+    "farm",
+    "fight",
+    "gap",
+    "grade",
+    "holiday",
+    "horror",
+    "horse",
+    "host",
+    "husband",
+    "loan",
+    "mistake",
+    "mountain",
+    "nail",
+    "noise",
+    "occasion",
+    "package",
+    "patient",
+    "pause",
+    "phrase",
+    "proof",
+    "race",
+    "relief",
+    "sand",
+    "sentence",
+    "shoulder",
+    "smoke",
+    "stomach",
+    "string",
+    "tourist",
+    "towel",
+    "vacation",
+    "west",
+    "wheel",
+    "wine",
+    "arm",
+    "aside",
+    "associate",
+    "bet",
+    "blow",
+    "border",
+    "branch",
+    "breast",
+    "brother",
+    "buddy",
+    "bunch",
+    "chip",
+    "coach",
+    "cross",
+    "document",
+    "draft",
+    "dust",
+    "expert",
+    "floor",
+    "god",
+    "golf",
+    "habit",
+    "iron",
+    "judge",
+    "knife",
+    "landscape",
+    "league",
+    "mail",
+    "mess",
+    "native",
+    "opening",
+    "parent",
+    "pattern",
+    "pin",
+    "pool",
+    "pound",
+    "request",
+    "salary",
+    "shame",
+    "shelter",
+    "shoe",
+    "silver",
+    "tackle",
+    "tank",
+    "trust",
+    "assist",
+    "bake",
+    "bar",
+    "bell",
+    "bike",
+    "blame",
+    "boy",
+    "brick",
+    "chair",
+    "closet",
+    "clue",
+    "collar",
+    "comment",
+    "conference",
+    "devil",
+    "diet",
+    "fear",
+    "fuel",
+    "glove",
+    "jacket",
+    "lunch",
+    "monitor",
+    "mortgage",
+    "nurse",
+    "pace",
+    "panic",
+    "peak",
+    "plane",
+    "reward",
+    "row",
+    "sandwich",
+    "shock",
+    "spite",
+    "spray",
+    "surprise",
+    "till",
+    "transition",
+    "weekend",
+    "welcome",
+    "yard",
+    "alarm",
+    "bend",
+    "bicycle",
+    "bite",
+    "blind",
+    "bottle",
+    "cable",
+    "candle",
+    "clerk",
+    "cloud",
+    "concert",
+    "counter",
+    "flower",
+    "grandfather",
+    "harm",
+    "knee",
+    "lawyer",
+    "leather",
+    "load",
+    "mirror",
+    "neck",
+    "pension",
+    "plate",
+    "purple",
+    "ruin",
+    "ship",
+    "skirt",
+    "slice",
+    "snow",
+    "specialist",
+    "stroke",
+    "switch",
+    "trash",
+    "tune",
+    "zone",
+    "anger",
+    "award",
+    "bid",
+    "bitter",
+    "boot",
+    "bug",
+    "camp",
+    "candy",
+    "carpet",
+    "cat",
+    "champion",
+    "channel",
+    "clock",
+    "comfort",
+    "cow",
+    "crack",
+    "engineer",
+    "entrance",
+    "fault",
+    "grass",
+    "guy",
+    "hell",
+    "highlight",
+    "incident",
+    "island",
+    "joke",
+    "jury",
+    "leg",
+    "lip",
+    "mate",
+    "motor",
+    "nerve",
+    "passage",
+    "pen",
+    "pride",
+    "priest",
+    "prize",
+    "promise",
+    "resident",
+    "resort",
+    "ring",
+    "roof",
+    "rope",
+    "sail",
+    "scheme",
+    "script",
+    "sock",
+    "station",
+    "toe",
+    "tower",
+    "truck",
+    "witness",
+    "a",
+    "you",
+    "it",
+    "can",
+    "will",
+    "if",
+    "one",
+    "many",
+    "most",
+    "other",
+    "use",
+    "make",
+    "good",
+    "look",
+    "help",
+    "go",
+    "great",
+    "being",
+    "few",
+    "might",
+    "still",
+    "public",
+    "read",
+    "keep",
+    "start",
+    "give",
+    "human",
+    "local",
+    "general",
+    "she",
+    "specific",
+    "long",
+    "play",
+    "feel",
+    "high",
+    "tonight",
+    "put",
+    "common",
+    "set",
+    "change",
+    "simple",
+    "past",
+    "big",
+    "possible",
+    "particular",
+    "today",
+    "major",
+    "personal",
+    "current",
+    "national",
+    "cut",
+    "natural",
+    "physical",
+    "show",
+    "try",
+    "check",
+    "second",
+    "call",
+    "move",
+    "pay",
+    "let",
+    "increase",
+    "single",
+    "individual",
+    "turn",
+    "ask",
+    "buy",
+    "guard",
+    "hold",
+    "main",
+    "offer",
+    "potential",
+    "professional",
+    "international",
+    "travel",
+    "cook",
+    "alternative",
+    "following",
+    "special",
+    "working",
+    "whole",
+    "dance",
+    "excuse",
+    "cold",
+    "commercial",
+    "low",
+    "purchase",
+    "deal",
+    "primary",
+    "worth",
+    "fall",
+    "necessary",
+    "positive",
+    "produce",
+    "search",
+    "present",
+    "spend",
+    "talk",
+    "creative",
+    "tell",
+    "cost",
+    "drive",
+    "green",
+    "support",
+    "glad",
+    "remove",
+    "return",
+    "run",
+    "complex",
+    "due",
+    "effective",
+    "middle",
+    "regular",
+    "reserve",
+    "independent",
+    "leave",
+    "original",
+    "reach",
+    "rest",
+    "serve",
+    "watch",
+    "beautiful",
+    "charge",
+    "active",
+    "break",
+    "negative",
+    "safe",
+    "stay",
+    "visit",
+    "visual",
+    "affect",
+    "cover",
+    "report",
+    "rise",
+    "walk",
+    "white",
+    "beyond",
+    "junior",
+    "pick",
+    "unique",
+    "anything",
+    "classic",
+    "final",
+    "lift",
+    "mix",
+    "private",
+    "stop",
+    "teach",
+    "western",
+    "concern",
+    "familiar",
+    "fly",
+    "official",
+    "broad",
+    "comfortable",
+    "gain",
+    "maybe",
+    "rich",
+    "save",
+    "stand",
+    "young",
+    "fail",
+    "heavy",
+    "hello",
+    "lead",
+    "listen",
+    "valuable",
+    "worry",
+    "handle",
+    "leading",
+    "meet",
+    "release",
+    "sell",
+    "finish",
+    "normal",
+    "press",
+    "ride",
+    "secret",
+    "spread",
+    "spring",
+    "tough",
+    "wait",
+    "brown",
+    "deep",
+    "display",
+    "flow",
+    "hit",
+    "objective",
+    "shoot",
+    "touch",
+    "cancel",
+    "chemical",
+    "cry",
+    "dump",
+    "extreme",
+    "push",
+    "conflict",
+    "eat",
+    "fill",
+    "formal",
+    "jump",
+    "kick",
+    "opposite",
+    "pass",
+    "pitch",
+    "remote",
+    "total",
+    "treat",
+    "vast",
+    "abuse",
+    "beat",
+    "burn",
+    "deposit",
+    "print",
+    "raise",
+    "sleep",
+    "somewhere",
+    "advance",
+    "anywhere",
+    "consist",
+    "dark",
+    "double",
+    "draw",
+    "equal",
+    "fix",
+    "hire",
+    "internal",
+    "join",
+    "kill",
+    "sensitive",
+    "tap",
+    "win",
+    "attack",
+    "claim",
+    "constant",
+    "drag",
+    "drink",
+    "guess",
+    "minor",
+    "pull",
+    "raw",
+    "soft",
+    "solid",
+    "wear",
+    "weird",
+    "wonder",
+    "annual",
+    "count",
+    "dead",
+    "doubt",
+    "feed",
+    "forever",
+    "impress",
+    "nobody",
+    "repeat",
+    "round",
+    "sing",
+    "slide",
+    "strip",
+    "whereas",
+    "wish",
+    "combine",
+    "command",
+    "dig",
+    "divide",
+    "equivalent",
+    "hang",
+    "hunt",
+    "initial",
+    "march",
+    "mention",
+    "smell",
+    "spiritual",
+    "survey",
+    "tie",
+    "adult",
+    "brief",
+    "crazy",
+    "escape",
+    "gather",
+    "hate",
+    "prior",
+    "repair",
+    "rough",
+    "sad",
+    "scratch",
+    "sick",
+    "strike",
+    "employ",
+    "external",
+    "hurt",
+    "illegal",
+    "laugh",
+    "lay",
+    "mobile",
+    "nasty",
+    "ordinary",
+    "respond",
+    "royal",
+    "senior",
+    "split",
+    "strain",
+    "struggle",
+    "swim",
+    "train",
+    "upper",
+    "wash",
+    "yellow",
+    "convert",
+    "crash",
+    "dependent",
+    "fold",
+    "funny",
+    "grab",
+    "hide",
+    "miss",
+    "permit",
+    "quote",
+    "recover",
+    "resolve",
+    "roll",
+    "sink",
+    "slip",
+    "spare",
+    "suspect",
+    "sweet",
+    "swing",
+    "twist",
+    "upstairs",
+    "usual",
+    "abroad",
+    "brave",
+    "calm",
+    "concentrate",
+    "estimate",
+    "grand",
+    "male",
+    "mine",
+    "prompt",
+    "quiet",
+    "refuse",
+    "regret",
+    "reveal",
+    "rush",
+    "shake",
+    "shift",
+    "shine",
+    "steal",
+    "suck",
+    "surround",
+    "anybody",
+    "bear",
+    "brilliant",
+    "dare",
+    "dear",
+    "delay",
+    "drunk",
+    "female",
+    "hurry",
+    "inevitable",
+    "invite",
+    "kiss",
+    "neat",
+    "pop",
+    "punch",
+    "quit",
+    "reply",
+    "representative",
+    "resist",
+    "rip",
+    "rub",
+    "silly",
+    "smile",
+    "spell",
+    "stretch",
+    "stupid",
+    "tear",
+    "temporary",
+    "tomorrow",
+    "wake",
+    "wrap",
+    "yesterday"
+]
+
+const adj = [
+    "abandoned",
+    "able",
+    "absolute",
+    "adorable",
+    "adventurous",
+    "academic",
+    "acceptable",
+    "acclaimed",
+    "accomplished",
+    "accurate",
+    "aching",
+    "acidic",
+    "acrobatic",
+    "active",
+    "actual",
+    "adept",
+    "admirable",
+    "admired",
+    "adolescent",
+    "adorable",
+    "adored",
+    "advanced",
+    "afraid",
+    "affectionate",
+    "aged",
+    "aggravating",
+    "aggressive",
+    "agile",
+    "agitated",
+    "agonizing",
+    "agreeable",
+    "ajar",
+    "alarmed",
+    "alarming",
+    "alert",
+    "alienated",
+    "alive",
+    "all",
+    "altruistic",
+    "amazing",
+    "ambitious",
+    "ample",
+    "amused",
+    "amusing",
+    "anchored",
+    "ancient",
+    "angelic",
+    "angry",
+    "anguished",
+    "animated",
+    "annual",
+    "another",
+    "antique",
+    "anxious",
+    "any",
+    "apprehensive",
+    "appropriate",
+    "apt",
+    "arctic",
+    "arid",
+    "aromatic",
+    "artistic",
+    "ashamed",
+    "assured",
+    "astonishing",
+    "athletic",
+    "attached",
+    "attentive",
+    "attractive",
+    "austere",
+    "authentic",
+    "authorized",
+    "automatic",
+    "avaricious",
+    "average",
+    "aware",
+    "awesome",
+    "awful",
+    "awkward",
+    "babyish",
+    "bad",
+    "back",
+    "baggy",
+    "bare",
+    "barren",
+    "basic",
+    "beautiful",
+    "belated",
+    "beloved",
+    "beneficial",
+    "better",
+    "best",
+    "bewitched",
+    "big",
+    "big-hearted",
+    "biodegradable",
+    "bite-sized",
+    "bitter",
+    "black",
+    "black-and-white",
+    "bland",
+    "blank",
+    "blaring",
+    "bleak",
+    "blind",
+    "blissful",
+    "blond",
+    "blue",
+    "blushing",
+    "bogus",
+    "boiling",
+    "bold",
+    "bony",
+    "boring",
+    "bossy",
+    "both",
+    "bouncy",
+    "bountiful",
+    "bowed",
+    "brave",
+    "breakable",
+    "brief",
+    "bright",
+    "brilliant",
+    "brisk",
+    "broken",
+    "bronze",
+    "brown",
+    "bruised",
+    "bubbly",
+    "bulky",
+    "bumpy",
+    "buoyant",
+    "burdensome",
+    "burly",
+    "bustling",
+    "busy",
+    "buttery",
+    "buzzing",
+    "calculating",
+    "calm",
+    "candid",
+    "canine",
+    "capital",
+    "carefree",
+    "careful",
+    "careless",
+    "caring",
+    "cautious",
+    "cavernous",
+    "celebrated",
+    "charming",
+    "cheap",
+    "cheerful",
+    "cheery",
+    "chief",
+    "chilly",
+    "chubby",
+    "circular",
+    "classic",
+    "clean",
+    "clear",
+    "clear-cut",
+    "clever",
+    "close",
+    "closed",
+    "cloudy",
+    "clueless",
+    "clumsy",
+    "cluttered",
+    "coarse",
+    "cold",
+    "colorful",
+    "colorless",
+    "colossal",
+    "comfortable",
+    "common",
+    "compassionate",
+    "competent",
+    "complete",
+    "complex",
+    "complicated",
+    "composed",
+    "concerned",
+    "concrete",
+    "confused",
+    "conscious",
+    "considerate",
+    "constant",
+    "content",
+    "conventional",
+    "cooked",
+    "cool",
+    "cooperative",
+    "coordinated",
+    "corny",
+    "corrupt",
+    "costly",
+    "courageous",
+    "courteous",
+    "crafty",
+    "crazy",
+    "creamy",
+    "creative",
+    "creepy",
+    "criminal",
+    "crisp",
+    "critical",
+    "crooked",
+    "crowded",
+    "cruel",
+    "crushing",
+    "cuddly",
+    "cultivated",
+    "cultured",
+    "cumbersome",
+    "curly",
+    "curvy",
+    "cute",
+    "cylindrical",
+    "damaged",
+    "damp",
+    "dangerous",
+    "dapper",
+    "daring",
+    "darling",
+    "dark",
+    "dazzling",
+    "dead",
+    "deadly",
+    "deafening",
+    "dear",
+    "dearest",
+    "decent",
+    "decimal",
+    "decisive",
+    "deep",
+    "defenseless",
+    "defensive",
+    "defiant",
+    "deficient",
+    "definite",
+    "definitive",
+    "delayed",
+    "delectable",
+    "delicious",
+    "delightful",
+    "delirious",
+    "demanding",
+    "dense",
+    "dental",
+    "dependable",
+    "dependent",
+    "descriptive",
+    "deserted",
+    "detailed",
+    "determined",
+    "devoted",
+    "different",
+    "difficult",
+    "digital",
+    "diligent",
+    "dim",
+    "dimpled",
+    "dimwitted",
+    "direct",
+    "disastrous",
+    "discrete",
+    "disfigured",
+    "disgusting",
+    "disloyal",
+    "dismal",
+    "distant",
+    "downright",
+    "dreary",
+    "dirty",
+    "disguised",
+    "dishonest",
+    "dismal",
+    "distant",
+    "distinct",
+    "distorted",
+    "dizzy",
+    "dopey",
+    "doting",
+    "double",
+    "downright",
+    "drab",
+    "drafty",
+    "dramatic",
+    "dreary",
+    "droopy",
+    "dry",
+    "dual",
+    "dull",
+    "dutiful",
+    "each",
+    "eager",
+    "earnest",
+    "early",
+    "easy",
+    "easy-going",
+    "ecstatic",
+    "edible",
+    "educated",
+    "elaborate",
+    "elastic",
+    "elated",
+    "elderly",
+    "electric",
+    "elegant",
+    "elementary",
+    "elliptical",
+    "embarrassed",
+    "embellished",
+    "eminent",
+    "emotional",
+    "empty",
+    "enchanted",
+    "enchanting",
+    "energetic",
+    "enlightened",
+    "enormous",
+    "enraged",
+    "entire",
+    "envious",
+    "equal",
+    "equatorial",
+    "essential",
+    "esteemed",
+    "ethical",
+    "euphoric",
+    "even",
+    "evergreen",
+    "everlasting",
+    "every",
+    "evil",
+    "exalted",
+    "excellent",
+    "exemplary",
+    "exhausted",
+    "excitable",
+    "excited",
+    "exciting",
+    "exotic",
+    "expensive",
+    "experienced",
+    "expert",
+    "extraneous",
+    "extroverted",
+    "extra-large",
+    "extra-small",
+    "fabulous",
+    "failing",
+    "faint",
+    "fair",
+    "faithful",
+    "fake",
+    "false",
+    "familiar",
+    "famous",
+    "fancy",
+    "fantastic",
+    "far",
+    "faraway",
+    "far-flung",
+    "far-off",
+    "fast",
+    "fat",
+    "fatal",
+    "fatherly",
+    "favorable",
+    "favorite",
+    "fearful",
+    "fearless",
+    "feisty",
+    "feline",
+    "female",
+    "feminine",
+    "few",
+    "fickle",
+    "filthy",
+    "fine",
+    "finished",
+    "firm",
+    "first",
+    "firsthand",
+    "fitting",
+    "fixed",
+    "flaky",
+    "flamboyant",
+    "flashy",
+    "flat",
+    "flawed",
+    "flawless",
+    "flickering",
+    "flimsy",
+    "flippant",
+    "flowery",
+    "fluffy",
+    "fluid",
+    "flustered",
+    "focused",
+    "fond",
+    "foolhardy",
+    "foolish",
+    "forceful",
+    "forked",
+    "formal",
+    "forsaken",
+    "forthright",
+    "fortunate",
+    "fragrant",
+    "frail",
+    "frank",
+    "frayed",
+    "free",
+    "French",
+    "fresh",
+    "frequent",
+    "friendly",
+    "frightened",
+    "frightening",
+    "frigid",
+    "frilly",
+    "frizzy",
+    "frivolous",
+    "front",
+    "frosty",
+    "frozen",
+    "frugal",
+    "fruitful",
+    "full",
+    "fumbling",
+    "functional",
+    "funny",
+    "fussy",
+    "fuzzy",
+    "gargantuan",
+    "gaseous",
+    "general",
+    "generous",
+    "gentle",
+    "genuine",
+    "giant",
+    "giddy",
+    "gigantic",
+    "gifted",
+    "giving",
+    "glamorous",
+    "glaring",
+    "glass",
+    "gleaming",
+    "gleeful",
+    "glistening",
+    "glittering",
+    "gloomy",
+    "glorious",
+    "glossy",
+    "glum",
+    "golden",
+    "good",
+    "good-natured",
+    "gorgeous",
+    "graceful",
+    "gracious",
+    "grand",
+    "grandiose",
+    "granular",
+    "grateful",
+    "grave",
+    "gray",
+    "great",
+    "greedy",
+    "green",
+    "gregarious",
+    "grim",
+    "grimy",
+    "gripping",
+    "grizzled",
+    "gross",
+    "grotesque",
+    "grouchy",
+    "grounded",
+    "growing",
+    "growling",
+    "grown",
+    "grubby",
+    "gruesome",
+    "grumpy",
+    "guilty",
+    "gullible",
+    "gummy",
+    "hairy",
+    "half",
+    "handmade",
+    "handsome",
+    "handy",
+    "happy",
+    "happy-go-lucky",
+    "hard",
+    "hard-to-find",
+    "harmful",
+    "harmless",
+    "harmonious",
+    "harsh",
+    "hasty",
+    "hateful",
+    "haunting",
+    "healthy",
+    "heartfelt",
+    "hearty",
+    "heavenly",
+    "heavy",
+    "hefty",
+    "helpful",
+    "helpless",
+    "hidden",
+    "hideous",
+    "high",
+    "high-level",
+    "hilarious",
+    "hoarse",
+    "hollow",
+    "homely",
+    "honest",
+    "honorable",
+    "honored",
+    "hopeful",
+    "horrible",
+    "hospitable",
+    "hot",
+    "huge",
+    "humble",
+    "humiliating",
+    "humming",
+    "humongous",
+    "hungry",
+    "hurtful",
+    "husky",
+    "icky",
+    "icy",
+    "ideal",
+    "idealistic",
+    "identical",
+    "idle",
+    "idiotic",
+    "idolized",
+    "ignorant",
+    "ill",
+    "illegal",
+    "ill-fated",
+    "ill-informed",
+    "illiterate",
+    "illustrious",
+    "imaginary",
+    "imaginative",
+    "immaculate",
+    "immaterial",
+    "immediate",
+    "immense",
+    "impassioned",
+    "impeccable",
+    "impartial",
+    "imperfect",
+    "imperturbable",
+    "impish",
+    "impolite",
+    "important",
+    "impossible",
+    "impractical",
+    "impressionable",
+    "impressive",
+    "improbable",
+    "impure",
+    "inborn",
+    "incomparable",
+    "incompatible",
+    "incomplete",
+    "inconsequential",
+    "incredible",
+    "indelible",
+    "inexperienced",
+    "indolent",
+    "infamous",
+    "infantile",
+    "infatuated",
+    "inferior",
+    "infinite",
+    "informal",
+    "innocent",
+    "insecure",
+    "insidious",
+    "insignificant",
+    "insistent",
+    "instructive",
+    "insubstantial",
+    "intelligent",
+    "intent",
+    "intentional",
+    "interesting",
+    "internal",
+    "international",
+    "intrepid",
+    "ironclad",
+    "irresponsible",
+    "irritating",
+    "itchy",
+    "jaded",
+    "jagged",
+    "jam-packed",
+    "jaunty",
+    "jealous",
+    "jittery",
+    "joint",
+    "jolly",
+    "jovial",
+    "joyful",
+    "joyous",
+    "jubilant",
+    "judicious",
+    "juicy",
+    "jumbo",
+    "junior",
+    "jumpy",
+    "juvenile",
+    "kaleidoscopic",
+    "keen",
+    "key",
+    "kind",
+    "kindhearted",
+    "kindly",
+    "klutzy",
+    "knobby",
+    "knotty",
+    "knowledgeable",
+    "knowing",
+    "known",
+    "kooky",
+    "kosher",
+    "lame",
+    "lanky",
+    "large",
+    "last",
+    "lasting",
+    "late",
+    "lavish",
+    "lawful",
+    "lazy",
+    "leading",
+    "lean",
+    "leafy",
+    "left",
+    "legal",
+    "legitimate",
+    "light",
+    "lighthearted",
+    "likable",
+    "likely",
+    "limited",
+    "limp",
+    "limping",
+    "linear",
+    "lined",
+    "liquid",
+    "little",
+    "live",
+    "lively",
+    "livid",
+    "loathsome",
+    "lone",
+    "lonely",
+    "long",
+    "long-term",
+    "loose",
+    "lopsided",
+    "lost",
+    "loud",
+    "lovable",
+    "lovely",
+    "loving",
+    "low",
+    "loyal",
+    "lucky",
+    "lumbering",
+    "luminous",
+    "lumpy",
+    "lustrous",
+    "luxurious",
+    "mad",
+    "made-up",
+    "magnificent",
+    "majestic",
+    "major",
+    "male",
+    "mammoth",
+    "married",
+    "marvelous",
+    "masculine",
+    "massive",
+    "mature",
+    "meager",
+    "mealy",
+    "mean",
+    "measly",
+    "meaty",
+    "medical",
+    "mediocre",
+    "medium",
+    "meek",
+    "mellow",
+    "melodic",
+    "memorable",
+    "menacing",
+    "merry",
+    "messy",
+    "metallic",
+    "mild",
+    "milky",
+    "mindless",
+    "miniature",
+    "minor",
+    "minty",
+    "miserable",
+    "miserly",
+    "misguided",
+    "misty",
+    "mixed",
+    "modern",
+    "modest",
+    "moist",
+    "monstrous",
+    "monthly",
+    "monumental",
+    "moral",
+    "mortified",
+    "motherly",
+    "motionless",
+    "mountainous",
+    "muddy",
+    "muffled",
+    "multicolored",
+    "mundane",
+    "murky",
+    "mushy",
+    "musty",
+    "muted",
+    "mysterious",
+    "naive",
+    "narrow",
+    "nasty",
+    "natural",
+    "naughty",
+    "nautical",
+    "near",
+    "neat",
+    "necessary",
+    "needy",
+    "negative",
+    "neglected",
+    "negligible",
+    "neighboring",
+    "nervous",
+    "new",
+    "next",
+    "nice",
+    "nifty",
+    "nimble",
+    "nippy",
+    "nocturnal",
+    "noisy",
+    "nonstop",
+    "normal",
+    "notable",
+    "noted",
+    "noteworthy",
+    "novel",
+    "noxious",
+    "numb",
+    "nutritious",
+    "nutty",
+    "obedient",
+    "obese",
+    "oblong",
+    "oily",
+    "oblong",
+    "obvious",
+    "occasional",
+    "odd",
+    "oddball",
+    "offbeat",
+    "offensive",
+    "official",
+    "old",
+    "old-fashioned",
+    "only",
+    "open",
+    "optimal",
+    "optimistic",
+    "opulent",
+    "orange",
+    "orderly",
+    "organic",
+    "ornate",
+    "ornery",
+    "ordinary",
+    "original",
+    "other",
+    "our",
+    "outlying",
+    "outgoing",
+    "outlandish",
+    "outrageous",
+    "outstanding",
+    "oval",
+    "overcooked",
+    "overdue",
+    "overjoyed",
+    "overlooked",
+    "palatable",
+    "pale",
+    "paltry",
+    "parallel",
+    "parched",
+    "partial",
+    "passionate",
+    "past",
+    "pastel",
+    "peaceful",
+    "peppery",
+    "perfect",
+    "perfumed",
+    "periodic",
+    "perky",
+    "personal",
+    "pertinent",
+    "pesky",
+    "pessimistic",
+    "petty",
+    "phony",
+    "physical",
+    "piercing",
+    "pink",
+    "pitiful",
+    "plain",
+    "plaintive",
+    "plastic",
+    "playful",
+    "pleasant",
+    "pleased",
+    "pleasing",
+    "plump",
+    "plush",
+    "polished",
+    "polite",
+    "political",
+    "pointed",
+    "pointless",
+    "poised",
+    "poor",
+    "popular",
+    "portly",
+    "posh",
+    "positive",
+    "possible",
+    "potable",
+    "powerful",
+    "powerless",
+    "practical",
+    "precious",
+    "present",
+    "prestigious",
+    "pretty",
+    "precious",
+    "previous",
+    "pricey",
+    "prickly",
+    "primary",
+    "prime",
+    "pristine",
+    "private",
+    "prize",
+    "probable",
+    "productive",
+    "profitable",
+    "profuse",
+    "proper",
+    "proud",
+    "prudent",
+    "punctual",
+    "pungent",
+    "puny",
+    "pure",
+    "purple",
+    "pushy",
+    "putrid",
+    "puzzled",
+    "puzzling",
+    "quaint",
+    "qualified",
+    "quarrelsome",
+    "quarterly",
+    "queasy",
+    "querulous",
+    "questionable",
+    "quick",
+    "quick-witted",
+    "quiet",
+    "quintessential",
+    "quirky",
+    "quixotic",
+    "quizzical",
+    "radiant",
+    "ragged",
+    "rapid",
+    "rare",
+    "rash",
+    "raw",
+    "recent",
+    "reckless",
+    "rectangular",
+    "ready",
+    "real",
+    "realistic",
+    "reasonable",
+    "red",
+    "reflecting",
+    "regal",
+    "regular",
+    "reliable",
+    "relieved",
+    "remarkable",
+    "remorseful",
+    "remote",
+    "repentant",
+    "required",
+    "respectful",
+    "responsible",
+    "repulsive",
+    "revolving",
+    "rewarding",
+    "rich",
+    "rigid",
+    "right",
+    "ringed",
+    "ripe",
+    "roasted",
+    "robust",
+    "rosy",
+    "rotating",
+    "rotten",
+    "rough",
+    "round",
+    "rowdy",
+    "royal",
+    "rubbery",
+    "rundown",
+    "ruddy",
+    "rude",
+    "runny",
+    "rural",
+    "rusty",
+    "sad",
+    "safe",
+    "salty",
+    "same",
+    "sandy",
+    "sane",
+    "sarcastic",
+    "sardonic",
+    "satisfied",
+    "scaly",
+    "scarce",
+    "scared",
+    "scary",
+    "scented",
+    "scholarly",
+    "scientific",
+    "scornful",
+    "scratchy",
+    "scrawny",
+    "second",
+    "secondary",
+    "second-hand",
+    "secret",
+    "self-assured",
+    "self-reliant",
+    "selfish",
+    "sentimental",
+    "separate",
+    "serene",
+    "serious",
+    "serpentine",
+    "several",
+    "severe",
+    "shabby",
+    "shadowy",
+    "shady",
+    "shallow",
+    "shameful",
+    "shameless",
+    "sharp",
+    "shimmering",
+    "shiny",
+    "shocked",
+    "shocking",
+    "shoddy",
+    "short",
+    "short-term",
+    "showy",
+    "shrill",
+    "shy",
+    "sick",
+    "silent",
+    "silky",
+    "silly",
+    "silver",
+    "similar",
+    "simple",
+    "simplistic",
+    "sinful",
+    "single",
+    "sizzling",
+    "skeletal",
+    "skinny",
+    "sleepy",
+    "slight",
+    "slim",
+    "slimy",
+    "slippery",
+    "slow",
+    "slushy",
+    "small",
+    "smart",
+    "smoggy",
+    "smooth",
+    "smug",
+    "snappy",
+    "snarling",
+    "sneaky",
+    "sniveling",
+    "snoopy",
+    "sociable",
+    "soft",
+    "soggy",
+    "solid",
+    "somber",
+    "some",
+    "spherical",
+    "sophisticated",
+    "sore",
+    "sorrowful",
+    "soulful",
+    "soupy",
+    "sour",
+    "Spanish",
+    "sparkling",
+    "sparse",
+    "specific",
+    "spectacular",
+    "speedy",
+    "spicy",
+    "spiffy",
+    "spirited",
+    "spiteful",
+    "splendid",
+    "spotless",
+    "spotted",
+    "spry",
+    "square",
+    "squeaky",
+    "squiggly",
+    "stable",
+    "staid",
+    "stained",
+    "stale",
+    "standard",
+    "starchy",
+    "stark",
+    "starry",
+    "steep",
+    "sticky",
+    "stiff",
+    "stimulating",
+    "stingy",
+    "stormy",
+    "straight",
+    "strange",
+    "steel",
+    "strict",
+    "strident",
+    "striking",
+    "striped",
+    "strong",
+    "studious",
+    "stunning",
+    "stupendous",
+    "stupid",
+    "sturdy",
+    "stylish",
+    "subdued",
+    "submissive",
+    "substantial",
+    "subtle",
+    "suburban",
+    "sudden",
+    "sugary",
+    "sunny",
+    "super",
+    "superb",
+    "superficial",
+    "superior",
+    "supportive",
+    "sure-footed",
+    "surprised",
+    "suspicious",
+    "svelte",
+    "sweaty",
+    "sweet",
+    "sweltering",
+    "swift",
+    "sympathetic",
+    "tall",
+    "talkative",
+    "tame",
+    "tan",
+    "tangible",
+    "tart",
+    "tasty",
+    "tattered",
+    "taut",
+    "tedious",
+    "teeming",
+    "tempting",
+    "tender",
+    "tense",
+    "tepid",
+    "terrible",
+    "terrific",
+    "testy",
+    "thankful",
+    "that",
+    "these",
+    "thick",
+    "thin",
+    "third",
+    "thirsty",
+    "this",
+    "thorough",
+    "thorny",
+    "those",
+    "thoughtful",
+    "threadbare",
+    "thrifty",
+    "thunderous",
+    "tidy",
+    "tight",
+    "timely",
+    "tinted",
+    "tiny",
+    "tired",
+    "torn",
+    "total",
+    "tough",
+    "traumatic",
+    "treasured",
+    "tremendous",
+    "tragic",
+    "trained",
+    "tremendous",
+    "triangular",
+    "tricky",
+    "trifling",
+    "trim",
+    "trivial",
+    "troubled",
+    "true",
+    "trusting",
+    "trustworthy",
+    "trusty",
+    "truthful",
+    "tubby",
+    "turbulent",
+    "twin",
+    "ugly",
+    "ultimate",
+    "unacceptable",
+    "unaware",
+    "uncomfortable",
+    "uncommon",
+    "unconscious",
+    "understated",
+    "unequaled",
+    "uneven",
+    "unfinished",
+    "unfit",
+    "unfolded",
+    "unfortunate",
+    "unhappy",
+    "unhealthy",
+    "uniform",
+    "unimportant",
+    "unique",
+    "united",
+    "unkempt",
+    "unknown",
+    "unlawful",
+    "unlined",
+    "unlucky",
+    "unnatural",
+    "unpleasant",
+    "unrealistic",
+    "unripe",
+    "unruly",
+    "unselfish",
+    "unsightly",
+    "unsteady",
+    "unsung",
+    "untidy",
+    "untimely",
+    "untried",
+    "untrue",
+    "unused",
+    "unusual",
+    "unwelcome",
+    "unwieldy",
+    "unwilling",
+    "unwitting",
+    "unwritten",
+    "upbeat",
+    "upright",
+    "upset",
+    "urban",
+    "usable",
+    "used",
+    "useful",
+    "useless",
+    "utilized",
+    "utter",
+    "vacant",
+    "vague",
+    "vain",
+    "valid",
+    "valuable",
+    "vapid",
+    "variable",
+    "vast",
+    "velvety",
+    "venerated",
+    "vengeful",
+    "verifiable",
+    "vibrant",
+    "vicious",
+    "victorious",
+    "vigilant",
+    "vigorous",
+    "villainous",
+    "violet",
+    "violent",
+    "virtual",
+    "virtuous",
+    "visible",
+    "vital",
+    "vivacious",
+    "vivid",
+    "voluminous",
+    "wan",
+    "warlike",
+    "warm",
+    "warmhearted",
+    "warped",
+    "wary",
+    "wasteful",
+    "watchful",
+    "waterlogged",
+    "watery",
+    "wavy",
+    "wealthy",
+    "weak",
+    "weary",
+    "webbed",
+    "wee",
+    "weekly",
+    "weepy",
+    "weighty",
+    "weird",
+    "welcome",
+    "well-documented",
+    "well-groomed",
+    "well-informed",
+    "well-lit",
+    "well-made",
+    "well-off",
+    "well-to-do",
+    "well-worn",
+    "wet",
+    "which",
+    "whimsical",
+    "whirlwind",
+    "whispered",
+    "white",
+    "whole",
+    "whopping",
+    "wicked",
+    "wide",
+    "wide-eyed",
+    "wiggly",
+    "wild",
+    "willing",
+    "wilted",
+    "winding",
+    "windy",
+    "winged",
+    "wiry",
+    "wise",
+    "witty",
+    "wobbly",
+    "woeful",
+    "wonderful",
+    "wooden",
+    "woozy",
+    "wordy",
+    "worldly",
+    "worn",
+    "worried",
+    "worrisome",
+    "worse",
+    "worst",
+    "worthless",
+    "worthwhile",
+    "worthy",
+    "wrathful",
+    "wretched",
+    "writhing",
+    "wrong",
+    "wry",
+    "yawning",
+    "yearly",
+    "yellow",
+    "yellowish",
+    "young",
+    "youthful",
+    "yummy",
+    "zany",
+    "zealous",
+    "zesty",
+    "zigzag",
+]
+
+export function getRandomUsername(): string {
+    const n = Math.random() * noun.length
+    const a = Math.random() * adj.length
+    return `tmp-${a}-${n}`
+}
+
+export function getRandomPassword(): string {
+    return encodeCrock(getRandomBytes(16))
+}
\ No newline at end of file
diff --git a/packages/demobank-ui/src/utils.ts b/packages/demobank-ui/src/utils.ts
index 4ce0f140e..c13b9a3cb 100644
--- a/packages/demobank-ui/src/utils.ts
+++ b/packages/demobank-ui/src/utils.ts
@@ -16,11 +16,12 @@
 
 import { HttpStatusCode, TranslatedString } from "@gnu-taler/taler-util";
 import {
+  ErrorNotification,
   ErrorType,
   HttpError,
   useTranslationContext,
 } from "@gnu-taler/web-util/browser";
-import { ErrorMessage } from "./hooks/notification.js";
+
 
 /**
  * Validate (the number part of) an amount.  If needed,
@@ -120,11 +121,12 @@ export function buildRequestErrorMessage(
     onClientError?: (status: HttpStatusCode) => TranslatedString | undefined;
     onServerError?: (status: HttpStatusCode) => TranslatedString | undefined;
   } = {},
-): ErrorMessage {
-  let result: ErrorMessage;
+): ErrorNotification {
+  let result: ErrorNotification;
   switch (cause.type) {
     case ErrorType.TIMEOUT: {
       result = {
+        type: "error",
         title: i18n.str`Request timeout`,
       };
       break;
@@ -133,8 +135,9 @@ export function buildRequestErrorMessage(
       const title =
         specialCases.onClientError && specialCases.onClientError(cause.status);
       result = {
+        type: "error",
         title: title ? title : i18n.str`The server didn't accept the request`,
-        description: cause?.payload?.error?.description,
+        description: cause?.payload?.error?.description as TranslatedString,
         debug: JSON.stringify(cause),
       };
       break;
@@ -143,24 +146,27 @@ export function buildRequestErrorMessage(
       const title =
         specialCases.onServerError && specialCases.onServerError(cause.status);
       result = {
+        type: "error",
         title: title
           ? title
           : i18n.str`The server had problems processing the request`,
-        description: cause?.payload?.error?.description,
+        description: cause?.payload?.error?.description as TranslatedString,
         debug: JSON.stringify(cause),
       };
       break;
     }
     case ErrorType.UNREADABLE: {
       result = {
+        type: "error",
         title: i18n.str`Unexpected error`,
-        description: `Response from ${cause?.info?.url} is unreadable, status: ${cause?.status}`,
+        description: `Response from ${cause?.info?.url} is unreadable, status: ${cause?.status}` as TranslatedString,
         debug: JSON.stringify(cause),
       };
       break;
     }
     case ErrorType.UNEXPECTED: {
       result = {
+        type: "error",
         title: i18n.str`Unexpected error`,
         debug: JSON.stringify(cause),
       };
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 295074f61..392f34981 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -1,4 +1,4 @@
-lockfileVersion: '6.1'
+lockfileVersion: '6.0'
 
 settings:
   autoInstallPeers: true
@@ -877,6 +877,9 @@ importers:
       '@gnu-taler/taler-util':
         specifier: workspace:*
         version: link:../taler-util
+      '@heroicons/react':
+        specifier: ^2.0.17
+        version: 2.0.17(react@18.2.0)
       '@linaria/babel-preset':
         specifier: 4.4.5
         version: 4.4.5
@@ -907,6 +910,9 @@ importers:
       chokidar:
         specifier: ^3.5.3
         version: 3.5.3
+      date-fns:
+        specifier: 2.29.3
+        version: 2.29.3
       esbuild:
         specifier: ^0.17.7
         version: 0.17.7
@@ -4872,7 +4878,6 @@ packages:
       react: '>= 16'
     dependencies:
       react: 18.2.0
-    dev: false
 
   /@humanwhocodes/config-array@0.11.11:
     resolution: {integrity: sha512-N2brEuAadi0CcdeMXUkhbZB84eskAc8MEX1By6qEchoVywSgXPIjou4rYsl0V3Hj0ZnuGycGCjdNgockbzeWNA==}
@@ -8680,7 +8685,6 @@ packages:
   /date-fns@2.29.3:
     resolution: {integrity: sha512-dDCnyH2WnnKusqvZZ6+jA1O51Ibt8ZMRNkDZdyAyK4YfbDwa/cEmuztzG5pk6hqlp9aSBPYcjOlktquahGwGeA==}
     engines: {node: '>=0.11'}
-    dev: false
 
   /date-time@3.1.0:
     resolution: {integrity: sha512-uqCUKXE5q1PNBXjPqvwhwJf9SwMoAHBgWJ6DcrnS5o+W2JOiIILl0JEdVD8SGujrNS02GGxgwAg2PN2zONgtjg==}
-- 
cgit v1.2.3


From 4faa037c20ca4c282d22d8e93bfa2b308b595d2a Mon Sep 17 00:00:00 2001
From: Sebastian <sebasjm@gmail.com>
Date: Thu, 21 Sep 2023 14:25:12 -0300
Subject: tx group by date

---
 .../src/components/Transactions/views.tsx          | 59 ++++++++++++++--------
 packages/demobank-ui/src/pages/PaymentOptions.tsx  |  2 +
 packages/demobank-ui/src/pages/admin/Home.tsx      |  5 +-
 3 files changed, 42 insertions(+), 24 deletions(-)

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

diff --git a/packages/demobank-ui/src/components/Transactions/views.tsx b/packages/demobank-ui/src/components/Transactions/views.tsx
index 6303037a1..b11888320 100644
--- a/packages/demobank-ui/src/components/Transactions/views.tsx
+++ b/packages/demobank-ui/src/components/Transactions/views.tsx
@@ -14,10 +14,10 @@
  GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 
-import { h, VNode } from "preact";
+import { Fragment, h, VNode } from "preact";
 import { useTranslationContext } from "@gnu-taler/web-util/browser";
 import { State } from "./index.js";
-import { format } from "date-fns";
+import { format, isToday } from "date-fns";
 import { Amounts } from "@gnu-taler/taler-util";
 
 export function LoadingUriView({ error }: State.LoadingUriError): VNode {
@@ -32,6 +32,16 @@ export function LoadingUriView({ error }: State.LoadingUriError): VNode {
 
 export function ReadyView({ transactions }: State.Ready): VNode {
   const { i18n } = useTranslationContext();
+  const txByDate = transactions.reduce((prev, cur) => {
+    const d = cur.when.t_ms === "never"
+      ? ""
+      : format(cur.when.t_ms, "dd/MM/yyyy")
+    if (!prev[d]) {
+      prev[d] = []
+    }
+    prev[d].push(cur)
+    return prev
+  }, {} as Record<string, typeof transactions>)
   return (
     <div class="px-4 mt-4">
       <div class="sm:flex sm:items-center">
@@ -50,25 +60,34 @@ export function ReadyView({ transactions }: State.Ready): VNode {
             </tr>
           </thead>
           <tbody>
-            {transactions.map((item, idx) => {
-              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, "dd/MM/yyyy HH:mm:ss")}</div>
-                  </td>
-                  <td class="px-3 py-3.5 text-sm text-gray-500">{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>
-                  <td class="px-3 py-3.5 text-sm text-gray-500 break-all min-w-md">{item.subject}</td>
+            {Object.entries(txByDate).map(([date, txs], idx) => {
+              return <Fragment>
+                <tr class="border-t border-gray-200">
+                  <th colSpan={4} scope="colgroup" class="bg-gray-50 py-2 pl-4 pr-3 text-left text-sm font-semibold text-gray-900 sm:pl-3">
+                    {date}
+                  </th>
                 </tr>
-              );
+                {txs.map(item => {
+                  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>
+                    </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>
+                    <td class="px-3 py-3.5 text-sm text-gray-500 break-all min-w-md">{item.subject}</td>
+                  </tr>)
+                })}
+              </Fragment>
             })}
           </tbody>
         </table>
diff --git a/packages/demobank-ui/src/pages/PaymentOptions.tsx b/packages/demobank-ui/src/pages/PaymentOptions.tsx
index 5cb09a5d4..1728074a3 100644
--- a/packages/demobank-ui/src/pages/PaymentOptions.tsx
+++ b/packages/demobank-ui/src/pages/PaymentOptions.tsx
@@ -46,6 +46,7 @@ export function PaymentOptions({ limit }: { limit: AmountJson }): VNode {
             setTab("charge-wallet")
           }} />
           <span class="flex flex-1">
+            <div class="text-lg mr-2">&#x1F4B5;</div>
             <span class="flex flex-col">
               <span id="project-type-0-label" class="block text-sm font-medium text-gray-900">
                 <i18n.Translate>a <b>Taler</b> wallet</i18n.Translate>
@@ -66,6 +67,7 @@ export function PaymentOptions({ limit }: { limit: AmountJson }): VNode {
             setTab("wire-transfer")
           }} />
           <span class="flex flex-1">
+          <div class="text-lg mr-2">&#x2194;</div>
             <span class="flex flex-col">
               <span id="project-type-1-label" class="block text-sm font-medium text-gray-900">
                 <i18n.Translate>another bank account</i18n.Translate>
diff --git a/packages/demobank-ui/src/pages/admin/Home.tsx b/packages/demobank-ui/src/pages/admin/Home.tsx
index f7d4e426e..625a49d45 100644
--- a/packages/demobank-ui/src/pages/admin/Home.tsx
+++ b/packages/demobank-ui/src/pages/admin/Home.tsx
@@ -27,10 +27,7 @@ export function AdminHome({ onRegister }: Props): VNode {
   const [action, setAction] = useState<{
     type: AccountAction,
     account: string
-  } | undefined>({
-    type:"remove-account",
-    account:"gnunet-at-sandbox"
-  })
+  } | undefined>()
 
   const [createAccount, setCreateAccount] = useState(false);
 
-- 
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/Transactions/views.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 4041a76a58503572c6fe8edc87658afc946a11e0 Mon Sep 17 00:00:00 2001
From: Sebastian <sebasjm@gmail.com>
Date: Mon, 25 Sep 2023 11:58:17 -0300
Subject: more ui: pagination

---
 packages/demobank-ui/src/components/Routing.tsx    |  3 +-
 .../src/components/Transactions/index.ts           |  2 +
 .../src/components/Transactions/state.ts           | 52 ++++++----------------
 .../src/components/Transactions/views.tsx          | 25 ++++++++++-
 packages/demobank-ui/src/declaration.d.ts          | 41 ++++++++---------
 packages/demobank-ui/src/hooks/access.ts           | 23 ++++++----
 packages/demobank-ui/src/hooks/backend.ts          | 18 +++++---
 .../demobank-ui/src/pages/AccountPage/state.ts     |  2 +-
 packages/demobank-ui/src/pages/BankFrame.tsx       | 17 +++++--
 packages/demobank-ui/src/pages/HomePage.tsx        |  5 +--
 packages/demobank-ui/src/pages/LoginForm.tsx       | 41 ++++++++++++++---
 .../src/pages/WithdrawalConfirmationQuestion.tsx   |  5 ++-
 packages/demobank-ui/src/pages/admin/Account.tsx   |  2 +-
 .../demobank-ui/src/pages/admin/AccountList.tsx    |  5 +--
 packages/demobank-ui/src/pages/admin/Home.tsx      |  8 ++--
 packages/demobank-ui/src/pages/business/Home.tsx   |  8 ++--
 16 files changed, 151 insertions(+), 106 deletions(-)

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

diff --git a/packages/demobank-ui/src/components/Routing.tsx b/packages/demobank-ui/src/components/Routing.tsx
index 2710069c2..05af1719b 100644
--- a/packages/demobank-ui/src/components/Routing.tsx
+++ b/packages/demobank-ui/src/components/Routing.tsx
@@ -17,7 +17,7 @@
 import { createHashHistory } from "history";
 import { VNode, h } from "preact";
 import { Route, Router, route } from "preact-router";
-import { useEffect } from "preact/hooks";
+import { useEffect, useErrorBoundary } from "preact/hooks";
 import { BankFrame } from "../pages/BankFrame.js";
 import { BusinessAccount } from "../pages/business/Home.js";
 import { HomePage, WithdrawalOperationPage } from "../pages/HomePage.js";
@@ -27,6 +27,7 @@ import { useBackendContext } from "../context/backend.js";
 import { LoginForm } from "../pages/LoginForm.js";
 import { AdminHome } from "../pages/admin/Home.js";
 import { bankUiSettings } from "../settings.js";
+import { notifyError } from "@gnu-taler/web-util/browser";
 
 export function Routing(): VNode {
   const history = createHashHistory();
diff --git a/packages/demobank-ui/src/components/Transactions/index.ts b/packages/demobank-ui/src/components/Transactions/index.ts
index 46b38ce74..9df1a70e5 100644
--- a/packages/demobank-ui/src/components/Transactions/index.ts
+++ b/packages/demobank-ui/src/components/Transactions/index.ts
@@ -46,6 +46,8 @@ export namespace State {
     status: "ready";
     error: undefined;
     transactions: Transaction[];
+    onPrev?: () => void;
+    onNext?: () => void;
   }
 }
 
diff --git a/packages/demobank-ui/src/components/Transactions/state.ts b/packages/demobank-ui/src/components/Transactions/state.ts
index d2a512b08..30c48aa45 100644
--- a/packages/demobank-ui/src/components/Transactions/state.ts
+++ b/packages/demobank-ui/src/components/Transactions/state.ts
@@ -14,7 +14,7 @@
  GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 
-import { AbsoluteTime, Amounts } from "@gnu-taler/taler-util";
+import { AbsoluteTime, Amounts, parsePaytoUri } from "@gnu-taler/taler-util";
 import { useTransactions } from "../../hooks/access.js";
 import { Props, State, Transaction } from "./index.js";
 
@@ -34,45 +34,19 @@ export function useComponentState({ account }: Props): State {
   }
 
   const transactions = result.data.transactions
-    .map((item: unknown) => {
-      if (
-        !item ||
-        typeof item !== "object" ||
-        !("direction" in item) ||
-        !("creditorIban" in item) ||
-        !("debtorIban" in item) ||
-        !("date" in item) ||
-        !("subject" in item) ||
-        !("currency" in item) ||
-        !("amount" in item)
-      ) {
-        //not valid
-        return;
-      }
-      const anyItem = item as any;
-      if (
-        !(typeof anyItem.creditorIban === "string") ||
-        !(typeof anyItem.debtorIban === "string") ||
-        !(typeof anyItem.date === "string") ||
-        !(typeof anyItem.subject === "string") ||
-        !(typeof anyItem.currency === "string") ||
-        !(typeof anyItem.amount === "string")
-      ) {
-        return;
-      }
+    .map((tx) => {
 
-      const negative = anyItem.direction === "DEBIT";
-      const counterpart = negative ? anyItem.creditorIban : anyItem.debtorIban;
+      const negative = tx.direction === "debit";
+      const cp = parsePaytoUri(negative ? tx.creditor_payto_uri : tx.debtor_payto_uri);
+      const counterpart = (cp === undefined || !cp.isKnown ? undefined :
+        cp.targetType === "iban" ? cp.iban : 
+        cp.targetType === "x-taler-bank" ? cp.account :
+        cp.targetType === "bitcoin" ? `${cp.targetPath.substring(0, 6)}...` : undefined) ??
+        "unkown";
 
-      let date = anyItem.date ? parseInt(anyItem.date, 10) : 0;
-      if (isNaN(date) || !isFinite(date)) {
-        date = 0;
-      }
-      const when: AbsoluteTime = !date
-        ? AbsoluteTime.never()
-        : AbsoluteTime.fromMilliseconds(date);
-      const amount = Amounts.parse(`${anyItem.currency}:${anyItem.amount}`);
-      const subject = anyItem.subject;
+      const when = AbsoluteTime.fromMilliseconds(tx.date / 1000);
+      const amount = Amounts.parse(tx.amount);
+      const subject = tx.subject;
       return {
         negative,
         counterpart,
@@ -87,5 +61,7 @@ export function useComponentState({ account }: Props): State {
     status: "ready",
     error: undefined,
     transactions,
+    onNext: result.isReachingEnd ? undefined : result.loadMore,
+    onPrev: result.isReachingStart ? undefined : result.loadMorePrev,
   };
 }
diff --git a/packages/demobank-ui/src/components/Transactions/views.tsx b/packages/demobank-ui/src/components/Transactions/views.tsx
index e34120e34..f8b2e3113 100644
--- a/packages/demobank-ui/src/components/Transactions/views.tsx
+++ b/packages/demobank-ui/src/components/Transactions/views.tsx
@@ -30,7 +30,7 @@ export function LoadingUriView({ error }: State.LoadingUriError): VNode {
   );
 }
 
-export function ReadyView({ transactions }: State.Ready): VNode {
+export function ReadyView({ transactions, onNext, onPrev }: State.Ready): VNode {
   const { i18n } = useTranslationContext();
   if (!transactions.length) return <div />
   const txByDate = transactions.reduce((prev, cur) => {
@@ -50,7 +50,7 @@ export function ReadyView({ transactions }: State.Ready): VNode {
           <h1 class="text-base font-semibold leading-6 text-gray-900"><i18n.Translate>Latest transactions</i18n.Translate></h1>
         </div>
       </div>
-      <div class="-mx-4 mt-5 ring-1 ring-gray-300 sm:mx-0 sm:rounded-lg min-w-fit bg-white">
+      <div class="-mx-4 mt-5 ring-1 ring-gray-300 sm:mx-0 rounded-lg min-w-fit bg-white">
         <table class="min-w-full divide-y divide-gray-300">
           <thead>
             <tr>
@@ -89,9 +89,30 @@ export function ReadyView({ transactions }: State.Ready): VNode {
                   </tr>)
                 })}
               </Fragment>
+
             })}
           </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">
+          <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"
+              disabled={!onPrev}
+              onClick={onPrev}
+            >
+              <i18n.Translate>First page</i18n.Translate>
+            </button>
+            <button
+              class="relative disabled:bg-gray-100 disabled:text-gray-500 ml-3 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"
+              disabled={!onNext}
+              onClick={onNext}
+            >
+              <i18n.Translate>Next</i18n.Translate>
+            </button>
+          </div>
+        </nav>
       </div>
     </div>
   );
diff --git a/packages/demobank-ui/src/declaration.d.ts b/packages/demobank-ui/src/declaration.d.ts
index 0c083a939..8d729c1f7 100644
--- a/packages/demobank-ui/src/declaration.d.ts
+++ b/packages/demobank-ui/src/declaration.d.ts
@@ -194,28 +194,25 @@ namespace SandboxBackend {
     }
 
     interface BankAccountTransactionInfo {
-      creditorIban: string;
-      creditorBic: string; // Optional
-      creditorName: string;
+      creditor_payto_uri: string;
+      debtor_payto_uri: string;
 
-      debtorIban: string;
-      debtorBic: string;
-      debtorName: string;
+      amount: Amount;
+      direction: "debit" | "credit";
 
-      amount: number;
-      currency: string;
       subject: string;
 
       // Transaction unique ID.  Matches
       // $transaction_id from the URI.
-      uid: string;
-      direction: "DBIT" | "CRDT";
-      date: string; // milliseconds since the Unix epoch
+      row_id: number;
+      date: number;
+      // date: Timestamp;
     }
+
     interface CreateBankAccountTransactionCreate {
       // Address in the Payto format of the wire transfer receiver.
       // It needs at least the 'message' query string parameter.
-      paytoUri: string;
+      payto_uri: string;
 
       // Transaction amount (in the $currency:x.y format), optional.
       // However, when not given, its value must occupy the 'amount'
@@ -326,32 +323,32 @@ namespace SandboxBackend {
     interface AccountMinimalData {
       // Username
       username: string;
-    
+
       // Legal name of the account owner.
       name: string;
-    
+
       // current balance of the account
       balance: Balance;
-    
+
       // Number indicating the max debit allowed for the requesting user.
       debit_threshold: Amount;
     }
-    
+
     interface AccountData {
       // Legal name of the account owner.
       name: string;
-    
+
       // Available balance on the account.
       balance: Balance;
-    
+
       // payto://-URI of the account.
       payto_uri: string;
-    
+
       // Number indicating the max debit allowed for the requesting user.
       debit_threshold: Amount;
-    
+
       contact_data?: ChallengeContactData;
-    
+
       // 'payto' address pointing the bank account
       // where to send cashouts.  This field is optional
       // because not all the accounts are required to participate
@@ -360,7 +357,7 @@ namespace SandboxBackend {
       // be done via the access API.
       cashout_payto_uri?: string;
     }
-    
+
   }
 
   namespace Circuit {
diff --git a/packages/demobank-ui/src/hooks/access.ts b/packages/demobank-ui/src/hooks/access.ts
index 2f6848af8..20fd64bfa 100644
--- a/packages/demobank-ui/src/hooks/access.ts
+++ b/packages/demobank-ui/src/hooks/access.ts
@@ -255,7 +255,7 @@ export function useTransactionDetails(
 }
 
 interface PaginationFilter {
-  page: number;
+  // page: number;
 }
 
 export function usePublicAccounts(
@@ -275,7 +275,7 @@ export function usePublicAccounts(
   } = useSWR<
     HttpResponseOk<SandboxBackend.CoreBank.PublicAccountsResponse>,
     RequestError<SandboxBackend.SandboxError>
-  >([`public-accounts`, args?.page, PAGE_SIZE], paginatedFetcher);
+  >([`public-accounts`, page, PAGE_SIZE], paginatedFetcher);
 
   const [lastAfter, setLastAfter] = useState<
     HttpResponse<
@@ -334,7 +334,7 @@ export function useTransactions(
 > {
   const { paginatedFetcher } = useAuthenticatedBackend();
 
-  const [page, setPage] = useState(1);
+  const [start, setStart] = useState<string>();
 
   const {
     data: afterData,
@@ -344,7 +344,7 @@ export function useTransactions(
     HttpResponseOk<SandboxBackend.CoreBank.BankAccountTransactionsResponse>,
     RequestError<SandboxBackend.SandboxError>
   >(
-    [`accounts/${account}/transactions`, args?.page, PAGE_SIZE],
+    [`accounts/${account}/transactions`, start, PAGE_SIZE],
     paginatedFetcher, {
     refreshInterval: 0,
     refreshWhenHidden: false,
@@ -374,19 +374,24 @@ export function useTransactions(
   // if the query returns less that we ask, then we have reach the end or beginning
   const isReachingEnd =
     afterData && afterData.data.transactions.length < PAGE_SIZE;
-  const isReachingStart = false;
+  const isReachingStart = start == undefined;
 
   const pagination = {
     isReachingEnd,
     isReachingStart,
     loadMore: () => {
       if (!afterData || isReachingEnd) return;
-      if (afterData.data.transactions.length < MAX_RESULT_SIZE) {
-        setPage(page + 1);
-      }
+      // 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));
+      // }
     },
     loadMorePrev: () => {
-      null;
+      if (!afterData || isReachingStart) return;
+      // if (afterData.data.transactions.length < MAX_RESULT_SIZE) {
+      setStart(undefined)
+      // }
     },
   };
 
diff --git a/packages/demobank-ui/src/hooks/backend.ts b/packages/demobank-ui/src/hooks/backend.ts
index e6a3a1c0c..3d5bfa360 100644
--- a/packages/demobank-ui/src/hooks/backend.ts
+++ b/packages/demobank-ui/src/hooks/backend.ts
@@ -159,7 +159,7 @@ interface useBackendType {
   fetcher: <T>(endpoint: string) => Promise<HttpResponseOk<T>>;
   multiFetcher: <T>(endpoint: string[][]) => Promise<HttpResponseOk<T>[]>;
   paginatedFetcher: <T>(
-    args: [string, number, number],
+    args: [string, string | undefined, number],
   ) => Promise<HttpResponseOk<T>>;
   sandboxAccountsFetcher: <T>(
     args: [string, number, number, string],
@@ -188,13 +188,15 @@ export function usePublicBackend(): useBackendType {
     [baseUrl],
   );
   const paginatedFetcher = useCallback(
-    function fetcherImpl<T>([endpoint, page, size]: [
+    function fetcherImpl<T>([endpoint, start, size]: [
       string,
-      number,
+      string | undefined,
       number,
     ]): Promise<HttpResponseOk<T>> {
+      const delta = -1 * size //descending order
+      const params = start ? { delta, start } : {delta}
       return requestHandler<T>(baseUrl, endpoint, {
-        params: { page: page || 1, size },
+        params,
       });
     },
     [baseUrl],
@@ -280,14 +282,16 @@ export function useAuthenticatedBackend(): useBackendType {
     [baseUrl, creds],
   );
   const paginatedFetcher = useCallback(
-    function fetcherImpl<T>([endpoint, page = 1, size]: [
+    function fetcherImpl<T>([endpoint, start, size]: [
       string,
-      number,
+      string | undefined,
       number,
     ]): Promise<HttpResponseOk<T>> {
+      const delta = -1 * size //descending order
+      const params = start ? { delta, start } : {delta}
       return requestHandler<T>(baseUrl, endpoint, {
         token: creds,
-        params: { delta: size, start: size * page },
+        params,
       });
     },
     [baseUrl, creds],
diff --git a/packages/demobank-ui/src/pages/AccountPage/state.ts b/packages/demobank-ui/src/pages/AccountPage/state.ts
index a23d3b569..c212e7484 100644
--- a/packages/demobank-ui/src/pages/AccountPage/state.ts
+++ b/packages/demobank-ui/src/pages/AccountPage/state.ts
@@ -49,7 +49,7 @@ export function useComponentState({ account, goToBusinessAccount, goToConfirmOpe
       };
     }
     if (result.status === HttpStatusCode.Unauthorized) {
-      notifyError(i18n.str`Require login`, undefined);
+      notifyError(i18n.str`Authorization denied`, i18n.str`Maybe the session has expired, login again.`);
       return {
         status: "error-user-not-found",
         error: result,
diff --git a/packages/demobank-ui/src/pages/BankFrame.tsx b/packages/demobank-ui/src/pages/BankFrame.tsx
index 39ca09f1b..c4f872679 100644
--- a/packages/demobank-ui/src/pages/BankFrame.tsx
+++ b/packages/demobank-ui/src/pages/BankFrame.tsx
@@ -15,9 +15,9 @@
  */
 
 import { Amounts, Logger, PaytoUriIBAN, TranslatedString, parsePaytoUri, stringifyPaytoUri } from "@gnu-taler/taler-util";
-import { useNotifications, useTranslationContext } from "@gnu-taler/web-util/browser";
+import { notifyError, useNotifications, useTranslationContext } from "@gnu-taler/web-util/browser";
 import { ComponentChildren, Fragment, h, VNode } from "preact";
-import { StateUpdater, useEffect, useState } from "preact/hooks";
+import { StateUpdater, useEffect, useErrorBoundary, useState } from "preact/hooks";
 import { LangSelectorLikePy as LangSelector } from "../components/LangSelector.js";
 import { useBackendContext } from "../context/backend.js";
 import { useBusinessAccountDetails } from "../hooks/circuit.js";
@@ -50,6 +50,15 @@ export function BankFrame({
   const [settings, updateSettings] = useSettings();
   const [open, setOpen] = useState(false)
 
+  const [error, resetError] = useErrorBoundary();
+
+  useEffect(() => {
+    if (error) {
+      notifyError(i18n.str`Internal error, please report.`, (error instanceof Error ? error.message : String(error)) as TranslatedString)
+      resetError()
+    }
+  }, [error])
+
   const demo_sites = [];
   for (const i in bankUiSettings.demoSites)
     demo_sites.push(
@@ -355,7 +364,9 @@ function StatusBanner(): VNode {
                 </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>
-                  <p class="mt-3 text-sm md:ml-6 md:mt-0">
+                </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();
diff --git a/packages/demobank-ui/src/pages/HomePage.tsx b/packages/demobank-ui/src/pages/HomePage.tsx
index fb30c1218..d945d80d1 100644
--- a/packages/demobank-ui/src/pages/HomePage.tsx
+++ b/packages/demobank-ui/src/pages/HomePage.tsx
@@ -68,7 +68,7 @@ export function HomePage({
       account={account}
       goToConfirmOperation={goToConfirmOperation}
       goToBusinessAccount={goToBusinessAccount}
-      onLoadNotOk={handleNotOkResult(i18n, onRegister)}
+      onLoadNotOk={handleNotOkResult(i18n)}
     />
   );
 }
@@ -112,7 +112,6 @@ export function WithdrawalOperationPage({
 
 export function handleNotOkResult(
   i18n: ReturnType<typeof useTranslationContext>["i18n"],
-  onRegister?: () => void,
 ): <T>(
   result:
     | HttpResponsePaginated<T, SandboxBackend.SandboxError>
@@ -133,7 +132,7 @@ export function handleNotOkResult(
         case ErrorType.CLIENT: {
           if (result.status === HttpStatusCode.Unauthorized) {
             notifyError(i18n.str`Wrong credentials`, undefined);
-            return <LoginForm onRegister={onRegister} />;
+            return <LoginForm />;
           }
           const errorData = result.payload;
           notify({
diff --git a/packages/demobank-ui/src/pages/LoginForm.tsx b/packages/demobank-ui/src/pages/LoginForm.tsx
index f1ba439ac..f579678f2 100644
--- a/packages/demobank-ui/src/pages/LoginForm.tsx
+++ b/packages/demobank-ui/src/pages/LoginForm.tsx
@@ -24,6 +24,7 @@ import { useCredentialsChecker } from "../hooks/useCredentialsChecker.js";
 import { bankUiSettings } from "../settings.js";
 import { undefinedIfEmpty } from "../utils.js";
 
+
 /**
  * Collect and submit login data.
  */
@@ -35,7 +36,6 @@ export function LoginForm({ onRegister }: { onRegister?: () => void }): VNode {
   const { i18n } = useTranslationContext();
   const { requestNewLoginToken, refreshLoginToken } = useCredentialsChecker();
 
-  // const testLogin = useCredentialsCheckerOld();
   const ref = useRef<HTMLInputElement>(null);
   useEffect(function focusInput() {
     ref.current?.focus();
@@ -120,6 +120,13 @@ export function LoginForm({ onRegister }: { onRegister?: () => void }): VNode {
     setBusy(undefined)
   }
 
+  /**
+   * Register form may be shown in the initialization step.
+   * If this is an error when usgin the app the registration
+   * callback is not set
+   */
+  const isSessionExpired = !onRegister
+
   return (
     <Fragment>
       <h1 class="nav"></h1>
@@ -128,9 +135,9 @@ export function LoginForm({ onRegister }: { onRegister?: () => void }): VNode {
       )} */}
 
       <div class="flex min-h-full flex-col justify-center">
-        <div class="sm:mx-auto sm:w-full sm:max-w-sm">
+        {/* <div class="sm:mx-auto sm:w-full sm:max-w-sm">
           <h2 class="text-center text-2xl font-bold leading-9 tracking-tight text-gray-900">{i18n.str`Welcome to ${bankUiSettings.bankName}!`}</h2>
-        </div>
+        </div> */}
 
         <div class="mt-10 sm:mx-auto sm:w-full sm:max-w-sm">
           <form class="space-y-6" noValidate
@@ -151,8 +158,9 @@ export function LoginForm({ onRegister }: { onRegister?: () => void }): VNode {
                   type="text"
                   name="username"
                   id="username"
-                  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"
+                  class="block w-full disabled:bg-gray-200 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={username ?? ""}
+                  disabled={isSessionExpired}
                   enterkeyhint="next"
                   placeholder="identification"
                   autocomplete="username"
@@ -194,7 +202,28 @@ export function LoginForm({ onRegister }: { onRegister?: () => void }): VNode {
               </div>
             </div>
 
-            <div>
+            {isSessionExpired ? <div class="flex justify-between">
+              <button type="submit"
+                class="rounded-md bg-white-600 px-3 py-1.5 text-sm font-semibold leading-6 text-black shadow-sm hover:bg-gray-100 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-gray-600"
+                onClick={(e) => {
+                  e.preventDefault()
+                  doLogin()
+                }}
+              >
+                <i18n.Translate>Cancel</i18n.Translate>
+              </button>
+              
+              <button type="submit"
+                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()
+                  doLogin()
+                }}
+              >
+                <i18n.Translate>Renew session</i18n.Translate>
+              </button>
+            </div> : <div>
               <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"
                 disabled={!!errors}
@@ -205,7 +234,7 @@ export function LoginForm({ onRegister }: { onRegister?: () => void }): VNode {
               >
                 <i18n.Translate>Log in</i18n.Translate>
               </button>
-            </div>
+            </div>}
           </form>
 
           {bankUiSettings.allowRegistrations && onRegister &&
diff --git a/packages/demobank-ui/src/pages/WithdrawalConfirmationQuestion.tsx b/packages/demobank-ui/src/pages/WithdrawalConfirmationQuestion.tsx
index 30fcbdff7..d160a88b3 100644
--- a/packages/demobank-ui/src/pages/WithdrawalConfirmationQuestion.tsx
+++ b/packages/demobank-ui/src/pages/WithdrawalConfirmationQuestion.tsx
@@ -317,8 +317,9 @@ export function WithdrawalConfirmationQuestion({
                   </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">To be added</dd>
-                    {/* Amounts.stringifyValue(details.amount) */}
+                    <dd class="mt-1 text-sm leading-6 text-gray-700 sm:col-span-2 sm:mt-0">
+                      {Amounts.stringifyValue(details.amount)}
+                    </dd>
                   </div>
                 </dl>
               </div>
diff --git a/packages/demobank-ui/src/pages/admin/Account.tsx b/packages/demobank-ui/src/pages/admin/Account.tsx
index 8ea59cdcb..521bc8eb3 100644
--- a/packages/demobank-ui/src/pages/admin/Account.tsx
+++ b/packages/demobank-ui/src/pages/admin/Account.tsx
@@ -13,7 +13,7 @@ export function AdminAccount({ onRegister }: { onRegister: () => void }): VNode
   const result = useAccountDetails(account);
 
   if (!result.ok) {
-    return handleNotOkResult(i18n, onRegister)(result);
+    return handleNotOkResult(i18n)(result);
   }
   const { data } = result;
 
diff --git a/packages/demobank-ui/src/pages/admin/AccountList.tsx b/packages/demobank-ui/src/pages/admin/AccountList.tsx
index eb5765533..f99b320a4 100644
--- a/packages/demobank-ui/src/pages/admin/AccountList.tsx
+++ b/packages/demobank-ui/src/pages/admin/AccountList.tsx
@@ -8,17 +8,16 @@ import { useTranslationContext } from "@gnu-taler/web-util/browser";
 interface Props {
   onAction: (type: AccountAction, account: string) => void;
   account: string | undefined;
-  onRegister: () => void;
   onCreateAccount: () => void;
 }
 
-export function AccountList({ account, onAction, onCreateAccount, onRegister }: Props): VNode {
+export function AccountList({ account, onAction, onCreateAccount }: Props): VNode {
   const result = useBusinessAccounts({ account });
   const { i18n } = useTranslationContext();
 
   if (result.loading) return <div />;
   if (!result.ok) {
-    return handleNotOkResult(i18n, onRegister)(result);
+    return handleNotOkResult(i18n)(result);
   }
 
   const { customers } = result.data;
diff --git a/packages/demobank-ui/src/pages/admin/Home.tsx b/packages/demobank-ui/src/pages/admin/Home.tsx
index 5033b7fdc..725820ada 100644
--- a/packages/demobank-ui/src/pages/admin/Home.tsx
+++ b/packages/demobank-ui/src/pages/admin/Home.tsx
@@ -37,7 +37,7 @@ export function AdminHome({ onRegister }: Props): VNode {
     switch (action.type) {
       case "show-cashouts-details": return <ShowCashoutDetails
         id={action.account}
-        onLoadNotOk={handleNotOkResult(i18n, onRegister)}
+        onLoadNotOk={handleNotOkResult(i18n)}
         onCancel={() => {
           setAction(undefined);
         }}
@@ -73,7 +73,7 @@ export function AdminHome({ onRegister }: Props): VNode {
       )
       case "update-password": return <UpdateAccountPassword
         account={action.account}
-        onLoadNotOk={handleNotOkResult(i18n, onRegister)}
+        onLoadNotOk={handleNotOkResult(i18n)}
         onUpdateSuccess={() => {
           notifyInfo(i18n.str`Password changed`);
           setAction(undefined);
@@ -84,7 +84,7 @@ export function AdminHome({ onRegister }: Props): VNode {
       />
       case "remove-account": return <RemoveAccount
         account={action.account}
-        onLoadNotOk={handleNotOkResult(i18n, onRegister)}
+        onLoadNotOk={handleNotOkResult(i18n)}
         onUpdateSuccess={() => {
           notifyInfo(i18n.str`Account removed`);
           setAction(undefined);
@@ -95,7 +95,7 @@ export function AdminHome({ onRegister }: Props): VNode {
       />
       case "show-details": return <ShowAccountDetails
         account={action.account}
-        onLoadNotOk={handleNotOkResult(i18n, onRegister)}
+        onLoadNotOk={handleNotOkResult(i18n)}
         onChangePassword={() => {
           setAction({
             type: "update-password",
diff --git a/packages/demobank-ui/src/pages/business/Home.tsx b/packages/demobank-ui/src/pages/business/Home.tsx
index 628ae328d..c9d798082 100644
--- a/packages/demobank-ui/src/pages/business/Home.tsx
+++ b/packages/demobank-ui/src/pages/business/Home.tsx
@@ -75,7 +75,7 @@ export function BusinessAccount({
     return (
       <CreateCashout
         account={account}
-        onLoadNotOk={handleNotOkResult(i18n, onRegister)}
+        onLoadNotOk={handleNotOkResult(i18n)}
         onCancel={() => {
           setNewcashout(false);
         }}
@@ -93,7 +93,7 @@ export function BusinessAccount({
     return (
       <ShowCashoutDetails
         id={showCashoutDetails}
-        onLoadNotOk={handleNotOkResult(i18n, onRegister)}
+        onLoadNotOk={handleNotOkResult(i18n)}
         onCancel={() => {
           setShowCashoutDetails(undefined);
         }}
@@ -104,7 +104,7 @@ export function BusinessAccount({
     return (
       <UpdateAccountPassword
         account={account}
-        onLoadNotOk={handleNotOkResult(i18n, onRegister)}
+        onLoadNotOk={handleNotOkResult(i18n)}
         onUpdateSuccess={() => {
           notifyInfo(i18n.str`Password changed`);
           setUpdatePassword(false);
@@ -119,7 +119,7 @@ export function BusinessAccount({
     <div>
       <ShowAccountDetails
         account={account}
-        onLoadNotOk={handleNotOkResult(i18n, onRegister)}
+        onLoadNotOk={handleNotOkResult(i18n)}
         onUpdateSuccess={() => {
           notifyInfo(i18n.str`Account updated`);
         }}
-- 
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/Transactions/views.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


From 372ddff91798cf9247eaf045f0d0ce33694fd880 Mon Sep 17 00:00:00 2001
From: Sebastian <sebasjm@gmail.com>
Date: Sun, 1 Oct 2023 12:50:43 -0300
Subject: render amount and limit input

---
 .../demobank-ui/src/components/Cashouts/views.tsx  |  5 +-
 .../src/components/Transactions/views.tsx          | 23 +++++----
 packages/demobank-ui/src/components/app.tsx        |  7 +--
 packages/demobank-ui/src/context/config.ts         | 52 +++++++++++++++++++
 packages/demobank-ui/src/declaration.d.ts          | 24 +++++++--
 packages/demobank-ui/src/hooks/config.ts           | 22 +++++---
 packages/demobank-ui/src/pages/BankFrame.tsx       | 58 +++++++++++-----------
 packages/demobank-ui/src/pages/PaymentOptions.tsx  |  6 +--
 .../src/pages/PaytoWireTransferForm.tsx            | 39 ++++++++++++---
 .../demobank-ui/src/pages/WalletWithdrawForm.tsx   |  4 +-
 .../src/pages/WithdrawalConfirmationQuestion.tsx   |  3 +-
 .../demobank-ui/src/pages/admin/AccountList.tsx    |  8 +--
 packages/demobank-ui/src/pages/business/Home.tsx   | 16 +++---
 .../src/components/HistoryItem.tsx                 | 30 +++++------
 14 files changed, 201 insertions(+), 96 deletions(-)
 create mode 100644 packages/demobank-ui/src/context/config.ts

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

diff --git a/packages/demobank-ui/src/components/Cashouts/views.tsx b/packages/demobank-ui/src/components/Cashouts/views.tsx
index 4b7649fb6..a32deb266 100644
--- a/packages/demobank-ui/src/components/Cashouts/views.tsx
+++ b/packages/demobank-ui/src/components/Cashouts/views.tsx
@@ -19,6 +19,7 @@ import { useTranslationContext } from "@gnu-taler/web-util/browser";
 import { State } from "./index.js";
 import { format } from "date-fns";
 import { Amounts } from "@gnu-taler/taler-util";
+import { RenderAmount } from "../../pages/PaytoWireTransferForm.js";
 
 export function LoadingUriView({ error }: State.LoadingUriError): VNode {
   const { i18n } = useTranslationContext();
@@ -62,8 +63,8 @@ export function ReadyView({ cashouts, onSelected }: State.Ready): VNode {
                     ? format(item.confirmation_time, "dd/MM/yyyy HH:mm:ss")
                     : "-"}
                 </td>
-                <td>{Amounts.stringifyValue(item.amount_debit)}</td>
-                <td>{Amounts.stringifyValue(item.amount_credit)}</td>
+                <td><RenderAmount value={Amounts.parseOrThrow(item.amount_debit)} /></td>
+                <td><RenderAmount value={Amounts.parseOrThrow(item.amount_credit)} /></td>
                 <td>{item.status}</td>
                 <td>
                   <a
diff --git a/packages/demobank-ui/src/components/Transactions/views.tsx b/packages/demobank-ui/src/components/Transactions/views.tsx
index f92c874f3..69f4369a2 100644
--- a/packages/demobank-ui/src/components/Transactions/views.tsx
+++ b/packages/demobank-ui/src/components/Transactions/views.tsx
@@ -20,6 +20,7 @@ import { State } from "./index.js";
 import { format, isToday } from "date-fns";
 import { Amounts } from "@gnu-taler/taler-util";
 import { useEffect, useRef } from "preact/hooks";
+import { RenderAmount } from "../../pages/PaytoWireTransferForm.js";
 
 export function LoadingUriView({ error }: State.LoadingUriError): VNode {
   const { i18n } = useTranslationContext();
@@ -72,13 +73,7 @@ export function ReadyView({ transactions, onNext, onPrev }: State.Ready): VNode
                 {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 ">
@@ -86,20 +81,26 @@ export function ReadyView({ transactions, onNext, onPrev }: State.Ready): VNode
                       <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)}`
+                          {item.negative ? i18n.str`sent` : i18n.str`received`}
+
+                          {item.amount ? (
+                            <RenderAmount value={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}
+                          {item.negative ? i18n.str`to` : i18n.str`from`} {item.counterpart}
                         </dd>
                       </dl>
                     </td>
                     <td data-negative={item.negative ? "true" : "false"}
                       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}
+                      {item.amount ? (<RenderAmount value={item.amount} negative={item.negative} />
+                      ) : (
+                        <span style={{ color: "grey" }}>&lt;{i18n.str`invalid value`}&gt;</span>
+                      )}
                     </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>
diff --git a/packages/demobank-ui/src/components/app.tsx b/packages/demobank-ui/src/components/app.tsx
index f15a9ee6a..7cf658681 100644
--- a/packages/demobank-ui/src/components/app.tsx
+++ b/packages/demobank-ui/src/components/app.tsx
@@ -31,6 +31,7 @@ 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";
+import { ConfigStateProvider } from "../context/config.js";
 const WITH_LOCAL_STORAGE_CACHE = false;
 
 /**
@@ -84,11 +85,11 @@ function VersionCheck({ children }: { children: ComponentChildren }): VNode {
     </BankFrame>
   }
   if (checked.type === "ok") {
-    return <Fragment>{children}</Fragment>
+    return <ConfigStateProvider value={checked.result}>{children}</ConfigStateProvider>
   }
-  
+
   return <BankFrame>
-    <ErrorLoading error={checked.result}/>
+    <ErrorLoading error={checked.result} />
   </BankFrame>
 }
 
diff --git a/packages/demobank-ui/src/context/config.ts b/packages/demobank-ui/src/context/config.ts
new file mode 100644
index 000000000..a2cde18eb
--- /dev/null
+++ b/packages/demobank-ui/src/context/config.ts
@@ -0,0 +1,52 @@
+/*
+ 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 { ComponentChildren, createContext, h, VNode } from "preact";
+import { useContext } from "preact/hooks";
+
+/**
+ *
+ * @author Sebastian Javier Marchano (sebasjm)
+ */
+
+export type Type = Required<SandboxBackend.Config>;
+
+const initial: Type = {
+  name: "",
+  version: "0:0:0",
+  currency_fraction_digits: 2,
+  currency_fraction_limit: 2,
+  fiat_currency: "",
+  have_cashout: false,
+};
+const Context = createContext<Type>(initial);
+
+export const useConfigContext = (): Type => useContext(Context);
+
+export const ConfigStateProvider = ({
+  value,
+  children,
+}: {
+  value: Type,
+  children: ComponentChildren;
+}): VNode => {
+
+  return h(Context.Provider, {
+    value,
+    children,
+  });
+};
+
diff --git a/packages/demobank-ui/src/declaration.d.ts b/packages/demobank-ui/src/declaration.d.ts
index bd7edf033..5c55cfade 100644
--- a/packages/demobank-ui/src/declaration.d.ts
+++ b/packages/demobank-ui/src/declaration.d.ts
@@ -107,9 +107,27 @@ namespace SandboxBackend {
     name: string;
     // API version in the form $n:$n:$n
     version: string;
-    // Contains ratios and fees related to buying
-    // and selling the circuit currency.
-    ratios_and_fees: RatiosAndFees;
+    // If 'true', the server provides local currency
+    // conversion support.
+    // If missing or false, some parts of the API
+    // are not supported and return 404.
+    have_cashout?: boolean;
+
+    // Fiat currency.  That is the currency in which
+    // cash-out operations ultimately wire money.
+    // Only applicable if have_cashout=true.
+    fiat_currency?: string;
+
+    // How many digits should the amounts be rendered
+    // with by default. Small capitals should
+    // be used to render fractions beyond the number
+    // given here (like on gas stations).
+    currency_fraction_digits?: number;
+
+    // How many decimal digits an operation can
+    // have. Wire transfers with more decimal 
+    // digits will not be accepted.
+    currency_fraction_limit?: number;
   }
   interface RatiosAndFees {
     // Exchange rate to buy the circuit currency from fiat.
diff --git a/packages/demobank-ui/src/hooks/config.ts b/packages/demobank-ui/src/hooks/config.ts
index bb5134510..a3bd294db 100644
--- a/packages/demobank-ui/src/hooks/config.ts
+++ b/packages/demobank-ui/src/hooks/config.ts
@@ -18,13 +18,13 @@ async function getConfigState(
   return result.data;
 }
 
-type Result = undefined
-  | { type: "ok", result: SandboxBackend.Config }
+export type ConfigResult = undefined
+  | { type: "ok", result: Required<SandboxBackend.Config> }
   | { type: "wrong", result: SandboxBackend.Config }
   | { type: "error", result: HttpError<SandboxBackend.SandboxError> }
 
-export function useConfigState(): Result {
-  const [checked, setChecked] = useState<Result>()
+export function useConfigState(): ConfigResult {
+  const [checked, setChecked] = useState<ConfigResult>()
   const { request } = useApiContext();
 
   useEffect(() => {
@@ -32,15 +32,23 @@ export function useConfigState(): Result {
       .then((result) => {
         const r = LibtoolVersion.compare(BANK_INTEGRATION_PROTOCOL_VERSION, result.version)
         if (r?.compatible) {
-          setChecked({ type: "ok",result });
+          const complete: Required<SandboxBackend.Config> = {
+            currency_fraction_digits: result.currency_fraction_digits ?? 2,
+            currency_fraction_limit: result.currency_fraction_limit ?? 2,
+            fiat_currency: "",
+            have_cashout: result.have_cashout ?? false,
+            name: result.name,
+            version: result.version,
+          }
+          setChecked({ type: "ok", result: complete });
         } else {
-          setChecked({ type: "wrong",result })
+          setChecked({ type: "wrong", result })
         }
       })
       .catch((error: unknown) => {
         if (error instanceof RequestError) {
           const result = error.cause
-          setChecked({ type:"error", result });
+          setChecked({ type: "error", result });
         }
       });
   }, []);
diff --git a/packages/demobank-ui/src/pages/BankFrame.tsx b/packages/demobank-ui/src/pages/BankFrame.tsx
index 29334cae4..fc8dfd599 100644
--- a/packages/demobank-ui/src/pages/BankFrame.tsx
+++ b/packages/demobank-ui/src/pages/BankFrame.tsx
@@ -27,6 +27,7 @@ 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";
+import { RenderAmount } from "./PaytoWireTransferForm.js";
 
 const GIT_HASH = typeof __GIT_HASH__ !== "undefined" ? __GIT_HASH__ : undefined;
 const VERSION = typeof __VERSION__ !== "undefined" ? __VERSION__ : undefined;
@@ -87,7 +88,7 @@ export function BankFrame({
                     class="h-8 w-auto"
                     src={logo}
                     alt="Taler"
-                    style={{ height: 35, margin: 10 }}
+                    style={{ height: "1.5rem", margin: ".5rem" }}
                   />
                 </a>
               </div>
@@ -322,10 +323,10 @@ function MaybeShowDebugInfo({ info }: { info: any }): VNode {
   const [settings] = useSettings()
   if (settings.showDebugInfo) {
     return <pre class="whitespace-break-spaces ">
-    {info}
-  </pre>
+      {info}
+    </pre>
   }
-  return <Fragment /> 
+  return <Fragment />
 }
 
 
@@ -333,19 +334,19 @@ function StatusBanner(): VNode {
   const notifs = useNotifications()
   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 <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">
+    notifs.map(n => {
+      switch (n.message.type) {
+        case "error":
+          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 &&
@@ -353,13 +354,13 @@ function StatusBanner(): VNode {
                   {n.message.debug}
                 </div>
               } */}
-            </Attention>
-          case "info":
-            return <Attention type="success" title={n.message.title} onClose={() => {
-              n.remove();
-            }} />
-        }
-      })}
+          </Attention>
+        case "info":
+          return <Attention type="success" title={n.message.title} onClose={() => {
+            n.remove();
+          }} />
+      }
+    })}
   </div>
 
 }
@@ -430,9 +431,8 @@ function AccountBalance({ account }: { account: string }): VNode {
   const result = useAccountDetails(account);
   if (!result.ok) return <div />
 
-  return <div>
-    {Amounts.currencyOf(result.data.balance.amount)}
-    &nbsp;{result.data.balance.credit_debit_indicator === "debit" ? "-" : ""}
-    {Amounts.stringifyValue(result.data.balance.amount)}
-  </div>
+  return <RenderAmount
+    value={Amounts.parseOrThrow(result.data.balance.amount)}
+    negative={result.data.balance.credit_debit_indicator === "debit"}
+  />
 }
diff --git a/packages/demobank-ui/src/pages/PaymentOptions.tsx b/packages/demobank-ui/src/pages/PaymentOptions.tsx
index fef272831..f60ba3270 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>("wire-transfer");
+  const [tab, setTab] = useState<"charge-wallet" | "wire-transfer" | undefined>();
 
   return (
     <div class="mt-2">
@@ -47,7 +47,7 @@ export function PaymentOptions({ limit, goToConfirmOperation }: { limit: AmountJ
               setTab("charge-wallet")
             }} />
             <span class="flex flex-1">
-              <div class="text-lg mr-2">&#x1F4B5;</div>
+              <div class="text-4xl mr-4 my-auto">&#x1F4B5;</div>
               <span class="flex flex-col">
                 <span id="project-type-0-label" class="block text-sm font-medium text-gray-900">
                   <i18n.Translate>a <b>Taler</b> wallet</i18n.Translate>
@@ -76,7 +76,7 @@ export function PaymentOptions({ limit, goToConfirmOperation }: { limit: AmountJ
               setTab("wire-transfer")
             }} />
             <span class="flex flex-1">
-              <div class="text-lg mr-2">&#x2194;</div>
+              <div class="text-4xl mr-4 my-auto">&#x2194;</div>
               <span class="flex flex-col">
                 <span id="project-type-1-label" class="block text-sm font-medium text-gray-900">
                   <i18n.Translate>another bank account</i18n.Translate>
diff --git a/packages/demobank-ui/src/pages/PaytoWireTransferForm.tsx b/packages/demobank-ui/src/pages/PaytoWireTransferForm.tsx
index 785dc4264..b3fd51670 100644
--- a/packages/demobank-ui/src/pages/PaytoWireTransferForm.tsx
+++ b/packages/demobank-ui/src/pages/PaytoWireTransferForm.tsx
@@ -39,6 +39,8 @@ import {
   undefinedIfEmpty,
   validateIBAN,
 } from "../utils.js";
+import { useConfigState } from "../hooks/config.js";
+import { useConfigContext } from "../context/config.js";
 
 const logger = new Logger("PaytoWireTransferForm");
 
@@ -55,7 +57,7 @@ export function PaytoWireTransferForm({
   onCancel: (() => void) | undefined;
   limit: AmountJson;
 }): VNode {
-  const [isRawPayto, setIsRawPayto] = useState(true);
+  const [isRawPayto, setIsRawPayto] = useState(false);
   // FIXME: remove this
   const [iban, setIban] = useState<string | undefined>("DE4745461198061");
   const [subject, setSubject] = useState<string | undefined>("ASD");
@@ -285,7 +287,7 @@ export function PaytoWireTransferForm({
 
             <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
+              <InputAmount
                 name="amount"
                 left
                 currency={limit.currency}
@@ -372,7 +374,9 @@ export function doAutoFocus(element: HTMLElement | null) {
   }
 }
 
-export function Amount(
+const FRAC_SEPARATOR = "."
+
+export function InputAmount(
   {
     currency,
     name,
@@ -390,6 +394,7 @@ export function Amount(
   },
   ref: Ref<HTMLInputElement>,
 ): VNode {
+  const cfg = useConfigContext()
   return (
     <div class="mt-2">
       <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">
@@ -409,10 +414,14 @@ export function Amount(
           autocomplete="off"
           value={value ?? ""}
           disabled={!onChange}
-          onInput={(e): void => {
-            if (onChange) {
-              onChange(e.currentTarget.value);
+          onInput={(e) => {
+            if (!onChange) return;
+            const l = e.currentTarget.value.length
+            const sep_pos = e.currentTarget.value.indexOf(FRAC_SEPARATOR)
+            if (sep_pos !== -1 && l - sep_pos - 1 > cfg.currency_fraction_limit) {
+              e.currentTarget.value = e.currentTarget.value.substring(0, sep_pos + cfg.currency_fraction_limit + 1)
             }
+            onChange(e.currentTarget.value);
           }}
         />
       </div>
@@ -421,3 +430,21 @@ export function Amount(
   );
 }
 
+export function RenderAmount({ value, negative }: { value: AmountJson, negative?: boolean }): VNode {
+  const cfg = useConfigContext()
+  const str = Amounts.stringifyValue(value)
+  const sep_pos = str.indexOf(FRAC_SEPARATOR)
+  if (sep_pos !== -1 && str.length - sep_pos - 1 > cfg.currency_fraction_digits) {
+    const limit = sep_pos + cfg.currency_fraction_digits + 1
+    const normal = str.substring(0, limit)
+    const small = str.substring(limit)
+    return <span class="whitespace-nowrap">
+      {negative ? "-" : undefined}
+      {value.currency} {normal} <sup class="-ml-2">{small}</sup>
+    </span>
+  }
+  return <span class="whitespace-nowrap">
+    {negative ? "-" : undefined}
+    {value.currency} {str}
+  </span>
+}
\ 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 7357223b7..da299b1c8 100644
--- a/packages/demobank-ui/src/pages/WalletWithdrawForm.tsx
+++ b/packages/demobank-ui/src/pages/WalletWithdrawForm.tsx
@@ -34,13 +34,13 @@ 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, doAutoFocus } from "./PaytoWireTransferForm.js";
+import { InputAmount, 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);
+const RefAmount = forwardRef(InputAmount);
 
 
 function OldWithdrawalForm({ goToConfirmOperation, limit, onCancel, focus }: {
diff --git a/packages/demobank-ui/src/pages/WithdrawalConfirmationQuestion.tsx b/packages/demobank-ui/src/pages/WithdrawalConfirmationQuestion.tsx
index 208d4b859..ddcd2492d 100644
--- a/packages/demobank-ui/src/pages/WithdrawalConfirmationQuestion.tsx
+++ b/packages/demobank-ui/src/pages/WithdrawalConfirmationQuestion.tsx
@@ -38,6 +38,7 @@ import { ShowInputErrorLabel } from "../components/ShowInputErrorLabel.js";
 import { useAccessAnonAPI } from "../hooks/access.js";
 import { buildRequestErrorMessage, undefinedIfEmpty } from "../utils.js";
 import { useSettings } from "../hooks/settings.js";
+import { RenderAmount } from "./PaytoWireTransferForm.js";
 
 const logger = new Logger("WithdrawalConfirmationQuestion");
 
@@ -318,7 +319,7 @@ 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">Amount</dt>
                     <dd class="mt-1 text-sm leading-6 text-gray-700 sm:col-span-2 sm:mt-0">
-                      {Amounts.currencyOf(details.amount)} {Amounts.stringifyValue(details.amount)}
+                      <RenderAmount value={details.amount} />
                     </dd>
                   </div>
                 </dl>
diff --git a/packages/demobank-ui/src/pages/admin/AccountList.tsx b/packages/demobank-ui/src/pages/admin/AccountList.tsx
index f99b320a4..a6899e679 100644
--- a/packages/demobank-ui/src/pages/admin/AccountList.tsx
+++ b/packages/demobank-ui/src/pages/admin/AccountList.tsx
@@ -4,6 +4,7 @@ import { handleNotOkResult } from "../HomePage.js";
 import { AccountAction } from "./Home.js";
 import { Amounts } from "@gnu-taler/taler-util";
 import { useTranslationContext } from "@gnu-taler/web-util/browser";
+import { RenderAmount } from "../PaytoWireTransferForm.js";
 
 interface Props {
   onAction: (type: AccountAction, account: string) => void;
@@ -88,12 +89,7 @@ export function AccountList({ account, onAction, onCreateAccount }: Props): VNod
                         i18n.str`unknown`
                       ) : (
                         <span class="amount">
-                          {balanceIsDebit ? <b>-</b> : null}
-                          <span class="value">{`${Amounts.stringifyValue(
-                            balance,
-                          )}`}</span>
-                          &nbsp;
-                          <span class="currency">{`${balance.currency}`}</span>
+                          <RenderAmount value={balance} negative={balanceIsDebit} />
                         </span>
                       )}
                     </td>
diff --git a/packages/demobank-ui/src/pages/business/Home.tsx b/packages/demobank-ui/src/pages/business/Home.tsx
index 2945cb65a..1a84effcd 100644
--- a/packages/demobank-ui/src/pages/business/Home.tsx
+++ b/packages/demobank-ui/src/pages/business/Home.tsx
@@ -45,7 +45,7 @@ import {
   undefinedIfEmpty,
 } from "../../utils.js";
 import { handleNotOkResult } from "../HomePage.js";
-import { Amount } from "../PaytoWireTransferForm.js";
+import { InputAmount } from "../PaytoWireTransferForm.js";
 import { ShowAccountDetails } from "../ShowAccountDetails.js";
 import { UpdateAccountPassword } from "../UpdateAccountPassword.js";
 
@@ -342,7 +342,7 @@ function CreateCashout({
 
           </label>
           <div style={{ display: "flex" }}>
-            <Amount
+            <InputAmount
               name="amount"
               currency={amount.currency}
               value={form.amount}
@@ -372,7 +372,7 @@ function CreateCashout({
         </fieldset>
         <fieldset>
           <label for="balance-now">{i18n.str`Balance now`}</label>
-          <Amount
+          <InputAmount
             name="banace-now"
             currency={balance.currency}
             value={Amounts.stringifyValue(balance)}
@@ -382,7 +382,7 @@ function CreateCashout({
           <label for="total-cost"
             style={{ fontWeight: "bold", color: "red" }}
           >{i18n.str`Total cost`}</label>
-          <Amount
+          <InputAmount
             name="total-cost"
             currency={balance.currency}
             value={Amounts.stringifyValue(calc.debit)}
@@ -390,7 +390,7 @@ function CreateCashout({
         </fieldset>
         <fieldset>
           <label for="balance-after">{i18n.str`Balance after`}</label>
-          <Amount
+          <InputAmount
             name="balance-after"
             currency={balance.currency}
             value={balanceAfter ? Amounts.stringifyValue(balanceAfter) : ""}
@@ -400,7 +400,7 @@ function CreateCashout({
           <Fragment>
             <fieldset>
               <label for="amount-conversiojn">{i18n.str`Amount after conversion`}</label>
-              <Amount
+              <InputAmount
                 name="amount-conversion"
                 currency={fiatCurrency}
                 value={Amounts.stringifyValue(calc.beforeFee)}
@@ -409,7 +409,7 @@ function CreateCashout({
 
             <fieldset>
               <label form="cashout-fee">{i18n.str`Cashout fee`}</label>
-              <Amount
+              <InputAmount
                 name="cashout-fee"
                 currency={fiatCurrency}
                 value={Amounts.stringifyValue(sellFee)}
@@ -421,7 +421,7 @@ function CreateCashout({
           <label for="total"
             style={{ fontWeight: "bold", color: "green" }}
           >{i18n.str`Total cashout transfer`}</label>
-          <Amount
+          <InputAmount
             name="total"
             currency={fiatCurrency}
             value={Amounts.stringifyValue(calc.credit)}
diff --git a/packages/taler-wallet-webextension/src/components/HistoryItem.tsx b/packages/taler-wallet-webextension/src/components/HistoryItem.tsx
index e072d2581..72881c746 100644
--- a/packages/taler-wallet-webextension/src/components/HistoryItem.tsx
+++ b/packages/taler-wallet-webextension/src/components/HistoryItem.tsx
@@ -62,10 +62,10 @@ export function HistoryItem(props: { tx: Transaction }): VNode {
                 WithdrawalType.TalerBankIntegrationApi
                 ? !tx.withdrawalDetails.confirmed
                   ? i18n.str`Need approval in the Bank`
-                  : i18n.str`Exchange is waiting the wire transfer`
+                  : i18n.str`Waiting for wire transfer to complete`
                 : tx.withdrawalDetails.type === WithdrawalType.ManualTransfer
-                ? i18n.str`Exchange is waiting the wire transfer`
-                : "" //pending but no message
+                  ? i18n.str`Waiting for wire transfer to complete`
+                  : "" //pending but no message
               : undefined
           }
         />
@@ -88,8 +88,8 @@ export function HistoryItem(props: { tx: Transaction }): VNode {
                   ? i18n.str`Need approval in the Bank`
                   : i18n.str`Exchange is waiting the wire transfer`
                 : tx.withdrawalDetails.type === WithdrawalType.ManualTransfer
-                ? i18n.str`Exchange is waiting the wire transfer`
-                : "" //pending but no message
+                  ? i18n.str`Exchange is waiting the wire transfer`
+                  : "" //pending but no message
               : undefined
           }
         />
@@ -267,14 +267,14 @@ function Layout(props: LayoutProps): VNode {
       style={{
         backgroundColor:
           props.currentState === TransactionMajorState.Pending ||
-          props.currentState === TransactionMajorState.Dialog
+            props.currentState === TransactionMajorState.Dialog
             ? "lightcyan"
             : props.currentState === TransactionMajorState.Failed
-            ? "#ff000040"
-            : props.currentState === TransactionMajorState.Aborted ||
-              props.currentState === TransactionMajorState.Aborting
-            ? "#00000010"
-            : "inherit",
+              ? "#ff000040"
+              : props.currentState === TransactionMajorState.Aborted ||
+                props.currentState === TransactionMajorState.Aborting
+                ? "#00000010"
+                : "inherit",
         alignItems: "center",
       }}
     >
@@ -353,10 +353,10 @@ function TransactionAmount(props: TransactionAmountProps): VNode {
           props.currentState !== TransactionMajorState.Done
             ? "gray"
             : sign === "+"
-            ? "darkgreen"
-            : sign === "-"
-            ? "darkred"
-            : undefined,
+              ? "darkgreen"
+              : sign === "-"
+                ? "darkred"
+                : undefined,
       }}
     >
       <ExtraLargeText>
-- 
cgit v1.2.3


From e84d2b6175da8c308835f1a7f27e177dcecd248a Mon Sep 17 00:00:00 2001
From: Sebastian <sebasjm@gmail.com>
Date: Tue, 3 Oct 2023 10:11:21 -0300
Subject: more ui, removed some testing values

---
 .../src/components/Transactions/views.tsx          |  4 +--
 .../src/pages/PaytoWireTransferForm.tsx            |  8 ++---
 packages/demobank-ui/src/pages/QrCodeSection.tsx   | 35 +++++++++++-----------
 .../demobank-ui/src/pages/WithdrawalQRCode.tsx     |  2 +-
 4 files changed, 23 insertions(+), 26 deletions(-)

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

diff --git a/packages/demobank-ui/src/components/Transactions/views.tsx b/packages/demobank-ui/src/components/Transactions/views.tsx
index 69f4369a2..696fb59f3 100644
--- a/packages/demobank-ui/src/components/Transactions/views.tsx
+++ b/packages/demobank-ui/src/components/Transactions/views.tsx
@@ -81,9 +81,7 @@ export function ReadyView({ transactions, onNext, onPrev }: State.Ready): VNode
                       <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 ? (
+                          {item.negative ? i18n.str`sent` : i18n.str`received`} {item.amount ? (
                             <RenderAmount value={item.amount} />
                           ) : (
                             <span style={{ color: "grey" }}>&lt;{i18n.str`invalid value`}&gt;</span>
diff --git a/packages/demobank-ui/src/pages/PaytoWireTransferForm.tsx b/packages/demobank-ui/src/pages/PaytoWireTransferForm.tsx
index b3fd51670..69a9a07b2 100644
--- a/packages/demobank-ui/src/pages/PaytoWireTransferForm.tsx
+++ b/packages/demobank-ui/src/pages/PaytoWireTransferForm.tsx
@@ -59,9 +59,9 @@ export function PaytoWireTransferForm({
 }): VNode {
   const [isRawPayto, setIsRawPayto] = useState(false);
   // 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 [iban, setIban] = useState<string | undefined>();
+  const [subject, setSubject] = useState<string | undefined>();
+  const [amount, setAmount] = useState<string | undefined>();
 
   const [rawPaytoInput, rawPaytoInputSetter] = useState<string | undefined>(
     undefined,
@@ -189,7 +189,7 @@ export function PaytoWireTransferForm({
             <span class="flex flex-1">
               <span class="flex flex-col">
                 <span class="block text-sm  font-medium text-gray-900">
-                  <i18n.Translate>using a form</i18n.Translate>
+                  <i18n.Translate>Using a form</i18n.Translate>
                 </span>
               </span>
             </span>
diff --git a/packages/demobank-ui/src/pages/QrCodeSection.tsx b/packages/demobank-ui/src/pages/QrCodeSection.tsx
index 6a50d4ef3..e07525ab4 100644
--- a/packages/demobank-ui/src/pages/QrCodeSection.tsx
+++ b/packages/demobank-ui/src/pages/QrCodeSection.tsx
@@ -84,27 +84,25 @@ export function QrCodeSection({
           <h3 class="text-base font-semibold leading-6 text-gray-900">
             <i18n.Translate>If you have a Taler wallet installed in this device</i18n.Translate>
           </h3>
-          <div class="mt-4">
-            <a href={talerWithdrawUri}
-              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>
-            </a>
-          </div>
-          <div class="mt-4 max-w-xl text-sm text-gray-500">
+          <div class="mt-4 mb-4 text-sm text-gray-500">
             <p><i18n.Translate>
               You will see the details of the operation in your wallet including the fees (if applies).
-              If you still one you can install it from <a class="font-semibold text-gray-500 hover:text-gray-400" href="https://taler.net/en/wallet.html">here</a>.
+              If you still don't have one you can install it from <a class="font-semibold text-gray-500 hover:text-gray-400" href="https://taler.net/en/wallet.html">here</a>.
             </i18n.Translate></p>
           </div>
           <div class="flex items-center justify-between gap-x-6 border-t border-gray-900/10 pt-2 mt-2 ">
-            <div />
             <button type="button"
-              class="disabled:opacity-50 disabled:cursor-default cursor-pointer 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-600"
+              // class="disabled:opacity-50 disabled:cursor-default cursor-pointer rounded-md  px-3 py-2 text-sm font-semibold text-black shadow-sm hover:bg-red-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-red-600"
+              class="text-sm font-semibold leading-6 text-gray-900"
               onClick={doAbort}
             >
-              Cancel withdrawal
+              Cancel
             </button>
+            <a href={talerWithdrawUri}
+              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>Withdraw</i18n.Translate>
+            </a>
           </div>
         </div>
       </div>
@@ -122,12 +120,13 @@ export function QrCodeSection({
           </div>
         </div>
         <div class="flex items-center justify-center gap-x-6 border-t border-gray-900/10 px-4 py-4 sm:px-8">
-          <button type="button"
-            class="disabled:opacity-50 disabled:cursor-default cursor-pointer 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-600"
-            onClick={doAbort}
-          >
-            Cancel withdrawal
-          </button>
+        <button type="button"
+              // class="disabled:opacity-50 disabled:cursor-default cursor-pointer rounded-md  px-3 py-2 text-sm font-semibold text-black shadow-sm hover:bg-red-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-red-600"
+              class="text-sm font-semibold leading-6 text-gray-900"
+              onClick={doAbort}
+            >
+              Cancel
+            </button>
         </div>
       </div>
 
diff --git a/packages/demobank-ui/src/pages/WithdrawalQRCode.tsx b/packages/demobank-ui/src/pages/WithdrawalQRCode.tsx
index c8efc033b..2d90b410c 100644
--- a/packages/demobank-ui/src/pages/WithdrawalQRCode.tsx
+++ b/packages/demobank-ui/src/pages/WithdrawalQRCode.tsx
@@ -112,7 +112,7 @@ export function WithdrawalQRCode({
             e.preventDefault();
             onClose()
           }}>
-          <i18n.Translate>Continue</i18n.Translate>
+          <i18n.Translate>Done</i18n.Translate>
         </button>
       </div>
     </div>
-- 
cgit v1.2.3

