gnunet-svn
[Top][All Lists]
Advanced

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

[taler-merchant-backoffice] branch master updated: some features


From: gnunet
Subject: [taler-merchant-backoffice] branch master updated: some features
Date: Thu, 18 Mar 2021 05:04:40 +0100

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

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

The following commit(s) were added to refs/heads/master by this push:
     new 0e36596  some features
0e36596 is described below

commit 0e365961bee9abf78fa7a0dc3eaae91a8985af3e
Author: Sebastian <sebasjm@gmail.com>
AuthorDate: Thu Mar 18 01:04:22 2021 -0300

    some features
---
 CHANGELOG.md                                       |  12 +-
 packages/frontend/src/AdminRoutes.tsx              |  15 +-
 packages/frontend/src/ApplicationReadyRoutes.tsx   |  90 ++++---
 packages/frontend/src/InstanceRoutes.tsx           | 290 +++++++++++++++++----
 .../frontend/src/components/exception/loading.tsx  |   8 +-
 .../frontend/src/components/exception/login.tsx    |  12 +-
 packages/frontend/src/components/menu/index.tsx    | 190 ++++++++------
 packages/frontend/src/hooks/backend.ts             |  26 +-
 .../login/index.tsx => hooks/notification.ts}      |  32 ++-
 packages/frontend/src/index.tsx                    |  52 ++--
 packages/frontend/src/messages/en.po               |   8 +-
 .../frontend/src/paths/admin/create/CreatePage.tsx |   4 +-
 packages/frontend/src/paths/admin/create/index.tsx | 104 +++++++-
 packages/frontend/src/paths/admin/list/index.tsx   |  15 +-
 .../frontend/src/paths/instance/details/index.tsx  |  16 +-
 .../src/paths/instance/orders/list/index.tsx       |   6 +-
 .../src/paths/instance/products/list/index.tsx     |  13 +-
 .../src/paths/instance/tips/list/index.tsx         |  14 +-
 .../src/paths/instance/transfers/list/index.tsx    |  13 +-
 .../frontend/src/paths/instance/update/index.tsx   |  15 +-
 packages/frontend/src/paths/login/index.tsx        |   5 +-
 packages/frontend/src/scss/main.scss               |  65 +++++
 22 files changed, 694 insertions(+), 311 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 3235e21..03f65f1 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -22,16 +22,10 @@ and this project adheres to [Semantic 
Versioning](https://semver.org/spec/v2.0.0
  - configure eslint
  - configure prettier
  - prune scss styles to reduce size
- - create a loading page to be use when the data is not ready
  - some way to copy the url of a created instance
  - fix mobile: some things are still on the left
- - instance id in instance list should be clickable
  - edit button to go to instance settings
- - notifications should tale place between title and content, and not disapear
- - confirmation page when creating instances
- - if there is enough space for tables in mobile, make the scrollables
-
- - create default instance if it is not already
+ - 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
  
  
  - product: main action => refund
@@ -55,6 +49,10 @@ deletiing product
  - 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
+ - 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
diff --git a/packages/frontend/src/AdminRoutes.tsx 
b/packages/frontend/src/AdminRoutes.tsx
index 8bd8d48..aefe6a0 100644
--- a/packages/frontend/src/AdminRoutes.tsx
+++ b/packages/frontend/src/AdminRoutes.tsx
@@ -20,7 +20,6 @@ import { Notification } from "./utils/types";
 
 import InstanceListPage from './paths/admin/list';
 import InstanceCreatePage from "./paths/admin/create";
-import NotFoundPage from './paths/notfound';
 
 export enum AdminPaths {
   list_instances = '/instances',
@@ -29,10 +28,9 @@ export enum AdminPaths {
 }
 
 interface Props {
-  pushNotification: (n: Notification) => void;
-  // instances: MerchantBackend.Instances.Instance[]
+  // pushNotification: (n: Notification) => void;
 }
-export function AdminRoutes({ pushNotification }: Props): VNode {
+export function AdminRoutes({ }: Props): VNode {
   const i18n = useMessageTemplate();
   
   return <Router>
@@ -54,17 +52,14 @@ export function AdminRoutes({ pushNotification }: Props): 
VNode {
       onBack={() => route(AdminPaths.list_instances)}
 
       onConfirm={() => {
-        pushNotification({ message: i18n`create_success`, type: 'SUCCESS' });
-        route(AdminPaths.list_instances);
+        // route(AdminPaths.list_instances);
       }}
 
-      onError={(error: any) => {
-        pushNotification({ message: i18n`create_error`, type: 'ERROR' });
-      }}
+      // onError={(error: any) => {
+      // }}
       
     />
 
-    {/* <Route default component={undefined} /> */}
 
   </Router>
 }
\ No newline at end of file
diff --git a/packages/frontend/src/ApplicationReadyRoutes.tsx 
b/packages/frontend/src/ApplicationReadyRoutes.tsx
index 62ed149..e36a2b5 100644
--- a/packages/frontend/src/ApplicationReadyRoutes.tsx
+++ b/packages/frontend/src/ApplicationReadyRoutes.tsx
@@ -22,16 +22,15 @@ import { Fragment, h, VNode } from 'preact';
 import { route } from 'preact-router';
 import { Notification } from "./utils/types";
 import { useBackendContext } from './context/backend';
-import { useBackendInstances } from "./hooks/backend";
+import { useBackendInstancesTestForAdmin } from "./hooks/backend";
 import { InstanceRoutes } from "./InstanceRoutes";
 import LoginPage from './paths/login';
 import { INSTANCE_ID_LOOKUP } from './utils/constants';
-import { NotYetReadyAppMenu, Menu } from './components/menu';
+import { NotYetReadyAppMenu, Menu, NotificationCard } from './components/menu';
 import { useMessageTemplate } from 'preact-messages';
 interface Props {
-  pushNotification: (n: Notification) => void;
 }
-export function ApplicationReadyRoutes({ pushNotification }: Props): VNode {
+export function ApplicationReadyRoutes({ }: Props): VNode {
   const i18n = useMessageTemplate();
   const { url: currentBaseUrl, changeBackend, updateToken, clearAllTokens } = 
useBackendContext();
 
@@ -39,19 +38,26 @@ export function ApplicationReadyRoutes({ pushNotification 
}: Props): VNode {
     changeBackend(url);
     if (token) updateToken(token);
   };
-  const list = useBackendInstances()
+  const list = useBackendInstancesTestForAdmin()
+
+  const clearTokenAndGoToRoot = () => {
+    clearAllTokens();
+    route('/')
+  }
 
   if (!list.data) {
     if (list.unauthorized) {
       return <Fragment>
-        <NotYetReadyAppMenu title="Login" onLogout={() => {
-          clearAllTokens();
-          route('/')
-        }} />
-        <LoginPage
-          withMessage={{ message: i18n`Access denied`, description: i18n`Check 
your token is valid`, type: 'ERROR', }}
-          onConfirm={updateLoginStatus}
+        <NotYetReadyAppMenu title="Login"
+          onLogout={clearTokenAndGoToRoot}
+        />
+        <NotificationCard notification={{
+          message: i18n`Access denied`,
+          description: i18n`Check your token is valid`,
+          type: 'ERROR'
+        }}
         />
+        <LoginPage onConfirm={updateLoginStatus} />
       </Fragment>
     }
     if (list.notfound) {
@@ -62,45 +68,43 @@ export function ApplicationReadyRoutes({ pushNotification 
}: Props): VNode {
         // query to /config is ok but the URL
         // doest not match with our pattern
         return <Fragment>
-          <NotYetReadyAppMenu title="Error" onLogout={() => {
-            clearAllTokens();
-            route('/')
-          }} />
-          <LoginPage
-            withMessage={{ message: i18n`Couldnt access the server`, 
description: i18n`Could not infer instance id from url ${currentBaseUrl}`, 
type: 'ERROR', }}
-            onConfirm={updateLoginStatus}
+          <NotYetReadyAppMenu title="Error" onLogout={clearTokenAndGoToRoot} />
+          <NotificationCard notification={{
+            message: i18n`Couldnt access the server`,
+            description: i18n`Could not infer instance id from url 
${currentBaseUrl}`,
+            type: 'ERROR',
+          }}
           />
+          <LoginPage onConfirm={updateLoginStatus} />
         </Fragment>
       }
 
       return <Fragment>
-        <Menu instance={match[1]} onLogout={() => {
-          clearAllTokens();
-          route('/')
+        <Menu instance={match[1]} onLogout={clearTokenAndGoToRoot} />
+        <InstanceRoutes id={match[1]} />
+      </Fragment>
+    }
+
+    if (list.error) {
+      return <Fragment>
+        <NotYetReadyAppMenu title="Error" />
+        <NotificationCard notification={{
+          message: i18n`Couldnt access the server`,
+          description: list.error.message,
+          type: 'ERROR'
         }} />
-        <InstanceRoutes id={match[1]} pushNotification={pushNotification} />
+        <LoginPage onConfirm={updateLoginStatus} />
       </Fragment>
-  }
+    }
 
-  if (list.error) {
-    return <Fragment>
-      <NotYetReadyAppMenu title="Error" />
-      <LoginPage
-        withMessage={{ message: i18n`Couldnt access the server`, description: 
list.error.message, type: 'ERROR', }}
-        onConfirm={updateLoginStatus}
-      />
-    </Fragment>
+    // is loading
+    return <NotYetReadyAppMenu title="Loading..." />
   }
 
-  // is loading
-  return <NotYetReadyAppMenu title="Loading..." />
-}
-
-return <Fragment>
-  <Menu instance="default" admin onLogout={() => {
-    clearAllTokens();
-    route('/')
-  }} />
-  <InstanceRoutes admin id="default" pushNotification={pushNotification} />
-</Fragment>
+  return <Fragment>
+    <Menu instance="default" admin
+      onLogout={clearTokenAndGoToRoot}
+    />
+    <InstanceRoutes admin id="default" />
+  </Fragment>
 }
diff --git a/packages/frontend/src/InstanceRoutes.tsx 
b/packages/frontend/src/InstanceRoutes.tsx
index aec17d6..1804ca7 100644
--- a/packages/frontend/src/InstanceRoutes.tsx
+++ b/packages/frontend/src/InstanceRoutes.tsx
@@ -26,11 +26,11 @@ import { useMessageTemplate } from 'preact-messages';
 import { createHashHistory } from 'history';
 import { useBackendInstanceToken } from './hooks';
 import { InstanceContextProvider, useBackendContext } from './context/backend';
-import { SwrError } from "./hooks/backend";
-import { Notification } from './utils/types';
+import { SwrError, useInstanceDetails } from "./hooks/backend";
+// import { Notification } from './utils/types';
 
 import LoginPage from './paths/login';
-import InstanceUpdatePage from "./paths/instance/update";
+import InstanceUpdatePage, { Props as InstanceUpdatePageProps } from 
"./paths/instance/update";
 import DetailPage from './paths/instance/details';
 import NotFoundPage from './paths/notfound';
 
@@ -51,6 +51,8 @@ import TransferCreatePage from 
'./paths/instance/transfers/create'
 
 import InstanceListPage from './paths/admin/list';
 import InstanceCreatePage from "./paths/admin/create";
+import { NotificationCard } from './components/menu';
+import { Loading } from './components/exception/loading';
 
 export enum InstancePaths {
   details = '/',
@@ -75,18 +77,45 @@ export enum InstancePaths {
 export enum AdminPaths {
   list_instances = '/instances',
   new_instance = '/instance/new',
-  instance_id_route = '/instance/:id/:rest*',
+  update_instance = '/instance/:id/update',
 }
 
 export interface Props {
   id: string;
-  pushNotification: (n: Notification) => void;
   admin?: boolean;
 }
 
-// const rootPath = typeof window !== 'undefined' ? window.location.pathname : 
'/'
+function AdminInstanceUpdatePage({ id, ...rest }: { id: string } & 
InstanceUpdatePageProps) {
+  const [token, updateToken] = useBackendInstanceToken(id);
+  const value = useMemo(() => ({ id, token, admin: true }), [id, token])
+  const { changeBackend } = useBackendContext();
+  const updateLoginStatus = (url: string, token?: string) => {
+    changeBackend(url);
+    if (token)
+      updateToken(token);
+  };
+  const i18n = useMessageTemplate('');
+  return <InstanceContextProvider value={value}>
+    <InstanceUpdatePage {...rest}
+      onLoadError={(error: SwrError) => {
+        return <Fragment>
+          <NotificationCard notification={{ message: i18n`Problem reaching the 
server`, description: i18n`Got message: ${error.message} from: ${error.backend} 
(hasToken: ${error.hasToken})`, type: 'ERROR' }} />
+          <LoginPage onConfirm={updateLoginStatus} />
+        </Fragment>
+      }}
+
+      onUnauthorized={() => {
+        return <Fragment>
+          <NotificationCard notification={{ message: i18n`Access denied`, 
description: i18n`Check your token is valid`, type: 'ERROR', }} />
+          <LoginPage onConfirm={updateLoginStatus} />
+        </Fragment>
+      }}
+
+    />
+  </InstanceContextProvider>
+}
 
-export function InstanceRoutes({ id, pushNotification, admin }: Props): VNode {
+export function InstanceRoutes({ id, admin }: Props): VNode {
   const [token, updateToken] = useBackendInstanceToken(id);
   const { changeBackend, addTokenCleaner } = useBackendContext();
   const cleaner = useCallback(() => { updateToken(undefined); }, [id]);
@@ -117,14 +146,20 @@ export function InstanceRoutes({ id, pushNotification, 
admin }: Props): VNode {
             route(`/instance/${id}/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} />}
-  
+          onUnauthorized={() => {
+            return <Fragment>
+              <NotificationCard notification={{ message: i18n`Access denied`, 
description: i18n`Check your token is valid`, type: 'ERROR' }} />
+              <LoginPage onConfirm={updateLoginStatus} />
+            </Fragment>
+          }}
+
+          onLoadError={(error: SwrError) => {
+            return <Fragment>
+              <NotificationCard notification={{ message: i18n`Problem reaching 
the server`, description: i18n`Got message: ${error.message} from: 
${error.backend} (hasToken: ${error.hasToken})`, type: 'ERROR' }} />
+              <LoginPage onConfirm={updateLoginStatus} />
+            </Fragment>
+          }}
+
         />
       }
 
@@ -134,12 +169,29 @@ export function InstanceRoutes({ id, pushNotification, 
admin }: Props): VNode {
           onBack={() => route(AdminPaths.list_instances)}
 
           onConfirm={() => {
-            pushNotification({ message: i18n`create_success`, type: 'SUCCESS' 
});
+            // pushNotification({ message: i18n`create_success`, type: 
'SUCCESS' });
             route(AdminPaths.list_instances);
           }}
 
           onError={(error: any) => {
-            pushNotification({ message: i18n`create_error`, type: 'ERROR' });
+            // pushNotification({ message: i18n`create_error`, type: 'ERROR' 
});
+          }}
+
+        />
+      }
+
+      {admin &&
+        <Route path={AdminPaths.update_instance} 
component={AdminInstanceUpdatePage}
+
+          onBack={() => route(AdminPaths.list_instances)}
+
+          onConfirm={() => {
+            // pushNotification({ message: i18n`create_success`, type: 
'SUCCESS' });
+            route(AdminPaths.list_instances);
+          }}
+
+          onUpdateError={(e: Error) => {
+            // pushNotification({ message: i18n`update_error`, type: 'ERROR' 
});
           }}
 
         />
@@ -148,49 +200,110 @@ export function InstanceRoutes({ id, pushNotification, 
admin }: Props): VNode {
       <Route path={InstancePaths.details}
         component={DetailPage}
 
-        onUnauthorized={() => <LoginPage
-          withMessage={{ message: i18n`Access denied`, description: i18n`Check 
your token is valid`, type: 'ERROR', }}
-          onConfirm={updateLoginStatus} />}
+        onUnauthorized={() => {
+          return <Fragment>
+            <NotificationCard notification={{ message: i18n`Access denied`, 
description: i18n`Check your token is valid`, type: 'ERROR', }} />
+            <LoginPage onConfirm={updateLoginStatus} />
+          </Fragment>
+        }}
+
+        onNotFound={() => {
+          if (admin) {
+            return <Fragment>
+              <NotificationCard notification={{
+                message: 'No default instance',
+                description: 'in order to use merchant backend, you should 
create the default instance',
+                type: 'INFO'
+              }} />
+              <InstanceCreatePage onError={() => null} onConfirm={() => null} 
/>
+            </Fragment>
+          }
+          return <NotFoundPage />
+        }}
 
-        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} />}
+        onLoadError={(error: SwrError) => {
+          return <Fragment>
+            <NotificationCard notification={{ message: i18n`Problem reaching 
the server`, description: i18n`Got message: ${error.message} from: 
${error.backend} (hasToken: ${error.hasToken})`, type: 'ERROR' }} />
+            <LoginPage onConfirm={updateLoginStatus} />
+          </Fragment>
+        }}
       />
 
       <Route path={InstancePaths.update}
         component={InstanceUpdatePage}
 
-        onUnauthorized={() => <LoginPage
-          withMessage={{ message: i18n`Access denied`, description: i18n`Check 
your token is valid`, type: 'ERROR', }}
-          onConfirm={updateLoginStatus} />}
+        onUnauthorized={() => {
+          return <Fragment>
+            <NotificationCard notification={{ message: i18n`Access denied`, 
description: i18n`Check your token is valid`, type: 'ERROR', }} />
+            <LoginPage onConfirm={updateLoginStatus} />
+          </Fragment>
+        }}
 
-        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} />}
+        onLoadError={(error: SwrError) => {
+          return <Fragment>
+            <NotificationCard notification={{ message: i18n`Problem reaching 
the server`, description: i18n`Got message: ${error.message} from: 
${error.backend} (hasToken: ${error.hasToken})`, type: 'ERROR' }} />
+            <LoginPage onConfirm={updateLoginStatus} />
+          </Fragment>
+        }}
+
+        onNotFound={() => {
+          if (admin) {
+            return <Fragment>
+              <NotificationCard notification={{
+                message: 'No default instance',
+                description: 'in order to use merchant backend, you should 
create the default instance',
+                type: 'INFO'
+              }} />
+              <InstanceCreatePage onError={() => null} onConfirm={() => null} 
/>
+            </Fragment>
+          }
+          return <NotFoundPage />
+        }}
 
         onBack={() => {
           route(`/`);
         }}
 
         onConfirm={() => {
-          pushNotification({ message: i18n`create_success`, type: 'SUCCESS' });
+          // pushNotification({ message: i18n`create_success`, type: 'SUCCESS' 
});
           route(`/`);
         }}
 
         onUpdateError={(e: Error) => {
-          pushNotification({ message: i18n`update_error`, type: 'ERROR' });
+          // pushNotification({ message: i18n`update_error`, type: 'ERROR' });
         }}
       />
 
       <Route path={InstancePaths.product_list}
         component={ProductListPage}
-        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} />}
+        onUnauthorized={() => {
+          return <Fragment>
+            <NotificationCard notification={{ message: i18n`Access denied`, 
description: i18n`Check your token is valid`, type: 'ERROR', }} />
+            <LoginPage onConfirm={updateLoginStatus} />
+          </Fragment>
+        }}
+
+        onNotFound={() => {
+          if (admin) {
+            return <Fragment>
+              <NotificationCard notification={{
+                message: 'No default instance',
+                description: 'in order to use merchant backend, you should 
create the default instance',
+                type: 'INFO'
+              }} />
+              <InstanceCreatePage onError={() => null} onConfirm={() => null} 
/>
+            </Fragment>
+          }
+          return <NotFoundPage />
+        }}
+
+        onLoadError={(error: SwrError) => {
+          return <Fragment>
+            <NotificationCard notification={{ message: i18n`Problem reaching 
the server`, description: i18n`Got message: ${error.message} from: 
${error.backend} (hasToken: ${error.hasToken})`, type: 'ERROR' }} />
+            <LoginPage onConfirm={updateLoginStatus} />
+          </Fragment>
+        }}
       />
       <Route path={InstancePaths.product_update}
         component={ProductUpdatePage}
@@ -201,13 +314,34 @@ export function InstanceRoutes({ id, pushNotification, 
admin }: Props): VNode {
 
       <Route path={InstancePaths.order_list}
         component={OrderListPage}
-        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} />}
+        onUnauthorized={() => {
+          return <Fragment>
+            <NotificationCard notification={{ message: i18n`Access denied`, 
description: i18n`Check your token is valid`, type: 'ERROR', }} />
+            <LoginPage onConfirm={updateLoginStatus} />
+          </Fragment>
+        }}
+
+        onNotFound={() => {
+          if (admin) {
+            return <Fragment>
+              <NotificationCard notification={{
+                message: 'No default instance',
+                description: 'in order to use merchant backend, you should 
create the default instance',
+                type: 'INFO'
+              }} />
+              <InstanceCreatePage onError={() => null} onConfirm={() => null} 
/>
+            </Fragment>
+          }
+          return <NotFoundPage />
+        }}
+
+        onLoadError={(error: SwrError) => {
+          return <Fragment>
+            <NotificationCard notification={{ message: i18n`Problem reaching 
the server`, description: i18n`Got message: ${error.message} from: 
${error.backend} (hasToken: ${error.hasToken})`, type: 'ERROR' }} />
+            <LoginPage onConfirm={updateLoginStatus} />
+          </Fragment>
+        }}
       />
       <Route path={InstancePaths.order_update}
         component={OrderUpdatePage}
@@ -218,13 +352,34 @@ export function InstanceRoutes({ id, pushNotification, 
admin }: Props): VNode {
 
       <Route path={InstancePaths.tips_list}
         component={TipListPage}
-        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} />}
+        onUnauthorized={() => {
+          return <Fragment>
+            <NotificationCard notification={{ message: i18n`Access denied`, 
description: i18n`Check your token is valid`, type: 'ERROR', }} />
+            <LoginPage onConfirm={updateLoginStatus} />
+          </Fragment>
+        }}
+
+        onNotFound={() => {
+          if (admin) {
+            return <Fragment>
+              <NotificationCard notification={{
+                message: 'No default instance',
+                description: 'in order to use merchant backend, you should 
create the default instance',
+                type: 'INFO'
+              }} />
+              <InstanceCreatePage onError={() => null} onConfirm={() => null} 
/>
+            </Fragment>
+          }
+          return <NotFoundPage />
+        }}
+
+        onLoadError={(error: SwrError) => {
+          return <Fragment>
+            <NotificationCard notification={{ message: i18n`Problem reaching 
the server`, description: i18n`Got message: ${error.message} from: 
${error.backend} (hasToken: ${error.hasToken})`, type: 'ERROR' }} />
+            <LoginPage onConfirm={updateLoginStatus} />
+          </Fragment>
+        }}
       />
       <Route path={InstancePaths.tips_update}
         component={TipUpdatePage}
@@ -235,19 +390,42 @@ export function InstanceRoutes({ id, pushNotification, 
admin }: Props): VNode {
 
       <Route path={InstancePaths.transfers_list}
         component={TransferListPage}
-        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} />}
+        onUnauthorized={() => {
+          return <Fragment>
+            <NotificationCard notification={{ message: i18n`Access denied`, 
description: i18n`Check your token is valid`, type: 'ERROR', }} />
+            <LoginPage onConfirm={updateLoginStatus} />
+          </Fragment>
+        }}
+
+        onNotFound={() => {
+          if (admin) {
+            return <Fragment>
+              <NotificationCard notification={{
+                message: 'No default instance',
+                description: 'in order to use merchant backend, you should 
create the default instance',
+                type: 'INFO'
+              }} />
+              <InstanceCreatePage onError={() => null} onConfirm={() => null} 
/>
+            </Fragment>
+          }
+          return <NotFoundPage />
+        }}
+
+        onLoadError={(error: SwrError) => {
+          return <Fragment>
+            <NotificationCard notification={{ message: i18n`Problem reaching 
the server`, description: i18n`Got message: ${error.message} from: 
${error.backend} (hasToken: ${error.hasToken})`, type: 'ERROR' }} />
+            <LoginPage onConfirm={updateLoginStatus} />
+          </Fragment>
+        }}
       />
       <Route path={InstancePaths.transfers_new}
         component={TransferCreatePage}
       />
 
-      <Route default component={NotFoundPage} />
+      {/* example of loading page*/}
+      <Route path="/loading" component={Loading} />
+    <Route default component={NotFoundPage} />
     </Router>
   </InstanceContextProvider>;
 
diff --git a/packages/frontend/src/components/exception/loading.tsx 
b/packages/frontend/src/components/exception/loading.tsx
index 0cd56cb..aa73453 100644
--- a/packages/frontend/src/components/exception/loading.tsx
+++ b/packages/frontend/src/components/exception/loading.tsx
@@ -1,5 +1,9 @@
 import { h, VNode } from "preact";
 
-export function Loading():VNode {
-  return <div>loading...</div>
+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><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 10854b1..fde6698 100644
--- a/packages/frontend/src/components/exception/login.tsx
+++ b/packages/frontend/src/components/exception/login.tsx
@@ -21,7 +21,7 @@
 
 import { h, VNode } from "preact";
 import { useMessageTemplate } from "preact-messages";
-import { useContext, useState } from "preact/hooks";
+import { useState } from "preact/hooks";
 import { useBackendContext } from "../../context/backend";
 import { Notification } from "../../utils/types";
 
@@ -39,16 +39,6 @@ export function LoginModal({ onConfirm, withMessage }: 
Props): VNode {
   return <div class="columns is-centered">
     <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">
-              <div>
-                <p><strong>{withMessage.message}</strong></p>
-                {withMessage.description}
-              </div>
-            </div>
-          </div>
-        </div>}
         <header class="modal-card-head" style={{ border: '1px solid', 
borderBottom: 0 }}>
           <p class="modal-card-title">{i18n`Login required`}</p>
         </header>
diff --git a/packages/frontend/src/components/menu/index.tsx 
b/packages/frontend/src/components/menu/index.tsx
index 7754177..5fa1494 100644
--- a/packages/frontend/src/components/menu/index.tsx
+++ b/packages/frontend/src/components/menu/index.tsx
@@ -14,91 +14,115 @@
  GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 
- import { ComponentChildren, Fragment, h, VNode } from "preact";
- import { useEffect, useState } from "preact/hooks";
- import { AdminPaths } from "../../AdminRoutes";
- import { InstancePaths } from "../../InstanceRoutes";
- import { NavigationBar } from "./NavigationBar";
- import { Sidebar } from "./SideBar";
- import Match from 'preact-router/match';
- 
- 
- function getInstanceTitle(path: string, id: string): string {
- 
-   switch (path) {
-     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_update: return `${id}: Update 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.transfers_list: return `${id}: Transfers`
-     case InstancePaths.transfers_new: return `${id}: New Transfer`
-     default: return '';
-   }
- }
- 
- const INSTANCE_ID_LOOKUP = /^\/instance\/([^/]*)\//
- function getAdminTitle(path: string) {
-   if (path === AdminPaths.new_instance) return `New instance`
-   if (path === AdminPaths.list_instances) return `Instances`
-   const match = INSTANCE_ID_LOOKUP.exec(path)
-   if (match && match[1]) return 
getInstanceTitle(path.replace(INSTANCE_ID_LOOKUP, '/'), match[1]);
-   return getInstanceTitle(path, 'default')
- }
- 
- interface MenuProps {
-   title?: string;
-   instance: string;
-   admin?: boolean;
-   onLogout?: () => void;
- }
- 
-function WithTitle({title,children}:{title:string, 
children:ComponentChildren}):VNode {
+import { ComponentChildren, Fragment, h, VNode } from "preact";
+import { useEffect, useState } from "preact/hooks";
+import { AdminPaths } from "../../AdminRoutes";
+import { InstancePaths } from "../../InstanceRoutes";
+import { NavigationBar } from "./NavigationBar";
+import { Sidebar } from "./SideBar";
+import Match from 'preact-router/match';
+import { Notification } from "../../utils/types";
+
+
+function getInstanceTitle(path: string, id: string): string {
+
+  switch (path) {
+    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_update: return `${id}: Update 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.transfers_list: return `${id}: Transfers`
+    case InstancePaths.transfers_new: return `${id}: New Transfer`
+    default: return '';
+  }
+}
+
+const INSTANCE_ID_LOOKUP = /^\/instance\/([^/]*)\//
+function getAdminTitle(path: string) {
+  if (path === AdminPaths.new_instance) return `New instance`
+  if (path === AdminPaths.list_instances) return `Instances`
+  const match = INSTANCE_ID_LOOKUP.exec(path)
+  if (match && match[1]) return 
getInstanceTitle(path.replace(INSTANCE_ID_LOOKUP, '/'), match[1]);
+  return getInstanceTitle(path, 'default')
+}
+
+interface MenuProps {
+  title?: string;
+  instance: string;
+  admin?: boolean;
+  onLogout?: () => void;
+}
+
+function WithTitle({ title, children }: { title: string, children: 
ComponentChildren }): VNode {
   useEffect(() => {
     document.title = `Taler Backoffice: ${title}`
   }, [title])
   return <Fragment>{children}</Fragment>
 }
- 
- export function Menu({ onLogout, title, instance, admin }: MenuProps): VNode {
-   const [mobileOpen, setMobileOpen] = useState(false)
- 
-   return <Match>{({ path }: any) => {
-     const titleWithSubtitle = title ? title : (!admin ? 
getInstanceTitle(path, instance) : getAdminTitle(path))
- 
-     return (<WithTitle title={titleWithSubtitle}>
-       <div class={mobileOpen ? "has-aside-mobile-expanded" : ""} onClick={() 
=> setMobileOpen(false)}>
-         <NavigationBar onMobileMenu={() => setMobileOpen(!mobileOpen)} 
title={titleWithSubtitle} />
-         {onLogout && <Sidebar onLogout={onLogout} admin={admin} 
instance={instance} mobile={mobileOpen} />}
-       </div>
-     </WithTitle>
-     )
-   }}</Match>
- 
- }
- 
- interface NotYetReadyAppMenuProps {
-   title: string;
-   onLogout?: () => void;
- }
- 
- export function NotYetReadyAppMenu({ onLogout, title }: 
NotYetReadyAppMenuProps): VNode {
-   const [mobileOpen, setMobileOpen] = useState(false)
- 
-   useEffect(() => {
-     document.title = `Taler Backoffice: ${title}`
-   }, [title])
- 
-   return <div class={mobileOpen ? "has-aside-mobile-expanded" : ""} 
onClick={() => setMobileOpen(false)}>
-     <NavigationBar onMobileMenu={() => setMobileOpen(!mobileOpen)} 
title={title} />
-     {onLogout && <Sidebar onLogout={onLogout} instance="" mobile={mobileOpen} 
/>}
-   </div>
- 
- }
- 
+
+export function Menu({ onLogout, title, instance, admin }: MenuProps): VNode {
+  const [mobileOpen, setMobileOpen] = useState(false)
+
+  return <Match>{({ path }: any) => {
+    const titleWithSubtitle = title ? title : (!admin ? getInstanceTitle(path, 
instance) : getAdminTitle(path))
+
+    return (<WithTitle title={titleWithSubtitle}>
+      <div class={mobileOpen ? "has-aside-mobile-expanded" : ""} onClick={() 
=> setMobileOpen(false)}>
+        <NavigationBar onMobileMenu={() => setMobileOpen(!mobileOpen)} 
title={titleWithSubtitle} />
+        {onLogout && <Sidebar onLogout={onLogout} admin={admin} 
instance={instance} mobile={mobileOpen} />}
+      </div>
+    </WithTitle>
+    )
+  }}</Match>
+
+}
+
+interface NotYetReadyAppMenuProps {
+  title: string;
+  onLogout?: () => void;
+}
+
+interface NotifProps {
+  notification?: Notification;
+}
+export function NotificationCard({ notification:n }: NotifProps) {
+  // const [n, setNotif] = useState(notification)
+  if (!n) return null
+  return <div class="notification">
+    <div class="columns is-vcentered">
+      <div class="column is-12">
+        <article class={n.type === 'ERROR' ? "message is-danger" : "message 
is-info"}>
+          <div class="message-header">
+            <p>{n.message}</p>
+            {/* {n.type !== 'ERROR' && <button class="delete" 
aria-label="delete" onClick={() => setNotif(undefined)}></button> } */}
+          </div>
+          <div class="message-body">
+          {n.description}
+          </div>
+        </article>
+      </div>
+    </div>
+  </div>
+}
+
+export function NotYetReadyAppMenu({ onLogout, title }: 
NotYetReadyAppMenuProps): VNode {
+  const [mobileOpen, setMobileOpen] = useState(false)
+
+  useEffect(() => {
+    document.title = `Taler Backoffice: ${title}`
+  }, [title])
+
+  return <div class={mobileOpen ? "has-aside-mobile-expanded" : ""} 
onClick={() => setMobileOpen(false)}>
+    <NavigationBar onMobileMenu={() => setMobileOpen(!mobileOpen)} 
title={title} />
+    {onLogout && <Sidebar onLogout={onLogout} instance="" mobile={mobileOpen} 
/>}
+  </div>
+
+}
+
diff --git a/packages/frontend/src/hooks/backend.ts 
b/packages/frontend/src/hooks/backend.ts
index 20e056a..a9160c9 100644
--- a/packages/frontend/src/hooks/backend.ts
+++ b/packages/frontend/src/hooks/backend.ts
@@ -23,6 +23,7 @@ import useSWR, { mutate, cache } from 'swr';
 import axios from 'axios'
 import { MerchantBackend } from '../declaration';
 import { useBackendContext, useInstanceContext } from '../context/backend';
+import { useEffect, useState } from 'preact/hooks';
 
 function mutateAll(re: RegExp) {
   cache.keys().filter(key => re.test(key)).forEach(key => mutate(key, null))
@@ -32,6 +33,8 @@ type HttpResponse<T> = HttpResponseOk<T> | HttpResponseError;
 
 interface HttpResponseOk<T> {
   data: T;
+  unauthorized: boolean;
+  notfound: boolean;
 }
 
 export interface SwrError {
@@ -107,6 +110,7 @@ interface AdminMutateAPI {
   createInstance: (data: 
MerchantBackend.Instances.InstanceConfigurationMessage) => Promise<void>;
   deleteInstance: (id: string) => Promise<void>;
 }
+
 export function useAdminMutateAPI(): AdminMutateAPI {
   const { url, token } = useBackendContext()
 
@@ -121,7 +125,7 @@ export function useAdminMutateAPI(): AdminMutateAPI {
   }
 
   const deleteInstance = async (id: string): Promise<void> => {
-    await request(`${url}/private/`, {
+    await request(`${url}/private/instances/${id}`, {
       method: 'delete',
       token,
     })
@@ -395,6 +399,26 @@ export function useInstanceMutateAPI(): InstaceMutateAPI {
   return { updateInstance, deleteInstance, setNewToken, clearToken }
 }
 
+export function useBackendInstancesTestForAdmin(): 
HttpResponse<MerchantBackend.Instances.InstancesResponse> {
+  const { url, token } = useBackendContext()
+  interface Result {
+    data?: MerchantBackend.Instances.InstancesResponse;
+    error?: SwrError;
+  }
+  const [result, setResult] = useState<Result|undefined>(undefined)
+
+  useEffect(() => {
+    request(`${url}/private/instances`, { token })
+      .then(data => setResult({data}))
+      .catch(error => setResult({error}))
+  },[url, token])
+
+  const data = result?.data
+  const error = result?.error
+
+  return { data, unauthorized: error?.status === 401, notfound: error?.status 
=== 404, error }
+}
+
 export function useBackendInstances(): 
HttpResponse<MerchantBackend.Instances.InstancesResponse> {
   const { url, token } = useBackendContext()
   const { data, error } = useSWR<MerchantBackend.Instances.InstancesResponse, 
SwrError>(['/private/instances', token, url], fetcher)
diff --git a/packages/frontend/src/paths/login/index.tsx 
b/packages/frontend/src/hooks/notification.ts
similarity index 54%
copy from packages/frontend/src/paths/login/index.tsx
copy to packages/frontend/src/hooks/notification.ts
index 91dc94c..d1dfbff 100644
--- a/packages/frontend/src/paths/login/index.tsx
+++ b/packages/frontend/src/hooks/notification.ts
@@ -14,18 +14,30 @@
  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 { LoginModal } from '../../components/exception/login';
-import { Notification } from "../../utils/types";
 
-interface Props {
-  withMessage?: Notification;
-  onConfirm: (url: string, token?: string) => void;
+import { useCallback, useState } from "preact/hooks";
+import { Notification } from '../utils/types';
+
+interface Result {
+  notification?: Notification;
+  pushNotification: (n: Notification) => void;
+  removeNotification: () => void;
+}
+
+export function useNotification(): Result {
+  const [notification, setNotifications] = 
useState<Notification|undefined>(undefined)
+
+  const pushNotification = useCallback((n: Notification): void => {
+    setNotifications(n)
+  },[])
+
+  const removeNotification = useCallback(() => {
+    setNotifications(undefined)
+  },[])
+
+  return { notification, pushNotification, removeNotification }
 }
-export default function LoginPage({ onConfirm, withMessage }: Props): VNode {
-  return <LoginModal onConfirm={onConfirm} withMessage={withMessage} />
-} 
\ No newline at end of file
diff --git a/packages/frontend/src/index.tsx b/packages/frontend/src/index.tsx
index 90625c7..57e2cc2 100644
--- a/packages/frontend/src/index.tsx
+++ b/packages/frontend/src/index.tsx
@@ -22,21 +22,19 @@
 import "./scss/main.scss"
 
 import { h, VNode } from 'preact';
-import { useCallback, useContext, useEffect, useMemo, useState } from 
"preact/hooks";
-import { Route, route } from 'preact-router';
+import { useEffect, useMemo } from "preact/hooks";
+import { route } from 'preact-router';
 import { MessageProvider, useMessageTemplate } from 'preact-messages';
 
-import { Notifications } from './components/notifications';
 import * as messages from './messages'
 import { useBackendContextState } from './hooks';
-import { useNotifications } from "./hooks/notifications";
 import { BackendContextProvider, ConfigContextProvider, useBackendContext } 
from './context/backend';
 import { useBackendConfig } from "./hooks/backend";
 import { hasKey, onTranslationError } from "./utils/functions";
 
 import LoginPage from './paths/login';
 import { ApplicationReadyRoutes } from "./ApplicationReadyRoutes";
-import { NotYetReadyAppMenu } from "./components/menu";
+import { NotificationCard, NotYetReadyAppMenu } from "./components/menu";
 
 export function Redirect({ to }: { to: string }): null {
   useEffect(() => {
@@ -58,7 +56,6 @@ export default function Application(): VNode {
 }
 
 function ApplicationStatusRoutes(): VNode {
-  const { notifications, pushNotification, removeNotification } = 
useNotifications()
   const { changeBackend, triedToLog, updateToken, resetBackend } = 
useBackendContext()
   const backendConfig = useBackendConfig();
   const i18n = useMessageTemplate()
@@ -66,16 +63,16 @@ function ApplicationStatusRoutes(): VNode {
   const v = `${backendConfig.data?.currency} ${backendConfig.data?.version}`
   const ctx = useMemo(() => ({ currency: backendConfig.data?.currency || '', 
version: backendConfig.data?.version || '' }), [v])
 
+  const updateLoginInfoAndGoToRoot = (url: string, token?: string) => {
+    changeBackend(url)
+    if (token) updateToken(token)
+    route('/')
+  }
+
   if (!triedToLog) {
     return <div id="app">
       <NotYetReadyAppMenu title="Welcome!" />
-      <LoginPage
-        onConfirm={(url: string, token?: string) => {
-          changeBackend(url)
-          if (token) updateToken(token)
-          route('/')
-        }}
-      />
+      <LoginPage onConfirm={updateLoginInfoAndGoToRoot} />
     </div>
   }
 
@@ -86,37 +83,24 @@ function ApplicationStatusRoutes(): VNode {
     if (backendConfig.unauthorized) {
       return <div id="app">
         <NotYetReadyAppMenu title="Login" />
-        <LoginPage
-          onConfirm={(url: string, token?: string) => {
-            changeBackend(url)
-            if (token) updateToken(token)
-            route('/')
-          }}
-        />
+        <LoginPage onConfirm={updateLoginInfoAndGoToRoot} />
       </div>
     }
 
     return <div id="app">
       <NotYetReadyAppMenu title="Error" />
-      <LoginPage
-        withMessage={{
-          message: i18n`Couldnt access the server`,
-          type: 'ERROR',
-          description: i18n`Got message: ${backendConfig.error.message} from: 
${backendConfig.error.backend} (hasToken: ${backendConfig.error.hasToken})`,
-        }}
-        onConfirm={(url: string, token?: string) => {
-          changeBackend(url)
-          if (token) updateToken(token)
-          route('/')
-        }}
-      />
+      <NotificationCard notification={{
+        message: i18n`Couldnt access the server`,
+        type: 'ERROR',
+        description: i18n`Got message: ${backendConfig.error.message} from: 
${backendConfig.error.backend} (hasToken: ${backendConfig.error.hasToken})`,
+      }} />
+      <LoginPage onConfirm={updateLoginInfoAndGoToRoot} />
     </div>
   }
 
   return <div id="app" class="has-navbar-fixed-top">
     <ConfigContextProvider value={ctx}>
-      <Notifications notifications={notifications} 
removeNotification={removeNotification} />
-      <Route default component={ApplicationReadyRoutes} 
pushNotification={pushNotification} />
+      <ApplicationReadyRoutes />
     </ConfigContextProvider>
   </div>
 }
diff --git a/packages/frontend/src/messages/en.po 
b/packages/frontend/src/messages/en.po
index c22199a..49ecd68 100644
--- a/packages/frontend/src/messages/en.po
+++ b/packages/frontend/src/messages/en.po
@@ -276,4 +276,10 @@ msgstr ""
 #  msgstr ""
 
 msgid "fields.instance.wired.label"
-msgstr "Wired"
\ No newline at end of file
+msgstr "Wired"
+
+msgid "create_success"
+msgstr "Creation succeed"
+
+msgid "create_error"
+msgstr "Creation failed"
diff --git a/packages/frontend/src/paths/admin/create/CreatePage.tsx 
b/packages/frontend/src/paths/admin/create/CreatePage.tsx
index ae76796..a286ba4 100644
--- a/packages/frontend/src/paths/admin/create/CreatePage.tsx
+++ b/packages/frontend/src/paths/admin/create/CreatePage.tsx
@@ -40,7 +40,7 @@ type Entity = 
MerchantBackend.Instances.InstanceConfigurationMessage & {auth_tok
 interface Props {
   onCreate: (d: Entity) => void;
   isLoading: boolean;
-  onBack: () => void;
+  onBack?: () => void;
 }
 
 interface KeyValue {
@@ -114,7 +114,7 @@ export function CreatePage({ onCreate, isLoading, onBack }: 
Props): VNode {
           </FormProvider>
 
           <div class="buttons is-right mt-5">
-            <button class="button" onClick={onBack} ><Message id="Cancel" 
/></button>
+            { onBack && <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/paths/admin/create/index.tsx 
b/packages/frontend/src/paths/admin/create/index.tsx
index f5591dd..7aeba5f 100644
--- a/packages/frontend/src/paths/admin/create/index.tsx
+++ b/packages/frontend/src/paths/admin/create/index.tsx
@@ -13,24 +13,112 @@
  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 { Fragment, h, VNode } from "preact";
+import { useState } from "preact/hooks";
+import { Loading } from "../../../components/exception/loading";
+import { FormProvider } from "../../../components/form/Field";
+import { Input } from "../../../components/form/Input";
+import { NotificationCard } from "../../../components/menu";
 import { MerchantBackend } from "../../../declaration";
 import { useAdminMutateAPI } from "../../../hooks/backend";
+import { Notification } from "../../../utils/types";
 import { CreatePage } from "./CreatePage";
 
 interface Props {
-  onBack: () => void;
+  onBack?: () => void;
   onConfirm: () => void;
   onError: (error: any) => void;
 }
+type Entity = MerchantBackend.Instances.InstanceConfigurationMessage;
 
 export default function Create({ onBack, onConfirm, onError }: Props): VNode {
   const { createInstance } = useAdminMutateAPI();
+  const [notif, setNotif] = useState<Notification | undefined>(undefined)
+  const [createdOk, setCreatedOk] = useState<Entity | undefined>(undefined);
 
-  return <CreatePage 
-    onBack={onBack}
-    isLoading={false}
-    onCreate={(d: MerchantBackend.Instances.InstanceConfigurationMessage): 
Promise<void> => {
-      return createInstance(d).then(onConfirm).catch(onError)
-    }} />
+  if (createdOk) {
+    return <div class="columns is-fullwidth is-vcentered content-full-size">
+      <div class="column" />
+      <div class="column is-half">
+        <div class="card">
+          <header class="card-header has-background-success">
+            <p class="card-header-title has-text-white-ter">
+              Instance created successfully
+            </p>
+          </header>
+          <div class="card-content">
+            <div class="field is-horizontal">
+              <div class="field-label is-normal">
+                <label class="label">ID</label>
+              </div>
+              <div class="field-body is-flex-grow-3">
+                <div class="field">
+                  <p class="control">
+                    <input class="input" readonly value={createdOk.id} 
disabled />
+                  </p>
+                </div>
+              </div>
+            </div>
+            <div class="field is-horizontal">
+              <div class="field-label is-normal">
+                <label class="label">Business Name</label>
+              </div>
+              <div class="field-body is-flex-grow-3">
+                <div class="field">
+                  <p class="control">
+                    <input class="input" readonly value={createdOk.name} 
disabled />
+                  </p>
+                </div>
+              </div>
+            </div>
+            <div class="field is-horizontal">
+              <div class="field-label is-normal">
+                <label class="label">Token</label>
+              </div>
+              <div class="field-body is-flex-grow-3">
+                <div class="field">
+                  <p class="control">
+                    <input class="input" readonly value={createdOk.auth.token} 
disabled />
+                  </p>
+                </div>
+              </div>
+            </div>
+          </div>
+          <footer class="card-footer">
+            <p class="card-footer-item" style={{ border: 'none' }}>
+              <span>
+              </span>
+            </p>
+            <p class="card-footer-item" style={{ border: 'none' }}>
+              <span>
+              </span>
+            </p>
+            <p class="card-footer-item">
+              <button class="button is-info" 
onClick={onConfirm}>Continue</button>
+            </p>
+          </footer>
+        </div>
+      </div>
+      <div class="column" />
+    </div>
+  }
+
+  return <Fragment>
+    <NotificationCard notification={notif} />
+
+    <CreatePage
+      onBack={onBack}
+      isLoading={false}
+      onCreate={(d: MerchantBackend.Instances.InstanceConfigurationMessage) => 
{
+        createInstance(d).then((r) => {
+          setCreatedOk(d)
+        }).catch((error) => {
+          setNotif({
+            message: 'could not create instance',
+            type: "ERROR",
+            description: error.message
+          })
+        })
+      }} />
+  </Fragment>
 }
\ No newline at end of file
diff --git a/packages/frontend/src/paths/admin/list/index.tsx 
b/packages/frontend/src/paths/admin/list/index.tsx
index 0fe63ab..a96c5b7 100644
--- a/packages/frontend/src/paths/admin/list/index.tsx
+++ b/packages/frontend/src/paths/admin/list/index.tsx
@@ -26,9 +26,10 @@ import { useState } from 'preact/hooks';
 import { MerchantBackend } from '../../../declaration';
 import { Notification } from '../../../utils/types';
 import { DeleteModal } from '../../../components/modal';
+import { Loading } from '../../../components/exception/loading';
 
 interface Props {
-  pushNotification: (n: Notification) => void;
+  // pushNotification: (n: Notification) => void;
   onCreate: () => void;
   onUpdate: (id: string) => void;
   instances: MerchantBackend.Instances.Instance[];
@@ -36,17 +37,15 @@ interface Props {
   onLoadError: (e: SwrError) => VNode;
 }
 
-export default function Instances({ pushNotification, onUnauthorized, 
onLoadError, onCreate, onUpdate }: Props): VNode {
+export default function Instances({ onUnauthorized, onLoadError, onCreate, 
onUpdate }: Props): VNode {
   const result = useBackendInstances()
   const [deleting, setDeleting] = useState<MerchantBackend.Instances.Instance 
| null>(null)
   const { deleteInstance } = useAdminMutateAPI()
 
+  if (result.unauthorized) return onUnauthorized()
   if (!result.data) {
-    if (result.unauthorized) return onUnauthorized()
     if (result.error) return onLoadError(result.error)
-    return <div>
-      loading ....
-    </div>
+    return <Loading />
   }
 
   return <Fragment>
@@ -63,9 +62,9 @@ export default function Instances({ pushNotification, 
onUnauthorized, onLoadErro
       onConfirm={async (): Promise<void> => {
         try {
           await deleteInstance(deleting.id)
-          pushNotification({ message: 'delete_success', type: 'SUCCESS' })
+          // pushNotification({ message: 'delete_success', type: 'SUCCESS' })
         } catch (e) {
-          pushNotification({ message: 'delete_error', type: 'ERROR' })
+          // pushNotification({ message: 'delete_error', type: 'ERROR' })
         }
         setDeleting(null)
       }}
diff --git a/packages/frontend/src/paths/instance/details/index.tsx 
b/packages/frontend/src/paths/instance/details/index.tsx
index 492878c..5771cac 100644
--- a/packages/frontend/src/paths/instance/details/index.tsx
+++ b/packages/frontend/src/paths/instance/details/index.tsx
@@ -20,28 +20,28 @@ import { Notification } from "../../../utils/types";
 import { useInstanceDetails, useInstanceMutateAPI, SwrError } from 
"../../../hooks/backend";
 import { DetailPage } from "./DetailPage";
 import { DeleteModal } from "../../../components/modal";
+import { Loading } from "../../../components/exception/loading";
 
 interface Props {
   onUnauthorized: () => VNode;
   onLoadError: (e: SwrError) => VNode;
   onUpdate: () => void;
+  onNotFound: () => VNode;
   onDelete: () => void;
-  pushNotification: (n: Notification) => void;
 }
 
-export default function Detail({ onUpdate, onLoadError, onUnauthorized, 
pushNotification, onDelete }: Props): VNode {
+export default function Detail({ onUpdate, onLoadError, onUnauthorized, 
onDelete, onNotFound }: Props): VNode {
   const { id } = useInstanceContext()
   const result = useInstanceDetails()
   const [deleting, setDeleting] = useState<boolean>(false)
 
   const { deleteInstance } = useInstanceMutateAPI()
 
+  if (result.unauthorized) return onUnauthorized()
+  if (result.notfound) return onNotFound();
   if (!result.data) {
-    if (result.unauthorized) return onUnauthorized()
     if (result.error) return onLoadError(result.error)
-    return <div>
-      loading ....
-    </div>
+    return <Loading />
   }
 
   return <Fragment>
@@ -56,10 +56,10 @@ export default function Detail({ onUpdate, onLoadError, 
onUnauthorized, pushNoti
       onConfirm={async (): Promise<void> => {
         try {
           await deleteInstance()
-          pushNotification({ message: 'delete_success', type: 'SUCCESS' })
+          // pushNotification({ message: 'delete_success', type: 'SUCCESS' })
           onDelete()
         } catch (error) {
-          pushNotification({ message: 'delete_error', type: 'ERROR' })
+          // pushNotification({ message: 'delete_error', type: 'ERROR' })
         }
         setDeleting(false)
       }}
diff --git a/packages/frontend/src/paths/instance/orders/list/index.tsx 
b/packages/frontend/src/paths/instance/orders/list/index.tsx
index 13ea48e..c0dde74 100644
--- a/packages/frontend/src/paths/instance/orders/list/index.tsx
+++ b/packages/frontend/src/paths/instance/orders/list/index.tsx
@@ -10,6 +10,7 @@ import { InputBoolean } from 
"../../../../components/form/InputBoolean";
 interface Props {
   onUnauthorized: () => VNode;
   onLoadError: (e: SwrError) => VNode;
+  onNotFound: () => VNode;
   onCreate: () => void;
 }
 
@@ -18,7 +19,7 @@ const fromBooleanToYesAndNo = {
   toBoolean: (b: string) => b === 'yes' ? true : (b === 'no' ? false : 
undefined)
 }
 
-export default function ({ onUnauthorized, onLoadError, onCreate }: Props): 
VNode {
+export default function ({ onUnauthorized, onLoadError, onCreate, onNotFound 
}: Props): VNode {
   const [filter, setFilter] = useState<InstanceOrderFilter>({})
   const result = useInstanceOrders(filter)
   const { createOrder, deleteOrder } = useOrderMutateAPI()
@@ -26,8 +27,9 @@ export default function ({ onUnauthorized, onLoadError, 
onCreate }: Props): VNod
 
   let instances: (MerchantBackend.Orders.OrderHistoryEntry & {id: string})[];
   
+  if (result.unauthorized) return onUnauthorized()
+  if (result.notfound) return onNotFound()
   if (!result.data) {
-    if (result.unauthorized) return onUnauthorized()
     if (result.error) return onLoadError(result.error)
     //if loading asume empty list
     instances = []
diff --git a/packages/frontend/src/paths/instance/products/list/index.tsx 
b/packages/frontend/src/paths/instance/products/list/index.tsx
index a7f271e..df6a973 100644
--- a/packages/frontend/src/paths/instance/products/list/index.tsx
+++ b/packages/frontend/src/paths/instance/products/list/index.tsx
@@ -5,21 +5,24 @@ import { CardTable } from './Table';
 import logo from '../../../../assets/logo.jpeg';
 import { useConfigContext } from '../../../../context/backend';
 import { MerchantBackend } from '../../../../declaration';
+import { Loading } from '../../../../components/exception/loading';
 
 interface Props {
   onUnauthorized: () => VNode;
+  onNotFound: () => VNode;
   onLoadError: (e: SwrError) => VNode;
 }
-export default function ({ onUnauthorized, onLoadError }: Props): VNode {
+export default function ({ onUnauthorized, onLoadError, onNotFound }: Props): 
VNode {
   const result = useInstanceProducts()
   const { createProduct, deleteProduct } = useProductMutateAPI()
   const { currency } = useConfigContext()
+
+  if (result.unauthorized) return onUnauthorized()
+  if (result.notfound) return onNotFound()
+  
   if (!result.data) {
-    if (result.unauthorized) return onUnauthorized()
     if (result.error) return onLoadError(result.error)
-    return <div>
-      loading ....
-    </div>
+    return <Loading />
   }
   return <section class="section is-main-section">
     <CardTable instances={result.data.products.map(o => ({ ...o, id: 
o.product_id }))}
diff --git a/packages/frontend/src/paths/instance/tips/list/index.tsx 
b/packages/frontend/src/paths/instance/tips/list/index.tsx
index c68f7f9..c8a2dc7 100644
--- a/packages/frontend/src/paths/instance/tips/list/index.tsx
+++ b/packages/frontend/src/paths/instance/tips/list/index.tsx
@@ -1,4 +1,5 @@
 import { h, VNode } from 'preact';
+import { Loading } from '../../../../components/exception/loading';
 import { useConfigContext } from '../../../../context/backend';
 import { MerchantBackend } from '../../../../declaration';
 import { SwrError, useInstanceMutateAPI, useInstanceTips, useTipsMutateAPI } 
from '../../../../hooks/backend';
@@ -7,18 +8,21 @@ import { CardTable } from './Table';
 interface Props {
   onUnauthorized: () => VNode;
   onLoadError: (e: SwrError) => VNode;
+  onNotFound: () => VNode;
 }
-export default function ({ onUnauthorized, onLoadError }: Props): VNode {
+export default function ({ onUnauthorized, onLoadError, onNotFound }: Props): 
VNode {
   const result = useInstanceTips()
   const { createReserve, deleteReserve } = useTipsMutateAPI()
   const { currency } = useConfigContext()
+
+  if (result.unauthorized) return onUnauthorized()
+  if (result.notfound) return onNotFound()
+
   if (!result.data) {
-    if (result.unauthorized) return onUnauthorized()
     if (result.error) return onLoadError(result.error)
-    return <div>
-      loading ....
-    </div>
+    return <Loading />
   }
+  
   return <section class="section is-main-section">
     <CardTable instances={result.data.reserves.filter(r => r.active).map(o => 
({ ...o, id: o.reserve_pub }))}
       onCreate={() => createReserve({
diff --git a/packages/frontend/src/paths/instance/transfers/list/index.tsx 
b/packages/frontend/src/paths/instance/transfers/list/index.tsx
index 488130c..bc0ffca 100644
--- a/packages/frontend/src/paths/instance/transfers/list/index.tsx
+++ b/packages/frontend/src/paths/instance/transfers/list/index.tsx
@@ -1,4 +1,5 @@
 import { h, VNode } from 'preact';
+import { Loading } from '../../../../components/exception/loading';
 import { useConfigContext } from '../../../../context/backend';
 import { SwrError, useInstanceTransfers, useTransferMutateAPI } from 
'../../../../hooks/backend';
 import { CardTable } from './Table';
@@ -6,17 +7,19 @@ import { CardTable } from './Table';
 interface Props {
   onUnauthorized: () => VNode;
   onLoadError: (e: SwrError) => VNode;
+  onNotFound: () => VNode;
 }
-export default function ({ onUnauthorized, onLoadError }: Props): VNode {
+export default function ({ onUnauthorized, onLoadError, onNotFound }: Props): 
VNode {
   const result = useInstanceTransfers()
   const { informTransfer } = useTransferMutateAPI()
   const { currency } = useConfigContext()
+  
+  if (result.unauthorized) return onUnauthorized()
+  if (result.notfound) return onNotFound();
+
   if (!result.data) {
-    if (result.unauthorized) return onUnauthorized()
     if (result.error) return onLoadError(result.error)
-    return <div>
-      loading ....
-    </div>
+    return <Loading />
   }
   return <section class="section is-main-section">
     <CardTable instances={result.data.transfers.map(o => ({ ...o, id: 
String(o.transfer_serial_id) }))}
diff --git a/packages/frontend/src/paths/instance/update/index.tsx 
b/packages/frontend/src/paths/instance/update/index.tsx
index 3bed0cf..3e76172 100644
--- a/packages/frontend/src/paths/instance/update/index.tsx
+++ b/packages/frontend/src/paths/instance/update/index.tsx
@@ -15,33 +15,34 @@
  */
 import { Fragment, h, VNode } from "preact";
 import { useState } from "preact/hooks";
+import { Loading } from "../../../components/exception/loading";
 import { UpdateTokenModal } from "../../../components/modal";
 import { useInstanceContext } from "../../../context/backend";
 import { MerchantBackend } from "../../../declaration";
 import { SwrError, useInstanceDetails, useInstanceMutateAPI } from 
"../../../hooks/backend";
 import { UpdatePage } from "./UpdatePage";
 
-interface Props {
+export interface Props {
   onBack: () => void;
   onConfirm: () => void;
-  pushNotification: (n: Notification) => void;
 
   onUnauthorized: () => VNode;
+  onNotFound: () => VNode;
   onLoadError: (e: SwrError) => VNode;
   onUpdateError: (e: Error) => void;
 
 }
 
-export default function Update({ onBack, onConfirm, onLoadError, 
onUpdateError, onUnauthorized }: Props): VNode {
+export default function Update({ onBack, onConfirm, onLoadError, onNotFound, 
onUpdateError, onUnauthorized }: Props): VNode {
   const { updateInstance } = useInstanceMutateAPI();
   const details = useInstanceDetails()
 
+  if (details.unauthorized) return onUnauthorized()
+  if (details.notfound) return onNotFound();
+  
   if (!details.data) {
-    if (details.unauthorized) return onUnauthorized()
     if (details.error) return onLoadError(details.error)
-    return <div>
-      loading ....
-    </div>
+    return <Loading />
   }
 
   return <Fragment>
diff --git a/packages/frontend/src/paths/login/index.tsx 
b/packages/frontend/src/paths/login/index.tsx
index 91dc94c..d6274e8 100644
--- a/packages/frontend/src/paths/login/index.tsx
+++ b/packages/frontend/src/paths/login/index.tsx
@@ -23,9 +23,8 @@ import { LoginModal } from '../../components/exception/login';
 import { Notification } from "../../utils/types";
 
 interface Props {
-  withMessage?: Notification;
   onConfirm: (url: string, token?: string) => void;
 }
-export default function LoginPage({ onConfirm, withMessage }: Props): VNode {
-  return <LoginModal onConfirm={onConfirm} withMessage={withMessage} />
+export default function LoginPage({ onConfirm }: Props): VNode {
+  return <LoginModal onConfirm={onConfirm} />
 } 
\ No newline at end of file
diff --git a/packages/frontend/src/scss/main.scss 
b/packages/frontend/src/scss/main.scss
index 8c4a353..fe99db8 100644
--- a/packages/frontend/src/scss/main.scss
+++ b/packages/frontend/src/scss/main.scss
@@ -93,6 +93,10 @@ input[type=checkbox]:indeterminate + .check {
   background-color: $white;
 }
 
+.right-sticky .buttons {
+  flex-wrap: nowrap  
+}
+
 .table.is-striped tbody tr:not(.is-selected):nth-child(even) .right-sticky {
   background-color: #fafafa;
 }
@@ -103,3 +107,64 @@ tr:hover .right-sticky {
 .table.is-striped tbody tr:nth-child(even):hover .right-sticky {
   background-color: hsl(0, 0%, 95%);
 }
+
+.lds-ring {
+  display: inline-block;
+  position: relative;
+  width: 80px;
+  height: 80px;
+}
+.lds-ring div {
+  box-sizing: border-box;
+  display: block;
+  position: absolute;
+  width: 64px;
+  height: 64px;
+  margin: 8px;
+  border: 8px solid black;
+  border-radius: 50%;
+  animation: lds-ring 1.2s cubic-bezier(0.5, 0, 0.5, 1) infinite;
+  border-color: black transparent transparent transparent;
+}
+.lds-ring div:nth-child(1) {
+  animation-delay: -0.45s;
+}
+.lds-ring div:nth-child(2) {
+  animation-delay: -0.3s;
+}
+.lds-ring div:nth-child(3) {
+  animation-delay: -0.15s;
+}
+@keyframes lds-ring {
+  0% {
+    transform: rotate(0deg);
+  }
+  100% {
+    transform: rotate(360deg);
+  }
+}
+
+.content-full-size {
+  height: calc(100% - 3rem);
+  position: absolute;
+  width: calc(100% - 14rem); 
+  display:flex;
+}
+
+.content-full-size .column .card {
+  min-width: 200px;
+
+}
+
+@include touch {
+  .content-full-size {
+    height: 100%;
+    position: absolute;
+    width: 100%;
+  }  
+}
+
+.column.is-half {
+  flex: none;
+  width: 50%;
+}
\ No newline at end of file

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