gnunet-svn
[Top][All Lists]
Advanced

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

[taler-merchant-backoffice] 02/02: some fixes


From: gnunet
Subject: [taler-merchant-backoffice] 02/02: some fixes
Date: Thu, 27 May 2021 16:02:47 +0200

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

sebasjm pushed a commit to branch master
in repository merchant-backoffice.

commit 8e91c94d4733e035d5a078461205e86641190e70
Author: Sebastian <sebasjm@gmail.com>
AuthorDate: Thu May 27 10:36:56 2021 -0300

    some fixes
    
     - api key remove from login, and bearer
     - 'secret-token:'  add missing, remove when the user set
     - change the password and stay there logged in
     - auth token section
     - header with the instance id
     - create a section with the auth token
     - bug after clicking change, cycliing over change the password
     - reserve created suffcefluy
     - message => wire transfer subject
     - reservers
     - exchange https:// pattern (wallet util base url helper)
     - valid wire method
     - add missing titles in navbar
     - loading spinner on slow backend
---
 CHANGELOG.md                                       | 136 +++---------
 packages/frontend/src/InstanceRoutes.tsx           |  17 +-
 .../frontend/src/components/exception/loading.tsx  |  10 +-
 .../frontend/src/components/exception/login.tsx    |  11 +-
 .../frontend/src/components/form/InputSecured.tsx  |  74 +++++--
 packages/frontend/src/components/menu/index.tsx    |  10 +-
 packages/frontend/src/components/modal/index.tsx   |  14 +-
 .../notifications/CreatedSuccessfully.tsx          |   4 +-
 .../src/components/product/ProductForm.tsx         |   2 +-
 packages/frontend/src/context/backend.ts           |   6 +-
 packages/frontend/src/context/instance.ts          |   1 +
 packages/frontend/src/declaration.d.ts             |  45 ++++
 packages/frontend/src/hooks/instance.ts            |  10 +-
 .../frontend/src/paths/admin/create/CreatePage.tsx |   3 +-
 .../paths/instance/orders/create/CreatePage.tsx    |  66 +++---
 .../src/paths/instance/orders/create/index.tsx     |   5 -
 .../src/paths/instance/products/list/Table.tsx     |   2 +-
 .../paths/instance/reserves/create/CreatePage.tsx  | 128 ++++++++++--
 .../reserves/create/CreatedSuccessfully.tsx        |  39 +++-
 .../src/paths/instance/reserves/create/index.tsx   |   8 +-
 .../paths/instance/reserves/details/DetailPage.tsx |  22 +-
 .../instance/reserves/list/AutorizeTipModal.tsx    |  23 ++-
 .../src/paths/instance/reserves/list/Table.tsx     | 227 +++++++++------------
 .../src/paths/instance/transfers/list/index.tsx    |  37 ++--
 .../src/paths/instance/update/UpdatePage.tsx       |  90 ++++++--
 .../frontend/src/paths/instance/update/index.tsx   |  15 +-
 packages/frontend/src/utils/amount.ts              |  41 ++--
 packages/frontend/src/utils/constants.ts           |   1 +
 28 files changed, 611 insertions(+), 436 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 45bfb40..ad3634d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -17,8 +17,6 @@ and this project adheres to [Semantic 
Versioning](https://semver.org/spec/v2.0.0
  - prune scss styles to reduce size
  - fix mobile: some things are still on the left
  - edit button to go to instance settings
- - check if there is a way to remove auto async for /routes 
/components/{async,routes} so it can be turned on when building 
non-single-bundle
- - navigation to another instance should not do full refresh
  - cleanup instance and token management, because code is a mess and can be 
refactored 
 
  - unlock a product when is locked
@@ -26,132 +24,44 @@ and this project adheres to [Semantic 
Versioning](https://semver.org/spec/v2.0.0
  - translation missing: yup (check for some ot  her dynamic message)
  - contract terms
  - fulfillment url should check absolute url or relative to the merchant domain
- - duplicate order button
- - simplify order 
- - react routing refactor to use query parameters from history
  - create taler ui
  - contract terms in the wallet
- - when backoffice get a response of the merchant that have info about the 
reponsse of the exchange, the error is not readed correctly... see wire transfer
+ - when backoffice get a response of the merchant that have info about the 
reponse of the exchange, the error is not readed correctly... see wire transfer
  - when creating the first default instance, the page keeps reloading 
preventing for filling the form
  - 
- - payto auto remove (add if is missing)
- - back cancel in all dialog (order)
- - transfer use the tabs instead of three state
- - spining while working, 
- - disabled button grey out
- - add timeout to the request
- - hash
 
- - delete button, without 
- - delete transaction if verified is missing
+ - api key remove from login, and bearer
+ - 'secret-token:'  add missing, remove when the user set
+ - change the password and stay there logged in
+ - auth token section
+ - header with the instance id
+ - create a section with the auth token
+ - bug after clicking change, cycliing over change the password  
+ - reserve created suffcefluy
+ - message => wire transfer subject
+ - reservers
+ - exchange https:// pattern (wallet util base url helper)
+ - valid wire method
 
- - update instance
- - auth token: status => external/managed
+ - create instance with auth token
+ - add more information about reserve when is not founded
+ - replace manipulation of amount with taler util libraries
 
+ - check, not running the backend, load the SPA, then start running the 
backend. after this click the login button should enter the app but doesnt do 
anything
 
- - price not required: product page has required field that should not be 
required
- - animation on load
+
+wallet
+
+ - show transaction with error state
+ - add developer mode in settings, this will show debug tab
+ - add transaction details, and delete button
 
 ## [Unreleased]
- - fixed bug when updating token and not admin
- - showing a yellow bar on non-default instance navigation (admin)
-
-## [0.0.6] - 2021-03-25
- - complete order list information (#6793)
- - complete product list information (#6792)
- - missing fields in the instance update
- - https://bugs.gnunet.org/view.php?id=6815
- 
-
-## [0.0.5] - 2021-03-18
- - change the admin title to "instances" if we are listing the instances and 
"settings: $ID" on updating instances (#6790)
- - update title with: Taler Backoffice: $PAGE_TITLE (#6790)
- - paths should be /orders instead of /o (same others)
- - if there is enough space for tables in mobile, make the scrollables (#6789)
- - show create default instance if it is not already
- - notifications should tale place between title and content, and not disapear 
(#6788)
- - create a loading page to be use when the data is not ready (test at 
#/loading)
- - confirmation page when creating instances
-
-## [0.0.4] - 2021-03-11
- - prevent letters to be input in numbers
- - instance id in instance list should be clickable
- - edit button to go to instance settings
- - add order section
- - add product section
- - add tips section
- - add transfers section
- - initial state before login
- - logout takes you to a initial state, not showing error messages
- - change the admin title to "instances" if we are listing the instances and 
"settings: $ID" on updating instances
- - update title with: Taler Backoffice: $PAGE_TITLE
-
-## [0.0.3] - 2021-03-04
- - submit form on key press == enter
- - version of backoffice in sidebar
- - fixed login dialog on mobile
- - LangSelector ascomponent
- - refactored Navigation and Sidebar
- - do not display Logout when there is no token
- - fix: Login Page should show on unauthorized
- - fix: row clicking on card table was overriding checkbox onClick
- - remove headers of the page
- - clear all tokens now remove backend-url
- - remove checkbox from auth token, use button (manage auth)
- - auth token config as popup with 3 actions (clear (sure?), cancel, set token)
- - new password enpoint
- - bug: there is missing a mutate call when updating to remove the instance 
from cache
- 
-
-## [0.0.2] - 2021-02-25
- - REFACTOR: remove react-i18n and implement messageformat
- - REFACTOR: routes definitions to allow nested routes and tokens
- - REFACTOR: remove yup from input form defitions
- - added PORT environment variable for `make dev` and `make serve` 
- - added `make dist` and `make install`
- - remove last '/' on the backend url 
- - what happend if cannot access the config
- - reorder the fields from the address/juriction section (take example)
- - save every auth token of different instances
- - remove footer
- - show the connection state (url, currency, version) in the sidebar
- - add backend url without slash
- - added linter rule for source header
- - bug: set text int the intpu date (seconds)
- - row in the list instance are now clickable
- - re implemented the language selector, remove the current lang from the 
dropdown
- - remove payment adress and public key from instance listing
- - fix bug on CORS error
- - moved the login button to the sidebar
- - remove the details page, go directly to the update page
- - login modal: url before token, and removed the checkbox
- - added payto:// to the field
- - validate payto_uris on add
 
 ## [0.0.1] - 2021-02-18
 ### Changed
- - button of the form to the right
- - add supported currency for Amount fields (like taler bank)
- - rename name to business name
- - change auth field to have a checkbox that activate the validation and show 
an input to set the token
- - rename PayTo URI to bank account
- - change id input to reflect that is going to be use in the url (prepend the 
backend url as a non editable and put the input after)
- - refactor: change create popup to create page
- - add the information popup into the fields to help with the description 
(like https://b2b.dab-bank.de/smartbroker/)
- - take default lang from the browser for localization  
- - refactor update page
- - Login button should be centered
- - replace default exports for named exports
 
 ### Added
- - implement taler built system (bootstrap, configure, makefile)
- - implement pnpm
- - take the url where the spa was loaded as a default backend url 
- - let the user change the backend url when ask for the auth token
- - take the currency from merchant-backend
- - change the input PayTO URI to a string field with a + button to add more
- - format duration as human readable
- - add copyright headers to every source file
 
 ### Deprecated
 
diff --git a/packages/frontend/src/InstanceRoutes.tsx 
b/packages/frontend/src/InstanceRoutes.tsx
index 402c760..a7e4227 100644
--- a/packages/frontend/src/InstanceRoutes.tsx
+++ b/packages/frontend/src/InstanceRoutes.tsx
@@ -95,17 +95,20 @@ export function InstanceRoutes({ id, admin }: Props): VNode 
{
     addTokenCleaner(cleaner);
   }, [addTokenCleaner, cleaner]);
 
-  const updateLoginStatus = (url: string, token?: string) => {
-    changeBackend(url);
-    if (!token) return
+  const changeToken = (token?:string) => {
     if (admin) {
       updateToken(token);
     } else {
       updateDefaultToken(token)
     }
+  }
+  const updateLoginStatus = (url: string, token?: string) => {
+    changeBackend(url);
+    if (!token) return
+    changeToken(token)
   };
 
-  const value = useMemo(() => ({ id, token, admin }), [id, token, admin])
+  const value = useMemo(() => ({ id, token, admin, changeToken }), [id, token, 
admin])
 
   const ServerErrorRedirectTo = (to: InstancePaths | AdminPaths) => (error: 
HttpError) => {
     setGlobalNotification({
@@ -297,14 +300,14 @@ export function Redirect({ to }: { to: string }): null {
 }
 
 function AdminInstanceUpdatePage({ id, ...rest }: { id: string } & 
InstanceUpdatePageProps) {
-  const [token, updateToken] = useBackendInstanceToken(id);
-  const value = useMemo(() => ({ id, token, admin: true }), [id, token])
+  const [token, changeToken] = useBackendInstanceToken(id);
   const { changeBackend } = useBackendContext();
   const updateLoginStatus = (url: string, token?: string) => {
     changeBackend(url);
     if (token)
-      updateToken(token);
+    changeToken(token);
   };
+  const value = useMemo(() => ({ id, token, admin: true, changeToken }), [id, 
token])
   const i18n = useTranslator();
   return <InstanceContextProvider value={value}>
     <InstanceUpdatePage {...rest}
diff --git a/packages/frontend/src/components/exception/loading.tsx 
b/packages/frontend/src/components/exception/loading.tsx
index 40c7c7b..f2139a1 100644
--- a/packages/frontend/src/components/exception/loading.tsx
+++ b/packages/frontend/src/components/exception/loading.tsx
@@ -23,10 +23,10 @@ import { h, VNode } from "preact";
 
 export function Loading(): VNode {
   return <div class="columns is-centered is-vcentered" style={{ height: 
'calc(100% - 3rem)', position: 'absolute', width: '100%' }}>
-    <div class="column is-one-fifth">
-      <div class="lds-ring">
-        <div /><div /><div /><div />
-      </div>
-    </div>
+    <Spinner />
   </div>
+}
+
+export function Spinner(): VNode {
+  return <div class="lds-ring"><div /><div /><div /><div /></div>
 }
\ No newline at end of file
diff --git a/packages/frontend/src/components/exception/login.tsx 
b/packages/frontend/src/components/exception/login.tsx
index 1132bfe..611af1a 100644
--- a/packages/frontend/src/components/exception/login.tsx
+++ b/packages/frontend/src/components/exception/login.tsx
@@ -36,6 +36,13 @@ export function LoginModal({ onConfirm, withMessage }: 
Props): VNode {
   const { admin, token: instanceToken } = useInstanceContext()
   const [token, setToken] = useState(!admin ? baseToken : instanceToken || '')
   
+  function updateToken(token:string) {
+    const value = token && token.startsWith('secret-token:')?
+      token.substring('secret-token:'.length) : token
+
+    setToken(`secret-token:${value}`)
+  }
+
   const [url, setURL] = useState(backendUrl)
   const i18n = useTranslator()
 
@@ -46,7 +53,7 @@ export function LoginModal({ onConfirm, withMessage }: 
Props): VNode {
           <p class="modal-card-title">{i18n`Login required`}</p>
         </header>
         <section class="modal-card-body" style={{ border: '1px solid', 
borderTop: 0, borderBottom: 0 }}>
-          {i18n`Please enter your auth token. Token should have 
"secret-token:" and start with Bearer or ApiKey`}
+          {i18n`Please enter your auth token.`}
           <div class="field is-horizontal">
             <div class="field-label is-normal">
               <label class="label">URL</label>
@@ -73,7 +80,7 @@ export function LoginModal({ onConfirm, withMessage }: 
Props): VNode {
                   <input class="input" type="text" placeholder={"set new 
token"} name="token"
                     onKeyPress={e => e.keyCode === 13 ? onConfirm(url, token ? 
token : undefined) : null}
                     value={token} 
-                    onInput={(e): void => setToken(e?.currentTarget.value)}
+                    onInput={(e): void => updateToken(e?.currentTarget.value)}
                   />
                 </p>
               </div>
diff --git a/packages/frontend/src/components/form/InputSecured.tsx 
b/packages/frontend/src/components/form/InputSecured.tsx
index 6e3059b..64737e3 100644
--- a/packages/frontend/src/components/form/InputSecured.tsx
+++ b/packages/frontend/src/components/form/InputSecured.tsx
@@ -21,7 +21,6 @@
 import { Fragment, h, VNode } from "preact";
 import { useState } from "preact/hooks";
 import { Translate } from "../../i18n";
-import { UpdateTokenModal } from "../modal";
 import { InputProps, useField } from "./useField";
 
 export type Props<T> = InputProps<T>;
@@ -40,7 +39,7 @@ export function InputSecured<T>({ name, readonly, 
placeholder, tooltip, label, h
   const { error, value, initial, onChange, toStr, fromStr } = 
useField<T>(name);
 
   const [active, setActive] = useState(false);
-  const [newValue, setNuewValue] = useState("");
+  const [newValue, setNuewValue] = useState("")
 
   return <Fragment>
     <div class="field is-horizontal">
@@ -53,22 +52,65 @@ export function InputSecured<T>({ name, readonly, 
placeholder, tooltip, label, h
         </label>
       </div>
       <div class="field-body is-flex-grow-3">
-        <div class="field has-addons">
-          <button class="button" onClick={(): void => { setActive(!active); }} 
>
-            <div class="icon is-left"><i class="mdi mdi-lock-reset" /></div>
-            <span><Translate>Manage token</Translate></span>
-          </button>
-          <TokenStatus prev={initial} post={value} />
-        </div>
+        {!active ?
+          <Fragment>
+            <div class="field has-addons">
+              <button class="button" onClick={(): void => { 
setActive(!active); }} >
+                <div class="icon is-left"><i class="mdi mdi-lock-reset" 
/></div>
+                <span><Translate>Manage token</Translate></span>
+              </button>
+              <TokenStatus prev={initial} post={value} />
+            </div>
+          </Fragment> :
+          <Fragment>
+            <div class="field has-addons">
+              <div class="control">
+                <a class="button is-static">secret-token:</a>
+              </div>
+              <div class="control is-expanded">
+                <input class="input" type="text"
+                  placeholder={placeholder} readonly={readonly || !active}
+                  disabled={readonly || !active}
+                  name={String(name)} value={newValue}
+                  onInput={(e): void => {
+                    setNuewValue(e.currentTarget.value)
+                  }} />
+                {help}
+              </div>
+              <div class="control">
+                <button class="button is-info" disabled={fromStr(newValue) === 
value} onClick={(): void => { onChange(fromStr(newValue)); setActive(!active); 
setNuewValue(""); }} >
+                  <div class="icon is-left"><i class="mdi mdi-lock-outline" 
/></div>
+                  <span><Translate>Update</Translate></span>
+                </button>
+              </div>
+            </div>
+          </Fragment>
+        }
         {error ? <p class="help is-danger">{error}</p> : null}
       </div>
     </div>
-    {active && <UpdateTokenModal oldToken={initial}
-      onCancel={() => { onChange(initial!); setActive(false); }}
-      onClear={() => { onChange(null!); setActive(false); }}
-      onConfirm={(newToken) => { 
-        onChange(newToken as any); setActive(false)
-       }}
-    />}
+    {active &&
+      <div class="field is-horizontal">
+        <div class="field-body is-flex-grow-3">
+          <div class="level" style={{ width: '100%' }}>
+            <div class="level-right is-flex-grow-1">
+              <div class="level-item">
+                <button class="button is-danger" disabled={null === value || 
undefined === value} onClick={(): void => { onChange(null!); 
setActive(!active); setNuewValue(""); }} >
+                  <div class="icon is-left"><i class="mdi 
mdi-lock-open-variant" /></div>
+                  <span><Translate>Remove</Translate></span>
+                </button>
+              </div>
+              <div class="level-item">
+                <button class="button " onClick={(): void => { 
onChange(initial!); setActive(!active); setNuewValue(""); }} >
+                  <div class="icon is-left"><i class="mdi 
mdi-lock-open-variant" /></div>
+                  <span><Translate>Cancel</Translate></span>
+                </button>
+              </div>
+            </div>
+
+          </div>
+        </div>
+      </div>
+    }
   </Fragment >;
 }
diff --git a/packages/frontend/src/components/menu/index.tsx 
b/packages/frontend/src/components/menu/index.tsx
index 5140eb0..31826ed 100644
--- a/packages/frontend/src/components/menu/index.tsx
+++ b/packages/frontend/src/components/menu/index.tsx
@@ -30,16 +30,16 @@ function getInstanceTitle(path: string, id: string): string 
{
     // case InstancePaths.details: return `${id}`
     case InstancePaths.update: return `${id}: Settings`
     case InstancePaths.order_list: return `${id}: Orders`
-    // case InstancePaths.order_new: return `${id}: New order`
+    case InstancePaths.order_new: return `${id}: New order`
     case InstancePaths.order_details: return `${id}: Detail of the order`
     case InstancePaths.product_list: return `${id}: Products`
     case InstancePaths.product_new: return `${id}: New product`
     case InstancePaths.product_update: return `${id}: Update product`
-    // case InstancePaths.tips_list: return `${id}: Tips`
-    // case InstancePaths.tips_new: return `${id}: New tip`
-    // case InstancePaths.tips_update: return `${id}: Update tip`
+    case InstancePaths.reserves_details: return `${id}: Detail of a reserve`
+    case InstancePaths.reserves_new: return `${id}: New reserve`
+    case InstancePaths.reserves_list: return `${id}: Reserves`
     case InstancePaths.transfers_list: return `${id}: Transfers`
-    // case InstancePaths.transfers_new: return `${id}: New Transfer`
+    case InstancePaths.transfers_new: return `${id}: New transfer`
     default: return '';
   }
 }
diff --git a/packages/frontend/src/components/modal/index.tsx 
b/packages/frontend/src/components/modal/index.tsx
index 747019c..cba1ce8 100644
--- a/packages/frontend/src/components/modal/index.tsx
+++ b/packages/frontend/src/components/modal/index.tsx
@@ -25,6 +25,7 @@ import { useState } from "preact/hooks";
 import { useInstanceContext } from "../../context/instance";
 import { Translate, useTranslator } from "../../i18n";
 import { DEFAULT_REQUEST_TIMEOUT } from "../../utils/constants";
+import { Loading, Spinner } from "../exception/loading";
 import { FormProvider } from "../form/FormProvider";
 import { Input } from "../form/Input";
 
@@ -167,20 +168,23 @@ export function UpdateTokenModal({ onCancel, onClear, 
onConfirm, oldToken }: Upd
 
 export function LoadingModal({ onCancel }: { onCancel: () => void }): VNode {
   const i18n = useTranslator()
-  return <div class={"modal is-active"}>
+  return <div class="modal is-active">
     <div class="modal-background " onClick={onCancel} />
     <div class="modal-card">
       <header class="modal-card-head">
-        <p class="modal-card-title"><Translate>Operation is taking to much 
time</Translate></p>
+        <p class="modal-card-title"><Translate>Operation in 
progress...</Translate></p>
       </header>
       <section class="modal-card-body">
-        <p><Translate>You can wait a little longer or abort the request to the 
backend. If the problem persist
-        contact the administrator.</Translate></p>
+        <div class="columns">
+          <div class="column" />
+          <Spinner />
+          <div class="column" />
+        </div>
         <p>{i18n`The operation will be automatically canceled after 
${DEFAULT_REQUEST_TIMEOUT} seconds`}</p>
       </section>
       <footer class="modal-card-foot">
         <div class="buttons is-right" style={{ width: '100%' }}>
-          <button class="button " onClick={onCancel} 
><Translate>Abort</Translate></button>
+          <button class="button " onClick={onCancel} 
><Translate>Cancel</Translate></button>
         </div>
       </footer>
     </div>
diff --git 
a/packages/frontend/src/components/notifications/CreatedSuccessfully.tsx 
b/packages/frontend/src/components/notifications/CreatedSuccessfully.tsx
index 8e2eee2..5f10e4b 100644
--- a/packages/frontend/src/components/notifications/CreatedSuccessfully.tsx
+++ b/packages/frontend/src/components/notifications/CreatedSuccessfully.tsx
@@ -26,9 +26,9 @@ interface Props {
 }
 
 export function CreatedSuccessfully({ children, onConfirm, onCreateAnother }: 
Props): VNode {
-  return <div class="columns is-fullwidth is-vcentered content-full-size">
+  return <div class="columns is-fullwidth is-vcentered mt-3">
     <div class="column" />
-    <div class="column is-three-quarters">
+    <div class="column is-four-fifths">
       <div class="card">
         <header class="card-header has-background-success">
           <p class="card-header-title has-text-white-ter">
diff --git a/packages/frontend/src/components/product/ProductForm.tsx 
b/packages/frontend/src/components/product/ProductForm.tsx
index 2729247..f6d52a7 100644
--- a/packages/frontend/src/components/product/ProductForm.tsx
+++ b/packages/frontend/src/components/product/ProductForm.tsx
@@ -95,7 +95,7 @@ export function ProductForm({ onSubscribe, initial, 
alreadyExist, }: Props) {
   return <div>
     <FormProvider<Entity> name="product" errors={errors} object={value} 
valueHandler={valueHandler} >
 
-      {alreadyExist ? undefined : <InputWithAddon<Entity> name="product_id" 
addonBefore={`${backend.url}/product/`} label={i18n`ID`} tooltip={i18n`unique 
name identification`} />}
+      {alreadyExist ? undefined : <InputWithAddon<Entity> name="product_id" 
addonBefore={`${backend.url}/product/`} label={i18n`ID`} tooltip={i18n`display 
name identification`} />}
 
       <InputImage<Entity> name="image" label={i18n`Image`} tooltip={i18n`photo 
of the product`} />
       <Input<Entity> name="description" inputType="multiline" 
label={i18n`Description`} tooltip={i18n`full-length description`} />
diff --git a/packages/frontend/src/context/backend.ts 
b/packages/frontend/src/context/backend.ts
index e33cdd5..c1b2c14 100644
--- a/packages/frontend/src/context/backend.ts
+++ b/packages/frontend/src/context/backend.ts
@@ -47,7 +47,11 @@ const BackendContext = createContext<BackendContextType>({
 
 export function useBackendContextState(): BackendContextType {
   const [url, triedToLog, changeBackend, resetBackend] = useBackendURL();
-  const [token, updateToken] = useBackendDefaultToken();
+  const [token, _updateToken] = useBackendDefaultToken();
+  const updateToken = (t?:string) => {
+    // console.log("update token", t)
+    _updateToken(t)
+  }
 
   const tokenCleaner = useCallback(() => { updateToken(undefined) }, [])
   const [cleaners, setCleaners] = useState([tokenCleaner])
diff --git a/packages/frontend/src/context/instance.ts 
b/packages/frontend/src/context/instance.ts
index 0edfbfe..fecf364 100644
--- a/packages/frontend/src/context/instance.ts
+++ b/packages/frontend/src/context/instance.ts
@@ -26,6 +26,7 @@ interface Type {
   id: string;
   token?: string;
   admin?: boolean;
+  changeToken: (t?:string) => void;
 }
 
 const Context = createContext<Type>({} as any)
diff --git a/packages/frontend/src/declaration.d.ts 
b/packages/frontend/src/declaration.d.ts
index 04a2e2d..6717566 100644
--- a/packages/frontend/src/declaration.d.ts
+++ b/packages/frontend/src/declaration.d.ts
@@ -51,6 +51,51 @@ type Amount = string;
 type UUID = string;
 type Integer = number;
 
+export namespace ExchangeBackend {
+    interface WireResponse {
+
+        // Master public key of the exchange, must match the key returned in 
/keys.
+        master_public_key: EddsaPublicKey;
+      
+        // Array of wire accounts operated by the exchange for
+        // incoming wire transfers.
+        accounts: WireAccount[];
+      
+        // Object mapping names of wire methods (i.e. "sepa" or "x-taler-bank")
+        // to wire fees.
+        fees: { method : AggregateTransferFee };
+      }
+      interface WireAccount {
+        // payto:// URI identifying the account and wire method
+        payto_uri: string;
+      
+        // Signature using the exchange's offline key
+        // with purpose TALER_SIGNATURE_MASTER_WIRE_DETAILS.
+        master_sig: EddsaSignature;
+      }
+      interface AggregateTransferFee {
+        // Per transfer wire transfer fee.
+        wire_fee: Amount;
+      
+        // Per transfer closing fee.
+        closing_fee: Amount;
+      
+        // What date (inclusive) does this fee go into effect?
+        // The different fees must cover the full time period in which
+        // any of the denomination keys are valid without overlap.
+        start_date: Timestamp;
+      
+        // What date (exclusive) does this fee stop going into effect?
+        // The different fees must cover the full time period in which
+        // any of the denomination keys are valid without overlap.
+        end_date: Timestamp;
+      
+        // Signature of TALER_MasterWireFeePS with
+        // purpose TALER_SIGNATURE_MASTER_WIRE_FEES.
+        sig: EddsaSignature;
+      }
+      
+}
 export namespace MerchantBackend {
     interface ErrorDetail {
 
diff --git a/packages/frontend/src/hooks/instance.ts 
b/packages/frontend/src/hooks/instance.ts
index 4bb6410..2e5c6c2 100644
--- a/packages/frontend/src/hooks/instance.ts
+++ b/packages/frontend/src/hooks/instance.ts
@@ -21,7 +21,7 @@ import { useInstanceContext } from '../context/instance';
 
 
 interface InstanceAPI {
-  updateInstance: (data: 
MerchantBackend.Instances.InstanceReconfigurationMessage, a?: 
MerchantBackend.Instances.InstanceAuthConfigurationMessage) => Promise<void>;
+  updateInstance: (data: 
MerchantBackend.Instances.InstanceReconfigurationMessage) => Promise<void>;
   deleteInstance: () => Promise<void>;
   clearToken: () => Promise<void>;
   setNewToken: (token: string) => Promise<void>;
@@ -33,19 +33,13 @@ export function useInstanceAPI(): InstanceAPI {
 
   const url = !admin ? baseUrl : `${baseUrl}/instances/${id}`
 
-  const updateInstance = async (instance: 
MerchantBackend.Instances.InstanceReconfigurationMessage, auth?: 
MerchantBackend.Instances.InstanceAuthConfigurationMessage): Promise<void> => {
+  const updateInstance = async (instance: 
MerchantBackend.Instances.InstanceReconfigurationMessage): Promise<void> => {
     await request(`${url}/private/`, {
       method: 'patch',
       token,
       data: instance
     })
 
-    if (auth) await request(`${url}/private/auth`, {
-      method: 'post',
-      token,
-      data: auth
-    })
-
     if (adminToken) mutate(['/private/instances', adminToken, baseUrl], null)
     mutate([`/private/`, token, url], null)
   };
diff --git a/packages/frontend/src/paths/admin/create/CreatePage.tsx 
b/packages/frontend/src/paths/admin/create/CreatePage.tsx
index 4cf4b3c..aa46f2f 100644
--- a/packages/frontend/src/paths/admin/create/CreatePage.tsx
+++ b/packages/frontend/src/paths/admin/create/CreatePage.tsx
@@ -56,7 +56,6 @@ function with_defaults(id?: string): Partial<Entity> {
 
 export function CreatePage({ onCreate, onBack, forceId }: Props): VNode {
   const [value, valueHandler] = useState(with_defaults(forceId))
-  // const [errors, setErrors] = useState<FormErrors<Entity>>({})
 
   let errors: FormErrors<Entity> = {}
   try {
@@ -86,7 +85,7 @@ export function CreatePage({ onCreate, onBack, forceId }: 
Props): VNode {
         <div class="column is-two-thirds">
           <FormProvider<Entity> errors={errors} object={value} 
valueHandler={valueHandler} >
 
-            <InputWithAddon<Entity> name="id" label={i18n`ID`} 
addonBefore={`${backend.url}/private/instances/`} readonly={!!forceId} 
tooltip={i18n`unique name identification`} />
+            <InputWithAddon<Entity> name="id" label={i18n`ID`} 
addonBefore={`${backend.url}/private/instances/`} readonly={!!forceId} 
tooltip={i18n`display name identification`} />
 
             <Input<Entity> name="name" label={i18n`Name`} 
tooltip={i18n`descriptive name`} />
 
diff --git a/packages/frontend/src/paths/instance/orders/create/CreatePage.tsx 
b/packages/frontend/src/paths/instance/orders/create/CreatePage.tsx
index 140d0c5..9d22fc8 100644
--- a/packages/frontend/src/paths/instance/orders/create/CreatePage.tsx
+++ b/packages/frontend/src/paths/instance/orders/create/CreatePage.tsx
@@ -88,43 +88,45 @@ interface Entity {
 
 export function CreatePage({ onCreate, onBack }: Props): VNode {
   const [value, valueHandler] = useState(with_defaults())
-  const [errors, setErrors] = useState<FormErrors<Entity>>({})
+  // const [errors, setErrors] = useState<FormErrors<Entity>>({})
 
   const inventoryList = Object.values(value.inventoryProducts)
   const productList = Object.values(value.products)
 
+  let errors: FormErrors<Entity> = {}
+  try {
+    schema.validateSync(value, { abortEarly: false })
+  } catch (err) {
+    const yupErrors = err.inner as yup.ValidationError[]
+    errors = yupErrors.reduce((prev, cur) => !cur.path ? prev : ({ ...prev, 
[cur.path]: cur.message }), {})
+  }
+  const hasErrors = Object.keys(errors).some(k => (errors as any)[k] !== 
undefined)
+
   const submit = (): void => {
-    try {
-      schema.validateSync(value, { abortEarly: false })
-      const order = schema.cast(value)
-
-      const request: MerchantBackend.Orders.PostOrderRequest = {
-        order: {
-          amount: order.pricing.order_price,
-          summary: order.pricing.summary,
-          products: productList,
-          extra: value.extra,
-          pay_deadline: value.payments.pay_deadline ? { t_ms: 
Math.floor(value.payments.pay_deadline.getTime() / 1000) * 1000 } : undefined,
-          wire_transfer_deadline: value.payments.pay_deadline ? { t_ms: 
Math.floor(value.payments.pay_deadline.getTime() / 1000) * 1000 } : undefined,
-          refund_deadline: value.payments.refund_deadline ? { t_ms: 
Math.floor(value.payments.refund_deadline.getTime() / 1000) * 1000 } : 
undefined,
-          max_fee: value.payments.max_fee,
-          max_wire_fee: value.payments.max_wire_fee,
-          delivery_date: value.payments.delivery_date ? { t_ms: 
value.payments.delivery_date.getTime() } : undefined,
-          delivery_location: value.payments.delivery_location,
-          fulfillment_url: value.payments.fullfilment_url,
-        },
-        inventory_products: inventoryList.map(p => ({
-          product_id: p.product.id,
-          quantity: p.quantity
-        })),
-      }
-
-      onCreate(request);
-    } catch (err) {
-      const errors = err.inner as yup.ValidationError[]
-      const pathMessages = errors.reduce((prev, cur) => !cur.path ? prev : ({ 
...prev, [cur.path]: cur.message }), {})
-      setErrors(pathMessages)
+    const order = schema.cast(value)
+
+    const request: MerchantBackend.Orders.PostOrderRequest = {
+      order: {
+        amount: order.pricing.order_price,
+        summary: order.pricing.summary,
+        products: productList,
+        extra: value.extra,
+        pay_deadline: value.payments.pay_deadline ? { t_ms: 
Math.floor(value.payments.pay_deadline.getTime() / 1000) * 1000 } : undefined,
+        wire_transfer_deadline: value.payments.pay_deadline ? { t_ms: 
Math.floor(value.payments.pay_deadline.getTime() / 1000) * 1000 } : undefined,
+        refund_deadline: value.payments.refund_deadline ? { t_ms: 
Math.floor(value.payments.refund_deadline.getTime() / 1000) * 1000 } : 
undefined,
+        max_fee: value.payments.max_fee,
+        max_wire_fee: value.payments.max_wire_fee,
+        delivery_date: value.payments.delivery_date ? { t_ms: 
value.payments.delivery_date.getTime() } : undefined,
+        delivery_location: value.payments.delivery_location,
+        fulfillment_url: value.payments.fullfilment_url,
+      },
+      inventory_products: inventoryList.map(p => ({
+        product_id: p.product.id,
+        quantity: p.quantity
+      })),
     }
+
+    onCreate(request);
   }
 
   const config = useConfigContext()
@@ -313,7 +315,7 @@ export function CreatePage({ onCreate, onBack }: Props): 
VNode {
 
           <div class="buttons is-right mt-5">
             {onBack && <button class="button" onClick={onBack} 
><Translate>Cancel</Translate></button>}
-            <button class="button is-success" onClick={submit} 
><Translate>Confirm</Translate></button>
+            <button class="button is-success" onClick={submit} 
disabled={hasErrors} ><Translate>Confirm</Translate></button>
           </div>
 
         </div>
diff --git a/packages/frontend/src/paths/instance/orders/create/index.tsx 
b/packages/frontend/src/paths/instance/orders/create/index.tsx
index 01d2e6c..ee0577a 100644
--- a/packages/frontend/src/paths/instance/orders/create/index.tsx
+++ b/packages/frontend/src/paths/instance/orders/create/index.tsx
@@ -42,11 +42,6 @@ export default function OrderCreate({ onConfirm, onBack }: 
Props): VNode {
 
 
   return <Fragment>
-    <NotificationCard notification={{
-      message: 'DEMO',
-      type: 'WARN',
-      description: 'this can be created as a popup or be expanded with more 
options'
-    }} />
     
     <NotificationCard notification={notif} />
 
diff --git a/packages/frontend/src/paths/instance/products/list/Table.tsx 
b/packages/frontend/src/paths/instance/products/list/Table.tsx
index 85e5ce4..878506d 100644
--- a/packages/frontend/src/paths/instance/products/list/Table.tsx
+++ b/packages/frontend/src/paths/instance/products/list/Table.tsx
@@ -242,7 +242,7 @@ function EmptyTable(): VNode {
     <p>
       <span class="icon is-large"><i class="mdi mdi-emoticon-sad mdi-48px" 
/></span>
     </p>
-    <p><Translate>There is no instances yet, add more pressing the + 
sign</Translate></p>
+    <p><Translate>There is no products yet, add more pressing the + 
sign</Translate></p>
   </div>
 }
 
diff --git 
a/packages/frontend/src/paths/instance/reserves/create/CreatePage.tsx 
b/packages/frontend/src/paths/instance/reserves/create/CreatePage.tsx
index 6c079e6..a98e665 100644
--- a/packages/frontend/src/paths/instance/reserves/create/CreatePage.tsx
+++ b/packages/frontend/src/paths/instance/reserves/create/CreatePage.tsx
@@ -19,14 +19,18 @@
 * @author Sebastian Javier Marchano (sebasjm)
 */
 
-import { h, VNode } from "preact";
-import { useState } from "preact/hooks";
+import { Fragment, h, VNode } from "preact";
+import { StateUpdater, useEffect, useState } from "preact/hooks";
 import { FormErrors, FormProvider } from 
"../../../../components/form/FormProvider";
 import { Input } from "../../../../components/form/Input";
 import { InputCurrency } from "../../../../components/form/InputCurrency";
-import { MerchantBackend } from "../../../../declaration";
+import { ExchangeBackend, MerchantBackend } from "../../../../declaration";
 import { Translate, useTranslator } from "../../../../i18n";
 import { AsyncButton } from "../../../../components/exception/AsyncButton";
+import { canonicalizeBaseUrl, ExchangeKeysJson } from "@gnu-taler/taler-util"
+import { PAYTO_WIRE_METHOD_LOOKUP, URL_REGEX } from 
"../../../../utils/constants";
+import { request } from "../../../../hooks/backend";
+import { InputSelector } from "../../../../components/form/InputSelector";
 
 type Entity = MerchantBackend.Tips.ReserveCreateRequest
 
@@ -36,38 +40,122 @@ interface Props {
 }
 
 
-export function CreatePage({ onCreate, onBack }: Props): VNode {
-  const [reserve, setReserve] = useState<Partial<Entity>>({})
+enum Steps {
+  EXCHANGE,
+  WIRE_METHOD,
+}
+
+interface ViewProps {
+  step : Steps,
+  setCurrentStep: (s:Steps) => void;
+  reserve: Partial<Entity>;
+  onBack?: () => void;
+  submitForm: () => Promise<void>;
+  setReserve: StateUpdater<Partial<Entity>>;
+}
+function ViewStep({ step, setCurrentStep, reserve, onBack, submitForm, 
setReserve }: ViewProps): VNode {
   const i18n = useTranslator()
+  const [wireMethods, setWireMethods] = useState<Array<string>>([])
+  const [exchangeQueryError, setExchangeQueryError] = 
useState<string|undefined>(undefined)
+
+  useEffect(() => {
+    setExchangeQueryError(undefined)
+  }, [reserve.exchange_url])
+
+  switch (step) {
+    case Steps.EXCHANGE: {
+      const errors: FormErrors<Entity> = {
+        initial_balance: !reserve.initial_balance ? 'cannot be empty' : 
!(parseInt(reserve.initial_balance.split(':')[1], 10) > 0) ? i18n`it should be 
greater than 0` : undefined,
+        exchange_url: !reserve.exchange_url ? i18n`cannot be empty` : 
!URL_REGEX.test(reserve.exchange_url) ? i18n`must be a valid URL` : 
!!exchangeQueryError ? exchangeQueryError : undefined,
+      }
+
+      const hasErrors = Object.keys(errors).some(k => (errors as any)[k] !== 
undefined)
+
+      return <Fragment>
+        <FormProvider<Entity> object={reserve} errors={errors} 
valueHandler={setReserve}>
+          <InputCurrency<Entity> name="initial_balance" label={i18n`Initial 
balance`} />
+          <Input<Entity> name="exchange_url" label={i18n`Exchange URL`} />
+        </FormProvider>
+
+        <div class="buttons is-right mt-5">
+          {onBack && <button class="button" onClick={onBack} 
><Translate>Cancel</Translate></button>}
+          <AsyncButton onClick={() => {
+            return 
request<ExchangeBackend.WireResponse>(`${reserve.exchange_url}wire`).then(r => {
+              const wireMethods = r.data.accounts.map(a => {
+                const match = PAYTO_WIRE_METHOD_LOOKUP.exec(a.payto_uri)
+                return match && match[1] || ''
+              })
+              setWireMethods(wireMethods)
+              setCurrentStep(Steps.WIRE_METHOD)
+              return 
+            }).catch((r: any) => {
+              setExchangeQueryError(r.message)
+            })
+          }} disabled={hasErrors} ><Translate>Next</Translate></AsyncButton>
+        </div>
+      </Fragment>
+    }
 
-  const errors: FormErrors<Entity> = {
-    initial_balance: !reserve.initial_balance ? 'cannot be empty' : 
!(parseInt(reserve.initial_balance.split(':')[1], 10) > 0) ? i18n`it should be 
greater than 0` : undefined,
-    exchange_url: !reserve.exchange_url ? i18n`cannot be empty` : undefined,
-    wire_method: !reserve.wire_method ? i18n`cannot be empty` : undefined,
+    case Steps.WIRE_METHOD: {
+      const errors: FormErrors<Entity> = {
+        wire_method: !reserve.wire_method ? i18n`cannot be empty` : undefined,
+      }
+
+      const hasErrors = Object.keys(errors).some(k => (errors as any)[k] !== 
undefined)
+      return <Fragment>
+        <FormProvider<Entity> object={reserve} errors={errors} 
valueHandler={setReserve}>
+          <InputCurrency<Entity> name="initial_balance" label={i18n`Initial 
balance`} readonly />
+          <Input<Entity> name="exchange_url" label={i18n`Exchange URL`} 
readonly />
+          <InputSelector<Entity> name="wire_method" label={i18n`Wire method`} 
values={wireMethods} placeholder={i18n`Select one wire method`}/>
+        </FormProvider>
+        <div class="buttons is-right mt-5">
+          {onBack && <button class="button" onClick={() => 
setCurrentStep(Steps.EXCHANGE)} ><Translate>Back</Translate></button>}
+          <AsyncButton onClick={submitForm} disabled={hasErrors} 
><Translate>Confirm</Translate></AsyncButton>
+        </div>
+      </Fragment>
+
+    }
   }
+}
+
+export function CreatePage({ onCreate, onBack }: Props): VNode {
+  const [reserve, setReserve] = useState<Partial<Entity>>({})
 
-  const hasErrors = Object.keys(errors).some(k => (errors as any)[k] !== 
undefined)
 
   const submitForm = () => {
-    if (hasErrors) return Promise.reject()
     return onCreate(reserve as Entity)
   }
 
+  const [currentStep, setCurrentStep] = useState(Steps.EXCHANGE)
+
+
   return <div>
     <section class="section is-main-section">
       <div class="columns">
         <div class="column" />
         <div class="column is-two-thirds">
-          <FormProvider<Entity> object={reserve} errors={errors} 
valueHandler={setReserve}>
-            <InputCurrency<Entity> name="initial_balance" label={i18n`Initial 
balance`} />
-            <Input<Entity> name="exchange_url" label={i18n`Exchange`} />
-            <Input<Entity> name="wire_method" label={i18n`Wire method`} />
-          </FormProvider>
-
-          <div class="buttons is-right mt-5">
-            {onBack && <button class="button" onClick={onBack} 
><Translate>Cancel</Translate></button>}
-            <AsyncButton onClick={submitForm} disabled={hasErrors} 
><Translate>Confirm</Translate></AsyncButton>
+
+          <div class="tabs is-toggle is-fullwidth is-small">
+            <ul>
+              <li class={currentStep === Steps.EXCHANGE?"is-active":""}>
+                <a style={{ cursor: 'initial' }}>
+                  <span>Set exchange</span>
+                </a>
+              </li>
+              <li class={currentStep === Steps.WIRE_METHOD?"is-active":""}>
+                <a style={{ cursor: 'initial' }}>
+                  <span>Set wire method</span>
+                </a>
+              </li>
+            </ul>
           </div>
+
+          <ViewStep step={currentStep} reserve={reserve} 
+            setCurrentStep={setCurrentStep}
+            setReserve={setReserve}
+            submitForm={submitForm}
+            onBack={onBack}
+           />
         </div>
         <div class="column" />
       </div>
diff --git 
a/packages/frontend/src/paths/instance/reserves/create/CreatedSuccessfully.tsx 
b/packages/frontend/src/paths/instance/reserves/create/CreatedSuccessfully.tsx
index af27c5b..2deae14 100644
--- 
a/packages/frontend/src/paths/instance/reserves/create/CreatedSuccessfully.tsx
+++ 
b/packages/frontend/src/paths/instance/reserves/create/CreatedSuccessfully.tsx
@@ -16,8 +16,9 @@
 import { h, VNode } from "preact";
 import { CreatedSuccessfully as Template } from 
"../../../../components/notifications/CreatedSuccessfully";
 import { MerchantBackend } from "../../../../declaration";
+import { Translate } from "../../../../i18n";
 
-type Entity = MerchantBackend.Tips.ReserveCreateConfirmation;
+type Entity = {request: MerchantBackend.Tips.ReserveCreateRequest, response: 
MerchantBackend.Tips.ReserveCreateConfirmation};
 
 interface Props {
   entity: Entity;
@@ -28,6 +29,30 @@ interface Props {
 export function CreatedSuccessfully({ entity, onConfirm, onCreateAnother }: 
Props): VNode {
 
   return <Template onConfirm={onConfirm} onCreateAnother={onCreateAnother}>
+    <div class="field is-horizontal">
+      <div class="field-label is-normal">
+        <label class="label">Exchange</label>
+      </div>
+      <div class="field-body is-flex-grow-3">
+        <div class="field">
+          <p class="control">
+            <input readonly class="input" value={entity.request.exchange_url} 
/>
+          </p>
+        </div>
+      </div>
+    </div>
+    <div class="field is-horizontal">
+      <div class="field-label is-normal">
+        <label class="label">Amount</label>
+      </div>
+      <div class="field-body is-flex-grow-3">
+        <div class="field">
+          <p class="control">
+            <input readonly class="input" 
value={entity.request.initial_balance} />
+          </p>
+        </div>
+      </div>
+    </div>
     <div class="field is-horizontal">
       <div class="field-label is-normal">
         <label class="label">Account address</label>
@@ -35,22 +60,28 @@ export function CreatedSuccessfully({ entity, onConfirm, 
onCreateAnother }: Prop
       <div class="field-body is-flex-grow-3">
         <div class="field">
           <p class="control">
-            <input readonly class="input" value={entity.payto_uri} />
+            <input readonly class="input" value={entity.response.payto_uri} />
           </p>
         </div>
       </div>
     </div>
     <div class="field is-horizontal">
       <div class="field-label is-normal">
-        <label class="label">Message</label>
+        <label class="label">Subject</label>
       </div>
       <div class="field-body is-flex-grow-3">
         <div class="field">
           <p class="control">
-            <input class="input" readonly value={entity.reserve_pub} />
+            <input class="input" readonly value={entity.response.reserve_pub} 
/>
           </p>
         </div>
       </div>
     </div>
+    <p class="is-size-5"><Translate>Now you should transfer to the exchange 
into the account address indicated above and the transaction must carry the 
subject message.</Translate></p>
+
+    <p class="is-size-5"><Translate>For example:</Translate></p>
+    <pre>
+    
{entity.response.payto_uri}?message={entity.response.reserve_pub}&amount={entity.request.initial_balance}
+    </pre>
   </Template>;
 }
diff --git a/packages/frontend/src/paths/instance/reserves/create/index.tsx 
b/packages/frontend/src/paths/instance/reserves/create/index.tsx
index ad990e9..eb16f85 100644
--- a/packages/frontend/src/paths/instance/reserves/create/index.tsx
+++ b/packages/frontend/src/paths/instance/reserves/create/index.tsx
@@ -28,7 +28,6 @@ import { useTranslator } from '../../../../i18n';
 import { Notification } from '../../../../utils/types';
 import { CreatedSuccessfully } from './CreatedSuccessfully';
 import { CreatePage } from './CreatePage';
-
 interface Props {
   onBack: () => void;
   onConfirm: () => void;
@@ -38,7 +37,10 @@ export default function CreateReserve({ onBack, onConfirm }: 
Props): VNode {
   const [notif, setNotif] = useState<Notification | undefined>(undefined)
   const i18n = useTranslator()
 
-  const [createdOk, setCreatedOk] = 
useState<MerchantBackend.Tips.ReserveCreateConfirmation | undefined>(undefined);
+  const [createdOk, setCreatedOk] = useState<{
+    request: MerchantBackend.Tips.ReserveCreateRequest,
+    response: MerchantBackend.Tips.ReserveCreateConfirmation
+  } | undefined>(undefined);
 
   if (createdOk) {
     return <CreatedSuccessfully entity={createdOk} onConfirm={onConfirm} />
@@ -49,7 +51,7 @@ export default function CreateReserve({ onBack, onConfirm }: 
Props): VNode {
     <CreatePage
       onBack={onBack}
       onCreate={(request: MerchantBackend.Tips.ReserveCreateRequest) => {
-        return createReserve(request).then((r) => 
setCreatedOk(r.data)).catch((error) => {
+        return createReserve(request).then((r) => setCreatedOk({ request, 
response: r.data })).catch((error) => {
           setNotif({
             message: i18n`could not create reserve`,
             type: "ERROR",
diff --git 
a/packages/frontend/src/paths/instance/reserves/details/DetailPage.tsx 
b/packages/frontend/src/paths/instance/reserves/details/DetailPage.tsx
index 3771337..08b463a 100644
--- a/packages/frontend/src/paths/instance/reserves/details/DetailPage.tsx
+++ b/packages/frontend/src/paths/instance/reserves/details/DetailPage.tsx
@@ -44,9 +44,9 @@ interface Props {
   selected: Entity;
 }
 
-export function DetailPage({ selected }: Props): VNode {
+export function DetailPage({ selected, onBack }: Props): VNode {
   const i18n = useTranslator()
-  return <Fragment>
+  return <div class="section main-section">
     <FormProvider object={selected} valueHandler={null} >
       <InputDate<Entity> name="creation_time" label={i18n`Created at`} 
readonly />
       <InputDate<Entity> name="expiration_time" label={i18n`Valid until`} 
readonly />
@@ -58,7 +58,17 @@ export function DetailPage({ selected }: Props): VNode {
     {selected.tips && selected.tips.length > 0 ? <Table tips={selected.tips} 
/> : <div>
       no tips for this reserve
     </div>}
-  </Fragment>
+    <div class="columns">
+      <div class="column" />
+      <div class="column is-two-thirds">
+        <div class="buttons is-right mt-5">
+          <button class="button" 
onClick={onBack}><Translate>Back</Translate></button>
+        </div>
+      </div>
+      <div class="column" />
+    </div>
+
+  </div>
 }
 
 async function copyToClipboard(text: string) {
@@ -101,10 +111,10 @@ function TipRow({ id, entry }: { id: string, entry: 
MerchantBackend.Tips.TipStat
   }
   if (!result.ok) {
     return <tr>
-      <td>...</td>
-      <td>{entry.total_amount}</td>
+      <td>...</td> {/* authorized */}
       <td>{entry.total_amount}</td>
-      <td>expired</td>
+      <td>{entry.reason}</td>
+      <td>...</td> {/* expired */}
     </tr>
   }
   const info = result.data
diff --git 
a/packages/frontend/src/paths/instance/reserves/list/AutorizeTipModal.tsx 
b/packages/frontend/src/paths/instance/reserves/list/AutorizeTipModal.tsx
index 42847ff..6f97815 100644
--- a/packages/frontend/src/paths/instance/reserves/list/AutorizeTipModal.tsx
+++ b/packages/frontend/src/paths/instance/reserves/list/AutorizeTipModal.tsx
@@ -44,19 +44,20 @@ export function AuthorizeTipModal({ onCancel, onConfirm, 
tipAuthorized }: Author
   type State = MerchantBackend.Tips.TipCreateRequest
   const [form, setValue] = useState<Partial<State>>({})
   const i18n = useTranslator();
-  const [errors, setErrors] = useState<FormErrors<State>>({})
 
-  const validateAndConfirm = () => {
-    try {
-      AuthorizeTipSchema.validateSync(form, { abortEarly: false })
-      onConfirm(form as State)
-    } catch (err) {
-      const errors = err.inner as any[]
-      const pathMessages = errors.reduce((prev, cur) => !cur.path ? prev : ({ 
...prev, [cur.path]: cur.message }), {})
-      setErrors(pathMessages)
-    }
+  // const [errors, setErrors] = useState<FormErrors<State>>({})
+  let errors: FormErrors<State> = {}
+  try {
+    AuthorizeTipSchema.validateSync(form, { abortEarly: false })
+  } catch (err) {
+    const yupErrors = err.inner as any[]
+    errors = yupErrors.reduce((prev, cur) => !cur.path ? prev : ({ ...prev, 
[cur.path]: cur.message }), {})
   }
+  const hasErrors = Object.keys(errors).some(k => (errors as any)[k] !== 
undefined)
 
+  const validateAndConfirm = () => {
+    onConfirm(form as State)
+  }
   if (tipAuthorized) {
     return <ContinueModal description="tip" active onConfirm={onCancel}>
       <CreatedSuccessfully
@@ -67,7 +68,7 @@ export function AuthorizeTipModal({ onCancel, onConfirm, 
tipAuthorized }: Author
     </ContinueModal>
   }
 
-  return <ConfirmModal description="tip" active onCancel={onCancel} 
onConfirm={validateAndConfirm}>
+  return <ConfirmModal description="tip" active onCancel={onCancel} 
disabled={hasErrors} onConfirm={validateAndConfirm}>
 
     <FormProvider<State> errors={errors} object={form} valueHandler={setValue} 
>
       <InputCurrency<State> name="amount" label={i18n`Amount`} 
tooltip={i18n`amount of tip`}/>
diff --git a/packages/frontend/src/paths/instance/reserves/list/Table.tsx 
b/packages/frontend/src/paths/instance/reserves/list/Table.tsx
index 58a2df5..a21de64 100644
--- a/packages/frontend/src/paths/instance/reserves/list/Table.tsx
+++ b/packages/frontend/src/paths/instance/reserves/list/Table.tsx
@@ -14,10 +14,10 @@
  GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 
- /**
- *
- * @author Sebastian Javier Marchano (sebasjm)
- */
+/**
+*
+* @author Sebastian Javier Marchano (sebasjm)
+*/
 
 import { format } from "date-fns"
 import { Fragment, h, VNode } from "preact"
@@ -37,23 +37,7 @@ interface Props {
   selected?: boolean;
 }
 
-export function CardTable({ instances, onCreate, onSelect, onNewTip, onDelete, 
selected }: Props): VNode {
-  const [actionQueue, actionQueueHandler] = useState<Actions<Entity>[]>([]);
-  const [rowSelection, rowSelectionHandler] = useState<string[]>([])
-
-  useEffect(() => {
-    if (actionQueue.length > 0 && !selected && actionQueue[0].type == 
'DELETE') {
-      onDelete(actionQueue[0].element)
-      actionQueueHandler(actionQueue.slice(1))
-    }
-  }, [actionQueue, selected, onDelete])
-
-  useEffect(() => {
-    if (actionQueue.length > 0 && !selected && actionQueue[0].type == 
'UPDATE') {
-      onNewTip(actionQueue[0].element)
-      actionQueueHandler(actionQueue.slice(1))
-    }
-  }, [actionQueue, selected, onNewTip])
+export function CardTable({ instances, onCreate, onSelect, onNewTip, onDelete 
}: Props): VNode {
 
   const [withoutFunds, withFunds] = instances.reduce((prev, current) => {
     const amount = current.exchange_initial_amount
@@ -63,118 +47,91 @@ export function CardTable({ instances, onCreate, onSelect, 
onNewTip, onDelete, s
       prev[1] = prev[1].concat(current)
     }
     return prev
-  }, new Array<Array<Entity>>([],[]))
+  }, new Array<Array<Entity>>([], []))
 
 
   return <Fragment>
     {withoutFunds.length > 0 && <div class="card has-table">
-    <header class="card-header">
-      <p class="card-header-title"><span class="icon"><i class="mdi mdi-cash" 
/></span><Translate>Reserves not yet funded</Translate></p>
-
-      <div class="card-header-icon" aria-label="more options">
-
-        <button class={rowSelection.length > 0 ? "button is-danger" : 
"is-hidden"}
-          type="button" onClick={(): void => 
actionQueueHandler(buildActions(instances, rowSelection, 'DELETE'))} >
-          Delete
-        </button>
-      </div>
-      <div class="card-header-icon" aria-label="more options">
-        <button class="button is-info" type="button" onClick={onCreate}>
-          <span class="icon is-small" ><i class="mdi mdi-plus mdi-36px" 
/></span>
-        </button>
-      </div>
-
-    </header>
-    <div class="card-content">
-      <div class="b-table has-pagination">
-        <div class="table-wrapper has-mobile-cards">
-          <TableWithoutFund instances={withoutFunds} onNewTip={onNewTip} 
onSelect={onSelect} onDelete={onDelete} rowSelection={rowSelection} 
rowSelectionHandler={rowSelectionHandler} />
+      <header class="card-header">
+        <p class="card-header-title"><span class="icon"><i class="mdi 
mdi-cash" /></span><Translate>Reserves not yet funded</Translate></p>
+        <div class="card-header-icon" aria-label="more options">
+          <button class="button is-info" type="button" onClick={onCreate}>
+            <span class="icon is-small" ><i class="mdi mdi-plus mdi-36px" 
/></span>
+          </button>
+        </div>
+      </header>
+      <div class="card-content">
+        <div class="b-table has-pagination">
+          <div class="table-wrapper has-mobile-cards">
+            <TableWithoutFund instances={withoutFunds} onNewTip={onNewTip} 
onSelect={onSelect} onDelete={onDelete} />
+          </div>
         </div>
       </div>
-    </div>
-  </div> }
-
-  <div class="card has-table">
-    <header class="card-header">
-      <p class="card-header-title"><span class="icon"><i class="mdi mdi-cash" 
/></span><Translate>Reserves ready</Translate></p>
-
-      <div class="card-header-icon" aria-label="more options">
-
-        <button class={rowSelection.length > 0 ? "button is-danger" : 
"is-hidden"}
-          type="button" onClick={(): void => 
actionQueueHandler(buildActions(instances, rowSelection, 'DELETE'))} >
-          Delete
-        </button>
-      </div>
-      <div class="card-header-icon" aria-label="more options">
-        <button class="button is-info" type="button" onClick={onCreate}>
-          <span class="icon is-small" ><i class="mdi mdi-plus mdi-36px" 
/></span>
-        </button>
-      </div>
+    </div>}
 
-    </header>
-    <div class="card-content">
-      <div class="b-table has-pagination">
-        <div class="table-wrapper has-mobile-cards">
-          {withFunds.length > 0 ?
-            <Table instances={withFunds} onNewTip={onNewTip} 
onSelect={onSelect} onDelete={onDelete} rowSelection={rowSelection} 
rowSelectionHandler={rowSelectionHandler} /> :
-            <EmptyTable />
-          }
+    <div class="card has-table">
+      <header class="card-header">
+        <p class="card-header-title"><span class="icon"><i class="mdi 
mdi-cash" /></span><Translate>Reserves ready</Translate></p>
+        <div class="card-header-icon" aria-label="more options">
+        </div>
+      </header>
+      <div class="card-content">
+        <div class="b-table has-pagination">
+          <div class="table-wrapper has-mobile-cards">
+            {withFunds.length > 0 ?
+              <Table instances={withFunds} onNewTip={onNewTip} 
onSelect={onSelect} onDelete={onDelete} /> :
+              <EmptyTable />
+            }
+          </div>
         </div>
       </div>
     </div>
-  </div>
   </Fragment>
 }
 interface TableProps {
-  rowSelection: string[];
   instances: Entity[];
   onNewTip: (id: Entity) => void;
   onDelete: (id: Entity) => void;
   onSelect: (id: Entity) => void;
-  rowSelectionHandler: StateUpdater<string[]>;
 }
 
-function toggleSelected<T>(id: T): (prev: T[]) => T[] {
-  return (prev: T[]): T[] => prev.indexOf(id) == -1 ? [...prev, id] : 
prev.filter(e => e != id)
-}
-
-function Table({ rowSelection, rowSelectionHandler, instances, onNewTip, 
onSelect, onDelete }: TableProps): VNode {
+function Table({ instances, onNewTip, onSelect, onDelete }: TableProps): VNode 
{
   return (
     <div class="table-container">
-    <table class="table is-fullwidth is-striped is-hoverable is-fullwidth">
-      <thead>
-        <tr>
-          <th><Translate>Created at</Translate></th>
-          <th><Translate>Expires at</Translate></th>
-          <th><Translate>Initial</Translate></th>
-          <th><Translate>Picked up</Translate></th>
-          <th><Translate>Committed</Translate></th>
-          <th />
-        </tr>
-      </thead>
-      <tbody>
-        {instances.map(i => {
-          return <tr key={i.id}>
-            <td onClick={(): void => onSelect(i)} style={{cursor: 'pointer'}} 
>{i.creation_time.t_ms === "never" ? "never" : format(i.creation_time.t_ms, 
'yyyy/MM/dd HH:mm:ss')}</td>
-            <td onClick={(): void => onSelect(i)} style={{cursor: 'pointer'}} 
>{i.expiration_time.t_ms === "never" ? "never" : format(i.expiration_time.t_ms, 
'yyyy/MM/dd HH:mm:ss')}</td>
-            <td onClick={(): void => onSelect(i)} style={{cursor: 'pointer'}} 
>{i.exchange_initial_amount}</td>
-            <td onClick={(): void => onSelect(i)} style={{cursor: 'pointer'}} 
>{i.pickup_amount}</td>
-            <td onClick={(): void => onSelect(i)} style={{cursor: 'pointer'}} 
>{i.committed_amount}</td>
-            <td class="is-actions-cell right-sticky">
-              <div class="buttons is-right">
-                <button class="button is-small is-danger jb-modal" 
type="button" onClick={(): void => onDelete(i)}>
-                  Delete
+      <table class="table is-fullwidth is-striped is-hoverable is-fullwidth">
+        <thead>
+          <tr>
+            <th><Translate>Created at</Translate></th>
+            <th><Translate>Expires at</Translate></th>
+            <th><Translate>Initial</Translate></th>
+            <th><Translate>Picked up</Translate></th>
+            <th><Translate>Committed</Translate></th>
+            <th />
+          </tr>
+        </thead>
+        <tbody>
+          {instances.map(i => {
+            return <tr key={i.id}>
+              <td onClick={(): void => onSelect(i)} style={{ cursor: 'pointer' 
}} >{i.creation_time.t_ms === "never" ? "never" : format(i.creation_time.t_ms, 
'yyyy/MM/dd HH:mm:ss')}</td>
+              <td onClick={(): void => onSelect(i)} style={{ cursor: 'pointer' 
}} >{i.expiration_time.t_ms === "never" ? "never" : 
format(i.expiration_time.t_ms, 'yyyy/MM/dd HH:mm:ss')}</td>
+              <td onClick={(): void => onSelect(i)} style={{ cursor: 'pointer' 
}} >{i.exchange_initial_amount}</td>
+              <td onClick={(): void => onSelect(i)} style={{ cursor: 'pointer' 
}} >{i.pickup_amount}</td>
+              <td onClick={(): void => onSelect(i)} style={{ cursor: 'pointer' 
}} >{i.committed_amount}</td>
+              <td class="is-actions-cell right-sticky">
+                <div class="buttons is-right">
+                  <button class="button is-small is-danger jb-modal" 
type="button" onClick={(): void => onDelete(i)}>
+                    Delete
                 </button>
-                <button class="button is-small is-info jb-modal" type="button" 
onClick={(): void => onNewTip(i)}>
-                  New Tip
+                  <button class="button is-small is-info jb-modal" 
type="button" onClick={(): void => onNewTip(i)}>
+                    New Tip
                 </button>
-              </div>
-            </td>
-          </tr>
-        })}
+                </div>
+              </td>
+            </tr>
+          })}
 
-      </tbody>
-    </table></div>)
+        </tbody>
+      </table></div>)
 }
 
 function EmptyTable(): VNode {
@@ -186,34 +143,34 @@ function EmptyTable(): VNode {
   </div>
 }
 
-function TableWithoutFund({ rowSelection, rowSelectionHandler, instances, 
onNewTip, onSelect, onDelete }: TableProps): VNode {
+function TableWithoutFund({ instances, onSelect, onDelete }: TableProps): 
VNode {
   return (
     <div class="table-container">
-    <table class="table is-fullwidth is-striped is-hoverable is-fullwidth">
-      <thead>
-        <tr>
-          <th><Translate>Created at</Translate></th>
-          <th><Translate>Expires at</Translate></th>
-          <th><Translate>Expected Balance</Translate></th>
-          <th />
-        </tr>
-      </thead>
-      <tbody>
-        {instances.map(i => {
-          return <tr key={i.id}>
-            <td onClick={(): void => onSelect(i)} style={{cursor: 'pointer'}} 
>{i.creation_time.t_ms === "never" ? "never" : format(i.creation_time.t_ms, 
'yyyy/MM/dd HH:mm:ss')}</td>
-            <td onClick={(): void => onSelect(i)} style={{cursor: 'pointer'}} 
>{i.expiration_time.t_ms === "never" ? "never" : format(i.expiration_time.t_ms, 
'yyyy/MM/dd HH:mm:ss')}</td>
-            <td onClick={(): void => onSelect(i)} style={{cursor: 'pointer'}} 
>{i.merchant_initial_amount}</td>
-            <td class="is-actions-cell right-sticky">
-              <div class="buttons is-right">
-                <button class="button is-small is-danger jb-modal" 
type="button" onClick={(): void => onDelete(i)}>
-                  Delete
-                </button>
-              </div>
-            </td>
+      <table class="table is-fullwidth is-striped is-hoverable is-fullwidth">
+        <thead>
+          <tr>
+            <th><Translate>Created at</Translate></th>
+            <th><Translate>Expires at</Translate></th>
+            <th><Translate>Expected Balance</Translate></th>
+            <th />
           </tr>
-        })}
+        </thead>
+        <tbody>
+          {instances.map(i => {
+            return <tr key={i.id}>
+              <td onClick={(): void => onSelect(i)} style={{ cursor: 'pointer' 
}} >{i.creation_time.t_ms === "never" ? "never" : format(i.creation_time.t_ms, 
'yyyy/MM/dd HH:mm:ss')}</td>
+              <td onClick={(): void => onSelect(i)} style={{ cursor: 'pointer' 
}} >{i.expiration_time.t_ms === "never" ? "never" : 
format(i.expiration_time.t_ms, 'yyyy/MM/dd HH:mm:ss')}</td>
+              <td onClick={(): void => onSelect(i)} style={{ cursor: 'pointer' 
}} >{i.merchant_initial_amount}</td>
+              <td class="is-actions-cell right-sticky">
+                <div class="buttons is-right">
+                  <button class="button is-small is-danger jb-modal" 
type="button" onClick={(): void => onDelete(i)}>
+                    Delete
+                </button>
+                </div>
+              </td>
+            </tr>
+          })}
 
-      </tbody>
-    </table></div>)
+        </tbody>
+      </table></div>)
 }
diff --git a/packages/frontend/src/paths/instance/transfers/list/index.tsx 
b/packages/frontend/src/paths/instance/transfers/list/index.tsx
index d141011..5486451 100644
--- a/packages/frontend/src/paths/instance/transfers/list/index.tsx
+++ b/packages/frontend/src/paths/instance/transfers/list/index.tsx
@@ -28,6 +28,7 @@ import { Input } from '../../../../components/form/Input';
 import { InputBoolean } from '../../../../components/form/InputBoolean';
 import { InputSearchProduct } from 
'../../../../components/form/InputSearchProduct';
 import { InputSelector } from '../../../../components/form/InputSelector';
+import { MerchantBackend } from '../../../../declaration';
 import { HttpError } from '../../../../hooks/backend';
 import { useInstanceDetails } from '../../../../hooks/instance';
 import { useInstanceTransfers, useTransferAPI } from 
"../../../../hooks/transfer";
@@ -59,6 +60,17 @@ export default function ListTransfer({ onUnauthorized, 
onLoadError, onCreate, on
   const isNonVerifiedTransfers = form.verified === 'no' ? "is-active" : ''
   const isAllTransfers = form.verified === undefined ? 'is-active' : ''
 
+  const result = useInstanceTransfers({
+    position,
+    payto_uri: form.payto_uri === '' ? undefined : form.payto_uri,
+    verified: form.verified,
+  }, (id) => setPosition(id))
+
+  if (result.clientError && result.isUnauthorized) return onUnauthorized()
+  if (result.clientError && result.isNotfound) return onNotFound()
+  if (result.loading) return <Loading />
+  if (!result.ok) return onLoadError(result)
+
   return <section class="section is-main-section">
     <div class="columns">
       <div class="column" />
@@ -82,6 +94,9 @@ export default function ListTransfer({ onUnauthorized, 
onLoadError, onCreate, on
     </div>
     <View
       accounts={accounts}
+      transfers={result.data.transfers}
+      onLoadMoreBefore={result.isReachingStart ? result.loadMorePrev : 
undefined}
+      onLoadMoreAfter={result.isReachingEnd ? result.loadMore : undefined}
       form={form} onCreate={onCreate} onLoadError={onLoadError} 
onNotFound={onNotFound} onUnauthorized={onUnauthorized}
       position={position} setPosition={setPosition}
     />
@@ -89,31 +104,23 @@ export default function ListTransfer({ onUnauthorized, 
onLoadError, onCreate, on
 }
 
 interface ViewProps extends Props {
+  transfers: MerchantBackend.Transfers.TransferDetails[];
+  onLoadMoreBefore?: () => void;
+  onLoadMoreAfter?: () => void;
   position?: string;
   setPosition: (s: string) => void;
   form: Form;
   accounts: string[];
 }
 
-function View({ onUnauthorized, onLoadError, onCreate, onNotFound, position, 
form, setPosition, accounts }: ViewProps) {
-  const result = useInstanceTransfers({
-    position,
-    payto_uri: form.payto_uri === '' ? undefined : form.payto_uri,
-    verified: form.verified,
-  }, (id) => setPosition(id))
-
-  if (result.clientError && result.isUnauthorized) return onUnauthorized()
-  if (result.clientError && result.isNotfound) return onNotFound()
-  if (result.loading) return <Loading />
-  if (!result.ok) return onLoadError(result)
-
-  return <CardTable instances={result.data.transfers.map(o => ({ ...o, id: 
String(o.transfer_serial_id) }))}
+function View({ transfers, onCreate, accounts, onLoadMoreBefore, 
onLoadMoreAfter }: ViewProps) {
+  return <CardTable instances={transfers.map(o => ({ ...o, id: 
String(o.transfer_serial_id) }))}
     accounts={accounts}
     onCreate={onCreate}
     onDelete={() => null}
     onUpdate={() => null}
-    onLoadMoreBefore={result.loadMorePrev} 
hasMoreBefore={!result.isReachingStart}
-    onLoadMoreAfter={result.loadMore} hasMoreAfter={!result.isReachingEnd}
+    onLoadMoreBefore={onLoadMoreBefore} hasMoreBefore={!onLoadMoreBefore}
+    onLoadMoreAfter={onLoadMoreAfter} hasMoreAfter={!onLoadMoreAfter}
   />
 
 }
diff --git a/packages/frontend/src/paths/instance/update/UpdatePage.tsx 
b/packages/frontend/src/paths/instance/update/UpdatePage.tsx
index 9d6777f..e12e09f 100644
--- a/packages/frontend/src/paths/instance/update/UpdatePage.tsx
+++ b/packages/frontend/src/paths/instance/update/UpdatePage.tsx
@@ -31,6 +31,7 @@ import { InputGroup } from 
"../../../components/form/InputGroup";
 import { InputLocation } from "../../../components/form/InputLocation";
 import { InputPayto } from "../../../components/form/InputPayto";
 import { InputSecured } from "../../../components/form/InputSecured";
+import { UpdateTokenModal } from "../../../components/modal";
 import { useInstanceContext } from "../../../context/instance";
 import { MerchantBackend } from "../../../declaration";
 import { Translate, useTranslator } from "../../../i18n";
@@ -38,15 +39,16 @@ import { InstanceUpdateSchema as schema } from 
'../../../schemas';
 
 
 type Entity = MerchantBackend.Instances.InstanceReconfigurationMessage & { 
auth_token?: string }
-
+//MerchantBackend.Instances.InstanceAuthConfigurationMessage
 interface Props {
-  onUpdate: (d: Entity, auth?: 
MerchantBackend.Instances.InstanceAuthConfigurationMessage) => void;
+  onUpdate: (d: Entity) => void;
+  onChangeAuth: (d: 
MerchantBackend.Instances.InstanceAuthConfigurationMessage) => Promise<void>;
   selected: MerchantBackend.Instances.QueryInstancesResponse;
   isLoading: boolean;
   onBack: () => void;
 }
 
-function convert(from: MerchantBackend.Instances.QueryInstancesResponse, 
token?: string): Entity {
+function convert(from: MerchantBackend.Instances.QueryInstancesResponse): 
Entity {
   const { accounts, ...rest } = from
   const payto_uris = accounts.filter(a => a.active).map(a => a.payto_uri)
   const defaults = {
@@ -54,7 +56,7 @@ function convert(from: 
MerchantBackend.Instances.QueryInstancesResponse, token?:
     default_pay_delay: { d_ms: 1000 * 60 * 60 }, //one hour
     default_wire_transfer_delay: { d_ms: 1000 * 60 * 60 * 2 }, //two hours
   }
-  return { ...defaults, ...rest, payto_uris, auth_token: from.auth.method === 
"external" ? undefined : token };
+  return { ...defaults, ...rest, payto_uris };
 }
 
 function getTokenValuePart(t?: string): string | undefined {
@@ -64,10 +66,24 @@ function getTokenValuePart(t?: string): string | undefined {
   return match[1]
 }
 
-export function UpdatePage({ onUpdate, selected, onBack }: Props): VNode {
-  const { token } = useInstanceContext()
+
+
+export function UpdatePage({ onUpdate, onChangeAuth, selected, onBack }: 
Props): VNode {
+  const { id, token } = useInstanceContext()
   const currentTokenValue = getTokenValuePart(token)
-  const [value, valueHandler] = useState<Partial<Entity>>(convert(selected, 
currentTokenValue))
+
+  function updateToken(token: string | undefined | null) {
+    const value = token && token.startsWith('secret-token:') ?
+      token.substring('secret-token:'.length) : token
+
+    if (!token) {
+      onChangeAuth({ method: 'external' })
+    } else {
+      onChangeAuth({ method: 'token', token: `secret-token:${value}` })
+    }
+  }
+
+  const [value, valueHandler] = useState<Partial<Entity>>(convert(selected))
 
   let errors: FormErrors<Entity> = {}
   try {
@@ -79,36 +95,72 @@ export function UpdatePage({ onUpdate, selected, onBack }: 
Props): VNode {
   const hasErrors = Object.keys(errors).some(k => (errors as any)[k] !== 
undefined)
   const submit = async (): Promise<void> => {
     // use conversion instead of this
-    const newToken = value.auth_token;
-    value.auth_token = undefined;
+    // const newToken = value.auth_token;
+    // value.auth_token = undefined;
 
     //if new token was not set or has been set to the actual current token
     //it is not needed to send a change
     //otherwise, checked where we are setting a new token or removing it
-    const auth: MerchantBackend.Instances.InstanceAuthConfigurationMessage | 
undefined =
-      newToken === undefined || newToken === currentTokenValue ? undefined : 
(newToken === null ?
-        { method: "external" } :
-        { method: "token", token: `secret-token:${newToken}` });
+    // const auth: MerchantBackend.Instances.InstanceAuthConfigurationMessage 
| undefined =
+    //   newToken === undefined || newToken === currentTokenValue ? undefined 
: (newToken === null ?
+    //     { method: "external" } :
+    //     { method: "token", token: `secret-token:${newToken}` });
 
     // remove above use conversion
     schema.validateSync(value, { abortEarly: false })
-    await onUpdate(schema.cast(value), auth);
+    await onUpdate(schema.cast(value));
     await onBack()
     return Promise.resolve()
   }
+  const [active, setActive] = useState(false);
 
   const i18n = useTranslator()
 
   return <div>
-    <section class="section is-main-section">
+    <section class="section ">
+
+      <section class="hero is-hero-bar">
+        <div class="hero-body">
+
+          <div class="level">
+            <div class="level-left">
+              <div class="level-item">
+                <span class="is-size-4">Instance id: <b>{id}</b></span>
+              </div>
+            </div>
+            <div class="level-right">
+              <div class="level-item">
+                <h1 class="title">
+                  <button class="button is-danger" onClick={(): void => { 
setActive(!active); }} >
+                    <div class="icon is-left"><i class="mdi mdi-lock-reset" 
/></div>
+                    <span><Translate>Manage token</Translate></span>
+                  </button>
+                </h1>
+              </div>
+            </div>
+          </div>
+        </div></section>
+
+      <div class="columns">
+        <div class="column" />
+        <div class="column is-four-fifths">
+          {active && <UpdateTokenModal oldToken={currentTokenValue}
+            onCancel={() => { setActive(false); }}
+            onClear={() => { updateToken(null); setActive(false); }}
+            onConfirm={(newToken) => {
+              updateToken(newToken); setActive(false)
+            }}
+          />}
+        </div>
+        <div class="column" />
+      </div>
+      <hr />
       <div class="columns">
         <div class="column" />
         <div class="column is-four-fifths">
           <FormProvider<Entity> errors={errors} object={value} 
valueHandler={valueHandler} >
 
-            <Input<Entity> name="name" label={i18n`Name`} tooltip={i18n`unique 
name of this instance`} />
-
-            <InputSecured<Entity> name="auth_token" label={i18n`Auth token`} />
+            <Input<Entity> name="name" label={i18n`Name`} 
tooltip={i18n`display name of this instance`} />
 
             <InputPayto<Entity> name="payto_uris" label={i18n`Account 
address`} help="x-taler-bank/bank.taler:5882/blogger" />
 
@@ -141,6 +193,6 @@ export function UpdatePage({ onUpdate, selected, onBack }: 
Props): VNode {
       </div>
     </section>
 
-  </div>
+  </div >
 
 }
diff --git a/packages/frontend/src/paths/instance/update/index.tsx 
b/packages/frontend/src/paths/instance/update/index.tsx
index 81c9a06..b226af0 100644
--- a/packages/frontend/src/paths/instance/update/index.tsx
+++ b/packages/frontend/src/paths/instance/update/index.tsx
@@ -15,6 +15,7 @@
  */
 import { Fragment, h, VNode } from "preact";
 import { Loading } from "../../../components/exception/loading";
+import { useInstanceContext } from "../../../context/instance";
 import { MerchantBackend } from "../../../declaration";
 import { HttpError } from "../../../hooks/backend";
 import { useInstanceAPI, useInstanceDetails } from "../../../hooks/instance";
@@ -32,8 +33,9 @@ export interface Props {
 }
 
 export default function Update({ onBack, onConfirm, onLoadError, onNotFound, 
onUpdateError, onUnauthorized }: Props): VNode {
-  const { updateInstance } = useInstanceAPI();
+  const { updateInstance, clearToken, setNewToken } = useInstanceAPI();
   const result = useInstanceDetails()
+  const { changeToken } = useInstanceContext()
 
   if (result.clientError && result.isUnauthorized) return onUnauthorized()
   if (result.clientError && result.isNotfound) return onNotFound()
@@ -45,8 +47,13 @@ export default function Update({ onBack, onConfirm, 
onLoadError, onNotFound, onU
       onBack={onBack}
       isLoading={false}
       selected={result.data}
-      onUpdate={(d: MerchantBackend.Instances.InstanceReconfigurationMessage, 
t?: MerchantBackend.Instances.InstanceAuthConfigurationMessage): Promise<void> 
=> {
-        return updateInstance(d, t).then(onConfirm).catch(onUpdateError)
-      }} />
+      onUpdate={(d: MerchantBackend.Instances.InstanceReconfigurationMessage): 
Promise<void> => {
+        return updateInstance(d).then(onConfirm).catch(onUpdateError)
+      }} 
+      onChangeAuth={(d: 
MerchantBackend.Instances.InstanceAuthConfigurationMessage): Promise<void> => {
+        const apiCall = d.method === 'external' ? clearToken() : 
setNewToken(d.token!);
+        return apiCall.then(() => 
changeToken(d.token)).then(onConfirm).catch(onUpdateError)
+      }} 
+      />
   </Fragment>
 }
\ No newline at end of file
diff --git a/packages/frontend/src/utils/amount.ts 
b/packages/frontend/src/utils/amount.ts
index c799d97..731dc76 100644
--- a/packages/frontend/src/utils/amount.ts
+++ b/packages/frontend/src/utils/amount.ts
@@ -13,6 +13,7 @@
  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 { Amounts } from "@gnu-taler/taler-util";
 import { MerchantBackend } from "../declaration";
 
 /**
@@ -55,24 +56,36 @@ export function mergeRefunds(prev: 
MerchantBackend.Orders.RefundDetails[], cur:
 }
 
 export const multiplyPrice = (price: string, q: number) => {
-  const [currency, value] = price.split(':')
-  const total = parseInt(value, 10) * q
-  return `${currency}:${total}`
+  const a = Amounts.parseOrThrow(price)
+  const r = Amounts.mult(a, q)
+  return Amounts.stringify(r.amount)
+  // const [currency, value] = price.split(':')
+  // const total = parseInt(value, 10) * q
+  // return `${currency}:${total}`
 }
 
 export const subtractPrices = (one: string, two: string) => {
-  const [currency, valueOne] = one.split(':')
-  const [, valueTwo] = two.split(':')
-  return `${currency}:${parseInt(valueOne, 10) - parseInt(valueTwo, 10)}`
+  const a = Amounts.parseOrThrow(one)
+  const b = Amounts.parseOrThrow(two)
+  const r = Amounts.sub(a, b)
+  return Amounts.stringify(r.amount)
+  // const [currency, valueOne] = one.split(':')
+  // const [, valueTwo] = two.split(':')
+  // return `${currency}:${parseInt(valueOne, 10) - parseInt(valueTwo, 10)}`
 }
 
-export const rate = (one?: string, two?: string) => {
-  const [, valueOne] = (one || '').split(':')
-  const [, valueTwo] = (two || '').split(':')
-  const intOne = parseInt(valueOne, 10)
-  const intTwo = parseInt(valueTwo, 10)
-  if (!intTwo) return intOne
-  if (!intOne) return 0
-  return intOne / intTwo
+export const rate = (one: string, two: string) => {
+  const a = Amounts.parseOrThrow(one)
+  const b = Amounts.parseOrThrow(two)
+  const af = Amounts.toFloat(a)
+  const bf = Amounts.toFloat(b)
+  return af / bf
+  // const [, valueOne] = (one || '').split(':')
+  // const [, valueTwo] = (two || '').split(':')
+  // const intOne = parseInt(valueOne, 10)
+  // const intTwo = parseInt(valueTwo, 10)
+  // if (!intTwo) return intOne
+  // if (!intOne) return 0
+  // return intOne / intTwo
 }
 
diff --git a/packages/frontend/src/utils/constants.ts 
b/packages/frontend/src/utils/constants.ts
index a8ab9ca..1f654c0 100644
--- a/packages/frontend/src/utils/constants.ts
+++ b/packages/frontend/src/utils/constants.ts
@@ -21,6 +21,7 @@
 
 //https://tools.ietf.org/html/rfc8905
 export const PAYTO_REGEX = 
/^payto:\/\/[a-zA-Z][a-zA-Z0-9-.]+(\/[a-zA-Z0-9\-\.\~\(\)@_%:!$&'*+,;=]*)*\??((amount|receiver-name|sender-name|instruction|message)=[a-zA-Z0-9\-\.\~\(\)@_%:!$'*+,;=]*&?)*$/
+export const PAYTO_WIRE_METHOD_LOOKUP = 
/payto:\/\/([a-zA-Z][a-zA-Z0-9-.]+)\/.*/
 
 export const AMOUNT_REGEX = /^[a-zA-Z][a-zA-Z]*:[0-9][0-9,]*\.?[0-9,]*$/
 

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