gnunet-svn
[Top][All Lists]
Advanced

[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.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]