[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[taler-merchant-backoffice] branch master updated: refactor src/index to
From: |
gnunet |
Subject: |
[taler-merchant-backoffice] branch master updated: refactor src/index to split routes |
Date: |
Tue, 23 Feb 2021 22:03:20 +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 64451fe refactor src/index to split routes
64451fe is described below
commit 64451fe1b74508c3eda71dbc8f3d4101a255a330
Author: Sebastian <sebasjm@gmail.com>
AuthorDate: Tue Feb 23 18:03:09 2021 -0300
refactor src/index to split routes
---
CHANGELOG.md | 1 +
packages/frontend/src/ApplicationReadyRoutes.tsx | 68 +++++++
packages/frontend/src/InstanceRoutes.tsx | 87 ++++++++
packages/frontend/src/components/auth/index.tsx | 2 +-
.../src/components/notifications/index.tsx | 3 +-
packages/frontend/src/components/yup/YupField.tsx | 208 +------------------
packages/frontend/src/components/yup/YupInput.tsx | 60 ++++++
.../frontend/src/components/yup/YupInputArray.tsx | 81 ++++++++
.../src/components/yup/YupInputSecured.tsx | 68 +++++++
.../src/components/yup/YupInputWithAddon.tsx | 68 +++++++
.../frontend/src/components/yup/YupObjectInput.tsx | 59 ++++++
packages/frontend/src/declaration.d.ts | 13 --
packages/frontend/src/hooks/index.ts | 12 +-
packages/frontend/src/hooks/notifications.ts | 2 +-
packages/frontend/src/index.tsx | 219 +++------------------
.../src/routes/instances/details/index.tsx | 2 +-
.../frontend/src/routes/instances/list/index.tsx | 13 +-
packages/frontend/src/routes/login/index.tsx | 2 +-
packages/frontend/src/schemas/index.ts | 2 +-
packages/frontend/src/{ => utils}/constants.ts | 0
.../src/{constants.ts => utils/functions.ts} | 15 +-
.../frontend/src/{constants.ts => utils/types.ts} | 19 +-
packages/frontend/tests/functions/regex.test.ts | 2 +-
23 files changed, 576 insertions(+), 430 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index c1e5d5f..4797763 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -17,6 +17,7 @@ and this project adheres to [Semantic
Versioning](https://semver.org/spec/v2.0.0
- validate on change everything
- all button to the right
- feature: input as date format
+ - bug: there is missing a mutate call when updating to remove the instance
from cache
- add order section
- add product section
diff --git a/packages/frontend/src/ApplicationReadyRoutes.tsx
b/packages/frontend/src/ApplicationReadyRoutes.tsx
new file mode 100644
index 0000000..21fa1b6
--- /dev/null
+++ b/packages/frontend/src/ApplicationReadyRoutes.tsx
@@ -0,0 +1,68 @@
+import { h, VNode } from 'preact';
+import { useContext } from "preact/hooks";
+import { Route, Router, route } from 'preact-router';
+import { useMessageTemplate } from 'preact-messages';
+import { Notification } from "./utils/types";
+import { BackendContext } from './context/backend';
+import { SwrError } from "./hooks/backend";
+import { InstanceRoutes } from "./InstanceRoutes";
+import { RootPaths, Redirect } from "./index";
+import NotFoundPage from './routes/notfound';
+import LoginPage from './routes/login';
+import InstanceListPage from './routes/instances/list';
+import InstanceCreatePage from "./routes/instances/create";
+
+export function ApplicationReadyRoutes({ pushNotification, addTokenCleaner }:
{ pushNotification: (n: Notification) => void; addTokenCleaner: any; }): VNode {
+ const { changeBackend, updateToken } = useContext(BackendContext);
+
+ const updateLoginStatus = (url: string, token?: string) => {
+ changeBackend(url);
+ if (token)
+ updateToken(token);
+ };
+ const i18n = useMessageTemplate();
+
+ return <Router>
+ <Route path={RootPaths.root} component={Redirect}
+ to={RootPaths.list_instances} />
+
+ <Route path={RootPaths.list_instances} component={InstanceListPage}
+
+ onCreate={() => {
+ route(RootPaths.new_instance);
+ }}
+
+ onUpdate={(id: string): void => {
+ route(`/instance/${id}/`);
+ }}
+
+ onUnauthorized={() => <LoginPage
+ withMessage={{ message: i18n`Access denied`, description: i18n`Check
your token is valid`, type: 'ERROR', }}
+ onConfirm={updateLoginStatus} />}
+
+ onError={(error: SwrError) => {
+ pushNotification({ message: i18n`error`, params: error, type: 'ERROR'
});
+ return <div />;
+ }} />
+
+ <Route path={RootPaths.new_instance} component={InstanceCreatePage}
+
+ onBack={() => route(RootPaths.list_instances)}
+
+ onConfirm={() => {
+ pushNotification({ message: i18n`create_success`, type: 'SUCCESS' });
+ route(RootPaths.list_instances);
+ }}
+
+ onError={(error: any) => {
+ pushNotification({ message: i18n`create_error`, type: 'ERROR', params:
error });
+ }} />
+
+ <Route path={RootPaths.instance_id_route} component={InstanceRoutes}
+ pushNotification={pushNotification}
+ addTokenCleaner={addTokenCleaner} />
+
+ <Route default component={NotFoundPage} />
+
+ </Router>;
+}
diff --git a/packages/frontend/src/InstanceRoutes.tsx
b/packages/frontend/src/InstanceRoutes.tsx
new file mode 100644
index 0000000..a9ae009
--- /dev/null
+++ b/packages/frontend/src/InstanceRoutes.tsx
@@ -0,0 +1,87 @@
+import { h, VNode } from 'preact';
+import { useCallback, useContext, useEffect } from "preact/hooks";
+import { Route, Router, route } from 'preact-router';
+import { useMessageTemplate } from 'preact-messages';
+import { useBackendInstanceToken } from './hooks';
+import { BackendContext, InstanceContext } from './context/backend';
+import { SwrError } from "./hooks/backend";
+import { InstancePaths } from "./index";
+import { Notification } from './utils/types';
+import NotFoundPage from './routes/notfound';
+import LoginPage from './routes/login';
+import InstanceDetailsPage from "./routes/instances/details";
+import InstanceUpdatePage from "./routes/instances/update";
+
+export interface Props {
+ id: string;
+ pushNotification: (n: Notification) => void;
+ addTokenCleaner: any;
+}
+
+export function InstanceRoutes({ id, pushNotification, addTokenCleaner }:
Props): VNode {
+ const [token, updateToken] = useBackendInstanceToken(id);
+ const { changeBackend } = useContext(BackendContext);
+ const cleaner = useCallback(() => { updateToken(undefined); }, [id]);
+ const i18n = useMessageTemplate('');
+
+ useEffect(() => {
+ addTokenCleaner(cleaner);
+ }, [addTokenCleaner, cleaner]);
+
+ const updateLoginStatus = (url: string, token?: string) => {
+ changeBackend(url);
+ if (token)
+ updateToken(token);
+ };
+
+ return <InstanceContext.Provider value={{ id, token }}>
+ <Router>
+ <Route path={InstancePaths.details}
+ component={InstanceDetailsPage}
+ pushNotification={pushNotification}
+
+ onUnauthorized={() => <LoginPage
+ withMessage={{ message: i18n`Access denied`, description: i18n`Check
your token is valid`, type: 'ERROR', }}
+ onConfirm={updateLoginStatus} />}
+
+ onUpdate={() => {
+ route(`/instance/${id}/update`);
+ }}
+
+ onDelete={() => {
+ route(`/instances`);
+ }}
+
+ onLoadError={(e: SwrError) => {
+ pushNotification({ message: i18n`update_load_error`, type: 'ERROR',
params: e });
+ route(`/instance/${id}/`);
+ return <div />;
+ }} />
+
+ <Route path={InstancePaths.update}
+ component={InstanceUpdatePage}
+ // pushNotification={pushNotification}
+ onUnauthorized={() => <LoginPage
+ withMessage={{ message: i18n`Access denied`, description: i18n`Check
your token is valid`, type: 'ERROR', }}
+ onConfirm={updateLoginStatus} />}
+ onLoadError={(e: SwrError) => {
+ pushNotification({ message: i18n`update_load_error`, type: 'ERROR',
params: e });
+ route(`/instance/${id}/`);
+ return <div />;
+ }}
+ onBack={() => {
+ route(`/instance/${id}/`);
+ }}
+ onConfirm={() => {
+ pushNotification({ message: i18n`create_success`, type: 'SUCCESS' });
+ route(`/instance/${id}/`);
+ }}
+ onUpdateError={(e: Error) => {
+ pushNotification({ message: i18n`update_error`, type: 'ERROR',
params: e });
+ }} />
+
+ <Route default component={NotFoundPage} />
+ </Router>
+ </InstanceContext.Provider>;
+
+}
diff --git a/packages/frontend/src/components/auth/index.tsx
b/packages/frontend/src/components/auth/index.tsx
index a12022a..dc5c84a 100644
--- a/packages/frontend/src/components/auth/index.tsx
+++ b/packages/frontend/src/components/auth/index.tsx
@@ -23,7 +23,7 @@ import { h, VNode } from "preact";
import { Message } from "preact-messages";
import { useContext, useState } from "preact/hooks";
import { BackendContext } from "../../context/backend";
-import { Notification } from "../../declaration";
+import { Notification } from "../../utils/types";
interface Props {
withMessage?: Notification;
diff --git a/packages/frontend/src/components/notifications/index.tsx
b/packages/frontend/src/components/notifications/index.tsx
index 045b8e0..4219073 100644
--- a/packages/frontend/src/components/notifications/index.tsx
+++ b/packages/frontend/src/components/notifications/index.tsx
@@ -20,8 +20,7 @@
*/
import { h, VNode } from "preact";
-import { Message } from "preact-messages";
-import { MessageType, Notification } from "../../declaration";
+import { MessageType, Notification } from "../../utils/types";
interface Props {
notifications: Notification[];
diff --git a/packages/frontend/src/components/yup/YupField.tsx
b/packages/frontend/src/components/yup/YupField.tsx
index b6103a2..5000055 100644
--- a/packages/frontend/src/components/yup/YupField.tsx
+++ b/packages/frontend/src/components/yup/YupField.tsx
@@ -20,36 +20,20 @@
*/
import { h, VNode } from "preact";
-import { Message, useMessage } from "preact-messages";
-import { StateUpdater, useContext, useState } from "preact/hooks";
+import { StateUpdater, useContext } from "preact/hooks";
import { intervalToDuration, formatDuration } from 'date-fns'
import { BackendContext, ConfigContext } from '../../context/backend';
+import { YupObjectInput } from "./YupObjectInput";
+import { YupInput } from "./YupInput";
+import { YupInputArray } from "./YupInputArray";
+import { YupInputWithAddon } from "./YupInputWithAddon";
+import { YupInputSecured } from "./YupInputSecured";
function readableDuration(duration?: number): string {
if (!duration) return ""
return formatDuration(intervalToDuration({ start: 0, end: duration }))
}
-// customFormatDuration({ start: 0, end: 10800 * 1000}) // 3 hours
-// customFormatDuration({ start: 0, end: 108000 * 1000}) // 1 day 6 hours
-
-interface PropsInputInternal {
- name: string;
- value: string;
- readonly?: boolean;
- errors: any;
- onChange: any;
-}
-
-interface PropsObject {
- name: string;
- info: any;
- value: any;
- errors: any;
- onChange: any;
- readonly?: boolean;
-}
-
interface Props {
name: string;
field: string;
@@ -93,183 +77,3 @@ export function YupField({ name, field, errors, object,
valueHandler, info, read
}
}
-
-function YupObjectInput({ name, info, value, errors, onChange, readonly }:
PropsObject): VNode {
- const [active, setActive] = useState(false)
- return <div class="card">
- <header class="card-header">
- <p class="card-header-title">
- <Message id={`fields.instance.${name}.label`} />
- </p>
- <button class="card-header-icon" aria-label="more options" onClick={():
void => setActive(!active)}>
- <span class="icon">
- {active ?
- <i class="mdi mdi-arrow-up" /> :
- <i class="mdi mdi-arrow-down" />}
- </span>
- </button>
- </header>
- <div class={active ? "card-content" : "is-hidden"}>
- <div class="content">
- {Object.keys(info.fields).map(f => <YupField name={`${name}.${f}`}
- field={f} errors={errors} object={value}
- valueHandler={onChange} info={info.fields[f]}
- readonly={readonly}
- />)}
- </div>
- </div>
- </div>
-}
-
-function YupInput({ name, readonly, value, errors, onChange }:
PropsInputInternal): VNode {
- const placeholder = useMessage(`fields.instance.${name}.placeholder`)
- const tooltip = useMessage(`fields.instance.${name}.tooltip`)
-
- return <div class="field is-horizontal">
- <div class="field-label is-normal">
- <label class="label">
- <Message id={`fields.instance.${name}.label`} />
- {tooltip && <span class="icon" data-tooltip={tooltip}>
- <i class="mdi mdi-information" />
- </span>}
- </label>
- </div>
- <div class="field-body">
- <div class="field">
- <p class="control">
- <input class={errors[name] ? "input is-danger" : "input"} type="text"
- placeholder={placeholder} readonly={readonly}
- name={name} value={value} disabled={readonly}
- onChange={(e): void => onChange(e.currentTarget.value)} />
- <Message id={`fields.instance.${name}.help`} > </Message>
- </p>
- {errors[name] ? <p class="help is-danger">
- <Message id={`validation.${errors[name].type}`}
fields={errors[name].params}>{errors[name].message} </Message>
- </p> : null}
- </div>
- </div>
- </div>
-}
-
-function YupInputArray({ name, readonly, value, errors, onChange }:
PropsInputInternal): VNode {
- const placeholder = useMessage(`fields.instance.${name}.placeholder`)
- const tooltip = useMessage(`fields.instance.${name}.tooltip`)
-
- const array = value as unknown as string[] || []
- const [currentValue, setCurrentValue] = useState('')
-
- return <div class="field is-horizontal">
- <div class="field-label is-normal">
- <label class="label">
- <Message id={`fields.instance.${name}.label`} />
- {tooltip && <span class="icon" data-tooltip={tooltip}>
- <i class="mdi mdi-information" />
- </span>}
- </label>
- </div>
- <div class="field-body">
- <div class="field">
- <div class="field has-addons">
- <p class="control">
- <input class={errors[name] ? "input is-danger" : "input"}
type="text"
- placeholder={placeholder} readonly={readonly} disabled={readonly}
- name={name} value={currentValue}
- onChange={(e): void => setCurrentValue(e.currentTarget.value)} />
- <Message id={`fields.instance.${name}.help`} > </Message>
- </p>
- <p class="control">
- <button class="button is-info" onClick={(): void => {
- onChange([currentValue, ...array])
- setCurrentValue('')
- }} >add</button>
- </p>
- </div>
- {errors[name] ? <p class="help is-danger">
- <Message id={`validation.${errors[name].type}`}
fields={errors[name].params}>{errors[name].message}</Message>
- </p> : null}
- {array.map(v => <div class="tags has-addons">
- <span class="tag is-medium is-info">{v}</span>
- <a class="tag is-medium is-danger is-delete" onClick={() => {
- onChange(array.filter(f => f !== v))
- setCurrentValue(v)
- }} />
- </div>
- )}
- </div>
-
- </div>
- </div>
-}
-
-function YupInputWithAddon({ name, readonly, value, errors, onChange, addon,
atTheEnd }: PropsInputInternal & { addon: string; atTheEnd?: boolean }): VNode {
- const placeholder = useMessage(`fields.instance.${name}.placeholder`)
- const tooltip = useMessage(`fields.instance.${name}.tooltip`)
-
- return <div class="field is-horizontal">
- <div class="field-label is-normal">
- <label class="label">
- <Message id={`fields.instance.${name}.label`} />
- {tooltip && <span class="icon" data-tooltip={tooltip}>
- <i class="mdi mdi-information" />
- </span>}
- </label>
- </div>
- <div class="field-body">
- <div class="field">
- <div class="field has-addons">
- {!atTheEnd && <div class="control">
- <a class="button is-static">{addon}</a>
- </div>}
- <p class="control is-expanded">
- <input class={errors[name] ? "input is-danger" : "input"}
type="text"
- placeholder={placeholder} readonly={readonly} disabled={readonly}
- name={name} value={value}
- onChange={(e): void => onChange(e.currentTarget.value)} />
- <Message id={`fields.instance.${name}.help`} > </Message>
- </p>
- {atTheEnd && <div class="control">
- <a class="button is-static">{addon}</a>
- </div>}
- </div>
- {errors[name] ? <p class="help is-danger"><Message
id={`validation.${errors[name].type}`}
fields={errors[name].params}>{errors[name].message}</Message></p> : null}
- </div>
- </div>
- </div>
-}
-
-function YupInputSecured({ name, readonly, value, errors, onChange }:
PropsInputInternal): VNode {
- const placeholder = useMessage(`fields.instance.${name}.placeholder`, {})
- const tooltip = useMessage(`fields.instance.${name}.tooltip`, {})
-
- const [active, setActive] = useState(false)
-
- return <div class="field is-horizontal">
- <div class="field-label is-normal">
- <label class="label">
- <Message id={`fields.instance.${name}.label`} />
- {tooltip && <span class="icon" data-tooltip={tooltip}>
- <i class="mdi mdi-information" />
- </span>}
- </label>
- </div>
- <div class="field-body">
- <div class="field">
- <div class="field has-addons">
- <label class="b-checkbox checkbox">
- <input type="checkbox" checked={active} onClick={(): void => {
onChange(''); setActive(!active) }} />
- <span class="check" />
- </label>
- <p class="control">
- <input class="input" type="text"
- placeholder={placeholder} readonly={readonly || !active}
- disabled={readonly || !active}
- name={name} value={value}
- onChange={(e): void => onChange(e.currentTarget.value)} />
- <Message id={`fields.instance.${name}.help`}> </Message>
- </p>
- </div>
- {errors[name] ? <p class="help is-danger"><Message
id={`validation.${errors[name].type}`}
fields={errors[name].params}>{errors[name].message}</Message></p> : null}
- </div>
- </div>
- </div>
-}
diff --git a/packages/frontend/src/components/yup/YupInput.tsx
b/packages/frontend/src/components/yup/YupInput.tsx
new file mode 100644
index 0000000..aba089d
--- /dev/null
+++ b/packages/frontend/src/components/yup/YupInput.tsx
@@ -0,0 +1,60 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021 Taler Systems S.A.
+
+ GNU Taler is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+ */
+
+/**
+*
+* @author Sebastian Javier Marchano (sebasjm)
+*/
+import { h, VNode } from "preact";
+import { Message, useMessage } from "preact-messages";
+
+interface Props {
+ name: string;
+ value: string;
+ readonly?: boolean;
+ errors: any;
+ onChange: any;
+}
+
+export function YupInput({ name, readonly, value, errors, onChange }: Props):
VNode {
+ const placeholder = useMessage(`fields.instance.${name}.placeholder`);
+ const tooltip = useMessage(`fields.instance.${name}.tooltip`);
+
+ return <div class="field is-horizontal">
+ <div class="field-label is-normal">
+ <label class="label">
+ <Message id={`fields.instance.${name}.label`} />
+ {tooltip && <span class="icon" data-tooltip={tooltip}>
+ <i class="mdi mdi-information" />
+ </span>}
+ </label>
+ </div>
+ <div class="field-body">
+ <div class="field">
+ <p class="control">
+ <input class={errors[name] ? "input is-danger" : "input"} type="text"
+ placeholder={placeholder} readonly={readonly}
+ name={name} value={value} disabled={readonly}
+ onChange={(e): void => onChange(e.currentTarget.value)} />
+ <Message id={`fields.instance.${name}.help`}> </Message>
+ </p>
+ {errors[name] ? <p class="help is-danger">
+ <Message id={`validation.${errors[name].type}`}
fields={errors[name].params}>{errors[name].message} </Message>
+ </p> : null}
+ </div>
+ </div>
+ </div>;
+}
diff --git a/packages/frontend/src/components/yup/YupInputArray.tsx
b/packages/frontend/src/components/yup/YupInputArray.tsx
new file mode 100644
index 0000000..c9387fe
--- /dev/null
+++ b/packages/frontend/src/components/yup/YupInputArray.tsx
@@ -0,0 +1,81 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021 Taler Systems S.A.
+
+ GNU Taler is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+ */
+
+/**
+*
+* @author Sebastian Javier Marchano (sebasjm)
+*/
+import { h, VNode } from "preact";
+import { Message, useMessage } from "preact-messages";
+import { useState } from "preact/hooks";
+
+export interface Props {
+ name: string;
+ value: string;
+ readonly?: boolean;
+ errors: any;
+ onChange: any;
+}
+
+export function YupInputArray({ name, readonly, value, errors, onChange }:
Props): VNode {
+ const placeholder = useMessage(`fields.instance.${name}.placeholder`);
+ const tooltip = useMessage(`fields.instance.${name}.tooltip`);
+
+ const array = value as unknown as string[] || [];
+ const [currentValue, setCurrentValue] = useState('');
+
+ return <div class="field is-horizontal">
+ <div class="field-label is-normal">
+ <label class="label">
+ <Message id={`fields.instance.${name}.label`} />
+ {tooltip && <span class="icon" data-tooltip={tooltip}>
+ <i class="mdi mdi-information" />
+ </span>}
+ </label>
+ </div>
+ <div class="field-body">
+ <div class="field">
+ <div class="field has-addons">
+ <p class="control">
+ <input class={errors[name] ? "input is-danger" : "input"}
type="text"
+ placeholder={placeholder} readonly={readonly} disabled={readonly}
+ name={name} value={currentValue}
+ onChange={(e): void => setCurrentValue(e.currentTarget.value)} />
+ <Message id={`fields.instance.${name}.help`}> </Message>
+ </p>
+ <p class="control">
+ <button class="button is-info" onClick={(): void => {
+ onChange([currentValue, ...array]);
+ setCurrentValue('');
+ }}>add</button>
+ </p>
+ </div>
+ {errors[name] ? <p class="help is-danger">
+ <Message id={`validation.${errors[name].type}`}
fields={errors[name].params}>{errors[name].message}</Message>
+ </p> : null}
+ {array.map(v => <div class="tags has-addons">
+ <span class="tag is-medium is-info">{v}</span>
+ <a class="tag is-medium is-danger is-delete" onClick={() => {
+ onChange(array.filter(f => f !== v));
+ setCurrentValue(v);
+ }} />
+ </div>
+ )}
+ </div>
+
+ </div>
+ </div>;
+}
diff --git a/packages/frontend/src/components/yup/YupInputSecured.tsx
b/packages/frontend/src/components/yup/YupInputSecured.tsx
new file mode 100644
index 0000000..5fac0dc
--- /dev/null
+++ b/packages/frontend/src/components/yup/YupInputSecured.tsx
@@ -0,0 +1,68 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021 Taler Systems S.A.
+
+ GNU Taler is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+ */
+
+/**
+*
+* @author Sebastian Javier Marchano (sebasjm)
+*/
+import { h, VNode } from "preact";
+import { Message, useMessage } from "preact-messages";
+import { useState } from "preact/hooks";
+
+export interface Props {
+ name: string;
+ value: string;
+ readonly?: boolean;
+ errors: any;
+ onChange: any;
+}
+
+export function YupInputSecured({ name, readonly, value, errors, onChange }:
Props): VNode {
+ const placeholder = useMessage(`fields.instance.${name}.placeholder`, {});
+ const tooltip = useMessage(`fields.instance.${name}.tooltip`, {});
+
+ const [active, setActive] = useState(false);
+
+ return <div class="field is-horizontal">
+ <div class="field-label is-normal">
+ <label class="label">
+ <Message id={`fields.instance.${name}.label`} />
+ {tooltip && <span class="icon" data-tooltip={tooltip}>
+ <i class="mdi mdi-information" />
+ </span>}
+ </label>
+ </div>
+ <div class="field-body">
+ <div class="field">
+ <div class="field has-addons">
+ <label class="b-checkbox checkbox">
+ <input type="checkbox" checked={active} onClick={(): void => {
onChange(''); setActive(!active); }} />
+ <span class="check" />
+ </label>
+ <p class="control">
+ <input class="input" type="text"
+ placeholder={placeholder} readonly={readonly || !active}
+ disabled={readonly || !active}
+ name={name} value={value}
+ onChange={(e): void => onChange(e.currentTarget.value)} />
+ <Message id={`fields.instance.${name}.help`}> </Message>
+ </p>
+ </div>
+ {errors[name] ? <p class="help is-danger"><Message
id={`validation.${errors[name].type}`}
fields={errors[name].params}>{errors[name].message}</Message></p> : null}
+ </div>
+ </div>
+ </div>;
+}
diff --git a/packages/frontend/src/components/yup/YupInputWithAddon.tsx
b/packages/frontend/src/components/yup/YupInputWithAddon.tsx
new file mode 100644
index 0000000..f789de7
--- /dev/null
+++ b/packages/frontend/src/components/yup/YupInputWithAddon.tsx
@@ -0,0 +1,68 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021 Taler Systems S.A.
+
+ GNU Taler is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+ */
+
+/**
+*
+* @author Sebastian Javier Marchano (sebasjm)
+*/
+import { h, VNode } from "preact";
+import { Message, useMessage } from "preact-messages";
+
+export interface Props {
+ name: string;
+ value: string;
+ readonly?: boolean;
+ errors: any;
+ onChange: any;
+ addon: string;
+ atTheEnd?: boolean;
+}
+
+export function YupInputWithAddon({ name, readonly, value, errors, onChange,
addon, atTheEnd }: Props): VNode {
+ const placeholder = useMessage(`fields.instance.${name}.placeholder`);
+ const tooltip = useMessage(`fields.instance.${name}.tooltip`);
+
+ return <div class="field is-horizontal">
+ <div class="field-label is-normal">
+ <label class="label">
+ <Message id={`fields.instance.${name}.label`} />
+ {tooltip && <span class="icon" data-tooltip={tooltip}>
+ <i class="mdi mdi-information" />
+ </span>}
+ </label>
+ </div>
+ <div class="field-body">
+ <div class="field">
+ <div class="field has-addons">
+ {!atTheEnd && <div class="control">
+ <a class="button is-static">{addon}</a>
+ </div>}
+ <p class="control is-expanded">
+ <input class={errors[name] ? "input is-danger" : "input"}
type="text"
+ placeholder={placeholder} readonly={readonly} disabled={readonly}
+ name={name} value={value}
+ onChange={(e): void => onChange(e.currentTarget.value)} />
+ <Message id={`fields.instance.${name}.help`}> </Message>
+ </p>
+ {atTheEnd && <div class="control">
+ <a class="button is-static">{addon}</a>
+ </div>}
+ </div>
+ {errors[name] ? <p class="help is-danger"><Message
id={`validation.${errors[name].type}`}
fields={errors[name].params}>{errors[name].message}</Message></p> : null}
+ </div>
+ </div>
+ </div>;
+}
diff --git a/packages/frontend/src/components/yup/YupObjectInput.tsx
b/packages/frontend/src/components/yup/YupObjectInput.tsx
new file mode 100644
index 0000000..76598e0
--- /dev/null
+++ b/packages/frontend/src/components/yup/YupObjectInput.tsx
@@ -0,0 +1,59 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021 Taler Systems S.A.
+
+ GNU Taler is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+ */
+
+/**
+*
+* @author Sebastian Javier Marchano (sebasjm)
+*/
+import { h, VNode } from "preact";
+import { Message } from "preact-messages";
+import { useState } from "preact/hooks";
+import { YupField } from "./YupField";
+
+export interface PropsObject {
+ name: string;
+ info: any;
+ value: any;
+ errors: any;
+ onChange: any;
+ readonly?: boolean;
+}
+
+export function YupObjectInput({ name, info, value, errors, onChange, readonly
}: PropsObject): VNode {
+ const [active, setActive] = useState(false);
+ return <div class="card">
+ <header class="card-header">
+ <p class="card-header-title">
+ <Message id={`fields.instance.${name}.label`} />
+ </p>
+ <button class="card-header-icon" aria-label="more options" onClick={():
void => setActive(!active)}>
+ <span class="icon">
+ {active ?
+ <i class="mdi mdi-arrow-up" /> :
+ <i class="mdi mdi-arrow-down" />}
+ </span>
+ </button>
+ </header>
+ <div class={active ? "card-content" : "is-hidden"}>
+ <div class="content">
+ {Object.keys(info.fields).map(f => <YupField name={`${name}.${f}`}
+ field={f} errors={errors} object={value}
+ valueHandler={onChange} info={info.fields[f]}
+ readonly={readonly} />)}
+ </div>
+ </div>
+ </div>;
+}
diff --git a/packages/frontend/src/declaration.d.ts
b/packages/frontend/src/declaration.d.ts
index 6e420c4..a1d4b65 100644
--- a/packages/frontend/src/declaration.d.ts
+++ b/packages/frontend/src/declaration.d.ts
@@ -20,19 +20,6 @@
*/
-interface KeyValue {
- [key: string]: string;
-}
-
-interface Notification {
- message: string;
- description?: string;
- type: MessageType;
- params?: any;
-}
-
-type ValueOrFunction<T> = T | ((p: T) => T)
-type MessageType = 'INFO' | 'WARN' | 'ERROR' | 'SUCCESS'
type EddsaPublicKey = string;
type RelativeTime = Duration;
diff --git a/packages/frontend/src/hooks/index.ts
b/packages/frontend/src/hooks/index.ts
index b832613..edd76b8 100644
--- a/packages/frontend/src/hooks/index.ts
+++ b/packages/frontend/src/hooks/index.ts
@@ -20,7 +20,16 @@
*/
import { StateUpdater, useEffect, useState } from "preact/hooks";
-import { ValueOrFunction } from '../declaration';
+import { ValueOrFunction } from '../utils/types';
+
+
+export function useBackendContextState() {
+ const [lang, setLang] = useLang()
+ const [url, changeBackend] = useBackendURL();
+ const [token, updateToken] = useBackendDefaultToken();
+
+ return { url, token, changeBackend, updateToken, lang, setLang }
+}
export function useBackendURL(): [string, StateUpdater<string>] {
const [value, setter] = useNotNullLocalStorage('backend-url', typeof window
!== 'undefined' ? window.location.origin : '')
@@ -112,3 +121,4 @@ export function useNotNullLocalStorage(key: string,
initialValue: string): [stri
return [storedValue, setValue];
}
+
diff --git a/packages/frontend/src/hooks/notifications.ts
b/packages/frontend/src/hooks/notifications.ts
index c66594c..1c0c373 100644
--- a/packages/frontend/src/hooks/notifications.ts
+++ b/packages/frontend/src/hooks/notifications.ts
@@ -20,7 +20,7 @@
*/
import { useState } from "preact/hooks";
-import { Notification } from '../declaration';
+import { Notification } from '../utils/types';
interface Result {
notifications: Notification[];
diff --git a/packages/frontend/src/index.tsx b/packages/frontend/src/index.tsx
index a4885cc..6a6444f 100644
--- a/packages/frontend/src/index.tsx
+++ b/packages/frontend/src/index.tsx
@@ -23,54 +23,60 @@ import "./scss/main.scss"
import { h, VNode } from 'preact';
import { useCallback, useContext, useEffect, useState } from "preact/hooks";
-import { Route, Router, route } from 'preact-router';
-import { MessageError, MessageProvider, useMessageTemplate } from
'preact-messages';
+import { Route, route } from 'preact-router';
+import { MessageProvider, useMessageTemplate } from 'preact-messages';
-import { Notification } from "./declaration";
import { Sidebar } from './components/sidebar';
import { NavigationBar } from './components/navbar';
import { Notifications } from './components/notifications';
import * as messages from './messages'
-import { useBackendURL, useBackendDefaultToken, useLang,
useBackendInstanceToken } from './hooks';
+import { useBackendContextState } from './hooks';
import { useNotifications } from "./hooks/notifications";
-import { BackendContext, ConfigContext, InstanceContext } from
'./context/backend';
-import { SwrError, useBackendConfig } from "./hooks/backend";
+import { BackendContext, ConfigContext } from './context/backend';
+import { useBackendConfig } from "./hooks/backend";
+import { hasKey, onTranslationError } from "./utils/functions";
-import NotFoundPage from './routes/notfound';
-import Login from './routes/login';
-import Instances from './routes/instances/list';
-import Create from "./routes/instances/create";
-import Details from "./routes/instances/details";
-import Update from "./routes/instances/update";
+import LoginPage from './routes/login';
+import { ApplicationReadyRoutes } from "./ApplicationReadyRoutes";
-enum RootPages {
+export enum RootPaths {
root = '/',
- instances = '/instances',
- new = '/new',
+ list_instances = '/instances',
+ new_instance = '/new',
instance_id_route = '/instance/:id/:rest*',
}
-enum InstancePages {
+export enum InstancePaths {
details = '/instance/:id/',
update = '/instance/:id/update',
}
-function Redirect({ to }: { to: string }): null {
+export function Redirect({ to }: { to: string }): null {
useEffect(() => {
route(to, true)
})
return null
}
+export default function Application(): VNode {
+ const state = useBackendContextState()
+ return (
+ <BackendContext.Provider value={state}>
+ <MessageProvider locale={state.lang} onError={onTranslationError}
messages={hasKey(messages, state.lang) ? messages[state.lang] : messages.en}
pathSep={null as any} >
+ <ApplicationStatusRoutes />
+ </MessageProvider >
+ </BackendContext.Provider>
+ );
+}
-function AppRouting(): VNode {
+function ApplicationStatusRoutes(): VNode {
const { notifications, pushNotification, removeNotification } =
useNotifications()
const { lang, setLang, changeBackend, updateToken } =
useContext(BackendContext)
const backendConfig = useBackendConfig();
const i18n = useMessageTemplate('')
- const LoginWithError = () => <Login
+ const LoginWithError = () => <LoginPage
withMessage={{
message: i18n`Couldnt access the server`,
type: 'ERROR',
@@ -79,187 +85,24 @@ function AppRouting(): VNode {
onConfirm={(url: string, token?: string) => {
changeBackend(url)
if (token) updateToken(token)
- route(RootPages.instances)
+ route(RootPaths.list_instances)
}}
/>
- const cleaner = useCallback(() =>{updateToken(undefined)},[])
-
+ const cleaner = useCallback(() => { updateToken(undefined) }, [])
const [cleaners, setCleaners] = useState([cleaner])
- const addTokenCleaner = (c:() => void) => setCleaners(cs => [...cs,c])
- const addTokenCleanerNemo = useCallback((c:() => void)
=>{addTokenCleaner(c)},[cleaner])
+ const addTokenCleaner = (c: () => void) => setCleaners(cs => [...cs, c])
+ const addTokenCleanerNemo = useCallback((c: () => void) => {
addTokenCleaner(c) }, [cleaner])
return <div id="app">
<ConfigContext.Provider value={backendConfig.data || {}}>
- <NavigationBar lang={lang} setLang={setLang} onLogout={() => {
cleaners.forEach( c => c() ) }} />
+ <NavigationBar lang={lang} setLang={setLang} onLogout={() => {
cleaners.forEach(c => c()) }} />
<Sidebar />
<Notifications notifications={notifications}
removeNotification={removeNotification} />
{!backendConfig.data ?
<Route default component={LoginWithError} /> :
- <Route default component={AppReady}
pushNotification={pushNotification} addTokenCleaner={addTokenCleanerNemo} />
+ <Route default component={ApplicationReadyRoutes}
pushNotification={pushNotification} addTokenCleaner={addTokenCleanerNemo} />
}
</ConfigContext.Provider>
</div>
}
-
-function AppReady({ pushNotification,addTokenCleaner }: { pushNotification:
(n: Notification) => void, addTokenCleaner: any }): VNode {
- const { changeBackend, updateToken } = useContext(BackendContext)
-
- const updateLoginStatus = (url: string, token?: string) => {
- changeBackend(url)
- if (token) updateToken(token)
- }
- const i18n = useMessageTemplate('')
-
- return <Router>
- <Route path={RootPages.root} component={Redirect} to={RootPages.instances}
/>
-
- <Route path={RootPages.instances}
- component={Instances}
-
- onCreate={() => {
- route(RootPages.new)
- }}
-
- onUpdate={(id: string): void => {
- route(`/instance/${id}/`)
- }}
-
- onUnauthorized={() => <Login
- withMessage={{ message: i18n`Access denied`, description: i18n`Check
your token is valid`, type: 'ERROR', }}
- onConfirm={updateLoginStatus}
- />}
-
- onError={(error: SwrError) => {
- pushNotification({ message: i18n`error`, params: error, type: 'ERROR'
})
- return <div />
- }}
- />
-
- <Route path={RootPages.new}
- component={Create}
- onBack={() => route(RootPages.instances)}
-
- onConfirm={() => {
- pushNotification({ message: i18n`create_success`, type: 'SUCCESS' })
- route(RootPages.instances)
- }}
-
- onError={(error: any) => {
- pushNotification({ message: i18n`create_error`, type: 'ERROR', params:
error })
- }}
- />
-
- <Route path={RootPages.instance_id_route} component={SubPages}
pushNotification={pushNotification} addTokenCleaner={addTokenCleaner} />
-
- <Route default component={NotFoundPage} />
-
- </Router>
-}
-
-function hasKey<O>(obj: O, key: string | number | symbol): key is keyof O {
- return key in obj
-}
-
-function useBackendContextState() {
- const [lang, setLang] = useLang()
- const [url, changeBackend] = useBackendURL();
- const [token, updateToken] = useBackendDefaultToken();
-
- return { url, token, changeBackend, updateToken, lang, setLang }
-}
-
-function onTranslationError(error: MessageError) {
- if (typeof window === "undefined") return;
- (window as any)['missing_locale'] = ([] as string[]).concat((window as
any)['missing_locale']).concat(error.path.join())
-}
-
-export default function Application(): VNode {
- const state = useBackendContextState()
-
- return (
- <BackendContext.Provider value={state}>
- <MessageProvider locale={state.lang} onError={onTranslationError}
messages={hasKey(messages, state.lang) ? messages[state.lang] : messages.en}
pathSep={null as any} >
- <AppRouting />
- </MessageProvider >
- </BackendContext.Provider>
- );
-}
-interface SubPagesProps {
- id: string;
- pushNotification: (n: Notification) => void;
- addTokenCleaner: any;
-}
-
-
-function SubPages({ id, pushNotification, addTokenCleaner }: SubPagesProps):
VNode {
- const [token, updateToken] = useBackendInstanceToken(id);
- const { changeBackend } = useContext(BackendContext)
- const cleaner = useCallback(() =>{updateToken(undefined)},[id])
- const i18n = useMessageTemplate('')
-
- useEffect(() => {
- addTokenCleaner(cleaner)
- }, [addTokenCleaner, cleaner])
-
- const updateLoginStatus = (url: string, token?: string) => {
- changeBackend(url)
- if (token) updateToken(token)
- }
-
- return <InstanceContext.Provider value={{id, token}}>
- <Router>
- <Route path={InstancePages.details}
- component={Details}
- pushNotification={pushNotification}
-
- onUnauthorized={() => <Login
- withMessage={{ message: i18n`Access denied`, description: i18n`Check
your token is valid`, type: 'ERROR', }}
- onConfirm={updateLoginStatus}
- />}
-
- onUpdate={() => {
- route(`/instance/${id}/update`)
- }}
-
- onDelete={() => {
- route(`/instances`)
- }}
-
- onLoadError={(e: SwrError) => {
- pushNotification({ message: i18n`update_load_error`, type: 'ERROR',
params: e })
- route(`/instance/${id}/`)
- return <div />
- }}
- />
-
- <Route path={InstancePages.update}
- component={Update}
- // pushNotification={pushNotification}
-
- onUnauthorized={() => <Login
- withMessage={{ message: i18n`Access denied`, description: i18n`Check
your token is valid`, type: 'ERROR', }}
- onConfirm={updateLoginStatus}
- />}
- onLoadError={(e: SwrError) => {
- pushNotification({ message: i18n`update_load_error`, type: 'ERROR',
params: e })
- route(`/instance/${id}/`)
- return <div />
- }}
- onBack={() => {
- route(`/instance/${id}/`)
- }}
- onConfirm={() => {
- pushNotification({ message: i18n`create_success`, type: 'SUCCESS' })
- route(`/instance/${id}/`)
- }}
- onUpdateError={(e: Error) => {
- pushNotification({ message: i18n`update_error`, type: 'ERROR',
params: e })
- }}
- />
-
- <Route default component={NotFoundPage} />
- </Router>
- </InstanceContext.Provider>
-
-}
\ No newline at end of file
diff --git a/packages/frontend/src/routes/instances/details/index.tsx
b/packages/frontend/src/routes/instances/details/index.tsx
index da57b27..d16565b 100644
--- a/packages/frontend/src/routes/instances/details/index.tsx
+++ b/packages/frontend/src/routes/instances/details/index.tsx
@@ -16,7 +16,7 @@
import { Fragment, h, VNode } from "preact";
import { useContext, useState } from "preact/hooks";
import { InstanceContext } from "../../../context/backend";
-import { Notification } from "../../../declaration";
+import { Notification } from "../../../utils/types";
import { useBackendInstance, useBackendInstanceMutateAPI, SwrError } from
"../../../hooks/backend";
import { DeleteModal } from "../list/DeleteModal";
import { DetailPage } from "./DetailPage";
diff --git a/packages/frontend/src/routes/instances/list/index.tsx
b/packages/frontend/src/routes/instances/list/index.tsx
index ba63b3e..76947d7 100644
--- a/packages/frontend/src/routes/instances/list/index.tsx
+++ b/packages/frontend/src/routes/instances/list/index.tsx
@@ -23,7 +23,8 @@ import { Fragment, h, VNode } from 'preact';
import { View } from './View';
import { useBackendInstances, useBackendInstanceMutateAPI, SwrError } from
'../../../hooks/backend';
import { useState } from 'preact/hooks';
-import { MerchantBackend, Notification } from '../../../declaration';
+import { MerchantBackend } from '../../../declaration';
+import { Notification } from '../../../utils/types';
import { DeleteModal } from './DeleteModal';
interface Props {
pushNotification: (n: Notification) => void;
@@ -43,7 +44,7 @@ export default function Instances({ pushNotification,
onUnauthorized, onError, o
if (!list.data) {
if (list.unauthorized) return onUnauthorized()
- if (list.error) return onError(list.error)
+ if (list.error) return onError(list.error)
}
return <Fragment>
@@ -54,9 +55,9 @@ export default function Instances({ pushNotification,
onUnauthorized, onError, o
onUpdate={onUpdate}
selected={!!deleting}
/>
- {deleting && <DeleteModal
- element={deleting}
- onCancel={() => setDeleting(null) }
+ {deleting && <DeleteModal
+ element={deleting}
+ onCancel={() => setDeleting(null)}
onConfirm={async (): Promise<void> => {
try {
await deleteInstance()
@@ -65,7 +66,7 @@ export default function Instances({ pushNotification,
onUnauthorized, onError, o
pushNotification({ message: 'delete_error', type: 'ERROR', params:
error })
}
setDeleting(null)
- }}
+ }}
/>}
</Fragment>;
}
diff --git a/packages/frontend/src/routes/login/index.tsx
b/packages/frontend/src/routes/login/index.tsx
index b8cc5d6..909bd10 100644
--- a/packages/frontend/src/routes/login/index.tsx
+++ b/packages/frontend/src/routes/login/index.tsx
@@ -20,7 +20,7 @@
*/
import { h, VNode } from "preact";
import { LoginModal } from '../../components/auth';
-import { Notification } from "../../declaration";
+import { Notification } from "../../utils/types";
interface Props {
withMessage?: Notification;
diff --git a/packages/frontend/src/schemas/index.ts
b/packages/frontend/src/schemas/index.ts
index 054b363..a2def9d 100644
--- a/packages/frontend/src/schemas/index.ts
+++ b/packages/frontend/src/schemas/index.ts
@@ -20,7 +20,7 @@
*/
import * as yup from 'yup';
-import { AMOUNT_REGEX, PAYTO_REGEX } from "../constants";
+import { AMOUNT_REGEX, PAYTO_REGEX } from "../utils/constants";
yup.setLocale({
mixed: {
diff --git a/packages/frontend/src/constants.ts
b/packages/frontend/src/utils/constants.ts
similarity index 100%
copy from packages/frontend/src/constants.ts
copy to packages/frontend/src/utils/constants.ts
diff --git a/packages/frontend/src/constants.ts
b/packages/frontend/src/utils/functions.ts
similarity index 64%
copy from packages/frontend/src/constants.ts
copy to packages/frontend/src/utils/functions.ts
index adc25a4..f51aacf 100644
--- a/packages/frontend/src/constants.ts
+++ b/packages/frontend/src/utils/functions.ts
@@ -14,10 +14,13 @@
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
- /**
- *
- * @author Sebastian Javier Marchano (sebasjm)
- */
+import { MessageError } from "preact-messages";
+
+export function hasKey<O>(obj: O, key: string | number | symbol): key is keyof
O {
+ return key in obj
+}
-export const
PAYTO_REGEX=/^payto:\/\/[a-zA-Z][a-zA-Z0-9-.]*(\/[a-zA-Z0-9\-\.\~\(\)@_%:!$&'*+,;=]*)*\??((amount|receiver-name|sender-name|instruction|message)=[a-zA-Z0-9\-\.\~\(\)@_%:!$'*+,;=]*&?)*$/
-export const AMOUNT_REGEX=/^[a-zA-Z][a-zA-Z]*:[0-9][0-9,]*\.?[0-9,]*$/
+export function onTranslationError(error: MessageError) {
+ if (typeof window === "undefined") return;
+ (window as any)['missing_locale'] = ([] as string[]).concat((window as
any)['missing_locale']).concat(error.path.join())
+}
diff --git a/packages/frontend/src/constants.ts
b/packages/frontend/src/utils/types.ts
similarity index 67%
rename from packages/frontend/src/constants.ts
rename to packages/frontend/src/utils/types.ts
index adc25a4..92d9692 100644
--- a/packages/frontend/src/constants.ts
+++ b/packages/frontend/src/utils/types.ts
@@ -14,10 +14,17 @@
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
- /**
- *
- * @author Sebastian Javier Marchano (sebasjm)
- */
+export interface KeyValue {
+ [key: string]: string;
+}
+
+export interface Notification {
+ message: string;
+ description?: string;
+ type: MessageType;
+ params?: any;
+}
+
+export type ValueOrFunction<T> = T | ((p: T) => T)
+export type MessageType = 'INFO' | 'WARN' | 'ERROR' | 'SUCCESS'
-export const
PAYTO_REGEX=/^payto:\/\/[a-zA-Z][a-zA-Z0-9-.]*(\/[a-zA-Z0-9\-\.\~\(\)@_%:!$&'*+,;=]*)*\??((amount|receiver-name|sender-name|instruction|message)=[a-zA-Z0-9\-\.\~\(\)@_%:!$'*+,;=]*&?)*$/
-export const AMOUNT_REGEX=/^[a-zA-Z][a-zA-Z]*:[0-9][0-9,]*\.?[0-9,]*$/
diff --git a/packages/frontend/tests/functions/regex.test.ts
b/packages/frontend/tests/functions/regex.test.ts
index b2e0f39..fc8a6a4 100644
--- a/packages/frontend/tests/functions/regex.test.ts
+++ b/packages/frontend/tests/functions/regex.test.ts
@@ -19,7 +19,7 @@
* @author Sebastian Javier Marchano (sebasjm)
*/
-import { AMOUNT_REGEX, PAYTO_REGEX } from "../../src/constants";
+import { AMOUNT_REGEX, PAYTO_REGEX } from "../../src/utils/constants";
describe('payto uri format', () => {
const valids = [
--
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: refactor src/index to split routes,
gnunet <=