[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.
- [taler-merchant-backoffice] branch master updated (379e8f2 -> e1bc057), gnunet, 2021/02/08
- [taler-merchant-backoffice] 04/06: missing babelrc for storybook, gnunet, 2021/02/08
- [taler-merchant-backoffice] 01/06: added icons, gnunet, 2021/02/08
- [taler-merchant-backoffice] 02/06: removing stories directory, gnunet, 2021/02/08
- [taler-merchant-backoffice] 03/06: configuring storybook with styling, gnunet, 2021/02/08
- [taler-merchant-backoffice] 05/06: removed until needed, gnunet, 2021/02/08
- [taler-merchant-backoffice] 06/06: working on instances listing,
gnunet <=