summaryrefslogtreecommitdiff
path: root/packages/demobank-ui
diff options
context:
space:
mode:
Diffstat (limited to 'packages/demobank-ui')
-rw-r--r--packages/demobank-ui/.gitignore4
-rw-r--r--packages/demobank-ui/Makefile37
-rw-r--r--packages/demobank-ui/README.md24
-rw-r--r--packages/demobank-ui/TODO49
-rwxr-xr-xpackages/demobank-ui/build.mjs28
-rwxr-xr-xpackages/demobank-ui/contrib/po2ts42
-rw-r--r--packages/demobank-ui/copyleft-header.js15
-rwxr-xr-xpackages/demobank-ui/dev.mjs41
-rw-r--r--packages/demobank-ui/package.json74
-rw-r--r--packages/demobank-ui/postcss.config.js6
-rw-r--r--packages/demobank-ui/src/Routing.tsx352
-rw-r--r--packages/demobank-ui/src/assets/empty.pngbin2785 -> 0 bytes
-rw-r--r--packages/demobank-ui/src/assets/example/id1.jpgbin103558 -> 0 bytes
-rw-r--r--packages/demobank-ui/src/assets/favicon.icobin15086 -> 0 bytes
-rw-r--r--packages/demobank-ui/src/assets/icons/android-chrome-192x192.pngbin14058 -> 0 bytes
-rw-r--r--packages/demobank-ui/src/assets/icons/android-chrome-512x512.pngbin51484 -> 0 bytes
-rw-r--r--packages/demobank-ui/src/assets/icons/apple-touch-icon.pngbin12746 -> 0 bytes
-rw-r--r--packages/demobank-ui/src/assets/icons/favicon-16x16.pngbin626 -> 0 bytes
-rw-r--r--packages/demobank-ui/src/assets/icons/favicon-32x32.pngbin1487 -> 0 bytes
-rw-r--r--packages/demobank-ui/src/assets/icons/languageicon.svg48
-rw-r--r--packages/demobank-ui/src/assets/icons/mstile-150x150.pngbin9050 -> 0 bytes
-rw-r--r--packages/demobank-ui/src/assets/logo-2021.svg9
-rw-r--r--packages/demobank-ui/src/assets/logo-white.svg45
-rw-r--r--packages/demobank-ui/src/assets/logo.jpegbin39336 -> 0 bytes
-rw-r--r--packages/demobank-ui/src/components/Cashouts/index.ts75
-rw-r--r--packages/demobank-ui/src/components/Cashouts/state.ts48
-rw-r--r--packages/demobank-ui/src/components/Cashouts/stories.tsx29
-rw-r--r--packages/demobank-ui/src/components/Cashouts/test.ts70
-rw-r--r--packages/demobank-ui/src/components/Cashouts/views.tsx163
-rw-r--r--packages/demobank-ui/src/components/EmptyComponentExample/index.ts56
-rw-r--r--packages/demobank-ui/src/components/EmptyComponentExample/state.ts25
-rw-r--r--packages/demobank-ui/src/components/EmptyComponentExample/stories.tsx29
-rw-r--r--packages/demobank-ui/src/components/EmptyComponentExample/test.ts28
-rw-r--r--packages/demobank-ui/src/components/EmptyComponentExample/views.tsx34
-rw-r--r--packages/demobank-ui/src/components/ErrorLoadingWithDebug.tsx9
-rw-r--r--packages/demobank-ui/src/components/QR.tsx51
-rw-r--r--packages/demobank-ui/src/components/Transactions/index.ts69
-rw-r--r--packages/demobank-ui/src/components/Transactions/state.ts67
-rw-r--r--packages/demobank-ui/src/components/Transactions/stories.tsx44
-rw-r--r--packages/demobank-ui/src/components/Transactions/test.ts198
-rw-r--r--packages/demobank-ui/src/components/Transactions/views.tsx136
-rw-r--r--packages/demobank-ui/src/components/app.tsx106
-rw-r--r--packages/demobank-ui/src/components/index.examples.ts17
-rw-r--r--packages/demobank-ui/src/context/backend.ts79
-rw-r--r--packages/demobank-ui/src/context/config.ts108
-rw-r--r--packages/demobank-ui/src/context/settings.ts44
-rw-r--r--packages/demobank-ui/src/declaration.d.ts35
-rw-r--r--packages/demobank-ui/src/endpoints.ts48
-rw-r--r--packages/demobank-ui/src/forms/simplest.ts66
-rw-r--r--packages/demobank-ui/src/hooks/access.ts233
-rw-r--r--packages/demobank-ui/src/hooks/async.ts77
-rw-r--r--packages/demobank-ui/src/hooks/backend.ts157
-rw-r--r--packages/demobank-ui/src/hooks/circuit.ts319
-rw-r--r--packages/demobank-ui/src/hooks/index.ts60
-rw-r--r--packages/demobank-ui/src/hooks/preferences.ts95
-rw-r--r--packages/demobank-ui/src/i18n/bank.pot486
-rw-r--r--packages/demobank-ui/src/i18n/de.po486
-rw-r--r--packages/demobank-ui/src/i18n/en.po511
-rw-r--r--packages/demobank-ui/src/i18n/es.po497
-rw-r--r--packages/demobank-ui/src/i18n/fr.po486
-rw-r--r--packages/demobank-ui/src/i18n/it.po521
-rw-r--r--packages/demobank-ui/src/i18n/poheader26
-rw-r--r--packages/demobank-ui/src/i18n/strings-prelude19
-rw-r--r--packages/demobank-ui/src/i18n/strings.ts510
-rw-r--r--packages/demobank-ui/src/index.html41
-rw-r--r--packages/demobank-ui/src/index.tsx23
-rw-r--r--packages/demobank-ui/src/manifest.json21
-rw-r--r--packages/demobank-ui/src/pages.ts44
-rw-r--r--packages/demobank-ui/src/pages/AccountPage/index.ts85
-rw-r--r--packages/demobank-ui/src/pages/AccountPage/state.ts84
-rw-r--r--packages/demobank-ui/src/pages/AccountPage/stories.tsx29
-rw-r--r--packages/demobank-ui/src/pages/AccountPage/test.ts32
-rw-r--r--packages/demobank-ui/src/pages/AccountPage/views.tsx81
-rw-r--r--packages/demobank-ui/src/pages/BankFrame.stories.tsx29
-rw-r--r--packages/demobank-ui/src/pages/BankFrame.tsx177
-rw-r--r--packages/demobank-ui/src/pages/DownloadStats.tsx396
-rw-r--r--packages/demobank-ui/src/pages/LoginForm.tsx215
-rw-r--r--packages/demobank-ui/src/pages/OperationState/index.ts128
-rw-r--r--packages/demobank-ui/src/pages/OperationState/state.ts221
-rw-r--r--packages/demobank-ui/src/pages/OperationState/stories.tsx29
-rw-r--r--packages/demobank-ui/src/pages/OperationState/test.ts32
-rw-r--r--packages/demobank-ui/src/pages/OperationState/views.tsx403
-rw-r--r--packages/demobank-ui/src/pages/PaymentOptions.stories.tsx35
-rw-r--r--packages/demobank-ui/src/pages/PaymentOptions.tsx122
-rw-r--r--packages/demobank-ui/src/pages/PaytoWireTransferForm.stories.tsx35
-rw-r--r--packages/demobank-ui/src/pages/PaytoWireTransferForm.tsx483
-rw-r--r--packages/demobank-ui/src/pages/ProfileNavigation.tsx72
-rw-r--r--packages/demobank-ui/src/pages/PublicHistoriesPage.tsx100
-rw-r--r--packages/demobank-ui/src/pages/QrCodeSection.stories.tsx32
-rw-r--r--packages/demobank-ui/src/pages/QrCodeSection.tsx153
-rw-r--r--packages/demobank-ui/src/pages/RegistrationPage.tsx426
-rw-r--r--packages/demobank-ui/src/pages/WalletWithdrawForm.tsx281
-rw-r--r--packages/demobank-ui/src/pages/WireTransfer.tsx52
-rw-r--r--packages/demobank-ui/src/pages/WithdrawalConfirmationQuestion.tsx356
-rw-r--r--packages/demobank-ui/src/pages/WithdrawalOperationPage.tsx68
-rw-r--r--packages/demobank-ui/src/pages/WithdrawalQRCode.tsx210
-rw-r--r--packages/demobank-ui/src/pages/account/CashoutListForAccount.tsx40
-rw-r--r--packages/demobank-ui/src/pages/account/ShowAccountDetails.tsx165
-rw-r--r--packages/demobank-ui/src/pages/account/UpdateAccountPassword.tsx227
-rw-r--r--packages/demobank-ui/src/pages/admin/AccountForm.tsx596
-rw-r--r--packages/demobank-ui/src/pages/admin/AccountList.tsx143
-rw-r--r--packages/demobank-ui/src/pages/admin/AdminHome.tsx246
-rw-r--r--packages/demobank-ui/src/pages/admin/CreateNewAccount.tsx145
-rw-r--r--packages/demobank-ui/src/pages/admin/RemoveAccount.tsx194
-rw-r--r--packages/demobank-ui/src/pages/business/CreateCashout.tsx519
-rw-r--r--packages/demobank-ui/src/pages/business/ShowCashoutDetails.tsx332
-rw-r--r--packages/demobank-ui/src/pages/index.stories.tsx20
-rw-r--r--packages/demobank-ui/src/pages/rnd.ts2893
-rw-r--r--packages/demobank-ui/src/route.ts167
-rw-r--r--packages/demobank-ui/src/scss/main.css3
-rw-r--r--packages/demobank-ui/src/settings.json11
-rw-r--r--packages/demobank-ui/src/settings.ts98
-rw-r--r--packages/demobank-ui/src/stories.test.ts89
-rw-r--r--packages/demobank-ui/src/stories.tsx41
-rw-r--r--packages/demobank-ui/src/utils.ts399
-rw-r--r--packages/demobank-ui/tailwind.config.js14
-rwxr-xr-xpackages/demobank-ui/test.mjs32
-rw-r--r--packages/demobank-ui/tsconfig.json46
118 files changed, 0 insertions, 17806 deletions
diff --git a/packages/demobank-ui/.gitignore b/packages/demobank-ui/.gitignore
deleted file mode 100644
index 30cb2774c..000000000
--- a/packages/demobank-ui/.gitignore
+++ /dev/null
@@ -1,4 +0,0 @@
-node_modules
-/build
-/*.log
-/demobank-ui-settings.js
diff --git a/packages/demobank-ui/Makefile b/packages/demobank-ui/Makefile
deleted file mode 100644
index 2399cc427..000000000
--- a/packages/demobank-ui/Makefile
+++ /dev/null
@@ -1,37 +0,0 @@
-# This Makefile has been placed in the public domain
-
-ifeq ($(TOPLEVEL), yes)
- $(info top-level build)
- -include ../../.config.mk
- override DESTDIR := $(TOP_DESTDIR)
-else
- $(info package-level build)
- -include ../../.config.mk
- -include .config.mk
-endif
-
-$(info prefix is $(prefix))
-
-.PHONY: all
-all:
- @echo run \'make install\' to install
-
-spa_dir=$(DESTDIR)$(prefix)/share/taler/demobank-ui
-
-.PHONY: deps
-deps:
- pnpm install --frozen-lockfile --filter @gnu-taler/demobank-ui...
- pnpm run --filter @gnu-taler/demobank-ui... compile
- pnpm run check
- pnpm run build
-
-.PHONY: install-nodeps
-install-nodeps:
- install -d $(spa_dir)
- install ./dist/prod/* $(spa_dir)
-
-.PHONY: install
-install:
- $(MAKE) deps
- $(MAKE) install-nodeps
-
diff --git a/packages/demobank-ui/README.md b/packages/demobank-ui/README.md
deleted file mode 100644
index e7019620b..000000000
--- a/packages/demobank-ui/README.md
+++ /dev/null
@@ -1,24 +0,0 @@
-# Taler Demobank UI
-
-Web-based user interface for the libeufin demobank.
-
-## CLI Commands
-
-- `./dev.mjs` development setup. Will listen in :8080 and reload every time a file is save.
-- `./build.mjs` build for production.
-- `./test.mjs` build and run unit test
-
-## Testing
-
-By default, the demobank-ui will expect the backend to be in `window.origin` but that can be overriden using the `settings.json` file or by session in the localStorage.
-
-```
-localStorage.setItem("bank-base-url", OTHER_URL);
-```
-
-## Customizing Per-Deployment Settings
-
-To customize per-deployment settings, make sure that the
-`settings.json` file is served alongside the UI.
-
-For more information about the values check the file `settings.ts` in the src folder.
diff --git a/packages/demobank-ui/TODO b/packages/demobank-ui/TODO
deleted file mode 100644
index cc578cce0..000000000
--- a/packages/demobank-ui/TODO
+++ /dev/null
@@ -1,49 +0,0 @@
-Urgent TODOs:
-
-- General:
-
- - not only Nora dark-theme, but default light! (CSS)
- - DONE: auto-focus on input fields is not working well
- - DONE: buttons should be visibly insensitive
- as long as required input fields are not
- working
- - DONE: next required invalid/missing input field is
- not properly highlighted in red
- - Logout button needs more padding to the right (CSS)
-
-- Error bar:
- - DONE: hows JSON, should only show good error message
- and numeric code, not JSON syntax
- - should auto-hide after next action, no need for
- "clear"!
- - need variant "status bar" in green (or blue)
- which shows status of last operation
-
-* H1-Titles:
- - Center more (currently way on the left) (CSS)
-
-- Assets:
-
- - Numeric amount needs to be shown MUCH bigger (CSS)
- - Center more? (CSS)
-
-- Payments:
-
- - Amount to withdraw currently shown in white-on-white (CSS)
- - Big frame drawn around notebook-tabs is not nice (CSS)
- - Center more? (CSS)
- - "Wire to bank account"
- - maybe split two types (payto and IBAN) into
- two tabs?
- - currently cannot switch back from payto to IBAN
-
-- Withdraw:
-
- - Should use new 'status' bar at the end, instead
- of extra dialog with "close" button
- - ditto for bank-wire-transfer final stage
-
-- Footer:
- - overlaps with transaction history or other
- content, needs to consistently show at the
- end! => change rendering logic!? (CSS?)
diff --git a/packages/demobank-ui/build.mjs b/packages/demobank-ui/build.mjs
deleted file mode 100755
index 64ddc3774..000000000
--- a/packages/demobank-ui/build.mjs
+++ /dev/null
@@ -1,28 +0,0 @@
-#!/usr/bin/env node
-/*
- This file is part of GNU Taler
- (C) 2022 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 { build } from "@gnu-taler/web-util/build";
-
-await build({
- type: "production",
- source: {
- js: ["src/index.tsx"],
- assets: [{ base: "src", files: ["src/index.html"] }],
- },
- destination: "./dist/prod",
- css: "postcss",
-});
diff --git a/packages/demobank-ui/contrib/po2ts b/packages/demobank-ui/contrib/po2ts
deleted file mode 100755
index a135da61b..000000000
--- a/packages/demobank-ui/contrib/po2ts
+++ /dev/null
@@ -1,42 +0,0 @@
-#!/usr/bin/env node
-/*
- This file is part of GNU Taler
- (C) 2020 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/>
- */
-
-/**
- * Convert a <lang>.po file into a JavaScript / TypeScript expression.
- */
-
-const po2json = require("po2json");
-
-const filename = process.argv[2];
-
-if (!filename) {
- console.error("error: missing filename");
- process.exit(1);
-}
-
-const m = filename.match(/([a-zA-Z0-9-_]+).po/);
-
-if (!m) {
- console.error("error: unexpected filename (expected <lang>.po)");
- process.exit(1);
-}
-
-const lang = m[1];
-const pojson = po2json.parseFileSync(filename, { format: "jed1.x", fuzzy: true });
-const s =
- "strings['" + lang + "'] = " + JSON.stringify(pojson, null, " ") + ";\n";
-console.log(s);
diff --git a/packages/demobank-ui/copyleft-header.js b/packages/demobank-ui/copyleft-header.js
deleted file mode 100644
index 2635717c5..000000000
--- a/packages/demobank-ui/copyleft-header.js
+++ /dev/null
@@ -1,15 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2022 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/demobank-ui/dev.mjs b/packages/demobank-ui/dev.mjs
deleted file mode 100755
index c5ea318e7..000000000
--- a/packages/demobank-ui/dev.mjs
+++ /dev/null
@@ -1,41 +0,0 @@
-#!/usr/bin/env node
-/*
- This file is part of GNU Taler
- (C) 2022 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 { serve } from "@gnu-taler/web-util/node";
-import { initializeDev } from "@gnu-taler/web-util/build";
-
-const devEntryPoints = ["src/stories.tsx", "src/index.tsx"];
-
-const build = initializeDev({
- type: "development",
- source: {
- js: devEntryPoints,
- assets: [{ base: "src", files: ["src/index.html", "src/settings.json"] }],
- },
- destination: "./dist/dev",
- public: "/app",
- css: "postcss",
-});
-
-await build();
-
-serve({
- folder: "./dist/dev",
- port: 8080,
- source: "./src",
- onSourceUpdate: build,
-});
diff --git a/packages/demobank-ui/package.json b/packages/demobank-ui/package.json
deleted file mode 100644
index fa6f5bc7b..000000000
--- a/packages/demobank-ui/package.json
+++ /dev/null
@@ -1,74 +0,0 @@
-{
- "private": true,
- "name": "@gnu-taler/demobank-ui",
- "version": "0.9.3-dev.29",
- "license": "AGPL-3.0-OR-LATER",
- "type": "module",
- "scripts": {
- "build": "./build.mjs",
- "check": "tsc",
- "clean": "rm -rf dist lib tsconfig.tsbuildinfo",
- "compile": "tsc && ./build.mjs",
- "test": "./test.mjs && mocha --require source-map-support/register 'dist/test/**/*.test.js' 'dist/test/**/test.js'",
- "lint": "eslint 'src/**/*.{js,jsx,ts,tsx}'",
- "typedoc": "typedoc --out dist/typedoc ./src/",
- "i18n:extract": "pogen extract",
- "i18n:merge": "pogen merge",
- "i18n:emit": "pogen emit",
- "i18n": "pnpm i18n:extract && pnpm i18n:merge && pnpm i18n:emit",
- "pretty": "prettier --write src"
- },
- "dependencies": {
- "@gnu-taler/taler-util": "workspace:*",
- "@gnu-taler/web-util": "workspace:*",
- "date-fns": "2.29.3",
- "history": "4.10.1",
- "jed": "1.1.1",
- "preact": "10.11.3",
- "preact-router": "3.2.1",
- "qrcode-generator": "^1.4.4",
- "swr": "2.0.3"
- },
- "eslintConfig": {
- "plugins": [
- "header"
- ],
- "rules": {
- "header/header": [
- 2,
- "copyleft-header.js"
- ]
- },
- "extends": [
- "prettier"
- ]
- },
- "devDependencies": {
- "@creativebulma/bulma-tooltip": "^1.2.0",
- "@gnu-taler/pogen": "^0.0.5",
- "@tailwindcss/forms": "^0.5.3",
- "@tailwindcss/typography": "^0.5.9",
- "@types/chai": "^4.3.0",
- "@types/history": "^4.7.8",
- "@types/mocha": "^10.0.1",
- "@types/node": "^18.11.17",
- "@typescript-eslint/eslint-plugin": "^5.41.0",
- "@typescript-eslint/parser": "^5.41.0",
- "autoprefixer": "^10.4.14",
- "bulma": "^0.9.4",
- "bulma-checkbox": "^1.1.1",
- "bulma-radio": "^1.1.1",
- "chai": "^4.3.6",
- "esbuild": "^0.19.9",
- "eslint-config-preact": "^1.2.0",
- "mocha": "^9.2.0",
- "po2json": "^0.4.5",
- "preact-render-to-string": "^5.2.6",
- "sass": "1.56.1",
- "tailwindcss": "^3.3.2",
- "typescript": "5.3.3"
- },
- "pogen": {
- "domain": "bank"
- }
-}
diff --git a/packages/demobank-ui/postcss.config.js b/packages/demobank-ui/postcss.config.js
deleted file mode 100644
index 2e7af2b7f..000000000
--- a/packages/demobank-ui/postcss.config.js
+++ /dev/null
@@ -1,6 +0,0 @@
-export default {
- plugins: {
- tailwindcss: {},
- autoprefixer: {},
- },
-}
diff --git a/packages/demobank-ui/src/Routing.tsx b/packages/demobank-ui/src/Routing.tsx
deleted file mode 100644
index 4a250a0d5..000000000
--- a/packages/demobank-ui/src/Routing.tsx
+++ /dev/null
@@ -1,352 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2022 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 { useTranslationContext } from "@gnu-taler/web-util/browser";
-import { createHashHistory } from "history";
-import { Fragment, VNode, h } from "preact";
-import { Route, Router, route } from "preact-router";
-import { useEffect } from "preact/hooks";
-
-import { useBackendState } from "./hooks/backend.js";
-import { BankFrame } from "./pages/BankFrame.js";
-import { WithdrawalOperationPage } from "./pages/WithdrawalOperationPage.js";
-import { LoginForm } from "./pages/LoginForm.js";
-import { PublicHistoriesPage } from "./pages/PublicHistoriesPage.js";
-import { RegistrationPage } from "./pages/RegistrationPage.js";
-import { AdminHome } from "./pages/admin/AdminHome.js";
-import { CreateCashout } from "./pages/business/CreateCashout.js";
-import { ShowAccountDetails } from "./pages/account/ShowAccountDetails.js";
-import { UpdateAccountPassword } from "./pages/account/UpdateAccountPassword.js";
-import { RemoveAccount } from "./pages/admin/RemoveAccount.js";
-import { CreateNewAccount } from "./pages/admin/CreateNewAccount.js";
-import { CashoutListForAccount } from "./pages/account/CashoutListForAccount.js";
-import { ShowCashoutDetails } from "./pages/business/ShowCashoutDetails.js";
-import { WireTransfer } from "./pages/WireTransfer.js";
-import { AccountPage } from "./pages/AccountPage/index.js";
-import { useSettingsContext } from "./context/settings.js";
-import { useBankCoreApiContext } from "./context/config.js";
-import { DownloadStats } from "./pages/DownloadStats.js";
-
-export function Routing(): VNode {
- const history = createHashHistory();
- const backend = useBackendState();
- const settings = useSettingsContext();
- const { config } = useBankCoreApiContext();
- const { i18n } = useTranslationContext();
-
- if (backend.state.status === "loggedOut") {
- return <BankFrame >
- <Router history={history}>
- <Route
- path="/login"
- component={() => (
- <Fragment>
- <div class="sm:mx-auto sm:w-full sm:max-w-sm">
- <h2 class="text-center text-2xl font-bold leading-9 tracking-tight text-gray-900">{i18n.str`Welcome to ${settings.bankName}!`}</h2>
- </div>
-
- <LoginForm
- onRegister={() => {
- route("/register");
- }}
- />
- </Fragment>
- )}
- />
- <Route
- path="/public-accounts"
- component={() => <PublicHistoriesPage />}
- />
- <Route
- path="/operation/:wopid"
- component={({ wopid }: { wopid: string }) => (
- <WithdrawalOperationPage
- operationId={wopid}
- onContinue={() => {
- route("/account");
- }}
- />
- )}
- />
- {config.allow_registrations &&
- <Route
- path="/register"
- component={() => (
- <RegistrationPage
- onComplete={() => {
- route("/account");
- }}
- onCancel={() => {
- route("/account");
- }}
- />
- )}
- />
- }
- <Route default component={Redirect} to="/login" />
- </Router>
- </BankFrame>
- }
- const { isUserAdministrator, username } = backend.state
-
- return (
- <BankFrame account={username}>
- <Router history={history}>
- <Route
- path="/operation/:wopid"
- component={({ wopid }: { wopid: string }) => (
- <WithdrawalOperationPage
- operationId={wopid}
- onContinue={() => {
- route("/account");
- }}
- />
- )}
- />
- <Route
- path="/public-accounts"
- component={() => <PublicHistoriesPage />}
- />
- <Route
- path="/download-stats"
- component={() => <DownloadStats
- onCancel={() => {
- route("/account")
- }}
- />}
- />
-
- <Route
- path="/new-account"
- component={() => <CreateNewAccount
- onCancel={() => {
- route("/account")
- }}
- onCreateSuccess={() => {
- route("/account")
- }}
- />}
- />
-
- <Route
- path="/profile/:account/details"
- component={({ account }: { account: string }) => (
- <ShowAccountDetails
- account={account}
- onUpdateSuccess={() => {
- route("/account")
- }}
- onClear={() => {
- route("/account")
- }}
- />
- )}
- />
-
- <Route
- path="/profile/:account/change-password"
- component={({ account }: { account: string }) => (
- <UpdateAccountPassword
- focus
- account={account}
- onUpdateSuccess={() => {
- route("/account")
- }}
- onCancel={() => {
- route("/account")
- }}
- />
- )}
- />
- <Route
- path="/profile/:account/delete"
- component={({ account }: { account: string }) => (
- <RemoveAccount
- account={account}
- onUpdateSuccess={() => {
- route("/account")
- }}
- onCancel={() => {
- route("/account")
- }}
- />
- )}
- />
-
- <Route
- path="/profile/:account/cashouts"
- component={({ account }: { account: string }) => (
- <CashoutListForAccount
- account={account}
- onSelected={(cid) => {
- route(`/cashout/${cid}`)
- }}
- onClose={() => {
- route("/account")
- }}
- />
- )}
- />
-
- <Route
- path="/delete-my-account"
- component={() => (
- <RemoveAccount
- account={username}
- onUpdateSuccess={() => {
- route("/")
- }}
- onCancel={() => {
- route("/account")
- }}
- />
- )}
- />
- <Route
- path="/my-profile"
- component={() => (
- <ShowAccountDetails
- account={username}
- onUpdateSuccess={() => {
- route("/account")
- }}
- onClear={() => {
- route("/account")
- }}
- />
- )}
- />
- <Route
- path="/my-password"
- component={() => (
- <UpdateAccountPassword
- focus
- account={username}
- onUpdateSuccess={() => {
- route("/account")
- }}
- onCancel={() => {
- route("/account")
- }}
- />
- )}
- />
-
- <Route
- path="/my-cashouts"
- component={() => (
- <CashoutListForAccount
- account={username}
- onSelected={(cid) => {
- route(`/cashout/${cid}`)
- }}
- onClose={() => {
- route("/account");
- }}
- />
- )}
- />
-
- <Route
- path="/new-cashout"
- component={() => (
- <CreateCashout
- account={username}
- onComplete={(cid) => {
- route(`/cashout/${cid}`);
- }}
- onCancel={() => {
- route("/account");
- }}
- />
- )}
- />
-
- <Route
- path="/cashout/:cid"
- component={({ cid }: { cid: string }) => (
- <ShowCashoutDetails
- id={cid}
- onCancel={() => {
- route("/my-cashouts");
- }}
- />
- )}
- />
-
-
- <Route
- path="/wire-transfer/:dest"
- component={({ dest }: { dest: string }) => (
- <WireTransfer
- toAccount={dest}
- onCancel={() => {
- route("/account")
- }}
- onSuccess={() => {
- route("/account")
- }}
- />
- )}
- />
-
- <Route
- path="/account"
- component={() => {
- if (isUserAdministrator) {
- return <AdminHome
- onRegister={() => {
- route("/register");
- }}
- onCreateAccount={() => {
- route("/new-account")
- }}
- onShowAccountDetails={(aid) => {
- route(`/profile/${aid}/details`)
- }}
- onRemoveAccount={(aid) => {
- route(`/profile/${aid}/delete`)
- }}
- onShowCashoutForAccount={(aid) => {
- route(`/profile/${aid}/cashouts`)
- }}
- onUpdateAccountPassword={(aid) => {
- route(`/profile/${aid}/change-password`)
-
- }}
- />;
- } else {
- return <AccountPage
- account={username}
- goToConfirmOperation={(wopid) => {
- route(`/operation/${wopid}`);
- }}
- />
- }
- }}
- />
- <Route default component={Redirect} to="/account" />
- </Router>
- </BankFrame>
- );
-}
-
-function Redirect({ to }: { to: string }): VNode {
- useEffect(() => {
- route(to, true);
- }, []);
- return <div>being redirected to {to}</div>;
-}
diff --git a/packages/demobank-ui/src/assets/empty.png b/packages/demobank-ui/src/assets/empty.png
deleted file mode 100644
index 5120d3138..000000000
--- a/packages/demobank-ui/src/assets/empty.png
+++ /dev/null
Binary files differ
diff --git a/packages/demobank-ui/src/assets/example/id1.jpg b/packages/demobank-ui/src/assets/example/id1.jpg
deleted file mode 100644
index 5d022a379..000000000
--- a/packages/demobank-ui/src/assets/example/id1.jpg
+++ /dev/null
Binary files differ
diff --git a/packages/demobank-ui/src/assets/favicon.ico b/packages/demobank-ui/src/assets/favicon.ico
deleted file mode 100644
index 07419145b..000000000
--- a/packages/demobank-ui/src/assets/favicon.ico
+++ /dev/null
Binary files differ
diff --git a/packages/demobank-ui/src/assets/icons/android-chrome-192x192.png b/packages/demobank-ui/src/assets/icons/android-chrome-192x192.png
deleted file mode 100644
index 93ebe2e2c..000000000
--- a/packages/demobank-ui/src/assets/icons/android-chrome-192x192.png
+++ /dev/null
Binary files differ
diff --git a/packages/demobank-ui/src/assets/icons/android-chrome-512x512.png b/packages/demobank-ui/src/assets/icons/android-chrome-512x512.png
deleted file mode 100644
index 52d1623ea..000000000
--- a/packages/demobank-ui/src/assets/icons/android-chrome-512x512.png
+++ /dev/null
Binary files differ
diff --git a/packages/demobank-ui/src/assets/icons/apple-touch-icon.png b/packages/demobank-ui/src/assets/icons/apple-touch-icon.png
deleted file mode 100644
index 254e4bb4d..000000000
--- a/packages/demobank-ui/src/assets/icons/apple-touch-icon.png
+++ /dev/null
Binary files differ
diff --git a/packages/demobank-ui/src/assets/icons/favicon-16x16.png b/packages/demobank-ui/src/assets/icons/favicon-16x16.png
deleted file mode 100644
index e81177dcb..000000000
--- a/packages/demobank-ui/src/assets/icons/favicon-16x16.png
+++ /dev/null
Binary files differ
diff --git a/packages/demobank-ui/src/assets/icons/favicon-32x32.png b/packages/demobank-ui/src/assets/icons/favicon-32x32.png
deleted file mode 100644
index 40e9b5b47..000000000
--- a/packages/demobank-ui/src/assets/icons/favicon-32x32.png
+++ /dev/null
Binary files differ
diff --git a/packages/demobank-ui/src/assets/icons/languageicon.svg b/packages/demobank-ui/src/assets/icons/languageicon.svg
deleted file mode 100644
index 22d58da65..000000000
--- a/packages/demobank-ui/src/assets/icons/languageicon.svg
+++ /dev/null
@@ -1,48 +0,0 @@
-<?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/demobank-ui/src/assets/icons/mstile-150x150.png b/packages/demobank-ui/src/assets/icons/mstile-150x150.png
deleted file mode 100644
index 9cfb889be..000000000
--- a/packages/demobank-ui/src/assets/icons/mstile-150x150.png
+++ /dev/null
Binary files differ
diff --git a/packages/demobank-ui/src/assets/logo-2021.svg b/packages/demobank-ui/src/assets/logo-2021.svg
deleted file mode 100644
index 8c5ff3e5b..000000000
--- a/packages/demobank-ui/src/assets/logo-2021.svg
+++ /dev/null
@@ -1,9 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 201 90">
- <g fill="#0042b3" fill-rule="evenodd" stroke-width=".3">
- <path d="M86.7 1.1c15.6 0 29 9.4 36 23.2h-5.9A35.1 35.1 0 0086.7 6.5C67 6.5 51 23.6 51 44.7c0 10.4 3.8 19.7 10 26.6a31.4 31.4 0 01-4.2 3A45.2 45.2 0 0146 44.7c0-24 18.2-43.6 40.7-43.6zm35.8 64.3a40.4 40.4 0 01-39 22.8c3-1.5 6-3.5 8.6-5.7a35.6 35.6 0 0024.6-17.1z" />
- <path d="M64.2 1.1l3.1.1c-3 1.6-5.9 3.5-8.5 5.8a37.5 37.5 0 00-30.2 37.7c0 14.3 7.3 26.7 18 33.3a29.6 29.6 0 01-8.5.2c-9-8-14.6-20-14.6-33.5 0-24 18.2-43.6 40.7-43.6zm5.4 81.4a35.6 35.6 0 0024.6-17.1h5.9a40.4 40.4 0 01-39 22.8c3-1.5 5.9-3.5 8.5-5.7zm24.8-58.2a37 37 0 00-12.6-12.8 29.6 29.6 0 018.5-.2c4 3.6 7.4 8 9.9 13z" />
- <path d="M41.8 1.1c1 0 2 0 3.1.2-3 1.5-5.9 3.4-8.5 5.6A37.5 37.5 0 006.1 44.7c0 21.1 16 38.3 35.7 38.3 12.6 0 23.6-7 30-17.6h5.8a40.4 40.4 0 01-35.8 23C19.3 88.4 1 68.8 1 44.7c0-24 18.2-43.6 40.7-43.6zm30.1 23.2a38.1 38.1 0 00-4.5-6.1c1.3-1.2 2.7-2.2 4.3-3 2.3 2.7 4.4 5.8 6 9.1z" />
- </g>
- <path d="M76.1 34.4h9.2v-5H61.9v5H71v26h5.1zM92.6 52.9h13.7l3 7.4h5.3l-12.7-31.2h-4.7L84.5 60.3h5.2zm11.8-4.9h-9.9l5-12.4zM123.8 29.4h-4.6v31h20.6v-5h-16zM166.5 29.4H145v31h21.6v-5H150v-8.3h14.5v-4.9h-14.5v-8h16.4zM191.2 39.5c0 1.6-.5 2.8-1.6 3.8s-2.6 1.4-4.4 1.4h-7.4V34.3h7.4c1.9 0 3.4.4 4.4 1.3 1 .9 1.6 2.2 1.6 3.9zm6 20.8l-7.7-11.7c1-.3 1.9-.7 2.7-1.3a8.8 8.8 0 003.6-4.6c.4-1 .5-2.2.5-3.5 0-1.5-.2-2.9-.7-4.1a8.4 8.4 0 00-2.1-3.1c-1-.8-2-1.5-3.4-2-1.3-.4-2.8-.6-4.5-.6h-12.9v31h5V49.4h6.5l7 10.8z" />
-</svg> \ No newline at end of file
diff --git a/packages/demobank-ui/src/assets/logo-white.svg b/packages/demobank-ui/src/assets/logo-white.svg
deleted file mode 100644
index cb1f023c5..000000000
--- a/packages/demobank-ui/src/assets/logo-white.svg
+++ /dev/null
@@ -1,45 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<svg
- xmlns:svg="http://www.w3.org/2000/svg"
- xmlns="http://www.w3.org/2000/svg"
- width="670"
- height="300"
- viewBox="0 0 201 90"
- version="1.1"
- id="svg8">
- <g
- id="logo">
- <g
- id="circles"
- style="fill:#FFF;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.327943">
- <path
- d="m 86.662153,1.1211936 c 15.589697,0 29.129227,9.4011664 35.961027,23.2018054 h -5.81736 C 110.4866,13.623304 99.349002,6.5180852 86.662153,6.5180852 c -19.690571,0 -35.652876,17.1120008 -35.652876,38.2205688 0,10.331797 3.825597,19.704678 10.03957,26.582945 -1.342357,1.120912 -2.771532,2.127905 -4.275488,3.006754 C 50.071485,66.553412 45.974857,56.15992 45.974857,44.738654 c 0,-24.089211 18.216325,-43.6174604 40.687296,-43.6174604 z M 122.51416,65.375898 c -6.86645,13.680134 -20.34561,22.980218 -35.852007,22.980218 -1.052702,0 -2.096093,-0.04291 -3.128683,-0.127026 3.052192,-1.561167 5.913582,-3.480387 8.538307,-5.707305 10.320963,-1.684389 19.185983,-8.113638 24.601813,-17.145887 z"
- id="path2350" />
- <path
- d="m 64.212372,1.1211936 c 1.052607,0 2.095998,0.042919 3.128684,0.1270583 C 64.288864,2.8094199 61.427378,4.728606 58.802653,6.9555572 41.679542,9.7498571 28.559494,25.601563 28.559494,44.738654 c 0,14.264563 7.29059,26.702023 18.093843,33.268925 -1.593656,0.26719 -3.226966,0.406948 -4.890748,0.406948 -1.239545,0 -2.46151,-0.07952 -3.663522,-0.229364 C 29.191129,70.184015 23.525076,58.171633 23.525076,44.738654 23.525076,20.649443 41.7414,1.1211936 64.212372,1.1211936 Z M 69.62209,82.521785 C 79.943207,80.837396 88.808164,74.407841 94.224059,65.375422 h 5.840511 c -6.866354,13.680305 -20.345548,22.980694 -35.852198,22.980694 -1.052703,0 -2.095999,-0.04291 -3.128684,-0.127026 3.052002,-1.561371 5.913836,-3.480218 8.538402,-5.707305 z M 94.355885,24.322999 c -3.13939,-5.314721 -7.467551,-9.74275 -12.584511,-12.853269 1.593656,-0.26719 3.226904,-0.406948 4.890779,-0.406948 1.239451,0 2.461512,0.07952 3.663524,0.229364 4.016018,3.607242 7.373195,8.030111 9.849053,13.030853 z"
- id="path2352" />
- <path
- d="m 41.762589,1.1211936 c 1.064296,0 2.118804,0.044379 3.162607,0.1302161 -3.046523,1.558961 -5.903162,3.4745139 -8.52358,5.6968133 C 19.254624,9.7205882 6.1097128,25.583465 6.1097128,44.738654 c 0,21.108568 15.9624012,38.22057 35.6528762,38.22057 12.599746,0 23.672446,-7.007056 30.013748,-17.583802 h 5.838515 C 70.748498,79.055727 57.26924,88.356116 41.762589,88.356116 c -22.470907,0 -40.6871998,-19.52825 -40.6871998,-43.617462 0,-24.089211 18.2162928,-43.6174604 40.6871998,-43.6174604 z M 71.905375,24.322999 c -1.31192,-2.220567 -2.830984,-4.287049 -4.528877,-6.166508 1.342452,-1.120945 2.771374,-2.128381 4.275139,-3.00723 2.372984,2.753011 4.418875,5.834636 6.072489,9.173738 z"
- id="path2354" />
- </g>
- <g
- id="letters"
- style="fill:#FFF">
- <path
- d="m 76.135411,34.409066 h 9.161042 V 29.36588 H 61.857537 v 5.043186 h 9.161137 v 25.92317 h 5.116737 z"
- id="path2346" />
- <path
- d="m 92.647571,52.856334 h 13.659009 l 2.93009,7.476072 h 5.36461 L 101.89122,29.144903 H 97.187186 L 84.477089,60.332406 h 5.199533 z m 11.802109,-4.822276 h -9.944771 l 4.951718,-12.386462 z"
- id="path2362" />
- <path
- d="m 123.80641,29.366084 h -4.58038 v 30.966322 h 20.54728 v -4.910253 c -5.32227,0 -10.64463,0 -15.9669,0 z"
- id="path2356" />
- <path
- d="m 166.4722,29.366084 h -21.37564 v 30.966322 h 21.58203 v -4.910253 h -16.54771 v -8.27275 h 14.48439 V 42.23925 h -14.48439 v -7.962811 h 16.34132 z"
- id="path2360" />
- <path
- d="m 191.19035,39.474593 c 0,1.59947 -0.53646,2.87535 -1.61628,3.818883 -1.07281,0.95124 -2.52409,1.422837 -4.34678,1.422837 h -7.44851 V 34.276439 h 7.4073 c 1.9051,0 3.38376,0.435027 4.42939,1.312178 1.05226,0.870258 1.57488,2.167734 1.57488,3.885976 z m 6.06602,20.857813 -7.79911,-11.723191 c 1.01771,-0.294794 1.94631,-0.714813 2.78553,-1.260566 0.83885,-0.545619 1.56122,-1.209263 2.16629,-1.990627 0.60541,-0.781738 1.07981,-1.681096 1.42369,-2.698345 0.34378,-1.017553 0.51561,-2.175238 0.51561,-3.472883 0,-1.50409 -0.24743,-2.867948 -0.74267,-4.092048 -0.49515,-1.223794 -1.20344,-2.256186 -2.12499,-3.096734 -0.92173,-0.840446 -2.04957,-1.489252 -3.38375,-1.946452 -1.33447,-0.457267 -2.82692,-0.685476 -4.4774,-0.685476 h -12.87512 v 30.966322 h 5.03433 V 49.538522 h 6.37569 l 7.11829,10.793884 z"
- id="path2358" />
- </g>
- </g>
-</svg>
diff --git a/packages/demobank-ui/src/assets/logo.jpeg b/packages/demobank-ui/src/assets/logo.jpeg
deleted file mode 100644
index 489832f7c..000000000
--- a/packages/demobank-ui/src/assets/logo.jpeg
+++ /dev/null
Binary files differ
diff --git a/packages/demobank-ui/src/components/Cashouts/index.ts b/packages/demobank-ui/src/components/Cashouts/index.ts
deleted file mode 100644
index 571f23184..000000000
--- a/packages/demobank-ui/src/components/Cashouts/index.ts
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2022 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 { Loading, utils } from "@gnu-taler/web-util/browser";
-import { AbsoluteTime, AmountJson, TalerCoreBankErrorsByMethod, TalerCorebankApi, TalerError } from "@gnu-taler/taler-util";
-import { ErrorLoadingWithDebug } from "../ErrorLoadingWithDebug.js";
-import { useComponentState } from "./state.js";
-import { FailedView, ReadyView } from "./views.js";
-
-export interface Props {
- account: string;
- onSelected: (id: number) => void;
-}
-
-export type State = State.Loading | State.Failed | State.LoadingUriError | State.Ready;
-
-export namespace State {
- export interface Loading {
- status: "loading";
- error: undefined;
- }
-
- export interface LoadingUriError {
- status: "loading-error";
- error: TalerError;
- }
-
- export interface Failed {
- status: "failed";
- error: TalerCoreBankErrorsByMethod<"getAccountCashouts">;
- }
-
- export interface BaseInfo {
- error: undefined;
- }
- export interface Ready extends BaseInfo {
- status: "ready";
- error: undefined;
- cashouts: (TalerCorebankApi.CashoutStatusResponse & { id: number })[];
- onSelected: (id: number) => void;
- }
-}
-
-export interface Transaction {
- negative: boolean;
- counterpart: string;
- when: AbsoluteTime;
- amount: AmountJson | undefined;
- subject: string;
-}
-
-const viewMapping: utils.StateViewMap<State> = {
- loading: Loading,
- "loading-error": ErrorLoadingWithDebug,
- "failed": FailedView,
- ready: ReadyView,
-};
-
-export const Cashouts = utils.compose(
- (p: Props) => useComponentState(p),
- viewMapping,
-);
diff --git a/packages/demobank-ui/src/components/Cashouts/state.ts b/packages/demobank-ui/src/components/Cashouts/state.ts
deleted file mode 100644
index 814755541..000000000
--- a/packages/demobank-ui/src/components/Cashouts/state.ts
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2022 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 { TalerError } from "@gnu-taler/taler-util";
-import { useCashouts } from "../../hooks/circuit.js";
-import { Props, State } from "./index.js";
-
-export function useComponentState({ account, onSelected }: Props): State {
- const result = useCashouts(account);
- if (!result) {
- return {
- status: "loading",
- error: undefined,
- };
- }
- if (result instanceof TalerError) {
- return {
- status: "loading-error",
- error: result,
- };
- }
- if (result.type === "fail") {
- return {
- status: "failed",
- error: result
- }
- }
-
- return {
- status: "ready",
- error: undefined,
- cashouts: result.body.cashouts,
- onSelected,
- };
-}
diff --git a/packages/demobank-ui/src/components/Cashouts/stories.tsx b/packages/demobank-ui/src/components/Cashouts/stories.tsx
deleted file mode 100644
index 0415b2362..000000000
--- a/packages/demobank-ui/src/components/Cashouts/stories.tsx
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2022 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 * as tests from "@gnu-taler/web-util/testing";
-import { ReadyView } from "./views.js";
-
-export default {
- title: "transaction list",
-};
-
-export const Ready = tests.createExample(ReadyView, {});
diff --git a/packages/demobank-ui/src/components/Cashouts/test.ts b/packages/demobank-ui/src/components/Cashouts/test.ts
deleted file mode 100644
index 423803cd2..000000000
--- a/packages/demobank-ui/src/components/Cashouts/test.ts
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2022 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 * as tests from "@gnu-taler/web-util/testing";
-import { SwrMockEnvironment } from "@gnu-taler/web-util/testing";
-import { expect } from "chai";
-import { CASHOUT_API_EXAMPLE } from "../../endpoints.js";
-import { Props } from "./index.js";
-import { useComponentState } from "./state.js";
-
-describe("Cashout states", () => {
- it.skip("should query backend and render transactions", async () => {
- const env = new SwrMockEnvironment();
-
- const props: Props = {
- account: "123",
- onSelected: () => {
- null;
- },
- };
-
- env.addRequestExpectation(CASHOUT_API_EXAMPLE.LIST_FIRST_PAGE, {
- response: {
- cashouts: [],
- },
- });
-
- env.addRequestExpectation(CASHOUT_API_EXAMPLE.MULTI_GET_EMPTY_FIRST_PAGE, {
- response: [],
- });
-
- const hookBehavior = await tests.hookBehaveLikeThis(
- useComponentState,
- props,
- [
- ({ status, error }) => {
- expect(status).equals("loading");
- expect(error).undefined;
- },
- ({ status, error }) => {
- expect(status).equals("ready");
- expect(error).undefined;
- },
- ],
- env.buildTestingContext(),
- );
-
- expect(hookBehavior).deep.eq({ result: "ok" });
-
- expect(env.assertJustExpectedRequestWereMade()).deep.eq({ result: "ok" });
- });
-});
diff --git a/packages/demobank-ui/src/components/Cashouts/views.tsx b/packages/demobank-ui/src/components/Cashouts/views.tsx
deleted file mode 100644
index 341c43b48..000000000
--- a/packages/demobank-ui/src/components/Cashouts/views.tsx
+++ /dev/null
@@ -1,163 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2022 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 { Amounts, TalerError, assertUnreachable } from "@gnu-taler/taler-util";
-import { Attention, Loading, useTranslationContext } from "@gnu-taler/web-util/browser";
-import { format } from "date-fns";
-import { Fragment, VNode, h } from "preact";
-import { useConversionInfo } from "../../hooks/circuit.js";
-import { RenderAmount } from "../../pages/PaytoWireTransferForm.js";
-import { ErrorLoadingWithDebug } from "../ErrorLoadingWithDebug.js";
-import { State } from "./index.js";
-
-export function FailedView({ error }: State.Failed) {
- const { i18n } = useTranslationContext();
- switch (error.case) {
- case "cashout-not-supported": return <Attention type="danger"
- title={i18n.str`Cashout not supported.`}>
- <div class="mt-2 text-sm text-red-700">
- {error.detail.hint}
- </div>
- </Attention>
- case "account-not-found": return <Attention type="danger"
- title={i18n.str`Account not found.`}>
- <div class="mt-2 text-sm text-red-700">
- {error.detail.hint}
- </div>
- </Attention>
- default: assertUnreachable(error)
- }
-}
-
-export function ReadyView({ cashouts, onSelected }: State.Ready): VNode {
- const { i18n } = useTranslationContext();
- const resp = useConversionInfo();
- if (!resp) {
- return <Loading />
- }
- if (resp instanceof TalerError) {
- return <ErrorLoadingWithDebug error={resp} />
- }
- if (!cashouts.length) return <div />
- const txByDate = cashouts.reduce((prev, cur) => {
- const d = cur.creation_time.t_s === "never"
- ? ""
- : format(cur.creation_time.t_s * 1000, "dd/MM/yyyy")
- if (!prev[d]) {
- prev[d] = []
- }
- prev[d].push(cur)
- return prev
- }, {} as Record<string, typeof cashouts>)
- return (
- <div class="px-4 mt-4">
- <div class="sm:flex sm:items-center">
- <div class="sm:flex-auto">
- <h1 class="text-base font-semibold leading-6 text-gray-900"><i18n.Translate>Latest cashouts</i18n.Translate></h1>
- </div>
- </div>
- <div class="-mx-4 mt-5 ring-1 ring-gray-300 sm:mx-0 rounded-lg min-w-fit bg-white">
- <table class="min-w-full divide-y divide-gray-300">
- <thead>
- <tr>
- <th scope="col" class=" pl-2 py-3.5 text-left text-sm font-semibold text-gray-900">{i18n.str`Created`}</th>
- <th scope="col" class="hidden sm:table-cell pl-2 py-3.5 text-left text-sm font-semibold text-gray-900">{i18n.str`Confirmed`}</th>
- <th scope="col" class="hidden sm:table-cell pl-2 py-3.5 text-left text-sm font-semibold text-gray-900">{i18n.str`Total debit`}</th>
- <th scope="col" class="hidden sm:table-cell pl-2 py-3.5 text-left text-sm font-semibold text-gray-900">{i18n.str`Total credit`}</th>
- <th scope="col" class="hidden sm:table-cell pl-2 py-3.5 text-left text-sm font-semibold text-gray-900">{i18n.str`Status`}</th>
- <th scope="col" class="hidden sm:table-cell pl-2 py-3.5 text-left text-sm font-semibold text-gray-900">{i18n.str`Subject`}</th>
- </tr>
- </thead>
- <tbody>
- {Object.entries(txByDate).map(([date, txs], idx) => {
- return <Fragment key={idx}>
- <tr class="border-t border-gray-200">
- <th colSpan={6} scope="colgroup" class="bg-gray-50 py-2 pl-4 pr-3 text-left text-sm font-semibold text-gray-900 sm:pl-3">
- {date}
- </th>
- </tr>
- {txs.map(item => {
- const creationTime = item.creation_time.t_s === "never" ? "" : format(item.creation_time.t_s * 1000, "HH:mm:ss")
- const confirmationTime = item.confirmation_time
- ? item.confirmation_time.t_s === "never" ? i18n.str`never` : format(item.confirmation_time.t_s, "dd/MM/yyyy HH:mm:ss")
- : "-"
- return (<tr key={idx} class="border-b border-gray-200 hover:bg-gray-200 last:border-none">
-
- <td onClick={(e) => {
- e.preventDefault();
- onSelected(item.id);
- }} class="relative py-2 pl-2 pr-2 text-sm ">
- <div class="font-medium text-gray-900">{creationTime}</div>
- {/* <dl class="font-normal sm:hidden">
- <dt class="sr-only sm:hidden"><i18n.Translate>Amount</i18n.Translate></dt>
- <dd class="mt-1 truncate text-gray-700">
- {item.negative ? i18n.str`sent` : i18n.str`received`} {item.amount ? (
- <span data-negative={item.negative ? "true" : "false"} class="data-[negative=false]:text-green-600 data-[negative=true]:text-red-600">
- <RenderAmount value={item.amount} />
- </span>
- ) : (
- <span style={{ color: "grey" }}>&lt;{i18n.str`invalid value`}&gt;</span>
- )}</dd>
-
- <dt class="sr-only sm:hidden"><i18n.Translate>Counterpart</i18n.Translate></dt>
- <dd class="mt-1 truncate text-gray-500 sm:hidden">
- {item.negative ? i18n.str`to` : i18n.str`from`} {item.counterpart}
- </dd>
- <dd class="mt-1 text-gray-500 sm:hidden" >
- <pre class="break-words w-56 whitespace-break-spaces p-2 rounded-md mx-auto my-2 bg-gray-100">
- {item.subject}
- </pre>
- </dd>
- </dl> */}
- </td>
- <td onClick={(e) => {
- e.preventDefault();
- onSelected(item.id);
- }} class="hidden sm:table-cell px-3 py-3.5 text-sm text-gray-500 cursor-pointer">{confirmationTime}</td>
- <td onClick={(e) => {
- e.preventDefault();
- onSelected(item.id);
- }} class="hidden sm:table-cell px-3 py-3.5 text-sm text-red-600 cursor-pointer"><RenderAmount value={Amounts.parseOrThrow(item.amount_debit)} spec={resp.body.regional_currency_specification} /></td>
- <td onClick={(e) => {
- e.preventDefault();
- onSelected(item.id);
- }} class="hidden sm:table-cell px-3 py-3.5 text-sm text-green-600 cursor-pointer"><RenderAmount value={Amounts.parseOrThrow(item.amount_credit)} spec={resp.body.fiat_currency_specification} /></td>
-
- <td onClick={(e) => {
- e.preventDefault();
- onSelected(item.id);
- }} class="hidden sm:table-cell px-3 py-3.5 text-sm text-gray-500 cursor-pointer">{item.status}</td>
- <td onClick={(e) => {
- e.preventDefault();
- onSelected(item.id);
- }} class="hidden sm:table-cell px-3 py-3.5 text-sm text-gray-500 break-all min-w-md">
- {item.subject}
- </td>
- </tr>)
- })}
- </Fragment>
-
- })}
- </tbody>
-
- </table>
-
-
- </div>
- </div>
- );
-
-}
diff --git a/packages/demobank-ui/src/components/EmptyComponentExample/index.ts b/packages/demobank-ui/src/components/EmptyComponentExample/index.ts
deleted file mode 100644
index d80e6bdf9..000000000
--- a/packages/demobank-ui/src/components/EmptyComponentExample/index.ts
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2022 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 { HookError, Loading, utils } from "@gnu-taler/web-util/browser";
-import { useComponentState } from "./state.js";
-import { LoadingUriView, ReadyView } from "./views.js";
-
-export interface Props {
- p: string;
-}
-
-export type State = State.Loading | State.LoadingUriError | State.Ready;
-
-export namespace State {
- export interface Loading {
- status: "loading";
- error: undefined;
- }
-
- export interface LoadingUriError {
- status: "loading-error";
- error: HookError;
- }
-
- export interface BaseInfo {
- error: undefined;
- }
- export interface Ready extends BaseInfo {
- status: "ready";
- error: undefined;
- }
-}
-
-const viewMapping: utils.StateViewMap<State> = {
- loading: Loading,
- "loading-error": LoadingUriView,
- ready: ReadyView,
-};
-
-export const ComponentName = utils.compose(
- (p: Props) => useComponentState(p),
- viewMapping,
-);
diff --git a/packages/demobank-ui/src/components/EmptyComponentExample/state.ts b/packages/demobank-ui/src/components/EmptyComponentExample/state.ts
deleted file mode 100644
index e147a7ccf..000000000
--- a/packages/demobank-ui/src/components/EmptyComponentExample/state.ts
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2022 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 { wxApi } from "../../wxApi.js";
-import { Props, State } from "./index.js";
-
-export function useComponentState({ p }: Props): State {
- return {
- status: "ready",
- error: undefined,
- };
-}
diff --git a/packages/demobank-ui/src/components/EmptyComponentExample/stories.tsx b/packages/demobank-ui/src/components/EmptyComponentExample/stories.tsx
deleted file mode 100644
index 628e97c02..000000000
--- a/packages/demobank-ui/src/components/EmptyComponentExample/stories.tsx
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2022 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 * as tests from "@gnu-taler/web-util/testing";
-import { ReadyView } from "./views.js";
-
-export default {
- title: "example",
-};
-
-export const Ready = tests.createExample(ReadyView, {});
diff --git a/packages/demobank-ui/src/components/EmptyComponentExample/test.ts b/packages/demobank-ui/src/components/EmptyComponentExample/test.ts
deleted file mode 100644
index eae4d4ca2..000000000
--- a/packages/demobank-ui/src/components/EmptyComponentExample/test.ts
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2022 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 { expect } from "chai";
-
-describe("test description", () => {
- it("should assert", () => {
- expect([]).deep.equals([]);
- });
-});
diff --git a/packages/demobank-ui/src/components/EmptyComponentExample/views.tsx b/packages/demobank-ui/src/components/EmptyComponentExample/views.tsx
deleted file mode 100644
index 5e0a5f0d2..000000000
--- a/packages/demobank-ui/src/components/EmptyComponentExample/views.tsx
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2022 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 { useTranslationContext } from "@gnu-taler/web-util/browser";
-import { State } from "./index.js";
-
-export function LoadingUriView({ error }: State.LoadingUriError): VNode {
- const { i18n } = useTranslationContext();
-
- return (
- <div>
- </div>
- );
-}
-
-export function ReadyView({ error }: State.Ready): VNode {
- const { i18n } = useTranslationContext();
-
- return <div />;
-}
diff --git a/packages/demobank-ui/src/components/ErrorLoadingWithDebug.tsx b/packages/demobank-ui/src/components/ErrorLoadingWithDebug.tsx
deleted file mode 100644
index 25e79e9e0..000000000
--- a/packages/demobank-ui/src/components/ErrorLoadingWithDebug.tsx
+++ /dev/null
@@ -1,9 +0,0 @@
-import { TalerError } from "@gnu-taler/taler-util";
-import { ErrorLoading } from "@gnu-taler/web-util/browser";
-import { VNode, h } from "preact";
-import { usePreferences } from "../hooks/preferences.js";
-
-export function ErrorLoadingWithDebug({ error }: { error: TalerError }): VNode {
- const [pref] = usePreferences();
- return <ErrorLoading error={error} showDetail={pref.showDebugInfo} />
-}
diff --git a/packages/demobank-ui/src/components/QR.tsx b/packages/demobank-ui/src/components/QR.tsx
deleted file mode 100644
index 945a08867..000000000
--- a/packages/demobank-ui/src/components/QR.tsx
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2022 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 { 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");
- qr.addData(text);
- qr.make();
- if (divRef.current)
- divRef.current.innerHTML = qr.createSvgTag({
- scalable: true,
- });
- });
-
- return (
- <div
- style={{
- display: "flex",
- flexDirection: "column",
- alignItems: "left",
- }}
- >
- <div
- style={{
- width: "100%",
- marginRight: "auto",
- marginLeft: "auto",
- }}
- ref={divRef}
- />
- </div>
- );
-}
diff --git a/packages/demobank-ui/src/components/Transactions/index.ts b/packages/demobank-ui/src/components/Transactions/index.ts
deleted file mode 100644
index b6a78deb1..000000000
--- a/packages/demobank-ui/src/components/Transactions/index.ts
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2022 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 { AbsoluteTime, AmountJson, TalerError } from "@gnu-taler/taler-util";
-import { Loading, utils } from "@gnu-taler/web-util/browser";
-import { ErrorLoadingWithDebug } from "../ErrorLoadingWithDebug.js";
-import { useComponentState } from "./state.js";
-import { ReadyView } from "./views.js";
-
-export interface Props {
- account: string;
-}
-
-export type State = State.Loading | State.LoadingUriError | State.Ready;
-
-export namespace State {
- export interface Loading {
- status: "loading";
- error: undefined;
- }
-
- export interface LoadingUriError {
- status: "loading-error";
- error: TalerError;
- }
-
- export interface BaseInfo {
- error: undefined;
- }
- export interface Ready extends BaseInfo {
- status: "ready";
- error: undefined;
- transactions: Transaction[];
- onPrev?: () => void;
- onNext?: () => void;
- }
-}
-
-export interface Transaction {
- negative: boolean;
- counterpart: string;
- when: AbsoluteTime;
- amount: AmountJson | undefined;
- subject: string;
-}
-
-const viewMapping: utils.StateViewMap<State> = {
- loading: Loading,
- "loading-error": ErrorLoadingWithDebug,
- ready: ReadyView,
-};
-
-export const Transactions = utils.compose(
- (p: Props) => useComponentState(p),
- viewMapping,
-);
diff --git a/packages/demobank-ui/src/components/Transactions/state.ts b/packages/demobank-ui/src/components/Transactions/state.ts
deleted file mode 100644
index c85fba85b..000000000
--- a/packages/demobank-ui/src/components/Transactions/state.ts
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2022 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 { AbsoluteTime, Amounts, TalerError, parsePaytoUri } from "@gnu-taler/taler-util";
-import { useTransactions } from "../../hooks/access.js";
-import { Props, State, Transaction } from "./index.js";
-
-export function useComponentState({ account }: Props): State {
- const result = useTransactions(account);
- if (!result) {
- return {
- status: "loading",
- error: undefined,
- };
- }
- if (result instanceof TalerError) {
- return {
- status: "loading-error",
- error: result,
- };
- }
-
- const transactions = result.data.type === "fail" ? [] : result.data.body.transactions
- .map((tx) => {
-
- const negative = tx.direction === "debit";
- const cp = parsePaytoUri(negative ? tx.creditor_payto_uri : tx.debtor_payto_uri);
- const counterpart = (cp === undefined || !cp.isKnown ? undefined :
- cp.targetType === "iban" ? cp.iban :
- cp.targetType === "x-taler-bank" ? cp.account :
- cp.targetType === "bitcoin" ? `${cp.targetPath.substring(0, 6)}...` : undefined) ??
- "unkown";
-
- const when = AbsoluteTime.fromProtocolTimestamp(tx.date);
- const amount = Amounts.parse(tx.amount);
- const subject = tx.subject;
- return {
- negative,
- counterpart,
- when,
- amount,
- subject,
- };
- })
- .filter((x): x is Transaction => x !== undefined);
-
- return {
- status: "ready",
- error: undefined,
- transactions,
- onNext: result.isLastPage ? undefined : result.loadMore,
- onPrev: result.isFirstPage ? undefined : result.loadMorePrev,
- };
-}
diff --git a/packages/demobank-ui/src/components/Transactions/stories.tsx b/packages/demobank-ui/src/components/Transactions/stories.tsx
deleted file mode 100644
index 17e234cc7..000000000
--- a/packages/demobank-ui/src/components/Transactions/stories.tsx
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2022 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 * as tests from "@gnu-taler/web-util/testing";
-import { ReadyView } from "./views.js";
-import { AbsoluteTime } from "@gnu-taler/taler-util";
-
-export default {
- title: "transaction list",
-};
-
-export const Ready = tests.createExample(ReadyView, {
- transactions: [
- {
- amount: {
- currency: "USD",
- fraction: 0,
- value: 1,
- },
- counterpart: "ASD",
- negative: false,
- subject: "Some",
- when: AbsoluteTime.now(),
- },
- ],
-});
diff --git a/packages/demobank-ui/src/components/Transactions/test.ts b/packages/demobank-ui/src/components/Transactions/test.ts
deleted file mode 100644
index ed20b8369..000000000
--- a/packages/demobank-ui/src/components/Transactions/test.ts
+++ /dev/null
@@ -1,198 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2022 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 { ErrorType } from "@gnu-taler/web-util/browser";
-import * as tests from "@gnu-taler/web-util/testing";
-import { SwrMockEnvironment } from "@gnu-taler/web-util/testing";
-import { expect } from "chai";
-import { TRANSACTION_API_EXAMPLE } from "../../endpoints.js";
-import { Props } from "./index.js";
-import { useComponentState } from "./state.js";
-import { HttpStatusCode, TalerError, TalerErrorCode } from "@gnu-taler/taler-util";
-
-describe("Transaction states", () => {
- it.skip("should query backend and render transactions", async () => {
- const env = new SwrMockEnvironment();
-
- const props: Props = {
- account: "myAccount",
- };
-
- //@ts-ignore
- env.addRequestExpectation(TRANSACTION_API_EXAMPLE.LIST_FIRST_PAGE, {
- response: {
- data: {
- transactions: [
- {
- creditorIban: "DE159593",
- creditorBic: "SANDBOXX",
- creditorName: "exchange company",
- debtorIban: "DE118695",
- debtorBic: "SANDBOXX",
- debtorName: "Name unknown",
- amount: "1",
- currency: "KUDOS",
- subject:
- "Taler Withdrawal N588V8XE9TR49HKAXFQ20P0EQ0EYW2AC9NNANV8ZP5P59N6N0410",
- date: "2022-12-12Z",
- uid: "8PPFR9EM",
- direction: "DBIT",
- pmtInfId: null,
- msgId: null,
- },
- {
- creditorIban: "DE159593",
- creditorBic: "SANDBOXX",
- creditorName: "exchange company",
- debtorIban: "DE118695",
- debtorBic: "SANDBOXX",
- debtorName: "Name unknown",
- amount: "5.00",
- currency: "KUDOS",
- subject: "HNEWWT679TQC5P1BVXJS48FX9NW18FWM6PTK2N80Z8GVT0ACGNK0",
- date: "2022-12-07Z",
- uid: "7FZJC3RJ",
- direction: "DBIT",
- pmtInfId: null,
- msgId: null,
- },
- {
- creditorIban: "DE118695",
- creditorBic: "SANDBOXX",
- creditorName: "Name unknown",
- debtorIban: "DE579516",
- debtorBic: "SANDBOXX",
- debtorName: "The Bank",
- amount: "100",
- currency: "KUDOS",
- subject: "Sign-up bonus",
- date: "2022-12-07Z",
- uid: "I31A06J8",
- direction: "CRDT",
- pmtInfId: null,
- msgId: null,
- },
- ],
- },
- },
- });
-
- const hookBehavior = await tests.hookBehaveLikeThis(
- useComponentState,
- props,
- [
- ({ status, error }) => {
- expect(status).equals("loading");
- expect(error).undefined;
- },
- ({ status, error }) => {
- expect(status).equals("ready");
- expect(error).undefined;
- },
- ],
- env.buildTestingContext(),
- );
-
- expect(hookBehavior).deep.eq({ result: "ok" });
-
- expect(env.assertJustExpectedRequestWereMade()).deep.eq({ result: "ok" });
- });
-
- // it("should show error message on not found", async () => {
- // const env = new SwrMockEnvironment();
-
- // const props: Props = {
- // account: "myAccount",
- // };
-
- // env.addRequestExpectation(TRANSACTION_API_EXAMPLE.LIST_NOT_FOUND, {
- // response: {
- // error: {
- // description: "Transaction page 0 could not be retrieved.",
- // },
- // },
- // });
-
- // const hookBehavior = await tests.hookBehaveLikeThis(
- // useComponentState,
- // props,
- // [
- // ({ status, error }) => {
- // expect(status).equals("loading");
- // expect(error).undefined;
- // },
- // ({ status, error }) => {
- // expect(status).equals("loading-error");
- // if (error === undefined || error.type !== ErrorType.CLIENT) {
- // throw Error("not the expected error");
- // }
- // expect(error.payload).deep.equal({
- // error: {
- // description: "Transaction page 0 could not be retrieved.",
- // },
- // });
- // },
- // ],
- // env.buildTestingContext(),
- // );
-
- // expect(hookBehavior).deep.eq({ result: "ok" });
- // expect(env.assertJustExpectedRequestWereMade()).deep.eq({ result: "ok" });
- // });
-
- it.skip("should show error message on server error", async () => {
- const env = new SwrMockEnvironment();
-
- const props: Props = {
- account: "myAccount",
- };
-
- env.addRequestExpectation(TRANSACTION_API_EXAMPLE.LIST_ERROR, {
- response: {
- error: {
- code: TalerErrorCode.WALLET_HTTP_REQUEST_THROTTLED,
- },
- },
- });
-
- const hookBehavior = await tests.hookBehaveLikeThis(
- useComponentState,
- props,
- [
- ({ status, error }) => {
- expect(status).equals("loading");
- expect(error).undefined;
- },
- ({ status, error }) => {
- expect(status).equals("loading-error");
- if (error === undefined || !error.hasErrorCode(TalerErrorCode.WALLET_HTTP_REQUEST_THROTTLED)) {
- throw Error("not the expected error");
- }
- expect(error.errorDetail.code).deep.equal(TalerErrorCode.WALLET_HTTP_REQUEST_THROTTLED);
- },
- ],
- env.buildTestingContext(),
- );
-
- expect(hookBehavior).deep.eq({ result: "ok" });
- expect(env.assertJustExpectedRequestWereMade()).deep.eq({ result: "ok" });
- });
-});
diff --git a/packages/demobank-ui/src/components/Transactions/views.tsx b/packages/demobank-ui/src/components/Transactions/views.tsx
deleted file mode 100644
index 8a8a8f72e..000000000
--- a/packages/demobank-ui/src/components/Transactions/views.tsx
+++ /dev/null
@@ -1,136 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2022 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 { useTranslationContext } from "@gnu-taler/web-util/browser";
-import { format } from "date-fns";
-import { Fragment, h, VNode } from "preact";
-import { useBankCoreApiContext } from "../../context/config.js";
-import { RenderAmount } from "../../pages/PaytoWireTransferForm.js";
-import { State } from "./index.js";
-
-
-export function ReadyView({ transactions, onNext, onPrev }: State.Ready): VNode {
- const { i18n } = useTranslationContext();
- const { config } = useBankCoreApiContext();
- if (!transactions.length) return <div />
- const txByDate = transactions.reduce((prev, cur) => {
- const d = cur.when.t_ms === "never"
- ? ""
- : format(cur.when.t_ms, "dd/MM/yyyy")
- if (!prev[d]) {
- prev[d] = []
- }
- prev[d].push(cur)
- return prev
- }, {} as Record<string, typeof transactions>)
- return (
- <div class="px-4 mt-4">
- <div class="sm:flex sm:items-center">
- <div class="sm:flex-auto">
- <h1 class="text-base font-semibold leading-6 text-gray-900"><i18n.Translate>Latest transactions</i18n.Translate></h1>
- </div>
- </div>
- <div class="-mx-4 mt-5 ring-1 ring-gray-300 sm:mx-0 rounded-lg min-w-fit bg-white">
- <table class="min-w-full divide-y divide-gray-300">
- <thead>
- <tr>
- <th scope="col" class="pl-2 py-3.5 text-left text-sm font-semibold text-gray-900 ">{i18n.str`Date`}</th>
- <th scope="col" class="hidden sm:table-cell pl-2 py-3.5 text-left text-sm font-semibold text-gray-900 ">{i18n.str`Amount`}</th>
- <th scope="col" class="hidden sm:table-cell pl-2 py-3.5 text-left text-sm font-semibold text-gray-900 ">{i18n.str`Counterpart`}</th>
- <th scope="col" class="hidden sm:table-cell pl-2 py-3.5 text-left text-sm font-semibold text-gray-900 ">{i18n.str`Subject`}</th>
- </tr>
- </thead>
- <tbody>
- {Object.entries(txByDate).map(([date, txs], idx) => {
- return <Fragment>
- <tr class="border-t border-gray-200">
- <th colSpan={4} scope="colgroup" class="bg-gray-50 py-2 pl-4 pr-3 text-left text-sm font-semibold text-gray-900 sm:pl-3">
- {date}
- </th>
- </tr>
- {txs.map(item => {
- const time = item.when.t_ms === "never" ? "" : format(item.when.t_ms, "HH:mm:ss")
- return (<tr key={idx} class="border-b border-gray-200 last:border-none">
- <td class="relative py-2 pl-2 pr-2 text-sm ">
- <div class="font-medium text-gray-900">{time}</div>
- <dl class="font-normal sm:hidden">
- <dt class="sr-only sm:hidden"><i18n.Translate>Amount</i18n.Translate></dt>
- <dd class="mt-1 truncate text-gray-700">
- {item.negative ? i18n.str`sent` : i18n.str`received`} {item.amount ? (
- <span data-negative={item.negative ? "true" : "false"} class="data-[negative=false]:text-green-600 data-[negative=true]:text-red-600">
- <RenderAmount value={item.amount} spec={config.currency_specification} />
- </span>
- ) : (
- <span style={{ color: "grey" }}>&lt;{i18n.str`invalid value`}&gt;</span>
- )}</dd>
-
- <dt class="sr-only sm:hidden"><i18n.Translate>Counterpart</i18n.Translate></dt>
- <dd class="mt-1 truncate text-gray-500 sm:hidden">
- {item.negative ? i18n.str`to` : i18n.str`from`} <a href={`#/wire-transfer/${item.counterpart}`} class="text-indigo-600 hover:text-indigo-900">
- {item.counterpart}
- </a>
- </dd>
- <dd class="mt-1 text-gray-500 sm:hidden" >
- <pre class="break-words w-56 whitespace-break-spaces p-2 rounded-md mx-auto my-2 bg-gray-100">
- {item.subject}
- </pre>
- </dd>
- </dl>
- </td>
- <td data-negative={item.negative ? "true" : "false"}
- class="hidden sm:table-cell px-3 py-3.5 text-sm text-gray-500 ">
- {item.amount ? (<RenderAmount value={item.amount} negative={item.negative} withColor spec={config.currency_specification} />
- ) : (
- <span style={{ color: "grey" }}>&lt;{i18n.str`invalid value`}&gt;</span>
- )}
- </td>
- <td class="hidden sm:table-cell px-3 py-3.5 text-sm text-gray-500">
- <a href={`#/wire-transfer/${item.counterpart}`} class="text-indigo-600 hover:text-indigo-900">
- {item.counterpart}
- </a>
- </td>
- <td class="hidden sm:table-cell px-3 py-3.5 text-sm text-gray-500 break-all min-w-md">{item.subject}</td>
- </tr>)
- })}
- </Fragment>
-
- })}
- </tbody>
-
- </table>
-
- <nav class="flex items-center justify-between border-t border-gray-200 bg-white px-4 py-3 sm:px-6 rounded-lg" aria-label="Pagination">
- <div class="flex flex-1 justify-between sm:justify-end">
- <button
- class="relative disabled:bg-gray-100 disabled:text-gray-500 inline-flex items-center rounded-md bg-white px-3 py-2 text-sm font-semibold text-gray-900 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus-visible:outline-offset-0"
- disabled={!onPrev}
- onClick={onPrev}
- >
- <i18n.Translate>First page</i18n.Translate>
- </button>
- <button
- class="relative disabled:bg-gray-100 disabled:text-gray-500 ml-3 inline-flex items-center rounded-md bg-white px-3 py-2 text-sm font-semibold text-gray-900 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus-visible:outline-offset-0"
- disabled={!onNext}
- onClick={onNext}
- >
- <i18n.Translate>Next</i18n.Translate>
- </button>
- </div>
- </nav>
- </div>
- </div>
- );
-}
diff --git a/packages/demobank-ui/src/components/app.tsx b/packages/demobank-ui/src/components/app.tsx
deleted file mode 100644
index 4921b6bff..000000000
--- a/packages/demobank-ui/src/components/app.tsx
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2022 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 {
- canonicalizeBaseUrl,
- getGlobalLogLevel,
- setGlobalLogLevelFromString
-} from "@gnu-taler/taler-util";
-import { Loading, TranslationProvider } from "@gnu-taler/web-util/browser";
-import { Fragment, FunctionalComponent, h } from "preact";
-import { SWRConfig } from "swr";
-import { BackendStateProvider } from "../context/backend.js";
-import { BankCoreApiProvider } from "../context/config.js";
-import { strings } from "../i18n/strings.js";
-import { BankUiSettings, fetchSettings } from "../settings.js";
-import { Routing } from "../Routing.js";
-import { BankFrame } from "../pages/BankFrame.js";
-import { useEffect, useState } from "preact/hooks";
-import { SettingsProvider } from "../context/settings.js";
-const WITH_LOCAL_STORAGE_CACHE = false;
-
-const App: FunctionalComponent = () => {
- const [settings, setSettings] = useState<BankUiSettings>()
- useEffect(() => {
- fetchSettings(setSettings)
- }, [])
- if (!settings) return <Loading />;
-
- const baseUrl = getInitialBackendBaseURL(settings.backendBaseURL);
- return (
- <SettingsProvider value={settings}>
- <TranslationProvider source={strings}>
- <BackendStateProvider>
- <BankCoreApiProvider baseUrl={baseUrl} frameOnError={BankFrame}>
- <SWRConfig
- value={{
- provider: WITH_LOCAL_STORAGE_CACHE
- ? localStorageProvider
- : undefined,
- }}
- >
- <Routing />
- </SWRConfig>
- </BankCoreApiProvider>
- </BackendStateProvider>
- </TranslationProvider >
- </SettingsProvider>
- );
-};
-
-(window as any).setGlobalLogLevelFromString = setGlobalLogLevelFromString;
-(window as any).getGlobalLevel = getGlobalLogLevel;
-
-function localStorageProvider(): Map<unknown, unknown> {
- const map = new Map(JSON.parse(localStorage.getItem("app-cache") || "[]"));
-
- window.addEventListener("beforeunload", () => {
- const appCache = JSON.stringify(Array.from(map.entries()));
- localStorage.setItem("app-cache", appCache);
- });
- return map;
-}
-
-export default App;
-
-function getInitialBackendBaseURL(backendFromSettings: string | undefined): string {
- const overrideUrl =
- typeof localStorage !== "undefined"
- ? localStorage.getItem("corebank-api-base-url")
- : undefined;
- let result: string;
-
- if (!overrideUrl) {
- //normal path
- if (!backendFromSettings) {
- console.error(
- "ERROR: backendBaseURL was overridden by a setting file and missing. Setting value to 'window.origin'",
- );
- result = window.origin
- } else {
- result = backendFromSettings;
- }
- } else {
- // testing/development path
- result = overrideUrl
- }
- try {
- return canonicalizeBaseUrl(result)
- } catch (e) {
- //fall back
- return canonicalizeBaseUrl(window.origin)
- }
-} \ No newline at end of file
diff --git a/packages/demobank-ui/src/components/index.examples.ts b/packages/demobank-ui/src/components/index.examples.ts
deleted file mode 100644
index 348d5c653..000000000
--- a/packages/demobank-ui/src/components/index.examples.ts
+++ /dev/null
@@ -1,17 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2022 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 tx from "./Transactions/stories.js";
diff --git a/packages/demobank-ui/src/context/backend.ts b/packages/demobank-ui/src/context/backend.ts
deleted file mode 100644
index eae187c6d..000000000
--- a/packages/demobank-ui/src/context/backend.ts
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2022 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 { ComponentChildren, createContext, h, VNode } from "preact";
-import { useContext } from "preact/hooks";
-import {
- BackendStateHandler,
- defaultState,
- useBackendState,
-} from "../hooks/backend.js";
-
-/**
- *
- * @author Sebastian Javier Marchano (sebasjm)
- */
-
-export type Type = BackendStateHandler;
-
-const initial: Type = {
- state: defaultState,
- logOut() {
- null;
- },
- expired() {
- null;
- },
- logIn(info) {
- null;
- },
-};
-const Context = createContext<Type>(initial);
-
-export const useBackendContext = (): Type => useContext(Context);
-
-export const BackendStateProvider = ({
- children,
-}: {
- children: ComponentChildren;
-}): VNode => {
- const value = useBackendState();
-
- return h(Context.Provider, {
- value,
- children,
- });
-};
-
-export const BackendStateProviderTesting = ({
- children,
- state,
-}: {
- children: ComponentChildren;
- state: typeof defaultState;
-}): VNode => {
- const value: BackendStateHandler = {
- state,
- logIn: () => {},
- expired: () => {},
- logOut: () => {},
- };
-
- return h(Context.Provider, {
- value,
- children,
- });
-};
diff --git a/packages/demobank-ui/src/context/config.ts b/packages/demobank-ui/src/context/config.ts
deleted file mode 100644
index 9908c73a2..000000000
--- a/packages/demobank-ui/src/context/config.ts
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2022 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 { TalerCorebankApi, TalerCoreBankHttpClient, TalerError } from "@gnu-taler/taler-util";
-import { BrowserHttpLib, ErrorLoading, useTranslationContext } from "@gnu-taler/web-util/browser";
-import { ComponentChildren, createContext, FunctionComponent, h, VNode } from "preact";
-import { useContext, useEffect, useState } from "preact/hooks";
-
-/**
- *
- * @author Sebastian Javier Marchano (sebasjm)
- */
-
-export type Type = {
- url: URL,
- config: TalerCorebankApi.Config,
- api: TalerCoreBankHttpClient,
-};
-
-const Context = createContext<Type>(undefined as any);
-
-export const useBankCoreApiContext = (): Type => useContext(Context);
-
-export type ConfigResult = undefined
- | { type: "ok", config: TalerCorebankApi.Config }
- | { type: "incompatible", result: TalerCorebankApi.Config, supported: string }
- | { type: "error", error: TalerError }
-
-export const BankCoreApiProvider = ({
- baseUrl,
- children,
- frameOnError,
-}: {
- baseUrl: string,
- children: ComponentChildren;
- frameOnError: FunctionComponent<{ children: ComponentChildren }>,
-}): VNode => {
- const [checked, setChecked] = useState<ConfigResult>()
- const { i18n } = useTranslationContext();
- const url = new URL(baseUrl)
- const api = new TalerCoreBankHttpClient(url.href, new BrowserHttpLib())
- useEffect(() => {
- api.getConfig()
- .then((resp) => {
- if (api.isCompatible(resp.body.version)) {
- setChecked({ type: "ok", config: resp.body });
- } else {
- setChecked({ type: "incompatible", result: resp.body, supported: api.PROTOCOL_VERSION })
- }
- })
- .catch((error: unknown) => {
- if (error instanceof TalerError) {
- setChecked({ type: "error", error });
- }
- });
- }, []);
-
- if (checked === undefined) {
- return h(frameOnError, { children: h("div", {}, "loading...") })
- }
- if (checked.type === "error") {
- return h(frameOnError, { children: h(ErrorLoading, { error: checked.error, showDetail: true }) })
- }
- if (checked.type === "incompatible") {
- return h(frameOnError, { children: h("div", {}, i18n.str`the bank backend is not supported. supported version "${checked.supported}", server version "${checked.result.version}"`) })
- }
- const value: Type = {
- url, config: checked.config, api
- }
- return h(Context.Provider, {
- value,
- children,
- });
-};
-
-export const BankCoreApiProviderTesting = ({
- children,
- state,
- url,
-}: {
- children: ComponentChildren;
- state: TalerCorebankApi.Config;
- url: string,
-}): VNode => {
- const value: Type = {
- url: new URL(url),
- config: state,
- api: undefined as any,
- };
-
- return h(Context.Provider, {
- value,
- children,
- });
-};
diff --git a/packages/demobank-ui/src/context/settings.ts b/packages/demobank-ui/src/context/settings.ts
deleted file mode 100644
index a14c14d15..000000000
--- a/packages/demobank-ui/src/context/settings.ts
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2022 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 { ComponentChildren, createContext, h, VNode } from "preact";
-import { useContext } from "preact/hooks";
-import { BankUiSettings } from "../settings.js";
-
-/**
- *
- * @author Sebastian Javier Marchano (sebasjm)
- */
-
-export type Type = BankUiSettings;
-
-const initial: BankUiSettings = {};
-const Context = createContext<Type>(initial);
-
-export const useSettingsContext = (): Type => useContext(Context);
-
-export const SettingsProvider = ({
- children,
- value,
-}: {
- value: BankUiSettings,
- children: ComponentChildren;
-}): VNode => {
- return h(Context.Provider, {
- value,
- children,
- });
-};
diff --git a/packages/demobank-ui/src/declaration.d.ts b/packages/demobank-ui/src/declaration.d.ts
deleted file mode 100644
index c8ba3d576..000000000
--- a/packages/demobank-ui/src/declaration.d.ts
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2022 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 "*.css" {
- const mapping: Record<string, string>;
- export default mapping;
-}
-declare module "*.svg" {
- const content: any;
- export default content;
-}
-declare module "*.jpeg" {
- const content: any;
- export default content;
-}
-declare module "*.png" {
- const content: any;
- export default content;
-}
-
-declare const __VERSION__: string;
-declare const __GIT_HASH__: string;
diff --git a/packages/demobank-ui/src/endpoints.ts b/packages/demobank-ui/src/endpoints.ts
deleted file mode 100644
index b28c76613..000000000
--- a/packages/demobank-ui/src/endpoints.ts
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2022 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)
- */
-
-export const TRANSACTION_API_EXAMPLE = {
- LIST_FIRST_PAGE: {
- method: "get" as const,
- url: '["access-api/accounts/myAccount/transactions",null,20]',
- },
- LIST_ERROR: {
- method: "get" as const,
- url: '["access-api/accounts/myAccount/transactions",null,20]',
- code: 500,
- },
- LIST_NOT_FOUND: {
- method: "get" as const,
- url: '["access-api/accounts/myAccount/transactions",null,20]',
- code: 404,
- },
-};
-
-export const CASHOUT_API_EXAMPLE = {
- LIST_FIRST_PAGE: {
- method: "get" as const,
- url: '["circuit-api/cashouts","123"]',
- },
- MULTI_GET_EMPTY_FIRST_PAGE: {
- method: "get" as const,
- url: "[[]]",
- },
-};
diff --git a/packages/demobank-ui/src/forms/simplest.ts b/packages/demobank-ui/src/forms/simplest.ts
deleted file mode 100644
index 54b6b1c65..000000000
--- a/packages/demobank-ui/src/forms/simplest.ts
+++ /dev/null
@@ -1,66 +0,0 @@
-import {
- AbsoluteTime,
- AmountJson,
- TranslatedString
-} from "@gnu-taler/taler-util";
-import { DoubleColumnForm, FormState } from "@gnu-taler/web-util/browser";
-
-export namespace Data {
- export interface WithResolution {
- when: AbsoluteTime;
- threshold: AmountJson;
- state: string;
- }
- export interface Form extends WithResolution {
- comment: string;
- }
-}
-
-const design: DoubleColumnForm = [
- {
- title: "Simple form" as TranslatedString,
- fields: [
- {
- type: "textArea",
- props: {
- name: "comment",
- label: "Comments" as TranslatedString,
- },
- },
- ],
- },
- {
- title: "Resolution" as TranslatedString,
- description: `Current state is and threshold at ` as TranslatedString,
- fields: [
- {
- type: "date",
- props: {
- name: "when",
- label: "Decision Time" as TranslatedString,
- },
- },
- {
- type: "amount",
- props: {
- name: "threshold",
- label: "New threshold" as TranslatedString,
- },
- },
- ],
- }
- ,
-];
-
-function formBehavior(v: Partial<Data.Form>): FormState<Data.Form> {
- return {
- when: {
- disabled: true,
- },
- threshold: {
- // disabled: v.state === AmlExchangeBackend.AmlState.frozen,
- },
- };
-}
-
-
diff --git a/packages/demobank-ui/src/hooks/access.ts b/packages/demobank-ui/src/hooks/access.ts
deleted file mode 100644
index fc1cff129..000000000
--- a/packages/demobank-ui/src/hooks/access.ts
+++ /dev/null
@@ -1,233 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2022 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 { AccessToken, TalerBankIntegrationResultByMethod, TalerCoreBankResultByMethod, TalerHttpError, WithdrawalOperationStatus } from "@gnu-taler/taler-util";
-import { useEffect, useState } from "preact/hooks";
-import { MAX_RESULT_SIZE, PAGE_SIZE } from "../utils.js";
-import { useBackendState } from "./backend.js";
-
-// FIX default import https://github.com/microsoft/TypeScript/issues/49189
-import _useSWR, { SWRHook } from "swr";
-import { useBankCoreApiContext } from "../context/config.js";
-const useSWR = _useSWR as unknown as SWRHook;
-
-
-export interface InstanceTemplateFilter {
- //FIXME: add filter to the template list
- position?: string;
-}
-
-export function useAccountDetails(account: string) {
- const { state: credentials } = useBackendState();
- const { api } = useBankCoreApiContext();
-
- async function fetcher([username, token]: [string, AccessToken]) {
- return await api.getAccount({ username, token })
- }
- const token = credentials.status !== "loggedIn" ? undefined : credentials.token
- const { data, error } = useSWR<TalerCoreBankResultByMethod<"getAccount">, TalerHttpError>(
- [account, token, "getAccount"], fetcher, {
- refreshInterval: 0,
- refreshWhenHidden: false,
- revalidateOnFocus: false,
- revalidateOnReconnect: false,
- refreshWhenOffline: false,
- errorRetryCount: 0,
- errorRetryInterval: 1,
- shouldRetryOnError: false,
- keepPreviousData: true,
- });
-
- if (data) return data
- if (error) return error;
- return undefined;
-}
-
-export function useWithdrawalDetails(wid: string) {
- const { api } = useBankCoreApiContext();
- const [latestStatus, setLatestStatus] = useState<WithdrawalOperationStatus>()
-
- async function fetcher([wid, old_state]: [string, WithdrawalOperationStatus | undefined]) {
- return await api.getWithdrawalById(wid, old_state === undefined ? undefined : { old_state, timeoutMs: 15000 })
- }
-
- const { data, error } = useSWR<TalerCoreBankResultByMethod<"getWithdrawalById">, TalerHttpError>(
- [wid, latestStatus, "getWithdrawalById"], fetcher, {
- refreshInterval: 3000,
- refreshWhenHidden: false,
- revalidateOnFocus: false,
- revalidateOnReconnect: false,
- refreshWhenOffline: false,
- errorRetryCount: 0,
- errorRetryInterval: 1,
- shouldRetryOnError: false,
- keepPreviousData: true,
- });
-
- const currentStatus = data !== undefined && data.type === "ok" ? data.body.status : undefined;
-
- useEffect(() => {
- if (currentStatus !== undefined && currentStatus !== latestStatus) {
- setLatestStatus(currentStatus)
- }
- }, [currentStatus])
-
- if (data) return data;
- if (error) return error;
- return undefined;
-}
-
-export function useTransactionDetails(account: string, tid: number) {
- const { state: credentials } = useBackendState();
- const token = credentials.status !== "loggedIn" ? undefined : credentials.token
- const { api } = useBankCoreApiContext();
-
- async function fetcher([username, token, txid]: [string, AccessToken, number]) {
- return await api.getTransactionById({ username, token }, txid)
- }
-
- const { data, error } = useSWR<TalerCoreBankResultByMethod<"getTransactionById">, TalerHttpError>(
- [account, token, tid, "getTransactionById"], fetcher, {
- refreshInterval: 0,
- refreshWhenHidden: false,
- revalidateOnFocus: false,
- revalidateOnReconnect: false,
- refreshWhenOffline: false,
- errorRetryCount: 0,
- errorRetryInterval: 1,
- shouldRetryOnError: false,
- keepPreviousData: true,
- });
-
- if (data) return data;
- if (error) return error;
- return undefined;
-}
-
-export function usePublicAccounts(filterAccount: string | undefined, initial?: number) {
- const [offset, setOffset] = useState<number | undefined>(initial);
- const { api } = useBankCoreApiContext();
-
- async function fetcher([account, txid]: [string | undefined, number | undefined]) {
- return await api.getPublicAccounts({ account }, {
- limit: MAX_RESULT_SIZE,
- offset: txid ? String(txid) : undefined,
- order: "asc"
- })
- }
-
- const { data, error } = useSWR<TalerCoreBankResultByMethod<"getPublicAccounts">, TalerHttpError>(
- [filterAccount, offset, "getPublicAccounts"], fetcher, {
- refreshInterval: 0,
- refreshWhenHidden: false,
- revalidateOnFocus: false,
- revalidateOnReconnect: false,
- refreshWhenOffline: false,
- errorRetryCount: 0,
- errorRetryInterval: 1,
- shouldRetryOnError: false,
- keepPreviousData: true,
- });
-
- const isLastPage =
- data && data.body.public_accounts.length < PAGE_SIZE;
- const isFirstPage = !initial;
-
- const pagination = {
- isLastPage,
- isFirstPage,
- loadMore: () => {
- if (isLastPage || data?.type !== "ok") return;
- const list = data.body.public_accounts
- if (list.length < MAX_RESULT_SIZE) {
- // setOffset(list[list.length-1].account_name);
- }
- },
- loadMorePrev: () => {
- null;
- },
- };
-
- // const public_accountslist = data?.type !== "ok" ? [] : data.body.public_accounts;
- if (data) {
- return { ok: true, data: data.body, ...pagination }
- }
- if (error) {
- return error;
- }
- return undefined;
-}
-
-/**
-
- * @param account
- * @param args
- * @returns
- */
-export function useTransactions(account: string, initial?: number) {
- const { state: credentials } = useBackendState();
- const token = credentials.status !== "loggedIn" ? undefined : credentials.token
-
- const [offset, setOffset] = useState<number | undefined>(initial);
- const { api } = useBankCoreApiContext();
-
- async function fetcher([username, token, txid]: [string, AccessToken, number | undefined]) {
- return await api.getTransactions({ username, token }, {
- limit: MAX_RESULT_SIZE,
- offset: txid ? String(txid) : undefined,
- order: "dec"
- })
- }
-
- const { data, error } = useSWR<TalerCoreBankResultByMethod<"getTransactions">, TalerHttpError>(
- [account, token, offset, "getTransactions"], fetcher, {
- refreshInterval: 0,
- refreshWhenHidden: false,
- refreshWhenOffline: false,
- // revalidateOnMount: false,
- revalidateIfStale: false,
- revalidateOnFocus: false,
- revalidateOnReconnect: false,
- }
- );
-
- const isLastPage =
- data && data.type === "ok" && data.body.transactions.length < PAGE_SIZE;
- const isFirstPage = true;
-
- const pagination = {
- isLastPage,
- isFirstPage,
- loadMore: () => {
- if (isLastPage || data?.type !== "ok") return;
- const list = data.body.transactions
- if (list.length < MAX_RESULT_SIZE) {
- setOffset(list[list.length - 1].row_id);
- }
- },
- loadMorePrev: () => {
- null;
- },
- };
-
- if (data) {
- return { ok: true, data, ...pagination }
- }
- if (error) {
- return error;
- }
- return undefined;
-}
diff --git a/packages/demobank-ui/src/hooks/async.ts b/packages/demobank-ui/src/hooks/async.ts
deleted file mode 100644
index b968cfb84..000000000
--- a/packages/demobank-ui/src/hooks/async.ts
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2022 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 { useState } from "preact/hooks";
-// import { cancelPendingRequest } from "./backend";
-
-export interface Options {
- slowTolerance: number;
-}
-
-export interface AsyncOperationApi<T> {
- request: (...a: any) => void;
- cancel: () => void;
- data: T | undefined;
- isSlow: boolean;
- isLoading: boolean;
- error: string | undefined;
-}
-
-export function useAsync<T>(
- fn?: (...args: any) => Promise<T>,
- { slowTolerance: tooLong }: Options = { slowTolerance: 1000 },
-): AsyncOperationApi<T> {
- const [data, setData] = useState<T | undefined>(undefined);
- const [isLoading, setLoading] = useState<boolean>(false);
- const [error, setError] = useState<any>(undefined);
- const [isSlow, setSlow] = useState(false);
-
- const request = async (...args: any) => {
- if (!fn) return;
- setLoading(true);
- const handler = setTimeout(() => {
- setSlow(true);
- }, tooLong);
-
- try {
- const result = await fn(...args);
- setData(result);
- } catch (error) {
- setError(error);
- }
- setLoading(false);
- setSlow(false);
- clearTimeout(handler);
- };
-
- function cancel() {
- setLoading(false);
- setSlow(false);
- }
-
- return {
- request,
- cancel,
- data,
- isSlow,
- isLoading,
- error,
- };
-}
diff --git a/packages/demobank-ui/src/hooks/backend.ts b/packages/demobank-ui/src/hooks/backend.ts
deleted file mode 100644
index 863b47bf3..000000000
--- a/packages/demobank-ui/src/hooks/backend.ts
+++ /dev/null
@@ -1,157 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2022 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 {
- AccessToken,
- Codec,
- buildCodecForObject,
- buildCodecForUnion,
- canonicalizeBaseUrl,
- codecForBoolean,
- codecForConstString,
- codecForString,
-} from "@gnu-taler/taler-util";
-import {
- buildStorageKey,
- useLocalStorage
-} from "@gnu-taler/web-util/browser";
-import { useSWRConfig } from "swr";
-
-/**
- * Has the information to reach and
- * authenticate at the bank's backend.
- */
-export type BackendState = LoggedIn | LoggedOut | Expired;
-
-interface LoggedIn {
- status: "loggedIn";
- isUserAdministrator: boolean;
- username: string;
- token: AccessToken;
-}
-interface Expired {
- status: "expired";
- isUserAdministrator: boolean;
- username: string;
-}
-interface LoggedOut {
- status: "loggedOut";
-}
-
-export const codecForBackendStateLoggedIn = (): Codec<LoggedIn> =>
- buildCodecForObject<LoggedIn>()
- .property("status", codecForConstString("loggedIn"))
- .property("username", codecForString())
- .property("token", codecForString() as Codec<AccessToken>)
- .property("isUserAdministrator", codecForBoolean())
- .build("BackendState.LoggedIn");
-
-export const codecForBackendStateExpired = (): Codec<Expired> =>
- buildCodecForObject<Expired>()
- .property("status", codecForConstString("expired"))
- .property("username", codecForString())
- .property("isUserAdministrator", codecForBoolean())
- .build("BackendState.Expired");
-
-export const codecForBackendStateLoggedOut = (): Codec<LoggedOut> =>
- buildCodecForObject<LoggedOut>()
- .property("status", codecForConstString("loggedOut"))
- .build("BackendState.LoggedOut");
-
-export const codecForBackendState = (): Codec<BackendState> =>
- buildCodecForUnion<BackendState>()
- .discriminateOn("status")
- .alternative("loggedIn", codecForBackendStateLoggedIn())
- .alternative("loggedOut", codecForBackendStateLoggedOut())
- .alternative("expired", codecForBackendStateExpired())
- .build("BackendState");
-
-export const defaultState: BackendState = {
- status: "loggedOut",
-};
-
-export interface BackendStateHandler {
- state: BackendState;
- logOut(): void;
- expired(): void;
- logIn(info: { username: string, token: AccessToken }): void;
-}
-
-const BACKEND_STATE_KEY = buildStorageKey(
- "bank-state",
- codecForBackendState(),
-);
-
-/**
- * Return getters and setters for
- * login credentials and backend's
- * base URL.
- */
-export function useBackendState(): BackendStateHandler {
- const { value: state, update } = useLocalStorage(
- BACKEND_STATE_KEY,
- defaultState,
- );
- const mutateAll = useMatchMutate();
-
- return {
- state,
- logOut() {
- update(defaultState);
- },
- expired() {
- if (state.status === "loggedOut") return;
- const nextState: BackendState = {
- status: "expired",
- username: state.username,
- isUserAdministrator: state.username === "admin",
- };
- update(nextState);
- },
- logIn(info) {
- //admin is defined by the username
- const nextState: BackendState = {
- status: "loggedIn",
- ...info,
- isUserAdministrator: info.username === "admin",
- };
- update(nextState);
- mutateAll(/.*/)
- },
- };
-}
-
-export function useMatchMutate(): (
- re: RegExp,
- value?: unknown,
-) => Promise<any> {
- const { cache, mutate } = useSWRConfig();
-
- if (!(cache instanceof Map)) {
- throw new Error(
- "matchMutate requires the cache provider to be a Map instance",
- );
- }
-
- return function matchRegexMutate(re: RegExp, value?: unknown) {
- const allKeys = Array.from(cache.keys());
- const keys = allKeys.filter((key) => re.test(key));
- const mutations = keys.map((key) => {
- return mutate(key, value, true);
- });
- return Promise.all(mutations);
- };
-}
diff --git a/packages/demobank-ui/src/hooks/circuit.ts b/packages/demobank-ui/src/hooks/circuit.ts
deleted file mode 100644
index 8a27f652c..000000000
--- a/packages/demobank-ui/src/hooks/circuit.ts
+++ /dev/null
@@ -1,319 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2022 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 { useState } from "preact/hooks";
-import { MAX_RESULT_SIZE, PAGE_SIZE } from "../utils.js";
-import { useBackendState } from "./backend.js";
-
-import { AccessToken, AmountJson, AmountString, Amounts, OperationOk, TalerBankConversionResultByMethod, TalerCoreBankErrorsByMethod, TalerCoreBankResultByMethod, TalerCorebankApi, TalerError, TalerHttpError, opFixedSuccess } from "@gnu-taler/taler-util";
-import _useSWR, { SWRHook } from "swr";
-import { useBankCoreApiContext } from "../context/config.js";
-import { assertUnreachable } from "../pages/WithdrawalOperationPage.js";
-import { format, getDate, getDay, getHours, getMonth, getYear, set, sub } from "date-fns";
-
-// FIX default import https://github.com/microsoft/TypeScript/issues/49189
-const useSWR = _useSWR as unknown as SWRHook;
-
-export type TransferCalculation = {
- debit: AmountJson;
- credit: AmountJson;
- beforeFee: AmountJson;
-};
-type EstimatorFunction = (
- amount: AmountJson,
- fee: AmountJson,
-) => Promise<TransferCalculation>;
-
-type CashoutEstimators = {
- estimateByCredit: EstimatorFunction;
- estimateByDebit: EstimatorFunction;
-};
-
-export function useConversionInfo() {
- const { api, config } = useBankCoreApiContext()
-
- async function fetcher() {
- return await api.getConversionInfoAPI().getConfig()
- }
- const { data, error } = useSWR<TalerBankConversionResultByMethod<"getConfig">, TalerHttpError>(
- !config.allow_conversion ? undefined : ["getConversionInfoAPI"], fetcher, {
- refreshInterval: 0,
- refreshWhenHidden: false,
- revalidateOnFocus: false,
- revalidateOnReconnect: false,
- refreshWhenOffline: false,
- errorRetryCount: 0,
- errorRetryInterval: 1,
- shouldRetryOnError: false,
- keepPreviousData: true,
- });
-
- if (data) return data
- if (error) return error;
- return undefined;
-
-}
-
-export function useEstimator(): CashoutEstimators {
- const { state } = useBackendState();
- const { api } = useBankCoreApiContext();
- return {
- estimateByCredit: async (fiatAmount, fee) => {
- const resp = await api.getConversionInfoAPI().getCashoutRate({
- credit: fiatAmount
- });
- if (resp.type === "fail") {
- // can't happen
- // not-supported: it should not be able to call this function
- // wrong-calculation: we are using just one parameter
- throw TalerError.fromDetail(resp.detail.code, {}, resp.detail.hint)
- }
- const credit = Amounts.parseOrThrow(resp.body.amount_credit);
- const debit = Amounts.parseOrThrow(resp.body.amount_debit);
- const beforeFee = Amounts.sub(credit, fee).amount;
-
- return {
- debit,
- beforeFee,
- credit,
- };
- },
- estimateByDebit: async (regionalAmount, fee) => {
- const resp = await api.getConversionInfoAPI().getCashoutRate({
- debit: regionalAmount
- });
- if (resp.type === "fail") {
- // can't happen
- // not-supported: it should not be able to call this function
- // wrong-calculation: we are using just one parameter
- throw TalerError.fromDetail(resp.detail.code, {}, resp.detail.hint)
- }
- const credit = Amounts.parseOrThrow(resp.body.amount_credit);
- const debit = Amounts.parseOrThrow(resp.body.amount_debit);
- const beforeFee = Amounts.add(credit, fee).amount;
-
- return {
- debit,
- beforeFee,
- credit,
- };
- },
- };
-}
-
-export function useBusinessAccounts() {
- const { state: credentials } = useBackendState();
- const token = credentials.status !== "loggedIn" ? undefined : credentials.token
- const { api } = useBankCoreApiContext();
-
- const [offset, setOffset] = useState<string | undefined>();
-
- function fetcher([token, offset]: [AccessToken, string]) {
- //FIXME: add account name filter
- return api.getAccounts(token, {}, {
- limit: MAX_RESULT_SIZE,
- offset,
- order: "asc"
- })
- }
-
- const { data, error } = useSWR<TalerCoreBankResultByMethod<"getAccounts">, TalerHttpError>(
- [token, offset, "getAccounts"], fetcher, {
- refreshInterval: 0,
- refreshWhenHidden: false,
- revalidateOnFocus: false,
- revalidateOnReconnect: false,
- refreshWhenOffline: false,
- errorRetryCount: 0,
- errorRetryInterval: 1,
- shouldRetryOnError: false,
- keepPreviousData: true,
- },
- );
-
- const isLastPage =
- data && data.type === "ok" && data.body.accounts.length < PAGE_SIZE;
- const isFirstPage = false;
-
- const pagination = {
- isLastPage,
- isFirstPage,
- loadMore: () => {
- if (isLastPage || data?.type !== "ok") return;
- const list = data.body.accounts
- if (list.length < MAX_RESULT_SIZE) {
- //FIXME: define pagination
-
- // setOffset(list[list.length - 1].row_id);
- }
- },
- loadMorePrev: () => {
- null;
- },
- };
-
- if (data) return { ok: true, data, ...pagination };
- if (error) return error;
- return undefined;
-}
-
-type CashoutWithId = TalerCorebankApi.CashoutStatusResponse & { id: number }
-function notUndefined(c: CashoutWithId | undefined): c is CashoutWithId {
- return c !== undefined
-}
-export function useOnePendingCashouts(account: string) {
- const { state: credentials } = useBackendState();
- const { api, config } = useBankCoreApiContext();
- const token = credentials.status !== "loggedIn" ? undefined : credentials.token
-
- async function fetcher([username, token]: [string, AccessToken]) {
- const list = await api.getAccountCashouts({ username, token })
- if (list.type !== "ok") {
- return list;
- }
- const pendingCashout = list.body.cashouts.find(c => c.status === "pending")
- if (!pendingCashout) return opFixedSuccess(undefined)
- const cashoutInfo = await api.getCashoutById({ username, token }, pendingCashout?.cashout_id)
- if (cashoutInfo.type !== "ok") {
- return cashoutInfo;
- }
- return opFixedSuccess({ ...cashoutInfo.body, id: pendingCashout.cashout_id })
- }
-
- const { data, error } = useSWR<OperationOk<CashoutWithId | undefined> | TalerCoreBankErrorsByMethod<"getAccountCashouts"> | TalerCoreBankErrorsByMethod<"getCashoutById">, TalerHttpError>(
- !config.allow_conversion ? undefined : [account, token, "useOnePendingCashouts"], fetcher, {
- refreshInterval: 0,
- refreshWhenHidden: false,
- revalidateOnFocus: false,
- revalidateOnReconnect: false,
- refreshWhenOffline: false,
- errorRetryCount: 0,
- errorRetryInterval: 1,
- shouldRetryOnError: false,
- keepPreviousData: true,
- });
-
- if (data) return data;
- if (error) return error;
- return undefined;
-}
-
-export function useCashouts(account: string) {
- const { state: credentials } = useBackendState();
- const { api, config } = useBankCoreApiContext();
- const token = credentials.status !== "loggedIn" ? undefined : credentials.token
-
- async function fetcher([username, token]: [string, AccessToken]) {
- const list = await api.getAccountCashouts({ username, token })
- if (list.type !== "ok") {
- return list;
- }
- const all: Array<CashoutWithId | undefined> = await Promise.all(list.body.cashouts.map(c => {
- return api.getCashoutById({ username, token }, c.cashout_id).then(r => {
- if (r.type === "fail") {
- return undefined
- }
- return { ...r.body, id: c.cashout_id }
- })
- }))
- const cashouts = all.filter(notUndefined)
- return { type: "ok" as const, body: { cashouts } }
- }
-
- const { data, error } = useSWR<OperationOk<{ cashouts: CashoutWithId[] }> | TalerCoreBankErrorsByMethod<"getAccountCashouts">, TalerHttpError>(
- !config.allow_conversion ? undefined : [account, token, "useCashouts"], fetcher, {
- refreshInterval: 0,
- refreshWhenHidden: false,
- revalidateOnFocus: false,
- revalidateOnReconnect: false,
- refreshWhenOffline: false,
- errorRetryCount: 0,
- errorRetryInterval: 1,
- shouldRetryOnError: false,
- keepPreviousData: true,
- });
-
- if (data) return data;
- if (error) return error;
- return undefined;
-}
-
-export function useCashoutDetails(cashoutId: number | undefined) {
- const { state: credentials } = useBackendState();
- const creds = credentials.status !== "loggedIn" ? undefined : credentials
- const { api } = useBankCoreApiContext();
-
- async function fetcher([username, token, id]: [string, AccessToken, number]) {
- return api.getCashoutById({ username, token }, id)
- }
-
- const { data, error } = useSWR<TalerCoreBankResultByMethod<"getCashoutById">, TalerHttpError>(
- cashoutId === undefined ? undefined : [creds?.username, creds?.token, cashoutId, "getCashoutById"], fetcher, {
- refreshInterval: 0,
- refreshWhenHidden: false,
- revalidateOnFocus: false,
- revalidateOnReconnect: false,
- refreshWhenOffline: false,
- errorRetryCount: 0,
- errorRetryInterval: 1,
- shouldRetryOnError: false,
- keepPreviousData: true,
- });
-
- if (data) return data;
- if (error) return error;
- return undefined;
-}
-export type MonitorMetrics = {
- lastHour: TalerCoreBankResultByMethod<"getMonitor">,
- lastDay: TalerCoreBankResultByMethod<"getMonitor">,
- lastMonth: TalerCoreBankResultByMethod<"getMonitor">,
-}
-
-export type LastMonitor = { current: TalerCoreBankResultByMethod<"getMonitor">, previous: TalerCoreBankResultByMethod<"getMonitor"> }
-export function useLastMonitorInfo(currentMoment: number, previousMoment: number, timeframe: TalerCorebankApi.MonitorTimeframeParam) {
- const { api, config } = useBankCoreApiContext();
- const { state: credentials } = useBackendState();
- const token = credentials.status !== "loggedIn" ? undefined : credentials.token
-
- async function fetcher([token, timeframe]: [AccessToken, TalerCorebankApi.MonitorTimeframeParam]) {
- const [current, previous] = await Promise.all([
- api.getMonitor(token, { timeframe, which: currentMoment }),
- api.getMonitor(token, { timeframe, which: previousMoment }),
- ])
- return {
- current,
- previous,
- }
- }
-
- const { data, error } = useSWR<LastMonitor, TalerHttpError>(
- !token ? undefined : [token, timeframe, "useLastMonitorInfo"], fetcher, {
- refreshInterval: 0,
- refreshWhenHidden: false,
- revalidateOnFocus: false,
- revalidateOnReconnect: false,
- refreshWhenOffline: false,
- errorRetryCount: 0,
- errorRetryInterval: 1,
- shouldRetryOnError: false,
- keepPreviousData: true,
- });
-
- if (data) return data;
- if (error) return error;
- return undefined;
-}
diff --git a/packages/demobank-ui/src/hooks/index.ts b/packages/demobank-ui/src/hooks/index.ts
deleted file mode 100644
index e9c68812c..000000000
--- a/packages/demobank-ui/src/hooks/index.ts
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2022 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 { StateUpdater } from "preact/hooks";
-import { buildStorageKey, useLocalStorage } from "@gnu-taler/web-util/browser";
-import { codecForBoolean } from "@gnu-taler/taler-util";
-export type ValueOrFunction<T> = T | ((p: T) => T);
-
-const calculateRootPath = () => {
- const rootPath =
- typeof window !== undefined
- ? window.location.origin + window.location.pathname
- : "/";
- return rootPath;
-};
-
-const BACKEND_URL_KEY = buildStorageKey("backend-url");
-const TRIED_LOGIN_KEY = buildStorageKey("tried-login", codecForBoolean());
-
-export function useBackendURL(
- url?: string,
-): [string, boolean, StateUpdater<string>, () => void] {
- const { value, update: setter } = useLocalStorage(
- BACKEND_URL_KEY,
- url || calculateRootPath(),
- );
-
- const {
- value: triedToLog,
- update: setTriedToLog,
- reset: resetBackend,
- } = useLocalStorage(TRIED_LOGIN_KEY);
-
- const checkedSetter = (v: ValueOrFunction<string>) => {
- setTriedToLog(true);
- const computedValue =
- v instanceof Function ? v(value) : v.replace(/\/$/, "");
- return setter(computedValue);
- };
-
- return [value, !!triedToLog, checkedSetter, resetBackend];
-}
diff --git a/packages/demobank-ui/src/hooks/preferences.ts b/packages/demobank-ui/src/hooks/preferences.ts
deleted file mode 100644
index a1525ac80..000000000
--- a/packages/demobank-ui/src/hooks/preferences.ts
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2022 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 {
- Codec,
- TranslatedString,
- buildCodecForObject,
- codecForBoolean,
- codecForNumber,
- codecForString,
- codecOptional
-} from "@gnu-taler/taler-util";
-import { buildStorageKey, useLocalStorage, useTranslationContext } from "@gnu-taler/web-util/browser";
-
-interface Preferences {
- currentWithdrawalOperationId: string | undefined;
- showWithdrawalSuccess: boolean;
- showDemoDescription: boolean;
- showInstallWallet: boolean;
- maxWithdrawalAmount: number;
- fastWithdrawal: boolean;
- showDebugInfo: boolean;
-
-}
-
-export function getAllBooleanPreferences(): Array<keyof Preferences> {
- return ["fastWithdrawal", "showDebugInfo", "showDemoDescription", "showInstallWallet", "showWithdrawalSuccess"]
-}
-
-export function getLabelForPreferences(k: keyof Preferences, i18n: ReturnType<typeof useTranslationContext>["i18n"]): TranslatedString {
- switch (k) {
- case "currentWithdrawalOperationId": return i18n.str`Current withdrawal operation`
- case "maxWithdrawalAmount": return i18n.str`Max withdrawal amount`
- case "showWithdrawalSuccess": return i18n.str`Show withdrawal confirmation`
- case "showDemoDescription": return i18n.str`Show demo description`
- case "showInstallWallet": return i18n.str`Show install wallet first`
- case "fastWithdrawal": return i18n.str`Use fast withdrawal form`
- case "showDebugInfo": return i18n.str`Show debug info`
- }
-}
-
-export const codecForPreferences = (): Codec<Preferences> =>
- buildCodecForObject<Preferences>()
- .property("currentWithdrawalOperationId", codecOptional(codecForString()))
- .property("showWithdrawalSuccess", (codecForBoolean()))
- .property("showDemoDescription", (codecForBoolean()))
- .property("showInstallWallet", (codecForBoolean()))
- .property("fastWithdrawal", (codecForBoolean()))
- .property("showDebugInfo", (codecForBoolean()))
- .property("maxWithdrawalAmount", codecForNumber())
- .build("Settings");
-
-const defaultPreferences: Preferences = {
- currentWithdrawalOperationId: undefined,
- showWithdrawalSuccess: true,
- showDemoDescription: true,
- showInstallWallet: true,
- maxWithdrawalAmount: 25,
- fastWithdrawal: false,
- showDebugInfo: false,
-};
-
-const BANK_PREFERENCES_KEY = buildStorageKey(
- "bank-preferences",
- codecForPreferences(),
-);
-
-export function usePreferences(): [
- Readonly<Preferences>,
- <T extends keyof Preferences>(key: T, value: Preferences[T]) => void,
-] {
- const { value, update } = useLocalStorage(
- BANK_PREFERENCES_KEY,
- defaultPreferences,
- );
-
- function updateField<T extends keyof Preferences>(k: T, v: Preferences[T]) {
- const newValue = { ...value, [k]: v };
- update(newValue);
- }
- return [value, updateField];
-}
diff --git a/packages/demobank-ui/src/i18n/bank.pot b/packages/demobank-ui/src/i18n/bank.pot
deleted file mode 100644
index 66e98976f..000000000
--- a/packages/demobank-ui/src/i18n/bank.pot
+++ /dev/null
@@ -1,486 +0,0 @@
-# This file is part of GNU Taler
-# (C) 2022 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/>
-#
-#, fuzzy
-msgid ""
-msgstr ""
-"Project-Id-Version: Taler Bank\n"
-"Report-Msgid-Bugs-To: taler@gnu.org\n"
-"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
-"Language-Team: LANGUAGE <LL@li.org>\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"Plural-Forms: nplurals=2; plural=(n != 1);\n"
-#: src/pages/home/BankFrame.tsx:55
-#, c-format
-msgid "Logout"
-msgstr ""
-
-#: src/pages/home/BankFrame.tsx:73
-#, c-format
-msgid "Skip to main content"
-msgstr ""
-
-#: src/pages/home/BankFrame.tsx:82
-#, c-format
-msgid ""
-"This part of the demo shows how a bank that supports Taler directly would work. "
-"In addition to using your own bank account, you can also see the transaction "
-"history of some %1$s."
-msgstr ""
-
-#: src/pages/home/BankFrame.tsx:94
-#, c-format
-msgid "Taler logo"
-msgstr ""
-
-#: src/pages/home/LoginForm.tsx:41
-#, c-format
-msgid "Missing username"
-msgstr ""
-
-#: src/pages/home/LoginForm.tsx:42
-#, c-format
-msgid "Missing password"
-msgstr ""
-
-#: src/pages/home/LoginForm.tsx:49
-#, c-format
-msgid "Please login!"
-msgstr ""
-
-#: src/pages/home/LoginForm.tsx:51
-#, c-format
-msgid "Username:"
-msgstr ""
-
-#: src/pages/home/LoginForm.tsx:71
-#, c-format
-msgid "Password:"
-msgstr ""
-
-#: src/pages/home/LoginForm.tsx:100
-#, c-format
-msgid "Login"
-msgstr ""
-
-#: src/pages/home/LoginForm.tsx:110
-#, c-format
-msgid "Register"
-msgstr ""
-
-#: src/pages/home/PaytoWireTransferForm.tsx:60
-#, c-format
-msgid "Missing IBAN"
-msgstr ""
-
-#: src/pages/home/PaytoWireTransferForm.tsx:62
-#, c-format
-msgid "IBAN should have just uppercased letters and numbers"
-msgstr ""
-
-#: src/pages/home/PaytoWireTransferForm.tsx:64
-#, c-format
-msgid "Missing subject"
-msgstr ""
-
-#: src/pages/home/PaytoWireTransferForm.tsx:66
-#, c-format
-msgid "Missing amount"
-msgstr ""
-
-#: src/pages/home/PaytoWireTransferForm.tsx:68
-#, c-format
-msgid "Amount is not valid"
-msgstr ""
-
-#: src/pages/home/PaytoWireTransferForm.tsx:70
-#, c-format
-msgid "Should be greater than 0"
-msgstr ""
-
-#: src/pages/home/PaytoWireTransferForm.tsx:79
-#, c-format
-msgid "Receiver IBAN:"
-msgstr ""
-
-#: src/pages/home/PaytoWireTransferForm.tsx:102
-#, c-format
-msgid "Transfer subject:"
-msgstr ""
-
-#: src/pages/home/PaytoWireTransferForm.tsx:123
-#, c-format
-msgid "Amount:"
-msgstr ""
-
-#: src/pages/home/PaytoWireTransferForm.tsx:177
-#, c-format
-msgid "Field(s) missing."
-msgstr ""
-
-#: src/pages/home/PaytoWireTransferForm.tsx:227
-#, c-format
-msgid "Want to try the raw payto://-format?"
-msgstr ""
-
-#: src/pages/home/PaytoWireTransferForm.tsx:235
-#, c-format
-msgid "Missing payto address"
-msgstr ""
-
-#: src/pages/home/PaytoWireTransferForm.tsx:237
-#, c-format
-msgid "Payto does not follow the pattern"
-msgstr ""
-
-#: src/pages/home/PaytoWireTransferForm.tsx:243
-#, c-format
-msgid "Transfer money to account identified by payto:// URI:"
-msgstr ""
-
-#: src/pages/home/PaytoWireTransferForm.tsx:246
-#, c-format
-msgid "payto URI:"
-msgstr ""
-
-#: src/pages/home/PaytoWireTransferForm.tsx:255
-#, c-format
-msgid "payto address"
-msgstr ""
-
-#: src/pages/home/PaytoWireTransferForm.tsx:279
-#, c-format
-msgid "Send"
-msgstr ""
-
-#: src/pages/home/PaytoWireTransferForm.tsx:314
-#, c-format
-msgid "Use wire-transfer form?"
-msgstr ""
-
-#: src/pages/home/PaytoWireTransferForm.tsx:373
-#, c-format
-msgid "No credentials found."
-msgstr ""
-
-#: src/pages/home/PaytoWireTransferForm.tsx:397
-#, c-format
-msgid "Could not create the wire transfer"
-msgstr ""
-
-#: src/pages/home/PaytoWireTransferForm.tsx:414
-#, c-format
-msgid "Transfer creation gave response error"
-msgstr ""
-
-#: src/pages/home/PaytoWireTransferForm.tsx:426
-#, c-format
-msgid "Wire transfer created!"
-msgstr ""
-
-#: src/pages/home/WalletWithdrawForm.tsx:50
-#, c-format
-msgid "Amount to withdraw:"
-msgstr ""
-
-#: src/pages/home/WalletWithdrawForm.tsx:84
-#, c-format
-msgid "Withdraw"
-msgstr ""
-
-#: src/pages/home/WalletWithdrawForm.tsx:128
-#, c-format
-msgid "No credentials given."
-msgstr ""
-
-#: src/pages/home/WalletWithdrawForm.tsx:155
-#, c-format
-msgid "Could not create withdrawal operation"
-msgstr ""
-
-#: src/pages/home/WalletWithdrawForm.tsx:171
-#, c-format
-msgid "Withdrawal creation gave response error"
-msgstr ""
-
-#: src/pages/home/PaymentOptions.tsx:44
-#, c-format
-msgid "Obtain digital cash"
-msgstr ""
-
-#: src/pages/home/PaymentOptions.tsx:52
-#, c-format
-msgid "Transfer to bank account"
-msgstr ""
-
-#: src/pages/home/Transactions.tsx:69
-#, c-format
-msgid "Date"
-msgstr ""
-
-#: src/pages/home/Transactions.tsx:70
-#, c-format
-msgid "Amount"
-msgstr ""
-
-#: src/pages/home/Transactions.tsx:71
-#, c-format
-msgid "Counterpart"
-msgstr ""
-
-#: src/pages/home/Transactions.tsx:72
-#, c-format
-msgid "Subject"
-msgstr ""
-
-#: src/pages/home/QrCodeSection.tsx:41
-#, c-format
-msgid "Transfer to Taler Wallet"
-msgstr ""
-
-#: src/pages/home/QrCodeSection.tsx:44
-#, c-format
-msgid "Use this QR code to withdraw to your mobile wallet:"
-msgstr ""
-
-#: src/pages/home/QrCodeSection.tsx:47
-#, c-format
-msgid "Click %1$s to open your Taler wallet!"
-msgstr ""
-
-#: src/pages/home/WithdrawalConfirmationQuestion.tsx:47
-#, c-format
-msgid "Confirm Withdrawal"
-msgstr ""
-
-#: src/pages/home/WithdrawalConfirmationQuestion.tsx:52
-#, c-format
-msgid "Authorize withdrawal by solving challenge"
-msgstr ""
-
-#: src/pages/home/WithdrawalConfirmationQuestion.tsx:55
-#, c-format
-msgid "What is"
-msgstr ""
-
-#: src/pages/home/WithdrawalConfirmationQuestion.tsx:94
-#, c-format
-msgid "Answer is wrong."
-msgstr ""
-
-#: src/pages/home/WithdrawalConfirmationQuestion.tsx:99
-#, c-format
-msgid "Confirm"
-msgstr ""
-
-#: src/pages/home/WithdrawalConfirmationQuestion.tsx:113
-#, c-format
-msgid "Cancel"
-msgstr ""
-
-#: src/pages/home/WithdrawalConfirmationQuestion.tsx:120
-#, c-format
-msgid ""
-"A this point, a %1$s bank would ask for an additional authentication proof "
-"(PIN/TAN, one time password, ..), instead of a simple calculation."
-msgstr ""
-
-#: src/pages/home/WithdrawalConfirmationQuestion.tsx:166
-#, c-format
-msgid "No withdrawal ID found."
-msgstr ""
-
-#: src/pages/home/WithdrawalConfirmationQuestion.tsx:201
-#, c-format
-msgid "Could not confirm the withdrawal"
-msgstr ""
-
-#: src/pages/home/WithdrawalConfirmationQuestion.tsx:219
-#, c-format
-msgid "Withdrawal confirmation gave response error"
-msgstr ""
-
-#: src/pages/home/WithdrawalConfirmationQuestion.tsx:231
-#, c-format
-msgid "Withdrawal confirmed!"
-msgstr ""
-
-#: src/pages/home/WithdrawalConfirmationQuestion.tsx:294
-#, c-format
-msgid "Could not abort the withdrawal."
-msgstr ""
-
-#: src/pages/home/WithdrawalConfirmationQuestion.tsx:311
-#, c-format
-msgid "Withdrawal abortion failed."
-msgstr ""
-
-#: src/pages/home/WithdrawalConfirmationQuestion.tsx:324
-#, c-format
-msgid "Withdrawal aborted!"
-msgstr ""
-
-#: src/pages/home/WithdrawalQRCode.tsx:54
-#, c-format
-msgid "Abort"
-msgstr ""
-
-#: src/pages/home/WithdrawalQRCode.tsx:74
-#, c-format
-msgid "withdrawal (%1$s) was never (correctly) created at the bank..."
-msgstr ""
-
-#: src/pages/home/WithdrawalQRCode.tsx:88
-#, c-format
-msgid "Waiting the bank to create the operation..."
-msgstr ""
-
-#: src/pages/home/WithdrawalQRCode.tsx:102
-#, c-format
-msgid "This withdrawal was aborted!"
-msgstr ""
-
-#: src/pages/home/AccountPage.tsx:40
-#, c-format
-msgid "Welcome to %1$s!"
-msgstr ""
-
-#: src/pages/home/AccountPage.tsx:133
-#, c-format
-msgid "Username or account label '%1$s' not found. Won't login."
-msgstr ""
-
-#: src/pages/home/AccountPage.tsx:159
-#, c-format
-msgid "Wrong credentials given."
-msgstr ""
-
-#: src/pages/home/AccountPage.tsx:169
-#, c-format
-msgid "Account information could not be retrieved."
-msgstr ""
-
-#: src/pages/home/AccountPage.tsx:210
-#, c-format
-msgid "Welcome, %1$s !"
-msgstr ""
-
-#: src/pages/home/AccountPage.tsx:221
-#, c-format
-msgid "Bank account balance"
-msgstr ""
-
-#: src/pages/home/AccountPage.tsx:237
-#, c-format
-msgid "Payments"
-msgstr ""
-
-#: src/pages/home/AccountPage.tsx:243
-#, c-format
-msgid "Latest transactions:"
-msgstr ""
-
-#: src/pages/home/PublicHistoriesPage.tsx:83
-#, c-format
-msgid "List of public accounts was not found."
-msgstr ""
-
-#: src/pages/home/PublicHistoriesPage.tsx:95
-#, c-format
-msgid "List of public accounts could not be retrieved."
-msgstr ""
-
-#: src/pages/home/PublicHistoriesPage.tsx:143
-#, c-format
-msgid "History of public accounts"
-msgstr ""
-
-#: src/pages/home/RegistrationPage.tsx:39
-#, c-format
-msgid "Currently, the bank is not accepting new registrations!"
-msgstr ""
-
-#: src/pages/home/RegistrationPage.tsx:68
-#, c-format
-msgid "Use only letter and numbers starting with a lower case letter"
-msgstr ""
-
-#: src/pages/home/RegistrationPage.tsx:78
-#, c-format
-msgid "Password don't match"
-msgstr ""
-
-#: src/pages/home/RegistrationPage.tsx:89
-#, c-format
-msgid "Please register!"
-msgstr ""
-
-#: src/pages/home/RegistrationPage.tsx:126
-#, c-format
-msgid "Repeat Password:"
-msgstr ""
-
-#: src/pages/home/RegistrationPage.tsx:226
-#, c-format
-msgid "Registration failed, please report"
-msgstr ""
-
-#: src/pages/home/RegistrationPage.tsx:239
-#, c-format
-msgid "That username is already taken"
-msgstr ""
-
-#: src/pages/home/RegistrationPage.tsx:248
-#, c-format
-msgid "New registration gave response error"
-msgstr ""
-
-#: src/components/menu/SideBar.tsx:53
-#, c-format
-msgid "Bank menu"
-msgstr ""
-
-#: src/components/menu/SideBar.tsx:59
-#, c-format
-msgid "Select option1"
-msgstr ""
-
-#: src/components/menu/SideBar.tsx:66
-#, c-format
-msgid "Select option2"
-msgstr ""
-
-#: src/components/picker/DurationPicker.tsx:55
-#, c-format
-msgid "days"
-msgstr ""
-
-#: src/components/picker/DurationPicker.tsx:65
-#, c-format
-msgid "hours"
-msgstr ""
-
-#: src/components/picker/DurationPicker.tsx:76
-#, c-format
-msgid "minutes"
-msgstr ""
-
-#: src/components/picker/DurationPicker.tsx:87
-#, c-format
-msgid "seconds"
-msgstr ""
-
diff --git a/packages/demobank-ui/src/i18n/de.po b/packages/demobank-ui/src/i18n/de.po
deleted file mode 100644
index dc76f83e2..000000000
--- a/packages/demobank-ui/src/i18n/de.po
+++ /dev/null
@@ -1,486 +0,0 @@
-# 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/>
-#
-msgid ""
-msgstr ""
-"Project-Id-Version: Taler Wallet\n"
-"Report-Msgid-Bugs-To: taler@gnu.org\n"
-"POT-Creation-Date: 2016-11-23 00:00+0100\n"
-"PO-Revision-Date: 2022-12-26 23:30+0000\n"
-"Last-Translator: Stefan Kügel <skuegel@web.de>\n"
-"Language-Team: German <https://weblate.taler.net/projects/gnu-taler/"
-"taler-bank-spa/de/>\n"
-"Language: de\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"Plural-Forms: nplurals=2; plural=n != 1;\n"
-"X-Generator: Weblate 4.13.1\n"
-
-#: src/pages/home/BankFrame.tsx:55
-#, c-format
-msgid "Logout"
-msgstr "Abmelden"
-
-#: src/pages/home/BankFrame.tsx:73
-#, c-format
-msgid "Skip to main content"
-msgstr "Navigationsmenü überspringen"
-
-#: src/pages/home/BankFrame.tsx:82
-#, c-format
-msgid ""
-"This part of the demo shows how a bank that supports Taler directly would "
-"work. In addition to using your own bank account, you can also see the "
-"transaction history of some %1$s."
-msgstr ""
-
-#: src/pages/home/BankFrame.tsx:94
-#, c-format
-msgid "Taler logo"
-msgstr "Taler-Logo"
-
-#: src/pages/home/LoginForm.tsx:41
-#, c-format
-msgid "Missing username"
-msgstr ""
-
-#: src/pages/home/LoginForm.tsx:42
-#, c-format
-msgid "Missing password"
-msgstr ""
-
-#: src/pages/home/LoginForm.tsx:49
-#, c-format
-msgid "Please login!"
-msgstr "Bitte melden Sie sich an!"
-
-#: src/pages/home/LoginForm.tsx:51
-#, c-format
-msgid "Username:"
-msgstr ""
-
-#: src/pages/home/LoginForm.tsx:71
-#, c-format
-msgid "Password:"
-msgstr ""
-
-#: src/pages/home/LoginForm.tsx:100
-#, c-format
-msgid "Login"
-msgstr ""
-
-#: src/pages/home/LoginForm.tsx:110
-#, c-format
-msgid "Register"
-msgstr ""
-
-#: src/pages/home/PaytoWireTransferForm.tsx:60
-#, c-format
-msgid "Missing IBAN"
-msgstr ""
-
-#: src/pages/home/PaytoWireTransferForm.tsx:62
-#, c-format
-msgid "IBAN should have just uppercased letters and numbers"
-msgstr ""
-
-#: src/pages/home/PaytoWireTransferForm.tsx:64
-#, c-format
-msgid "Missing subject"
-msgstr ""
-
-#: src/pages/home/PaytoWireTransferForm.tsx:66
-#, c-format
-msgid "Missing amount"
-msgstr ""
-
-#: src/pages/home/PaytoWireTransferForm.tsx:68
-#, c-format
-msgid "Amount is not valid"
-msgstr ""
-
-#: src/pages/home/PaytoWireTransferForm.tsx:70
-#, c-format
-msgid "Should be greater than 0"
-msgstr ""
-
-#: src/pages/home/PaytoWireTransferForm.tsx:79
-#, c-format
-msgid "Receiver IBAN:"
-msgstr ""
-
-#: src/pages/home/PaytoWireTransferForm.tsx:102
-#, c-format
-msgid "Transfer subject:"
-msgstr ""
-
-#: src/pages/home/PaytoWireTransferForm.tsx:123
-#, c-format
-msgid "Amount:"
-msgstr ""
-
-#: src/pages/home/PaytoWireTransferForm.tsx:177
-#, c-format
-msgid "Field(s) missing."
-msgstr ""
-
-#: src/pages/home/PaytoWireTransferForm.tsx:227
-#, c-format
-msgid "Want to try the raw payto://-format?"
-msgstr ""
-
-#: src/pages/home/PaytoWireTransferForm.tsx:235
-#, c-format
-msgid "Missing payto address"
-msgstr ""
-
-#: src/pages/home/PaytoWireTransferForm.tsx:237
-#, c-format
-msgid "Payto does not follow the pattern"
-msgstr ""
-
-#: src/pages/home/PaytoWireTransferForm.tsx:243
-#, c-format
-msgid "Transfer money to account identified by payto:// URI:"
-msgstr ""
-
-#: src/pages/home/PaytoWireTransferForm.tsx:246
-#, c-format
-msgid "payto URI:"
-msgstr ""
-
-#: src/pages/home/PaytoWireTransferForm.tsx:255
-#, c-format
-msgid "payto address"
-msgstr "payto-Adresse"
-
-#: src/pages/home/PaytoWireTransferForm.tsx:279
-#, c-format
-msgid "Send"
-msgstr ""
-
-#: src/pages/home/PaytoWireTransferForm.tsx:314
-#, c-format
-msgid "Use wire-transfer form?"
-msgstr ""
-
-#: src/pages/home/PaytoWireTransferForm.tsx:373
-#, c-format
-msgid "No credentials found."
-msgstr ""
-
-#: src/pages/home/PaytoWireTransferForm.tsx:397
-#, c-format
-msgid "Could not create the wire transfer"
-msgstr ""
-
-#: src/pages/home/PaytoWireTransferForm.tsx:414
-#, c-format
-msgid "Transfer creation gave response error"
-msgstr ""
-
-#: src/pages/home/PaytoWireTransferForm.tsx:426
-#, c-format
-msgid "Wire transfer created!"
-msgstr ""
-
-#: src/pages/home/WalletWithdrawForm.tsx:50
-#, c-format
-msgid "Amount to withdraw:"
-msgstr ""
-
-#: src/pages/home/WalletWithdrawForm.tsx:84
-#, c-format
-msgid "Withdraw"
-msgstr ""
-
-#: src/pages/home/WalletWithdrawForm.tsx:128
-#, c-format
-msgid "No credentials given."
-msgstr ""
-
-#: src/pages/home/WalletWithdrawForm.tsx:155
-#, c-format
-msgid "Could not create withdrawal operation"
-msgstr ""
-
-#: src/pages/home/WalletWithdrawForm.tsx:171
-#, c-format
-msgid "Withdrawal creation gave response error"
-msgstr ""
-
-#: src/pages/home/PaymentOptions.tsx:44
-#, c-format
-msgid "Obtain digital cash"
-msgstr ""
-
-#: src/pages/home/PaymentOptions.tsx:52
-#, c-format
-msgid "Transfer to bank account"
-msgstr ""
-
-#: src/pages/home/Transactions.tsx:69
-#, c-format
-msgid "Date"
-msgstr "Datum"
-
-#: src/pages/home/Transactions.tsx:70
-#, c-format
-msgid "Amount"
-msgstr "Betrag"
-
-#: src/pages/home/Transactions.tsx:71
-#, c-format
-msgid "Counterpart"
-msgstr "Empfänger"
-
-#: src/pages/home/Transactions.tsx:72
-#, c-format
-msgid "Subject"
-msgstr "Verwendungszweck"
-
-#: src/pages/home/QrCodeSection.tsx:41
-#, c-format
-msgid "Transfer to Taler Wallet"
-msgstr ""
-
-#: src/pages/home/QrCodeSection.tsx:44
-#, c-format
-msgid "Use this QR code to withdraw to your mobile wallet:"
-msgstr ""
-
-#: src/pages/home/QrCodeSection.tsx:47
-#, c-format
-msgid "Click %1$s to open your Taler wallet!"
-msgstr ""
-
-#: src/pages/home/WithdrawalConfirmationQuestion.tsx:47
-#, c-format
-msgid "Confirm Withdrawal"
-msgstr "Abhebung bestätigen"
-
-#: src/pages/home/WithdrawalConfirmationQuestion.tsx:52
-#, c-format
-msgid "Authorize withdrawal by solving challenge"
-msgstr ""
-
-#: src/pages/home/WithdrawalConfirmationQuestion.tsx:55
-#, c-format
-msgid "What is"
-msgstr ""
-
-#: src/pages/home/WithdrawalConfirmationQuestion.tsx:94
-#, c-format
-msgid "Answer is wrong."
-msgstr ""
-
-#: src/pages/home/WithdrawalConfirmationQuestion.tsx:99
-#, c-format
-msgid "Confirm"
-msgstr "Bestätigen"
-
-#: src/pages/home/WithdrawalConfirmationQuestion.tsx:113
-#, c-format
-msgid "Cancel"
-msgstr ""
-
-#: src/pages/home/WithdrawalConfirmationQuestion.tsx:120
-#, c-format
-msgid ""
-"A this point, a %1$s bank would ask for an additional authentication proof "
-"(PIN/TAN, one time password, ..), instead of a simple calculation."
-msgstr ""
-
-#: src/pages/home/WithdrawalConfirmationQuestion.tsx:166
-#, c-format
-msgid "No withdrawal ID found."
-msgstr ""
-
-#: src/pages/home/WithdrawalConfirmationQuestion.tsx:201
-#, c-format
-msgid "Could not confirm the withdrawal"
-msgstr ""
-
-#: src/pages/home/WithdrawalConfirmationQuestion.tsx:219
-#, c-format
-msgid "Withdrawal confirmation gave response error"
-msgstr ""
-
-#: src/pages/home/WithdrawalConfirmationQuestion.tsx:231
-#, c-format
-msgid "Withdrawal confirmed!"
-msgstr ""
-
-#: src/pages/home/WithdrawalConfirmationQuestion.tsx:294
-#, c-format
-msgid "Could not abort the withdrawal."
-msgstr ""
-
-#: src/pages/home/WithdrawalConfirmationQuestion.tsx:311
-#, c-format
-msgid "Withdrawal abortion failed."
-msgstr ""
-
-#: src/pages/home/WithdrawalConfirmationQuestion.tsx:324
-#, c-format
-msgid "Withdrawal aborted!"
-msgstr ""
-
-#: src/pages/home/WithdrawalQRCode.tsx:54
-#, c-format
-msgid "Abort"
-msgstr ""
-
-#: src/pages/home/WithdrawalQRCode.tsx:74
-#, c-format
-msgid "withdrawal (%1$s) was never (correctly) created at the bank..."
-msgstr ""
-
-#: src/pages/home/WithdrawalQRCode.tsx:88
-#, c-format
-msgid "Waiting the bank to create the operation..."
-msgstr ""
-
-#: src/pages/home/WithdrawalQRCode.tsx:102
-#, c-format
-msgid "This withdrawal was aborted!"
-msgstr ""
-
-#: src/pages/home/AccountPage.tsx:40
-#, c-format
-msgid "Welcome to %1$s!"
-msgstr ""
-
-#: src/pages/home/AccountPage.tsx:133
-#, c-format
-msgid "Username or account label '%1$s' not found. Won't login."
-msgstr ""
-
-#: src/pages/home/AccountPage.tsx:159
-#, c-format
-msgid "Wrong credentials given."
-msgstr ""
-
-#: src/pages/home/AccountPage.tsx:169
-#, c-format
-msgid "Account information could not be retrieved."
-msgstr ""
-
-#: src/pages/home/AccountPage.tsx:210
-#, c-format
-msgid "Welcome, %1$s !"
-msgstr ""
-
-#: src/pages/home/AccountPage.tsx:221
-#, c-format
-msgid "Bank account balance"
-msgstr "Kontostand"
-
-#: src/pages/home/AccountPage.tsx:237
-#, c-format
-msgid "Payments"
-msgstr ""
-
-#: src/pages/home/AccountPage.tsx:243
-#, c-format
-msgid "Latest transactions:"
-msgstr ""
-
-#: src/pages/home/PublicHistoriesPage.tsx:83
-#, c-format
-msgid "List of public accounts was not found."
-msgstr ""
-
-#: src/pages/home/PublicHistoriesPage.tsx:95
-#, c-format
-msgid "List of public accounts could not be retrieved."
-msgstr ""
-
-#: src/pages/home/PublicHistoriesPage.tsx:143
-#, c-format
-msgid "History of public accounts"
-msgstr "Buchungen auf öffentlich sichtbaren Konten"
-
-#: src/pages/home/RegistrationPage.tsx:39
-#, c-format
-msgid "Currently, the bank is not accepting new registrations!"
-msgstr ""
-
-#: src/pages/home/RegistrationPage.tsx:68
-#, c-format
-msgid "Use only letter and numbers starting with a lower case letter"
-msgstr ""
-
-#: src/pages/home/RegistrationPage.tsx:78
-#, c-format
-msgid "Password don't match"
-msgstr ""
-
-#: src/pages/home/RegistrationPage.tsx:89
-#, c-format
-msgid "Please register!"
-msgstr ""
-
-#: src/pages/home/RegistrationPage.tsx:126
-#, c-format
-msgid "Repeat Password:"
-msgstr ""
-
-#: src/pages/home/RegistrationPage.tsx:226
-#, c-format
-msgid "Registration failed, please report"
-msgstr ""
-
-#: src/pages/home/RegistrationPage.tsx:239
-#, c-format
-msgid "That username is already taken"
-msgstr ""
-
-#: src/pages/home/RegistrationPage.tsx:248
-#, c-format
-msgid "New registration gave response error"
-msgstr ""
-
-#: src/components/menu/SideBar.tsx:53
-#, c-format
-msgid "Bank menu"
-msgstr ""
-
-#: src/components/menu/SideBar.tsx:59
-#, c-format
-msgid "Select option1"
-msgstr ""
-
-#: src/components/menu/SideBar.tsx:66
-#, c-format
-msgid "Select option2"
-msgstr ""
-
-#: src/components/picker/DurationPicker.tsx:55
-#, c-format
-msgid "days"
-msgstr ""
-
-#: src/components/picker/DurationPicker.tsx:65
-#, c-format
-msgid "hours"
-msgstr ""
-
-#: src/components/picker/DurationPicker.tsx:76
-#, c-format
-msgid "minutes"
-msgstr ""
-
-#: src/components/picker/DurationPicker.tsx:87
-#, c-format
-msgid "seconds"
-msgstr ""
diff --git a/packages/demobank-ui/src/i18n/en.po b/packages/demobank-ui/src/i18n/en.po
deleted file mode 100644
index 907ebdf0c..000000000
--- a/packages/demobank-ui/src/i18n/en.po
+++ /dev/null
@@ -1,511 +0,0 @@
-# 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/>
-#
-msgid ""
-msgstr ""
-"Project-Id-Version: Taler Wallet\n"
-"Report-Msgid-Bugs-To: taler@gnu.org\n"
-"POT-Creation-Date: 2016-11-23 00:00+0100\n"
-"PO-Revision-Date: 2022-01-08 09:57+0100\n"
-"Last-Translator: <translate@taler.net>\n"
-"Language-Team: English\n"
-"Language: en\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"Plural-Forms: nplurals=2; plural=(n != 1);\n"
-
-#: src/pages/home/BankFrame.tsx:55
-#, c-format
-msgid "Logout"
-msgstr ""
-
-#: src/pages/home/BankFrame.tsx:73
-#, c-format
-msgid "Skip to main content"
-msgstr ""
-
-#: src/pages/home/BankFrame.tsx:82
-#, c-format
-msgid ""
-"This part of the demo shows how a bank that supports Taler directly would "
-"work. In addition to using your own bank account, you can also see the "
-"transaction history of some %1$s."
-msgstr ""
-
-#: src/pages/home/BankFrame.tsx:94
-#, c-format
-msgid "Taler logo"
-msgstr ""
-
-#: src/pages/home/LoginForm.tsx:41
-#, c-format
-msgid "Missing username"
-msgstr ""
-
-#: src/pages/home/LoginForm.tsx:42
-#, c-format
-msgid "Missing password"
-msgstr ""
-
-#: src/pages/home/LoginForm.tsx:49
-#, c-format
-msgid "Please login!"
-msgstr ""
-
-#: src/pages/home/LoginForm.tsx:51
-#, c-format
-msgid "Username:"
-msgstr ""
-
-#: src/pages/home/LoginForm.tsx:71
-#, c-format
-msgid "Password:"
-msgstr ""
-
-#: src/pages/home/LoginForm.tsx:100
-#, c-format
-msgid "Login"
-msgstr ""
-
-#: src/pages/home/LoginForm.tsx:110
-#, c-format
-msgid "Register"
-msgstr ""
-
-#: src/pages/home/PaytoWireTransferForm.tsx:60
-#, c-format
-msgid "Missing IBAN"
-msgstr ""
-
-#: src/pages/home/PaytoWireTransferForm.tsx:62
-#, c-format
-msgid "IBAN should have just uppercased letters and numbers"
-msgstr ""
-
-#: src/pages/home/PaytoWireTransferForm.tsx:64
-#, c-format
-msgid "Missing subject"
-msgstr ""
-
-#: src/pages/home/PaytoWireTransferForm.tsx:66
-#, c-format
-msgid "Missing amount"
-msgstr ""
-
-#: src/pages/home/PaytoWireTransferForm.tsx:68
-#, c-format
-msgid "Amount is not valid"
-msgstr ""
-
-#: src/pages/home/PaytoWireTransferForm.tsx:70
-#, c-format
-msgid "Should be greater than 0"
-msgstr ""
-
-#: src/pages/home/PaytoWireTransferForm.tsx:79
-#, c-format
-msgid "Receiver IBAN:"
-msgstr ""
-
-#: src/pages/home/PaytoWireTransferForm.tsx:102
-#, c-format
-msgid "Transfer subject:"
-msgstr ""
-
-#: src/pages/home/PaytoWireTransferForm.tsx:123
-#, c-format
-msgid "Amount:"
-msgstr ""
-
-#: src/pages/home/PaytoWireTransferForm.tsx:177
-#, c-format
-msgid "Field(s) missing."
-msgstr ""
-
-#: src/pages/home/PaytoWireTransferForm.tsx:227
-#, c-format
-msgid "Want to try the raw payto://-format?"
-msgstr ""
-
-#: src/pages/home/PaytoWireTransferForm.tsx:235
-#, c-format
-msgid "Missing payto address"
-msgstr ""
-
-#: src/pages/home/PaytoWireTransferForm.tsx:237
-#, c-format
-msgid "Payto does not follow the pattern"
-msgstr ""
-
-#: src/pages/home/PaytoWireTransferForm.tsx:243
-#, c-format
-msgid "Transfer money to account identified by payto:// URI:"
-msgstr ""
-
-#: src/pages/home/PaytoWireTransferForm.tsx:246
-#, c-format
-msgid "payto URI:"
-msgstr ""
-
-#: src/pages/home/PaytoWireTransferForm.tsx:255
-#, c-format
-msgid "payto address"
-msgstr ""
-
-#: src/pages/home/PaytoWireTransferForm.tsx:279
-#, c-format
-msgid "Send"
-msgstr ""
-
-#: src/pages/home/PaytoWireTransferForm.tsx:314
-#, c-format
-msgid "Use wire-transfer form?"
-msgstr ""
-
-#: src/pages/home/PaytoWireTransferForm.tsx:373
-#, c-format
-msgid "No credentials found."
-msgstr ""
-
-#: src/pages/home/PaytoWireTransferForm.tsx:397
-#, c-format
-msgid "Could not create the wire transfer"
-msgstr ""
-
-#: src/pages/home/PaytoWireTransferForm.tsx:414
-#, c-format
-msgid "Transfer creation gave response error"
-msgstr ""
-
-#: src/pages/home/PaytoWireTransferForm.tsx:426
-#, c-format
-msgid "Wire transfer created!"
-msgstr ""
-
-#: src/pages/home/WalletWithdrawForm.tsx:50
-#, fuzzy, c-format
-msgid "Amount to withdraw:"
-msgstr "Amount to withdraw"
-
-#: src/pages/home/WalletWithdrawForm.tsx:84
-#, fuzzy, c-format
-msgid "Withdraw"
-msgstr "Confirm withdrawal"
-
-#: src/pages/home/WalletWithdrawForm.tsx:128
-#, c-format
-msgid "No credentials given."
-msgstr ""
-
-#: src/pages/home/WalletWithdrawForm.tsx:155
-#, c-format
-msgid "Could not create withdrawal operation"
-msgstr ""
-
-#: src/pages/home/WalletWithdrawForm.tsx:171
-#, c-format
-msgid "Withdrawal creation gave response error"
-msgstr ""
-
-#: src/pages/home/PaymentOptions.tsx:44
-#, c-format
-msgid "Obtain digital cash"
-msgstr ""
-
-#: src/pages/home/PaymentOptions.tsx:52
-#, c-format
-msgid "Transfer to bank account"
-msgstr ""
-
-#: src/pages/home/Transactions.tsx:69
-#, c-format
-msgid "Date"
-msgstr ""
-
-#: src/pages/home/Transactions.tsx:70
-#, c-format
-msgid "Amount"
-msgstr ""
-
-#: src/pages/home/Transactions.tsx:71
-#, c-format
-msgid "Counterpart"
-msgstr ""
-
-#: src/pages/home/Transactions.tsx:72
-#, c-format
-msgid "Subject"
-msgstr ""
-
-#: src/pages/home/QrCodeSection.tsx:41
-#, fuzzy, c-format
-msgid "Transfer to Taler Wallet"
-msgstr "Top up Taler wallet"
-
-#: src/pages/home/QrCodeSection.tsx:44
-#, c-format
-msgid "Use this QR code to withdraw to your mobile wallet:"
-msgstr ""
-
-#: src/pages/home/QrCodeSection.tsx:47
-#, c-format
-msgid "Click %1$s to open your Taler wallet!"
-msgstr ""
-
-#: src/pages/home/WithdrawalConfirmationQuestion.tsx:47
-#, fuzzy, c-format
-msgid "Confirm Withdrawal"
-msgstr "Confirm withdrawal"
-
-#: src/pages/home/WithdrawalConfirmationQuestion.tsx:52
-#, c-format
-msgid "Authorize withdrawal by solving challenge"
-msgstr ""
-
-#: src/pages/home/WithdrawalConfirmationQuestion.tsx:55
-#, c-format
-msgid "What is"
-msgstr ""
-
-#: src/pages/home/WithdrawalConfirmationQuestion.tsx:94
-#, c-format
-msgid "Answer is wrong."
-msgstr ""
-
-#: src/pages/home/WithdrawalConfirmationQuestion.tsx:99
-#, c-format
-msgid "Confirm"
-msgstr ""
-
-#: src/pages/home/WithdrawalConfirmationQuestion.tsx:113
-#, c-format
-msgid "Cancel"
-msgstr ""
-
-#: src/pages/home/WithdrawalConfirmationQuestion.tsx:120
-#, c-format
-msgid ""
-"A this point, a %1$s bank would ask for an additional authentication proof "
-"(PIN/TAN, one time password, ..), instead of a simple calculation."
-msgstr ""
-
-#: src/pages/home/WithdrawalConfirmationQuestion.tsx:166
-#, c-format
-msgid "No withdrawal ID found."
-msgstr ""
-
-#: src/pages/home/WithdrawalConfirmationQuestion.tsx:201
-#, fuzzy, c-format
-msgid "Could not confirm the withdrawal"
-msgstr "Confirm withdrawal"
-
-#: src/pages/home/WithdrawalConfirmationQuestion.tsx:219
-#, c-format
-msgid "Withdrawal confirmation gave response error"
-msgstr ""
-
-#: src/pages/home/WithdrawalConfirmationQuestion.tsx:231
-#, c-format
-msgid "Withdrawal confirmed!"
-msgstr ""
-
-#: src/pages/home/WithdrawalConfirmationQuestion.tsx:294
-#, fuzzy, c-format
-msgid "Could not abort the withdrawal."
-msgstr "Close Taler withdrawal"
-
-#: src/pages/home/WithdrawalConfirmationQuestion.tsx:311
-#, c-format
-msgid "Withdrawal abortion failed."
-msgstr ""
-
-#: src/pages/home/WithdrawalConfirmationQuestion.tsx:324
-#, c-format
-msgid "Withdrawal aborted!"
-msgstr ""
-
-#: src/pages/home/WithdrawalQRCode.tsx:54
-#, c-format
-msgid "Abort"
-msgstr ""
-
-#: src/pages/home/WithdrawalQRCode.tsx:74
-#, c-format
-msgid "withdrawal (%1$s) was never (correctly) created at the bank..."
-msgstr ""
-
-#: src/pages/home/WithdrawalQRCode.tsx:88
-#, c-format
-msgid "Waiting the bank to create the operation..."
-msgstr ""
-
-#: src/pages/home/WithdrawalQRCode.tsx:102
-#, c-format
-msgid "This withdrawal was aborted!"
-msgstr ""
-
-#: src/pages/home/AccountPage.tsx:40
-#, c-format
-msgid "Welcome to %1$s!"
-msgstr ""
-
-#: src/pages/home/AccountPage.tsx:133
-#, c-format
-msgid "Username or account label '%1$s' not found. Won't login."
-msgstr ""
-
-#: src/pages/home/AccountPage.tsx:159
-#, c-format
-msgid "Wrong credentials given."
-msgstr ""
-
-#: src/pages/home/AccountPage.tsx:169
-#, c-format
-msgid "Account information could not be retrieved."
-msgstr ""
-
-#: src/pages/home/AccountPage.tsx:210
-#, c-format
-msgid "Welcome, %1$s !"
-msgstr ""
-
-#: src/pages/home/AccountPage.tsx:221
-#, c-format
-msgid "Bank account balance"
-msgstr ""
-
-#: src/pages/home/AccountPage.tsx:237
-#, c-format
-msgid "Payments"
-msgstr ""
-
-#: src/pages/home/AccountPage.tsx:243
-#, c-format
-msgid "Latest transactions:"
-msgstr ""
-
-#: src/pages/home/PublicHistoriesPage.tsx:83
-#, c-format
-msgid "List of public accounts was not found."
-msgstr ""
-
-#: src/pages/home/PublicHistoriesPage.tsx:95
-#, c-format
-msgid "List of public accounts could not be retrieved."
-msgstr ""
-
-#: src/pages/home/PublicHistoriesPage.tsx:143
-#, c-format
-msgid "History of public accounts"
-msgstr ""
-
-#: src/pages/home/RegistrationPage.tsx:39
-#, c-format
-msgid "Currently, the bank is not accepting new registrations!"
-msgstr ""
-
-#: src/pages/home/RegistrationPage.tsx:68
-#, c-format
-msgid "Use only letter and numbers starting with a lower case letter"
-msgstr ""
-
-#: src/pages/home/RegistrationPage.tsx:78
-#, c-format
-msgid "Password don't match"
-msgstr ""
-
-#: src/pages/home/RegistrationPage.tsx:89
-#, c-format
-msgid "Please register!"
-msgstr ""
-
-#: src/pages/home/RegistrationPage.tsx:126
-#, c-format
-msgid "Repeat Password:"
-msgstr ""
-
-#: src/pages/home/RegistrationPage.tsx:226
-#, c-format
-msgid "Registration failed, please report"
-msgstr ""
-
-#: src/pages/home/RegistrationPage.tsx:239
-#, c-format
-msgid "That username is already taken"
-msgstr ""
-
-#: src/pages/home/RegistrationPage.tsx:248
-#, c-format
-msgid "New registration gave response error"
-msgstr ""
-
-#: src/components/menu/SideBar.tsx:53
-#, c-format
-msgid "Bank menu"
-msgstr ""
-
-#: src/components/menu/SideBar.tsx:59
-#, c-format
-msgid "Select option1"
-msgstr ""
-
-#: src/components/menu/SideBar.tsx:66
-#, c-format
-msgid "Select option2"
-msgstr ""
-
-#: src/components/picker/DurationPicker.tsx:55
-#, c-format
-msgid "days"
-msgstr "days"
-
-#: src/components/picker/DurationPicker.tsx:65
-#, c-format
-msgid "hours"
-msgstr "hours"
-
-#: src/components/picker/DurationPicker.tsx:76
-#, c-format
-msgid "minutes"
-msgstr "minutes"
-
-#: src/components/picker/DurationPicker.tsx:87
-#, c-format
-msgid "seconds"
-msgstr "seconds"
-
-#~ msgid "Go back"
-#~ msgstr "Go back"
-
-#, fuzzy
-#~ msgid "Start withdrawal"
-#~ msgstr "Start withdrawal"
-
-#, fuzzy
-#~ msgid "Withdraw Money into a Taler wallet"
-#~ msgstr "Top up Taler wallet"
-
-#~ msgid "Page has a problem: logged in but backend state is lost."
-#~ msgstr "Page has a problem: logged in but backend state is lost."
-
-#, fuzzy
-#~ msgid "Welcome to the euFin bank!"
-#~ msgstr "Welcome to euFin bank: Taler+IBAN now possible!"
-
-#~ msgid "Page has a problem:"
-#~ msgstr "Page has a problem:"
-
-#~ msgid "Close"
-#~ msgstr "Close"
-
-#~ msgid "Sign in"
-#~ msgstr "Sign in"
diff --git a/packages/demobank-ui/src/i18n/es.po b/packages/demobank-ui/src/i18n/es.po
deleted file mode 100644
index 0787b1035..000000000
--- a/packages/demobank-ui/src/i18n/es.po
+++ /dev/null
@@ -1,497 +0,0 @@
-# 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/>
-#
-msgid ""
-msgstr ""
-"Project-Id-Version: Taler Wallet\n"
-"Report-Msgid-Bugs-To: taler@gnu.org\n"
-"POT-Creation-Date: 2016-11-23 00:00+0100\n"
-"PO-Revision-Date: 2022-12-09 14:13+0000\n"
-"Last-Translator: Sebastian Marchano <sebasjm@gmail.com>\n"
-"Language-Team: Spanish <https://weblate.taler.net/projects/gnu-taler/taler-"
-"bank-spa/es/>\n"
-"Language: es\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"Plural-Forms: nplurals=2; plural=n != 1;\n"
-"X-Generator: Weblate 4.13.1\n"
-
-#: src/pages/home/BankFrame.tsx:55
-#, c-format
-msgid "Logout"
-msgstr "Cierre de sesión"
-
-#: src/pages/home/BankFrame.tsx:73
-#, c-format
-msgid "Skip to main content"
-msgstr "Saltar el menú de navegación"
-
-#: src/pages/home/BankFrame.tsx:82
-#, c-format
-msgid ""
-"This part of the demo shows how a bank that supports Taler directly would "
-"work. In addition to using your own bank account, you can also see the "
-"transaction history of some %1$s."
-msgstr ""
-"Esta parte de la demostración muestra cómo funciona un banco que soporta "
-"Taler directamente. Además de usar tu propia cuenta de banco, también podrás "
-"ver el historial de transacciones de algunas %1$s."
-
-#: src/pages/home/BankFrame.tsx:94
-#, c-format
-msgid "Taler logo"
-msgstr "Logo Taler"
-
-#: src/pages/home/LoginForm.tsx:41
-#, c-format
-msgid "Missing username"
-msgstr "Falta nombre de usuario"
-
-#: src/pages/home/LoginForm.tsx:42
-#, c-format
-msgid "Missing password"
-msgstr "Falta contraseña"
-
-#: src/pages/home/LoginForm.tsx:49
-#, c-format
-msgid "Please login!"
-msgstr "Por favor inicia sesión!"
-
-#: src/pages/home/LoginForm.tsx:51
-#, c-format
-msgid "Username:"
-msgstr "Nombre de usuario:"
-
-#: src/pages/home/LoginForm.tsx:71
-#, c-format
-msgid "Password:"
-msgstr "Password:"
-
-#: src/pages/home/LoginForm.tsx:100
-#, c-format
-msgid "Login"
-msgstr "Iniciar sesión"
-
-#: src/pages/home/LoginForm.tsx:110
-#, c-format
-msgid "Register"
-msgstr "Registrarse"
-
-#: src/pages/home/PaytoWireTransferForm.tsx:60
-#, c-format
-msgid "Missing IBAN"
-msgstr "Falta IBAN"
-
-#: src/pages/home/PaytoWireTransferForm.tsx:62
-#, c-format
-msgid "IBAN should have just uppercased letters and numbers"
-msgstr "IBAN debería tener letras mayúsculas y números"
-
-#: src/pages/home/PaytoWireTransferForm.tsx:64
-#, c-format
-msgid "Missing subject"
-msgstr "Falta asunto"
-
-#: src/pages/home/PaytoWireTransferForm.tsx:66
-#, c-format
-msgid "Missing amount"
-msgstr "Falta monto"
-
-#: src/pages/home/PaytoWireTransferForm.tsx:68
-#, c-format
-msgid "Amount is not valid"
-msgstr "Monto no válido"
-
-#: src/pages/home/PaytoWireTransferForm.tsx:70
-#, c-format
-msgid "Should be greater than 0"
-msgstr "Debería ser mas grande que 0"
-
-#: src/pages/home/PaytoWireTransferForm.tsx:79
-#, c-format
-msgid "Receiver IBAN:"
-msgstr "IBAN receptor:"
-
-#: src/pages/home/PaytoWireTransferForm.tsx:102
-#, c-format
-msgid "Transfer subject:"
-msgstr "Asunto de transferencia:"
-
-#: src/pages/home/PaytoWireTransferForm.tsx:123
-#, c-format
-msgid "Amount:"
-msgstr "Monto:"
-
-#: src/pages/home/PaytoWireTransferForm.tsx:177
-#, c-format
-msgid "Field(s) missing."
-msgstr "Faltan campo(s)."
-
-#: src/pages/home/PaytoWireTransferForm.tsx:227
-#, c-format
-msgid "Want to try the raw payto://-format?"
-msgstr "Quieres probar el formato payto:// ?"
-
-#: src/pages/home/PaytoWireTransferForm.tsx:235
-#, c-format
-msgid "Missing payto address"
-msgstr "Falta direccion payto"
-
-#: src/pages/home/PaytoWireTransferForm.tsx:237
-#, c-format
-msgid "Payto does not follow the pattern"
-msgstr "Payto no sigue el patrón"
-
-#: src/pages/home/PaytoWireTransferForm.tsx:243
-#, c-format
-msgid "Transfer money to account identified by payto:// URI:"
-msgstr "Transferir dinero a la cuenta identificada por la URI payto://:"
-
-#: src/pages/home/PaytoWireTransferForm.tsx:246
-#, c-format
-msgid "payto URI:"
-msgstr "payto URI:"
-
-#: src/pages/home/PaytoWireTransferForm.tsx:255
-#, c-format
-msgid "payto address"
-msgstr "direccion payto"
-
-#: src/pages/home/PaytoWireTransferForm.tsx:279
-#, c-format
-msgid "Send"
-msgstr "Envíar"
-
-#: src/pages/home/PaytoWireTransferForm.tsx:314
-#, c-format
-msgid "Use wire-transfer form?"
-msgstr "Usar el formulario de transferencia bancaria?"
-
-#: src/pages/home/PaytoWireTransferForm.tsx:373
-#, c-format
-msgid "No credentials found."
-msgstr "Se dieron las credenciales incorrectas."
-
-#: src/pages/home/PaytoWireTransferForm.tsx:397
-#, c-format
-msgid "Could not create the wire transfer"
-msgstr "No se pudo create la transferencia bancaria"
-
-#: src/pages/home/PaytoWireTransferForm.tsx:414
-#, c-format
-msgid "Transfer creation gave response error"
-msgstr "La creación de la transferencia dió una respuesta erronea"
-
-#: src/pages/home/PaytoWireTransferForm.tsx:426
-#, c-format
-msgid "Wire transfer created!"
-msgstr "Transferencia bancaria creada!"
-
-#: src/pages/home/WalletWithdrawForm.tsx:50
-#, c-format
-msgid "Amount to withdraw:"
-msgstr "Monto a retirar:"
-
-#: src/pages/home/WalletWithdrawForm.tsx:84
-#, c-format
-msgid "Withdraw"
-msgstr "Retirar"
-
-#: src/pages/home/WalletWithdrawForm.tsx:128
-#, c-format
-msgid "No credentials given."
-msgstr "Se dieron las credenciales incorrectas."
-
-#: src/pages/home/WalletWithdrawForm.tsx:155
-#, c-format
-msgid "Could not create withdrawal operation"
-msgstr "No se pude create la operación de retiro"
-
-#: src/pages/home/WalletWithdrawForm.tsx:171
-#, c-format
-msgid "Withdrawal creation gave response error"
-msgstr "La creación de retiro dió una respuesta errónea"
-
-#: src/pages/home/PaymentOptions.tsx:44
-#, c-format
-msgid "Obtain digital cash"
-msgstr "Obtener dinero digital"
-
-#: src/pages/home/PaymentOptions.tsx:52
-#, c-format
-msgid "Transfer to bank account"
-msgstr "Transferir a una cuenta bancaria"
-
-#: src/pages/home/Transactions.tsx:69
-#, c-format
-msgid "Date"
-msgstr "Fecha"
-
-#: src/pages/home/Transactions.tsx:70
-#, c-format
-msgid "Amount"
-msgstr "Monto"
-
-#: src/pages/home/Transactions.tsx:71
-#, c-format
-msgid "Counterpart"
-msgstr "Contraparte"
-
-#: src/pages/home/Transactions.tsx:72
-#, c-format
-msgid "Subject"
-msgstr "Asunto"
-
-#: src/pages/home/QrCodeSection.tsx:41
-#, c-format
-msgid "Transfer to Taler Wallet"
-msgstr "Transferir a una cartera Taler"
-
-#: src/pages/home/QrCodeSection.tsx:44
-#, c-format
-msgid "Use this QR code to withdraw to your mobile wallet:"
-msgstr "Usar el código QR para retirar a tu cartera móvil:"
-
-#: src/pages/home/QrCodeSection.tsx:47
-#, c-format
-msgid "Click %1$s to open your Taler wallet!"
-msgstr "Click %1$s para abrir una cartera Taler!"
-
-#: src/pages/home/WithdrawalConfirmationQuestion.tsx:47
-#, c-format
-msgid "Confirm Withdrawal"
-msgstr "Confirmar retirada"
-
-#: src/pages/home/WithdrawalConfirmationQuestion.tsx:52
-#, c-format
-msgid "Authorize withdrawal by solving challenge"
-msgstr "Autorizar retiro resolviendo una pregunta"
-
-#: src/pages/home/WithdrawalConfirmationQuestion.tsx:55
-#, c-format
-msgid "What is"
-msgstr "Cuanto es"
-
-#: src/pages/home/WithdrawalConfirmationQuestion.tsx:94
-#, c-format
-msgid "Answer is wrong."
-msgstr "La respuesta es incorrecta."
-
-#: src/pages/home/WithdrawalConfirmationQuestion.tsx:99
-#, c-format
-msgid "Confirm"
-msgstr "Confirmar"
-
-#: src/pages/home/WithdrawalConfirmationQuestion.tsx:113
-#, c-format
-msgid "Cancel"
-msgstr "Cancelar"
-
-#: src/pages/home/WithdrawalConfirmationQuestion.tsx:120
-#, c-format
-msgid ""
-"A this point, a %1$s bank would ask for an additional authentication proof "
-"(PIN/TAN, one time password, ..), instead of a simple calculation."
-msgstr ""
-"En este punto, un banco %1$s preguntaría por una prueba adicional de "
-"autenticación (PIN/TAN, password de un solo uso, ....), en vez de un simple "
-"cálculo."
-
-#: src/pages/home/WithdrawalConfirmationQuestion.tsx:166
-#, c-format
-msgid "No withdrawal ID found."
-msgstr "No ID de retiro encontrado."
-
-#: src/pages/home/WithdrawalConfirmationQuestion.tsx:201
-#, c-format
-msgid "Could not confirm the withdrawal"
-msgstr "No se pudo confirmar la retirada"
-
-#: src/pages/home/WithdrawalConfirmationQuestion.tsx:219
-#, c-format
-msgid "Withdrawal confirmation gave response error"
-msgstr "La confirmación de retiro dió una respuesta errónea"
-
-#: src/pages/home/WithdrawalConfirmationQuestion.tsx:231
-#, c-format
-msgid "Withdrawal confirmed!"
-msgstr "El retiro fue confirmado!"
-
-#: src/pages/home/WithdrawalConfirmationQuestion.tsx:294
-#, c-format
-msgid "Could not abort the withdrawal."
-msgstr "No se pudo cancelar el retiro."
-
-#: src/pages/home/WithdrawalConfirmationQuestion.tsx:311
-#, c-format
-msgid "Withdrawal abortion failed."
-msgstr "La cancelación del retiro falló."
-
-#: src/pages/home/WithdrawalConfirmationQuestion.tsx:324
-#, c-format
-msgid "Withdrawal aborted!"
-msgstr "Este retiro fue cancelado!"
-
-#: src/pages/home/WithdrawalQRCode.tsx:54
-#, c-format
-msgid "Abort"
-msgstr "Cancelar"
-
-#: src/pages/home/WithdrawalQRCode.tsx:74
-#, c-format
-msgid "withdrawal (%1$s) was never (correctly) created at the bank..."
-msgstr "retiro (%1$s) nunca fue (correctamente) generado en el banco..."
-
-#: src/pages/home/WithdrawalQRCode.tsx:88
-#, c-format
-msgid "Waiting the bank to create the operation..."
-msgstr "Esperando que el banco genere la operación...."
-
-#: src/pages/home/WithdrawalQRCode.tsx:102
-#, c-format
-msgid "This withdrawal was aborted!"
-msgstr "Este retiro fue cancelado!"
-
-#: src/pages/home/AccountPage.tsx:40
-#, c-format
-msgid "Welcome to %1$s!"
-msgstr "Bienvenido a %1$s!"
-
-#: src/pages/home/AccountPage.tsx:133
-#, c-format
-msgid "Username or account label '%1$s' not found. Won't login."
-msgstr ""
-"Nombre de usuario o etiqueta de cuenta '%1$s' no encontrada. No se iniciará "
-"sesión."
-
-#: src/pages/home/AccountPage.tsx:159
-#, c-format
-msgid "Wrong credentials given."
-msgstr "Se dieron las credenciales incorrectas."
-
-#: src/pages/home/AccountPage.tsx:169
-#, c-format
-msgid "Account information could not be retrieved."
-msgstr "La información de la cuenta no pudo ser accedida."
-
-#: src/pages/home/AccountPage.tsx:210
-#, c-format
-msgid "Welcome, %1$s !"
-msgstr "Bienvenido/a, %1$s!"
-
-#: src/pages/home/AccountPage.tsx:221
-#, c-format
-msgid "Bank account balance"
-msgstr "Balance de cuenta bancaria"
-
-#: src/pages/home/AccountPage.tsx:237
-#, c-format
-msgid "Payments"
-msgstr "Pagos"
-
-#: src/pages/home/AccountPage.tsx:243
-#, c-format
-msgid "Latest transactions:"
-msgstr "Últimas transacciones:"
-
-#: src/pages/home/PublicHistoriesPage.tsx:83
-#, c-format
-msgid "List of public accounts was not found."
-msgstr "La lista de cuentas públicas no fue encontrada."
-
-#: src/pages/home/PublicHistoriesPage.tsx:95
-#, c-format
-msgid "List of public accounts could not be retrieved."
-msgstr "La lista de cuentas públicas no pudo ser accedida."
-
-#: src/pages/home/PublicHistoriesPage.tsx:143
-#, c-format
-msgid "History of public accounts"
-msgstr "Historial de cuentas públicas"
-
-#: src/pages/home/RegistrationPage.tsx:39
-#, c-format
-msgid "Currently, the bank is not accepting new registrations!"
-msgstr "Actualmente, el banco no está aceptado nuevos registros!"
-
-#: src/pages/home/RegistrationPage.tsx:68
-#, c-format
-msgid "Use only letter and numbers starting with a lower case letter"
-msgstr "Solo use letras y números comenzando con una letra minúscula"
-
-#: src/pages/home/RegistrationPage.tsx:78
-#, c-format
-msgid "Password don't match"
-msgstr "La contraseña no coincide"
-
-#: src/pages/home/RegistrationPage.tsx:89
-#, c-format
-msgid "Please register!"
-msgstr "Por favor, registrese!"
-
-#: src/pages/home/RegistrationPage.tsx:126
-#, c-format
-msgid "Repeat Password:"
-msgstr "Repita la contraseña:"
-
-#: src/pages/home/RegistrationPage.tsx:226
-#, c-format
-msgid "Registration failed, please report"
-msgstr "El registro falló, por favor reportelo"
-
-#: src/pages/home/RegistrationPage.tsx:239
-#, c-format
-msgid "That username is already taken"
-msgstr "El nombre del usuario ya está tomado"
-
-#: src/pages/home/RegistrationPage.tsx:248
-#, c-format
-msgid "New registration gave response error"
-msgstr "Nuevo registro dió una respuesta errónea"
-
-#: src/components/menu/SideBar.tsx:53
-#, c-format
-msgid "Bank menu"
-msgstr "Menu del banco"
-
-#: src/components/menu/SideBar.tsx:59
-#, c-format
-msgid "Select option1"
-msgstr "Seleccione opción 1"
-
-#: src/components/menu/SideBar.tsx:66
-#, c-format
-msgid "Select option2"
-msgstr "Seleccione opción 2"
-
-#: src/components/picker/DurationPicker.tsx:55
-#, c-format
-msgid "days"
-msgstr "días"
-
-#: src/components/picker/DurationPicker.tsx:65
-#, c-format
-msgid "hours"
-msgstr "horas"
-
-#: src/components/picker/DurationPicker.tsx:76
-#, c-format
-msgid "minutes"
-msgstr "minutos"
-
-#: src/components/picker/DurationPicker.tsx:87
-#, c-format
-msgid "seconds"
-msgstr "segundos"
-
-#~ msgid "this link"
-#~ msgstr "este link"
diff --git a/packages/demobank-ui/src/i18n/fr.po b/packages/demobank-ui/src/i18n/fr.po
deleted file mode 100644
index 203d55343..000000000
--- a/packages/demobank-ui/src/i18n/fr.po
+++ /dev/null
@@ -1,486 +0,0 @@
-# This file is part of GNU Taler
-# (C) 2022 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/>
-#
-msgid ""
-msgstr ""
-"Project-Id-Version: Taler Bank\n"
-"Report-Msgid-Bugs-To: taler@gnu.org\n"
-"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"Last-Translator: Automatically generated\n"
-"Language-Team: none\n"
-"Language: fr\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"Plural-Forms: nplurals=2; plural=n > 1;\n"
-
-#: src/pages/home/BankFrame.tsx:55
-#, c-format
-msgid "Logout"
-msgstr ""
-
-#: src/pages/home/BankFrame.tsx:73
-#, c-format
-msgid "Skip to main content"
-msgstr ""
-
-#: src/pages/home/BankFrame.tsx:82
-#, c-format
-msgid ""
-"This part of the demo shows how a bank that supports Taler directly would work. "
-"In addition to using your own bank account, you can also see the transaction "
-"history of some %1$s."
-msgstr ""
-
-#: src/pages/home/BankFrame.tsx:94
-#, c-format
-msgid "Taler logo"
-msgstr ""
-
-#: src/pages/home/LoginForm.tsx:41
-#, c-format
-msgid "Missing username"
-msgstr ""
-
-#: src/pages/home/LoginForm.tsx:42
-#, c-format
-msgid "Missing password"
-msgstr ""
-
-#: src/pages/home/LoginForm.tsx:49
-#, c-format
-msgid "Please login!"
-msgstr ""
-
-#: src/pages/home/LoginForm.tsx:51
-#, c-format
-msgid "Username:"
-msgstr ""
-
-#: src/pages/home/LoginForm.tsx:71
-#, c-format
-msgid "Password:"
-msgstr ""
-
-#: src/pages/home/LoginForm.tsx:100
-#, c-format
-msgid "Login"
-msgstr ""
-
-#: src/pages/home/LoginForm.tsx:110
-#, c-format
-msgid "Register"
-msgstr ""
-
-#: src/pages/home/PaytoWireTransferForm.tsx:60
-#, c-format
-msgid "Missing IBAN"
-msgstr ""
-
-#: src/pages/home/PaytoWireTransferForm.tsx:62
-#, c-format
-msgid "IBAN should have just uppercased letters and numbers"
-msgstr ""
-
-#: src/pages/home/PaytoWireTransferForm.tsx:64
-#, c-format
-msgid "Missing subject"
-msgstr ""
-
-#: src/pages/home/PaytoWireTransferForm.tsx:66
-#, c-format
-msgid "Missing amount"
-msgstr ""
-
-#: src/pages/home/PaytoWireTransferForm.tsx:68
-#, c-format
-msgid "Amount is not valid"
-msgstr ""
-
-#: src/pages/home/PaytoWireTransferForm.tsx:70
-#, c-format
-msgid "Should be greater than 0"
-msgstr ""
-
-#: src/pages/home/PaytoWireTransferForm.tsx:79
-#, c-format
-msgid "Receiver IBAN:"
-msgstr ""
-
-#: src/pages/home/PaytoWireTransferForm.tsx:102
-#, c-format
-msgid "Transfer subject:"
-msgstr ""
-
-#: src/pages/home/PaytoWireTransferForm.tsx:123
-#, c-format
-msgid "Amount:"
-msgstr ""
-
-#: src/pages/home/PaytoWireTransferForm.tsx:177
-#, c-format
-msgid "Field(s) missing."
-msgstr ""
-
-#: src/pages/home/PaytoWireTransferForm.tsx:227
-#, c-format
-msgid "Want to try the raw payto://-format?"
-msgstr ""
-
-#: src/pages/home/PaytoWireTransferForm.tsx:235
-#, c-format
-msgid "Missing payto address"
-msgstr ""
-
-#: src/pages/home/PaytoWireTransferForm.tsx:237
-#, c-format
-msgid "Payto does not follow the pattern"
-msgstr ""
-
-#: src/pages/home/PaytoWireTransferForm.tsx:243
-#, c-format
-msgid "Transfer money to account identified by payto:// URI:"
-msgstr ""
-
-#: src/pages/home/PaytoWireTransferForm.tsx:246
-#, c-format
-msgid "payto URI:"
-msgstr ""
-
-#: src/pages/home/PaytoWireTransferForm.tsx:255
-#, c-format
-msgid "payto address"
-msgstr ""
-
-#: src/pages/home/PaytoWireTransferForm.tsx:279
-#, c-format
-msgid "Send"
-msgstr ""
-
-#: src/pages/home/PaytoWireTransferForm.tsx:314
-#, c-format
-msgid "Use wire-transfer form?"
-msgstr ""
-
-#: src/pages/home/PaytoWireTransferForm.tsx:373
-#, c-format
-msgid "No credentials found."
-msgstr ""
-
-#: src/pages/home/PaytoWireTransferForm.tsx:397
-#, c-format
-msgid "Could not create the wire transfer"
-msgstr ""
-
-#: src/pages/home/PaytoWireTransferForm.tsx:414
-#, c-format
-msgid "Transfer creation gave response error"
-msgstr ""
-
-#: src/pages/home/PaytoWireTransferForm.tsx:426
-#, c-format
-msgid "Wire transfer created!"
-msgstr ""
-
-#: src/pages/home/WalletWithdrawForm.tsx:50
-#, c-format
-msgid "Amount to withdraw:"
-msgstr ""
-
-#: src/pages/home/WalletWithdrawForm.tsx:84
-#, c-format
-msgid "Withdraw"
-msgstr ""
-
-#: src/pages/home/WalletWithdrawForm.tsx:128
-#, c-format
-msgid "No credentials given."
-msgstr ""
-
-#: src/pages/home/WalletWithdrawForm.tsx:155
-#, c-format
-msgid "Could not create withdrawal operation"
-msgstr ""
-
-#: src/pages/home/WalletWithdrawForm.tsx:171
-#, c-format
-msgid "Withdrawal creation gave response error"
-msgstr ""
-
-#: src/pages/home/PaymentOptions.tsx:44
-#, c-format
-msgid "Obtain digital cash"
-msgstr ""
-
-#: src/pages/home/PaymentOptions.tsx:52
-#, c-format
-msgid "Transfer to bank account"
-msgstr ""
-
-#: src/pages/home/Transactions.tsx:69
-#, c-format
-msgid "Date"
-msgstr ""
-
-#: src/pages/home/Transactions.tsx:70
-#, c-format
-msgid "Amount"
-msgstr ""
-
-#: src/pages/home/Transactions.tsx:71
-#, c-format
-msgid "Counterpart"
-msgstr ""
-
-#: src/pages/home/Transactions.tsx:72
-#, c-format
-msgid "Subject"
-msgstr ""
-
-#: src/pages/home/QrCodeSection.tsx:41
-#, c-format
-msgid "Transfer to Taler Wallet"
-msgstr ""
-
-#: src/pages/home/QrCodeSection.tsx:44
-#, c-format
-msgid "Use this QR code to withdraw to your mobile wallet:"
-msgstr ""
-
-#: src/pages/home/QrCodeSection.tsx:47
-#, c-format
-msgid "Click %1$s to open your Taler wallet!"
-msgstr ""
-
-#: src/pages/home/WithdrawalConfirmationQuestion.tsx:47
-#, c-format
-msgid "Confirm Withdrawal"
-msgstr ""
-
-#: src/pages/home/WithdrawalConfirmationQuestion.tsx:52
-#, c-format
-msgid "Authorize withdrawal by solving challenge"
-msgstr ""
-
-#: src/pages/home/WithdrawalConfirmationQuestion.tsx:55
-#, c-format
-msgid "What is"
-msgstr ""
-
-#: src/pages/home/WithdrawalConfirmationQuestion.tsx:94
-#, c-format
-msgid "Answer is wrong."
-msgstr ""
-
-#: src/pages/home/WithdrawalConfirmationQuestion.tsx:99
-#, c-format
-msgid "Confirm"
-msgstr ""
-
-#: src/pages/home/WithdrawalConfirmationQuestion.tsx:113
-#, c-format
-msgid "Cancel"
-msgstr ""
-
-#: src/pages/home/WithdrawalConfirmationQuestion.tsx:120
-#, c-format
-msgid ""
-"A this point, a %1$s bank would ask for an additional authentication proof "
-"(PIN/TAN, one time password, ..), instead of a simple calculation."
-msgstr ""
-
-#: src/pages/home/WithdrawalConfirmationQuestion.tsx:166
-#, c-format
-msgid "No withdrawal ID found."
-msgstr ""
-
-#: src/pages/home/WithdrawalConfirmationQuestion.tsx:201
-#, c-format
-msgid "Could not confirm the withdrawal"
-msgstr ""
-
-#: src/pages/home/WithdrawalConfirmationQuestion.tsx:219
-#, c-format
-msgid "Withdrawal confirmation gave response error"
-msgstr ""
-
-#: src/pages/home/WithdrawalConfirmationQuestion.tsx:231
-#, c-format
-msgid "Withdrawal confirmed!"
-msgstr ""
-
-#: src/pages/home/WithdrawalConfirmationQuestion.tsx:294
-#, c-format
-msgid "Could not abort the withdrawal."
-msgstr ""
-
-#: src/pages/home/WithdrawalConfirmationQuestion.tsx:311
-#, c-format
-msgid "Withdrawal abortion failed."
-msgstr ""
-
-#: src/pages/home/WithdrawalConfirmationQuestion.tsx:324
-#, c-format
-msgid "Withdrawal aborted!"
-msgstr ""
-
-#: src/pages/home/WithdrawalQRCode.tsx:54
-#, c-format
-msgid "Abort"
-msgstr ""
-
-#: src/pages/home/WithdrawalQRCode.tsx:74
-#, c-format
-msgid "withdrawal (%1$s) was never (correctly) created at the bank..."
-msgstr ""
-
-#: src/pages/home/WithdrawalQRCode.tsx:88
-#, c-format
-msgid "Waiting the bank to create the operation..."
-msgstr ""
-
-#: src/pages/home/WithdrawalQRCode.tsx:102
-#, c-format
-msgid "This withdrawal was aborted!"
-msgstr ""
-
-#: src/pages/home/AccountPage.tsx:40
-#, c-format
-msgid "Welcome to %1$s!"
-msgstr ""
-
-#: src/pages/home/AccountPage.tsx:133
-#, c-format
-msgid "Username or account label '%1$s' not found. Won't login."
-msgstr ""
-
-#: src/pages/home/AccountPage.tsx:159
-#, c-format
-msgid "Wrong credentials given."
-msgstr ""
-
-#: src/pages/home/AccountPage.tsx:169
-#, c-format
-msgid "Account information could not be retrieved."
-msgstr ""
-
-#: src/pages/home/AccountPage.tsx:210
-#, c-format
-msgid "Welcome, %1$s !"
-msgstr ""
-
-#: src/pages/home/AccountPage.tsx:221
-#, c-format
-msgid "Bank account balance"
-msgstr ""
-
-#: src/pages/home/AccountPage.tsx:237
-#, c-format
-msgid "Payments"
-msgstr ""
-
-#: src/pages/home/AccountPage.tsx:243
-#, c-format
-msgid "Latest transactions:"
-msgstr ""
-
-#: src/pages/home/PublicHistoriesPage.tsx:83
-#, c-format
-msgid "List of public accounts was not found."
-msgstr ""
-
-#: src/pages/home/PublicHistoriesPage.tsx:95
-#, c-format
-msgid "List of public accounts could not be retrieved."
-msgstr ""
-
-#: src/pages/home/PublicHistoriesPage.tsx:143
-#, c-format
-msgid "History of public accounts"
-msgstr ""
-
-#: src/pages/home/RegistrationPage.tsx:39
-#, c-format
-msgid "Currently, the bank is not accepting new registrations!"
-msgstr ""
-
-#: src/pages/home/RegistrationPage.tsx:68
-#, c-format
-msgid "Use only letter and numbers starting with a lower case letter"
-msgstr ""
-
-#: src/pages/home/RegistrationPage.tsx:78
-#, c-format
-msgid "Password don't match"
-msgstr ""
-
-#: src/pages/home/RegistrationPage.tsx:89
-#, c-format
-msgid "Please register!"
-msgstr ""
-
-#: src/pages/home/RegistrationPage.tsx:126
-#, c-format
-msgid "Repeat Password:"
-msgstr ""
-
-#: src/pages/home/RegistrationPage.tsx:226
-#, c-format
-msgid "Registration failed, please report"
-msgstr ""
-
-#: src/pages/home/RegistrationPage.tsx:239
-#, c-format
-msgid "That username is already taken"
-msgstr ""
-
-#: src/pages/home/RegistrationPage.tsx:248
-#, c-format
-msgid "New registration gave response error"
-msgstr ""
-
-#: src/components/menu/SideBar.tsx:53
-#, c-format
-msgid "Bank menu"
-msgstr ""
-
-#: src/components/menu/SideBar.tsx:59
-#, c-format
-msgid "Select option1"
-msgstr ""
-
-#: src/components/menu/SideBar.tsx:66
-#, c-format
-msgid "Select option2"
-msgstr ""
-
-#: src/components/picker/DurationPicker.tsx:55
-#, c-format
-msgid "days"
-msgstr ""
-
-#: src/components/picker/DurationPicker.tsx:65
-#, c-format
-msgid "hours"
-msgstr ""
-
-#: src/components/picker/DurationPicker.tsx:76
-#, c-format
-msgid "minutes"
-msgstr ""
-
-#: src/components/picker/DurationPicker.tsx:87
-#, c-format
-msgid "seconds"
-msgstr ""
diff --git a/packages/demobank-ui/src/i18n/it.po b/packages/demobank-ui/src/i18n/it.po
deleted file mode 100644
index 5dfebedae..000000000
--- a/packages/demobank-ui/src/i18n/it.po
+++ /dev/null
@@ -1,521 +0,0 @@
-# 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/>
-#
-msgid ""
-msgstr ""
-"Project-Id-Version: Taler Wallet\n"
-"Report-Msgid-Bugs-To: taler@gnu.org\n"
-"POT-Creation-Date: 2016-11-23 00:00+0100\n"
-"PO-Revision-Date: 2023-08-15 07:28+0000\n"
-"Last-Translator: Krystian Baran <kiszkot@murena.io>\n"
-"Language-Team: Italian <https://weblate.taler.net/projects/gnu-taler/"
-"taler-bank-spa/it/>\n"
-"Language: it\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"Plural-Forms: nplurals=2; plural=n != 1;\n"
-"X-Generator: Weblate 4.13.1\n"
-
-#: src/pages/home/BankFrame.tsx:55
-#, c-format
-msgid "Logout"
-msgstr ""
-
-#: src/pages/home/BankFrame.tsx:73
-#, c-format
-msgid "Skip to main content"
-msgstr "Saltare il menu di navigazione"
-
-#: src/pages/home/BankFrame.tsx:82
-#, c-format
-msgid ""
-"This part of the demo shows how a bank that supports Taler directly would "
-"work. In addition to using your own bank account, you can also see the "
-"transaction history of some %1$s."
-msgstr ""
-
-#: src/pages/home/BankFrame.tsx:94
-#, c-format
-msgid "Taler logo"
-msgstr ""
-
-#: src/pages/home/LoginForm.tsx:41
-#, c-format
-msgid "Missing username"
-msgstr ""
-
-#: src/pages/home/LoginForm.tsx:42
-#, c-format
-msgid "Missing password"
-msgstr ""
-
-#: src/pages/home/LoginForm.tsx:49
-#, c-format
-msgid "Please login!"
-msgstr "Accedi!"
-
-#: src/pages/home/LoginForm.tsx:51
-#, c-format
-msgid "Username:"
-msgstr ""
-
-#: src/pages/home/LoginForm.tsx:71
-#, c-format
-msgid "Password:"
-msgstr ""
-
-#: src/pages/home/LoginForm.tsx:100
-#, c-format
-msgid "Login"
-msgstr "Accedi"
-
-#: src/pages/home/LoginForm.tsx:110
-#, c-format
-msgid "Register"
-msgstr "Registrati"
-
-#: src/pages/home/PaytoWireTransferForm.tsx:60
-#, c-format
-msgid "Missing IBAN"
-msgstr ""
-
-#: src/pages/home/PaytoWireTransferForm.tsx:62
-#, c-format
-msgid "IBAN should have just uppercased letters and numbers"
-msgstr ""
-
-#: src/pages/home/PaytoWireTransferForm.tsx:64
-#, c-format
-msgid "Missing subject"
-msgstr ""
-
-#: src/pages/home/PaytoWireTransferForm.tsx:66
-#, c-format
-msgid "Missing amount"
-msgstr ""
-
-#: src/pages/home/PaytoWireTransferForm.tsx:68
-#, c-format
-msgid "Amount is not valid"
-msgstr ""
-
-#: src/pages/home/PaytoWireTransferForm.tsx:70
-#, c-format
-msgid "Should be greater than 0"
-msgstr ""
-
-#: src/pages/home/PaytoWireTransferForm.tsx:79
-#, c-format
-msgid "Receiver IBAN:"
-msgstr ""
-
-#: src/pages/home/PaytoWireTransferForm.tsx:102
-#, c-format
-msgid "Transfer subject:"
-msgstr ""
-
-#: src/pages/home/PaytoWireTransferForm.tsx:123
-#, fuzzy, c-format
-msgid "Amount:"
-msgstr "Somma"
-
-#: src/pages/home/PaytoWireTransferForm.tsx:177
-#, c-format
-msgid "Field(s) missing."
-msgstr ""
-
-#: src/pages/home/PaytoWireTransferForm.tsx:227
-#, c-format
-msgid "Want to try the raw payto://-format?"
-msgstr "Prova il trasferimento tramite il formato Payto!"
-
-#: src/pages/home/PaytoWireTransferForm.tsx:235
-#, fuzzy, c-format
-msgid "Missing payto address"
-msgstr "indirizzo Payto"
-
-#: src/pages/home/PaytoWireTransferForm.tsx:237
-#, c-format
-msgid "Payto does not follow the pattern"
-msgstr ""
-
-#: src/pages/home/PaytoWireTransferForm.tsx:243
-#, fuzzy, c-format
-msgid "Transfer money to account identified by payto:// URI:"
-msgstr "Trasferisci fondi a un altro conto di questa banca:"
-
-#: src/pages/home/PaytoWireTransferForm.tsx:246
-#, c-format
-msgid "payto URI:"
-msgstr ""
-
-#: src/pages/home/PaytoWireTransferForm.tsx:255
-#, c-format
-msgid "payto address"
-msgstr "indirizzo Payto"
-
-#: src/pages/home/PaytoWireTransferForm.tsx:279
-#, c-format
-msgid "Send"
-msgstr ""
-
-#: src/pages/home/PaytoWireTransferForm.tsx:314
-#, fuzzy, c-format
-msgid "Use wire-transfer form?"
-msgstr "Chiudi il bonifico"
-
-#: src/pages/home/PaytoWireTransferForm.tsx:373
-#, fuzzy, c-format
-msgid "No credentials found."
-msgstr "Credenziali invalide."
-
-#: src/pages/home/PaytoWireTransferForm.tsx:397
-#, c-format
-msgid "Could not create the wire transfer"
-msgstr ""
-
-#: src/pages/home/PaytoWireTransferForm.tsx:414
-#, c-format
-msgid "Transfer creation gave response error"
-msgstr ""
-
-#: src/pages/home/PaytoWireTransferForm.tsx:426
-#, fuzzy, c-format
-msgid "Wire transfer created!"
-msgstr "Bonifico"
-
-#: src/pages/home/WalletWithdrawForm.tsx:50
-#, fuzzy, c-format
-msgid "Amount to withdraw:"
-msgstr "Somma da ritirare"
-
-#: src/pages/home/WalletWithdrawForm.tsx:84
-#, c-format
-msgid "Withdraw"
-msgstr "Prelevare"
-
-#: src/pages/home/WalletWithdrawForm.tsx:128
-#, fuzzy, c-format
-msgid "No credentials given."
-msgstr "Credenziali invalide."
-
-#: src/pages/home/WalletWithdrawForm.tsx:155
-#, c-format
-msgid "Could not create withdrawal operation"
-msgstr ""
-
-#: src/pages/home/WalletWithdrawForm.tsx:171
-#, c-format
-msgid "Withdrawal creation gave response error"
-msgstr ""
-
-#: src/pages/home/PaymentOptions.tsx:44
-#, c-format
-msgid "Obtain digital cash"
-msgstr ""
-
-#: src/pages/home/PaymentOptions.tsx:52
-#, fuzzy, c-format
-msgid "Transfer to bank account"
-msgstr "Trasferisci fondi a un altro conto di questa banca:"
-
-#: src/pages/home/Transactions.tsx:69
-#, c-format
-msgid "Date"
-msgstr "Data"
-
-#: src/pages/home/Transactions.tsx:70
-#, c-format
-msgid "Amount"
-msgstr "Importo"
-
-#: src/pages/home/Transactions.tsx:71
-#, c-format
-msgid "Counterpart"
-msgstr "Controparte"
-
-#: src/pages/home/Transactions.tsx:72
-#, c-format
-msgid "Subject"
-msgstr "Soggetto"
-
-#: src/pages/home/QrCodeSection.tsx:41
-#, fuzzy, c-format
-msgid "Transfer to Taler Wallet"
-msgstr "Ritira contante nel portafoglio Taler"
-
-#: src/pages/home/QrCodeSection.tsx:44
-#, fuzzy, c-format
-msgid "Use this QR code to withdraw to your mobile wallet:"
-msgstr "Usa questo codice QR per ritirare contante nel tuo wallet:"
-
-#: src/pages/home/QrCodeSection.tsx:47
-#, c-format
-msgid "Click %1$s to open your Taler wallet!"
-msgstr ""
-
-#: src/pages/home/WithdrawalConfirmationQuestion.tsx:47
-#, c-format
-msgid "Confirm Withdrawal"
-msgstr "Conferma il ritiro"
-
-#: src/pages/home/WithdrawalConfirmationQuestion.tsx:52
-#, c-format
-msgid "Authorize withdrawal by solving challenge"
-msgstr ""
-
-#: src/pages/home/WithdrawalConfirmationQuestion.tsx:55
-#, c-format
-msgid "What is"
-msgstr ""
-
-#: src/pages/home/WithdrawalConfirmationQuestion.tsx:94
-#, c-format
-msgid "Answer is wrong."
-msgstr ""
-
-#: src/pages/home/WithdrawalConfirmationQuestion.tsx:99
-#, c-format
-msgid "Confirm"
-msgstr "Conferma"
-
-#: src/pages/home/WithdrawalConfirmationQuestion.tsx:113
-#, c-format
-msgid "Cancel"
-msgstr ""
-
-#: src/pages/home/WithdrawalConfirmationQuestion.tsx:120
-#, c-format
-msgid ""
-"A this point, a %1$s bank would ask for an additional authentication proof "
-"(PIN/TAN, one time password, ..), instead of a simple calculation."
-msgstr ""
-
-#: src/pages/home/WithdrawalConfirmationQuestion.tsx:166
-#, c-format
-msgid "No withdrawal ID found."
-msgstr ""
-
-#: src/pages/home/WithdrawalConfirmationQuestion.tsx:201
-#, fuzzy, c-format
-msgid "Could not confirm the withdrawal"
-msgstr "Conferma il ritiro"
-
-#: src/pages/home/WithdrawalConfirmationQuestion.tsx:219
-#, c-format
-msgid "Withdrawal confirmation gave response error"
-msgstr ""
-
-#: src/pages/home/WithdrawalConfirmationQuestion.tsx:231
-#, fuzzy, c-format
-msgid "Withdrawal confirmed!"
-msgstr "Questo ritiro è stato annullato!"
-
-#: src/pages/home/WithdrawalConfirmationQuestion.tsx:294
-#, fuzzy, c-format
-msgid "Could not abort the withdrawal."
-msgstr "Chiudi il ritiro Taler"
-
-#: src/pages/home/WithdrawalConfirmationQuestion.tsx:311
-#, fuzzy, c-format
-msgid "Withdrawal abortion failed."
-msgstr "Questo ritiro è stato annullato!"
-
-#: src/pages/home/WithdrawalConfirmationQuestion.tsx:324
-#, fuzzy, c-format
-msgid "Withdrawal aborted!"
-msgstr "Questo ritiro è stato annullato!"
-
-#: src/pages/home/WithdrawalQRCode.tsx:54
-#, c-format
-msgid "Abort"
-msgstr "Annulla"
-
-#: src/pages/home/WithdrawalQRCode.tsx:74
-#, c-format
-msgid "withdrawal (%1$s) was never (correctly) created at the bank..."
-msgstr ""
-
-#: src/pages/home/WithdrawalQRCode.tsx:88
-#, fuzzy, c-format
-msgid "Waiting the bank to create the operation..."
-msgstr "La banca sta creando l'operazione..."
-
-#: src/pages/home/WithdrawalQRCode.tsx:102
-#, c-format
-msgid "This withdrawal was aborted!"
-msgstr "Questo ritiro è stato annullato!"
-
-#: src/pages/home/AccountPage.tsx:40
-#, c-format
-msgid "Welcome to %1$s!"
-msgstr ""
-
-#: src/pages/home/AccountPage.tsx:133
-#, c-format
-msgid "Username or account label '%1$s' not found. Won't login."
-msgstr "L'utente '%1$s' non esiste. Login impossibile"
-
-#: src/pages/home/AccountPage.tsx:159
-#, c-format
-msgid "Wrong credentials given."
-msgstr "Credenziali invalide."
-
-#: src/pages/home/AccountPage.tsx:169
-#, c-format
-msgid "Account information could not be retrieved."
-msgstr "Impossibile ricevere le informazioni relative al conto."
-
-#: src/pages/home/AccountPage.tsx:210
-#, c-format
-msgid "Welcome, %1$s !"
-msgstr ""
-
-#: src/pages/home/AccountPage.tsx:221
-#, fuzzy, c-format
-msgid "Bank account balance"
-msgstr "Bilancio:"
-
-#: src/pages/home/AccountPage.tsx:237
-#, c-format
-msgid "Payments"
-msgstr ""
-
-#: src/pages/home/AccountPage.tsx:243
-#, c-format
-msgid "Latest transactions:"
-msgstr "Ultime transazioni:"
-
-#: src/pages/home/PublicHistoriesPage.tsx:83
-#, c-format
-msgid "List of public accounts was not found."
-msgstr "Lista conti pubblici non trovata."
-
-#: src/pages/home/PublicHistoriesPage.tsx:95
-#, c-format
-msgid "List of public accounts could not be retrieved."
-msgstr "Lista conti pubblici non pervenuta."
-
-#: src/pages/home/PublicHistoriesPage.tsx:143
-#, c-format
-msgid "History of public accounts"
-msgstr "Storico dei conti pubblici"
-
-#: src/pages/home/RegistrationPage.tsx:39
-#, c-format
-msgid "Currently, the bank is not accepting new registrations!"
-msgstr ""
-
-#: src/pages/home/RegistrationPage.tsx:68
-#, c-format
-msgid "Use only letter and numbers starting with a lower case letter"
-msgstr ""
-
-#: src/pages/home/RegistrationPage.tsx:78
-#, c-format
-msgid "Password don't match"
-msgstr ""
-
-#: src/pages/home/RegistrationPage.tsx:89
-#, fuzzy, c-format
-msgid "Please register!"
-msgstr "Accedi!"
-
-#: src/pages/home/RegistrationPage.tsx:126
-#, c-format
-msgid "Repeat Password:"
-msgstr ""
-
-#: src/pages/home/RegistrationPage.tsx:226
-#, fuzzy, c-format
-msgid "Registration failed, please report"
-msgstr "Registrazione"
-
-#: src/pages/home/RegistrationPage.tsx:239
-#, c-format
-msgid "That username is already taken"
-msgstr ""
-
-#: src/pages/home/RegistrationPage.tsx:248
-#, c-format
-msgid "New registration gave response error"
-msgstr ""
-
-#: src/components/menu/SideBar.tsx:53
-#, c-format
-msgid "Bank menu"
-msgstr ""
-
-#: src/components/menu/SideBar.tsx:59
-#, c-format
-msgid "Select option1"
-msgstr ""
-
-#: src/components/menu/SideBar.tsx:66
-#, c-format
-msgid "Select option2"
-msgstr ""
-
-#: src/components/picker/DurationPicker.tsx:55
-#, c-format
-msgid "days"
-msgstr ""
-
-#: src/components/picker/DurationPicker.tsx:65
-#, c-format
-msgid "hours"
-msgstr ""
-
-#: src/components/picker/DurationPicker.tsx:76
-#, c-format
-msgid "minutes"
-msgstr ""
-
-#: src/components/picker/DurationPicker.tsx:87
-#, c-format
-msgid "seconds"
-msgstr ""
-
-#~ msgid "this link"
-#~ msgstr "questo link"
-
-#~ msgid "Clear"
-#~ msgstr "Cancella"
-
-#~ msgid "Demo Bank"
-#~ msgstr "Banca 'demo'"
-
-#~ msgid "Go back"
-#~ msgstr "Indietro"
-
-#~ msgid "Transfer money via the Payto system:"
-#~ msgstr "Effettua un bonifico tramite il sistema Payto:"
-
-#~ msgid "Start withdrawal"
-#~ msgstr "Ritira contante"
-
-#~ msgid "Withdraw Money into a Taler wallet"
-#~ msgstr "Ritira contante nel portafoglio Taler"
-
-#~ msgid "Register to the euFin bank!"
-#~ msgstr "Apri un conto in banca euFin!"
-
-#~ msgid "Transfer money manually"
-#~ msgstr "Effettua un bonifico"
-
-#~ msgid "Page has a problem: logged in but backend state is lost."
-#~ msgstr ""
-#~ "Stato inconsistente: accesso utente effettuato ma stato con server perso."
-
-#, fuzzy
-#~ msgid "Welcome to the euFin bank!"
-#~ msgstr "Benvenuti in banca euFin!"
diff --git a/packages/demobank-ui/src/i18n/poheader b/packages/demobank-ui/src/i18n/poheader
deleted file mode 100644
index a251e9584..000000000
--- a/packages/demobank-ui/src/i18n/poheader
+++ /dev/null
@@ -1,26 +0,0 @@
-# This file is part of GNU Taler
-# (C) 2022 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/>
-#
-#, fuzzy
-msgid ""
-msgstr ""
-"Project-Id-Version: Taler Bank\n"
-"Report-Msgid-Bugs-To: taler@gnu.org\n"
-"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
-"Language-Team: LANGUAGE <LL@li.org>\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"Plural-Forms: nplurals=2; plural=(n != 1);\n"
diff --git a/packages/demobank-ui/src/i18n/strings-prelude b/packages/demobank-ui/src/i18n/strings-prelude
deleted file mode 100644
index a0aeb8268..000000000
--- a/packages/demobank-ui/src/i18n/strings-prelude
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2022 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/>
- */
-
-/*eslint quote-props: ["error", "consistent"]*/
-export const strings: {[s: string]: any} = {};
-
diff --git a/packages/demobank-ui/src/i18n/strings.ts b/packages/demobank-ui/src/i18n/strings.ts
deleted file mode 100644
index 86d1fff5b..000000000
--- a/packages/demobank-ui/src/i18n/strings.ts
+++ /dev/null
@@ -1,510 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2022 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/>
- */
-
-/*eslint quote-props: ["error", "consistent"]*/
-export const strings: { [s: string]: any } = {};
-
-strings["de"] = {
- domain: "messages",
- locale_data: {
- messages: {
- "": {
- domain: "messages",
- plural_forms: "nplurals=2; plural=(n != 1);",
- lang: "de",
- },
- Logout: [""],
- "Skip to main content": [""],
- "This part of the demo shows how a bank that supports Taler directly would work. In addition to using your own bank account, you can also see the transaction history of some %1$s.":
- [""],
- "Taler logo": [""],
- "Missing username": [""],
- "Missing password": [""],
- "Please login!": [""],
- "Username:": [""],
- "Password:": [""],
- Login: [""],
- Register: [""],
- "Missing IBAN": [""],
- "IBAN should have just uppercased letters and numbers": [""],
- "Missing subject": [""],
- "Missing amount": [""],
- "Amount is not valid": [""],
- "Should be greater than 0": [""],
- "Receiver IBAN:": [""],
- "Transfer subject:": [""],
- "Amount:": [""],
- "Field(s) missing.": [""],
- "Want to try the raw payto://-format?": [""],
- "Missing payto address": [""],
- "Payto does not follow the pattern": [""],
- "Transfer money to account identified by payto:// URI:": [""],
- "payto URI:": [""],
- "payto address": [""],
- Send: [""],
- "Use wire-transfer form?": [""],
- "No credentials found.": [""],
- "Could not create the wire transfer": [""],
- "Transfer creation gave response error": [""],
- "Wire transfer created!": [""],
- "Amount to withdraw:": [""],
- Withdraw: [""],
- "No credentials given.": [""],
- "Could not create withdrawal operation": [""],
- "Withdrawal creation gave response error": [""],
- "Obtain digital cash": [""],
- "Transfer to bank account": [""],
- Date: [""],
- Amount: [""],
- Counterpart: [""],
- Subject: [""],
- "Transfer to Taler Wallet": [""],
- "Use this QR code to withdraw to your mobile wallet:": [""],
- "Click %1$s to open your Taler wallet!": [""],
- "Confirm Withdrawal": [""],
- "Authorize withdrawal by solving challenge": [""],
- "What is": [""],
- "Answer is wrong.": [""],
- Confirm: [""],
- Cancel: [""],
- "A this point, a %1$s bank would ask for an additional authentication proof (PIN/TAN, one time password, ..), instead of a simple calculation.":
- [""],
- "No withdrawal ID found.": [""],
- "Could not confirm the withdrawal": [""],
- "Withdrawal confirmation gave response error": [""],
- "Withdrawal confirmed!": [""],
- "Could not abort the withdrawal.": [""],
- "Withdrawal abortion failed.": [""],
- "Withdrawal aborted!": [""],
- Abort: [""],
- "withdrawal (%1$s) was never (correctly) created at the bank...": [""],
- "Waiting the bank to create the operation...": [""],
- "This withdrawal was aborted!": [""],
- "Welcome to %1$s!": [""],
- "Username or account label '%1$s' not found. Won't login.": [""],
- "Wrong credentials given.": [""],
- "Account information could not be retrieved.": [""],
- "Welcome, %1$s !": [""],
- "Bank account balance": [""],
- Payments: [""],
- "Latest transactions:": [""],
- "List of public accounts was not found.": [""],
- "List of public accounts could not be retrieved.": [""],
- "History of public accounts": [""],
- "Currently, the bank is not accepting new registrations!": [""],
- "Use only letter and numbers starting with a lower case letter": [""],
- "Password don't match": [""],
- "Please register!": [""],
- "Repeat Password:": [""],
- "Registration failed, please report": [""],
- "That username is already taken": [""],
- "New registration gave response error": [""],
- "Bank menu": [""],
- "Select option1": [""],
- "Select option2": [""],
- days: [""],
- hours: [""],
- minutes: [""],
- seconds: [""],
- },
- },
-};
-
-strings["en"] = {
- domain: "messages",
- locale_data: {
- messages: {
- "": {
- domain: "messages",
- plural_forms: "nplurals=2; plural=(n != 1);",
- lang: "en",
- },
- Logout: [""],
- "Skip to main content": [""],
- "This part of the demo shows how a bank that supports Taler directly would work. In addition to using your own bank account, you can also see the transaction history of some %1$s.":
- [""],
- "Taler logo": [""],
- "Missing username": [""],
- "Missing password": [""],
- "Please login!": [""],
- "Username:": [""],
- "Password:": [""],
- Login: [""],
- Register: [""],
- "Missing IBAN": [""],
- "IBAN should have just uppercased letters and numbers": [""],
- "Missing subject": [""],
- "Missing amount": [""],
- "Amount is not valid": [""],
- "Should be greater than 0": [""],
- "Receiver IBAN:": [""],
- "Transfer subject:": [""],
- "Amount:": [""],
- "Field(s) missing.": [""],
- "Want to try the raw payto://-format?": [""],
- "Missing payto address": [""],
- "Payto does not follow the pattern": [""],
- "Transfer money to account identified by payto:// URI:": [""],
- "payto URI:": [""],
- "payto address": [""],
- Send: [""],
- "Use wire-transfer form?": [""],
- "No credentials found.": [""],
- "Could not create the wire transfer": [""],
- "Transfer creation gave response error": [""],
- "Wire transfer created!": [""],
- "Amount to withdraw:": ["Amount to withdraw"],
- Withdraw: ["Withdraw"],
- "No credentials given.": [""],
- "Could not create withdrawal operation": [""],
- "Withdrawal creation gave response error": [""],
- "Obtain digital cash": [""],
- "Transfer to bank account": [""],
- Date: [""],
- Amount: [""],
- Counterpart: [""],
- Subject: [""],
- "Transfer to Taler Wallet": ["Top up Taler wallet"],
- "Use this QR code to withdraw to your mobile wallet:": [""],
- "Click %1$s to open your Taler wallet!": [""],
- "Confirm Withdrawal": ["Confirm withdrawal"],
- "Authorize withdrawal by solving challenge": [""],
- "What is": [""],
- "Answer is wrong.": [""],
- Confirm: [""],
- Cancel: [""],
- "A this point, a %1$s bank would ask for an additional authentication proof (PIN/TAN, one time password, ..), instead of a simple calculation.":
- [""],
- "No withdrawal ID found.": [""],
- "Could not confirm the withdrawal": ["Confirm withdrawal"],
- "Withdrawal confirmation gave response error": [""],
- "Withdrawal confirmed!": [""],
- "Could not abort the withdrawal.": ["Close Taler withdrawal"],
- "Withdrawal abortion failed.": [""],
- "Withdrawal aborted!": [""],
- Abort: [""],
- "withdrawal (%1$s) was never (correctly) created at the bank...": [""],
- "Waiting the bank to create the operation...": [""],
- "This withdrawal was aborted!": [""],
- "Welcome to %1$s!": [""],
- "Username or account label '%1$s' not found. Won't login.": [""],
- "Wrong credentials given.": [""],
- "Account information could not be retrieved.": [""],
- "Welcome, %1$s !": [""],
- "Bank account balance": [""],
- Payments: [""],
- "Latest transactions:": [""],
- "List of public accounts was not found.": [""],
- "List of public accounts could not be retrieved.": [""],
- "History of public accounts": [""],
- "Currently, the bank is not accepting new registrations!": [""],
- "Use only letter and numbers starting with a lower case letter": [""],
- "Password don't match": [""],
- "Please register!": [""],
- "Repeat Password:": [""],
- "Registration failed, please report": [""],
- "That username is already taken": [""],
- "New registration gave response error": [""],
- "Bank menu": [""],
- "Select option1": [""],
- "Select option2": [""],
- days: ["days"],
- hours: ["hours"],
- minutes: ["minutes"],
- seconds: ["seconds"],
- },
- },
-};
-
-strings["es"] = {
- domain: "messages",
- locale_data: {
- messages: {
- "": {
- domain: "messages",
- plural_forms: "nplurals=2; plural=n != 1;",
- lang: "es",
- },
- Logout: ["Cierre de sesión"],
- "Skip to main content": ["Saltar el menú de navegación"],
- "This part of the demo shows how a bank that supports Taler directly would work. In addition to using your own bank account, you can also see the transaction history of some %1$s.":
- [
- "Esta parte de la demostración muestra cómo funciona un banco que soporta Taler directamente. Además de usar tu propia cuenta de banco, también podrás ver el historial de transacciones de algunas %1$s.",
- ],
- "Taler logo": ["Logo Taler"],
- "Missing username": ["Falta nombre de usuario"],
- "Missing password": ["Falta contraseña"],
- "Please login!": ["Por favor inicia sesión!"],
- "Username:": ["Nombre de usuario:"],
- "Password:": ["Password:"],
- Login: ["Iniciar sesión"],
- Register: ["Registrarse"],
- "Missing IBAN": ["Falta IBAN"],
- "IBAN should have just uppercased letters and numbers": [
- "IBAN debería tener letras mayúsculas y números",
- ],
- "Missing subject": ["Falta asunto"],
- "Missing amount": ["Falta monto"],
- "Amount is not valid": ["Monto no válido"],
- "Should be greater than 0": ["Debería ser mas grande que 0"],
- "Receiver IBAN:": ["IBAN receptor:"],
- "Transfer subject:": ["Asunto de transferencia:"],
- "Amount:": ["Monto:"],
- "Field(s) missing.": ["Faltan campo(s)."],
- "Want to try the raw payto://-format?": [
- "Quieres probar el formato payto:// ?",
- ],
- "Missing payto address": ["Falta direccion payto"],
- "Payto does not follow the pattern": ["Payto no sigue el patrón"],
- "Transfer money to account identified by payto:// URI:": [
- "Transferir dinero a la cuenta identificada por la URI payto://:",
- ],
- "payto URI:": ["payto URI:"],
- "payto address": ["direccion payto"],
- Send: ["Envíar"],
- "Use wire-transfer form?": [
- "Usar el formulario de transferencia bancaria?",
- ],
- "No credentials found.": ["Se dieron las credenciales incorrectas."],
- "Could not create the wire transfer": [
- "No se pudo create la transferencia bancaria",
- ],
- "Transfer creation gave response error": [
- "La creación de la transferencia dió una respuesta erronea",
- ],
- "Wire transfer created!": ["Transferencia bancaria creada!"],
- "Amount to withdraw:": ["Monto a retirar:"],
- Withdraw: ["Retirar"],
- "No credentials given.": ["Se dieron las credenciales incorrectas."],
- "Could not create withdrawal operation": [
- "No se pude create la operación de retiro",
- ],
- "Withdrawal creation gave response error": [
- "La creación de retiro dió una respuesta errónea",
- ],
- "Obtain digital cash": ["Obtener dinero digital"],
- "Transfer to bank account": ["Transferir a una cuenta bancaria"],
- Date: ["Fecha"],
- Amount: ["Monto"],
- Counterpart: ["Contraparte"],
- Subject: ["Asunto"],
- "Transfer to Taler Wallet": ["Transferir a una cartera Taler"],
- "Use this QR code to withdraw to your mobile wallet:": [
- "Usar el código QR para retirar a tu cartera móvil:",
- ],
- "Click %1$s to open your Taler wallet!": [
- "Click %1$s para abrir una cartera Taler!",
- ],
- "Confirm Withdrawal": ["Confirmar retirada"],
- "Authorize withdrawal by solving challenge": [
- "Autorizar retiro resolviendo una pregunta",
- ],
- "What is": ["Cuanto es"],
- "Answer is wrong.": ["La respuesta es incorrecta."],
- Confirm: ["Confirmar"],
- Cancel: ["Cancelar"],
- "A this point, a %1$s bank would ask for an additional authentication proof (PIN/TAN, one time password, ..), instead of a simple calculation.":
- [
- "En este punto, un banco %1$s preguntaría por una prueba adicional de autenticación (PIN/TAN, password de un solo uso, ....), en vez de un simple cálculo.",
- ],
- "No withdrawal ID found.": ["No ID de retiro encontrado."],
- "Could not confirm the withdrawal": ["No se pudo confirmar la retirada"],
- "Withdrawal confirmation gave response error": [
- "La confirmación de retiro dió una respuesta errónea",
- ],
- "Withdrawal confirmed!": ["El retiro fue confirmado!"],
- "Could not abort the withdrawal.": ["No se pudo cancelar el retiro."],
- "Withdrawal abortion failed.": ["La cancelación del retiro falló."],
- "Withdrawal aborted!": ["Este retiro fue cancelado!"],
- Abort: ["Cancelar"],
- "withdrawal (%1$s) was never (correctly) created at the bank...": [
- "retiro (%1$s) nunca fue (correctamente) generado en el banco...",
- ],
- "Waiting the bank to create the operation...": [
- "Esperando que el banco genere la operación....",
- ],
- "This withdrawal was aborted!": ["Este retiro fue cancelado!"],
- "Welcome to %1$s!": ["Bienvenido a %1$s!"],
- "Username or account label '%1$s' not found. Won't login.": [
- "Nombre de usuario o etiqueta de cuenta '%1$s' no encontrada. No se iniciará sesión.",
- ],
- "Wrong credentials given.": ["Se dieron las credenciales incorrectas."],
- "Account information could not be retrieved.": [
- "La información de la cuenta no pudo ser accedida.",
- ],
- "Welcome, %1$s !": ["Bienvenido/a, %1$s!"],
- "Bank account balance": ["Balance de cuenta bancaria"],
- Payments: ["Pagos"],
- "Latest transactions:": ["Últimas transacciones:"],
- "List of public accounts was not found.": [
- "La lista de cuentas públicas no fue encontrada.",
- ],
- "List of public accounts could not be retrieved.": [
- "La lista de cuentas públicas no pudo ser accedida.",
- ],
- "History of public accounts": ["Historial de cuentas públicas"],
- "Currently, the bank is not accepting new registrations!": [
- "Actualmente, el banco no está aceptado nuevos registros!",
- ],
- "Use only letter and numbers starting with a lower case letter": [
- "Solo use letras y números comenzando con una letra minúscula",
- ],
- "Password don't match": ["La contraseña no coincide"],
- "Please register!": ["Por favor, registrese!"],
- "Repeat Password:": ["Repita la contraseña:"],
- "Registration failed, please report": [
- "El registro falló, por favor reportelo",
- ],
- "That username is already taken": [
- "El nombre del usuario ya está tomado",
- ],
- "New registration gave response error": [
- "Nuevo registro dió una respuesta errónea",
- ],
- "Bank menu": ["Menu del banco"],
- "Select option1": ["Seleccione opción 1"],
- "Select option2": ["Seleccione opción 2"],
- days: ["días"],
- hours: ["horas"],
- minutes: ["minutos"],
- seconds: ["segundos"],
- },
- },
-};
-
-strings["it"] = {
- domain: "messages",
- locale_data: {
- messages: {
- "": {
- domain: "messages",
- plural_forms: "nplurals=2; plural=(n != 1);",
- lang: "it",
- },
- Logout: [""],
- "Skip to main content": [""],
- "This part of the demo shows how a bank that supports Taler directly would work. In addition to using your own bank account, you can also see the transaction history of some %1$s.":
- [""],
- "Taler logo": [""],
- "Missing username": [""],
- "Missing password": [""],
- "Please login!": ["Accedi!"],
- "Username:": [""],
- "Password:": [""],
- Login: ["Accedi"],
- Register: ["Registrati"],
- "Missing IBAN": [""],
- "IBAN should have just uppercased letters and numbers": [""],
- "Missing subject": [""],
- "Missing amount": [""],
- "Amount is not valid": [""],
- "Should be greater than 0": [""],
- "Receiver IBAN:": [""],
- "Transfer subject:": [""],
- "Amount:": ["Somma"],
- "Field(s) missing.": [""],
- "Want to try the raw payto://-format?": [
- "Prova il trasferimento tramite il formato Payto!",
- ],
- "Missing payto address": ["indirizzo Payto"],
- "Payto does not follow the pattern": [""],
- "Transfer money to account identified by payto:// URI:": [
- "Trasferisci fondi a un altro conto di questa banca:",
- ],
- "payto URI:": [""],
- "payto address": ["indirizzo Payto"],
- Send: [""],
- "Use wire-transfer form?": ["Chiudi il bonifico"],
- "No credentials found.": ["Credenziali invalide."],
- "Could not create the wire transfer": [""],
- "Transfer creation gave response error": [""],
- "Wire transfer created!": ["Bonifico"],
- "Amount to withdraw:": ["Somma da ritirare"],
- Withdraw: ["Conferma il ritiro"],
- "No credentials given.": ["Credenziali invalide."],
- "Could not create withdrawal operation": [""],
- "Withdrawal creation gave response error": [""],
- "Obtain digital cash": [""],
- "Transfer to bank account": [
- "Trasferisci fondi a un altro conto di questa banca:",
- ],
- Date: [""],
- Amount: ["Somma"],
- Counterpart: ["Controparte"],
- Subject: ["Causale"],
- "Transfer to Taler Wallet": ["Ritira contante nel portafoglio Taler"],
- "Use this QR code to withdraw to your mobile wallet:": [
- "Usa questo codice QR per ritirare contante nel tuo wallet:",
- ],
- "Click %1$s to open your Taler wallet!": [""],
- "Confirm Withdrawal": ["Conferma il ritiro"],
- "Authorize withdrawal by solving challenge": [""],
- "What is": [""],
- "Answer is wrong.": [""],
- Confirm: ["Conferma"],
- Cancel: [""],
- "A this point, a %1$s bank would ask for an additional authentication proof (PIN/TAN, one time password, ..), instead of a simple calculation.":
- [""],
- "No withdrawal ID found.": [""],
- "Could not confirm the withdrawal": ["Conferma il ritiro"],
- "Withdrawal confirmation gave response error": [""],
- "Withdrawal confirmed!": ["Questo ritiro è stato annullato!"],
- "Could not abort the withdrawal.": ["Chiudi il ritiro Taler"],
- "Withdrawal abortion failed.": ["Questo ritiro è stato annullato!"],
- "Withdrawal aborted!": ["Questo ritiro è stato annullato!"],
- Abort: ["Annulla"],
- "withdrawal (%1$s) was never (correctly) created at the bank...": [""],
- "Waiting the bank to create the operation...": [
- "La banca sta creando l'operazione...",
- ],
- "This withdrawal was aborted!": ["Questo ritiro è stato annullato!"],
- "Welcome to %1$s!": [""],
- "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.",
- ],
- "Welcome, %1$s !": [""],
- "Bank account balance": ["Bilancio:"],
- Payments: [""],
- "Latest transactions:": ["Ultime transazioni:"],
- "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"],
- "Currently, the bank is not accepting new registrations!": [""],
- "Use only letter and numbers starting with a lower case letter": [""],
- "Password don't match": [""],
- "Please register!": ["Accedi!"],
- "Repeat Password:": [""],
- "Registration failed, please report": ["Registrazione"],
- "That username is already taken": [""],
- "New registration gave response error": [""],
- "Bank menu": [""],
- "Select option1": [""],
- "Select option2": [""],
- days: [""],
- hours: [""],
- minutes: [""],
- seconds: [""],
- },
- },
-};
diff --git a/packages/demobank-ui/src/index.html b/packages/demobank-ui/src/index.html
deleted file mode 100644
index 3cc7f7fd2..000000000
--- a/packages/demobank-ui/src/index.html
+++ /dev/null
@@ -1,41 +0,0 @@
-<!--
- This file is part of GNU Taler
- (C) 2021--2022 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
--->
-<!DOCTYPE html>
-<html lang="en" class="h-full bg-gray-100">
-
-<head>
- <meta http-equiv="content-type" content="text/html; charset=utf-8" />
- <meta charset="utf-8" />
- <meta name="viewport" content="width=device-width,initial-scale=1" />
- <meta name="taler-support" content="uri">
- <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/Vr5T/48i2/8J+VP/Qn3//3ryn/795Tf/WrpP/2LCW/8B6T//w4Nb///////Pn4P+/d0v/9u3n/+7d0v/EhV7//v///+HDr//fxLD/zph2/+TJt//8/Pv/woBX//Lm3f/y5dz/v3hN//bu6f/JjGn/4sW0///////Df1j/8OLZ//v6+P+/elH/+vj1//jy7f+/elL//////+zYzP/Eg13//////967p//MlHT/wn5X///////v4Nb/yY1s///////jw7H/06KG////////////z5t9/+fNvf//////x4pn//Pp4v/8+vn/w39X/8WEX///////5s/A/9CbfP//////27Oc/9y2n////////////9itlf/gu6f//////86Vdf/r2Mz//////8SCXP/Df1j//////+7d0v/KkG7//////+HBrf/VpYr////////////RnoH/5sq6///////Ii2n/8ubf//39/P/Cf1j/xohk/+bNvv//////wn5W//Tq4//58/D/wHxV//7+/f/59fH/v3xU//39/P/w4Nf/xIFb///////hw7H/yo9t/+/f1f/AeU3/+/n2/+nSxP/FhmD//////9qzm//Upon/4MSx/96+qf//////xINc/+3bz//48e3/v3hN//Pn3///////6M+//752S//gw6//06aK/8J+VP/kzLr/zZd1/8OCWv/q18r/17KZ/9Ooi//fv6r/v3dK/+vWyP///////v39///////27un/1aeK/9Opjv/m1cf/1KCC/9a0nP/n08T/0Jx8/82YdP/QnHz/16yR//jx7P///////v39///////+/f3///7+///////+//7//v7+///////+/v7//v/+/////////////////////////v7//v79///////////////////+/v/+/Pv//v39///+/v/+/Pv///7+//7+/f/+/Pv//v39//79/P/+/Pv///7+////////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==" />
- <link rel="shortcut icon" href="data:image/x-icon;," type="image/x-icon" />
- <title>Bank</title>
- <!-- Entry point for the bank SPA. -->
- <script type="module" src="index.js"></script>
- <link rel="stylesheet" href="index.css" />
-</head>
-
-<body class="h-full">
- <div id="app"></div>
-</body>
-
-</html> \ No newline at end of file
diff --git a/packages/demobank-ui/src/index.tsx b/packages/demobank-ui/src/index.tsx
deleted file mode 100644
index b7d69fd2d..000000000
--- a/packages/demobank-ui/src/index.tsx
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2022 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 App from "./components/app.js";
-import { h, render } from "preact";
-import "./scss/main.css"
-
-const app = document.getElementById("app");
-
-render(<App />, app as any);
diff --git a/packages/demobank-ui/src/manifest.json b/packages/demobank-ui/src/manifest.json
deleted file mode 100644
index 8790b10c9..000000000
--- a/packages/demobank-ui/src/manifest.json
+++ /dev/null
@@ -1,21 +0,0 @@
-{
- "name": "taler-bank",
- "short_name": "taler-bank",
- "start_url": "/",
- "display": "standalone",
- "orientation": "portrait",
- "background_color": "#fff",
- "theme_color": "#673ab8",
- "icons": [
- {
- "src": "/assets/icons/android-chrome-192x192.png",
- "type": "image/png",
- "sizes": "192x192"
- },
- {
- "src": "/assets/icons/android-chrome-512x512.png",
- "type": "image/png",
- "sizes": "512x512"
- }
- ]
-}
diff --git a/packages/demobank-ui/src/pages.ts b/packages/demobank-ui/src/pages.ts
deleted file mode 100644
index cf003fde5..000000000
--- a/packages/demobank-ui/src/pages.ts
+++ /dev/null
@@ -1,44 +0,0 @@
-import { WithdrawalOperationPage } from "./pages/WithdrawalOperationPage.js";
-import { PageEntry, pageDefinition } from "./route.js";
-
-// const operationById: PageEntry<{ operationId: string }> = {
-// url: pageDefinition("#/operation/:operationId"),
-// view: WithdrawalOperationPage,
-// };
-
-
-// const home: PageEntry = {
-// url: "#/",
-// view: Home,
-// };
-// const cases: PageEntry = {
-// url: "#/cases",
-// view: Cases,
-// };
-
-// const newFormEntry: PageEntry<{ account?: string; type?: string }> = {
-// url: pageDefinition("#/account/:account/new/:type?"),
-// view: NewFormEntry,
-// };
-
-// const settings: PageEntry = {
-// url: "#/settings",
-// view: Settings,
-// };
-// const officer: PageEntry = {
-// url: "#/officer",
-// view: Officer,
-// };
-// const welcome: PageEntry<{ asd?: string; name?: string }> = {
-// url: pageDefinition("#/welcome/:name?"),
-// view: Welcome,
-// };
-// const form: PageEntry<{ number?: string }> = {
-// url: pageDefinition("#/form/:number?"),
-// view: AntiMoneyLaunderingForm,
-// };
-
-export const Pages = {
- // operationById,
-
-};
diff --git a/packages/demobank-ui/src/pages/AccountPage/index.ts b/packages/demobank-ui/src/pages/AccountPage/index.ts
deleted file mode 100644
index 7261af69a..000000000
--- a/packages/demobank-ui/src/pages/AccountPage/index.ts
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2022 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 { AbsoluteTime, AmountJson, TalerCorebankApi, TalerError } from "@gnu-taler/taler-util";
-import { Loading, utils } from "@gnu-taler/web-util/browser";
-import { ErrorLoadingWithDebug } from "../../components/ErrorLoadingWithDebug.js";
-import { LoginForm } from "../LoginForm.js";
-import { useComponentState } from "./state.js";
-import { InvalidIbanView, ReadyView } from "./views.js";
-
-export interface Props {
- account: string;
- goToConfirmOperation: (id: string) => void;
-}
-
-export type State = State.Loading | State.LoadingError | State.Ready | State.InvalidIban | State.UserNotFound;
-
-export namespace State {
- export interface Loading {
- status: "loading";
- error: undefined;
- }
-
- export interface LoadingError {
- status: "loading-error";
- error: TalerError;
- }
-
- export interface BaseInfo {
- error: undefined;
- }
-
- export interface Ready extends BaseInfo {
- status: "ready";
- error: undefined;
- account: string,
- limit: AmountJson,
- goToConfirmOperation: (id: string) => void;
- }
-
- export interface InvalidIban {
- status: "invalid-iban",
- error: TalerCorebankApi.AccountData;
- }
-
- export interface UserNotFound {
- status: "login",
- reason: "not-found" | "forbidden";
- onRegister?: () => void;
- }
-}
-
-export interface Transaction {
- negative: boolean;
- counterpart: string;
- when: AbsoluteTime;
- amount: AmountJson | undefined;
- subject: string;
-}
-
-const viewMapping: utils.StateViewMap<State> = {
- loading: Loading,
- "login": LoginForm,
- "invalid-iban": InvalidIbanView,
- "loading-error": ErrorLoadingWithDebug,
- ready: ReadyView,
-};
-
-export const AccountPage = utils.compose(
- (p: Props) => useComponentState(p),
- viewMapping,
-);
diff --git a/packages/demobank-ui/src/pages/AccountPage/state.ts b/packages/demobank-ui/src/pages/AccountPage/state.ts
deleted file mode 100644
index d28aba7bf..000000000
--- a/packages/demobank-ui/src/pages/AccountPage/state.ts
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2022 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 { Amounts, TalerError, parsePaytoUri } from "@gnu-taler/taler-util";
-import { useTranslationContext } from "@gnu-taler/web-util/browser";
-import { useAccountDetails } from "../../hooks/access.js";
-import { assertUnreachable } from "../WithdrawalOperationPage.js";
-import { Props, State } from "./index.js";
-
-export function useComponentState({ account, goToConfirmOperation }: Props): State {
- const result = useAccountDetails(account);
- const { i18n } = useTranslationContext();
-
- if (!result) {
- return {
- status: "loading",
- error: undefined,
- };
- }
-
- if (result instanceof TalerError) {
- return {
- status: "loading-error",
- error: result,
- };
- }
-
- if (result.type === "fail") {
- switch (result.case) {
- case "unauthorized": return {
- status: "login",
- reason: "forbidden"
- }
- case "not-found": return {
- status: "login",
- reason: "not-found",
- }
- default: {
- assertUnreachable(result)
- }
- }
- }
-
- const { body: data } = result;
-
- const balance = Amounts.parseOrThrow(data.balance.amount);
-
- const debitThreshold = Amounts.parseOrThrow(data.debit_threshold);
- const payto = parsePaytoUri(data.payto_uri);
-
- if (!payto || !payto.isKnown || (payto.targetType !== "iban" && payto.targetType !== "x-taler-bank")) {
- return {
- status: "invalid-iban",
- error: data
- };
- }
-
- const balanceIsDebit = data.balance.credit_debit_indicator == "debit";
- const limit = balanceIsDebit
- ? Amounts.sub(debitThreshold, balance).amount
- : Amounts.add(balance, debitThreshold).amount;
-
-
- return {
- status: "ready",
- goToConfirmOperation,
- error: undefined,
- account,
- limit,
- };
-}
diff --git a/packages/demobank-ui/src/pages/AccountPage/stories.tsx b/packages/demobank-ui/src/pages/AccountPage/stories.tsx
deleted file mode 100644
index f3828a5d6..000000000
--- a/packages/demobank-ui/src/pages/AccountPage/stories.tsx
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2022 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 * as tests from "@gnu-taler/web-util/testing";
-import { ReadyView } from "./views.js";
-
-export default {
- title: "account page",
-};
-
-export const Ready = tests.createExample(ReadyView, {});
diff --git a/packages/demobank-ui/src/pages/AccountPage/test.ts b/packages/demobank-ui/src/pages/AccountPage/test.ts
deleted file mode 100644
index 588b84c35..000000000
--- a/packages/demobank-ui/src/pages/AccountPage/test.ts
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2022 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 * as tests from "@gnu-taler/web-util/testing";
-import { SwrMockEnvironment } from "@gnu-taler/web-util/testing";
-import { expect } from "chai";
-import { CASHOUT_API_EXAMPLE } from "../../endpoints.js";
-import { Props } from "./index.js";
-import { useComponentState } from "./state.js";
-
-describe("Account states", () => {
- it("should do some tests", async () => {
- });
-});
diff --git a/packages/demobank-ui/src/pages/AccountPage/views.tsx b/packages/demobank-ui/src/pages/AccountPage/views.tsx
deleted file mode 100644
index d760543c6..000000000
--- a/packages/demobank-ui/src/pages/AccountPage/views.tsx
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2022 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 { useTranslationContext } from "@gnu-taler/web-util/browser";
-import { Fragment, VNode, h } from "preact";
-import { Attention } from "@gnu-taler/web-util/browser";
-import { Transactions } from "../../components/Transactions/index.js";
-import { usePreferences } from "../../hooks/preferences.js";
-import { PaymentOptions } from "../PaymentOptions.js";
-import { State } from "./index.js";
-import { useCashouts, useOnePendingCashouts } from "../../hooks/circuit.js";
-import { TalerError } from "@gnu-taler/taler-util";
-
-export function InvalidIbanView({ error }: State.InvalidIban) {
- return (
- <div>Payto from server is not valid &quot;{error.payto_uri}&quot;</div>
- );
-}
-
-const IS_PUBLIC_ACCOUNT_ENABLED = false
-
-function ShowDemoInfo(): VNode {
- const { i18n } = useTranslationContext();
- const [settings, updateSettings] = usePreferences();
- if (!settings.showDemoDescription) return <Fragment />
- return <Attention title={i18n.str`This is a demo bank`} onClose={() => {
- updateSettings("showDemoDescription", false);
- }}>
- {IS_PUBLIC_ACCOUNT_ENABLED ? (
- <i18n.Translate>
- This part of the demo shows how a bank that supports Taler
- directly would work. In addition to using your own bank
- account, you can also see the transaction history of some{" "}
- <a href="/public-accounts">Public Accounts</a>.
- </i18n.Translate>
- ) : (
- <i18n.Translate>
- This part of the demo shows how a bank that supports Taler
- directly would work.
- </i18n.Translate>
- )}
- </Attention>
-}
-
-export function ReadyView({ account, limit, goToConfirmOperation }: State.Ready): VNode<{}> {
-
- return <Fragment>
- <ShowDemoInfo />
- <PendingCashouts account={account}/>
- <PaymentOptions limit={limit} goToConfirmOperation={goToConfirmOperation} />
- <Transactions account={account} />
- </Fragment>;
-}
-
-
-function PendingCashouts({account}: {account: string}):VNode {
- const { i18n } = useTranslationContext();
- const result = useOnePendingCashouts(account)
- if (!result || result instanceof TalerError || result.type !== "ok" || !result.body) {
- return <Fragment />
- }
-
- return <Attention title={i18n.str`You have pending cashout operation to complete`} >
- <i18n.Translate>
- Cashout with subject "{result.body.subject}", look for the code and complete the operation <a target="_blank" rel="noreferrer noopener" class="font-semibold text-blue-700 hover:text-blue-600" href={`#/cashout/${result.body.id}`}>here</a>.
- </i18n.Translate>
- </Attention>
-} \ No newline at end of file
diff --git a/packages/demobank-ui/src/pages/BankFrame.stories.tsx b/packages/demobank-ui/src/pages/BankFrame.stories.tsx
deleted file mode 100644
index a64885489..000000000
--- a/packages/demobank-ui/src/pages/BankFrame.stories.tsx
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2022 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 * as tests from "@gnu-taler/web-util/testing";
-import { BankFrame } from "./BankFrame.js";
-
-export default {
- title: "bank frame",
-};
-
-export const Ready = tests.createExample(BankFrame, {});
diff --git a/packages/demobank-ui/src/pages/BankFrame.tsx b/packages/demobank-ui/src/pages/BankFrame.tsx
deleted file mode 100644
index 737a00b57..000000000
--- a/packages/demobank-ui/src/pages/BankFrame.tsx
+++ /dev/null
@@ -1,177 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2022 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 { Amounts, TalerError, TranslatedString } from "@gnu-taler/taler-util";
-import { Footer, GlobalNotificationsBanner, Header, Loading, notifyError, notifyException, useTranslationContext } from "@gnu-taler/web-util/browser";
-import { ComponentChildren, Fragment, VNode, h } from "preact";
-import { useEffect, useErrorBoundary, useState } from "preact/hooks";
-import { useAccountDetails } from "../hooks/access.js";
-import { useBackendState } from "../hooks/backend.js";
-import { getAllBooleanPreferences, getLabelForPreferences, usePreferences } from "../hooks/preferences.js";
-import { RenderAmount } from "./PaytoWireTransferForm.js";
-import { useSettingsContext } from "../context/settings.js";
-import { useBankCoreApiContext } from "../context/config.js";
-
-const GIT_HASH = typeof __GIT_HASH__ !== "undefined" ? __GIT_HASH__ : undefined;
-const VERSION = typeof __VERSION__ !== "undefined" ? __VERSION__ : undefined;
-
-
-export function BankFrame({
- children,
- account,
-}: {
- account?: string,
- children: ComponentChildren;
-}): VNode {
- const { i18n } = useTranslationContext();
- const backend = useBackendState();
- const settings = useSettingsContext();
- const [preferences, updatePreferences] = usePreferences();
-
- const [error, resetError] = useErrorBoundary();
-
- useEffect(() => {
- if (error) {
- const desc = (error instanceof Error ? error.stack : String(error)) as TranslatedString
- if (error instanceof Error) {
- console.log("Internal error, please report", error)
- notifyException(i18n.str`Internal error, please report.`, error)
- } else {
- console.log("Internal error, please report", error)
- notifyError(i18n.str`Internal error, please report.`, String(error) as TranslatedString)
- }
- resetError()
- }
- }, [error])
-
- return (<div class="min-h-full flex flex-col m-0 bg-slate-200" style="min-height: 100vh;">
-
- <div class="bg-indigo-600 pb-32">
- <Header
- title="Bank"
- iconLinkURL={settings.iconLinkURL ?? "#"}
- onLogout={backend.state.status !== "loggedIn" ? undefined : () => {
- backend.logOut()
- updatePreferences("currentWithdrawalOperationId", undefined);
- }}
- sites={!settings.topNavSites ? [] : Object.entries(settings.topNavSites)}
- supportedLangs={["en", "es", "de"]}
- >
- <li>
- <div class="text-xs font-semibold leading-6 text-gray-400">
- <i18n.Translate>Preferences</i18n.Translate>
- </div>
- <ul role="list" class="space-y-1">
- {getAllBooleanPreferences().map(set => {
- const isOn: boolean = !!preferences[set]
- return <li class="mt-2 pl-2">
- <div class="flex items-center justify-between">
- <span class="flex flex-grow flex-col">
- <span class="text-sm text-black font-medium leading-6 " id="availability-label">
- {getLabelForPreferences(set, i18n)}
- </span>
- </span>
- <button type="button" data-enabled={isOn} class="bg-indigo-600 data-[enabled=false]:bg-gray-200 relative inline-flex h-6 w-11 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none focus:ring-2 focus:ring-indigo-600 focus:ring-offset-2" role="switch" aria-checked="false" aria-labelledby="availability-label" aria-describedby="availability-description"
-
- onClick={() => { updatePreferences(set, !isOn); }}>
- <span aria-hidden="true" data-enabled={isOn} class="translate-x-5 data-[enabled=false]:translate-x-0 pointer-events-none inline-block h-5 w-5 transform rounded-full bg-white shadow ring-0 transition duration-200 ease-in-out"></span>
- </button>
- </div>
- </li>
- })}
- </ul>
- </li>
- </Header>
- </div >
-
- <GlobalNotificationsBanner />
-
- <main class="-mt-32 flex-1">
- {account &&
- <header class="py-5 bg-indigo-600 ">
- <div class="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
- <h1 class=" flex flex-wrap items-center justify-between sm:flex-nowrap">
- <span class="text-2xl font-bold tracking-tight text-white"><WelcomeAccount account={account} /></span>
- <span class="text-2xl font-bold tracking-tight text-white"><AccountBalance account={account} /></span>
- </h1>
- </div>
- </header>
- }
-
- <div class="mx-auto max-w-7xl px-4 pb-12 sm:px-6 lg:px-8">
- <div class="rounded-lg bg-white px-5 py-6 shadow sm:px-6">
- {children}
- </div>
- </div>
- </main>
-
- <Footer
- testingUrlKey="corebank-api-base-url"
- GIT_HASH={GIT_HASH}
- VERSION={VERSION}
- />
-
- </div >
-
- );
-}
-
-function WelcomeAccount({ account: accountName }: { account: string }): VNode {
- const { i18n } = useTranslationContext();
- return <a href="#/my-profile" class="underline underline-offset-2">
- <i18n.Translate>Welcome, <span class="whitespace-nowrap">{accountName}</span></i18n.Translate>
- </a>
- // const result = useAccountDetails(accountName);
- // if (!result) {
- // return <Loading />
- // }
- // if (result instanceof TalerError) {
- // return <div />
- // }
-
- // const payto = result.type === "fail" ? undefined : parsePaytoUri(result.body.payto_uri)
- // const info = !payto || !payto.isKnown ? undefined
- // : payto.targetType === "iban" ? { account: payto.iban, uri: stringifyPaytoUri(payto) }
- // : payto.targetType === "x-taler-bank" ? { account: payto.account, uri: stringifyPaytoUri(payto) }
- // : undefined;
-
- // return <i18n.Translate>
- // Welcome, <span class="whitespace-nowrap">{accountName}</span> {info !== undefined ?
- // <small class="whitespace-nowrap">
- // (<a href={info.uri}>{info.account}</a> <CopyButton getContent={() => info.uri} />)
- // </small>
- // : <Fragment />}!
- // </i18n.Translate>
-
-}
-
-function AccountBalance({ account }: { account: string }): VNode {
- const result = useAccountDetails(account);
- const { config } = useBankCoreApiContext();
- if (!result) {
- return <Loading />
- }
- if (result instanceof TalerError) {
- return <div />
- }
- if (result.type === "fail") return <div />
-
- return <RenderAmount
- value={Amounts.parseOrThrow(result.body.balance.amount)}
- negative={result.body.balance.credit_debit_indicator === "debit"}
- spec={config.currency_specification}
- />
-}
diff --git a/packages/demobank-ui/src/pages/DownloadStats.tsx b/packages/demobank-ui/src/pages/DownloadStats.tsx
deleted file mode 100644
index 596539e7e..000000000
--- a/packages/demobank-ui/src/pages/DownloadStats.tsx
+++ /dev/null
@@ -1,396 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2022 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 { AccessToken, AmountString, Logger, TalerCoreBankHttpClient, TalerCorebankApi, TalerError } from "@gnu-taler/taler-util";
-import { Attention, LocalNotificationBanner, useLocalNotification, useTranslationContext } from "@gnu-taler/web-util/browser";
-import { Fragment, VNode, h } from "preact";
-import { useState } from "preact/hooks";
-import { useBankCoreApiContext } from "../context/config.js";
-import { useBackendState } from "../hooks/backend.js";
-import { getTimeframesForDate } from "./admin/AdminHome.js";
-
-const logger = new Logger("PublicHistoriesPage");
-
-interface Props {
- onCancel: () => void;
-}
-
-type Options = {
- dayMetric: boolean;
- hourMetric: boolean;
- monthMetric: boolean;
- yearMetric: boolean;
- compareWithPrevious: boolean;
- endOnFirstFail: boolean;
- includeHeader: boolean;
-}
-
-/**
- * Show histories of public accounts.
- */
-export function DownloadStats({ onCancel }: Props): VNode {
- const { i18n } = useTranslationContext();
-
- const { state: credentials } = useBackendState();
- const creds = credentials.status !== "loggedIn" || !credentials.isUserAdministrator ? undefined : credentials
- const { api } = useBankCoreApiContext();
-
- const [options, setOptions] = useState<Options>({
- compareWithPrevious: true,
- dayMetric: true,
- endOnFirstFail: false,
- hourMetric: true,
- includeHeader: true,
- monthMetric: true,
- yearMetric: true,
- })
- const [lastStep, setLastStep] = useState<{ step: number, total: number }>()
- const [downloaded, setDownloaded] = useState<string>()
- const referenceDates = [new Date()]
- const [notification, notify, handleError] = useLocalNotification()
-
- if (!creds) {
- return <div>only admin can download stats</div>
- }
-
- return (
- <div>
-
- <div class="grid grid-cols-1 gap-x-8 gap-y-8 pt-10 md:grid-cols-3 bg-gray-100 my-4 px-4 pb-4 rounded-lg">
- <LocalNotificationBanner notification={notification} />
-
- <div class="px-4 sm:px-0">
- <h2 class="text-base font-semibold leading-7 text-gray-900">
- <i18n.Translate>Download bank stats</i18n.Translate>
- </h2>
- </div>
-
-
- <form
- class="bg-white shadow-sm ring-1 ring-gray-900/5 sm:rounded-xl md:col-span-2"
- autoCapitalize="none"
- autoCorrect="off"
- onSubmit={e => {
- e.preventDefault()
- }}
- >
- <div class="px-4 py-6 sm:p-8">
- <div class="grid max-w-2xl grid-cols-1 gap-x-6 gap-y-8 sm:grid-cols-6">
- <div class="sm:col-span-5">
- <div class="flex items-center justify-between">
- <span class="flex flex-grow flex-col">
- <span class="text-sm text-black font-medium leading-6 " id="availability-label">
- <i18n.Translate>Include hour metric</i18n.Translate>
- </span>
- </span>
- <button type="button" data-enabled={options.hourMetric} class="bg-indigo-600 data-[enabled=false]:bg-gray-200 relative inline-flex h-6 w-11 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none focus:ring-2 focus:ring-indigo-600 focus:ring-offset-2" role="switch" aria-checked="false" aria-labelledby="availability-label" aria-describedby="availability-description"
-
- onClick={() => { setOptions({ ...options, hourMetric: !options.hourMetric }) }}>
- <span aria-hidden="true" data-enabled={options.hourMetric} class="translate-x-5 data-[enabled=false]:translate-x-0 pointer-events-none inline-block h-5 w-5 transform rounded-full bg-white shadow ring-0 transition duration-200 ease-in-out"></span>
- </button>
- </div>
- </div>
- <div class="sm:col-span-5">
- <div class="flex items-center justify-between">
- <span class="flex flex-grow flex-col">
- <span class="text-sm text-black font-medium leading-6 " id="availability-label">
- <i18n.Translate>Include day metric</i18n.Translate>
- </span>
- </span>
- <button type="button" data-enabled={!!options.dayMetric} class="bg-indigo-600 data-[enabled=false]:bg-gray-200 relative inline-flex h-6 w-11 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none focus:ring-2 focus:ring-indigo-600 focus:ring-offset-2" role="switch" aria-checked="false" aria-labelledby="availability-label" aria-describedby="availability-description"
-
- onClick={() => { setOptions({ ...options, dayMetric: !options.dayMetric }) }}>
- <span aria-hidden="true" data-enabled={options.dayMetric} class="translate-x-5 data-[enabled=false]:translate-x-0 pointer-events-none inline-block h-5 w-5 transform rounded-full bg-white shadow ring-0 transition duration-200 ease-in-out"></span>
- </button>
- </div>
- </div>
- <div class="sm:col-span-5">
- <div class="flex items-center justify-between">
- <span class="flex flex-grow flex-col">
- <span class="text-sm text-black font-medium leading-6 " id="availability-label">
- <i18n.Translate>Include month metric</i18n.Translate>
- </span>
- </span>
- <button type="button" data-enabled={!!options.monthMetric} class="bg-indigo-600 data-[enabled=false]:bg-gray-200 relative inline-flex h-6 w-11 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none focus:ring-2 focus:ring-indigo-600 focus:ring-offset-2" role="switch" aria-checked="false" aria-labelledby="availability-label" aria-describedby="availability-description"
-
- onClick={() => { setOptions({ ...options, monthMetric: !options.monthMetric }) }}>
- <span aria-hidden="true" data-enabled={options.monthMetric} class="translate-x-5 data-[enabled=false]:translate-x-0 pointer-events-none inline-block h-5 w-5 transform rounded-full bg-white shadow ring-0 transition duration-200 ease-in-out"></span>
- </button>
- </div>
- </div>
- <div class="sm:col-span-5">
- <div class="flex items-center justify-between">
- <span class="flex flex-grow flex-col">
- <span class="text-sm text-black font-medium leading-6 " id="availability-label">
- <i18n.Translate>Include year metric</i18n.Translate>
- </span>
- </span>
- <button type="button" data-enabled={!!options.yearMetric} class="bg-indigo-600 data-[enabled=false]:bg-gray-200 relative inline-flex h-6 w-11 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none focus:ring-2 focus:ring-indigo-600 focus:ring-offset-2" role="switch" aria-checked="false" aria-labelledby="availability-label" aria-describedby="availability-description"
-
- onClick={() => { setOptions({ ...options, yearMetric: !options.yearMetric }) }}>
- <span aria-hidden="true" data-enabled={options.yearMetric} class="translate-x-5 data-[enabled=false]:translate-x-0 pointer-events-none inline-block h-5 w-5 transform rounded-full bg-white shadow ring-0 transition duration-200 ease-in-out"></span>
- </button>
- </div>
- </div>
- <div class="sm:col-span-5">
- <div class="flex items-center justify-between">
- <span class="flex flex-grow flex-col">
- <span class="text-sm text-black font-medium leading-6 " id="availability-label">
- <i18n.Translate>Include table header</i18n.Translate>
- </span>
- </span>
- <button type="button" data-enabled={!!options.includeHeader} class="bg-indigo-600 data-[enabled=false]:bg-gray-200 relative inline-flex h-6 w-11 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none focus:ring-2 focus:ring-indigo-600 focus:ring-offset-2" role="switch" aria-checked="false" aria-labelledby="availability-label" aria-describedby="availability-description"
-
- onClick={() => { setOptions({ ...options, includeHeader: !options.includeHeader }) }}>
- <span aria-hidden="true" data-enabled={options.includeHeader} class="translate-x-5 data-[enabled=false]:translate-x-0 pointer-events-none inline-block h-5 w-5 transform rounded-full bg-white shadow ring-0 transition duration-200 ease-in-out"></span>
- </button>
- </div>
- </div>
- <div class="sm:col-span-5">
- <div class="flex items-center justify-between">
- <span class="flex flex-grow flex-col">
- <span class="text-sm text-black font-medium leading-6 " id="availability-label">
- <i18n.Translate>Add previous metric for compare</i18n.Translate>
- </span>
- </span>
- <button type="button" data-enabled={!!options.compareWithPrevious} class="bg-indigo-600 data-[enabled=false]:bg-gray-200 relative inline-flex h-6 w-11 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none focus:ring-2 focus:ring-indigo-600 focus:ring-offset-2" role="switch" aria-checked="false" aria-labelledby="availability-label" aria-describedby="availability-description"
-
- onClick={() => { setOptions({ ...options, compareWithPrevious: !options.compareWithPrevious }) }}>
- <span aria-hidden="true" data-enabled={options.compareWithPrevious} class="translate-x-5 data-[enabled=false]:translate-x-0 pointer-events-none inline-block h-5 w-5 transform rounded-full bg-white shadow ring-0 transition duration-200 ease-in-out"></span>
- </button>
- </div>
- </div>
- <div class="sm:col-span-5">
- <div class="flex items-center justify-between">
- <span class="flex flex-grow flex-col">
- <span class="text-sm text-black font-medium leading-6 " id="availability-label">
- <i18n.Translate>Fail on first error</i18n.Translate>
- </span>
- </span>
- <button type="button" data-enabled={!!options.endOnFirstFail} class="bg-indigo-600 data-[enabled=false]:bg-gray-200 relative inline-flex h-6 w-11 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none focus:ring-2 focus:ring-indigo-600 focus:ring-offset-2" role="switch" aria-checked="false" aria-labelledby="availability-label" aria-describedby="availability-description"
-
- onClick={() => { setOptions({ ...options, endOnFirstFail: !options.endOnFirstFail }) }}>
- <span aria-hidden="true" data-enabled={options.endOnFirstFail} class="translate-x-5 data-[enabled=false]:translate-x-0 pointer-events-none inline-block h-5 w-5 transform rounded-full bg-white shadow ring-0 transition duration-200 ease-in-out"></span>
- </button>
- </div>
- </div>
- </div>
- </div>
-
-
-
- <div class="flex items-center justify-between gap-x-6 border-t border-gray-900/10 px-4 py-4 sm:px-8">
- <button type="button" class="text-sm font-semibold leading-6 text-gray-900"
- onClick={onCancel}
- >
- <i18n.Translate>Cancel</i18n.Translate>
- </button>
- <button type="submit"
- class="disabled:opacity-50 disabled:cursor-default cursor-pointer rounded-md bg-indigo-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600"
- disabled={lastStep !== undefined}
- onClick={async () => {
- setDownloaded(undefined)
- await handleError(async () => {
- const csv = await fetchAllStatus(api, creds.token, options, referenceDates, (step, total) => {
- setLastStep({ step, total })
- })
- setDownloaded(csv)
- })
- setLastStep(undefined)
- }}
- >
- <i18n.Translate>Download</i18n.Translate>
- </button>
- </div>
- </form>
-
- </div>
- {!lastStep || lastStep.step === lastStep.total ? <div class="h-5 mb-5"/> : <div>
- <div class="relative mb-5 h-5 rounded-full bg-gray-200">
- <div class="h-full animate-pulse rounded-full bg-blue-500" style={{
- width: `${Math.round((((lastStep.step / lastStep.total))* 100) )}%`
- }}>
- <span class="absolute inset-0 flex items-center justify-center text-xs font-semibold text-white">
- <i18n.Translate>downloading... {Math.round((((lastStep.step / lastStep.total))* 100) )}</i18n.Translate>
- </span>
- </div>
- </div>
- </div>}
- {!downloaded ? <div class="h-5 mb-5"/> :
- <a href={"data:text/plain;charset=utf-8," + encodeURIComponent(downloaded)} download={"bank-stats.csv"}>
- <Attention title={i18n.str`Download completed`}>
- <i18n.Translate>click here to save the file in your computer</i18n.Translate>
- </Attention>
- </a>
- }
- </div>
- );
-}
-
-
-async function fetchAllStatus(api: TalerCoreBankHttpClient, token: AccessToken, options: Options, references: Date[], progres: (current: number, total: number) => void): Promise<string> {
- const allMetrics: TalerCorebankApi.MonitorTimeframeParam[] = [];
- if (options.hourMetric) {
- allMetrics.push(TalerCorebankApi.MonitorTimeframeParam.hour)
- }
- if (options.dayMetric) {
- allMetrics.push(TalerCorebankApi.MonitorTimeframeParam.day)
- }
- if (options.monthMetric) {
- allMetrics.push(TalerCorebankApi.MonitorTimeframeParam.month)
- }
- if (options.yearMetric) {
- allMetrics.push(TalerCorebankApi.MonitorTimeframeParam.year)
- }
-
- /**
- * conver request into frames
- */
- const allFrames = allMetrics.flatMap(timeframe => references.map(reference => ({
- reference,
- timeframe,
- moment: getTimeframesForDate(reference, timeframe)
- }))
- )
- const total = allFrames.length
-
- /**
- * call API for info
- */
- const allInfo = await allFrames.reduce(async (prev, frame, index) => {
- const accumulatedMap = await prev
- progres(index, total)
- // await delay()
- const previous = options.compareWithPrevious ? (await api.getMonitor(token, {
- timeframe: frame.timeframe,
- which: frame.moment.previous
- })) : undefined
-
- if (previous && previous.type === "fail" && options.endOnFirstFail) {
- throw TalerError.fromUncheckedDetail(previous.detail)
- }
-
- const current = await api.getMonitor(token, {
- timeframe: frame.timeframe,
- which: frame.moment.current
- })
-
- if (current.type === "fail" && options.endOnFirstFail) {
- throw TalerError.fromUncheckedDetail(current.detail)
- }
-
- const metricName = TalerCorebankApi.MonitorTimeframeParam[allMetrics[index]]
- accumulatedMap[metricName] = {
- reference: frame.reference,
- current: current.type !== "ok" ? undefined : current.body,
- previous: !previous || previous.type !== "ok" ? undefined : previous.body,
- }
- return accumulatedMap
- }, Promise.resolve({} as Record<string, Data>))
- progres(total, total)
-
- /**
- * conver into table format
- *
- */
- const table: Array<string[]> = [];
- if (options.includeHeader) {
- table.push(["date",
- "metric",
- "reference",
- "talerInCount",
- "talerInVolume",
- "talerOutCount",
- "talerOutVolume",
- "cashinCount",
- "cashinFiatVolume",
- "cashinRegionalVolume",
- "cashoutCount",
- "cashoutFiatVolume",
- "cashoutRegionalVolume",])
- }
- Object.entries(allInfo).forEach(([name, data]) => {
- if (data.current) {
- const row: TableRow = {
- date: data.reference.getTime(),
- metric: name,
- reference: "current",
- ...dataToRow(data.current)
- }
- table.push((Object.values(row) as string[]))
- }
-
- if (data.previous) {
- const row: TableRow = {
- date: data.reference.getTime(),
- metric: name,
- reference: "previous",
- ...dataToRow(data.previous)
- }
- table.push((Object.values(row) as string[]))
- }
- })
-
- const csv = table.reduce((acc, row) => {
- return acc + row.join(",") + "\n"
- }, "")
-
- return csv
-}
-
-type JustData = Omit<Omit<Omit<TableRow, "metric">, "date">, "reference">
-function dataToRow(info: TalerCorebankApi.MonitorResponse): JustData {
- return {
- talerInCount: info.talerInCount,
- talerInVolume: info.talerInVolume,
- talerOutCount: info.talerOutCount,
- talerOutVolume: info.talerOutVolume,
- cashinCount: info.type === "no-conversions" ? undefined : info.cashinCount,
- cashinFiatVolume: info.type === "no-conversions" ? undefined : info.cashinFiatVolume,
- cashinRegionalVolume: info.type === "no-conversions" ? undefined : info.cashinRegionalVolume,
- cashoutCount: info.type === "no-conversions" ? undefined : info.cashoutCount,
- cashoutFiatVolume: info.type === "no-conversions" ? undefined : info.cashoutFiatVolume,
- cashoutRegionalVolume: info.type === "no-conversions" ? undefined : info.cashoutRegionalVolume,
- }
-}
-
-type Data = {
- reference: Date,
- previous: TalerCorebankApi.MonitorResponse | undefined;
- current: TalerCorebankApi.MonitorResponse | undefined;
-}
-type TableRow = {
- date: number,
- metric: string,
- reference: "current" | "previous",
- cashinCount?: number;
- cashinRegionalVolume?: AmountString;
- cashinFiatVolume?: AmountString;
- cashoutCount?: number;
- cashoutRegionalVolume?: AmountString;
- cashoutFiatVolume?: AmountString;
- talerInCount: number;
- talerInVolume: AmountString;
- talerOutCount: number;
- talerOutVolume: AmountString;
-}
-async function delay() {
- return new Promise(res => {
- setTimeout(( )=> {
- res(null)
- }, 500)
- })
-} \ No newline at end of file
diff --git a/packages/demobank-ui/src/pages/LoginForm.tsx b/packages/demobank-ui/src/pages/LoginForm.tsx
deleted file mode 100644
index 5eaad4bb0..000000000
--- a/packages/demobank-ui/src/pages/LoginForm.tsx
+++ /dev/null
@@ -1,215 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2022 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 { TranslatedString } from "@gnu-taler/taler-util";
-import { LocalNotificationBanner, ShowInputErrorLabel, useLocalNotification, useTranslationContext } from "@gnu-taler/web-util/browser";
-import { Fragment, VNode, h } from "preact";
-import { useEffect, useRef, useState } from "preact/hooks";
-import { useBankCoreApiContext } from "../context/config.js";
-import { useBackendState } from "../hooks/backend.js";
-import { undefinedIfEmpty } from "../utils.js";
-import { doAutoFocus } from "./PaytoWireTransferForm.js";
-import { assertUnreachable } from "./WithdrawalOperationPage.js";
-
-
-/**
- * Collect and submit login data.
- */
-export function LoginForm({ currentUser, fixedUser, onRegister }: { fixedUser?: boolean, currentUser?: string, onRegister?: () => void }): VNode {
- const backend = useBackendState();
-
- const sessionUser = backend.state.status !== "loggedOut" ? backend.state.username : undefined
- const [username, setUsername] = useState<string | undefined>(currentUser ?? sessionUser);
- const [password, setPassword] = useState<string | undefined>();
- const { i18n } = useTranslationContext();
- const { api } = useBankCoreApiContext();
- const [notification, notify, handleError] = useLocalNotification()
- const {config} = useBankCoreApiContext();
-
- const ref = useRef<HTMLInputElement>(null);
- useEffect(function focusInput() {
- ref.current?.focus();
- }, []);
-
- const [busy, setBusy] = useState<Record<string, undefined>>()
-
- const errors = undefinedIfEmpty({
- username: !username
- ? i18n.str`Missing username`
- // : !USERNAME_REGEX.test(username)
- // ? i18n.str`Use letters and numbers only, and start with a lowercase letter`
- : undefined,
- password: !password ? i18n.str`Missing password` : undefined,
- }) ?? busy;
-
- async function doLogout() {
- backend.logOut()
- }
-
- async function doLogin() {
- if (!username || !password) return;
- setBusy({})
- await handleError(async () => {
- const resp = await api.getAuthenticationAPI(username).createAccessToken(password, {
- // scope: "readwrite" as "write", //FIX: different than merchant
- scope: "readwrite",
- duration: {
- d_us: "forever" //FIX: should return shortest
- // d_us: 60 * 60 * 24 * 7 * 1000 * 1000
- },
- refreshable: true,
- })
- if (resp.type === "ok") {
- backend.logIn({ username, token: resp.body.access_token });
- } else {
- switch (resp.case) {
- case "wrong-credentials": return notify({
- type: "error",
- title: i18n.str`Wrong credentials for "${username}"`,
- description: resp.detail.hint as TranslatedString,
- debug: resp.detail,
- })
- case "not-found": return notify({
- type: "error",
- title: i18n.str`Account not found`,
- description: resp.detail.hint as TranslatedString,
- debug: resp.detail,
- })
- default: assertUnreachable(resp)
- }
- }
- })
- setPassword(undefined);
- setBusy(undefined)
- }
-
- return (
- <div class="flex min-h-full flex-col justify-center ">
- <LocalNotificationBanner notification={notification} />
- <div class="mt-10 sm:mx-auto sm:w-full sm:max-w-sm">
- <form class="space-y-6" noValidate
- onSubmit={(e) => {
- e.preventDefault();
- }}
- autoCapitalize="none"
- autoCorrect="off"
- >
- <div>
- <label for="username" class="block text-sm font-medium leading-6 text-gray-900">
- <i18n.Translate>Username</i18n.Translate>
- </label>
- <div class="mt-2">
- <input
- ref={doAutoFocus}
- type="text"
- name="username"
- id="username"
- class="block w-full disabled:bg-gray-200 rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
- value={username ?? ""}
- disabled={fixedUser}
- enterkeyhint="next"
- placeholder="identification"
- autocomplete="username"
- required
- onInput={(e): void => {
- setUsername(e.currentTarget.value);
- }}
- />
- <ShowInputErrorLabel
- message={errors?.username}
- isDirty={username !== undefined}
- />
- </div>
- </div>
-
- <div>
- <div class="flex items-center justify-between">
- <label for="password" class="block text-sm font-medium leading-6 text-gray-900">Password</label>
- </div>
- <div class="mt-2">
- <input
- type="password"
- name="password"
- id="password"
- autocomplete="current-password"
- class="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
- enterkeyhint="send"
- value={password ?? ""}
- placeholder="Password"
- required
- onInput={(e): void => {
- setPassword(e.currentTarget.value);
- }}
- />
- <ShowInputErrorLabel
- message={errors?.password}
- isDirty={password !== undefined}
- />
- </div>
- </div>
-
- {backend.state.status !== "loggedOut" ? <div class="flex justify-between">
- <button type="submit"
- class="rounded-md bg-white-600 px-3 py-1.5 text-sm font-semibold leading-6 text-black shadow-sm hover:bg-gray-100 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-gray-600"
- onClick={(e) => {
- e.preventDefault()
- doLogout()
- }}
- >
- <i18n.Translate>Cancel</i18n.Translate>
- </button>
-
- <button type="submit"
- class="rounded-md bg-indigo-600 disabled:bg-gray-300 px-3 py-1.5 text-sm font-semibold leading-6 text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600"
- disabled={!!errors}
- onClick={async (e) => {
- e.preventDefault()
- doLogin()
- }}
- >
- <i18n.Translate>Check</i18n.Translate>
- </button>
- </div> : <div>
- <button type="submit"
- class="flex w-full justify-center rounded-md bg-indigo-600 disabled:bg-gray-300 px-3 py-1.5 text-sm font-semibold leading-6 text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600"
- disabled={!!errors}
- onClick={(e) => {
- e.preventDefault()
- doLogin()
- }}
- >
- <i18n.Translate>Log in</i18n.Translate>
- </button>
- </div>}
- </form>
-
- {config.allow_registrations && onRegister &&
- <p class="mt-10 text-center text-sm text-gray-500 border-t">
- <button type="submit"
- class="flex mt-4 rounded-md bg-blue-600 px-3 py-1.5 text-sm font-semibold leading-6 text-white shadow-sm hover:bg-blue-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-600"
- onClick={(e) => {
- e.preventDefault()
- onRegister()
- }}
- >
- <i18n.Translate>Register</i18n.Translate>
- </button>
- </p>
- }
- </div>
- </div>
- );
-}
diff --git a/packages/demobank-ui/src/pages/OperationState/index.ts b/packages/demobank-ui/src/pages/OperationState/index.ts
deleted file mode 100644
index e3aec21c5..000000000
--- a/packages/demobank-ui/src/pages/OperationState/index.ts
+++ /dev/null
@@ -1,128 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2022 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 { AbsoluteTime, AmountJson, TalerCoreBankErrorsByMethod, TalerError, WithdrawUriResult } from "@gnu-taler/taler-util";
-import { Loading, utils } from "@gnu-taler/web-util/browser";
-import { ErrorLoadingWithDebug } from "../../components/ErrorLoadingWithDebug.js";
-import { useComponentState } from "./state.js";
-import { AbortedView, ConfirmedView, FailedView, InvalidPaytoView, InvalidReserveView, InvalidWithdrawalView, NeedConfirmationView, ReadyView } from "./views.js";
-
-export interface Props {
- currency: string;
- onClose: () => void;
-}
-
-export type State = State.Loading |
- State.LoadingError |
- State.Ready |
- State.Failed |
- State.Aborted |
- State.Confirmed |
- State.InvalidPayto |
- State.InvalidWithdrawal |
- State.InvalidReserve |
- State.NeedConfirmation;
-
-export namespace State {
- export interface Loading {
- status: "loading";
- error: undefined;
- }
-
- export interface Failed {
- status: "failed";
- error: TalerCoreBankErrorsByMethod<"createWithdrawal">;
- }
-
- export interface LoadingError {
- status: "loading-error";
- error: TalerError;
- }
-
- /**
- * Need to open the wallet
- */
- export interface Ready {
- status: "ready";
- error: undefined;
- uri: WithdrawUriResult,
- onClose: () => Promise<TalerCoreBankErrorsByMethod<"abortWithdrawalById"> | undefined>;
- }
-
- export interface InvalidPayto {
- status: "invalid-payto",
- error: undefined;
- payto: string | undefined;
- onClose: () => void;
- }
- export interface InvalidWithdrawal {
- status: "invalid-withdrawal",
- error: undefined;
- onClose: () => void;
- uri: string,
- }
- export interface InvalidReserve {
- status: "invalid-reserve",
- error: undefined;
- onClose: () => void;
- reserve: string | undefined;
- }
- export interface NeedConfirmation {
- status: "need-confirmation",
- account: string,
- onAbort: undefined | (() => Promise<TalerCoreBankErrorsByMethod<"abortWithdrawalById"> | undefined>);
- onConfirm: undefined | (() => Promise<TalerCoreBankErrorsByMethod<"confirmWithdrawalById"> | undefined>);
- error: undefined;
- busy: boolean,
- }
- export interface Aborted {
- status: "aborted",
- error: undefined;
- onClose: () => void;
- }
- export interface Confirmed {
- status: "confirmed",
- error: undefined;
- onClose: () => void;
- }
-
-}
-
-export interface Transaction {
- negative: boolean;
- counterpart: string;
- when: AbsoluteTime;
- amount: AmountJson | undefined;
- subject: string;
-}
-
-const viewMapping: utils.StateViewMap<State> = {
- loading: Loading,
- "failed": FailedView,
- "invalid-payto": InvalidPaytoView,
- "invalid-withdrawal": InvalidWithdrawalView,
- "invalid-reserve": InvalidReserveView,
- "need-confirmation": NeedConfirmationView,
- "aborted": AbortedView,
- "confirmed": ConfirmedView,
- "loading-error": ErrorLoadingWithDebug,
- ready: ReadyView,
-};
-
-export const OperationState = utils.compose(
- (p: Props) => useComponentState(p),
- viewMapping,
-);
diff --git a/packages/demobank-ui/src/pages/OperationState/state.ts b/packages/demobank-ui/src/pages/OperationState/state.ts
deleted file mode 100644
index defca6f13..000000000
--- a/packages/demobank-ui/src/pages/OperationState/state.ts
+++ /dev/null
@@ -1,221 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2022 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 { Amounts, FailCasesByMethod, TalerCoreBankErrorsByMethod, TalerError, TalerErrorDetail, TranslatedString, parsePaytoUri, parseWithdrawUri, stringifyWithdrawUri } from "@gnu-taler/taler-util";
-import { notify, notifyError, notifyInfo, useTranslationContext, utils } from "@gnu-taler/web-util/browser";
-import { useEffect, useState } from "preact/hooks";
-import { mutate } from "swr";
-import { useBankCoreApiContext } from "../../context/config.js";
-import { useWithdrawalDetails } from "../../hooks/access.js";
-import { useBackendState } from "../../hooks/backend.js";
-import { usePreferences } from "../../hooks/preferences.js";
-import { assertUnreachable } from "../WithdrawalOperationPage.js";
-import { Props, State } from "./index.js";
-
-export function useComponentState({ currency, onClose }: Props): utils.RecursiveState<State> {
- const [settings, updateSettings] = usePreferences()
- const { state: credentials } = useBackendState()
- const creds = credentials.status !== "loggedIn" ? undefined : credentials
- const { api } = useBankCoreApiContext()
-
- const [busy, setBusy] = useState<Record<string, undefined>>()
- const [failure, setFailure] = useState<TalerCoreBankErrorsByMethod<"createWithdrawal"> | undefined>()
- const amount = settings.maxWithdrawalAmount
-
- async function doSilentStart() {
- //FIXME: if amount is not enough use balance
- const parsedAmount = Amounts.parseOrThrow(`${currency}:${amount}`)
- if (!creds) return;
- const resp = await api.createWithdrawal(creds, {
- amount: Amounts.stringify(parsedAmount),
- });
- if (resp.type === "fail") {
- setFailure(resp)
- return;
- }
- updateSettings("currentWithdrawalOperationId", resp.body.withdrawal_id)
-
- }
-
- const withdrawalOperationId = settings.currentWithdrawalOperationId
- useEffect(() => {
- if (withdrawalOperationId === undefined) {
- doSilentStart()
- }
- }, [settings.fastWithdrawal, amount])
-
- if (failure) {
- return {
- status: "failed",
- error: failure
- }
- }
-
- if (!withdrawalOperationId) {
- return {
- status: "loading",
- error: undefined
- }
- }
-
- const wid = withdrawalOperationId
-
- async function doAbort() {
- if (!creds) return;
- const resp = await api.abortWithdrawalById(creds, wid);
- if (resp.type === "ok") {
- updateSettings("currentWithdrawalOperationId", undefined)
- onClose();
- } else {
- return resp;
- }
- }
-
- async function doConfirm(): Promise<TalerCoreBankErrorsByMethod<"confirmWithdrawalById"> | undefined> {
- if (!creds) return;
- setBusy({})
- const resp = await api.confirmWithdrawalById(creds, wid);
- setBusy(undefined)
- if (resp.type === "ok") {
- mutate(() => true)//clean withdrawal state
- } else {
- return resp;
- }
- }
-
- const uri = stringifyWithdrawUri({
- bankIntegrationApiBaseUrl: api.getIntegrationAPI().baseUrl,
- withdrawalOperationId,
- });
- const parsedUri = parseWithdrawUri(uri);
- if (!parsedUri) {
- return {
- status: "invalid-withdrawal",
- error: undefined,
- uri,
- onClose,
- }
- }
-
- return (): utils.RecursiveState<State> => {
- const result = useWithdrawalDetails(withdrawalOperationId);
- const shouldCreateNewOperation = result && !(result instanceof TalerError)
-
- useEffect(() => {
- if (shouldCreateNewOperation) {
- doSilentStart()
- }
- }, [])
- if (!result) {
- return {
- status: "loading",
- error: undefined
- }
- }
- if (result instanceof TalerError) {
- return {
- status: "loading-error",
- error: result
- }
- }
-
- if (result.type === "fail") {
- switch (result.case) {
- case "invalid-id":
- case "not-found": {
- return {
- status: "aborted",
- error: undefined,
- onClose: async () => {
- updateSettings("currentWithdrawalOperationId", undefined)
- onClose()
- },
- }
- }
- default: assertUnreachable(result)
- }
- }
-
- const { body: data } = result;
- if (data.status === "aborted") {
- return {
- status: "aborted",
- error: undefined,
- onClose: async () => {
- updateSettings("currentWithdrawalOperationId", undefined)
- onClose()
- },
- }
- }
-
- if (data.status === "confirmed") {
- if (!settings.showWithdrawalSuccess) {
- updateSettings("currentWithdrawalOperationId", undefined)
- onClose()
- }
- return {
- status: "confirmed",
- error: undefined,
- onClose: async () => {
- updateSettings("currentWithdrawalOperationId", undefined)
- onClose()
- },
- }
- }
-
- if (data.status === "pending") {
- return {
- status: "ready",
- error: undefined,
- uri: parsedUri,
- onClose: !creds ? (async () => {
- onClose();
- return undefined
- }) : doAbort,
- }
- }
-
- if (!data.selected_reserve_pub) {
- return {
- status: "invalid-reserve",
- error: undefined,
- reserve: data.selected_reserve_pub,
- onClose,
- }
- }
-
- const account = !data.selected_exchange_account ? undefined : parsePaytoUri(data.selected_exchange_account)
-
- if (!account) {
- return {
- status: "invalid-payto",
- error: undefined,
- payto: data.selected_exchange_account,
- onClose,
- }
- }
-
- return {
- status: "need-confirmation",
- error: undefined,
- account: data.username,
- onAbort: !creds ? undefined : doAbort,
- busy: !!busy,
- onConfirm: !creds ? undefined : doConfirm
- }
- }
-
-}
diff --git a/packages/demobank-ui/src/pages/OperationState/stories.tsx b/packages/demobank-ui/src/pages/OperationState/stories.tsx
deleted file mode 100644
index 03917a8fb..000000000
--- a/packages/demobank-ui/src/pages/OperationState/stories.tsx
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2022 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 * as tests from "@gnu-taler/web-util/testing";
-import { ReadyView } from "./views.js";
-
-export default {
- title: "operation status page",
-};
-
-export const Ready = tests.createExample(ReadyView, {});
diff --git a/packages/demobank-ui/src/pages/OperationState/test.ts b/packages/demobank-ui/src/pages/OperationState/test.ts
deleted file mode 100644
index f4d6cf4b2..000000000
--- a/packages/demobank-ui/src/pages/OperationState/test.ts
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2022 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 * as tests from "@gnu-taler/web-util/testing";
-import { SwrMockEnvironment } from "@gnu-taler/web-util/testing";
-import { expect } from "chai";
-import { CASHOUT_API_EXAMPLE } from "../../endpoints.js";
-import { Props } from "./index.js";
-import { useComponentState } from "./state.js";
-
-describe("Withdrawal operation states", () => {
- it("should do some tests", async () => {
- });
-});
diff --git a/packages/demobank-ui/src/pages/OperationState/views.tsx b/packages/demobank-ui/src/pages/OperationState/views.tsx
deleted file mode 100644
index b1f09ba2a..000000000
--- a/packages/demobank-ui/src/pages/OperationState/views.tsx
+++ /dev/null
@@ -1,403 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2022 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 { TranslatedString, stringifyWithdrawUri } from "@gnu-taler/taler-util";
-import { Attention, LocalNotificationBanner, ShowInputErrorLabel, notifyInfo, useLocalNotification, useTranslationContext } from "@gnu-taler/web-util/browser";
-import { Fragment, VNode, h } from "preact";
-import { useEffect, useMemo, useState } from "preact/hooks";
-import { QR } from "../../components/QR.js";
-import { usePreferences } from "../../hooks/preferences.js";
-import { undefinedIfEmpty } from "../../utils.js";
-import { ShouldBeSameUser } from "../WithdrawalConfirmationQuestion.js";
-import { assertUnreachable } from "../WithdrawalOperationPage.js";
-import { State } from "./index.js";
-
-export function InvalidPaytoView({ payto, onClose }: State.InvalidPayto) {
- return (
- <div>Payto from server is not valid &quot;{payto}&quot;</div>
- );
-}
-export function InvalidWithdrawalView({ uri, onClose }: State.InvalidWithdrawal) {
- return (
- <div>Withdrawal uri from server is not valid &quot;{uri}&quot;</div>
- );
-}
-export function InvalidReserveView({ reserve, onClose }: State.InvalidReserve) {
- return (
- <div>Reserve from server is not valid &quot;{reserve}&quot;</div>
- );
-}
-
-export function NeedConfirmationView({ error, onAbort: doAbort, onConfirm: doConfirm, busy, account }: State.NeedConfirmation) {
- const { i18n } = useTranslationContext()
- const [settings] = usePreferences()
- const [notification, notify, errorHandler] = useLocalNotification()
-
- const captchaNumbers = useMemo(() => {
- return {
- a: Math.floor(Math.random() * 10),
- b: Math.floor(Math.random() * 10),
- };
- }, []);
- const [captchaAnswer, setCaptchaAnswer] = useState<string | undefined>();
- const answer = parseInt(captchaAnswer ?? "", 10);
- const errors = undefinedIfEmpty({
- answer: !captchaAnswer
- ? i18n.str`Answer the question before continue`
- : Number.isNaN(answer)
- ? i18n.str`The answer should be a number`
- : answer !== captchaNumbers.a + captchaNumbers.b
- ? i18n.str`The answer "${answer}" to "${captchaNumbers.a} + ${captchaNumbers.b}" is wrong.`
- : undefined,
- }) ?? (busy ? {} as Record<string, undefined> : undefined);
-
- async function onCancel() {
- errorHandler(async () => {
- if (!doAbort) return;
- const resp = await doAbort()
- if (!resp) return;
- switch (resp.case) {
- case "previously-confirmed": return notify({
- type: "error",
- title: i18n.str`The reserve operation has been confirmed previously and can't be aborted`,
- description: resp.detail.hint as TranslatedString,
- debug: resp.detail,
- })
- case "invalid-id": return notify({
- type: "error",
- title: i18n.str`The operation id is invalid.`,
- description: resp.detail.hint as TranslatedString,
- debug: resp.detail,
- });
- case "not-found": return notify({
- type: "error",
- title: i18n.str`The operation was not found.`,
- description: resp.detail.hint as TranslatedString,
- debug: resp.detail,
- });
- default: assertUnreachable(resp)
- }
- })
- }
-
- async function onConfirm() {
- errorHandler(async () => {
- if (!doConfirm) return;
- const hasError = await doConfirm()
- if (!hasError) {
- if (!settings.showWithdrawalSuccess) {
- notifyInfo(i18n.str`Wire transfer completed!`)
- }
- return
- }
- switch (hasError.case) {
- case "previously-aborted": return notify({
- type: "error",
- title: i18n.str`The withdrawal has been aborted previously and can't be confirmed`,
- description: hasError.detail.hint as TranslatedString,
- debug: hasError.detail,
- })
- case "no-exchange-or-reserve-selected": return notify({
- type: "error",
- title: i18n.str`The withdraw operation cannot be confirmed because no exchange and reserve public key selection happened before`,
- description: hasError.detail.hint as TranslatedString,
- debug: hasError.detail,
- })
- case "invalid-id": return notify({
- type: "error",
- title: i18n.str`The operation id is invalid.`,
- description: hasError.detail.hint as TranslatedString,
- debug: hasError.detail,
- });
- case "not-found": return notify({
- type: "error",
- title: i18n.str`The operation was not found.`,
- description: hasError.detail.hint as TranslatedString,
- debug: hasError.detail,
- });
- case "insufficient-funds": return notify({
- type: "error",
- title: i18n.str`Your balance is not enough.`,
- description: hasError.detail.hint as TranslatedString,
- debug: hasError.detail,
- });
- default: assertUnreachable(hasError)
- }
- })
- }
-
- return (
- <div class="bg-white shadow sm:rounded-lg">
- <LocalNotificationBanner notification={notification} />
- <div class="px-4 py-5 sm:p-6">
- <h3 class="text-base font-semibold text-gray-900">
- <i18n.Translate>Confirm the withdrawal operation</i18n.Translate>
- </h3>
- <div class="mt-3 text-sm leading-6">
- <ShouldBeSameUser username={account}>
- <form
- class="bg-white shadow-sm ring-1 ring-gray-900/5 sm:rounded-xl md:col-span-2"
- autoCapitalize="none"
- autoCorrect="off"
- onSubmit={e => {
- e.preventDefault()
- }}
- >
- <div class="px-4 py-6 sm:p-8">
- <label for="withdraw-amount">{i18n.str`What is`}&nbsp;
- <em>
- {captchaNumbers.a}&nbsp;+&nbsp;{captchaNumbers.b}
- </em>
- ?
- </label>
- <div class="mt-2">
- <div class="relative rounded-md shadow-sm">
- <input
- type="text"
- // class="block w-full rounded-md border-0 py-1.5 pl-16 text-gray-900 ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
- aria-describedby="answer"
- autoFocus
- class="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
- value={captchaAnswer ?? ""}
- required
-
- name="answer"
- id="answer"
- autocomplete="off"
- onChange={(e): void => {
- setCaptchaAnswer(e.currentTarget.value)
- }}
- />
- </div>
- <ShowInputErrorLabel message={errors?.answer} isDirty={captchaAnswer !== undefined} />
- </div>
- </div>
- <div class="flex items-center justify-between gap-x-6 border-t border-gray-900/10 px-4 py-4 sm:px-8">
- <button type="button" class="text-sm font-semibold leading-6 text-gray-900"
- onClick={(e) => {
- e.preventDefault()
- onCancel()
- }}
- >
- <i18n.Translate>Cancel</i18n.Translate></button>
- <button type="submit"
- class="disabled:opacity-50 disabled:cursor-default cursor-pointer rounded-md bg-indigo-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600"
- disabled={!!errors}
- onClick={(e) => {
- e.preventDefault()
- onConfirm()
- }}
- >
- <i18n.Translate>Transfer</i18n.Translate>
- </button>
- </div>
- </form>
- </ShouldBeSameUser>
- </div>
- </div>
- </div>
-
- );
-}
-export function FailedView({ error }: State.Failed) {
- const { i18n } = useTranslationContext();
- switch (error.case) {
- case "unauthorized": return <Attention type="danger"
- title={i18n.str`Unauthorized to make the operation, maybe the session has expired or the password changed.`}>
- <div class="mt-2 text-sm text-red-700">
- {error.detail.hint}
- </div>
- </Attention>
- case "insufficient-funds": return <Attention type="danger"
- title={i18n.str`The operation was rejected due to insufficient funds.`}>
- <div class="mt-2 text-sm text-red-700">
- {error.detail.hint}
- </div>
- </Attention>
- case "account-not-found": return <Attention type="danger"
- title={i18n.str`The operation was rejected due to insufficient funds.`}>
- <div class="mt-2 text-sm text-red-700">
- {error.detail.hint}
- </div>
- </Attention>
- default: assertUnreachable(error)
- }
-}
-
-export function AbortedView({ error, onClose }: State.Aborted) {
- return (
- <div>aborted</div>
- );
-}
-
-export function ConfirmedView({ error, onClose }: State.Confirmed) {
- const { i18n } = useTranslationContext();
- const [settings, updateSettings] = usePreferences()
- return (
- <Fragment>
-
- <div class="relative ml-auto mr-auto transform overflow-hidden rounded-lg bg-white p-4 text-left shadow-xl transition-all ">
-
- <div class="mx-auto flex h-12 w-12 items-center justify-center rounded-full bg-green-100">
- <svg class="h-6 w-6 text-green-600" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" aria-hidden="true">
- <path stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5" />
- </svg>
- </div>
- <div class="mt-3 text-center sm:mt-5">
- <h3 class="text-base font-semibold leading-6 text-gray-900" id="modal-title">
- <i18n.Translate>Withdrawal confirmed</i18n.Translate>
- </h3>
- <div class="mt-2">
- <p class="text-sm text-gray-500">
- <i18n.Translate>
- The wire transfer to the Taler operator has been initiated. You will soon receive the requested amount in your Taler wallet.
- </i18n.Translate>
- </p>
- </div>
- </div>
- </div>
- <div class="mt-4">
- <div class="flex items-center justify-between">
- <span class="flex flex-grow flex-col">
- <span class="text-sm text-black font-medium leading-6 " id="availability-label">
- <i18n.Translate>Do not show this again</i18n.Translate>
- </span>
- </span>
- <button type="button" data-enabled={!settings.showWithdrawalSuccess} class="bg-indigo-600 data-[enabled=false]:bg-gray-200 relative inline-flex h-6 w-11 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none focus:ring-2 focus:ring-indigo-600 focus:ring-offset-2" role="switch" aria-checked="false" aria-labelledby="availability-label" aria-describedby="availability-description"
- onClick={() => {
- updateSettings("showWithdrawalSuccess", !settings.showWithdrawalSuccess);
- }}>
- <span aria-hidden="true" data-enabled={!settings.showWithdrawalSuccess} class="translate-x-5 data-[enabled=false]:translate-x-0 pointer-events-none inline-block h-5 w-5 transform rounded-full bg-white shadow ring-0 transition duration-200 ease-in-out"></span>
- </button>
- </div>
- </div>
- <div class="mt-5 sm:mt-6">
- <button type="button"
- class="inline-flex w-full justify-center rounded-md bg-indigo-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600"
- onClick={async (e) => {
- e.preventDefault();
- onClose()
- }}>
- <i18n.Translate>Close</i18n.Translate>
- </button>
- </div>
- </Fragment>
-
- );
-}
-
-export function ReadyView({ uri, onClose: doClose }: State.Ready): VNode<{}> {
- const { i18n } = useTranslationContext();
- const [notification, notify, errorHandler] = useLocalNotification()
-
- const talerWithdrawUri = stringifyWithdrawUri(uri);
- useEffect(() => {
- //Taler Wallet WebExtension is listening to headers response and tab updates.
- //In the SPA there is no header response with the Taler URI so
- //this hack manually triggers the tab update after the QR is in the DOM.
- // WebExtension will be using
- // https://developer.chrome.com/docs/extensions/reference/tabs/#event-onUpdated
- document.title = `${document.title} ${uri.withdrawalOperationId}`;
- const meta = document.createElement("meta")
- meta.setAttribute("name", "taler-uri")
- meta.setAttribute("content", talerWithdrawUri)
- document.head.insertBefore(meta, document.head.children.length ? document.head.children[0] : null)
- }, []);
-
- async function onClose() {
- errorHandler(async () => {
- const hasError = await doClose()
- if (!hasError) return;
- switch (hasError.case) {
- case "previously-confirmed": return notify({
- type: "error",
- title: i18n.str`The reserve operation has been confirmed previously and can't be aborted`,
- description: hasError.detail.hint as TranslatedString,
- debug: hasError.detail,
- })
- case "invalid-id": return notify({
- type: "error",
- title: i18n.str`The operation id is invalid.`,
- description: hasError.detail.hint as TranslatedString,
- debug: hasError.detail,
- });
- case "not-found": return notify({
- type: "error",
- title: i18n.str`The operation was not found.`,
- description: hasError.detail.hint as TranslatedString,
- debug: hasError.detail,
- });
- default: assertUnreachable(hasError)
- }
- })
- }
-
- return <Fragment>
- <LocalNotificationBanner notification={notification} />
-
- <div class="flex justify-end mt-4">
- <button type="button"
- class="inline-flex items-center rounded-md bg-red-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-red-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-red-500"
- onClick={() => {
- onClose()
- }}
- >
- Cancel
- </button>
- </div>
-
- <div class="bg-white shadow sm:rounded-lg mt-4">
- <div class="p-4">
- <h3 class="text-base font-semibold leading-6 text-gray-900">
- <i18n.Translate>On this device</i18n.Translate>
- </h3>
- <div class="mt-2 sm:flex sm:items-start sm:justify-between">
- <div class="max-w-xl text-sm text-gray-500">
- <p>
- <i18n.Translate>If you are using a desktop browser you can open the popup now or click the link if you have the "Inject Taler support" option enabled.</i18n.Translate>
- </p>
- </div>
- <div class="mt-5 sm:ml-6 sm:mt-0 sm:flex sm:flex-shrink-0 sm:items-center">
- <a href={talerWithdrawUri}
- class="inline-flex items-center disabled:opacity-50 disabled:cursor-default cursor-pointer rounded-md bg-indigo-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600"
- >
- <i18n.Translate>Start</i18n.Translate>
- </a>
- </div>
- </div>
- </div>
- </div>
- <div class="bg-white shadow sm:rounded-lg mt-2">
- <div class="p-4">
- <h3 class="text-base font-semibold leading-6 text-gray-900">
- <i18n.Translate>On a mobile phone</i18n.Translate>
- </h3>
- <div class="mt-2 sm:flex sm:items-start sm:justify-between">
- <div class="max-w-xl text-sm text-gray-500">
- <p>
- <i18n.Translate>Scan the QR code with your mobile device.</i18n.Translate>
- </p>
- </div>
- </div>
- <div class="mt-2 max-w-md ml-auto mr-auto">
- <QR text={talerWithdrawUri} />
- </div>
- </div>
- </div>
-
- </Fragment>
-
-}
diff --git a/packages/demobank-ui/src/pages/PaymentOptions.stories.tsx b/packages/demobank-ui/src/pages/PaymentOptions.stories.tsx
deleted file mode 100644
index 23046dd2e..000000000
--- a/packages/demobank-ui/src/pages/PaymentOptions.stories.tsx
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2022 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 * as tests from "@gnu-taler/web-util/testing";
-import { PaymentOptions } from "./PaymentOptions.js";
-
-export default {
- title: "PaymentOptions",
-};
-
-export const USD = tests.createExample(PaymentOptions, {
- limit: {
- currency: "USD",
- fraction: 0,
- value: 1,
- },
-});
diff --git a/packages/demobank-ui/src/pages/PaymentOptions.tsx b/packages/demobank-ui/src/pages/PaymentOptions.tsx
deleted file mode 100644
index bbe33eb57..000000000
--- a/packages/demobank-ui/src/pages/PaymentOptions.tsx
+++ /dev/null
@@ -1,122 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2022 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 { AmountJson } from "@gnu-taler/taler-util";
-import { notifyInfo, useTranslationContext } from "@gnu-taler/web-util/browser";
-import { h, VNode } from "preact";
-import { useState } from "preact/hooks";
-import { PaytoWireTransferForm, doAutoFocus } from "./PaytoWireTransferForm.js";
-import { WalletWithdrawForm } from "./WalletWithdrawForm.js";
-import { usePreferences } from "../hooks/preferences.js";
-
-/**
- * Let the user choose a payment option,
- * then specify the details trigger the action.
- */
-export function PaymentOptions({ limit, goToConfirmOperation }: { limit: AmountJson, goToConfirmOperation: (id: string) => void }): VNode {
- const { i18n } = useTranslationContext();
- const [settings] = usePreferences();
-
- const [tab, setTab] = useState<"charge-wallet" | "wire-transfer" | undefined>();
-
- return (
- <div class="mt-4">
-
- <fieldset>
- <legend class="px-4 text-base font-semibold leading-6 text-gray-900">
- <i18n.Translate>Send money to</i18n.Translate>
- </legend>
-
- <div class="px-4 mt-4 grid grid-cols-1 gap-y-6 sm:grid-cols-2 sm:gap-x-4">
- {/* <!-- Active: "border-indigo-600 ring-2 ring-indigo-600", Not Active: "border-gray-300" --> */}
- <label class={"relative flex cursor-pointer rounded-lg border bg-white p-4 shadow-sm focus:outline-none" + (tab === "charge-wallet" ? "border-indigo-600 ring-2 ring-indigo-600" : "border-gray-300")}>
- <input type="radio" name="project-type" value="Newsletter" class="sr-only" aria-labelledby="project-type-0-label" aria-describedby="project-type-0-description-0 project-type-0-description-1" onClick={() => {
- setTab("charge-wallet")
- }} />
- <div class="flex flex-col">
- <span class="flex">
- <div class="text-4xl mr-4 my-auto">&#x1F4B5;</div>
- <span class="grow self-center text-lg text-gray-900 align-middle text-center">
- <i18n.Translate>a <b>Taler</b> wallet</i18n.Translate>
- </span>
- <svg class="self-center flex-none h-5 w-5 text-indigo-600" style={{ visibility: tab === "charge-wallet" ? "visible" : "hidden" }} viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
- <path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.857-9.809a.75.75 0 00-1.214-.882l-3.483 4.79-1.88-1.88a.75.75 0 10-1.06 1.061l2.5 2.5a.75.75 0 001.137-.089l4-5.5z" clip-rule="evenodd" />
- </svg>
- </span>
- <div class="mt-1 flex items-center text-sm text-gray-500">
- <i18n.Translate>Withdraw digital money into your mobile wallet or browser extension</i18n.Translate>
- </div>
- {!!settings.currentWithdrawalOperationId &&
- <span class="flex items-center gap-x-1.5 w-fit rounded-md bg-green-100 px-2 py-1 text-xs font-medium text-green-700 whitespace-pre">
- <svg class="h-1.5 w-1.5 fill-green-500" viewBox="0 0 6 6" aria-hidden="true">
- <circle cx="3" cy="3" r="3" />
- </svg>
- <i18n.Translate>operation ready</i18n.Translate>
- </span>
- }
- </div>
- </label>
-
-
- <label class={"relative flex cursor-pointer rounded-lg border bg-white p-4 shadow-sm focus:outline-none" + (tab === "wire-transfer" ? "border-indigo-600 ring-2 ring-indigo-600" : "border-gray-300")}>
- <input type="radio" name="project-type" value="Existing Customers" class="sr-only" aria-labelledby="project-type-1-label" aria-describedby="project-type-1-description-0 project-type-1-description-1" onClick={() => {
- setTab("wire-transfer")
- }} />
- <div class="flex flex-col">
- <span class="flex">
- <div class="text-4xl mr-4 my-auto">&#x2194;</div>
- <span class="grow self-center text-lg font-medium text-gray-900 align-middle text-center">
- <i18n.Translate>another bank account</i18n.Translate>
- </span>
- <svg class="self-center flex-none h-5 w-5 text-indigo-600" style={{ visibility: tab === "wire-transfer" ? "visible" : "hidden" }} viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
- <path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.857-9.809a.75.75 0 00-1.214-.882l-3.483 4.79-1.88-1.88a.75.75 0 10-1.06 1.061l2.5 2.5a.75.75 0 001.137-.089l4-5.5z" clip-rule="evenodd" />
- </svg>
- </span>
- <div class="mt-1 flex items-center text-sm text-gray-500">
- <i18n.Translate>Make a wire transfer to an account with known bank account number.</i18n.Translate>
- </div>
- </div>
- </label>
- </div>
- {tab === "charge-wallet" && (
- <WalletWithdrawForm
- focus
- limit={limit}
- goToConfirmOperation={goToConfirmOperation}
- onCancel={() => {
- setTab(undefined)
- }}
- />
- )}
- {tab === "wire-transfer" && (
- <PaytoWireTransferForm
- focus
- title={i18n.str`Transfer details`}
- limit={limit}
- onSuccess={() => {
- notifyInfo(i18n.str`Wire transfer created!`);
- setTab(undefined)
- }}
- onCancel={() => {
- setTab(undefined)
- }}
- />
- )}
-
- </fieldset>
- </div>
- )
-}
diff --git a/packages/demobank-ui/src/pages/PaytoWireTransferForm.stories.tsx b/packages/demobank-ui/src/pages/PaytoWireTransferForm.stories.tsx
deleted file mode 100644
index 81f2d82f5..000000000
--- a/packages/demobank-ui/src/pages/PaytoWireTransferForm.stories.tsx
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2022 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 * as tests from "@gnu-taler/web-util/testing";
-import { PaytoWireTransferForm } from "./PaytoWireTransferForm.js";
-
-export default {
- title: "PaytoWireTransferForm",
-};
-
-export const USD = tests.createExample(PaytoWireTransferForm, {
- limit: {
- currency: "USD",
- fraction: 0,
- value: 1,
- },
-});
diff --git a/packages/demobank-ui/src/pages/PaytoWireTransferForm.tsx b/packages/demobank-ui/src/pages/PaytoWireTransferForm.tsx
deleted file mode 100644
index c785b24df..000000000
--- a/packages/demobank-ui/src/pages/PaytoWireTransferForm.tsx
+++ /dev/null
@@ -1,483 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2022 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 {
- AmountJson,
- AmountLike,
- AmountString,
- Amounts,
- CurrencySpecification,
- FRAC_SEPARATOR,
- Logger,
- PaytoString,
- TranslatedString,
- buildPayto,
- parsePaytoUri,
- stringifyPaytoUri
-} from "@gnu-taler/taler-util";
-import {
- useLocalNotification,
- useTranslationContext
-} from "@gnu-taler/web-util/browser";
-import { Fragment, Ref, VNode, h } from "preact";
-import { useState } from "preact/hooks";
-import { mutate } from "swr";
-import { ShowInputErrorLabel } from "@gnu-taler/web-util/browser";
-import { useBankCoreApiContext } from "../context/config.js";
-import { useBackendState } from "../hooks/backend.js";
-import {
- undefinedIfEmpty,
- validateIBAN,
- withRuntimeErrorHandling
-} from "../utils.js";
-import { assertUnreachable } from "./WithdrawalOperationPage.js";
-import { LocalNotificationBanner } from "@gnu-taler/web-util/browser";
-
-const logger = new Logger("PaytoWireTransferForm");
-
-export function PaytoWireTransferForm({
- focus,
- title,
- toAccount,
- onSuccess,
- onCancel,
- limit,
-}: {
- title: TranslatedString,
- focus?: boolean;
- toAccount?: string,
- onSuccess: () => void;
- onCancel: (() => void) | undefined;
- limit: AmountJson;
-}): VNode {
- const [isRawPayto, setIsRawPayto] = useState(false);
- const { state: credentials } = useBackendState()
- const { api } = useBankCoreApiContext();
-
- const sendingToFixedAccount = toAccount !== undefined
- //FIXME: support other destination that just IBAN
- const [iban, setIban] = useState<string | undefined>(toAccount);
- const [subject, setSubject] = useState<string | undefined>();
- const [amount, setAmount] = useState<string | undefined>();
-
- const [rawPaytoInput, rawPaytoInputSetter] = useState<string | undefined>(
- undefined,
- );
- const { i18n } = useTranslationContext();
- const ibanRegex = "^[A-Z][A-Z][0-9]+$";
-
- const trimmedAmountStr = amount?.trim();
- const parsedAmount = Amounts.parse(`${limit.currency}:${trimmedAmountStr}`);
- const IBAN_REGEX = /^[A-Z][A-Z0-9]*$/;
- const [notification, notify, handleError] = useLocalNotification()
-
- const errorsWire = undefinedIfEmpty({
- iban: !iban
- ? i18n.str`required`
- : !IBAN_REGEX.test(iban)
- ? i18n.str`IBAN should have just uppercased letters and numbers`
- : validateIBAN(iban, i18n),
- subject: !subject ? i18n.str`required` : undefined,
- amount: !trimmedAmountStr
- ? i18n.str`required`
- : !parsedAmount
- ? i18n.str`not valid`
- : Amounts.isZero(parsedAmount)
- ? i18n.str`should be greater than 0`
- : Amounts.cmp(limit, parsedAmount) === -1
- ? i18n.str`balance is not enough`
- : undefined,
- });
-
- const parsed = !rawPaytoInput ? undefined : parsePaytoUri(rawPaytoInput);
-
- const errorsPayto = undefinedIfEmpty({
- rawPaytoInput: !rawPaytoInput
- ? i18n.str`required`
- : !parsed
- ? i18n.str`does not follow the pattern`
- : !parsed.isKnown || parsed.targetType !== "iban"
- ? i18n.str`only "IBAN" target are supported`
- : !parsed.params.amount
- ? i18n.str`use the "amount" parameter to specify the amount to be transferred`
- : Amounts.parse(parsed.params.amount) === undefined
- ? i18n.str`the amount is not valid`
- : !parsed.params.message
- ? i18n.str`use the "message" parameter to specify a reference text for the transfer`
- : !IBAN_REGEX.test(parsed.iban)
- ? i18n.str`IBAN should have just uppercased letters and numbers`
- : validateIBAN(parsed.iban, i18n),
- });
-
- async function doSend() {
- let payto_uri: PaytoString | undefined;
- let sendingAmount: AmountString | undefined;
-
- if (credentials.status !== "loggedIn") return;
- if (rawPaytoInput) {
- const p = parsePaytoUri(rawPaytoInput)
- if (!p) return;
- sendingAmount = p.params.amount as AmountString
- delete p.params.amount
- //if this payto is valid then it already have message
- payto_uri = stringifyPaytoUri(p)
- } else {
- if (!iban || !subject) return;
- const ibanPayto = buildPayto("iban", iban, undefined);
- ibanPayto.params.message = encodeURIComponent(subject);
- payto_uri = stringifyPaytoUri(ibanPayto);
- sendingAmount = `${limit.currency}:${trimmedAmountStr}` as AmountString
- }
- const puri = payto_uri;
-
- await handleError(async () => {
- const res = await api.createTransaction(credentials, {
- payto_uri: puri,
- amount: sendingAmount,
- });
- mutate(() => true)
- if (res.type === "fail") {
- switch (res.case) {
- case "invalid-input": return notify({
- type: "error",
- title: i18n.str`The request was invalid or the payto://-URI used unacceptable features.`,
- description: res.detail.hint as TranslatedString,
- debug: res.detail,
- })
- case "unauthorized": return notify({
- type: "error",
- title: i18n.str`Not enough permission to complete the operation.`,
- description: res.detail.hint as TranslatedString,
- debug: res.detail,
- })
- case "creditor-not-found": return notify({
- type: "error",
- title: i18n.str`The destination account "${puri}" was not found.`,
- description: res.detail.hint as TranslatedString,
- debug: res.detail,
- })
- case "creditor-same": return notify({
- type: "error",
- title: i18n.str`The origin and the destination of the transfer can't be the same.`,
- description: res.detail.hint as TranslatedString,
- debug: res.detail,
- })
- case "insufficient-funds": return notify({
- type: "error",
- title: i18n.str`Your balance is not enough.`,
- description: res.detail.hint as TranslatedString,
- debug: res.detail,
- })
- case "not-found": return notify({
- type: "error",
- title: i18n.str`The origin account "${puri}" was not found.`,
- description: res.detail.hint as TranslatedString,
- debug: res.detail,
- })
- default: assertUnreachable(res)
- }
- }
- onSuccess();
- setAmount(undefined);
- setIban(undefined);
- setSubject(undefined);
- rawPaytoInputSetter(undefined)
- })
- }
-
- return (<div class="grid grid-cols-1 gap-x-8 gap-y-8 pt-10 md:grid-cols-3 bg-gray-100 my-4 px-4 pb-4 rounded-lg">
- {/**
- * FIXME: Scan a qr code
- */}
- <div class="">
- <h2 class="text-base font-semibold leading-7 text-gray-900">
- {title}
- </h2>
- <div>
- <div class="px-2 mt-2 grid grid-cols-1 gap-y-4 sm:gap-x-4">
- <label class={"relative flex cursor-pointer rounded-lg border bg-white p-4 shadow-sm focus:outline-none" + (!isRawPayto ? "border-indigo-600 ring-2 ring-indigo-600" : "border-gray-300")}>
- <input type="radio" name="project-type" value="Newsletter" class="sr-only" aria-labelledby="project-type-0-label" aria-describedby="project-type-0-description-0 project-type-0-description-1" onChange={() => {
- if (parsed && parsed.isKnown && parsed.targetType === "iban") {
- setIban(parsed.iban)
- const amountStr = parsed.params["amount"]
- if (amountStr) {
- const amount = Amounts.parse(parsed.params["amount"])
- if (amount) {
- setAmount(Amounts.stringifyValue(amount))
- }
- }
- const subject = parsed.params["message"]
- if (subject) {
- setSubject(subject)
- }
- }
- setIsRawPayto(false)
- }} />
- <span class="flex flex-1">
- <span class="flex flex-col">
- <span class="block text-sm font-medium text-gray-900">
- <i18n.Translate>Using a form</i18n.Translate>
- </span>
- </span>
- </span>
- </label>
-
- {sendingToFixedAccount ? undefined :
- <label class={"relative flex cursor-pointer rounded-lg border bg-white p-4 shadow-sm focus:outline-none" + (isRawPayto ? "border-indigo-600 ring-2 ring-indigo-600" : "border-gray-300")}>
- <input type="radio" name="project-type" value="Existing Customers" class="sr-only" aria-labelledby="project-type-1-label" aria-describedby="project-type-1-description-0 project-type-1-description-1" onChange={() => {
- if (iban) {
- const payto = buildPayto("iban", iban, undefined)
- if (parsedAmount) {
- payto.params["amount"] = Amounts.stringify(parsedAmount)
- }
- if (subject) {
- payto.params["message"] = subject
- }
- rawPaytoInputSetter(stringifyPaytoUri(payto))
- }
- setIsRawPayto(true)
- }} />
- <span class="flex flex-1">
- <span class="flex flex-col">
- <span class="block text-sm font-medium text-gray-900">
- <i18n.Translate>Import payto:// URI</i18n.Translate>
- </span>
- </span>
- </span>
- </label>
- }
- </div>
- </div>
- </div>
-
- <form
- class="bg-white shadow-sm ring-1 ring-gray-900/5 rounded-md sm:rounded-xl md:col-span-2 w-fit mx-auto"
- autoCapitalize="none"
- autoCorrect="off"
- onSubmit={e => {
- e.preventDefault()
- }}
- >
- <div class="p-4 sm:p-8">
- {!isRawPayto ?
- <div class="grid max-w-xs grid-cols-1 gap-x-6 gap-y-8 sm:grid-cols-6">
- <div class="sm:col-span-5">
- <label for="iban" class="block text-sm font-medium leading-6 text-gray-900">{i18n.str`Recipient`}</label>
- <div class="mt-2">
- <input
- ref={focus ? doAutoFocus : undefined}
- type="text"
- class="block w-full disabled:bg-gray-200 rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
- name="iban"
- id="iban"
- disabled={sendingToFixedAccount}
- value={iban ?? ""}
- placeholder="CC0123456789"
- autocomplete="off"
- required
- pattern={ibanRegex}
- onInput={(e): void => {
- setIban(e.currentTarget.value.toUpperCase());
- }}
- />
- <ShowInputErrorLabel
- message={errorsWire?.iban}
- isDirty={iban !== undefined}
- />
- </div>
- <p class="mt-2 text-sm text-gray-500" >
- <i18n.Translate>IBAN of the recipient's account</i18n.Translate>
- </p>
- </div>
-
- <div class="sm:col-span-5">
- <label for="subject" class="block text-sm font-medium leading-6 text-gray-900">{i18n.str`Transfer subject`}</label>
- <div class="mt-2">
- <input
- type="text"
- class="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
- name="subject"
- id="subject"
- autocomplete="off"
- placeholder="subject"
- value={subject ?? ""}
- required
- onInput={(e): void => {
- setSubject(e.currentTarget.value);
- }}
- />
- <ShowInputErrorLabel
- message={errorsWire?.subject}
- isDirty={subject !== undefined}
- />
- </div>
- <p class="mt-2 text-sm text-gray-500" >some text to identify the transfer</p>
- </div>
-
- <div class="sm:col-span-5">
- <label for="amount" class="block text-sm font-medium leading-6 text-gray-900">{i18n.str`Amount`}</label>
- <InputAmount
- name="amount"
- left
- currency={limit.currency}
- value={trimmedAmountStr}
- onChange={(d) => {
- setAmount(d)
- }}
- />
- <ShowInputErrorLabel
- message={errorsWire?.amount}
- isDirty={trimmedAmountStr !== undefined}
- />
- <p class="mt-2 text-sm text-gray-500" >amount to transfer</p>
- </div>
-
- </div> :
- <div class="grid max-w-2xl grid-cols-1 gap-x-6 gap-y-8 sm:grid-cols-6 w-full">
- <div class="sm:col-span-6">
- <label for="address" class="block text-sm font-medium leading-6 text-gray-900">{i18n.str`payto URI:`}</label>
- <div class="mt-2">
- <textarea
- ref={focus ? doAutoFocus : undefined}
- name="address"
- id="address"
- type="textarea"
- rows={5}
- class="block overflow-hidden w-44 sm:w-96 rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
- value={rawPaytoInput ?? ""}
- required
- placeholder={i18n.str`payto://iban/[receiver-iban]?message=[subject]&amount=[${limit.currency}:X.Y]`}
- onInput={(e): void => {
- rawPaytoInputSetter(e.currentTarget.value);
- }}
- />
- <ShowInputErrorLabel
- message={errorsPayto?.rawPaytoInput}
- isDirty={rawPaytoInput !== undefined}
- />
- </div>
- </div>
- </div>
- }
- </div>
- <div class="flex items-center justify-between gap-x-6 border-t border-gray-900/10 px-4 py-4 sm:px-8">
- {onCancel ?
- <button type="button" class="text-sm font-semibold leading-6 text-gray-900"
- onClick={onCancel}
- >
- <i18n.Translate>Cancel</i18n.Translate>
- </button>
- : <div />
- }
- <button type="submit"
- class="disabled:opacity-50 disabled:cursor-default cursor-pointer rounded-md bg-indigo-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600"
- disabled={isRawPayto ? !!errorsPayto : !!errorsWire}
- onClick={(e) => {
- e.preventDefault()
- doSend()
- }}
- >
- <i18n.Translate>Send</i18n.Translate>
- </button>
- </div>
- <LocalNotificationBanner notification={notification} />
- </form>
- </div >
- )
-
-}
-
-/**
- * Show the element when the load ended
- * @param element
- */
-export function doAutoFocus(element: HTMLElement | null) {
- if (element) {
- setTimeout(() => {
- element.focus({ preventScroll: true })
- element.scrollIntoView({
- behavior: "smooth",
- block: "center",
- inline: "center",
- })
- }, 100)
- }
-}
-
-export function InputAmount(
- {
- currency,
- name,
- value,
- error,
- left,
- onChange,
- }: {
- error?: string;
- currency: string;
- name: string;
- left?: boolean | undefined,
- value: string | undefined;
- onChange?: (s: string) => void;
- },
- ref: Ref<HTMLInputElement>,
-): VNode {
- const { config } = useBankCoreApiContext()
- return (
- <div class="mt-2">
- <div class="flex rounded-md shadow-sm border-0 ring-1 ring-inset ring-gray-300 focus:ring-2 focus:ring-inset focus:ring-indigo-600">
- <div
- class="pointer-events-none inset-y-0 flex items-center px-3"
- >
- <span class="text-gray-500 sm:text-sm">{currency}</span>
- </div>
- <input
- type="number"
- data-left={left}
- class="disabled:bg-gray-200 text-right rounded-md rounded-l-none data-[left=true]:text-left w-full py-1.5 pl-3 text-gray-900 placeholder:text-gray-400 sm:text-sm sm:leading-6"
- placeholder="0.00" aria-describedby="price-currency"
- ref={ref}
- name={name}
- id={name}
- autocomplete="off"
- value={value ?? ""}
- disabled={!onChange}
- onInput={(e) => {
- if (!onChange) return;
- const l = e.currentTarget.value.length
- const sep_pos = e.currentTarget.value.indexOf(FRAC_SEPARATOR)
- if (sep_pos !== -1 && l - sep_pos - 1 > config.currency_specification.num_fractional_input_digits) {
- e.currentTarget.value = e.currentTarget.value.substring(0, sep_pos + config.currency_specification.num_fractional_input_digits + 1)
- }
- onChange(e.currentTarget.value);
- }}
- />
- </div>
- <ShowInputErrorLabel message={error} isDirty={value !== undefined} />
- </div>
- );
-}
-
-export function RenderAmount({ value, spec, negative, withColor, hideSmall }: { spec: CurrencySpecification; value: AmountJson, hideSmall?: boolean, negative?: boolean, withColor?: boolean }): VNode {
- const neg = !!negative //convert to true or false
-
- const { currency, normal, small } = Amounts.stringifyValueWithSpec(value, spec)
-
- return <span data-negative={withColor ? neg : undefined} class="whitespace-nowrap data-[negative=false]:text-green-600 data-[negative=true]:text-red-600">
- {negative ? "- " : undefined}
- {currency} {normal} {!hideSmall && small && <sup class="-ml-1">{small}</sup>}
- </span>
-} \ No newline at end of file
diff --git a/packages/demobank-ui/src/pages/ProfileNavigation.tsx b/packages/demobank-ui/src/pages/ProfileNavigation.tsx
deleted file mode 100644
index 3596d0f11..000000000
--- a/packages/demobank-ui/src/pages/ProfileNavigation.tsx
+++ /dev/null
@@ -1,72 +0,0 @@
-import { useTranslationContext } from "@gnu-taler/web-util/browser";
-import { Fragment, VNode, h } from "preact";
-import { useBankCoreApiContext } from "../context/config.js";
-import { assertUnreachable } from "./WithdrawalOperationPage.js";
-import { useBackendState } from "../hooks/backend.js";
-
-export function ProfileNavigation({ current }: { current: "details" | "delete" | "credentials" | "cashouts" }): VNode {
- const { i18n } = useTranslationContext()
- const { config } = useBankCoreApiContext()
- const { state: credentials } = useBackendState();
- const nonAdminUser = credentials.status !== "loggedIn" ? false : !credentials.isUserAdministrator
- return <div>
- <div class="sm:hidden">
- <label for="tabs" class="sr-only"><i18n.Translate>Select a section</i18n.Translate></label>
- <select id="tabs" name="tabs" class="block w-full rounded-md border-gray-300 focus:border-indigo-500 focus:ring-indigo-500" onChange={(e) => {
- const op = e.currentTarget.value as typeof current
- switch (op) {
- case "details": {
- window.location.href = "#/my-profile";
- return;
- }
- case "delete": {
- window.location.href = "#/delete-my-account";
- return;
- }
- case "credentials": {
- window.location.href = "#/my-password";
- return;
- }
- case "cashouts": {
- window.location.href = "#/my-cashouts";
- return;
- }
- default: assertUnreachable(op)
- }
- }}>
- <option value="details" selected={current == "details"}><i18n.Translate>Details</i18n.Translate></option>
- {!config.allow_deletions ? undefined :
- <option value="delete" selected={current == "delete"}><i18n.Translate>Delete</i18n.Translate></option>
- }
- <option value="credentials" selected={current == "credentials"}><i18n.Translate>Credentials</i18n.Translate></option>
- {config.allow_conversion ?
- <option value="cashouts" selected={current == "cashouts"}><i18n.Translate>Cashouts</i18n.Translate></option>
- : undefined}
- </select>
- </div>
- <div class="hidden sm:block">
- <nav class="isolate flex divide-x divide-gray-200 rounded-lg shadow" aria-label="Tabs">
- <a href="#/my-profile" data-selected={current == "details"} class="rounded-l-lg text-gray-500 hover:text-gray-700 data-[selected=true]:text-gray-900 group relative min-w-0 flex-1 overflow-hidden bg-white py-4 px-4 text-center text-sm font-medium hover:bg-gray-50 focus:z-10" >
- <span><i18n.Translate>Details</i18n.Translate></span>
- <span aria-hidden="true" data-selected={current == "details"} class="bg-transparent data-[selected=true]:bg-indigo-500 absolute inset-x-0 bottom-0 h-0.5"></span>
- </a>
- {!config.allow_deletions ? undefined :
- <a href="#/delete-my-account" data-selected={current == "delete"} aria-current="page" class=" text-gray-500 hover:text-gray-700 data-[selected=true]:text-gray-900 group relative min-w-0 flex-1 overflow-hidden bg-white py-4 px-4 text-center text-sm font-medium hover:bg-gray-50 focus:z-10">
- <span><i18n.Translate>Delete</i18n.Translate></span>
- <span aria-hidden="true" data-selected={current == "delete"} class="bg-transparent data-[selected=true]:bg-indigo-500 absolute inset-x-0 bottom-0 h-0.5"></span>
- </a>
- }
- <a href="#/my-password" data-selected={current == "credentials"} aria-current="page" class=" text-gray-500 hover:text-gray-700 data-[selected=true]:text-gray-900 group relative min-w-0 flex-1 overflow-hidden bg-white py-4 px-4 text-center text-sm font-medium hover:bg-gray-50 focus:z-10">
- <span><i18n.Translate>Credentials</i18n.Translate></span>
- <span aria-hidden="true" data-selected={current == "credentials"} class="bg-transparent data-[selected=true]:bg-indigo-500 absolute inset-x-0 bottom-0 h-0.5"></span>
- </a>
- {config.allow_conversion && nonAdminUser ?
- <a href="#/my-cashouts" data-selected={current == "cashouts"} class="rounded-r-lg text-gray-500 hover:text-gray-700 data-[selected=true]:text-gray-900 group relative min-w-0 flex-1 overflow-hidden bg-white py-4 px-4 text-center text-sm font-medium hover:bg-gray-50 focus:z-10">
- <span>Cashouts</span>
- <span aria-hidden="true" data-selected={current == "cashouts"} class="bg-transparent data-[selected=true]:bg-indigo-500 absolute inset-x-0 bottom-0 h-0.5"></span>
- </a>
- : undefined}
- </nav>
- </div>
- </div>
-} \ No newline at end of file
diff --git a/packages/demobank-ui/src/pages/PublicHistoriesPage.tsx b/packages/demobank-ui/src/pages/PublicHistoriesPage.tsx
deleted file mode 100644
index 08503fb9b..000000000
--- a/packages/demobank-ui/src/pages/PublicHistoriesPage.tsx
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2022 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 { Logger, TalerError } from "@gnu-taler/taler-util";
-import { useTranslationContext } from "@gnu-taler/web-util/browser";
-import { Fragment, VNode, h } from "preact";
-import { useState } from "preact/hooks";
-import { Loading } from "@gnu-taler/web-util/browser";
-import { Transactions } from "../components/Transactions/index.js";
-import { usePublicAccounts } from "../hooks/access.js";
-
-const logger = new Logger("PublicHistoriesPage");
-
-interface Props { }
-
-/**
- * Show histories of public accounts.
- */
-export function PublicHistoriesPage({ }: Props): VNode {
- const { i18n } = useTranslationContext();
-
- //TODO: implemented filter by account name
- const result = usePublicAccounts(undefined);
- const firstAccount = result && !(result instanceof TalerError) && result.data.public_accounts.length > 0
- ? result.data.public_accounts[0].username
- : undefined;
-
- const [showAccount, setShowAccount] = useState(firstAccount);
-
- if (!result) {
- return <Loading />
- }
- if (result instanceof TalerError) {
- return <Loading />
- }
-
- const { data } = result;
-
- const txs: Record<string, h.JSX.Element> = {};
- const accountsBar = [];
-
- // Ask story of all the public accounts.
- for (const account of data.public_accounts) {
- logger.trace("Asking transactions for", account.username);
- const isSelected = account.username == showAccount;
- accountsBar.push(
- <li
- class={
- isSelected
- ? "pure-menu-selected pure-menu-item"
- : "pure-menu-item pure-menu"
- }
- >
- <a
- href="#"
- class="pure-menu-link"
- onClick={() => setShowAccount(account.username)}
- >
- {account.username}
- </a>
- </li>,
- );
- txs[account.username] = <Transactions account={account.username} />;
- }
-
- return (
- <Fragment>
- <h1 class="nav">{i18n.str`History of public accounts`}</h1>
- <section id="main">
- <article>
- <div class="pure-menu pure-menu-horizontal" name="accountMenu">
- <ul class="pure-menu-list">{accountsBar}</ul>
- {typeof showAccount !== "undefined" ? (
- txs[showAccount]
- ) : (
- <p>No public transactions found.</p>
- )}
- <br />
- <a href="/account" class="pure-button">
- Go back
- </a>
- </div>
- </article>
- </section>
- </Fragment>
- );
-}
diff --git a/packages/demobank-ui/src/pages/QrCodeSection.stories.tsx b/packages/demobank-ui/src/pages/QrCodeSection.stories.tsx
deleted file mode 100644
index 95ac4bc33..000000000
--- a/packages/demobank-ui/src/pages/QrCodeSection.stories.tsx
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2022 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 * as tests from "@gnu-taler/web-util/testing";
-import { QrCodeSection } from "./QrCodeSection.js";
-import { parseWithdrawUri } from "@gnu-taler/taler-util";
-
-export default {
- title: "Qr Code Selection",
-};
-
-export const SimpleExample = tests.createExample(QrCodeSection, {
- withdrawUri: parseWithdrawUri("taler://withdraw/bank.com/operationId"),
-});
diff --git a/packages/demobank-ui/src/pages/QrCodeSection.tsx b/packages/demobank-ui/src/pages/QrCodeSection.tsx
deleted file mode 100644
index 60beb012e..000000000
--- a/packages/demobank-ui/src/pages/QrCodeSection.tsx
+++ /dev/null
@@ -1,153 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2022 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 {
- stringifyWithdrawUri,
- TranslatedString,
- WithdrawUriResult
-} from "@gnu-taler/taler-util";
-import {
- useLocalNotification,
- useTranslationContext
-} from "@gnu-taler/web-util/browser";
-import { Fragment, h, VNode } from "preact";
-import { useEffect } from "preact/hooks";
-import { QR } from "../components/QR.js";
-import { useBankCoreApiContext } from "../context/config.js";
-import { withRuntimeErrorHandling } from "../utils.js";
-import { assertUnreachable } from "./WithdrawalOperationPage.js";
-import { LocalNotificationBanner } from "@gnu-taler/web-util/browser";
-import { useBackendState } from "../hooks/backend.js";
-
-export function QrCodeSection({
- withdrawUri,
- onAborted,
-}: {
- withdrawUri: WithdrawUriResult;
- onAborted: () => void;
-}): VNode {
- const { i18n } = useTranslationContext();
- const talerWithdrawUri = stringifyWithdrawUri(withdrawUri);
- const { state: credentials } = useBackendState();
- const creds = credentials.status !== "loggedIn" ? undefined : credentials
-
- useEffect(() => {
- //Taler Wallet WebExtension is listening to headers response and tab updates.
- //In the SPA there is no header response with the Taler URI so
- //this hack manually triggers the tab update after the QR is in the DOM.
- // WebExtension will be using
- // https://developer.chrome.com/docs/extensions/reference/tabs/#event-onUpdated
- document.title = `${document.title} ${withdrawUri.withdrawalOperationId}`;
- const meta = document.createElement("meta")
- meta.setAttribute("name", "taler-uri")
- meta.setAttribute("content", talerWithdrawUri)
- document.head.insertBefore(meta, document.head.children.length ? document.head.children[0] : null)
- }, []);
- const [notification, notify, handleError] = useLocalNotification()
-
- const { api } = useBankCoreApiContext()
-
- async function doAbort() {
- await handleError(async () => {
- if (!creds) return;
- const resp = await api.abortWithdrawalById(creds, withdrawUri.withdrawalOperationId);
- if (resp.type === "ok") {
- onAborted();
- } else {
- switch (resp.case) {
- case "previously-confirmed": return notify({
- type: "error",
- title: i18n.str`The reserve operation has been confirmed previously and can't be aborted`
- })
- case "invalid-id": return notify({
- type: "error",
- title: i18n.str`The operation id is invalid.`,
- description: resp.detail.hint as TranslatedString,
- debug: resp.detail,
- })
- case "not-found": return notify({
- type: "error",
- title: i18n.str`The operation was not found.`,
- description: resp.detail.hint as TranslatedString,
- debug: resp.detail,
- })
- default: {
- assertUnreachable(resp)
- }
- }
- }
- })
- }
-
- return (
- <Fragment>
- <LocalNotificationBanner notification={notification} />
- <div class="bg-white shadow-xl sm:rounded-lg">
- <div class="px-4 py-5 sm:p-6">
- <h3 class="text-base font-semibold leading-6 text-gray-900">
- <i18n.Translate>If you have a Taler wallet installed in this device</i18n.Translate>
- </h3>
- <div class="mt-4 mb-4 text-sm text-gray-500">
- <p><i18n.Translate>
- You will see the details of the operation in your wallet including the fees (if applies).
- If you still don't have one you can install it from <a class="font-semibold text-gray-500 hover:text-gray-400" href="https://taler.net/en/wallet.html">here</a>.
- </i18n.Translate></p>
- </div>
- <div class="flex items-center justify-between gap-x-6 border-t border-gray-900/10 pt-2 mt-2 ">
- <button type="button"
- // class="disabled:opacity-50 disabled:cursor-default cursor-pointer rounded-md px-3 py-2 text-sm font-semibold text-black shadow-sm hover:bg-red-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-red-600"
- class="text-sm font-semibold leading-6 text-gray-900"
- onClick={doAbort}
- >
- Cancel
- </button>
- <a href={talerWithdrawUri}
- class="inline-flex items-center disabled:opacity-50 disabled:cursor-default cursor-pointer rounded-md bg-indigo-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600"
- >
- <i18n.Translate>Withdraw</i18n.Translate>
- </a>
- </div>
- </div>
- </div>
-
- <div class="bg-white shadow-xl sm:rounded-lg mt-8">
- <div class="px-4 py-5 sm:p-6">
- <h3 class="text-base font-semibold leading-6 text-gray-900">
- <i18n.Translate>Or if you have the wallet in another device</i18n.Translate>
- </h3>
- <div class="mt-4 max-w-xl text-sm text-gray-500">
- <i18n.Translate>Scan the QR below to start the withdrawal.</i18n.Translate>
- </div>
- <div class="mt-2 max-w-md ml-auto mr-auto">
- <QR text={talerWithdrawUri} />
- </div>
- </div>
- <div class="flex items-center justify-center gap-x-6 border-t border-gray-900/10 px-4 py-4 sm:px-8">
- <button type="button"
- // class="disabled:opacity-50 disabled:cursor-default cursor-pointer rounded-md px-3 py-2 text-sm font-semibold text-black shadow-sm hover:bg-red-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-red-600"
- class="text-sm font-semibold leading-6 text-gray-900"
- onClick={doAbort}
- >
- Cancel
- </button>
- </div>
- </div>
-
- </Fragment>
- );
-}
-
-
diff --git a/packages/demobank-ui/src/pages/RegistrationPage.tsx b/packages/demobank-ui/src/pages/RegistrationPage.tsx
deleted file mode 100644
index e1d32002b..000000000
--- a/packages/demobank-ui/src/pages/RegistrationPage.tsx
+++ /dev/null
@@ -1,426 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2022 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 { AccessToken, Logger, TranslatedString } from "@gnu-taler/taler-util";
-import {
- LocalNotificationBanner,
- ShowInputErrorLabel,
- useLocalNotification,
- useTranslationContext
-} from "@gnu-taler/web-util/browser";
-import { Fragment, VNode, h } from "preact";
-import { useState } from "preact/hooks";
-import { useBankCoreApiContext } from "../context/config.js";
-import { useBackendState } from "../hooks/backend.js";
-import { undefinedIfEmpty } from "../utils.js";
-import { getRandomPassword, getRandomUsername } from "./rnd.js";
-import { useSettingsContext } from "../context/settings.js";
-
-const logger = new Logger("RegistrationPage");
-
-export function RegistrationPage({
- onComplete,
- onCancel
-}: {
- onComplete: () => void;
- onCancel: () => void;
-}): VNode {
- const { i18n } = useTranslationContext();
- const { config } = useBankCoreApiContext();
- if (!config.allow_registrations) {
- return (
- <p>{i18n.str`Currently, the bank is not accepting new registrations!`}</p>
- );
- }
- return <RegistrationForm onComplete={onComplete} onCancel={onCancel} />;
-}
-
-export const USERNAME_REGEX = /^[a-z][a-zA-Z0-9-]*$/;
-export const PHONE_REGEX = /^(\+\d{1,2}\s)?\(?\d{3}\)?[\s.-]\d{3}[\s.-]\d{4}$/;
-export const EMAIL_REGEX = /^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$/;
-
-/**
- * Collect and submit registration data.
- */
-function RegistrationForm({ onComplete, onCancel }: { onComplete: () => void, onCancel: () => void }): VNode {
- const backend = useBackendState();
- const [username, setUsername] = useState<string | undefined>();
- const [name, setName] = useState<string | undefined>();
- const [password, setPassword] = useState<string | undefined>();
- const [phone, setPhone] = useState<string | undefined>();
- const [email, setEmail] = useState<string | undefined>();
- const [repeatPassword, setRepeatPassword] = useState<string | undefined>();
- const [notification, notify, handleError] = useLocalNotification()
- const settings = useSettingsContext();
-
- const { api } = useBankCoreApiContext()
- // const { register } = useTestingAPI();
- const { i18n } = useTranslationContext();
-
- const errors = undefinedIfEmpty({
- name: !name
- ? i18n.str`Missing name`
- : undefined,
- username: !username
- ? i18n.str`Missing username`
- : !USERNAME_REGEX.test(username)
- ? i18n.str`Use letters and numbers only, and start with a lowercase letter`
- : undefined,
- phone: !phone
- ? undefined
- : !PHONE_REGEX.test(phone)
- ? i18n.str`Use letters and numbers only, and start with a lowercase letter`
- : undefined,
- email: !email
- ? undefined
- : !EMAIL_REGEX.test(email)
- ? i18n.str`Use letters and numbers only, and start with a lowercase letter`
- : undefined,
- password: !password ? i18n.str`Missing password` : undefined,
- repeatPassword: !repeatPassword
- ? i18n.str`Missing password`
- : repeatPassword !== password
- ? i18n.str`Passwords don't match`
- : undefined,
- });
-
- async function doRegistrationAndLogin(name: string, username: string, password: string, onComplete: () => void) {
- await handleError(async () => {
- const creationResponse = await api.createAccount("" as AccessToken, { name, username, password });
- if (creationResponse.type === "fail") {
- switch (creationResponse.case) {
- case "invalid-phone-or-email": return notify({
- type: "error",
- title: i18n.str`Server replied with invalid phone or email.`,
- description: creationResponse.detail.hint as TranslatedString,
- debug: creationResponse.detail,
- })
- case "insufficient-funds": return notify({
- type: "error",
- title: i18n.str`Registration is disabled because the bank ran out of bonus credit.`,
- description: creationResponse.detail.hint as TranslatedString,
- debug: creationResponse.detail,
- })
- case "unauthorized": return notify({
- type: "error",
- title: i18n.str`No enough permission to create that account.`,
- description: creationResponse.detail.hint as TranslatedString,
- debug: creationResponse.detail,
- })
- case "payto-already-exists": return notify({
- type: "error",
- title: i18n.str`That account id is already taken.`,
- description: creationResponse.detail.hint as TranslatedString,
- debug: creationResponse.detail,
- })
- case "username-already-exists": return notify({
- type: "error",
- title: i18n.str`That username is already taken.`,
- description: creationResponse.detail.hint as TranslatedString,
- debug: creationResponse.detail,
- })
- case "username-reserved": return notify({
- type: "error",
- title: i18n.str`That username can't be used because is reserved.`,
- description: creationResponse.detail.hint as TranslatedString,
- debug: creationResponse.detail,
- })
- case "user-cant-set-debt": return notify({
- type: "error",
- title: i18n.str`Only admin is allow to set debt limit.`,
- description: creationResponse.detail.hint as TranslatedString,
- debug: creationResponse.detail,
- })
- default: assertUnreachable(creationResponse)
- }
- }
- const resp = await api.getAuthenticationAPI(username).createAccessToken(password, {
- scope: "readwrite",
- duration: { d_us: "forever" },
- refreshable: true,
- })
-
- if (resp.type === "ok") {
- backend.logIn({ username, token: resp.body.access_token });
- } else {
- switch (resp.case) {
- case "wrong-credentials": return notify({
- type: "error",
- title: i18n.str`Wrong credentials for "${username}"`,
- description: resp.detail.hint as TranslatedString,
- debug: resp.detail,
- })
- case "not-found": return notify({
- type: "error",
- title: i18n.str`Account not found`,
- description: resp.detail.hint as TranslatedString,
- debug: resp.detail,
- })
- default: assertUnreachable(resp)
- }
- }
- onComplete()
- })
- }
-
- async function doRegistrationStep() {
- if (!username || !password || !name) return;
- await doRegistrationAndLogin(name, username, password, () => {
- setUsername(undefined);
- setPassword(undefined);
- setRepeatPassword(undefined);
- onComplete();
- })
- }
-
- async function doRandomRegistration(tries: number = 3) {
- const user = getRandomUsername();
-
- const pass = settings.simplePasswordForRandomAccounts ? "123" : getRandomPassword();
- const username = `_${user.first}-${user.second}_`
- const name = `${user.first}, ${user.second}`
- await doRegistrationAndLogin(name, username, pass, onComplete)
-
- }
-
- return (
- <Fragment>
- <LocalNotificationBanner notification={notification} />
-
- <div class="flex min-h-full flex-col justify-center">
- <div class="sm:mx-auto sm:w-full sm:max-w-sm">
- <h2 class="text-center text-2xl font-bold leading-9 tracking-tight text-gray-900">{i18n.str`Account registration`}</h2>
- </div>
-
- <div class="mt-10 sm:mx-auto sm:w-full sm:max-w-sm">
- <form class="space-y-6" noValidate
- onSubmit={(e) => {
- e.preventDefault();
- }}
- autoCapitalize="none"
- autoCorrect="off"
- >
- <div>
- <label for="username" class="block text-sm font-medium leading-6 text-gray-900">
- <i18n.Translate>Username</i18n.Translate>
- <b style={{ color: "red" }}> *</b>
- </label>
- <div class="mt-2">
- <input
- autoFocus
- type="text"
- name="username"
- id="username"
- class="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
- value={username ?? ""}
- enterkeyhint="next"
- placeholder="identification"
- autocomplete="username"
- required
- onInput={(e): void => {
- setUsername(e.currentTarget.value);
- }}
- />
- <ShowInputErrorLabel
- message={errors?.username}
- isDirty={username !== undefined}
- />
- </div>
- </div>
-
- <div>
- <div class="flex items-center justify-between">
- <label for="password" class="block text-sm font-medium leading-6 text-gray-900">
- <i18n.Translate>Password</i18n.Translate>
- <b style={{ color: "red" }}> *</b>
- </label>
- </div>
- <div class="mt-2">
- <input
- type="password"
- name="password"
- id="password"
- autocomplete="current-password"
- class="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
- enterkeyhint="send"
- value={password ?? ""}
- placeholder="Password"
- required
- onInput={(e): void => {
- setPassword(e.currentTarget.value);
- }}
- />
- <ShowInputErrorLabel
- message={errors?.password}
- isDirty={password !== undefined}
- />
- </div>
- </div>
-
- <div>
- <div class="flex items-center justify-between">
- <label for="register-repeat" class="block text-sm font-medium leading-6 text-gray-900">
- <i18n.Translate>Repeat password</i18n.Translate>
- <b style={{ color: "red" }}> *</b>
- </label>
- </div>
- <div class="mt-2">
- <input
- type="password"
- name="register-repeat"
- id="register-repeat"
- autocomplete="current-password"
- class="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
- enterkeyhint="send"
- value={repeatPassword ?? ""}
- placeholder="Same password"
- required
- onInput={(e): void => {
- setRepeatPassword(e.currentTarget.value);
- }}
- />
- <ShowInputErrorLabel
- message={errors?.repeatPassword}
- isDirty={repeatPassword !== undefined}
- />
- </div>
- </div>
-
- <div>
- <label for="name" class="block text-sm font-medium leading-6 text-gray-900">
- <i18n.Translate>Name</i18n.Translate>
- </label>
- <div class="mt-2">
- <input
- autoFocus
- type="text"
- name="name"
- id="name"
- class="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
- value={name ?? ""}
- enterkeyhint="next"
- placeholder="your name"
- autocomplete="name"
- required
- onInput={(e): void => {
- setName(e.currentTarget.value);
- }}
- />
- {/* <ShowInputErrorLabel
- message={errors?.name}
- isDirty={name !== undefined}
- /> */}
- </div>
- </div>
-
- {/* <div>
- <label for="phone" class="block text-sm font-medium leading-6 text-gray-900">
- <i18n.Translate>Phone</i18n.Translate>
- </label>
- <div class="mt-2">
- <input
- autoFocus
- type="text"
- name="phone"
- id="phone"
- class="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
- value={phone ?? ""}
- enterkeyhint="next"
- placeholder="your phone"
- autocomplete="none"
- onInput={(e): void => {
- setPhone(e.currentTarget.value);
- }}
- />
- <ShowInputErrorLabel
- message={errors?.phone}
- isDirty={phone !== undefined}
- />
- </div>
- </div>
- <div>
- <label for="email" class="block text-sm font-medium leading-6 text-gray-900">
- <i18n.Translate>Email</i18n.Translate>
- </label>
- <div class="mt-2">
- <input
- autoFocus
- type="text"
- name="email"
- id="email"
- class="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
- value={email ?? ""}
- enterkeyhint="next"
- placeholder="your email"
- autocomplete="email"
- onInput={(e): void => {
- setEmail(e.currentTarget.value);
- }}
- />
- <ShowInputErrorLabel
- message={errors?.email}
- isDirty={email !== undefined}
- />
- </div>
- </div> */}
-
- <div class="flex w-full justify-between">
- <button type="submit"
- class="ring-1 ring-gray-600 rounded-md bg-white disabled:bg-gray-300 px-3 py-1.5 text-sm font-semibold leading-6 text-black shadow-sm hover:bg-white-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2"
- onClick={(e) => {
- e.preventDefault()
- onCancel()
- }}
- >
- <i18n.Translate>Cancel</i18n.Translate>
- </button>
- <button type="submit"
- class=" rounded-md bg-indigo-600 disabled:bg-gray-300 px-3 py-1.5 text-sm font-semibold leading-6 text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600"
- disabled={!!errors}
- onClick={async (e) => {
- e.preventDefault()
-
- doRegistrationStep()
- }}
- >
- <i18n.Translate>Register</i18n.Translate>
- </button>
- </div>
-
- </form>
-
- {settings.allowRandomAccountCreation &&
- <p class="mt-10 text-center text-sm text-gray-500 border-t">
- <button type="submit"
- class="flex mt-4 w-full justify-center rounded-md bg-green-600 px-3 py-1.5 text-sm font-semibold leading-6 text-white shadow-sm hover:bg-green-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-green-600"
- onClick={(e) => {
- e.preventDefault()
- doRandomRegistration()
- }}
- >
- <i18n.Translate>Create a random user</i18n.Translate>
- </button>
- </p>
- }
- </div>
- </div>
-
- </Fragment>
- );
-}
-
-export function assertUnreachable(x: never): never {
- throw new Error("Didn't expect to get here");
-}
diff --git a/packages/demobank-ui/src/pages/WalletWithdrawForm.tsx b/packages/demobank-ui/src/pages/WalletWithdrawForm.tsx
deleted file mode 100644
index 5eef95f1e..000000000
--- a/packages/demobank-ui/src/pages/WalletWithdrawForm.tsx
+++ /dev/null
@@ -1,281 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2022 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 {
- AmountJson,
- Amounts,
- Logger,
- TranslatedString,
- parseWithdrawUri
-} from "@gnu-taler/taler-util";
-import {
- notifyError,
- useLocalNotification,
- useTranslationContext
-} from "@gnu-taler/web-util/browser";
-import { Fragment, VNode, h } from "preact";
-import { forwardRef } from "preact/compat";
-import { useState } from "preact/hooks";
-import { Attention } from "@gnu-taler/web-util/browser";
-import { useBankCoreApiContext } from "../context/config.js";
-import { useBackendState } from "../hooks/backend.js";
-import { usePreferences } from "../hooks/preferences.js";
-import { undefinedIfEmpty, withRuntimeErrorHandling } from "../utils.js";
-import { OperationState } from "./OperationState/index.js";
-import { InputAmount, doAutoFocus } from "./PaytoWireTransferForm.js";
-import { assertUnreachable } from "./WithdrawalOperationPage.js";
-import { LocalNotificationBanner } from "@gnu-taler/web-util/browser";
-
-const logger = new Logger("WalletWithdrawForm");
-const RefAmount = forwardRef(InputAmount);
-
-
-function OldWithdrawalForm({ goToConfirmOperation, limit, onCancel, focus }: {
- limit: AmountJson;
- focus?: boolean;
- goToConfirmOperation: (operationId: string) => void;
- onCancel: () => void;
-}): VNode {
- const { i18n } = useTranslationContext();
- const [settings, updateSettings] = usePreferences()
-
- const { state: credentials } = useBackendState();
- const creds = credentials.status !== "loggedIn" ? undefined : credentials
-
- const { api } = useBankCoreApiContext()
- const [amountStr, setAmountStr] = useState<string | undefined>(`${settings.maxWithdrawalAmount}`);
- const [notification, notify, handleError] = useLocalNotification()
-
- if (!!settings.currentWithdrawalOperationId) {
- return <Attention type="warning" title={i18n.str`There is an operation already`}>
- <span ref={focus ? doAutoFocus : undefined} />
- <i18n.Translate>
- To complete or cancel the operation click <a class="font-semibold text-yellow-700 hover:text-yellow-600" href={`#/operation/${settings.currentWithdrawalOperationId}`}>here</a>
- </i18n.Translate>
- </Attention>
- }
-
- const trimmedAmountStr = amountStr?.trim();
-
- const parsedAmount = trimmedAmountStr
- ? Amounts.parse(`${limit.currency}:${trimmedAmountStr}`)
- : undefined;
-
- const errors = undefinedIfEmpty({
- amount:
- trimmedAmountStr == null
- ? i18n.str`required`
- : !parsedAmount
- ? i18n.str`invalid`
- : Amounts.cmp(limit, parsedAmount) === -1
- ? i18n.str`balance is not enough`
- : undefined,
- });
-
- async function doStart() {
- if (!parsedAmount || !creds) return;
- await handleError(async () => {
- const resp = await api.createWithdrawal(creds, {
- amount: Amounts.stringify(parsedAmount),
- });
- if (resp.type === "ok") {
- const uri = parseWithdrawUri(resp.body.taler_withdraw_uri);
- if (!uri) {
- return notifyError(
- i18n.str`Server responded with an invalid withdraw URI`,
- i18n.str`Withdraw URI: ${resp.body.taler_withdraw_uri}`);
- } else {
- updateSettings("currentWithdrawalOperationId", uri.withdrawalOperationId)
- goToConfirmOperation(uri.withdrawalOperationId);
- }
- } else {
- switch (resp.case) {
- case "insufficient-funds": {
- notify({
- type: "error",
- title: i18n.str`The operation was rejected due to insufficient funds`,
- description: resp.detail.hint as TranslatedString,
- debug: resp.detail,
- })
- break;
- }
- case "unauthorized": {
- notify({
- type: "error",
- title: i18n.str`The operation was rejected due to insufficient funds`,
- description: resp.detail.hint as TranslatedString,
- debug: resp.detail,
- })
- break;
- }
- case "account-not-found": {
- notify({
- type: "error",
- title: i18n.str`Account not found`,
- description: resp.detail.hint as TranslatedString,
- debug: resp.detail,
- })
- break;
- }
- default: assertUnreachable(resp)
- }
- }
- })
- }
-
- return <form
- class="bg-white shadow-sm ring-1 ring-gray-900/5 sm:rounded-xl md:col-span-2 mt-4"
- autoCapitalize="none"
- autoCorrect="off"
- onSubmit={e => {
- e.preventDefault()
- }}
- >
- <LocalNotificationBanner notification={notification} />
-
- <div class="px-4 py-6 ">
- <div class="grid max-w-xs grid-cols-1 gap-x-6 gap-y-8 sm:grid-cols-6">
- <div class="sm:col-span-5">
- <label for="withdraw-amount">{i18n.str`Amount`}</label>
- <RefAmount
- currency={limit.currency}
- value={amountStr}
- name="withdraw-amount"
- onChange={(v) => {
- setAmountStr(v);
- }}
- error={errors?.amount}
- ref={focus ? doAutoFocus : undefined}
- />
- </div>
- </div>
- <div class="mt-4">
- <div class="sm:inline">
-
- <button type="button"
- class=" inline-flex px-6 py-4 text-sm items-center rounded-l-md bg-white text-gray-900 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus:z-10"
- onClick={(e) => {
- e.preventDefault();
- setAmountStr("50.00")
- }}
- >
- 50.00
- </button>
- <button type="button"
- class=" -ml-px -mr-px inline-flex px-6 py-4 text-sm items-center rounded-r-md sm:rounded-none bg-white text-gray-900 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus:z-10"
- onClick={(e) => {
- e.preventDefault();
- setAmountStr("25.00")
- }}
- >
-
- 25.00
- </button>
- </div>
- <div class="mt-4 sm:inline">
- <button type="button"
- class=" -ml-px -mr-px inline-flex px-6 py-4 text-sm items-center rounded-l-md sm:rounded-none bg-white text-gray-900 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus:z-10"
- onClick={(e) => {
- e.preventDefault();
- setAmountStr("10.00")
- }}
- >
- 10.00
- </button>
- <button type="button"
- class=" inline-flex px-6 py-4 text-sm items-center rounded-r-md bg-white text-gray-900 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus:z-10"
- onClick={(e) => {
- e.preventDefault();
- setAmountStr("5.00")
- }}
- >
- 5.00
- </button>
- </div>
- </div>
- </div>
- <div class="flex items-center justify-between gap-x-6 border-t border-gray-900/10 px-4 py-4 sm:px-8">
- <button type="button" class="text-sm font-semibold leading-6 text-gray-900"
- onClick={onCancel}
- >
- <i18n.Translate>Cancel</i18n.Translate></button>
- <button type="submit"
- class="disabled:opacity-50 disabled:cursor-default cursor-pointer rounded-md bg-indigo-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600"
- // disabled={isRawPayto ? !!errorsPayto : !!errorsWire}
- onClick={(e) => {
- e.preventDefault()
- doStart()
- }}
- >
- <i18n.Translate>Continue</i18n.Translate>
- </button>
- </div>
-
- </form>
-}
-
-
-export function WalletWithdrawForm({
- focus,
- limit,
- onCancel,
- goToConfirmOperation,
-}: {
- limit: AmountJson;
- focus?: boolean;
- goToConfirmOperation: (operationId: string) => void;
- onCancel: () => void;
-}): VNode {
- const { i18n } = useTranslationContext();
- const [settings, updateSettings] = usePreferences()
-
- return (<div class="grid grid-cols-1 gap-x-8 gap-y-8 pt-10 md:grid-cols-3 bg-gray-100 my-4 px-4 pb-4 rounded-lg">
- <div class="px-4 sm:px-0">
- <h2 class="text-base font-semibold leading-7 text-gray-900"><i18n.Translate>Prepare your wallet</i18n.Translate></h2>
- <p class="mt-1 text-sm text-gray-500">
- <i18n.Translate>After using your wallet you will need to confirm or cancel the operation on this site.</i18n.Translate>
- </p>
- </div>
-
- <div class="col-span-2">
- {settings.showInstallWallet &&
- <Attention title={i18n.str`You need a GNU Taler Wallet`} onClose={() => {
- updateSettings("showInstallWallet", false);
- }}>
- <i18n.Translate>
- If you don't have one yet you can follow the instruction <a target="_blank" rel="noreferrer noopener" class="font-semibold text-blue-700 hover:text-blue-600" href="https://taler.net/en/wallet.html">here</a>
- </i18n.Translate>
- </Attention>
- }
-
- {!settings.fastWithdrawal ?
- <OldWithdrawalForm
- focus={focus}
- limit={limit}
- onCancel={onCancel}
- goToConfirmOperation={goToConfirmOperation}
- />
- :
- <OperationState
- currency={limit.currency}
- onClose={onCancel}
- />
- }
- </div>
- </div>
- );
-}
-
diff --git a/packages/demobank-ui/src/pages/WireTransfer.tsx b/packages/demobank-ui/src/pages/WireTransfer.tsx
deleted file mode 100644
index 7648df482..000000000
--- a/packages/demobank-ui/src/pages/WireTransfer.tsx
+++ /dev/null
@@ -1,52 +0,0 @@
-import { Amounts, TalerError } from "@gnu-taler/taler-util";
-import { Loading, notifyInfo, useTranslationContext } from "@gnu-taler/web-util/browser";
-import { Fragment, VNode, h } from "preact";
-import { ErrorLoadingWithDebug } from "../components/ErrorLoadingWithDebug.js";
-import { useAccountDetails } from "../hooks/access.js";
-import { useBackendState } from "../hooks/backend.js";
-import { LoginForm } from "./LoginForm.js";
-import { PaytoWireTransferForm } from "./PaytoWireTransferForm.js";
-import { assertUnreachable } from "./WithdrawalOperationPage.js";
-
-export function WireTransfer({ toAccount, onRegister, onCancel, onSuccess }: { onSuccess?: () => void; toAccount?: string, onCancel?: () => void, onRegister?: () => void }): VNode {
- const { i18n } = useTranslationContext();
- const r = useBackendState();
- const account = r.state.status !== "loggedOut" ? r.state.username : "admin";
- const result = useAccountDetails(account);
-
- if (!result) {
- return <Loading />
- }
- if (result instanceof TalerError) {
- return <ErrorLoadingWithDebug error={result} />
- }
- if (result.type === "fail") {
- switch (result.case) {
- case "unauthorized": return <LoginForm currentUser={account} />
- case "not-found": return <LoginForm currentUser={account} />
- default: assertUnreachable(result)
- }
- }
- const { body: data } = result;
-
- const balance = Amounts.parseOrThrow(data.balance.amount);
- const balanceIsDebit = data.balance.credit_debit_indicator == "debit";
-
- const debitThreshold = Amounts.parseOrThrow(data.debit_threshold);
- const limit = balanceIsDebit
- ? Amounts.sub(debitThreshold, balance).amount
- : Amounts.add(balance, debitThreshold).amount;
- if (!balance) return <Fragment />;
- return (
- <PaytoWireTransferForm
- title={i18n.str`Make a wire transfer`}
- toAccount={toAccount}
- limit={limit}
- onSuccess={() => {
- notifyInfo(i18n.str`Wire transfer created!`);
- if (onSuccess) onSuccess()
- }}
- onCancel={onCancel}
- />
- );
-}
diff --git a/packages/demobank-ui/src/pages/WithdrawalConfirmationQuestion.tsx b/packages/demobank-ui/src/pages/WithdrawalConfirmationQuestion.tsx
deleted file mode 100644
index 43b88ccd8..000000000
--- a/packages/demobank-ui/src/pages/WithdrawalConfirmationQuestion.tsx
+++ /dev/null
@@ -1,356 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2022 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 {
- AmountJson,
- Logger,
- PaytoUri,
- PaytoUriIBAN,
- PaytoUriTalerBank,
- TalerError,
- TranslatedString,
- WithdrawUriResult
-} from "@gnu-taler/taler-util";
-import {
- Attention,
- Loading,
- LocalNotificationBanner,
- ShowInputErrorLabel,
- notifyInfo,
- useLocalNotification,
- useTranslationContext
-} from "@gnu-taler/web-util/browser";
-import { ComponentChildren, Fragment, VNode, h } from "preact";
-import { useMemo, useState } from "preact/hooks";
-import { mutate } from "swr";
-import { ErrorLoadingWithDebug } from "../components/ErrorLoadingWithDebug.js";
-import { useBankCoreApiContext } from "../context/config.js";
-import { useWithdrawalDetails } from "../hooks/access.js";
-import { useBackendState } from "../hooks/backend.js";
-import { usePreferences } from "../hooks/preferences.js";
-import { undefinedIfEmpty } from "../utils.js";
-import { LoginForm } from "./LoginForm.js";
-import { RenderAmount } from "./PaytoWireTransferForm.js";
-import { assertUnreachable } from "./WithdrawalOperationPage.js";
-import { OperationNotFound } from "./WithdrawalQRCode.js";
-
-const logger = new Logger("WithdrawalConfirmationQuestion");
-
-interface Props {
- onAborted: () => void;
- withdrawUri: WithdrawUriResult;
- details: {
- account: PaytoUri,
- reserve: string,
- username: string,
- amount: AmountJson,
- }
-}
-/**
- * Additional authentication required to complete the operation.
- * Not providing a back button, only abort.
- */
-export function WithdrawalConfirmationQuestion({
- onAborted,
- details,
- withdrawUri,
-}: Props): VNode {
- const { i18n } = useTranslationContext();
- const [settings] = usePreferences()
- const { state: credentials } = useBackendState();
- const creds = credentials.status !== "loggedIn" ? undefined : credentials
- const withdrawalInfo = useWithdrawalDetails(withdrawUri.withdrawalOperationId)
- if (!withdrawalInfo) {
- return <Loading />
- }
- if (withdrawalInfo instanceof TalerError) {
- return <ErrorLoadingWithDebug error={withdrawalInfo} />
- }
- if (withdrawalInfo.type === "fail") {
- switch (withdrawalInfo.case) {
- case "not-found": return <OperationNotFound onClose={onAborted} />
- case "invalid-id": return <OperationNotFound onClose={onAborted} />
- default: assertUnreachable(withdrawalInfo)
- }
- }
-
- const captchaNumbers = useMemo(() => {
- return {
- a: Math.floor(Math.random() * 10),
- b: Math.floor(Math.random() * 10),
- };
- }, []);
- const [notification, notify, handleError] = useLocalNotification()
-
- const { config, api } = useBankCoreApiContext()
- const [captchaAnswer, setCaptchaAnswer] = useState<string | undefined>();
- const answer = parseInt(captchaAnswer ?? "", 10);
- const [busy, setBusy] = useState<Record<string, undefined>>()
- const errors = undefinedIfEmpty({
- answer: !captchaAnswer
- ? i18n.str`Answer the question before continue`
- : Number.isNaN(answer)
- ? i18n.str`The answer should be a number`
- : answer !== captchaNumbers.a + captchaNumbers.b
- ? i18n.str`The answer "${answer}" to "${captchaNumbers.a} + ${captchaNumbers.b}" is wrong.`
- : undefined,
- }) ?? busy;
-
- async function doTransfer() {
- setBusy({})
- await handleError(async () => {
- if (!creds) return;
- const resp = await api.confirmWithdrawalById(creds, withdrawUri.withdrawalOperationId);
- if (resp.type === "ok") {
- mutate(() => true)// clean any info that we have
- if (!settings.showWithdrawalSuccess) {
- notifyInfo(i18n.str`Wire transfer completed!`)
- }
- } else {
- switch (resp.case) {
- case "previously-aborted": return notify({
- type: "error",
- title: i18n.str`The withdrawal has been aborted previously and can't be confirmed`,
- description: resp.detail.hint as TranslatedString,
- debug: resp.detail,
- });
- case "no-exchange-or-reserve-selected": return notify({
- type: "error",
- title: i18n.str`The withdraw operation cannot be confirmed because no exchange and reserve public key selection happened before`,
- description: resp.detail.hint as TranslatedString,
- debug: resp.detail,
- });
- case "invalid-id": return notify({
- type: "error",
- title: i18n.str`The operation id is invalid.`,
- description: resp.detail.hint as TranslatedString,
- debug: resp.detail,
- })
- case "not-found": return notify({
- type: "error",
- title: i18n.str`The operation was not found.`,
- description: resp.detail.hint as TranslatedString,
- debug: resp.detail,
- })
- case "insufficient-funds": return notify({
- type: "error",
- title: i18n.str`Your balance is not enough for the operation.`,
- description: resp.detail.hint as TranslatedString,
- debug: resp.detail,
- })
- default: assertUnreachable(resp)
- }
- }
- })
- setBusy(undefined)
- }
-
- async function doCancel() {
- setBusy({})
- await handleError(async () => {
- if (!creds) return;
- const resp = await api.abortWithdrawalById(creds, withdrawUri.withdrawalOperationId);
- if (resp.type === "ok") {
- onAborted();
- } else {
- switch (resp.case) {
- case "previously-confirmed": return notify({
- type: "error",
- title: i18n.str`The reserve operation has been confirmed previously and can't be aborted`
- });
- case "invalid-id": return notify({
- type: "error",
- title: i18n.str`The operation id is invalid.`,
- description: resp.detail.hint as TranslatedString,
- debug: resp.detail,
- })
- case "not-found": return notify({
- type: "error",
- title: i18n.str`The operation was not found.`,
- description: resp.detail.hint as TranslatedString,
- debug: resp.detail,
- })
- default: {
- assertUnreachable(resp)
- }
- }
- }
- })
- setBusy(undefined)
- }
-
- return (
- <Fragment>
- <LocalNotificationBanner notification={notification} />
-
- <div class="bg-white shadow sm:rounded-lg">
- <div class="px-4 py-5 sm:p-6">
- <h3 class="text-base font-semibold text-gray-900">
- <i18n.Translate>Confirm the withdrawal operation</i18n.Translate>
- </h3>
- <div class="mt-3 text-sm leading-6">
-
- <ShouldBeSameUser username={details.username}>
- <div class="grid grid-cols-1 gap-x-8 gap-y-8 pt-10 md:grid-cols-3 bg-gray-100 my-4 px-4 pb-4 rounded-lg">
- <div class="px-4 sm:px-0">
- <h2 class="text-base font-semibold text-gray-900"><i18n.Translate>Answer the next question to authorize the wire transfer.</i18n.Translate></h2>
- </div>
- <form
- class="bg-white shadow-sm ring-1 ring-gray-900/5 sm:rounded-xl md:col-span-2"
- autoCapitalize="none"
- autoCorrect="off"
- onSubmit={e => {
- e.preventDefault()
- }}
- >
- <div class="px-4 py-6 sm:p-8">
- <label for="withdraw-amount">{i18n.str`What is`}&nbsp;
- <em>
- {captchaNumbers.a}&nbsp;+&nbsp;{captchaNumbers.b}
- </em>
- ?
- </label>
-
- <div class="mt-2">
- <div class="relative rounded-md shadow-sm">
- <input
- type="text"
- // class="block w-full rounded-md border-0 py-1.5 pl-16 text-gray-900 ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
- aria-describedby="answer"
- autoFocus
- class="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
- value={captchaAnswer ?? ""}
- required
-
- name="answer"
- id="answer"
- autocomplete="off"
- onChange={(e): void => {
- setCaptchaAnswer(e.currentTarget.value)
- }}
- />
- </div>
- <ShowInputErrorLabel message={errors?.answer} isDirty={captchaAnswer !== undefined} />
- </div>
- </div>
-
- <div class="flex items-center justify-between gap-x-6 border-t border-gray-900/10 px-4 py-4 sm:px-8">
- <button type="button" class="text-sm font-semibold leading-6 text-gray-900"
- onClick={doCancel}
- >
- <i18n.Translate>Cancel</i18n.Translate></button>
- <button type="submit"
- class="disabled:opacity-50 disabled:cursor-default cursor-pointer rounded-md bg-indigo-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600"
- disabled={!!errors}
- onClick={(e) => {
- e.preventDefault()
- doTransfer()
- }}
- >
- <i18n.Translate>Transfer</i18n.Translate>
- </button>
- </div>
-
- </form>
- </div>
- </ShouldBeSameUser>
- </div>
- <div class="px-4 mt-4 ">
- <div class="w-full">
- <div class="px-4 sm:px-0 text-sm">
- <p><i18n.Translate>Wire transfer details</i18n.Translate></p>
- </div>
- <div class="mt-6 border-t border-gray-100">
- <dl class="divide-y divide-gray-100">
- {((): VNode => {
- switch (details.account.targetType) {
- case "iban": {
- const p = details.account as PaytoUriIBAN
- const name = p.params["receiver-name"]
- return <Fragment>
- <div class="px-4 py-2 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-0">
- <dt class="text-sm font-medium leading-6 text-gray-900">Exchange account</dt>
- <dd class="mt-1 text-sm leading-6 text-gray-700 sm:col-span-2 sm:mt-0">{p.iban}</dd>
- </div>
- {name &&
- <div class="px-4 py-2 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-0">
- <dt class="text-sm font-medium leading-6 text-gray-900">Exchange name</dt>
- <dd class="mt-1 text-sm leading-6 text-gray-700 sm:col-span-2 sm:mt-0">{p.params["receiver-name"]}</dd>
- </div>
- }
- </Fragment>
- }
- case "x-taler-bank": {
- const p = details.account as PaytoUriTalerBank
- const name = p.params["receiver-name"]
- return <Fragment>
- <div class="px-4 py-2 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-0">
- <dt class="text-sm font-medium leading-6 text-gray-900">Exchange account</dt>
- <dd class="mt-1 text-sm leading-6 text-gray-700 sm:col-span-2 sm:mt-0">{p.account}</dd>
- </div>
- {name &&
- <div class="px-4 py-2 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-0">
- <dt class="text-sm font-medium leading-6 text-gray-900">Exchange name</dt>
- <dd class="mt-1 text-sm leading-6 text-gray-700 sm:col-span-2 sm:mt-0">{p.params["receiver-name"]}</dd>
- </div>
- }
- </Fragment>
- }
- default:
- return <div class="px-4 py-2 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-0">
- <dt class="text-sm font-medium leading-6 text-gray-900">Exchange account</dt>
- <dd class="mt-1 text-sm leading-6 text-gray-700 sm:col-span-2 sm:mt-0">{details.account.targetPath}</dd>
- </div>
-
- }
- })()}
- <div class="px-4 py-2 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-0">
- <dt class="text-sm font-medium leading-6 text-gray-900">Amount</dt>
- <dd class="mt-1 text-sm leading-6 text-gray-700 sm:col-span-2 sm:mt-0">
- <RenderAmount value={details.amount} spec={config.currency_specification} />
- </dd>
- </div>
- </dl>
- </div>
- </div>
-
- </div>
- </div>
- </div>
-
- </Fragment >
- );
-}
-
-export function ShouldBeSameUser({ username, children }: { username: string, children: ComponentChildren }): VNode {
- const { state: credentials } = useBackendState();
- const { i18n } = useTranslationContext()
- if (credentials.status === "loggedOut") {
- return <Fragment>
- <Attention type="info" title={i18n.str`Authentication required`} />
- <LoginForm currentUser={username} fixedUser />
- </Fragment>
- }
- if (credentials.username !== username) {
- return <Fragment>
- <Attention type="warning" title={i18n.str`This operation was created with other username`} />
- <LoginForm currentUser={username} fixedUser />
- </Fragment>
- }
- return <Fragment>
- {children}
- </Fragment>
-} \ No newline at end of file
diff --git a/packages/demobank-ui/src/pages/WithdrawalOperationPage.tsx b/packages/demobank-ui/src/pages/WithdrawalOperationPage.tsx
deleted file mode 100644
index 7060b7a98..000000000
--- a/packages/demobank-ui/src/pages/WithdrawalOperationPage.tsx
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2022 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 {
- Logger,
- parseWithdrawUri,
- stringifyWithdrawUri
-} from "@gnu-taler/taler-util";
-import {
- useTranslationContext
-} from "@gnu-taler/web-util/browser";
-import { Fragment, VNode, h } from "preact";
-import { Attention } from "@gnu-taler/web-util/browser";
-import { useBankCoreApiContext } from "../context/config.js";
-import { usePreferences } from "../hooks/preferences.js";
-import { WithdrawalQRCode } from "./WithdrawalQRCode.js";
-
-const logger = new Logger("AccountPage");
-
-export function WithdrawalOperationPage({
- operationId,
- onContinue,
-}: {
- operationId: string;
- onContinue: () => void;
-}): VNode {
- const { api } = useBankCoreApiContext()
- const uri = stringifyWithdrawUri({
- bankIntegrationApiBaseUrl: api.getIntegrationAPI().baseUrl,
- withdrawalOperationId: operationId,
- });
- const parsedUri = parseWithdrawUri(uri);
- const { i18n } = useTranslationContext();
- const [settings, updateSettings] = usePreferences();
-
- if (!parsedUri) {
- return <Attention type="danger" title={i18n.str`The Withdrawal URI is not valid`}>
- {uri}
- </Attention>
- }
-
- return (
- <WithdrawalQRCode
- withdrawUri={parsedUri}
- onClose={() => {
- updateSettings("currentWithdrawalOperationId", undefined)
- onContinue()
- }}
- />
- );
-}
-
-export function assertUnreachable(x: never): never {
- throw new Error("Didn't expect to get here");
-}
diff --git a/packages/demobank-ui/src/pages/WithdrawalQRCode.tsx b/packages/demobank-ui/src/pages/WithdrawalQRCode.tsx
deleted file mode 100644
index c5a558ab8..000000000
--- a/packages/demobank-ui/src/pages/WithdrawalQRCode.tsx
+++ /dev/null
@@ -1,210 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2022 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 {
- Amounts,
- Logger,
- TalerError,
- WithdrawUriResult,
- parsePaytoUri
-} from "@gnu-taler/taler-util";
-import { Attention, Loading, notifyInfo, useTranslationContext } from "@gnu-taler/web-util/browser";
-import { Fragment, VNode, h } from "preact";
-import { ErrorLoadingWithDebug } from "../components/ErrorLoadingWithDebug.js";
-import { useWithdrawalDetails } from "../hooks/access.js";
-import { QrCodeSection } from "./QrCodeSection.js";
-import { WithdrawalConfirmationQuestion } from "./WithdrawalConfirmationQuestion.js";
-import { assertUnreachable } from "./WithdrawalOperationPage.js";
-
-const logger = new Logger("WithdrawalQRCode");
-
-interface Props {
- withdrawUri: WithdrawUriResult;
- onClose: () => void;
-}
-/**
- * Offer the QR code (and a clickable taler://-link) to
- * permit the passing of exchange and reserve details to
- * the bank. Poll the backend until such operation is done.
- */
-export function WithdrawalQRCode({
- withdrawUri,
- onClose,
-}: Props): VNode {
- const { i18n } = useTranslationContext();
- const result = useWithdrawalDetails(withdrawUri.withdrawalOperationId);
-
- if (!result) {
- return <Loading />
- }
- if (result instanceof TalerError) {
- return <ErrorLoadingWithDebug error={result} />
- }
- if (result.type === "fail") {
- switch (result.case) {
- case "invalid-id":
- case "not-found": return <OperationNotFound onClose={onClose} />
- default: assertUnreachable(result)
- }
- }
-
- const { body: data } = result;
-
- if (data.status === "aborted") {
- return <section id="main" class="content">
- <h1 class="nav">{i18n.str`Operation aborted`}</h1>
- <section>
- <p>
- <i18n.Translate>
- The wire transfer to the GNU Taler Exchange bank's account was aborted, your balance
- was not affected.
- </i18n.Translate>
- </p>
- <p>
- <i18n.Translate>
- You can close this page now or continue to the account page.
- </i18n.Translate>
- </p>
- <a class="pure-button pure-button-primary"
- style={{ float: "right" }}
- onClick={async (e) => {
- e.preventDefault();
- onClose()
- }}>
- {i18n.str`Continue`}
- </a>
-
- </section>
- </section>
- }
-
- if (data.status === "confirmed") {
- return <div class="relative ml-auto mr-auto transform overflow-hidden rounded-lg bg-white px-4 pb-4 pt-5 text-left shadow-xl transition-all sm:my-8 sm:w-full sm:max-w-sm sm:p-6">
- <div>
- <div class="mx-auto flex h-12 w-12 items-center justify-center rounded-full bg-green-100">
- <svg class="h-6 w-6 text-green-600" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" aria-hidden="true">
- <path stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5" />
- </svg>
- </div>
- <div class="mt-3 text-center sm:mt-5">
- <h3 class="text-base font-semibold leading-6 text-gray-900" id="modal-title">
- <i18n.Translate>Withdrawal confirmed</i18n.Translate>
- </h3>
- <div class="mt-2">
- <p class="text-sm text-gray-500">
- <i18n.Translate>
- The wire transfer to the Taler operator has been initiated. You will soon receive the requested amount in your Taler wallet.
- </i18n.Translate>
- </p>
- </div>
- </div>
- </div>
- <div class="mt-5 sm:mt-6">
- <button type="button"
- class="inline-flex w-full justify-center rounded-md bg-indigo-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600"
- onClick={async (e) => {
- e.preventDefault();
- onClose()
- }}>
- <i18n.Translate>Done</i18n.Translate>
- </button>
- </div>
- </div>
-
-
- }
- if (data.status === "pending") {
- return (
- <QrCodeSection
- withdrawUri={withdrawUri}
- onAborted={() => {
- notifyInfo(i18n.str`Operation canceled`);
- onClose()
- }}
- />
- );
- }
-
- if (!data.selected_reserve_pub) {
- return <Attention type="danger"
- title={i18n.str`The operation is incomplete or some step in the withdrawal failed`} >
- <i18n.Translate>The exchange is selected but no reserve public key found.</i18n.Translate>
- </Attention>
- }
-
- const account = !data.selected_exchange_account ? undefined : parsePaytoUri(data.selected_exchange_account)
-
- if (!account) {
- return <Attention type="danger"
- title={i18n.str`The operation is incomplete or some step in the withdrawal failed`} >
- <i18n.Translate>The exchange is selected but the exchange payto URI is missing or invalid.</i18n.Translate>
- </Attention>
- }
-
- return (
- <WithdrawalConfirmationQuestion
- withdrawUri={withdrawUri}
- details={{
- username: data.username,
- account,
- reserve: data.selected_reserve_pub,
- amount: Amounts.parseOrThrow(data.amount)
- }}
- onAborted={() => {
- notifyInfo(i18n.str`Operation canceled`);
- onClose()
- }}
- />
- );
-}
-
-
-export function OperationNotFound({ onClose }: { onClose: () => void }): VNode {
- const { i18n } = useTranslationContext();
- return <div class="relative ml-auto mr-auto transform overflow-hidden rounded-lg bg-white px-4 pb-4 pt-5 text-left shadow-xl transition-all sm:my-8 sm:w-full sm:max-w-sm sm:p-6">
- <div>
- <div class="mx-auto flex h-12 w-12 items-center justify-center rounded-full bg-red-100 ">
- <svg class="h-6 w-6 text-red-600" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" aria-hidden="true">
- <path stroke-linecap="round" stroke-linejoin="round" d="M12 9v3.75m-9.303 3.376c-.866 1.5.217 3.374 1.948 3.374h14.71c1.73 0 2.813-1.874 1.948-3.374L13.949 3.378c-.866-1.5-3.032-1.5-3.898 0L2.697 16.126zM12 15.75h.007v.008H12v-.008z" />
- </svg>
- </div>
-
- <div class="mt-3 text-center sm:mt-5">
- <h3 class="text-base font-semibold leading-6 text-gray-900" id="modal-title">
- <i18n.Translate>Operation not found</i18n.Translate>
- </h3>
- <div class="mt-2">
- <p class="text-sm text-gray-500">
- <i18n.Translate>
- This operation is not known by the server. The operation id is wrong or the
- server deleted the operation information before reaching here.
- </i18n.Translate>
- </p>
- </div>
- </div>
- </div>
- <div class="mt-5 sm:mt-6">
- <button type="button"
- class="inline-flex w-full justify-center rounded-md bg-indigo-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600"
- onClick={async (e) => {
- e.preventDefault();
- onClose()
- }}>
- <i18n.Translate>Cotinue to dashboard</i18n.Translate>
- </button>
- </div>
- </div>
-} \ No newline at end of file
diff --git a/packages/demobank-ui/src/pages/account/CashoutListForAccount.tsx b/packages/demobank-ui/src/pages/account/CashoutListForAccount.tsx
deleted file mode 100644
index f2972ed65..000000000
--- a/packages/demobank-ui/src/pages/account/CashoutListForAccount.tsx
+++ /dev/null
@@ -1,40 +0,0 @@
-import { useTranslationContext } from "@gnu-taler/web-util/browser";
-import { Fragment, VNode, h } from "preact";
-import { Cashouts } from "../../components/Cashouts/index.js";
-import { useBackendState } from "../../hooks/backend.js";
-import { ProfileNavigation } from "../ProfileNavigation.js";
-import { CreateNewAccount } from "../admin/CreateNewAccount.js";
-import { CreateCashout } from "../business/CreateCashout.js";
-
-interface Props {
- account: string,
- onClose: () => void,
- onSelected: (cid: number) => void
-}
-
-export function CashoutListForAccount({ account, onSelected, onClose }: Props): VNode {
- const { i18n } = useTranslationContext();
-
- const { state: credentials } = useBackendState();
-
- const accountIsTheCurrentUser = credentials.status === "loggedIn" ?
- credentials.username === account : false
-
- return <Fragment>
- {accountIsTheCurrentUser ?
- <ProfileNavigation current="cashouts" />
- :
- <h1 class="text-base font-semibold leading-6 text-gray-900">
- <i18n.Translate>Cashout for account {account}</i18n.Translate>
- </h1>
- }
-
- <CreateCashout focus onCancel={onClose} onComplete={() => { }} account={account} />
-
- <Cashouts
- account={account}
- onSelected={onSelected}
- />
- </Fragment>
-}
-
diff --git a/packages/demobank-ui/src/pages/account/ShowAccountDetails.tsx b/packages/demobank-ui/src/pages/account/ShowAccountDetails.tsx
deleted file mode 100644
index 1f2d67c49..000000000
--- a/packages/demobank-ui/src/pages/account/ShowAccountDetails.tsx
+++ /dev/null
@@ -1,165 +0,0 @@
-import { TalerCorebankApi, TalerError, TranslatedString } from "@gnu-taler/taler-util";
-import { Loading, LocalNotificationBanner, notifyInfo, useLocalNotification, useTranslationContext } from "@gnu-taler/web-util/browser";
-import { Fragment, VNode, h } from "preact";
-import { useState } from "preact/hooks";
-import { ErrorLoadingWithDebug } from "../../components/ErrorLoadingWithDebug.js";
-import { useBankCoreApiContext } from "../../context/config.js";
-import { useAccountDetails } from "../../hooks/access.js";
-import { useBackendState } from "../../hooks/backend.js";
-import { LoginForm } from "../LoginForm.js";
-import { ProfileNavigation } from "../ProfileNavigation.js";
-import { assertUnreachable } from "../WithdrawalOperationPage.js";
-import { AccountForm } from "../admin/AccountForm.js";
-
-export function ShowAccountDetails({
- account,
- onClear,
- onUpdateSuccess,
-}: {
- onClear?: () => void;
- onUpdateSuccess: () => void;
- account: string;
-}): VNode {
- const { i18n } = useTranslationContext();
- const { state: credentials } = useBackendState();
- const creds = credentials.status !== "loggedIn" ? undefined : credentials
- const { api } = useBankCoreApiContext()
- const accountIsTheCurrentUser = credentials.status === "loggedIn" ?
- credentials.username === account : false
-
- const [update, setUpdate] = useState(false);
- const [submitAccount, setSubmitAccount] = useState<TalerCorebankApi.AccountReconfiguration | undefined>();
- const [notification, notify, handleError] = useLocalNotification()
-
- const result = useAccountDetails(account);
- if (!result) {
- return <Loading />
- }
- if (result instanceof TalerError) {
- return <ErrorLoadingWithDebug error={result} />
- }
- if (result.type === "fail") {
- switch (result.case) {
- case "not-found": return <LoginForm currentUser={account} />
- case "unauthorized": return <LoginForm currentUser={account} />
- default: assertUnreachable(result)
- }
- }
-
- async function doUpdate() {
- if (!update || !submitAccount || !creds) return;
- await handleError(async () => {
- const resp = await api.updateAccount({
- token: creds.token,
- username: account,
- }, submitAccount);
-
- if (resp.type === "ok") {
- notifyInfo(i18n.str`Account updated`);
- onUpdateSuccess();
- } else {
- switch (resp.case) {
- case "unauthorized": return notify({
- type: "error",
- title: i18n.str`The rights to change the account are not sufficient`,
- description: resp.detail.hint as TranslatedString,
- debug: resp.detail,
- })
- case "not-found": return notify({
- type: "error",
- title: i18n.str`The username was not found`,
- description: resp.detail.hint as TranslatedString,
- debug: resp.detail,
- })
- case "user-cant-change-name": return notify({
- type: "error",
- title: i18n.str`You can't change the legal name, please contact the your account administrator.`,
- description: resp.detail.hint as TranslatedString,
- debug: resp.detail,
- })
- case "user-cant-change-debt": return notify({
- type: "error",
- title: i18n.str`You can't change the debt limit, please contact the your account administrator.`,
- description: resp.detail.hint as TranslatedString,
- debug: resp.detail,
- })
- case "user-cant-change-cashout": return notify({
- type: "error",
- title: i18n.str`You can't change the cashout address, please contact the your account administrator.`,
- description: resp.detail.hint as TranslatedString,
- debug: resp.detail,
- })
- case "user-cant-change-contact": return notify({
- type: "error",
- title: i18n.str`You can't change the contact info, please contact the your account administrator.`,
- description: resp.detail.hint as TranslatedString,
- debug: resp.detail,
- })
- default: assertUnreachable(resp)
- }
- }
- })
-
- }
-
- return (
- <Fragment>
- <LocalNotificationBanner notification={notification} />
- {accountIsTheCurrentUser ?
- <ProfileNavigation current="details" />
- :
- <h1 class="text-base font-semibold leading-6 text-gray-900">
- <i18n.Translate>Account "{account}"</i18n.Translate>
- </h1>
-
- }
-
- <div class="grid grid-cols-1 gap-x-8 gap-y-8 pt-10 md:grid-cols-3 bg-gray-100 my-4 px-4 pb-4 rounded-lg">
- <div class="px-4 sm:px-0">
- <h2 class="text-base font-semibold leading-7 text-gray-900">
- <div class="flex items-center justify-between">
- <span class="flex flex-grow flex-col">
- <span class="text-sm text-black font-semibold leading-6 " id="availability-label">
- <i18n.Translate>Change details</i18n.Translate>
- </span>
- </span>
- <button type="button" data-enabled={!update} class="bg-indigo-600 data-[enabled=true]:bg-gray-200 relative inline-flex h-5 w-10 flex-shrink-0 cursor-pointer rounded-full ring-2 border-gray-600 transition-colors duration-200 ease-in-out focus:outline-none focus:ring-2 focus:ring-indigo-600 focus:ring-offset-2" role="switch" aria-checked="false" aria-labelledby="availability-label" aria-describedby="availability-description"
- onClick={() => {
- setUpdate(!update)
- }}>
- <span aria-hidden="true" data-enabled={!update} class="translate-x-5 data-[enabled=true]:translate-x-0 pointer-events-none inline-block h-5 w-5 transform rounded-full bg-white shadow ring-0 transition duration-200 ease-in-out"></span>
- </button>
- </div>
- </h2>
- </div>
-
- <AccountForm
- focus={update}
- username={account}
- template={result.body}
- purpose={update ? "update" : "show"}
- onChange={(a) => setSubmitAccount(a)}
- >
- <div class="flex items-center justify-between gap-x-6 border-t border-gray-900/10 px-4 py-4 sm:px-8">
- {onClear ?
- <button type="button" class="text-sm font-semibold leading-6 text-gray-900"
- onClick={onClear}
- >
- <i18n.Translate>Cancel</i18n.Translate>
- </button>
- : <div />
- }
- <button type="submit"
- class="disabled:opacity-50 disabled:cursor-default cursor-pointer rounded-md bg-indigo-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600"
- disabled={!update || !submitAccount}
- onClick={doUpdate}
- >
- <i18n.Translate>Update</i18n.Translate>
- </button>
- </div>
- </AccountForm>
- </div>
- </Fragment>
- );
-}
-
diff --git a/packages/demobank-ui/src/pages/account/UpdateAccountPassword.tsx b/packages/demobank-ui/src/pages/account/UpdateAccountPassword.tsx
deleted file mode 100644
index ece1f63e7..000000000
--- a/packages/demobank-ui/src/pages/account/UpdateAccountPassword.tsx
+++ /dev/null
@@ -1,227 +0,0 @@
-import { notifyInfo, useLocalNotification, useTranslationContext } from "@gnu-taler/web-util/browser";
-import { Fragment, VNode, h } from "preact";
-import { useState } from "preact/hooks";
-import { ShowInputErrorLabel } from "@gnu-taler/web-util/browser";
-import { useBankCoreApiContext } from "../../context/config.js";
-import { useBackendState } from "../../hooks/backend.js";
-import { undefinedIfEmpty, withRuntimeErrorHandling } from "../../utils.js";
-import { doAutoFocus } from "../PaytoWireTransferForm.js";
-import { ProfileNavigation } from "../ProfileNavigation.js";
-import { assertUnreachable } from "../WithdrawalOperationPage.js";
-import { LocalNotificationBanner } from "@gnu-taler/web-util/browser";
-
-export function UpdateAccountPassword({
- account: accountName,
- onCancel,
- onUpdateSuccess,
- focus,
-}: {
- onCancel: () => void;
- focus?: boolean,
- onUpdateSuccess: () => void;
- account: string;
-}): VNode {
- const { i18n } = useTranslationContext();
- const { state: credentials } = useBackendState();
- const token = credentials.status !== "loggedIn" ? undefined : credentials.token
- const { api } = useBankCoreApiContext();
-
- const [current, setCurrent] = useState<string | undefined>();
- const [password, setPassword] = useState<string | undefined>();
- const [repeat, setRepeat] = useState<string | undefined>();
-
- const accountIsTheCurrentUser = credentials.status === "loggedIn" ?
- credentials.username === accountName : false
-
- const errors = undefinedIfEmpty({
- current: !accountIsTheCurrentUser ? undefined : !current ? i18n.str`required` : undefined,
- password: !password ? i18n.str`required` : undefined,
- repeat: !repeat
- ? i18n.str`required`
- : password !== repeat
- ? i18n.str`password doesn't match`
- : undefined,
- });
- const [notification, notify, handleError] = useLocalNotification()
-
-
- async function doChangePassword() {
- if (!!errors || !password || !token) return;
- await handleError(async () => {
- const resp = await api.updatePassword({ username: accountName, token }, {
- old_password: current,
- new_password: password,
- });
- if (resp.type === "ok") {
- notifyInfo(i18n.str`Password changed`);
- onUpdateSuccess();
- } else {
- switch (resp.case) {
- case "unauthorized": return notify({
- type: "error",
- title: i18n.str`Not authorized to change the password, maybe the session is invalid.`
- })
- case "not-found": return notify({
- type: "error",
- title: i18n.str`Account not found`
- })
- case "user-require-old-password": return notify({
- type: "error",
- title: i18n.str`Old password need to be provided in order to change new one. If you don't have it contact your account administrator.`
- })
- case "wrong-old-password": return notify({
- type: "error",
- title: i18n.str`Your current password doesn't match, can't change to a new password.`
- })
- default: assertUnreachable(resp)
- }
- }
- })
- }
-
- return (
- <Fragment>
- <LocalNotificationBanner notification={notification} />
- {accountIsTheCurrentUser ?
- <ProfileNavigation current="credentials" /> :
- <h1 class="text-base font-semibold leading-6 text-gray-900">
- <i18n.Translate>Account "{accountName}"</i18n.Translate>
- </h1>
-
- }
-
- <div class="grid grid-cols-1 gap-x-8 gap-y-8 pt-10 md:grid-cols-3 bg-gray-100 my-4 px-4 pb-4 rounded-lg">
- <div class="px-4 sm:px-0">
- <h2 class="text-base font-semibold leading-7 text-gray-900">
- <i18n.Translate>Update password</i18n.Translate>
- </h2>
- </div>
- <form
- class="bg-white shadow-sm ring-1 ring-gray-900/5 sm:rounded-xl md:col-span-2"
- autoCapitalize="none"
- autoCorrect="off"
- onSubmit={e => {
- e.preventDefault()
- }}
- >
- <div class="px-4 py-6 sm:p-8">
- <div class="grid max-w-2xl grid-cols-1 gap-x-6 gap-y-8 sm:grid-cols-6">
- <div class="sm:col-span-5">
- <label
- class="block text-sm font-medium leading-6 text-gray-900"
- for="password"
- >
- {i18n.str`New password`}
- </label>
- <div class="mt-2">
- <input
- ref={focus ? doAutoFocus : undefined}
- type="password"
- class="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 data-[error=true]:ring-red-500 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
- name="password"
- id="password"
- data-error={!!errors?.password && password !== undefined}
- value={password ?? ""}
- onChange={(e) => {
- setPassword(e.currentTarget.value)
- }}
- autocomplete="off"
- />
- <ShowInputErrorLabel
- message={errors?.password}
- isDirty={password !== undefined}
- />
- </div>
- </div>
-
- <div class="sm:col-span-5">
- <label
- class="block text-sm font-medium leading-6 text-gray-900"
- for="repeat"
- >
- {i18n.str`Type it again`}
- </label>
- <div class="mt-2">
- <input
- type="password"
- class="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 data-[error=true]:ring-red-500 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
- name="repeat"
- id="repeat"
- data-error={!!errors?.repeat && repeat !== undefined}
- value={repeat ?? ""}
- onChange={(e) => {
- setRepeat(e.currentTarget.value)
- }}
- // placeholder=""
- autocomplete="off"
- />
- <ShowInputErrorLabel
- message={errors?.repeat}
- isDirty={repeat !== undefined}
- />
- </div>
- <p class="mt-2 text-sm text-gray-500" >
- <i18n.Translate>repeat the same password</i18n.Translate>
- </p>
- </div>
-
- {accountIsTheCurrentUser ?
- <div class="sm:col-span-5">
- <label
- class="block text-sm font-medium leading-6 text-gray-900"
- for="password"
- >
- {i18n.str`Current password`}
- </label>
- <div class="mt-2">
- <input
- type="password"
- class="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 data-[error=true]:ring-red-500 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
- name="current"
- id="current-password"
- data-error={!!errors?.current && current !== undefined}
- value={current ?? ""}
- onChange={(e) => {
- setCurrent(e.currentTarget.value)
- }}
- autocomplete="off"
- />
- <ShowInputErrorLabel
- message={errors?.current}
- isDirty={current !== undefined}
- />
- </div>
- <p class="mt-2 text-sm text-gray-500" >
- <i18n.Translate>your current password, for security</i18n.Translate>
- </p>
- </div>
- : undefined}
-
- </div>
- </div>
- <div class="flex items-center justify-between gap-x-6 border-t border-gray-900/10 px-4 py-4 sm:px-8">
- {onCancel ?
- <button type="button" class="text-sm font-semibold leading-6 text-gray-900"
- onClick={onCancel}
- >
- <i18n.Translate>Cancel</i18n.Translate>
- </button>
- : <div />
- }
- <button type="submit"
- class="disabled:opacity-50 disabled:cursor-default cursor-pointer rounded-md bg-indigo-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600"
- disabled={!!errors}
- onClick={(e) => {
- e.preventDefault()
- doChangePassword()
- }}
- >
- <i18n.Translate>Change</i18n.Translate>
- </button>
- </div>
- </form>
- </div>
- </Fragment>
-
- );
-} \ No newline at end of file
diff --git a/packages/demobank-ui/src/pages/admin/AccountForm.tsx b/packages/demobank-ui/src/pages/admin/AccountForm.tsx
deleted file mode 100644
index c64431918..000000000
--- a/packages/demobank-ui/src/pages/admin/AccountForm.tsx
+++ /dev/null
@@ -1,596 +0,0 @@
-import { AmountString, Amounts, PaytoString, TalerCorebankApi, TranslatedString, buildPayto, parsePaytoUri, stringifyPaytoUri } from "@gnu-taler/taler-util";
-import { CopyButton, ShowInputErrorLabel, useTranslationContext } from "@gnu-taler/web-util/browser";
-import { ComponentChildren, Fragment, VNode, h } from "preact";
-import { useState } from "preact/hooks";
-import { useBankCoreApiContext } from "../../context/config.js";
-import { ErrorMessageMappingFor, PartialButDefined, WithIntermediate, undefinedIfEmpty, validateIBAN } from "../../utils.js";
-import { InputAmount, doAutoFocus } from "../PaytoWireTransferForm.js";
-import { assertUnreachable } from "../WithdrawalOperationPage.js";
-import { getRandomPassword } from "../rnd.js";
-import { useBackendState } from "../../hooks/backend.js";
-
-const IBAN_REGEX = /^[A-Z][A-Z0-9]*$/;
-const EMAIL_REGEX =
- /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
-const REGEX_JUST_NUMBERS_REGEX = /^\+[0-9 ]*$/;
-
-export type AccountFormData = {
- debit_threshold?: string,
- isExchange?: boolean,
- isPublic?: boolean,
- name?: string,
- username?: string,
- payto_uri?: string,
- cashout_payto_uri?: string,
- email?: string,
- phone?: string,
-}
-
-type ChangeByPurposeType = {
- "create": (a: TalerCorebankApi.RegisterAccountRequest | undefined) => void,
- "update": (a: TalerCorebankApi.AccountReconfiguration | undefined) => void,
- "show": undefined
-}
-/**
- * FIXME:
- * is_public is missing on PATCH
- * account email/password should require 2FA
- *
- *
- * @param param0
- * @returns
- */
-export function AccountForm<PurposeType extends keyof ChangeByPurposeType>({
- template,
- username,
- purpose,
- onChange,
- focus,
- children,
-}: {
- focus?: boolean,
- children: ComponentChildren,
- username?: string,
- template: TalerCorebankApi.AccountData | undefined;
- onChange: ChangeByPurposeType[PurposeType];
- purpose: PurposeType;
-}): VNode {
- const { config } = useBankCoreApiContext()
- const { i18n } = useTranslationContext();
- const { state: credentials } = useBackendState();
- const [form, setForm] = useState<AccountFormData>({});
-
- const [errors, setErrors] = useState<
- ErrorMessageMappingFor<typeof defaultValue> | undefined
- >(undefined);
-
-
- const defaultValue: AccountFormData = {
- debit_threshold: Amounts.stringifyValue(template?.debit_threshold ??config.default_debit_threshold),
- isExchange: template?.is_taler_exchange,
- isPublic: template?.is_public,
- name: template?.name ?? "",
- cashout_payto_uri: stringifyIbanPayto(template?.cashout_payto_uri) ?? "" as PaytoString,
- payto_uri: stringifyIbanPayto(template?.payto_uri) ?? "" as PaytoString,
- email: template?.contact_data?.email ?? "",
- phone: template?.contact_data?.phone ?? "",
- username: username ?? "",
- }
-
- const showingCurrentUserInfo = credentials.status !== "loggedIn" ? false : username === credentials.username
- const userIsAdmin = credentials.status !== "loggedIn" ? false : credentials.isUserAdministrator
-
- const editableUsername = (purpose === "create")
- const editableName = (purpose === "create" || purpose === "update" && (config.allow_edit_name || userIsAdmin))
- const editableCashout = showingCurrentUserInfo && (purpose === "create" || purpose === "update" && (config.allow_edit_cashout_payto_uri || userIsAdmin))
- const editableThreshold = userIsAdmin && (purpose === "create" || purpose === "update")
- const editableAccount = purpose === "create" && userIsAdmin
-
- function updateForm(newForm: typeof defaultValue): void {
- const cashoutParsed = !newForm.cashout_payto_uri
- ? undefined
- : buildPayto("iban", newForm.cashout_payto_uri, undefined);;
-
- const internalParsed = !newForm.payto_uri
- ? undefined
- : buildPayto("iban", newForm.payto_uri, undefined);;
-
- const trimmedAmountStr = newForm.debit_threshold?.trim();
- const parsedAmount = Amounts.parse(`${config.currency}:${trimmedAmountStr}`);
-
- const errors = undefinedIfEmpty<ErrorMessageMappingFor<typeof defaultValue>>({
- cashout_payto_uri: (!newForm.cashout_payto_uri
- ? undefined :
- !editableCashout ? undefined :
- !cashoutParsed
- ? i18n.str`does not follow the pattern` :
- !cashoutParsed.isKnown || cashoutParsed.targetType !== "iban"
- ? i18n.str`only "IBAN" target are supported` :
- !IBAN_REGEX.test(cashoutParsed.iban)
- ? i18n.str`IBAN should have just uppercased letters and numbers` :
- validateIBAN(cashoutParsed.iban, i18n)),
- payto_uri: (!newForm.payto_uri
- ? undefined :
- !editableAccount ? undefined :
- !internalParsed
- ? i18n.str`does not follow the pattern` :
- !internalParsed.isKnown || internalParsed.targetType !== "iban"
- ? i18n.str`only "IBAN" target are supported` :
- !IBAN_REGEX.test(internalParsed.iban)
- ? i18n.str`IBAN should have just uppercased letters and numbers` :
- validateIBAN(internalParsed.iban, i18n)),
- email: !newForm.email
- ? undefined :
- !EMAIL_REGEX.test(newForm.email)
- ? i18n.str`it should be an email` :
- undefined,
- phone: !newForm.phone
- ? undefined :
- !newForm.phone.startsWith("+") // FIXME: better phone number check
- ? i18n.str`should start with +` :
- !REGEX_JUST_NUMBERS_REGEX.test(newForm.phone)
- ? i18n.str`phone number can't have other than numbers`
- :
- undefined,
- debit_threshold: !editableThreshold ? undefined :
- !trimmedAmountStr ? undefined :
- !parsedAmount ? i18n.str`not valid` :
- undefined,
- name: !editableName ? undefined : //disabled
- !newForm.name ? i18n.str`required` : undefined,
- username: !editableUsername ? undefined : !newForm.username ? i18n.str`required` : undefined,
- });
- setErrors(errors);
-
- setForm(newForm);
- if (!onChange) return;
-
- if (errors) {
- onChange(undefined)
- } else {
- const cashout = !newForm.cashout_payto_uri ? undefined : buildPayto("iban", newForm.cashout_payto_uri, undefined)
- const cashoutURI = !cashout ? undefined : stringifyPaytoUri(cashout)
-
- const internal = !newForm.payto_uri ? undefined : buildPayto("iban", newForm.payto_uri, undefined);
- const internalURI = !internal ? undefined : stringifyPaytoUri(internal)
-
- const threshold = !parsedAmount ? undefined : Amounts.stringify(parsedAmount)
-
- switch (purpose) {
- case "create": {
- //typescript doesn't correctly narrow a generic type
- const callback = onChange as ChangeByPurposeType["create"]
- const result: TalerCorebankApi.RegisterAccountRequest = {
- name: newForm.name!,
- password: getRandomPassword(),
- username: newForm.username!,
- contact_data: undefinedIfEmpty({
- email: newForm.email,
- phone: newForm.phone,
- }),
- debit_threshold: threshold ?? config.default_debit_threshold,
- cashout_payto_uri: cashoutURI,
- payto_uri: internalURI,
- is_public: !!newForm.isPublic,
- is_taler_exchange: !!newForm.isExchange,
- }
- callback(result)
- return;
- }
- case "update": {
- //typescript doesn't correctly narrow a generic type
- const callback = onChange as ChangeByPurposeType["update"]
-
- const result: TalerCorebankApi.AccountReconfiguration = {
- cashout_payto_uri: cashoutURI,
- contact_data: undefinedIfEmpty({
- email: newForm.email ?? template?.contact_data?.email,
- phone: newForm.phone ?? template?.contact_data?.phone,
- }),
- debit_threshold: threshold,
- is_public: !!newForm.isPublic,
- name: newForm.name,
- }
- callback(result)
- return;
- }
- case "show": {
- return;
- }
- default: {
- assertUnreachable(purpose)
- }
- }
- }
- }
- return (
- <form
- class="bg-white shadow-sm ring-1 ring-gray-900/5 sm:rounded-xl md:col-span-2"
- autoCapitalize="none"
- autoCorrect="off"
- onSubmit={e => {
- e.preventDefault()
- }}
- >
- <div class="px-4 py-6 sm:p-8">
- <div class="grid max-w-2xl grid-cols-1 gap-x-6 gap-y-8 sm:grid-cols-6">
-
- <div class="sm:col-span-5">
- <label
- class="block text-sm font-medium leading-6 text-gray-900"
- for="username"
- >
- {i18n.str`Username`}
- {editableUsername && <b style={{ color: "red" }}> *</b>}
- </label>
- <div class="mt-2">
- <input
- ref={focus && purpose === "create" ? doAutoFocus : undefined}
- type="text"
- class="block w-full disabled:bg-gray-100 rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 data-[error=true]:ring-red-500 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
- name="username"
- id="username"
- data-error={!!errors?.username && form.username !== undefined}
- disabled={!editableUsername}
- value={form.username ?? defaultValue.username}
- onChange={(e) => {
- form.username = e.currentTarget.value;
- updateForm(structuredClone(form));
- }}
- // placeholder=""
- autocomplete="off"
- />
- <ShowInputErrorLabel
- message={errors?.username}
- isDirty={form.username !== undefined}
- />
- </div>
- <p class="mt-2 text-sm text-gray-500" >
- <i18n.Translate>account identification in the bank</i18n.Translate>
- </p>
- </div>
-
- <div class="sm:col-span-5">
- <label
- class="block text-sm font-medium leading-6 text-gray-900"
- for="name"
- >
- {i18n.str`Name`}
- {editableName && <b style={{ color: "red" }}> *</b>}
- </label>
- <div class="mt-2">
- <input
- type="text"
- class="block w-full disabled:bg-gray-100 rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 data-[error=true]:ring-red-500 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
- name="name"
- data-error={!!errors?.name && form.name !== undefined}
- id="name"
- disabled={!editableName}
- value={form.name ?? defaultValue.name}
- onChange={(e) => {
- form.name = e.currentTarget.value;
- updateForm(structuredClone(form));
- }}
- // placeholder=""
- autocomplete="off"
- />
- <ShowInputErrorLabel
- message={errors?.name}
- isDirty={form.name !== undefined}
- />
- </div>
- <p class="mt-2 text-sm text-gray-500" >
- <i18n.Translate>name of the person owner the account</i18n.Translate>
- </p>
- </div>
-
-
- <PaytoField
- type="iban"
- name="internal-account"
- label={i18n.str`Internal IBAN`}
- help={purpose === "create" ?
- i18n.str`if empty a random account number will be assigned` :
- i18n.str`account identification for bank transfer`}
- value={(form.payto_uri ?? defaultValue.payto_uri) as PaytoString}
- disabled={!editableAccount}
- error={errors?.payto_uri}
- onChange={(e) => {
- form.payto_uri = e as PaytoString
- updateForm(structuredClone(form))
- }} />
-
- <div class="sm:col-span-5">
- <label
- class="block text-sm font-medium leading-6 text-gray-900"
- for="email"
- >
- {i18n.str`Email`}
- </label>
- <div class="mt-2">
- <input
- type="email"
- class="block w-full disabled:bg-gray-100 rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 data-[error=true]:ring-red-500 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
- name="email"
- id="email"
- data-error={!!errors?.email && form.email !== undefined}
- disabled={purpose === "show"}
- value={form.email ?? defaultValue.email}
- onChange={(e) => {
- form.email = e.currentTarget.value;
- updateForm(structuredClone(form));
- }}
- autocomplete="off"
- />
- <ShowInputErrorLabel
- message={errors?.email}
- isDirty={form.email !== undefined}
- />
- </div>
- </div>
-
- <div class="sm:col-span-5">
- <label
- class="block text-sm font-medium leading-6 text-gray-900"
- for="phone"
- >
- {i18n.str`Phone`}
- </label>
- <div class="mt-2">
- <input
- type="text"
- class="block w-full disabled:bg-gray-100 rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 data-[error=true]:ring-red-500 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
- name="phone"
- id="phone"
- disabled={purpose === "show"}
- value={form.phone ?? defaultValue.phone}
- data-error={!!errors?.phone && form.phone !== undefined}
- onChange={(e) => {
- form.phone = e.currentTarget.value;
- updateForm(structuredClone(form));
- }}
- autocomplete="off"
- />
- <ShowInputErrorLabel
- message={errors?.phone}
- isDirty={form.phone !== undefined}
- />
- </div>
- </div>
-
- {showingCurrentUserInfo &&
- <PaytoField
- type="iban"
- name="cashout-account"
- label={i18n.str`Cashout IBAN`}
- help={i18n.str`account number where the money is going to be sent when doing cashouts`}
- value={(form.cashout_payto_uri ?? defaultValue.cashout_payto_uri) as PaytoString}
- disabled={!editableCashout}
- error={errors?.cashout_payto_uri}
- onChange={(e) => {
- form.cashout_payto_uri = e as PaytoString
- updateForm(structuredClone(form))
- }} />
- }
-
- <div class="sm:col-span-5">
- <label for="debit" class="block text-sm font-medium leading-6 text-gray-900">{i18n.str`Max debt`}</label>
- <InputAmount
- name="debit"
- left
- currency={config.currency}
- value={form.debit_threshold ?? defaultValue.debit_threshold}
- onChange={!editableThreshold ? undefined : (e) => {
- form.debit_threshold = e as AmountString
- updateForm(structuredClone(form))
- }}
- />
- <ShowInputErrorLabel
- message={errors?.debit_threshold ? String(errors?.debit_threshold) : undefined}
- isDirty={form.debit_threshold !== undefined}
- />
- <p class="mt-2 text-sm text-gray-500" >how much is user able to transfer </p>
- </div>
-
- {purpose !== "create" || !userIsAdmin ? undefined :
- <div class="sm:col-span-5">
- <div class="flex items-center justify-between">
- <span class="flex flex-grow flex-col">
- <span class="text-sm text-black font-medium leading-6 " id="availability-label">
- <i18n.Translate>Is an exchange</i18n.Translate>
- </span>
- </span>
- <button type="button" data-enabled={form.isExchange ?? defaultValue.isExchange ? "true" : "false"} class="bg-indigo-600 data-[enabled=false]:bg-gray-200 relative inline-flex h-6 w-11 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none focus:ring-2 focus:ring-indigo-600 focus:ring-offset-2" role="switch" aria-checked="false" aria-labelledby="availability-label" aria-describedby="availability-description"
-
- onClick={() => {
- form.isExchange = !form.isExchange
- updateForm(structuredClone(form))
- }}>
- <span aria-hidden="true" data-enabled={form.isExchange ?? defaultValue.isExchange ? "true" : "false"} class="translate-x-5 data-[enabled=false]:translate-x-0 pointer-events-none inline-block h-5 w-5 transform rounded-full bg-white shadow ring-0 transition duration-200 ease-in-out"></span>
- </button>
- </div>
- </div>}
-
- <div class="sm:col-span-5">
- <div class="flex items-center justify-between">
- <span class="flex flex-grow flex-col">
- <span class="text-sm text-black font-medium leading-6 " id="availability-label">
- <i18n.Translate>Is public</i18n.Translate>
- </span>
- </span>
- <button type="button" data-enabled={form.isPublic ?? defaultValue.isPublic ? "true" : "false"} class="bg-indigo-600 data-[enabled=false]:bg-gray-200 relative inline-flex h-6 w-11 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none focus:ring-2 focus:ring-indigo-600 focus:ring-offset-2" role="switch" aria-checked="false" aria-labelledby="availability-label" aria-describedby="availability-description"
-
- onClick={() => {
- form.isPublic = !form.isPublic
- updateForm(structuredClone(form))
- }}>
- <span aria-hidden="true" data-enabled={form.isPublic ?? defaultValue.isPublic ? "true" : "false"} class="translate-x-5 data-[enabled=false]:translate-x-0 pointer-events-none inline-block h-5 w-5 transform rounded-full bg-white shadow ring-0 transition duration-200 ease-in-out"></span>
- </button>
- </div>
- <p class="mt-2 text-sm text-gray-500" >
- <i18n.Translate>public accounts have their balance publicly accesible</i18n.Translate>
- </p>
- </div>
-
- </div>
- </div>
- <pre>
- {JSON.stringify(errors, undefined, 2)}
- </pre>
- {children}
- </form>
- );
-}
-
-function stringifyIbanPayto(s: PaytoString | undefined): string | undefined {
- if (s === undefined) return undefined
- const p = parsePaytoUri(s)
- if (p === undefined) return undefined
- if (!p.isKnown) return undefined
- if (p.targetType !== "iban") return undefined
- return p.iban
-}
-
-{/* <div class="sm:col-span-5">
- <label
- class="block text-sm font-medium leading-6 text-gray-900"
- for="cashout"
- >
- {}
- </label>
- <div class="mt-2">
- <input
- type="text"
- ref={focus && purpose === "update" ? doAutoFocus : undefined}
- data-error={!!errors?.cashout_payto_uri && form.cashout_payto_uri !== undefined}
- class="block w-full disabled:bg-gray-100 rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 data-[error=true]:ring-red-500 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
- name="cashout"
- id="cashout"
- disabled={purpose === "show"}
- value={form.cashout_payto_uri ?? defaultValue.cashout_payto_uri}
- onChange={(e) => {
- form.cashout_payto_uri = e.currentTarget.value as PaytoString;
- if (!form.cashout_payto_uri) {
- form.cashout_payto_uri = undefined
- }
- updateForm(structuredClone(form));
- }}
- autocomplete="off"
- />
- <ShowInputErrorLabel
- message={errors?.cashout_payto_uri}
- isDirty={form.cashout_payto_uri !== undefined}
- />
- </div>
- <p class="mt-2 text-sm text-gray-500" >
- <i18n.Translate></i18n.Translate>
- </p>
- </div> */}
-
-function PaytoField({ name, label, help, type, value, disabled, onChange, error }: { error: TranslatedString | undefined, name: string, label: TranslatedString, help: TranslatedString, onChange: (s: string) => void, type: "iban" | "x-taler-bank" | "bitcoin", disabled?: boolean, value: string | undefined }): VNode {
- if (type === "iban") {
- return <div class="sm:col-span-5">
- <label
- class="block text-sm font-medium leading-6 text-gray-900"
- for={name}
- >
- {label}
- </label>
- <div class="mt-2">
- <div class="flex justify-between">
- <input
- type="text"
- class="mr-4 w-full block-inline disabled:bg-gray-100 rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 data-[error=true]:ring-red-500 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
- name={name}
- id={name}
- disabled={disabled}
- value={value ?? ""}
- onChange={(e) => {
- onChange(e.currentTarget.value)
- }}
- />
- <CopyButton
- class="p-2 rounded-full text-black shadow-sm focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 "
- getContent={() => value ?? ""}
- />
- </div>
- <ShowInputErrorLabel
- message={error}
- isDirty={value !== undefined}
- />
- </div>
- <p class="mt-2 text-sm text-gray-500" >
- {help}
- </p>
- </div>
- }
- if (type === "x-taler-bank") {
- return <div class="sm:col-span-5">
- <label
- class="block text-sm font-medium leading-6 text-gray-900"
- for={name}
- >
- {label}
- </label>
- <div class="mt-2">
- <div class="flex justify-between">
- <input
- type="text"
- class="mr-4 w-full block-inline disabled:bg-gray-100 rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 data-[error=true]:ring-red-500 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
- name={name}
- id={name}
- disabled={disabled}
- value={value ?? ""}
- />
- <CopyButton
- class="p-2 rounded-full text-black shadow-sm focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 "
- getContent={() => value ?? ""}
- />
- </div>
- <ShowInputErrorLabel
- message={error}
- isDirty={value !== undefined}
- />
- </div>
- <p class="mt-2 text-sm text-gray-500" >
- {/* <i18n.Translate>internal account id</i18n.Translate> */}
- {help}
- </p>
- </div>
- }
- if (type === "bitcoin") {
- return <div class="sm:col-span-5">
- <label
- class="block text-sm font-medium leading-6 text-gray-900"
- for={name}
- >
- {label}
- </label>
- <div class="mt-2">
- <div class="flex justify-between">
- <input
- type="text"
- class="mr-4 w-full block-inline disabled:bg-gray-100 rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 data-[error=true]:ring-red-500 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
- name={name}
- id={name}
- disabled={disabled}
- value={value ?? ""}
- />
- <CopyButton
- class="p-2 rounded-full text-black shadow-sm focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 "
- getContent={() => value ?? ""}
- />
- <ShowInputErrorLabel
- message={error}
- isDirty={value !== undefined}
- />
- </div>
- </div>
- <p class="mt-2 text-sm text-gray-500" >
- {/* <i18n.Translate>bitcoin address</i18n.Translate> */}
- {help}
- </p>
- </div>
- }
- assertUnreachable(type)
-}
diff --git a/packages/demobank-ui/src/pages/admin/AccountList.tsx b/packages/demobank-ui/src/pages/admin/AccountList.tsx
deleted file mode 100644
index 42d3d7fca..000000000
--- a/packages/demobank-ui/src/pages/admin/AccountList.tsx
+++ /dev/null
@@ -1,143 +0,0 @@
-import { Amounts, TalerError } from "@gnu-taler/taler-util";
-import { Loading, useTranslationContext } from "@gnu-taler/web-util/browser";
-import { Fragment, VNode, h } from "preact";
-import { ErrorLoadingWithDebug } from "../../components/ErrorLoadingWithDebug.js";
-import { useBankCoreApiContext } from "../../context/config.js";
-import { useBusinessAccounts } from "../../hooks/circuit.js";
-import { RenderAmount } from "../PaytoWireTransferForm.js";
-import { assertUnreachable } from "../WithdrawalOperationPage.js";
-
-interface Props {
- onCreateAccount: () => void;
-
- onShowAccountDetails: (aid: string) => void;
- onRemoveAccount: (aid: string) => void;
- onUpdateAccountPassword: (aid: string) => void;
- onShowCashoutForAccount: (aid: string) => void;
-}
-
-export function AccountList({ onRemoveAccount, onShowAccountDetails, onUpdateAccountPassword, onShowCashoutForAccount, onCreateAccount }: Props): VNode {
- const result = useBusinessAccounts();
- const { i18n } = useTranslationContext();
- const { config } = useBankCoreApiContext()
-
- if (!result) {
- return <Loading />
- }
- if (result instanceof TalerError) {
- return <ErrorLoadingWithDebug error={result} />
- }
- if (result.data.type === "fail") {
- switch (result.data.case) {
- case "unauthorized": return <Fragment />
- default: assertUnreachable(result.data.case)
- }
- }
-
- const { accounts } = result.data.body;
- return <Fragment>
- <div class="px-4 sm:px-6 lg:px-8 mt-4">
- <div class="sm:flex sm:items-center">
- <div class="sm:flex-auto">
- <h1 class="text-base font-semibold leading-6 text-gray-900">
- <i18n.Translate>Accounts</i18n.Translate>
- </h1>
- <p class="mt-2 text-sm text-gray-700">
- <i18n.Translate>A list of all business account in the bank.</i18n.Translate>
- </p>
- </div>
- <div class="mt-4 sm:ml-16 sm:mt-0 sm:flex-none">
- <button type="button" class="block rounded-md bg-indigo-600 px-3 py-2 text-center text-sm font-semibold text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600"
- onClick={(e) => {
- e.preventDefault()
- onCreateAccount()
- }}>
- <i18n.Translate>Create account</i18n.Translate>
- </button>
- </div>
- </div>
- <div class="mt-8 flow-root">
- <div class="-mx-4 -my-2 overflow-x-auto sm:-mx-6 lg:-mx-8">
- <div class="inline-block min-w-full py-2 align-middle sm:px-6 lg:px-8">
- {!accounts.length ? (
- <div></div>
- ) : (
- <table class="min-w-full divide-y divide-gray-300">
- <thead>
- <tr>
- <th scope="col" class="py-3.5 pl-4 pr-3 text-left text-sm font-semibold text-gray-900 sm:pl-0">{i18n.str`Username`}</th>
- <th scope="col" class="px-3 py-3.5 text-left text-sm font-semibold text-gray-900">{i18n.str`Name`}</th>
- <th scope="col" class="px-3 py-3.5 text-left text-sm font-semibold text-gray-900">{i18n.str`Balance`}</th>
- <th scope="col" class="relative py-3.5 pl-3 pr-4 sm:pr-0">
- <span class="sr-only">{i18n.str`Actions`}</span>
- </th>
- </tr>
- </thead>
- <tbody class="divide-y divide-gray-200">
- {accounts.map((item, idx) => {
- const balance = !item.balance
- ? undefined
- : Amounts.parse(item.balance.amount);
- const noBalance = Amounts.isZero(item.balance.amount)
- const balanceIsDebit =
- item.balance &&
- item.balance.credit_debit_indicator == "debit";
-
- return <tr key={idx}>
- <td class="whitespace-nowrap py-4 pl-4 pr-3 text-sm font-medium text-gray-900 sm:pl-0">
- <a href="#" class="text-indigo-600 hover:text-indigo-900"
- onClick={(e) => {
- e.preventDefault();
- onShowAccountDetails(item.username)
- }}
- >
- {item.username}
- </a>
-
-
- </td>
- <td class="whitespace-nowrap px-3 py-4 text-sm text-gray-500">
- {item.name}
- </td>
- <td data-negative={noBalance ? undefined : balanceIsDebit ? "true" : "false"} class="whitespace-nowrap px-3 py-4 text-sm text-gray-500 data-[negative=false]:text-green-600 data-[negative=true]:text-red-600 ">
- {!balance ? (
- i18n.str`unknown`
- ) : (
- <span class="amount">
- <RenderAmount value={balance} negative={balanceIsDebit} spec={config.currency_specification} />
- </span>
- )}
- </td>
- <td class="relative whitespace-nowrap py-4 pl-3 pr-4 text-right text-sm font-medium sm:pr-0">
- <a href="#" class="text-indigo-600 hover:text-indigo-900"
- onClick={(e) => {
- e.preventDefault();
- onUpdateAccountPassword(item.username)
- }}
- >
- change password
- </a>
- <br />
- {noBalance ?
- <a href="#" class="text-indigo-600 hover:text-indigo-900" onClick={(e) => {
- e.preventDefault();
- onRemoveAccount(item.username)
- }}
- >
- remove
- </a>
- : undefined}
- </td>
- </tr>
- })}
-
- </tbody>
- </table>
- )}
- </div>
- </div>
- </div>
- </div>
- </Fragment>
-
-} \ No newline at end of file
diff --git a/packages/demobank-ui/src/pages/admin/AdminHome.tsx b/packages/demobank-ui/src/pages/admin/AdminHome.tsx
deleted file mode 100644
index 82a341dbe..000000000
--- a/packages/demobank-ui/src/pages/admin/AdminHome.tsx
+++ /dev/null
@@ -1,246 +0,0 @@
-import { AmountString, Amounts, CurrencySpecification, TalerCorebankApi, TalerError, assertUnreachable } from "@gnu-taler/taler-util";
-import { useTranslationContext } from "@gnu-taler/web-util/browser";
-import { format, getDate, getHours, getMonth, getYear, setDate, setHours, setMonth, setYear, sub } from "date-fns";
-import { Fragment, VNode, h } from "preact";
-import { useState } from "preact/hooks";
-import { ErrorLoadingWithDebug } from "../../components/ErrorLoadingWithDebug.js";
-import { Transactions } from "../../components/Transactions/index.js";
-import { useBankCoreApiContext } from "../../context/config.js";
-import { useConversionInfo, useLastMonitorInfo } from "../../hooks/circuit.js";
-import { RenderAmount } from "../PaytoWireTransferForm.js";
-import { WireTransfer } from "../WireTransfer.js";
-import { AccountList } from "./AccountList.js";
-
-
-/**
- * Query account information and show QR code if there is pending withdrawal
- */
-interface Props {
- onRegister: () => void;
-
- onCreateAccount: () => void;
- onShowAccountDetails: (aid: string) => void;
- onRemoveAccount: (aid: string) => void;
- onUpdateAccountPassword: (aid: string) => void;
- onShowCashoutForAccount: (aid: string) => void;
-}
-export function AdminHome({ onCreateAccount, onRegister, onRemoveAccount, onShowAccountDetails, onShowCashoutForAccount, onUpdateAccountPassword }: Props): VNode {
- return <Fragment>
- <Metrics />
- <WireTransfer onRegister={onRegister} />
-
- <Transactions account="admin" />
- <AccountList
- onCreateAccount={onCreateAccount}
- onRemoveAccount={onRemoveAccount}
- onShowCashoutForAccount={onShowCashoutForAccount}
- onShowAccountDetails={onShowAccountDetails}
- onUpdateAccountPassword={onUpdateAccountPassword}
- />
-
- </Fragment>
-}
-
-function getDateForTimeframe(which: number, timeframe: TalerCorebankApi.MonitorTimeframeParam): string {
- const time = Date.now()
-
- switch (timeframe) {
- case TalerCorebankApi.MonitorTimeframeParam.hour: return `${format(setHours(time, which), "HH")}hs`;
- case TalerCorebankApi.MonitorTimeframeParam.day: return format(setDate(time, which), "EEEE");
- case TalerCorebankApi.MonitorTimeframeParam.month: return format(setMonth(time, which), "MMMM");
- case TalerCorebankApi.MonitorTimeframeParam.year: return format(setYear(time, which), "yyyy");
- case TalerCorebankApi.MonitorTimeframeParam.decade: return format(setYear(time, which), "yyyy");
- }
- assertUnreachable(timeframe)
-}
-
-export function getTimeframesForDate(time: Date, timeframe: TalerCorebankApi.MonitorTimeframeParam): { current: number, previous: number } {
- switch (timeframe) {
- case TalerCorebankApi.MonitorTimeframeParam.hour: return {
- current: getHours(sub(time, { hours: 1 })),
- previous: getHours(sub(time, { hours: 2 }))
- }
- case TalerCorebankApi.MonitorTimeframeParam.day: return {
- current: getDate(sub(time, { days: 1 })),
- previous: getDate(sub(time, { days: 2 }))
- }
- case TalerCorebankApi.MonitorTimeframeParam.month: return {
- current: getMonth(sub(time, { months: 1 })),
- previous: getMonth(sub(time, { months: 2 }))
- }
- case TalerCorebankApi.MonitorTimeframeParam.year: return {
- current: getYear(sub(time, { years: 1 })),
- previous: getYear(sub(time, { years: 2 }))
- }
- case TalerCorebankApi.MonitorTimeframeParam.decade: return {
- current: getYear(sub(time, { years: 10 })),
- previous: getYear(sub(time, { years: 20 }))
- }
- default: assertUnreachable(timeframe)
- }
-}
-
-
-function Metrics(): VNode {
- const { i18n } = useTranslationContext()
- const [metricType, setMetricType] = useState<TalerCorebankApi.MonitorTimeframeParam>(TalerCorebankApi.MonitorTimeframeParam.hour);
- const { config } = useBankCoreApiContext();
- const respInfo = useConversionInfo()
- const params = getTimeframesForDate(new Date(), metricType)
-
- const resp = useLastMonitorInfo(params.current, params.previous, metricType);
- if (!resp) return <Fragment />;
- if (resp instanceof TalerError) {
- return <ErrorLoadingWithDebug error={resp} />
- }
- if (resp.current.type !== "ok" || resp.previous.type !== "ok") {
- return <Fragment />
- }
- const fiatSpec = respInfo && (!(respInfo instanceof TalerError)) ? respInfo.body.fiat_currency_specification : undefined
- return <Fragment>
- <div class="sm:hidden">
- <label for="tabs" class="sr-only"><i18n.Translate>Select a section</i18n.Translate></label>
- <select id="tabs" name="tabs" class="block w-full rounded-md border-gray-300 focus:border-indigo-500 focus:ring-indigo-500" onChange={(e) => {
- // const op = e.currentTarget.value as typeof metricType
- setMetricType(e.currentTarget.value as any)
- }}>
- <option value={TalerCorebankApi.MonitorTimeframeParam.hour} selected={metricType == TalerCorebankApi.MonitorTimeframeParam.hour}><i18n.Translate>Last hour</i18n.Translate></option>
- <option value={TalerCorebankApi.MonitorTimeframeParam.day} selected={metricType == TalerCorebankApi.MonitorTimeframeParam.day}><i18n.Translate>Last day</i18n.Translate></option>
- <option value={TalerCorebankApi.MonitorTimeframeParam.month} selected={metricType == TalerCorebankApi.MonitorTimeframeParam.month}><i18n.Translate>Last month</i18n.Translate></option>
- <option value={TalerCorebankApi.MonitorTimeframeParam.year} selected={metricType == TalerCorebankApi.MonitorTimeframeParam.year}><i18n.Translate>Last year</i18n.Translate></option>
- </select>
- </div>
- <div class="hidden sm:block">
- <nav class="isolate flex divide-x divide-gray-200 rounded-lg shadow" aria-label="Tabs">
- <a href="#" onClick={(e) => { e.preventDefault(); setMetricType(TalerCorebankApi.MonitorTimeframeParam.hour) }} data-selected={metricType == TalerCorebankApi.MonitorTimeframeParam.hour} class="rounded-l-lg text-gray-500 hover:text-gray-700 data-[selected=true]:text-gray-900 group relative min-w-0 flex-1 overflow-hidden bg-white py-4 px-4 text-center text-sm font-medium hover:bg-gray-50 focus:z-10" >
- <span><i18n.Translate>Last hour</i18n.Translate></span>
- <span aria-hidden="true" data-selected={metricType == TalerCorebankApi.MonitorTimeframeParam.hour} class="bg-transparent data-[selected=true]:bg-indigo-500 absolute inset-x-0 bottom-0 h-0.5"></span>
- </a>
- <a href="#" onClick={(e) => { e.preventDefault(); setMetricType(TalerCorebankApi.MonitorTimeframeParam.day) }} data-selected={metricType == TalerCorebankApi.MonitorTimeframeParam.day} aria-current="page" class=" text-gray-500 hover:text-gray-700 data-[selected=true]:text-gray-900 group relative min-w-0 flex-1 overflow-hidden bg-white py-4 px-4 text-center text-sm font-medium hover:bg-gray-50 focus:z-10">
- <span><i18n.Translate>Last day</i18n.Translate></span>
- <span aria-hidden="true" data-selected={metricType == TalerCorebankApi.MonitorTimeframeParam.day} class="bg-transparent data-[selected=true]:bg-indigo-500 absolute inset-x-0 bottom-0 h-0.5"></span>
- </a>
- <a href="#" onClick={(e) => { e.preventDefault(); setMetricType(TalerCorebankApi.MonitorTimeframeParam.month) }} data-selected={metricType == TalerCorebankApi.MonitorTimeframeParam.month} class="rounded-r-lg text-gray-500 hover:text-gray-700 data-[selected=true]:text-gray-900 group relative min-w-0 flex-1 overflow-hidden bg-white py-4 px-4 text-center text-sm font-medium hover:bg-gray-50 focus:z-10">
- <span><i18n.Translate>Last month</i18n.Translate></span>
- <span aria-hidden="true" data-selected={metricType == TalerCorebankApi.MonitorTimeframeParam.month} class="bg-transparent data-[selected=true]:bg-indigo-500 absolute inset-x-0 bottom-0 h-0.5"></span>
- </a>
- <a href="#" onClick={(e) => { e.preventDefault(); setMetricType(TalerCorebankApi.MonitorTimeframeParam.year) }} data-selected={metricType == TalerCorebankApi.MonitorTimeframeParam.year} class="rounded-r-lg text-gray-500 hover:text-gray-700 data-[selected=true]:text-gray-900 group relative min-w-0 flex-1 overflow-hidden bg-white py-4 px-4 text-center text-sm font-medium hover:bg-gray-50 focus:z-10">
- <span><i18n.Translate>Last Year</i18n.Translate></span>
- <span aria-hidden="true" data-selected={metricType == TalerCorebankApi.MonitorTimeframeParam.year} class="bg-transparent data-[selected=true]:bg-indigo-500 absolute inset-x-0 bottom-0 h-0.5"></span>
- </a>
- </nav>
- </div>
-
- <div class="w-full flex justify-between">
- <h1 class="text-base font-semibold leading-7 text-gray-900 mt-5">
- <i18n.Translate>Trading volume on {getDateForTimeframe(params.current, metricType)} compared to {getDateForTimeframe(params.previous, metricType)}</i18n.Translate>
- </h1>
- </div>
- <dl class="mt-5 grid grid-cols-1 md:grid-cols-2 divide-y divide-gray-200 overflow-hidden rounded-lg bg-white shadow-lg md:divide-x md:divide-y-0">
-
- {!fiatSpec || resp.current.body.type !== "with-conversions" || resp.previous.body.type !== "with-conversions" ? undefined :
- <Fragment>
- <div class="px-4 py-5 sm:p-6">
- <dt class="text-base font-normal text-gray-900">
- <i18n.Translate>Cashin</i18n.Translate>
- </dt>
- <MetricValue
- current={resp.current.body.cashinFiatVolume}
- previous={resp.previous.body.cashinFiatVolume}
- spec={fiatSpec}
- />
- </div>
- <div class="px-4 py-5 sm:p-6">
- <dt class="text-base font-normal text-gray-900">
- <i18n.Translate>Cashout</i18n.Translate>
- </dt>
- <MetricValue
- current={resp.current.body.cashoutFiatVolume}
- previous={resp.previous.body.cashoutFiatVolume}
- spec={fiatSpec}
- />
- </div>
- </Fragment>
- }
- <div class="px-4 py-5 sm:p-6">
- <dt class="text-base font-normal text-gray-900">
- <i18n.Translate>Payin</i18n.Translate>
- </dt>
- <MetricValue
- current={resp.current.body.talerInVolume}
- previous={resp.previous.body.talerInVolume}
- spec={config.currency_specification}
- />
- </div>
- <div class="px-4 py-5 sm:p-6">
- <dt class="text-base font-normal text-gray-900">
- <i18n.Translate>Payout</i18n.Translate>
- </dt>
- <MetricValue
- current={resp.current.body.talerOutVolume}
- previous={resp.previous.body.talerOutVolume}
- spec={config.currency_specification}
- />
- </div>
- </dl>
- <div class="flex justify-end mt-2">
- <a href="#/download-stats"
- class="disabled:opacity-50 disabled:cursor-default cursor-pointer rounded-md bg-indigo-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600"
- ><i18n.Translate>
- download stats as csv
- </i18n.Translate></a>
- </div>
- </Fragment>
-
-}
-
-
-function MetricValue({ current, previous, spec }: { spec: CurrencySpecification, current: AmountString | undefined, previous: AmountString | undefined }): VNode {
- const { i18n } = useTranslationContext()
- const cmp = current && previous ? Amounts.cmp(current, previous) : 0;
- const cv = !current ? undefined : Amounts.stringifyValue(current)
- const currAmount = !cv ? undefined : Number.parseFloat(cv)
- const prevAmount = !previous ? undefined : Number.parseFloat(Amounts.stringifyValue(previous))
-
- const rate = !currAmount || Number.isNaN(currAmount) || !prevAmount || Number.isNaN(prevAmount) ? 0 :
- cmp === -1 ? 1 - Math.round(currAmount) / Math.round(prevAmount) :
- cmp === 1 ? (Math.round(currAmount) / Math.round(prevAmount)) - 1 : 0;
-
- const negative = cmp === 0 ? undefined : cmp === -1
- const rateStr = `${(Math.abs(rate) * 100).toFixed(2)}%`
- return <Fragment>
- <dd class="mt-1 block ">
- <div class="flex justify-start text-2xl items-baseline font-semibold text-indigo-600">
- {!current ? "-" : <RenderAmount value={Amounts.parseOrThrow(current)} spec={spec} hideSmall />}
- </div>
- <div class="flex flex-col">
-
- <div class="flex justify-end items-baseline text-2xl font-semibold text-indigo-600">
- <small class="ml-2 text-sm font-medium text-gray-500">
- <i18n.Translate>from</i18n.Translate> {!previous ? "-" : <RenderAmount value={Amounts.parseOrThrow(previous)} spec={spec} hideSmall />}
- </small>
- </div>
- {!!rate &&
- <span data-negative={negative} class="flex items-center gap-x-1.5 w-fit rounded-md bg-green-100 text-green-800 data-[negative=true]:bg-red-100 px-2 py-1 text-xs font-medium data-[negative=true]:text-red-700 whitespace-pre">
- {negative ?
- <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
- <path stroke-linecap="round" stroke-linejoin="round" d="M12 4.5v15m0 0l6.75-6.75M12 19.5l-6.75-6.75" />
- </svg>
- :
- <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
- <path stroke-linecap="round" stroke-linejoin="round" d="M12 19.5v-15m0 0l-6.75 6.75M12 4.5l6.75 6.75" />
- </svg>
- }
-
- {negative ?
- <span class="sr-only"><i18n.Translate>Descreased by</i18n.Translate></span> :
- <span class="sr-only"><i18n.Translate>Increased by</i18n.Translate></span>
- }
- {rateStr}
- </span>
- }
- </div>
-
- </dd>
- </Fragment>
-}
diff --git a/packages/demobank-ui/src/pages/admin/CreateNewAccount.tsx b/packages/demobank-ui/src/pages/admin/CreateNewAccount.tsx
deleted file mode 100644
index 3d196973e..000000000
--- a/packages/demobank-ui/src/pages/admin/CreateNewAccount.tsx
+++ /dev/null
@@ -1,145 +0,0 @@
-import { TalerCorebankApi, TranslatedString } from "@gnu-taler/taler-util";
-import { Attention, LocalNotificationBanner, notifyInfo, useLocalNotification, useTranslationContext } from "@gnu-taler/web-util/browser";
-import { Fragment, VNode, h } from "preact";
-import { useState } from "preact/hooks";
-import { mutate } from "swr";
-import { useBankCoreApiContext } from "../../context/config.js";
-import { useBackendState } from "../../hooks/backend.js";
-import { assertUnreachable } from "../WithdrawalOperationPage.js";
-import { getRandomPassword } from "../rnd.js";
-import { AccountForm, AccountFormData } from "./AccountForm.js";
-
-export function CreateNewAccount({
- onCancel,
- onCreateSuccess,
-}: {
- onCancel: () => void;
- onCreateSuccess: () => void;
-}): VNode {
- const { i18n } = useTranslationContext();
- const { state: credentials } = useBackendState()
- const token = credentials.status !== "loggedIn" ? undefined : credentials.token
- const { api } = useBankCoreApiContext();
-
- const [submitAccount, setSubmitAccount] = useState<TalerCorebankApi.RegisterAccountRequest | undefined>();
- const [notification, notify, handleError] = useLocalNotification()
-
- async function doCreate() {
- if (!submitAccount || !token) return;
- await handleError(async () => {
- // const account: TalerCorebankApi.RegisterAccountRequest = {
- // cashout_payto_uri: submitAccount.cashout_payto_uri,
- // challenge_contact_data: submitAccount.challenge_contact_data,
- // internal_payto_uri: submitAccount.internal_payto_uri,
- // debit_threshold: submitAccount.debit_threshold,
- // is_public: submitAccount.is_public,
- // is_taler_exchange: submitAccount.is_taler_exchange,
- // name: submitAccount.name,
- // username: submitAccount.username,
- // password: getRandomPassword(),
- // };
-
- const resp = await api.createAccount(token, submitAccount);
- if (resp.type === "ok") {
- mutate(() => true)// clean account list
- notifyInfo(
- i18n.str`Account created with password "${submitAccount.password}". The user must change the password on the next login.`,
- );
- onCreateSuccess();
- } else {
- switch (resp.case) {
- case "invalid-phone-or-email": return notify({
- type: "error",
- title: i18n.str`Server replied with invalid phone or email`,
- description: resp.detail.hint as TranslatedString,
- debug: resp.detail,
- })
- case "unauthorized": return notify({
- type: "error",
- title: i18n.str`The rights to perform the operation are not sufficient`,
- description: resp.detail.hint as TranslatedString,
- debug: resp.detail,
- })
- case "username-already-exists": return notify({
- type: "error",
- title: i18n.str`Account username is already taken`,
- description: resp.detail.hint as TranslatedString,
- debug: resp.detail,
- })
- case "payto-already-exists": return notify({
- type: "error",
- title: i18n.str`Account id is already taken`,
- description: resp.detail.hint as TranslatedString,
- debug: resp.detail,
- })
- case "insufficient-funds": return notify({
- type: "error",
- title: i18n.str`Bank ran out of bonus credit.`,
- description: resp.detail.hint as TranslatedString,
- debug: resp.detail,
- })
- case "username-reserved": return notify({
- type: "error",
- title: i18n.str`Account username can't be used because is reserved`,
- description: resp.detail.hint as TranslatedString,
- debug: resp.detail,
- })
- case "user-cant-set-debt": return notify({
- type: "error",
- title: i18n.str`Only admin is allow to set debt limit.`,
- description: resp.detail.hint as TranslatedString,
- debug: resp.detail,
- })
- default: assertUnreachable(resp)
- }
- }
- })
- }
-
- if (!(credentials.status === "loggedIn" && credentials.isUserAdministrator)) {
- return <Attention type="warning" title={i18n.str`Can't create accounts`} onClose={onCancel}>
- <i18n.Translate>Only system admin can create accounts.</i18n.Translate>
- </Attention>
- }
-
- return (
- <div class="grid grid-cols-1 gap-x-8 gap-y-8 pt-10 md:grid-cols-3 bg-gray-100 my-4 px-4 pb-4 rounded-lg">
- <LocalNotificationBanner notification={notification} />
-
- <div class="px-4 sm:px-0">
- <h2 class="text-base font-semibold leading-7 text-gray-900">
- <i18n.Translate>New business account</i18n.Translate>
- </h2>
- </div>
- <AccountForm
- template={undefined}
- purpose="create"
- onChange={(a) => {
- setSubmitAccount(a);
- }}
- >
- <div class="flex items-center justify-between gap-x-6 border-t border-gray-900/10 px-4 py-4 sm:px-8">
- {onCancel ?
- <button type="button" class="text-sm font-semibold leading-6 text-gray-900"
- onClick={onCancel}
- >
- <i18n.Translate>Cancel</i18n.Translate>
- </button>
- : <div />
- }
- <button type="submit"
- class="disabled:opacity-50 disabled:cursor-default cursor-pointer rounded-md bg-indigo-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600"
- disabled={!submitAccount}
- onClick={(e) => {
- e.preventDefault()
- doCreate()
- }}
- >
- <i18n.Translate>Create</i18n.Translate>
- </button>
- </div>
-
- </AccountForm>
- </div>
- );
-}
diff --git a/packages/demobank-ui/src/pages/admin/RemoveAccount.tsx b/packages/demobank-ui/src/pages/admin/RemoveAccount.tsx
deleted file mode 100644
index 0d294a3a6..000000000
--- a/packages/demobank-ui/src/pages/admin/RemoveAccount.tsx
+++ /dev/null
@@ -1,194 +0,0 @@
-import { Amounts, TalerError, TranslatedString } from "@gnu-taler/taler-util";
-import { Attention, Loading, LocalNotificationBanner, ShowInputErrorLabel, notifyInfo, useLocalNotification, useTranslationContext } from "@gnu-taler/web-util/browser";
-import { Fragment, VNode, h } from "preact";
-import { useState } from "preact/hooks";
-import { ErrorLoadingWithDebug } from "../../components/ErrorLoadingWithDebug.js";
-import { useBankCoreApiContext } from "../../context/config.js";
-import { useAccountDetails } from "../../hooks/access.js";
-import { useBackendState } from "../../hooks/backend.js";
-import { undefinedIfEmpty } from "../../utils.js";
-import { LoginForm } from "../LoginForm.js";
-import { doAutoFocus } from "../PaytoWireTransferForm.js";
-import { assertUnreachable } from "../WithdrawalOperationPage.js";
-
-export function RemoveAccount({
- account,
- onCancel,
- onUpdateSuccess,
- focus,
-}: {
- focus?: boolean;
- onCancel: () => void;
- onUpdateSuccess: () => void;
- account: string;
-}): VNode {
- const { i18n } = useTranslationContext();
- const result = useAccountDetails(account);
- const [accountName, setAccountName] = useState<string | undefined>()
-
- const { state } = useBackendState();
- const token = state.status !== "loggedIn" ? undefined : state.token
- const { api } = useBankCoreApiContext()
- const [notification, notify, handleError] = useLocalNotification()
-
- if (!result) {
- return <Loading />
- }
- if (result instanceof TalerError) {
- return <ErrorLoadingWithDebug error={result} />
- }
- if (result.type === "fail") {
- switch (result.case) {
- case "unauthorized": return <LoginForm currentUser={account} />
- case "not-found": return <LoginForm currentUser={account} />
- default: assertUnreachable(result)
- }
- }
-
- const balance = Amounts.parse(result.body.balance.amount);
- if (!balance) {
- return <div>there was an error reading the balance</div>;
- }
- const isBalanceEmpty = Amounts.isZero(balance);
- if (!isBalanceEmpty) {
- return <Attention type="warning" title={i18n.str`Can't delete the account`} onClose={onCancel}>
- <i18n.Translate>The account can't be delete while still holding some balance. First make sure that the owner make a complete cashout.</i18n.Translate>
- </Attention>
- }
-
- async function doRemove() {
- if (!token) return;
- await handleError(async () => {
- const resp = await api.deleteAccount({ username: account, token });
- if (resp.type === "ok") {
- notifyInfo(i18n.str`Account removed`);
- onUpdateSuccess();
- } else {
- switch (resp.case) {
- case "unauthorized": return notify({
- type: "error",
- title: i18n.str`No enough permission to delete the account.`,
- description: resp.detail.hint as TranslatedString,
- debug: resp.detail,
- })
- case "not-found": return notify({
- type: "error",
- title: i18n.str`The username was not found.`,
- description: resp.detail.hint as TranslatedString,
- debug: resp.detail,
- })
- case "username-reserved": return notify({
- type: "error",
- title: i18n.str`Can't delete a reserved username.`,
- description: resp.detail.hint as TranslatedString,
- debug: resp.detail,
- })
- case "balance-not-zero": return notify({
- type: "error",
- title: i18n.str`Can't delete an account with balance different than zero.`,
- description: resp.detail.hint as TranslatedString,
- debug: resp.detail,
- })
- default: {
- assertUnreachable(resp)
- }
- }
- }
- })
- }
-
- const errors = undefinedIfEmpty({
- accountName: !accountName
- ? i18n.str`required`
- : account !== accountName
- ? i18n.str`name doesn't match`
- : undefined,
- });
-
-
- return (
- <div>
- <LocalNotificationBanner notification={notification} />
-
- <Attention type="warning" title={i18n.str`You are going to remove the account`}>
- <i18n.Translate>This step can't be undone.</i18n.Translate>
- </Attention>
-
- <div class="grid grid-cols-1 gap-x-8 gap-y-8 pt-10 md:grid-cols-3 bg-gray-100 my-4 px-4 pb-4 rounded-lg">
- <div class="px-4 sm:px-0">
- <h2 class="text-base font-semibold leading-7 text-gray-900">
- <i18n.Translate>Deleting account "{account}"</i18n.Translate>
- </h2>
- </div>
- <form
- class="bg-white shadow-sm ring-1 ring-gray-900/5 sm:rounded-xl md:col-span-2"
- autoCapitalize="none"
- autoCorrect="off"
- onSubmit={e => {
- e.preventDefault()
- }}
- >
- <div class="px-4 py-6 sm:p-8">
- <div class="grid max-w-2xl grid-cols-1 gap-x-6 gap-y-8 sm:grid-cols-6">
-
- <div class="sm:col-span-5">
- <label
- class="block text-sm font-medium leading-6 text-gray-900"
- for="password"
- >
- {i18n.str`Verification`}
- </label>
- <div class="mt-2">
- <input
- ref={focus ? doAutoFocus : undefined}
- type="text"
- class="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 data-[error=true]:ring-red-500 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
- name="password"
- id="password"
- data-error={!!errors?.accountName && accountName !== undefined}
- value={accountName ?? ""}
- onChange={(e) => {
- setAccountName(e.currentTarget.value)
- }}
- placeholder={account}
- autocomplete="off"
- />
- <ShowInputErrorLabel
- message={errors?.accountName}
- isDirty={accountName !== undefined}
- />
- </div>
- <p class="mt-2 text-sm text-gray-500" >
- <i18n.Translate>enter the account name that is going to be deleted</i18n.Translate>
- </p>
- </div>
-
-
-
- </div>
- </div>
- <div class="flex items-center justify-between gap-x-6 border-t border-gray-900/10 px-4 py-4 sm:px-8">
- {onCancel ?
- <button type="button" class="text-sm font-semibold leading-6 text-gray-900"
- onClick={onCancel}
- >
- <i18n.Translate>Cancel</i18n.Translate>
- </button>
- : <div />
- }
- <button type="submit"
- class="disabled:opacity-50 disabled:cursor-default cursor-pointer rounded-md bg-red-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-red-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-red-600"
- disabled={!!errors}
- onClick={(e) => {
- e.preventDefault()
- doRemove()
- }}
- >
- <i18n.Translate>Delete</i18n.Translate>
- </button>
- </div>
- </form>
- </div>
- </div>
- );
-}
diff --git a/packages/demobank-ui/src/pages/business/CreateCashout.tsx b/packages/demobank-ui/src/pages/business/CreateCashout.tsx
deleted file mode 100644
index 8987accd1..000000000
--- a/packages/demobank-ui/src/pages/business/CreateCashout.tsx
+++ /dev/null
@@ -1,519 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2022 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 {
- Amounts,
- TalerError,
- TranslatedString,
- encodeCrock,
- getRandomBytes,
- parsePaytoUri
-} from "@gnu-taler/taler-util";
-import {
- Attention,
- Loading,
- LocalNotificationBanner,
- ShowInputErrorLabel,
- notifyInfo,
- useLocalNotification,
- useTranslationContext
-} from "@gnu-taler/web-util/browser";
-import { Fragment, VNode, h } from "preact";
-import { useEffect, useState } from "preact/hooks";
-import { ErrorLoadingWithDebug } from "../../components/ErrorLoadingWithDebug.js";
-import { useBankCoreApiContext } from "../../context/config.js";
-import { useAccountDetails } from "../../hooks/access.js";
-import { useBackendState } from "../../hooks/backend.js";
-import {
- useConversionInfo,
- useEstimator
-} from "../../hooks/circuit.js";
-import {
- TanChannel,
- undefinedIfEmpty
-} from "../../utils.js";
-import { LoginForm } from "../LoginForm.js";
-import { InputAmount, RenderAmount, doAutoFocus } from "../PaytoWireTransferForm.js";
-import { assertUnreachable } from "../WithdrawalOperationPage.js";
-
-interface Props {
- account: string;
- focus?: boolean,
- onComplete: (id: string) => void;
- onCancel?: () => void;
-}
-
-type FormType = {
- isDebit: boolean;
- amount: string;
- subject: string;
- channel: TanChannel;
-};
-type ErrorFrom<T> = {
- [P in keyof T]+?: string;
-};
-
-
-export function CreateCashout({
- account: accountName,
- onComplete,
- focus,
- onCancel,
-}: Props): VNode {
- const { i18n } = useTranslationContext();
- const resultAccount = useAccountDetails(accountName);
- const {
- estimateByCredit: calculateFromCredit,
- estimateByDebit: calculateFromDebit,
- } = useEstimator();
- const { state: credentials } = useBackendState();
- const creds = credentials.status !== "loggedIn" ? undefined : credentials
-
- const { api, config } = useBankCoreApiContext()
- const [form, setForm] = useState<Partial<FormType>>({ isDebit: true, });
- const [notification, notify, handleError] = useLocalNotification()
- const info = useConversionInfo();
-
- if (!config.allow_conversion) {
- return <Attention type="warning" title={i18n.str`Unable to create a cashout`} onClose={onCancel}>
- <i18n.Translate>The bank configuration does not support cashout operations.</i18n.Translate>
- </Attention>
- }
- if (!resultAccount) {
- return <Loading />
- }
- if (resultAccount instanceof TalerError) {
- return <ErrorLoadingWithDebug error={resultAccount} />
- }
- if (resultAccount.type === "fail") {
- switch (resultAccount.case) {
- case "unauthorized": return <LoginForm currentUser={accountName} />
- case "not-found": return <LoginForm currentUser={accountName} />
- default: assertUnreachable(resultAccount)
- }
- }
- if (!info) {
- return <Loading />
- }
-
- if (info instanceof TalerError) {
- return <ErrorLoadingWithDebug error={info} />
- }
-
- const conversionInfo = info.body.conversion_rate
- if (!conversionInfo) {
- return <div>conversion enabled but server replied without conversion_rate</div>
- }
-
- const account = {
- balance: Amounts.parseOrThrow(resultAccount.body.balance.amount),
- balanceIsDebit: resultAccount.body.balance.credit_debit_indicator == "debit",
- debitThreshold: Amounts.parseOrThrow(resultAccount.body.debit_threshold)
- }
-
- const { fiat_currency, regional_currency, fiat_currency_specification, regional_currency_specification } = info.body
- const regionalZero = Amounts.zeroOfCurrency(regional_currency);
- const fiatZero = Amounts.zeroOfCurrency(fiat_currency);
- const limit = account.balanceIsDebit
- ? Amounts.sub(account.debitThreshold, account.balance).amount
- : Amounts.add(account.balance, account.debitThreshold).amount;
-
- const zeroCalc = { debit: regionalZero, credit: fiatZero, beforeFee: fiatZero };
- const [calc, setCalc] = useState(zeroCalc);
- const sellFee = Amounts.parseOrThrow(conversionInfo.cashout_fee);
- const sellRate = conversionInfo.cashout_ratio
- /**
- * can be in regional currency or fiat currency
- * depending on the isDebit flag
- */
- const inputAmount = Amounts.parseOrThrow(
- `${form.isDebit ? regional_currency : fiat_currency}:${!form.amount ? "0" : form.amount}`,
- );
-
- useEffect(() => {
- async function doAsync() {
- await handleError(async () => {
- if (Amounts.isNonZero(inputAmount)) {
- const resp = await (form.isDebit ?
- calculateFromDebit(inputAmount, sellFee) :
- calculateFromCredit(inputAmount, sellFee));
- setCalc(resp)
- }
- })
- }
- doAsync()
- }, [form.amount, form.isDebit]);
-
- const balanceAfter = Amounts.sub(account.balance, calc.debit).amount;
-
- function updateForm(newForm: typeof form): void {
- setForm(newForm);
- }
- const errors = undefinedIfEmpty<ErrorFrom<typeof form>>({
- subject: !form.subject ? i18n.str`required` : undefined,
- amount: !form.amount
- ? i18n.str`required`
- : !inputAmount
- ? i18n.str`could not be parsed`
- : Amounts.cmp(limit, calc.debit) === -1
- ? i18n.str`balance is not enough`
- : Amounts.cmp(calc.credit, sellFee) === -1
- ? i18n.str`need to be higher due to fees`
- : Amounts.isZero(calc.credit)
- ? i18n.str`the total transfer at destination will be zero`
- : undefined,
- channel: !form.channel ? i18n.str`required` : undefined,
- });
- const trimmedAmountStr = form.amount?.trim();
-
- async function createCashout() {
- const request_uid = encodeCrock(getRandomBytes(32))
- await handleError(async () => {
- if (!creds || !form.subject || !form.channel) return;
-
- const resp = await api.createCashout(creds, {
- request_uid,
- amount_credit: Amounts.stringify(calc.credit),
- amount_debit: Amounts.stringify(calc.debit),
- subject: form.subject,
- tan_channel: form.channel,
- })
- if (resp.type === "ok") {
- notifyInfo(i18n.str`Cashout created`)
- } else {
- switch (resp.case) {
- case "account-not-found": return notify({
- type: "error",
- title: i18n.str`Account not found`,
- description: resp.detail.hint as TranslatedString,
- debug: resp.detail,
- });
- case "request-already-used": return notify({
- type: "error",
- title: i18n.str`Duplicated request detected, check if the operation succeded or try again.`,
- description: resp.detail.hint as TranslatedString,
- debug: resp.detail,
- });
- case "incorrect-exchange-rate": return notify({
- type: "error",
- title: i18n.str`The exchange rate was incorrectly applied`,
- description: resp.detail.hint as TranslatedString,
- debug: resp.detail,
- });
- case "no-contact-info": return notify({
- type: "error",
- title: i18n.str`Missing contact info before to create the cashout`,
- description: resp.detail.hint as TranslatedString,
- debug: resp.detail,
- });
- case "no-enough-balance": return notify({
- type: "error",
- title: i18n.str`The account does not have sufficient funds`,
- description: resp.detail.hint as TranslatedString,
- debug: resp.detail,
- });
- case "cashout-not-supported": return notify({
- type: "error",
- title: i18n.str`Cashouts are not supported`,
- description: resp.detail.hint as TranslatedString,
- debug: resp.detail,
- });
- case "tan-failed": return notify({
- type: "error",
- title: i18n.str`Sending the confirmation code failed.`,
- description: resp.detail.hint as TranslatedString,
- debug: resp.detail,
- });
- }
- assertUnreachable(resp)
- }
- })
- }
- const cashoutAccount = !resultAccount.body.cashout_payto_uri ? undefined :
- parsePaytoUri(resultAccount.body.cashout_payto_uri);
- const cashoutAccountName = !cashoutAccount ? undefined : cashoutAccount.targetPath
- return (
- <div>
- <LocalNotificationBanner notification={notification} />
-
- <div class="grid grid-cols-1 gap-x-8 gap-y-8 pt-10 md:grid-cols-3 bg-gray-100 my-4 px-4 pb-4 rounded-lg">
-
- <section class="mt-4 rounded-sm px-4 py-6 p-8 ">
- <h2 id="summary-heading" class="font-medium text-lg"><i18n.Translate>Cashout</i18n.Translate></h2>
-
- <dl class="mt-4 space-y-4">
- <div class="justify-between items-center flex">
- <dt class="text-sm text-gray-600"><i18n.Translate>Convertion rate</i18n.Translate></dt>
- <dd class="text-sm text-gray-900">{sellRate}</dd>
- </div>
-
-
- <div class="flex items-center justify-between border-t-2 afu pt-4">
- <dt class="flex items-center text-sm text-gray-600">
- <span><i18n.Translate>Balance</i18n.Translate></span>
- </dt>
- <dd class="text-sm text-gray-900">
- <RenderAmount value={account.balance} spec={regional_currency_specification} />
- </dd>
- </div>
- <div class="flex items-center justify-between border-t-2 afu pt-4">
- <dt class="flex items-center text-sm text-gray-600">
- <span><i18n.Translate>Fee</i18n.Translate></span>
- </dt>
- <dd class="text-sm text-gray-900">
- <RenderAmount value={sellFee} spec={fiat_currency_specification} />
- </dd>
- </div>
- {cashoutAccountName ?
- <div class="flex items-center justify-between border-t-2 afu pt-4">
- <dt class="flex items-center text-sm text-gray-600">
- <span><i18n.Translate>To account</i18n.Translate></span>
- </dt>
- <dd class="text-sm text-gray-900">
- {cashoutAccountName}
- </dd>
- </div> :
- <div class="flex items-center justify-between border-t-2 afu pt-4">
- <Attention type="warning" title={i18n.str`No cashout account`}>
- <i18n.Translate>
- Before doing a cashout you need to complete your profile
- </i18n.Translate>
- </Attention>
- </div>
- }
-
- </dl>
-
- </section>
- <form
- class="bg-white shadow-sm ring-1 ring-gray-900/5 sm:rounded-xl md:col-span-2"
- autoCapitalize="none"
- autoCorrect="off"
- onSubmit={e => {
- e.preventDefault()
- }}
- >
- <div class="px-4 py-6 sm:p-8">
- <div class="grid max-w-2xl grid-cols-1 gap-x-6 gap-y-8 sm:grid-cols-6">
- {/* subject */}
-
- <div class="sm:col-span-5">
- <label
- class="block text-sm font-medium leading-6 text-gray-900"
- for="subject"
- >
- {i18n.str`Transfer subject`}
- </label>
- <div class="mt-2">
- <input
- ref={focus ? doAutoFocus : undefined}
- type="text"
- class="block w-full rounded-md disabled:bg-gray-200 border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 data-[error=true]:ring-red-500 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
- name="subject"
- id="subject"
- disabled={!resultAccount.body.cashout_payto_uri}
- data-error={!!errors?.subject && form.subject !== undefined}
- value={form.subject ?? ""}
- onChange={(e) => {
- form.subject = e.currentTarget.value;
- updateForm(structuredClone(form));
- }}
- autocomplete="off"
- />
- <ShowInputErrorLabel
- message={errors?.subject}
- isDirty={form.subject !== undefined}
- />
- </div>
-
- </div>
-
- {/* amount */}
- <div class="sm:col-span-5">
- <div class="flex justify-between">
- <label
- class="block text-sm font-medium leading-6 text-gray-900"
- for="amount"
- >
- {form.isDebit
- ? i18n.str`Amount to send`
- : i18n.str`Amount to receive`}
- </label>
- <button type="button" data-enabled={form.isDebit} class="bg-indigo-600 data-[enabled=false]:bg-gray-200 relative inline-flex h-6 w-11 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none focus:ring-2 focus:ring-indigo-600 focus:ring-offset-2" role="switch" aria-checked="false" aria-labelledby="availability-label" aria-describedby="availability-description"
- onClick={() => {
- form.isDebit = !form.isDebit
- updateForm(structuredClone(form))
- }}>
- <span aria-hidden="true" data-enabled={form.isDebit} class="translate-x-5 data-[enabled=false]:translate-x-0 pointer-events-none inline-block h-5 w-5 transform rounded-full bg-white shadow ring-0 transition duration-200 ease-in-out"></span>
- </button>
-
- </div>
- <div class="mt-2">
- <InputAmount
- name="amount"
- left
- currency={limit.currency}
- value={trimmedAmountStr}
- onChange={!resultAccount.body.cashout_payto_uri ? undefined : (value) => {
- form.amount = value;
- updateForm(structuredClone(form));
- }}
- />
- <ShowInputErrorLabel
- message={errors?.amount}
- isDirty={form.amount !== undefined}
- />
- </div>
-
- </div>
-
- {Amounts.isZero(calc.credit) ? undefined : (
- <div class="sm:col-span-5">
- <dl class="mt-4 space-y-4">
-
- <div class="justify-between items-center flex ">
- <dt class="text-sm text-gray-600"><i18n.Translate>Total cost</i18n.Translate></dt>
- <dd class="text-sm text-gray-900">
- <RenderAmount value={calc.debit} negative withColor spec={regional_currency_specification} />
- </dd>
- </div>
-
-
- <div class="flex items-center justify-between border-t-2 afu pt-4">
- <dt class="flex items-center text-sm text-gray-600">
- <span><i18n.Translate>Balance left</i18n.Translate></span>
- {/* <a href="#" class="ml-2 shrink-0 text-gray-400 bkx">
- <span class="sr-only">Learn more about how shipping is calculated</span>
- <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true"
- class="w-5 h-5"><path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zM8.94 6.94a.75.75 0 11-1.061-1.061 3 3 0 112.871 5.026v.345a.75.75 0 01-1.5 0v-.5c0-.72.57-1.172 1.081-1.287A1.5 1.5 0 108.94 6.94zM10 15a1 1 0 100-2 1 1 0 000 2z" clip-rule="evenodd"></path></svg>
- </a> */}
- </dt>
- <dd class="text-sm text-gray-900">
- <RenderAmount value={balanceAfter} spec={regional_currency_specification} />
- </dd>
- </div>
- {Amounts.isZero(sellFee) || Amounts.isZero(calc.beforeFee) ? undefined : (
- <div class="flex items-center justify-between border-t-2 afu pt-4">
- <dt class="flex items-center text-sm text-gray-600">
- <span><i18n.Translate>Before fee</i18n.Translate></span>
- {/* <a href="#" class="ml-2 shrink-0 text-gray-400 bkx">
- <span class="sr-only">Learn more about how shipping is calculated</span>
- <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true"
- class="w-5 h-5"><path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zM8.94 6.94a.75.75 0 11-1.061-1.061 3 3 0 112.871 5.026v.345a.75.75 0 01-1.5 0v-.5c0-.72.57-1.172 1.081-1.287A1.5 1.5 0 108.94 6.94zM10 15a1 1 0 100-2 1 1 0 000 2z" clip-rule="evenodd"></path></svg>
- </a> */}
- </dt>
- <dd class="text-sm text-gray-900">
- <RenderAmount value={calc.beforeFee} spec={fiat_currency_specification} />
- </dd>
- </div>
- )}
- <div class="flex justify-between items-center border-t-2 afu pt-4">
- <dt class="text-lg text-gray-900 font-medium"><i18n.Translate>Total cashout transfer</i18n.Translate></dt>
- <dd class="text-lg text-gray-900 font-medium">
- <RenderAmount value={calc.credit} withColor spec={fiat_currency_specification} />
- </dd>
- </div>
- </dl>
- </div>
- )}
-
- {/* channel */}
- {config.supported_tan_channels.length === 0 ? undefined :
- <div class="sm:col-span-5">
- <label
- class="block text-sm font-medium leading-6 text-gray-900"
- for="channel"
- >
- {i18n.str`Confirmation the operation using`}
- </label>
- <div class="mt-2 max-w-xl text-sm text-gray-500">
- <div class="px-4 mt-4 grid grid-cols-1 gap-y-6">
- {config.supported_tan_channels.indexOf(TanChannel.EMAIL) === -1 ? undefined :
- <label onClick={() => {
- if (!resultAccount.body.contact_data?.email) return;
- form.channel = TanChannel.EMAIL
- updateForm(structuredClone(form))
- }} data-disabled={!resultAccount.body.contact_data?.email} data-selected={form.channel === TanChannel.EMAIL}
- class="relative flex data-[disabled=false]:cursor-pointer rounded-lg border bg-white data-[disabled=true]:bg-gray-200 p-4 shadow-sm focus:outline-none border-gray-300 data-[selected=true]:ring-2 data-[selected=true]:ring-indigo-600">
- <input type="radio" name="channel" value="Newsletter" class="sr-only" />
- <span class="flex flex-1">
- <span class="flex flex-col">
- <span id="project-type-0-label" class="block text-sm font-medium text-gray-900 ">
- <i18n.Translate>Email</i18n.Translate>
- </span>
- {!resultAccount.body.contact_data?.email && i18n.str`add a email in your profile to enable this option`}
- </span>
- </span>
- <svg data-selected={form.channel === TanChannel.EMAIL} class="h-5 w-5 text-indigo-600 data-[selected=false]:hidden" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
- <path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.857-9.809a.75.75 0 00-1.214-.882l-3.483 4.79-1.88-1.88a.75.75 0 10-1.06 1.061l2.5 2.5a.75.75 0 001.137-.089l4-5.5z" clip-rule="evenodd" />
- </svg>
- </label>
- }
-
- {config.supported_tan_channels.indexOf(TanChannel.SMS) === -1 ? undefined :
- <label onClick={() => {
- if (!resultAccount.body.contact_data?.phone) return;
- form.channel = TanChannel.SMS
- updateForm(structuredClone(form))
- }} data-disabled={!resultAccount.body.contact_data?.phone} data-selected={form.channel === TanChannel.SMS}
- class="relative flex data-[disabled=false]:cursor-pointer rounded-lg border data-[disabled=true]:bg-gray-200 p-4 shadow-sm focus:outline-none border-gray-300 data-[selected=true]:ring-2 data-[selected=true]:ring-indigo-600">
- <input type="radio" name="channel" value="Existing Customers" class="sr-only" />
- <span class="flex flex-1">
- <span class="flex flex-col">
- <span id="project-type-1-label" class="block text-sm font-medium text-gray-900">
- <i18n.Translate>SMS</i18n.Translate>
- </span>
- {!resultAccount.body.contact_data?.phone && i18n.str`add a phone number in your profile to enable this option`}
- </span>
- </span>
- <svg data-selected={form.channel === TanChannel.SMS} class="h-5 w-5 text-indigo-600 data-[selected=false]:hidden" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
- <path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.857-9.809a.75.75 0 00-1.214-.882l-3.483 4.79-1.88-1.88a.75.75 0 10-1.06 1.061l2.5 2.5a.75.75 0 001.137-.089l4-5.5z" clip-rule="evenodd" />
- </svg>
- </label>
- }
- </div>
- </div>
-
- </div>
- }
- </div>
- </div>
-
- <div class="flex items-center justify-between gap-x-6 border-t border-gray-900/10 px-4 py-4 sm:px-8">
- {onCancel ?
- <button type="button" class="text-sm font-semibold leading-6 text-gray-900"
- onClick={onCancel}
- >
- <i18n.Translate>Cancel</i18n.Translate>
- </button>
- : <div />
- }
- <button type="submit"
- class="disabled:opacity-50 disabled:cursor-default cursor-pointer rounded-md bg-indigo-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600"
- disabled={!!errors}
- onClick={(e) => {
- e.preventDefault()
- createCashout()
- }}
- >
- <i18n.Translate>Cashout</i18n.Translate>
- </button>
- </div>
- </form>
- </div>
-
- </div>
- );
-}
diff --git a/packages/demobank-ui/src/pages/business/ShowCashoutDetails.tsx b/packages/demobank-ui/src/pages/business/ShowCashoutDetails.tsx
deleted file mode 100644
index 3ef835574..000000000
--- a/packages/demobank-ui/src/pages/business/ShowCashoutDetails.tsx
+++ /dev/null
@@ -1,332 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2022 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 {
- Amounts,
- TalerError,
- TranslatedString
-} from "@gnu-taler/taler-util";
-import {
- Attention,
- Loading,
- LocalNotificationBanner,
- ShowInputErrorLabel,
- useLocalNotification,
- useTranslationContext
-} from "@gnu-taler/web-util/browser";
-import { format } from "date-fns";
-import { Fragment, VNode, h } from "preact";
-import { useState } from "preact/hooks";
-import { mutate } from "swr";
-import { ErrorLoadingWithDebug } from "../../components/ErrorLoadingWithDebug.js";
-import { useBankCoreApiContext } from "../../context/config.js";
-import { useBackendState } from "../../hooks/backend.js";
-import {
- useCashoutDetails, useConversionInfo
-} from "../../hooks/circuit.js";
-import {
- undefinedIfEmpty
-} from "../../utils.js";
-import { RenderAmount } from "../PaytoWireTransferForm.js";
-import { assertUnreachable } from "../WithdrawalOperationPage.js";
-
-interface Props {
- id: string;
- onCancel: () => void;
-}
-export function ShowCashoutDetails({
- id,
- onCancel,
-}: Props): VNode {
- const { i18n } = useTranslationContext();
- const { state } = useBackendState();
- const creds = state.status !== "loggedIn" ? undefined : state
- const { api } = useBankCoreApiContext()
- const cid = Number.parseInt(id, 10)
-
- const result = useCashoutDetails(Number.isNaN(cid) ? undefined : cid);
- const [code, setCode] = useState<string | undefined>(undefined);
- const [notification, notify, handleError] = useLocalNotification()
- const info = useConversionInfo();
-
- if (Number.isNaN(cid)) {
- //TODO: better error message
- return <div>cashout id should be a number</div>
- }
- if (!result) {
- return <Loading />
- }
- if (result instanceof TalerError) {
- return <ErrorLoadingWithDebug error={result} />
- }
- if (result.type === "fail") {
- switch (result.case) {
- case "not-found": return <Attention type="warning" title={i18n.str`This cashout not found. Maybe already aborted.`}>
- </Attention>
- case "cashout-not-supported": return <Attention type="warning" title={i18n.str`Cashouts are not supported`}>
- </Attention>
- default: assertUnreachable(result)
- }
- }
- if (!info) {
- return <Loading />
- }
-
- if (info instanceof TalerError) {
- return <ErrorLoadingWithDebug error={info} />
- }
-
- const errors = undefinedIfEmpty({
- code: !code ? i18n.str`required` : undefined,
- });
- const isPending = String(result.body.status).toUpperCase() === "PENDING";
- const { fiat_currency_specification, regional_currency_specification } = info.body
- async function doAbortCashout() {
- if (!creds) return;
- await handleError(async () => {
- const resp = await api.abortCashoutById(creds, cid);
- if (resp.type === "ok") {
- onCancel();
- } else {
- switch (resp.case) {
- case "not-found": return notify({
- type: "error",
- title: i18n.str`Cashout not found. It may be also mean that it was already aborted.`,
- description: resp.detail.hint as TranslatedString,
- debug: resp.detail,
- })
- case "already-confirmed": return notify({
- type: "error",
- title: i18n.str`Cashout was already confimed.`,
- description: resp.detail.hint as TranslatedString,
- debug: resp.detail,
- })
- case "cashout-not-supported": return notify({
- type: "error",
- title: i18n.str`Cashout operation is not supported.`,
- description: resp.detail.hint as TranslatedString,
- debug: resp.detail,
- })
- default: {
- assertUnreachable(resp)
- }
- }
- }
- })
- }
- async function doConfirmCashout() {
- if (!creds || !code) return;
- await handleError(async () => {
- const resp = await api.confirmCashoutById(creds, cid, {
- tan: code,
- });
- if (resp.type === "ok") {
- mutate(() => true)//clean cashout state
- } else {
- switch (resp.case) {
- case "not-found": return notify({
- type: "error",
- title: i18n.str`Cashout not found. It may be also mean that it was already aborted.`,
- description: resp.detail.hint as TranslatedString,
- debug: resp.detail,
- })
- case "no-enough-balance": return notify({
- type: "error",
- title: i18n.str`The account does not have sufficient funds`,
- description: resp.detail.hint as TranslatedString,
- debug: resp.detail,
- });
- case "incorrect-exchange-rate": return notify({
- type: "error",
- title: i18n.str`The exchange rate was incorrectly applied`,
- description: resp.detail.hint as TranslatedString,
- debug: resp.detail,
- });
- case "already-aborted": return notify({
- type: "error",
- title: i18n.str`The cashout operation is already aborted.`,
- description: resp.detail.hint as TranslatedString,
- debug: resp.detail,
- });
- case "no-cashout-payto": return notify({
- type: "error",
- title: i18n.str`Missing destination account.`,
- description: resp.detail.hint as TranslatedString,
- debug: resp.detail,
- })
- case "too-many-attempts": return notify({
- type: "error",
- title: i18n.str`Too many failed attempts.`,
- description: resp.detail.hint as TranslatedString,
- debug: resp.detail,
- })
- case "cashout-not-supported": return notify({
- type: "error",
- title: i18n.str`Cashout operation is not supported.`,
- description: resp.detail.hint as TranslatedString,
- debug: resp.detail,
- })
- case "invalid-code": return notify({
- type: "error",
- title: i18n.str`The code for this cashout is invalid.`,
- description: resp.detail.hint as TranslatedString,
- debug: resp.detail,
- })
- default: assertUnreachable(resp)
- }
- }
- })
- }
-
- return (
- <div>
- <LocalNotificationBanner notification={notification} />
- <div class="grid grid-cols-1 gap-x-8 gap-y-8 pt-10 md:grid-cols-3 bg-gray-100 my-4 px-4 pb-4 rounded-lg">
-
- <section class="rounded-sm px-4">
- <h2 id="summary-heading" class="font-medium text-lg"><i18n.Translate>Cashout detail</i18n.Translate></h2>
- <dl class="mt-8 space-y-4">
- <div class="justify-between items-center flex">
- <dt class="text-sm text-gray-600"><i18n.Translate>Subject</i18n.Translate></dt>
- <dd class="text-sm ">{result.body.subject}</dd>
- </div>
-
-
- <div class="flex items-center justify-between border-t-2 afu pt-4">
- <dt class="flex items-center text-sm text-gray-600">
- <span><i18n.Translate>Status</i18n.Translate></span>
- </dt>
- <dd data-status={result.body.status} class="text-sm uppercase data-[status=pending]:text-yellow-600 data-[status=aborted]:text-red-600 data-[status=confirmed]:text-green-600" >
- {result.body.status}
- </dd>
- </div>
- </dl>
- </section>
- <div class="bg-white shadow-sm ring-1 ring-gray-900/5 sm:rounded-xl md:col-span-2">
- <div class="px-4 py-6 sm:p-8">
- <div class="grid max-w-2xl grid-cols-1 gap-x-6 gap-y-8 sm:grid-cols-6">
- <div class="sm:col-span-5">
- <dl class="space-y-4">
-
- {result.body.creation_time.t_s !== "never" ?
- <div class="justify-between items-center flex ">
- <dt class=" text-gray-600"><i18n.Translate>Created</i18n.Translate></dt>
- <dd class="text-sm ">
- {format(result.body.creation_time.t_s * 1000, "dd/MM/yyyy HH:mm:ss")}
- </dd>
- </div>
- : undefined}
-
- <div class="flex justify-between items-center border-t-2 afu pt-4">
- <dt class="text-gray-600"><i18n.Translate>Debited</i18n.Translate></dt>
- <dd class=" font-medium">
- <RenderAmount value={Amounts.parseOrThrow(result.body.amount_debit)} negative withColor spec={regional_currency_specification} />
- </dd>
- </div>
-
- <div class="flex items-center justify-between border-t-2 afu pt-4">
- <dt class="flex items-center text-gray-600">
- <span><i18n.Translate>Credited</i18n.Translate></span>
-
- </dt>
- <dd class="text-sm ">
- <RenderAmount value={Amounts.parseOrThrow(result.body.amount_credit)} withColor spec={fiat_currency_specification} />
- </dd>
- </div>
-
- {result.body.confirmation_time && result.body.confirmation_time.t_s !== "never" ?
- <div class="flex justify-between items-center border-t-2 afu pt-4">
- <dt class=" font-medium"><i18n.Translate>Confirmed</i18n.Translate></dt>
- <dd class=" font-medium">
- {format(result.body.confirmation_time.t_s * 1000, "dd/MM/yyyy HH:mm:ss")}
- </dd>
- </div>
- : undefined}
- </dl>
- </div>
- </div>
- </div>
-
- </div>
-
- {!isPending ? undefined :
- <Fragment>
-
- <div />
- <form
- class="bg-white shadow-sm ring-1 ring-gray-900/5"
- autoCapitalize="none"
- autoCorrect="off"
- onSubmit={e => {
- e.preventDefault()
- }}
- >
- <div class="px-4 py-6 sm:p-8">
- <label for="withdraw-amount">
- Enter the confirmation code
- </label>
- <div class="mt-2">
- <div class="relative rounded-md shadow-sm">
- <input
- type="text"
- // class="block w-full rounded-md border-0 py-1.5 pl-16 text-gray-900 ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
- aria-describedby="answer"
- autoFocus
- class="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
- value={code ?? ""}
- required
-
- name="answer"
- id="answer"
- autocomplete="off"
- onChange={(e): void => {
- setCode(e.currentTarget.value)
- }}
- />
- </div>
- <ShowInputErrorLabel message={errors?.code} isDirty={code !== undefined} />
- </div>
- </div>
- <div class="flex items-center justify-between gap-x-6 border-t border-gray-900/10 px-4 py-4 sm:px-8">
- <button type="button"
- class="inline-flex items-center rounded-md bg-red-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-red-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-red-500"
- onClick={doAbortCashout}
- >
- <i18n.Translate>Abort</i18n.Translate></button>
- <button type="submit"
- class="disabled:opacity-50 disabled:cursor-default cursor-pointer rounded-md bg-indigo-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600"
- disabled={!!errors}
- onClick={(e) => {
- doConfirmCashout()
- }}
- >
- <i18n.Translate>Confirm</i18n.Translate>
- </button>
- </div>
-
- </form>
- </Fragment>}
- </div>
-
- <br />
- <div style={{ display: "flex", justifyContent: "space-between" }}>
- <button type="button" class="text-sm font-semibold leading-6 text-gray-900"
- onClick={onCancel}
- >
- <i18n.Translate>Cancel</i18n.Translate></button>
- </div>
- </div>
- );
-}
diff --git a/packages/demobank-ui/src/pages/index.stories.tsx b/packages/demobank-ui/src/pages/index.stories.tsx
deleted file mode 100644
index 168e9938e..000000000
--- a/packages/demobank-ui/src/pages/index.stories.tsx
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2022 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 qr from "./QrCodeSection.stories.js";
-export * as po from "./PaymentOptions.stories.js";
-export * as ptf from "./PaytoWireTransferForm.stories.js";
-export * as frame from "./BankFrame.stories.js";
diff --git a/packages/demobank-ui/src/pages/rnd.ts b/packages/demobank-ui/src/pages/rnd.ts
deleted file mode 100644
index 46111425e..000000000
--- a/packages/demobank-ui/src/pages/rnd.ts
+++ /dev/null
@@ -1,2893 +0,0 @@
-import { createEddsaKeyPair, encodeCrock, getRandomBytes } from "@gnu-taler/taler-util"
-
-
-const noun = [
- "people",
- "history",
- "way",
- "art",
- "world",
- "information",
- "map",
- "two",
- "family",
- "government",
- "health",
- "system",
- "computer",
- "meat",
- "year",
- "thanks",
- "music",
- "person",
- "reading",
- "method",
- "data",
- "food",
- "understanding",
- "theory",
- "law",
- "bird",
- "literature",
- "problem",
- "software",
- "control",
- "knowledge",
- "power",
- "ability",
- "economics",
- "love",
- "internet",
- "television",
- "science",
- "library",
- "nature",
- "fact",
- "product",
- "idea",
- "temperature",
- "investment",
- "area",
- "society",
- "activity",
- "story",
- "industry",
- "media",
- "thing",
- "oven",
- "community",
- "definition",
- "safety",
- "quality",
- "development",
- "language",
- "management",
- "player",
- "variety",
- "video",
- "week",
- "security",
- "country",
- "exam",
- "movie",
- "organization",
- "equipment",
- "physics",
- "analysis",
- "policy",
- "series",
- "thought",
- "basis",
- "boyfriend",
- "direction",
- "strategy",
- "technology",
- "army",
- "camera",
- "freedom",
- "paper",
- "environment",
- "child",
- "instance",
- "month",
- "truth",
- "marketing",
- "university",
- "writing",
- "article",
- "department",
- "difference",
- "goal",
- "news",
- "audience",
- "fishing",
- "growth",
- "income",
- "marriage",
- "user",
- "combination",
- "failure",
- "meaning",
- "medicine",
- "philosophy",
- "teacher",
- "communication",
- "night",
- "chemistry",
- "disease",
- "disk",
- "energy",
- "nation",
- "road",
- "role",
- "soup",
- "advertising",
- "location",
- "success",
- "addition",
- "apartment",
- "education",
- "math",
- "moment",
- "painting",
- "politics",
- "attention",
- "decision",
- "event",
- "property",
- "shopping",
- "student",
- "wood",
- "competition",
- "distribution",
- "entertainment",
- "office",
- "population",
- "president",
- "unit",
- "category",
- "cigarette",
- "context",
- "introduction",
- "opportunity",
- "performance",
- "driver",
- "flight",
- "length",
- "magazine",
- "newspaper",
- "relationship",
- "teaching",
- "cell",
- "dealer",
- "finding",
- "lake",
- "member",
- "message",
- "phone",
- "scene",
- "appearance",
- "association",
- "concept",
- "customer",
- "death",
- "discussion",
- "housing",
- "inflation",
- "insurance",
- "mood",
- "woman",
- "advice",
- "blood",
- "effort",
- "expression",
- "importance",
- "opinion",
- "payment",
- "reality",
- "responsibility",
- "situation",
- "skill",
- "statement",
- "wealth",
- "application",
- "city",
- "county",
- "depth",
- "estate",
- "foundation",
- "grandmother",
- "heart",
- "perspective",
- "photo",
- "recipe",
- "studio",
- "topic",
- "collection",
- "depression",
- "imagination",
- "passion",
- "percentage",
- "resource",
- "setting",
- "ad",
- "agency",
- "college",
- "connection",
- "criticism",
- "debt",
- "description",
- "memory",
- "patience",
- "secretary",
- "solution",
- "administration",
- "aspect",
- "attitude",
- "director",
- "personality",
- "psychology",
- "recommendation",
- "response",
- "selection",
- "storage",
- "version",
- "alcohol",
- "argument",
- "complaint",
- "contract",
- "emphasis",
- "highway",
- "loss",
- "membership",
- "possession",
- "preparation",
- "steak",
- "union",
- "agreement",
- "cancer",
- "currency",
- "employment",
- "engineering",
- "entry",
- "interaction",
- "mixture",
- "preference",
- "region",
- "republic",
- "tradition",
- "virus",
- "actor",
- "classroom",
- "delivery",
- "device",
- "difficulty",
- "drama",
- "election",
- "engine",
- "football",
- "guidance",
- "hotel",
- "owner",
- "priority",
- "protection",
- "suggestion",
- "tension",
- "variation",
- "anxiety",
- "atmosphere",
- "awareness",
- "bath",
- "bread",
- "candidate",
- "climate",
- "comparison",
- "confusion",
- "construction",
- "elevator",
- "emotion",
- "employee",
- "employer",
- "guest",
- "height",
- "leadership",
- "mall",
- "manager",
- "operation",
- "recording",
- "sample",
- "transportation",
- "charity",
- "cousin",
- "disaster",
- "editor",
- "efficiency",
- "excitement",
- "extent",
- "feedback",
- "guitar",
- "homework",
- "leader",
- "mom",
- "outcome",
- "permission",
- "presentation",
- "promotion",
- "reflection",
- "refrigerator",
- "resolution",
- "revenue",
- "session",
- "singer",
- "tennis",
- "basket",
- "bonus",
- "cabinet",
- "childhood",
- "church",
- "clothes",
- "coffee",
- "dinner",
- "drawing",
- "hair",
- "hearing",
- "initiative",
- "judgment",
- "lab",
- "measurement",
- "mode",
- "mud",
- "orange",
- "poetry",
- "police",
- "possibility",
- "procedure",
- "queen",
- "ratio",
- "relation",
- "restaurant",
- "satisfaction",
- "sector",
- "signature",
- "significance",
- "song",
- "tooth",
- "town",
- "vehicle",
- "volume",
- "wife",
- "accident",
- "airport",
- "appointment",
- "arrival",
- "assumption",
- "baseball",
- "chapter",
- "committee",
- "conversation",
- "database",
- "enthusiasm",
- "error",
- "explanation",
- "farmer",
- "gate",
- "girl",
- "hall",
- "historian",
- "hospital",
- "injury",
- "instruction",
- "maintenance",
- "manufacturer",
- "meal",
- "perception",
- "pie",
- "poem",
- "presence",
- "proposal",
- "reception",
- "replacement",
- "revolution",
- "river",
- "son",
- "speech",
- "tea",
- "village",
- "warning",
- "winner",
- "worker",
- "writer",
- "assistance",
- "breath",
- "buyer",
- "chest",
- "chocolate",
- "conclusion",
- "contribution",
- "cookie",
- "courage",
- "dad",
- "desk",
- "drawer",
- "establishment",
- "examination",
- "garbage",
- "grocery",
- "honey",
- "impression",
- "improvement",
- "independence",
- "insect",
- "inspection",
- "inspector",
- "king",
- "ladder",
- "menu",
- "penalty",
- "piano",
- "potato",
- "profession",
- "professor",
- "quantity",
- "reaction",
- "requirement",
- "salad",
- "sister",
- "supermarket",
- "tongue",
- "weakness",
- "wedding",
- "affair",
- "ambition",
- "analyst",
- "apple",
- "assignment",
- "assistant",
- "bathroom",
- "bedroom",
- "beer",
- "birthday",
- "celebration",
- "championship",
- "cheek",
- "client",
- "consequence",
- "departure",
- "diamond",
- "dirt",
- "ear",
- "fortune",
- "friendship",
- "funeral",
- "gene",
- "girlfriend",
- "hat",
- "indication",
- "intention",
- "lady",
- "midnight",
- "negotiation",
- "obligation",
- "passenger",
- "pizza",
- "platform",
- "poet",
- "pollution",
- "recognition",
- "reputation",
- "shirt",
- "sir",
- "speaker",
- "stranger",
- "surgery",
- "sympathy",
- "tale",
- "throat",
- "trainer",
- "uncle",
- "youth",
- "time",
- "work",
- "film",
- "water",
- "money",
- "example",
- "while",
- "business",
- "study",
- "game",
- "life",
- "form",
- "air",
- "day",
- "place",
- "number",
- "part",
- "field",
- "fish",
- "back",
- "process",
- "heat",
- "hand",
- "experience",
- "job",
- "book",
- "end",
- "point",
- "type",
- "home",
- "economy",
- "value",
- "body",
- "market",
- "guide",
- "interest",
- "state",
- "radio",
- "course",
- "company",
- "price",
- "size",
- "card",
- "list",
- "mind",
- "trade",
- "line",
- "care",
- "group",
- "risk",
- "word",
- "fat",
- "force",
- "key",
- "light",
- "training",
- "name",
- "school",
- "top",
- "amount",
- "level",
- "order",
- "practice",
- "research",
- "sense",
- "service",
- "piece",
- "web",
- "boss",
- "sport",
- "fun",
- "house",
- "page",
- "term",
- "test",
- "answer",
- "sound",
- "focus",
- "matter",
- "kind",
- "soil",
- "board",
- "oil",
- "picture",
- "access",
- "garden",
- "range",
- "rate",
- "reason",
- "future",
- "site",
- "demand",
- "exercise",
- "image",
- "case",
- "cause",
- "coast",
- "action",
- "age",
- "bad",
- "boat",
- "record",
- "result",
- "section",
- "building",
- "mouse",
- "cash",
- "class",
- "nothing",
- "period",
- "plan",
- "store",
- "tax",
- "side",
- "subject",
- "space",
- "rule",
- "stock",
- "weather",
- "chance",
- "figure",
- "man",
- "model",
- "source",
- "beginning",
- "earth",
- "program",
- "chicken",
- "design",
- "feature",
- "head",
- "material",
- "purpose",
- "question",
- "rock",
- "salt",
- "act",
- "birth",
- "car",
- "dog",
- "object",
- "scale",
- "sun",
- "note",
- "profit",
- "rent",
- "speed",
- "style",
- "war",
- "bank",
- "craft",
- "half",
- "inside",
- "outside",
- "standard",
- "bus",
- "exchange",
- "eye",
- "fire",
- "position",
- "pressure",
- "stress",
- "advantage",
- "benefit",
- "box",
- "frame",
- "issue",
- "step",
- "cycle",
- "face",
- "item",
- "metal",
- "paint",
- "review",
- "room",
- "screen",
- "structure",
- "view",
- "account",
- "ball",
- "discipline",
- "medium",
- "share",
- "balance",
- "bit",
- "black",
- "bottom",
- "choice",
- "gift",
- "impact",
- "machine",
- "shape",
- "tool",
- "wind",
- "address",
- "average",
- "career",
- "culture",
- "morning",
- "pot",
- "sign",
- "table",
- "task",
- "condition",
- "contact",
- "credit",
- "egg",
- "hope",
- "ice",
- "network",
- "north",
- "square",
- "attempt",
- "date",
- "effect",
- "link",
- "post",
- "star",
- "voice",
- "capital",
- "challenge",
- "friend",
- "self",
- "shot",
- "brush",
- "couple",
- "debate",
- "exit",
- "front",
- "function",
- "lack",
- "living",
- "plant",
- "plastic",
- "spot",
- "summer",
- "taste",
- "theme",
- "track",
- "wing",
- "brain",
- "button",
- "click",
- "desire",
- "foot",
- "gas",
- "influence",
- "notice",
- "rain",
- "wall",
- "base",
- "damage",
- "distance",
- "feeling",
- "pair",
- "savings",
- "staff",
- "sugar",
- "target",
- "text",
- "animal",
- "author",
- "budget",
- "discount",
- "file",
- "ground",
- "lesson",
- "minute",
- "officer",
- "phase",
- "reference",
- "register",
- "sky",
- "stage",
- "stick",
- "title",
- "trouble",
- "bowl",
- "bridge",
- "campaign",
- "character",
- "club",
- "edge",
- "evidence",
- "fan",
- "letter",
- "lock",
- "maximum",
- "novel",
- "option",
- "pack",
- "park",
- "plenty",
- "quarter",
- "skin",
- "sort",
- "weight",
- "baby",
- "background",
- "carry",
- "dish",
- "factor",
- "fruit",
- "glass",
- "joint",
- "master",
- "muscle",
- "red",
- "strength",
- "traffic",
- "trip",
- "vegetable",
- "appeal",
- "chart",
- "gear",
- "ideal",
- "kitchen",
- "land",
- "log",
- "mother",
- "net",
- "party",
- "principle",
- "relative",
- "sale",
- "season",
- "signal",
- "spirit",
- "street",
- "tree",
- "wave",
- "belt",
- "bench",
- "commission",
- "copy",
- "drop",
- "minimum",
- "path",
- "progress",
- "project",
- "sea",
- "south",
- "status",
- "stuff",
- "ticket",
- "tour",
- "angle",
- "blue",
- "breakfast",
- "confidence",
- "daughter",
- "degree",
- "doctor",
- "dot",
- "dream",
- "duty",
- "essay",
- "father",
- "fee",
- "finance",
- "hour",
- "juice",
- "limit",
- "luck",
- "milk",
- "mouth",
- "peace",
- "pipe",
- "seat",
- "stable",
- "storm",
- "substance",
- "team",
- "trick",
- "afternoon",
- "bat",
- "beach",
- "blank",
- "catch",
- "chain",
- "consideration",
- "cream",
- "crew",
- "detail",
- "gold",
- "interview",
- "kid",
- "mark",
- "match",
- "mission",
- "pain",
- "pleasure",
- "score",
- "screw",
- "sex",
- "shop",
- "shower",
- "suit",
- "tone",
- "window",
- "agent",
- "band",
- "block",
- "bone",
- "calendar",
- "cap",
- "coat",
- "contest",
- "corner",
- "court",
- "cup",
- "district",
- "door",
- "east",
- "finger",
- "garage",
- "guarantee",
- "hole",
- "hook",
- "implement",
- "layer",
- "lecture",
- "lie",
- "manner",
- "meeting",
- "nose",
- "parking",
- "partner",
- "profile",
- "respect",
- "rice",
- "routine",
- "schedule",
- "swimming",
- "telephone",
- "tip",
- "winter",
- "airline",
- "bag",
- "battle",
- "bed",
- "bill",
- "bother",
- "cake",
- "code",
- "curve",
- "designer",
- "dimension",
- "dress",
- "ease",
- "emergency",
- "evening",
- "extension",
- "farm",
- "fight",
- "gap",
- "grade",
- "holiday",
- "horror",
- "horse",
- "host",
- "husband",
- "loan",
- "mistake",
- "mountain",
- "nail",
- "noise",
- "occasion",
- "package",
- "patient",
- "pause",
- "phrase",
- "proof",
- "race",
- "relief",
- "sand",
- "sentence",
- "shoulder",
- "smoke",
- "stomach",
- "string",
- "tourist",
- "towel",
- "vacation",
- "west",
- "wheel",
- "wine",
- "arm",
- "aside",
- "associate",
- "bet",
- "blow",
- "border",
- "branch",
- "breast",
- "brother",
- "buddy",
- "bunch",
- "chip",
- "coach",
- "cross",
- "document",
- "draft",
- "dust",
- "expert",
- "floor",
- "god",
- "golf",
- "habit",
- "iron",
- "judge",
- "knife",
- "landscape",
- "league",
- "mail",
- "mess",
- "native",
- "opening",
- "parent",
- "pattern",
- "pin",
- "pool",
- "pound",
- "request",
- "salary",
- "shame",
- "shelter",
- "shoe",
- "silver",
- "tackle",
- "tank",
- "trust",
- "assist",
- "bake",
- "bar",
- "bell",
- "bike",
- "blame",
- "boy",
- "brick",
- "chair",
- "closet",
- "clue",
- "collar",
- "comment",
- "conference",
- "devil",
- "diet",
- "fear",
- "fuel",
- "glove",
- "jacket",
- "lunch",
- "monitor",
- "mortgage",
- "nurse",
- "pace",
- "panic",
- "peak",
- "plane",
- "reward",
- "row",
- "sandwich",
- "shock",
- "spite",
- "spray",
- "surprise",
- "till",
- "transition",
- "weekend",
- "welcome",
- "yard",
- "alarm",
- "bend",
- "bicycle",
- "bite",
- "blind",
- "bottle",
- "cable",
- "candle",
- "clerk",
- "cloud",
- "concert",
- "counter",
- "flower",
- "grandfather",
- "harm",
- "knee",
- "lawyer",
- "leather",
- "load",
- "mirror",
- "neck",
- "pension",
- "plate",
- "purple",
- "ruin",
- "ship",
- "skirt",
- "slice",
- "snow",
- "specialist",
- "stroke",
- "switch",
- "trash",
- "tune",
- "zone",
- "anger",
- "award",
- "bid",
- "bitter",
- "boot",
- "bug",
- "camp",
- "candy",
- "carpet",
- "cat",
- "champion",
- "channel",
- "clock",
- "comfort",
- "cow",
- "crack",
- "engineer",
- "entrance",
- "fault",
- "grass",
- "guy",
- "hell",
- "highlight",
- "incident",
- "island",
- "joke",
- "jury",
- "leg",
- "lip",
- "mate",
- "motor",
- "nerve",
- "passage",
- "pen",
- "pride",
- "priest",
- "prize",
- "promise",
- "resident",
- "resort",
- "ring",
- "roof",
- "rope",
- "sail",
- "scheme",
- "script",
- "sock",
- "station",
- "toe",
- "tower",
- "truck",
- "witness",
- "a",
- "you",
- "it",
- "can",
- "will",
- "if",
- "one",
- "many",
- "most",
- "other",
- "use",
- "make",
- "good",
- "look",
- "help",
- "go",
- "great",
- "being",
- "few",
- "might",
- "still",
- "public",
- "read",
- "keep",
- "start",
- "give",
- "human",
- "local",
- "general",
- "she",
- "specific",
- "long",
- "play",
- "feel",
- "high",
- "tonight",
- "put",
- "common",
- "set",
- "change",
- "simple",
- "past",
- "big",
- "possible",
- "particular",
- "today",
- "major",
- "personal",
- "current",
- "national",
- "cut",
- "natural",
- "physical",
- "show",
- "try",
- "check",
- "second",
- "call",
- "move",
- "pay",
- "let",
- "increase",
- "single",
- "individual",
- "turn",
- "ask",
- "buy",
- "guard",
- "hold",
- "main",
- "offer",
- "potential",
- "professional",
- "international",
- "travel",
- "cook",
- "alternative",
- "following",
- "special",
- "working",
- "whole",
- "dance",
- "excuse",
- "cold",
- "commercial",
- "low",
- "purchase",
- "deal",
- "primary",
- "worth",
- "fall",
- "necessary",
- "positive",
- "produce",
- "search",
- "present",
- "spend",
- "talk",
- "creative",
- "tell",
- "cost",
- "drive",
- "green",
- "support",
- "glad",
- "remove",
- "return",
- "run",
- "complex",
- "due",
- "effective",
- "middle",
- "regular",
- "reserve",
- "independent",
- "leave",
- "original",
- "reach",
- "rest",
- "serve",
- "watch",
- "beautiful",
- "charge",
- "active",
- "break",
- "negative",
- "safe",
- "stay",
- "visit",
- "visual",
- "affect",
- "cover",
- "report",
- "rise",
- "walk",
- "white",
- "beyond",
- "junior",
- "pick",
- "unique",
- "anything",
- "classic",
- "final",
- "lift",
- "mix",
- "private",
- "stop",
- "teach",
- "western",
- "concern",
- "familiar",
- "fly",
- "official",
- "broad",
- "comfortable",
- "gain",
- "maybe",
- "rich",
- "save",
- "stand",
- "young",
- "fail",
- "heavy",
- "hello",
- "lead",
- "listen",
- "valuable",
- "worry",
- "handle",
- "leading",
- "meet",
- "release",
- "sell",
- "finish",
- "normal",
- "press",
- "ride",
- "secret",
- "spread",
- "spring",
- "tough",
- "wait",
- "brown",
- "deep",
- "display",
- "flow",
- "hit",
- "objective",
- "shoot",
- "touch",
- "cancel",
- "chemical",
- "cry",
- "dump",
- "extreme",
- "push",
- "conflict",
- "eat",
- "fill",
- "formal",
- "jump",
- "kick",
- "opposite",
- "pass",
- "pitch",
- "remote",
- "total",
- "treat",
- "vast",
- "abuse",
- "beat",
- "burn",
- "deposit",
- "print",
- "raise",
- "sleep",
- "somewhere",
- "advance",
- "anywhere",
- "consist",
- "dark",
- "double",
- "draw",
- "equal",
- "fix",
- "hire",
- "internal",
- "join",
- "kill",
- "sensitive",
- "tap",
- "win",
- "attack",
- "claim",
- "constant",
- "drag",
- "drink",
- "guess",
- "minor",
- "pull",
- "raw",
- "soft",
- "solid",
- "wear",
- "weird",
- "wonder",
- "annual",
- "count",
- "dead",
- "doubt",
- "feed",
- "forever",
- "impress",
- "nobody",
- "repeat",
- "round",
- "sing",
- "slide",
- "strip",
- "whereas",
- "wish",
- "combine",
- "command",
- "dig",
- "divide",
- "equivalent",
- "hang",
- "hunt",
- "initial",
- "march",
- "mention",
- "smell",
- "spiritual",
- "survey",
- "tie",
- "adult",
- "brief",
- "crazy",
- "escape",
- "gather",
- "hate",
- "prior",
- "repair",
- "rough",
- "sad",
- "scratch",
- "sick",
- "strike",
- "employ",
- "external",
- "hurt",
- "illegal",
- "laugh",
- "lay",
- "mobile",
- "nasty",
- "ordinary",
- "respond",
- "royal",
- "senior",
- "split",
- "strain",
- "struggle",
- "swim",
- "train",
- "upper",
- "wash",
- "yellow",
- "convert",
- "crash",
- "dependent",
- "fold",
- "funny",
- "grab",
- "hide",
- "miss",
- "permit",
- "quote",
- "recover",
- "resolve",
- "roll",
- "sink",
- "slip",
- "spare",
- "suspect",
- "sweet",
- "swing",
- "twist",
- "upstairs",
- "usual",
- "abroad",
- "brave",
- "calm",
- "concentrate",
- "estimate",
- "grand",
- "male",
- "mine",
- "prompt",
- "quiet",
- "refuse",
- "regret",
- "reveal",
- "rush",
- "shake",
- "shift",
- "shine",
- "steal",
- "suck",
- "surround",
- "anybody",
- "bear",
- "brilliant",
- "dare",
- "dear",
- "delay",
- "drunk",
- "female",
- "hurry",
- "inevitable",
- "invite",
- "kiss",
- "neat",
- "pop",
- "punch",
- "quit",
- "reply",
- "representative",
- "resist",
- "rip",
- "rub",
- "silly",
- "smile",
- "spell",
- "stretch",
- "stupid",
- "tear",
- "temporary",
- "tomorrow",
- "wake",
- "wrap",
- "yesterday"
-]
-
-const adj = [
- "abandoned",
- "able",
- "absolute",
- "adorable",
- "adventurous",
- "academic",
- "acceptable",
- "acclaimed",
- "accomplished",
- "accurate",
- "aching",
- "acidic",
- "acrobatic",
- "active",
- "actual",
- "adept",
- "admirable",
- "admired",
- "adolescent",
- "adorable",
- "adored",
- "advanced",
- "afraid",
- "affectionate",
- "aged",
- "aggravating",
- "aggressive",
- "agile",
- "agitated",
- "agonizing",
- "agreeable",
- "ajar",
- "alarmed",
- "alarming",
- "alert",
- "alienated",
- "alive",
- "all",
- "altruistic",
- "amazing",
- "ambitious",
- "ample",
- "amused",
- "amusing",
- "anchored",
- "ancient",
- "angelic",
- "angry",
- "anguished",
- "animated",
- "annual",
- "another",
- "antique",
- "anxious",
- "any",
- "apprehensive",
- "appropriate",
- "apt",
- "arctic",
- "arid",
- "aromatic",
- "artistic",
- "ashamed",
- "assured",
- "astonishing",
- "athletic",
- "attached",
- "attentive",
- "attractive",
- "austere",
- "authentic",
- "authorized",
- "automatic",
- "avaricious",
- "average",
- "aware",
- "awesome",
- "awful",
- "awkward",
- "babyish",
- "bad",
- "back",
- "baggy",
- "bare",
- "barren",
- "basic",
- "beautiful",
- "belated",
- "beloved",
- "beneficial",
- "better",
- "best",
- "bewitched",
- "big",
- "big-hearted",
- "biodegradable",
- "bite-sized",
- "bitter",
- "black",
- "black-and-white",
- "bland",
- "blank",
- "blaring",
- "bleak",
- "blind",
- "blissful",
- "blond",
- "blue",
- "blushing",
- "bogus",
- "boiling",
- "bold",
- "bony",
- "boring",
- "bossy",
- "both",
- "bouncy",
- "bountiful",
- "bowed",
- "brave",
- "breakable",
- "brief",
- "bright",
- "brilliant",
- "brisk",
- "broken",
- "bronze",
- "brown",
- "bruised",
- "bubbly",
- "bulky",
- "bumpy",
- "buoyant",
- "burdensome",
- "burly",
- "bustling",
- "busy",
- "buttery",
- "buzzing",
- "calculating",
- "calm",
- "candid",
- "canine",
- "capital",
- "carefree",
- "careful",
- "careless",
- "caring",
- "cautious",
- "cavernous",
- "celebrated",
- "charming",
- "cheap",
- "cheerful",
- "cheery",
- "chief",
- "chilly",
- "chubby",
- "circular",
- "classic",
- "clean",
- "clear",
- "clear-cut",
- "clever",
- "close",
- "closed",
- "cloudy",
- "clueless",
- "clumsy",
- "cluttered",
- "coarse",
- "cold",
- "colorful",
- "colorless",
- "colossal",
- "comfortable",
- "common",
- "compassionate",
- "competent",
- "complete",
- "complex",
- "complicated",
- "composed",
- "concerned",
- "concrete",
- "confused",
- "conscious",
- "considerate",
- "constant",
- "content",
- "conventional",
- "cooked",
- "cool",
- "cooperative",
- "coordinated",
- "corny",
- "corrupt",
- "costly",
- "courageous",
- "courteous",
- "crafty",
- "crazy",
- "creamy",
- "creative",
- "creepy",
- "criminal",
- "crisp",
- "critical",
- "crooked",
- "crowded",
- "cruel",
- "crushing",
- "cuddly",
- "cultivated",
- "cultured",
- "cumbersome",
- "curly",
- "curvy",
- "cute",
- "cylindrical",
- "damaged",
- "damp",
- "dangerous",
- "dapper",
- "daring",
- "darling",
- "dark",
- "dazzling",
- "dead",
- "deadly",
- "deafening",
- "dear",
- "dearest",
- "decent",
- "decimal",
- "decisive",
- "deep",
- "defenseless",
- "defensive",
- "defiant",
- "deficient",
- "definite",
- "definitive",
- "delayed",
- "delectable",
- "delicious",
- "delightful",
- "delirious",
- "demanding",
- "dense",
- "dental",
- "dependable",
- "dependent",
- "descriptive",
- "deserted",
- "detailed",
- "determined",
- "devoted",
- "different",
- "difficult",
- "digital",
- "diligent",
- "dim",
- "dimpled",
- "dimwitted",
- "direct",
- "disastrous",
- "discrete",
- "disfigured",
- "disgusting",
- "disloyal",
- "dismal",
- "distant",
- "downright",
- "dreary",
- "dirty",
- "disguised",
- "dishonest",
- "dismal",
- "distant",
- "distinct",
- "distorted",
- "dizzy",
- "dopey",
- "doting",
- "double",
- "downright",
- "drab",
- "drafty",
- "dramatic",
- "dreary",
- "droopy",
- "dry",
- "dual",
- "dull",
- "dutiful",
- "each",
- "eager",
- "earnest",
- "early",
- "easy",
- "easy-going",
- "ecstatic",
- "edible",
- "educated",
- "elaborate",
- "elastic",
- "elated",
- "elderly",
- "electric",
- "elegant",
- "elementary",
- "elliptical",
- "embarrassed",
- "embellished",
- "eminent",
- "emotional",
- "empty",
- "enchanted",
- "enchanting",
- "energetic",
- "enlightened",
- "enormous",
- "enraged",
- "entire",
- "envious",
- "equal",
- "equatorial",
- "essential",
- "esteemed",
- "ethical",
- "euphoric",
- "even",
- "evergreen",
- "everlasting",
- "every",
- "evil",
- "exalted",
- "excellent",
- "exemplary",
- "exhausted",
- "excitable",
- "excited",
- "exciting",
- "exotic",
- "expensive",
- "experienced",
- "expert",
- "extraneous",
- "extroverted",
- "extra-large",
- "extra-small",
- "fabulous",
- "failing",
- "faint",
- "fair",
- "faithful",
- "fake",
- "false",
- "familiar",
- "famous",
- "fancy",
- "fantastic",
- "far",
- "faraway",
- "far-flung",
- "far-off",
- "fast",
- "fat",
- "fatal",
- "fatherly",
- "favorable",
- "favorite",
- "fearful",
- "fearless",
- "feisty",
- "feline",
- "female",
- "feminine",
- "few",
- "fickle",
- "filthy",
- "fine",
- "finished",
- "firm",
- "first",
- "firsthand",
- "fitting",
- "fixed",
- "flaky",
- "flamboyant",
- "flashy",
- "flat",
- "flawed",
- "flawless",
- "flickering",
- "flimsy",
- "flippant",
- "flowery",
- "fluffy",
- "fluid",
- "flustered",
- "focused",
- "fond",
- "foolhardy",
- "foolish",
- "forceful",
- "forked",
- "formal",
- "forsaken",
- "forthright",
- "fortunate",
- "fragrant",
- "frail",
- "frank",
- "frayed",
- "free",
- "French",
- "fresh",
- "frequent",
- "friendly",
- "frightened",
- "frightening",
- "frigid",
- "frilly",
- "frizzy",
- "frivolous",
- "front",
- "frosty",
- "frozen",
- "frugal",
- "fruitful",
- "full",
- "fumbling",
- "functional",
- "funny",
- "fussy",
- "fuzzy",
- "gargantuan",
- "gaseous",
- "general",
- "generous",
- "gentle",
- "genuine",
- "giant",
- "giddy",
- "gigantic",
- "gifted",
- "giving",
- "glamorous",
- "glaring",
- "glass",
- "gleaming",
- "gleeful",
- "glistening",
- "glittering",
- "gloomy",
- "glorious",
- "glossy",
- "glum",
- "golden",
- "good",
- "good-natured",
- "gorgeous",
- "graceful",
- "gracious",
- "grand",
- "grandiose",
- "granular",
- "grateful",
- "grave",
- "gray",
- "great",
- "greedy",
- "green",
- "gregarious",
- "grim",
- "grimy",
- "gripping",
- "grizzled",
- "gross",
- "grotesque",
- "grouchy",
- "grounded",
- "growing",
- "growling",
- "grown",
- "grubby",
- "gruesome",
- "grumpy",
- "guilty",
- "gullible",
- "gummy",
- "hairy",
- "half",
- "handmade",
- "handsome",
- "handy",
- "happy",
- "happy-go-lucky",
- "hard",
- "hard-to-find",
- "harmful",
- "harmless",
- "harmonious",
- "harsh",
- "hasty",
- "hateful",
- "haunting",
- "healthy",
- "heartfelt",
- "hearty",
- "heavenly",
- "heavy",
- "hefty",
- "helpful",
- "helpless",
- "hidden",
- "hideous",
- "high",
- "high-level",
- "hilarious",
- "hoarse",
- "hollow",
- "homely",
- "honest",
- "honorable",
- "honored",
- "hopeful",
- "horrible",
- "hospitable",
- "hot",
- "huge",
- "humble",
- "humiliating",
- "humming",
- "humongous",
- "hungry",
- "hurtful",
- "husky",
- "icky",
- "icy",
- "ideal",
- "idealistic",
- "identical",
- "idle",
- "idiotic",
- "idolized",
- "ignorant",
- "ill",
- "illegal",
- "ill-fated",
- "ill-informed",
- "illiterate",
- "illustrious",
- "imaginary",
- "imaginative",
- "immaculate",
- "immaterial",
- "immediate",
- "immense",
- "impassioned",
- "impeccable",
- "impartial",
- "imperfect",
- "imperturbable",
- "impish",
- "impolite",
- "important",
- "impossible",
- "impractical",
- "impressionable",
- "impressive",
- "improbable",
- "impure",
- "inborn",
- "incomparable",
- "incompatible",
- "incomplete",
- "inconsequential",
- "incredible",
- "indelible",
- "inexperienced",
- "indolent",
- "infamous",
- "infantile",
- "infatuated",
- "inferior",
- "infinite",
- "informal",
- "innocent",
- "insecure",
- "insidious",
- "insignificant",
- "insistent",
- "instructive",
- "insubstantial",
- "intelligent",
- "intent",
- "intentional",
- "interesting",
- "internal",
- "international",
- "intrepid",
- "ironclad",
- "irresponsible",
- "irritating",
- "itchy",
- "jaded",
- "jagged",
- "jam-packed",
- "jaunty",
- "jealous",
- "jittery",
- "joint",
- "jolly",
- "jovial",
- "joyful",
- "joyous",
- "jubilant",
- "judicious",
- "juicy",
- "jumbo",
- "junior",
- "jumpy",
- "juvenile",
- "kaleidoscopic",
- "keen",
- "key",
- "kind",
- "kindhearted",
- "kindly",
- "klutzy",
- "knobby",
- "knotty",
- "knowledgeable",
- "knowing",
- "known",
- "kooky",
- "kosher",
- "lame",
- "lanky",
- "large",
- "last",
- "lasting",
- "late",
- "lavish",
- "lawful",
- "lazy",
- "leading",
- "lean",
- "leafy",
- "left",
- "legal",
- "legitimate",
- "light",
- "lighthearted",
- "likable",
- "likely",
- "limited",
- "limp",
- "limping",
- "linear",
- "lined",
- "liquid",
- "little",
- "live",
- "lively",
- "livid",
- "loathsome",
- "lone",
- "lonely",
- "long",
- "long-term",
- "loose",
- "lopsided",
- "lost",
- "loud",
- "lovable",
- "lovely",
- "loving",
- "low",
- "loyal",
- "lucky",
- "lumbering",
- "luminous",
- "lumpy",
- "lustrous",
- "luxurious",
- "mad",
- "made-up",
- "magnificent",
- "majestic",
- "major",
- "male",
- "mammoth",
- "married",
- "marvelous",
- "masculine",
- "massive",
- "mature",
- "meager",
- "mealy",
- "mean",
- "measly",
- "meaty",
- "medical",
- "mediocre",
- "medium",
- "meek",
- "mellow",
- "melodic",
- "memorable",
- "menacing",
- "merry",
- "messy",
- "metallic",
- "mild",
- "milky",
- "mindless",
- "miniature",
- "minor",
- "minty",
- "miserable",
- "miserly",
- "misguided",
- "misty",
- "mixed",
- "modern",
- "modest",
- "moist",
- "monstrous",
- "monthly",
- "monumental",
- "moral",
- "mortified",
- "motherly",
- "motionless",
- "mountainous",
- "muddy",
- "muffled",
- "multicolored",
- "mundane",
- "murky",
- "mushy",
- "musty",
- "muted",
- "mysterious",
- "naive",
- "narrow",
- "nasty",
- "natural",
- "naughty",
- "nautical",
- "near",
- "neat",
- "necessary",
- "needy",
- "negative",
- "neglected",
- "negligible",
- "neighboring",
- "nervous",
- "new",
- "next",
- "nice",
- "nifty",
- "nimble",
- "nippy",
- "nocturnal",
- "noisy",
- "nonstop",
- "normal",
- "notable",
- "noted",
- "noteworthy",
- "novel",
- "noxious",
- "numb",
- "nutritious",
- "nutty",
- "obedient",
- "obese",
- "oblong",
- "oily",
- "oblong",
- "obvious",
- "occasional",
- "odd",
- "oddball",
- "offbeat",
- "offensive",
- "official",
- "old",
- "old-fashioned",
- "only",
- "open",
- "optimal",
- "optimistic",
- "opulent",
- "orange",
- "orderly",
- "organic",
- "ornate",
- "ornery",
- "ordinary",
- "original",
- "other",
- "our",
- "outlying",
- "outgoing",
- "outlandish",
- "outrageous",
- "outstanding",
- "oval",
- "overcooked",
- "overdue",
- "overjoyed",
- "overlooked",
- "palatable",
- "pale",
- "paltry",
- "parallel",
- "parched",
- "partial",
- "passionate",
- "past",
- "pastel",
- "peaceful",
- "peppery",
- "perfect",
- "perfumed",
- "periodic",
- "perky",
- "personal",
- "pertinent",
- "pesky",
- "pessimistic",
- "petty",
- "phony",
- "physical",
- "piercing",
- "pink",
- "pitiful",
- "plain",
- "plaintive",
- "plastic",
- "playful",
- "pleasant",
- "pleased",
- "pleasing",
- "plump",
- "plush",
- "polished",
- "polite",
- "political",
- "pointed",
- "pointless",
- "poised",
- "poor",
- "popular",
- "portly",
- "posh",
- "positive",
- "possible",
- "potable",
- "powerful",
- "powerless",
- "practical",
- "precious",
- "present",
- "prestigious",
- "pretty",
- "precious",
- "previous",
- "pricey",
- "prickly",
- "primary",
- "prime",
- "pristine",
- "private",
- "prize",
- "probable",
- "productive",
- "profitable",
- "profuse",
- "proper",
- "proud",
- "prudent",
- "punctual",
- "pungent",
- "puny",
- "pure",
- "purple",
- "pushy",
- "putrid",
- "puzzled",
- "puzzling",
- "quaint",
- "qualified",
- "quarrelsome",
- "quarterly",
- "queasy",
- "querulous",
- "questionable",
- "quick",
- "quick-witted",
- "quiet",
- "quintessential",
- "quirky",
- "quixotic",
- "quizzical",
- "radiant",
- "ragged",
- "rapid",
- "rare",
- "rash",
- "raw",
- "recent",
- "reckless",
- "rectangular",
- "ready",
- "real",
- "realistic",
- "reasonable",
- "red",
- "reflecting",
- "regal",
- "regular",
- "reliable",
- "relieved",
- "remarkable",
- "remorseful",
- "remote",
- "repentant",
- "required",
- "respectful",
- "responsible",
- "repulsive",
- "revolving",
- "rewarding",
- "rich",
- "rigid",
- "right",
- "ringed",
- "ripe",
- "roasted",
- "robust",
- "rosy",
- "rotating",
- "rotten",
- "rough",
- "round",
- "rowdy",
- "royal",
- "rubbery",
- "rundown",
- "ruddy",
- "rude",
- "runny",
- "rural",
- "rusty",
- "sad",
- "safe",
- "salty",
- "same",
- "sandy",
- "sane",
- "sarcastic",
- "sardonic",
- "satisfied",
- "scaly",
- "scarce",
- "scared",
- "scary",
- "scented",
- "scholarly",
- "scientific",
- "scornful",
- "scratchy",
- "scrawny",
- "second",
- "secondary",
- "second-hand",
- "secret",
- "self-assured",
- "self-reliant",
- "selfish",
- "sentimental",
- "separate",
- "serene",
- "serious",
- "serpentine",
- "several",
- "severe",
- "shabby",
- "shadowy",
- "shady",
- "shallow",
- "shameful",
- "shameless",
- "sharp",
- "shimmering",
- "shiny",
- "shocked",
- "shocking",
- "shoddy",
- "short",
- "short-term",
- "showy",
- "shrill",
- "shy",
- "sick",
- "silent",
- "silky",
- "silly",
- "silver",
- "similar",
- "simple",
- "simplistic",
- "sinful",
- "single",
- "sizzling",
- "skeletal",
- "skinny",
- "sleepy",
- "slight",
- "slim",
- "slimy",
- "slippery",
- "slow",
- "slushy",
- "small",
- "smart",
- "smoggy",
- "smooth",
- "smug",
- "snappy",
- "snarling",
- "sneaky",
- "sniveling",
- "snoopy",
- "sociable",
- "soft",
- "soggy",
- "solid",
- "somber",
- "some",
- "spherical",
- "sophisticated",
- "sore",
- "sorrowful",
- "soulful",
- "soupy",
- "sour",
- "Spanish",
- "sparkling",
- "sparse",
- "specific",
- "spectacular",
- "speedy",
- "spicy",
- "spiffy",
- "spirited",
- "spiteful",
- "splendid",
- "spotless",
- "spotted",
- "spry",
- "square",
- "squeaky",
- "squiggly",
- "stable",
- "staid",
- "stained",
- "stale",
- "standard",
- "starchy",
- "stark",
- "starry",
- "steep",
- "sticky",
- "stiff",
- "stimulating",
- "stingy",
- "stormy",
- "straight",
- "strange",
- "steel",
- "strict",
- "strident",
- "striking",
- "striped",
- "strong",
- "studious",
- "stunning",
- "stupendous",
- "stupid",
- "sturdy",
- "stylish",
- "subdued",
- "submissive",
- "substantial",
- "subtle",
- "suburban",
- "sudden",
- "sugary",
- "sunny",
- "super",
- "superb",
- "superficial",
- "superior",
- "supportive",
- "sure-footed",
- "surprised",
- "suspicious",
- "svelte",
- "sweaty",
- "sweet",
- "sweltering",
- "swift",
- "sympathetic",
- "tall",
- "talkative",
- "tame",
- "tan",
- "tangible",
- "tart",
- "tasty",
- "tattered",
- "taut",
- "tedious",
- "teeming",
- "tempting",
- "tender",
- "tense",
- "tepid",
- "terrible",
- "terrific",
- "testy",
- "thankful",
- "that",
- "these",
- "thick",
- "thin",
- "third",
- "thirsty",
- "this",
- "thorough",
- "thorny",
- "those",
- "thoughtful",
- "threadbare",
- "thrifty",
- "thunderous",
- "tidy",
- "tight",
- "timely",
- "tinted",
- "tiny",
- "tired",
- "torn",
- "total",
- "tough",
- "traumatic",
- "treasured",
- "tremendous",
- "tragic",
- "trained",
- "tremendous",
- "triangular",
- "tricky",
- "trifling",
- "trim",
- "trivial",
- "troubled",
- "true",
- "trusting",
- "trustworthy",
- "trusty",
- "truthful",
- "tubby",
- "turbulent",
- "twin",
- "ugly",
- "ultimate",
- "unacceptable",
- "unaware",
- "uncomfortable",
- "uncommon",
- "unconscious",
- "understated",
- "unequaled",
- "uneven",
- "unfinished",
- "unfit",
- "unfolded",
- "unfortunate",
- "unhappy",
- "unhealthy",
- "uniform",
- "unimportant",
- "unique",
- "united",
- "unkempt",
- "unknown",
- "unlawful",
- "unlined",
- "unlucky",
- "unnatural",
- "unpleasant",
- "unrealistic",
- "unripe",
- "unruly",
- "unselfish",
- "unsightly",
- "unsteady",
- "unsung",
- "untidy",
- "untimely",
- "untried",
- "untrue",
- "unused",
- "unusual",
- "unwelcome",
- "unwieldy",
- "unwilling",
- "unwitting",
- "unwritten",
- "upbeat",
- "upright",
- "upset",
- "urban",
- "usable",
- "used",
- "useful",
- "useless",
- "utilized",
- "utter",
- "vacant",
- "vague",
- "vain",
- "valid",
- "valuable",
- "vapid",
- "variable",
- "vast",
- "velvety",
- "venerated",
- "vengeful",
- "verifiable",
- "vibrant",
- "vicious",
- "victorious",
- "vigilant",
- "vigorous",
- "villainous",
- "violet",
- "violent",
- "virtual",
- "virtuous",
- "visible",
- "vital",
- "vivacious",
- "vivid",
- "voluminous",
- "wan",
- "warlike",
- "warm",
- "warmhearted",
- "warped",
- "wary",
- "wasteful",
- "watchful",
- "waterlogged",
- "watery",
- "wavy",
- "wealthy",
- "weak",
- "weary",
- "webbed",
- "wee",
- "weekly",
- "weepy",
- "weighty",
- "weird",
- "welcome",
- "well-documented",
- "well-groomed",
- "well-informed",
- "well-lit",
- "well-made",
- "well-off",
- "well-to-do",
- "well-worn",
- "wet",
- "which",
- "whimsical",
- "whirlwind",
- "whispered",
- "white",
- "whole",
- "whopping",
- "wicked",
- "wide",
- "wide-eyed",
- "wiggly",
- "wild",
- "willing",
- "wilted",
- "winding",
- "windy",
- "winged",
- "wiry",
- "wise",
- "witty",
- "wobbly",
- "woeful",
- "wonderful",
- "wooden",
- "woozy",
- "wordy",
- "worldly",
- "worn",
- "worried",
- "worrisome",
- "worse",
- "worst",
- "worthless",
- "worthwhile",
- "worthy",
- "wrathful",
- "wretched",
- "writhing",
- "wrong",
- "wry",
- "yawning",
- "yearly",
- "yellow",
- "yellowish",
- "young",
- "youthful",
- "yummy",
- "zany",
- "zealous",
- "zesty",
- "zigzag",
-]
-
-export function getRandomUsername(): { first: string, second: string } {
- const n = Math.floor(Math.random() * noun.length)
- const a = Math.floor(Math.random() * adj.length)
- return {
- first: adj[a],
- second: noun[n]
- }
-}
-
-export function getRandomPassword(): string {
- return encodeCrock(getRandomBytes(16))
-} \ No newline at end of file
diff --git a/packages/demobank-ui/src/route.ts b/packages/demobank-ui/src/route.ts
deleted file mode 100644
index d54f9be83..000000000
--- a/packages/demobank-ui/src/route.ts
+++ /dev/null
@@ -1,167 +0,0 @@
-import { createHashHistory } from "history";
-import { h as create, VNode } from "preact";
-import { useEffect, useState } from "preact/hooks";
-const history = createHashHistory();
-
-type PageDefinition<DynamicPart extends Record<string, string>> = {
- pattern: string;
- (params: DynamicPart): string;
-};
-
-function replaceAll(
- pattern: string,
- vars: Record<string, string>,
- values: Record<string, string>,
-): string {
- let result = pattern;
- for (const v in vars) {
- result = result.replace(vars[v], !values[v] ? "" : values[v]);
- }
- return result;
-}
-
-export function pageDefinition<T extends Record<string, string>>(
- pattern: string,
-): PageDefinition<T> {
- const patternParams = pattern.match(/(:[\w?]*)/g);
- if (!patternParams)
- throw Error(
- `page definition pattern ${pattern} doesn't have any parameter`,
- );
-
- const vars = patternParams.reduce((prev, cur) => {
- const pName = cur.match(/(\w+)/g);
-
- //skip things like :? in the path pattern
- if (!pName || !pName[0]) return prev;
- const name = pName[0];
- return { ...prev, [name]: cur };
- }, {} as Record<string, string>);
-
- const f = (values: T): string => replaceAll(pattern, vars, values);
- f.pattern = pattern;
- return f;
-}
-
-export type PageEntry<T = unknown> = T extends Record<string, string>
- ? {
- url: PageDefinition<T>;
- view: (props: T) => VNode;
- }
- : T extends unknown
- ? {
- url: string;
- view: (props: {}) => VNode;
- }
- : never;
-
-export function Router({
- pageList,
- onNotFound,
-}: {
- pageList: Array<PageEntry<any>>;
- onNotFound: () => VNode;
-}): VNode {
- const current = useCurrentLocation(pageList);
- if (current !== undefined) {
- return create(current.page.view, current.values);
- }
- return onNotFound();
-}
-
-type Location = {
- page: PageEntry<any>;
- path: string;
- values: Record<string, string>;
-};
-export function useCurrentLocation(pageList: Array<PageEntry<any>>) {
- const [currentLocation, setCurrentLocation] = useState<Location>();
- /**
- * Search path in the pageList
- * get the values from the path found
- * add params from searchParams
- *
- * @param path
- * @param params
- */
- function doSync(path: string, params: URLSearchParams) {
- let result: typeof currentLocation;
- for (let idx = 0; idx < pageList.length; idx++) {
- const page = pageList[idx];
- if (typeof page.url === "string") {
- if (page.url === path) {
- const values: Record<string, string> = {};
- params.forEach((v, k) => {
- values[k] = v;
- });
- result = { page, values, path };
- break;
- }
- } else {
- const values = doestUrlMatchToRoute(path, page.url.pattern);
- if (values !== undefined) {
- params.forEach((v, k) => {
- values[k] = v;
- });
- result = { page, values, path };
- break;
- }
- }
- }
- setCurrentLocation(result);
- }
- useEffect(() => {
- doSync(window.location.hash, new URLSearchParams(window.location.search));
- return history.listen(() => {
- doSync(window.location.hash, new URLSearchParams(window.location.search));
- });
- }, []);
- return currentLocation;
-}
-
-function doestUrlMatchToRoute(
- url: string,
- route: string,
-): undefined | Record<string, string> {
- const paramsPattern = /(?:\?([^#]*))?$/;
- // const paramsPattern = /(?:\?([^#]*))?(#.*)?$/;
- const params = url.match(paramsPattern);
- const urlWithoutParams = url.replace(paramsPattern, "");
-
- const result: Record<string, string> = {};
- if (params && params[1]) {
- const paramList = params[1].split("&");
- for (let i = 0; i < paramList.length; i++) {
- const idx = paramList[i].indexOf("=");
- const name = paramList[i].substring(0, idx);
- const value = paramList[i].substring(idx + 1);
- result[decodeURIComponent(name)] = decodeURIComponent(value);
- }
- }
- const urlSeg = urlWithoutParams.split("/");
- const routeSeg = route.split("/");
- let max = Math.max(urlSeg.length, routeSeg.length);
- for (let i = 0; i < max; i++) {
- if (routeSeg[i] && routeSeg[i].charAt(0) === ":") {
- const param = routeSeg[i].replace(/(^:|[+*?]+$)/g, "");
-
- const flags = (routeSeg[i].match(/[+*?]+$/) || EMPTY)[0] || "";
- const plus = ~flags.indexOf("+");
- const star = ~flags.indexOf("*");
- const val = urlSeg[i] || "";
-
- if (!val && !star && (flags.indexOf("?") < 0 || plus)) {
- return undefined;
- }
- result[param] = decodeURIComponent(val);
- if (plus || star) {
- result[param] = urlSeg.slice(i).map(decodeURIComponent).join("/");
- break;
- }
- } else if (routeSeg[i] !== urlSeg[i]) {
- return undefined;
- }
- }
- return result;
-}
-const EMPTY: Record<string, string> = {};
diff --git a/packages/demobank-ui/src/scss/main.css b/packages/demobank-ui/src/scss/main.css
deleted file mode 100644
index b5c61c956..000000000
--- a/packages/demobank-ui/src/scss/main.css
+++ /dev/null
@@ -1,3 +0,0 @@
-@tailwind base;
-@tailwind components;
-@tailwind utilities;
diff --git a/packages/demobank-ui/src/settings.json b/packages/demobank-ui/src/settings.json
deleted file mode 100644
index a9246bc93..000000000
--- a/packages/demobank-ui/src/settings.json
+++ /dev/null
@@ -1,11 +0,0 @@
-{
- "backendBaseURL": "http://bank.taler.test:1180/",
- "simplePasswordForRandomAccounts": true,
- "allowRandomAccountCreation": true,
- "bankName": "Taler DEVELOPMENT Bank",
- "topNavSites": {
- "Exchange": "http://Exchnage.taler.test:1180/",
- "Bank": "http://bank-ui.taler.test:1180/",
- "Merchant": "http://merchant.taler.test:1180/"
- }
-} \ No newline at end of file
diff --git a/packages/demobank-ui/src/settings.ts b/packages/demobank-ui/src/settings.ts
deleted file mode 100644
index 2c6ac1c67..000000000
--- a/packages/demobank-ui/src/settings.ts
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2022 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 { Codec, buildCodecForObject, codecForBoolean, codecForList, codecForMap, codecForString, codecOptional } from "@gnu-taler/taler-util";
-
-export interface BankUiSettings {
- // Where libeufin backend is localted
- // default: window.origin without "webui/"
- backendBaseURL?: string;
- // Shows a button "create random account" in the registration form
- // Useful for testing
- // default: false
- allowRandomAccountCreation?: boolean;
- // Create all random accounts with password "123"
- // Useful for testing
- // default: false
- simplePasswordForRandomAccounts?: boolean;
- // Bank name shown in the header
- // default: "Taler Bank"
- bankName?: string;
- // URL where the user is going to be redirected after
- // clicking in Taler Logo
- // default: home page
- iconLinkURL?: string;
- // Mapping for every link shown in the top navitation bar
- // - key: link label, what the user will read
- // - value: link target, where the user is going to be redirected
- // default: empty list
- topNavSites?: Record<string, string>;
-}
-
-/**
- * Global settings for the bank UI.
- */
-const defaultSettings: BankUiSettings = {
- backendBaseURL: buildDefaultBackendBaseURL(),
- iconLinkURL: undefined,
- bankName: "Taler Bank",
- simplePasswordForRandomAccounts: false,
- allowRandomAccountCreation: false,
- topNavSites: {},
-};
-
-const codecForBankUISettings = (): Codec<BankUiSettings> =>
- buildCodecForObject<BankUiSettings>()
- .property("backendBaseURL", codecOptional(codecForString()))
- .property("allowRandomAccountCreation", codecOptional(codecForBoolean()))
- .property("simplePasswordForRandomAccounts", codecOptional(codecForBoolean()))
- .property("bankName", codecOptional(codecForString()))
- .property("iconLinkURL", codecOptional(codecForString()))
- .property("topNavSites", codecOptional(codecForMap(codecForString())))
- .build("BankUiSettings");
-
-function removeUndefineField(obj: any): object {
- return Object.keys(obj).reduce((prev, cur) => {
- if (typeof prev[cur] === "undefined") {
- delete prev[cur]
- }
- return prev
- }, obj)
-}
-
-export function fetchSettings(listener: (s: BankUiSettings) => void): void {
- fetch("./settings.json")
- .then(resp => resp.json())
- .then(json => codecForBankUISettings().decode(json))
- .then(result => listener({
- ...defaultSettings,
- ...removeUndefineField(result),
- }))
- .catch(e => {
- console.log("failed to fetch settings", e)
- listener(defaultSettings)
- })
-}
-
-
-
-function buildDefaultBackendBaseURL(): string | undefined {
- if (typeof window !== "undefined") {
- const currentLocation = new URL(window.location.pathname, window.location.origin).href
- return currentLocation.replace("/webui", "")
- }
- return undefined
-}
diff --git a/packages/demobank-ui/src/stories.test.ts b/packages/demobank-ui/src/stories.test.ts
deleted file mode 100644
index ebd9e6d8a..000000000
--- a/packages/demobank-ui/src/stories.test.ts
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2022 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 { AccessToken, AmountString, TalerCorebankApi, setupI18n } from "@gnu-taler/taler-util";
-import { parseGroupImport } from "@gnu-taler/web-util/browser";
-import * as tests from "@gnu-taler/web-util/testing";
-import * as components from "./components/index.examples.js";
-import * as pages from "./pages/index.stories.js";
-
-import { ComponentChildren, VNode, h as create } from "preact";
-import { BackendStateProviderTesting } from "./context/backend.js";
-import { BankCoreApiProviderTesting } from "./context/config.js";
-
-setupI18n("en", { en: {} });
-
-describe("All the examples:", () => {
- const cms = parseGroupImport({ pages, components });
- cms.forEach((group) => {
- describe(`Example for group "${group.title}:"`, () => {
- group.list.forEach((component) => {
- describe(`Component ${component.name}:`, () => {
- component.examples.forEach((example) => {
- it(`should render example: ${example.name}`, () => {
- tests.renderUI(example.render, DefaultTestingContext);
- });
- });
- });
- });
- });
- });
-});
-
-function DefaultTestingContext({
- children,
-}: {
- children: ComponentChildren;
-}): VNode {
- const ctx1 = create(BackendStateProviderTesting, {
- children,
- state: {
- status: "loggedIn",
- username: "test",
- token: "pwd" as AccessToken,
- isUserAdministrator: false,
- },
- });
- const cfg: TalerCorebankApi.Config = {
- name: "libeufin-bank",
- allow_deletions: true,
- supported_tan_channels: [],
- allow_registrations: true,
- allow_conversion: true,
- allow_edit_cashout_payto_uri: false,
- allow_edit_name: false,
- currency: "ASR",
- currency_specification: {
- name: "ARS",
- alt_unit_names: {},
- num_fractional_input_digits: 2,
- num_fractional_normal_digits: 2,
- num_fractional_trailing_zero_digits: 2,
- },
- default_debit_threshold: "ARS:10" as AmountString,
- version: "1:0:0",
- }
- const ctx2 = create(BankCoreApiProviderTesting, {
- children: ctx1,
- state: cfg,
- url: "http://localhost",
- });
- return ctx2;
-}
diff --git a/packages/demobank-ui/src/stories.tsx b/packages/demobank-ui/src/stories.tsx
deleted file mode 100644
index 87848cb09..000000000
--- a/packages/demobank-ui/src/stories.tsx
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2022 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 { strings } from "./i18n/strings.js";
-
-import * as pages from "./pages/index.stories.js";
-import * as components from "./components/index.examples.js";
-
-import { renderStories } from "@gnu-taler/web-util/browser";
-
-function main(): void {
- renderStories(
- { pages, components },
- {
- strings,
- },
- );
-}
-
-if (document.readyState === "loading") {
- document.addEventListener("DOMContentLoaded", main);
-} else {
- main();
-}
diff --git a/packages/demobank-ui/src/utils.ts b/packages/demobank-ui/src/utils.ts
deleted file mode 100644
index 7cdd8a861..000000000
--- a/packages/demobank-ui/src/utils.ts
+++ /dev/null
@@ -1,399 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2022 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 { AmountString, HttpStatusCode, PaytoString, TalerError, TalerErrorCode, TranslatedString } from "@gnu-taler/taler-util";
-import {
- ErrorNotification,
- ErrorType,
- HttpError,
- notify,
- notifyError,
- useTranslationContext,
-} from "@gnu-taler/web-util/browser";
-
-
-/**
- * Validate (the number part of) an amount. If needed,
- * replace comma with a dot. Returns 'false' whenever
- * the input is invalid, the valid amount otherwise.
- */
-const amountRegex = /^[0-9]+(.[0-9]+)?$/;
-export function validateAmount(
- maybeAmount: string | undefined,
-): string | undefined {
- if (!maybeAmount || !amountRegex.test(maybeAmount)) {
- return;
- }
- return maybeAmount;
-}
-
-/**
- * Extract IBAN from a Payto URI.
- */
-export function getIbanFromPayto(url: string): string {
- const pathSplit = new URL(url).pathname.split("/");
- let lastIndex = pathSplit.length - 1;
- // Happens if the path ends with "/".
- if (pathSplit[lastIndex] === "") lastIndex--;
- const iban = pathSplit[lastIndex];
- return iban;
-}
-
-export function undefinedIfEmpty<T extends object>(obj: T): T | undefined {
- return Object.keys(obj).some((k) => (obj as any)[k] !== undefined)
- ? obj
- : undefined;
-}
-
-export type PartialButDefined<T> = {
- [P in keyof T]: T[P] | undefined;
-};
-
-/**
- * every non-map field can be undefined
- */
-export type WithIntermediate<Type> = {
- [prop in keyof Type]:
- Type[prop] extends PaytoString ? Type[prop] | undefined :
- Type[prop] extends AmountString ? Type[prop] | undefined :
- Type[prop] extends TranslatedString ? Type[prop] | undefined :
- Type[prop] extends object
- ? WithIntermediate<Type[prop]>
- : Type[prop] | undefined;
-};
-export type RecursivePartial<Type> = {
- [P in keyof Type]?: Type[P] extends (infer U)[]
- ? RecursivePartial<U>[]
- : Type[P] extends object
- ? RecursivePartial<Type[P]>
- : Type[P];
-};
-export type ErrorMessageMappingFor<Type> = {
- [prop in keyof Type]+?:
- //enumerate known object
- Exclude<Type[prop],undefined> extends PaytoString ? TranslatedString :
- Exclude<Type[prop],undefined> extends AmountString ? TranslatedString :
- Exclude<Type[prop],undefined> extends TranslatedString ? TranslatedString :
- // arrays: every element
- Exclude<Type[prop],undefined> extends (infer U)[] ? ErrorMessageMappingFor<U>[] :
- // map: every field
- Exclude<Type[prop],undefined> extends object ? ErrorMessageMappingFor<Type[prop]>
- : TranslatedString;
-};
-
-export enum TanChannel {
- SMS = "sms",
- EMAIL = "email",
-}
-export enum CashoutStatus {
- // The payment was initiated after a valid
- // TAN was received by the bank.
- CONFIRMED = "confirmed",
-
- // The cashout was created and now waits
- // for the TAN by the author.
- PENDING = "pending",
-}
-
-
-export const PAGE_SIZE = 20;
-export const MAX_RESULT_SIZE = PAGE_SIZE * 2 - 1;
-
-type Translator = ReturnType<typeof useTranslationContext>["i18n"]
-
-export async function withRuntimeErrorHandling<T>(i18n: Translator, cb: () => Promise<T>): Promise<void> {
- try {
- await cb()
- } catch (error: unknown) {
- if (error instanceof TalerError) {
- notify(buildRequestErrorMessage(i18n, error))
- } else {
- notifyError(
- i18n.str`Operation failed, please report`,
- (error instanceof Error
- ? error.message
- : JSON.stringify(error)) as TranslatedString
- )
- }
- }
-}
-
-
-export function buildRequestErrorMessage(i18n: Translator, cause: TalerError): ErrorNotification {
- let result: ErrorNotification;
- switch (cause.errorDetail.code) {
- case TalerErrorCode.WALLET_HTTP_REQUEST_GENERIC_TIMEOUT: {
- result = {
- type: "error",
- title: i18n.str`Request timeout`,
- description: cause.message as TranslatedString,
- debug: JSON.stringify(cause.errorDetail, undefined, 2),
- };
- break;
- }
- case TalerErrorCode.WALLET_HTTP_REQUEST_THROTTLED: {
- result = {
- type: "error",
- title: i18n.str`Request throttled`,
- description: cause.message as TranslatedString,
- debug: JSON.stringify(cause.errorDetail, undefined, 2),
- };
- break;
- }
- case TalerErrorCode.WALLET_RECEIVED_MALFORMED_RESPONSE: {
- result = {
- type: "error",
- title: i18n.str`Malformed response`,
- description: cause.message as TranslatedString,
- debug: JSON.stringify(cause.errorDetail, undefined, 2),
- };
- break;
- }
- case TalerErrorCode.WALLET_NETWORK_ERROR: {
- result = {
- type: "error",
- title: i18n.str`Network error`,
- description: cause.message as TranslatedString,
- debug: JSON.stringify(cause.errorDetail, undefined, 2),
- };
- break;
- }
- case TalerErrorCode.WALLET_UNEXPECTED_REQUEST_ERROR: {
- result = {
- type: "error",
- title: i18n.str`Unexpected request error`,
- description: cause.message as TranslatedString,
- debug: JSON.stringify(cause.errorDetail, undefined, 2),
- };
- break;
- }
- default: {
- result = {
- type: "error",
- title: i18n.str`Unexpected error`,
- description: cause.message as TranslatedString,
- debug: JSON.stringify(cause.errorDetail, undefined, 2),
- };
- break;
- }
- }
- return result;
-}
-
-export const COUNTRY_TABLE = {
- AE: "U.A.E.",
- AF: "Afghanistan",
- AL: "Albania",
- AM: "Armenia",
- AN: "Netherlands Antilles",
- AR: "Argentina",
- AT: "Austria",
- AU: "Australia",
- AZ: "Azerbaijan",
- BA: "Bosnia and Herzegovina",
- BD: "Bangladesh",
- BE: "Belgium",
- BG: "Bulgaria",
- BH: "Bahrain",
- BN: "Brunei Darussalam",
- BO: "Bolivia",
- BR: "Brazil",
- BT: "Bhutan",
- BY: "Belarus",
- BZ: "Belize",
- CA: "Canada",
- CG: "Congo",
- CH: "Switzerland",
- CI: "Cote d'Ivoire",
- CL: "Chile",
- CM: "Cameroon",
- CN: "People's Republic of China",
- CO: "Colombia",
- CR: "Costa Rica",
- CS: "Serbia and Montenegro",
- CZ: "Czech Republic",
- DE: "Germany",
- DK: "Denmark",
- DO: "Dominican Republic",
- DZ: "Algeria",
- EC: "Ecuador",
- EE: "Estonia",
- EG: "Egypt",
- ER: "Eritrea",
- ES: "Spain",
- ET: "Ethiopia",
- FI: "Finland",
- FO: "Faroe Islands",
- FR: "France",
- GB: "United Kingdom",
- GD: "Caribbean",
- GE: "Georgia",
- GL: "Greenland",
- GR: "Greece",
- GT: "Guatemala",
- HK: "Hong Kong",
- // HK: "Hong Kong S.A.R.",
- HN: "Honduras",
- HR: "Croatia",
- HT: "Haiti",
- HU: "Hungary",
- ID: "Indonesia",
- IE: "Ireland",
- IL: "Israel",
- IN: "India",
- IQ: "Iraq",
- IR: "Iran",
- IS: "Iceland",
- IT: "Italy",
- JM: "Jamaica",
- JO: "Jordan",
- JP: "Japan",
- KE: "Kenya",
- KG: "Kyrgyzstan",
- KH: "Cambodia",
- KR: "South Korea",
- KW: "Kuwait",
- KZ: "Kazakhstan",
- LA: "Laos",
- LB: "Lebanon",
- LI: "Liechtenstein",
- LK: "Sri Lanka",
- LT: "Lithuania",
- LU: "Luxembourg",
- LV: "Latvia",
- LY: "Libya",
- MA: "Morocco",
- MC: "Principality of Monaco",
- MD: "Moldava",
- // MD: "Moldova",
- ME: "Montenegro",
- MK: "Former Yugoslav Republic of Macedonia",
- ML: "Mali",
- MM: "Myanmar",
- MN: "Mongolia",
- MO: "Macau S.A.R.",
- MT: "Malta",
- MV: "Maldives",
- MX: "Mexico",
- MY: "Malaysia",
- NG: "Nigeria",
- NI: "Nicaragua",
- NL: "Netherlands",
- NO: "Norway",
- NP: "Nepal",
- NZ: "New Zealand",
- OM: "Oman",
- PA: "Panama",
- PE: "Peru",
- PH: "Philippines",
- PK: "Islamic Republic of Pakistan",
- PL: "Poland",
- PR: "Puerto Rico",
- PT: "Portugal",
- PY: "Paraguay",
- QA: "Qatar",
- RE: "Reunion",
- RO: "Romania",
- RS: "Serbia",
- RU: "Russia",
- RW: "Rwanda",
- SA: "Saudi Arabia",
- SE: "Sweden",
- SG: "Singapore",
- SI: "Slovenia",
- SK: "Slovak",
- SN: "Senegal",
- SO: "Somalia",
- SR: "Suriname",
- SV: "El Salvador",
- SY: "Syria",
- TH: "Thailand",
- TJ: "Tajikistan",
- TM: "Turkmenistan",
- TN: "Tunisia",
- TR: "Turkey",
- TT: "Trinidad and Tobago",
- TW: "Taiwan",
- TZ: "Tanzania",
- UA: "Ukraine",
- US: "United States",
- UY: "Uruguay",
- VA: "Vatican",
- VE: "Venezuela",
- VN: "Viet Nam",
- YE: "Yemen",
- ZA: "South Africa",
- ZW: "Zimbabwe",
-};
-
-/**
- * An IBAN is validated by converting it into an integer and performing a
- * basic mod-97 operation (as described in ISO 7064) on it.
- * If the IBAN is valid, the remainder equals 1.
- *
- * The algorithm of IBAN validation is as follows:
- * 1.- Check that the total IBAN length is correct as per the country. If not, the IBAN is invalid
- * 2.- Move the four initial characters to the end of the string
- * 3.- Replace each letter in the string with two digits, thereby expanding the string, where A = 10, B = 11, ..., Z = 35
- * 4.- Interpret the string as a decimal integer and compute the remainder of that number on division by 97
- *
- * If the remainder is 1, the check digit test is passed and the IBAN might be valid.
- *
- */
-export function validateIBAN(
- iban: string,
- i18n: ReturnType<typeof useTranslationContext>["i18n"],
-): TranslatedString | undefined {
- // Check total length
- if (iban.length < 4)
- return i18n.str`IBAN numbers usually have more that 4 digits`;
- if (iban.length > 34)
- return i18n.str`IBAN numbers usually have less that 34 digits`;
-
- const A_code = "A".charCodeAt(0);
- const Z_code = "Z".charCodeAt(0);
- const IBAN = iban.toUpperCase();
- // check supported country
- const code = IBAN.substring(0, 2);
- const found = code in COUNTRY_TABLE;
- if (!found) return i18n.str`IBAN country code not found`;
-
- // 2.- Move the four initial characters to the end of the string
- const step2 = IBAN.substring(4) + iban.substring(0, 4);
- const step3 = Array.from(step2)
- .map((letter) => {
- const code = letter.charCodeAt(0);
- if (code < A_code || code > Z_code) return letter;
- return `${letter.charCodeAt(0) - "A".charCodeAt(0) + 10}`;
- })
- .join("");
-
- const checksum = calculate_iban_checksum(step3);
- if (checksum !== 1)
- return i18n.str`IBAN number is not valid, checksum is wrong`;
- return undefined;
-}
-
-function calculate_iban_checksum(str: string): number {
- const numberStr = str.substring(0, 5);
- const rest = str.substring(5);
- const number = parseInt(numberStr, 10);
- const result = number % 97;
- if (rest.length > 0) {
- return calculate_iban_checksum(`${result}${rest}`);
- }
- return result;
-}
diff --git a/packages/demobank-ui/tailwind.config.js b/packages/demobank-ui/tailwind.config.js
deleted file mode 100644
index ec51dfbb8..000000000
--- a/packages/demobank-ui/tailwind.config.js
+++ /dev/null
@@ -1,14 +0,0 @@
-/** @type {import('tailwindcss').Config} */
-export default {
- content: {
- relative: true,
- files: [
- "./src/**/*.{html,tsx}",
- "./node_modules/@gnu-taler/web-util/src/**/*.{html,tsx}"
- ],
- },
- theme: {
- extend: {},
- },
- plugins: [require("@tailwindcss/typography"), require("@tailwindcss/forms")],
-};
diff --git a/packages/demobank-ui/test.mjs b/packages/demobank-ui/test.mjs
deleted file mode 100755
index 9ff6055c6..000000000
--- a/packages/demobank-ui/test.mjs
+++ /dev/null
@@ -1,32 +0,0 @@
-#!/usr/bin/env node
-/*
- This file is part of GNU Taler
- (C) 2022 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 { build } from "@gnu-taler/web-util/build";
-import { getFilesInDirectory } from "@gnu-taler/web-util/build";
-
-const allTestFiles = getFilesInDirectory("src", /.test.tsx?$/);
-
-await build({
- type: "test",
- source: {
- js: allTestFiles.files,
- assets: [{base:"src",files:["src/index.html"]}],
-
- },
- destination: "./dist/test",
- css: "sass",
-});
diff --git a/packages/demobank-ui/tsconfig.json b/packages/demobank-ui/tsconfig.json
deleted file mode 100644
index 9826fac07..000000000
--- a/packages/demobank-ui/tsconfig.json
+++ /dev/null
@@ -1,46 +0,0 @@
-{
- "compilerOptions": {
- /* Basic Options */
- "target": "ES2020",
- "module": "Node16",
- "lib": ["DOM", "ES2020"],
- "allowJs": true /* Allow javascript files to be compiled. */,
- // "checkJs": true, /* Report errors in .js files. */
- "jsx": "react" /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */,
- "jsxFactory": "h",
- "jsxFragmentFactory": "Fragment",
- "noEmit": true /* Do not emit outputs. */,
- // "importHelpers": true, /* Import emit helpers from 'tslib'. */
- // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
- // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */
- /* Strict Type-Checking Options */
- "strict": true /* Enable all strict type-checking options. */,
- "noImplicitAny": true /* Raise error on expressions and declarations with an implied 'any' type. */,
- /* Additional Checks */
- // "noUnusedLocals": true, /* Report errors on unused locals. */
- // "noUnusedParameters": true, /* Report errors on unused parameters. */
- // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
- // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
- /* Module Resolution Options */
- "moduleResolution": "Node16" /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */,
- "esModuleInterop": true /* */,
- // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */
- // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
- // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
- // "typeRoots": [], /* List of folders to include type definitions from. */
- // "types": [], /* Type declaration files to be included in compilation. */
- "allowSyntheticDefaultImports": true /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */,
- // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
- /* Source Map Options */
- // "sourceRoot": "./", /* Specify the location where debugger should locate TypeScript files instead of source locations. */
- // "mapRoot": "./", /* Specify the location where debugger should locate map files instead of generated locations. */
- // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */
- // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */
- /* Experimental Options */
- // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
- // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
- /* Advanced Options */
- "skipLibCheck": true /* Skip type checking of declaration files. */
- },
- "include": ["src/**/*"]
-}