From a1c5917e626856f2abd9dbe6ddaa71c1458334c6 Mon Sep 17 00:00:00 2001 From: Sebastian Date: Fri, 26 Apr 2024 14:31:48 -0300 Subject: update code to match others --- packages/aml-backoffice-ui/src/App.tsx | 97 ++++++-- packages/aml-backoffice-ui/src/Dashboard.tsx | 266 -------------------- .../aml-backoffice-ui/src/ExchangeAmlFrame.tsx | 273 +++++++++++++++++++++ packages/aml-backoffice-ui/src/Routing.tsx | 151 ++++++++++++ packages/aml-backoffice-ui/src/context/config.ts | 100 -------- packages/aml-backoffice-ui/src/context/settings.ts | 44 ++++ packages/aml-backoffice-ui/src/declaration.d.ts | 2 +- packages/aml-backoffice-ui/src/forms.ts | 2 +- packages/aml-backoffice-ui/src/forms/902_11e.ts | 2 +- packages/aml-backoffice-ui/src/forms/902_12e.ts | 2 +- packages/aml-backoffice-ui/src/forms/902_13e.ts | 2 +- packages/aml-backoffice-ui/src/forms/902_15e.ts | 2 +- packages/aml-backoffice-ui/src/forms/902_1e.ts | 2 +- packages/aml-backoffice-ui/src/forms/902_4e.ts | 2 +- packages/aml-backoffice-ui/src/forms/902_5e.ts | 2 +- packages/aml-backoffice-ui/src/forms/902_9e.ts | 2 +- .../aml-backoffice-ui/src/forms/declaration.ts | 2 +- packages/aml-backoffice-ui/src/forms/icons.tsx | 2 +- packages/aml-backoffice-ui/src/forms/index.ts | 2 +- packages/aml-backoffice-ui/src/forms/simplest.ts | 2 +- packages/aml-backoffice-ui/src/hooks/form.ts | 124 ++++++++++ packages/aml-backoffice-ui/src/hooks/officer.ts | 159 ++++++++++++ .../aml-backoffice-ui/src/hooks/preferences.ts | 85 +++++++ packages/aml-backoffice-ui/src/hooks/useBackend.ts | 48 ---- .../aml-backoffice-ui/src/hooks/useCaseDetails.ts | 8 +- packages/aml-backoffice-ui/src/hooks/useCases.ts | 50 ++-- packages/aml-backoffice-ui/src/hooks/useOfficer.ts | 150 ----------- .../aml-backoffice-ui/src/hooks/useSettings.ts | 71 ------ packages/aml-backoffice-ui/src/i18n/bank.pot | 2 +- packages/aml-backoffice-ui/src/i18n/fr.po | 2 +- packages/aml-backoffice-ui/src/i18n/poheader | 2 +- .../aml-backoffice-ui/src/i18n/strings-prelude | 2 +- packages/aml-backoffice-ui/src/i18n/strings.ts | 2 +- packages/aml-backoffice-ui/src/index.html | 2 +- packages/aml-backoffice-ui/src/index.tsx | 2 +- packages/aml-backoffice-ui/src/pages.ts | 58 ----- .../src/pages/AntiMoneyLaunderingForm.stories.tsx | 2 +- .../src/pages/AntiMoneyLaunderingForm.tsx | 20 +- .../aml-backoffice-ui/src/pages/CaseDetails.tsx | 6 +- .../aml-backoffice-ui/src/pages/CaseUpdate.tsx | 132 ++++++++++ .../aml-backoffice-ui/src/pages/Cases.stories.tsx | 2 +- packages/aml-backoffice-ui/src/pages/Cases.tsx | 6 +- .../aml-backoffice-ui/src/pages/CreateAccount.tsx | 8 +- .../src/pages/HandleAccountNotReady.tsx | 4 +- .../aml-backoffice-ui/src/pages/NewFormEntry.tsx | 136 ---------- packages/aml-backoffice-ui/src/pages/Officer.tsx | 27 +- .../src/pages/ShowConsolidated.stories.tsx | 2 +- .../src/pages/ShowConsolidated.tsx | 2 +- .../aml-backoffice-ui/src/pages/UnlockAccount.tsx | 2 +- .../aml-backoffice-ui/src/pages/index.stories.ts | 2 +- packages/aml-backoffice-ui/src/route.ts | 239 ------------------ packages/aml-backoffice-ui/src/settings.ts | 74 +++++- packages/aml-backoffice-ui/src/stories.test.ts | 23 +- packages/aml-backoffice-ui/src/stories.tsx | 51 ++-- packages/aml-backoffice-ui/src/utils/QR.tsx | 2 +- 55 files changed, 1267 insertions(+), 1199 deletions(-) delete mode 100644 packages/aml-backoffice-ui/src/Dashboard.tsx create mode 100644 packages/aml-backoffice-ui/src/ExchangeAmlFrame.tsx create mode 100644 packages/aml-backoffice-ui/src/Routing.tsx delete mode 100644 packages/aml-backoffice-ui/src/context/config.ts create mode 100644 packages/aml-backoffice-ui/src/context/settings.ts create mode 100644 packages/aml-backoffice-ui/src/hooks/form.ts create mode 100644 packages/aml-backoffice-ui/src/hooks/officer.ts create mode 100644 packages/aml-backoffice-ui/src/hooks/preferences.ts delete mode 100644 packages/aml-backoffice-ui/src/hooks/useBackend.ts delete mode 100644 packages/aml-backoffice-ui/src/hooks/useOfficer.ts delete mode 100644 packages/aml-backoffice-ui/src/hooks/useSettings.ts delete mode 100644 packages/aml-backoffice-ui/src/pages.ts create mode 100644 packages/aml-backoffice-ui/src/pages/CaseUpdate.tsx delete mode 100644 packages/aml-backoffice-ui/src/pages/NewFormEntry.tsx delete mode 100644 packages/aml-backoffice-ui/src/route.ts (limited to 'packages/aml-backoffice-ui/src') diff --git a/packages/aml-backoffice-ui/src/App.tsx b/packages/aml-backoffice-ui/src/App.tsx index 55f03322d..d55de776b 100644 --- a/packages/aml-backoffice-ui/src/App.tsx +++ b/packages/aml-backoffice-ui/src/App.tsx @@ -1,6 +1,6 @@ /* This file is part of GNU Taler - (C) 2022 Taler Systems S.A. + (C) 2022-2024 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 @@ -13,26 +13,46 @@ You should have received a copy of the GNU General Public License along with GNU Taler; see the file COPYING. If not, see */ -import { TranslationProvider } from "@gnu-taler/web-util/browser"; -import { h, VNode } from "preact"; +import { canonicalizeBaseUrl } from "@gnu-taler/taler-util"; +import { + BrowserHashNavigationProvider, + ExchangeApiProvider, + Loading, + TranslationProvider, +} from "@gnu-taler/web-util/browser"; +import { VNode, h } from "preact"; +import { useEffect, useState } from "preact/hooks"; import { SWRConfig } from "swr"; -import { ExchangeApiProvider } from "./context/config.js"; -import { ExchangeAmlFrame } from "./Dashboard.js"; -import { getInitialBackendBaseURL } from "./hooks/useBackend.js"; -import { Pages } from "./pages.js"; -import { HashPathProvider, Router } from "./route.js"; +import { ExchangeAmlFrame } from "./ExchangeAmlFrame.js"; +import { Routing } from "./Routing.js"; +import { SettingsProvider } from "./context/settings.js"; +import { strings } from "./i18n/strings.js"; import "./scss/main.css"; +import { UiSettings, fetchSettings } from "./settings.js"; const WITH_LOCAL_STORAGE_CACHE = false; -const pageList = Object.values(Pages); - export function App(): VNode { - const baseUrl = getInitialBackendBaseURL(); + const [settings, setSettings] = useState(); + useEffect(() => { + fetchSettings(setSettings); + }, []); + if (!settings) return ; + + const baseUrl = getInitialBackendBaseURL(settings.backendBaseURL); return ( - - - + + + - - { - window.location.href = Pages.cases.url; - return
not found
; - }} - /> -
+ + +
-
-
-
+ + + ); } @@ -85,3 +99,34 @@ function localStorageProvider(): Map { }); return map; } + +function getInitialBackendBaseURL( + backendFromSettings: string | undefined, +): string { + const overrideUrl = + typeof localStorage !== "undefined" + ? localStorage.getItem("exchange-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); + } +} diff --git a/packages/aml-backoffice-ui/src/Dashboard.tsx b/packages/aml-backoffice-ui/src/Dashboard.tsx deleted file mode 100644 index 78980265d..000000000 --- a/packages/aml-backoffice-ui/src/Dashboard.tsx +++ /dev/null @@ -1,266 +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 - */ -import { TranslatedString } from "@gnu-taler/taler-util"; -import { - Footer, - Header, - ToastBanner, - notifyError, - notifyException, - useTranslationContext, -} from "@gnu-taler/web-util/browser"; -import { ComponentChildren, VNode, h } from "preact"; -import { useEffect, useErrorBoundary } from "preact/hooks"; -import { useOfficer } from "./hooks/useOfficer.js"; -import { - getAllBooleanSettings, - getLabelForSetting, - useSettings, -} from "./hooks/useSettings.js"; -import { Pages } from "./pages.js"; -import { PageEntry, useChangeLocation } from "./route.js"; -import { uiSettings } from "./settings.js"; - -/** - * mapping route to view - * not found (error page) - * nested, index element, relative routes - * link interception - * form POST interception, call action - * fromData => Object.fromEntries - * segments in the URL - * navigationState: idle, submitting, loading - * form GET interception: does a navigateTo - * form GET Sync: - * 1.- back after submit: useEffect to sync URL to form - * 2.- refresh after submit: input default value - * useSubmit for form submission onChange, history replace - * - * post form without redirect - * - * - * @param param0 - * @returns - */ - -const GIT_HASH = typeof __GIT_HASH__ !== "undefined" ? __GIT_HASH__ : undefined; -const VERSION = typeof __VERSION__ !== "undefined" ? __VERSION__ : undefined; - -/** - * TO BE FIXED: - * - * 1.- when the form change to other form and both form share the same structure - * the same input component may be rendered in the same place, - * since input are uncontrolled the are not re-rendered and since they are - * uncontrolled it will keep the value of the previous form. - * One solutions could be to remove the form when unloading and when the new - * form load it will start without previous vdom, preventing the cache - * to create this behavior. - * Other solutions could be using IDs in the fields that are constructed - * with the ID of the form, so two fields of different form will need to re-render - * cleaning up the state of the previous form. - * - * 2.- currently the design prop and the behavior prop of the flexible form - * are two side of the same coin. From the design point of view, it is important - * to design the form in a list-of-field manner and there may be additional - * content that is not directly mapped to the form structure (object) - * So maybe we want to change the current shape so the computation of the state - * of the form is in a field level, but this computation required the field value and - * the whole form values and state (since one field may be disabled/hidden) because - * of the value of other field. - * - * 3.- given the previous requirement, maybe the name of the field of the form could be - * a function (P: F -> V) where F is the form (or parent object) and V is the type of the - * property. That will help with the typing of the forms props - * - * 4.- tooltip are not placed correctly: the arrow should point the question mark - * and the text area should be bigger - * - */ - -/** - * check this fields - * - * Signature of Contracting partner, 902_9e - * Currency and amount of deposited assets, 902_5e - * Signature on declaration of trust, 902.13e - * also fundations - * also life insurance - * - * no all state are handled by all the inputs - * all the input implementation should respect - * ui props and state - */ - -export function ExchangeAmlFrame({ - children, -}: { - children?: ComponentChildren; -}): VNode { - const { i18n } = useTranslationContext(); - - const [error] = useErrorBoundary(); - - useEffect(() => { - if (error) { - if (error instanceof Error) { - notifyException(i18n.str`Internal error, please report.`, error); - } else { - notifyError( - i18n.str`Internal error, please report.`, - String(error) as TranslatedString, - ); - } - console.log(error); - // resetError() - } - }, [error]); - - const officer = useOfficer(); - const [settings, updateSettings] = useSettings(); - - return ( -
-
-
{ - officer.lock(); - } - } - sites={[]} - supportedLangs={["en", "es", "de"]} - > -
  • -
    - Preferences -
    -
      - {getAllBooleanSettings().map((set) => { - const isOn: boolean = !!settings[set]; - return ( -
    • -
      - - - {getLabelForSetting(set, i18n)} - - - -
      -
    • - ); - })} -
    -
  • -
    -
    - -
    -
    - -
    -
    - -
    - {officer.state !== "ready" ? undefined : } -
    -
    {children}
    -
    -
    - -
    -
    - ); -} - -function Navigation(): VNode { - const pageList: Array = [Pages.officer, Pages.cases]; - const location = useChangeLocation(); - return ( - - ); -} diff --git a/packages/aml-backoffice-ui/src/ExchangeAmlFrame.tsx b/packages/aml-backoffice-ui/src/ExchangeAmlFrame.tsx new file mode 100644 index 000000000..66eb3df36 --- /dev/null +++ b/packages/aml-backoffice-ui/src/ExchangeAmlFrame.tsx @@ -0,0 +1,273 @@ +/* + This file is part of GNU Taler + (C) 2022-2024 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 + */ +import { TranslatedString } from "@gnu-taler/taler-util"; +import { + Footer, + Header, + ToastBanner, + notifyError, + notifyException, + useNavigationContext, + useTranslationContext, +} from "@gnu-taler/web-util/browser"; +import { ComponentChildren, VNode, h } from "preact"; +import { useEffect, useErrorBoundary } from "preact/hooks"; +import { privatePages } from "./Routing.js"; +import { useSettingsContext } from "./context/settings.js"; +import { OfficerState } from "./hooks/officer.js"; +import { + getAllBooleanPreferences, + getLabelForPreferences, + usePreferences, +} from "./hooks/preferences.js"; +import { HomeIcon } from "./pages/Cases.js"; + +/** + * mapping route to view + * not found (error page) + * nested, index element, relative routes + * link interception + * form POST interception, call action + * fromData => Object.fromEntries + * segments in the URL + * navigationState: idle, submitting, loading + * form GET interception: does a navigateTo + * form GET Sync: + * 1.- back after submit: useEffect to sync URL to form + * 2.- refresh after submit: input default value + * useSubmit for form submission onChange, history replace + * + * post form without redirect + * + * + * @param param0 + * @returns + */ + +const GIT_HASH = typeof __GIT_HASH__ !== "undefined" ? __GIT_HASH__ : undefined; +const VERSION = typeof __VERSION__ !== "undefined" ? __VERSION__ : undefined; + +/** + * TO BE FIXED: + * + * 1.- when the form change to other form and both form share the same structure + * the same input component may be rendered in the same place, + * since input are uncontrolled the are not re-rendered and since they are + * uncontrolled it will keep the value of the previous form. + * One solutions could be to remove the form when unloading and when the new + * form load it will start without previous vdom, preventing the cache + * to create this behavior. + * Other solutions could be using IDs in the fields that are constructed + * with the ID of the form, so two fields of different form will need to re-render + * cleaning up the state of the previous form. + * + * 2.- currently the design prop and the behavior prop of the flexible form + * are two side of the same coin. From the design point of view, it is important + * to design the form in a list-of-field manner and there may be additional + * content that is not directly mapped to the form structure (object) + * So maybe we want to change the current shape so the computation of the state + * of the form is in a field level, but this computation required the field value and + * the whole form values and state (since one field may be disabled/hidden) because + * of the value of other field. + * + * 3.- given the previous requirement, maybe the name of the field of the form could be + * a function (P: F -> V) where F is the form (or parent object) and V is the type of the + * property. That will help with the typing of the forms props + * + * 4.- tooltip are not placed correctly: the arrow should point the question mark + * and the text area should be bigger + * + */ + +/** + * check this fields + * + * Signature of Contracting partner, 902_9e + * Currency and amount of deposited assets, 902_5e + * Signature on declaration of trust, 902.13e + * also fundations + * also life insurance + * + * no all state are handled by all the inputs + * all the input implementation should respect + * ui props and state + */ + +export function ExchangeAmlFrame({ + children, + officer, +}: { + officer?: OfficerState, + children?: ComponentChildren; +}): VNode { + const { i18n } = useTranslationContext(); + + const [error] = useErrorBoundary(); + + useEffect(() => { + if (error) { + if (error instanceof Error) { + notifyException(i18n.str`Internal error, please report.`, error); + } else { + notifyError( + i18n.str`Internal error, please report.`, + String(error) as TranslatedString, + ); + } + console.log(error); + // resetError() + } + }, [error]); + + const [preferences, updatePreferences] = usePreferences(); + const settings = useSettingsContext() + + return ( +
    +
    +
    { + officer.lock(); + } + } + sites={[]} + supportedLangs={["en", "es", "de"]} + > +
  • +
    + Preferences +
    +
      + {getAllBooleanPreferences().map((set) => { + const isOn: boolean = !!preferences[set]; + return ( +
    • +
      + + + {getLabelForPreferences(set, i18n)} + + + +
      +
    • + ); + })} +
    +
  • +
    +
    + +
    +
    + +
    +
    + +
    + {officer?.state !== "ready" ? undefined : } +
    +
    {children}
    +
    +
    + +
    +
    + ); +} + +function Navigation(): VNode { + const { i18n } = useTranslationContext(); + const pageList = [ + { route: privatePages.account, Icon: HomeIcon, label: i18n.str`Account` }, + { route: privatePages.cases, Icon: HomeIcon, label: i18n.str`Cases` }, + ]; + const { path } = useNavigationContext(); + return ( + + ); +} diff --git a/packages/aml-backoffice-ui/src/Routing.tsx b/packages/aml-backoffice-ui/src/Routing.tsx new file mode 100644 index 000000000..1e32e4f4c --- /dev/null +++ b/packages/aml-backoffice-ui/src/Routing.tsx @@ -0,0 +1,151 @@ +/* + This file is part of GNU Taler + (C) 2022-2024 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 + */ + +import { + urlPattern, + useCurrentLocation, + useNavigationContext, + useTranslationContext, +} from "@gnu-taler/web-util/browser"; +import { Fragment, VNode, h } from "preact"; + +import { assertUnreachable } from "@gnu-taler/taler-util"; +import { useEffect } from "preact/hooks"; +import { ExchangeAmlFrame } from "./ExchangeAmlFrame.js"; +import { useOfficer } from "./hooks/officer.js"; +import { Cases } from "./pages/Cases.js"; +import { Officer } from "./pages/Officer.js"; +import { CaseDetails } from "./pages/CaseDetails.js"; +import { CaseUpdate, SelectForm } from "./pages/CaseUpdate.js"; +import { HandleAccountNotReady } from "./pages/HandleAccountNotReady.js"; + +export function Routing(): VNode { + const session = useOfficer(); + + if (session.state === "ready") { + return ( + + + + ); + } + return ( + + + + ); +} + +const publicPages = { + config: urlPattern(/\/config/, () => "#/config"), + login: urlPattern(/\/login/, () => "#/login"), +}; + +function PublicRounting(): VNode { + const { i18n } = useTranslationContext(); + const location = useCurrentLocation(publicPages); + // const { navigateTo } = useNavigationContext(); + // const { config, lib } = useExchangeApiContext(); + // const [notification, notify, handleError] = useLocalNotification(); + const session = useOfficer(); + + if (location === undefined) { + if (session.state !== "ready") { + return ; + } else { + return
    + } + } + + switch (location.name) { + case "config": { + return ( + +
    +

    {i18n.str`Welcome to exchange config!`}

    +
    +
    + ); + } + case "login": { + return ( + +
    +

    {i18n.str`Welcome to exchange config!`}

    +
    +
    + ); + } + default: + assertUnreachable(location); + } +} + +export const privatePages = { + account: urlPattern(/\/account/, () => "#/account"), + cases: urlPattern(/\/cases/, () => "#/cases"), + caseDetails: urlPattern<{ cid: string }>( + /\/case\/(?[a-zA-Z0-9]+)/, + ({ cid }) => `#/case/${cid}`, + ), + caseUpdate: urlPattern<{ cid: string; type: string }>( + /\/case\/(?[a-zA-Z0-9]+)\/new\/(?[a-zA-Z0-9]+)/, + ({ cid, type }) => `#/case/${cid}/new/${type}`, + ), + caseNew: urlPattern<{ cid: string }>( + /\/case\/(?[a-zA-Z0-9]+)\/new/, + ({ cid }) => `#/case/${cid}/new`, + ), +}; + +function PrivateRouting(): VNode { + const { navigateTo } = useNavigationContext(); + const location = useCurrentLocation(privatePages); + useEffect(() => { + if (location === undefined) { + navigateTo(privatePages.account.url({})); + } + }, [location]); + + if (location === undefined) { + return ; + } + + switch (location.name) { + case "account": { + return ; + } + case "caseDetails": { + return ; + } + case "caseUpdate": { + return ( + + ); + } + case "caseNew": { + return ; + } + case "cases": { + return ; + } + default: + assertUnreachable(location); + } +} diff --git a/packages/aml-backoffice-ui/src/context/config.ts b/packages/aml-backoffice-ui/src/context/config.ts deleted file mode 100644 index d2bc58578..000000000 --- a/packages/aml-backoffice-ui/src/context/config.ts +++ /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 - */ - -import { TalerExchangeApi, TalerExchangeHttpClient, TalerError } from "@gnu-taler/taler-util"; -import { BrowserFetchHttpLib, useTranslationContext } from "@gnu-taler/web-util/browser"; -import { ComponentChildren, createContext, FunctionComponent, h, VNode } from "preact"; -import { useContext, useEffect, useState } from "preact/hooks"; -import { ErrorLoading } from "@gnu-taler/web-util/browser"; - -/** - * - * @author Sebastian Javier Marchano (sebasjm) - */ - -export type Type = { - url: URL, - config: TalerExchangeApi.ExchangeVersionResponse, - api: TalerExchangeHttpClient, -}; - -const Context = createContext(undefined!); - -export const useExchangeApiContext = (): Type => useContext(Context); -export const useMaybeExchangeApiContext = (): Type | undefined => useContext(Context); - -export function ExchangeApiContextTesting({ config, children }: { config: TalerExchangeApi.ExchangeVersionResponse, children?: ComponentChildren; }): VNode { - return h(Context.Provider, { - value: { url: new URL("http://testing"), config, api: null! }, - children - } - ) -} - -export type ConfigResult = undefined - | { type: "ok", config: TalerExchangeApi.ExchangeVersionResponse } - | { type: "incompatible", result: TalerExchangeApi.ExchangeVersionResponse, supported: string } - | { type: "error", error: TalerError } - -export const ExchangeApiProvider = ({ - baseUrl, - children, - frameOnError, -}: { - baseUrl: string, - children: ComponentChildren; - frameOnError: FunctionComponent<{ children: ComponentChildren }>, -}): VNode => { - const [checked, setChecked] = useState() - const { i18n } = useTranslationContext(); - const url = new URL(baseUrl) - const api = new TalerExchangeHttpClient(url.href, new BrowserFetchHttpLib()) - useEffect(() => { - api.getConfig() - .then((resp) => { - if (resp.type === "fail") { - setChecked({ type: "error", error: TalerError.fromUncheckedDetail(resp.detail) }); - } else 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, - }); -}; - diff --git a/packages/aml-backoffice-ui/src/context/settings.ts b/packages/aml-backoffice-ui/src/context/settings.ts new file mode 100644 index 000000000..6c61a7b4a --- /dev/null +++ b/packages/aml-backoffice-ui/src/context/settings.ts @@ -0,0 +1,44 @@ +/* + This file is part of GNU Taler + (C) 2022-2024 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 + */ + +import { ComponentChildren, createContext, h, VNode } from "preact"; +import { useContext } from "preact/hooks"; +import { UiSettings } from "../settings.js"; + +/** + * + * @author Sebastian Javier Marchano (sebasjm) + */ + +export type Type = UiSettings; + +const initial: UiSettings = {}; +const Context = createContext(initial); + +export const useSettingsContext = (): Type => useContext(Context); + +export const SettingsProvider = ({ + children, + value, +}: { + value: UiSettings; + children: ComponentChildren; +}): VNode => { + return h(Context.Provider, { + value, + children, + }); +}; diff --git a/packages/aml-backoffice-ui/src/declaration.d.ts b/packages/aml-backoffice-ui/src/declaration.d.ts index 663271ec7..7868e41bd 100644 --- a/packages/aml-backoffice-ui/src/declaration.d.ts +++ b/packages/aml-backoffice-ui/src/declaration.d.ts @@ -1,6 +1,6 @@ /* This file is part of GNU Taler - (C) 2022 Taler Systems S.A. + (C) 2022-2024 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 diff --git a/packages/aml-backoffice-ui/src/forms.ts b/packages/aml-backoffice-ui/src/forms.ts index e1fb283d6..3ecec2bb0 100644 --- a/packages/aml-backoffice-ui/src/forms.ts +++ b/packages/aml-backoffice-ui/src/forms.ts @@ -1,6 +1,6 @@ /* This file is part of GNU Taler - (C) 2022 Taler Systems S.A. + (C) 2022-2024 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 diff --git a/packages/aml-backoffice-ui/src/forms/902_11e.ts b/packages/aml-backoffice-ui/src/forms/902_11e.ts index b1cdda6ba..ee4323f77 100644 --- a/packages/aml-backoffice-ui/src/forms/902_11e.ts +++ b/packages/aml-backoffice-ui/src/forms/902_11e.ts @@ -1,6 +1,6 @@ /* This file is part of GNU Taler - (C) 2022 Taler Systems S.A. + (C) 2022-2024 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 diff --git a/packages/aml-backoffice-ui/src/forms/902_12e.ts b/packages/aml-backoffice-ui/src/forms/902_12e.ts index d5a420177..0c14f6ee7 100644 --- a/packages/aml-backoffice-ui/src/forms/902_12e.ts +++ b/packages/aml-backoffice-ui/src/forms/902_12e.ts @@ -1,6 +1,6 @@ /* This file is part of GNU Taler - (C) 2022 Taler Systems S.A. + (C) 2022-2024 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 diff --git a/packages/aml-backoffice-ui/src/forms/902_13e.ts b/packages/aml-backoffice-ui/src/forms/902_13e.ts index 9e05e061a..a4851002e 100644 --- a/packages/aml-backoffice-ui/src/forms/902_13e.ts +++ b/packages/aml-backoffice-ui/src/forms/902_13e.ts @@ -1,6 +1,6 @@ /* This file is part of GNU Taler - (C) 2022 Taler Systems S.A. + (C) 2022-2024 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 diff --git a/packages/aml-backoffice-ui/src/forms/902_15e.ts b/packages/aml-backoffice-ui/src/forms/902_15e.ts index 72acf8d06..915b7dbf7 100644 --- a/packages/aml-backoffice-ui/src/forms/902_15e.ts +++ b/packages/aml-backoffice-ui/src/forms/902_15e.ts @@ -1,6 +1,6 @@ /* This file is part of GNU Taler - (C) 2022 Taler Systems S.A. + (C) 2022-2024 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 diff --git a/packages/aml-backoffice-ui/src/forms/902_1e.ts b/packages/aml-backoffice-ui/src/forms/902_1e.ts index c6d33b9ca..1e7c54f25 100644 --- a/packages/aml-backoffice-ui/src/forms/902_1e.ts +++ b/packages/aml-backoffice-ui/src/forms/902_1e.ts @@ -1,6 +1,6 @@ /* This file is part of GNU Taler - (C) 2022 Taler Systems S.A. + (C) 2022-2024 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 diff --git a/packages/aml-backoffice-ui/src/forms/902_4e.ts b/packages/aml-backoffice-ui/src/forms/902_4e.ts index 15446bb27..46803333b 100644 --- a/packages/aml-backoffice-ui/src/forms/902_4e.ts +++ b/packages/aml-backoffice-ui/src/forms/902_4e.ts @@ -1,6 +1,6 @@ /* This file is part of GNU Taler - (C) 2022 Taler Systems S.A. + (C) 2022-2024 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 diff --git a/packages/aml-backoffice-ui/src/forms/902_5e.ts b/packages/aml-backoffice-ui/src/forms/902_5e.ts index 40dd4d7ad..efe47b213 100644 --- a/packages/aml-backoffice-ui/src/forms/902_5e.ts +++ b/packages/aml-backoffice-ui/src/forms/902_5e.ts @@ -1,6 +1,6 @@ /* This file is part of GNU Taler - (C) 2022 Taler Systems S.A. + (C) 2022-2024 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 diff --git a/packages/aml-backoffice-ui/src/forms/902_9e.ts b/packages/aml-backoffice-ui/src/forms/902_9e.ts index 539e086f5..62fca5647 100644 --- a/packages/aml-backoffice-ui/src/forms/902_9e.ts +++ b/packages/aml-backoffice-ui/src/forms/902_9e.ts @@ -1,6 +1,6 @@ /* This file is part of GNU Taler - (C) 2022 Taler Systems S.A. + (C) 2022-2024 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 diff --git a/packages/aml-backoffice-ui/src/forms/declaration.ts b/packages/aml-backoffice-ui/src/forms/declaration.ts index d944a7a53..fb7b8f334 100644 --- a/packages/aml-backoffice-ui/src/forms/declaration.ts +++ b/packages/aml-backoffice-ui/src/forms/declaration.ts @@ -1,6 +1,6 @@ /* This file is part of GNU Taler - (C) 2022 Taler Systems S.A. + (C) 2022-2024 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 diff --git a/packages/aml-backoffice-ui/src/forms/icons.tsx b/packages/aml-backoffice-ui/src/forms/icons.tsx index 824f75c8f..8bd369c4f 100644 --- a/packages/aml-backoffice-ui/src/forms/icons.tsx +++ b/packages/aml-backoffice-ui/src/forms/icons.tsx @@ -1,6 +1,6 @@ /* This file is part of GNU Taler - (C) 2022 Taler Systems S.A. + (C) 2022-2024 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 diff --git a/packages/aml-backoffice-ui/src/forms/index.ts b/packages/aml-backoffice-ui/src/forms/index.ts index 0a6c1df40..6c5f5d767 100644 --- a/packages/aml-backoffice-ui/src/forms/index.ts +++ b/packages/aml-backoffice-ui/src/forms/index.ts @@ -1,6 +1,6 @@ /* This file is part of GNU Taler - (C) 2022 Taler Systems S.A. + (C) 2022-2024 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 diff --git a/packages/aml-backoffice-ui/src/forms/simplest.ts b/packages/aml-backoffice-ui/src/forms/simplest.ts index 77ed4ebbb..bd512546d 100644 --- a/packages/aml-backoffice-ui/src/forms/simplest.ts +++ b/packages/aml-backoffice-ui/src/forms/simplest.ts @@ -1,6 +1,6 @@ /* This file is part of GNU Taler - (C) 2022 Taler Systems S.A. + (C) 2022-2024 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 diff --git a/packages/aml-backoffice-ui/src/hooks/form.ts b/packages/aml-backoffice-ui/src/hooks/form.ts new file mode 100644 index 000000000..fae11c05c --- /dev/null +++ b/packages/aml-backoffice-ui/src/hooks/form.ts @@ -0,0 +1,124 @@ +/* + This file is part of GNU Taler + (C) 2022-2024 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 + */ + +import { AmountJson, TranslatedString } from "@gnu-taler/taler-util"; +import { useState } from "preact/hooks"; + +export type UIField = { + value: string | undefined; + onUpdate: (s: string) => void; + error: TranslatedString | undefined; +}; + +type FormHandler = { + [k in keyof T]?: T[k] extends string + ? UIField + : T[k] extends AmountJson + ? UIField + : FormHandler; +}; + +export type FormValues = { + [k in keyof T]: T[k] extends string + ? string | undefined + : T[k] extends AmountJson + ? string | undefined + : FormValues; +}; + +export type RecursivePartial = { + [k in keyof T]?: T[k] extends string + ? string + : T[k] extends AmountJson + ? AmountJson + : RecursivePartial; +}; + +export type FormErrors = { + [k in keyof T]?: T[k] extends string + ? TranslatedString + : T[k] extends AmountJson + ? TranslatedString + : FormErrors; +}; + +export type FormStatus = + | { + status: "ok"; + result: T; + errors: undefined; + } + | { + status: "fail"; + result: RecursivePartial; + errors: FormErrors; + }; + +function constructFormHandler( + form: FormValues, + updateForm: (d: FormValues) => void, + errors: FormErrors | undefined, +): FormHandler { + + const keys = Object.keys(form) as Array; + + const handler = keys.reduce((prev, fieldName) => { + const currentValue: unknown = form[fieldName]; + const currentError: unknown = errors ? errors[fieldName] : undefined; + function updater(newValue: unknown) { + updateForm({ ...form, [fieldName]: newValue }); + } + if (typeof currentValue === "object") { + // @ts-expect-error FIXME better typing + const group = constructFormHandler(currentValue, updater, currentError); + // @ts-expect-error FIXME better typing + prev[fieldName] = group; + return prev; + } + const field: UIField = { + // @ts-expect-error FIXME better typing + error: currentError, + // @ts-expect-error FIXME better typing + value: currentValue, + onUpdate: updater, + }; + // @ts-expect-error FIXME better typing + prev[fieldName] = field; + return prev; + }, {} as FormHandler); + + return handler; +} + +/** + * FIXME: Consider sending this to web-utils + * + * + * @param defaultValue + * @param check + * @returns + */ +export function useFormState( + defaultValue: FormValues, + check: (f: FormValues) => FormStatus, +): [FormHandler, FormStatus] { + const [form, updateForm] = useState>(defaultValue); + + const status = check(form); + const handler = constructFormHandler(form, updateForm, status.errors); + + return [handler, status]; +} diff --git a/packages/aml-backoffice-ui/src/hooks/officer.ts b/packages/aml-backoffice-ui/src/hooks/officer.ts new file mode 100644 index 000000000..3ac4c857c --- /dev/null +++ b/packages/aml-backoffice-ui/src/hooks/officer.ts @@ -0,0 +1,159 @@ +/* + This file is part of GNU Taler + (C) 2022-2024 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 + */ +import { + AbsoluteTime, + Codec, + LockedAccount, + OfficerAccount, + OfficerId, + SigningKey, + buildCodecForObject, + codecForAbsoluteTime, + codecForString, + createNewOfficerAccount, + decodeCrock, + encodeCrock, + unlockOfficerAccount, +} from "@gnu-taler/taler-util"; +import { buildStorageKey, useExchangeApiContext, useLocalStorage } from "@gnu-taler/web-util/browser"; +import { useMemo } from "preact/hooks"; +import { usePreferences } from "./preferences.js"; + +export interface Officer { + account: LockedAccount; + when: AbsoluteTime; +} + +const codecForLockedAccount = codecForString() as Codec; + +type OfficerAccountString = { + id: string; + strKey: string; +}; + +export const codecForOfficerAccount = (): Codec => + buildCodecForObject() + .property("id", codecForString()) // FIXME + .property("strKey", codecForString()) // FIXME + .build("OfficerAccount"); + +export const codecForOfficer = (): Codec => + buildCodecForObject() + .property("account", codecForLockedAccount) // FIXME + .property("when", codecForAbsoluteTime) // FIXME + .build("Officer"); + +export type OfficerState = OfficerNotReady | OfficerReady; +export type OfficerNotReady = OfficerNotFound | OfficerLocked; +interface OfficerNotFound { + state: "not-found"; + create: (password: string) => Promise; +} +interface OfficerLocked { + state: "locked"; + forget: () => void; + tryUnlock: (password: string) => Promise; +} +interface OfficerReady { + state: "ready"; + account: OfficerAccount; + forget: () => void; + lock: () => void; +} + +const OFFICER_KEY = buildStorageKey("officer", codecForOfficer()); +const DEV_ACCOUNT_KEY = buildStorageKey( + "account-dev", + codecForOfficerAccount(), +); + +export function useOfficer(): OfficerState { + const exchangeContext = useExchangeApiContext(); + const [pref] = usePreferences(); + pref.keepSessionAfterReload; + // dev account, is kept on reloaded. + const accountStorage = useLocalStorage(DEV_ACCOUNT_KEY); + const account = useMemo(() => { + if (!accountStorage.value) return undefined; + + return { + id: accountStorage.value.id as OfficerId, + signingKey: decodeCrock(accountStorage.value.strKey) as SigningKey, + }; + }, [accountStorage.value?.id, accountStorage.value?.strKey]); + + const officerStorage = useLocalStorage(OFFICER_KEY); + const officer = useMemo(() => { + if (!officerStorage.value) return undefined; + return officerStorage.value; + }, [officerStorage.value?.account, officerStorage.value?.when.t_ms]); + + if (officer === undefined) { + return { + state: "not-found", + create: async (pwd: string) => { + const req = await fetch( + new URL("seed", exchangeContext.lib.exchange.baseUrl).href, + ); + const b = await req.blob(); + const ar = await b.arrayBuffer(); + const uintar = new Uint8Array(ar); + + const { id, safe, signingKey } = await createNewOfficerAccount( + pwd, + uintar, + ); + officerStorage.update({ + account: safe, + when: AbsoluteTime.now(), + }); + + // accountStorage.update({ id, signingKey }); + const strKey = encodeCrock(signingKey); + accountStorage.update({ id, strKey }); + }, + }; + } + + if (account === undefined) { + return { + state: "locked", + forget: () => { + officerStorage.reset(); + }, + tryUnlock: async (pwd: string) => { + const ac = await unlockOfficerAccount(officer.account, pwd); + // accountStorage.update(ac); + accountStorage.update({ + id: ac.id, + strKey: encodeCrock(ac.signingKey), + }); + }, + }; + } + + return { + state: "ready", + account, + lock: () => { + accountStorage.reset(); + }, + forget: () => { + officerStorage.reset(); + accountStorage.reset(); + }, + }; +} diff --git a/packages/aml-backoffice-ui/src/hooks/preferences.ts b/packages/aml-backoffice-ui/src/hooks/preferences.ts new file mode 100644 index 000000000..12e85d249 --- /dev/null +++ b/packages/aml-backoffice-ui/src/hooks/preferences.ts @@ -0,0 +1,85 @@ +/* + This file is part of GNU Taler + (C) 2022-2024 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 + */ + +import { + Codec, + TranslatedString, + buildCodecForObject, + codecForBoolean +} from "@gnu-taler/taler-util"; +import { + buildStorageKey, + useLocalStorage, + useTranslationContext, +} from "@gnu-taler/web-util/browser"; + +interface Preferences { + allowInsecurePassword: boolean; + keepSessionAfterReload: boolean; +} + +export const codecForPreferences = (): Codec => + buildCodecForObject() + .property("allowInsecurePassword", (codecForBoolean())) + .property("keepSessionAfterReload", (codecForBoolean())) + .build("Preferences"); + +const defaultPreferences: Preferences = { + allowInsecurePassword: false, + keepSessionAfterReload: false, +}; + +const PREFERENCES_KEY = buildStorageKey( + "exchange-preferences", + codecForPreferences(), +); +/** + * User preferences. + * + * @returns tuple of [state, update()] + */ +export function usePreferences(): [ + Readonly, + (key: T, value: Preferences[T]) => void, +] { + const { value, update } = useLocalStorage( + PREFERENCES_KEY, + defaultPreferences, + ); + + function updateField(k: T, v: Preferences[T]) { + const newValue = { ...value, [k]: v }; + update(newValue); + } + return [value, updateField]; +} + +export function getAllBooleanPreferences(): Array { + return [ + "allowInsecurePassword", + "keepSessionAfterReload", + ]; +} + +export function getLabelForPreferences( + k: keyof Preferences, + i18n: ReturnType["i18n"], +): TranslatedString { + switch (k) { + case "allowInsecurePassword": return i18n.str`Allow Insecure password` + case "keepSessionAfterReload": return i18n.str`Keep session after reload` + } +} diff --git a/packages/aml-backoffice-ui/src/hooks/useBackend.ts b/packages/aml-backoffice-ui/src/hooks/useBackend.ts deleted file mode 100644 index 310f7fe59..000000000 --- a/packages/aml-backoffice-ui/src/hooks/useBackend.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 - */ - import { canonicalizeBaseUrl } from "@gnu-taler/taler-util"; -import { uiSettings } from "../settings.js"; - - -export function getInitialBackendBaseURL(): string { - const overrideUrl = - typeof localStorage !== "undefined" - ? localStorage.getItem("exchange-base-url") - : undefined; - - let result: string; - - if (!overrideUrl) { - //normal path - if (!uiSettings.backendBaseURL) { - console.error( - "ERROR: backendBaseURL was overridden by a setting file and missing. Setting value to 'window.origin'", - ); - result = typeof window !== "undefined" ? window.origin : "localhost" - } else { - result = uiSettings.backendBaseURL; - } - } else { - // testing/development path - result = overrideUrl - } - try { - return canonicalizeBaseUrl(result) - } catch (e) { - //fall back - return canonicalizeBaseUrl(window.origin) - } -} diff --git a/packages/aml-backoffice-ui/src/hooks/useCaseDetails.ts b/packages/aml-backoffice-ui/src/hooks/useCaseDetails.ts index de1c5af17..78574ada4 100644 --- a/packages/aml-backoffice-ui/src/hooks/useCaseDetails.ts +++ b/packages/aml-backoffice-ui/src/hooks/useCaseDetails.ts @@ -1,6 +1,6 @@ /* This file is part of GNU Taler - (C) 2022 Taler Systems S.A. + (C) 2022-2024 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 @@ -16,15 +16,15 @@ import { OfficerAccount, PaytoString, TalerExchangeResultByMethod, TalerHttpError } from "@gnu-taler/taler-util"; // FIX default import https://github.com/microsoft/TypeScript/issues/49189 import _useSWR, { SWRHook } from "swr"; -import { useExchangeApiContext } from "../context/config.js"; -import { useOfficer } from "./useOfficer.js"; +import { useOfficer } from "./officer.js"; +import { useExchangeApiContext } from "@gnu-taler/web-util/browser"; const useSWR = _useSWR as unknown as SWRHook; export function useCaseDetails(paytoHash: string) { const officer = useOfficer(); const session = officer.state === "ready" ? officer.account : undefined; - const { api } = useExchangeApiContext(); + const { lib: {exchange: api} } = useExchangeApiContext(); async function fetcher([officer, account]: [OfficerAccount, PaytoString]) { return await api.getDecisionDetails(officer, account) diff --git a/packages/aml-backoffice-ui/src/hooks/useCases.ts b/packages/aml-backoffice-ui/src/hooks/useCases.ts index 7c8bb5bc1..59d1c9001 100644 --- a/packages/aml-backoffice-ui/src/hooks/useCases.ts +++ b/packages/aml-backoffice-ui/src/hooks/useCases.ts @@ -1,6 +1,6 @@ /* This file is part of GNU Taler - (C) 2022 Taler Systems S.A. + (C) 2022-2024 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 @@ -16,11 +16,16 @@ import { useState } from "preact/hooks"; // FIX default import https://github.com/microsoft/TypeScript/issues/49189 -import { OfficerAccount, OperationOk, TalerExchangeResultByMethod, TalerHttpError } from "@gnu-taler/taler-util"; +import { + OfficerAccount, + OperationOk, + TalerExchangeResultByMethod, + TalerHttpError, +} from "@gnu-taler/taler-util"; import _useSWR, { SWRHook } from "swr"; -import { useExchangeApiContext } from "../context/config.js"; import { AmlExchangeBackend } from "../utils/types.js"; -import { useOfficer } from "./useOfficer.js"; +import { useOfficer } from "./officer.js"; +import { useExchangeApiContext } from "@gnu-taler/web-util/browser"; const useSWR = _useSWR as unknown as SWRHook; export const PAGINATED_LIST_SIZE = 10; @@ -37,17 +42,28 @@ export const PAGINATED_LIST_REQUEST = PAGINATED_LIST_SIZE + 1; export function useCases(state: AmlExchangeBackend.AmlState) { const officer = useOfficer(); const session = officer.state === "ready" ? officer.account : undefined; - const { api } = useExchangeApiContext(); + const { + lib: { exchange: api }, + } = useExchangeApiContext(); const [offset, setOffset] = useState(); - async function fetcher([officer, state, offset]: [OfficerAccount, AmlExchangeBackend.AmlState, string | undefined]) { + async function fetcher([officer, state, offset]: [ + OfficerAccount, + AmlExchangeBackend.AmlState, + string | undefined, + ]) { return await api.getDecisionsByState(officer, state, { - order: "asc", offset, limit: PAGINATED_LIST_REQUEST - }) + order: "asc", + offset, + limit: PAGINATED_LIST_REQUEST, + }); } - const { data, error } = useSWR, TalerHttpError>( + const { data, error } = useSWR< + TalerExchangeResultByMethod<"getDecisionsByState">, + TalerHttpError + >( !session ? undefined : [session, state, offset, "getDecisionsByState"], fetcher, ); @@ -56,7 +72,9 @@ export function useCases(state: AmlExchangeBackend.AmlState) { if (data === undefined) return undefined; if (data.type !== "ok") return data; - return buildPaginatedResult(data.body.records, offset, setOffset, (d) => String(d.rowid)); + return buildPaginatedResult(data.body.records, offset, setOffset, (d) => + String(d.rowid), + ); } type PaginatedResult = OperationOk & { @@ -64,11 +82,15 @@ type PaginatedResult = OperationOk & { isFirstPage: boolean; loadNext(): void; loadFirst(): void; -} +}; //TODO: consider sending this to web-util -export function buildPaginatedResult(data: R[], offset: OffId | undefined, setOffset: (o: OffId | undefined) => void, getId: (r: R) => OffId): PaginatedResult { - +export function buildPaginatedResult( + data: R[], + offset: OffId | undefined, + setOffset: (o: OffId | undefined) => void, + getId: (r: R) => OffId, +): PaginatedResult { const isLastPage = data.length < PAGINATED_LIST_REQUEST; const isFirstPage = offset === undefined; @@ -83,7 +105,7 @@ export function buildPaginatedResult(data: R[], offset: OffId | undefi isFirstPage, loadNext: () => { if (!result.length) return; - const id = getId(result[result.length - 1]) + const id = getId(result[result.length - 1]); setOffset(id); }, loadFirst: () => { diff --git a/packages/aml-backoffice-ui/src/hooks/useOfficer.ts b/packages/aml-backoffice-ui/src/hooks/useOfficer.ts deleted file mode 100644 index b49c9db26..000000000 --- a/packages/aml-backoffice-ui/src/hooks/useOfficer.ts +++ /dev/null @@ -1,150 +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 - */ -import { - AbsoluteTime, - Codec, - LockedAccount, - OfficerAccount, - OfficerId, - SigningKey, - buildCodecForObject, - codecForAbsoluteTime, - codecForString, - createNewOfficerAccount, - decodeCrock, - encodeCrock, - unlockOfficerAccount -} from "@gnu-taler/taler-util"; -import { - buildStorageKey, - useLocalStorage -} from "@gnu-taler/web-util/browser"; -import { useMemo } from "preact/hooks"; -import { useMaybeExchangeApiContext } from "../context/config.js"; - -export interface Officer { - account: LockedAccount; - when: AbsoluteTime; -} - -const codecForLockedAccount = codecForString() as Codec; - -type OfficerAccountString = { - id: string, - strKey: string; -} - -export const codecForOfficerAccount = (): Codec => - buildCodecForObject() - .property("id", codecForString()) // FIXME - .property("strKey", codecForString()) // FIXME - .build("OfficerAccount"); - -export const codecForOfficer = (): Codec => - buildCodecForObject() - .property("account", codecForLockedAccount) // FIXME - .property("when", codecForAbsoluteTime) // FIXME - .build("Officer"); - -export type OfficerState = OfficerNotReady | OfficerReady; -export type OfficerNotReady = OfficerNotFound | OfficerLocked; -interface OfficerNotFound { - state: "not-found"; - create: (password: string) => Promise; -} -interface OfficerLocked { - state: "locked"; - forget: () => void; - tryUnlock: (password: string) => Promise; -} -interface OfficerReady { - state: "ready"; - account: OfficerAccount; - forget: () => void; - lock: () => void; -} - -const OFFICER_KEY = buildStorageKey("officer", codecForOfficer()); -const DEV_ACCOUNT_KEY = buildStorageKey("account-dev", codecForOfficerAccount()); - -export function useOfficer(): OfficerState { - const exchangeContext = useMaybeExchangeApiContext(); - // dev account, is save when reloaded. - const accountStorage = useLocalStorage(DEV_ACCOUNT_KEY); - const account = useMemo(() => { - if (!accountStorage.value) return undefined - - return { - id: accountStorage.value.id as OfficerId, - signingKey: decodeCrock(accountStorage.value.strKey) as SigningKey - } - }, [accountStorage.value?.id, accountStorage.value?.strKey]) - - const officerStorage = useLocalStorage(OFFICER_KEY); - const officer = useMemo(() => { - if (!officerStorage.value) return undefined - return officerStorage.value - }, [officerStorage.value?.account, officerStorage.value?.when.t_ms]) - - if (officer === undefined) { - return { - state: "not-found", - create: async (pwd: string) => { - if (!exchangeContext) return; - const req = await fetch(new URL("seed", exchangeContext.api.baseUrl).href) - const b = await req.blob() - const ar = await b.arrayBuffer() - const uintar = new Uint8Array(ar) - - const { id, safe, signingKey } = await createNewOfficerAccount(pwd, uintar); - officerStorage.update({ - account: safe, - when: AbsoluteTime.now(), - }); - - // accountStorage.update({ id, signingKey }); - const strKey = encodeCrock(signingKey) - accountStorage.update({ id, strKey }) - }, - }; - } - - if (account === undefined) { - return { - state: "locked", - forget: () => { - officerStorage.reset(); - }, - tryUnlock: async (pwd: string) => { - const ac = await unlockOfficerAccount(officer.account, pwd); - // accountStorage.update(ac); - accountStorage.update({ id: ac.id, strKey: encodeCrock(ac.signingKey) }) - }, - }; - } - - return { - state: "ready", - account, - lock: () => { - accountStorage.reset(); - }, - forget: () => { - officerStorage.reset(); - accountStorage.reset(); - }, - }; -} diff --git a/packages/aml-backoffice-ui/src/hooks/useSettings.ts b/packages/aml-backoffice-ui/src/hooks/useSettings.ts deleted file mode 100644 index 55cdafb23..000000000 --- a/packages/aml-backoffice-ui/src/hooks/useSettings.ts +++ /dev/null @@ -1,71 +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 - */ - -import { - Codec, - TranslatedString, - buildCodecForObject, - codecForBoolean -} from "@gnu-taler/taler-util"; -import { buildStorageKey, useLocalStorage, useTranslationContext } from "@gnu-taler/web-util/browser"; - -interface Settings { - allowInsecurePassword: boolean; - keepSessionAfterReload: boolean; -} - -export function getAllBooleanSettings(): Array { - return ["allowInsecurePassword", "keepSessionAfterReload"] -} - -export function getLabelForSetting(k: keyof Settings, i18n: ReturnType["i18n"]): TranslatedString { - switch (k) { - case "allowInsecurePassword": return i18n.str`Allow Insecure password` - case "keepSessionAfterReload": return i18n.str`Keep session after reload` - } -} - -export const codecForSettings = (): Codec => - buildCodecForObject() - .property("allowInsecurePassword", (codecForBoolean())) - .property("keepSessionAfterReload", (codecForBoolean())) - .build("Settings"); - -const defaultSettings: Settings = { - allowInsecurePassword: false, - keepSessionAfterReload: false, -}; - -const EXCHANGE_SETTINGS_KEY = buildStorageKey( - "exchange-settings", - codecForSettings(), -); - -export function useSettings(): [ - Readonly, - (key: T, value: Settings[T]) => void, -] { - const { value, update } = useLocalStorage( - EXCHANGE_SETTINGS_KEY, - defaultSettings, - ); - - function updateField(k: T, v: Settings[T]) { - const newValue = { ...value, [k]: v }; - update(newValue); - } - return [value, updateField]; -} diff --git a/packages/aml-backoffice-ui/src/i18n/bank.pot b/packages/aml-backoffice-ui/src/i18n/bank.pot index 66e98976f..39f9de5ce 100644 --- a/packages/aml-backoffice-ui/src/i18n/bank.pot +++ b/packages/aml-backoffice-ui/src/i18n/bank.pot @@ -1,5 +1,5 @@ # This file is part of GNU Taler -# (C) 2022 Taler Systems S.A. +# (C) 2022-2024 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 diff --git a/packages/aml-backoffice-ui/src/i18n/fr.po b/packages/aml-backoffice-ui/src/i18n/fr.po index 203d55343..8148f6a0c 100644 --- a/packages/aml-backoffice-ui/src/i18n/fr.po +++ b/packages/aml-backoffice-ui/src/i18n/fr.po @@ -1,5 +1,5 @@ # This file is part of GNU Taler -# (C) 2022 Taler Systems S.A. +# (C) 2022-2024 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 diff --git a/packages/aml-backoffice-ui/src/i18n/poheader b/packages/aml-backoffice-ui/src/i18n/poheader index a251e9584..d7a371934 100644 --- a/packages/aml-backoffice-ui/src/i18n/poheader +++ b/packages/aml-backoffice-ui/src/i18n/poheader @@ -1,5 +1,5 @@ # This file is part of GNU Taler -# (C) 2022 Taler Systems S.A. +# (C) 2022-2024 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 diff --git a/packages/aml-backoffice-ui/src/i18n/strings-prelude b/packages/aml-backoffice-ui/src/i18n/strings-prelude index a0aeb8268..3ab0fd1e5 100644 --- a/packages/aml-backoffice-ui/src/i18n/strings-prelude +++ b/packages/aml-backoffice-ui/src/i18n/strings-prelude @@ -1,6 +1,6 @@ /* This file is part of GNU Taler - (C) 2022 Taler Systems S.A. + (C) 2022-2024 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 diff --git a/packages/aml-backoffice-ui/src/i18n/strings.ts b/packages/aml-backoffice-ui/src/i18n/strings.ts index a779bbc49..4f7419eb4 100644 --- a/packages/aml-backoffice-ui/src/i18n/strings.ts +++ b/packages/aml-backoffice-ui/src/i18n/strings.ts @@ -1,6 +1,6 @@ /* This file is part of GNU Taler - (C) 2022 Taler Systems S.A. + (C) 2022-2024 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 diff --git a/packages/aml-backoffice-ui/src/index.html b/packages/aml-backoffice-ui/src/index.html index c1de73520..b7f73d0a2 100644 --- a/packages/aml-backoffice-ui/src/index.html +++ b/packages/aml-backoffice-ui/src/index.html @@ -1,6 +1,6 @@