[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[taler-merchant-backoffice] branch master updated: simple login, but int
From: |
gnunet |
Subject: |
[taler-merchant-backoffice] branch master updated: simple login, but integrated with merchant backend for create update and delete |
Date: |
Wed, 10 Feb 2021 20:02:18 +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 df19187 simple login, but integrated with merchant backend for create
update and delete
df19187 is described below
commit df1918755c0af900c6c447f1d4e0ef27174fb26e
Author: Sebastian <sebasjm@gmail.com>
AuthorDate: Wed Feb 10 16:02:06 2021 -0300
simple login, but integrated with merchant backend for create update and
delete
---
.gitignore | 1 +
.storybook/main.js | 4 +-
.storybook/preview.js | 1 +
package.json | 11 +-
src/components/hooks/backend.ts | 63 +++++++--
src/components/navbar/index.tsx | 2 +-
src/declaration.d.ts | 7 +-
src/routes/dashboard/index.tsx | 120 ++++++++--------
src/routes/instances/ConfirmModal.tsx | 30 ++++
src/routes/instances/CreateModal.tsx | 86 ++++++++++++
src/routes/instances/DeleteAllModal.tsx | 29 ++++
src/routes/instances/DeleteModal.tsx | 16 +++
src/routes/instances/Table.tsx | 196 ++++++++++++++++-----------
src/routes/instances/UpdateModal.stories.tsx | 24 ++++
src/routes/instances/UpdateModal.tsx | 76 +++++++++++
src/routes/instances/View.stories.tsx | 45 +++---
src/routes/instances/View.tsx | 71 +++++-----
src/routes/instances/index.tsx | 17 ++-
src/scss/main.scss | 23 ++++
yarn.lock | 165 ++++++++++++++++++++--
20 files changed, 768 insertions(+), 219 deletions(-)
diff --git a/.gitignore b/.gitignore
index a62a00c..44a3358 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
/build
/node_modules
/size-plugin.json
+/storybook-static
diff --git a/.storybook/main.js b/.storybook/main.js
index b9dea09..b2695b1 100644
--- a/.storybook/main.js
+++ b/.storybook/main.js
@@ -5,7 +5,7 @@ module.exports = {
],
"addons": [
"@storybook/preset-scss",
- "@storybook/addon-links",
- "@storybook/addon-essentials"
+ // "@storybook/addon-a11y",
+ "@storybook/addon-essentials" //docs, control, actions, viewpot, toolbar,
background
]
}
\ No newline at end of file
diff --git a/.storybook/preview.js b/.storybook/preview.js
index 15bd64e..ea8be75 100644
--- a/.storybook/preview.js
+++ b/.storybook/preview.js
@@ -1,5 +1,6 @@
import "../src/scss/main.scss"
export const parameters = {
+ controls: { expanded: true },
actions: { argTypesRegex: "^on[A-Z].*" },
}
\ No newline at end of file
diff --git a/package.json b/package.json
index 87f2b37..d518ce6 100644
--- a/package.json
+++ b/package.json
@@ -20,17 +20,24 @@
],
"ignorePatterns": [
"build/"
- ]
+ ],
+ "rules": {
+ "@typescript-eslint/camelcase": [
+ "off"
+ ]
+ }
},
"dependencies": {
"axios": "^0.21.1",
"preact": "^10.3.1",
"preact-router": "^3.2.1",
- "swr": "^0.4.1"
+ "swr": "^0.4.1",
+ "yup": "^0.32.8"
},
"devDependencies": {
"@babel/core": "^7.12.13",
"@babel/plugin-transform-react-jsx-source": "^7.12.13",
+ "@storybook/addon-a11y": "^6.1.17",
"@storybook/addon-actions": "^6.1.16",
"@storybook/addon-essentials": "^6.1.16",
"@storybook/addon-links": "^6.1.16",
diff --git a/src/components/hooks/backend.ts b/src/components/hooks/backend.ts
index ba322f0..5f32692 100644
--- a/src/components/hooks/backend.ts
+++ b/src/components/hooks/backend.ts
@@ -1,4 +1,4 @@
-import useSWR, { mutate } from 'swr';
+import useSWR, { mutate as globalMutate } from 'swr';
import axios from 'axios'
import { MerchantBackend } from '../../declaration';
@@ -20,13 +20,20 @@ class AuthError extends Error {
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> {
+type Methods = 'get' | 'post' | 'patch' | 'delete' | 'put';
+
+async function request(url: string, method?: Methods, data?: object):
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
+ const res = await axios({
+ method: method || 'get',
+ url: `${BACKEND}/private${url}`,
+ responseType: 'json',
+ headers,
+ data
+ })
+ if (res.status == 200 || res.status == 204) return res.data
if (res.status == 401) throw new AuthError()
const error = new Error('An error occurred while fetching the data.')
@@ -35,13 +42,51 @@ async function fetcher(url: string): Promise<any> {
throw { info, status, ...error }
}
+async function fetcher(url: string): Promise<any> {
+ return request(url, 'get')
+}
+
export function updateToken(token: string): void {
localStorage.setItem(TOKEN_KEY, token)
- mutate('instances')
+ globalMutate('instances')
+}
+
+interface WithCreate<T> {
+ create: (data: T) => Promise<void>;
+}
+interface WithUpdate<T> {
+ update: (id: string, data: T) => Promise<void>;
+}
+interface WithDelete {
+ delete: (id: string) => Promise<void>;
}
-export function useBackendInstances():
HttpResponse<MerchantBackend.Instances.InstancesResponse> {
- const { data, error } =
useSWR<MerchantBackend.Instances.InstancesResponse>('instances', fetcher)
+export function useBackendInstances():
HttpResponse<MerchantBackend.Instances.InstancesResponse> &
WithCreate<MerchantBackend.Instances.InstanceConfigurationMessage> {
+ const { data, error, mutate } =
useSWR<MerchantBackend.Instances.InstancesResponse>('/instances', fetcher)
+
+ const create = async (instance:
MerchantBackend.Instances.InstanceConfigurationMessage) => {
+ await request('/instances', 'post', instance)
+
+ globalMutate('/instances')
+ }
+
+ return { data, needsAuth: error instanceof AuthError, error, create }
+}
+
+export function useBackendInstance(id: string | null):
HttpResponse<MerchantBackend.Instances.QueryInstancesResponse> &
WithUpdate<MerchantBackend.Instances.InstanceReconfigurationMessage> &
WithDelete {
+ const { data, error } =
useSWR<MerchantBackend.Instances.QueryInstancesResponse>(id ?
`/instances/${id}` : null, fetcher)
+
+ const update = async (updateId: string, instance:
MerchantBackend.Instances.InstanceReconfigurationMessage) => {
+ await request(`/instances/${updateId}`, 'patch', instance)
+
+ globalMutate(`/instances/${updateId}`, null)
+ };
+ const _delete = async (deleteId: string) => {
+ await request(`/instances/${deleteId}`, 'delete')
+
+ globalMutate('/instances')
+ globalMutate(`/instances/${deleteId}`, null)
+ }
- return { data, needsAuth: error instanceof AuthError, error }
+ return { data, needsAuth: error instanceof AuthError, error, update, delete:
_delete }
}
diff --git a/src/components/navbar/index.tsx b/src/components/navbar/index.tsx
index 86e981b..00b093d 100644
--- a/src/components/navbar/index.tsx
+++ b/src/components/navbar/index.tsx
@@ -12,7 +12,7 @@ export default function NavigationBar(): VNode {
</div>
</div>
<div class="navbar-brand is-right">
- <a class="navbar-item is-hidden-desktop jb-navbar-menu-toggle"
data-target="navbar-menu">
+ <a class="navbar-item is-hidden-desktop jb-navbar-menu-toggle">
<span class="icon"><i class="mdi mdi-dots-vertical" /></span>
</a>
</div>
diff --git a/src/declaration.d.ts b/src/declaration.d.ts
index bd31d17..7b83773 100644
--- a/src/declaration.d.ts
+++ b/src/declaration.d.ts
@@ -1,3 +1,4 @@
+import { number } from "yup/lib/locale";
declare module "*.css" {
const mapping: Record<string, string>;
@@ -9,7 +10,7 @@ declare module "*.scss" {
}
type EddsaPublicKey = string;
-// type RelativeTime = Duration;
+type RelativeTime = Duration;
interface Timestamp {
// Milliseconds since epoch, or the special
// value "forever" to represent an event that will
@@ -22,6 +23,10 @@ interface Duration {
d_ms: number | "forever";
}
+interface WidthId {
+ id: string;
+}
+
type Amount = string;
type UUID = string;
type Integer = number;
diff --git a/src/routes/dashboard/index.tsx b/src/routes/dashboard/index.tsx
index 04b86ae..6a0590d 100644
--- a/src/routes/dashboard/index.tsx
+++ b/src/routes/dashboard/index.tsx
@@ -155,13 +155,13 @@ export default function BulmaIndex({}): VNode {
<img
src="https://avatars.dicebear.com/4.5/api/male/rebecca-bauch.svg"
class="is-rounded" />
</div>
</td>
- <td data-label="Name">Rebecca Bauch</td>
- <td data-label="Company">Daugherty-Daniel</td>
- <td data-label="City">South Cory</td>
- <td data-label="Progress" class="is-progress-cell">
+ <td>Rebecca Bauch</td>
+ <td>Daugherty-Daniel</td>
+ <td>South Cory</td>
+ <td class="is-progress-cell">
<progress max="100" class="progress is-small is-primary"
value="79">79</progress>
</td>
- <td data-label="Created">
+ <td>
<small class="has-text-grey is-abbr-like" title="Oct 25,
2020">Oct 25, 2020</small>
</td>
<td class="is-actions-cell">
@@ -169,7 +169,7 @@ export default function BulmaIndex({}): VNode {
<button class="button is-small is-primary" type="button">
<span class="icon"><i class="mdi mdi-eye" /></span>
</button>
- <button class="button is-small is-danger jb-modal"
data-target="sample-modal" type="button">
+ <button class="button is-small is-danger jb-modal"
type="button">
<span class="icon"><i class="mdi mdi-trash-can" /></span>
</button>
</div>
@@ -181,13 +181,13 @@ export default function BulmaIndex({}): VNode {
<img
src="https://avatars.dicebear.com/4.5/api/male/felicita-yundt.svg"
class="is-rounded" />
</div>
</td>
- <td data-label="Name">Felicita Yundt</td>
- <td data-label="Company">Johns-Weissnat</td>
- <td data-label="City">East Ariel</td>
- <td data-label="Progress" class="is-progress-cell">
+ <td>Felicita Yundt</td>
+ <td>Johns-Weissnat</td>
+ <td>East Ariel</td>
+ <td class="is-progress-cell">
<progress max="100" class="progress is-small is-primary"
value="67">67</progress>
</td>
- <td data-label="Created">
+ <td>
<small class="has-text-grey is-abbr-like" title="Jan 8,
2020">Jan 8, 2020</small>
</td>
<td class="is-actions-cell">
@@ -195,7 +195,7 @@ export default function BulmaIndex({}): VNode {
<button class="button is-small is-primary" type="button">
<span class="icon"><i class="mdi mdi-eye" /></span>
</button>
- <button class="button is-small is-danger jb-modal"
data-target="sample-modal" type="button">
+ <button class="button is-small is-danger jb-modal"
type="button">
<span class="icon"><i class="mdi mdi-trash-can" /></span>
</button>
</div>
@@ -207,13 +207,13 @@ export default function BulmaIndex({}): VNode {
<img
src="https://avatars.dicebear.com/4.5/api/male/mr-larry-satterfield-v.svg"
class="is-rounded" />
</div>
</td>
- <td data-label="Name">Mr. Larry Satterfield V</td>
- <td data-label="Company">Hyatt Ltd</td>
- <td data-label="City">Windlerburgh</td>
- <td data-label="Progress" class="is-progress-cell">
+ <td>Mr. Larry Satterfield V</td>
+ <td>Hyatt Ltd</td>
+ <td>Windlerburgh</td>
+ <td class="is-progress-cell">
<progress max="100" class="progress is-small is-primary"
value="16">16</progress>
</td>
- <td data-label="Created">
+ <td>
<small class="has-text-grey is-abbr-like" title="Dec 18,
2020">Dec 18, 2020</small>
</td>
<td class="is-actions-cell">
@@ -221,7 +221,7 @@ export default function BulmaIndex({}): VNode {
<button class="button is-small is-primary" type="button">
<span class="icon"><i class="mdi mdi-eye" /></span>
</button>
- <button class="button is-small is-danger jb-modal"
data-target="sample-modal" type="button">
+ <button class="button is-small is-danger jb-modal"
type="button">
<span class="icon"><i class="mdi mdi-trash-can" /></span>
</button>
</div>
@@ -233,13 +233,13 @@ export default function BulmaIndex({}): VNode {
<img
src="https://avatars.dicebear.com/4.5/api/male/mr-broderick-kub.svg"
class="is-rounded" />
</div>
</td>
- <td data-label="Name">Mr. Broderick Kub</td>
- <td data-label="Company">Kshlerin, Bauch and Ernser</td>
- <td data-label="City">New Kirstenport</td>
- <td data-label="Progress" class="is-progress-cell">
+ <td>Mr. Broderick Kub</td>
+ <td>Kshlerin, Bauch and Ernser</td>
+ <td>New Kirstenport</td>
+ <td class="is-progress-cell">
<progress max="100" class="progress is-small is-primary"
value="71">71</progress>
</td>
- <td data-label="Created">
+ <td>
<small class="has-text-grey is-abbr-like" title="Sep 13,
2020">Sep 13, 2020</small>
</td>
<td class="is-actions-cell">
@@ -247,7 +247,7 @@ export default function BulmaIndex({}): VNode {
<button class="button is-small is-primary" type="button">
<span class="icon"><i class="mdi mdi-eye" /></span>
</button>
- <button class="button is-small is-danger jb-modal"
data-target="sample-modal" type="button">
+ <button class="button is-small is-danger jb-modal"
type="button">
<span class="icon"><i class="mdi mdi-trash-can" /></span>
</button>
</div>
@@ -259,13 +259,13 @@ export default function BulmaIndex({}): VNode {
<img
src="https://avatars.dicebear.com/4.5/api/male/barry-weber.svg"
class="is-rounded" />
</div>
</td>
- <td data-label="Name">Barry Weber</td>
- <td data-label="Company">Schulist, Mosciski and
Heidenreich</td>
- <td data-label="City">East Violettestad</td>
- <td data-label="Progress" class="is-progress-cell">
+ <td>Barry Weber</td>
+ <td>Schulist, Mosciski and Heidenreich</td>
+ <td>East Violettestad</td>
+ <td class="is-progress-cell">
<progress max="100" class="progress is-small is-primary"
value="80">80</progress>
</td>
- <td data-label="Created">
+ <td>
<small class="has-text-grey is-abbr-like" title="Jul 24,
2020">Jul 24, 2020</small>
</td>
<td class="is-actions-cell">
@@ -273,7 +273,7 @@ export default function BulmaIndex({}): VNode {
<button class="button is-small is-primary" type="button">
<span class="icon"><i class="mdi mdi-eye" /></span>
</button>
- <button class="button is-small is-danger jb-modal"
data-target="sample-modal" type="button">
+ <button class="button is-small is-danger jb-modal"
type="button">
<span class="icon"><i class="mdi mdi-trash-can" /></span>
</button>
</div>
@@ -285,13 +285,13 @@ export default function BulmaIndex({}): VNode {
<img
src="https://avatars.dicebear.com/4.5/api/male/bert-kautzer-md.svg"
class="is-rounded" />
</div>
</td>
- <td data-label="Name">Bert Kautzer MD</td>
- <td data-label="Company">Gerhold and Sons</td>
- <td data-label="City">Mayeport</td>
- <td data-label="Progress" class="is-progress-cell">
+ <td>Bert Kautzer MD</td>
+ <td>Gerhold and Sons</td>
+ <td>Mayeport</td>
+ <td class="is-progress-cell">
<progress max="100" class="progress is-small is-primary"
value="62">62</progress>
</td>
- <td data-label="Created">
+ <td>
<small class="has-text-grey is-abbr-like" title="Mar 30,
2020">Mar 30, 2020</small>
</td>
<td class="is-actions-cell">
@@ -299,7 +299,7 @@ export default function BulmaIndex({}): VNode {
<button class="button is-small is-primary" type="button">
<span class="icon"><i class="mdi mdi-eye" /></span>
</button>
- <button class="button is-small is-danger jb-modal"
data-target="sample-modal" type="button">
+ <button class="button is-small is-danger jb-modal"
type="button">
<span class="icon"><i class="mdi mdi-trash-can" /></span>
</button>
</div>
@@ -311,13 +311,13 @@ export default function BulmaIndex({}): VNode {
<img
src="https://avatars.dicebear.com/4.5/api/male/lonzo-steuber.svg"
class="is-rounded" />
</div>
</td>
- <td data-label="Name">Lonzo Steuber</td>
- <td data-label="Company">Skiles Ltd</td>
- <td data-label="City">Marilouville</td>
- <td data-label="Progress" class="is-progress-cell">
+ <td>Lonzo Steuber</td>
+ <td>Skiles Ltd</td>
+ <td>Marilouville</td>
+ <td class="is-progress-cell">
<progress max="100" class="progress is-small is-primary"
value="17">17</progress>
</td>
- <td data-label="Created">
+ <td>
<small class="has-text-grey is-abbr-like" title="Feb 12,
2020">Feb 12, 2020</small>
</td>
<td class="is-actions-cell">
@@ -325,7 +325,7 @@ export default function BulmaIndex({}): VNode {
<button class="button is-small is-primary" type="button">
<span class="icon"><i class="mdi mdi-eye" /></span>
</button>
- <button class="button is-small is-danger jb-modal"
data-target="sample-modal" type="button">
+ <button class="button is-small is-danger jb-modal"
type="button">
<span class="icon"><i class="mdi mdi-trash-can" /></span>
</button>
</div>
@@ -337,13 +337,13 @@ export default function BulmaIndex({}): VNode {
<img
src="https://avatars.dicebear.com/4.5/api/male/jonathon-hahn.svg"
class="is-rounded" />
</div>
</td>
- <td data-label="Name">Jonathon Hahn</td>
- <td data-label="Company">Flatley Ltd</td>
- <td data-label="City">Billiemouth</td>
- <td data-label="Progress" class="is-progress-cell">
+ <td>Jonathon Hahn</td>
+ <td>Flatley Ltd</td>
+ <td>Billiemouth</td>
+ <td class="is-progress-cell">
<progress max="100" class="progress is-small is-primary"
value="74">74</progress>
</td>
- <td data-label="Created">
+ <td>
<small class="has-text-grey is-abbr-like" title="Dec 30,
2020">Dec 30, 2020</small>
</td>
<td class="is-actions-cell">
@@ -351,7 +351,7 @@ export default function BulmaIndex({}): VNode {
<button class="button is-small is-primary" type="button">
<span class="icon"><i class="mdi mdi-eye" /></span>
</button>
- <button class="button is-small is-danger jb-modal"
data-target="sample-modal" type="button">
+ <button class="button is-small is-danger jb-modal"
type="button">
<span class="icon"><i class="mdi mdi-trash-can" /></span>
</button>
</div>
@@ -363,13 +363,13 @@ export default function BulmaIndex({}): VNode {
<img
src="https://avatars.dicebear.com/4.5/api/male/ryley-wuckert.svg"
class="is-rounded" />
</div>
</td>
- <td data-label="Name">Ryley Wuckert</td>
- <td data-label="Company">Heller-Little</td>
- <td data-label="City">Emeraldtown</td>
- <td data-label="Progress" class="is-progress-cell">
+ <td>Ryley Wuckert</td>
+ <td>Heller-Little</td>
+ <td>Emeraldtown</td>
+ <td class="is-progress-cell">
<progress max="100" class="progress is-small is-primary"
value="54">54</progress>
</td>
- <td data-label="Created">
+ <td>
<small class="has-text-grey is-abbr-like" title="Jun 28,
2020">Jun 28, 2020</small>
</td>
<td class="is-actions-cell">
@@ -377,7 +377,7 @@ export default function BulmaIndex({}): VNode {
<button class="button is-small is-primary" type="button">
<span class="icon"><i class="mdi mdi-eye" /></span>
</button>
- <button class="button is-small is-danger jb-modal"
data-target="sample-modal" type="button">
+ <button class="button is-small is-danger jb-modal"
type="button">
<span class="icon"><i class="mdi mdi-trash-can" /></span>
</button>
</div>
@@ -389,13 +389,13 @@ export default function BulmaIndex({}): VNode {
<img
src="https://avatars.dicebear.com/4.5/api/male/sienna-hayes.svg"
class="is-rounded" />
</div>
</td>
- <td data-label="Name">Sienna Hayes</td>
- <td data-label="Company">Conn, Jerde and Douglas</td>
- <td data-label="City">Jonathanfort</td>
- <td data-label="Progress" class="is-progress-cell">
+ <td>Sienna Hayes</td>
+ <td>Conn, Jerde and Douglas</td>
+ <td>Jonathanfort</td>
+ <td class="is-progress-cell">
<progress max="100" class="progress is-small is-primary"
value="55">55</progress>
</td>
- <td data-label="Created">
+ <td>
<small class="has-text-grey is-abbr-like" title="Mar 7,
2020">Mar 7, 2020</small>
</td>
<td class="is-actions-cell">
@@ -403,7 +403,7 @@ export default function BulmaIndex({}): VNode {
<button class="button is-small is-primary" type="button">
<span class="icon"><i class="mdi mdi-eye" /></span>
</button>
- <button class="button is-small is-danger jb-modal"
data-target="sample-modal" type="button">
+ <button class="button is-small is-danger jb-modal"
type="button">
<span class="icon"><i class="mdi mdi-trash-can" /></span>
</button>
</div>
diff --git a/src/routes/instances/ConfirmModal.tsx
b/src/routes/instances/ConfirmModal.tsx
new file mode 100644
index 0000000..a074a60
--- /dev/null
+++ b/src/routes/instances/ConfirmModal.tsx
@@ -0,0 +1,30 @@
+import { h, VNode } from "preact";
+
+interface Props {
+ active?: boolean;
+ description?: string;
+ onCancel?: () => void;
+ onConfirm?: () => void;
+ children?: VNode[];
+ danger?: boolean;
+}
+
+export default function ConfirmModal({ active, description, onCancel,
onConfirm, children, danger }: Props): VNode {
+ return <div class={active ? "modal is-active is-clipped" : "modal"}>
+ <div class="modal-background jb-modal-close" onClick={onCancel} />
+ <div class="modal-card">
+ <header class="modal-card-head">
+ <p class="modal-card-title">Confirm action: {description}</p>
+ <button class="delete jb-modal-close" aria-label="close"
onClick={onCancel} />
+ </header>
+ <section class="modal-card-body">
+ {children}
+ </section>
+ <footer class="modal-card-foot">
+ <button class="button jb-modal-close" onClick={onCancel}
>Cancel</button>
+ <button class={danger ? "button is-danger jb-modal-close" : "button
is-info jb-modal-close"} onClick={onConfirm} >Confirm</button>
+ </footer>
+ </div>
+ <button class="modal-close is-large jb-modal-close" aria-label="close"
onClick={onCancel} />
+ </div>
+}
\ No newline at end of file
diff --git a/src/routes/instances/CreateModal.tsx
b/src/routes/instances/CreateModal.tsx
new file mode 100644
index 0000000..d22ae7d
--- /dev/null
+++ b/src/routes/instances/CreateModal.tsx
@@ -0,0 +1,86 @@
+import { h, VNode } from "preact";
+import { useState } from "preact/hooks";
+import { Duration, MerchantBackend } from "../../declaration";
+import * as yup from 'yup';
+import ConfirmModal from "./ConfirmModal";
+
+function stringToArray(this: yup.AnySchema, current: any, original: string):
string[] {
+ if (this.isType(current)) return current;
+ return original.split(',').filter(s => s.length > 0)
+}
+function numberToDuration(this: yup.AnySchema, current: any, original:
string): Duration {
+ if (this.isType(current)) return current;
+ console.log('numberToDuration', current, original)
+ const d_ms = parseInt(original, 10)
+ return { d_ms }
+}
+/*
+validations
+ * delays size
+ * payto-uri format
+ * currency
+*/
+
+const schema = yup.object().shape({
+ id: yup.string().required().label("Id"),
+ name: yup.string().required().label("Name"),
+ payto_uris: yup.array().of(yup.string())
+ .min(1).label("Payment Method")
+ .meta({ placeholder: 'comma separated values' })
+ .transform(stringToArray),
+ default_max_deposit_fee: yup.string().required().label("Max Deposit Fee"),
+ default_max_wire_fee: yup.string().required().label("Max Wire"),
+ default_wire_fee_amortization: yup.number().required().label("WireFee
Amortization"),
+ // default_pay_delay: yup.number().required().label("Pay
delay").transform(numberToDuration),
+ // default_wire_transfer_delay: yup.number().required().label("Wire transfer
Delay").transform(numberToDuration),
+});
+
+interface Props {
+ active: boolean;
+ onCancel: () => void;
+ onConfirm: (i: MerchantBackend.Instances.InstanceConfigurationMessage) =>
void;
+}
+
+interface KeyValue {
+ [key: string]: string;
+}
+
+export default function CreateModal({ active, onCancel, onConfirm }: Props):
VNode {
+ const [value, valueHandler] = useState((active || {}) as any)
+ const [errors, setErrors] = useState<KeyValue>({})
+
+ const submit = (): void => {
+ try {
+ schema.validateSync(value, { abortEarly: false })
+ onConfirm({ ...schema.cast(value), address: {}, jurisdiction: {},
default_wire_transfer_delay: { d_ms: 6000 }, default_pay_delay: { d_ms: 3000 }
} as MerchantBackend.Instances.InstanceConfigurationMessage);
+ } catch (err) {
+ const errors = err.inner as yup.ValidationError[]
+ const pathMessages = errors.reduce((prev, cur) => !cur.path ? prev : ({
...prev, [cur.path]: cur.message }), {})
+ setErrors(pathMessages)
+ }
+ }
+
+ return <ConfirmModal description="update instance" active={active}
onConfirm={submit} onCancel={onCancel}>
+ {Object.keys(schema.fields).map(f => {
+
+ const info = schema.fields[f].describe()
+
+ // Just text field for now
+ return <div class="field is-horizontal">
+ <div class="field-label is-normal">
+ <label class="label">{info.label}</label>
+ </div>
+ <div class="field-body">
+ <div class="field">
+ <p class="control is-expanded has-icons-left">
+ <input class="input" type="text"
placeholder={info?.meta?.placeholder} readonly={info?.meta?.readonly} name={f}
value={value[f]} onChange={e => valueHandler(prev => ({ ...prev, [f]:
e.currentTarget.value }))} />
+ </p>
+ {errors[f] ? <p class="help is-danger">{errors[f]}</p> : null}
+ </div>
+ </div>
+ </div>
+
+ })}
+
+ </ConfirmModal>
+}
\ No newline at end of file
diff --git a/src/routes/instances/DeleteAllModal.tsx
b/src/routes/instances/DeleteAllModal.tsx
new file mode 100644
index 0000000..6f81e2c
--- /dev/null
+++ b/src/routes/instances/DeleteAllModal.tsx
@@ -0,0 +1,29 @@
+import { h, VNode } from "preact";
+import { MerchantBackend } from "../../declaration";
+
+interface Props {
+ element: MerchantBackend.Instances.Instance | null;
+ onCancel: () => void;
+ onConfirm: (i: MerchantBackend.Instances.Instance) => void;
+}
+
+export default function DeleteAllModal({ element, onCancel, onConfirm }:
Props): VNode {
+ return <div class={element ? "modal is-active is-clipped" : "modal"}>
+ <div class="modal-background jb-modal-close" onClick={e => onCancel()} />
+ <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" onClick={e =>
onCancel()} />
+ </header>
+ <section class="modal-card-body">
+ <p>This will permanently delete instance "{element?.name}" with id
<b>{element?.id}</b></p>
+ <p>Please confirm this action</p>
+ </section>
+ <footer class="modal-card-foot">
+ <button class="button jb-modal-close" onClick={e => onCancel()}
>Cancel</button>
+ <button class="button is-danger jb-modal-close" onClick={element ? e
=> onConfirm(element) : undefined} >Delete</button>
+ </footer>
+ </div>
+ <button class="modal-close is-large jb-modal-close" aria-label="close"
onClick={e => onCancel()} />
+ </div>
+}
\ No newline at end of file
diff --git a/src/routes/instances/DeleteModal.tsx
b/src/routes/instances/DeleteModal.tsx
new file mode 100644
index 0000000..c3d0d22
--- /dev/null
+++ b/src/routes/instances/DeleteModal.tsx
@@ -0,0 +1,16 @@
+import { h, VNode } from "preact";
+import { MerchantBackend } from "../../declaration";
+import ConfirmModal from "./ConfirmModal";
+
+interface Props {
+ element: MerchantBackend.Instances.Instance;
+ onCancel: () => void;
+ onConfirm: (i: MerchantBackend.Instances.Instance) => void;
+}
+
+export default function DeleteModal({ element, onCancel, onConfirm }: Props):
VNode {
+ return <ConfirmModal description="delete instance" danger active
onCancel={onCancel} onConfirm={() => onConfirm(element)}>
+ <p>This will permanently delete instance "{element.name}" with id
<b>{element.id}</b></p>
+ <p>Please confirm this actionssss</p>
+ </ConfirmModal>
+}
diff --git a/src/routes/instances/Table.tsx b/src/routes/instances/Table.tsx
index c3a0572..8946867 100644
--- a/src/routes/instances/Table.tsx
+++ b/src/routes/instances/Table.tsx
@@ -1,88 +1,130 @@
import { h, VNode } from "preact";
-import { MerchantBackend } from "../../declaration";
+import { useEffect, useState } from "preact/hooks";
+import { MerchantBackend, WidthId as WithId } from "../../declaration";
+import DeleteModal from './DeleteModal'
+import UpdateModal from './UpdateModal'
+import CreateModal from './CreateModal'
interface Props {
instances: MerchantBackend.Instances.Instance[];
+ onCreate: (d: MerchantBackend.Instances.InstanceConfigurationMessage) =>
void;
+ onUpdate: (id: string, d:
MerchantBackend.Instances.InstanceReconfigurationMessage) => void;
+ onDelete: (id: string) => void;
+ onSelect: (id: string | null) => void;
+ selected: MerchantBackend.Instances.QueryInstancesResponse & WithId |
undefined;
}
-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">
- <thead>
- <tr>
- <th class="is-checkbox-cell">
- <label class="b-checkbox checkbox">
- <input type="checkbox" value="false" />
- <span class="check" />
- </label>
- </th>
- <th />
- <th>id</th>
- <th>name</th>
- <th>City</th>
- <th>Progress</th>
- <th>Created</th>
- <th />
- </tr>
- </thead>
- <tbody>
- {instances.map(i => {
- return <tr>
- <td class="is-checkbox-cell">
- <label class="b-checkbox checkbox">
- <input type="checkbox" value="false" />
- <span class="check" />
- </label>
- </td>
- <td class="is-image-cell">
- <div class="image">
- <img
src="https://avatars.dicebear.com/4.5/api/female/rebecca-bauch.svg"
class="is-rounded" />
- </div>
- </td>
- <td data-label="Name">Rebecca Bauch</td>
- <td data-label="Company">Daugherty-Daniel</td>
- <td data-label="City">South Cory</td>
- <td data-label="Progress" class="is-progress-cell">
- <progress max="100" class="progress is-small is-primary"
value="79">79</progress>
- </td>
- <td data-label="Created">
- <small class="has-text-grey is-abbr-like" title="Oct 25,
2020">Oct 25, 2020</small>
- </td>
- <td class="is-actions-cell">
- <div class="buttons is-right">
- <button class="button is-small is-primary" type="button">
- <span class="icon"><i class="mdi mdi-eye" /></span>
- </button>
- <button class="button is-small is-danger jb-modal"
data-target="sample-modal" type="button">
- <span class="icon"><i class="mdi mdi-trash-can" /></span>
- </button>
- </div>
- </td>
- </tr>
- })}
+function toggleSelected<T>(id: T) {
+ return (prev: T[]): T[] => prev.indexOf(id) == -1 ? [...prev, id] :
prev.filter(e => e != id)
+}
- </tbody>
- </table>
- </div>
- <div class="notification">
- <div class="level">
- <div class="level-left">
- <div class="level-item">
- <div class="buttons has-addons">
- <button type="button" class="button is-active">1</button>
- <button type="button" class="button">2</button>
- <button type="button" class="button">3</button>
- </div>
- </div>
- </div>
- <div class="level-right">
- <div class="level-item">
- <small>Page 1 of 3</small>
- </div>
+interface Actions {
+ element: MerchantBackend.Instances.Instance;
+ type: 'DELETE' | 'UPDATE';
+}
+
+function notEmpty<TValue>(value: TValue | null | undefined): value is TValue {
+ return value !== null && value !== undefined;
+}
+
+function buildActions(intances: MerchantBackend.Instances.Instance[],
selected: string[], action: 'DELETE'): Actions[] {
+ return selected.map(id => intances.find(i => i.id === id))
+ .filter(notEmpty)
+ .map(id => ({ element: id, type: action }))
+}
+
+export default function Table({ instances, onCreate, onDelete, onSelect,
onUpdate, selected }: Props): VNode {
+ const [toBeCreated, toBeCreatedHandler] = useState<boolean>(false)
+ const [actionQueue, actionQueueHandler] = useState<Actions[]>([]);
+ const [toBeDeleted, toBeDeletedHandler] =
useState<MerchantBackend.Instances.Instance | null>(null)
+
+ const [rowSelection, rowSelectionHandler] = useState<string[]>([])
+
+ useEffect(() => {
+ if (actionQueue.length > 0 && !toBeDeleted && actionQueue[0].type ==
'DELETE') {
+ toBeDeletedHandler(actionQueue[0].element)
+ actionQueueHandler(actionQueue.slice(1))
+ }
+ }, [actionQueue, toBeDeleted])
+
+ useEffect(() => {
+ if (actionQueue.length > 0 && !selected && actionQueue[0].type ==
'UPDATE') {
+ onSelect(actionQueue[0].element.id)
+ actionQueueHandler(actionQueue.slice(1))
+ }
+ }, [actionQueue, selected])
+
+ return <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>
+ <button class={rowSelection.length > 0 ? "card-header-icon" :
"card-header-icon is-hidden"} type="button" onClick={e =>
actionQueueHandler(buildActions(instances, rowSelection, 'DELETE'))} >
+ <span class="icon"><i class="mdi mdi-trash-can" /></span>
+ </button>
+ <button class="card-header-icon" type="button" onClick={e =>
toBeCreatedHandler(true)}>
+ <span class="icon"><i class="mdi mdi-plus" /></span>
+ </button>
+ </header>
+ <div class="card-content">
+ <div class="b-table has-pagination">
+ <div class="table-wrapper has-mobile-cards">
+ <table class="table is-fullwidth is-striped is-hoverable
is-fullwidth">
+ <thead>
+ <tr>
+ <th class="is-checkbox-cell">
+ <label class="b-checkbox checkbox">
+ <input type="checkbox" checked={rowSelection.length ===
instances.length} onClick={e => rowSelectionHandler(rowSelection.length ===
instances.length ? [] : instances.map(i => i.id))} />
+ <span class="check" />
+ </label>
+ </th>
+ <th>id</th>
+ <th>name</th>
+ <th>public key</th>
+ <th>payments</th>
+ <th />
+ </tr>
+ </thead>
+ <tbody>
+ {instances.map(i => {
+ return <tr>
+ <td class="is-checkbox-cell">
+ <label class="b-checkbox checkbox">
+ <input type="checkbox"
checked={rowSelection.indexOf(i.id) != -1} onClick={e =>
rowSelectionHandler(toggleSelected(i.id))} />
+ <span class="check" />
+ </label>
+ </td>
+ <td >{i.id}</td>
+ <td >{i.name}</td>
+ <td >{i.merchant_pub}</td>
+ <td >{i.payment_targets}</td>
+ <td class="is-actions-cell">
+ <div class="buttons is-right">
+ <button class="button is-small is-primary" type="button"
onClick={e => onSelect(i.id)}>
+ <span class="icon"><i class="mdi mdi-eye" /></span>
+ </button>
+ <button class="button is-small is-danger jb-modal"
type="button" onClick={e => toBeDeletedHandler(i)}>
+ <span class="icon"><i class="mdi mdi-trash-can"
/></span>
+ </button>
+ </div>
+ </td>
+ </tr>
+ })}
+
+ </tbody>
+ </table>
</div>
+ {toBeDeleted ? <DeleteModal element={toBeDeleted} onCancel={() =>
toBeDeletedHandler(null)} onConfirm={(i) => {
+ onDelete(i.id)
+ toBeDeletedHandler(null);
+ }} /> : null}
+ {selected ? <UpdateModal element={selected} onCancel={() =>
onSelect(null)} onConfirm={(i) => {
+ onUpdate(selected.id, i)
+ onSelect(null);
+ }} /> : null}
+ {toBeCreated ? <CreateModal active={toBeCreated} onCancel={() =>
toBeCreatedHandler(false)} onConfirm={(i) => {
+ onCreate(i)
+ toBeCreatedHandler(false);
+ }} /> : null}
</div>
</div>
- </div>
-
+ </div >
}
\ No newline at end of file
diff --git a/src/routes/instances/UpdateModal.stories.tsx
b/src/routes/instances/UpdateModal.stories.tsx
new file mode 100644
index 0000000..db39d61
--- /dev/null
+++ b/src/routes/instances/UpdateModal.stories.tsx
@@ -0,0 +1,24 @@
+/* eslint-disable @typescript-eslint/camelcase */
+import { h } from 'preact';
+import UpdateModal from './UpdateModal'
+
+
+export default {
+ title: 'Instances/UpdateModal',
+ component: UpdateModal,
+ argTypes: {
+ element: { control: 'object' },
+ onCancel: { action: 'onCancel' },
+ onConfirm: { action: 'onConfirm' },
+ }
+};
+
+export const WithDefaultInstance = (a) => <UpdateModal {...a} />;
+WithDefaultInstance.args = {
+ element: {
+ id: 'default',
+ name: 'the default instance',
+ merchant_pub: 'abcdef',
+ payment_targets: ['qwe','asd']
+ }
+}
diff --git a/src/routes/instances/UpdateModal.tsx
b/src/routes/instances/UpdateModal.tsx
new file mode 100644
index 0000000..1275911
--- /dev/null
+++ b/src/routes/instances/UpdateModal.tsx
@@ -0,0 +1,76 @@
+import { h, VNode } from "preact";
+import { useState } from "preact/hooks";
+import { MerchantBackend } from "../../declaration";
+import * as yup from 'yup';
+import ConfirmModal from "./ConfirmModal";
+
+function stringToArray(this: yup.AnySchema, current: any, original: string):
string[] {
+ if (this.isType(current)) return current;
+ return original.split(',').filter(s => s.length > 0)
+}
+
+const schema = yup.object().shape({
+ name: yup.string().required().label("Name"),
+ payto_uris: yup.array().of(yup.string())
+ .min(1).label("Payment Method")
+ .meta({ placeholder: 'comma separated values' })
+ .transform(stringToArray),
+ default_max_deposit_fee: yup.string().required().label("Max Deposit Fee"),
+ default_max_wire_fee: yup.string().required().label("Max Wire"),
+ default_wire_fee_amortization: yup.number().required().label("WireFee
Amortization"),
+ // default_pay_delay: yup.number().required().label("Pay
delay").transform(numberToDuration),
+ // default_wire_transfer_delay: yup.number().required().label("Wire transfer
Delay").transform(numberToDuration),
+});
+
+interface Props {
+ element: MerchantBackend.Instances.QueryInstancesResponse | null;
+ onCancel: () => void;
+ onConfirm: (i: MerchantBackend.Instances.InstanceReconfigurationMessage) =>
void;
+}
+
+interface KeyValue {
+ [key: string]: string;
+}
+
+export default function UpdateModal({ element, onCancel, onConfirm }: Props):
VNode {
+ const copy = !element ? {} : Object.keys(schema.fields).reduce((prev,cur) =>
({...prev, [cur]: (element as any)[cur] }), {})
+
+ const [value, valueHandler] = useState(copy)
+ const [errors, setErrors] = useState<KeyValue>({})
+
+ const submit = (): void => {
+ try {
+ schema.validateSync(value, { abortEarly: false })
+
+ onConfirm({...schema.cast(value), address: {}, jurisdiction: {},
default_wire_transfer_delay: { d_ms: 6000 }, default_pay_delay: { d_ms: 3000 }}
as MerchantBackend.Instances.InstanceReconfigurationMessage);
+ } catch (err) {
+ const errors = err.inner as yup.ValidationError[]
+ const pathMessages = errors.reduce((prev, cur) => !cur.path ? prev : ({
...prev, [cur.path]: cur.message }), {})
+ setErrors(pathMessages)
+ }
+ }
+
+ return <ConfirmModal description="update instance" active={element != null}
onConfirm={submit} onCancel={onCancel}>
+ {Object.keys(schema.fields).map(f => {
+
+ const info = schema.fields[f].describe()
+
+ // Just text field for now
+ return <div class="field is-horizontal">
+ <div class="field-label is-normal">
+ <label class="label">{info.label}</label>
+ </div>
+ <div class="field-body">
+ <div class="field">
+ <p class="control is-expanded has-icons-left">
+ <input class="input" type="text"
placeholder={info?.meta?.placeholder} readonly={info?.meta?.readonly} name={f}
value={value[f]} onChange={e => valueHandler(prev => ({ ...prev, [f]:
e.currentTarget.value }))} />
+ </p>
+ {errors[f] ? <p class="help is-danger">{errors[f]}</p> : null}
+ </div>
+ </div>
+ </div>
+
+ })}
+
+ </ConfirmModal>
+}
\ No newline at end of file
diff --git a/src/routes/instances/View.stories.tsx
b/src/routes/instances/View.stories.tsx
index a508298..208de17 100644
--- a/src/routes/instances/View.stories.tsx
+++ b/src/routes/instances/View.stories.tsx
@@ -13,23 +13,32 @@ export default {
},
};
-export const Empty = () => <View instances={[]} />
+export const Empty = (a) => <View {...a} />;
+Empty.args = {
+ instances: []
+}
-export const WithDefaultInstance = () => <View instances={[{
- id: 'default',
- name: 'the default instance',
- merchant_pub: 'abcdef',
- payment_targets: []
-}]} />
+export const WithDefaultInstance = (a) => <View {...a} />;
+WithDefaultInstance.args = {
+ 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']
-}]} />
+export const WithTwoInstance = (a) => <View {...a} />;
+WithTwoInstance.args = {
+ 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
index b209726..6d357af 100644
--- a/src/routes/instances/View.tsx
+++ b/src/routes/instances/View.tsx
@@ -1,14 +1,41 @@
import { h, VNode } from "preact";
-import { MerchantBackend } from "../../declaration";
+import { MerchantBackend, WidthId } from "../../declaration";
import Table from './Table';
interface Props {
instances: MerchantBackend.Instances.Instance[];
+ onCreate: (d: MerchantBackend.Instances.InstanceConfigurationMessage) =>
void;
+ onUpdate: (id: string, d:
MerchantBackend.Instances.InstanceReconfigurationMessage) => void;
+ onDelete: (id: string) => void;
+ onSelect: (id: string | null) => void;
+ selected: MerchantBackend.Instances.QueryInstancesResponse & WidthId |
undefined;
}
-export default function View({ instances }: Props): VNode {
+export default function View({ instances, onCreate, onDelete, onSelect,
onUpdate, selected }: Props): VNode {
return <div id="app">
+ <div class="toast">
+ <article class="message">
+ <div class="message-header">
+ <p>Normal message</p>
+ <button class="delete" aria-label="delete" />
+ </div>
+ <div class="message-body">
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit.
+ </div>
+ </article>
+ <article class="message is-danger">
+ <div class="message-header">
+ <p>Normal message</p>
+ <button class="delete" aria-label="delete" />
+ </div>
+ <div class="message-body">
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit.
+ </div>
+ </article>
+ </div>
+
<section class="section is-title-bar">
+
<div class="level">
<div class="level-left">
<div class="level-item">
@@ -35,40 +62,12 @@ export default function View({ instances }: Props): VNode {
</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>
-
+ <Table instances={instances} onCreate={onCreate}
+ onUpdate={onUpdate}
+ onDelete={onDelete} onSelect={onSelect}
+ selected={selected}
+ />
</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>
+ </div >
}
\ No newline at end of file
diff --git a/src/routes/instances/index.tsx b/src/routes/instances/index.tsx
index 0aeef3e..b83eee0 100644
--- a/src/routes/instances/index.tsx
+++ b/src/routes/instances/index.tsx
@@ -1,15 +1,22 @@
import { h, VNode } from 'preact';
import View from './View';
import LoginPage from '../../components/auth/LoginPage';
-import { updateToken, useBackendInstances } from
'../../components/hooks/backend';
+import { updateToken, useBackendInstance, useBackendInstances } from
'../../components/hooks/backend';
+import { useState } from 'preact/hooks';
export default function Instances(): VNode {
- const resp = useBackendInstances()
+ const list = useBackendInstances()
+ const [selectedId, select] = useState<string|null>(null)
+ const details = useBackendInstance(selectedId)
- if (!resp.data) {
+ if (!list.data || (selectedId != null && !details.data)) {
return <LoginPage onLogIn={updateToken} />
}
- return <View instances={resp.data.instances} />;
-}
\ No newline at end of file
+ return <View instances={list.data.instances}
+ onCreate={list.create} onUpdate={details.update}
+ onDelete={details.delete} onSelect={select}
+ selected={ !details.data || !selectedId ? undefined : {...details.data,
id:selectedId} }
+ />;
+}
diff --git a/src/scss/main.scss b/src/scss/main.scss
index 657a871..8f6ed75 100644
--- a/src/scss/main.scss
+++ b/src/scss/main.scss
@@ -20,3 +20,26 @@
@import "modal";
@import "footer";
@import "misc";
+
+@import "fonts/nunito.css";
+@import "icons/materialdesignicons-4.9.95.min.css";
+
+.toast {
+ position: fixed;
+ right: 10px;
+ top: 10px;
+ z-index: 999;
+
+ display: flex;
+ flex-direction: column;
+ padding: 15px;
+ text-align: right;
+ align-items: flex-end;
+ width: auto;
+ pointer-events: none;
+}
+
+.toast > .message {
+ white-space:pre-wrap;
+ opacity:80%;
+}
diff --git a/yarn.lock b/yarn.lock
index 8fdddeb..303a427 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -960,7 +960,7 @@
pirates "^4.0.0"
source-map-support "^0.5.16"
-"@babel/runtime@^7.0.0", "@babel/runtime@^7.10.2", "@babel/runtime@^7.11.2",
"@babel/runtime@^7.12.5", "@babel/runtime@^7.3.1", "@babel/runtime@^7.5.0",
"@babel/runtime@^7.5.5", "@babel/runtime@^7.7.2", "@babel/runtime@^7.8.4":
+"@babel/runtime@^7.0.0", "@babel/runtime@^7.10.2", "@babel/runtime@^7.10.5",
"@babel/runtime@^7.11.2", "@babel/runtime@^7.12.5", "@babel/runtime@^7.3.1",
"@babel/runtime@^7.4.4", "@babel/runtime@^7.5.0", "@babel/runtime@^7.5.5",
"@babel/runtime@^7.7.2", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7":
version "7.12.13"
resolved
"https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.12.13.tgz#0a21452352b02542db0ffb928ac2d3ca7cb6d66d"
integrity
sha512-8+3UMPBrjFa/6TtKi/7sehPKqfAm4g6K+YQjyyFOLUTxzOngcRZTlAVY8sc2CORJYqdHQY8gRPHmn+qo15rCBw==
@@ -1018,7 +1018,7 @@
exec-sh "^0.3.2"
minimist "^1.2.0"
-"@emotion/cache@^10.0.27":
+"@emotion/cache@^10.0.27", "@emotion/cache@^10.0.9":
version "10.0.29"
resolved
"https://registry.yarnpkg.com/@emotion/cache/-/cache-10.0.29.tgz#87e7e64f412c060102d589fe7c6dc042e6f9d1e0"
integrity
sha512-fU2VtSVlHiF27empSbxi1O2JFdNWZO+2NFHfwO0pxgTep6Xa3uGb+3pVKfLww2l/IBGLNEZl5Xf/++A4wAYDYQ==
@@ -1028,7 +1028,7 @@
"@emotion/utils" "0.11.3"
"@emotion/weak-memoize" "0.2.5"
-"@emotion/core@^10.1.1":
+"@emotion/core@^10.0.9", "@emotion/core@^10.1.1":
version "10.1.1"
resolved
"https://registry.yarnpkg.com/@emotion/core/-/core-10.1.1.tgz#c956c1365f2f2481960064bcb8c4732e5fb612c3"
integrity
sha512-ZMLG6qpXR8x031NXD8HJqugy/AZSkAuMxxqB46pmAR7ze47MhNJ56cdoX243QPZdGctrdfo+s08yZTiwaUcRKA==
@@ -1040,7 +1040,7 @@
"@emotion/sheet" "0.9.4"
"@emotion/utils" "0.11.3"
-"@emotion/css@^10.0.27":
+"@emotion/css@^10.0.27", "@emotion/css@^10.0.9":
version "10.0.27"
resolved
"https://registry.yarnpkg.com/@emotion/css/-/css-10.0.27.tgz#3a7458198fbbebb53b01b2b87f64e5e21241e14c"
integrity
sha512-6wZjsvYeBhyZQYNrGoR5yPMYbMBNEnanDrqmsqS1mzDm1cOTu12shvl2j4QHNS36UaTE0USIJawCH9C8oW34Zw==
@@ -1514,6 +1514,28 @@
dependencies:
"@sinonjs/commons" "^1.7.0"
+"@storybook/addon-a11y@^6.1.17":
+ version "6.1.17"
+ resolved
"https://registry.yarnpkg.com/@storybook/addon-a11y/-/addon-a11y-6.1.17.tgz#67fe6e140c6a1365f6547ac9458c454c9d7ad572"
+ integrity
sha512-RBL0sx5FuNrPNW7GQaSdyly5WwsqD7FB+APPPFYvEbwyIksS5rZ3x2JnO2iM6UQxCkYxOL2Ou3ZPgJloXfwjxA==
+ dependencies:
+ "@storybook/addons" "6.1.17"
+ "@storybook/api" "6.1.17"
+ "@storybook/channels" "6.1.17"
+ "@storybook/client-api" "6.1.17"
+ "@storybook/client-logger" "6.1.17"
+ "@storybook/components" "6.1.17"
+ "@storybook/core-events" "6.1.17"
+ "@storybook/theming" "6.1.17"
+ axe-core "^4.0.1"
+ core-js "^3.0.1"
+ global "^4.3.2"
+ lodash "^4.17.15"
+ react-sizeme "^2.5.2"
+ regenerator-runtime "^0.13.7"
+ ts-dedent "^2.0.0"
+ util-deprecate "^1.0.2"
+
"@storybook/addon-actions@6.1.17", "@storybook/addon-actions@^6.1.16":
version "6.1.17"
resolved
"https://registry.yarnpkg.com/@storybook/addon-actions/-/addon-actions-6.1.17.tgz#9d32336284738cefa69b99acafa4b132d5533600"
@@ -1633,6 +1655,31 @@
regenerator-runtime "^0.13.7"
ts-dedent "^2.0.0"
+"@storybook/addon-knobs@^6.1.17":
+ version "6.1.17"
+ resolved
"https://registry.yarnpkg.com/@storybook/addon-knobs/-/addon-knobs-6.1.17.tgz#c7cdd5be813c2b80ce7f464eabf8ceb06486e82d"
+ integrity
sha512-WUkoGtHhXIurXFQybsMXZaphAtCNclZjZHvji8O5eg+ahx7pIM/Wldh3uJwOdOkW5LHxT76hLxPvuXvOEysnbw==
+ dependencies:
+ "@storybook/addons" "6.1.17"
+ "@storybook/api" "6.1.17"
+ "@storybook/channels" "6.1.17"
+ "@storybook/client-api" "6.1.17"
+ "@storybook/components" "6.1.17"
+ "@storybook/core-events" "6.1.17"
+ "@storybook/theming" "6.1.17"
+ copy-to-clipboard "^3.0.8"
+ core-js "^3.0.1"
+ escape-html "^1.0.3"
+ fast-deep-equal "^3.1.1"
+ global "^4.3.2"
+ lodash "^4.17.15"
+ prop-types "^15.7.2"
+ qs "^6.6.0"
+ react-color "^2.17.0"
+ react-lifecycles-compat "^3.0.4"
+ react-select "^3.0.8"
+ regenerator-runtime "^0.13.7"
+
"@storybook/addon-links@^6.1.16":
version "6.1.17"
resolved
"https://registry.yarnpkg.com/@storybook/addon-links/-/addon-links-6.1.17.tgz#53cd2f85a83006950cd86cef9975ae8311ac1de4"
@@ -1651,6 +1698,26 @@
regenerator-runtime "^0.13.7"
ts-dedent "^2.0.0"
+"@storybook/addon-storysource@^6.1.17":
+ version "6.1.17"
+ resolved
"https://registry.yarnpkg.com/@storybook/addon-storysource/-/addon-storysource-6.1.17.tgz#8db1ce2851227a2fe705d1b7f2d6cd4cff786481"
+ integrity
sha512-baRAlC8EErK0slK1GP8XgE34omiQP3zip7690yB2zj5aBdDiUUIjeBqzhluW840OEIGUrBicvLZaO8JmLSffTA==
+ dependencies:
+ "@storybook/addons" "6.1.17"
+ "@storybook/api" "6.1.17"
+ "@storybook/client-logger" "6.1.17"
+ "@storybook/components" "6.1.17"
+ "@storybook/router" "6.1.17"
+ "@storybook/source-loader" "6.1.17"
+ "@storybook/theming" "6.1.17"
+ core-js "^3.0.1"
+ estraverse "^4.2.0"
+ loader-utils "^2.0.0"
+ prettier "~2.0.5"
+ prop-types "^15.7.2"
+ react-syntax-highlighter "^13.5.0"
+ regenerator-runtime "^0.13.7"
+
"@storybook/addon-toolbars@6.1.17":
version "6.1.17"
resolved
"https://registry.yarnpkg.com/@storybook/addon-toolbars/-/addon-toolbars-6.1.17.tgz#15a015dc871e26be66340b5641631e649925c5e2"
@@ -2207,6 +2274,11 @@
resolved
"https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.7.tgz#98a993516c859eb0d5c4c8f098317a9ea68db9ad"
integrity
sha512-cxWFQVseBm6O9Gbw1IWb8r6OS4OhSt3hPZLkFApLjM8TEXROBuQGLAH2i2gZpcXdLBIrpXuTDhH7Vbm1iXmNGA==
+"@types/lodash@^4.14.165":
+ version "4.14.168"
+ resolved
"https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.168.tgz#fe24632e79b7ade3f132891afff86caa5e5ce008"
+ integrity
sha512-oVfRvqHV/V6D1yifJbVRU3TMp8OT6o6BG+U9MkwuJ3U8/CsDHvalRpsxBqivn71ztOFZBTfJMvETbqHiaNSj7Q==
+
"@types/markdown-to-jsx@^6.11.0":
version "6.11.3"
resolved
"https://registry.yarnpkg.com/@types/markdown-to-jsx/-/markdown-to-jsx-6.11.3.tgz#cdd1619308fecbc8be7e6a26f3751260249b020e"
@@ -3062,6 +3134,11 @@ aws4@^1.8.0:
resolved
"https://registry.yarnpkg.com/aws4/-/aws4-1.11.0.tgz#d61f46d83b2519250e2784daf5b09479a8b41c59"
integrity
sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==
+axe-core@^4.0.1:
+ version "4.1.2"
+ resolved
"https://registry.yarnpkg.com/axe-core/-/axe-core-4.1.2.tgz#7cf783331320098bfbef620df3b3c770147bc224"
+ integrity
sha512-V+Nq70NxKhYt89ArVcaNL9FDryB3vQOd+BFXZIfO3RP6rwtj+2yqqqdHEkacutglPaZLkJeuXKCjCJDMGPtPqg==
+
axios@^0.21.1:
version "0.21.1"
resolved
"https://registry.yarnpkg.com/axios/-/axios-0.21.1.tgz#22563481962f4d6bde9a76d516ef0e5d3c09b2b8"
@@ -5327,6 +5404,14 @@ dom-converter@^0.2:
dependencies:
utila "~0.4"
+dom-helpers@^5.0.1:
+ version "5.2.0"
+ resolved
"https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-5.2.0.tgz#57fd054c5f8f34c52a3eeffdb7e7e93cd357d95b"
+ integrity
sha512-Ru5o9+V8CpunKnz5LGgWXkmrH/20cGKwcHwS4m73zIvs54CN9epEmT/HLqFJW3kXpakAFkEdzgy1hzlJe3E4OQ==
+ dependencies:
+ "@babel/runtime" "^7.8.7"
+ csstype "^3.0.2"
+
dom-serializer@0:
version "0.2.2"
resolved
"https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.2.2.tgz#1afb81f533717175d478655debc5e332d9f9bb51"
@@ -5769,7 +5854,7 @@ escape-goat@^2.0.0:
resolved
"https://registry.yarnpkg.com/escape-goat/-/escape-goat-2.1.1.tgz#1b2dc77003676c457ec760b2dc68edb648188675"
integrity
sha512-8/uIhbG12Csjy2JEW7D9pHbreaVaS/OpN3ycnyvElTdwM5n6GY6W6e2IPemfvGZeUMqZ9A/3GqIZMgKnBhAw/Q==
-escape-html@~1.0.3:
+escape-html@^1.0.3, escape-html@~1.0.3:
version "1.0.3"
resolved
"https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988"
integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=
@@ -8993,7 +9078,7 @@ locate-path@^5.0.0:
dependencies:
p-locate "^4.1.0"
-lodash-es@^4.17.15:
+lodash-es@^4.17.11, lodash-es@^4.17.15:
version "4.17.20"
resolved
"https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.20.tgz#29f6332eefc60e849f869c264bc71126ad61e8f7"
integrity
sha512-JD1COMZsq8maT6mnuz1UMV0jvYD0E0aUsSOdrr1/nAG3dhqQXwRRgeW0cSqH1U43INKcqxaiVIQNOUDld7gRDA==
@@ -9270,6 +9355,11 @@ media-typer@0.3.0:
resolved
"https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748"
integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=
+memoize-one@^5.0.0:
+ version "5.1.1"
+ resolved
"https://registry.yarnpkg.com/memoize-one/-/memoize-one-5.1.1.tgz#047b6e3199b508eaec03504de71229b8eb1d75c0"
+ integrity
sha512-HKeeBpWvqiVJD57ZUAsJNm71eHTykffzcLZVYWiVfQeI1rJtuEaS7hQiEpWfVVk18donPwJEcFKIkCmPJNOhHA==
+
memoizerific@^1.11.3:
version "1.11.3"
resolved
"https://registry.yarnpkg.com/memoizerific/-/memoizerific-1.11.3.tgz#7c87a4646444c32d75438570905f2dbd1b1a805a"
@@ -9592,6 +9682,11 @@ nan@^2.12.1, nan@^2.13.2:
resolved
"https://registry.yarnpkg.com/nan/-/nan-2.14.2.tgz#f5376400695168f4cc694ac9393d0c9585eeea19"
integrity
sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ==
+nanoclone@^0.2.1:
+ version "0.2.1"
+ resolved
"https://registry.yarnpkg.com/nanoclone/-/nanoclone-0.2.1.tgz#dd4090f8f1a110d26bb32c49ed2f5b9235209ed4"
+ integrity
sha512-wynEP02LmIbLpcYw8uBKpcfF6dmg2vcpKqxeH5UcoKEYdExslsdUA4ugFauuaeYdTB76ez6gJW8XAZ6CgkXYxA==
+
nanomatch@^1.2.9:
version "1.2.13"
resolved
"https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119"
@@ -11109,7 +11204,7 @@ prompts@^2.0.1, prompts@^2.2.1:
kleur "^3.0.3"
sisteransi "^1.0.5"
-prop-types@^15.0.0, prop-types@^15.5.10, prop-types@^15.6.0,
prop-types@^15.6.1, prop-types@^15.6.2, prop-types@^15.7.2:
+prop-types@^15.0.0, prop-types@^15.5.10, prop-types@^15.5.8,
prop-types@^15.6.0, prop-types@^15.6.1, prop-types@^15.6.2, prop-types@^15.7.2:
version "15.7.2"
resolved
"https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5"
integrity
sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==
@@ -11118,6 +11213,11 @@ prop-types@^15.0.0, prop-types@^15.5.10,
prop-types@^15.6.0, prop-types@^15.6.1,
object-assign "^4.1.1"
react-is "^16.8.1"
+property-expr@^2.0.4:
+ version "2.0.4"
+ resolved
"https://registry.yarnpkg.com/property-expr/-/property-expr-2.0.4.tgz#37b925478e58965031bb612ec5b3260f8241e910"
+ integrity
sha512-sFPkHQjVKheDNnPvotjQmm3KD3uk1fWKUN7CrpdbwmUx3CrG3QiM8QpTSimvig5vTXmTvjz7+TDvXOI9+4rkcg==
+
property-information@^5.0.0, property-information@^5.3.0:
version "5.6.0"
resolved
"https://registry.yarnpkg.com/property-information/-/property-information-5.6.0.tgz#61675545fb23002f245c6540ec46077d4da3ed69"
@@ -11420,6 +11520,13 @@ react-hotkeys@2.0.0:
dependencies:
prop-types "^15.6.1"
+react-input-autosize@^3.0.0:
+ version "3.0.0"
+ resolved
"https://registry.yarnpkg.com/react-input-autosize/-/react-input-autosize-3.0.0.tgz#6b5898c790d4478d69420b55441fcc31d5c50a85"
+ integrity
sha512-nL9uS7jEs/zu8sqwFE5MAPx6pPkNAriACQ2rGLlqmKr2sPGtN7TXTyDdQt4lbNXVx7Uzadb40x8qotIuru6Rhg==
+ dependencies:
+ prop-types "^15.5.8"
+
react-inspector@^5.0.1:
version "5.1.0"
resolved
"https://registry.yarnpkg.com/react-inspector/-/react-inspector-5.1.0.tgz#45a325e15f33e595be5356ca2d3ceffb7d6b8c3a"
@@ -11466,7 +11573,21 @@ react-refresh@0.8.3:
resolved
"https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.8.3.tgz#721d4657672d400c5e3c75d063c4a85fb2d5d68f"
integrity
sha512-X8jZHc7nCMjaCqoU+V2I0cOhNW+QMBwSUkeXnTi8IPe6zaRWfn60ZzvFDZqWPfmSJfjub7dDW1SP0jaHWLu/hg==
-react-sizeme@^2.6.7:
+react-select@^3.0.8:
+ version "3.2.0"
+ resolved
"https://registry.yarnpkg.com/react-select/-/react-select-3.2.0.tgz#de9284700196f5f9b5277c5d850a9ce85f5c72fe"
+ integrity
sha512-B/q3TnCZXEKItO0fFN/I0tWOX3WJvi/X2wtdffmwSQVRwg5BpValScTO1vdic9AxlUgmeSzib2hAZAwIUQUZGQ==
+ dependencies:
+ "@babel/runtime" "^7.4.4"
+ "@emotion/cache" "^10.0.9"
+ "@emotion/core" "^10.0.9"
+ "@emotion/css" "^10.0.9"
+ memoize-one "^5.0.0"
+ prop-types "^15.6.0"
+ react-input-autosize "^3.0.0"
+ react-transition-group "^4.3.0"
+
+react-sizeme@^2.5.2, react-sizeme@^2.6.7:
version "2.6.12"
resolved
"https://registry.yarnpkg.com/react-sizeme/-/react-sizeme-2.6.12.tgz#ed207be5476f4a85bf364e92042520499455453e"
integrity
sha512-tL4sCgfmvapYRZ1FO2VmBmjPVzzqgHA7kI8lSJ6JS6L78jXFNRdOZFpXyK6P1NBZvKPPCZxReNgzZNUajAerZw==
@@ -11496,6 +11617,16 @@ react-textarea-autosize@^8.1.1:
use-composed-ref "^1.0.0"
use-latest "^1.0.0"
+react-transition-group@^4.3.0:
+ version "4.4.1"
+ resolved
"https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-4.4.1.tgz#63868f9325a38ea5ee9535d828327f85773345c9"
+ integrity
sha512-Djqr7OQ2aPUiYurhPalTrVy9ddmFCCzwhqQmtN+J3+3DzLO209Fdr70QrN8Z3DsglWql6iY1lDWAfpFiBtuKGw==
+ dependencies:
+ "@babel/runtime" "^7.5.5"
+ dom-helpers "^5.0.1"
+ loose-envify "^1.4.0"
+ prop-types "^15.6.2"
+
react@16.13.1:
version "16.13.1"
resolved
"https://registry.yarnpkg.com/react/-/react-16.13.1.tgz#2e818822f1a9743122c063d6410d85c1e3afe48e"
@@ -13330,6 +13461,11 @@ toposort@^1.0.0:
resolved
"https://registry.yarnpkg.com/toposort/-/toposort-1.0.7.tgz#2e68442d9f64ec720b8cc89e6443ac6caa950029"
integrity sha1-LmhELZ9k7HILjMieZEOsbKqVACk=
+toposort@^2.0.2:
+ version "2.0.2"
+ resolved
"https://registry.yarnpkg.com/toposort/-/toposort-2.0.2.tgz#ae21768175d1559d48bef35420b2f4962f09c330"
+ integrity sha1-riF2gXXRVZ1IvvNUILL0li8JwzA=
+
totalist@^1.0.0:
version "1.1.0"
resolved
"https://registry.yarnpkg.com/totalist/-/totalist-1.1.0.tgz#a4d65a3e546517701e3e5c37a47a70ac97fe56df"
@@ -14573,6 +14709,19 @@ yocto-queue@^0.1.0:
resolved
"https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b"
integrity
sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==
+yup@^0.32.8:
+ version "0.32.8"
+ resolved
"https://registry.yarnpkg.com/yup/-/yup-0.32.8.tgz#16e4a949a86a69505abf99fd0941305ac9adfc39"
+ integrity
sha512-SZulv5FIZ9d5H99EN5tRCRPXL0eyoYxWIP1AacCrjC9d4DfP13J1dROdKGfpfRHT3eQB6/ikBl5jG21smAfCkA==
+ dependencies:
+ "@babel/runtime" "^7.10.5"
+ "@types/lodash" "^4.14.165"
+ lodash "^4.17.20"
+ lodash-es "^4.17.11"
+ nanoclone "^0.2.1"
+ property-expr "^2.0.4"
+ toposort "^2.0.2"
+
zwitch@^1.0.0:
version "1.0.5"
resolved
"https://registry.yarnpkg.com/zwitch/-/zwitch-1.0.5.tgz#d11d7381ffed16b742f6af7b3f223d5cd9fe9920"
--
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [taler-merchant-backoffice] branch master updated: simple login, but integrated with merchant backend for create update and delete,
gnunet <=