gnunet-svn
[Top][All Lists]
Advanced

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

[taler-merchant-backoffice] 06/06: working on instances listing


From: gnunet
Subject: [taler-merchant-backoffice] 06/06: working on instances listing
Date: Mon, 08 Feb 2021 21:35:31 +0100

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

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

commit e1bc057a4adb73aaebf605f3c8b2de33b5808845
Author: Sebastian <sebasjm@gmail.com>
AuthorDate: Mon Feb 8 17:31:36 2021 -0300

    working on instances listing
    
    Signed-off-by: Sebastian <sebasjm@gmail.com>
---
 src/components/auth/LoginPage.tsx             |  15 +
 src/components/hooks/backend.ts               |  47 +++
 src/components/navbar/index.tsx               |   2 +-
 src/declaration.d.ts                          | 421 +++++++++++++++++++++++++-
 src/routes/index.tsx                          |  11 +-
 src/routes/instanceDetail/index.tsx           |   6 +
 src/routes/instances/{table.tsx => Table.tsx} |  10 +-
 src/routes/instances/View.stories.tsx         |  35 +++
 src/routes/instances/View.tsx                 |  74 +++++
 src/routes/instances/index.tsx                | 127 +-------
 10 files changed, 603 insertions(+), 145 deletions(-)

diff --git a/src/components/auth/LoginPage.tsx 
b/src/components/auth/LoginPage.tsx
new file mode 100644
index 0000000..427de64
--- /dev/null
+++ b/src/components/auth/LoginPage.tsx
@@ -0,0 +1,15 @@
+import { h, VNode } from "preact";
+import { useState } from "preact/hooks";
+
+interface Props {
+  onLogIn: (token: string) => void;
+}
+
+export default function LoginPage({ onLogIn }: Props): VNode {
+  const [token, update] = useState('')
+
+  return <div>
+    <input value={token} onInput={e => update(e?.currentTarget.value)} />
+    <button onClick={(): void => onLogIn(token)}>set</button>
+  </div>
+}
\ No newline at end of file
diff --git a/src/components/hooks/backend.ts b/src/components/hooks/backend.ts
new file mode 100644
index 0000000..ba322f0
--- /dev/null
+++ b/src/components/hooks/backend.ts
@@ -0,0 +1,47 @@
+import useSWR, { mutate } from 'swr';
+import axios from 'axios'
+import { MerchantBackend } from '../../declaration';
+
+type HttpResponse<T> = HttpResponseOk<T> | HttpResponseError<T>;
+
+interface HttpResponseOk<T> {
+  data: T;
+}
+interface HttpResponseError<T> {
+  data: undefined;
+  needsAuth: boolean;
+  error: Error;
+}
+
+class AuthError extends Error {
+  public readonly isAuth = true
+}
+
+const BACKEND = 'http://localhost:9966'
+const TOKEN_KEY = 'backend-token'
+
+// eslint-disable-next-line @typescript-eslint/no-explicit-any
+async function fetcher(url: string): Promise<any> {
+  const token = localStorage.getItem(TOKEN_KEY)
+  const headers = token ? { Authorization: `Bearer secret-token:${token}` } : 
undefined
+
+  const res = await axios.get(`${BACKEND}/private/${url}`, { headers })
+  if (res.status == 200) return res.data
+  if (res.status == 401) throw new AuthError()
+
+  const error = new Error('An error occurred while fetching the data.')
+  const info = res.data
+  const status = res.status
+  throw { info, status, ...error }
+}
+
+export function updateToken(token: string): void {
+  localStorage.setItem(TOKEN_KEY, token)
+  mutate('instances')
+}
+
+export function useBackendInstances(): 
HttpResponse<MerchantBackend.Instances.InstancesResponse> {
+  const { data, error } = 
useSWR<MerchantBackend.Instances.InstancesResponse>('instances', fetcher)
+
+  return { data, needsAuth: error instanceof AuthError, error }
+}
diff --git a/src/components/navbar/index.tsx b/src/components/navbar/index.tsx
index ab86cad..86e981b 100644
--- a/src/components/navbar/index.tsx
+++ b/src/components/navbar/index.tsx
@@ -17,7 +17,7 @@ export default function NavigationBar(): VNode {
         </a>
       </div>
       <div class="navbar-menu navbar-end">
-      <button class="button is-primary">Log out</button>
+        <button class="button is-primary">Log out</button>
       </div>
     </nav>
   );
diff --git a/src/declaration.d.ts b/src/declaration.d.ts
index 4b14502..bd31d17 100644
--- a/src/declaration.d.ts
+++ b/src/declaration.d.ts
@@ -1,4 +1,3 @@
-import StringRenderer from 
"enzyme-adapter-preact-pure/build/src/StringRenderer";
 
 declare module "*.css" {
     const mapping: Record<string, string>;
@@ -9,14 +8,416 @@ declare module "*.scss" {
     export default mapping;
 }
 
-namespace MerchantBackend {
-    type PrivateInstances = {
-        instances: Instance[];
-    };
-    type Instance = {
+type EddsaPublicKey = string;
+// type RelativeTime = Duration;
+interface Timestamp {
+    // Milliseconds since epoch, or the special
+    // value "forever" to represent an event that will
+    // never happen.
+    t_ms: number | "never";
+}
+interface Duration {
+    // Duration in milliseconds or "forever"
+    // to represent an infinite duration.
+    d_ms: number | "forever";
+}
+
+type Amount = string;
+type UUID = string;
+type Integer = number;
+
+export namespace MerchantBackend {
+    // Delivery location, loosely modeled as a subset of
+    // ISO20022's PostalAddress25.
+    interface Tax {
+        // the name of the tax
         name: string;
-        id: string;
-        merchant_pub: string;
-        payment_targets: string[];
+
+        // amount paid in tax
+        tax: Amount;
+    }
+
+    interface Location {
+        // Nation with its own government.
+        country?: string;
+
+        // Identifies a subdivision of a country such as state, region, county.
+        country_subdivision?: string;
+
+        // Identifies a subdivision within a country sub-division.
+        district?: string;
+
+        // Name of a built-up area, with defined boundaries, and a local 
government.
+        town?: string;
+
+        // Specific location name within the town.
+        town_location?: string;
+
+        // Identifier consisting of a group of letters and/or numbers that
+        // is added to a postal address to assist the sorting of mail.
+        post_code?: string;
+
+        // Name of a street or thoroughfare.
+        street?: string;
+
+        // Name of the building or house.
+        building_name?: string;
+
+        // Number that identifies the position of a building on a street.
+        building_number?: string;
+
+        // Free-form address lines, should not exceed 7 elements.
+        address_lines?: string[];
+    }
+    namespace Instances {
+
+        //POST /private/instances
+        interface InstanceConfigurationMessage {
+            // The URI where the wallet will send coins.  A merchant may have
+            // multiple accounts, thus this is an array.  Note that by
+            // removing URIs from this list the respective account is set to
+            // inactive and thus unavailable for new contracts, but preserved
+            // in the database as existing offers and contracts may still refer
+            // to it.
+            payto_uris: string[];
+
+            // Name of the merchant instance to create (will become $INSTANCE).
+            id: string;
+
+            // 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;
+
+            // The jurisdiction under which the merchant conducts its business
+            // (to be put into contracts).
+            jurisdiction: Location;
+
+            // Maximum wire fee this instance is willing to pay.
+            // Can be overridden by the frontend on a per-order basis.
+            default_max_wire_fee: Amount;
+
+            // Default factor for wire fee amortization calculations.
+            // Can be overridden by the frontend on a per-order basis.
+            default_wire_fee_amortization: Integer;
+
+            // Maximum deposit fee (sum over all coins) this instance is 
willing to pay.
+            // Can be overridden by the frontend on a per-order basis.
+            default_max_deposit_fee: Amount;
+
+            //  If the frontend does NOT specify an execution date, how long 
should
+            // we tell the exchange to wait to aggregate transactions before
+            // executing the wire transfer?  This delay is added to the current
+            // time when we generate the advisory execution time for the 
exchange.
+            default_wire_transfer_delay: RelativeTime;
+
+            // If the frontend does NOT specify a payment deadline, how long 
should
+            // offers we make be valid by default?
+            default_pay_delay: RelativeTime;
+
+        }
+
+        // PATCH /private/instances/$INSTANCE
+        interface InstanceReconfigurationMessage {
+            // The URI where the wallet will send coins.  A merchant may have
+            // multiple accounts, thus this is an array.  Note that by
+            // removing URIs from this list
+            payto_uris: string[];
+
+            // 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;
+
+            // The jurisdiction under which the merchant conducts its business
+            // (to be put into contracts).
+            jurisdiction: Location;
+
+            // Maximum wire fee this instance is willing to pay.
+            // Can be overridden by the frontend on a per-order basis.
+            default_max_wire_fee: Amount;
+
+            // Default factor for wire fee amortization calculations.
+            // Can be overridden by the frontend on a per-order basis.
+            default_wire_fee_amortization: Integer;
+
+            // Maximum deposit fee (sum over all coins) this instance is 
willing to pay.
+            // Can be overridden by the frontend on a per-order basis.
+            default_max_deposit_fee: Amount;
+
+            //  If the frontend does NOT specify an execution date, how long 
should
+            // we tell the exchange to wait to aggregate transactions before
+            // executing the wire transfer?  This delay is added to the current
+            // time when we generate the advisory execution time for the 
exchange.
+            default_wire_transfer_delay: RelativeTime;
+
+            // If the frontend does NOT specify a payment deadline, how long 
should
+            // offers we make be valid by default?
+            default_pay_delay: RelativeTime;
+
+        }
+
+        //   GET /private/instances
+        interface InstancesResponse {
+            // List of instances that are present in the backend (see Instance)
+            instances: Instance[];
+        }
+
+        interface Instance {
+            // Merchant name corresponding to this instance.
+            name: string;
+
+            // Merchant instance this response is about ($INSTANCE)
+            id: string;
+
+            // Public key of the merchant/instance, in Crockford Base32 
encoding.
+            merchant_pub: EddsaPublicKey;
+
+            // List of the payment targets supported by this instance. Clients 
can
+            // specify the desired payment target in /order requests.  Note 
that
+            // front-ends do not have to support wallets selecting payment 
targets.
+            payment_targets: string[];
+
+        }
+
+        //GET /private/instances/$INSTANCE
+        interface QueryInstancesResponse {
+            // The URI where the wallet will send coins.  A merchant may have
+            // multiple accounts, thus this is an array.
+            accounts: MerchantAccount[];
+
+            // Merchant name corresponding to this instance.
+            name: string;
+
+            // Public key of the merchant/instance, in Crockford Base32 
encoding.
+            merchant_pub: EddsaPublicKey;
+
+            // The merchant's physical address (to be put into contracts).
+            address: Location;
+
+            // The jurisdiction under which the merchant conducts its business
+            // (to be put into contracts).
+            jurisdiction: Location;
+
+            // Maximum wire fee this instance is willing to pay.
+            // Can be overridden by the frontend on a per-order basis.
+            default_max_wire_fee: Amount;
+
+            // Default factor for wire fee amortization calculations.
+            // Can be overridden by the frontend on a per-order basis.
+            default_wire_fee_amortization: Integer;
+
+            // Maximum deposit fee (sum over all coins) this instance is 
willing to pay.
+            // Can be overridden by the frontend on a per-order basis.
+            default_max_deposit_fee: Amount;
+
+            //  If the frontend does NOT specify an execution date, how long 
should
+            // we tell the exchange to wait to aggregate transactions before
+            // executing the wire transfer?  This delay is added to the current
+            // time when we generate the advisory execution time for the 
exchange.
+            default_wire_transfer_delay: RelativeTime;
+
+            // If the frontend does NOT specify a payment deadline, how long 
should
+            // offers we make be valid by default?
+            default_pay_deadline: RelativeTime;
+
+        }
+
+        interface MerchantAccount {
+
+            // payto:// URI of the account.
+            payto_uri: string;
+
+            // Hash over the wire details (including over the salt)
+            h_wire: HashCode;
+
+            // salt used to compute h_wire
+            salt: HashCode;
+
+            // true if this account is active,
+            // false if it is historic.
+            active: boolean;
+        }
+
+        //   DELETE /private/instances/$INSTANCE
+
+
+    }
+
+    namespace Inventory {
+        // POST /private/products
+        interface ProductAddDetail {
+
+            // product ID to use.
+            product_id: string;
+
+            // Human-readable product description.
+            description: string;
+
+            // Map from IETF BCP 47 language tags to localized descriptions
+            // eslint-disable-next-line @typescript-eslint/camelcase
+            description_i18n: { [lang_tag: string]: string };
+
+            // unit in which the product is measured (liters, kilograms, 
packages, etc.)
+            unit: string;
+
+            // The price for one unit of the product. Zero is used
+            // to imply that this product is not sold separately, or
+            // that the price is not fixed, and must be supplied by the
+            // front-end.  If non-zero, this price MUST include applicable
+            // taxes.
+            price: Amount;
+
+            // An optional base64-encoded product image
+            image: ImageDataUrl;
+
+            // a list of taxes paid by the merchant for one unit of this 
product
+            taxes: Tax[];
+
+            // Number of units of the product in stock in sum in total,
+            // including all existing sales ever. Given in product-specific
+            // units.
+            // A value of -1 indicates "infinite" (i.e. for "electronic" 
books).
+            total_stock: Integer;
+
+            // Identifies where the product is in stock.
+            address: Location;
+
+            // Identifies when we expect the next restocking to happen.
+            next_restock?: Timestamp;
+
+        }
+        //   PATCH /private/products/$PRODUCT_ID
+        interface ProductPatchDetail {
+
+            // Human-readable product description.
+            description: string;
+
+            // Map from IETF BCP 47 language tags to localized descriptions
+            // eslint-disable-next-line @typescript-eslint/camelcase
+            description_i18n: { [lang_tag: string]: string };
+
+            // unit in which the product is measured (liters, kilograms, 
packages, etc.)
+            unit: string;
+
+            // The price for one unit of the product. Zero is used
+            // to imply that this product is not sold separately, or
+            // that the price is not fixed, and must be supplied by the
+            // front-end.  If non-zero, this price MUST include applicable
+            // taxes.
+            price: Amount;
+
+            // An optional base64-encoded product image
+            image: ImageDataUrl;
+
+            // a list of taxes paid by the merchant for one unit of this 
product
+            taxes: Tax[];
+
+            // Number of units of the product in stock in sum in total,
+            // including all existing sales ever. Given in product-specific
+            // units.
+            // A value of -1 indicates "infinite" (i.e. for "electronic" 
books).
+            total_stock: Integer;
+
+            // Number of units of the product that were lost (spoiled, stolen, 
etc.)
+            total_lost: Integer;
+
+            // Identifies where the product is in stock.
+            address: Location;
+
+            // Identifies when we expect the next restocking to happen.
+            next_restock?: Timestamp;
+
+        }
+
+        // GET /private/products
+        interface InventorySummaryResponse {
+            // List of products that are present in the inventory
+            products: InventoryEntry[];
+        }
+        interface InventoryEntry {
+            // Product identifier, as found in the product.
+            product_id: string;
+
+        }
+
+        // GET /private/products/$PRODUCT_ID
+        interface ProductDetail {
+
+            // Human-readable product description.
+            description: string;
+
+            // Map from IETF BCP 47 language tags to localized descriptions
+            // eslint-disable-next-line @typescript-eslint/camelcase
+            description_i18n: { [lang_tag: string]: string };
+
+            // unit in which the product is measured (liters, kilograms, 
packages, etc.)
+            unit: string;
+
+            // The price for one unit of the product. Zero is used
+            // to imply that this product is not sold separately, or
+            // that the price is not fixed, and must be supplied by the
+            // front-end.  If non-zero, this price MUST include applicable
+            // taxes.
+            price: Amount;
+
+            // An optional base64-encoded product image
+            image: ImageDataUrl;
+
+            // a list of taxes paid by the merchant for one unit of this 
product
+            taxes: Tax[];
+
+            // Number of units of the product in stock in sum in total,
+            // including all existing sales ever. Given in product-specific
+            // units.
+            // A value of -1 indicates "infinite" (i.e. for "electronic" 
books).
+            total_stock: Integer;
+
+            // Number of units of the product that have already been sold.
+            total_sold: Integer;
+
+            // Number of units of the product that were lost (spoiled, stolen, 
etc.)
+            total_lost: Integer;
+
+            // Identifies where the product is in stock.
+            address: Location;
+
+            // Identifies when we expect the next restocking to happen.
+            next_restock?: Timestamp;
+
+        }
+
+        // POST /private/products/$PRODUCT_ID/lock
+        interface LockRequest {
+
+            // UUID that identifies the frontend performing the lock
+            // It is suggested that clients use a timeflake for this,
+            // see https://github.com/anthonynsimon/timeflake
+            lock_uuid: UUID;
+
+            // How long does the frontend intend to hold the lock
+            duration: RelativeTime;
+
+            // How many units should be locked?
+            quantity: Integer;
+
+        }
+
+        //   DELETE /private/products/$PRODUCT_ID
+
     }
-}
\ No newline at end of file
+}
diff --git a/src/routes/index.tsx b/src/routes/index.tsx
index 723fc13..1543a1a 100644
--- a/src/routes/index.tsx
+++ b/src/routes/index.tsx
@@ -3,13 +3,11 @@ import { route, Route, Router } from 'preact-router';
 
 import NotFoundPage from './notfound';
 import Instances from './instances';
-import Dash from './dashboard';
-import BulmaForms from './forms';
-import BulmaProfile from './profile';
 import Footer from '../components/footer';
 import Sidebar from '../components/sidebar';
 import NavigationBar from '../components/navbar';
 import { useEffect } from 'preact/hooks';
+import InstanceDetail from './instanceDetail';
 
 function Redirector({ to }: { path: string; to: string }): null {
   useEffect(() => {
@@ -24,11 +22,8 @@ export default function PageRouter(): VNode {
       <NavigationBar />
       <Sidebar />
       <Router>
-        <Redirector path="/" to="/instances" />
-        <Route path="/instances" component={Instances} />
-        <Route path="/dashboard" component={Dash} />
-        <Route path="/forms" component={BulmaForms} />
-        <Route path="/profile" component={BulmaProfile} />
+        <Route path="/" component={Instances} />
+        <Route path="/i/:instance" component={InstanceDetail} />
         <NotFoundPage default />
       </Router>
       <Footer />
diff --git a/src/routes/instanceDetail/index.tsx 
b/src/routes/instanceDetail/index.tsx
new file mode 100644
index 0000000..e80a65a
--- /dev/null
+++ b/src/routes/instanceDetail/index.tsx
@@ -0,0 +1,6 @@
+import { h, VNode } from 'preact';
+
+
+export default function InstanceDetail({ instance }: any): VNode {
+  return <div>hola {instance}</div>
+}
\ No newline at end of file
diff --git a/src/routes/instances/table.tsx b/src/routes/instances/Table.tsx
similarity index 93%
rename from src/routes/instances/table.tsx
rename to src/routes/instances/Table.tsx
index f9b16e3..c3a0572 100644
--- a/src/routes/instances/table.tsx
+++ b/src/routes/instances/Table.tsx
@@ -2,10 +2,10 @@ import { h, VNode } from "preact";
 import { MerchantBackend } from "../../declaration";
 
 interface Props {
-  instances: MerchantBackend.Instance[];
+  instances: MerchantBackend.Instances.Instance[];
 }
 
-export default function Table({instances}:Props): VNode {
+export default function Table({ instances }: Props): VNode {
   return <div class="b-table has-pagination">
     <div class="table-wrapper has-mobile-cards">
       <table class="table is-fullwidth is-striped is-hoverable is-fullwidth">
@@ -18,8 +18,8 @@ export default function Table({instances}:Props): VNode {
               </label>
             </th>
             <th />
-            <th>Name</th>
-            <th>Company</th>
+            <th>id</th>
+            <th>name</th>
             <th>City</th>
             <th>Progress</th>
             <th>Created</th>
@@ -27,7 +27,7 @@ export default function Table({instances}:Props): VNode {
           </tr>
         </thead>
         <tbody>
-          { instances.map( i => {
+          {instances.map(i => {
             return <tr>
               <td class="is-checkbox-cell">
                 <label class="b-checkbox checkbox">
diff --git a/src/routes/instances/View.stories.tsx 
b/src/routes/instances/View.stories.tsx
new file mode 100644
index 0000000..a508298
--- /dev/null
+++ b/src/routes/instances/View.stories.tsx
@@ -0,0 +1,35 @@
+/* eslint-disable @typescript-eslint/camelcase */
+import { h } from 'preact';
+import View from './View'
+
+
+export default {
+  title: 'Instances/View',
+  component: View,
+  argTypes: {
+    onLogin: { action: 'onLogin' },
+    onLogout: { action: 'onLogout' },
+    onCreateAccount: { action: 'onCreateAccount' },
+  },
+};
+
+export const Empty = () => <View instances={[]} />
+
+export const WithDefaultInstance = () => <View instances={[{
+  id: 'default',
+  name: 'the default instance',
+  merchant_pub: 'abcdef',
+  payment_targets: []
+}]} />
+
+export const WithTwoInstance = () => <View instances={[{
+  id: 'first',
+  name: 'the first instance',
+  merchant_pub: 'abcdefgh',
+  payment_targets: []
+},{
+  id: 'second',
+  name: 'other instance',
+  merchant_pub: 'zxcvvbnm',
+  payment_targets: ['pay-to']
+}]} />
diff --git a/src/routes/instances/View.tsx b/src/routes/instances/View.tsx
new file mode 100644
index 0000000..b209726
--- /dev/null
+++ b/src/routes/instances/View.tsx
@@ -0,0 +1,74 @@
+import { h, VNode } from "preact";
+import { MerchantBackend } from "../../declaration";
+import Table from './Table';
+
+interface Props {
+  instances: MerchantBackend.Instances.Instance[];
+}
+
+export default function View({ instances }: 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>Merchant</li>
+              <li>Instances</li>
+            </ul>
+          </div>
+        </div>
+      </div>
+    </section>
+    <section class="hero is-hero-bar">
+      <div class="hero-body">
+        <div class="level">
+          <div class="level-left">
+            <div class="level-item">
+              <h1 class="title">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">
+      <div class="card has-table">
+        <header class="card-header">
+          <p class="card-header-title">
+            <span class="icon"><i class="mdi mdi-account-multiple" /></span>
+          Instances
+        </p>
+          <a href="#" class="card-header-icon">
+            <span class="icon"><i class="mdi mdi-reload" /></span>
+          </a>
+        </header>
+        <div class="card-content">
+          <Table instances={instances} />
+        </div>
+      </div>
+
+    </section>
+
+    <div id="sample-modal" class="modal">
+      <div class="modal-background jb-modal-close" />
+      <div class="modal-card">
+        <header class="modal-card-head">
+          <p class="modal-card-title">Confirm action</p>
+          <button class="delete jb-modal-close" aria-label="close" />
+        </header>
+        <section class="modal-card-body">
+          <p>This will permanently delete <b>Some Object</b></p>
+          <p>This is sample modal</p>
+        </section>
+        <footer class="modal-card-foot">
+          <button class="button jb-modal-close">Cancel</button>
+          <button class="button is-danger jb-modal-close">Delete</button>
+        </footer>
+      </div>
+      <button class="modal-close is-large jb-modal-close" aria-label="close" />
+    </div>
+  </div>
+}
\ No newline at end of file
diff --git a/src/routes/instances/index.tsx b/src/routes/instances/index.tsx
index 2ad4272..0aeef3e 100644
--- a/src/routes/instances/index.tsx
+++ b/src/routes/instances/index.tsx
@@ -1,130 +1,15 @@
 import { h, VNode } from 'preact';
-import { useState } from 'preact/hooks';
-import useSWR, { mutate } from 'swr';
-import Table from './table';
-import { MerchantBackend } from '../../declaration';
-import axios from 'axios'
+import View from './View';
+import LoginPage from '../../components/auth/LoginPage';
+import { updateToken, useBackendInstances } from 
'../../components/hooks/backend';
 
-class AuthError extends Error {
-  public readonly isAuth = true
-}
 
-const BACKEND = 'http://localhost:9966'
-
-async function fetcher(url: string) {
-  const token = localStorage.getItem('backend-token')
-  const headers = token ? { Authorization: `Bearer secret-token:${token}` } : 
undefined
-
-  const res = await axios.get(`${BACKEND}/private/${url}`, {headers})
-  if (res.status == 200) return res.data
-  if (res.status == 401) throw new AuthError()
-
-  const error = new Error('An error occurred while fetching the data.')
-  const info = res.data
-  const status = res.status
-  throw { info, status, ...error }
-}
-
-function updateToken(token: string): void {
-  localStorage.setItem('backend-token', token)
-  mutate('instances')
-}
-
-type HttpResponse<T> = HttpResponseOk<T> | HttpResponseError<T>;
-
-interface HttpResponseOk<T> {
-  data: T;
-}
-interface HttpResponseError<T> {
-  data: undefined;
-  needsAuth: boolean;
-  error: Error;
-}
-
-
-function useBackendInstances(): HttpResponse<MerchantBackend.PrivateInstances> 
{
-  const { data, error } = 
useSWR<MerchantBackend.PrivateInstances>('instances', fetcher)
-  return { data, needsAuth: error instanceof AuthError, error }
-}
-
-function TokenModal(): VNode {
-  const [token, update] = useState('')
-
-  return <div>
-    <input value={token} onInput={e => update(e?.currentTarget.value)} />
-    <button onClick={(): void => updateToken(token)}>set</button>
-  </div>
-}
-
-export default function BulmaTable({}): VNode {
+export default function Instances(): VNode {
   const resp = useBackendInstances()
 
   if (!resp.data) {
-    return <TokenModal />
+    return <LoginPage onLogIn={updateToken} />
   }
 
-  return <div id="app">
-    <section class="section is-title-bar">
-      <div class="level">
-        <div class="level-left">
-          <div class="level-item">
-            <ul>
-              <li>Merchant</li>
-              <li>Instances</li>
-            </ul>
-          </div>
-        </div>
-      </div>
-    </section>
-    <section class="hero is-hero-bar">
-      <div class="hero-body">
-        <div class="level">
-          <div class="level-left">
-            <div class="level-item"><h1 class="title">
-              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">
-      <div class="card has-table">
-        <header class="card-header">
-          <p class="card-header-title">
-            <span class="icon"><i class="mdi mdi-account-multiple" /></span>
-            Instances
-          </p>
-          <a href="#" class="card-header-icon">
-            <span class="icon"><i class="mdi mdi-reload" /></span>
-          </a>
-        </header>
-        <div class="card-content">
-          <Table instances={resp.data.instances} />
-        </div>
-      </div>
-
-    </section>
-
-    <div id="sample-modal" class="modal">
-      <div class="modal-background jb-modal-close" />
-      <div class="modal-card">
-        <header class="modal-card-head">
-          <p class="modal-card-title">Confirm action</p>
-          <button class="delete jb-modal-close" aria-label="close" />
-        </header>
-        <section class="modal-card-body">
-          <p>This will permanently delete <b>Some Object</b></p>
-          <p>This is sample modal</p>
-        </section>
-        <footer class="modal-card-foot">
-          <button class="button jb-modal-close">Cancel</button>
-          <button class="button is-danger jb-modal-close">Delete</button>
-        </footer>
-      </div>
-      <button class="modal-close is-large jb-modal-close" aria-label="close" />
-    </div>
-  </div>;
+  return <View instances={resp.data.instances} />;
 }
\ 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]