gnunet-svn
[Top][All Lists]
Advanced

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

[taler-wallet-core] branch master updated (d783cdc82 -> 2ac73949e)


From: gnunet
Subject: [taler-wallet-core] branch master updated (d783cdc82 -> 2ac73949e)
Date: Sun, 22 Oct 2023 01:26:02 +0200

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

sebasjm pushed a change to branch master
in repository wallet-core.

    from d783cdc82 fix typo: reject for promise instead of throw
     new 4b98b693d add missing response code
     new 2ac73949e more ui

The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .../assets/{icons/languageicon.svg => lang.svg}    |  96 +++---
 .../demobank-ui/src/components/Cashouts/views.tsx  | 159 +++++++---
 packages/demobank-ui/src/components/CopyButton.tsx |  32 +-
 .../demobank-ui/src/components/LangSelector.tsx    |   3 +-
 packages/demobank-ui/src/components/Routing.tsx    | 208 +++++++++++--
 .../src/components/Transactions/views.tsx          |  12 +-
 packages/demobank-ui/src/components/app.tsx        |   3 +-
 packages/demobank-ui/src/context/config.ts         |  10 +-
 packages/demobank-ui/src/hooks/access.ts           |  24 +-
 packages/demobank-ui/src/hooks/circuit.ts          |  14 +-
 packages/demobank-ui/src/pages.ts                  |  44 +++
 .../demobank-ui/src/pages/AccountPage/views.tsx    |  29 --
 packages/demobank-ui/src/pages/BankFrame.tsx       | 122 ++++----
 packages/demobank-ui/src/pages/LoginForm.tsx       |  12 +-
 .../demobank-ui/src/pages/OperationState/state.ts  |  15 +-
 packages/demobank-ui/src/pages/PaymentOptions.tsx  |  24 +-
 .../src/pages/PaytoWireTransferForm.tsx            |  65 ++--
 .../demobank-ui/src/pages/ProfileNavigation.tsx    |  56 ++++
 .../demobank-ui/src/pages/RegistrationPage.tsx     |  16 +-
 .../demobank-ui/src/pages/ShowAccountDetails.tsx   | 123 +++-----
 .../src/pages/UpdateAccountPassword.tsx            | 259 ++++++++-------
 .../demobank-ui/src/pages/WithdrawalQRCode.tsx     |   1 +
 packages/demobank-ui/src/pages/admin/Account.tsx   |  10 +-
 .../demobank-ui/src/pages/admin/AccountForm.tsx    | 182 ++++++++---
 .../demobank-ui/src/pages/admin/AccountList.tsx    |  58 ++--
 packages/demobank-ui/src/pages/admin/AdminHome.tsx |  32 ++
 .../src/pages/admin/CashoutListForAccount.tsx      |  47 +++
 .../src/pages/admin/CreateNewAccount.tsx           |  24 +-
 packages/demobank-ui/src/pages/admin/Home.tsx      | 143 ---------
 .../demobank-ui/src/pages/admin/RemoveAccount.tsx  |   3 +-
 .../pages/business/{Home.tsx => CreateCashout.tsx} | 346 +--------------------
 .../src/pages/business/ShowCashoutDetails.tsx      | 237 ++++++++++++++
 .../src/route.ts                                   |   0
 packages/taler-util/src/http-client/bank-core.ts   |   6 +-
 packages/taler-util/src/http-client/types.ts       |   5 +-
 packages/web-util/src/utils/http-impl.browser.ts   |   2 +-
 36 files changed, 1369 insertions(+), 1053 deletions(-)
 copy packages/demobank-ui/src/assets/{icons/languageicon.svg => lang.svg} (98%)
 create mode 100644 packages/demobank-ui/src/pages.ts
 create mode 100644 packages/demobank-ui/src/pages/ProfileNavigation.tsx
 create mode 100644 packages/demobank-ui/src/pages/admin/AdminHome.tsx
 create mode 100644 
packages/demobank-ui/src/pages/admin/CashoutListForAccount.tsx
 delete mode 100644 packages/demobank-ui/src/pages/admin/Home.tsx
 rename packages/demobank-ui/src/pages/business/{Home.tsx => CreateCashout.tsx} 
(57%)
 create mode 100644 
packages/demobank-ui/src/pages/business/ShowCashoutDetails.tsx
 copy packages/{aml-backoffice-ui => demobank-ui}/src/route.ts (100%)

diff --git a/packages/demobank-ui/src/assets/icons/languageicon.svg 
b/packages/demobank-ui/src/assets/lang.svg
similarity index 98%
copy from packages/demobank-ui/src/assets/icons/languageicon.svg
copy to packages/demobank-ui/src/assets/lang.svg
index 22d58da65..dd72ce65e 100644
--- a/packages/demobank-ui/src/assets/icons/languageicon.svg
+++ b/packages/demobank-ui/src/assets/lang.svg
@@ -1,48 +1,48 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Generator: Adobe Illustrator 19.1.0, SVG Export Plug-In . SVG Version: 
6.00 Build 0)  -->
-<svg version="1.1" xmlns="http://www.w3.org/2000/svg"; 
xmlns:xlink="http://www.w3.org/1999/xlink"; x="0px" y="0px"
-        viewBox="0 0 2411.2 2794" style="enable-background:new 0 0 2411.2 
2794;" xml:space="preserve">
-<style type="text/css">
-       .st0{fill:#FFFFFF;}
-       .st1{fill-rule:evenodd;clip-rule:evenodd;}
-       .st2{fill-rule:evenodd;clip-rule:evenodd;fill:#FFFFFF;}
-</style>
-<g id="Layer_2">
-</g>
-<g id="Layer_x5F_1_x5F_1">
-       <g>
-               <polygon points="1204.6,359.2 271.8,30 271.8,2060.1 
1204.6,1758.3               "/>
-               <polygon class="st0" points="1182.2,358.1 2150.6,29 2150.6,2059 
1182.2,1757.3           "/>
-               <polygon class="st0" points="30,2415.4 1182.2,2031.4 
1182.2,357.9 30,742                "/>
-               <polygon points="1707.2,2440.7 1870.5,2709.4 1956.6,2459.8      
        "/>
-               <g>
-                       <path 
d="M421.7,934.8c-6.1-6,8,49.1,27.6,68.9c34.8,35.1,61.9,39.6,76.4,40.2c32,1.3,71.5-8,94.9-17.8
-                               
c22.7-9.7,62.4-30,77.5-59.6c3.2-6.3,11.9-17,6.4-43.2c-4.2-20.2-17-27.3-32.7-26.2c-15.7,1.1-63.2,13.7-86.1,20.8
-                               
c-23,7-70.3,21.4-90.9,25.8C474.3,948.2,429,941.7,421.7,934.8z"/>
-                       <path 
d="M1003.1,1593.7c-9.1-3.3-196.9-81.1-223.6-93.9c-21.8-10.5-75.2-33.1-100.4-43.3c70.8-109.2,115.5-191.6,121.5-204.1
-                               
c11-23,86-169.6,87.7-178.7c1.7-9.1,3.8-42.9,2.2-51c-1.7-8.2-29.1,7.6-66.4,20.2c-37.4,12.6-108.4,58.8-135.8,64.6
-                               
c-27.5,5.7-115.5,39.1-160.5,54c-45,14.9-130.2,40.9-165.2,50.4c-35.1,9.5-65.7,10.2-85.3,16.2c0,0,2.6,27.5,7.8,35.7
-                               
c5.2,8.2,23.7,28.4,45.3,34.1c21.6,5.7,57.3,3.4,73.6-0.3c16.3-3.8,44.4-17.5,48.2-23.6c3.8-6.1-2-24.9,4.5-30.6
-                               
c6.5-5.6,92.2-25.7,124.6-35.4c32.4-10,156.3-52.6,173.1-50.5c-5.3,17.7-105,215.1-137.1,274c-32.1,58.9-218.6,318-258.3,363.6
-                               
c-30.1,34.7-103.2,123.5-128.5,143.6c6.4,1.8,51.6-2.1,59.9-7.2c51.3-31.6,136.9-138.1,164.4-170.5
-                               
c81.9-96,153.8-196.8,210.8-283.4h0.1c11.1,4.6,100.9,77.8,124.4,94c23.4,16.2,115.9,67.8,136,76.4c20,8.7,97.1,44.2,100.3,32.2
-                               C1029.4,1668,1012.2,1597.1,1003.1,1593.7z"/>
-               </g>
-               <path class="st1" 
d="M569,2572c18,11,35,20,54,29c38,19,81,39,122,54c56,21,112,38,168,51c31,7,65,13,98,18c3,0,92,11,110,11h90
-                       
c35-3,68-5,103-10c28-4,59-9,89-16c22-5,45-10,67-17c21-6,45-14,68-22c15-5,31-12,47-18c13-6,29-13,44-19c18-8,39-19,59-29
-                       
c16-8,34-18,51-28c13-7,43-30,59-30c18,0,30,16,30,30c0,29-39,38-57,51c-19,13-42,23-62,34c-40,21-81,39-120,54
-                       
c-51,19-107,37-157,49c-19,4-38,9-57,12c-10,2-114,18-143,18h-132c-35-3-72-7-107-12c-31-5-64-11-95-18c-24-5-50-12-73-19
-                       
c-40-11-79-25-117-40c-69-26-141-60-209-105c-12-8-13-16-13-25c0-15,11-29,29-29C531,2546,563,2569,569,2572z"/>
-               <path class="st1" d="M1151,2009L61,2372V764l1090-363V2009z 
M1212,354v1680c-1,5-3,10-7,15c-2,3-6,7-9,8c-25,10-1151,388-1166,388
-                       
c-12,0-23-8-29-21c0-1-1-2-1-4V739c2-5,3-12,7-16c8-11,22-13,31-16c17-6,1126-378,1142-378C1190,329,1212,336,1212,354z"/>
-               <path class="st1" d="M2120,2017l-907-282V380l907-308V2017z 
M2181,32v2023c-1,23-17,33-32,33c-13,0-107-32-123-37
-                       
c-126-39-253-78-378-117c-28-9-57-18-84-27c-24-7-50-15-74-23c-107-33-216-66-323-102c-4-1-14-15-14-18V351c2-5,4-11,9-15
-                       
c8-9,351-123,486-168c36-13,487-168,501-168C2167,0,2181,13,2181,32z"/>
-               <polygon points="2411.2,2440.7 1199.5,2054.5 1204.6,373.2 
2411.2,757.2          "/>
-               <g>
-                       <path class="st2" 
d="M1800.3,1124.6L1681.4,1412l218.6,66.3L1800.3,1124.6z 
M1729,853.2l156.1,47.3l284.4,1025l-160.3-48.7
-                               
l-57.6-210.4L1620.2,1566l-71.3,171.4l-160.4-48.7L1729,853.2z"/>
-               </g>
-       </g>
-</g>
-</svg>
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 19.1.0, SVG Export Plug-In . SVG Version: 
6.00 Build 0)  -->
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg"; 
xmlns:xlink="http://www.w3.org/1999/xlink"; x="0px" y="0px"
+        viewBox="0 0 2411.2 2794" style="enable-background:new 0 0 2411.2 
2794;" xml:space="preserve">
+<style type="text/css">
+       .st0{fill:#FFFFFF;}
+       .st1{fill-rule:evenodd;clip-rule:evenodd;}
+       .st2{fill-rule:evenodd;clip-rule:evenodd;fill:#FFFFFF;}
+</style>
+<g id="Layer_2">
+</g>
+<g id="Layer_x5F_1_x5F_1">
+       <g>
+               <polygon points="1204.6,359.2 271.8,30 271.8,2060.1 
1204.6,1758.3               "/>
+               <polygon class="st0" points="1182.2,358.1 2150.6,29 2150.6,2059 
1182.2,1757.3           "/>
+               <polygon class="st0" points="30,2415.4 1182.2,2031.4 
1182.2,357.9 30,742                "/>
+               <polygon points="1707.2,2440.7 1870.5,2709.4 1956.6,2459.8      
        "/>
+               <g>
+                       <path 
d="M421.7,934.8c-6.1-6,8,49.1,27.6,68.9c34.8,35.1,61.9,39.6,76.4,40.2c32,1.3,71.5-8,94.9-17.8
+                               
c22.7-9.7,62.4-30,77.5-59.6c3.2-6.3,11.9-17,6.4-43.2c-4.2-20.2-17-27.3-32.7-26.2c-15.7,1.1-63.2,13.7-86.1,20.8
+                               
c-23,7-70.3,21.4-90.9,25.8C474.3,948.2,429,941.7,421.7,934.8z"/>
+                       <path 
d="M1003.1,1593.7c-9.1-3.3-196.9-81.1-223.6-93.9c-21.8-10.5-75.2-33.1-100.4-43.3c70.8-109.2,115.5-191.6,121.5-204.1
+                               
c11-23,86-169.6,87.7-178.7c1.7-9.1,3.8-42.9,2.2-51c-1.7-8.2-29.1,7.6-66.4,20.2c-37.4,12.6-108.4,58.8-135.8,64.6
+                               
c-27.5,5.7-115.5,39.1-160.5,54c-45,14.9-130.2,40.9-165.2,50.4c-35.1,9.5-65.7,10.2-85.3,16.2c0,0,2.6,27.5,7.8,35.7
+                               
c5.2,8.2,23.7,28.4,45.3,34.1c21.6,5.7,57.3,3.4,73.6-0.3c16.3-3.8,44.4-17.5,48.2-23.6c3.8-6.1-2-24.9,4.5-30.6
+                               
c6.5-5.6,92.2-25.7,124.6-35.4c32.4-10,156.3-52.6,173.1-50.5c-5.3,17.7-105,215.1-137.1,274c-32.1,58.9-218.6,318-258.3,363.6
+                               
c-30.1,34.7-103.2,123.5-128.5,143.6c6.4,1.8,51.6-2.1,59.9-7.2c51.3-31.6,136.9-138.1,164.4-170.5
+                               
c81.9-96,153.8-196.8,210.8-283.4h0.1c11.1,4.6,100.9,77.8,124.4,94c23.4,16.2,115.9,67.8,136,76.4c20,8.7,97.1,44.2,100.3,32.2
+                               C1029.4,1668,1012.2,1597.1,1003.1,1593.7z"/>
+               </g>
+               <path class="st1" 
d="M569,2572c18,11,35,20,54,29c38,19,81,39,122,54c56,21,112,38,168,51c31,7,65,13,98,18c3,0,92,11,110,11h90
+                       
c35-3,68-5,103-10c28-4,59-9,89-16c22-5,45-10,67-17c21-6,45-14,68-22c15-5,31-12,47-18c13-6,29-13,44-19c18-8,39-19,59-29
+                       
c16-8,34-18,51-28c13-7,43-30,59-30c18,0,30,16,30,30c0,29-39,38-57,51c-19,13-42,23-62,34c-40,21-81,39-120,54
+                       
c-51,19-107,37-157,49c-19,4-38,9-57,12c-10,2-114,18-143,18h-132c-35-3-72-7-107-12c-31-5-64-11-95-18c-24-5-50-12-73-19
+                       
c-40-11-79-25-117-40c-69-26-141-60-209-105c-12-8-13-16-13-25c0-15,11-29,29-29C531,2546,563,2569,569,2572z"/>
+               <path class="st1" d="M1151,2009L61,2372V764l1090-363V2009z 
M1212,354v1680c-1,5-3,10-7,15c-2,3-6,7-9,8c-25,10-1151,388-1166,388
+                       
c-12,0-23-8-29-21c0-1-1-2-1-4V739c2-5,3-12,7-16c8-11,22-13,31-16c17-6,1126-378,1142-378C1190,329,1212,336,1212,354z"/>
+               <path class="st1" d="M2120,2017l-907-282V380l907-308V2017z 
M2181,32v2023c-1,23-17,33-32,33c-13,0-107-32-123-37
+                       
c-126-39-253-78-378-117c-28-9-57-18-84-27c-24-7-50-15-74-23c-107-33-216-66-323-102c-4-1-14-15-14-18V351c2-5,4-11,9-15
+                       
c8-9,351-123,486-168c36-13,487-168,501-168C2167,0,2181,13,2181,32z"/>
+               <polygon points="2411.2,2440.7 1199.5,2054.5 1204.6,373.2 
2411.2,757.2          "/>
+               <g>
+                       <path class="st2" 
d="M1800.3,1124.6L1681.4,1412l218.6,66.3L1800.3,1124.6z 
M1729,853.2l156.1,47.3l284.4,1025l-160.3-48.7
+                               
l-57.6-210.4L1620.2,1566l-71.3,171.4l-160.4-48.7L1729,853.2z"/>
+               </g>
+       </g>
+</g>
+</svg>
diff --git a/packages/demobank-ui/src/components/Cashouts/views.tsx 
b/packages/demobank-ui/src/components/Cashouts/views.tsx
index 0602f507e..32fe0aa9e 100644
--- a/packages/demobank-ui/src/components/Cashouts/views.tsx
+++ b/packages/demobank-ui/src/components/Cashouts/views.tsx
@@ -14,7 +14,7 @@
  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";
@@ -33,55 +33,118 @@ export function LoadingUriView({ error }: 
State.LoadingUriError): VNode {
 
 export function ReadyView({ cashouts, onSelected }: State.Ready): VNode {
   const { i18n } = useTranslationContext();
-  if (!cashouts.length) {
-    return (
-      <div>
-        <i18n.Translate>No cashout at the moment</i18n.Translate>
-      </div>
-    );
-  }
+  if (!cashouts.length) return <div />
+  const txByDate = cashouts.reduce((prev, cur) => {
+    const d = cur.creation_time.t_s === "never"
+      ? ""
+      : format(cur.creation_time.t_s * 1000, "dd/MM/yyyy")
+    if (!prev[d]) {
+      prev[d] = []
+    }
+    prev[d].push(cur)
+    return prev
+  }, {} as Record<string, typeof cashouts>)
   return (
-    <div class="results">
-      <table class="pure-table pure-table-striped">
-        <thead>
-          <tr>
-            <th>{i18n.str`Created`}</th>
-            <th>{i18n.str`Confirmed`}</th>
-            <th>{i18n.str`Total debit`}</th>
-            <th>{i18n.str`Total credit`}</th>
-            <th>{i18n.str`Status`}</th>
-            <th>{i18n.str`Subject`}</th>
-          </tr>
-        </thead>
-        <tbody>
-          {cashouts.map((item, idx) => {
-            return (
-              <tr key={idx}>
-                <td>{item.creation_time.t_s === "never" ? i18n.str`never` : 
format(item.creation_time.t_s, "dd/MM/yyyy HH:mm:ss")}</td>
-                <td>
-                  {item.confirmation_time
+    <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 cashouts</i18n.Translate></h1>
+        </div>
+      </div>
+      <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>
+              <th scope="col" class="                     pl-2 py-3.5 
text-left text-sm font-semibold text-gray-900">{i18n.str`Created`}</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`Confirmed`}</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`Total debit`}</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`Total credit`}</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`Status`}</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`Subject`}</th>
+            </tr>
+          </thead>
+          <tbody>
+            {Object.entries(txByDate).map(([date, txs], idx) => {
+              return <Fragment key={idx}>
+                <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 => {
+                  const creationTime = item.creation_time.t_s === "never" ? "" 
: format(item.creation_time.t_s * 1000, "HH:mm:ss")
+                  const confirmationTime = item.confirmation_time
                     ? item.confirmation_time.t_s === "never" ? i18n.str`never` 
: format(item.confirmation_time.t_s, "dd/MM/yyyy HH:mm:ss")
-                    : "-"}
-                </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
-                    href="#"
-                    onClick={(e) => {
-                      e.preventDefault();
-                      onSelected(item.id);
-                    }}
-                  >
-                    {item.subject}
-                  </a>
-                </td>
-              </tr>
-            );
-          })}
-        </tbody>
-      </table>
+                    : "-"
+                  return (<tr key={idx} class="border-b border-gray-200 
last:border-none">
+
+                    <td class="relative py-2 pl-2 pr-2 text-sm ">
+                      <div class="font-medium 
text-gray-900">{creationTime}</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 ? (
+                            <span data-negative={item.negative ? "true" : 
"false"} class="data-[negative=false]:text-green-600 
data-[negative=true]:text-red-600">
+                              <RenderAmount value={item.amount} />
+                            </span>
+                          ) : (
+                            <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>
+                        <dd class="mt-1 text-gray-500 sm:hidden" >
+                          <pre class="break-words w-56 whitespace-break-spaces 
p-2 rounded-md mx-auto my-2 bg-gray-100">
+                            {item.subject}
+                          </pre>
+                        </dd>
+                      </dl> */}
+                    </td>
+                    <td class="hidden sm:table-cell px-3 py-3.5 text-sm 
text-gray-500">{confirmationTime}</td>
+                    <td class="hidden sm:table-cell px-3 py-3.5 text-sm 
text-red-600"><RenderAmount value={Amounts.parseOrThrow(item.amount_debit)} 
/></td>
+                    <td class="hidden sm:table-cell px-3 py-3.5 text-sm 
text-green-600"><RenderAmount value={Amounts.parseOrThrow(item.amount_credit)} 
/></td>
+
+                    <td class="hidden sm:table-cell px-3 py-3.5 text-sm 
text-gray-500">{item.status}</td>
+                    <td class="hidden sm:table-cell px-3 py-3.5 text-sm 
text-gray-500 break-all min-w-md">
+                      <a href="#" onClick={(e) => {
+                        e.preventDefault();
+                        onSelected(item.id);
+                      }}>
+                        {item.subject}
+                      </a>
+                    </td>
+                  </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/components/CopyButton.tsx 
b/packages/demobank-ui/src/components/CopyButton.tsx
index b36de770e..ca1ceaa8a 100644
--- a/packages/demobank-ui/src/components/CopyButton.tsx
+++ b/packages/demobank-ui/src/components/CopyButton.tsx
@@ -5,31 +5,21 @@ import { useEffect, useState } from "preact/hooks";
 
 export function CopyIcon(): VNode {
   return (
-    <svg height="16" viewBox="0 0 16 16" width="16" stroke="currentColor" 
strokeWidth="1.5">
-      <path
-        fill-rule="evenodd"
-        d="M0 6.75C0 5.784.784 5 1.75 5h1.5a.75.75 0 010 1.5h-1.5a.25.25 0 
00-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 00.25-.25v-1.5a.75.75 0 011.5 
0v1.5A1.75 1.75 0 019.25 16h-7.5A1.75 1.75 0 010 14.25v-7.5z"
-      />
-      <path
-        fill-rule="evenodd"
-        d="M5 1.75C5 .784 5.784 0 6.75 0h7.5C15.216 0 16 .784 16 1.75v7.5A1.75 
1.75 0 0114.25 11h-7.5A1.75 1.75 0 015 9.25v-7.5zm1.75-.25a.25.25 0 
00-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 00.25-.25v-7.5a.25.25 0 
00-.25-.25h-7.5z"
-      />
+    <svg xmlns="http://www.w3.org/2000/svg"; fill="none" viewBox="0 0 24 24" 
stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
+      <path stroke-linecap="round" stroke-linejoin="round" d="M15.75 
17.25v3.375c0 .621-.504 1.125-1.125 1.125h-9.75a1.125 1.125 0 
01-1.125-1.125V7.875c0-.621.504-1.125 1.125-1.125H6.75a9.06 9.06 0 
011.5.124m7.5 10.376h3.375c.621 0 1.125-.504 
1.125-1.125V11.25c0-4.46-3.243-8.161-7.5-8.876a9.06 9.06 0 
00-1.5-.124H9.375c-.621 0-1.125.504-1.125 1.125v3.5m7.5 10.375H9.375a1.125 
1.125 0 01-1.125-1.125v-9.25m12 6.625v-1.875a3.375 3.375 0 
00-3.375-3.375h-1.5a1.125 1.125 0 01-1.125-1.125v-1.5a3. [...]
     </svg>
   )
 };
 
 export function CopiedIcon(): VNode {
   return (
-    <svg height="16" viewBox="0 0 16 16" width="16" stroke="currentColor" 
strokeWidth="1.5">
-      <path
-        fill-rule="evenodd"
-        d="M13.78 4.22a.75.75 0 010 1.06l-7.25 7.25a.75.75 0 01-1.06 0L2.22 
9.28a.75.75 0 011.06-1.06L6 10.94l6.72-6.72a.75.75 0 011.06 0z"
-      />
+    <svg xmlns="http://www.w3.org/2000/svg"; fill="none" viewBox="0 0 24 24" 
stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
+      <path stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 
9-13.5" />
     </svg>
   )
 };
 
-export function CopyButton({ getContent }: { getContent: () => string }): 
VNode {
+export function CopyButton({ class: clazz, getContent }: { class: string, 
getContent: () => string }): VNode {
   const [copied, setCopied] = useState(false);
   function copyText(): void {
     navigator.clipboard.writeText(getContent() || "");
@@ -45,16 +35,14 @@ export function CopyButton({ getContent }: { getContent: () 
=> string }): VNode
 
   if (!copied) {
     return (
-      <button class="text-white" onClick={copyText} style={{ width: 16, 
height: 16, fontSize: "initial" }}>
+      <button class={clazz} onClick={copyText} >
         <CopyIcon />
       </button>
     );
   }
   return (
-    <div class="text-white" content="Copied" style={{ display: "inline-block" 
}}>
-      <button disabled style={{ width: 16, height: 16, fontSize: "initial" }}>
-        <CopiedIcon />
-      </button>
-    </div>
+    <button class={clazz} disabled>
+      <CopiedIcon />
+    </button>
   );
-}
\ No newline at end of file
+}
diff --git a/packages/demobank-ui/src/components/LangSelector.tsx 
b/packages/demobank-ui/src/components/LangSelector.tsx
index c1d0f64ef..7cf0300df 100644
--- a/packages/demobank-ui/src/components/LangSelector.tsx
+++ b/packages/demobank-ui/src/components/LangSelector.tsx
@@ -23,6 +23,7 @@ import { Fragment, h, VNode } from "preact";
 import { useEffect, useState } from "preact/hooks";
 import { useTranslationContext } from "@gnu-taler/web-util/browser";
 import { strings as messages } from "../i18n/strings.js";
+import langIcon from "../assets/lang.svg";
 
 type LangsNames = {
   [P in keyof typeof messages]: string;
@@ -69,7 +70,7 @@ export function LangSelector(): VNode {
             setHidden((h) => !h);
           }}>
           <span class="flex items-center">
-            <img src="https://taler.net/images/languageicon.svg"; alt="" 
class="h-5 w-5 flex-shrink-0 rounded-full" />
+            <img src={langIcon} alt="" class="h-5 w-5 flex-shrink-0 
rounded-full" />
             <span class="ml-3 block truncate">{getLangName(lang)}</span>
           </span>
           <span class="pointer-events-none absolute inset-y-0 right-0 flex 
items-center pr-2">
diff --git a/packages/demobank-ui/src/components/Routing.tsx 
b/packages/demobank-ui/src/components/Routing.tsx
index 04cf96190..1d587fe32 100644
--- a/packages/demobank-ui/src/components/Routing.tsx
+++ b/packages/demobank-ui/src/components/Routing.tsx
@@ -19,19 +19,26 @@ import { createHashHistory } from "history";
 import { Fragment, VNode, h } from "preact";
 import { Route, Router, route } from "preact-router";
 import { useEffect } from "preact/hooks";
-import { useBackendContext } from "../context/backend.js";
+import { useBackendState } from "../hooks/backend.js";
 import { BankFrame } from "../pages/BankFrame.js";
 import { HomePage, WithdrawalOperationPage } from "../pages/HomePage.js";
 import { LoginForm } from "../pages/LoginForm.js";
 import { PublicHistoriesPage } from "../pages/PublicHistoriesPage.js";
 import { RegistrationPage } from "../pages/RegistrationPage.js";
-import { AdminHome } from "../pages/admin/Home.js";
-import { BusinessAccount } from "../pages/business/Home.js";
+import { AdminHome } from "../pages/admin/AdminHome.js";
+import { CreateCashout } from "../pages/business/CreateCashout.js";
 import { bankUiSettings } from "../settings.js";
+import { ShowAccountDetails } from "../pages/ShowAccountDetails.js";
+import { UpdateAccountPassword } from "../pages/UpdateAccountPassword.js";
+import { RemoveAccount } from "../pages/admin/RemoveAccount.js";
+import { CreateNewAccount } from "../pages/admin/CreateNewAccount.js";
+import { CashoutListForAccount } from 
"../pages/admin/CashoutListForAccount.js";
+import { ShowCashoutDetails } from "../pages/business/ShowCashoutDetails.js";
+import { WireTransfer } from "../pages/admin/Account.js";
 
 export function Routing(): VNode {
   const history = createHashHistory();
-  const backend = useBackendContext();
+  const backend = useBackendState();
   const { i18n } = useTranslationContext();
 
   if (backend.state.status === "loggedOut") {
@@ -90,7 +97,7 @@ export function Routing(): VNode {
   const { isUserAdministrator, username } = backend.state
 
   return (
-    <BankFrame account={backend.state.username}>
+    <BankFrame account={username}>
       <Router history={history}>
         <Route
           path="/operation/:wopid"
@@ -107,6 +114,167 @@ export function Routing(): VNode {
           path="/public-accounts"
           component={() => <PublicHistoriesPage />}
         />
+
+        <Route
+          path="/new-account"
+          component={() => <CreateNewAccount
+            onCancel={() => {
+              route("/account")
+            }}
+            onCreateSuccess={() => {
+              route("/account")
+            }}
+          />}
+        />
+
+        <Route
+          path="/profile/:account/details"
+          component={({ account }: { account: string }) => (
+            <ShowAccountDetails
+              account={account}
+              onUpdateSuccess={() => {
+                route("/account")
+              }}
+              onClear={() => {
+                route("/account")
+              }}
+            />
+          )}
+        />
+
+        <Route
+          path="/profile/:account/change-password"
+          component={({ account }: { account: string }) => (
+            <UpdateAccountPassword
+              focus
+              account={account}
+              onUpdateSuccess={() => {
+                route("/account")
+              }}
+              onCancel={() => {
+                route("/account")
+              }}
+            />
+          )}
+        />
+        <Route
+          path="/profile/:account/delete"
+          component={({ account }: { account: string }) => (
+            <RemoveAccount
+              account={account}
+              onUpdateSuccess={() => {
+                route("/account")
+              }}
+              onCancel={() => {
+                route("/account")
+              }}
+            />
+          )}
+        />
+
+        <Route
+          path="/profile/:account/cashouts"
+          component={({ account }: { account: string }) => (
+            <CashoutListForAccount
+              account={account}
+              onSelected={(cid) => {
+                route(`/cashout/${cid}`)
+              }}
+              onClose={() => {
+                route("/account")
+              }}
+            />
+          )}
+        />
+
+        <Route
+          path="/my-profile"
+          component={() => (
+            <ShowAccountDetails
+              account={username}
+              onUpdateSuccess={() => {
+                route("/account")
+              }}
+              onClear={() => {
+                route("/account")
+              }}
+            />
+          )}
+        />
+        <Route
+          path="/my-password"
+          component={() => (
+            <UpdateAccountPassword
+              focus
+              account={username}
+              onUpdateSuccess={() => {
+                route("/account")
+              }}
+              onCancel={() => {
+                route("/account")
+              }}
+            />
+          )}
+        />
+
+        <Route
+          path="/my-cashouts"
+          component={() => (
+            <CashoutListForAccount
+              account={username}
+              onSelected={(cid) => {
+                route(`/cashout/${cid}`)
+              }}
+              onClose={() => {
+                route("/account");
+              }}
+            />
+          )}
+        />
+
+        <Route
+          path="/new-cashout"
+          component={() => (
+            <CreateCashout
+              account={username}
+              onComplete={(cid) => {
+                route(`/cashout/${cid}`);
+              }}
+              onCancel={() => {
+                route("/account");
+              }}
+            />
+          )}
+        />
+
+        <Route
+          path="/cashout/:cid"
+          component={({ cid }: { cid: string }) => (
+            <ShowCashoutDetails
+              id={cid}
+              onCancel={() => {
+                route("/account");
+              }}
+            />
+          )}
+        />
+
+
+        <Route
+          path="/wire-transfer/:dest"
+          component={({ dest }: { dest: string }) => (
+            <WireTransfer
+              toAccount={dest}
+              onCancel={() => {
+                route("/account")
+              }}
+              onSuccess={() => {
+                route("/account")
+              }}
+            />
+          )}
+        />
+
         <Route
           path="/account"
           component={() => {
@@ -115,6 +283,22 @@ export function Routing(): VNode {
                 onRegister={() => {
                   route("/register");
                 }}
+                onCreateAccount={() => {
+                  route("/new-account")
+                }}
+                onShowAccountDetails={(aid) => {
+                  route(`/profile/${aid}/details`)
+                }}
+                onRemoveAccount={(aid) => {
+                  route(`/profile/${aid}/delete`)
+                }}
+                onShowCashoutForAccount={(aid) => {
+                  route(`/profile/${aid}/cashouts`)
+                }}
+                onUpdateAccountPassword={(aid) => {
+                  route(`/profile/${aid}/change-password`)
+
+                }}
               />;
             } else {
               return <HomePage
@@ -132,20 +316,6 @@ export function Routing(): VNode {
             }
           }}
         />
-        <Route
-          path="/business"
-          component={() => (
-            <BusinessAccount
-              account={username}
-              onClose={() => {
-                route("/account");
-              }}
-              onRegister={() => {
-                route("/register");
-              }}
-            />
-          )}
-        />
         <Route default component={Redirect} to="/account" />
       </Router>
     </BankFrame>
diff --git a/packages/demobank-ui/src/components/Transactions/views.tsx 
b/packages/demobank-ui/src/components/Transactions/views.tsx
index 5cdb47a0c..47daf8963 100644
--- a/packages/demobank-ui/src/components/Transactions/views.tsx
+++ b/packages/demobank-ui/src/components/Transactions/views.tsx
@@ -86,11 +86,13 @@ export function ReadyView({ transactions, onNext, onPrev }: 
State.Ready): VNode
 
                         <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`} <a 
href={`#/wire-transfer/${item.counterpart}`} class="text-indigo-600 
hover:text-indigo-900">
+                            {item.counterpart}
+                          </a>
                         </dd>
                         <dd class="mt-1 text-gray-500 sm:hidden" >
                           <pre class="break-words w-56 whitespace-break-spaces 
p-2 rounded-md mx-auto my-2 bg-gray-100">
-                          {item.subject}
+                            {item.subject}
                           </pre>
                         </dd>
                       </dl>
@@ -102,7 +104,11 @@ export function ReadyView({ transactions, onNext, onPrev 
}: State.Ready): VNode
                         <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="hidden sm:table-cell px-3 py-3.5 text-sm 
text-gray-500">
+                      <a href={`#/wire-transfer/${item.counterpart}`} 
class="text-indigo-600 hover:text-indigo-900">
+                        {item.counterpart}
+                      </a>
+                    </td>
                     <td class="hidden sm:table-cell 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/components/app.tsx 
b/packages/demobank-ui/src/components/app.tsx
index beb24da57..55e1178fe 100644
--- a/packages/demobank-ui/src/components/app.tsx
+++ b/packages/demobank-ui/src/components/app.tsx
@@ -27,6 +27,7 @@ import { BankCoreApiProvider } from "../context/config.js";
 import { strings } from "../i18n/strings.js";
 import { bankUiSettings } from "../settings.js";
 import { Routing } from "./Routing.js";
+import { BankFrame } from "../pages/BankFrame.js";
 const WITH_LOCAL_STORAGE_CACHE = false;
 
 const App: FunctionalComponent = () => {
@@ -34,7 +35,7 @@ const App: FunctionalComponent = () => {
   return (
     <TranslationProvider source={strings}>
       <BackendStateProvider>
-        <BankCoreApiProvider baseUrl={baseUrl}>
+        <BankCoreApiProvider baseUrl={baseUrl} frameOnError={BankFrame}>
           <SWRConfig
             value={{
               provider: WITH_LOCAL_STORAGE_CACHE
diff --git a/packages/demobank-ui/src/context/config.ts 
b/packages/demobank-ui/src/context/config.ts
index 013d8922e..a31d914b8 100644
--- a/packages/demobank-ui/src/context/config.ts
+++ b/packages/demobank-ui/src/context/config.ts
@@ -16,7 +16,7 @@
 
 import { TalerCorebankApi, TalerCoreBankHttpClient, TalerError } from 
"@gnu-taler/taler-util";
 import { BrowserHttpLib, useTranslationContext } from 
"@gnu-taler/web-util/browser";
-import { ComponentChildren, createContext, h, VNode } from "preact";
+import { ComponentChildren, createContext, FunctionComponent, h, VNode } from 
"preact";
 import { useContext, useEffect, useState } from "preact/hooks";
 import { ErrorLoading } from "../components/ErrorLoading.js";
 
@@ -43,9 +43,11 @@ export type ConfigResult = undefined
 export const BankCoreApiProvider = ({
   baseUrl,
   children,
+  frameOnError,
 }: {
   baseUrl: string,
   children: ComponentChildren;
+  frameOnError: FunctionComponent<{ children: ComponentChildren }>,
 }): VNode => {
   const [checked, setChecked] = useState<ConfigResult>()
   const { i18n } = useTranslationContext();
@@ -68,13 +70,13 @@ export const BankCoreApiProvider = ({
   }, []);
 
   if (checked === undefined) {
-    return h("div", {}, "loading...")
+    return h(frameOnError, { children: h("div", {}, "loading...") })
   }
   if (checked.type === "error") {
-    return h(ErrorLoading, { error: checked.error, showDetail: true })
+    return h(frameOnError, { children: h(ErrorLoading, { error: checked.error, 
showDetail: true }) })
   }
   if (checked.type === "incompatible") {
-    return h("div", {}, i18n.str`the bank backend is not supported. supported 
version "${checked.supported}", server version "${checked.result.version}"`)
+    return h(frameOnError, { children: h("div", {}, i18n.str`the bank backend 
is not supported. supported version "${checked.supported}", server version 
"${checked.result.version}"`) })
   }
   const value: Type = {
     url, config: checked.config, api
diff --git a/packages/demobank-ui/src/hooks/access.ts 
b/packages/demobank-ui/src/hooks/access.ts
index 7023b8803..da9812ffa 100644
--- a/packages/demobank-ui/src/hooks/access.ts
+++ b/packages/demobank-ui/src/hooks/access.ts
@@ -61,7 +61,7 @@ export function useWithdrawalDetails(wid: string) {
   // const { state: credentials } = useBackendState();
   const { api } = useBankCoreApiContext();
 
-  async function fetcher(wid: string) {
+  async function fetcher([wid]: [string]) {
     return await api.getWithdrawalById(wid)
   }
 
@@ -114,7 +114,7 @@ export function usePublicAccounts(initial?: number) {
   const [offset, setOffset] = useState<number | undefined>(initial);
   const { api } = useBankCoreApiContext();
 
-  async function fetcher(txid: number | undefined) {
+  async function fetcher([txid]: [number | undefined]) {
     return await api.getPublicAccounts({
       limit: MAX_RESULT_SIZE,
       offset: txid ? String(txid) : undefined,
@@ -124,16 +124,16 @@ export function usePublicAccounts(initial?: number) {
 
   const { data, error } = 
useSWR<TalerCoreBankResultByMethod<"getPublicAccounts">, TalerHttpError>(
     [offset, "getPublicAccounts"], fetcher, {
-      refreshInterval: 0,
-      refreshWhenHidden: false,
-      revalidateOnFocus: false,
-      revalidateOnReconnect: false,
-      refreshWhenOffline: false,
-      errorRetryCount: 0,
-      errorRetryInterval: 1,
-      shouldRetryOnError: false,
-      keepPreviousData: true,  
-    });
+    refreshInterval: 0,
+    refreshWhenHidden: false,
+    revalidateOnFocus: false,
+    revalidateOnReconnect: false,
+    refreshWhenOffline: false,
+    errorRetryCount: 0,
+    errorRetryInterval: 1,
+    shouldRetryOnError: false,
+    keepPreviousData: true,
+  });
 
   const isLastPage =
     data && data.body.public_accounts.length < PAGE_SIZE;
diff --git a/packages/demobank-ui/src/hooks/circuit.ts 
b/packages/demobank-ui/src/hooks/circuit.ts
index 0f7af5fe5..06e068d6d 100644
--- a/packages/demobank-ui/src/hooks/circuit.ts
+++ b/packages/demobank-ui/src/hooks/circuit.ts
@@ -15,16 +15,15 @@
  */
 
 import { useState } from "preact/hooks";
-import { useBackendContext } from "../context/backend.js";
 import { MAX_RESULT_SIZE, PAGE_SIZE } from "../utils.js";
 import { useBackendState } from "./backend.js";
 
-// FIX default import https://github.com/microsoft/TypeScript/issues/49189
 import { AccessToken, AmountJson, Amounts, OperationOk, 
TalerCoreBankResultByMethod, TalerCorebankApi, TalerError, TalerHttpError } 
from "@gnu-taler/taler-util";
 import _useSWR, { SWRHook } from "swr";
 import { useBankCoreApiContext } from "../context/config.js";
 import { assertUnreachable } from "../pages/HomePage.js";
 
+// FIX default import https://github.com/microsoft/TypeScript/issues/49189
 const useSWR = _useSWR as unknown as SWRHook;
 
 export type TransferCalculation = {
@@ -44,12 +43,8 @@ type CashoutEstimators = {
 };
 
 export function useEstimator(): CashoutEstimators {
-  const { state } = useBackendContext();
+  const { state } = useBackendState();
   const { api } = useBankCoreApiContext();
-  const creds =
-    state.status !== "loggedIn"
-      ? undefined
-      : state.token;
   return {
     estimateByCredit: async (amount, fee, rate) => {
       const resp = await api.getCashoutRate({
@@ -101,13 +96,14 @@ export function useEstimator(): CashoutEstimators {
 }
 
 export function useRatiosAndFeeConfig() {
-  const { api } = useBankCoreApiContext();
+  const { api, config } = useBankCoreApiContext();
+
   function fetcher() {
     return api.getConversionRates()
   }
 
   const { data, error } = 
useSWR<TalerCoreBankResultByMethod<"getConversionRates">, TalerHttpError>(
-    [, "getConversionRates"], fetcher, {
+    !config.have_cashout || !config.fiat_currency ? false : [, 
"getConversionRates"], fetcher, {
     refreshInterval: 60 * 1000,
     refreshWhenHidden: false,
     revalidateOnFocus: false,
diff --git a/packages/demobank-ui/src/pages.ts 
b/packages/demobank-ui/src/pages.ts
new file mode 100644
index 000000000..c78240a02
--- /dev/null
+++ b/packages/demobank-ui/src/pages.ts
@@ -0,0 +1,44 @@
+import { WithdrawalOperationPage } from "./pages/HomePage.js";
+import { PageEntry, pageDefinition } from "./route.js";
+
+// const operationById: PageEntry<{ operationId: string }> = {
+//   url: pageDefinition("#/operation/:operationId"),
+//   view: WithdrawalOperationPage,
+// };
+
+
+// const home: PageEntry = {
+//   url: "#/",
+//   view: Home,
+// };
+// const cases: PageEntry = {
+//   url: "#/cases",
+//   view: Cases,
+// };
+
+// const newFormEntry: PageEntry<{ account?: string; type?: string }> = {
+//   url: pageDefinition("#/account/:account/new/:type?"),
+//   view: NewFormEntry,
+// };
+
+// const settings: PageEntry = {
+//   url: "#/settings",
+//   view: Settings,
+// };
+// const officer: PageEntry = {
+//   url: "#/officer",
+//   view: Officer,
+// };
+// const welcome: PageEntry<{ asd?: string; name?: string }> = {
+//   url: pageDefinition("#/welcome/:name?"),
+//   view: Welcome,
+// };
+// const form: PageEntry<{ number?: string }> = {
+//   url: pageDefinition("#/form/:number?"),
+//   view: AntiMoneyLaunderingForm,
+// };
+
+export const Pages = {
+  // operationById,
+
+};
diff --git a/packages/demobank-ui/src/pages/AccountPage/views.tsx 
b/packages/demobank-ui/src/pages/AccountPage/views.tsx
index 0604001e3..00643ec3e 100644
--- a/packages/demobank-ui/src/pages/AccountPage/views.tsx
+++ b/packages/demobank-ui/src/pages/AccountPage/views.tsx
@@ -54,40 +54,11 @@ function ShowDemoInfo(): VNode {
 }
 
 export function ReadyView({ account, limit, goToBusinessAccount, 
goToConfirmOperation }: State.Ready): VNode<{}> {
-  const { i18n } = useTranslationContext();
 
   return <Fragment>
-    <MaybeBusinessButton account={account} onClick={goToBusinessAccount} />
-
     <ShowDemoInfo />
-
     <PaymentOptions limit={limit} goToConfirmOperation={goToConfirmOperation} 
/>
     <Transactions account={account} />
   </Fragment>;
 }
 
-function MaybeBusinessButton({
-  account,
-  onClick,
-}: {
-  account: string;
-  onClick: () => void;
-}): VNode {
-  const { i18n } = useTranslationContext();
-  return <Fragment />
-  // const result = useBusinessAccountDetails(account);
-  // if (!result.ok) return <Fragment />;
-  // return (
-  //   <div class="w-full flex justify-end">
-  //     <button
-  //       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"
-  //       onClick={(e) => {
-  //         e.preventDefault()
-  //         onClick()
-  //       }}
-  //     >
-  //       <i18n.Translate>Business Profile</i18n.Translate>
-  //     </button>
-  //   </div>
-  // );
-}
diff --git a/packages/demobank-ui/src/pages/BankFrame.tsx 
b/packages/demobank-ui/src/pages/BankFrame.tsx
index 96ce9c317..c0babd0c9 100644
--- a/packages/demobank-ui/src/pages/BankFrame.tsx
+++ b/packages/demobank-ui/src/pages/BankFrame.tsx
@@ -23,8 +23,8 @@ import { Attention } from "../components/Attention.js";
 import { CopyButton } from "../components/CopyButton.js";
 import { LangSelector } from "../components/LangSelector.js";
 import { Loading } from "../components/Loading.js";
-import { useBackendContext } from "../context/backend.js";
 import { useAccountDetails } from "../hooks/access.js";
+import { useBackendState } from "../hooks/backend.js";
 import { getAllBooleanSettings, getLabelForSetting, useSettings } from 
"../hooks/settings.js";
 import { bankUiSettings } from "../settings.js";
 import { RenderAmount } from "./PaytoWireTransferForm.js";
@@ -49,7 +49,7 @@ export function BankFrame({
   children: ComponentChildren;
 }): VNode {
   const { i18n } = useTranslationContext();
-  const backend = useBackendContext();
+  const backend = useBackendState();
   const [settings, updateSettings] = useSettings();
   const [open, setOpen] = useState(false)
 
@@ -80,9 +80,9 @@ export function BankFrame({
   return (<div class="min-h-full flex flex-col m-0" style="min-height: 100vh;">
     <div class="bg-indigo-600 pb-32">
       <nav class="">
-        <div class="mx-auto max-w-7xl px-2 sm:px-4 lg:px-8">
+        <div class="mx-auto max-w-7xl px-2 ">
           <div class="relative flex h-16 items-center justify-between ">
-            <div class="flex items-center px-2 lg:px-0">
+            <div class="flex items-center px-2">
               <div class="flex-shrink-0 bg-white rounded-lg">
                 <a href={bankUiSettings.iconLinkURL ?? "#"}>
                   <img
@@ -94,7 +94,7 @@ export function BankFrame({
                 </a>
               </div>
               {bankUiSettings.demoSites &&
-                <div class="hidden sm:block lg:ml-10 ">
+                <div class="hidden sm:block ml-6 ">
                   <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]) => {
@@ -161,41 +161,26 @@ export function BankFrame({
                       <div class="relative mt-6 flex-1 px-4 sm:px-6">
                         <nav class="flex flex-1 flex-col" aria-label="Sidebar">
                           <ul role="list" class="flex flex-1 flex-col gap-y-7">
-                            <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>
-                                <i18n.Translate>Log out</i18n.Translate>
-                              </a>
-                            </li>
+                            {backend.state.status === "loggedIn" ?
+                              <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>
+                                  <i18n.Translate>Log out</i18n.Translate>
+                                </a>
+                              </li>
+                              : undefined}
                             <li>
                               <LangSelector />
                             </li>
-                            {bankUiSettings.demoSites &&
-                              <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="space-y-1">
-                                  {bankUiSettings.demoSites.map(([name, url]) 
=> {
-                                    return <li>
-                                      <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>
-                                    </li>
-                                  })}
-                                </ul>
-                              </li>
-                            }
                             <li>
                               <div class="text-xs font-semibold leading-6 
text-gray-400">
                                 <i18n.Translate>Preferences</i18n.Translate>
@@ -220,6 +205,23 @@ export function BankFrame({
                                 })}
                               </ul>
                             </li>
+                            {bankUiSettings.demoSites &&
+                              <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="space-y-1">
+                                  {bankUiSettings.demoSites.map(([name, url]) 
=> {
+                                    return <li>
+                                      <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>
+                                    </li>
+                                  })}
+                                </ul>
+                              </li>
+                            }
                           </ul>
                         </nav>
                       </div>
@@ -342,28 +344,30 @@ function Footer() {
 
 function WelcomeAccount({ account: accountName }: { account: string }): VNode {
   const { i18n } = useTranslationContext();
-
-  const result = useAccountDetails(accountName);
-  if (!result) {
-    return <Loading />
-  }
-  if (result instanceof TalerError) {
-    return <div />
-  }
-
-  const payto = result.type === "fail" ? undefined : 
parsePaytoUri(result.body.payto_uri)
-  const info = !payto || !payto.isKnown ? undefined
-    : payto.targetType === "iban" ? { account: payto.iban, uri: 
stringifyPaytoUri(payto) }
-      : payto.targetType === "x-taler-bank" ? { account: payto.account, uri: 
stringifyPaytoUri(payto) }
-        : undefined;
-
-  return <i18n.Translate>
-    Welcome,  <span class="whitespace-nowrap">{accountName}</span> {info !== 
undefined ?
-      <small class="whitespace-nowrap">
-        (<a href={info.uri}>{info.account}</a> <CopyButton getContent={() => 
info.uri} />)
-      </small>
-      : <Fragment />}!
-  </i18n.Translate>
+  return <a href="#/my-profile" class="underline underline-offset-2">
+    <i18n.Translate>Welcome,  <span 
class="whitespace-nowrap">{accountName}</span></i18n.Translate>
+  </a>
+  // const result = useAccountDetails(accountName);
+  // if (!result) {
+  //   return <Loading />
+  // }
+  // if (result instanceof TalerError) {
+  //   return <div />
+  // }
+
+  // const payto = result.type === "fail" ? undefined : 
parsePaytoUri(result.body.payto_uri)
+  // const info = !payto || !payto.isKnown ? undefined
+  //   : payto.targetType === "iban" ? { account: payto.iban, uri: 
stringifyPaytoUri(payto) }
+  //     : payto.targetType === "x-taler-bank" ? { account: payto.account, 
uri: stringifyPaytoUri(payto) }
+  //       : undefined;
+
+  // return <i18n.Translate>
+  //   Welcome,  <span class="whitespace-nowrap">{accountName}</span> {info 
!== undefined ?
+  //     <small class="whitespace-nowrap">
+  //       (<a href={info.uri}>{info.account}</a> <CopyButton getContent={() 
=> info.uri} />)
+  //     </small>
+  //     : <Fragment />}!
+  // </i18n.Translate>
 
 }
 
diff --git a/packages/demobank-ui/src/pages/LoginForm.tsx 
b/packages/demobank-ui/src/pages/LoginForm.tsx
index 981b0f880..6d1d35288 100644
--- a/packages/demobank-ui/src/pages/LoginForm.tsx
+++ b/packages/demobank-ui/src/pages/LoginForm.tsx
@@ -14,24 +14,24 @@
  GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 
-import { HttpStatusCode, TalerAuthentication, TranslatedString } from 
"@gnu-taler/taler-util";
-import { ErrorType, notify, notifyError, useTranslationContext } from 
"@gnu-taler/web-util/browser";
+import { TranslatedString } from "@gnu-taler/taler-util";
+import { notify, useTranslationContext } from "@gnu-taler/web-util/browser";
 import { Fragment, VNode, h } from "preact";
 import { useEffect, useRef, useState } from "preact/hooks";
 import { ShowInputErrorLabel } from "../components/ShowInputErrorLabel.js";
-import { useBackendContext } from "../context/backend.js";
+import { useBankCoreApiContext } from "../context/config.js";
+import { useBackendState } from "../hooks/backend.js";
 import { bankUiSettings } from "../settings.js";
 import { undefinedIfEmpty, withRuntimeErrorHandling } from "../utils.js";
-import { doAutoFocus } from "./PaytoWireTransferForm.js";
-import { useBankCoreApiContext } from "../context/config.js";
 import { assertUnreachable } from "./HomePage.js";
+import { doAutoFocus } from "./PaytoWireTransferForm.js";
 
 
 /**
  * Collect and submit login data.
  */
 export function LoginForm({ reason, onRegister }: { reason?: "not-found" | 
"forbidden", onRegister?: () => void }): VNode {
-  const backend = useBackendContext();
+  const backend = useBackendState();
   const currentUser = backend.state.status !== "loggedOut" ? 
backend.state.username : undefined
   const [username, setUsername] = useState<string | undefined>(currentUser);
   const [password, setPassword] = useState<string | undefined>();
diff --git a/packages/demobank-ui/src/pages/OperationState/state.ts 
b/packages/demobank-ui/src/pages/OperationState/state.ts
index c9c1fa238..9e34a846b 100644
--- a/packages/demobank-ui/src/pages/OperationState/state.ts
+++ b/packages/demobank-ui/src/pages/OperationState/state.ts
@@ -86,10 +86,10 @@ export function useComponentState({ currency, onClose }: 
Props): utils.Recursive
   const wid = withdrawalOperationId
 
   async function doAbort() {
-    setBusy({})
     await withRuntimeErrorHandling(i18n, async () => {
       const resp = await api.abortWithdrawalById(wid);
       if (resp.type === "ok") {
+        updateSettings("currentWithdrawalOperationId", undefined)
         onClose();
       } else {
         switch (resp.case) {
@@ -103,7 +103,6 @@ export function useComponentState({ currency, onClose }: 
Props): utils.Recursive
         }
       }
     })
-    setBusy(undefined)
   }
 
   async function doConfirm() {
@@ -220,11 +219,7 @@ export function useComponentState({ currency, onClose }: 
Props): utils.Recursive
         status: "ready",
         error: undefined,
         uri: parsedUri,
-        onClose: async () => {
-          await doAbort()
-          updateSettings("currentWithdrawalOperationId", undefined)
-          onClose()
-        },
+        onClose: doAbort,
         onAbort: doAbort,
       }
     }
@@ -252,11 +247,7 @@ export function useComponentState({ currency, onClose }: 
Props): utils.Recursive
     return {
       status: "need-confirmation",
       error: undefined,
-      onAbort: async () => {
-        await doAbort()
-        updateSettings("currentWithdrawalOperationId", undefined)
-        onClose()
-      },
+      onAbort: doAbort,
       busy: !!busy,
       onConfirm: doConfirm
     }
diff --git a/packages/demobank-ui/src/pages/PaymentOptions.tsx 
b/packages/demobank-ui/src/pages/PaymentOptions.tsx
index c2d87d0e6..d0ee0243d 100644
--- a/packages/demobank-ui/src/pages/PaymentOptions.tsx
+++ b/packages/demobank-ui/src/pages/PaymentOptions.tsx
@@ -30,8 +30,8 @@ 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>();
+  console.log("patment", tab)
   return (
     <div class="mt-2">
 
@@ -48,9 +48,9 @@ export function PaymentOptions({ limit, goToConfirmOperation 
}: { limit: AmountJ
             }} />
             <div class="flex flex-col">
               <span class="flex">
-              <div class="text-4xl mr-4 my-auto">&#x1F4B5;</div>
+                <div class="text-4xl mr-4 my-auto">&#x1F4B5;</div>
                 <span class="grow self-center text-lg text-gray-900 
align-middle text-center">
-                <i18n.Translate>a <b>Taler</b> wallet</i18n.Translate>
+                  <i18n.Translate>a <b>Taler</b> wallet</i18n.Translate>
                 </span>
                 <svg class="self-center flex-none 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" />
@@ -59,14 +59,14 @@ export function PaymentOptions({ limit, 
goToConfirmOperation }: { limit: AmountJ
               <div 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>
               </div>
-                {!!settings.currentWithdrawalOperationId &&
-                  <span class="flex items-center gap-x-1.5 w-fit rounded-md 
bg-green-100 px-2 py-1 text-xs font-medium text-green-700 whitespace-pre">
-                    <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 ready</i18n.Translate>
-                  </span>
-                }
+              {!!settings.currentWithdrawalOperationId &&
+                <span class="flex items-center gap-x-1.5 w-fit rounded-md 
bg-green-100 px-2 py-1 text-xs font-medium text-green-700 whitespace-pre">
+                  <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 ready</i18n.Translate>
+                </span>
+              }
             </div>
           </label>
 
diff --git a/packages/demobank-ui/src/pages/PaytoWireTransferForm.tsx 
b/packages/demobank-ui/src/pages/PaytoWireTransferForm.tsx
index e713324c5..d859c10d7 100644
--- a/packages/demobank-ui/src/pages/PaytoWireTransferForm.tsx
+++ b/packages/demobank-ui/src/pages/PaytoWireTransferForm.tsx
@@ -51,20 +51,25 @@ const logger = new Logger("PaytoWireTransferForm");
 export function PaytoWireTransferForm({
   focus,
   title,
+  toAccount,
   onSuccess,
   onCancel,
   limit,
 }: {
   title: TranslatedString,
   focus?: boolean;
+  toAccount?: string,
   onSuccess: () => void;
   onCancel: (() => void) | undefined;
   limit: AmountJson;
 }): VNode {
-  const [isRawPayto, setIsRawPayto] = useState(true);
+  const [isRawPayto, setIsRawPayto] = useState(false);
   const { state: credentials } = useBackendState()
   const { api } = useBankCoreApiContext();
-  const [iban, setIban] = useState<string | undefined>();
+
+  const sendingToFixedAccount = toAccount !== undefined
+  //FIXME: support other destination that just IBAN
+  const [iban, setIban] = useState<string | undefined>(toAccount);
   const [subject, setSubject] = useState<string | undefined>();
   const [amount, setAmount] = useState<string | undefined>();
 
@@ -163,7 +168,7 @@ export function PaytoWireTransferForm({
       setAmount(undefined);
       setIban(undefined);
       setSubject(undefined);
-      rawPaytoInputSetter(undefined)      
+      rawPaytoInputSetter(undefined)
     })
   }
 
@@ -181,9 +186,12 @@ export function PaytoWireTransferForm({
             <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 amountStr = parsed.params["amount"]
+                if (amountStr) {
+                  const amount = Amounts.parse(parsed.params["amount"])
+                  if (amount) {
+                    setAmount(Amounts.stringifyValue(amount))
+                  }
                 }
                 const subject = parsed.params["message"]
                 if (subject) {
@@ -201,28 +209,30 @@ export function PaytoWireTransferForm({
             </span>
           </label>
 
-          <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
+          {sendingToFixedAccount ? undefined :
+            <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))
                 }
-                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>Import payto:// URI</i18n.Translate>
+                setIsRawPayto(true)
+              }} />
+              <span class="flex flex-1">
+                <span class="flex flex-col">
+                  <span class="block text-sm font-medium text-gray-900">
+                    <i18n.Translate>Import payto:// URI</i18n.Translate>
+                  </span>
                 </span>
               </span>
-            </span>
-          </label>
+            </label>
+          }
         </div>
       </div>
     </div>
@@ -244,9 +254,10 @@ export function PaytoWireTransferForm({
                 <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"
+                  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"
                   name="iban"
                   id="iban"
+                  disabled={sendingToFixedAccount}
                   value={iban ?? ""}
                   placeholder="CC0123456789"
                   autocomplete="off"
@@ -369,7 +380,7 @@ export function PaytoWireTransferForm({
 export function doAutoFocus(element: HTMLElement | null) {
   if (element) {
     setTimeout(() => {
-      element.focus()
+      element.focus({ preventScroll: true })
       element.scrollIntoView({
         behavior: "smooth",
         block: "center",
diff --git a/packages/demobank-ui/src/pages/ProfileNavigation.tsx 
b/packages/demobank-ui/src/pages/ProfileNavigation.tsx
new file mode 100644
index 000000000..c061c9742
--- /dev/null
+++ b/packages/demobank-ui/src/pages/ProfileNavigation.tsx
@@ -0,0 +1,56 @@
+import { useTranslationContext } from "@gnu-taler/web-util/browser";
+import { Fragment, VNode, h } from "preact";
+import { useBankCoreApiContext } from "../context/config.js";
+import { assertUnreachable } from "./HomePage.js";
+
+export function ProfileNavigation({ current }: { current: "details" | 
"credentials" | "cashouts" }): VNode {
+  const { i18n } = useTranslationContext()
+  const { config } = useBankCoreApiContext()
+  return <div>
+    <div class="sm:hidden">
+      <label for="tabs" class="sr-only"><i18n.Translate>Select a 
section</i18n.Translate></label>
+      <select id="tabs" name="tabs" class="block w-full rounded-md 
border-gray-300 focus:border-indigo-500 focus:ring-indigo-500" onChange={(e) => 
{
+        const op = e.currentTarget.value as typeof current
+        switch (op) {
+          case "details": {
+            window.location.href = "#/my-profile";
+            return;
+          }
+          case "credentials": {
+            window.location.href = "#/my-password";
+            return;
+          }
+          case "cashouts": {
+            window.location.href = "#/my-cashouts";
+            return;
+          }
+          default: assertUnreachable(op)
+        }
+      }}>
+        <option value="details" selected={current == 
"details"}><i18n.Translate>Details</i18n.Translate></option>
+        <option value="credentials" selected={current == 
"credentials"}><i18n.Translate>Credentials</i18n.Translate></option>
+        {config.have_cashout ?
+          <option value="cashouts" selected={current == 
"cashouts"}><i18n.Translate>Cashouts</i18n.Translate></option>
+          : undefined}
+      </select>
+    </div>
+    <div class="hidden sm:block">
+      <nav class="isolate flex divide-x divide-gray-200 rounded-lg shadow" 
aria-label="Tabs">
+        <a href="#/my-profile" data-selected={current == "details"} 
class="rounded-l-lg text-gray-500 hover:text-gray-700 
data-[selected=true]:text-gray-900 group relative min-w-0 flex-1 
overflow-hidden bg-white py-4 px-4 text-center text-sm font-medium 
hover:bg-gray-50 focus:z-10" >
+          <span><i18n.Translate>Details</i18n.Translate></span>
+          <span aria-hidden="true" data-selected={current == "details"} 
class="bg-transparent data-[selected=true]:bg-indigo-500 absolute inset-x-0 
bottom-0 h-0.5"></span>
+        </a>
+        <a href="#/my-password" data-selected={current == "credentials"} 
aria-current="page" class="             text-gray-500 hover:text-gray-700 
data-[selected=true]:text-gray-900 group relative min-w-0 flex-1 
overflow-hidden bg-white py-4 px-4 text-center text-sm font-medium 
hover:bg-gray-50 focus:z-10">
+          <span><i18n.Translate>Credentials</i18n.Translate></span>
+          <span aria-hidden="true" data-selected={current == "credentials"} 
class="bg-transparent data-[selected=true]:bg-indigo-500 absolute inset-x-0 
bottom-0 h-0.5"></span>
+        </a>
+        {config.have_cashout ?
+          <a href="#/my-cashouts" data-selected={current == "cashouts"} 
class="rounded-r-lg text-gray-500 hover:text-gray-700 
data-[selected=true]:text-gray-900 group relative min-w-0 flex-1 
overflow-hidden bg-white py-4 px-4 text-center text-sm font-medium 
hover:bg-gray-50 focus:z-10">
+            <span>Cashouts</span>
+            <span aria-hidden="true" data-selected={current == "cashouts"} 
class="bg-transparent data-[selected=true]:bg-indigo-500 absolute inset-x-0 
bottom-0 h-0.5"></span>
+          </a>
+          : undefined}
+      </nav>
+    </div>
+  </div>
+}
\ No newline at end of file
diff --git a/packages/demobank-ui/src/pages/RegistrationPage.tsx 
b/packages/demobank-ui/src/pages/RegistrationPage.tsx
index ce38a9fb8..3520405c5 100644
--- a/packages/demobank-ui/src/pages/RegistrationPage.tsx
+++ b/packages/demobank-ui/src/pages/RegistrationPage.tsx
@@ -13,21 +13,19 @@
  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 { AccessToken, HttpStatusCode, Logger, TalerError, TranslatedString } 
from "@gnu-taler/taler-util";
+import { AccessToken, Logger, TranslatedString } from "@gnu-taler/taler-util";
 import {
-  RequestError,
   notify,
-  notifyError,
-  useTranslationContext,
+  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 { bankUiSettings } from "../settings.js";
-import { buildRequestErrorMessage, undefinedIfEmpty, withRuntimeErrorHandling 
} from "../utils.js";
 import { ShowInputErrorLabel } from "../components/ShowInputErrorLabel.js";
-import { getRandomPassword, getRandomUsername } from "./rnd.js";
 import { useBankCoreApiContext } from "../context/config.js";
+import { useBackendState } from "../hooks/backend.js";
+import { bankUiSettings } from "../settings.js";
+import { undefinedIfEmpty, withRuntimeErrorHandling } from "../utils.js";
+import { getRandomPassword, getRandomUsername } from "./rnd.js";
 
 const logger = new Logger("RegistrationPage");
 
@@ -55,7 +53,7 @@ export const EMAIL_REGEX = /^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$/;
  * Collect and submit registration data.
  */
 function RegistrationForm({ onComplete, onCancel }: { onComplete: () => void, 
onCancel: () => void }): VNode {
-  const backend = useBackendContext();
+  const backend = useBackendState();
   const [username, setUsername] = useState<string | undefined>();
   const [name, setName] = useState<string | undefined>();
   const [password, setPassword] = useState<string | undefined>();
diff --git a/packages/demobank-ui/src/pages/ShowAccountDetails.tsx 
b/packages/demobank-ui/src/pages/ShowAccountDetails.tsx
index c65b90503..21724474a 100644
--- a/packages/demobank-ui/src/pages/ShowAccountDetails.tsx
+++ b/packages/demobank-ui/src/pages/ShowAccountDetails.tsx
@@ -1,25 +1,24 @@
-import { HttpStatusCode, TalerCorebankApi, TalerError, TranslatedString } from 
"@gnu-taler/taler-util";
-import { HttpResponsePaginated, RequestError, notify, notifyError, 
useTranslationContext } from "@gnu-taler/web-util/browser";
-import { VNode, h } from "preact";
+import { TalerCorebankApi, TalerError, TranslatedString } from 
"@gnu-taler/taler-util";
+import { notify, notifyInfo, useTranslationContext } from 
"@gnu-taler/web-util/browser";
+import { Fragment, VNode, h } from "preact";
 import { useState } from "preact/hooks";
 import { ErrorLoading } from "../components/ErrorLoading.js";
 import { Loading } from "../components/Loading.js";
 import { useBankCoreApiContext } from "../context/config.js";
 import { useAccountDetails } from "../hooks/access.js";
 import { useBackendState } from "../hooks/backend.js";
-import { buildRequestErrorMessage, undefinedIfEmpty, withRuntimeErrorHandling 
} from "../utils.js";
+import { undefinedIfEmpty, withRuntimeErrorHandling } from "../utils.js";
 import { assertUnreachable } from "./HomePage.js";
 import { LoginForm } from "./LoginForm.js";
 import { AccountForm } from "./admin/AccountForm.js";
+import { ProfileNavigation } from "./ProfileNavigation.js";
 
 export function ShowAccountDetails({
   account,
   onClear,
   onUpdateSuccess,
-  onChangePassword,
 }: {
   onClear?: () => void;
-  onChangePassword: () => void;
   onUpdateSuccess: () => void;
   account: string;
 }): VNode {
@@ -27,6 +26,8 @@ export function ShowAccountDetails({
   const { state: credentials } = useBackendState();
   const creds = credentials.status !== "loggedIn" ? undefined : credentials
   const { api } = useBankCoreApiContext()
+  const accountIsTheCurrentUser = credentials.status === "loggedIn" ?
+    credentials.username === account : false
 
   const [update, setUpdate] = useState(false);
   const [submitAccount, setSubmitAccount] = 
useState<TalerCorebankApi.AccountData | undefined>();
@@ -47,11 +48,7 @@ export function ShowAccountDetails({
   }
 
   async function doUpdate() {
-    if (!update) {
-      setUpdate(true);
-      return;
-    }
-    if (!submitAccount || !creds) return;
+    if (!update || !submitAccount || !creds) return;
     await withRuntimeErrorHandling(i18n, async () => {
       const resp = await api.updateAccount(creds, {
         cashout_address: submitAccount.cashout_payto_uri,
@@ -62,8 +59,9 @@ export function ShowAccountDetails({
         is_exchange: false,
         name: submitAccount.name,
       });
-      
+
       if (resp.type === "ok") {
+        notifyInfo(i18n.str`Account updated`);
         onUpdateSuccess();
       } else {
         switch (resp.case) {
@@ -87,21 +85,23 @@ export function ShowAccountDetails({
   }
 
   return (
-    <div>
+    <Fragment>
+      {accountIsTheCurrentUser ?
+        <ProfileNavigation current="details" />
+        :
+        <h1 class="text-base font-semibold leading-6 text-gray-900">
+          <i18n.Translate>Account "{account}"</i18n.Translate>
+        </h1>
+
+      }
+
       <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">
-            {update ?
-              <i18n.Translate>Update account</i18n.Translate>
-              :
-              <i18n.Translate>Account details</i18n.Translate>
-            }
-          </h2>
-          <div class="mt-4">
             <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>change the account details</i18n.Translate>
+                <span class="text-sm text-black font-semibold leading-6 " 
id="availability-label">
+                  <i18n.Translate>Change details</i18n.Translate>
                 </span>
               </span>
               <button type="button" data-enabled={!update} 
class="bg-indigo-600 data-[enabled=true]:bg-gray-200 relative inline-flex h-5 
w-10 flex-shrink-0 cursor-pointer rounded-full ring-2 border-gray-600 
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"
@@ -111,69 +111,36 @@ export function ShowAccountDetails({
                 <span aria-hidden="true" data-enabled={!update} 
class="translate-x-5 data-[enabled=true]: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>
-          </div>
-
+          </h2>
         </div>
+
         <AccountForm
+          focus={update}
+          username={account}
           template={result.body}
           purpose={update ? "update" : "show"}
           onChange={(a) => setSubmitAccount(a)}
         >
-
-        </AccountForm>
-
-        <p class="buttons-account">
-          <div
-            style={{
-              display: "flex",
-              justifyContent: "space-between",
-              flexFlow: "wrap-reverse",
-            }}
-          >
-            <div>
-              {onClear ? (
-                <input
-                  class="pure-button"
-                  type="submit"
-                  value={i18n.str`Close`}
-                  onClick={async (e) => {
-                    e.preventDefault();
-                    onClear();
-                  }}
-                />
-              ) : undefined}
-            </div>
-            <div style={{ display: "flex" }}>
-              <div>
-                <input
-                  id="select-exchange"
-                  class="pure-button pure-button-primary content"
-                  disabled={update && !submitAccount}
-                  type="submit"
-                  value={i18n.str`Change password`}
-                  onClick={async (e) => {
-                    e.preventDefault();
-                    onChangePassword();
-                  }}
-                />
-              </div>
-              <div>
-                <input
-                  id="select-exchange"
-                  class="pure-button pure-button-primary content"
-                  disabled={update && !submitAccount}
-                  type="submit"
-                  value={update ? i18n.str`Confirm` : i18n.str`Update`}
-                  onClick={async (e) => {
-                    e.preventDefault();
-                    doUpdate()
-                  }}
-                />
-              </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">
+            {onClear ?
+              <button type="button" class="text-sm font-semibold leading-6 
text-gray-900"
+                onClick={onClear}
+              >
+                <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={!update || !submitAccount}
+              onClick={doUpdate}
+            >
+              <i18n.Translate>Update</i18n.Translate>
+            </button>
           </div>
-        </p>
+        </AccountForm>
       </div>
-    </div>
+    </Fragment>
   );
 }
+
diff --git a/packages/demobank-ui/src/pages/UpdateAccountPassword.tsx 
b/packages/demobank-ui/src/pages/UpdateAccountPassword.tsx
index d82dac4b1..e3f0de8cc 100644
--- a/packages/demobank-ui/src/pages/UpdateAccountPassword.tsx
+++ b/packages/demobank-ui/src/pages/UpdateAccountPassword.tsx
@@ -1,16 +1,16 @@
-import { TalerError, TranslatedString } from "@gnu-taler/taler-util";
-import { HttpResponsePaginated, RequestError, notify, notifyError, 
useTranslationContext } from "@gnu-taler/web-util/browser";
+import { notify, notifyInfo, useTranslationContext } from 
"@gnu-taler/web-util/browser";
 import { Fragment, VNode, h } from "preact";
 import { useState } from "preact/hooks";
 import { ShowInputErrorLabel } from "../components/ShowInputErrorLabel.js";
-import { buildRequestErrorMessage, undefinedIfEmpty, withRuntimeErrorHandling 
} from "../utils.js";
-import { doAutoFocus } from "./PaytoWireTransferForm.js";
 import { useBankCoreApiContext } from "../context/config.js";
-import { assertUnreachable } from "./HomePage.js";
 import { useBackendState } from "../hooks/backend.js";
+import { undefinedIfEmpty, withRuntimeErrorHandling } from "../utils.js";
+import { assertUnreachable } from "./HomePage.js";
+import { doAutoFocus } from "./PaytoWireTransferForm.js";
+import { ProfileNavigation } from "./ProfileNavigation.js";
 
 export function UpdateAccountPassword({
-  account,
+  account: accountName,
   onCancel,
   onUpdateSuccess,
   focus,
@@ -22,13 +22,18 @@ export function UpdateAccountPassword({
 }): VNode {
   const { i18n } = useTranslationContext();
   const { state: credentials } = useBackendState();
-  const creds = credentials.status !== "loggedIn" ? undefined : credentials
+  const token = credentials.status !== "loggedIn" ? undefined : 
credentials.token
   const { api } = useBankCoreApiContext();
 
+  const [current, setCurrent] = useState<string | undefined>();
   const [password, setPassword] = useState<string | undefined>();
   const [repeat, setRepeat] = useState<string | undefined>();
 
+  const accountIsTheCurrentUser = credentials.status === "loggedIn" ?
+    credentials.username === accountName : false
+
   const errors = undefinedIfEmpty({
+    current: !accountIsTheCurrentUser ? undefined : !current ? 
i18n.str`required` : undefined,
     password: !password ? i18n.str`required` : undefined,
     repeat: !repeat
       ? i18n.str`required`
@@ -37,13 +42,16 @@ export function UpdateAccountPassword({
         : undefined,
   });
 
+
   async function doChangePassword() {
-    if (!!errors || !password || !creds) return;
+    if (!!errors || !password || !token) return;
     await withRuntimeErrorHandling(i18n, async () => {
-      const resp = await api.updatePassword(creds, {
+      const resp = await api.updatePassword({ username: accountName, token }, {
+        // old_password: current,
         new_password: password,
       });
       if (resp.type === "ok") {
+        notifyInfo(i18n.str`Password changed`);
         onUpdateSuccess();
       } else {
         switch (resp.case) {
@@ -51,6 +59,10 @@ export function UpdateAccountPassword({
             type: "error",
             title: i18n.str`Not authorized to change the password, maybe the 
session is invalid.`
           })
+          case "no-rights": return notify({
+            type: "error",
+            title: i18n.str`This user have no right on to change the password.`
+          })
           case "not-found": return notify({
             type: "error",
             title: i18n.str`Account not found`
@@ -62,112 +74,147 @@ export function UpdateAccountPassword({
   }
 
   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>Update password for account 
"{account}"</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">
-          <div class="grid max-w-2xl grid-cols-1 gap-x-6 gap-y-8 
sm:grid-cols-6">
+    <Fragment>
+      {accountIsTheCurrentUser ?
+        <ProfileNavigation current="credentials" /> :
+        <h1 class="text-base font-semibold leading-6 text-gray-900">
+          <i18n.Translate>Account "{accountName}"</i18n.Translate>
+        </h1>
 
-            <div class="sm:col-span-5">
-              <label
-                class="block text-sm font-medium leading-6 text-gray-900"
-                for="password"
-              >
-                {i18n.str`New password`}
-              </label>
-              <div class="mt-2">
-                <input
-                  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"
-                  id="password"
-                  data-error={!!errors?.password && password !== undefined}
-                  value={password ?? ""}
-                  onChange={(e) => {
-                    setPassword(e.currentTarget.value)
-                  }}
-                  // placeholder=""
-                  autocomplete="off"
-                />
-                <ShowInputErrorLabel
-                  message={errors?.password}
-                  isDirty={password !== undefined}
-                />
-              </div>
-              {/* <p class="mt-2 text-sm text-gray-500" >
-              <i18n.Translate>user </i18n.Translate>
-            </p> */}
-            </div>
+      }
 
-            <div class="sm:col-span-5">
-              <label
-                class="block text-sm font-medium leading-6 text-gray-900"
-                for="repeat"
-              >
-                {i18n.str`Type it again`}
-              </label>
-              <div class="mt-2">
-                <input
-                  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="repeat"
-                  id="repeat"
-                  data-error={!!errors?.repeat && repeat !== undefined}
-                  value={repeat ?? ""}
-                  onChange={(e) => {
-                    setRepeat(e.currentTarget.value)
-                  }}
-                  // placeholder=""
-                  autocomplete="off"
-                />
-                <ShowInputErrorLabel
-                  message={errors?.repeat}
-                  isDirty={repeat !== undefined}
-                />
+      <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>Update password</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">
+            <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"
+                  for="password"
+                >
+                  {i18n.str`New password`}
+                </label>
+                <div class="mt-2">
+                  <input
+                    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"
+                    id="password"
+                    data-error={!!errors?.password && password !== undefined}
+                    value={password ?? ""}
+                    onChange={(e) => {
+                      setPassword(e.currentTarget.value)
+                    }}
+                    autocomplete="off"
+                  />
+                  <ShowInputErrorLabel
+                    message={errors?.password}
+                    isDirty={password !== undefined}
+                  />
+                </div>
               </div>
-              <p class="mt-2 text-sm text-gray-500" >
-                <i18n.Translate>repeat the same password</i18n.Translate>
-              </p>
-            </div>
 
+              <div class="sm:col-span-5">
+                <label
+                  class="block text-sm font-medium leading-6 text-gray-900"
+                  for="repeat"
+                >
+                  {i18n.str`Type it again`}
+                </label>
+                <div class="mt-2">
+                  <input
+                    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="repeat"
+                    id="repeat"
+                    data-error={!!errors?.repeat && repeat !== undefined}
+                    value={repeat ?? ""}
+                    onChange={(e) => {
+                      setRepeat(e.currentTarget.value)
+                    }}
+                    // placeholder=""
+                    autocomplete="off"
+                  />
+                  <ShowInputErrorLabel
+                    message={errors?.repeat}
+                    isDirty={repeat !== undefined}
+                  />
+                </div>
+                <p class="mt-2 text-sm text-gray-500" >
+                  <i18n.Translate>repeat the same password</i18n.Translate>
+                </p>
+              </div>
 
+              {accountIsTheCurrentUser ?
+                <div class="sm:col-span-5">
+                  <label
+                    class="block text-sm font-medium leading-6 text-gray-900"
+                    for="password"
+                  >
+                    {i18n.str`Current password`}
+                  </label>
+                  <div class="mt-2">
+                    <input
+                      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="current"
+                      id="current-password"
+                      data-error={!!errors?.current && current !== undefined}
+                      value={current ?? ""}
+                      onChange={(e) => {
+                        setCurrent(e.currentTarget.value)
+                      }}
+                      autocomplete="off"
+                    />
+                    <ShowInputErrorLabel
+                      message={errors?.current}
+                      isDirty={current !== undefined}
+                    />
+                  </div>
+                  <p class="mt-2 text-sm text-gray-500" >
+                    <i18n.Translate>your current password, for 
security</i18n.Translate>
+                  </p>
+                </div>
+                : undefined}
 
+            </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 ?
-            <button type="button" class="text-sm font-semibold leading-6 
text-gray-900"
-              onClick={onCancel}
+          <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={!!errors}
+              onClick={(e) => {
+                e.preventDefault()
+                doChangePassword()
+              }}
             >
-              <i18n.Translate>Cancel</i18n.Translate>
+              <i18n.Translate>Change</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={!!errors}
-            onClick={(e) => {
-              e.preventDefault()
-              doChangePassword()
-            }}
-          >
-            <i18n.Translate>Change</i18n.Translate>
-          </button>
-        </div>
-      </form>
-    </div>
+          </div>
+        </form>
+      </div>
+    </Fragment>
 
   );
 }
\ No newline at end of file
diff --git a/packages/demobank-ui/src/pages/WithdrawalQRCode.tsx 
b/packages/demobank-ui/src/pages/WithdrawalQRCode.tsx
index 7266e4de4..51edbc95f 100644
--- a/packages/demobank-ui/src/pages/WithdrawalQRCode.tsx
+++ b/packages/demobank-ui/src/pages/WithdrawalQRCode.tsx
@@ -30,6 +30,7 @@ import { assertUnreachable } from "./HomePage.js";
 import { QrCodeSection } from "./QrCodeSection.js";
 import { WithdrawalConfirmationQuestion } from 
"./WithdrawalConfirmationQuestion.js";
 import { Attention } from "../components/Attention.js";
+import { Pages } from "../pages.js";
 
 const logger = new Logger("WithdrawalQRCode");
 
diff --git a/packages/demobank-ui/src/pages/admin/Account.tsx 
b/packages/demobank-ui/src/pages/admin/Account.tsx
index bf2fa86f0..103747414 100644
--- a/packages/demobank-ui/src/pages/admin/Account.tsx
+++ b/packages/demobank-ui/src/pages/admin/Account.tsx
@@ -3,15 +3,15 @@ import { notifyInfo, useTranslationContext } from 
"@gnu-taler/web-util/browser";
 import { Fragment, VNode, h } from "preact";
 import { ErrorLoading } from "../../components/ErrorLoading.js";
 import { Loading } from "../../components/Loading.js";
-import { useBackendContext } from "../../context/backend.js";
 import { useAccountDetails } from "../../hooks/access.js";
 import { assertUnreachable } from "../HomePage.js";
 import { LoginForm } from "../LoginForm.js";
 import { PaytoWireTransferForm } from "../PaytoWireTransferForm.js";
+import { useBackendState } from "../../hooks/backend.js";
 
-export function AdminAccount({ onRegister }: { onRegister: () => void }): 
VNode {
+export function WireTransfer({ toAccount, onRegister, onCancel, onSuccess }: { 
onSuccess?: () => void; toAccount?: string, onCancel?: () => void, onRegister?: 
() => void }): VNode {
   const { i18n } = useTranslationContext();
-  const r = useBackendContext();
+  const r = useBackendState();
   const account = r.state.status !== "loggedOut" ? r.state.username : "admin";
   const result = useAccountDetails(account);
 
@@ -41,11 +41,13 @@ export function AdminAccount({ onRegister }: { onRegister: 
() => void }): VNode
   return (
     <PaytoWireTransferForm
       title={i18n.str`Make a wire transfer`}
+      toAccount={toAccount}
       limit={limit}
       onSuccess={() => {
         notifyInfo(i18n.str`Wire transfer created!`);
+        if (onSuccess) onSuccess()
       }}
-      onCancel={undefined}
+      onCancel={onCancel}
     />
   );
 }
diff --git a/packages/demobank-ui/src/pages/admin/AccountForm.tsx 
b/packages/demobank-ui/src/pages/admin/AccountForm.tsx
index 8470930bf..bce089560 100644
--- a/packages/demobank-ui/src/pages/admin/AccountForm.tsx
+++ b/packages/demobank-ui/src/pages/admin/AccountForm.tsx
@@ -1,16 +1,20 @@
-import { ComponentChildren, VNode, h } from "preact";
+import { ComponentChildren, Fragment, VNode, h } from "preact";
 import { ShowInputErrorLabel } from "../../components/ShowInputErrorLabel.js";
 import { PartialButDefined, RecursivePartial, WithIntermediate, 
undefinedIfEmpty, validateIBAN } from "../../utils.js";
 import { useEffect, useRef, useState } from "preact/hooks";
 import { useTranslationContext } from "@gnu-taler/web-util/browser";
 import { TalerCorebankApi, buildPayto, parsePaytoUri } from 
"@gnu-taler/taler-util";
 import { doAutoFocus } from "../PaytoWireTransferForm.js";
+import { CopyButton } from "../../components/CopyButton.js";
+import { assertUnreachable } from "../HomePage.js";
 
 const IBAN_REGEX = /^[A-Z][A-Z0-9]*$/;
 const EMAIL_REGEX =
   
/^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
 const REGEX_JUST_NUMBERS_REGEX = /^\+[0-9 ]*$/;
 
+export type AccountFormData = TalerCorebankApi.AccountData & { username: 
string }
+
 /**
  * Create valid account object to update or create
  * Take template as initial values for the form
@@ -21,6 +25,7 @@ const REGEX_JUST_NUMBERS_REGEX = /^\+[0-9 ]*$/;
  */
 export function AccountForm({
   template,
+  username,
   purpose,
   onChange,
   focus,
@@ -28,11 +33,12 @@ export function AccountForm({
 }: {
   focus?: boolean,
   children: ComponentChildren,
+  username?: string,
   template: TalerCorebankApi.AccountData | undefined;
-  onChange: (a: TalerCorebankApi.AccountData | undefined) => void;
+  onChange: (a: AccountFormData | undefined) => void;
   purpose: "create" | "update" | "show";
 }): VNode {
-  const initial = initializeFromTemplate(template);
+  const initial = initializeFromTemplate(username, template);
   const [form, setForm] = useState(initial);
   const [errors, setErrors] = useState<
     RecursivePartial<typeof initial> | undefined
@@ -69,14 +75,8 @@ export function AccountForm({
               ? 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),
       name: !newForm.name ? i18n.str`required` : undefined,
-
-      // username: !newForm.username ? i18n.str`required` : undefined,
+      username: !newForm.username ? i18n.str`required` : undefined,
     });
     setErrors(errors);
     setForm(newForm);
@@ -95,7 +95,7 @@ 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">
+          <div class="sm:col-span-5">
             <label
               class="block text-sm font-medium leading-6 text-gray-900"
               for="username"
@@ -105,7 +105,7 @@ export function AccountForm({
             </label>
             <div class="mt-2">
               <input
-                ref={focus ? doAutoFocus : undefined}
+                ref={focus && purpose === "create" ? 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"
@@ -128,7 +128,7 @@ export function AccountForm({
             <p class="mt-2 text-sm text-gray-500" >
               <i18n.Translate>account identification in the 
bank</i18n.Translate>
             </p>
-          </div> */}
+          </div>
 
           <div class="sm:col-span-5">
             <label
@@ -165,27 +165,7 @@ export function AccountForm({
           </div>
 
 
-          {purpose !== "create" && (<div class="sm:col-span-5">
-            <label
-              class="block text-sm font-medium leading-6 text-gray-900"
-              for="internal-iban"
-            >
-              {i18n.str`Internal IBAN`}
-            </label>
-            <div class="mt-2">
-              <input
-                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="internal-iban"
-                id="internal-iban"
-                disabled={true}
-                value={form.payto_uri ?? ""}
-              />
-            </div>
-            <p class="mt-2 text-sm text-gray-500" >
-              <i18n.Translate>international bank account 
number</i18n.Translate>
-            </p>
-          </div>)}
+          {purpose !== "create" && (<RenderPaytoDisabledField 
paytoURI={form.payto_uri} />)}
 
           <div class="sm:col-span-5">
             <label
@@ -264,6 +244,7 @@ export function AccountForm({
             <div class="mt-2">
               <input
                 type="text"
+                ref={focus && purpose === "update" ? doAutoFocus : undefined}
                 data-error={!!errors?.cashout_payto_uri && 
form.cashout_payto_uri !== undefined}
                 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="cashout"
@@ -294,8 +275,9 @@ export function AccountForm({
 }
 
 function initializeFromTemplate(
+  username: string | undefined,
   account: TalerCorebankApi.AccountData | undefined,
-): WithIntermediate<TalerCorebankApi.AccountData> {
+): WithIntermediate<AccountFormData> {
   const emptyAccount = {
     cashout_payto_uri: undefined,
     contact_data: undefined,
@@ -314,8 +296,136 @@ function initializeFromTemplate(
   if (typeof initial.contact_data === "undefined") {
     initial.contact_data = emptyContact;
   }
-  // initial.contact_data.email;
+  const result: WithIntermediate<AccountFormData> = initial as any // FIXME: 
check types
+  result.username = username
+
   return initial as any;
 }
 
 
+function RenderPaytoDisabledField({ paytoURI }: { paytoURI: string | undefined 
}): VNode {
+  const { i18n } = useTranslationContext()
+  const payto = parsePaytoUri(paytoURI ?? "");
+  if (payto?.isKnown) {
+    if (payto.targetType === "iban") {
+      const value = payto.iban;
+      return <div class="sm:col-span-5">
+        <label
+          class="block text-sm font-medium leading-6 text-gray-900"
+          for="internal-iban"
+        >
+          {i18n.str`Internal IBAN`}
+        </label>
+        <div class="mt-2">
+          <div class="flex justify-between">
+            <input
+              type="text"
+              class="mr-4 w-full block-inline  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="internal-iban"
+              id="internal-iban"
+              disabled={true}
+              value={value ?? ""}
+            />
+            <CopyButton
+              class="p-2 rounded-full  text-black shadow-sm  
focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 "
+              getContent={() => value ?? ""}
+            />
+          </div>
+        </div>
+        <p class="mt-2 text-sm text-gray-500" >
+          <i18n.Translate>international bank account number</i18n.Translate>
+        </p>
+      </div>
+    }
+    if (payto.targetType === "x-taler-bank") {
+      const value = payto.account;
+      return <div class="sm:col-span-5">
+        <label
+          class="block text-sm font-medium leading-6 text-gray-900"
+          for="account-id"
+        >
+          {i18n.str`Account ID`}
+        </label>
+        <div class="mt-2">
+          <div class="flex justify-between">
+            <input
+              type="text"
+              class="mr-4 w-full block-inline  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="account-id"
+              id="account-id"
+              disabled={true}
+              value={value ?? ""}
+            />
+            <CopyButton
+              class="p-2 rounded-full  text-black shadow-sm  
focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 "
+              getContent={() => value ?? ""}
+            />
+          </div>
+        </div>
+        <p class="mt-2 text-sm text-gray-500" >
+          <i18n.Translate>internal account id</i18n.Translate>
+        </p>
+      </div>
+    }
+    if (payto.targetType === "bitcoin") {
+      const value = payto.targetPath;
+      return <div class="sm:col-span-5">
+        <label
+          class="block text-sm font-medium leading-6 text-gray-900"
+          for="account-id"
+        >
+          {i18n.str`Bitcoin address`}
+        </label>
+        <div class="mt-2">
+          <div class="flex justify-between">
+            <input
+              type="text"
+              class="mr-4 w-full block-inline  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="account-id"
+              id="account-id"
+              disabled={true}
+              value={value ?? ""}
+            />
+            <CopyButton
+              class="p-2 rounded-full  text-black shadow-sm  
focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 "
+              getContent={() => value ?? ""}
+            />
+          </div>
+        </div>
+        <p class="mt-2 text-sm text-gray-500" >
+          <i18n.Translate>bitcoin address</i18n.Translate>
+        </p>
+      </div>
+    }
+    assertUnreachable(payto)
+  }
+
+  const value = paytoURI ?? ""
+  return <div class="sm:col-span-5">
+    <label
+      class="block text-sm font-medium leading-6 text-gray-900"
+      for="internal-payto"
+    >
+      {i18n.str`Internal account`}
+    </label>
+    <div class="mt-2">
+      <div class="flex justify-between">
+        <input
+          type="text"
+          class="mr-4 w-full block-inline  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="internal-payto"
+          id="internal-payto"
+          disabled={true}
+          value={value ?? ""}
+        />
+        <CopyButton
+          class="p-2 rounded-full  text-black shadow-sm  focus-visible:outline 
focus-visible:outline-2 focus-visible:outline-offset-2 "
+          getContent={() => value ?? ""}
+        />
+      </div>
+    </div>
+    <p class="mt-2 text-sm text-gray-500" >
+      <i18n.Translate>generic payto URI</i18n.Translate>
+    </p>
+  </div>
+}
diff --git a/packages/demobank-ui/src/pages/admin/AccountList.tsx 
b/packages/demobank-ui/src/pages/admin/AccountList.tsx
index 8a1e8294a..39b43b9b1 100644
--- a/packages/demobank-ui/src/pages/admin/AccountList.tsx
+++ b/packages/demobank-ui/src/pages/admin/AccountList.tsx
@@ -1,22 +1,26 @@
 import { Amounts, TalerError } from "@gnu-taler/taler-util";
 import { useTranslationContext } from "@gnu-taler/web-util/browser";
-import { VNode, h } from "preact";
+import { Fragment, VNode, h } from "preact";
 import { ErrorLoading } from "../../components/ErrorLoading.js";
 import { Loading } from "../../components/Loading.js";
 import { useBusinessAccounts } from "../../hooks/circuit.js";
 import { assertUnreachable } from "../HomePage.js";
 import { RenderAmount } from "../PaytoWireTransferForm.js";
-import { AccountAction } from "./Home.js";
+import { useBankCoreApiContext } from "../../context/config.js";
 
 interface Props {
-  onAction: (type: AccountAction, account: string) => void;
-  account: string | undefined;
   onCreateAccount: () => void;
+
+  onShowAccountDetails: (aid: string) => void;
+  onRemoveAccount: (aid: string) => void;
+  onUpdateAccountPassword: (aid: string) => void;
+  onShowCashoutForAccount: (aid: string) => void;
 }
 
-export function AccountList({ account, onAction, onCreateAccount }: Props): 
VNode {
+export function AccountList({ onRemoveAccount, onShowAccountDetails, 
onUpdateAccountPassword, onShowCashoutForAccount, onCreateAccount }: Props): 
VNode {
   const result = useBusinessAccounts();
   const { i18n } = useTranslationContext();
+  const { config } = useBankCoreApiContext()
 
   if (!result) {
     return <Loading />
@@ -74,6 +78,7 @@ export function AccountList({ account, onAction, 
onCreateAccount }: Props): VNod
                   const balance = !item.balance
                     ? undefined
                     : Amounts.parse(item.balance.amount);
+                  const noBalance = Amounts.isZero(item.balance.amount)
                   const balanceIsDebit =
                     item.balance &&
                     item.balance.credit_debit_indicator == "debit";
@@ -83,7 +88,7 @@ export function AccountList({ account, onAction, 
onCreateAccount }: Props): VNod
                       <a href="#" class="text-indigo-600 hover:text-indigo-900"
                         onClick={(e) => {
                           e.preventDefault();
-                          onAction("show-details", item.username)
+                          onShowAccountDetails(item.username)
                         }}
                       >
                         {item.username}
@@ -94,7 +99,7 @@ export function AccountList({ account, onAction, 
onCreateAccount }: Props): VNod
                     <td class="whitespace-nowrap px-3 py-4 text-sm 
text-gray-500">
                       {item.name}
                     </td>
-                    <td class="whitespace-nowrap px-3 py-4 text-sm 
text-gray-500">
+                    <td data-negative={noBalance ? undefined : balanceIsDebit 
? "true" : "false"} class="whitespace-nowrap px-3 py-4 text-sm text-gray-500 
data-[negative=false]:text-green-600 data-[negative=true]:text-red-600 ">
                       {!balance ? (
                         i18n.str`unknown`
                       ) : (
@@ -107,27 +112,34 @@ export function AccountList({ account, onAction, 
onCreateAccount }: Props): VNod
                       <a href="#" class="text-indigo-600 hover:text-indigo-900"
                         onClick={(e) => {
                           e.preventDefault();
-                          onAction("update-password", item.username)
+                          onUpdateAccountPassword(item.username)
                         }}
                       >
                         change password
                       </a>
                       <br />
-                      <a href="#" class="text-indigo-600 
hover:text-indigo-900" onClick={(e) => {
-                        e.preventDefault();
-                        onAction("show-cashout", item.username)
-                      }}
-                      >
-                        cashouts
-                      </a>
-                      <br />
-                      <a href="#" class="text-indigo-600 
hover:text-indigo-900" onClick={(e) => {
-                        e.preventDefault();
-                        onAction("remove-account", item.username)
-                      }}
-                      >
-                        remove
-                      </a>
+                      {config.have_cashout ?
+                        <Fragment>
+
+                          <a href="#" class="text-indigo-600 
hover:text-indigo-900" onClick={(e) => {
+                            e.preventDefault();
+                            onShowCashoutForAccount(item.username)
+                          }}
+                          >
+                            cashouts
+                          </a>
+                          <br />
+                        </Fragment>
+                        : undefined}
+                      {noBalance ?
+                        <a href="#" class="text-indigo-600 
hover:text-indigo-900" onClick={(e) => {
+                          e.preventDefault();
+                          onRemoveAccount(item.username)
+                        }}
+                        >
+                          remove
+                        </a>
+                        : undefined}
                     </td>
                   </tr>
                 })}
diff --git a/packages/demobank-ui/src/pages/admin/AdminHome.tsx 
b/packages/demobank-ui/src/pages/admin/AdminHome.tsx
new file mode 100644
index 000000000..01f9f6dbd
--- /dev/null
+++ b/packages/demobank-ui/src/pages/admin/AdminHome.tsx
@@ -0,0 +1,32 @@
+import { Fragment, VNode, h } from "preact";
+import { Transactions } from "../../components/Transactions/index.js";
+import { WireTransfer } from "./Account.js";
+import { AccountList } from "./AccountList.js";
+
+/**
+ * Query account information and show QR code if there is pending withdrawal
+ */
+interface Props {
+  onRegister: () => void;
+
+  onCreateAccount: () => void;
+  onShowAccountDetails: (aid: string) => void;
+  onRemoveAccount: (aid: string) => void;
+  onUpdateAccountPassword: (aid: string) => void;
+  onShowCashoutForAccount: (aid: string) => void;
+}
+export function AdminHome({ onCreateAccount, onRegister, onRemoveAccount, 
onShowAccountDetails, onShowCashoutForAccount, onUpdateAccountPassword }: 
Props): VNode {
+  return <Fragment>
+    <AccountList
+      onCreateAccount={onCreateAccount}
+      onRemoveAccount={onRemoveAccount}
+      onShowCashoutForAccount={onShowCashoutForAccount}
+      onShowAccountDetails={onShowAccountDetails}
+      onUpdateAccountPassword={onUpdateAccountPassword}
+    />
+
+    <WireTransfer onRegister={onRegister} />
+
+    <Transactions account="admin" />
+  </Fragment>
+}
\ No newline at end of file
diff --git a/packages/demobank-ui/src/pages/admin/CashoutListForAccount.tsx 
b/packages/demobank-ui/src/pages/admin/CashoutListForAccount.tsx
new file mode 100644
index 000000000..466dc1a4b
--- /dev/null
+++ b/packages/demobank-ui/src/pages/admin/CashoutListForAccount.tsx
@@ -0,0 +1,47 @@
+import { useTranslationContext } from "@gnu-taler/web-util/browser";
+import { Fragment, VNode, h } from "preact";
+import { Cashouts } from "../../components/Cashouts/index.js";
+import { useBackendState } from "../../hooks/backend.js";
+import { ProfileNavigation } from "../ProfileNavigation.js";
+
+interface Props {
+  account: string,
+  onClose: () => void,
+  onSelected: (cid: string) => void
+}
+
+export function CashoutListForAccount({ account, onSelected, onClose }: 
Props): VNode {
+  const { i18n } = useTranslationContext();
+
+  const { state: credentials } = useBackendState();
+  const token = credentials.status !== "loggedIn" ? undefined : 
credentials.token
+
+  const accountIsTheCurrentUser = credentials.status === "loggedIn" ?
+    credentials.username === account : false
+
+  return <Fragment>
+    {accountIsTheCurrentUser ?
+      <ProfileNavigation current="cashouts" />
+      :
+      <h1 class="text-base font-semibold leading-6 text-gray-900">
+        <i18n.Translate>Cashout for account {account}</i18n.Translate>
+      </h1>
+    }
+    <Cashouts
+      account={account}
+      onSelected={onSelected}
+    />
+    <p>
+      <input
+        class="pure-button"
+        type="submit"
+        value={i18n.str`Close`}
+        onClick={async (e) => {
+          e.preventDefault();
+          onClose();
+        }}
+      />
+    </p>
+  </Fragment>
+}
+
diff --git a/packages/demobank-ui/src/pages/admin/CreateNewAccount.tsx 
b/packages/demobank-ui/src/pages/admin/CreateNewAccount.tsx
index e10c3ad41..772ea6e84 100644
--- a/packages/demobank-ui/src/pages/admin/CreateNewAccount.tsx
+++ b/packages/demobank-ui/src/pages/admin/CreateNewAccount.tsx
@@ -1,21 +1,22 @@
 import { HttpStatusCode, TalerCorebankApi, TalerError, TranslatedString } from 
"@gnu-taler/taler-util";
-import { RequestError, notify, notifyError, useTranslationContext } from 
"@gnu-taler/web-util/browser";
+import { RequestError, notify, notifyError, notifyInfo, useTranslationContext 
} from "@gnu-taler/web-util/browser";
 import { Fragment, VNode, h } from "preact";
 import { useState } from "preact/hooks";
 import { buildRequestErrorMessage, withRuntimeErrorHandling } from 
"../../utils.js";
 import { getRandomPassword } from "../rnd.js";
-import { AccountForm } from "./AccountForm.js";
+import { AccountForm, AccountFormData } from "./AccountForm.js";
 import { useBackendState } from "../../hooks/backend.js";
 import { useBankCoreApiContext } from "../../context/config.js";
 import { assertUnreachable } from "../HomePage.js";
 import { mutate } from "swr";
+import { Attention } from "../../components/Attention.js";
 
 export function CreateNewAccount({
   onCancel,
   onCreateSuccess,
 }: {
   onCancel: () => void;
-  onCreateSuccess: (password: string) => void;
+  onCreateSuccess: () => void;
 }): VNode {
   const { i18n } = useTranslationContext();
   // const { createAccount } = useAdminAccountAPI();
@@ -23,9 +24,7 @@ export function CreateNewAccount({
   const token = credentials.status !== "loggedIn" ? undefined : 
credentials.token
   const { api } = useBankCoreApiContext();
 
-  const [submitAccount, setSubmitAccount] = useState<
-    TalerCorebankApi.AccountData | undefined
-  >();
+  const [submitAccount, setSubmitAccount] = useState<AccountFormData | 
undefined>();
 
   async function doCreate() {
     if (!submitAccount || !token) return;
@@ -35,14 +34,17 @@ export function CreateNewAccount({
         challenge_contact_data: submitAccount.contact_data,
         internal_payto_uri: submitAccount.payto_uri,
         name: submitAccount.name,
-        username: "",//FIXME: not in account data
+        username: submitAccount.username,//FIXME: not in account data
         password: getRandomPassword(),
       };
 
       const resp = await api.createAccount(token, account);
       if (resp.type === "ok") {
         mutate(() => true)// clean account list
-        onCreateSuccess(account.password);
+        notifyInfo(
+          i18n.str`Account created with password "${account.password}". The 
user must change the password on the next login.`,
+        );
+        onCreateSuccess();
       } else {
         switch (resp.case) {
           case "invalid-input": return notify({
@@ -75,6 +77,12 @@ export function CreateNewAccount({
     })
   }
 
+  if (!(credentials.status === "loggedIn" && credentials.isUserAdministrator)) 
{
+    return <Attention type="warning" title={i18n.str`Can't create accounts`} 
onClose={onCancel}>
+      <i18n.Translate>Only system admin can create accounts.</i18n.Translate>
+    </Attention>
+  }
+
   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">
diff --git a/packages/demobank-ui/src/pages/admin/Home.tsx 
b/packages/demobank-ui/src/pages/admin/Home.tsx
deleted file mode 100644
index 71ea8ce1b..000000000
--- a/packages/demobank-ui/src/pages/admin/Home.tsx
+++ /dev/null
@@ -1,143 +0,0 @@
-import { notifyInfo, useTranslationContext } from 
"@gnu-taler/web-util/browser";
-import { Fragment, VNode, h } from "preact";
-import { useState } from "preact/hooks";
-import { Cashouts } from "../../components/Cashouts/index.js";
-import { Transactions } from "../../components/Transactions/index.js";
-import { ShowAccountDetails } from "../ShowAccountDetails.js";
-import { UpdateAccountPassword } from "../UpdateAccountPassword.js";
-import { ShowCashoutDetails } from "../business/Home.js";
-import { AdminAccount } from "./Account.js";
-import { AccountList } from "./AccountList.js";
-import { CreateNewAccount } from "./CreateNewAccount.js";
-import { RemoveAccount } from "./RemoveAccount.js";
-
-/**
- * Query account information and show QR code if there is pending withdrawal
- */
-interface Props {
-  onRegister: () => void;
-}
-export type AccountAction = "show-details" |
-  "show-cashout" |
-  "update-password" |
-  "remove-account" |
-  "show-cashouts-details";
-
-export function AdminHome({ onRegister }: Props): VNode {
-  const [action, setAction] = useState<{
-    type: AccountAction,
-    account: string
-  } | undefined>()
-
-  const [createAccount, setCreateAccount] = useState(false);
-
-  const { i18n } = useTranslationContext();
-
-  if (action) {
-    switch (action.type) {
-      case "show-cashouts-details": return <ShowCashoutDetails
-        id={action.account}
-        onCancel={() => {
-          setAction(undefined);
-        }}
-      />
-      case "show-cashout": return (
-        <div>
-          <div>
-            <h1 class="nav welcome-text">
-              <i18n.Translate>Cashout for account 
{action.account}</i18n.Translate>
-            </h1>
-          </div>
-          <Cashouts
-            account={action.account}
-            onSelected={(id) => {
-              setAction({
-                type: "show-cashouts-details",
-                account: action.account
-              });
-            }}
-          />
-          <p>
-            <input
-              class="pure-button"
-              type="submit"
-              value={i18n.str`Close`}
-              onClick={async (e) => {
-                e.preventDefault();
-                setAction(undefined);
-              }}
-            />
-          </p>
-        </div>
-      )
-      case "update-password": return <UpdateAccountPassword
-        account={action.account}
-        onUpdateSuccess={() => {
-          notifyInfo(i18n.str`Password changed`);
-          setAction(undefined);
-        }}
-        onCancel={() => {
-          setAction(undefined);
-        }}
-      />
-      case "remove-account": return <RemoveAccount
-        account={action.account}
-        onUpdateSuccess={() => {
-          notifyInfo(i18n.str`Account removed`);
-          setAction(undefined);
-        }}
-        onCancel={() => {
-          setAction(undefined);
-        }}
-      />
-      case "show-details": return <ShowAccountDetails
-        account={action.account}
-        onChangePassword={() => {
-          setAction({
-            type: "update-password",
-            account: action.account,
-          })
-        }}
-        onUpdateSuccess={() => {
-          notifyInfo(i18n.str`Account updated`);
-          setAction(undefined);
-        }}
-        onClear={() => {
-          setAction(undefined);
-        }}
-      />
-    }
-  }
-
-  if (createAccount) {
-    return (
-      <CreateNewAccount
-        onCancel={() => setCreateAccount(false)}
-        onCreateSuccess={(password) => {
-          notifyInfo(
-            i18n.str`Account created with password "${password}". The user 
must change the password on the next login.`,
-          );
-          setCreateAccount(false);
-        }}
-      />
-    );
-  }
-
-  return (
-    <Fragment>
-
-      <AccountList
-        onCreateAccount={() => {
-          setCreateAccount(true);
-        }}
-        account={undefined}
-        onAction={(type, account) => setAction({ account, type })}
-
-      />
-
-      <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 9a212ebd0..88961c2cb 100644
--- a/packages/demobank-ui/src/pages/admin/RemoveAccount.tsx
+++ b/packages/demobank-ui/src/pages/admin/RemoveAccount.tsx
@@ -1,5 +1,5 @@
 import { Amounts, HttpStatusCode, TalerError, TranslatedString } from 
"@gnu-taler/taler-util";
-import { HttpResponsePaginated, RequestError, notify, notifyError, 
useTranslationContext } from "@gnu-taler/web-util/browser";
+import { HttpResponsePaginated, RequestError, notify, notifyError, notifyInfo, 
useTranslationContext } from "@gnu-taler/web-util/browser";
 import { Fragment, VNode, h } from "preact";
 import { useState } from "preact/hooks";
 import { Attention } from "../../components/Attention.js";
@@ -63,6 +63,7 @@ export function RemoveAccount({
     await withRuntimeErrorHandling(i18n, async () => {
       const resp = await api.deleteAccount({ username: account, token });
       if (resp.type === "ok") {
+        notifyInfo(i18n.str`Account removed`);
         onUpdateSuccess();
       } else {
         switch (resp.case) {
diff --git a/packages/demobank-ui/src/pages/business/Home.tsx 
b/packages/demobank-ui/src/pages/business/CreateCashout.tsx
similarity index 57%
rename from packages/demobank-ui/src/pages/business/Home.tsx
rename to packages/demobank-ui/src/pages/business/CreateCashout.tsx
index d7beda01d..4696c899e 100644
--- a/packages/demobank-ui/src/pages/business/Home.tsx
+++ b/packages/demobank-ui/src/pages/business/CreateCashout.tsx
@@ -21,15 +21,12 @@ import {
 } from "@gnu-taler/taler-util";
 import {
   notify,
-  notifyError,
-  notifyInfo,
   useTranslationContext
 } from "@gnu-taler/web-util/browser";
 import { format } from "date-fns";
 import { Fragment, VNode, h } from "preact";
 import { useEffect, useState } from "preact/hooks";
 import { mutate } from "swr";
-import { Cashouts } from "../../components/Cashouts/index.js";
 import { ErrorLoading } from "../../components/ErrorLoading.js";
 import { Loading } from "../../components/Loading.js";
 import { ShowInputErrorLabel } from "../../components/ShowInputErrorLabel.js";
@@ -43,114 +40,15 @@ import {
 } from "../../hooks/circuit.js";
 import {
   TanChannel,
-  buildRequestErrorMessage,
   undefinedIfEmpty,
-  withRuntimeErrorHandling,
+  withRuntimeErrorHandling
 } from "../../utils.js";
 import { LoginForm } from "../LoginForm.js";
 import { InputAmount } from "../PaytoWireTransferForm.js";
-import { ShowAccountDetails } from "../ShowAccountDetails.js";
-import { UpdateAccountPassword } from "../UpdateAccountPassword.js";
+import { assertUnreachable } from "../HomePage.js";
+import { Attention } from "../../components/Attention.js";
 
 interface Props {
-  account: string,
-  onClose: () => void;
-  onRegister: () => void;
-}
-export function BusinessAccount({
-  onClose,
-  account,
-  onRegister,
-}: Props): VNode {
-  const { i18n } = useTranslationContext();
-  const [updatePassword, setUpdatePassword] = useState(false);
-  const [newCashout, setNewcashout] = useState(false);
-  const [showCashoutDetails, setShowCashoutDetails] = useState<
-    string | undefined
-  >();
-
-  if (newCashout) {
-    return (
-      <CreateCashout
-        account={account}
-        onCancel={() => {
-          setNewcashout(false);
-        }}
-        onComplete={(id) => {
-          notifyInfo(
-            i18n.str`Cashout created. You need to confirm the operation to 
complete the transaction.`,
-          );
-          setNewcashout(false);
-          setShowCashoutDetails(id);
-        }}
-      />
-    );
-  }
-  if (showCashoutDetails) {
-    return (
-      <ShowCashoutDetails
-        id={showCashoutDetails}
-        onCancel={() => {
-          setShowCashoutDetails(undefined);
-        }}
-      />
-    );
-  }
-  if (updatePassword) {
-    return (
-      <UpdateAccountPassword
-        account={account}
-        onUpdateSuccess={() => {
-          notifyInfo(i18n.str`Password changed`);
-          setUpdatePassword(false);
-        }}
-        onCancel={() => {
-          setUpdatePassword(false);
-        }}
-      />
-    );
-  }
-  return (
-    <div>
-      <ShowAccountDetails
-        account={account}
-        onUpdateSuccess={() => {
-          notifyInfo(i18n.str`Account updated`);
-        }}
-        onChangePassword={() => {
-          setUpdatePassword(true);
-        }}
-        onClear={onClose}
-      />
-      <section style={{ marginTop: "2em" }}>
-        <div class="active">
-          <h3>{i18n.str`Latest cashouts`}</h3>
-          <Cashouts
-            account={account}
-            onSelected={(id) => {
-              setShowCashoutDetails(id);
-            }}
-          />
-        </div>
-        <br />
-        <div style={{ display: "flex", justifyContent: "space-between" }}>
-          <div />
-          <input
-            class="pure-button pure-button-primary content"
-            type="submit"
-            value={i18n.str`New cashout`}
-            onClick={async (e) => {
-              e.preventDefault();
-              setNewcashout(true);
-            }}
-          />
-        </div>
-      </section>
-    </div>
-  );
-}
-
-interface PropsCashout {
   account: string;
   onComplete: (id: string) => void;
   onCancel: () => void;
@@ -167,11 +65,11 @@ type ErrorFrom<T> = {
 };
 
 
-function CreateCashout({
+export function CreateCashout({
   account: accountName,
   onComplete,
   onCancel,
-}: PropsCashout): VNode {
+}: Props): VNode {
   const { i18n } = useTranslationContext();
   const resultRatios = useRatiosAndFeeConfig();
   const resultAccount = useAccountDetails(accountName);
@@ -184,6 +82,17 @@ function CreateCashout({
   const { api, config } = useBankCoreApiContext()
   const [form, setForm] = useState<Partial<FormType>>({ isDebit: true });
 
+  if (!config.have_cashout) {
+    return <Attention type="warning" title={i18n.str`Unable to create a 
cashout`} onClose={onCancel}>
+      <i18n.Translate>The bank configuration does not support cashout 
operations.</i18n.Translate>
+    </Attention>
+  }
+  if (!config.fiat_currency) {
+    return <Attention type="warning" title={i18n.str`Unable to create a 
cashout`} onClose={onCancel}>
+      <i18n.Translate>The bank configuration support cashout operations but 
there is no fiat currency.</i18n.Translate>
+    </Attention>
+  }
+
   if (!resultAccount || !resultRatios) {
     return <Loading />
   }
@@ -207,9 +116,6 @@ function CreateCashout({
       default: assertUnreachable(resultRatios.case)
     }
   }
-  if (!config.fiat_currency) {
-    return <div>cashout operations are not supported</div>
-  }
 
   const ratio = resultRatios.body
 
@@ -514,223 +420,3 @@ function CreateCashout({
     </div>
   );
 }
-
-interface ShowCashoutProps {
-  id: string;
-  onCancel: () => void;
-}
-export function ShowCashoutDetails({
-  id,
-  onCancel,
-}: ShowCashoutProps): VNode {
-  const { i18n } = useTranslationContext();
-  const { state } = useBackendState();
-  const creds = state.status !== "loggedIn" ? undefined : state
-  const { api } = useBankCoreApiContext()
-  const result = useCashoutDetails(id);
-  const [code, setCode] = useState<string | undefined>(undefined);
-
-  if (!result) {
-    return <Loading />
-  }
-  if (result instanceof TalerError) {
-    return <ErrorLoading error={result} />
-  }
-  if (result.type === "fail") {
-    switch (result.case) {
-      case "already-aborted": return <div>this cashout is already aborted</div>
-      default: assertUnreachable(result.case)
-    }
-  }
-  const errors = undefinedIfEmpty({
-    code: !code ? i18n.str`required` : undefined,
-  });
-  const isPending = String(result.body.status).toUpperCase() === "PENDING";
-  return (
-    <div>
-      <h1>Cashout details {id}</h1>
-      <form class="pure-form">
-        <fieldset>
-          <label>
-            <i18n.Translate>Subject</i18n.Translate>
-          </label>
-          <input readOnly value={result.body.subject} />
-        </fieldset>
-        <fieldset>
-          <label>
-            <i18n.Translate>Created</i18n.Translate>
-          </label>
-          <input readOnly value={result.body.creation_time.t_s === "never" ? 
i18n.str`never` : format(result.body.creation_time.t_s, "dd/MM/yyyy HH:mm:ss")} 
/>
-        </fieldset>
-        <fieldset>
-          <label>
-            <i18n.Translate>Confirmed</i18n.Translate>
-          </label>
-          <input readOnly value={result.body.confirmation_time === undefined ? 
"-" :
-            (result.body.confirmation_time.t_s === "never" ?
-              i18n.str`never` :
-              format(result.body.confirmation_time.t_s, "dd/MM/yyyy HH:mm:ss"))
-          } />
-        </fieldset>
-        <fieldset>
-          <label>
-            <i18n.Translate>Debited</i18n.Translate>
-          </label>
-          <input readOnly value={result.body.amount_debit} />
-        </fieldset>
-        <fieldset>
-          <label>
-            <i18n.Translate>Credit</i18n.Translate>
-          </label>
-          <input readOnly value={result.body.amount_credit} />
-        </fieldset>
-        <fieldset>
-          <label>
-            <i18n.Translate>Status</i18n.Translate>
-          </label>
-          <input readOnly value={result.body.status} />
-        </fieldset>
-        <fieldset>
-          <label>
-            <i18n.Translate>Destination</i18n.Translate>
-          </label>
-          <input readOnly value={result.body.credit_payto_uri} />
-        </fieldset>
-        {isPending ? (
-          <fieldset>
-            <label>
-              <i18n.Translate>Code</i18n.Translate>
-            </label>
-            <input
-              value={code ?? ""}
-              onChange={(e) => {
-                setCode(e.currentTarget.value);
-              }}
-            />
-            <ShowInputErrorLabel
-              message={errors?.code}
-              isDirty={code !== undefined}
-            />
-          </fieldset>
-        ) : undefined}
-      </form>
-      <br />
-      <div style={{ display: "flex", justifyContent: "space-between" }}>
-        <button
-          class="pure-button pure-button-secondary btn-cancel"
-          onClick={(e) => {
-            e.preventDefault();
-            onCancel();
-          }}
-        >
-          {i18n.str`Back`}
-        </button>
-        {isPending ? (
-          <div>
-            <button
-              type="submit"
-              class="pure-button pure-button-primary button-error"
-              onClick={async (e) => {
-                e.preventDefault();
-                if (!creds) return;
-                await withRuntimeErrorHandling(i18n, async () => {
-                  const resp = await api.abortCashoutById(creds, id);
-                  if (resp.type === "ok") {
-                    onCancel();
-                  } else {
-                    switch (resp.case) {
-                      case "not-found": return notify({
-                        type: "error",
-                        title: i18n.str`Cashout not found. It may be also mean 
that it was already aborted.`,
-                        description: resp.detail.hint as TranslatedString,
-                        debug: resp.detail,
-                      })
-                      case "already-confirmed": return notify({
-                        type: "error",
-                        title: i18n.str`Cashout was already confimed.`,
-                        description: resp.detail.hint as TranslatedString,
-                        debug: resp.detail,
-                      })
-                      default: {
-                        assertUnreachable(resp)
-                      }
-                    }
-                  }
-                })
-              }}
-            >
-              {i18n.str`Abort`}
-            </button>
-            &nbsp;
-            <button
-              type="submit"
-              disabled={!code}
-              class="pure-button pure-button-primary "
-              onClick={async (e) => {
-                e.preventDefault();
-                if (!creds || !code) return;
-                await withRuntimeErrorHandling(i18n, async () => {
-                  const resp = await api.confirmCashoutById(creds, id, {
-                    tan: code,
-                  });
-                  if (resp.type === "ok") {
-                    mutate(() => true)//clean cashout state
-                  } else {
-                    switch (resp.case) {
-                      case "not-found": return notify({
-                        type: "error",
-                        title: i18n.str`Cashout not found. It may be also mean 
that it was already aborted.`,
-                        description: resp.detail.hint as TranslatedString,
-                        debug: resp.detail,
-                      })
-                      case "wrong-tan-or-credential": return notify({
-                        type: "error",
-                        title: i18n.str`Invalid code or credentials.`,
-                        description: resp.detail.hint as TranslatedString,
-                        debug: resp.detail,
-                      })
-                      case "cashout-address-changed": return notify({
-                        type: "error",
-                        title: i18n.str`The cash-out address between the 
creation and the confirmation changed.`,
-                        description: resp.detail.hint as TranslatedString,
-                        debug: resp.detail,
-                      })
-                      default: assertUnreachable(resp)
-                    }
-                  }                  
-                })
-              }}
-            >
-              {i18n.str`Confirm`}
-            </button>
-          </div>
-        ) : (
-          <div />
-        )}
-      </div>
-    </div>
-  );
-}
-
-const MAX_AMOUNT_DIGIT = 2;
-/**
- * Truncate the amount of digits to display
- * in the form based on the fee calculations
- *
- * Backend must have the same truncation
- * @param a
- * @returns
- */
-function truncate(a: AmountJson): AmountJson {
-  const str = Amounts.stringify(a);
-  const idx = str.indexOf(".");
-  if (idx === -1) {
-    return a;
-  }
-  const truncated = str.substring(0, idx + 1 + MAX_AMOUNT_DIGIT);
-  return Amounts.parseOrThrow(truncated);
-}
-
-export function assertUnreachable(x: never): never {
-  throw new Error("Didn't expect to get here");
-}
diff --git a/packages/demobank-ui/src/pages/business/ShowCashoutDetails.tsx 
b/packages/demobank-ui/src/pages/business/ShowCashoutDetails.tsx
new file mode 100644
index 000000000..a8e34e4b9
--- /dev/null
+++ b/packages/demobank-ui/src/pages/business/ShowCashoutDetails.tsx
@@ -0,0 +1,237 @@
+/*
+ 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 {
+  TalerError,
+  TranslatedString
+} from "@gnu-taler/taler-util";
+import {
+  notify,
+  useTranslationContext
+} from "@gnu-taler/web-util/browser";
+import { format } from "date-fns";
+import { Fragment, VNode, h } from "preact";
+import { useState } from "preact/hooks";
+import { mutate } from "swr";
+import { ErrorLoading } from "../../components/ErrorLoading.js";
+import { Loading } from "../../components/Loading.js";
+import { ShowInputErrorLabel } from "../../components/ShowInputErrorLabel.js";
+import { useBankCoreApiContext } from "../../context/config.js";
+import { useBackendState } from "../../hooks/backend.js";
+import {
+  useCashoutDetails
+} from "../../hooks/circuit.js";
+import {
+  undefinedIfEmpty,
+  withRuntimeErrorHandling
+} from "../../utils.js";
+import { assertUnreachable } from "../HomePage.js";
+
+interface Props {
+  id: string;
+  onCancel: () => void;
+}
+export function ShowCashoutDetails({
+  id,
+  onCancel,
+}: Props): VNode {
+  const { i18n } = useTranslationContext();
+  const { state } = useBackendState();
+  const creds = state.status !== "loggedIn" ? undefined : state
+  const { api } = useBankCoreApiContext()
+  const result = useCashoutDetails(id);
+  const [code, setCode] = useState<string | undefined>(undefined);
+
+  if (!result) {
+    return <Loading />
+  }
+  if (result instanceof TalerError) {
+    return <ErrorLoading error={result} />
+  }
+  if (result.type === "fail") {
+    switch (result.case) {
+      case "already-aborted": return <div>this cashout is already aborted</div>
+      default: assertUnreachable(result.case)
+    }
+  }
+  const errors = undefinedIfEmpty({
+    code: !code ? i18n.str`required` : undefined,
+  });
+  const isPending = String(result.body.status).toUpperCase() === "PENDING";
+  return (
+    <div>
+      <h1>Cashout details {id}</h1>
+      <form class="pure-form">
+        <fieldset>
+          <label>
+            <i18n.Translate>Subject</i18n.Translate>
+          </label>
+          <input readOnly value={result.body.subject} />
+        </fieldset>
+        <fieldset>
+          <label>
+            <i18n.Translate>Created</i18n.Translate>
+          </label>
+          <input readOnly value={result.body.creation_time.t_s === "never" ? 
i18n.str`never` : format(result.body.creation_time.t_s, "dd/MM/yyyy HH:mm:ss")} 
/>
+        </fieldset>
+        <fieldset>
+          <label>
+            <i18n.Translate>Confirmed</i18n.Translate>
+          </label>
+          <input readOnly value={result.body.confirmation_time === undefined ? 
"-" :
+            (result.body.confirmation_time.t_s === "never" ?
+              i18n.str`never` :
+              format(result.body.confirmation_time.t_s, "dd/MM/yyyy HH:mm:ss"))
+          } />
+        </fieldset>
+        <fieldset>
+          <label>
+            <i18n.Translate>Debited</i18n.Translate>
+          </label>
+          <input readOnly value={result.body.amount_debit} />
+        </fieldset>
+        <fieldset>
+          <label>
+            <i18n.Translate>Credit</i18n.Translate>
+          </label>
+          <input readOnly value={result.body.amount_credit} />
+        </fieldset>
+        <fieldset>
+          <label>
+            <i18n.Translate>Status</i18n.Translate>
+          </label>
+          <input readOnly value={result.body.status} />
+        </fieldset>
+        <fieldset>
+          <label>
+            <i18n.Translate>Destination</i18n.Translate>
+          </label>
+          <input readOnly value={result.body.credit_payto_uri} />
+        </fieldset>
+        {isPending ? (
+          <fieldset>
+            <label>
+              <i18n.Translate>Code</i18n.Translate>
+            </label>
+            <input
+              value={code ?? ""}
+              onChange={(e) => {
+                setCode(e.currentTarget.value);
+              }}
+            />
+            <ShowInputErrorLabel
+              message={errors?.code}
+              isDirty={code !== undefined}
+            />
+          </fieldset>
+        ) : undefined}
+      </form>
+      <br />
+      <div style={{ display: "flex", justifyContent: "space-between" }}>
+        <button
+          class="pure-button pure-button-secondary btn-cancel"
+          onClick={(e) => {
+            e.preventDefault();
+            onCancel();
+          }}
+        >
+          {i18n.str`Back`}
+        </button>
+        {isPending ? (
+          <div>
+            <button
+              type="submit"
+              class="pure-button pure-button-primary button-error"
+              onClick={async (e) => {
+                e.preventDefault();
+                if (!creds) return;
+                await withRuntimeErrorHandling(i18n, async () => {
+                  const resp = await api.abortCashoutById(creds, id);
+                  if (resp.type === "ok") {
+                    onCancel();
+                  } else {
+                    switch (resp.case) {
+                      case "not-found": return notify({
+                        type: "error",
+                        title: i18n.str`Cashout not found. It may be also mean 
that it was already aborted.`,
+                        description: resp.detail.hint as TranslatedString,
+                        debug: resp.detail,
+                      })
+                      case "already-confirmed": return notify({
+                        type: "error",
+                        title: i18n.str`Cashout was already confimed.`,
+                        description: resp.detail.hint as TranslatedString,
+                        debug: resp.detail,
+                      })
+                      default: {
+                        assertUnreachable(resp)
+                      }
+                    }
+                  }
+                })
+              }}
+            >
+              {i18n.str`Abort`}
+            </button>
+            &nbsp;
+            <button
+              type="submit"
+              disabled={!code}
+              class="pure-button pure-button-primary "
+              onClick={async (e) => {
+                e.preventDefault();
+                if (!creds || !code) return;
+                await withRuntimeErrorHandling(i18n, async () => {
+                  const resp = await api.confirmCashoutById(creds, id, {
+                    tan: code,
+                  });
+                  if (resp.type === "ok") {
+                    mutate(() => true)//clean cashout state
+                  } else {
+                    switch (resp.case) {
+                      case "not-found": return notify({
+                        type: "error",
+                        title: i18n.str`Cashout not found. It may be also mean 
that it was already aborted.`,
+                        description: resp.detail.hint as TranslatedString,
+                        debug: resp.detail,
+                      })
+                      case "wrong-tan-or-credential": return notify({
+                        type: "error",
+                        title: i18n.str`Invalid code or credentials.`,
+                        description: resp.detail.hint as TranslatedString,
+                        debug: resp.detail,
+                      })
+                      case "cashout-address-changed": return notify({
+                        type: "error",
+                        title: i18n.str`The cash-out address between the 
creation and the confirmation changed.`,
+                        description: resp.detail.hint as TranslatedString,
+                        debug: resp.detail,
+                      })
+                      default: assertUnreachable(resp)
+                    }
+                  }
+                })
+              }}
+            >
+              {i18n.str`Confirm`}
+            </button>
+          </div>
+        ) : (
+          <div />
+        )}
+      </div>
+    </div>
+  );
+}
diff --git a/packages/aml-backoffice-ui/src/route.ts 
b/packages/demobank-ui/src/route.ts
similarity index 100%
copy from packages/aml-backoffice-ui/src/route.ts
copy to packages/demobank-ui/src/route.ts
diff --git a/packages/taler-util/src/http-client/bank-core.ts 
b/packages/taler-util/src/http-client/bank-core.ts
index 033b78f67..c49a094e4 100644
--- a/packages/taler-util/src/http-client/bank-core.ts
+++ b/packages/taler-util/src/http-client/bank-core.ts
@@ -166,7 +166,7 @@ export class TalerCoreBankHttpClient {
    * 
    */
   async updatePassword(auth: UserAndToken, body: 
TalerCorebankApi.AccountPasswordChange) {
-    const url = new URL(`accounts/${auth.username}`, this.baseUrl);
+    const url = new URL(`accounts/${auth.username}/auth`, this.baseUrl);
     const resp = await this.httpLib.fetch(url.href, {
       method: "PATCH",
       body,
@@ -179,6 +179,8 @@ export class TalerCoreBankHttpClient {
       //FIXME: missing in docs
       case HttpStatusCode.NotFound: return opKnownFailure("not-found", resp);
       //FIXME: missing in docs
+      case HttpStatusCode.Unauthorized: return opKnownFailure("no-rights", 
resp);
+      //FIXME: missing in docs
       case HttpStatusCode.Forbidden: return opKnownFailure("unauthorized", 
resp);
       default: return opUnknownFailure(resp, await resp.text())
     }
@@ -241,6 +243,8 @@ export class TalerCoreBankHttpClient {
     });
     switch (resp.status) {
       case HttpStatusCode.Ok: return opSuccess(resp, codecForAccountData())
+      //FIXME: missing in docs (401 when not found?)
+      case HttpStatusCode.Unauthorized: return opKnownFailure("not-found", 
resp);
       //FIXME: missing in docs
       case HttpStatusCode.NotFound: return opKnownFailure("not-found", resp);
       //FIXME: missing in docs
diff --git a/packages/taler-util/src/http-client/types.ts 
b/packages/taler-util/src/http-client/types.ts
index 5a76981df..b0d6c163b 100644
--- a/packages/taler-util/src/http-client/types.ts
+++ b/packages/taler-util/src/http-client/types.ts
@@ -1017,9 +1017,12 @@ export namespace TalerCorebankApi {
 
 
   export interface AccountPasswordChange {
-
+    // FIXME: missing in docs
     // New password.
     new_password: string;
+    // Old password. If present, chec that the old password matches.
+    // Optional for admin account.
+    old_password?: string;
   }
 
   export interface PublicAccountsResponse {
diff --git a/packages/web-util/src/utils/http-impl.browser.ts 
b/packages/web-util/src/utils/http-impl.browser.ts
index f19d1fe61..db789a536 100644
--- a/packages/web-util/src/utils/http-impl.browser.ts
+++ b/packages/web-util/src/utils/http-impl.browser.ts
@@ -69,7 +69,7 @@ export class BrowserHttpLib implements HttpRequestLibrary {
     }
 
     let myBody: ArrayBuffer | undefined =
-      requestMethod === "POST" || requestMethod === "PUT"
+      requestMethod === "POST" || requestMethod === "PUT" || requestMethod === 
"PATCH"
         ? encodeBody(requestBody)
         : undefined;
 

-- 
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]