diff options
Diffstat (limited to 'packages/auditor-backoffice-ui')
45 files changed, 1774 insertions, 3913 deletions
diff --git a/packages/auditor-backoffice-ui/package.json b/packages/auditor-backoffice-ui/package.json index bbebabf39..78a09b1fd 100644 --- a/packages/auditor-backoffice-ui/package.json +++ b/packages/auditor-backoffice-ui/package.json @@ -1,15 +1,14 @@ { "private": true, "name": "@gnu-taler/auditor-backoffice-ui", - "version": "0.12.2", + "version": "0.13.5", "license": "AGPL-3.0-or-later", "type": "module", "scripts": { "clean": "rm -rf dist lib tsconfig.tsbuildinfo", - "build": "./build.mjs", "check": "tsc", "compile": "tsc && ./build.mjs", - "dev": "preact watch --port ${PORT:=8080} --no-sw --no-esm", + "dev": "./dev.mjs", "test": "./test.mjs && mocha --require source-map-support/register 'dist/**/*.test.js' 'dist/**/test.js'", "lint": "eslint 'src/**/*.{js,jsx,ts,tsx}'", "i18n:extract": "pogen extract", diff --git a/packages/auditor-backoffice-ui/src/Application.tsx b/packages/auditor-backoffice-ui/src/Application.tsx index 3b6aa8dd3..4188a53a6 100644 --- a/packages/auditor-backoffice-ui/src/Application.tsx +++ b/packages/auditor-backoffice-ui/src/Application.tsx @@ -20,35 +20,33 @@ * @author Nic Eigel */ -import {HttpStatusCode, LibtoolVersion} from "@gnu-taler/taler-util"; +import { HttpStatusCode, LibtoolVersion } from "@gnu-taler/taler-util"; import { - ErrorType, - TranslationProvider, - useTranslationContext + ErrorType, + TranslationProvider, + useTranslationContext, } from "@gnu-taler/web-util/browser"; -import {Fragment, VNode, h, render} from "preact"; -import {useMemo} from "preact/hooks"; -import {ApplicationReadyRoutes} from "./ApplicationReadyRoutes.js"; -import {Loading} from "./components/exception/loading.js"; +import { Fragment, VNode, h } from "preact"; +import { useMemo } from "preact/hooks"; +import { ApplicationReadyRoutes } from "./ApplicationReadyRoutes.js"; +import { Loading } from "./components/exception/loading.js"; import { - NotConnectedAppMenu, - NotificationCard + NotConnectedAppMenu, + NotificationCard, } from "./components/menu/index.js"; -import { - BackendContextProvider -} from "./context/backend.js"; -import {ConfigContextProvider} from "./context/config.js"; -import {useBackendConfig} from "./hooks/backend.js"; +import { BackendContextProvider } from "./context/backend.js"; +import { ConfigContextProvider } from "./context/config.js"; +import { useBackendConfig } from "./hooks/backend.js"; import { strings } from "./i18n/strings.js"; export function Application(): VNode { - return ( - <BackendContextProvider> - <TranslationProvider source={strings}> - <ApplicationStatusRoutes/> - </TranslationProvider> - </BackendContextProvider> - ); + return ( + <BackendContextProvider> + <TranslationProvider source={strings}> + <ApplicationStatusRoutes /> + </TranslationProvider> + </BackendContextProvider> + ); } /** @@ -57,110 +55,110 @@ export function Application(): VNode { * @returns */ function ApplicationStatusRoutes(): VNode { - const result = useBackendConfig(); - const {i18n} = useTranslationContext(); + const result = useBackendConfig(); + const { i18n } = useTranslationContext(); - const configData = result.ok && result.data - ? result.data - : undefined; - const ctx = useMemo(() => (configData), [configData]); + const configData = result.ok && result.data ? result.data : undefined; + const ctx = useMemo(() => configData, [configData]); - if (!result.ok) { - if (result.loading) return <Loading/>; - if ( - result.type === ErrorType.CLIENT && - result.status === HttpStatusCode.Unauthorized - ) { - return ( - <Fragment> - <NotConnectedAppMenu title="Login"/> - <NotificationCard - notification={{ - message: i18n.str`Checking the /config endpoint got authorization error`, - type: "ERROR", - description: `The /config endpoint of the backend server should be accessible`, - }} - /> - </Fragment> - ); - } - if ( - result.type === ErrorType.CLIENT && - result.status === HttpStatusCode.NotFound - ) { - return ( - <Fragment> - <NotConnectedAppMenu title="Error"/> - <NotificationCard - notification={{ - message: i18n.str`Could not find /config endpoint on this URL`, - type: "ERROR", - description: `Check the URL or contact the system administrator.`, - }} - /> - </Fragment> - ); - } - if (result.type === ErrorType.SERVER) { - <Fragment> - <NotConnectedAppMenu title="Error"/> - <NotificationCard - notification={{ - message: i18n.str`Server response with an error code`, - type: "ERROR", - description: i18n.str`Got message "${result.message}" from ${result.info?.url}`, - }} - /> - </Fragment>; - } - if (result.type === ErrorType.UNREADABLE) { - <Fragment> - <NotConnectedAppMenu title="Error"/> - <NotificationCard - notification={{ - message: i18n.str`Response from server is unreadable, http status: ${result.status}`, - type: "ERROR", - description: i18n.str`Got message "${result.message}" from ${result.info?.url}`, - }} - /> - </Fragment>; - } - return ( - <Fragment> - <NotConnectedAppMenu title="Error"/> - <NotificationCard - notification={{ - message: i18n.str`Unexpected Error`, - type: "ERROR", - description: i18n.str`Got message "${result.message}" from ${result.info?.url}`, - }} - /> - </Fragment> - ); + if (!result.ok) { + if (result.loading) return <Loading />; + if ( + result.type === ErrorType.CLIENT && + result.status === HttpStatusCode.Unauthorized + ) { + return ( + <Fragment> + <NotConnectedAppMenu title="Login" /> + <NotificationCard + notification={{ + message: i18n.str`Checking the /config endpoint got authorization error`, + type: "ERROR", + description: `The /config endpoint of the backend server should be accessible`, + }} + /> + </Fragment> + ); } - - const SUPPORTED_VERSION = "1:0:1" - if (result.data && !LibtoolVersion.compare( - SUPPORTED_VERSION, - result.data.version, - )?.compatible) { - return <Fragment> - <NotConnectedAppMenu title="Error"/> - <NotificationCard - notification={{ - message: i18n.str`Incompatible version`, - type: "ERROR", - description: i18n.str`Auditor backend server version ${result.data.version} is not compatible with the supported version ${SUPPORTED_VERSION}`, - }} - /> + if ( + result.type === ErrorType.CLIENT && + result.status === HttpStatusCode.NotFound + ) { + return ( + <Fragment> + <NotConnectedAppMenu title="Error" /> + <NotificationCard + notification={{ + message: i18n.str`Could not find /config endpoint on this URL`, + type: "ERROR", + description: `Check the URL or contact the system administrator.`, + }} + /> </Fragment> + ); + } + if (result.type === ErrorType.SERVER) { + <Fragment> + <NotConnectedAppMenu title="Error" /> + <NotificationCard + notification={{ + message: i18n.str`Server response with an error code`, + type: "ERROR", + description: i18n.str`Got message "${result.message}" from ${result.info?.url}`, + }} + /> + </Fragment>; } + if (result.type === ErrorType.UNREADABLE) { + <Fragment> + <NotConnectedAppMenu title="Error" /> + <NotificationCard + notification={{ + message: i18n.str`Response from server is unreadable, http status: ${result.status}`, + type: "ERROR", + description: i18n.str`Got message "${result.message}" from ${result.info?.url}`, + }} + /> + </Fragment>; + } + return ( + <Fragment> + <NotConnectedAppMenu title="Error" /> + <NotificationCard + notification={{ + message: i18n.str`Unexpected Error`, + type: "ERROR", + description: i18n.str`Got message "${result.message}" from ${result.info?.url}`, + }} + /> + </Fragment> + ); + } + const SUPPORTED_VERSION = "1:0:1"; + if ( + result.data && + !LibtoolVersion.compare(SUPPORTED_VERSION, result.data.version)?.compatible + ) { return ( - <div class="has-navbar-fixed-top"> - <ConfigContextProvider value={ctx!}> - <ApplicationReadyRoutes/> - </ConfigContextProvider> - </div> + <Fragment> + <NotConnectedAppMenu title="Error" /> + <NotificationCard + notification={{ + message: i18n.str`Incompatible version`, + type: "ERROR", + description: i18n.str`Auditor backend server version ${result.data.version} is not compatible with the supported version ${SUPPORTED_VERSION}`, + }} + /> + </Fragment> ); -}
\ No newline at end of file + } + + return ( + <div class="has-navbar-fixed-top"> + <ConfigContextProvider value={ctx!}> + <ApplicationReadyRoutes /> + </ConfigContextProvider> + </div> + ); +} diff --git a/packages/auditor-backoffice-ui/src/ApplicationReadyRoutes.tsx b/packages/auditor-backoffice-ui/src/ApplicationReadyRoutes.tsx index 576792d6f..9e0bda499 100644 --- a/packages/auditor-backoffice-ui/src/ApplicationReadyRoutes.tsx +++ b/packages/auditor-backoffice-ui/src/ApplicationReadyRoutes.tsx @@ -19,71 +19,52 @@ * @author Sebastian Javier Marchano (sebasjm) * @author Nic Eigel */ -import {ErrorType, 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, useErrorBoundary, useState} from "preact/hooks"; -import {InstanceRoutes} from "./InstanceRoutes.js"; -import { - NotConnectedAppMenu, - NotYetReadyAppMenu, - NotificationCard, -} from "./components/menu/index.js"; -import { useBackendContext, useBackendTokenContext } from "./context/backend.js"; -import {Settings} from "./paths/settings/index.js"; -import { useBackendConfig, useBackendToken } from "./hooks/backend.js"; +import { createHashHistory } from "history"; +import { Fragment, VNode, h } from "preact"; +import { Route, Router } from "preact-router"; +import { useState } from "preact/hooks"; +import { InstanceRoutes } from "./InstanceRoutes.js"; import { Loading } from "./components/exception/loading.js"; +import { NotYetReadyAppMenu } from "./components/menu/index.js"; +import { useBackendToken } from "./hooks/backend.js"; import { LoginPage } from "./paths/login/index.js"; +import { Settings } from "./paths/settings/index.js"; /** * Check if admin against /management/instances * @returns */ export function ApplicationReadyRoutes(): VNode { - const {i18n} = useTranslationContext(); - const [unauthorized, setUnauthorized] = useState(false) - const [backendToken, setToken] = useState(false) - const { url: backendURL} = useBackendContext(); - const { token } = useBackendTokenContext(); + //TODO FIX bearer + const result = useBackendToken(); + if (result.loading) return <Loading />; + if (!result.ok) { + return <LoginPage />; + } + const [showSettings, setShowSettings] = useState(false); - //TODO FIX bearer - const result = useBackendToken(); - if (result.loading) return <Loading/>; - if (!result.ok) { - return ( - <LoginPage /> - ); - } - const [showSettings, setShowSettings] = useState(false) - - if (showSettings) { - return <Fragment> - <NotYetReadyAppMenu onShowSettings={() => setShowSettings(true)} title="UI Settings"/> - <Settings onClose={() => setShowSettings(false)}/> - </Fragment> - } - - const history = createHashHistory(); + if (showSettings) { return ( - <Router history={history}> - <Route - default - component={DefaultMainRoute} - /> - </Router> + <Fragment> + <NotYetReadyAppMenu + onShowSettings={() => setShowSettings(true)} + title="UI Settings" + /> + <Settings onClose={() => setShowSettings(false)} /> + </Fragment> ); + } + + const history = createHashHistory(); + return ( + <Router history={history}> + <Route default component={DefaultMainRoute} /> + </Router> + ); } function DefaultMainRoute({ - url, //from preact-router - }: any): VNode { - //TODO - url = "app/#" + url; - - return ( - <InstanceRoutes - path={url} - /> - ); + url, //from preact-router +}: any): VNode { + return <InstanceRoutes path={url} />; } diff --git a/packages/auditor-backoffice-ui/src/InstanceRoutes.tsx b/packages/auditor-backoffice-ui/src/InstanceRoutes.tsx index 83c1c9f4d..ab3f3dde3 100644 --- a/packages/auditor-backoffice-ui/src/InstanceRoutes.tsx +++ b/packages/auditor-backoffice-ui/src/InstanceRoutes.tsx @@ -20,202 +20,275 @@ * @author Nicola Eigel */ -import {TranslatedString} from "@gnu-taler/taler-util"; -import { - useTranslationContext, -} from "@gnu-taler/web-util/browser"; -import {VNode, h} from "preact"; -import {Route, Router, route} from "preact-router"; -import {useEffect, useErrorBoundary, useMemo, useState} from "preact/hooks"; -import {Menu, NotificationCard} from "./components/menu/index.js"; -import {EntityContextProvider} from "./context/entity.js"; -import {Notification} from "./utils/types.js"; -import NotFoundPage from "./paths/notfound/index.js"; -import {Settings} from "./paths/settings/index.js"; +import { TranslatedString } from "@gnu-taler/taler-util"; +import { VNode, h } from "preact"; +import { Route, Router, route } from "preact-router"; +import { useEffect, useErrorBoundary, useMemo, useState } from "preact/hooks"; +import { Menu, NotificationCard } from "./components/menu/index.js"; +import { EntityContextProvider } from "./context/entity.js"; +import { AuditorBackend } from "./declaration.js"; import DefaultList from "./paths/default/index.js"; -import { - AuditorBackend, -} from "./declaration.js"; -import FinanceDashboard from "./paths/finance/index.js"; import DetailsDashboard from "./paths/details/index.js"; +import FinanceDashboard from "./paths/finance/index.js"; +import NotFoundPage from "./paths/notfound/index.js"; import OperationsDashboard from "./paths/operations/index.js"; import SecurityDashboard from "./paths/security/index.js"; +import { Settings } from "./paths/settings/index.js"; +import { Notification } from "./utils/types.js"; export enum Paths { - error = "/error", - settings = "/settings", + error = "/error", + settings = "/settings", - key_figures = "/key-figures", - critical_errors = "/critical-errors", - operating_status = "/operating-status", - detail_view = "/detail-view", + key_figures = "/key-figures", + critical_errors = "/critical-errors", + operating_status = "/operating-status", + detail_view = "/detail-view", - amount_arithmethic_inconsistency_list = "/amount-arithmetic-inconsistencies", + amount_arithmethic_inconsistency_list = "/amount-arithmetic-inconsistencies", - bad_sig_losses_list = "/bad-sig-losses", + bad_sig_losses_list = "/bad-sig-losses", - balance_list = "/balance", + balance_list = "/balance", - closure_lag_list = "/closure-lags", + closure_lag_list = "/closure-lags", - coin_inconsistency_list = "/coin-inconsistencies", + coin_inconsistency_list = "/coin-inconsistencies", - denomination_key_validity_withdraw_inconsistency_list = "/denomination-key-validity-withdraw-inconsistencies", + denomination_key_validity_withdraw_inconsistency_list = "/denomination-key-validity-withdraw-inconsistencies", - denomination_pending_list = "/denominations-pending", + denomination_pending_list = "/denominations-pending", - denomination_without_sig_list = "/denominations-without-sig", + denomination_without_sig_list = "/denominations-without-sig", - deposit_confirmation_list = "/deposit-confirmations", - deposit_confirmation_update = "/deposit-confirmation/:rowid/update", + deposit_confirmation_list = "/deposit-confirmations", + deposit_confirmation_update = "/deposit-confirmation/:rowid/update", - emergency_list = "/emergencies", + emergency_list = "/emergencies", - emergency_by_count_list = "/emergencies-by-count", + emergency_by_count_list = "/emergencies-by-count", - exchange_signkey_list = "/exchange-sign-keys", + exchange_signkey_list = "/exchange-sign-keys", - fee_time_inconsistency_list = "/fee-time-inconsistencies", + fee_time_inconsistency_list = "/fee-time-inconsistencies", - historic_denomination_revenue_list = "/historic-denomination-revenues", + historic_denomination_revenue_list = "/historic-denomination-revenues", - misattribution_in_inconsistency_list = "/misattribution-in-inconsistencies", + misattribution_in_inconsistency_list = "/misattribution-in-inconsistencies", - progress_list = "/progress", + progress_list = "/progress", - purse_not_closed_inconsistency_list = "/purse-not-closed-inconsistencies", + purse_not_closed_inconsistency_list = "/purse-not-closed-inconsistencies", - purse_list = "/purses", + purse_list = "/purses", - refresh_hanging_list = "/refreshes-hanging", + refresh_hanging_list = "/refreshes-hanging", - reserve_balance_insufficient_inconsistency_list = "/reserve-balance-insufficient-inconsistencies", + reserve_balance_insufficient_inconsistency_list = "/reserve-balance-insufficient-inconsistencies", - reserve_balance_summary_wrong_inconsistency_list = "/reserve-balance-summary-wrong-inconsistencies", + reserve_balance_summary_wrong_inconsistency_list = "/reserve-balance-summary-wrong-inconsistencies", - reserve_in_inconsistency_list = "/reserve-in-inconsistencies", + reserve_in_inconsistency_list = "/reserve-in-inconsistencies", - reserve_not_closed_inconsistency_list = "/reserve-not-closed-inconsistencies", + reserve_not_closed_inconsistency_list = "/reserve-not-closed-inconsistencies", - reserves_list = "/reserves", + reserves_list = "/reserves", - row_inconsistency_list = "/row-inconsistencies", + row_inconsistency_list = "/row-inconsistencies", - row_minor_inconsistency_list = "/row-minor-inconsistencies", + row_minor_inconsistency_list = "/row-minor-inconsistencies", - wire_format_inconsistency_list = "/wire-format-inconsistencies", + wire_format_inconsistency_list = "/wire-format-inconsistencies", - wire_out_inconsistency_list = "/wire-out-inconsistencies" + wire_out_inconsistency_list = "/wire-out-inconsistencies", } interface TestProps { - title: string; - endpoint: string; - entity: any; + title: string; + endpoint: string; + entity: any; } function getInstanceTitle(path: string): TestProps { - switch (path) { - case Paths.key_figures: - return {title: `Key figures`, endpoint: "helper", entity: null}; - case Paths.critical_errors: - return {title: `Critical errors`, endpoint: "helper", entity: null}; - case Paths.operating_status: - return {title: `Operating status`, endpoint: "helper", entity: null}; - case Paths.detail_view: - return {title: `Inconsistencies`, endpoint: "helper", entity: null}; - case Paths.amount_arithmethic_inconsistency_list: - let amountArithmeticInconsistency: AuditorBackend.AmountArithmeticInconsistency.ClassAmountArithmeticInconsistency = {} as AuditorBackend.AmountArithmeticInconsistency.ClassAmountArithmeticInconsistency; - return { - title: `Amount arithmetic inconsistencies`, - endpoint: "amount-arithmetic-inconsistency", - entity: amountArithmeticInconsistency - }; - case Paths.bad_sig_losses_list: - return {title: `Bad Sig Losses`, endpoint: "bad-sig-losses", entity: null}; - case Paths.balance_list: - return {title: "Balances", endpoint: "balances", entity: null}; - case Paths.closure_lag_list: - return {title: `Closure Lags`, endpoint: "closure-lags", entity: null}; - case Paths.coin_inconsistency_list: - return {title: `Coin inconsistencies`, endpoint: "coin-inconsistency", entity: null}; - case Paths.denomination_key_validity_withdraw_inconsistency_list: - return {title: `Denomination key validity withdraw inconsistency`, endpoint: "denomination-key-validity-withdraw-inconsistency", entity: null}; - case Paths.denomination_pending_list: - return {title: `Denominations pending`, endpoint: "denomination-pending", entity: null}; - case Paths.denomination_without_sig_list: - return {title: `Denominations without sigs`, endpoint: "denominations-without-sigs", entity: null}; - case Paths.deposit_confirmation_list: - return {title: "Deposit Confirmations", endpoint: "deposit-confirmation", entity: null}; - case Paths.emergency_list: - return {title: "Emergencies", endpoint: "emergency", entity: null}; - case Paths.emergency_by_count_list: - return {title: "Emergencies by count", endpoint: "emergency-by-count", entity: null}; - case Paths.fee_time_inconsistency_list: - return {title: "Fee time inconsistencies", endpoint: "fee-time-inconsistency", entity: null}; - case Paths.historic_denomination_revenue_list: - return {title: "Historic denomination revenue", endpoint: "historic-denomination-revenue", entity: null}; - case Paths.misattribution_in_inconsistency_list: - return {title: "Misattribution in inconsistencies", endpoint: "misattribution-in-inconsistency", entity: null}; - case Paths.progress_list: - return {title: "Progress", endpoint: "progress", entity: null}; - case Paths.purse_not_closed_inconsistency_list: - return {title: "Purse not closed inconsistencies", endpoint: "purse-not-closed-inconsistencies", entity: null}; - case Paths.purse_list: - return {title: "Purses", endpoint: "purses", entity: null}; - case Paths.refresh_hanging_list: - return {title: "Refreshes hanging", endpoint: "refreshes-hanging", entity: null}; - case Paths.reserves_list: - return {title: "Reserves", endpoint: "reserves ", entity: null}; - case Paths.reserve_balance_insufficient_inconsistency_list: - return {title: "Reserve balance insufficient inconsistencies", endpoint: "reserve-balance-insufficient-inconsistency", entity: null}; - case Paths.reserve_balance_summary_wrong_inconsistency_list: - return {title: "Reserve balance summary wrong inconsistencies", endpoint: "reserve-balance-summary-wrong-inconsistency", entity: null}; - case Paths.reserve_in_inconsistency_list: - return {title: "Reserves in inconsistencies", endpoint: "reserve-in-inconsistency", entity: null}; - case Paths.reserve_not_closed_inconsistency_list: - return {title: "Reserves not closed inconsistencies", endpoint: "reserve-not-closed-inconsistency", entity: null}; - case Paths.row_inconsistency_list: - return {title: "Row inconsistencies", endpoint: "row-inconsistency", entity: null}; - case Paths.row_minor_inconsistency_list: - return {title: "Row minor inconsistencies", endpoint: "row-minor-inconsistencies", entity: null}; - case Paths.wire_format_inconsistency_list: - let wireFormatInconsistency: AuditorBackend.WireFormatInconsistency.ClassWireFormatInconsistency = {} as AuditorBackend.WireFormatInconsistency.ClassWireFormatInconsistency; - return {title: "Wire format inconsistencies", endpoint: "wire-format-inconsistency", entity: wireFormatInconsistency}; - case Paths.wire_out_inconsistency_list: - return {title: "Wire out inconsistencies", endpoint: "wire-out-inconsistency", entity: null}; - case Paths.settings: - return {title: `Settings`, endpoint: "settings", entity: null}; - default: - return {title: "", endpoint: "", entity: null}; - } + switch (path) { + case Paths.key_figures: + return { title: `Key figures`, endpoint: "helper", entity: null }; + case Paths.critical_errors: + return { title: `Critical errors`, endpoint: "helper", entity: null }; + case Paths.operating_status: + return { title: `Operating status`, endpoint: "helper", entity: null }; + case Paths.detail_view: + return { title: `Inconsistencies`, endpoint: "helper", entity: null }; + case Paths.amount_arithmethic_inconsistency_list: + let amountArithmeticInconsistency: AuditorBackend.AmountArithmeticInconsistency.ClassAmountArithmeticInconsistency = + {} as AuditorBackend.AmountArithmeticInconsistency.ClassAmountArithmeticInconsistency; + return { + title: `Amount arithmetic inconsistencies`, + endpoint: "amount-arithmetic-inconsistency", + entity: amountArithmeticInconsistency, + }; + case Paths.bad_sig_losses_list: + return { + title: `Bad Sig Losses`, + endpoint: "bad-sig-losses", + entity: null, + }; + case Paths.balance_list: + return { title: "Balances", endpoint: "balances", entity: null }; + case Paths.closure_lag_list: + return { title: `Closure Lags`, endpoint: "closure-lags", entity: null }; + case Paths.coin_inconsistency_list: + return { + title: `Coin inconsistencies`, + endpoint: "coin-inconsistency", + entity: null, + }; + case Paths.denomination_key_validity_withdraw_inconsistency_list: + return { + title: `Denomination key validity withdraw inconsistency`, + endpoint: "denomination-key-validity-withdraw-inconsistency", + entity: null, + }; + case Paths.denomination_pending_list: + return { + title: `Denominations pending`, + endpoint: "denomination-pending", + entity: null, + }; + case Paths.denomination_without_sig_list: + return { + title: `Denominations without sigs`, + endpoint: "denominations-without-sigs", + entity: null, + }; + case Paths.deposit_confirmation_list: + return { + title: "Deposit Confirmations", + endpoint: "deposit-confirmation", + entity: null, + }; + case Paths.emergency_list: + return { title: "Emergencies", endpoint: "emergency", entity: null }; + case Paths.emergency_by_count_list: + return { + title: "Emergencies by count", + endpoint: "emergency-by-count", + entity: null, + }; + case Paths.fee_time_inconsistency_list: + return { + title: "Fee time inconsistencies", + endpoint: "fee-time-inconsistency", + entity: null, + }; + case Paths.historic_denomination_revenue_list: + return { + title: "Historic denomination revenue", + endpoint: "historic-denomination-revenue", + entity: null, + }; + case Paths.misattribution_in_inconsistency_list: + return { + title: "Misattribution in inconsistencies", + endpoint: "misattribution-in-inconsistency", + entity: null, + }; + case Paths.progress_list: + return { title: "Progress", endpoint: "progress", entity: null }; + case Paths.purse_not_closed_inconsistency_list: + return { + title: "Purse not closed inconsistencies", + endpoint: "purse-not-closed-inconsistencies", + entity: null, + }; + case Paths.purse_list: + return { title: "Purses", endpoint: "purses", entity: null }; + case Paths.refresh_hanging_list: + return { + title: "Refreshes hanging", + endpoint: "refreshes-hanging", + entity: null, + }; + case Paths.reserves_list: + return { title: "Reserves", endpoint: "reserves ", entity: null }; + case Paths.reserve_balance_insufficient_inconsistency_list: + return { + title: "Reserve balance insufficient inconsistencies", + endpoint: "reserve-balance-insufficient-inconsistency", + entity: null, + }; + case Paths.reserve_balance_summary_wrong_inconsistency_list: + return { + title: "Reserve balance summary wrong inconsistencies", + endpoint: "reserve-balance-summary-wrong-inconsistency", + entity: null, + }; + case Paths.reserve_in_inconsistency_list: + return { + title: "Reserves in inconsistencies", + endpoint: "reserve-in-inconsistency", + entity: null, + }; + case Paths.reserve_not_closed_inconsistency_list: + return { + title: "Reserves not closed inconsistencies", + endpoint: "reserve-not-closed-inconsistency", + entity: null, + }; + case Paths.row_inconsistency_list: + return { + title: "Row inconsistencies", + endpoint: "row-inconsistency", + entity: null, + }; + case Paths.row_minor_inconsistency_list: + return { + title: "Row minor inconsistencies", + endpoint: "row-minor-inconsistencies", + entity: null, + }; + case Paths.wire_format_inconsistency_list: + let wireFormatInconsistency: AuditorBackend.WireFormatInconsistency.ClassWireFormatInconsistency = + {} as AuditorBackend.WireFormatInconsistency.ClassWireFormatInconsistency; + return { + title: "Wire format inconsistencies", + endpoint: "wire-format-inconsistency", + entity: wireFormatInconsistency, + }; + case Paths.wire_out_inconsistency_list: + return { + title: "Wire out inconsistencies", + endpoint: "wire-out-inconsistency", + entity: null, + }; + case Paths.settings: + return { title: `Settings`, endpoint: "settings", entity: null }; + default: + return { title: "", endpoint: "", entity: null }; + } } export interface Props { - path: string; + path: string; } -export function InstanceRoutes({ - // id, - path, - // setInstanceName - }: Props): VNode { - const {i18n} = useTranslationContext(); - - type GlobalNotifState = (Notification & { to: string | undefined }) | undefined; - const [globalNotification, setGlobalNotification] = - useState<GlobalNotifState>(undefined); +export function InstanceRoutes({ path }: Props): VNode { + type GlobalNotifState = + | (Notification & { to: string | undefined }) + | undefined; + const [globalNotification, setGlobalNotification] = + useState<GlobalNotifState>(undefined); - const [error] = useErrorBoundary(); - const {title, endpoint, entity} = getInstanceTitle(path.replace("app/#", "")); + const [error] = useErrorBoundary(); + const { title, endpoint, entity } = getInstanceTitle(path); - const value = useMemo( - () => ({title, path, endpoint, entity}), - [title, path, endpoint, entity], - ); + const value = useMemo( + () => ({ title, path, endpoint, entity }), + [title, path, endpoint, entity], + ); - //TODO add if needed - /*function ServerErrorRedirectTo(to: Paths) { + //TODO add if needed + /*function ServerErrorRedirectTo(to: Paths) { return function ServerErrorRedirectToImpl( error: HttpError<AuditorBackend.ErrorDetail>, ) { @@ -242,231 +315,272 @@ export function InstanceRoutes({ }; }*/ + return ( + <EntityContextProvider value={value}> + <Menu + // instance={id} + path={path} + title={"Settings"} + onShowSettings={() => { + route(Paths.settings); + }} + /> + <NotificationCard notification={globalNotification} /> + {error && ( + <NotificationCard + notification={{ + message: "Internal error, please report", + type: "ERROR", + description: ( + <pre> + { + (error instanceof Error + ? error.stack + : String(error)) as TranslatedString + } + </pre> + ), + }} + /> + )} + <Router + onChange={(e) => { + const movingOutFromNotification = + globalNotification && e.url !== globalNotification.to; + if (movingOutFromNotification) { + setGlobalNotification(undefined); + } + }} + > + <Route path="/" component={Redirect} to={Paths.key_figures} /> + + <Route + path={Paths.key_figures} + component={FinanceDashboard} + onNotFound={NotFoundPage} + //onLoadError={ServerErrorRedirectTo(Paths.balance_list)} + /> + <Route + path={Paths.critical_errors} + component={SecurityDashboard} + onNotFound={NotFoundPage} + //onLoadError={ServerErrorRedirectTo(Paths.balance_list)} + /> + <Route + path={Paths.operating_status} + component={OperationsDashboard} + onNotFound={NotFoundPage} + //onLoadError={ServerErrorRedirectTo(Paths.balance_list)} + /> + <Route + path={Paths.detail_view} + component={DetailsDashboard} + onNotFound={NotFoundPage} + //onLoadError={ServerErrorRedirectTo(Paths.balance_list)} + /> + <Route + path={Paths.amount_arithmethic_inconsistency_list} + component={DefaultList} + onNotFound={NotFoundPage} + //onLoadError={ServerErrorRedirectTo(Paths.balance_list)} + /> + <Route + path={Paths.bad_sig_losses_list} + component={DefaultList} + onNotFound={NotFoundPage} + //onLoadError={ServerErrorRedirectTo(Paths.balance_list)} + /> + <Route + path={Paths.balance_list} + component={DefaultList} + onNotFound={NotFoundPage} + //onLoadError={ServerErrorRedirectTo(Paths.balance_list)} + /> + <Route + path={Paths.closure_lag_list} + component={DefaultList} + onNotFound={NotFoundPage} + //onLoadError={ServerErrorRedirectTo(Paths.balance_list)} + /> + <Route + path={Paths.coin_inconsistency_list} + component={DefaultList} + onNotFound={NotFoundPage} + //onLoadError={ServerErrorRedirectTo(Paths.balance_list)} + /> + <Route + path={Paths.denomination_key_validity_withdraw_inconsistency_list} + component={DefaultList} + onNotFound={NotFoundPage} + //onLoadError={ServerErrorRedirectTo(Paths.balance_list)} + /> + <Route + path={Paths.denomination_pending_list} + component={DefaultList} + onNotFound={NotFoundPage} + //onLoadError={ServerErrorRedirectTo(Paths.balance_list)} + /> + <Route + path={Paths.denomination_without_sig_list} + component={DefaultList} + onNotFound={NotFoundPage} + //onLoadError={ServerErrorRedirectTo(Paths.balance_list)} + /> + <Route + path={Paths.deposit_confirmation_list} + component={DefaultList} + onNotFound={NotFoundPage} + //onLoadError={ServerErrorRedirectTo(Paths.balance_list)} + /> + <Route + path={Paths.emergency_list} + component={DefaultList} + onNotFound={NotFoundPage} + //onLoadError={ServerErrorRedirectTo(Paths.balance_list)} + /> + <Route + path={Paths.emergency_by_count_list} + component={DefaultList} + onNotFound={NotFoundPage} + //onLoadError={ServerErrorRedirectTo(Paths.balance_list)} + /> + { + <Route + path={Paths.exchange_signkey_list} + component={DefaultList} + onNotFound={NotFoundPage} + /> + } + { + <Route + path={Paths.fee_time_inconsistency_list} + component={DefaultList} + onNotFound={NotFoundPage} + /> + } + { + <Route + path={Paths.historic_denomination_revenue_list} + component={DefaultList} + onNotFound={NotFoundPage} + /> + } + { + <Route + path={Paths.misattribution_in_inconsistency_list} + component={DefaultList} + onNotFound={NotFoundPage} + /> + } + { + <Route + path={Paths.progress_list} + component={DefaultList} + onNotFound={NotFoundPage} + /> + } + { + <Route + path={Paths.purse_not_closed_inconsistency_list} + component={DefaultList} + onNotFound={NotFoundPage} + /> + } + { + <Route + path={Paths.purse_list} + component={DefaultList} + onNotFound={NotFoundPage} + /> + } + { + <Route + path={Paths.refresh_hanging_list} + component={DefaultList} + onNotFound={NotFoundPage} + /> + } + { + <Route + path={Paths.reserve_balance_insufficient_inconsistency_list} + component={DefaultList} + onNotFound={NotFoundPage} + /> + } + { + <Route + path={Paths.reserve_balance_summary_wrong_inconsistency_list} + component={DefaultList} + onNotFound={NotFoundPage} + /> + } + { + <Route + path={Paths.reserve_in_inconsistency_list} + component={DefaultList} + onNotFound={NotFoundPage} + /> + } + { + <Route + path={Paths.reserve_not_closed_inconsistency_list} + component={DefaultList} + onNotFound={NotFoundPage} + /> + } + { + <Route + path={Paths.reserves_list} + component={DefaultList} + onNotFound={NotFoundPage} + /> + } + { + <Route + path={Paths.row_inconsistency_list} + component={DefaultList} + onNotFound={NotFoundPage} + /> + } + { + <Route + path={Paths.row_minor_inconsistency_list} + component={DefaultList} + onNotFound={NotFoundPage} + /> + } + { + <Route + path={Paths.wire_out_inconsistency_list} + component={DefaultList} + onNotFound={NotFoundPage} + /> + } + { + <Route + path={Paths.wire_format_inconsistency_list} + component={DefaultList} + onNotFound={NotFoundPage} + /> + } + <Route path={Paths.settings} component={Settings} /> - return ( - <EntityContextProvider value={value}> - <Menu - // instance={id} - path={path} - title={"Settings"} - onShowSettings={() => { - route(Paths.settings); - }}/> - <NotificationCard notification={globalNotification}/> - {error && - <NotificationCard notification={{ - message: "Internal error, please report", - type: "ERROR", - description: <pre> - {(error instanceof Error ? error.stack : String(error)) as TranslatedString} - </pre>, - }}/> - } - <Router - onChange={(e) => { - const movingOutFromNotification = - globalNotification && e.url !== globalNotification.to; - if (movingOutFromNotification) { - setGlobalNotification(undefined); - } - }} - > - <Route path="/" component={Redirect} to={Paths.key_figures}/> - - <Route - path={Paths.key_figures} - component={FinanceDashboard} - onNotFound={NotFoundPage} - //onLoadError={ServerErrorRedirectTo(Paths.balance_list)} - /> - <Route - path={Paths.critical_errors} - component={SecurityDashboard} - onNotFound={NotFoundPage} - //onLoadError={ServerErrorRedirectTo(Paths.balance_list)} - /> - <Route - path={Paths.operating_status} - component={OperationsDashboard} - onNotFound={NotFoundPage} - //onLoadError={ServerErrorRedirectTo(Paths.balance_list)} - /> - <Route - path={Paths.detail_view} - component={DetailsDashboard} - onNotFound={NotFoundPage} - //onLoadError={ServerErrorRedirectTo(Paths.balance_list)} - /> - <Route - path={Paths.amount_arithmethic_inconsistency_list} - component={DefaultList} - onNotFound={NotFoundPage} - //onLoadError={ServerErrorRedirectTo(Paths.balance_list)} - /> - <Route - path={Paths.bad_sig_losses_list} - component={DefaultList} - onNotFound={NotFoundPage} - //onLoadError={ServerErrorRedirectTo(Paths.balance_list)} - /> - <Route - path={Paths.balance_list} - component={DefaultList} - onNotFound={NotFoundPage} - //onLoadError={ServerErrorRedirectTo(Paths.balance_list)} - /> - <Route - path={Paths.closure_lag_list} - component={DefaultList} - onNotFound={NotFoundPage} - //onLoadError={ServerErrorRedirectTo(Paths.balance_list)} - /> - <Route - path={Paths.coin_inconsistency_list} - component={DefaultList} - onNotFound={NotFoundPage} - //onLoadError={ServerErrorRedirectTo(Paths.balance_list)} - /> - <Route - path={Paths.denomination_key_validity_withdraw_inconsistency_list} - component={DefaultList} - onNotFound={NotFoundPage} - //onLoadError={ServerErrorRedirectTo(Paths.balance_list)} - /> - <Route - path={Paths.denomination_pending_list} - component={DefaultList} - onNotFound={NotFoundPage} - //onLoadError={ServerErrorRedirectTo(Paths.balance_list)} - /> - <Route - path={Paths.denomination_without_sig_list} - component={DefaultList} - onNotFound={NotFoundPage} - //onLoadError={ServerErrorRedirectTo(Paths.balance_list)} - /> - <Route - path={Paths.deposit_confirmation_list} - component={DefaultList} - onNotFound={NotFoundPage} - //onLoadError={ServerErrorRedirectTo(Paths.balance_list)} - /> - <Route - path={Paths.emergency_list} - component={DefaultList} - onNotFound={NotFoundPage} - //onLoadError={ServerErrorRedirectTo(Paths.balance_list)} - /> - <Route - path={Paths.emergency_by_count_list} - component={DefaultList} - onNotFound={NotFoundPage} - //onLoadError={ServerErrorRedirectTo(Paths.balance_list)} - /> - {<Route - path={Paths.exchange_signkey_list} - component={DefaultList} - onNotFound={NotFoundPage} - />} - {<Route - path={Paths.fee_time_inconsistency_list} - component={DefaultList} - onNotFound={NotFoundPage} - />} - {<Route - path={Paths.historic_denomination_revenue_list} - component={DefaultList} - onNotFound={NotFoundPage} - />} - {<Route - path={Paths.misattribution_in_inconsistency_list} - component={DefaultList} - onNotFound={NotFoundPage} - />} - {<Route - path={Paths.progress_list} - component={DefaultList} - onNotFound={NotFoundPage} - />} - {<Route - path={Paths.purse_not_closed_inconsistency_list} - component={DefaultList} - onNotFound={NotFoundPage} - />} - {<Route - path={Paths.purse_list} - component={DefaultList} - onNotFound={NotFoundPage} - />} - {<Route - path={Paths.refresh_hanging_list} - component={DefaultList} - onNotFound={NotFoundPage} - />} - {<Route - path={Paths.reserve_balance_insufficient_inconsistency_list} - component={DefaultList} - onNotFound={NotFoundPage} - />} - {<Route - path={Paths.reserve_balance_summary_wrong_inconsistency_list} - component={DefaultList} - onNotFound={NotFoundPage} - />} - {<Route - path={Paths.reserve_in_inconsistency_list} - component={DefaultList} - onNotFound={NotFoundPage} - />} - {<Route - path={Paths.reserve_not_closed_inconsistency_list} - component={DefaultList} - onNotFound={NotFoundPage} - />} - {<Route - path={Paths.reserves_list} - component={DefaultList} - onNotFound={NotFoundPage} - />} - {<Route - path={Paths.row_inconsistency_list} - component={DefaultList} - onNotFound={NotFoundPage} - />} - {<Route - path={Paths.row_minor_inconsistency_list} - component={DefaultList} - onNotFound={NotFoundPage} - />} - {<Route - path={Paths.wire_out_inconsistency_list} - component={DefaultList} - onNotFound={NotFoundPage} - />} - {<Route - path={Paths.wire_format_inconsistency_list} - component={DefaultList} - onNotFound={NotFoundPage} - />} - <Route - path={Paths.settings} - component={Settings} - /> - - {//TODO add if needed - /** - * Example pages - */} - {/* <Route path="/loading" component={Loading}/> + { + //TODO add if needed + /** + * Example pages + */ + } + {/* <Route path="/loading" component={Loading}/> <Route default component={NotFoundPage}/>*/} - </Router> - </EntityContextProvider> - ); + </Router> + </EntityContextProvider> + ); } -export function Redirect({to}: { to: string }): null { - useEffect(() => { - route(to, true); - }); - return null; -}
\ No newline at end of file +export function Redirect({ to }: { to: string }): null { + useEffect(() => { + route(to, true); + }); + return null; +} diff --git a/packages/auditor-backoffice-ui/src/components/exception/loading.tsx b/packages/auditor-backoffice-ui/src/components/exception/loading.tsx index 11b62c124..5c249f79d 100644 --- a/packages/auditor-backoffice-ui/src/components/exception/loading.tsx +++ b/packages/auditor-backoffice-ui/src/components/exception/loading.tsx @@ -22,27 +22,27 @@ import { h, VNode } from "preact"; export function Loading(): VNode { - return ( - <div - class="columns is-centered is-vcentered" - style={{ - height: "calc(100% - 3rem)", - position: "absolute", - width: "100%", - }} - > - <Spinner /> - </div> - ); + return ( + <div + class="columns is-centered is-vcentered" + style={{ + height: "calc(100% - 3rem)", + position: "absolute", + width: "100%", + }} + > + <Spinner /> + </div> + ); } export function Spinner(): VNode { - return ( - <div class="lds-ring"> - <div /> - <div /> - <div /> - <div /> - </div> - ); + return ( + <div class="lds-ring"> + <div /> + <div /> + <div /> + <div /> + </div> + ); } diff --git a/packages/auditor-backoffice-ui/src/components/forms/FormProvider.tsx b/packages/auditor-backoffice-ui/src/components/forms/FormProvider.tsx index a5f3c1d2f..4abe465c7 100644 --- a/packages/auditor-backoffice-ui/src/components/forms/FormProvider.tsx +++ b/packages/auditor-backoffice-ui/src/components/forms/FormProvider.tsx @@ -20,7 +20,7 @@ */ import { ComponentChildren, createContext, h, VNode } from "preact"; -import { useContext, useMemo } from "preact/hooks"; +import { useMemo } from "preact/hooks"; type Updater<S> = (value: (prevState: S) => S) => void; @@ -82,16 +82,6 @@ export interface FormType<T> { const FormContext = createContext<FormType<unknown>>(null!); -/** - * FIXME: - * USE MEMORY EVENTS INSTEAD OF CONTEXT - * @deprecated - */ - -export function useFormContext<T>() { - return useContext<FormType<T>>(FormContext); -} - export type FormErrors<T> = { [P in keyof T]?: string | FormErrors<T[P]>; }; @@ -103,7 +93,3 @@ export type FormtoStr<T> = { export type FormfromStr<T> = { [P in keyof T]?: (f: string) => T[P]; }; - -export type FormUpdater<T> = { - [P in keyof T]?: (f: keyof T) => (v: T[P]) => void; -}; diff --git a/packages/auditor-backoffice-ui/src/components/forms/Input.tsx b/packages/auditor-backoffice-ui/src/components/forms/Input.tsx deleted file mode 100644 index 899061c35..000000000 --- a/packages/auditor-backoffice-ui/src/components/forms/Input.tsx +++ /dev/null @@ -1,116 +0,0 @@ -/* - This file is part of GNU Taler - (C) 2021-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 <http://www.gnu.org/licenses/> - */ - -/** - * - * @author Sebastian Javier Marchano (sebasjm) - */ -import { ComponentChildren, h, VNode } from "preact"; -import { useField, InputProps } from "./useField.js"; - -interface Props<T> extends InputProps<T> { - inputType?: "text" | "number" | "multiline" | "password"; - expand?: boolean; - toStr?: (v?: any) => string; - fromStr?: (s: string) => any; - inputExtra?: any; - side?: ComponentChildren; - children?: ComponentChildren; -} - -const defaultToString = (f?: any): string => f || ""; -const defaultFromString = (v: string): any => v as any; - -const TextInput = ({ inputType, error, ...rest }: any) => - inputType === "multiline" ? ( - <textarea - {...rest} - class={error ? "textarea is-danger" : "textarea"} - rows="3" - /> - ) : ( - <input - {...rest} - class={error ? "input is-danger" : "input"} - type={inputType} - /> - ); - -export function Input<T>({ - name, - readonly, - placeholder, - tooltip, - label, - expand, - help, - children, - inputType, - inputExtra, - side, - fromStr = defaultFromString, - toStr = defaultToString, -}: Props<keyof T>): VNode { - const { error, value, onChange, required } = useField<T>(name); - return ( - <div class="field is-horizontal"> - <div class="field-label is-normal"> - <label class="label"> - {label} - {tooltip && ( - <span class="icon has-tooltip-right" data-tooltip={tooltip}> - <i class="mdi mdi-information" /> - </span> - )} - </label> - </div> - <div class="field-body is-flex-grow-3"> - <div class="field"> - <p - class={ - expand - ? "control is-expanded has-icons-right" - : "control has-icons-right" - } - > - <TextInput - error={error} - {...inputExtra} - inputType={inputType} - placeholder={placeholder} - readonly={readonly} - disabled={readonly} - name={String(name)} - value={toStr(value)} - onChange={(e: h.JSX.TargetedEvent<HTMLInputElement>): void => - onChange(fromStr(e.currentTarget.value)) - } - /> - {help} - {children} - {required && ( - <span class="icon has-text-danger is-right"> - <i class="mdi mdi-alert" /> - </span> - )} - </p> - {error && <p class="help is-danger">{error}</p>} - </div> - {side} - </div> - </div> - ); -} diff --git a/packages/auditor-backoffice-ui/src/components/forms/InputCurrency.tsx b/packages/auditor-backoffice-ui/src/components/forms/InputCurrency.tsx deleted file mode 100644 index c1359e641..000000000 --- a/packages/auditor-backoffice-ui/src/components/forms/InputCurrency.tsx +++ /dev/null @@ -1,67 +0,0 @@ -/* - This file is part of GNU Taler - (C) 2021-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 <http://www.gnu.org/licenses/> - */ - -/** - * - * @author Sebastian Javier Marchano (sebasjm) - */ -import { ComponentChildren, h, VNode } from "preact"; -import { useConfigContext } from "../../context/config.js"; -import { Amount } from "../../declaration.js"; -import { InputWithAddon } from "./InputWithAddon.js"; -import { InputProps } from "./useField.js"; - -export interface Props<T> extends InputProps<T> { - expand?: boolean; - addonAfter?: ComponentChildren; - children?: ComponentChildren; - side?: ComponentChildren; -} - -export function InputCurrency<T>({ - name, - readonly, - label, - placeholder, - help, - tooltip, - expand, - addonAfter, - children, - side, -}: Props<keyof T>): VNode { - const config = useConfigContext(); - return ( - <InputWithAddon<T> - name={name} - readonly={readonly} - addonBefore={config.currency} - side={side} - label={label} - placeholder={placeholder} - help={help} - tooltip={tooltip} - addonAfter={addonAfter} - inputType="number" - expand={expand} - toStr={(v?: Amount) => v?.split(":")[1] || ""} - fromStr={(v: string) => (!v ? undefined : `${config.currency}:${v}`)} - inputExtra={{ min: 0 }} - > - {children} - </InputWithAddon> - ); -} diff --git a/packages/auditor-backoffice-ui/src/components/forms/InputNumber.tsx b/packages/auditor-backoffice-ui/src/components/forms/InputNumber.tsx deleted file mode 100644 index 10b28cd93..000000000 --- a/packages/auditor-backoffice-ui/src/components/forms/InputNumber.tsx +++ /dev/null @@ -1,60 +0,0 @@ -/* - This file is part of GNU Taler - (C) 2021-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 <http://www.gnu.org/licenses/> - */ - -/** - * - * @author Sebastian Javier Marchano (sebasjm) - */ -import { ComponentChildren, h } from "preact"; -import { InputWithAddon } from "./InputWithAddon.js"; -import { InputProps } from "./useField.js"; - -export interface Props<T> extends InputProps<T> { - readonly?: boolean; - expand?: boolean; - side?: ComponentChildren; - children?: ComponentChildren; -} - -export function InputNumber<T>({ - name, - readonly, - placeholder, - tooltip, - label, - help, - expand, - children, - side, -}: Props<keyof T>) { - return ( - <InputWithAddon<T> - name={name} - readonly={readonly} - fromStr={(v) => (!v ? undefined : parseInt(v, 10))} - toStr={(v) => `${v}`} - inputType="number" - expand={expand} - label={label} - placeholder={placeholder} - help={help} - tooltip={tooltip} - inputExtra={{ min: 0 }} - children={children} - side={side} - /> - ); -} diff --git a/packages/auditor-backoffice-ui/src/components/forms/InputSelector.tsx b/packages/auditor-backoffice-ui/src/components/forms/InputSelector.tsx deleted file mode 100644 index f567f7247..000000000 --- a/packages/auditor-backoffice-ui/src/components/forms/InputSelector.tsx +++ /dev/null @@ -1,94 +0,0 @@ -/* - This file is part of GNU Taler - (C) 2021-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 <http://www.gnu.org/licenses/> - */ - -/** - * - * @author Sebastian Javier Marchano (sebasjm) - */ -import { h, VNode } from "preact"; -import { InputProps, useField } from "./useField.js"; - -interface Props<T> extends InputProps<T> { - readonly?: boolean; - expand?: boolean; - values: any[]; - toStr?: (v?: any) => string; - fromStr?: (s: string) => any; -} - -const defaultToString = (f?: any): string => f || ""; -const defaultFromString = (v: string): any => v as any; - -export function InputSelector<T>({ - name, - readonly, - expand, - placeholder, - tooltip, - label, - help, - values, - fromStr = defaultFromString, - toStr = defaultToString, -}: Props<keyof T>): VNode { - const { error, value, onChange, required } = useField<T>(name); - return ( - <div class="field is-horizontal"> - <div class="field-label is-normal"> - <label class="label"> - {label} - {tooltip && ( - <span class="icon has-tooltip-right" data-tooltip={tooltip}> - <i class="mdi mdi-information" /> - </span> - )} - </label> - </div> - <div class="field-body is-flex-grow-3"> - <div class="field has-icons-right"> - <p class={expand ? "control is-expanded select" : "control select "}> - <select - class={error ? "select is-danger" : "select"} - name={String(name)} - disabled={readonly} - readonly={readonly} - onChange={(e) => { - onChange(fromStr(e.currentTarget.value)); - }} - > - {placeholder && <option>{placeholder}</option>} - {values.map((v, i) => { - return ( - <option key={i} value={v} selected={value === v}> - {toStr(v)} - </option> - ); - })} - </select> - - {help} - </p> - {required && ( - <span class="icon has-text-danger is-right" style={{height: "2.5em"}}> - <i class="mdi mdi-alert" /> - </span> - )} - {error && <p class="help is-danger">{error}</p>} - </div> - </div> - </div> - ); -} diff --git a/packages/auditor-backoffice-ui/src/components/forms/InputToggle.tsx b/packages/auditor-backoffice-ui/src/components/forms/InputToggle.tsx deleted file mode 100644 index 89b815b4b..000000000 --- a/packages/auditor-backoffice-ui/src/components/forms/InputToggle.tsx +++ /dev/null @@ -1,91 +0,0 @@ -/* - This file is part of GNU Taler - (C) 2021-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 <http://www.gnu.org/licenses/> - */ - -/** - * - * @author Sebastian Javier Marchano (sebasjm) - */ -import { h, VNode } from "preact"; -import { InputProps, useField } from "./useField.js"; - -interface Props<T> extends InputProps<T> { - name: T; - readonly?: boolean; - expand?: boolean; - threeState?: boolean; - toBoolean?: (v?: any) => boolean | undefined; - fromBoolean?: (s: boolean | undefined) => any; -} - -const defaultToBoolean = (f?: any): boolean | undefined => f || ""; -const defaultFromBoolean = (v: boolean | undefined): any => v as any; - -export function InputToggle<T>({ - name, - readonly, - placeholder, - tooltip, - label, - help, - threeState, - expand, - fromBoolean = defaultFromBoolean, - toBoolean = defaultToBoolean, -}: Props<keyof T>): VNode { - const { error, value, onChange } = useField<T>(name); - - const onCheckboxClick = (): void => { - const c = toBoolean(value); - if (c === false && threeState) return onChange(undefined as any); - return onChange(fromBoolean(!c)); - }; - - return ( - <div class="field is-horizontal"> - <div class="field-label is-normal"> - <label class="label" > - {label} - {tooltip && ( - <span class="icon has-tooltip-right" data-tooltip={tooltip}> - <i class="mdi mdi-information" /> - </span> - )} - </label> - </div> - <div class="field-body is-flex-grow-3"> - <div class="field"> - <p class={expand ? "control is-expanded" : "control"}> - <label class="toggle" style={{ marginLeft: 4, marginTop: 0 }}> - <input - type="checkbox" - class={toBoolean(value) === undefined ? "is-indeterminate" : "toggle-checkbox"} - checked={toBoolean(value)} - placeholder={placeholder} - readonly={readonly} - name={String(name)} - disabled={readonly} - onChange={onCheckboxClick} - /> - <div class="toggle-switch"></div> - </label> - {help} - </p> - {error && <p class="help is-danger">{error}</p>} - </div> - </div> - </div> - ); -} diff --git a/packages/auditor-backoffice-ui/src/components/forms/InputWithAddon.tsx b/packages/auditor-backoffice-ui/src/components/forms/InputWithAddon.tsx deleted file mode 100644 index b8cd4c2d2..000000000 --- a/packages/auditor-backoffice-ui/src/components/forms/InputWithAddon.tsx +++ /dev/null @@ -1,116 +0,0 @@ -/* - This file is part of GNU Taler - (C) 2021-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 <http://www.gnu.org/licenses/> - */ - -/** - * - * @author Sebastian Javier Marchano (sebasjm) - */ -import { ComponentChildren, h, VNode } from "preact"; -import { InputProps, useField } from "./useField.js"; - -export interface Props<T> extends InputProps<T> { - expand?: boolean; - inputType?: "text" | "number" | "password"; - addonBefore?: ComponentChildren; - addonAfter?: ComponentChildren; - addonAfterAction?: () => void; - toStr?: (v?: any) => string; - fromStr?: (s: string) => any; - inputExtra?: any; - children?: ComponentChildren; - side?: ComponentChildren; -} - -const defaultToString = (f?: any): string => f || ""; -const defaultFromString = (v: string): any => v as any; - -export function InputWithAddon<T>({ - name, - readonly, - addonBefore, - children, - expand, - label, - placeholder, - help, - tooltip, - inputType, - inputExtra, - side, - addonAfter, - addonAfterAction, - toStr = defaultToString, - fromStr = defaultFromString, -}: Props<keyof T>): VNode { - const { error, value, onChange, required } = useField<T>(name); - - return ( - <div class="field is-horizontal"> - <div class="field-label is-normal"> - <label class="label"> - {label} - {tooltip && ( - <span class="icon has-tooltip-right" data-tooltip={tooltip}> - <i class="mdi mdi-information" /> - </span> - )} - </label> - </div> - <div class="field-body is-flex-grow-3"> - <div class="field"> - <div class="field has-addons"> - {addonBefore && ( - <div class="control"> - <a class="button is-static">{addonBefore}</a> - </div> - )} - <p - class={`control${expand ? " is-expanded" : ""}${required ? " has-icons-right" : "" - }`} - > - <input - {...(inputExtra || {})} - class={error ? "input is-danger" : "input"} - type={inputType} - placeholder={placeholder} - readonly={readonly} - disabled={readonly} - name={String(name)} - value={toStr(value)} - onChange={(e): void => onChange(fromStr(e.currentTarget.value))} - /> - {required && ( - <span class="icon has-text-danger is-right"> - <i class="mdi mdi-alert" /> - </span> - )} - {children} - </p> - {addonAfter && ( - <div class="control" onClick={addonAfterAction} style={{ cursor: addonAfterAction ? "pointer" : undefined }}> - <a class="button is-static">{addonAfter}</a> - </div> - )} - </div> - {error && <p class="help is-danger">{error}</p>} - <span class="has-text-grey">{help}</span> - </div> - {expand ? <div>{side}</div> : side} - </div> - - </div> - ); -} diff --git a/packages/auditor-backoffice-ui/src/components/forms/useField.tsx b/packages/auditor-backoffice-ui/src/components/forms/useField.tsx deleted file mode 100644 index 49bba4984..000000000 --- a/packages/auditor-backoffice-ui/src/components/forms/useField.tsx +++ /dev/null @@ -1,92 +0,0 @@ -/* - This file is part of GNU Taler - (C) 2021-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 <http://www.gnu.org/licenses/> - */ - -/** - * - * @author Sebastian Javier Marchano (sebasjm) - */ - -import { ComponentChildren, VNode } from "preact"; -import { useState } from "preact/hooks"; -import { useFormContext } from "./FormProvider.js"; - -interface Use<V> { - error?: string; - required: boolean; - value: any; - initial: any; - onChange: (v: V) => void; - toStr: (f: V | undefined) => string; - fromStr: (v: string) => V; -} - -export function useField<T>(name: keyof T): Use<T[typeof name]> { - const { errors, object, initialObject, toStr, fromStr, valueHandler } = - useFormContext<T>(); - type P = typeof name; - type V = T[P]; - const [isDirty, setDirty] = useState(false); - const updateField = - (field: P) => - (value: V): void => { - setDirty(true); - return valueHandler((prev) => { - return setValueDeeper(prev, String(field).split("."), value); - }); - }; - - const defaultToString = (f?: V): string => String(!f ? "" : f); - const defaultFromString = (v: string): V => v as any; - const value = readField(object, String(name)); - const initial = readField(initialObject, String(name)); - const hasError = readField(errors, String(name)); - return { - error: isDirty ? hasError : undefined, - required: !isDirty && hasError, - value, - initial, - onChange: updateField(name) as any, - toStr: toStr[name] ? toStr[name]! : defaultToString, - fromStr: fromStr[name] ? fromStr[name]! : defaultFromString, - }; -} -/** - * read the field of an object an support accessing it using '.' - * - * @param object - * @param name - * @returns - */ -const readField = (object: any, name: string) => { - return name - .split(".") - .reduce((prev, current) => prev && prev[current], object); -}; - -const setValueDeeper = (object: any, names: string[], value: any): any => { - if (names.length === 0) return value; - const [head, ...rest] = names; - return { ...object, [head]: setValueDeeper(object[head] || {}, rest, value) }; -}; - -export interface InputProps<T> { - name: T; - label: ComponentChildren; - placeholder?: string; - tooltip?: ComponentChildren; - readonly?: boolean; - help?: ComponentChildren; -} diff --git a/packages/auditor-backoffice-ui/src/components/menu/NavigationBar.tsx b/packages/auditor-backoffice-ui/src/components/menu/NavigationBar.tsx index d81410bdf..66469378b 100644 --- a/packages/auditor-backoffice-ui/src/components/menu/NavigationBar.tsx +++ b/packages/auditor-backoffice-ui/src/components/menu/NavigationBar.tsx @@ -55,7 +55,7 @@ export function NavigationBar({ onMobileMenu, title }: Props): VNode { </a> </div> - <div class="navbar-menu "> + <div class="navbar-menu"> <a class="navbar-start is-justify-content-center is-flex-grow-1" href="https://taler.net" @@ -63,8 +63,10 @@ export function NavigationBar({ onMobileMenu, title }: Props): VNode { <img src={logo} style={{ height: 35, margin: 10 }} /> </a> <div class="navbar-end"> - <div class="navbar-item" style={{ paddingTop: 4, paddingBottom: 4 }}> - </div> + <div + class="navbar-item" + style={{ paddingTop: 4, paddingBottom: 4 }} + ></div> </div> </div> </nav> diff --git a/packages/auditor-backoffice-ui/src/components/menu/index.tsx b/packages/auditor-backoffice-ui/src/components/menu/index.tsx index e411939c7..214b6bd3b 100644 --- a/packages/auditor-backoffice-ui/src/components/menu/index.tsx +++ b/packages/auditor-backoffice-ui/src/components/menu/index.tsx @@ -22,221 +22,219 @@ GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> * @author Nic Eigel */ -import {ComponentChildren, Fragment, h, VNode} from "preact"; -import {useEffect, useState} from "preact/hooks"; -import {Paths} from "../../InstanceRoutes.js"; -import {Notification} from "../../utils/types.js"; -import {NavigationBar} from "./NavigationBar.js"; -import {Sidebar} from "./SideBar.js"; +import { ComponentChildren, Fragment, h, VNode } from "preact"; +import { useEffect, useState } from "preact/hooks"; +import { Paths } from "../../InstanceRoutes.js"; +import { Notification } from "../../utils/types.js"; +import { NavigationBar } from "./NavigationBar.js"; +import { Sidebar } from "./SideBar.js"; function getInstanceTitle(path: string): string { - switch (path) { - case Paths.key_figures: - return 'Key figures'; - case Paths.critical_errors: - return 'Critical errors'; - case Paths.operating_status: - return 'Operating status'; - case Paths.detail_view: - return 'Inconsistencies'; - case Paths.amount_arithmethic_inconsistency_list: - return `Amount arithmetic inconsistencies`; - case Paths.bad_sig_losses_list: - return `Bad sig losses`; - case Paths.balance_list: - return `Balances`; - case Paths.closure_lag_list: - return `Closure Lags`; - case Paths.coin_inconsistency_list: - return `Coin inconsistencies`; - case Paths.denomination_key_validity_withdraw_inconsistency_list: - return `Denomination key validity withdraw inconsistency`; - case Paths.denomination_pending_list: - return `Denominations pending`; - case Paths.denomination_without_sig_list: - return `Denominations without sigs`; - case Paths.deposit_confirmation_list: - return `Deposit confirmations`; - case Paths.deposit_confirmation_update: - return `Update deposit confirmation`; - case Paths.emergency_list: - return `Emergencies`; - case Paths.emergency_by_count_list: - return `Emergencies by count`; - case Paths.exchange_signkey_list: - return `Exchange signkeys`; - case Paths.fee_time_inconsistency_list: - return `Fee time inconsistencies`; - case Paths.historic_denomination_revenue_list: - return `Historic denomination revenue`; - case Paths.misattribution_in_inconsistency_list: - return `Misattribution in inconsistencies`; - case Paths.progress_list: - return `Progress`; - case Paths.purse_not_closed_inconsistency_list: - return `Purse not closed inconsistencies`; - case Paths.purse_list: - return `Purses`; - case Paths.refresh_hanging_list: - return `Refreshes hanging`; - case Paths.reserve_balance_insufficient_inconsistency_list: - return `Reserve balance insufficient inconsistencies`; - case Paths.reserve_balance_summary_wrong_inconsistency_list: - return `Reserve balance summary wrong inconsistencies`; - case Paths.reserve_in_inconsistency_list: - return `Reserves in inconsistencies`; - case Paths.reserve_not_closed_inconsistency_list: - return `Reserves not closed inconsistencies`; - case Paths.row_inconsistency_list: - return `Row inconsistencies`; - case Paths.row_minor_inconsistency_list: - return `Row minor inconsistencies`; - case Paths.wire_format_inconsistency_list: - return `Wire format inconsistencies`; - case Paths.wire_out_inconsistency_list: - return `Wire out inconsistencies`; - case Paths.settings: - return `Settings`; - default: - return ""; - } + switch (path) { + case Paths.key_figures: + return "Key figures"; + case Paths.critical_errors: + return "Critical errors"; + case Paths.operating_status: + return "Operating status"; + case Paths.detail_view: + return "Inconsistencies"; + case Paths.amount_arithmethic_inconsistency_list: + return `Amount arithmetic inconsistencies`; + case Paths.bad_sig_losses_list: + return `Bad sig losses`; + case Paths.balance_list: + return `Balances`; + case Paths.closure_lag_list: + return `Closure Lags`; + case Paths.coin_inconsistency_list: + return `Coin inconsistencies`; + case Paths.denomination_key_validity_withdraw_inconsistency_list: + return `Denomination key validity withdraw inconsistency`; + case Paths.denomination_pending_list: + return `Denominations pending`; + case Paths.denomination_without_sig_list: + return `Denominations without sigs`; + case Paths.deposit_confirmation_list: + return `Deposit confirmations`; + case Paths.deposit_confirmation_update: + return `Update deposit confirmation`; + case Paths.emergency_list: + return `Emergencies`; + case Paths.emergency_by_count_list: + return `Emergencies by count`; + case Paths.exchange_signkey_list: + return `Exchange signkeys`; + case Paths.fee_time_inconsistency_list: + return `Fee time inconsistencies`; + case Paths.historic_denomination_revenue_list: + return `Historic denomination revenue`; + case Paths.misattribution_in_inconsistency_list: + return `Misattribution in inconsistencies`; + case Paths.progress_list: + return `Progress`; + case Paths.purse_not_closed_inconsistency_list: + return `Purse not closed inconsistencies`; + case Paths.purse_list: + return `Purses`; + case Paths.refresh_hanging_list: + return `Refreshes hanging`; + case Paths.reserve_balance_insufficient_inconsistency_list: + return `Reserve balance insufficient inconsistencies`; + case Paths.reserve_balance_summary_wrong_inconsistency_list: + return `Reserve balance summary wrong inconsistencies`; + case Paths.reserve_in_inconsistency_list: + return `Reserves in inconsistencies`; + case Paths.reserve_not_closed_inconsistency_list: + return `Reserves not closed inconsistencies`; + case Paths.row_inconsistency_list: + return `Row inconsistencies`; + case Paths.row_minor_inconsistency_list: + return `Row minor inconsistencies`; + case Paths.wire_format_inconsistency_list: + return `Wire format inconsistencies`; + case Paths.wire_out_inconsistency_list: + return `Wire out inconsistencies`; + case Paths.settings: + return `Settings`; + default: + return ""; + } } interface MenuProps { - title?: string; - path: string; - onShowSettings: () => void; + title?: string; + path: string; + onShowSettings: () => void; } function WithTitle({ - title, - children, - }: { - title: string; - children: ComponentChildren; + title, + children, +}: { + title: string; + children: ComponentChildren; }): VNode { - useEffect(() => { - document.title = `Taler Backoffice: ${title}`; - }, [title]); - return <Fragment>{children}</Fragment>; + useEffect(() => { + document.title = `Taler Backoffice: ${title}`; + }, [title]); + return <Fragment>{children}</Fragment>; } -export function Menu({ - onShowSettings, - title, - path, - }: MenuProps): VNode { - const [mobileOpen, setMobileOpen] = useState(false); - const titleWithSubtitle = getInstanceTitle(path.replace("app/#", "")); - return ( - <WithTitle title={titleWithSubtitle}> - <div - class={mobileOpen ? "has-aside-mobile-expanded" : ""} - onClick={() => setMobileOpen(false)} - > - <NavigationBar - onMobileMenu={() => setMobileOpen(!mobileOpen)} - title={titleWithSubtitle} - /> - - <Sidebar - onShowSettings={onShowSettings} - mobile={mobileOpen} - /> - </div> - </WithTitle> - ); +export function Menu({ onShowSettings, title, path }: MenuProps): VNode { + const [mobileOpen, setMobileOpen] = useState(false); + const titleWithSubtitle = getInstanceTitle(path.replace("app/#", "")); + return ( + <WithTitle title={titleWithSubtitle}> + <div + class={mobileOpen ? "has-aside-mobile-expanded" : ""} + onClick={() => setMobileOpen(false)} + > + <NavigationBar + onMobileMenu={() => setMobileOpen(!mobileOpen)} + title={titleWithSubtitle} + /> + + <Sidebar onShowSettings={onShowSettings} mobile={mobileOpen} /> + </div> + </WithTitle> + ); } interface NotYetReadyAppMenuProps { - title: string; - onShowSettings: () => void; + title: string; + onShowSettings: () => void; } interface NotifProps { - notification?: Notification; + notification?: Notification; } -export function NotificationCard({notification: n}: NotifProps): VNode | null { - if (!n) return null; - return ( - <div class="notification"> - <div class="columns is-vcentered"> - <div class="column is-12"> - <article - class={ - n.type === "ERROR" - ? "message is-danger" - : n.type === "WARN" - ? "message is-warning" - : "message is-info" - } - > - <div class="message-header"> - <p>{n.message}</p> - </div> - {n.description && ( - <div class="message-body"> - <div>{n.description}</div> - {n.details && <pre>{n.details}</pre>} - </div> - )} - </article> - </div> +export function NotificationCard({ + notification: n, +}: NotifProps): VNode | null { + if (!n) return null; + return ( + <div class="notification"> + <div class="columns is-vcentered"> + <div class="column is-12"> + <article + class={ + n.type === "ERROR" + ? "message is-danger" + : n.type === "WARN" + ? "message is-warning" + : "message is-info" + } + > + <div class="message-header"> + <p>{n.message}</p> </div> + {n.description && ( + <div class="message-body"> + <div>{n.description}</div> + {n.details && <pre>{n.details}</pre>} + </div> + )} + </article> </div> - ); + </div> + </div> + ); } interface NotConnectedAppMenuProps { - title: string; + title: string; } export function NotConnectedAppMenu({ - title, - }: NotConnectedAppMenuProps): VNode { - const [mobileOpen, setMobileOpen] = useState(false); - - useEffect(() => { - document.title = `Taler Backoffice: ${title}`; - }, [title]); - - return ( - <div - class={mobileOpen ? "has-aside-mobile-expanded" : ""} - onClick={() => setMobileOpen(false)} - > - <NavigationBar - onMobileMenu={() => setMobileOpen(!mobileOpen)} - title={title} - /> - </div> - ); + title, +}: NotConnectedAppMenuProps): VNode { + const [mobileOpen, setMobileOpen] = useState(false); + + useEffect(() => { + document.title = `Taler Backoffice: ${title}`; + }, [title]); + + return ( + <div + class={mobileOpen ? "has-aside-mobile-expanded" : ""} + onClick={() => setMobileOpen(false)} + > + <NavigationBar + onMobileMenu={() => setMobileOpen(!mobileOpen)} + title={title} + /> + </div> + ); } - export function NotYetReadyAppMenu({ - onShowSettings, - title - }: NotYetReadyAppMenuProps): VNode { - const [mobileOpen, setMobileOpen] = useState(false); - - useEffect(() => { - document.title = `Taler Backoffice: ${title}`; - }, [title]); - - return ( - <div - class={mobileOpen ? "has-aside-mobile-expanded" : ""} - onClick={() => setMobileOpen(false)} - > - <NavigationBar - onMobileMenu={() => setMobileOpen(!mobileOpen)} - title={title} - /> - ( - <Sidebar onShowSettings={onShowSettings} instance="" mobile={mobileOpen}/> - ) - </div> - ); -}
\ No newline at end of file + onShowSettings, + title, +}: NotYetReadyAppMenuProps): VNode { + const [mobileOpen, setMobileOpen] = useState(false); + + useEffect(() => { + document.title = `Taler Backoffice: ${title}`; + }, [title]); + + return ( + <div + class={mobileOpen ? "has-aside-mobile-expanded" : ""} + onClick={() => setMobileOpen(false)} + > + <NavigationBar + onMobileMenu={() => setMobileOpen(!mobileOpen)} + title={title} + /> + ( + <Sidebar + onShowSettings={onShowSettings} + instance="" + mobile={mobileOpen} + /> + ) + </div> + ); +} diff --git a/packages/auditor-backoffice-ui/src/components/modal/index.tsx b/packages/auditor-backoffice-ui/src/components/modal/index.tsx index ab2834d86..b8e3a43a3 100644 --- a/packages/auditor-backoffice-ui/src/components/modal/index.tsx +++ b/packages/auditor-backoffice-ui/src/components/modal/index.tsx @@ -19,14 +19,11 @@ * @author Sebastian Javier Marchano (sebasjm) */ +/** + * Imports. + */ import { useTranslationContext } from "@gnu-taler/web-util/browser"; import { ComponentChildren, Fragment, h, VNode } from "preact"; -import { useState } from "preact/hooks"; -import { useEntityContext } from "../../context/entity.js"; -import { DEFAULT_REQUEST_TIMEOUT } from "../../utils/constants.js"; -import { Spinner } from "../exception/loading.js"; -import { FormProvider } from "../forms/FormProvider.js"; -import { Input } from "../forms/Input.js"; interface Props { active?: boolean; @@ -95,402 +92,3 @@ export function ConfirmModal({ </div> ); } - -export function ContinueModal({ - active, - description, - onCancel, - onConfirm, - children, - disabled, -}: Props): VNode { - const { i18n } = useTranslationContext(); - return ( - <div class={active ? "modal is-active" : "modal"}> - <div class="modal-background " onClick={onCancel} /> - <div class="modal-card"> - <header class="modal-card-head has-background-success"> - {!description ? null : <p class="modal-card-title">{description}</p>} - <button class="delete " aria-label="close" onClick={onCancel} /> - </header> - <section class="modal-card-body">{children}</section> - <footer class="modal-card-foot"> - <div class="buttons is-right" style={{ width: "100%" }}> - <button - class="button is-success " - disabled={disabled} - onClick={onConfirm} - > - <i18n.Translate>Continue</i18n.Translate> - </button> - </div> - </footer> - </div> - <button - class="modal-close is-large " - aria-label="close" - onClick={onCancel} - /> - </div> - ); -} - -export function SimpleModal({ onCancel, children }: any): VNode { - return ( - <div class="modal is-active"> - <div class="modal-background " onClick={onCancel} /> - <div class="modal-card"> - <section class="modal-card-body is-main-section">{children}</section> - </div> - <button - class="modal-close is-large " - aria-label="close" - onClick={onCancel} - /> - </div> - ); -} - -export function ClearConfirmModal({ - description, - onCancel, - onClear, - onConfirm, - children, -}: Props & { onClear?: () => void }): VNode { - const { i18n } = useTranslationContext(); - return ( - <div class="modal is-active"> - <div class="modal-background " onClick={onCancel} /> - <div class="modal-card"> - <header class="modal-card-head"> - {!description ? null : <p class="modal-card-title">{description}</p>} - <button class="delete " aria-label="close" onClick={onCancel} /> - </header> - <section class="modal-card-body is-main-section">{children}</section> - <footer class="modal-card-foot"> - {onClear && ( - <button - class="button is-danger" - onClick={onClear} - disabled={onClear === undefined} - > - <i18n.Translate>Clear</i18n.Translate> - </button> - )} - <div class="buttons is-right" style={{ width: "100%" }}> - <button class="button " onClick={onCancel}> - <i18n.Translate>Cancel</i18n.Translate> - </button> - <button - class="button is-info" - onClick={onConfirm} - disabled={onConfirm === undefined} - > - <i18n.Translate>Confirm</i18n.Translate> - </button> - </div> - </footer> - </div> - <button - class="modal-close is-large " - aria-label="close" - onClick={onCancel} - /> - </div> - ); -} - -interface DeleteModalProps { - element: { id: string; name: string }; - onCancel: () => void; - onConfirm: (id: string) => void; -} - -export function DeleteModal({ - element, - onCancel, - onConfirm, -}: DeleteModalProps): VNode { - return ( - <ConfirmModal - label={`Delete instance`} - description={`Delete the instance "${element.name}"`} - danger - active - onCancel={onCancel} - onConfirm={() => onConfirm(element.id)} - > - <p> - If you delete the instance named <b>"{element.name}"</b> (ID:{" "} - <b>{element.id}</b>), the merchant will no longer be able to process - orders or refunds - </p> - <p> - This action deletes the instance private key, but preserves all - transaction data. You can still access that data after deleting the - instance. - </p> - <p class="warning"> - Deleting an instance <b>cannot be undone</b>. - </p> - </ConfirmModal> - ); -} - -export function PurgeModal({ - element, - onCancel, - onConfirm, -}: DeleteModalProps): VNode { - return ( - <ConfirmModal - label={`Purge the instance`} - description={`Purge the instance "${element.name}"`} - danger - active - onCancel={onCancel} - onConfirm={() => onConfirm(element.id)} - > - <p> - If you purge the instance named <b>"{element.name}"</b> (ID:{" "} - <b>{element.id}</b>), you will also delete all it's transaction - data. - </p> - <p> - The instance will disappear from your list, and you will no longer be - able to access it's data. - </p> - <p class="warning"> - Purging an instance <b>cannot be undone</b>. - </p> - </ConfirmModal> - ); -} - -interface UpdateTokenModalProps { - oldToken?: string; - onCancel: () => void; - onConfirm: (value: string) => void; - onClear: () => void; -} - -//FIXME: merge UpdateTokenModal with SetTokenNewInstanceModal -export function UpdateTokenModal({ - onCancel, - onClear, - onConfirm, - oldToken, -}: UpdateTokenModalProps): VNode { - type State = { old_token: string; new_token: string; repeat_token: string }; - const [form, setValue] = useState<Partial<State>>({ - old_token: "", - new_token: "", - repeat_token: "", - }); - const { i18n } = useTranslationContext(); - - const hasInputTheCorrectOldToken = oldToken && oldToken !== form.old_token; - const errors = { - old_token: hasInputTheCorrectOldToken - ? i18n.str`is not the same as the current access token` - : undefined, - new_token: !form.new_token - ? i18n.str`cannot be empty` - : form.new_token === form.old_token - ? i18n.str`cannot be the same as the old token` - : undefined, - repeat_token: - form.new_token !== form.repeat_token - ? i18n.str`is not the same` - : undefined, - }; - - const hasErrors = Object.keys(errors).some( - (k) => (errors as any)[k] !== undefined, - ); - - const instance = useEntityContext(); - - const text = i18n.str`You are updating the access token from instance with id `; - - return ( - <ClearConfirmModal - description={text} - onCancel={onCancel} - onConfirm={!hasErrors ? () => onConfirm(form.new_token!) : undefined} - onClear={!hasInputTheCorrectOldToken && oldToken ? onClear : undefined} - > - <div class="columns"> - <div class="column" /> - <div class="column is-four-fifths"> - <FormProvider errors={errors} object={form} valueHandler={setValue}> - {oldToken && ( - <Input<State> - name="old_token" - label={i18n.str`Old access token`} - tooltip={i18n.str`access token currently in use`} - inputType="password" - /> - )} - <Input<State> - name="new_token" - label={i18n.str`New access token`} - tooltip={i18n.str`next access token to be used`} - inputType="password" - /> - <Input<State> - name="repeat_token" - label={i18n.str`Repeat access token`} - tooltip={i18n.str`confirm the same access token`} - inputType="password" - /> - </FormProvider> - <p> - <i18n.Translate> - Clearing the access token will mean public access to the instance - </i18n.Translate> - </p> - </div> - <div class="column" /> - </div> - </ClearConfirmModal> - ); -} - -export function SetTokenNewInstanceModal({ - onCancel, - onClear, - onConfirm, -}: UpdateTokenModalProps): VNode { - type State = { old_token: string; new_token: string; repeat_token: string }; - const [form, setValue] = useState<Partial<State>>({ - new_token: "", - repeat_token: "", - }); - const { i18n } = useTranslationContext(); - - const errors = { - new_token: !form.new_token - ? i18n.str`cannot be empty` - : form.new_token === form.old_token - ? i18n.str`cannot be the same as the old access token` - : undefined, - repeat_token: - form.new_token !== form.repeat_token - ? i18n.str`is not the same` - : undefined, - }; - - const hasErrors = Object.keys(errors).some( - (k) => (errors as any)[k] !== undefined, - ); - - return ( - <div class="modal is-active"> - <div class="modal-background " onClick={onCancel} /> - <div class="modal-card"> - <header class="modal-card-head"> - <p class="modal-card-title">{i18n.str`You are setting the access token for the new instance`}</p> - <button class="delete " aria-label="close" onClick={onCancel} /> - </header> - <section class="modal-card-body is-main-section"> - <div class="columns"> - <div class="column" /> - <div class="column is-four-fifths"> - <FormProvider - errors={errors} - object={form} - valueHandler={setValue} - > - <Input<State> - name="new_token" - label={i18n.str`New access token`} - tooltip={i18n.str`next access token to be used`} - inputType="password" - /> - <Input<State> - name="repeat_token" - label={i18n.str`Repeat access token`} - tooltip={i18n.str`confirm the same access token`} - inputType="password" - /> - </FormProvider> - <p> - <i18n.Translate> - With external authorization method no check will be done by - the merchant backend - </i18n.Translate> - </p> - </div> - <div class="column" /> - </div> - </section> - <footer class="modal-card-foot"> - {onClear && ( - <button - class="button is-danger" - onClick={onClear} - disabled={onClear === undefined} - > - <i18n.Translate>Set external authorization</i18n.Translate> - </button> - )} - <div class="buttons is-right" style={{ width: "100%" }}> - <button class="button " onClick={onCancel}> - <i18n.Translate>Cancel</i18n.Translate> - </button> - <button - class="button is-info" - onClick={() => onConfirm(form.new_token!)} - disabled={hasErrors} - > - <i18n.Translate>Set access token</i18n.Translate> - </button> - </div> - </footer> - </div> - <button - class="modal-close is-large " - aria-label="close" - onClick={onCancel} - /> - </div> - ); -} - -export function LoadingModal({ onCancel }: { onCancel: () => void }): VNode { - const { i18n } = useTranslationContext(); - return ( - <div class="modal is-active"> - <div class="modal-background " onClick={onCancel} /> - <div class="modal-card"> - <header class="modal-card-head"> - <p class="modal-card-title"> - <i18n.Translate>Operation in progress...</i18n.Translate> - </p> - </header> - <section class="modal-card-body"> - <div class="columns"> - <div class="column" /> - <Spinner /> - <div class="column" /> - </div> - <p>{i18n.str`The operation will be automatically canceled after ${DEFAULT_REQUEST_TIMEOUT} seconds`}</p> - </section> - <footer class="modal-card-foot"> - <div class="buttons is-right" style={{ width: "100%" }}> - <button class="button " onClick={onCancel}> - <i18n.Translate>Cancel</i18n.Translate> - </button> - </div> - </footer> - </div> - <button - class="modal-close is-large " - aria-label="close" - onClick={onCancel} - /> - </div> - ); -} diff --git a/packages/auditor-backoffice-ui/src/context/backend.ts b/packages/auditor-backoffice-ui/src/context/backend.ts index ce321c3e6..0d041f069 100644 --- a/packages/auditor-backoffice-ui/src/context/backend.ts +++ b/packages/auditor-backoffice-ui/src/context/backend.ts @@ -25,39 +25,32 @@ import { useContext } from "preact/hooks"; import { useBackendURL } from "../hooks/index.js"; interface BackendContextType { - url: string, + url: string; } const BackendContext = createContext<BackendContextType>({ - url: "", + url: "", }); -function useBackendContextState( - defaultUrl?: string, -): BackendContextType { - const [url] = useBackendURL(defaultUrl); - - return { - url, - }; -} - export const BackendContextProvider = ({ - children, - defaultUrl, - }: { - children: any; - defaultUrl?: string; + children, + defaultUrl, +}: { + children: any; + defaultUrl?: string; }): VNode => { - const value = useBackendContextState(defaultUrl); - - return h(BackendContext.Provider, { value, children }); + const [url] = useBackendURL(defaultUrl); + + return h(BackendContext.Provider, { + value: { + url, + }, + children, + }); }; - - export const useBackendContext = (): BackendContextType => - useContext(BackendContext); + useContext(BackendContext); interface BackendTokenType { token: string; @@ -67,4 +60,5 @@ const BackendTokenContext = createContext<BackendTokenType>({} as any); export const BackendTokenContextProvider = BackendTokenContext.Provider; -export const useBackendTokenContext = (): BackendTokenType => useContext(BackendTokenContext);
\ No newline at end of file +export const useBackendTokenContext = (): BackendTokenType => + useContext(BackendTokenContext); diff --git a/packages/auditor-backoffice-ui/src/declaration.d.ts b/packages/auditor-backoffice-ui/src/declaration.d.ts index a8cdee53c..69839ef05 100644 --- a/packages/auditor-backoffice-ui/src/declaration.d.ts +++ b/packages/auditor-backoffice-ui/src/declaration.d.ts @@ -20,87 +20,11 @@ * @author Nic Eigel */ -type HashCode = string; -type EddsaPublicKey = string; -type EddsaSignature = string; -type WireTransferIdentifierRawP = string; -type RelativeTime = TalerProtocolDuration; -type ImageDataUrl = string; -type AuditorUserType = "business" | "individual"; - - export interface WithId { id: string; } -interface Timestamp { - // Milliseconds since epoch, or the special - // value "forever" to represent an event that will - // never happen. - t_s: number | "never"; -} - -interface TalerProtocolDuration { - d_us: number | "forever"; -} - -interface Duration { - d_ms: number | "forever"; -} - -interface WithId { - id: string; -} - -type Amount = string; -type UUID = string; -type Integer = number; - export namespace AuditorBackend { - interface DepositConfirmation { - // identifier - deposit_confirmation_serial_id: number; - - h_contract_terms: string; - - h_policy: string; - - h_wire: string; - - exchange_timestamp: string; - - refund_deadline: string; - - wire_deadline: string; - - total_without_fee: string; - - coin_pubs: string; - - coin_sigs: string; - - merchant_pub: string; - - merchant_sig: string; - - exchange_pub: string; - - exchange_sig: string; - - suppressed: string; - - ancient: string; - } - - interface Config { - name: string; - version: string; - implementation: string; - currency: string; - auditor_public_key: string; - exchange_master_public_key: string; - } - interface ErrorDetail { // Numeric error code unique to the condition. // The other arguments are specific to the error value reported here. @@ -175,18 +99,7 @@ export namespace AuditorBackend { // exchanges: ExchangeConfigInfo[]; } - export interface TokenResponse { - null; - } - - namespace Default { - interface ObjectResponse { - object: AnyEntry[]; - } - } - - namespace AmountArithmeticInconsistency { - + namespace AmountArithmeticInconsistency { class ClassAmountArithmeticInconsistency { data: AmountArithmeticInconsistencyDetail[]; } @@ -205,517 +118,7 @@ export namespace AuditorBackend { } } - namespace BadSigLoss { - class ClassBadSigLoss { - data: BadSigLossDetail[]; - } - - interface SummaryResponse { - amount_arithmetic_inconsistency: BadSigLossDetail[]; - } - - interface BadSigLossDetail - { - row_id: number; - operation: string; - loss: string; - operation_specific_pub: string; - suppressed: boolean; - } - } - - namespace Balance { - - class ClassBalance { - // List of products that are present in the inventory - data: BalanceDetail[]; - } - - interface SummaryResponse { - // List of products that are present in the inventory - balances: BalanceDetail[]; - } - - interface BalanceDetail { - // identifier - row_id: number; - - balance_key: string; - - balance_value: string; - - suppressed: boolean; - } - } - - namespace ClosureLag { - class ClassClosureLag { - // List of products that are present in the inventory - data: ClosureLagDetail[]; - } - - interface SummaryResponse { - // List of products that are present in the inventory - closure_lags: ClosureLagDetail[]; - } - - interface ClosureLagDetail { - row_id: number; - amount: string; - deadline: number; - wtid: number; - account: string; - suppressed: boolean; - } - } - - namespace CoinInconsistency { - class ClassCoinInconsistency { - data: CoinInconsistencyDetail[]; - } - - interface SummaryResponse { - amount_arithmetic_inconsistency: CoinInconsistencyDetail[]; - } - - interface CoinInconsistencyDetail - { - row_id: number; - operation: string; - exchange_amount: string; - auditor_amount: string; - coin_pub: string; - profitable: boolean; - suppressed: boolean; - } - } - - namespace DenominationKeyValidityWithdrawInconsistency { - class ClassDenominationKeyValidityWithdrawInconsistency { - data: DenominationKeyValidityWithdrawInconsistencyDetail[]; - } - - interface SummaryResponse { - responseData: DenominationKeyValidityWithdrawInconsistencyDetail[]; - } - - interface DenominationKeyValidityWithdrawInconsistencyDetail - { - row_id: number; - operation: string; - loss: string; - operation_specific_pub: string; - suppressed: boolean; - } - } - - namespace DenominationPending { - class ClassDenominationPending { - data: DenominationPendingDetail[]; - } - - interface SummaryResponse { - responseData: DenominationPendingDetail[]; - } - - interface DenominationPendingDetail - { - denom_pub_hash: string; - denom_balance: string; - denom_loss: string; - num_issued: number; - denom_risk: string; - recoup_loss: string; - suppressed: boolean; - } - } - - namespace DenominationsWithoutSigs { - class ClassDenominationsWithoutSigs { - data: DenominationsWithoutSigsDetail[]; - } - - interface SummaryResponse { - responseData: DenominationsWithoutSigsDetail[]; - } - - interface DenominationsWithoutSigsDetail - { - row_id: number; - denompub_h: string; - value: string; - start_time: number; - end_time: number; - suppressed: boolean; - } - } - - namespace DepositConfirmation { - class ClassDepositConfirmation{ - data: DepositConfirmationDetail[]; - } - - interface SummaryResponse { - responseData: DepositConfirmationDetail[]; - } - - interface DepositConfirmationDetail { - deposit_confirmation_serial_id: number; - h_contract_terms: string; - h_policy: string; - h_wire: string; - exchange_timestamp: string; - refund_deadline: string; - wire_deadline: string; - total_without_fee: string; - coin_pubs: string; - coin_sigs: string; - merchant_pub: string; - merchant_sig: string; - exchange_pub: string; - exchange_sig: string; - suppressed: string; - ancient: string; - } - } - - namespace Emergency { - class ClassEmergency{ - data: EmergencyDetail[]; - } - - interface SummaryResponse { - responseData: EmergencyDetail[]; - } - - interface EmergencyDetail - { - row_id: number; - denompub_h: string; - denom_risk: string; - denom_loss: string; - deposit_start: number; - deposit_end: number; - value: string; - } - } - - namespace EmergencyByCount { - class ClassEmergencyByCount{ - data: EmergencyByCountDetail[]; - } - - interface SummaryResponse { - responseData: EmergencyByCountDetail[]; - } - - interface EmergencyByCountDetail - { - row_id: number; - denompub_h: string; - num_issued: number; - num_known: number; - risk: string; - start: number; - deposit_end: number; - value: string; - suppressed: boolean; - } - } - - namespace FeeTimeInconsistency { - class ClassFeeTimeInconsistency{ - data: FeeTimeInconsistencyDetail[]; - } - - interface SummaryResponse { - responseData: FeeTimeInconsistencyDetail[]; - } - - interface FeeTimeInconsistencyDetail - { - row_id: number; - type: string; - time: string; - diagnostic: string; - suppressed: boolean; - } - } - - namespace HistoricDenominationRevenue { - class ClassHistoricDenominationRevenue { - data: HistoricDenominationRevenueDetail[]; - } - - interface SummaryResponse { - responseData: HistoricDenominationRevenueDetail[]; - } - - interface HistoricDenominationRevenueDetail - { - denom_pub_hash: string; - revenue_timestamp: number; - revenue_balance: string; - loss_balance: string; - suppressed: boolean; - } - } - - namespace HistoricReserveSummary { - class ClassHistoricReserveSummary { - data: HistoricReserveSummaryDetail[]; - } - - interface SummaryResponse { - responseData: HistoricReserveSummaryDetail[]; - } - - interface HistoricReserveSummaryDetail - { - denom_pub_hash: string; - revenue_timestamp: number; - revenue_balance: string; - loss_balance: string; - suppressed: boolean; - } - } - - namespace MisattributionInInconsistency { - class ClassMisattributionInInconsistency { - data: MisattributionInInconsistencyDetail[]; - } - - interface SummaryResponse { - responseData: MisattributionInInconsistencyDetail[]; - } - - interface MisattributionInInconsistencyDetail - { - row_id: number; - amount: string; - bank_row: number; - reserve_pub: string; - suppressed: boolean; - } - } - - namespace Progress { - class ClassProgress { - data: ProgressDetail[]; - } - - interface SummaryResponse { - responseData: ProgressDetail[]; - } - - interface ProgressDetail - { - progress_key: string; - progress_offset: number; - suppressed: boolean; - } - } - - namespace PurseNotClosedInconsistency { - class ClassPurseNotClosedInconsistency { - data: PurseNotClosedInconsistencyDetail[]; - } - - interface SummaryResponse { - responseData: PurseNotClosedInconsistencyDetail[]; - } - - interface PurseNotClosedInconsistencyDetail - { - row_id: number; - purse_pub: string, - amount: string; - expiration_date: number; - suppressed: boolean; - } - } - - namespace Purses { - class ClassPurses { - data: PursesDetail[]; - } - - interface SummaryResponse { - responseData: PursesDetail[]; - } - - interface PursesDetail - { - auditor_purses_rowid: number; - purse_pub: string; - balance: string; - target: string, - expiration_date: number; - suppressed: boolean; - } - } - - namespace RefreshesHanging { - class ClassRefreshesHanging { - data: RefreshesHangingDetail[]; - } - - interface SummaryResponse { - responseData: RefreshesHangingDetail[]; - } - - interface RefreshesHangingDetail - { - row_id: number; - amount: string; - coin_pub: string; - suppressed: boolean; - } - } - - namespace ReserveBalanceInsufficientInconsistency { - class ClassReserveBalanceInsufficientInconsistency { - data: ReserveBalanceInsufficientInconsistencyDetail[]; - } - - interface SummaryResponse { - responseData: ReserveBalanceInsufficientInconsistencyDetail[]; - } - - interface ReserveBalanceInsufficientInconsistencyDetail - { - row_id: number; - reserve_pub: string; - inconsistency_gain: boolean; - inconsistency_amount: string; - suppressed: boolean; - } - } - - namespace ReserveBalanceSummaryWrongInconsistency { - class ClassReserveBalanceSummaryWrongInconsistency { - data: ReserveBalanceSummaryWrongInconsistencyDetail[]; - } - - interface SummaryResponse { - responseData: ReserveBalanceSummaryWrongInconsistencyDetail[]; - } - - interface ReserveBalanceSummaryWrongInconsistencyDetail - { - row_id: number; - reserve_pub: string; - exchange_amount: string; - auditor_amount: string; - suppressed: boolean; - } - } - - namespace ReserveInInconsistency { - class ClassReserveInInconsistency { - data: ReserveInInconsistencyDetail[]; - } - - interface SummaryResponse { - responseData: ReserveInInconsistencyDetail[]; - } - - interface ReserveInInconsistencyDetail - { - row_id: number; - amount_exchange_expected: string; - amount_wired: string; - reserve_pub: string; - timestamp: number; - account: string; - diagnostic: string; - suppressed: boolean; - } - } - - namespace ReserveNotClosedInconsistency { - class ClassReserveNotClosedInconsistency { - data: ReserveNotClosedInconsistencyDetail[]; - } - - interface SummaryResponse { - responseData: ReserveNotClosedInconsistencyDetail[]; - } - - interface ReserveNotClosedInconsistencyDetail - { - row_id: number; - reserve_pub: string; - balance: string; - expiration_time: number; - diagnostic: string; - suppressed: boolean; - } - } - - namespace Reserves { - class ClassReserves{ - data: ReservesDetail[]; - } - - interface SummaryResponse { - responseData: ReservesDetail[]; - } - - interface ReservesDetail - { - auditor_reserves_rowid: number; - reserve_pub: string; - reserve_balance: string; - reserve_loss: string; - withdraw_fee_balance: string; - close_fee_balance: string; - purse_fee_balance: string; - open_fee_balance: string; - history_fee_balance: string; - expiration_date: number; - origin_account: string; - suppressed: boolean; - } - } - - namespace RowInconsistency { - class ClassRowInconsistency { - data: RowInconsistencyDetail[]; - } - - interface SummaryResponse { - responseData: RowInconsistencyDetail[]; - } - - interface RowInconsistencyDetail - { - row_id: number; - row_table: string; - diagnostic: string; - suppressed: boolean; - } - } - - namespace RowMinorInconsistency { - class ClassRowMinorInconsistency { - data: RowMinorInconsistencyDetail[]; - } - - interface SummaryResponse { - responseData: RowMinorInconsistencyDetail[]; - } - - interface RowMinorInconsistencyDetail - { - row_id: number; - row_table: string; - diagnostic: string; - suppressed: boolean; - } - } - - namespace WireFormatInconsistency { + namespace WireFormatInconsistency { class ClassWireFormatInconsistency { data: WireFormatInconsistencyDetail[]; } @@ -724,8 +127,7 @@ export namespace AuditorBackend { responseData: WireFormatInconsistencyDetail[]; } - interface WireFormatInconsistencyDetail - { + interface WireFormatInconsistencyDetail { row_id: number; amount: string; wire_offset: string; @@ -733,23 +135,4 @@ export namespace AuditorBackend { suppressed: boolean; } } - - namespace WireOutInconsistency { - class ClassWireOutInconsistency{ - data: WireOutInconsistencyDetail[]; - } - - interface SummaryResponse { - responseData: WireOutInconsistencyDetail[]; - } - - interface WireOutInconsistencyDetail - { - row_id: number; - destination_account: string; - expected: string; - claimed: string; - suppressed: boolean; - } - } -}
\ No newline at end of file +} diff --git a/packages/auditor-backoffice-ui/src/hooks/backend.ts b/packages/auditor-backoffice-ui/src/hooks/backend.ts index 4b0a5a828..69b63e02b 100644 --- a/packages/auditor-backoffice-ui/src/hooks/backend.ts +++ b/packages/auditor-backoffice-ui/src/hooks/backend.ts @@ -20,150 +20,160 @@ * @author Nic Eigel */ +/** + * Imports. + */ import { - HttpResponse, - HttpResponseOk, - RequestError, - RequestOptions, - useApiContext, + HttpResponse, + HttpResponseOk, + RequestError, + RequestOptions, + useApiContext, } from "@gnu-taler/web-util/browser"; -import {useCallback, useEffect, useState} from "preact/hooks"; -import {useSWRConfig} from "swr"; +import { useCallback, useEffect, useState } from "preact/hooks"; +import { useSWRConfig } from "swr"; import { useBackendContext } from "../context/backend.js"; import { AuditorBackend } from "../declaration.js"; export function useMatchMutate(): ( - re?: RegExp, - value?: unknown, + 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", - ); - } + const { cache, mutate } = useSWRConfig(); - return function matchRegexMutate(re?: RegExp) { - return mutate((key) => { - // evict if no key or regex === all - if (!key || !re) return true - // match string - if (typeof key === 'string' && re.test(key)) return true - // record or object have the path at [0] - if (typeof key === 'object' && re.test(key[0])) return true - //key didn't match regex - return false - }, undefined, { - revalidate: true, - }); - }; + if (!(cache instanceof Map)) { + throw new Error( + "matchMutate requires the cache provider to be a Map instance", + ); + } + + return function matchRegexMutate(re?: RegExp) { + return mutate( + (key) => { + // evict if no key or regex === all + if (!key || !re) return true; + // match string + if (typeof key === "string" && re.test(key)) return true; + // record or object have the path at [0] + if (typeof key === "object" && re.test(key[0])) return true; + //key didn't match regex + return false; + }, + undefined, + { + revalidate: true, + }, + ); + }; } const CHECK_CONFIG_INTERVAL_OK = 5 * 60 * 1000; const CHECK_CONFIG_INTERVAL_FAIL = 2 * 1000; export function useBackendConfig(): HttpResponse< - AuditorBackend.VersionResponse | undefined, - RequestError<AuditorBackend.ErrorDetail> + AuditorBackend.VersionResponse | undefined, + RequestError<AuditorBackend.ErrorDetail> > { - const {request} = useBackendBaseRequest(); - - type Type = AuditorBackend.VersionResponse; - type State = { data: HttpResponse<Type, RequestError<AuditorBackend.ErrorDetail>>, timer: number } - const [result, setResult] = useState<State>({data: {loading: true}, timer: 0}); - - useEffect(() => { - if (result.timer) { - clearTimeout(result.timer); - } + const { request } = useBackendBaseRequest(); + + type Type = AuditorBackend.VersionResponse; + type State = { + data: HttpResponse<Type, RequestError<AuditorBackend.ErrorDetail>>; + timer: number; + }; + const [result, setResult] = useState<State>({ + data: { loading: true }, + timer: 0, + }); + + useEffect(() => { + if (result.timer) { + clearTimeout(result.timer); + } - function tryConfig(): void { - request<Type>(`/config`) - .then((data) => { - const timer: any = setTimeout(() => { - tryConfig(); - }, CHECK_CONFIG_INTERVAL_OK); - setResult({data, timer}); - }) - .catch((error) => { - const timer: any = setTimeout(() => { - tryConfig(); - }, CHECK_CONFIG_INTERVAL_FAIL); - const data = error.cause; - setResult({data, timer}); - }); - } + function tryConfig(): void { + request<Type>("config") + .then((data) => { + const timer: any = setTimeout(() => { + tryConfig(); + }, CHECK_CONFIG_INTERVAL_OK); + setResult({ data, timer }); + }) + .catch((error) => { + const timer: any = setTimeout(() => { + tryConfig(); + }, CHECK_CONFIG_INTERVAL_FAIL); + const data = error.cause; + setResult({ data, timer }); + }); + } - tryConfig(); - }, [request]); + tryConfig(); + }, [request]); - return result.data; + return result.data; } export function useBackendToken(): HttpResponse< AuditorBackend.VersionResponse, RequestError<AuditorBackend.ErrorDetail> > { - const {request} = useBackendBaseRequest(); - - type Type = AuditorBackend.VersionResponse; - type State = { data: HttpResponse<Type, RequestError<AuditorBackend.ErrorDetail>>, timer: number } - const [result, setResult] = useState<State>({data: {loading: true}, timer: 0}); - - useEffect(() => { - if (result.timer) { - clearTimeout(result.timer); - } + const { request } = useBackendBaseRequest(); + + type Type = AuditorBackend.VersionResponse; + type State = { + data: HttpResponse<Type, RequestError<AuditorBackend.ErrorDetail>>; + timer: number; + }; + const [result, setResult] = useState<State>({ + data: { loading: true }, + timer: 0, + }); + + useEffect(() => { + if (result.timer) { + clearTimeout(result.timer); + } - function tryToken(): void { - request<Type>(`/monitoring/balances`) - .then((data) => { - const timer: any = setTimeout(() => { - tryToken(); - }, CHECK_CONFIG_INTERVAL_OK); - setResult({data, timer}); - }) - .catch((error) => { - const timer: any = setTimeout(() => { - tryToken(); - }, CHECK_CONFIG_INTERVAL_FAIL); - const data = error.cause; - setResult({data, timer}); - }); - } + function tryToken(): void { + request<Type>(`/monitoring/balances`) + .then((data) => { + const timer: any = setTimeout(() => { + tryToken(); + }, CHECK_CONFIG_INTERVAL_OK); + setResult({ data, timer }); + }) + .catch((error) => { + const timer: any = setTimeout(() => { + tryToken(); + }, CHECK_CONFIG_INTERVAL_FAIL); + const data = error.cause; + setResult({ data, timer }); + }); + } - tryToken(); - }, [request]); + tryToken(); + }, [request]); - return result.data; + return result.data; } interface useBackendInstanceRequestType { - - request: <T>( - endpoint: string, - options?: RequestOptions, - ) => Promise<HttpResponseOk<T>>; - fetcher: <T>(endpoint: string) => Promise<HttpResponseOk<T>>; - multiFetcher: <T>(params: string[]) => Promise<HttpResponseOk<T>[]>; - depositConfirmationFetcher: <T>( - params: [ - endpoint: string, - ], - ) => Promise<HttpResponseOk<T>>; + request: <T>( + endpoint: string, + options?: RequestOptions, + ) => Promise<HttpResponseOk<T>>; + fetcher: <T>(endpoint: string) => Promise<HttpResponseOk<T>>; + multiFetcher: <T>(params: string[]) => Promise<HttpResponseOk<T>[]>; } interface useBackendBaseRequestType { - - request: <T>( - endpoint: string, - options?: RequestOptions - ) => Promise<HttpResponseOk<T>>; + request: <T>( + endpoint: string, + options?: RequestOptions, + ) => Promise<HttpResponseOk<T>>; } -type YesOrNo = "yes" | "no"; - /** * * @param root the request is intended to the base URL and no the instance URL @@ -171,91 +181,67 @@ type YesOrNo = "yes" | "no"; */ //TODO: Add token export function useBackendBaseRequest(): useBackendBaseRequestType { - const {url: backend} = useBackendContext(); - const {request: requestHandler} = useApiContext(); - //const { token } = useBackendTokenContext(); - const token = "D4CST1Z6AHN3RT03M0T9NSTF2QGHTB5ZD2D3RYZB4HAWG8SX0JEFWBXCKXZHMB7Y3Z7KVFW0B3XPXD5BHCFP8EB0R6CNH2KAWDWVET0"; - - - const request = useCallback( - function requestImpl<T>( - endpoint: string, - //todo: remove - options: RequestOptions = {}, - ): Promise<HttpResponseOk<T>> { - return requestHandler<T>(backend, endpoint, {...options, token}).then(res => { - return res; - }).catch(err => { - throw err; - }); - }, - [backend], - ); + const { url: backend } = useBackendContext(); + const { request: requestHandler } = useApiContext(); + + const request = useCallback( + function requestImpl<T>( + endpoint: string, + //todo: remove + options: RequestOptions = {}, + ): Promise<HttpResponseOk<T>> { + return requestHandler<T>(backend, endpoint, { ...options }) + .then((res) => { + return res; + }) + .catch((err) => { + throw err; + }); + }, + [backend], + ); - return {request}; + return { request }; } - export function useBackendRequest(): useBackendInstanceRequestType { - const {url: rootBackendUrl} = useBackendContext(); - // const {id} = useInstanceContext(); - const {request: requestHandler} = useApiContext(); - - //TODO: check - const baseUrl = "http://localhost:8083/"; - const token = "D4CST1Z6AHN3RT03M0T9NSTF2QGHTB5ZD2D3RYZB4HAWG8SX0JEFWBXCKXZHMB7Y3Z7KVFW0B3XPXD5BHCFP8EB0R6CNH2KAWDWVET0"; - - - - - const request = useCallback( - function requestImpl<T>( - endpoint: string, - options: RequestOptions = {}, - ): Promise<HttpResponseOk<T>> { - return requestHandler<T>(baseUrl, endpoint, {...options, token}); - }, - [baseUrl], - ); - - const multiFetcher = useCallback( - function multiFetcherImpl<T>( - params: string[], - options: RequestOptions = {}, - ): Promise<HttpResponseOk<T>[]> { - return Promise.all( - params.map((endpoint) => - requestHandler<T>(baseUrl, endpoint, {...options, token}), - ), - ); - }, - [baseUrl], - ); - - const fetcher = useCallback( - function fetcherImpl<T>(endpoint: string): Promise<HttpResponseOk<T>> { - return requestHandler<T>(baseUrl, endpoint, {token}); - }, - [baseUrl], - ); - - const depositConfirmationFetcher = useCallback( - function orderFetcherImpl<T>( - args: [endpoint: string, - ], - ): Promise<HttpResponseOk<T>> { - const [endpoint] = args; - const params: any = {"token": "D4CST1Z6AHN3RT03M0T9NSTF2QGHTB5ZD2D3RYZB4HAWG8SX0JEFWBXCKXZHMB7Y3Z7KVFW0B3XPXD5BHCFP8EB0R6CNH2KAWDWVET0"}; - return requestHandler<T>(baseUrl, endpoint, {params, token}); - }, - [baseUrl], - ); - - - return { - request, - fetcher, - depositConfirmationFetcher, - multiFetcher - }; -}
\ No newline at end of file + const { url: baseUrl } = useBackendContext(); + const { request: requestHandler } = useApiContext(); + + const request = useCallback( + function requestImpl<T>( + endpoint: string, + options: RequestOptions = {}, + ): Promise<HttpResponseOk<T>> { + return requestHandler<T>(baseUrl, endpoint, { ...options }); + }, + [baseUrl], + ); + + const multiFetcher = useCallback( + function multiFetcherImpl<T>( + params: string[], + options: RequestOptions = {}, + ): Promise<HttpResponseOk<T>[]> { + return Promise.all( + params.map((endpoint) => + requestHandler<T>(baseUrl, endpoint, { ...options }), + ), + ); + }, + [baseUrl], + ); + + const fetcher = useCallback( + function fetcherImpl<T>(endpoint: string): Promise<HttpResponseOk<T>> { + return requestHandler<T>(baseUrl, endpoint, {}); + }, + [baseUrl], + ); + + return { + request, + fetcher, + multiFetcher, + }; +} diff --git a/packages/auditor-backoffice-ui/src/hooks/critical.ts b/packages/auditor-backoffice-ui/src/hooks/critical.ts index 6a25d3037..8283fefbb 100644 --- a/packages/auditor-backoffice-ui/src/hooks/critical.ts +++ b/packages/auditor-backoffice-ui/src/hooks/critical.ts @@ -16,16 +16,13 @@ import { HttpResponse, HttpResponseOk, - HttpResponsePaginated, RequestError, } from "@gnu-taler/web-util/browser"; -import { useEffect, useState } from "preact/hooks"; -import { AuditorBackend, WithId } from "../declaration.js"; -import { MAX_RESULT_SIZE, PAGE_SIZE } from "../utils/constants.js"; -import { useBackendRequest, useMatchMutate } from "./backend.js"; +import { AuditorBackend } from "../declaration.js"; +import { useBackendRequest } from "./backend.js"; // FIX default import https://github.com/microsoft/TypeScript/issues/49189 -import _useSWR, { SWRHook, useSWRConfig } from "swr"; +import _useSWR, { SWRHook } from "swr"; const useSWR = _useSWR as unknown as SWRHook; @@ -50,9 +47,9 @@ export function getCriticalData( "monitoring/reserve-balance-insufficient-inconsistency", ]; - const { data: list, error: listError } = useSWR< - HttpResponseOk<any>[], RequestError<AuditorBackend.ErrorDetail> + HttpResponseOk<any>[], + RequestError<AuditorBackend.ErrorDetail> >(endpoints, multiFetcher, { refreshInterval: 60, refreshWhenHidden: false, @@ -67,4 +64,4 @@ export function getCriticalData( return { ok: true, data: [list] }; } return { loading: true }; -}
\ No newline at end of file +} diff --git a/packages/auditor-backoffice-ui/src/hooks/entity.ts b/packages/auditor-backoffice-ui/src/hooks/entity.ts index ae62da35e..3cfdd8616 100644 --- a/packages/auditor-backoffice-ui/src/hooks/entity.ts +++ b/packages/auditor-backoffice-ui/src/hooks/entity.ts @@ -13,28 +13,33 @@ 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/> */ + +/** + * Imports. + */ import { HttpResponse, HttpResponseOk, RequestError, } from "@gnu-taler/web-util/browser"; -import { AuditorBackend, WithId } from "../declaration.js"; +import { AuditorBackend } from "../declaration.js"; import { useBackendRequest, useMatchMutate } from "./backend.js"; // FIX default import https://github.com/microsoft/TypeScript/issues/49189 -import _useSWR, { SWRHook, useSWRConfig } from "swr"; +import _useSWR, { SWRHook } from "swr"; import { useEntityContext } from "../context/entity.js"; const useSWR = _useSWR as unknown as SWRHook; -type YesOrNo = "yes" | "no"; - interface Props { endpoint: string; entity: any; } -export function getEntityList({ endpoint, entity }: Props): HttpResponse<any, AuditorBackend.ErrorDetail> { +export function getEntityList({ + endpoint, + entity, +}: Props): HttpResponse<any, AuditorBackend.ErrorDetail> { const { fetcher } = useBackendRequest(); const { data: list, error: listError } = useSWR< @@ -56,20 +61,16 @@ export function getEntityList({ endpoint, entity }: Props): HttpResponse<any, Au return { loading: true }; } export interface EntityAPI { - updateEntity: ( - id: string - ) => Promise<void>; + updateEntity: (id: string) => Promise<void>; } export function useEntityAPI(): EntityAPI { const mutateAll = useMatchMutate(); const { request } = useBackendRequest(); - const { endpoint } = useEntityContext(); - const data = {"suppressed": true}; + const { endpoint } = useEntityContext(); + const data = { suppressed: true }; - const updateEntity = async ( - id: string, - ): Promise<void> => { + const updateEntity = async (id: string): Promise<void> => { const r = await request(`monitoring/${endpoint}/${id}`, { method: "PATCH", data, @@ -79,4 +80,4 @@ export function useEntityAPI(): EntityAPI { }; return { updateEntity }; -}
\ No newline at end of file +} diff --git a/packages/auditor-backoffice-ui/src/hooks/finance.ts b/packages/auditor-backoffice-ui/src/hooks/finance.ts index 97bf2577f..a0d035735 100644 --- a/packages/auditor-backoffice-ui/src/hooks/finance.ts +++ b/packages/auditor-backoffice-ui/src/hooks/finance.ts @@ -13,21 +13,27 @@ 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/> */ + +/** + * Imports. + */ import { HttpResponse, HttpResponseOk, RequestError, } from "@gnu-taler/web-util/browser"; -import { AuditorBackend, WithId } from "../declaration.js"; -import { useBackendRequest, useMatchMutate } from "./backend.js"; +import { AuditorBackend } from "../declaration.js"; +import { useBackendRequest } from "./backend.js"; // FIX default import https://github.com/microsoft/TypeScript/issues/49189 -import _useSWR, { SWRHook, useSWRConfig } from "swr"; +import _useSWR, { SWRHook } from "swr"; const useSWR = _useSWR as unknown as SWRHook; - -export function getKeyFiguresData(): HttpResponse<any, AuditorBackend.ErrorDetail> { +export function getKeyFiguresData(): HttpResponse< + any, + AuditorBackend.ErrorDetail +> { const { multiFetcher } = useBackendRequest(); const endpoints = [ "monitoring/misattribution-in-inconsistency", @@ -39,11 +45,11 @@ export function getKeyFiguresData(): HttpResponse<any, AuditorBackend.ErrorDetai "monitoring/wire-format-inconsistency", "monitoring/wire-out-inconsistency", "monitoring/reserve-balance-summary-wrong-inconsistency", - ]; const { data: list, error: listError } = useSWR< - HttpResponseOk<any>[], RequestError<AuditorBackend.ErrorDetail> + HttpResponseOk<any>[], + RequestError<AuditorBackend.ErrorDetail> >(endpoints, multiFetcher, { refreshInterval: 60, refreshWhenHidden: false, diff --git a/packages/auditor-backoffice-ui/src/hooks/index.ts b/packages/auditor-backoffice-ui/src/hooks/index.ts index cf1c57771..b844c49cf 100644 --- a/packages/auditor-backoffice-ui/src/hooks/index.ts +++ b/packages/auditor-backoffice-ui/src/hooks/index.ts @@ -14,66 +14,67 @@ GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> */ -import {StateUpdater, useState} from "preact/hooks"; +import { canonicalizeBaseUrl } from "@gnu-taler/taler-util"; +import { StateUpdater, useState } from "preact/hooks"; import { ValueOrFunction } from "../utils/types.js"; -export function useBackendURL( - url?: string, -): [string, StateUpdater<string>] { - const [value, setter] = useSimpleLocalStorage( - "auditor-base-url", - url || calculateRootPath(), - ); +export function useBackendURL(url?: string): [string, StateUpdater<string>] { + const canonUrl = canonicalizeBaseUrl(url ?? calculateRootPath()); + + const [value, setter] = useSimpleLocalStorage("auditor-base-url", canonUrl); - const checkedSetter = (v: ValueOrFunction<string>) => { - return setter((p) => (v instanceof Function ? v(p ?? "") : v).replace(/\/$/, "")); - }; + const checkedSetter = (v: ValueOrFunction<string>) => { + // FIXME: Explain?! + return setter((p) => + (v instanceof Function ? v(p ?? "") : v).replace(/\/$/, ""), + ); + }; - return [value!, checkedSetter]; + return [value!, checkedSetter]; } -const calculateRootPath = () => { - const rootPath = - typeof window !== undefined - ? window.location.origin + window.location.pathname - : "/"; +function calculateRootPath() { + const rootPath = + typeof window !== undefined + ? window.location.origin + window.location.pathname + : "/"; - /** - * By default, auditor backend serves the html content - * from the /webui root. This should cover most of the - * cases and the rootPath will be the auditor backend - * URL where the instances are - */ - return rootPath.replace("/webui/", ""); -}; + /* + * By default, auditor backend serves the html content + * from the /webui root. This should cover most of the + * cases and the rootPath will be the auditor backend + * URL where the instances are + */ + return rootPath.replace("/spa/", ""); +} -export function useSimpleLocalStorage( - key: string, - initialValue?: string, +function useSimpleLocalStorage( + key: string, + initialValue?: string, ): [string | undefined, StateUpdater<string | undefined>] { - const [storedValue, setStoredValue] = useState<string | undefined>( - (): string | undefined => { - return typeof window !== "undefined" - ? window.localStorage.getItem(key) || initialValue - : initialValue; - }, - ); + const [storedValue, setStoredValue] = useState<string | undefined>( + (): string | undefined => { + return typeof window !== "undefined" + ? window.localStorage.getItem(key) || initialValue + : initialValue; + }, + ); - const setValue = ( - value?: string | ((val?: string) => string | undefined), - ) => { - setStoredValue((p) => { - const toStore = value instanceof Function ? value(p) : value; - if (typeof window !== "undefined") { - if (!toStore) { - window.localStorage.removeItem(key); - } else { - window.localStorage.setItem(key, toStore); - } - } - return toStore; - }); - }; + const setValue = ( + value?: string | ((val?: string) => string | undefined), + ) => { + setStoredValue((p) => { + const toStore = value instanceof Function ? value(p) : value; + if (typeof window !== "undefined") { + if (!toStore) { + window.localStorage.removeItem(key); + } else { + window.localStorage.setItem(key, toStore); + } + } + return toStore; + }); + }; - return [storedValue, setValue]; -}
\ No newline at end of file + return [storedValue, setValue]; +} diff --git a/packages/auditor-backoffice-ui/src/hooks/operational.ts b/packages/auditor-backoffice-ui/src/hooks/operational.ts index 89524f24e..c40a1423c 100644 --- a/packages/auditor-backoffice-ui/src/hooks/operational.ts +++ b/packages/auditor-backoffice-ui/src/hooks/operational.ts @@ -18,11 +18,11 @@ import { HttpResponseOk, RequestError, } from "@gnu-taler/web-util/browser"; -import { AuditorBackend, WithId } from "../declaration.js"; -import { useBackendRequest, useMatchMutate } from "./backend.js"; +import { AuditorBackend } from "../declaration.js"; +import { useBackendRequest } from "./backend.js"; // FIX default import https://github.com/microsoft/TypeScript/issues/49189 -import _useSWR, { SWRHook, useSWRConfig } from "swr"; +import _useSWR, { SWRHook } from "swr"; const useSWR = _useSWR as unknown as SWRHook; @@ -49,17 +49,16 @@ export function getOperationData( "monitoring/denomination-key-validity-withdraw-inconsistency", "monitoring/refreshes-hanging", //TODO fix endpoint - // "monitoring/closure-lags", - // "monitoring/row-minor-inconsistencies", - // "monitoring/historic-denomination-revenue", - // "monitoring/denomination-pending", + // "monitoring/closure-lags", + // "monitoring/row-minor-inconsistencies", + // "monitoring/historic-denomination-revenue", + // "monitoring/denomination-pending", "monitoring/historic-reserve-summary", - ]; - const { data: list, error: listError } = useSWR< - HttpResponseOk<any>[], RequestError<AuditorBackend.ErrorDetail> + HttpResponseOk<any>[], + RequestError<AuditorBackend.ErrorDetail> >(endpoints, multiFetcher, { refreshInterval: 60, refreshWhenHidden: false, @@ -77,7 +76,5 @@ export function getOperationData( } export interface EntityAPI { - updateEntity: ( - id: string, - ) => Promise<void>; -}
\ No newline at end of file + updateEntity: (id: string) => Promise<void>; +} diff --git a/packages/auditor-backoffice-ui/src/index.tsx b/packages/auditor-backoffice-ui/src/index.tsx index fc956e8aa..7fdf7c1c3 100644 --- a/packages/auditor-backoffice-ui/src/index.tsx +++ b/packages/auditor-backoffice-ui/src/index.tsx @@ -21,4 +21,4 @@ import "./scss/main.scss"; const app = document.getElementById("app"); -render(<Application />, app as any);
\ No newline at end of file +render(<Application />, app as any); diff --git a/packages/auditor-backoffice-ui/src/paths/default/Table.tsx b/packages/auditor-backoffice-ui/src/paths/default/Table.tsx index 9bb75907d..05db3fc43 100644 --- a/packages/auditor-backoffice-ui/src/paths/default/Table.tsx +++ b/packages/auditor-backoffice-ui/src/paths/default/Table.tsx @@ -20,16 +20,18 @@ */ import { useTranslationContext } from "@gnu-taler/web-util/browser"; -import { ComponentChildren, Fragment, h, VNode } from "preact"; -import { StateUpdater, useState } from "preact/hooks"; -import { useEntityContext, useEntityDataContext } from "../../context/entity.js"; +import { Fragment, h, VNode } from "preact"; +import { useState } from "preact/hooks"; +import { + useEntityContext, + useEntityDataContext, +} from "../../context/entity.js"; interface Props { onSuppress: (id: any) => void; } -export function CardTable({onSuppress}: Props): any { - +export function CardTable({ onSuppress }: Props): any { const data = useEntityDataContext(); const [rowSelection, rowSelectionHandler] = useState<string | undefined>( undefined, @@ -46,17 +48,14 @@ export function CardTable({onSuppress}: Props): any { </span> <i18n.Translate>{title}</i18n.Translate> </p> - <div class="card-header-icon" aria-label="more options"> - </div> + <div class="card-header-icon" aria-label="more options"></div> </header> <div class="card-content"> <div class="b-table has-pagination"> <div class="table-wrapper has-mobile-cards"> - {(data.data[0][endpoint] !== undefined && data.data[0][endpoint].length != 0) ? ( - <Table - data={data.data[0][endpoint]} - onSuppress={onSuppress} - /> + {data.data[0][endpoint] !== undefined && + data.data[0][endpoint].length != 0 ? ( + <Table data={data.data[0][endpoint]} onSuppress={onSuppress} /> ) : ( <EmptyTable /> )} @@ -72,10 +71,7 @@ interface TableProps { onSuppress: (id: any) => void; } -function Table({ - data, - onSuppress, - }: TableProps): VNode { +function Table({ data, onSuppress }: TableProps): VNode { const { i18n } = useTranslationContext(); const { entity } = useEntityContext(); type Entity = typeof entity; @@ -85,51 +81,50 @@ function Table({ <div class="table-container"> <table class="table is-fullwidth is-striped is-hoverable is-fullwidth"> <thead> - <tr> - {Object.keys(data[0]).map((i: Entity) => { - const paramName = i[0].toUpperCase() + i.replace("_", " ").slice(1, i.count); - return ( - <Fragment key={count.toString() + i}> - <th> - <i18n.Translate>{paramName}</i18n.Translate> - </th> - </Fragment>); - })} - </tr> + <tr> + {Object.keys(data[0]).map((i: Entity) => { + const paramName = + i[0].toUpperCase() + i.replace("_", " ").slice(1, i.count); + return ( + <Fragment key={count.toString() + i}> + <th> + <i18n.Translate>{paramName}</i18n.Translate> + </th> + </Fragment> + ); + })} + </tr> </thead> <tbody> - {data.map((key: Entity, value: string) => { - return ( - <tr> - {Object.keys(data[0]).map((i: Entity) => { - return ( - <Fragment> - <td> - {(key[i] == false) ? "false" : key[i]} - </td> - </Fragment> - ); - })} - <td class="is-actions-cell right-sticky"> - <div class="buttons is-right"> - <span - class="has-tooltip-bottom" - data-tooltip={i18n.str`suppress`} + {data.map((key: Entity, value: string) => { + return ( + <tr> + {Object.keys(data[0]).map((i: Entity) => { + return ( + <Fragment> + <td>{key[i] == false ? "false" : key[i]}</td> + </Fragment> + ); + })} + <td class="is-actions-cell right-sticky"> + <div class="buttons is-right"> + <span + class="has-tooltip-bottom" + data-tooltip={i18n.str`suppress`} + > + <button + class="button is-small is-success " + type="button" + onClick={(): void => onSuppress(key["row_id"])} > - <button - class="button is-small is-success " - type="button" - onClick={(): void => onSuppress(key["row_id"])} - > - {<i18n.Translate>Suppress</i18n.Translate>} - </button> - </span> - </div> - </td> - </tr> - ); - }) - } + {<i18n.Translate>Suppress</i18n.Translate>} + </button> + </span> + </div> + </td> + </tr> + ); + })} </tbody> </table> </div> @@ -142,14 +137,12 @@ function EmptyTable(): VNode { <div class="content has-text-grey has-text-centered"> <p> <span class="icon is-large"> - <i class="mdi mdi-emoticon-happy mdi-48px" /> + <i class="mdi mdi-emoticon-happy mdi-48px" /> </span> </p> <p> - <i18n.Translate> - There are no entries yet - </i18n.Translate> + <i18n.Translate>There are no entries yet</i18n.Translate> </p> </div> ); -}
\ No newline at end of file +} diff --git a/packages/auditor-backoffice-ui/src/paths/default/index.tsx b/packages/auditor-backoffice-ui/src/paths/default/index.tsx index 1b7758190..1bcd17b1c 100644 --- a/packages/auditor-backoffice-ui/src/paths/default/index.tsx +++ b/packages/auditor-backoffice-ui/src/paths/default/index.tsx @@ -19,41 +19,40 @@ * @author Sebastian Javier Marchano (sebasjm) */ +import { HttpStatusCode } from "@gnu-taler/taler-util"; import { ErrorType, HttpError, useTranslationContext, } from "@gnu-taler/web-util/browser"; import { h, VNode } from "preact"; -import { useState } from "preact/hooks"; +import { route } from "preact-router"; +import { useMemo, useState } from "preact/hooks"; import { Loading } from "../../components/exception/loading.js"; import { NotificationCard } from "../../components/menu/index.js"; +import { ConfirmModal } from "../../components/modal/index.js"; +import { + EntityDataContextProvider, + useEntityContext, +} from "../../context/entity.js"; import { AuditorBackend, WithId } from "../../declaration.js"; -import { Notification } from "../../utils/types.js"; -import { CardTable } from "./Table.js"; -import { HttpStatusCode } from "@gnu-taler/taler-util"; -import { EntityDataContextProvider, useEntityContext } from "../../context/entity.js"; import { getEntityList, useEntityAPI } from "../../hooks/entity.js"; -import { useMemo } from "preact/hooks"; -import { ConfirmModal, DeleteModal } from "../../components/modal/index.js"; -import { route } from "preact-router"; import { Paths } from "../../InstanceRoutes.js"; - +import { Notification } from "../../utils/types.js"; +import { CardTable } from "./Table.js"; interface Props { onNotFound: () => VNode; onLoadError: (e: HttpError<AuditorBackend.ErrorDetail>) => VNode; } -export default function DefaultList({ - onLoadError, - onNotFound, - }: Props): VNode { +export default function DefaultList({ onLoadError, onNotFound }: Props): VNode { const { endpoint, entity } = useEntityContext(); const result = getEntityList({ endpoint, entity }); const { updateEntity } = useEntityAPI(); - const [suppressing, setSuppressing] = - useState<typeof entity & WithId | null>(null); + const [suppressing, setSuppressing] = useState< + (typeof entity & WithId) | null + >(null); const [notif, setNotif] = useState<Notification | undefined>(undefined); const { i18n } = useTranslationContext(); @@ -68,31 +67,24 @@ export default function DefaultList({ } let data = result.data; - const value = useMemo( - () => ({ data }), - [data], - ); + const value = useMemo(() => ({ data }), [data]); function onReturn(): void { route(Paths.detail_view); } return ( - <section class="section is-main-section"> - <button - class="button is-fullwidth" - onClick={onReturn} - >Back - </button><br /> + <button class="button is-fullwidth" onClick={onReturn}> + Back + </button> + <br /> <NotificationCard notification={notif} /> <EntityDataContextProvider value={value}> <CardTable - onSuppress={(e: typeof entity & WithId) => - setSuppressing(e) - } + onSuppress={(e: typeof entity & WithId) => setSuppressing(e)} /> </EntityDataContextProvider> diff --git a/packages/auditor-backoffice-ui/src/paths/details/ListPage.tsx b/packages/auditor-backoffice-ui/src/paths/details/ListPage.tsx index 60ae7b578..602ce5ef0 100644 --- a/packages/auditor-backoffice-ui/src/paths/details/ListPage.tsx +++ b/packages/auditor-backoffice-ui/src/paths/details/ListPage.tsx @@ -20,10 +20,9 @@ */ import { useTranslationContext } from "@gnu-taler/web-util/browser"; -import { h, VNode, Fragment } from "preact"; -import { route, Route } from "preact-router"; -import { Paths, Redirect } from "../../InstanceRoutes.js"; -import { AuditorBackend } from "../../declaration.js"; +import { Fragment, h, VNode } from "preact"; +import { route } from "preact-router"; +import { Paths } from "../../InstanceRoutes.js"; export interface ListPageProps { onShowAll: () => void; @@ -56,16 +55,18 @@ export function ListPage(): VNode { return ( <Fragment> - <div class="columns"> <div class="column"> <div class="card"> <div class="card-body"> <button class="button is-fullwidth" - onClick={(e) => route(Paths.amount_arithmethic_inconsistency_list) } + onClick={(e) => + route(Paths.amount_arithmethic_inconsistency_list) + } value={"Amount arithmetic inconsistencies"} - >Amount arithmetic inconsistencies + > + Amount arithmetic inconsistencies </button> </div> </div> @@ -75,9 +76,10 @@ export function ListPage(): VNode { <div class="card-body"> <button class="button is-fullwidth" - onClick={(e) => route(Paths.bad_sig_losses_list) } + onClick={(e) => route(Paths.bad_sig_losses_list)} value={"Bad signature losses"} - >Bad signature losses + > + Bad signature losses </button> </div> </div> @@ -87,8 +89,9 @@ export function ListPage(): VNode { <div class="card-body"> <button class="button is-fullwidth" - onClick={(e) => route(Paths.closure_lag_list) } - >Closure Lags + onClick={(e) => route(Paths.closure_lag_list)} + > + Closure Lags </button> </div> </div> @@ -98,8 +101,9 @@ export function ListPage(): VNode { <div class="card-body"> <button class="button is-fullwidth" - onClick={(e) => route(Paths.coin_inconsistency_list) } - >Coin inconsistencies + onClick={(e) => route(Paths.coin_inconsistency_list)} + > + Coin inconsistencies </button> </div> </div> @@ -112,8 +116,13 @@ export function ListPage(): VNode { <div class="card-body"> <button class="button is-fullwidth" - onClick={(e) => route(Paths.denomination_key_validity_withdraw_inconsistency_list) } - >Denominations key validity + onClick={(e) => + route( + Paths.denomination_key_validity_withdraw_inconsistency_list, + ) + } + > + Denominations key validity </button> </div> </div> @@ -123,8 +132,9 @@ export function ListPage(): VNode { <div class="card-body"> <button class="button is-fullwidth" - onClick={(e) => route(Paths.denomination_without_sig_list) } - >Denominations without signature + onClick={(e) => route(Paths.denomination_without_sig_list)} + > + Denominations without signature </button> </div> </div> @@ -134,8 +144,9 @@ export function ListPage(): VNode { <div class="card-body"> <button class="button is-fullwidth" - onClick={(e) => route(Paths.denomination_pending_list) } - >Denominations pending + onClick={(e) => route(Paths.denomination_pending_list)} + > + Denominations pending </button> </div> </div> @@ -145,8 +156,9 @@ export function ListPage(): VNode { <div class="card-body"> <button class="button is-fullwidth" - onClick={(e) => route(Paths.deposit_confirmation_list) } - >Deposit confirmations + onClick={(e) => route(Paths.deposit_confirmation_list)} + > + Deposit confirmations </button> </div> </div> @@ -159,8 +171,9 @@ export function ListPage(): VNode { <div class="card-body"> <button class="button is-fullwidth" - onClick={(e) => route(Paths.emergency_list) } - >Emergencies + onClick={(e) => route(Paths.emergency_list)} + > + Emergencies </button> </div> </div> @@ -170,8 +183,9 @@ export function ListPage(): VNode { <div class="card-body"> <button class="button is-fullwidth" - onClick={(e) => route(Paths.emergency_by_count_list) } - >Emergencies by count + onClick={(e) => route(Paths.emergency_by_count_list)} + > + Emergencies by count </button> </div> </div> @@ -181,8 +195,9 @@ export function ListPage(): VNode { <div class="card-body"> <button class="button is-fullwidth" - onClick={(e) => route(Paths.fee_time_inconsistency_list) } - >Fee time inconsistencies + onClick={(e) => route(Paths.fee_time_inconsistency_list)} + > + Fee time inconsistencies </button> </div> </div> @@ -192,8 +207,11 @@ export function ListPage(): VNode { <div class="card-body"> <button class="button is-fullwidth" - onClick={(e) => route(Paths.misattribution_in_inconsistency_list) } - >Misattribution in inconsistencies + onClick={(e) => + route(Paths.misattribution_in_inconsistency_list) + } + > + Misattribution in inconsistencies </button> </div> </div> @@ -206,8 +224,11 @@ export function ListPage(): VNode { <div class="card-body"> <button class="button is-fullwidth" - onClick={(e) => route(Paths.purse_not_closed_inconsistency_list) } - >Purses not closed + onClick={(e) => + route(Paths.purse_not_closed_inconsistency_list) + } + > + Purses not closed </button> </div> </div> @@ -217,8 +238,9 @@ export function ListPage(): VNode { <div class="card-body"> <button class="button is-fullwidth" - onClick={(e) => route(Paths.purse_list) } - >Purses + onClick={(e) => route(Paths.purse_list)} + > + Purses </button> </div> </div> @@ -228,8 +250,9 @@ export function ListPage(): VNode { <div class="card-body"> <button class="button is-fullwidth" - onClick={(e) => route(Paths.refresh_hanging_list) } - >Refreshes hanging + onClick={(e) => route(Paths.refresh_hanging_list)} + > + Refreshes hanging </button> </div> </div> @@ -239,8 +262,11 @@ export function ListPage(): VNode { <div class="card-body"> <button class="button is-fullwidth" - onClick={(e) => route(Paths.reserve_balance_insufficient_inconsistency_list) } - >Reserve balances insufficient + onClick={(e) => + route(Paths.reserve_balance_insufficient_inconsistency_list) + } + > + Reserve balances insufficient </button> </div> </div> @@ -253,8 +279,11 @@ export function ListPage(): VNode { <div class="card-body"> <button class="button is-fullwidth" - onClick={(e) => route(Paths.reserve_balance_summary_wrong_inconsistency_list) } - >Reserve balances summary wrong + onClick={(e) => + route(Paths.reserve_balance_summary_wrong_inconsistency_list) + } + > + Reserve balances summary wrong </button> </div> </div> @@ -264,8 +293,9 @@ export function ListPage(): VNode { <div class="card-body"> <button class="button is-fullwidth" - onClick={(e) => route(Paths.reserve_in_inconsistency_list) } - >Reserves in + onClick={(e) => route(Paths.reserve_in_inconsistency_list)} + > + Reserves in </button> </div> </div> @@ -275,8 +305,11 @@ export function ListPage(): VNode { <div class="card-body"> <button class="button is-fullwidth" - onClick={(e) => route(Paths.reserve_not_closed_inconsistency_list) } - >Reserves not closed + onClick={(e) => + route(Paths.reserve_not_closed_inconsistency_list) + } + > + Reserves not closed </button> </div> </div> @@ -286,8 +319,9 @@ export function ListPage(): VNode { <div class="card-body"> <button class="button is-fullwidth" - onClick={(e) => route(Paths.reserves_list) } - >Reserves + onClick={(e) => route(Paths.reserves_list)} + > + Reserves </button> </div> </div> @@ -300,8 +334,9 @@ export function ListPage(): VNode { <div class="card-body"> <button class="button is-fullwidth" - onClick={(e) => route(Paths.row_inconsistency_list) } - >Row inconsistencies + onClick={(e) => route(Paths.row_inconsistency_list)} + > + Row inconsistencies </button> </div> </div> @@ -311,8 +346,9 @@ export function ListPage(): VNode { <div class="card-body"> <button class="button is-fullwidth" - onClick={(e) => route(Paths.row_minor_inconsistency_list) } - >Row minor inconsistencies + onClick={(e) => route(Paths.row_minor_inconsistency_list)} + > + Row minor inconsistencies </button> </div> </div> @@ -322,8 +358,9 @@ export function ListPage(): VNode { <div class="card-body"> <button class="button is-fullwidth" - onClick={(e) => route(Paths.wire_format_inconsistency_list) } - >Wire format inconsistencies + onClick={(e) => route(Paths.wire_format_inconsistency_list)} + > + Wire format inconsistencies </button> </div> </div> @@ -333,14 +370,14 @@ export function ListPage(): VNode { <div class="card-body"> <button class="button is-fullwidth" - onClick={(e) => route(Paths.wire_out_inconsistency_list) } - >Wire out inconsistencies + onClick={(e) => route(Paths.wire_out_inconsistency_list)} + > + Wire out inconsistencies </button> </div> </div> </div> </div> - </Fragment> ); } diff --git a/packages/auditor-backoffice-ui/src/paths/details/index.tsx b/packages/auditor-backoffice-ui/src/paths/details/index.tsx index f99dae7e5..163ccd3c9 100644 --- a/packages/auditor-backoffice-ui/src/paths/details/index.tsx +++ b/packages/auditor-backoffice-ui/src/paths/details/index.tsx @@ -27,7 +27,6 @@ import { Notification } from "../../utils/types.js"; import { ListPage } from "./ListPage.js"; export default function DetailsDashboard(): VNode { - const [notif, setNotif] = useState<Notification | undefined>(undefined); return ( @@ -36,4 +35,4 @@ export default function DetailsDashboard(): VNode { <ListPage /> </section> ); -}
\ No newline at end of file +} diff --git a/packages/auditor-backoffice-ui/src/paths/finance/ListPage.tsx b/packages/auditor-backoffice-ui/src/paths/finance/ListPage.tsx index 88ca6bcfd..4a05ae851 100644 --- a/packages/auditor-backoffice-ui/src/paths/finance/ListPage.tsx +++ b/packages/auditor-backoffice-ui/src/paths/finance/ListPage.tsx @@ -20,12 +20,12 @@ * @author Sebastian Javier Marchano (sebasjm) */ -import { useTranslationContext } from "@gnu-taler/web-util/browser"; -import { h, VNode, Fragment } from "preact"; +/** + * Imports. + */ +import { Fragment, h, VNode } from "preact"; export function ListPage(data: any): VNode { - const { i18n } = useTranslationContext(); - let balances = data.data.data[0][4].data.balances; let coinBalances = [ "Total recoup loss", @@ -62,45 +62,77 @@ export function ListPage(data: any): VNode { <div class="card-content"> <table class="table is-striped is-fullwidth is-dark"> <tbody> - <tr> - <th>Finding</th> - <td class="has-text-right"><b>Count</b></td> - <td class="has-text-right"><b>Gain/Loss</b></td> - </tr> - { - data["data"]["data"][0].map((x: any) => { + <tr> + <th>Finding</th> + <td class="has-text-right"> + <b>Count</b> + </td> + <td class="has-text-right"> + <b>Gain/Loss</b> + </td> + </tr> + {data["data"]["data"][0].map((x: any) => { const key = Object.keys(x.data)[0]; let value = Object.values(x.data)[0]; - const paramName = key[0].toUpperCase() + key.split("_").join(" ").split("-").join(" ").slice(1, key.length); + const paramName = + key[0].toUpperCase() + + key + .split("_") + .join(" ") + .split("-") + .join(" ") + .slice(1, key.length); if (key == "balances") { //TODO fix let gains = 0; - if (value == null) - value = 0; - else - value = Object.keys(value).length; + if (value == null) value = 0; + else value = Object.keys(value).length; return ( <tr class="is-link"> <td>{paramName}</td> - <td class="has-text-right"><p - class={value == 0 ? "text-success" : "text-danger"}>{String(value)}</p></td> - <td class="has-text-right"><p - class={gains == 0 ? "text-success" : "text-danger"}>{String(gains)}</p></td> + <td class="has-text-right"> + <p + class={ + value == 0 ? "text-success" : "text-danger" + } + > + {String(value)} + </p> + </td> + <td class="has-text-right"> + <p + class={ + gains == 0 ? "text-success" : "text-danger" + } + > + {String(gains)} + </p> + </td> </tr> ); } else { <tr class="is-link"> <td>{paramName}</td> - <td class="has-text-right"><p - class={value == 0 ? "text-success" : "text-danger"}>{String(value)}</p></td> - <td class="has-text-right"><p>{ - //TODO - }</p></td> + <td class="has-text-right"> + <p + class={ + value == 0 ? "text-success" : "text-danger" + } + > + {String(value)} + </p> + </td> + <td class="has-text-right"> + <p> + { + //TODO + } + </p> + </td> </tr>; } - }) - } + })} </tbody> </table> </div> @@ -109,34 +141,44 @@ export function ListPage(data: any): VNode { <div class="card-content"> <table class="table is-striped is-fullwidth is-dark"> <tbody> - <tr> - <th>Summary</th> - <td class="has-text-right"><b>Value</b></td> - </tr> - <tr> - <td>Total gain/loss</td> - <td class="has-text-right">{ - //TODO fix - }</td> - </tr> - <tr> - <td>Pending gain/loss</td> - <td class="has-text-right">{ - //TODO fix - }</td> - </tr> - <tr> - <td>Transaction count</td> - <td class="has-text-right">{ - //TODO fix - }</td> - </tr> - <tr> - <td>Transactions pending</td> - <td class="has-text-right">{ - //TODO fix - }</td> - </tr> + <tr> + <th>Summary</th> + <td class="has-text-right"> + <b>Value</b> + </td> + </tr> + <tr> + <td>Total gain/loss</td> + <td class="has-text-right"> + { + //TODO fix + } + </td> + </tr> + <tr> + <td>Pending gain/loss</td> + <td class="has-text-right"> + { + //TODO fix + } + </td> + </tr> + <tr> + <td>Transaction count</td> + <td class="has-text-right"> + { + //TODO fix + } + </td> + </tr> + <tr> + <td>Transactions pending</td> + <td class="has-text-right"> + { + //TODO fix + } + </td> + </tr> </tbody> </table> </div> @@ -150,59 +192,75 @@ export function ListPage(data: any): VNode { <p class="has-text-weight-bold">Helper coin</p> <table class="table is-striped is-fullwidth is-dark"> <tbody> - <tr> - <th>Balance</th> - <td><b>Value</b></td> - </tr> - { - balances.map((x: any) => { + <tr> + <th>Balance</th> + <td> + <b>Value</b> + </td> + </tr> + {balances.map((x: any) => { let key = x.balance_key; - let balanceName = key[0].toUpperCase() + key.split("_").join(" ").split("-").join(" ").slice(1, key.length); + let balanceName = + key[0].toUpperCase() + + key + .split("_") + .join(" ") + .split("-") + .join(" ") + .slice(1, key.length); - if(coinBalances.includes(balanceName)) - { + if (coinBalances.includes(balanceName)) { let value = balances[i].balance_value.replace(":", " "); - i=i+1; + i = i + 1; return ( <tr class="is-link"> <td>{balanceName}</td> - <td><p>{value}</p></td> + <td> + <p>{value}</p> + </td> </tr> ); } else { return null; } - }) - } + })} </tbody> </table> <p class="has-text-weight-bold">Helper reserve</p> <table class="table is-striped is-fullwidth is-dark"> <tbody> - <tr> - <th>Balance</th> - <td><b>Value</b></td> - </tr> - { - balances.map((x: any) => { + <tr> + <th>Balance</th> + <td> + <b>Value</b> + </td> + </tr> + {balances.map((x: any) => { let key = x.balance_key; - let balanceName = key[0].toUpperCase() + key.split("_").join(" ").split("-").join(" ").slice(1, key.length); + let balanceName = + key[0].toUpperCase() + + key + .split("_") + .join(" ") + .split("-") + .join(" ") + .slice(1, key.length); - if(reserveBalances.includes(balanceName)) - { + if (reserveBalances.includes(balanceName)) { let value = balances[i].balance_value.replace(":", " "); - i = i+1; + i = i + 1; return ( <tr class="is-link"> <td>{balanceName}</td> - <td><p>{value}</p></td> + <td> + <p>{value}</p> + </td> </tr> ); } else { return null; } - }) - } + })} </tbody> </table> </div> diff --git a/packages/auditor-backoffice-ui/src/paths/finance/index.tsx b/packages/auditor-backoffice-ui/src/paths/finance/index.tsx index b0d07aa0f..13c718886 100644 --- a/packages/auditor-backoffice-ui/src/paths/finance/index.tsx +++ b/packages/auditor-backoffice-ui/src/paths/finance/index.tsx @@ -20,19 +20,15 @@ * @author Sebastian Javier Marchano (sebasjm) */ -import { - ErrorType, - useTranslationContext, -} from "@gnu-taler/web-util/browser"; +import { HttpStatusCode } from "@gnu-taler/taler-util"; +import { ErrorType, useTranslationContext } from "@gnu-taler/web-util/browser"; import { h, VNode } from "preact"; import { useState } from "preact/hooks"; import { Loading } from "../../components/exception/loading.js"; import { NotificationCard } from "../../components/menu/index.js"; +import { getKeyFiguresData } from "../../hooks/finance.js"; import { Notification } from "../../utils/types.js"; import { ListPage } from "./ListPage.js"; -import { HttpStatusCode } from "@gnu-taler/taler-util"; -import { getKeyFiguresData } from "../../hooks/finance.js"; - interface Props { onUnauthorized: () => VNode; @@ -42,13 +38,12 @@ interface Props { } export default function FinanceDashboard({ - onUnauthorized, - // onLoadError, - onCreate, - onSelect, - onNotFound, - }: Props): VNode { - + onUnauthorized, + // onLoadError, + onCreate, + onSelect, + onNotFound, +}: Props): VNode { const result = getKeyFiguresData(); const [notif, setNotif] = useState<Notification | undefined>(undefined); @@ -67,14 +62,13 @@ export default function FinanceDashboard({ result.status === HttpStatusCode.NotFound ) return onNotFound(); - else - return onNotFound(); + else return onNotFound(); } return ( <section class="section is-main-section"> <NotificationCard notification={notif} /> - <ListPage data={result} /> + <ListPage data={result} /> </section> ); -}
\ No newline at end of file +} diff --git a/packages/auditor-backoffice-ui/src/paths/login/index.tsx b/packages/auditor-backoffice-ui/src/paths/login/index.tsx index c99dc6050..16fb18fdf 100644 --- a/packages/auditor-backoffice-ui/src/paths/login/index.tsx +++ b/packages/auditor-backoffice-ui/src/paths/login/index.tsx @@ -22,106 +22,102 @@ import { useTranslationContext } from "@gnu-taler/web-util/browser"; import { ComponentChildren, Fragment, h, VNode } from "preact"; -import { useCallback, useState } from "preact/hooks"; -import { useBackendContext, useBackendTokenContext } from "../../context/backend.js"; +import { Route } from "preact-router"; +import { useCallback, useState } from "preact/hooks"; import { NotificationCard } from "../../components/menu/index.js"; -import { Notification } from "../../utils/types.js"; +import { useBackendTokenContext } from "../../context/backend.js"; import { useBackendToken } from "../../hooks/backend.js"; -import { Route } from "preact-router"; import { Paths, Redirect } from "../../InstanceRoutes.js"; +import { Notification } from "../../utils/types.js"; export function LoginPage(): VNode { const [token, setToken] = useState(""); const [notif, setNotif] = useState<Notification | undefined>(undefined); const { i18n } = useTranslationContext(); - const doLogin = useCallback(async function doLoginImpl() { - - const result = useBackendToken(); - if (!result.ok) { - } - if (result.ok) { - //TODO fixme - const { token } = useBackendTokenContext(); - /* return ( + const doLogin = useCallback( + async function doLoginImpl() { + const result = useBackendToken(); + if (!result.ok) { + } + if (result.ok) { + //TODO fixme + const { token } = useBackendTokenContext(); + /* return ( <Route path="/" component={Redirect} to={Paths.key_figures}/> );*/ - } else { - setNotif({ - message: "Your password is incorrect", - type: "ERROR", - }); - } - }, [token]); - - return ( - <Route path="/" component={Redirect} to={Paths.key_figures}/> + } else { + setNotif({ + message: "Your password is incorrect", + type: "ERROR", + }); + } + }, + [token], ); + return <Route path="/" component={Redirect} to={Paths.key_figures} />; + return ( <div class="columns is-centered" style={{ margin: "auto" }}> - <div class="column is-two-thirds "> - <div class="modal-card" style={{ width: "100%", margin: 0 }}> - <header - class="modal-card-head" - style={{ border: "1px solid", borderBottom: 0 }} - > - <p class="modal-card-title">{i18n.str`Token required`}</p> - </header> - <section - class="modal-card-body" - style={{ border: "1px solid", borderTop: 0, borderBottom: 0 }} - > - - <p> - <i18n.Translate>Need the access token for the API.</i18n.Translate> - </p> - <div class="field is-horizontal"> - <div class="field-label is-normal"> - <label class="label"> - <i18n.Translate>Access Token</i18n.Translate> - </label> - </div> - <div class="field-body"> - <div class="field"> - <p class="control is-expanded"> - <input - class="input" - type="password" - placeholder={"current access token"} - name="token" - onKeyPress={(e) => - e.keyCode === 13 - ? doLogin() - : null - } - value={token} - onInput={(e): void => setToken(e?.currentTarget.value)} - /> - </p> + <div class="column is-two-thirds "> + <div class="modal-card" style={{ width: "100%", margin: 0 }}> + <header + class="modal-card-head" + style={{ border: "1px solid", borderBottom: 0 }} + > + <p class="modal-card-title">{i18n.str`Token required`}</p> + </header> + <section + class="modal-card-body" + style={{ border: "1px solid", borderTop: 0, borderBottom: 0 }} + > + <p> + <i18n.Translate> + Need the access token for the API. + </i18n.Translate> + </p> + <div class="field is-horizontal"> + <div class="field-label is-normal"> + <label class="label"> + <i18n.Translate>Access Token</i18n.Translate> + </label> + </div> + <div class="field-body"> + <div class="field"> + <p class="control is-expanded"> + <input + class="input" + type="password" + placeholder={"current access token"} + name="token" + onKeyPress={(e) => (e.keyCode === 13 ? doLogin() : null)} + value={token} + onInput={(e): void => setToken(e?.currentTarget.value)} + /> + </p> + </div> </div> </div> - </div> - </section> - <footer - class="modal-card-foot " - style={{ - justifyContent: "flex-end", - border: "1px solid", - borderTop: 0, - }} - > - <AsyncButton - onClick={() => doLogin()} + </section> + <footer + class="modal-card-foot " + style={{ + justifyContent: "flex-end", + border: "1px solid", + borderTop: 0, + }} > - <i18n.Translate>Confirm</i18n.Translate> - </AsyncButton> - </footer> + <AsyncButton onClick={() => doLogin()}> + <i18n.Translate>Confirm</i18n.Translate> + </AsyncButton> + </footer> + </div> </div> </div> - </div>); - + ); - return (<Fragment> + return ( + <Fragment> <NotificationCard notification={notif} /> <div class="columns is-centered" style={{ margin: "auto" }}> <div class="column is-two-thirds "> @@ -145,7 +141,6 @@ export function LoginPage(): VNode { </label> </div> <div class="field-body"> - <div class="field"> <p class="control is-expanded"> <input @@ -154,9 +149,7 @@ export function LoginPage(): VNode { placeholder={"current access token"} name="token" onKeyPress={(e) => - e.keyCode === 13 - ? doLogin() - : null + e.keyCode === 13 ? doLogin() : null } value={token} onInput={(e): void => setToken(e?.currentTarget.value)} @@ -175,39 +168,45 @@ export function LoginPage(): VNode { }} > <div /> - <AsyncButton - type="is-info" - onClick={doLogin} - > + <AsyncButton type="is-info" onClick={doLogin}> <i18n.Translate>Confirm</i18n.Translate> </AsyncButton> - </footer> </div> </div> </div> </Fragment> - ); } -function AsyncButton({ onClick, disabled, type = "", children }: { - type?: string, - disabled?: boolean, - onClick: () => Promise<void>, - children: ComponentChildren +function AsyncButton({ + onClick, + disabled, + type = "", + children, +}: { + type?: string; + disabled?: boolean; + onClick: () => Promise<void>; + children: ComponentChildren; }): VNode { const [running, setRunning] = useState(false); - return <button class={"button " + type} disabled={disabled || running} onClick={() => { - setRunning(true); - onClick().then(() => { - setRunning(false); - }).catch(() => { - setRunning(false); - }); - }}> - {children} - </button>; + return ( + <button + class={"button " + type} + disabled={disabled || running} + onClick={() => { + setRunning(true); + onClick() + .then(() => { + setRunning(false); + }) + .catch(() => { + setRunning(false); + }); + }} + > + {children} + </button> + ); } - - diff --git a/packages/auditor-backoffice-ui/src/paths/notfound/index.tsx b/packages/auditor-backoffice-ui/src/paths/notfound/index.tsx index 114b95219..68adb79bf 100644 --- a/packages/auditor-backoffice-ui/src/paths/notfound/index.tsx +++ b/packages/auditor-backoffice-ui/src/paths/notfound/index.tsx @@ -23,12 +23,12 @@ import { h, VNode } from "preact"; import { Link } from "preact-router"; export default function NotFoundPage(): VNode { - return ( - <div> - <p>That page doesn't exist.</p> - <Link href="/"> - <h4>Back to Home</h4> - </Link> - </div> - ); + return ( + <div> + <p>That page doesn't exist.</p> + <Link href="/"> + <h4>Back to Home</h4> + </Link> + </div> + ); } diff --git a/packages/auditor-backoffice-ui/src/paths/operations/ListPage.tsx b/packages/auditor-backoffice-ui/src/paths/operations/ListPage.tsx index 7f0579b2b..3b26ff071 100644 --- a/packages/auditor-backoffice-ui/src/paths/operations/ListPage.tsx +++ b/packages/auditor-backoffice-ui/src/paths/operations/ListPage.tsx @@ -20,12 +20,12 @@ * @author Sebastian Javier Marchano (sebasjm) */ -import { useTranslationContext } from "@gnu-taler/web-util/browser"; -import { h, VNode, Fragment } from "preact"; +/** + * Imports. + */ +import { Fragment, h, VNode } from "preact"; export function ListPage(data: any): VNode { - const { i18n } = useTranslationContext(); - return ( <Fragment> <div class="columns is-fullwidth"> @@ -34,33 +34,54 @@ export function ListPage(data: any): VNode { <div class="card-content"> <table class="table is-striped is-fullwidth"> <tbody> - <tr> - <th>Finding</th> - <td class="has-text-right"><b>Count</b></td> - <td class="has-text-right"><b>Time difference (s)</b></td> - <td class="has-text-right"><b>Diagnostic</b></td> - </tr> - { - data["data"]["data"][0].map((x: any) => { + <tr> + <th>Finding</th> + <td class="has-text-right"> + <b>Count</b> + </td> + <td class="has-text-right"> + <b>Time difference (s)</b> + </td> + <td class="has-text-right"> + <b>Diagnostic</b> + </td> + </tr> + {data["data"]["data"][0].map((x: any) => { const key = Object.keys(x.data)[0]; let value = Object.values(x.data)[0]; console.log(value); - if (!!value) - value = 0; - const paramName = key[0].toUpperCase() + key.split("_").join(" ").split("-").join(" ").slice(1, key.length); + if (!!value) value = 0; + const paramName = + key[0].toUpperCase() + + key + .split("_") + .join(" ") + .split("-") + .join(" ") + .slice(1, key.length); return ( <tr class="is-link"> <td>{paramName}</td> - <td className="has-text-right"><p - class={value == 0 ? "text-success" : "text-danger"}>{String(value)}</p></td> - <td className="has-text-right">{//TODO - }</td> - <td>{//TODO - }</td> + <td className="has-text-right"> + <p + class={value == 0 ? "text-success" : "text-danger"} + > + {String(value)} + </p> + </td> + <td className="has-text-right"> + { + //TODO + } + </td> + <td> + { + //TODO + } + </td> </tr> ); - }) - } + })} </tbody> </table> </div> @@ -69,4 +90,4 @@ export function ListPage(data: any): VNode { </div> </Fragment> ); -}
\ No newline at end of file +} diff --git a/packages/auditor-backoffice-ui/src/paths/operations/index.tsx b/packages/auditor-backoffice-ui/src/paths/operations/index.tsx index c05b271fe..da8374b20 100644 --- a/packages/auditor-backoffice-ui/src/paths/operations/index.tsx +++ b/packages/auditor-backoffice-ui/src/paths/operations/index.tsx @@ -20,19 +20,15 @@ * @author Sebastian Javier Marchano (sebasjm) */ -import { - ErrorType, - useTranslationContext, -} from "@gnu-taler/web-util/browser"; +import { HttpStatusCode } from "@gnu-taler/taler-util"; +import { ErrorType, useTranslationContext } from "@gnu-taler/web-util/browser"; import { h, VNode } from "preact"; import { useState } from "preact/hooks"; import { Loading } from "../../components/exception/loading.js"; import { NotificationCard } from "../../components/menu/index.js"; +import { getOperationData } from "../../hooks/operational.js"; import { Notification } from "../../utils/types.js"; import { ListPage } from "./ListPage.js"; -import { HttpStatusCode } from "@gnu-taler/taler-util"; -import { getOperationData } from "../../hooks/operational.js"; - interface Props { onUnauthorized: () => VNode; @@ -42,13 +38,12 @@ interface Props { } export default function OperationsDashboard({ - onUnauthorized, - // onLoadError, - onCreate, - onSelect, - onNotFound, - }: Props): VNode { - + onUnauthorized, + // onLoadError, + onCreate, + onSelect, + onNotFound, +}: Props): VNode { const result = getOperationData(); const [notif, setNotif] = useState<Notification | undefined>(undefined); @@ -67,14 +62,13 @@ export default function OperationsDashboard({ result.status === HttpStatusCode.NotFound ) return onNotFound(); - else - return onNotFound(); + else return onNotFound(); } return ( <section class="section is-main-section"> <NotificationCard notification={notif} /> - <ListPage data={result} /> + <ListPage data={result} /> </section> ); -}
\ No newline at end of file +} diff --git a/packages/auditor-backoffice-ui/src/paths/security/ListPage.tsx b/packages/auditor-backoffice-ui/src/paths/security/ListPage.tsx index 74f83bd4a..4408dd7f6 100644 --- a/packages/auditor-backoffice-ui/src/paths/security/ListPage.tsx +++ b/packages/auditor-backoffice-ui/src/paths/security/ListPage.tsx @@ -21,7 +21,7 @@ */ import { useTranslationContext } from "@gnu-taler/web-util/browser"; -import { h, VNode, Fragment } from "preact"; +import { Fragment, h, VNode } from "preact"; export function ListPage(data: any): VNode { const { i18n } = useTranslationContext(); @@ -34,31 +34,46 @@ export function ListPage(data: any): VNode { <div class="card-content"> <table class="table is-striped is-fullwidth"> <tbody> - <tr> - <th>Finding</th> - <td class="has-text-right"><b>Count</b></td> - <td class="has-text-right"><b>Expiration dates</b></td> - </tr> - { - data["data"]["data"][0].map((x: any) => { + <tr> + <th>Finding</th> + <td class="has-text-right"> + <b>Count</b> + </td> + <td class="has-text-right"> + <b>Expiration dates</b> + </td> + </tr> + {data["data"]["data"][0].map((x: any) => { const key = Object.keys(x.data)[0]; let value = Object.values(x.data)[0]; console.log(value); - if (!!value) - value = 0; - const paramName = key[0].toUpperCase() + key.split("_").join(" ").split("-").join(" ").slice(1, key.length); + if (!!value) value = 0; + const paramName = + key[0].toUpperCase() + + key + .split("_") + .join(" ") + .split("-") + .join(" ") + .slice(1, key.length); return ( <tr class="is-link"> <td>{paramName}</td> - <td class="has-text-right"><p - class={value == 0 ? "text-success" : "text-danger"}>{String(value)}</p></td> - <td class="has-text-right">{ - //TODO - }</td> + <td class="has-text-right"> + <p + class={value == 0 ? "text-success" : "text-danger"} + > + {String(value)} + </p> + </td> + <td class="has-text-right"> + { + //TODO + } + </td> </tr> ); - }) - } + })} </tbody> </table> </div> diff --git a/packages/auditor-backoffice-ui/src/paths/security/index.tsx b/packages/auditor-backoffice-ui/src/paths/security/index.tsx index 99c98a5e7..873494352 100644 --- a/packages/auditor-backoffice-ui/src/paths/security/index.tsx +++ b/packages/auditor-backoffice-ui/src/paths/security/index.tsx @@ -20,18 +20,19 @@ * @author Sebastian Javier Marchano (sebasjm) */ -import { - ErrorType, - useTranslationContext, -} from "@gnu-taler/web-util/browser"; +/** + * Imports. + */ +import { HttpStatusCode } from "@gnu-taler/taler-util"; +import { ErrorType, useTranslationContext } from "@gnu-taler/web-util/browser"; import { h, VNode } from "preact"; import { useState } from "preact/hooks"; import { Loading } from "../../components/exception/loading.js"; import { NotificationCard } from "../../components/menu/index.js"; +import { getCriticalData } from "../../hooks/critical.js"; import { Notification } from "../../utils/types.js"; import { ListPage } from "./ListPage.js"; -import { HttpStatusCode } from "@gnu-taler/taler-util"; -import { getCriticalData } from "../../hooks/critical.js"; + interface Props { onUnauthorized: () => VNode; onNotFound: () => VNode; @@ -40,13 +41,12 @@ interface Props { } export default function SecurityDashboard({ - onUnauthorized, - // onLoadError, - onCreate, - onSelect, - onNotFound, - }: Props): VNode { - + onUnauthorized, + // onLoadError, + onCreate, + onSelect, + onNotFound, +}: Props): VNode { const result = getCriticalData(); const [notif, setNotif] = useState<Notification | undefined>(undefined); @@ -65,14 +65,13 @@ export default function SecurityDashboard({ result.status === HttpStatusCode.NotFound ) return onNotFound(); - else - return onNotFound(); + else return onNotFound(); } return ( <section class="section is-main-section"> <NotificationCard notification={notif} /> - <ListPage data={result} /> + <ListPage data={result} /> </section> ); -}
\ No newline at end of file +} diff --git a/packages/auditor-backoffice-ui/src/paths/settings/index.tsx b/packages/auditor-backoffice-ui/src/paths/settings/index.tsx index 77a56a794..3d07f4cf8 100644 --- a/packages/auditor-backoffice-ui/src/paths/settings/index.tsx +++ b/packages/auditor-backoffice-ui/src/paths/settings/index.tsx @@ -20,9 +20,15 @@ * @author Nic Eigel */ +/** + * Imports. + */ import { useLang, useTranslationContext } from "@gnu-taler/web-util/browser"; import { VNode, h } from "preact"; -import { FormErrors, FormProvider } from "../../components/forms/FormProvider.js"; +import { + FormErrors, + FormProvider, +} from "../../components/forms/FormProvider.js"; import { LangSelector } from "../../components/menu/LangSelector.js"; import { Settings, useSettings } from "../../hooks/useSettings.js"; @@ -34,74 +40,78 @@ function getBrowserLang(): string | undefined { } export function Settings({ onClose }: { onClose?: () => void }): VNode { - const { i18n } = useTranslationContext() - const borwserLang = getBrowserLang() - const { update } = useLang(undefined, {}) + const { i18n } = useTranslationContext(); + const borwserLang = getBrowserLang(); + const { update } = useLang(undefined, {}); - const [value, updateValue] = useSettings() - const errors: FormErrors<Settings> = { - } + const [value, updateValue] = useSettings(); + const errors: FormErrors<Settings> = {}; function valueHandler(s: (d: Partial<Settings>) => Partial<Settings>): void { - const next = s(value) + const next = s(value); const v: Settings = { advanceOrderMode: next.advanceOrderMode ?? false, - dateFormat: next.dateFormat ?? "ymd" - } - updateValue(v) + dateFormat: next.dateFormat ?? "ymd", + }; + updateValue(v); } - return <div> - <section class="section is-main-section"> - <div class="columns"> - <div class="column" /> - <div class="column is-four-fifths"> - <div> - - <FormProvider<Settings> - name="settings" - errors={errors} - object={value} - valueHandler={valueHandler} - > - <div class="field is-horizontal"> - <div class="field-label is-normal"> - <label class="label"> - <i18n.Translate>Language</i18n.Translate> - <span class="icon has-tooltip-right" data-tooltip={"Force language setting instance of taking the browser"}> - <i class="mdi mdi-information" /> - </span> - </label> - </div> - <div class="field field-body has-addons is-flex-grow-3"> - <LangSelector /> - - {borwserLang !== undefined && <button - data-tooltip={i18n.str`generate random secret key`} - class="button is-info mr-2" - onClick={(e) => { - update(borwserLang.substring(0, 2)) - }} - > - <i18n.Translate>Set default</i18n.Translate> - </button>} + return ( + <div> + <section class="section is-main-section"> + <div class="columns"> + <div class="column" /> + <div class="column is-four-fifths"> + <div> + <FormProvider<Settings> + name="settings" + errors={errors} + object={value} + valueHandler={valueHandler} + > + <div class="field is-horizontal"> + <div class="field-label is-normal"> + <label class="label"> + <i18n.Translate>Language</i18n.Translate> + <span + class="icon has-tooltip-right" + data-tooltip={ + "Force language setting instance of taking the browser" + } + > + <i class="mdi mdi-information" /> + </span> + </label> + </div> + <div class="field field-body has-addons is-flex-grow-3"> + <LangSelector /> + + {borwserLang !== undefined && ( + <button + data-tooltip={i18n.str`generate random secret key`} + class="button is-info mr-2" + onClick={(e) => { + update(borwserLang.substring(0, 2)); + }} + > + <i18n.Translate>Set default</i18n.Translate> + </button> + )} + </div> </div> - </div> - </FormProvider> + </FormProvider> + </div> </div> + <div class="column" /> </div> - <div class="column" /> - </div> - </section > - {onClose && - <section class="section is-main-section"> - <button - class="button" - onClick={onClose} - > - <i18n.Translate>Close</i18n.Translate> - </button> </section> - } - </div > -}
\ No newline at end of file + {onClose && ( + <section class="section is-main-section"> + <button class="button" onClick={onClose}> + <i18n.Translate>Close</i18n.Translate> + </button> + </section> + )} + </div> + ); +} diff --git a/packages/auditor-backoffice-ui/src/schemas/index.ts b/packages/auditor-backoffice-ui/src/schemas/index.ts deleted file mode 100644 index 380466e13..000000000 --- a/packages/auditor-backoffice-ui/src/schemas/index.ts +++ /dev/null @@ -1,245 +0,0 @@ -/* - This file is part of GNU Taler - (C) 2021-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 <http://www.gnu.org/licenses/> - */ - -/** - * - * @author Sebastian Javier Marchano (sebasjm) - */ - -import { isAfter, isFuture } from "date-fns"; -import * as yup from "yup"; -import { AMOUNT_REGEX, PAYTO_REGEX } from "../utils/constants.js"; -import { Amounts } from "@gnu-taler/taler-util"; - -yup.setLocale({ - mixed: { - default: "field_invalid", - }, - number: { - min: ({ min }: any) => ({ key: "field_too_short", values: { min } }), - max: ({ max }: any) => ({ key: "field_too_big", values: { max } }), - }, -}); - -function listOfPayToUrisAreValid(values?: (string | undefined)[]): boolean { - return !!values && values.every((v) => v && PAYTO_REGEX.test(v)); -} - -function currencyWithAmountIsValid(value?: string): boolean { - return !!value && Amounts.parse(value) !== undefined; -} -function currencyGreaterThan0(value?: string) { - if (value) { - try { - const [, amount] = value.split(":"); - const intAmount = parseInt(amount, 10); - return intAmount > 0; - } catch { - return false; - } - } - return true; -} - -export const InstanceSchema = yup.object().shape({ - id: yup.string().required().meta({ type: "url" }), - name: yup.string().required(), - auth: yup.object().shape({ - method: yup.string().matches(/^(external|token)$/), - token: yup.string().optional().nullable(), - }), - payto_uris: yup - .array() - .of(yup.string()) - .min(1) - .meta({ type: "array" }) - .test("payto", "{path} is not valid", listOfPayToUrisAreValid), - default_max_deposit_fee: yup - .string() - .required() - .test("amount", "the amount is not valid", currencyWithAmountIsValid) - .meta({ type: "amount" }), - default_max_wire_fee: yup - .string() - .required() - .test("amount", "{path} is not valid", currencyWithAmountIsValid) - .meta({ type: "amount" }), - default_wire_fee_amortization: yup.number().required(), - address: yup - .object() - .shape({ - country: yup.string().optional(), - address_lines: yup.array().of(yup.string()).max(7).optional(), - building_number: yup.string().optional(), - building_name: yup.string().optional(), - street: yup.string().optional(), - post_code: yup.string().optional(), - town_location: yup.string().optional(), - town: yup.string(), - district: yup.string().optional(), - country_subdivision: yup.string().optional(), - }) - .meta({ type: "group" }), - jurisdiction: yup - .object() - .shape({ - country: yup.string().optional(), - address_lines: yup.array().of(yup.string()).max(7).optional(), - building_number: yup.string().optional(), - building_name: yup.string().optional(), - street: yup.string().optional(), - post_code: yup.string().optional(), - town_location: yup.string().optional(), - town: yup.string(), - district: yup.string().optional(), - country_subdivision: yup.string().optional(), - }) - .meta({ type: "group" }), - // default_pay_delay: yup.object() - // .shape({ d_us: yup.number() }) - // .required() - // .meta({ type: 'duration' }), - // .transform(numberToDuration), - default_wire_transfer_delay: yup - .object() - .shape({ d_us: yup.number() }) - .required() - .meta({ type: "duration" }), - // .transform(numberToDuration), -}); - -export const InstanceUpdateSchema = InstanceSchema.clone().omit(["id"]); -export const InstanceCreateSchema = InstanceSchema.clone(); - -export const AuthorizeRewardSchema = yup.object().shape({ - justification: yup.string().required(), - amount: yup - .string() - .required() - .test("amount", "the amount is not valid", currencyWithAmountIsValid) - .test("amount_positive", "the amount is not valid", currencyGreaterThan0), - next_url: yup.string().required(), -}); - -const stringIsValidJSON = (value?: string) => { - const p = value?.trim(); - if (!p) return true; - try { - JSON.parse(p); - return true; - } catch { - return false; - } -}; - -export const OrderCreateSchema = yup.object().shape({ - pricing: yup - .object() - .required() - .shape({ - summary: yup.string().ensure().required(), - order_price: yup - .string() - .ensure() - .required() - .test("amount", "the amount is not valid", currencyWithAmountIsValid) - .test( - "amount_positive", - "the amount should be greater than 0", - currencyGreaterThan0, - ), - }), - // extra: yup.object().test("extra", "is not a JSON format", stringIsValidJSON), - payments: yup - .object() - .required() - .shape({ - refund_deadline: yup - .date() - .test("future", "should be in the future", (d) => - d ? isFuture(d) : true, - ), - pay_deadline: yup - .date() - .test("future", "should be in the future", (d) => - d ? isFuture(d) : true, - ), - auto_refund_deadline: yup - .date() - .test("future", "should be in the future", (d) => - d ? isFuture(d) : true, - ), - delivery_date: yup - .date() - .test("future", "should be in the future", (d) => - d ? isFuture(d) : true, - ), - }) - .test("payment", "dates", (d) => { - if ( - d.pay_deadline && - d.refund_deadline && - isAfter(d.refund_deadline, d.pay_deadline) - ) { - return new yup.ValidationError( - "pay deadline should be greater than refund", - "asd", - "payments.pay_deadline", - ); - } - return true; - }), -}); - -export const ProductCreateSchema = yup.object().shape({ - product_id: yup.string().ensure().required(), - description: yup.string().required(), - unit: yup.string().ensure().required(), - price: yup - .string() - .required() - .test("amount", "the amount is not valid", currencyWithAmountIsValid), - stock: yup.object({}).optional(), - minimum_age: yup.number().optional().min(0), -}); - -export const ProductUpdateSchema = yup.object().shape({ - description: yup.string().required(), - price: yup - .string() - .required() - .test("amount", "the amount is not valid", currencyWithAmountIsValid), - stock: yup.object({}).optional(), - minimum_age: yup.number().optional().min(0), -}); - -export const TaxSchema = yup.object().shape({ - name: yup.string().required().ensure(), - tax: yup - .string() - .required() - .test("amount", "the amount is not valid", currencyWithAmountIsValid), -}); - -export const NonInventoryProductSchema = yup.object().shape({ - quantity: yup.number().required().positive(), - description: yup.string().required(), - unit: yup.string().ensure().required(), - price: yup - .string() - .required() - .test("amount", "the amount is not valid", currencyWithAmountIsValid), -}); diff --git a/packages/auditor-backoffice-ui/src/scss/_aside.scss b/packages/auditor-backoffice-ui/src/scss/_aside.scss index b7b59516b..719da7d2c 100644 --- a/packages/auditor-backoffice-ui/src/scss/_aside.scss +++ b/packages/auditor-backoffice-ui/src/scss/_aside.scss @@ -130,7 +130,11 @@ aside.aside { @include touch { nav.navbar { - @include transition(margin-left); + // @include transition(margin-left); + // TODO: adapt above transition mixin to work with multiple transitions + transition: + margin-left 250ms ease-in-out 50ms, + width 250ms ease-in-out 50ms; } aside.aside { @include transition(left); @@ -173,6 +177,7 @@ aside.aside { div.has-aside-mobile-expanded { nav.navbar { margin-left: $aside-mobile-width; + width: calc(100vw - $aside-mobile-width); } aside.aside { left: 0; diff --git a/packages/auditor-backoffice-ui/src/utils/amount.ts b/packages/auditor-backoffice-ui/src/utils/amount.ts deleted file mode 100644 index 0796087ac..000000000 --- a/packages/auditor-backoffice-ui/src/utils/amount.ts +++ /dev/null @@ -1,72 +0,0 @@ -/* - This file is part of GNU Taler - (C) 2021-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 <http://www.gnu.org/licenses/> - *//* -import { - amountFractionalBase, - AmountJson, - Amounts, -} from "@gnu-taler/taler-util"; -import { AuditorBackend } from "../declaration.js"; - -/** - * merge refund with the same description and a difference less than one minute - * @param prev list of refunds that will hold the merged refunds - * @param cur new refund to add to the list - * @returns list with the new refund, may be merged with the last - */ -/*export function mergeRefunds( - prev: MerchantBackend.Orders.RefundDetails[], - cur: MerchantBackend.Orders.RefundDetails, -): MerchantBackend.Orders.RefundDetails[] { - let tail; - - if ( - prev.length === 0 || //empty list - cur.timestamp.t_s === "never" || //current does not have timestamp - (tail = prev[prev.length - 1]).timestamp.t_s === "never" || // last does not have timestamp - cur.reason !== tail.reason || //different reason - cur.pending !== tail.pending || //different pending state - Math.abs(cur.timestamp.t_s - tail.timestamp.t_s) > 1000 * 60 - ) { - //more than 1 minute difference - - //can't merge refunds, they are different or to distant in time - prev.push(cur); - return prev; - } - - const a = Amounts.parseOrThrow(tail.amount); - const b = Amounts.parseOrThrow(cur.amount); - const r = Amounts.add(a, b).amount; - - prev[prev.length - 1] = { - ...tail, - amount: Amounts.stringify(r), - }; - - return prev; -} - -export function rate(a: AmountJson, b: AmountJson): number { - const af = toFloat(a); - const bf = toFloat(b); - if (bf === 0) return 0; - return af / bf; -} - -function toFloat(amount: AmountJson): number { - return amount.value + amount.fraction / amountFractionalBase; -} -*/
\ No newline at end of file diff --git a/packages/auditor-backoffice-ui/src/utils/constants.ts b/packages/auditor-backoffice-ui/src/utils/constants.ts deleted file mode 100644 index e8db927c4..000000000 --- a/packages/auditor-backoffice-ui/src/utils/constants.ts +++ /dev/null @@ -1,197 +0,0 @@ -/* - This file is part of GNU Taler - (C) 2021-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 <http://www.gnu.org/licenses/> - */ - -/** - * - * @author Sebastian Javier Marchano (sebasjm) - */ - -//https://tools.ietf.org/html/rfc8905 -export const PAYTO_REGEX = - /^payto:\/\/[a-zA-Z][a-zA-Z0-9-.]+(\/[a-zA-Z0-9\-\.\~\(\)@_%:!$&'*+,;=]*)*\??((amount|receiver-name|sender-name|instruction|message)=[a-zA-Z0-9\-\.\~\(\)@_%:!$'*+,;=]*&?)*$/; -export const PAYTO_WIRE_METHOD_LOOKUP = - /payto:\/\/([a-zA-Z][a-zA-Z0-9-.]+)\/.*/; - -export const AMOUNT_REGEX = /^[a-zA-Z][a-zA-Z]{1,11}:[0-9][0-9,]*\.?[0-9,]*$/; - -export const INSTANCE_ID_LOOKUP = /\/instances\/([^/]*)\/?$/; - -export const AMOUNT_ZERO_REGEX = /^[a-zA-Z][a-zA-Z]*:0$/; - -export const CROCKFORD_BASE32_REGEX = - /^[0123456789ABCDEFGHJKMNPQRSTVWXYZ]+[*~$=U]*$/; - -export const URL_REGEX = - /^((https?:)(\/\/\/?)([\w]*(?::[\w]*)?@)?([\d\w\.-]+)(?::(\d+))?)\/$/; - -// how much rows we add every time user hit load more -export const PAGE_SIZE = 20; -// how bigger can be the result set -// after this threshold, load more with move the cursor -export const MAX_RESULT_SIZE = PAGE_SIZE * 2 - 1; - -// how much we will wait for all request, in seconds -export const DEFAULT_REQUEST_TIMEOUT = 10; - -export const MAX_IMAGE_SIZE = 1024 * 1024; - -export const INSTANCE_ID_REGEX = /^[a-zA-Z0-9][a-zA-Z0-9_.@-]+$/; - -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", -}; diff --git a/packages/auditor-backoffice-ui/src/utils/regex.test.ts b/packages/auditor-backoffice-ui/src/utils/regex.test.ts deleted file mode 100644 index 78f2ef5ae..000000000 --- a/packages/auditor-backoffice-ui/src/utils/regex.test.ts +++ /dev/null @@ -1,88 +0,0 @@ -/* - This file is part of GNU Taler - (C) 2021-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 <http://www.gnu.org/licenses/> - */ - -/** - * - * @author Sebastian Javier Marchano (sebasjm) - */ - -import { expect } from "chai"; -import { AMOUNT_REGEX, PAYTO_REGEX } from "./constants.js"; - -describe("payto uri format", () => { - const valids = [ - "payto://iban/DE75512108001245126199?amount=EUR:200.0&message=hello", - "payto://ach/122000661/1234", - "payto://upi/alice@example.com?receiver-name=Alice&amount=INR:200", - "payto://void/?amount=EUR:10.5", - "payto://ilp/g.acme.bob", - ]; - - it("should be valid", () => { - valids.forEach((v) => expect(v).match(PAYTO_REGEX)); - }); - - const invalids = [ - // has two question marks - "payto://iban/DE75?512108001245126199?amount=EUR:200.0&message=hello", - // has a space - "payto://ach /122000661/1234", - // has a space - "payto://upi/alice@ example.com?receiver-name=Alice&amount=INR:200", - // invalid field name (mount instead of amount) - "payto://void/?mount=EUR:10.5", - // payto:// is incomplete - "payto: //ilp/g.acme.bob", - ]; - - it("should not be valid", () => { - invalids.forEach((v) => expect(v).not.match(PAYTO_REGEX)); - }); -}); - -describe("amount format", () => { - const valids = [ - "ARS:10", - "COL:10.2", - "UY:1,000.2", - "ARS:10.123,123", - "ARS:1,000,000", - "ARSCOL:10", - "LONGESTCURR:1,000,000.123,123", - ]; - - - it("should be valid", () => { - valids.forEach((v) => expect(v).match(AMOUNT_REGEX)); - }); - - const invalids = [ - //no currency name - ":10", - //use . instead of , - "ARS:1.000.000", - //currency name with numbers - "1ARS:10", - //currency name with numbers - "AR5:10", - //missing value - "USD:", - ]; - - it("should not be valid", () => { - invalids.forEach((v) => expect(v).not.match(AMOUNT_REGEX)); - }); -}); diff --git a/packages/auditor-backoffice-ui/src/utils/table.ts b/packages/auditor-backoffice-ui/src/utils/table.ts deleted file mode 100644 index 1322ad804..000000000 --- a/packages/auditor-backoffice-ui/src/utils/table.ts +++ /dev/null @@ -1,57 +0,0 @@ -/* - This file is part of GNU Taler - (C) 2021-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 <http://www.gnu.org/licenses/> - */ -/* -import { WithId } from "../declaration.js"; -*/ -/** - * - * @author Sebastian Javier Marchano (sebasjm) - */ -/* -export interface Actions<T extends WithId> { - element: T; - type: "DELETE" | "UPDATE"; -} - -function notEmpty<TValue>(value: TValue | null | undefined): value is TValue { - return value !== null && value !== undefined; -} - -export function buildActions<T extends WithId>( - instances: T[], - selected: string[], - action: "DELETE", -): Actions<T>[] { - return selected - .map((id) => instances.find((i) => i.id === id)) - .filter(notEmpty) - .map((id) => ({ element: id, type: action })); -} -*/ -/** - * For any object or array, return the same object if is not empty. - * not empty: - * - for arrays: at least one element not undefined - * - for objects: at least one property not undefined - * @param obj - * @returns - *//* -export function undefinedIfEmpty< - T extends Record<string, unknown> | Array<unknown>, ->(obj: T | undefined): T | undefined { - if (obj === undefined) return undefined; - return Object.values(obj).some((v) => v !== undefined) ? obj : undefined; -}*/ diff --git a/packages/auditor-backoffice-ui/src/utils/types.ts b/packages/auditor-backoffice-ui/src/utils/types.ts index f96606a16..de26cd82e 100644 --- a/packages/auditor-backoffice-ui/src/utils/types.ts +++ b/packages/auditor-backoffice-ui/src/utils/types.ts @@ -14,12 +14,11 @@ GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> */ +/** + * Imports. + */ import { VNode } from "preact"; -export interface KeyValue { - [key: string]: string; -} - export interface Notification { message: string; description?: string | VNode; |