gnunet-svn
[Top][All Lists]
Advanced

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

[taler-wallet-core] branch master updated (c34e71cf3 -> af29a02e5)


From: gnunet
Subject: [taler-wallet-core] branch master updated (c34e71cf3 -> af29a02e5)
Date: Wed, 26 Oct 2022 21:04:24 +0200

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

sebasjm pushed a change to branch master
in repository wallet-core.

    from c34e71cf3 prevent form submitting
     new b4bad2dea pretty
     new af29a02e5 using util to parse uri, some more fixs

The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 packages/demobank-ui/package.json                  |   1 +
 .../demobank-ui/src/components/AsyncButton.tsx     |  16 +-
 packages/demobank-ui/src/components/FileButton.tsx |  13 +-
 .../demobank-ui/src/components/Notifications.tsx   |  24 +-
 packages/demobank-ui/src/components/QR.tsx         |  18 +-
 .../src/components/fields/DateInput.tsx            |  22 +-
 .../src/components/fields/EmailInput.tsx           |  16 +-
 .../src/components/fields/FileInput.tsx            |  29 +-
 .../src/components/fields/ImageInput.tsx           |  21 +-
 .../src/components/fields/NumberInput.tsx          |  16 +-
 .../src/components/fields/TextInput.tsx            |  22 +-
 .../src/components/menu/LangSelector.tsx           |  63 +-
 .../src/components/menu/NavigationBar.tsx          |   6 +-
 .../demobank-ui/src/components/menu/SideBar.tsx    |   8 +-
 packages/demobank-ui/src/components/menu/index.tsx |  24 +-
 .../src/components/picker/DatePicker.tsx           | 107 ++-
 .../components/picker/DurationPicker.stories.tsx   |  12 +-
 .../src/components/picker/DurationPicker.tsx       |  27 +-
 packages/demobank-ui/src/context/translation.ts    |  21 +-
 packages/demobank-ui/src/declaration.d.ts          |  10 +-
 packages/demobank-ui/src/hooks/async.ts            |   6 +-
 packages/demobank-ui/src/i18n/index.tsx            |  50 +-
 packages/demobank-ui/src/i18n/strings.ts           | 647 ++++++-----------
 packages/demobank-ui/src/index.html                |  18 +-
 packages/demobank-ui/src/manifest.json             |   2 +-
 packages/demobank-ui/src/pages/home/index.tsx      | 120 +++-
 .../src/pages/profile/index.stories.tsx            |  20 +-
 .../demobank-ui/src/scss/_custom-calendar.scss     |  12 +-
 packages/demobank-ui/src/scss/bank.scss            | 250 +++----
 packages/demobank-ui/src/scss/colors-bank.scss     |   2 +-
 packages/demobank-ui/src/scss/fonts/nunito.css     |   4 +-
 packages/demobank-ui/src/scss/libs/_all.scss       |   2 +-
 packages/demobank-ui/src/scss/pure.scss            | 783 +++++++++++----------
 pnpm-lock.yaml                                     |   2 +
 34 files changed, 1091 insertions(+), 1303 deletions(-)

diff --git a/packages/demobank-ui/package.json 
b/packages/demobank-ui/package.json
index 0f0c6f8e1..a8f390611 100644
--- a/packages/demobank-ui/package.json
+++ b/packages/demobank-ui/package.json
@@ -14,6 +14,7 @@
     "jed": "1.1.1",
     "preact": "10.6.5",
     "preact-router": "3.2.1",
+    "@gnu-taler/taler-util": "workspace:*",
     "qrcode-generator": "^1.4.4",
     "react": "npm:@preact/compat@^17.1.2",
     "swr": "1.3.0"
diff --git a/packages/demobank-ui/src/components/AsyncButton.tsx 
b/packages/demobank-ui/src/components/AsyncButton.tsx
index 0c4305668..eec11f4a1 100644
--- a/packages/demobank-ui/src/components/AsyncButton.tsx
+++ b/packages/demobank-ui/src/components/AsyncButton.tsx
@@ -19,10 +19,10 @@
  * @author Sebastian Javier Marchano (sebasjm)
  */
 
-import { ComponentChildren, h, VNode } from 'preact';
-import { useLayoutEffect, useRef } from 'preact/hooks';
+import { ComponentChildren, h, VNode } from "preact";
+import { useLayoutEffect, useRef } from "preact/hooks";
 // import { LoadingModal } from "../modal";
-import { useAsync } from '../hooks/async';
+import { useAsync } from "../hooks/async";
 // import { Translate } from "../../i18n";
 
 type Props = {
@@ -44,20 +44,16 @@ export function AsyncButton({
 
   const buttonRef = useRef<HTMLButtonElement>(null);
   useLayoutEffect(() => {
-    if (grabFocus) 
-      buttonRef.current?.focus();
-    
+    if (grabFocus) buttonRef.current?.focus();
   }, [grabFocus]);
 
   // if (isSlow) {
   //   return <LoadingModal onCancel={cancel} />;
   // }
-  if (isLoading) 
-    return <button class="button">Loading...</button>;
-  
+  if (isLoading) return <button class="button">Loading...</button>;
 
   return (
-    <span data-tooltip={rest['data-tooltip']} style={{ marginLeft: 5 }}>
+    <span data-tooltip={rest["data-tooltip"]} style={{ marginLeft: 5 }}>
       <button {...rest} ref={buttonRef} onClick={request} disabled={disabled}>
         {children}
       </button>
diff --git a/packages/demobank-ui/src/components/FileButton.tsx 
b/packages/demobank-ui/src/components/FileButton.tsx
index dba86ccbf..7fad7f03a 100644
--- a/packages/demobank-ui/src/components/FileButton.tsx
+++ b/packages/demobank-ui/src/components/FileButton.tsx
@@ -1,5 +1,5 @@
-import { h, VNode } from 'preact';
-import { useRef, useState } from 'preact/hooks';
+import { h, VNode } from "preact";
+import { useRef, useState } from "preact/hooks";
 
 const MAX_IMAGE_UPLOAD_SIZE = 1024 * 1024;
 
@@ -23,13 +23,12 @@ export function FileButton(props: Props): VNode {
       </button>
       <input
         ref={fileInputRef}
-        style={{ display: 'none' }}
+        style={{ display: "none" }}
         type="file"
         onChange={(e) => {
           const f: FileList | null = e.currentTarget.files;
-          if (!f || f.length != 1) 
-            return props.onChange(undefined);
-          
+          if (!f || f.length != 1) return props.onChange(undefined);
+
           console.log(f);
           if (f[0].size > MAX_IMAGE_UPLOAD_SIZE) {
             setSizeError(true);
@@ -39,7 +38,7 @@ export function FileButton(props: Props): VNode {
           return f[0].arrayBuffer().then((b) => {
             const content = new Uint8Array(b).reduce(
               (data, byte) => data + String.fromCharCode(byte),
-              '',
+              "",
             );
             return props.onChange({
               content,
diff --git a/packages/demobank-ui/src/components/Notifications.tsx 
b/packages/demobank-ui/src/components/Notifications.tsx
index 09329442a..e34550386 100644
--- a/packages/demobank-ui/src/components/Notifications.tsx
+++ b/packages/demobank-ui/src/components/Notifications.tsx
@@ -19,7 +19,7 @@
  * @author Sebastian Javier Marchano (sebasjm)
  */
 
-import { h, VNode } from 'preact';
+import { h, VNode } from "preact";
 
 export interface Notification {
   message: string;
@@ -27,7 +27,7 @@ export interface Notification {
   type: MessageType;
 }
 
-export type MessageType = 'INFO' | 'WARN' | 'ERROR' | 'SUCCESS';
+export type MessageType = "INFO" | "WARN" | "ERROR" | "SUCCESS";
 
 interface Props {
   notifications: Notification[];
@@ -36,16 +36,16 @@ interface Props {
 
 function messageStyle(type: MessageType): string {
   switch (type) {
-  case 'INFO':
-    return 'message is-info';
-  case 'WARN':
-    return 'message is-warning';
-  case 'ERROR':
-    return 'message is-danger';
-  case 'SUCCESS':
-    return 'message is-success';
-  default:
-    return 'message';
+    case "INFO":
+      return "message is-info";
+    case "WARN":
+      return "message is-warning";
+    case "ERROR":
+      return "message is-danger";
+    case "SUCCESS":
+      return "message is-success";
+    default:
+      return "message";
   }
 }
 
diff --git a/packages/demobank-ui/src/components/QR.tsx 
b/packages/demobank-ui/src/components/QR.tsx
index ee5b73c69..f154ddebe 100644
--- a/packages/demobank-ui/src/components/QR.tsx
+++ b/packages/demobank-ui/src/components/QR.tsx
@@ -14,14 +14,14 @@
  GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 
-import { h, VNode } from 'preact';
-import { useEffect, useRef } from 'preact/hooks';
-import qrcode from 'qrcode-generator';
+import { h, VNode } from "preact";
+import { useEffect, useRef } from "preact/hooks";
+import qrcode from "qrcode-generator";
 
 export function QR({ text }: { text: string }): VNode {
   const divRef = useRef<HTMLDivElement>(null);
   useEffect(() => {
-    const qr = qrcode(0, 'L');
+    const qr = qrcode(0, "L");
     qr.addData(text);
     qr.make();
     if (divRef.current)
@@ -33,14 +33,14 @@ export function QR({ text }: { text: string }): VNode {
   return (
     <div
       style={{
-        width: '100%',
-        display: 'flex',
-        flexDirection: 'column',
-        alignItems: 'left',
+        width: "100%",
+        display: "flex",
+        flexDirection: "column",
+        alignItems: "left",
       }}
     >
       <div
-        style={{ width: '50%', minWidth: 200, maxWidth: 300 }}
+        style={{ width: "50%", minWidth: 200, maxWidth: 300 }}
         ref={divRef}
       />
     </div>
diff --git a/packages/demobank-ui/src/components/fields/DateInput.tsx 
b/packages/demobank-ui/src/components/fields/DateInput.tsx
index 06ec4b6a7..22a83c93c 100644
--- a/packages/demobank-ui/src/components/fields/DateInput.tsx
+++ b/packages/demobank-ui/src/components/fields/DateInput.tsx
@@ -1,7 +1,7 @@
-import { format, subYears } from 'date-fns';
-import { h, VNode } from 'preact';
-import { useLayoutEffect, useRef, useState } from 'preact/hooks';
-import { DatePicker } from '../picker/DatePicker';
+import { format, subYears } from "date-fns";
+import { h, VNode } from "preact";
+import { useLayoutEffect, useRef, useState } from "preact/hooks";
+import { DatePicker } from "../picker/DatePicker";
 
 export interface DateInputProps {
   label: string;
@@ -16,13 +16,11 @@ export interface DateInputProps {
 export function DateInput(props: DateInputProps): VNode {
   const inputRef = useRef<HTMLInputElement>(null);
   useLayoutEffect(() => {
-    if (props.grabFocus) 
-      inputRef.current?.focus();
-    
+    if (props.grabFocus) inputRef.current?.focus();
   }, [props.grabFocus]);
   const [opened, setOpened] = useState(false);
 
-  const value = props.bind[0] || '';
+  const value = props.bind[0] || "";
   const [dirty, setDirty] = useState(false);
   const showError = dirty && props.error;
 
@@ -43,12 +41,10 @@ export function DateInput(props: DateInputProps): VNode {
           <p class="control">
             <input
               type="text"
-              class={showError ? 'input is-danger' : 'input'}
+              class={showError ? "input is-danger" : "input"}
               value={value}
               onKeyPress={(e) => {
-                if (e.key === 'Enter' && props.onConfirm) 
-                  props.onConfirm()
-                
+                if (e.key === "Enter" && props.onConfirm) props.onConfirm();
               }}
               onInput={(e) => {
                 const text = e.currentTarget.value;
@@ -81,7 +77,7 @@ export function DateInput(props: DateInputProps): VNode {
         closeFunction={() => setOpened(false)}
         dateReceiver={(d) => {
           setDirty(true);
-          const v = format(d, 'yyyy-MM-dd');
+          const v = format(d, "yyyy-MM-dd");
           props.bind[1](v);
         }}
       />
diff --git a/packages/demobank-ui/src/components/fields/EmailInput.tsx 
b/packages/demobank-ui/src/components/fields/EmailInput.tsx
index 8b64264ed..2a22b26e8 100644
--- a/packages/demobank-ui/src/components/fields/EmailInput.tsx
+++ b/packages/demobank-ui/src/components/fields/EmailInput.tsx
@@ -1,5 +1,5 @@
-import { h, VNode } from 'preact';
-import { useLayoutEffect, useRef, useState } from 'preact/hooks';
+import { h, VNode } from "preact";
+import { useLayoutEffect, useRef, useState } from "preact/hooks";
 
 export interface TextInputProps {
   label: string;
@@ -14,9 +14,7 @@ export interface TextInputProps {
 export function EmailInput(props: TextInputProps): VNode {
   const inputRef = useRef<HTMLInputElement>(null);
   useLayoutEffect(() => {
-    if (props.grabFocus) 
-      inputRef.current?.focus();
-    
+    if (props.grabFocus) inputRef.current?.focus();
   }, [props.grabFocus]);
   const value = props.bind[0];
   const [dirty, setDirty] = useState(false);
@@ -37,18 +35,16 @@ export function EmailInput(props: TextInputProps): VNode {
           required
           placeholder={props.placeholder}
           type="email"
-          class={showError ? 'input is-danger' : 'input'}
+          class={showError ? "input is-danger" : "input"}
           onKeyPress={(e) => {
-            if (e.key === 'Enter' && props.onConfirm) 
-              props.onConfirm()
-            
+            if (e.key === "Enter" && props.onConfirm) props.onConfirm();
           }}
           onInput={(e) => {
             setDirty(true);
             props.bind[1]((e.target as HTMLInputElement).value);
           }}
           ref={inputRef}
-          style={{ display: 'block' }}
+          style={{ display: "block" }}
         />
       </div>
       {showError && <p class="help is-danger">{props.error}</p>}
diff --git a/packages/demobank-ui/src/components/fields/FileInput.tsx 
b/packages/demobank-ui/src/components/fields/FileInput.tsx
index 17413b907..79cd76f30 100644
--- a/packages/demobank-ui/src/components/fields/FileInput.tsx
+++ b/packages/demobank-ui/src/components/fields/FileInput.tsx
@@ -18,8 +18,8 @@
  *
  * @author Sebastian Javier Marchano (sebasjm)
  */
-import { h, VNode } from 'preact';
-import { useLayoutEffect, useRef, useState } from 'preact/hooks';
+import { h, VNode } from "preact";
+import { useLayoutEffect, useRef, useState } from "preact/hooks";
 
 const MAX_IMAGE_UPLOAD_SIZE = 1024 * 1024;
 
@@ -42,9 +42,7 @@ export interface FileInputProps {
 export function FileInput(props: FileInputProps): VNode {
   const inputRef = useRef<HTMLInputElement>(null);
   useLayoutEffect(() => {
-    if (props.grabFocus) 
-      inputRef.current?.focus();
-    
+    if (props.grabFocus) inputRef.current?.focus();
   }, [props.grabFocus]);
 
   const fileInputRef = useRef<HTMLInputElement>(null);
@@ -56,9 +54,7 @@ export function FileInput(props: FileInputProps): VNode {
           <div class="icon is-small ">
             <i class="mdi mdi-folder" />
           </div>
-          <span>
-            {props.label}
-          </span>
+          <span>{props.label}</span>
         </a>
         {props.tooltip && (
           <span class="icon has-tooltip-right" data-tooltip={props.tooltip}>
@@ -69,15 +65,14 @@ export function FileInput(props: FileInputProps): VNode {
       <div class="control">
         <input
           ref={fileInputRef}
-          style={{ display: 'none' }}
+          style={{ display: "none" }}
           type="file"
           // name={String(name)}
           onChange={(e) => {
             const f: FileList | null = e.currentTarget.files;
-            if (!f || f.length != 1) 
-              return props.onChange(undefined);
-            
-            console.log(f)
+            if (!f || f.length != 1) return props.onChange(undefined);
+
+            console.log(f);
             if (f[0].size > MAX_IMAGE_UPLOAD_SIZE) {
               setSizeError(true);
               return props.onChange(undefined);
@@ -87,10 +82,14 @@ export function FileInput(props: FileInputProps): VNode {
               const b64 = btoa(
                 new Uint8Array(b).reduce(
                   (data, byte) => data + String.fromCharCode(byte),
-                  '',
+                  "",
                 ),
               );
-              return props.onChange({content: 
`data:${f[0].type};base64,${b64}`, name: f[0].name, type: f[0].type});
+              return props.onChange({
+                content: `data:${f[0].type};base64,${b64}`,
+                name: f[0].name,
+                type: f[0].type,
+              });
             });
           }}
         />
diff --git a/packages/demobank-ui/src/components/fields/ImageInput.tsx 
b/packages/demobank-ui/src/components/fields/ImageInput.tsx
index 98457af21..c190de9a9 100644
--- a/packages/demobank-ui/src/components/fields/ImageInput.tsx
+++ b/packages/demobank-ui/src/components/fields/ImageInput.tsx
@@ -18,19 +18,17 @@
  *
  * @author Sebastian Javier Marchano (sebasjm)
  */
-import { h, VNode } from 'preact';
-import { useLayoutEffect, useRef, useState } from 'preact/hooks';
-import emptyImage from '../../assets/empty.png';
-import { TextInputProps } from './TextInput';
+import { h, VNode } from "preact";
+import { useLayoutEffect, useRef, useState } from "preact/hooks";
+import emptyImage from "../../assets/empty.png";
+import { TextInputProps } from "./TextInput";
 
 const MAX_IMAGE_UPLOAD_SIZE = 1024 * 1024;
 
 export function ImageInput(props: TextInputProps): VNode {
   const inputRef = useRef<HTMLInputElement>(null);
   useLayoutEffect(() => {
-    if (props.grabFocus) 
-      inputRef.current?.focus();
-    
+    if (props.grabFocus) inputRef.current?.focus();
   }, [props.grabFocus]);
 
   const value = props.bind[0];
@@ -59,14 +57,13 @@ export function ImageInput(props: TextInputProps): VNode {
         />
         <input
           ref={image}
-          style={{ display: 'none' }}
+          style={{ display: "none" }}
           type="file"
           name={String(name)}
           onChange={(e) => {
             const f: FileList | null = e.currentTarget.files;
-            if (!f || f.length != 1) 
-              return onChange(emptyImage);
-            
+            if (!f || f.length != 1) return onChange(emptyImage);
+
             if (f[0].size > MAX_IMAGE_UPLOAD_SIZE) {
               setSizeError(true);
               return onChange(emptyImage);
@@ -76,7 +73,7 @@ export function ImageInput(props: TextInputProps): VNode {
               const b64 = btoa(
                 new Uint8Array(b).reduce(
                   (data, byte) => data + String.fromCharCode(byte),
-                  '',
+                  "",
                 ),
               );
               return onChange(`data:${f[0].type};base64,${b64}` as any);
diff --git a/packages/demobank-ui/src/components/fields/NumberInput.tsx 
b/packages/demobank-ui/src/components/fields/NumberInput.tsx
index 881c61c57..1a54d24b6 100644
--- a/packages/demobank-ui/src/components/fields/NumberInput.tsx
+++ b/packages/demobank-ui/src/components/fields/NumberInput.tsx
@@ -1,5 +1,5 @@
-import { h, VNode } from 'preact';
-import { useLayoutEffect, useRef, useState } from 'preact/hooks';
+import { h, VNode } from "preact";
+import { useLayoutEffect, useRef, useState } from "preact/hooks";
 
 export interface TextInputProps {
   label: string;
@@ -14,9 +14,7 @@ export interface TextInputProps {
 export function PhoneNumberInput(props: TextInputProps): VNode {
   const inputRef = useRef<HTMLInputElement>(null);
   useLayoutEffect(() => {
-    if (props.grabFocus) 
-      inputRef.current?.focus();
-    
+    if (props.grabFocus) inputRef.current?.focus();
   }, [props.grabFocus]);
   const value = props.bind[0];
   const [dirty, setDirty] = useState(false);
@@ -36,18 +34,16 @@ export function PhoneNumberInput(props: TextInputProps): 
VNode {
           value={value}
           type="tel"
           placeholder={props.placeholder}
-          class={showError ? 'input is-danger' : 'input'}
+          class={showError ? "input is-danger" : "input"}
           onKeyPress={(e) => {
-            if (e.key === 'Enter' && props.onConfirm) 
-              props.onConfirm()
-            
+            if (e.key === "Enter" && props.onConfirm) props.onConfirm();
           }}
           onInput={(e) => {
             setDirty(true);
             props.bind[1]((e.target as HTMLInputElement).value);
           }}
           ref={inputRef}
-          style={{ display: 'block' }}
+          style={{ display: "block" }}
         />
       </div>
       {showError && <p class="help is-danger">{props.error}</p>}
diff --git a/packages/demobank-ui/src/components/fields/TextInput.tsx 
b/packages/demobank-ui/src/components/fields/TextInput.tsx
index 5cc9f32ad..cc7104cf5 100644
--- a/packages/demobank-ui/src/components/fields/TextInput.tsx
+++ b/packages/demobank-ui/src/components/fields/TextInput.tsx
@@ -1,8 +1,8 @@
-import { h, VNode } from 'preact';
-import { useLayoutEffect, useRef, useState } from 'preact/hooks';
+import { h, VNode } from "preact";
+import { useLayoutEffect, useRef, useState } from "preact/hooks";
 
 export interface TextInputProps {
-  inputType?: 'text' | 'number' | 'multiline' | 'password';
+  inputType?: "text" | "number" | "multiline" | "password";
   label: string;
   grabFocus?: boolean;
   disabled?: boolean;
@@ -16,13 +16,11 @@ export interface TextInputProps {
 const TextInputType = function ({ inputType, grabFocus, ...rest }: any): VNode 
{
   const inputRef = useRef<HTMLInputElement>(null);
   useLayoutEffect(() => {
-    if (grabFocus) 
-      inputRef.current?.focus();
-    
+    if (grabFocus) inputRef.current?.focus();
   }, [grabFocus]);
 
-  return inputType === 'multiline' ? (
-    <textarea {...rest} rows={5} ref={inputRef} style={{ height: 'unset' }} />
+  return inputType === "multiline" ? (
+    <textarea {...rest} rows={5} ref={inputRef} style={{ height: "unset" }} />
   ) : (
     <input {...rest} type={inputType} ref={inputRef} />
   );
@@ -49,17 +47,15 @@ export function TextInput(props: TextInputProps): VNode {
           grabFocus={props.grabFocus}
           disabled={props.disabled}
           placeholder={props.placeholder}
-          class={showError ? 'input is-danger' : 'input'}
+          class={showError ? "input is-danger" : "input"}
           onKeyPress={(e: any) => {
-            if (e.key === 'Enter' && props.onConfirm) 
-              props.onConfirm();
-            
+            if (e.key === "Enter" && props.onConfirm) props.onConfirm();
           }}
           onInput={(e: any) => {
             setDirty(true);
             props.bind[1]((e.target as HTMLInputElement).value);
           }}
-          style={{ display: 'block' }}
+          style={{ display: "block" }}
         />
       </div>
       {showError && <p class="help is-danger">{props.error}</p>}
diff --git a/packages/demobank-ui/src/components/menu/LangSelector.tsx 
b/packages/demobank-ui/src/components/menu/LangSelector.tsx
index 221237a5b..69d6ee64a 100644
--- a/packages/demobank-ui/src/components/menu/LangSelector.tsx
+++ b/packages/demobank-ui/src/components/menu/LangSelector.tsx
@@ -19,23 +19,23 @@
  * @author Sebastian Javier Marchano (sebasjm)
  */
 
-import { h, VNode, Fragment } from 'preact';
-import { useCallback, useEffect, useState } from 'preact/hooks';
-import langIcon from '../../assets/icons/languageicon.svg';
-import { useTranslationContext } from '../../context/translation';
-import { strings as messages } from '../../i18n/strings';
+import { h, VNode, Fragment } from "preact";
+import { useCallback, useEffect, useState } from "preact/hooks";
+import langIcon from "../../assets/icons/languageicon.svg";
+import { useTranslationContext } from "../../context/translation";
+import { strings as messages } from "../../i18n/strings";
 
 type LangsNames = {
   [P in keyof typeof messages]: string;
 };
 
 const names: LangsNames = {
-  es: 'Español [es]',
-  en: 'English [en]',
-  fr: 'Français [fr]',
-  de: 'Deutsch [de]',
-  sv: 'Svenska [sv]',
-  it: 'Italiano [it]',
+  es: "Español [es]",
+  en: "English [en]",
+  fr: "Français [fr]",
+  de: "Deutsch [de]",
+  sv: "Svenska [sv]",
+  it: "Italiano [it]",
 };
 
 function getLangName(s: keyof LangsNames | string): string {
@@ -47,36 +47,38 @@ function getLangName(s: keyof LangsNames | string): string {
 export function LangSelectorLikePy(): VNode {
   const [updatingLang, setUpdatingLang] = useState(false);
   const { lang, changeLanguage } = useTranslationContext();
-  const [hidden, setHidden] = useState(true)
+  const [hidden, setHidden] = useState(true);
   useEffect(() => {
-    function bodyKeyPress(event:KeyboardEvent) {
-      if (event.code === 'Escape') 
-        setHidden(true);
-      
+    function bodyKeyPress(event: KeyboardEvent) {
+      if (event.code === "Escape") setHidden(true);
     }
-    function bodyOnClick(event:Event) {
+    function bodyOnClick(event: Event) {
       setHidden(true);
     }
-    document.body.addEventListener('click', bodyOnClick)
-    document.body.addEventListener('keydown', bodyKeyPress as any)
+    document.body.addEventListener("click", bodyOnClick);
+    document.body.addEventListener("keydown", bodyKeyPress as any);
     return () => {
-      document.body.removeEventListener('keydown', bodyKeyPress as any)
-      document.body.removeEventListener('click', bodyOnClick)
-    }
-  },[])
+      document.body.removeEventListener("keydown", bodyKeyPress as any);
+      document.body.removeEventListener("click", bodyOnClick);
+    };
+  }, []);
   return (
     <Fragment>
-      <button name="language" onClick={(ev) => {
-        setHidden(h => !h);
-        ev.stopPropagation();
-      }}>
+      <button
+        name="language"
+        onClick={(ev) => {
+          setHidden((h) => !h);
+          ev.stopPropagation();
+        }}
+      >
         {getLangName(lang)}
       </button>
-      <div id="lang" class={hidden ? 'hide' : ''}>
+      <div id="lang" class={hidden ? "hide" : ""}>
         <div style="position: relative; overflow: visible;">
           <div
             class="nav"
-            style="position: absolute; max-height: 60vh; overflow-y: scroll">
+            style="position: absolute; max-height: 60vh; overflow-y: scroll"
+          >
             {Object.keys(messages)
               .filter((l) => l !== lang)
               .map((l) => (
@@ -88,7 +90,8 @@ export function LangSelectorLikePy(): VNode {
                   onClick={() => {
                     changeLanguage(l);
                     setUpdatingLang(false);
-                  }}>
+                  }}
+                >
                   {getLangName(l)}
                 </a>
               ))}
diff --git a/packages/demobank-ui/src/components/menu/NavigationBar.tsx 
b/packages/demobank-ui/src/components/menu/NavigationBar.tsx
index 9e540213d..d344875eb 100644
--- a/packages/demobank-ui/src/components/menu/NavigationBar.tsx
+++ b/packages/demobank-ui/src/components/menu/NavigationBar.tsx
@@ -19,9 +19,9 @@
  * @author Sebastian Javier Marchano (sebasjm)
  */
 
-import { h, VNode } from 'preact';
-import logo from '../../assets/logo.jpeg';
-import { LangSelectorLikePy as LangSelector } from './LangSelector';
+import { h, VNode } from "preact";
+import logo from "../../assets/logo.jpeg";
+import { LangSelectorLikePy as LangSelector } from "./LangSelector";
 
 interface Props {
   onMobileMenu: () => void;
diff --git a/packages/demobank-ui/src/components/menu/SideBar.tsx 
b/packages/demobank-ui/src/components/menu/SideBar.tsx
index 7f9981a1c..d7833df5a 100644
--- a/packages/demobank-ui/src/components/menu/SideBar.tsx
+++ b/packages/demobank-ui/src/components/menu/SideBar.tsx
@@ -19,8 +19,8 @@
  * @author Sebastian Javier Marchano (sebasjm)
  */
 
-import { h, VNode } from 'preact';
-import { Translate } from '../../i18n';
+import { h, VNode } from "preact";
+import { Translate } from "../../i18n";
 
 interface Props {
   mobile?: boolean;
@@ -28,9 +28,9 @@ interface Props {
 
 export function Sidebar({ mobile }: Props): VNode {
   // const config = useConfigContext();
-  const config = { version: 'none' };
+  const config = { version: "none" };
   // FIXME: add replacement for __VERSION__ with the current version
-  const process = { env: { __VERSION__: '0.0.0' } };
+  const process = { env: { __VERSION__: "0.0.0" } };
 
   return (
     <aside class="aside is-placed-left is-expanded">
diff --git a/packages/demobank-ui/src/components/menu/index.tsx 
b/packages/demobank-ui/src/components/menu/index.tsx
index 07e1c5265..99d0f7646 100644
--- a/packages/demobank-ui/src/components/menu/index.tsx
+++ b/packages/demobank-ui/src/components/menu/index.tsx
@@ -14,11 +14,11 @@
  GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 
-import { ComponentChildren, Fragment, h, VNode } from 'preact';
-import Match from 'preact-router/match';
-import { useEffect, useState } from 'preact/hooks';
-import { NavigationBar } from './NavigationBar';
-import { Sidebar } from './SideBar';
+import { ComponentChildren, Fragment, h, VNode } from "preact";
+import Match from "preact-router/match";
+import { useEffect, useState } from "preact/hooks";
+import { NavigationBar } from "./NavigationBar";
+import { Sidebar } from "./SideBar";
 
 interface MenuProps {
   title: string;
@@ -47,7 +47,7 @@ export function Menu({ title }: MenuProps): VNode {
         return (
           <WithTitle title={titleWithSubtitle}>
             <div
-              class={mobileOpen ? 'has-aside-mobile-expanded' : ''}
+              class={mobileOpen ? "has-aside-mobile-expanded" : ""}
               onClick={() => setMobileOpen(false)}
             >
               <NavigationBar
@@ -82,11 +82,11 @@ export function NotificationCard({
         <div class="column is-12">
           <article
             class={
-              n.type === 'ERROR'
-                ? 'message is-danger'
-                : n.type === 'WARN'
-                  ? 'message is-warning'
-                  : 'message is-info'
+              n.type === "ERROR"
+                ? "message is-danger"
+                : n.type === "WARN"
+                ? "message is-warning"
+                : "message is-info"
             }
           >
             <div class="message-header">
@@ -132,4 +132,4 @@ export interface Notification {
 }
 
 export type ValueOrFunction<T> = T | ((p: T) => T);
-export type MessageType = 'INFO' | 'WARN' | 'ERROR' | 'SUCCESS';
+export type MessageType = "INFO" | "WARN" | "ERROR" | "SUCCESS";
diff --git a/packages/demobank-ui/src/components/picker/DatePicker.tsx 
b/packages/demobank-ui/src/components/picker/DatePicker.tsx
index 94dbc9458..ba53578ef 100644
--- a/packages/demobank-ui/src/components/picker/DatePicker.tsx
+++ b/packages/demobank-ui/src/components/picker/DatePicker.tsx
@@ -19,7 +19,7 @@
  * @author Sebastian Javier Marchano (sebasjm)
  */
 
-import { h, Component } from 'preact';
+import { h, Component } from "preact";
 
 interface Props {
   closeFunction?: () => void;
@@ -37,36 +37,36 @@ interface State {
 const now = new Date();
 
 const monthArrShortFull = [
-  'January',
-  'February',
-  'March',
-  'April',
-  'May',
-  'June',
-  'July',
-  'August',
-  'September',
-  'October',
-  'November',
-  'December',
+  "January",
+  "February",
+  "March",
+  "April",
+  "May",
+  "June",
+  "July",
+  "August",
+  "September",
+  "October",
+  "November",
+  "December",
 ];
 
 const monthArrShort = [
-  'Jan',
-  'Feb',
-  'Mar',
-  'Apr',
-  'May',
-  'Jun',
-  'Jul',
-  'Aug',
-  'Sep',
-  'Oct',
-  'Nov',
-  'Dec',
+  "Jan",
+  "Feb",
+  "Mar",
+  "Apr",
+  "May",
+  "Jun",
+  "Jul",
+  "Aug",
+  "Sep",
+  "Oct",
+  "Nov",
+  "Dec",
 ];
 
-const dayArr = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
+const dayArr = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
 
 const yearArr: number[] = [];
 
@@ -83,10 +83,10 @@ export class DatePicker extends Component<Props, State> {
   dayClicked(e: any) {
     const element = e.target; // the actual element clicked
 
-    if (element.innerHTML === '') return false; // don't continue if <span /> 
empty
+    if (element.innerHTML === "") return false; // don't continue if <span /> 
empty
 
     // get date from clicked element (gets attached when rendered)
-    const date = new Date(element.getAttribute('data-value'));
+    const date = new Date(element.getAttribute("data-value"));
 
     // update the state
     this.setState({ currentDate: date });
@@ -131,40 +131,37 @@ export class DatePicker extends Component<Props, State> {
    * Display previous month by updating state
    */
   displayPrevMonth() {
-    if (this.state.displayedMonth <= 0) 
+    if (this.state.displayedMonth <= 0)
       this.setState({
         displayedMonth: 11,
         displayedYear: this.state.displayedYear - 1,
       });
-    else 
+    else
       this.setState({
         displayedMonth: this.state.displayedMonth - 1,
       });
-    
   }
 
   /**
    * Display next month by updating state
    */
   displayNextMonth() {
-    if (this.state.displayedMonth >= 11) 
+    if (this.state.displayedMonth >= 11)
       this.setState({
         displayedMonth: 0,
         displayedYear: this.state.displayedYear + 1,
       });
-    else 
+    else
       this.setState({
         displayedMonth: this.state.displayedMonth + 1,
       });
-    
   }
 
   /**
    * Display the selected month (gets fired when clicking on the date string)
    */
   displaySelectedMonth() {
-    if (this.state.selectYearMode) 
-      this.toggleYearSelector();
+    if (this.state.selectYearMode) this.toggleYearSelector();
     else {
       if (!this.state.currentDate) return false;
       this.setState({
@@ -194,7 +191,7 @@ export class DatePicker extends Component<Props, State> {
     this.passDateToParent(this.state.currentDate);
   }
   passDateToParent(date: Date) {
-    if (typeof this.props.dateReceiver === 'function')
+    if (typeof this.props.dateReceiver === "function")
       this.props.dateReceiver(date);
     this.closeDatePicker();
   }
@@ -229,22 +226,18 @@ export class DatePicker extends Component<Props, State> {
   }
 
   render() {
-    const {
-      currentDate,
-      displayedMonth,
-      displayedYear,
-      selectYearMode,
-    } = this.state;
+    const { currentDate, displayedMonth, displayedYear, selectYearMode } =
+      this.state;
 
     return (
       <div>
-        <div class={`datePicker ${this.props.opened && 'datePicker--opened'}`}>
+        <div class={`datePicker ${this.props.opened && "datePicker--opened"}`}>
           <div class="datePicker--titles">
             <h3
               style={{
                 color: selectYearMode
-                  ? 'rgba(255,255,255,.87)'
-                  : 'rgba(255,255,255,.57)',
+                  ? "rgba(255,255,255,.87)"
+                  : "rgba(255,255,255,.57)",
               }}
               onClick={this.toggleYearSelector}
             >
@@ -253,12 +246,12 @@ export class DatePicker extends Component<Props, State> {
             <h2
               style={{
                 color: !selectYearMode
-                  ? 'rgba(255,255,255,.87)'
-                  : 'rgba(255,255,255,.57)',
+                  ? "rgba(255,255,255,.87)"
+                  : "rgba(255,255,255,.57)",
               }}
               onClick={this.displaySelectedMonth}
             >
-              {dayArr[currentDate.getDay()]},{' '}
+              {dayArr[currentDate.getDay()]},{" "}
               {monthArrShort[currentDate.getMonth()]} {currentDate.getDate()}
             </h2>
           </div>
@@ -267,7 +260,7 @@ export class DatePicker extends Component<Props, State> {
             <nav>
               <span onClick={this.displayPrevMonth} class="icon">
                 <i
-                  style={{ transform: 'rotate(180deg)' }}
+                  style={{ transform: "rotate(180deg)" }}
                   class="mdi mdi-forward"
                 />
               </span>
@@ -284,7 +277,7 @@ export class DatePicker extends Component<Props, State> {
             {!selectYearMode && (
               <div class="datePicker--calendar">
                 <div class="datePicker--dayNames">
-                  {['S', 'M', 'T', 'W', 'T', 'F', 'S'].map((day, i) => (
+                  {["S", "M", "T", "W", "T", "F", "S"].map((day, i) => (
                     <span key={i}>{day}</span>
                   ))}
                 </div>
@@ -309,8 +302,8 @@ export class DatePicker extends Component<Props, State> {
                       <span
                         key={day.day}
                         class={
-                          (day.today ? 'datePicker--today ' : '') +
-                          (selected ? 'datePicker--selected' : '')
+                          (day.today ? "datePicker--today " : "") +
+                          (selected ? "datePicker--selected" : "")
                         }
                         disabled={!day.date}
                         data-value={day.date}
@@ -328,7 +321,7 @@ export class DatePicker extends Component<Props, State> {
                 {(this.props.years || yearArr).map((year) => (
                   <span
                     key={year}
-                    class={year === displayedYear ? 'selected' : ''}
+                    class={year === displayedYear ? "selected" : ""}
                     onClick={this.changeDisplayedYear}
                   >
                     {year}
@@ -343,7 +336,7 @@ export class DatePicker extends Component<Props, State> {
           class="datePicker--background"
           onClick={this.closeDatePicker}
           style={{
-            display: this.props.opened ? 'block' : 'none',
+            display: this.props.opened ? "block" : "none",
           }}
         />
       </div>
@@ -351,6 +344,4 @@ export class DatePicker extends Component<Props, State> {
   }
 }
 
-for (let i = 2010; i <= now.getFullYear() + 10; i++) 
-  yearArr.push(i);
-
+for (let i = 2010; i <= now.getFullYear() + 10; i++) yearArr.push(i);
diff --git 
a/packages/demobank-ui/src/components/picker/DurationPicker.stories.tsx 
b/packages/demobank-ui/src/components/picker/DurationPicker.stories.tsx
index 5e9930522..7f96cc15b 100644
--- a/packages/demobank-ui/src/components/picker/DurationPicker.stories.tsx
+++ b/packages/demobank-ui/src/components/picker/DurationPicker.stories.tsx
@@ -19,16 +19,16 @@
  * @author Sebastian Javier Marchano (sebasjm)
  */
 
-import { h, FunctionalComponent } from 'preact';
-import { useState } from 'preact/hooks';
-import { DurationPicker as TestedComponent } from './DurationPicker';
+import { h, FunctionalComponent } from "preact";
+import { useState } from "preact/hooks";
+import { DurationPicker as TestedComponent } from "./DurationPicker";
 
 export default {
-  title: 'Components/Picker/Duration',
+  title: "Components/Picker/Duration",
   component: TestedComponent,
   argTypes: {
-    onCreate: { action: 'onCreate' },
-    goBack: { action: 'goBack' },
+    onCreate: { action: "onCreate" },
+    goBack: { action: "goBack" },
   },
 };
 
diff --git a/packages/demobank-ui/src/components/picker/DurationPicker.tsx 
b/packages/demobank-ui/src/components/picker/DurationPicker.tsx
index 542ff2f01..94f2326bc 100644
--- a/packages/demobank-ui/src/components/picker/DurationPicker.tsx
+++ b/packages/demobank-ui/src/components/picker/DurationPicker.tsx
@@ -19,10 +19,10 @@
  * @author Sebastian Javier Marchano (sebasjm)
  */
 
-import { h, VNode } from 'preact';
-import { useState } from 'preact/hooks';
-import { useTranslator } from '../../i18n';
-import '../../scss/DurationPicker.scss';
+import { h, VNode } from "preact";
+import { useState } from "preact/hooks";
+import { useTranslator } from "../../i18n";
+import "../../scss/DurationPicker.scss";
 
 export interface Props {
   hours?: boolean;
@@ -129,9 +129,9 @@ function InputNumber({
       }}
       style={{
         width: 50,
-        border: 'none',
-        fontSize: 'inherit',
-        background: 'inherit',
+        border: "none",
+        fontSize: "inherit",
+        background: "inherit",
       }}
     />
   );
@@ -157,7 +157,7 @@ function DurationColumn({
           <div class="rdp-cell" key={value - 2}>
             {onDecrease && (
               <button
-                style={{ width: '100%', textAlign: 'center', margin: 5 }}
+                style={{ width: "100%", textAlign: "center", margin: 5 }}
                 onClick={onDecrease}
               >
                 <span class="icon">
@@ -167,7 +167,7 @@ function DurationColumn({
             )}
           </div>
           <div class="rdp-cell" key={value - 1}>
-            {value > min ? toTwoDigitString(value - 1) : ''}
+            {value > min ? toTwoDigitString(value - 1) : ""}
           </div>
           <div class="rdp-cell rdp-center" key={value}>
             {onChange ? (
@@ -182,13 +182,13 @@ function DurationColumn({
           </div>
 
           <div class="rdp-cell" key={value + 1}>
-            {value < max ? toTwoDigitString(value + 1) : ''}
+            {value < max ? toTwoDigitString(value + 1) : ""}
           </div>
 
           <div class="rdp-cell" key={value + 2}>
             {onIncrease && (
               <button
-                style={{ width: '100%', textAlign: 'center', margin: 5 }}
+                style={{ width: "100%", textAlign: "center", margin: 5 }}
                 onClick={onIncrease}
               >
                 <span class="icon">
@@ -204,8 +204,7 @@ function DurationColumn({
 }
 
 function toTwoDigitString(n: number) {
-  if (n < 10) 
-    return `0${n}`;
-  
+  if (n < 10) return `0${n}`;
+
   return `${n}`;
 }
diff --git a/packages/demobank-ui/src/context/translation.ts 
b/packages/demobank-ui/src/context/translation.ts
index 1dbca56e6..a411ecb16 100644
--- a/packages/demobank-ui/src/context/translation.ts
+++ b/packages/demobank-ui/src/context/translation.ts
@@ -19,11 +19,11 @@
  * @author Sebastian Javier Marchano (sebasjm)
  */
 
-import { createContext, h, VNode } from 'preact';
-import { useContext, useEffect } from 'preact/hooks';
-import { useLang } from '../hooks/index.js';
-import * as jedLib from 'jed';
-import { strings } from '../i18n/strings.js';
+import { createContext, h, VNode } from "preact";
+import { useContext, useEffect } from "preact/hooks";
+import { useLang } from "../hooks/index.js";
+import * as jedLib from "jed";
+import { strings } from "../i18n/strings.js";
 
 interface Type {
   lang: string;
@@ -31,7 +31,7 @@ interface Type {
   changeLanguage: (l: string) => void;
 }
 const initial = {
-  lang: 'en',
+  lang: "en",
   handler: null,
   changeLanguage: () => {
     /**
@@ -55,15 +55,12 @@ export const TranslationProvider = ({
   children,
   forceLang,
 }: Props): VNode => {
-
   const [lang, changeLanguage] = useLang(initial);
   useEffect(() => {
-    if (forceLang)
-      changeLanguage(forceLang);
-
+    if (forceLang) changeLanguage(forceLang);
   });
-  console.log('lang store', strings);
-  const handler = new jedLib.Jed(strings[lang] || strings['en']);
+  console.log("lang store", strings);
+  const handler = new jedLib.Jed(strings[lang] || strings["en"]);
   return h(Context.Provider, {
     value: { lang, handler, changeLanguage },
     children,
diff --git a/packages/demobank-ui/src/declaration.d.ts 
b/packages/demobank-ui/src/declaration.d.ts
index 73bccac71..00b3d41d5 100644
--- a/packages/demobank-ui/src/declaration.d.ts
+++ b/packages/demobank-ui/src/declaration.d.ts
@@ -1,20 +1,20 @@
-declare module '*.css' {
+declare module "*.css" {
   const mapping: Record<string, string>;
   export default mapping;
 }
-declare module '*.svg' {
+declare module "*.svg" {
   const content: any;
   export default content;
 }
-declare module '*.jpeg' {
+declare module "*.jpeg" {
   const content: any;
   export default content;
 }
-declare module '*.png' {
+declare module "*.png" {
   const content: any;
   export default content;
 }
-declare module 'jed' {
+declare module "jed" {
   const x: any;
   export = x;
 }
diff --git a/packages/demobank-ui/src/hooks/async.ts 
b/packages/demobank-ui/src/hooks/async.ts
index be41b4e07..0fc197554 100644
--- a/packages/demobank-ui/src/hooks/async.ts
+++ b/packages/demobank-ui/src/hooks/async.ts
@@ -18,7 +18,7 @@
  *
  * @author Sebastian Javier Marchano (sebasjm)
  */
-import { useState } from 'preact/hooks';
+import { useState } from "preact/hooks";
 // import { cancelPendingRequest } from "./backend";
 
 export interface Options {
@@ -51,9 +51,9 @@ export function useAsync<T>(
     }, tooLong);
 
     try {
-      console.log('calling async', args);
+      console.log("calling async", args);
       const result = await fn(...args);
-      console.log('async back', result);
+      console.log("async back", result);
       setData(result);
     } catch (error) {
       setError(error);
diff --git a/packages/demobank-ui/src/i18n/index.tsx 
b/packages/demobank-ui/src/i18n/index.tsx
index 9882525a1..2489184b2 100644
--- a/packages/demobank-ui/src/i18n/index.tsx
+++ b/packages/demobank-ui/src/i18n/index.tsx
@@ -21,9 +21,9 @@
 /**
  * Imports
  */
-import { ComponentChild, ComponentChildren, h, Fragment, VNode } from 'preact';
+import { ComponentChild, ComponentChildren, h, Fragment, VNode } from "preact";
 
-import { useTranslationContext } from '../context/translation';
+import { useTranslationContext } from "../context/translation";
 
 export function useTranslator() {
   const ctx = useTranslationContext();
@@ -46,12 +46,10 @@ export function useTranslator() {
  * Convert template strings to a msgid
  */
 function toI18nString(stringSeq: ReadonlyArray<string>): string {
-  let s = '';
+  let s = "";
   for (let i = 0; i < stringSeq.length; i++) {
     s += stringSeq[i];
-    if (i < stringSeq.length - 1) 
-      s += `%${i + 1}$s`;
-    
+    if (i < stringSeq.length - 1) s += `%${i + 1}$s`;
   }
   return s;
 }
@@ -64,12 +62,11 @@ interface TranslateSwitchProps {
 function stringifyChildren(children: ComponentChildren): string {
   let n = 1;
   const ss = (children instanceof Array ? children : [children]).map((c) => {
-    if (typeof c === 'string') 
-      return c;
-    
+    if (typeof c === "string") return c;
+
     return `%${n++}$s`;
   });
-  const s = ss.join('').replace(/ +/g, ' ').trim();
+  const s = ss.join("").replace(/ +/g, " ").trim();
   return s;
 }
 
@@ -97,24 +94,20 @@ function getTranslatedChildren(
   const placeholderChildren = Array<ComponentChild>();
   for (let i = 0; i < childArray.length; i++) {
     const x = childArray[i];
-    if (x === undefined) 
-      continue;
-    else if (typeof x === 'string') 
-      continue;
-    else 
-      placeholderChildren.push(x);
-    
+    if (x === undefined) continue;
+    else if (typeof x === "string") continue;
+    else placeholderChildren.push(x);
   }
   const result = Array<ComponentChild>();
-  for (let i = 0; i < tr.length; i++) 
-    if (i % 2 == 0) 
+  for (let i = 0; i < tr.length; i++)
+    if (i % 2 == 0)
       // Text
       result.push(tr[i]);
     else {
       const childIdx = Number.parseInt(tr[i], 10) - 1;
       result.push(placeholderChildren[childIdx]);
     }
-  
+
   return result;
 }
 
@@ -154,21 +147,18 @@ export function TranslateSwitch({ children, target }: 
TranslateSwitchProps) {
   let singular: VNode<TranslationPluralProps> | undefined;
   let plural: VNode<TranslationPluralProps> | undefined;
   // const children = this.props.children;
-  if (children) 
+  if (children)
     (children instanceof Array ? children : [children]).forEach(
       (child: any) => {
-        if (child.type === TranslatePlural) 
-          plural = child;
-        
-        if (child.type === TranslateSingular) 
-          singular = child;
-        
+        if (child.type === TranslatePlural) plural = child;
+
+        if (child.type === TranslateSingular) singular = child;
       },
     );
-  
+
   if (!singular || !plural) {
-    console.error('translation not found');
-    return h('span', {}, ['translation not found']);
+    console.error("translation not found");
+    return h("span", {}, ["translation not found"]);
   }
   singular.props.target = target;
   plural.props.target = target;
diff --git a/packages/demobank-ui/src/i18n/strings.ts 
b/packages/demobank-ui/src/i18n/strings.ts
index 1a3c72f85..d9af71657 100644
--- a/packages/demobank-ui/src/i18n/strings.ts
+++ b/packages/demobank-ui/src/i18n/strings.ts
@@ -15,458 +15,207 @@
  */
 
 /*eslint quote-props: ["error", "consistent"]*/
-export const strings: {[s: string]: any} = {};
+export const strings: { [s: string]: any } = {};
 
-strings['de'] = {
-  'domain': 'messages',
-  'locale_data': {
-    'messages': {
-      'days': [
-        ''
-      ],
-      'hours': [
-        ''
-      ],
-      'minutes': [
-        ''
-      ],
-      'seconds': [
-        ''
-      ],
-      'Clear': [
-        ''
-      ],
-      'Logout': [
-        ''
-      ],
-      'Demo Bank': [
-        ''
-      ],
-      'Go back': [
-        ''
-      ],
-      'Wire transfer': [
-        ''
-      ],
-      'Transfer money to another account of this bank:': [
-        ''
-      ],
-      'Want to try the raw payto://-format?': [
-        ''
-      ],
-      'Transfer money via the Payto system:': [
-        ''
-      ],
-      'payto address': [
-        ''
-      ],
-      'Confirm': [
-        ''
-      ],
-      'Confirm Withdrawal': [
-        ''
-      ],
-      'Waiting the bank to create the operaion...': [
-        ''
-      ],
-      'This withdrawal was aborted!': [
-        ''
-      ],
-      'Withdraw to a Taler Wallet': [
-        ''
-      ],
-      'You can use this QR code to withdraw to your mobile wallet:': [
-        ''
-      ],
-      'this link': [
-        ''
-      ],
-      'Abort': [
-        ''
-      ],
-      'Start withdrawal': [
-        ''
-      ],
-      'Withdraw Money into a Taler wallet': [
-        ''
-      ],
-      'Amount to withdraw': [
-        ''
-      ],
-      'Please login!': [
-        ''
-      ],
-      'Login': [
-        ''
-      ],
-      'Register to the euFin bank!': [
-        ''
-      ],
-      'Registration form': [
-        ''
-      ],
-      'Register': [
-        ''
-      ],
-      'Date': [
-        ''
-      ],
-      'Amount': [
-        ''
-      ],
-      'Counterpart': [
-        ''
-      ],
-      'Subject': [
-        ''
-      ],
-      'Username or account label \'%1$s\' not found.  Won\'t login.': [
-        ''
-      ],
-      'Wrong credentials given.': [
-        ''
-      ],
-      'Account information could not be retrieved.': [
-        ''
-      ],
-      'Close wire transfer': [
-        ''
-      ],
-      'Close Taler withdrawal': [
-        ''
-      ],
-      'Bank account balance:': [
-        ''
-      ],
-      'Latest transactions:': [
-        ''
-      ],
-      'Transfer money manually': [
-        ''
-      ],
-      'List of public accounts was not found.': [
-        ''
-      ],
-      'List of public accounts could not be retrieved.': [
-        ''
-      ],
-      'History of public accounts': [
-        ''
-      ],
-      'Page has a problem: logged in but backend state is lost.': [
-        ''
-      ],
-      'Welcome to the euFin bank!': [
-        ''
-      ],
-      '': {
-        'domain': 'messages',
-        'plural_forms': 'nplurals=2; plural=(n != 1);',
-        'lang': 'de'
-      }
-    }
-  }
+strings["de"] = {
+  domain: "messages",
+  locale_data: {
+    messages: {
+      days: [""],
+      hours: [""],
+      minutes: [""],
+      seconds: [""],
+      Clear: [""],
+      Logout: [""],
+      "Demo Bank": [""],
+      "Go back": [""],
+      "Wire transfer": [""],
+      "Transfer money to another account of this bank:": [""],
+      "Want to try the raw payto://-format?": [""],
+      "Transfer money via the Payto system:": [""],
+      "payto address": [""],
+      Confirm: [""],
+      "Confirm Withdrawal": [""],
+      "Waiting the bank to create the operaion...": [""],
+      "This withdrawal was aborted!": [""],
+      "Withdraw to a Taler Wallet": [""],
+      "You can use this QR code to withdraw to your mobile wallet:": [""],
+      "this link": [""],
+      Abort: [""],
+      "Start withdrawal": [""],
+      "Withdraw Money into a Taler wallet": [""],
+      "Amount to withdraw": [""],
+      "Please login!": [""],
+      Login: [""],
+      "Register to the euFin bank!": [""],
+      "Registration form": [""],
+      Register: [""],
+      Date: [""],
+      Amount: [""],
+      Counterpart: [""],
+      Subject: [""],
+      "Username or account label '%1$s' not found.  Won't login.": [""],
+      "Wrong credentials given.": [""],
+      "Account information could not be retrieved.": [""],
+      "Close wire transfer": [""],
+      "Close Taler withdrawal": [""],
+      "Bank account balance:": [""],
+      "Latest transactions:": [""],
+      "Transfer money manually": [""],
+      "List of public accounts was not found.": [""],
+      "List of public accounts could not be retrieved.": [""],
+      "History of public accounts": [""],
+      "Page has a problem: logged in but backend state is lost.": [""],
+      "Welcome to the euFin bank!": [""],
+      "": {
+        domain: "messages",
+        plural_forms: "nplurals=2; plural=(n != 1);",
+        lang: "de",
+      },
+    },
+  },
 };
 
-strings['en'] = {
-  'domain': 'messages',
-  'locale_data': {
-    'messages': {
-      'days': [
-        'days'
-      ],
-      'hours': [
-        'hours'
-      ],
-      'minutes': [
-        'minutes'
-      ],
-      'seconds': [
-        'seconds'
-      ],
-      'Clear': [
-        ''
-      ],
-      'Logout': [
-        ''
-      ],
-      'Demo Bank': [
-        ''
-      ],
-      'Go back': [
-        'Go back'
-      ],
-      'Wire transfer': [
-        ''
-      ],
-      'Transfer money to another account of this bank:': [
-        ''
-      ],
-      'Want to try the raw payto://-format?': [
-        ''
-      ],
-      'Transfer money via the Payto system:': [
-        ''
-      ],
-      'payto address': [
-        ''
-      ],
-      'Confirm': [
-        ''
-      ],
-      'Confirm Withdrawal': [
-        'Confirm withdrawal'
-      ],
-      'Waiting the bank to create the operaion...': [
-        ''
-      ],
-      'This withdrawal was aborted!': [
-        ''
-      ],
-      'Withdraw to a Taler Wallet': [
-        'Charge Taler wallet'
-      ],
-      'You can use this QR code to withdraw to your mobile wallet:': [
-        ''
-      ],
-      'this link': [
-        ''
-      ],
-      'Abort': [
-        ''
-      ],
-      'Start withdrawal': [
-        'Start withdrawal'
-      ],
-      'Withdraw Money into a Taler wallet': [
-        'Charge Taler wallet'
-      ],
-      'Amount to withdraw': [
-        'Amount to withdraw'
-      ],
-      'Please login!': [
-        ''
-      ],
-      'Login': [
-        ''
-      ],
-      'Register to the euFin bank!': [
-        ''
-      ],
-      'Registration form': [
-        ''
-      ],
-      'Register': [
-        ''
-      ],
-      'Date': [
-        ''
-      ],
-      'Amount': [
-        ''
-      ],
-      'Counterpart': [
-        ''
-      ],
-      'Subject': [
-        ''
-      ],
-      'Username or account label \'%1$s\' not found.  Won\'t login.': [
-        ''
-      ],
-      'Wrong credentials given.': [
-        ''
-      ],
-      'Account information could not be retrieved.': [
-        ''
-      ],
-      'Close wire transfer': [
-        ''
-      ],
-      'Close Taler withdrawal': [
-        'Close Taler withdrawal'
-      ],
-      'Bank account balance:': [
-        ''
-      ],
-      'Latest transactions:': [
-        ''
-      ],
-      'Transfer money manually': [
-        ''
-      ],
-      'List of public accounts was not found.': [
-        ''
-      ],
-      'List of public accounts could not be retrieved.': [
-        ''
-      ],
-      'History of public accounts': [
-        ''
-      ],
-      'Page has a problem: logged in but backend state is lost.': [
-        'Page has a problem: logged in but backend state is lost.'
-      ],
-      'Welcome to the euFin bank!': [
-        'Welcome to euFin bank: Taler+IBAN now possible!'
-      ],
-      '': {
-        'domain': 'messages',
-        'plural_forms': 'nplurals=2; plural=(n != 1);',
-        'lang': 'en'
-      }
-    }
-  }
+strings["en"] = {
+  domain: "messages",
+  locale_data: {
+    messages: {
+      days: ["days"],
+      hours: ["hours"],
+      minutes: ["minutes"],
+      seconds: ["seconds"],
+      Clear: [""],
+      Logout: [""],
+      "Demo Bank": [""],
+      "Go back": ["Go back"],
+      "Wire transfer": [""],
+      "Transfer money to another account of this bank:": [""],
+      "Want to try the raw payto://-format?": [""],
+      "Transfer money via the Payto system:": [""],
+      "payto address": [""],
+      Confirm: [""],
+      "Confirm Withdrawal": ["Confirm withdrawal"],
+      "Waiting the bank to create the operaion...": [""],
+      "This withdrawal was aborted!": [""],
+      "Withdraw to a Taler Wallet": ["Charge Taler wallet"],
+      "You can use this QR code to withdraw to your mobile wallet:": [""],
+      "this link": [""],
+      Abort: [""],
+      "Start withdrawal": ["Start withdrawal"],
+      "Withdraw Money into a Taler wallet": ["Charge Taler wallet"],
+      "Amount to withdraw": ["Amount to withdraw"],
+      "Please login!": [""],
+      Login: [""],
+      "Register to the euFin bank!": [""],
+      "Registration form": [""],
+      Register: [""],
+      Date: [""],
+      Amount: [""],
+      Counterpart: [""],
+      Subject: [""],
+      "Username or account label '%1$s' not found.  Won't login.": [""],
+      "Wrong credentials given.": [""],
+      "Account information could not be retrieved.": [""],
+      "Close wire transfer": [""],
+      "Close Taler withdrawal": ["Close Taler withdrawal"],
+      "Bank account balance:": [""],
+      "Latest transactions:": [""],
+      "Transfer money manually": [""],
+      "List of public accounts was not found.": [""],
+      "List of public accounts could not be retrieved.": [""],
+      "History of public accounts": [""],
+      "Page has a problem: logged in but backend state is lost.": [
+        "Page has a problem: logged in but backend state is lost.",
+      ],
+      "Welcome to the euFin bank!": [
+        "Welcome to euFin bank: Taler+IBAN now possible!",
+      ],
+      "": {
+        domain: "messages",
+        plural_forms: "nplurals=2; plural=(n != 1);",
+        lang: "en",
+      },
+    },
+  },
 };
 
-strings['it'] = {
-  'domain': 'messages',
-  'locale_data': {
-    'messages': {
-      'days': [
-        ''
-      ],
-      'hours': [
-        ''
-      ],
-      'minutes': [
-        ''
-      ],
-      'seconds': [
-        ''
-      ],
-      'Clear': [
-        'Cancella'
-      ],
-      'Logout': [
-        ''
-      ],
-      'Demo Bank': [
-        'Banca \'demo\''
-      ],
-      'Go back': [
-        'Indietro'
-      ],
-      'Wire transfer': [
-        'Bonifico'
-      ],
-      'Transfer money to another account of this bank:': [
-        'Trasferisci fondi a un altro conto di questa banca:'
-      ],
-      'Want to try the raw payto://-format?': [
-        'Prova il trasferimento tramite il formato Payto!'
-      ],
-      'Transfer money via the Payto system:': [
-        'Effettua un bonifico tramite il sistema Payto:'
-      ],
-      'payto address': [
-        'indirizzo Payto'
-      ],
-      'Confirm': [
-        'Conferma'
-      ],
-      'Confirm Withdrawal': [
-        'Conferma il ritiro'
-      ],
-      'Waiting the bank to create the operaion...': [
-        'La banca sta creando l\'operazione...'
-      ],
-      'This withdrawal was aborted!': [
-        'Questo ritiro è stato annullato!'
-      ],
-      'Withdraw to a Taler Wallet': [
-        'Ritira contante nel portafoglio Taler'
-      ],
-      'You can use this QR code to withdraw to your mobile wallet:': [
-        'Usa questo codice QR per ritirare contante nel tuo wallet:'
-      ],
-      'this link': [
-        'questo link'
-      ],
-      'Abort': [
-        'Annulla'
-      ],
-      'Start withdrawal': [
-        'Ritira contante'
-      ],
-      'Withdraw Money into a Taler wallet': [
-        'Ritira contante nel portafoglio Taler'
-      ],
-      'Amount to withdraw': [
-        'Somma da ritirare'
-      ],
-      'Please login!': [
-        'Accedi!'
-      ],
-      'Login': [
-        'Accedi'
-      ],
-      'Register to the euFin bank!': [
-        'Apri un conto in banca euFin!'
-      ],
-      'Registration form': [
-        'Registrazione'
-      ],
-      'Register': [
-        'Registrati'
-      ],
-      'Date': [
-        ''
-      ],
-      'Amount': [
-        'Somma'
-      ],
-      'Counterpart': [
-        'Controparte'
-      ],
-      'Subject': [
-        'Causale'
-      ],
-      'Username or account label \'%1$s\' not found.  Won\'t login.': [
-        'L\'utente \'%1$s\' non esiste.  Login impossibile'
-      ],
-      'Wrong credentials given.': [
-        'Credenziali invalide.'
-      ],
-      'Account information could not be retrieved.': [
-        'Impossibile ricevere le informazioni relative al conto.'
-      ],
-      'Close wire transfer': [
-        'Chiudi il bonifico'
-      ],
-      'Close Taler withdrawal': [
-        'Chiudi il ritiro Taler'
-      ],
-      'Bank account balance:': [
-        'Bilancio:'
-      ],
-      'Latest transactions:': [
-        'Ultime transazioni:'
-      ],
-      'Transfer money manually': [
-        'Effettua un bonifico'
-      ],
-      'List of public accounts was not found.': [
-        'Lista conti pubblici non trovata.'
-      ],
-      'List of public accounts could not be retrieved.': [
-        'Lista conti pubblici non pervenuta.'
-      ],
-      'History of public accounts': [
-        'Storico dei conti pubblici'
-      ],
-      'Page has a problem: logged in but backend state is lost.': [
-        'Stato inconsistente: accesso utente effettuato ma stato con server 
perso.'
-      ],
-      'Welcome to the euFin bank!': [
-        'Benvenuti in banca euFin!'
-      ],
-      '': {
-        'domain': 'messages',
-        'plural_forms': 'nplurals=2; plural=(n != 1);',
-        'lang': 'it'
-      }
-    }
-  }
+strings["it"] = {
+  domain: "messages",
+  locale_data: {
+    messages: {
+      days: [""],
+      hours: [""],
+      minutes: [""],
+      seconds: [""],
+      Clear: ["Cancella"],
+      Logout: [""],
+      "Demo Bank": ["Banca 'demo'"],
+      "Go back": ["Indietro"],
+      "Wire transfer": ["Bonifico"],
+      "Transfer money to another account of this bank:": [
+        "Trasferisci fondi a un altro conto di questa banca:",
+      ],
+      "Want to try the raw payto://-format?": [
+        "Prova il trasferimento tramite il formato Payto!",
+      ],
+      "Transfer money via the Payto system:": [
+        "Effettua un bonifico tramite il sistema Payto:",
+      ],
+      "payto address": ["indirizzo Payto"],
+      Confirm: ["Conferma"],
+      "Confirm Withdrawal": ["Conferma il ritiro"],
+      "Waiting the bank to create the operaion...": [
+        "La banca sta creando l'operazione...",
+      ],
+      "This withdrawal was aborted!": ["Questo ritiro è stato annullato!"],
+      "Withdraw to a Taler Wallet": ["Ritira contante nel portafoglio Taler"],
+      "You can use this QR code to withdraw to your mobile wallet:": [
+        "Usa questo codice QR per ritirare contante nel tuo wallet:",
+      ],
+      "this link": ["questo link"],
+      Abort: ["Annulla"],
+      "Start withdrawal": ["Ritira contante"],
+      "Withdraw Money into a Taler wallet": [
+        "Ritira contante nel portafoglio Taler",
+      ],
+      "Amount to withdraw": ["Somma da ritirare"],
+      "Please login!": ["Accedi!"],
+      Login: ["Accedi"],
+      "Register to the euFin bank!": ["Apri un conto in banca euFin!"],
+      "Registration form": ["Registrazione"],
+      Register: ["Registrati"],
+      Date: [""],
+      Amount: ["Somma"],
+      Counterpart: ["Controparte"],
+      Subject: ["Causale"],
+      "Username or account label '%1$s' not found.  Won't login.": [
+        "L'utente '%1$s' non esiste.  Login impossibile",
+      ],
+      "Wrong credentials given.": ["Credenziali invalide."],
+      "Account information could not be retrieved.": [
+        "Impossibile ricevere le informazioni relative al conto.",
+      ],
+      "Close wire transfer": ["Chiudi il bonifico"],
+      "Close Taler withdrawal": ["Chiudi il ritiro Taler"],
+      "Bank account balance:": ["Bilancio:"],
+      "Latest transactions:": ["Ultime transazioni:"],
+      "Transfer money manually": ["Effettua un bonifico"],
+      "List of public accounts was not found.": [
+        "Lista conti pubblici non trovata.",
+      ],
+      "List of public accounts could not be retrieved.": [
+        "Lista conti pubblici non pervenuta.",
+      ],
+      "History of public accounts": ["Storico dei conti pubblici"],
+      "Page has a problem: logged in but backend state is lost.": [
+        "Stato inconsistente: accesso utente effettuato ma stato con server 
perso.",
+      ],
+      "Welcome to the euFin bank!": ["Benvenuti in banca euFin!"],
+      "": {
+        domain: "messages",
+        plural_forms: "nplurals=2; plural=(n != 1);",
+        lang: "it",
+      },
+    },
+  },
 };
-
diff --git a/packages/demobank-ui/src/index.html 
b/packages/demobank-ui/src/index.html
index 3df1ceb5c..a2154429b 100644
--- a/packages/demobank-ui/src/index.html
+++ b/packages/demobank-ui/src/index.html
@@ -16,14 +16,20 @@
  @author Sebastian Javier Marchano
 -->
 <!DOCTYPE html>
-<html lang="en" class="has-aside-left has-aside-mobile-transition 
has-navbar-fixed-top has-aside-expanded">
+<html
+  lang="en"
+  class="has-aside-left has-aside-mobile-transition has-navbar-fixed-top 
has-aside-expanded"
+>
   <head>
-    <meta charset="utf-8">
-    <meta name="viewport" content="width=device-width,initial-scale=1">
-    <meta name="mobile-web-app-capable" content="yes">
-    <meta name="apple-mobile-web-app-capable" content="yes">
+    <meta charset="utf-8" />
+    <meta name="viewport" content="width=device-width,initial-scale=1" />
+    <meta name="mobile-web-app-capable" content="yes" />
+    <meta name="apple-mobile-web-app-capable" content="yes" />
 
-    <link rel="icon" 
href="data:;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAQAABILAAASCwAAAAAAAAAAAAD///////////////////////////////////////////////////////////////////////////////////////////////////7//v38//78/P/+/fz//vz7///+/v/+/f3//vz7///+/v/+/fz//v38///////////////////////+/v3///7+/////////////////////////////////////////////////////////v3//v79///////+/v3///////r28v/ct5//06SG/9Gffv/Xqo7/7N/V/9e2nf/bsJb/6uDW/9Sskf/euKH/+/j2///////+/v3//////+3azv+/eE3/2rWd/9Kkhv/V
 [...]
+    <link
+      rel="icon"
+      
href="data:;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAQAABILAAASCwAAAAAAAAAAAAD///////////////////////////////////////////////////////////////////////////////////////////////////7//v38//78/P/+/fz//vz7///+/v/+/f3//vz7///+/v/+/fz//v38///////////////////////+/v3///7+/////////////////////////////////////////////////////////v3//v79///////+/v3///////r28v/ct5//06SG/9Gffv/Xqo7/7N/V/9e2nf/bsJb/6uDW/9Sskf/euKH/+/j2///////+/v3//////+3azv+/eE3/2rWd/9Kkhv/Vr5T/48i2/8J+VP/
 [...]
+    />
     <link rel="shortcut icon" href="data:image/x-icon;," type="image/x-icon" />
   </head>
 
diff --git a/packages/demobank-ui/src/manifest.json 
b/packages/demobank-ui/src/manifest.json
index feca12a0e..8790b10c9 100644
--- a/packages/demobank-ui/src/manifest.json
+++ b/packages/demobank-ui/src/manifest.json
@@ -18,4 +18,4 @@
       "sizes": "512x512"
     }
   ]
-}
\ No newline at end of file
+}
diff --git a/packages/demobank-ui/src/pages/home/index.tsx 
b/packages/demobank-ui/src/pages/home/index.tsx
index 6fbb00ce4..8ab815efd 100644
--- a/packages/demobank-ui/src/pages/home/index.tsx
+++ b/packages/demobank-ui/src/pages/home/index.tsx
@@ -31,6 +31,7 @@ import { QR } from "../../components/QR.js";
 import { useLocalStorage, useNotNullLocalStorage } from "../../hooks/index.js";
 import { Translate, useTranslator } from "../../i18n/index.js";
 import "../../scss/main.scss";
+import { parsePaytoUri } from "@gnu-taler/taler-util";
 
 /**
  * If the first argument does not look like a placeholder, return it.
@@ -113,6 +114,7 @@ interface TransactionRequestType {
 interface CredentialsRequestType {
   username: string;
   password: string;
+  repeatPassword: string;
 }
 
 /**
@@ -336,14 +338,16 @@ type RawPaytoInputType = string;
 type RawPaytoInputTypeOpt = RawPaytoInputType | undefined;
 function useRawPaytoInputType(
   state?: RawPaytoInputType,
-): [RawPaytoInputTypeOpt, StateUpdater<RawPaytoInputTypeOpt>] {
+): [RawPaytoInputTypeOpt, StateUpdater<RawPaytoInputTypeOpt>, boolean] {
   const ret = useLocalStorage("raw-payto-input-state", state);
+  const [dirty, setDirty] = useState(false);
   const retObj: RawPaytoInputTypeOpt = ret[0];
   const retSetter: StateUpdater<RawPaytoInputTypeOpt> = function (val) {
     const newVal = val instanceof Function ? val(retObj) : val;
+    setDirty(true);
     ret[1](newVal);
   };
-  return [retObj, retSetter];
+  return [retObj, retSetter, dirty];
 }
 
 /**
@@ -1012,12 +1016,24 @@ function BankFrame(Props: any): VNode {
     </Fragment>
   );
 }
+function ShowInputErrorLabel({
+  isDirty,
+  message,
+}: {
+  message: string | undefined;
+  isDirty: boolean;
+}): VNode {
+  if (message && isDirty)
+    return <p class="informational informational-fail">{message}</p>;
+  return <Fragment />;
+}
 
 function PaytoWireTransfer(Props: any): VNode {
   const currency = useContext(CurrencyContext);
   const [pageState, pageStateSetter] = useContext(PageContext); // NOTE: used 
for go-back button?
   const [submitData, submitDataSetter] = useWireTransferRequestType();
-  const [rawPaytoInput, rawPaytoInputSetter] = useRawPaytoInputType();
+  const [rawPaytoInput, rawPaytoInputSetter, rawPaytoInputDirty] =
+    useRawPaytoInputType();
   const i18n = useTranslator();
   const { focus, backendState } = Props;
   const amountRegex = "^[0-9]+(.[0-9]+)?$";
@@ -1159,6 +1175,14 @@ function PaytoWireTransfer(Props: any): VNode {
       </div>
     );
 
+  const errors = undefinedIfEmpty({
+    rawPaytoInput: !rawPaytoInput
+      ? i18n`Missing payto address`
+      : !parsePaytoUri(rawPaytoInput)
+      ? i18n`Payto does not follow the pattern`
+      : undefined,
+  });
+
   return (
     <div>
       <p>{i18n`Transfer money to account identified by payto:// URI:`}</p>
@@ -1168,17 +1192,21 @@ function PaytoWireTransfer(Props: any): VNode {
           <input
             name="address"
             type="text"
-            size={90}
+            size={50}
             ref={ref}
             id="address"
             value={rawPaytoInput}
             required
             placeholder={i18n`payto address`}
-            pattern={`payto://iban/[A-Z][A-Z][0-9]+?message=[a-zA-Z0-9 
]+&amount=${currency}:[0-9]+(.[0-9]+)?`}
+            // pattern={`payto://iban/[A-Z][A-Z][0-9]+?message=[a-zA-Z0-9 
]+&amount=${currency}:[0-9]+(.[0-9]+)?`}
             onInput={(e): void => {
               rawPaytoInputSetter(e.currentTarget.value);
             }}
           />
+          <ShowInputErrorLabel
+            message={errors?.rawPaytoInput}
+            isDirty={rawPaytoInputDirty}
+          />
           <br />
           <div class="hint">
             Hint:
@@ -1192,6 +1220,7 @@ function PaytoWireTransfer(Props: any): VNode {
           <input
             class="pure-button pure-button-primary"
             type="submit"
+            disabled={!!errors}
             value={i18n`Send`}
             onClick={async () => {
               // empty string evaluates to false.
@@ -1593,6 +1622,11 @@ function RegistrationButton(Props: any): VNode {
   return <span />;
 }
 
+function undefinedIfEmpty<T extends object>(obj: T): T | undefined {
+  return Object.keys(obj).some((k) => (obj as any)[k] !== undefined)
+    ? obj
+    : undefined;
+}
 /**
  * Collect and submit login data.
  */
@@ -1604,6 +1638,16 @@ function LoginForm(Props: any): VNode {
   useEffect(() => {
     ref.current?.focus();
   }, []);
+
+  const errors = undefinedIfEmpty({
+    username: !(submitData && submitData.username)
+      ? i18n`Missing username`
+      : undefined,
+    password: !(submitData && submitData.password)
+      ? i18n`Missing password`
+      : undefined,
+  });
+
   return (
     <div class="login-div">
       <form action="javascript:void(0);" class="login-form">
@@ -1649,6 +1693,7 @@ function LoginForm(Props: any): VNode {
           <button
             type="submit"
             class="pure-button pure-button-primary"
+            disabled={!!errors}
             onClick={() => {
               if (typeof submitData === "undefined") {
                 console.log("login data is undefined", submitData);
@@ -1691,7 +1736,19 @@ function RegistrationForm(Props: any): VNode {
   const [pageState, pageStateSetter] = useContext(PageContext);
   const [submitData, submitDataSetter] = useCredentialsRequestType();
   const i18n = useTranslator();
-  // 
https://stackoverflow.com/questions/36683770/how-to-get-the-value-of-an-input-field-using-reactjs
+
+  const errors = !submitData
+    ? undefined
+    : undefinedIfEmpty({
+        username: !submitData.username ? i18n`Missing username` : undefined,
+        password: !submitData.password ? i18n`Missing password` : undefined,
+        repeatPassword: !submitData.repeatPassword
+          ? i18n`Missing password`
+          : submitData.repeatPassword !== submitData.password
+          ? i18n`Password don't match`
+          : undefined,
+      });
+
   return (
     <Fragment>
       <h1 class="nav">{i18n`Welcome to ${UI_BANK_NAME}!`}</h1>
@@ -1735,6 +1792,24 @@ function RegistrationForm(Props: any): VNode {
                   }));
                 }}
               />
+              <p class="unameFieldLabel registerFieldLabel formFieldLabel">
+                <label for="register-repeat">{i18n`Repeat Password:`}</label>
+              </p>
+              <input
+                type="password"
+                style={{ marginBottom: 8 }}
+                name="register-repeat"
+                id="register-repeat"
+                placeholder="Same password"
+                value={submitData && submitData.repeatPassword}
+                required
+                onInput={(e): void => {
+                  submitDataSetter((submitData: any) => ({
+                    ...submitData,
+                    repeatPassword: e.currentTarget.value,
+                  }));
+                }}
+              />
               <br />
               {/*
               <label for="phone">{i18n`Phone number:`}</label>
@@ -1755,28 +1830,10 @@ function RegistrationForm(Props: any): VNode {
               */}
               <button
                 class="pure-button pure-button-primary btn-register"
+                disabled={!!errors}
                 onClick={() => {
                   console.log("maybe submitting the registration..");
-                  console.log(submitData);
-                  if (typeof submitData === "undefined") {
-                    console.log(`submit data ${submitData} is undefined`);
-                    return;
-                  }
-                  if (
-                    typeof submitData.password === "undefined" ||
-                    typeof submitData.username === "undefined"
-                  ) {
-                    console.log("username or password is undefined");
-                    return;
-                  }
-                  if (
-                    submitData.password.length === 0 ||
-                    submitData.username.length === 0
-                  ) {
-                    console.log("username or password are the empty string");
-                    return;
-                  }
-                  console.log("submitting the registration..");
+                  if (!submitData) return;
                   registrationCall(
                     { ...submitData },
                     Props.backendStateSetter, // will store BE URL, if OK.
@@ -1790,7 +1847,11 @@ function RegistrationForm(Props: any): VNode {
                    * strings due to a non lively update of the <input> fields
                    * after setting to undefined.
                    */
-                  submitDataSetter({ username: "", password: "" });
+                  submitDataSetter({
+                    username: "",
+                    password: "",
+                    repeatPassword: "",
+                  });
                 }}
               >
                 {i18n`Register`}
@@ -1799,6 +1860,11 @@ function RegistrationForm(Props: any): VNode {
               <button
                 class="pure-button pure-button-secondary btn-cancel"
                 onClick={() => {
+                  submitDataSetter({
+                    username: "",
+                    password: "",
+                    repeatPassword: "",
+                  });
                   pageStateSetter((prevState: PageStateType) => ({
                     ...prevState,
                     tryRegister: false,
diff --git a/packages/demobank-ui/src/pages/profile/index.stories.tsx 
b/packages/demobank-ui/src/pages/profile/index.stories.tsx
index 15fd7c7e5..7ea06ee98 100644
--- a/packages/demobank-ui/src/pages/profile/index.stories.tsx
+++ b/packages/demobank-ui/src/pages/profile/index.stories.tsx
@@ -15,24 +15,22 @@
  */
 
 /**
-*
-* @author Sebastian Javier Marchano (sebasjm)
-*/
-
-import { h } from 'preact';
-import Profile from './index';
+ *
+ * @author Sebastian Javier Marchano (sebasjm)
+ */
 
+import { h } from "preact";
+import Profile from "./index";
 
 export default {
-  title: 'Profile/View',
+  title: "Profile/View",
   component: Profile,
   argTypes: {
-    onSelect: { action: 'onSelect' },
+    onSelect: { action: "onSelect" },
   },
 };
 
 export const Empty = (a: any) => <Profile {...a} />;
 Empty.args = {
-  instances: []
-}
-
+  instances: [],
+};
diff --git a/packages/demobank-ui/src/scss/_custom-calendar.scss 
b/packages/demobank-ui/src/scss/_custom-calendar.scss
index e0334b62d..463cd88d3 100644
--- a/packages/demobank-ui/src/scss/_custom-calendar.scss
+++ b/packages/demobank-ui/src/scss/_custom-calendar.scss
@@ -135,9 +135,9 @@
       text-align: center;
 
       // there's probably a better way to do this, but wanted to try out CSS 
grid
-      grid-template-columns: calc(100% / 7) calc(100% / 7) calc(100% / 7) calc(
-          100% / 7
-        ) calc(100% / 7) calc(100% / 7) calc(100% / 7);
+      grid-template-columns:
+        calc(100% / 7) calc(100% / 7) calc(100% / 7) calc(100% / 7)
+        calc(100% / 7) calc(100% / 7) calc(100% / 7);
 
       span {
         color: var(--secondary-text-color-dark);
@@ -151,9 +151,9 @@
       width: 100%;
       display: grid;
       text-align: center;
-      grid-template-columns: calc(100% / 7) calc(100% / 7) calc(100% / 7) calc(
-          100% / 7
-        ) calc(100% / 7) calc(100% / 7) calc(100% / 7);
+      grid-template-columns:
+        calc(100% / 7) calc(100% / 7) calc(100% / 7) calc(100% / 7)
+        calc(100% / 7) calc(100% / 7) calc(100% / 7);
 
       span {
         color: var(--primary-text-color-dark);
diff --git a/packages/demobank-ui/src/scss/bank.scss 
b/packages/demobank-ui/src/scss/bank.scss
index b524cfe29..fec0ea5c9 100644
--- a/packages/demobank-ui/src/scss/bank.scss
+++ b/packages/demobank-ui/src/scss/bank.scss
@@ -1,5 +1,5 @@
 .navcontainer:not(.default-navcontainer) {
-    margin-bottom: 0 !important;
+  margin-bottom: 0 !important;
 }
 
 .abort-button {
@@ -17,8 +17,8 @@ div.pages-list {
 
 .login-div,
 .register-div {
-    display: block;
-    text-align: center;
+  display: block;
+  text-align: center;
 }
 
 a.page-number {
@@ -42,55 +42,55 @@ input[type="number"]::-webkit-inner-spin-button {
 
 /* This CSS code styles the tab */
 .tab {
-    overflow: hidden;
+  overflow: hidden;
 }
 
 .logout {
-    float: right;
-    border: 20px;
-    margin-right: 15px;
-    margin-top: 15px;
+  float: right;
+  border: 20px;
+  margin-right: 15px;
+  margin-top: 15px;
 }
 
 .tab button {
-    background-color: lightgray;
-    color: black;
-    float: left;
-    border: none;
-    outline: none;
-    cursor: pointer;
-    padding: 18px 19px;
-    border: 2px solid #c1c1c1;
-    transition: 0.5s;
-    font-weight: bold;
+  background-color: lightgray;
+  color: black;
+  float: left;
+  border: none;
+  outline: none;
+  cursor: pointer;
+  padding: 18px 19px;
+  border: 2px solid #c1c1c1;
+  transition: 0.5s;
+  font-weight: bold;
 }
 
 .tab button:hover {
-    background-color: yellow;
-    border: 2px solid #c1c1c1;
-    color: black;
+  background-color: yellow;
+  border: 2px solid #c1c1c1;
+  color: black;
 }
 
 .tab button.active {
-    background-color: orange;
-    border: 2px solid #c1c1c1;
-    color: black;
-    font-weight: bold;
+  background-color: orange;
+  border: 2px solid #c1c1c1;
+  color: black;
+  font-weight: bold;
 }
 
 .tabcontent {
-    display: none;
-    padding: 8px 16px;
-    border: 2px solid #c1c1c1;
-    width: max-content;
+  display: none;
+  padding: 8px 16px;
+  border: 2px solid #c1c1c1;
+  width: max-content;
 }
 
 .tabcontent.active {
-    display: block;
+  display: block;
 }
 
 input[type="number"] {
-    -moz-appearance: textfield;
+  -moz-appearance: textfield;
 }
 
 #transfer-fields {
@@ -115,16 +115,16 @@ input[type="number"] {
 }
 
 input {
-    background-color: inherit;
+  background-color: inherit;
 }
 
 .large-amount {
-    font-weight: bold;
-    font-size: x-large;
+  font-weight: bold;
+  font-size: xxx-large;
 }
 
 .currency {
-    font-style: oblique;
+  font-style: oblique;
 }
 
 /*
@@ -154,111 +154,111 @@ input {
 
 .register-form > .pure-form,
 .login-form > .pure-form {
-    background: #4a4a4a;
-    color: #ffffff;
-    display: inline-block;
-    text-align: left;
-    margin-left: auto;
-    margin-right: auto;
-    padding: 16px 16px;
-    border-radius: 8px;
-    width: max-content;
-    .formFieldLabel {
-        margin: 2px 2px;
-    }
-    input[type="text"],
-    input[type="password"] {
-        border: none;
-        border-radius: 4px;
-        background: #6a6a6a;
-        color: #fefefe;
-        box-shadow: none;
-    }
-    input[placeholder="Password"][type="password"] {
-        margin-bottom: 8px;
-    }
-    .btn-register,
-    .btn-login {
-        float: left;
-    }
-    .btn-cancel {
-        float: right;
-    }
-    h2 {
-        margin-top: 0;
-        margin-bottom: 10px;
-    }
+  background: #4a4a4a;
+  color: #ffffff;
+  display: inline-block;
+  text-align: left;
+  margin-left: auto;
+  margin-right: auto;
+  padding: 16px 16px;
+  border-radius: 8px;
+  width: max-content;
+  .formFieldLabel {
+    margin: 2px 2px;
+  }
+  input[type="text"],
+  input[type="password"] {
+    border: none;
+    border-radius: 4px;
+    background: #6a6a6a;
+    color: #fefefe;
+    box-shadow: none;
+  }
+  input[placeholder="Password"][type="password"] {
+    margin-bottom: 8px;
+  }
+  .btn-register,
+  .btn-login {
+    float: left;
+  }
+  .btn-cancel {
+    float: right;
+  }
+  h2 {
+    margin-top: 0;
+    margin-bottom: 10px;
+  }
 }
 
-
 .challenge-div {
-    display: block;
-    text-align: center;
+  display: block;
+  text-align: center;
 }
 
 .challenge-form > .pure-form {
-    background: #4a4a4a;
-    color: #ffffff;
-    display: inline-block;
-    text-align: left;
-    margin-left: auto;
-    margin-right: auto;
-    padding: 16px 16px;
-    border-radius: 8px;
-    width: max-content;
-    .formFieldLabel {
-        margin: 2px 2px;
-    }
-    input[type="text"] {
-        border: none;
-        border-radius: 4px;
-        background: #6a6a6a;
-        color: #fefefe;
-        box-shadow: none;
-    }
-    .btn-confirm {
-        float: left;
-    }
-    .btn-cancel {
-        float: right;
-    }
-    h2 {
-        margin-top: 0;
-        margin-bottom: 10px;
-    }
+  background: #4a4a4a;
+  color: #ffffff;
+  display: inline-block;
+  text-align: left;
+  margin-left: auto;
+  margin-right: auto;
+  padding: 16px 16px;
+  border-radius: 8px;
+  width: max-content;
+  .formFieldLabel {
+    margin: 2px 2px;
+  }
+  input[type="text"] {
+    border: none;
+    border-radius: 4px;
+    background: #6a6a6a;
+    color: #fefefe;
+    box-shadow: none;
+  }
+  .btn-confirm {
+    float: left;
+  }
+  .btn-cancel {
+    float: right;
+  }
+  h2 {
+    margin-top: 0;
+    margin-bottom: 10px;
+  }
 }
 
-
 .wire-transfer-form > .pure-form,
 .payto-form > .pure-form,
 .reserve-form > .pure-form {
-    background: #4a4a4a;
-    color: #ffffff;
-    display: inline-block;
-    text-align: left;
-    margin-left: auto;
-    margin-right: auto;
-    padding: 16px 16px;
-    border-radius: 8px;
-    width: max-content;
-    .formFieldLabel {
-        margin: 2px 2px;
-    }
-    input[type="text"] {
-        border: none;
-        border-radius: 4px;
-        background: #6a6a6a;
-        color: #fefefe;
-        box-shadow: none;
-    }
+  background: #4a4a4a;
+  color: #ffffff;
+  display: inline-block;
+  text-align: left;
+  margin-left: auto;
+  margin-right: auto;
+  padding: 16px 16px;
+  border-radius: 8px;
+  width: max-content;
+  .formFieldLabel {
+    margin: 2px 2px;
+  }
+  input[type="text"] {
+    border: none;
+    border-radius: 4px;
+    background: #6a6a6a;
+    color: #fefefe;
+    box-shadow: none;
+  }
 }
 
-
 html {
-    background: #ffffff;
-    color: #2a2a2a;
+  background: #ffffff;
+  color: #2a2a2a;
 }
 
 .hint {
-    scale: 0.7;
+  scale: 0.7;
+}
+h1.nav {
+  text-align: center;
 }
diff --git a/packages/demobank-ui/src/scss/colors-bank.scss 
b/packages/demobank-ui/src/scss/colors-bank.scss
index c34610948..e11bbe203 100644
--- a/packages/demobank-ui/src/scss/colors-bank.scss
+++ b/packages/demobank-ui/src/scss/colors-bank.scss
@@ -28,4 +28,4 @@ nav span:hover,
 
 nav a.navbtn.langbtn:focus {
   background-color: #df3d3d;
-}
\ No newline at end of file
+}
diff --git a/packages/demobank-ui/src/scss/fonts/nunito.css 
b/packages/demobank-ui/src/scss/fonts/nunito.css
index ab30db36b..8d45df9a1 100644
--- a/packages/demobank-ui/src/scss/fonts/nunito.css
+++ b/packages/demobank-ui/src/scss/fonts/nunito.css
@@ -15,8 +15,8 @@
  */
 
 @font-face {
-  font-family: 'Nunito';
+  font-family: "Nunito";
   font-style: normal;
   font-weight: 400;
-  src: url(./XRXV3I6Li01BKofINeaE.ttf) format('truetype');
+  src: url(./XRXV3I6Li01BKofINeaE.ttf) format("truetype");
 }
diff --git a/packages/demobank-ui/src/scss/libs/_all.scss 
b/packages/demobank-ui/src/scss/libs/_all.scss
index 08bd76cd1..d33f8acc4 100644
--- a/packages/demobank-ui/src/scss/libs/_all.scss
+++ b/packages/demobank-ui/src/scss/libs/_all.scss
@@ -14,7 +14,7 @@
  GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 
- /**
+/**
  *
  * @author Sebastian Javier Marchano (sebasjm)
  */
diff --git a/packages/demobank-ui/src/scss/pure.scss 
b/packages/demobank-ui/src/scss/pure.scss
index 83fcadce7..af98eb293 100644
--- a/packages/demobank-ui/src/scss/pure.scss
+++ b/packages/demobank-ui/src/scss/pure.scss
@@ -18,7 +18,7 @@ Copyright (c) Nicolas Gallagher and Jonathan Neal
  * 2. Prevent adjustments of font size after orientation changes in iOS.
  */
 
- html {
+html {
   line-height: 1.15; /* 1 */
   -webkit-text-size-adjust: 100%; /* 2 */
 }
@@ -62,7 +62,7 @@ h1 {
 
 hr {
   -webkit-box-sizing: content-box;
-          box-sizing: content-box; /* 1 */
+  box-sizing: content-box; /* 1 */
   height: 0; /* 1 */
   overflow: visible; /* 2 */
 }
@@ -97,7 +97,7 @@ abbr[title] {
   border-bottom: none; /* 1 */
   text-decoration: underline; /* 2 */
   -webkit-text-decoration: underline dotted;
-          text-decoration: underline dotted; /* 2 */
+  text-decoration: underline dotted; /* 2 */
 }
 
 /**
@@ -186,7 +186,8 @@ textarea {
  */
 
 button,
-input { /* 1 */
+input {
+  /* 1 */
   overflow: visible;
 }
 
@@ -196,7 +197,8 @@ input { /* 1 */
  */
 
 button,
-select { /* 1 */
+select {
+  /* 1 */
   text-transform: none;
 }
 
@@ -251,7 +253,7 @@ fieldset {
 
 legend {
   -webkit-box-sizing: border-box;
-          box-sizing: border-box; /* 1 */
+  box-sizing: border-box; /* 1 */
   color: inherit; /* 2 */
   display: table; /* 1 */
   max-width: 100%; /* 1 */
@@ -283,7 +285,7 @@ textarea {
 [type="checkbox"],
 [type="radio"] {
   -webkit-box-sizing: border-box;
-          box-sizing: border-box; /* 1 */
+  box-sizing: border-box; /* 1 */
   padding: 0; /* 2 */
 }
 
@@ -373,7 +375,7 @@ template {
  */
 
 html {
-    font-family: sans-serif;
+  font-family: sans-serif;
 }
 
 /**
@@ -382,7 +384,7 @@ html {
 
 .hidden,
 [hidden] {
-    display: none !important;
+  display: none !important;
 }
 
 /**
@@ -390,18 +392,18 @@ html {
  * aspect ratio.
  */
 .pure-img {
-    max-width: 100%;
-    height: auto;
-    display: block;
+  max-width: 100%;
+  height: auto;
+  display: block;
 }
 
 /*csslint regex-selectors:false, known-properties:false, 
duplicate-properties:false*/
 
 .pure-g {
-    letter-spacing: -0.31em; /* Webkit: collapse white-space between units */
-    text-rendering: optimizespeed; /* Webkit: fixes text-rendering: 
optimizeLegibility */
+  letter-spacing: -0.31em; /* Webkit: collapse white-space between units */
+  text-rendering: optimizespeed; /* Webkit: fixes text-rendering: 
optimizeLegibility */
 
-    /*
+  /*
     Sets the font stack to fonts known to work properly with the above letter
     and word spacings. See: https://github.com/pure-css/pure/issues/41/
 
@@ -417,27 +419,27 @@ html {
 
     * Helvetica, Arial, sans-serif: Common font stack on OS X and Windows.
     */
-    font-family: FreeSans, Arimo, "Droid Sans", Helvetica, Arial, sans-serif;
+  font-family: FreeSans, Arimo, "Droid Sans", Helvetica, Arial, sans-serif;
 
-    /* Use flexbox when possible to avoid `letter-spacing` side-effects. */
-    display: -webkit-box;
-    display: -ms-flexbox;
-    display: flex;
-    -webkit-box-orient: horizontal;
-    -webkit-box-direction: normal;
-        -ms-flex-flow: row wrap;
-            flex-flow: row wrap;
+  /* Use flexbox when possible to avoid `letter-spacing` side-effects. */
+  display: -webkit-box;
+  display: -ms-flexbox;
+  display: flex;
+  -webkit-box-orient: horizontal;
+  -webkit-box-direction: normal;
+  -ms-flex-flow: row wrap;
+  flex-flow: row wrap;
 
-    /* Prevents distributing space between rows */
-    -ms-flex-line-pack: start;
-        align-content: flex-start;
+  /* Prevents distributing space between rows */
+  -ms-flex-line-pack: start;
+  align-content: flex-start;
 }
 
 /* IE10 display: -ms-flexbox (and display: flex in IE 11) does not work inside 
a table; fall back to block and rely on font hack */
 @media all and (-ms-high-contrast: none), (-ms-high-contrast: active) {
-       table .pure-g {
-               display: block;
-       }
+  table .pure-g {
+    display: block;
+  }
 }
 
 /* Opera as of 12 on Windows needs word-spacing.
@@ -446,23 +448,23 @@ html {
 */
 .opera-only :-o-prefocus,
 .pure-g {
-    word-spacing: -0.43em;
+  word-spacing: -0.43em;
 }
 
 .pure-u {
-    display: inline-block;
-    letter-spacing: normal;
-    word-spacing: normal;
-    vertical-align: top;
-    text-rendering: auto;
+  display: inline-block;
+  letter-spacing: normal;
+  word-spacing: normal;
+  vertical-align: top;
+  text-rendering: auto;
 }
 
 /*
 Resets the font family back to the OS/browser's default sans-serif font,
 this the same font stack that Normalize.css sets for the `body`.
 */
-.pure-g [class *= "pure-u"] {
-    font-family: sans-serif;
+.pure-g [class*="pure-u"] {
+  font-family: sans-serif;
 }
 
 .pure-u-1,
@@ -511,210 +513,223 @@ this the same font stack that Normalize.css sets for 
the `body`.
 .pure-u-22-24,
 .pure-u-23-24,
 .pure-u-24-24 {
-    display: inline-block;
-    letter-spacing: normal;
-    word-spacing: normal;
-    vertical-align: top;
-    text-rendering: auto;
+  display: inline-block;
+  letter-spacing: normal;
+  word-spacing: normal;
+  vertical-align: top;
+  text-rendering: auto;
 }
 
 .pure-u-1-24 {
-    width: 4.1667%;
+  width: 4.1667%;
 }
 
 .pure-u-1-12,
 .pure-u-2-24 {
-    width: 8.3333%;
+  width: 8.3333%;
 }
 
 .pure-u-1-8,
 .pure-u-3-24 {
-    width: 12.5000%;
+  width: 12.5%;
 }
 
 .pure-u-1-6,
 .pure-u-4-24 {
-    width: 16.6667%;
+  width: 16.6667%;
 }
 
 .pure-u-1-5 {
-    width: 20%;
+  width: 20%;
 }
 
 .pure-u-5-24 {
-    width: 20.8333%;
+  width: 20.8333%;
 }
 
 .pure-u-1-4,
 .pure-u-6-24 {
-    width: 25%;
+  width: 25%;
 }
 
 .pure-u-7-24 {
-    width: 29.1667%;
+  width: 29.1667%;
 }
 
 .pure-u-1-3,
 .pure-u-8-24 {
-    width: 33.3333%;
+  width: 33.3333%;
 }
 
 .pure-u-3-8,
 .pure-u-9-24 {
-    width: 37.5000%;
+  width: 37.5%;
 }
 
 .pure-u-2-5 {
-    width: 40%;
+  width: 40%;
 }
 
 .pure-u-5-12,
 .pure-u-10-24 {
-    width: 41.6667%;
+  width: 41.6667%;
 }
 
 .pure-u-11-24 {
-    width: 45.8333%;
+  width: 45.8333%;
 }
 
 .pure-u-1-2,
 .pure-u-12-24 {
-    width: 50%;
+  width: 50%;
 }
 
 .pure-u-13-24 {
-    width: 54.1667%;
+  width: 54.1667%;
 }
 
 .pure-u-7-12,
 .pure-u-14-24 {
-    width: 58.3333%;
+  width: 58.3333%;
 }
 
 .pure-u-3-5 {
-    width: 60%;
+  width: 60%;
 }
 
 .pure-u-5-8,
 .pure-u-15-24 {
-    width: 62.5000%;
+  width: 62.5%;
 }
 
 .pure-u-2-3,
 .pure-u-16-24 {
-    width: 66.6667%;
+  width: 66.6667%;
 }
 
 .pure-u-17-24 {
-    width: 70.8333%;
+  width: 70.8333%;
 }
 
 .pure-u-3-4,
 .pure-u-18-24 {
-    width: 75%;
+  width: 75%;
 }
 
 .pure-u-19-24 {
-    width: 79.1667%;
+  width: 79.1667%;
 }
 
 .pure-u-4-5 {
-    width: 80%;
+  width: 80%;
 }
 
 .pure-u-5-6,
 .pure-u-20-24 {
-    width: 83.3333%;
+  width: 83.3333%;
 }
 
 .pure-u-7-8,
 .pure-u-21-24 {
-    width: 87.5000%;
+  width: 87.5%;
 }
 
 .pure-u-11-12,
 .pure-u-22-24 {
-    width: 91.6667%;
+  width: 91.6667%;
 }
 
 .pure-u-23-24 {
-    width: 95.8333%;
+  width: 95.8333%;
 }
 
 .pure-u-1,
 .pure-u-1-1,
 .pure-u-5-5,
 .pure-u-24-24 {
-    width: 100%;
+  width: 100%;
 }
 .pure-button {
-    /* Structure */
-    display: inline-block;
-    line-height: normal;
-    white-space: nowrap;
-    vertical-align: middle;
-    text-align: center;
-    cursor: pointer;
-    -webkit-user-drag: none;
-    -webkit-user-select: none;
-       -moz-user-select: none;
-        -ms-user-select: none;
-            user-select: none;
-    -webkit-box-sizing: border-box;
-            box-sizing: border-box;
+  /* Structure */
+  display: inline-block;
+  line-height: normal;
+  white-space: nowrap;
+  vertical-align: middle;
+  text-align: center;
+  cursor: pointer;
+  -webkit-user-drag: none;
+  -webkit-user-select: none;
+  -moz-user-select: none;
+  -ms-user-select: none;
+  user-select: none;
+  -webkit-box-sizing: border-box;
+  box-sizing: border-box;
 }
 
 /* Firefox: Get rid of the inner focus border */
 .pure-button::-moz-focus-inner {
-    padding: 0;
-    border: 0;
+  padding: 0;
+  border: 0;
 }
 
 /* Inherit .pure-g styles */
 .pure-button-group {
-    letter-spacing: -0.31em; /* Webkit: collapse white-space between units */
-    text-rendering: optimizespeed; /* Webkit: fixes text-rendering: 
optimizeLegibility */
+  letter-spacing: -0.31em; /* Webkit: collapse white-space between units */
+  text-rendering: optimizespeed; /* Webkit: fixes text-rendering: 
optimizeLegibility */
 }
 
 .opera-only :-o-prefocus,
 .pure-button-group {
-    word-spacing: -0.43em;
+  word-spacing: -0.43em;
 }
 
 .pure-button-group .pure-button {
-    letter-spacing: normal;
-    word-spacing: normal;
-    vertical-align: top;
-    text-rendering: auto;
+  letter-spacing: normal;
+  word-spacing: normal;
+  vertical-align: top;
+  text-rendering: auto;
 }
 
 /*csslint outline-none:false*/
 
 .pure-button {
-    font-family: inherit;
-    font-size: 100%;
-    padding: 0.5em 1em;
-    color: rgba(0, 0, 0, 0.80);
-    border: none rgba(0, 0, 0, 0);
-    background-color: #E6E6E6;
-    text-decoration: none;
-    border-radius: 2px;
+  font-family: inherit;
+  font-size: 100%;
+  padding: 0.5em 1em;
+  color: rgba(0, 0, 0, 0.8);
+  border: none rgba(0, 0, 0, 0);
+  background-color: #e6e6e6;
+  text-decoration: none;
+  border-radius: 2px;
 }
 
 .pure-button-hover,
 .pure-button:hover,
 .pure-button:focus {
-    background-image: -webkit-gradient(linear, left top, left bottom, 
from(transparent), color-stop(40%, rgba(0,0,0, 0.05)), to(rgba(0,0,0, 0.10)));
-    background-image: linear-gradient(transparent, rgba(0,0,0, 0.05) 40%, 
rgba(0,0,0, 0.10));
+  background-image: -webkit-gradient(
+    linear,
+    left top,
+    left bottom,
+    from(transparent),
+    color-stop(40%, rgba(0, 0, 0, 0.05)),
+    to(rgba(0, 0, 0, 0.1))
+  );
+  background-image: linear-gradient(
+    transparent,
+    rgba(0, 0, 0, 0.05) 40%,
+    rgba(0, 0, 0, 0.1)
+  );
 }
 .pure-button:focus {
-    outline: 0;
+  outline: 0;
 }
 .pure-button-active,
 .pure-button:active {
-    -webkit-box-shadow: 0 0 0 1px rgba(0,0,0, 0.15) inset, 0 0 6px rgba(0,0,0, 
0.20) inset;
-            box-shadow: 0 0 0 1px rgba(0,0,0, 0.15) inset, 0 0 6px rgba(0,0,0, 
0.20) inset;
-    border-color: #000;
+  -webkit-box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.15) inset,
+    0 0 6px rgba(0, 0, 0, 0.2) inset;
+  box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.15) inset,
+    0 0 6px rgba(0, 0, 0, 0.2) inset;
+  border-color: #000;
 }
 
 .pure-button[disabled],
@@ -722,43 +737,42 @@ this the same font stack that Normalize.css sets for the 
`body`.
 .pure-button-disabled:hover,
 .pure-button-disabled:focus,
 .pure-button-disabled:active {
-    border: none;
-    background-image: none;
-    opacity: 0.40;
-    cursor: not-allowed;
-    -webkit-box-shadow: none;
-            box-shadow: none;
-    pointer-events: none;
+  border: none;
+  background-image: none;
+  opacity: 0.4;
+  cursor: not-allowed;
+  -webkit-box-shadow: none;
+  box-shadow: none;
+  pointer-events: none;
 }
 
 .pure-button-hidden {
-    display: none;
+  display: none;
 }
 
 .pure-button-primary,
 .pure-button-selected,
 a.pure-button-primary,
 a.pure-button-selected {
-    background-color: rgb(0, 120, 231);
-    color: #fff;
+  background-color: rgb(0, 120, 231);
+  color: #fff;
 }
 
 /* Button Groups */
 .pure-button-group .pure-button {
-    margin: 0;
-    border-radius: 0;
-    border-right: 1px solid rgba(0, 0, 0, 0.2);
-
+  margin: 0;
+  border-radius: 0;
+  border-right: 1px solid rgba(0, 0, 0, 0.2);
 }
 
 .pure-button-group .pure-button:first-child {
-    border-top-left-radius: 2px;
-    border-bottom-left-radius: 2px;
+  border-top-left-radius: 2px;
+  border-bottom-left-radius: 2px;
 }
 .pure-button-group .pure-button:last-child {
-    border-top-right-radius: 2px;
-    border-bottom-right-radius: 2px;
-    border-right: none;
+  border-top-right-radius: 2px;
+  border-bottom-right-radius: 2px;
+  border-right: none;
 }
 
 /*csslint box-model:false*/
@@ -785,15 +799,15 @@ so we can ignore the csslint warning.
 .pure-form input[type="color"],
 .pure-form select,
 .pure-form textarea {
-    padding: 0.5em 0.6em;
-    display: inline-block;
-    border: 1px solid #ccc;
-    -webkit-box-shadow: inset 0 1px 3px #ddd;
-            box-shadow: inset 0 1px 3px #ddd;
-    border-radius: 4px;
-    vertical-align: middle;
-    -webkit-box-sizing: border-box;
-            box-sizing: border-box;
+  padding: 0.5em 0.6em;
+  display: inline-block;
+  border: 1px solid #ccc;
+  -webkit-box-shadow: inset 0 1px 3px #ddd;
+  box-shadow: inset 0 1px 3px #ddd;
+  border-radius: 4px;
+  vertical-align: middle;
+  -webkit-box-sizing: border-box;
+  box-sizing: border-box;
 }
 
 /*
@@ -801,24 +815,22 @@ Need to separate out the :not() selector from the rest of 
the CSS 2.1 selectors
 since IE8 won't execute CSS that contains a CSS3 selector.
 */
 .pure-form input:not([type]) {
-    padding: 0.5em 0.6em;
-    display: inline-block;
-    border: 1px solid #ccc;
-    -webkit-box-shadow: inset 0 1px 3px #ddd;
-            box-shadow: inset 0 1px 3px #ddd;
-    border-radius: 4px;
-    -webkit-box-sizing: border-box;
-            box-sizing: border-box;
+  padding: 0.5em 0.6em;
+  display: inline-block;
+  border: 1px solid #ccc;
+  -webkit-box-shadow: inset 0 1px 3px #ddd;
+  box-shadow: inset 0 1px 3px #ddd;
+  border-radius: 4px;
+  -webkit-box-sizing: border-box;
+  box-sizing: border-box;
 }
 
-
 /* Chrome (as of v.32/34 on OS X) needs additional room for color to display. 
*/
 /* May be able to remove this tweak as color inputs become more standardized 
across browsers. */
 .pure-form input[type="color"] {
-    padding: 0.2em 0.5em;
+  padding: 0.2em 0.5em;
 }
 
-
 .pure-form input[type="text"]:focus,
 .pure-form input[type="password"]:focus,
 .pure-form input[type="email"]:focus,
@@ -835,8 +847,8 @@ since IE8 won't execute CSS that contains a CSS3 selector.
 .pure-form input[type="color"]:focus,
 .pure-form select:focus,
 .pure-form textarea:focus {
-    outline: 0;
-    border-color: #129FEA;
+  outline: 0;
+  border-color: #129fea;
 }
 
 /*
@@ -844,20 +856,20 @@ Need to separate out the :not() selector from the rest of 
the CSS 2.1 selectors
 since IE8 won't execute CSS that contains a CSS3 selector.
 */
 .pure-form input:not([type]):focus {
-    outline: 0;
-    border-color: #129FEA;
+  outline: 0;
+  border-color: #129fea;
 }
 
 .pure-form input[type="file"]:focus,
 .pure-form input[type="radio"]:focus,
 .pure-form input[type="checkbox"]:focus {
-    outline: thin solid #129FEA;
-    outline: 1px auto #129FEA;
+  outline: thin solid #129fea;
+  outline: 1px auto #129fea;
 }
 .pure-form .pure-checkbox,
 .pure-form .pure-radio {
-    margin: 0.5em 0;
-    display: block;
+  margin: 0.5em 0;
+  display: block;
 }
 
 .pure-form input[type="text"][disabled],
@@ -876,9 +888,9 @@ since IE8 won't execute CSS that contains a CSS3 selector.
 .pure-form input[type="color"][disabled],
 .pure-form select[disabled],
 .pure-form textarea[disabled] {
-    cursor: not-allowed;
-    background-color: #eaeded;
-    color: #cad2d3;
+  cursor: not-allowed;
+  background-color: #eaeded;
+  color: #cad2d3;
 }
 
 /*
@@ -886,53 +898,53 @@ Need to separate out the :not() selector from the rest of 
the CSS 2.1 selectors
 since IE8 won't execute CSS that contains a CSS3 selector.
 */
 .pure-form input:not([type])[disabled] {
-    cursor: not-allowed;
-    background-color: #eaeded;
-    color: #cad2d3;
+  cursor: not-allowed;
+  background-color: #eaeded;
+  color: #cad2d3;
 }
 .pure-form input[readonly],
 .pure-form select[readonly],
 .pure-form textarea[readonly] {
-    background-color: #eee; /* menu hover bg color */
-    color: #777; /* menu text color */
-    border-color: #ccc;
+  background-color: #eee; /* menu hover bg color */
+  color: #777; /* menu text color */
+  border-color: #ccc;
 }
 
 .pure-form input:focus:invalid,
 .pure-form textarea:focus:invalid,
 .pure-form select:focus:invalid {
-    color: #b94a48;
-    border-color: #e9322d;
+  color: #b94a48;
+  border-color: #e9322d;
 }
 .pure-form input[type="file"]:focus:invalid:focus,
 .pure-form input[type="radio"]:focus:invalid:focus,
 .pure-form input[type="checkbox"]:focus:invalid:focus {
-    outline-color: #e9322d;
+  outline-color: #e9322d;
 }
 .pure-form select {
-    /* Normalizes the height; padding is not sufficient. */
-    height: 2.25em;
-    border: 1px solid #ccc;
-    background-color: white;
+  /* Normalizes the height; padding is not sufficient. */
+  height: 2.25em;
+  border: 1px solid #ccc;
+  background-color: white;
 }
 .pure-form select[multiple] {
-    height: auto;
+  height: auto;
 }
 .pure-form label {
-    margin: 0.5em 0 0.2em;
+  margin: 0.5em 0 0.2em;
 }
 .pure-form fieldset {
-    margin: 0;
-    padding: 0.35em 0 0.75em;
-    border: 0;
+  margin: 0;
+  padding: 0.35em 0 0.75em;
+  border: 0;
 }
 .pure-form legend {
-    display: block;
-    width: 100%;
-    padding: 0.3em 0;
-    margin-bottom: 0.3em;
-    color: #333;
-    border-bottom: 1px solid #e5e5e5;
+  display: block;
+  width: 100%;
+  padding: 0.3em 0;
+  margin-bottom: 0.3em;
+  color: #333;
+  border-bottom: 1px solid #e5e5e5;
 }
 
 .pure-form-stacked input[type="text"],
@@ -953,8 +965,8 @@ since IE8 won't execute CSS that contains a CSS3 selector.
 .pure-form-stacked select,
 .pure-form-stacked label,
 .pure-form-stacked textarea {
-    display: block;
-    margin: 0.25em 0;
+  display: block;
+  margin: 0.25em 0;
 }
 
 /*
@@ -962,384 +974,384 @@ Need to separate out the :not() selector from the rest 
of the CSS 2.1 selectors
 since IE8 won't execute CSS that contains a CSS3 selector.
 */
 .pure-form-stacked input:not([type]) {
-    display: block;
-    margin: 0.25em 0;
+  display: block;
+  margin: 0.25em 0;
 }
 .pure-form-aligned input,
 .pure-form-aligned textarea,
 .pure-form-aligned select,
 .pure-form-message-inline {
-    display: inline-block;
-    vertical-align: middle;
+  display: inline-block;
+  vertical-align: middle;
 }
 .pure-form-aligned textarea {
-    vertical-align: top;
+  vertical-align: top;
 }
 
 /* Aligned Forms */
 .pure-form-aligned .pure-control-group {
-    margin-bottom: 0.5em;
+  margin-bottom: 0.5em;
 }
 .pure-form-aligned .pure-control-group label {
-    text-align: right;
-    display: inline-block;
-    vertical-align: middle;
-    width: 10em;
-    margin: 0 1em 0 0;
+  text-align: right;
+  display: inline-block;
+  vertical-align: middle;
+  width: 10em;
+  margin: 0 1em 0 0;
 }
 .pure-form-aligned .pure-controls {
-    margin: 1.5em 0 0 11em;
+  margin: 1.5em 0 0 11em;
 }
 
 /* Rounded Inputs */
 .pure-form input.pure-input-rounded,
 .pure-form .pure-input-rounded {
-    border-radius: 2em;
-    padding: 0.5em 1em;
+  border-radius: 2em;
+  padding: 0.5em 1em;
 }
 
 /* Grouped Inputs */
 .pure-form .pure-group fieldset {
-    margin-bottom: 10px;
+  margin-bottom: 10px;
 }
 .pure-form .pure-group input,
 .pure-form .pure-group textarea {
-    display: block;
-    padding: 10px;
-    margin: 0 0 -1px;
-    border-radius: 0;
-    position: relative;
-    top: -1px;
+  display: block;
+  padding: 10px;
+  margin: 0 0 -1px;
+  border-radius: 0;
+  position: relative;
+  top: -1px;
 }
 .pure-form .pure-group input:focus,
 .pure-form .pure-group textarea:focus {
-    z-index: 3;
+  z-index: 3;
 }
 .pure-form .pure-group input:first-child,
 .pure-form .pure-group textarea:first-child {
-    top: 1px;
-    border-radius: 4px 4px 0 0;
-    margin: 0;
+  top: 1px;
+  border-radius: 4px 4px 0 0;
+  margin: 0;
 }
 .pure-form .pure-group input:first-child:last-child,
 .pure-form .pure-group textarea:first-child:last-child {
-    top: 1px;
-    border-radius: 4px;
-    margin: 0;
+  top: 1px;
+  border-radius: 4px;
+  margin: 0;
 }
 .pure-form .pure-group input:last-child,
 .pure-form .pure-group textarea:last-child {
-    top: -2px;
-    border-radius: 0 0 4px 4px;
-    margin: 0;
+  top: -2px;
+  border-radius: 0 0 4px 4px;
+  margin: 0;
 }
 .pure-form .pure-group button {
-    margin: 0.35em 0;
+  margin: 0.35em 0;
 }
 
 .pure-form .pure-input-1 {
-    width: 100%;
+  width: 100%;
 }
 .pure-form .pure-input-3-4 {
-    width: 75%;
+  width: 75%;
 }
 .pure-form .pure-input-2-3 {
-    width: 66%;
+  width: 66%;
 }
 .pure-form .pure-input-1-2 {
-    width: 50%;
+  width: 50%;
 }
 .pure-form .pure-input-1-3 {
-    width: 33%;
+  width: 33%;
 }
 .pure-form .pure-input-1-4 {
-    width: 25%;
+  width: 25%;
 }
 
 /* Inline help for forms */
 .pure-form-message-inline {
-    display: inline-block;
-    padding-left: 0.3em;
-    color: #666;
-    vertical-align: middle;
-    font-size: 0.875em;
+  display: inline-block;
+  padding-left: 0.3em;
+  color: #666;
+  vertical-align: middle;
+  font-size: 0.875em;
 }
 
 /* Block help for forms */
 .pure-form-message {
+  display: block;
+  color: #666;
+  font-size: 0.875em;
+}
+
+@media only screen and (max-width: 480px) {
+  .pure-form button[type="submit"] {
+    margin: 0.7em 0 0;
+  }
+
+  .pure-form input:not([type]),
+  .pure-form input[type="text"],
+  .pure-form input[type="password"],
+  .pure-form input[type="email"],
+  .pure-form input[type="url"],
+  .pure-form input[type="date"],
+  .pure-form input[type="month"],
+  .pure-form input[type="time"],
+  .pure-form input[type="datetime"],
+  .pure-form input[type="datetime-local"],
+  .pure-form input[type="week"],
+  .pure-form input[type="number"],
+  .pure-form input[type="search"],
+  .pure-form input[type="tel"],
+  .pure-form input[type="color"],
+  .pure-form label {
+    margin-bottom: 0.3em;
     display: block;
-    color: #666;
-    font-size: 0.875em;
-}
-
-@media only screen and (max-width : 480px) {
-    .pure-form button[type="submit"] {
-        margin: 0.7em 0 0;
-    }
-
-    .pure-form input:not([type]),
-    .pure-form input[type="text"],
-    .pure-form input[type="password"],
-    .pure-form input[type="email"],
-    .pure-form input[type="url"],
-    .pure-form input[type="date"],
-    .pure-form input[type="month"],
-    .pure-form input[type="time"],
-    .pure-form input[type="datetime"],
-    .pure-form input[type="datetime-local"],
-    .pure-form input[type="week"],
-    .pure-form input[type="number"],
-    .pure-form input[type="search"],
-    .pure-form input[type="tel"],
-    .pure-form input[type="color"],
-    .pure-form label {
-        margin-bottom: 0.3em;
-        display: block;
-    }
-
-    .pure-group input:not([type]),
-    .pure-group input[type="text"],
-    .pure-group input[type="password"],
-    .pure-group input[type="email"],
-    .pure-group input[type="url"],
-    .pure-group input[type="date"],
-    .pure-group input[type="month"],
-    .pure-group input[type="time"],
-    .pure-group input[type="datetime"],
-    .pure-group input[type="datetime-local"],
-    .pure-group input[type="week"],
-    .pure-group input[type="number"],
-    .pure-group input[type="search"],
-    .pure-group input[type="tel"],
-    .pure-group input[type="color"] {
-        margin-bottom: 0;
-    }
-
-    .pure-form-aligned .pure-control-group label {
-        margin-bottom: 0.3em;
-        text-align: left;
-        display: block;
-        width: 100%;
-    }
-
-    .pure-form-aligned .pure-controls {
-        margin: 1.5em 0 0 0;
-    }
-
-    .pure-form-message-inline,
-    .pure-form-message {
-        display: block;
-        font-size: 0.75em;
-        /* Increased bottom padding to make it group with its related input 
element. */
-        padding: 0.2em 0 0.8em;
-    }
+  }
+
+  .pure-group input:not([type]),
+  .pure-group input[type="text"],
+  .pure-group input[type="password"],
+  .pure-group input[type="email"],
+  .pure-group input[type="url"],
+  .pure-group input[type="date"],
+  .pure-group input[type="month"],
+  .pure-group input[type="time"],
+  .pure-group input[type="datetime"],
+  .pure-group input[type="datetime-local"],
+  .pure-group input[type="week"],
+  .pure-group input[type="number"],
+  .pure-group input[type="search"],
+  .pure-group input[type="tel"],
+  .pure-group input[type="color"] {
+    margin-bottom: 0;
+  }
+
+  .pure-form-aligned .pure-control-group label {
+    margin-bottom: 0.3em;
+    text-align: left;
+    display: block;
+    width: 100%;
+  }
+
+  .pure-form-aligned .pure-controls {
+    margin: 1.5em 0 0 0;
+  }
+
+  .pure-form-message-inline,
+  .pure-form-message {
+    display: block;
+    font-size: 0.75em;
+    /* Increased bottom padding to make it group with its related input 
element. */
+    padding: 0.2em 0 0.8em;
+  }
 }
 
 /*csslint adjoining-classes: false, box-model:false*/
 .pure-menu {
-    -webkit-box-sizing: border-box;
-            box-sizing: border-box;
+  -webkit-box-sizing: border-box;
+  box-sizing: border-box;
 }
 
 .pure-menu-fixed {
-    position: fixed;
-    left: 0;
-    top: 0;
-    z-index: 3;
+  position: fixed;
+  left: 0;
+  top: 0;
+  z-index: 3;
 }
 
 .pure-menu-list,
 .pure-menu-item {
-    position: relative;
+  position: relative;
 }
 
 .pure-menu-list {
-    list-style: none;
-    margin: 0;
-    padding: 0;
+  list-style: none;
+  margin: 0;
+  padding: 0;
 }
 
 .pure-menu-item {
-    padding: 0;
-    margin: 0;
-    height: 100%;
+  padding: 0;
+  margin: 0;
+  height: 100%;
 }
 
 .pure-menu-link,
 .pure-menu-heading {
-    display: block;
-    text-decoration: none;
-    white-space: nowrap;
+  display: block;
+  text-decoration: none;
+  white-space: nowrap;
 }
 
 /* HORIZONTAL MENU */
 .pure-menu-horizontal {
-    width: 100%;
-    white-space: nowrap;
+  width: 100%;
+  white-space: nowrap;
 }
 
 .pure-menu-horizontal .pure-menu-list {
-    display: inline-block;
+  display: inline-block;
 }
 
 /* Initial menus should be inline-block so that they are horizontal */
 .pure-menu-horizontal .pure-menu-item,
 .pure-menu-horizontal .pure-menu-heading,
 .pure-menu-horizontal .pure-menu-separator {
-    display: inline-block;
-    vertical-align: middle;
+  display: inline-block;
+  vertical-align: middle;
 }
 
 /* Submenus should still be display: block; */
 .pure-menu-item .pure-menu-item {
-    display: block;
+  display: block;
 }
 
 .pure-menu-children {
-    display: none;
-    position: absolute;
-    left: 100%;
-    top: 0;
-    margin: 0;
-    padding: 0;
-    z-index: 3;
+  display: none;
+  position: absolute;
+  left: 100%;
+  top: 0;
+  margin: 0;
+  padding: 0;
+  z-index: 3;
 }
 
 .pure-menu-horizontal .pure-menu-children {
-    left: 0;
-    top: auto;
-    width: inherit;
+  left: 0;
+  top: auto;
+  width: inherit;
 }
 
 .pure-menu-allow-hover:hover > .pure-menu-children,
 .pure-menu-active > .pure-menu-children {
-    display: block;
-    position: absolute;
+  display: block;
+  position: absolute;
 }
 
 /* Vertical Menus - show the dropdown arrow */
 .pure-menu-has-children > .pure-menu-link:after {
-    padding-left: 0.5em;
-    content: "\25B8";
-    font-size: small;
+  padding-left: 0.5em;
+  content: "\25B8";
+  font-size: small;
 }
 
 /* Horizontal Menus - show the dropdown arrow */
 .pure-menu-horizontal .pure-menu-has-children > .pure-menu-link:after {
-    content: "\25BE";
+  content: "\25BE";
 }
 
 /* scrollable menus */
 .pure-menu-scrollable {
-    overflow-y: scroll;
-    overflow-x: hidden;
+  overflow-y: scroll;
+  overflow-x: hidden;
 }
 
 .pure-menu-scrollable .pure-menu-list {
-    display: block;
+  display: block;
 }
 
 .pure-menu-horizontal.pure-menu-scrollable .pure-menu-list {
-    display: inline-block;
+  display: inline-block;
 }
 
 .pure-menu-horizontal.pure-menu-scrollable {
-    white-space: nowrap;
-    overflow-y: hidden;
-    overflow-x: auto;
-    /* a little extra padding for this style to allow for scrollbars */
-    padding: .5em 0;
+  white-space: nowrap;
+  overflow-y: hidden;
+  overflow-x: auto;
+  /* a little extra padding for this style to allow for scrollbars */
+  padding: 0.5em 0;
 }
 
 /* misc default styling */
 
 .pure-menu-separator,
 .pure-menu-horizontal .pure-menu-children .pure-menu-separator {
-    background-color: #ccc;
-    height: 1px;
-    margin: .3em 0;
+  background-color: #ccc;
+  height: 1px;
+  margin: 0.3em 0;
 }
 
 .pure-menu-horizontal .pure-menu-separator {
-    width: 1px;
-    height: 1.3em;
-    margin: 0 .3em ;
+  width: 1px;
+  height: 1.3em;
+  margin: 0 0.3em;
 }
 
 /* Need to reset the separator since submenu is vertical */
 .pure-menu-horizontal .pure-menu-children .pure-menu-separator {
-    display: block;
-    width: auto;
+  display: block;
+  width: auto;
 }
 
 .pure-menu-heading {
-    text-transform: uppercase;
-    color: #565d64;
+  text-transform: uppercase;
+  color: #565d64;
 }
 
 .pure-menu-link {
-    color: #777;
+  color: #777;
 }
 
 .pure-menu-children {
-    background-color: #fff;
+  background-color: #fff;
 }
 
 .pure-menu-link,
 .pure-menu-heading {
-    padding: .5em 1em;
+  padding: 0.5em 1em;
 }
 
 .pure-menu-disabled {
-    opacity: .5;
+  opacity: 0.5;
 }
 
 .pure-menu-disabled .pure-menu-link:hover {
-    background-color: transparent;
-    cursor: default;
+  background-color: transparent;
+  cursor: default;
 }
 
 .pure-menu-active > .pure-menu-link,
 .pure-menu-link:hover,
 .pure-menu-link:focus {
-    background-color: #eee;
+  background-color: #eee;
 }
 
 .pure-menu-selected > .pure-menu-link,
 .pure-menu-selected > .pure-menu-link:visited {
-    color: #000;
+  color: #000;
 }
 
 .pure-table {
-    /* Remove spacing between table cells (from Normalize.css) */
-    border-collapse: collapse;
-    border-spacing: 0;
-    empty-cells: show;
-    border: 1px solid #cbcbcb;
+  /* Remove spacing between table cells (from Normalize.css) */
+  border-collapse: collapse;
+  border-spacing: 0;
+  empty-cells: show;
+  border: 1px solid #cbcbcb;
 }
 
 .pure-table caption {
-    color: #000;
-    font: italic 85%/1 arial, sans-serif;
-    padding: 1em 0;
-    text-align: center;
+  color: #000;
+  font: italic 85%/1 arial, sans-serif;
+  padding: 1em 0;
+  text-align: center;
 }
 
 .pure-table td,
 .pure-table th {
-    border-left: 1px solid #cbcbcb;/*  inner column border */
-    border-width: 0 0 0 1px;
-    font-size: inherit;
-    margin: 0;
-    overflow: visible; /*to make ths where the title is really long work*/
-    padding: 0.5em 1em; /* cell padding */
+  border-left: 1px solid #cbcbcb; /*  inner column border */
+  border-width: 0 0 0 1px;
+  font-size: inherit;
+  margin: 0;
+  overflow: visible; /*to make ths where the title is really long work*/
+  padding: 0.5em 1em; /* cell padding */
 }
 
 .pure-table thead {
-    background-color: #e0e0e0;
-    color: #000;
-    text-align: left;
-    vertical-align: bottom;
+  background-color: #e0e0e0;
+  color: #000;
+  text-align: left;
+  vertical-align: bottom;
 }
 
 /*
@@ -1348,33 +1360,32 @@ striping:
    odd  - #f2f2f2 (light gray)
 */
 .pure-table td {
-    background-color: transparent;
+  background-color: transparent;
 }
 .pure-table-odd td {
-    background-color: #f2f2f2;
+  background-color: #f2f2f2;
 }
 
 /* nth-child selector for modern browsers */
 .pure-table-striped tr:nth-child(2n-1) td {
-    background-color: #f2f2f2;
+  background-color: #f2f2f2;
 }
 
 /* BORDERED TABLES */
 .pure-table-bordered td {
-    border-bottom: 1px solid #cbcbcb;
+  border-bottom: 1px solid #cbcbcb;
 }
 .pure-table-bordered tbody > tr:last-child > td {
-    border-bottom-width: 0;
+  border-bottom-width: 0;
 }
 
-
 /* HORIZONTAL BORDERED TABLES */
 
 .pure-table-horizontal td,
 .pure-table-horizontal th {
-    border-width: 0 0 1px 0;
-    border-bottom: 1px solid #cbcbcb;
+  border-width: 0 0 1px 0;
+  border-bottom: 1px solid #cbcbcb;
 }
 .pure-table-horizontal tbody > tr:last-child > td {
-    border-bottom-width: 0;
-}
\ No newline at end of file
+  border-bottom-width: 0;
+}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index d8bd34846..9be93a45d 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -96,6 +96,7 @@ importers:
     specifiers:
       '@creativebulma/bulma-tooltip': ^1.2.0
       '@gnu-taler/pogen': ^0.0.5
+      '@gnu-taler/taler-util': workspace:*
       '@typescript-eslint/eslint-plugin': ^5.41.0
       '@typescript-eslint/parser': ^5.41.0
       bulma: ^0.9.4
@@ -116,6 +117,7 @@ importers:
       swr: 1.3.0
       typescript: ^4.4.4
     dependencies:
+      '@gnu-taler/taler-util': link:../taler-util
       date-fns: 2.29.3
       jed: 1.1.1
       preact: 10.6.5

-- 
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]