merchant-backoffice

ZZZ: Inactive/Deprecated
Log | Files | Refs | Submodules | README

commit 2a54606abd6b00fb33381e07d8a31b20c2ff25fb
parent 9298e3ea3e46d6bf206a4b8612f42c6006ae1a04
Author: Sebastian <sebasjm@gmail.com>
Date:   Tue, 23 Feb 2021 15:53:41 -0300

some fixes

Diffstat:
MCHANGELOG.md | 10++++++----
MREADME.md | 2++
Mbuild-system/Makefile | 13++-----------
Mpackage.json | 2+-
Apackages/frontend/copyleft-header.js | 15+++++++++++++++
Mpackages/frontend/package.json | 19+++++++++++++++----
Apackages/frontend/src/assets/icons/languageicon.svg | 48++++++++++++++++++++++++++++++++++++++++++++++++
Mpackages/frontend/src/components/auth/index.tsx | 2+-
Mpackages/frontend/src/components/modal/index.tsx | 9+++++----
Mpackages/frontend/src/components/navbar/index.tsx | 6++++--
Mpackages/frontend/src/components/sidebar/index.tsx | 33+++++++++++++++++++++++++--------
Mpackages/frontend/src/components/yup/YupField.tsx | 28++++++++++++++++------------
Mpackages/frontend/src/context/backend.ts | 17+++++++++++++++++
Mpackages/frontend/src/custom.d.ts | 19+++++++++++++++++++
Mpackages/frontend/src/declaration.d.ts | 1+
Mpackages/frontend/src/hooks/index.ts | 26+++++++++++++++-----------
Mpackages/frontend/src/index.tsx | 15+++++++++++----
Mpackages/frontend/src/messages/en.po | 44+++++++++++++++++++++++++++++++++++++++++---
Mpackages/frontend/src/messages/index.ts | 15+++++++++++++++
Mpackages/frontend/src/routes/instances/create/index.tsx | 15+++++++++++++++
Mpackages/frontend/src/routes/instances/details/DetailPage.tsx | 17++++++++++++-----
Mpackages/frontend/src/routes/instances/details/index.tsx | 31++++++++++++++++++++++++-------
Mpackages/frontend/src/routes/instances/list/EmptyTable.tsx | 3+--
Mpackages/frontend/src/routes/instances/list/Table.tsx | 10+++++-----
Mpackages/frontend/src/routes/instances/update/index.tsx | 15+++++++++++++++
Mpackages/frontend/src/routes/login/index.tsx | 20++++++++++++++++++++
Mpackages/frontend/src/schemas/index.ts | 3+--
Mpackages/frontend/src/scss/_aside.scss | 6+++---
Mpackages/frontend/src/scss/_theme-default.scss | 2++
Mpnpm-lock.yaml | 10++++++++++
30 files changed, 367 insertions(+), 89 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md @@ -5,13 +5,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Future work] - - show the connection state (url, currency, version) in the sidebar - date format (error handling) - bug: set text int the intpu date (seconds) - connection errors - - add backend url without slash - - edit button should be a pen - - check the url from the backend when login - allow point separator for amounts - prevent letters to be input in numbers - red color when input is invalid (onchange) @@ -33,11 +29,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - prune scss styles to reduce size ## [Unreleased] + - check the url from the backend when login + - edit button should be a pen - implement correct weblate feature - what happend if cannot access the config - reorder the fields from the address/juriction section (take example) - save every auth token of different instances - remove footer + - show the connection state (url, currency, version) in the sidebar + - add backend url without slash + - added linter rule for source header + ## [0.0.1] - 2021-02-18 ### Changed - button of the form to the right diff --git a/README.md b/README.md @@ -68,3 +68,5 @@ This should start a watch process that will reload the server every time that a * Date-fns: library for manipulating javascript date +* messageformat: ICU MessageFormat for Javascript, with support for .po and .mo files + diff --git a/build-system/Makefile b/build-system/Makefile @@ -2,12 +2,7 @@ src = src -tsc = node_modules/typescript/bin/tsc -typedoc = node_modules/typedoc/bin/typedoc -ava = node_modules/.bin/ava -nyc = node_modules/nyc/bin/nyc.js -git-archive-all = ./build-system/taler-build-scripts/archive-with-submodules/git_archive_all.py - +PORT = 8080 include ./build-system/config.mk .PHONY: compile @@ -15,15 +10,11 @@ compile: pnpm i -r pnpm run compile -# .PHONY: dist -# dist: -# $(git-archive-all) --include ./configure taler-wallet-$(shell git describe --tags).tar.gz - # .PHONY: publish # publish: compile # pnpm publish -r --no-git-checks -# make documentation from docstrings + .PHONY: typedoc typedoc: pnpm run typedoc diff --git a/package.json b/package.json @@ -5,7 +5,7 @@ "compile": "pnpm run --filter '{packages}' compile", "build": "pnpm run --filter '{packages}' build", "serve": "pnpm run --filter '{packages}' serve", - "lint": "pnpm run --filter '{packages}' lint", + "lint": "pnpm run --filter '{packages}' lint-check", "typedoc": "pnpm run --filter '{packages}' typedoc", "pretty": "pnpm run --filter '{packages}' pretty", "check": "pnpm run --filter '{packages}' --if-present test" diff --git a/packages/frontend/copyleft-header.js b/packages/frontend/copyleft-header.js @@ -0,0 +1,15 @@ +/* + This file is part of GNU Taler + (C) 2021 Taler Systems S.A. + + GNU Taler is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + */ diff --git a/packages/frontend/package.json b/packages/frontend/package.json @@ -6,9 +6,10 @@ "scripts": { "build": "preact build --no-sw --no-esm", "compile": "tsc", - "serve": "sirv build --port 8080 --cors --single --no-sw --no-esm", - "dev": "preact watch --no-sw --no-esm", - "lint": "eslint 'src/**/*.{js,jsx,ts,tsx}' --fix", + "serve": "sirv build --port ${PORT:=8080} --cors --single --no-sw --no-esm", + "dev": "preact watch --port ${PORT:=8080} --no-sw --no-esm", + "lint-check": "eslint 'src/**/*.{js,jsx,ts,tsx}'", + "lint-fix": "eslint --fix 'src/**/*.{js,jsx,ts,tsx}'", "test": "jest ./tests", "typedoc": "typedoc src", "clean": "rimraf build storybook-static docs", @@ -24,6 +25,15 @@ "preact", "plugin:@typescript-eslint/recommended" ], + "plugins": [ + "header" + ], + "rules": { + "header/header": [ + 2, + "copyleft-header.js" + ] + }, "ignorePatterns": [ "build/" ] @@ -33,8 +43,8 @@ "date-fns": "^2.17.0", "messageformat": "^2.3.0", "preact": "^10.3.1", - "preact-router": "^3.2.1", "preact-messages": "workspace:*", + "preact-router": "^3.2.1", "swr": "^0.4.1", "yup": "^0.32.8" }, @@ -67,6 +77,7 @@ "enzyme-adapter-preact-pure": "^3.0.0", "eslint": "^7.20.0", "eslint-config-preact": "^1.1.1", + "eslint-plugin-header": "^3.1.1", "jest": "^26.2.2", "jest-preset-preact": "^4.0.2", "messageformat-po-loader": "^0.3.0", diff --git a/packages/frontend/src/assets/icons/languageicon.svg b/packages/frontend/src/assets/icons/languageicon.svg @@ -0,0 +1,48 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Generator: Adobe Illustrator 19.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) --> +<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" + viewBox="0 0 2411.2 2794" style="enable-background:new 0 0 2411.2 2794;" xml:space="preserve"> +<style type="text/css"> + .st0{fill:#FFFFFF;} + .st1{fill-rule:evenodd;clip-rule:evenodd;} + .st2{fill-rule:evenodd;clip-rule:evenodd;fill:#FFFFFF;} +</style> +<g id="Layer_2"> +</g> +<g id="Layer_x5F_1_x5F_1"> + <g> + <polygon points="1204.6,359.2 271.8,30 271.8,2060.1 1204.6,1758.3 "/> + <polygon class="st0" points="1182.2,358.1 2150.6,29 2150.6,2059 1182.2,1757.3 "/> + <polygon class="st0" points="30,2415.4 1182.2,2031.4 1182.2,357.9 30,742 "/> + <polygon points="1707.2,2440.7 1870.5,2709.4 1956.6,2459.8 "/> + <g> + <path d="M421.7,934.8c-6.1-6,8,49.1,27.6,68.9c34.8,35.1,61.9,39.6,76.4,40.2c32,1.3,71.5-8,94.9-17.8 + c22.7-9.7,62.4-30,77.5-59.6c3.2-6.3,11.9-17,6.4-43.2c-4.2-20.2-17-27.3-32.7-26.2c-15.7,1.1-63.2,13.7-86.1,20.8 + c-23,7-70.3,21.4-90.9,25.8C474.3,948.2,429,941.7,421.7,934.8z"/> + <path d="M1003.1,1593.7c-9.1-3.3-196.9-81.1-223.6-93.9c-21.8-10.5-75.2-33.1-100.4-43.3c70.8-109.2,115.5-191.6,121.5-204.1 + c11-23,86-169.6,87.7-178.7c1.7-9.1,3.8-42.9,2.2-51c-1.7-8.2-29.1,7.6-66.4,20.2c-37.4,12.6-108.4,58.8-135.8,64.6 + c-27.5,5.7-115.5,39.1-160.5,54c-45,14.9-130.2,40.9-165.2,50.4c-35.1,9.5-65.7,10.2-85.3,16.2c0,0,2.6,27.5,7.8,35.7 + c5.2,8.2,23.7,28.4,45.3,34.1c21.6,5.7,57.3,3.4,73.6-0.3c16.3-3.8,44.4-17.5,48.2-23.6c3.8-6.1-2-24.9,4.5-30.6 + c6.5-5.6,92.2-25.7,124.6-35.4c32.4-10,156.3-52.6,173.1-50.5c-5.3,17.7-105,215.1-137.1,274c-32.1,58.9-218.6,318-258.3,363.6 + c-30.1,34.7-103.2,123.5-128.5,143.6c6.4,1.8,51.6-2.1,59.9-7.2c51.3-31.6,136.9-138.1,164.4-170.5 + c81.9-96,153.8-196.8,210.8-283.4h0.1c11.1,4.6,100.9,77.8,124.4,94c23.4,16.2,115.9,67.8,136,76.4c20,8.7,97.1,44.2,100.3,32.2 + C1029.4,1668,1012.2,1597.1,1003.1,1593.7z"/> + </g> + <path class="st1" d="M569,2572c18,11,35,20,54,29c38,19,81,39,122,54c56,21,112,38,168,51c31,7,65,13,98,18c3,0,92,11,110,11h90 + c35-3,68-5,103-10c28-4,59-9,89-16c22-5,45-10,67-17c21-6,45-14,68-22c15-5,31-12,47-18c13-6,29-13,44-19c18-8,39-19,59-29 + c16-8,34-18,51-28c13-7,43-30,59-30c18,0,30,16,30,30c0,29-39,38-57,51c-19,13-42,23-62,34c-40,21-81,39-120,54 + c-51,19-107,37-157,49c-19,4-38,9-57,12c-10,2-114,18-143,18h-132c-35-3-72-7-107-12c-31-5-64-11-95-18c-24-5-50-12-73-19 + c-40-11-79-25-117-40c-69-26-141-60-209-105c-12-8-13-16-13-25c0-15,11-29,29-29C531,2546,563,2569,569,2572z"/> + <path class="st1" d="M1151,2009L61,2372V764l1090-363V2009z M1212,354v1680c-1,5-3,10-7,15c-2,3-6,7-9,8c-25,10-1151,388-1166,388 + c-12,0-23-8-29-21c0-1-1-2-1-4V739c2-5,3-12,7-16c8-11,22-13,31-16c17-6,1126-378,1142-378C1190,329,1212,336,1212,354z"/> + <path class="st1" d="M2120,2017l-907-282V380l907-308V2017z M2181,32v2023c-1,23-17,33-32,33c-13,0-107-32-123-37 + c-126-39-253-78-378-117c-28-9-57-18-84-27c-24-7-50-15-74-23c-107-33-216-66-323-102c-4-1-14-15-14-18V351c2-5,4-11,9-15 + c8-9,351-123,486-168c36-13,487-168,501-168C2167,0,2181,13,2181,32z"/> + <polygon points="2411.2,2440.7 1199.5,2054.5 1204.6,373.2 2411.2,757.2 "/> + <g> + <path class="st2" d="M1800.3,1124.6L1681.4,1412l218.6,66.3L1800.3,1124.6z M1729,853.2l156.1,47.3l284.4,1025l-160.3-48.7 + l-57.6-210.4L1620.2,1566l-71.3,171.4l-160.4-48.7L1729,853.2z"/> + </g> + </g> +</g> +</svg> diff --git a/packages/frontend/src/components/auth/index.tsx b/packages/frontend/src/components/auth/index.tsx @@ -44,7 +44,7 @@ export function LoginModal({ onConfirm, withMessage }: Props): VNode { <div class="columns is-vcentered"> <div class="column is-12"> <div> - <p>{withMessage.message}</p> + <p><strong>{withMessage.message}</strong></p> {withMessage.description} </div> </div> diff --git a/packages/frontend/src/components/modal/index.tsx b/packages/frontend/src/components/modal/index.tsx @@ -14,10 +14,11 @@ GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> */ - /** - * - * @author Sebastian Javier Marchano (sebasjm) - */ +/** +* +* @author Sebastian Javier Marchano (sebasjm) +*/ + import { h, VNode } from "preact"; import { Message } from "preact-messages"; diff --git a/packages/frontend/src/components/navbar/index.tsx b/packages/frontend/src/components/navbar/index.tsx @@ -22,6 +22,8 @@ import { h, VNode } from 'preact'; import * as messages from '../../messages' import logo from '../../assets/logo.jpeg'; +import langIcon from '../../assets/icons/languageicon.svg' +import { Message } from 'preact-messages'; interface Props { lang: string; @@ -50,11 +52,11 @@ export function NavigationBar({ lang, setLang, onLogout }: Props): VNode { <div class="control has-icons-left"> <div class="select"> <select onChange={(e): void => setLang(e.currentTarget.value)}> - {Object.keys(messages).map(l => <option selected={lang === l} value={l}>{l}</option>)} + {Object.keys(messages).map(l => <option selected={lang === l} value={l}><Message id={l} /></option>)} </select> </div> <div class="icon is-small is-left"> - <i class="mdi mdi-web" /> + <img src={langIcon} /> </div> </div> </div> diff --git a/packages/frontend/src/components/sidebar/index.tsx b/packages/frontend/src/components/sidebar/index.tsx @@ -14,14 +14,19 @@ GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> */ - /** - * - * @author Sebastian Javier Marchano (sebasjm) - */ +/** +* +* @author Sebastian Javier Marchano (sebasjm) +*/ + import { h, VNode } from 'preact'; +import { useContext } from 'preact/hooks'; +import { BackendContext, ConfigContext } from '../../context/backend'; export function Sidebar(): VNode { + const config = useContext(ConfigContext); + const backend = useContext(BackendContext); return ( <aside class="aside is-placed-left is-expanded"> <div class="aside-tools"> @@ -66,12 +71,24 @@ export function Sidebar(): VNode { </a> </li> </ul> - <p class="menu-label">About</p> + <p class="menu-label">Connection</p> <ul class="menu-list"> <li> - <a href="https://taler.net/" class="has-icon"> - <span class="icon"><i class="mdi mdi-help-circle" /></span> - <span class="menu-item-label">About</span> + <a> + <span class="icon"><i class="mdi mdi-currency-eur" /></span> + <span class="menu-item-label">{config.currency}</span> + </a> + </li> + <li> + <a> + <span class="icon"><i class="mdi mdi-web" /></span> + <span class="menu-item-label">{backend.url}</span> + </a> + </li> + <li> + <a> + <span class="icon">V</span> + <span class="menu-item-label">{config.version}</span> </a> </li> </ul> diff --git a/packages/frontend/src/components/yup/YupField.tsx b/packages/frontend/src/components/yup/YupField.tsx @@ -47,6 +47,7 @@ interface PropsObject { value: any; errors: any; onChange: any; + readonly?: boolean; } interface Props { @@ -56,12 +57,13 @@ interface Props { object: any; valueHandler: StateUpdater<any>; info: any; + readonly?: boolean; } -export function YupField({ name, field, errors, object, valueHandler, info }: Props): VNode { +export function YupField({ name, field, errors, object, valueHandler, info, readonly }: Props): VNode { const updateField = (f: string) => (v: string): void => valueHandler((prev: any) => ({ ...prev, [f]: v })) const values = { name, errors, - readonly: info?.meta?.readonly, + readonly: readonly || info?.meta?.readonly, value: object && object[field], onChange: updateField(field) } @@ -73,6 +75,7 @@ export function YupField({ name, field, errors, object, valueHandler, info }: Pr return <YupObjectInput name={name} info={info} errors={errors} value={object && object[field]} + readonly={values.readonly} onChange={(updater: any): void => valueHandler((prev: any) => ({ ...prev, [field]: updater(prev[field]) }))} /> } @@ -91,7 +94,7 @@ export function YupField({ name, field, errors, object, valueHandler, info }: Pr } } -function YupObjectInput({ name, info, value, errors, onChange }: PropsObject): VNode { +function YupObjectInput({ name, info, value, errors, onChange, readonly }: PropsObject): VNode { const [active, setActive] = useState(false) return <div class="card"> <header class="card-header"> @@ -111,6 +114,7 @@ function YupObjectInput({ name, info, value, errors, onChange }: PropsObject): V {Object.keys(info.fields).map(f => <YupField name={`${name}.${f}`} field={f} errors={errors} object={value} valueHandler={onChange} info={info.fields[f]} + readonly={readonly} />)} </div> </div> @@ -135,7 +139,7 @@ function YupInput({ name, readonly, value, errors, onChange }: PropsInputInterna <p class="control"> <input class={errors[name] ? "input is-danger" : "input"} type="text" placeholder={placeholder} readonly={readonly} - name={name} value={value} + name={name} value={value} disabled={readonly} onChange={(e): void => onChange(e.currentTarget.value)} /> <Message id={`fields.instance.${name}.help`} > </Message> </p> @@ -167,18 +171,18 @@ function YupInputArray({ name, readonly, value, errors, onChange }: PropsInputIn <div class="field"> <div class="field has-addons"> <p class="control"> - <button class="button is-info" onClick={(): void => { - onChange([currentValue, ...array]) - setCurrentValue('') - }} >add</button> - </p> - <p class="control"> <input class={errors[name] ? "input is-danger" : "input"} type="text" - placeholder={placeholder} readonly={readonly} + placeholder={placeholder} readonly={readonly} disabled={readonly} name={name} value={currentValue} onChange={(e): void => setCurrentValue(e.currentTarget.value)} /> <Message id={`fields.instance.${name}.help`} > </Message> </p> + <p class="control"> + <button class="button is-info" onClick={(): void => { + onChange([currentValue, ...array]) + setCurrentValue('') + }} >add</button> + </p> </div> {errors[name] ? <p class="help is-danger"> <Message id={`validation.${errors[name].type}`} fields={errors[name].params}>{errors[name].message}</Message> @@ -218,7 +222,7 @@ function YupInputWithAddon({ name, readonly, value, errors, onChange, addon, atT </div>} <p class="control is-expanded"> <input class={errors[name] ? "input is-danger" : "input"} type="text" - placeholder={placeholder} readonly={readonly} + placeholder={placeholder} readonly={readonly} disabled={readonly} name={name} value={value} onChange={(e): void => onChange(e.currentTarget.value)} /> <Message id={`fields.instance.${name}.help`} > </Message> diff --git a/packages/frontend/src/context/backend.ts b/packages/frontend/src/context/backend.ts @@ -1,3 +1,18 @@ +/* + This file is part of GNU Taler + (C) 2021 Taler Systems S.A. + + GNU Taler is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + */ import { createContext } from 'preact' import { StateUpdater } from 'preact/hooks' @@ -14,6 +29,7 @@ export interface BackendContextType { export interface ConfigContextType { currency?: string; + version?: string; } export interface InstanceContextType { @@ -34,6 +50,7 @@ export const BackendContext = createContext<BackendContextType>({ export const ConfigContext = createContext<ConfigContextType>({ currency: undefined, + version: undefined, }) export const InstanceContext = createContext<InstanceContextType>({ diff --git a/packages/frontend/src/custom.d.ts b/packages/frontend/src/custom.d.ts @@ -1,3 +1,18 @@ +/* + This file is part of GNU Taler + (C) 2021 Taler Systems S.A. + + GNU Taler is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + */ declare module '*.po' { const content: any; export default content; @@ -6,3 +21,7 @@ declare module "*.jpeg" { const content: any; export default content; } +declare module '*.svg' { + const content: any; + export default content; +} diff --git a/packages/frontend/src/declaration.d.ts b/packages/frontend/src/declaration.d.ts @@ -31,6 +31,7 @@ interface Notification { params?: any; } +type ValueOrFunction<T> = T | ((p: T) => T) type MessageType = 'INFO' | 'WARN' | 'ERROR' | 'SUCCESS' type EddsaPublicKey = string; diff --git a/packages/frontend/src/hooks/index.ts b/packages/frontend/src/hooks/index.ts @@ -20,10 +20,12 @@ */ import { StateUpdater, useEffect, useState } from "preact/hooks"; -import { mutate } from 'swr'; +import { ValueOrFunction } from '../declaration'; export function useBackendURL(): [string, StateUpdater<string>] { - return useNotNullLocalStorage('backend-url', typeof window !== 'undefined' ? window.location.origin : '') + const [value, setter] = useNotNullLocalStorage('backend-url', typeof window !== 'undefined' ? window.location.origin : '') + const checkedSetter = (v: ValueOrFunction<string>) => setter(p => (v instanceof Function ? v(p) : v).replace(/\/$/, '')) + return [value, checkedSetter] } export function useBackendDefaultToken(): [string | undefined, StateUpdater<string | undefined>] { return useLocalStorage('backend-token') @@ -46,7 +48,7 @@ export function useBackendInstanceToken(id: string): [string | undefined, StateU return all.concat(id).filter(Boolean).join(',') }) }, [id, setIds]) - + return [token, setToken, clearAllTokens] } @@ -75,15 +77,17 @@ export function useLocalStorage(key: string, initialValue?: string): [string | u }); const setValue = (value?: string | ((val?: string) => string | undefined)) => { - const valueToStore = value instanceof Function ? value(storedValue) : value; - setStoredValue(valueToStore); - if (typeof window !== "undefined") { - if (!valueToStore) { - window.localStorage.removeItem(key) - } else { - window.localStorage.setItem(key, valueToStore); + setStoredValue(p => { + const toStore = value instanceof Function ? value(p) : value + if (typeof window !== "undefined") { + if (!toStore) { + window.localStorage.removeItem(key) + } else { + window.localStorage.setItem(key, toStore); + } } - } + return toStore + }) }; return [storedValue, setValue]; diff --git a/packages/frontend/src/index.tsx b/packages/frontend/src/index.tsx @@ -122,11 +122,11 @@ function AppReady({ pushNotification,addTokenCleaner }: { pushNotification: (n: }} onUpdate={(id: string): void => { - route(`/instance/${id}/update`) + route(`/instance/${id}/`) }} onUnauthorized={() => <Login - withMessage={{ message: i18n`unauthorized`, type: 'ERROR', }} + withMessage={{ message: i18n`Access denied`, description: i18n`Check your token is valid`, type: 'ERROR', }} onConfirm={updateLoginStatus} />} @@ -211,9 +211,10 @@ function SubPages({ id, pushNotification, addTokenCleaner }: SubPagesProps): VNo <Router> <Route path={InstancePages.details} component={Details} + pushNotification={pushNotification} onUnauthorized={() => <Login - withMessage={{ message: i18n`unauthorized`, type: 'ERROR', }} + withMessage={{ message: i18n`Access denied`, description: i18n`Check your token is valid`, type: 'ERROR', }} onConfirm={updateLoginStatus} />} @@ -221,6 +222,10 @@ function SubPages({ id, pushNotification, addTokenCleaner }: SubPagesProps): VNo route(`/instance/${id}/update`) }} + onDelete={() => { + route(`/instances`) + }} + onLoadError={(e: SwrError) => { pushNotification({ message: i18n`update_load_error`, type: 'ERROR', params: e }) route(`/instance/${id}/`) @@ -230,8 +235,10 @@ function SubPages({ id, pushNotification, addTokenCleaner }: SubPagesProps): VNo <Route path={InstancePages.update} component={Update} + // pushNotification={pushNotification} + onUnauthorized={() => <Login - withMessage={{ message: i18n`unauthorized`, type: 'ERROR', }} + withMessage={{ message: i18n`Access denied`, description: i18n`Check your token is valid`, type: 'ERROR', }} onConfirm={updateLoginStatus} />} onLoadError={(e: SwrError) => { diff --git a/packages/frontend/src/messages/en.po b/packages/frontend/src/messages/en.po @@ -137,7 +137,7 @@ msgid "Couldnt access the server" msgstr "Couldnt access the server" msgid "Got message: %s from: %s (hasToken: %s)" -msgstr "Recibimos el mensaje: %s desde: %s (con token: %s)" +msgstr "Got message \"%s\" from %s" msgid "Merchant" msgstr "Merchant" @@ -152,4 +152,43 @@ msgid "Cancel" msgstr "Cancel" msgid "Confirm" -msgstr "Confirm" -\ No newline at end of file +msgstr "Confirm" + +msgid "en" +msgstr "English [en]" + +msgid "es" +msgstr "EspaƱol [es]" + +msgid "fields.instance.id.label" +msgstr "Id" + +msgid "fields.instance.name.label" +msgstr "Business Name" + +msgid "fields.instance.merchant_pub.label" +msgstr "Public Key" + +msgid "fields.instance.payment_targets.label" +msgstr "Payment targets" + +msgid "Access denied" +msgstr "Access denied" + +msgid "Check your token is valid" +msgstr "Check your token is valid" + +msgid "delete" +msgstr "Delete" + +msgid "update" +msgstr "Update" + +msgid "Instance details" +msgstr "Instance details" + + + + + + diff --git a/packages/frontend/src/messages/index.ts b/packages/frontend/src/messages/index.ts @@ -1,3 +1,18 @@ +/* + This file is part of GNU Taler + (C) 2021 Taler Systems S.A. + + GNU Taler is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + */ export * as en from './en.po' export * as es from './es.po' diff --git a/packages/frontend/src/routes/instances/create/index.tsx b/packages/frontend/src/routes/instances/create/index.tsx @@ -1,3 +1,18 @@ +/* + This file is part of GNU Taler + (C) 2021 Taler Systems S.A. + + GNU Taler is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + */ import { h, VNode } from "preact"; import { MerchantBackend } from "../../../declaration"; import { useBackendMutateAPI } from "../../../hooks/backend"; diff --git a/packages/frontend/src/routes/instances/details/DetailPage.tsx b/packages/frontend/src/routes/instances/details/DetailPage.tsx @@ -23,7 +23,7 @@ import { h, VNode } from "preact"; import { useState } from "preact/hooks"; import { MerchantBackend } from "../../../declaration"; import { YupField } from "../../../components/yup/YupField" -import { InstanceUpdateSchema as schema } from '../../../schemas' +import { InstanceSchema as schema } from '../../../schemas' import { Message } from "preact-messages"; interface Props { @@ -87,15 +87,22 @@ export function DetailPage({ onUpdate, isLoading, selected, onDelete }: Props): <section class="section is-main-section"> <div class="columns"> <div class="column" /> - <div class="column is-two-thirds"> - {Object.keys(schema.fields) + <div class="column is-6"> + {Object.keys(schema.pick(['name','address']).fields) .map(f => <YupField name={f} field={f} errors={errors} object={value} valueHandler={valueHandler} info={schema.fields[f].describe()} + readonly />)} <div class="buttons is-right"> - <button class="button is-danger" onClick={() => onDelete()} ><Message id="delete" /></button> - <button class="button is-success" onClick={() => onUpdate()} ><Message id="update" /></button> + <button class="button is-danger" onClick={() => onDelete()} > + <span class="icon"><i class="mdi mdi-delete" /></span> + <span><Message id="delete" /></span> + </button> + <button class="button is-success" onClick={() => onUpdate()} > + <span class="icon"><i class="mdi mdi-pen" /></span> + <span><Message id="update" /></span> + </button> </div> </div> <div class="column" /> diff --git a/packages/frontend/src/routes/instances/details/index.tsx b/packages/frontend/src/routes/instances/details/index.tsx @@ -1,3 +1,18 @@ +/* + This file is part of GNU Taler + (C) 2021 Taler Systems S.A. + + GNU Taler is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + */ import { Fragment, h, VNode } from "preact"; import { useContext, useState } from "preact/hooks"; import { InstanceContext } from "../../../context/backend"; @@ -10,10 +25,11 @@ interface Props { onUnauthorized: () => VNode; onLoadError: (e: SwrError) => VNode; onUpdate: () => void; + onDelete: () => void; pushNotification: (n: Notification) => void; } -export default function Detail({ onUpdate, onLoadError, onUnauthorized, pushNotification }: Props): VNode { +export default function Detail({ onUpdate, onLoadError, onUnauthorized, pushNotification, onDelete }: Props): VNode { const { id } = useContext(InstanceContext) const details = useBackendInstance() const [deleting, setDeleting] = useState<boolean>(false) @@ -31,22 +47,23 @@ export default function Detail({ onUpdate, onLoadError, onUnauthorized, pushNoti return <Fragment> <DetailPage isLoading={false} - selected={details.data} + selected={details.data} onUpdate={onUpdate} - onDelete={() => setDeleting(true) } + onDelete={() => setDeleting(true)} /> - {deleting && <DeleteModal - element={{name: details.data.name, id }} - onCancel={() => setDeleting(false) } + {deleting && <DeleteModal + element={{ name: details.data.name, id }} + onCancel={() => setDeleting(false)} onConfirm={async (): Promise<void> => { try { await deleteInstance() pushNotification({ message: 'delete_success', type: 'SUCCESS' }) + onDelete() } catch (error) { pushNotification({ message: 'delete_error', type: 'ERROR', params: error }) } setDeleting(false) - }} + }} />} </Fragment> diff --git a/packages/frontend/src/routes/instances/list/EmptyTable.tsx b/packages/frontend/src/routes/instances/list/EmptyTable.tsx @@ -20,8 +20,7 @@ */ import { h, VNode } from "preact"; -import { useMessageTemplate } from "preact-messages"; -import { Message } from "preact-messages"; +import { Message, useMessageTemplate } from "preact-messages"; export function EmptyTable(): VNode { return <div class="content has-text-grey has-text-centered"> diff --git a/packages/frontend/src/routes/instances/list/Table.tsx b/packages/frontend/src/routes/instances/list/Table.tsx @@ -47,10 +47,10 @@ export function Table({ rowSelection, rowSelectionHandler, instances, onUpdate, <span class="check" /> </label> </th> - <th><Message id="fields_instance_id_label" /></th> - <th><Message id="fields_instance_name_label" /></th> - <th><Message id="fields_instance_merchant_pub_label" /></th> - <th><Message id="fields_instance_payment_targets_label" /></th> + <th><Message id="fields.instance.id.label" /></th> + <th><Message id="fields.instance.name.label" /></th> + <th><Message id="fields.instance.merchant_pub.label" /></th> + <th><Message id="fields.instance.payment_targets.label" /></th> <th /> </tr> </thead> @@ -69,7 +69,7 @@ export function Table({ rowSelection, rowSelectionHandler, instances, onUpdate, <td >{i.payment_targets}</td> <td class="is-actions-cell"> <div class="buttons is-right"> - <button class="button is-small is-primary" type="button" onClick={(): void => onUpdate(i.id)}> + <button class="button is-small is-link" type="button" onClick={(): void => onUpdate(i.id)}> <span class="icon"><i class="mdi mdi-eye" /></span> </button> <button class="button is-small is-danger jb-modal" type="button" onClick={(): void => onDelete(i)}> diff --git a/packages/frontend/src/routes/instances/update/index.tsx b/packages/frontend/src/routes/instances/update/index.tsx @@ -1,3 +1,18 @@ +/* + This file is part of GNU Taler + (C) 2021 Taler Systems S.A. + + GNU Taler is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + */ import { h, VNode } from "preact"; import { MerchantBackend } from "../../../declaration"; import { SwrError, useBackendInstance, useBackendInstanceMutateAPI } from "../../../hooks/backend"; diff --git a/packages/frontend/src/routes/login/index.tsx b/packages/frontend/src/routes/login/index.tsx @@ -1,3 +1,23 @@ +/* + This file is part of GNU Taler + (C) 2021 Taler Systems S.A. + + GNU Taler is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + */ + + /** +* +* @author Sebastian Javier Marchano (sebasjm) +*/ import { h, VNode } from "preact"; import { LoginModal } from '../../components/auth'; import { Notification } from "../../declaration"; diff --git a/packages/frontend/src/schemas/index.ts b/packages/frontend/src/schemas/index.ts @@ -21,7 +21,6 @@ import * as yup from 'yup'; import { AMOUNT_REGEX, PAYTO_REGEX } from "../constants"; -import { Duration } from '../declaration'; yup.setLocale({ mixed: { @@ -46,7 +45,7 @@ function listOfPayToUrisAreValid(values?: (string | undefined)[]): boolean { function currencyWithAmountIsValid(value?: string): boolean { return !!value && AMOUNT_REGEX.test(value) } -const InstanceSchema = yup.object().shape({ +export const InstanceSchema = yup.object().shape({ id: yup.string().required().meta({type: 'url'}), name: yup.string().required(), auth_token: yup.string() diff --git a/packages/frontend/src/scss/_aside.scss b/packages/frontend/src/scss/_aside.scss @@ -131,7 +131,7 @@ aside.aside { } @include touch { - #app, nav.navbar { + nav.navbar { @include transition(margin-left); } aside.aside { @@ -141,7 +141,7 @@ aside.aside { body { overflow-x: hidden; } - body, #app, nav.navbar { + body, nav.navbar { width: 100vw; } aside.aside { @@ -172,7 +172,7 @@ aside.aside { } } html.has-aside-mobile-expanded { - #app, nav.navbar { + nav.navbar { margin-left: $aside-mobile-width; } aside.aside { diff --git a/packages/frontend/src/scss/_theme-default.scss b/packages/frontend/src/scss/_theme-default.scss @@ -122,3 +122,5 @@ $progress-bar-background-color: $grey-lighter; /* Icon: specifics */ $icon-update-mark-size: $size-base * .5; $icon-update-mark-color: $yellow; + +$input-disabled-border-color: $grey-lighter; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml @@ -40,6 +40,7 @@ importers: enzyme-adapter-preact-pure: 3.0.0_enzyme@3.11.0+preact@10.5.12 eslint: 7.20.0 eslint-config-preact: 1.1.3_eslint@7.20.0+typescript@4.1.5 + eslint-plugin-header: 3.1.1_eslint@7.20.0 jest: 26.6.3 jest-preset-preact: 4.0.2_120c6743da4bd73ebdbf5629f89f97bc messageformat-po-loader: 0.3.0_messageformat@2.3.0 @@ -82,6 +83,7 @@ importers: enzyme-adapter-preact-pure: ^3.0.0 eslint: ^7.20.0 eslint-config-preact: ^1.1.1 + eslint-plugin-header: ^3.1.1 jest: ^26.2.2 jest-preset-preact: ^4.0.2 messageformat: ^2.3.0 @@ -7131,6 +7133,14 @@ packages: eslint: ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 resolution: integrity: sha512-lt3l5PHFHVEYSZ5zijcoYvtQJPsBifRiH5N0Et57KwVu7l/yxmHhSG6VJiLMa/lXrg93Qu8049RNQOMn0+yJBg== + /eslint-plugin-header/3.1.1_eslint@7.20.0: + dependencies: + eslint: 7.20.0 + dev: true + peerDependencies: + eslint: '>=7.7.0' + resolution: + integrity: sha512-9vlKxuJ4qf793CmeeSrZUvVClw6amtpghq3CuWcB5cUNnWHQhgcqy5eF8oVKFk1G3Y/CbchGfEaw3wiIJaNmVg== /eslint-plugin-jest/23.20.0_eslint@7.20.0+typescript@4.1.5: dependencies: '@typescript-eslint/experimental-utils': 2.34.0_eslint@7.20.0+typescript@4.1.5