gnunet-svn
[Top][All Lists]
Advanced

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

[taler-merchant-backoffice] branch master updated (6c363c9 -> f3aea32)


From: gnunet
Subject: [taler-merchant-backoffice] branch master updated (6c363c9 -> f3aea32)
Date: Wed, 03 Mar 2021 18:44:07 +0100

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

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

    from 6c363c9  managed token update
     new 2560ef5  fixed some comments
     new a868b23  lang selector behavior
     new 9128d98  improve app routing
     new 0d59f0e  login modal should be sent on update
     new 6ca26ca  improve sidebar to show instance id and navbar to show a title
     new 90e4e00  add left label to inputsecured
     new f3aea32  refactor backend mutate api to use token from instance id, 
implemented new passwod endpoint

The 7 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:
 CHANGELOG.md                                       |  19 ++-
 packages/frontend/.storybook/preview.js            |   4 +-
 packages/frontend/preact.config.js                 |   9 +-
 packages/frontend/src/AdminRoutes.tsx              |  68 +++++++++
 packages/frontend/src/ApplicationReadyRoutes.tsx   | 167 +++++++++++++--------
 packages/frontend/src/InstanceRoutes.tsx           |  34 +++--
 packages/frontend/src/components/auth/index.tsx    |  34 ++---
 .../frontend/src/components/form/InputArray.tsx    |   2 +-
 .../frontend/src/components/form/InputSecured.tsx  |  12 +-
 .../frontend/src/components/menu/LangSelector.tsx  |  60 ++++++++
 .../frontend/src/components/menu/NavigationBar.tsx |  58 +++++++
 .../{sidebar/index.tsx => menu/SideBar.tsx}        |  62 +++++---
 .../instances/create => components/menu}/index.tsx |  27 ++--
 packages/frontend/src/components/modal/index.tsx   |   5 +-
 packages/frontend/src/components/navbar/index.tsx  |  93 ------------
 packages/frontend/src/context/backend.ts           |  19 ++-
 packages/frontend/src/declaration.d.ts             |  39 +++--
 packages/frontend/src/hooks/backend.ts             | 117 +++++++++------
 packages/frontend/src/hooks/index.ts               |   9 +-
 packages/frontend/src/index.tsx                    |  72 +++++----
 packages/frontend/src/messages/en.po               |   3 +
 .../src/routes/instances/create/CreatePage.tsx     |  49 ++----
 .../frontend/src/routes/instances/create/index.tsx |   4 +-
 .../src/routes/instances/details/DetailPage.tsx    |  31 +---
 .../src/routes/instances/details/index.tsx         |  11 +-
 .../frontend/src/routes/instances/list/Table.tsx   |   6 +-
 .../frontend/src/routes/instances/list/View.tsx    |  29 ----
 .../frontend/src/routes/instances/list/index.tsx   |  38 +----
 .../src/routes/instances/update/UpdatePage.tsx     |  67 ++++-----
 .../frontend/src/routes/instances/update/index.tsx |  23 +--
 packages/frontend/src/schemas/index.ts             |   8 +-
 packages/frontend/src/scss/_aside.scss             |   4 +-
 packages/frontend/src/template.html                |   2 +-
 packages/frontend/src/utils/constants.ts           |   3 +
 packages/frontend/tests/header.test.tsx            |   2 +-
 35 files changed, 654 insertions(+), 536 deletions(-)
 create mode 100644 packages/frontend/src/AdminRoutes.tsx
 create mode 100644 packages/frontend/src/components/menu/LangSelector.tsx
 create mode 100644 packages/frontend/src/components/menu/NavigationBar.tsx
 rename packages/frontend/src/components/{sidebar/index.tsx => 
menu/SideBar.tsx} (59%)
 copy packages/frontend/src/{routes/instances/create => 
components/menu}/index.tsx (53%)
 delete mode 100644 packages/frontend/src/components/navbar/index.tsx

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 348addc..c674aa4 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -13,20 +13,31 @@ and this project adheres to [Semantic 
Versioning](https://semver.org/spec/v2.0.0
  - validate everything using onChange
  - feature: input as date format
  - bug: there is missing a mutate call when updating to remove the instance 
from cache
- - submit form on key press == enter
- - (WIP) remove checkbox from auth token, use button (manage auth)
- - (WIP) auth token config as popup with 3 actions (clear (sure?), cancel, set 
token)
 
  - add order section
  - add product section
  - add tips section
- - implement better error handling
+ - implement better error handling (improve creation of duplicated instances)
  - replace Yup and type definition with a taler-library for the purpose (first 
wait Florian to refactor wallet core)
  - add more doc style comments 
  - configure eslint
  - configure prettier
  - prune scss styles to reduce size
  - create a loading page to be use when the data is not ready
+ 
+## [Unreleased]
+ - 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
+ - (WIP) remove checkbox from auth token, use button (manage auth)
+ - (WIP) auth token config as popup with 3 actions (clear (sure?), cancel, set 
token)
 
 ## [0.0.2] - 2021-02-25
  - REFACTOR: remove react-i18n and implement messageformat
diff --git a/packages/frontend/.storybook/preview.js 
b/packages/frontend/.storybook/preview.js
index 0299766..059edaa 100644
--- a/packages/frontend/.storybook/preview.js
+++ b/packages/frontend/.storybook/preview.js
@@ -1,6 +1,6 @@
 import "../src/scss/main.scss"
 import { MessageProvider } from "preact-messages";
-import { ConfigContext } from '../src/context/backend'
+import { ConfigContextProvider } from '../src/context/backend'
 import * as messages from '../src/messages'
 import { h } from 'preact';
 
@@ -35,5 +35,5 @@ export const decorators = [
       <Story />
     </MessageProvider>
   },
-  (Story) => <ConfigContext.Provider value={mockConfig}> <Story /> 
</ConfigContext.Provider>
+  (Story) => <ConfigContextProvider value={mockConfig}> <Story /> 
</ConfigContextProvider>
 ];
diff --git a/packages/frontend/preact.config.js 
b/packages/frontend/preact.config.js
index 5e42bc6..f37d757 100644
--- a/packages/frontend/preact.config.js
+++ b/packages/frontend/preact.config.js
@@ -19,13 +19,16 @@
 * @author Sebastian Javier Marchano (sebasjm)
 */
 
-// import { DefinePlugin } from 'webpack';
-// import { config } from 'dotenv';
-// const { parsed } = config();
+import { DefinePlugin } from 'webpack';
 
 export default {
     webpack(config, env, helpers, options) {
         config.node.process = 'mock'
+        config.plugins.push(
+            new DefinePlugin({
+                'process.env.__VERSION__': 
JSON.stringify(require("./package.json").version),
+            }),
+        );
         config.resolve.extensions.push('.po');
         config.module.rules.push({
             enforce: 'pre',
diff --git a/packages/frontend/src/AdminRoutes.tsx 
b/packages/frontend/src/AdminRoutes.tsx
new file mode 100644
index 0000000..cf70d3f
--- /dev/null
+++ b/packages/frontend/src/AdminRoutes.tsx
@@ -0,0 +1,68 @@
+import { h, VNode } from "preact";
+import Router, { route, Route } from "preact-router";
+import { RootPaths, Redirect } from "./index";
+import NotFoundPage from './routes/notfound';
+import InstanceListPage from './routes/instances/list';
+import InstanceCreatePage from "./routes/instances/create";
+import { MerchantBackend } from "./declaration";
+import { useMessageTemplate } from "preact-messages";
+import { Notification } from "./utils/types";
+import { InstanceRoutes } from "./InstanceRoutes";
+
+interface Props {
+  pushNotification: (n: Notification) => void;
+  instances: MerchantBackend.Instances.Instance[]
+  addTokenCleaner: any;
+}
+export function AdminRoutes({ instances, pushNotification, addTokenCleaner }: 
Props): VNode {
+  const i18n = useMessageTemplate();
+
+  return <Router>
+    <Route path={RootPaths.root} component={Redirect} 
to={RootPaths.list_instances} />
+
+    <Route path={RootPaths.list_instances} component={InstanceListPage}
+
+      onCreate={() => {
+        route(RootPaths.new_instance);
+      }}
+
+      instances={instances}
+
+      onUpdate={(id: string): void => {
+        route(`/instance/${id}/update`);
+      }}
+
+    // onUnauthorized={() => <LoginPage
+    //   withMessage={{ message: i18n`Access denied`, description: i18n`Check 
your token is valid`, type: 'ERROR', }}
+    //   onConfirm={updateLoginStatus} />}
+
+    // onQueryError={(error: SwrError) => <LoginPage
+    //   withMessage={{ message: i18n`Problem reaching the server`, 
description: i18n`Got message: ${error.message} from: ${error.backend} 
(hasToken: ${error.hasToken})`, type: 'ERROR', }}
+    //   onConfirm={updateLoginStatus} />}
+
+    />
+
+    <Route path={RootPaths.new_instance} component={InstanceCreatePage}
+
+      onBack={() => route(RootPaths.list_instances)}
+
+      onConfirm={() => {
+        pushNotification({ message: i18n`create_success`, type: 'SUCCESS' });
+        route(RootPaths.list_instances);
+      }}
+
+      onError={(error: any) => {
+        pushNotification({ message: i18n`create_error`, type: 'ERROR' });
+      }}
+    />
+
+    <Route path={RootPaths.instance_id_route} component={InstanceRoutes}
+      pushNotification={pushNotification}
+      addTokenCleaner={addTokenCleaner}
+      parent="/instance/:id"
+    />
+
+    <Route default component={NotFoundPage} />
+
+  </Router>
+}
\ No newline at end of file
diff --git a/packages/frontend/src/ApplicationReadyRoutes.tsx 
b/packages/frontend/src/ApplicationReadyRoutes.tsx
index 61c098c..0b0fc92 100644
--- a/packages/frontend/src/ApplicationReadyRoutes.tsx
+++ b/packages/frontend/src/ApplicationReadyRoutes.tsx
@@ -18,73 +18,118 @@
 *
 * @author Sebastian Javier Marchano (sebasjm)
 */
-import { h, VNode } from 'preact';
-import { useContext } from "preact/hooks";
-import { Route, Router, route } from 'preact-router';
-import { useMessageTemplate } from 'preact-messages';
+import { Fragment, h, VNode } from 'preact';
+import { route } from 'preact-router';
 import { Notification } from "./utils/types";
-import { BackendContext } from './context/backend';
-import { SwrError } from "./hooks/backend";
+import { useBackendContext } from './context/backend';
+import { useBackendInstances } from "./hooks/backend";
 import { InstanceRoutes } from "./InstanceRoutes";
-import { RootPaths, Redirect } from "./index";
-import NotFoundPage from './routes/notfound';
 import LoginPage from './routes/login';
-import InstanceListPage from './routes/instances/list';
-import InstanceCreatePage from "./routes/instances/create";
-import { Notifications } from './components/notifications';
-
-export function ApplicationReadyRoutes({ pushNotification, addTokenCleaner }: 
{ pushNotification: (n: Notification) => void; addTokenCleaner: any; }): VNode {
-  const { changeBackend, updateToken } = useContext(BackendContext);
+import { INSTANCE_ID_LOOKUP } from './utils/constants';
+import { Menu } from './components/menu';
+import { AdminRoutes } from './AdminRoutes';
+import { useMessageTemplate } from 'preact-messages';
+interface Props {
+  pushNotification: (n: Notification) => void;
+  addTokenCleaner: any;
+  clearAllTokens: () => void;
+}
+export function ApplicationReadyRoutes({ pushNotification, addTokenCleaner, 
clearAllTokens }: Props): VNode {
+  const i18n = useMessageTemplate();
+  const { url: currentBaseUrl, changeBackend, updateToken } = 
useBackendContext();
 
   const updateLoginStatus = (url: string, token?: string) => {
     changeBackend(url);
-    if (token)
-      updateToken(token);
+    if (token) updateToken(token);
   };
-  const i18n = useMessageTemplate();
-
-  return <Router>
-    <Route path={RootPaths.root} component={Redirect}
-      to={RootPaths.list_instances} />
-
-    <Route path={RootPaths.list_instances} component={InstanceListPage}
-
-      onCreate={() => {
-        route(RootPaths.new_instance);
-      }}
-
-      onUpdate={(id: string): void => {
-        route(`/instance/${id}/update`);
-      }}
-
-      onUnauthorized={() => <LoginPage
-        withMessage={{ message: i18n`Access denied`, description: i18n`Check 
your token is valid`, type: 'ERROR', }}
-        onConfirm={updateLoginStatus} />}
-
-      onQueryError={(error: SwrError) => <LoginPage
-        withMessage={{ message: i18n`Problem reaching the server`, 
description: i18n`Got message: ${error.message} from: ${error.backend} 
(hasToken: ${error.hasToken})`, type: 'ERROR', }}
-        onConfirm={updateLoginStatus} />}  
-
-    />
-
-    <Route path={RootPaths.new_instance} component={InstanceCreatePage}
-
-      onBack={() => route(RootPaths.list_instances)}
-
-      onConfirm={() => {
-        pushNotification({ message: i18n`create_success`, type: 'SUCCESS' });
-        route(RootPaths.list_instances);
-      }}
-
-      onError={(error: any) => {
-        pushNotification({ message: i18n`create_error`, type: 'ERROR' });
-      }} />
-
-    <Route path={RootPaths.instance_id_route} component={InstanceRoutes}
+  const list = useBackendInstances()
+
+  if (!list.data) {
+    if (list.unauthorized) {
+      return <Fragment>
+        <Menu onLogout={() => {
+          clearAllTokens();
+          route('/')
+        }} />
+        <LoginPage
+          withMessage={{ message: i18n`Access denied`, description: i18n`Check 
your token is valid`, type: 'ERROR', }}
+          onConfirm={updateLoginStatus}
+        />
+      </Fragment>
+    }
+    if (list.notfound) {
+      try {
+        const path = new URL(currentBaseUrl).pathname
+        const match = INSTANCE_ID_LOOKUP.exec(path)
+        if (!match || !match[1]) throw new Error(i18n`Could not infer instance 
id from url ${currentBaseUrl}`)
+        return <Fragment>
+          <Menu instance={match[1]} onLogout={() => {
+            clearAllTokens();
+            route('/')
+          }} />
+          <InstanceRoutes
+            id={match[1]}
+            addTokenCleaner={addTokenCleaner}
+            pushNotification={pushNotification}
+          />
+        </Fragment>
+      } catch (e) {
+        // this should be rare becuase
+        // query to /config is ok but the URL
+        // doest not match with our pattern
+        return <Fragment>
+          <Menu onLogout={() => {
+            clearAllTokens();
+            route('/')
+          }} />
+          <LoginPage
+            withMessage={{ message: i18n`Couldnt access the server`, 
description: e.message, type: 'ERROR', }}
+            onConfirm={updateLoginStatus}
+          />
+        </Fragment>
+      }
+    }
+    if (!list.error) {
+      return <div id="app">
+        <section class="section is-title-bar">
+
+          <div class="level">
+            <div class="level-left">
+              <div class="level-item">
+                <ul>
+                  <li>loading s</li>
+                </ul>
+              </div>
+            </div>
+          </div>
+        </section>
+      </div>
+    }
+    return <div id="app">
+      <section class="section is-title-bar">
+
+        <div class="level">
+          <div class="level-left">
+            <div class="level-item">
+              <ul>
+                <li>There was an unexpected error</li>
+                <li>{JSON.stringify(list.error)}</li>
+              </ul>
+            </div>
+          </div>
+        </div>
+      </section>
+    </div>
+  }
+
+  return <Fragment>
+    <Menu onLogout={() => {
+      clearAllTokens();
+      route('/')
+    }} />
+    <AdminRoutes instances={list.data.instances}
+      addTokenCleaner={addTokenCleaner}
       pushNotification={pushNotification}
-      addTokenCleaner={addTokenCleaner} />
-
-    <Route default component={NotFoundPage} />
-
-  </Router>;
+    />
+  </Fragment>
 }
diff --git a/packages/frontend/src/InstanceRoutes.tsx 
b/packages/frontend/src/InstanceRoutes.tsx
index 14c1da6..b3adc92 100644
--- a/packages/frontend/src/InstanceRoutes.tsx
+++ b/packages/frontend/src/InstanceRoutes.tsx
@@ -20,28 +20,29 @@
 */
 
 import { h, VNode } from 'preact';
-import { useCallback, useContext, useEffect, useMemo } from "preact/hooks";
+import { useCallback, useEffect, useMemo } from "preact/hooks";
 import { Route, Router, route } from 'preact-router';
 import { useMessageTemplate } from 'preact-messages';
 import { useBackendInstanceToken } from './hooks';
-import { BackendContext, InstanceContext } from './context/backend';
+import { InstanceContextProvider, useBackendContext } from './context/backend';
 import { SwrError } from "./hooks/backend";
 import { InstancePaths } from "./index";
 import { Notification } from './utils/types';
 import NotFoundPage from './routes/notfound';
 import LoginPage from './routes/login';
-import InstanceDetailsPage from "./routes/instances/details";
 import InstanceUpdatePage from "./routes/instances/update";
+import DetailPage from './routes/instances/details';
 
 export interface Props {
   id: string;
   pushNotification: (n: Notification) => void;
   addTokenCleaner: any;
+  parent?: string;
 }
 
-export function InstanceRoutes({ id, pushNotification, addTokenCleaner }: 
Props): VNode {
+export function InstanceRoutes({ id, pushNotification, addTokenCleaner, parent 
}: Props): VNode {
   const [token, updateToken] = useBackendInstanceToken(id);
-  const { changeBackend } = useContext(BackendContext);
+  const { changeBackend } = useBackendContext();
   const cleaner = useCallback(() => { updateToken(undefined); }, [id]);
   const i18n = useMessageTemplate('');
 
@@ -55,12 +56,23 @@ export function InstanceRoutes({ id, pushNotification, 
addTokenCleaner }: Props)
       updateToken(token);
   };
 
-  const value = useMemo(() => ({ id, token }), [id, token])
+  const value = useMemo(() => ({ id, token, admin: !!parent }), [id, token])
 
-  return <InstanceContext.Provider value={value}>
+  return <InstanceContextProvider value={value}>
     <Router>
+      <Route path={(!parent? "" : parent) + InstancePaths.details}
+        component={DetailPage}
 
-      <Route path={InstancePaths.update}
+        onUnauthorized={() => <LoginPage
+          withMessage={{ message: i18n`Access denied`, description: i18n`Check 
your token is valid`, type: 'ERROR', }}
+          onConfirm={updateLoginStatus} />}
+
+        onLoadError={(error: SwrError) => <LoginPage
+          withMessage={{ message: i18n`Problem reaching the server`, 
description: i18n`Got message: ${error.message} from: ${error.backend} 
(hasToken: ${error.hasToken})`, type: 'ERROR', }}
+          onConfirm={updateLoginStatus} />}
+      />     
+
+      <Route path={(!parent? "" : parent) + InstancePaths.update}
         component={InstanceUpdatePage}
 
         onUnauthorized={() => <LoginPage
@@ -72,12 +84,12 @@ export function InstanceRoutes({ id, pushNotification, 
addTokenCleaner }: Props)
           onConfirm={updateLoginStatus} />}
 
         onBack={() => {
-          route(`/instances`);
+          route(`/`);
         }}
 
         onConfirm={() => {
           pushNotification({ message: i18n`create_success`, type: 'SUCCESS' });
-          route(`/instances`);
+          route(`/`);
         }}
 
         onUpdateError={(e: Error) => {
@@ -87,6 +99,6 @@ export function InstanceRoutes({ id, pushNotification, 
addTokenCleaner }: Props)
 
       <Route default component={NotFoundPage} />
     </Router>
-  </InstanceContext.Provider>;
+  </InstanceContextProvider>;
 
 }
diff --git a/packages/frontend/src/components/auth/index.tsx 
b/packages/frontend/src/components/auth/index.tsx
index fd5e717..10854b1 100644
--- a/packages/frontend/src/components/auth/index.tsx
+++ b/packages/frontend/src/components/auth/index.tsx
@@ -22,7 +22,7 @@
 import { h, VNode } from "preact";
 import { useMessageTemplate } from "preact-messages";
 import { useContext, useState } from "preact/hooks";
-import { BackendContext } from "../../context/backend";
+import { useBackendContext } from "../../context/backend";
 import { Notification } from "../../utils/types";
 
 interface Props {
@@ -31,16 +31,14 @@ interface Props {
 }
 
 export function LoginModal({ onConfirm, withMessage }: Props): VNode {
-  const backend = useContext(BackendContext)
+  const backend = useBackendContext()
   const [token, setToken] = useState(backend.token || '')
   const [url, setURL] = useState(backend.url)
-  // const [updatingToken, setUpdatingToken] = useState(false)
-  // const toggleUpdatingToken = (): void => setUpdatingToken(v => !v)
   const i18n = useMessageTemplate()
 
   return <div class="columns is-centered">
-    <div class="column is-two-thirds ">
-      <div class="modal-card">
+    <div class="column is-two-thirds " >
+      <div class="modal-card" style={{ width: '100%', margin: 0 }}>
         {withMessage && <div class={withMessage.type === 'ERROR' ? 
"notification is-danger" : "notification is-info"}>
           <div class="columns is-vcentered">
             <div class="column is-12">
@@ -51,10 +49,10 @@ export function LoginModal({ onConfirm, withMessage }: 
Props): VNode {
             </div>
           </div>
         </div>}
-        <header class="modal-card-head" style={{border: '1px solid', 
borderBottom: 0}}>
+        <header class="modal-card-head" style={{ border: '1px solid', 
borderBottom: 0 }}>
           <p class="modal-card-title">{i18n`Login required`}</p>
         </header>
-        <section class="modal-card-body" style={{border: '1px solid', 
borderTop: 0, borderBottom: 0}}>
+        <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`}
           <div class="field is-horizontal">
             <div class="field-label is-normal">
@@ -63,7 +61,11 @@ export function LoginModal({ onConfirm, withMessage }: 
Props): VNode {
             <div class="field-body">
               <div class="field">
                 <p class="control is-expanded">
-                  <input class="input" type="text" placeholder="set new url" 
name="id" value={url} onInput={(e): void => setURL(e?.currentTarget.value)} />
+                  <input class="input" type="text" placeholder="set new url" 
name="id"
+                    value={url}
+                    onKeyPress={e => e.keyCode === 13 ? onConfirm(url, token ? 
token : undefined) : null}
+                    onInput={(e): void => setURL(e?.currentTarget.value)}
+                  />
                 </p>
               </div>
             </div>
@@ -73,15 +75,13 @@ export function LoginModal({ onConfirm, withMessage }: 
Props): VNode {
               <label class="label">Token</label>
             </div>
             <div class="field-body">
-              {/* <div class="field has-addons">
-              <label class="b-checkbox checkbox">
-                <input type="checkbox" checked={updatingToken} 
onClick={toggleUpdatingToken} />
-                <span class="check" />
-              </label> */}
-
               <div class="field">
                 <p class="control is-expanded">
-                  <input class="input" type="text" placeholder={true ? "set 
new token" : "hidden token value"} name="id" value={token} onInput={(e): void 
=> setToken(e?.currentTarget.value)} />
+                  <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)}
+                  />
                 </p>
               </div>
             </div>
@@ -95,6 +95,4 @@ export function LoginModal({ onConfirm, withMessage }: 
Props): VNode {
       </div>
     </div>
   </div>
-
-  // </div>
 }
\ No newline at end of file
diff --git a/packages/frontend/src/components/form/InputArray.tsx 
b/packages/frontend/src/components/form/InputArray.tsx
index d7aef82..da9b48d 100644
--- a/packages/frontend/src/components/form/InputArray.tsx
+++ b/packages/frontend/src/components/form/InputArray.tsx
@@ -87,7 +87,7 @@ export function InputArray<T>({ name, readonly, addonBefore, 
isValid, fromStr =
           <Message id={`validation.${error.type}`} 
fields={error.params}>{error.message}</Message>
         </p> : null}
         {array.map(v => <div class="tags has-addons">
-          <span class="tag is-medium is-info">{v}</span>
+          <span class="tag is-medium is-info" 
style={{maxWidth:'90%'}}>{v}</span>
           <a class="tag is-medium is-danger is-delete" onClick={() => {
             onChange(array.filter(f => f !== v) as any);
             setCurrentValue(toStr(v));
diff --git a/packages/frontend/src/components/form/InputSecured.tsx 
b/packages/frontend/src/components/form/InputSecured.tsx
index 53acd76..01b0f19 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 { Message, useMessage } from "preact-messages";
 import { useState } from "preact/hooks";
-import { DeleteModal } from "../modal";
 import { useField } from "./Field";
 
 export interface Props<T> {
@@ -46,7 +45,7 @@ export function InputSecured<T>({ name, readonly }: 
Props<T>): VNode {
   const tooltip = useMessage(`fields.instance.${name}.tooltip`, {});
 
   const [active, setActive] = useState(false);
-  const [newValue, setNuewValue] = useState(toStr(value))
+  const [newValue, setNuewValue] = useState("")
 
   return <div class="field is-horizontal">
     <div class="field-label is-normal">
@@ -70,6 +69,9 @@ export function InputSecured<T>({ name, readonly }: 
Props<T>): VNode {
         </Fragment> :
         <Fragment>
           <div class="field has-addons">
+            <div class="control">
+              <a class="button is-static">secret-token:</a>
+            </div>
             <div class="control">
               <input class="input" type="password"
                 placeholder={placeholder} readonly={readonly || !active}
@@ -81,21 +83,21 @@ export function InputSecured<T>({ name, readonly }: 
Props<T>): VNode {
               <Message id={`fields.instance.${name}.help`}> </Message>
             </div>
             <div class="control">
-              <button class="button is-info" disabled={fromStr(newValue) === 
value} onClick={(): void => { onChange(fromStr(newValue)); setActive(!active); 
}} >
+              <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>Update</span>
               </button>
             </div>
           </div>
           <div class="control">
-            <button class="button is-danger" disabled={null === value} 
onClick={(): void => { onChange(null!); setActive(!active); }} >
+            <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>Remove</span>
             </button>
           </div>
           <div class="field ml-4">
             <div class="control">
-              <button class="button " onClick={(): void => { 
onChange(initial!); setActive(!active); }} >
+              <button class="button " onClick={(): void => { 
onChange(initial!); setActive(!active); setNuewValue(""); }} >
                 <div class="icon is-left"><i class="mdi mdi-lock-open-variant" 
/></div>
                 <span>Cancel update</span>
               </button>
diff --git a/packages/frontend/src/components/menu/LangSelector.tsx 
b/packages/frontend/src/components/menu/LangSelector.tsx
new file mode 100644
index 0000000..50a2199
--- /dev/null
+++ b/packages/frontend/src/components/menu/LangSelector.tsx
@@ -0,0 +1,60 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021 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 { h, VNode } from "preact";
+import { useState } from "preact/hooks";
+import { useBackendContext } from "../../context/backend";
+import langIcon from '../../assets/icons/languageicon.svg'
+import * as messages from '../../messages'
+
+type LangsNames = {
+  [P in keyof typeof messages]: string
+}
+
+const names: LangsNames = {
+  es: 'Español [es]',
+  en: 'English [en]',
+}
+
+function getLangName(s: keyof LangsNames | string) {
+  if (s === 'es' || s === 'en') return names[s]
+  return s
+}
+
+export function LangSelector(): VNode {
+  const [updatingLang, setUpdatingLang] = useState(false)
+  const { lang, setLang } = useBackendContext()
+  return <div class="dropdown is-active ">
+    <div class="dropdown-trigger">
+      <button class="button" aria-haspopup="true" 
aria-controls="dropdown-menu" onClick={() => setUpdatingLang(!updatingLang)}>
+        <div class="icon is-small is-left">
+          <img src={langIcon} />
+        </div>
+        <span>{getLangName(lang)}</span>
+        <div class="icon is-right">
+          <i class="mdi mdi-chevron-down" />
+        </div>
+      </button>
+    </div>
+    {updatingLang && <div class="dropdown-menu" id="dropdown-menu" role="menu">
+      <div class="dropdown-content">
+        {Object.keys(messages)
+          .filter((l) => l !== lang)
+          .map(l => <a class="dropdown-item" value={l} onClick={() => { 
setLang(l); setUpdatingLang(false) }}>{getLangName(l)}</a>)}
+      </div>
+    </div>}
+  </div>
+}
\ No newline at end of file
diff --git a/packages/frontend/src/components/menu/NavigationBar.tsx 
b/packages/frontend/src/components/menu/NavigationBar.tsx
new file mode 100644
index 0000000..22d432e
--- /dev/null
+++ b/packages/frontend/src/components/menu/NavigationBar.tsx
@@ -0,0 +1,58 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021 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/>
+ */
+
+/**
+*
+* @author Sebastian Javier Marchano (sebasjm)
+*/
+
+import { h, VNode } from 'preact';
+import logo from '../../assets/logo.jpeg';
+import { LangSelector } from './LangSelector';
+
+interface Props {
+  onMobileMenu: () => void;
+  title: string;
+}
+
+export function NavigationBar({ onMobileMenu, title }: Props): VNode {
+  return (<nav class="navbar is-fixed-top" role="navigation" aria-label="main 
navigation">
+    <div class="navbar-brand">
+      <span class="navbar-item" style={{ fontSize: 24, fontWeight: 900 
}}>{title}</span>
+
+      <a role="button" class="navbar-burger" aria-label="menu" 
aria-expanded="false" onClick={(e) => {
+        onMobileMenu()
+        e.stopPropagation()
+      }}>
+        <span aria-hidden="true" />
+        <span aria-hidden="true" />
+        <span aria-hidden="true" />
+      </a>
+    </div>
+
+    <div class="navbar-menu ">
+      <a class="navbar-start is-justify-content-center is-flex-grow-1" 
href="https://taler.net";>
+        <img src={logo} style={{ height: 50, maxHeight: 50 }} />
+      </a>
+      <div class="navbar-end">
+        <div class="navbar-item">
+          <LangSelector />
+        </div>
+      </div>
+    </div>
+  </nav>
+  );
+}
\ No newline at end of file
diff --git a/packages/frontend/src/components/sidebar/index.tsx 
b/packages/frontend/src/components/menu/SideBar.tsx
similarity index 59%
rename from packages/frontend/src/components/sidebar/index.tsx
rename to packages/frontend/src/components/menu/SideBar.tsx
index 5bc415c..5967117 100644
--- a/packages/frontend/src/components/sidebar/index.tsx
+++ b/packages/frontend/src/components/menu/SideBar.tsx
@@ -20,36 +20,52 @@
 */
 
 
-import { h, VNode } from 'preact';
+import { Fragment, h, VNode } from 'preact';
 import { useContext } from 'preact/hooks';
-import { BackendContext, useConfigContext } from '../../context/backend';
+import { useBackendContext, useConfigContext } from '../../context/backend';
+import { LangSelector } from './LangSelector';
 
 interface Props {
   onLogout: () => void;
+  mobile?: boolean;
+  instance?: string;
 }
 
-export function Sidebar({ onLogout }: Props): VNode {
+export function Sidebar({ mobile, instance, onLogout }: Props): VNode {
   const config = useConfigContext();
-  const backend = useContext(BackendContext);
+  const backend = useBackendContext();
+
   return (
     <aside class="aside is-placed-left is-expanded">
+      { mobile && <div class="footer" onClick={(e) => { return 
e.stopImmediatePropagation() }}>
+        <LangSelector />
+      </div>}
       <div class="aside-tools">
         <div class="aside-tools-label">
-          <span><b>Taler</b> Merchant Backend</span>
+          <div><b>Taler</b> Merchant Office</div>
+          <div class="is-size-7 has-text-right " style={{ lineHeight: 0, 
marginTop: -10 }}>v{process.env.__VERSION__} ({config.version})</div>
         </div>
       </div>
       <div class="menu is-menu-main">
-        <p class="menu-label">General</p>
-        <ul class="menu-list">
-          <li>
-            <a href="/" class="is-active router-link-active has-icon">
-              <span class="icon"><i class="mdi mdi-desktop-mac" /></span>
-              <span class="menu-item-label">Instances</span>
-            </a>
-          </li>
-        </ul>
+        {!instance && <Fragment>
+          <p class="menu-label">General</p>
+          <ul class="menu-list">
+            <li>
+              <a href="/" class="is-active router-link-active has-icon">
+                <span class="icon"><i class="mdi mdi-desktop-mac" /></span>
+                <span class="menu-item-label">Instances</span>
+              </a>
+            </li>
+          </ul>
+        </Fragment>}
         <p class="menu-label">Instance</p>
         <ul class="menu-list">
+          { instance && <li>
+            <a href="/update" class="has-icon">
+              <span class="icon"><i class="mdi mdi-square-edit-outline" 
/></span>
+              <span class="menu-item-label">Settings</span>
+            </a>
+          </li> }
           <li>
             <a href="/forms" class="has-icon">
               <span class="icon"><i class="mdi mdi-square-edit-outline" 
/></span>
@@ -73,25 +89,21 @@ export function Sidebar({ onLogout }: Props): VNode {
         <ul class="menu-list">
           <li>
             <div>
-              <span class="icon"><i class="mdi mdi-currency-eur" /></span>
+              <span style={{ width: '3rem' }} class="icon"><i class="mdi 
mdi-currency-eur" /></span>
               <span class="menu-item-label">{config.currency}</span>
             </div>
           </li>
           <li>
-            <div>
-              <span class="icon"><i class="mdi mdi-web" /></span>
-              <span class="menu-item-label">{backend.url}</span>
-            </div>
-          </li>
-          <li>
-            <div>
-              <span class="icon">V</span>
-              <span class="menu-item-label">{config.version}</span>
+            <div >
+              <span style={{ width: '3rem' }} class="icon"><i class="mdi 
mdi-web" /></span>
+              <span class="menu-item-label">
+                {new URL(backend.url).hostname} / {!instance ? "default" : 
instance}
+              </span>
             </div>
           </li>
           <li>
             <a class="has-icon is-state-info is-hoverable" onClick={(): void 
=> onLogout()}>
-              <span class="icon"><i class="mdi mdi-logout default"></i></span>
+              <span class="icon"><i class="mdi mdi-logout default" /></span>
               <span class="menu-item-label">Log out</span>
             </a>
           </li>
diff --git a/packages/frontend/src/routes/instances/create/index.tsx 
b/packages/frontend/src/components/menu/index.tsx
similarity index 53%
copy from packages/frontend/src/routes/instances/create/index.tsx
copy to packages/frontend/src/components/menu/index.tsx
index 819b549..6353854 100644
--- a/packages/frontend/src/routes/instances/create/index.tsx
+++ b/packages/frontend/src/components/menu/index.tsx
@@ -13,24 +13,23 @@
  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 { h, VNode } from "preact";
-import { MerchantBackend } from "../../../declaration";
-import { useBackendMutateAPI } from "../../../hooks/backend";
-import { CreatePage } from "./CreatePage";
+import { useState } from "preact/hooks";
+import { NavigationBar } from "./NavigationBar";
+import { Sidebar } from "./SideBar";
 
 interface Props {
-  onBack: () => void;
-  onConfirm: () => void;
-  onError: (error: any) => void;
+  instance?: string;
+  onLogout?: () => void;
 }
 
-export default function Create({ onBack, onConfirm, onError }: Props): VNode {
-  const { createInstance } = useBackendMutateAPI();
+export function Menu({ onLogout, instance }: Props): VNode {
+  const [mobileOpen, setMobileOpen] = useState(false)
+  const title = !onLogout ? "Welcome!" : ( !instance ? "Admin" : instance)
+  return <div class={mobileOpen ? "has-aside-mobile-expanded" : ""} 
onClick={() => setMobileOpen(false)}>
+    <NavigationBar onMobileMenu={() => setMobileOpen(!mobileOpen)} 
title={title}/>
+    {onLogout && <Sidebar onLogout={onLogout} instance={instance} 
mobile={mobileOpen} />}
+  </div>
 
-  return <CreatePage 
-    onBack={onBack}
-    isLoading={false}
-    onCreate={(d: MerchantBackend.Instances.InstanceConfigurationMessage): 
Promise<void> => {
-      return createInstance(d).then(onConfirm).catch(onError)
-    }} />
 }
\ No newline at end of file
diff --git a/packages/frontend/src/components/modal/index.tsx 
b/packages/frontend/src/components/modal/index.tsx
index 682c1ed..aaa50fc 100644
--- a/packages/frontend/src/components/modal/index.tsx
+++ b/packages/frontend/src/components/modal/index.tsx
@@ -23,6 +23,7 @@
 import { h, VNode } from "preact";
 import { Message } from "preact-messages";
 import { useState } from "preact/hooks";
+import { MerchantBackend } from "../../declaration";
 import { FormProvider } from "../form/Field";
 import { Input } from "../form/Input";
 
@@ -92,7 +93,7 @@ export function DeleteModal({ element, onCancel, onConfirm }: 
DeleteModalProps):
 
 interface UpdateTokenModalProps {
   element: { id: string, name: string };
-  oldToken: string;
+  oldToken?: string;
   onCancel: () => void;
   onConfirm: (value: string) => void;
   onClear: () => void;
@@ -105,7 +106,7 @@ export function UpdateTokenModal({ element, onCancel, 
onClear, onConfirm, oldTok
   })
 
   const errors = {
-    old_token: oldToken !== form.old_token ? { message: 'should be the same' } 
: undefined,
+    old_token: oldToken && oldToken !== form.old_token ? { message: 'should be 
the same' } : undefined,
     new_token: !form.new_token ? { message: 'should be the same' } : ( 
form.new_token === form.old_token ? { message: 'cant repeat' } : undefined ),
   }
   
diff --git a/packages/frontend/src/components/navbar/index.tsx 
b/packages/frontend/src/components/navbar/index.tsx
deleted file mode 100644
index eb7857e..0000000
--- a/packages/frontend/src/components/navbar/index.tsx
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021 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/>
- */
-
-/**
-*
-* @author Sebastian Javier Marchano (sebasjm)
-*/
-
-import { h, VNode } from 'preact';
-import * as messages from '../../messages'
-import logo from '../../assets/logo.jpeg';
-import langIcon from '../../assets/icons/languageicon.svg'
-import { useState } from 'preact/hooks';
-
-interface Props {
-  lang: string;
-  setLang: (l: string) => void;
-}
-
-type LangsNames = {
-  [P in keyof typeof messages]: string
-}
-
-const names: LangsNames = {
-  es: 'Español [es]', 
-  en: 'English [en]',
-}
-
-function getLangName(s: keyof LangsNames | string) {
-  if (s === 'es' || s === 'en') return names[s]
-  return s
-}
-
-
-export function NavigationBar({ lang, setLang }: Props): VNode {
-  const [updatingLang, setUpdatingLang] = useState(false)
-  return (<nav class="navbar is-fixed-top" role="navigation" aria-label="main 
navigation">
-    <div class="navbar-brand">
-      <a class="navbar-item" href="https://taler.net";>
-        <img src={logo} style={{ height: 50, maxHeight: 50 }} />
-      </a>
-
-      <a role="button" class="navbar-burger" aria-label="menu" 
aria-expanded="false" data-target="navbarBasicExample">
-        <span aria-hidden="true" />
-        <span aria-hidden="true" />
-        <span aria-hidden="true" />
-      </a>
-    </div>
-
-    <div class="navbar-menu">
-
-      <div class="navbar-end">
-        <div class="navbar-item">
-
-          <div class="dropdown is-active ">
-            <div class="dropdown-trigger">
-              <button class="button" aria-haspopup="true" 
aria-controls="dropdown-menu" onClick={() => setUpdatingLang(!updatingLang)}>
-                <div class="icon is-small is-left">
-                  <img src={langIcon} />
-                </div>
-                <span>{getLangName(lang)}</span>
-                <div class="icon is-right">
-                  <i class="mdi mdi-chevron-down" />
-                </div>
-              </button>
-            </div>
-            { updatingLang && <div class="dropdown-menu" id="dropdown-menu" 
role="menu">
-              <div class="dropdown-content">
-                {Object.keys(messages)
-                  .filter((l) => l !== lang)
-                  .map(l => <a class="dropdown-item" value={l} onClick={() => 
{setLang(l); setUpdatingLang(false)}}>{getLangName(l)}</a>)}
-              </div>
-            </div> }
-          </div>
-        </div>
-      </div>
-    </div>
-  </nav>
-  );
-}
\ No newline at end of file
diff --git a/packages/frontend/src/context/backend.ts 
b/packages/frontend/src/context/backend.ts
index a41c32d..5b9b648 100644
--- a/packages/frontend/src/context/backend.ts
+++ b/packages/frontend/src/context/backend.ts
@@ -20,6 +20,7 @@ export interface BackendContextType {
   url: string;
   token?: string;
   changeBackend: (url: string) => void;
+  resetBackend: () => void;
   // clearTokens: () => void;
   // addTokenCleaner: (c: StateUpdater<string | undefined>) => void;
   updateToken: (token?:string) => void;
@@ -35,25 +36,29 @@ export interface ConfigContextType {
 export interface InstanceContextType {
   id: string;
   token?: string;
+  admin?: boolean;
 }
 
-export const BackendContext = createContext<BackendContextType>({
+const BackendContext = createContext<BackendContextType>({
   url: '',
   lang: 'en',
   token: undefined,
   changeBackend: () => null,
+  resetBackend: () => null,
   // clearTokens: () => null,
   // addTokenCleaner: () => null,
   updateToken: () => null,
   setLang: () => null,
 })
 
-export const ConfigContext = createContext<ConfigContextType>(null!)
+const ConfigContext = createContext<ConfigContextType>(null!)
 
-export const useConfigContext = () => useContext(ConfigContext);
+const InstanceContext = createContext<InstanceContextType>({} as any)
 
+export const ConfigContextProvider = ConfigContext.Provider
+export const useConfigContext = () => useContext(ConfigContext);
+export const BackendContextProvider = BackendContext.Provider
+export const useBackendContext = () => useContext(BackendContext);
+export const InstanceContextProvider = InstanceContext.Provider
+export const useInstanceContext = () => useContext(InstanceContext);
 
-export const InstanceContext = createContext<InstanceContextType>({
-  id: '',
-  token: undefined,
-})
\ No newline at end of file
diff --git a/packages/frontend/src/declaration.d.ts 
b/packages/frontend/src/declaration.d.ts
index a1d4b65..6a08212 100644
--- a/packages/frontend/src/declaration.d.ts
+++ b/packages/frontend/src/declaration.d.ts
@@ -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)
+*/
 
 
 
@@ -101,6 +101,24 @@ export namespace MerchantBackend {
     }
     namespace Instances {
 
+        //POST /private/instances/$INSTANCE/auth
+        interface InstanceAuthConfigurationMessage {
+            // Type of authentication.
+            // "external":  The mechant backend does not do
+            //   any authentication checks.  Instead an API
+            //   gateway must do the authentication.
+            // "token": The merchant checks an auth token.
+            //   See "token" for details.
+            method: "external" | "token";
+
+            // For method "external", this field is mandatory.
+            // The token MUST begin with the string "secret-token:".
+            // After the auth token has been set (with method "token"),
+            // the value must be provided in a "Authorization: Bearer $token"
+            // header.
+            token?: string;
+
+        }
         //POST /private/instances
         interface InstanceConfigurationMessage {
             // The URI where the wallet will send coins.  A merchant may have
@@ -121,7 +139,7 @@ export namespace MerchantBackend {
             // Optional, if not given authentication will be disabled for
             // this instance (hopefully authentication checks are still
             // done by some reverse proxy).
-            auth_token?: string;
+            auth: InstanceAuthConfigurationMessage;
 
             // The merchant's physical address (to be put into contracts).
             address: Location;
@@ -164,12 +182,6 @@ export namespace MerchantBackend {
             // Merchant name corresponding to this instance.
             name: string;
 
-            // "Authentication" header required to authorize management access 
the instance.
-            // Optional, if not given authentication will be disabled for
-            // this instance (hopefully authentication checks are still
-            // done by some reverse proxy).
-            auth_token?: string;
-
             // The merchant's physical address (to be put into contracts).
             address: Location;
 
@@ -265,6 +277,11 @@ export namespace MerchantBackend {
             // offers we make be valid by default?
             default_pay_deadline: RelativeTime;
 
+            // Authentication configuration.
+            // Does not contain the token when token auth is configured.
+            auth: {
+                method: "external" | "token";
+            };
         }
 
         interface MerchantAccount {
diff --git a/packages/frontend/src/hooks/backend.ts 
b/packages/frontend/src/hooks/backend.ts
index 8781e87..56c85e0 100644
--- a/packages/frontend/src/hooks/backend.ts
+++ b/packages/frontend/src/hooks/backend.ts
@@ -23,7 +23,7 @@ import useSWR, { mutate } from 'swr';
 import axios from 'axios'
 import { MerchantBackend } from '../declaration';
 import { useContext } from 'preact/hooks';
-import { BackendContext, InstanceContext } from '../context/backend';
+import { useBackendContext, useInstanceContext } from '../context/backend';
 
 type HttpResponse<T> = HttpResponseOk<T> | HttpResponseError;
 
@@ -41,6 +41,7 @@ export interface SwrError {
 interface HttpResponseError {
   data: undefined;
   unauthorized: boolean;
+  notfound: boolean;
   error?: SwrError;
 }
 
@@ -56,9 +57,17 @@ interface RequestOptions {
 
 
 async function request(url: string, options: RequestOptions = {}): 
Promise<any> {
-  const headers = options.token ? { Authorization: `${options.token}` } : 
undefined
+  const headers = options.token ? { Authorization: `Bearer ${options.token}` } 
: undefined
 
   try {
+    // http://localhost:9966/instances/blog/private/instances
+    // Hack, endpoint should respond 404
+    if (/^\/instances\/[^/]*\/private\/instances$/.test(new 
URL(url).pathname)) {
+      console.warn(`HACK: Not going to query ${url}, instead return 404`)
+      throw ({ response: { status: 404 }, message: 'not found' })
+    }
+
+
     const res = await axios({
       method: options.method || 'get',
       url,
@@ -79,18 +88,12 @@ function fetcher(url: string, token: string, backend: 
string) {
   return request(`${backend}${url}`, { token })
 }
 
-interface BackendMutateAPI {
+interface AdminMutateAPI {
   createInstance: (data: 
MerchantBackend.Instances.InstanceConfigurationMessage) => Promise<void>;
+  deleteInstance: (id: string) => Promise<void>;
 }
-interface BackendInstaceMutateAPI {
-  updateInstance: (data: 
MerchantBackend.Instances.InstanceReconfigurationMessage) => Promise<void>;
-  deleteInstance: () => Promise<void>;
-  clearToken: () => Promise<void>;
-  setNewToken: (token: string) => Promise<void>;
-}
-
-export function useBackendMutateAPI(): BackendMutateAPI {
-  const { url, token } = useContext(BackendContext)
+export function useAdminMutateAPI(): AdminMutateAPI {
+  const { url, token } = useBackendContext()
 
   const createInstance = async (instance: 
MerchantBackend.Instances.InstanceConfigurationMessage): Promise<void> => {
     await request(`${url}/private/instances`, {
@@ -99,77 +102,105 @@ export function useBackendMutateAPI(): BackendMutateAPI {
       data: instance
     })
 
-    mutate('/private/instances')
+    mutate(['/private/instances', token, url], null)
+  }
+
+  const deleteInstance = async (id: string): Promise<void> => {
+    await request(`${url}/private/`, {
+      method: 'delete',
+      token,
+    })
+
+    mutate(['/private/instances', token, url], null)
   }
-  return { createInstance }
+
+  return { createInstance, deleteInstance }
+}
+
+interface InstaceMutateAPI {
+  updateInstance: (data: 
MerchantBackend.Instances.InstanceReconfigurationMessage, a?: 
MerchantBackend.Instances.InstanceAuthConfigurationMessage) => Promise<void>;
+  deleteInstance: () => Promise<void>;
+  clearToken: () => Promise<void>;
+  setNewToken: (token: string) => Promise<void>;
 }
 
-export function useBackendInstanceMutateAPI(): BackendInstaceMutateAPI {
-  const { url } = useContext(BackendContext)
-  const { id, token } = useContext(InstanceContext)
+export function useInstanceMutateAPI(): InstaceMutateAPI {
+  const { url: baseUrl, token: adminToken } = useBackendContext()
+  const { token, id, admin } = useInstanceContext()
+
+  const url = !admin ? baseUrl: `${baseUrl}/instances/${id}`
 
-  const updateInstance = async (instance: 
MerchantBackend.Instances.InstanceReconfigurationMessage): Promise<void> => {
-    await request(`${url}/private/instances/${id}`, {
+  const updateInstance = async (instance: 
MerchantBackend.Instances.InstanceReconfigurationMessage, auth?: 
MerchantBackend.Instances.InstanceAuthConfigurationMessage): Promise<void> => {
+    await request(`${url}/private/`, {
       method: 'patch',
       token,
       data: instance
     })
 
-    mutate('/private/instances', null)
-    mutate(`/private/instances/${id}`, null)
+    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)
   };
-  
+
   const deleteInstance = async (): Promise<void> => {
-    await request(`${url}/private/instances/${id}`, {
+    await request(`${url}/private/`, {
       method: 'delete',
-      token,
+      token: adminToken,
     })
 
-    mutate('/private/instances', null)
-    mutate(`/private/instances/${id}`, null)
+    if (adminToken) mutate(['/private/instances', adminToken, baseUrl], null)
+    mutate([`/private/`, token, url], null)
   }
 
   const clearToken = async (): Promise<void> => {
-    await request(`${url}/private/instances/${id}`, {
-      method: 'patch',
+    await request(`${url}/private/auth`, {
+      method: 'post',
       token,
-      data: { auth_token: null }
+      data: { method: 'external' }
     })
 
-    mutate(`/private/instances/${id}`, null)
+    mutate([`/private/`, token, url], null)
   }
 
-  const setNewToken = async (token: string): Promise<void> => {
-    await request(`${url}/private/instances/${id}`, {
-      method: 'patch',
+  const setNewToken = async (newToken: string): Promise<void> => {
+    await request(`${url}/private/auth`, {
+      method: 'post',
       token,
-      data: { auth_token: token }
+      data: { method: 'token', token: newToken }
     })
 
-    mutate(`/private/instances/${id}`, null)
+    mutate([`/private/`, token, url], null)
   }
 
   return { updateInstance, deleteInstance, setNewToken, clearToken }
 }
 
 export function useBackendInstances(): 
HttpResponse<MerchantBackend.Instances.InstancesResponse> {
-  const { url, token } = useContext(BackendContext)
+  const { url, token } = useBackendContext()
   const { data, error } = useSWR<MerchantBackend.Instances.InstancesResponse, 
SwrError>(['/private/instances', token, url], fetcher)
 
-  return { data, unauthorized: error?.status === 401, error }
+  return { data, unauthorized: error?.status === 401, notfound: error?.status 
=== 404, error }
 }
 
 export function useBackendInstance(): 
HttpResponse<MerchantBackend.Instances.QueryInstancesResponse> {
-  const { url } = useContext(BackendContext);
-  const { id, token } = useContext(InstanceContext);
-  const { data, error } = 
useSWR<MerchantBackend.Instances.QueryInstancesResponse, 
SwrError>([`/private/instances/${id}`, token, url], fetcher)
+  const { url: baseUrl } = useBackendContext();
+  const { token, id, admin } = useInstanceContext();
+
+  const url = !admin ? baseUrl: `${baseUrl}/instances/${id}`
+
+  const { data, error } = 
useSWR<MerchantBackend.Instances.QueryInstancesResponse, 
SwrError>([`/private/`, token, url], fetcher)
 
-  return { data, unauthorized: error?.status === 401, error }
+  return { data, unauthorized: error?.status === 401, notfound: error?.status 
=== 404, error }
 }
 
 export function useBackendConfig(): 
HttpResponse<MerchantBackend.VersionResponse> {
-  const { url, token } = useContext(BackendContext)
+  const { url, token } = useBackendContext()
   const { data, error } = useSWR<MerchantBackend.VersionResponse, 
SwrError>(['/config', token, url], fetcher)
 
-  return { data, unauthorized: error?.status === 401, error }
+  return { data, unauthorized: error?.status === 401, notfound: error?.status 
=== 404, error }
 }
diff --git a/packages/frontend/src/hooks/index.ts 
b/packages/frontend/src/hooks/index.ts
index edd76b8..514a458 100644
--- a/packages/frontend/src/hooks/index.ts
+++ b/packages/frontend/src/hooks/index.ts
@@ -25,16 +25,17 @@ import { ValueOrFunction } from '../utils/types';
 
 export function useBackendContextState() {
   const [lang, setLang] = useLang()
-  const [url, changeBackend] = useBackendURL();
+  const [url, changeBackend, resetBackend] = useBackendURL();
   const [token, updateToken] = useBackendDefaultToken();
 
-  return { url, token, changeBackend, updateToken, lang, setLang }
+  return { url, token, changeBackend, updateToken, lang, setLang, resetBackend 
}
 }
 
-export function useBackendURL(): [string, StateUpdater<string>] {
+export function useBackendURL(): [string, StateUpdater<string>, () => void] {
   const [value, setter] = useNotNullLocalStorage('backend-url', typeof window 
!== 'undefined' ? window.location.origin : '')
   const checkedSetter = (v: ValueOrFunction<string>) => setter(p => (v 
instanceof Function ? v(p) : v).replace(/\/$/, ''))
-  return [value, checkedSetter]
+  const reset = () => checkedSetter(typeof window !== 'undefined' ? 
window.location.origin : '')
+  return [value, checkedSetter, reset]
 }
 export function useBackendDefaultToken(): [string | undefined, 
StateUpdater<string | undefined>] {
   return useLocalStorage('backend-token')
diff --git a/packages/frontend/src/index.tsx b/packages/frontend/src/index.tsx
index 62e96f9..3a047b5 100644
--- a/packages/frontend/src/index.tsx
+++ b/packages/frontend/src/index.tsx
@@ -26,18 +26,17 @@ import { useCallback, useContext, useEffect, useMemo, 
useState } from "preact/ho
 import { Route, route } from 'preact-router';
 import { MessageProvider, useMessageTemplate } from 'preact-messages';
 
-import { Sidebar } from './components/sidebar';
-import { NavigationBar } from './components/navbar';
 import { Notifications } from './components/notifications';
 import * as messages from './messages'
 import { useBackendContextState } from './hooks';
 import { useNotifications } from "./hooks/notifications";
-import { BackendContext, ConfigContext } from './context/backend';
+import { BackendContextProvider, ConfigContextProvider, useBackendContext } 
from './context/backend';
 import { useBackendConfig } from "./hooks/backend";
 import { hasKey, onTranslationError } from "./utils/functions";
 
 import LoginPage from './routes/login';
 import { ApplicationReadyRoutes } from "./ApplicationReadyRoutes";
+import { Menu } from "./components/menu";
 
 export enum RootPaths {
   root = '/',
@@ -47,7 +46,8 @@ export enum RootPaths {
 }
 
 export enum InstancePaths {
-  update = '/instance/:id/update',
+  details = '/',
+  update = '/update',
 }
 
 export function Redirect({ to }: { to: string }): null {
@@ -61,53 +61,57 @@ export default function Application(): VNode {
   const state = useBackendContextState()
 
   return (
-    <BackendContext.Provider value={state}>
+    <BackendContextProvider value={state}>
       <MessageProvider locale={state.lang} onError={onTranslationError} 
messages={hasKey(messages, state.lang) ? messages[state.lang] : messages.en} 
pathSep={null as any} >
         <ApplicationStatusRoutes />
       </MessageProvider >
-    </BackendContext.Provider>
+    </BackendContextProvider>
   );
 }
 
 function ApplicationStatusRoutes(): VNode {
   const { notifications, pushNotification, removeNotification } = 
useNotifications()
-  const { lang, setLang, changeBackend, updateToken, token } = 
useContext(BackendContext)
+  const { changeBackend, updateToken, resetBackend } = useBackendContext()
   const backendConfig = useBackendConfig();
   const i18n = useMessageTemplate()
 
 
-  const cleaner = useCallback(() => { updateToken(undefined) }, [])
-  const [cleaners, setCleaners] = useState([cleaner])
+  const tokenCleaner = useCallback(() => { updateToken(undefined) }, [])
+  const [cleaners, setCleaners] = useState([tokenCleaner])
   const addTokenCleaner = (c: () => void) => setCleaners(cs => [...cs, c])
-  const addTokenCleanerNemo = useCallback((c: () => void) => { 
addTokenCleaner(c) }, [cleaner])
+  const addTokenCleanerMemo = useCallback((c: () => void) => { 
addTokenCleaner(c) }, [tokenCleaner])
+  const clearAllTokens = () => {
+    cleaners.forEach(c => c())
+    resetBackend()
+  }
 
-  const v = backendConfig.data?.currency + ' ' + backendConfig.data?.version
+  const v = `${backendConfig.data?.currency} ${backendConfig.data?.version}`
   const ctx = useMemo(() => ({ currency: backendConfig.data?.currency || '', 
version: backendConfig.data?.version || '' }), [v])
 
-  if (!token) {
-    return <div id="app">
-      <NavigationBar lang={lang} setLang={setLang} />
-      <LoginPage
-        onConfirm={(url: string, token?: string) => {
-          changeBackend(url)
-          if (token) updateToken(token)
-          route(RootPaths.list_instances)
-        }}
-      />
-    </div>
-  }
-
   if (!backendConfig.data) {
-    if (!backendConfig.error) return <div class="is-loading"></div>
-    if (backendConfig.unauthorized) return <div class="asd">asd </div>
+
+    if (!backendConfig.error) return <div class="is-loading" />
+
+    if (backendConfig.unauthorized) {
+      return <div id="app">
+        <Menu />
+        <LoginPage
+          onConfirm={(url: string, token?: string) => {
+            changeBackend(url)
+            if (token) updateToken(token)
+            route(RootPaths.list_instances)
+          }}
+        />
+      </div>
+    }
 
     return <div id="app">
-      <NavigationBar lang={lang} setLang={setLang} />
+      <Menu />
       <LoginPage
         withMessage={{
           message: i18n`Couldnt access the server`,
           type: 'ERROR',
-          description: !backendConfig.data && backendConfig.error ? i18n`Got 
message: ${backendConfig.error.message} from: ${backendConfig.error.backend} 
(hasToken: ${backendConfig.error.hasToken})` : undefined,
+          description: i18n`Got message: ${backendConfig.error.message} from: 
${backendConfig.error.backend} (hasToken: ${backendConfig.error.hasToken})`,
         }}
         onConfirm={(url: string, token?: string) => {
           changeBackend(url)
@@ -119,15 +123,9 @@ function ApplicationStatusRoutes(): VNode {
   }
 
   return <div id="app" class="has-navbar-fixed-top">
-    <ConfigContext.Provider value={ctx}>
-      <NavigationBar lang={lang} setLang={setLang} />
-      <Sidebar onLogout={() => {
-        cleaners.forEach(c => c())
-        route(RootPaths.list_instances)
-      }} />
+    <ConfigContextProvider value={ctx}>
       <Notifications notifications={notifications} 
removeNotification={removeNotification} />
-      <Route default component={ApplicationReadyRoutes} 
pushNotification={pushNotification} addTokenCleaner={addTokenCleanerNemo} /> :
-        {/* <Route default component={LoginWithError} /> */}
-    </ConfigContext.Provider>
+      <Route default component={ApplicationReadyRoutes} 
pushNotification={pushNotification} addTokenCleaner={addTokenCleanerMemo} 
clearAllTokens={clearAllTokens}/> :
+    </ConfigContextProvider>
   </div>
 }
diff --git a/packages/frontend/src/messages/en.po 
b/packages/frontend/src/messages/en.po
index 8e34568..717e4f0 100644
--- a/packages/frontend/src/messages/en.po
+++ b/packages/frontend/src/messages/en.po
@@ -73,6 +73,9 @@ msgstr "Wire fee Amortization"
 msgid "fields.instance.address.label"
 msgstr "Address"
 
+msgid "Could not infer instance id from url %s"
+msgstr "Could not infer instance id from url %s"
+
 msgid "fields.instance.address.country.label"
 msgstr "Country"
 
diff --git a/packages/frontend/src/routes/instances/create/CreatePage.tsx 
b/packages/frontend/src/routes/instances/create/CreatePage.tsx
index 2a595a6..ae76796 100644
--- a/packages/frontend/src/routes/instances/create/CreatePage.tsx
+++ b/packages/frontend/src/routes/instances/create/CreatePage.tsx
@@ -20,7 +20,7 @@
 */
 
 import { h, VNode } from "preact";
-import { useContext, useState } from "preact/hooks";
+import { useState } from "preact/hooks";
 import { MerchantBackend } from "../../../declaration";
 import * as yup from 'yup';
 import { FormErrors, FormProvider } from "../../../components/form/Field"
@@ -30,12 +30,12 @@ import { Input } from "../../../components/form/Input";
 import { InputSecured } from "../../../components/form/InputSecured";
 import { InputWithAddon } from "../../../components/form/InputWithAddon";
 import { InputGroup } from "../../../components/form/InputGroup";
-import { BackendContext, ConfigContext } from "../../../context/backend";
+import { useConfigContext, useBackendContext } from "../../../context/backend";
 import { InputDuration } from "../../../components/form/InputDuration";
 import { InputCurrency } from "../../../components/form/InputCurrency";
 import { InputPayto } from "../../../components/form/InputPayto";
 
-type Entity = MerchantBackend.Instances.InstanceConfigurationMessage
+type Entity = MerchantBackend.Instances.InstanceConfigurationMessage & 
{auth_token?: string}
 
 interface Props {
   onCreate: (d: Entity) => void;
@@ -61,6 +61,11 @@ export function CreatePage({ onCreate, isLoading, onBack }: 
Props): VNode {
 
   const submit = (): void => {
     try {
+      // use conversion instead of this
+      const newToken = value.auth_token;
+      value.auth_token = undefined;
+      value.auth = newToken === null || newToken === undefined ? { method: 
"external" } : { method: "token", token: `secret-token:${newToken}` };
+      // remove above use conversion
       schema.validateSync(value, { abortEarly: false })
       onCreate(schema.cast(value) as Entity);
     } catch (err) {
@@ -69,40 +74,10 @@ export function CreatePage({ onCreate, isLoading, onBack }: 
Props): VNode {
       setErrors(pathMessages)
     }
   }
-  const backend = useContext(BackendContext)
-  const config = useContext(ConfigContext)
+  const backend = useBackendContext()
+  const config = useConfigContext()
 
   return <div>
-    <section class="section is-title-bar">
-
-      <div class="level">
-        <div class="level-left">
-          <div class="level-item">
-            <ul>
-              <li><Message id="Merchant" /></li>
-              <li><Message id="Instances" /></li>
-            </ul>
-          </div>
-        </div>
-      </div>
-    </section>
-
-    <section class={isLoading ? "hero is-hero-bar" : "hero is-hero-bar 
is-loading"}>
-      <div class="hero-body">
-        <div class="level">
-          <div class="level-left">
-            <div class="level-item">
-              <h1 class="title">
-                <Message id="Create new instances" />
-              </h1>
-            </div>
-          </div>
-          <div class="level-right" style="display: none;">
-            <div class="level-item" />
-          </div>
-        </div>
-      </div>
-    </section>
 
     <section class="section is-main-section">
       <div class="columns">
@@ -137,10 +112,12 @@ export function CreatePage({ onCreate, isLoading, onBack 
}: Props): VNode {
             <InputDuration<Entity> name="default_wire_transfer_delay" />
 
           </FormProvider>
-          <div class="buttons is-right">
+
+          <div class="buttons is-right mt-5">
             <button class="button" onClick={onBack} ><Message id="Cancel" 
/></button>
             <button class="button is-success" onClick={submit} ><Message 
id="Confirm" /></button>
           </div>
+
         </div>
         <div class="column" />
       </div>
diff --git a/packages/frontend/src/routes/instances/create/index.tsx 
b/packages/frontend/src/routes/instances/create/index.tsx
index 819b549..f5591dd 100644
--- a/packages/frontend/src/routes/instances/create/index.tsx
+++ b/packages/frontend/src/routes/instances/create/index.tsx
@@ -15,7 +15,7 @@
  */
 import { h, VNode } from "preact";
 import { MerchantBackend } from "../../../declaration";
-import { useBackendMutateAPI } from "../../../hooks/backend";
+import { useAdminMutateAPI } from "../../../hooks/backend";
 import { CreatePage } from "./CreatePage";
 
 interface Props {
@@ -25,7 +25,7 @@ interface Props {
 }
 
 export default function Create({ onBack, onConfirm, onError }: Props): VNode {
-  const { createInstance } = useBackendMutateAPI();
+  const { createInstance } = useAdminMutateAPI();
 
   return <CreatePage 
     onBack={onBack}
diff --git a/packages/frontend/src/routes/instances/details/DetailPage.tsx 
b/packages/frontend/src/routes/instances/details/DetailPage.tsx
index 9eccd99..687610a 100644
--- a/packages/frontend/src/routes/instances/details/DetailPage.tsx
+++ b/packages/frontend/src/routes/instances/details/DetailPage.tsx
@@ -32,7 +32,6 @@ interface Props {
   onUpdate: () => void;
   onDelete: () => void;
   selected: MerchantBackend.Instances.QueryInstancesResponse;
-  isLoading: boolean;
 }
 
 interface KeyValue {
@@ -50,32 +49,18 @@ function convert(from: 
MerchantBackend.Instances.QueryInstancesResponse): Entity
   return { ...defaults, ...rest, payto_uris };
 }
 
-export function DetailPage({ onUpdate, isLoading, selected, onDelete }: 
Props): VNode {
+export function DetailPage({ onUpdate, selected, onDelete }: Props): VNode {
   const [value, valueHandler] = useState<Partial<Entity>>(convert(selected))
   const [errors, setErrors] = useState<KeyValue>({})
 
   return <div>
-    <section class="section is-title-bar">
-
-      <div class="level">
-        <div class="level-left">
-          <div class="level-item">
-            <ul>
-              <li><Message id="Merchant" /></li>
-              <li><Message id="Instances" /></li>
-            </ul>
-          </div>
-        </div>
-      </div>
-    </section>
-
-    <section class={isLoading ? "hero is-hero-bar" : "hero is-hero-bar 
is-loading"}>
+    <section class="hero is-hero-bar">
       <div class="hero-body">
         <div class="level">
           <div class="level-left">
             <div class="level-item">
               <h1 class="title">
-                <Message id="Instance details" />
+                Here goes the instance description
               </h1>
             </div>
           </div>
@@ -96,16 +81,6 @@ export function DetailPage({ onUpdate, isLoading, selected, 
onDelete }: Props):
             <Input<Entity> name="payto_uris" readonly />
 
           </FormProvider>
-          <div class="buttons is-right">
-            <button class="button is-danger" onClick={() => onDelete()} >
-              <span class="icon"><i class="mdi mdi-delete" /></span>
-              <span><Message id="delete" /></span>
-            </button>
-            <button class="button is-success" onClick={() => onUpdate()} >
-              <span class="icon"><i class="mdi mdi-pen" /></span>
-              <span><Message id="update" /></span>
-            </button>
-          </div>
         </div>
         <div class="column" />
       </div>
diff --git a/packages/frontend/src/routes/instances/details/index.tsx 
b/packages/frontend/src/routes/instances/details/index.tsx
index 32b5a4b..e0a3248 100644
--- a/packages/frontend/src/routes/instances/details/index.tsx
+++ b/packages/frontend/src/routes/instances/details/index.tsx
@@ -14,10 +14,10 @@
  GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 import { Fragment, h, VNode } from "preact";
-import { useContext, useState } from "preact/hooks";
-import { InstanceContext } from "../../../context/backend";
+import { useState } from "preact/hooks";
+import { useInstanceContext } from "../../../context/backend";
 import { Notification } from "../../../utils/types";
-import { useBackendInstance, useBackendInstanceMutateAPI, SwrError } from 
"../../../hooks/backend";
+import { useBackendInstance, useInstanceMutateAPI, SwrError } from 
"../../../hooks/backend";
 import { DetailPage } from "./DetailPage";
 import { DeleteModal } from "../../../components/modal";
 
@@ -30,11 +30,11 @@ interface Props {
 }
 
 export default function Detail({ onUpdate, onLoadError, onUnauthorized, 
pushNotification, onDelete }: Props): VNode {
-  const { id } = useContext(InstanceContext)
+  const { id } = useInstanceContext()
   const details = useBackendInstance()
   const [deleting, setDeleting] = useState<boolean>(false)
 
-  const { deleteInstance } = useBackendInstanceMutateAPI()
+  const { deleteInstance } = useInstanceMutateAPI()
 
   if (!details.data) {
     if (details.unauthorized) return onUnauthorized()
@@ -46,7 +46,6 @@ export default function Detail({ onUpdate, onLoadError, 
onUnauthorized, pushNoti
 
   return <Fragment>
     <DetailPage
-      isLoading={false}
       selected={details.data}
       onUpdate={onUpdate}
       onDelete={() => setDeleting(true)}
diff --git a/packages/frontend/src/routes/instances/list/Table.tsx 
b/packages/frontend/src/routes/instances/list/Table.tsx
index 3270806..2c00668 100644
--- a/packages/frontend/src/routes/instances/list/Table.tsx
+++ b/packages/frontend/src/routes/instances/list/Table.tsx
@@ -111,15 +111,15 @@ function Table({ rowSelection, rowSelectionHandler, 
instances, onUpdate, onDelet
       </thead>
       <tbody>
         {instances.map(i => {
-          return <tr onClick={(): void => onUpdate(i.id)} style={{cursor: 
'pointer'}}>
+          return <tr>
             <td class="is-checkbox-cell">
               <label class="b-checkbox checkbox">
                 <input type="checkbox" checked={rowSelection.indexOf(i.id) != 
-1} onClick={(): void => rowSelectionHandler(toggleSelected(i.id))} />
                 <span class="check" />
               </label>
             </td>
-            <td >{i.id}</td>
-            <td >{i.name}</td>
+            <td onClick={(): void => onUpdate(i.id)} style={{cursor: 
'pointer'}} >{i.id}</td>
+            <td onClick={(): void => onUpdate(i.id)} style={{cursor: 
'pointer'}} >{i.name}</td>
             <td class="is-actions-cell">
               <div class="buttons is-right">
                 <button class="button is-small is-danger jb-modal" 
type="button" onClick={(): void => onDelete(i)}>
diff --git a/packages/frontend/src/routes/instances/list/View.tsx 
b/packages/frontend/src/routes/instances/list/View.tsx
index d224980..a66fb5e 100644
--- a/packages/frontend/src/routes/instances/list/View.tsx
+++ b/packages/frontend/src/routes/instances/list/View.tsx
@@ -36,36 +36,7 @@ interface Props {
 export function View({ instances, isLoading, onCreate, onDelete, onUpdate, 
selected }: Props): VNode {
 
   return <div id="app">
-    <section class="section is-title-bar">
 
-      <div class="level">
-        <div class="level-left">
-          <div class="level-item">
-            <ul>
-              <li><Message id="Merchant" /></li>
-              <li><Message id="Instances" /></li>
-            </ul>
-          </div>
-        </div>
-      </div>
-    </section>
-
-    <section class={isLoading ? "hero is-hero-bar" : "hero is-hero-bar 
is-loading"}>
-      <div class="hero-body">
-        <div class="level">
-          <div class="level-left">
-            <div class="level-item">
-              <h1 class="title">
-                <Message id="List of configured instances" />
-              </h1>
-            </div>
-          </div>
-          <div class="level-right" style="display: none;">
-            <div class="level-item" />
-          </div>
-        </div>
-      </div>
-    </section>
     <section class="section is-main-section">
       <CardTable instances={instances} onDelete={onDelete} onUpdate={onUpdate} 
selected={selected} onCreate={onCreate} />
     </section>
diff --git a/packages/frontend/src/routes/instances/list/index.tsx 
b/packages/frontend/src/routes/instances/list/index.tsx
index 81e920a..19e3696 100644
--- a/packages/frontend/src/routes/instances/list/index.tsx
+++ b/packages/frontend/src/routes/instances/list/index.tsx
@@ -21,7 +21,7 @@
 
 import { Fragment, h, VNode } from 'preact';
 import { View } from './View';
-import { useBackendInstances, useBackendInstanceMutateAPI, SwrError } from 
'../../../hooks/backend';
+import { useAdminMutateAPI } from '../../../hooks/backend';
 import { useState } from 'preact/hooks';
 import { MerchantBackend } from '../../../declaration';
 import { Notification } from '../../../utils/types';
@@ -29,42 +29,18 @@ import { DeleteModal } from '../../../components/modal';
 
 interface Props {
   pushNotification: (n: Notification) => void;
-  onUnauthorized: () => VNode;
-  onQueryError: (e: SwrError) => VNode;
   onCreate: () => void;
   onUpdate: (id: string) => void;
+  instances: MerchantBackend.Instances.Instance[]
 }
 
-export default function Instances({ pushNotification, onUnauthorized, 
onQueryError, onCreate, onUpdate }: Props): VNode {
-  const list = useBackendInstances()
+export default function Instances({ pushNotification, instances, onCreate, 
onUpdate }: Props): VNode {
   const [deleting, setDeleting] = useState<MerchantBackend.Instances.Instance 
| null>(null)
-  const { deleteInstance } = useBackendInstanceMutateAPI()
-
-  const isLoadingTheList = (!list.data && !list.error)
-  const error = !list.data && list.error
-
-  if (!list.data) {
-    if (list.unauthorized) return onUnauthorized()
-    if (list.error) return onQueryError(list.error)
-    return <div id="app">
-      <section class="section is-title-bar">
-
-        <div class="level">
-          <div class="level-left">
-            <div class="level-item">
-              <ul>
-                <li>loading</li>
-              </ul>
-            </div>
-          </div>
-        </div>
-      </section>
-    </div>
-  }
+  const { deleteInstance } = useAdminMutateAPI()
 
   return <Fragment>
-    <View instances={list.data.instances}
-      isLoading={isLoadingTheList}
+    <View instances={instances}
+      isLoading={false}
       onDelete={setDeleting}
       onCreate={onCreate}
       onUpdate={onUpdate}
@@ -75,7 +51,7 @@ export default function Instances({ pushNotification, 
onUnauthorized, onQueryErr
       onCancel={() => setDeleting(null)}
       onConfirm={async (): Promise<void> => {
         try {
-          await deleteInstance()
+          await deleteInstance(deleting.id)
           pushNotification({ message: 'delete_success', type: 'SUCCESS' })
         } catch (e) {
           pushNotification({ message: 'delete_error', type: 'ERROR' })
diff --git a/packages/frontend/src/routes/instances/update/UpdatePage.tsx 
b/packages/frontend/src/routes/instances/update/UpdatePage.tsx
index cb7fcb6..6fde628 100644
--- a/packages/frontend/src/routes/instances/update/UpdatePage.tsx
+++ b/packages/frontend/src/routes/instances/update/UpdatePage.tsx
@@ -30,21 +30,21 @@ import { InstanceUpdateSchema as schema } from 
'../../../schemas'
 import { Message } from "preact-messages";
 import { Input } from "../../../components/form/Input";
 import { InputSecured } from "../../../components/form/InputSecured";
-import { ConfigContext } from "../../../context/backend";
+import { useConfigContext, useInstanceContext } from 
"../../../context/backend";
 import { InputDuration } from "../../../components/form/InputDuration";
 import { InputCurrency } from "../../../components/form/InputCurrency";
 import { InputPayto } from "../../../components/form/InputPayto";
 
-type Entity = MerchantBackend.Instances.InstanceReconfigurationMessage
+type Entity = MerchantBackend.Instances.InstanceReconfigurationMessage & 
{auth_token?: string}
 
 interface Props {
-  onUpdate: (d: Entity) => void;
+  onUpdate: (d: Entity, auth?: 
MerchantBackend.Instances.InstanceAuthConfigurationMessage) => void;
   selected: MerchantBackend.Instances.QueryInstancesResponse;
   isLoading: boolean;
   onBack: () => void;
 }
 
-function convert(from: MerchantBackend.Instances.QueryInstancesResponse): 
Entity {
+function convert(from: MerchantBackend.Instances.QueryInstancesResponse, 
token?: string): Entity {
   const { accounts, ...rest } = from
   const payto_uris = accounts.filter(a => a.active).map(a => a.payto_uri)
   const defaults = {
@@ -52,19 +52,35 @@ function convert(from: 
MerchantBackend.Instances.QueryInstancesResponse): Entity
     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 };
+  return { ...defaults, ...rest, payto_uris, auth_token: from.auth.method === 
"external" ? undefined : token };
 }
 
-
+function getTokenValuePart(t?: string): string | undefined {
+  if (!t) return t
+  const match = /secret-token:(.*)/.exec(t);
+  if (!match || !match[1]) return undefined;
+  return match[1]
+}
 
 export function UpdatePage({ onUpdate, isLoading, selected, onBack }: Props): 
VNode {
-  const [value, valueHandler] = useState<Partial<Entity>>(convert(selected))
+  const { token } = useInstanceContext()
+  const currentTokenValue = getTokenValuePart(token)
+  const [value, valueHandler] = useState<Partial<Entity>>(convert(selected, 
currentTokenValue))
   const [errors, setErrors] = useState<FormErrors<Entity>>({})
 
   const submit = (): void => {
     try {
+      // use conversion instead of this
+      const newToken = value.auth_token;
+      value.auth_token = undefined;
+      const auth: MerchantBackend.Instances.InstanceAuthConfigurationMessage | 
undefined = 
+      newToken === currentTokenValue ? undefined : (newToken === null ? 
+          { method: "external" } : 
+          { method: "token", token: `secret-token:${newToken}` });
+      
+      // remove above use conversion
       schema.validateSync(value, { abortEarly: false })
-      onUpdate(schema.cast(value));
+      onUpdate(schema.cast(value), auth);
       onBack()
     } catch (err) {
       const errors = err.inner as yup.ValidationError[]
@@ -72,40 +88,9 @@ export function UpdatePage({ onUpdate, isLoading, selected, 
onBack }: Props): VN
       setErrors(pathMessages)
     }
   }
-  const config = useContext(ConfigContext)
+  const config = useConfigContext()
 
   return <div>
-    <section class="section is-title-bar">
-
-      <div class="level">
-        <div class="level-left">
-          <div class="level-item">
-            <ul>
-              <li><Message id="Merchant" /></li>
-              <li><Message id="Instances" /></li>
-            </ul>
-          </div>
-        </div>
-      </div>
-    </section>
-
-    <section class={isLoading ? "hero is-hero-bar" : "hero is-hero-bar 
is-loading"}>
-      <div class="hero-body">
-        <div class="level">
-          <div class="level-left">
-            <div class="level-item">
-              <h1 class="title">
-                <Message id="Update this instance" />
-              </h1>
-            </div>
-          </div>
-          <div class="level-right" style="display: none;">
-            <div class="level-item" />
-          </div>
-        </div>
-      </div>
-    </section>
-
     <section class="section is-main-section">
       <div class="columns">
         <div class="column" />
@@ -138,7 +123,7 @@ export function UpdatePage({ onUpdate, isLoading, selected, 
onBack }: Props): VN
 
           </FormProvider>
 
-          <div class="buttons is-right">
+          <div class="buttons is-right mt-4">
             <button class="button" onClick={onBack} ><Message id="Cancel" 
/></button>
             <button class="button is-success" onClick={submit} ><Message 
id="Confirm" /></button>
           </div>
diff --git a/packages/frontend/src/routes/instances/update/index.tsx 
b/packages/frontend/src/routes/instances/update/index.tsx
index ff2473a..2f75258 100644
--- a/packages/frontend/src/routes/instances/update/index.tsx
+++ b/packages/frontend/src/routes/instances/update/index.tsx
@@ -14,11 +14,11 @@
  GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 import { Fragment, h, VNode } from "preact";
-import { useContext, useState } from "preact/hooks";
+import { useState } from "preact/hooks";
 import { UpdateTokenModal } from "../../../components/modal";
-import { InstanceContext } from "../../../context/backend";
+import { useInstanceContext } from "../../../context/backend";
 import { MerchantBackend } from "../../../declaration";
-import { SwrError, useBackendInstance, useBackendInstanceMutateAPI } from 
"../../../hooks/backend";
+import { SwrError, useBackendInstance, useInstanceMutateAPI } from 
"../../../hooks/backend";
 import { UpdatePage } from "./UpdatePage";
 
 interface Props {
@@ -33,10 +33,10 @@ interface Props {
 }
 
 export default function Update({ onBack, onConfirm, onLoadError, 
onUpdateError, onUnauthorized }: Props): VNode {
-  const { updateInstance, setNewToken, clearToken } = 
useBackendInstanceMutateAPI();
-  const [updatingToken, setUpdatingToken] = useState<string | null>(null)
+  const { updateInstance, setNewToken, clearToken } = useInstanceMutateAPI();
+  const [updatingToken, setUpdatingToken] = useState<boolean>(false)
   const details = useBackendInstance()
-  const { id } = useContext(InstanceContext)
+  const { id, token } = useInstanceContext()
 
   if (!details.data) {
     if (details.unauthorized) return onUnauthorized()
@@ -51,15 +51,16 @@ export default function Update({ onBack, onConfirm, 
onLoadError, onUpdateError,
       onBack={onBack}
       isLoading={false}
       selected={details.data}
-      onUpdate={(d: MerchantBackend.Instances.InstanceReconfigurationMessage): 
Promise<void> => {
-        return updateInstance(d).then(onConfirm).catch(onUpdateError)
+      onUpdate={(d: MerchantBackend.Instances.InstanceReconfigurationMessage, 
t?: MerchantBackend.Instances.InstanceAuthConfigurationMessage): Promise<void> 
=> {
+        return updateInstance(d,t).then(onConfirm).catch(onUpdateError)
       }} />
+    <button class="button" onClick={() => setUpdatingToken(true)}>auth</button>
     {updatingToken && <UpdateTokenModal
-      oldToken={updatingToken}
+      oldToken={token}
       element={{ id, name: details.data.name }}
-      onCancel={() => setUpdatingToken(null)}
+      onCancel={() => setUpdatingToken(false)}
       onClear={() => clearToken()}
-      onConfirm={(newToken) => setNewToken(newToken) }
+      onConfirm={(newToken) => setNewToken(newToken)}
     />}
   </Fragment>
 }
\ No newline at end of file
diff --git a/packages/frontend/src/schemas/index.ts 
b/packages/frontend/src/schemas/index.ts
index a2def9d..c595ea5 100644
--- a/packages/frontend/src/schemas/index.ts
+++ b/packages/frontend/src/schemas/index.ts
@@ -48,10 +48,10 @@ function currencyWithAmountIsValid(value?: string): boolean 
{
 export const InstanceSchema = yup.object().shape({
   id: yup.string().required().meta({type: 'url'}),
   name: yup.string().required(),
-  auth_token: yup.string()
-    .min(8).max(20)
-    .optional()
-    .meta({type: 'secured'}),
+  auth: yup.object().shape({
+    method: yup.string().matches(/^(external|token)$/),
+    token: yup.string().optional().nullable(),
+  }),
   payto_uris: yup.array().of(yup.string())
     .min(1)
     .meta({type: 'array'})
diff --git a/packages/frontend/src/scss/_aside.scss 
b/packages/frontend/src/scss/_aside.scss
index 9598c60..21fe189 100644
--- a/packages/frontend/src/scss/_aside.scss
+++ b/packages/frontend/src/scss/_aside.scss
@@ -171,8 +171,8 @@ aside.aside {
       }
     }
   }
-  html.has-aside-mobile-expanded {
-     nav.navbar {
+  div.has-aside-mobile-expanded {
+    nav.navbar {
       margin-left: $aside-mobile-width;
     }
     aside.aside {
diff --git a/packages/frontend/src/template.html 
b/packages/frontend/src/template.html
index ade6507..49c1c88 100644
--- a/packages/frontend/src/template.html
+++ b/packages/frontend/src/template.html
@@ -9,7 +9,7 @@
                <link rel="apple-touch-icon"           
href="/assets/icons/apple-touch-icon.png">
                <% preact.headEnd %>
        </head>
-       <body class="has-navbar-fixed-top">
+       <body>
                <% preact.bodyEnd %>
        </body>
 </html>
diff --git a/packages/frontend/src/utils/constants.ts 
b/packages/frontend/src/utils/constants.ts
index a560612..b8c9cdd 100644
--- a/packages/frontend/src/utils/constants.ts
+++ b/packages/frontend/src/utils/constants.ts
@@ -21,4 +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 AMOUNT_REGEX=/^[a-zA-Z][a-zA-Z]*:[0-9][0-9,]*\.?[0-9,]*$/
+
+export const INSTANCE_ID_LOOKUP = /^\/instances\/([^/]*)\/?$/
diff --git a/packages/frontend/tests/header.test.tsx 
b/packages/frontend/tests/header.test.tsx
index 42e23ec..f163960 100644
--- a/packages/frontend/tests/header.test.tsx
+++ b/packages/frontend/tests/header.test.tsx
@@ -20,7 +20,7 @@
 */
 
 import { h } from 'preact';
-import { Sidebar,  } from '../src/components/sidebar';
+import { Sidebar } from '../src/components/menu/SideBar';
 // See: https://github.com/preactjs/enzyme-adapter-preact-pure
 import { shallow } from 'enzyme';
 import * as backend from '../src/context/backend';

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